@@ -568,6 +568,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;
@@ -629,20 +634,18 @@ 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);
@@ -660,10 +663,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);
@@ -676,13 +682,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);
@@ -698,13 +709,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);
@@ -748,7 +764,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);
@@ -775,8 +792,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);
}
@@ -882,6 +905,117 @@ 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;
+ u8 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, 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 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,
@@ -1330,13 +1464,14 @@ 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 */
@@ -1344,7 +1479,12 @@ void handle_probe_req(struct hostapd_data *hapd,
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;
@@ -1419,6 +1559,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;
@@ -1453,6 +1596,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;
@@ -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,
@@ -455,7 +455,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 */
@@ -503,8 +504,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)
@@ -535,10 +537,8 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
EHT_PER_STA_CTRL_BSS_PARAM_CNT_PRESENT_MSK;
wpabuf_put_le16(buf, control);
- /* 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);
@@ -563,7 +563,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;
@@ -700,6 +700,48 @@ static u8 *hostapd_eid_eht_reconf_ml(struct hostapd_data *hapd,
}
+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 += 1;
+
+ for (link_id = 0;
+ info && link_id < ARRAY_SIZE(info->links);
+ link_id++) {
+ struct mld_link_info *link;
+ int 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)
@@ -721,6 +763,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);