Support HT capability overrides

This allows HT capabilities overrides on kernels that
support these features.

MCS Rates can be disabled to force to slower speeds when using HT.
Rates cannot be forced higher.

HT can be disabled, forcing an 802.11a/b/g/n station to act like
an 802.11a/b/g station.

HT40 can be disabled.

MAX A-MSDU can be disabled.
A-MPDU Factor and A-MPDU Density can be modified.

Please note that these are suggestions to the kernel. Only mac80211
drivers will work at all. The A-MPDU Factor can only be decreased and
the A-MPDU Density can only be increased currently.

Signed-hostap: Ben Greear <greearb@candelatech.com>
master
Ben Greear 13 years ago committed by Jouni Malinen
parent 499e7286b0
commit 80e8a5eef1

@ -519,6 +519,9 @@ struct ieee80211_mgmt {
} STRUCT_PACKED;
/* Rx MCS bitmask is in the first 77 bits of supported_mcs_set */
#define IEEE80211_HT_MCS_MASK_LEN 10
struct ieee80211_ht_capabilities {
le16 ht_capabilities_info;
u8 a_mpdu_params;

@ -523,6 +523,19 @@ struct wpa_driver_associate_params {
* 0 = Do not fix BSSID.
*/
int fixed_bssid;
/**
* disable_ht - Disable HT (IEEE 802.11n) for this connection
*/
int disable_ht;
/**
* HT Capabilities over-rides. Only bits set in the mask will be used,
* and not all values are used by the kernel anyway. Currently, MCS,
* MPDU and MSDU fields are used.
*/
const u8 *htcaps; /* struct ieee80211_ht_capabilities * */
const u8 *htcaps_mask; /* struct ieee80211_ht_capabilities * */
};
enum hide_ssid {

@ -6648,6 +6648,16 @@ skip_auth_type:
NLA_PUT_U32(msg, NL80211_ATTR_AKM_SUITES, mgmt);
}
if (params->disable_ht)
NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_HT);
if (params->htcaps && params->htcaps_mask) {
int sz = sizeof(struct ieee80211_ht_capabilities);
NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY, sz, params->htcaps);
NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sz,
params->htcaps_mask);
}
ret = nl80211_set_conn_keys(params, msg);
if (ret)
goto nla_put_failure;
@ -6795,6 +6805,16 @@ static int wpa_driver_nl80211_associate(
params->prev_bssid);
}
if (params->disable_ht)
NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_HT);
if (params->htcaps && params->htcaps_mask) {
int sz = sizeof(struct ieee80211_ht_capabilities);
NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY, sz, params->htcaps);
NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sz,
params->htcaps_mask);
}
if (params->p2p)
wpa_printf(MSG_DEBUG, " * P2P group");

@ -113,6 +113,10 @@ ifdef CONFIG_EAPOL_TEST
CFLAGS += -Werror -DEAPOL_TEST
endif
ifdef CONFIG_HT_OVERRIDES
CFLAGS += -DCONFIG_HT_OVERRIDES
endif
ifndef CONFIG_BACKEND
CONFIG_BACKEND=file
endif

@ -1598,6 +1598,14 @@ static const struct parse_data ssid_fields[] = {
#ifdef CONFIG_P2P
{ FUNC(p2p_client_list) },
#endif /* CONFIG_P2P */
#ifdef CONFIG_HT_OVERRIDES
{ INT_RANGE(disable_ht, 0, 1) },
{ INT_RANGE(disable_ht40, -1, 1) },
{ INT_RANGE(disable_max_amsdu, -1, 1) },
{ INT_RANGE(ampdu_factor, -1, 3) },
{ INT_RANGE(ampdu_density, -1, 7) },
{ STR(ht_mcs) },
#endif /* CONFIG_HT_OVERRIDES */
};
#undef OFFSET
@ -1765,6 +1773,9 @@ void wpa_config_free_ssid(struct wpa_ssid *ssid)
os_free(ssid->freq_list);
os_free(ssid->bgscan);
os_free(ssid->p2p_client_list);
#ifdef CONFIG_HT_OVERRIDES
os_free(ssid->ht_mcs);
#endif /* CONFIG_HT_OVERRIDES */
os_free(ssid);
}
@ -1952,6 +1963,13 @@ void wpa_config_set_network_defaults(struct wpa_ssid *ssid)
ssid->eap_workaround = DEFAULT_EAP_WORKAROUND;
ssid->eap.fragment_size = DEFAULT_FRAGMENT_SIZE;
#endif /* IEEE8021X_EAPOL */
#ifdef CONFIG_HT_OVERRIDES
ssid->disable_ht = DEFAULT_DISABLE_HT;
ssid->disable_ht40 = DEFAULT_DISABLE_HT40;
ssid->disable_max_amsdu = DEFAULT_DISABLE_MAX_AMSDU;
ssid->ampdu_factor = DEFAULT_AMPDU_FACTOR;
ssid->ampdu_density = DEFAULT_AMPDU_DENSITY;
#endif /* CONFIG_HT_OVERRIDES */
}

@ -31,6 +31,12 @@
WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40)
#define DEFAULT_FRAGMENT_SIZE 1398
#define DEFAULT_DISABLE_HT 0
#define DEFAULT_DISABLE_HT40 0
#define DEFAULT_DISABLE_MAX_AMSDU -1 /* no change */
#define DEFAULT_AMPDU_FACTOR -1 /* no change */
#define DEFAULT_AMPDU_DENSITY -1 /* no change */
/**
* struct wpa_ssid - Network configuration data
*
@ -422,6 +428,54 @@ struct wpa_ssid {
* WPS or similar so that they may be exported.
*/
int export_keys;
#ifdef CONFIG_HT_OVERRIDES
/**
* disable_ht - Disable HT (IEEE 802.11n) for this network
*
* By default, use it if it is available, but this can be configured
* to 1 to have it disabled.
*/
int disable_ht;
/**
* disable_ht40 - Disable HT40 for this network
*
* By default, use it if it is available, but this can be configured
* to 1 to have it disabled.
*/
int disable_ht40;
/**
* disable_max_amsdu - Disable MAX A-MSDU
*
* A-MDSU will be 3839 bytes when disabled, or 7935
* when enabled (assuming it is otherwise supported)
* -1 (default) means do not apply any settings to the kernel.
*/
int disable_max_amsdu;
/**
* ampdu_factor - Maximum A-MPDU Length Exponent
*
* Value: 0-3, see 7.3.2.56.3 in IEEE Std 802.11n-2009.
*/
int ampdu_factor;
/**
* ampdu_density - Minimum A-MPDU Start Spacing
*
* Value: 0-7, see 7.3.2.56.3 in IEEE Std 802.11n-2009.
*/
int ampdu_density;
/**
* ht_mcs - Allowed HT-MCS rates, in ASCII hex: ffff0000...
*
* By default (empty string): Use whatever the OS has configured.
*/
char *ht_mcs;
#endif /* CONFIG_HT_OVERRIDES */
};
#endif /* CONFIG_SSID_H */

@ -217,6 +217,9 @@ CONFIG_SMARTCARD=y
# Enable this if EAP-SIM or EAP-AKA is included
#CONFIG_PCSC=y
# Support HT overrides (disable HT/HT40, mask MCS rates, etc.)
#CONFIG_HT_OVERRIDES=y
# Development testing
#CONFIG_EAPOL_TEST=y

