diff options
Diffstat (limited to 'net/mac80211/mlme.c')
-rw-r--r-- | net/mac80211/mlme.c | 236 |
1 files changed, 168 insertions, 68 deletions
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 50423c66c239..05b229e3b226 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c | |||
@@ -114,6 +114,9 @@ enum rx_mgmt_action { | |||
114 | 114 | ||
115 | /* caller must call cfg80211_send_assoc_timeout() */ | 115 | /* caller must call cfg80211_send_assoc_timeout() */ |
116 | RX_MGMT_CFG80211_ASSOC_TIMEOUT, | 116 | RX_MGMT_CFG80211_ASSOC_TIMEOUT, |
117 | |||
118 | /* used when a processed beacon causes a deauth */ | ||
119 | RX_MGMT_CFG80211_TX_DEAUTH, | ||
117 | }; | 120 | }; |
118 | 121 | ||
119 | /* utils */ | 122 | /* utils */ |
@@ -234,7 +237,7 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, | |||
234 | struct ieee80211_channel *channel, | 237 | struct ieee80211_channel *channel, |
235 | const struct ieee80211_ht_operation *ht_oper, | 238 | const struct ieee80211_ht_operation *ht_oper, |
236 | const struct ieee80211_vht_operation *vht_oper, | 239 | const struct ieee80211_vht_operation *vht_oper, |
237 | struct cfg80211_chan_def *chandef) | 240 | struct cfg80211_chan_def *chandef, bool verbose) |
238 | { | 241 | { |
239 | struct cfg80211_chan_def vht_chandef; | 242 | struct cfg80211_chan_def vht_chandef; |
240 | u32 ht_cfreq, ret; | 243 | u32 ht_cfreq, ret; |
@@ -262,10 +265,11 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, | |||
262 | * since we look at probe response/beacon data here | 265 | * since we look at probe response/beacon data here |
263 | * it should be OK. | 266 | * it should be OK. |
264 | */ | 267 | */ |
265 | sdata_info(sdata, | 268 | if (verbose) |
266 | "Wrong control channel: center-freq: %d ht-cfreq: %d ht->primary_chan: %d band: %d - Disabling HT\n", | 269 | sdata_info(sdata, |
267 | channel->center_freq, ht_cfreq, | 270 | "Wrong control channel: center-freq: %d ht-cfreq: %d ht->primary_chan: %d band: %d - Disabling HT\n", |
268 | ht_oper->primary_chan, channel->band); | 271 | channel->center_freq, ht_cfreq, |
272 | ht_oper->primary_chan, channel->band); | ||
269 | ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; | 273 | ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; |
270 | goto out; | 274 | goto out; |
271 | } | 275 | } |
@@ -319,16 +323,18 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, | |||
319 | vht_chandef.width = NL80211_CHAN_WIDTH_80P80; | 323 | vht_chandef.width = NL80211_CHAN_WIDTH_80P80; |
320 | break; | 324 | break; |
321 | default: | 325 | default: |
322 | sdata_info(sdata, | 326 | if (verbose) |
323 | "AP VHT operation IE has invalid channel width (%d), disable VHT\n", | 327 | sdata_info(sdata, |
324 | vht_oper->chan_width); | 328 | "AP VHT operation IE has invalid channel width (%d), disable VHT\n", |
329 | vht_oper->chan_width); | ||
325 | ret = IEEE80211_STA_DISABLE_VHT; | 330 | ret = IEEE80211_STA_DISABLE_VHT; |
326 | goto out; | 331 | goto out; |
327 | } | 332 | } |
328 | 333 | ||
329 | if (!cfg80211_chandef_valid(&vht_chandef)) { | 334 | if (!cfg80211_chandef_valid(&vht_chandef)) { |
330 | sdata_info(sdata, | 335 | if (verbose) |
331 | "AP VHT information is invalid, disable VHT\n"); | 336 | sdata_info(sdata, |
337 | "AP VHT information is invalid, disable VHT\n"); | ||
332 | ret = IEEE80211_STA_DISABLE_VHT; | 338 | ret = IEEE80211_STA_DISABLE_VHT; |
333 | goto out; | 339 | goto out; |
334 | } | 340 | } |
@@ -339,8 +345,9 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, | |||
339 | } | 345 | } |
340 | 346 | ||
341 | if (!cfg80211_chandef_compatible(chandef, &vht_chandef)) { | 347 | if (!cfg80211_chandef_compatible(chandef, &vht_chandef)) { |
342 | sdata_info(sdata, | 348 | if (verbose) |
343 | "AP VHT information doesn't match HT, disable VHT\n"); | 349 | sdata_info(sdata, |
350 | "AP VHT information doesn't match HT, disable VHT\n"); | ||
344 | ret = IEEE80211_STA_DISABLE_VHT; | 351 | ret = IEEE80211_STA_DISABLE_VHT; |
345 | goto out; | 352 | goto out; |
346 | } | 353 | } |
@@ -361,7 +368,7 @@ out: | |||
361 | ret |= chandef_downgrade(chandef); | 368 | ret |= chandef_downgrade(chandef); |
362 | } | 369 | } |
363 | 370 | ||
364 | if (chandef->width != vht_chandef.width) | 371 | if (chandef->width != vht_chandef.width && verbose) |
365 | sdata_info(sdata, | 372 | sdata_info(sdata, |
366 | "capabilities/regulatory prevented using AP HT/VHT configuration, downgraded\n"); | 373 | "capabilities/regulatory prevented using AP HT/VHT configuration, downgraded\n"); |
367 | 374 | ||
@@ -369,66 +376,128 @@ out: | |||
369 | return ret; | 376 | return ret; |
370 | } | 377 | } |
371 | 378 | ||
372 | static u32 ieee80211_config_ht_tx(struct ieee80211_sub_if_data *sdata, | 379 | static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata, |
373 | struct sta_info *sta, | 380 | struct sta_info *sta, |
374 | struct ieee80211_ht_operation *ht_oper, | 381 | const struct ieee80211_ht_operation *ht_oper, |
375 | const u8 *bssid, bool reconfig) | 382 | const struct ieee80211_vht_operation *vht_oper, |
383 | const u8 *bssid, u32 *changed) | ||
376 | { | 384 | { |
377 | struct ieee80211_local *local = sdata->local; | 385 | struct ieee80211_local *local = sdata->local; |
386 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | ||
378 | struct ieee80211_supported_band *sband; | 387 | struct ieee80211_supported_band *sband; |
379 | struct ieee80211_channel *chan; | 388 | struct ieee80211_channel *chan; |
380 | u32 changed = 0; | 389 | struct cfg80211_chan_def chandef; |
381 | u16 ht_opmode; | 390 | u16 ht_opmode; |
382 | bool disable_40 = false; | 391 | u32 flags; |
392 | enum ieee80211_sta_rx_bandwidth new_sta_bw; | ||
393 | int ret; | ||
383 | 394 | ||
384 | if (WARN_ON_ONCE(!sta)) | 395 | /* if HT was/is disabled, don't track any bandwidth changes */ |
396 | if (ifmgd->flags & IEEE80211_STA_DISABLE_HT || !ht_oper) | ||
385 | return 0; | 397 | return 0; |
386 | 398 | ||
399 | /* don't check VHT if we associated as non-VHT station */ | ||
400 | if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT) | ||
401 | vht_oper = NULL; | ||
402 | |||
403 | if (WARN_ON_ONCE(!sta)) | ||
404 | return -EINVAL; | ||
405 | |||
387 | chan = sdata->vif.bss_conf.chandef.chan; | 406 | chan = sdata->vif.bss_conf.chandef.chan; |
388 | sband = local->hw.wiphy->bands[chan->band]; | 407 | sband = local->hw.wiphy->bands[chan->band]; |
389 | 408 | ||
390 | switch (sdata->vif.bss_conf.chandef.width) { | 409 | /* calculate new channel (type) based on HT/VHT operation IEs */ |
410 | flags = ieee80211_determine_chantype(sdata, sband, chan, ht_oper, | ||
411 | vht_oper, &chandef, false); | ||
412 | |||
413 | /* | ||
414 | * Downgrade the new channel if we associated with restricted | ||
415 | * capabilities. For example, if we associated as a 20 MHz STA | ||
416 | * to a 40 MHz AP (due to regulatory, capabilities or config | ||
417 | * reasons) then switching to a 40 MHz channel now won't do us | ||
418 | * any good -- we couldn't use it with the AP. | ||
419 | */ | ||
420 | if (ifmgd->flags & IEEE80211_STA_DISABLE_80P80MHZ && | ||
421 | chandef.width == NL80211_CHAN_WIDTH_80P80) | ||
422 | flags |= chandef_downgrade(&chandef); | ||
423 | if (ifmgd->flags & IEEE80211_STA_DISABLE_160MHZ && | ||
424 | chandef.width == NL80211_CHAN_WIDTH_160) | ||
425 | flags |= chandef_downgrade(&chandef); | ||
426 | if (ifmgd->flags & IEEE80211_STA_DISABLE_40MHZ && | ||
427 | chandef.width > NL80211_CHAN_WIDTH_20) | ||
428 | flags |= chandef_downgrade(&chandef); | ||
429 | |||
430 | if (cfg80211_chandef_identical(&chandef, &sdata->vif.bss_conf.chandef)) | ||
431 | return 0; | ||
432 | |||
433 | sdata_info(sdata, | ||
434 | "AP %pM changed bandwidth, new config is %d MHz, width %d (%d/%d MHz)\n", | ||
435 | ifmgd->bssid, chandef.chan->center_freq, chandef.width, | ||
436 | chandef.center_freq1, chandef.center_freq2); | ||
437 | |||
438 | if (flags != (ifmgd->flags & (IEEE80211_STA_DISABLE_HT | | ||
439 | IEEE80211_STA_DISABLE_VHT | | ||
440 | IEEE80211_STA_DISABLE_40MHZ | | ||
441 | IEEE80211_STA_DISABLE_80P80MHZ | | ||
442 | IEEE80211_STA_DISABLE_160MHZ)) || | ||
443 | !cfg80211_chandef_valid(&chandef)) { | ||
444 | sdata_info(sdata, | ||
445 | "AP %pM changed bandwidth in a way we can't support - disconnect\n", | ||
446 | ifmgd->bssid); | ||
447 | return -EINVAL; | ||
448 | } | ||
449 | |||
450 | switch (chandef.width) { | ||
451 | case NL80211_CHAN_WIDTH_20_NOHT: | ||
452 | case NL80211_CHAN_WIDTH_20: | ||
453 | new_sta_bw = IEEE80211_STA_RX_BW_20; | ||
454 | break; | ||
391 | case NL80211_CHAN_WIDTH_40: | 455 | case NL80211_CHAN_WIDTH_40: |
392 | if (chan->center_freq > | 456 | new_sta_bw = IEEE80211_STA_RX_BW_40; |
393 | sdata->vif.bss_conf.chandef.center_freq1 && | ||
394 | chan->flags & IEEE80211_CHAN_NO_HT40MINUS) | ||
395 | disable_40 = true; | ||
396 | if (chan->center_freq < | ||
397 | sdata->vif.bss_conf.chandef.center_freq1 && | ||
398 | chan->flags & IEEE80211_CHAN_NO_HT40PLUS) | ||
399 | disable_40 = true; | ||
400 | break; | 457 | break; |
401 | default: | 458 | case NL80211_CHAN_WIDTH_80: |
459 | new_sta_bw = IEEE80211_STA_RX_BW_80; | ||
402 | break; | 460 | break; |
461 | case NL80211_CHAN_WIDTH_80P80: | ||
462 | case NL80211_CHAN_WIDTH_160: | ||
463 | new_sta_bw = IEEE80211_STA_RX_BW_160; | ||
464 | break; | ||
465 | default: | ||
466 | return -EINVAL; | ||
403 | } | 467 | } |
404 | 468 | ||
405 | /* This can change during the lifetime of the BSS */ | 469 | if (new_sta_bw > sta->cur_max_bandwidth) |
406 | if (!(ht_oper->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) | 470 | new_sta_bw = sta->cur_max_bandwidth; |
407 | disable_40 = true; | ||
408 | 471 | ||
409 | if (!(sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) | 472 | if (new_sta_bw < sta->sta.bandwidth) { |
410 | disable_40 = true; | 473 | sta->sta.bandwidth = new_sta_bw; |
474 | rate_control_rate_update(local, sband, sta, | ||
475 | IEEE80211_RC_BW_CHANGED); | ||
476 | } | ||
411 | 477 | ||
412 | if (disable_40 != (sta->sta.bandwidth < IEEE80211_STA_RX_BW_40)) { | 478 | ret = ieee80211_vif_change_bandwidth(sdata, &chandef, changed); |
413 | if (disable_40) | 479 | if (ret) { |
414 | sta->sta.bandwidth = IEEE80211_STA_RX_BW_20; | 480 | sdata_info(sdata, |
415 | else | 481 | "AP %pM changed bandwidth to incompatible one - disconnect\n", |
416 | sta->sta.bandwidth = ieee80211_sta_cur_vht_bw(sta); | 482 | ifmgd->bssid); |
483 | return ret; | ||
484 | } | ||
417 | 485 | ||
418 | if (reconfig) | 486 | if (new_sta_bw > sta->sta.bandwidth) { |
419 | rate_control_rate_update(local, sband, sta, | 487 | sta->sta.bandwidth = new_sta_bw; |
420 | IEEE80211_RC_BW_CHANGED); | 488 | rate_control_rate_update(local, sband, sta, |
489 | IEEE80211_RC_BW_CHANGED); | ||
421 | } | 490 | } |
422 | 491 | ||
423 | ht_opmode = le16_to_cpu(ht_oper->operation_mode); | 492 | ht_opmode = le16_to_cpu(ht_oper->operation_mode); |
424 | 493 | ||
425 | /* if bss configuration changed store the new one */ | 494 | /* if bss configuration changed store the new one */ |
426 | if (!reconfig || (sdata->vif.bss_conf.ht_operation_mode != ht_opmode)) { | 495 | if (sdata->vif.bss_conf.ht_operation_mode != ht_opmode) { |
427 | changed |= BSS_CHANGED_HT; | 496 | *changed |= BSS_CHANGED_HT; |
428 | sdata->vif.bss_conf.ht_operation_mode = ht_opmode; | 497 | sdata->vif.bss_conf.ht_operation_mode = ht_opmode; |
429 | } | 498 | } |
430 | 499 | ||
431 | return changed; | 500 | return 0; |
432 | } | 501 | } |
433 | 502 | ||
434 | /* frame sending functions */ | 503 | /* frame sending functions */ |
@@ -2377,6 +2446,24 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, | |||
2377 | 2446 | ||
2378 | ifmgd->aid = aid; | 2447 | ifmgd->aid = aid; |
2379 | 2448 | ||
2449 | /* | ||
2450 | * We previously checked these in the beacon/probe response, so | ||
2451 | * they should be present here. This is just a safety net. | ||
2452 | */ | ||
2453 | if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT) && | ||
2454 | (!elems.wmm_param || !elems.ht_cap_elem || !elems.ht_operation)) { | ||
2455 | sdata_info(sdata, | ||
2456 | "HT AP is missing WMM params or HT capability/operation in AssocResp\n"); | ||
2457 | return false; | ||
2458 | } | ||
2459 | |||
2460 | if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT) && | ||
2461 | (!elems.vht_cap_elem || !elems.vht_operation)) { | ||
2462 | sdata_info(sdata, | ||
2463 | "VHT AP is missing VHT capability/operation in AssocResp\n"); | ||
2464 | return false; | ||
2465 | } | ||
2466 | |||
2380 | mutex_lock(&sdata->local->sta_mtx); | 2467 | mutex_lock(&sdata->local->sta_mtx); |
2381 | /* | 2468 | /* |
2382 | * station info was already allocated and inserted before | 2469 | * station info was already allocated and inserted before |
@@ -2390,6 +2477,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, | |||
2390 | 2477 | ||
2391 | sband = local->hw.wiphy->bands[ieee80211_get_sdata_band(sdata)]; | 2478 | sband = local->hw.wiphy->bands[ieee80211_get_sdata_band(sdata)]; |
2392 | 2479 | ||
2480 | /* Set up internal HT/VHT capabilities */ | ||
2393 | if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) | 2481 | if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) |
2394 | ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, | 2482 | ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, |
2395 | elems.ht_cap_elem, sta); | 2483 | elems.ht_cap_elem, sta); |
@@ -2398,11 +2486,12 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, | |||
2398 | ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband, | 2486 | ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband, |
2399 | elems.vht_cap_elem, sta); | 2487 | elems.vht_cap_elem, sta); |
2400 | 2488 | ||
2401 | if (elems.ht_operation && elems.wmm_param && | 2489 | /* |
2402 | !(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) | 2490 | * Some APs, e.g. Netgear WNDR3700, report invalid HT operation data |
2403 | changed |= ieee80211_config_ht_tx(sdata, sta, | 2491 | * in their association response, so ignore that data for our own |
2404 | elems.ht_operation, | 2492 | * configuration. If it changed since the last beacon, we'll get the |
2405 | cbss->bssid, false); | 2493 | * next beacon and update then. |
2494 | */ | ||
2406 | 2495 | ||
2407 | /* | 2496 | /* |
2408 | * If an operating mode notification IE is present, override the | 2497 | * If an operating mode notification IE is present, override the |
@@ -2679,10 +2768,10 @@ static const u64 care_about_ies = | |||
2679 | (1ULL << WLAN_EID_HT_CAPABILITY) | | 2768 | (1ULL << WLAN_EID_HT_CAPABILITY) | |
2680 | (1ULL << WLAN_EID_HT_OPERATION); | 2769 | (1ULL << WLAN_EID_HT_OPERATION); |
2681 | 2770 | ||
2682 | static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, | 2771 | static enum rx_mgmt_action |
2683 | struct ieee80211_mgmt *mgmt, | 2772 | ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, |
2684 | size_t len, | 2773 | struct ieee80211_mgmt *mgmt, size_t len, |
2685 | struct ieee80211_rx_status *rx_status) | 2774 | u8 *deauth_buf, struct ieee80211_rx_status *rx_status) |
2686 | { | 2775 | { |
2687 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | 2776 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
2688 | struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf; | 2777 | struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf; |
@@ -2703,18 +2792,18 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, | |||
2703 | /* Process beacon from the current BSS */ | 2792 | /* Process beacon from the current BSS */ |
2704 | baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt; | 2793 | baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt; |
2705 | if (baselen > len) | 2794 | if (baselen > len) |
2706 | return; | 2795 | return RX_MGMT_NONE; |
2707 | 2796 | ||
2708 | rcu_read_lock(); | 2797 | rcu_read_lock(); |
2709 | chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); | 2798 | chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); |
2710 | if (!chanctx_conf) { | 2799 | if (!chanctx_conf) { |
2711 | rcu_read_unlock(); | 2800 | rcu_read_unlock(); |
2712 | return; | 2801 | return RX_MGMT_NONE; |
2713 | } | 2802 | } |
2714 | 2803 | ||
2715 | if (rx_status->freq != chanctx_conf->def.chan->center_freq) { | 2804 | if (rx_status->freq != chanctx_conf->def.chan->center_freq) { |
2716 | rcu_read_unlock(); | 2805 | rcu_read_unlock(); |
2717 | return; | 2806 | return RX_MGMT_NONE; |
2718 | } | 2807 | } |
2719 | chan = chanctx_conf->def.chan; | 2808 | chan = chanctx_conf->def.chan; |
2720 | rcu_read_unlock(); | 2809 | rcu_read_unlock(); |
@@ -2742,12 +2831,12 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, | |||
2742 | ifmgd->assoc_data->timeout = jiffies; | 2831 | ifmgd->assoc_data->timeout = jiffies; |
2743 | ifmgd->assoc_data->timeout_started = true; | 2832 | ifmgd->assoc_data->timeout_started = true; |
2744 | run_again(ifmgd, ifmgd->assoc_data->timeout); | 2833 | run_again(ifmgd, ifmgd->assoc_data->timeout); |
2745 | return; | 2834 | return RX_MGMT_NONE; |
2746 | } | 2835 | } |
2747 | 2836 | ||
2748 | if (!ifmgd->associated || | 2837 | if (!ifmgd->associated || |
2749 | !ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid)) | 2838 | !ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid)) |
2750 | return; | 2839 | return RX_MGMT_NONE; |
2751 | bssid = ifmgd->associated->bssid; | 2840 | bssid = ifmgd->associated->bssid; |
2752 | 2841 | ||
2753 | /* Track average RSSI from the Beacon frames of the current AP */ | 2842 | /* Track average RSSI from the Beacon frames of the current AP */ |
@@ -2885,7 +2974,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, | |||
2885 | } | 2974 | } |
2886 | 2975 | ||
2887 | if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid) | 2976 | if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid) |
2888 | return; | 2977 | return RX_MGMT_NONE; |
2889 | ifmgd->beacon_crc = ncrc; | 2978 | ifmgd->beacon_crc = ncrc; |
2890 | ifmgd->beacon_crc_valid = true; | 2979 | ifmgd->beacon_crc_valid = true; |
2891 | 2980 | ||
@@ -2934,11 +3023,14 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, | |||
2934 | mutex_lock(&local->sta_mtx); | 3023 | mutex_lock(&local->sta_mtx); |
2935 | sta = sta_info_get(sdata, bssid); | 3024 | sta = sta_info_get(sdata, bssid); |
2936 | 3025 | ||
2937 | if (elems.ht_cap_elem && elems.ht_operation && elems.wmm_param && | 3026 | if (ieee80211_config_bw(sdata, sta, elems.ht_operation, |
2938 | !(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) | 3027 | elems.vht_operation, bssid, &changed)) { |
2939 | changed |= ieee80211_config_ht_tx(sdata, sta, | 3028 | mutex_unlock(&local->sta_mtx); |
2940 | elems.ht_operation, | 3029 | ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, |
2941 | bssid, true); | 3030 | WLAN_REASON_DEAUTH_LEAVING, |
3031 | true, deauth_buf); | ||
3032 | return RX_MGMT_CFG80211_TX_DEAUTH; | ||
3033 | } | ||
2942 | 3034 | ||
2943 | if (sta && elems.opmode_notif) | 3035 | if (sta && elems.opmode_notif) |
2944 | ieee80211_vht_handle_opmode(sdata, sta, *elems.opmode_notif, | 3036 | ieee80211_vht_handle_opmode(sdata, sta, *elems.opmode_notif, |
@@ -2954,6 +3046,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, | |||
2954 | elems.pwr_constr_elem); | 3046 | elems.pwr_constr_elem); |
2955 | 3047 | ||
2956 | ieee80211_bss_info_change_notify(sdata, changed); | 3048 | ieee80211_bss_info_change_notify(sdata, changed); |
3049 | |||
3050 | return RX_MGMT_NONE; | ||
2957 | } | 3051 | } |
2958 | 3052 | ||
2959 | void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, | 3053 | void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, |
@@ -2964,6 +3058,7 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, | |||
2964 | struct ieee80211_mgmt *mgmt; | 3058 | struct ieee80211_mgmt *mgmt; |
2965 | struct cfg80211_bss *bss = NULL; | 3059 | struct cfg80211_bss *bss = NULL; |
2966 | enum rx_mgmt_action rma = RX_MGMT_NONE; | 3060 | enum rx_mgmt_action rma = RX_MGMT_NONE; |
3061 | u8 deauth_buf[IEEE80211_DEAUTH_FRAME_LEN]; | ||
2967 | u16 fc; | 3062 | u16 fc; |
2968 | 3063 | ||
2969 | rx_status = (struct ieee80211_rx_status *) skb->cb; | 3064 | rx_status = (struct ieee80211_rx_status *) skb->cb; |
@@ -2974,7 +3069,8 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, | |||
2974 | 3069 | ||
2975 | switch (fc & IEEE80211_FCTL_STYPE) { | 3070 | switch (fc & IEEE80211_FCTL_STYPE) { |
2976 | case IEEE80211_STYPE_BEACON: | 3071 | case IEEE80211_STYPE_BEACON: |
2977 | ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len, rx_status); | 3072 | rma = ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len, |
3073 | deauth_buf, rx_status); | ||
2978 | break; | 3074 | break; |
2979 | case IEEE80211_STYPE_PROBE_RESP: | 3075 | case IEEE80211_STYPE_PROBE_RESP: |
2980 | ieee80211_rx_mgmt_probe_resp(sdata, skb); | 3076 | ieee80211_rx_mgmt_probe_resp(sdata, skb); |
@@ -3023,6 +3119,10 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, | |||
3023 | case RX_MGMT_CFG80211_ASSOC_TIMEOUT: | 3119 | case RX_MGMT_CFG80211_ASSOC_TIMEOUT: |
3024 | cfg80211_send_assoc_timeout(sdata->dev, mgmt->bssid); | 3120 | cfg80211_send_assoc_timeout(sdata->dev, mgmt->bssid); |
3025 | break; | 3121 | break; |
3122 | case RX_MGMT_CFG80211_TX_DEAUTH: | ||
3123 | cfg80211_send_deauth(sdata->dev, deauth_buf, | ||
3124 | sizeof(deauth_buf)); | ||
3125 | break; | ||
3026 | default: | 3126 | default: |
3027 | WARN(1, "unexpected: %d", rma); | 3127 | WARN(1, "unexpected: %d", rma); |
3028 | } | 3128 | } |
@@ -3620,7 +3720,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, | |||
3620 | ifmgd->flags |= ieee80211_determine_chantype(sdata, sband, | 3720 | ifmgd->flags |= ieee80211_determine_chantype(sdata, sband, |
3621 | cbss->channel, | 3721 | cbss->channel, |
3622 | ht_oper, vht_oper, | 3722 | ht_oper, vht_oper, |
3623 | &chandef); | 3723 | &chandef, true); |
3624 | 3724 | ||
3625 | sdata->needed_rx_chains = min(ieee80211_ht_vht_rx_chains(sdata, cbss), | 3725 | sdata->needed_rx_chains = min(ieee80211_ht_vht_rx_chains(sdata, cbss), |
3626 | local->rx_chains); | 3726 | local->rx_chains); |