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/bluetooth/nm-bluez-manager.c2
-rw-r--r--src/core/devices/nm-acd-manager.c11
-rw-r--r--src/core/devices/nm-device-bond.c42
-rw-r--r--src/core/devices/nm-device-ethernet.c4
-rw-r--r--src/core/devices/nm-device.c147
-rw-r--r--src/core/devices/nm-device.h35
-rw-r--r--src/core/devices/ovs/nm-device-ovs-interface.c10
-rw-r--r--src/core/devices/wifi/nm-device-iwd.c15
-rw-r--r--src/core/devices/wifi/nm-device-wifi.c8
-rw-r--r--src/core/devices/wifi/nm-iwd-manager.c1
-rw-r--r--src/core/devices/wwan/nm-modem-broadband.c10
11 files changed, 163 insertions, 122 deletions
diff --git a/src/core/devices/bluetooth/nm-bluez-manager.c b/src/core/devices/bluetooth/nm-bluez-manager.c
index dd998d29..459ea409 100644
--- a/src/core/devices/bluetooth/nm-bluez-manager.c
+++ b/src/core/devices/bluetooth/nm-bluez-manager.c
@@ -2879,6 +2879,8 @@ dispose(GObject *object)
     g_clear_object(&priv->dbus_connection);
 
     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);
 }
 
 static void
diff --git a/src/core/devices/nm-acd-manager.c b/src/core/devices/nm-acd-manager.c
index b95f90fd..81a28797 100644
--- a/src/core/devices/nm-acd-manager.c
+++ b/src/core/devices/nm-acd-manager.c
@@ -188,6 +188,7 @@ acd_event(int fd, GIOCondition condition, gpointer data)
         char          to_string_buffer[ACD_EVENT_TO_STRING_BUF_SIZE];
         gs_free char *hwaddr_str         = NULL;
         gboolean      check_probing_done = FALSE;
+        char          buf[ETH_ALEN * 3];
 
         switch (event->event) {
         case N_ACD_EVENT_READY:
@@ -202,8 +203,9 @@ acd_event(int fd, GIOCondition condition, gpointer data)
                           nm_platform_link_get_name(NM_PLATFORM_GET, self->ifindex),
                           acd_error_to_string(r));
                 } else {
-                    _LOGD("announcing address %s",
-                          _nm_utils_inet4_ntop(info->address, address_str));
+                    _LOGD("announcing address %s (hw-addr %s)",
+                          _nm_utils_inet4_ntop(info->address, address_str),
+                          _nm_utils_hwaddr_ntoa(self->hwaddr, ETH_ALEN, TRUE, buf, sizeof(buf)));
                 }
             }
             check_probing_done = TRUE;
@@ -394,6 +396,7 @@ nm_acd_manager_announce_addresses(NMAcdManager *self)
     int            r;
     int            fd;
     gboolean       success = TRUE;
+    char           buf[ETH_ALEN * 3];
 
     r = acd_init(self);
     if (r) {
@@ -428,7 +431,9 @@ nm_acd_manager_announce_addresses(NMAcdManager *self)
                       acd_error_to_string(r));
                 success = FALSE;
             } else
-                _LOGD("announcing address %s", _nm_utils_inet4_ntop(info->address, sbuf));
+                _LOGD("announcing address %s (hw-addr %s)",
+                      _nm_utils_inet4_ntop(info->address, sbuf),
+                      _nm_utils_hwaddr_ntoa(self->hwaddr, ETH_ALEN, TRUE, buf, sizeof(buf)));
         }
     }
 
diff --git a/src/core/devices/nm-device-bond.c b/src/core/devices/nm-device-bond.c
index f68c080b..9c1c484f 100644
--- a/src/core/devices/nm-device-bond.c
+++ b/src/core/devices/nm-device-bond.c
@@ -34,7 +34,7 @@
         NM_SETTING_BOND_OPTION_PACKETS_PER_SLAVE, NM_SETTING_BOND_OPTION_PRIMARY_RESELECT, \
         NM_SETTING_BOND_OPTION_RESEND_IGMP, NM_SETTING_BOND_OPTION_TLB_DYNAMIC_LB,         \
         NM_SETTING_BOND_OPTION_USE_CARRIER, NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY,       \
