summary refs log tree commit diff
path: root/src/core/devices
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/devices')
-rw-r--r--src/core/devices/adsl/nm-device-adsl.c2
-rw-r--r--src/core/devices/bluetooth/nm-bluez-manager.c163
-rw-r--r--src/core/devices/bluetooth/nm-bluez5-dun.c44
-rw-r--r--src/core/devices/bluetooth/nm-device-bt.c31
-rw-r--r--src/core/devices/bluetooth/tests/nm-bt-test.c2
-rw-r--r--src/core/devices/nm-acd-manager.c8
-rw-r--r--src/core/devices/nm-device-6lowpan.c8
-rw-r--r--src/core/devices/nm-device-bond.c99
-rw-r--r--src/core/devices/nm-device-bridge.c22
-rw-r--r--src/core/devices/nm-device-dummy.c36
-rw-r--r--src/core/devices/nm-device-ethernet.c36
-rw-r--r--src/core/devices/nm-device-infiniband.c21
-rw-r--r--src/core/devices/nm-device-ip-tunnel.c13
-rw-r--r--src/core/devices/nm-device-logging.h12
-rw-r--r--src/core/devices/nm-device-macvlan.c13
-rw-r--r--src/core/devices/nm-device-ppp.c57
-rw-r--r--src/core/devices/nm-device-private.h2
-rw-r--r--src/core/devices/nm-device-tun.c7
-rw-r--r--src/core/devices/nm-device-utils.c27
-rw-r--r--src/core/devices/nm-device-utils.h16
-rw-r--r--src/core/devices/nm-device-vlan.c8
-rw-r--r--src/core/devices/nm-device-vrf.c8
-rw-r--r--src/core/devices/nm-device-vxlan.c12
-rw-r--r--src/core/devices/nm-device-wireguard.c11
-rw-r--r--src/core/devices/nm-device-wpan.c9
-rw-r--r--src/core/devices/nm-device.c776
-rw-r--r--src/core/devices/nm-device.h1
-rw-r--r--src/core/devices/nm-lldp-listener.c42
-rw-r--r--src/core/devices/ovs/nm-ovsdb.c141
-rw-r--r--src/core/devices/team/nm-device-team.c22
-rw-r--r--src/core/devices/wifi/nm-device-iwd.c11
-rw-r--r--src/core/devices/wifi/nm-device-olpc-mesh.c7
-rw-r--r--src/core/devices/wifi/nm-device-wifi-p2p.c44
-rw-r--r--src/core/devices/wifi/nm-device-wifi.c23
-rw-r--r--src/core/devices/wifi/nm-wifi-ap.c2
-rw-r--r--src/core/devices/wifi/nm-wifi-common.c2
-rw-r--r--src/core/devices/wifi/nm-wifi-p2p-peer.c18
-rw-r--r--src/core/devices/wifi/nm-wifi-utils.c27
-rw-r--r--src/core/devices/wifi/tests/test-devices-wifi.c2
-rw-r--r--src/core/devices/wwan/nm-device-modem.c22
-rw-r--r--src/core/devices/wwan/nm-modem-broadband.c7
-rw-r--r--src/core/devices/wwan/nm-modem-manager.c6
-rw-r--r--src/core/devices/wwan/nm-modem.c49
-rw-r--r--src/core/devices/wwan/nm-modem.h2
44 files changed, 934 insertions, 937 deletions
diff --git a/src/core/devices/adsl/nm-device-adsl.c b/src/core/devices/adsl/nm-device-adsl.c
index 4e4c9dcc..adcf8785 100644
--- a/src/core/devices/adsl/nm-device-adsl.c
+++ b/src/core/devices/adsl/nm-device-adsl.c
@@ -356,7 +356,7 @@ br2684_create_iface(NMDeviceAdsl *self)
             return FALSE;
         }
 
-        nm_utils_strdup_reset(&priv->nas_ifname, ni.ifname);
+        nm_strdup_reset(&priv->nas_ifname, ni.ifname);
         _LOGD(LOGD_ADSL, "waiting for br2684 iface '%s' to appear", priv->nas_ifname);
         priv->nas_update_count = 0;
         priv->nas_update_id    = g_timeout_add(100, nas_update_cb, self);
diff --git a/src/core/devices/bluetooth/nm-bluez-manager.c b/src/core/devices/bluetooth/nm-bluez-manager.c
index fc081580..36e442ad 100644
--- a/src/core/devices/bluetooth/nm-bluez-manager.c
+++ b/src/core/devices/bluetooth/nm-bluez-manager.c
@@ -31,9 +31,9 @@
 /*****************************************************************************/
 
 #if WITH_BLUEZ5_DUN
-    #define _NM_BT_CAPABILITY_SUPPORTED_DUN NM_BT_CAPABILITY_DUN
+#define _NM_BT_CAPABILITY_SUPPORTED_DUN NM_BT_CAPABILITY_DUN
 #else
-    #define _NM_BT_CAPABILITY_SUPPORTED_DUN NM_BT_CAPABILITY_NONE
+#define _NM_BT_CAPABILITY_SUPPORTED_DUN NM_BT_CAPABILITY_NONE
 #endif
 #define _NM_BT_CAPABILITY_SUPPORTED (NM_BT_CAPABILITY_NAP | _NM_BT_CAPABILITY_SUPPORTED_DUN)
 
@@ -382,64 +382,56 @@ _bzobj_to_string(const BzDBusObj *bzobj, char *buf, gsize len)
         buf[0] = '\0';
 
     if (bzobj->d_has_adapter_iface) {
-        nm_utils_strbuf_append_str(&buf, &len, prefix);
+        nm_strbuf_append_str(&buf, &len, prefix);
         prefix = ", ";
-        nm_utils_strbuf_append_str(&buf, &len, "Adapter1 {");
+        nm_strbuf_append_str(&buf, &len, "Adapter1 {");
         if (bzobj->d_adapter.address) {
-            nm_utils_strbuf_append(&buf, &len, " d.address: \"%s\"", bzobj->d_adapter.address);
+            nm_strbuf_append(&buf, &len, " d.address: \"%s\"", bzobj->d_adapter.address);
             if (bzobj->d_adapter_powered)
-                nm_utils_strbuf_append_str(&buf, &len, ",");
+                nm_strbuf_append_str(&buf, &len, ",");
         }
         if (bzobj->d_adapter_powered)
-            nm_utils_strbuf_append(&buf, &len, " d.powered: 1");
-        nm_utils_strbuf_append_str(&buf, &len, " }");
+            nm_strbuf_append(&buf, &len, " d.powered: 1");
+        nm_strbuf_append_str(&buf, &len, " }");
     }
 
     if (bzobj->d_has_device_iface) {
         const char *prefix1 = "";
 
-        nm_utils_strbuf_append_str(&buf, &len, prefix);
+        nm_strbuf_append_str(&buf, &len, prefix);
         prefix = ", ";
-        nm_utils_strbuf_append_str(&buf, &len, "Device1 {");
+        nm_strbuf_append_str(&buf, &len, "Device1 {");
         if (bzobj->d_device.address) {
-            nm_utils_strbuf_append(&buf,
-                                   &len,
-                                   "%s d.address: \"%s\"",
-                                   prefix1,
-                                   bzobj->d_device.address);
+            nm_strbuf_append(&buf, &len, "%s d.address: \"%s\"", prefix1, bzobj->d_device.address);
             prefix1 = ",";
         }
         if (bzobj->d_device.name) {
-            nm_utils_strbuf_append(&buf, &len, "%s d.name: \"%s\"", prefix1, bzobj->d_device.name);
+            nm_strbuf_append(&buf, &len, "%s d.name: \"%s\"", prefix1, bzobj->d_device.name);
             prefix1 = ",";
         }
         if (bzobj->d_device.adapter) {
-            nm_utils_strbuf_append(&buf,
-                                   &len,
-                                   "%s d.adapter: \"%s\"",
-                                   prefix1,
-                                   bzobj->d_device.adapter);
+            nm_strbuf_append(&buf, &len, "%s d.adapter: \"%s\"", prefix1, bzobj->d_device.adapter);
             prefix1 = ",";
         }
         if (bzobj->d_device_capabilities != NM_BT_CAPABILITY_NONE) {
-            nm_utils_strbuf_append(&buf,
-                                   &len,
-                                   "%s d.capabilities: \"%s\"",
-                                   prefix1,
-                                   nm_bluetooth_capability_to_string(bzobj->d_device_capabilities,
-                                                                     sbuf_cap,
-                                                                     sizeof(sbuf_cap)));
+            nm_strbuf_append(&buf,
+                             &len,
+                             "%s d.capabilities: \"%s\"",
+                             prefix1,
+                             nm_bluetooth_capability_to_string(bzobj->d_device_capabilities,
+                                                               sbuf_cap,
+                                                               sizeof(sbuf_cap)));
             prefix1 = ",";
         }
         if (bzobj->d_device_connected) {
-            nm_utils_strbuf_append(&buf, &len, "%s d.connected: 1", prefix1);
+            nm_strbuf_append(&buf, &len, "%s d.connected: 1", prefix1);
             prefix1 = ",";
         }
         if (bzobj->d_device_paired) {
-            nm_utils_strbuf_append(&buf, &len, "%s d.paired: 1", prefix1);
+            nm_strbuf_append(&buf, &len, "%s d.paired: 1", prefix1);
             prefix1 = ",";
         }
-        nm_utils_strbuf_append_str(&buf, &len, " }");
+        nm_strbuf_append_str(&buf, &len, " }");
     }
 
     network_server_is_usable = _bzobjs_network_server_is_usable(bzobj, TRUE);
@@ -450,43 +442,43 @@ _bzobj_to_string(const BzDBusObj *bzobj, char *buf, gsize len)
         || !nm_streq0(bzobj->d_has_adapter_iface ? bzobj->d_adapter.address : NULL,
                       bzobj->x_network_server.adapter_address)
         || bzobj->x_network_server.device_br || bzobj->x_network_server.r_req_data) {
-        nm_utils_strbuf_append_str(&buf, &len, prefix);
+        nm_strbuf_append_str(&buf, &len, prefix);
         prefix = ", ";
 
-        nm_utils_strbuf_append(&buf, &len, "NetworkServer1 { ");
+        nm_strbuf_append(&buf, &len, "NetworkServer1 { ");
 
         if (!bzobj->d_has_network_server_iface)
-            nm_utils_strbuf_append(&buf, &len, " has-d-iface: 0, ");
+            nm_strbuf_append(&buf, &len, " has-d-iface: 0, ");
 
         if (network_server_is_usable != (!c_list_is_empty(&bzobj->x_network_server.lst)))
-            nm_utils_strbuf_append(&buf,
-                                   &len,
-                                   "usable: %d, used: %d",
-                                   !!network_server_is_usable,
-                                   !network_server_is_usable);
+            nm_strbuf_append(&buf,
+                             &len,
+                             "usable: %d, used: %d",
+                             !!network_server_is_usable,
+                             !network_server_is_usable);
         else if (network_server_is_usable)
-            nm_utils_strbuf_append(&buf, &len, "used: 1");
+            nm_strbuf_append(&buf, &len, "used: 1");
         else
-            nm_utils_strbuf_append(&buf, &len, "usable: 0");
+            nm_strbuf_append(&buf, &len, "usable: 0");
 
         if (!nm_streq0(bzobj->d_has_adapter_iface ? bzobj->d_adapter.address : NULL,
                        bzobj->x_network_server.adapter_address)) {
             if (bzobj->x_network_server.adapter_address)
-                nm_utils_strbuf_append(&buf,
-                                       &len,
-                                       ", adapter-address: \"%s\"",
-                                       bzobj->x_network_server.adapter_address);
+                nm_strbuf_append(&buf,
+                                 &len,
+                                 ", adapter-address: \"%s\"",
+                                 bzobj->x_network_server.adapter_address);
             else
-                nm_utils_strbuf_append(&buf, &len, ", adapter-address: <NULL>");
+                nm_strbuf_append(&buf, &len, ", adapter-address: <NULL>");
         }
 
         if (bzobj->x_network_server.device_br)
-            nm_utils_strbuf_append(&buf, &len, ", bridge-device: 1");
+            nm_strbuf_append(&buf, &len, ", bridge-device: 1");
 
         if (bzobj->x_network_server.r_req_data)
-            nm_utils_strbuf_append(&buf, &len, ", register-in-progress: 1");
+            nm_strbuf_append(&buf, &len, ", register-in-progress: 1");
 
-        nm_utils_strbuf_append_str(&buf, &len, " }");
+        nm_strbuf_append_str(&buf, &len, " }");
     }
 
     device_is_usable = _bzobjs_device_is_usable(bzobj, NULL, &create_panu_connection);
@@ -497,76 +489,72 @@ _bzobj_to_string(const BzDBusObj *bzobj, char *buf, gsize len)
         || bzobj->x_device_connect_bt_type != NM_BT_CAPABILITY_NONE
         || bzobj->x_device.connect_dun_context || bzobj->x_device.c_req_data
         || bzobj->x_device_is_connected != bzobj->d_network_connected) {
-        nm_utils_strbuf_append_str(&buf, &len, prefix);
+        nm_strbuf_append_str(&buf, &len, prefix);
         prefix = ", ";
-        nm_utils_strbuf_append_str(&buf, &len, "Network1 {");
+        nm_strbuf_append_str(&buf, &len, "Network1 {");
         if (bzobj->d_network.interface)
-            nm_utils_strbuf_append(&buf,
-                                   &len,
-                                   " d.interface: \"%s\", ",
-                                   bzobj->d_network.interface);
+            nm_strbuf_append(&buf, &len, " d.interface: \"%s\", ", bzobj->d_network.interface);
         if (bzobj->d_network_connected)
-            nm_utils_strbuf_append(&buf, &len, " d.connected: %d, ", !!bzobj->d_network_connected);
+            nm_strbuf_append(&buf, &len, " d.connected: %d, ", !!bzobj->d_network_connected);
         if (!bzobj->d_has_network_iface)
-            nm_utils_strbuf_append(&buf, &len, " has-d-iface: 0, ");
+            nm_strbuf_append(&buf, &len, " has-d-iface: 0, ");
         if (device_is_usable != bzobj->x_device_is_usable)
-            nm_utils_strbuf_append(&buf,
-                                   &len,
-                                   " usable: %d, used: %d",
-                                   !!device_is_usable,
-                                   !device_is_usable);
+            nm_strbuf_append(&buf,
+                             &len,
+                             " usable: %d, used: %d",
+                             !!device_is_usable,
+                             !device_is_usable);
         else if (device_is_usable)
-            nm_utils_strbuf_append(&buf, &len, " used: 1");
+            nm_strbuf_append(&buf, &len, " used: 1");
         else
-            nm_utils_strbuf_append(&buf, &len, " usable: 0");
+            nm_strbuf_append(&buf, &len, " usable: 0");
 
         if (create_panu_connection)
-            nm_utils_strbuf_append(&buf, &len, ", create-panu-connection: 1");
+            nm_strbuf_append(&buf, &len, ", create-panu-connection: 1");
 
         if (bzobj->x_device.panu_connection)
-            nm_utils_strbuf_append(&buf, &len, ", has-panu-connection: 1");
+            nm_strbuf_append(&buf, &len, ", has-panu-connection: 1");
 
         if (bzobj->x_device.device_bt)
-            nm_utils_strbuf_append(&buf, &len, ", has-device: 1");
+            nm_strbuf_append(&buf, &len, ", has-device: 1");
 
         if (bzobj->x_device_connect_bt_type != NM_BT_CAPABILITY_NONE
             || bzobj->x_device.connect_dun_context) {
-            nm_utils_strbuf_append(
-                &buf,
-                &len,
-                ", connect: %s%s",
-                nm_bluetooth_capability_to_string(bzobj->x_device_connect_bt_type,
-                                                  sbuf_cap,
-                                                  sizeof(sbuf_cap)),
-                bzobj->x_device.connect_dun_context ? ",with-dun-context" : "");
+            nm_strbuf_append(&buf,
+                             &len,
+                             ", connect: %s%s",
+                             nm_bluetooth_capability_to_string(bzobj->x_device_connect_bt_type,
+                                                               sbuf_cap,
+                                                               sizeof(sbuf_cap)),
+                             bzobj->x_device.connect_dun_context ? ",with-dun-context" : "");
         }
 
         if (bzobj->x_device.c_req_data)
-            nm_utils_strbuf_append(&buf, &len, ", connecting: 1");
+            nm_strbuf_append(&buf, &len, ", connecting: 1");
 
         if (bzobj->x_device_is_connected != bzobj->d_network_connected)
-            nm_utils_strbuf_append(&buf, &len, ", connected: %d", !!bzobj->x_device_is_connected);
+            nm_strbuf_append(&buf, &len, ", connected: %d", !!bzobj->x_device_is_connected);
 
-        nm_utils_strbuf_append_str(&buf, &len, " }");
+        nm_strbuf_append_str(&buf, &len, " }");
     }
 
     if (_bzobjs_is_dead(bzobj)) {
-        nm_utils_strbuf_append_str(&buf, &len, prefix);
+        nm_strbuf_append_str(&buf, &len, prefix);
         prefix = ", ";
-        nm_utils_strbuf_append_str(&buf, &len, "dead: 1");
+        nm_strbuf_append_str(&buf, &len, "dead: 1");
     }
 
     if (!c_list_is_empty(&bzobj->process_change_lst)) {
-        nm_utils_strbuf_append_str(&buf, &len, prefix);
+        nm_strbuf_append_str(&buf, &len, prefix);
         prefix = ", ";
-        nm_utils_strbuf_append(&buf, &len, "change-pending-on-idle: 1");
+        nm_strbuf_append(&buf, &len, "change-pending-on-idle: 1");
     }
 
     if (_bzobjs_adapter_is_usable_for_device(bzobj)
         != bzobj->was_usable_adapter_for_device_before) {
-        nm_utils_strbuf_append_str(&buf, &len, prefix);
+        nm_strbuf_append_str(&buf, &len, prefix);
         prefix = ", ";
-        nm_utils_strbuf_append(&buf, &len, "change-usable-adapter-for-device: 1");
+        nm_strbuf_append(&buf, &len, "change-usable-adapter-for-device: 1");
     }
 
     return buf0;
@@ -2864,9 +2852,7 @@ dispose(GObject *object)
      *   BzDBusObj instances and do necessary cleanup actions (like disconnecting devices
      *   or deleting panu_connection). */
 
-    nm_assert(c_list_is_empty(&priv->network_server_lst_head));
-    nm_assert(c_list_is_empty(&priv->process_change_lst_head));
-    nm_assert(priv->process_change_idle_id == 0);
+    nm_clear_g_source(&priv->process_change_idle_id);
 
     g_atomic_pointer_compare_and_exchange(&nm_bt_vtable_network_server,
                                           &priv->vtable_network_server,
@@ -2883,6 +2869,9 @@ dispose(GObject *object)
     nm_clear_pointer(&priv->bzobjs, g_hash_table_destroy);
     nm_clear_pointer(&priv->conn_data_heads, g_hash_table_destroy);
     nm_clear_pointer(&priv->conn_data_elems, g_hash_table_destroy);
+
+    nm_assert(c_list_is_empty(&priv->network_server_lst_head));
+    nm_assert(c_list_is_empty(&priv->process_change_lst_head));
 }
 
 static void
diff --git a/src/core/devices/bluetooth/nm-bluez5-dun.c b/src/core/devices/bluetooth/nm-bluez5-dun.c
index e29884d8..7efbfaf4 100644
--- a/src/core/devices/bluetooth/nm-bluez5-dun.c
+++ b/src/core/devices/bluetooth/nm-bluez5-dun.c
@@ -182,25 +182,18 @@ _connect_open_tty(NMBluez5DunContext *context)
                   nm_strerror_native(errsv),
                   errsv);
             context->cdat->connect_open_tty_started_at = nm_utils_get_monotonic_timestamp_nsec();
-            context->cdat->source                      = nm_g_timeout_source_new(100,
-                                                            G_PRIORITY_DEFAULT,
-                                                            _connect_open_tty_retry_cb,
-                                                            context,
-                                                            NULL);
-            g_source_attach(context->cdat->source, NULL);
+            context->cdat->source =
+                nm_g_timeout_add_source(100, _connect_open_tty_retry_cb, context);
         }
         return -errsv;
     }
 
     context->rfcomm_tty_fd = fd;
 
-    context->rfcomm_tty_poll_source = nm_g_unix_fd_source_new(context->rfcomm_tty_fd,
+    context->rfcomm_tty_poll_source = nm_g_unix_fd_add_source(context->rfcomm_tty_fd,
                                                               G_IO_ERR | G_IO_HUP,
-                                                              G_PRIORITY_DEFAULT,
                                                               _rfcomm_tty_poll_cb,
-                                                              context,
-                                                              NULL);
-    g_source_attach(context->rfcomm_tty_poll_source, NULL);
+                                                              context);
 
     _context_invoke_callback_success(context);
     return 0;
@@ -369,13 +362,10 @@ _connect_socket_connect(NMBluez5DunContext *context)
               context->dst_str,
               context->rfcomm_channel);
 
-        context->cdat->source = nm_g_unix_fd_source_new(context->rfcomm_sock_fd,
+        context->cdat->source = nm_g_unix_fd_add_source(context->rfcomm_sock_fd,
                                                         G_IO_OUT,
-                                                        G_PRIORITY_DEFAULT,
                                                         _connect_socket_connect_cb,
-                                                        context,
-                                                        NULL);
-        g_source_attach(context->cdat->source, NULL);
+                                                        context);
         return;
     }
 
@@ -567,12 +557,8 @@ _connect_sdp_io_cb(int fd, GIOCondition condition, gpointer user_data)
                   nm_strerror_native(errsv),
                   errsv);
             nm_clear_g_source_inst(&context->cdat->source);
-            context->cdat->source = nm_g_timeout_source_new(1000,
-                                                            G_PRIORITY_DEFAULT,
-                                                            _connect_sdp_session_start_on_idle_cb,
-                                                            context,
-                                                            NULL);
-            g_source_attach(context->cdat->source, NULL);
+            context->cdat->source =
+                nm_g_timeout_add_source(1000, _connect_sdp_session_start_on_idle_cb, context);
             return G_SOURCE_REMOVE;
         }
 
@@ -616,13 +602,10 @@ _connect_sdp_io_cb(int fd, GIOCondition condition, gpointer user_data)
     }
 
     /* Set callback responsible for update the internal SDP transaction */
-    context->cdat->source = nm_g_unix_fd_source_new(fd,
+    context->cdat->source = nm_g_unix_fd_add_source(fd,
                                                     G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
-                                                    G_PRIORITY_DEFAULT,
                                                     _connect_sdp_search_io_cb,
-                                                    context,
-                                                    NULL);
-    g_source_attach(context->cdat->source, NULL);
+                                                    context);
 
 done:
     if (error)
@@ -664,13 +647,10 @@ _connect_sdp_session_start(NMBluez5DunContext *context, GError **error)
         return FALSE;
     }
 
-    context->cdat->source = nm_g_unix_fd_source_new(sdp_get_socket(context->cdat->sdp_session),
+    context->cdat->source = nm_g_unix_fd_add_source(sdp_get_socket(context->cdat->sdp_session),
                                                     G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
-                                                    G_PRIORITY_DEFAULT,
                                                     _connect_sdp_io_cb,
-                                                    context,
-                                                    NULL);
-    g_source_attach(context->cdat->source, NULL);
+                                                    context);
     return TRUE;
 }
 
diff --git a/src/core/devices/bluetooth/nm-device-bt.c b/src/core/devices/bluetooth/nm-device-bt.c
index 8f4ceba7..3dbfbbe0 100644
--- a/src/core/devices/bluetooth/nm-device-bt.c
+++ b/src/core/devices/bluetooth/nm-device-bt.c
@@ -10,6 +10,7 @@
 #include <stdio.h>
 #include <linux/if_ether.h>
 
+#include "libnm-core-aux-intern/nm-libnm-core-utils.h"
 #include "libnm-core-intern/nm-core-internal.h"
 #include "nm-bluez-common.h"
 #include "nm-bluez-manager.h"
