EHT: Process (Re)Association Request frame capabilities

Parse EHT capabilities sent by a non-AP STA in (Re)Association Request
frames. Validate the length of the element, matching MCS rates between
AP TX and STA RX. Store the capabilities in the station info structure.

Signed-off-by: Aloka Dixit <quic_alokad@quicinc.com>
Signed-off-by: Pradeep Kumar Chitrapu <quic_pradeepc@quicinc.com>
This commit is contained in:
Aloka Dixit 2022-04-19 11:04:12 -07:00 committed by Jouni Malinen
parent 340c0e2122
commit a6d1b4c46c
5 changed files with 139 additions and 0 deletions

View file

@ -4554,6 +4554,17 @@ static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
}
}
#endif /* CONFIG_IEEE80211AX */
#ifdef CONFIG_IEEE80211BE
if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
resp = copy_sta_eht_capab(hapd, sta, IEEE80211_MODE_AP,
elems.he_capabilities,
elems.he_capabilities_len,
elems.eht_capabilities,
elems.eht_capabilities_len);
if (resp != WLAN_STATUS_SUCCESS)
return resp;
}
#endif /* CONFIG_IEEE80211BE */
#ifdef CONFIG_P2P
if (elems.p2p) {

View file

@ -206,5 +206,9 @@ size_t hostapd_eid_eht_capab_len(struct hostapd_data *hapd,
u8 * hostapd_eid_eht_capab(struct hostapd_data *hapd, u8 *eid,
enum ieee80211_op_mode opmode);
u8 * hostapd_eid_eht_operation(struct hostapd_data *hapd, u8 *eid);
u16 copy_sta_eht_capab(struct hostapd_data *hapd, struct sta_info *sta,
enum ieee80211_op_mode opmode,
const u8 *he_capab, size_t he_capab_len,
const u8 *eht_capab, size_t eht_capab_len);
#endif /* IEEE802_11_H */

View file

@ -9,6 +9,7 @@
#include "utils/includes.h"
#include "utils/common.h"
#include "hostapd.h"
#include "sta_info.h"
#include "ieee802_11.h"
@ -216,3 +217,122 @@ u8 * hostapd_eid_eht_operation(struct hostapd_data *hapd, u8 *eid)
return pos + 4;
}
static bool check_valid_eht_mcs_nss(struct hostapd_data *hapd, const u8 *ap_mcs,
const u8 *sta_mcs, u8 mcs_count, u8 map_len)
{
unsigned int i, j;
for (i = 0; i < mcs_count; i++) {
ap_mcs += i * 3;
sta_mcs += i * 3;
for (j = 0; j < map_len; j++) {
if (((ap_mcs[j] >> 4) & 0xFF) == 0)
continue;
if ((sta_mcs[j] & 0xFF) == 0)
continue;
return true;
}
}
wpa_printf(MSG_DEBUG,
"No matching EHT MCS found between AP TX and STA RX");
return false;
}
static bool check_valid_eht_mcs(struct hostapd_data *hapd,
const u8 *sta_eht_capab,
enum ieee80211_op_mode opmode)
{
struct hostapd_hw_modes *mode;
const struct ieee80211_eht_capabilities *capab;
const u8 *ap_mcs, *sta_mcs;
u8 mcs_count = 1;
mode = hapd->iface->current_mode;
if (!mode)
return true;
ap_mcs = mode->eht_capab[opmode].mcs;
capab = (const struct ieee80211_eht_capabilities *) sta_eht_capab;
sta_mcs = capab->optional;
if (ieee80211_eht_mcs_set_size(mode->he_capab[opmode].phy_cap,
mode->eht_capab[opmode].phy_cap) ==
EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY)
return check_valid_eht_mcs_nss(
hapd, ap_mcs, sta_mcs, 1,
EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY);
switch (hapd->iface->conf->eht_oper_chwidth) {
/* TODO: CHANWIDTH_320MHZ */
case CHANWIDTH_80P80MHZ:
case CHANWIDTH_160MHZ:
mcs_count = 2;
break;
}
return check_valid_eht_mcs_nss(hapd, ap_mcs, sta_mcs, mcs_count,
EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS);
}
static bool ieee80211_invalid_eht_cap_size(const u8 *he_cap, const u8 *eht_cap,
size_t len)
{
const struct ieee80211_he_capabilities *he_capab;
struct ieee80211_eht_capabilities *cap;
const u8 *he_phy_cap;
size_t cap_len;
he_capab = (const struct ieee80211_he_capabilities *) he_cap;
he_phy_cap = he_capab->he_phy_capab_info;
cap = (struct ieee80211_eht_capabilities *) eht_cap;
cap_len = sizeof(*cap);
if (len < cap_len)
return true;
cap_len += ieee80211_eht_mcs_set_size(he_phy_cap, cap->phy_cap);
if (len < cap_len)
return true;
cap_len += ieee80211_eht_ppet_size(&eht_cap[cap_len], cap->phy_cap);
return len < cap_len;
}
u16 copy_sta_eht_capab(struct hostapd_data *hapd, struct sta_info *sta,
enum ieee80211_op_mode opmode,
const u8 *he_capab, size_t he_capab_len,
const u8 *eht_capab, size_t eht_capab_len)
{
if (!hapd->iconf->ieee80211be || hapd->conf->disable_11be ||
!he_capab || he_capab_len < IEEE80211_HE_CAPAB_MIN_LEN ||
!eht_capab ||
ieee80211_invalid_eht_cap_size(he_capab, eht_capab,
eht_capab_len) ||
!check_valid_eht_mcs(hapd, eht_capab, opmode)) {
sta->flags &= ~WLAN_STA_EHT;
os_free(sta->eht_capab);
sta->eht_capab = NULL;
return WLAN_STATUS_SUCCESS;
}
os_free(sta->eht_capab);
sta->eht_capab = os_memdup(eht_capab, eht_capab_len);
if (!sta->eht_capab) {
sta->eht_capab_len = 0;
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
sta->flags |= WLAN_STA_EHT;
sta->eht_capab_len = eht_capab_len;
return WLAN_STATUS_SUCCESS;
}

View file

@ -358,6 +358,7 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
os_free(sta->vht_operation);
os_free(sta->he_capab);
os_free(sta->he_6ghz_capab);
os_free(sta->eht_capab);
hostapd_free_psk_list(sta->psk);
os_free(sta->identity);
os_free(sta->radius_cui);

View file

@ -42,6 +42,7 @@
#define WLAN_STA_HE BIT(24)
#define WLAN_STA_6GHZ BIT(25)
#define WLAN_STA_PENDING_PASN_FILS_ERP BIT(26)
#define WLAN_STA_EHT BIT(27)
#define WLAN_STA_PENDING_DISASSOC_CB BIT(29)
#define WLAN_STA_PENDING_DEAUTH_CB BIT(30)
#define WLAN_STA_NONERP BIT(31)
@ -213,6 +214,8 @@ struct sta_info {
struct ieee80211_he_capabilities *he_capab;
size_t he_capab_len;
struct ieee80211_he_6ghz_band_cap *he_6ghz_capab;
struct ieee80211_eht_capabilities *eht_capab;
size_t eht_capab_len;
int sa_query_count; /* number of pending SA Query requests;
* 0 = no SA Query in progress */