about summary refs log tree commit diff
path: root/src/libnm-core-impl
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2023-03-09 20:01:53 +0100
committerMichael Biebl <biebl@debian.org>2023-03-09 20:01:53 +0100
commitcffb548b401e2cd3fad166aa92e0f634bbb1d4d6 (patch)
tree6d803d8f7d0d92a1c5e5705974fff8975472af95 /src/libnm-core-impl
parent647d6b95d5475e0a1bf3d6f299d1cb8c9041a7a4 (diff)
parent12e1ee797bbf0b10d1fd5afd4b947484769f2b9f (diff)
Merge tag 'debian/1.42.4-1' into debian/bullseye-backports
network-manager Debian release 1.42.4-1
Diffstat (limited to 'src/libnm-core-impl')
-rw-r--r--src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.c160
-rw-r--r--src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.xml.in2500
-rw-r--r--src/libnm-core-impl/meson.build52
-rw-r--r--src/libnm-core-impl/nm-connection.c162
-rw-r--r--src/libnm-core-impl/nm-dbus-utils.c259
-rw-r--r--src/libnm-core-impl/nm-default-libnm-core.h1
-rw-r--r--src/libnm-core-impl/nm-keyfile.c203
-rw-r--r--src/libnm-core-impl/nm-meta-setting-base-impl.c162
-rw-r--r--src/libnm-core-impl/nm-setting-6lowpan.c4
-rw-r--r--src/libnm-core-impl/nm-setting-8021x.c133
-rw-r--r--src/libnm-core-impl/nm-setting-adsl.c4
-rw-r--r--src/libnm-core-impl/nm-setting-bond.c49
-rw-r--r--src/libnm-core-impl/nm-setting-bridge.c39
-rw-r--r--src/libnm-core-impl/nm-setting-cdma.c4
-rw-r--r--src/libnm-core-impl/nm-setting-connection.c26
-rw-r--r--src/libnm-core-impl/nm-setting-ethtool.c8
-rw-r--r--src/libnm-core-impl/nm-setting-gsm.c13
-rw-r--r--src/libnm-core-impl/nm-setting-hostname.c4
-rw-r--r--src/libnm-core-impl/nm-setting-infiniband.c3
-rw-r--r--src/libnm-core-impl/nm-setting-ip-config.c562
-rw-r--r--src/libnm-core-impl/nm-setting-ip-tunnel.c65
-rw-r--r--src/libnm-core-impl/nm-setting-ip4-config.c202
-rw-r--r--src/libnm-core-impl/nm-setting-ip6-config.c127
-rw-r--r--src/libnm-core-impl/nm-setting-loopback.c212
-rw-r--r--src/libnm-core-impl/nm-setting-macsec.c4
-rw-r--r--src/libnm-core-impl/nm-setting-ovs-bridge.c2
-rw-r--r--src/libnm-core-impl/nm-setting-ovs-dpdk.c116
-rw-r--r--src/libnm-core-impl/nm-setting-ovs-external-ids.c122
-rw-r--r--src/libnm-core-impl/nm-setting-ovs-interface.c42
-rw-r--r--src/libnm-core-impl/nm-setting-ovs-other-config.c403
-rw-r--r--src/libnm-core-impl/nm-setting-ovs-port.c364
-rw-r--r--src/libnm-core-impl/nm-setting-pppoe.c4
-rw-r--r--src/libnm-core-impl/nm-setting-private.h116
-rw-r--r--src/libnm-core-impl/nm-setting-serial.c3
-rw-r--r--src/libnm-core-impl/nm-setting-sriov.c2
-rw-r--r--src/libnm-core-impl/nm-setting-tc-config.c39
-rw-r--r--src/libnm-core-impl/nm-setting-team.c7
-rw-r--r--src/libnm-core-impl/nm-setting-vlan.c68
-rw-r--r--src/libnm-core-impl/nm-setting-vpn.c8
-rw-r--r--src/libnm-core-impl/nm-setting-vxlan.c4
-rw-r--r--src/libnm-core-impl/nm-setting-wimax.c6
-rw-r--r--src/libnm-core-impl/nm-setting-wired.c38
-rw-r--r--src/libnm-core-impl/nm-setting-wireguard.c36
-rw-r--r--src/libnm-core-impl/nm-setting-wireless-security.c16
-rw-r--r--src/libnm-core-impl/nm-setting-wireless.c57
-rw-r--r--src/libnm-core-impl/nm-setting-wpan.c12
-rw-r--r--src/libnm-core-impl/nm-setting.c397
-rw-r--r--src/libnm-core-impl/nm-simple-connection.c14
-rw-r--r--src/libnm-core-impl/nm-team-utils.c4
-rw-r--r--src/libnm-core-impl/nm-utils-private.h6
-rw-r--r--src/libnm-core-impl/nm-utils.c276
-rw-r--r--src/libnm-core-impl/nm-vpn-plugin-info.c12
-rw-r--r--src/libnm-core-impl/tests/test-general.c605
-rw-r--r--src/libnm-core-impl/tests/test-setting.c180
54 files changed, 6639 insertions, 1278 deletions
diff --git a/src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.c b/src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.c
new file mode 100644
index 00000000..7e25e510
--- /dev/null
+++ b/src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.c
@@ -0,0 +1,160 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "libnm-core-impl/nm-default-libnm-core.h"
+
+#include "libnm-glib-aux/nm-str-buf.h"
+#include "libnm-core-intern/nm-meta-setting-base.h"
+#include "libnm-core-intern/nm-core-internal.h"
+#include "libnm-base/nm-ethtool-base.h"
+
+#include "libnm-core-public/nm-setting-ethtool.h"
+
+#define INDENT 4
+
+static const char *
+_xml_escape_attr(NMStrBuf *sbuf, const char *value)
+{
+    gs_free char *s = NULL;
+
+    nm_str_buf_reset(sbuf);
+    s = g_markup_escape_text(value, -1);
+    nm_str_buf_append_c(sbuf, '"');
+    nm_str_buf_append(sbuf, s);
+    nm_str_buf_append_c(sbuf, '"');
+    return nm_str_buf_get_str(sbuf);
+}
+
+static const char *
+_indent_level(guint num_spaces)
+{
+    static const char spaces[] = "                      ";
+
+    nm_assert(num_spaces < G_N_ELEMENTS(spaces));
+    return &spaces[G_N_ELEMENTS(spaces) - num_spaces - 1];
+}
+
+int
+main(int argc, char *argv[])
+{
+    nm_auto_str_buf NMStrBuf sbuf1 = NM_STR_BUF_INIT(NM_UTILS_GET_NEXT_REALLOC_SIZE_1000, FALSE);
+    const NMSettInfoSetting *sett_info_settings = nmtst_sett_info_settings();
+    NMMetaSettingType        meta_type;
+
+    g_print("<!--\n"
+            "  This file is generated.\n"
+            "\n"
+            "  This XML contains meta data of NetworkManager connection profiles.\n"
+            "\n"
+            "  NetworkManager's connection profiles are a bunch of settings, and this\n"
+            "  contains the known properties. See also `man nm-settings-{dbus,nmcli,keyfile}`.\n"
+            "\n"
+            "  Note that there are different manifestations of these properties. We have them\n"
+            "  on the D-Bus API (`man nm-settings-dbus`), in keyfile format (`man "
+            "nm-settings-keyfile`)\n"
+            "  in libnm's NMConnection and NMSetting API, and in nmcli (`man nm-settings-nmcli`).\n"
+            "  There are similarities between these, but also subtle differencs. For example,\n"
+            "  a property might not be shown in nmcli, or a property might be named different\n"
+            "  on D-Bus or keyfile. Also, the data types may differ due to the differences of the\n"
+            "  technology.\n"
+            "\n"
+            "  This list of properties is not directly the properties as they are in any of\n"
+            "  those manifestations. Instead, it's a general idea that this property exists in\n"
+            "  NetworkManager. Whether and how it is represented in nmcli or keyfile, may differ.\n"
+            "  The XML however aims to provide information for various backends.\n"
+            "\n"
+            "  <setting> Attributes:\n"
+            "   \"name\": the name of the setting.\n"
+            "   \"gtype\": the typename of the NMSetting class in libnm.\n"
+            "\n"
+            "  <property> Attributes:\n"
+            "   \"name\": the name of the property.\n"
+            "   \"is-deprecated\": whether this property is deprecated.\n"
+            "   \"is-secret\": whether this property is a secret.\n"
+            "   \"is-secret-flags\": whether this property is a secret flags property.\n"
+            "   \"dbus-type\": if this property is exposed on D-Bus. In that case, this\n"
+            "       is the D-Bus type format. Also, \"name\" is the actual name of the field\n"
+            "   \"dbus-deprecated\": if this property is on D-Bus and that representation is\n"
+            "       deprecated. This usually means, that there is a replacement D-Bus property\n"
+            "       that should be used instead.\n"
+            "   \"gprop-type\": if this is a GObject property in the NMSetting class, this\n"
+            "       is the GParamSpec.value_type of the property.\n"
+            "   \"is-setting-option\": whether the property is implemented in libnm's NMSetting\n"
+            "       via the nm_setting_option_*() API.\n"
+            " -->\n");
+    g_print("<nm-setting-docs>\n");
+    for (meta_type = 0; meta_type < _NM_META_SETTING_TYPE_NUM; meta_type++) {
+        const NMSettInfoSetting                 *sis   = &sett_info_settings[meta_type];
+        const NMMetaSettingInfo                 *msi   = &nm_meta_setting_infos[meta_type];
+        nm_auto_unref_gtypeclass NMSettingClass *klass = NULL;
+        guint                                    prop_idx;
+        GType                                    gtype;
+
+        gtype = msi->get_setting_gtype();
+        klass = g_type_class_ref(gtype);
+
+        g_print("%s<setting", _indent_level(INDENT));
+        g_print(" name=%s", _xml_escape_attr(&sbuf1, msi->setting_name));
+        g_print("\n%sgtype=%s",
+                _indent_level(INDENT + 9),
+                _xml_escape_attr(&sbuf1, g_type_name(gtype)));
+        g_print("\n%s>\n", _indent_level(INDENT + 9));
+
+        for (prop_idx = 0; prop_idx < sis->property_infos_len; prop_idx++) {
+            const NMSettInfoProperty *sip = &sis->property_infos[prop_idx];
+
+            if (nm_streq(sip->name, NM_SETTING_NAME))
+                continue;
+
+            g_print("%s<property", _indent_level(2 * INDENT));
+            g_print(" name=%s", _xml_escape_attr(&sbuf1, sip->name));
+            if (sip->is_deprecated)
+                g_print("\n%sis-deprecated=\"1\"", _indent_level(2 * INDENT + 10));
+            if (sip->param_spec && NM_FLAGS_HAS(sip->param_spec->flags, NM_SETTING_PARAM_SECRET)) {
+                g_print("\n%sis-secret=\"1\"", _indent_level(2 * INDENT + 10));
+            }
+            if (sip->param_spec
+                && G_PARAM_SPEC_VALUE_TYPE(sip->param_spec) == NM_TYPE_SETTING_SECRET_FLAGS) {
+                g_print("\n%sis-secret-flags=\"1\"", _indent_level(2 * INDENT + 10));
+            }
+            if (sip->property_type->dbus_type) {
+                g_print("\n%sdbus-type=%s",
+                        _indent_level(2 * INDENT + 10),
+                        _xml_escape_attr(&sbuf1, (const char *) sip->property_type->dbus_type));
+            }
+            if (sip->dbus_deprecated) {
+                nm_assert(sip->property_type->dbus_type);
+                g_print("\n%sdbus-deprecated=\"1\"", _indent_level(2 * INDENT + 10));
+            }
+            if (sip->param_spec) {
+                nm_assert(nm_streq(sip->name, sip->param_spec->name));
+                g_print("\n%sgprop-type=%s",
+                        _indent_level(2 * INDENT + 10),
+                        _xml_escape_attr(&sbuf1,
+                                         g_type_name(G_PARAM_SPEC_VALUE_TYPE(sip->param_spec))));
+            }
+            g_print("\n%s/>\n", _indent_level(2 * INDENT + 10));
+        }
+
+        if (nm_streq(msi->setting_name, NM_SETTING_ETHTOOL_SETTING_NAME)) {
+            NMEthtoolID ethtool_id;
+
+            /* NMSettingEthtool's properties are "gendata" options. They are implemented differently. */
+            for (ethtool_id = _NM_ETHTOOL_ID_FIRST; ethtool_id <= _NM_ETHTOOL_ID_LAST;
+                 ethtool_id++) {
+                g_print("%s<property", _indent_level(2 * INDENT));
+                g_print(" name=%s", _xml_escape_attr(&sbuf1, nm_ethtool_data[ethtool_id]->optname));
+                g_print(
+                    "\n%sdbus-type=%s",
+                    _indent_level(2 * INDENT + 10),
+                    _xml_escape_attr(&sbuf1,
+                                     (const char *) nm_ethtool_id_get_variant_type(ethtool_id)));
+                g_print("\n%sis-setting-option=\"1\"", _indent_level(2 * INDENT + 10));
+                g_print("\n%s/>\n", _indent_level(2 * INDENT + 10));
+            }
+        }
+
+        g_print("%s</setting>\n", _indent_level(INDENT));
+    }
+    g_print("</nm-setting-docs>\n");
+    return 0;
+}
diff --git a/src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.xml.in b/src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.xml.in
new file mode 100644
index 00000000..a40b8695
--- /dev/null
+++ b/src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.xml.in
@@ -0,0 +1,2500 @@
+<!--
+  This file is generated.
+
+  This XML contains meta data of NetworkManager connection profiles.
+
+  NetworkManager's connection profiles are a bunch of settings, and this
+  contains the known properties. See also `man nm-settings-{dbus,nmcli,keyfile}`.
+
+  Note that there are different manifestations of these properties. We have them
+  on the D-Bus API (`man nm-settings-dbus`), in keyfile format (`man nm-settings-keyfile`)
+  in libnm's NMConnection and NMSetting API, and in nmcli (`man nm-settings-nmcli`).
+  There are similarities between these, but also subtle differencs. For example,
+  a property might not be shown in nmcli, or a property might be named different
+  on D-Bus or keyfile. Also, the data types may differ due to the differences of the
+  technology.
+
+  This list of properties is not directly the properties as they are in any of
+  those manifestations. Instead, it's a general idea that this property exists in
+  NetworkManager. Whether and how it is represented in nmcli or keyfile, may differ.
+  The XML however aims to provide information for various backends.
+
+  <setting> Attributes:
+   "name": the name of the setting.
+   "gtype": the typename of the NMSetting class in libnm.
+
+  <property> Attributes:
+   "name": the name of the property.
+   "is-deprecated": whether this property is deprecated.
+   "is-secret": whether this property is a secret.
+   "is-secret-flags": whether this property is a secret flags property.
+   "dbus-type": if this property is exposed on D-Bus. In that case, this
+       is the D-Bus type format. Also, "name" is the actual name of the field
+   "dbus-deprecated": if this property is on D-Bus and that representation is
+       deprecated. This usually means, that there is a replacement D-Bus property
+       that should be used instead.
+   "gprop-type": if this is a GObject property in the NMSetting class, this
+       is the GParamSpec.value_type of the property.
+   "is-setting-option": whether the property is implemented in libnm's NMSetting
+       via the nm_setting_option_*() API.
+ -->
+<nm-setting-docs>
+    <setting name="6lowpan"
+             gtype="NMSetting6Lowpan"
+             >
+        <property name="parent"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+    </setting>
+    <setting name="802-11-olpc-mesh"
+             gtype="NMSettingOlpcMesh"
+             >
+        <property name="channel"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="dhcp-anycast-address"
+                  dbus-type="ay"
+                  gprop-type="gchararray"
+                  />
+        <property name="ssid"
+                  dbus-type="ay"
+                  gprop-type="GBytes"
+                  />
+    </setting>
+    <setting name="802-11-wireless"
+             gtype="NMSettingWireless"
+             >
+        <property name="ap-isolation"
+                  dbus-type="i"
+                  gprop-type="NMTernary"
+                  />
+        <property name="assigned-mac-address"
+                  dbus-type="s"
+                  />
+        <property name="band"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="bssid"
+                  dbus-type="ay"
+                  gprop-type="gchararray"
+                  />
+        <property name="channel"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="cloned-mac-address"
+                  dbus-type="ay"
+                  dbus-deprecated="1"
+                  gprop-type="gchararray"
+                  />
+        <property name="generate-mac-address-mask"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="hidden"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="mac-address"
+                  dbus-type="ay"
+                  gprop-type="gchararray"
+                  />
+        <property name="mac-address-blacklist"
+                  dbus-type="as"
+                  gprop-type="GStrv"
+                  />
+        <property name="mac-address-randomization"
+                  is-deprecated="1"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="mode"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="mtu"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="powersave"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="rate"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="security"
+                  dbus-type="s"
+                  dbus-deprecated="1"
+                  />
+        <property name="seen-bssids"
+                  dbus-type="as"
+                  gprop-type="GStrv"
+                  />
+        <property name="ssid"
+                  dbus-type="ay"
+                  gprop-type="GBytes"
+                  />
+        <property name="tx-power"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="wake-on-wlan"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+    </setting>
+    <setting name="802-11-wireless-security"
+             gtype="NMSettingWirelessSecurity"
+             >
+        <property name="auth-alg"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="fils"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="group"
+                  dbus-type="as"
+                  gprop-type="GStrv"
+                  />
+        <property name="key-mgmt"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="leap-password"
+                  is-secret="1"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="leap-password-flags"
+                  is-secret-flags="1"
+                  dbus-type="u"
+                  gprop-type="NMSettingSecretFlags"
+                  />
+        <property name="leap-username"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="pairwise"
+                  dbus-type="as"
+                  gprop-type="GStrv"
+                  />
+        <property name="pmf"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="proto"
+                  dbus-type="as"
+                  gprop-type="GStrv"
+                  />
+        <property name="psk"
+                  is-secret="1"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="psk-flags"
+                  is-secret-flags="1"
+                  dbus-type="u"
+                  gprop-type="NMSettingSecretFlags"
+                  />
+        <property name="wep-key-flags"
+                  is-secret-flags="1"
+                  dbus-type="u"
+                  gprop-type="NMSettingSecretFlags"
+                  />
+        <property name="wep-key-type"
+                  dbus-type="u"
+                  gprop-type="NMWepKeyType"
+                  />
+        <property name="wep-key0"
+                  is-secret="1"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="wep-key1"
+                  is-secret="1"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="wep-key2"
+                  is-secret="1"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="wep-key3"
+                  is-secret="1"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="wep-tx-keyidx"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="wps-method"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+    </setting>
+    <setting name="802-1x"
+             gtype="NMSetting8021x"
+             >
+        <property name="altsubject-matches"
+                  dbus-type="as"
+                  gprop-type="GStrv"
+                  />
+        <property name="anonymous-identity"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="auth-timeout"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="ca-cert"
+                  dbus-type="ay"
+                  gprop-type="GBytes"
+                  />
+        <property name="ca-cert-password"
+                  is-secret="1"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="ca-cert-password-flags"
+                  is-secret-flags="1"
+                  dbus-type="u"
+                  gprop-type="NMSettingSecretFlags"
+                  />
+        <property name="ca-path"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="client-cert"
+                  dbus-type="ay"
+                  gprop-type="GBytes"
+                  />
+        <property name="client-cert-password"
+                  is-secret="1"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="client-cert-password-flags"
+                  is-secret-flags="1"
+                  dbus-type="u"
+                  gprop-type="NMSettingSecretFlags"
+                  />
+        <property name="domain-match"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="domain-suffix-match"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="eap"
+                  dbus-type="as"
+                  gprop-type="GStrv"
+                  />
+        <property name="identity"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="optional"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="pac-file"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="password"
+                  is-secret="1"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="password-flags"
+                  is-secret-flags="1"
+                  dbus-type="u"
+                  gprop-type="NMSettingSecretFlags"
+                  />
+        <property name="password-raw"
+                  is-secret="1"
+                  dbus-type="ay"
+                  gprop-type="GBytes"
+                  />
+        <property name="password-raw-flags"
+                  is-secret-flags="1"
+                  dbus-type="u"
+                  gprop-type="NMSettingSecretFlags"
+                  />
+        <property name="phase1-auth-flags"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="phase1-fast-provisioning"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="phase1-peaplabel"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="phase1-peapver"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="phase2-altsubject-matches"
+                  dbus-type="as"
+                  gprop-type="GStrv"
+                  />
+        <property name="phase2-auth"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="phase2-autheap"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="phase2-ca-cert"
+                  dbus-type="ay"
+                  gprop-type="GBytes"
+                  />
+        <property name="phase2-ca-cert-password"
+                  is-secret="1"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="phase2-ca-cert-password-flags"
+                  is-secret-flags="1"
+                  dbus-type="u"
+                  gprop-type="NMSettingSecretFlags"
+                  />
+        <property name="phase2-ca-path"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="phase2-client-cert"
+                  dbus-type="ay"
+                  gprop-type="GBytes"
+                  />
+        <property name="phase2-client-cert-password"
+                  is-secret="1"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="phase2-client-cert-password-flags"
+                  is-secret-flags="1"
+                  dbus-type="u"
+                  gprop-type="NMSettingSecretFlags"
+                  />
+        <property name="phase2-domain-match"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="phase2-domain-suffix-match"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="phase2-private-key"
+                  dbus-type="ay"
+                  gprop-type="GBytes"
+                  />
+        <property name="phase2-private-key-password"
+                  is-secret="1"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="phase2-private-key-password-flags"
+                  is-secret-flags="1"
+                  dbus-type="u"
+                  gprop-type="NMSettingSecretFlags"
+                  />
+        <property name="phase2-subject-match"
+                  is-deprecated="1"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="pin"
+                  is-secret="1"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="pin-flags"
+                  is-secret-flags="1"
+                  dbus-type="u"
+                  gprop-type="NMSettingSecretFlags"
+                  />
+        <property name="private-key"
+                  dbus-type="ay"
+                  gprop-type="GBytes"
+                  />
+        <property name="private-key-password"
+                  is-secret="1"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="private-key-password-flags"
+                  is-secret-flags="1"
+                  dbus-type="u"
+                  gprop-type="NMSettingSecretFlags"
+                  />
+        <property name="subject-match"
+                  is-deprecated="1"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="system-ca-certs"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+    </setting>
+    <setting name="802-3-ethernet"
+             gtype="NMSettingWired"
+             >
+        <property name="accept-all-mac-addresses"
+                  dbus-type="i"
+                  gprop-type="NMTernary"
+                  />
+        <property name="assigned-mac-address"
+                  dbus-type="s"
+                  />
+        <property name="auto-negotiate"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="cloned-mac-address"
+                  dbus-type="ay"
+                  dbus-deprecated="1"
+                  gprop-type="gchararray"
+                  />
+        <property name="duplex"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="generate-mac-address-mask"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="mac-address"
+                  dbus-type="ay"
+                  gprop-type="gchararray"
+                  />
+        <property name="mac-address-blacklist"
+                  dbus-type="as"
+                  gprop-type="GStrv"
+                  />
+        <property name="mtu"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="port"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="s390-nettype"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="s390-options"
+                  dbus-type="a{ss}"
+                  gprop-type="GHashTable"
+                  />
+        <property name="s390-subchannels"
+                  dbus-type="as"
+                  gprop-type="GStrv"
+                  />
+        <property name="speed"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="wake-on-lan"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="wake-on-lan-password"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+    </setting>
+    <setting name="adsl"
+             gtype="NMSettingAdsl"
+             >
+        <property name="encapsulation"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="password"
+                  is-secret="1"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="password-flags"
+                  is-secret-flags="1"
+                  dbus-type="u"
+                  gprop-type="NMSettingSecretFlags"
+                  />
+        <property name="protocol"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="username"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="vci"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="vpi"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+    </setting>
+    <setting name="bluetooth"
+             gtype="NMSettingBluetooth"
+             >
+        <property name="bdaddr"
+                  dbus-type="ay"
+                  gprop-type="gchararray"
+                  />
+        <property name="type"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+    </setting>
+    <setting name="bond"
+             gtype="NMSettingBond"
+             >
+        <property name="interface-name"
+                  dbus-type="s"
+                  dbus-deprecated="1"
+                  />
+        <property name="options"
+                  dbus-type="a{ss}"
+                  gprop-type="GHashTable"
+                  />
+    </setting>
+    <setting name="bond-port"
+             gtype="NMSettingBondPort"
+             >
+        <property name="queue-id"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+    </setting>
+    <setting name="bridge"
+             gtype="NMSettingBridge"
+             >
+        <property name="ageing-time"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="forward-delay"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="group-address"
+                  dbus-type="ay"
+                  gprop-type="gchararray"
+                  />
+        <property name="group-forward-mask"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="hello-time"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="interface-name"
+                  dbus-type="s"
+                  dbus-deprecated="1"
+                  />
+        <property name="mac-address"
+                  is-deprecated="1"
+                  dbus-type="ay"
+                  gprop-type="gchararray"
+                  />
+        <property name="max-age"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="multicast-hash-max"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="multicast-last-member-count"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="multicast-last-member-interval"
+                  dbus-type="t"
+                  gprop-type="guint64"
+                  />
+        <property name="multicast-membership-interval"
+                  dbus-type="t"
+                  gprop-type="guint64"
+                  />
+        <property name="multicast-querier"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="multicast-querier-interval"
+                  dbus-type="t"
+                  gprop-type="guint64"
+                  />
+        <property name="multicast-query-interval"
+                  dbus-type="t"
+                  gprop-type="guint64"
+                  />
+        <property name="multicast-query-response-interval"
+                  dbus-type="t"
+                  gprop-type="guint64"
+                  />
+        <property name="multicast-query-use-ifaddr"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="multicast-router"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="multicast-snooping"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="multicast-startup-query-count"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="multicast-startup-query-interval"
+                  dbus-type="t"
+                  gprop-type="guint64"
+                  />
+        <property name="priority"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="stp"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="vlan-default-pvid"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="vlan-filtering"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="vlan-protocol"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="vlan-stats-enabled"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="vlans"
+                  dbus-type="aa{sv}"
+                  gprop-type="GPtrArray"
+                  />
+    </setting>
+    <setting name="bridge-port"
+             gtype="NMSettingBridgePort"
+             >
+        <property name="hairpin-mode"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="path-cost"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="priority"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="vlans"
+                  dbus-type="aa{sv}"
+                  gprop-type="GPtrArray"
+                  />
+    </setting>
+    <setting name="cdma"
+             gtype="NMSettingCdma"
+             >
+        <property name="mtu"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="number"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="password"
+                  is-secret="1"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="password-flags"
+                  is-secret-flags="1"
+                  dbus-type="u"
+                  gprop-type="NMSettingSecretFlags"
+                  />
+        <property name="username"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+    </setting>
+    <setting name="connection"
+             gtype="NMSettingConnection"
+             >
+        <property name="auth-retries"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="autoconnect"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="autoconnect-priority"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="autoconnect-retries"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="autoconnect-slaves"
+                  dbus-type="i"
+                  gprop-type="NMSettingConnectionAutoconnectSlaves"
+                  />
+        <property name="dns-over-tls"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="gateway-ping-timeout"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="id"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="interface-name"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="lldp"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="llmnr"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="master"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="mdns"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="metered"
+                  dbus-type="i"
+                  gprop-type="NMMetered"
+                  />
+        <property name="mptcp-flags"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="mud-url"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="multi-connect"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="permissions"
+                  dbus-type="as"
+                  gprop-type="GStrv"
+                  />
+        <property name="read-only"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="secondaries"
+                  dbus-type="as"
+                  gprop-type="GStrv"
+                  />
+        <property name="slave-type"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="stable-id"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="timestamp"
+                  dbus-type="t"
+                  gprop-type="guint64"
+                  />
+        <property name="type"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="uuid"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="wait-activation-delay"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="wait-device-timeout"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="zone"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+    </setting>
+    <setting name="dcb"
+             gtype="NMSettingDcb"
+             >
+        <property name="app-fcoe-flags"
+                  dbus-type="u"
+                  gprop-type="NMSettingDcbFlags"
+                  />
+        <property name="app-fcoe-mode"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="app-fcoe-priority"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="app-fip-flags"
+                  dbus-type="u"
+                  gprop-type="NMSettingDcbFlags"
+                  />
+        <property name="app-fip-priority"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="app-iscsi-flags"
+                  dbus-type="u"
+                  gprop-type="NMSettingDcbFlags"
+                  />
+        <property name="app-iscsi-priority"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="priority-bandwidth"
+                  dbus-type="au"
+                  gprop-type="GArray"
+                  />
+        <property name="priority-flow-control"
+                  dbus-type="au"
+                  gprop-type="GArray"
+                  />
+        <property name="priority-flow-control-flags"
+                  dbus-type="u"
+                  gprop-type="NMSettingDcbFlags"
+                  />
+        <property name="priority-group-bandwidth"
+                  dbus-type="au"
+                  gprop-type="GArray"
+                  />
+        <property name="priority-group-flags"
+                  dbus-type="u"
+                  gprop-type="NMSettingDcbFlags"
+                  />
+        <property name="priority-group-id"
+                  dbus-type="au"
+                  gprop-type="GArray"
+                  />
+        <property name="priority-strict-bandwidth"
+                  dbus-type="au"
+                  gprop-type="GArray"
+                  />
+        <property name="priority-traffic-class"
+                  dbus-type="au"
+                  gprop-type="GArray"
+                  />
+    </setting>
+    <setting name="dummy"
+             gtype="NMSettingDummy"
+             >
+    </setting>
+    <setting name="ethtool"
+             gtype="NMSettingEthtool"
+             >
+        <property name="coalesce-adaptive-rx"
+                  dbus-type="u"
+                  is-setting-option="1"
+                  />
+        <property name="coalesce-adaptive-tx"
+                  dbus-type="u"
+                  is-setting-option="1"
+                  />
+        <property name="coalesce-pkt-rate-high"
+                  dbus-type="u"
+                  is-setting-option="1"
+                  />
+        <property name="coalesce-pkt-rate-low"
+                  dbus-type="u"
+                  is-setting-option="1"
+                  />
+        <property name="coalesce-rx-frames"
+                  dbus-type="u"
+                  is-setting-option="1"
+                  />
+        <property name="coalesce-rx-frames-high"
+                  dbus-type="u"
+                  is-setting-option="1"
+                  />
+        <property name="coalesce-rx-frames-irq"
+                  dbus-type="u"
+                  is-setting-option="1"
+                  />
+        <property name="coalesce-rx-frames-low"
+                  dbus-type="u"
+                  is-setting-option="1"
+                  />
+        <property name="coalesce-rx-usecs"
+                  dbus-type="u"
+                  is-setting-option="1"
+                  />
+        <property name="coalesce-rx-usecs-high"
+                  dbus-type="u"
+                  is-setting-option="1"
+                  />
+        <property name="coalesce-rx-usecs-irq"
+                  dbus-type="u"
+                  is-setting-option="1"
+                  />
+        <property name="coalesce-rx-usecs-low"
+                  dbus-type="u"
+                  is-setting-option="1"
+                  />
+        <property name="coalesce-sample-interval"
+                  dbus-type="u"
+                  is-setting-option="1"
+                  />
+        <property name="coalesce-stats-block-usecs"
+                  dbus-type="u"
+                  is-setting-option="1"
+                  />
+        <property name="coalesce-tx-frames"
+                  dbus-type="u"
+                  is-setting-option="1"
+                  />
+        <property name="coalesce-tx-frames-high"
+                  dbus-type="u"
+                  is-setting-option="1"
+                  />
+        <property name="coalesce-tx-frames-irq"
+                  dbus-type="u"
+                  is-setting-option="1"
+                  />
+        <property name="coalesce-tx-frames-low"
+                  dbus-type="u"
+                  is-setting-option="1"
+                  />
+        <property name="coalesce-tx-usecs"
+                  dbus-type="u"
+                  is-setting-option="1"
+                  />
+        <property name="coalesce-tx-usecs-high"
+                  dbus-type="u"
+                  is-setting-option="1"
+                  />
+        <property name="coalesce-tx-usecs-irq"
+                  dbus-type="u"
+                  is-setting-option="1"
+                  />
+        <property name="coalesce-tx-usecs-low"
+                  dbus-type="u"
+                  is-setting-option="1"
+                  />
+        <property name="feature-esp-hw-offload"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-esp-tx-csum-hw-offload"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-fcoe-mtu"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-gro"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-gso"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-highdma"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-hw-tc-offload"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-l2-fwd-offload"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-loopback"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-lro"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-macsec-hw-offload"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-ntuple"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-rx"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-rxhash"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-rxvlan"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-rx-all"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-rx-fcs"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-rx-gro-hw"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-rx-gro-list"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-rx-udp-gro-forwarding"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-rx-udp_tunnel-port-offload"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-rx-vlan-filter"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-rx-vlan-stag-filter"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-rx-vlan-stag-hw-parse"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-sg"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-tls-hw-record"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-tls-hw-rx-offload"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-tls-hw-tx-offload"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-tso"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-tx"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-txvlan"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-tx-checksum-fcoe-crc"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-tx-checksum-ipv4"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-tx-checksum-ipv6"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-tx-checksum-ip-generic"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-tx-checksum-sctp"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-tx-esp-segmentation"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-tx-fcoe-segmentation"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-tx-gre-csum-segmentation"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-tx-gre-segmentation"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-tx-gso-list"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-tx-gso-partial"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-tx-gso-robust"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-tx-ipxip4-segmentation"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-tx-ipxip6-segmentation"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-tx-nocache-copy"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-tx-scatter-gather"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-tx-scatter-gather-fraglist"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-tx-sctp-segmentation"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-tx-tcp6-segmentation"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-tx-tcp-ecn-segmentation"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-tx-tcp-mangleid-segmentation"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-tx-tcp-segmentation"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-tx-tunnel-remcsum-segmentation"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-tx-udp-segmentation"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-tx-udp_tnl-csum-segmentation"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-tx-udp_tnl-segmentation"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="feature-tx-vlan-stag-hw-insert"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="pause-autoneg"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="pause-rx"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="pause-tx"
+                  dbus-type="b"
+                  is-setting-option="1"
+                  />
+        <property name="ring-rx"
+                  dbus-type="u"
+                  is-setting-option="1"
+                  />
+        <property name="ring-rx-jumbo"
+                  dbus-type="u"
+                  is-setting-option="1"
+                  />
+        <property name="ring-rx-mini"
+                  dbus-type="u"
+                  is-setting-option="1"
+                  />
+        <property name="ring-tx"
+                  dbus-type="u"
+                  is-setting-option="1"
+                  />
+    </setting>
+    <setting name="generic"
+             gtype="NMSettingGeneric"
+             >
+    </setting>
+    <setting name="gsm"
+             gtype="NMSettingGsm"
+             >
+        <property name="allowed-bands"
+                  dbus-type="u"
+                  dbus-deprecated="1"
+                  />
+        <property name="apn"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="auto-config"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="device-id"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="home-only"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="mtu"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="network-id"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="network-type"
+                  dbus-type="i"
+                  dbus-deprecated="1"
+                  />
+        <property name="number"
+                  is-deprecated="1"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="password"
+                  is-secret="1"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="password-flags"
+                  is-secret-flags="1"
+                  dbus-type="u"
+                  gprop-type="NMSettingSecretFlags"
+                  />
+        <property name="pin"
+                  is-secret="1"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="pin-flags"
+                  is-secret-flags="1"
+                  dbus-type="u"
+                  gprop-type="NMSettingSecretFlags"
+                  />
+        <property name="sim-id"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="sim-operator-id"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="username"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+    </setting>
+    <setting name="hostname"
+             gtype="NMSettingHostname"
+             >
+        <property name="from-dhcp"
+                  dbus-type="i"
+                  gprop-type="NMTernary"
+                  />
+        <property name="from-dns-lookup"
+                  dbus-type="i"
+                  gprop-type="NMTernary"
+                  />
+        <property name="only-from-default"
+                  dbus-type="i"
+                  gprop-type="NMTernary"
+                  />
+        <property name="priority"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+    </setting>
+    <setting name="infiniband"
+             gtype="NMSettingInfiniband"
+             >
+        <property name="mac-address"
+                  dbus-type="ay"
+                  gprop-type="gchararray"
+                  />
+        <property name="mtu"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="p-key"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="parent"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="transport-mode"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+    </setting>
+    <setting name="ip-tunnel"
+             gtype="NMSettingIPTunnel"
+             >
+        <property name="encapsulation-limit"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="flags"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="flow-label"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="fwmark"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="input-key"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="local"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="mode"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="mtu"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="output-key"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="parent"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="path-mtu-discovery"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="remote"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="tos"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="ttl"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+    </setting>
+    <setting name="ipv4"
+             gtype="NMSettingIP4Config"
+             >
+        <property name="address-data"
+                  dbus-type="aa{sv}"
+                  />
+        <property name="address-labels"
+                  dbus-type="as"
+                  dbus-deprecated="1"
+                  />
+        <property name="addresses"
+                  dbus-type="aau"
+                  dbus-deprecated="1"
+                  gprop-type="GPtrArray"
+                  />
+        <property name="auto-route-ext-gw"
+                  dbus-type="i"
+                  gprop-type="NMTernary"
+                  />
+        <property name="dad-timeout"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="dhcp-client-id"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="dhcp-fqdn"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="dhcp-hostname"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="dhcp-hostname-flags"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="dhcp-iaid"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="dhcp-reject-servers"
+                  dbus-type="as"
+                  gprop-type="GStrv"
+                  />
+        <property name="dhcp-send-hostname"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="dhcp-timeout"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="dhcp-vendor-class-identifier"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="dns"
+                  dbus-type="au"
+                  dbus-deprecated="1"
+                  gprop-type="GStrv"
+                  />
+        <property name="dns-data"
+                  dbus-type="as"
+                  />
+        <property name="dns-options"
+                  dbus-type="as"
+                  gprop-type="GStrv"
+                  />
+        <property name="dns-priority"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="dns-search"
+                  dbus-type="as"
+                  gprop-type="GStrv"
+                  />
+        <property name="gateway"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="ignore-auto-dns"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="ignore-auto-routes"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="link-local"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="may-fail"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="method"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="never-default"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="replace-local-rule"
+                  dbus-type="i"
+                  gprop-type="NMTernary"
+                  />
+        <property name="required-timeout"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="route-data"
+                  dbus-type="aa{sv}"
+                  />
+        <property name="route-metric"
+                  dbus-type="x"
+                  gprop-type="gint64"
+                  />
+        <property name="route-table"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="routes"
+                  dbus-type="aau"
+                  dbus-deprecated="1"
+                  gprop-type="GPtrArray"
+                  />
+        <property name="routing-rules"
+                  dbus-type="aa{sv}"
+                  />
+    </setting>
+    <setting name="ipv6"
+             gtype="NMSettingIP6Config"
+             >
+        <property name="addr-gen-mode"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="address-data"
+                  dbus-type="aa{sv}"
+                  />
+        <property name="addresses"
+                  dbus-type="a(ayuay)"
+                  dbus-deprecated="1"
+                  gprop-type="GPtrArray"
+                  />
+        <property name="auto-route-ext-gw"
+                  dbus-type="i"
+                  gprop-type="NMTernary"
+                  />
+        <property name="dad-timeout"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="dhcp-duid"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="dhcp-hostname"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="dhcp-hostname-flags"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="dhcp-iaid"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="dhcp-reject-servers"
+                  dbus-type="as"
+                  gprop-type="GStrv"
+                  />
+        <property name="dhcp-send-hostname"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="dhcp-timeout"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="dns"
+                  dbus-type="aay"
+                  dbus-deprecated="1"
+                  gprop-type="GStrv"
+                  />
+        <property name="dns-data"
+                  dbus-type="as"
+                  />
+        <property name="dns-options"
+                  dbus-type="as"
+                  gprop-type="GStrv"
+                  />
+        <property name="dns-priority"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="dns-search"
+                  dbus-type="as"
+                  gprop-type="GStrv"
+                  />
+        <property name="gateway"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="ignore-auto-dns"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="ignore-auto-routes"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="ip6-privacy"
+                  dbus-type="i"
+                  gprop-type="NMSettingIP6ConfigPrivacy"
+                  />
+        <property name="may-fail"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="method"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="mtu"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="never-default"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="ra-timeout"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="replace-local-rule"
+                  dbus-type="i"
+                  gprop-type="NMTernary"
+                  />
+        <property name="required-timeout"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="route-data"
+                  dbus-type="aa{sv}"
+                  />
+        <property name="route-metric"
+                  dbus-type="x"
+                  gprop-type="gint64"
+                  />
+        <property name="route-table"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="routes"
+                  dbus-type="a(ayuayu)"
+                  dbus-deprecated="1"
+                  gprop-type="GPtrArray"
+                  />
+        <property name="routing-rules"
+                  dbus-type="aa{sv}"
+                  />
+        <property name="token"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+    </setting>
+    <setting name="loopback"
+             gtype="NMSettingLoopback"
+             >
+        <property name="mtu"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+    </setting>
+    <setting name="macsec"
+             gtype="NMSettingMacsec"
+             >
+        <property name="encrypt"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="mka-cak"
+                  is-secret="1"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="mka-cak-flags"
+                  is-secret-flags="1"
+                  dbus-type="u"
+                  gprop-type="NMSettingSecretFlags"
+                  />
+        <property name="mka-ckn"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="mode"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="parent"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="port"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="send-sci"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="validation"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+    </setting>
+    <setting name="macvlan"
+             gtype="NMSettingMacvlan"
+             >
+        <property name="mode"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="parent"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="promiscuous"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="tap"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+    </setting>
+    <setting name="match"
+             gtype="NMSettingMatch"
+             >
+        <property name="driver"
+                  dbus-type="as"
+                  gprop-type="GStrv"
+                  />
+        <property name="interface-name"
+                  dbus-type="as"
+                  gprop-type="GStrv"
+                  />
+        <property name="kernel-command-line"
+                  dbus-type="as"
+                  gprop-type="GStrv"
+                  />
+        <property name="path"
+                  dbus-type="as"
+                  gprop-type="GStrv"
+                  />
+    </setting>
+    <setting name="ovs-bridge"
+             gtype="NMSettingOvsBridge"
+             >
+        <property name="datapath-type"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="fail-mode"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="mcast-snooping-enable"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="rstp-enable"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="stp-enable"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+    </setting>
+    <setting name="ovs-dpdk"
+             gtype="NMSettingOvsDpdk"
+             >
+        <property name="devargs"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="n-rxq"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="n-rxq-desc"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="n-txq-desc"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+    </setting>
+    <setting name="ovs-external-ids"
+             gtype="NMSettingOvsExternalIDs"
+             >
+        <property name="data"
+                  dbus-type="a{ss}"
+                  gprop-type="GHashTable"
+                  />
+    </setting>
+    <setting name="ovs-interface"
+             gtype="NMSettingOvsInterface"
+             >
+        <property name="ofport-request"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="type"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+    </setting>
+    <setting name="ovs-other-config"
+             gtype="NMSettingOvsOtherConfig"
+             >
+        <property name="data"
+                  dbus-type="a{ss}"
+                  gprop-type="GHashTable"
+                  />
+    </setting>
+    <setting name="ovs-patch"
+             gtype="NMSettingOvsPatch"
+             >
+        <property name="peer"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+    </setting>
+    <setting name="ovs-port"
+             gtype="NMSettingOvsPort"
+             >
+        <property name="bond-downdelay"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="bond-mode"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="bond-updelay"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="lacp"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="tag"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="trunks"
+                  dbus-type="aa{sv}"
+                  gprop-type="GPtrArray"
+                  />
+        <property name="vlan-mode"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+    </setting>
+    <setting name="ppp"
+             gtype="NMSettingPpp"
+             >
+        <property name="baud"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="crtscts"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="lcp-echo-failure"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="lcp-echo-interval"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="mppe-stateful"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="mru"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="mtu"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="no-vj-comp"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="noauth"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="nobsdcomp"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="nodeflate"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="refuse-chap"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="refuse-eap"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="refuse-mschap"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="refuse-mschapv2"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="refuse-pap"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="require-mppe"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="require-mppe-128"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+    </setting>
+    <setting name="pppoe"
+             gtype="NMSettingPppoe"
+             >
+        <property name="parent"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="password"
+                  is-secret="1"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="password-flags"
+                  is-secret-flags="1"
+                  dbus-type="u"
+                  gprop-type="NMSettingSecretFlags"
+                  />
+        <property name="service"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="username"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+    </setting>
+    <setting name="proxy"
+             gtype="NMSettingProxy"
+             >
+        <property name="browser-only"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="method"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="pac-script"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="pac-url"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+    </setting>
+    <setting name="serial"
+             gtype="NMSettingSerial"
+             >
+        <property name="baud"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="bits"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="parity"
+                  dbus-type="y"
+                  gprop-type="NMSettingSerialParity"
+                  />
+        <property name="send-delay"
+                  dbus-type="t"
+                  gprop-type="guint64"
+                  />
+        <property name="stopbits"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+    </setting>
+    <setting name="sriov"
+             gtype="NMSettingSriov"
+             >
+        <property name="autoprobe-drivers"
+                  dbus-type="i"
+                  gprop-type="NMTernary"
+                  />
+        <property name="total-vfs"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="vfs"
+                  dbus-type="aa{sv}"
+                  gprop-type="GPtrArray"
+                  />
+    </setting>
+    <setting name="tc"
+             gtype="NMSettingTCConfig"
+             >
+        <property name="qdiscs"
+                  dbus-type="aa{sv}"
+                  gprop-type="GPtrArray"
+                  />
+        <property name="tfilters"
+                  dbus-type="aa{sv}"
+                  gprop-type="GPtrArray"
+                  />
+    </setting>
+    <setting name="team"
+             gtype="NMSettingTeam"
+             >
+        <property name="config"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="interface-name"
+                  dbus-type="s"
+                  dbus-deprecated="1"
+                  />
+        <property name="link-watchers"
+                  dbus-type="aa{sv}"
+                  gprop-type="GPtrArray"
+                  />
+        <property name="mcast-rejoin-count"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="mcast-rejoin-interval"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="notify-peers-count"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="notify-peers-interval"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="runner"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="runner-active"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="runner-agg-select-policy"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="runner-fast-rate"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="runner-hwaddr-policy"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="runner-min-ports"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="runner-sys-prio"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="runner-tx-balancer"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="runner-tx-balancer-interval"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="runner-tx-hash"
+                  dbus-type="as"
+                  gprop-type="GStrv"
+                  />
+    </setting>
+    <setting name="team-port"
+             gtype="NMSettingTeamPort"
+             >
+        <property name="config"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="lacp-key"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="lacp-prio"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="link-watchers"
+                  dbus-type="aa{sv}"
+                  gprop-type="GPtrArray"
+                  />
+        <property name="prio"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="queue-id"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="sticky"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+    </setting>
+    <setting name="tun"
+             gtype="NMSettingTun"
+             >
+        <property name="group"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="mode"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="multi-queue"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="owner"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="pi"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="vnet-hdr"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+    </setting>
+    <setting name="user"
+             gtype="NMSettingUser"
+             >
+        <property name="data"
+                  dbus-type="a{ss}"
+                  gprop-type="GHashTable"
+                  />
+    </setting>
+    <setting name="veth"
+             gtype="NMSettingVeth"
+             >
+        <property name="peer"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+    </setting>
+    <setting name="vlan"
+             gtype="NMSettingVlan"
+             >
+        <property name="egress-priority-map"
+                  dbus-type="as"
+                  gprop-type="GStrv"
+                  />
+        <property name="flags"
+                  dbus-type="u"
+                  gprop-type="NMVlanFlags"
+                  />
+        <property name="id"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="ingress-priority-map"
+                  dbus-type="as"
+                  gprop-type="GStrv"
+                  />
+        <property name="interface-name"
+                  dbus-type="s"
+                  dbus-deprecated="1"
+                  />
+        <property name="parent"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="protocol"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+    </setting>
+    <setting name="vpn"
+             gtype="NMSettingVpn"
+             >
+        <property name="data"
+                  dbus-type="a{ss}"
+                  gprop-type="GHashTable"
+                  />
+        <property name="persistent"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="secrets"
+                  is-secret="1"
+                  dbus-type="a{ss}"
+                  gprop-type="GHashTable"
+                  />
+        <property name="service-type"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="timeout"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="user-name"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+    </setting>
+    <setting name="vrf"
+             gtype="NMSettingVrf"
+             >
+        <property name="table"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+    </setting>
+    <setting name="vxlan"
+             gtype="NMSettingVxlan"
+             >
+        <property name="ageing"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="destination-port"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="id"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="l2-miss"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="l3-miss"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="learning"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="limit"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="local"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="parent"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="proxy"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="remote"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="rsc"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="source-port-max"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="source-port-min"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="tos"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="ttl"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+    </setting>
+    <setting name="wifi-p2p"
+             gtype="NMSettingWifiP2P"
+             >
+        <property name="peer"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="wfd-ies"
+                  dbus-type="ay"
+                  gprop-type="GBytes"
+                  />
+        <property name="wps-method"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+    </setting>
+    <setting name="wimax"
+             gtype="NMSettingWimax"
+             >
+        <property name="mac-address"
+                  is-deprecated="1"
+                  dbus-type="ay"
+                  gprop-type="gchararray"
+                  />
+        <property name="network-name"
+                  is-deprecated="1"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+    </setting>
+    <setting name="wireguard"
+             gtype="NMSettingWireGuard"
+             >
+        <property name="fwmark"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="ip4-auto-default-route"
+                  dbus-type="i"
+                  gprop-type="NMTernary"
+                  />
+        <property name="ip6-auto-default-route"
+                  dbus-type="i"
+                  gprop-type="NMTernary"
+                  />
+        <property name="listen-port"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="mtu"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="peer-routes"
+                  dbus-type="b"
+                  gprop-type="gboolean"
+                  />
+        <property name="peers"
+                  dbus-type="aa{sv}"
+                  />
+        <property name="private-key"
+                  is-secret="1"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="private-key-flags"
+                  is-secret-flags="1"
+                  dbus-type="u"
+                  gprop-type="NMSettingSecretFlags"
+                  />
+    </setting>
+    <setting name="wpan"
+             gtype="NMSettingWpan"
+             >
+        <property name="channel"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="mac-address"
+                  dbus-type="s"
+                  gprop-type="gchararray"
+                  />
+        <property name="page"
+                  dbus-type="i"
+                  gprop-type="gint"
+                  />
+        <property name="pan-id"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+        <property name="short-address"
+                  dbus-type="u"
+                  gprop-type="guint"
+                  />
+    </setting>
+</nm-setting-docs>
diff --git a/src/libnm-core-impl/meson.build b/src/libnm-core-impl/meson.build
index 83c29085..6408ae17 100644
--- a/src/libnm-core-impl/meson.build
+++ b/src/libnm-core-impl/meson.build
@@ -24,6 +24,7 @@ libnm_core_settings_sources = files(
   'nm-setting-ip-tunnel.c',
   'nm-setting-ip4-config.c',
   'nm-setting-ip6-config.c',
+  'nm-setting-loopback.c',
   'nm-setting-macsec.c',
   'nm-setting-macvlan.c',
   'nm-setting-match.c',
@@ -32,6 +33,7 @@ libnm_core_settings_sources = files(
   'nm-setting-ovs-dpdk.c',
   'nm-setting-ovs-external-ids.c',
   'nm-setting-ovs-interface.c',
+  'nm-setting-ovs-other-config.c',
   'nm-setting-ovs-patch.c',
   'nm-setting-ovs-port.c',
   'nm-setting-ppp.c',
@@ -60,7 +62,6 @@ libnm_core_settings_sources = files(
 
 libnm_core_impl_sources = files(
   'nm-connection.c',
-  'nm-dbus-utils.c',
   'nm-errors.c',
   'nm-keyfile-utils.c',
   'nm-keyfile.c',
@@ -88,3 +89,52 @@ libnm_core_impl = static_library(
     uuid_dep,
   ],
 )
+
+###############################################################################
+
+gen_metadata_nm_settings_libnm_core = executable(
+  'gen-metadata-nm-settings-libnm-core',
+  files(
+    'gen-metadata-nm-settings-libnm-core.c',
+  ),
+  dependencies: [
+    libnm_core_public_dep,
+  ],
+  link_with: [
+    libnm_core_aux_intern,
+    libnm_core_impl,
+    libnm_crypto,
+    libnm_base,
+    libnm_systemd_shared,
+    libnm_log_null,
+    libnm_glib_aux,
+    libnm_std_aux,
+    libc_siphash,
+  ],
+  link_args: ldflags_linker_script_binary,
+  link_depends: linker_script_binary,
+)
+
+if enable_docs
+  gen_metadata_nm_settings_libnm_core_xml = custom_target(
+    'gen-metadata-nm-settings-libnm-core.xml',
+    output: 'gen-metadata-nm-settings-libnm-core.xml',
+    command: [ gen_metadata_nm_settings_libnm_core ],
+    capture: true,
+  )
+
+  test(
+    'check-local-gen-metadata-nm-settings-libnm-core',
+    find_program(join_paths(source_root, 'tools', 'check-compare-generated.sh')),
+    args: [
+      source_root + '/src/libnm-core-impl/gen-metadata-nm-settings-libnm-core.xml.in',
+      gen_metadata_nm_settings_libnm_core_xml,
+    ],
+  )
+else
+  gen_metadata_nm_settings_libnm_core_xml = configure_file(
+    input: 'gen-metadata-nm-settings-libnm-core.xml.in',
+    output: '@BASENAME@',
+    configuration: configuration_data(),
+  )
+endif
diff --git a/src/libnm-core-impl/nm-connection.c b/src/libnm-core-impl/nm-connection.c
index 2f5bf357..3a9eda0e 100644
--- a/src/libnm-core-impl/nm-connection.c
+++ b/src/libnm-core-impl/nm-connection.c
@@ -11,6 +11,7 @@
 #include <arpa/inet.h>
 
 #include "libnm-glib-aux/nm-uuid.h"
+#include "libnm-glib-aux/nm-ref-string.h"
 #include "nm-connection-private.h"
 #include "nm-utils.h"
 #include "nm-setting-private.h"
@@ -54,39 +55,12 @@ static gboolean _nm_connection_clear_settings(NMConnection *connection, NMConnec
 
 /*****************************************************************************/
 
-#undef NM_IS_SIMPLE_CONNECTION
-#define NM_IS_SIMPLE_CONNECTION(self)                                                           \
-    ({                                                                                          \
-        gconstpointer _self1 = (self);                                                          \
-        gboolean      _result;                                                                  \
-                                                                                                \
-        _result =                                                                               \
-            (_self1                                                                             \
-             && (((GTypeInstance *) _self1)->g_class == _nm_simple_connection_class_instance)); \
-                                                                                                \
-        nm_assert(_result == G_TYPE_CHECK_INSTANCE_TYPE(_self1, NM_TYPE_SIMPLE_CONNECTION));    \
-                                                                                                \
-        _result;                                                                                \
-    })
-
-#undef NM_IS_CONNECTION
-#define NM_IS_CONNECTION(self)                                            \
-    ({                                                                    \
-        gconstpointer _self0 = (self);                                    \
-                                                                          \
-        (_self0                                                           \
-         && (NM_IS_SIMPLE_CONNECTION(_self0)                              \
-             || G_TYPE_CHECK_INSTANCE_TYPE(_self0, NM_TYPE_CONNECTION))); \
-    })
-
-/*****************************************************************************/
-
 void
 _nm_connection_private_clear(NMConnectionPrivate *priv)
 {
     if (priv->self) {
         _nm_connection_clear_settings(priv->self, priv);
-        nm_clear_g_free(&priv->path);
+        nm_clear_pointer(&priv->path, nm_ref_string_unref);
         priv->self = NULL;
     }
 }
@@ -124,19 +98,19 @@ _nm_connection_get_private_from_qdata(NMConnection *connection)
     return priv;
 }
 
-#define NM_CONNECTION_GET_PRIVATE(connection)                                                     \
-    ({                                                                                            \
-        NMConnection        *_connection = (connection);                                          \
-        NMConnectionPrivate *_priv;                                                               \
-                                                                                                  \
-        if (G_LIKELY(NM_IS_SIMPLE_CONNECTION(_connection)))                                       \
-            _priv = (gpointer) (&(((char *) _connection)[_nm_simple_connection_private_offset])); \
-        else                                                                                      \
-            _priv = _nm_connection_get_private_from_qdata(_connection);                           \
-                                                                                                  \
-        nm_assert(_priv && _priv->self == _connection);                                           \
-                                                                                                  \
-        _priv;                                                                                    \
+#define NM_CONNECTION_GET_PRIVATE(connection)                                  \
+    ({                                                                         \
+        NMConnection        *_connection = (connection);                       \
+        NMConnectionPrivate *_priv;                                            \
+                                                                               \
+        if (G_LIKELY(NM_IS_SIMPLE_CONNECTION(_connection)))                    \
+            _priv = _NM_SIMPLE_CONNECTION_GET_CONNECTION_PRIVATE(_connection); \
+        else                                                                   \
+            _priv = _nm_connection_get_private_from_qdata(_connection);        \
+                                                                               \
+        nm_assert(_priv && _priv->self == _connection);                        \
+                                                                               \
+        _priv;                                                                 \
     })
 
 /*****************************************************************************/
@@ -1308,11 +1282,11 @@ _normalize_ip_config(NMConnection *self, GHashTable *parameters)
                 && nm_setting_ip6_config_get_addr_gen_mode((NMSettingIP6Config *) s_ip6)
                        == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64) {
                 struct in6_addr i6_token;
-                char            normalized[NM_UTILS_INET_ADDRSTRLEN];
+                char            normalized[NM_INET_ADDRSTRLEN];
 
                 if (inet_pton(AF_INET6, token, &i6_token) == 1
                     && _nm_utils_inet6_is_token(&i6_token)) {
-                    _nm_utils_inet6_ntop(&i6_token, normalized);
+                    nm_inet6_ntop(&i6_token, normalized);
                     if (g_strcmp0(token, normalized)) {
                         g_object_set(s_ip6, NM_SETTING_IP6_CONFIG_TOKEN, normalized, NULL);
                         changed = TRUE;
@@ -1624,6 +1598,18 @@ _normalize_bridge_port_vlan_order(NMConnection *self)
 }
 
 static gboolean
+_normalize_ovs_port_trunks(NMConnection *self)
+{
+    NMSettingOvsPort *s_ovs_port;
+
+    s_ovs_port = nm_connection_get_setting_ovs_port(self);
+    if (!s_ovs_port)
+        return FALSE;
+
+    return _nm_setting_ovs_port_sort_trunks(s_ovs_port);
+}
+
+static gboolean
 _normalize_gsm_auto_config(NMConnection *self)
 {
     NMSettingGsm *s_gsm;
@@ -2015,6 +2001,7 @@ _connection_normalize(NMConnection *connection,
     was_modified |= _normalize_bridge_port_vlan_order(connection);
     was_modified |= _normalize_gsm_auto_config(connection);
     was_modified |= _normalize_802_1x_empty_strings(connection);
+    was_modified |= _normalize_ovs_port_trunks(connection);
 
     was_modified = !!was_modified;
 
@@ -2361,35 +2348,15 @@ nm_connection_update_secrets(NMConnection *connection,
     return success;
 }
 
-/**
- * nm_connection_need_secrets:
- * @connection: the #NMConnection
- * @hints: (out) (element-type utf8) (allow-none) (transfer container):
- *   the address of a pointer to a #GPtrArray, initialized to %NULL, which on
- *   return points to an allocated #GPtrArray containing the property names of
- *   secrets of the #NMSetting which may be required; the caller owns the array
- *   and must free the array itself with g_ptr_array_free(), but not free its
- *   elements
- *
- * Returns the name of the first setting object in the connection which would
- * need secrets to make a successful connection.  The returned hints are only
- * intended as a guide to what secrets may be required, because in some
- * circumstances, there is no way to conclusively determine exactly which
- * secrets are needed.
- *
- * Returns: the setting name of the #NMSetting object which has invalid or
- *   missing secrets
- **/
-const char *
-nm_connection_need_secrets(NMConnection *connection, GPtrArray **hints)
+static const char *
+_need_secrets(NMConnection *connection, gboolean check_rerequest, GPtrArray **hints)
 {
     NMSetting           *setting_before = NULL;
     NMConnectionPrivate *priv;
     int                  i;
 
-    g_return_val_if_fail(NM_IS_CONNECTION(connection), NULL);
-    if (hints)
-        g_return_val_if_fail(*hints == NULL, NULL);
+    nm_assert(NM_IS_CONNECTION(connection));
+    nm_assert(!hints || !*hints);
 
     priv = NM_CONNECTION_GET_PRIVATE(connection);
 
@@ -2405,7 +2372,7 @@ nm_connection_need_secrets(NMConnection *connection, GPtrArray **hints)
         nm_assert(!setting_before || _nm_setting_compare_priority(setting_before, setting) <= 0);
         setting_before = setting;
 
-        secrets = _nm_setting_need_secrets(setting);
+        secrets = _nm_setting_need_secrets(setting, check_rerequest);
         if (!secrets)
             continue;
 
@@ -2421,6 +2388,48 @@ nm_connection_need_secrets(NMConnection *connection, GPtrArray **hints)
 }
 
 /**
+ * nm_connection_need_secrets:
+ * @connection: the #NMConnection
+ * @hints: (out) (element-type utf8) (allow-none) (transfer container):
+ *   the address of a pointer to a #GPtrArray, initialized to %NULL, which on
+ *   return points to an allocated #GPtrArray containing the property names of
+ *   secrets of the #NMSetting which may be required; the caller owns the array
+ *   and must free the array itself with g_ptr_array_free(), but not free its
+ *   elements
+ *
+ * Returns the name of the first setting object in the connection which would
+ * need secrets to make a successful connection.  The returned hints are only
+ * intended as a guide to what secrets may be required, because in some
+ * circumstances, there is no way to conclusively determine exactly which
+ * secrets are needed.
+ *
+ * Returns: the setting name of the #NMSetting object which has invalid or
+ *   missing secrets
+ **/
+const char *
+nm_connection_need_secrets(NMConnection *connection, GPtrArray **hints)
+{
+    g_return_val_if_fail(NM_IS_CONNECTION(connection), NULL);
+    g_return_val_if_fail(!hints || !*hints, NULL);
+
+    return _need_secrets(connection, FALSE, hints);
+}
+
+/**
+ * nm_connection_need_secrets_for_rerequest:
+ * @connection: the #NMConnection
+ *
+ * Returns TRUE if some secret needs to be re-requested
+ **/
+gboolean
+nm_connection_need_secrets_for_rerequest(NMConnection *connection)
+{
+    g_return_val_if_fail(NM_IS_CONNECTION(connection), FALSE);
+
+    return !!_need_secrets(connection, TRUE, NULL);
+}
+
+/**
  * nm_connection_clear_secrets:
  * @connection: the #NMConnection
  *
@@ -2942,14 +2951,15 @@ nm_connection_dump(NMConnection *connection)
 void
 nm_connection_set_path(NMConnection *connection, const char *path)
 {
-    NMConnectionPrivate *priv;
-
     g_return_if_fail(NM_IS_CONNECTION(connection));
 
-    priv = NM_CONNECTION_GET_PRIVATE(connection);
+    nm_ref_string_reset_str(&NM_CONNECTION_GET_PRIVATE(connection)->path, path);
+}
 
-    g_free(priv->path);
-    priv->path = g_strdup(path);
+void
+_nm_connection_set_path_rstr(NMConnection *connection, NMRefString *path)
+{
+    nm_ref_string_reset(&NM_CONNECTION_GET_PRIVATE(connection)->path, path);
 }
 
 /**
@@ -2966,6 +2976,12 @@ nm_connection_get_path(NMConnection *connection)
 {
     g_return_val_if_fail(NM_IS_CONNECTION(connection), NULL);
 
+    return nm_ref_string_get_str(NM_CONNECTION_GET_PRIVATE(connection)->path);
+}
+
+NMRefString *
+_nm_connection_get_path_rstr(NMConnection *connection)
+{
     return NM_CONNECTION_GET_PRIVATE(connection)->path;
 }
 
diff --git a/src/libnm-core-impl/nm-dbus-utils.c b/src/libnm-core-impl/nm-dbus-utils.c
deleted file mode 100644
index c9443ff9..00000000
--- a/src/libnm-core-impl/nm-dbus-utils.c
+++ /dev/null
@@ -1,259 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/*
- * Copyright (C) 2015 Red Hat, Inc.
- */
-
-#include "libnm-core-impl/nm-default-libnm-core.h"
-
-#include "libnm-core-intern/nm-core-internal.h"
-
-typedef struct {
-    char               *signal_name;
-    const GVariantType *signature;
-} NMDBusSignalData;
-
-static void
-dbus_signal_data_free(gpointer data, GClosure *closure)
-{
-    NMDBusSignalData *sd = data;
-
-    g_free(sd->signal_name);
-    g_slice_free(NMDBusSignalData, sd);
-}
-
-static void
-dbus_signal_meta_marshal(GClosure     *closure,
-                         GValue       *return_value,
-                         guint         n_param_values,
-                         const GValue *param_values,
-                         gpointer      invocation_hint,
-                         gpointer      marshal_data)
-{
-    NMDBusSignalData *sd = marshal_data;
-    const char       *signal_name;
-    GVariant         *parameters, *param;
-    GValue           *closure_params;
-    gsize             n_params, i;
-
-    g_return_if_fail(n_param_values == 4);
-
-    signal_name = g_value_get_string(&param_values[2]);
-    parameters  = g_value_get_variant(&param_values[3]);
-
-    if (strcmp(signal_name, sd->signal_name) != 0)
-        return;
-
-    if (sd->signature) {
-        if (!g_variant_is_of_type(parameters, sd->signature)) {
-            g_warning("%p: got signal '%s' but parameters were of type '%s', not '%s'",
-                      g_value_get_object(&param_values[0]),
-                      signal_name,
-                      g_variant_get_type_string(parameters),
-                      g_variant_type_peek_string(sd->signature));
-            return;
-        }
-
-        n_params = g_variant_n_children(parameters) + 1;
-    } else
-        n_params = 1;
-
-    closure_params = g_new0(GValue, n_params);
-    g_value_init(&closure_params[0], G_TYPE_OBJECT);
-    g_value_copy(&param_values[0], &closure_params[0]);
-
-    for (i = 1; i < n_params; i++) {
-        param = g_variant_get_child_value(parameters, i - 1);
-        if (g_variant_is_of_type(param, G_VARIANT_TYPE("ay"))
-            || g_variant_is_of_type(param, G_VARIANT_TYPE("aay"))) {
-            /* g_dbus_gvariant_to_gvalue() thinks 'ay' means "non-UTF-8 NUL-terminated string" */
-            g_value_init(&closure_params[i], G_TYPE_VARIANT);
-            g_value_set_variant(&closure_params[i], param);
-        } else
-            g_dbus_gvariant_to_gvalue(param, &closure_params[i]);
-        g_variant_unref(param);
-    }
-
-    g_cclosure_marshal_generic(closure, NULL, n_params, closure_params, invocation_hint, NULL);
-
-    for (i = 0; i < n_params; i++)
-        g_value_unset(&closure_params[i]);
-    g_free(closure_params);
-}
-
-/**
- * _nm_dbus_signal_connect_data:
- * @proxy: a #GDBusProxy
- * @signal_name: the D-Bus signal to connect to
- * @signature: (allow-none): the signal's type signature (must be a tuple)
- * @c_handler: the signal handler function
- * @data: (allow-none): data to pass to @c_handler
- * @destroy_data: (allow-none): closure destroy notify for @data
- * @connect_flags: connection flags
- *
- * Connects to the D-Bus signal @signal_name on @proxy. @c_handler must be a
- * void function whose first argument is a #GDBusProxy, followed by arguments
- * for each element of @signature, ending with a #gpointer argument for @data.
- *
- * The argument types in @c_handler correspond to the types output by
- * g_dbus_gvariant_to_gvalue(), except for 'ay' and 'aay'. In particular:
- * - both 16-bit and 32-bit integers are passed as #int/#guint
- * - 'as' values are passed as #GStrv (char **)
- * - all other array, tuple, and dict types are passed as #GVariant
- *
- * If @signature is %NULL, then the signal's parameters will be ignored, and
- * @c_handler should take only the #GDBusProxy and #gpointer arguments.
- *
- * Returns: the signal handler ID, which can be used with
- *   g_signal_handler_remove(). Beware that because of the way the signal is
- *   connected, you will not be able to remove it with
- *   g_signal_handlers_disconnect_by_func(), although
- *   g_signal_handlers_disconnect_by_data() will work correctly.
- */
-gulong
-_nm_dbus_signal_connect_data(GDBusProxy         *proxy,
-                             const char         *signal_name,
-                             const GVariantType *signature,
-                             GCallback           c_handler,
-                             gpointer            data,
-                             GClosureNotify      destroy_data,
-                             GConnectFlags       connect_flags)
-{
-    NMDBusSignalData *sd;
-    GClosure         *closure;
-    gboolean          swapped = !!(connect_flags & G_CONNECT_SWAPPED);
-    gboolean          after   = !!(connect_flags & G_CONNECT_AFTER);
-
-    g_return_val_if_fail(G_IS_DBUS_PROXY(proxy), 0);
-    g_return_val_if_fail(signal_name != NULL, 0);
-    g_return_val_if_fail(signature == NULL || g_variant_type_is_tuple(signature), 0);
-    g_return_val_if_fail(c_handler != NULL, 0);
-
-    sd              = g_slice_new(NMDBusSignalData);
-    sd->signal_name = g_strdup(signal_name);
-    sd->signature   = signature;
-
-    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);
-
-    return g_signal_connect_closure(proxy, "g-signal", closure, after);
-}
-
-/**
- * _nm_dbus_signal_connect:
- * @proxy: a #GDBusProxy
- * @signal_name: the D-Bus signal to connect to
- * @signature: the signal's type signature (must be a tuple)
- * @c_handler: the signal handler function
- * @data: (allow-none): data to pass to @c_handler
- *
- * Simplified version of _nm_dbus_signal_connect_data() with fewer arguments.
- *
- * Returns: the signal handler ID, as with _nm_signal_connect_data().
- */
-
-/**
- * _nm_dbus_typecheck_response:
- * @response: the #GVariant response to check.
- * @reply_type: the expected reply type. It may be %NULL to perform no
- *   checking.
- * @error: (allow-none): the error in case the @reply_type does not match.
- *
- * Returns: %TRUE, if @response is of the expected @reply_type.
- */
-gboolean
-_nm_dbus_typecheck_response(GVariant *response, const GVariantType *reply_type, GError **error)
-{
-    g_return_val_if_fail(response, FALSE);
-
-    if (!reply_type)
-        return TRUE;
-    if (g_variant_is_of_type(response, reply_type))
-        return TRUE;
-
-    /* This is the same error code that g_dbus_connection_call() returns if
-     * @reply_type doesn't match.
-     */
-    g_set_error(error,
-                G_IO_ERROR,
-                G_IO_ERROR_INVALID_ARGUMENT,
-                _("Method returned type '%s', but expected '%s'"),
-                g_variant_get_type_string(response),
-                g_variant_type_peek_string(reply_type));
-    return FALSE;
-}
-
-/**
- * _nm_dbus_proxy_call_finish:
- * @proxy: A #GDBusProxy.
- * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to
- *   g_dbus_proxy_call().
- * @reply_type: (allow-none): the expected type of the reply, or %NULL
- * @error: Return location for error or %NULL.
- *
- * Finishes an operation started with g_dbus_proxy_call(), as with
- * g_dbus_proxy_call_finish(), except thatif @reply_type is non-%NULL, then it
- * will also check that the response matches that type signature, and return
- * an error if not.
- *
- * Returns: %NULL if @error is set. Otherwise, a #GVariant tuple with
- * return values. Free with g_variant_unref().
- */
-GVariant *
-_nm_dbus_proxy_call_finish(GDBusProxy         *proxy,
-                           GAsyncResult       *res,
-                           const GVariantType *reply_type,
-                           GError            **error)
-{
-    GVariant *variant;
-
-    variant = g_dbus_proxy_call_finish(proxy, res, error);
-    if (variant && !_nm_dbus_typecheck_response(variant, reply_type, error))
-        nm_clear_pointer(&variant, g_variant_unref);
-    return variant;
-}
-
-GVariant *
-_nm_dbus_connection_call_finish(GDBusConnection    *dbus_connection,
-                                GAsyncResult       *result,
-                                const GVariantType *reply_type,
-                                GError            **error)
-{
-    GVariant *variant;
-
-    variant = g_dbus_connection_call_finish(dbus_connection, result, error);
-    if (variant && !_nm_dbus_typecheck_response(variant, reply_type, error))
-        nm_clear_pointer(&variant, g_variant_unref);
-    return variant;
-}
-
-/**
- * _nm_dbus_error_has_name:
- * @error: (allow-none): a #GError, or %NULL
- * @dbus_error_name: a D-Bus error name
- *
- * Checks if @error is set and corresponds to the D-Bus error @dbus_error_name.
- *
- * This should only be used for "foreign" D-Bus errors (eg, errors
- * from BlueZ or wpa_supplicant). All NetworkManager D-Bus errors
- * should be properly mapped by gdbus to one of the domains/codes in
- * nm-errors.h.
- *
- * Returns: %TRUE or %FALSE
- */
-gboolean
-_nm_dbus_error_has_name(GError *error, const char *dbus_error_name)
-{
-    gboolean has_name = FALSE;
-
-    if (error && g_dbus_error_is_remote_error(error)) {
-        char *error_name;
-
-        error_name = g_dbus_error_get_remote_error(error);
-        has_name   = !g_strcmp0(error_name, dbus_error_name);
-        g_free(error_name);
-    }
-
-    return has_name;
-}
diff --git a/src/libnm-core-impl/nm-default-libnm-core.h b/src/libnm-core-impl/nm-default-libnm-core.h
index 0f27f82d..013df67d 100644
--- a/src/libnm-core-impl/nm-default-libnm-core.h
+++ b/src/libnm-core-impl/nm-default-libnm-core.h
@@ -16,6 +16,7 @@
 /*****************************************************************************/
 
 #include "nm-version.h"
+#include "nm-errors.h"
 
 /*****************************************************************************/
 
diff --git a/src/libnm-core-impl/nm-keyfile.c b/src/libnm-core-impl/nm-keyfile.c
index 6ee68b5c..9a6ffc6f 100644
--- a/src/libnm-core-impl/nm-keyfile.c
+++ b/src/libnm-core-impl/nm-keyfile.c
@@ -28,12 +28,14 @@
 #include "nm-setting-private.h"
 #include "nm-setting-user.h"
 #include "nm-setting-ovs-external-ids.h"
+#include "nm-setting-ovs-other-config.h"
 
 #include "libnm-core-intern/nm-keyfile-utils.h"
 
 #define ETHERNET_S390_OPTIONS_GROUP_NAME "ethernet-s390-options"
 
-#define OVS_EXTERNAL_IDS_DATA_PREFIX "data."
+/* used for "ovs-external-ids.data" and "ovs-other-config.data". */
+#define STRDICT_DATA_PREFIX "data."
 
 /*****************************************************************************/
 
@@ -397,7 +399,7 @@ build_route(KeyfileReaderInfo *info,
 
     /* Next hop */
     if (gateway_str && gateway_str[0]) {
-        if (!nm_utils_ipaddr_is_valid(family, gateway_str)) {
+        if (!nm_inet_is_valid(family, gateway_str)) {
             /* Try workaround for routes written by broken keyfile writer.
              * Due to bug bgo#719851, an older version of writer would have
              * written "a:b:c:d::/plen,metric" if the gateway was ::, instead
@@ -1060,23 +1062,29 @@ ip_routing_rule_parser_full(KeyfileReaderInfo        *info,
 }
 
 static void
-_parser_full_ovs_external_ids_data(KeyfileReaderInfo        *info,
-                                   const NMMetaSettingInfo  *setting_info,
-                                   const NMSettInfoProperty *property_info,
-                                   const ParseInfoProperty  *pip,
-                                   NMSetting                *setting)
+_parser_full_strdict_data(KeyfileReaderInfo        *info,
+                          const NMMetaSettingInfo  *setting_info,
+                          const NMSettInfoProperty *property_info,
+                          const ParseInfoProperty  *pip,
+                          NMSetting                *setting)
 {
-    const char        *setting_name = NM_SETTING_OVS_EXTERNAL_IDS_SETTING_NAME;
-    gs_strfreev char **keys         = NULL;
+    gs_strfreev char **keys = NULL;
     gsize              n_keys;
     gsize              i;
+    gboolean           is_exid;
 
-    nm_assert(NM_IS_SETTING_OVS_EXTERNAL_IDS(setting));
-    nm_assert(nm_streq(property_info->name, NM_SETTING_OVS_EXTERNAL_IDS_DATA));
-    nm_assert(nm_streq(setting_name, setting_info->setting_name));
-    nm_assert(nm_streq(setting_name, nm_setting_get_name(setting)));
+    if (NM_IS_SETTING_OVS_EXTERNAL_IDS(setting)) {
+        nm_assert(nm_streq(property_info->name, NM_SETTING_OVS_EXTERNAL_IDS_DATA));
+        is_exid = TRUE;
+    } else {
+        nm_assert(NM_IS_SETTING_OVS_OTHER_CONFIG(setting));
+        nm_assert(nm_streq(property_info->name, NM_SETTING_OVS_OTHER_CONFIG_DATA));
+        is_exid = FALSE;
+    }
 
-    keys = nm_keyfile_plugin_kf_get_keys(info->keyfile, setting_name, &n_keys, NULL);
+    nm_assert(nm_streq(setting_info->setting_name, nm_setting_get_name(setting)));
+
+    keys = nm_keyfile_plugin_kf_get_keys(info->keyfile, setting_info->setting_name, &n_keys, NULL);
 
     for (i = 0; i < n_keys; i++) {
         const char   *key          = keys[i];
@@ -1084,16 +1092,20 @@ _parser_full_ovs_external_ids_data(KeyfileReaderInfo        *info,
         gs_free char *value        = NULL;
         const char   *name;
 
-        if (!NM_STR_HAS_PREFIX(key, OVS_EXTERNAL_IDS_DATA_PREFIX))
+        if (!NM_STR_HAS_PREFIX(key, STRDICT_DATA_PREFIX))
             continue;
 
-        value = nm_keyfile_plugin_kf_get_string(info->keyfile, setting_name, key, NULL);
+        value =
+            nm_keyfile_plugin_kf_get_string(info->keyfile, setting_info->setting_name, key, NULL);
         if (!value)
             continue;
 
-        name = &key[NM_STRLEN(OVS_EXTERNAL_IDS_DATA_PREFIX)];
+        name = &key[NM_STRLEN(STRDICT_DATA_PREFIX)];
         name = nm_keyfile_key_decode(name, &name_to_free);
-        nm_setting_ovs_external_ids_set_data(NM_SETTING_OVS_EXTERNAL_IDS(setting), name, value);
+        if (is_exid)
+            nm_setting_ovs_external_ids_set_data(NM_SETTING_OVS_EXTERNAL_IDS(setting), name, value);
+        else
+            nm_setting_ovs_other_config_set_data(NM_SETTING_OVS_OTHER_CONFIG(setting), name, value);
     }
 }
 
@@ -1102,7 +1114,9 @@ ip_dns_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key)
 {
     int                addr_family;
     gs_strfreev char **list = NULL;
-    gsize              i, n, length;
+    gsize              length;
+    gsize              n;
+    gsize              i;
 
     nm_assert(NM_IS_SETTING_IP4_CONFIG(setting) || NM_IS_SETTING_IP6_CONFIG(setting));
 
@@ -1115,13 +1129,10 @@ ip_dns_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key)
     if (length == 0)
         return;
 
-    addr_family = NM_IS_SETTING_IP4_CONFIG(setting) ? AF_INET : AF_INET6;
-
-    n = 0;
-    for (i = 0; i < length; i++) {
-        NMIPAddr addr;
+    addr_family = NM_SETTING_IP_CONFIG_GET_ADDR_FAMILY(setting);
 
-        if (inet_pton(addr_family, list[i], &addr) <= 0) {
+    for (i = 0, n = 0; i < length; i++) {
+        if (!nm_utils_dnsname_parse(addr_family, list[i], NULL, NULL, NULL)) {
             if (!read_handle_warn(info,
                                   key,
                                   key,
@@ -2009,6 +2020,44 @@ bridge_vlan_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key)
 }
 
 static void
+range_list_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key)
+{
+    gs_unref_ptrarray GPtrArray *ranges = NULL;
+    gs_free char                *value  = NULL;
+    gs_free const char         **strv   = NULL;
+    const char *const           *iter;
+    GError                      *local = NULL;
+    NMRange                     *range;
+
+    value = nm_keyfile_plugin_kf_get_string(info->keyfile, nm_setting_get_name(setting), key, NULL);
+    if (!value || !value[0])
+        return;
+
+    ranges = g_ptr_array_new_with_free_func((GDestroyNotify) nm_range_unref);
+
+    strv = nm_utils_escaped_tokens_split(value, ",");
+    if (strv) {
+        for (iter = strv; *iter; iter++) {
+            range = nm_range_from_str(*iter, &local);
+            if (!range) {
+                read_handle_warn(info,
+                                 key,
+                                 key,
+                                 NM_KEYFILE_WARN_SEVERITY_WARN,
+                                 "invalid range: %s",
+                                 local->message);
+                g_clear_error(&local);
+                continue;
+            }
+            g_ptr_array_add(ranges, range);
+        }
+    }
+
+    if (ranges->len > 0)
+        g_object_set(setting, key, ranges, NULL);
+}
+
+static void
 qdisc_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key)
 {
     const char                  *setting_name = nm_setting_get_name(setting);
@@ -2173,7 +2222,7 @@ write_array_of_uint(GKeyFile *file, NMSetting *setting, const char *key, const G
     nm_keyfile_plugin_kf_set_integer_list_uint(file,
                                                nm_setting_get_name(setting),
                                                key,
-                                               (const guint *) array->data,
+                                               &nm_g_array_first(array, const guint),
                                                array->len);
 }
 
@@ -2346,6 +2395,33 @@ bridge_vlan_writer(KeyfileWriterInfo *info,
 }
 
 static void
+range_list_writer(KeyfileWriterInfo *info, NMSetting *setting, const char *key, const GValue *value)
+{
+    GPtrArray *ranges;
+
+    ranges = g_value_get_boxed(value);
+    if (ranges && ranges->len > 0) {
+        const guint              string_initial_size = ranges->len * 10u;
+        nm_auto_str_buf NMStrBuf string              = NM_STR_BUF_INIT(string_initial_size, FALSE);
+        guint                    i;
+
+        for (i = 0; i < ranges->len; i++) {
+            gs_free char *range_str = NULL;
+
+            range_str = nm_range_to_str(ranges->pdata[i]);
+            if (i > 0)
+                nm_str_buf_append_c(&string, ',');
+            nm_utils_escaped_tokens_escape_strbuf_assert(range_str, ",", &string);
+        }
+
+        nm_keyfile_plugin_kf_set_string(info->keyfile,
+                                        nm_setting_get_name(setting),
+                                        key,
+                                        nm_str_buf_get_str(&string));
+    }
+}
+
+static void
 wired_s390_options_parser_full(KeyfileReaderInfo        *info,
                                const NMMetaSettingInfo  *setting_info,
                                const NMSettInfoProperty *property_info,
@@ -2505,24 +2581,32 @@ tfilter_writer(KeyfileWriterInfo *info, NMSetting *setting, const char *key, con
 }
 
 static void
-_writer_full_ovs_external_ids_data(KeyfileWriterInfo        *info,
-                                   const NMMetaSettingInfo  *setting_info,
-                                   const NMSettInfoProperty *property_info,
-                                   const ParseInfoProperty  *pip,
-                                   NMSetting                *setting)
+_writer_full_strdict_data(KeyfileWriterInfo        *info,
+                          const NMMetaSettingInfo  *setting_info,
+                          const NMSettInfoProperty *property_info,
+                          const ParseInfoProperty  *pip,
+                          NMSetting                *setting)
 {
     GHashTable                *hash;
     NMUtilsNamedValue          data_static[300u / sizeof(NMUtilsNamedValue)];
     gs_free NMUtilsNamedValue *data_free = NULL;
     const NMUtilsNamedValue   *data;
     guint                      data_len;
-    char                       full_key_static[NM_STRLEN(OVS_EXTERNAL_IDS_DATA_PREFIX) + 300u];
+    char                       full_key_static[NM_STRLEN(STRDICT_DATA_PREFIX) + 300u];
     guint                      i;
+    gboolean                   is_exid;
 
-    nm_assert(NM_IS_SETTING_OVS_EXTERNAL_IDS(setting));
-    nm_assert(nm_streq(property_info->name, NM_SETTING_OVS_EXTERNAL_IDS_DATA));
+    if (NM_IS_SETTING_OVS_EXTERNAL_IDS(setting)) {
+        nm_assert(nm_streq(property_info->name, NM_SETTING_OVS_EXTERNAL_IDS_DATA));
+        is_exid = TRUE;
+    } else {
+        nm_assert(NM_IS_SETTING_OVS_OTHER_CONFIG(setting));
+        nm_assert(nm_streq(property_info->name, NM_SETTING_OVS_OTHER_CONFIG_DATA));
+        is_exid = FALSE;
+    }
 
-    hash = _nm_setting_ovs_external_ids_get_data(NM_SETTING_OVS_EXTERNAL_IDS(setting));
+    hash = is_exid ? _nm_setting_ovs_external_ids_get_data(NM_SETTING_OVS_EXTERNAL_IDS(setting))
+                   : _nm_setting_ovs_other_config_get_data(NM_SETTING_OVS_OTHER_CONFIG(setting));
     if (!hash)
         return;
 
@@ -2530,7 +2614,7 @@ _writer_full_ovs_external_ids_data(KeyfileWriterInfo        *info,
     if (data_len == 0)
         return;
 
-    memcpy(full_key_static, OVS_EXTERNAL_IDS_DATA_PREFIX, NM_STRLEN(OVS_EXTERNAL_IDS_DATA_PREFIX));
+    memcpy(full_key_static, STRDICT_DATA_PREFIX, NM_STRLEN(STRDICT_DATA_PREFIX));
 
     for (i = 0; i < data_len; i++) {
         const char   *key                 = data[i].name;
@@ -2544,15 +2628,16 @@ _writer_full_ovs_external_ids_data(KeyfileWriterInfo        *info,
         escaped_key = nm_keyfile_key_encode(key, &escaped_key_to_free);
 
         len = strlen(escaped_key) + 1u;
-        if (len >= G_N_ELEMENTS(full_key_static) - NM_STRLEN(OVS_EXTERNAL_IDS_DATA_PREFIX)) {
-            full_key_free = g_new(char, NM_STRLEN(OVS_EXTERNAL_IDS_DATA_PREFIX) + len);
+        if (len >= G_N_ELEMENTS(full_key_static) - NM_STRLEN(STRDICT_DATA_PREFIX)) {
+            full_key_free = g_new(char, NM_STRLEN(STRDICT_DATA_PREFIX) + len);
             full_key      = full_key_free;
-            memcpy(full_key, OVS_EXTERNAL_IDS_DATA_PREFIX, NM_STRLEN(OVS_EXTERNAL_IDS_DATA_PREFIX));
+            memcpy(full_key, STRDICT_DATA_PREFIX, NM_STRLEN(STRDICT_DATA_PREFIX));
         }
-        memcpy(&full_key[NM_STRLEN(OVS_EXTERNAL_IDS_DATA_PREFIX)], escaped_key, len);
+        memcpy(&full_key[NM_STRLEN(STRDICT_DATA_PREFIX)], escaped_key, len);
 
         nm_keyfile_plugin_kf_set_string(info->keyfile,
-                                        NM_SETTING_OVS_EXTERNAL_IDS_SETTING_NAME,
+                                        is_exid ? NM_SETTING_OVS_EXTERNAL_IDS_SETTING_NAME
+                                                : NM_SETTING_OVS_OTHER_CONFIG_SETTING_NAME,
                                         full_key,
                                         val);
     }
@@ -2935,6 +3020,12 @@ static const ParseInfoSetting *const parse_infos[_NM_META_SETTING_TYPE_NUM] = {
                                                   .parser              = bridge_vlan_parser,
                                                   .writer = bridge_vlan_writer, ), ), ),
     PARSE_INFO_SETTING(
+        NM_META_SETTING_TYPE_OVS_PORT,
+        PARSE_INFO_PROPERTIES(PARSE_INFO_PROPERTY(NM_SETTING_OVS_PORT_TRUNKS,
+                                                  .parser_no_check_key = TRUE,
+                                                  .parser              = range_list_parser,
+                                                  .writer              = range_list_writer, ), ), ),
+    PARSE_INFO_SETTING(
         NM_META_SETTING_TYPE_BRIDGE_PORT,
         PARSE_INFO_PROPERTIES(PARSE_INFO_PROPERTY(NM_SETTING_BRIDGE_PORT_VLANS,
                                                   .parser_no_check_key = TRUE,
@@ -3007,10 +3098,18 @@ static const ParseInfoSetting *const parse_infos[_NM_META_SETTING_TYPE_NUM] = {
         NM_META_SETTING_TYPE_OVS_EXTERNAL_IDS,
         PARSE_INFO_PROPERTIES(PARSE_INFO_PROPERTY(NM_SETTING_OVS_EXTERNAL_IDS_DATA,
                                                   .parser_no_check_key = TRUE,
-                                                  .parser_full = _parser_full_ovs_external_ids_data,
-                                                  .writer_full = _writer_full_ovs_external_ids_data,
-                                                  .has_parser_full = TRUE,
-                                                  .has_writer_full = TRUE, ), ), ),
+                                                  .parser_full         = _parser_full_strdict_data,
+                                                  .writer_full         = _writer_full_strdict_data,
+                                                  .has_parser_full     = TRUE,
+                                                  .has_writer_full     = TRUE, ), ), ),
+    PARSE_INFO_SETTING(
+        NM_META_SETTING_TYPE_OVS_OTHER_CONFIG,
+        PARSE_INFO_PROPERTIES(PARSE_INFO_PROPERTY(NM_SETTING_OVS_OTHER_CONFIG_DATA,
+                                                  .parser_no_check_key = TRUE,
+                                                  .parser_full         = _parser_full_strdict_data,
+                                                  .writer_full         = _writer_full_strdict_data,
+                                                  .has_parser_full     = TRUE,
+                                                  .has_writer_full     = TRUE, ), ), ),
     PARSE_INFO_SETTING(NM_META_SETTING_TYPE_SERIAL,
                        PARSE_INFO_PROPERTIES(PARSE_INFO_PROPERTY(NM_SETTING_SERIAL_PARITY,
                                                                  .parser = parity_parser, ), ), ),
@@ -3188,11 +3287,11 @@ _parse_info_find(NMSetting                *setting,
 
         G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(ParseInfoProperty, property_name) == 0);
 
-        idx = nm_utils_ptrarray_find_binary_search((gconstpointer *) pis->properties,
-                                                   NM_PTRARRAY_LEN(pis->properties),
-                                                   &property_name,
-                                                   nm_strcmp_p_with_data,
-                                                   NULL);
+        idx = nm_ptrarray_find_bsearch((gconstpointer *) pis->properties,
+                                       NM_PTRARRAY_LEN(pis->properties),
+                                       &property_name,
+                                       nm_strcmp_p_with_data,
+                                       NULL);
         if (idx >= 0)
             pip = pis->properties[idx];
     }
@@ -3692,7 +3791,7 @@ _read_setting_wireguard_peer(KeyfileReaderInfo *info)
         gsize    i;
 
         for (i = 0; i < n_sa; i++) {
-            if (!nm_utils_parse_inaddr_prefix_bin(AF_UNSPEC, sa[i], NULL, NULL, NULL)) {
+            if (!nm_inet_parse_with_prefix_bin(AF_UNSPEC, sa[i], NULL, NULL, NULL)) {
                 has_error = TRUE;
                 continue;
             }
@@ -3800,7 +3899,7 @@ nm_keyfile_read_ensure_uuid(NMConnection *connection, const char *fallback_uuid_
     if (nm_setting_connection_get_uuid(s_con))
         return FALSE;
 
-    hashed_uuid = nm_uuid_generate_from_strings("keyfile", fallback_uuid_seed, NULL);
+    hashed_uuid = nm_uuid_generate_from_strings_old("keyfile", fallback_uuid_seed);
     g_object_set(s_con, NM_SETTING_CONNECTION_UUID, hashed_uuid, NULL);
     return TRUE;
 }
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 69daa76c..19082671 100644
--- a/src/libnm-core-impl/nm-meta-setting-base-impl.c
+++ b/src/libnm-core-impl/nm-meta-setting-base-impl.c
@@ -3,6 +3,8 @@
  * Copyright (C) 2017 - 2018 Red Hat, Inc.
  */
 
+#define NM_WANT_NM_ARRAY_FIND_BSEARCH_INLINE
+
 #include "libnm-glib-aux/nm-default-glib-i18n-lib.h"
 
 #include "nm-meta-setting-base.h"
@@ -32,14 +34,16 @@
 #include "nm-setting-ip-tunnel.h"
 #include "nm-setting-ip4-config.h"
 #include "nm-setting-ip6-config.h"
+#include "nm-setting-loopback.h"
 #include "nm-setting-macsec.h"
 #include "nm-setting-macvlan.h"
 #include "nm-setting-match.h"
 #include "nm-setting-olpc-mesh.h"
 #include "nm-setting-ovs-bridge.h"
-#include "nm-setting-ovs-interface.h"
 #include "nm-setting-ovs-dpdk.h"
 #include "nm-setting-ovs-external-ids.h"
+#include "nm-setting-ovs-interface.h"
+#include "nm-setting-ovs-other-config.h"
 #include "nm-setting-ovs-patch.h"
 #include "nm-setting-ovs-port.h"
 #include "nm-setting-ppp.h"
@@ -358,6 +362,13 @@ const NMMetaSettingInfo nm_meta_setting_infos[] = {
             .setting_name      = NM_SETTING_IP_TUNNEL_SETTING_NAME,
             .get_setting_gtype = nm_setting_ip_tunnel_get_type,
         },
+    [NM_META_SETTING_TYPE_LOOPBACK] =
+        {
+            .meta_type         = NM_META_SETTING_TYPE_LOOPBACK,
+            .setting_priority  = NM_SETTING_PRIORITY_HW_BASE,
+            .setting_name      = NM_SETTING_LOOPBACK_SETTING_NAME,
+            .get_setting_gtype = nm_setting_loopback_get_type,
+        },
     [NM_META_SETTING_TYPE_MACSEC] =
         {
             .meta_type         = NM_META_SETTING_TYPE_MACSEC,
@@ -400,6 +411,13 @@ const NMMetaSettingInfo nm_meta_setting_infos[] = {
             .setting_name      = NM_SETTING_OVS_DPDK_SETTING_NAME,
             .get_setting_gtype = nm_setting_ovs_dpdk_get_type,
         },
+    [NM_META_SETTING_TYPE_OVS_OTHER_CONFIG] =
+        {
+            .meta_type         = NM_META_SETTING_TYPE_OVS_OTHER_CONFIG,
+            .setting_priority  = NM_SETTING_PRIORITY_AUX,
+            .setting_name      = NM_SETTING_OVS_OTHER_CONFIG_SETTING_NAME,
+            .get_setting_gtype = nm_setting_ovs_other_config_get_type,
+        },
     [NM_META_SETTING_TYPE_OVS_EXTERNAL_IDS] =
         {
             .meta_type         = NM_META_SETTING_TYPE_OVS_EXTERNAL_IDS,
@@ -608,6 +626,7 @@ const NMMetaSettingType nm_meta_setting_types_by_priority[] = {
     NM_META_SETTING_TYPE_GSM,
     NM_META_SETTING_TYPE_INFINIBAND,
     NM_META_SETTING_TYPE_IP_TUNNEL,
+    NM_META_SETTING_TYPE_LOOPBACK,
     NM_META_SETTING_TYPE_MACSEC,
     NM_META_SETTING_TYPE_MACVLAN,
     NM_META_SETTING_TYPE_OVS_BRIDGE,
@@ -643,6 +662,7 @@ const NMMetaSettingType nm_meta_setting_types_by_priority[] = {
     NM_META_SETTING_TYPE_ETHTOOL,
     NM_META_SETTING_TYPE_MATCH,
     NM_META_SETTING_TYPE_OVS_EXTERNAL_IDS,
+    NM_META_SETTING_TYPE_OVS_OTHER_CONFIG,
     NM_META_SETTING_TYPE_PPP,
     NM_META_SETTING_TYPE_PPPOE,
     NM_META_SETTING_TYPE_TEAM_PORT,
@@ -693,29 +713,31 @@ nm_meta_setting_infos_by_name(const char *name)
     }
 
     G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMMetaSettingInfo, setting_name) == 0);
-    idx = nm_utils_array_find_binary_search(nm_meta_setting_infos,
-                                            sizeof(NMMetaSettingInfo),
-                                            _NM_META_SETTING_TYPE_NUM,
-                                            &name,
-                                            nm_strcmp_p_with_data,
-                                            NULL);
+    idx = nm_array_find_bsearch(nm_meta_setting_infos,
+                                _NM_META_SETTING_TYPE_NUM,
+                                sizeof(NMMetaSettingInfo),
+                                &name,
+                                nm_strcmp_p_with_data,
+                                NULL);
 
     return idx >= 0 ? &nm_meta_setting_infos[idx] : NULL;
 }
 
-const NMMetaSettingInfo *
-nm_meta_setting_infos_by_gtype(GType gtype)
-{
+/*****************************************************************************/
+
 #if _NM_META_SETTING_BASE_IMPL_LIBNM
+static const NMMetaSettingInfo *
+_infos_by_gtype_from_class(GType gtype)
+{
     nm_auto_unref_gtypeclass GTypeClass *gtypeclass_unref = NULL;
     GTypeClass                          *gtypeclass;
     NMSettingClass                      *klass;
 
     if (!g_type_is_a(gtype, NM_TYPE_SETTING))
-        goto out_none;
+        return NULL;
 
     gtypeclass = g_type_class_peek(gtype);
-    if (!gtypeclass)
+    if (G_UNLIKELY(!gtypeclass))
         gtypeclass = gtypeclass_unref = g_type_class_ref(gtype);
 
     nm_assert(NM_IS_SETTING_CLASS(gtypeclass));
@@ -723,38 +745,112 @@ nm_meta_setting_infos_by_gtype(GType gtype)
     klass = (NMSettingClass *) gtypeclass;
 
     if (!klass->setting_info)
-        goto out_none;
+        return NULL;
 
     nm_assert(klass->setting_info->get_setting_gtype);
     nm_assert(klass->setting_info->get_setting_gtype() == gtype);
-
     return klass->setting_info;
+}
+#endif
 
-out_none:
-
-    if (NM_MORE_ASSERTS > 10) {
-        int i;
-
-        /* this might hint to a bug, but it would be expected for NM_TYPE_SETTING
-         * and NM_TYPE_SETTING_IP_CONFIG.
-         *
-         * Assert that we didn't lookup for a gtype, which we would expect to find.
-         * An assertion failure here, hints to a bug in nm_setting_*_class_init().
-         */
-        for (i = 0; i < _NM_META_SETTING_TYPE_NUM; i++)
-            nm_assert(nm_meta_setting_infos[i].get_setting_gtype() != gtype);
-    }
-
-    return NULL;
-#else
-    guint i;
+static const NMMetaSettingInfo *
+_infos_by_gtype_search(GType gtype)
+{
+    int i;
 
-    for (i = 0; i < _NM_META_SETTING_TYPE_NUM; i++) {
+    for (i = 0; i < (int) _NM_META_SETTING_TYPE_NUM; i++) {
         if (nm_meta_setting_infos[i].get_setting_gtype() == gtype)
             return &nm_meta_setting_infos[i];
     }
     return NULL;
+}
+
+typedef struct {
+    GType                    gtype;
+    const NMMetaSettingInfo *setting_info;
+} LookupData;
+
+_nm_always_inline static inline int
+_lookup_data_cmp(gconstpointer ptr_a, gconstpointer ptr_b, gpointer user_data)
+{
+    const GType *const a = ptr_a;
+    const GType *const b = ptr_b;
+
+    nm_assert(a);
+    nm_assert(b);
+    nm_assert(a != b);
+
+    NM_CMP_DIRECT(*a, *b);
+    return 0;
+}
+
+static const NMMetaSettingInfo *
+_infos_by_gtype_binary_search(GType gtype)
+{
+    static LookupData        static_array[_NM_META_SETTING_TYPE_NUM];
+    static const LookupData *static_ptr = NULL;
+    const LookupData        *ptr;
+    gssize                   idx;
+
+again:
+    ptr = g_atomic_pointer_get(&static_ptr);
+    if (G_UNLIKELY(!ptr)) {
+        static gsize g_lock = 0;
+        int          i;
+
+        if (!g_once_init_enter(&g_lock))
+            goto again;
+
+        for (i = 0; i < _NM_META_SETTING_TYPE_NUM; i++) {
+            const NMMetaSettingInfo *m = &nm_meta_setting_infos[i];
+
+            static_array[i] = (LookupData){
+                .gtype        = m->get_setting_gtype(),
+                .setting_info = m,
+            };
+        }
+
+        g_qsort_with_data(static_array,
+                          _NM_META_SETTING_TYPE_NUM,
+                          sizeof(static_array[0]),
+                          _lookup_data_cmp,
+                          NULL);
+
+        ptr = static_array;
+        g_atomic_pointer_set(&static_ptr, ptr);
+
+        g_once_init_leave(&g_lock, 1);
+    }
+
+    idx = nm_array_find_bsearch_inline(ptr,
+                                       _NM_META_SETTING_TYPE_NUM,
+                                       sizeof(ptr[0]),
+                                       &gtype,
+                                       _lookup_data_cmp,
+                                       NULL);
+    if (idx < 0)
+        return NULL;
+
+    return ptr[idx].setting_info;
+}
+
+const NMMetaSettingInfo *
+nm_meta_setting_infos_by_gtype(GType gtype)
+{
+    const NMMetaSettingInfo *setting_info;
+
+#if _NM_META_SETTING_BASE_IMPL_LIBNM
+    setting_info = _infos_by_gtype_from_class(gtype);
+#else
+    setting_info = _infos_by_gtype_binary_search(gtype);
 #endif
+
+    if (NM_MORE_ASSERTS > 20) {
+        nm_assert(setting_info == _infos_by_gtype_search(gtype));
+        nm_assert(setting_info == _infos_by_gtype_binary_search(gtype));
+    }
+
+    return setting_info;
 }
 
 /*****************************************************************************/
diff --git a/src/libnm-core-impl/nm-setting-6lowpan.c b/src/libnm-core-impl/nm-setting-6lowpan.c
index 21b3dbb9..e0b5d650 100644
--- a/src/libnm-core-impl/nm-setting-6lowpan.c
+++ b/src/libnm-core-impl/nm-setting-6lowpan.c
@@ -53,7 +53,7 @@ G_DEFINE_TYPE(NMSetting6Lowpan, nm_setting_6lowpan, NM_TYPE_SETTING)
  *
  * Returns: the #NMSetting6Lowpan:parent property of the setting
  *
- * Since: 1.42, 1.40.4
+ * Since: 1.42
  **/
 const char *
 nm_setting_6lowpan_get_parent(NMSetting6Lowpan *setting)
@@ -141,7 +141,7 @@ nm_setting_6lowpan_init(NMSetting6Lowpan *setting)
  *
  * Returns: (transfer full): the new empty #NMSetting6Lowpan object
  *
- * Since: 1.42, 1.40.4
+ * Since: 1.42
  **/
 NMSetting *
 nm_setting_6lowpan_new(void)
diff --git a/src/libnm-core-impl/nm-setting-8021x.c b/src/libnm-core-impl/nm-setting-8021x.c
index fd9a0961..1bc4de9d 100644
--- a/src/libnm-core-impl/nm-setting-8021x.c
+++ b/src/libnm-core-impl/nm-setting-8021x.c
@@ -67,7 +67,10 @@ _crypto_format_to_ck(NMCryptoFileFormat format)
 
 /*****************************************************************************/
 
-typedef void (*EAPMethodNeedSecretsFunc)(NMSetting8021x *self, GPtrArray *secrets, gboolean phase2);
+typedef void (*EAPMethodNeedSecretsFunc)(NMSetting8021x *self,
+                                         GPtrArray      *secrets,
+                                         gboolean        phase2,
+                                         gboolean        check_rerequest);
 
 typedef gboolean (*EAPMethodValidateFunc)(NMSetting8021x *self, gboolean phase2, GError **error);
 
@@ -1601,7 +1604,7 @@ nm_setting_802_1x_get_phase2_ca_cert_path(NMSetting8021x *setting)
  * nm_setting_802_1x_get_phase2_ca_cert_blob() and
  * nm_setting_802_1x_get_phase2_ca_cert_path().
  *
- * Currently, it's limited to PKCS#11 URIs ('pkcs11' scheme as defined by RFC
+ * Currently, it's limited to PKCS#<!-- -->11 URIs ('pkcs11' scheme as defined by RFC
  * 7512), but may be extended to other schemes in future (such as 'file' URIs
  * for local files and 'data' URIs for inline certificate data).
  *
@@ -1937,7 +1940,7 @@ nm_setting_802_1x_get_phase2_client_cert_path(NMSetting8021x *setting)
  * nm_setting_802_1x_get_phase2_ca_cert_blob() and
  * nm_setting_802_1x_get_phase2_ca_cert_path().
  *
- * Currently, it's limited to PKCS#11 URIs ('pkcs11' scheme as defined by RFC
+ * Currently, it's limited to PKCS#<!-- -->11 URIs ('pkcs11' scheme as defined by RFC
  * 7512), but may be extended to other schemes in future (such as 'file' URIs
  * for local files and 'data' URIs for inline certificate data).
  *
@@ -2173,7 +2176,7 @@ nm_setting_802_1x_get_private_key_path(NMSetting8021x *setting)
  * nm_setting_802_1x_get_private_key_blob() and
  * nm_setting_802_1x_get_private_key_path().
  *
- * Currently, it's limited to PKCS#11 URIs ('pkcs11' scheme as defined by RFC
+ * Currently, it's limited to PKCS#<!-- -->11 URIs ('pkcs11' scheme as defined by RFC
  * 7512), but may be extended to other schemes in future (such as 'file' URIs
  * for local files and 'data' URIs for inline certificate data).
  *
@@ -2376,7 +2379,7 @@ nm_setting_802_1x_get_phase2_private_key_path(NMSetting8021x *setting)
  * nm_setting_802_1x_get_phase2_private_key_blob() and
  * nm_setting_802_1x_get_phase2_private_key_path().
  *
- * Currently, it's limited to PKCS#11 URIs ('pkcs11' scheme as defined by RFC
+ * Currently, it's limited to PKCS#<!-- -->11 URIs ('pkcs11' scheme as defined by RFC
  * 7512), but may be extended to other schemes in future (such as 'file' URIs
  * for local files and 'data' URIs for inline certificate data).
  *
@@ -2500,32 +2503,45 @@ nm_setting_802_1x_get_optional(NMSetting8021x *setting)
 /*****************************************************************************/
 
 static void
-need_secrets_password(NMSetting8021x *self, GPtrArray *secrets, gboolean phase2)
+need_secrets_password(NMSetting8021x *self,
+                      GPtrArray      *secrets,
+                      gboolean        phase2,
+                      gboolean        check_rerequest)
 {
     NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE(self);
 
-    if (nm_str_is_empty(priv->password)
-        && (!priv->password_raw || !g_bytes_get_size(priv->password_raw))) {
+    if (check_rerequest
+        || (nm_str_is_empty(priv->password)
+            && (!priv->password_raw || !g_bytes_get_size(priv->password_raw)))) {
         g_ptr_array_add(secrets, NM_SETTING_802_1X_PASSWORD);
         g_ptr_array_add(secrets, NM_SETTING_802_1X_PASSWORD_RAW);
     }
 }
 
 static void
-need_secrets_sim(NMSetting8021x *self, GPtrArray *secrets, gboolean phase2)
+need_secrets_sim(NMSetting8021x *self,
+                 GPtrArray      *secrets,
+                 gboolean        phase2,
+                 gboolean        check_rerequest)
 {
     NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE(self);
 
-    if (nm_str_is_empty(priv->pin))
+    if (check_rerequest || nm_str_is_empty(priv->pin))
         g_ptr_array_add(secrets, NM_SETTING_802_1X_PIN);
 }
 
 static void
-need_secrets_tls(NMSetting8021x *self, GPtrArray *secrets, gboolean phase2)
+need_secrets_tls(NMSetting8021x *self,
+                 GPtrArray      *secrets,
+                 gboolean        phase2,
+                 gboolean        check_rerequest)
 {
     NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE(self);
     NMSetting8021xCKScheme scheme;
 
+    /* If check_rerequest is TRUE do not return secrets, unless missing.
+     * This secret cannot be wrong. */
+
     if (!NM_FLAGS_HAS(phase2 ? priv->phase2_private_key_password_flags
                              : priv->private_key_password_flags,
                       NM_SETTING_SECRET_FLAG_NOT_REQUIRED)) {
@@ -2719,7 +2735,10 @@ verify_ttls(NMSetting8021x *self, gboolean phase2, GError **error)
 }
 
 static void
-need_secrets_phase2(NMSetting8021x *self, GPtrArray *secrets, gboolean phase2)
+need_secrets_phase2(NMSetting8021x *self,
+                    GPtrArray      *secrets,
+                    gboolean        phase2,
+                    gboolean        check_rerequest)
 {
     NMSetting8021xPrivate *priv   = NM_SETTING_802_1X_GET_PRIVATE(self);
     char                  *method = NULL;
@@ -2740,7 +2759,7 @@ need_secrets_phase2(NMSetting8021x *self, GPtrArray *secrets, gboolean phase2)
         if (!eap_methods_table[i].ns_func)
             continue;
         if (nm_streq(eap_methods_table[i].method, method)) {
-            (*eap_methods_table[i].ns_func)(self, secrets, TRUE);
+            (*eap_methods_table[i].ns_func)(self, secrets, TRUE, check_rerequest);
             break;
         }
     }
@@ -2885,10 +2904,35 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
     }
 
     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,
-                            _("invalid auth flags"));
+        g_set_error(error,
+                    NM_CONNECTION_ERROR,
+                    NM_CONNECTION_ERROR_INVALID_PROPERTY,
+                    _("invalid auth flags: '%d' contains unknown flags"),
+                    priv->phase1_auth_flags);
+        g_prefix_error(error,
+                       "%s.%s: ",
+                       NM_SETTING_802_1X_SETTING_NAME,
+                       NM_SETTING_802_1X_PHASE1_AUTH_FLAGS);
+        return FALSE;
+    }
+
+    if (NM_FLAGS_ALL(priv->phase1_auth_flags,
+                     NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_0_ENABLE
+                         | NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_0_DISABLE)
+        || NM_FLAGS_ALL(priv->phase1_auth_flags,
+                        NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_1_ENABLE
+                            | NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_1_DISABLE)
+        || NM_FLAGS_ALL(priv->phase1_auth_flags,
+                        NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_2_ENABLE
+                            | NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_2_DISABLE)
+        || NM_FLAGS_ALL(priv->phase1_auth_flags,
+                        NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_3_ENABLE
+                            | NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_3_DISABLE)) {
+        g_set_error_literal(
+            error,
+            NM_CONNECTION_ERROR,
+            NM_CONNECTION_ERROR_INVALID_PROPERTY,
+            _("invalid auth flags: both enable and disable are set for the same TLS version"));
         g_prefix_error(error,
                        "%s.%s: ",
                        NM_SETTING_802_1X_SETTING_NAME,
@@ -3030,7 +3074,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
 /*****************************************************************************/
 
 static GPtrArray *
-need_secrets(NMSetting *setting)
+need_secrets(NMSetting *setting, gboolean check_rerequest)
 {
     NMSetting8021x        *self = NM_SETTING_802_1X(setting);
     NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE(self);
@@ -3049,7 +3093,7 @@ need_secrets(NMSetting *setting)
             if (eap_methods_table[i].ns_func == NULL)
                 continue;
             if (!strcmp(eap_methods_table[i].method, method)) {
-                (*eap_methods_table[i].ns_func)(self, secrets, FALSE);
+                (*eap_methods_table[i].ns_func)(self, secrets, FALSE, check_rerequest);
 
                 /* Only break out of the outer loop if this EAP method
                  * needed secrets.
@@ -3296,9 +3340,6 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      *
      * Since: 1.8
      **/
-    /* ---ifcfg-rh---
-     * ---end---
-     */
     _nm_setting_property_define_direct_string(properties_override,
                                               obj_properties,
                                               NM_SETTING_802_1X_CA_CERT_PASSWORD,
@@ -3314,9 +3355,6 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      *
      * Since: 1.8
      **/
-    /* ---ifcfg-rh---
-     * ---end---
-     */
     _nm_setting_property_define_direct_secret_flags(properties_override,
                                                     obj_properties,
                                                     NM_SETTING_802_1X_CA_CERT_PASSWORD_FLAGS,
@@ -3353,9 +3391,10 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      *
      * Substring to be matched against the subject of the certificate presented
      * by the authentication server. When unset, no verification of the
-     * authentication server certificate's subject is performed.  This property
-     * provides little security, if any, and its use is deprecated in favor of
-     * NMSetting8021x:domain-suffix-match.
+     * authentication server certificate's subject is performed. This property
+     * provides little security, if any, and should not be used.
+     *
+     * Deprecated: 1.2: Use #NMSetting8021x:phase2-domain-suffix-match instead.
      **/
     /* ---ifcfg-rh---
      * property: subject-match
@@ -3370,7 +3409,8 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
                                               PROP_SUBJECT_MATCH,
                                               NM_SETTING_PARAM_NONE,
                                               NMSetting8021xPrivate,
-                                              subject_match);
+                                              subject_match,
+                                              .is_deprecated = TRUE, );
 
     /**
      * NMSetting8021x:altsubject-matches:
@@ -3486,9 +3526,6 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      *
      * Since: 1.8
      **/
-    /* ---ifcfg-rh---
-     * ---end---
-     */
     _nm_setting_property_define_direct_string(properties_override,
                                               obj_properties,
                                               NM_SETTING_802_1X_CLIENT_CERT_PASSWORD,
@@ -3504,9 +3541,6 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      *
      * Since: 1.8
      **/
-    /* ---ifcfg-rh---
-     * ---end---
-     */
     _nm_setting_property_define_direct_secret_flags(properties_override,
                                                     obj_properties,
                                                     NM_SETTING_802_1X_CLIENT_CERT_PASSWORD_FLAGS,
@@ -3594,9 +3628,10 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      *
      * Specifies authentication flags to use in "phase 1" outer
      * authentication using #NMSetting8021xAuthFlags options.
-     * The individual TLS versions can be explicitly disabled. If a certain
-     * TLS disable flag is not set, it is up to the supplicant to allow
-     * or forbid it. The TLS options map to tls_disable_tlsv1_x settings.
+     * The individual TLS versions can be explicitly disabled. TLS time checks
+     * can be also disabled. If a certain TLS disable flag is not
+     * set, it is up to the supplicant to allow or forbid it. The TLS options
+     * map to tls_disable_tlsv1_x and tls_disable_time_checks settings.
      * See the wpa_supplicant documentation for more details.
      *
      * Since: 1.8
@@ -3718,9 +3753,6 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      *
      * Since: 1.8
      **/
-    /* ---ifcfg-rh---
-     * ---end---
-     */
     _nm_setting_property_define_direct_string(properties_override,
                                               obj_properties,
                                               NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD,
@@ -3736,9 +3768,6 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      *
      * Since: 1.8
      **/
-    /* ---ifcfg-rh---
-     * ---end---
-     */
     _nm_setting_property_define_direct_secret_flags(properties_override,
                                                     obj_properties,
                                                     NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD_FLAGS,
@@ -3776,9 +3805,10 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      * Substring to be matched against the subject of the certificate presented
      * by the authentication server during the inner "phase 2"
      * authentication. When unset, no verification of the authentication server
-     * certificate's subject is performed.  This property provides little security,
-     * if any, and its use is deprecated in favor of
-     * NMSetting8021x:phase2-domain-suffix-match.
+     * certificate's subject is performed. This property provides little security,
+     * if any, and should not be used.
+     *
+     * Deprecated: 1.2: Use #NMSetting8021x:phase2-domain-suffix-match instead.
      **/
     /* ---ifcfg-rh---
      * property: phase2-subject-match
@@ -3793,7 +3823,8 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
                                               PROP_PHASE2_SUBJECT_MATCH,
                                               NM_SETTING_PARAM_NONE,
                                               NMSetting8021xPrivate,
-                                              phase2_subject_match);
+                                              phase2_subject_match,
+                                              .is_deprecated = TRUE, );
 
     /**
      * NMSetting8021x:phase2-altsubject-matches:
@@ -3913,9 +3944,6 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      *
      * Since: 1.8
      **/
-    /* ---ifcfg-rh---
-     * ---end---
-     */
     _nm_setting_property_define_direct_string(properties_override,
                                               obj_properties,
                                               NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD,
@@ -3931,9 +3959,6 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      *
      * Since: 1.8
      **/
-    /* ---ifcfg-rh---
-     * ---end---
-     */
     _nm_setting_property_define_direct_secret_flags(
         properties_override,
         obj_properties,
diff --git a/src/libnm-core-impl/nm-setting-adsl.c b/src/libnm-core-impl/nm-setting-adsl.c
index 88606b69..ca291389 100644
--- a/src/libnm-core-impl/nm-setting-adsl.c
+++ b/src/libnm-core-impl/nm-setting-adsl.c
@@ -225,12 +225,12 @@ verify_secrets(NMSetting *setting, NMConnection *connection, GError **error)
 }
 
 static GPtrArray *
-need_secrets(NMSetting *setting)
+need_secrets(NMSetting *setting, gboolean check_rerequest)
 {
     NMSettingAdslPrivate *priv    = NM_SETTING_ADSL_GET_PRIVATE(setting);
     GPtrArray            *secrets = NULL;
 
-    if (priv->password && *priv->password)
+    if (!check_rerequest && priv->password && *priv->password)
         return NULL;
 
     if (!(priv->password_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED)) {
diff --git a/src/libnm-core-impl/nm-setting-bond.c b/src/libnm-core-impl/nm-setting-bond.c
index 18b6fefb..54030e0b 100644
--- a/src/libnm-core-impl/nm-setting-bond.c
+++ b/src/libnm-core-impl/nm-setting-bond.c
@@ -70,6 +70,7 @@ static const char *const valid_options_lst[] = {
     NM_SETTING_BOND_OPTION_ARP_INTERVAL,
     NM_SETTING_BOND_OPTION_ARP_IP_TARGET,
     NM_SETTING_BOND_OPTION_ARP_VALIDATE,
+    NM_SETTING_BOND_OPTION_BALANCE_SLB,
     NM_SETTING_BOND_OPTION_PRIMARY,
     NM_SETTING_BOND_OPTION_PRIMARY_RESELECT,
     NM_SETTING_BOND_OPTION_FAIL_OVER_MAC,
@@ -195,6 +196,7 @@ static NM_UTILS_STRING_TABLE_LOOKUP_STRUCT_DEFINE(
     {NM_SETTING_BOND_OPTION_ARP_IP_TARGET, {"", NM_BOND_OPTION_TYPE_IP}},
     {NM_SETTING_BOND_OPTION_ARP_VALIDATE,
      {"none", NM_BOND_OPTION_TYPE_BOTH, 0, 6, _option_default_strv_arp_validate}},
+    {NM_SETTING_BOND_OPTION_BALANCE_SLB, {"0", NM_BOND_OPTION_TYPE_INT, 0, 1}},
     {NM_SETTING_BOND_OPTION_DOWNDELAY, {"0", NM_BOND_OPTION_TYPE_INT, 0, G_MAXINT}},
     {NM_SETTING_BOND_OPTION_FAIL_OVER_MAC,
      {"none", NM_BOND_OPTION_TYPE_BOTH, 0, 2, _option_default_strv_fail_over_mac}},
@@ -344,6 +346,17 @@ _bond_get_option_normalized(NMSettingBond *self, const char *option, gboolean ge
             value = _bond_get_option(self, NM_SETTING_BOND_OPTION_PRIMARY);
             if (!value)
                 value = _bond_get_option(self, NM_SETTING_BOND_OPTION_ACTIVE_SLAVE);
+        } else if (nm_streq(option, NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY)) {
+            if (_nm_utils_ascii_str_to_int64(
+                    _bond_get_option(self, NM_SETTING_BOND_OPTION_BALANCE_SLB),
+                    10,
+                    0,
+                    1,
+                    -1)
+                == 1) {
+                /* balance-slb implies vlan+srcmac */
+                return "5";
+            }
         } else
             value = _bond_get_option(self, option);
 
@@ -506,7 +519,7 @@ validate_ip(const char *name, const char *value, GError **error)
         return FALSE;
     }
     for (i = 0; addrs[i]; i++) {
-        if (!nm_utils_parse_inaddr_bin(AF_INET, addrs[i], NULL, NULL)) {
+        if (!nm_inet_parse_bin(AF_INET, addrs[i], NULL, NULL)) {
             g_set_error(error,
                         NM_CONNECTION_ERROR,
                         NM_CONNECTION_ERROR_INVALID_PROPERTY,
@@ -588,7 +601,7 @@ handle_error:
 /**
  * nm_setting_bond_validate_option:
  * @name: the name of the option to validate
- * @value (allow-none): the value of the option to validate.
+ * @value: (allow-none): the value of the option to validate.
  *
  * Checks whether @name is a valid bond option and @value is a valid value for
  * the @name. If @value is %NULL, the function only validates the option name.
@@ -840,6 +853,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
     const char              *arp_ip_target = NULL;
     const char              *lacp_rate;
     const char              *primary;
+    const char              *s;
     NMBondMode               bond_mode;
     guint                    i;
     const NMUtilsNamedValue *n;
@@ -1067,6 +1081,32 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
         return FALSE;
     }
 
+    s = _bond_get_option(self, NM_SETTING_BOND_OPTION_BALANCE_SLB);
+    if (s && _atoi(s) > 0) {
+        if (bond_mode != NM_BOND_MODE_XOR) {
+            g_set_error(error,
+                        NM_CONNECTION_ERROR,
+                        NM_CONNECTION_ERROR_INVALID_PROPERTY,
+                        _("%s requires bond mode \"%s\""),
+                        NM_SETTING_BOND_OPTION_BALANCE_SLB,
+                        "balance-xor");
+            g_prefix_error(error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
+            return FALSE;
+        }
+        s = _bond_get_option(self, NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY);
+        if (s
+            && _nm_setting_bond_xmit_hash_policy_from_string(s)
+                   != NM_BOND_XMIT_HASH_POLICY_VLAN_SRCMAC) {
+            g_set_error(error,
+                        NM_CONNECTION_ERROR,
+                        NM_CONNECTION_ERROR_INVALID_PROPERTY,
+                        _("%s requires xmit_hash_policy \"vlan+srcmac\""),
+                        NM_SETTING_BOND_OPTION_BALANCE_SLB);
+            g_prefix_error(error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
+            return FALSE;
+        }
+    }
+
     if (!_nm_connection_verify_required_interface_name(connection, error))
         return FALSE;
 
@@ -1233,7 +1273,7 @@ nm_setting_bond_class_init(NMSettingBondClass *klass)
     setting_class->verify = verify;
 
     /**
-     * NMSettingBond:options: (type GHashTable(utf8,utf8)):
+     * NMSettingBond:options: (type GHashTable(utf8,utf8))
      *
      * Dictionary of key/value pairs of bonding options.  Both keys and values
      * must be strings. Option names must contain only alphanumeric characters
@@ -1273,7 +1313,8 @@ nm_setting_bond_class_init(NMSettingBondClass *klass)
      */
     _nm_properties_override_dbus(properties_override,
                                  "interface-name",
-                                 &nm_sett_info_propert_type_deprecated_interface_name);
+                                 &nm_sett_info_propert_type_deprecated_interface_name,
+                                 .dbus_deprecated = TRUE, );
 
     g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
diff --git a/src/libnm-core-impl/nm-setting-bridge.c b/src/libnm-core-impl/nm-setting-bridge.c
index 89611147..c1a2621d 100644
--- a/src/libnm-core-impl/nm-setting-bridge.c
+++ b/src/libnm-core-impl/nm-setting-bridge.c
@@ -109,7 +109,7 @@ G_DEFINE_TYPE(NMSettingBridge, nm_setting_bridge, NM_TYPE_SETTING)
 G_DEFINE_BOXED_TYPE(NMBridgeVlan, nm_bridge_vlan, _nm_bridge_vlan_dup, nm_bridge_vlan_unref)
 
 struct _NMBridgeVlan {
-    guint   refcount;
+    int     refcount;
     guint16 vid_start;
     guint16 vid_end;
     bool    untagged : 1;
@@ -132,6 +132,8 @@ NM_IS_BRIDGE_VLAN(const NMBridgeVlan *self, gboolean also_sealed)
  * Setting @vid_end to 0 is equivalent to setting it to @vid_start
  * and creates a single-id VLAN.
  *
+ * Since 1.42, ref-counting of #NMBridgeVlan is thread-safe.
+ *
  * Returns: (transfer full): the new #NMBridgeVlan object.
  *
  * Since: 1.18
@@ -148,11 +150,12 @@ nm_bridge_vlan_new(guint16 vid_start, guint16 vid_end)
     g_return_val_if_fail(vid_end <= NM_BRIDGE_VLAN_VID_MAX, NULL);
     g_return_val_if_fail(vid_start <= vid_end, NULL);
 
-    vlan            = g_slice_new0(NMBridgeVlan);
-    vlan->refcount  = 1;
-    vlan->vid_start = vid_start;
-    vlan->vid_end   = vid_end;
-
+    vlan  = g_slice_new(NMBridgeVlan);
+    *vlan = (NMBridgeVlan){
+        .refcount  = 1,
+        .vid_start = vid_start,
+        .vid_end   = vid_end,
+    };
     return vlan;
 }
 
@@ -164,6 +167,8 @@ nm_bridge_vlan_new(guint16 vid_start, guint16 vid_end)
  *
  * Returns: the input argument @vlan object.
  *
+ * Since 1.42, ref-counting of #NMBridgeVlan is thread-safe.
+ *
  * Since: 1.18
  **/
 NMBridgeVlan *
@@ -171,9 +176,9 @@ nm_bridge_vlan_ref(NMBridgeVlan *vlan)
 {
     g_return_val_if_fail(NM_IS_BRIDGE_VLAN(vlan, TRUE), NULL);
 
-    nm_assert(vlan->refcount < G_MAXUINT);
+    nm_assert(vlan->refcount < G_MAXINT);
 
-    vlan->refcount++;
+    g_atomic_int_inc(&vlan->refcount);
     return vlan;
 }
 
@@ -184,6 +189,8 @@ nm_bridge_vlan_ref(NMBridgeVlan *vlan)
  * Decreases the reference count of the object.  If the reference count
  * reaches zero the object will be destroyed.
  *
+ * Since 1.42, ref-counting of #NMBridgeVlan is thread-safe.
+ *
  * Since: 1.18
  **/
 void
@@ -191,7 +198,7 @@ nm_bridge_vlan_unref(NMBridgeVlan *vlan)
 {
     g_return_if_fail(NM_IS_BRIDGE_VLAN(vlan, TRUE));
 
-    if (--vlan->refcount == 0)
+    if (g_atomic_int_dec_and_test(&vlan->refcount))
         g_slice_free(NMBridgeVlan, vlan);
 }
 
@@ -1426,10 +1433,9 @@ nm_setting_bridge_class_init(NMSettingBridgeClass *klass)
      * If this field is left unspecified, the "ethernet.cloned-mac-address" is
      * referred instead to generate the initial MAC address. Note that setting
      * "ethernet.cloned-mac-address" anyway overwrites the MAC address of
-     * the bridge later while activating the bridge. Hence, this property
-     * is deprecated.
+     * the bridge later while activating the bridge.
      *
-     * Deprecated: 1.12: Use the ethernet.cloned-mac-address property instead.
+     * Deprecated: 1.12: Use the #NMSettingWired:cloned-mac-address property instead.
      **/
     /* ---keyfile---
      * property: mac-address
@@ -1439,7 +1445,8 @@ nm_setting_bridge_class_init(NMSettingBridgeClass *klass)
      * example: mac-address=00:22:68:12:79:A2
      *  mac-address=0;34;104;18;121;162;
      * ---end---
-     * ---ifcfg-rh---
+     */
+    /* ---ifcfg-rh---
      * property: mac-address
      * variable: BRIDGE_MACADDR(+)
      * description: MAC address of the bridge. Note that this requires a recent
@@ -1454,7 +1461,8 @@ nm_setting_bridge_class_init(NMSettingBridgeClass *klass)
                                                    NM_SETTING_PARAM_INFERRABLE,
                                                    NMSettingBridge,
                                                    _priv.mac_address,
-                                                   .direct_set_string_mac_address_len = ETH_ALEN);
+                                                   .direct_set_string_mac_address_len = ETH_ALEN,
+                                                   .is_deprecated                     = TRUE, );
 
     /**
      * NMSettingBridge:stp:
@@ -1748,7 +1756,8 @@ nm_setting_bridge_class_init(NMSettingBridgeClass *klass)
      */
     _nm_properties_override_dbus(properties_override,
                                  "interface-name",
-                                 &nm_sett_info_propert_type_deprecated_interface_name);
+                                 &nm_sett_info_propert_type_deprecated_interface_name,
+                                 .dbus_deprecated = TRUE, );
 
     /**
      * NMSettingBridge:group-address:
diff --git a/src/libnm-core-impl/nm-setting-cdma.c b/src/libnm-core-impl/nm-setting-cdma.c
index 46f29f96..c32af6b7 100644
--- a/src/libnm-core-impl/nm-setting-cdma.c
+++ b/src/libnm-core-impl/nm-setting-cdma.c
@@ -174,12 +174,12 @@ verify_secrets(NMSetting *setting, NMConnection *connection, GError **error)
 }
 
 static GPtrArray *
-need_secrets(NMSetting *setting)
+need_secrets(NMSetting *setting, gboolean check_rerequest)
 {
     NMSettingCdmaPrivate *priv    = NM_SETTING_CDMA_GET_PRIVATE(setting);
     GPtrArray            *secrets = NULL;
 
-    if (!nm_str_is_empty(priv->password))
+    if (!check_rerequest && !nm_str_is_empty(priv->password))
         return NULL;
 
     if (priv->username) {
diff --git a/src/libnm-core-impl/nm-setting-connection.c b/src/libnm-core-impl/nm-setting-connection.c
index bc14c767..0ec36a89 100644
--- a/src/libnm-core-impl/nm-setting-connection.c
+++ b/src/libnm-core-impl/nm-setting-connection.c
@@ -339,7 +339,7 @@ nm_setting_connection_get_permission(NMSettingConnection *setting,
 
     g_return_val_if_fail(idx < nm_g_array_len(priv->permissions), FALSE);
 
-    permission = &g_array_index(priv->permissions, Permission, idx);
+    permission = &nm_g_array_index(priv->permissions, Permission, idx);
     switch (permission->ptype) {
     case PERM_TYPE_USER:
         NM_SET_OUT(out_ptype, NM_SETTINGS_CONNECTION_PERMISSION_USER);
@@ -384,7 +384,7 @@ nm_setting_connection_permissions_user_allowed(NMSettingConnection *setting, con
     }
 
     for (i = 0; i < priv->permissions->len; i++) {
-        const Permission *permission = &g_array_index(priv->permissions, Permission, i);
+        const Permission *permission = &nm_g_array_index(priv->permissions, Permission, i);
 
         if (permission->ptype == PERM_TYPE_USER && nm_streq(permission->item, uname))
             return TRUE;
@@ -440,7 +440,7 @@ nm_setting_connection_add_permission(NMSettingConnection *setting,
     }
 
     for (i = 0; i < priv->permissions->len; i++) {
-        const Permission *permission = &g_array_index(priv->permissions, Permission, i);
+        const Permission *permission = &nm_g_array_index(priv->permissions, Permission, i);
 
         if (permission->ptype == PERM_TYPE_USER && nm_streq(permission->item, pitem))
             return TRUE;
@@ -511,7 +511,7 @@ nm_setting_connection_remove_permission_by_value(NMSettingConnection *setting,
     priv = NM_SETTING_CONNECTION_GET_PRIVATE(setting);
     if (priv->permissions) {
         for (i = 0; i < priv->permissions->len; i++) {
-            const Permission *permission = &g_array_index(priv->permissions, Permission, i);
+            const Permission *permission = &nm_g_array_index(priv->permissions, Permission, i);
 
             if (permission->ptype == PERM_TYPE_USER && nm_streq(permission->item, pitem)) {
                 g_array_remove_index(priv->permissions, i);
@@ -1023,7 +1023,7 @@ nm_setting_connection_get_dns_over_tls(NMSettingConnection *setting)
  *
  * Returns: the #NMSettingConnection:mptcp-flags property of the setting.
  *
- * Since: 1.42, 1.40.4
+ * Since: 1.42
  **/
 NMMptcpFlags
 nm_setting_connection_get_mptcp_flags(NMSettingConnection *setting)
@@ -1490,7 +1490,7 @@ after_interface_name:
         guint i;
 
         for (i = 0; i < priv->permissions->len; i++) {
-            const Permission *permissions = &g_array_index(priv->permissions, Permission, i);
+            const Permission *permissions = &nm_g_array_index(priv->permissions, Permission, i);
 
             if (permissions->ptype != PERM_TYPE_USER) {
                 g_set_error_literal(error,
@@ -1709,7 +1709,7 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
         strv = g_new(char *, l + 1u);
 
         for (i = 0; i < l; i++)
-            strv[i] = _permission_to_string(&g_array_index(priv->permissions, Permission, i));
+            strv[i] = _permission_to_string(&nm_g_array_index(priv->permissions, Permission, i));
         strv[i] = NULL;
 
         g_value_take_boxed(value, strv);
@@ -1848,6 +1848,18 @@ nm_setting_connection_class_init(NMSettingConnectionClass *klass)
      * be generated by nm_utils_uuid_generate() or
      * nm_uuid_generate_from_string_str().
      **/
+    /* ---nmcli---
+     * property: uuid
+     * format: a valid RFC4122 universally unique identifier (UUID).
+     * description: The connection.uuid is the real identifier of a profile.
+     *   It cannot change and it must be unique. It is therefore often best
+     *   to refer to a profile by UUID, for example with `nmcli connection up uuid $UUID`.
+     *
+     *   The UUID cannot be changed, except in offline mode. In that case,
+     *   the special values "new", "generate" and "" are allowed to generate
+     *   a new random UUID.
+     * ---end---
+     */
     /* ---ifcfg-rh---
      * property: uuid
      * variable: UUID(+)
diff --git a/src/libnm-core-impl/nm-setting-ethtool.c b/src/libnm-core-impl/nm-setting-ethtool.c
index 1db6c335..d5199823 100644
--- a/src/libnm-core-impl/nm-setting-ethtool.c
+++ b/src/libnm-core-impl/nm-setting-ethtool.c
@@ -29,7 +29,7 @@
  *
  * Checks whether @optname is a valid option name for an offload feature.
  *
- * %Returns: %TRUE, if @optname is valid
+ * Returns: %TRUE, if @optname is valid
  *
  * Note that nm_ethtool_optname_is_feature() was first added to the libnm header files
  * in 1.14.0 but forgot to actually add to the library. This happened belatedly in 1.20.0 and
@@ -49,7 +49,7 @@ nm_ethtool_optname_is_feature(const char *optname)
  *
  * Checks whether @optname is a valid option name for a coalesce setting.
  *
- * %Returns: %TRUE, if @optname is valid
+ * Returns: %TRUE, if @optname is valid
  *
  * Since: 1.26
  */
@@ -65,7 +65,7 @@ nm_ethtool_optname_is_coalesce(const char *optname)
  *
  * Checks whether @optname is a valid option name for a ring setting.
  *
- * %Returns: %TRUE, if @optname is valid
+ * Returns: %TRUE, if @optname is valid
  *
  * Since: 1.26
  */
@@ -81,7 +81,7 @@ nm_ethtool_optname_is_ring(const char *optname)
  *
  * Checks whether @optname is a valid option name for a pause setting.
  *
- * %Returns: %TRUE, if @optname is valid
+ * Returns: %TRUE, if @optname is valid
  *
  * Since: 1.32
  */
diff --git a/src/libnm-core-impl/nm-setting-gsm.c b/src/libnm-core-impl/nm-setting-gsm.c
index 15460ccc..3fe5dfa1 100644
--- a/src/libnm-core-impl/nm-setting-gsm.c
+++ b/src/libnm-core-impl/nm-setting-gsm.c
@@ -460,12 +460,12 @@ verify_secrets(NMSetting *setting, NMConnection *connection, GError **error)
 }
 
 static GPtrArray *
-need_secrets(NMSetting *setting)
+need_secrets(NMSetting *setting, gboolean check_rerequest)
 {
     NMSettingGsmPrivate *priv    = NM_SETTING_GSM_GET_PRIVATE(setting);
     GPtrArray           *secrets = NULL;
 
-    if (priv->password && *priv->password)
+    if (!check_rerequest && priv->password && *priv->password)
         return NULL;
 
     if (priv->username) {
@@ -545,7 +545,8 @@ nm_setting_gsm_class_init(NMSettingGsmClass *klass)
                                               PROP_NUMBER,
                                               NM_SETTING_PARAM_NONE,
                                               NMSettingGsmPrivate,
-                                              number);
+                                              number,
+                                              .is_deprecated = TRUE, );
 
     /**
      * NMSettingGsm:username:
@@ -744,10 +745,12 @@ nm_setting_gsm_class_init(NMSettingGsmClass *klass)
     /* Ignore incoming deprecated properties */
     _nm_properties_override_dbus(properties_override,
                                  "allowed-bands",
-                                 &nm_sett_info_propert_type_deprecated_ignore_u);
+                                 &nm_sett_info_propert_type_deprecated_ignore_u,
+                                 .dbus_deprecated = TRUE, );
     _nm_properties_override_dbus(properties_override,
                                  "network-type",
-                                 &nm_sett_info_propert_type_deprecated_ignore_i);
+                                 &nm_sett_info_propert_type_deprecated_ignore_i,
+                                 .dbus_deprecated = TRUE, );
 
     g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
diff --git a/src/libnm-core-impl/nm-setting-hostname.c b/src/libnm-core-impl/nm-setting-hostname.c
index 8a5e50be..399f41a8 100644
--- a/src/libnm-core-impl/nm-setting-hostname.c
+++ b/src/libnm-core-impl/nm-setting-hostname.c
@@ -134,7 +134,7 @@ nm_setting_hostname_init(NMSettingHostname *setting)
  *
  * Returns: (transfer full): the new empty #NMSettingHostname object
  *
- * Since: 1.42, 1.40.4
+ * Since: 1.42
  **/
 NMSetting *
 nm_setting_hostname_new(void)
@@ -233,7 +233,7 @@ nm_setting_hostname_class_init(NMSettingHostnameClass *klass)
      * Since: 1.30
      **/
     /* ---ifcfg-rh---
-     * property: from-dhcp
+     * property: from-dns-lookup
      * variable: HOSTNAME_FROM_DNS_LOOKUP(+)
      * default: missing variable means global default or 1
      * description: whether the system hostname can be determined from reverse
diff --git a/src/libnm-core-impl/nm-setting-infiniband.c b/src/libnm-core-impl/nm-setting-infiniband.c
index 787b838b..410f1f06 100644
--- a/src/libnm-core-impl/nm-setting-infiniband.c
+++ b/src/libnm-core-impl/nm-setting-infiniband.c
@@ -378,7 +378,8 @@ nm_setting_infiniband_class_init(NMSettingInfinibandClass *klass)
      *   or semicolon separated list of 20 decimal bytes (obsolete)
      * example: mac-address= 80:00:00:6d:fe:80:00:00:00:00:00:00:00:02:55:00:70:33:cf:01
      * ---end---
-     * ---ifcfg-rh---
+     */
+    /* ---ifcfg-rh---
      * property: mac-address
      * variable: HWADDR
      * description: IBoIP 20-byte hardware address of the device (in traditional
diff --git a/src/libnm-core-impl/nm-setting-ip-config.c b/src/libnm-core-impl/nm-setting-ip-config.c
index 73f98ab1..5d947e29 100644
--- a/src/libnm-core-impl/nm-setting-ip-config.c
+++ b/src/libnm-core-impl/nm-setting-ip-config.c
@@ -67,7 +67,7 @@ canonicalize_ip_binary(int family, const NMIPAddr *ip, gboolean null_any)
     if (null_any && nm_ip_addr_is_null(family, ip))
         return NULL;
 
-    return nm_utils_inet_ntop_dup(family, ip);
+    return nm_inet_ntop_dup(family, ip);
 }
 
 static gboolean
@@ -80,7 +80,7 @@ valid_ip(int family, const char *ip, NMIPAddr *addr, GError **error)
                     family == AF_INET ? _("Missing IPv4 address") : _("Missing IPv6 address"));
         return FALSE;
     }
-    if (!nm_utils_parse_inaddr_bin(family, ip, NULL, addr)) {
+    if (!nm_inet_parse_bin(family, ip, NULL, addr)) {
         g_set_error(error,
                     NM_CONNECTION_ERROR,
                     NM_CONNECTION_ERROR_FAILED,
@@ -210,7 +210,7 @@ nm_ip_address_new_binary(int family, gconstpointer addr, guint prefix, GError **
     *address = (NMIPAddress){
         .refcount = 1,
         .family   = family,
-        .address  = nm_utils_inet_ntop_dup(family, addr),
+        .address  = nm_inet_ntop_dup(family, addr),
         .prefix   = prefix,
     };
 
@@ -420,7 +420,7 @@ nm_ip_address_set_address(NMIPAddress *address, const char *addr)
 
     if (!valid_ip(address->family, addr, &addr_bin, NULL)) {
         g_return_if_fail(addr != NULL);
-        g_return_if_fail(nm_utils_ipaddr_is_valid(address->family, addr));
+        g_return_if_fail(nm_inet_is_valid(address->family, addr));
         nm_assert_not_reached();
     }
 
@@ -462,7 +462,7 @@ nm_ip_address_set_address_binary(NMIPAddress *address, gconstpointer addr)
     g_return_if_fail(addr != NULL);
 
     g_free(address->address);
-    address->address = nm_utils_inet_ntop_dup(address->family, addr);
+    address->address = nm_inet_ntop_dup(address->family, addr);
 }
 
 /**
@@ -885,7 +885,7 @@ nm_ip_route_get_dest(NMIPRoute *route)
  * Sets the destination property of this route object.
  *
  * @dest must be a valid address of @route's family. If you aren't sure you
- * have a valid address, use nm_utils_ipaddr_is_valid() to check it.
+ * have a valid address, use nm_inet_is_valid() to check it.
  **/
 void
 nm_ip_route_set_dest(NMIPRoute *route, const char *dest)
@@ -895,7 +895,7 @@ nm_ip_route_set_dest(NMIPRoute *route, const char *dest)
     g_return_if_fail(route != NULL);
 
     if (!valid_ip(route->family, dest, &dest_bin, NULL)) {
-        g_return_if_fail(nm_utils_ipaddr_is_valid(route->family, dest));
+        g_return_if_fail(nm_inet_is_valid(route->family, dest));
         nm_assert_not_reached();
     }
 
@@ -937,7 +937,7 @@ nm_ip_route_set_dest_binary(NMIPRoute *route, gconstpointer dest)
     g_return_if_fail(dest != NULL);
 
     g_free(route->dest);
-    route->dest = nm_utils_inet_ntop_dup(route->family, dest);
+    route->dest = nm_inet_ntop_dup(route->family, dest);
 }
 
 /**
@@ -1010,7 +1010,7 @@ nm_ip_route_set_next_hop(NMIPRoute *route, const char *next_hop)
     g_return_if_fail(route != NULL);
 
     if (next_hop && !valid_ip(route->family, next_hop, &next_hop_bin, NULL)) {
-        g_return_if_fail(!next_hop || nm_utils_ipaddr_is_valid(route->family, next_hop));
+        g_return_if_fail(!next_hop || nm_inet_is_valid(route->family, next_hop));
         nm_assert_not_reached();
     }
 
@@ -1278,6 +1278,10 @@ static const NMVariantAttributeSpec *const ip_route_attribute_spec[] = {
                                      .v4          = TRUE,
                                      .v6          = TRUE,
                                      .type_detail = 'T', ),
+    NM_VARIANT_ATTRIBUTE_SPEC_DEFINE(NM_IP_ROUTE_ATTRIBUTE_WEIGHT,
+                                     G_VARIANT_TYPE_UINT32,
+                                     .v4          = TRUE,
+                                     .type_detail = 'w'),
     NM_VARIANT_ATTRIBUTE_SPEC_DEFINE(NM_IP_ROUTE_ATTRIBUTE_WINDOW,
                                      G_VARIANT_TYPE_UINT32,
                                      .v4 = TRUE,
@@ -1299,8 +1303,9 @@ nm_ip_route_get_variant_attribute_spec(void)
 }
 
 typedef struct {
-    int type;
-    int scope;
+    int    type;
+    int    scope;
+    gint16 weight;
 } IPRouteAttrParseData;
 
 static gboolean
@@ -1313,6 +1318,7 @@ _ip_route_attribute_validate(const char           *name,
 {
     const NMVariantAttributeSpec *spec;
     const char                   *string;
+    guint32                       u32;
 
     nm_assert(name);
     nm_assert(value);
@@ -1354,7 +1360,7 @@ _ip_route_attribute_validate(const char           *name,
     switch (spec->type_detail) {
     case 'a': /* IP address */
         string = g_variant_get_string(value, NULL);
-        if (!nm_utils_ipaddr_is_valid(family, string)) {
+        if (!nm_inet_is_valid(family, string)) {
             g_set_error(error,
                         NM_CONNECTION_ERROR,
                         NM_CONNECTION_ERROR_FAILED,
@@ -1386,7 +1392,7 @@ _ip_route_attribute_validate(const char           *name,
                 return FALSE;
             }
         }
-        if (!nm_utils_ipaddr_is_valid(family, addr)) {
+        if (!nm_inet_is_valid(family, addr)) {
             g_set_error(error,
                         NM_CONNECTION_ERROR,
                         NM_CONNECTION_ERROR_FAILED,
@@ -1426,6 +1432,18 @@ _ip_route_attribute_validate(const char           *name,
         if (parse_data)
             parse_data->scope = g_variant_get_byte(value);
         break;
+    case 'w': /* weight */
+        u32 = g_variant_get_uint32(value);
+        if (u32 > 256) {
+            g_set_error_literal(error,
+                                NM_CONNECTION_ERROR,
+                                NM_CONNECTION_ERROR_FAILED,
+                                _("route weight cannot be larger than 256"));
+            return FALSE;
+        }
+        if (parse_data)
+            parse_data->weight = (guint16) u32;
+        break;
     case '\0':
         break;
     default:
@@ -1475,8 +1493,9 @@ _nm_ip_route_attribute_validate_all(const NMIPRoute *route, GError **error)
     guint                      attrs_len;
     guint                      i;
     IPRouteAttrParseData       parse_data = {
-              .type  = RTN_UNICAST,
-              .scope = -1,
+              .type   = RTN_UNICAST,
+              .scope  = -1,
+              .weight = 0,
     };
 
     g_return_val_if_fail(route, FALSE);
@@ -1525,6 +1544,17 @@ _nm_ip_route_attribute_validate_all(const NMIPRoute *route, GError **error)
         break;
     }
 
+    if (parse_data.weight > 0) {
+        if (parse_data.type != RTN_UNICAST) {
+            g_set_error(error,
+                        NM_CONNECTION_ERROR,
+                        NM_CONNECTION_ERROR_INVALID_PROPERTY,
+                        _("a %s route cannot have a ECMP multi-hop \"weight\""),
+                        nm_net_aux_rtnl_rtntype_n2a(parse_data.type));
+            return FALSE;
+        }
+    }
+
     return TRUE;
 }
 
@@ -1537,7 +1567,7 @@ struct NMIPRoutingRule {
     char    *to_str;
     char    *iifname;
     char    *oifname;
-    guint    ref_count;
+    int      ref_count;
     guint32  priority;
     guint32  table;
     gint32   suppress_prefixlength;
@@ -1636,9 +1666,11 @@ nm_ip_routing_rule_new(int addr_family)
  * nm_ip_routing_rule_new_clone:
  * @rule: the #NMIPRoutingRule to clone.
  *
+ * Since 1.42, ref-counting of #NMIPRoutingRule is thread-safe.
+ *
  * Returns: (transfer full): a newly created rule instance with
  *   the same settings as @rule. Note that the instance will
- *   always be unsealred.
+ *   always be unsealed.
  *
  * Since: 1.18
  */
@@ -1704,11 +1736,12 @@ nm_ip_routing_rule_new_clone(const NMIPRoutingRule *rule)
  * @self: (allow-none): the #NMIPRoutingRule instance
  *
  * Increases the reference count of the instance.
- * This is not thread-safe.
  *
  * Returns: (transfer full): the @self argument with incremented
  *  reference count.
  *
+ * Since 1.42, ref-counting of #NMIPRoutingRule is thread-safe.
+ *
  * Since: 1.18
  */
 NMIPRoutingRule *
@@ -1719,8 +1752,9 @@ nm_ip_routing_rule_ref(NMIPRoutingRule *self)
 
     g_return_val_if_fail(NM_IS_IP_ROUTING_RULE(self, TRUE), NULL);
 
-    nm_assert(self->ref_count < G_MAXUINT);
-    self->ref_count++;
+    nm_assert(self->ref_count < G_MAXINT);
+
+    g_atomic_int_inc(&self->ref_count);
     return self;
 }
 
@@ -1730,7 +1764,8 @@ nm_ip_routing_rule_ref(NMIPRoutingRule *self)
  *
  * Decreases the reference count of the instance and destroys
  * the instance if the reference count reaches zero.
- * This is not thread-safe.
+ *
+ * Since 1.42, ref-counting of #NMIPRoutingRule is thread-safe.
  *
  * Since: 1.18
  */
@@ -1742,7 +1777,7 @@ nm_ip_routing_rule_unref(NMIPRoutingRule *self)
 
     g_return_if_fail(NM_IS_IP_ROUTING_RULE(self, TRUE));
 
-    if (--self->ref_count > 0)
+    if (!g_atomic_int_dec_and_test(&self->ref_count))
         return;
 
     g_free(self->from_str);
@@ -1913,7 +1948,7 @@ nm_ip_routing_rule_get_from(const NMIPRoutingRule *self)
     if (!self->from_str) {
         nm_assert(self->from_valid);
         ((NMIPRoutingRule *) self)->from_str =
-            nm_utils_inet_ntop_dup(_ip_routing_rule_get_addr_family(self), &self->from_bin);
+            nm_inet_ntop_dup(_ip_routing_rule_get_addr_family(self), &self->from_bin);
     }
     return self->from_str;
 }
@@ -1970,12 +2005,10 @@ nm_ip_routing_rule_set_from(NMIPRoutingRule *self, const char *from, guint8 len)
     }
 
     nm_clear_g_free(&self->from_str);
-    self->from_has   = TRUE;
-    self->from_len   = len;
-    self->from_valid = nm_utils_parse_inaddr_bin(_ip_routing_rule_get_addr_family(self),
-                                                 from,
-                                                 NULL,
-                                                 &self->from_bin);
+    self->from_has = TRUE;
+    self->from_len = len;
+    self->from_valid =
+        nm_inet_parse_bin(_ip_routing_rule_get_addr_family(self), from, NULL, &self->from_bin);
     if (!self->from_valid)
         self->from_str = g_strdup(from);
 }
@@ -2015,7 +2048,7 @@ nm_ip_routing_rule_get_to(const NMIPRoutingRule *self)
     if (!self->to_str) {
         nm_assert(self->to_valid);
         ((NMIPRoutingRule *) self)->to_str =
-            nm_utils_inet_ntop_dup(_ip_routing_rule_get_addr_family(self), &self->to_bin);
+            nm_inet_ntop_dup(_ip_routing_rule_get_addr_family(self), &self->to_bin);
     }
     return self->to_str;
 }
@@ -2076,7 +2109,7 @@ nm_ip_routing_rule_set_to(NMIPRoutingRule *self, const char *to, guint8 len)
     self->to_has = TRUE;
     self->to_len = len;
     self->to_valid =
-        nm_utils_parse_inaddr_bin(_ip_routing_rule_get_addr_family(self), to, NULL, &self->to_bin);
+        nm_inet_parse_bin(_ip_routing_rule_get_addr_family(self), to, NULL, &self->to_bin);
     if (!self->to_valid)
         self->to_str = g_strdup(to);
 }
@@ -2938,12 +2971,12 @@ _rr_dbus_attr_from_name(const char *name)
         }
     }
 
-    idx = nm_utils_array_find_binary_search(rr_dbus_data,
-                                            sizeof(rr_dbus_data[0]),
-                                            _RR_DBUS_ATTR_NUM,
-                                            &name,
-                                            nm_strcmp_p_with_data,
-                                            NULL);
+    idx = nm_array_find_bsearch(rr_dbus_data,
+                                _RR_DBUS_ATTR_NUM,
+                                sizeof(rr_dbus_data[0]),
+                                &name,
+                                nm_strcmp_p_with_data,
+                                NULL);
     if (idx < 0)
         return _RR_DBUS_ATTR_NUM;
     return idx;
@@ -3169,7 +3202,7 @@ GVariant *
 nm_ip_routing_rule_to_dbus(const NMIPRoutingRule *self)
 {
     GVariantBuilder builder;
-    char            addr_str[NM_UTILS_INET_ADDRSTRLEN];
+    char            addr_str[NM_INET_ADDRSTRLEN];
 
     g_return_val_if_fail(NM_IS_IP_ROUTING_RULE(self, TRUE), NULL);
 
@@ -3222,9 +3255,9 @@ nm_ip_routing_rule_to_dbus(const NMIPRoutingRule *self)
             &builder,
             RR_DBUS_ATTR_FROM,
             g_variant_new_string(self->from_str
-                                     ?: nm_utils_inet_ntop(_ip_routing_rule_get_addr_family(self),
-                                                           &self->from_bin,
-                                                           addr_str)));
+                                     ?: nm_inet_ntop(_ip_routing_rule_get_addr_family(self),
+                                                     &self->from_bin,
+                                                     addr_str)));
         _rr_to_dbus_add(&builder, RR_DBUS_ATTR_FROM_LEN, g_variant_new_byte(self->from_len));
     }
 
@@ -3233,9 +3266,9 @@ nm_ip_routing_rule_to_dbus(const NMIPRoutingRule *self)
             &builder,
             RR_DBUS_ATTR_TO,
             g_variant_new_string(self->to_str
-                                     ?: nm_utils_inet_ntop(_ip_routing_rule_get_addr_family(self),
-                                                           &self->to_bin,
-                                                           addr_str)));
+                                     ?: nm_inet_ntop(_ip_routing_rule_get_addr_family(self),
+                                                     &self->to_bin,
+                                                     addr_str)));
         _rr_to_dbus_add(&builder, RR_DBUS_ATTR_TO_LEN, g_variant_new_byte(self->to_len));
     }
 
@@ -3634,11 +3667,11 @@ next_words_consumed:
     }
 
     if (!NM_IN_STRSET(word_from, NULL, "all")) {
-        if (!nm_utils_parse_inaddr_prefix_bin(addr_family,
-                                              word_from,
-                                              &addr_family,
-                                              &val_from,
-                                              &val_from_len)) {
+        if (!nm_inet_parse_with_prefix_bin(addr_family,
+                                           word_from,
+                                           &addr_family,
+                                           &val_from,
+                                           &val_from_len)) {
             g_set_error_literal(error,
                                 NM_CONNECTION_ERROR,
                                 NM_CONNECTION_ERROR_FAILED,
@@ -3650,11 +3683,11 @@ next_words_consumed:
     }
 
     if (!NM_IN_STRSET(word_to, NULL, "all")) {
-        if (!nm_utils_parse_inaddr_prefix_bin(addr_family,
-                                              word_to,
-                                              &addr_family,
-                                              &val_to,
-                                              &val_to_len)) {
+        if (!nm_inet_parse_with_prefix_bin(addr_family,
+                                           word_to,
+                                           &addr_family,
+                                           &val_to,
+                                           &val_to_len)) {
             g_set_error_literal(error,
                                 NM_CONNECTION_ERROR,
                                 NM_CONNECTION_ERROR_FAILED,
@@ -3753,7 +3786,7 @@ _rr_string_append_inet_addr(NMStrBuf       *str,
                             const NMIPAddr *addr_bin,
                             guint8          addr_len)
 {
-    char addr_str[NM_UTILS_INET_ADDRSTRLEN];
+    char addr_str[NM_INET_ADDRSTRLEN];
 
     if (addr_len == 0) {
         if (required) {
@@ -3768,7 +3801,7 @@ _rr_string_append_inet_addr(NMStrBuf       *str,
     nm_str_buf_append_printf(nm_str_buf_append_required_delimiter(str, ' '),
                              "%s %s",
                              is_from ? "from" : "to",
-                             nm_utils_inet_ntop(addr_family, addr_bin, addr_str));
+                             nm_inet_ntop(addr_family, addr_bin, addr_str));
     if (addr_len != nm_utils_addr_family_to_size(addr_family) * 8) {
         nm_str_buf_append_printf(str, "/%u", addr_len);
     }
@@ -3793,8 +3826,8 @@ nm_ip_routing_rule_to_string(const NMIPRoutingRule       *self,
                              GHashTable                  *extra_args,
                              GError                     **error)
 {
-    int      addr_family;
-    NMStrBuf str;
+    int                      addr_family;
+    nm_auto_str_buf NMStrBuf str = NM_STR_BUF_INIT_A(NM_UTILS_GET_NEXT_REALLOC_SIZE_488, FALSE);
 
     g_return_val_if_fail(NM_IS_IP_ROUTING_RULE(self, TRUE), NULL);
 
@@ -3832,8 +3865,6 @@ nm_ip_routing_rule_to_string(const NMIPRoutingRule       *self,
         }
     }
 
-    str = NM_STR_BUF_INIT(NM_UTILS_GET_NEXT_REALLOC_SIZE_32, FALSE);
-
     if (self->priority_has) {
         nm_str_buf_append_printf(nm_str_buf_append_required_delimiter(&str, ' '),
                                  "priority %u",
@@ -3940,7 +3971,7 @@ nm_ip_routing_rule_to_string(const NMIPRoutingRule       *self,
                                  nm_net_aux_rtnl_rtntype_n2a_maybe_buf(self->action, sbuf));
     }
 
-    return nm_str_buf_finalize(&str, NULL);
+    return nm_str_buf_dup_str(&str);
 }
 
 /*****************************************************************************/
@@ -3967,7 +3998,9 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMSettingIPConfig,
                              PROP_DHCP_TIMEOUT,
                              PROP_REQUIRED_TIMEOUT,
                              PROP_DHCP_IAID,
-                             PROP_DHCP_REJECT_SERVERS, );
+                             PROP_DHCP_REJECT_SERVERS,
+                             PROP_AUTO_ROUTE_EXT_GW,
+                             PROP_REPLACE_LOCAL_RULE, );
 
 G_DEFINE_ABSTRACT_TYPE(NMSettingIPConfig, nm_setting_ip_config, NM_TYPE_SETTING)
 
@@ -3991,9 +4024,6 @@ _NM_SETTING_IP_CONFIG_GET_PRIVATE(NMSettingIPConfig *self)
 
 /*****************************************************************************/
 
-#define NM_SETTING_IP_CONFIG_GET_FAMILY(setting) \
-    (NM_IS_SETTING_IP4_CONFIG(setting) ? AF_INET : AF_INET6)
-
 /**
  * nm_setting_ip_config_get_method:
  * @setting: the #NMSettingIPConfig
@@ -4021,7 +4051,7 @@ nm_setting_ip_config_get_num_dns(NMSettingIPConfig *setting)
 {
     g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), 0);
 
-    return NM_SETTING_IP_CONFIG_GET_PRIVATE(setting)->dns->len;
+    return nm_g_ptr_array_len(NM_SETTING_IP_CONFIG_GET_PRIVATE(setting)->dns);
 }
 
 /**
@@ -4039,11 +4069,35 @@ nm_setting_ip_config_get_dns(NMSettingIPConfig *setting, int idx)
     g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), NULL);
 
     priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
-    g_return_val_if_fail(idx >= 0 && idx < priv->dns->len, NULL);
+    g_return_val_if_fail(idx >= 0 && ((guint) idx) < nm_g_ptr_array_len(priv->dns), NULL);
 
     return priv->dns->pdata[idx];
 }
 
+static gboolean
+_ip_config_add_dns(NMSettingIPConfig *setting, const char *dns)
+{
+    NMSettingIPConfigPrivate *priv;
+    gs_free char             *s_free = NULL;
+    const char               *s;
+
+    nm_assert(NM_IS_SETTING_IP_CONFIG(setting));
+    nm_assert(dns);
+
+    priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
+
+    s = nm_utils_dnsname_normalize(NM_SETTING_IP_CONFIG_GET_ADDR_FAMILY(setting), dns, &s_free);
+    if (!s)
+        s = dns;
+
+    if (nm_strv_ptrarray_contains(priv->dns, s))
+        return FALSE;
+
+    nm_strv_ptrarray_add_string_take(nm_strv_ptrarray_ensure(&priv->dns),
+                                     g_steal_pointer(&s_free) ?: g_strdup(s));
+    return TRUE;
+}
+
 /**
  * nm_setting_ip_config_add_dns:
  * @setting: the #NMSettingIPConfig
@@ -4053,36 +4107,20 @@ nm_setting_ip_config_get_dns(NMSettingIPConfig *setting, int idx)
  *
  * Returns: %TRUE if the DNS server was added; %FALSE if the server was already
  * known
+ *
+ * Before 1.42, setting @dns to an invalid string was treated as user-error.
+ * Now, also invalid DNS values can be set, but will be rejected later during
+ * nm_connection_verify().
  **/
 gboolean
 nm_setting_ip_config_add_dns(NMSettingIPConfig *setting, const char *dns)
 {
-    NMSettingIPConfigPrivate *priv;
-    int                       addr_family;
-    NMIPAddr                  dns_bin;
-    char                      dns_canonical[NM_UTILS_INET_ADDRSTRLEN];
-    guint                     i;
-
     g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), FALSE);
+    g_return_val_if_fail(dns, FALSE);
 
-    addr_family = NM_SETTING_IP_CONFIG_GET_FAMILY(setting);
-
-    if (!valid_ip(addr_family, dns, &dns_bin, NULL)) {
-        g_return_val_if_fail(dns != NULL, FALSE);
-        g_return_val_if_fail(nm_utils_ipaddr_is_valid(addr_family, dns), FALSE);
-        nm_assert_not_reached();
-    }
-
-    priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
-
-    nm_utils_inet_ntop(addr_family, &dns_bin, dns_canonical);
-
-    for (i = 0; i < priv->dns->len; i++) {
-        if (nm_streq(dns_canonical, priv->dns->pdata[i]))
-            return FALSE;
-    }
+    if (!_ip_config_add_dns(setting, dns))
+        return FALSE;
 
-    g_ptr_array_add(priv->dns, g_strdup(dns_canonical));
     _notify(setting, PROP_DNS);
     return TRUE;
 }
@@ -4102,7 +4140,8 @@ nm_setting_ip_config_remove_dns(NMSettingIPConfig *setting, int idx)
     g_return_if_fail(NM_IS_SETTING_IP_CONFIG(setting));
 
     priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
-    g_return_if_fail(idx >= 0 && idx < priv->dns->len);
+
+    g_return_if_fail(idx >= 0 && ((guint) idx) < nm_g_ptr_array_len(priv->dns));
 
     g_ptr_array_remove_index(priv->dns, idx);
     _notify(setting, PROP_DNS);
@@ -4116,38 +4155,39 @@ nm_setting_ip_config_remove_dns(NMSettingIPConfig *setting, int idx)
  * Removes the DNS server @dns.
  *
  * Returns: %TRUE if the DNS server was found and removed; %FALSE if it was not.
+ *
+ * Before 1.42, setting @dns to an invalid string was treated as user-error.
  **/
 gboolean
 nm_setting_ip_config_remove_dns_by_value(NMSettingIPConfig *setting, const char *dns)
 {
     NMSettingIPConfigPrivate *priv;
-    int                       addr_family;
-    NMIPAddr                  dns_bin;
-    char                      dns_canonical[NM_UTILS_INET_ADDRSTRLEN];
-    guint                     i;
+    gssize                    idx;
 
     g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), FALSE);
-
-    addr_family = NM_SETTING_IP_CONFIG_GET_FAMILY(setting);
-
-    if (!valid_ip(addr_family, dns, &dns_bin, NULL)) {
-        g_return_val_if_fail(dns != NULL, FALSE);
-        g_return_val_if_fail(nm_utils_ipaddr_is_valid(addr_family, dns), FALSE);
-        nm_assert_not_reached();
-    }
+    g_return_val_if_fail(dns, FALSE);
 
     priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
 
-    nm_utils_inet_ntop(addr_family, &dns_bin, dns_canonical);
+    /* "priv->dns" can only contain normalized or invalid values. Expect that
+     * "dns" is normalized already, so lookup first for that string. Only
+     * if that fails, fallback to normalize "dns". */
+    idx = nm_strv_ptrarray_find_first(priv->dns, dns);
+    if (idx < 0) {
+        gs_free char *s_free = NULL;
+        const char   *s;
 
-    for (i = 0; i < priv->dns->len; i++) {
-        if (nm_streq(dns_canonical, priv->dns->pdata[i])) {
-            g_ptr_array_remove_index(priv->dns, i);
-            _notify(setting, PROP_DNS);
-            return TRUE;
-        }
+        s = nm_utils_dnsname_normalize(NM_SETTING_IP_CONFIG_GET_ADDR_FAMILY(setting), dns, &s_free);
+        if (s && !nm_streq(dns, s))
+            idx = nm_strv_ptrarray_find_first(priv->dns, dns);
     }
-    return FALSE;
+
+    if (idx < 0)
+        return FALSE;
+
+    g_ptr_array_remove_index(priv->dns, idx);
+    _notify(setting, PROP_DNS);
+    return TRUE;
 }
 
 /**
@@ -4165,7 +4205,7 @@ nm_setting_ip_config_clear_dns(NMSettingIPConfig *setting)
 
     priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
 
-    if (priv->dns->len != 0) {
+    if (nm_g_ptr_array_len(priv->dns) != 0) {
         g_ptr_array_set_size(priv->dns, 0);
         _notify(setting, PROP_DNS);
     }
@@ -4610,7 +4650,7 @@ nm_setting_ip_config_add_address(NMSettingIPConfig *setting, NMIPAddress *addres
 
     g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), FALSE);
     g_return_val_if_fail(address != NULL, FALSE);
-    g_return_val_if_fail(address->family == NM_SETTING_IP_CONFIG_GET_FAMILY(setting), FALSE);
+    g_return_val_if_fail(address->family == NM_SETTING_IP_CONFIG_GET_ADDR_FAMILY(setting), FALSE);
 
     priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
     for (i = 0; i < priv->addresses->len; i++) {
@@ -4767,7 +4807,7 @@ nm_setting_ip_config_add_route(NMSettingIPConfig *setting, NMIPRoute *route)
 
     g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), FALSE);
     g_return_val_if_fail(route != NULL, FALSE);
-    g_return_val_if_fail(route->family == NM_SETTING_IP_CONFIG_GET_FAMILY(setting), FALSE);
+    g_return_val_if_fail(route->family == NM_SETTING_IP_CONFIG_GET_ADDR_FAMILY(setting), FALSE);
 
     priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
     for (i = 0; i < priv->routes->len; i++) {
@@ -4963,7 +5003,7 @@ nm_setting_ip_config_add_routing_rule(NMSettingIPConfig *setting, NMIPRoutingRul
     g_return_if_fail(NM_IS_SETTING_IP_CONFIG(setting));
     g_return_if_fail(NM_IS_IP_ROUTING_RULE(routing_rule, TRUE));
     g_return_if_fail(_ip_routing_rule_get_addr_family(routing_rule)
-                     == NM_SETTING_IP_CONFIG_GET_FAMILY(setting));
+                     == NM_SETTING_IP_CONFIG_GET_ADDR_FAMILY(setting));
 
     priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
 
@@ -5021,7 +5061,7 @@ nm_setting_ip_config_clear_routing_rules(NMSettingIPConfig *setting)
 }
 
 static GVariant *
-_routing_rules_dbus_only_synth(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
+routing_rules_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
     NMSettingIPConfig        *self = NM_SETTING_IP_CONFIG(setting);
     NMSettingIPConfigPrivate *priv;
@@ -5052,7 +5092,7 @@ _routing_rules_dbus_only_synth(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 }
 
 static gboolean
-_routing_rules_dbus_only_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
+routing_rules_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 {
     GVariantIter iter_rules;
     GVariant    *rule_var;
@@ -5295,7 +5335,7 @@ nm_setting_ip_config_get_required_timeout(NMSettingIPConfig *setting)
  *
  * Returns: the configured DHCP IAID (Identity Association Identifier)
  *
- * Since: 1.42, 1.40.4
+ * Since: 1.42
  **/
 const char *
 nm_setting_ip_config_get_dhcp_iaid(NMSettingIPConfig *setting)
@@ -5390,6 +5430,38 @@ nm_setting_ip_config_clear_dhcp_reject_servers(NMSettingIPConfig *setting)
     }
 }
 
+/**
+ * nm_setting_ip_config_get_auto_route_ext_gw:
+ * @setting: the #NMSettingIPConfig
+ *
+ * Returns: the #NMSettingIPConfig:auto-route-ext-gw property of the setting
+ *
+ * Since: 1.42
+ **/
+NMTernary
+nm_setting_ip_config_get_auto_route_ext_gw(NMSettingIPConfig *setting)
+{
+    g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), NM_TERNARY_DEFAULT);
+
+    return NM_SETTING_IP_CONFIG_GET_PRIVATE(setting)->auto_route_ext_gw;
+}
+
+/**
+ * nm_setting_ip_config_get_replace_local_rule:
+ * @setting: the #NMSettingIPConfig
+ *
+ * Returns: the #NMSettingIPConfig:replace-local-rule property of the setting
+ *
+ * Since: 1.44, 1.42.2
+ **/
+NMTernary
+nm_setting_ip_config_get_replace_local_rule(NMSettingIPConfig *setting)
+{
+    g_return_val_if_fail(NM_IS_SETTING_IP_CONFIG(setting), NM_TERNARY_DEFAULT);
+
+    return NM_SETTING_IP_CONFIG_GET_PRIVATE(setting)->replace_local_rule;
+}
+
 static gboolean
 verify_label(const char *label)
 {
@@ -5442,20 +5514,26 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
     }
 
     /* Validate DNS */
-    for (i = 0; i < priv->dns->len; i++) {
-        const char *dns = priv->dns->pdata[i];
-
-        if (!nm_utils_ipaddr_is_valid(NM_SETTING_IP_CONFIG_GET_FAMILY(setting), dns)) {
-            g_set_error(error,
-                        NM_CONNECTION_ERROR,
-                        NM_CONNECTION_ERROR_INVALID_PROPERTY,
-                        _("%d. DNS server address is invalid"),
-                        (int) (i + 1));
-            g_prefix_error(error,
-                           "%s.%s: ",
-                           nm_setting_get_name(setting),
-                           NM_SETTING_IP_CONFIG_DNS);
-            return FALSE;
+    if (priv->dns) {
+        for (i = 0; i < priv->dns->len; i++) {
+            const char *dns = priv->dns->pdata[i];
+
+            if (!nm_utils_dnsname_parse(NM_SETTING_IP_CONFIG_GET_ADDR_FAMILY(setting),
+                                        dns,
+                                        NULL,
+                                        NULL,
+                                        NULL)) {
+                g_set_error(error,
+                            NM_CONNECTION_ERROR,
+                            NM_CONNECTION_ERROR_INVALID_PROPERTY,
+                            _("%u. DNS server address is invalid"),
+                            (i + 1u));
+                g_prefix_error(error,
+                               "%s.%s: ",
+                               nm_setting_get_name(setting),
+                               NM_SETTING_IP_CONFIG_DNS);
+                return FALSE;
+            }
         }
     }
 
@@ -5464,7 +5542,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
         NMIPAddress *addr = (NMIPAddress *) priv->addresses->pdata[i];
         GVariant    *label;
 
-        if (nm_ip_address_get_family(addr) != NM_SETTING_IP_CONFIG_GET_FAMILY(setting)) {
+        if (nm_ip_address_get_family(addr) != NM_SETTING_IP_CONFIG_GET_ADDR_FAMILY(setting)) {
             g_set_error(error,
                         NM_CONNECTION_ERROR,
                         NM_CONNECTION_ERROR_INVALID_PROPERTY,
@@ -5521,7 +5599,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
             return FALSE;
         }
 
-        if (!nm_utils_ipaddr_is_valid(NM_SETTING_IP_CONFIG_GET_FAMILY(setting), priv->gateway)) {
+        if (!nm_inet_is_valid(NM_SETTING_IP_CONFIG_GET_ADDR_FAMILY(setting), priv->gateway)) {
             g_set_error_literal(error,
                                 NM_CONNECTION_ERROR,
                                 NM_CONNECTION_ERROR_INVALID_PROPERTY,
@@ -5539,7 +5617,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
         gs_free_error GError *local = NULL;
         NMIPRoute            *route = (NMIPRoute *) priv->routes->pdata[i];
 
-        if (nm_ip_route_get_family(route) != NM_SETTING_IP_CONFIG_GET_FAMILY(setting)) {
+        if (nm_ip_route_get_family(route) != NM_SETTING_IP_CONFIG_GET_ADDR_FAMILY(setting)) {
             g_set_error(error,
                         NM_CONNECTION_ERROR,
                         NM_CONNECTION_ERROR_INVALID_PROPERTY,
@@ -5572,7 +5650,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
             gs_free_error GError *local = NULL;
 
             if (_ip_routing_rule_get_addr_family(rule)
-                != NM_SETTING_IP_CONFIG_GET_FAMILY(setting)) {
+                != NM_SETTING_IP_CONFIG_GET_ADDR_FAMILY(setting)) {
                 g_set_error(error,
                             NM_CONNECTION_ERROR,
                             NM_CONNECTION_ERROR_INVALID_PROPERTY,
@@ -5629,7 +5707,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
 
     if (priv->dhcp_hostname_flags != (NMDhcpHostnameFlags) priv->dhcp_hostname_flags
         || !_nm_utils_validate_dhcp_hostname_flags(priv->dhcp_hostname_flags,
-                                                   NM_SETTING_IP_CONFIG_GET_FAMILY(setting),
+                                                   NM_SETTING_IP_CONFIG_GET_ADDR_FAMILY(setting),
                                                    error)) {
         g_prefix_error(error,
                        "%s.%s: ",
@@ -5640,7 +5718,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
 
     /* Validate reject servers */
     if (priv->dhcp_reject_servers && priv->dhcp_reject_servers->len != 0) {
-        if (NM_SETTING_IP_CONFIG_GET_FAMILY(setting) != AF_INET) {
+        if (NM_SETTING_IP_CONFIG_GET_ADDR_FAMILY(setting) != AF_INET) {
             g_set_error_literal(error,
                                 NM_CONNECTION_ERROR,
                                 NM_CONNECTION_ERROR_INVALID_PROPERTY,
@@ -5653,16 +5731,16 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
         }
 
         for (i = 0; i < priv->dhcp_reject_servers->len; i++) {
-            if (!nm_utils_parse_inaddr_prefix(
-                    NM_SETTING_IP_CONFIG_GET_FAMILY(setting),
-                    g_array_index(priv->dhcp_reject_servers, const char *, i),
+            if (!nm_inet_parse_with_prefix_str(
+                    NM_SETTING_IP_CONFIG_GET_ADDR_FAMILY(setting),
+                    nm_g_array_index(priv->dhcp_reject_servers, const char *, i),
                     NULL,
                     NULL)) {
                 g_set_error(error,
                             NM_CONNECTION_ERROR,
                             NM_CONNECTION_ERROR_INVALID_PROPERTY,
                             _("'%s' is not a valid IP or subnet"),
-                            g_array_index(priv->dhcp_reject_servers, const char *, i));
+                            nm_g_array_index(priv->dhcp_reject_servers, const char *, i));
                 g_prefix_error(error,
                                "%s.%s: ",
                                nm_setting_get_name(setting),
@@ -5736,6 +5814,20 @@ _nm_setting_ip_config_compare_fcn_routes(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm
     return TRUE;
 }
 
+NMTernary
+_nm_setting_ip_config_compare_fcn_dns(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil)
+{
+    if (NM_FLAGS_HAS(flags, NM_SETTING_COMPARE_FLAG_INFERRABLE))
+        return NM_TERNARY_DEFAULT;
+
+    if (!set_b)
+        return TRUE;
+
+    return (nm_strv_ptrarray_cmp(NM_SETTING_IP_CONFIG_GET_PRIVATE(set_a)->dns,
+                                 NM_SETTING_IP_CONFIG_GET_PRIVATE(set_b)->dns)
+            == 0);
+}
+
 static NMTernary
 compare_fcn_routing_rules(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil)
 {
@@ -5838,6 +5930,35 @@ _nm_setting_property_from_dbus_fcn_direct_ip_config_gateway(
                                                      error);
 }
 
+static GVariant *
+dns_data_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
+{
+    GPtrArray *arr;
+
+    if (!_nm_connection_serialize_non_secret(flags))
+        return NULL;
+
+    arr = _nm_setting_ip_config_get_dns_array(NM_SETTING_IP_CONFIG(setting));
+    if (nm_g_ptr_array_len(arr) == 0)
+        return NULL;
+    return g_variant_new_strv((const char *const *) arr->pdata, arr->len);
+}
+
+static gboolean
+dns_data_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
+{
+    gs_free const char **strv = NULL;
+
+    if (_nm_setting_use_legacy_property(setting, connection_dict, "dns", "dns-data")) {
+        *out_is_modified = FALSE;
+        return TRUE;
+    }
+
+    strv = g_variant_get_strv(value, NULL);
+    g_object_set(setting, NM_SETTING_IP_CONFIG_DNS, strv, NULL);
+    return TRUE;
+}
+
 GArray *
 _nm_sett_info_property_override_create_array_ip_config(int addr_family)
 {
@@ -5880,16 +6001,21 @@ _nm_sett_info_property_override_create_array_ip_config(int addr_family)
     /* ---dbus---
      * property: routing-rules
      * format: array of 'a{sv}'
-     * description: Array of dictionaries for routing rules.
+     * description: Array of dictionaries for routing rules. Each routing rule
+     *  supports the following options: action (y), dport-end (q),
+     *  dport-start (q), family (i), from (s), from-len (y), fwmark (u),
+     *  fwmask (u), iifname (s), invert (b), ipproto (s), oifname (s),
+     *  priority (u), sport-end (q), sport-start (q), supress-prefixlength (i),
+     *  table (u), to (s), tos (y), to-len (y), range-end (u), range-start (u).
      * ---end---
      */
     _nm_properties_override_dbus(
         properties_override,
         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,
+                                       .to_dbus_fcn   = routing_rules_to_dbus,
                                        .compare_fcn   = compare_fcn_routing_rules,
-                                       .from_dbus_fcn = _routing_rules_dbus_only_set, ));
+                                       .from_dbus_fcn = routing_rules_from_dbus, ));
 
     _nm_properties_override_gobj(
         properties_override,
@@ -5919,6 +6045,21 @@ _nm_sett_info_property_override_create_array_ip_config(int addr_family)
         .direct_offset =
             NM_STRUCT_OFFSET_ENSURE_TYPE(bool, NMSettingIPConfigPrivate, ignore_auto_dns));
 
+    /* ---dbus---
+     * property: dns-data
+     * format: array of strings
+     * description: Array of DNS name servers. This replaces the deprecated
+     *   "dns" property. Each name server can also contain a DoT server name.
+     * ---end---
+     */
+    _nm_properties_override_dbus(
+        properties_override,
+        "dns-data",
+        NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("as"),
+                                       .to_dbus_fcn   = dns_data_to_dbus,
+                                       .from_dbus_fcn = dns_data_from_dbus,
+                                       .compare_fcn   = _nm_setting_property_compare_fcn_ignore, ));
+
     _nm_properties_override_gobj(
         properties_override,
         obj_properties[PROP_DNS_PRIORITY],
@@ -5974,6 +6115,20 @@ _nm_sett_info_property_override_create_array_ip_config(int addr_family)
         &nm_sett_info_propert_type_direct_boolean,
         .direct_offset = NM_STRUCT_OFFSET_ENSURE_TYPE(bool, NMSettingIPConfigPrivate, may_fail));
 
+    _nm_properties_override_gobj(
+        properties_override,
+        obj_properties[PROP_AUTO_ROUTE_EXT_GW],
+        &nm_sett_info_propert_type_direct_enum,
+        .direct_offset =
+            NM_STRUCT_OFFSET_ENSURE_TYPE(int, NMSettingIPConfigPrivate, auto_route_ext_gw));
+
+    _nm_properties_override_gobj(
+        properties_override,
+        obj_properties[PROP_REPLACE_LOCAL_RULE],
+        &nm_sett_info_propert_type_direct_enum,
+        .direct_offset =
+            NM_STRUCT_OFFSET_ENSURE_TYPE(int, NMSettingIPConfigPrivate, replace_local_rule));
+
     return properties_override;
 }
 
@@ -6028,9 +6183,17 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
 
     switch (prop_id) {
     case PROP_DNS:
-        g_ptr_array_unref(priv->dns);
-        priv->dns = nm_strv_to_ptrarray(g_value_get_boxed(value));
+    {
+        gs_unref_ptrarray GPtrArray *dns_old = NULL;
+
+        dns_old = g_steal_pointer(&priv->dns);
+        strv    = g_value_get_boxed(value);
+        if (strv) {
+            for (i = 0; strv[i]; i++)
+                _ip_config_add_dns(setting, strv[i]);
+        }
         break;
+    }
     case PROP_DNS_SEARCH:
         g_ptr_array_unref(priv->dns_search);
         priv->dns_search = nm_strv_to_ptrarray(g_value_get_boxed(value));
@@ -6082,7 +6245,6 @@ _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);
@@ -6100,7 +6262,7 @@ finalize(GObject *object)
     NMSettingIPConfig        *self = NM_SETTING_IP_CONFIG(object);
     NMSettingIPConfigPrivate *priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(self);
 
-    g_ptr_array_unref(priv->dns);
+    nm_g_ptr_array_unref(priv->dns);
     g_ptr_array_unref(priv->dns_search);
     nm_g_ptr_array_unref(priv->dns_options);
     g_ptr_array_unref(priv->addresses);
@@ -6159,12 +6321,18 @@ nm_setting_ip_config_class_init(NMSettingIPConfigClass *klass)
      * NMSettingIPConfig:dns:
      *
      * Array of IP addresses of DNS servers.
+     *
+     * For DoT (DNS over TLS), the SNI server name can be specified by appending
+     * "#example.com" to the IP address of the DNS server. This currently only has
+     * effect when using systemd-resolved.
      **/
-    obj_properties[PROP_DNS] = g_param_spec_boxed(NM_SETTING_IP_CONFIG_DNS,
-                                                  "",
-                                                  "",
-                                                  G_TYPE_STRV,
-                                                  G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    obj_properties[PROP_DNS] =
+        g_param_spec_boxed(NM_SETTING_IP_CONFIG_DNS,
+                           "",
+                           "",
+                           G_TYPE_STRV,
+                           /* On D-Bus, "dns" is deprecated for "dns-data". */
+                           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
 
     /**
      * NMSettingIPConfig:dns-search:
@@ -6295,12 +6463,9 @@ nm_setting_ip_config_class_init(NMSettingIPConfigClass *klass)
                            "",
                            "",
                            G_TYPE_PTR_ARRAY,
-                           /* "addresses" is a legacy D-Bus property, because the
-                            * "addresses" GObject property normally gets set from
-                            * the "address-data" D-Bus property...
-                            */
-                           G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | NM_SETTING_PARAM_LEGACY
-                               | G_PARAM_STATIC_STRINGS);
+                           /* On D-Bus, "addresses" is deprecated for "address-data". */
+                           G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE
+                               | NM_SETTING_PARAM_UNUSED1 | G_PARAM_STATIC_STRINGS);
 
     /**
      * NMSettingIPConfig:gateway:
@@ -6322,6 +6487,8 @@ nm_setting_ip_config_class_init(NMSettingIPConfigClass *klass)
         "",
         "",
         NULL,
+        /* On D-Bus, the legacy property "addresses" contains the gateway.
+         * This was replaced by "address-data" and "gateway". */
         G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS);
 
     /**
@@ -6334,9 +6501,9 @@ nm_setting_ip_config_class_init(NMSettingIPConfigClass *klass)
                            "",
                            "",
                            G_TYPE_PTR_ARRAY,
-                           G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE |
-                               /* See :addresses above Re: LEGACY */
-                               NM_SETTING_PARAM_LEGACY | G_PARAM_STATIC_STRINGS);
+                           /* On D-Bus, "routes" is deprecated for "route-data". */
+                           G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE
+                               | NM_SETTING_PARAM_UNUSED1 | G_PARAM_STATIC_STRINGS);
 
     /**
      * NMSettingIPConfig:route-metric:
@@ -6559,18 +6726,28 @@ nm_setting_ip_config_class_init(NMSettingIPConfigClass *klass)
     /**
      * NMSettingIPConfig:dhcp-iaid:
      *
-     * A string containing the "Identity Association Identifier" (IAID) used
-     * by the DHCP client. The property is a 32-bit decimal value or a
-     * special value among "mac", "perm-mac", "ifname" and "stable". When
-     * set to "mac" (or "perm-mac"), the last 4 bytes of the current (or
-     * permanent) MAC address are used as IAID. When set to "ifname", the
-     * IAID is computed by hashing the interface name. The special value
-     * "stable" can be used to generate an IAID based on the stable-id (see
-     * connection.stable-id), a per-host key and the interface name. When
-     * the property is unset, the value from global configuration is used;
-     * if no global default is set then the IAID is assumed to be
-     * "ifname". Note that at the moment this property is ignored for IPv6
-     * by dhclient, which always derives the IAID from the MAC address.
+     * A string containing the "Identity Association Identifier" (IAID) used by
+     * the DHCP client. The string can be a 32-bit number (either decimal,
+     * hexadecimal or or as colon separated hexadecimal numbers). Alternatively
+     * it can be set to the special values "mac", "perm-mac", "ifname" or
+     * "stable". When set to "mac" (or "perm-mac"), the last 4 bytes of the
+     * current (or permanent) MAC address are used as IAID. When set to
+     * "ifname", the IAID is computed by hashing the interface name. The
+     * special value "stable" can be used to generate an IAID based on the
+     * stable-id (see connection.stable-id), a per-host key and the interface
+     * name. When the property is unset, the value from global configuration is
+     * used; if no global default is set then the IAID is assumed to be
+     * "ifname".
+     *
+     * For DHCPv4, the IAID is only used with "ipv4.dhcp-client-id"
+     * values "duid" and "ipv6-duid" to generate the client-id.
+     *
+     * For DHCPv6, note that at the moment this property is
+     * only supported by the "internal" DHCPv6 plugin. The "dhclient" DHCPv6
+     * plugin always derives the IAID from the MAC address.
+     *
+     * The actually used DHCPv6 IAID for a currently activated interface is
+     * exposed in the lease information of the device.
      *
      * Since: 1.22
      **/
@@ -6635,5 +6812,40 @@ nm_setting_ip_config_class_init(NMSettingIPConfigClass *klass)
                            G_TYPE_STRV,
                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
 
+    /**
+     * NMSettingIPConfig:auto-route-ext-gw:
+     *
+     * VPN connections will default to add the route automatically unless this
+     * setting is set to %FALSE.
+     *
+     * For other connection types, adding such an automatic route is currently
+     * not supported and setting this to %TRUE has no effect.
+     *
+     * Since: 1.42
+     */
+    obj_properties[PROP_AUTO_ROUTE_EXT_GW] =
+        g_param_spec_enum(NM_SETTING_IP_CONFIG_AUTO_ROUTE_EXT_GW,
+                          "",
+                          "",
+                          NM_TYPE_TERNARY,
+                          NM_TERNARY_DEFAULT,
+                          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+    /**
+     * NMSettingIPConfig:replace-local-rule:
+     *
+     * Connections will default to keep the autogenerated priority 0 local rule
+     * unless this setting is set to %TRUE.
+     *
+     * Since: 1.44, 1.42.2
+     */
+    obj_properties[PROP_REPLACE_LOCAL_RULE] =
+        g_param_spec_enum(NM_SETTING_IP_CONFIG_REPLACE_LOCAL_RULE,
+                          "",
+                          "",
+                          NM_TYPE_TERNARY,
+                          NM_TERNARY_DEFAULT,
+                          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
     g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 }
diff --git a/src/libnm-core-impl/nm-setting-ip-tunnel.c b/src/libnm-core-impl/nm-setting-ip-tunnel.c
index 73416813..7fb8b017 100644
--- a/src/libnm-core-impl/nm-setting-ip-tunnel.c
+++ b/src/libnm-core-impl/nm-setting-ip-tunnel.c
@@ -28,6 +28,7 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_PARENT,
                                   PROP_OUTPUT_KEY,
                                   PROP_ENCAPSULATION_LIMIT,
                                   PROP_FLOW_LABEL,
+                                  PROP_FWMARK,
                                   PROP_MTU,
                                   PROP_FLAGS, );
 
@@ -41,6 +42,7 @@ typedef struct {
     guint32 tos;
     guint32 encapsulation_limit;
     guint32 flow_label;
+    guint32 fwmark;
     guint32 mode;
     guint32 mtu;
     guint32 flags;
@@ -240,7 +242,7 @@ nm_setting_ip_tunnel_get_output_key(NMSettingIPTunnel *setting)
  *
  * Returns: the encapsulation limit value
  *
- * Since: 1.42, 1.40.4
+ * Since: 1.42
  **/
 guint
 nm_setting_ip_tunnel_get_encapsulation_limit(NMSettingIPTunnel *setting)
@@ -258,7 +260,7 @@ nm_setting_ip_tunnel_get_encapsulation_limit(NMSettingIPTunnel *setting)
  *
  * Returns: the flow label value
  *
- * Since: 1.42, 1.40.4
+ * Since: 1.42
  **/
 guint
 nm_setting_ip_tunnel_get_flow_label(NMSettingIPTunnel *setting)
@@ -269,6 +271,24 @@ nm_setting_ip_tunnel_get_flow_label(NMSettingIPTunnel *setting)
 }
 
 /**
+ * nm_setting_ip_tunnel_get_fwmark:
+ * @setting: the #NMSettingIPTunnel
+ *
+ * Returns the #NMSettingIPTunnel:fwmark property of the setting.
+ *
+ * Returns: the fwmark value
+ *
+ * Since: 1.42
+ **/
+guint32
+nm_setting_ip_tunnel_get_fwmark(NMSettingIPTunnel *setting)
+{
+    g_return_val_if_fail(NM_IS_SETTING_IP_TUNNEL(setting), 0);
+
+    return NM_SETTING_IP_TUNNEL_GET_PRIVATE(setting)->fwmark;
+}
+
+/**
  * nm_setting_ip_tunnel_get_mtu:
  * @setting: the #NMSettingIPTunnel
  *
@@ -366,7 +386,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
         return FALSE;
     }
 
-    if (priv->local && !nm_utils_ipaddr_is_valid(family, priv->local)) {
+    if (priv->local && !nm_inet_is_valid(family, priv->local)) {
         g_set_error(error,
                     NM_CONNECTION_ERROR,
                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
@@ -392,7 +412,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
         return FALSE;
     }
 
-    if (!nm_utils_ipaddr_is_valid(family, priv->remote)) {
+    if (!nm_inet_is_valid(family, priv->remote)) {
         g_set_error(error,
                     NM_CONNECTION_ERROR,
                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
@@ -411,11 +431,13 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
                        NM_IP_TUNNEL_MODE_GRE,
                        NM_IP_TUNNEL_MODE_GRETAP,
                        NM_IP_TUNNEL_MODE_IP6GRE,
-                       NM_IP_TUNNEL_MODE_IP6GRETAP)) {
+                       NM_IP_TUNNEL_MODE_IP6GRETAP,
+                       NM_IP_TUNNEL_MODE_VTI,
+                       NM_IP_TUNNEL_MODE_VTI6)) {
             g_set_error_literal(error,
                                 NM_CONNECTION_ERROR,
                                 NM_CONNECTION_ERROR_INVALID_PROPERTY,
-                                _("tunnel keys can only be specified for GRE tunnels"));
+                                _("tunnel keys can only be specified for GRE and VTI tunnels"));
             return FALSE;
         }
     }
@@ -484,6 +506,18 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
         return FALSE;
     }
 
+    if (priv->fwmark && !NM_IN_SET(priv->mode, NM_IP_TUNNEL_MODE_VTI, NM_IP_TUNNEL_MODE_VTI6)) {
+        g_set_error_literal(error,
+                            NM_CONNECTION_ERROR,
+                            NM_CONNECTION_ERROR_INVALID_PROPERTY,
+                            _("can be set only on VTI tunnels"));
+        g_prefix_error(error,
+                       "%s.%s: ",
+                       NM_SETTING_IP_TUNNEL_SETTING_NAME,
+                       NM_SETTING_IP_TUNNEL_FWMARK);
+        return FALSE;
+    }
+
     if (nm_connection_get_setting_wired(connection) && !_nm_ip_tunnel_mode_is_layer2(priv->mode)) {
         g_set_error(error,
                     NM_CONNECTION_ERROR,
@@ -728,6 +762,25 @@ nm_setting_ip_tunnel_class_init(NMSettingIPTunnelClass *klass)
                                               flow_label);
 
     /**
+     * NMSettingIPTunnel:fwmark:
+     *
+     * The fwmark value to assign to tunnel packets. This property can be set
+     * to a non zero value only on VTI and VTI6 tunnels.
+     *
+     * Since: 1.42
+     **/
+    _nm_setting_property_define_direct_uint32(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_IP_TUNNEL_FWMARK,
+                                              PROP_FWMARK,
+                                              0,
+                                              G_MAXUINT32,
+                                              0,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingIPTunnelPrivate,
+                                              fwmark);
+
+    /**
      * NMSettingIPTunnel:mtu:
      *
      * If non-zero, only transmit packets of the specified size or smaller,
diff --git a/src/libnm-core-impl/nm-setting-ip4-config.c b/src/libnm-core-impl/nm-setting-ip4-config.c
index d991152c..7819c2e8 100644
--- a/src/libnm-core-impl/nm-setting-ip4-config.c
+++ b/src/libnm-core-impl/nm-setting-ip4-config.c
@@ -138,7 +138,7 @@ nm_setting_ip4_config_get_dhcp_vendor_class_identifier(NMSettingIP4Config *setti
  *
  * Returns: the link-local configuration
  *
- * Since: 1.42, 1.40.4
+ * Since: 1.42
  **/
 NMSettingIP4LinkLocal
 nm_setting_ip4_config_get_link_local(NMSettingIP4Config *setting)
@@ -388,21 +388,29 @@ ip4_dns_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
     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);
+    return nm_utils_dns_to_variant(AF_INET, (const char *const *) dns->pdata, dns->len);
 }
 
-static void
-ip4_dns_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_GPROP_FCN_ARGS _nm_nil)
+static gboolean
+ip4_dns_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 {
-    g_value_take_boxed(to, nm_utils_ip4_dns_from_variant(from));
+    gs_strfreev char **strv = NULL;
+
+    if (!_nm_setting_use_legacy_property(setting, connection_dict, "dns", "dns-data")) {
+        *out_is_modified = FALSE;
+        return TRUE;
+    }
+
+    strv = nm_utils_ip4_dns_from_variant(value);
+    g_object_set(setting, NM_SETTING_IP_CONFIG_DNS, strv, NULL);
+    return TRUE;
 }
 
 static GVariant *
-ip4_addresses_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
+ip4_addresses_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
     gs_unref_ptrarray GPtrArray *addrs = NULL;
     const char                  *gateway;
@@ -413,12 +421,13 @@ ip4_addresses_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 }
 
 static gboolean
-ip4_addresses_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
+ip4_addresses_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 {
-    GPtrArray *addrs;
-    GVariant  *s_ip4;
-    char     **labels, *gateway = NULL;
-    int        i;
+    gs_unref_ptrarray GPtrArray *addrs   = NULL;
+    gs_unref_variant GVariant   *s_ip4   = NULL;
+    gs_free const char         **labels  = NULL;
+    gs_free char                *gateway = NULL;
+    guint                        i;
 
     /* FIXME: properly handle errors */
 
@@ -432,15 +441,15 @@ ip4_addresses_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
     s_ip4 = g_variant_lookup_value(connection_dict,
                                    NM_SETTING_IP4_CONFIG_SETTING_NAME,
                                    NM_VARIANT_TYPE_SETTING);
-    if (g_variant_lookup(s_ip4, "address-labels", "^as", &labels)) {
-        for (i = 0; i < addrs->len && labels[i]; i++)
-            if (*labels[i])
+    if (g_variant_lookup(s_ip4, "address-labels", "^a&s", &labels)) {
+        for (i = 0; i < addrs->len && labels[i]; i++) {
+            if (*labels[i]) {
                 nm_ip_address_set_attribute(addrs->pdata[i],
                                             NM_IP_ADDRESS_ATTRIBUTE_LABEL,
                                             g_variant_new_string(labels[i]));
-        g_strfreev(labels);
+            }
+        }
     }
-    g_variant_unref(s_ip4);
 
     g_object_set(setting,
                  NM_SETTING_IP_CONFIG_ADDRESSES,
@@ -448,52 +457,50 @@ ip4_addresses_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
                  NM_SETTING_IP_CONFIG_GATEWAY,
                  gateway,
                  NULL);
-    g_ptr_array_unref(addrs);
-    g_free(gateway);
     return TRUE;
 }
 
 static GVariant *
-ip4_address_labels_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
+ip4_address_labels_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
     NMSettingIPConfig *s_ip        = NM_SETTING_IP_CONFIG(setting);
     gboolean           have_labels = FALSE;
-    GPtrArray         *labels;
-    GVariant          *ret;
-    int                num_addrs, i;
+    gs_free GVariant **labels_free = NULL;
+    GVariant          *s_empty;
+    GVariant         **labels;
+    guint              num_addrs;
+    guint              i;
 
     if (!_nm_connection_serialize_non_secret(flags))
         return NULL;
 
     num_addrs = nm_setting_ip_config_get_num_addresses(s_ip);
+    if (num_addrs == 0)
+        return NULL;
+
+    labels = nm_malloc_maybe_a(500, sizeof(gpointer) * num_addrs, &labels_free);
+
+    s_empty = nm_g_variant_singleton_s_empty();
+
     for (i = 0; i < num_addrs; i++) {
         NMIPAddress *addr  = nm_setting_ip_config_get_address(s_ip, i);
         GVariant    *label = nm_ip_address_get_attribute(addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL);
 
         if (label) {
             have_labels = TRUE;
-            break;
-        }
+            labels[i]   = label;
+        } else
+            labels[i] = s_empty;
     }
+
     if (!have_labels)
         return NULL;
 
-    labels = g_ptr_array_sized_new(num_addrs);
-    for (i = 0; i < num_addrs; i++) {
-        NMIPAddress *addr  = nm_setting_ip_config_get_address(s_ip, i);
-        GVariant    *label = nm_ip_address_get_attribute(addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL);
-
-        g_ptr_array_add(labels, (char *) (label ? g_variant_get_string(label, NULL) : ""));
-    }
-
-    ret = g_variant_new_strv((const char *const *) labels->pdata, labels->len);
-    g_ptr_array_unref(labels);
-
-    return ret;
+    return g_variant_new_array(G_VARIANT_TYPE_STRING, labels, num_addrs);
 }
 
 static GVariant *
-ip4_address_data_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
+ip4_address_data_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
     gs_unref_ptrarray GPtrArray *addrs = NULL;
 
@@ -505,9 +512,9 @@ ip4_address_data_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 }
 
 static gboolean
-ip4_address_data_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
+ip4_address_data_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 {
-    GPtrArray *addrs;
+    gs_unref_ptrarray GPtrArray *addrs = NULL;
 
     /* FIXME: properly handle errors */
 
@@ -519,12 +526,11 @@ ip4_address_data_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 
     addrs = nm_utils_ip_addresses_from_variant(value, AF_INET);
     g_object_set(setting, NM_SETTING_IP_CONFIG_ADDRESSES, addrs, NULL);
-    g_ptr_array_unref(addrs);
     return TRUE;
 }
 
 static GVariant *
-ip4_routes_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
+ip4_routes_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
     gs_unref_ptrarray GPtrArray *routes = NULL;
 
@@ -533,9 +539,9 @@ ip4_routes_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 }
 
 static gboolean
-ip4_routes_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
+ip4_routes_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 {
-    GPtrArray *routes;
+    gs_unref_ptrarray GPtrArray *routes = NULL;
 
     /* FIXME: properly handle errors */
 
@@ -546,12 +552,11 @@ ip4_routes_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 
     routes = nm_utils_ip4_routes_from_variant(value);
     g_object_set(setting, property_info->name, routes, NULL);
-    g_ptr_array_unref(routes);
     return TRUE;
 }
 
 static GVariant *
-ip4_route_data_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
+ip4_route_data_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
     gs_unref_ptrarray GPtrArray *routes = NULL;
 
@@ -563,9 +568,9 @@ ip4_route_data_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 }
 
 static gboolean
-ip4_route_data_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
+ip4_route_data_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 {
-    GPtrArray *routes;
+    gs_unref_ptrarray GPtrArray *routes = NULL;
 
     /* FIXME: properly handle errors */
 
@@ -577,7 +582,6 @@ ip4_route_data_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 
     routes = nm_utils_ip_routes_from_variant(value, AF_INET);
     g_object_set(setting, NM_SETTING_IP_CONFIG_ROUTES, routes, NULL);
-    g_ptr_array_unref(routes);
     return TRUE;
 }
 
@@ -620,13 +624,15 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass)
     setting_class->verify = verify;
 
     setting_ip_config_class->private_offset = g_type_class_get_instance_private_offset(klass);
+    setting_ip_config_class->is_ipv4        = TRUE;
+    setting_ip_config_class->addr_family    = AF_INET;
 
     /* ---ifcfg-rh---
      * property: method
      * variable: BOOTPROTO
-     * format:   string
-     * values:   none, dhcp (bootp), static, ibft, autoip, shared
-     * default:  none
+     * format: string
+     * values: none, dhcp (bootp), static, ibft, autoip, shared
+     * default: none
      * description: Method used for IPv4 protocol configuration.
      * ---end---
      */
@@ -637,10 +643,11 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass)
      * description: List of DNS servers.
      * example: dns=1.2.3.4;8.8.8.8;8.8.4.4;
      * ---end---
-     * ---ifcfg-rh---
+     */
+    /* ---ifcfg-rh---
      * property: dns
      * variable: DNS1, DNS2, ...
-     * format:   string
+     * format: string
      * description: List of DNS servers. Even if NetworkManager supports many DNS
      *   servers, initscripts and resolver only care about the first three, usually.
      * example: DNS1=1.2.3.4 DNS2=10.0.0.254 DNS3=8.8.8.8
@@ -650,7 +657,7 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass)
     /* ---ifcfg-rh---
      * property: dns-search
      * variable: DOMAIN
-     * format:   string (space-separated domains)
+     * format: string (space-separated domains)
      * description: List of DNS search domains.
      * ---end---
      */
@@ -662,7 +669,8 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass)
      * description: List of static IP addresses.
      * example: address1=192.168.100.100/24 address2=10.1.1.5/24
      * ---end---
-     * ---ifcfg-rh---
+     */
+    /* ---ifcfg-rh---
      * property: addresses
      * variable: IPADDR, PREFIX (NETMASK), IPADDR1, PREFIX1 (NETMASK1), ...
      * description: List of static IP addresses.
@@ -677,7 +685,8 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass)
      * description: Gateway IP addresses as a string.
      * example: gateway=192.168.100.1
      * ---end---
-     * ---ifcfg-rh---
+     */
+    /* ---ifcfg-rh---
      * property: gateway
      * variable: GATEWAY
      * description: Gateway IP address.
@@ -693,7 +702,8 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass)
      * example: route1=8.8.8.0/24,10.1.1.1,77
      *   route2=7.7.0.0/16
      * ---end---
-     * ---ifcfg-rh---
+     */
+    /* ---ifcfg-rh---
      * property: routes
      * variable: ADDRESS1, NETMASK1, GATEWAY1, METRIC1, OPTIONS1, ...
      * description: List of static routes. They are not stored in ifcfg-* file,
@@ -787,6 +797,26 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass)
      * ---end---
      */
 
+    /* ---ifcfg-rh---
+     * property: auto-route-ext-gw
+     * variable: IPV4_AUTO_ROUTE_EXT_GW(+)
+     * default: yes
+     * description: VPN connections will default to add the route automatically unless this
+     *     setting is set to %FALSE.
+     *     For other connection types, adding such an automatic route is currently
+     *     not supported and setting this to %TRUE has no effect.
+     * ---end---
+     */
+
+    /* ---ifcfg-rh---
+     * property: replace-local-rule
+     * variable: IPV4_REPLACE_LOCAL_RULE(+)
+     * default: no
+     * description: Connections will default to keep the autogenerated priority
+     *     0 local rule unless this setting is set to %TRUE.
+     * ---end---
+     */
+
     /**
      * NMSettingIP4Config:dhcp-client-id:
      *
@@ -860,6 +890,7 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass)
      * variable: DHCP_HOSTNAME_FLAGS
      * description: flags for the DHCP hostname and FQDN properties
      * example: DHCP_HOSTNAME_FLAGS=5
+     * ---end---
      */
 
     /**
@@ -961,11 +992,11 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass)
         properties_override,
         g_object_class_find_property(G_OBJECT_CLASS(setting_class), NM_SETTING_IP_CONFIG_DNS),
         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), );
+                                       .compare_fcn   = _nm_setting_ip_config_compare_fcn_dns,
+                                       .to_dbus_fcn   = ip4_dns_to_dbus,
+                                       .from_dbus_fcn = ip4_dns_from_dbus, ),
+        .to_dbus_only_in_manager_process = TRUE,
+        .dbus_deprecated                 = TRUE, );
 
     /* ---dbus---
      * property: addresses
@@ -995,15 +1026,19 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass)
         properties_override,
         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,
+                                       .to_dbus_fcn   = ip4_addresses_to_dbus,
                                        .compare_fcn   = _nm_setting_ip_config_compare_fcn_addresses,
-                                       .from_dbus_fcn = ip4_addresses_set, ));
+                                       .from_dbus_fcn = ip4_addresses_from_dbus, ),
+        .to_dbus_only_in_manager_process = TRUE,
+        .dbus_deprecated                 = TRUE, );
     _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,
-                                       .compare_fcn = _nm_setting_property_compare_fcn_ignore, ));
+                                       .to_dbus_fcn = ip4_address_labels_to_dbus,
+                                       .compare_fcn = _nm_setting_property_compare_fcn_ignore,
+                                       /* from_dbus() is handled by ip4_addresses_from_dbus(). */),
+        .dbus_deprecated = TRUE, );
 
     /* ---dbus---
      * property: address-data
@@ -1018,9 +1053,9 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass)
         properties_override,
         "address-data",
         NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("aa{sv}"),
-                                       .to_dbus_fcn   = ip4_address_data_get,
+                                       .to_dbus_fcn   = ip4_address_data_to_dbus,
                                        .compare_fcn   = _nm_setting_property_compare_fcn_ignore,
-                                       .from_dbus_fcn = ip4_address_data_set, ));
+                                       .from_dbus_fcn = ip4_address_data_from_dbus, ));
 
     /* ---dbus---
      * property: routes
@@ -1090,7 +1125,11 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass)
      *        <para><literal>"mtu"</literal> - an unsigned 32 bit integer.</para>
      *      </listitem>
      *      <listitem>
-     *        <para><literal>"onlink"</literal> - a boolean value.</para>
+     *        <para><literal>"onlink"</literal> - a boolean value. The onlink flag
+     *          is ignored for IPv4 routes without a gateway. That also means,
+     *          with a positive "weight" the route cannot merge with ECMP routes
+     *          which are onlink and have a gateway.
+     *        </para>
      *      </listitem>
      *      <listitem>
      *        <para><literal>"quickack"</literal> - a boolean value.</para>
@@ -1113,10 +1152,21 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass)
      *      </listitem>
      *      <listitem>
      *        <para><literal>"type"</literal> - one of <literal>unicast</literal>, <literal>local</literal>, <literal>blackhole</literal>,
-     *          <literal>unavailable</literal>, <literal>prohibit</literal>, <literal>throw</literal>.
+     *          <literal>unreachable</literal>, <literal>prohibit</literal>, <literal>throw</literal>.
      *          The default is <literal>unicast</literal>.</para>
      *      </listitem>
      *      <listitem>
+     *        <para><literal>"weight"</literal> - an unsigned 32 bit integer
+     *        ranging from 0 to 256. A non-zero weight indicates that the IPv4
+     *        route is an ECMP IPv4 route.  NetworkManager will automatically
+     *        merge compatible ECMP routes into multi-hop routes.  Setting to
+     *        zero or omitting the attribute configures single hop routes that
+     *        won't get merged. If the route finds no merge partner, it is
+     *        configured as single hop route.</para> <para>Note that in
+     *        NetworkManager, currently all nexthops of a ECMP route must share
+     *        the same "onlink" flag in order to be mergable.</para>
+     *      </listitem>
+     *      <listitem>
      *        <para><literal>"window"</literal> - an unsigned 32 bit integer.</para>
      *      </listitem>
      *     </itemizedlist>
@@ -1130,9 +1180,11 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass)
         properties_override,
         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,
+                                       .to_dbus_fcn   = ip4_routes_to_dbus,
                                        .compare_fcn   = _nm_setting_ip_config_compare_fcn_routes,
-                                       .from_dbus_fcn = ip4_routes_set, ));
+                                       .from_dbus_fcn = ip4_routes_from_dbus, ),
+        .to_dbus_only_in_manager_process = TRUE,
+        .dbus_deprecated                 = TRUE, );
 
     /* ---dbus---
      * property: route-data
@@ -1151,9 +1203,9 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass)
         properties_override,
         "route-data",
         NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("aa{sv}"),
-                                       .to_dbus_fcn   = ip4_route_data_get,
+                                       .to_dbus_fcn   = ip4_route_data_to_dbus,
                                        .compare_fcn   = _nm_setting_property_compare_fcn_ignore,
-                                       .from_dbus_fcn = ip4_route_data_set, ));
+                                       .from_dbus_fcn = ip4_route_data_from_dbus, ));
 
     /* ---nmcli---
      * property: routing-rules
diff --git a/src/libnm-core-impl/nm-setting-ip6-config.c b/src/libnm-core-impl/nm-setting-ip6-config.c
index 94794d1e..573211b2 100644
--- a/src/libnm-core-impl/nm-setting-ip6-config.c
+++ b/src/libnm-core-impl/nm-setting-ip6-config.c
@@ -305,7 +305,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
     if (priv->token) {
         if (priv->addr_gen_mode == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64) {
             struct in6_addr i6_token;
-            char            s_token[NM_UTILS_INET_ADDRSTRLEN];
+            char            s_token[NM_INET_ADDRSTRLEN];
 
             if (inet_pton(AF_INET6, priv->token, &i6_token) != 1
                 || !_nm_utils_inet6_is_token(&i6_token)) {
@@ -320,7 +320,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
                 return FALSE;
             }
 
-            if (g_strcmp0(priv->token, _nm_utils_inet6_ntop(&i6_token, s_token)))
+            if (g_strcmp0(priv->token, nm_inet6_ntop(&i6_token, s_token)))
                 token_needs_normalization = TRUE;
         } else {
             g_set_error_literal(error,
@@ -389,21 +389,29 @@ ip6_dns_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
     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);
+    return nm_utils_dns_to_variant(AF_INET6, (const char *const *) dns->pdata, dns->len);
 }
 
-static void
-ip6_dns_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_GPROP_FCN_ARGS _nm_nil)
+static gboolean
+ip6_dns_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 {
-    g_value_take_boxed(to, nm_utils_ip6_dns_from_variant(from));
+    gs_strfreev char **strv = NULL;
+
+    if (!_nm_setting_use_legacy_property(setting, connection_dict, "dns", "dns-data")) {
+        *out_is_modified = FALSE;
+        return TRUE;
+    }
+
+    strv = nm_utils_ip6_dns_from_variant(value);
+    g_object_set(setting, NM_SETTING_IP_CONFIG_DNS, strv, NULL);
+    return TRUE;
 }
 
 static GVariant *
-ip6_addresses_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
+ip6_addresses_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
     gs_unref_ptrarray GPtrArray *addrs = NULL;
     const char                  *gateway;
@@ -414,10 +422,10 @@ ip6_addresses_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 }
 
 static gboolean
-ip6_addresses_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
+ip6_addresses_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 {
-    GPtrArray *addrs;
-    char      *gateway = NULL;
+    gs_unref_ptrarray GPtrArray *addrs   = NULL;
+    gs_free char                *gateway = NULL;
 
     if (!_nm_setting_use_legacy_property(setting, connection_dict, "addresses", "address-data")) {
         *out_is_modified = FALSE;
@@ -432,13 +440,11 @@ ip6_addresses_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
                  NM_SETTING_IP_CONFIG_GATEWAY,
                  gateway,
                  NULL);
-    g_ptr_array_unref(addrs);
-    g_free(gateway);
     return TRUE;
 }
 
 static GVariant *
-ip6_address_data_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
+ip6_address_data_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
     gs_unref_ptrarray GPtrArray *addrs = NULL;
 
@@ -450,9 +456,9 @@ ip6_address_data_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 }
 
 static gboolean
-ip6_address_data_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
+ip6_address_data_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 {
-    GPtrArray *addrs;
+    gs_unref_ptrarray GPtrArray *addrs = NULL;
 
     /* Ignore 'address-data' if we're going to process 'addresses' */
     if (_nm_setting_use_legacy_property(setting, connection_dict, "addresses", "address-data")) {
@@ -462,12 +468,11 @@ ip6_address_data_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 
     addrs = nm_utils_ip_addresses_from_variant(value, AF_INET6);
     g_object_set(setting, NM_SETTING_IP_CONFIG_ADDRESSES, addrs, NULL);
-    g_ptr_array_unref(addrs);
     return TRUE;
 }
 
 static GVariant *
-ip6_routes_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
+ip6_routes_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
     gs_unref_ptrarray GPtrArray *routes = NULL;
 
@@ -476,9 +481,9 @@ ip6_routes_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 }
 
 static gboolean
-ip6_routes_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
+ip6_routes_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 {
-    GPtrArray *routes;
+    gs_unref_ptrarray GPtrArray *routes = NULL;
 
     if (!_nm_setting_use_legacy_property(setting, connection_dict, "routes", "route-data")) {
         *out_is_modified = FALSE;
@@ -487,12 +492,11 @@ ip6_routes_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 
     routes = nm_utils_ip6_routes_from_variant(value);
     g_object_set(setting, property_info->name, routes, NULL);
-    g_ptr_array_unref(routes);
     return TRUE;
 }
 
 static GVariant *
-ip6_route_data_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
+ip6_route_data_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
     gs_unref_ptrarray GPtrArray *routes = NULL;
 
@@ -504,9 +508,9 @@ ip6_route_data_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 }
 
 static gboolean
-ip6_route_data_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
+ip6_route_data_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 {
-    GPtrArray *routes;
+    gs_unref_ptrarray GPtrArray *routes = NULL;
 
     /* Ignore 'route-data' if we're going to process 'routes' */
     if (_nm_setting_use_legacy_property(setting, connection_dict, "routes", "route-data")) {
@@ -516,7 +520,6 @@ ip6_route_data_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 
     routes = nm_utils_ip_routes_from_variant(value, AF_INET6);
     g_object_set(setting, NM_SETTING_IP_CONFIG_ROUTES, routes, NULL);
-    g_ptr_array_unref(routes);
     return TRUE;
 }
 
@@ -559,11 +562,13 @@ nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *klass)
     setting_class->verify = verify;
 
     setting_ip_config_class->private_offset = g_type_class_get_instance_private_offset(klass);
+    setting_ip_config_class->is_ipv4        = FALSE;
+    setting_ip_config_class->addr_family    = AF_INET6;
 
     /* ---ifcfg-rh---
      * property: method
      * variable: IPV6INIT, IPV6FORWARDING, IPV6_AUTOCONF, DHCPV6C, IPV6_DISABLED
-     * default:  IPV6INIT=yes; IPV6FORWARDING=no; IPV6_AUTOCONF=!IPV6FORWARDING, DHCPV6=no
+     * default: IPV6INIT=yes; IPV6FORWARDING=no; IPV6_AUTOCONF=!IPV6FORWARDING, DHCPV6=no
      * description: Method used for IPv6 protocol configuration.
      *   ignore ~ IPV6INIT=no; auto ~ IPV6_AUTOCONF=yes; dhcp ~ IPV6_AUTOCONF=no and DHCPV6C=yes;
      *   disabled ~ IPV6_DISABLED=yes
@@ -576,10 +581,11 @@ nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *klass)
      * description: List of DNS servers.
      * example: dns=2001:4860:4860::8888;2001:4860:4860::8844;
      * ---end---
-     * ---ifcfg-rh---
+     */
+    /* ---ifcfg-rh---
      * property: dns
      * variable: DNS1, DNS2, ...
-     * format:   string
+     * format: string
      * description: List of DNS servers. NetworkManager uses the variables both
      *   for IPv4 and IPv6.
      * ---end---
@@ -588,7 +594,7 @@ nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *klass)
     /* ---ifcfg-rh---
      * property: dns-search
      * variable: IPV6_DOMAIN(+)
-     * format:   string (space-separated domains)
+     * format: string (space-separated domains)
      * description: List of DNS search domains.
      * ---end---
      */
@@ -600,7 +606,8 @@ nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *klass)
      * description: List of static IP addresses.
      * example: address1=abbe::cafe/96 address2=2001::1234
      * ---end---
-     * ---ifcfg-rh---
+     */
+    /* ---ifcfg-rh---
      * property: addresses
      * variable: IPV6ADDR, IPV6ADDR_SECONDARIES
      * description: List of static IP addresses.
@@ -616,7 +623,8 @@ nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *klass)
      * description: Gateway IP addresses as a string.
      * example: gateway=abbe::1
      * ---end---
-     * ---ifcfg-rh---
+     */
+    /* ---ifcfg-rh---
      * property: gateway
      * variable: IPV6_DEFAULTGW
      * description: Gateway IP address.
@@ -631,7 +639,8 @@ nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *klass)
      * description: List of IP routes.
      * example: route1=2001:4860:4860::/64,2620:52:0:2219:222:68ff:fe11:5403
      * ---end---
-     * ---ifcfg-rh---
+     */
+    /* ---ifcfg-rh---
      * property: routes
      * variable: (none)
      * description: List of static routes. They are not stored in ifcfg-* file,
@@ -732,6 +741,26 @@ nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *klass)
      * ---end---
      */
 
+    /* ---ifcfg-rh---
+     * property: auto-route-ext-gw
+     * variable: IPV6_AUTO_ROUTE_EXT_GW(+)
+     * default: yes
+     * description: VPN connections will default to add the route automatically unless this
+     *     setting is set to %FALSE.
+     *     For other connection types, adding such an automatic route is currently
+     *     not supported and setting this to %TRUE has no effect.
+     * ---end---
+     */
+
+    /* ---ifcfg-rh---
+     * property: replace-local-rule
+     * variable: IPV6_REPLACE_LOCAL_RULE(+)
+     * default: no
+     * description: Connections will default to keep the autogenerated priority
+     *     0 local rule unless this setting is set to %TRUE.
+     * ---end---
+     */
+
     /**
      * NMSettingIP6Config:ip6-privacy:
      *
@@ -869,7 +898,7 @@ nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *klass)
      * Since: 1.24
      **/
     /* ---ifcfg-rh---
-     * property: dhcp-timeout
+     * property: ra-timeout
      * variable: IPV6_RA_TIMEOUT(+)
      * description: A timeout for waiting Router Advertisements in seconds.
      * example: IPV6_RA_TIMEOUT=10
@@ -972,11 +1001,11 @@ nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *klass)
         properties_override,
         g_object_class_find_property(G_OBJECT_CLASS(setting_class), NM_SETTING_IP_CONFIG_DNS),
         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));
+                                       .compare_fcn   = _nm_setting_ip_config_compare_fcn_dns,
+                                       .to_dbus_fcn   = ip6_dns_to_dbus,
+                                       .from_dbus_fcn = ip6_dns_from_dbus, ),
+        .to_dbus_only_in_manager_process = TRUE,
+        .dbus_deprecated                 = TRUE);
 
     /* ---dbus---
      * property: addresses
@@ -1006,9 +1035,11 @@ nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *klass)
         properties_override,
         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,
+                                       .to_dbus_fcn   = ip6_addresses_to_dbus,
                                        .compare_fcn   = _nm_setting_ip_config_compare_fcn_addresses,
-                                       .from_dbus_fcn = ip6_addresses_set, ));
+                                       .from_dbus_fcn = ip6_addresses_from_dbus, ),
+        .to_dbus_only_in_manager_process = TRUE,
+        .dbus_deprecated                 = TRUE, );
 
     /* ---dbus---
      * property: address-data
@@ -1023,9 +1054,9 @@ nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *klass)
         properties_override,
         "address-data",
         NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("aa{sv}"),
-                                       .to_dbus_fcn   = ip6_address_data_get,
+                                       .to_dbus_fcn   = ip6_address_data_to_dbus,
                                        .compare_fcn   = _nm_setting_property_compare_fcn_ignore,
-                                       .from_dbus_fcn = ip6_address_data_set, ));
+                                       .from_dbus_fcn = ip6_address_data_from_dbus, ));
 
     /* ---dbus---
      * property: routes
@@ -1107,7 +1138,7 @@ nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *klass)
      *      </listitem>
      *      <listitem>
      *        <para><literal>"type"</literal> - one of <literal>unicast</literal>, <literal>local</literal>, <literal>blackhole</literal>,
-     *          <literal>unavailable</literal>, <literal>prohibit</literal>, <literal>throw</literal>.
+     *          <literal>unreachable</literal>, <literal>prohibit</literal>, <literal>throw</literal>.
      *          The default is <literal>unicast</literal>.</para>
      *      </listitem>
      *      <listitem>
@@ -1124,9 +1155,11 @@ nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *klass)
         properties_override,
         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,
+                                       .to_dbus_fcn   = ip6_routes_to_dbus,
                                        .compare_fcn   = _nm_setting_ip_config_compare_fcn_routes,
-                                       .from_dbus_fcn = ip6_routes_set, ));
+                                       .from_dbus_fcn = ip6_routes_from_dbus, ),
+        .to_dbus_only_in_manager_process = TRUE,
+        .dbus_deprecated                 = TRUE, );
 
     /* ---dbus---
      * property: route-data
@@ -1145,9 +1178,9 @@ nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *klass)
         properties_override,
         "route-data",
         NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("aa{sv}"),
-                                       .to_dbus_fcn   = ip6_route_data_get,
+                                       .to_dbus_fcn   = ip6_route_data_to_dbus,
                                        .compare_fcn   = _nm_setting_property_compare_fcn_ignore,
-                                       .from_dbus_fcn = ip6_route_data_set, ));
+                                       .from_dbus_fcn = ip6_route_data_from_dbus, ));
 
     /* ---nmcli---
      * property: routing-rules
diff --git a/src/libnm-core-impl/nm-setting-loopback.c b/src/libnm-core-impl/nm-setting-loopback.c
new file mode 100644
index 00000000..1451b411
--- /dev/null
+++ b/src/libnm-core-impl/nm-setting-loopback.c
@@ -0,0 +1,212 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2022 Red Hat, Inc.
+ */
+
+#include "libnm-core-impl/nm-default-libnm-core.h"
+
+#include "nm-setting-loopback.h"
+
+#include "nm-connection-private.h"
+#include "nm-setting-connection.h"
+#include "nm-setting-private.h"
+
+/**
+ * SECTION:nm-setting-loopback
+ * @short_description: Describes connection properties for loopback interfaces
+ *
+ * The #NMSettingLoopback object is a #NMSetting subclass that describes properties
+ * necessary for connection to loopback devices
+ **/
+
+/*****************************************************************************/
+
+NM_GOBJECT_PROPERTIES_DEFINE(NMSettingLoopback, PROP_MTU, );
+
+typedef struct {
+    guint32 mtu;
+} NMSettingLoopbackPrivate;
+
+/**
+ * NMSettingLoopback:
+ *
+ * Loopback Link Settings
+ *
+ * Since: 1.42
+ */
+struct _NMSettingLoopback {
+    NMSetting                parent;
+    NMSettingLoopbackPrivate _priv;
+};
+
+struct _NMSettingLoopbackClass {
+    NMSettingClass parent;
+};
+
+#define NM_SETTING_LOOPBACK_GET_PRIVATE(self) \
+    _NM_GET_PRIVATE(self, NMSettingLoopback, NM_IS_SETTING_LOOPBACK)
+
+G_DEFINE_TYPE(NMSettingLoopback, nm_setting_loopback, NM_TYPE_SETTING)
+
+/*****************************************************************************/
+
+/**
+ * nm_setting_loopback_get_mtu:
+ * @setting: the #NMSettingLoopback
+ *
+ * Returns: the #NMSettingLoopback:mtu property of the setting
+ *
+ * Since: 1.42
+ **/
+guint32
+nm_setting_loopback_get_mtu(NMSettingLoopback *setting)
+{
+    g_return_val_if_fail(NM_IS_SETTING_LOOPBACK(setting), 0);
+
+    return NM_SETTING_LOOPBACK_GET_PRIVATE(setting)->mtu;
+}
+
+static gboolean
+verify(NMSetting *setting, NMConnection *connection, GError **error)
+{
+    if (connection) {
+        NMSettingIPConfig   *s_ip4;
+        NMSettingIPConfig   *s_ip6;
+        NMSettingConnection *s_con;
+        const char          *method;
+
+        if ((s_ip4 = nm_connection_get_setting_ip4_config(connection))) {
+            if ((method = nm_setting_ip_config_get_method(s_ip4))
+                && !NM_IN_STRSET(method,
+                                 NM_SETTING_IP4_CONFIG_METHOD_AUTO,
+                                 NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) {
+                g_set_error(error,
+                            NM_CONNECTION_ERROR,
+                            NM_CONNECTION_ERROR_INVALID_PROPERTY,
+                            _("ipv4 method \"%s\" is not supported for loopback"),
+                            method);
+                g_prefix_error(error,
+                               "%s.%s: ",
+                               NM_SETTING_IP4_CONFIG_SETTING_NAME,
+                               NM_SETTING_IP_CONFIG_METHOD);
+                return FALSE;
+            }
+            if (!NM_IN_SET(nm_setting_ip4_config_get_link_local(NM_SETTING_IP4_CONFIG(s_ip4)),
+                           NM_SETTING_IP4_LL_DEFAULT,
+                           NM_SETTING_IP4_LL_AUTO,
+                           NM_SETTING_IP4_LL_DISABLED)) {
+                g_set_error(error,
+                            NM_CONNECTION_ERROR,
+                            NM_CONNECTION_ERROR_INVALID_PROPERTY,
+                            _("ipv4.link-local cannot be enabled for loopback"));
+                g_prefix_error(error,
+                               "%s.%s: ",
+                               NM_SETTING_IP4_CONFIG_SETTING_NAME,
+                               NM_SETTING_IP4_CONFIG_LINK_LOCAL);
+                return FALSE;
+            }
+        }
+        if ((s_ip6 = nm_connection_get_setting_ip6_config(connection))) {
+            if ((method = nm_setting_ip_config_get_method(s_ip6))
+                && !NM_IN_STRSET(method,
+                                 NM_SETTING_IP6_CONFIG_METHOD_AUTO,
+                                 NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) {
+                g_set_error(error,
+                            NM_CONNECTION_ERROR,
+                            NM_CONNECTION_ERROR_INVALID_PROPERTY,
+                            _("ipv6 method \"%s\" is not supported for loopback"),
+                            method);
+                g_prefix_error(error,
+                               "%s.%s: ",
+                               NM_SETTING_IP6_CONFIG_SETTING_NAME,
+                               NM_SETTING_IP_CONFIG_METHOD);
+                return FALSE;
+            }
+        }
+
+        if ((s_con = nm_connection_get_setting_connection(connection))) {
+            if (nm_setting_connection_get_slave_type(s_con)
+                || nm_setting_connection_get_master(s_con)) {
+                g_set_error(error,
+                            NM_CONNECTION_ERROR,
+                            NM_CONNECTION_ERROR_INVALID_PROPERTY,
+                            _("a loopback profile cannot be a port"));
+                g_prefix_error(error,
+                               "%s.%s: ",
+                               NM_SETTING_CONNECTION_SETTING_NAME,
+                               nm_setting_connection_get_slave_type(s_con)
+                                   ? NM_SETTING_CONNECTION_SLAVE_TYPE
+                                   : NM_SETTING_CONNECTION_MASTER);
+                return FALSE;
+            }
+        }
+    }
+    return TRUE;
+}
+
+/*****************************************************************************/
+
+static void
+nm_setting_loopback_init(NMSettingLoopback *setting)
+{}
+
+/**
+ * nm_setting_loopback_new:
+ *
+ * Creates a new #NMSettingLoopback object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingLoopback object
+ *
+ * Since: 1.42
+ **/
+NMSetting *
+nm_setting_loopback_new(void)
+{
+    return g_object_new(NM_TYPE_SETTING_LOOPBACK, NULL);
+}
+
+static void
+nm_setting_loopback_class_init(NMSettingLoopbackClass *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;
+
+    /**
+     * NMSettingLoopback:mtu:
+     *
+     * If non-zero, only transmit packets of the specified size or smaller,
+     * breaking larger packets up into multiple Ethernet frames.
+     *
+     * Since: 1.42
+     **/
+    /* ---ifcfg-rh---
+     * property: mtu
+     * variable: MTU
+     * description: MTU of the interface.
+     * ---end---
+     */
+    _nm_setting_property_define_direct_uint32(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_LOOPBACK_MTU,
+                                              PROP_MTU,
+                                              0,
+                                              G_MAXUINT32,
+                                              0,
+                                              NM_SETTING_PARAM_FUZZY_IGNORE,
+                                              NMSettingLoopback,
+                                              _priv.mtu);
+
+    g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_LOOPBACK,
+                             NULL,
+                             properties_override,
+                             0);
+}
diff --git a/src/libnm-core-impl/nm-setting-macsec.c b/src/libnm-core-impl/nm-setting-macsec.c
index 1463fd92..bf48f049 100644
--- a/src/libnm-core-impl/nm-setting-macsec.c
+++ b/src/libnm-core-impl/nm-setting-macsec.c
@@ -215,13 +215,13 @@ nm_setting_macsec_get_send_sci(NMSettingMacsec *setting)
 }
 
 static GPtrArray *
-need_secrets(NMSetting *setting)
+need_secrets(NMSetting *setting, gboolean check_rerequest)
 {
     NMSettingMacsecPrivate *priv    = NM_SETTING_MACSEC_GET_PRIVATE(setting);
     GPtrArray              *secrets = NULL;
 
     if (priv->mode == NM_SETTING_MACSEC_MODE_PSK) {
-        if (!priv->mka_cak
+        if ((check_rerequest || !priv->mka_cak)
             && !NM_FLAGS_HAS(priv->mka_cak_flags, NM_SETTING_SECRET_FLAG_NOT_REQUIRED)) {
             secrets = g_ptr_array_sized_new(1);
             g_ptr_array_add(secrets, NM_SETTING_MACSEC_MKA_CAK);
diff --git a/src/libnm-core-impl/nm-setting-ovs-bridge.c b/src/libnm-core-impl/nm-setting-ovs-bridge.c
index 7dc9fda4..8e850157 100644
--- a/src/libnm-core-impl/nm-setting-ovs-bridge.c
+++ b/src/libnm-core-impl/nm-setting-ovs-bridge.c
@@ -120,7 +120,7 @@ nm_setting_ovs_bridge_get_stp_enable(NMSettingOvsBridge *self)
  *
  * Returns: the #NMSettingOvsBridge:datapath_type property of the setting
  *
- * Since: 1.42, 1.40.4
+ * Since: 1.42
  **/
 const char *
 nm_setting_ovs_bridge_get_datapath_type(NMSettingOvsBridge *self)
diff --git a/src/libnm-core-impl/nm-setting-ovs-dpdk.c b/src/libnm-core-impl/nm-setting-ovs-dpdk.c
index e26f918b..87e9a3e8 100644
--- a/src/libnm-core-impl/nm-setting-ovs-dpdk.c
+++ b/src/libnm-core-impl/nm-setting-ovs-dpdk.c
@@ -22,7 +22,7 @@
 
 /*****************************************************************************/
 
-NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_DEVARGS, PROP_N_RXQ, );
+NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_DEVARGS, PROP_N_RXQ, PROP_N_RXQ_DESC, PROP_N_TXQ_DESC, );
 
 /**
  * NMSettingOvsDpdk:
@@ -34,6 +34,8 @@ struct _NMSettingOvsDpdk {
 
     char   *devargs;
     guint32 n_rxq;
+    guint32 n_rxq_desc;
+    guint32 n_txq_desc;
 };
 
 struct _NMSettingOvsDpdkClass {
@@ -76,6 +78,72 @@ nm_setting_ovs_dpdk_get_n_rxq(NMSettingOvsDpdk *self)
     return self->n_rxq;
 }
 
+/**
+ * nm_setting_ovs_dpdk_get_n_rxq_desc:
+ * @self: the #NMSettingOvsDpdk
+ *
+ * Returns: the #NMSettingOvsDpdk:n-rxq-desc property of the setting
+ *
+ * Since: 1.42
+ **/
+guint32
+nm_setting_ovs_dpdk_get_n_rxq_desc(NMSettingOvsDpdk *self)
+{
+    g_return_val_if_fail(NM_IS_SETTING_OVS_DPDK(self), 0);
+
+    return self->n_rxq_desc;
+}
+
+/**
+ * nm_setting_ovs_dpdk_get_n_txq_desc:
+ * @self: the #NMSettingOvsDpdk
+ *
+ * Returns: the #NMSettingOvsDpdk:n-txq-desc property of the setting
+ *
+ * Since: 1.42
+ **/
+guint32
+nm_setting_ovs_dpdk_get_n_txq_desc(NMSettingOvsDpdk *self)
+{
+    g_return_val_if_fail(NM_IS_SETTING_OVS_DPDK(self), 0);
+
+    return self->n_txq_desc;
+}
+
+/*****************************************************************************/
+
+static gboolean
+verify(NMSetting *setting, NMConnection *connection, GError **error)
+{
+    NMSettingOvsDpdk *self = NM_SETTING_OVS_DPDK(setting);
+
+    if (self->n_rxq_desc != 0 && !nm_utils_is_power_of_two(self->n_rxq_desc)) {
+        g_set_error(error,
+                    NM_CONNECTION_ERROR,
+                    NM_CONNECTION_ERROR_INVALID_PROPERTY,
+                    _("must be a power of two"));
+        g_prefix_error(error,
+                       "%s.%s: ",
+                       NM_SETTING_OVS_DPDK_SETTING_NAME,
+                       NM_SETTING_OVS_DPDK_N_RXQ_DESC);
+        return FALSE;
+    }
+
+    if (self->n_txq_desc != 0 && !nm_utils_is_power_of_two(self->n_txq_desc)) {
+        g_set_error(error,
+                    NM_CONNECTION_ERROR,
+                    NM_CONNECTION_ERROR_INVALID_PROPERTY,
+                    _("must be a power of two"));
+        g_prefix_error(error,
+                       "%s.%s: ",
+                       NM_SETTING_OVS_DPDK_SETTING_NAME,
+                       NM_SETTING_OVS_DPDK_N_TXQ_DESC);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
 /*****************************************************************************/
 
 static void
@@ -107,6 +175,8 @@ nm_setting_ovs_dpdk_class_init(NMSettingOvsDpdkClass *klass)
     object_class->get_property = _nm_setting_property_get_property_direct;
     object_class->set_property = _nm_setting_property_set_property_direct;
 
+    setting_class->verify = verify;
+
     /**
      * NMSettingOvsDpdk:devargs:
      *
@@ -142,6 +212,50 @@ nm_setting_ovs_dpdk_class_init(NMSettingOvsDpdkClass *klass)
                                               NMSettingOvsDpdk,
                                               n_rxq);
 
+    /**
+     * NMSettingOvsDpdk:n-rxq-desc:
+     *
+     * The rx queue size (number of rx descriptors) for DPDK ports.
+     * Must be zero or a power of 2 between 1 and 4096, and supported
+     * by the hardware. Defaults to zero which means to leave the
+     * parameter in OVS unspecified and effectively configures 2048
+     * descriptors.
+     *
+     * Since: 1.42
+     **/
+    _nm_setting_property_define_direct_uint32(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_OVS_DPDK_N_RXQ_DESC,
+                                              PROP_N_RXQ_DESC,
+                                              0,
+                                              4096,
+                                              0,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingOvsDpdk,
+                                              n_rxq_desc);
+
+    /**
+     * NMSettingOvsDpdk:n-txq-desc:
+     *
+     * The tx queue size (number of tx descriptors) for DPDK ports.
+     * Must be zero or a power of 2 between 1 and 4096, and supported
+     * by the hardware. Defaults to zero which means to leave the
+     * parameter in OVS unspecified and effectively configures 2048
+     * descriptors.
+     *
+     * Since: 1.42
+     **/
+    _nm_setting_property_define_direct_uint32(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_OVS_DPDK_N_TXQ_DESC,
+                                              PROP_N_TXQ_DESC,
+                                              0,
+                                              4096,
+                                              0,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingOvsDpdk,
+                                              n_txq_desc);
+
     g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
     _nm_setting_class_commit(setting_class,
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 f4e31f81..54684380 100644
--- a/src/libnm-core-impl/nm-setting-ovs-external-ids.c
+++ b/src/libnm-core-impl/nm-setting-ovs-external-ids.c
@@ -10,6 +10,7 @@
 #include "nm-setting-private.h"
 #include "nm-utils-private.h"
 #include "nm-connection-private.h"
+#include "nm-setting-ovs-other-config.h"
 
 #define MAX_NUM_KEYS 256
 
@@ -53,15 +54,6 @@ G_DEFINE_TYPE(NMSettingOvsExternalIDs, nm_setting_ovs_external_ids, NM_TYPE_SETT
 
 /*****************************************************************************/
 
-static gboolean
-_exid_key_char_is_regular(char ch)
-{
-    /* allow words of printable characters, plus some
-     * special characters, for example to support base64 encoding. */
-    return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')
-           || NM_IN_SET(ch, '-', '_', '+', '/', '=', '.');
-}
-
 /**
  * nm_setting_ovs_external_ids_check_key:
  * @key: (allow-none): the key to check
@@ -105,7 +97,7 @@ nm_setting_ovs_external_ids_check_key(const char *key, GError **error)
                             _("key must be UTF8"));
         return FALSE;
     }
-    if (!NM_STRCHAR_ALL(key, ch, _exid_key_char_is_regular(ch))) {
+    if (!NM_STRCHAR_ALL(key, ch, nm_ascii_is_regular_char(ch))) {
         /* Probably OVS is more forgiving about what makes a valid key for
          * an external-id. However, we are strict (at least, for now). */
         g_set_error_literal(error,
@@ -127,6 +119,56 @@ nm_setting_ovs_external_ids_check_key(const char *key, GError **error)
     return TRUE;
 }
 
+gboolean
+_nm_setting_ovs_verify_connection_type(GType gtype, NMConnection *connection, GError **error)
+{
+    NMSettingConnection *s_con;
+    const char          *type;
+    const char          *slave_type;
+
+    nm_assert(!connection || NM_IS_CONNECTION(connection));
+    nm_assert(NM_IN_SET(gtype, NM_TYPE_SETTING_OVS_EXTERNAL_IDS, NM_TYPE_SETTING_OVS_OTHER_CONFIG));
+    nm_assert(!error || !*error);
+
+    if (!connection) {
+        /* We don't know. It's valid. */
+        return TRUE;
+    }
+
+    type = nm_connection_get_connection_type(connection);
+    if (!type) {
+        NMSetting *s_base;
+
+        s_base = _nm_connection_find_base_type_setting(connection);
+        if (s_base)
+            type = nm_setting_get_name(s_base);
+    }
+    if (NM_IN_STRSET(type,
+                     NM_SETTING_OVS_BRIDGE_SETTING_NAME,
+                     NM_SETTING_OVS_PORT_SETTING_NAME,
+                     NM_SETTING_OVS_INTERFACE_SETTING_NAME))
+        return TRUE;
+
+    if ((s_con = nm_connection_get_setting_connection(connection))
+        && _nm_connection_detect_slave_type_full(s_con,
+                                                 connection,
+                                                 &slave_type,
+                                                 NULL,
+                                                 NULL,
+                                                 NULL,
+                                                 NULL)
+        && nm_streq0(slave_type, NM_SETTING_OVS_PORT_SETTING_NAME))
+        return TRUE;
+
+    g_set_error(error,
+                NM_CONNECTION_ERROR,
+                NM_CONNECTION_ERROR_INVALID_PROPERTY,
+                _("OVS %s can only be added to a profile of type OVS "
+                  "bridge/port/interface or to OVS system interface"),
+                gtype == NM_TYPE_SETTING_OVS_EXTERNAL_IDS ? "external-ids" : "other-config");
+    return FALSE;
+}
+
 /**
  * nm_setting_ovs_external_ids_check_val:
  * @val: (allow-none): the value to check
@@ -204,6 +246,8 @@ nm_setting_ovs_external_ids_get_data_keys(NMSettingOvsExternalIDs *setting, guin
     NMSettingOvsExternalIDs        *self = setting;
     NMSettingOvsExternalIDsPrivate *priv;
 
+    NM_SET_OUT(out_len, 0);
+
     g_return_val_if_fail(NM_IS_SETTING_OVS_EXTERNAL_IDS(self), NULL);
 
     priv = NM_SETTING_OVS_EXTERNAL_IDS_GET_PRIVATE(self);
@@ -302,12 +346,16 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
 
     if (priv->data) {
         gs_free_error GError *local = NULL;
-        GHashTableIter        iter;
-        const char           *key;
-        const char           *val;
+        const char *const    *keys;
+        guint                 len;
+        guint                 i;
+
+        keys = nm_setting_ovs_external_ids_get_data_keys(self, &len);
+
+        for (i = 0; i < len; i++) {
+            const char *key = keys[i];
+            const char *val = g_hash_table_lookup(priv->data, key);
 
-        g_hash_table_iter_init(&iter, priv->data);
-        while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &val)) {
             if (!nm_setting_ovs_external_ids_check_key(key, &local)) {
                 g_set_error(error,
                             NM_CONNECTION_ERROR,
@@ -336,7 +384,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
         g_set_error(error,
                     NM_CONNECTION_ERROR,
                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
-                    _("maximum number of user data entries reached (%u instead of %u)"),
+                    _("maximum number of entries reached (%u instead of %u)"),
                     g_hash_table_size(priv->data),
                     (unsigned) MAX_NUM_KEYS);
         g_prefix_error(error,
@@ -346,44 +394,10 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
         return FALSE;
     }
 
-    if (connection) {
-        NMSettingConnection *s_con;
-        const char          *type;
-        const char          *slave_type;
-
-        type = nm_connection_get_connection_type(connection);
-        if (!type) {
-            NMSetting *s_base;
-
-            s_base = _nm_connection_find_base_type_setting(connection);
-            if (s_base)
-                type = nm_setting_get_name(s_base);
-        }
-        if (NM_IN_STRSET(type,
-                         NM_SETTING_OVS_BRIDGE_SETTING_NAME,
-                         NM_SETTING_OVS_PORT_SETTING_NAME,
-                         NM_SETTING_OVS_INTERFACE_SETTING_NAME))
-            goto connection_type_is_good;
-
-        if ((s_con = nm_connection_get_setting_connection(connection))
-            && _nm_connection_detect_slave_type_full(s_con,
-                                                     connection,
-                                                     &slave_type,
-                                                     NULL,
-                                                     NULL,
-                                                     NULL,
-                                                     NULL)
-            && nm_streq0(slave_type, NM_SETTING_OVS_PORT_SETTING_NAME))
-            goto connection_type_is_good;
-
-        g_set_error_literal(error,
-                            NM_CONNECTION_ERROR,
-                            NM_CONNECTION_ERROR_INVALID_PROPERTY,
-                            _("OVS external IDs can only be added to a profile of type OVS "
-                              "bridge/port/interface or to OVS system interface"));
+    if (!_nm_setting_ovs_verify_connection_type(NM_TYPE_SETTING_OVS_EXTERNAL_IDS,
+                                                connection,
+                                                error))
         return FALSE;
-    }
-connection_type_is_good:
 
     return TRUE;
 }
@@ -519,7 +533,7 @@ nm_setting_ovs_external_ids_class_init(NMSettingOvsExternalIDsClass *klass)
     /**
      * NMSettingOvsExternalIDs:data: (type GHashTable(utf8,utf8))
      *
-     * A dictionary of key/value pairs with exernal-ids for OVS.
+     * A dictionary of key/value pairs with external-ids for OVS.
      *
      * Since: 1.30
      **/
diff --git a/src/libnm-core-impl/nm-setting-ovs-interface.c b/src/libnm-core-impl/nm-setting-ovs-interface.c
index 6554f520..34e66480 100644
--- a/src/libnm-core-impl/nm-setting-ovs-interface.c
+++ b/src/libnm-core-impl/nm-setting-ovs-interface.c
@@ -21,7 +21,7 @@
 
 /*****************************************************************************/
 
-NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_TYPE, );
+NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_TYPE, PROP_OFPORT_REQUEST, );
 
 /**
  * NMSettingOvsInterface:
@@ -31,7 +31,8 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_TYPE, );
 struct _NMSettingOvsInterface {
     NMSetting parent;
 
-    char *type;
+    char   *type;
+    guint32 ofport_request;
 };
 
 struct _NMSettingOvsInterfaceClass {
@@ -58,6 +59,22 @@ nm_setting_ovs_interface_get_interface_type(NMSettingOvsInterface *self)
     return self->type;
 }
 
+/**
+ * nm_setting_ovs_interface_get_ofport_request:
+ * @self: the #NMSettingOvsInterface
+ *
+ * Returns: id of the preassigned ovs port
+ *
+ * Since: 1.42
+ **/
+guint32
+nm_setting_ovs_interface_get_ofport_request(NMSettingOvsInterface *self)
+{
+    g_return_val_if_fail(NM_IS_SETTING_OVS_INTERFACE(self), 0);
+
+    return self->ofport_request;
+}
+
 /*****************************************************************************/
 
 int
@@ -378,6 +395,27 @@ nm_setting_ovs_interface_class_init(NMSettingOvsInterfaceClass *klass)
                                               NM_SETTING_PARAM_INFERRABLE,
                                               NMSettingOvsInterface,
                                               type);
+    /**
+     * NMSettingOvsInterface:ofport-request:
+     *
+     * Open vSwitch openflow port number.
+     * Defaults to zero which means that port number will not be specified
+     * and it will be chosen randomly by ovs. OpenFlow ports are the network interfaces
+     * for passing packets between OpenFlow processing and the rest of the network.
+     * OpenFlow switches connect logically to each other via their OpenFlow ports.
+     *
+     * Since: 1.42
+     **/
+    _nm_setting_property_define_direct_uint32(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_OVS_INTERFACE_OFPORT_REQUEST,
+                                              PROP_OFPORT_REQUEST,
+                                              0,
+                                              65279,
+                                              0,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingOvsInterface,
+                                              ofport_request);
 
     g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
diff --git a/src/libnm-core-impl/nm-setting-ovs-other-config.c b/src/libnm-core-impl/nm-setting-ovs-other-config.c
new file mode 100644
index 00000000..ca46ae95
--- /dev/null
+++ b/src/libnm-core-impl/nm-setting-ovs-other-config.c
@@ -0,0 +1,403 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2017 - 2020, 2022 Red Hat, Inc.
+ */
+
+#include "libnm-core-impl/nm-default-libnm-core.h"
+
+#include "nm-setting-ovs-other-config.h"
+
+#include "nm-setting-private.h"
+#include "nm-utils-private.h"
+#include "nm-connection-private.h"
+
+#define MAX_NUM_KEYS 256
+
+/*****************************************************************************/
+
+/**
+ * SECTION:nm-setting-ovs-other-config
+ * @short_description: Other-config settings for OVS
+ *
+ * The #NMSettingOvsOtherConfig object is a #NMSetting subclass that allows to
+ * configure other_config settings for OVS. See also "other_config" in the
+ * "ovs-vswitchd.conf.db" manual for the keys that OVS supports.
+ **/
+
+/*****************************************************************************/
+
+NM_GOBJECT_PROPERTIES_DEFINE(NMSettingOvsOtherConfig, PROP_DATA, );
+
+typedef struct {
+    GHashTable  *data;
+    const char **data_keys;
+} NMSettingOvsOtherConfigPrivate;
+
+/**
+ * NMSettingOvsOtherConfig:
+ *
+ * OVS Other Config Settings
+ *
+ * Since: 1.42
+ */
+struct _NMSettingOvsOtherConfig {
+    NMSetting                      parent;
+    NMSettingOvsOtherConfigPrivate _priv;
+};
+
+struct _NMSettingOvsOtherConfigClass {
+    NMSettingClass parent;
+};
+
+G_DEFINE_TYPE(NMSettingOvsOtherConfig, nm_setting_ovs_other_config, NM_TYPE_SETTING)
+
+#define NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(self) \
+    _NM_GET_PRIVATE(self, NMSettingOvsOtherConfig, NM_IS_SETTING_OVS_OTHER_CONFIG)
+
+/*****************************************************************************/
+
+static GHashTable *
+_create_data_hash(void)
+{
+    return g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, g_free);
+}
+
+GHashTable *
+_nm_setting_ovs_other_config_get_data(NMSettingOvsOtherConfig *self)
+{
+    return NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(self)->data;
+}
+
+/**
+ * nm_setting_ovs_other_config_get_data_keys:
+ * @setting: the #NMSettingOvsOtherConfig
+ * @out_len: (out): the length of the returned array
+ *
+ * Returns: (array length=out_len) (transfer none): a
+ *   %NULL-terminated array containing each key from the table.
+ *
+ * Since: 1.42
+  **/
+const char *const *
+nm_setting_ovs_other_config_get_data_keys(NMSettingOvsOtherConfig *setting, guint *out_len)
+{
+    NMSettingOvsOtherConfig        *self = setting;
+    NMSettingOvsOtherConfigPrivate *priv;
+
+    NM_SET_OUT(out_len, 0);
+
+    g_return_val_if_fail(NM_IS_SETTING_OVS_OTHER_CONFIG(self), NULL);
+
+    priv = NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(self);
+
+    if (priv->data_keys) {
+        NM_SET_OUT(out_len, g_hash_table_size(priv->data));
+        return priv->data_keys;
+    }
+
+    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. */
+    return priv->data_keys ?: ((const char **) &priv->data_keys);
+}
+
+/*****************************************************************************/
+
+/**
+ * nm_setting_ovs_other_config_get_data:
+ * @setting: the #NMSettingOvsOtherConfig instance
+ * @key: the other-config to lookup
+ *
+ * Since: 1.42
+ *
+ * Returns: (transfer none): the value associated with @key or %NULL if no such
+ *   value exists.
+ */
+const char *
+nm_setting_ovs_other_config_get_data(NMSettingOvsOtherConfig *setting, const char *key)
+{
+    NMSettingOvsOtherConfig        *self = setting;
+    NMSettingOvsOtherConfigPrivate *priv;
+
+    g_return_val_if_fail(NM_IS_SETTING_OVS_OTHER_CONFIG(self), NULL);
+    g_return_val_if_fail(key, NULL);
+
+    priv = NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(self);
+
+    if (!priv->data)
+        return NULL;
+
+    return g_hash_table_lookup(priv->data, key);
+}
+
+/**
+ * nm_setting_ovs_other_config_set_data:
+ * @setting: the #NMSettingOvsOtherConfig instance
+ * @key: the key to set
+ * @val: (allow-none): the value to set or %NULL to clear a key.
+ *
+ * Since: 1.42
+ */
+void
+nm_setting_ovs_other_config_set_data(NMSettingOvsOtherConfig *setting,
+                                     const char              *key,
+                                     const char              *val)
+{
+    NMSettingOvsOtherConfig        *self = setting;
+    NMSettingOvsOtherConfigPrivate *priv;
+
+    g_return_if_fail(NM_IS_SETTING_OVS_OTHER_CONFIG(self));
+
+    priv = NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(self);
+
+    if (!val) {
+        if (priv->data && g_hash_table_remove(priv->data, key))
+            goto out_changed;
+        return;
+    }
+
+    if (priv->data) {
+        const char *val2;
+
+        if (g_hash_table_lookup_extended(priv->data, key, NULL, (gpointer *) &val2)) {
+            if (nm_streq(val, val2))
+                return;
+        }
+    } else
+        priv->data = _create_data_hash();
+
+    g_hash_table_insert(priv->data, g_strdup(key), g_strdup(val));
+
+out_changed:
+    nm_clear_g_free(&priv->data_keys);
+    _notify(self, PROP_DATA);
+}
+
+/*****************************************************************************/
+
+static gboolean
+verify(NMSetting *setting, NMConnection *connection, GError **error)
+{
+    NMSettingOvsOtherConfig        *self = NM_SETTING_OVS_OTHER_CONFIG(setting);
+    NMSettingOvsOtherConfigPrivate *priv = NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(self);
+
+    if (priv->data) {
+        gs_free_error GError *local = NULL;
+        const char *const    *keys;
+        guint                 len;
+        guint                 i;
+
+        keys = nm_setting_ovs_other_config_get_data_keys(self, &len);
+
+        for (i = 0; i < len; i++) {
+            const char *key = keys[i];
+            const char *val = g_hash_table_lookup(priv->data, key);
+
+            if (!nm_setting_ovs_other_config_check_key(key, &local)) {
+                g_set_error(error,
+                            NM_CONNECTION_ERROR,
+                            NM_CONNECTION_ERROR_FAILED,
+                            _("invalid key \"%s\": %s"),
+                            key,
+                            local->message);
+            } else if (!nm_setting_ovs_other_config_check_val(val, &local)) {
+                g_set_error(error,
+                            NM_CONNECTION_ERROR,
+                            NM_CONNECTION_ERROR_FAILED,
+                            _("invalid value for \"%s\": %s"),
+                            key,
+                            local->message);
+            } else
+                continue;
+            g_prefix_error(error,
+                           "%s.%s: ",
+                           NM_SETTING_OVS_OTHER_CONFIG_SETTING_NAME,
+                           NM_SETTING_OVS_OTHER_CONFIG_DATA);
+            return FALSE;
+        }
+    }
+
+    if (priv->data && g_hash_table_size(priv->data) > MAX_NUM_KEYS) {
+        g_set_error(error,
+                    NM_CONNECTION_ERROR,
+                    NM_CONNECTION_ERROR_INVALID_PROPERTY,
+                    _("maximum number of entries reached (%u instead of %u)"),
+                    g_hash_table_size(priv->data),
+                    (unsigned) MAX_NUM_KEYS);
+        g_prefix_error(error,
+                       "%s.%s: ",
+                       NM_SETTING_OVS_OTHER_CONFIG_SETTING_NAME,
+                       NM_SETTING_OVS_OTHER_CONFIG_DATA);
+        return FALSE;
+    }
+
+    if (!_nm_setting_ovs_verify_connection_type(NM_TYPE_SETTING_OVS_OTHER_CONFIG,
+                                                connection,
+                                                error))
+        return FALSE;
+
+    return TRUE;
+}
+
+static NMTernary
+compare_fcn_data(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil)
+{
+    NMSettingOvsOtherConfigPrivate *priv;
+    NMSettingOvsOtherConfigPrivate *pri2;
+
+    if (NM_FLAGS_HAS(flags, NM_SETTING_COMPARE_FLAG_INFERRABLE))
+        return NM_TERNARY_DEFAULT;
+
+    if (!set_b)
+        return TRUE;
+
+    priv = NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(NM_SETTING_OVS_OTHER_CONFIG(set_a));
+    pri2 = NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(NM_SETTING_OVS_OTHER_CONFIG(set_b));
+    return nm_utils_hashtable_equal(priv->data, pri2->data, TRUE, g_str_equal);
+}
+
+/*****************************************************************************/
+
+static void
+get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+    NMSettingOvsOtherConfig        *self = NM_SETTING_OVS_OTHER_CONFIG(object);
+    NMSettingOvsOtherConfigPrivate *priv = NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(self);
+    GHashTableIter                  iter;
+    GHashTable                     *data;
+    const char                     *key;
+    const char                     *val;
+
+    switch (prop_id) {
+    case PROP_DATA:
+        data = _create_data_hash();
+        if (priv->data) {
+            g_hash_table_iter_init(&iter, priv->data);
+            while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &val))
+                g_hash_table_insert(data, g_strdup(key), g_strdup(val));
+        }
+        g_value_take_boxed(value, data);
+        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)
+{
+    NMSettingOvsOtherConfig        *self = NM_SETTING_OVS_OTHER_CONFIG(object);
+    NMSettingOvsOtherConfigPrivate *priv = NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(self);
+
+    switch (prop_id) {
+    case PROP_DATA:
+    {
+        gs_unref_hashtable GHashTable *old = NULL;
+        GHashTableIter                 iter;
+        GHashTable                    *data;
+        const char                    *key;
+        const char                    *val;
+
+        nm_clear_g_free(&priv->data_keys);
+
+        old = g_steal_pointer(&priv->data);
+
+        data = g_value_get_boxed(value);
+        if (nm_g_hash_table_size(data) <= 0)
+            return;
+
+        priv->data = _create_data_hash();
+        g_hash_table_iter_init(&iter, data);
+        while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &val))
+            g_hash_table_insert(priv->data, g_strdup(key), g_strdup(val));
+        break;
+    }
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+        break;
+    }
+}
+
+/*****************************************************************************/
+
+static void
+nm_setting_ovs_other_config_init(NMSettingOvsOtherConfig *self)
+{}
+
+/**
+ * nm_setting_ovs_other_config_new:
+ *
+ * Creates a new #NMSettingOvsOtherConfig object with default values.
+ *
+ * Returns: (transfer full) (type NMSettingOvsOtherConfig): the new empty
+ * #NMSettingOvsOtherConfig object
+ *
+ * Since: 1.42
+ */
+NMSetting *
+nm_setting_ovs_other_config_new(void)
+{
+    return g_object_new(NM_TYPE_SETTING_OVS_OTHER_CONFIG, NULL);
+}
+
+static void
+finalize(GObject *object)
+{
+    NMSettingOvsOtherConfig        *self = NM_SETTING_OVS_OTHER_CONFIG(object);
+    NMSettingOvsOtherConfigPrivate *priv = NM_SETTING_OVS_OTHER_CONFIG_GET_PRIVATE(self);
+
+    g_free(priv->data_keys);
+    if (priv->data)
+        g_hash_table_unref(priv->data);
+
+    G_OBJECT_CLASS(nm_setting_ovs_other_config_parent_class)->finalize(object);
+}
+
+static void
+nm_setting_ovs_other_config_class_init(NMSettingOvsOtherConfigClass *klass)
+{
+    GObjectClass   *object_class        = G_OBJECT_CLASS(klass);
+    NMSettingClass *setting_class       = NM_SETTING_CLASS(klass);
+    GArray         *properties_override = _nm_sett_info_property_override_create_array();
+
+    object_class->get_property = get_property;
+    object_class->set_property = set_property;
+    object_class->finalize     = finalize;
+
+    setting_class->verify = verify;
+
+    /**
+     * NMSettingOvsOtherConfig:data: (type GHashTable(utf8,utf8))
+     *
+     * A dictionary of key/value pairs with other_config settings for OVS.
+     * See also "other_config" in the "ovs-vswitchd.conf.db" manual for the keys
+     * that OVS supports.
+     *
+     * Since: 1.42
+     **/
+    obj_properties[PROP_DATA] = g_param_spec_boxed(NM_SETTING_OVS_OTHER_CONFIG_DATA,
+                                                   "",
+                                                   "",
+                                                   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_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(setting_class,
+                             NM_META_SETTING_TYPE_OVS_OTHER_CONFIG,
+                             NULL,
+                             properties_override,
+                             0);
+}
diff --git a/src/libnm-core-impl/nm-setting-ovs-port.c b/src/libnm-core-impl/nm-setting-ovs-port.c
index ab9c0a11..190f1e5d 100644
--- a/src/libnm-core-impl/nm-setting-ovs-port.c
+++ b/src/libnm-core-impl/nm-setting-ovs-port.c
@@ -21,12 +21,14 @@
 
 /*****************************************************************************/
 
-NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_VLAN_MODE,
-                                  PROP_TAG,
-                                  PROP_LACP,
-                                  PROP_BOND_MODE,
-                                  PROP_BOND_UPDELAY,
-                                  PROP_BOND_DOWNDELAY, );
+NM_GOBJECT_PROPERTIES_DEFINE(NMSettingOvsPort,
+                             PROP_VLAN_MODE,
+                             PROP_TAG,
+                             PROP_TRUNKS,
+                             PROP_LACP,
+                             PROP_BOND_MODE,
+                             PROP_BOND_UPDELAY,
+                             PROP_BOND_DOWNDELAY, );
 
 /**
  * NMSettingOvsPort:
@@ -36,12 +38,13 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_VLAN_MODE,
 struct _NMSettingOvsPort {
     NMSetting parent;
 
-    char   *vlan_mode;
-    char   *lacp;
-    char   *bond_mode;
-    guint32 tag;
-    guint32 bond_updelay;
-    guint32 bond_downdelay;
+    GPtrArray *trunks;
+    char      *vlan_mode;
+    char      *lacp;
+    char      *bond_mode;
+    guint32    tag;
+    guint32    bond_updelay;
+    guint32    bond_downdelay;
 };
 
 struct _NMSettingOvsPortClass {
@@ -84,6 +87,143 @@ nm_setting_ovs_port_get_tag(NMSettingOvsPort *self)
     return self->tag;
 }
 
+/*****************************************************************************/
+
+/**
+ * nm_setting_ovs_port_add_trunk:
+ * @setting: the #NMSettingOvsPort
+ * @trunk: the trunk to add
+ *
+ * Appends a new trunk range to the setting.
+ * This takes a reference to @trunk.
+ *
+ * Since: 1.42
+ **/
+void
+nm_setting_ovs_port_add_trunk(NMSettingOvsPort *self, NMRange *trunk)
+{
+    g_return_if_fail(NM_IS_SETTING_OVS_PORT(self));
+    g_return_if_fail(trunk);
+
+    g_ptr_array_add(self->trunks, nm_range_ref(trunk));
+    _notify(self, PROP_TRUNKS);
+}
+
+/**
+ * nm_setting_ovs_port_get_num_trunks:
+ * @setting: the #NMSettingOvsPort
+ *
+ * Returns: the number of trunk ranges
+ *
+ * Since: 1.42
+ **/
+guint
+nm_setting_ovs_port_get_num_trunks(NMSettingOvsPort *self)
+{
+    g_return_val_if_fail(NM_IS_SETTING_OVS_PORT(self), 0);
+
+    return self->trunks->len;
+}
+
+/**
+ * nm_setting_ovs_port_get_trunk:
+ * @setting: the #NMSettingOvsPort
+ * @idx: index number of the trunk range to return
+ *
+ * Returns: (transfer none): the trunk range at index @idx
+ *
+ * Since: 1.42
+ **/
+NMRange *
+nm_setting_ovs_port_get_trunk(NMSettingOvsPort *self, guint idx)
+{
+    g_return_val_if_fail(NM_IS_SETTING_OVS_PORT(self), NULL);
+
+    g_return_val_if_fail(idx < self->trunks->len, NULL);
+
+    return self->trunks->pdata[idx];
+}
+
+/**
+ * nm_setting_ovs_port_remove_trunk:
+ * @setting: the #NMSettingOvsPort
+ * @idx: index number of the trunk range.
+ *
+ * Removes the trunk range at index @idx.
+ *
+ * Since: 1.42
+ **/
+void
+nm_setting_ovs_port_remove_trunk(NMSettingOvsPort *self, guint idx)
+{
+    g_return_if_fail(NM_IS_SETTING_OVS_PORT(self));
+
+    g_return_if_fail(idx < self->trunks->len);
+
+    g_ptr_array_remove_index(self->trunks, idx);
+    _notify(self, PROP_TRUNKS);
+}
+
+/**
+ * nm_setting_ovs_port_remove_trunk_by_value:
+ * @setting: the #NMSettingOvsPort
+ * @start: the trunk range start index
+ * @end: the trunk range end index
+ *
+ * Remove the trunk range with range @start to @end.
+ *
+ * Returns: %TRUE if the trunk range was found and removed; %FALSE otherwise
+ *
+ * Since: 1.42
+ **/
+gboolean
+nm_setting_ovs_port_remove_trunk_by_value(NMSettingOvsPort *self, guint start, guint end)
+{
+    NMRange *trunk;
+    guint    i;
+
+    g_return_val_if_fail(NM_IS_SETTING_OVS_PORT(self), FALSE);
+
+    for (i = 0; i < self->trunks->len; i++) {
+        trunk = (NMRange *) self->trunks->pdata[i];
+        if (trunk->start == start && trunk->end == end) {
+            g_ptr_array_remove_index(self->trunks, i);
+            _notify(self, PROP_TRUNKS);
+            return TRUE;
+        }
+    }
+    return FALSE;
+}
+
+/**
+ * nm_setting_ovs_port_clear_trunks:
+ * @setting: the #NMSettingOvsPort
+ *
+ * Removes all configured trunk ranges.
+ *
+ * Since: 1.42
+ **/
+void
+nm_setting_ovs_port_clear_trunks(NMSettingOvsPort *self)
+{
+    g_return_if_fail(NM_IS_SETTING_OVS_PORT(self));
+
+    if (self->trunks->len != 0) {
+        g_ptr_array_set_size(self->trunks, 0);
+        _notify(self, PROP_TRUNKS);
+    }
+}
+
+const GPtrArray *
+_nm_setting_ovs_port_get_trunks_arr(NMSettingOvsPort *self)
+{
+    g_return_val_if_fail(NM_IS_SETTING_OVS_PORT(self), NULL);
+
+    return self->trunks;
+}
+
+/*****************************************************************************/
+
 /**
  * nm_setting_ovs_port_get_lacp:
  * @self: the #NMSettingOvsPort
@@ -151,6 +291,107 @@ nm_setting_ovs_port_get_bond_downdelay(NMSettingOvsPort *self)
 /*****************************************************************************/
 
 static int
+range_cmp(gconstpointer a, gconstpointer b)
+{
+    const NMRange *range_a = *(const NMRange **) a;
+    const NMRange *range_b = *(const NMRange **) b;
+
+    return nm_range_cmp(range_a, range_b);
+}
+
+gboolean
+_nm_setting_ovs_port_sort_trunks(NMSettingOvsPort *self)
+{
+    gboolean need_sort = FALSE;
+    guint    i;
+
+    for (i = 1; i < self->trunks->len; i++) {
+        NMRange *range_prev = self->trunks->pdata[i - 1];
+        NMRange *range      = self->trunks->pdata[i];
+
+        if (nm_range_cmp(range_prev, range) > 0) {
+            need_sort = TRUE;
+            break;
+        }
+    }
+
+    if (need_sort) {
+        g_ptr_array_sort(self->trunks, range_cmp);
+        _notify(self, PROP_TRUNKS);
+    }
+
+    return need_sort;
+}
+
+static gboolean
+verify_trunks(GPtrArray *ranges, GError **error)
+{
+    gs_unref_hashtable GHashTable *h = NULL;
+    NMRange                       *range;
+    guint                          i;
+    guint                          vlan;
+
+    if (!ranges)
+        return TRUE;
+
+    h = g_hash_table_new(nm_direct_hash, NULL);
+
+    for (i = 0; i < ranges->len; i++) {
+        range = ranges->pdata[i];
+        nm_assert(range->start <= range->end);
+
+        if (range->start > 4095 || range->end > 4095) {
+            g_set_error_literal(error,
+                                NM_CONNECTION_ERROR,
+                                NM_CONNECTION_ERROR_INVALID_PROPERTY,
+                                _("VLANs must be between 0 and 4095"));
+            return FALSE;
+        }
+
+        for (vlan = range->start; vlan <= range->end; vlan++) {
+            if (!nm_g_hash_table_add(h, GUINT_TO_POINTER(vlan))) {
+                g_set_error(error,
+                            NM_CONNECTION_ERROR,
+                            NM_CONNECTION_ERROR_INVALID_PROPERTY,
+                            _("duplicate VLAN %u"),
+                            vlan);
+                return FALSE;
+            }
+        }
+    }
+
+    return TRUE;
+}
+
+static gboolean
+verify_trunks_normalizable(GPtrArray *ranges, GError **error)
+{
+    guint i;
+
+    nm_assert(verify_trunks(ranges, NULL));
+
+    if (!ranges || ranges->len <= 1)
+        return TRUE;
+
+    for (i = 1; i < ranges->len; i++) {
+        NMRange *range_prev = ranges->pdata[i - 1];
+        NMRange *range      = ranges->pdata[i];
+
+        if (nm_range_cmp(range_prev, range) > 0) {
+            g_set_error(error,
+                        NM_CONNECTION_ERROR,
+                        NM_CONNECTION_ERROR_INVALID_PROPERTY,
+                        _("VLANs %u and %u are not sorted in ascending order"),
+                        (guint) range_prev->start,
+                        (guint) range->start);
+            return FALSE;
+        }
+    }
+
+    return TRUE;
+}
+
+static int
 verify(NMSetting *setting, NMConnection *connection, GError **error)
 {
     NMSettingOvsPort *self = NM_SETTING_OVS_PORT(setting);
@@ -208,6 +449,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
                       "native-tagged",
                       "native-untagged",
                       "trunk",
+                      "dot1q-tunnel",
                       NULL)) {
         g_set_error(error,
                     NM_CONNECTION_ERROR,
@@ -257,14 +499,68 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
         return FALSE;
     }
 
+    if (!verify_trunks(self->trunks, error)) {
+        g_prefix_error(error,
+                       "%s.%s: ",
+                       NM_SETTING_OVS_PORT_SETTING_NAME,
+                       NM_SETTING_OVS_PORT_TRUNKS);
+        return FALSE;
+    }
+
+    if (!verify_trunks_normalizable(self->trunks, error)) {
+        g_prefix_error(error,
+                       "%s.%s: ",
+                       NM_SETTING_OVS_PORT_SETTING_NAME,
+                       NM_SETTING_OVS_PORT_TRUNKS);
+        return NM_SETTING_VERIFY_NORMALIZABLE;
+    }
+
     return TRUE;
 }
 
 /*****************************************************************************/
 
 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_TRUNKS:
+        g_value_take_boxed(value,
+                           _nm_utils_copy_array(self->trunks,
+                                                (NMUtilsCopyFunc) nm_range_ref,
+                                                (GDestroyNotify) nm_range_unref));
+        break;
+    default:
+        _nm_setting_property_get_property_direct(object, prop_id, value, 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_TRUNKS:
+        g_ptr_array_unref(self->trunks);
+        self->trunks = _nm_utils_copy_array(g_value_get_boxed(value),
+                                            (NMUtilsCopyFunc) nm_range_ref,
+                                            (GDestroyNotify) nm_range_unref);
+        break;
+    default:
+        _nm_setting_property_set_property_direct(object, prop_id, value, pspec);
+        break;
+    }
+}
+
+static void
 nm_setting_ovs_port_init(NMSettingOvsPort *self)
-{}
+{
+    self->trunks = g_ptr_array_new_with_free_func((GDestroyNotify) nm_range_unref);
+}
 
 /**
  * nm_setting_ovs_port_new:
@@ -282,14 +578,25 @@ nm_setting_ovs_port_new(void)
 }
 
 static void
+finalize(GObject *object)
+{
+    NMSettingOvsPort *self = NM_SETTING_OVS_PORT(object);
+
+    g_ptr_array_unref(self->trunks);
+
+    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);
     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;
+    object_class->get_property = get_property;
+    object_class->set_property = set_property;
+    object_class->finalize     = finalize;
 
     setting_class->verify = verify;
 
@@ -297,7 +604,7 @@ nm_setting_ovs_port_class_init(NMSettingOvsPortClass *klass)
      * NMSettingOvsPort:vlan-mode:
      *
      * The VLAN mode. One of "access", "native-tagged", "native-untagged",
-     * "trunk" or unset.
+     * "trunk", "dot1q-tunnel" or unset.
      *
      * Since: 1.10
      **/
@@ -328,6 +635,31 @@ nm_setting_ovs_port_class_init(NMSettingOvsPortClass *klass)
                                               tag);
 
     /**
+     * NMSettingOvsPort:trunks: (type GPtrArray(NMRange))
+     *
+     * A list of VLAN ranges that this port trunks.
+     *
+     * The property is valid only for ports with mode "trunk",
+     * "native-tagged", or "native-untagged port".
+     * If it is empty, the port trunks all VLANs.
+     *
+     * Since: 1.42
+     **/
+    obj_properties[PROP_TRUNKS] = g_param_spec_boxed(NM_SETTING_OVS_PORT_TRUNKS,
+                                                     "",
+                                                     "",
+                                                     G_TYPE_PTR_ARRAY,
+                                                     G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE
+                                                         | G_PARAM_STATIC_STRINGS);
+    _nm_properties_override_gobj(
+        properties_override,
+        obj_properties[PROP_TRUNKS],
+        NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("aa{sv}"),
+                                       .to_dbus_fcn   = _nm_utils_ranges_to_dbus,
+                                       .compare_fcn   = _nm_utils_ranges_cmp,
+                                       .from_dbus_fcn = _nm_utils_ranges_from_dbus));
+
+    /**
      * NMSettingOvsPort:lacp:
      *
      * LACP mode. One of "active", "off", or "passive".
diff --git a/src/libnm-core-impl/nm-setting-pppoe.c b/src/libnm-core-impl/nm-setting-pppoe.c
index c4a9a89c..5c4f3c28 100644
--- a/src/libnm-core-impl/nm-setting-pppoe.c
+++ b/src/libnm-core-impl/nm-setting-pppoe.c
@@ -178,12 +178,12 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
 }
 
 static GPtrArray *
-need_secrets(NMSetting *setting)
+need_secrets(NMSetting *setting, gboolean check_rerequest)
 {
     NMSettingPppoePrivate *priv    = NM_SETTING_PPPOE_GET_PRIVATE(setting);
     GPtrArray             *secrets = NULL;
 
-    if (priv->password)
+    if (!check_rerequest && priv->password)
         return NULL;
 
     if (!(priv->password_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED)) {
diff --git a/src/libnm-core-impl/nm-setting-private.h b/src/libnm-core-impl/nm-setting-private.h
index 38b233e9..fff6c1cc 100644
--- a/src/libnm-core-impl/nm-setting-private.h
+++ b/src/libnm-core-impl/nm-setting-private.h
@@ -13,24 +13,69 @@
 #include "nm-setting.h"
 #include "nm-setting-bridge.h"
 #include "nm-connection.h"
+#include "nm-simple-connection.h"
 #include "nm-core-enum-types.h"
 
 #include "libnm-core-intern/nm-core-internal.h"
 
 /*****************************************************************************/
 
+struct _NMRefString;
+
 typedef struct {
     NMConnection *self;
 
     NMSetting *settings[_NM_META_SETTING_TYPE_NUM];
 
     /* D-Bus path of the connection, if any */
-    char *path;
+    struct _NMRefString *path;
 } NMConnectionPrivate;
 
 extern GTypeClass *_nm_simple_connection_class_instance;
 extern int         _nm_simple_connection_private_offset;
 
+#undef NM_IS_SIMPLE_CONNECTION
+#define NM_IS_SIMPLE_CONNECTION(self)                                                           \
+    ({                                                                                          \
+        gconstpointer _self1 = (self);                                                          \
+        gboolean      _result;                                                                  \
+                                                                                                \
+        _result =                                                                               \
+            (_self1                                                                             \
+             && (((GTypeInstance *) _self1)->g_class == _nm_simple_connection_class_instance)); \
+                                                                                                \
+        nm_assert(_result == G_TYPE_CHECK_INSTANCE_TYPE(_self1, NM_TYPE_SIMPLE_CONNECTION));    \
+                                                                                                \
+        _result;                                                                                \
+    })
+
+#undef NM_IS_CONNECTION
+#define NM_IS_CONNECTION(self)                                            \
+    ({                                                                    \
+        gconstpointer _self0 = (self);                                    \
+                                                                          \
+        (_self0                                                           \
+         && (NM_IS_SIMPLE_CONNECTION(_self0)                              \
+             || G_TYPE_CHECK_INSTANCE_TYPE(_self0, NM_TYPE_CONNECTION))); \
+    })
+
+#define _NM_SIMPLE_CONNECTION_GET_CONNECTION_PRIVATE(connection)                                \
+    ({                                                                                          \
+        gpointer             _connection_1 = (connection);                                      \
+        NMConnectionPrivate *_priv_1;                                                           \
+                                                                                                \
+        nm_assert(NM_IS_SIMPLE_CONNECTION(_connection_1));                                      \
+                                                                                                \
+        _priv_1 = (void *) (&(((char *) _connection_1)[_nm_simple_connection_private_offset])); \
+                                                                                                \
+        nm_assert(_priv_1                                                                       \
+                  == G_TYPE_INSTANCE_GET_PRIVATE(_connection_1,                                 \
+                                                 NM_TYPE_SIMPLE_CONNECTION,                     \
+                                                 NMConnectionPrivate));                         \
+                                                                                                \
+        _priv_1;                                                                                \
+    })
+
 void _nm_connection_private_clear(NMConnectionPrivate *priv);
 
 /*****************************************************************************/
@@ -55,7 +100,7 @@ struct _NMSettingClass {
 
     gboolean (*verify_secrets)(NMSetting *setting, NMConnection *connection, GError **error);
 
-    GPtrArray *(*need_secrets)(NMSetting *setting);
+    GPtrArray *(*need_secrets)(NMSetting *setting, gboolean check_rerequest);
 
     int (*update_one_secret)(NMSetting *setting, const char *key, GVariant *value, GError **error);
 
@@ -120,11 +165,23 @@ struct _NMSettingIPConfigClass {
     NMSettingClass parent;
 
     /* In the past, this struct was public API. Preserve ABI! */
+
     union {
-        gpointer _dummy;
+        gpointer _dummy1;
         int      private_offset;
     };
-    gpointer padding[7];
+
+    union {
+        gpointer _dummy2;
+        gint8    addr_family;
+    };
+
+    union {
+        gpointer _dummy3;
+        bool     is_ipv4;
+    };
+
+    gpointer padding[5];
 };
 
 typedef struct {
@@ -140,6 +197,8 @@ typedef struct {
     char      *dhcp_hostname;
     char      *dhcp_iaid;
     gint64     route_metric;
+    int        auto_route_ext_gw;
+    int        replace_local_rule;
     gint32     required_timeout;
     gint32     dad_timeout;
     gint32     dhcp_timeout;
@@ -155,6 +214,11 @@ typedef struct {
 
 void _nm_setting_ip_config_private_init(gpointer self, NMSettingIPConfigPrivate *priv);
 
+#define NM_SETTING_IP_CONFIG_GET_ADDR_FAMILY(setting) \
+    (NM_SETTING_IP_CONFIG_GET_CLASS(setting)->addr_family)
+
+#define NM_SETTING_IP_CONFIG_IS_IPv4(setting) (NM_SETTING_IP_CONFIG_GET_CLASS(setting)->is_ipv4)
+
 /*****************************************************************************/
 
 NMSettingPriority _nm_setting_get_base_type_priority(NMSetting *setting);
@@ -240,6 +304,14 @@ typedef struct {
 
 /*****************************************************************************/
 
+struct _NMRange {
+    int     refcount;
+    guint64 start;
+    guint64 end;
+};
+
+/*****************************************************************************/
+
 #define NM_SETTING_PARAM_NONE 0
 
 /* The property of the #NMSetting should be considered during comparisons that
@@ -256,8 +328,9 @@ typedef struct {
  */
 #define NM_SETTING_PARAM_INFERRABLE (1 << (4 + G_PARAM_USER_SHIFT))
 
-/* This is a legacy property, which clients should not send to the daemon. */
-#define NM_SETTING_PARAM_LEGACY (1 << (5 + G_PARAM_USER_SHIFT))
+/* This flag has no meaning (anymore). It's only kept, because we used it
+ * on some older versions of libnm. */
+#define NM_SETTING_PARAM_UNUSED1 (1 << (5 + G_PARAM_USER_SHIFT))
 
 /* When a connection is active and gets modified, usually the change
  * to the settings-connection does not propagate automatically to the
@@ -442,10 +515,14 @@ _nm_properties_override(GArray *properties_override, const NMSettInfoProperty *p
                                                   .property_type = (p_property_type), \
                                                   __VA_ARGS__))
 
-#define _nm_properties_override_dbus(properties_override, p_name, p_property_type) \
-    _nm_properties_override(                                                       \
-        (properties_override),                                                     \
-        NM_SETT_INFO_PROPERTY(.name = ("" p_name ""), .property_type = (p_property_type), ))
+#define _nm_properties_override_dbus(properties_override,                             \
+                                     p_name,                                          \
+                                     p_property_type,                                 \
+                                     ... /* extra NMSettInfoProperty fields */)       \
+    _nm_properties_override((properties_override),                                    \
+                            NM_SETT_INFO_PROPERTY(.name          = ("" p_name ""),    \
+                                                  .property_type = (p_property_type), \
+                                                  __VA_ARGS__))
 
 /*****************************************************************************/
 
@@ -984,7 +1061,7 @@ gboolean _nm_setting_use_legacy_property(NMSetting  *setting,
                                          const char *legacy_property,
                                          const char *new_property);
 
-GPtrArray *_nm_setting_need_secrets(NMSetting *setting);
+GPtrArray *_nm_setting_need_secrets(NMSetting *setting, gboolean check_rerequest);
 
 gboolean _nm_setting_should_compare_secret_property(NMSetting            *setting,
                                                     NMSetting            *other,
@@ -998,15 +1075,26 @@ gboolean _nm_utils_bridge_vlans_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS
 
 GVariant *_nm_utils_bridge_vlans_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil);
 
+gboolean _nm_utils_ranges_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil);
+
+NMTernary _nm_utils_ranges_cmp(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil);
+
+GVariant *_nm_utils_ranges_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);
+NMTernary _nm_setting_ip_config_compare_fcn_dns(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil);
 
-GVariant *_nm_utils_hwaddr_cloned_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil);
+gboolean _nm_sett_info_prop_missing_from_dbus_fcn_cloned_mac_address(
+    _NM_SETT_INFO_PROP_MISSING_FROM_DBUS_FCN_ARGS _nm_nil);
+
+GVariant *
+_nm_sett_info_prop_to_dbus_fcn_cloned_mac_address(_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);
+gboolean
+_nm_sett_info_prop_from_dbus_fcn_cloned_mac_address(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil);
 
 /*****************************************************************************/
 
diff --git a/src/libnm-core-impl/nm-setting-serial.c b/src/libnm-core-impl/nm-setting-serial.c
index a8201876..6fe3e487 100644
--- a/src/libnm-core-impl/nm-setting-serial.c
+++ b/src/libnm-core-impl/nm-setting-serial.c
@@ -278,7 +278,8 @@ nm_setting_serial_class_init(NMSettingSerialClass *klass)
      *   111 ('o') for odd, or 110 ('n') for none.
      * example: parity=n
      * ---end---
-     * ---dbus---
+     */
+    /* ---dbus---
      * property: parity
      * format: byte
      * description: The connection parity: 69 (ASCII 'E') for even parity,
diff --git a/src/libnm-core-impl/nm-setting-sriov.c b/src/libnm-core-impl/nm-setting-sriov.c
index f9bee21e..091cc0c6 100644
--- a/src/libnm-core-impl/nm-setting-sriov.c
+++ b/src/libnm-core-impl/nm-setting-sriov.c
@@ -372,7 +372,7 @@ const NMVariantAttributeSpec *const _nm_sriov_vf_attribute_spec[] = {
  *
  * Returns: %TRUE if the attribute is valid, %FALSE otherwise
  *
- * Since: 1.42, 1.40.4
+ * Since: 1.42
  */
 gboolean
 nm_sriov_vf_attribute_validate(const char *name, GVariant *value, gboolean *known, GError **error)
diff --git a/src/libnm-core-impl/nm-setting-tc-config.c b/src/libnm-core-impl/nm-setting-tc-config.c
index 529bbca1..80135201 100644
--- a/src/libnm-core-impl/nm-setting-tc-config.c
+++ b/src/libnm-core-impl/nm-setting-tc-config.c
@@ -935,7 +935,7 @@ nm_tc_tfilter_get_parent(NMTCTfilter *tfilter)
  *
  * Returns: the action associated with a traffic filter.
  *
- * Since: 1.42, 1.40.4
+ * Since: 1.42
  **/
 NMTCAction *
 nm_tc_tfilter_get_action(NMTCTfilter *tfilter)
@@ -956,7 +956,7 @@ nm_tc_tfilter_get_action(NMTCTfilter *tfilter)
  *
  * Sets the action associated with a traffic filter.
  *
- * Since: 1.42, 1.40.4
+ * Since: 1.42
  **/
 void
 nm_tc_tfilter_set_action(NMTCTfilter *tfilter, NMTCAction *action)
@@ -1475,7 +1475,7 @@ next:
 }
 
 static GVariant *
-tc_qdiscs_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
+qdiscs_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
     gs_unref_ptrarray GPtrArray *qdiscs = NULL;
 
@@ -1484,7 +1484,7 @@ tc_qdiscs_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 }
 
 static gboolean
-tc_qdiscs_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
+qdiscs_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 {
     gs_unref_ptrarray GPtrArray *qdiscs = NULL;
 
@@ -1661,7 +1661,7 @@ next:
 }
 
 static GVariant *
-tc_tfilters_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
+tfilters_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
     gs_unref_ptrarray GPtrArray *tfilters = NULL;
 
@@ -1670,7 +1670,7 @@ tc_tfilters_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 }
 
 static gboolean
-tc_tfilters_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
+tfilters_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 {
     gs_unref_ptrarray GPtrArray *tfilters = NULL;
 
@@ -1944,8 +1944,8 @@ nm_setting_tc_config_class_init(NMSettingTCConfigClass *klass)
      *        <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.
+     *     a power of two and cannot be larger than 65536.
+     *     Default value: 1024.
      *        </para>
      *      </listitem>
      *  </varlistentry>
@@ -2083,12 +2083,13 @@ nm_setting_tc_config_class_init(NMSettingTCConfigClass *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_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, ));
+    _nm_properties_override_gobj(
+        properties_override,
+        obj_properties[PROP_QDISCS],
+        NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("aa{sv}"),
+                                       .to_dbus_fcn   = qdiscs_to_dbus,
+                                       .compare_fcn   = compare_fcn_qdiscs,
+                                       .from_dbus_fcn = qdiscs_from_dbus, ));
 
     /**
      * NMSettingTCConfig:tfilters: (type GPtrArray(NMTCTfilter))
@@ -2107,8 +2108,8 @@ nm_setting_tc_config_class_init(NMSettingTCConfigClass *klass)
      * format: GPtrArray(NMTCTfilter)
      * description-docbook:
      *  <para>
-     * Array of TC traffic filters. Traffic control can manage the packet content during
-     * classification by using filters.
+     *   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:
@@ -2206,7 +2207,7 @@ nm_setting_tc_config_class_init(NMSettingTCConfigClass *klass)
      * ---end---
      **/
     /* ---ifcfg-rh---
-     * property: qdiscs
+     * property: tfilters
      * variable: FILTER1(+), FILTER2(+), ..., TC_COMMIT(+)
      * description: Traffic filters to set on the interface. When no
      *  QDISC1, QDISC2, ..., FILTER1, FILTER2, ... keys are present,
@@ -2225,9 +2226,9 @@ nm_setting_tc_config_class_init(NMSettingTCConfigClass *klass)
         properties_override,
         obj_properties[PROP_TFILTERS],
         NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("aa{sv}"),
-                                       .to_dbus_fcn   = tc_tfilters_get,
+                                       .to_dbus_fcn   = tfilters_to_dbus,
                                        .compare_fcn   = compare_fcn_tfilter,
-                                       .from_dbus_fcn = tc_tfilters_set, ));
+                                       .from_dbus_fcn = tfilters_from_dbus, ));
 
     g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
diff --git a/src/libnm-core-impl/nm-setting-team.c b/src/libnm-core-impl/nm-setting-team.c
index 3eeeb2c3..e6e375e7 100644
--- a/src/libnm-core-impl/nm-setting-team.c
+++ b/src/libnm-core-impl/nm-setting-team.c
@@ -375,6 +375,8 @@ _nm_team_link_watcher_ref(NMTeamLinkWatcher *watcher)
  *
  * Increases the reference count of the object.
  *
+ * Since 1.20, ref-counting of #NMTeamLinkWatcher is thread-safe.
+ *
  * Since: 1.12
  **/
 void
@@ -390,6 +392,8 @@ nm_team_link_watcher_ref(NMTeamLinkWatcher *watcher)
  * Decreases the reference count of the object.  If the reference count
  * reaches zero, the object will be destroyed.
  *
+ * Since 1.20, ref-counting of #NMTeamLinkWatcher is thread-safe.
+ *
  * Since: 1.12
  **/
 void
@@ -1829,7 +1833,8 @@ nm_setting_team_class_init(NMSettingTeamClass *klass)
      */
     _nm_properties_override_dbus(properties_override,
                                  "interface-name",
-                                 &nm_sett_info_propert_type_deprecated_interface_name);
+                                 &nm_sett_info_propert_type_deprecated_interface_name,
+                                 .dbus_deprecated = TRUE);
 
     g_object_class_install_properties(object_class, G_N_ELEMENTS(obj_properties), obj_properties);
 
diff --git a/src/libnm-core-impl/nm-setting-vlan.c b/src/libnm-core-impl/nm-setting-vlan.c
index 5f50acdd..696799d8 100644
--- a/src/libnm-core-impl/nm-setting-vlan.c
+++ b/src/libnm-core-impl/nm-setting-vlan.c
@@ -30,6 +30,7 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMSettingVlan,
                              PROP_PARENT,
                              PROP_ID,
                              PROP_FLAGS,
+                             PROP_PROTOCOL,
                              PROP_INGRESS_PRIORITY_MAP,
                              PROP_EGRESS_PRIORITY_MAP, );
 
@@ -37,6 +38,7 @@ typedef struct {
     GSList *ingress_priority_map;
     GSList *egress_priority_map;
     char   *parent;
+    char   *protocol;
     guint32 id;
     guint32 flags;
 } NMSettingVlanPrivate;
@@ -82,6 +84,22 @@ nm_setting_vlan_get_parent(NMSettingVlan *setting)
 }
 
 /**
+ * nm_setting_vlan_get_protocol:
+ * @setting: the #NMSettingVlan
+ *
+ * Since: 1.42
+ *
+ * Returns: the #NMSettingVlan:protocol property of the setting
+ **/
+const char *
+nm_setting_vlan_get_protocol(NMSettingVlan *setting)
+{
+    g_return_val_if_fail(NM_IS_SETTING_VLAN(setting), NULL);
+
+    return NM_SETTING_VLAN_GET_PRIVATE(setting)->protocol;
+}
+
+/**
  * nm_setting_vlan_get_id:
  * @setting: the #NMSettingVlan
  *
@@ -662,6 +680,16 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
         return FALSE;
     }
 
+    if (!NM_IN_STRSET(priv->protocol, NULL, "802.1Q", "802.1ad")) {
+        g_set_error(error,
+                    NM_CONNECTION_ERROR,
+                    NM_CONNECTION_ERROR_INVALID_PROPERTY,
+                    _("invalid VLAN protocol %s: must be '802.1Q' or '802.1ad'"),
+                    priv->protocol);
+        g_prefix_error(error, "%s.%s: ", NM_SETTING_VLAN_SETTING_NAME, NM_SETTING_VLAN_PROTOCOL);
+        return FALSE;
+    }
+
     if (connection && !s_wired) {
         /* technically, a VLAN setting does not require an ethernet setting. However,
          * the ifcfg-rh reader always adds a ethernet setting when reading a vlan setting.
@@ -678,13 +706,13 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
 }
 
 static GVariant *
-_override_flags_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
+vlan_flags_to_dbus(_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(_NM_SETT_INFO_PROP_MISSING_FROM_DBUS_FCN_ARGS _nm_nil)
+vlan_flags_missing_from_dbus(_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. */
@@ -909,13 +937,39 @@ 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,
+                                       .to_dbus_fcn = vlan_flags_to_dbus,
                                        .compare_fcn = _nm_setting_property_compare_fcn_default,
-                                       .missing_from_dbus_fcn = _override_flags_not_set,
+                                       .missing_from_dbus_fcn = vlan_flags_missing_from_dbus,
                                        .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_gprop,
                                        .from_dbus_is_full = TRUE));
 
     /**
+     * NMSettingVlan:protocol:
+     *
+     * Specifies the VLAN protocol to use for encapsulation.
+     *
+     * Supported values are: '802.1Q', '802.1ad'. If not specified the default
+     * value is '802.1Q'.
+     *
+     * Since: 1.42
+     **/
+    /* ---ifcfg-rh---
+     * property: protocol
+     * variable: VLAN_PROTOCOL
+     * description: VLAN protocol.
+     * example: VLAN_PROTOCOL="802.1ad"
+     * ---end---
+     */
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_VLAN_PROTOCOL,
+                                              PROP_PROTOCOL,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingVlanPrivate,
+                                              protocol,
+                                              .direct_string_is_refstr = TRUE);
+
+    /**
      * NMSettingVlan:ingress-priority-map:
      *
      * For incoming packets, a list of mappings from 802.1p priorities to Linux
@@ -965,7 +1019,8 @@ nm_setting_vlan_class_init(NMSettingVlanClass *klass)
      *   but VLAN id from DEVICE takes precedence over VLAN_ID.
      * example: PHYSDEV=eth0, VLAN_ID=12; or DEVICE=eth0.12
      * ---end---
-     * ---dbus---
+     */
+    /* ---dbus---
      * property: interface-name
      * format: string
      * description: Deprecated in favor of connection.interface-name, but can
@@ -975,7 +1030,8 @@ nm_setting_vlan_class_init(NMSettingVlanClass *klass)
      */
     _nm_properties_override_dbus(properties_override,
                                  "interface-name",
-                                 &nm_sett_info_propert_type_deprecated_interface_name);
+                                 &nm_sett_info_propert_type_deprecated_interface_name,
+                                 .dbus_deprecated = TRUE, );
 
     g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
diff --git a/src/libnm-core-impl/nm-setting-vpn.c b/src/libnm-core-impl/nm-setting-vpn.c
index 7e6f18dd..4ff98908 100644
--- a/src/libnm-core-impl/nm-setting-vpn.c
+++ b/src/libnm-core-impl/nm-setting-vpn.c
@@ -148,7 +148,7 @@ nm_setting_vpn_get_user_name(NMSettingVpn *setting)
  *
  * Returns: the #NMSettingVpn:persistent property of the setting
  *
- * Since: 1.42, 1.40.4
+ * Since: 1.42
  **/
 gboolean
 nm_setting_vpn_get_persistent(NMSettingVpn *setting)
@@ -803,7 +803,7 @@ set_secret_flags(NMSetting           *setting,
 }
 
 static GPtrArray *
-need_secrets(NMSetting *setting)
+need_secrets(NMSetting *setting, gboolean check_rerequest)
 {
     /* Assume that VPN connections need secrets since they almost always will */
     return g_ptr_array_sized_new(1);
@@ -1132,7 +1132,7 @@ nm_setting_vpn_class_init(NMSettingVpnClass *klass)
                                                persistent);
 
     /**
-     * NMSettingVpn:data: (type GHashTable(utf8,utf8)):
+     * NMSettingVpn:data: (type GHashTable(utf8,utf8))
      *
      * Dictionary of key/value pairs of VPN plugin specific data.  Both keys and
      * values must be strings.
@@ -1155,7 +1155,7 @@ nm_setting_vpn_class_init(NMSettingVpnClass *klass)
                                  &nm_sett_info_propert_type_strdict);
 
     /**
-     * NMSettingVpn:secrets: (type GHashTable(utf8,utf8)):
+     * NMSettingVpn:secrets: (type GHashTable(utf8,utf8))
      *
      * Dictionary of key/value pairs of VPN plugin specific secrets like
      * passwords or private keys.  Both keys and values must be strings.
diff --git a/src/libnm-core-impl/nm-setting-vxlan.c b/src/libnm-core-impl/nm-setting-vxlan.c
index f617a073..3fb4c48d 100644
--- a/src/libnm-core-impl/nm-setting-vxlan.c
+++ b/src/libnm-core-impl/nm-setting-vxlan.c
@@ -344,9 +344,9 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
     gboolean               remote_is_valid = TRUE;
     gboolean               local_is_valid  = TRUE;
 
-    if (priv->remote && !nm_utils_parse_inaddr_bin(addr_family, priv->remote, &addr_family, NULL))
+    if (priv->remote && !nm_inet_parse_bin(addr_family, priv->remote, &addr_family, NULL))
         remote_is_valid = FALSE;
-    if (priv->local && !nm_utils_parse_inaddr_bin(addr_family, priv->local, &addr_family, NULL))
+    if (priv->local && !nm_inet_parse_bin(addr_family, priv->local, &addr_family, NULL))
         local_is_valid = FALSE;
 
     if (!remote_is_valid) {
diff --git a/src/libnm-core-impl/nm-setting-wimax.c b/src/libnm-core-impl/nm-setting-wimax.c
index 1e2b51c3..c33ad3ea 100644
--- a/src/libnm-core-impl/nm-setting-wimax.c
+++ b/src/libnm-core-impl/nm-setting-wimax.c
@@ -178,7 +178,8 @@ nm_setting_wimax_class_init(NMSettingWimaxClass *klass)
                                               PROP_NETWORK_NAME,
                                               NM_SETTING_PARAM_NONE,
                                               NMSettingWimaxPrivate,
-                                              network_name);
+                                              network_name,
+                                              .is_deprecated = TRUE, );
 
     /**
      * NMSettingWimax:mac-address:
@@ -196,7 +197,8 @@ nm_setting_wimax_class_init(NMSettingWimaxClass *klass)
                                                    NM_SETTING_PARAM_NONE,
                                                    NMSettingWimaxPrivate,
                                                    mac_address,
-                                                   .direct_set_string_mac_address_len = ETH_ALEN);
+                                                   .direct_set_string_mac_address_len = ETH_ALEN,
+                                                   .is_deprecated                     = TRUE, );
 
     g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
diff --git a/src/libnm-core-impl/nm-setting-wired.c b/src/libnm-core-impl/nm-setting-wired.c
index 830b2c73..3acd5d97 100644
--- a/src/libnm-core-impl/nm-setting-wired.c
+++ b/src/libnm-core-impl/nm-setting-wired.c
@@ -292,7 +292,7 @@ nm_setting_wired_get_mac_address_blacklist(NMSettingWired *setting)
     g_return_val_if_fail(NM_IS_SETTING_WIRED(setting), NULL);
 
     priv = NM_SETTING_WIRED_GET_PRIVATE(setting);
-    return (const char *const *) priv->mac_address_blacklist->data;
+    return nm_g_array_data(priv->mac_address_blacklist);
 }
 
 /**
@@ -327,7 +327,7 @@ nm_setting_wired_get_mac_blacklist_item(NMSettingWired *setting, guint32 idx)
     priv = NM_SETTING_WIRED_GET_PRIVATE(setting);
     g_return_val_if_fail(idx <= priv->mac_address_blacklist->len, NULL);
 
-    return g_array_index(priv->mac_address_blacklist, const char *, idx);
+    return nm_g_array_index(priv->mac_address_blacklist, const char *, idx);
 }
 
 /**
@@ -355,7 +355,7 @@ nm_setting_wired_add_mac_blacklist_item(NMSettingWired *setting, const char *mac
 
     priv = NM_SETTING_WIRED_GET_PRIVATE(setting);
     for (i = 0; i < priv->mac_address_blacklist->len; i++) {
-        candidate = g_array_index(priv->mac_address_blacklist, char *, i);
+        candidate = nm_g_array_index(priv->mac_address_blacklist, char *, i);
         if (nm_utils_hwaddr_matches(mac, -1, candidate, -1))
             return FALSE;
     }
@@ -409,7 +409,7 @@ nm_setting_wired_remove_mac_blacklist_item_by_value(NMSettingWired *setting, con
 
     priv = NM_SETTING_WIRED_GET_PRIVATE(setting);
     for (i = 0; i < priv->mac_address_blacklist->len; i++) {
-        candidate = g_array_index(priv->mac_address_blacklist, char *, i);
+        candidate = nm_g_array_index(priv->mac_address_blacklist, char *, i);
         if (!nm_utils_hwaddr_matches(mac, -1, candidate, -1)) {
             g_array_remove_index(priv->mac_address_blacklist, i);
             _notify(setting, PROP_MAC_ADDRESS_BLACKLIST);
@@ -810,7 +810,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
     }
 
     for (i = 0; i < priv->mac_address_blacklist->len; i++) {
-        const char *mac = g_array_index(priv->mac_address_blacklist, const char *, i);
+        const char *mac = nm_g_array_index(priv->mac_address_blacklist, const char *, i);
 
         if (!nm_utils_hwaddr_valid(mac, ETH_ALEN)) {
             g_set_error(error,
@@ -1007,7 +1007,7 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
         g_value_set_string(value, nm_setting_wired_get_cloned_mac_address(setting));
         break;
     case PROP_MAC_ADDRESS_BLACKLIST:
-        g_value_set_boxed(value, (char **) priv->mac_address_blacklist->data);
+        g_value_set_boxed(value, nm_g_array_data(priv->mac_address_blacklist));
         break;
     case PROP_S390_SUBCHANNELS:
         g_value_set_boxed(value, priv->s390_subchannels);
@@ -1311,7 +1311,8 @@ nm_setting_wired_class_init(NMSettingWiredClass *klass)
      *   (e.g. 00:22:68:12:79:A2), or semicolon separated list of 6 bytes (obsolete)
      *   (e.g. 0;34;104;18;121;162)
      * ---end---
-     * ---ifcfg-rh---
+     */
+    /* ---ifcfg-rh---
      * property: mac-address
      * variable: HWADDR
      * description: Hardware address of the device in traditional hex-digits-and-colons
@@ -1359,13 +1360,15 @@ nm_setting_wired_class_init(NMSettingWiredClass *klass)
      *   (e.g. 00:22:68:12:79:B2), or semicolon separated list of 6 bytes (obsolete)
      *   (e.g. 0;34;104;18;121;178).
      * ---end---
-     * ---ifcfg-rh---
+     */
+    /* ---ifcfg-rh---
      * property: cloned-mac-address
      * variable: MACADDR
      * description: Cloned (spoofed) MAC address in traditional hex-digits-and-colons
      *    notation (e.g. 00:22:68:14:5A:99).
      * ---end---
-     * ---dbus---
+     */
+    /* ---dbus---
      * property: cloned-mac-address
      * format: byte array
      * description: This D-Bus field is deprecated in favor of "assigned-mac-address"
@@ -1382,11 +1385,13 @@ nm_setting_wired_class_init(NMSettingWiredClass *klass)
     _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, ));
+        NM_SETT_INFO_PROPERT_TYPE_DBUS(
+            G_VARIANT_TYPE_BYTESTRING,
+            .compare_fcn           = compare_fcn_cloned_mac_address,
+            .to_dbus_fcn           = _nm_sett_info_prop_to_dbus_fcn_cloned_mac_address,
+            .from_dbus_fcn         = _nm_sett_info_prop_from_dbus_fcn_cloned_mac_address,
+            .missing_from_dbus_fcn = _nm_sett_info_prop_missing_from_dbus_fcn_cloned_mac_address, ),
+        .dbus_deprecated = TRUE);
 
     /* ---dbus---
      * property: assigned-mac-address
@@ -1465,7 +1470,8 @@ nm_setting_wired_class_init(NMSettingWiredClass *klass)
      * description: MAC address blacklist.
      * example: mac-address-blacklist= 00:22:68:12:79:A6;00:22:68:12:79:78
      * ---end---
-     * ---ifcfg-rh---
+     */
+    /* ---ifcfg-rh---
      * property: mac-address-blacklist
      * variable: HWADDR_BLACKLIST(+)
      * description: It denies usage of the connection for any device whose address
@@ -1551,7 +1557,7 @@ nm_setting_wired_class_init(NMSettingWiredClass *klass)
                                               s390_nettype);
 
     /**
-     * NMSettingWired:s390-options: (type GHashTable(utf8,utf8)):
+     * NMSettingWired:s390-options: (type GHashTable(utf8,utf8))
      *
      * Dictionary of key/value pairs of s390-specific device options.  Both keys
      * and values must be strings.  Allowed keys include "portno", "layer2",
diff --git a/src/libnm-core-impl/nm-setting-wireguard.c b/src/libnm-core-impl/nm-setting-wireguard.c
index 599ded3c..a981187f 100644
--- a/src/libnm-core-impl/nm-setting-wireguard.c
+++ b/src/libnm-core-impl/nm-setting-wireguard.c
@@ -50,7 +50,7 @@ struct _NMWireGuardPeer {
     char                *public_key;
     char                *preshared_key;
     GPtrArray           *allowed_ips;
-    guint                refcount;
+    int                  refcount;
     NMSettingSecretFlags preshared_key_flags;
     guint16              persistent_keepalive;
     bool                 public_key_valid : 1;
@@ -127,11 +127,11 @@ nm_wireguard_peer_new_clone(const NMWireGuardPeer *self, gboolean with_secrets)
  * nm_wireguard_peer_ref:
  * @self: (allow-none): the #NMWireGuardPeer instance
  *
- * This is not thread-safe.
- *
  * Returns: returns the input argument @self after incrementing
  *   the reference count.
  *
+ * Since 1.42, ref-counting of #NMWireGuardPeer is thread-safe.
+ *
  * Since: 1.16
  */
 NMWireGuardPeer *
@@ -142,9 +142,9 @@ nm_wireguard_peer_ref(NMWireGuardPeer *self)
 
     g_return_val_if_fail(NM_IS_WIREGUARD_PEER(self, TRUE), NULL);
 
-    nm_assert(self->refcount < G_MAXUINT);
+    nm_assert(self->refcount < G_MAXINT);
 
-    self->refcount++;
+    g_atomic_int_inc(&self->refcount);
     return self;
 }
 
@@ -155,7 +155,7 @@ nm_wireguard_peer_ref(NMWireGuardPeer *self)
  * Drop a reference to @self. If the last reference is dropped,
  * the instance is freed and all associate data released.
  *
- * This is not thread-safe.
+ * Since 1.42, ref-counting of #NMWireGuardPeer is thread-safe.
  *
  * Since: 1.16
  */
@@ -167,7 +167,7 @@ nm_wireguard_peer_unref(NMWireGuardPeer *self)
 
     g_return_if_fail(NM_IS_WIREGUARD_PEER(self, TRUE));
 
-    if (--self->refcount > 0)
+    if (!g_atomic_int_dec_and_test(&self->refcount))
         return;
 
     nm_sock_addr_endpoint_unref(self->endpoint);
@@ -628,18 +628,18 @@ _peer_append_allowed_ip(NMWireGuardPeer *self, const char *allowed_ip, gboolean
 
     /* normalize the address (if it is valid. Otherwise, take it
      * as-is (it will render the instance invalid). */
-    if (!nm_utils_parse_inaddr_prefix_bin(AF_UNSPEC, allowed_ip, &addr_family, &addrbin, &prefix)) {
+    if (!nm_inet_parse_with_prefix_bin(AF_UNSPEC, allowed_ip, &addr_family, &addrbin, &prefix)) {
         if (!accept_invalid)
             return FALSE;
         /* mark the entry as invalid by having a "X" prefix. */
         str      = g_strconcat(ALLOWED_IP_INVALID_X_STR, allowed_ip, NULL);
         is_valid = FALSE;
     } else {
-        char addrstr[NM_UTILS_INET_ADDRSTRLEN];
+        char addrstr[NM_INET_ADDRSTRLEN];
 
         nm_assert_addr_family(addr_family);
 
-        nm_utils_inet_ntop(addr_family, &addrbin, addrstr);
+        nm_inet_ntop(addr_family, &addrbin, addrstr);
         if (prefix >= 0)
             str = g_strdup_printf("%s/%d", addrstr, prefix);
         else
@@ -1460,7 +1460,7 @@ nm_setting_wireguard_clear_peers(NMSettingWireGuard *self)
 /*****************************************************************************/
 
 static GVariant *
-_peers_dbus_only_synth(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
+peers_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
     NMSettingWireGuard        *self = NM_SETTING_WIREGUARD(setting);
     NMSettingWireGuardPrivate *priv;
@@ -1558,7 +1558,7 @@ _peers_dbus_only_synth(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 }
 
 static gboolean
-_peers_dbus_only_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
+peers_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 {
     GVariantIter iter_peers;
     GVariant    *peer_var;
@@ -1800,13 +1800,13 @@ verify_secrets(NMSetting *setting, NMConnection *connection, GError **error)
 }
 
 static GPtrArray *
-need_secrets(NMSetting *setting)
+need_secrets(NMSetting *setting, gboolean check_rerequest)
 {
     NMSettingWireGuardPrivate *priv    = NM_SETTING_WIREGUARD_GET_PRIVATE(setting);
     GPtrArray                 *secrets = NULL;
     guint                      i;
 
-    if (!priv->private_key_valid) {
+    if (check_rerequest || !priv->private_key_valid) {
         secrets = g_ptr_array_new_full(1, g_free);
         g_ptr_array_add(secrets, g_strdup(NM_SETTING_WIREGUARD_PRIVATE_KEY));
     }
@@ -1857,9 +1857,7 @@ clear_secrets(const NMSettInfoSetting         *sett_info,
                 if (j++ < 5) {
                     /* we use alloca() inside a loop here, but it is guarded to happen at most
                      * a few times. */
-                    name = peers_psk_get_secret_name_a(/* lgtm [cpp/alloca-in-loop] */
-                                                       peer->public_key,
-                                                       &name_free);
+                    name = peers_psk_get_secret_name_a(peer->public_key, &name_free);
                 } else {
                     name_free = peers_psk_get_secret_name_dup(peer->public_key);
                     name      = name_free;
@@ -2528,9 +2526,9 @@ nm_setting_wireguard_class_init(NMSettingWireGuardClass *klass)
         properties_override,
         NM_SETTING_WIREGUARD_PEERS,
         NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("aa{sv}"),
-                                       .to_dbus_fcn   = _peers_dbus_only_synth,
+                                       .to_dbus_fcn   = peers_to_dbus,
                                        .compare_fcn   = compare_fcn_peers,
-                                       .from_dbus_fcn = _peers_dbus_only_set, ));
+                                       .from_dbus_fcn = peers_from_dbus, ));
 
     g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
diff --git a/src/libnm-core-impl/nm-setting-wireless-security.c b/src/libnm-core-impl/nm-setting-wireless-security.c
index ebefd504..a7d12df4 100644
--- a/src/libnm-core-impl/nm-setting-wireless-security.c
+++ b/src/libnm-core-impl/nm-setting-wireless-security.c
@@ -815,7 +815,7 @@ nm_setting_wireless_security_get_fils(NMSettingWirelessSecurity *setting)
 }
 
 static GPtrArray *
-need_secrets(NMSetting *setting)
+need_secrets(NMSetting *setting, gboolean check_rerequest)
 {
     NMSettingWirelessSecurity        *self = NM_SETTING_WIRELESS_SECURITY(setting);
     NMSettingWirelessSecurityPrivate *priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE(self);
@@ -828,22 +828,22 @@ need_secrets(NMSetting *setting)
     /* Static WEP */
     if (strcmp(priv->key_mgmt, "none") == 0) {
         if ((priv->wep_tx_keyidx == 0)
-            && !nm_utils_wep_key_valid(priv->wep_key0, priv->wep_key_type)) {
+            && (check_rerequest || !nm_utils_wep_key_valid(priv->wep_key0, priv->wep_key_type))) {
             g_ptr_array_add(secrets, NM_SETTING_WIRELESS_SECURITY_WEP_KEY0);
             return secrets;
         }
         if ((priv->wep_tx_keyidx == 1)
-            && !nm_utils_wep_key_valid(priv->wep_key1, priv->wep_key_type)) {
+            && (check_rerequest || !nm_utils_wep_key_valid(priv->wep_key1, priv->wep_key_type))) {
             g_ptr_array_add(secrets, NM_SETTING_WIRELESS_SECURITY_WEP_KEY1);
             return secrets;
         }
         if ((priv->wep_tx_keyidx == 2)
-            && !nm_utils_wep_key_valid(priv->wep_key2, priv->wep_key_type)) {
+            && (check_rerequest || !nm_utils_wep_key_valid(priv->wep_key2, priv->wep_key_type))) {
             g_ptr_array_add(secrets, NM_SETTING_WIRELESS_SECURITY_WEP_KEY2);
             return secrets;
         }
         if ((priv->wep_tx_keyidx == 3)
-            && !nm_utils_wep_key_valid(priv->wep_key3, priv->wep_key_type)) {
+            && (check_rerequest || !nm_utils_wep_key_valid(priv->wep_key3, priv->wep_key_type))) {
             g_ptr_array_add(secrets, NM_SETTING_WIRELESS_SECURITY_WEP_KEY3);
             return secrets;
         }
@@ -852,7 +852,7 @@ need_secrets(NMSetting *setting)
 
     /* WPA-PSK infrastructure */
     if (strcmp(priv->key_mgmt, "wpa-psk") == 0) {
-        if (!nm_utils_wpa_psk_valid(priv->psk)) {
+        if (check_rerequest || !nm_utils_wpa_psk_valid(priv->psk)) {
             g_ptr_array_add(secrets, NM_SETTING_WIRELESS_SECURITY_PSK);
             return secrets;
         }
@@ -861,7 +861,7 @@ need_secrets(NMSetting *setting)
 
     /* SAE, used in MESH and WPA3-Personal */
     if (strcmp(priv->key_mgmt, "sae") == 0) {
-        if (!priv->psk || !*priv->psk) {
+        if (check_rerequest || !priv->psk || !*priv->psk) {
             g_ptr_array_add(secrets, NM_SETTING_WIRELESS_SECURITY_PSK);
             return secrets;
         }
@@ -870,7 +870,7 @@ need_secrets(NMSetting *setting)
 
     /* LEAP */
     if (priv->auth_alg && !strcmp(priv->auth_alg, "leap") && !strcmp(priv->key_mgmt, "ieee8021x")) {
-        if (!priv->leap_password || !*priv->leap_password) {
+        if (check_rerequest || !priv->leap_password || !*priv->leap_password) {
             g_ptr_array_add(secrets, NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD);
             return secrets;
         }
diff --git a/src/libnm-core-impl/nm-setting-wireless.c b/src/libnm-core-impl/nm-setting-wireless.c
index cf391598..33e16d4b 100644
--- a/src/libnm-core-impl/nm-setting-wireless.c
+++ b/src/libnm-core-impl/nm-setting-wireless.c
@@ -473,7 +473,7 @@ nm_setting_wireless_get_mac_address_blacklist(NMSettingWireless *setting)
     g_return_val_if_fail(NM_IS_SETTING_WIRELESS(setting), NULL);
 
     priv = NM_SETTING_WIRELESS_GET_PRIVATE(setting);
-    return (const char *const *) priv->mac_address_blacklist->data;
+    return nm_g_array_data(priv->mac_address_blacklist);
 }
 
 /**
@@ -508,7 +508,7 @@ nm_setting_wireless_get_mac_blacklist_item(NMSettingWireless *setting, guint32 i
     priv = NM_SETTING_WIRELESS_GET_PRIVATE(setting);
     g_return_val_if_fail(idx <= priv->mac_address_blacklist->len, NULL);
 
-    return g_array_index(priv->mac_address_blacklist, const char *, idx);
+    return nm_g_array_index(priv->mac_address_blacklist, const char *, idx);
 }
 
 /**
@@ -536,7 +536,7 @@ nm_setting_wireless_add_mac_blacklist_item(NMSettingWireless *setting, const cha
 
     priv = NM_SETTING_WIRELESS_GET_PRIVATE(setting);
     for (i = 0; i < priv->mac_address_blacklist->len; i++) {
-        candidate = g_array_index(priv->mac_address_blacklist, char *, i);
+        candidate = nm_g_array_index(priv->mac_address_blacklist, char *, i);
         if (nm_utils_hwaddr_matches(mac, -1, candidate, -1))
             return FALSE;
     }
@@ -590,7 +590,7 @@ nm_setting_wireless_remove_mac_blacklist_item_by_value(NMSettingWireless *settin
 
     priv = NM_SETTING_WIRELESS_GET_PRIVATE(setting);
     for (i = 0; i < priv->mac_address_blacklist->len; i++) {
-        candidate = g_array_index(priv->mac_address_blacklist, char *, i);
+        candidate = nm_g_array_index(priv->mac_address_blacklist, char *, i);
         if (!nm_utils_hwaddr_matches(mac, -1, candidate, -1)) {
             g_array_remove_index(priv->mac_address_blacklist, i);
             _notify(setting, PROP_MAC_ADDRESS_BLACKLIST);
@@ -988,7 +988,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
     }
 
     for (i = 0; i < priv->mac_address_blacklist->len; i++) {
-        const char *mac = g_array_index(priv->mac_address_blacklist, const char *, i);
+        const char *mac = nm_g_array_index(priv->mac_address_blacklist, const char *, i);
 
         if (!nm_utils_hwaddr_valid(mac, ETH_ALEN)) {
             g_set_error(error,
@@ -1123,7 +1123,7 @@ compare_fcn_seen_bssids(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil)
 /*****************************************************************************/
 
 static GVariant *
-nm_setting_wireless_get_security(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
+security_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
     if (!_nm_connection_serialize_non_secret(flags))
         return NULL;
@@ -1174,7 +1174,7 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
         g_value_set_string(value, nm_setting_wireless_get_cloned_mac_address(setting));
         break;
     case PROP_MAC_ADDRESS_BLACKLIST:
-        g_value_set_boxed(value, (char **) priv->mac_address_blacklist->data);
+        g_value_set_boxed(value, nm_g_array_data(priv->mac_address_blacklist));
         break;
     case PROP_SEEN_BSSIDS:
         g_value_take_boxed(
@@ -1311,7 +1311,8 @@ nm_setting_wireless_class_init(NMSettingWirelessClass *klass)
      * description: SSID of Wi-Fi network.
      * example: ssid=Quick Net
      * ---end---
-     * ---ifcfg-rh---
+     */
+    /* ---ifcfg-rh---
      * property: ssid
      * variable: ESSID
      * description: SSID of Wi-Fi network.
@@ -1493,7 +1494,8 @@ nm_setting_wireless_class_init(NMSettingWirelessClass *klass)
      *   (e.g. 00:22:68:12:79:A2), or semicolon separated list of 6 bytes (obsolete)
      *   (e.g. 0;34;104;18;121;162).
      * ---end---
-     * ---ifcfg-rh---
+     */
+    /* ---ifcfg-rh---
      * property: mac-address
      * variable: HWADDR
      * description: Hardware address of the device in traditional hex-digits-and-colons
@@ -1540,13 +1542,15 @@ nm_setting_wireless_class_init(NMSettingWirelessClass *klass)
      *   (e.g. 00:22:68:12:79:B2), or semicolon separated list of 6 bytes (obsolete)
      *   (e.g. 0;34;104;18;121;178).
      * ---end---
-     * ---ifcfg-rh---
+     */
+    /* ---ifcfg-rh---
      * property: cloned-mac-address
      * variable: MACADDR
      * description: Cloned (spoofed) MAC address in traditional hex-digits-and-colons
      *    notation (e.g. 00:22:68:14:5A:99).
      * ---end---
-     * ---dbus---
+     */
+    /* ---dbus---
      * property: cloned-mac-address
      * format: byte array
      * description: This D-Bus field is deprecated in favor of "assigned-mac-address"
@@ -1563,11 +1567,13 @@ nm_setting_wireless_class_init(NMSettingWirelessClass *klass)
     _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, ));
+        NM_SETT_INFO_PROPERT_TYPE_DBUS(
+            G_VARIANT_TYPE_BYTESTRING,
+            .compare_fcn           = compare_fcn_cloned_mac_address,
+            .to_dbus_fcn           = _nm_sett_info_prop_to_dbus_fcn_cloned_mac_address,
+            .from_dbus_fcn         = _nm_sett_info_prop_from_dbus_fcn_cloned_mac_address,
+            .missing_from_dbus_fcn = _nm_sett_info_prop_missing_from_dbus_fcn_cloned_mac_address, ),
+        .dbus_deprecated = TRUE, );
 
     /* ---dbus---
      * property: assigned-mac-address
@@ -1645,7 +1651,8 @@ nm_setting_wireless_class_init(NMSettingWirelessClass *klass)
      * description: MAC address blacklist.
      * example: mac-address-blacklist= 00:22:68:12:79:A6;00:22:68:12:79:78
      * ---end---
-     * ---ifcfg-rh---
+     */
+    /* ---ifcfg-rh---
      * property: mac-address-blacklist
      * variable: HWADDR_BLACKLIST(+)
      * description: It denies usage of the connection for any device whose address
@@ -1782,11 +1789,10 @@ nm_setting_wireless_class_init(NMSettingWirelessClass *klass)
      * the user has set a global default to randomize and the supplicant
      * supports randomization),  %NM_SETTING_MAC_RANDOMIZATION_NEVER (never
      * randomize the MAC address), or %NM_SETTING_MAC_RANDOMIZATION_ALWAYS
-     * (always randomize the MAC address). This property is deprecated for
-     * 'cloned-mac-address'.
+     * (always randomize the MAC address).
      *
      * Since: 1.2
-     * Deprecated: 1.4: Deprecated by NMSettingWireless:cloned-mac-address property.
+     * Deprecated: 1.4: Use the #NMSettingWireless:cloned-mac-address property instead.
      **/
     /* ---ifcfg-rh---
      * property: mac-address-randomization
@@ -1805,7 +1811,8 @@ nm_setting_wireless_class_init(NMSettingWirelessClass *klass)
                                               NM_SETTING_MAC_RANDOMIZATION_DEFAULT,
                                               NM_SETTING_PARAM_NONE,
                                               NMSettingWirelessPrivate,
-                                              mac_address_randomization);
+                                              mac_address_randomization,
+                                              .is_deprecated = TRUE, );
 
     /* Compatibility for deprecated property */
     /* ---ifcfg-rh---
@@ -1813,7 +1820,8 @@ nm_setting_wireless_class_init(NMSettingWirelessClass *klass)
      * variable: (none)
      * description: This property is deprecated and not handled by ifcfg-rh-plugin.
      * ---end---
-     * ---dbus---
+     */
+    /* ---dbus---
      * property: security
      * description: This property is deprecated, but can be set to the value
      *   '802-11-wireless-security' when a wireless security setting is also
@@ -1825,8 +1833,9 @@ 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,
-                                       .compare_fcn = _nm_setting_property_compare_fcn_ignore, ));
+                                       .to_dbus_fcn = security_to_dbus,
+                                       .compare_fcn = _nm_setting_property_compare_fcn_ignore, ),
+        .dbus_deprecated = TRUE, );
 
     /**
      * NMSettingWireless:wake-on-wlan:
diff --git a/src/libnm-core-impl/nm-setting-wpan.c b/src/libnm-core-impl/nm-setting-wpan.c
index a3e56ded..bc84b01e 100644
--- a/src/libnm-core-impl/nm-setting-wpan.c
+++ b/src/libnm-core-impl/nm-setting-wpan.c
@@ -77,7 +77,7 @@ G_DEFINE_TYPE(NMSettingWpan, nm_setting_wpan, NM_TYPE_SETTING)
  *
  * Returns: the #NMSettingWpan:mac-address property of the setting
  *
- * Since: 1.42, 1.40.4
+ * Since: 1.42
  **/
 const char *
 nm_setting_wpan_get_mac_address(NMSettingWpan *setting)
@@ -93,7 +93,7 @@ nm_setting_wpan_get_mac_address(NMSettingWpan *setting)
  *
  * Returns: the #NMSettingWpan:pan-id property of the setting
  *
- * Since: 1.42, 1.40.4
+ * Since: 1.42
  **/
 guint16
 nm_setting_wpan_get_pan_id(NMSettingWpan *setting)
@@ -109,7 +109,7 @@ nm_setting_wpan_get_pan_id(NMSettingWpan *setting)
  *
  * Returns: the #NMSettingWpan:short-address property of the setting
  *
- * Since: 1.42, 1.40.4
+ * Since: 1.42
  **/
 guint16
 nm_setting_wpan_get_short_address(NMSettingWpan *setting)
@@ -125,7 +125,7 @@ nm_setting_wpan_get_short_address(NMSettingWpan *setting)
  *
  * Returns: the #NMSettingWpan:page property of the setting
  *
- * Since: 1.42, 1.40.4
+ * Since: 1.42
  **/
 gint16
 nm_setting_wpan_get_page(NMSettingWpan *setting)
@@ -141,7 +141,7 @@ nm_setting_wpan_get_page(NMSettingWpan *setting)
  *
  * Returns: the #NMSettingWpan:channel property of the setting
  *
- * Since: 1.42, 1.40.4
+ * Since: 1.42
  **/
 gint16
 nm_setting_wpan_get_channel(NMSettingWpan *setting)
@@ -213,7 +213,7 @@ nm_setting_wpan_init(NMSettingWpan *setting)
  *
  * Returns: (transfer full): the new empty #NMSettingWpan object
  *
- * Since: 1.42, 1.40.4
+ * Since: 1.42
  **/
 NMSetting *
 nm_setting_wpan_new(void)
diff --git a/src/libnm-core-impl/nm-setting.c b/src/libnm-core-impl/nm-setting.c
index b6f72137..5a46a1b4 100644
--- a/src/libnm-core-impl/nm-setting.c
+++ b/src/libnm-core-impl/nm-setting.c
@@ -331,7 +331,7 @@ _nm_setting_class_commit(NMSettingClass             *setting_class,
         override_len = properties_override->len;
 
         for (i = 0; i < override_len; i++) {
-            NMSettInfoProperty *p = &g_array_index(properties_override, NMSettInfoProperty, i);
+            NMSettInfoProperty *p = &nm_g_array_index(properties_override, NMSettInfoProperty, i);
 
             nm_assert((!!p->name) != (!!p->param_spec));
 
@@ -346,14 +346,14 @@ _nm_setting_class_commit(NMSettingClass             *setting_class,
 #if NM_MORE_ASSERTS > 10
     /* assert that properties_override is constructed consistently. */
     for (i = 0; i < override_len; i++) {
-        const NMSettInfoProperty *p = &g_array_index(properties_override, NMSettInfoProperty, i);
+        const NMSettInfoProperty *p = &nm_g_array_index(properties_override, NMSettInfoProperty, i);
         gboolean                  found = FALSE;
         guint                     k;
 
-        nm_assert(
-            !_nm_sett_info_property_find_in_array((NMSettInfoProperty *) properties_override->data,
-                                                  i,
-                                                  p->name));
+        nm_assert(!_nm_sett_info_property_find_in_array(
+            nm_g_array_index_p(properties_override, NMSettInfoProperty, 0),
+            i,
+            p->name));
         for (k = 0; k < n_property_specs; k++) {
             if (!nm_streq(property_specs[k]->name, p->name))
                 continue;
@@ -369,20 +369,20 @@ _nm_setting_class_commit(NMSettingClass             *setting_class,
         const char         *name = property_specs[i]->name;
         NMSettInfoProperty *p;
 
-        if (_nm_sett_info_property_find_in_array((NMSettInfoProperty *) properties_override->data,
-                                                 override_len,
-                                                 name))
+        if (_nm_sett_info_property_find_in_array(
+                nm_g_array_index_p(properties_override, NMSettInfoProperty, 0),
+                override_len,
+                name))
             continue;
 
-        g_array_set_size(properties_override, properties_override->len + 1);
-        p = &g_array_index(properties_override, NMSettInfoProperty, properties_override->len - 1);
+        p = nm_g_array_append_new(properties_override, NMSettInfoProperty);
         memset(p, 0, sizeof(*p));
         p->name       = name;
         p->param_spec = property_specs[i];
     }
 
     for (i = 0; i < properties_override->len; i++) {
-        NMSettInfoProperty *p = &g_array_index(properties_override, NMSettInfoProperty, i);
+        NMSettInfoProperty *p = &nm_g_array_index(properties_override, NMSettInfoProperty, i);
         GType               vtype;
 
         if (p->property_type)
@@ -492,12 +492,12 @@ _nm_sett_info_setting_get_property_info(const NMSettInfoSetting *sett_info,
         return NULL;
 
     G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMSettInfoProperty, name) == 0);
-    idx = nm_utils_array_find_binary_search(sett_info->property_infos,
-                                            sizeof(NMSettInfoProperty),
-                                            sett_info->property_infos_len,
-                                            &property_name,
-                                            nm_strcmp_p_with_data,
-                                            NULL);
+    idx = nm_array_find_bsearch(sett_info->property_infos,
+                                sett_info->property_infos_len,
+                                sizeof(NMSettInfoProperty),
+                                &property_name,
+                                nm_strcmp_p_with_data,
+                                NULL);
 
     if (idx < 0)
         return NULL;
@@ -604,7 +604,48 @@ _nm_setting_use_legacy_property(NMSetting  *setting,
                                 const char *new_property)
 {
     gs_unref_variant GVariant *setting_dict = NULL;
-    gs_unref_variant GVariant *value        = NULL;
+    gs_unref_variant GVariant *val_leg      = NULL;
+    gs_unref_variant GVariant *val_new      = NULL;
+
+    /* We want to be both forward and backward compatible (both the client or the daemon
+     * can be newer).
+     *
+     * For the most part, we achieve that by ignoring unknown properties (to be forward
+     * compatible). That of course has the downside, that we don't do strong validation
+     * of the input.
+     *
+     * In some cases, we deprecated a D-Bus property for another one (e.g. the legacy property
+     * "ipv4.routes" became the new property "ipv4.route-data"). In that case, the to/from D-Bus
+     * methods behave differently on the client and the daemon.
+     *
+     * The daemon will serialize both the legacy property and the new property to D-Bus.
+     * The client, will prefer the newer property (if it exists) when deserializing from D-Bus.
+     *
+     * Usually that scheme would fully suffice to support forward and backward compatibility.
+     * However, there is a problem. An old client (unaware of the new property) might get
+     * the profile, modify the old property, and send the entire profile back to the daemon.
+     * In this case, the old client does not know that the new property conflicts with the
+     * old property. The client also might try to preserve any unknown properties and send
+     * them back to the daemon. If the daemon now would prefer the new property, it would be wrong.
+     *
+     * The solution to this is that the daemon -- when both old and new property is set --
+     * will prefer the old property. This is what _nm_setting_use_legacy_property() checks
+     * for. Consequently, a new client will not serialize both the old and the new property.
+     * This is done via "to_dbus_only_in_manager_process" flag.
+     *
+     * The downside of this scheme is that:
+     *
+     * - to/from D-Bus just got more complicated and behaves differently on the client
+     *   and the daemon.
+     * - backward compatibility does not work with a newer client vs. and older daemon.
+     *   This is the major downside. It's only not that severe, because we only deprecate
+     *   properties seldom and only on major versions. Major version updates happen not
+     *   often and they user might reboot (restart the daemon).
+     *
+     * The benefit is that the case with an older client and a newer daemon works, even
+     * if the client fetches a (new) profile, modifies only parts that it understands,
+     * and sends back the complete profile (including the new, unmodified properties).
+     */
 
     if (!connection_dict) {
         /* we also allow the caller to provide no connection_dict.
@@ -626,19 +667,24 @@ _nm_setting_use_legacy_property(NMSetting  *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)
-        return TRUE;
-    nm_clear_pointer(&value, g_variant_unref);
+    if (!_nm_utils_is_manager_process) {
+        /* The client will prefer the new property, unless it does not exist and
+         * the legacy property exists. */
+        val_new = g_variant_lookup_value(setting_dict, new_property, NULL);
+        if (!val_new) {
+            val_leg = g_variant_lookup_value(setting_dict, legacy_property, NULL);
+            if (val_leg)
+                return TRUE;
+        }
 
-    /* Otherwise, clients always prefer new properties sent from the daemon. */
-    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);
-    return !!value;
+    /* The daemon prefers the old property (if it exists). */
+    val_leg = g_variant_lookup_value(setting_dict, legacy_property, NULL);
+    if (val_leg)
+        return TRUE;
+    return FALSE;
 }
 
 /*****************************************************************************/
@@ -1256,7 +1302,7 @@ _nm_setting_property_to_dbus_fcn_direct(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_
             (const NMValueStrv *) _nm_setting_get_private_field(setting, sett_info, property_info);
         if (!val->arr)
             return NULL;
-        return g_variant_new_strv((const char *const *) val->arr->data, val->arr->len);
+        return g_variant_new_strv(nm_g_array_data(val->arr), val->arr->len);
     }
     default:
         return nm_assert_unreachable_val(NULL);
@@ -1310,7 +1356,7 @@ _nm_setting_property_to_dbus_fcn_gprop(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_n
         nm_assert(G_VALUE_HOLDS(&prop_value, G_TYPE_ARRAY));
         tmp_array = g_value_get_boxed(&prop_value);
         nm_assert(tmp_array);
-        return nm_g_variant_new_au((const guint32 *) tmp_array->data, tmp_array->len);
+        return nm_g_variant_new_au(nm_g_array_data(tmp_array), 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_strdict_to_variant_ass(g_value_get_boxed(&prop_value));
@@ -1752,31 +1798,36 @@ property_to_dbus(const NMSettInfoSetting                *sett_info,
               || NM_FLAGS_HAS(property_info->param_spec->flags, G_PARAM_WRITABLE)
               || property_info->property_type == &nm_sett_info_propert_type_setting_name);
 
-    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)
+    if (ignore_flags) {
+        /* We are called from _nm_setting_property_compare_fcn_default(). We want
+         * to serialize the property, and ignore the flags. */
+    } else {
+        if (property_info->to_dbus_only_in_manager_process && !_nm_utils_is_manager_process)
             return NULL;
 
-        if (NM_FLAGS_HAS(property_info->param_spec->flags, NM_SETTING_PARAM_SECRET)) {
-            NMSettingSecretFlags f = NM_SETTING_SECRET_FLAG_NONE;
+        if (property_info->param_spec
+            && !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_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_info->param_spec->name,
+                                                     &f,
+                                                     NULL))
+                        return NULL;
+                }
 
-            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_info->param_spec->name,
-                                                 &f,
-                                                 NULL))
+                if (!_nm_connection_serialize_secrets(flags, f))
+                    return NULL;
+            } else {
+                if (!_nm_connection_serialize_non_secret(flags))
                     return NULL;
             }
-
-            if (!_nm_connection_serialize_secrets(flags, f))
-                return NULL;
-        } else {
-            if (!_nm_connection_serialize_non_secret(flags))
-                return NULL;
         }
     }
 
@@ -3196,6 +3247,12 @@ _nm_setting_clear_secrets(NMSetting                       *setting,
 /**
  * _nm_setting_need_secrets:
  * @setting: the #NMSetting
+ * @check_rerequest: If %TRUE: the stored secrets might be wrong and the agent
+ *   should query the user for the correct credentials. If an #NMSetting knows
+ *   that this cannot be the case it should *not* return the corresponding
+ *   setting object. Otherwise it should always return it, even if it is not
+ *   missing.
+ *   If %FALSE: only return it when it is missing.
  *
  * Returns an array of property names for each secret which may be required
  * to make a successful connection.  The returned hints are only intended as a
@@ -3208,14 +3265,14 @@ _nm_setting_clear_secrets(NMSetting                       *setting,
  * free the elements.
  **/
 GPtrArray *
-_nm_setting_need_secrets(NMSetting *setting)
+_nm_setting_need_secrets(NMSetting *setting, gboolean check_rerequest)
 {
     GPtrArray *secrets = NULL;
 
     g_return_val_if_fail(NM_IS_SETTING(setting), NULL);
 
     if (NM_SETTING_GET_CLASS(setting)->need_secrets)
-        secrets = NM_SETTING_GET_CLASS(setting)->need_secrets(setting);
+        secrets = NM_SETTING_GET_CLASS(setting)->need_secrets(setting, check_rerequest);
 
     return secrets;
 }
@@ -3513,7 +3570,7 @@ nm_setting_to_string(NMSetting *setting)
 }
 
 static GVariant *
-_nm_setting_get_deprecated_virtual_interface_name(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
+depreated_interface_name_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
     NMSettingConnection *s_con;
 
@@ -3533,8 +3590,9 @@ _nm_setting_get_deprecated_virtual_interface_name(_NM_SETT_INFO_PROP_TO_DBUS_FCN
 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, );
+                                        .to_dbus_fcn = depreated_interface_name_to_dbus,
+                                        /* from_dbus_fcn() is handled by the connection.interface-name setter.
+                                         * See nm_setting_connection_no_interface_name(). */ );
 
 const NMSettInfoPropertType nm_sett_info_propert_type_setting_name =
     NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(G_VARIANT_TYPE_STRING,
@@ -4066,6 +4124,233 @@ nm_setting_option_set_uint32(NMSetting *setting, const char *opt_name, guint32 v
 
 /*****************************************************************************/
 
+G_DEFINE_BOXED_TYPE(NMRange, nm_range, nm_range_ref, (GBoxedFreeFunc) nm_range_unref)
+
+static gboolean
+NM_IS_RANGE(const NMRange *self)
+{
+    return self && self->refcount > 0;
+}
+
+/**
+ * nm_range_new:
+ * @start: the first element of the range
+ * @end: the last element of the range, must be greater than or equal
+ * to @start.
+ *
+ * Creates a new #NMRange object for the given range. Setting @end
+ * equal to @start creates a single-element range.
+ *
+ * Returns: (transfer full): the new #NMRange object.
+ *
+ * Since: 1.42
+ **/
+NMRange *
+nm_range_new(guint64 start, guint64 end)
+{
+    NMRange *range;
+
+    g_return_val_if_fail(start <= end, NULL);
+
+    range  = g_slice_new(NMRange);
+    *range = (NMRange){
+        .refcount = 1,
+        .start    = start,
+        .end      = end,
+    };
+
+    return range;
+}
+
+/**
+ * nm_range_ref:
+ * @range: the #NMRange
+ *
+ * Increases the reference count of the object.
+ * This is thread-safe.
+ *
+ * Returns: the input argument @range object.
+ *
+ * Since: 1.42
+ **/
+NMRange *
+nm_range_ref(const NMRange *range)
+{
+    g_return_val_if_fail(NM_IS_RANGE(range), NULL);
+
+    nm_assert(range->refcount < G_MAXINT);
+
+    g_atomic_int_inc(&((NMRange *) range)->refcount);
+    return (NMRange *) range;
+}
+
+/**
+ * nm_range_unref:
+ * @range: the #NMRange
+ *
+ * Decreases the reference count of the object.  If the reference count
+ * reaches zero the object will be destroyed.
+ * This is thread-safe.
+ *
+ * Since: 1.42
+ **/
+void
+nm_range_unref(const NMRange *range)
+{
+    g_return_if_fail(NM_IS_RANGE(range));
+
+    if (g_atomic_int_dec_and_test(&((NMRange *) range)->refcount))
+        nm_g_slice_free((NMRange *) range);
+}
+
+/**
+ * nm_range_cmp:
+ * @a: a #NMRange
+ * @b: another #NMRange
+ *
+ * Compare two ranges.
+ *
+ * Returns: zero if the two instances are equivalent or
+ *   a non-zero integer otherwise. This defines a total ordering
+ *   over the ranges.
+ *
+ * Since: 1.42
+ **/
+int
+nm_range_cmp(const NMRange *a, const NMRange *b)
+{
+    NM_CMP_SELF(a, b);
+    NM_CMP_FIELD(a, b, start);
+    NM_CMP_FIELD(a, b, end);
+
+    return 0;
+}
+
+/**
+ * nm_range_get_range:
+ * @range: the #NMRange
+ * @start: (out): location to store the start value
+ * @end: (out): location to store the end value
+ *
+ * Gets the start and end values for the range.
+ *
+ * Returns: %TRUE if the range contains more than one
+ * element, %FALSE otherwise.
+ *
+ * Since: 1.42
+ **/
+gboolean
+nm_range_get_range(const NMRange *range, guint64 *start, guint64 *end)
+{
+    /* with LTO and optimization, the compiler complains that the
+     * output variables are not initialized. In practice, the function
+     * only sets the output on success. But make the compiler happy.
+     */
+    NM_SET_OUT(start, 0);
+    NM_SET_OUT(end, 0);
+
+    g_return_val_if_fail(NM_IS_RANGE(range), 0);
+
+    NM_SET_OUT(start, range->start);
+    NM_SET_OUT(end, range->end);
+
+    return range->start != range->end;
+}
+
+/**
+ * nm_range_to_str:
+ * @range: the %NMRange
+ *
+ * Convert a %NMRange to a string.
+ *
+ * Returns: (transfer full): a string representing the range.
+ *
+ * Since: 1.42
+ */
+char *
+nm_range_to_str(const NMRange *range)
+{
+    char  buf[200];
+    char *b = buf;
+    gsize l = sizeof(buf);
+
+    g_return_val_if_fail(NM_IS_RANGE(range), NULL);
+
+    nm_strbuf_append(&b, &l, "%" G_GUINT64_FORMAT, range->start);
+    if (range->start != range->end)
+        nm_strbuf_append(&b, &l, "-%" G_GUINT64_FORMAT, range->end);
+
+    nm_assert(l > 0);
+    return nm_memdup_nul(buf, sizeof(buf) - l);
+}
+
+/**
+ * nm_range_from_str:
+ * @str: the string representation of a range
+ * @error: (out) (allow-none): location to store the error on failure
+ *
+ * Parses the string representation of the range to create a %NMRange
+ * instance.
+ *
+ * Returns: (transfer full): the %NMRange or %NULL
+ *
+ * Since: 1.42
+ */
+NMRange *
+nm_range_from_str(const char *str, GError **error)
+{
+    gs_free char *str_free = NULL;
+    guint64       start;
+    guint64       end = 0;
+    char         *c;
+
+    g_return_val_if_fail(str, NULL);
+    g_return_val_if_fail(!error || !*error, NULL);
+
+    c = strchr(str, '-');
+    if (c) {
+        str = nm_strndup_a(300, str, c - str, &str_free);
+        c++;
+    }
+
+    start = _nm_utils_ascii_str_to_uint64(str, 10, 0, G_MAXUINT64, 0);
+    if (errno != 0) {
+        g_set_error(error,
+                    NM_CONNECTION_ERROR,
+                    NM_CONNECTION_ERROR_FAILED,
+                    "invalid range start '%s'",
+                    str);
+        return NULL;
+    }
+
+    if (c) {
+        end = _nm_utils_ascii_str_to_uint64(c, 10, 0, G_MAXUINT64, 0);
+        if (errno != 0) {
+            g_set_error(error,
+                        NM_CONNECTION_ERROR,
+                        NM_CONNECTION_ERROR_FAILED,
+                        "invalid range end '%s'",
+                        c);
+            return NULL;
+        }
+        if (end < start) {
+            g_set_error(error,
+                        NM_CONNECTION_ERROR,
+                        NM_CONNECTION_ERROR_FAILED,
+                        "invalid range %" G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT
+                        ", start must be less than or equal to end",
+                        start,
+                        end);
+            return NULL;
+        }
+    } else
+        end = start;
+
+    return nm_range_new(start, end);
+}
+
+/*****************************************************************************/
+
 static void
 get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
 {
diff --git a/src/libnm-core-impl/nm-simple-connection.c b/src/libnm-core-impl/nm-simple-connection.c
index 6252dc2c..a9c66dc7 100644
--- a/src/libnm-core-impl/nm-simple-connection.c
+++ b/src/libnm-core-impl/nm-simple-connection.c
@@ -47,9 +47,6 @@ G_DEFINE_TYPE_WITH_CODE(NMSimpleConnection,
                         G_IMPLEMENT_INTERFACE(NM_TYPE_CONNECTION,
                                               nm_simple_connection_interface_init);)
 
-#define _GET_PRIVATE(self) \
-    G_TYPE_INSTANCE_GET_PRIVATE(self, NM_TYPE_SIMPLE_CONNECTION, NMConnectionPrivate)
-
 /*****************************************************************************/
 
 static void
@@ -57,7 +54,7 @@ nm_simple_connection_init(NMSimpleConnection *self)
 {
     NMConnectionPrivate *priv;
 
-    priv = _GET_PRIVATE(self);
+    priv = _NM_SIMPLE_CONNECTION_GET_CONNECTION_PRIVATE(self);
 
     priv->self = (NMConnection *) self;
 }
@@ -139,15 +136,12 @@ NMConnection *
 nm_simple_connection_new_clone(NMConnection *connection)
 {
     NMConnection *clone;
-    const char   *path;
 
     g_return_val_if_fail(NM_IS_CONNECTION(connection), NULL);
 
     clone = nm_simple_connection_new();
 
-    path = nm_connection_get_path(connection);
-    if (path)
-        nm_connection_set_path(clone, path);
+    _nm_connection_set_path_rstr(clone, _nm_connection_get_path_rstr(connection));
 
     nm_connection_replace_settings_from_connection(clone, connection);
 
@@ -157,14 +151,12 @@ nm_simple_connection_new_clone(NMConnection *connection)
 static void
 dispose(GObject *object)
 {
-    NMConnection *connection = NM_CONNECTION(object);
-
 #if NM_MORE_ASSERTS
     g_signal_handlers_disconnect_by_data(object,
                                          (gpointer) &_nm_assert_connection_unchanging_user_data);
 #endif
 
-    _nm_connection_private_clear(_GET_PRIVATE(connection));
+    _nm_connection_private_clear(_NM_SIMPLE_CONNECTION_GET_CONNECTION_PRIVATE(object));
 
     G_OBJECT_CLASS(nm_simple_connection_parent_class)->dispose(object);
 }
diff --git a/src/libnm-core-impl/nm-team-utils.c b/src/libnm-core-impl/nm-team-utils.c
index 6d87f50b..2f62f6c6 100644
--- a/src/libnm-core-impl/nm-team-utils.c
+++ b/src/libnm-core-impl/nm-team-utils.c
@@ -2148,11 +2148,11 @@ nm_team_setting_config_set(NMTeamSetting *self, const char *js_str)
         else {
             gboolean unrecognized_content            = FALSE;
             bool     has_lst[_NM_TEAM_ATTRIBUTE_NUM] = {
-                    FALSE,
+                FALSE,
             };
             NMValueTypUnion val_lst[_NM_TEAM_ATTRIBUTE_NUM];
             nm_json_t      *found_keys[_NM_TEAM_ATTRIBUTE_NUM] = {
-                     NULL,
+                NULL,
             };
             gs_unref_ptrarray GPtrArray *ptr_array_master_runner_tx_hash_free = NULL;
             gs_unref_ptrarray GPtrArray *ptr_array_link_watchers_free         = NULL;
diff --git a/src/libnm-core-impl/nm-utils-private.h b/src/libnm-core-impl/nm-utils-private.h
index 1a6f29ef..e2e1aff7 100644
--- a/src/libnm-core-impl/nm-utils-private.h
+++ b/src/libnm-core-impl/nm-utils-private.h
@@ -64,11 +64,13 @@ GVariant *_nm_team_settings_property_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS
 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);
+GVariant *nm_utils_dns_to_variant(int addr_family, const char *const *dns, gssize len);
 
 const char *const *nmtst_system_encodings_for_lang(const char *lang);
 const char *const *nmtst_system_encodings_get_default(void);
 const char *const *nmtst_system_encodings_get(void);
 
+gboolean
+_nm_setting_ovs_verify_connection_type(GType gtype, NMConnection *connection, GError **error);
+
 #endif
diff --git a/src/libnm-core-impl/nm-utils.c b/src/libnm-core-impl/nm-utils.c
index 1c6d36c2..c389213f 100644
--- a/src/libnm-core-impl/nm-utils.c
+++ b/src/libnm-core-impl/nm-utils.c
@@ -269,7 +269,7 @@ nm_sock_addr_endpoint_get_fixed_sockaddr(NMSockAddrEndpoint *self, gpointer sock
     if (!self->host)
         return FALSE;
 
-    if (nm_utils_parse_inaddr_bin(AF_UNSPEC, self->host, &addr_family, &addrbin))
+    if (nm_inet_parse_bin(AF_UNSPEC, self->host, &addr_family, &addrbin))
         goto good;
 
     /* See if there is an IPv6 scope-id...
@@ -295,7 +295,7 @@ nm_sock_addr_endpoint_get_fixed_sockaddr(NMSockAddrEndpoint *self, gpointer sock
         const char   *host_part;
 
         host_part = nm_strndup_a(200, self->host, s - self->host, &tmp_str);
-        if (nm_utils_parse_inaddr_bin(AF_INET6, host_part, &addr_family, &addrbin))
+        if (nm_inet_parse_bin(AF_INET6, host_part, &addr_family, &addrbin))
             goto good;
     }
 
@@ -1297,12 +1297,30 @@ 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);
+    return nm_utils_dns_to_variant(AF_INET, NM_CAST_STRV_CC(dns), -1);
+}
+
+/**
+ * nm_utils_ip6_dns_to_variant:
+ * @dns: (type utf8): an array of IP address strings
+ *
+ * Utility function to convert an array of IP address strings int a #GVariant of
+ * type 'aay' representing an array of IPv6 addresses.
+ *
+ * If a string cannot be parsed, it will be silently ignored.
+ *
+ * Returns: (transfer none): a new floating #GVariant representing @dns.
+ **/
+GVariant *
+nm_utils_ip6_dns_to_variant(char **dns)
+{
+    return nm_utils_dns_to_variant(AF_INET6, NM_CAST_STRV_CC(dns), -1);
 }
 
 GVariant *
-_nm_utils_ip4_dns_to_variant(const char *const *dns, gssize len)
+nm_utils_dns_to_variant(int addr_family, const char *const *dns, gssize len)
 {
+    const int       IS_IPv4 = NM_IS_IPv4(addr_family);
     GVariantBuilder builder;
     gsize           l;
     gsize           i;
@@ -1312,13 +1330,20 @@ _nm_utils_ip4_dns_to_variant(const char *const *dns, gssize len)
     else
         l = len;
 
-    g_variant_builder_init(&builder, G_VARIANT_TYPE("au"));
+    g_variant_builder_init(&builder, IS_IPv4 ? G_VARIANT_TYPE("au") : G_VARIANT_TYPE("aay"));
 
     for (i = 0; i < l; i++) {
-        in_addr_t ip;
+        NMIPAddr ip;
+
+        /* We can only represent the IP address on the legacy property "ipv[46].dns".
+         * Expose what we can. */
+        if (!nm_utils_dnsname_parse(addr_family, dns[i], NULL, &ip, NULL))
+            continue;
 
-        if (inet_pton(AF_INET, dns[i], &ip) == 1)
+        if (IS_IPv4)
             g_variant_builder_add(&builder, "u", ip);
+        else
+            g_variant_builder_add(&builder, "@ay", nm_g_variant_new_ay_in6addr(&ip.addr6));
     }
 
     return g_variant_builder_end(&builder);
@@ -1346,7 +1371,7 @@ nm_utils_ip4_dns_from_variant(GVariant *value)
     array = g_variant_get_fixed_array(value, &length, sizeof(guint32));
     dns   = g_new(char *, length + 1u);
     for (i = 0; i < length; i++)
-        dns[i] = nm_utils_inet4_ntop_dup(array[i]);
+        dns[i] = nm_inet4_ntop_dup(array[i]);
     dns[i] = NULL;
 
     return dns;
@@ -1449,7 +1474,7 @@ nm_utils_ip4_addresses_from_variant(GVariant *value, char **out_gateway)
             g_ptr_array_add(addresses, addr);
 
             if (addr_array[2] && out_gateway && !*out_gateway)
-                *out_gateway = nm_utils_inet4_ntop_dup(addr_array[2]);
+                *out_gateway = nm_inet4_ntop_dup(addr_array[2]);
         } else {
             g_warning("Ignoring invalid IP4 address: %s", error->message);
             g_clear_error(&error);
@@ -1571,7 +1596,7 @@ nm_utils_ip4_routes_from_variant(GVariant *value)
 guint32
 nm_utils_ip4_netmask_to_prefix(guint32 netmask)
 {
-    return _nm_utils_ip4_netmask_to_prefix(netmask);
+    return nm_ip4_addr_netmask_to_prefix(netmask);
 }
 
 /**
@@ -1585,7 +1610,7 @@ nm_utils_ip4_prefix_to_netmask(guint32 prefix)
 {
     g_return_val_if_fail(prefix <= 32, 0xffffffffu);
 
-    return _nm_utils_ip4_prefix_to_netmask(prefix);
+    return nm_ip4_addr_netmask_from_prefix(prefix);
 }
 
 /**
@@ -1603,47 +1628,7 @@ nm_utils_ip4_prefix_to_netmask(guint32 prefix)
 guint32
 nm_utils_ip4_get_default_prefix(guint32 ip)
 {
-    return _nm_utils_ip4_get_default_prefix(ip);
-}
-
-/**
- * nm_utils_ip6_dns_to_variant:
- * @dns: (type utf8): an array of IP address strings
- *
- * Utility function to convert an array of IP address strings int a #GVariant of
- * type 'aay' representing an array of IPv6 addresses.
- *
- * If a string cannot be parsed, it will be silently ignored.
- *
- * Returns: (transfer none): a new floating #GVariant representing @dns.
- **/
-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"));
-    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));
-    }
-    return g_variant_builder_end(&builder);
+    return nm_ip4_addr_get_default_prefix(ip);
 }
 
 /**
@@ -1676,7 +1661,7 @@ nm_utils_ip6_dns_from_variant(GVariant *value)
         const struct in6_addr *ip = g_variant_get_fixed_array(ip_var, &length, 1);
 
         if (length == sizeof(struct in6_addr))
-            dns[i++] = nm_utils_inet6_ntop_dup(ip);
+            dns[i++] = nm_inet6_ntop_dup(ip);
 
         g_variant_unref(ip_var);
     }
@@ -1798,7 +1783,7 @@ nm_utils_ip6_addresses_from_variant(GVariant *value, char **out_gateway)
                     goto next;
                 }
                 if (!IN6_IS_ADDR_UNSPECIFIED(gateway_bytes))
-                    *out_gateway = nm_utils_inet6_ntop_dup(gateway_bytes);
+                    *out_gateway = nm_inet6_ntop_dup(gateway_bytes);
             }
         } else {
             g_warning("Ignoring invalid IP6 address: %s", error->message);
@@ -1943,7 +1928,7 @@ next:
  *
  * Returns: (transfer none): a new floating #GVariant representing @addresses.
  *
- * Since: 1.42, 1.40.4
+ * Since: 1.42
  **/
 GVariant *
 nm_utils_ip_addresses_to_variant(GPtrArray *addresses)
@@ -1998,7 +1983,7 @@ nm_utils_ip_addresses_to_variant(GPtrArray *addresses)
  * Returns: (transfer full) (element-type NMIPAddress): a newly allocated
  *   #GPtrArray of #NMIPAddress objects
  *
- * Since: 1.42, 1.40.4
+ * Since: 1.42
  **/
 GPtrArray *
 nm_utils_ip_addresses_from_variant(GVariant *value, int family)
@@ -2059,7 +2044,7 @@ nm_utils_ip_addresses_from_variant(GVariant *value, int family)
  *
  * Returns: (transfer none): a new floating #GVariant representing @routes.
  *
- * Since: 1.42, 1.40.4
+ * Since: 1.42
  **/
 GVariant *
 nm_utils_ip_routes_to_variant(GPtrArray *routes)
@@ -2127,7 +2112,7 @@ nm_utils_ip_routes_to_variant(GPtrArray *routes)
  * Returns: (transfer full) (element-type NMIPRoute): a newly allocated
  *   #GPtrArray of #NMIPRoute objects
  *
- * Since: 1.42, 1.40.4
+ * Since: 1.42
  **/
 GPtrArray *
 nm_utils_ip_routes_from_variant(GVariant *value, int family)
@@ -3833,13 +3818,13 @@ _nm_utils_ipaddr_canonical_or_invalid(int addr_family, const char *ip, gboolean
     if (!ip)
         return NULL;
 
-    if (!nm_utils_parse_inaddr_bin(addr_family, ip, &addr_family, &addr_bin))
+    if (!nm_inet_parse_bin(addr_family, ip, &addr_family, &addr_bin))
         return g_strdup(ip);
 
     if (map_zero_to_null && nm_ip_addr_is_null(addr_family, &addr_bin))
         return NULL;
 
-    return nm_utils_inet_ntop_dup(addr_family, &addr_bin);
+    return nm_inet_ntop_dup(addr_family, &addr_bin);
 }
 
 /*
@@ -3988,7 +3973,7 @@ nm_utils_hwaddr_to_dbus(const char *str)
 }
 
 GVariant *
-_nm_utils_hwaddr_cloned_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
+_nm_sett_info_prop_to_dbus_fcn_cloned_mac_address(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
     gs_free char *addr = NULL;
 
@@ -3999,7 +3984,7 @@ _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)
+_nm_sett_info_prop_from_dbus_fcn_cloned_mac_address(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 {
     gsize         length;
     const guint8 *array;
@@ -4030,14 +4015,15 @@ _nm_utils_hwaddr_cloned_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 }
 
 gboolean
-_nm_utils_hwaddr_cloned_not_set(_NM_SETT_INFO_PROP_MISSING_FROM_DBUS_FCN_ARGS _nm_nil)
+_nm_sett_info_prop_missing_from_dbus_fcn_cloned_mac_address(
+    _NM_SETT_INFO_PROP_MISSING_FROM_DBUS_FCN_ARGS _nm_nil)
 {
     nm_assert(nm_streq0(property, "cloned-mac-address"));
     return TRUE;
 }
 
 static GVariant *
-_nm_utils_hwaddr_cloned_data_synth(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
+assigned_mac_address_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
     gs_free char *addr = NULL;
 
@@ -4066,7 +4052,7 @@ _nm_utils_hwaddr_cloned_data_synth(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 }
 
 static gboolean
-_nm_utils_hwaddr_cloned_data_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
+assigned_mac_address_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 {
     nm_assert(nm_streq0(property_info->name, "assigned-mac-address"));
 
@@ -4088,8 +4074,8 @@ _nm_utils_hwaddr_cloned_data_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 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, );
+                                        .to_dbus_fcn   = assigned_mac_address_to_dbus,
+                                        .from_dbus_fcn = assigned_mac_address_from_dbus, );
 
 /*****************************************************************************/
 
@@ -4255,13 +4241,13 @@ _nm_utils_generate_mac_address_mask_parse(const char         *value,
         ouis = g_array_sized_new(FALSE, FALSE, sizeof(struct ether_addr), 4);
 
         do {
+            struct ether_addr *new;
+
             s      = s_next;
             s_next = _split_word(s);
 
-            g_array_set_size(ouis, ouis->len + 1);
-            if (!nm_utils_hwaddr_aton(s,
-                                      &g_array_index(ouis, struct ether_addr, ouis->len - 1),
-                                      ETH_ALEN)) {
+            new = nm_g_array_append_new(ouis, struct ether_addr);
+            if (!nm_utils_hwaddr_aton(s, new, ETH_ALEN)) {
                 g_set_error(error,
                             NM_UTILS_ERROR,
                             NM_UTILS_ERROR_UNKNOWN,
@@ -4373,13 +4359,13 @@ nm_utils_is_uuid(const char *str)
     return nm_uuid_is_valid_nmlegacy(str);
 }
 
-static _nm_thread_local char _nm_utils_inet_ntop_buffer[NM_UTILS_INET_ADDRSTRLEN];
+static _nm_thread_local char _nm_utils_inet_ntop_buffer[NM_INET_ADDRSTRLEN];
 
 /**
  * nm_utils_inet4_ntop: (skip)
  * @inaddr: the address that should be converted to string.
  * @dst: the destination buffer, it must contain at least
- *  <literal>INET_ADDRSTRLEN</literal> or %NM_UTILS_INET_ADDRSTRLEN
+ *  <literal>INET_ADDRSTRLEN</literal> or %NM_INET_ADDRSTRLEN
  *  characters. If set to %NULL, it will return a pointer to an internal, static
  *  buffer (shared with nm_utils_inet6_ntop()).  Beware, that the internal
  *  buffer will be overwritten with ever new call of nm_utils_inet4_ntop() or
@@ -4401,14 +4387,14 @@ nm_utils_inet4_ntop(in_addr_t inaddr, char *dst)
      *
      * However, still support it to be lenient against mistakes and because
      * this is public API of libnm. */
-    return _nm_utils_inet4_ntop(inaddr, dst ?: _nm_utils_inet_ntop_buffer);
+    return nm_inet4_ntop(inaddr, dst ?: _nm_utils_inet_ntop_buffer);
 }
 
 /**
  * nm_utils_inet6_ntop: (skip)
  * @in6addr: the address that should be converted to string.
  * @dst: the destination buffer, it must contain at least
- *  <literal>INET6_ADDRSTRLEN</literal> or %NM_UTILS_INET_ADDRSTRLEN
+ *  <literal>INET6_ADDRSTRLEN</literal> or %NM_INET_ADDRSTRLEN
  *  characters. If set to %NULL, it will return a pointer to an internal, static
  *  buffer (shared with nm_utils_inet4_ntop()).  Beware, that the internal
  *  buffer will be overwritten with ever new call of nm_utils_inet4_ntop() or
@@ -4432,7 +4418,7 @@ nm_utils_inet6_ntop(const struct in6_addr *in6addr, char *dst)
      * However, still support it to be lenient against mistakes and because
      * this is public API of libnm. */
     g_return_val_if_fail(in6addr, NULL);
-    return _nm_utils_inet6_ntop(in6addr, dst ?: _nm_utils_inet_ntop_buffer);
+    return nm_inet6_ntop(in6addr, dst ?: _nm_utils_inet_ntop_buffer);
 }
 
 /**
@@ -4450,7 +4436,7 @@ nm_utils_ipaddr_valid(int family, const char *ip)
 {
     g_return_val_if_fail(family == AF_INET || family == AF_INET6 || family == AF_UNSPEC, FALSE);
 
-    return nm_utils_ipaddr_is_valid(family, ip);
+    return nm_inet_is_valid(family, ip);
 }
 
 /**
@@ -5030,11 +5016,8 @@ _nm_variant_attribute_spec_find_binary_search(const NMVariantAttributeSpec *cons
 
     G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMVariantAttributeSpec, name) == 0);
 
-    idx = nm_utils_ptrarray_find_binary_search((gconstpointer *) array,
-                                               len,
-                                               &name,
-                                               nm_strcmp_p_with_data,
-                                               NULL);
+    idx =
+        nm_ptrarray_find_bsearch((gconstpointer *) array, len, &name, nm_strcmp_p_with_data, NULL);
     if (idx < 0)
         return NULL;
     return array[idx];
@@ -5589,10 +5572,102 @@ _nm_utils_bridge_vlan_verify_list(GPtrArray  *vlans,
     return TRUE;
 }
 
+GVariant *
+_nm_utils_ranges_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
+{
+    gs_unref_ptrarray GPtrArray *ranges = NULL;
+    GVariantBuilder              builder;
+    const char                  *property_name = property_info->name;
+    guint                        i;
+
+    nm_assert(property_name);
+
+    g_object_get(setting, property_name, &ranges, NULL);
+    g_variant_builder_init(&builder, G_VARIANT_TYPE("aa{sv}"));
+
+    if (ranges) {
+        for (i = 0; i < ranges->len; i++) {
+            NMRange        *range = ranges->pdata[i];
+            GVariantBuilder range_builder;
+
+            g_variant_builder_init(&range_builder, G_VARIANT_TYPE_VARDICT);
+            g_variant_builder_add(&range_builder,
+                                  "{sv}",
+                                  "start",
+                                  g_variant_new_uint64(range->start));
+            g_variant_builder_add(&range_builder, "{sv}", "end", g_variant_new_uint64(range->end));
+
+            g_variant_builder_add(&builder, "a{sv}", &range_builder);
+        }
+    }
+
+    return g_variant_builder_end(&builder);
+}
+
+gboolean
+_nm_utils_ranges_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
+{
+    gs_unref_ptrarray GPtrArray *ranges = NULL;
+    GVariantIter                 iter;
+    GVariant                    *range_var;
+
+    g_return_val_if_fail(g_variant_is_of_type(value, G_VARIANT_TYPE("aa{sv}")), FALSE);
+
+    ranges = g_ptr_array_new_with_free_func((GDestroyNotify) nm_range_unref);
+    g_variant_iter_init(&iter, value);
+    while (g_variant_iter_next(&iter, "@a{sv}", &range_var)) {
+        _nm_unused gs_unref_variant GVariant *var_unref = range_var;
+        gint64                                start;
+        gint64                                end;
+
+        if (!g_variant_lookup(range_var, "start", "t", &start))
+            continue;
+        if (!g_variant_lookup(range_var, "end", "t", &end))
+            continue;
+        if (start > end)
+            continue;
+
+        g_ptr_array_add(ranges, nm_range_new(start, end));
+    }
+
+    g_object_set(setting, property_info->name, ranges, NULL);
+
+    return TRUE;
+}
+
+NMTernary
+_nm_utils_ranges_cmp(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil)
+{
+    const GPtrArray *ranges_a = NULL;
+    const GPtrArray *ranges_b = NULL;
+    guint            len;
+    guint            i;
+
+    if (nm_streq0(nm_setting_get_name(set_a), NM_SETTING_OVS_PORT_SETTING_NAME)
+        && nm_streq0(property_info->name, NM_SETTING_OVS_PORT_TRUNKS)) {
+        ranges_a = _nm_setting_ovs_port_get_trunks_arr(NM_SETTING_OVS_PORT(set_a));
+        if (set_b)
+            ranges_b = _nm_setting_ovs_port_get_trunks_arr(NM_SETTING_OVS_PORT(set_b));
+    } else {
+        nm_assert_not_reached();
+    }
+
+    len = nm_g_ptr_array_len(ranges_a);
+    if (len != nm_g_ptr_array_len(ranges_b))
+        return FALSE;
+    for (i = 0; i < len; i++) {
+        if (nm_range_cmp(ranges_a->pdata[i], ranges_b->pdata[i]))
+            return FALSE;
+    }
+
+    return TRUE;
+}
+
 gboolean
 _nm_utils_iaid_verify(const char *str, gint64 *out_value)
 {
-    gint64 iaid;
+    gint64  i64;
+    guint32 u32;
 
     NM_SET_OUT(out_value, -1);
 
@@ -5602,10 +5677,16 @@ _nm_utils_iaid_verify(const char *str, gint64 *out_value)
     if (NM_IAID_IS_SPECIAL(str))
         return TRUE;
 
-    if (NM_STRCHAR_ALL(str, ch, ch >= '0' && ch <= '9') && (str[0] != '0' || str[1] == '\0')
-        && (iaid = _nm_utils_ascii_str_to_int64(str, 10, 0, G_MAXUINT32, -1)) != -1) {
-        NM_SET_OUT(out_value, iaid);
-        return TRUE;
+    if (NM_STRCHAR_ALL(str, ch, g_ascii_isxdigit(ch) || NM_IN_SET(ch, 'x', ':'))) {
+        if ((i64 = _nm_utils_ascii_str_to_int64(str, 0, 0, G_MAXUINT32, -1)) != -1) {
+            NM_SET_OUT(out_value, i64);
+            return TRUE;
+        }
+
+        if (nm_dhcp_iaid_from_hexstr(str, &u32)) {
+            NM_SET_OUT(out_value, u32);
+            return TRUE;
+        }
     }
 
     return FALSE;
@@ -5660,3 +5741,24 @@ _nm_utils_validate_dhcp_hostname_flags(NMDhcpHostnameFlags flags, int addr_famil
 
     return TRUE;
 }
+
+/*****************************************************************************/
+
+/**
+ * nm_utils_ensure_gtypes:
+ *
+ * This ensures that all NMSetting GTypes are created. For example,
+ * after this call, g_type_from_name("NMSettingConnection") will work.
+ *
+ * This cannot fail and does nothing if the type already exists.
+ *
+ * Since: 1.42
+ */
+void
+nm_utils_ensure_gtypes(void)
+{
+    NMMetaSettingType meta_type;
+
+    for (meta_type = 0; meta_type < _NM_META_SETTING_TYPE_NUM; meta_type++)
+        nm_meta_setting_infos[meta_type].get_setting_gtype();
+}
diff --git a/src/libnm-core-impl/nm-vpn-plugin-info.c b/src/libnm-core-impl/nm-vpn-plugin-info.c
index baa19347..61e9cde5 100644
--- a/src/libnm-core-impl/nm-vpn-plugin-info.c
+++ b/src/libnm-core-impl/nm-vpn-plugin-info.c
@@ -198,7 +198,7 @@ _sort_files(LoadDirInfo *a, LoadDirInfo *b)
  *   VPN plugin directory.
  */
 const char *
-_nm_vpn_plugin_info_get_default_dir_etc()
+_nm_vpn_plugin_info_get_default_dir_etc(void)
 {
     return DEFAULT_DIR_ETC;
 }
@@ -210,7 +210,7 @@ _nm_vpn_plugin_info_get_default_dir_etc()
  *   VPN plugin directory.
  */
 const char *
-_nm_vpn_plugin_info_get_default_dir_lib()
+_nm_vpn_plugin_info_get_default_dir_lib(void)
 {
     return DEFAULT_DIR_LIB;
 }
@@ -223,7 +223,7 @@ _nm_vpn_plugin_info_get_default_dir_lib()
  * that directory.
  */
 const char *
-_nm_vpn_plugin_info_get_default_dir_user()
+_nm_vpn_plugin_info_get_default_dir_user(void)
 {
     return nm_str_not_empty(g_getenv("NM_VPN_PLUGIN_DIR"));
 }
@@ -296,7 +296,7 @@ _nm_vpn_plugin_info_list_load_dir(const char               *dirname,
     g_array_sort(array, (GCompareFunc) _sort_files);
 
     for (i = 0; i < array->len; i++)
-        res = g_slist_prepend(res, g_array_index(array, LoadDirInfo, i).plugin_info);
+        res = g_slist_prepend(res, nm_g_array_index(array, LoadDirInfo, i).plugin_info);
 
     g_array_unref(array);
 
@@ -312,7 +312,7 @@ _nm_vpn_plugin_info_list_load_dir(const char               *dirname,
  * Since: 1.2
  */
 GSList *
-nm_vpn_plugin_info_list_load()
+nm_vpn_plugin_info_list_load(void)
 {
     int               i;
     gint64            uid;
@@ -898,7 +898,7 @@ nm_vpn_plugin_info_get_program(NMVpnPluginInfo *self)
  *
  * Returns: %TRUE if the service supports multiple instances with different bus names, otherwise %FALSE
  *
- * Since: 1.42, 1.40.4
+ * Since: 1.42
  */
 gboolean
 nm_vpn_plugin_info_supports_multiple(NMVpnPluginInfo *self)
diff --git a/src/libnm-core-impl/tests/test-general.c b/src/libnm-core-impl/tests/test-general.c
index a21f50db..8a98265a 100644
--- a/src/libnm-core-impl/tests/test-general.c
+++ b/src/libnm-core-impl/tests/test-general.c
@@ -2501,6 +2501,9 @@ test_setting_ip_route_attributes(void)
     TEST_ATTR("tos", byte, 127, AF_INET, TRUE, TRUE);
     TEST_ATTR("tos", string, "0x28", AF_INET, FALSE, TRUE);
 
+    TEST_ATTR("weight", uint32, 100, AF_INET, TRUE, TRUE);
+    TEST_ATTR("weight", string, "100", AF_INET, FALSE, TRUE);
+
     TEST_ATTR("advmss", uint32, 1400, AF_INET, TRUE, TRUE);
     TEST_ATTR("advmss", string, "1400", AF_INET, FALSE, TRUE);
 
@@ -3900,7 +3903,7 @@ ensure_diffs(GHashTable *diffs, const DiffSetting *check, gsize n_check)
 {
     guint i;
 
-    g_assert(g_hash_table_size(diffs) == n_check);
+    g_assert_cmpint(g_hash_table_size(diffs), ==, n_check);
 
     /* Loop through the settings */
     for (i = 0; i < n_check; i++) {
@@ -3913,14 +3916,14 @@ ensure_diffs(GHashTable *diffs, const DiffSetting *check, gsize n_check)
         /* Get the number of keys to check */
         while (check[i].keys[z].key_name)
             z++;
-        g_assert(g_hash_table_size(setting_hash) == z);
+        g_assert_cmpint(g_hash_table_size(setting_hash), ==, z);
 
         /* Now compare the actual keys */
         for (z = 0; check[i].keys[z].key_name; z++) {
             NMSettingDiffResult result;
 
             result = GPOINTER_TO_UINT(g_hash_table_lookup(setting_hash, check[i].keys[z].key_name));
-            g_assert(result == check[i].keys[z].result);
+            g_assert_cmpint(result, ==, check[i].keys[z].result);
         }
     }
 }
@@ -4010,6 +4013,8 @@ test_connection_diff_a_only(void)
              {NM_SETTING_IP4_CONFIG_DHCP_VENDOR_CLASS_IDENTIFIER, NM_SETTING_DIFF_RESULT_IN_A},
              {NM_SETTING_IP_CONFIG_DHCP_REJECT_SERVERS, NM_SETTING_DIFF_RESULT_IN_A},
              {NM_SETTING_IP4_CONFIG_LINK_LOCAL, NM_SETTING_DIFF_RESULT_IN_A},
+             {NM_SETTING_IP_CONFIG_AUTO_ROUTE_EXT_GW, NM_SETTING_DIFF_RESULT_IN_A},
+             {NM_SETTING_IP_CONFIG_REPLACE_LOCAL_RULE, NM_SETTING_DIFF_RESULT_IN_A},
              {NULL, NM_SETTING_DIFF_RESULT_UNKNOWN},
          }},
     };
@@ -4052,11 +4057,11 @@ test_connection_diff_different(void)
     NMSettingIPConfig *s_ip4;
     gboolean           same;
     const DiffSetting  settings[] = {
-         {NM_SETTING_IP4_CONFIG_SETTING_NAME,
+        {NM_SETTING_IP4_CONFIG_SETTING_NAME,
           {
-              {NM_SETTING_IP_CONFIG_METHOD,
+             {NM_SETTING_IP_CONFIG_METHOD,
                NM_SETTING_DIFF_RESULT_IN_A | NM_SETTING_DIFF_RESULT_IN_B},
-              {NULL, NM_SETTING_DIFF_RESULT_UNKNOWN},
+             {NULL, NM_SETTING_DIFF_RESULT_UNKNOWN},
          }},
     };
 
@@ -4137,10 +4142,10 @@ test_connection_diff_inferrable(void)
     NMSettingIPConfig   *s_ip4;
     char                *uuid;
     const DiffSetting    settings[] = {
-           {NM_SETTING_CONNECTION_SETTING_NAME,
+        {NM_SETTING_CONNECTION_SETTING_NAME,
             {
-                {NM_SETTING_CONNECTION_INTERFACE_NAME, NM_SETTING_DIFF_RESULT_IN_A},
-                {NULL, NM_SETTING_DIFF_RESULT_UNKNOWN},
+             {NM_SETTING_CONNECTION_INTERFACE_NAME, NM_SETTING_DIFF_RESULT_IN_A},
+             {NULL, NM_SETTING_DIFF_RESULT_UNKNOWN},
          }},
     };
 
@@ -5061,7 +5066,7 @@ _netmask_to_prefix(guint32 netmask)
 
     /* we re-implemented the netmask-to-prefix code differently. Check
      * that they agree. */
-    g_assert_cmpint(prefix, ==, _nm_utils_ip4_netmask_to_prefix(netmask));
+    g_assert_cmpint(prefix, ==, nm_ip4_addr_netmask_to_prefix(netmask));
 
     return prefix;
 }
@@ -5072,7 +5077,7 @@ test_ip4_prefix_to_netmask(void)
     int i;
 
     for (i = 0; i <= 32; i++) {
-        guint32 netmask = _nm_utils_ip4_prefix_to_netmask(i);
+        guint32 netmask = nm_ip4_addr_netmask_from_prefix(i);
         int     plen    = _netmask_to_prefix(netmask);
 
         g_assert_cmpint(i, ==, plen);
@@ -5100,8 +5105,8 @@ test_ip4_netmask_to_prefix(void)
     g_rand_set_seed(rand, 1);
 
     for (i = 2; i <= 32; i++) {
-        guint32 netmask            = _nm_utils_ip4_prefix_to_netmask(i);
-        guint32 netmask_lowest_bit = netmask & ~_nm_utils_ip4_prefix_to_netmask(i - 1);
+        guint32 netmask            = nm_ip4_addr_netmask_from_prefix(i);
+        guint32 netmask_lowest_bit = netmask & ~nm_ip4_addr_netmask_from_prefix(i - 1);
 
         g_assert_cmpint(i, ==, _netmask_to_prefix(netmask));
 
@@ -5250,7 +5255,8 @@ test_setting_ip4_changed_signal(void)
     ASSERT_CHANGED(nm_setting_ip_config_add_dns(s_ip4, "11.22.0.0"));
     ASSERT_CHANGED(nm_setting_ip_config_remove_dns(s_ip4, 0));
 
-    NMTST_EXPECT_LIBNM_CRITICAL(NMTST_G_RETURN_MSG(idx >= 0 && idx < priv->dns->len));
+    NMTST_EXPECT_LIBNM_CRITICAL(
+        NMTST_G_RETURN_MSG(idx >= 0 && ((guint) idx) < nm_g_ptr_array_len(priv->dns)));
     ASSERT_UNCHANGED(nm_setting_ip_config_remove_dns(s_ip4, 1));
     g_test_assert_expected_messages();
 
@@ -5326,7 +5332,8 @@ test_setting_ip6_changed_signal(void)
     ASSERT_CHANGED(nm_setting_ip_config_add_dns(s_ip6, "1:2:3::4:5:6"));
     ASSERT_CHANGED(nm_setting_ip_config_remove_dns(s_ip6, 0));
 
-    NMTST_EXPECT_LIBNM_CRITICAL(NMTST_G_RETURN_MSG(idx >= 0 && idx < priv->dns->len));
+    NMTST_EXPECT_LIBNM_CRITICAL(
+        NMTST_G_RETURN_MSG(idx >= 0 && ((guint) idx) < nm_g_ptr_array_len(priv->dns)));
     ASSERT_UNCHANGED(nm_setting_ip_config_remove_dns(s_ip6, 1));
     g_test_assert_expected_messages();
 
@@ -6957,7 +6964,7 @@ test_setting_ip6_gateway(void)
 
         gateway_bytes = g_variant_get_fixed_array(gateway_var, &length, 1);
         g_assert_cmpint(length, ==, 16);
-        nmtst_assert_ip6_address((struct in6_addr *) gateway_bytes, "abcd::1");
+        nmtst_assert_ip6_address(NM_CAST_ALIGN(struct in6_addr, gateway_bytes), "abcd::1");
         g_variant_unref(gateway_var);
     }
     g_variant_unref(value);
@@ -7143,7 +7150,7 @@ _sock_addr_endpoint_fixed(const char *endpoint, const char *host, guint16 port,
     g_assert(host);
     g_assert(port > 0);
 
-    if (!nm_utils_parse_inaddr_bin(AF_UNSPEC, host, &addr_family, &addrbin))
+    if (!nm_inet_parse_bin(AF_UNSPEC, host, &addr_family, &addrbin))
         g_assert_not_reached();
 
     ep = nm_sock_addr_endpoint_new(endpoint);
@@ -7887,13 +7894,13 @@ test_nm_utils_uuid_generate_from_string(void)
                NM_UUID_NS_DNS);
 
     for (i = 0; i < G_N_ELEMENTS(zero_uuids); i++) {
-        nm_sprintf_buf(i_str, "%u", i),
-            _test_uuid(NM_UUID_TYPE_VERSION3, zero_uuids[i].uuid3, i_str, -1, NULL);
+        nm_sprintf_buf(i_str, "%u", i);
+        _test_uuid(NM_UUID_TYPE_VERSION3, zero_uuids[i].uuid3, i_str, -1, NULL);
         _test_uuid(NM_UUID_TYPE_VERSION5, zero_uuids[i].uuid5, i_str, -1, NULL);
     }
     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, NM_UUID_NS_DNS);
+        nm_sprintf_buf(i_str, "%u", i);
+        _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);
     }
 
@@ -7949,12 +7956,52 @@ test_nm_utils_uuid_generate_from_string(void)
 /*****************************************************************************/
 
 static void
-__test_uuid(const char *expected_uuid, const char *str, gssize slen, char *uuid_test)
+_check_uuid(NMUuidType         uuid_type,
+            const NMUuid      *type_arg,
+            const char        *expected_uuid,
+            const char        *str,
+            gssize             slen,
+            const char *const *strv,
+            gssize             strv_len)
 {
+    gs_free char *uuid_test        = NULL;
+    gs_free char *uuid_test2       = NULL;
+    gboolean      uuid_test2_valid = TRUE;
+
+    g_assert(str);
+    g_assert(strv_len < 0 || strv);
+
+    uuid_test = nm_uuid_generate_from_strings_strv(uuid_type, type_arg, strv, strv_len);
+
     g_assert(uuid_test);
     g_assert(nm_uuid_is_normalized(uuid_test));
 
-    if (strcmp(uuid_test, expected_uuid)) {
+    if (strv_len < 0 && strv) {
+        uuid_test2 =
+            nm_uuid_generate_from_strings_strv(uuid_type, type_arg, strv, NM_PTRARRAY_LEN(strv));
+    } else if (strv_len >= 0) {
+        gssize               l     = nm_strv_find_first(strv, strv_len, NULL);
+        gs_free const char **strv2 = nm_strv_dup_packed(strv, l < 0 ? strv_len : l);
+
+        uuid_test2 = nm_uuid_generate_from_strings_strv(uuid_type,
+                                                        type_arg,
+                                                        strv2 ?: NM_STRV_EMPTY_CC(),
+                                                        -1);
+        if (l >= 0) {
+            /* there are NULL strings. The result won't be match. */
+            uuid_test2_valid = FALSE;
+        }
+    }
+    if (uuid_test2) {
+        if (uuid_test2_valid)
+            g_assert_cmpstr(uuid_test, ==, uuid_test2);
+        else {
+            g_assert(nm_uuid_is_normalized(uuid_test));
+            g_assert_cmpstr(uuid_test, !=, uuid_test2);
+        }
+    }
+
+    if (!nm_streq(uuid_test, expected_uuid)) {
         g_error("UUID test failed (1): text=%s, len=%lld, expected=%s, uuid_test=%s",
                 str,
                 (long long) slen,
@@ -7963,7 +8010,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_ns_1);
+    uuid_test = nm_uuid_generate_from_string_str(str, slen, uuid_type, type_arg);
 
     g_assert(uuid_test);
     g_assert(nm_utils_is_uuid(uuid_test));
@@ -7975,11 +8022,20 @@ __test_uuid(const char *expected_uuid, const char *str, gssize slen, char *uuid_
                 expected_uuid,
                 uuid_test);
     }
-    g_free(uuid_test);
 }
 
-#define _test_uuid(expected_uuid, str, strlen, ...) \
-    __test_uuid(expected_uuid, str, strlen, nm_uuid_generate_from_strings(__VA_ARGS__, NULL))
+#define check_uuid(uuid_type, type_arg, expected_uuid, str, ...)                             \
+    ({                                                                                       \
+        const NMUuidType   _uuid_type     = (uuid_type);                                     \
+        const NMUuid      *_type_arg      = type_arg;                                        \
+        const char        *_expected_uuid = (expected_uuid);                                 \
+        const char        *_str           = (str);                                           \
+        const gsize        _strlen        = NM_STRLEN(str);                                  \
+        const char *const *_strv          = NM_MAKE_STRV(__VA_ARGS__);                       \
+        const gssize       _strv_len      = NM_NARG(__VA_ARGS__);                            \
+                                                                                             \
+        _check_uuid(_uuid_type, _type_arg, _expected_uuid, _str, _strlen, _strv, _strv_len); \
+    })
 
 static void
 test_nm_utils_uuid_generate_from_strings(void)
@@ -8001,20 +8057,157 @@ test_nm_utils_uuid_generate_from_strings(void)
     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");
-    _test_uuid("69e22c7e-f89f-3a43-b239-1cb52ed8db69", "aa\0", 3, "aa");
-    _test_uuid("59829fd3-5ad5-3d90-a7b0-4911747e4088", "\0\0", 2, "", "");
-    _test_uuid("01ad0e06-6c50-3384-8d86-ddab81421425", "a\0\0", 3, "a", "");
-    _test_uuid("e1ed8647-9ed3-3ec8-8c6d-e8204524d71d", "aa\0\0", 4, "aa", "");
-    _test_uuid("fb1c7cd6-275c-3489-9382-83b900da8af0", "\0a\0", 3, "", "a");
-    _test_uuid("5d79494e-c4ba-31a6-80a2-d6016ccd7e17", "a\0a\0", 4, "a", "a");
-    _test_uuid("fd698d86-1b60-3ebe-855f-7aada9950a8d", "aa\0a\0", 5, "aa", "a");
-    _test_uuid("8c573b48-0f01-30ba-bb94-c5f59f4fe517", "\0aa\0", 4, "", "aa");
-    _test_uuid("2bdd3d46-eb83-3c53-a41b-a724d04b5544", "a\0aa\0", 5, "a", "aa");
-    _test_uuid("13d4b780-07c1-3ba7-b449-81c4844ef039", "aa\0aa\0", 6, "aa", "aa");
-    _test_uuid("dd265bf7-c05a-3037-9939-b9629858a477", "a\0b\0", 4, "a", "b");
+    _check_uuid(NM_UUID_TYPE_VERSION3,
+                &nm_uuid_ns_1,
+                "457229f4-fe49-32f5-8b09-c531d81f44d9",
+                "x",
+                1,
+                NULL,
+                -1);
+    check_uuid(NM_UUID_TYPE_VERSION3, &nm_uuid_ns_1, "b07c334a-399b-32de-8d50-58e4e08f98e3", "");
+    check_uuid(NM_UUID_TYPE_VERSION3,
+               &nm_uuid_ns_1,
+               "b8a426cb-bcb5-30a3-bd8f-6786fea72df9",
+               "\0",
+               "");
+    check_uuid(NM_UUID_TYPE_VERSION3,
+               &nm_uuid_ns_1,
+               "9232afda-85fc-3b8f-8736-4f99c8d5db9c",
+               "_n",
+               NULL);
+    check_uuid(NM_UUID_TYPE_VERSION3,
+               &nm_uuid_ns_1,
+               "12a4a982-7aae-39e1-951e-41aeb1250959",
+               "a\0",
+               "a");
+    check_uuid(NM_UUID_TYPE_VERSION3,
+               &nm_uuid_ns_1,
+               "69e22c7e-f89f-3a43-b239-1cb52ed8db69",
+               "aa\0",
+               "aa");
+    check_uuid(NM_UUID_TYPE_VERSION3,
+               &nm_uuid_ns_1,
+               "59829fd3-5ad5-3d90-a7b0-4911747e4088",
+               "\0\0",
+               "",
+               "");
+    check_uuid(NM_UUID_TYPE_VERSION3,
+               &nm_uuid_ns_1,
+               "01ad0e06-6c50-3384-8d86-ddab81421425",
+               "a\0\0",
+               "a",
+               "");
+    check_uuid(NM_UUID_TYPE_VERSION3,
+               &nm_uuid_ns_1,
+               "e1ed8647-9ed3-3ec8-8c6d-e8204524d71d",
+               "aa\0\0",
+               "aa",
+               "");
+    check_uuid(NM_UUID_TYPE_VERSION3,
+               &nm_uuid_ns_1,
+               "fb1c7cd6-275c-3489-9382-83b900da8af0",
+               "\0a\0",
+               "",
+               "a");
+    check_uuid(NM_UUID_TYPE_VERSION3,
+               &nm_uuid_ns_1,
+               "5d79494e-c4ba-31a6-80a2-d6016ccd7e17",
+               "a\0a\0",
+               "a",
+               "a");
+    check_uuid(NM_UUID_TYPE_VERSION3,
+               &nm_uuid_ns_1,
+               "f36cec99-1db8-3baa-8c3f-13e13d980318",
+               "a\0a\0001_1n",
+               "a",
+               NULL,
+               "a");
+    check_uuid(NM_UUID_TYPE_VERSION3,
+               &nm_uuid_ns_1,
+               "fd698d86-1b60-3ebe-855f-7aada9950a8d",
+               "aa\0a\0",
+               "aa",
+               "a");
+    check_uuid(NM_UUID_TYPE_VERSION3,
+               &nm_uuid_ns_1,
+               "8c573b48-0f01-30ba-bb94-c5f59f4fe517",
+               "\0aa\0",
+               "",
+               "aa");
+    check_uuid(NM_UUID_TYPE_VERSION3,
+               &nm_uuid_ns_1,
+               "2bdd3d46-eb83-3c53-a41b-a724d04b5544",
+               "a\0aa\0",
+               "a",
+               "aa");
+    check_uuid(NM_UUID_TYPE_VERSION3,
+               &nm_uuid_ns_1,
+               "13d4b780-07c1-3ba7-b449-81c4844ef039",
+               "aa\0aa\0",
+               "aa",
+               "aa");
+    check_uuid(NM_UUID_TYPE_VERSION3,
+               &nm_uuid_ns_1,
+               "dd265bf7-c05a-3037-9939-b9629858a477",
+               "a\0b\0",
+               "a",
+               "b");
+
+    check_uuid(NM_UUID_TYPE_VERSION5,
+               _uuid(NM_UUID_NS_URL),
+               "dd247a64-df22-5d30-8087-0bd709f6941a",
+               "a\0b\0",
+               "a",
+               "b");
+    check_uuid(NM_UUID_TYPE_VERSION5,
+               _uuid(NM_UUID_NS_URL),
+               "cbb93d73-085d-5072-94cd-a394b8149993",
+               "\0b\0",
+               "",
+               "b");
+    check_uuid(NM_UUID_TYPE_VERSION5,
+               _uuid(NM_UUID_NS_URL),
+               "db3dfd17-c785-509d-a0ca-740fdd68dc68",
+               "\0b\00011_n",
+               "",
+               "b",
+               NULL);
+    check_uuid(NM_UUID_TYPE_VERSION3,
+               _uuid(NM_UUID_NS_URL),
+               "916dcdd8-5042-3b9b-9763-4312a31e5735",
+               "aa\0a\0",
+               "aa",
+               "a");
+    check_uuid(NM_UUID_TYPE_VERSION3,
+               NULL,
+               "1700bb72-7116-3d1f-8cd2-6d074a40a3a9",
+               "aa\0a\0",
+               "aa",
+               "a");
+    check_uuid(NM_UUID_TYPE_VERSION3,
+               &nm_uuid_ns_zero,
+               "1700bb72-7116-3d1f-8cd2-6d074a40a3a9",
+               "aa\0a\0",
+               "aa",
+               "a");
+    check_uuid(NM_UUID_TYPE_VERSION5,
+               NULL,
+               "03c5de66-28ad-5a2e-8ed3-e256f3218900",
+               "aa\0a\0",
+               "aa",
+               "a");
+    check_uuid(NM_UUID_TYPE_VERSION5,
+               &nm_uuid_ns_zero,
+               "03c5de66-28ad-5a2e-8ed3-e256f3218900",
+               "aa\0a\0",
+               "aa",
+               "a");
+    check_uuid(NM_UUID_TYPE_LEGACY,
+               NULL,
+               "c38f63cf-1e50-ad7f-ae26-50f85cc5da47",
+               "aa\0a\0",
+               "aa",
+               "a");
 }
 
 static void
@@ -8287,7 +8480,7 @@ test_nm_utils_strstrdictkey(void)
         const char           *v1;
         const char           *v2;
         NMUtilsStrStrDictKey *v_static;
-    } * val1, *val2,
+    } *val1, *val2,
         values[] = {
             {NULL, NULL},
             {"", NULL},
@@ -8787,19 +8980,15 @@ _test_find_binary_search_do(const int *array, gsize len)
 
     expected_result = nm_utils_ptrarray_find_first(parray, len, pneedle);
 
-    idx = nm_utils_ptrarray_find_binary_search_range(parray,
-                                                     len,
-                                                     pneedle,
-                                                     _test_find_binary_search_cmp,
-                                                     NULL,
-                                                     &idx_first,
-                                                     &idx_last);
+    idx = nm_ptrarray_find_bsearch_range(parray,
+                                         len,
+                                         pneedle,
+                                         _test_find_binary_search_cmp,
+                                         NULL,
+                                         &idx_first,
+                                         &idx_last);
 
-    idx2 = nm_utils_ptrarray_find_binary_search(parray,
-                                                len,
-                                                pneedle,
-                                                _test_find_binary_search_cmp,
-                                                NULL);
+    idx2 = nm_ptrarray_find_bsearch(parray, len, pneedle, _test_find_binary_search_cmp, NULL);
     g_assert_cmpint(idx, ==, idx2);
 
     if (expected_result >= 0) {
@@ -8864,12 +9053,12 @@ _test_find_binary_search_do_uint32(const int *int_array, gsize len)
             expected_result = idx;
     }
 
-    idx = nm_utils_array_find_binary_search(array,
-                                            sizeof(guint32),
-                                            len,
-                                            &NEEDLE,
-                                            nm_cmp_uint32_p_with_data,
-                                            NULL);
+    idx = nm_array_find_bsearch(array,
+                                len,
+                                sizeof(guint32),
+                                &NEEDLE,
+                                nm_cmp_uint32_p_with_data,
+                                NULL);
     if (expected_result >= 0)
         g_assert_cmpint(expected_result, ==, idx);
     else {
@@ -8963,29 +9152,25 @@ test_nm_utils_ptrarray_find_binary_search_with_duplicates(void)
             for (i = 0; i < i_len + BIN_SEARCH_W_DUPS_JITTER; i++) {
                 gconstpointer p = GINT_TO_POINTER(i);
 
-                idx = nm_utils_ptrarray_find_binary_search_range(arr,
-                                                                 i_len,
-                                                                 p,
-                                                                 _test_bin_search2_cmp,
-                                                                 NULL,
-                                                                 &idx_first,
-                                                                 &idx_last);
+                idx = nm_ptrarray_find_bsearch_range(arr,
+                                                     i_len,
+                                                     p,
+                                                     _test_bin_search2_cmp,
+                                                     NULL,
+                                                     &idx_first,
+                                                     &idx_last);
 
                 idx_first2 = nm_utils_ptrarray_find_first(arr, i_len, p);
 
-                idx2 = nm_utils_array_find_binary_search(arr,
-                                                         sizeof(gpointer),
-                                                         i_len,
-                                                         &p,
-                                                         _test_bin_search2_cmp_p,
-                                                         NULL);
+                idx2 = nm_array_find_bsearch(arr,
+                                             i_len,
+                                             sizeof(gpointer),
+                                             &p,
+                                             _test_bin_search2_cmp_p,
+                                             NULL);
                 g_assert_cmpint(idx, ==, idx2);
 
-                idx2 = nm_utils_ptrarray_find_binary_search(arr,
-                                                            i_len,
-                                                            p,
-                                                            _test_bin_search2_cmp,
-                                                            NULL);
+                idx2 = nm_ptrarray_find_bsearch(arr, i_len, p, _test_bin_search2_cmp, NULL);
                 g_assert_cmpint(idx, ==, idx2);
 
                 if (idx_first2 < 0) {
@@ -9791,7 +9976,7 @@ test_route_attributes_parse(void)
     g_assert(!ht);
     g_clear_error(&error);
 
-    ht = nm_utils_parse_variant_attributes("mtu.1400 src.1\\.2\\.3\\.4 ",
+    ht = nm_utils_parse_variant_attributes("mtu.1400 weight.5 src.1\\.2\\.3\\.4 ",
                                            ' ',
                                            '.',
                                            FALSE,
@@ -9804,6 +9989,11 @@ test_route_attributes_parse(void)
     g_assert(g_variant_is_of_type(variant, G_VARIANT_TYPE_UINT32));
     g_assert_cmpuint(g_variant_get_uint32(variant), ==, 1400);
 
+    variant = g_hash_table_lookup(ht, NM_IP_ROUTE_ATTRIBUTE_WEIGHT);
+    g_assert(variant);
+    g_assert(g_variant_is_of_type(variant, G_VARIANT_TYPE_UINT32));
+    g_assert_cmpuint(g_variant_get_uint32(variant), ==, 5);
+
     variant = g_hash_table_lookup(ht, NM_IP_ROUTE_ATTRIBUTE_SRC);
     g_assert(variant);
     g_assert(g_variant_is_of_type(variant, G_VARIANT_TYPE_STRING));
@@ -10255,8 +10445,8 @@ static void
 test_nm_ip_addr_zero(void)
 {
     in_addr_t       a4 = nmtst_inet4_from_string("0.0.0.0");
-    struct in6_addr a6 = *nmtst_inet6_from_string("::");
-    char            buf[NM_UTILS_INET_ADDRSTRLEN];
+    struct in6_addr a6 = nmtst_inet6_from_string("::");
+    char            buf[NM_INET_ADDRSTRLEN];
     NMIPAddr        a = NM_IP_ADDR_INIT;
 
     g_assert(memcmp(&a, &nm_ip_addr_zero, sizeof(a)) == 0);
@@ -10267,11 +10457,11 @@ test_nm_ip_addr_zero(void)
     g_assert(memcmp(&nm_ip_addr_zero, &a4, sizeof(a4)) == 0);
     g_assert(memcmp(&nm_ip_addr_zero, &a6, sizeof(a6)) == 0);
 
-    g_assert_cmpstr(_nm_utils_inet4_ntop(nm_ip_addr_zero.addr4, buf), ==, "0.0.0.0");
-    g_assert_cmpstr(_nm_utils_inet6_ntop(&nm_ip_addr_zero.addr6, buf), ==, "::");
+    g_assert_cmpstr(nm_inet4_ntop(nm_ip_addr_zero.addr4, buf), ==, "0.0.0.0");
+    g_assert_cmpstr(nm_inet6_ntop(&nm_ip_addr_zero.addr6, buf), ==, "::");
 
-    g_assert_cmpstr(nm_utils_inet_ntop(AF_INET, &nm_ip_addr_zero, buf), ==, "0.0.0.0");
-    g_assert_cmpstr(nm_utils_inet_ntop(AF_INET6, &nm_ip_addr_zero, buf), ==, "::");
+    g_assert_cmpstr(nm_inet_ntop(AF_INET, &nm_ip_addr_zero, buf), ==, "0.0.0.0");
+    g_assert_cmpstr(nm_inet_ntop(AF_INET6, &nm_ip_addr_zero, buf), ==, "::");
 
     G_STATIC_ASSERT_EXPR(sizeof(a) == sizeof(a.array));
 }
@@ -10951,6 +11141,246 @@ test_direct_string_is_refstr(void)
 
 /*****************************************************************************/
 
+static void
+test_connection_path(void)
+{
+    gs_unref_object NMConnection *conn = NULL;
+    const char *const             PATH = "/org/freedesktop/NetworkManager/Settings/171950003017";
+    const char                   *path;
+    NMRefString                  *rstr;
+
+    g_assert(!nmtst_ref_string_find(PATH));
+
+    conn = nmtst_create_minimal_connection("test_setting_ip6_gateway",
+                                           NULL,
+                                           NM_SETTING_WIRED_SETTING_NAME,
+                                           NULL);
+
+    g_assert(!nm_connection_get_path(conn));
+    g_assert(!nmtst_ref_string_find(PATH));
+
+    nm_connection_set_path(conn, PATH);
+
+    path = nm_connection_get_path(conn);
+    g_assert_cmpstr(path, ==, PATH);
+
+    /* nm_connection_get_path() gives a NMRefString. This is an
+     * implementation detail, but libnm (which statically links with
+     * libnm-core) may choose to rely on that. */
+    rstr = nmtst_ref_string_find(PATH);
+    g_assert(rstr);
+    g_assert(NM_REF_STRING_UPCAST(path) == rstr);
+
+    g_clear_object(&conn);
+
+    g_assert(!nmtst_ref_string_find(PATH));
+}
+
+/*****************************************************************************/
+
+static void
+_t_dnsname_1(const char *str, const char *exp_addr, const char *exp_server_name)
+{
+    int           addr_family;
+    NMIPAddr      exp_addr_bin;
+    gboolean      addr_family_request;
+    gboolean      r;
+    int           detect_addr_family;
+    NMIPAddr      detect_addr;
+    const char   *detect_server_name;
+    int          *p_detect_addr_family = &detect_addr_family;
+    NMIPAddr     *p_detect_addr        = &detect_addr;
+    const char  **p_detect_server_name = &detect_server_name;
+    char          str_construct_buf[100];
+    char          str_construct_buf2[100];
+    const char   *str_construct;
+    const char   *str_construct2;
+    gsize         l;
+    const char   *str_normalized;
+    gs_free char *str_normalized_alloc = NULL;
+
+    g_assert(str);
+    g_assert(exp_addr);
+
+    r = nm_inet_parse_bin(AF_UNSPEC, exp_addr, &addr_family, &exp_addr_bin);
+    g_assert(r);
+    g_assert(NM_IN_SET(addr_family, AF_INET, AF_INET6));
+
+    addr_family_request = nmtst_get_rand_bool();
+    if (nmtst_get_rand_bool())
+        p_detect_addr = NULL;
+    if ((addr_family_request || !p_detect_addr) && nmtst_get_rand_bool())
+        p_detect_addr_family = NULL;
+    if (nmtst_get_rand_bool())
+        p_detect_server_name = NULL;
+
+    r = nm_utils_dnsname_parse(addr_family_request ? addr_family : AF_UNSPEC,
+                               str,
+                               p_detect_addr_family,
+                               p_detect_addr,
+                               p_detect_server_name);
+    g_assert(r);
+
+    if (p_detect_addr_family)
+        g_assert_cmpint(addr_family, ==, detect_addr_family);
+    if (p_detect_addr)
+        g_assert_cmpstr(nmtst_inet_to_string(addr_family, &detect_addr), ==, exp_addr);
+    if (p_detect_server_name)
+        g_assert_cmpstr(detect_server_name, ==, exp_server_name);
+
+    r = nm_utils_dnsname_parse(addr_family == AF_INET ? AF_INET6 : AF_INET,
+                               str,
+                               p_detect_addr_family,
+                               p_detect_addr,
+                               p_detect_server_name);
+    g_assert(!r);
+
+    /* Construct the expected value. */
+    str_construct = nm_utils_dnsname_construct(addr_family,
+                                               &exp_addr_bin,
+                                               exp_server_name,
+                                               str_construct_buf,
+                                               sizeof(str_construct_buf));
+    g_assert(str_construct);
+    g_assert(str_construct == str_construct_buf);
+    g_assert(strlen(str_construct) < sizeof(str_construct_buf));
+
+    /* Check that a too short buffer causes truncation. */
+    l              = nmtst_get_rand_uint32() % (strlen(str_construct) + 10);
+    str_construct2 = nm_utils_dnsname_construct(addr_family,
+                                                &exp_addr_bin,
+                                                exp_server_name,
+                                                str_construct_buf2,
+                                                l);
+    if (str_construct2) {
+        g_assert(str_construct2 == str_construct_buf2);
+        g_assert_cmpstr(str_construct2, ==, str_construct);
+        g_assert(l > strlen(str_construct));
+    } else
+        g_assert(l <= strlen(str_construct));
+
+    if (!nm_streq(str_construct, str)) {
+        _t_dnsname_1(str_construct, exp_addr, exp_server_name);
+    }
+
+    str_normalized = nm_utils_dnsname_normalize(nmtst_get_rand_bool() ? addr_family : AF_UNSPEC,
+                                                str,
+                                                &str_normalized_alloc);
+    g_assert(str_normalized);
+    if (str_normalized_alloc) {
+        g_assert(str_normalized == str_normalized_alloc);
+        g_assert_cmpstr(str_normalized, !=, str);
+    } else {
+        g_assert(str == str_normalized);
+    }
+    g_assert_cmpstr(str_normalized, ==, str_construct);
+
+    nm_clear_g_free(&str_normalized_alloc);
+    str_normalized = nm_utils_dnsname_normalize(addr_family == AF_INET ? AF_INET6 : AF_INET,
+                                                str,
+                                                &str_normalized_alloc);
+    g_assert(!str_normalized);
+    g_assert(!str_normalized_alloc);
+}
+
+static void
+_t_dnsname_0(const char *str)
+{
+    gboolean      addr_family_request;
+    int           detect_addr_family;
+    NMIPAddr      detect_addr;
+    const char   *detect_server_name;
+    int          *p_detect_addr_family = &detect_addr_family;
+    NMIPAddr     *p_detect_addr        = &detect_addr;
+    const char  **p_detect_server_name = &detect_server_name;
+    const char   *str_normalized;
+    gs_free char *str_normalized_alloc = NULL;
+    gboolean      r;
+
+    g_assert(str);
+
+    addr_family_request = nmtst_get_rand_bool();
+    if (nmtst_get_rand_bool())
+        p_detect_addr = NULL;
+    if ((addr_family_request || !p_detect_addr) && nmtst_get_rand_bool())
+        p_detect_addr_family = NULL;
+    if (nmtst_get_rand_bool())
+        p_detect_server_name = NULL;
+
+    r = nm_utils_dnsname_parse(addr_family_request ? nmtst_rand_select(AF_INET, AF_INET6)
+                                                   : AF_UNSPEC,
+                               str,
+                               p_detect_addr_family,
+                               p_detect_addr,
+                               p_detect_server_name);
+    g_assert(!r);
+
+    str_normalized = nm_utils_dnsname_normalize(nmtst_rand_select(AF_UNSPEC, AF_INET, AF_INET6),
+                                                str,
+                                                &str_normalized_alloc);
+    g_assert(!str_normalized);
+    g_assert(!str_normalized_alloc);
+}
+
+static void
+test_dnsname(void)
+{
+    _t_dnsname_1("1.2.3.4", "1.2.3.4", NULL);
+    _t_dnsname_1("1.2.3.4#foo", "1.2.3.4", "foo");
+    _t_dnsname_1("1::#x", "1::", "x");
+    _t_dnsname_1("1::0#x", "1::", "x");
+    _t_dnsname_1("192.168.0.1", "192.168.0.1", NULL);
+    _t_dnsname_1("192.168.0.1#test.com", "192.168.0.1", "test.com");
+    _t_dnsname_1("fe80::18", "fe80::18", NULL);
+    _t_dnsname_1("fe80::18#hoge.com", "fe80::18", "hoge.com");
+
+    _t_dnsname_0("1.2.3.4#");
+    _t_dnsname_0("1::0#");
+    _t_dnsname_0("192.168.0.1:53");
+    _t_dnsname_0("192.168.0.1:53#example.com");
+    _t_dnsname_0("fe80::18%19");
+    _t_dnsname_0("fe80::18%lo");
+    _t_dnsname_0("[fe80::18]:53");
+    _t_dnsname_0("[fe80::18]:53%19");
+    _t_dnsname_0("[fe80::18]:53%lo");
+    _t_dnsname_0("fe80::18%19#hoge.com");
+    _t_dnsname_0("[fe80::18]:53#hoge.com");
+    _t_dnsname_0("[fe80::18]:53%19");
+    _t_dnsname_0("[fe80::18]:53%19#hoge.com");
+    _t_dnsname_0("[fe80::18]:53%lo");
+    _t_dnsname_0("[fe80::18]:53%lo#hoge.com");
+}
+
+/*****************************************************************************/
+
+static void
+test_dhcp_iaid_hexstr(void)
+{
+    char str[NM_DHCP_IAID_TO_HEXSTR_BUF_LEN];
+    int  i;
+
+    for (i = 0; i < 10; i++) {
+        guint32  iaid = nmtst_get_rand_uint32();
+        guint32  iaid2;
+        char    *s;
+        gboolean r;
+
+        s = nm_dhcp_iaid_to_hexstr(iaid, str);
+        g_assert(s == str);
+        g_assert(strlen(s) < sizeof(str));
+
+        r = nm_dhcp_iaid_from_hexstr(str, &iaid2);
+        g_assert(r);
+        g_assert_cmpint(iaid, ==, iaid2);
+    }
+
+    g_assert_cmpstr(nm_dhcp_iaid_to_hexstr(0, str), ==, "00:00:00:00");
+    g_assert_cmpstr(nm_dhcp_iaid_to_hexstr(1, str), ==, "00:00:00:01");
+    g_assert_cmpstr(nm_dhcp_iaid_to_hexstr(0x01002044, str), ==, "01:00:20:44");
+}
+
+/*****************************************************************************/
+
 NMTST_DEFINE();
 
 int
@@ -11257,7 +11687,7 @@ main(int argc, char **argv)
 
     g_test_add_func("/core/general/_nm_utils_ascii_str_to_int64", test_nm_utils_ascii_str_to_int64);
     g_test_add_func("/core/general/nm_utils_is_power_of_two", test_nm_utils_is_power_of_two);
-    g_test_add_func("/core/general/nm_utils_ptrarray_find_binary_search_range",
+    g_test_add_func("/core/general/nm_ptrarray_find_bsearch_range",
                     test_nm_utils_ptrarray_find_binary_search);
     g_test_add_func("/core/general/nm_utils_ptrarray_find_binary_search_with_duplicates",
                     test_nm_utils_ptrarray_find_binary_search_with_duplicates);
@@ -11296,6 +11726,9 @@ main(int argc, char **argv)
 
     g_test_add_func("/core/general/test_system_encodings", test_system_encodings);
     g_test_add_func("/core/general/test_direct_string_is_refstr", test_direct_string_is_refstr);
+    g_test_add_func("/core/general/test_connection_path", test_connection_path);
+    g_test_add_func("/core/general/test_dnsname", test_dnsname);
+    g_test_add_func("/core/general/test_dhcp_iaid_hexstr", test_dhcp_iaid_hexstr);
 
     return g_test_run();
 }
diff --git a/src/libnm-core-impl/tests/test-setting.c b/src/libnm-core-impl/tests/test-setting.c
index a4e3932a..09e0fc70 100644
--- a/src/libnm-core-impl/tests/test-setting.c
+++ b/src/libnm-core-impl/tests/test-setting.c
@@ -121,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 == 52);
+    G_STATIC_ASSERT_EXPR(_NM_META_SETTING_TYPE_NUM == 54);
 
     arr = g_ptr_array_new_with_free_func(g_object_unref);
 
@@ -975,25 +975,25 @@ test_dcb_flags_invalid(void)
     s_dcb = (NMSettingDcb *) nm_setting_dcb_new();
     g_assert(s_dcb);
 
-    NMTST_EXPECT("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*");
+    NMTST_EXPECT("GLib-GObject", NMTST_EXPECT_GOBJECT_ASSERT_LEVEL, "*invalid or out of range*");
     TEST_FLAG(NM_SETTING_DCB_APP_FCOE_FLAGS, nm_setting_dcb_get_app_fcoe_flags, 0x332523);
     g_test_assert_expected_messages();
 
-    NMTST_EXPECT("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*");
+    NMTST_EXPECT("GLib-GObject", NMTST_EXPECT_GOBJECT_ASSERT_LEVEL, "*invalid or out of range*");
     TEST_FLAG(NM_SETTING_DCB_APP_ISCSI_FLAGS, nm_setting_dcb_get_app_iscsi_flags, 0xFF);
     g_test_assert_expected_messages();
 
-    NMTST_EXPECT("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*");
+    NMTST_EXPECT("GLib-GObject", NMTST_EXPECT_GOBJECT_ASSERT_LEVEL, "*invalid or out of range*");
     TEST_FLAG(NM_SETTING_DCB_APP_FIP_FLAGS, nm_setting_dcb_get_app_fip_flags, 0x1111);
     g_test_assert_expected_messages();
 
-    NMTST_EXPECT("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*");
+    NMTST_EXPECT("GLib-GObject", NMTST_EXPECT_GOBJECT_ASSERT_LEVEL, "*invalid or out of range*");
     TEST_FLAG(NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS,
               nm_setting_dcb_get_priority_flow_control_flags,
               G_MAXUINT32);
     g_test_assert_expected_messages();
 
-    NMTST_EXPECT("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*");
+    NMTST_EXPECT("GLib-GObject", NMTST_EXPECT_GOBJECT_ASSERT_LEVEL, "*invalid or out of range*");
     TEST_FLAG(
         NM_SETTING_DCB_PRIORITY_GROUP_FLAGS,
         nm_setting_dcb_get_priority_group_flags,
@@ -3755,7 +3755,7 @@ test_roundtrip_conversion(gconstpointer test_data)
         for (is_ipv4 = 0; is_ipv4 < 2; is_ipv4++) {
             g_assert(NM_IS_SETTING_IP_CONFIG(s_ip.s_x[is_ipv4]));
             for (i = 0; i < 3; i++) {
-                char addrstr[NM_UTILS_INET_ADDRSTRLEN];
+                char addrstr[NM_INET_ADDRSTRLEN];
 
                 nm_auto_unref_ip_routing_rule NMIPRoutingRule *rr = NULL;
 
@@ -4166,6 +4166,88 @@ test_routing_rule(gconstpointer test_data)
 /*****************************************************************************/
 
 static void
+test_ranges(void)
+{
+    GError  *error = NULL;
+    NMRange *r1;
+    NMRange *r2;
+    guint64  start;
+    guint64  end;
+    char    *str  = NULL;
+    char    *str2 = NULL;
+
+    r1 = nm_range_from_str("99", &error);
+    nmtst_assert_success(r1, error);
+    nm_range_get_range(r1, &start, &end);
+    g_assert_cmpint(start, ==, 99);
+    g_assert_cmpint(end, ==, 99);
+    str = nm_range_to_str(r1);
+    g_assert_cmpstr(str, ==, "99");
+    nm_clear_g_free(&str);
+    nm_range_unref(r1);
+
+    r1 = nm_range_from_str("1000-2000", &error);
+    nmtst_assert_success(r1, error);
+    nm_range_get_range(r1, &start, &end);
+    g_assert_cmpint(start, ==, 1000);
+    g_assert_cmpint(end, ==, 2000);
+    str = nm_range_to_str(r1);
+    g_assert_cmpstr(str, ==, "1000-2000");
+    nm_clear_g_free(&str);
+    nm_range_unref(r1);
+
+    r1 = nm_range_from_str("0", &error);
+    nmtst_assert_success(r1, error);
+    nm_range_unref(r1);
+
+    r1 = nm_range_from_str("-1", &error);
+    nmtst_assert_no_success(r1, error);
+    g_clear_error(&error);
+
+    r1 = nm_range_from_str("foobar", &error);
+    nmtst_assert_no_success(r1, error);
+    g_clear_error(&error);
+
+    r1 = nm_range_from_str("200-100", &error);
+    nmtst_assert_no_success(r1, error);
+    g_clear_error(&error);
+
+    r1 = nm_range_from_str("100-200", &error);
+    nmtst_assert_success(r1, error);
+    r2 = nm_range_from_str("100-200", &error);
+    nmtst_assert_success(r2, error);
+    g_assert_cmpint(nm_range_cmp(r1, r2), ==, 0);
+    nm_range_unref(r1);
+    nm_range_unref(r2);
+
+    r1 = nm_range_from_str("100-200", &error);
+    nmtst_assert_success(r1, error);
+    r2 = nm_range_from_str("1", &error);
+    nmtst_assert_success(r2, error);
+    g_assert_cmpint(nm_range_cmp(r1, r2), ==, 1);
+    nm_range_ref(r1);
+    nm_range_unref(r1);
+    nm_range_unref(r1);
+    nm_range_unref(r2);
+
+    r1 = nm_range_new(G_MAXUINT64 - 1, G_MAXUINT64);
+    g_assert(r1);
+    str = nm_range_to_str(r1);
+    g_assert_cmpstr(str, ==, "18446744073709551614-18446744073709551615");
+    r2 = nm_range_from_str(str, &error);
+    nmtst_assert_success(r2, error);
+    str2 = nm_range_to_str(r2);
+    g_assert_cmpstr(str, ==, str2);
+    g_assert_cmpint(nm_range_cmp(r1, r2), ==, 0);
+    nm_range_unref(r1);
+    nm_range_unref(r2);
+    nm_clear_g_free(&str);
+    nm_clear_g_free(&str2);
+}
+
+/*****************************************************************************/
+
+static void
 test_parse_tc_handle(void)
 {
 #define _parse_tc_handle(str, exp)                                              \
@@ -4303,7 +4385,7 @@ _PROP_IDX_OWNER(GHashTable *h_property_types, const NMSettInfoPropertType *prope
     g_assert(arr);
     g_assert(arr->len > 0);
 
-    idx = g_array_index(arr, guint, 0);
+    idx = nm_g_array_first(arr, guint);
 
     meta_type = (idx & 0xFFu);
     prop_idx  = idx >> 8;
@@ -5069,6 +5151,84 @@ test_6lowpan_1(void)
 /*****************************************************************************/
 
 static void
+test_settings_dns(void)
+{
+    int i_run;
+
+    for (i_run = 0; i_run < 10; i_run++) {
+        gs_unref_object NMConnection *con1 = NULL;
+        gs_unref_object NMConnection *con2 = NULL;
+        int                           IS_IPv4;
+        guint                         n_dns;
+        guint                         i;
+        gboolean                      same = TRUE;
+
+        con1 =
+            nmtst_create_minimal_connection("test-dns", NULL, NM_SETTING_WIRED_SETTING_NAME, NULL);
+        nmtst_connection_normalize(con1);
+
+        con2 = nmtst_connection_duplicate_and_normalize(con1);
+
+        nmtst_assert_connection_equals(con1, nmtst_get_rand_bool(), con2, nmtst_get_rand_bool());
+
+        for (IS_IPv4 = 1; IS_IPv4 >= 0; IS_IPv4--) {
+            const char *nameservers[2][7] = {
+                [0] =
+                    {
+                        "11:22::b:0",
+                        "11:22::b:1#hello1",
+                        "11:22::b:2",
+                        "11:22::b:3#hello2",
+                        "11:22::b:4",
+                        "11:22::b:5",
+                        "bogus6",
+                    },
+                [1] =
+                    {
+                        "1.1.1.0",
+                        "1.1.1.1#foo1",
+                        "1.1.1.2",
+                        "1.1.1.3#foo2",
+                        "1.1.1.4",
+                        "1.1.1.5",
+                        "bogus4",
+                    },
+            };
+            GType gtype = IS_IPv4 ? NM_TYPE_SETTING_IP4_CONFIG : NM_TYPE_SETTING_IP6_CONFIG;
+            NMSettingIPConfig *s_ip1 = _nm_connection_get_setting(con1, gtype);
+            NMSettingIPConfig *s_ip2 = _nm_connection_get_setting(con2, gtype);
+
+            n_dns = nmtst_get_rand_uint32() % G_N_ELEMENTS(nameservers[0]);
+            for (i = 0; i < n_dns; i++) {
+                const char *d =
+                    nameservers[IS_IPv4][nmtst_get_rand_uint32() % G_N_ELEMENTS(nameservers[0])];
+
+                if (!nmtst_get_rand_one_case_in(4))
+                    nm_setting_ip_config_add_dns(s_ip1, d);
+                if (!nmtst_get_rand_one_case_in(4))
+                    nm_setting_ip_config_add_dns(s_ip2, d);
+            }
+
+            if (nm_strv_ptrarray_cmp(_nm_setting_ip_config_get_dns_array(s_ip1),
+                                     _nm_setting_ip_config_get_dns_array(s_ip2))
+                != 0)
+                same = FALSE;
+        }
+
+        _nm_utils_is_manager_process = nmtst_get_rand_bool();
+        if (same) {
+            nmtst_assert_connection_equals(con1, FALSE, con2, FALSE);
+            g_assert(nm_connection_compare(con1, con2, NM_SETTING_COMPARE_FLAG_EXACT));
+        } else {
+            g_assert(!nm_connection_compare(con1, con2, NM_SETTING_COMPARE_FLAG_EXACT));
+        }
+        _nm_utils_is_manager_process = FALSE;
+    }
+}
+
+/*****************************************************************************/
+
+static void
 test_bond_meta(void)
 {
     gs_unref_object NMConnection *con = NULL;
@@ -5170,6 +5330,8 @@ main(int argc, char **argv)
 
     g_test_add_func("/libnm/settings/6lowpan/1", test_6lowpan_1);
 
+    g_test_add_func("/libnm/settings/dns", test_settings_dns);
+
     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);
@@ -5236,6 +5398,8 @@ main(int argc, char **argv)
 
     g_test_add_data_func("/libnm/settings/routing-rule/1", GINT_TO_POINTER(0), test_routing_rule);
 
+    g_test_add_func("/libnm/settings/ranges", test_ranges);
+
     g_test_add_func("/libnm/parse-tc-handle", test_parse_tc_handle);
 
     g_test_add_func("/libnm/test_team_setting", test_team_setting);