mac80211: restrict assoc request VHT capabilities
authorJohannes Berg <johannes.berg@intel.com>
Fri, 7 Dec 2012 12:06:48 +0000 (13:06 +0100)
committerJohannes Berg <johannes.berg@intel.com>
Thu, 3 Jan 2013 12:01:39 +0000 (13:01 +0100)
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>
net/mac80211/ieee80211_i.h
net/mac80211/mlme.c

index c084c1503c0408ca665d39cde3bd7d0ed436f50e..7182907e282a2290488fa05f8ff552d31b3022ad 100644 (file)
@@ -405,6 +405,8 @@ struct ieee80211_mgd_assoc_data {
 
        u8 ap_ht_param;
 
+       struct ieee80211_vht_cap ap_vht_cap;
+
        size_t ie_len;
        u8 ie[];
 };
index 7d253a02217333d1c8d29e5ca35e524ae9124d18..6ffbf701de95a72bb15b5dba18b6a790038efc39 100644 (file)
@@ -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,
                                 struct sk_buff *skb,
-                                struct ieee80211_supported_band *sband)
+                                struct ieee80211_supported_band *sband,
+                                struct ieee80211_vht_cap *ap_vht_cap)
 {
        u8 *pos;
        u32 cap;
        struct ieee80211_sta_vht_cap vht_cap;
+       int i;
 
        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,
                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 */
        pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2);
        ieee80211_ie_build_vht_cap(pos, &vht_cap, cap);
@@ -562,7 +600,8 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
                                    sband, chan, sdata->smps_mode);
 
        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 (assoc_data->ie_len && assoc_data->ie) {
@@ -3753,7 +3792,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_bss *bss = (void *)req->bss->priv;
        struct ieee80211_mgd_assoc_data *assoc_data;
        struct ieee80211_supported_band *sband;
-       const u8 *ssidie, *ht_ie;
+       const u8 *ssidie, *ht_ie, *vht_ie;
        int i, err;
 
        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,
                        ((struct ieee80211_ht_operation *)(ht_ie + 2))->ht_param;
        else
                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();
 
        if (bss->wmm_used && bss->uapsd_supported &&