From 3bde811756406eb1b56a75eb60cf351914efcf8d Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Tue, 21 Nov 2023 01:51:35 +0200 Subject: [PATCH] ML: Add basic handling of ML probe requests This responds by simply embedding most of the IEs from the other links into the ML element. This is not correct really, as inheritance rules should be applied and an inheritance element may need to be added. Signed-off-by: Benjamin Berg Signed-off-by: Andrei Otcheretianski --- src/ap/beacon.c | 212 +++++++++++++++++++++++++++++++++------- src/ap/ieee802_11.h | 3 + src/ap/ieee802_11_eht.c | 61 +++++++++++- 3 files changed, 238 insertions(+), 38 deletions(-) diff --git a/src/ap/beacon.c b/src/ap/beacon.c index d6fe2e342..5a5be0ef4 100644 --- a/src/ap/beacon.c +++ b/src/ap/beacon.c @@ -569,6 +569,11 @@ struct probe_resp_params { const struct ieee80211_mgmt *req; bool is_p2p; + /* Generated IEs will be included inside an ML element */ + bool is_ml_sta_info; + struct hostapd_data *mld_ap; + struct mld_info *mld_info; + struct ieee80211_mgmt *resp; size_t resp_len; u8 *csa_pos; @@ -630,20 +635,21 @@ static size_t hostapd_probe_resp_elems_len(struct hostapd_data *hapd, if (hapd->iconf->punct_bitmap) buflen += EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE; - /* - * TODO: Multi-Link element has variable length and can be - * long based on the common info and number of per - * station profiles. For now use 256. - */ - if (hapd->conf->mld_ap) - buflen += 256; + if (!params->is_ml_sta_info && hapd->conf->mld_ap) { + struct hostapd_data *ml_elem_ap = + params->mld_ap ? params->mld_ap : hapd; + + buflen += hostapd_eid_eht_ml_beacon_len( + ml_elem_ap, params->mld_info, !!params->mld_ap); + } } #endif /* CONFIG_IEEE80211BE */ buflen += hostapd_eid_mbssid_len(hapd, WLAN_FC_STYPE_PROBE_RESP, NULL, params->known_bss, params->known_bss_len, NULL); - buflen += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_PROBE_RESP); + if (!params->is_ml_sta_info) + buflen += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_PROBE_RESP); buflen += hostapd_mbo_ie_len(hapd); buflen += hostapd_eid_owe_trans_len(hapd); buflen += hostapd_eid_dpp_cc_len(hapd); @@ -661,10 +667,13 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd, epos = pos + len; - *pos++ = WLAN_EID_SSID; - *pos++ = hapd->conf->ssid.ssid_len; - os_memcpy(pos, hapd->conf->ssid.ssid, hapd->conf->ssid.ssid_len); - pos += hapd->conf->ssid.ssid_len; + if (!params->is_ml_sta_info) { + *pos++ = WLAN_EID_SSID; + *pos++ = hapd->conf->ssid.ssid_len; + os_memcpy(pos, hapd->conf->ssid.ssid, + hapd->conf->ssid.ssid_len); + pos += hapd->conf->ssid.ssid_len; + } /* Supported rates */ pos = hostapd_eid_supp_rates(hapd, pos); @@ -677,13 +686,18 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd, /* Power Constraint element */ pos = hostapd_eid_pwr_constraint(hapd, pos); - /* CSA IE */ - csa_pos = hostapd_eid_csa(hapd, pos); - if (csa_pos != pos) - params->csa_pos = csa_pos - 1; - else - params->csa_pos = NULL; - pos = csa_pos; + /* + * CSA IE + * TODO: This should be included inside the ML sta profile + */ + if (!params->is_ml_sta_info) { + csa_pos = hostapd_eid_csa(hapd, pos); + if (csa_pos != pos) + params->csa_pos = csa_pos - 1; + else + params->csa_pos = NULL; + pos = csa_pos; + } /* ERP Information element */ pos = hostapd_eid_erp_info(hapd, pos); @@ -699,13 +713,18 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd, pos = hostapd_eid_rm_enabled_capab(hapd, pos, epos - pos); pos = hostapd_get_mde(hapd, pos, epos - pos); - /* eCSA IE */ - csa_pos = hostapd_eid_ecsa(hapd, pos); - if (csa_pos != pos) - params->ecsa_pos = csa_pos - 1; - else - params->ecsa_pos = NULL; - pos = csa_pos; + /* + * eCSA IE + * TODO: This should be included inside the ML sta profile + */ + if (!params->is_ml_sta_info) { + csa_pos = hostapd_eid_ecsa(hapd, pos); + if (csa_pos != pos) + params->ecsa_pos = csa_pos - 1; + else + params->ecsa_pos = NULL; + pos = csa_pos; + } pos = hostapd_eid_supported_op_classes(hapd, pos); pos = hostapd_eid_ht_capabilities(hapd, pos); @@ -749,7 +768,8 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd, pos = hostapd_eid_wb_chsw_wrapper(hapd, pos); - pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_PROBE_RESP); + if (!params->is_ml_sta_info) + pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_PROBE_RESP); pos = hostapd_eid_fils_indic(hapd, pos, 0); pos = hostapd_get_rsnxe(hapd, pos, epos - pos); @@ -776,8 +796,14 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd, #ifdef CONFIG_IEEE80211BE if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) { - if (hapd->conf->mld_ap) - pos = hostapd_eid_eht_ml_beacon(hapd, NULL, pos, true); + struct hostapd_data *ml_elem_ap = + params->mld_ap ? params->mld_ap : hapd; + + if (ml_elem_ap->conf->mld_ap) + pos = hostapd_eid_eht_ml_beacon( + ml_elem_ap, params->mld_info, + pos, !!params->mld_ap); + pos = hostapd_eid_eht_capab(hapd, pos, IEEE80211_MODE_AP); pos = hostapd_eid_eht_operation(hapd, pos); } @@ -883,6 +909,115 @@ void hostapd_gen_probe_resp(struct hostapd_data *hapd, } +#ifdef CONFIG_IEEE80211BE +static void hostapd_fill_probe_resp_ml_params(struct hostapd_data *hapd, + struct probe_resp_params *params, + const struct ieee80211_mgmt *mgmt, + int mld_id, u16 links) +{ + struct probe_resp_params sta_info_params; + struct hostapd_data *link; + unsigned int probed_mld_id, i, j; + + params->mld_ap = NULL; + params->mld_info = os_zalloc(sizeof(*params->mld_info)); + if (!params->mld_info) + return; + + wpa_printf(MSG_DEBUG, + "MLD: Got ML probe request with AP MLD ID %d for links %04x", + mld_id, links); + + /* + * We want to include the AP MLD ID in the response if it was + * included in the request. + */ + probed_mld_id = mld_id != -1 ? mld_id : hapd->conf->mld_id; + + for_each_mld_link(link, i, j, hapd->iface->interfaces, + probed_mld_id) { + struct mld_link_info *link_info; + size_t buflen; + u8 mld_link_id = link->mld_link_id; + u8 *epos; + + /* + * Set mld_ap iff the ML probe request explicitly + * requested a specific MLD ID. In that case, the targeted + * AP may have been a nontransmitted BSSID on the same + * interface. + */ + if (mld_id != -1 && link->iface == hapd->iface) + params->mld_ap = link; + + /* Never duplicate main Probe Response frame body */ + if (link == hapd) + continue; + + /* Only include requested links */ + if (!(BIT(mld_link_id) & links)) + continue; + + link_info = ¶ms->mld_info->links[mld_link_id]; + + sta_info_params.req = params->req; + sta_info_params.is_p2p = false; + sta_info_params.is_ml_sta_info = true; + sta_info_params.mld_ap = NULL; + sta_info_params.mld_info = NULL; + + buflen = MAX_PROBERESP_LEN; + buflen += hostapd_probe_resp_elems_len(link, &sta_info_params); + + if (buflen > sizeof(link_info->resp_sta_profile)) { + wpa_printf(MSG_DEBUG, + "MLD: Not including link %d in ML probe response (%zu bytes is too long)", + mld_link_id, buflen); + goto fail; + } + + /* + * NOTE: This does not properly handle inheritance and + * various other things. + */ + link_info->valid = true; + epos = link_info->resp_sta_profile; + + /* Capabilities is the only fixed parameter */ + WPA_PUT_LE16(link_info->resp_sta_profile, + hostapd_own_capab_info(hapd)); + + epos = hostapd_probe_resp_fill_elems( + link, &sta_info_params, + link_info->resp_sta_profile + 2, + sizeof(link_info->resp_sta_profile) - 2); + link_info->resp_sta_profile_len = + epos - link_info->resp_sta_profile; + os_memcpy(link_info->local_addr, link->own_addr, ETH_ALEN); + + wpa_printf(MSG_DEBUG, + "MLD: ML probe response includes link sta info for %d: %zu bytes (estimate %zu)", + mld_link_id, link_info->resp_sta_profile_len, + buflen); + } + + if (mld_id != -1 && !params->mld_ap) { + wpa_printf(MSG_DEBUG, + "MLD: No nontransmitted BSSID for MLD ID %d", + mld_id); + goto fail; + } + + return; + +fail: + os_free(params->mld_info); + params->mld_ap = NULL; + params->mld_info = NULL; +} +#endif /* CONFIG_IEEE80211BE */ + + enum ssid_match_result { NO_SSID_MATCH, EXACT_SSID_MATCH, @@ -1415,22 +1550,27 @@ void handle_probe_req(struct hostapd_data *hapd, wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, RX_PROBE_REQUEST "sa=" MACSTR " signal=%d", MAC2STR(mgmt->sa), ssi_signal); + os_memset(¶ms, 0, sizeof(params)); + #ifdef CONFIG_IEEE80211BE if (hapd->conf->mld_ap && elems.probe_req_mle && parse_ml_probe_req((struct ieee80211_eht_ml *) elems.probe_req_mle, elems.probe_req_mle_len, &mld_id, &links)) { - wpa_printf(MSG_DEBUG, - "MLD: Got ML probe request with AP MLD ID %d for links %04x", - mld_id, links); + hostapd_fill_probe_resp_ml_params(hapd, ¶ms, mgmt, + mld_id, links); } #endif /* CONFIG_IEEE80211BE */ - os_memset(¶ms, 0, sizeof(params)); params.req = mgmt; params.is_p2p = !!elems.p2p; params.known_bss = elems.mbssid_known_bss; params.known_bss_len = elems.mbssid_known_bss_len; + params.is_ml_sta_info = false; + hostapd_gen_probe_resp(hapd, ¶ms); + + os_free(params.mld_info); + if (!params.resp) return; @@ -1506,6 +1646,9 @@ static u8 * hostapd_probe_resp_offloads(struct hostapd_data *hapd, params.is_p2p = false; params.known_bss = NULL; params.known_bss_len = 0; + params.is_ml_sta_info = false; + params.mld_ap = NULL; + params.mld_info = NULL; hostapd_gen_probe_resp(hapd, ¶ms); *resp_len = params.resp_len; @@ -1547,6 +1690,9 @@ static u8 * hostapd_unsol_bcast_probe_resp(struct hostapd_data *hapd, probe_params.is_p2p = false; probe_params.known_bss = NULL; probe_params.known_bss_len = 0; + probe_params.is_ml_sta_info = false; + probe_params.mld_ap = NULL; + probe_params.mld_info = NULL; hostapd_gen_probe_resp(hapd, &probe_params); params->unsol_bcast_probe_resp_tmpl_len = probe_params.resp_len; diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h index ada6dc5e8..dce26c398 100644 --- a/src/ap/ieee802_11.h +++ b/src/ap/ieee802_11.h @@ -94,6 +94,9 @@ u8 * hostapd_eid_eht_ml_beacon(struct hostapd_data *hapd, u8 *eid, bool include_mld_id); u8 * hostapd_eid_eht_ml_assoc(struct hostapd_data *hapd, struct sta_info *info, u8 *eid); +size_t hostapd_eid_eht_ml_beacon_len(struct hostapd_data *hapd, + struct mld_info *info, + bool include_mld_id); struct wpabuf * hostapd_ml_auth_resp(struct hostapd_data *hapd); const u8 * hostapd_process_ml_auth(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c index 69ff60dd9..c40c19a02 100644 --- a/src/ap/ieee802_11_eht.c +++ b/src/ap/ieee802_11_eht.c @@ -466,7 +466,8 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd, * BSS Parameters Change Count (1) + EML Capabilities (2) + * MLD Capabilities and Operations (2) */ - common_info_len = 13; +#define EHT_ML_COMMON_INFO_LEN 13 + common_info_len = EHT_ML_COMMON_INFO_LEN; if (include_mld_id) { /* AP MLD ID */ @@ -514,8 +515,9 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd, * beacon interval (2) + TSF offset (8) + DTIM info (2) + BSS * parameters change counter (1) + station profile length. */ - const size_t fixed_len = 22; - size_t total_len = fixed_len + link->resp_sta_profile_len; +#define EHT_ML_STA_INFO_LEN 22 + size_t total_len = EHT_ML_STA_INFO_LEN + + link->resp_sta_profile_len; /* Skip the local one */ if (link_id == hapd->mld_link_id || !link->valid) @@ -549,7 +551,7 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd, /* STA Info */ /* STA Info Length */ - wpabuf_put_u8(buf, fixed_len - 2); + wpabuf_put_u8(buf, EHT_ML_STA_INFO_LEN - 2); wpabuf_put_data(buf, link->local_addr, ETH_ALEN); wpabuf_put_le16(buf, link_bss->iconf->beacon_int); @@ -574,7 +576,7 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd, ptr = link->resp_sta_profile; len = link->resp_sta_profile_len; - slice_len = 255 - fixed_len; + slice_len = 255 - EHT_ML_STA_INFO_LEN; wpabuf_put_data(buf, ptr, slice_len); len -= slice_len; @@ -708,6 +710,47 @@ static u8 * hostapd_eid_eht_reconf_ml(struct hostapd_data *hapd, u8 *eid) } +static size_t hostapd_eid_eht_ml_len(struct mld_info *info, + bool include_mld_id) +{ + size_t len = 0; + size_t eht_ml_len = 2 + EHT_ML_COMMON_INFO_LEN; + u8 link_id; + + if (include_mld_id) + eht_ml_len++; + + for (link_id = 0; info && link_id < ARRAY_SIZE(info->links); + link_id++) { + struct mld_link_info *link; + size_t sta_len = EHT_ML_STA_INFO_LEN; + + link = &info->links[link_id]; + if (!link->valid) + continue; + + sta_len += link->resp_sta_profile_len; + + /* Element data and (fragmentation) headers */ + eht_ml_len += sta_len; + eht_ml_len += 2 + sta_len / 255 * 2; + } + + /* Element data */ + len += eht_ml_len; + + /* First header (254 bytes of data) */ + len += 3; + + /* Fragmentation headers; +1 for shorter first chunk */ + len += (eht_ml_len + 1) / 255 * 2; + + return len; +} +#undef EHT_ML_COMMON_INFO_LEN +#undef EHT_ML_STA_INFO_LEN + + u8 * hostapd_eid_eht_ml_beacon(struct hostapd_data *hapd, struct mld_info *info, u8 *eid, bool include_mld_id) @@ -730,6 +773,14 @@ u8 * hostapd_eid_eht_ml_assoc(struct hostapd_data *hapd, struct sta_info *info, } +size_t hostapd_eid_eht_ml_beacon_len(struct hostapd_data *hapd, + struct mld_info *info, + bool include_mld_id) +{ + return hostapd_eid_eht_ml_len(info, include_mld_id); +} + + struct wpabuf * hostapd_ml_auth_resp(struct hostapd_data *hapd) { struct wpabuf *buf = wpabuf_alloc(12);