-        NM_SETTING_BOND_OPTION_NUM_GRAT_ARP
+        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,                        \
@@ -46,7 +46,7 @@
         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_NUM_GRAT_ARP, NM_SETTING_BOND_OPTION_PEER_NOTIF_DELAY
 
 #define OPTIONS_REAPPLY_FULL                                     \
     OPTIONS_REAPPLY_SUBSET, NM_SETTING_BOND_OPTION_ACTIVE_SLAVE, \
@@ -109,6 +109,24 @@ _set_bond_attr(NMDevice *device, const char *attr, const char *value)
     int           ifindex = nm_device_get_ifindex(device);
     gboolean      ret;
 
+    nm_assert(attr && attr[0]);
+    nm_assert(value);
+
+    if (nm_streq(value, NM_BOND_AD_ACTOR_SYSTEM_DEFAULT)
+        && nm_streq(attr, NM_SETTING_BOND_OPTION_AD_ACTOR_SYSTEM)) {
+        gs_free char *cur_val = NULL;
+
+        /* kernel does not allow setting ad_actor_system to "00:00:00:00:00:00". We would thus
+         * log an EINVAL error. Avoid that... at least, if the value is already "00:00:00:00:00:00". */
+        cur_val =
+            nm_platform_sysctl_master_get_option(nm_device_get_platform(device), ifindex, attr);
+        if (nm_streq0(cur_val, NM_BOND_AD_ACTOR_SYSTEM_DEFAULT))
+            return TRUE;
+
+        /* OK, the current value is different, and we will proceed setting "00:00:00:00:00:00".
+         * That will fail, and we will log a warning. There is nothing else to do. */
+    }
+
     ret =
         nm_platform_sysctl_master_set_option(nm_device_get_platform(device), ifindex, attr, value);
     if (!ret)
@@ -426,9 +444,10 @@ release_slave(NMDevice *device, NMDevice *slave, gboolean configure)
         _LOGD(LOGD_BOND, "bond slave %s is already released", nm_device_get_ip_iface(slave));
 
     if (configure) {
-        /* When the last slave is released the bond MAC will be set to a random
-         * value by kernel; remember the current one and restore it afterwards.
-         */
+        NMConnection *  applied;
+        NMSettingWired *s_wired;
+        const char *    cloned_mac;
+
         address = g_strdup(nm_device_get_hw_address(device));
 
         if (ifindex_slave > 0) {
@@ -443,9 +462,16 @@ release_slave(NMDevice *device, NMDevice *slave, gboolean configure)
             }
         }
 
-        nm_platform_process_events(nm_device_get_platform(device));
-        if (nm_device_update_hw_address(device))
-            nm_device_hw_addr_set(device, address, "restore", FALSE);
+        if ((applied = nm_device_get_applied_connection(device))
+            && ((s_wired = nm_connection_get_setting_wired(applied)))
+            && ((cloned_mac = nm_setting_wired_get_cloned_mac_address(s_wired)))) {
+            /* When the last slave is released the bond MAC will be set to a random
+             * value by kernel; if we have set a cloned-mac-address, we need to
+             * restore it to the previous value. */
+            nm_platform_process_events(nm_device_get_platform(device));
+            if (nm_device_update_hw_address(device))
+                nm_device_hw_addr_set(device, address, "restore", FALSE);
+        }
 
         /* Kernel bonding code "closes" the slave when releasing it, (which clears
          * IFF_UP), so we must bring it back up here to ensure carrier changes and
diff --git a/src/core/devices/nm-device-ethernet.c b/src/core/devices/nm-device-ethernet.c
index 44428869..59f6c613 100644
--- a/src/core/devices/nm-device-ethernet.c
+++ b/src/core/devices/nm-device-ethernet.c
@@ -730,7 +730,9 @@ supplicant_iface_state_cb(NMSupplicantInterface *iface,
 
     if (new_state == NM_SUPPLICANT_INTERFACE_STATE_DOWN) {
         supplicant_interface_release(self);
-        wired_auth_cond_fail(self, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
+        nm_device_state_changed(NM_DEVICE(self),
+                                NM_DEVICE_STATE_FAILED,
+                                NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
         return;
     }
 
diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c
index 040dd0b4..3874e586 100644
--- a/src/core/devices/nm-device.c
+++ b/src/core/devices/nm-device.c
@@ -125,7 +125,6 @@ typedef struct {
 typedef struct {
     NMDevice *device;
     guint     idle_add_id;
-    int       ifindex;
 } DeleteOnDeactivateData;
 
 typedef struct {
@@ -4703,10 +4702,6 @@ nm_device_master_enslave_slave(NMDevice *self, NMDevice *slave, NMConnection *co
      */
     nm_device_update_hw_address(self);
 
