]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
AP MLD: De-initialize/disable link BSS properly
authorAditya Kumar Singh <quic_adisi@quicinc.com>
Wed, 6 Mar 2024 06:40:16 +0000 (12:10 +0530)
committerJouni Malinen <j@w1.fi>
Wed, 27 Mar 2024 16:12:39 +0000 (18:12 +0200)
When the first link BSS of an interface was de-initialized/disabled, the
whole MLD was brought down. All other links were stopped beaconing and
links were removed. And if the non-first link BSS was
de-initialized/disabled, nothing happened. Even beaconing was not
stopped which is wrong.

Fix this by properly bringing down the intended link alone from the
interface.

Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
hostapd/main.c
src/ap/hostapd.c
src/drivers/driver.h
src/drivers/driver_nl80211.c

index 1191857269f15d9d88374b3f31eea1afb88c8571..a43d3a5be1d5a539a0276472ea1b9c6ec3b63284 100644 (file)
@@ -195,6 +195,7 @@ static int hostapd_driver_init(struct hostapd_iface *iface)
                }
 
                hapd->drv_priv = h_hapd->drv_priv;
+               hapd->interface_added = h_hapd->interface_added;
 
                /*
                 * All interfaces participating in the AP MLD would have
index f94b89e8fc9a1d4305372cb4eeee54daf5846aef..323c02754cf7d06b2f3113d552e5c48b729ea68a 100644 (file)
@@ -397,27 +397,6 @@ static int hostapd_broadcast_wep_set(struct hostapd_data *hapd)
 #endif /* CONFIG_WEP */
 
 
-static void hostapd_clear_drv_priv(struct hostapd_data *hapd)
-{
-#ifdef CONFIG_IEEE80211BE
-       unsigned int i;
-
-       for (i = 0; i < hapd->iface->interfaces->count; i++) {
-               struct hostapd_iface *iface = hapd->iface->interfaces->iface[i];
-
-               if (hapd->iface == iface || !iface)
-                       continue;
-
-               if (iface->bss && iface->bss[0] &&
-                   hostapd_mld_get_first_bss(iface->bss[0]) == hapd)
-                       iface->bss[0]->drv_priv = NULL;
-       }
-#endif /* CONFIG_IEEE80211BE */
-
-       hapd->drv_priv = NULL;
-}
-
-
 #ifdef CONFIG_IEEE80211BE
 #ifdef CONFIG_TESTING_OPTIONS
 
@@ -554,10 +533,20 @@ void hostapd_free_hapd_data(struct hostapd_data *hapd)
                         * driver wrapper may have removed its internal instance
                         * and hapd->drv_priv is not valid anymore.
                         */
-                       hostapd_clear_drv_priv(hapd);
+                       hapd->drv_priv = NULL;
                }
        }
 
+#ifdef CONFIG_IEEE80211BE
+       /* If the interface was not added as well as it is not the first BSS,
+        * at least the link should be removed here since deinit will take care
+        * of only the first BSS. */
+       if (hapd->conf->mld_ap && !hapd->interface_added &&
+           hapd->iface->bss[0] != hapd)
+               hostapd_if_link_remove(hapd, WPA_IF_AP_BSS, hapd->conf->iface,
+                                      hapd->mld_link_id);
+#endif /* CONFIG_IEEE80211BE */
+
        wpabuf_free(hapd->time_adv);
        hapd->time_adv = NULL;
 
@@ -3304,6 +3293,37 @@ hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy,
 }
 
 
+static void hostapd_cleanup_driver(const struct wpa_driver_ops *driver,
+                                  void *drv_priv, struct hostapd_iface *iface)
+{
+#ifdef CONFIG_IEEE80211BE
+       if (!driver || !driver->hapd_deinit || !drv_priv)
+               return;
+
+       /* In case of non-ML operation, de-init. But if ML operation exist,
+        * even if that's the last BSS in the interface, the driver (drv) could
+        * be in use for a different AP MLD. Hence, need to check if drv is
+        * still being used by some other BSS before de-initiallizing. */
+       if (!iface->bss[0]->conf->mld_ap) {
+               driver->hapd_deinit(drv_priv);
+       } else if (hostapd_mld_is_first_bss(iface->bss[0]) &&
+                  driver->is_drv_shared &&
+                  !driver->is_drv_shared(drv_priv, iface->bss[0])) {
+               driver->hapd_deinit(drv_priv);
+       } else if (hostapd_if_link_remove(iface->bss[0],
+                                         WPA_IF_AP_BSS,
+                                         iface->bss[0]->conf->iface,
+                                         iface->bss[0]->mld_link_id)) {
+               wpa_printf(MSG_WARNING, "Failed to remove BSS interface %s",
+                          iface->bss[0]->conf->iface);
+       }
+#else /* CONFIG_IEEE80211BE */
+       driver->hapd_deinit(drv_priv);
+#endif /* CONFIG_IEEE80211BE */
+       iface->bss[0]->drv_priv = NULL;
+}
+
+
 void hostapd_interface_deinit_free(struct hostapd_iface *iface)
 {
        const struct wpa_driver_ops *driver;
@@ -3320,11 +3340,7 @@ void hostapd_interface_deinit_free(struct hostapd_iface *iface)
        hostapd_interface_deinit(iface);
        wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit",
                   __func__, driver, drv_priv);
