From 80e8a5eef15cbca4e92037e6a92d75bdd0317700 Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Sun, 29 Jan 2012 21:01:31 +0200 Subject: [PATCH] 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 --- src/common/ieee802_11_defs.h | 3 + src/drivers/driver.h | 13 ++ src/drivers/driver_nl80211.c | 20 ++++ wpa_supplicant/Makefile | 4 + wpa_supplicant/config.c | 18 +++ wpa_supplicant/config_ssid.h | 54 +++++++++ wpa_supplicant/defconfig | 3 + wpa_supplicant/sme.c | 11 ++ wpa_supplicant/wpa_supplicant.c | 189 ++++++++++++++++++++++++++++++ wpa_supplicant/wpa_supplicant_i.h | 5 + 10 files changed, 320 insertions(+) diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index 66801fd5d..25bc5c6a4 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -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; diff --git a/src/drivers/driver.h b/src/drivers/driver.h index f56c41b91..8bfb12b74 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -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 { diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 0bf1dca8b..381be6656 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -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"); diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index fd1c5d3b8..5eef85560 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -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 diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c index 0fd1f3e0d..89fba408a 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -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 */ } diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h index 8a47c0b9d..9fa7f8bda 100644 --- a/wpa_supplicant/config_ssid.h +++ b/wpa_supplicant/config_ssid.h @@ -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 */ diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig index cff25d6c8..69a86252b 100644 --- a/wpa_supplicant/defconfig +++ b/wpa_supplicant/defconfig @@ -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 diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index fc283477b..c07b7a631 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -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(¶ms, 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, ¶ms); +#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; diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index 304ae098a..4d3e30735 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -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, ¶ms); +#endif /* CONFIG_HT_OVERRIDES */ + ret = wpa_drv_associate(wpa_s, ¶ms); 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) { diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 91982e581..b4d8b280a 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -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);