@@ -273,11 +274,7 @@ complete_connection(NMDevice *           device,
     s_serial = nm_connection_get_setting_serial(connection);
     s_ppp    = nm_connection_get_setting_ppp(connection);
 
-    s_bt = nm_connection_get_setting_bluetooth(connection);
-    if (!s_bt) {
-        s_bt = (NMSettingBluetooth *) nm_setting_bluetooth_new();
-        nm_connection_add_setting(connection, NM_SETTING(s_bt));
-    }
+    s_bt = _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_BLUETOOTH);
 
     ctype = nm_setting_bluetooth_get_connection_type(s_bt);
     if (ctype) {
@@ -1007,21 +1004,21 @@ act_stage3_ip_config_start(NMDevice *           device,
                            gpointer *           out_config,
                            NMDeviceStateReason *out_failure_reason)
 {
-    NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE(device);
+    NMDeviceBtPrivate *priv    = NM_DEVICE_BT_GET_PRIVATE(device);
+    gboolean           autoip4 = FALSE;
+    NMActStageReturn   ret;
 
-    nm_assert_addr_family(addr_family);
+    if (priv->connect_bt_type != NM_BT_CAPABILITY_DUN)
+        goto out_chain_up;
 
-    if (priv->connect_bt_type == NM_BT_CAPABILITY_DUN) {
-        if (addr_family == AF_INET) {
-            return nm_modem_stage3_ip4_config_start(priv->modem,
-                                                    device,
-                                                    NM_DEVICE_CLASS(nm_device_bt_parent_class),
-                                                    out_failure_reason);
-        } else {
-            return nm_modem_stage3_ip6_config_start(priv->modem, device, out_failure_reason);
-        }
-    }
+    if (!NM_IS_IPv4(addr_family))
+        return nm_modem_stage3_ip6_config_start(priv->modem, device, out_failure_reason);
+
+    ret = nm_modem_stage3_ip4_config_start(priv->modem, device, &autoip4, out_failure_reason);
+    if (ret != NM_ACT_STAGE_RETURN_SUCCESS || !autoip4)
+        return ret;
 
+out_chain_up:
     return NM_DEVICE_CLASS(nm_device_bt_parent_class)
         ->act_stage3_ip_config_start(device, addr_family, out_config, out_failure_reason);
 }
diff --git a/src/core/devices/bluetooth/tests/nm-bt-test.c b/src/core/devices/bluetooth/tests/nm-bt-test.c
index 0fc8aa87..548e7249 100644
--- a/src/core/devices/bluetooth/tests/nm-bt-test.c
+++ b/src/core/devices/bluetooth/tests/nm-bt-test.c
@@ -2,8 +2,6 @@
 
 #include "src/core/nm-default-daemon.h"
 
-#include <glib-unix.h>
-
 #include "devices/bluetooth/nm-bluez5-dun.h"
 
 #include "nm-test-utils-core.h"
diff --git a/src/core/devices/nm-acd-manager.c b/src/core/devices/nm-acd-manager.c
index 145947bb..c041163e 100644
--- a/src/core/devices/nm-acd-manager.c
+++ b/src/core/devices/nm-acd-manager.c
@@ -349,9 +349,7 @@ nm_acd_manager_start_probe(NMAcdManager *self, guint timeout)
 
     nm_assert(!self->event_source);
     n_acd_get_fd(self->acd, &fd);
-    self->event_source =
-        nm_g_unix_fd_source_new(fd, G_IO_IN, G_PRIORITY_DEFAULT, acd_event, self, NULL);
-    g_source_attach(self->event_source, NULL);
+    self->event_source = nm_g_unix_fd_add_source(fd, G_IO_IN, acd_event, self);
 
     return success ? 0 : -NME_UNSPEC;
 }
@@ -439,9 +437,7 @@ nm_acd_manager_announce_addresses(NMAcdManager *self)
 
     if (!self->event_source) {
         n_acd_get_fd(self->acd, &fd);
-        self->event_source =
-            nm_g_unix_fd_source_new(fd, G_IO_IN, G_PRIORITY_DEFAULT, acd_event, self, NULL);
-        g_source_attach(self->event_source, NULL);
+        self->event_source = nm_g_unix_fd_add_source(fd, G_IO_IN, acd_event, self);
     }
 
     return success ? 0 : -NME_UNSPEC;
diff --git a/src/core/devices/nm-device-6lowpan.c b/src/core/devices/nm-device-6lowpan.c
index 96218658..8b77c22c 100644
--- a/src/core/devices/nm-device-6lowpan.c
+++ b/src/core/devices/nm-device-6lowpan.c
@@ -9,6 +9,7 @@
 
 #include "nm-device-private.h"
 #include "settings/nm-settings.h"
+#include "libnm-core-aux-intern/nm-libnm-core-utils.h"
 #include "libnm-platform/nm-platform.h"
 #include "nm-device-factory.h"
 #include "nm-setting-6lowpan.h"
@@ -221,12 +222,7 @@ static void
 update_connection(NMDevice *device, NMConnection *connection)
 {
     NMSetting6Lowpan *s_6lowpan =
-        NM_SETTING_6LOWPAN(nm_connection_get_setting(connection, NM_TYPE_SETTING_6LOWPAN));
-
-    if (!s_6lowpan) {
-        s_6lowpan = (NMSetting6Lowpan *) nm_setting_6lowpan_new();
-        nm_connection_add_setting(connection, (NMSetting *) s_6lowpan);
-    }
+        _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_6LOWPAN);
 
     g_object_set(
         s_6lowpan,
diff --git a/src/core/devices/nm-device-bond.c b/src/core/devices/nm-device-bond.c
index b208e2c0..f7d78f57 100644
--- a/src/core/devices/nm-device-bond.c
+++ b/src/core/devices/nm-device-bond.c
@@ -14,8 +14,10 @@
 #include "nm-device-private.h"
 #include "libnm-platform/nm-platform.h"
 #include "nm-device-factory.h"
+#include "libnm-core-aux-intern/nm-libnm-core-utils.h"
 #include "libnm-core-intern/nm-core-internal.h"
 #include "nm-ip4-config.h"
+#include "nm-setting-bond-port.h"
 
 #define _NMLOG_DEVICE_TYPE NMDeviceBond
 #include "nm-device-logging.h"
@@ -79,8 +81,6 @@ complete_connection(NMDevice *           device,
                     NMConnection *const *existing_connections,
                     GError **            error)
 {
-    NMSettingBond *s_bond;
-
     nm_utils_complete_generic(nm_device_get_platform(device),
                               connection,
                               NM_SETTING_BOND_SETTING_NAME,
@@ -91,11 +91,7 @@ complete_connection(NMDevice *           device,
                               NULL,
                               TRUE);
 
-    s_bond = nm_connection_get_setting_bond(connection);
-    if (!s_bond) {
-        s_bond = (NMSettingBond *) nm_setting_bond_new();
-        nm_connection_add_setting(connection, NM_SETTING(s_bond));
-    }
+    _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_BOND);
 
     return TRUE;
 }
@@ -168,16 +164,11 @@ static void
 update_connection(NMDevice *device, NMConnection *connection)
 {
     NMDeviceBond * self    = NM_DEVICE_BOND(device);
-    NMSettingBond *s_bond  = nm_connection_get_setting_bond(connection);
+    NMSettingBond *s_bond  = _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_BOND);
     int            ifindex = nm_device_get_ifindex(device);
     NMBondMode     mode    = NM_BOND_MODE_UNKNOWN;
     const char **  options;
 
-    if (!s_bond) {
-        s_bond = (NMSettingBond *) nm_setting_bond_new();
-        nm_connection_add_setting(connection, (NMSetting *) s_bond);
-    }
-
     /* Read bond options from sysfs and update the Bond setting to match */
     options = nm_setting_bond_get_valid_options(NULL);
     for (; options[0]; options++) {
@@ -225,11 +216,29 @@ update_connection(NMDevice *device, NMConnection *connection)
 }
 
 static gboolean
-master_update_slave_connection(NMDevice *    self,
-                               NMDevice *    slave,
-                               NMConnection *connection,
-                               GError **     error)
+controller_update_port_connection(NMDevice *    self,
+                                  NMDevice *    port,
+                                  NMConnection *connection,
+                                  GError **     error)
 {
+    NMSettingBondPort *s_port;
+    int                ifindex_port = nm_device_get_ifindex(port);
+    uint               queue_id     = NM_BOND_PORT_QUEUE_ID_DEF;
+    gs_free char *     queue_id_str = NULL;
+
+    g_return_val_if_fail(ifindex_port > 0, FALSE);
+
+    s_port = _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_BOND_PORT);
+
+    queue_id_str =
+        nm_platform_sysctl_slave_get_option(nm_device_get_platform(self), ifindex_port, "queue_id");
+    if (queue_id_str) {
+        queue_id =
+            _nm_utils_ascii_str_to_int64(queue_id_str, 10, 0, 65535, NM_BOND_PORT_QUEUE_ID_DEF);
+        g_object_set(s_port, NM_SETTING_BOND_PORT_QUEUE_ID, queue_id, NULL);
+    } else
+        _LOGW(LOGD_BOND, "failed to read bond port setting '%s'", NM_SETTING_BOND_PORT_QUEUE_ID);
+
     g_object_set(nm_connection_get_setting_connection(connection),
                  NM_SETTING_CONNECTION_MASTER,
                  nm_device_get_iface(self),
@@ -250,9 +259,8 @@ set_arp_targets(NMDevice *device, const char *cur_arp_ip_target, const char *new
     gsize                        i;
     gsize                        j;
 
-    cur_strv = nm_utils_strsplit_set_full(cur_arp_ip_target,
-                                          NM_ASCII_SPACES,
-                                          NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP);
+    cur_strv =
+        nm_strsplit_set_full(cur_arp_ip_target, NM_ASCII_SPACES, NM_STRSPLIT_SET_FLAGS_STRSTRIP);
     new_strv = nm_utils_bond_option_arp_ip_targets_split(new_arp_ip_target);
 
     cur_len = NM_PTRARRAY_LEN(cur_strv);
@@ -276,7 +284,7 @@ set_arp_targets(NMDevice *device, const char *cur_arp_ip_target, const char *new
                 }
             }
 
-            if (nm_utils_strv_find_first((char **) new_strv, i, s) < 0)
+            if (nm_strv_find_first(new_strv, i, s) < 0)
                 new_strv[j++] = s;
         }
         new_strv[j] = NULL;
@@ -286,7 +294,7 @@ set_arp_targets(NMDevice *device, const char *cur_arp_ip_target, const char *new
     if (cur_len == 0 && new_len == 0)
         return;
 
-    if (nm_utils_strv_equal(cur_strv, new_strv))
+    if (nm_strv_equal(cur_strv, new_strv))
         return;
 
     for (i = 0; i < cur_len; i++)
@@ -395,30 +403,57 @@ act_stage1_prepare(NMDevice *device, NMDeviceStateReason *out_failure_reason)
     return ret;
 }
 
+static void
+commit_port_options(NMDevice *bond_device, NMDevice *port, NMSettingBondPort *s_port)
+{
+    char queue_id_str[IFNAMSIZ + NM_STRLEN(":") + 5 + 100];
+
+    /*
+     * The queue-id of bond port is read only, we should modify bond interface using:
+     *    echo "eth1:2" > /sys/class/net/bond0/bonding/queue_id
+     * Kernel allows parital editing, so no need to care about other bond ports.
+     */
+    g_snprintf(queue_id_str,
+               sizeof(queue_id_str),
+               "%s:%" G_GUINT32_FORMAT,
+               nm_device_get_iface(port),
+               s_port ? nm_setting_bond_port_get_queue_id(s_port) : NM_BOND_PORT_QUEUE_ID_DEF);
+
+    nm_platform_sysctl_master_set_option(nm_device_get_platform(bond_device),
+                                         nm_device_get_ifindex(bond_device),
+                                         "queue_id",
+                                         queue_id_str);
+}
+
 static gboolean
-enslave_slave(NMDevice *device, NMDevice *slave, NMConnection *connection, gboolean configure)
+enslave_slave(NMDevice *device, NMDevice *port, NMConnection *connection, gboolean configure)
 {
-    NMDeviceBond *self = NM_DEVICE_BOND(device);
+    NMDeviceBond *     self = NM_DEVICE_BOND(device);
+    NMSettingBondPort *s_port;
 
-    nm_device_master_check_slave_physical_port(device, slave, LOGD_BOND);
+    nm_device_master_check_slave_physical_port(device, port, LOGD_BOND);
 
     if (configure) {
         gboolean success;
 
-        nm_device_take_down(slave, TRUE);
+        nm_device_take_down(port, TRUE);
         success = nm_platform_link_enslave(nm_device_get_platform(device),
                                            nm_device_get_ip_ifindex(device),
-                                           nm_device_get_ip_ifindex(slave));
-        nm_device_bring_up(slave, TRUE, NULL);
+                                           nm_device_get_ip_ifindex(port));
+        nm_device_bring_up(port, TRUE, NULL);
 
         if (!success) {
-            _LOGI(LOGD_BOND, "enslaved bond slave %s: failed", nm_device_get_ip_iface(slave));
+            _LOGI(LOGD_BOND, "assigning bond port %s: failed", nm_device_get_ip_iface(port));
             return FALSE;
         }
 
-        _LOGI(LOGD_BOND, "enslaved bond slave %s", nm_device_get_ip_iface(slave));
+        s_port = _nm_connection_get_setting(connection, NM_TYPE_SETTING_BOND_PORT);
+
+        commit_port_options(device, port, s_port);
+
+        _LOGI(LOGD_BOND, "assigned bond port %s", nm_device_get_ip_iface(port));
     } else
-        _LOGI(LOGD_BOND, "bond slave %s was enslaved", nm_device_get_ip_iface(slave));
+        _LOGI(LOGD_BOND, "bond port %s was assigned", nm_device_get_ip_iface(port));
 
     return TRUE;
 }
@@ -624,7 +659,7 @@ nm_device_bond_class_init(NMDeviceBondClass *klass)
     device_class->complete_connection      = complete_connection;
 
     device_class->update_connection              = update_connection;
-    device_class->master_update_slave_connection = master_update_slave_connection;
+    device_class->master_update_slave_connection = controller_update_port_connection;
 
     device_class->create_and_realize = create_and_realize;
     device_class->act_stage1_prepare = act_stage1_prepare;
diff --git a/src/core/devices/nm-device-bridge.c b/src/core/devices/nm-device-bridge.c
index a095dc3e..36a73978 100644
--- a/src/core/devices/nm-device-bridge.c
+++ b/src/core/devices/nm-device-bridge.c
@@ -14,6 +14,7 @@
 #include "nm-device-private.h"
 #include "libnm-platform/nm-platform.h"
 #include "nm-device-factory.h"
+#include "libnm-core-aux-intern/nm-libnm-core-utils.h"
 #include "libnm-core-intern/nm-core-internal.h"
 
 #define _NMLOG_DEVICE_TYPE NMDeviceBridge
@@ -145,8 +146,6 @@ complete_connection(NMDevice *           device,
                     NMConnection *const *existing_connections,
                     GError **            error)
 {
-    NMSettingBridge *s_bridge;
-
     nm_utils_complete_generic(nm_device_get_platform(device),
                               connection,
                               NM_SETTING_BRIDGE_SETTING_NAME,
@@ -157,11 +156,7 @@ complete_connection(NMDevice *           device,
                               NULL,
                               TRUE);
 
-    s_bridge = nm_connection_get_setting_bridge(connection);
-    if (!s_bridge) {
-        s_bridge = (NMSettingBridge *) nm_setting_bridge_new();
-        nm_connection_add_setting(connection, NM_SETTING(s_bridge));
-    }
+    _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_BRIDGE);
 
     return TRUE;
 }
@@ -576,17 +571,12 @@ static void
 update_connection(NMDevice *device, NMConnection *connection)
 {
     NMDeviceBridge * self     = NM_DEVICE_BRIDGE(device);
-    NMSettingBridge *s_bridge = nm_connection_get_setting_bridge(connection);
+    NMSettingBridge *s_bridge = _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_BRIDGE);
     int              ifindex  = nm_device_get_ifindex(device);
     const Option *   option;
     gs_free char *   stp = NULL;
     int              stp_value;
 
-    if (!s_bridge) {
-        s_bridge = (NMSettingBridge *) nm_setting_bridge_new();
-        nm_connection_add_setting(connection, (NMSetting *) s_bridge);
-    }
-
     option = master_options;
     nm_assert(nm_streq(option->sysname, "stp_state"));
 
@@ -690,11 +680,7 @@ master_update_slave_connection(NMDevice *    device,
     g_return_val_if_fail(ifindex_slave > 0, FALSE);
 
     s_con  = nm_connection_get_setting_connection(connection);
-    s_port = nm_connection_get_setting_bridge_port(connection);
-    if (!s_port) {
-        s_port = (NMSettingBridgePort *) nm_setting_bridge_port_new();
-        nm_connection_add_setting(connection, NM_SETTING(s_port));
-    }
+    s_port = _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_BRIDGE_PORT);
 
     for (option = slave_options; option->name; option++) {
         gs_free char *str = nm_platform_sysctl_slave_get_option(nm_device_get_platform(device),
diff --git a/src/core/devices/nm-device-dummy.c b/src/core/devices/nm-device-dummy.c
index 488f0ba0..4c12648f 100644
--- a/src/core/devices/nm-device-dummy.c
+++ b/src/core/devices/nm-device-dummy.c
@@ -16,6 +16,7 @@
 #include "libnm-platform/nm-platform.h"
 #include "nm-device-factory.h"
 #include "nm-setting-dummy.h"
+#include "libnm-core-aux-intern/nm-libnm-core-utils.h"
 #include "libnm-core-intern/nm-core-internal.h"
 
 #define _NMLOG_DEVICE_TYPE NMDeviceDummy
@@ -48,26 +49,16 @@ complete_connection(NMDevice *           device,
                     NMConnection *const *existing_connections,
                     GError **            error)
 {
-    NMSettingDummy *s_dummy;
+    nm_utils_complete_generic_with_params(nm_device_get_platform(device),
+                                          connection,
+                                          NM_SETTING_DUMMY_SETTING_NAME,
+                                          existing_connections,
+                                          NULL,
+                                          _("Dummy connection"),
+                                          NULL,
+                                          nm_device_get_ip_iface(device));
 
-    nm_utils_complete_generic(nm_device_get_platform(device),
-                              connection,
-                              NM_SETTING_DUMMY_SETTING_NAME,
-                              existing_connections,
-                              NULL,
-                              _("Dummy connection"),
-                              NULL,
-                              NULL,
-                              TRUE);
-
-    s_dummy = nm_connection_get_setting_dummy(connection);
-    if (!s_dummy) {
-        g_set_error_literal(error,
-                            NM_DEVICE_ERROR,
-                            NM_DEVICE_ERROR_INVALID_CONNECTION,
-                            "A 'dummy' setting is required.");
-        return FALSE;
-    }
+    _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_DUMMY);
 
     return TRUE;
 }
@@ -75,12 +66,7 @@ complete_connection(NMDevice *           device,
 static void
 update_connection(NMDevice *device, NMConnection *connection)
 {
-    NMSettingDummy *s_dummy = nm_connection_get_setting_dummy(connection);
-
-    if (!s_dummy) {
-        s_dummy = (NMSettingDummy *) nm_setting_dummy_new();
-        nm_connection_add_setting(connection, (NMSetting *) s_dummy);
-    }
+    _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_DUMMY);
 }
 
 static gboolean
diff --git a/src/core/devices/nm-device-ethernet.c b/src/core/devices/nm-device-ethernet.c
index 95336c7a..667ea9b9 100644
--- a/src/core/devices/nm-device-ethernet.c
+++ b/src/core/devices/nm-device-ethernet.c
@@ -33,6 +33,7 @@
 #include "nm-device-ethernet-utils.h"
 #include "settings/nm-settings.h"
 #include "nm-device-factory.h"
+#include "libnm-core-aux-intern/nm-libnm-core-utils.h"
 #include "libnm-core-intern/nm-core-internal.h"
 #include "NetworkManagerUtils.h"
 #include "libnm-udev-aux/nm-udev-utils.h"
@@ -1618,11 +1619,7 @@ complete_connection(NMDevice *           device,
                                   NULL,
                                   TRUE);
 
-        s_veth = _nm_connection_get_setting(connection, NM_TYPE_SETTING_VETH);
-        if (!s_veth) {
-            s_veth = (NMSettingVeth *) nm_setting_veth_new();
-            nm_connection_add_setting(connection, NM_SETTING(s_veth));
-        }
+        s_veth = _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_VETH);
 
         ifindex = nm_device_get_ip_ifindex(device);
         if (ifindex > 0) {
@@ -1664,11 +1661,7 @@ complete_connection(NMDevice *           device,
     if (s_pppoe && !nm_setting_verify(NM_SETTING(s_pppoe), NULL, error))
         return FALSE;
 
-    s_wired = nm_connection_get_setting_wired(connection);
-    if (!s_wired) {
-        s_wired = (NMSettingWired *) nm_setting_wired_new();
-        nm_connection_add_setting(connection, NM_SETTING(s_wired));
-    }
+    s_wired = _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_WIRED);
 
     /* Default to an ethernet-only connection, but if a PPPoE setting was given
      * then PPPoE should be our connection type.
@@ -1782,20 +1775,15 @@ get_s390_subchannels(NMDevice *device)
 static void
 update_connection(NMDevice *device, NMConnection *connection)
 {
-    NMDeviceEthernetPrivate *priv    = NM_DEVICE_ETHERNET_GET_PRIVATE(device);
-    NMSettingWired *         s_wired = nm_connection_get_setting_wired(connection);
-    gboolean                 perm_hw_addr_is_fake;
-    const char *             perm_hw_addr;
-    const char *             mac      = nm_device_get_hw_address(device);
-    const char *             mac_prop = NM_SETTING_WIRED_MAC_ADDRESS;
-    GHashTableIter           iter;
-    const char *             key;
-    const char *             value;
-
-    if (!s_wired) {
-        s_wired = (NMSettingWired *) nm_setting_wired_new();
-        nm_connection_add_setting(connection, (NMSetting *) s_wired);
-    }
+    NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE(device);
+    NMSettingWired *s_wired = _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_WIRED);
+    gboolean        perm_hw_addr_is_fake;
+    const char *    perm_hw_addr;
+    const char *    mac      = nm_device_get_hw_address(device);
+    const char *    mac_prop = NM_SETTING_WIRED_MAC_ADDRESS;
+    GHashTableIter  iter;
+    const char *    key;
+    const char *    value;
 
     g_object_set(nm_connection_get_setting_connection(connection),
                  NM_SETTING_CONNECTION_TYPE,
diff --git a/src/core/devices/nm-device-infiniband.c b/src/core/devices/nm-device-infiniband.c
index 37065bc8..df14dfa8 100644
--- a/src/core/devices/nm-device-infiniband.c
+++ b/src/core/devices/nm-device-infiniband.c
@@ -16,6 +16,7 @@
 #include "nm-ip4-config.h"
 #include "libnm-platform/nm-platform.h"
 #include "nm-device-factory.h"
+#include "libnm-core-aux-intern/nm-libnm-core-utils.h"
 #include "libnm-core-intern/nm-core-internal.h"
 
 #define NM_DEVICE_INFINIBAND_IS_PARTITION "is-partition"
@@ -146,11 +147,7 @@ complete_connection(NMDevice *           device,
 {
     NMSettingInfiniband *s_infiniband;
 
-    s_infiniband = nm_connection_get_setting_infiniband(connection);
-    if (!s_infiniband) {
-        s_infiniband = (NMSettingInfiniband *) nm_setting_infiniband_new();
-        nm_connection_add_setting(connection, NM_SETTING(s_infiniband));
-    }
+    s_infiniband = _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_INFINIBAND);
 
     nm_utils_complete_generic(
         nm_device_get_platform(device),
@@ -175,15 +172,11 @@ complete_connection(NMDevice *           device,
 static void
 update_connection(NMDevice *device, NMConnection *connection)
 {
-    NMSettingInfiniband *s_infiniband   = nm_connection_get_setting_infiniband(connection);
-    const char *         mac            = nm_device_get_permanent_hw_address(device);
-    const char *         transport_mode = "datagram";
-    int                  ifindex;
-
-    if (!s_infiniband) {
-        s_infiniband = (NMSettingInfiniband *) nm_setting_infiniband_new();
-        nm_connection_add_setting(connection, (NMSetting *) s_infiniband);
-    }
+    NMSettingInfiniband *s_infiniband =
+        _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_INFINIBAND);
+    const char *mac            = nm_device_get_permanent_hw_address(device);
+    const char *transport_mode = "datagram";
+    int         ifindex;
 
     if (mac && !nm_utils_hwaddr_matches(mac, -1, NULL, INFINIBAND_ALEN))
         g_object_set(s_infiniband, NM_SETTING_INFINIBAND_MAC_ADDRESS, mac, NULL);
diff --git a/src/core/devices/nm-device-ip-tunnel.c b/src/core/devices/nm-device-ip-tunnel.c
index c55dd9f6..fe2e2023 100644
--- a/src/core/devices/nm-device-ip-tunnel.c
+++ b/src/core/devices/nm-device-ip-tunnel.c
@@ -18,6 +18,7 @@
 #include "nm-manager.h"
 #include "libnm-platform/nm-platform.h"
 #include "nm-device-factory.h"
+#include "libnm-core-aux-intern/nm-libnm-core-utils.h"
 #include "libnm-core-intern/nm-core-internal.h"
 #include "settings/nm-settings.h"
 #include "nm-act-request.h"
@@ -416,14 +417,10 @@ complete_connection(NMDevice *           device,
 static void
 update_connection(NMDevice *device, NMConnection *connection)
 {
-    NMDeviceIPTunnel *       self        = NM_DEVICE_IP_TUNNEL(device);
-    NMDeviceIPTunnelPrivate *priv        = NM_DEVICE_IP_TUNNEL_GET_PRIVATE(self);
-    NMSettingIPTunnel *      s_ip_tunnel = nm_connection_get_setting_ip_tunnel(connection);
-
-    if (!s_ip_tunnel) {
-        s_ip_tunnel = (NMSettingIPTunnel *) nm_setting_ip_tunnel_new();
-        nm_connection_add_setting(connection, (NMSetting *) s_ip_tunnel);
-    }
+    NMDeviceIPTunnel *       self = NM_DEVICE_IP_TUNNEL(device);
+    NMDeviceIPTunnelPrivate *priv = NM_DEVICE_IP_TUNNEL_GET_PRIVATE(self);
+    NMSettingIPTunnel *      s_ip_tunnel =
+        _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_IP_TUNNEL);
 
     if (nm_setting_ip_tunnel_get_mode(s_ip_tunnel) != priv->mode)
         g_object_set(G_OBJECT(s_ip_tunnel), NM_SETTING_IP_TUNNEL_MODE, priv->mode, NULL);
diff --git a/src/core/devices/nm-device-logging.h b/src/core/devices/nm-device-logging.h
index 844e9949..c2602189 100644
--- a/src/core/devices/nm-device-logging.h
+++ b/src/core/devices/nm-device-logging.h
@@ -9,15 +9,13 @@
 #include "nm-device.h"
 
 #if !_NM_CC_SUPPORT_GENERIC
-    #define _NM_DEVICE_CAST(self) ((NMDevice *) (self))
+#define _NM_DEVICE_CAST(self) ((NMDevice *) (self))
 #elif !defined(_NMLOG_DEVICE_TYPE)
-    #define _NM_DEVICE_CAST(self)                         \
-        _Generic((self), NMDevice *                       \
-                 : ((NMDevice *) (self)), NMDevice *const \
-                 : ((NMDevice *) (self)))
+#define _NM_DEVICE_CAST(self) \
+    _Generic((self), NMDevice * : ((NMDevice *) (self)), NMDevice *const : ((NMDevice *) (self)))
 #else
-    #define _NM_DEVICE_CAST(self) \
-        _Generic((self), \
+#define _NM_DEVICE_CAST(self) \
+    _Generic((self), \
                  _NMLOG_DEVICE_TYPE *      : ((NMDevice *) (self)), \
                  _NMLOG_DEVICE_TYPE * const: ((NMDevice *) (self)), \
                  NMDevice *                : ((NMDevice *) (self)), \
diff --git a/src/core/devices/nm-device-macvlan.c b/src/core/devices/nm-device-macvlan.c
index 7302ecdf..937ba9e4 100644
--- a/src/core/devices/nm-device-macvlan.c
+++ b/src/core/devices/nm-device-macvlan.c
@@ -13,6 +13,7 @@
 #include "settings/nm-settings.h"
 #include "nm-act-request.h"
 #include "nm-manager.h"
+#include "libnm-core-aux-intern/nm-libnm-core-utils.h"
 #include "libnm-platform/nm-platform.h"
 #include "nm-device-factory.h"
 #include "nm-setting-macvlan.h"
@@ -419,14 +420,10 @@ complete_connection(NMDevice *           device,
 static void
 update_connection(NMDevice *device, NMConnection *connection)
 {
-    NMDeviceMacvlanPrivate *priv      = NM_DEVICE_MACVLAN_GET_PRIVATE(device);
-    NMSettingMacvlan *      s_macvlan = nm_connection_get_setting_macvlan(connection);
-    int                     new_mode;
-
-    if (!s_macvlan) {
-        s_macvlan = (NMSettingMacvlan *) nm_setting_macvlan_new();
-        nm_connection_add_setting(connection, (NMSetting *) s_macvlan);
-    }
+    NMDeviceMacvlanPrivate *priv = NM_DEVICE_MACVLAN_GET_PRIVATE(device);
+    NMSettingMacvlan *      s_macvlan =
+        _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_MACVLAN);
+    int new_mode;
 
     new_mode = platform_mode_to_setting(priv->props.mode);
     if (new_mode != nm_setting_macvlan_get_mode(s_macvlan))
diff --git a/src/core/devices/nm-device-ppp.c b/src/core/devices/nm-device-ppp.c
index 8df245bb..04051bec 100644
--- a/src/core/devices/nm-device-ppp.c
+++ b/src/core/devices/nm-device-ppp.c
@@ -93,28 +93,35 @@ ppp_ifindex_set(NMPPPManager *ppp_manager, int ifindex, const char *iface, gpoin
 }
 
 static void
-ppp_ip4_config(NMPPPManager *ppp_manager, NMIP4Config *config, gpointer user_data)
+_ppp_ip4_config_handle(NMDevicePpp *self)
 {
-    NMDevice *          device = NM_DEVICE(user_data);
-    NMDevicePpp *       self   = NM_DEVICE_PPP(device);
+    NMDevice *          device = NM_DEVICE(self);
     NMDevicePppPrivate *priv   = NM_DEVICE_PPP_GET_PRIVATE(self);
 
-    _LOGT(LOGD_DEVICE | LOGD_PPP, "received IPv4 config from pppd");
+    if (!priv->ip4_config)
+        return;
 
-    if (nm_device_get_state(device) == NM_DEVICE_STATE_IP_CONFIG) {
-        if (nm_device_activate_ip4_state_in_conf(device)) {
-            nm_device_activate_schedule_ip_config_result(device,
-                                                         AF_INET,
-                                                         NM_IP_CONFIG_CAST(config));
-            return;
-        }
-    } else {
-        if (priv->ip4_config)
-            g_object_unref(priv->ip4_config);
-        priv->ip4_config = g_object_ref(config);
+    if (nm_device_get_state(device) == NM_DEVICE_STATE_IP_CONFIG
+        && nm_device_activate_ip4_state_in_conf(device)) {
+        nm_device_activate_schedule_ip_config_result(
+            device,
+            AF_INET,
+            NM_IP_CONFIG_CAST(g_steal_pointer(&priv->ip4_config)));
+        return;
     }
 }
 
+static void
+ppp_ip4_config(NMPPPManager *ppp_manager, NMIP4Config *config, gpointer user_data)
+{
+    NMDevicePpp *       self = NM_DEVICE_PPP(user_data);
+    NMDevicePppPrivate *priv = NM_DEVICE_PPP_GET_PRIVATE(self);
+
+    _LOGT(LOGD_DEVICE | LOGD_PPP, "received IPv4 config from pppd");
+    nm_g_object_ref_set(&priv->ip4_config, config);
+    _ppp_ip4_config_handle(self);
+}
+
 static gboolean
 check_connection_compatible(NMDevice *device, NMConnection *connection, GError **error)
 {
@@ -193,6 +200,15 @@ act_stage2_config(NMDevice *device, NMDeviceStateReason *out_failure_reason)
     return NM_ACT_STAGE_RETURN_POSTPONE;
 }
 
+static gboolean
+_schedule_ip_config_result(gpointer user_data)
+{
+    gs_unref_object NMDevicePpp *self = user_data;
+
+    _ppp_ip4_config_handle(self);
+    return G_SOURCE_REMOVE;
+}
+
 static NMActStageReturn
 act_stage3_ip_config_start(NMDevice *           device,
                            int                  addr_family,
@@ -203,13 +219,8 @@ act_stage3_ip_config_start(NMDevice *           device,
         NMDevicePpp *       self = NM_DEVICE_PPP(device);
         NMDevicePppPrivate *priv = NM_DEVICE_PPP_GET_PRIVATE(self);
 
-        if (priv->ip4_config) {
-            if (out_config)
-                *out_config = g_steal_pointer(&priv->ip4_config);
-            else
-                g_clear_object(&priv->ip4_config);
-            return NM_ACT_STAGE_RETURN_SUCCESS;
-        }
+        if (priv->ip4_config)
+            nm_g_idle_add(_schedule_ip_config_result, g_object_ref(self));
 
         /* Wait IPCP termination */
         return NM_ACT_STAGE_RETURN_POSTPONE;
@@ -256,6 +267,8 @@ deactivate(NMDevice *device)
         nm_ppp_manager_stop(priv->ppp_manager, NULL, NULL, NULL);
         g_clear_object(&priv->ppp_manager);
     }
