From ade710c95f8c249b97e87e41b5cebb40c74ee63e Mon Sep 17 00:00:00 2001 From: Aditya Kumar Singh Date: Tue, 10 Oct 2023 15:24:10 +0530 Subject: [PATCH 1/2] hostapd: add support to use underlying hw_idx for multi-hardware under single phy mode During multi-hardware under singly phy scenario, single channel list is maintained for a given band which does not clearly tells how many different underlying hardwares are being used to form that band. For example - in 5 GHz split hardware case, two underlying hardwares 5 GHz Low and 5 GHz High are being used to enable full 5 GHz band. Now, when channel is selected from list of channels, it is possible that a channel can be picked which the hardware does not support (lower chanels can be picked for 5 GHz high hardware) which is wrong. However, the kernel does tells us about the underlying hardwares and the list of supported frequencies. Hence, add support to get the advertised hw_idx and the corresponding list of supported frequencies and then decide what is the current hw_idx being used. Signed-off-by: Aditya Kumar Singh --- src/ap/ap_drv_ops.c | 9 ++ src/ap/ap_drv_ops.h | 3 + src/ap/hostapd.c | 7 ++ src/ap/hostapd.h | 4 + src/ap/hw_features.c | 77 ++++++++++++++++ src/ap/hw_features.h | 14 +++ src/drivers/driver.h | 31 +++++++ src/drivers/driver_nl80211.c | 1 + src/drivers/driver_nl80211.h | 2 + src/drivers/driver_nl80211_capa.c | 144 ++++++++++++++++++++++++++++++ tests/fuzzing/ap-mgmt/ap-mgmt.c | 2 + 11 files changed, 294 insertions(+) --- a/src/ap/ap_drv_ops.c +++ b/src/ap/ap_drv_ops.c @@ -1165,3 +1165,12 @@ int hostapd_drv_set_secure_ranging_ctx(s return hapd->driver->set_secure_ranging_ctx(hapd->drv_priv, ¶ms); } #endif /* CONFIG_PASN */ + +struct hostapd_multi_hw_info * +hostapd_get_multi_hw_info(struct hostapd_data *hapd, u8 *num_multi_hws) +{ + if (hapd->driver == NULL || + hapd->driver->get_multi_hw_info == NULL) + return NULL; + return hapd->driver->get_multi_hw_info(hapd->drv_priv, num_multi_hws); +} --- a/src/ap/ap_drv_ops.h +++ b/src/ap/ap_drv_ops.h @@ -462,4 +462,7 @@ static inline int hostapd_drv_link_add(s } #endif /* CONFIG_IEEE80211BE */ +struct hostapd_multi_hw_info * +hostapd_get_multi_hw_info(struct hostapd_data *hapd, u8 *num_multi_hws); + #endif /* AP_DRV_OPS */ --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -683,6 +683,9 @@ void hostapd_cleanup_iface_partial(struc ap_list_deinit(iface); sta_track_deinit(iface); airtime_policy_update_deinit(iface); + hostapd_free_multi_hw_info(iface->multi_hw_info, iface->num_multi_hws); + iface->multi_hw_info = NULL; + iface->current_hw_info = NULL; } @@ -2466,6 +2469,10 @@ static int hostapd_setup_interface_compl hostapd_hw_mode_txt(iface->conf->hw_mode), iface->conf->channel, iface->freq); + if (hostapd_set_current_hw_info(iface, iface->freq)) { + wpa_printf(MSG_ERROR, "Failed to get operating hw mac id"); + goto fail; + } #ifdef NEED_AP_MLME /* Handle DFS only if it is not offloaded to the driver */ if (!(iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)) { --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -719,6 +719,10 @@ struct hostapd_iface { /* Configured freq of interface is NO_IR */ bool is_no_ir; + + struct hostapd_multi_hw_info *multi_hw_info; + u8 num_multi_hws; + struct hostapd_multi_hw_info *current_hw_info; }; /* Iterate over all affiliated links in MLD to which @self belongs. --- a/src/ap/hw_features.c +++ b/src/ap/hw_features.c @@ -79,6 +79,8 @@ int hostapd_get_hw_features(struct hosta u16 num_modes, flags; struct hostapd_hw_modes *modes; u8 dfs_domain; + struct hostapd_multi_hw_info *multi_hw_info; + u8 num_multi_hws; if (hostapd_drv_none(hapd)) return -1; @@ -141,6 +143,35 @@ int hostapd_get_hw_features(struct hosta } } + multi_hw_info = hostapd_get_multi_hw_info(hapd, &num_multi_hws); + if (multi_hw_info == NULL) + return 0; + + hostapd_free_multi_hw_info(iface->multi_hw_info, iface->num_multi_hws); + iface->multi_hw_info = multi_hw_info; + iface->num_multi_hws = num_multi_hws; + + wpa_printf(MSG_DEBUG, "Multi Underlying Hardwares Info: "); + + for (i = 0; i < num_multi_hws; i++) { + struct hostapd_multi_hw_info *hw_info = &multi_hw_info[i]; + char str[1000]; + char *pos = str; + char *end = pos + sizeof(str); + int res; + + for (j = 0; j < hw_info->num_freqs; j++) { + res = os_snprintf(pos, end - pos, "%d, ", hw_info->freqs[j]); + if (os_snprintf_error(end - pos, res)) + break; + pos += res; + } + + *pos = '\0'; + wpa_printf(MSG_DEBUG, " %d. hw_idx=%u, frequencies: %s", + i + 1, hw_info->hw_idx, str); + } + return 0; } @@ -1431,3 +1462,49 @@ int hostapd_hw_skip_mode(struct hostapd_ } return 0; } + +void hostapd_free_multi_hw_info(struct hostapd_multi_hw_info *multi_hw_info, + size_t num_multi_hws) +{ + size_t i; + + if (multi_hw_info == NULL) + return; + + for (i = 0; i < num_multi_hws; i++) + os_free(multi_hw_info[i].freqs); + + os_free(multi_hw_info); +} + +int hostapd_set_current_hw_info(struct hostapd_iface *iface, u32 oper_freq) +{ + struct hostapd_multi_hw_info *hw_info; + int i, j; + bool found = false; + + if (!iface->num_multi_hws) + return 0; + + for (i = 0; i < iface->num_multi_hws; i++) { + hw_info = &iface->multi_hw_info[i]; + + for (j = 0; j < hw_info->num_freqs; j++) { + if (hw_info->freqs[j] == oper_freq) { + found = true; + break; + } + } + + if (found) + break; + } + + if (i >= iface->num_multi_hws) + return 1; + + iface->current_hw_info = hw_info; + wpa_printf(MSG_DEBUG, "Mode: Selected underlying HW: hw_idx=%u", + iface->current_hw_info->hw_idx); + return 0; +} --- a/src/ap/hw_features.h +++ b/src/ap/hw_features.h @@ -29,6 +29,9 @@ void hostapd_stop_setup_timers(struct ho int hostapd_hw_skip_mode(struct hostapd_iface *iface, struct hostapd_hw_modes *mode); void hostapd_determine_mode(struct hostapd_iface *iface); +void hostapd_free_multi_hw_info(struct hostapd_multi_hw_info *multi_macs, + size_t num_multi_hws); +int hostapd_set_current_hw_info(struct hostapd_iface *iface, u32 oper_freq); #else /* NEED_AP_MLME */ static inline void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features, @@ -96,6 +99,17 @@ static inline void hostapd_determine_mod { } +static inline +void hostapd_free_multi_hw_info(struct hostapd_multi_hw_info *multi_macs, + size_t num_multi_hws) +{ +} + +static inline int hostapd_set_current_hw_info(struct hostapd_iface *iface, + u32 oper_freq) +{ + return 0; +} #endif /* NEED_AP_MLME */ #endif /* HW_FEATURES_H */ --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -320,6 +320,25 @@ struct hostapd_hw_modes { s8 psd_values[NL80211_REG_NUM_POWER_MODES]; }; +/** + * struct hostapd_multi_hw_info: Supported multiple underyling hardware info + */ +struct hostapd_multi_hw_info { + /** + * hw_idx - Hardware index + */ + u8 hw_idx; + + /** + * num_freqs - Number of entries in the freqs array + */ + u8 num_freqs; + + /** + * freqs - Array of supported frequencies + */ + u32 *freqs; +}; #define IEEE80211_CAP_ESS 0x0001 #define IEEE80211_CAP_IBSS 0x0002 @@ -5225,6 +5244,18 @@ struct wpa_driver_ops { */ int (*can_share_drv)(void *ctx, struct wpa_init_params *params, void **drv_priv); #endif /* CONFIG_IEEE80211BE */ + + /** + * get_multi_hw_info - Get multi hardware data (hardware IDx and + * supported frequencies) + * @priv: Private driver interface data + * @num_multi_hws: Variable for returning the number of returned hardware + * info data + * Returns: Pointer to allocated mutli hardware data on success or + * %NULL on failure. Caller is responsible for freeing this. + */ + struct hostapd_multi_hw_info * (*get_multi_hw_info)(void *priv, + u8 *num_multi_hws); }; /** --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -14020,6 +14020,12 @@ free_all: } #endif /* CONFIG_IEEE80211BE */ +static struct hostapd_multi_hw_info * +wpa_driver_get_multi_hw_info(void *priv, u8 *num_multi_hws) +{ + return nl80211_get_multi_hw_info(priv, num_multi_hws); +} + const struct wpa_driver_ops wpa_driver_nl80211_ops = { .name = "nl80211", .desc = "Linux nl80211/cfg80211", @@ -14179,4 +14185,5 @@ const struct wpa_driver_ops wpa_driver_n .get_drv_shared_status = wpa_driver_get_shared_status, .can_share_drv = wpa_driver_nl80211_can_share_drv, #endif /* CONFIG_IEEE80211BE */ + .get_multi_hw_info = wpa_driver_get_multi_hw_info, }; --- a/src/drivers/driver_nl80211.h +++ b/src/drivers/driver_nl80211.h @@ -368,5 +368,7 @@ int wpa_driver_nl80211_vendor_scan(struc 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); +struct hostapd_multi_hw_info * +nl80211_get_multi_hw_info(void *priv, u8 *num_multi_hws); #endif /* DRIVER_NL80211_H */ --- a/src/drivers/driver_nl80211_capa.c +++ b/src/drivers/driver_nl80211_capa.c @@ -1599,6 +1599,12 @@ struct phy_info_arg { u8 pwr_mode; }; +struct phy_multi_hw_info_arg { + int failed; + u8 *num_multi_hws; + struct hostapd_multi_hw_info *multi_hws; +}; + static void phy_info_ht_capa(struct hostapd_hw_modes *mode, struct nlattr *capa, struct nlattr *ampdu_factor, struct nlattr *ampdu_density, @@ -2677,6 +2683,144 @@ nl80211_get_hw_feature_data(void *priv, } return NULL; +} + +static int phy_multi_hw_info_parse(struct phy_multi_hw_info_arg *multi_hw_info, + struct nlattr *nl_hw_macs) +{ + struct nlattr *tb_hw[NL80211_MULTI_HW_MAC_ATTR_MAX + 1]; + struct nlattr *tb_freq[NL80211_MULTI_HW_MAC_CHAN_LIST_ATTR_MAX + 1]; + struct nlattr *nl_freq_list; + struct hostapd_multi_hw_info *multi_hws; + int rem_freq_list; + u8 hw_idx, freq_idx = 0, i; + u32 freq[256]; + + nla_parse(tb_hw, NL80211_MULTI_HW_MAC_ATTR_MAX, nla_data(nl_hw_macs), + nla_len(nl_hw_macs), NULL); + + if (!tb_hw[NL80211_MULTI_HW_MAC_ATTR_IDX] || + !tb_hw[NL80211_MULTI_HW_MAC_ATTR_CHAN_LIST]) + return NL_STOP; + + hw_idx = nla_get_u8(tb_hw[NL80211_MULTI_HW_MAC_ATTR_IDX]); + + nla_for_each_nested(nl_freq_list, tb_hw[NL80211_MULTI_HW_MAC_ATTR_CHAN_LIST], + rem_freq_list) + { + nla_parse(tb_freq, NL80211_MULTI_HW_MAC_CHAN_LIST_ATTR_MAX, + nla_data(nl_freq_list), nla_len(nl_freq_list), NULL); + + if (!tb_freq[NL80211_MULTI_HW_MAC_CHAN_LIST_ATTR_FREQ]) + return NL_STOP; + + freq[freq_idx++] = + nla_get_u32(tb_freq[NL80211_MULTI_HW_MAC_CHAN_LIST_ATTR_FREQ]); + } + + multi_hws = os_realloc_array(multi_hw_info->multi_hws, + *multi_hw_info->num_multi_hws + 1, + sizeof(*multi_hws)); + if (!multi_hws) { + multi_hw_info->failed = 1; + return NL_STOP; + } + + multi_hw_info->multi_hws = multi_hws; + + multi_hws = &multi_hw_info->multi_hws[*(multi_hw_info->num_multi_hws)]; + + multi_hws->hw_idx = hw_idx; + multi_hws->num_freqs = freq_idx; + + multi_hws->freqs = os_calloc(multi_hws->num_freqs, sizeof(u32)); + if (!multi_hws->freqs) { + multi_hw_info->failed = 1; + return NL_STOP; + } + + for (i = 0; i < multi_hws->num_freqs; i++) + multi_hws->freqs[i] = freq[i]; + + *(multi_hw_info->num_multi_hws) += 1; + + return NL_OK; +} + +static int phy_multi_hw_info_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct phy_multi_hw_info_arg *multi_hw_info = arg; + struct nlattr *nl_hw_macs; + int rem_hw_macs, res; + + nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb_msg[NL80211_ATTR_MULTI_HW_MACS]) + return NL_SKIP; + + nla_for_each_nested(nl_hw_macs, tb_msg[NL80211_ATTR_MULTI_HW_MACS], + rem_hw_macs) + { + res = phy_multi_hw_info_parse(multi_hw_info, nl_hw_macs); + if (res != NL_OK) + return res; + } + + return NL_SKIP; +} + +struct hostapd_multi_hw_info * +nl80211_get_multi_hw_info(void *priv, u8 *num_multi_hws) +{ + u32 feat; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + int nl_flags = 0; + struct nl_msg *msg; + struct phy_multi_hw_info_arg result = { + .failed = 0, + .num_multi_hws = num_multi_hws, + .multi_hws = NULL, + }; + + *num_multi_hws = 0; + + if (!drv->has_capability) + return NULL; + + if (!(drv->capa.flags2 & WPA_DRIVER_FLAGS2_MLO)) + return NULL; + + feat = get_nl80211_protocol_features(drv); + if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP) + nl_flags = NLM_F_DUMP; + if (!(msg = nl80211_cmd_msg(bss, nl_flags, NL80211_CMD_GET_WIPHY)) || + nla_put_flag(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP)) { + nlmsg_free(msg); + return NULL; + } + + if (send_and_recv_msgs(drv, msg, phy_multi_hw_info_handler, &result, + NULL, NULL) == 0) { + if (result.failed) { + int i; + + for (i = 0; result.num_multi_hws && i < *num_multi_hws; i++) + os_free(result.multi_hws[i].freqs); + + os_free(result.multi_hws); + *num_multi_hws = 0; + + return NULL; + } + + return result.multi_hws; + } + + return NULL; } #ifdef CONFIG_IEEE80211BE --- a/tests/fuzzing/ap-mgmt/ap-mgmt.c +++ b/tests/fuzzing/ap-mgmt/ap-mgmt.c @@ -156,6 +156,8 @@ int LLVMFuzzerTestOneInput(const uint8_t hostapd_free_stas(&ctx.hapd); hostapd_free_hw_features(ctx.hapd.iface->hw_features, ctx.hapd.iface->num_hw_features); + hostapd_free_multi_hw_info(ctx.hapd.iface->multi_hw_info, + ctx.hapd.iface->num_multi_hws); fail: hostapd_config_free(ctx.hapd.iconf);