@ -367,6 +367,10 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
{
struct wpa_driver_associate_params params;
struct ieee802_11_elems elems;
#ifdef CONFIG_HT_OVERRIDES
struct ieee80211_ht_capabilities htcaps;
struct ieee80211_ht_capabilities htcaps_mask;
#endif /* CONFIG_HT_OVERRIDES */
os_memset(&params, 0, sizeof(params));
params.bssid = bssid;
@ -378,6 +382,13 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
params.wpa_ie_len = wpa_s->sme.assoc_req_ie_len;
params.pairwise_suite = cipher_suite2driver(wpa_s->pairwise_cipher);
params.group_suite = cipher_suite2driver(wpa_s->group_cipher);
#ifdef CONFIG_HT_OVERRIDES
os_memset(&htcaps, 0, sizeof(htcaps));
os_memset(&htcaps_mask, 0, sizeof(htcaps_mask));
params.htcaps = (u8 *) &htcaps;
params.htcaps_mask = (u8 *) &htcaps_mask;
wpa_supplicant_apply_ht_overrides(wpa_s, wpa_s->current_ssid, &params);
#endif /* CONFIG_HT_OVERRIDES */
#ifdef CONFIG_IEEE80211R
if (auth_type == WLAN_AUTH_FT && wpa_s->sme.ft_ies) {
params.wpa_ie = wpa_s->sme.ft_ies;

@ -1110,6 +1110,10 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
struct wpa_driver_capa capa;
int assoc_failed = 0;
struct wpa_ssid *old_ssid;
#ifdef CONFIG_HT_OVERRIDES
struct ieee80211_ht_capabilities htcaps;
struct ieee80211_ht_capabilities htcaps_mask;
#endif /* CONFIG_HT_OVERRIDES */
#ifdef CONFIG_IBSS_RSN
ibss_rsn_deinit(wpa_s->ibss_rsn);
@ -1411,6 +1415,14 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
else
params.uapsd = -1;
#ifdef CONFIG_HT_OVERRIDES
os_memset(&htcaps, 0, sizeof(htcaps));
os_memset(&htcaps_mask, 0, sizeof(htcaps_mask));
params.htcaps = (u8 *) &htcaps;
params.htcaps_mask = (u8 *) &htcaps_mask;
wpa_supplicant_apply_ht_overrides(wpa_s, ssid, &params);
#endif /* CONFIG_HT_OVERRIDES */
ret = wpa_drv_associate(wpa_s, &params);
if (ret < 0) {
wpa_msg(wpa_s, MSG_INFO, "Association request to the driver "
@ -2165,6 +2177,183 @@ static struct wpa_supplicant * wpa_supplicant_alloc(void)
}
#ifdef CONFIG_HT_OVERRIDES
static int wpa_set_htcap_mcs(struct wpa_supplicant *wpa_s,
struct ieee80211_ht_capabilities *htcaps,
struct ieee80211_ht_capabilities *htcaps_mask,
const char *ht_mcs)
{
/* parse ht_mcs into hex array */
int i;
const char *tmp = ht_mcs;
char *end = NULL;
/* If ht_mcs is null, do not set anything */
if (!ht_mcs)
return 0;
/* This is what we are setting in the kernel */
os_memset(&htcaps->supported_mcs_set, 0, IEEE80211_HT_MCS_MASK_LEN);
wpa_msg(wpa_s, MSG_DEBUG, "set_htcap, ht_mcs -:%s:-", ht_mcs);
for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) {
errno = 0;
long v = strtol(tmp, &end, 16);
if (errno == 0) {
wpa_msg(wpa_s, MSG_DEBUG,
"htcap value[%i]: %ld end: %p tmp: %p",
i, v, end, tmp);
if (end == tmp)
break;
htcaps->supported_mcs_set[i] = v;
tmp = end;
} else {
wpa_msg(wpa_s, MSG_ERROR,
"Failed to parse ht-mcs: %s, error: %s\n",
ht_mcs, strerror(errno));
return -1;
}
}
/*
* If we were able to parse any values, then set mask for the MCS set.
*/
if (i) {
os_memset(&htcaps_mask->supported_mcs_set, 0xff,
IEEE80211_HT_MCS_MASK_LEN - 1);
/* skip the 3 reserved bits */
htcaps_mask->supported_mcs_set[IEEE80211_HT_MCS_MASK_LEN - 1] =
0x1f;
}
return 0;
}
static int wpa_disable_max_amsdu(struct wpa_supplicant *wpa_s,
struct ieee80211_ht_capabilities *htcaps,
struct ieee80211_ht_capabilities *htcaps_mask,
int disabled)
{
u16 msk;
wpa_msg(wpa_s, MSG_DEBUG, "set_disable_max_amsdu: %d", disabled);
if (disabled == -1)
return 0;
msk = host_to_le16(HT_CAP_INFO_MAX_AMSDU_SIZE);
htcaps_mask->ht_capabilities_info |= msk;
if (disabled)
htcaps->ht_capabilities_info &= msk;
else
htcaps->ht_capabilities_info |= msk;
return 0;
}
static int wpa_set_ampdu_factor(struct wpa_supplicant *wpa_s,
struct ieee80211_ht_capabilities *htcaps,
struct ieee80211_ht_capabilities *htcaps_mask,
int factor)
{
wpa_msg(wpa_s, MSG_DEBUG, "set_ampdu_factor: %d", factor);
if (factor == -1)
return 0;
if (factor < 0 || factor > 3) {
wpa_msg(wpa_s, MSG_ERROR, "ampdu_factor: %d out of range. "
"Must be 0-3 or -1", factor);
return -EINVAL;
}
htcaps_mask->a_mpdu_params |= 0x3; /* 2 bits for factor */
htcaps->a_mpdu_params &= ~0x3;
htcaps->a_mpdu_params |= factor & 0x3;
return 0;
}
static int wpa_set_ampdu_density(struct wpa_supplicant *wpa_s,
struct ieee80211_ht_capabilities *htcaps,
struct ieee80211_ht_capabilities *htcaps_mask,
int density)
{
wpa_msg(wpa_s, MSG_DEBUG, "set_ampdu_density: %d", density);
if (density == -1)
return 0;
if (density < 0 || density > 7) {
wpa_msg(wpa_s, MSG_ERROR,
"ampdu_density: %d out of range. Must be 0-7 or -1.",
density);
return -EINVAL;
}
htcaps_mask->a_mpdu_params |= 0x1C;
htcaps->a_mpdu_params &= ~(0x1C);
htcaps->a_mpdu_params |= (density << 2) & 0x1C;
return 0;
}
static int wpa_set_disable_ht40(struct wpa_supplicant *wpa_s,
struct ieee80211_ht_capabilities *htcaps,
struct ieee80211_ht_capabilities *htcaps_mask,
int disabled)
{
/* Masking these out disables HT40 */
u16 msk = host_to_le16(HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET |
HT_CAP_INFO_SHORT_GI40MHZ);
wpa_msg(wpa_s, MSG_DEBUG, "set_disable_ht40: %d", disabled);
if (disabled)
htcaps->ht_capabilities_info &= ~msk;
else
htcaps->ht_capabilities_info |= msk;
htcaps_mask->ht_capabilities_info |= msk;
return 0;
}
void wpa_supplicant_apply_ht_overrides(
struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
struct wpa_driver_associate_params *params)
{
struct ieee80211_ht_capabilities *htcaps;
struct ieee80211_ht_capabilities *htcaps_mask;
if (!ssid)
return;
params->disable_ht = ssid->disable_ht;
if (!params->htcaps || !params->htcaps_mask)
return;
htcaps = (struct ieee80211_ht_capabilities *) params->htcaps;
htcaps_mask = (struct ieee80211_ht_capabilities *) params->htcaps_mask;
wpa_set_htcap_mcs(wpa_s, htcaps, htcaps_mask, ssid->ht_mcs);
wpa_disable_max_amsdu(wpa_s, htcaps, htcaps_mask,
ssid->disable_max_amsdu);
wpa_set_ampdu_factor(wpa_s, htcaps, htcaps_mask, ssid->ampdu_factor);
wpa_set_ampdu_density(wpa_s, htcaps, htcaps_mask, ssid->ampdu_density);
wpa_set_disable_ht40(wpa_s, htcaps, htcaps_mask, ssid->disable_ht40);
}
#endif /* CONFIG_HT_OVERRIDES */
static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
struct wpa_interface *iface)
{

@ -36,6 +36,7 @@ struct scan_info;
struct wpa_bss;
struct wpa_scan_results;
struct hostapd_hw_modes;
struct wpa_driver_associate_params;
/*
* Forward declarations of private structures used within the ctrl_iface
@ -528,6 +529,10 @@ struct wpa_supplicant {
/* wpa_supplicant.c */
void wpa_supplicant_apply_ht_overrides(
struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
struct wpa_driver_associate_params *params);
int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s);

Loading…
Cancel
Save