+
+    g_clear_object(&priv->ip4_config);
 }
 
 static void
diff --git a/src/core/devices/nm-device-private.h b/src/core/devices/nm-device-private.h
index eb37b14f..b55e8b43 100644
--- a/src/core/devices/nm-device-private.h
+++ b/src/core/devices/nm-device-private.h
@@ -143,7 +143,7 @@ NMIP6Config *nm_device_ip6_config_new(NMDevice *self);
 
 NMIPConfig *nm_device_ip_config_new(NMDevice *self, int addr_family);
 
-NML3ConfigData *nm_device_create_l3_config_data(NMDevice *self);
+NML3ConfigData *nm_device_create_l3_config_data(NMDevice *self, NMIPConfigSource source);
 
 /*****************************************************************************/
 
diff --git a/src/core/devices/nm-device-tun.c b/src/core/devices/nm-device-tun.c
index ebccddfc..0ab08f65 100644
--- a/src/core/devices/nm-device-tun.c
+++ b/src/core/devices/nm-device-tun.c
@@ -17,6 +17,7 @@
 #include "libnm-platform/nm-platform.h"
 #include "nm-device-factory.h"
 #include "nm-setting-tun.h"
+#include "libnm-core-aux-intern/nm-libnm-core-utils.h"
 #include "libnm-core-intern/nm-core-internal.h"
 
 #define _NMLOG_DEVICE_TYPE NMDeviceTun
@@ -186,11 +187,7 @@ update_connection(NMDevice *device, NMConnection *connection)
         return;
     }
 
-    s_tun = nm_connection_get_setting_tun(connection);
-    if (!s_tun) {
-        s_tun = (NMSettingTun *) nm_setting_tun_new();
-        nm_connection_add_setting(connection, (NMSetting *) s_tun);
-    }
+    s_tun = _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_TUN);
 
     if (mode != nm_setting_tun_get_mode(s_tun))
         g_object_set(G_OBJECT(s_tun), NM_SETTING_TUN_MODE, (guint) mode, NULL);
diff --git a/src/core/devices/nm-device-utils.c b/src/core/devices/nm-device-utils.c
index f40ca570..2a6ab04e 100644
--- a/src/core/devices/nm-device-utils.c
+++ b/src/core/devices/nm-device-utils.c
@@ -10,7 +10,7 @@
 /*****************************************************************************/
 
 NM_UTILS_LOOKUP_STR_DEFINE(
-    nm_device_state_queued_state_to_str,
+    nm_device_state_queued_state_to_string,
     NMDeviceState,
     NM_UTILS_LOOKUP_DEFAULT(NM_PENDING_ACTIONPREFIX_QUEUED_STATE_CHANGE "???"),
     NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_UNKNOWN,
@@ -41,14 +41,14 @@ NM_UTILS_LOOKUP_STR_DEFINE(
                              NM_PENDING_ACTIONPREFIX_QUEUED_STATE_CHANGE "failed"), );
 
 const char *
-nm_device_state_to_str(NMDeviceState state)
+nm_device_state_to_string(NMDeviceState state)
 {
-    return nm_device_state_queued_state_to_str(state)
+    return nm_device_state_queued_state_to_string(state)
            + NM_STRLEN(NM_PENDING_ACTIONPREFIX_QUEUED_STATE_CHANGE);
 }
 
 NM_UTILS_LOOKUP_STR_DEFINE(
-    nm_device_state_reason_to_str,
+    nm_device_state_reason_to_string,
     NMDeviceStateReason,
     NM_UTILS_LOOKUP_DEFAULT(NULL),
     NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_UNKNOWN, "unknown"),
@@ -129,7 +129,7 @@ NM_UTILS_LOOKUP_STR_DEFINE(
                              "sriov-configuration-failed"),
     NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_PEER_NOT_FOUND, "peer-not-found"), );
 
-NM_UTILS_LOOKUP_STR_DEFINE(nm_device_mtu_source_to_str,
+NM_UTILS_LOOKUP_STR_DEFINE(nm_device_mtu_source_to_string,
                            NMDeviceMtuSource,
                            NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT("unknown"),
                            NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_MTU_SOURCE_NONE, "none"),
@@ -138,7 +138,7 @@ NM_UTILS_LOOKUP_STR_DEFINE(nm_device_mtu_source_to_str,
                            NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_MTU_SOURCE_CONNECTION,
                                                     "connection"), );
 
-NM_UTILS_LOOKUP_STR_DEFINE(nm_device_sys_iface_state_to_str,
+NM_UTILS_LOOKUP_STR_DEFINE(nm_device_sys_iface_state_to_string,
                            NMDeviceSysIfaceState,
                            NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT("unknown"),
                            NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_SYS_IFACE_STATE_EXTERNAL, "external"),
@@ -147,7 +147,7 @@ NM_UTILS_LOOKUP_STR_DEFINE(nm_device_sys_iface_state_to_str,
                            NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_SYS_IFACE_STATE_REMOVED,
                                                     "removed"), );
 