-    /* Send ARP announcements if did not yet and have addresses. */
-    if (priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE && !priv->acd.announcing)
-        nm_device_arp_announce(self);
-
     /* Restart IP configuration if we're waiting for slaves.  Do this
      * after updating the hardware address as IP config may need the
      * new address.
@@ -5053,6 +5048,10 @@ nm_device_set_carrier(NMDevice *self, gboolean carrier)
             nm_device_remove_pending_action(self, NM_PENDING_ACTION_CARRIER_WAIT, FALSE);
             _carrier_wait_check_queued_act_request(self);
         }
+
+        /* Send ARP announcements if did not yet and have carrier. */
+        if (priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE && !priv->acd.announcing)
+            nm_device_arp_announce(self);
     } else {
         if (priv->carrier_wait_id)
             nm_device_add_pending_action(self, NM_PENDING_ACTION_CARRIER_WAIT, FALSE);
@@ -5415,11 +5414,11 @@ device_link_changed(NMDevice *self)
                 /* Ensure the assume check is queued before any queued state changes
                  * from the transition to UNAVAILABLE.
                  */
-                nm_device_queue_recheck_assume(self);
                 reason = NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED;
             }
         }
 
+        nm_device_queue_recheck_assume(self);
         nm_device_set_unmanaged_by_flags(self, NM_UNMANAGED_PLATFORM_INIT, FALSE, reason);
     }
 
@@ -8362,26 +8361,23 @@ _routing_rules_sync(NMDevice *self, NMTernary set_mode)
 static gboolean
 tc_commit(NMDevice *self)
 {
-    NMConnection *    connection          = NULL;
     gs_unref_ptrarray GPtrArray *qdiscs   = NULL;
     gs_unref_ptrarray GPtrArray *tfilters = NULL;
-    NMSettingTCConfig *          s_tc     = NULL;
+    NMSettingTCConfig *          s_tc;
     NMPlatform *                 platform;
     int                          ip_ifindex;
 
-    platform   = nm_device_get_platform(self);
-    connection = nm_device_get_applied_connection(self);
-    if (connection)
-        s_tc = nm_connection_get_setting_tc_config(connection);
+    s_tc = nm_device_get_applied_setting(self, NM_TYPE_SETTING_TC_CONFIG);
+    if (!s_tc)
+        return TRUE;
 
     ip_ifindex = nm_device_get_ip_ifindex(self);
     if (!ip_ifindex)
-        return s_tc == NULL;
+        return FALSE;
 
-    if (s_tc) {
-        qdiscs   = nm_utils_qdiscs_from_tc_setting(platform, s_tc, ip_ifindex);
-        tfilters = nm_utils_tfilters_from_tc_setting(platform, s_tc, ip_ifindex);
-    }
+    platform = nm_device_get_platform(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;
@@ -9980,7 +9976,9 @@ nm_device_request_ip6_prefixes(NMDevice *self, int needed_prefixes)
         _LOGD(LOGD_IP6, "ipv6-pd: asking DHCPv6 for %d prefixes", needed_prefixes);
         nm_device_dhcp6_renew(self, FALSE);
     } else {
-        _LOGI(LOGD_IP6, "ipv6-pd: device doesn't use DHCPv6, can't request prefixes");
+        priv->dhcp6.mode = NM_NDISC_DHCP_LEVEL_OTHERCONF;
+        _LOGD(LOGD_DEVICE | LOGD_DHCP6, "ipv6-pd: starting DHCPv6 to request a prefix");
+        dhcp6_start(self, FALSE);
     }
 }
 
