From b4bd16074a9655c0b40e462325c6946c0c3f99c4 Mon Sep 17 00:00:00 2001 From: Aditya Kumar Singh Date: Fri, 20 Oct 2023 15:39:32 +0530 Subject: [PATCH] hostapd: re-assign driver context if existing ML partner is found Currently multiple driver contexts were maintained for a single interface in certain co-hosted situations. For instance - 5 GHz SLO and 5 GHz + 6 GHz MLD. In this case, if 6 GHz config is passed first then driver would be created. And when 5 GHz config is parsed, since bss[0] is SLO, there won't be any partner and hence again driver would be created. This makes 5 GHz interface to have two different drivers. During de-initialization, proper deinitialization will not happen and a few times seg fault could be seen. Hence to avoid this situation, try to maintain single driver if possible. Signed-off-by: Aditya Kumar Singh --- src/ap/ap_drv_ops.h | 9 +++++ src/ap/hostapd.c | 1 + src/drivers/driver.h | 10 +++++ src/drivers/driver_nl80211.c | 71 ++++++++++++++++++++++++++++++++++++ 4 files changed, 91 insertions(+) --- a/hostapd/main.c +++ b/hostapd/main.c @@ -249,6 +249,15 @@ static int hostapd_driver_init(struct ho params.own_addr = hapd->own_addr; +#ifdef CONFIG_IEEE80211BE + if (hapd->driver->can_share_drv && + !hapd->driver->can_share_drv(hapd, ¶ms, &hapd->drv_priv)) { + hapd->interface_added = 1; + os_free(params.bridge); + goto pre_setup_mld; + } +#endif /* CONFIG_IEEE80211BE */ + hapd->drv_priv = hapd->driver->hapd_init(hapd, ¶ms); if (hapd->conf->is_bonded_iface_enabled) hostapd_mld_update_bridge_conn(hapd->conf->bridge, hapd->conf->iface); @@ -261,6 +270,7 @@ static int hostapd_driver_init(struct ho } #ifdef CONFIG_IEEE80211BE +pre_setup_mld: /* * This is the first interface added to the AP MLD, so have the * interface hardware address be the MLD address and set a link address --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -523,7 +523,10 @@ void hostapd_free_hapd_data(struct hosta authsrv_deinit(hapd); - if (hapd->interface_added) { + /* For single drv, first bss would have interface_added flag set. Don't + * remove interface now. driver deinit part will take care + */ + if (hapd->interface_added && hapd->iface->bss[0] != hapd) { hapd->interface_added = 0; if (hostapd_if_remove(hapd, WPA_IF_AP_BSS, hapd->conf->iface)) { wpa_printf(MSG_WARNING, @@ -3216,25 +3219,26 @@ static void hostapd_cleanup_driver(const 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); - 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])) { + if (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)) { + goto exit; + } + + if (ap_mld) { + 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 link BSS interface %s", + iface->bss[0]->conf->iface); + } else if (hostapd_if_remove(iface->bss[0], WPA_IF_AP_BSS, + iface->bss[0]->conf->iface)) { wpa_printf(MSG_WARNING, "Failed to remove BSS interface %s", iface->bss[0]->conf->iface); } +exit: iface->bss[0]->drv_priv = NULL; } --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -5210,6 +5210,20 @@ struct wpa_driver_ops { * Returns: True if it is being used or else False. */ bool (*get_drv_shared_status)(void *priv, void *bss_ctx); + + /** + * can_share_drv - Check if already a drv exist in the same global driver + * which can be shared instead of creating a new drv. + * Used during single wiphy operation. + * + * @ctx: Pointer to hostapd context + * @params: Configuration for the driver wrapper + * @drv_priv: Pointer for overwriting the driver context or %NULL if + * cant' find a shared drv + * + * Returns: 0 on success, -1 on failure + */ + int (*can_share_drv)(void *ctx, struct wpa_init_params *params, void **drv_priv); #endif /* CONFIG_IEEE80211BE */ }; --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -2916,10 +2916,12 @@ wpa_driver_nl80211_finish_drv_init(struc if (!bss->if_dynamic && nl80211_get_ifmode(bss) == NL80211_IFTYPE_AP) bss->static_ap = 1; - if (first && - nl80211_get_ifmode(bss) != NL80211_IFTYPE_P2P_DEVICE && - linux_iface_up(drv->global->ioctl_sock, bss->ifname) > 0) - drv->start_iface_up = 1; + if (nl80211_get_ifmode(bss) != NL80211_IFTYPE_P2P_DEVICE && + linux_iface_up(drv->global->ioctl_sock, bss->ifname) > 0) { + if (first) + drv->start_iface_up = 1; + bss->start_iface_up = 1; + } if (wpa_driver_nl80211_capa(drv)) return -1; @@ -2936,8 +2938,11 @@ wpa_driver_nl80211_finish_drv_init(struc set_addr))) return -1; - if (first && nl80211_get_ifmode(bss) == NL80211_IFTYPE_STATION) - drv->start_mode_sta = 1; + if (nl80211_get_ifmode(bss) == NL80211_IFTYPE_STATION) { + if (first) + drv->start_mode_sta = 1; + bss->start_mode_sta = 1; + } if (drv->hostapd || bss->static_ap) nlmode = NL80211_IFTYPE_AP; @@ -8907,6 +8912,7 @@ static int wpa_driver_nl80211_if_remove( { struct wpa_driver_nl80211_data *drv = bss->drv; int ifindex = if_nametoindex(ifname); + enum nl80211_iftype nlmode; wpa_printf(MSG_DEBUG, "nl80211: %s(type=%d ifname=%s) ifindex=%d added_if=%d", __func__, type, ifname, ifindex, bss->added_if); @@ -8945,12 +8951,20 @@ static int wpa_driver_nl80211_if_remove( for (tbss = drv->first_bss; tbss; tbss = tbss->next) { if (tbss->next == bss) { tbss->next = bss->next; - /* Unsubscribe management frames */ - nl80211_teardown_ap(bss); + bss->in_deinit = 1; nl80211_remove_links(bss); - nl80211_destroy_bss(bss); + nlmode = drv->nlmode; if (!bss->added_if) i802_set_iface_flags(bss, 0); + if (drv->nlmode != NL80211_IFTYPE_P2P_DEVICE && + bss->start_mode_sta) + wpa_driver_nl80211_set_mode(bss, + NL80211_IFTYPE_STATION); + else + /* Unsubscribe management frames */ + nl80211_teardown_ap(bss); + nl80211_destroy_bss(bss); + drv->nlmode = nlmode; os_free(bss); bss = NULL; break; @@ -8960,16 +8974,29 @@ static int wpa_driver_nl80211_if_remove( wpa_printf(MSG_INFO, "nl80211: %s - could not find " "BSS %p in the list", __func__, bss); } else { - wpa_printf(MSG_DEBUG, "nl80211: First BSS - reassign context"); - nl80211_teardown_ap(bss); + bss->in_deinit = 1; nl80211_remove_links(bss); - nl80211_destroy_bss(bss); - if (!bss->added_if) + nlmode = drv->nlmode; + if (!bss->start_iface_up) i802_set_iface_flags(bss, 0); + if (drv->nlmode != NL80211_IFTYPE_P2P_DEVICE && + bss->start_mode_sta) + wpa_driver_nl80211_set_mode(bss, + NL80211_IFTYPE_STATION); + else + nl80211_teardown_ap(bss); + nl80211_destroy_bss(bss); + drv->nlmode = nlmode; + + wpa_printf(MSG_DEBUG, "nl80211: First BSS - reassign context"); if (drv->first_bss->next) { drv->first_bss = drv->first_bss->next; drv->ctx = drv->first_bss->ctx; + drv->ifindex = drv->first_bss->ifindex; + drv->start_iface_up = drv->first_bss->start_iface_up; + drv->start_mode_sta = drv->first_bss->start_mode_sta; os_free(bss); + bss = NULL; } else { wpa_printf(MSG_DEBUG, "nl80211: No second BSS to reassign context to"); } @@ -13884,6 +13911,120 @@ static bool wpa_driver_get_shared_status return false; } +#ifdef CONFIG_IEEE80211BE +static bool wpa_driver_nl80211_name_match(struct wpa_driver_nl80211_data *drv, + struct wpa_driver_nl80211_data **match_drv) +{ + struct wpa_driver_nl80211_data *drv_itr, *tmp; + + dl_list_for_each_safe(drv_itr, tmp, &drv->global->interfaces, + struct wpa_driver_nl80211_data, list) { + if (os_strcmp(drv_itr->phyname, drv->phyname) == 0) { + if (match_drv) + *match_drv = drv_itr; + return true; + } + } + + return false; +} + +static int wpa_driver_nl80211_can_share_drv(void *ctx, + struct wpa_init_params *params, + void **drv_priv) +{ + struct wpa_driver_nl80211_data *drv, *match_drv; + struct i802_bss *bss, *new_bss; + char force_ifname[IFNAMSIZ]; + u8 if_addr[ETH_ALEN]; + const u8 *addr; + int ret = -1; + + /* create a dummy drv to read the phyname */ + if (params->global_priv == NULL) + return ret; + drv = os_zalloc(sizeof(*drv)); + + if (drv == NULL) + return ret; + drv->global = params->global_priv; + drv->ctx = ctx; + + drv->first_bss = os_zalloc(sizeof(*drv->first_bss)); + if (!drv->first_bss) { + os_free(drv); + return ret; + } + bss = drv->first_bss; + bss->drv = drv; + bss->ctx = ctx; + + os_strlcpy(bss->ifname, params->ifname, sizeof(bss->ifname)); + + if (nl80211_init_bss(bss)) + goto free_all; + + drv->ifindex = if_nametoindex(bss->ifname); + bss->ifindex = drv->ifindex; + + if (wpa_driver_set_phyname(drv)) + goto free_all; + + match_drv = NULL; + if (!wpa_driver_nl80211_name_match(drv, &match_drv)) + goto free_all; + + if (!match_drv) + goto free_all; + + wpa_printf(MSG_DEBUG, "nl80211: Driver for phy %s already exist. Using that.", + match_drv->phyname); + + /* store the original state and mode */ + if (nl80211_get_ifmode(bss) != NL80211_IFTYPE_P2P_DEVICE && + linux_iface_up(match_drv->global->ioctl_sock, bss->ifname) > 0) + bss->start_iface_up = 1; + + if (nl80211_get_ifmode(bss) == NL80211_IFTYPE_STATION) + bss->start_mode_sta = 1; + + addr = params->bssid; + if (wpa_driver_nl80211_if_add(match_drv->first_bss, WPA_IF_AP_BSS, + params->ifname, addr, ctx, drv_priv, + force_ifname, if_addr, + params->num_bridge && params->bridge[0] ? params->bridge[0] : NULL, + 0, 1)) { + wpa_printf(MSG_DEBUG, "nl80211: Failed to add BSS. Expect issues!"); + goto free_all; + } + + if (!addr) + os_memcpy(params->own_addr, if_addr, ETH_ALEN); + else + os_memcpy(params->own_addr, addr, ETH_ALEN); + + /* retain the state and mode in newly created bss. This will be used during + * driver deinit and reassigning first bss to this bss + */ + new_bss = *drv_priv; + new_bss->start_iface_up = bss->start_iface_up; + new_bss->start_mode_sta = bss->start_mode_sta; + + /* This flag was set in wpa_driver_nl80211_if_add(), but it was already + * present, hence reset it back. This would be useful during deinit flow + */ + new_bss->added_if = 0; + + ret = 0; + +free_all: + nl80211_destroy_bss(bss); + os_free(bss); + os_free(drv); + return ret; +} +#endif /* CONFIG_IEEE80211BE */ + const struct wpa_driver_ops wpa_driver_nl80211_ops = { .name = "nl80211", .desc = "Linux nl80211/cfg80211", @@ -14041,5 +14182,6 @@ const struct wpa_driver_ops wpa_driver_n #ifdef CONFIG_IEEE80211BE .if_link_remove = driver_nl80211_if_link_remove, .get_drv_shared_status = wpa_driver_get_shared_status, + .can_share_drv = wpa_driver_nl80211_can_share_drv, #endif /* CONFIG_IEEE80211BE */ }; --- a/src/drivers/driver_nl80211.h +++ b/src/drivers/driver_nl80211.h @@ -82,6 +82,8 @@ struct i802_bss { unsigned int added_if:1; unsigned int static_ap:1; unsigned int use_nl_connect:1; + unsigned int start_iface_up:1; + unsigned int start_mode_sta:1; u8 addr[ETH_ALEN]; u8 prev_addr[ETH_ALEN]; @@ -365,5 +367,6 @@ int wpa_driver_nl80211_abort_scan(void * int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss, struct wpa_driver_scan_params *params); int nl80211_set_default_scan_ies(void *priv, const u8 *ies, size_t ies_len); +int wpa_driver_set_phyname(struct wpa_driver_nl80211_data *drv); #endif /* DRIVER_NL80211_H */ --- a/src/drivers/driver_nl80211_capa.c +++ b/src/drivers/driver_nl80211_capa.c @@ -2678,3 +2678,46 @@ nl80211_get_hw_feature_data(void *priv, return NULL; } + +#ifdef CONFIG_IEEE80211BE +static int wpa_driver_get_wiphy_name(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct wpa_driver_nl80211_data *drv = arg; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb[NL80211_ATTR_WIPHY_NAME]) + return NL_SKIP; + + os_strlcpy(drv->phyname, nla_get_string(tb[NL80211_ATTR_WIPHY_NAME]), + sizeof(drv->phyname)); + + return NL_SKIP; +} + +int wpa_driver_set_phyname(struct wpa_driver_nl80211_data *drv) +{ + struct nl_msg *msg; + u32 feat, nl_flags; + + feat = get_nl80211_protocol_features(drv); + if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP) + nl_flags = NLM_F_DUMP; + + if (!(msg = nl80211_cmd_msg(drv->first_bss, nl_flags, + NL80211_CMD_GET_WIPHY)) || + nla_put_flag(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP)) { + nlmsg_free(msg); + return -1; + } + + if (send_and_recv_msgs(drv, msg, wpa_driver_get_wiphy_name, drv, + NULL, NULL)) + return -1; + + return 0; +} +#endif /* CONFIG_IEEE80211BE */