-NM_UTILS_LOOKUP_STR_DEFINE(nm_device_ip_state_to_str,
+NM_UTILS_LOOKUP_STR_DEFINE(nm_device_ip_state_to_string,
                            NMDeviceIPState,
                            NM_UTILS_LOOKUP_DEFAULT_WARN("unknown"),
                            NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_IP_STATE_NONE, "none"),
@@ -267,7 +267,18 @@ resolve_addr_resolved_cb(NMDnsSystemdResolved *                   resolved,
         _LOG2D(info, "error resolving via systemd-resolved: %s", error->message);
 
         dbus_error = g_dbus_error_get_remote_error(error);
-        if (nm_streq0(dbus_error, "org.freedesktop.resolve1.DnsError.NXDOMAIN")) {
+        if (NM_STR_HAS_PREFIX(dbus_error, "org.freedesktop.resolve1.")) {
+            /* systemd-resolved is enabled but it couldn't resolve the
+             * address via DNS.  Don't fall back to spawning the helper,
+             * because the helper will possibly ask again to
+             * systemd-resolved (via /etc/resolv.conf), potentially using
+             * other protocols than DNS or returning synthetic results.
+             *
+             * Consider the error as the final indication that the address
+             * can't be resolved.
+             *
+             * See: https://www.freedesktop.org/wiki/Software/systemd/resolved/#commonerrors
+             */
             resolve_addr_complete(info, NULL, g_error_copy(error));
             return;
         }
diff --git a/src/core/devices/nm-device-utils.h b/src/core/devices/nm-device-utils.h
index 8bc957a4..7200b0ea 100644
--- a/src/core/devices/nm-device-utils.h
+++ b/src/core/devices/nm-device-utils.h
@@ -5,11 +5,11 @@
 
 /*****************************************************************************/
 
-const char *nm_device_state_to_str(NMDeviceState state);
-const char *nm_device_state_reason_to_str(NMDeviceStateReason reason);
+const char *nm_device_state_to_string(NMDeviceState state);
+const char *nm_device_state_reason_to_string(NMDeviceStateReason reason);
 
-#define nm_device_state_reason_to_str_a(reason) \
-    NM_UTILS_LOOKUP_STR_A(nm_device_state_reason_to_str, reason)
+#define nm_device_state_reason_to_string_a(reason) \
+    NM_UTILS_LOOKUP_STR_A(nm_device_state_reason_to_string, reason)
 
 static inline NMDeviceStateReason
 nm_device_state_reason_check(NMDeviceStateReason reason)
@@ -41,7 +41,7 @@ nm_device_state_reason_check(NMDeviceStateReason reason)
 #define NM_PENDING_ACTIONPREFIX_QUEUED_STATE_CHANGE "queued-state-change-"
 #define NM_PENDING_ACTIONPREFIX_ACTIVATION          "activation-"
 
-const char *nm_device_state_queued_state_to_str(NMDeviceState state);
+const char *nm_device_state_queued_state_to_string(NMDeviceState state);
 
 /*****************************************************************************/
 
@@ -52,7 +52,7 @@ typedef enum {
     NM_DEVICE_MTU_SOURCE_CONNECTION,
 } NMDeviceMtuSource;
 
-const char *nm_device_mtu_source_to_str(NMDeviceMtuSource mtu_source);
+const char *nm_device_mtu_source_to_string(NMDeviceMtuSource mtu_source);
 
 /*****************************************************************************/
 
@@ -69,7 +69,7 @@ typedef enum _nm_packed {
     NM_DEVICE_SYS_IFACE_STATE_REMOVED,
 } NMDeviceSysIfaceState;
 
-const char *nm_device_sys_iface_state_to_str(NMDeviceSysIfaceState sys_iface_state);
+const char *nm_device_sys_iface_state_to_string(NMDeviceSysIfaceState sys_iface_state);
 
 /*****************************************************************************/
 
@@ -81,7 +81,7 @@ typedef enum {
     NM_DEVICE_IP_STATE_FAIL,
 } NMDeviceIPState;
 
-const char *nm_device_ip_state_to_str(NMDeviceIPState ip_state);
+const char *nm_device_ip_state_to_string(NMDeviceIPState ip_state);
 
 /*****************************************************************************/
 
diff --git a/src/core/devices/nm-device-vlan.c b/src/core/devices/nm-device-vlan.c
index 548245f8..d1e57411 100644
--- a/src/core/devices/nm-device-vlan.c
+++ b/src/core/devices/nm-device-vlan.c
@@ -19,6 +19,7 @@
 #include "libnm-platform/nm-platform.h"
 #include "nm-device-factory.h"
 #include "nm-manager.h"
+#include "libnm-core-aux-intern/nm-libnm-core-utils.h"
 #include "libnm-core-intern/nm-core-internal.h"
 #include "libnm-platform/nmp-object.h"
 #include "libnm-platform/nm-platform-utils.h"
@@ -432,18 +433,13 @@ static void
 update_connection(NMDevice *device, NMConnection *connection)
 {
     NMDeviceVlanPrivate * priv    = NM_DEVICE_VLAN_GET_PRIVATE(device);
-    NMSettingVlan *       s_vlan  = nm_connection_get_setting_vlan(connection);
+    NMSettingVlan *       s_vlan  = _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_VLAN);
     int                   ifindex = nm_device_get_ifindex(device);
     const NMPlatformLink *plink;
     const NMPObject *     polnk;
     guint                 vlan_id;
     _NMVlanFlags          vlan_flags;
 
-    if (!s_vlan) {
-        s_vlan = (NMSettingVlan *) nm_setting_vlan_new();
-        nm_connection_add_setting(connection, (NMSetting *) s_vlan);
-    }
-
     polnk = nm_platform_link_get_lnk(nm_device_get_platform(device),
                                      ifindex,
                                      NM_LINK_TYPE_VLAN,
diff --git a/src/core/devices/nm-device-vrf.c b/src/core/devices/nm-device-vrf.c
index 2a1f42c6..c752fa0a 100644
--- a/src/core/devices/nm-device-vrf.c
+++ b/src/core/devices/nm-device-vrf.c
@@ -9,6 +9,7 @@
 #include "nm-device-private.h"
 #include "nm-manager.h"
 #include "nm-setting-vrf.h"
+#include "libnm-core-aux-intern/nm-libnm-core-utils.h"
 #include "libnm-platform/nm-platform.h"
 #include "settings/nm-settings.h"
 
@@ -199,12 +200,7 @@ static void
 update_connection(NMDevice *device, NMConnection *connection)
 {
     NMDeviceVrfPrivate *priv  = NM_DEVICE_VRF_GET_PRIVATE(device);
-    NMSettingVrf *      s_vrf = _nm_connection_get_setting(connection, NM_TYPE_SETTING_VRF);
-
-    if (!s_vrf) {
-        s_vrf = (NMSettingVrf *) nm_setting_vrf_new();
-        nm_connection_add_setting(connection, (NMSetting *) s_vrf);
-    }
+    NMSettingVrf *      s_vrf = _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_VRF);
 
     if (priv->props.table != nm_setting_vrf_get_table(s_vrf))
         g_object_set(G_OBJECT(s_vrf), NM_SETTING_VRF_TABLE, priv->props.table, NULL);
diff --git a/src/core/devices/nm-device-vxlan.c b/src/core/devices/nm-device-vxlan.c
index f9dfad2a..fcd6950b 100644
--- a/src/core/devices/nm-device-vxlan.c
+++ b/src/core/devices/nm-device-vxlan.c
@@ -17,6 +17,7 @@
 #include "settings/nm-settings.h"
 #include "nm-act-request.h"
 #include "nm-ip4-config.h"
+#include "libnm-core-aux-intern/nm-libnm-core-utils.h"
 #include "libnm-core-intern/nm-core-internal.h"
 
 #define _NMLOG_DEVICE_TYPE NMDeviceVxlan
@@ -399,14 +400,9 @@ complete_connection(NMDevice *           device,
 static void
 update_connection(NMDevice *device, NMConnection *connection)
 {
-    NMDeviceVxlanPrivate *priv    = NM_DEVICE_VXLAN_GET_PRIVATE(device);
-    NMSettingVxlan *      s_vxlan = nm_connection_get_setting_vxlan(connection);
-    char                  sbuf[NM_UTILS_INET_ADDRSTRLEN];
-
-    if (!s_vxlan) {
-        s_vxlan = (NMSettingVxlan *) nm_setting_vxlan_new();
-        nm_connection_add_setting(connection, (NMSetting *) s_vxlan);
-    }
+    NMDeviceVxlanPrivate *priv = NM_DEVICE_VXLAN_GET_PRIVATE(device);
+    NMSettingVxlan *s_vxlan    = _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_VXLAN);
+    char            sbuf[NM_UTILS_INET_ADDRSTRLEN];
 
     if (priv->props.id != nm_setting_vxlan_get_id(s_vxlan))
         g_object_set(G_OBJECT(s_vxlan), NM_SETTING_VXLAN_ID, priv->props.id, NULL);
diff --git a/src/core/devices/nm-device-wireguard.c b/src/core/devices/nm-device-wireguard.c
index aa9c46eb..f129478f 100644
--- a/src/core/devices/nm-device-wireguard.c
+++ b/src/core/devices/nm-device-wireguard.c
@@ -11,6 +11,7 @@
 #include <linux/fib_rules.h>
 
 #include "nm-setting-wireguard.h"
+#include "libnm-core-aux-intern/nm-libnm-core-utils.h"
 #include "libnm-core-intern/nm-core-internal.h"
 #include "libnm-glib-aux/nm-secret-utils.h"
 #include "nm-device-private.h"
@@ -1891,17 +1892,11 @@ static void
 update_connection(NMDevice *device, NMConnection *connection)
 {
     NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE(device);
-    NMSettingWireGuard *      s_wg =
-        NM_SETTING_WIREGUARD(nm_connection_get_setting(connection, NM_TYPE_SETTING_WIREGUARD));
-    const NMPObject *            obj_wg;
+    NMSettingWireGuard *s_wg = _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_WIREGUARD);
+    const NMPObject *   obj_wg;
     const NMPObjectLnkWireGuard *olnk_wg;
     guint                        i;
 
-    if (!s_wg) {
-        s_wg = NM_SETTING_WIREGUARD(nm_setting_wireguard_new());
-        nm_connection_add_setting(connection, NM_SETTING(s_wg));
-    }
-
     g_object_set(s_wg,
                  NM_SETTING_WIREGUARD_FWMARK,
                  (guint) priv->lnk_curr.fwmark,
diff --git a/src/core/devices/nm-device-wpan.c b/src/core/devices/nm-device-wpan.c
index 73d79410..9bc43a85 100644
--- a/src/core/devices/nm-device-wpan.c
+++ b/src/core/devices/nm-device-wpan.c
@@ -18,6 +18,7 @@
 #include "libnm-platform/nm-platform.h"
 #include "nm-device-factory.h"
 #include "nm-setting-wpan.h"
+#include "libnm-core-aux-intern/nm-libnm-core-utils.h"
 #include "libnm-core-intern/nm-core-internal.h"
 
 #define _NMLOG_DEVICE_TYPE NMDeviceWpan
@@ -71,13 +72,7 @@ complete_connection(NMDevice *           device,
 static void
 update_connection(NMDevice *device, NMConnection *connection)
 {
-    NMSettingWpan *s_wpan =
-        NM_SETTING_WPAN(nm_connection_get_setting(connection, NM_TYPE_SETTING_WPAN));
-
-    if (!s_wpan) {
-        s_wpan = (NMSettingWpan *) nm_setting_wpan_new();
-        nm_connection_add_setting(connection, (NMSetting *) s_wpan);
-    }
+    _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_WPAN);
 }
 
 static gboolean
diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c
index 50386d00..ea1b9647 100644
--- a/src/core/devices/nm-device.c
+++ b/src/core/devices/nm-device.c
@@ -185,7 +185,7 @@ typedef struct {
 typedef struct {
     NMDhcpClient *client;
     NMDhcpConfig *config;
-    gulong        state_sigid;
+    gulong        notify_sigid;
     guint         grace_id;
     bool          grace_pending : 1;
     bool          was_active : 1;
@@ -290,7 +290,8 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMDevice,
                              PROP_STATISTICS_RX_BYTES,
                              PROP_IP4_CONNECTIVITY,
                              PROP_IP6_CONNECTIVITY,
-                             PROP_INTERFACE_FLAGS, );
+                             PROP_INTERFACE_FLAGS,
+                             PROP_PORTS, );
 
 typedef struct _NMDevicePrivate {
     bool in_state_changed;
@@ -316,7 +317,12 @@ typedef struct _NMDevicePrivate {
         guint queued_ip_config_id_x[2];
     };
 
-    GSList *pending_actions;
+    struct {
+        const char **arr;
+        guint        len;
+        guint        alloc;
+    } pending_actions;
+
     GSList *dad6_failed_addrs;
 
     NMDBusTrackObjPath parent_device;
@@ -500,6 +506,7 @@ typedef struct _NMDevicePrivate {
     NMDeviceStageState stage1_sriov_state : 3;
 
     bool ip_config_started : 1;
+    bool tc_committed : 1;
 
     char *current_stable_id;
 
@@ -574,7 +581,6 @@ typedef struct _NMDevicePrivate {
         AppliedConfig ip6_config;
         /* Event ID of the current IP6 config from DHCP */
         char *           event_id;
-        gulong           prefix_sigid;
         NMNDiscDHCPLevel mode;
         guint            needed_prefixes;
     } dhcp6;
@@ -696,15 +702,17 @@ typedef struct _NMDevicePrivate {
     guint sriov_reset_pending;
 
     struct {
-        guint   timeout_id;
-        guint   refresh_rate_ms;
-        guint64 tx_bytes;
-        guint64 rx_bytes;
+        GSource *timeout_source;
+        guint    refresh_rate_ms;
+        guint64  tx_bytes;
+        guint64  rx_bytes;
     } stats;
 
     bool mtu_force_set_done : 1;
 
     NMOptionBool promisc_reset;
+
+    GVariant *ports_variant; /* Array of port devices D-Bus path */
 } NMDevicePrivate;
 
 G_DEFINE_ABSTRACT_TYPE(NMDevice, nm_device, NM_TYPE_DBUS_OBJECT)
@@ -1190,6 +1198,29 @@ _prop_get_connection_llmnr(NMDevice *self)
                                                        NM_SETTING_CONNECTION_LLMNR_DEFAULT);
 }
 
+static NMSettingConnectionDnsOverTls
+_prop_get_connection_dns_over_tls(NMDevice *self)
+{
+    NMConnection *                connection;
+    NMSettingConnectionDnsOverTls dns_over_tls = NM_SETTING_CONNECTION_DNS_OVER_TLS_DEFAULT;
+
+    g_return_val_if_fail(NM_IS_DEVICE(self), NM_SETTING_CONNECTION_DNS_OVER_TLS_DEFAULT);
+
+    connection = nm_device_get_applied_connection(self);
+    if (connection)
+        dns_over_tls = nm_setting_connection_get_dns_over_tls(
+            nm_connection_get_setting_connection(connection));
+    if (dns_over_tls != NM_SETTING_CONNECTION_DNS_OVER_TLS_DEFAULT)
+        return dns_over_tls;
+
+    return nm_config_data_get_connection_default_int64(NM_CONFIG_GET_DATA,
+                                                       NM_CON_DEFAULT("connection.dns-over-tls"),
+                                                       self,
+                                                       NM_SETTING_CONNECTION_DNS_OVER_TLS_NO,
+                                                       NM_SETTING_CONNECTION_DNS_OVER_TLS_YES,
+                                                       NM_SETTING_CONNECTION_DNS_OVER_TLS_DEFAULT);
+}
+
 static guint32
 _prop_get_ipvx_route_table(NMDevice *self, int addr_family)
 {
@@ -2407,7 +2438,7 @@ nm_device_ip_config_new(NMDevice *self, int addr_family)
 }
 
 NML3ConfigData *
-nm_device_create_l3_config_data(NMDevice *self)
+nm_device_create_l3_config_data(NMDevice *self, NMIPConfigSource source)
 {
     int ifindex;
 
@@ -2417,7 +2448,7 @@ nm_device_create_l3_config_data(NMDevice *self)
     if (ifindex <= 0)
         g_return_val_if_reached(NULL);
 
-    return nm_l3_config_data_new(nm_device_get_multi_index(self), ifindex);
+    return nm_l3_config_data_new(nm_device_get_multi_index(self), ifindex, source);
 }
 
 static void
@@ -2554,8 +2585,8 @@ nm_device_sys_iface_state_set(NMDevice *self, NMDeviceSysIfaceState sys_iface_st
     if (priv->sys_iface_state != sys_iface_state) {
         _LOGT(LOGD_DEVICE,
               "sys-iface-state: %s -> %s",
-              nm_device_sys_iface_state_to_str(priv->sys_iface_state),
-              nm_device_sys_iface_state_to_str(sys_iface_state));
+              nm_device_sys_iface_state_to_string(priv->sys_iface_state),
+              nm_device_sys_iface_state_to_string(sys_iface_state));
         priv->sys_iface_state_ = sys_iface_state;
     }
 
@@ -2741,7 +2772,7 @@ nm_device_sysctl_ip_conf_get_int_checked(NMDevice *  self,
 }
 
 static void
-set_ipv6_token(NMDevice *self, NMUtilsIPv6IfaceId iid, const char *token_str)
+set_ipv6_token(NMDevice *self, const NMUtilsIPv6IfaceId *iid, const char *token_str)
 {
     NMPlatform *          platform;
     int                   ifindex;
@@ -2757,7 +2788,7 @@ set_ipv6_token(NMDevice *self, NMUtilsIPv6IfaceId iid, const char *token_str)
     ifindex  = nm_device_get_ip_ifindex(self);
     link     = nm_platform_link_get(platform, ifindex);
 
-    if (link && link->inet6_token.id == iid.id) {
+    if (link && link->inet6_token.id == iid->id) {
         _LOGT(LOGD_DEVICE | LOGD_IP6, "token %s already set", token_str);
         return;
     }
@@ -2905,7 +2936,7 @@ _set_ip_state(NMDevice *self, int addr_family, NMDeviceIPState new_state)
           "ip%c-state: set to %d (%s)",
           nm_utils_addr_family_to_char(addr_family),
           (int) new_state,
-          nm_device_ip_state_to_str(new_state));
+          nm_device_ip_state_to_string(new_state));
 
     priv->ip_state_x_[IS_IPv4] = new_state;
 
@@ -3141,8 +3172,9 @@ _set_ip_ifindex(NMDevice *self, int ifindex, const char *ifname)
 
         nm_platform_process_events_ensure_link(platform, priv->ip_ifindex, priv->ip_iface);
 
-        if (nm_platform_kernel_support_get(NM_PLATFORM_KERNEL_SUPPORT_TYPE_USER_IPV6LL))
-            nm_platform_link_set_user_ipv6ll_enabled(platform, priv->ip_ifindex, TRUE);
+        nm_platform_link_set_inet6_addr_gen_mode(platform,
+                                                 priv->ip_ifindex,
+                                                 NM_IN6_ADDR_GEN_MODE_NONE);
 
         if (!nm_platform_link_is_up(platform, priv->ip_ifindex))
             nm_platform_link_change_flags(platform, priv->ip_ifindex, IFF_UP, TRUE);
@@ -3444,7 +3476,7 @@ _stats_set_refresh_rate(NMDevice *self, guint refresh_rate_ms)
     if (_stats_refresh_rate_real(old_rate) == refresh_rate_ms)
         return;
 
-    nm_clear_g_source(&priv->stats.timeout_id);
+    nm_clear_g_source_inst(&priv->stats.timeout_source);
 
     if (!refresh_rate_ms)
         return;
@@ -3456,7 +3488,7 @@ _stats_set_refresh_rate(NMDevice *self, guint refresh_rate_ms)
     if (ifindex > 0)
         nm_platform_link_refresh(nm_device_get_platform(self), ifindex);
 
-    priv->stats.timeout_id = g_timeout_add(refresh_rate_ms, _stats_timeout_cb, self);
+    priv->stats.timeout_source = nm_g_timeout_add_source(refresh_rate_ms, _stats_timeout_cb, self);
 }
 
 /*****************************************************************************/
@@ -4509,7 +4541,7 @@ concheck_cb(NMConnectivity *           connectivity,
     seq = handle->seq;
 
     _LOGT(LOGD_CONCHECK,
-          "connectivity: [Ipv%c] complete check (seq:%llu, state:%s)",
+          "connectivity: [IPv%c] complete check (seq:%llu, state:%s)",
           nm_utils_addr_family_to_char(handle->addr_family),
           (long long unsigned) handle->seq,
           nm_connectivity_state_to_string(state));
@@ -5803,7 +5835,7 @@ nm_device_update_from_platform_link(NMDevice *self, const NMPlatformLink *plink)
         _notify(self, PROP_PATH);
     }
 
-    if (plink && !nm_str_is_empty(plink->name) && nm_utils_strdup_reset(&priv->iface_, plink->name))
+    if (plink && !nm_str_is_empty(plink->name) && nm_strdup_reset(&priv->iface_, plink->name))
         _notify(self, PROP_IFACE);
 
     str = plink ? plink->driver : NULL;
@@ -5976,9 +6008,10 @@ config_changed(NMConfig *          config,
 {
     NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
 
-    if (priv->state <= NM_DEVICE_STATE_DISCONNECTED || priv->state > NM_DEVICE_STATE_ACTIVATED) {
+    if (priv->state <= NM_DEVICE_STATE_DISCONNECTED || priv->state >= NM_DEVICE_STATE_ACTIVATED) {
         priv->ignore_carrier = nm_config_data_get_ignore_carrier(config_data, self);
-        if (NM_FLAGS_HAS(changes, NM_CONFIG_CHANGE_VALUES))
+        if (NM_FLAGS_HAS(changes, NM_CONFIG_CHANGE_VALUES)
+            && !nm_device_get_applied_setting(self, NM_TYPE_SETTING_SRIOV))
             device_init_static_sriov_num_vfs(self);
     }
 }
@@ -6023,7 +6056,7 @@ realize_start_setup(NMDevice *            self,
     NMPlatform *         platform;
     NMDeviceCapabilities capabilities = 0;
     NMConfig *           config;
-    guint                real_rate;
+    guint                refresh_rate_ms;
     gboolean             unmanaged;
 
     /* plink is a NMPlatformLink type, however, we require it to come from the platform
@@ -6087,8 +6120,8 @@ realize_start_setup(NMDevice *            self,
         if (priv->firmware_version)
             _notify(self, PROP_FIRMWARE_VERSION);
 
-        if (nm_platform_kernel_support_get(NM_PLATFORM_KERNEL_SUPPORT_TYPE_USER_IPV6LL))
-            priv->ipv6ll_handle = nm_platform_link_get_user_ipv6ll_enabled(platform, priv->ifindex);
+        priv->ipv6ll_handle = (nm_platform_link_get_inet6_addr_gen_mode(platform, priv->ifindex)
+                               == NM_IN6_ADDR_GEN_MODE_NONE);
 
         if (nm_platform_link_supports_sriov(platform, priv->ifindex))
             capabilities |= NM_DEVICE_CAP_SRIOV;
@@ -6131,10 +6164,12 @@ realize_start_setup(NMDevice *            self,
 
     nm_device_set_carrier_from_platform(self);
 
-    nm_assert(!priv->stats.timeout_id);
-    real_rate = _stats_refresh_rate_real(priv->stats.refresh_rate_ms);
-    if (real_rate)
-        priv->stats.timeout_id = g_timeout_add(real_rate, _stats_timeout_cb, self);
+    nm_assert(!priv->stats.timeout_source);
+    refresh_rate_ms = _stats_refresh_rate_real(priv->stats.refresh_rate_ms);
+    if (refresh_rate_ms > 0) {
+        priv->stats.timeout_source =
+            nm_g_timeout_add_source(refresh_rate_ms, _stats_timeout_cb, self);
+    }
 
     klass->realize_start_notify(self, plink);
 
@@ -6320,7 +6355,7 @@ nm_device_unrealize(NMDevice *self, gboolean remove_resources, GError **error)
         _notify(self, PROP_PHYSICAL_PORT_ID);
     }
 
-    nm_clear_g_source(&priv->stats.timeout_id);
+    nm_clear_g_source_inst(&priv->stats.timeout_source);
     _stats_update_counters(self, 0, 0);
 
     priv->hw_addr_len_ = 0;
@@ -6442,9 +6477,9 @@ slave_state_changed(NMDevice *          slave,
           "slave %s state change %d (%s) -> %d (%s)",
           nm_device_get_iface(slave),
           slave_old_state,
-          nm_device_state_to_str(slave_old_state),
+          nm_device_state_to_string(slave_old_state),
           slave_new_state,
-          nm_device_state_to_str(slave_new_state));
+          nm_device_state_to_string(slave_new_state));
 
     /* Don't try to enslave slaves until the master is ready */
     if (priv->state < NM_DEVICE_STATE_CONFIG)
@@ -6788,7 +6823,9 @@ nm_device_slave_notify_enslave(NMDevice *self, gboolean success)
             priv->is_enslaved = TRUE;
 
             _notify(self, PROP_MASTER);
-            _notify(priv->master, PROP_SLAVES);
+
+            nm_clear_pointer(&NM_DEVICE_GET_PRIVATE(priv->master)->ports_variant, g_variant_unref);
+            nm_gobject_notify_together(priv->master, PROP_PORTS, PROP_SLAVES);
         } else if (activating) {
             _LOGW(LOGD_DEVICE,
                   "Activation: connection '%s' could not be enslaved",
@@ -6852,8 +6889,11 @@ nm_device_slave_notify_release(NMDevice *self, NMDeviceStateReason reason)
 
     if (priv->is_enslaved) {
         priv->is_enslaved = FALSE;
+
         _notify(self, PROP_MASTER);
-        _notify(priv->master, PROP_SLAVES);
+
+        nm_clear_pointer(&NM_DEVICE_GET_PRIVATE(priv->master)->ports_variant, g_variant_unref);
+        nm_gobject_notify_together(priv->master, PROP_PORTS, PROP_SLAVES);
     }
 }
 
@@ -7339,7 +7379,7 @@ nm_device_generate_connection(NMDevice *self,
                          NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE,
                          NM_IN6_ADDR_GEN_MODE_EUI64,
                          NM_SETTING_IP6_CONFIG_TOKEN,
-                         nm_utils_inet6_interface_identifier_to_token(pllink->inet6_token, sbuf),
+                         nm_utils_inet6_interface_identifier_to_token(&pllink->inet6_token, sbuf),
                          NULL);
         }
     }
@@ -7756,7 +7796,8 @@ recheck_available(gpointer user_data)
               now_available ? "" : "not ",
               new_state == NM_DEVICE_STATE_UNAVAILABLE ? "no change required for"
                                                        : "will transition to",
-              nm_device_state_to_str(new_state == NM_DEVICE_STATE_UNAVAILABLE ? state : new_state));
+              nm_device_state_to_string(new_state == NM_DEVICE_STATE_UNAVAILABLE ? state
+                                                                                 : new_state));
 
         priv->recheck_available.available_reason   = NM_DEVICE_STATE_REASON_NONE;
         priv->recheck_available.unavailable_reason = NM_DEVICE_STATE_REASON_NONE;
@@ -8259,7 +8300,7 @@ activate_stage1_device_prepare(NMDevice *self)
                 priv->master_ready_id =
                     g_signal_connect(active,
                                      "notify::" NM_ACTIVE_CONNECTION_INT_MASTER_READY,
-                                     (GCallback) master_ready_cb,
+                                     G_CALLBACK(master_ready_cb),
                                      self);
             }
             return;
@@ -8478,10 +8519,7 @@ tc_commit(NMDevice *self)
     qdiscs   = nm_utils_qdiscs_from_tc_setting(platform, s_tc, ip_ifindex);
     tfilters = nm_utils_tfilters_from_tc_setting(platform, s_tc, ip_ifindex);
 
-    if (!nm_platform_qdisc_sync(platform, ip_ifindex, qdiscs))
-        return FALSE;
-
-    if (!nm_platform_tfilter_sync(platform, ip_ifindex, tfilters))
+    if (!nm_platform_tc_sync(platform, ip_ifindex, qdiscs, tfilters))
         return FALSE;
 
     return TRUE;
@@ -8511,13 +8549,14 @@ activate_stage2_device_config(NMDevice *self)
         _ethtool_state_set(self);
 
     if (!nm_device_sys_iface_state_is_external_or_assume(self)) {
-        if (!tc_commit(self)) {
-            _LOGW(LOGD_IP6, "failed applying traffic control rules");
+        if (!priv->tc_committed && !tc_commit(self)) {
+            _LOGW(LOGD_DEVICE, "failed applying traffic control rules");
             nm_device_state_changed(self,
                                     NM_DEVICE_STATE_FAILED,
                                     NM_DEVICE_STATE_REASON_CONFIG_FAILED);
             return;
         }
+        priv->tc_committed = TRUE;
     }
 
     _routing_rules_sync(self, NM_TERNARY_TRUE);
@@ -8981,6 +9020,7 @@ ensure_con_ip_config(NMDevice *self, int addr_family)
                                     nm_connection_get_setting_ip4_config(connection),
                                     _prop_get_connection_mdns(self),
                                     _prop_get_connection_llmnr(self),
+                                    _prop_get_connection_dns_over_tls(self),
                                     nm_device_get_route_table(self, addr_family),
                                     nm_device_get_route_metric(self, addr_family));
     } else {
@@ -9028,7 +9068,7 @@ dhcp4_cleanup(NMDevice *self, CleanupType cleanup_type, gboolean release)
 
     if (priv->dhcp_data_4.client) {
         /* Stop any ongoing DHCP transaction on this device */
-        nm_clear_g_signal_handler(priv->dhcp_data_4.client, &priv->dhcp_data_4.state_sigid);
+        nm_clear_g_signal_handler(priv->dhcp_data_4.client, &priv->dhcp_data_4.notify_sigid);
 
         if (cleanup_type == CLEANUP_TYPE_DECONFIGURE || cleanup_type == CLEANUP_TYPE_REMOVED)
             nm_dhcp_client_stop(priv->dhcp_data_4.client, release);
@@ -9251,7 +9291,7 @@ ip_config_merge_and_apply(NMDevice *self, int addr_family, gboolean commit)
 
         if (commit && priv->ndisc_started && ip6_addr_gen_token
             && nm_utils_ipv6_interface_identifier_get_from_token(&iid, ip6_addr_gen_token)) {
-            set_ipv6_token(self, iid, ip6_addr_gen_token);
+            set_ipv6_token(self, &iid, ip6_addr_gen_token);
         }
     }
 
@@ -9291,7 +9331,7 @@ dhcp4_lease_change(NMDevice *self, NMIP4Config *config, gboolean bound)
         return FALSE;
     }
 
-    nm_dispatcher_call_device(NM_DISPATCHER_ACTION_DHCP4_CHANGE, self, NULL, NULL, NULL, NULL);
+    nm_dispatcher_call_device(NM_DISPATCHER_ACTION_DHCP_CHANGE_4, self, NULL, NULL, NULL, NULL);
 
     return TRUE;
 }
@@ -9373,7 +9413,7 @@ dhcp4_fail(NMDevice *self, NMDhcpState dhcp_state)
 
     _LOGD(LOGD_DHCP4,
           "DHCPv4 failed (ip_state %s, was_active %d)",
-          nm_device_ip_state_to_str(priv->ip_state_4),
+          nm_device_ip_state_to_string(priv->ip_state_4),
           priv->dhcp_data_4.was_active);
 
     /* The client is always left running after a failure. */
@@ -9429,21 +9469,27 @@ dhcp4_dad_cb(NMDevice *self, NMIP4Config **configs, gboolean success)
 }
 
 static void
-dhcp4_state_changed(NMDhcpClient *client,
-                    NMDhcpState   state,
-                    NMIP4Config * ip4_config,
-                    GHashTable *  options,
-                    gpointer      user_data)
+dhcp4_notify(NMDhcpClient *client, const NMDhcpClientNotifyData *notify_data, NMDevice *self)
 {
-    NMDevice *       self = NM_DEVICE(user_data);
     NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
-    NMIP4Config *    manual, **configs;
+    NMIP4Config *    manual;
+    NMIP4Config **   configs;
     NMConnection *   connection;
+    NMDhcpState      state;
+    NMIP4Config *    ip4_config;
+    GHashTable *     options;
+
+    nm_assert(nm_dhcp_client_get_addr_family(client) == AF_INET);
+    nm_assert(notify_data);
+    nm_assert(notify_data->notify_type == NM_DHCP_CLIENT_NOTIFY_TYPE_STATE_CHANGED);
 
-    g_return_if_fail(nm_dhcp_client_get_addr_family(client) == AF_INET);
-    g_return_if_fail(!ip4_config || NM_IS_IP4_CONFIG(ip4_config));
+    state      = notify_data->state_changed.dhcp_state;
+    ip4_config = NM_IP4_CONFIG(notify_data->state_changed.ip_config);
+    options    = notify_data->state_changed.options;
 
-    _LOGD(LOGD_DHCP4, "new DHCPv4 client state %d", state);
+    nm_assert(!ip4_config || NM_IS_IP4_CONFIG(ip4_config));
+
+    _LOGD(LOGD_DHCP4, "new DHCPv4 client state %d", (int) state);
 
     switch (state) {
     case NM_DHCP_STATE_BOUND:
@@ -9479,6 +9525,7 @@ dhcp4_state_changed(NMDhcpClient *client,
                                         nm_connection_get_setting_ip4_config(connection),
                                         NM_SETTING_CONNECTION_MDNS_DEFAULT,
                                         NM_SETTING_CONNECTION_LLMNR_DEFAULT,
+                                        NM_SETTING_CONNECTION_DNS_OVER_TLS_DEFAULT,
                                         nm_device_get_route_table(self, AF_INET),
                                         nm_device_get_route_metric(self, AF_INET));
 
@@ -9594,10 +9641,10 @@ dhcp4_start(NMDevice *self)
         return NM_ACT_STAGE_RETURN_FAILURE;
     }
 
-    priv->dhcp_data_4.state_sigid = g_signal_connect(priv->dhcp_data_4.client,
-                                                     NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED,
-                                                     G_CALLBACK(dhcp4_state_changed),
-                                                     self);
+    priv->dhcp_data_4.notify_sigid = g_signal_connect(priv->dhcp_data_4.client,
+                                                      NM_DHCP_CLIENT_NOTIFY,
+                                                      G_CALLBACK(dhcp4_notify),
+                                                      self);
 
     if (nm_device_sys_iface_state_is_external_or_assume(self))
         priv->dhcp_data_4.was_active = TRUE;
@@ -9768,8 +9815,7 @@ dhcp6_cleanup(NMDevice *self, CleanupType cleanup_type, gboolean release)
     priv->dhcp_data_6.grace_pending = FALSE;
 
     if (priv->dhcp_data_6.client) {
-        nm_clear_g_signal_handler(priv->dhcp_data_6.client, &priv->dhcp_data_6.state_sigid);
-        nm_clear_g_signal_handler(priv->dhcp_data_6.client, &priv->dhcp6.prefix_sigid);
+        nm_clear_g_signal_handler(priv->dhcp_data_6.client, &priv->dhcp_data_6.notify_sigid);
 
         if (cleanup_type == CLEANUP_TYPE_DECONFIGURE || cleanup_type == CLEANUP_TYPE_REMOVED)
             nm_dhcp_client_stop(priv->dhcp_data_6.client, release);
@@ -9805,7 +9851,7 @@ dhcp6_lease_change(NMDevice *self)
         return FALSE;
     }
 
-    nm_dispatcher_call_device(NM_DISPATCHER_ACTION_DHCP6_CHANGE, self, NULL, NULL, NULL, NULL);
+    nm_dispatcher_call_device(NM_DISPATCHER_ACTION_DHCP_CHANGE_6, self, NULL, NULL, NULL, NULL);
 
     return TRUE;
 }
@@ -9818,7 +9864,7 @@ dhcp6_fail(NMDevice *self, NMDhcpState dhcp_state)
 
     _LOGD(LOGD_DHCP6,
           "DHCPv6 failed (ip_state %s, was_active %d)",
-          nm_device_ip_state_to_str(priv->ip_state_6),
+          nm_device_ip_state_to_string(priv->ip_state_6),
           priv->dhcp_data_6.was_active);
 
     /* The client is always left running after a failure. */
@@ -9869,20 +9915,34 @@ clear_config:
 }
 
 static void
-dhcp6_state_changed(NMDhcpClient *client,
-                    NMDhcpState   state,
-                    NMIP6Config * ip6_config,
-                    GHashTable *  options,
-                    gpointer      user_data)
+dhcp6_notify(NMDhcpClient *client, const NMDhcpClientNotifyData *notify_data, NMDevice *self)
 {
-    NMDevice *       self     = NM_DEVICE(user_data);
     NMDevicePrivate *priv     = NM_DEVICE_GET_PRIVATE(self);
     gs_free char *   event_id = NULL;
+    NMDhcpState      state;
+    NMIP6Config *    ip6_config;
+    GHashTable *     options;
+
+    nm_assert(nm_dhcp_client_get_addr_family(client) == AF_INET6);
+    nm_assert(notify_data);
+
+    if (notify_data->notify_type == NM_DHCP_CLIENT_NOTIFY_TYPE_PREFIX_DELEGATED) {
+        /* Just re-emit. The device just contributes the prefix to the
+         * pool in NMPolicy, which decides about subnet allocation
+         * on the shared devices. */
+        g_signal_emit(self, signals[IP6_PREFIX_DELEGATED], 0, notify_data->prefix_delegated.prefix);
+        return;
+    }
+
+    nm_assert(notify_data->notify_type == NM_DHCP_CLIENT_NOTIFY_TYPE_STATE_CHANGED);
 
-    g_return_if_fail(nm_dhcp_client_get_addr_family(client) == AF_INET6);
-    g_return_if_fail(!ip6_config || NM_IS_IP6_CONFIG(ip6_config));
+    state      = notify_data->state_changed.dhcp_state;
+    ip6_config = NM_IP6_CONFIG(notify_data->state_changed.ip_config);
+    options    = notify_data->state_changed.options;
 
-    _LOGD(LOGD_DHCP6, "new DHCPv6 client state %d", state);
+    nm_assert(!ip6_config || NM_IS_IP6_CONFIG(ip6_config));
+
+    _LOGD(LOGD_DHCP6, "new DHCPv6 client state %d", (int) state);
 
     switch (state) {
     case NM_DHCP_STATE_BOUND:
@@ -9962,17 +10022,6 @@ dhcp6_state_changed(NMDhcpClient *client,
     }
 }
 
-static void
-dhcp6_prefix_delegated(NMDhcpClient *client, NMPlatformIP6Address *prefix, gpointer user_data)
-{
-    NMDevice *self = NM_DEVICE(user_data);
-
-    /* Just re-emit. The device just contributes the prefix to the
-     * pool in NMPolicy, which decides about subnet allocation
-     * on the shared devices. */
-    g_signal_emit(self, signals[IP6_PREFIX_DELEGATED], 0, prefix);
-}
-
 /*****************************************************************************/
 
 static gboolean
@@ -10054,14 +10103,10 @@ dhcp6_start_with_link_ready(NMDevice *self, NMConnection *connection)
         return FALSE;
     }
 
-    priv->dhcp_data_6.state_sigid = g_signal_connect(priv->dhcp_data_6.client,
-                                                     NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED,
-                                                     G_CALLBACK(dhcp6_state_changed),
-                                                     self);
-    priv->dhcp6.prefix_sigid      = g_signal_connect(priv->dhcp_data_6.client,
-                                                NM_DHCP_CLIENT_SIGNAL_PREFIX_DELEGATED,
-                                                G_CALLBACK(dhcp6_prefix_delegated),
-                                                self);
+    priv->dhcp_data_6.notify_sigid = g_signal_connect(priv->dhcp_data_6.client,
+                                                      NM_DHCP_CLIENT_NOTIFY,
+                                                      G_CALLBACK(dhcp6_notify),
+                                                      self);
 
     if (nm_device_sys_iface_state_is_external_or_assume(self))
         priv->dhcp_data_6.was_active = TRUE;
@@ -10321,12 +10366,12 @@ check_and_add_ipv6ll_addr(NMDevice *self)
         const char *      stable_id;
 
         stable_id = _prop_get_connection_stable_id(self, connection, &stable_type);
-        if (!nm_utils_ipv6_addr_set_stable_privacy(stable_type,
-                                                   &lladdr,
-                                                   nm_device_get_iface(self),
-                                                   stable_id,
-                                                   priv->linklocal6_dad_counter++,
-                                                   &error)) {
+        if (!nm_utils_ipv6_addr_set_stable_privacy_may_fail(stable_type,
+                                                            &lladdr,
+                                                            nm_device_get_iface(self),
+                                                            stable_id,
+                                                            priv->linklocal6_dad_counter++,
+                                                            &error)) {
             _LOGW(LOGD_IP6, "linklocal6: failed to generate an address: %s", error->message);
             g_clear_error(&error);
             linklocal6_failed(self);
@@ -10348,7 +10393,7 @@ check_and_add_ipv6ll_addr(NMDevice *self)
             _LOGW(LOGD_IP6, "linklocal6: failed to get interface identifier; IPv6 cannot continue");
             return;
         }
-        nm_utils_ipv6_addr_set_interface_identifier(&lladdr, iid);
+        nm_utils_ipv6_addr_set_interface_identifier(&lladdr, &iid);
         addr_type = "EUI-64";
     }
 
@@ -10616,9 +10661,9 @@ _commit_mtu(NMDevice *self, const NMIP4Config *config)
             _LOGT(LOGD_DEVICE,
                   "mtu: value %u from source '%s' (%u), current source '%s' (%u)%s",
                   (guint) mtu,
-                  nm_device_mtu_source_to_str(source),
+                  nm_device_mtu_source_to_string(source),
                   (guint) source,
-                  nm_device_mtu_source_to_str(priv->mtu_source),
+                  nm_device_mtu_source_to_string(priv->mtu_source),
                   (guint) priv->mtu_source,
                   force ? " (forced)" : "");
         }
@@ -10801,7 +10846,7 @@ nm_device_commit_mtu(NMDevice *self)
     } else
         _LOGT(LOGD_DEVICE,
               "mtu: commit-mtu... skip due to state %s",
-              nm_device_state_to_str(state));
+              nm_device_state_to_string(state));
 }
 
 static void
@@ -10809,66 +10854,68 @@ ndisc_config_changed(NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_int
 {
     NMNDiscConfigMap changed = changed_int;
     NMDevicePrivate *priv    = NM_DEVICE_GET_PRIVATE(self);
+    int              ifindex;
     guint            i;
 
     g_return_if_fail(priv->act_request.obj);
 
+    ifindex = nm_device_get_ip_ifindex(self);
+
+    if (ifindex <= 0
+        || (applied_config_get_current(&priv->ac_ip6_config)
+            && ifindex
+                   != nm_ip_config_get_ifindex(applied_config_get_current(&priv->ac_ip6_config))))
+        applied_config_clear(&priv->ac_ip6_config);
+
+    if (ifindex <= 0)
+        return;
+
     if (!applied_config_get_current(&priv->ac_ip6_config))
         applied_config_init_new(&priv->ac_ip6_config, self, AF_INET6);
 
     if (changed & NM_NDISC_CONFIG_ADDRESSES) {
-        guint8  plen;
         guint32 ifa_flags;
 
         /* Check, whether kernel is recent enough to help user space handling RA.
          * If it's not supported, we have no ipv6-privacy and must add autoconf
          * addresses as /128. The reason for the /128 is to prevent the kernel
          * from adding a prefix route for this address. */
-        ifa_flags = 0;
-        if (nm_platform_kernel_support_get(NM_PLATFORM_KERNEL_SUPPORT_TYPE_EXTENDED_IFA_FLAGS)) {
-            ifa_flags |= IFA_F_NOPREFIXROUTE;
-            if (NM_IN_SET(priv->ndisc_use_tempaddr,
-                          NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR,
-                          NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR))
-                ifa_flags |= IFA_F_MANAGETEMPADDR;
-            plen = 64;
-        } else
-            plen = 128;
+        ifa_flags = IFA_F_NOPREFIXROUTE;
+        if (NM_IN_SET(priv->ndisc_use_tempaddr,
+                      NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR,
+                      NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR))
+            ifa_flags |= IFA_F_MANAGETEMPADDR;
 
         nm_ip6_config_reset_addresses_ndisc((NMIP6Config *) priv->ac_ip6_config.orig,
                                             rdata->addresses,
                                             rdata->addresses_n,
-                                            plen,
+                                            64,
                                             ifa_flags);
         if (priv->ac_ip6_config.current) {
             nm_ip6_config_reset_addresses_ndisc((NMIP6Config *) priv->ac_ip6_config.current,
                                                 rdata->addresses,
                                                 rdata->addresses_n,
-                                                plen,
+                                                64,
                                                 ifa_flags);
         }
     }
 
     if (NM_FLAGS_ANY(changed, NM_NDISC_CONFIG_ROUTES | NM_NDISC_CONFIG_GATEWAYS)) {
-        nm_ip6_config_reset_routes_ndisc(
-            (NMIP6Config *) priv->ac_ip6_config.orig,
-            rdata->gateways,
-            rdata->gateways_n,
-            rdata->routes,
-            rdata->routes_n,
-            nm_device_get_route_table(self, AF_INET6),
-            nm_device_get_route_metric(self, AF_INET6),
-            nm_platform_kernel_support_get(NM_PLATFORM_KERNEL_SUPPORT_TYPE_RTA_PREF));
+        nm_ip6_config_reset_routes_ndisc((NMIP6Config *) priv->ac_ip6_config.orig,
+                                         rdata->gateways,
+                                         rdata->gateways_n,
+                                         rdata->routes,
+                                         rdata->routes_n,
+                                         nm_device_get_route_table(self, AF_INET6),
+                                         nm_device_get_route_metric(self, AF_INET6));
         if (priv->ac_ip6_config.current) {
-            nm_ip6_config_reset_routes_ndisc(
-                (NMIP6Config *) priv->ac_ip6_config.current,
-                rdata->gateways,
-                rdata->gateways_n,
-                rdata->routes,
-                rdata->routes_n,
-                nm_device_get_route_table(self, AF_INET6),
-                nm_device_get_route_metric(self, AF_INET6),
-                nm_platform_kernel_support_get(NM_PLATFORM_KERNEL_SUPPORT_TYPE_RTA_PREF));
+            nm_ip6_config_reset_routes_ndisc((NMIP6Config *) priv->ac_ip6_config.current,
+                                             rdata->gateways,
+                                             rdata->gateways_n,
+                                             rdata->routes,
+                                             rdata->routes_n,
+                                             nm_device_get_route_table(self, AF_INET6),
+                                             nm_device_get_route_metric(self, AF_INET6));
         }
     }
 
@@ -11024,6 +11071,11 @@ addrconf6_start(NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr)
     guint32             ra_timeout;
     guint32             default_ra_timeout;
 
+    if (!g_file_test("/proc/sys/net/ipv6", G_FILE_TEST_IS_DIR)) {
+        _LOGI(LOGD_IP6, "addrconf6: kernel does not support IPv6");
+        return FALSE;
+    }
+
     connection = nm_device_get_applied_connection(self);
     g_assert(connection);
 
@@ -11037,17 +11089,17 @@ addrconf6_start(NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr)
     g_assert(s_ip6);
 
     if (nm_streq(nm_device_get_effective_ip_config_method(self, AF_INET6),
-                 NM_SETTING_IP4_CONFIG_METHOD_SHARED))
+                 NM_SETTING_IP6_CONFIG_METHOD_SHARED))
         node_type = NM_NDISC_NODE_TYPE_ROUTER;
     else
         node_type = NM_NDISC_NODE_TYPE_HOST;
 
-    nm_lndp_ndisc_get_sysctl(nm_device_get_platform(self),
-                             nm_device_get_ip_iface(self),
-                             &max_addresses,
-                             &router_solicitations,
-                             &router_solicitation_interval,
-                             &default_ra_timeout);
+    nm_ndisc_get_sysctl(nm_device_get_platform(self),
+                        nm_device_get_ip_iface(self),
+                        &max_addresses,
+                        &router_solicitations,
+                        &router_solicitation_interval,
+                        &default_ra_timeout);
 
     if (node_type == NM_NDISC_NODE_TYPE_ROUTER)
         ra_timeout = 0u;
@@ -11078,15 +11130,6 @@ addrconf6_start(NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr)
 
     priv->ndisc_use_tempaddr = use_tempaddr;
 
-    if (NM_IN_SET(use_tempaddr,
-                  NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR,
-                  NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR)
-        && !nm_platform_kernel_support_get(NM_PLATFORM_KERNEL_SUPPORT_TYPE_EXTENDED_IFA_FLAGS)) {
-        _LOGW(LOGD_IP6,
-              "The kernel does not support extended IFA_FLAGS needed by NM for "
-              "IPv6 private addresses. This feature is not available");
-    }
-
     /* ensure link local is ready... */
     if (!linklocal6_start(self)) {
         /* wait for the LL address to show up */
@@ -11181,21 +11224,20 @@ set_nm_ipv6ll(NMDevice *self, gboolean enable)
     NMDevicePrivate *priv    = NM_DEVICE_GET_PRIVATE(self);
     int              ifindex = nm_device_get_ip_ifindex(self);
 
-    if (!nm_platform_kernel_support_get(NM_PLATFORM_KERNEL_SUPPORT_TYPE_USER_IPV6LL))
-        return;
-
     priv->ipv6ll_handle = enable;
     if (ifindex > 0) {
-        const char *detail = enable ? "enable" : "disable";
-        int         r;
+        int r;
 
-        _LOGD(LOGD_IP6, "will %s userland IPv6LL", detail);
-        r = nm_platform_link_set_user_ipv6ll_enabled(nm_device_get_platform(self), ifindex, enable);
+        _LOGD(LOGD_IP6, "will %s userland IPv6LL", enable ? "enable" : "disable");
+        r = nm_platform_link_set_inet6_addr_gen_mode(nm_device_get_platform(self),
+                                                     ifindex,
+                                                     enable ? NM_IN6_ADDR_GEN_MODE_NONE
+                                                            : NM_IN6_ADDR_GEN_MODE_EUI64);
         if (r < 0) {
             _NMLOG(NM_IN_SET(r, -NME_PL_NOT_FOUND, -NME_PL_OPNOTSUPP) ? LOGL_DEBUG : LOGL_WARN,
                    LOGD_IP6,
                    "failed to %s userspace IPv6LL address handling (%s)",
-                   detail,
+                   enable ? "enable" : "disable",
                    nm_strerror(r));
         }
 
@@ -11310,6 +11352,7 @@ act_stage3_ip_config_start(NMDevice *           self,
                                             nm_connection_get_setting_ip4_config(connection),
                                             NM_SETTING_CONNECTION_MDNS_DEFAULT,
                                             NM_SETTING_CONNECTION_LLMNR_DEFAULT,
+                                            NM_SETTING_CONNECTION_DNS_OVER_TLS_DEFAULT,
                                             nm_device_get_route_table(self, AF_INET),
                                             nm_device_get_route_metric(self, AF_INET));
                 configs    = g_new0(NMIP4Config *, 2);
@@ -11521,45 +11564,6 @@ nm_device_activate_stage3_ip_start(NMDevice *self, int addr_family)
     return TRUE;
 }
 
-/*
- * activate_stage3_ip_config_start
- *
- * Begin automatic/manual IP configuration
- *
- */
-static void
-activate_stage3_ip_config_start(NMDevice *self)
-{
-    int ifindex;
-
-    _set_ip_state(self, AF_INET, NM_DEVICE_IP_STATE_WAIT);
-    _set_ip_state(self, AF_INET6, NM_DEVICE_IP_STATE_WAIT);
-
-    _active_connection_set_state_flags(self, NM_ACTIVATION_STATE_FLAG_LAYER2_READY);
-
-    nm_device_state_changed(self, NM_DEVICE_STATE_IP_CONFIG, NM_DEVICE_STATE_REASON_NONE);
-
-    /* Device should be up before we can do anything with it */
-    if ((ifindex = nm_device_get_ip_ifindex(self)) > 0
-        && !nm_platform_link_is_up(nm_device_get_platform(self), ifindex))
-        _LOGW(LOGD_DEVICE,
-              "interface %s not up for IP configuration",
-              nm_device_get_ip_iface(self));
-
-    if (nm_device_activate_ip4_state_in_wait(self)
-        && !nm_device_activate_stage3_ip_start(self, AF_INET))
-        return;
-
-    if (nm_device_activate_ip6_state_in_wait(self)
-        && !nm_device_activate_stage3_ip_start(self, AF_INET6))
-        return;
-
-    /* Proxy */
-    nm_device_set_proxy_config(self, NULL);
-
-    check_ip_state(self, TRUE, TRUE);
-}
-
 static void
 fw_change_zone_cb(NMFirewalldManager *      firewalld_manager,
                   NMFirewalldManagerCallId *call_id,
@@ -11642,20 +11646,19 @@ fw_change_zone(NMDevice *self)
 }
 
 /*
- * nm_device_activate_schedule_stage3_ip_config_start
+ * activate_stage3_ip_config_start
+ *
+ * Begin automatic/manual IP configuration
  *
- * Schedule IP configuration start
  */
-void
-nm_device_activate_schedule_stage3_ip_config_start(NMDevice *self)
+static void
+activate_stage3_ip_config_start(NMDevice *self)
 {
-    NMDevicePrivate *priv;
+    NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
     int              ifindex;
 
-    g_return_if_fail(NM_IS_DEVICE(self));
-
-    priv = NM_DEVICE_GET_PRIVATE(self);
     g_return_if_fail(priv->act_request.obj);
+
     ifindex = nm_device_get_ip_ifindex(self);
 
     /* Add the interface to the specified firewall zone */
@@ -11676,6 +11679,51 @@ nm_device_activate_schedule_stage3_ip_config_start(NMDevice *self)
 
     nm_assert(ifindex <= 0 || priv->fw_state == FIREWALL_STATE_INITIALIZED);
 
+    _set_ip_state(self, AF_INET, NM_DEVICE_IP_STATE_WAIT);
+    _set_ip_state(self, AF_INET6, NM_DEVICE_IP_STATE_WAIT);
+
+    _active_connection_set_state_flags(self, NM_ACTIVATION_STATE_FLAG_LAYER2_READY);
+
+    nm_device_state_changed(self, NM_DEVICE_STATE_IP_CONFIG, NM_DEVICE_STATE_REASON_NONE);
+
+    /* Device should be up before we can do anything with it */
+    if (!nm_device_sys_iface_state_is_external(self)
+        && (ifindex = nm_device_get_ip_ifindex(self)) > 0
+        && !nm_platform_link_is_up(nm_device_get_platform(self), ifindex))
+        _LOGW(LOGD_DEVICE,
+              "interface %s not up for IP configuration",
+              nm_device_get_ip_iface(self));
+
+    if (nm_device_activate_ip4_state_in_wait(self)
+        && !nm_device_activate_stage3_ip_start(self, AF_INET))
+        return;
+
+    if (nm_device_activate_ip6_state_in_wait(self)
+        && !nm_device_activate_stage3_ip_start(self, AF_INET6))
+        return;
+
+    /* Proxy */
+    nm_device_set_proxy_config(self, NULL);
+
+    check_ip_state(self, TRUE, TRUE);
+}
+
+/*
+ * nm_device_activate_schedule_stage3_ip_config_start
+ *
+ * Schedule IP configuration start
+ */
+void
+nm_device_activate_schedule_stage3_ip_config_start(NMDevice *self)
+{
+    NMDevicePrivate *priv;
+
+    g_return_if_fail(NM_IS_DEVICE(self));
+
+    priv = NM_DEVICE_GET_PRIVATE(self);
+
+    g_return_if_fail(priv->act_request.obj);
+
     activation_source_schedule(self, activate_stage3_ip_config_start, AF_INET);
 }
 
@@ -11728,22 +11776,23 @@ activate_stage4_ip_config_timeout_6(NMDevice *self)
     activate_stage4_ip_config_timeout_x(self, AF_INET6);
 }
 
+#define activate_stage4_ip_config_timeout_x_fcn(addr_family)       \
+    (NM_IS_IPv4(addr_family) ? activate_stage4_ip_config_timeout_4 \
+                             : activate_stage4_ip_config_timeout_6)
+
 void
 nm_device_activate_schedule_ip_config_timeout(NMDevice *self, int addr_family)
 {
     NMDevicePrivate *priv;
-    const int        IS_IPv4 = NM_IS_IPv4(addr_family);
 
     g_return_if_fail(NM_IS_DEVICE(self));
-    g_return_if_fail(NM_IN_SET(addr_family, AF_INET, AF_INET6));
 
     priv = NM_DEVICE_GET_PRIVATE(self);
 
     g_return_if_fail(priv->act_request.obj);
 
     activation_source_schedule(self,
-                               IS_IPv4 ? activate_stage4_ip_config_timeout_4
-                                       : activate_stage4_ip_config_timeout_6,
+                               activate_stage4_ip_config_timeout_x_fcn(addr_family),
                                addr_family);
 }
 
