summary refs log tree commit diff
path: root/src/core/settings
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2021-10-01 23:05:04 +0200
committerMichael Biebl <biebl@debian.org>2021-10-01 23:05:04 +0200
commite74c568b07b50b97873fb4ee1d776dedefbd54d6 (patch)
tree3469f17ea9af91f7ff169b890633bda68b0cf76e /src/core/settings
parentbfe522304da217296e2a61040f58e35ec5d6f3f2 (diff)
New upstream version 1.32.12 upstream/1.32.12
Diffstat (limited to 'src/core/settings')
-rw-r--r--src/core/settings/nm-agent-manager.c4
-rw-r--r--src/core/settings/nm-secret-agent.c10
-rw-r--r--src/core/settings/nm-settings-connection.c542
-rw-r--r--src/core/settings/nm-settings-connection.h12
-rw-r--r--src/core/settings/nm-settings-plugin.c2
-rw-r--r--src/core/settings/nm-settings-storage.c5
-rw-r--r--src/core/settings/nm-settings-storage.h15
-rw-r--r--src/core/settings/nm-settings.c250
-rw-r--r--src/core/settings/nm-settings.h5
-rw-r--r--src/core/settings/plugins/ifcfg-rh/meson.build2
-rwxr-xr-x[-rw-r--r--]src/core/settings/plugins/ifcfg-rh/nm-ifdown47
-rwxr-xr-x[-rw-r--r--]src/core/settings/plugins/ifcfg-rh/nm-ifup47
-rw-r--r--src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c10
-rw-r--r--src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c93
-rw-r--r--src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.c5
-rw-r--r--src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c20
-rw-r--r--src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h4
-rw-r--r--src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c184
-rw-r--r--src/core/settings/plugins/ifcfg-rh/shvar.c261
-rw-r--r--src/core/settings/plugins/ifcfg-rh/shvar.h3
-rw-r--r--src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-a.cexpected2
-rw-r--r--src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-b.cexpected2
-rw-r--r--src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-wired-802-1X-subj-matches.cexpected2
-rw-r--r--src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bridge_Component.cexpected2
-rw-r--r--src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Static_Routes.cexpected2
-rw-r--r--src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-static-routes-legacy.cexpected2
-rw-r--r--src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-vlanid-use4
-rw-r--r--src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-vlanid-use.cexpected15
-rw-r--r--src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-eap-suite-b-192-tls1
-rw-r--r--src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-eap-ttls-tls1
-rw-r--r--src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-4.expected6
-rw-r--r--src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-1.cexpected14
-rw-r--r--src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-2.cexpected15
-rw-r--r--src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-3.cexpected15
-rw-r--r--src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-4.cexpected15
-rw-r--r--src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-5.cexpected15
-rw-r--r--src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c325
-rw-r--r--src/core/settings/plugins/ifupdown/meson.build2
-rw-r--r--src/core/settings/plugins/ifupdown/nms-ifupdown-parser.c26
-rw-r--r--src/core/settings/plugins/ifupdown/nms-ifupdown-plugin.c2
-rw-r--r--src/core/settings/plugins/ifupdown/tests/test-ifupdown.c2
-rw-r--r--src/core/settings/plugins/keyfile/nms-keyfile-plugin.c28
-rw-r--r--src/core/settings/plugins/keyfile/nms-keyfile-reader.c2
-rw-r--r--src/core/settings/plugins/keyfile/nms-keyfile-storage.c7
-rw-r--r--src/core/settings/plugins/keyfile/nms-keyfile-utils.c32
-rw-r--r--src/core/settings/plugins/keyfile/nms-keyfile-writer.c9
-rw-r--r--src/core/settings/plugins/keyfile/tests/test-keyfile-settings.c5
47 files changed, 1427 insertions, 647 deletions
diff --git a/src/core/settings/nm-agent-manager.c b/src/core/settings/nm-agent-manager.c
index 7e617909..a1c2eb9c 100644
--- a/src/core/settings/nm-agent-manager.c
+++ b/src/core/settings/nm-agent-manager.c
@@ -9,7 +9,7 @@
 
 #include <pwd.h>
 
-#include "nm-libnm-core-intern/nm-common-macros.h"
+#include "libnm-core-aux-intern/nm-common-macros.h"
 #include "nm-dbus-interface.h"
 #include "nm-secret-agent.h"
 #include "nm-auth-utils.h"
@@ -19,7 +19,7 @@
 #include "nm-session-monitor.h"
 #include "nm-simple-connection.h"
 #include "NetworkManagerUtils.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "c-list/src/c-list.h"
 
 /*****************************************************************************/
diff --git a/src/core/settings/nm-secret-agent.c b/src/core/settings/nm-secret-agent.c
index 7e7c5d7d..45152c27 100644
--- a/src/core/settings/nm-secret-agent.c
+++ b/src/core/settings/nm-secret-agent.c
@@ -10,11 +10,11 @@
 #include <sys/types.h>
 #include <pwd.h>
 
-#include "nm-glib-aux/nm-c-list.h"
-#include "nm-glib-aux/nm-dbus-aux.h"
+#include "libnm-glib-aux/nm-c-list.h"
+#include "libnm-glib-aux/nm-dbus-aux.h"
 #include "nm-dbus-interface.h"
-#include "nm-core-internal.h"
-#include "nm-libnm-core-intern/nm-auth-subject.h"
+#include "libnm-core-intern/nm-core-internal.h"
+#include "libnm-core-aux-intern/nm-auth-subject.h"
 #include "nm-simple-connection.h"
 #include "NetworkManagerUtils.h"
 #include "c-list/src/c-list.h"