@@ -11679,8 +11677,6 @@ start_sharing(NMDevice *self, NMIP4Config *config, GError **error)
 
     nm_utils_share_rules_add_all_rules(share_rules, ip_iface, ip4_addr->address, ip4_addr->plen);
 
-    nm_utils_share_rules_apply(share_rules, TRUE);
-
     nm_act_request_set_shared(req, share_rules);
 
     conn  = nm_act_request_get_applied_connection(req);
@@ -11792,7 +11788,6 @@ activate_stage5_ip_config_result_x(NMDevice *self, int addr_family)
     const char *     method;
     int              ip_ifindex;
     int              errsv;
-    gboolean         do_announce = FALSE;
 
     req = nm_device_get_act_request(self);
     g_assert(req);
@@ -11918,31 +11913,13 @@ activate_stage5_ip_config_result_x(NMDevice *self, int addr_family)
         }
     }
 
-    if (IS_IPv4) {
-        /* Send ARP announcements */
-
-        if (nm_device_is_master(self)) {
-            CList *    iter;
-            SlaveInfo *info;
-
-            /* Skip announcement if there are no device enslaved, for two reasons:
-             * 1) the master has a temporary MAC address until the first slave comes
-             * 2) announcements are going to be dropped anyway without slaves
-             */
-            do_announce = FALSE;
-
-            c_list_for_each (iter, &priv->slaves) {
-                info = c_list_entry(iter, SlaveInfo, lst_slave);
-                if (info->slave_is_enslaved) {
-                    do_announce = TRUE;
-                    break;
-                }
-            }
-        } else
-            do_announce = TRUE;
-
-        if (do_announce)
-            nm_device_arp_announce(self);
+    if (IS_IPv4 && priv->carrier) {
+        /* We send ARP announcements only when the link gets carrier,
+         * otherwise the announcements would be lost. Furthermore, for
+         * controllers having carrier implies that there is at least one
+         * port and therefore the MAC address is the correct one.
+         */
+        nm_device_arp_announce(self);
     }
 
     if (IS_IPv4) {
@@ -12141,28 +12118,19 @@ nm_device_is_nm_owned(NMDevice *self)
 static gboolean
 delete_on_deactivate_link_delete(gpointer user_data)
 {
-    DeleteOnDeactivateData *data = user_data;
-    NMDevice *              self = data->device;
+    DeleteOnDeactivateData *data        = user_data;
+    nm_auto_unref_object NMDevice *self = data->device;
+    NMDevicePrivate *              priv = NM_DEVICE_GET_PRIVATE(self);
+    gs_free_error GError *error         = NULL;
 
     _LOGD(LOGD_DEVICE,
-          "delete_on_deactivate: cleanup and delete virtual link #%d (id=%u)",
-          data->ifindex,
+          "delete_on_deactivate: cleanup and delete virtual link (id=%u)",
           data->idle_add_id);
 
-    if (data->device) {
-        NMDevicePrivate *priv       = NM_DEVICE_GET_PRIVATE(data->device);
-        gs_free_error GError *error = NULL;
-
-        g_object_remove_weak_pointer(G_OBJECT(data->device), (void **) &data->device);
-        priv->delete_on_deactivate_data = NULL;
+    priv->delete_on_deactivate_data = NULL;
 
-        if (!nm_device_unrealize(data->device, TRUE, &error))
-            _LOGD(LOGD_DEVICE,
-                  "delete_on_deactivate: unrealizing %d failed (%s)",
-                  data->ifindex,
-                  error->message);
-    } else if (data->ifindex > 0)
-        nm_platform_link_delete(nm_device_get_platform(self), data->ifindex);
+    if (!nm_device_unrealize(self, TRUE, &error))
+        _LOGD(LOGD_DEVICE, "delete_on_deactivate: unrealizing failed (%s)", error->message);
 
     nm_device_emit_recheck_auto_activate(self);
 
@@ -12181,17 +12149,16 @@ delete_on_deactivate_unschedule(NMDevice *self)
         priv->delete_on_deactivate_data = NULL;
 
         g_source_remove(data->idle_add_id);
-        g_object_remove_weak_pointer(G_OBJECT(self), (void **) &data->device);
         _LOGD(LOGD_DEVICE,
-              "delete_on_deactivate: cancel cleanup and delete virtual link #%d (id=%u)",
-              data->ifindex,
+              "delete_on_deactivate: cancel cleanup and delete virtual link (id=%u)",
               data->idle_add_id);
+        g_object_unref(data->device);
         g_free(data);
     }
 }
 
 static void
-delete_on_deactivate_check_and_schedule(NMDevice *self, int ifindex)
+delete_on_deactivate_check_and_schedule(NMDevice *self)
 {
     NMDevicePrivate *       priv = NM_DEVICE_GET_PRIVATE(self);
     DeleteOnDeactivateData *data;
@@ -12208,16 +12175,13 @@ delete_on_deactivate_check_and_schedule(NMDevice *self, int ifindex)
         return;
     delete_on_deactivate_unschedule(self); /* always cancel and reschedule */
 
-    data = g_new(DeleteOnDeactivateData, 1);
-    g_object_add_weak_pointer(G_OBJECT(self), (void **) &data->device);
-    data->device                    = self;
-    data->ifindex                   = ifindex;
+    data                            = g_new(DeleteOnDeactivateData, 1);
+    data->device                    = g_object_ref(self);
     data->idle_add_id               = g_idle_add(delete_on_deactivate_link_delete, data);
     priv->delete_on_deactivate_data = data;
 
     _LOGD(LOGD_DEVICE,
-          "delete_on_deactivate: schedule cleanup and delete virtual link #%d (id=%u)",
-          ifindex,
+          "delete_on_deactivate: schedule cleanup and delete virtual link (id=%u)",
           data->idle_add_id);
 }
 
@@ -13478,12 +13442,13 @@ nm_device_set_ip_config(NMDevice *  self,
                         gboolean    commit,
                         GPtrArray * ip4_dev_route_blacklist)
 {
-    NMDevicePrivate *     priv    = NM_DEVICE_GET_PRIVATE(self);
-    const int             IS_IPv4 = NM_IS_IPv4(addr_family);
-    NMIPConfig *          old_config;
-    gboolean              has_changes = FALSE;
-    gboolean              success     = TRUE;
-    NMSettingsConnection *settings_connection;
+    NMDevicePrivate *      priv    = NM_DEVICE_GET_PRIVATE(self);
+    const int              IS_IPv4 = NM_IS_IPv4(addr_family);
+    NMIPConfig *           old_config;
+    gboolean               has_changes = FALSE;
+    gboolean               success     = TRUE;
+    NMSettingsConnection * settings_connection;
+    NMIPRouteTableSyncMode route_table_sync_mode;
 
     nm_assert_addr_family(addr_family);
     nm_assert(!new_config || nm_ip_config_get_addr_family(new_config) == addr_family);
@@ -13495,11 +13460,18 @@ nm_device_set_ip_config(NMDevice *  self,
                   })));
     nm_assert(IS_IPv4 || !ip4_dev_route_blacklist);
 
