Commit b08fbbd8 authored by Johannes Berg's avatar Johannes Berg

mac80211: restrict assoc request VHT capabilities

In interoperability testing some APs showed bad behaviour
if some of the VHT capabilities of the station are better
than their own. Restrict the assoc request parameters
 - beamformee capabable,
 - RX STBC and
 - RX MCS set
to the subset that the AP can support.
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 0f500a5f
...@@ -405,6 +405,8 @@ struct ieee80211_mgd_assoc_data { ...@@ -405,6 +405,8 @@ struct ieee80211_mgd_assoc_data {
u8 ap_ht_param; u8 ap_ht_param;
struct ieee80211_vht_cap ap_vht_cap;
size_t ie_len; size_t ie_len;
u8 ie[]; u8 ie[];
}; };
......
...@@ -341,11 +341,13 @@ static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata, ...@@ -341,11 +341,13 @@ static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata,
static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata, static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, struct sk_buff *skb,
struct ieee80211_supported_band *sband) struct ieee80211_supported_band *sband,
struct ieee80211_vht_cap *ap_vht_cap)
{ {
u8 *pos; u8 *pos;
u32 cap; u32 cap;
struct ieee80211_sta_vht_cap vht_cap; struct ieee80211_sta_vht_cap vht_cap;
int i;
BUILD_BUG_ON(sizeof(vht_cap) != sizeof(sband->vht_cap)); BUILD_BUG_ON(sizeof(vht_cap) != sizeof(sband->vht_cap));
...@@ -364,6 +366,42 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata, ...@@ -364,6 +366,42 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
} }
/*
* Some APs apparently get confused if our capabilities are better
* than theirs, so restrict what we advertise in the assoc request.
*/
if (!(ap_vht_cap->vht_cap_info &
cpu_to_le32(IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE)))
cap &= ~IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
if (!(ap_vht_cap->vht_cap_info &
cpu_to_le32(IEEE80211_VHT_CAP_TXSTBC)))
cap &= ~(IEEE80211_VHT_CAP_RXSTBC_1 |
IEEE80211_VHT_CAP_RXSTBC_3 |
IEEE80211_VHT_CAP_RXSTBC_4);
for (i = 0; i < 8; i++) {
int shift = i * 2;
u16 mask = IEEE80211_VHT_MCS_NOT_SUPPORTED << shift;
u16 ap_mcs, our_mcs;
ap_mcs = (le16_to_cpu(ap_vht_cap->supp_mcs.tx_mcs_map) &
mask) >> shift;
our_mcs = (le16_to_cpu(vht_cap.vht_mcs.rx_mcs_map) &
mask) >> shift;
switch (ap_mcs) {
default:
if (our_mcs <= ap_mcs)
break;
/* fall through */
case IEEE80211_VHT_MCS_NOT_SUPPORTED:
vht_cap.vht_mcs.rx_mcs_map &= cpu_to_le16(~mask);
vht_cap.vht_mcs.rx_mcs_map |=
cpu_to_le16(ap_mcs << shift);
}
}
/* reserve and fill IE */ /* reserve and fill IE */
pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2); pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2);
ieee80211_ie_build_vht_cap(pos, &vht_cap, cap); ieee80211_ie_build_vht_cap(pos, &vht_cap, cap);
...@@ -562,7 +600,8 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) ...@@ -562,7 +600,8 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
sband, chan, sdata->smps_mode); sband, chan, sdata->smps_mode);
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT))
ieee80211_add_vht_ie(sdata, skb, sband); ieee80211_add_vht_ie(sdata, skb, sband,
&assoc_data->ap_vht_cap);
/* if present, add any custom non-vendor IEs that go after HT */ /* if present, add any custom non-vendor IEs that go after HT */
if (assoc_data->ie_len && assoc_data->ie) { if (assoc_data->ie_len && assoc_data->ie) {
...@@ -3753,7 +3792,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, ...@@ -3753,7 +3792,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
struct ieee80211_bss *bss = (void *)req->bss->priv; struct ieee80211_bss *bss = (void *)req->bss->priv;
struct ieee80211_mgd_assoc_data *assoc_data; struct ieee80211_mgd_assoc_data *assoc_data;
struct ieee80211_supported_band *sband; struct ieee80211_supported_band *sband;
const u8 *ssidie, *ht_ie; const u8 *ssidie, *ht_ie, *vht_ie;
int i, err; int i, err;
assoc_data = kzalloc(sizeof(*assoc_data) + req->ie_len, GFP_KERNEL); assoc_data = kzalloc(sizeof(*assoc_data) + req->ie_len, GFP_KERNEL);
...@@ -3872,6 +3911,12 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, ...@@ -3872,6 +3911,12 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
((struct ieee80211_ht_operation *)(ht_ie + 2))->ht_param; ((struct ieee80211_ht_operation *)(ht_ie + 2))->ht_param;
else else
ifmgd->flags |= IEEE80211_STA_DISABLE_HT; ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
vht_ie = ieee80211_bss_get_ie(req->bss, WLAN_EID_VHT_CAPABILITY);
if (vht_ie && vht_ie[1] >= sizeof(struct ieee80211_vht_cap))
memcpy(&assoc_data->ap_vht_cap, vht_ie + 2,
sizeof(struct ieee80211_vht_cap));
else
ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
rcu_read_unlock(); rcu_read_unlock();
if (bss->wmm_used && bss->uapsd_supported && if (bss->wmm_used && bss->uapsd_supported &&
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment