about summary refs log tree commit diff
path: root/src/core/devices
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2022-05-04 15:35:24 +0200
committerMichael Biebl <biebl@debian.org>2022-05-04 15:35:24 +0200
commit9959fdb2e8ddd06f2161798ca0a39c77d67c652d (patch)
tree2ce24a336d2b1c5fd5dec3090db312eded6c78ba /src/core/devices
parent8c623dddbdebe354cb94bfc559a5371a14865317 (diff)
New upstream version 1.37.92 upstream/1.37.92
Diffstat (limited to 'src/core/devices')
-rw-r--r--src/core/devices/bluetooth/nm-bluez-manager.c1
-rw-r--r--src/core/devices/nm-device-bond.c24
-rw-r--r--src/core/devices/nm-device-bridge.c124
-rw-r--r--src/core/devices/nm-device-ethernet.c2
-rw-r--r--src/core/devices/nm-device-private.h2
-rw-r--r--src/core/devices/nm-device-tun.c6
-rw-r--r--src/core/devices/nm-device.c223
-rw-r--r--src/core/devices/nm-device.h53
-rw-r--r--src/core/devices/ovs/nm-device-ovs-interface.c95
-rw-r--r--src/core/devices/ovs/nm-ovsdb.c53
-rw-r--r--src/core/devices/wifi/nm-device-iwd-p2p.c4
-rw-r--r--src/core/devices/wifi/nm-device-iwd.c4
-rw-r--r--src/core/devices/wifi/nm-device-wifi-p2p.c4
-rw-r--r--src/core/devices/wifi/nm-device-wifi.c133
-rw-r--r--src/core/devices/wifi/nm-iwd-manager.c1
-rw-r--r--src/core/devices/wifi/nm-wifi-ap.c7
-rw-r--r--src/core/devices/wifi/nm-wifi-p2p-peer.c9
-rw-r--r--src/core/devices/wwan/nm-device-modem.c8
-rw-r--r--src/core/devices/wwan/nm-modem-broadband.c2
-rw-r--r--src/core/devices/wwan/nm-modem-ofono.c2
20 files changed, 472 insertions, 285 deletions
diff --git a/src/core/devices/bluetooth/nm-bluez-manager.c b/src/core/devices/bluetooth/nm-bluez-manager.c
index 05f85297..6bc00faf 100644
--- a/src/core/devices/bluetooth/nm-bluez-manager.c
+++ b/src/core/devices/bluetooth/nm-bluez-manager.c
@@ -1352,6 +1352,7 @@ _conn_create_panu_connection(NMBluezManager *self, BzDBusObj *bzobj)
           bzobj->d_device.address);
 
     nm_settings_add_connection(priv->settings,
+                               NULL,
                                connection,
                                NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY,
                                NM_SETTINGS_CONNECTION_ADD_REASON_NONE,
diff --git a/src/core/devices/nm-device-bond.c b/src/core/devices/nm-device-bond.c
index 56b729e9..16896d57 100644
--- a/src/core/devices/nm-device-bond.c
+++ b/src/core/devices/nm-device-bond.c
@@ -37,17 +37,17 @@
         NM_SETTING_BOND_OPTION_USE_CARRIER, NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY,       \
         NM_SETTING_BOND_OPTION_NUM_GRAT_ARP, NM_SETTING_BOND_OPTION_PEER_NOTIF_DELAY
 
-#define OPTIONS_REAPPLY_SUBSET                                                            \
-    NM_SETTING_BOND_OPTION_MIIMON, NM_SETTING_BOND_OPTION_UPDELAY,                        \
-        NM_SETTING_BOND_OPTION_DOWNDELAY, NM_SETTING_BOND_OPTION_ARP_INTERVAL,            \
-        NM_SETTING_BOND_OPTION_ARP_VALIDATE, NM_SETTING_BOND_OPTION_PRIMARY,              \
-        NM_SETTING_BOND_OPTION_AD_ACTOR_SYSTEM, NM_SETTING_BOND_OPTION_AD_ACTOR_SYS_PRIO, \
-        NM_SETTING_BOND_OPTION_ALL_SLAVES_ACTIVE, NM_SETTING_BOND_OPTION_ARP_ALL_TARGETS, \
-        NM_SETTING_BOND_OPTION_FAIL_OVER_MAC, NM_SETTING_BOND_OPTION_LP_INTERVAL,         \
-        NM_SETTING_BOND_OPTION_MIN_LINKS, NM_SETTING_BOND_OPTION_PACKETS_PER_SLAVE,       \
-        NM_SETTING_BOND_OPTION_PRIMARY_RESELECT, NM_SETTING_BOND_OPTION_RESEND_IGMP,      \
-        NM_SETTING_BOND_OPTION_USE_CARRIER, NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY,      \
-        NM_SETTING_BOND_OPTION_NUM_GRAT_ARP, NM_SETTING_BOND_OPTION_PEER_NOTIF_DELAY
+#define OPTIONS_REAPPLY_SUBSET                                                             \
+    NM_SETTING_BOND_OPTION_MIIMON, NM_SETTING_BOND_OPTION_UPDELAY,                         \
+        NM_SETTING_BOND_OPTION_DOWNDELAY, NM_SETTING_BOND_OPTION_ARP_INTERVAL,             \
+        NM_SETTING_BOND_OPTION_ARP_VALIDATE, NM_SETTING_BOND_OPTION_PRIMARY,               \
+        NM_SETTING_BOND_OPTION_AD_ACTOR_SYSTEM, NM_SETTING_BOND_OPTION_AD_ACTOR_SYS_PRIO,  \
+        NM_SETTING_BOND_OPTION_ALL_SLAVES_ACTIVE, NM_SETTING_BOND_OPTION_ARP_ALL_TARGETS,  \
+        NM_SETTING_BOND_OPTION_LP_INTERVAL, NM_SETTING_BOND_OPTION_MIN_LINKS,              \
+        NM_SETTING_BOND_OPTION_PACKETS_PER_SLAVE, NM_SETTING_BOND_OPTION_PRIMARY_RESELECT, \
+        NM_SETTING_BOND_OPTION_RESEND_IGMP, NM_SETTING_BOND_OPTION_USE_CARRIER,            \
+        NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY, NM_SETTING_BOND_OPTION_NUM_GRAT_ARP,      \
+        NM_SETTING_BOND_OPTION_PEER_NOTIF_DELAY
 
 #define OPTIONS_REAPPLY_FULL                                     \
     OPTIONS_REAPPLY_SUBSET, NM_SETTING_BOND_OPTION_ACTIVE_SLAVE, \
@@ -373,7 +373,7 @@ apply_bonding_config(NMDeviceBond *self)
                                                             NM_SETTING_BOND_OPTION_MODE);
     /* Need to release all slaves before we can change bond mode */
     if (!nm_streq0(device_bond_mode, mode_str))
-        nm_device_master_release_slaves(device);
+        nm_device_master_release_slaves_all(device);
 
     set_bond_attr_or_default(device, s_bond, NM_SETTING_BOND_OPTION_MODE);
 
diff --git a/src/core/devices/nm-device-bridge.c b/src/core/devices/nm-device-bridge.c
index 4e4c2637..f11c172a 100644
--- a/src/core/devices/nm-device-bridge.c
+++ b/src/core/devices/nm-device-bridge.c
@@ -22,11 +22,17 @@
 
 /*****************************************************************************/
 
+enum _NMBtCbState {
+    _NM_BT_CB_STATE_NONE    = 0, /* Registration not done    */
+    _NM_BT_CB_STATE_WAIT    = 1, /* Waiting for the callback */
+    _NM_BT_CB_STATE_SUCCESS = 2, /* Callback succeeded       */
+};
+
 struct _NMDeviceBridge {
     NMDevice      parent;
     GCancellable *bt_cancellable;
     bool          vlan_configured : 1;
-    bool          bt_registered : 1;
+    unsigned      bt_cb_state : 2;
 };
 
 struct _NMDeviceBridgeClass {
@@ -76,7 +82,8 @@ check_connection_available(NMDevice                      *device,
         if (!nm_bt_vtable_network_server->is_available(
                 nm_bt_vtable_network_server,
                 bdaddr,
-                (self->bt_cancellable || self->bt_registered) ? device : NULL)) {
+                (self->bt_cancellable || self->bt_cb_state != _NM_BT_CB_STATE_NONE) ? device
+                                                                                    : NULL)) {
             if (bdaddr)
                 nm_utils_error_set(error,
                                    NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
@@ -791,23 +798,68 @@ bridge_set_vlan_options(NMDevice *device, NMSettingBridge *s_bridge)
     return TRUE;
 }
 
+static void
+_platform_lnk_bridge_init_from_setting(NMSettingBridge *s_bridge, NMPlatformLnkBridge *props)
+{
+    *props = (NMPlatformLnkBridge){
+        .forward_delay = _DEFAULT_IF_ZERO(nm_setting_bridge_get_forward_delay(s_bridge) * 100u,
+                                          NM_BRIDGE_FORWARD_DELAY_DEF_SYS),
+        .hello_time    = _DEFAULT_IF_ZERO(nm_setting_bridge_get_hello_time(s_bridge) * 100u,
+                                       NM_BRIDGE_HELLO_TIME_DEF_SYS),
+        .max_age       = _DEFAULT_IF_ZERO(nm_setting_bridge_get_max_age(s_bridge) * 100u,
+                                    NM_BRIDGE_MAX_AGE_DEF_SYS),
+        .ageing_time   = nm_setting_bridge_get_ageing_time(s_bridge) * 100u,
+        .stp_state     = nm_setting_bridge_get_stp(s_bridge),
+        .priority      = nm_setting_bridge_get_priority(s_bridge),
+        .vlan_protocol = to_sysfs_vlan_protocol_sys(nm_setting_bridge_get_vlan_protocol(s_bridge)),
+        .vlan_stats_enabled = nm_setting_bridge_get_vlan_stats_enabled(s_bridge),
+        .group_fwd_mask     = nm_setting_bridge_get_group_forward_mask(s_bridge),
+        .mcast_snooping     = nm_setting_bridge_get_multicast_snooping(s_bridge),
+        .mcast_router =
+            to_sysfs_multicast_router_sys(nm_setting_bridge_get_multicast_router(s_bridge)),
+        .mcast_query_use_ifaddr    = nm_setting_bridge_get_multicast_query_use_ifaddr(s_bridge),
+        .mcast_querier             = nm_setting_bridge_get_multicast_querier(s_bridge),
+        .mcast_hash_max            = nm_setting_bridge_get_multicast_hash_max(s_bridge),
+        .mcast_last_member_count   = nm_setting_bridge_get_multicast_last_member_count(s_bridge),
+        .mcast_startup_query_count = nm_setting_bridge_get_multicast_startup_query_count(s_bridge),
+        .mcast_last_member_interval =
+            nm_setting_bridge_get_multicast_last_member_interval(s_bridge),
+        .mcast_membership_interval = nm_setting_bridge_get_multicast_membership_interval(s_bridge),
+        .mcast_querier_interval    = nm_setting_bridge_get_multicast_querier_interval(s_bridge),
+        .mcast_query_interval      = nm_setting_bridge_get_multicast_query_interval(s_bridge),
+        .mcast_query_response_interval =
+            nm_setting_bridge_get_multicast_query_response_interval(s_bridge),
+        .mcast_startup_query_interval =
+            nm_setting_bridge_get_multicast_startup_query_interval(s_bridge),
+    };
+
+    to_sysfs_group_address_sys(nm_setting_bridge_get_group_address(s_bridge), &props->group_addr);
+}
+
 static NMActStageReturn
 act_stage1_prepare(NMDevice *device, NMDeviceStateReason *out_failure_reason)
 {
-    NMConnection *connection;
-    NMSetting    *s_bridge;
-    const Option *option;
+    NMConnection       *connection;
+    NMSettingBridge    *s_bridge;
+    NMPlatformLnkBridge props;
+    int                 r;
+    int                 ifindex = nm_device_get_ifindex(device);
 
     connection = nm_device_get_applied_connection(device);
     g_return_val_if_fail(connection, NM_ACT_STAGE_RETURN_FAILURE);
 
-    s_bridge = (NMSetting *) nm_connection_get_setting_bridge(connection);
+    s_bridge = nm_connection_get_setting_bridge(connection);
     g_return_val_if_fail(s_bridge, NM_ACT_STAGE_RETURN_FAILURE);
 
-    for (option = master_options; option->name; option++)
-        commit_option(device, s_bridge, option, FALSE);
+    _platform_lnk_bridge_init_from_setting(s_bridge, &props);
+
+    r = nm_platform_link_bridge_change(nm_device_get_platform(device), ifindex, &props);
+    if (r < 0) {
+        NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
+        return NM_ACT_STAGE_RETURN_FAILURE;
+    }
 
-    if (!bridge_set_vlan_options(device, (NMSettingBridge *) s_bridge)) {
+    if (!bridge_set_vlan_options(device, s_bridge)) {
         NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
         return NM_ACT_STAGE_RETURN_FAILURE;
     }
@@ -835,6 +887,7 @@ _bt_register_bridge_cb(GError *error, gpointer user_data)
         return;
     }
 
+    self->bt_cb_state = _NM_BT_CB_STATE_SUCCESS;
     nm_device_activate_schedule_stage2_device_config(NM_DEVICE(self), FALSE);
 }
 
@@ -846,12 +899,12 @@ _nm_device_bridge_notify_unregister_bt_nap(NMDevice *device, const char *reason)
     _LOGD(LOGD_DEVICE,
           "bluetooth NAP server unregistered from bridge: %s%s",
           reason,
-          self->bt_registered ? "" : " (was no longer registered)");
+          self->bt_cb_state != _NM_BT_CB_STATE_NONE ? "" : " (was no longer registered)");
 
     nm_clear_g_cancellable(&self->bt_cancellable);
 
-    if (self->bt_registered) {
-        self->bt_registered = FALSE;
+    if (self->bt_cb_state != _NM_BT_CB_STATE_NONE) {
+        self->bt_cb_state = _NM_BT_CB_STATE_NONE;
         nm_device_state_changed(device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_BT_FAILED);
     }
 }
@@ -879,9 +932,12 @@ act_stage2_config(NMDevice *device, NMDeviceStateReason *out_failure_reason)
     if (self->bt_cancellable)
         return NM_ACT_STAGE_RETURN_POSTPONE;
 
-    if (self->bt_registered)
+    if (self->bt_cb_state == _NM_BT_CB_STATE_WAIT)
         return NM_ACT_STAGE_RETURN_POSTPONE;
 
+    if (self->bt_cb_state == _NM_BT_CB_STATE_SUCCESS)
+        return NM_ACT_STAGE_RETURN_SUCCESS;
+
     self->bt_cancellable = g_cancellable_new();
     if (!nm_bt_vtable_network_server->register_bridge(nm_bt_vtable_network_server,
                                                       nm_setting_bluetooth_get_bdaddr(s_bt),
@@ -895,7 +951,7 @@ act_stage2_config(NMDevice *device, NMDeviceStateReason *out_failure_reason)
         return NM_ACT_STAGE_RETURN_FAILURE;
     }
 
-    self->bt_registered = TRUE;
+    self->bt_cb_state = _NM_BT_CB_STATE_WAIT;
     return NM_ACT_STAGE_RETURN_POSTPONE;
 }
 
@@ -906,14 +962,14 @@ deactivate(NMDevice *device)
 
     _LOGD(LOGD_DEVICE,
           "deactivate bridge%s",
-          self->bt_registered ? " (registered as NAP bluetooth device)" : "");
+          self->bt_cb_state != _NM_BT_CB_STATE_NONE ? " (registered as NAP bluetooth device)" : "");
 
     self->vlan_configured = FALSE;
 
     nm_clear_g_cancellable(&self->bt_cancellable);
 
-    if (self->bt_registered) {
-        self->bt_registered = FALSE;
+    if (self->bt_cb_state != _NM_BT_CB_STATE_NONE) {
+        self->bt_cb_state = _NM_BT_CB_STATE_NONE;
         nm_bt_vtable_network_server->unregister_bridge(nm_bt_vtable_network_server, device);
     }
 }
@@ -1054,39 +1110,7 @@ create_and_realize(NMDevice              *device,
         }
     }
 
-    props = (NMPlatformLnkBridge){
-        .forward_delay = _DEFAULT_IF_ZERO(nm_setting_bridge_get_forward_delay(s_bridge) * 100u,
-                                          NM_BRIDGE_FORWARD_DELAY_DEF_SYS),
-        .hello_time    = _DEFAULT_IF_ZERO(nm_setting_bridge_get_hello_time(s_bridge) * 100u,
-                                       NM_BRIDGE_HELLO_TIME_DEF_SYS),
-        .max_age       = _DEFAULT_IF_ZERO(nm_setting_bridge_get_max_age(s_bridge) * 100u,
-                                    NM_BRIDGE_MAX_AGE_DEF_SYS),
-        .ageing_time   = nm_setting_bridge_get_ageing_time(s_bridge) * 100u,
-        .stp_state     = nm_setting_bridge_get_stp(s_bridge),
-        .priority      = nm_setting_bridge_get_priority(s_bridge),
-        .vlan_protocol = to_sysfs_vlan_protocol_sys(nm_setting_bridge_get_vlan_protocol(s_bridge)),
-        .vlan_stats_enabled = nm_setting_bridge_get_vlan_stats_enabled(s_bridge),
-        .group_fwd_mask     = nm_setting_bridge_get_group_forward_mask(s_bridge),
-        .mcast_snooping     = nm_setting_bridge_get_multicast_snooping(s_bridge),
-        .mcast_router =
-            to_sysfs_multicast_router_sys(nm_setting_bridge_get_multicast_router(s_bridge)),
-        .mcast_query_use_ifaddr    = nm_setting_bridge_get_multicast_query_use_ifaddr(s_bridge),
-        .mcast_querier             = nm_setting_bridge_get_multicast_querier(s_bridge),
-        .mcast_hash_max            = nm_setting_bridge_get_multicast_hash_max(s_bridge),
-        .mcast_last_member_count   = nm_setting_bridge_get_multicast_last_member_count(s_bridge),
-        .mcast_startup_query_count = nm_setting_bridge_get_multicast_startup_query_count(s_bridge),
-        .mcast_last_member_interval =
-            nm_setting_bridge_get_multicast_last_member_interval(s_bridge),
-        .mcast_membership_interval = nm_setting_bridge_get_multicast_membership_interval(s_bridge),
-        .mcast_querier_interval    = nm_setting_bridge_get_multicast_querier_interval(s_bridge),
-        .mcast_query_interval      = nm_setting_bridge_get_multicast_query_interval(s_bridge),
-        .mcast_query_response_interval =
-            nm_setting_bridge_get_multicast_query_response_interval(s_bridge),
-        .mcast_startup_query_interval =
-            nm_setting_bridge_get_multicast_startup_query_interval(s_bridge),
-    };
-
-    to_sysfs_group_address_sys(nm_setting_bridge_get_group_address(s_bridge), &props.group_addr);
+    _platform_lnk_bridge_init_from_setting(s_bridge, &props);
 
     /* If mtu != 0, we set the MTU of the new bridge at creation time. However, kernel will still
      * automatically adjust the MTU of the bridge based on the minimum of the slave's MTU.
diff --git a/src/core/devices/nm-device-ethernet.c b/src/core/devices/nm-device-ethernet.c
index 407f8ab8..32f2fbf9 100644
--- a/src/core/devices/nm-device-ethernet.c
+++ b/src/core/devices/nm-device-ethernet.c
@@ -1014,7 +1014,7 @@ act_stage1_prepare(NMDevice *device, NMDeviceStateReason *out_failure_reason)
      * get confused and fail to negotiate the new connection. (rh #1023503)
      *
      * FIXME(shutdown): when exiting, we also need to wait before quitting,
-     * at least for additional NM_SHUTDOWN_TIMEOUT_MS seconds because
+     * at least for additional NM_SHUTDOWN_TIMEOUT_MAX_MSEC seconds because
      * otherwise after restart the device won't work for the first seconds.
      */
     if (priv->ppp_data.last_pppoe_time_msec != 0) {
diff --git a/src/core/devices/nm-device-private.h b/src/core/devices/nm-device-private.h
index 790bb823..b54aed6a 100644
--- a/src/core/devices/nm-device-private.h
+++ b/src/core/devices/nm-device-private.h
@@ -61,7 +61,7 @@ void nm_device_recheck_available_connections(NMDevice *device);
 void
 nm_device_master_check_slave_physical_port(NMDevice *self, NMDevice *slave, NMLogDomain log_domain);
 
-void nm_device_master_release_slaves(NMDevice *self);
+void nm_device_master_release_slaves_all(NMDevice *self);
 
 void nm_device_set_carrier(NMDevice *self, gboolean carrier);
 
diff --git a/src/core/devices/nm-device-tun.c b/src/core/devices/nm-device-tun.c
index b6c4f88b..e623e9c0 100644
--- a/src/core/devices/nm-device-tun.c
+++ b/src/core/devices/nm-device-tun.c
@@ -542,6 +542,12 @@ create_device(NMDeviceFactory      *factory,
                                           NM_SETTING_TUN_SETTING_NAME),
                          NULL);
 
