aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2013-10-15 06:25:07 -0400
committerJohannes Berg <johannes.berg@intel.com>2013-10-17 09:38:22 -0400
commit095d81cee742fc31e1218077ca6ca8f0df07c613 (patch)
treea4518d4b69a13353f1114f35ac84d9a284a98df6 /net
parent1d2d350bbf4a1ba6cef5e3dbb63c66da11814d9a (diff)
mac80211: disable WMM with invalid parameters
Some APs (notably a Sitecom WL-153 v1 with firmware 1.45) are sending invalid WMM parameters setting AIFSN, ECWmin and ECWmax to zero. The spec mandates that the value of AIFSN is at least 2, and some cards (e.g. Intel with the iwldvm driver) can't transmit when the invalid QoS parameters are actually uploaded to the firmware. Since there's little chance of being able to guess the values that the AP actually meant, disable WMM if such an invalid case is found. Since ECWmin/ECWmax are allowed to be zero, only verify AIFSN >= 2 and ECWmin <= ECWmax. Reviewed-by: Eliad Peller <eliad@wizery.com> Reported-by: Antonio Quartulli <antonio@meshcoding.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net')
-rw-r--r--net/mac80211/ieee80211_i.h1
-rw-r--r--net/mac80211/mlme.c95
2 files changed, 87 insertions, 9 deletions
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 611abfcfb5eb..076409526bcb 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -335,6 +335,7 @@ enum ieee80211_sta_flags {
335 IEEE80211_STA_DISABLE_VHT = BIT(11), 335 IEEE80211_STA_DISABLE_VHT = BIT(11),
336 IEEE80211_STA_DISABLE_80P80MHZ = BIT(12), 336 IEEE80211_STA_DISABLE_80P80MHZ = BIT(12),
337 IEEE80211_STA_DISABLE_160MHZ = BIT(13), 337 IEEE80211_STA_DISABLE_160MHZ = BIT(13),
338 IEEE80211_STA_DISABLE_WMM = BIT(14),
338}; 339};
339 340
340struct ieee80211_mgd_auth_data { 341struct ieee80211_mgd_auth_data {
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 86e4ad56b573..54ebc8155b49 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -2717,7 +2717,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
2717 */ 2717 */
2718 ifmgd->wmm_last_param_set = -1; 2718 ifmgd->wmm_last_param_set = -1;
2719 2719
2720 if (elems.wmm_param) 2720 if (!(ifmgd->flags & IEEE80211_STA_DISABLE_WMM) && elems.wmm_param)
2721 ieee80211_sta_wmm_params(local, sdata, elems.wmm_param, 2721 ieee80211_sta_wmm_params(local, sdata, elems.wmm_param,
2722 elems.wmm_param_len); 2722 elems.wmm_param_len);
2723 else 2723 else
@@ -3152,7 +3152,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
3152 ieee80211_sta_process_chanswitch(sdata, rx_status->mactime, 3152 ieee80211_sta_process_chanswitch(sdata, rx_status->mactime,
3153 &elems, true); 3153 &elems, true);
3154 3154
3155 if (ieee80211_sta_wmm_params(local, sdata, elems.wmm_param, 3155 if (!(ifmgd->flags & IEEE80211_STA_DISABLE_WMM) &&
3156 ieee80211_sta_wmm_params(local, sdata, elems.wmm_param,
3156 elems.wmm_param_len)) 3157 elems.wmm_param_len))
3157 changed |= BSS_CHANGED_QOS; 3158 changed |= BSS_CHANGED_QOS;
3158 3159
@@ -4135,6 +4136,44 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
4135 return err; 4136 return err;
4136} 4137}
4137 4138
4139static bool ieee80211_usable_wmm_params(struct ieee80211_sub_if_data *sdata,
4140 const u8 *wmm_param, int len)
4141{
4142 const u8 *pos;
4143 size_t left;
4144
4145 if (len < 8)
4146 return false;
4147
4148 if (wmm_param[5] != 1 /* version */)
4149 return false;
4150
4151 pos = wmm_param + 8;
4152 left = len - 8;
4153
4154 for (; left >= 4; left -= 4, pos += 4) {
4155 u8 aifsn = pos[0] & 0x0f;
4156 u8 ecwmin = pos[1] & 0x0f;
4157 u8 ecwmax = (pos[1] & 0xf0) >> 4;
4158 int aci = (pos[0] >> 5) & 0x03;
4159
4160 if (aifsn < 2) {
4161 sdata_info(sdata,
4162 "AP has invalid WMM params (AIFSN=%d for ACI %d), disabling WMM\n",
4163 aifsn, aci);
4164 return false;
4165 }
4166 if (ecwmin > ecwmax) {
4167 sdata_info(sdata,
4168 "AP has invalid WMM params (ECWmin/max=%d/%d for ACI %d), disabling WMM\n",
4169 ecwmin, ecwmax, aci);
4170 return false;
4171 }
4172 }
4173
4174 return true;
4175}
4176
4138int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, 4177int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
4139 struct cfg80211_assoc_request *req) 4178 struct cfg80211_assoc_request *req)
4140{ 4179{
@@ -4192,9 +4231,45 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
4192 } 4231 }
4193 4232
4194 /* prepare assoc data */ 4233 /* prepare assoc data */
4195 4234
4196 ifmgd->beacon_crc_valid = false; 4235 ifmgd->beacon_crc_valid = false;
4197 4236
4237 assoc_data->wmm = bss->wmm_used &&
4238 (local->hw.queues >= IEEE80211_NUM_ACS);
4239 if (assoc_data->wmm) {
4240 /* try to check validity of WMM params IE */
4241 const struct cfg80211_bss_ies *ies;
4242 const u8 *wp, *start, *end;
4243
4244 rcu_read_lock();
4245 ies = rcu_dereference(req->bss->ies);
4246 start = ies->data;
4247 end = start + ies->len;
4248
4249 while (true) {
4250 wp = cfg80211_find_vendor_ie(
4251 WLAN_OUI_MICROSOFT,
4252 WLAN_OUI_TYPE_MICROSOFT_WMM,
4253 start, end - start);
4254 if (!wp)
4255 break;
4256 start = wp + wp[1] + 2;
4257 /* if this IE is too short, try the next */
4258 if (wp[1] <= 4)
4259 continue;
4260 /* if this IE is WMM params, we found what we wanted */
4261 if (wp[6] == 1)
4262 break;
4263 }
4264
4265 if (!wp || !ieee80211_usable_wmm_params(sdata, wp + 2,
4266 wp[1] - 2)) {
4267 assoc_data->wmm = false;
4268 ifmgd->flags |= IEEE80211_STA_DISABLE_WMM;
4269 }
4270 rcu_read_unlock();
4271 }
4272
4198 /* 4273 /*
4199 * IEEE802.11n does not allow TKIP/WEP as pairwise ciphers in HT mode. 4274 * IEEE802.11n does not allow TKIP/WEP as pairwise ciphers in HT mode.
4200 * We still associate in non-HT mode (11a/b/g) if any one of these 4275 * We still associate in non-HT mode (11a/b/g) if any one of these
@@ -4224,18 +4299,22 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
4224 /* Also disable HT if we don't support it or the AP doesn't use WMM */ 4299 /* Also disable HT if we don't support it or the AP doesn't use WMM */
4225 sband = local->hw.wiphy->bands[req->bss->channel->band]; 4300 sband = local->hw.wiphy->bands[req->bss->channel->band];
4226 if (!sband->ht_cap.ht_supported || 4301 if (!sband->ht_cap.ht_supported ||
4227 local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used) { 4302 local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used ||
4303 ifmgd->flags & IEEE80211_STA_DISABLE_WMM) {
4228 ifmgd->flags |= IEEE80211_STA_DISABLE_HT; 4304 ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
4229 if (!bss->wmm_used) 4305 if (!bss->wmm_used &&
4306 !(ifmgd->flags & IEEE80211_STA_DISABLE_WMM))
4230 netdev_info(sdata->dev, 4307 netdev_info(sdata->dev,
4231 "disabling HT as WMM/QoS is not supported by the AP\n"); 4308 "disabling HT as WMM/QoS is not supported by the AP\n");
4232 } 4309 }
4233 4310
4234 /* disable VHT if we don't support it or the AP doesn't use WMM */ 4311 /* disable VHT if we don't support it or the AP doesn't use WMM */
4235 if (!sband->vht_cap.vht_supported || 4312 if (!sband->vht_cap.vht_supported ||
4236 local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used) { 4313 local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used ||
4314 ifmgd->flags & IEEE80211_STA_DISABLE_WMM) {
4237 ifmgd->flags |= IEEE80211_STA_DISABLE_VHT; 4315 ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
4238 if (!bss->wmm_used) 4316 if (!bss->wmm_used &&
4317 !(ifmgd->flags & IEEE80211_STA_DISABLE_WMM))
4239 netdev_info(sdata->dev, 4318 netdev_info(sdata->dev,
4240 "disabling VHT as WMM/QoS is not supported by the AP\n"); 4319 "disabling VHT as WMM/QoS is not supported by the AP\n");
4241 } 4320 }
@@ -4264,8 +4343,6 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
4264 sdata->smps_mode = ifmgd->req_smps; 4343 sdata->smps_mode = ifmgd->req_smps;
4265 4344
4266 assoc_data->capability = req->bss->capability; 4345 assoc_data->capability = req->bss->capability;
4267 assoc_data->wmm = bss->wmm_used &&
4268 (local->hw.queues >= IEEE80211_NUM_ACS);
4269 assoc_data->supp_rates = bss->supp_rates; 4346 assoc_data->supp_rates = bss->supp_rates;
4270 assoc_data->supp_rates_len = bss->supp_rates_len; 4347 assoc_data->supp_rates_len = bss->supp_rates_len;
4271 4348