[PATCH 14/16] hostapd: MLO: de-initialize/disable link BSS properly
Aditya Kumar Singh
quic_adisi at quicinc.com
Wed Mar 6 09:39:45 PST 2024
Currently, if first link BSS of an interface is getting de-initialized/
disabled, then whole MLD is brought down. All other links are made
to stop beaconing and links are removed. And if, non-first link BSS
is de-initialized/disabled, nothing happens. Even beaconing is not
stopped which is wrong.
Fix the above by properly bringing down the intended link alone from the
interface.
Signed-off-by: Aditya Kumar Singh <quic_adisi at quicinc.com>
---
hostapd/main.c | 1 +
src/ap/hostapd.c | 100 +++++++++++++++++++++--------------
src/drivers/driver.h | 13 +++++
src/drivers/driver_nl80211.c | 33 ++++++++++++
4 files changed, 107 insertions(+), 40 deletions(-)
diff --git a/hostapd/main.c b/hostapd/main.c
index 97e9704c7046..1ac9b4be30fc 100644
--- a/hostapd/main.c
+++ b/hostapd/main.c
@@ -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
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 0171649b21cc..9dd9e6c3fd8d 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -401,28 +401,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
@@ -561,10 +539,22 @@ 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 interface was not added as well as it is not the first bss, then
+ * at least link should be removed here since deinit will take care only
+ * for 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;
@@ -3305,6 +3295,41 @@ hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy,
return iface;
}
+static void hostapd_cleanup_driver(const struct wpa_driver_ops *driver,
+ void *drv_priv,
+ struct hostapd_iface *iface)
+{
+ bool ap_mld = false;
+
+ if (!driver || !driver->hapd_deinit || !drv_priv)
+ return;
+
+#ifdef CONFIG_IEEE80211BE
+ ap_mld = !!iface->bss[0]->conf->mld_ap;
+#endif /* CONFIG_IEEE80211BE */
+
+ /* In case of non-ML operation, de-init. But, if ML operation exist, then
+ * even if thats the last BSS in the interface, the driver (drv) could
+ * be in use for a different MLD. Hence, need to check if drv is still
+ * being used by some other bss before de-initiallizing
+ */
+ if (!ap_mld)
+ driver->hapd_deinit(drv_priv);
+#ifdef CONFIG_IEEE80211BE
+ else if (hostapd_mld_is_first_bss(iface->bss[0]) &&
+ driver->get_drv_shared_status &&
+ !driver->get_drv_shared_status(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);
+ }
+#endif /* CONFIG_IEEE80211BE */
+ iface->bss[0]->drv_priv = NULL;
+}
void hostapd_interface_deinit_free(struct hostapd_iface *iface)
{
@@ -3322,13 +3347,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) {
-#ifdef CONFIG_IEEE80211BE
- if (hostapd_mld_is_first_bss(iface->bss[0]))
-#endif /* CONFIG_IEEE80211BE */
- driver->hapd_deinit(drv_priv);
- hostapd_clear_drv_priv(iface->bss[0]);
- }
+ hostapd_cleanup_driver(driver, drv_priv, iface);
hostapd_interface_free(iface);
}
@@ -3341,21 +3360,22 @@ 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) {
-#ifdef CONFIG_IEEE80211BE
- if (hostapd_mld_is_first_bss(hapd_iface->bss[0]))
-#endif /* CONFIG_IEEE80211BE */
- 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->extended_capa = NULL;
- hapd_iface->extended_capa_mask = NULL;
- hapd_iface->extended_capa_len = 0;
- }
+
+ if (hapd_iface->bss[j]->drv_priv != drv_priv)
+ continue;
+
+ hapd_iface->bss[j]->drv_priv = NULL;
+ hapd_iface->extended_capa = NULL;
+ hapd_iface->extended_capa_mask = NULL;
+ hapd_iface->extended_capa_len = 0;
}
}
}
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 6616397fb5d9..d50cbcdb26d1 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -5154,6 +5154,19 @@ struct wpa_driver_ops {
*/
int (*if_link_remove)(void *priv, enum wpa_driver_if_type type,
const char *ifname, s8 link_id);
+
+ /**
+ * get_drv_shared_status - Get shared status of driver interface
+ * @priv: private driver interface data from init()
+ * @bss_ctx: BSS context for %WPA_IF_AP_BSS interfaces
+ *
+ * Checks whether driver interface is being used by other partner
+ * BSS(s) or not. This is used to decide whether driver interface
+ * needs to be deinitilized when one iface is getting deinitialized.
+ *
+ * Returns: True if it is being used or else False.
+ */
+ bool (*get_drv_shared_status)(void *priv, void *bss_ctx);
#endif /* CONFIG_IEEE80211BE */
#ifdef CONFIG_TESTING_OPTIONS
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index d5f7cc7d041c..a0616ce39192 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -10755,6 +10755,38 @@ static int driver_nl80211_if_link_remove(void *priv, enum wpa_driver_if_type typ
return wpa_driver_nl80211_if_link_remove(bss, type, ifname, link_id);
}
+
+static bool wpa_driver_get_shared_status(void *priv, void *bss_ctx)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int num_bss = 0;
+
+ /* If any other bss exist, means someone else is using since at this
+ * time, we would have removed all bss created by this driver and only
+ * this bss should be remaining if 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 then no one is sharing */
+ if (bss->valid_links <= 1)
+ return false;
+
+ /* More than one links means some one 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 */
static int driver_nl80211_send_mlme(void *priv, const u8 *data,
@@ -14087,6 +14119,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.link_add = nl80211_link_add,
#ifdef CONFIG_IEEE80211BE
.if_link_remove = driver_nl80211_if_link_remove,
+ .get_drv_shared_status = wpa_driver_get_shared_status,
#endif /* CONFIG_IEEE80211BE */
#ifdef CONFIG_TESTING_OPTIONS
.register_frame = testing_nl80211_register_frame,
--
2.25.1
More information about the Hostap
mailing list