@@ -583,7 +583,7 @@ nm_secret_agent_delete_secrets(NMSecretAgent *       self,
     priv = NM_SECRET_AGENT_GET_PRIVATE(self);
 
     /* No secrets sent; agents must be smart enough to track secrets using the UUID or something */
-    dict = nm_connection_to_dbus(connection, NM_CONNECTION_SERIALIZE_NO_SECRETS);
+    dict = nm_connection_to_dbus(connection, NM_CONNECTION_SERIALIZE_WITH_NON_SECRET);
 
     call_id = _call_id_new(self, METHOD_DELETE_SECRETS, path, NULL, callback, callback_data);
 
diff --git a/src/core/settings/nm-settings-connection.c b/src/core/settings/nm-settings-connection.c
index 0ff07189..b423bf5b 100644
--- a/src/core/settings/nm-settings-connection.c
+++ b/src/core/settings/nm-settings-connection.c
@@ -10,8 +10,9 @@
 
 #include "c-list/src/c-list.h"
 
-#include "nm-glib-aux/nm-keyfile-aux.h"
-#include "nm-libnm-core-intern/nm-common-macros.h"
+#include "libnm-glib-aux/nm-keyfile-aux.h"
+#include "libnm-glib-aux/nm-c-list.h"
+#include "libnm-core-aux-intern/nm-common-macros.h"
 #include "nm-config.h"
 #include "nm-config-data.h"
 #include "nm-dbus-interface.h"
@@ -20,7 +21,7 @@
 #include "nm-auth-utils.h"
 #include "nm-agent-manager.h"
 #include "NetworkManagerUtils.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "nm-audit-manager.h"
 #include "nm-settings.h"
 #include "nm-dbus-manager.h"
@@ -30,10 +31,13 @@
 #define AUTOCONNECT_RETRIES_FOREVER     -1
 #define AUTOCONNECT_RESET_RETRIES_TIMER 300
 
-#define _NM_SETTINGS_UPDATE2_FLAG_ALL_PERSIST_MODES                           \
-    ((NMSettingsUpdate2Flags)(                                                \
-        NM_SETTINGS_UPDATE2_FLAG_TO_DISK | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY \
-        | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY))
+#define SEEN_BSSIDS_MAX 30
+
+#define _NM_SETTINGS_UPDATE2_FLAG_ALL_PERSIST_MODES                          \
+    ((NMSettingsUpdate2Flags) (NM_SETTINGS_UPDATE2_FLAG_TO_DISK              \
+                               | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY          \
+                               | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED \
+                               | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY))
 
 /*****************************************************************************/
 
@@ -58,6 +62,56 @@ nm_settings_connections_array_to_connections(NMSettingsConnection *const *connec
 
 /*****************************************************************************/
 
+typedef struct {
+    char  bssid[sizeof(NMEtherAddr) * 3];
+    CList seen_bssids_lst;
+} SeenBssidEntry;
+
+static inline SeenBssidEntry *
+_seen_bssid_entry_init_stale(SeenBssidEntry *entry, const NMEtherAddr *bssid_bin)
+{
+    _nm_utils_hwaddr_ntoa(bssid_bin, sizeof(NMEtherAddr), TRUE, entry->bssid, sizeof(entry->bssid));
+    return entry;
+}
+
+static inline SeenBssidEntry *
+_seen_bssid_entry_new_stale_bin(const NMEtherAddr *bssid_bin)
+{
+    return _seen_bssid_entry_init_stale(g_slice_new(SeenBssidEntry), bssid_bin);
+}
+
+static inline SeenBssidEntry *
+_seen_bssid_entry_new_stale_copy(const SeenBssidEntry *src)
+{
+    SeenBssidEntry *entry;
+
+    entry = g_slice_new(SeenBssidEntry);
+    memcpy(entry->bssid, src->bssid, sizeof(entry->bssid));
+    return entry;
+}
+
+static void
+_seen_bssid_entry_free(gpointer data)
+{
+    SeenBssidEntry *entry = data;
+
+    c_list_unlink_stale(&entry->seen_bssids_lst);
+    nm_g_slice_free(entry);
+}
+
+/*****************************************************************************/
+
+static GHashTable *
+_seen_bssids_hash_new(void)
+{
+    return g_hash_table_new_full(nm_str_hash,
+                                 g_str_equal,
+                                 (GDestroyNotify) _seen_bssid_entry_free,
+                                 NULL);
+}
+
+/*****************************************************************************/
+
 NM_GOBJECT_PROPERTIES_DEFINE(NMSettingsConnection, PROP_UNSAVED, PROP_FLAGS, PROP_FILENAME, );
 
 enum { UPDATED_INTERNAL, FLAGS_CHANGED, LAST_SIGNAL };
@@ -79,19 +133,17 @@ typedef struct _NMSettingsConnectionPrivate {
 
     NMConnection *connection;
 
+    struct {
+        NMConnectionSerializationOptions options;
+        GVariant *                       variant;
+    } getsettings_cached;
+
     NMSettingsStorage *storage;
 
     char *filename;
 
     NMDevice *default_wired_device;
 
-    /* Caches secrets from on-disk connections; were they not cached any
-     * call to nm_connection_clear_secrets() wipes them out and we'd have
-     * to re-read them from disk which defeats the purpose of having the
-     * connection in-memory at all.
-     */
-    GVariant *system_secrets;
-
     /* Caches secrets from agents during the activation process; if new system
      * secrets are returned from an agent, they get written out to disk,
      * triggering a re-read of the connection, which reads only system
@@ -100,7 +152,8 @@ typedef struct _NMSettingsConnectionPrivate {
      */
     GVariant *agent_secrets;
 
-    GHashTable *seen_bssids; /* Up-to-date BSSIDs that's been seen for the connection */
+    CList       seen_bssids_lst_head;
+    GHashTable *seen_bssids_hash;
 
     guint64 timestamp; /* Up-to-date timestamp of connection use */
 
@@ -168,8 +221,9 @@ static const GDBusSignalInfo             signal_info_updated;
 static const GDBusSignalInfo             signal_info_removed;
 static const NMDBusInterfaceInfoExtended interface_info_settings_connection;
 
-static void update_system_secrets_cache(NMSettingsConnection *self, NMConnection *new);
-static void update_agent_secrets_cache(NMSettingsConnection *self, NMConnection *new);
+static void  update_agent_secrets_cache(NMSettingsConnection *self, NMConnection *new);
+static guint _get_seen_bssids(NMSettingsConnection *self,
+                              const char *          strv_buf[static(SEEN_BSSIDS_MAX + 1)]);
 
 /*****************************************************************************/
 
@@ -248,10 +302,53 @@ nm_settings_connection_still_valid(NMSettingsConnection *self)
 
 /*****************************************************************************/
 
-static GHashTable *
-_seen_bssids_hash_new(void)
+static void
+_getsettings_cached_clear(NMSettingsConnectionPrivate *priv)
 {
-    return g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, NULL);
+    if (nm_clear_pointer(&priv->getsettings_cached.variant, g_variant_unref)) {
+        priv->getsettings_cached.options.timestamp.has = FALSE;
+        priv->getsettings_cached.options.timestamp.val = 0;
+        nm_clear_g_free((gpointer *) &priv->getsettings_cached.options.seen_bssids);
+    }
+}
+
+static GVariant *
+_getsettings_cached_get(NMSettingsConnection *self, const NMConnectionSerializationOptions *options)
+{
+    NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE(self);
+    GVariant *                   variant;
+
+    if (priv->getsettings_cached.variant) {
+        if (nm_connection_serialization_options_equal(&priv->getsettings_cached.options, options)) {
+#if NM_MORE_ASSERTS > 10
+            gs_unref_variant GVariant *variant2 = NULL;
+
+            variant = nm_connection_to_dbus_full(priv->connection,
+                                                 NM_CONNECTION_SERIALIZE_WITH_NON_SECRET,
+                                                 options);
+            nm_assert(variant);
+            variant2 = g_variant_new("(@a{sa{sv}})", variant);
+            nm_assert(g_variant_equal(priv->getsettings_cached.variant, variant2));
+#endif
+            return priv->getsettings_cached.variant;
+        }
+        _getsettings_cached_clear(priv);
+    }
+
+    nm_assert(!priv->getsettings_cached.options.seen_bssids);
+
+    variant = nm_connection_to_dbus_full(priv->connection,
+                                         NM_CONNECTION_SERIALIZE_WITH_NON_SECRET,
+                                         options);
+    nm_assert(variant);
+
+    priv->getsettings_cached.variant = g_variant_ref_sink(g_variant_new("(@a{sa{sv}})", variant));
+
+    priv->getsettings_cached.options = *options;
+    priv->getsettings_cached.options.seen_bssids =
+        nm_utils_strv_dup_packed(priv->getsettings_cached.options.seen_bssids, -1);
+
+    return priv->getsettings_cached.variant;
 }
 
 /*****************************************************************************/
@@ -287,6 +384,9 @@ _nm_settings_connection_set_connection(NMSettingsConnection *           self,
         priv->connection = g_object_ref(new_connection);
         nmtst_connection_assert_unchanging(priv->connection);
 
+        _getsettings_cached_clear(priv);
+        _nm_settings_notify_sorted_by_autoconnect_priority_maybe_changed(priv->settings);
+
         /* note that we only return @connection_old if the new connection actually differs from
          * before.
          *
@@ -298,11 +398,6 @@ _nm_settings_connection_set_connection(NMSettingsConnection *           self,
         NM_SET_OUT(out_connection_old, g_steal_pointer(&connection_old));
     }
 
-    if (NM_FLAGS_HAS(update_reason, NM_SETTINGS_CONNECTION_UPDATE_REASON_CLEAR_SYSTEM_SECRETS))
-        update_system_secrets_cache(self, NULL);
-    else if (NM_FLAGS_HAS(update_reason, NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS))
-        update_system_secrets_cache(self, priv->connection);
-
     if (NM_FLAGS_HAS(update_reason, NM_SETTINGS_CONNECTION_UPDATE_REASON_CLEAR_AGENT_SECRETS))
         update_agent_secrets_cache(self, NULL);
     else if (NM_FLAGS_HAS(update_reason, NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS))
@@ -434,63 +529,20 @@ nm_settings_connection_check_permission(NMSettingsConnection *self, const char *
 /*****************************************************************************/
 
 static void
-update_system_secrets_cache(NMSettingsConnection *self, NMConnection *new)
-{
-    NMSettingsConnectionPrivate *priv               = NM_SETTINGS_CONNECTION_GET_PRIVATE(self);
-    gs_unref_object NMConnection *connection_cloned = NULL;
-    gs_unref_variant GVariant *old_secrets          = NULL;
-
-    old_secrets = g_steal_pointer(&priv->system_secrets);
-
-    if (!new)
-        goto out;
-
-    /* FIXME: improve NMConnection API so we can avoid the overhead of cloning the connection,
-     *   in particular if there are no secrets to begin with. */
-
-    connection_cloned = nm_simple_connection_new_clone(new);
-
-    /* Clear out non-system-owned and not-saved secrets */
-    _nm_connection_clear_secrets_by_secret_flags(connection_cloned, NM_SETTING_SECRET_FLAG_NONE);
-
-    priv->system_secrets = nm_g_variant_ref_sink(
-        nm_connection_to_dbus(connection_cloned, NM_CONNECTION_SERIALIZE_ONLY_SECRETS));
-
-out:
-    if (_LOGT_ENABLED()) {
-        if ((!!old_secrets) != (!!priv->system_secrets)) {
-            _LOGT("update system secrets: secrets %s", old_secrets ? "cleared" : "set");
-        } else if (priv->system_secrets && !g_variant_equal(old_secrets, priv->system_secrets))
-            _LOGT("update system secrets: secrets updated");
-    }
-}
-
-static void
 update_agent_secrets_cache(NMSettingsConnection *self, NMConnection *new)
 {
-    NMSettingsConnectionPrivate *priv               = NM_SETTINGS_CONNECTION_GET_PRIVATE(self);
-    gs_unref_object NMConnection *connection_cloned = NULL;
-    gs_unref_variant GVariant *old_secrets          = NULL;
+    NMSettingsConnectionPrivate *priv      = NM_SETTINGS_CONNECTION_GET_PRIVATE(self);
+    gs_unref_variant GVariant *old_secrets = NULL;
 
     old_secrets = g_steal_pointer(&priv->agent_secrets);
 
-    if (!new)
-        goto out;
-
-    /* FIXME: improve NMConnection API so we can avoid the overhead of cloning the connection,
-     *   in particular if there are no secrets to begin with. */
-
-    connection_cloned = nm_simple_connection_new_clone(new);
-
-    /* Clear out non-system-owned secrets */
-    _nm_connection_clear_secrets_by_secret_flags(connection_cloned,
-                                                 NM_SETTING_SECRET_FLAG_NOT_SAVED
-                                                     | NM_SETTING_SECRET_FLAG_AGENT_OWNED);
-
-    priv->agent_secrets = nm_g_variant_ref_sink(
-        nm_connection_to_dbus(connection_cloned, NM_CONNECTION_SERIALIZE_ONLY_SECRETS));
+    if (new) {
+        priv->agent_secrets = nm_g_variant_ref_sink(
+            nm_connection_to_dbus(new,
+                                  NM_CONNECTION_SERIALIZE_WITH_SECRETS_AGENT_OWNED
+                                      | NM_CONNECTION_SERIALIZE_WITH_SECRETS_NOT_SAVED));
+    }
 
-out:
     if (_LOGT_ENABLED()) {
         if ((!!old_secrets) != (!!priv->agent_secrets)) {
             _LOGT("update agent secrets: secrets %s", old_secrets ? "cleared" : "set");
@@ -499,39 +551,6 @@ out:
     }
 }
 
-void
-nm_settings_connection_clear_secrets(NMSettingsConnection *self,
-                                     gboolean              clear_cached_system_secrets,
-                                     gboolean              persist)
-{
-    gs_unref_object NMConnection *connection_cloned = NULL;
-
-    if (!nm_settings_connection_still_valid(self))
-        return;
-
-    /* FIXME: add API to NMConnection so that we can clone a profile without secrets. */
-
-    connection_cloned = nm_simple_connection_new_clone(nm_settings_connection_get_connection(self));
-
-    nm_connection_clear_secrets(connection_cloned);
-
-    if (!nm_settings_connection_update(
-            self,
-            connection_cloned,
-            persist ? NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP
-                    : NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST,
-            NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
-            NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
-            NM_SETTINGS_CONNECTION_UPDATE_REASON_IGNORE_PERSIST_FAILURE
-                | (clear_cached_system_secrets
-                       ? NM_SETTINGS_CONNECTION_UPDATE_REASON_CLEAR_SYSTEM_SECRETS
-                       : NM_SETTINGS_CONNECTION_UPDATE_REASON_NONE)
-                | NM_SETTINGS_CONNECTION_UPDATE_REASON_CLEAR_AGENT_SECRETS,
-            "clear-secrets",
-            NULL))
-        nm_assert_not_reached();
-}
-
 static gboolean
 _secrets_update(NMConnection * connection,
                 const char *   setting_name,
@@ -829,6 +848,18 @@ nm_settings_connection_new_secrets(NMSettingsConnection *self,
     return TRUE;
 }
 
+static gboolean
+match_secret_by_setting_name_and_flags_cb(NMSetting *          setting,
+                                          const char *         secret,
+                                          NMSettingSecretFlags flags,
+                                          gpointer             user_data)
+{
+    const char *get_secrets_setting_name = user_data;
+
+    return nm_streq(nm_setting_get_name(setting), get_secrets_setting_name)
+           && NM_FLAGS_HAS(flags, NM_SETTING_SECRET_FLAG_AGENT_OWNED);
+}
+
 static void
 get_secrets_done_cb(NMAgentManager *             manager,
                     NMAgentManagerCallId         call_id_a,
@@ -846,7 +877,6 @@ get_secrets_done_cb(NMAgentManager *             manager,
     NMSettingsConnectionPrivate *priv;
     NMConnection *               applied_connection;
     gs_free_error GError *local                    = NULL;
-    gs_unref_variant GVariant *system_secrets      = NULL;
     gs_unref_object NMConnection *new_connection   = NULL;
     gboolean                      agent_had_system = FALSE;
     ForEachSecretFlags cmp_flags = {NM_SETTING_SECRET_FLAG_NONE, NM_SETTING_SECRET_FLAG_NONE};
@@ -917,18 +947,12 @@ get_secrets_done_cb(NMAgentManager *             manager,
 
     _LOGD("(%s:%p) secrets request completed", setting_name, call_id);
 
-    system_secrets = nm_g_variant_ref(priv->system_secrets);
-
     new_connection = nm_simple_connection_new_clone(nm_settings_connection_get_connection(self));
 
-    nm_connection_clear_secrets(new_connection);
-
-    if (!_secrets_update(new_connection, setting_name, system_secrets, NULL, &local)) {
-        _LOGD("(%s:%p) failed to update with existing secrets: %s",
-              setting_name,
-              call_id,
-              local->message);
-    }
+    /* Remove old agent-owned secrets in the requested setting */
+    nm_connection_clear_secrets_with_flags(new_connection,
+                                           match_secret_by_setting_name_and_flags_cb,
+                                           (gpointer) setting_name);
 
     /* Update the connection with the agent's secrets; by this point if any
      * system-owned secrets exist in 'secrets' the agent that provided them
@@ -962,7 +986,8 @@ get_secrets_done_cb(NMAgentManager *             manager,
             NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
             NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
             NM_SETTINGS_CONNECTION_UPDATE_REASON_IGNORE_PERSIST_FAILURE
-                | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS
+                | (agent_had_system ? NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS
+                                    : NM_SETTINGS_CONNECTION_UPDATE_REASON_NONE)
                 | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS,
             "get-new-secrets",
             NULL))
@@ -970,6 +995,8 @@ get_secrets_done_cb(NMAgentManager *             manager,
 
     applied_connection = call_id->applied_connection;
     if (applied_connection) {
+        gs_unref_variant GVariant *filtered_secrets2 = NULL;
+
         get_cmp_flags(self,
                       call_id,
                       applied_connection,
@@ -981,18 +1008,12 @@ get_secrets_done_cb(NMAgentManager *             manager,
                       &agent_had_system,
                       &cmp_flags);
 
-        nm_connection_clear_secrets(applied_connection);
-
-        if (!system_secrets
-            || nm_connection_update_secrets(applied_connection,
-                                            setting_name,
-                                            system_secrets,
-                                            NULL)) {
-            gs_unref_variant GVariant *filtered_secrets2 = NULL;
+        nm_connection_clear_secrets_with_flags(applied_connection,
+                                               match_secret_by_setting_name_and_flags_cb,
+                                               (gpointer) setting_name);
 
-            filtered_secrets2 = validate_secret_flags(applied_connection, secrets, &cmp_flags);
-            nm_connection_update_secrets(applied_connection, setting_name, filtered_secrets2, NULL);
-        }
+        filtered_secrets2 = validate_secret_flags(applied_connection, secrets, &cmp_flags);
+        nm_connection_update_secrets(applied_connection, setting_name, filtered_secrets2, NULL);
     }
 
     _get_secrets_info_callback(call_id, agent_username, setting_name, local);
@@ -1060,7 +1081,8 @@ nm_settings_connection_get_secrets(NMSettingsConnection *          self,
     NMAgentManagerCallId         call_id_a;
     gs_free char *               joined_hints = NULL;
     NMSettingsConnectionCallId * call_id;
-    GError *                     local = NULL;
+    GError *                     local        = NULL;
+    gs_unref_variant GVariant *system_secrets = NULL;
 
     g_return_val_if_fail(NM_IS_SETTINGS_CONNECTION(self), NULL);
     g_return_val_if_fail(
@@ -1111,14 +1133,15 @@ nm_settings_connection_get_secrets(NMSettingsConnection *          self,
      * Then we know that the this request probably did not yet include the latest secret-agent. */
     priv->last_secret_agent_version_id = nm_agent_manager_get_agent_version_id(priv->agent_mgr);
 
-    /* Use priv->system_secrets to work around the fact that nm_connection_clear_secrets()
-     * will clear secrets on this object's settings.
-     */
+    system_secrets = nm_g_variant_ref_sink(
+        nm_connection_to_dbus(nm_settings_connection_get_connection(self),
+                              NM_CONNECTION_SERIALIZE_WITH_SECRETS_SYSTEM_OWNED));
+
     call_id_a = nm_agent_manager_get_secrets(priv->agent_mgr,
                                              nm_dbus_object_get_path(NM_DBUS_OBJECT(self)),
                                              nm_settings_connection_get_connection(self),
                                              subject,
-                                             priv->system_secrets,
+                                             system_secrets,
                                              setting_name,
                                              flags,
                                              hints,
@@ -1325,9 +1348,8 @@ get_settings_auth_cb(NMSettingsConnection * self,
                      GError *               error,
                      gpointer               data)
 {
-    gs_free const char **            seen_bssids = NULL;
-    NMConnectionSerializationOptions options     = {};
-    GVariant *                       settings;
+    const char *                     seen_bssids_strv[SEEN_BSSIDS_MAX + 1];
+    NMConnectionSerializationOptions options = {};
 
     if (error) {
         g_dbus_method_invocation_return_gerror(context, error);
@@ -1347,17 +1369,15 @@ get_settings_auth_cb(NMSettingsConnection * self,
      * from the same reason as timestamp. Thus we put it here to GetSettings()
      * return settings too.
      */
-    seen_bssids         = nm_settings_connection_get_seen_bssids(self);
-    options.seen_bssids = seen_bssids;
+    _get_seen_bssids(self, seen_bssids_strv);
+    options.seen_bssids = seen_bssids_strv;
 
     /* Secrets should *never* be returned by the GetSettings method, they
      * get returned by the GetSecrets method which can be better
      * protected against leakage of secrets to unprivileged callers.
      */
-    settings = nm_connection_to_dbus_full(nm_settings_connection_get_connection(self),
-                                          NM_CONNECTION_SERIALIZE_NO_SECRETS,
-                                          &options);
-    g_dbus_method_invocation_return_value(context, g_variant_new("(@a{sa{sv}})", settings));
+
+    g_dbus_method_invocation_return_value(context, _getsettings_cached_get(self, &options));
 }
 
 static void
@@ -1488,14 +1508,21 @@ update_auth_cb(NMSettingsConnection * self,
         if (!_nm_connection_aggregate(info->new_settings,
                                       NM_CONNECTION_AGGREGATE_ANY_SECRETS,
                                       NULL)) {
+            gs_unref_variant GVariant *secrets = NULL;
+
             /* If the new connection has no secrets, we do not want to remove all
              * secrets, rather we keep all the existing ones. Do that by merging
              * them in to the new connection.
              */
+            secrets = nm_g_variant_ref_sink(
+                nm_connection_to_dbus(nm_settings_connection_get_connection(self),
+                                      NM_CONNECTION_SERIALIZE_WITH_SECRETS));
+
+            if (secrets)
+                nm_connection_update_secrets(info->new_settings, NULL, secrets, NULL);
+
             if (priv->agent_secrets)
                 nm_connection_update_secrets(info->new_settings, NULL, priv->agent_secrets, NULL);
-            if (priv->system_secrets)
-                nm_connection_update_secrets(info->new_settings, NULL, priv->system_secrets, NULL);
         } else {
             /* Cache the new secrets from the agent, as stuff like inotify-triggered
              * changes to connection's backing config files will blow them away if
@@ -1558,6 +1585,7 @@ update_auth_cb(NMSettingsConnection * self,
                    : NM_SETTINGS_CONNECTION_UPDATE_REASON_REAPPLY_PARTIAL)
             | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS
             | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS
+            | NM_SETTINGS_CONNECTION_UPDATE_REASON_UPDATE_NON_SECRET
             | (NM_FLAGS_HAS(info->flags, NM_SETTINGS_UPDATE2_FLAG_BLOCK_AUTOCONNECT)
                    ? NM_SETTINGS_CONNECTION_UPDATE_REASON_BLOCK_AUTOCONNECT
                    : NM_SETTINGS_CONNECTION_UPDATE_REASON_NONE),
@@ -1568,7 +1596,7 @@ update_auth_cb(NMSettingsConnection * self,
         gs_unref_object NMConnection *for_agent = NULL;
 
         /* Dupe the connection so we can clear out non-agent-owned secrets,
-         * as agent-owned secrets are the only ones we send back be saved.
+         * as agent-owned secrets are the only ones we send back to be saved.
          * Only send secrets to agents of the same UID that called update too.
          */
         for_agent = nm_simple_connection_new_clone(nm_settings_connection_get_connection(self));
@@ -1754,10 +1782,10 @@ impl_settings_connection_update2(NMDBusObject *                     obj,
     g_variant_get(parameters, "(@a{sa{sv}}u@a{sv})", &settings, &flags_u, &args);
 
     if (NM_FLAGS_ANY(flags_u,
-                     ~((guint32)(_NM_SETTINGS_UPDATE2_FLAG_ALL_PERSIST_MODES
-                                 | NM_SETTINGS_UPDATE2_FLAG_VOLATILE
-                                 | NM_SETTINGS_UPDATE2_FLAG_BLOCK_AUTOCONNECT
-                                 | NM_SETTINGS_UPDATE2_FLAG_NO_REAPPLY)))) {
+                     ~((guint32) (_NM_SETTINGS_UPDATE2_FLAG_ALL_PERSIST_MODES
+                                  | NM_SETTINGS_UPDATE2_FLAG_VOLATILE
+                                  | NM_SETTINGS_UPDATE2_FLAG_BLOCK_AUTOCONNECT
+                                  | NM_SETTINGS_UPDATE2_FLAG_NO_REAPPLY)))) {
         error = g_error_new_literal(NM_SETTINGS_ERROR,
                                     NM_SETTINGS_ERROR_INVALID_ARGUMENTS,
                                     "Unknown flags");
@@ -1888,9 +1916,9 @@ dbus_get_agent_secrets_cb(NMSettingsConnection *      self,
          * by the time we get here.
          */
         dict = nm_connection_to_dbus(nm_settings_connection_get_connection(self),
-                                     NM_CONNECTION_SERIALIZE_ONLY_SECRETS);
+                                     NM_CONNECTION_SERIALIZE_WITH_SECRETS);
         if (!dict)
-            dict = g_variant_new_array(G_VARIANT_TYPE("{sa{sv}}"), NULL, 0);
+            dict = nm_g_variant_singleton_aLsaLsvII();
         g_dbus_method_invocation_return_value(context, g_variant_new("(@a{sa{sv}})", dict));
     }
 }
@@ -1959,8 +1987,9 @@ dbus_clear_secrets_auth_cb(NMSettingsConnection * self,
                            GError *               error,
                            gpointer               user_data)
 {
-    NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE(self);
-    gs_free_error GError *local       = NULL;
+    NMSettingsConnectionPrivate *priv               = NM_SETTINGS_CONNECTION_GET_PRIVATE(self);
+    gs_free_error GError *local                     = NULL;
+    gs_unref_object NMConnection *connection_cloned = NULL;
 
     if (error) {
         g_dbus_method_invocation_return_gerror(context, error);
@@ -1973,7 +2002,24 @@ dbus_clear_secrets_auth_cb(NMSettingsConnection * self,
         return;
     }
 
-    nm_settings_connection_clear_secrets(self, TRUE, TRUE);
+    /* FIXME: add API to NMConnection so that we can clone a profile without secrets. */
+
+    connection_cloned = nm_simple_connection_new_clone(nm_settings_connection_get_connection(self));
+
+    nm_connection_clear_secrets(connection_cloned);
+
+    if (!nm_settings_connection_update(
+            self,
+            connection_cloned,
+            NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP,
+            NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
+            NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
+            NM_SETTINGS_CONNECTION_UPDATE_REASON_IGNORE_PERSIST_FAILURE
+                | NM_SETTINGS_CONNECTION_UPDATE_REASON_CLEAR_SYSTEM_SECRETS
+                | NM_SETTINGS_CONNECTION_UPDATE_REASON_CLEAR_AGENT_SECRETS,
+            "clear-secrets",
+            NULL))
+        nm_assert_not_reached();
 
     /* Tell agents to remove secrets for this connection */
     nm_agent_manager_delete_secrets(priv->agent_mgr,
@@ -2142,7 +2188,9 @@ _cmp_last_resort(NMSettingsConnection *a, NMSettingsConnection *b)
 
     /* hm, same UUID. Use their pointer value to give them a stable
      * order. */
-    return (a > b) ? -1 : 1;
+    NM_CMP_DIRECT_PTR(a, b);
+
+    return nm_assert_unreachable_val(0);
 }
 
 /* sorting for "best" connections.
@@ -2184,6 +2232,15 @@ nm_settings_connection_cmp_autoconnect_priority(NMSettingsConnection *a, NMSetti
 }
 
 int
+nm_settings_connection_cmp_autoconnect_priority_with_data(gconstpointer pa,
+                                                          gconstpointer pb,
+                                                          gpointer      user_data)
+{
+    return nm_settings_connection_cmp_autoconnect_priority((NMSettingsConnection *) pa,
+                                                           (NMSettingsConnection *) pb);
+}
+
+int
 nm_settings_connection_cmp_autoconnect_priority_p_with_data(gconstpointer pa,
                                                             gconstpointer pb,
                                                             gpointer      user_data)
@@ -2243,6 +2300,8 @@ nm_settings_connection_update_timestamp(NMSettingsConnection *self, guint64 time
 
     _LOGT("timestamp: set timestamp %" G_GUINT64_FORMAT, timestamp);
 
+    _nm_settings_notify_sorted_by_autoconnect_priority_maybe_changed(priv->settings);
+
     if (!priv->kf_db_timestamps)
         return;
 
@@ -2293,68 +2352,66 @@ _nm_settings_connection_register_kf_dbs(NMSettingsConnection *self,
 
     if (priv->kf_db_seen_bssids != kf_db_seen_bssids) {
         gs_strfreev char **tmp_strv = NULL;
-        gsize              i, len;
+        gsize              len;
+        gsize              i;
+        guint              result_len;
 
         nm_key_file_db_unref(priv->kf_db_seen_bssids);
         priv->kf_db_seen_bssids = nm_key_file_db_ref(kf_db_seen_bssids);
 
         tmp_strv = nm_key_file_db_get_string_list(priv->kf_db_seen_bssids, connection_uuid, &len);
 
-        nm_clear_pointer(&priv->seen_bssids, g_hash_table_unref);
+        if (priv->seen_bssids_hash)
+            g_hash_table_remove_all(priv->seen_bssids_hash);
 
-        if (len > 0) {
-            _LOGT("read %zu seen-bssids from keyfile database \"%s\"",
-                  len,
-                  nm_key_file_db_get_filename(priv->kf_db_seen_bssids));
-            priv->seen_bssids = _seen_bssids_hash_new();
-            for (i = len; i > 0;)
-                g_hash_table_add(priv->seen_bssids, g_steal_pointer(&tmp_strv[--i]));
-            nm_clear_g_free(&tmp_strv);
-        } else {
-            NMSettingWireless *s_wifi;
+        for (result_len = 0, i = 0; i < len; i++) {
+            NMEtherAddr     addr_bin;
+            SeenBssidEntry *entry;
 
-            _LOGT("no seen-bssids from keyfile database \"%s\"",
-                  nm_key_file_db_get_filename(priv->kf_db_seen_bssids));
+            nm_assert(result_len == nm_g_hash_table_size(priv->seen_bssids_hash));
+            if (result_len >= SEEN_BSSIDS_MAX)
+                break;
 
-            /* If this connection didn't have an entry in the seen-bssids database,
-             * maybe this is the first time we've read it in, so populate the
-             * seen-bssids list from the deprecated seen-bssids property of the
-             * wifi setting.
-             */
-            s_wifi =
-                nm_connection_get_setting_wireless(nm_settings_connection_get_connection(self));
-            if (s_wifi) {
-                len = nm_setting_wireless_get_num_seen_bssids(s_wifi);
-                if (len > 0) {
-                    priv->seen_bssids = _seen_bssids_hash_new();
-                    for (i = 0; i < len; i++) {
-                        const char *bssid = nm_setting_wireless_get_seen_bssid(s_wifi, i);
-
-                        g_hash_table_add(priv->seen_bssids, g_strdup(bssid));
-                    }
-                }
+            if (!_nm_utils_hwaddr_aton_exact(tmp_strv[i], &addr_bin, sizeof(addr_bin)))
+                continue;
+
+            if (!priv->seen_bssids_hash)
+                priv->seen_bssids_hash = _seen_bssids_hash_new();
+
+            entry = _seen_bssid_entry_new_stale_bin(&addr_bin);
+            c_list_link_tail(&priv->seen_bssids_lst_head, &entry->seen_bssids_lst);
+            if (!g_hash_table_insert(priv->seen_bssids_hash, entry, entry)) {
+                /* duplicate detected! The @entry key was freed by g_hash_table_insert(). */
+                continue;
             }
+            result_len++;
         }
+        if (result_len > 0) {
+            _LOGT("read %u seen-bssids from keyfile database \"%s\"",
+                  result_len,
+                  nm_key_file_db_get_filename(priv->kf_db_seen_bssids));
+        } else
+            nm_clear_pointer(&priv->seen_bssids_hash, g_hash_table_destroy);
+
+        nm_assert(nm_g_hash_table_size(priv->seen_bssids_hash) == result_len);
+        nm_assert(result_len <= SEEN_BSSIDS_MAX);
     }
 }
 
-/**
- * nm_settings_connection_get_seen_bssids:
- * @self: the #NMSettingsConnection
- *
- * Returns current list of seen BSSIDs for the connection.
- *
- * Returns: (transfer container) list of seen BSSIDs (in the standard hex-digits-and-colons notation).
- * The caller is responsible for freeing the list, but not the content.
- **/
-const char **
-nm_settings_connection_get_seen_bssids(NMSettingsConnection *self)
+static guint
+_get_seen_bssids(NMSettingsConnection *self, const char *strv_buf[static(SEEN_BSSIDS_MAX + 1)])
 {
-    g_return_val_if_fail(NM_IS_SETTINGS_CONNECTION(self), NULL);
+    NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE(self);
+    SeenBssidEntry *             entry;
+    guint                        i;
 
-    return nm_utils_strdict_get_keys(NM_SETTINGS_CONNECTION_GET_PRIVATE(self)->seen_bssids,
-                                     TRUE,
-                                     NULL);
+    i = 0;
+    c_list_for_each_entry (entry, &priv->seen_bssids_lst_head, seen_bssids_lst) {
+        nm_assert(i <= SEEN_BSSIDS_MAX);
+        strv_buf[i++] = entry->bssid;
+    }
+    strv_buf[i] = NULL;
+    return i;
 }
 
 /**
@@ -2368,14 +2425,17 @@ gboolean
 nm_settings_connection_has_seen_bssid(NMSettingsConnection *self, const char *bssid)
 {
     NMSettingsConnectionPrivate *priv;
+    NMEtherAddr                  addr_bin;
 
     g_return_val_if_fail(NM_IS_SETTINGS_CONNECTION(self), FALSE);
     g_return_val_if_fail(bssid, FALSE);
+    nm_assert(_nm_utils_hwaddr_aton_exact(bssid, &addr_bin, sizeof(addr_bin)));
 
     priv = NM_SETTINGS_CONNECTION_GET_PRIVATE(self);
 
-    return priv->seen_bssids
-           && g_hash_table_contains(NM_SETTINGS_CONNECTION_GET_PRIVATE(self)->seen_bssids, bssid);
+    return priv->seen_bssids_hash
+           && g_hash_table_contains(NM_SETTINGS_CONNECTION_GET_PRIVATE(self)->seen_bssids_hash,
+                                    bssid);
 }
 
 /**
@@ -2390,15 +2450,47 @@ void
 nm_settings_connection_add_seen_bssid(NMSettingsConnection *self, const char *seen_bssid)
 {
     NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE(self);
-    gs_free const char **        strv = NULL;
+    const char *                 seen_bssids_strv[SEEN_BSSIDS_MAX + 1];
+    NMEtherAddr                  addr_bin;
     const char *                 connection_uuid;
+    SeenBssidEntry               entry_stack;
+    SeenBssidEntry *             entry;
+    guint                        i;
 
-    g_return_if_fail(seen_bssid != NULL);
+    g_return_if_fail(seen_bssid);
 
-    if (!priv->seen_bssids)
-        priv->seen_bssids = _seen_bssids_hash_new();
+    if (!_nm_utils_hwaddr_aton_exact(seen_bssid, &addr_bin, sizeof(addr_bin)))
+        g_return_if_reached();
 
-    g_hash_table_add(priv->seen_bssids, g_strdup(seen_bssid));
+    _seen_bssid_entry_init_stale(&entry_stack, &addr_bin);
+
+    if (!priv->seen_bssids_hash) {
+        priv->seen_bssids_hash = _seen_bssids_hash_new();
+        entry                  = NULL;
+    } else
+        entry = g_hash_table_lookup(priv->seen_bssids_hash, &entry_stack);
+
+    if (entry) {
+        if (!nm_c_list_move_front(&priv->seen_bssids_lst_head, &entry->seen_bssids_lst)) {
+            /* no change. */
+            return;
+        }
+    } else {
+        entry = _seen_bssid_entry_new_stale_copy(&entry_stack);
+        c_list_link_front(&priv->seen_bssids_lst_head, &entry->seen_bssids_lst);
+        if (!g_hash_table_add(priv->seen_bssids_hash, entry))
+            nm_assert_not_reached();
+
+        if (g_hash_table_size(priv->seen_bssids_hash) > SEEN_BSSIDS_MAX) {
+            g_hash_table_remove(
+                priv->seen_bssids_hash,
+                c_list_last_entry(&priv->seen_bssids_lst_head, SeenBssidEntry, seen_bssids_lst));
+        }
+    }
+
+    nm_assert(g_hash_table_size(priv->seen_bssids_hash) <= SEEN_BSSIDS_MAX);
+    nm_assert(g_hash_table_size(priv->seen_bssids_hash)
+              == c_list_length(&priv->seen_bssids_lst_head));
 
     if (!priv->kf_db_seen_bssids)
         return;
@@ -2407,12 +2499,8 @@ nm_settings_connection_add_seen_bssid(NMSettingsConnection *self, const char *se
     if (!connection_uuid)
         return;
 
-    strv = nm_utils_strdict_get_keys(priv->seen_bssids, TRUE, NULL);
-
-    nm_key_file_db_set_string_list(priv->kf_db_seen_bssids,
-                                   connection_uuid,
-                                   strv ?: NM_PTRARRAY_EMPTY(const char *),
-                                   -1);
+    i = _get_seen_bssids(self, seen_bssids_strv);
+    nm_key_file_db_set_string_list(priv->kf_db_seen_bssids, connection_uuid, seen_bssids_strv, i);
 }
 
 /*****************************************************************************/
@@ -2623,7 +2711,7 @@ nm_settings_connection_init(NMSettingsConnection *self)
     self->_priv = priv;
 
     c_list_init(&self->_connections_lst);
-
+    c_list_init(&priv->seen_bssids_lst_head);
     c_list_init(&priv->call_ids_lst_head);
     c_list_init(&priv->auth_lst_head);
 
@@ -2659,15 +2747,16 @@ dispose(GObject *object)
             _get_secrets_cancel(self, call_id, TRUE);
     }
 
-    nm_clear_pointer(&priv->system_secrets, g_variant_unref);
     nm_clear_pointer(&priv->agent_secrets, g_variant_unref);
 
-    nm_clear_pointer(&priv->seen_bssids, g_hash_table_destroy);
+    nm_clear_pointer(&priv->seen_bssids_hash, g_hash_table_destroy);
 
     g_clear_object(&priv->agent_mgr);
 
     g_clear_object(&priv->connection);
 
+    _getsettings_cached_clear(priv);
+
     nm_clear_pointer(&priv->kf_db_timestamps, nm_key_file_db_unref);
     nm_clear_pointer(&priv->kf_db_seen_bssids, nm_key_file_db_unref);
 
@@ -2732,20 +2821,17 @@ static const NMDBusInterfaceInfoExtended interface_info_settings_connection = {
                     .out_args =
                         NM_DEFINE_GDBUS_ARG_INFOS(NM_DEFINE_GDBUS_ARG_INFO("result", "a{sv}"), ), ),
                 .handle = impl_settings_connection_update2, ), ),
-        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&nm_signal_info_property_changed_legacy,
-                                                &signal_info_updated,
-                                                &signal_info_removed, ),
+        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&signal_info_updated, &signal_info_removed, ),
         .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Unsaved",
-                                                             "b",
-                                                             NM_SETTINGS_CONNECTION_UNSAVED),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Unsaved",
+                                                           "b",
+                                                           NM_SETTINGS_CONNECTION_UNSAVED),
             NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Flags",
                                                            "u",
                                                            NM_SETTINGS_CONNECTION_FLAGS),
             NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Filename",
                                                            "s",
                                                            NM_SETTINGS_CONNECTION_FILENAME), ), ),
-    .legacy_property_changed = TRUE,
 };
 
 static void
