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@xxxxxxxxxxx> --- 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 _______________________________________________ Hostap mailing list Hostap@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/hostap