diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c index fce0922cd..612802b11 100644 --- a/src/ap/drv_callbacks.c +++ b/src/ap/drv_callbacks.c @@ -132,8 +132,122 @@ static bool check_sa_query_need(struct hostapd_data *hapd, struct sta_info *sta) } +#ifdef CONFIG_IEEE80211BE +static int hostapd_update_sta_links_status(struct hostapd_data *hapd, + struct sta_info *sta, + const u8 *resp_ies, + size_t resp_ies_len) +{ + struct mld_info *info = &sta->mld_info; + struct wpabuf *mlebuf; + const u8 *mle, *pos; + struct ieee802_11_elems elems; + size_t mle_len, rem_len; + int ret = 0; + + if (!resp_ies) { + wpa_printf(MSG_DEBUG, + "MLO: (Re)Association Response frame elements not available"); + return -1; + } + + if (ieee802_11_parse_elems(resp_ies, resp_ies_len, &elems, 0) == + ParseFailed) { + wpa_printf(MSG_DEBUG, + "MLO: Failed to parse (Re)Association Response frame elements"); + return -1; + } + + mlebuf = ieee802_11_defrag_mle(&elems, MULTI_LINK_CONTROL_TYPE_BASIC); + if (!mlebuf) { + wpa_printf(MSG_ERROR, + "MLO: Basic Multi-Link element not found in (Re)Association Response frame"); + return -1; + } + + mle = wpabuf_head(mlebuf); + mle_len = wpabuf_len(mlebuf); + if (mle_len < MULTI_LINK_CONTROL_LEN + 1 || + mle_len - MULTI_LINK_CONTROL_LEN < mle[MULTI_LINK_CONTROL_LEN]) { + wpa_printf(MSG_ERROR, + "MLO: Invalid Multi-Link element in (Re)Association Response frame"); + ret = -1; + goto out; + } + + /* Skip Common Info */ + pos = mle + MULTI_LINK_CONTROL_LEN + mle[MULTI_LINK_CONTROL_LEN]; + rem_len = mle_len - + (MULTI_LINK_CONTROL_LEN + mle[MULTI_LINK_CONTROL_LEN]); + + /* Parse Subelements */ + while (rem_len > 2) { + size_t ie_len = 2 + pos[1]; + + if (rem_len < ie_len) + break; + + if (pos[0] == MULTI_LINK_SUB_ELEM_ID_PER_STA_PROFILE) { + u8 link_id; + const u8 *sta_profile; + size_t sta_profile_len; + u16 sta_ctrl; + + if (pos[1] < BASIC_MLE_STA_CTRL_LEN + 1) { + wpa_printf(MSG_DEBUG, + "MLO: Invalid per-STA profile IE"); + goto next_subelem; + } + + sta_profile_len = pos[1]; + sta_profile = &pos[2]; + sta_ctrl = WPA_GET_LE16(sta_profile); + link_id = sta_ctrl & BASIC_MLE_STA_CTRL_LINK_ID_MASK; + if (link_id >= MAX_NUM_MLD_LINKS) { + wpa_printf(MSG_DEBUG, + "MLO: Invalid link ID in per-STA profile IE"); + goto next_subelem; + } + + /* Skip STA Control and STA Info */ + if (sta_profile_len - BASIC_MLE_STA_CTRL_LEN < + sta_profile[BASIC_MLE_STA_CTRL_LEN]) { + wpa_printf(MSG_DEBUG, + "MLO: Invalid STA info in per-STA profile IE"); + goto next_subelem; + } + + sta_profile_len = sta_profile_len - + (BASIC_MLE_STA_CTRL_LEN + + sta_profile[BASIC_MLE_STA_CTRL_LEN]); + sta_profile = sta_profile + BASIC_MLE_STA_CTRL_LEN + + sta_profile[BASIC_MLE_STA_CTRL_LEN]; + + /* Skip Capabilities Information field */ + if (sta_profile_len < 2) + goto next_subelem; + sta_profile_len -= 2; + sta_profile += 2; + + /* Get status of the link */ + info->links[link_id].status = WPA_GET_LE16(sta_profile); + } +next_subelem: + pos += ie_len; + rem_len -= ie_len; + } + +out: + wpabuf_free(mlebuf); + return ret; +} +#endif /* CONFIG_IEEE80211BE */ + + int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, - const u8 *req_ies, size_t req_ies_len, int reassoc) + const u8 *req_ies, size_t req_ies_len, + const u8 *resp_ies, size_t resp_ies_len, + const u8 *link_addr, int reassoc) { struct sta_info *sta; int new_assoc; @@ -226,6 +340,45 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, return -1; } } + +#ifdef CONFIG_IEEE80211BE + if (link_addr) { + struct mld_info *info = &sta->mld_info; + int i, num_valid_links = 0; + u8 link_id = hapd->mld_link_id; + + info->mld_sta = true; + sta->mld_assoc_link_id = link_id; + os_memcpy(info->common_info.mld_addr, addr, ETH_ALEN); + info->links[link_id].valid = true; + os_memcpy(info->links[link_id].peer_addr, link_addr, ETH_ALEN); + os_memcpy(info->links[link_id].local_addr, hapd->own_addr, + ETH_ALEN); + + if (!elems.basic_mle || + hostapd_process_ml_assoc_req(hapd, &elems, sta) != + WLAN_STATUS_SUCCESS) { + reason = WLAN_REASON_UNSPECIFIED; + wpa_printf(MSG_DEBUG, + "Failed to get STA non-assoc links info"); + goto fail; + } + + for (i = 0 ; i < MAX_NUM_MLD_LINKS; i++) { + if (info->links[i].valid) + num_valid_links++; + } + if (num_valid_links > 1 && + hostapd_update_sta_links_status(hapd, sta, resp_ies, + resp_ies_len)) { + wpa_printf(MSG_DEBUG, + "Failed to get STA non-assoc links status info"); + reason = WLAN_REASON_UNSPECIFIED; + goto fail; + } + } +#endif /* CONFIG_IEEE80211BE */ + sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2); /* @@ -365,6 +518,15 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, "Failed to initialize WPA state machine"); return -1; } +#ifdef CONFIG_IEEE80211BE + if (sta->mld_info.mld_sta) { + wpa_printf(MSG_DEBUG, + "MLD: Set ML info in RSN Authenticator"); + wpa_auth_set_ml_info(sta->wpa_sm, hapd->mld_addr, + sta->mld_assoc_link_id, + &sta->mld_info); + } +#endif /* CONFIG_IEEE80211BE */ res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, hapd->iface->freq, ie, ielen, @@ -2120,9 +2282,23 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, case EVENT_ASSOC: if (!data) return; +#ifdef CONFIG_IEEE80211BE + if (data->assoc_info.assoc_link_id != -1) { + hapd = hostapd_mld_get_link_bss( + hapd, data->assoc_info.assoc_link_id); + if (!hapd) { + wpa_printf(MSG_ERROR, + "MLD: Failed to get link BSS for EVENT_ASSOC"); + return; + } + } +#endif /* CONFIG_IEEE80211BE */ hostapd_notif_assoc(hapd, data->assoc_info.addr, data->assoc_info.req_ies, data->assoc_info.req_ies_len, + data->assoc_info.resp_ies, + data->assoc_info.resp_ies_len, + data->assoc_info.link_addr, data->assoc_info.reassoc); break; #ifdef CONFIG_OWE diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index 6e0ce3dea..2994b2788 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -730,7 +730,8 @@ void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr, void hostapd_notify_assoc_fils_finish(struct hostapd_data *hapd, struct sta_info *sta); int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, - const u8 *ie, size_t ielen, int reassoc); + const u8 *req_ie, size_t req_ielen, const u8 *resp_ie, + size_t resp_ielen, const u8 *link_addr, int reassoc); void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr); void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr); void hostapd_event_connect_failed_reason(struct hostapd_data *hapd, diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index 571ace2f5..381613d14 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -2607,6 +2607,7 @@ struct ieee80211_eht_capabilities { * STA Control field definitions of Per-STA Profile subelement in Basic * Multi-Link element as described in Figure 9-1002n: STA Control field format. */ +#define BASIC_MLE_STA_CTRL_LEN 2 #define BASIC_MLE_STA_CTRL_LINK_ID_MASK 0x000F #define BASIC_MLE_STA_CTRL_COMPLETE_PROFILE 0x0010 #define BASIC_MLE_STA_CTRL_PRES_STA_MAC 0x0020 diff --git a/src/drivers/driver.h b/src/drivers/driver.h index ece5f1259..5957567a9 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -5953,6 +5953,17 @@ union wpa_event_data { * fils_pmkid - PMKID used or generated in FILS authentication */ const u8 *fils_pmkid; + + /** + * link_addr - Link address of the STA + */ + const u8 *link_addr; + + /** + * assoc_link_id - Association link id of the MLO association or + * -1 if MLO is not used + */ + int assoc_link_id; } assoc_info; /** @@ -6674,15 +6685,21 @@ void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event, * event indication for some of the common events. */ -static inline void drv_event_assoc(void *ctx, const u8 *addr, const u8 *ie, - size_t ielen, int reassoc) +static inline void drv_event_assoc(void *ctx, const u8 *addr, const u8 *req_ies, + size_t req_ielen, const u8 *resp_ies, + size_t resp_ielen, const u8 *link_addr, + int assoc_link_id, int reassoc) { union wpa_event_data event; os_memset(&event, 0, sizeof(event)); event.assoc_info.reassoc = reassoc; - event.assoc_info.req_ies = ie; - event.assoc_info.req_ies_len = ielen; + event.assoc_info.req_ies = req_ies; + event.assoc_info.req_ies_len = req_ielen; + event.assoc_info.resp_ies = resp_ies; + event.assoc_info.resp_ies_len = resp_ielen; event.assoc_info.addr = addr; + event.assoc_info.link_addr = link_addr; + event.assoc_info.assoc_link_id = assoc_link_id; wpa_supplicant_event(ctx, EVENT_ASSOC, &event); } diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c index 08ff915ac..59f65b810 100644 --- a/src/drivers/driver_atheros.c +++ b/src/drivers/driver_atheros.c @@ -913,14 +913,16 @@ static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf, break; ielen = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req)); iebuf = mgmt->u.assoc_req.variable; - drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, 0); + drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, NULL, 0, + NULL, -1, 0); break; case WLAN_FC_STYPE_REASSOC_REQ: if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req)) break; ielen = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req)); iebuf = mgmt->u.reassoc_req.variable; - drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, 1); + drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, NULL, 0, + NULL, -1, 1); break; case WLAN_FC_STYPE_AUTH: if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) @@ -1222,7 +1224,7 @@ atheros_new_sta(struct atheros_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) ielen += 2; no_ie: - drv_event_assoc(hapd, addr, iebuf, ielen, 0); + drv_event_assoc(hapd, addr, iebuf, ielen, NULL, 0, NULL, -1, 0); if (os_memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) { /* Cached accounting data is not valid anymore. */ diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c index c32d05e65..850637f0d 100644 --- a/src/drivers/driver_bsd.c +++ b/src/drivers/driver_bsd.c @@ -553,7 +553,7 @@ bsd_new_sta(void *priv, void *ctx, u8 addr[IEEE80211_ADDR_LEN]) ielen += 2; no_ie: - drv_event_assoc(ctx, addr, iebuf, ielen, 0); + drv_event_assoc(ctx, addr, iebuf, ielen, NULL, 0, NULL, -1, 0); } static int diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c index d6e150539..03b395bca 100644 --- a/src/drivers/driver_nl80211_event.c +++ b/src/drivers/driver_nl80211_event.c @@ -2165,23 +2165,60 @@ static void nl80211_new_station_event(struct wpa_driver_nl80211_data *drv, struct i802_bss *bss, struct nlattr **tb) { - u8 *addr; + u8 *peer_addr; union wpa_event_data data; if (tb[NL80211_ATTR_MAC] == NULL) return; - addr = nla_data(tb[NL80211_ATTR_MAC]); - wpa_printf(MSG_DEBUG, "nl80211: New station " MACSTR, MAC2STR(addr)); + peer_addr = nla_data(tb[NL80211_ATTR_MAC]); + wpa_printf(MSG_DEBUG, "nl80211: New station " MACSTR, + MAC2STR(peer_addr)); if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) { - u8 *ies = NULL; - size_t ies_len = 0; - if (tb[NL80211_ATTR_IE]) { - ies = nla_data(tb[NL80211_ATTR_IE]); - ies_len = nla_len(tb[NL80211_ATTR_IE]); + u8 *link_addr = NULL; + int assoc_link_id = -1; + u8 *req_ies = NULL, *resp_ies = NULL; + size_t req_ies_len = 0, resp_ies_len = 0; + + if (bss->links[0].link_id == NL80211_DRV_LINK_ID_NA && + (tb[NL80211_ATTR_MLO_LINK_ID] || + tb[NL80211_ATTR_MLD_ADDR])) { + wpa_printf(MSG_ERROR, + "nl80211: MLO info not expected for new station event for non-MLD AP"); + return; } - wpa_hexdump(MSG_DEBUG, "nl80211: Assoc Req IEs", ies, ies_len); - drv_event_assoc(bss->ctx, addr, ies, ies_len, 0); + + if (tb[NL80211_ATTR_MLO_LINK_ID]) { + assoc_link_id = + nla_get_u8(tb[NL80211_ATTR_MLO_LINK_ID]); + wpa_printf(MSG_DEBUG, "nl80211: STA assoc link ID %d", + assoc_link_id); + if (tb[NL80211_ATTR_MLD_ADDR]) { + peer_addr = nla_data(tb[NL80211_ATTR_MLD_ADDR]); + link_addr = nla_data(tb[NL80211_ATTR_MAC]); + wpa_printf(MSG_DEBUG, + "nl80211: STA MLD address " MACSTR, + MAC2STR(peer_addr)); + } + } + + if (tb[NL80211_ATTR_IE]) { + req_ies = nla_data(tb[NL80211_ATTR_IE]); + req_ies_len = nla_len(tb[NL80211_ATTR_IE]); + wpa_hexdump(MSG_DEBUG, "nl80211: Assoc Req IEs", + req_ies, req_ies_len); + } + + if (tb[NL80211_ATTR_RESP_IE]) { + resp_ies = nla_data(tb[NL80211_ATTR_RESP_IE]); + resp_ies_len = nla_len(tb[NL80211_ATTR_RESP_IE]); + wpa_hexdump(MSG_DEBUG, "nl80211: Assoc Resp IEs", + resp_ies, resp_ies_len); + } + + drv_event_assoc(bss->ctx, peer_addr, req_ies, req_ies_len, + resp_ies, resp_ies_len, link_addr, + assoc_link_id, 0); return; } @@ -2189,7 +2226,7 @@ static void nl80211_new_station_event(struct wpa_driver_nl80211_data *drv, return; os_memset(&data, 0, sizeof(data)); - os_memcpy(data.ibss_rsn_start.peer, addr, ETH_ALEN); + os_memcpy(data.ibss_rsn_start.peer, peer_addr, ETH_ALEN); wpa_supplicant_event(bss->ctx, EVENT_IBSS_RSN_START, &data); } diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index 9db6c14a3..41e25f077 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -3583,8 +3583,8 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, hostapd_notif_assoc(wpa_s->ap_iface->bss[0], data->assoc_info.addr, data->assoc_info.req_ies, - data->assoc_info.req_ies_len, - data->assoc_info.reassoc); + data->assoc_info.req_ies_len, NULL, 0, + NULL, data->assoc_info.reassoc); return; } #endif /* CONFIG_AP */