+    if (commit && new_config)
+        route_table_sync_mode = _get_route_table_sync_mode_stateful(self, addr_family);
+    else
+        route_table_sync_mode = NM_IP_ROUTE_TABLE_SYNC_MODE_NONE;
+
     _LOGD(LOGD_IPX(IS_IPv4),
-          "ip%c-config: update (commit=%d, new-config=%p)",
+          "ip%c-config: update (commit=%d, new-config=" NM_HASH_OBFUSCATE_PTR_FMT
+          ", route-table-sync-mode=%d)",
           nm_utils_addr_family_to_char(addr_family),
           commit,
-          new_config);
+          NM_HASH_OBFUSCATE_PTR(new_config),
+          (int) route_table_sync_mode);
 
     /* Always commit to nm-platform to update lifetimes */
     if (commit && new_config) {
@@ -13508,7 +13480,7 @@ nm_device_set_ip_config(NMDevice *  self,
         if (IS_IPv4) {
             success = nm_ip4_config_commit(NM_IP4_CONFIG(new_config),
                                            nm_device_get_platform(self),
-                                           _get_route_table_sync_mode_stateful(self, AF_INET));
+                                           route_table_sync_mode);
             nm_platform_ip4_dev_route_blacklist_set(nm_device_get_platform(self),
                                                     nm_ip_config_get_ifindex(new_config),
                                                     ip4_dev_route_blacklist);
@@ -13517,7 +13489,7 @@ nm_device_set_ip_config(NMDevice *  self,
 
             success = nm_ip6_config_commit(NM_IP6_CONFIG(new_config),
                                            nm_device_get_platform(self),
-                                           _get_route_table_sync_mode_stateful(self, AF_INET6),
+                                           route_table_sync_mode,
                                            &temporary_not_available);
 
             if (!_rt6_temporary_not_available_set(self, temporary_not_available))
@@ -15846,7 +15818,7 @@ _cleanup_generic_post(NMDevice *self, CleanupType cleanup_type)
         /* Check if the device was deactivated, and if so, delete_link.
          * Don't call delete_link synchronously because we are currently
          * handling a state change -- which is not reentrant. */
-        delete_on_deactivate_check_and_schedule(self, nm_device_get_ip_ifindex(self));
+        delete_on_deactivate_check_and_schedule(self);
     }
 
     /* ip_iface should be cleared after flushing all routes and addresses, since
@@ -15906,9 +15878,12 @@ 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);
-            nm_platform_tfilter_sync(platform, ifindex, NULL);
-            nm_platform_qdisc_sync(platform, ifindex, NULL);
             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);
+            }
         }
     }
 
diff --git a/src/core/devices/nm-device.h b/src/core/devices/nm-device.h
index 72777d0a..68343ef5 100644
--- a/src/core/devices/nm-device.h
+++ b/src/core/devices/nm-device.h
@@ -627,27 +627,28 @@ void nm_device_copy_ip6_dns_config(NMDevice *self, NMDevice *from_device);
  *   setting the NM_UNMANAGED_IS_SLAVE to %TRUE makes no sense, this flag has only
  *   meaning to set a slave device as managed if the parent is managed too.
  */
-typedef enum { /*< skip >*/
-               NM_UNMANAGED_NONE = 0,
+typedef enum {
+    NM_UNMANAGED_NONE = 0,
 
-               /* these flags are authoritative. If one of them is set,
+    /* these flags are authoritative. If one of them is set,
      * the device cannot be managed. */
-               NM_UNMANAGED_SLEEPING      = (1LL << 0),
-               NM_UNMANAGED_QUITTING      = (1LL << 1),
-               NM_UNMANAGED_PARENT        = (1LL << 2),
-               NM_UNMANAGED_BY_TYPE       = (1LL << 3),
-               NM_UNMANAGED_PLATFORM_INIT = (1LL << 4),
-               NM_UNMANAGED_USER_EXPLICIT = (1LL << 5),
-               NM_UNMANAGED_USER_SETTINGS = (1LL << 6),
-
-               /* These flags can be non-effective and be overwritten
+    NM_UNMANAGED_SLEEPING      = (1LL << 0),
+    NM_UNMANAGED_QUITTING      = (1LL << 1),
+    NM_UNMANAGED_PARENT        = (1LL << 2),
+    NM_UNMANAGED_BY_TYPE       = (1LL << 3),
+    NM_UNMANAGED_PLATFORM_INIT = (1LL << 4),
+    NM_UNMANAGED_USER_EXPLICIT = (1LL << 5),
+    NM_UNMANAGED_USER_SETTINGS = (1LL << 6),
+
+    /* These flags can be non-effective and be overwritten
      * by other flags. */
-               NM_UNMANAGED_BY_DEFAULT    = (1LL << 8),
-               NM_UNMANAGED_USER_CONF     = (1LL << 9),
-               NM_UNMANAGED_USER_UDEV     = (1LL << 10),
-               NM_UNMANAGED_EXTERNAL_DOWN = (1LL << 11),
-               NM_UNMANAGED_IS_SLAVE      = (1LL << 12),
+    NM_UNMANAGED_BY_DEFAULT    = (1LL << 7),
+    NM_UNMANAGED_USER_CONF     = (1LL << 8),
+    NM_UNMANAGED_USER_UDEV     = (1LL << 9),
+    NM_UNMANAGED_EXTERNAL_DOWN = (1LL << 10),
+    NM_UNMANAGED_IS_SLAVE      = (1LL << 11),
 
+    NM_UNMANAGED_ALL = ((1LL << 12) - 1),
 } NMUnmanagedFlags;
 
 typedef enum {
diff --git a/src/core/devices/ovs/nm-device-ovs-interface.c b/src/core/devices/ovs/nm-device-ovs-interface.c
index 5d07c211..0a537f7a 100644
--- a/src/core/devices/ovs/nm-device-ovs-interface.c
+++ b/src/core/devices/ovs/nm-device-ovs-interface.c
@@ -78,6 +78,15 @@ is_available(NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
 }
 
 static gboolean
+can_auto_connect(NMDevice *device, NMSettingsConnection *sett_conn, char **specific_object)
+{
+    NMDeviceOvsInterface *       self = NM_DEVICE_OVS_INTERFACE(device);
+    NMDeviceOvsInterfacePrivate *priv = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE(self);
+
+    return nm_ovsdb_is_ready(priv->ovsdb);
+}
+
+static gboolean
 check_connection_compatible(NMDevice *device, NMConnection *connection, GError **error)
 {
     NMSettingOvsInterface *s_ovs_iface;
@@ -427,6 +436,7 @@ nm_device_ovs_interface_class_init(NMDeviceOvsInterfaceClass *klass)
     device_class->connection_type_check_compatible = NM_SETTING_OVS_INTERFACE_SETTING_NAME;
     device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES(NM_LINK_TYPE_OPENVSWITCH);
 
+    device_class->can_auto_connect                    = can_auto_connect;
     device_class->can_update_from_platform_link       = can_update_from_platform_link;
     device_class->deactivate                          = deactivate;
     device_class->deactivate_async                    = deactivate_async;
diff --git a/src/core/devices/wifi/nm-device-iwd.c b/src/core/devices/wifi/nm-device-iwd.c
index 95ade44b..01f2a303 100644
--- a/src/core/devices/wifi/nm-device-iwd.c
+++ b/src/core/devices/wifi/nm-device-iwd.c
@@ -588,10 +588,16 @@ deactivate(NMDevice *device)
             return;
     }
 
-    cleanup_association_attempt(self, TRUE);
+    cleanup_association_attempt(self, FALSE);
     priv->act_mode_switch = FALSE;
 
-    if (!priv->dbus_station_proxy)
+    /* Don't trigger any actions on the IWD side until the device is managed */
+    if (priv->iwd_autoconnect && nm_device_get_state(device) < NM_DEVICE_STATE_DISCONNECTED)
+        return;
+
+    if (priv->dbus_station_proxy)
+        send_disconnect(self);
+    else
         reset_mode(self, NULL, NULL, NULL);
 }
 
@@ -647,6 +653,11 @@ deactivate_async(NMDevice *                 device,
     cleanup_association_attempt(self, FALSE);
     priv->act_mode_switch = FALSE;
 
+    if (priv->iwd_autoconnect && nm_device_get_state(device) < NM_DEVICE_STATE_DISCONNECTED) {
+        nm_utils_invoke_on_idle(cancellable, disconnect_cb_on_idle, user_data);
+        return;
+    }
+
     if (priv->dbus_station_proxy) {
         g_dbus_proxy_call(priv->dbus_station_proxy,
                           "Disconnect",
diff --git a/src/core/devices/wifi/nm-device-wifi.c b/src/core/devices/wifi/nm-device-wifi.c
index 042d4887..c9655eea 100644
--- a/src/core/devices/wifi/nm-device-wifi.c
+++ b/src/core/devices/wifi/nm-device-wifi.c
@@ -2565,6 +2565,7 @@ supplicant_iface_notify_current_bss(NMSupplicantInterface *iface,
     NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE(self);
     NMRefString *        current_bss;
     NMWifiAP *           new_ap = NULL;
+    NMActRequest *       req;
 
     current_bss = nm_supplicant_interface_get_current_bss(iface);
     if (current_bss)
@@ -2612,6 +2613,13 @@ supplicant_iface_notify_current_bss(NMSupplicantInterface *iface,
         }
 
         set_current_ap(self, new_ap, TRUE);
+
+        req = nm_device_get_act_request(NM_DEVICE(self));
+        if (req) {
+            nm_active_connection_set_specific_object(
+                NM_ACTIVE_CONNECTION(req),
+                new_ap ? nm_dbus_object_get_path(NM_DBUS_OBJECT(new_ap)) : NULL);
+        }
     }
 }
 
diff --git a/src/core/devices/wifi/nm-iwd-manager.c b/src/core/devices/wifi/nm-iwd-manager.c
index b4b019d3..68a74438 100644
--- a/src/core/devices/wifi/nm-iwd-manager.c
+++ b/src/core/devices/wifi/nm-iwd-manager.c
@@ -1119,6 +1119,7 @@ object_compare_interfaces(gconstpointer a, gconstpointer b)
         NM_IWD_KNOWN_NETWORK_INTERFACE,
         NM_IWD_NETWORK_INTERFACE,
         NM_IWD_DEVICE_INTERFACE,
+        NULL,
     };
     int   rank_a = G_N_ELEMENTS(interface_order);
     int   rank_b = G_N_ELEMENTS(interface_order);
diff --git a/src/core/devices/wwan/nm-modem-broadband.c b/src/core/devices/wwan/nm-modem-broadband.c
index ca028804..1297d964 100644
--- a/src/core/devices/wwan/nm-modem-broadband.c
+++ b/src/core/devices/wwan/nm-modem-broadband.c
@@ -369,10 +369,10 @@ static void
 connect_ready(MMModemSimple *simple_iface, GAsyncResult *res, NMModemBroadband *self)
 {
     ConnectContext *ctx;
-    GError *        error      = NULL;
-    NMModemIPMethod ip4_method = NM_MODEM_IP_METHOD_UNKNOWN;
-    NMModemIPMethod ip6_method = NM_MODEM_IP_METHOD_UNKNOWN;
-    MMBearer *      bearer;
+    GError *        error            = NULL;
+    NMModemIPMethod ip4_method       = NM_MODEM_IP_METHOD_UNKNOWN;
+    NMModemIPMethod ip6_method       = NM_MODEM_IP_METHOD_UNKNOWN;
+    gs_unref_object MMBearer *bearer = NULL;
 
     bearer = mm_modem_simple_connect_finish(simple_iface, res, &error);
 
@@ -386,7 +386,7 @@ connect_ready(MMModemSimple *simple_iface, GAsyncResult *res, NMModemBroadband *
     if (!ctx)
         return;
 
-    self->_priv.bearer = bearer;
+    self->_priv.bearer = g_steal_pointer(&bearer);
 
     if (!self->_priv.bearer) {
         if (g_error_matches(error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN)