aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2012-12-07 07:06:48 -0500
committerJohannes Berg <johannes.berg@intel.com>2013-01-03 07:01:39 -0500
commitb08fbbd8ade1fe3eea821a65b98d9f6a828329da (patch)
tree09c6f053e83f97cbe8207036eadb3755fa329e1f /net
parent0f500a5f6cadac888d5d3fce326452372d1a8343 (diff)
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: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net')
-rw-r--r--net/mac80211/ieee80211_i.h2
-rw-r--r--net/mac80211/mlme.c51
2 files changed, 50 insertions, 3 deletions
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index c084c1503c04..7182907e282a 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -405,6 +405,8 @@ struct ieee80211_mgd_assoc_data {
405 405
406 u8 ap_ht_param; 406 u8 ap_ht_param;
407 407
408 struct ieee80211_vht_cap ap_vht_cap;
409
408 size_t ie_len; 410 size_t ie_len;
409 u8 ie[]; 411 u8 ie[];
410}; 412};
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 7d253a022173..6ffbf701de95 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -341,11 +341,13 @@ static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata,
341 341
342static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata, 342static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
343 struct sk_buff *skb, 343 struct sk_buff *skb,
344 struct ieee80211_supported_band *sband) 344 struct ieee80211_supported_band *sband,
345 struct ieee80211_vht_cap *ap_vht_cap)
345{ 346{
346 u8 *pos; 347 u8 *pos;
347 u32 cap; 348 u32 cap;
348 struct ieee80211_sta_vht_cap vht_cap; 349 struct ieee80211_sta_vht_cap vht_cap;
350 int i;
349 351
350 BUILD_BUG_ON(sizeof(vht_cap) != sizeof(sband->vht_cap)); 352 BUILD_BUG_ON(sizeof(vht_cap) != sizeof(sband->vht_cap));
351 353
@@ -364,6 +366,42 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
364 cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; 366 cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
365 } 367 }
366 368
369 /*
370 * Some APs apparently get confused if our capabilities are better
371 * than theirs, so restrict what we advertise in the assoc request.
372 */
373 if (!(ap_vht_cap->vht_cap_info &
374 cpu_to_le32(IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE)))
375 cap &= ~IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
376
377 if (!(ap_vht_cap->vht_cap_info &
378 cpu_to_le32(IEEE80211_VHT_CAP_TXSTBC)))
379 cap &= ~(IEEE80211_VHT_CAP_RXSTBC_1 |
380 IEEE80211_VHT_CAP_RXSTBC_3 |
381 IEEE80211_VHT_CAP_RXSTBC_4);
382
383 for (i = 0; i < 8; i++) {
384 int shift = i * 2;
385 u16 mask = IEEE80211_VHT_MCS_NOT_SUPPORTED << shift;
386 u16 ap_mcs, our_mcs;
387
388 ap_mcs = (le16_to_cpu(ap_vht_cap->supp_mcs.tx_mcs_map) &
389 mask) >> shift;
390 our_mcs = (le16_to_cpu(vht_cap.vht_mcs.rx_mcs_map) &
391 mask) >> shift;
392
393 switch (ap_mcs) {
394 default:
395 if (our_mcs <= ap_mcs)
396 break;
397 /* fall through */
398 case IEEE80211_VHT_MCS_NOT_SUPPORTED:
399 vht_cap.vht_mcs.rx_mcs_map &= cpu_to_le16(~mask);
400 vht_cap.vht_mcs.rx_mcs_map |=
401 cpu_to_le16(ap_mcs << shift);
402 }
403 }
404
367 /* reserve and fill IE */ 405 /* reserve and fill IE */
368 pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2); 406 pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2);
369 ieee80211_ie_build_vht_cap(pos, &vht_cap, cap); 407 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 sband, chan, sdata->smps_mode); 600 sband, chan, sdata->smps_mode);
563 601
564 if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) 602 if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT))
565 ieee80211_add_vht_ie(sdata, skb, sband); 603 ieee80211_add_vht_ie(sdata, skb, sband,
604 &assoc_data->ap_vht_cap);
566 605
567 /* if present, add any custom non-vendor IEs that go after HT */ 606 /* if present, add any custom non-vendor IEs that go after HT */
568 if (assoc_data->ie_len && assoc_data->ie) { 607 if (assoc_data->ie_len && assoc_data->ie) {
@@ -3753,7 +3792,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
3753 struct ieee80211_bss *bss = (void *)req->bss->priv; 3792 struct ieee80211_bss *bss = (void *)req->bss->priv;
3754 struct ieee80211_mgd_assoc_data *assoc_data; 3793 struct ieee80211_mgd_assoc_data *assoc_data;
3755 struct ieee80211_supported_band *sband; 3794 struct ieee80211_supported_band *sband;
3756 const u8 *ssidie, *ht_ie; 3795 const u8 *ssidie, *ht_ie, *vht_ie;
3757 int i, err; 3796 int i, err;
3758 3797
3759 assoc_data = kzalloc(sizeof(*assoc_data) + req->ie_len, GFP_KERNEL); 3798 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 ((struct ieee80211_ht_operation *)(ht_ie + 2))->ht_param; 3911 ((struct ieee80211_ht_operation *)(ht_ie + 2))->ht_param;
3873 else 3912 else
3874 ifmgd->flags |= IEEE80211_STA_DISABLE_HT; 3913 ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
3914 vht_ie = ieee80211_bss_get_ie(req->bss, WLAN_EID_VHT_CAPABILITY);
3915 if (vht_ie && vht_ie[1] >= sizeof(struct ieee80211_vht_cap))
3916 memcpy(&assoc_data->ap_vht_cap, vht_ie + 2,
3917 sizeof(struct ieee80211_vht_cap));
3918 else
3919 ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
3875 rcu_read_unlock(); 3920 rcu_read_unlock();
3876 3921
3877 if (bss->wmm_used && bss->uapsd_supported && 3922 if (bss->wmm_used && bss->uapsd_supported &&