From 78d35b16de46dfe4f5ae75f7290dfb224a3fad1f Mon Sep 17 00:00:00 2001 From: John Crispin Date: Mon, 20 May 2019 09:55:05 +0200 Subject: [PATCH] HE: Add AP mode MLME/SME handling for HE stations Process HE information in (Re)Association Request frames and add HE elements into (Re)Association Response frames when HE is enabled in the BSS. Signed-off-by: Shashidhar Lakkavalli Signed-off-by: John Crispin --- src/ap/ap_drv_ops.c | 4 ++ src/ap/ap_drv_ops.h | 2 + src/ap/ieee802_11.c | 29 +++++++++- src/ap/ieee802_11.h | 6 ++ src/ap/ieee802_11_he.c | 103 +++++++++++++++++++++++++++++++++++ src/ap/sta_info.c | 1 + src/ap/sta_info.h | 3 + src/common/ieee802_11_defs.h | 2 + 8 files changed, 149 insertions(+), 1 deletion(-) diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c index 9d7759d46..8bdacf6ff 100644 --- a/src/ap/ap_drv_ops.c +++ b/src/ap/ap_drv_ops.c @@ -413,6 +413,8 @@ int hostapd_sta_add(struct hostapd_data *hapd, u16 listen_interval, const struct ieee80211_ht_capabilities *ht_capab, const struct ieee80211_vht_capabilities *vht_capab, + const struct ieee80211_he_capabilities *he_capab, + size_t he_capab_len, u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps, int set) { @@ -432,6 +434,8 @@ int hostapd_sta_add(struct hostapd_data *hapd, params.listen_interval = listen_interval; params.ht_capabilities = ht_capab; params.vht_capabilities = vht_capab; + params.he_capab = he_capab; + params.he_capab_len = he_capab_len; params.vht_opmode_enabled = !!(flags & WLAN_STA_VHT_OPMODE_ENABLED); params.vht_opmode = vht_opmode; params.flags = hostapd_sta_flags_to_drv(flags); diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h index 8758ec104..4357ceed3 100644 --- a/src/ap/ap_drv_ops.h +++ b/src/ap/ap_drv_ops.h @@ -41,6 +41,8 @@ int hostapd_sta_add(struct hostapd_data *hapd, u16 listen_interval, const struct ieee80211_ht_capabilities *ht_capab, const struct ieee80211_vht_capabilities *vht_capab, + const struct ieee80211_he_capabilities *he_capab, + size_t he_capab_len, u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps, int set); int hostapd_set_privacy(struct hostapd_data *hapd, int enabled); diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 890da7aa9..f05033655 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -2325,7 +2325,8 @@ static void handle_auth(struct hostapd_data *hapd, WLAN_STA_AUTHORIZED); if (hostapd_sta_add(hapd, sta->addr, 0, 0, NULL, 0, 0, - NULL, NULL, sta->flags, 0, 0, 0, 0)) { + NULL, NULL, NULL, 0, + sta->flags, 0, 0, 0, 0)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE, @@ -2866,6 +2867,14 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, return resp; } #endif /* CONFIG_IEEE80211AC */ +#ifdef CONFIG_IEEE80211AX + if (hapd->iconf->ieee80211ax) { + resp = copy_sta_he_capab(hapd, sta, elems.he_capabilities, + elems.he_capabilities_len); + if (resp != WLAN_STATUS_SUCCESS) + return resp; + } +#endif /* CONFIG_IEEE80211AX */ #ifdef CONFIG_P2P if (elems.p2p) { @@ -3228,6 +3237,7 @@ static int add_associated_sta(struct hostapd_data *hapd, { struct ieee80211_ht_capabilities ht_cap; struct ieee80211_vht_capabilities vht_cap; + struct ieee80211_he_capabilities he_cap; int set = 1; /* @@ -3280,6 +3290,12 @@ static int add_associated_sta(struct hostapd_data *hapd, if (sta->flags & WLAN_STA_VHT) hostapd_get_vht_capab(hapd, sta->vht_capabilities, &vht_cap); #endif /* CONFIG_IEEE80211AC */ +#ifdef CONFIG_IEEE80211AX + if (sta->flags & WLAN_STA_HE) { + hostapd_get_he_capab(hapd, sta->he_capab, &he_cap, + sta->he_capab_len); + } +#endif /* CONFIG_IEEE80211AX */ /* * Add the station with forced WLAN_STA_ASSOC flag. The sta->flags @@ -3291,6 +3307,8 @@ static int add_associated_sta(struct hostapd_data *hapd, sta->listen_interval, sta->flags & WLAN_STA_HT ? &ht_cap : NULL, sta->flags & WLAN_STA_VHT ? &vht_cap : NULL, + sta->flags & WLAN_STA_HE ? &he_cap : NULL, + sta->flags & WLAN_STA_HE ? sta->he_capab_len : 0, sta->flags | WLAN_STA_ASSOC, sta->qosinfo, sta->vht_opmode, sta->p2p_ie ? 1 : 0, set)) { @@ -3442,6 +3460,15 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, } #endif /* CONFIG_IEEE80211AC */ +#ifdef CONFIG_IEEE80211AX + if (hapd->iconf->ieee80211ax) { + p = hostapd_eid_he_capab(hapd, p); + p = hostapd_eid_he_operation(hapd, p); + p = hostapd_eid_spatial_reuse(hapd, p); + p = hostapd_eid_he_mu_edca_parameter_set(hapd, p); + } +#endif /* CONFIG_IEEE80211AX */ + p = hostapd_eid_ext_capab(hapd, p); p = hostapd_eid_bss_max_idle_period(hapd, p); if (sta && sta->qos_map_enabled) diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h index 3699246b3..914cd1f19 100644 --- a/src/ap/ieee802_11.h +++ b/src/ap/ieee802_11.h @@ -71,6 +71,10 @@ void hostapd_get_ht_capab(struct hostapd_data *hapd, void hostapd_get_vht_capab(struct hostapd_data *hapd, struct ieee80211_vht_capabilities *vht_cap, struct ieee80211_vht_capabilities *neg_vht_cap); +void hostapd_get_he_capab(struct hostapd_data *hapd, + const struct ieee80211_he_capabilities *he_cap, + struct ieee80211_he_capabilities *neg_he_cap, + size_t he_capab_len); 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, const u8 *ht_capab); @@ -86,6 +90,8 @@ u16 copy_sta_vht_oper(struct hostapd_data *hapd, struct sta_info *sta, const u8 *vht_oper); u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta, const u8 *vht_opmode); +u16 copy_sta_he_capab(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *he_capab, size_t he_capab_len); void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr, const u8 *buf, size_t len, int ack); void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst, diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c index c22427a7e..066a032c0 100644 --- a/src/ap/ieee802_11_he.c +++ b/src/ap/ieee802_11_he.c @@ -1,6 +1,7 @@ /* * hostapd / IEEE 802.11ax HE * Copyright (c) 2016-2017, Qualcomm Atheros, Inc. + * Copyright (c) 2019 John Crispin * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -13,6 +14,7 @@ #include "hostapd.h" #include "ap_config.h" #include "beacon.h" +#include "sta_info.h" #include "ieee802_11.h" #include "dfs.h" @@ -236,3 +238,104 @@ u8 * hostapd_eid_spatial_reuse(struct hostapd_data *hapd, u8 *eid) return pos; } + + +void hostapd_get_he_capab(struct hostapd_data *hapd, + const struct ieee80211_he_capabilities *he_cap, + struct ieee80211_he_capabilities *neg_he_cap, + size_t he_capab_len) +{ + if (!he_cap) + return; + + if (he_capab_len > sizeof(*neg_he_cap)) + he_capab_len = sizeof(*neg_he_cap); + /* TODO: mask out unsupported features */ + + os_memcpy(neg_he_cap, he_cap, he_capab_len); +} + + +static int check_valid_he_mcs(struct hostapd_data *hapd, const u8 *sta_he_capab) +{ + u16 sta_rx_mcs_set, ap_tx_mcs_set; + u8 mcs_count = 0; + const u16 *ap_mcs_set, *sta_mcs_set; + int i; + + if (!hapd->iface->current_mode) + return 1; + ap_mcs_set = (u16 *) hapd->iface->current_mode->he_capab.mcs; + sta_mcs_set = (u16 *) ((const struct ieee80211_he_capabilities *) + sta_he_capab)->optional; + + /* + * Disable HE capabilities for STAs for which there is not even a single + * allowed MCS in any supported number of streams, i.e., STA is + * advertising 3 (not supported) as HE MCS rates for all supported + * band/stream cases. + */ + switch (hapd->iface->conf->he_oper_chwidth) { + case CHANWIDTH_80P80MHZ: + mcs_count = 3; + break; + case CHANWIDTH_160MHZ: + mcs_count = 2; + break; + default: + mcs_count = 1; + break; + } + + for (i = 0; i < mcs_count; i++) { + int j; + + /* AP Tx MCS map vs. STA Rx MCS map */ + sta_rx_mcs_set = WPA_GET_LE16((const u8 *) &sta_mcs_set[i * 2]); + ap_tx_mcs_set = WPA_GET_LE16((const u8 *) + &ap_mcs_set[(i * 2) + 1]); + + for (j = 0; j < HE_NSS_MAX_STREAMS; j++) { + if (((ap_tx_mcs_set >> (j * 2)) & 0x3) == 3) + continue; + + if (((sta_rx_mcs_set >> (j * 2)) & 0x3) == 3) + continue; + + return 1; + } + } + + wpa_printf(MSG_DEBUG, + "No matching HE MCS found between AP TX and STA RX"); + + return 0; +} + + +u16 copy_sta_he_capab(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *he_capab, size_t he_capab_len) +{ + if (!he_capab || !hapd->iconf->ieee80211ax || + !check_valid_he_mcs(hapd, he_capab) || + he_capab_len > sizeof(struct ieee80211_he_capabilities)) { + sta->flags &= ~WLAN_STA_HE; + os_free(sta->he_capab); + sta->he_capab = NULL; + return WLAN_STATUS_SUCCESS; + } + + if (!sta->he_capab) { + sta->he_capab = + os_zalloc(sizeof(struct ieee80211_he_capabilities)); + if (!sta->he_capab) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + sta->flags |= WLAN_STA_HE; + os_memset(sta->he_capab, 0, sizeof(struct ieee80211_he_capabilities)); + os_memcpy(sta->he_capab, he_capab, he_capab_len); + sta->he_capab_len = he_capab_len; + + return WLAN_STATUS_SUCCESS; +} diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c index 4f9eae847..98b609c18 100644 --- a/src/ap/sta_info.c +++ b/src/ap/sta_info.c @@ -330,6 +330,7 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) os_free(sta->ht_capabilities); os_free(sta->vht_capabilities); os_free(sta->vht_operation); + os_free(sta->he_capab); hostapd_free_psk_list(sta->psk); os_free(sta->identity); os_free(sta->radius_cui); diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h index c185d7a0a..9a081cb23 100644 --- a/src/ap/sta_info.h +++ b/src/ap/sta_info.h @@ -37,6 +37,7 @@ #define WLAN_STA_VENDOR_VHT BIT(21) #define WLAN_STA_PENDING_FILS_ERP BIT(22) #define WLAN_STA_MULTI_AP BIT(23) +#define WLAN_STA_HE BIT(24) #define WLAN_STA_PENDING_DISASSOC_CB BIT(29) #define WLAN_STA_PENDING_DEAUTH_CB BIT(30) #define WLAN_STA_NONERP BIT(31) @@ -166,6 +167,8 @@ struct sta_info { struct ieee80211_vht_capabilities *vht_capabilities; struct ieee80211_vht_operation *vht_operation; u8 vht_opmode; + struct ieee80211_he_capabilities *he_capab; + size_t he_capab_len; #ifdef CONFIG_IEEE80211W int sa_query_count; /* number of pending SA Query requests; diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index dbe832c2e..e6ffc1030 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -1280,6 +1280,8 @@ struct ieee80211_ampe_ie { #define CHANWIDTH_160MHZ 2 #define CHANWIDTH_80P80MHZ 3 +#define HE_NSS_MAX_STREAMS 8 + #define OUI_MICROSOFT 0x0050f2 /* Microsoft (also used in Wi-Fi specs) * 00:50:F2 */ #define WPA_IE_VENDOR_TYPE 0x0050f201