@@ -11963,7 +12012,7 @@ activate_stage5_ip_config_result_x(NMDevice *self, int addr_family)
     g_return_if_fail(ip_ifindex);
 
     if (!nm_platform_link_is_up(nm_device_get_platform(self), ip_ifindex)
-        && !nm_device_sys_iface_state_is_external_or_assume(self)) {
+        && !nm_device_sys_iface_state_is_external(self)) {
         nm_platform_link_change_flags(nm_device_get_platform(self), ip_ifindex, IFF_UP, TRUE);
         if (!nm_platform_link_is_up(nm_device_get_platform(self), ip_ifindex))
             _LOGW(LOGD_DEVICE,
@@ -11986,7 +12035,7 @@ activate_stage5_ip_config_result_x(NMDevice *self, int addr_family)
                 /* If IPv6 wasn't the first IP to complete, and DHCP was used,
                  * then ensure dispatcher scripts get the DHCP lease information.
                  */
-                nm_dispatcher_call_device(NM_DISPATCHER_ACTION_DHCP6_CHANGE,
+                nm_dispatcher_call_device(NM_DISPATCHER_ACTION_DHCP_CHANGE_6,
                                           self,
                                           NULL,
                                           NULL,
@@ -12052,7 +12101,7 @@ activate_stage5_ip_config_result_x(NMDevice *self, int addr_family)
          */
         if (priv->dhcp_data_4.client && nm_device_activate_ip4_state_in_conf(self)
             && (nm_device_get_state(self) > NM_DEVICE_STATE_IP_CONFIG)) {
-            nm_dispatcher_call_device(NM_DISPATCHER_ACTION_DHCP4_CHANGE,
+            nm_dispatcher_call_device(NM_DISPATCHER_ACTION_DHCP_CHANGE_4,
                                       self,
                                       NULL,
                                       NULL,
@@ -12425,7 +12474,7 @@ _nm_device_hash_check_invalid_keys(GHashTable *       hash,
 
         g_hash_table_iter_init(&iter, hash);
         while (g_hash_table_iter_next(&iter, (gpointer *) &k, NULL)) {
-            if (nm_utils_strv_find_first((char **) whitelist, -1, k) < 0) {
+            if (nm_strv_find_first(whitelist, -1, k) < 0) {
                 first_invalid_key = k;
                 break;
             }
@@ -12490,6 +12539,7 @@ nm_device_reactivate_ip_config(NMDevice *         self,
                                     s_ip_new,
                                     _prop_get_connection_mdns(self),
                                     _prop_get_connection_llmnr(self),
+                                    _prop_get_connection_dns_over_tls(self),
                                     nm_device_get_route_table(self, AF_INET),
                                     nm_device_get_route_metric(self, AF_INET));
     } else {
@@ -12623,7 +12673,8 @@ can_reapply_change(NMDevice *  self,
                                                  NM_SETTING_CONNECTION_METERED,
                                                  NM_SETTING_CONNECTION_LLDP,
                                                  NM_SETTING_CONNECTION_MDNS,
-                                                 NM_SETTING_CONNECTION_LLMNR);
+                                                 NM_SETTING_CONNECTION_LLMNR,
+                                                 NM_SETTING_CONNECTION_DNS_OVER_TLS);
     }
 
     if (NM_IN_STRSET(setting_name,
@@ -12912,12 +12963,12 @@ reapply_cb(NMDevice *             self,
                                audit_args,
                                subject,
                                local->message);
-        g_dbus_method_invocation_take_error(context, local);
-        local = NULL;
-    } else {
-        nm_audit_log_device_op(NM_AUDIT_OP_DEVICE_REAPPLY, self, TRUE, audit_args, subject, NULL);
-        g_dbus_method_invocation_return_value(context, NULL);
+        g_dbus_method_invocation_take_error(context, g_steal_pointer(&local));
+        return;
     }
+
+    nm_audit_log_device_op(NM_AUDIT_OP_DEVICE_REAPPLY, self, TRUE, audit_args, subject, NULL);
+    g_dbus_method_invocation_return_value(context, NULL);
 }
 
 static void
@@ -14737,15 +14788,15 @@ _unmanaged_flags2str(NMUnmanagedFlags flags, NMUnmanagedFlags mask, char *buf, g
         tmp = buf2;
         while (TRUE) {
             if (add_separator)
-                nm_utils_strbuf_append_c(&b, &len, ',');
+                nm_strbuf_append_c(&b, &len, ',');
             add_separator = TRUE;
 
             tmp2 = strchr(tmp, ',');
             if (tmp2)
                 tmp2[0] = '\0';
 
-            nm_utils_strbuf_append_c(&b, &len, '!');
-            nm_utils_strbuf_append_str(&b, &len, tmp);
+            nm_strbuf_append_c(&b, &len, '!');
+            nm_strbuf_append_str(&b, &len, tmp);
             if (!tmp2)
                 break;
 
@@ -14950,7 +15001,7 @@ _set_unmanaged_flags(NMDevice *          self,
         priv->queued_ip_config_id_4 = g_idle_add(queued_ip4_config_change, self);
         priv->queued_ip_config_id_6 = g_idle_add(queued_ip6_config_change, self);
 
-        if (!priv->pending_actions) {
+        if (priv->pending_actions.len == 0) {
             do_notify_has_pending_actions = TRUE;
             had_pending_actions           = nm_device_has_pending_action(self);
         }
@@ -15004,7 +15055,7 @@ _set_unmanaged_flags(NMDevice *          self,
           flags,
           NM_PRINT_FMT_QUOTED(allow_state_transition,
                               ", reason ",
-                              nm_device_state_reason_to_str_a(reason),
+                              nm_device_state_reason_to_string_a(reason),
                               transition_state ? ", transition-state" : "",
                               ""));
 
@@ -15744,37 +15795,39 @@ gboolean
 nm_device_add_pending_action(NMDevice *self, const char *action, gboolean assert_not_yet_pending)
 {
     NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
-    GSList *         iter;
-    guint            count = 0;
+    gssize           idx;
 
     g_return_val_if_fail(action, FALSE);
 
-    /* Check if the action is already pending. Cannot add duplicate actions */
-    for (iter = priv->pending_actions; iter; iter = iter->next) {
-        if (nm_streq(action, iter->data)) {
-            if (assert_not_yet_pending) {
-                _LOGW(LOGD_DEVICE,
-                      "add_pending_action (%d): '%s' already pending",
-                      count + g_slist_length(iter),
-                      action);
-                g_return_val_if_reached(FALSE);
-            } else {
-                _LOGT(LOGD_DEVICE,
-                      "add_pending_action (%d): '%s' already pending (expected)",
-                      count + g_slist_length(iter),
-                      action);
-            }
-            return FALSE;
+    idx = nm_strv_find_binary_search(priv->pending_actions.arr, priv->pending_actions.len, action);
+    if (idx >= 0) {
+        if (assert_not_yet_pending) {
+            _LOGW(LOGD_DEVICE,
+                  "add_pending_action (%u): '%s' already pending",
+                  priv->pending_actions.len,
+                  action);
+            g_return_val_if_reached(FALSE);
+        } else {
+            _LOGT(LOGD_DEVICE,
+                  "add_pending_action (%u): '%s' already pending (expected)",
+                  priv->pending_actions.len,
+                  action);
         }
-        count++;
+        return FALSE;
     }
 
-    priv->pending_actions = g_slist_prepend(priv->pending_actions, (char *) action);
-    count++;
+    if (priv->pending_actions.len == priv->pending_actions.alloc) {
+        nm_assert(priv->pending_actions.alloc < G_MAXUINT / 2u);
+        priv->pending_actions.alloc = NM_MAX(priv->pending_actions.alloc * 2u, 4u);
+        priv->pending_actions.arr =
+            g_renew(const char *, priv->pending_actions.arr, priv->pending_actions.alloc);
+    }
+    nm_arr_insert_at(priv->pending_actions.arr, priv->pending_actions.len, ~idx, action);
+    priv->pending_actions.len++;
 
-    _LOGD(LOGD_DEVICE, "add_pending_action (%d): '%s'", count, action);
+    _LOGD(LOGD_DEVICE, "add_pending_action (%u): '%s'", priv->pending_actions.len, action);
 
-    if (count == 1)
+    if (priv->pending_actions.len == 1)
         _notify(self, PROP_HAS_PENDING_ACTION);
 
     return TRUE;
@@ -15796,37 +15849,38 @@ gboolean
 nm_device_remove_pending_action(NMDevice *self, const char *action, gboolean assert_is_pending)
 {
     NMDevicePrivate *priv;
-    GSList *         iter, *next;
-    guint            count = 0;
+    gssize           idx;
 
     g_return_val_if_fail(self, FALSE);
     g_return_val_if_fail(action, FALSE);
 
     priv = NM_DEVICE_GET_PRIVATE(self);
 
-    for (iter = priv->pending_actions; iter; iter = next) {
-        next = iter->next;
-        if (nm_streq(action, iter->data)) {
-            _LOGD(LOGD_DEVICE,
-                  "remove_pending_action (%d): '%s'",
-                  count + g_slist_length(iter->next), /* length excluding 'iter' */
-                  action);
-            priv->pending_actions = g_slist_delete_link(priv->pending_actions, iter);
-            if (priv->pending_actions == NULL)
-                _notify(self, PROP_HAS_PENDING_ACTION);
-            return TRUE;
-        }
-        count++;
+    idx = nm_strv_find_binary_search(priv->pending_actions.arr, priv->pending_actions.len, action);
+    if (idx >= 0) {
+        _LOGD(LOGD_DEVICE,
+              "remove_pending_action (%u): '%s'",
+              priv->pending_actions.len - 1u,
+              action);
+        nm_arr_remove_at(priv->pending_actions.arr, priv->pending_actions.len, idx);
+        priv->pending_actions.len--;
+        if (priv->pending_actions.len == 0)
+            _notify(self, PROP_HAS_PENDING_ACTION);
+        return TRUE;
     }
 
     if (assert_is_pending) {
-        _LOGW(LOGD_DEVICE, "remove_pending_action (%d): '%s' not pending", count, action);
+        _LOGW(LOGD_DEVICE,
+              "remove_pending_action (%u): '%s' not pending",
+              priv->pending_actions.len,
+              action);
         g_return_val_if_reached(FALSE);
-    } else
+    } else {
         _LOGT(LOGD_DEVICE,
-              "remove_pending_action (%d): '%s' not pending (expected)",
-              count,
+              "remove_pending_action (%u): '%s' not pending (expected)",
+              priv->pending_actions.len,
               action);
+    }
 
     return FALSE;
 }
@@ -15836,15 +15890,15 @@ nm_device_has_pending_action_reason(NMDevice *self)
 {
     NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
 
-    if (priv->pending_actions) {
-        if (!priv->pending_actions->next && nm_device_get_state(self) == NM_DEVICE_STATE_ACTIVATED
-            && nm_streq(priv->pending_actions->data, NM_PENDING_ACTION_CARRIER_WAIT)) {
+    if (priv->pending_actions.len > 0) {
+        if (priv->pending_actions.len == 1 && nm_device_get_state(self) == NM_DEVICE_STATE_ACTIVATED
+            && nm_streq(priv->pending_actions.arr[0], NM_PENDING_ACTION_CARRIER_WAIT)) {
             /* if the device is already in activated state, and the only reason
              * why it appears still busy is "carrier-wait", then we are already complete. */
             return NULL;
         }
 
-        return priv->pending_actions->data;
+        return priv->pending_actions.arr[0];
     }
 
     if (nm_device_is_real(self)
@@ -16014,7 +16068,7 @@ nm_device_cleanup(NMDevice *self, NMDeviceStateReason reason, CleanupType cleanu
     else
         _LOGD(LOGD_DEVICE,
               "deactivating device (reason '%s') [%d]",
-              nm_device_state_reason_to_str_a(reason),
+              nm_device_state_reason_to_string_a(reason),
               reason);
 
     /* Save whether or not we tried IPv6 for later */
@@ -16045,15 +16099,16 @@ nm_device_cleanup(NMDevice *self, NMDeviceStateReason reason, CleanupType cleanu
 
             nm_platform_ip_route_flush(platform, AF_UNSPEC, ifindex);
             nm_platform_ip_address_flush(platform, AF_UNSPEC, ifindex);
-            set_ipv6_token(self, iid, "::");
+            set_ipv6_token(self, &iid, "::");
 
             if (nm_device_get_applied_setting(self, NM_TYPE_SETTING_TC_CONFIG)) {
-                nm_platform_tfilter_sync(platform, ifindex, NULL);
-                nm_platform_qdisc_sync(platform, ifindex, NULL);
+                nm_platform_tc_sync(platform, ifindex, NULL, NULL);
             }
         }
     }
 
+    priv->tc_committed = FALSE;
+
     _routing_rules_sync(self,
                         cleanup_type == CLEANUP_TYPE_KEEP ? NM_TERNARY_DEFAULT : NM_TERNARY_FALSE);
 
@@ -16508,20 +16563,20 @@ _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason,
         && (state != NM_DEVICE_STATE_UNAVAILABLE || !priv->firmware_missing)) {
         _LOGD(LOGD_DEVICE,
               "state change: %s -> %s (reason '%s', sys-iface-state: '%s'%s)",
-              nm_device_state_to_str(old_state),
-              nm_device_state_to_str(state),
-              nm_device_state_reason_to_str_a(reason),
-              nm_device_sys_iface_state_to_str(priv->sys_iface_state),
+              nm_device_state_to_string(old_state),
+              nm_device_state_to_string(state),
+              nm_device_state_reason_to_string_a(reason),
+              nm_device_sys_iface_state_to_string(priv->sys_iface_state),
               priv->firmware_missing ? ", missing firmware" : "");
         return;
     }
 
     _LOGI(LOGD_DEVICE,
           "state change: %s -> %s (reason '%s', sys-iface-state: '%s')",
-          nm_device_state_to_str(old_state),
-          nm_device_state_to_str(state),
-          nm_device_state_reason_to_str_a(reason),
-          nm_device_sys_iface_state_to_str(priv->sys_iface_state));
+          nm_device_state_to_string(old_state),
+          nm_device_state_to_string(state),
+          nm_device_state_reason_to_string_a(reason),
+          nm_device_sys_iface_state_to_string(priv->sys_iface_state));
 
     /* in order to prevent triggering any callback caused
      * by the device not having any pending action anymore
@@ -16866,8 +16921,8 @@ queued_state_set(gpointer user_data)
 
     _LOGD(LOGD_DEVICE,
           "queue-state[%s, reason:%s, id:%u]: %s",
-          nm_device_state_to_str(priv->queued_state.state),
-          nm_device_state_reason_to_str_a(priv->queued_state.reason),
+          nm_device_state_to_string(priv->queued_state.state),
+          nm_device_state_reason_to_string_a(priv->queued_state.reason),
           priv->queued_state.id,
           "change state");
 
@@ -16879,7 +16934,7 @@ queued_state_set(gpointer user_data)
     new_reason            = priv->queued_state.reason;
 
     nm_device_state_changed(self, new_state, new_reason);
-    nm_device_remove_pending_action(self, nm_device_state_queued_state_to_str(new_state), TRUE);
+    nm_device_remove_pending_action(self, nm_device_state_queued_state_to_string(new_state), TRUE);
 
     return G_SOURCE_REMOVE;
 }
@@ -16896,13 +16951,13 @@ nm_device_queue_state(NMDevice *self, NMDeviceState state, NMDeviceStateReason r
     if (priv->queued_state.id && priv->queued_state.state == state) {
         _LOGD(LOGD_DEVICE,
               "queue-state[%s, reason:%s, id:%u]: %s%s%s%s",
-              nm_device_state_to_str(priv->queued_state.state),
-              nm_device_state_reason_to_str_a(priv->queued_state.reason),
+              nm_device_state_to_string(priv->queued_state.state),
+              nm_device_state_reason_to_string_a(priv->queued_state.reason),
               priv->queued_state.id,
               "ignore queuing same state change",
               NM_PRINT_FMT_QUOTED(priv->queued_state.reason != reason,
                                   " (reason differs: ",
-                                  nm_device_state_reason_to_str_a(reason),
+                                  nm_device_state_reason_to_string_a(reason),
                                   ")",
                                   ""));
         return;
@@ -16910,20 +16965,20 @@ nm_device_queue_state(NMDevice *self, NMDeviceState state, NMDeviceStateReason r
 
     /* Add pending action for the new state before clearing the queued states, so
      * that we don't accidentally pop all pending states and reach 'startup complete'  */
-    nm_device_add_pending_action(self, nm_device_state_queued_state_to_str(state), TRUE);
+    nm_device_add_pending_action(self, nm_device_state_queued_state_to_string(state), TRUE);
 
     /* We should only ever have one delayed state transition at a time */
     if (priv->queued_state.id) {
         _LOGW(LOGD_DEVICE,
               "queue-state[%s, reason:%s, id:%u]: %s",
-              nm_device_state_to_str(priv->queued_state.state),
-              nm_device_state_reason_to_str_a(priv->queued_state.reason),
+              nm_device_state_to_string(priv->queued_state.state),
+              nm_device_state_reason_to_string_a(priv->queued_state.reason),
               priv->queued_state.id,
               "replace previously queued state change");
         nm_clear_g_source(&priv->queued_state.id);
         nm_device_remove_pending_action(
             self,
-            nm_device_state_queued_state_to_str(priv->queued_state.state),
+            nm_device_state_queued_state_to_string(priv->queued_state.state),
             TRUE);
     }
 
@@ -16933,8 +16988,8 @@ nm_device_queue_state(NMDevice *self, NMDeviceState state, NMDeviceStateReason r
 
     _LOGD(LOGD_DEVICE,
           "queue-state[%s, reason:%s, id:%u]: %s",
-          nm_device_state_to_str(state),
-          nm_device_state_reason_to_str_a(reason),
+          nm_device_state_to_string(state),
+          nm_device_state_reason_to_string_a(reason),
           priv->queued_state.id,
           "queue state change");
 }
@@ -16949,14 +17004,15 @@ queued_state_clear(NMDevice *self)
 
     _LOGD(LOGD_DEVICE,
           "queue-state[%s, reason:%s, id:%u]: %s",
-          nm_device_state_to_str(priv->queued_state.state),
-          nm_device_state_reason_to_str_a(priv->queued_state.reason),
+          nm_device_state_to_string(priv->queued_state.state),
+          nm_device_state_reason_to_string_a(priv->queued_state.reason),
           priv->queued_state.id,
           "clear queued state change");
     nm_clear_g_source(&priv->queued_state.id);
-    nm_device_remove_pending_action(self,
-                                    nm_device_state_queued_state_to_str(priv->queued_state.state),
-                                    TRUE);
+    nm_device_remove_pending_action(
+        self,
+        nm_device_state_queued_state_to_string(priv->queued_state.state),
+        TRUE);
 }
 
 NMDeviceState
@@ -17069,11 +17125,11 @@ nm_device_update_permanent_hw_address(NMDevice *self, gboolean force_freeze)
 {
     NMDevicePrivate *              priv = NM_DEVICE_GET_PRIVATE(self);
     guint8                         buf[_NM_UTILS_HWADDR_LEN_MAX];
-    size_t                         len = 0;
     gboolean                       success_read;
     int                            ifindex;
     const NMPlatformLink *         pllink;
     const NMConfigDeviceStateData *dev_state;
+    NMPLinkAddress                 cached_hw_addr_perm;
 
     if (priv->hw_addr_perm) {
         /* the permanent hardware address is only read once and not
@@ -17112,11 +17168,13 @@ nm_device_update_permanent_hw_address(NMDevice *self, gboolean force_freeze)
         return;
     }
 
-    success_read =
-        nm_platform_link_get_permanent_address(nm_device_get_platform(self), ifindex, buf, &len);
-    if (success_read && priv->hw_addr_len == len) {
+    success_read = nm_platform_link_get_permanent_address(nm_device_get_platform(self),
+                                                          pllink,
+                                                          &cached_hw_addr_perm);
+    if (success_read && priv->hw_addr_len == cached_hw_addr_perm.len) {
         priv->hw_addr_perm_fake = FALSE;
-        priv->hw_addr_perm      = nm_utils_hwaddr_ntoa(buf, len);
+        priv->hw_addr_perm =
+            nm_utils_hwaddr_ntoa(cached_hw_addr_perm.data, cached_hw_addr_perm.len);
         _LOGD(LOGD_DEVICE, "hw-addr: read permanent MAC address '%s'", priv->hw_addr_perm);
         goto notify_and_out;
     }
@@ -17791,7 +17849,7 @@ hostname_dns_lookup_callback(GObject *source, GAsyncResult *result, gpointer use
         gboolean valid;
 
         resolver->hostname = g_steal_pointer(&output);
-        valid              = nm_hostname_manager_validate_hostname(resolver->hostname);
+        valid              = nm_utils_validate_hostname(resolver->hostname);
 
         _LOGD(LOGD_DNS,
               "hostname-from-dns: lookup done for %s, result %s%s%s%s",
@@ -17800,7 +17858,7 @@ hostname_dns_lookup_callback(GObject *source, GAsyncResult *result, gpointer use
               valid ? "" : " (invalid)");
 
         if (!valid)
-            g_clear_pointer(&resolver->hostname, g_free);
+            nm_clear_g_free(&resolver->hostname);
     }
 
     nm_clear_g_cancellable(&resolver->cancellable);
@@ -18022,6 +18080,37 @@ _activation_func_to_string(ActivationHandleFunc func)
     g_return_val_if_reached("unknown");
 }
 
+static GVariant *
+_device_get_ports_variant(NMDevice *device)
+{
+    NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(device);
+    SlaveInfo *      info;
+    GVariantBuilder  builder;
+    gboolean         any = FALSE;
+
+    if (priv->ports_variant)
+        return priv->ports_variant;
+
+    c_list_for_each_entry (info, &priv->slaves, lst_slave) {
+        const char *path;
+
+        if (!NM_DEVICE_GET_PRIVATE(info->slave)->is_enslaved)
+            continue;
+        path = nm_dbus_object_get_path(NM_DBUS_OBJECT(info->slave));
+        if (!path)
+            continue;
+        if (!any) {
+            any = TRUE;
+            g_variant_builder_init(&builder, G_VARIANT_TYPE("ao"));
+        }
+        g_variant_builder_add(&builder, "o", path);
+    }
+    priv->ports_variant = any ? g_variant_ref_sink(g_variant_builder_end(&builder))
+                              : g_variant_ref(nm_g_variant_singleton_ao());
+
+    return priv->ports_variant;
+}
+
 /*****************************************************************************/
 
 static void
@@ -18188,29 +18277,9 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
         g_value_set_boolean(value, nm_device_is_real(self));
         break;
     case PROP_SLAVES:
-    {
-        CList *slave_iter;
-        char **slave_list;
-        gsize  i, n;
-
-        n          = c_list_length(&priv->slaves);
-        slave_list = g_new(char *, n + 1);
-        i          = 0;
-        c_list_for_each (slave_iter, &priv->slaves) {
-            SlaveInfo * info = c_list_entry(slave_iter, SlaveInfo, lst_slave);
-            const char *path;
-
-            if (!NM_DEVICE_GET_PRIVATE(info->slave)->is_enslaved)
-                continue;
-            path = nm_dbus_object_get_path(NM_DBUS_OBJECT(info->slave));
-            if (path)
-                slave_list[i++] = g_strdup(path);
-        }
-        nm_assert(i <= n);
-        slave_list[i] = NULL;
-        g_value_take_boxed(value, slave_list);
+    case PROP_PORTS:
+        g_value_set_variant(value, _device_get_ports_variant(self));
         break;
-    }
     case PROP_STATISTICS_REFRESH_RATE_MS:
         g_value_set_uint(value, priv->stats.refresh_rate_ms);
         break;
@@ -18518,7 +18587,7 @@ dispose(GObject *object)
 
     nm_clear_g_source(&priv->check_delete_unrealized_id);
 
-    nm_clear_g_source(&priv->stats.timeout_id);
+    nm_clear_g_source_inst(&priv->stats.timeout_source);
 
     carrier_disconnected_action_cancel(self);
 
@@ -18573,7 +18642,7 @@ finalize(GObject *object)
     g_free(priv->hw_addr);
     g_free(priv->hw_addr_perm);
     g_free(priv->hw_addr_initial);
-    g_slist_free(priv->pending_actions);
+    g_free(priv->pending_actions.arr);
     g_slist_free_full(priv->dad6_failed_addrs, (GDestroyNotify) nmp_object_unref);
     nm_clear_g_free(&priv->physical_port_id);
     g_free(priv->udi);
@@ -18592,6 +18661,8 @@ finalize(GObject *object)
     nm_dbus_track_obj_path_deinit(&priv->parent_device);
     nm_dbus_track_obj_path_deinit(&priv->act_request);
 
+    nm_g_variant_unref(priv->ports_variant);
+
     G_OBJECT_CLASS(nm_device_parent_class)->finalize(object);
 
     /* for testing, NMDeviceTest does not invoke NMDevice::constructed,
@@ -18710,9 +18781,8 @@ static const NMDBusInterfaceInfoExtended interface_info_device = {
             NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("InterfaceFlags",
                                                            "u",
                                                            NM_DEVICE_INTERFACE_FLAGS),
-            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("HwAddress",
-                                                           "s",
-                                                           NM_DEVICE_HW_ADDRESS), ), ),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("HwAddress", "s", NM_DEVICE_HW_ADDRESS),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Ports", "ao", NM_DEVICE_PORTS), ), ),
 };
 
 static const NMDBusInterfaceInfoExtended interface_info_device_statistics = {
@@ -19007,11 +19077,18 @@ nm_device_class_init(NMDeviceClass *klass)
                                                      "",
                                                      FALSE,
                                                      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
-    obj_properties[PROP_SLAVES] = g_param_spec_boxed(NM_DEVICE_SLAVES,
-                                                     "",
-                                                     "",
-                                                     G_TYPE_STRV,
-                                                     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+    obj_properties[PROP_SLAVES] = g_param_spec_variant(NM_DEVICE_SLAVES,
+                                                       "",
+                                                       "",
+                                                       G_VARIANT_TYPE("ao"),
+                                                       NULL,
+                                                       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+    obj_properties[PROP_PORTS]  = g_param_spec_variant(NM_DEVICE_PORTS,
+                                                      "",
+                                                      "",
+                                                      G_VARIANT_TYPE("ao"),
+                                                      NULL,
+                                                      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
 
     obj_properties[PROP_STATISTICS_REFRESH_RATE_MS] =
         g_param_spec_uint(NM_DEVICE_STATISTICS_REFRESH_RATE_MS,
@@ -19113,16 +19190,17 @@ nm_device_class_init(NMDeviceClass *klass)
                                                G_TYPE_OBJECT,
                                                G_TYPE_OBJECT);
 
-    signals[IP6_PREFIX_DELEGATED] = g_signal_new(NM_DEVICE_IP6_PREFIX_DELEGATED,
-                                                 G_OBJECT_CLASS_TYPE(object_class),
-                                                 G_SIGNAL_RUN_FIRST,
-                                                 0,
-                                                 NULL,
-                                                 NULL,
-                                                 NULL,
-                                                 G_TYPE_NONE,
-                                                 1,
-                                                 G_TYPE_POINTER);
+    signals[IP6_PREFIX_DELEGATED] =
+        g_signal_new(NM_DEVICE_IP6_PREFIX_DELEGATED,
+                     G_OBJECT_CLASS_TYPE(object_class),
+                     G_SIGNAL_RUN_FIRST,
+                     0,
+                     NULL,
+                     NULL,
+                     NULL,
+                     G_TYPE_NONE,
+                     1,
+                     G_TYPE_POINTER /* const NMPlatformIP6Address *prefix */);
 
     signals[IP6_SUBNET_NEEDED] = g_signal_new(NM_DEVICE_IP6_SUBNET_NEEDED,
                                               G_OBJECT_CLASS_TYPE(object_class),
diff --git a/src/core/devices/nm-device.h b/src/core/devices/nm-device.h
index f59b6fa8..d967bcb2 100644
--- a/src/core/devices/nm-device.h
+++ b/src/core/devices/nm-device.h
@@ -45,6 +45,7 @@
 #define NM_DEVICE_PHYSICAL_PORT_ID      "physical-port-id"
 #define NM_DEVICE_MTU                   "mtu"
 #define NM_DEVICE_HW_ADDRESS            "hw-address"
+#define NM_DEVICE_PORTS                 "ports"
 
 /* "perm-hw-address" is exposed on D-Bus both for NMDeviceEthernet
  * and NMDeviceWifi. */
diff --git a/src/core/devices/nm-lldp-listener.c b/src/core/devices/nm-lldp-listener.c
index aba972ae..4e178491 100644
--- a/src/core/devices/nm-lldp-listener.c
+++ b/src/core/devices/nm-lldp-listener.c
@@ -32,7 +32,7 @@
 /*****************************************************************************/
 
 struct _NMLldpListener {
-    sd_lldp *   lldp_handle;
+    sd_lldp_rx *lldp_handle;
     GHashTable *lldp_neighbors;
     GVariant *  variant;
 
@@ -711,9 +711,9 @@ lldp_neighbor_to_variant(LldpNeighbor *neigh)
                             g_variant_new_uint32(unaligned_read_be16(data8));
                     break;
                 }
-            } else if (memcmp(oui, SD_LLDP_OUI_MUD, sizeof(oui)) == 0) {
+            } else if (memcmp(oui, SD_LLDP_OUI_IANA, sizeof(oui)) == 0) {
                 switch (subtype) {
-                case SD_LLDP_OUI_SUBTYPE_MUD_USAGE_DESCRIPTION:
+                case SD_LLDP_OUI_IANA_SUBTYPE_MUD:
                     if (!v_mud_url) {
                         gs_free char *s_free = NULL;
                         const char *  s;
@@ -884,12 +884,14 @@ handle_changed:
 }
 
 static void
-lldp_event_handler(sd_lldp *lldp, sd_lldp_event_t event, sd_lldp_neighbor *n, void *userdata)
+lldp_event_handler(sd_lldp_rx *lldp, sd_lldp_rx_event_t event, sd_lldp_neighbor *n, void *userdata)
 {
-    process_lldp_neighbor(
-        userdata,
-        n,
-        !NM_IN_SET(event, SD_LLDP_EVENT_ADDED, SD_LLDP_EVENT_UPDATED, SD_LLDP_EVENT_REFRESHED));
+    process_lldp_neighbor(userdata,
+                          n,
+                          !NM_IN_SET(event,
+                                     SD_LLDP_RX_EVENT_ADDED,
+                                     SD_LLDP_RX_EVENT_UPDATED,
+                                     SD_LLDP_RX_EVENT_REFRESHED));
 }
 
 /*****************************************************************************/
@@ -934,14 +936,14 @@ nm_lldp_listener_new(int                  ifindex,
                      GError **            error)
 {
     NMLldpListener *self = NULL;
-    sd_lldp *       lldp_handle;
+    sd_lldp_rx *    lldp_handle;
     int             r;
 
     g_return_val_if_fail(ifindex > 0, FALSE);
     g_return_val_if_fail(!error || !*error, FALSE);
     g_return_val_if_fail(notify_callback, FALSE);
 
-    r = sd_lldp_new(&lldp_handle);
+    r = sd_lldp_rx_new(&lldp_handle);
     if (r < 0) {
         g_set_error_literal(error,
                             NM_DEVICE_ERROR,
@@ -950,7 +952,7 @@ nm_lldp_listener_new(int                  ifindex,
         return FALSE;
     }
 
-    r = sd_lldp_set_ifindex(lldp_handle, ifindex);
+    r = sd_lldp_rx_set_ifindex(lldp_handle, ifindex);
     if (r < 0) {
         g_set_error_literal(error,
                             NM_DEVICE_ERROR,
@@ -959,7 +961,7 @@ nm_lldp_listener_new(int                  ifindex,
         goto fail_handle;
     }
 
-    r = sd_lldp_set_neighbors_max(lldp_handle, MAX_NEIGHBORS);
+    r = sd_lldp_rx_set_neighbors_max(lldp_handle, MAX_NEIGHBORS);
     nm_assert(r == 0);
 
     self  = g_slice_new(NMLldpListener);
@@ -969,19 +971,19 @@ nm_lldp_listener_new(int                  ifindex,
         .notify_user_data = notify_user_data,
     };
 
-    r = sd_lldp_set_callback(lldp_handle, lldp_event_handler, self);
+    r = sd_lldp_rx_set_callback(lldp_handle, lldp_event_handler, self);
     if (r < 0) {
         g_set_error_literal(error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, "set callback failed");
         goto fail_handle;
     }
 
-    r = sd_lldp_attach_event(lldp_handle, NULL, 0);
+    r = sd_lldp_rx_attach_event(lldp_handle, NULL, 0);
     if (r < 0) {
         g_set_error_literal(error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, "attach event failed");
         goto fail_attached;
     }
 
-    r = sd_lldp_start(lldp_handle);
+    r = sd_lldp_rx_start(lldp_handle);
     if (r < 0) {
         g_set_error_literal(error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, "start failed");
         goto fail_attached;
@@ -997,11 +999,11 @@ nm_lldp_listener_new(int                  ifindex,
     return self;
 
 fail_attached:
-    sd_lldp_detach_event(lldp_handle);
+    sd_lldp_rx_detach_event(lldp_handle);
 fail_handle:
     if (self)
         nm_g_slice_free(self);
-    sd_lldp_unref(lldp_handle);
+    sd_lldp_rx_unref(lldp_handle);
     return NULL;
 }
 
@@ -1010,9 +1012,9 @@ nm_lldp_listener_destroy(NMLldpListener *self)
 {
     g_return_if_fail(self);
 
-    sd_lldp_stop(self->lldp_handle);
-    sd_lldp_detach_event(self->lldp_handle);
-    sd_lldp_unref(self->lldp_handle);
+    sd_lldp_rx_stop(self->lldp_handle);
+    sd_lldp_rx_detach_event(self->lldp_handle);
+    sd_lldp_rx_unref(self->lldp_handle);
 
     nm_clear_g_source_inst(&self->ratelimit_source);
 
diff --git a/src/core/devices/ovs/nm-ovsdb.c b/src/core/devices/ovs/nm-ovsdb.c
index b8d5311a..e5673347 100644
--- a/src/core/devices/ovs/nm-ovsdb.c
+++ b/src/core/devices/ovs/nm-ovsdb.c
@@ -17,6 +17,7 @@
 #include "devices/nm-device.h"
 #include "nm-manager.h"
 #include "nm-setting-ovs-external-ids.h"
+#include "nm-priv-helper-call.h"
 
 /*****************************************************************************/
 
@@ -25,7 +26,7 @@
 /*****************************************************************************/
 
 #if JANSSON_VERSION_HEX < 0x020400
-    #warning "requires at least libjansson 2.4"
+#warning "requires at least libjansson 2.4"
 #endif
 
 typedef struct {
@@ -118,9 +119,8 @@ enum {
 static guint signals[LAST_SIGNAL] = {0};
 
 typedef struct {
-    GSocketClient *    client;
     GSocketConnection *conn;
-    GCancellable *     cancellable;
+    GCancellable *     conn_cancellable;
     char               buf[4096]; /* Input buffer */
     size_t             bufp;      /* Last decoded byte in the input buffer. */
     GString *          input;     /* JSON stream waiting for decoding. */
@@ -753,6 +753,7 @@ _insert_interface(json_t *      params,
     NMSettingOvsInterface *s_ovs_iface;
     NMSettingOvsDpdk *     s_ovs_dpdk;
     NMSettingOvsPatch *    s_ovs_patch;
+    const char *           dpdk_devargs;
     json_t *               options = json_array();
     json_t *               row;
     guint32                mtu = 0;
@@ -777,9 +778,11 @@ _insert_interface(json_t *      params,
         s_ovs_patch = nm_connection_get_setting_ovs_patch(interface);
 
     if (s_ovs_dpdk) {
-        json_array_append_new(
-            options,
-            json_pack("[[s, s]]", "dpdk-devargs", nm_setting_ovs_dpdk_get_devargs(s_ovs_dpdk)));
+        dpdk_devargs = nm_setting_ovs_dpdk_get_devargs(s_ovs_dpdk);
+        if (dpdk_devargs)
+            json_array_append_new(options, json_pack("[[s, s]]", "dpdk-devargs", dpdk_devargs));
+        else
+            json_array_append_new(options, json_array());
     } else if (s_ovs_patch) {
         json_array_append_new(
             options,
@@ -1595,7 +1598,7 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg)
         iter = json_object_iter(ovs);
         s    = json_object_iter_key(iter);
         if (s)
-            nm_utils_strdup_reset(&priv->db_uuid, s);
+            nm_strdup_reset(&priv->db_uuid, s);
     }
 
     json_object_foreach (interface, key, value) {
@@ -1665,8 +1668,8 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg)
 
             nm_assert(nm_streq0(ovs_interface->name, name));
 
-            changed |= nm_utils_strdup_reset(&ovs_interface->type, type);
-            changed |= nm_utils_strdup_reset(&ovs_interface->connection_uuid, connection_uuid);
+            changed |= nm_strdup_reset(&ovs_interface->type, type);
+            changed |= nm_strdup_reset(&ovs_interface->connection_uuid, connection_uuid);
             if (!_external_ids_equal(ovs_interface->external_ids, external_ids_arr)) {
                 NM_SWAP(&ovs_interface->external_ids, &external_ids_arr);
                 changed = TRUE;
@@ -1776,8 +1779,8 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg)
 
             nm_assert(nm_streq0(ovs_port->name, name));
 
-            changed |= nm_utils_strdup_reset(&ovs_port->name, name);
-            changed |= nm_utils_strdup_reset(&ovs_port->connection_uuid, connection_uuid);
+            changed |= nm_strdup_reset(&ovs_port->name, name);
+            changed |= nm_strdup_reset(&ovs_port->connection_uuid, connection_uuid);
             if (nm_strv_ptrarray_cmp(ovs_port->interfaces, interfaces) != 0) {
                 NM_SWAP(&ovs_port->interfaces, &interfaces);
                 changed = TRUE;
@@ -1881,8 +1884,8 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg)
 
             nm_assert(nm_streq0(ovs_bridge->name, name));
 
-            changed = nm_utils_strdup_reset(&ovs_bridge->name, name);
-            changed = nm_utils_strdup_reset(&ovs_bridge->connection_uuid, connection_uuid);
+            changed = nm_strdup_reset(&ovs_bridge->name, name);
+            changed = nm_strdup_reset(&ovs_bridge->connection_uuid, connection_uuid);
             if (nm_strv_ptrarray_cmp(ovs_bridge->ports, ports) != 0) {
                 NM_SWAP(&ovs_bridge->ports, &ports);
                 changed = TRUE;
@@ -2223,7 +2226,7 @@ ovsdb_disconnect(NMOvsdb *self, gboolean retry, gboolean is_disposing)
 
     nm_assert(!retry || !is_disposing);
 
-    if (!priv->client)
+    if (!priv->conn && !priv->conn_cancellable)
         return;
 
     _LOGD("disconnecting from ovsdb, retry %d", retry);
@@ -2250,10 +2253,9 @@ ovsdb_disconnect(NMOvsdb *self, gboolean retry, gboolean is_disposing)
     priv->bufp = 0;
     g_string_truncate(priv->input, 0);
     g_string_truncate(priv->output, 0);
-    g_clear_object(&priv->client);
     g_clear_object(&priv->conn);
     nm_clear_g_free(&priv->db_uuid);
-    nm_clear_g_cancellable(&priv->cancellable);
+    nm_clear_g_cancellable(&priv->conn_cancellable);
 
     if (retry)
         ovsdb_try_connect(self);
@@ -2348,32 +2350,82 @@ _monitor_bridges_cb(NMOvsdb *self, json_t *result, GError *error, gpointer user_
 }
 
 static void
-_client_connect_cb(GObject *source_object, GAsyncResult *res, gpointer user_data)
+_ovsdb_connect_complete_with_fd(NMOvsdb *self, int fd_take)
 {
-    GSocketClient *    client = G_SOCKET_CLIENT(source_object);
-    NMOvsdb *          self   = NM_OVSDB(user_data);
-    NMOvsdbPrivate *   priv;
-    GError *           error = NULL;
-    GSocketConnection *conn;
-
-    conn = g_socket_client_connect_finish(client, res, &error);
-    if (conn == NULL) {
-        if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
-            _LOGI("%s", error->message);
+    NMOvsdbPrivate *priv            = NM_OVSDB_GET_PRIVATE(self);
+    gs_unref_object GSocket *socket = NULL;
+    gs_free_error GError *error     = NULL;
 
+    socket = g_socket_new_from_fd(nm_steal_fd(&fd_take), &error);
+    if (!socket) {
+        _LOGT("connect: failure to open socket for new FD: %s", error->message);
         ovsdb_disconnect(self, FALSE, FALSE);
-        g_clear_error(&error);
         return;
     }
 
-    priv       = NM_OVSDB_GET_PRIVATE(self);
-    priv->conn = conn;
-    g_clear_object(&priv->cancellable);
+    priv->conn = g_socket_connection_factory_create_connection(socket);
+    g_clear_object(&priv->conn_cancellable);
 
     ovsdb_read(self);
     ovsdb_next_command(self);
 }
 
+static void
+_ovsdb_connect_priv_helper_cb(int fd_take, GError *error, gpointer user_data)
+{
+    nm_auto_close int fd = fd_take;
+    NMOvsdb *         self;
+
+    if (nm_utils_error_is_cancelled(error))
+        return;
+
+    self = user_data;
+
+    if (error) {
+        _LOGT("connect: failure to get FD from nm-priv-helper: %s", error->message);
+        ovsdb_disconnect(self, FALSE, FALSE);
+        return;
+    }
+
+    _LOGT("connect: connected successfully with FD from nm-priv-helper");
+    _ovsdb_connect_complete_with_fd(self, nm_steal_fd(&fd));
+}
+
+static void
+_ovsdb_connect_idle(gpointer user_data, GCancellable *cancellable)
+{
+    NMOvsdb *         self;
+    NMOvsdbPrivate *  priv;
+    nm_auto_close int fd        = -1;
+    gs_free_error GError *error = NULL;
+
+    if (g_cancellable_is_cancelled(cancellable))
+        return;
+
+    self = user_data;
+    priv = NM_OVSDB_GET_PRIVATE(self);
+
+    fd = nm_priv_helper_utils_open_fd(NM_PRIV_HELPER_GET_FD_TYPE_OVSDB_SOCKET, &error);
+    if (fd == -ENOENT) {
+        _LOGT("connect: opening %s failed (\"%s\")", NM_OVSDB_SOCKET, error->message);
+        ovsdb_disconnect(self, FALSE, FALSE);
+        return;
+    }
+    if (fd < 0) {
+        _LOGT("connect: opening %s failed (\"%s\"). Retry with nm-priv-helper",
+              NM_OVSDB_SOCKET,
+              error->message);
+        nm_priv_helper_call_get_fd(NM_PRIV_HELPER_GET_FD_TYPE_OVSDB_SOCKET,
+                                   priv->conn_cancellable,
+                                   _ovsdb_connect_priv_helper_cb,
+                                   self);
+        return;
+    }
+
+    _LOGT("connect: opening %s succeeded", NM_OVSDB_SOCKET);
+    _ovsdb_connect_complete_with_fd(self, nm_steal_fd(&fd));
+}
+
 /**
  * ovsdb_try_connect:
  *
@@ -2385,22 +2437,13 @@ static void
 ovsdb_try_connect(NMOvsdb *self)
 {
     NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE(self);
-    GSocketAddress *addr;
 
-    if (priv->client)
+    if (priv->conn || priv->conn_cancellable)
         return;
 
-    /* TODO: This should probably be made configurable via NetworkManager.conf */
-    addr = g_unix_socket_address_new(RUNSTATEDIR "/openvswitch/db.sock");
-
-    priv->client      = g_socket_client_new();
-    priv->cancellable = g_cancellable_new();
-    g_socket_client_connect_async(priv->client,
-                                  G_SOCKET_CONNECTABLE(addr),
-                                  priv->cancellable,
-                                  _client_connect_cb,
-                                  self);
-    g_object_unref(addr);
+    _LOGT("connect: start connecting socket %s on idle", NM_OVSDB_SOCKET);
+    priv->conn_cancellable = g_cancellable_new();
+    nm_utils_invoke_on_idle(priv->conn_cancellable, _ovsdb_connect_idle, self);
 
     /* Queue a monitor call before any other command, ensuring that we have an up
      * to date view of existing bridged that we need for add and remove ops. */
@@ -2534,12 +2577,10 @@ nm_ovsdb_set_external_ids(NMOvsdb *                self,
     gs_unref_hashtable GHashTable *exid_old = NULL;
     gs_unref_hashtable GHashTable *exid_new = NULL;
 
-    exid_old = s_exid_old
-                   ? nm_utils_strdict_clone(_nm_setting_ovs_external_ids_get_data(s_exid_old))
-                   : NULL;
-    exid_new = s_exid_new
-                   ? nm_utils_strdict_clone(_nm_setting_ovs_external_ids_get_data(s_exid_new))
-                   : NULL;
+    exid_old =
+        s_exid_old ? nm_strdict_clone(_nm_setting_ovs_external_ids_get_data(s_exid_old)) : NULL;
+    exid_new =
+        s_exid_new ? nm_strdict_clone(_nm_setting_ovs_external_ids_get_data(s_exid_new)) : NULL;
 
     ovsdb_call_method(self,
                       NULL,
diff --git a/src/core/devices/team/nm-device-team.c b/src/core/devices/team/nm-device-team.c
index 0f1a8c4c..3398e467 100644
--- a/src/core/devices/team/nm-device-team.c
+++ b/src/core/devices/team/nm-device-team.c
@@ -20,6 +20,7 @@
 #include "devices/nm-device-private.h"
 #include "libnm-platform/nm-platform.h"
 #include "nm-config.h"
+#include "libnm-core-aux-intern/nm-libnm-core-utils.h"
 #include "libnm-core-intern/nm-core-internal.h"
 #include "nm-dbus-manager.h"
 #include "nm-ip4-config.h"
@@ -78,8 +79,6 @@ complete_connection(NMDevice *           device,
                     NMConnection *const *existing_connections,
                     GError **            error)
 {
-    NMSettingTeam *s_team;
-
     nm_utils_complete_generic(nm_device_get_platform(device),
                               connection,
                               NM_SETTING_TEAM_SETTING_NAME,
@@ -90,11 +89,7 @@ complete_connection(NMDevice *           device,
                               NULL,
                               TRUE);
 
-    s_team = nm_connection_get_setting_team(connection);
-    if (!s_team) {
-        s_team = (NMSettingTeam *) nm_setting_team_new();
-        nm_connection_add_setting(connection, NM_SETTING(s_team));
-    }
+    _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_TEAM);
 
     return TRUE;
 }
@@ -168,15 +163,10 @@ static void
 update_connection(NMDevice *device, NMConnection *connection)
 {
     NMDeviceTeam *       self   = NM_DEVICE_TEAM(device);
-    NMSettingTeam *      s_team = nm_connection_get_setting_team(connection);
+    NMSettingTeam *      s_team = _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_TEAM);
     NMDeviceTeamPrivate *priv   = NM_DEVICE_TEAM_GET_PRIVATE(self);
     struct teamdctl *    tdc    = priv->tdc;
 
-    if (!s_team) {
-        s_team = (NMSettingTeam *) nm_setting_team_new();
-        nm_connection_add_setting(connection, (NMSetting *) s_team);
-    }
-
     /* Read the configuration only if not already set */
     if (!priv->config && ensure_teamd_connection(device))
         teamd_read_config(self);
@@ -250,11 +240,7 @@ master_update_slave_connection(NMDevice *    self,
         return FALSE;
     }
 
-    s_port = nm_connection_get_setting_team_port(connection);
-    if (!s_port) {
-        s_port = (NMSettingTeamPort *) nm_setting_team_port_new();
-        nm_connection_add_setting(connection, NM_SETTING(s_port));
-    }
+    s_port = _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_TEAM_PORT);
 
     g_object_set(G_OBJECT(s_port), NM_SETTING_TEAM_PORT_CONFIG, port_config, NULL);
     g_free(port_config);
diff --git a/src/core/devices/wifi/nm-device-iwd.c b/src/core/devices/wifi/nm-device-iwd.c
index 56338743..27a3188b 100644
--- a/src/core/devices/wifi/nm-device-iwd.c
+++ b/src/core/devices/wifi/nm-device-iwd.c
@@ -18,6 +18,7 @@
 #include "libnm-glib-aux/nm-ref-string.h"
 #include "nm-iwd-manager.h"
 #include "libnm-core-aux-intern/nm-common-macros.h"
+#include "libnm-core-aux-intern/nm-libnm-core-utils.h"
 #include "nm-setting-8021x.h"
 #include "nm-setting-connection.h"
 #include "nm-setting-wireless-security.h"
@@ -966,7 +967,7 @@ complete_connection(NMDevice *           device,
     gboolean            hidden = FALSE;
     const char *        mode;
 
-    s_wifi = nm_connection_get_setting_wireless(connection);
+    s_wifi = _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_WIRELESS);
 
     mode = s_wifi ? nm_setting_wireless_get_mode(s_wifi) : NULL;
 
@@ -1031,12 +1032,6 @@ complete_connection(NMDevice *           device,
         }
 
         ssid = nm_wifi_ap_get_ssid(ap);
-
-        /* Add a wifi setting if one doesn't exist yet */
-        if (!s_wifi) {
-            s_wifi = (NMSettingWireless *) nm_setting_wireless_new();
-            nm_connection_add_setting(connection, NM_SETTING(s_wifi));
-        }
     }
 
     if (ap) {
@@ -2651,7 +2646,7 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
         break;
     case PROP_ACCESS_POINTS:
         list = nm_wifi_aps_get_paths(&priv->aps_lst_head, TRUE);
-        g_value_take_boxed(value, nm_utils_strv_make_deep_copied(list));
+        g_value_take_boxed(value, nm_strv_make_deep_copied(list));
         break;
     case PROP_ACTIVE_ACCESS_POINT:
         nm_dbus_utils_g_value_set_object_path(value, priv->current_ap);
diff --git a/src/core/devices/wifi/nm-device-olpc-mesh.c b/src/core/devices/wifi/nm-device-olpc-mesh.c
index 040350d5..d829f9ae 100644
--- a/src/core/devices/wifi/nm-device-olpc-mesh.c
+++ b/src/core/devices/wifi/nm-device-olpc-mesh.c
@@ -28,6 +28,7 @@
 #include "nm-setting-connection.h"
 #include "nm-setting-olpc-mesh.h"
 #include "nm-manager.h"
+#include "libnm-core-aux-intern/nm-libnm-core-utils.h"
 #include "libnm-platform/nm-platform.h"
 
 #define _NMLOG_DEVICE_TYPE NMDeviceOlpcMesh
@@ -88,11 +89,7 @@ complete_connection(NMDevice *           device,
 {
     NMSettingOlpcMesh *s_mesh;
 
-    s_mesh = nm_connection_get_setting_olpc_mesh(connection);
-    if (!s_mesh) {
-        s_mesh = (NMSettingOlpcMesh *) nm_setting_olpc_mesh_new();
-        nm_connection_add_setting(connection, NM_SETTING(s_mesh));
-    }
+    s_mesh = _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_OLPC_MESH);
 
     if (!nm_setting_olpc_mesh_get_ssid(s_mesh)) {
         gs_unref_bytes GBytes *ssid = NULL;
diff --git a/src/core/devices/wifi/nm-device-wifi-p2p.c b/src/core/devices/wifi/nm-device-wifi-p2p.c
index a8ea2ed7..67202a67 100644
--- a/src/core/devices/wifi/nm-device-wifi-p2p.c
+++ b/src/core/devices/wifi/nm-device-wifi-p2p.c
@@ -15,6 +15,7 @@
 #include "NetworkManagerUtils.h"
 #include "devices/nm-device-private.h"
 #include "nm-act-request.h"
+#include "libnm-core-aux-intern/nm-libnm-core-utils.h"
 #include "libnm-core-intern/nm-core-internal.h"
 #include "libnm-glib-aux/nm-ref-string.h"
 #include "nm-ip4-config.h"
@@ -302,10 +303,7 @@ complete_connection(NMDevice *           device,
     }
 
     /* Add a Wi-Fi P2P setting if one doesn't exist yet */
-    if (!s_wifi_p2p) {
-        s_wifi_p2p = NM_SETTING_WIFI_P2P(nm_setting_wifi_p2p_new());
-        nm_connection_add_setting(connection, NM_SETTING(s_wifi_p2p));
-    }
+    s_wifi_p2p = _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_WIFI_P2P);
 
     g_object_set(G_OBJECT(s_wifi_p2p), NM_SETTING_WIFI_P2P_PEER, setting_peer, NULL);
 
@@ -560,14 +558,39 @@ act_stage3_ip_config_start(NMDevice *           device,
                            gpointer *           out_config,
                            NMDeviceStateReason *out_failure_reason)
 {
-    gboolean      indicate_addressing_running;
-    NMConnection *connection;
-    const char *  method;
+    NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE(device);
+    gboolean                indicate_addressing_running;
+    NMConnection *          connection;
+    const char *            method;
 
     connection = nm_device_get_applied_connection(device);
 
     method = nm_utils_get_ip_config_method(connection, addr_family);
 
+    /* We may have an address assigned by the group owner */
+    if (NM_IN_STRSET(method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) && priv->group_iface
+        && !nm_supplicant_interface_get_p2p_group_owner(priv->group_iface)) {
+        in_addr_t addr;
+        guint8    plen;
+
+        if (nm_supplicant_interface_get_p2p_assigned_addr(priv->group_iface, &addr, &plen)) {
+            NMPlatformIP4Address address = {
+                .addr_source = NM_IP_CONFIG_SOURCE_DHCP,
+            };
+            gs_unref_object NMIP4Config *ip4_config = NULL;
+
+            nm_platform_ip4_address_set_addr(&address, addr, plen);
+
+            ip4_config = nm_device_ip4_config_new(device);
+            nm_ip4_config_add_address(ip4_config, &address);
+
+            nm_device_set_dev2_ip_config(device, AF_INET, NM_IP_CONFIG(ip4_config));
+
+            /* This just disables the addressing indicator. */
+            method = NM_SETTING_IP4_CONFIG_METHOD_DISABLED;
+        }
+    }
+
     if (addr_family == AF_INET)
         indicate_addressing_running = NM_IN_STRSET(method, NM_SETTING_IP4_CONFIG_METHOD_AUTO);
     else {
@@ -622,6 +645,11 @@ get_auto_ip_config_method(NMDevice *device, int addr_family)
     NMDeviceWifiP2P *       self = NM_DEVICE_WIFI_P2P(device);
     NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE(self);
 
+    if (addr_family == AF_INET && priv->group_iface
+        && !nm_supplicant_interface_get_p2p_group_owner(priv->group_iface)
+        && nm_supplicant_interface_get_p2p_assigned_addr(priv->group_iface, NULL, NULL))
+        return NM_SETTING_IP4_CONFIG_METHOD_DISABLED;
+
     /* Override the AUTO method to mean shared if we are group owner. */
     if (priv->group_iface && nm_supplicant_interface_get_p2p_group_owner(priv->group_iface)) {
         if (addr_family == AF_INET)
@@ -1163,7 +1191,7 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
     switch (prop_id) {
     case PROP_PEERS:
         list = nm_wifi_p2p_peers_get_paths(&priv->peers_lst_head);
-        g_value_take_boxed(value, nm_utils_strv_make_deep_copied(list));
+        g_value_take_boxed(value, nm_strv_make_deep_copied(list));
         break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
diff --git a/src/core/devices/wifi/nm-device-wifi.c b/src/core/devices/wifi/nm-device-wifi.c
index fca2fde5..82896c77 100644
--- a/src/core/devices/wifi/nm-device-wifi.c
+++ b/src/core/devices/wifi/nm-device-wifi.c
@@ -17,6 +17,7 @@
 #include "nm-device-wifi-p2p.h"
 #include "nm-wifi-ap.h"
 #include "libnm-core-aux-intern/nm-common-macros.h"
+#include "libnm-core-aux-intern/nm-libnm-core-utils.h"
 #include "devices/nm-device.h"
 #include "devices/nm-device-private.h"
 #include "nm-dbus-manager.h"
@@ -321,7 +322,7 @@ _scan_request_ssids_track(NMDeviceWifiPrivate *priv, const GPtrArray *ssids)
     now_msec = nm_utils_get_monotonic_timestamp_msec();
 
     if (!priv->scan_request_ssids_hash)
-        priv->scan_request_ssids_hash = g_hash_table_new(nm_pgbytes_hash, nm_pgbytes_equal);
+        priv->scan_request_ssids_hash = g_hash_table_new(nm_pg_bytes_hash, nm_pg_bytes_equal);
 
     /* Do a little dance. New elements shall keep their order as in @ssids, but all
      * new elements should be sorted in the list preexisting elements of the list.
@@ -1205,10 +1206,7 @@ complete_connection(NMDevice *           device,
     }
 
     /* Add a wifi setting if one doesn't exist yet */
-    if (!s_wifi) {
-        s_wifi = (NMSettingWireless *) nm_setting_wireless_new();
-        nm_connection_add_setting(connection, NM_SETTING(s_wifi));
-    }
+    s_wifi = _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_WIRELESS);
 
     if (ap)
         ssid = nm_wifi_ap_get_ssid(ap);
@@ -1571,7 +1569,7 @@ _scan_request_ssids_build_hidden(NMDeviceWifi *self,
     if (ssids) {
         if (ssids->len < max_scan_ssids) {
             /* Add wildcard SSID using a static wildcard SSID used for every scan */
-            g_ptr_array_insert(ssids, 0, g_bytes_ref(nm_gbytes_get_empty()));
+            g_ptr_array_insert(ssids, 0, g_bytes_ref(nm_g_bytes_get_empty()));
         }
         if (ssids->len >= max_scan_ssids) {
             /* there is no more space. Use what we have. */
@@ -1592,10 +1590,10 @@ _scan_request_ssids_build_hidden(NMDeviceWifi *self,
     if (!ssids) {
         ssids = g_ptr_array_new_full(max_scan_ssids, (GDestroyNotify) g_bytes_unref);
         /* Add wildcard SSID using a static wildcard SSID used for every scan */
-        g_ptr_array_insert(ssids, 0, g_bytes_ref(nm_gbytes_get_empty()));
+        g_ptr_array_insert(ssids, 0, g_bytes_ref(nm_g_bytes_get_empty()));
     }
 
-    unique_ssids = g_hash_table_new(nm_gbytes_hash, nm_gbytes_equal);
+    unique_ssids = g_hash_table_new(nm_g_bytes_hash, nm_g_bytes_equal);
     for (i = 1; i < ssids->len; i++) {
         if (!g_hash_table_add(unique_ssids, ssids->pdata[i]))
             nm_assert_not_reached();
@@ -1673,12 +1671,7 @@ _scan_supplicant_request_scan_cb(NMSupplicantInterface *supp_iface,
      * Artificially keep the scanning state on, for another SCAN_EXTRA_DELAY_MSEC msec. */
     nm_clear_g_source_inst(&priv->scan_request_delay_source);
     priv->scan_request_delay_source =
-        nm_g_source_attach(nm_g_timeout_source_new(SCAN_EXTRA_DELAY_MSEC,
-                                                   G_PRIORITY_DEFAULT,
-                                                   _scan_request_delay_cb,
-                                                   self,
-                                                   NULL),
-                           NULL);
+        nm_g_timeout_add_source(SCAN_EXTRA_DELAY_MSEC, _scan_request_delay_cb, self);
 
     g_clear_object(&priv->scan_request_cancellable);
     _scan_notify_is_scanning(self);
@@ -3646,7 +3639,7 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
         break;
     case PROP_ACCESS_POINTS:
         list = nm_wifi_aps_get_paths(&priv->aps_lst_head, TRUE);
-        g_value_take_boxed(value, nm_utils_strv_make_deep_copied(list));
+        g_value_take_boxed(value, nm_strv_make_deep_copied(list));
         break;
     case PROP_ACTIVE_ACCESS_POINT:
         nm_dbus_utils_g_value_set_object_path(value, priv->current_ap);
diff --git a/src/core/devices/wifi/nm-wifi-ap.c b/src/core/devices/wifi/nm-wifi-ap.c
index 20ed6833..72a60885 100644
--- a/src/core/devices/wifi/nm-wifi-ap.c
+++ b/src/core/devices/wifi/nm-wifi-ap.c
@@ -669,7 +669,7 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
         g_value_set_uint(value, priv->rsn_flags);
         break;
     case PROP_SSID:
-        g_value_take_variant(value, nm_utils_gbytes_to_variant_ay(priv->ssid));
+        g_value_take_variant(value, nm_g_bytes_to_variant_ay(priv->ssid));
         break;
     case PROP_FREQUENCY:
         g_value_set_uint(value, priv->freq);
diff --git a/src/core/devices/wifi/nm-wifi-common.c b/src/core/devices/wifi/nm-wifi-common.c
index b98ef222..d029ce7f 100644
--- a/src/core/devices/wifi/nm-wifi-common.c
+++ b/src/core/devices/wifi/nm-wifi-common.c
@@ -13,7 +13,7 @@
 #include "nm-dbus-manager.h"
 
 #if WITH_IWD
-    #include "nm-device-iwd.h"
+#include "nm-device-iwd.h"
 #endif
 
 /*****************************************************************************/
diff --git a/src/core/devices/wifi/nm-wifi-p2p-peer.c b/src/core/devices/wifi/nm-wifi-p2p-peer.c
index 433c2833..ac13952d 100644
--- a/src/core/devices/wifi/nm-wifi-p2p-peer.c
+++ b/src/core/devices/wifi/nm-wifi-p2p-peer.c
@@ -168,7 +168,7 @@ nm_wifi_p2p_peer_set_name(NMWifiP2PPeer *peer, const char *str)
 {
     NMWifiP2PPeerPrivate *priv = NM_WIFI_P2P_PEER_GET_PRIVATE(peer);
 
-    if (!nm_utils_strdup_reset(&priv->name, str))
+    if (!nm_strdup_reset(&priv->name, str))
         return FALSE;
     _notify(peer, PROP_NAME);
     return TRUE;
@@ -187,7 +187,7 @@ nm_wifi_p2p_peer_set_manufacturer(NMWifiP2PPeer *peer, const char *str)
 {
     NMWifiP2PPeerPrivate *priv = NM_WIFI_P2P_PEER_GET_PRIVATE(peer);
 
-    if (!nm_utils_strdup_reset(&priv->manufacturer, str))
+    if (!nm_strdup_reset(&priv->manufacturer, str))
         return FALSE;
     _notify(peer, PROP_MANUFACTURER);
     return TRUE;
@@ -206,7 +206,7 @@ nm_wifi_p2p_peer_set_model(NMWifiP2PPeer *peer, const char *str)
 {
     NMWifiP2PPeerPrivate *priv = NM_WIFI_P2P_PEER_GET_PRIVATE(peer);
 
-    if (!nm_utils_strdup_reset(&priv->model, str))
+    if (!nm_strdup_reset(&priv->model, str))
         return FALSE;
     _notify(peer, PROP_MODEL);
     return TRUE;
@@ -225,7 +225,7 @@ nm_wifi_p2p_peer_set_model_number(NMWifiP2PPeer *peer, const char *str)
 {
     NMWifiP2PPeerPrivate *priv = NM_WIFI_P2P_PEER_GET_PRIVATE(peer);
 
-    if (!nm_utils_strdup_reset(&priv->model_number, str))
+    if (!nm_strdup_reset(&priv->model_number, str))
         return FALSE;
     _notify(peer, PROP_MODEL_NUMBER);
     return TRUE;
@@ -244,7 +244,7 @@ nm_wifi_p2p_peer_set_serial(NMWifiP2PPeer *peer, const char *str)
 {
     NMWifiP2PPeerPrivate *priv = NM_WIFI_P2P_PEER_GET_PRIVATE(peer);
 
-    if (!nm_utils_strdup_reset(&priv->serial, str))
+    if (!nm_strdup_reset(&priv->serial, str))
         return FALSE;
     _notify(peer, PROP_SERIAL);
     return TRUE;
@@ -268,7 +268,7 @@ nm_wifi_p2p_peer_set_wfd_ies(NMWifiP2PPeer *peer, GBytes *wfd_ies)
 
     priv = NM_WIFI_P2P_PEER_GET_PRIVATE(peer);
 
-    if (nm_gbytes_equal0(priv->wfd_ies, wfd_ies))
+    if (nm_g_bytes_equal0(priv->wfd_ies, wfd_ies))
         return FALSE;
 
     wfd_ies_old   = g_steal_pointer(&priv->wfd_ies);
@@ -408,9 +408,9 @@ nm_wifi_p2p_peer_update_from_properties(NMWifiP2PPeer *peer, const NMSupplicantP
 
     /* We currently only use the groups information internally to check if
      * the peer is still joined. */
-    if (!nm_utils_strv_equal(priv->groups, peer_info->groups)) {
+    if (!nm_strv_equal(priv->groups, peer_info->groups)) {
         g_free(priv->groups);
-        priv->groups = nm_utils_strv_dup_packed(peer_info->groups, -1);
+        priv->groups = nm_strv_dup_packed(peer_info->groups, -1);
         changed |= TRUE;
     }
 
@@ -509,7 +509,7 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
         g_value_set_string(value, priv->serial);
         break;
     case PROP_WFD_IES:
-        g_value_take_variant(value, nm_utils_gbytes_to_variant_ay(priv->wfd_ies));
+        g_value_take_variant(value, nm_g_bytes_to_variant_ay(priv->wfd_ies));
         break;
     case PROP_HW_ADDRESS:
         g_value_set_string(value, priv->address);
diff --git a/src/core/devices/wifi/nm-wifi-utils.c b/src/core/devices/wifi/nm-wifi-utils.c
index 15ced990..4939e786 100644
--- a/src/core/devices/wifi/nm-wifi-utils.c
+++ b/src/core/devices/wifi/nm-wifi-utils.c
@@ -13,6 +13,7 @@
 
 #include "nm-utils.h"
 #include "libnm-core-intern/nm-core-internal.h"
+#include "libnm-core-aux-intern/nm-libnm-core-utils.h"
 #include "libnm-core-aux-intern/nm-common-macros.h"
 #include "libnm-base/nm-config-base.h"
 
@@ -679,10 +680,7 @@ nm_wifi_utils_complete_connection(GBytes *      ap_ssid,
     }
 
     /* Everything else requires security */
-    if (!s_wsec) {
-        s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new();
-        nm_connection_add_setting(connection, NM_SETTING(s_wsec));
-    }
+    s_wsec = _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_WIRELESS_SECURITY);
 
     key_mgmt      = nm_setting_wireless_security_get_key_mgmt(s_wsec);
     auth_alg      = nm_setting_wireless_security_get_auth_alg(s_wsec);
@@ -825,21 +823,11 @@ nm_wifi_utils_complete_connection(GBytes *      ap_ssid,
                      "open",
                      NULL);
     } else if (nm_streq0(key_mgmt, "sae") || (ap_rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_SAE)) {
-        g_object_set(s_wsec,
-                     NM_SETTING_WIRELESS_SECURITY_KEY_MGMT,
-                     "sae",
-                     NM_SETTING_WIRELESS_SECURITY_AUTH_ALG,
-                     "open",
-                     NULL);
+        g_object_set(s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "sae", NULL);
     } else if (nm_streq0(key_mgmt, "owe")
                || NM_FLAGS_ANY(ap_rsn_flags,
                                NM_802_11_AP_SEC_KEY_MGMT_OWE | NM_802_11_AP_SEC_KEY_MGMT_OWE_TM)) {
-        g_object_set(s_wsec,
-                     NM_SETTING_WIRELESS_SECURITY_KEY_MGMT,
-                     "owe",
-                     NM_SETTING_WIRELESS_SECURITY_AUTH_ALG,
-                     "open",
-                     NULL);
+        g_object_set(s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "owe", NULL);
     } else if (ap_wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK
                || ap_rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK) {
         g_object_set(s_wsec,
@@ -853,12 +841,7 @@ nm_wifi_utils_complete_connection(GBytes *      ap_ssid,
          */
     } else if (nm_streq0(key_mgmt, "wpa-eap-suite-b-192")
                || (ap_rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_EAP_SUITE_B_192)) {
-        g_object_set(s_wsec,
-                     NM_SETTING_WIRELESS_SECURITY_KEY_MGMT,
-                     "wpa-eap-suite-b-192",
-                     NM_SETTING_WIRELESS_SECURITY_AUTH_ALG,
-                     "open",
-                     NULL);
+        g_object_set(s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-eap-suite-b-192", NULL);
     } else {
         g_set_error_literal(error,
                             NM_CONNECTION_ERROR,
diff --git a/src/core/devices/wifi/tests/test-devices-wifi.c b/src/core/devices/wifi/tests/test-devices-wifi.c
index dc278c94..5d1167cf 100644
--- a/src/core/devices/wifi/tests/test-devices-wifi.c
+++ b/src/core/devices/wifi/tests/test-devices-wifi.c
@@ -1445,7 +1445,7 @@ do_test_ssids_options_to_ptrarray(const char *const *ssids)
         const char *ssid  = ssids[i];
         GBytes *    bytes = ssids_arr->pdata[i];
 
-        g_assert(nm_utils_gbytes_equal_mem(bytes, ssid, strlen(ssid)));
+        g_assert(nm_g_bytes_equal_mem(bytes, ssid, strlen(ssid)));
     }
 }
 
diff --git a/src/core/devices/wwan/nm-device-modem.c b/src/core/devices/wwan/nm-device-modem.c
index 4d892e6f..1b285469 100644
--- a/src/core/devices/wwan/nm-device-modem.c
+++ b/src/core/devices/wwan/nm-device-modem.c
@@ -616,18 +616,20 @@ act_stage3_ip_config_start(NMDevice *           device,
                            gpointer *           out_config,
                            NMDeviceStateReason *out_failure_reason)
 {
-    NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE(device);
-
-    nm_assert_addr_family(addr_family);
+    NMDeviceModemPrivate *priv    = NM_DEVICE_MODEM_GET_PRIVATE(device);
+    gboolean              autoip4 = FALSE;
+    NMActStageReturn      ret;
 
-    if (addr_family == AF_INET) {
-        return nm_modem_stage3_ip4_config_start(priv->modem,
-                                                device,
-                                                NM_DEVICE_CLASS(nm_device_modem_parent_class),
-                                                out_failure_reason);
-    } else {
+    if (!NM_IS_IPv4(addr_family))
         return nm_modem_stage3_ip6_config_start(priv->modem, device, out_failure_reason);
-    }
+
+    ret = nm_modem_stage3_ip4_config_start(priv->modem, device, &autoip4, out_failure_reason);
+
+    if (ret != NM_ACT_STAGE_RETURN_SUCCESS || !autoip4)
+        return ret;
+
+    return NM_DEVICE_CLASS(nm_device_modem_parent_class)
+        ->act_stage3_ip_config_start(device, addr_family, out_config, out_failure_reason);
 }
 
 static void
diff --git a/src/core/devices/wwan/nm-modem-broadband.c b/src/core/devices/wwan/nm-modem-broadband.c
index 0872a8a1..a5139f08 100644
--- a/src/core/devices/wwan/nm-modem-broadband.c
+++ b/src/core/devices/wwan/nm-modem-broadband.c
@@ -11,6 +11,7 @@
 #include <arpa/inet.h>
 #include <libmm-glib.h>
 
+#include "libnm-core-aux-intern/nm-libnm-core-utils.h"
 #include "libnm-core-intern/nm-core-internal.h"
 #include "NetworkManagerUtils.h"
 #include "devices/nm-device-private.h"
@@ -829,11 +830,7 @@ complete_connection(NMModem *            modem,
     if (MODEM_CAPS_3GPP2(modem_caps)) {
         NMSettingCdma *s_cdma;
 
-        s_cdma = nm_connection_get_setting_cdma(connection);
-        if (!s_cdma) {
-            s_cdma = (NMSettingCdma *) nm_setting_cdma_new();
-            nm_connection_add_setting(connection, NM_SETTING(s_cdma));
-        }
+        s_cdma = _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_CDMA);
 
         if (!nm_setting_cdma_get_number(s_cdma))
             g_object_set(G_OBJECT(s_cdma), NM_SETTING_CDMA_NUMBER, "#777", NULL);
diff --git a/src/core/devices/wwan/nm-modem-manager.c b/src/core/devices/wwan/nm-modem-manager.c
index 4fb9a378..8d087d13 100644
--- a/src/core/devices/wwan/nm-modem-manager.c
+++ b/src/core/devices/wwan/nm-modem-manager.c
@@ -12,9 +12,9 @@
 #include <libmm-glib.h>
 
 #if HAVE_LIBSYSTEMD
-    #include <systemd/sd-daemon.h>
+#include <systemd/sd-daemon.h>
 #else
-    #define sd_booted() FALSE
+#define sd_booted() FALSE
 #endif
 
 #include "libnm-std-aux/nm-dbus-compat.h"
@@ -22,7 +22,7 @@
 #include "nm-modem-broadband.h"
 
 #if WITH_OFONO
-    #include "nm-modem-ofono.h"
+#include "nm-modem-ofono.h"
 #endif
 
 #define MODEM_POKE_INTERVAL 120
diff --git a/src/core/devices/wwan/nm-modem.c b/src/core/devices/wwan/nm-modem.c
index 15baa677..ccea69db 100644
--- a/src/core/devices/wwan/nm-modem.c
+++ b/src/core/devices/wwan/nm-modem.c
@@ -526,48 +526,6 @@ static void
 ppp_ip4_config(NMPPPManager *ppp_manager, NMIP4Config *config, gpointer user_data)
 {
     NMModem *self = NM_MODEM(user_data);
-    guint32  i, num;
-    guint32  bad_dns1       = htonl(0x0A0B0C0D);
-    guint32  good_dns1      = htonl(0x04020201); /* GTE nameserver */
-    guint32  bad_dns2       = htonl(0x0A0B0C0E);
-    guint32  good_dns2      = htonl(0x04020202); /* GTE nameserver */
-    gboolean dns_workaround = FALSE;
-
-    /* Work around a PPP bug (#1732) which causes many mobile broadband
-     * providers to return 10.11.12.13 and 10.11.12.14 for the DNS servers.
-     * Apparently fixed in ppp-2.4.5 but we've had some reports that this is
-     * not the case.
-     *
-     * http://git.ozlabs.org/?p=ppp.git;a=commitdiff_plain;h=2e09ef6886bbf00bc5a9a641110f801e372ffde6
-     * http://git.ozlabs.org/?p=ppp.git;a=commitdiff_plain;h=f8191bf07df374f119a07910a79217c7618f113e
-     */
-
-    num = nm_ip4_config_get_num_nameservers(config);
-    if (num == 2) {
-        gboolean found1 = FALSE, found2 = FALSE;
-
-        for (i = 0; i < num; i++) {
-            guint32 ns = nm_ip4_config_get_nameserver(config, i);
-
-            if (ns == bad_dns1)
-                found1 = TRUE;
-            else if (ns == bad_dns2)
-                found2 = TRUE;
-        }
-
-        /* Be somewhat conservative about substitutions; the "bad" nameservers
-         * could actually be valid in some cases, so only substitute if ppp
-         * returns *only* the two bad nameservers.
-         */
-        dns_workaround = (found1 && found2);
-    }
-
-    if (!num || dns_workaround) {
-        _LOGW("compensating for invalid PPP-provided nameservers");
-        nm_ip4_config_reset_nameservers(config);
-        nm_ip4_config_add_nameserver(config, good_dns1);
-        nm_ip4_config_add_nameserver(config, good_dns2);
-    }
 
     g_signal_emit(self, signals[IP4_CONFIG_RESULT], 0, config, NULL);
 }
@@ -727,7 +685,7 @@ ppp_stage3_ip_config_start(NMModem *            self,
 NMActStageReturn
 nm_modem_stage3_ip4_config_start(NMModem *            self,
                                  NMDevice *           device,
-                                 NMDeviceClass *      device_class,
+                                 gboolean *           out_autoip4,
                                  NMDeviceStateReason *out_failure_reason)
 {
     NMModemPrivate * priv;
@@ -740,7 +698,7 @@ nm_modem_stage3_ip4_config_start(NMModem *            self,
 
     g_return_val_if_fail(NM_IS_MODEM(self), NM_ACT_STAGE_RETURN_FAILURE);
     g_return_val_if_fail(NM_IS_DEVICE(device), NM_ACT_STAGE_RETURN_FAILURE);
-    g_return_val_if_fail(NM_IS_DEVICE_CLASS(device_class), NM_ACT_STAGE_RETURN_FAILURE);
+    nm_assert(out_autoip4 && !*out_autoip4);
 
     req = nm_device_get_act_request(device);
     g_return_val_if_fail(req, NM_ACT_STAGE_RETURN_FAILURE);
@@ -774,7 +732,8 @@ nm_modem_stage3_ip4_config_start(NMModem *            self,
         break;
     case NM_MODEM_IP_METHOD_AUTO:
         _LOGD("MODEM_IP_METHOD_AUTO");
-        ret = device_class->act_stage3_ip_config_start(device, AF_INET, NULL, out_failure_reason);
+        *out_autoip4 = TRUE;
+        ret          = NM_ACT_STAGE_RETURN_SUCCESS;
         break;
     default:
         _LOGI("IPv4 configuration disabled");
diff --git a/src/core/devices/wwan/nm-modem.h b/src/core/devices/wwan/nm-modem.h
index 87162cfc..4bc81ff8 100644
--- a/src/core/devices/wwan/nm-modem.h
+++ b/src/core/devices/wwan/nm-modem.h
@@ -210,7 +210,7 @@ void nm_modem_act_stage2_config(NMModem *modem);
 
 NMActStageReturn nm_modem_stage3_ip4_config_start(NMModem *            modem,
                                                   NMDevice *           device,
-                                                  NMDeviceClass *      device_class,
+                                                  gboolean *           out_autoip4,
                                                   NMDeviceStateReason *out_failure_reason);
 
 NMActStageReturn nm_modem_stage3_ip6_config_start(NMModem *            modem,