AP: MLO: Process Multi-Link element from (Re)Association Request frame
Implement processing of the Multi-Link element in the (Re)Association Request frame, including processing of the Per-STA Profile subelement. After handling the basic parsing of the element and extracting the information about the requested links, handle the link specific processing for each link: - Find the interface with the corresponding link ID. - Process the station profile in the interface. - Prepare the Per-STA Profile subelement to be included in the Multi-Link element in the (Re)Association Response frame. Signed-off-by: Ilan Peer <ilan.peer@intel.com> Signed-off-by: Andrei Otcheretianski <andrei.otcheretianski@intel.com>
This commit is contained in:
parent
d924be3bd0
commit
5f5db9366c
4 changed files with 591 additions and 2 deletions
|
@ -83,6 +83,8 @@ static void pasn_fils_auth_resp(struct hostapd_data *hapd,
|
||||||
static void handle_auth(struct hostapd_data *hapd,
|
static void handle_auth(struct hostapd_data *hapd,
|
||||||
const struct ieee80211_mgmt *mgmt, size_t len,
|
const struct ieee80211_mgmt *mgmt, size_t len,
|
||||||
int rssi, int from_queue);
|
int rssi, int from_queue);
|
||||||
|
static int add_associated_sta(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *sta, int reassoc);
|
||||||
|
|
||||||
|
|
||||||
u8 * hostapd_eid_multi_ap(struct hostapd_data *hapd, u8 *eid)
|
u8 * hostapd_eid_multi_ap(struct hostapd_data *hapd, u8 *eid)
|
||||||
|
@ -3788,7 +3790,8 @@ static bool check_sa_query(struct hostapd_data *hapd, struct sta_info *sta,
|
||||||
|
|
||||||
static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
|
static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
|
||||||
const u8 *ies, size_t ies_len,
|
const u8 *ies, size_t ies_len,
|
||||||
struct ieee802_11_elems *elems, int reassoc)
|
struct ieee802_11_elems *elems, int reassoc,
|
||||||
|
bool link)
|
||||||
{
|
{
|
||||||
int resp;
|
int resp;
|
||||||
const u8 *wpa_ie;
|
const u8 *wpa_ie;
|
||||||
|
@ -3890,6 +3893,12 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
|
||||||
elems->eht_capabilities_len);
|
elems->eht_capabilities_len);
|
||||||
if (resp != WLAN_STATUS_SUCCESS)
|
if (resp != WLAN_STATUS_SUCCESS)
|
||||||
return resp;
|
return resp;
|
||||||
|
|
||||||
|
if (!link) {
|
||||||
|
resp = hostapd_process_ml_assoc_req(hapd, elems, sta);
|
||||||
|
if (resp != WLAN_STATUS_SUCCESS)
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_IEEE80211BE */
|
#endif /* CONFIG_IEEE80211BE */
|
||||||
|
|
||||||
|
@ -4249,7 +4258,255 @@ static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
|
||||||
return WLAN_STATUS_UNSPECIFIED_FAILURE;
|
return WLAN_STATUS_UNSPECIFIED_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return __check_assoc_ies(hapd, sta, ies, ies_len, &elems, reassoc);
|
return __check_assoc_ies(hapd, sta, ies, ies_len, &elems, reassoc,
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef CONFIG_IEEE80211BE
|
||||||
|
|
||||||
|
static size_t ieee80211_ml_build_assoc_resp(struct hostapd_data *hapd,
|
||||||
|
u16 status_code,
|
||||||
|
u8 *buf, size_t buflen)
|
||||||
|
{
|
||||||
|
u8 *p = buf;
|
||||||
|
|
||||||
|
/* Capability Info */
|
||||||
|
WPA_PUT_LE16(p, hostapd_own_capab_info(hapd));
|
||||||
|
p += 2;
|
||||||
|
|
||||||
|
/* Status Code */
|
||||||
|
WPA_PUT_LE16(p, status_code);
|
||||||
|
p += 2;
|
||||||
|
|
||||||
|
if (status_code != WLAN_STATUS_SUCCESS)
|
||||||
|
return p - buf;
|
||||||
|
|
||||||
|
/* AID is not included */
|
||||||
|
p = hostapd_eid_supp_rates(hapd, p);
|
||||||
|
p = hostapd_eid_ext_supp_rates(hapd, p);
|
||||||
|
p = hostapd_eid_rm_enabled_capab(hapd, p, buf + buflen - p);
|
||||||
|
p = hostapd_eid_ht_capabilities(hapd, p);
|
||||||
|
p = hostapd_eid_ht_operation(hapd, p);
|
||||||
|
|
||||||
|
if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) {
|
||||||
|
p = hostapd_eid_vht_capabilities(hapd, p, 0);
|
||||||
|
p = hostapd_eid_vht_operation(hapd, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
|
||||||
|
p = hostapd_eid_he_capab(hapd, p, IEEE80211_MODE_AP);
|
||||||
|
p = hostapd_eid_he_operation(hapd, p);
|
||||||
|
p = hostapd_eid_spatial_reuse(hapd, p);
|
||||||
|
p = hostapd_eid_he_mu_edca_parameter_set(hapd, p);
|
||||||
|
p = hostapd_eid_he_6ghz_band_cap(hapd, p);
|
||||||
|
if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
|
||||||
|
p = hostapd_eid_eht_capab(hapd, p, IEEE80211_MODE_AP);
|
||||||
|
p = hostapd_eid_eht_operation(hapd, p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p = hostapd_eid_ext_capab(hapd, p, false);
|
||||||
|
p = hostapd_eid_mbo(hapd, p, buf + buflen - p);
|
||||||
|
p = hostapd_eid_wmm(hapd, p);
|
||||||
|
|
||||||
|
if (hapd->conf->assocresp_elements &&
|
||||||
|
(size_t) (buf + buflen - p) >=
|
||||||
|
wpabuf_len(hapd->conf->assocresp_elements)) {
|
||||||
|
os_memcpy(p, wpabuf_head(hapd->conf->assocresp_elements),
|
||||||
|
wpabuf_len(hapd->conf->assocresp_elements));
|
||||||
|
p += wpabuf_len(hapd->conf->assocresp_elements);
|
||||||
|
}
|
||||||
|
|
||||||
|
return p - buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void ieee80211_ml_process_link(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *origin_sta,
|
||||||
|
struct mld_link_info *link,
|
||||||
|
const u8 *ies, size_t ies_len,
|
||||||
|
bool reassoc)
|
||||||
|
{
|
||||||
|
struct ieee802_11_elems elems;
|
||||||
|
struct wpabuf *mlbuf = NULL;
|
||||||
|
struct sta_info *sta = NULL;
|
||||||
|
u16 status = WLAN_STATUS_SUCCESS;
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "MLD: link: link_id=%u, peer=" MACSTR,
|
||||||
|
hapd->mld_link_id, MAC2STR(link->peer_addr));
|
||||||
|
|
||||||
|
if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) {
|
||||||
|
wpa_printf(MSG_DEBUG, "MLD: link: Element parsing failed");
|
||||||
|
status = WLAN_STATUS_UNSPECIFIED_FAILURE;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
sta = ap_get_sta(hapd, origin_sta->addr);
|
||||||
|
if (sta) {
|
||||||
|
wpa_printf(MSG_INFO, "MLD: link: Station already exists");
|
||||||
|
status = WLAN_STATUS_UNSPECIFIED_FAILURE;
|
||||||
|
sta = NULL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
sta = ap_sta_add(hapd, origin_sta->addr);
|
||||||
|
if (!sta) {
|
||||||
|
wpa_printf(MSG_DEBUG, "MLD: link: ap_sta_add() failed");
|
||||||
|
status = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
mlbuf = ieee802_11_defrag_mle(&elems, MULTI_LINK_CONTROL_TYPE_BASIC);
|
||||||
|
if (!mlbuf)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (ieee802_11_parse_link_assoc_req(ies, ies_len, &elems, mlbuf,
|
||||||
|
hapd->mld_link_id, true) ==
|
||||||
|
ParseFailed) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"MLD: link: Failed to parse association request Multi-Link element");
|
||||||
|
status = WLAN_STATUS_UNSPECIFIED_FAILURE;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
sta->flags |= origin_sta->flags | WLAN_STA_ASSOC_REQ_OK;
|
||||||
|
status = __check_assoc_ies(hapd, sta, NULL, 0, &elems, reassoc, true);
|
||||||
|
if (status != WLAN_STATUS_SUCCESS) {
|
||||||
|
wpa_printf(MSG_DEBUG, "MLD: link: Element check failed");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
sta->mld_info.mld_sta = true;
|
||||||
|
sta->mld_assoc_link_id = origin_sta->mld_assoc_link_id;
|
||||||
|
|
||||||
|
os_memcpy(&sta->mld_info, &origin_sta->mld_info, sizeof(sta->mld_info));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the AID from the station on which the association was performed,
|
||||||
|
* and mark it as used.
|
||||||
|
*/
|
||||||
|
sta->aid = origin_sta->aid;
|
||||||
|
if (sta->aid == 0) {
|
||||||
|
wpa_printf(MSG_DEBUG, "MLD: link: No AID assigned");
|
||||||
|
status = WLAN_STATUS_UNSPECIFIED_FAILURE;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
hapd->sta_aid[(sta->aid - 1) / 32] |= BIT((sta->aid - 1) % 32);
|
||||||
|
sta->listen_interval = origin_sta->listen_interval;
|
||||||
|
update_ht_state(hapd, sta);
|
||||||
|
|
||||||
|
/* RSN Authenticator should always be the one on the original station */
|
||||||
|
wpa_auth_sta_deinit(sta->wpa_sm);
|
||||||
|
sta->wpa_sm = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Do not initialize the EAPOL state machine.
|
||||||
|
* TODO: Maybe it is needed?
|
||||||
|
*/
|
||||||
|
sta->eapol_sm = NULL;
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "MLD: link=%u, association OK (aid=%u)",
|
||||||
|
hapd->mld_link_id, sta->aid);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get RSNE and RSNXE for the current BSS as they are required by the
|
||||||
|
* Authenticator.
|
||||||
|
*/
|
||||||
|
link->rsne = hostapd_wpa_ie(hapd, WLAN_EID_RSN);
|
||||||
|
link->rsnxe = hostapd_wpa_ie(hapd, WLAN_EID_RSNX);
|
||||||
|
|
||||||
|
sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC_REQ_OK;
|
||||||
|
|
||||||
|
/* TODO: What other processing is required? */
|
||||||
|
|
||||||
|
if (add_associated_sta(hapd, sta, reassoc))
|
||||||
|
status = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
|
||||||
|
out:
|
||||||
|
wpabuf_free(mlbuf);
|
||||||
|
link->status = status;
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "MLD: link: status=%u", status);
|
||||||
|
if (sta && status != WLAN_STATUS_SUCCESS)
|
||||||
|
ap_free_sta(hapd, sta);
|
||||||
|
|
||||||
|
link->resp_sta_profile_len =
|
||||||
|
ieee80211_ml_build_assoc_resp(hapd, link->status,
|
||||||
|
link->resp_sta_profile,
|
||||||
|
sizeof(link->resp_sta_profile));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool hostapd_is_mld_ap(struct hostapd_data *hapd)
|
||||||
|
{
|
||||||
|
if (!hapd->conf->mld_ap)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!hapd->iface || !hapd->iface->interfaces ||
|
||||||
|
hapd->iface->interfaces->count <= 1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CONFIG_IEEE80211BE */
|
||||||
|
|
||||||
|
|
||||||
|
static void hostapd_process_assoc_ml_info(struct hostapd_data *hapd,
|
||||||
|
struct sta_info *sta,
|
||||||
|
const u8 *ies, size_t ies_len,
|
||||||
|
bool reassoc)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_IEEE80211BE
|
||||||
|
unsigned int i, j;
|
||||||
|
|
||||||
|
if (!hostapd_is_mld_ap(hapd))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is not really needed, but make the interaction with the RSN
|
||||||
|
* Authenticator more consistent
|
||||||
|
*/
|
||||||
|
sta->mld_info.links[hapd->mld_link_id].rsne =
|
||||||
|
hostapd_wpa_ie(hapd, WLAN_EID_RSN);
|
||||||
|
sta->mld_info.links[hapd->mld_link_id].rsnxe =
|
||||||
|
hostapd_wpa_ie(hapd, WLAN_EID_RSNX);
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
|
||||||
|
struct hostapd_iface *iface = NULL;
|
||||||
|
struct mld_link_info *link = &sta->mld_info.links[i];
|
||||||
|
|
||||||
|
if (!link->valid)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (j = 0; j < hapd->iface->interfaces->count; j++) {
|
||||||
|
iface = hapd->iface->interfaces->iface[j];
|
||||||
|
|
||||||
|
if (hapd->iface == iface)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (iface->bss[0]->conf->mld_ap &&
|
||||||
|
hapd->conf->mld_id == iface->bss[0]->conf->mld_id &&
|
||||||
|
i == iface->bss[0]->mld_link_id)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!iface || j == hapd->iface->interfaces->count) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"MLD: No link match for link_id=%u", i);
|
||||||
|
|
||||||
|
link->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
|
||||||
|
link->resp_sta_profile_len =
|
||||||
|
ieee80211_ml_build_assoc_resp(
|
||||||
|
hapd, link->status,
|
||||||
|
link->resp_sta_profile,
|
||||||
|
sizeof(link->resp_sta_profile));
|
||||||
|
} else {
|
||||||
|
ieee80211_ml_process_link(iface->bss[0], sta, link,
|
||||||
|
ies, ies_len, reassoc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_IEEE80211BE */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -5175,6 +5432,9 @@ static void handle_assoc(struct hostapd_data *hapd,
|
||||||
* issues with processing other non-Data Class 3 frames during this
|
* issues with processing other non-Data Class 3 frames during this
|
||||||
* window.
|
* window.
|
||||||
*/
|
*/
|
||||||
|
if (resp == WLAN_STATUS_SUCCESS)
|
||||||
|
hostapd_process_assoc_ml_info(hapd, sta, pos, left, reassoc);
|
||||||
|
|
||||||
if (resp == WLAN_STATUS_SUCCESS && sta &&
|
if (resp == WLAN_STATUS_SUCCESS && sta &&
|
||||||
add_associated_sta(hapd, sta, reassoc))
|
add_associated_sta(hapd, sta, reassoc))
|
||||||
resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
|
resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
|
||||||
|
|
|
@ -19,6 +19,7 @@ struct ieee80211_mgmt;
|
||||||
struct radius_sta;
|
struct radius_sta;
|
||||||
enum ieee80211_op_mode;
|
enum ieee80211_op_mode;
|
||||||
enum oper_chan_width;
|
enum oper_chan_width;
|
||||||
|
struct ieee802_11_elems;
|
||||||
|
|
||||||
int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
|
int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
|
||||||
struct hostapd_frame_info *fi);
|
struct hostapd_frame_info *fi);
|
||||||
|
@ -90,6 +91,9 @@ struct wpabuf * hostapd_ml_auth_resp(struct hostapd_data *hapd);
|
||||||
const u8 * hostapd_process_ml_auth(struct hostapd_data *hapd,
|
const u8 * hostapd_process_ml_auth(struct hostapd_data *hapd,
|
||||||
const struct ieee80211_mgmt *mgmt,
|
const struct ieee80211_mgmt *mgmt,
|
||||||
size_t len);
|
size_t len);
|
||||||
|
u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd,
|
||||||
|
struct ieee802_11_elems *elems,
|
||||||
|
struct sta_info *sta);
|
||||||
int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta);
|
int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta);
|
||||||
u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
|
u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
|
||||||
const u8 *ht_capab);
|
const u8 *ht_capab);
|
||||||
|
|
|
@ -827,3 +827,313 @@ const u8 * hostapd_process_ml_auth(struct hostapd_data *hapd,
|
||||||
|
|
||||||
return get_basic_mle_mld_addr(elems.basic_mle, elems.basic_mle_len);
|
return get_basic_mle_mld_addr(elems.basic_mle, elems.basic_mle_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int hostapd_mld_validate_assoc_info(struct hostapd_data *hapd,
|
||||||
|
struct mld_info *info)
|
||||||
|
{
|
||||||
|
u8 i, link_id;
|
||||||
|
|
||||||
|
if (!info->mld_sta) {
|
||||||
|
wpa_printf(MSG_DEBUG, "MLD: Not a non-AP MLD");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Iterate over the links negotiated in the (Re)Association Request
|
||||||
|
* frame and validate that they are indeed valid links in the local AP
|
||||||
|
* MLD.
|
||||||
|
*
|
||||||
|
* While at it, also update the local address for the links in the
|
||||||
|
* mld_info, so it could be easily available for later flows, e.g., for
|
||||||
|
* the RSN Authenticator, etc.
|
||||||
|
*/
|
||||||
|
for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
|
||||||
|
struct hostapd_data *other_hapd;
|
||||||
|
|
||||||
|
if (!info->links[link_id].valid)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (i = 0; i < hapd->iface->interfaces->count; i++) {
|
||||||
|
other_hapd = hapd->iface->interfaces->iface[i]->bss[0];
|
||||||
|
|
||||||
|
if (hapd == other_hapd)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (other_hapd->conf->mld_ap &&
|
||||||
|
other_hapd->conf->mld_id == hapd->conf->mld_id &&
|
||||||
|
link_id == other_hapd->mld_link_id)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == hapd->iface->interfaces->count &&
|
||||||
|
link_id != hapd->mld_link_id) {
|
||||||
|
wpa_printf(MSG_DEBUG, "MLD: Invalid link ID=%u",
|
||||||
|
link_id);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < hapd->iface->interfaces->count)
|
||||||
|
os_memcpy(info->links[link_id].local_addr,
|
||||||
|
other_hapd->own_addr,
|
||||||
|
ETH_ALEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd,
|
||||||
|
struct ieee802_11_elems *elems,
|
||||||
|
struct sta_info *sta)
|
||||||
|
{
|
||||||
|
struct wpabuf *mlbuf;
|
||||||
|
const struct ieee80211_eht_ml *ml;
|
||||||
|
const struct eht_ml_basic_common_info *common_info;
|
||||||
|
size_t ml_len, common_info_len;
|
||||||
|
struct mld_link_info *link_info;
|
||||||
|
struct mld_info *info = &sta->mld_info;
|
||||||
|
const u8 *pos;
|
||||||
|
int ret = -1;
|
||||||
|
u16 ml_control;
|
||||||
|
|
||||||
|
mlbuf = ieee802_11_defrag_mle(elems, MULTI_LINK_CONTROL_TYPE_BASIC);
|
||||||
|
if (!mlbuf)
|
||||||
|
return WLAN_STATUS_SUCCESS;
|
||||||
|
|
||||||
|
ml = wpabuf_head(mlbuf);
|
||||||
|
ml_len = wpabuf_len(mlbuf);
|
||||||
|
|
||||||
|
ml_control = le_to_host16(ml->ml_control);
|
||||||
|
if ((ml_control & MULTI_LINK_CONTROL_TYPE_MASK) !=
|
||||||
|
MULTI_LINK_CONTROL_TYPE_BASIC) {
|
||||||
|
wpa_printf(MSG_DEBUG, "MLD: Invalid ML type=%u",
|
||||||
|
ml_control & MULTI_LINK_CONTROL_TYPE_MASK);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Common Info length and MLD MAC address must always be present */
|
||||||
|
common_info_len = 1 + ETH_ALEN;
|
||||||
|
|
||||||
|
if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_LINK_ID) {
|
||||||
|
wpa_printf(MSG_DEBUG, "MLD: Link ID info not expected");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT) {
|
||||||
|
wpa_printf(MSG_DEBUG, "MLD: BSS params change not expected");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_MSD_INFO) {
|
||||||
|
wpa_printf(MSG_DEBUG, "MLD: Sync delay not expected");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA) {
|
||||||
|
common_info_len += 2;
|
||||||
|
} else {
|
||||||
|
wpa_printf(MSG_DEBUG, "MLD: EML capabilities not present");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA) {
|
||||||
|
common_info_len += 2;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
wpa_printf(MSG_DEBUG, "MLD: MLD capabilities not present");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "MLD: expected_common_info_len=%lu",
|
||||||
|
common_info_len);
|
||||||
|
|
||||||
|
if (sizeof(*ml) + common_info_len > ml_len) {
|
||||||
|
wpa_printf(MSG_DEBUG, "MLD: Not enough bytes for common info");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
common_info = (const struct eht_ml_basic_common_info *) ml->variable;
|
||||||
|
|
||||||
|
/* Common information length includes the length octet */
|
||||||
|
if (common_info->len != common_info_len) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"MLD: Invalid common info len=%u (expected %zu)",
|
||||||
|
common_info->len, common_info_len);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
pos = common_info->variable;
|
||||||
|
|
||||||
|
if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA) {
|
||||||
|
info->common_info.eml_capa = WPA_GET_LE16(pos);
|
||||||
|
pos += 2;
|
||||||
|
} else {
|
||||||
|
info->common_info.eml_capa = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
info->common_info.mld_capa = WPA_GET_LE16(pos);
|
||||||
|
pos += 2;
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "MLD: addr=" MACSTR ", eml=0x%x, mld=0x%x",
|
||||||
|
MAC2STR(info->common_info.mld_addr),
|
||||||
|
info->common_info.eml_capa, info->common_info.mld_capa);
|
||||||
|
|
||||||
|
/* Check the MLD MAC Address */
|
||||||
|
if (os_memcmp(info->common_info.mld_addr, common_info->mld_addr,
|
||||||
|
ETH_ALEN) != 0) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"MLD: MLD address mismatch between authentication ("
|
||||||
|
MACSTR ") and association (" MACSTR ")",
|
||||||
|
MAC2STR(info->common_info.mld_addr),
|
||||||
|
MAC2STR(common_info->mld_addr));
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
info->links[hapd->mld_link_id].valid = true;
|
||||||
|
|
||||||
|
/* Parse the link info field */
|
||||||
|
ml_len -= sizeof(*ml) + common_info_len;
|
||||||
|
|
||||||
|
while (ml_len > 2) {
|
||||||
|
size_t sub_elem_len = *(pos + 1);
|
||||||
|
size_t sta_info_len;
|
||||||
|
u16 control;
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "MLD: sub element len=%zu",
|
||||||
|
sub_elem_len);
|
||||||
|
|
||||||
|
if (2 + sub_elem_len > ml_len) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"MLD: Invalid link info len: %zu %zu",
|
||||||
|
2 + sub_elem_len, ml_len);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*pos == MULTI_LINK_SUB_ELEM_ID_VENDOR) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"MLD: Skip vendor specific subelement");
|
||||||
|
|
||||||
|
pos += 2 + sub_elem_len;
|
||||||
|
ml_len -= 2 + sub_elem_len;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*pos != MULTI_LINK_SUB_ELEM_ID_PER_STA_PROFILE) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"MLD: Unexpected Multi-Link element subelement ID=%u",
|
||||||
|
*pos);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Skip the subelement ID and the length */
|
||||||
|
pos += 2;
|
||||||
|
ml_len -= 2;
|
||||||
|
|
||||||
|
/* Get the station control field */
|
||||||
|
if (sub_elem_len < 2) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"MLD: Too short Per-STA Profile subelement");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
control = WPA_GET_LE16(pos);
|
||||||
|
link_info = &info->links[control &
|
||||||
|
EHT_PER_STA_CTRL_LINK_ID_MSK];
|
||||||
|
pos += 2;
|
||||||
|
ml_len -= 2;
|
||||||
|
sub_elem_len -= 2;
|
||||||
|
|
||||||
|
if (!(control & EHT_PER_STA_CTRL_COMPLETE_PROFILE_MSK)) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"MLD: Per-STA complete profile expected");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(control & EHT_PER_STA_CTRL_MAC_ADDR_PRESENT_MSK)) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"MLD: Per-STA MAC address not present");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((control & (EHT_PER_STA_CTRL_BEACON_INTERVAL_PRESENT_MSK |
|
||||||
|
EHT_PER_STA_CTRL_DTIM_INFO_PRESENT_MSK))) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"MLD: Beacon/DTIM interval not expected");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The length octet and the MAC address must be present */
|
||||||
|
sta_info_len = 1 + ETH_ALEN;
|
||||||
|
|
||||||
|
if (control & EHT_PER_STA_CTRL_NSTR_LINK_PAIR_PRESENT_MSK) {
|
||||||
|
if (control & EHT_PER_STA_CTRL_NSTR_BM_SIZE_MSK)
|
||||||
|
link_info->nstr_bitmap_len = 2;
|
||||||
|
else
|
||||||
|
link_info->nstr_bitmap_len = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sta_info_len += link_info->nstr_bitmap_len;
|
||||||
|
|
||||||
|
if (sta_info_len > ml_len || sta_info_len != *pos ||
|
||||||
|
sta_info_len > sub_elem_len) {
|
||||||
|
wpa_printf(MSG_DEBUG, "MLD: Invalid STA Info length");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* skip the length */
|
||||||
|
pos++;
|
||||||
|
ml_len--;
|
||||||
|
|
||||||
|
/* get the link address */
|
||||||
|
os_memcpy(link_info->peer_addr, pos, ETH_ALEN);
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"MLD: assoc: link id=%u, addr=" MACSTR,
|
||||||
|
control & EHT_PER_STA_CTRL_LINK_ID_MSK,
|
||||||
|
MAC2STR(link_info->peer_addr));
|
||||||
|
|
||||||
|
pos += ETH_ALEN;
|
||||||
|
ml_len -= ETH_ALEN;
|
||||||
|
|
||||||
|
/* Get the NSTR bitmap */
|
||||||
|
if (link_info->nstr_bitmap_len) {
|
||||||
|
os_memcpy(link_info->nstr_bitmap, pos,
|
||||||
|
link_info->nstr_bitmap_len);
|
||||||
|
pos += link_info->nstr_bitmap_len;
|
||||||
|
ml_len -= link_info->nstr_bitmap_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub_elem_len -= sta_info_len;
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "MLD: STA Profile len=%zu", sub_elem_len);
|
||||||
|
if (sub_elem_len > ml_len)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (sub_elem_len > 2)
|
||||||
|
link_info->capability = WPA_GET_LE16(pos);
|
||||||
|
|
||||||
|
pos += sub_elem_len;
|
||||||
|
ml_len -= sub_elem_len;
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "MLD: link ctrl=0x%x, " MACSTR
|
||||||
|
", nstr bitmap len=%lu",
|
||||||
|
control, MAC2STR(link_info->peer_addr),
|
||||||
|
link_info->nstr_bitmap_len);
|
||||||
|
|
||||||
|
link_info->valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ml_len) {
|
||||||
|
wpa_printf(MSG_DEBUG, "MLD: %zu bytes left after parsing. fail",
|
||||||
|
ml_len);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = hostapd_mld_validate_assoc_info(hapd, info);
|
||||||
|
out:
|
||||||
|
wpabuf_free(mlbuf);
|
||||||
|
if (ret) {
|
||||||
|
os_memset(info, 0, sizeof(*info));
|
||||||
|
return WLAN_STATUS_UNSPECIFIED_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return WLAN_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
|
@ -301,7 +301,15 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
|
||||||
sae_clear_retransmit_timer(hapd, sta);
|
sae_clear_retransmit_timer(hapd, sta);
|
||||||
|
|
||||||
ieee802_1x_free_station(hapd, sta);
|
ieee802_1x_free_station(hapd, sta);
|
||||||
|
|
||||||
|
#ifdef CONFIG_IEEE80211BE
|
||||||
|
if (!hapd->conf->mld_ap || !sta->mld_info.mld_sta ||
|
||||||
|
hapd->mld_link_id == sta->mld_assoc_link_id)
|
||||||
|
wpa_auth_sta_deinit(sta->wpa_sm);
|
||||||
|
#else
|
||||||
wpa_auth_sta_deinit(sta->wpa_sm);
|
wpa_auth_sta_deinit(sta->wpa_sm);
|
||||||
|
#endif /* CONFIG_IEEE80211BE */
|
||||||
|
|
||||||
rsn_preauth_free_station(hapd, sta);
|
rsn_preauth_free_station(hapd, sta);
|
||||||
#ifndef CONFIG_NO_RADIUS
|
#ifndef CONFIG_NO_RADIUS
|
||||||
if (hapd->radius)
|
if (hapd->radius)
|
||||||
|
@ -866,7 +874,14 @@ void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta,
|
||||||
ap_handle_timer, hapd, sta);
|
ap_handle_timer, hapd, sta);
|
||||||
accounting_sta_stop(hapd, sta);
|
accounting_sta_stop(hapd, sta);
|
||||||
ieee802_1x_free_station(hapd, sta);
|
ieee802_1x_free_station(hapd, sta);
|
||||||
|
#ifdef CONFIG_IEEE80211BE
|
||||||
|
if (!hapd->conf->mld_ap ||
|
||||||
|
hapd->mld_link_id == sta->mld_assoc_link_id)
|
||||||
|
wpa_auth_sta_deinit(sta->wpa_sm);
|
||||||
|
#else
|
||||||
wpa_auth_sta_deinit(sta->wpa_sm);
|
wpa_auth_sta_deinit(sta->wpa_sm);
|
||||||
|
#endif /* CONFIG_IEEE80211BE */
|
||||||
|
|
||||||
sta->wpa_sm = NULL;
|
sta->wpa_sm = NULL;
|
||||||
|
|
||||||
sta->disassoc_reason = reason;
|
sta->disassoc_reason = reason;
|
||||||
|
|
Loading…
Reference in a new issue