diff --git a/src/core/settings/nm-settings-connection.h b/src/core/settings/nm-settings-connection.h
index 7c61dbeb..fa3dbcfb 100644
--- a/src/core/settings/nm-settings-connection.h
+++ b/src/core/settings/nm-settings-connection.h
@@ -63,6 +63,9 @@ typedef enum {
 
     NM_SETTINGS_CONNECTION_UPDATE_REASON_BLOCK_AUTOCONNECT = (1u << 8),
 
+    /* Is anything other than secrets changing */
+    NM_SETTINGS_CONNECTION_UPDATE_REASON_UPDATE_NON_SECRET = (1u << 9),
+
 } NMSettingsConnectionUpdateReason;
 
 typedef enum {
@@ -274,10 +277,6 @@ nm_settings_connection_get_secrets(NMSettingsConnection *          self,
 void nm_settings_connection_cancel_secrets(NMSettingsConnection *      self,
                                            NMSettingsConnectionCallId *call_id);
 
-void nm_settings_connection_clear_secrets(NMSettingsConnection *self,
-                                          gboolean              clear_cached_system_secrets,
-                                          gboolean              persist);
-
 gboolean nm_settings_connection_check_visibility(NMSettingsConnection *self,
                                                  NMSessionMonitor *    session_monitor);
 
@@ -324,6 +323,9 @@ int nm_settings_connection_cmp_timestamp_p_with_data(gconstpointer pa,
                                                      gpointer      user_data);
 int nm_settings_connection_cmp_autoconnect_priority(NMSettingsConnection *a,
                                                     NMSettingsConnection *b);
+int nm_settings_connection_cmp_autoconnect_priority_with_data(gconstpointer pa,
+                                                              gconstpointer pb,
+                                                              gpointer      user_data);
 int nm_settings_connection_cmp_autoconnect_priority_p_with_data(gconstpointer pa,
                                                                 gconstpointer pb,
                                                                 gpointer      user_data);
@@ -338,8 +340,6 @@ gboolean nm_settings_connection_get_timestamp(NMSettingsConnection *self, guint6
 
 void nm_settings_connection_update_timestamp(NMSettingsConnection *self, guint64 timestamp);
 
-const char **nm_settings_connection_get_seen_bssids(NMSettingsConnection *self);
-
 gboolean nm_settings_connection_has_seen_bssid(NMSettingsConnection *self, const char *bssid);
 
 void nm_settings_connection_add_seen_bssid(NMSettingsConnection *self, const char *seen_bssid);
diff --git a/src/core/settings/nm-settings-plugin.c b/src/core/settings/nm-settings-plugin.c
index 9e81caab..66cc19b6 100644
--- a/src/core/settings/nm-settings-plugin.c
+++ b/src/core/settings/nm-settings-plugin.c
@@ -9,7 +9,7 @@
 #include "nm-settings-plugin.h"
 
 #include "nm-utils.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 
 #include "nm-settings-connection.h"
 
diff --git a/src/core/settings/nm-settings-storage.c b/src/core/settings/nm-settings-storage.c
index c56ba075..e284b822 100644
--- a/src/core/settings/nm-settings-storage.c
+++ b/src/core/settings/nm-settings-storage.c
@@ -7,6 +7,7 @@
 
 #include "nm-settings-storage.h"
 
+#include "libnm-glib-aux/nm-uuid.h"
 #include "nm-utils.h"
 #include "nm-settings-plugin.h"
 
@@ -72,7 +73,7 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
     case PROP_UUID:
         /* construct-only */
         self->_uuid = g_value_dup_string(value);
-        nm_assert(!self->_uuid || nm_utils_is_uuid(self->_uuid));
+        nm_assert(!self->_uuid || nm_uuid_is_normalized(self->_uuid));
         break;
     case PROP_FILENAME:
         /* construct-only */
@@ -97,7 +98,7 @@ NMSettingsStorage *
 nm_settings_storage_new(NMSettingsPlugin *plugin, const char *uuid, const char *filename)
 {
     nm_assert(NM_IS_SETTINGS_PLUGIN(plugin));
-    nm_assert(nm_utils_is_uuid(uuid));
+    nm_assert(nm_uuid_is_normalized(uuid));
 
     return g_object_new(NM_TYPE_SETTINGS_STORAGE,
                         NM_SETTINGS_STORAGE_PLUGIN,
diff --git a/src/core/settings/nm-settings-storage.h b/src/core/settings/nm-settings-storage.h
index 970cfd77..2e5e1c55 100644
--- a/src/core/settings/nm-settings-storage.h
+++ b/src/core/settings/nm-settings-storage.h
@@ -48,36 +48,35 @@ GType nm_settings_storage_get_type(void);
 NMSettingsStorage *
 nm_settings_storage_new(struct _NMSettingsPlugin *plugin, const char *uuid, const char *filename);
 
+/* forward declare so we don't have to include "nm-settings-plugin.h" here. */
+GType nm_settings_plugin_get_type(void);
+
 static inline struct _NMSettingsPlugin *
 nm_settings_storage_get_plugin(const NMSettingsStorage *self)
 {
-    GType nm_settings_plugin_get_type(void);
-
     g_return_val_if_fail(NM_IS_SETTINGS_STORAGE(self), NULL);
 
     nm_assert(G_TYPE_CHECK_INSTANCE_TYPE(self->_plugin, nm_settings_plugin_get_type()));
     return self->_plugin;
 }
 
+gboolean nm_uuid_is_normalized_full(const char *str);
+
 static inline const char *
 nm_settings_storage_get_uuid(const NMSettingsStorage *self)
 {
-    gboolean nm_utils_is_uuid(const char *str);
-
     g_return_val_if_fail(NM_IS_SETTINGS_STORAGE(self), NULL);
 
-    nm_assert(nm_utils_is_uuid(self->_uuid));
+    nm_assert(nm_uuid_is_normalized_full(self->_uuid));
     return self->_uuid;
 }
 
 static inline const char *
 nm_settings_storage_get_uuid_opt(const NMSettingsStorage *self)
 {
-    gboolean nm_utils_is_uuid(const char *str);
-
     g_return_val_if_fail(NM_IS_SETTINGS_STORAGE(self), NULL);
 
-    nm_assert(!self->_uuid || nm_utils_is_uuid(self->_uuid));
+    nm_assert(!self->_uuid || nm_uuid_is_normalized_full(self->_uuid));
     return self->_uuid;
 }
 
diff --git a/src/core/settings/nm-settings.c b/src/core/settings/nm-settings.c
index 3d31fb8b..f9f98de7 100644
--- a/src/core/settings/nm-settings.c
+++ b/src/core/settings/nm-settings.c
@@ -20,9 +20,10 @@
     #include <selinux/selinux.h>
 #endif
 
-#include "nm-libnm-core-intern/nm-common-macros.h"
-#include "nm-glib-aux/nm-keyfile-aux.h"
-#include "nm-keyfile-internal.h"
+#include "libnm-core-aux-intern/nm-common-macros.h"
+#include "libnm-glib-aux/nm-uuid.h"
+#include "libnm-glib-aux/nm-keyfile-aux.h"
+#include "libnm-core-intern/nm-keyfile-internal.h"
 #include "nm-dbus-interface.h"
 #include "nm-connection.h"
 #include "nm-setting-8021x.h"
@@ -44,17 +45,17 @@
 #include "nm-setting-proxy.h"
 #include "nm-setting-bond.h"
 #include "nm-utils.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 
-#include "nm-std-aux/c-list-util.h"
-#include "nm-glib-aux/nm-c-list.h"
+#include "libnm-std-aux/c-list-util.h"
+#include "libnm-glib-aux/nm-c-list.h"
 #include "nm-dbus-object.h"
 #include "devices/nm-device-ethernet.h"
 #include "nm-settings-connection.h"
 #include "nm-settings-plugin.h"
 #include "nm-dbus-manager.h"
 #include "nm-auth-utils.h"
-#include "nm-libnm-core-intern/nm-auth-subject.h"
+#include "libnm-core-aux-intern/nm-auth-subject.h"
 #include "nm-session-monitor.h"
 #include "plugins/keyfile/nms-keyfile-plugin.h"
 #include "plugins/keyfile/nms-keyfile-storage.h"
@@ -139,7 +140,7 @@ nm_assert_storage_data_lst(CList *head)
         u = nm_settings_storage_get_uuid(sd->storage);
         if (!uuid) {
             uuid = u;
-            nm_assert(nm_utils_is_uuid(uuid));
+            nm_assert(nm_uuid_is_normalized(uuid));
         } else
             nm_assert(nm_streq0(uuid, u));
     }
@@ -182,7 +183,7 @@ _sett_conn_entry_new(const char *uuid)
     SettConnEntry *sett_conn_entry;
     gsize          l_p_1;
 
-    nm_assert(nm_utils_is_uuid(uuid));
+    nm_assert(nm_uuid_is_normalized(uuid));
 
     l_p_1 = strlen(uuid) + 1;
 
@@ -370,6 +371,7 @@ typedef struct {
     CList connections_lst_head;
 
     NMSettingsConnection **connections_cached_list;
+    NMSettingsConnection **connections_cached_list_sorted_by_autoconnect_priority;
 
     GSList *unmanaged_specs;
     GSList *unrecognized_specs;
@@ -386,8 +388,17 @@ typedef struct {
     guint kf_db_flush_idle_id_timestamps;
     guint kf_db_flush_idle_id_seen_bssids;
 
+    bool kf_db_pruned_timestamps;
+    bool kf_db_pruned_seen_bssid;
+
     bool started : 1;
 
+    /* Whether NMSettingsConnections changed in a way that affects the comparison
+     * with nm_settings_connection_cmp_autoconnect_priority_with_data(). In that case,
+     * we may need to re-sort the connections_cached_list_sorted_by_autoconnect_priority
+     * list. */
+    bool sorted_by_autoconnect_priority_maybe_changed : 1;
+
 } NMSettingsPrivate;
 
 struct _NMSettings {
@@ -487,6 +498,8 @@ _startup_complete_check_is_ready(NMSettings *          self,
     conn = nm_settings_connection_get_connection(sett_conn);
 
     nm_manager_for_each_device (priv->manager, device, tmp_lst) {
+        gs_free_error GError *error = NULL;
+
         if (!nm_device_is_real(device))
             continue;
 
@@ -497,7 +510,13 @@ _startup_complete_check_is_ready(NMSettings *          self,
             continue;
         }
 
-        if (!nm_device_check_connection_compatible(device, conn, NULL))
+        /* Check that device is compatible with the device. We are also happy
+         * with a device compatible but for which the connection is disallowed
+         * by NM configuration. */
+        if (!nm_device_check_connection_compatible(device, conn, &error)
+            && !g_error_matches(error,
+                                NM_UTILS_ERROR,
+                                NM_UTILS_ERROR_CONNECTION_AVAILABLE_DISALLOWED))
             continue;
 
         return TRUE;
@@ -1433,7 +1452,8 @@ _plugin_connections_reload(NMSettings *self)
         NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
         TRUE,
         NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS
-            | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS);
+            | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS
+            | NM_SETTINGS_CONNECTION_UPDATE_REASON_UPDATE_NON_SECRET);
 
     for (iter = priv->plugins; iter; iter = iter->next)
         nm_settings_plugin_load_connections_done(iter->data);
@@ -1460,7 +1480,7 @@ _add_connection_to_first_plugin(NMSettings *                 self,
 
     uuid = nm_connection_get_uuid(new_connection);
 
-    nm_assert(nm_utils_is_uuid(uuid));
+    nm_assert(nm_uuid_is_normalized(uuid));
 
     for (iter = priv->plugins; iter; iter = iter->next) {
         NMSettingsPlugin *plugin                               = NM_SETTINGS_PLUGIN(iter->data);
@@ -1541,9 +1561,7 @@ _add_connection_to_first_plugin(NMSettings *                 self,
         }
 
         agent_owned_secrets =
-            nm_connection_to_dbus(new_connection,
-                                  NM_CONNECTION_SERIALIZE_ONLY_SECRETS
-                                      | NM_CONNECTION_SERIALIZE_WITH_SECRETS_AGENT_OWNED);
+            nm_connection_to_dbus(new_connection, NM_CONNECTION_SERIALIZE_WITH_SECRETS_AGENT_OWNED);
         connection_to_add_real =
             _connection_changed_normalize_connection(storage,
                                                      connection_to_add,
@@ -1912,7 +1930,8 @@ again_delete_tombstone:
         _NM_SETTINGS_CONNECTION_INT_FLAGS_PERSISTENT_MASK,
         FALSE,
         NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS
-            | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS
+            | NM_SETTINGS_CONNECTION_UPDATE_REASON_CLEAR_AGENT_SECRETS
+            | NM_SETTINGS_CONNECTION_UPDATE_REASON_UPDATE_NON_SECRET
             | (NM_FLAGS_HAS(add_reason, NM_SETTINGS_CONNECTION_ADD_REASON_BLOCK_AUTOCONNECT)
                    ? NM_SETTINGS_CONNECTION_UPDATE_REASON_BLOCK_AUTOCONNECT
                    : NM_SETTINGS_CONNECTION_UPDATE_REASON_NONE));
@@ -2218,9 +2237,7 @@ nm_settings_update_connection(NMSettings *                     self,
             nm_assert(nm_streq(uuid, nm_settings_storage_get_uuid(new_storage)));
 
             agent_owned_secrets =
-                nm_connection_to_dbus(connection,
-                                      NM_CONNECTION_SERIALIZE_ONLY_SECRETS
-                                          | NM_CONNECTION_SERIALIZE_WITH_SECRETS_AGENT_OWNED);
+                nm_connection_to_dbus(connection, NM_CONNECTION_SERIALIZE_WITH_SECRETS_AGENT_OWNED);
             new_connection_real = _connection_changed_normalize_connection(new_storage,
                                                                            new_connection,
                                                                            agent_owned_secrets,
@@ -2297,7 +2314,7 @@ nm_settings_delete_connection(NMSettings *          self,
     nm_assert(NM_IS_SETTINGS_STORAGE(cur_storage));
 
     uuid = nm_settings_storage_get_uuid(cur_storage);
-    nm_assert(nm_utils_is_uuid(uuid));
+    nm_assert(nm_uuid_is_normalized(uuid));
 
     sett_conn_entry = _sett_conn_entries_get(self, uuid);
 
@@ -2691,9 +2708,9 @@ impl_settings_add_connection2(NMDBusObject *                     obj,
     g_variant_get(parameters, "(@a{sa{sv}}u@a{sv})", &settings, &flags_u, &args);
 
     if (NM_FLAGS_ANY(flags_u,
-                     ~((guint32)(NM_SETTINGS_ADD_CONNECTION2_FLAG_TO_DISK
-                                 | NM_SETTINGS_ADD_CONNECTION2_FLAG_IN_MEMORY
-                                 | NM_SETTINGS_ADD_CONNECTION2_FLAG_BLOCK_AUTOCONNECT)))) {
+                     ~((guint32) (NM_SETTINGS_ADD_CONNECTION2_FLAG_TO_DISK
+                                  | NM_SETTINGS_ADD_CONNECTION2_FLAG_IN_MEMORY
+                                  | NM_SETTINGS_ADD_CONNECTION2_FLAG_BLOCK_AUTOCONNECT)))) {
         g_dbus_method_invocation_take_error(invocation,
                                             g_error_new_literal(NM_SETTINGS_ERROR,
                                                                 NM_SETTINGS_ERROR_INVALID_ARGUMENTS,
@@ -2814,7 +2831,8 @@ impl_settings_load_connections(NMDBusObject *                     obj,
             NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
             TRUE,
             NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS
-                | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS);
+                | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS
+                | NM_SETTINGS_CONNECTION_UPDATE_REASON_UPDATE_NON_SECRET);
 
         for (iter = priv->plugins; iter; iter = iter->next)
             nm_settings_plugin_load_connections_done(iter->data);
@@ -2832,7 +2850,7 @@ impl_settings_load_connections(NMDBusObject *                     obj,
 
     g_dbus_method_invocation_return_value(invocation,
                                           g_variant_new("(b^as)",
-                                                        (gboolean)(!failures),
+                                                        (gboolean) (!failures),
                                                         failures
                                                             ? (const char **) failures->pdata
                                                             : NM_PTRARRAY_EMPTY(const char *)));
@@ -2870,25 +2888,48 @@ impl_settings_reload_connections(NMDBusObject *                     obj,
 
 /*****************************************************************************/
 
+void
+_nm_settings_notify_sorted_by_autoconnect_priority_maybe_changed(NMSettings *self)
+{
+    NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE(self);
+
+    priv->sorted_by_autoconnect_priority_maybe_changed = TRUE;
+}
+
 static void
 _clear_connections_cached_list(NMSettingsPrivate *priv)
 {
-    if (!priv->connections_cached_list)
-        return;
+    if (priv->connections_cached_list) {
+        nm_assert(priv->connections_len == NM_PTRARRAY_LEN(priv->connections_cached_list));
+
+#if NM_MORE_ASSERTS
+        /* set the pointer to a bogus value. This makes it more apparent
+         * if somebody has a reference to the cached list and still uses
+         * it. That is a bug, this code just tries to make it blow up
+         * more eagerly. */
+        memset(priv->connections_cached_list,
+               0x43,
+               sizeof(NMSettingsConnection *) * (priv->connections_len + 1));
+#endif
 
-    nm_assert(priv->connections_len == NM_PTRARRAY_LEN(priv->connections_cached_list));
+        nm_clear_g_free(&priv->connections_cached_list);
+    }
+    if (priv->connections_cached_list_sorted_by_autoconnect_priority) {
+        nm_assert(priv->connections_len
+                  == NM_PTRARRAY_LEN(priv->connections_cached_list_sorted_by_autoconnect_priority));
 
 #if NM_MORE_ASSERTS
-    /* set the pointer to a bogus value. This makes it more apparent
-     * if somebody has a reference to the cached list and still uses
-     * it. That is a bug, this code just tries to make it blow up
-     * more eagerly. */
-    memset(priv->connections_cached_list,
-           0x43,
-           sizeof(NMSettingsConnection *) * (priv->connections_len + 1));
+        /* set the pointer to a bogus value. This makes it more apparent
+         * if somebody has a reference to the cached list and still uses
+         * it. That is a bug, this code just tries to make it blow up
+         * more eagerly. */
+        memset(priv->connections_cached_list_sorted_by_autoconnect_priority,
+               0x42,
+               sizeof(NMSettingsConnection *) * (priv->connections_len + 1));
 #endif
 
-    nm_clear_g_free(&priv->connections_cached_list);
+        nm_clear_g_free(&priv->connections_cached_list_sorted_by_autoconnect_priority);
+    }
 }
 
 static void
@@ -3027,6 +3068,65 @@ nm_settings_get_connections(NMSettings *self, guint *out_len)
     return priv->connections_cached_list;
 }
 
+NMSettingsConnection *const *
+nm_settings_get_connections_sorted_by_autoconnect_priority(NMSettings *self, guint *out_len)
+{
+    NMSettingsPrivate *priv;
+    gboolean           needs_sort = FALSE;
+
+    g_return_val_if_fail(NM_IS_SETTINGS(self), NULL);
+
+    priv = NM_SETTINGS_GET_PRIVATE(self);
+
+    nm_assert(priv->connections_len == c_list_length(&priv->connections_lst_head));
+    nm_assert(
+        !priv->connections_cached_list_sorted_by_autoconnect_priority
+        || (priv->connections_len
+            == NM_PTRARRAY_LEN(priv->connections_cached_list_sorted_by_autoconnect_priority)));
+
+    if (!priv->connections_cached_list_sorted_by_autoconnect_priority) {
+        NMSettingsConnection *const *list_cached;
+        guint                        len;
+
+        list_cached = nm_settings_get_connections(self, &len);
+        priv->connections_cached_list_sorted_by_autoconnect_priority =
+            nm_memdup(list_cached, sizeof(NMSettingsConnection *) * (len + 1));
+        needs_sort = (len > 1);
+    } else if (priv->sorted_by_autoconnect_priority_maybe_changed) {
+        if (!nm_utils_ptrarray_is_sorted(
+                (gconstpointer *) priv->connections_cached_list_sorted_by_autoconnect_priority,
+                priv->connections_len,
+                FALSE,
+                nm_settings_connection_cmp_autoconnect_priority_with_data,
+                NULL)) {
+            /* We cache the sorted list, but we don't monitor all entries whether they
+             * get modified to invalidate the sort order. So every time we have to check
+             * whether the sort order is still correct. The vast majority of the time it
+             * is, and this check is faster than sorting anew. */
+            needs_sort = TRUE;
+        }
+    } else {
+        nm_assert(nm_utils_ptrarray_is_sorted(
+            (gconstpointer *) priv->connections_cached_list_sorted_by_autoconnect_priority,
+            priv->connections_len,
+            TRUE,
+            nm_settings_connection_cmp_autoconnect_priority_with_data,
+            NULL));
+    }
+
+    priv->sorted_by_autoconnect_priority_maybe_changed = FALSE;
+    if (needs_sort) {
+        g_qsort_with_data(priv->connections_cached_list_sorted_by_autoconnect_priority,
+                          priv->connections_len,
+                          sizeof(NMSettingsConnection *),
+                          nm_settings_connection_cmp_autoconnect_priority_p_with_data,
+                          NULL);
+    }
+
+    NM_SET_OUT(out_len, priv->connections_len);
+    return priv->connections_cached_list_sorted_by_autoconnect_priority;
+}
+
 /**
  * nm_settings_get_connections_clone:
  * @self: the #NMSetting
@@ -3058,9 +3158,13 @@ nm_settings_get_connections_clone(NMSettings *                   self,
 
     g_return_val_if_fail(NM_IS_SETTINGS(self), NULL);
 
-    list_cached = nm_settings_get_connections(self, &len);
+    if (sort_compare_func == nm_settings_connection_cmp_autoconnect_priority_p_with_data) {
+        list_cached       = nm_settings_get_connections_sorted_by_autoconnect_priority(self, &len);
+        sort_compare_func = NULL;
+    } else
+        list_cached = nm_settings_get_connections(self, &len);
 
-#if NM_MORE_ASSERTS
+#if NM_MORE_ASSERTS > 10
     nm_assert(list_cached);
     for (i = 0; i < len; i++)
         nm_assert(NM_IS_SETTINGS_CONNECTION(list_cached[i]));
@@ -3204,7 +3308,7 @@ add_plugin_load_file(NMSettings *self, const char *pname, GError **error)
 
     /* errors after this point are fatal, because we loaded the shared library already. */
 
-    if (!g_module_symbol(module, "nm_settings_plugin_factory", (gpointer)(&factory_func))) {
+    if (!g_module_symbol(module, "nm_settings_plugin_factory", (gpointer) (&factory_func))) {
         g_set_error(error,
                     NM_SETTINGS_ERROR,
                     NM_SETTINGS_ERROR_FAILED,
@@ -3591,6 +3695,41 @@ again:
 
 /*****************************************************************************/
 
+static gboolean
+_kf_db_prune_predicate(const char *uuid, gpointer user_data)
+{
+    return !!nm_settings_get_connection_by_uuid(user_data, uuid);
+}
+
+static void
+_kf_db_to_file(NMSettings *self, gboolean is_timestamps, gboolean force_write)
+{
+    NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE(self);
+    NMKeyFileDB *      kf_db;
+    bool *             p_kf_db_pruned;
+
+    if (is_timestamps) {
+        kf_db          = priv->kf_db_timestamps;
+        p_kf_db_pruned = &priv->kf_db_pruned_timestamps;
+    } else {
+        kf_db          = priv->kf_db_seen_bssids;
+        p_kf_db_pruned = &priv->kf_db_pruned_seen_bssid;
+    }
+
+    if (!*p_kf_db_pruned) {
+        /* we only prune the DB once, because afterwards every
+         * add/remove of an connection will lead to a direct update. */
+        *p_kf_db_pruned = TRUE;
+        nm_key_file_db_prune(kf_db, _kf_db_prune_predicate, self);
+
+        /* once we also go over the directory, and see whether we
+         * have any left over temporary files to delete. */
+        nm_key_file_db_prune_tmp_files(kf_db);
+    }
+
+    nm_key_file_db_to_file(kf_db, force_write);
+}
+
 G_GNUC_PRINTF(4, 5)
 static void
 _kf_db_log_fcn(NMKeyFileDB *kf_db, int syslog_level, gpointer user_data, const char *fmt, ...)
@@ -3639,7 +3778,7 @@ _kf_db_got_dirty_flush(NMSettings *self, gboolean is_timestamps)
     }
 
     if (nm_key_file_db_is_dirty(kf_db))
-        nm_key_file_db_to_file(kf_db, FALSE);
+        _kf_db_to_file(self, is_timestamps, FALSE);
     else {
         _LOGT("[%s-keyfile]: skip saving changes to \"%s\"",
               prefix,
@@ -3692,15 +3831,10 @@ _kf_db_got_dirty_fcn(NMKeyFileDB *kf_db, gpointer user_data)
 void
 nm_settings_kf_db_write(NMSettings *self)
 {
-    NMSettingsPrivate *priv;
-
     g_return_if_fail(NM_IS_SETTINGS(self));
 
-    priv = NM_SETTINGS_GET_PRIVATE(self);
-    if (priv->kf_db_timestamps)
-        nm_key_file_db_to_file(priv->kf_db_timestamps, TRUE);
-    if (priv->kf_db_seen_bssids)
-        nm_key_file_db_to_file(priv->kf_db_seen_bssids, TRUE);
+    _kf_db_to_file(self, TRUE, TRUE);
+    _kf_db_to_file(self, FALSE, TRUE);
 }
 
 /*****************************************************************************/
@@ -3938,8 +4072,8 @@ finalize(GObject *object)
 
     nm_clear_g_source(&priv->kf_db_flush_idle_id_timestamps);
     nm_clear_g_source(&priv->kf_db_flush_idle_id_seen_bssids);
-    nm_key_file_db_to_file(priv->kf_db_timestamps, FALSE);
-    nm_key_file_db_to_file(priv->kf_db_seen_bssids, FALSE);
+    _kf_db_to_file(self, TRUE, FALSE);
+    _kf_db_to_file(self, FALSE, FALSE);
     nm_key_file_db_destroy(priv->kf_db_timestamps);
     nm_key_file_db_destroy(priv->kf_db_seen_bssids);
 
@@ -4027,18 +4161,16 @@ static const NMDBusInterfaceInfoExtended interface_info_settings = {
                     .in_args =
                         NM_DEFINE_GDBUS_ARG_INFOS(NM_DEFINE_GDBUS_ARG_INFO("hostname", "s"), ), ),
                 .handle = impl_settings_save_hostname, ), ),
-        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&nm_signal_info_property_changed_legacy,
-                                                &signal_info_new_connection,
+        .signals    = NM_DEFINE_GDBUS_SIGNAL_INFOS(&signal_info_new_connection,
                                                 &signal_info_connection_removed, ),
         .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS(
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Connections",
-                                                             "ao",
-                                                             NM_SETTINGS_CONNECTIONS),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Hostname", "s", NM_SETTINGS_HOSTNAME),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("CanModify",
-                                                             "b",
-                                                             NM_SETTINGS_CAN_MODIFY), ), ),
-    .legacy_property_changed = TRUE,
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Connections",
+                                                           "ao",
+                                                           NM_SETTINGS_CONNECTIONS),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Hostname", "s", NM_SETTINGS_HOSTNAME),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("CanModify",
+                                                           "b",
+                                                           NM_SETTINGS_CAN_MODIFY), ), ),
 };
 
 static void
diff --git a/src/core/settings/nm-settings.h b/src/core/settings/nm-settings.h
index 09a0af57..ce18fecc 100644
--- a/src/core/settings/nm-settings.h
+++ b/src/core/settings/nm-settings.h
@@ -79,6 +79,9 @@ void nm_settings_add_connection_dbus(NMSettings *                    self,
 
 NMSettingsConnection *const *nm_settings_get_connections(NMSettings *settings, guint *out_len);
 
+NMSettingsConnection *const *
+nm_settings_get_connections_sorted_by_autoconnect_priority(NMSettings *self, guint *out_len);
+
 NMSettingsConnection **nm_settings_get_connections_clone(NMSettings *                   self,
                                                          guint *                        out_len,
                                                          NMSettingsConnectionFilterFunc func,
@@ -127,4 +130,6 @@ const char *nm_settings_get_startup_complete_blocked_reason(NMSettings *self,
 
 void nm_settings_kf_db_write(NMSettings *settings);
 
+void _nm_settings_notify_sorted_by_autoconnect_priority_maybe_changed(NMSettings *self);
+
 #endif /* __NM_SETTINGS_H__ */
diff --git a/src/core/settings/plugins/ifcfg-rh/meson.build b/src/core/settings/plugins/ifcfg-rh/meson.build
index a2f11a92..c62fa89d 100644
--- a/src/core/settings/plugins/ifcfg-rh/meson.build
+++ b/src/core/settings/plugins/ifcfg-rh/meson.build
@@ -28,7 +28,6 @@ libnms_ifcfg_rh_core = static_library(
     'shvar.c',
   ),
   dependencies: core_default_dep,
-  c_args: daemon_c_flags,
 )
 
 libnm_settings_plugin_ifcfg_rh = shared_module(
@@ -38,7 +37,6 @@ libnm_settings_plugin_ifcfg_rh = shared_module(
     'nms-ifcfg-rh-plugin.c',
   ),
   dependencies: core_plugin_dep,
-  c_args: daemon_c_flags,
   link_with: libnms_ifcfg_rh_core,
   link_args: ldflags_linker_script_settings,
   link_depends: linker_script_settings,
diff --git a/src/core/settings/plugins/ifcfg-rh/nm-ifdown b/src/core/settings/plugins/ifcfg-rh/nm-ifdown
index e7bd73ae..3b05ef1c 100644..100755
--- a/src/core/settings/plugins/ifcfg-rh/nm-ifdown
+++ b/src/core/settings/plugins/ifcfg-rh/nm-ifdown
@@ -1,3 +1,44 @@
-#!/bin/sh
-nmcli connection load "/etc/sysconfig/network-scripts/ifcfg-$1" &&
-exec nmcli connection down filename "/etc/sysconfig/network-scripts/ifcfg-$1"
+#!/bin/bash
+
+CONFIG="$1"
+
+if [ -z "${CONFIG}" ] ; then
+    echo $"Usage: ifdown <device name>" >&2
+    exit 1
+fi
+
+usage_and_fail() {
+    cat <<EOF >&2
+$1!
+
+See all profiles with \`nmcli connection\`.
+Reload files from disk with \`nmcli connection reload\`
+Deactivate the desired profile with \`nmcli connection down \"\$NAME\"\`
+EOF
+    exit 1
+}
+
+OLD_IFS="$IFS"
+
+re='^/etc/sysconfig/network-scripts/ifcfg-[^/]+$'
+for f in "/etc/sysconfig/network-scripts/ifcfg-$CONFIG" "/etc/sysconfig/network-scripts/$CONFIG" ; do
+    [[ "$f" =~ $re ]] || continue
+    [ -f "$f" ] || continue
+
+    nmcli connection load "$f" \
+    && nmcli connection down filename "$f" \
+    && exit 0
+
+    usage_and_fail "Failure to deactivate file \"$CONFIG\""
+done
+
+IFS=$'\n '
+APATHS=( $(nmcli -g GENERAL.DBUS-PATH connection show --active "$CONFIG" 2>/dev/null) )
+IFS="$OLD_IFS"
+if [ "${#APATHS[@]}" -eq 1 ] ; then
+    nmcli connection down apath "${APATHS[0]}" \
+    && exit 0
+    usage_and_fail "Failure to deactivate profile \"$CONFIG\" (apath ${APATHS[@]})"
+fi
+
+usage_and_fail "No suitable profile \"$CONFIG\" found"
diff --git a/src/core/settings/plugins/ifcfg-rh/nm-ifup b/src/core/settings/plugins/ifcfg-rh/nm-ifup
index 96637a92..6b32fe1b 100644..100755
--- a/src/core/settings/plugins/ifcfg-rh/nm-ifup
+++ b/src/core/settings/plugins/ifcfg-rh/nm-ifup
@@ -1,3 +1,44 @@
-#!/bin/sh
-nmcli connection load "/etc/sysconfig/network-scripts/ifcfg-$1" &&
-exec nmcli connection up filename "/etc/sysconfig/network-scripts/ifcfg-$1"
+#!/bin/bash
+
+CONFIG="$1"
+
+if [ -z "${CONFIG}" ] ; then
+    echo $"Usage: ifup <device name>" >&2
+    exit 1
+fi
+
+usage_and_fail() {
+    cat <<EOF >&2
+$1!
+
+See all profiles with \`nmcli connection\`.
+Reload files from disk with \`nmcli connection reload\`
+Activate the desired profile with \`nmcli connection up \"\$NAME\"\`
+EOF
+    exit 1
+}
+
+OLD_IFS="$IFS"
+
+re='^/etc/sysconfig/network-scripts/ifcfg-[^/]+$'
+for f in "/etc/sysconfig/network-scripts/ifcfg-$CONFIG" "/etc/sysconfig/network-scripts/$CONFIG" ; do
+    [[ "$f" =~ $re ]] || continue
+    [ -f "$f" ] || continue
+
+    nmcli connection load "$f" \
+    && nmcli connection up filename "$f" \
+    && exit 0
+
+    usage_and_fail "Failure to activate file \"$CONFIG\""
+done
+
+IFS=$'\n '
+UUIDS=( $(nmcli -g connection.uuid connection show "$CONFIG" 2>/dev/null) )
+IFS="$OLD_IFS"
+if [ "${#UUIDS[@]}" -eq 1 ] ; then
+    nmcli connection up uuid "${UUIDS[0]}" \
+    && exit 0
+    usage_and_fail "Failure to activate profile \"$CONFIG\" (uuid ${UUIDS[0]})"
+fi
+
+usage_and_fail "No suitable profile \"$CONFIG\" found"
diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c
index d114db0f..5038ac6a 100644
--- a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c
+++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c
@@ -13,12 +13,12 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
-#include "nm-std-aux/c-list-util.h"
-#include "nm-glib-aux/nm-c-list.h"
-#include "nm-glib-aux/nm-io-utils.h"
-#include "nm-std-aux/nm-dbus-compat.h"
+#include "libnm-std-aux/c-list-util.h"
+#include "libnm-glib-aux/nm-c-list.h"
+#include "libnm-glib-aux/nm-io-utils.h"
+#include "libnm-std-aux/nm-dbus-compat.h"
 #include "nm-utils.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "nm-config.h"
 #include "nm-dbus-manager.h"
 #include "settings/nm-settings-plugin.h"
diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
index a42c4188..03b347f9 100644
--- a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
+++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
@@ -18,7 +18,8 @@
 #include <linux/rtnetlink.h>
 #include <linux/if_ether.h>
 
-#include "nm-glib-aux/nm-secret-utils.h"
+#include "libnm-glib-aux/nm-uuid.h"
+#include "libnm-glib-aux/nm-secret-utils.h"
 #include "nm-connection.h"
 #include "nm-dbus-interface.h"
 #include "nm-setting-connection.h"
@@ -38,11 +39,11 @@
 #include "nm-setting-user.h"
 #include "nm-setting-proxy.h"
 #include "nm-setting-generic.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "nm-utils.h"
-#include "nm-base/nm-ethtool-base.h"
+#include "libnm-base/nm-ethtool-base.h"
 
-#include "platform/nm-platform.h"
+#include "libnm-platform/nm-platform.h"
 #include "NetworkManagerUtils.h"
 
 #include "nms-ifcfg-rh-common.h"
@@ -405,11 +406,9 @@ make_connection_setting(const char *file,
     /* Try for a UUID key before falling back to hashing the file name */
     uuid = svGetValueStr(ifcfg, "UUID", &uuid_free);
     if (!uuid) {
-        uuid_free = nm_utils_uuid_generate_from_string(svFileGetName(ifcfg),
-                                                       -1,
-                                                       NM_UTILS_UUID_TYPE_LEGACY,
-                                                       NULL);
-        uuid      = uuid_free;
+        uuid_free =
+            nm_uuid_generate_from_string_str(svFileGetName(ifcfg), -1, NM_UUID_TYPE_LEGACY, NULL);
+        uuid = uuid_free;
     }
 
     g_object_set(s_con,
@@ -1896,6 +1895,8 @@ make_ip4_setting(shvarFile *ifcfg,
                  svGetValueBoolean(ifcfg, "DHCP_SEND_HOSTNAME", TRUE),
                  NM_SETTING_IP_CONFIG_DHCP_TIMEOUT,
                  (int) svGetValueInt64(ifcfg, "IPV4_DHCP_TIMEOUT", 10, 0, G_MAXINT32, 0),
+                 NM_SETTING_IP_CONFIG_REQUIRED_TIMEOUT,
+                 (int) svGetValueInt64(ifcfg, "IPV4_REQUIRED_TIMEOUT", 10, 0, G_MAXINT32, -1),
                  NULL);
 
     nm_clear_g_free(&value);
@@ -1986,7 +1987,11 @@ make_ip4_setting(shvarFile *ifcfg,
                 } else if (nm_utils_ipaddr_is_valid(AF_INET6, v)) {
                     /* Ignore IPv6 addresses */
                 } else {
-                    PARSE_WARNING("invalid DNS server address %s", v);
+                    g_set_error(error,
+                                NM_SETTINGS_ERROR,
+                                NM_SETTINGS_ERROR_INVALID_CONNECTION,
+                                "Invalid DNS server address '%s'",
+                                v);
                     return NULL;
                 }
             }
@@ -2153,6 +2158,8 @@ read_aliases(NMSettingIPConfig *s_ip4, gboolean read_defroute, const char *filen
                 continue;
             }
 
+            svWarnInvalid(parsed, "alias", _NMLOG_DOMAIN);
+
             device = svGetValueStr(parsed, "DEVICE", &device_value);
             if (!device) {
                 PARSE_WARNING("alias file '%s' has no DEVICE", full_path);
@@ -2404,6 +2411,8 @@ make_ip6_setting(shvarFile *ifcfg, shvarFile *network_ifcfg, gboolean routes_rea
                  svGetValueBoolean(ifcfg, "DHCPV6_SEND_HOSTNAME", TRUE),
                  NM_SETTING_IP_CONFIG_DHCP_TIMEOUT,
                  (int) svGetValueInt64(ifcfg, "IPV6_DHCP_TIMEOUT", 10, 0, G_MAXINT32, 0),
+                 NM_SETTING_IP_CONFIG_REQUIRED_TIMEOUT,
+                 (int) svGetValueInt64(ifcfg, "IPV6_REQUIRED_TIMEOUT", 10, 0, G_MAXINT32, -1),
                  NM_SETTING_IP6_CONFIG_RA_TIMEOUT,
                  (int) svGetValueInt64(ifcfg, "IPV6_RA_TIMEOUT", 10, 0, G_MAXINT32, 0),
                  NULL);
@@ -2505,7 +2514,11 @@ make_ip6_setting(shvarFile *ifcfg, shvarFile *network_ifcfg, gboolean routes_rea
         } else if (nm_utils_ipaddr_is_valid(AF_INET, v)) {
             /* Ignore IPv4 addresses */
         } else {
-            PARSE_WARNING("invalid DNS server address %s", v);
+            g_set_error(error,
+                        NM_SETTINGS_ERROR,
+                        NM_SETTINGS_ERROR_INVALID_CONNECTION,
+                        "Invalid DNS server address '%s'",
+                        v);
             return NULL;
         }
     }
@@ -4303,7 +4316,7 @@ make_wireless_setting(shvarFile *ifcfg, GError **error)
             bytes = g_bytes_new(value, value_len);
 
         ssid_len = g_bytes_get_size(bytes);
-        if (ssid_len > 32 || ssid_len == 0) {
+        if (ssid_len == 0 || ssid_len > NM_IW_ESSID_MAX_SIZE) {
             g_set_error(error,
                         NM_SETTINGS_ERROR,
                         NM_SETTINGS_ERROR_INVALID_CONNECTION,
@@ -4679,7 +4692,9 @@ static NM_UTILS_STRING_TABLE_LOOKUP_DEFINE(
     {"--coalesce", NM_ETHTOOL_TYPE_COALESCE},
     {"--features", NM_ETHTOOL_TYPE_FEATURE},
     {"--offload", NM_ETHTOOL_TYPE_FEATURE},
+    {"--pause", NM_ETHTOOL_TYPE_PAUSE},
     {"--set-ring", NM_ETHTOOL_TYPE_RING},
+    {"-A", NM_ETHTOOL_TYPE_PAUSE},
     {"-C", NM_ETHTOOL_TYPE_COALESCE},
     {"-G", NM_ETHTOOL_TYPE_RING},
     {"-K", NM_ETHTOOL_TYPE_FEATURE}, );
@@ -4719,7 +4734,7 @@ parse_ethtool_option(const char *             value,
         w_iter = &words[2];
 
         while (w_iter && *w_iter) {
-            if (ethtool_type == NM_ETHTOOL_TYPE_FEATURE) {
+            if (NM_IN_SET(ethtool_type, NM_ETHTOOL_TYPE_FEATURE, NM_ETHTOOL_TYPE_PAUSE)) {
                 w_iter = _next_ethtool_options_nmternary(w_iter, ethtool_type, &ifcfg_option);
 
                 if (ifcfg_option.has_value) {
@@ -5022,6 +5037,7 @@ make_wired_setting(shvarFile *ifcfg, const char *file, NMSetting8021x **s_8021x,
     const char *                    cvalue;
     gs_free char *                  value = NULL;
     gboolean                        found = FALSE;
+    NMTernary                       accept_all_mac_addresses;
 
     s_wired = NM_SETTING_WIRED(nm_setting_wired_new());
 
@@ -5119,15 +5135,15 @@ make_wired_setting(shvarFile *ifcfg, const char *file, NMSetting8021x **s_8021x,
         for (i = 0; options && options[i]; i++) {
             const char *line = options[i];
             const char *equals;
-            gboolean    valid = FALSE;
 
             equals = strchr(line, '=');
-            if (equals) {
-                ((char *) equals)[0] = '\0';
-                valid                = nm_setting_wired_add_s390_option(s_wired, line, equals + 1);
-            }
-            if (!valid)
-                PARSE_WARNING("invalid s390 OPTION '%s'", line);
+            if (!equals)
+                continue;
+
+            /* Here we don't verify the key/value further. If the file contains invalid keys,
+             * we will later reject the connection as invalid. */
+            ((char *) equals)[0] = '\0';
+            nm_setting_wired_add_s390_option(s_wired, line, equals + 1);
         }
         found = TRUE;
     }
@@ -5180,6 +5196,15 @@ make_wired_setting(shvarFile *ifcfg, const char *file, NMSetting8021x **s_8021x,
     }
     nm_clear_g_free(&value);
 
+    accept_all_mac_addresses = svGetValueTernary(ifcfg, "ACCEPT_ALL_MAC_ADDRESSES");
+    if (accept_all_mac_addresses != NM_TERNARY_DEFAULT) {
+        g_object_set(s_wired,
+                     NM_SETTING_WIRED_ACCEPT_ALL_MAC_ADDRESSES,
+                     accept_all_mac_addresses,
+                     NULL);
+        found = TRUE;
+    }
+
     if (!found) {
         g_set_error(error,
                     NM_UTILS_ERROR,
@@ -6072,15 +6097,14 @@ make_vlan_setting(shvarFile *ifcfg, const char *file, GError **error)
                 v = iface_name + 4;
         }
 
-        if (v) {
-            int device_vlan_id;
-
-            /* Grab VLAN ID from interface name; this takes precedence over the
-             * separate VLAN_ID property for backwards compat.
+        if (vlan_id == -1 && v) {
+            /* Grab VLAN ID from interface name; The explicit VLAN_ID option takes precedence
+             * over detecting the ID based on PHYSDEV.
+             *
+             * Note that older versions of NetworkManager had a bug and this would overwrite the
+             * VLAN_ID in this case.
              */
-            device_vlan_id = _nm_utils_ascii_str_to_int64(v, 10, 0, 4095, -1);
-            if (device_vlan_id != -1)
-                vlan_id = device_vlan_id;
+            vlan_id = _nm_utils_ascii_str_to_int64(v, 10, 0, 4095, -1);
         }
     }
 
@@ -6288,6 +6312,7 @@ connection_from_file_full(const char *filename,
     NMSetting *                   s_ip4;
     NMSetting *                   s_ip6;
     const char *                  ifcfg_name       = NULL;
+    gs_free char *                s_tmp            = NULL;
     gboolean                      has_ip4_defroute = FALSE;
     gboolean                      has_complex_routes_v4;
     gboolean                      has_complex_routes_v6;
@@ -6315,8 +6340,6 @@ connection_from_file_full(const char *filename,
     if (!main_ifcfg)
         return NULL;
 
-    network_ifcfg = svOpenFile(network_file, NULL);
-
     if (!svGetValueBoolean(main_ifcfg, "NM_CONTROLLED", TRUE)) {
         connection = create_unhandled_connection(filename, main_ifcfg, "unmanaged", out_unhandled);
         if (!connection) {
@@ -6330,6 +6353,16 @@ connection_from_file_full(const char *filename,
         return g_steal_pointer(&connection);
     }
 
+    if (NM_IN_STRSET(svGetValueStr(main_ifcfg, "DEVICE", &s_tmp), "lo")) {
+        /* "lo" is not handled by NetworkManager and we ignore it. */
+    } else
+        svWarnInvalid(main_ifcfg, "ifcfg", _NMLOG_DOMAIN);
+    nm_clear_g_free(&s_tmp);
+
+    network_ifcfg = svOpenFile(network_file, NULL);
+    /* we don't call svWarnInvalid(network_ifcfg), because we will load this file for
+     * every profile. So we would get a large number of duplicate warnings. */
+
     /* iBFT is handled by nm-initrd-generator during boot. */
     bootproto = svGetValueStr_cp(main_ifcfg, "BOOTPROTO");
     if (bootproto && !g_ascii_strcasecmp(bootproto, "ibft")) {
diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.c b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.c
index 3f674dea..134bdf68 100644
--- a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.c
+++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.c
@@ -7,8 +7,9 @@
 
 #include "nms-ifcfg-rh-storage.h"
 
+#include "libnm-glib-aux/nm-uuid.h"
 #include "nm-utils.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "nm-connection.h"
 #include "nms-ifcfg-rh-plugin.h"
 
@@ -88,7 +89,7 @@ static NMSIfcfgRHStorage *
 _storage_new(NMSIfcfgRHPlugin *plugin, const char *uuid, const char *filename)
 {
     nm_assert(NMS_IS_IFCFG_RH_PLUGIN(plugin));
-    nm_assert(!uuid || nm_utils_is_uuid(uuid));
+    nm_assert(!uuid || nm_uuid_is_normalized(uuid));
     nm_assert(filename && filename[0] == '/');
 
     return g_object_new(NMS_TYPE_IFCFG_RH_STORAGE,
diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c
index ada1942a..febfc120 100644
--- a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c
+++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c
@@ -9,7 +9,7 @@
 
 #include <stdlib.h>
 
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "NetworkManagerUtils.h"
 
 #include "nms-ifcfg-rh-common.h"
@@ -593,6 +593,9 @@ const char *const _nm_ethtool_ifcfg_names[] = {
     ETHT_NAME(NM_ETHTOOL_ID_FEATURE_TX_UDP_TNL_CSUM_SEGMENTATION, "tx-udp_tnl-csum-segmentation"),
     ETHT_NAME(NM_ETHTOOL_ID_FEATURE_TX_UDP_TNL_SEGMENTATION, "tx-udp_tnl-segmentation"),
     ETHT_NAME(NM_ETHTOOL_ID_FEATURE_TX_VLAN_STAG_HW_INSERT, "tx-vlan-stag-hw-insert"),
+    ETHT_NAME(NM_ETHTOOL_ID_PAUSE_AUTONEG, "pause-autoneg"),
+    ETHT_NAME(NM_ETHTOOL_ID_PAUSE_RX, "pause-rx"),
+    ETHT_NAME(NM_ETHTOOL_ID_PAUSE_TX, "pause-tx"),
     ETHT_NAME(NM_ETHTOOL_ID_RING_RX, "rx"),
     ETHT_NAME(NM_ETHTOOL_ID_RING_RX_JUMBO, "rx-jumbo"),
     ETHT_NAME(NM_ETHTOOL_ID_RING_RX_MINI, "rx-mini"),
@@ -722,6 +725,15 @@ static NM_UTILS_STRING_TABLE_LOOKUP_DEFINE(
     {"rx-mini", NM_ETHTOOL_ID_RING_RX_MINI},
     {"tx", NM_ETHTOOL_ID_RING_TX}, );
 
+static NM_UTILS_STRING_TABLE_LOOKUP_DEFINE(
+    _get_ethtoolid_pause_by_name,
+    NMEthtoolID,
+    { nm_assert(name); },
+    { return NM_ETHTOOL_ID_UNKNOWN; },
+    {"pause-autoneg", NM_ETHTOOL_ID_PAUSE_AUTONEG},
+    {"pause-rx", NM_ETHTOOL_ID_PAUSE_RX},
+    {"pause-tx", NM_ETHTOOL_ID_PAUSE_TX}, );
+
 const NMEthtoolData *
 nms_ifcfg_rh_utils_get_ethtool_by_name(const char *name, NMEthtoolType ethtool_type)
 {
@@ -737,6 +749,9 @@ nms_ifcfg_rh_utils_get_ethtool_by_name(const char *name, NMEthtoolType ethtool_t
     case NM_ETHTOOL_TYPE_RING:
         id = _get_ethtoolid_ring_by_name(name);
         break;
+    case NM_ETHTOOL_TYPE_PAUSE:
+        id = _get_ethtoolid_pause_by_name(name);
+        break;
     default:
         nm_assert_not_reached();
         return NULL;
@@ -800,6 +815,7 @@ nms_ifcfg_rh_utils_is_numbered_tag_impl(const char *key,
     }
 
 const NMSIfcfgKeyTypeInfo nms_ifcfg_well_known_keys[] = {
+    _KEY_TYPE("ACCEPT_ALL_MAC_ADDRESSES", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
     _KEY_TYPE("ACD_TIMEOUT", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
     _KEY_TYPE("ADDRESS", NMS_IFCFG_KEY_TYPE_IS_NUMBERED),
     _KEY_TYPE("AP_ISOLATION", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
@@ -942,6 +958,7 @@ const NMSIfcfgKeyTypeInfo nms_ifcfg_well_known_keys[] = {
     _KEY_TYPE("IPV4_DHCP_TIMEOUT", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
     _KEY_TYPE("IPV4_DNS_PRIORITY", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
     _KEY_TYPE("IPV4_FAILURE_FATAL", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
+    _KEY_TYPE("IPV4_REQUIRED_TIMEOUT", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
     _KEY_TYPE("IPV4_ROUTE_METRIC", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
     _KEY_TYPE("IPV4_ROUTE_TABLE", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
     _KEY_TYPE("IPV6ADDR", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
@@ -964,6 +981,7 @@ const NMSIfcfgKeyTypeInfo nms_ifcfg_well_known_keys[] = {
     _KEY_TYPE("IPV6_PRIVACY", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
     _KEY_TYPE("IPV6_PRIVACY_PREFER_PUBLIC_IP", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
     _KEY_TYPE("IPV6_RA_TIMEOUT", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
+    _KEY_TYPE("IPV6_REQUIRED_TIMEOUT", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
     _KEY_TYPE("IPV6_RES_OPTIONS", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
     _KEY_TYPE("IPV6_ROUTE_METRIC", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
     _KEY_TYPE("IPV6_ROUTE_TABLE", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h
index 04a1b63d..b7751ec9 100644
--- a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h
+++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h
@@ -7,7 +7,7 @@
 #define _UTILS_H_
 
 #include "nm-connection.h"
-#include "nm-base/nm-ethtool-base.h"
+#include "libnm-base/nm-ethtool-base.h"
 
 #include "shvar.h"
 
@@ -33,7 +33,7 @@ typedef struct {
     NMSIfcfgKeyTypeFlags key_flags;
 } NMSIfcfgKeyTypeInfo;
 
-extern const NMSIfcfgKeyTypeInfo nms_ifcfg_well_known_keys[248];
+extern const NMSIfcfgKeyTypeInfo nms_ifcfg_well_known_keys[251];
 
 const NMSIfcfgKeyTypeInfo *nms_ifcfg_well_known_key_find_info(const char *key, gssize *out_idx);
 
diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
index 45b76456..ef85b6bf 100644
--- a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
+++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
@@ -15,8 +15,9 @@
 #include <unistd.h>
 #include <stdio.h>
 
-#include "nm-glib-aux/nm-enum-utils.h"
-#include "nm-glib-aux/nm-io-utils.h"
+#include "libnm-glib-aux/nm-enum-utils.h"
+#include "libnm-glib-aux/nm-str-buf.h"
+#include "libnm-glib-aux/nm-io-utils.h"
 #include "nm-manager.h"
 #include "nm-setting-connection.h"
 #include "nm-setting-wired.h"
@@ -32,9 +33,9 @@
 #include "nm-setting-team.h"
 #include "nm-setting-team-port.h"
 #include "nm-utils.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "NetworkManagerUtils.h"
-#include "nm-base/nm-ethtool-base.h"
+#include "libnm-base/nm-ethtool-base.h"
 
 #include "nms-ifcfg-rh-common.h"
 #include "nms-ifcfg-rh-reader.h"
@@ -203,18 +204,18 @@ write_object(NMSetting8021x *                s_8021x,
     g_return_val_if_fail(ifcfg != NULL, FALSE);
     g_return_val_if_fail(objtype != NULL, FALSE);
 
-    scheme = (*(objtype->vtable->scheme_func))(s_8021x);
+    scheme = (*(objtype->vtable->scheme_func)) (s_8021x);
     switch (scheme) {
     case NM_SETTING_802_1X_CK_SCHEME_UNKNOWN:
         break;
     case NM_SETTING_802_1X_CK_SCHEME_BLOB:
-        blob = (*(objtype->vtable->blob_func))(s_8021x);
+        blob = (*(objtype->vtable->blob_func)) (s_8021x);
         break;
     case NM_SETTING_802_1X_CK_SCHEME_PATH:
-        value = (*(objtype->vtable->path_func))(s_8021x);
+        value = (*(objtype->vtable->path_func)) (s_8021x);
         break;
     case NM_SETTING_802_1X_CK_SCHEME_PKCS11:
-        value = (*(objtype->vtable->uri_func))(s_8021x);
+        value = (*(objtype->vtable->uri_func)) (s_8021x);
         break;
     default:
         g_set_error(error,
@@ -227,8 +228,8 @@ write_object(NMSetting8021x *                s_8021x,
     /* Set the password for certificate/private key. */
     nm_sprintf_buf(secret_name, "%s_PASSWORD", objtype->ifcfg_rh_key);
     nm_sprintf_buf(secret_flags, "%s_PASSWORD_FLAGS", objtype->ifcfg_rh_key);
-    password = (*(objtype->vtable->passwd_func))(s_8021x);
-    flags    = (*(objtype->vtable->pwflag_func))(s_8021x);
+    password = (*(objtype->vtable->passwd_func)) (s_8021x);
+    flags    = (*(objtype->vtable->pwflag_func)) (s_8021x);
     set_secret(ifcfg, secrets, secret_name, password, secret_flags, flags);
 
     if (!objtype->vtable->format_func)
@@ -299,6 +300,7 @@ write_blobs(GHashTable *blobs, GError **error)
                                         g_bytes_get_size(blob),
                                         0600,
                                         NULL,
+                                        NULL,
                                         &write_error)) {
             g_set_error(error,
                         NM_SETTINGS_ERROR,
@@ -862,7 +864,7 @@ write_wireless_setting(NMConnection *connection,
         return FALSE;
     }
     ssid_data = g_bytes_get_data(ssid, &ssid_len);
-    if (!ssid_len || ssid_len > 32) {
+    if (ssid_len == 0 || ssid_len > NM_IW_ESSID_MAX_SIZE) {
         g_set_error(error,
                     NM_SETTINGS_ERROR,
                     NM_SETTINGS_ERROR_FAILED,
@@ -903,9 +905,9 @@ write_wireless_setting(NMConnection *connection,
         svSetValueStr(ifcfg, "ESSID", str->str);
         g_string_free(str, TRUE);
     } else {
-        char buf[33];
+        char buf[NM_IW_ESSID_MAX_SIZE + 1];
 
-        nm_assert(ssid_len <= 32);
+        nm_assert(ssid_len <= NM_IW_ESSID_MAX_SIZE);
         memcpy(buf, ssid_data, ssid_len);
         buf[ssid_len] = '\0';
         svSetValueStr(ifcfg, "ESSID", buf);
@@ -1075,25 +1077,18 @@ write_hostname_setting(NMConnection *connection, shvarFile *ifcfg)
     svSetValueInt64_cond(ifcfg, "HOSTNAME_ONLY_FROM_DEFAULT", t != NM_TERNARY_DEFAULT, t);
 }
 
-static gboolean
-write_wired_setting(NMConnection *connection, shvarFile *ifcfg, GError **error)
+static void
+write_wired_setting_impl(NMSettingWired *s_wired, shvarFile *ifcfg, gboolean is_virtual)
 {
-    NMSettingWired *   s_wired;
     const char *const *s390_subchannels;
-    guint32            mtu, num_opts, i;
+    guint32            mtu;
+    guint32            num_opts;
+    guint32            i;
     const char *const *macaddr_blacklist;
 
-    s_wired = nm_connection_get_setting_wired(connection);
-    if (!s_wired) {
-        g_set_error(error,
-                    NM_SETTINGS_ERROR,
-                    NM_SETTINGS_ERROR_FAILED,
-                    "Missing '%s' setting",
-                    NM_SETTING_WIRED_SETTING_NAME);
-        return FALSE;
-    }
-
-    svSetValueStr(ifcfg, "HWADDR", nm_setting_wired_get_mac_address(s_wired));
+    svSetValue(ifcfg,
+               "HWADDR",
+               nm_setting_wired_get_mac_address(s_wired) ?: (is_virtual ? "" : NULL));
 
     svSetValueStr(ifcfg, "MACADDR", nm_setting_wired_get_cloned_mac_address(s_wired));
 
@@ -1167,8 +1162,42 @@ write_wired_setting(NMConnection *connection, shvarFile *ifcfg, GError **error)
             svSetValueStr(ifcfg, "OPTIONS", tmp->str);
     }
 
+    svSetValueTernary(ifcfg,
+                      "ACCEPT_ALL_MAC_ADDRESSES",
+                      nm_setting_wired_get_accept_all_mac_addresses(s_wired));
+}
+
+static gboolean
+write_wired_setting(NMConnection *connection, shvarFile *ifcfg, GError **error)
+{
+    NMSettingWired *s_wired;
+
+    s_wired = nm_connection_get_setting_wired(connection);
+    if (!s_wired) {
+        g_set_error(error,
+                    NM_SETTINGS_ERROR,
+                    NM_SETTINGS_ERROR_FAILED,
+                    "Missing '%s' setting",
+                    NM_SETTING_WIRED_SETTING_NAME);
+        return FALSE;
+    }
+
     svSetValueStr(ifcfg, "TYPE", TYPE_ETHERNET);
 
+    write_wired_setting_impl(s_wired, ifcfg, FALSE);
+    return TRUE;
+}
+
+static gboolean
+write_wired_for_virtual(NMConnection *connection, shvarFile *ifcfg)
+{
+    NMSettingWired *s_wired;
+
+    s_wired = nm_connection_get_setting_wired(connection);
+    if (!s_wired)
+        return FALSE;
+
+    write_wired_setting_impl(s_wired, ifcfg, TRUE);
     return TRUE;
 }
 
@@ -1334,6 +1363,26 @@ write_ethtool_setting(NMConnection *connection, shvarFile *ifcfg, GError **error
             g_string_append(str, nms_ifcfg_rh_utils_get_ethtool_name(ethtool_id));
             g_string_append_printf(str, " %" G_GUINT32_FORMAT, u32);
         }
+        for (ethtool_id = _NM_ETHTOOL_ID_PAUSE_FIRST; ethtool_id <= _NM_ETHTOOL_ID_PAUSE_LAST;
+             ethtool_id++) {
+            nm_assert(nms_ifcfg_rh_utils_get_ethtool_name(ethtool_id));
+            if (!nm_setting_option_get_boolean(NM_SETTING(s_ethtool),
+                                               nm_ethtool_data[ethtool_id]->optname,
+                                               &b))
+                continue;
+
+            _ethtool_gstring_prepare(&str, &is_first, 'A', iface);
+            g_string_append_c(str, ' ');
+            g_string_append(str, nms_ifcfg_rh_utils_get_ethtool_name(ethtool_id));
+            g_string_append(str, b ? " on" : " off");
+        }
+
+        if (!str) {
+            /* Write an empty dummy "-A" option without arguments. This is to
+             * ensure that the reader will create an (all default) NMSettingEthtool.
+             * Also, it seems that `ethtool -A "$IFACE"` is silently accepted. */
+            _ethtool_gstring_prepare(&str, &is_first, 'A', iface);
+        }
     }
 
     if (str) {
@@ -1367,35 +1416,6 @@ vlan_priority_maplist_to_stringlist(NMSettingVlan *s_vlan, NMVlanPriorityMap map
 }
 
 static gboolean
-write_wired_for_virtual(NMConnection *connection, shvarFile *ifcfg)
-{
-    NMSettingWired *s_wired;
-    gboolean        has_wired = FALSE;
-
-    s_wired = nm_connection_get_setting_wired(connection);
-    if (s_wired) {
-        const char *device_mac, *cloned_mac;
-        guint32     mtu;
-
-        has_wired = TRUE;
-
-        device_mac = nm_setting_wired_get_mac_address(s_wired);
-        svSetValue(ifcfg, "HWADDR", device_mac ?: "");
-
-        cloned_mac = nm_setting_wired_get_cloned_mac_address(s_wired);
-        svSetValueStr(ifcfg, "MACADDR", cloned_mac);
-
-        svSetValueStr(ifcfg,
-                      "GENERATE_MAC_ADDRESS_MASK",
-                      nm_setting_wired_get_generate_mac_address_mask(s_wired));
-
-        mtu = nm_setting_wired_get_mtu(s_wired);
-        svSetValueInt64_cond(ifcfg, "MTU", mtu != 0, mtu);
-    }
-    return has_wired;
-}
-
-static gboolean
 write_vlan_setting(NMConnection *connection, shvarFile *ifcfg, gboolean *wired, GError **error)
 {
     NMSettingVlan *s_vlan;
@@ -1542,36 +1562,40 @@ write_team_setting(NMConnection *connection, shvarFile *ifcfg, gboolean *wired,
 
 static gboolean
 write_bridge_vlans(NMSetting * setting,
-                   const char *property_name,
+                   gboolean    is_port,
                    shvarFile * ifcfg,
                    const char *key,
                    GError **   error)
 {
-    gs_unref_ptrarray GPtrArray *vlans = NULL;
-    NMBridgeVlan *               vlan;
-    GString *                    string;
-    guint                        i;
+    GPtrArray *              vlans;
+    NMBridgeVlan *           vlan;
+    nm_auto_str_buf NMStrBuf strbuf = NM_STR_BUF_INIT(0, FALSE);
+    guint                    i;
 
-    g_object_get(setting, property_name, &vlans, NULL);
+    if (is_port)
+        vlans = _nm_setting_bridge_port_get_vlans(NM_SETTING_BRIDGE_PORT(setting));
+    else
+        vlans = _nm_setting_bridge_get_vlans(NM_SETTING_BRIDGE(setting));
 
     if (!vlans || !vlans->len)
         return TRUE;
 
-    string = g_string_new("");
     for (i = 0; i < vlans->len; i++) {
         gs_free char *vlan_str = NULL;
 
         vlan     = vlans->pdata[i];
         vlan_str = nm_bridge_vlan_to_str(vlan, error);
-        if (!vlan_str)
-            return FALSE;
-        if (string->len > 0)
-            g_string_append(string, ",");
-        nm_utils_escaped_tokens_escape_gstr_assert(vlan_str, ",", string);
+        if (!vlan_str) {
+            /* nm_bridge_vlan_to_str() cannot fail (for now). */
+            nm_assert_not_reached();
+            continue;
+        }
+        if (strbuf.len > 0)
+            nm_str_buf_append_c(&strbuf, ',');
+        nm_str_buf_append(&strbuf, nm_utils_escaped_tokens_escape_unnecessary(vlan_str, ","));
     }
 
-    svSetValueStr(ifcfg, key, string->str);
-    g_string_free(string, TRUE);
+    svSetValueStr(ifcfg, key, nm_str_buf_get_str(&strbuf));
     return TRUE;
 }
 
@@ -1807,11 +1831,7 @@ write_bridge_setting(NMConnection *connection, shvarFile *ifcfg, gboolean *wired
         svSetValueStr(ifcfg, "BRIDGING_OPTS", opts->str);
     g_string_free(opts, TRUE);
 
-    if (!write_bridge_vlans((NMSetting *) s_bridge,
-                            NM_SETTING_BRIDGE_VLANS,
-                            ifcfg,
-                            "BRIDGE_VLANS",
-                            error))
+    if (!write_bridge_vlans((NMSetting *) s_bridge, FALSE, ifcfg, "BRIDGE_VLANS", error))
         return FALSE;
 
     svSetValueStr(ifcfg, "TYPE", TYPE_BRIDGE);
@@ -1862,11 +1882,7 @@ write_bridge_port_setting(NMConnection *connection, shvarFile *ifcfg, GError **e
         svSetValueStr(ifcfg, "BRIDGING_OPTS", string->str);
     g_string_free(string, TRUE);
 
-    if (!write_bridge_vlans((NMSetting *) s_port,
-                            NM_SETTING_BRIDGE_PORT_VLANS,
-                            ifcfg,
-                            "BRIDGE_PORT_VLANS",
-                            error))
+    if (!write_bridge_vlans((NMSetting *) s_port, TRUE, ifcfg, "BRIDGE_PORT_VLANS", error))
         return FALSE;
 
     return TRUE;
@@ -2824,6 +2840,9 @@ write_ip4_setting(NMConnection *connection,
     timeout = nm_setting_ip_config_get_dhcp_timeout(s_ip4);
     svSetValueInt64_cond(ifcfg, "IPV4_DHCP_TIMEOUT", timeout != 0, timeout);
 
+    timeout = nm_setting_ip_config_get_required_timeout(s_ip4);
+    svSetValueInt64_cond(ifcfg, "IPV4_REQUIRED_TIMEOUT", timeout != -1, timeout);
+
     svSetValueBoolean(ifcfg, "IPV4_FAILURE_FATAL", !nm_setting_ip_config_get_may_fail(s_ip4));
 
     route_metric = nm_setting_ip_config_get_route_metric(s_ip4);
@@ -3021,6 +3040,9 @@ write_ip6_setting(NMConnection *connection,
     timeout = nm_setting_ip_config_get_dhcp_timeout(s_ip6);
     svSetValueInt64_cond(ifcfg, "IPV6_DHCP_TIMEOUT", timeout != 0, timeout);
 
+    timeout = nm_setting_ip_config_get_required_timeout(s_ip6);
+    svSetValueInt64_cond(ifcfg, "IPV6_REQUIRED_TIMEOUT", timeout != -1, timeout);
+
     flags = nm_setting_ip_config_get_dhcp_hostname_flags(s_ip6);
     svSetValueInt64_cond(ifcfg,
                          "DHCPV6_HOSTNAME_FLAGS",
diff --git a/src/core/settings/plugins/ifcfg-rh/shvar.c b/src/core/settings/plugins/ifcfg-rh/shvar.c
index 386bcac7..d82efb3c 100644
--- a/src/core/settings/plugins/ifcfg-rh/shvar.c
+++ b/src/core/settings/plugins/ifcfg-rh/shvar.c
@@ -14,10 +14,11 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
-#include "nm-core-internal.h"
+#include "libnm-glib-aux/nm-str-buf.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "nm-core-utils.h"
-#include "nm-glib-aux/nm-enum-utils.h"
-#include "nm-glib-aux/nm-io-utils.h"
+#include "libnm-glib-aux/nm-enum-utils.h"
+#include "libnm-glib-aux/nm-io-utils.h"
 #include "c-list/src/c-list.h"
 #include "nms-ifcfg-rh-utils.h"
 
@@ -152,17 +153,42 @@ _escape_ansic(const char *source)
     const char *p;
     char *      dest;
     char *      q;
+    gsize       n_alloc;
 
     nm_assert(source);
 
-    p = (const char *) source;
-    /* Each source byte needs maximally four destination chars (\777) */
-    q = dest = g_malloc(strlen(source) * 4 + 1 + 3);
+    n_alloc = 4;
+    for (p = source; p[0]; p++) {
+        switch (*p) {
+        case '\b':
+        case '\f':
+        case '\n':
+        case '\r':
+        case '\t':
+        case '\v':
+        case '\\':
+        case '"':
+        case '\'':
+            n_alloc += 2;
+            break;
+        default:
+            if (!nm_ascii_is_regular(*p))
+                n_alloc += 4;
+            else
+                n_alloc += 1;
+            break;
+        }
+    }
+
+    dest = g_malloc(n_alloc);
+
+    q = dest;
 
     *q++ = '$';
     *q++ = '\'';
 
-    while (*p) {
+    for (p = source; p[0]; p++) {
+        nm_assert(q < &dest[n_alloc]);
         switch (*p) {
         case '\b':
             *q++ = '\\';
@@ -195,7 +221,7 @@ _escape_ansic(const char *source)
             *q++ = *p;
             break;
         default:
-            if ((*p < ' ') || (*p >= 0177)) {
+            if (!nm_ascii_is_regular(*p)) {
                 *q++ = '\\';
                 *q++ = '0' + (((*p) >> 6) & 07);
                 *q++ = '0' + (((*p) >> 3) & 07);
@@ -204,12 +230,11 @@ _escape_ansic(const char *source)
                 *q++ = *p;
             break;
         }
-        p++;
     }
     *q++ = '\'';
     *q++ = '\0';
 
-    nm_assert(q - dest <= strlen(source) * 4 + 1 + 3);
+    nm_assert(q - dest == n_alloc);
 
     return dest;
 }
@@ -217,7 +242,7 @@ _escape_ansic(const char *source)
 /*****************************************************************************/
 
 #define _char_req_escape(ch)     NM_IN_SET(ch, '"', '\\', '$', '`')
-#define _char_req_escape_old(ch) NM_IN_SET(ch, '"', '\\', '\'', '$', '`', '~')
+#define _char_req_escape_old(ch) NM_IN_SET(ch, '"', '\\', '$', '`', '\'', '~')
 #define _char_req_quotes(ch)     NM_IN_SET(ch, ' ', '\'', '~', '\t', '|', '&', ';', '(', ')', '<', '>')
 
 const char *
@@ -226,29 +251,48 @@ svEscape(const char *s, char **to_free)
     char *new;
     gsize    mangle          = 0;
     gboolean requires_quotes = FALSE;
-    int      newlen;
-    size_t   i, j, slen;
+    gsize    n_alloc;
+    gsize    slen;
+    gsize    i;
+    gsize    j;
+    gboolean all_ascii = TRUE;
 
     for (slen = 0; s[slen]; slen++) {
         if (_char_req_escape(s[slen]))
             mangle++;
         else if (_char_req_quotes(s[slen]))
             requires_quotes = TRUE;
-        else if (s[slen] < ' ') {
-            /* if the string contains newline we can only express it using ANSI C quotation
-             * (as we don't support line continuation).
-             * Additionally, ANSI control characters look odd with regular quotation, so handle
-             * them too. */
-            return (*to_free = _escape_ansic(s));
+        else if (!nm_ascii_is_regular(s[slen])) {
+            if (nm_ascii_is_ctrl_or_del(s[slen])) {
+                /* if the string contains newline we can only express it using ANSI C quotation
+                 * (as we don't support line continuation).
+                 * Additionally, ANSI control characters look odd with regular quotation, so handle
+                 * them too. */
+                return (*to_free = _escape_ansic(s));
+            }
+            all_ascii       = FALSE;
+            requires_quotes = TRUE;
         }
     }
+
+    if (!all_ascii && !g_utf8_validate(s, -1, NULL)) {
+        /* The string is not valid ASCII/UTF-8. We can escape that via
+         * _escape_ansic(), however the reader might have a problem to
+         * do something sensible with the blob later.
+         *
+         * This is really a bug of the caller, which should not present us with
+         * non-text in the first place. But at this place, we cannot handle the
+         * error better, so just escape it. */
+        return (*to_free = _escape_ansic(s));
+    }
+
     if (!mangle && !requires_quotes) {
         *to_free = NULL;
         return s;
     }
 
-    newlen = slen + mangle + 3; /* 3 is extra ""\0 */
-    new    = g_malloc(newlen);
+    n_alloc = slen + mangle + 3; /* 3 is extra ""\0 */
+    new     = g_malloc(n_alloc);
 
     j        = 0;
     new[j++] = '"';
@@ -260,7 +304,7 @@ svEscape(const char *s, char **to_free)
     new[j++] = '"';
     new[j++] = '\0';
 
-    nm_assert(j == slen + mangle + 3);
+    nm_assert(j == n_alloc);
 
     *to_free = new;
     return new;
@@ -318,14 +362,14 @@ _ch_hex_get(char ch)
 }
 
 static void
-_gstr_init(GString **str, const char *value, gsize i)
+_strbuf_init(NMStrBuf *str, const char *value, gsize i)
 {
     nm_assert(str);
     nm_assert(value);
 
-    if (!(*str)) {
-        /* if @str is not yet initialized, it allocates
-         * a new GString and copies @i characters from
+    if (str->allocated == 0) {
+        /* if @str is not yet initialized, it initializes
+         * a new NMStrBuf and copies @i characters from
          * @value over.
          *
          * Unescaping usually does not extend the length of a string,
@@ -335,20 +379,26 @@ _gstr_init(GString **str, const char *value, gsize i)
          * (FACTOR*strlen(value) + CONST), which is non trivial to get
          * right in all cases. Also, we would have to provision for the
          * very unlikely extreme case.
-         * Instead, use a GString buffer which can grow as needed. But for an
+         * Instead, use a NMStrBuf buffer which can grow as needed. But for an
          * initial guess, strlen(value) is a good start */
-        *str = g_string_new_len(NULL, strlen(value) + 3);
-        if (i)
-            g_string_append_len(*str, value, i);
+        nm_str_buf_maybe_expand(str, strlen(value) + 3u, FALSE);
+        nm_str_buf_append_len(str, value, i);
     }
 }
 
 const char *
 svUnescape(const char *value, char **to_free)
 {
-    gsize    i, j;
-    GString *str                      = NULL;
+    return svUnescape_full(value, to_free, TRUE);
+}
+
+const char *
+svUnescape_full(const char *value, char **to_free, gboolean check_utf8)
+{
+    NMStrBuf str                      = NM_STR_BUF_INIT(0, FALSE);
     int      looks_like_old_svescaped = -1;
+    gsize    i;
+    gsize    j;
 
     /* we handle bash syntax here (note that ifup has #!/bin/bash.
      * Thus, see https://www.gnu.org/software/bash/manual/html_node/Quoting.html#Quoting */
@@ -395,20 +445,20 @@ svUnescape(const char *value, char **to_free)
 
         if (value[i] == '\\') {
             /* backslash escape */
-            _gstr_init(&str, value, i);
+            _strbuf_init(&str, value, i);
             i++;
             if (G_UNLIKELY(value[i] == '\0')) {
                 /* we don't support line continuation */
                 goto out_error;
             }
-            g_string_append_c(str, value[i]);
+            nm_str_buf_append_c(&str, value[i]);
             i++;
             goto loop1_next;
         }
 
         if (value[i] == '\'') {
             /* single quotes */
-            _gstr_init(&str, value, i);
+            _strbuf_init(&str, value, i);
             i++;
             j = i;
             while (TRUE) {
@@ -420,14 +470,14 @@ svUnescape(const char *value, char **to_free)
                     break;
                 j++;
             }
-            g_string_append_len(str, &value[i], j - i);
+            nm_str_buf_append_len(&str, &value[i], j - i);
             i = j + 1;
             goto loop1_next;
         }
 
         if (value[i] == '"') {
             /* double quotes */
-            _gstr_init(&str, value, i);
+            _strbuf_init(&str, value, i);
             i++;
             while (TRUE) {
                 if (value[i] == '"') {
@@ -466,11 +516,11 @@ svUnescape(const char *value, char **to_free)
                         if (looks_like_old_svescaped < 0)
                             looks_like_old_svescaped = _looks_like_old_svescaped(value);
                         if (!looks_like_old_svescaped)
-                            g_string_append_c(str, '\\');
+                            nm_str_buf_append_c(&str, '\\');
                     } else
-                        g_string_append_c(str, '\\');
+                        nm_str_buf_append_c(&str, '\\');
                 }
-                g_string_append_c(str, value[i]);
+                nm_str_buf_append_c(&str, value[i]);
                 i++;
             }
             goto loop1_next;
@@ -478,7 +528,7 @@ svUnescape(const char *value, char **to_free)
 
         if (value[i] == '$' && value[i + 1] == '\'') {
             /* ANSI-C Quoting */
-            _gstr_init(&str, value, i);
+            _strbuf_init(&str, value, i);
             i += 2;
             while (TRUE) {
                 char ch;
@@ -552,7 +602,7 @@ svUnescape(const char *value, char **to_free)
                                 }
                             }
                             /* like bash, we cut too large numbers off. E.g. A=$'\772' becomes 0xfa  */
-                            g_string_append_c(str, (guint8) v);
+                            nm_str_buf_append_c(&str, (guint8) v);
                         } else if (NM_IN_SET(value[i], 'x', 'u', 'U')) {
                             const char escape_type = value[i];
                             int max_digits = escape_type == 'x' ? 2 : escape_type == 'u' ? 4 : 8;
@@ -561,8 +611,7 @@ svUnescape(const char *value, char **to_free)
                             i++;
                             if (!_ch_hex_is(value[i])) {
                                 /* missing hex value after "\x" escape. This is treated like no escaping. */
-                                g_string_append_c(str, '\\');
-                                g_string_append_c(str, escape_type);
+                                nm_str_buf_append_c(&str, '\\', escape_type);
                             } else {
                                 v = _ch_hex_get(value[i]);
                                 i++;
@@ -574,22 +623,21 @@ svUnescape(const char *value, char **to_free)
                                     i++;
                                 }
                                 if (escape_type == 'x')
-                                    g_string_append_c(str, v);
+                                    nm_str_buf_append_c(&str, v);
                                 else {
                                     /* we treat the unicode escapes as utf-8 encoded values. */
-                                    g_string_append_unichar(str, v);
+                                    nm_str_buf_append_unichar(&str, v);
                                 }
                             }
                         } else {
-                            g_string_append_c(str, '\\');
-                            g_string_append_c(str, value[i]);
+                            nm_str_buf_append_c(&str, '\\', value[i]);
                             i++;
                         }
                         goto loop_ansic_next;
                     }
                 } else
                     ch = value[i];
-                g_string_append_c(str, ch);
+                nm_str_buf_append_c(&str, ch);
                 i++;
 loop_ansic_next:;
             }
@@ -603,8 +651,8 @@ loop_ansic_next:;
         }
 
         /* an unquoted, regular character. Just consume it directly. */
-        if (str)
-            g_string_append_c(str, value[i]);
+        if (str.allocated > 0)
+            nm_str_buf_append_c(&str, value[i]);
         i++;
 
 loop1_next:;
@@ -614,22 +662,30 @@ loop1_next:;
 
 out_value:
     if (i == 0) {
-        nm_assert(!str);
+        nm_assert(str.allocated == 0);
+        nm_assert(!str._priv_str);
         *to_free = NULL;
         return "";
     }
 
-    if (str) {
-        if (str->len == 0 || str->str[0] == '\0') {
-            g_string_free(str, TRUE);
+    if (str.allocated > 0) {
+        if (check_utf8 && !nm_str_buf_utf8_validate(&str))
+            goto out_error;
+        if (str.len == 0 || nm_str_buf_get_str_unsafe(&str)[0] == '\0') {
+            nm_str_buf_destroy(&str);
             *to_free = NULL;
             return "";
         } else {
-            *to_free = g_string_free(str, FALSE);
+            *to_free = nm_str_buf_finalize(&str, NULL);
             return *to_free;
         }
     }
 
+    if (check_utf8 && !g_utf8_validate(value, i, NULL)) {
+        *to_free = NULL;
+        return NULL;
+    }
+
     if (value[i] != '\0') {
         *to_free = g_strndup(value, i);
         return *to_free;
@@ -639,8 +695,7 @@ out_value:
     return value;
 
 out_error:
-    if (str)
-        g_string_free(str, TRUE);
+    nm_str_buf_destroy(&str);
     *to_free = NULL;
     return NULL;
 }
@@ -1095,9 +1150,8 @@ _svGetValue(shvarFile *s, const char *key, char **to_free)
     if (line && line->line) {
         v = svUnescape(line->line, to_free);
         if (!v) {
-            /* a wrongly quoted value is treated like the empty string.
-             * See also svWriteFile(), which handles unparsable values
-             * that way. */
+            /* a wrongly quoted value or non-UTF-8 is treated like the empty string.
+             * See also svWriteFile(), which handles unparsable values that way. */
             nm_assert(!*to_free);
             return "";
         }
@@ -1470,6 +1524,91 @@ svUnsetValue(shvarFile *s, const char *key)
 
 /*****************************************************************************/
 
+void
+svWarnInvalid(shvarFile *s, const char *file_type, NMLogDomain log_domain)
+{
+    shvarLine *line;
+    gsize      n;
+
+    if (!nm_logging_enabled(LOGL_WARN, log_domain))
+        return;
+
+    n = 0;
+    c_list_for_each_entry (line, &s->lst_head, lst) {
+        gs_free char *s_tmp = NULL;
+
+        n++;
+
+        if (!line->key) {
+            const char *str;
+
+            nm_assert(line->line);
+            str = nm_str_skip_leading_spaces(line->line);
+            if (!NM_IN_SET(str[0], '\0', '#')) {
+                nm_log_warn(log_domain,
+                            "ifcfg-rh: %s,%s:%zu: invalid line ignored",
+                            file_type,
+                            s->fileName,
+                            n);
+            }
+            continue;
+        }
+
+        if (g_hash_table_lookup(s->lst_idx, line) != line) {
+            nm_log_warn(
+                log_domain,
+                "ifcfg-rh: %s,%s:%zu: key %s is duplicated and the early occurrence ignored",
+                file_type,
+                s->fileName,
+                n,
+                line->key);
+            continue;
+        }
+
+        if (!line->line) {
+            /* the line is deleted via svUnsetValue(). Ignore. */
+            continue;
+        }
+
+        if (!svUnescape(line->line, &s_tmp)) {
+            if (!svUnescape_full(line->line, &s_tmp, FALSE)) {
+                nm_log_warn(log_domain,
+                            "ifcfg-rh: %s,%s:%zu: key %s is badly quoted and is treated as \"\"",
+                            file_type,
+                            s->fileName,
+                            n,
+                            line->key);
+            } else {
+                nm_log_warn(log_domain,
+                            "ifcfg-rh: %s,%s:%zu: key %s does not contain valid UTF-8 and is "
+                            "treated as \"\"",
+                            file_type,
+                            s->fileName,
+                            n,
+                            line->key);
+            }
+            continue;
+        }
+
+        /* TODO: we read different shell scripts, and whether a key is recognized
+         * depends on the type. For example, alias files only accept a subset of
+         * known keys.
+         *
+         * Basically, depending on the @file_type, different keys are valid. */
+        if (!nms_ifcfg_rh_utils_is_well_known_key(line->key)) {
+            nm_log_dbg(log_domain,
+                       "ifcfg-rh: %s,%s:%zu: key %s is unknown and ignored",
+                       file_type,
+                       s->fileName,
+                       n,
+                       line->key);
+            continue;
+        }
+    }
+}
+
+/*****************************************************************************/
+
 /* Write the current contents iff modified.  Returns FALSE on error
  * and TRUE on success.  Do not write if no values have been modified.
  * The mode argument is only used if creating the file, not if
diff --git a/src/core/settings/plugins/ifcfg-rh/shvar.h b/src/core/settings/plugins/ifcfg-rh/shvar.h
index 6965d873..cf91642f 100644
--- a/src/core/settings/plugins/ifcfg-rh/shvar.h
+++ b/src/core/settings/plugins/ifcfg-rh/shvar.h
@@ -107,6 +107,7 @@ void svCloseFile(shvarFile *s);
 
 const char *svEscape(const char *s, char **to_free);
 const char *svUnescape(const char *s, char **to_free);
+const char *svUnescape_full(const char *value, char **to_free, gboolean check_utf8);
 
 static inline void
 _nm_auto_shvar_file_close(shvarFile **p_s)
@@ -120,4 +121,6 @@ _nm_auto_shvar_file_close(shvarFile **p_s)
 }
 #define nm_auto_shvar_file_close nm_auto(_nm_auto_shvar_file_close)
 
+void svWarnInvalid(shvarFile *s, const char *file_type, NMLogDomain log_domain);
+
 #endif /* _SHVAR_H */
diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-a.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-a.cexpected
index d81d9187..aebcd2b8 100644
--- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-a.cexpected
+++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-a.cexpected
@@ -1,5 +1,5 @@
-HWADDR=00:22:15:59:62:97
 TYPE=Ethernet
+HWADDR=00:22:15:59:62:97
 BRIDGING_OPTS="priority=28 hairpin_mode=1"
 NAME="System test-bridge-component"
 UUID=${UUID}
diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-b.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-b.cexpected
index 2e69c5e7..71d2cda8 100644
--- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-b.cexpected
+++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-b.cexpected
@@ -1,5 +1,5 @@
-HWADDR=00:22:15:59:62:97
 TYPE=Ethernet
+HWADDR=00:22:15:59:62:97
 NAME="System test-bridge-component"
 UUID=${UUID}
 DEVICE=eth0
diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-wired-802-1X-subj-matches.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-wired-802-1X-subj-matches.cexpected
index 59a6f79b..240cd5c1 100644
--- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-wired-802-1X-subj-matches.cexpected
+++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-wired-802-1X-subj-matches.cexpected
@@ -1,5 +1,5 @@
-HWADDR=00:11:22:33:44:EE
 TYPE=Ethernet
+HWADDR=00:11:22:33:44:EE
 KEY_MGMT=IEEE8021X
 IEEE_8021X_EAP_METHODS=PEAP
 IEEE_8021X_IDENTITY="Jara Cimrman"
diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bridge_Component.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bridge_Component.cexpected
index a8ff8df3..d31d8ff5 100644
--- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bridge_Component.cexpected
+++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bridge_Component.cexpected
@@ -1,6 +1,6 @@
+TYPE=Ethernet
 HWADDR=31:33:33:37:BE:CD
 MTU=1492
-TYPE=Ethernet
 BRIDGING_OPTS="priority=50 path_cost=33"
 BRIDGE_PORT_VLANS="1 untagged,2 pvid,4-4094 untagged"
 NAME="Test Write Bridge Component"
diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Static_Routes.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Static_Routes.cexpected
index cd8fc96f..cc29a2cc 100644
--- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Static_Routes.cexpected
+++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Static_Routes.cexpected
@@ -1,6 +1,6 @@
+TYPE=Ethernet
 HWADDR=31:33:33:37:BE:CD
 MTU=1492
-TYPE=Ethernet
 PROXY_METHOD=none
 BROWSER_ONLY=no
 BOOTPROTO=none
diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-static-routes-legacy.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-static-routes-legacy.cexpected
index a28c5c1c..8197f320 100644
--- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-static-routes-legacy.cexpected
+++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-static-routes-legacy.cexpected
@@ -1,5 +1,5 @@
-HWADDR=00:16:41:11:22:33
 TYPE=Ethernet
+HWADDR=00:16:41:11:22:33
 PROXY_METHOD=none
 BROWSER_ONLY=no
 BOOTPROTO=dhcp
diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-vlanid-use b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-vlanid-use
new file mode 100644
index 00000000..fc9c8a45
--- /dev/null
+++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-vlanid-use
@@ -0,0 +1,4 @@
+VLAN=yes
+TYPE=Vlan
+DEVICE=eth0.9
+VLAN_ID=10
diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-vlanid-use.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-vlanid-use.cexpected
new file mode 100644
index 00000000..a7be14ce
--- /dev/null
+++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-vlan-vlanid-use.cexpected
@@ -0,0 +1,15 @@
+VLAN=yes
+TYPE=Vlan
+PHYSDEV=eth0
+VLAN_ID=10
+REORDER_HDR=yes
+GVRP=no
+MVRP=no
+HWADDR=
+PROXY_METHOD=none
+BROWSER_ONLY=no
+IPV6INIT=no
+NAME="Vlan test-vlan-vlanid-use"
+UUID=${UUID}
+DEVICE=eth0.9
+ONBOOT=yes
diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-eap-suite-b-192-tls b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-eap-suite-b-192-tls
index 9a74bb4d..a21c301c 100644
--- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-eap-suite-b-192-tls
+++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-eap-suite-b-192-tls
@@ -3,7 +3,6 @@ DEVICE=eth2
 HWADDR=00:16:41:11:22:33
 BOOTPROTO=dhcp
 ONBOOT=yes
-ONBOOT=yes
 USERCTL=yes
 IPV6INIT=no
 NM_CONTROLLED=yes
diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-eap-ttls-tls b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-eap-ttls-tls
index 42ed1d68..bb63d5e8 100644
--- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-eap-ttls-tls
+++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wifi-wpa-eap-ttls-tls
@@ -4,7 +4,6 @@ DEVICE=eth2
 HWADDR=00:16:41:11:22:33
 BOOTPROTO=dhcp
 ONBOOT=yes
-ONBOOT=yes
 USERCTL=yes
 IPV6INIT=no
 NM_CONTROLLED=yes
diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-4.expected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-4.expected
index 040ddc9d..92c03b12 100644
--- a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-4.expected
+++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-4.expected
@@ -13,8 +13,10 @@
 #L2
 
 METRIC1=''
-METRIC2=$'\U0x'
-METRIC3=$'x\U0'
+METRIC2=
+#NM: METRIC2=$'\U0x'
+METRIC3=
+#NM: METRIC3=$'x\U0'
 
 #L4
 IPADDR=set-by-test1
diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-1.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-1.cexpected
new file mode 100644
index 00000000..5186f90c
--- /dev/null
+++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-1.cexpected
@@ -0,0 +1,14 @@
+TYPE=Ethernet
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=yes
+IPV6_AUTOCONF=yes
+IPV6_DEFROUTE=yes
+IPV6_FAILURE_FATAL=no
+IPV6_ADDR_GEN_MODE=stable-privacy
+NAME=test_roundtrip_ethtool
+UUID=${UUID}
+ONBOOT=yes
diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-2.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-2.cexpected
new file mode 100644
index 00000000..5632ebec
--- /dev/null
+++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-2.cexpected
@@ -0,0 +1,15 @@
+TYPE=Ethernet
+PROXY_METHOD=none
+BROWSER_ONLY=no
+ETHTOOL_OPTS="-A net0"
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=yes
+IPV6_AUTOCONF=yes
+IPV6_DEFROUTE=yes
+IPV6_FAILURE_FATAL=no
+IPV6_ADDR_GEN_MODE=stable-privacy
+NAME=test_roundtrip_ethtool
+UUID=${UUID}
+ONBOOT=yes
diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-3.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-3.cexpected
new file mode 100644
index 00000000..96eec43b
--- /dev/null
+++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-3.cexpected
@@ -0,0 +1,15 @@
+TYPE=Ethernet
+PROXY_METHOD=none
+BROWSER_ONLY=no
+ETHTOOL_OPTS="autoneg on"
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=yes
+IPV6_AUTOCONF=yes
+IPV6_DEFROUTE=yes
+IPV6_FAILURE_FATAL=no
+IPV6_ADDR_GEN_MODE=stable-privacy
+NAME=test_roundtrip_ethtool
+UUID=${UUID}
+ONBOOT=yes
diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-4.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-4.cexpected
new file mode 100644
index 00000000..90250a30
--- /dev/null
+++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-4.cexpected
@@ -0,0 +1,15 @@
+TYPE=Ethernet
+PROXY_METHOD=none
+BROWSER_ONLY=no
+ETHTOOL_OPTS="-K net0 rx on"
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=yes
+IPV6_AUTOCONF=yes
+IPV6_DEFROUTE=yes
+IPV6_FAILURE_FATAL=no
+IPV6_ADDR_GEN_MODE=stable-privacy
+NAME=test_roundtrip_ethtool
+UUID=${UUID}
+ONBOOT=yes
diff --git a/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-5.cexpected b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-5.cexpected
new file mode 100644
index 00000000..721eacb2
--- /dev/null
+++ b/src/core/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_roundtrip_ethtool-5.cexpected
@@ -0,0 +1,15 @@
+TYPE=Ethernet
+PROXY_METHOD=none
+BROWSER_ONLY=no
+ETHTOOL_OPTS="autoneg on ; -K net0 rx on"
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=yes
+IPV6_AUTOCONF=yes
+IPV6_DEFROUTE=yes
+IPV6_FAILURE_FATAL=no
+IPV6_ADDR_GEN_MODE=stable-privacy
+NAME=test_roundtrip_ethtool
+UUID=${UUID}
+ONBOOT=yes
diff --git a/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
index 9d9ed626..d9829f84 100644
--- a/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
+++ b/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
@@ -17,7 +17,8 @@
 #include <linux/if_ether.h>
 #include <linux/if_infiniband.h>
 
-#include "nm-glib-aux/nm-json-aux.h"
+#include "libnm-glib-aux/nm-uuid.h"
+#include "libnm-glib-aux/nm-json-aux.h"
 #include "nm-utils.h"
 #include "nm-setting-connection.h"
 #include "nm-setting-wired.h"
@@ -36,9 +37,9 @@
 #include "nm-setting-serial.h"
 #include "nm-setting-vlan.h"
 #include "nm-setting-dcb.h"
-#include "nm-core-internal.h"
-#include "nm-base/nm-ethtool-base.h"
-#include "nm-base/nm-ethtool-utils-base.h"
+#include "libnm-core-intern/nm-core-internal.h"
+#include "libnm-base/nm-ethtool-base.h"
+#include "libnm-base/nm-ethtool-utils-base.h"
 
 #include "NetworkManagerUtils.h"
 
@@ -180,8 +181,20 @@ _assert_expected_content(NMConnection *connection, const char *filename, const c
     }
 
     if (len_expectd != len_written || memcmp(content_expectd, content_written, len_expectd) != 0) {
-        if (g_getenv("NMTST_IFCFG_RH_UPDATE_EXPECTED")
-            || nm_streq0(g_getenv("NM_TEST_REGENERATE"), "1")) {
+        static int rewrite_static = 0;
+        int        rewrite;
+
+        rewrite = g_atomic_int_get(&rewrite_static);
+        if (G_UNLIKELY(rewrite == 0)) {
+            rewrite = (g_getenv("NMTST_IFCFG_RH_UPDATE_EXPECTED")
+                       || nm_streq0(g_getenv("NM_TEST_REGENERATE"), "1"))
+                          ? 1
+                          : -1;
+            if (!g_atomic_int_compare_and_exchange(&rewrite_static, 0, rewrite))
+                g_assert_not_reached();
+        }
+
+        if (rewrite > 0) {
             if (uuid) {
                 gs_free char *search = g_strdup_printf("UUID=%s\n", uuid);
                 const char *  s;
@@ -209,15 +222,16 @@ _assert_expected_content(NMConnection *connection, const char *filename, const c
             success = g_file_set_contents(expected, content_written, len_written, &error);
             nmtst_assert_success(success, error);
         } else {
-            g_error("The content of \"%s\" (%zu) differs from \"%s\" (%zu). Set "
-                    "NMTST_IFCFG_RH_UPDATE_EXPECTED=yes to update the files "
-                    "inplace\n\n>>>%s<<<\n\n>>>%s<<<\n",
-                    filename,
-                    len_written,
-                    expected,
-                    len_expectd,
-                    content_written,
-                    content_expectd);
+            g_error(
+                "The content of \"%s\" (%zu) differs from \"%s\" (%zu). Set "
+                "NMTST_IFCFG_RH_UPDATE_EXPECTED=yes (or NM_TEST_REGENERATE=1) to update the files "
+                "inplace\n\n>>>%s<<<\n\n>>>%s<<<\n",
+                filename,
+                len_written,
+                expected,
+                len_expectd,
+                content_written,
+                content_expectd);
         }
     }
 }
@@ -359,8 +373,10 @@ _writer_new_connection_reread(NMConnection * connection,
 
     if (out_filename)
         *out_filename = filename;
-    else
+    else {
+        nmtst_file_unlink(filename);
         g_free(filename);
+    }
 }
 
 static void
@@ -722,10 +738,13 @@ test_read_variables_corner_cases(void)
     const char *         mac;
     char                 expected_mac_address[ETH_ALEN] = {0x00, 0x16, 0x41, 0x11, 0x22, 0x33};
 
+    NMTST_EXPECT_NM_WARN("*key NAME is badly quoted and is treated as \"\"*");
+    NMTST_EXPECT_NM_WARN("*key ZONE is badly quoted and is treated as \"\"*");
     connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-variables-corner-cases-1",
                                        NULL,
                                        TYPE_ETHERNET,
                                        NULL);
+    g_test_assert_expected_messages();
 
     /* ===== CONNECTION SETTING ===== */
     s_con = nm_connection_get_setting_connection(connection);
@@ -814,10 +833,12 @@ test_read_unrecognized(void)
     gs_free char *       unhandled_spec     = NULL;
     guint64              expected_timestamp = 0;
 
+    NMTST_EXPECT_NM_WARN("*key NAME is badly quoted and is treated as \"\"*");
     connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-unrecognized",
                                        NULL,
                                        NULL,
                                        &unhandled_spec);
+    g_test_assert_expected_messages();
     g_assert_cmpstr(unhandled_spec, ==, "unrecognized:mac:00:11:22:33");
 
     /* ===== CONNECTION SETTING ===== */
@@ -988,10 +1009,12 @@ test_read_wired_dhcp(void)
     char                 expected_mac_address[ETH_ALEN] = {0x00, 0x11, 0x22, 0x33, 0x44, 0xee};
     const char *         mac;
 
+    NMTST_EXPECT_NM_WARN("*key IPV6INIT is duplicated and the early occurrence ignored*");
     connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wired-dhcp",
                                        NULL,
                                        TYPE_ETHERNET,
                                        &unmanaged);
+    g_test_assert_expected_messages();
     g_assert(unmanaged == NULL);
 
     /* ===== CONNECTION SETTING ===== */
@@ -2519,7 +2542,7 @@ test_write_dns_options(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test DNS options",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -3567,10 +3590,12 @@ test_read_wifi_wpa_eap_tls(void)
     char *             unmanaged                 = NULL;
     const char *       expected_privkey_password = "test1";
 
+    NMTST_EXPECT_NM_WARN("*key ONBOOT is duplicated and the early occurrence ignored*");
     connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wifi-wpa-eap-tls",
                                        NULL,
                                        TYPE_ETHERNET,
                                        &unmanaged);
+    g_test_assert_expected_messages();
     g_assert(!unmanaged);
 
     /* ===== WIRELESS SETTING ===== */
@@ -3775,10 +3800,12 @@ test_read_wifi_wep_eap_ttls_chap(void)
     NMSetting8021x *           s_8021x;
     char *                     unmanaged = NULL;
 
+    NMTST_EXPECT_NM_WARN("*key ONBOOT is duplicated and the early occurrence ignored*");
     connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-wifi-wep-eap-ttls-chap",
                                        NULL,
                                        TYPE_WIRELESS,
                                        &unmanaged);
+    g_test_assert_expected_messages();
     g_assert(!unmanaged);
 
     /* ===== WIRELESS SETTING ===== */
@@ -3933,6 +3960,76 @@ test_read_wired_unknown_ethtool_opt(void)
 }
 
 static void
+test_roundtrip_ethtool(void)
+{
+    gs_unref_object NMConnection *connection = NULL;
+    NMSetting *                   s_ethtool;
+    NMSetting *                   s_wired;
+
+    connection = nmtst_create_minimal_connection("test_roundtrip_ethtool",
+                                                 NULL,
+                                                 NM_SETTING_WIRED_SETTING_NAME,
+                                                 NULL);
+    _writer_new_connec_exp(connection,
+                           TEST_SCRATCH_DIR,
+                           TEST_IFCFG_DIR "/ifcfg-test_roundtrip_ethtool-1.cexpected",
+                           NULL);
+    g_clear_object(&connection);
+
+    connection = nmtst_create_minimal_connection("test_roundtrip_ethtool",
+                                                 NULL,
+                                                 NM_SETTING_WIRED_SETTING_NAME,
+                                                 NULL);
+    s_ethtool  = nm_setting_ethtool_new();
+    nm_connection_add_setting(connection, s_ethtool);
+    _writer_new_connec_exp(connection,
+                           TEST_SCRATCH_DIR,
+                           TEST_IFCFG_DIR "/ifcfg-test_roundtrip_ethtool-2.cexpected",
+                           NULL);
+    g_clear_object(&connection);
+
+    connection = nmtst_create_minimal_connection("test_roundtrip_ethtool",
+                                                 NULL,
+                                                 NM_SETTING_WIRED_SETTING_NAME,
+                                                 NULL);
+    s_wired    = nm_connection_get_setting(connection, NM_TYPE_SETTING_WIRED);
+    g_object_set(s_wired, NM_SETTING_WIRED_AUTO_NEGOTIATE, TRUE, NULL);
+    _writer_new_connec_exp(connection,
+                           TEST_SCRATCH_DIR,
+                           TEST_IFCFG_DIR "/ifcfg-test_roundtrip_ethtool-3.cexpected",
+                           NULL);
+    g_clear_object(&connection);
+
+    connection = nmtst_create_minimal_connection("test_roundtrip_ethtool",
+                                                 NULL,
+                                                 NM_SETTING_WIRED_SETTING_NAME,
+                                                 NULL);
+    s_ethtool  = nm_setting_ethtool_new();
+    nm_connection_add_setting(connection, s_ethtool);
+    nm_setting_option_set_boolean(s_ethtool, NM_ETHTOOL_OPTNAME_FEATURE_RX, TRUE);
+    _writer_new_connec_exp(connection,
+                           TEST_SCRATCH_DIR,
+                           TEST_IFCFG_DIR "/ifcfg-test_roundtrip_ethtool-4.cexpected",
+                           NULL);
+    g_clear_object(&connection);
+
+    connection = nmtst_create_minimal_connection("test_roundtrip_ethtool",
+                                                 NULL,
+                                                 NM_SETTING_WIRED_SETTING_NAME,
+                                                 NULL);
+    s_wired    = nm_connection_get_setting(connection, NM_TYPE_SETTING_WIRED);
+    g_object_set(s_wired, NM_SETTING_WIRED_AUTO_NEGOTIATE, TRUE, NULL);
+    s_ethtool = nm_setting_ethtool_new();
+    nm_connection_add_setting(connection, s_ethtool);
+    nm_setting_option_set_boolean(s_ethtool, NM_ETHTOOL_OPTNAME_FEATURE_RX, TRUE);
+    _writer_new_connec_exp(connection,
+                           TEST_SCRATCH_DIR,
+                           TEST_IFCFG_DIR "/ifcfg-test_roundtrip_ethtool-5.cexpected",
+                           NULL);
+    g_clear_object(&connection);
+}
+
+static void
 test_read_wifi_hidden(void)
 {
     NMConnection *       connection;
@@ -3977,7 +4074,7 @@ test_write_wifi_hidden(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wi-Fi Hidden",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_TYPE,
                  NM_SETTING_WIRELESS_SETTING_NAME,
                  NULL);
@@ -4062,7 +4159,7 @@ test_write_wifi_mac_random(gconstpointer user_data)
                  NM_SETTING_CONNECTION_ID,
                  val,
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_TYPE,
                  NM_SETTING_WIRELESS_SETTING_NAME,
                  NULL);
@@ -4122,7 +4219,7 @@ test_write_wired_wake_on_lan(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wired Wake-on-LAN",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_TYPE,
                  NM_SETTING_WIRED_SETTING_NAME,
                  NULL);
@@ -4305,7 +4402,7 @@ test_write_wifi_band_a(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wi-Fi Band A",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_TYPE,
                  NM_SETTING_WIRELESS_SETTING_NAME,
                  NULL);
@@ -4363,7 +4460,7 @@ test_write_wifi_ap_mode(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wi-Fi AP Mode",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_TYPE,
                  NM_SETTING_WIRELESS_SETTING_NAME,
                  NULL);
@@ -4676,7 +4773,7 @@ test_write_wired_static(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wired Static",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_AUTOCONNECT_RETRIES,
@@ -4831,7 +4928,7 @@ test_write_wired_static_with_generic(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wired Static",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_AUTOCONNECT_RETRIES,
@@ -4999,7 +5096,7 @@ test_write_wired_dhcp(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wired DHCP",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -5111,7 +5208,7 @@ test_write_routing_rules(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Routing Rules",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -5169,7 +5266,7 @@ test_write_wired_match(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wired with Match setting",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -5315,7 +5412,7 @@ test_write_wired_static_ip6_only(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wired Static IP6 Only",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -5432,7 +5529,7 @@ test_write_wired_static_ip6_only_gw(gconstpointer user_data)
                  NM_SETTING_CONNECTION_ID,
                  id,
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -5587,7 +5684,7 @@ test_write_wired_static_routes(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wired Static Routes",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -5711,7 +5808,7 @@ test_write_wired_dhcp_8021x_peap_mschapv2(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wired DHCP 802.1x PEAP MSCHAPv2",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -5813,7 +5910,7 @@ test_write_wired_8021x_tls(gconstpointer test_data)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wired 802.1x TLS Blobs",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -5983,7 +6080,7 @@ test_write_wired_aliases(void)
                  NM_SETTING_CONNECTION_ID,
                  "alias0",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_TYPE,
                  NM_SETTING_WIRED_SETTING_NAME,
                  NULL);
@@ -6110,7 +6207,7 @@ test_write_gateway(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Static Addresses Gateway",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_TYPE,
                  NM_SETTING_WIRED_SETTING_NAME,
                  NULL);
@@ -6187,7 +6284,7 @@ test_write_wifi_open(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wifi Open",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -6273,7 +6370,7 @@ test_write_wifi_open_hex_ssid(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wifi Open Hex SSID",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -6347,7 +6444,7 @@ test_write_wifi_wep(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wifi WEP",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -6445,7 +6542,7 @@ test_write_wifi_wep_adhoc(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wifi WEP AdHoc",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -6539,7 +6636,7 @@ test_write_wifi_wep_passphrase(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wifi WEP Passphrase",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -6634,7 +6731,7 @@ test_write_wifi_wep_40_ascii(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wifi WEP 40 ASCII",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -6732,7 +6829,7 @@ test_write_wifi_wep_104_ascii(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wifi WEP 104 ASCII",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -6833,7 +6930,7 @@ test_write_wifi_leap(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wifi LEAP",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -6931,7 +7028,7 @@ test_write_wifi_leap_secret_flags(gconstpointer data)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wifi LEAP Secret Flags",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_TYPE,
                  NM_SETTING_WIRELESS_SETTING_NAME,
                  NULL);
@@ -7037,7 +7134,7 @@ test_write_wifi_wpa_psk(gconstpointer test_data)
                  NM_SETTING_CONNECTION_ID,
                  args.name,
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -7142,7 +7239,7 @@ test_write_wifi_wpa_psk_adhoc(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wifi WPA PSK",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -7252,7 +7349,7 @@ test_write_wifi_wpa_eap_tls(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wifi WPA EAP-TLS",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -7295,8 +7392,8 @@ test_write_wifi_wpa_eap_tls(void)
     g_object_set(s_8021x, NM_SETTING_802_1X_IDENTITY, "Bill Smith", NULL);
     g_object_set(s_8021x,
                  NM_SETTING_802_1X_PHASE1_AUTH_FLAGS,
-                 (guint)(NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_0_DISABLE
-                         | NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_1_DISABLE),
+                 (guint) (NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_0_DISABLE
+                          | NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_1_DISABLE),
                  NULL);
 
     nm_setting_802_1x_add_eap_method(s_8021x, "tls");
@@ -7379,7 +7476,7 @@ test_write_wifi_wpa_eap_ttls_tls(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wifi WPA EAP-TTLS (TLS)",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -7515,7 +7612,7 @@ test_write_wifi_wpa_eap_ttls_mschapv2(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wifi WPA EAP-TTLS (MSCHAPv2)",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -7630,7 +7727,7 @@ test_write_wifi_wpa_then_open(void)
                  NM_SETTING_CONNECTION_ID,
                  "random wifi connection",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -7747,7 +7844,7 @@ test_write_wifi_wpa_then_wep_with_perms(void)
                  NM_SETTING_CONNECTION_ID,
                  "random wifi connection 2",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_PERMISSIONS,
@@ -7872,7 +7969,7 @@ test_write_wifi_dynamic_wep_leap(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wifi Dynamic WEP LEAP",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_TYPE,
                  NM_SETTING_WIRELESS_SETTING_NAME,
                  NULL);
@@ -7969,7 +8066,7 @@ test_write_wired_qeth_dhcp(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wired qeth Static",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -8043,7 +8140,7 @@ test_write_wired_ctc_dhcp(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wired ctc Static",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_TYPE,
                  NM_SETTING_WIRED_SETTING_NAME,
                  NULL);
@@ -8118,7 +8215,7 @@ test_write_permissions(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Permissions",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -8187,7 +8284,7 @@ test_write_wifi_wep_agent_keys(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wifi WEP Agent Owned",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_TYPE,
                  NM_SETTING_WIRELESS_SETTING_NAME,
                  NULL);
@@ -8273,7 +8370,7 @@ test_write_wired_pppoe(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Wired PPPoE",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -8334,7 +8431,7 @@ test_write_vpn(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write VPN",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -8392,7 +8489,7 @@ test_write_mobile_broadband(gconstpointer data)
                  NM_SETTING_CONNECTION_ID,
                  gsm ? "Test Write GSM" : "Test Write CDMA",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -8510,7 +8607,7 @@ test_write_bridge_main(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Bridge Main",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_INTERFACE_NAME,
@@ -8639,7 +8736,7 @@ test_write_bridge_component(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Bridge Component",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -8787,6 +8884,38 @@ test_read_vlan_only_vlan_id(void)
 }
 
 static void
+test_read_vlan_vlanid_use(void)
+{
+    nmtst_auto_unlinkfile char *testfile     = NULL;
+    gs_unref_object NMConnection *connection = NULL;
+    gs_unref_object NMConnection *reread     = NULL;
+    NMSettingVlan *               s_vlan;
+
+    connection = _connection_from_file(TEST_IFCFG_DIR "/ifcfg-test-vlan-vlanid-use",
+                                       NULL,
+                                       TYPE_ETHERNET,
+                                       NULL);
+
+    g_assert_cmpstr(nm_connection_get_interface_name(connection), ==, "eth0.9");
+
+    s_vlan = nm_connection_get_setting_vlan(connection);
+    g_assert(s_vlan);
+
+    g_assert_cmpstr(nm_setting_vlan_get_parent(s_vlan), ==, "eth0");
+    g_assert_cmpint(nm_setting_vlan_get_id(s_vlan), ==, 10);
+    g_assert_cmpint(nm_setting_vlan_get_flags(s_vlan), ==, NM_VLAN_FLAG_REORDER_HEADERS);
+
+    _writer_new_connec_exp(connection,
+                           TEST_SCRATCH_DIR,
+                           TEST_IFCFG_DIR "/ifcfg-test-vlan-vlanid-use.cexpected",
+                           &testfile);
+
+    reread = _connection_from_file(testfile, NULL, TYPE_ETHERNET, NULL);
+
+    nmtst_assert_connection_equals(connection, TRUE, reread, FALSE);
+}
+
+static void
 test_read_vlan_only_device(void)
 {
     NMConnection * connection;
@@ -8991,7 +9120,7 @@ test_write_vlan_reorder_hdr(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write VLAN reorder_hdr",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  FALSE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -9046,7 +9175,7 @@ test_write_ethernet_missing_ipv6(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Ethernet Without IPv6 Setting",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -9170,7 +9299,7 @@ test_write_bond_main(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Bond Main",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_INTERFACE_NAME,
@@ -9270,7 +9399,7 @@ test_write_bond_slave(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Bond Slave",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -9357,7 +9486,7 @@ test_write_infiniband(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write InfiniBand",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -9446,7 +9575,7 @@ test_write_bond_slave_ib(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Bond Slave InfiniBand",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_TYPE,
@@ -9604,7 +9733,7 @@ test_write_dcb_basic(void)
                  NM_SETTING_CONNECTION_ID,
                  "dcb-test",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_TYPE,
                  NM_SETTING_WIRED_SETTING_NAME,
                  NM_SETTING_CONNECTION_INTERFACE_NAME,
@@ -9850,7 +9979,7 @@ test_write_fcoe_mode(gconstpointer user_data)
                  NM_SETTING_CONNECTION_ID,
                  "fcoe-test",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_TYPE,
                  NM_SETTING_WIRED_SETTING_NAME,
                  NM_SETTING_CONNECTION_INTERFACE_NAME,
@@ -9968,7 +10097,7 @@ test_write_team_master(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Team Master",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_INTERFACE_NAME,
                  "team0",
                  NM_SETTING_CONNECTION_TYPE,
@@ -10060,7 +10189,7 @@ test_write_team_port(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Team Port",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_TYPE,
                  NM_SETTING_WIRED_SETTING_NAME,
                  NM_SETTING_CONNECTION_MASTER,
@@ -10119,7 +10248,7 @@ test_write_team_infiniband_port(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Team Infiniband Port",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_TYPE,
                  NM_SETTING_INFINIBAND_SETTING_NAME,
                  NM_SETTING_CONNECTION_MASTER,
@@ -10309,7 +10438,7 @@ test_write_proxy_basic(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write Proxy Basic",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_TYPE,
                  NM_SETTING_WIRED_SETTING_NAME,
                  NULL);
@@ -10358,7 +10487,7 @@ _svUnescape(const char *str, char **to_free)
         str = (str_free = g_strdup(str));
     }
 
-    s = svUnescape(str, to_free);
+    s = svUnescape_full(str, to_free, FALSE);
     if (*to_free) {
         g_assert(s == *to_free);
         g_assert(s[0]);
@@ -10366,6 +10495,37 @@ _svUnescape(const char *str, char **to_free)
         g_assert(s == NULL || (!s[0] && (s < str || s > strchr(str, '\0')))
                  || (s[0] && s >= str && s <= strchr(str, '\0')));
     }
+
+    {
+        const char *  s2;
+        gs_free char *to_free2 = NULL;
+
+        gboolean is_utf8 = s && g_utf8_validate(s, -1, NULL);
+
+        s2 = svUnescape_full(str, &to_free2, TRUE);
+        if (NM_IN_STRSET(str, "$'\\U0x'", "$'\\x0'", "$'\\008'", "$'\\08'")) {
+            g_assert_cmpstr(s2, ==, NULL);
+            g_assert(!to_free2);
+            g_assert_cmpstr(s, ==, "");
+            g_assert(!*to_free);
+        } else if (NM_IN_STRSET(str, "$'x\\U0'")) {
+            g_assert_cmpstr(s2, ==, NULL);
+            g_assert(!to_free2);
+            g_assert_cmpstr(s, ==, "x");
+            g_assert(*to_free == s);
+        } else if (!is_utf8) {
+            g_assert(!s2);
+            g_assert(!to_free2);
+        } else if (!to_free2) {
+            g_assert_cmpstr(s, ==, s2);
+            g_assert(s == s2);
+        } else {
+            g_assert_cmpstr(s, ==, s2);
+            g_assert(s != s2);
+            g_assert(s2 == to_free2);
+        }
+    }
+
     return s;
 }
 
@@ -10547,6 +10707,9 @@ test_svUnescape(void)
         V1("\"\\'\"''", "\\'"),
         V0("\"b\\~b\" ", "b\\~b"),
         V1("\"b\\~b\"x", "b\\~bx"),
+
+        V0("$'x\\U0'", "x"),
+        V0("$'\\U0x'", ""),
     };
     const UnescapeTestData data_ansi[] = {
         /* strings inside $''. They cannot be compared directly, but must
@@ -10733,7 +10896,7 @@ test_write_unknown(gconstpointer test_data)
         _svGetValue_check(sv, "METRIC", NULL);
         _svGetValue_check(sv, "METRIC1", "");
         _svGetValue_check(sv, "METRIC2", "");
-        _svGetValue_check(sv, "METRIC3", "x");
+        _svGetValue_check(sv, "METRIC3", "");
 
         _svGetValue_check(sv, "IPADDR", "set-by-test1");
         _svGetValue_check(sv, "IPADDR2", "set-by-test2");
@@ -10999,7 +11162,7 @@ test_sriov_write(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write SR-IOV config",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_INTERFACE_NAME,
@@ -11132,7 +11295,7 @@ test_tc_write_empty(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write TC config",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_INTERFACE_NAME,
@@ -11213,7 +11376,7 @@ test_tc_write(void)
                  NM_SETTING_CONNECTION_ID,
                  "Test Write TC config",
                  NM_SETTING_CONNECTION_UUID,
-                 nm_utils_uuid_generate_a(),
+                 nm_uuid_generate_random_str_a(),
                  NM_SETTING_CONNECTION_AUTOCONNECT,
                  TRUE,
                  NM_SETTING_CONNECTION_INTERFACE_NAME,
@@ -11405,6 +11568,7 @@ test_ethtool_names(void)
     } s_idxs[] = {
         {_NM_ETHTOOL_ID_FEATURE_FIRST, _NM_ETHTOOL_ID_FEATURE_LAST},
         {_NM_ETHTOOL_ID_COALESCE_FIRST, _NM_ETHTOOL_ID_COALESCE_LAST},
+        {_NM_ETHTOOL_ID_PAUSE_FIRST, _NM_ETHTOOL_ID_PAUSE_LAST},
         {_NM_ETHTOOL_ID_RING_FIRST, _NM_ETHTOOL_ID_RING_LAST},
     };
     const NMEthtoolData *data;
@@ -11599,6 +11763,8 @@ main(int argc, char **argv)
     g_test_add_func(TPATH "802-1x/password_raw", test_read_write_802_1x_password_raw);
     g_test_add_func(TPATH "802-1x/tls-p12-no-client-cert", test_read_802_1x_tls_p12_no_client_cert);
 
+    g_test_add_func(TPATH "wired/roundtrip/ethtool", test_roundtrip_ethtool);
+
     g_test_add_data_func(TPATH "wired/read/aliases/good/0",
                          GINT_TO_POINTER(0),
                          test_read_wired_aliases_good);
@@ -11705,6 +11871,7 @@ main(int argc, char **argv)
     g_test_add_func(TPATH "vlan/read/physdev", test_read_vlan_physdev);
     g_test_add_func(TPATH "vlan/read/reorder-hdr-1", test_read_vlan_reorder_hdr_1);
     g_test_add_func(TPATH "vlan/read/reorder-hdr-2", test_read_vlan_reorder_hdr_2);
+    g_test_add_func(TPATH "vlan/read/vlanid-use", test_read_vlan_vlanid_use);
     g_test_add_func(TPATH "wired/read/read-wake-on-lan", test_read_wired_wake_on_lan);
     g_test_add_func(TPATH "wired/read/read-auto-negotiate-off", test_read_wired_auto_negotiate_off);
     g_test_add_func(TPATH "wired/read/read-auto-negotiate-on", test_read_wired_auto_negotiate_on);
diff --git a/src/core/settings/plugins/ifupdown/meson.build b/src/core/settings/plugins/ifupdown/meson.build
index dd252783..1bff8155 100644
--- a/src/core/settings/plugins/ifupdown/meson.build
+++ b/src/core/settings/plugins/ifupdown/meson.build
@@ -7,14 +7,12 @@ libnms_ifupdown_core = static_library(
     'nms-ifupdown-parser.c',
   ),
   dependencies: core_default_dep,
-  c_args: daemon_c_flags,
 )
 
 libnm_settings_plugin_ifupdown = shared_module(
   'nm-settings-plugin-ifupdown',
   sources: 'nms-ifupdown-plugin.c',
   dependencies: core_plugin_dep,
-  c_args: daemon_c_flags,
   link_with: libnms_ifupdown_core,
   link_args: ldflags_linker_script_settings,
   link_depends: linker_script_settings,
diff --git a/src/core/settings/plugins/ifupdown/nms-ifupdown-parser.c b/src/core/settings/plugins/ifupdown/nms-ifupdown-parser.c
index a4974a96..db6b1aef 100644
--- a/src/core/settings/plugins/ifupdown/nms-ifupdown-parser.c
+++ b/src/core/settings/plugins/ifupdown/nms-ifupdown-parser.c
@@ -12,7 +12,8 @@
 #include <stdlib.h>
 #include <ctype.h>
 
-#include "nm-core-internal.h"
+#include "libnm-glib-aux/nm-uuid.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "settings/nm-settings-plugin.h"
 
 #include "nms-ifupdown-plugin.h"
@@ -32,14 +33,6 @@
 
 /*****************************************************************************/
 
-#define _str_has_prefix(val, prefix, require_suffix)                   \
-    ({                                                                 \
-        const char *_val = (val);                                      \
-                                                                       \
-        (strncmp(_val, "" prefix "", NM_STRLEN(prefix)) == 0)          \
-            && (!(require_suffix) || _val[NM_STRLEN(prefix)] != '\0'); \
-    })
-
 static const char *
 _ifupdownplugin_guess_connection_type(if_block *block)
 {
@@ -51,8 +44,7 @@ _ifupdownplugin_guess_connection_type(if_block *block)
         if_data *ifb;
 
         c_list_for_each_entry (ifb, &block->data_lst_head, data_lst) {
-            if (_str_has_prefix(ifb->key, "wireless-", FALSE)
-                || _str_has_prefix(ifb->key, "wpa-", FALSE)) {
+            if (NM_STR_HAS_PREFIX(ifb->key, "wireless-") || NM_STR_HAS_PREFIX(ifb->key, "wpa-")) {
                 ret_type = NM_SETTING_WIRELESS_SETTING_NAME;
                 break;
             }
@@ -102,7 +94,7 @@ update_wireless_setting_from_if_block(NMConnection *connection, if_block *block)
     wireless_setting = NM_SETTING_WIRELESS(nm_setting_wireless_new());
 
     c_list_for_each_entry (curr, &block->data_lst_head, data_lst) {
-        if (_str_has_prefix(curr->key, "wireless-", TRUE)) {
+        if (NM_STR_HAS_PREFIX_WITH_MORE(curr->key, "wireless-")) {
             const char *newkey = map_by_mapping(mapping, curr->key + NM_STRLEN("wireless-"));
 
             _LOGI("wireless setting key: %s='%s'", newkey, curr->data);
@@ -137,7 +129,7 @@ update_wireless_setting_from_if_block(NMConnection *connection, if_block *block)
             } else {
                 g_object_set(wireless_setting, newkey, curr->data, NULL);
             }
-        } else if (_str_has_prefix(curr->key, "wpa-", TRUE)) {
+        } else if (NM_STR_HAS_PREFIX_WITH_MORE(curr->key, "wpa-")) {
             const char *newkey = map_by_mapping(mapping, curr->key + NM_STRLEN("wpa-"));
 
             if (nm_streq0(newkey, "ssid")) {
@@ -304,7 +296,7 @@ update_wireless_security_setting_from_if_block(NMConnection *connection, if_bloc
     wireless_security_setting = NM_SETTING_WIRELESS_SECURITY(nm_setting_wireless_security_new());
 
     c_list_for_each_entry (curr, &block->data_lst_head, data_lst) {
-        if (_str_has_prefix(curr->key, "wireless-", TRUE)) {
+        if (NM_STR_HAS_PREFIX_WITH_MORE(curr->key, "wireless-")) {
             const char *          key                  = curr->key + NM_STRLEN("wireless-");
             char *                property_value       = NULL;
             gpointer              typed_property_value = NULL;
@@ -336,7 +328,7 @@ wireless_next:
             if (typed_property_value && free_func)
                 (*free_func)(typed_property_value);
 
-        } else if (_str_has_prefix(curr->key, "wpa-", TRUE)) {
+        } else if (NM_STR_HAS_PREFIX_WITH_MORE(curr->key, "wpa-")) {
             const char *          key                  = curr->key + NM_STRLEN("wpa-");
             char *                property_value       = NULL;
             gpointer              typed_property_value = NULL;
@@ -644,7 +636,7 @@ ifupdown_new_connection_from_if_block(if_block *block, gboolean autoconnect, GEr
     type  = _ifupdownplugin_guess_connection_type(block);
     idstr = g_strconcat("Ifupdown (", block->name, ")", NULL);
 
-    uuid = nm_utils_uuid_generate_from_string(idstr, -1, NM_UTILS_UUID_TYPE_LEGACY, NULL);
+    uuid = nm_uuid_generate_from_string_str(idstr, -1, NM_UUID_TYPE_LEGACY, NULL);
     g_object_set(s_con,
                  NM_SETTING_CONNECTION_TYPE,
                  type,
@@ -655,7 +647,7 @@ ifupdown_new_connection_from_if_block(if_block *block, gboolean autoconnect, GEr
                  NM_SETTING_CONNECTION_UUID,
                  uuid,
                  NM_SETTING_CONNECTION_AUTOCONNECT,
-                 (gboolean)(!!autoconnect),
+                 (gboolean) (!!autoconnect),
                  NULL);
 
     _LOGD("update_connection_setting_from_if_block: name:%s, type:%s, id:%s, uuid: %s",
diff --git a/src/core/settings/plugins/ifupdown/nms-ifupdown-plugin.c b/src/core/settings/plugins/ifupdown/nms-ifupdown-plugin.c
index 34523f8d..80a5638a 100644
--- a/src/core/settings/plugins/ifupdown/nms-ifupdown-plugin.c
+++ b/src/core/settings/plugins/ifupdown/nms-ifupdown-plugin.c
@@ -9,7 +9,7 @@
 
 #include "nms-ifupdown-plugin.h"
 
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "nm-core-utils.h"
 #include "nm-config.h"
 #include "settings/nm-settings-plugin.h"
diff --git a/src/core/settings/plugins/ifupdown/tests/test-ifupdown.c b/src/core/settings/plugins/ifupdown/tests/test-ifupdown.c
index 6e3eb0e7..0e8db733 100644
--- a/src/core/settings/plugins/ifupdown/tests/test-ifupdown.c
+++ b/src/core/settings/plugins/ifupdown/tests/test-ifupdown.c
@@ -5,7 +5,7 @@
 
 #include "src/core/nm-default-daemon.h"
 
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 
 #include "settings/plugins/ifupdown/nms-ifupdown-interface-parser.h"
 #include "settings/plugins/ifupdown/nms-ifupdown-parser.h"
diff --git a/src/core/settings/plugins/keyfile/nms-keyfile-plugin.c b/src/core/settings/plugins/keyfile/nms-keyfile-plugin.c
index 789bfc1a..e8ae1814 100644
--- a/src/core/settings/plugins/keyfile/nms-keyfile-plugin.c
+++ b/src/core/settings/plugins/keyfile/nms-keyfile-plugin.c
@@ -13,19 +13,20 @@
 #include <sys/types.h>
 #include <sys/time.h>
 
-#include "nm-std-aux/c-list-util.h"
-#include "nm-glib-aux/nm-c-list.h"
-#include "nm-glib-aux/nm-io-utils.h"
+#include "libnm-std-aux/c-list-util.h"
+#include "libnm-glib-aux/nm-c-list.h"
+#include "libnm-glib-aux/nm-uuid.h"
+#include "libnm-glib-aux/nm-io-utils.h"
 
 #include "nm-connection.h"
 #include "nm-setting.h"
 #include "nm-setting-connection.h"
 #include "nm-utils.h"
 #include "nm-config.h"
-#include "nm-core-internal.h"
-#include "nm-keyfile-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
+#include "libnm-core-intern/nm-keyfile-internal.h"
 
-#include "systemd/nm-sd-utils-shared.h"
+#include "libnm-systemd-shared/nm-sd-utils-shared.h"
 
 #include "settings/nm-settings-plugin.h"
 #include "settings/nm-settings-storage.h"
@@ -233,7 +234,7 @@ _read_from_file(const char * full_filename,
 
     nm_assert(!connection
               || (_nm_connection_verify(connection, NULL) == NM_SETTING_VERIFY_SUCCESS));
-    nm_assert(!connection || nm_utils_is_uuid(nm_connection_get_uuid(connection)));
+    nm_assert(!connection || nm_uuid_is_normalized(nm_connection_get_uuid(connection)));
 
     return connection;
 }
@@ -260,7 +261,7 @@ _nm_assert_storage(gpointer plugin /* NMSKeyfilePlugin  */,
 
     uuid = nms_keyfile_storage_get_uuid(storage);
 
-    nm_assert(nm_utils_is_uuid(uuid));
+    nm_assert(nm_uuid_is_normalized(uuid));
 
     nm_assert(((NMSKeyfileStorage *) storage)->is_meta_data
               || !(((NMSKeyfileStorage *) storage)->u.conn_data.connection)
@@ -1106,7 +1107,7 @@ nms_keyfile_plugin_set_nmmeta_tombstone(NMSKeyfilePlugin *  self,
     const char *                       dirname;
 
     nm_assert(NMS_IS_KEYFILE_PLUGIN(self));
-    nm_assert(nm_utils_is_uuid(uuid));
+    nm_assert(nm_uuid_is_normalized(uuid));
     nm_assert(!out_storage || !*out_storage);
     nm_assert(!shadowed_storage || (set && in_memory));
 
@@ -1246,9 +1247,9 @@ nms_keyfile_plugin_init(NMSKeyfilePlugin *plugin)
     /* dirname_libs are a set of read-only directories with lower priority than /etc or /run.
      * There is nothing complicated about having multiple of such directories, so dirname_libs
      * is a list (which currently only has at most one directory). */
-    priv->dirname_libs[0] = nm_sd_utils_path_simplify(g_strdup(NM_KEYFILE_PATH_NAME_LIB), FALSE);
+    priv->dirname_libs[0] = nm_sd_utils_path_simplify(g_strdup(NM_KEYFILE_PATH_NAME_LIB));
     priv->dirname_libs[1] = NULL;
-    priv->dirname_run     = nm_sd_utils_path_simplify(g_strdup(NM_KEYFILE_PATH_NAME_RUN), FALSE);
+    priv->dirname_run     = nm_sd_utils_path_simplify(g_strdup(NM_KEYFILE_PATH_NAME_RUN));
     priv->dirname_etc     = nm_config_data_get_value(NM_CONFIG_GET_DATA_ORIG,
                                                  NM_CONFIG_KEYFILE_GROUP_KEYFILE,
                                                  NM_CONFIG_KEYFILE_KEY_KEYFILE_PATH,
@@ -1261,10 +1262,9 @@ nms_keyfile_plugin_init(NMSKeyfilePlugin *plugin)
     } else if (!priv->dirname_etc || priv->dirname_etc[0] != '/') {
         /* either invalid path or unspecified. Use the default. */
         g_free(priv->dirname_etc);
-        priv->dirname_etc =
-            nm_sd_utils_path_simplify(g_strdup(NM_KEYFILE_PATH_NAME_ETC_DEFAULT), FALSE);
+        priv->dirname_etc = nm_sd_utils_path_simplify(g_strdup(NM_KEYFILE_PATH_NAME_ETC_DEFAULT));
     } else
-        nm_sd_utils_path_simplify(priv->dirname_etc, FALSE);
+        nm_sd_utils_path_simplify(priv->dirname_etc);
 
     /* no duplicates */
     if (NM_IN_STRSET(priv->dirname_libs[0], priv->dirname_etc, priv->dirname_run))
diff --git a/src/core/settings/plugins/keyfile/nms-keyfile-reader.c b/src/core/settings/plugins/keyfile/nms-keyfile-reader.c
index 70b047f3..8b9d982f 100644
--- a/src/core/settings/plugins/keyfile/nms-keyfile-reader.c
+++ b/src/core/settings/plugins/keyfile/nms-keyfile-reader.c
@@ -9,7 +9,7 @@
 
 #include <sys/stat.h>
 
-#include "nm-keyfile-internal.h"
+#include "libnm-core-intern/nm-keyfile-internal.h"
 
 #include "NetworkManagerUtils.h"
 #include "nms-keyfile-utils.h"
diff --git a/src/core/settings/plugins/keyfile/nms-keyfile-storage.c b/src/core/settings/plugins/keyfile/nms-keyfile-storage.c
index 89be817a..8c526c81 100644
--- a/src/core/settings/plugins/keyfile/nms-keyfile-storage.c
+++ b/src/core/settings/plugins/keyfile/nms-keyfile-storage.c
@@ -7,8 +7,9 @@
 
 #include "nms-keyfile-storage.h"
 
+#include "libnm-glib-aux/nm-uuid.h"
 #include "nm-utils.h"
-#include "nm-core-internal.h"
+#include "libnm-core-intern/nm-core-internal.h"
 #include "nms-keyfile-plugin.h"
 
 /*****************************************************************************/
@@ -108,7 +109,7 @@ _storage_new(NMSKeyfilePlugin *    plugin,
     NMSKeyfileStorage *self;
 
     nm_assert(NMS_IS_KEYFILE_PLUGIN(plugin));
-    nm_assert(nm_utils_is_uuid(uuid));
+    nm_assert(nm_uuid_is_normalized(uuid));
     nm_assert(filename && filename[0] == '/');
 
     self = g_object_new(NMS_TYPE_KEYFILE_STORAGE,
@@ -135,7 +136,7 @@ nms_keyfile_storage_new_tombstone(NMSKeyfilePlugin *    plugin,
 {
     NMSKeyfileStorage *self;
 
-    nm_assert(nm_utils_is_uuid(uuid));
+    nm_assert(nm_uuid_is_normalized(uuid));
     nm_assert(filename && filename[0] == '/');
     nm_assert(nms_keyfile_nmmeta_check_filename(filename, NULL));
     nm_assert(NM_IN_SET(storage_type, NMS_KEYFILE_STORAGE_TYPE_ETC, NMS_KEYFILE_STORAGE_TYPE_RUN));
diff --git a/src/core/settings/plugins/keyfile/nms-keyfile-utils.c b/src/core/settings/plugins/keyfile/nms-keyfile-utils.c
index ca1233f5..e2c68bbe 100644
--- a/src/core/settings/plugins/keyfile/nms-keyfile-utils.c
+++ b/src/core/settings/plugins/keyfile/nms-keyfile-utils.c
@@ -10,8 +10,9 @@
 #include <stdlib.h>
 #include <sys/stat.h>
 
-#include "nm-glib-aux/nm-io-utils.h"
-#include "nm-keyfile-internal.h"
+#include "libnm-glib-aux/nm-uuid.h"
+#include "libnm-glib-aux/nm-io-utils.h"
+#include "libnm-core-intern/nm-keyfile-internal.h"
 #include "nm-utils.h"
 #include "nm-setting-wired.h"
 #include "nm-setting-wireless.h"
@@ -30,9 +31,9 @@
 const char *
 nms_keyfile_nmmeta_check_filename(const char *filename, guint *out_uuid_len)
 {
-    const char *uuid;
     const char *s;
     gsize       len;
+    char        uuid[37];
 
     s = strrchr(filename, '/');
     if (s)
@@ -50,17 +51,18 @@ nms_keyfile_nmmeta_check_filename(const char *filename, guint *out_uuid_len)
 
     len -= NM_STRLEN(NM_KEYFILE_PATH_SUFFIX_NMMETA);
 
-    if (!NM_IN_SET(len, 36, 40)) {
+    if (len != 36) {
         /* the remaining part of the filename has not the right length to
-         * contain a UUID (according to nm_utils_is_uuid()). */
+         * contain a UUID (according to nm_uuid_is_normalized()). */
         return NULL;
     }
 
-    uuid = nm_strndup_a(100, filename, len, NULL);
-    if (!nm_utils_is_uuid(uuid))
+    memcpy(uuid, filename, 36);
+    uuid[36] = '\0';
+    if (!nm_uuid_is_normalized(uuid))
         return NULL;
 
-    NM_SET_OUT(out_uuid_len, len);
+    NM_SET_OUT(out_uuid_len, 36);
     return filename;
 }
 
@@ -71,7 +73,7 @@ nms_keyfile_nmmeta_filename(const char *dirname, const char *uuid, gboolean temp
     char *s;
 
     nm_assert(dirname && dirname[0] == '/');
-    nm_assert(nm_utils_is_uuid(uuid) && !strchr(uuid, '/'));
+    nm_assert(nm_uuid_is_normalized(uuid) && !strchr(uuid, '/'));
 
     if (g_snprintf(filename,
                    sizeof(filename),
@@ -80,7 +82,7 @@ nms_keyfile_nmmeta_filename(const char *dirname, const char *uuid, gboolean temp
                    NM_KEYFILE_PATH_SUFFIX_NMMETA,
                    temporary ? "~" : "")
         >= sizeof(filename)) {
-        /* valid uuids are limited in length (nm_utils_is_uuid). The buffer should always
+        /* valid uuids are limited in length (nm_uuid_is_normalized). The buffer should always
          * be large enough. */
         nm_assert_not_reached();
     }
@@ -215,7 +217,7 @@ nms_keyfile_nmmeta_write(const char *dirname,
     int           errsv;
 
     nm_assert(dirname && dirname[0] == '/');
-    nm_assert(nm_utils_is_uuid(uuid) && !strchr(uuid, '/'));
+    nm_assert(nm_uuid_is_normalized(uuid) && !strchr(uuid, '/'));
     nm_assert(!loaded_path || loaded_path[0] == '/');
     nm_assert(!shadowed_storage || loaded_path);
 
@@ -273,7 +275,13 @@ nms_keyfile_nmmeta_write(const char *dirname,
 
         contents = g_key_file_to_data(kf, &length, NULL);
 
-        if (!nm_utils_file_set_contents(full_filename, contents, length, 0600, &errsv, NULL)) {
+        if (!nm_utils_file_set_contents(full_filename,
+                                        contents,
+                                        length,
+                                        0600,
+                                        NULL,
+                                        &errsv,
+                                        NULL)) {
             NM_SET_OUT(out_full_filename, g_steal_pointer(&full_filename_tmp));
             return -NM_ERRNO_NATIVE(errsv);
         }
diff --git a/src/core/settings/plugins/keyfile/nms-keyfile-writer.c b/src/core/settings/plugins/keyfile/nms-keyfile-writer.c
index 0e2c94b5..70afdc23 100644
--- a/src/core/settings/plugins/keyfile/nms-keyfile-writer.c
+++ b/src/core/settings/plugins/keyfile/nms-keyfile-writer.c
@@ -12,12 +12,12 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
-#include "nm-keyfile-internal.h"
+#include "libnm-core-intern/nm-keyfile-internal.h"
 
 #include "nms-keyfile-utils.h"
 #include "nms-keyfile-reader.h"
 
-#include "nm-glib-aux/nm-io-utils.h"
+#include "libnm-glib-aux/nm-io-utils.h"
 
 /*****************************************************************************/
 
@@ -125,13 +125,14 @@ cert_writer(NMConnection *                    connection,
 
         /* FIXME(keyfile-parse-in-memory): writer must not access/write to the file system before
          * being sure that the entire profile can be written and all circumstances are good to
-         * proceed. That means, while writing we must only collect the blogs in-memory, and write
+         * proceed. That means, while writing we must only collect the blobs in-memory, and write
          * them all in the end together (or not at all). */
         success = nm_utils_file_set_contents(new_path,
                                              (const char *) blob_data,
                                              blob_len,
                                              0600,
                                              NULL,
+                                             NULL,
                                              &local);
         if (success) {
             /* Write the path value to the keyfile.
@@ -378,7 +379,7 @@ _internal_write_connection(NMConnection *                  connection,
         }
     }
 
-    nm_utils_file_set_contents(path, kf_content_buf, kf_content_len, 0600, NULL, &local_err);
+    nm_utils_file_set_contents(path, kf_content_buf, kf_content_len, 0600, NULL, NULL, &local_err);
     if (local_err) {
         g_set_error(error,
                     NM_SETTINGS_ERROR,
diff --git a/src/core/settings/plugins/keyfile/tests/test-keyfile-settings.c b/src/core/settings/plugins/keyfile/tests/test-keyfile-settings.c
index 348b0ed6..a13c2586 100644
--- a/src/core/settings/plugins/keyfile/tests/test-keyfile-settings.c
+++ b/src/core/settings/plugins/keyfile/tests/test-keyfile-settings.c
@@ -15,7 +15,8 @@
 #include <linux/if_ether.h>
 #include <linux/if_infiniband.h>
 
-#include "nm-core-internal.h"
+#include "libnm-glib-aux/nm-uuid.h"
+#include "libnm-core-intern/nm-core-internal.h"
 
 #include "settings/plugins/keyfile/nms-keyfile-reader.h"
 #include "settings/plugins/keyfile/nms-keyfile-writer.h"
@@ -2289,7 +2290,7 @@ test_read_missing_id_uuid(void)
     gs_free char *                expected_uuid = NULL;
     const char *                  FILENAME      = TEST_KEYFILES_DIR "/Test_Missing_ID_UUID";
 
-    expected_uuid = _nm_utils_uuid_generate_from_strings("keyfile", FILENAME, NULL);
+    expected_uuid = nm_uuid_generate_from_strings("keyfile", FILENAME, NULL);
 
     connection = keyfile_read_connection_from_file(FILENAME);