aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/common/ieee802_11_defs.h3
-rw-r--r--src/drivers/driver.h13
-rw-r--r--src/drivers/driver_nl80211.c20
-rw-r--r--wpa_supplicant/Makefile4
-rw-r--r--wpa_supplicant/config.c18
-rw-r--r--wpa_supplicant/config_ssid.h54
-rw-r--r--wpa_supplicant/defconfig3
-rw-r--r--wpa_supplicant/sme.c11
-rw-r--r--wpa_supplicant/wpa_supplicant.c189
-rw-r--r--wpa_supplicant/wpa_supplicant_i.h5
10 files changed, 320 insertions, 0 deletions
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 66801fd..25bc5c6 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 f56c41b..8bfb12b 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 0bf1dca..381be66 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 fd1c5d3..5eef855 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 0fd1f3e..89fba40 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 8a47c0b..9fa7f8b 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 cff25d6..69a8625 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 fc28347..c07b7a6 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(&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;
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index 304ae09..4d3e307 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, &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)
{
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 91982e5..b4d8b28 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);