+    /* OpenvSwitch will create a tun device named ovs-netdev when the datapath selected is netdev */
+    if (nm_streq0(iface, "ovs-netdev")) {
+        *out_ignore = TRUE;
+        return NULL;
+    }
+
     return g_object_new(NM_TYPE_DEVICE_TUN,
                         NM_DEVICE_IFACE,
                         iface,
diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c
index a11486d5..1d9f235a 100644
--- a/src/core/devices/nm-device.c
+++ b/src/core/devices/nm-device.c
@@ -101,6 +101,12 @@
 typedef void (*ActivationHandleFunc)(NMDevice *self);
 
 typedef enum {
+    RELEASE_SLAVE_TYPE_NO_CONFIG,
+    RELEASE_SLAVE_TYPE_CONFIG,
+    RELEASE_SLAVE_TYPE_CONFIG_FORCE,
+} ReleaseSlaveType;
+
+typedef enum {
     CLEANUP_TYPE_KEEP,
     CLEANUP_TYPE_REMOVED,
     CLEANUP_TYPE_DECONFIGURE,
@@ -362,7 +368,6 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMDevice,
                              PROP_FIRMWARE_MISSING,
                              PROP_NM_PLUGIN_MISSING,
                              PROP_TYPE_DESC,
-                             PROP_RFKILL_TYPE,
                              PROP_IFINDEX,
                              PROP_AVAILABLE_CONNECTIONS,
                              PROP_PHYSICAL_PORT_ID,
@@ -474,7 +479,6 @@ typedef struct _NMDevicePrivate {
     char                *driver;
     char                *driver_version;
     char                *firmware_version;
-    RfKillType           rfkill_type;
     bool                 firmware_missing : 1;
     bool                 nm_plugin_missing : 1;
     bool
@@ -1135,7 +1139,7 @@ _prop_get_ipv6_dhcp_duid(NMDevice     *self,
             duid_out = nm_utils_generate_duid_llt(arp_type,
                                                   hwaddr_bin,
                                                   hwaddr_len,
-                                                  nm_utils_host_id_get_timestamp_ns()
+                                                  nm_utils_host_id_get_timestamp_nsec()
                                                       / NM_UTILS_NSEC_PER_SEC);
         }
 
@@ -1236,7 +1240,7 @@ _prop_get_ipv6_dhcp_duid(NMDevice     *self,
              * before. Let's compute the time (in seconds) from 0 to 3 years; then we'll
              * subtract it from the host_id timestamp.
              */
-            time = nm_utils_host_id_get_timestamp_ns() / NM_UTILS_NSEC_PER_SEC;
+            time = nm_utils_host_id_get_timestamp_nsec() / NM_UTILS_NSEC_PER_SEC;
 
             /* don't use too old timestamps. They cannot be expressed in DUID-LLT and
              * would all be truncated to zero. */
@@ -3855,6 +3859,7 @@ update_external_connection(NMDevice *self)
 
     if (connection_new) {
         nm_settings_connection_update(settings_connection,
+                                      NULL,
                                       connection_new,
                                       NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY,
                                       NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
@@ -3893,12 +3898,15 @@ _dev_l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, N
     case NM_L3_CONFIG_NOTIFY_TYPE_PRE_COMMIT:
     {
         const NML3ConfigData *l3cd;
+        NMDeviceState         state = nm_device_get_state(self);
 
-        /* FIXME(l3cfg): MTU handling should be moved to l3cfg. */
-        l3cd = nm_l3cfg_get_combined_l3cd(l3cfg, TRUE);
-        if (l3cd)
-            priv->ip6_mtu = nm_l3_config_data_get_ip6_mtu(l3cd);
-        _commit_mtu(self);
+        if (state >= NM_DEVICE_STATE_IP_CONFIG && state < NM_DEVICE_STATE_DEACTIVATING) {
+            /* FIXME(l3cfg): MTU handling should be moved to l3cfg. */
+            l3cd = nm_l3cfg_get_combined_l3cd(l3cfg, TRUE);
+            if (l3cd)
+                priv->ip6_mtu = nm_l3_config_data_get_ip6_mtu(l3cd);
+            _commit_mtu(self);
+        }
         return;
     }
     case NM_L3_CONFIG_NOTIFY_TYPE_POST_COMMIT:
@@ -3976,7 +3984,9 @@ _dev_l3_cfg_commit_type_reset(NMDevice *self)
         commit_type = NM_L3_CFG_COMMIT_TYPE_NONE;
         goto do_set;
     case NM_DEVICE_SYS_IFACE_STATE_ASSUME:
-        commit_type = NM_L3_CFG_COMMIT_TYPE_ASSUME;
+        /* TODO: NM_DEVICE_SYS_IFACE_STATE_ASSUME, will be dropped from the code.
+         * Meanwhile, the commit type must be updated. */
+        commit_type = NM_L3_CFG_COMMIT_TYPE_UPDATE;
         goto do_set;
     case NM_DEVICE_SYS_IFACE_STATE_MANAGED:
         commit_type = NM_L3_CFG_COMMIT_TYPE_UPDATE;
@@ -4455,9 +4465,9 @@ _parent_set_ifindex(NMDevice *self, int parent_ifindex, gboolean force_check)
             _LOGD(LOGD_DEVICE, "parent: ifindex %d, no device", priv->parent_ifindex);
         else {
             _LOGD(LOGD_DEVICE,
-                  "parent: ifindex %d, device %p, %s",
+                  "parent: ifindex %d, device " NM_HASH_OBFUSCATE_PTR_FMT ", %s",
                   priv->parent_ifindex,
-                  priv->parent_device.obj,
+                  NM_HASH_OBFUSCATE_PTR(priv->parent_device.obj),
                   nm_device_get_iface(priv->parent_device.obj));
         }
 
@@ -4719,7 +4729,7 @@ nm_device_get_ip_iface_identifier(NMDevice           *self,
                                   gboolean           *out_is_token)
 {
     NMSettingIP6Config *s_ip6;
-    const char         *token = NULL;
+    const char         *token;
 
     g_return_val_if_fail(NM_IS_DEVICE(self), FALSE);
 
@@ -4731,13 +4741,12 @@ nm_device_get_ip_iface_identifier(NMDevice           *self,
         g_return_val_if_fail(s_ip6, FALSE);
 
         token = nm_setting_ip6_config_get_token(s_ip6);
-        if (token)
+        if (token) {
             NM_SET_OUT(out_is_token, TRUE);
+            return nm_utils_ipv6_interface_identifier_get_from_token(iid, token);
+        }
     }
-    if (token)
-        return nm_utils_ipv6_interface_identifier_get_from_token(iid, token);
-    else
-        return NM_DEVICE_GET_CLASS(self)->get_ip_iface_identifier(self, iid);
+    return NM_DEVICE_GET_CLASS(self)->get_ip_iface_identifier(self, iid);
 }
 
 const char *
@@ -5162,12 +5171,17 @@ nm_device_get_applied_setting(NMDevice *self, GType setting_type)
     return connection ? nm_connection_get_setting(connection, setting_type) : NULL;
 }
 
-RfKillType
+NMRfkillType
 nm_device_get_rfkill_type(NMDevice *self)
 {
+    NMRfkillType t;
+
     g_return_val_if_fail(NM_IS_DEVICE(self), FALSE);
 
-    return NM_DEVICE_GET_PRIVATE(self)->rfkill_type;
+    t = NM_DEVICE_GET_CLASS(self)->rfkill_type;
+
+    nm_assert(NM_IN_SET(t, NM_RFKILL_TYPE_UNKNOWN, NM_RFKILL_TYPE_WLAN, NM_RFKILL_TYPE_WWAN));
+    return t;
 }
 
 static const char *
@@ -5875,11 +5889,9 @@ static SlaveInfo *
 find_slave_info(NMDevice *self, NMDevice *slave)
 {
     NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
-    CList           *iter;
     SlaveInfo       *info;
 
-    c_list_for_each (iter, &priv->slaves) {
-        info = c_list_entry(iter, SlaveInfo, lst_slave);
+    c_list_for_each_entry (info, &priv->slaves, lst_slave) {
         if (info->slave == slave)
             return info;
     }
@@ -5947,12 +5959,12 @@ nm_device_master_enslave_slave(NMDevice *self, NMDevice *slave, NMConnection *co
 }
 
 /**
- * nm_device_master_release_one_slave:
+ * nm_device_master_release_slave:
  * @self: the master device
  * @slave: the slave device to release
  * @configure: whether @self needs to actually release @slave
- * @force: force the release of @slave even if it wasn't added
- *         to @master by NetworkManager
+ * @release_type: whether @self needs to actually release slave
+ *   and whether that is forced.
  * @reason: the state change reason for the @slave
  *
  * If @self is capable of enslaving other devices (ie it's a bridge, bond, team,
@@ -5960,11 +5972,10 @@ nm_device_master_enslave_slave(NMDevice *self, NMDevice *slave, NMConnection *co
  * updates the state of @self and @slave to reflect its release.
  */
 static void
-nm_device_master_release_one_slave(NMDevice           *self,
-                                   NMDevice           *slave,
-                                   gboolean            configure,
-                                   gboolean            force,
-                                   NMDeviceStateReason reason)
+nm_device_master_release_slave(NMDevice           *self,
+                               NMDevice           *slave,
+                               ReleaseSlaveType    release_type,
+                               NMDeviceStateReason reason)
 {
     NMDevicePrivate          *priv;
     NMDevicePrivate          *slave_priv;
@@ -5973,17 +5984,22 @@ nm_device_master_release_one_slave(NMDevice           *self,
 
     g_return_if_fail(NM_DEVICE(self));
     g_return_if_fail(NM_DEVICE(slave));
-    g_return_if_fail(!force || configure);
+    nm_assert(NM_IN_SET(release_type,
+                        RELEASE_SLAVE_TYPE_NO_CONFIG,
+                        RELEASE_SLAVE_TYPE_CONFIG,
+                        RELEASE_SLAVE_TYPE_CONFIG_FORCE));
     g_return_if_fail(NM_DEVICE_GET_CLASS(self)->release_slave != NULL);
 
     info = find_slave_info(self, slave);
 
     _LOGT(LOGD_CORE,
-          "master: release one slave %p/%s %s%s",
-          slave,
+          "master: release one slave " NM_HASH_OBFUSCATE_PTR_FMT "/%s %s%s",
+          NM_HASH_OBFUSCATE_PTR(slave),
           nm_device_get_iface(slave),
           !info ? "(not registered)" : (info->slave_is_enslaved ? "(enslaved)" : "(not enslaved)"),
-          force ? " (force-configure)" : (configure ? " (configure)" : ""));
+          release_type == RELEASE_SLAVE_TYPE_CONFIG_FORCE
+              ? " (force-configure)"
+              : (release_type == RELEASE_SLAVE_TYPE_CONFIG ? " (configure)" : "(no-config)"));
 
     if (!info)
         g_return_if_reached();
@@ -5995,8 +6011,11 @@ nm_device_master_release_one_slave(NMDevice           *self,
     nm_assert(slave == info->slave);
 
     /* first, let subclasses handle the release ... */
-    if (info->slave_is_enslaved || nm_device_sys_iface_state_is_external(slave) || force)
-        NM_DEVICE_GET_CLASS(self)->release_slave(self, slave, configure);
+    if (info->slave_is_enslaved || nm_device_sys_iface_state_is_external(slave)
+        || release_type >= RELEASE_SLAVE_TYPE_CONFIG_FORCE)
+        NM_DEVICE_GET_CLASS(self)->release_slave(self,
+                                                 slave,
+                                                 release_type >= RELEASE_SLAVE_TYPE_CONFIG);
 
     /* raise notifications about the release, including clearing is_enslaved. */
     nm_device_slave_notify_release(slave, reason);
@@ -6314,7 +6333,8 @@ device_recheck_slave_status(NMDevice *self, const NMPlatformLink *plink)
     plink_master            = nm_platform_link_get(nm_device_get_platform(self), plink->master);
     plink_master_keep_alive = nmp_object_ref(NMP_OBJECT_UP_CAST(plink_master));
 
-    if (master == NULL && plink_master && nm_streq0(plink_master->name, "ovs-system")
+    if (master == NULL && plink_master
+        && NM_IN_STRSET(plink_master->name, "ovs-system", "ovs-netdev")
         && plink_master->type == NM_LINK_TYPE_OPENVSWITCH) {
         _LOGD(LOGD_DEVICE, "the device claimed by openvswitch");
         goto out;
@@ -6330,11 +6350,10 @@ device_recheck_slave_status(NMDevice *self, const NMPlatformLink *plink)
             goto out;
         }
 
-        nm_device_master_release_one_slave(priv->master,
-                                           self,
-                                           FALSE,
-                                           FALSE,
-                                           NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
+        nm_device_master_release_slave(priv->master,
+                                       self,
+                                       RELEASE_SLAVE_TYPE_NO_CONFIG,
+                                       NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
     }
 
     if (master && NM_DEVICE_GET_CLASS(master)->enslave_slave) {
@@ -6638,7 +6657,7 @@ static void
 link_changed_cb(NMPlatform     *platform,
                 int             obj_type_i,
                 int             ifindex,
-                NMPlatformLink *info,
+                NMPlatformLink *pllink,
                 int             change_type_i,
                 NMDevice       *self)
 {
@@ -6651,7 +6670,7 @@ link_changed_cb(NMPlatform     *platform,
     priv = NM_DEVICE_GET_PRIVATE(self);
 
     if (ifindex == nm_device_get_ifindex(self)) {
-        if (!(info->n_ifi_flags & IFF_UP))
+        if (!(pllink->n_ifi_flags & IFF_UP))
             priv->device_link_changed_down = TRUE;
         if (!priv->device_link_changed_id) {
             priv->device_link_changed_id = g_idle_add(device_link_changed, self);
@@ -7004,7 +7023,7 @@ sriov_op_queue(NMDevice               *self,
      *
      * FIXME(shutdown): However, during shutdown we don't have a follow-up write request to cancel
      * this operation and we have to give it at least some time to complete. The solution is that
-     * we register a way to abort the last call during shutdown, and after NM_SHUTDOWN_TIMEOUT_MS
+     * we register a way to abort the last call during shutdown, and after NM_SHUTDOWN_TIMEOUT_MAX_MSEC
      * grace period we pull the plug and cancel it. */
 
     op  = g_slice_new(SriovOp);
@@ -7527,7 +7546,11 @@ slave_state_changed(NMDevice           *slave,
         configure = priv->sys_iface_state == NM_DEVICE_SYS_IFACE_STATE_MANAGED
                     && nm_device_sys_iface_state_get(slave) != NM_DEVICE_SYS_IFACE_STATE_EXTERNAL;
 
-        nm_device_master_release_one_slave(self, slave, configure, FALSE, reason);
+        nm_device_master_release_slave(self,
+                                       slave,
+                                       configure ? RELEASE_SLAVE_TYPE_CONFIG
+                                                 : RELEASE_SLAVE_TYPE_NO_CONFIG,
+                                       reason);
         /* Bridge/bond/team interfaces are left up until manually deactivated */
         if (c_list_is_empty(&priv->slaves) && priv->state == NM_DEVICE_STATE_ACTIVATED)
             _LOGD(LOGD_DEVICE, "last slave removed; remaining activated");
@@ -7565,8 +7588,8 @@ nm_device_master_add_slave(NMDevice *self, NMDevice *slave, gboolean configure)
     info = find_slave_info(self, slave);
 
     _LOGT(LOGD_CORE,
-          "master: add one slave %p/%s%s",
-          slave,
+          "master: add one slave " NM_HASH_OBFUSCATE_PTR_FMT "/%s%s",
+          NM_HASH_OBFUSCATE_PTR(slave),
           nm_device_get_iface(slave),
           info ? " (already registered)" : "");
 
@@ -7624,14 +7647,12 @@ nm_device_master_check_slave_physical_port(NMDevice *self, NMDevice *slave, NMLo
     NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
     const char      *slave_physical_port_id, *existing_physical_port_id;
     SlaveInfo       *info;
-    CList           *iter;
 
     slave_physical_port_id = nm_device_get_physical_port_id(slave);
     if (!slave_physical_port_id)
         return;
 
-    c_list_for_each (iter, &priv->slaves) {
-        info = c_list_entry(iter, SlaveInfo, lst_slave);
+    c_list_for_each_entry (info, &priv->slaves, lst_slave) {
         if (info->slave == slave)
             continue;
 
@@ -7651,13 +7672,13 @@ nm_device_master_check_slave_physical_port(NMDevice *self, NMDevice *slave, NMLo
     }
 }
 
-/* release all slaves */
 void
-nm_device_master_release_slaves(NMDevice *self)
+nm_device_master_release_slaves_all(NMDevice *self)
 {
     NMDevicePrivate    *priv = NM_DEVICE_GET_PRIVATE(self);
     NMDeviceStateReason reason;
-    CList              *iter, *safe;
+    SlaveInfo          *info;
+    SlaveInfo          *safe;
 
     /* Don't release the slaves if this connection doesn't belong to NM. */
     if (nm_device_sys_iface_state_is_external(self))
@@ -7667,9 +7688,7 @@ nm_device_master_release_slaves(NMDevice *self)
     if (priv->state == NM_DEVICE_STATE_FAILED)
         reason = NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED;
 
-    c_list_for_each_safe (iter, safe, &priv->slaves) {
-        SlaveInfo *info = c_list_entry(iter, SlaveInfo, lst_slave);
-
+    c_list_for_each_entry_safe (info, safe, &priv->slaves, lst_slave) {
         if (priv->activation_state_preserve_external_ports
             && nm_device_sys_iface_state_is_external(info->slave)) {
             _LOGT(LOGD_DEVICE,
@@ -7677,7 +7696,7 @@ nm_device_master_release_slaves(NMDevice *self)
                   nm_device_get_iface(info->slave));
             continue;
         }
-        nm_device_master_release_one_slave(self, info->slave, TRUE, FALSE, reason);
+        nm_device_master_release_slave(self, info->slave, RELEASE_SLAVE_TYPE_CONFIG, reason);
     }
 
     /* We only need this flag for a short time. It served its purpose. Clear
@@ -7852,11 +7871,10 @@ nm_device_removed(NMDevice *self, gboolean unconfigure_ip_config)
     if (priv->master) {
         /* this is called when something externally messes with the slave or during shut-down.
          * Release the slave from master, but don't touch the device. */
-        nm_device_master_release_one_slave(priv->master,
-                                           self,
-                                           FALSE,
-                                           FALSE,
-                                           NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
+        nm_device_master_release_slave(priv->master,
+                                       self,
+                                       RELEASE_SLAVE_TYPE_NO_CONFIG,
+                                       NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
     }
 
     _dev_l3_register_l3cds(self, priv->l3cfg, FALSE, unconfigure_ip_config);
@@ -8928,11 +8946,10 @@ master_ready(NMDevice *self, NMActiveConnection *active)
     _LOGD(LOGD_DEVICE, "master connection ready; master device %s", nm_device_get_iface(master));
 
     if (priv->master && priv->master != master)
-        nm_device_master_release_one_slave(priv->master,
-                                           self,
-                                           FALSE,
-                                           FALSE,
-                                           NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
+        nm_device_master_release_slave(priv->master,
+                                       self,
+                                       RELEASE_SLAVE_TYPE_NO_CONFIG,
+                                       NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
 
     /* If the master didn't change, add-slave only rechecks whether to assume a connection. */
     nm_device_master_add_slave(master,
@@ -9214,11 +9231,10 @@ activate_stage1_device_prepare(NMDevice *self)
     if (master)
         master_ready(self, active);
     else if (priv->master) {
-        nm_device_master_release_one_slave(priv->master,
-                                           self,
-                                           TRUE,
-                                           TRUE,
-                                           NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
+        nm_device_master_release_slave(priv->master,
+                                       self,
+                                       RELEASE_SLAVE_TYPE_CONFIG_FORCE,
+                                       NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
     }
 
     nm_device_activate_schedule_stage2_device_config(self, TRUE);
@@ -9434,7 +9450,7 @@ activate_stage2_device_config(NMDevice *self)
     NMActStageReturn ret;
     NMSettingWired  *s_wired;
     gboolean         no_firmware = FALSE;
-    CList           *iter;
+    SlaveInfo       *info;
     NMTernary        accept_all_mac_addresses;
 
     nm_device_state_changed(self, NM_DEVICE_STATE_CONFIG, NM_DEVICE_STATE_REASON_NONE);
@@ -9481,8 +9497,7 @@ activate_stage2_device_config(NMDevice *self)
     }
 
     /* If we have slaves that aren't yet enslaved, do that now */
-    c_list_for_each (iter, &priv->slaves) {
-        SlaveInfo    *info        = c_list_entry(iter, SlaveInfo, lst_slave);
+    c_list_for_each_entry (info, &priv->slaves, lst_slave) {
         NMDeviceState slave_state = nm_device_get_state(info->slave);
 
         if (slave_state == NM_DEVICE_STATE_IP_CONFIG)
@@ -9517,8 +9532,6 @@ activate_stage2_device_config(NMDevice *self)
 
     lldp_setup(self, NM_TERNARY_DEFAULT);
 
-    _commit_mtu(self);
-
     nm_device_activate_schedule_stage3_ip_config(self, TRUE);
 }
 
@@ -10410,14 +10423,12 @@ have_any_ready_slaves(NMDevice *self)
 {
     NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
     SlaveInfo       *info;
-    CList           *iter;
 
     /* Any enslaved slave is "ready" in the generic case as it's
-     * at least >= NM_DEVCIE_STATE_IP_CONFIG and has had Layer 2
+     * at least >= NM_DEVICE_STATE_IP_CONFIG and has had Layer 2
      * properties set up.
      */
-    c_list_for_each (iter, &priv->slaves) {
-        info = c_list_entry(iter, SlaveInfo, lst_slave);
+    c_list_for_each_entry (info, &priv->slaves, lst_slave) {
         if (NM_DEVICE_GET_PRIVATE(info->slave)->is_enslaved)
             return TRUE;
     }
@@ -10440,7 +10451,7 @@ void
 nm_device_use_ip6_subnet(NMDevice *self, const NMPlatformIP6Address *subnet)
 {
     nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL;
-    char                                    sbuf[sizeof(_nm_utils_to_string_buffer)];
+    char                                    sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE];
     NMPlatformIP6Address                    address;
 
     l3cd = nm_device_create_l3_config_data(self, NM_IP_CONFIG_SOURCE_SHARED);
@@ -11864,6 +11875,15 @@ activate_stage3_ip_config(NMDevice *self)
                   nm_device_get_ip_iface(self));
     }
 
+    /* We currently will attach ports in the state change NM_DEVICE_STATE_IP_CONFIG above.
+     * Note that kernel changes the MTU of bond ports, so we want to commit the MTU
+     * afterwards!
+     *
+     * This might reset the MTU to something different from the bond controller and
+     * it might not be a working configuration. But it's what the user asked for, so
+     * let's do it! */
+    _commit_mtu(self);
+
     ipv4_method = nm_device_get_effective_ip_config_method(self, AF_INET);
     if (nm_streq(ipv4_method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) {
         /* "auto" usually means DHCPv4 or autoconf6, but it doesn't have to be. Subclasses
@@ -12455,6 +12475,7 @@ can_reapply_change(NMDevice   *self,
                                                  NM_SETTING_CONNECTION_UUID,
                                                  NM_SETTING_CONNECTION_STABLE_ID,
                                                  NM_SETTING_CONNECTION_AUTOCONNECT,
+                                                 NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES,
                                                  NM_SETTING_CONNECTION_ZONE,
                                                  NM_SETTING_CONNECTION_METERED,
                                                  NM_SETTING_CONNECTION_LLDP,
@@ -15085,7 +15106,7 @@ nm_device_cleanup(NMDevice *self, NMDeviceStateReason reason, CleanupType cleanu
 
     if (cleanup_type == CLEANUP_TYPE_DECONFIGURE) {
         /* master: release slaves */
-        nm_device_master_release_slaves(self);
+        nm_device_master_release_slaves_all(self);
 
         /* Take out any entries in the routing table and any IP address the device had. */
         if (ifindex > 0) {
@@ -15109,12 +15130,12 @@ nm_device_cleanup(NMDevice *self, NMDeviceStateReason reason, CleanupType cleanu
 
     /* slave: mark no longer enslaved */
     if (priv->master && priv->ifindex > 0
-        && nm_platform_link_get_master(nm_device_get_platform(self), priv->ifindex) <= 0)
-        nm_device_master_release_one_slave(priv->master,
-                                           self,
-                                           FALSE,
-                                           FALSE,
-                                           NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
+        && nm_platform_link_get_master(nm_device_get_platform(self), priv->ifindex) <= 0) {
+        nm_device_master_release_slave(priv->master,
+                                       self,
+                                       RELEASE_SLAVE_TYPE_NO_CONFIG,
+                                       NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
+    }
 
     lldp_setup(self, NM_TERNARY_FALSE);
 
@@ -15614,7 +15635,7 @@ _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason,
               sett_conn ? nm_settings_connection_get_id(sett_conn) : "<unknown>");
 
         /* Notify any slaves of the unexpected failure */
-        nm_device_master_release_slaves(self);
+        nm_device_master_release_slaves_all(self);
 
         /* If the connection doesn't yet have a timestamp, set it to zero so that
          * we can distinguish between connections we've tried to activate and have
@@ -17070,9 +17091,6 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
     case PROP_TYPE_DESC:
         g_value_set_string(value, priv->type_desc);
         break;
-    case PROP_RFKILL_TYPE:
-        g_value_set_uint(value, priv->rfkill_type);
-        break;
     case PROP_AVAILABLE_CONNECTIONS:
         nm_dbus_utils_g_value_set_object_path_from_hash(value, priv->available_connections, TRUE);
         break;
@@ -17208,10 +17226,6 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
         /* construct-only */
         priv->type_desc = g_value_dup_string(value);
         break;
-    case PROP_RFKILL_TYPE:
-        /* construct-only */
-        priv->rfkill_type = g_value_get_uint(value);
-        break;
     case PROP_PERM_HW_ADDRESS:
         /* construct-only */
         priv->hw_addr_perm = g_value_dup_string(value);
@@ -17261,7 +17275,6 @@ nm_device_init(NMDevice *self)
     priv->capabilities          = NM_DEVICE_CAP_NM_SUPPORTED;
     priv->state                 = NM_DEVICE_STATE_UNMANAGED;
     priv->state_reason          = NM_DEVICE_STATE_REASON_NONE;
-    priv->rfkill_type           = RFKILL_TYPE_UNKNOWN;
     priv->unmanaged_flags       = NM_UNMANAGED_PLATFORM_INIT;
     priv->unmanaged_mask        = priv->unmanaged_flags;
     priv->available_connections = g_hash_table_new_full(nm_direct_hash, NULL, g_object_unref, NULL);
@@ -17664,6 +17677,8 @@ nm_device_class_init(NMDeviceClass *klass)
     klass->reapply_connection            = reapply_connection;
     klass->set_platform_mtu              = set_platform_mtu;
 
+    klass->rfkill_type = NM_RFKILL_TYPE_UNKNOWN;
+
     obj_properties[PROP_UDI] =
         g_param_spec_string(NM_DEVICE_UDI,
                             "",
@@ -17821,14 +17836,6 @@ nm_device_class_init(NMDeviceClass *klass)
                             "",
                             NULL,
                             G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
-    obj_properties[PROP_RFKILL_TYPE] =
-        g_param_spec_uint(NM_DEVICE_RFKILL_TYPE,
-                          "",
-                          "",
-                          RFKILL_TYPE_WLAN,
-                          RFKILL_TYPE_MAX,
-                          RFKILL_TYPE_UNKNOWN,
-                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
     obj_properties[PROP_IFINDEX] = g_param_spec_int(NM_DEVICE_IFINDEX,
                                                     "",
                                                     "",
diff --git a/src/core/devices/nm-device.h b/src/core/devices/nm-device.h
index a7badb86..80def125 100644
--- a/src/core/devices/nm-device.h
+++ b/src/core/devices/nm-device.h
@@ -65,7 +65,6 @@
 #define NM_DEVICE_SLAVES "slaves" /* partially internal */
 
 #define NM_DEVICE_TYPE_DESC          "type-desc"          /* Internal only */
-#define NM_DEVICE_RFKILL_TYPE        "rfkill-type"        /* Internal only */
 #define NM_DEVICE_IFINDEX            "ifindex"            /* Internal only */
 #define NM_DEVICE_MASTER             "master"             /* Internal only */
 #define NM_DEVICE_HAS_PENDING_ACTION "has-pending-action" /* Internal only */
@@ -105,38 +104,38 @@ typedef enum NMActStageReturn NMActStageReturn;
  * a condition, so that adding a flag might make a connection available that would
  * not be available otherwise. Adding a flag should never make a connection
  * not available if it would be available otherwise. */
-typedef enum { /*< skip >*/
-               NM_DEVICE_CHECK_CON_AVAILABLE_NONE = 0,
+typedef enum {
+    NM_DEVICE_CHECK_CON_AVAILABLE_NONE = 0,
 
-               /* since NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST is a collection of flags with more fine grained
+    /* since NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST is a collection of flags with more fine grained
      * parts, this flag in general indicates that this is a user-request. */
-               _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST = (1L << 0),
+    _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST = (1L << 0),
 
-               /* we also consider devices which have no carrier but are still waiting for the driver
+    /* we also consider devices which have no carrier but are still waiting for the driver
      * to detect carrier. Usually, such devices are not yet available, however for a user-request
      * they are. They might fail later if carrier doesn't come. */
-               _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_WAITING_CARRIER = (1L << 1),
+    _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_WAITING_CARRIER = (1L << 1),
 
-               /* usually, a profile is only available if the Wi-Fi AP is in range. For an
+    /* usually, a profile is only available if the Wi-Fi AP is in range. For an
      * explicit user request, we also consider profiles for APs that are not (yet)
      * visible. */
-               _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_IGNORE_AP = (1L << 2),
+    _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_IGNORE_AP = (1L << 2),
 
-               /* a device can be marked as unmanaged for various reasons. Some of these reasons
+    /* a device can be marked as unmanaged for various reasons. Some of these reasons
      * are authoritative, others not. Non-authoritative reasons can be overruled by
      * `nmcli device set $DEVICE managed yes`. Also, for an explicit user activation
      * request we may want to consider the device as managed. This flag makes devices
      * that are unmanaged appear available. */
-               _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_OVERRULE_UNMANAGED = (1L << 3),
+    _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_OVERRULE_UNMANAGED = (1L << 3),
 
-               /* a collection of flags, that are commonly set for an explicit user-request. */
-               NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST =
-                   _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST
-                   | _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_WAITING_CARRIER
-                   | _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_IGNORE_AP
-                   | _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_OVERRULE_UNMANAGED,
+    /* a collection of flags, that are commonly set for an explicit user-request. */
+    NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST =
+        _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST
+        | _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_WAITING_CARRIER
+        | _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_IGNORE_AP
+        | _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_OVERRULE_UNMANAGED,
 
-               NM_DEVICE_CHECK_CON_AVAILABLE_ALL = (1L << 4) - 1,
+    NM_DEVICE_CHECK_CON_AVAILABLE_ALL = (1L << 4) - 1,
 } NMDeviceCheckConAvailableFlags;
 
 struct _NMDevicePrivate;
@@ -149,19 +148,18 @@ struct _NMDevice {
 
 /* The flags have an relaxing meaning, that means, specifying more flags, can make
  * a device appear more available. It can never make a device less available. */
-typedef enum { /*< skip >*/
-               NM_DEVICE_CHECK_DEV_AVAILABLE_NONE = 0,
+typedef enum {
+    NM_DEVICE_CHECK_DEV_AVAILABLE_NONE = 0,
 
-               /* the device is considered available, even if it has no carrier.
+    /* the device is considered available, even if it has no carrier.
      *
      * For various device types (software devices) we ignore carrier based
      * on the type. So, for them, this flag has no effect anyway. */
-               _NM_DEVICE_CHECK_DEV_AVAILABLE_IGNORE_CARRIER = (1L << 0),
+    _NM_DEVICE_CHECK_DEV_AVAILABLE_IGNORE_CARRIER = (1L << 0),
 
-               NM_DEVICE_CHECK_DEV_AVAILABLE_FOR_USER_REQUEST =
-                   _NM_DEVICE_CHECK_DEV_AVAILABLE_IGNORE_CARRIER,
+    NM_DEVICE_CHECK_DEV_AVAILABLE_FOR_USER_REQUEST = _NM_DEVICE_CHECK_DEV_AVAILABLE_IGNORE_CARRIER,
 
-               NM_DEVICE_CHECK_DEV_AVAILABLE_ALL = (1L << 1) - 1,
+    NM_DEVICE_CHECK_DEV_AVAILABLE_ALL = (1L << 1) - 1,
 } NMDeviceCheckDevAvailableFlags;
 
 typedef void (*NMDeviceDeactivateCallback)(NMDevice *self, GError *error, gpointer user_data);
@@ -209,6 +207,8 @@ typedef struct _NMDeviceClass {
 
     bool can_reapply_change_ovs_external_ids : 1;
 
+    NMRfkillType rfkill_type : 4;
+
     void (*state_changed)(NMDevice           *device,
                           NMDeviceState       new_state,
                           NMDeviceState       old_state,
@@ -410,7 +410,6 @@ typedef struct _NMDeviceClass {
     gboolean (*set_platform_mtu)(NMDevice *self, guint32 mtu);
 
     const char *(*get_dhcp_anycast_address)(NMDevice *self);
-
 } NMDeviceClass;
 
 GType nm_device_get_type(void);
@@ -538,7 +537,7 @@ gboolean nm_device_get_enabled(NMDevice *device);
 
 void nm_device_set_enabled(NMDevice *device, gboolean enabled);
 
-RfKillType nm_device_get_rfkill_type(NMDevice *device);
+NMRfkillType nm_device_get_rfkill_type(NMDevice *device);
 
 /* IPv6 prefix delegation */
 
diff --git a/src/core/devices/ovs/nm-device-ovs-interface.c b/src/core/devices/ovs/nm-device-ovs-interface.c
index 74707d24..6b9d3d7e 100644
--- a/src/core/devices/ovs/nm-device-ovs-interface.c
+++ b/src/core/devices/ovs/nm-device-ovs-interface.c
@@ -13,6 +13,7 @@
 #include "devices/nm-device-private.h"
 #include "nm-active-connection.h"
 #include "nm-setting-connection.h"
+#include "nm-setting-ovs-bridge.h"
 #include "nm-setting-ovs-interface.h"
 #include "nm-setting-ovs-port.h"
 
@@ -23,7 +24,10 @@
 
 typedef struct {
     NMOvsdb *ovsdb;
-    bool     waiting_for_interface : 1;
+    GSource *wait_link_idle_source;
+    gulong   wait_link_signal_id;
+    int      wait_link_ifindex;
+    bool     wait_link_is_waiting : 1;
 } NMDeviceOvsInterfacePrivate;
 
 struct _NMDeviceOvsInterface {
@@ -115,10 +119,10 @@ link_changed(NMDevice *device, const NMPlatformLink *pllink)
 {
     NMDeviceOvsInterfacePrivate *priv = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE(device);
 
-    if (!pllink || !priv->waiting_for_interface)
+    if (!pllink || !priv->wait_link_is_waiting)
         return;
 
-    priv->waiting_for_interface = FALSE;
+    priv->wait_link_is_waiting = FALSE;
 
     if (nm_device_get_state(device) == NM_DEVICE_STATE_IP_CONFIG) {
         if (!nm_device_hw_addr_set_cloned(device,
@@ -199,17 +203,83 @@ ready_for_ip_config(NMDevice *device)
     return nm_device_get_ip_ifindex(device) > 0;
 }
 
+static gboolean
+_set_ip_ifindex_tun(gpointer user_data)
+{
+    NMDevice                    *device = user_data;
+    NMDeviceOvsInterface        *self   = NM_DEVICE_OVS_INTERFACE(device);
+    NMDeviceOvsInterfacePrivate *priv   = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE(self);
+
+    nm_clear_g_source_inst(&priv->wait_link_idle_source);
+
+    priv->wait_link_is_waiting = FALSE;
+    nm_device_set_ip_ifindex(device, priv->wait_link_ifindex);
+
+    nm_device_devip_set_state(device, AF_INET, NM_DEVICE_IP_STATE_PENDING, NULL);
+    nm_device_devip_set_state(device, AF_INET6, NM_DEVICE_IP_STATE_PENDING, NULL);
+    nm_device_activate_schedule_stage3_ip_config(device, FALSE);
+
+    return G_SOURCE_CONTINUE;
+}
+
+static void
+_netdev_tun_link_cb(NMPlatform     *platform,
+                    int             obj_type_i,
+                    int             ifindex,
+                    NMPlatformLink *pllink,
+                    int             change_type_i,
+                    NMDevice       *device)
+{
+    const NMPlatformSignalChangeType change_type = change_type_i;
+    NMDeviceOvsInterface            *self        = NM_DEVICE_OVS_INTERFACE(device);
+    NMDeviceOvsInterfacePrivate     *priv        = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE(self);
+
+    if (change_type == NM_PLATFORM_SIGNAL_ADDED) {
+        if (pllink->type == NM_LINK_TYPE_TUN
+            && nm_streq0(pllink->name, nm_device_get_iface(device))) {
+            nm_clear_g_signal_handler(platform, &priv->wait_link_signal_id);
+
+            priv->wait_link_ifindex = ifindex;
+
+            priv->wait_link_idle_source = nm_g_idle_add_source(_set_ip_ifindex_tun, device);
+        }
+    }
+}
+
 static void
 act_stage3_ip_config(NMDevice *device, int addr_family)
 {
-    NMDeviceOvsInterface        *self = NM_DEVICE_OVS_INTERFACE(device);
-    NMDeviceOvsInterfacePrivate *priv = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE(self);
+    NMActiveConnection          *controller_act = NULL;
+    NMSettingOvsBridge          *s_ovs_bridge   = NULL;
+    NMDeviceOvsInterface        *self           = NM_DEVICE_OVS_INTERFACE(device);
+    NMDeviceOvsInterfacePrivate *priv           = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE(self);
 
     if (!_is_internal_interface(device)) {
         nm_device_devip_set_state(device, addr_family, NM_DEVICE_IP_STATE_READY, NULL);
         return;
     }
 
+    /* When the ovs-bridge controller is using netdev datapath, the interface
+     * link created is a tun device instead of a ovs-interface. NetworkManager must
+     * detect the creation of the tun link and attach the ifindex to the
+     * ovs-interface device. */
+    controller_act = NM_ACTIVE_CONNECTION(nm_device_get_act_request(device));
+    if (controller_act && nm_device_get_ip_ifindex(device) <= 0 && priv->wait_link_signal_id == 0) {
+        controller_act = nm_active_connection_get_master(controller_act);
+        if (controller_act) {
+            controller_act = nm_active_connection_get_master(controller_act);
+            if (controller_act)
+                s_ovs_bridge = nm_connection_get_setting_ovs_bridge(
+                    nm_active_connection_get_applied_connection(controller_act));
+            if (s_ovs_bridge
+                && nm_streq0(nm_setting_ovs_bridge_get_datapath_type(s_ovs_bridge), "netdev"))
+                priv->wait_link_signal_id = g_signal_connect(nm_device_get_platform(device),
+                                                             NM_PLATFORM_SIGNAL_LINK_CHANGED,
+                                                             G_CALLBACK(_netdev_tun_link_cb),
+                                                             self);
+        }
+    }
+
     /* FIXME(l3cfg): we should create the IP ifindex before stage3 start.
      *
      * For now it's here because when the ovs-interface enters stage3, then it's added to the
@@ -219,11 +289,15 @@ act_stage3_ip_config(NMDevice *device, int addr_family)
      * This should change. */
     if (nm_device_get_ip_ifindex(device) <= 0) {
         _LOGT(LOGD_DEVICE, "waiting for link to appear");
-        priv->waiting_for_interface = TRUE;
+        priv->wait_link_is_waiting = TRUE;
         nm_device_devip_set_state(device, addr_family, NM_DEVICE_IP_STATE_PENDING, NULL);
         return;
     }
 
+    priv->wait_link_is_waiting = FALSE;
+    nm_clear_g_source_inst(&priv->wait_link_idle_source);
+    nm_clear_g_signal_handler(nm_device_get_platform(device), &priv->wait_link_signal_id);
+
     if (!nm_device_hw_addr_set_cloned(device, nm_device_get_applied_connection(device), FALSE)) {
         nm_device_devip_set_failed(device, addr_family, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
         return;
@@ -244,7 +318,8 @@ deactivate(NMDevice *device)
     NMDeviceOvsInterface        *self = NM_DEVICE_OVS_INTERFACE(device);
     NMDeviceOvsInterfacePrivate *priv = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE(self);
 
-    priv->waiting_for_interface = FALSE;
+    priv->wait_link_is_waiting = FALSE;
+    nm_clear_g_source_inst(&priv->wait_link_idle_source);
 }
 
 typedef struct {
@@ -351,7 +426,7 @@ deactivate_async(NMDevice                  *device,
         .callback_user_data = callback_user_data,
     };
 
-    if (!priv->waiting_for_interface
+    if (!priv->wait_link_is_waiting
         && !nm_platform_link_get_by_ifname(nm_device_get_platform(device),
                                            nm_device_get_iface(device))) {
         _LOGT(LOGD_CORE, "deactivate: link not present, proceeding");
@@ -360,7 +435,9 @@ deactivate_async(NMDevice                  *device,
         return;
     }
 
-    if (priv->waiting_for_interface) {
+    nm_clear_g_source_inst(&priv->wait_link_idle_source);
+
+    if (priv->wait_link_is_waiting) {
         /* At this point we have issued an INSERT and a DELETE
          * command for the interface to ovsdb. We don't know if
          * vswitchd will see the two updates or only one. We
diff --git a/src/core/devices/ovs/nm-ovsdb.c b/src/core/devices/ovs/nm-ovsdb.c
index 7c45e0e4..44e16cb7 100644
--- a/src/core/devices/ovs/nm-ovsdb.c
+++ b/src/core/devices/ovs/nm-ovsdb.c
@@ -104,6 +104,7 @@ typedef struct {
     OvsdbMethodCallback callback;
     gpointer            user_data;
     OvsdbMethodPayload  payload;
+    GObject            *shutdown_wait_obj;
 } OvsdbMethodCall;
 
 /*****************************************************************************/
@@ -245,6 +246,8 @@ static NM_UTILS_LOOKUP_STR_DEFINE(_device_type_to_table,
 static void
 _call_complete(OvsdbMethodCall *call, json_t *response, GError *error)
 {
+    g_clear_object(&call->shutdown_wait_obj);
+
     if (response) {
         gs_free char *str = NULL;
 
@@ -378,12 +381,14 @@ ovsdb_call_method(NMOvsdb                  *self,
 
     call  = g_slice_new(OvsdbMethodCall);
     *call = (OvsdbMethodCall){
-        .self      = self,
-        .call_id   = CALL_ID_UNSPEC,
-        .command   = command,
-        .callback  = callback,
-        .user_data = user_data,
+        .self              = self,
+        .call_id           = CALL_ID_UNSPEC,
+        .command           = command,
+        .callback          = callback,
+        .user_data         = user_data,
+        .shutdown_wait_obj = g_object_new(G_TYPE_OBJECT, NULL),
     };
+    nm_shutdown_wait_obj_register_object(call->shutdown_wait_obj, "ovsdb-call");
 
     if (add_first)
         c_list_link_front(&priv->calls_lst_head, &call->calls_lst);
@@ -2481,29 +2486,27 @@ typedef struct {
 static void
 _transact_cb(NMOvsdb *self, json_t *result, GError *error, gpointer user_data)
 {
-    OvsdbCall  *call = user_data;
-    const char *err;
-    const char *err_details;
-    size_t      index;
-    json_t     *value;
-
-    if (error)
-        goto out;
-
-    json_array_foreach (result, index, value) {
-        if (json_unpack(value, "{s:s, s:s}", "error", &err, "details", &err_details) == 0) {
-            g_set_error(&error,
-                        G_IO_ERROR,
-                        G_IO_ERROR_FAILED,
-                        "Error running the transaction: %s: %s",
-                        err,
-                        err_details);
-            goto out;
+    OvsdbCall            *call  = user_data;
+    gs_free_error GError *local = NULL;
+    const char           *err;
+    const char           *err_details;
+    size_t                index;
+    json_t               *value;
+
+    if (!error) {
+        json_array_foreach (result, index, value) {
+            if (json_unpack(value, "{s:s, s:s}", "error", &err, "details", &err_details) == 0) {
+                local = g_error_new(G_IO_ERROR,
+                                    G_IO_ERROR_FAILED,
+                                    "Error running the transaction: %s: %s",
+                                    err,
+                                    err_details);
+                break;
+            }
         }
     }
 
-out:
-    call->callback(error, call->user_data);
+    call->callback(local ?: error, call->user_data);
     nm_g_slice_free(call);
 }
 
diff --git a/src/core/devices/wifi/nm-device-iwd-p2p.c b/src/core/devices/wifi/nm-device-iwd-p2p.c
index 01774b12..40e38321 100644
--- a/src/core/devices/wifi/nm-device-iwd-p2p.c
+++ b/src/core/devices/wifi/nm-device-iwd-p2p.c
@@ -1196,8 +1196,6 @@ nm_device_iwd_p2p_new(GDBusObject *dbus_obj)
                         NM_DEVICE_TYPE_WIFI_P2P,
                         NM_DEVICE_LINK_TYPE,
                         NM_LINK_TYPE_WIFI,
-                        NM_DEVICE_RFKILL_TYPE,
-                        RFKILL_TYPE_WLAN,
                         NULL);
 
     if (!self || !nm_device_iwd_p2p_set_dbus_obj(self, dbus_obj))
@@ -1252,6 +1250,8 @@ nm_device_iwd_p2p_class_init(NMDeviceIwdP2PClass *klass)
 
     device_class->state_changed = device_state_changed;
 
+    device_class->rfkill_type = NM_RFKILL_TYPE_WLAN;
+
     obj_properties[PROP_PEERS] = g_param_spec_boxed(NM_DEVICE_IWD_P2P_PEERS,
                                                     "",
                                                     "",
diff --git a/src/core/devices/wifi/nm-device-iwd.c b/src/core/devices/wifi/nm-device-iwd.c
index 7543d9da..ab37cbec 100644
--- a/src/core/devices/wifi/nm-device-iwd.c
+++ b/src/core/devices/wifi/nm-device-iwd.c
@@ -3426,8 +3426,6 @@ nm_device_iwd_new(const char *iface)
                         NM_DEVICE_TYPE_WIFI,
                         NM_DEVICE_LINK_TYPE,
                         NM_LINK_TYPE_WIFI,
-                        NM_DEVICE_RFKILL_TYPE,
-                        RFKILL_TYPE_WLAN,
                         NULL);
 }
 
@@ -3490,6 +3488,8 @@ nm_device_iwd_class_init(NMDeviceIwdClass *klass)
 
     device_class->state_changed = device_state_changed;
 
+    device_class->rfkill_type = NM_RFKILL_TYPE_WLAN;
+
     obj_properties[PROP_MODE] = g_param_spec_uint(NM_DEVICE_IWD_MODE,
                                                   "",
                                                   "",
diff --git a/src/core/devices/wifi/nm-device-wifi-p2p.c b/src/core/devices/wifi/nm-device-wifi-p2p.c
index dfbf8978..4576af95 100644
--- a/src/core/devices/wifi/nm-device-wifi-p2p.c
+++ b/src/core/devices/wifi/nm-device-wifi-p2p.c
@@ -1229,8 +1229,6 @@ nm_device_wifi_p2p_new(const char *iface)
                         NM_DEVICE_TYPE_WIFI_P2P,
                         NM_DEVICE_LINK_TYPE,
                         NM_LINK_TYPE_WIFI,
-                        NM_DEVICE_RFKILL_TYPE,
-                        RFKILL_TYPE_WLAN,
                         NULL);
 }
 
@@ -1293,6 +1291,8 @@ nm_device_wifi_p2p_class_init(NMDeviceWifiP2PClass *klass)
 
     device_class->state_changed = device_state_changed;
 
+    device_class->rfkill_type = NM_RFKILL_TYPE_WLAN;
+
     obj_properties[PROP_PEERS] = g_param_spec_boxed(NM_DEVICE_WIFI_P2P_PEERS,
                                                     "",
                                                     "",
diff --git a/src/core/devices/wifi/nm-device-wifi.c b/src/core/devices/wifi/nm-device-wifi.c
index 010cee08..d83b1f35 100644
--- a/src/core/devices/wifi/nm-device-wifi.c
+++ b/src/core/devices/wifi/nm-device-wifi.c
@@ -978,14 +978,16 @@ deactivate_reset_hw_addr(NMDevice *device)
 static gboolean
 check_connection_compatible(NMDevice *device, NMConnection *connection, GError **error)
 {
-    NMDeviceWifi        *self = NM_DEVICE_WIFI(device);
-    NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE(self);
-    NMSettingWireless   *s_wireless;
-    const char          *mac;
-    const char *const   *mac_blacklist;
-    int                  i;
-    const char          *mode;
-    const char          *perm_hw_addr;
+    NMDeviceWifi              *self = NM_DEVICE_WIFI(device);
+    NMDeviceWifiPrivate       *priv = NM_DEVICE_WIFI_GET_PRIVATE(self);
+    NMSettingWireless         *s_wireless;
+    NMSettingWirelessSecurity *s_wsec;
+    const char                *mac;
+    const char *const         *mac_blacklist;
+    int                        i;
+    const char                *mode;
+    const char                *perm_hw_addr;
+    const char                *key_mgmt;
 
     if (!NM_DEVICE_CLASS(nm_device_wifi_parent_class)
              ->check_connection_compatible(device, connection, error))
@@ -1070,6 +1072,20 @@ check_connection_compatible(NMDevice *device, NMConnection *connection, GError *
         }
     }
 
+    s_wsec = nm_connection_get_setting_wireless_security(connection);
+    if (s_wsec) {
+        key_mgmt = nm_setting_wireless_security_get_key_mgmt(s_wsec);
+
+        if (nm_supplicant_interface_get_capability(priv->sup_iface, NM_SUPPL_CAP_TYPE_WEP)
+                == NM_TERNARY_FALSE
+            && NM_IN_STRSET(key_mgmt, "ieee8021x", "none")) {
+            nm_utils_error_set_literal(error,
+                                       NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+                                       "wpa_supplicant does not support WEP encryption");
+            return FALSE;
+        }
+    }
+
     // FIXME: check channel/freq/band against bands the hardware supports
     // FIXME: check encryption against device capabilities
     // FIXME: check bitrate against device capabilities
@@ -2936,14 +2952,16 @@ build_supplicant_config(NMDeviceWifi *self,
         }
 
         s_8021x = nm_connection_get_setting_802_1x(connection);
-        if (!nm_supplicant_config_add_setting_wireless_security(config,
-                                                                s_wireless_sec,
-                                                                s_8021x,
-                                                                con_uuid,
-                                                                mtu,
-                                                                pmf,
-                                                                fils,
-                                                                error)) {
+        if (!nm_supplicant_config_add_setting_wireless_security(
+                config,
+                s_wireless_sec,
+                s_8021x,
+                con_uuid,
+                nm_setting_wireless_get_mode(s_wireless),
+                mtu,
+                pmf,
+                fils,
+                error)) {
             g_prefix_error(error, "802-11-wireless-security: ");
             goto error;
         }
@@ -3103,28 +3121,75 @@ act_stage1_prepare(NMDevice *device, NMDeviceStateReason *out_failure_reason)
 static void
 ensure_hotspot_frequency(NMDeviceWifi *self, NMSettingWireless *s_wifi, NMWifiAP *ap)
 {
-    NMDevice     *device     = NM_DEVICE(self);
-    const char   *band       = nm_setting_wireless_get_band(s_wifi);
-    const guint32 a_freqs[]  = {5180, 5200, 5220, 5745, 5765, 5785, 5805, 0};
-    const guint32 bg_freqs[] = {2412, 2437, 2462, 2472, 0};
-    guint32       freq       = 0;
+    guint32     a_freqs[]  = {5180, 5200, 5220, 5745, 5765, 5785, 5805, 0};
+    guint32     bg_freqs[] = {2412, 2437, 2462, 2472, 0};
+    guint32    *rnd_freqs;
+    guint       rnd_freqs_len;
+    NMDevice   *device = NM_DEVICE(self);
+    const char *band   = nm_setting_wireless_get_band(s_wifi);
+    guint32     freq;
+    guint64     rnd;
+    guint       i;
+    guint       l;
 
-    g_assert(ap);
+    nm_assert(ap);
+    nm_assert(NM_IN_STRSET(band, NULL, "a", "bg"));
 
     if (nm_wifi_ap_get_freq(ap))
         return;
 
-    if (g_strcmp0(band, "a") == 0)
-        freq = nm_platform_wifi_find_frequency(nm_device_get_platform(device),
-                                               nm_device_get_ifindex(device),
-                                               a_freqs);
-    else
-        freq = nm_platform_wifi_find_frequency(nm_device_get_platform(device),
-                                               nm_device_get_ifindex(device),
-                                               bg_freqs);
+    {
+        GBytes       *ssid;
+        gsize         ssid_len;
+        const guint8 *ssid_data;
+        const guint8  random_seed[16] = {0x9a,
+                                        0xdc,
+                                        0x86,
+                                        0x9a,
+                                        0xa8,
+                                        0xa2,
+                                        0x07,
+                                        0x97,
+                                        0xbe,
+                                        0x6d,
+                                        0xe6,
+                                        0x99,
+                                        0x9f,
+                                        0xa8,
+                                        0x09,
+                                        0x2b};
+
+        /* Calculate a stable "random" number based on the SSID. */
+        ssid      = nm_setting_wireless_get_ssid(s_wifi);
+        ssid_data = g_bytes_get_data(ssid, &ssid_len);
+        rnd       = c_siphash_hash(random_seed, ssid_data, ssid_len);
+    }
+
+    if (nm_streq0(band, "a")) {
+        rnd_freqs     = a_freqs;
+        rnd_freqs_len = G_N_ELEMENTS(a_freqs) - 1;
+    } else {
+        rnd_freqs     = bg_freqs;
+        rnd_freqs_len = G_N_ELEMENTS(bg_freqs) - 1;
+    }
+
+    /* shuffle the frequencies (inplace). The idea is to choose
+     * a different frequency depending on the SSID. */
+    for (i = 0, l = rnd_freqs_len; l > 1; i++, l--) {
+        /* Add an arbitrary chosen (prime) number to rnd, to get more "random"
+         * numbers. Since we only shuffle a handful of elements, that's good
+         * enough (and stable). */
+        rnd += 5630246189u;
+        NM_SWAP(&rnd_freqs[i], &rnd_freqs[i + (rnd % l)]);
+    }
+
+    freq = nm_platform_wifi_find_frequency(nm_device_get_platform(device),
+                                           nm_device_get_ifindex(device),
+                                           rnd_freqs);
+    if (freq == 0)
+        freq = rnd_freqs[0];
 
-    if (!freq)
-        freq = (g_strcmp0(band, "a") == 0) ? 5180 : 2462;
+    _LOGD(LOGD_WIFI, "set frequency for hotspot AP to %u", freq);
 
     if (nm_wifi_ap_set_freq(ap, freq))
         _ap_dump(self, LOGL_DEBUG, ap, "updated", 0);
@@ -3663,8 +3728,6 @@ nm_device_wifi_new(const char *iface, _NMDeviceWifiCapabilities capabilities)
                         NM_DEVICE_TYPE_WIFI,
                         NM_DEVICE_LINK_TYPE,
                         NM_LINK_TYPE_WIFI,
-                        NM_DEVICE_RFKILL_TYPE,
-                        RFKILL_TYPE_WLAN,
                         NM_DEVICE_WIFI_CAPABILITIES,
                         (guint) capabilities,
                         NULL);
@@ -3756,6 +3819,8 @@ nm_device_wifi_class_init(NMDeviceWifiClass *klass)
 
     device_class->state_changed = device_state_changed;
 
+    device_class->rfkill_type = NM_RFKILL_TYPE_WLAN;
+
     obj_properties[PROP_MODE] = g_param_spec_uint(NM_DEVICE_WIFI_MODE,
                                                   "",
                                                   "",
diff --git a/src/core/devices/wifi/nm-iwd-manager.c b/src/core/devices/wifi/nm-iwd-manager.c
index 5563ebf8..27222aae 100644
--- a/src/core/devices/wifi/nm-iwd-manager.c
+++ b/src/core/devices/wifi/nm-iwd-manager.c
@@ -948,6 +948,7 @@ mirror_connection(NMIwdManager         *self,
 
     if (!nm_settings_add_connection(
             priv->settings,
+            NULL,
             connection,
             NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY,
             NM_SETTINGS_CONNECTION_ADD_REASON_NONE,
diff --git a/src/core/devices/wifi/nm-wifi-ap.c b/src/core/devices/wifi/nm-wifi-ap.c
index ac0b748a..825a5aa8 100644
--- a/src/core/devices/wifi/nm-wifi-ap.c
+++ b/src/core/devices/wifi/nm-wifi-ap.c
@@ -1037,9 +1037,10 @@ nm_wifi_ap_lookup_for_device(NMDevice *device, const char *exported_path)
 
     g_return_val_if_fail(NM_IS_DEVICE(device), NULL);
 
-    ap = nm_dbus_manager_lookup_object(nm_dbus_object_get_manager(NM_DBUS_OBJECT(device)),
-                                       exported_path);
-    if (!ap || !NM_IS_WIFI_AP(ap) || ap->wifi_device != device)
+    ap = nm_dbus_manager_lookup_object_with_type(nm_dbus_object_get_manager(NM_DBUS_OBJECT(device)),
+                                                 NM_TYPE_WIFI_AP,
+                                                 exported_path);
+    if (!ap || ap->wifi_device != device)
         return NULL;
 
     return ap;
diff --git a/src/core/devices/wifi/nm-wifi-p2p-peer.c b/src/core/devices/wifi/nm-wifi-p2p-peer.c
index 0a174270..8ccb2a4d 100644
--- a/src/core/devices/wifi/nm-wifi-p2p-peer.c
+++ b/src/core/devices/wifi/nm-wifi-p2p-peer.c
@@ -139,10 +139,11 @@ nm_wifi_p2p_peer_lookup_for_device(NMDevice *device, const char *exported_path)
 
     g_return_val_if_fail(NM_IS_DEVICE(device), NULL);
 
-    peer = (NMWifiP2PPeer *) nm_dbus_manager_lookup_object(
-        nm_dbus_object_get_manager(NM_DBUS_OBJECT(device)),
-        exported_path);
-    if (!peer || !NM_IS_WIFI_P2P_PEER(peer) || peer->wifi_device != device)
+    peer =
+        nm_dbus_manager_lookup_object_with_type(nm_dbus_object_get_manager(NM_DBUS_OBJECT(device)),
+                                                NM_TYPE_WIFI_P2P_PEER,
+                                                exported_path);
+    if (!peer || peer->wifi_device != device)
         return NULL;
 
     return peer;
diff --git a/src/core/devices/wwan/nm-device-modem.c b/src/core/devices/wwan/nm-device-modem.c
index ef802a44..77ba1b24 100644
--- a/src/core/devices/wwan/nm-device-modem.c
+++ b/src/core/devices/wwan/nm-device-modem.c
@@ -181,8 +181,10 @@ modem_new_config(NMModem                  *modem,
         return;
     }
 
-    if (!IS_IPv4)
+    if (!IS_IPv4) {
         priv->iid = iid ? *iid : ((NMUtilsIPv6IfaceId) NM_UTILS_IPV6_IFACE_ID_INIT);
+        nm_device_sysctl_ip_conf_set(device, AF_INET6, "disable_ipv6", "0");
+    }
 
     if (do_auto) {
         if (IS_IPv4)
@@ -757,8 +759,6 @@ nm_device_modem_new(NMModem *modem)
                         "Broadband",
                         NM_DEVICE_DEVICE_TYPE,
                         NM_DEVICE_TYPE_MODEM,
-                        NM_DEVICE_RFKILL_TYPE,
-                        RFKILL_TYPE_WWAN,
                         NM_DEVICE_MODEM_MODEM,
                         modem,
                         NM_DEVICE_MODEM_CAPABILITIES,
@@ -840,6 +840,8 @@ nm_device_modem_class_init(NMDeviceModemClass *klass)
 
     device_class->state_changed = device_state_changed;
 
+    device_class->rfkill_type = NM_RFKILL_TYPE_WWAN;
+
     obj_properties[PROP_MODEM] =
         g_param_spec_object(NM_DEVICE_MODEM_MODEM,
                             "",
diff --git a/src/core/devices/wwan/nm-modem-broadband.c b/src/core/devices/wwan/nm-modem-broadband.c
index b585652e..cbf30f56 100644
--- a/src/core/devices/wwan/nm-modem-broadband.c
+++ b/src/core/devices/wwan/nm-modem-broadband.c
@@ -957,7 +957,7 @@ stage3_ip_config_start(NMModem *modem, int addr_family, NMModemIPMethod ip_metho
     const int                               IS_IPv4 = NM_IS_IPv4(addr_family);
     NMModemBroadband                       *self    = NM_MODEM_BROADBAND(modem);
     nm_auto_unref_l3cd_init NML3ConfigData *l3cd    = NULL;
-    char                                    sbuf[sizeof(_nm_utils_to_string_buffer)];
+    char                                    sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE];
     gs_free_error GError                   *error = NULL;
     const char                             *data_port;
     const char                             *address_string;
diff --git a/src/core/devices/wwan/nm-modem-ofono.c b/src/core/devices/wwan/nm-modem-ofono.c
index cac90d32..0db30046 100644
--- a/src/core/devices/wwan/nm-modem-ofono.c
+++ b/src/core/devices/wwan/nm-modem-ofono.c
@@ -734,7 +734,7 @@ handle_settings(GVariant *v_dict, gpointer user_data)
 {
     NMModemOfono        *self = NM_MODEM_OFONO(user_data);
     NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE(self);
-    char                 sbuf[sizeof(_nm_utils_to_string_buffer)];
+    char                 sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE];
     NMPlatformIP4Address address;
     gboolean             ret = FALSE;
     const char          *interface;