-       if (driver && driver->hapd_deinit && drv_priv) {
-               if (hostapd_mld_is_first_bss(iface->bss[0]))
-                       driver->hapd_deinit(drv_priv);
-               hostapd_clear_drv_priv(iface->bss[0]);
-       }
+       hostapd_cleanup_driver(driver, drv_priv, iface);
        hostapd_interface_free(iface);
 }
 
@@ -3337,15 +3353,16 @@ static void hostapd_deinit_driver(const struct wpa_driver_ops *driver,
 
        wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit",
                   __func__, driver, drv_priv);
+
+       hostapd_cleanup_driver(driver, drv_priv, hapd_iface);
+
        if (driver && driver->hapd_deinit && drv_priv) {
-               if (hostapd_mld_is_first_bss(hapd_iface->bss[0]))
-                       driver->hapd_deinit(drv_priv);
                for (j = 0; j < hapd_iface->num_bss; j++) {
                        wpa_printf(MSG_DEBUG, "%s:bss[%d]->drv_priv=%p",
                                   __func__, (int) j,
                                   hapd_iface->bss[j]->drv_priv);
                        if (hapd_iface->bss[j]->drv_priv == drv_priv) {
-                               hostapd_clear_drv_priv(hapd_iface->bss[j]);
+                               hapd_iface->bss[j]->drv_priv = NULL;
                                hapd_iface->extended_capa = NULL;
                                hapd_iface->extended_capa_mask = NULL;
                                hapd_iface->extended_capa_len = 0;
index 61601eb9da48897575b2d5531f8227468e2b18bd..833dbc89513c1dad5f4e86c1591430e2641484c7 100644 (file)
@@ -5165,6 +5165,19 @@ struct wpa_driver_ops {
        int (*link_remove)(void *priv, enum wpa_driver_if_type type,
                           const char *ifname, u8 link_id);
 
+       /**
+        * is_drv_shared - Check whether the driver interface is shared
+        * @priv: Private driver interface data from init()
+        * @bss_ctx: BSS context for %WPA_IF_AP_BSS interfaces
+        *
+        * Checks whether the driver interface is being used by other partner
+        * BSS(s) or not. This is used to decide whether the driver interface
+        * needs to be deinitilized when one interface is getting deinitialized.
+        *
+        * Returns: true if it is being used or else false.
+        */
+       bool (*is_drv_shared)(void *priv, void *bss_ctx);
+
 #ifdef CONFIG_TESTING_OPTIONS
        int (*register_frame)(void *priv, u16 type,
                              const u8 *match, size_t match_len,
index 7a68b47efd4fc666b72cf0ebc42242ef1a42c872..7590b30abe13d579d56e0ffe30a3a86959db60f0 100644 (file)
@@ -10715,6 +10715,7 @@ static int driver_nl80211_if_remove(void *priv, enum wpa_driver_if_type type,
 
 
 #ifdef CONFIG_IEEE80211BE
+
 static int driver_nl80211_link_remove(void *priv, enum wpa_driver_if_type type,
                                      const char *ifname, u8 link_id)
 {
@@ -10744,6 +10745,39 @@ static int driver_nl80211_link_remove(void *priv, enum wpa_driver_if_type type,
 
        return 0;
 }
+
+
+static bool nl80211_is_drv_shared(void *priv, void *bss_ctx)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       unsigned int num_bss = 0;
+
+       /* If any other BSS exist, someone else is using this since at this
+        * time, we would have removed all BSSs created by this driver and only
+        * this BSS should be remaining if the driver is not shared by anyone.
+        */
+       for (bss = drv->first_bss; bss; bss = bss->next) {
+               num_bss++;
+               if (num_bss > 1)
+                       return true;
+       }
+
+       /* This is the only BSS present */
+       bss = priv;
+
+       /* If only one/no link is there no one is sharing */
+       if (bss->valid_links <= 1)
+               return false;
+
+       /* More than one link means someone is still using. To check if
+        * only 1 bit is set, power of 2 condition can be checked. */
+       if (!(bss->valid_links & (bss->valid_links - 1)))
+               return false;
+
+       return true;
+}
+
 #endif /* CONFIG_IEEE80211BE */
 
 
@@ -14078,6 +14112,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .link_add = nl80211_link_add,
 #ifdef CONFIG_IEEE80211BE
        .link_remove = driver_nl80211_link_remove,
+       .is_drv_shared = nl80211_is_drv_shared,
 #endif /* CONFIG_IEEE80211BE */
 #ifdef CONFIG_TESTING_OPTIONS
        .register_frame = testing_nl80211_register_frame,