fragattacks/src/ap/ieee802_11_he.c
John Crispin 8f5fc369e2 HE: Fix HE Capabilities element variable length encoding
The HE Capibilities element has dynamic size due to the variable length
and optional fields at the end. Mask out the channel width capabilities
that are less than the configured. Only add the MCS/NSS sets for the
announced channel widths and also add the PPET elements.

Signed-off-by: Shashidhar Lakkavalli <slakkavalli@datto.com>
Signed-off-by: John Crispin <john@phrozen.org>
2019-05-27 16:30:23 +03:00

239 lines
5.9 KiB
C

/*
* hostapd / IEEE 802.11ax HE
* Copyright (c) 2016-2017, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#include "utils/common.h"
#include "common/ieee802_11_defs.h"
#include "hostapd.h"
#include "ap_config.h"
#include "beacon.h"
#include "ieee802_11.h"
#include "dfs.h"
static u8 ieee80211_he_ppet_size(u8 ppe_thres_hdr, const u8 *phy_cap_info)
{
u8 sz = 0, ru;
if ((phy_cap_info[HE_PHYCAP_PPE_THRESHOLD_PRESENT_IDX] &
HE_PHYCAP_PPE_THRESHOLD_PRESENT_IDX) == 0)
return 0;
ru = (ppe_thres_hdr >> HE_PPE_THRES_RU_INDEX_BITMASK_SHIFT) &
HE_PPE_THRES_RU_INDEX_BITMASK_MASK;
while (ru) {
if (ru & 0x1)
sz++;
ru >>= 1;
}
sz *= 1 + (ppe_thres_hdr & HE_PPE_THRES_NSS_MASK);
sz = (sz * 6) + 7;
if (sz % 8)
sz += 8;
sz /= 8;
return sz;
}
u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid)
{
struct ieee80211_he_capabilities *cap;
struct hostapd_hw_modes *mode = hapd->iface->current_mode;
u8 he_oper_chwidth = ~HE_PHYCAP_CHANNEL_WIDTH_MASK;
u8 *pos = eid;
u8 ie_size = 0, mcs_nss_size = 0, ppet_size = 0;
if (!mode)
return eid;
ie_size = sizeof(struct ieee80211_he_capabilities);
ppet_size = ieee80211_he_ppet_size(mode->he_capab.ppet[0],
mode->he_capab.phy_cap);
switch (hapd->iface->conf->he_oper_chwidth) {
case CHANWIDTH_80P80MHZ:
he_oper_chwidth |=
HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G;
mcs_nss_size += 4;
/* fall through */
case CHANWIDTH_160MHZ:
he_oper_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
mcs_nss_size += 4;
/* fall through */
case CHANWIDTH_80MHZ:
case CHANWIDTH_USE_HT:
he_oper_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G;
mcs_nss_size += 4;
break;
}
ie_size += mcs_nss_size + ppet_size;
*pos++ = WLAN_EID_EXTENSION;
*pos++ = 1 + ie_size;
*pos++ = WLAN_EID_EXT_HE_CAPABILITIES;
cap = (struct ieee80211_he_capabilities *) pos;
os_memset(cap, 0, sizeof(*cap));
os_memcpy(cap->he_mac_capab_info, mode->he_capab.mac_cap,
HE_MAX_MAC_CAPAB_SIZE);
os_memcpy(cap->he_phy_capab_info, mode->he_capab.phy_cap,
HE_MAX_PHY_CAPAB_SIZE);
os_memcpy(cap->optional, mode->he_capab.mcs, mcs_nss_size);
if (ppet_size)
os_memcpy(&cap->optional[mcs_nss_size], mode->he_capab.ppet,
ppet_size);
if (hapd->iface->conf->he_phy_capab.he_su_beamformer)
cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] |=
HE_PHYCAP_SU_BEAMFORMER_CAPAB;
else
cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] &=
~HE_PHYCAP_SU_BEAMFORMER_CAPAB;
if (hapd->iface->conf->he_phy_capab.he_su_beamformee)
cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX] |=
HE_PHYCAP_SU_BEAMFORMEE_CAPAB;
else
cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX] &=
~HE_PHYCAP_SU_BEAMFORMEE_CAPAB;
if (hapd->iface->conf->he_phy_capab.he_mu_beamformer)
cap->he_phy_capab_info[HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX] |=
HE_PHYCAP_MU_BEAMFORMER_CAPAB;
else
cap->he_phy_capab_info[HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX] &=
~HE_PHYCAP_MU_BEAMFORMER_CAPAB;
cap->he_phy_capab_info[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &=
he_oper_chwidth;
pos += ie_size;
return pos;
}
u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid)
{
struct ieee80211_he_operation *oper;
u8 *pos = eid;
int oper_size = 6;
u32 params = 0;
if (!hapd->iface->current_mode)
return eid;
*pos++ = WLAN_EID_EXTENSION;
*pos++ = 1 + oper_size;
*pos++ = WLAN_EID_EXT_HE_OPERATION;
oper = (struct ieee80211_he_operation *) pos;
os_memset(oper, 0, sizeof(*oper));
if (hapd->iface->conf->he_op.he_default_pe_duration)
params |= (hapd->iface->conf->he_op.he_default_pe_duration <<
HE_OPERATION_DFLT_PE_DURATION_OFFSET);
if (hapd->iface->conf->he_op.he_twt_required)
params |= HE_OPERATION_TWT_REQUIRED;
if (hapd->iface->conf->he_op.he_rts_threshold)
params |= (hapd->iface->conf->he_op.he_rts_threshold <<
HE_OPERATION_RTS_THRESHOLD_OFFSET);
if (hapd->iface->conf->he_op.he_bss_color)
params |= (hapd->iface->conf->he_op.he_bss_color <<
HE_OPERATION_BSS_COLOR_OFFSET);
/* TODO: conditional MaxBSSID Indicator subfield */
oper->he_oper_params = host_to_le32(params);
pos += oper_size;
return pos;
}
u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid)
{
struct ieee80211_he_mu_edca_parameter_set *edca;
u8 *pos;
size_t i;
pos = (u8 *) &hapd->iface->conf->he_mu_edca;
for (i = 0; i < sizeof(*edca); i++) {
if (pos[i])
break;
}
if (i == sizeof(*edca))
return eid; /* no MU EDCA Parameters configured */
pos = eid;
*pos++ = WLAN_EID_EXTENSION;
*pos++ = 1 + sizeof(*edca);
*pos++ = WLAN_EID_EXT_HE_MU_EDCA_PARAMS;
edca = (struct ieee80211_he_mu_edca_parameter_set *) pos;
os_memcpy(edca, &hapd->iface->conf->he_mu_edca, sizeof(*edca));
wpa_hexdump(MSG_DEBUG, "HE: MU EDCA Parameter Set element",
pos, sizeof(*edca));
pos += sizeof(*edca);
return pos;
}
u8 * hostapd_eid_spatial_reuse(struct hostapd_data *hapd, u8 *eid)
{
struct ieee80211_spatial_reuse *spr;
u8 *pos = eid, *spr_param;
u8 sz = 1;
if (!hapd->iface->conf->spr.sr_control)
return eid;
if (hapd->iface->conf->spr.sr_control &
SPATIAL_REUSE_NON_SRG_OFFSET_PRESENT)
sz++;
if (hapd->iface->conf->spr.sr_control &
SPATIAL_REUSE_SRG_INFORMATION_PRESENT)
sz += 18;
*pos++ = WLAN_EID_EXTENSION;
*pos++ = 1 + sz;
*pos++ = WLAN_EID_EXT_SPATIAL_REUSE;
spr = (struct ieee80211_spatial_reuse *) pos;
os_memset(spr, 0, sizeof(*spr));
spr->sr_ctrl = hapd->iface->conf->spr.sr_control;
pos++;
spr_param = spr->params;
if (spr->sr_ctrl & SPATIAL_REUSE_NON_SRG_OFFSET_PRESENT) {
*spr_param++ =
hapd->iface->conf->spr.non_srg_obss_pd_max_offset;
pos++;
}
if (spr->sr_ctrl & SPATIAL_REUSE_SRG_INFORMATION_PRESENT) {
*spr_param++ = hapd->iface->conf->spr.srg_obss_pd_min_offset;
*spr_param++ = hapd->iface->conf->spr.srg_obss_pd_max_offset;
pos += 18;
}
return pos;
}