diff options
Diffstat (limited to 'net/mac80211/mlme.c')
-rw-r--r-- | net/mac80211/mlme.c | 735 |
1 files changed, 565 insertions, 170 deletions
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 1b7eed252fe9..7753a9ca98a6 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c | |||
@@ -178,20 +178,32 @@ static u32 ieee80211_config_ht_tx(struct ieee80211_sub_if_data *sdata, | |||
178 | { | 178 | { |
179 | struct ieee80211_local *local = sdata->local; | 179 | struct ieee80211_local *local = sdata->local; |
180 | struct ieee80211_supported_band *sband; | 180 | struct ieee80211_supported_band *sband; |
181 | struct ieee80211_chanctx_conf *chanctx_conf; | ||
182 | struct ieee80211_channel *chan; | ||
181 | struct sta_info *sta; | 183 | struct sta_info *sta; |
182 | u32 changed = 0; | 184 | u32 changed = 0; |
183 | u16 ht_opmode; | 185 | u16 ht_opmode; |
184 | bool disable_40 = false; | 186 | bool disable_40 = false; |
185 | 187 | ||
186 | sband = local->hw.wiphy->bands[local->oper_channel->band]; | 188 | rcu_read_lock(); |
189 | chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); | ||
190 | if (WARN_ON(!chanctx_conf)) { | ||
191 | rcu_read_unlock(); | ||
192 | return 0; | ||
193 | } | ||
194 | chan = chanctx_conf->def.chan; | ||
195 | rcu_read_unlock(); | ||
196 | sband = local->hw.wiphy->bands[chan->band]; | ||
187 | 197 | ||
188 | switch (sdata->vif.bss_conf.channel_type) { | 198 | switch (sdata->vif.bss_conf.chandef.width) { |
189 | case NL80211_CHAN_HT40PLUS: | 199 | case NL80211_CHAN_WIDTH_40: |
190 | if (local->oper_channel->flags & IEEE80211_CHAN_NO_HT40PLUS) | 200 | if (sdata->vif.bss_conf.chandef.chan->center_freq > |
201 | sdata->vif.bss_conf.chandef.center_freq1 && | ||
202 | chan->flags & IEEE80211_CHAN_NO_HT40PLUS) | ||
191 | disable_40 = true; | 203 | disable_40 = true; |
192 | break; | 204 | if (sdata->vif.bss_conf.chandef.chan->center_freq < |
193 | case NL80211_CHAN_HT40MINUS: | 205 | sdata->vif.bss_conf.chandef.center_freq1 && |
194 | if (local->oper_channel->flags & IEEE80211_CHAN_NO_HT40MINUS) | 206 | chan->flags & IEEE80211_CHAN_NO_HT40MINUS) |
195 | disable_40 = true; | 207 | disable_40 = true; |
196 | break; | 208 | break; |
197 | default: | 209 | default: |
@@ -342,8 +354,18 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata, | |||
342 | /* determine capability flags */ | 354 | /* determine capability flags */ |
343 | cap = vht_cap.cap; | 355 | cap = vht_cap.cap; |
344 | 356 | ||
357 | if (sdata->u.mgd.flags & IEEE80211_STA_DISABLE_80P80MHZ) { | ||
358 | cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ; | ||
359 | cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; | ||
360 | } | ||
361 | |||
362 | if (sdata->u.mgd.flags & IEEE80211_STA_DISABLE_160MHZ) { | ||
363 | cap &= ~IEEE80211_VHT_CAP_SHORT_GI_160; | ||
364 | cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; | ||
365 | } | ||
366 | |||
345 | /* reserve and fill IE */ | 367 | /* reserve and fill IE */ |
346 | pos = skb_put(skb, sizeof(struct ieee80211_vht_capabilities) + 2); | 368 | pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2); |
347 | ieee80211_ie_build_vht_cap(pos, &vht_cap, cap); | 369 | ieee80211_ie_build_vht_cap(pos, &vht_cap, cap); |
348 | } | 370 | } |
349 | 371 | ||
@@ -359,11 +381,21 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) | |||
359 | int i, count, rates_len, supp_rates_len; | 381 | int i, count, rates_len, supp_rates_len; |
360 | u16 capab; | 382 | u16 capab; |
361 | struct ieee80211_supported_band *sband; | 383 | struct ieee80211_supported_band *sband; |
384 | struct ieee80211_chanctx_conf *chanctx_conf; | ||
385 | struct ieee80211_channel *chan; | ||
362 | u32 rates = 0; | 386 | u32 rates = 0; |
363 | 387 | ||
364 | lockdep_assert_held(&ifmgd->mtx); | 388 | lockdep_assert_held(&ifmgd->mtx); |
365 | 389 | ||
366 | sband = local->hw.wiphy->bands[local->oper_channel->band]; | 390 | rcu_read_lock(); |
391 | chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); | ||
392 | if (WARN_ON(!chanctx_conf)) { | ||
393 | rcu_read_unlock(); | ||
394 | return; | ||
395 | } | ||
396 | chan = chanctx_conf->def.chan; | ||
397 | rcu_read_unlock(); | ||
398 | sband = local->hw.wiphy->bands[chan->band]; | ||
367 | 399 | ||
368 | if (assoc_data->supp_rates_len) { | 400 | if (assoc_data->supp_rates_len) { |
369 | /* | 401 | /* |
@@ -392,7 +424,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) | |||
392 | 4 + /* power capability */ | 424 | 4 + /* power capability */ |
393 | 2 + 2 * sband->n_channels + /* supported channels */ | 425 | 2 + 2 * sband->n_channels + /* supported channels */ |
394 | 2 + sizeof(struct ieee80211_ht_cap) + /* HT */ | 426 | 2 + sizeof(struct ieee80211_ht_cap) + /* HT */ |
395 | 2 + sizeof(struct ieee80211_vht_capabilities) + /* VHT */ | 427 | 2 + sizeof(struct ieee80211_vht_cap) + /* VHT */ |
396 | assoc_data->ie_len + /* extra IEs */ | 428 | assoc_data->ie_len + /* extra IEs */ |
397 | 9, /* WMM */ | 429 | 9, /* WMM */ |
398 | GFP_KERNEL); | 430 | GFP_KERNEL); |
@@ -485,7 +517,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) | |||
485 | *pos++ = WLAN_EID_PWR_CAPABILITY; | 517 | *pos++ = WLAN_EID_PWR_CAPABILITY; |
486 | *pos++ = 2; | 518 | *pos++ = 2; |
487 | *pos++ = 0; /* min tx power */ | 519 | *pos++ = 0; /* min tx power */ |
488 | *pos++ = local->oper_channel->max_power; /* max tx power */ | 520 | *pos++ = chan->max_power; /* max tx power */ |
489 | 521 | ||
490 | /* 2. supported channels */ | 522 | /* 2. supported channels */ |
491 | /* TODO: get this in reg domain format */ | 523 | /* TODO: get this in reg domain format */ |
@@ -521,9 +553,13 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) | |||
521 | offset = noffset; | 553 | offset = noffset; |
522 | } | 554 | } |
523 | 555 | ||
524 | if (!(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) | 556 | if (WARN_ON_ONCE((ifmgd->flags & IEEE80211_STA_DISABLE_HT) && |
557 | !(ifmgd->flags & IEEE80211_STA_DISABLE_VHT))) | ||
558 | ifmgd->flags |= IEEE80211_STA_DISABLE_VHT; | ||
559 | |||
560 | if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) | ||
525 | ieee80211_add_ht_ie(sdata, skb, assoc_data->ap_ht_param, | 561 | ieee80211_add_ht_ie(sdata, skb, assoc_data->ap_ht_param, |
526 | sband, local->oper_channel, ifmgd->ap_smps); | 562 | sband, chan, sdata->smps_mode); |
527 | 563 | ||
528 | if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) | 564 | if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) |
529 | ieee80211_add_vht_ie(sdata, skb, sband); | 565 | ieee80211_add_vht_ie(sdata, skb, sband); |
@@ -657,18 +693,18 @@ static void ieee80211_chswitch_work(struct work_struct *work) | |||
657 | if (!ifmgd->associated) | 693 | if (!ifmgd->associated) |
658 | goto out; | 694 | goto out; |
659 | 695 | ||
660 | sdata->local->oper_channel = sdata->local->csa_channel; | 696 | sdata->local->_oper_channel = sdata->local->csa_channel; |
661 | if (!sdata->local->ops->channel_switch) { | 697 | if (!sdata->local->ops->channel_switch) { |
662 | /* call "hw_config" only if doing sw channel switch */ | 698 | /* call "hw_config" only if doing sw channel switch */ |
663 | ieee80211_hw_config(sdata->local, | 699 | ieee80211_hw_config(sdata->local, |
664 | IEEE80211_CONF_CHANGE_CHANNEL); | 700 | IEEE80211_CONF_CHANGE_CHANNEL); |
665 | } else { | 701 | } else { |
666 | /* update the device channel directly */ | 702 | /* update the device channel directly */ |
667 | sdata->local->hw.conf.channel = sdata->local->oper_channel; | 703 | sdata->local->hw.conf.channel = sdata->local->_oper_channel; |
668 | } | 704 | } |
669 | 705 | ||
670 | /* XXX: shouldn't really modify cfg80211-owned data! */ | 706 | /* XXX: shouldn't really modify cfg80211-owned data! */ |
671 | ifmgd->associated->channel = sdata->local->oper_channel; | 707 | ifmgd->associated->channel = sdata->local->_oper_channel; |
672 | 708 | ||
673 | /* XXX: wait for a beacon first? */ | 709 | /* XXX: wait for a beacon first? */ |
674 | ieee80211_wake_queues_by_reason(&sdata->local->hw, | 710 | ieee80211_wake_queues_by_reason(&sdata->local->hw, |
@@ -680,11 +716,8 @@ static void ieee80211_chswitch_work(struct work_struct *work) | |||
680 | 716 | ||
681 | void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success) | 717 | void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success) |
682 | { | 718 | { |
683 | struct ieee80211_sub_if_data *sdata; | 719 | struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); |
684 | struct ieee80211_if_managed *ifmgd; | 720 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
685 | |||
686 | sdata = vif_to_sdata(vif); | ||
687 | ifmgd = &sdata->u.mgd; | ||
688 | 721 | ||
689 | trace_api_chswitch_done(sdata, success); | 722 | trace_api_chswitch_done(sdata, success); |
690 | if (!success) { | 723 | if (!success) { |
@@ -723,6 +756,7 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, | |||
723 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | 756 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
724 | int new_freq = ieee80211_channel_to_frequency(sw_elem->new_ch_num, | 757 | int new_freq = ieee80211_channel_to_frequency(sw_elem->new_ch_num, |
725 | cbss->channel->band); | 758 | cbss->channel->band); |
759 | struct ieee80211_chanctx *chanctx; | ||
726 | 760 | ||
727 | ASSERT_MGD_MTX(ifmgd); | 761 | ASSERT_MGD_MTX(ifmgd); |
728 | 762 | ||
@@ -748,10 +782,35 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, | |||
748 | return; | 782 | return; |
749 | } | 783 | } |
750 | 784 | ||
751 | sdata->local->csa_channel = new_ch; | ||
752 | |||
753 | ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED; | 785 | ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED; |
754 | 786 | ||
787 | if (sdata->local->use_chanctx) { | ||
788 | sdata_info(sdata, | ||
789 | "not handling channel switch with channel contexts\n"); | ||
790 | ieee80211_queue_work(&sdata->local->hw, | ||
791 | &ifmgd->csa_connection_drop_work); | ||
792 | return; | ||
793 | } | ||
794 | |||
795 | mutex_lock(&sdata->local->chanctx_mtx); | ||
796 | if (WARN_ON(!rcu_access_pointer(sdata->vif.chanctx_conf))) { | ||
797 | mutex_unlock(&sdata->local->chanctx_mtx); | ||
798 | return; | ||
799 | } | ||
800 | chanctx = container_of(rcu_access_pointer(sdata->vif.chanctx_conf), | ||
801 | struct ieee80211_chanctx, conf); | ||
802 | if (chanctx->refcount > 1) { | ||
803 | sdata_info(sdata, | ||
804 | "channel switch with multiple interfaces on the same channel, disconnecting\n"); | ||
805 | ieee80211_queue_work(&sdata->local->hw, | ||
806 | &ifmgd->csa_connection_drop_work); | ||
807 | mutex_unlock(&sdata->local->chanctx_mtx); | ||
808 | return; | ||
809 | } | ||
810 | mutex_unlock(&sdata->local->chanctx_mtx); | ||
811 | |||
812 | sdata->local->csa_channel = new_ch; | ||
813 | |||
755 | if (sw_elem->mode) | 814 | if (sw_elem->mode) |
756 | ieee80211_stop_queues_by_reason(&sdata->local->hw, | 815 | ieee80211_stop_queues_by_reason(&sdata->local->hw, |
757 | IEEE80211_QUEUE_STOP_REASON_CSA); | 816 | IEEE80211_QUEUE_STOP_REASON_CSA); |
@@ -778,10 +837,10 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, | |||
778 | cbss->beacon_interval)); | 837 | cbss->beacon_interval)); |
779 | } | 838 | } |
780 | 839 | ||
781 | static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata, | 840 | static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata, |
782 | struct ieee80211_channel *channel, | 841 | struct ieee80211_channel *channel, |
783 | const u8 *country_ie, u8 country_ie_len, | 842 | const u8 *country_ie, u8 country_ie_len, |
784 | const u8 *pwr_constr_elem) | 843 | const u8 *pwr_constr_elem) |
785 | { | 844 | { |
786 | struct ieee80211_country_ie_triplet *triplet; | 845 | struct ieee80211_country_ie_triplet *triplet; |
787 | int chan = ieee80211_frequency_to_channel(channel->center_freq); | 846 | int chan = ieee80211_frequency_to_channel(channel->center_freq); |
@@ -790,7 +849,7 @@ static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata, | |||
790 | 849 | ||
791 | /* Invalid IE */ | 850 | /* Invalid IE */ |
792 | if (country_ie_len % 2 || country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) | 851 | if (country_ie_len % 2 || country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) |
793 | return; | 852 | return 0; |
794 | 853 | ||
795 | triplet = (void *)(country_ie + 3); | 854 | triplet = (void *)(country_ie + 3); |
796 | country_ie_len -= 3; | 855 | country_ie_len -= 3; |
@@ -831,19 +890,21 @@ static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata, | |||
831 | } | 890 | } |
832 | 891 | ||
833 | if (!have_chan_pwr) | 892 | if (!have_chan_pwr) |
834 | return; | 893 | return 0; |
835 | 894 | ||
836 | new_ap_level = max_t(int, 0, chan_pwr - *pwr_constr_elem); | 895 | new_ap_level = max_t(int, 0, chan_pwr - *pwr_constr_elem); |
837 | 896 | ||
838 | if (sdata->local->ap_power_level == new_ap_level) | 897 | if (sdata->ap_power_level == new_ap_level) |
839 | return; | 898 | return 0; |
840 | 899 | ||
841 | sdata_info(sdata, | 900 | sdata_info(sdata, |
842 | "Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n", | 901 | "Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n", |
843 | new_ap_level, chan_pwr, *pwr_constr_elem, | 902 | new_ap_level, chan_pwr, *pwr_constr_elem, |
844 | sdata->u.mgd.bssid); | 903 | sdata->u.mgd.bssid); |
845 | sdata->local->ap_power_level = new_ap_level; | 904 | sdata->ap_power_level = new_ap_level; |
846 | ieee80211_hw_config(sdata->local, 0); | 905 | if (__ieee80211_recalc_txpower(sdata)) |
906 | return BSS_CHANGED_TXPOWER; | ||
907 | return 0; | ||
847 | } | 908 | } |
848 | 909 | ||
849 | void ieee80211_enable_dyn_ps(struct ieee80211_vif *vif) | 910 | void ieee80211_enable_dyn_ps(struct ieee80211_vif *vif) |
@@ -1280,7 +1341,7 @@ static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata, | |||
1280 | } | 1341 | } |
1281 | 1342 | ||
1282 | use_short_slot = !!(capab & WLAN_CAPABILITY_SHORT_SLOT_TIME); | 1343 | use_short_slot = !!(capab & WLAN_CAPABILITY_SHORT_SLOT_TIME); |
1283 | if (sdata->local->oper_channel->band == IEEE80211_BAND_5GHZ) | 1344 | if (ieee80211_get_sdata_band(sdata) == IEEE80211_BAND_5GHZ) |
1284 | use_short_slot = true; | 1345 | use_short_slot = true; |
1285 | 1346 | ||
1286 | if (use_protection != bss_conf->use_cts_prot) { | 1347 | if (use_protection != bss_conf->use_cts_prot) { |
@@ -1321,6 +1382,29 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, | |||
1321 | 1382 | ||
1322 | sdata->u.mgd.flags |= IEEE80211_STA_RESET_SIGNAL_AVE; | 1383 | sdata->u.mgd.flags |= IEEE80211_STA_RESET_SIGNAL_AVE; |
1323 | 1384 | ||
1385 | if (sdata->vif.p2p) { | ||
1386 | const struct cfg80211_bss_ies *ies; | ||
1387 | |||
1388 | rcu_read_lock(); | ||
1389 | ies = rcu_dereference(cbss->ies); | ||
1390 | if (ies) { | ||
1391 | u8 noa[2]; | ||
1392 | int ret; | ||
1393 | |||
1394 | ret = cfg80211_get_p2p_attr( | ||
1395 | ies->data, ies->len, | ||
1396 | IEEE80211_P2P_ATTR_ABSENCE_NOTICE, | ||
1397 | noa, sizeof(noa)); | ||
1398 | if (ret >= 2) { | ||
1399 | bss_conf->p2p_oppps = noa[1] & 0x80; | ||
1400 | bss_conf->p2p_ctwindow = noa[1] & 0x7f; | ||
1401 | bss_info_changed |= BSS_CHANGED_P2P_PS; | ||
1402 | sdata->u.mgd.p2p_noa_index = noa[0]; | ||
1403 | } | ||
1404 | } | ||
1405 | rcu_read_unlock(); | ||
1406 | } | ||
1407 | |||
1324 | /* just to be sure */ | 1408 | /* just to be sure */ |
1325 | ieee80211_stop_poll(sdata); | 1409 | ieee80211_stop_poll(sdata); |
1326 | 1410 | ||
@@ -1350,7 +1434,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, | |||
1350 | ieee80211_recalc_ps(local, -1); | 1434 | ieee80211_recalc_ps(local, -1); |
1351 | mutex_unlock(&local->iflist_mtx); | 1435 | mutex_unlock(&local->iflist_mtx); |
1352 | 1436 | ||
1353 | ieee80211_recalc_smps(local); | 1437 | ieee80211_recalc_smps(sdata); |
1354 | ieee80211_recalc_ps_vif(sdata); | 1438 | ieee80211_recalc_ps_vif(sdata); |
1355 | 1439 | ||
1356 | netif_tx_start_all_queues(sdata->dev); | 1440 | netif_tx_start_all_queues(sdata->dev); |
@@ -1443,11 +1527,14 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, | |||
1443 | changed |= BSS_CHANGED_ASSOC; | 1527 | changed |= BSS_CHANGED_ASSOC; |
1444 | sdata->vif.bss_conf.assoc = false; | 1528 | sdata->vif.bss_conf.assoc = false; |
1445 | 1529 | ||
1530 | sdata->vif.bss_conf.p2p_ctwindow = 0; | ||
1531 | sdata->vif.bss_conf.p2p_oppps = false; | ||
1532 | |||
1446 | /* on the next assoc, re-program HT parameters */ | 1533 | /* on the next assoc, re-program HT parameters */ |
1447 | memset(&ifmgd->ht_capa, 0, sizeof(ifmgd->ht_capa)); | 1534 | memset(&ifmgd->ht_capa, 0, sizeof(ifmgd->ht_capa)); |
1448 | memset(&ifmgd->ht_capa_mask, 0, sizeof(ifmgd->ht_capa_mask)); | 1535 | memset(&ifmgd->ht_capa_mask, 0, sizeof(ifmgd->ht_capa_mask)); |
1449 | 1536 | ||
1450 | local->ap_power_level = 0; | 1537 | sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL; |
1451 | 1538 | ||
1452 | del_timer_sync(&local->dynamic_ps_timer); | 1539 | del_timer_sync(&local->dynamic_ps_timer); |
1453 | cancel_work_sync(&local->dynamic_ps_enable_work); | 1540 | cancel_work_sync(&local->dynamic_ps_enable_work); |
@@ -1465,10 +1552,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, | |||
1465 | changed |= BSS_CHANGED_BSSID | BSS_CHANGED_HT; | 1552 | changed |= BSS_CHANGED_BSSID | BSS_CHANGED_HT; |
1466 | ieee80211_bss_info_change_notify(sdata, changed); | 1553 | ieee80211_bss_info_change_notify(sdata, changed); |
1467 | 1554 | ||
1468 | /* channel(_type) changes are handled by ieee80211_hw_config */ | ||
1469 | WARN_ON(!ieee80211_set_channel_type(local, sdata, NL80211_CHAN_NO_HT)); | ||
1470 | ieee80211_hw_config(local, 0); | ||
1471 | |||
1472 | /* disassociated - set to defaults now */ | 1555 | /* disassociated - set to defaults now */ |
1473 | ieee80211_set_wmm_default(sdata, false); | 1556 | ieee80211_set_wmm_default(sdata, false); |
1474 | 1557 | ||
@@ -1478,6 +1561,9 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, | |||
1478 | del_timer_sync(&sdata->u.mgd.chswitch_timer); | 1561 | del_timer_sync(&sdata->u.mgd.chswitch_timer); |
1479 | 1562 | ||
1480 | sdata->u.mgd.timers_running = 0; | 1563 | sdata->u.mgd.timers_running = 0; |
1564 | |||
1565 | ifmgd->flags = 0; | ||
1566 | ieee80211_vif_release_channel(sdata); | ||
1481 | } | 1567 | } |
1482 | 1568 | ||
1483 | void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, | 1569 | void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, |
@@ -1581,6 +1667,7 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata) | |||
1581 | } else { | 1667 | } else { |
1582 | int ssid_len; | 1668 | int ssid_len; |
1583 | 1669 | ||
1670 | rcu_read_lock(); | ||
1584 | ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID); | 1671 | ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID); |
1585 | if (WARN_ON_ONCE(ssid == NULL)) | 1672 | if (WARN_ON_ONCE(ssid == NULL)) |
1586 | ssid_len = 0; | 1673 | ssid_len = 0; |
@@ -1589,7 +1676,8 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata) | |||
1589 | 1676 | ||
1590 | ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid_len, NULL, | 1677 | ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid_len, NULL, |
1591 | 0, (u32) -1, true, false, | 1678 | 0, (u32) -1, true, false, |
1592 | ifmgd->associated->channel); | 1679 | ifmgd->associated->channel, false); |
1680 | rcu_read_unlock(); | ||
1593 | } | 1681 | } |
1594 | 1682 | ||
1595 | ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms); | 1683 | ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms); |
@@ -1685,6 +1773,7 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw, | |||
1685 | else | 1773 | else |
1686 | return NULL; | 1774 | return NULL; |
1687 | 1775 | ||
1776 | rcu_read_lock(); | ||
1688 | ssid = ieee80211_bss_get_ie(cbss, WLAN_EID_SSID); | 1777 | ssid = ieee80211_bss_get_ie(cbss, WLAN_EID_SSID); |
1689 | if (WARN_ON_ONCE(ssid == NULL)) | 1778 | if (WARN_ON_ONCE(ssid == NULL)) |
1690 | ssid_len = 0; | 1779 | ssid_len = 0; |
@@ -1692,10 +1781,10 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw, | |||
1692 | ssid_len = ssid[1]; | 1781 | ssid_len = ssid[1]; |
1693 | 1782 | ||
1694 | skb = ieee80211_build_probe_req(sdata, cbss->bssid, | 1783 | skb = ieee80211_build_probe_req(sdata, cbss->bssid, |
1695 | (u32) -1, | 1784 | (u32) -1, cbss->channel, |
1696 | sdata->local->oper_channel, | ||
1697 | ssid + 2, ssid_len, | 1785 | ssid + 2, ssid_len, |
1698 | NULL, 0, true); | 1786 | NULL, 0, true); |
1787 | rcu_read_unlock(); | ||
1699 | 1788 | ||
1700 | return skb; | 1789 | return skb; |
1701 | } | 1790 | } |
@@ -1804,6 +1893,8 @@ static void ieee80211_destroy_auth_data(struct ieee80211_sub_if_data *sdata, | |||
1804 | 1893 | ||
1805 | memset(sdata->u.mgd.bssid, 0, ETH_ALEN); | 1894 | memset(sdata->u.mgd.bssid, 0, ETH_ALEN); |
1806 | ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID); | 1895 | ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID); |
1896 | sdata->u.mgd.flags = 0; | ||
1897 | ieee80211_vif_release_channel(sdata); | ||
1807 | } | 1898 | } |
1808 | 1899 | ||
1809 | cfg80211_put_bss(auth_data->bss); | 1900 | cfg80211_put_bss(auth_data->bss); |
@@ -1824,7 +1915,7 @@ static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata, | |||
1824 | return; | 1915 | return; |
1825 | auth_data->expected_transaction = 4; | 1916 | auth_data->expected_transaction = 4; |
1826 | drv_mgd_prepare_tx(sdata->local, sdata); | 1917 | drv_mgd_prepare_tx(sdata->local, sdata); |
1827 | ieee80211_send_auth(sdata, 3, auth_data->algorithm, | 1918 | ieee80211_send_auth(sdata, 3, auth_data->algorithm, 0, |
1828 | elems.challenge - 2, elems.challenge_len + 2, | 1919 | elems.challenge - 2, elems.challenge_len + 2, |
1829 | auth_data->bss->bssid, auth_data->bss->bssid, | 1920 | auth_data->bss->bssid, auth_data->bss->bssid, |
1830 | auth_data->key, auth_data->key_len, | 1921 | auth_data->key, auth_data->key_len, |
@@ -1858,8 +1949,13 @@ ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, | |||
1858 | status_code = le16_to_cpu(mgmt->u.auth.status_code); | 1949 | status_code = le16_to_cpu(mgmt->u.auth.status_code); |
1859 | 1950 | ||
1860 | if (auth_alg != ifmgd->auth_data->algorithm || | 1951 | if (auth_alg != ifmgd->auth_data->algorithm || |
1861 | auth_transaction != ifmgd->auth_data->expected_transaction) | 1952 | auth_transaction != ifmgd->auth_data->expected_transaction) { |
1953 | sdata_info(sdata, "%pM unexpected authentication state: alg %d (expected %d) transact %d (expected %d)\n", | ||
1954 | mgmt->sa, auth_alg, ifmgd->auth_data->algorithm, | ||
1955 | auth_transaction, | ||
1956 | ifmgd->auth_data->expected_transaction); | ||
1862 | return RX_MGMT_NONE; | 1957 | return RX_MGMT_NONE; |
1958 | } | ||
1863 | 1959 | ||
1864 | if (status_code != WLAN_STATUS_SUCCESS) { | 1960 | if (status_code != WLAN_STATUS_SUCCESS) { |
1865 | sdata_info(sdata, "%pM denied authentication (status %d)\n", | 1961 | sdata_info(sdata, "%pM denied authentication (status %d)\n", |
@@ -1872,6 +1968,7 @@ ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, | |||
1872 | case WLAN_AUTH_OPEN: | 1968 | case WLAN_AUTH_OPEN: |
1873 | case WLAN_AUTH_LEAP: | 1969 | case WLAN_AUTH_LEAP: |
1874 | case WLAN_AUTH_FT: | 1970 | case WLAN_AUTH_FT: |
1971 | case WLAN_AUTH_SAE: | ||
1875 | break; | 1972 | break; |
1876 | case WLAN_AUTH_SHARED_KEY: | 1973 | case WLAN_AUTH_SHARED_KEY: |
1877 | if (ifmgd->auth_data->expected_transaction != 4) { | 1974 | if (ifmgd->auth_data->expected_transaction != 4) { |
@@ -1891,6 +1988,15 @@ ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, | |||
1891 | ifmgd->auth_data->timeout = jiffies + IEEE80211_AUTH_WAIT_ASSOC; | 1988 | ifmgd->auth_data->timeout = jiffies + IEEE80211_AUTH_WAIT_ASSOC; |
1892 | run_again(ifmgd, ifmgd->auth_data->timeout); | 1989 | run_again(ifmgd, ifmgd->auth_data->timeout); |
1893 | 1990 | ||
1991 | if (ifmgd->auth_data->algorithm == WLAN_AUTH_SAE && | ||
1992 | ifmgd->auth_data->expected_transaction != 2) { | ||
1993 | /* | ||
1994 | * Report auth frame to user space for processing since another | ||
1995 | * round of Authentication frames is still needed. | ||
1996 | */ | ||
1997 | return RX_MGMT_CFG80211_RX_AUTH; | ||
1998 | } | ||
1999 | |||
1894 | /* move station state to auth */ | 2000 | /* move station state to auth */ |
1895 | mutex_lock(&sdata->local->sta_mtx); | 2001 | mutex_lock(&sdata->local->sta_mtx); |
1896 | sta = sta_info_get(sdata, bssid); | 2002 | sta = sta_info_get(sdata, bssid); |
@@ -2030,6 +2136,8 @@ static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata, | |||
2030 | 2136 | ||
2031 | memset(sdata->u.mgd.bssid, 0, ETH_ALEN); | 2137 | memset(sdata->u.mgd.bssid, 0, ETH_ALEN); |
2032 | ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID); | 2138 | ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID); |
2139 | sdata->u.mgd.flags = 0; | ||
2140 | ieee80211_vif_release_channel(sdata); | ||
2033 | } | 2141 | } |
2034 | 2142 | ||
2035 | kfree(assoc_data); | 2143 | kfree(assoc_data); |
@@ -2091,15 +2199,20 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, | |||
2091 | return false; | 2199 | return false; |
2092 | } | 2200 | } |
2093 | 2201 | ||
2094 | sband = local->hw.wiphy->bands[local->oper_channel->band]; | 2202 | sband = local->hw.wiphy->bands[ieee80211_get_sdata_band(sdata)]; |
2095 | 2203 | ||
2096 | if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) | 2204 | if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) |
2097 | ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, | 2205 | ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, |
2098 | elems.ht_cap_elem, &sta->sta.ht_cap); | 2206 | elems.ht_cap_elem, &sta->sta.ht_cap); |
2099 | 2207 | ||
2100 | sta->supports_40mhz = | 2208 | sta->supports_40mhz = |
2101 | sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40; | 2209 | sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40; |
2102 | 2210 | ||
2211 | if (elems.vht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) | ||
2212 | ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband, | ||
2213 | elems.vht_cap_elem, | ||
2214 | &sta->sta.vht_cap); | ||
2215 | |||
2103 | rate_control_rate_init(sta); | 2216 | rate_control_rate_init(sta); |
2104 | 2217 | ||
2105 | if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED) | 2218 | if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED) |
@@ -2140,7 +2253,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, | |||
2140 | changed |= BSS_CHANGED_QOS; | 2253 | changed |= BSS_CHANGED_QOS; |
2141 | 2254 | ||
2142 | if (elems.ht_operation && elems.wmm_param && | 2255 | if (elems.ht_operation && elems.wmm_param && |
2143 | !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) | 2256 | !(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) |
2144 | changed |= ieee80211_config_ht_tx(sdata, elems.ht_operation, | 2257 | changed |= ieee80211_config_ht_tx(sdata, elems.ht_operation, |
2145 | cbss->bssid, false); | 2258 | cbss->bssid, false); |
2146 | 2259 | ||
@@ -2247,9 +2360,9 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, | |||
2247 | 2360 | ||
2248 | return RX_MGMT_CFG80211_RX_ASSOC; | 2361 | return RX_MGMT_CFG80211_RX_ASSOC; |
2249 | } | 2362 | } |
2363 | |||
2250 | static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, | 2364 | static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, |
2251 | struct ieee80211_mgmt *mgmt, | 2365 | struct ieee80211_mgmt *mgmt, size_t len, |
2252 | size_t len, | ||
2253 | struct ieee80211_rx_status *rx_status, | 2366 | struct ieee80211_rx_status *rx_status, |
2254 | struct ieee802_11_elems *elems, | 2367 | struct ieee802_11_elems *elems, |
2255 | bool beacon) | 2368 | bool beacon) |
@@ -2369,8 +2482,10 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, | |||
2369 | size_t baselen; | 2482 | size_t baselen; |
2370 | struct ieee802_11_elems elems; | 2483 | struct ieee802_11_elems elems; |
2371 | struct ieee80211_local *local = sdata->local; | 2484 | struct ieee80211_local *local = sdata->local; |
2485 | struct ieee80211_chanctx_conf *chanctx_conf; | ||
2486 | struct ieee80211_channel *chan; | ||
2372 | u32 changed = 0; | 2487 | u32 changed = 0; |
2373 | bool erp_valid, directed_tim = false; | 2488 | bool erp_valid; |
2374 | u8 erp_value = 0; | 2489 | u8 erp_value = 0; |
2375 | u32 ncrc; | 2490 | u32 ncrc; |
2376 | u8 *bssid; | 2491 | u8 *bssid; |
@@ -2382,8 +2497,19 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, | |||
2382 | if (baselen > len) | 2497 | if (baselen > len) |
2383 | return; | 2498 | return; |
2384 | 2499 | ||
2385 | if (rx_status->freq != local->oper_channel->center_freq) | 2500 | rcu_read_lock(); |
2501 | chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); | ||
2502 | if (!chanctx_conf) { | ||
2503 | rcu_read_unlock(); | ||
2504 | return; | ||
2505 | } | ||
2506 | |||
2507 | if (rx_status->freq != chanctx_conf->def.chan->center_freq) { | ||
2508 | rcu_read_unlock(); | ||
2386 | return; | 2509 | return; |
2510 | } | ||
2511 | chan = chanctx_conf->def.chan; | ||
2512 | rcu_read_unlock(); | ||
2387 | 2513 | ||
2388 | if (ifmgd->assoc_data && !ifmgd->assoc_data->have_beacon && | 2514 | if (ifmgd->assoc_data && !ifmgd->assoc_data->have_beacon && |
2389 | ether_addr_equal(mgmt->bssid, ifmgd->assoc_data->bss->bssid)) { | 2515 | ether_addr_equal(mgmt->bssid, ifmgd->assoc_data->bss->bssid)) { |
@@ -2490,11 +2616,10 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, | |||
2490 | len - baselen, &elems, | 2616 | len - baselen, &elems, |
2491 | care_about_ies, ncrc); | 2617 | care_about_ies, ncrc); |
2492 | 2618 | ||
2493 | if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) | ||
2494 | directed_tim = ieee80211_check_tim(elems.tim, elems.tim_len, | ||
2495 | ifmgd->aid); | ||
2496 | |||
2497 | if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) { | 2619 | if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) { |
2620 | bool directed_tim = ieee80211_check_tim(elems.tim, | ||
2621 | elems.tim_len, | ||
2622 | ifmgd->aid); | ||
2498 | if (directed_tim) { | 2623 | if (directed_tim) { |
2499 | if (local->hw.conf.dynamic_ps_timeout > 0) { | 2624 | if (local->hw.conf.dynamic_ps_timeout > 0) { |
2500 | if (local->hw.conf.flags & IEEE80211_CONF_PS) { | 2625 | if (local->hw.conf.flags & IEEE80211_CONF_PS) { |
@@ -2519,6 +2644,27 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, | |||
2519 | } | 2644 | } |
2520 | } | 2645 | } |
2521 | 2646 | ||
2647 | if (sdata->vif.p2p) { | ||
2648 | u8 noa[2]; | ||
2649 | int ret; | ||
2650 | |||
2651 | ret = cfg80211_get_p2p_attr(mgmt->u.beacon.variable, | ||
2652 | len - baselen, | ||
2653 | IEEE80211_P2P_ATTR_ABSENCE_NOTICE, | ||
2654 | noa, sizeof(noa)); | ||
2655 | if (ret >= 2 && sdata->u.mgd.p2p_noa_index != noa[0]) { | ||
2656 | bss_conf->p2p_oppps = noa[1] & 0x80; | ||
2657 | bss_conf->p2p_ctwindow = noa[1] & 0x7f; | ||
2658 | changed |= BSS_CHANGED_P2P_PS; | ||
2659 | sdata->u.mgd.p2p_noa_index = noa[0]; | ||
2660 | /* | ||
2661 | * make sure we update all information, the CRC | ||
2662 | * mechanism doesn't look at P2P attributes. | ||
2663 | */ | ||
2664 | ifmgd->beacon_crc_valid = false; | ||
2665 | } | ||
2666 | } | ||
2667 | |||
2522 | if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid) | 2668 | if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid) |
2523 | return; | 2669 | return; |
2524 | ifmgd->beacon_crc = ncrc; | 2670 | ifmgd->beacon_crc = ncrc; |
@@ -2543,22 +2689,17 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, | |||
2543 | 2689 | ||
2544 | 2690 | ||
2545 | if (elems.ht_cap_elem && elems.ht_operation && elems.wmm_param && | 2691 | if (elems.ht_cap_elem && elems.ht_operation && elems.wmm_param && |
2546 | !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) { | 2692 | !(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) |
2547 | struct ieee80211_supported_band *sband; | ||
2548 | |||
2549 | sband = local->hw.wiphy->bands[local->oper_channel->band]; | ||
2550 | |||
2551 | changed |= ieee80211_config_ht_tx(sdata, elems.ht_operation, | 2693 | changed |= ieee80211_config_ht_tx(sdata, elems.ht_operation, |
2552 | bssid, true); | 2694 | bssid, true); |
2553 | } | ||
2554 | 2695 | ||
2555 | if (elems.country_elem && elems.pwr_constr_elem && | 2696 | if (elems.country_elem && elems.pwr_constr_elem && |
2556 | mgmt->u.probe_resp.capab_info & | 2697 | mgmt->u.probe_resp.capab_info & |
2557 | cpu_to_le16(WLAN_CAPABILITY_SPECTRUM_MGMT)) | 2698 | cpu_to_le16(WLAN_CAPABILITY_SPECTRUM_MGMT)) |
2558 | ieee80211_handle_pwr_constr(sdata, local->oper_channel, | 2699 | changed |= ieee80211_handle_pwr_constr(sdata, chan, |
2559 | elems.country_elem, | 2700 | elems.country_elem, |
2560 | elems.country_elem_len, | 2701 | elems.country_elem_len, |
2561 | elems.pwr_constr_elem); | 2702 | elems.pwr_constr_elem); |
2562 | 2703 | ||
2563 | ieee80211_bss_info_change_notify(sdata, changed); | 2704 | ieee80211_bss_info_change_notify(sdata, changed); |
2564 | } | 2705 | } |
@@ -2703,13 +2844,23 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata) | |||
2703 | drv_mgd_prepare_tx(local, sdata); | 2844 | drv_mgd_prepare_tx(local, sdata); |
2704 | 2845 | ||
2705 | if (auth_data->bss->proberesp_ies) { | 2846 | if (auth_data->bss->proberesp_ies) { |
2847 | u16 trans = 1; | ||
2848 | u16 status = 0; | ||
2849 | |||
2706 | sdata_info(sdata, "send auth to %pM (try %d/%d)\n", | 2850 | sdata_info(sdata, "send auth to %pM (try %d/%d)\n", |
2707 | auth_data->bss->bssid, auth_data->tries, | 2851 | auth_data->bss->bssid, auth_data->tries, |
2708 | IEEE80211_AUTH_MAX_TRIES); | 2852 | IEEE80211_AUTH_MAX_TRIES); |
2709 | 2853 | ||
2710 | auth_data->expected_transaction = 2; | 2854 | auth_data->expected_transaction = 2; |
2711 | ieee80211_send_auth(sdata, 1, auth_data->algorithm, | 2855 | |
2712 | auth_data->ie, auth_data->ie_len, | 2856 | if (auth_data->algorithm == WLAN_AUTH_SAE) { |
2857 | trans = auth_data->sae_trans; | ||
2858 | status = auth_data->sae_status; | ||
2859 | auth_data->expected_transaction = trans; | ||
2860 | } | ||
2861 | |||
2862 | ieee80211_send_auth(sdata, trans, auth_data->algorithm, status, | ||
2863 | auth_data->data, auth_data->data_len, | ||
2713 | auth_data->bss->bssid, | 2864 | auth_data->bss->bssid, |
2714 | auth_data->bss->bssid, NULL, 0, 0); | 2865 | auth_data->bss->bssid, NULL, 0, 0); |
2715 | } else { | 2866 | } else { |
@@ -2719,16 +2870,20 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata) | |||
2719 | auth_data->bss->bssid, auth_data->tries, | 2870 | auth_data->bss->bssid, auth_data->tries, |
2720 | IEEE80211_AUTH_MAX_TRIES); | 2871 | IEEE80211_AUTH_MAX_TRIES); |
2721 | 2872 | ||
2873 | rcu_read_lock(); | ||
2722 | ssidie = ieee80211_bss_get_ie(auth_data->bss, WLAN_EID_SSID); | 2874 | ssidie = ieee80211_bss_get_ie(auth_data->bss, WLAN_EID_SSID); |
2723 | if (!ssidie) | 2875 | if (!ssidie) { |
2876 | rcu_read_unlock(); | ||
2724 | return -EINVAL; | 2877 | return -EINVAL; |
2878 | } | ||
2725 | /* | 2879 | /* |
2726 | * Direct probe is sent to broadcast address as some APs | 2880 | * Direct probe is sent to broadcast address as some APs |
2727 | * will not answer to direct packet in unassociated state. | 2881 | * will not answer to direct packet in unassociated state. |
2728 | */ | 2882 | */ |
2729 | ieee80211_send_probe_req(sdata, NULL, ssidie + 2, ssidie[1], | 2883 | ieee80211_send_probe_req(sdata, NULL, ssidie + 2, ssidie[1], |
2730 | NULL, 0, (u32) -1, true, false, | 2884 | NULL, 0, (u32) -1, true, false, |
2731 | auth_data->bss->channel); | 2885 | auth_data->bss->channel, false); |
2886 | rcu_read_unlock(); | ||
2732 | } | 2887 | } |
2733 | 2888 | ||
2734 | auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT; | 2889 | auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT; |
@@ -3058,90 +3213,313 @@ int ieee80211_max_network_latency(struct notifier_block *nb, | |||
3058 | return 0; | 3213 | return 0; |
3059 | } | 3214 | } |
3060 | 3215 | ||
3216 | static u32 chandef_downgrade(struct cfg80211_chan_def *c) | ||
3217 | { | ||
3218 | u32 ret; | ||
3219 | int tmp; | ||
3220 | |||
3221 | switch (c->width) { | ||
3222 | case NL80211_CHAN_WIDTH_20: | ||
3223 | c->width = NL80211_CHAN_WIDTH_20_NOHT; | ||
3224 | ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; | ||
3225 | break; | ||
3226 | case NL80211_CHAN_WIDTH_40: | ||
3227 | c->width = NL80211_CHAN_WIDTH_20; | ||
3228 | c->center_freq1 = c->chan->center_freq; | ||
3229 | ret = IEEE80211_STA_DISABLE_40MHZ | | ||
3230 | IEEE80211_STA_DISABLE_VHT; | ||
3231 | break; | ||
3232 | case NL80211_CHAN_WIDTH_80: | ||
3233 | tmp = (30 + c->chan->center_freq - c->center_freq1)/20; | ||
3234 | /* n_P40 */ | ||
3235 | tmp /= 2; | ||
3236 | /* freq_P40 */ | ||
3237 | c->center_freq1 = c->center_freq1 - 20 + 40 * tmp; | ||
3238 | c->width = NL80211_CHAN_WIDTH_40; | ||
3239 | ret = IEEE80211_STA_DISABLE_VHT; | ||
3240 | break; | ||
3241 | case NL80211_CHAN_WIDTH_80P80: | ||
3242 | c->center_freq2 = 0; | ||
3243 | c->width = NL80211_CHAN_WIDTH_80; | ||
3244 | ret = IEEE80211_STA_DISABLE_80P80MHZ | | ||
3245 | IEEE80211_STA_DISABLE_160MHZ; | ||
3246 | break; | ||
3247 | case NL80211_CHAN_WIDTH_160: | ||
3248 | /* n_P20 */ | ||
3249 | tmp = (70 + c->chan->center_freq - c->center_freq1)/20; | ||
3250 | /* n_P80 */ | ||
3251 | tmp /= 4; | ||
3252 | c->center_freq1 = c->center_freq1 - 40 + 80 * tmp; | ||
3253 | c->width = NL80211_CHAN_WIDTH_80; | ||
3254 | ret = IEEE80211_STA_DISABLE_80P80MHZ | | ||
3255 | IEEE80211_STA_DISABLE_160MHZ; | ||
3256 | break; | ||
3257 | default: | ||
3258 | case NL80211_CHAN_WIDTH_20_NOHT: | ||
3259 | WARN_ON_ONCE(1); | ||
3260 | c->width = NL80211_CHAN_WIDTH_20_NOHT; | ||
3261 | ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; | ||
3262 | break; | ||
3263 | } | ||
3264 | |||
3265 | WARN_ON_ONCE(!cfg80211_chandef_valid(c)); | ||
3266 | |||
3267 | return ret; | ||
3268 | } | ||
3269 | |||
3270 | static u32 | ||
3271 | ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, | ||
3272 | struct ieee80211_supported_band *sband, | ||
3273 | struct ieee80211_channel *channel, | ||
3274 | const struct ieee80211_ht_operation *ht_oper, | ||
3275 | const struct ieee80211_vht_operation *vht_oper, | ||
3276 | struct cfg80211_chan_def *chandef) | ||
3277 | { | ||
3278 | struct cfg80211_chan_def vht_chandef; | ||
3279 | u32 ht_cfreq, ret; | ||
3280 | |||
3281 | chandef->chan = channel; | ||
3282 | chandef->width = NL80211_CHAN_WIDTH_20_NOHT; | ||
3283 | chandef->center_freq1 = channel->center_freq; | ||
3284 | chandef->center_freq2 = 0; | ||
3285 | |||
3286 | if (!ht_oper || !sband->ht_cap.ht_supported) { | ||
3287 | ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; | ||
3288 | goto out; | ||
3289 | } | ||
3290 | |||
3291 | chandef->width = NL80211_CHAN_WIDTH_20; | ||
3292 | |||
3293 | ht_cfreq = ieee80211_channel_to_frequency(ht_oper->primary_chan, | ||
3294 | channel->band); | ||
3295 | /* check that channel matches the right operating channel */ | ||
3296 | if (channel->center_freq != ht_cfreq) { | ||
3297 | /* | ||
3298 | * It's possible that some APs are confused here; | ||
3299 | * Netgear WNDR3700 sometimes reports 4 higher than | ||
3300 | * the actual channel in association responses, but | ||
3301 | * since we look at probe response/beacon data here | ||
3302 | * it should be OK. | ||
3303 | */ | ||
3304 | sdata_info(sdata, | ||
3305 | "Wrong control channel: center-freq: %d ht-cfreq: %d ht->primary_chan: %d band: %d - Disabling HT\n", | ||
3306 | channel->center_freq, ht_cfreq, | ||
3307 | ht_oper->primary_chan, channel->band); | ||
3308 | ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; | ||
3309 | goto out; | ||
3310 | } | ||
3311 | |||
3312 | /* check 40 MHz support, if we have it */ | ||
3313 | if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) { | ||
3314 | switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { | ||
3315 | case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: | ||
3316 | chandef->width = NL80211_CHAN_WIDTH_40; | ||
3317 | chandef->center_freq1 += 10; | ||
3318 | break; | ||
3319 | case IEEE80211_HT_PARAM_CHA_SEC_BELOW: | ||
3320 | chandef->width = NL80211_CHAN_WIDTH_40; | ||
3321 | chandef->center_freq1 -= 10; | ||
3322 | break; | ||
3323 | } | ||
3324 | } else { | ||
3325 | /* 40 MHz (and 80 MHz) must be supported for VHT */ | ||
3326 | ret = IEEE80211_STA_DISABLE_VHT; | ||
3327 | goto out; | ||
3328 | } | ||
3329 | |||
3330 | if (!vht_oper || !sband->vht_cap.vht_supported) { | ||
3331 | ret = IEEE80211_STA_DISABLE_VHT; | ||
3332 | goto out; | ||
3333 | } | ||
3334 | |||
3335 | vht_chandef.chan = channel; | ||
3336 | vht_chandef.center_freq1 = | ||
3337 | ieee80211_channel_to_frequency(vht_oper->center_freq_seg1_idx, | ||
3338 | channel->band); | ||
3339 | vht_chandef.center_freq2 = 0; | ||
3340 | |||
3341 | if (vht_oper->center_freq_seg2_idx) | ||
3342 | vht_chandef.center_freq2 = | ||
3343 | ieee80211_channel_to_frequency( | ||
3344 | vht_oper->center_freq_seg2_idx, | ||
3345 | channel->band); | ||
3346 | |||
3347 | switch (vht_oper->chan_width) { | ||
3348 | case IEEE80211_VHT_CHANWIDTH_USE_HT: | ||
3349 | vht_chandef.width = chandef->width; | ||
3350 | break; | ||
3351 | case IEEE80211_VHT_CHANWIDTH_80MHZ: | ||
3352 | vht_chandef.width = NL80211_CHAN_WIDTH_80; | ||
3353 | break; | ||
3354 | case IEEE80211_VHT_CHANWIDTH_160MHZ: | ||
3355 | vht_chandef.width = NL80211_CHAN_WIDTH_160; | ||
3356 | break; | ||
3357 | case IEEE80211_VHT_CHANWIDTH_80P80MHZ: | ||
3358 | vht_chandef.width = NL80211_CHAN_WIDTH_80P80; | ||
3359 | break; | ||
3360 | default: | ||
3361 | sdata_info(sdata, | ||
3362 | "AP VHT operation IE has invalid channel width (%d), disable VHT\n", | ||
3363 | vht_oper->chan_width); | ||
3364 | ret = IEEE80211_STA_DISABLE_VHT; | ||
3365 | goto out; | ||
3366 | } | ||
3367 | |||
3368 | if (!cfg80211_chandef_valid(&vht_chandef)) { | ||
3369 | sdata_info(sdata, | ||
3370 | "AP VHT information is invalid, disable VHT\n"); | ||
3371 | ret = IEEE80211_STA_DISABLE_VHT; | ||
3372 | goto out; | ||
3373 | } | ||
3374 | |||
3375 | if (cfg80211_chandef_identical(chandef, &vht_chandef)) { | ||
3376 | ret = 0; | ||
3377 | goto out; | ||
3378 | } | ||
3379 | |||
3380 | if (!cfg80211_chandef_compatible(chandef, &vht_chandef)) { | ||
3381 | sdata_info(sdata, | ||
3382 | "AP VHT information doesn't match HT, disable VHT\n"); | ||
3383 | ret = IEEE80211_STA_DISABLE_VHT; | ||
3384 | goto out; | ||
3385 | } | ||
3386 | |||
3387 | *chandef = vht_chandef; | ||
3388 | |||
3389 | ret = 0; | ||
3390 | |||
3391 | while (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef, | ||
3392 | IEEE80211_CHAN_DISABLED)) { | ||
3393 | if (WARN_ON(chandef->width == NL80211_CHAN_WIDTH_20_NOHT)) { | ||
3394 | ret = IEEE80211_STA_DISABLE_HT | | ||
3395 | IEEE80211_STA_DISABLE_VHT; | ||
3396 | goto out; | ||
3397 | } | ||
3398 | |||
3399 | ret = chandef_downgrade(chandef); | ||
3400 | } | ||
3401 | |||
3402 | if (chandef->width != vht_chandef.width) | ||
3403 | sdata_info(sdata, | ||
3404 | "local regulatory prevented using AP HT/VHT configuration, downgraded\n"); | ||
3405 | |||
3406 | out: | ||
3407 | WARN_ON_ONCE(!cfg80211_chandef_valid(chandef)); | ||
3408 | return ret; | ||
3409 | } | ||
3410 | |||
3411 | static u8 ieee80211_ht_vht_rx_chains(struct ieee80211_sub_if_data *sdata, | ||
3412 | struct cfg80211_bss *cbss) | ||
3413 | { | ||
3414 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | ||
3415 | const u8 *ht_cap_ie, *vht_cap_ie; | ||
3416 | const struct ieee80211_ht_cap *ht_cap; | ||
3417 | const struct ieee80211_vht_cap *vht_cap; | ||
3418 | u8 chains = 1; | ||
3419 | |||
3420 | if (ifmgd->flags & IEEE80211_STA_DISABLE_HT) | ||
3421 | return chains; | ||
3422 | |||
3423 | ht_cap_ie = ieee80211_bss_get_ie(cbss, WLAN_EID_HT_CAPABILITY); | ||
3424 | if (ht_cap_ie && ht_cap_ie[1] >= sizeof(*ht_cap)) { | ||
3425 | ht_cap = (void *)(ht_cap_ie + 2); | ||
3426 | chains = ieee80211_mcs_to_chains(&ht_cap->mcs); | ||
3427 | /* | ||
3428 | * TODO: use "Tx Maximum Number Spatial Streams Supported" and | ||
3429 | * "Tx Unequal Modulation Supported" fields. | ||
3430 | */ | ||
3431 | } | ||
3432 | |||
3433 | if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT) | ||
3434 | return chains; | ||
3435 | |||
3436 | vht_cap_ie = ieee80211_bss_get_ie(cbss, WLAN_EID_VHT_CAPABILITY); | ||
3437 | if (vht_cap_ie && vht_cap_ie[1] >= sizeof(*vht_cap)) { | ||
3438 | u8 nss; | ||
3439 | u16 tx_mcs_map; | ||
3440 | |||
3441 | vht_cap = (void *)(vht_cap_ie + 2); | ||
3442 | tx_mcs_map = le16_to_cpu(vht_cap->supp_mcs.tx_mcs_map); | ||
3443 | for (nss = 8; nss > 0; nss--) { | ||
3444 | if (((tx_mcs_map >> (2 * (nss - 1))) & 3) != | ||
3445 | IEEE80211_VHT_MCS_NOT_SUPPORTED) | ||
3446 | break; | ||
3447 | } | ||
3448 | /* TODO: use "Tx Highest Supported Long GI Data Rate" field? */ | ||
3449 | chains = max(chains, nss); | ||
3450 | } | ||
3451 | |||
3452 | return chains; | ||
3453 | } | ||
3454 | |||
3061 | static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, | 3455 | static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, |
3062 | struct cfg80211_bss *cbss) | 3456 | struct cfg80211_bss *cbss) |
3063 | { | 3457 | { |
3064 | struct ieee80211_local *local = sdata->local; | 3458 | struct ieee80211_local *local = sdata->local; |
3065 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | 3459 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
3066 | int ht_cfreq; | ||
3067 | enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; | ||
3068 | const u8 *ht_oper_ie; | ||
3069 | const struct ieee80211_ht_operation *ht_oper = NULL; | 3460 | const struct ieee80211_ht_operation *ht_oper = NULL; |
3461 | const struct ieee80211_vht_operation *vht_oper = NULL; | ||
3070 | struct ieee80211_supported_band *sband; | 3462 | struct ieee80211_supported_band *sband; |
3463 | struct cfg80211_chan_def chandef; | ||
3464 | int ret; | ||
3071 | 3465 | ||
3072 | sband = local->hw.wiphy->bands[cbss->channel->band]; | 3466 | sband = local->hw.wiphy->bands[cbss->channel->band]; |
3073 | 3467 | ||
3074 | ifmgd->flags &= ~IEEE80211_STA_DISABLE_40MHZ; | 3468 | ifmgd->flags &= ~(IEEE80211_STA_DISABLE_40MHZ | |
3469 | IEEE80211_STA_DISABLE_80P80MHZ | | ||
3470 | IEEE80211_STA_DISABLE_160MHZ); | ||
3471 | |||
3472 | rcu_read_lock(); | ||
3473 | |||
3474 | if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT) && | ||
3475 | sband->ht_cap.ht_supported) { | ||
3476 | const u8 *ht_oper_ie; | ||
3075 | 3477 | ||
3076 | if (sband->ht_cap.ht_supported) { | 3478 | ht_oper_ie = ieee80211_bss_get_ie(cbss, WLAN_EID_HT_OPERATION); |
3077 | ht_oper_ie = cfg80211_find_ie(WLAN_EID_HT_OPERATION, | ||
3078 | cbss->information_elements, | ||
3079 | cbss->len_information_elements); | ||
3080 | if (ht_oper_ie && ht_oper_ie[1] >= sizeof(*ht_oper)) | 3479 | if (ht_oper_ie && ht_oper_ie[1] >= sizeof(*ht_oper)) |
3081 | ht_oper = (void *)(ht_oper_ie + 2); | 3480 | ht_oper = (void *)(ht_oper_ie + 2); |
3082 | } | 3481 | } |
3083 | 3482 | ||
3084 | if (ht_oper) { | 3483 | if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT) && |
3085 | ht_cfreq = ieee80211_channel_to_frequency(ht_oper->primary_chan, | 3484 | sband->vht_cap.vht_supported) { |
3086 | cbss->channel->band); | 3485 | const u8 *vht_oper_ie; |
3087 | /* check that channel matches the right operating channel */ | 3486 | |
3088 | if (cbss->channel->center_freq != ht_cfreq) { | 3487 | vht_oper_ie = ieee80211_bss_get_ie(cbss, |
3089 | /* | 3488 | WLAN_EID_VHT_OPERATION); |
3090 | * It's possible that some APs are confused here; | 3489 | if (vht_oper_ie && vht_oper_ie[1] >= sizeof(*vht_oper)) |
3091 | * Netgear WNDR3700 sometimes reports 4 higher than | 3490 | vht_oper = (void *)(vht_oper_ie + 2); |
3092 | * the actual channel in association responses, but | 3491 | if (vht_oper && !ht_oper) { |
3093 | * since we look at probe response/beacon data here | 3492 | vht_oper = NULL; |
3094 | * it should be OK. | ||
3095 | */ | ||
3096 | sdata_info(sdata, | 3493 | sdata_info(sdata, |
3097 | "Wrong control channel: center-freq: %d ht-cfreq: %d ht->primary_chan: %d band: %d - Disabling HT\n", | 3494 | "AP advertised VHT without HT, disabling both\n"); |
3098 | cbss->channel->center_freq, | 3495 | sdata->flags |= IEEE80211_STA_DISABLE_HT; |
3099 | ht_cfreq, ht_oper->primary_chan, | 3496 | sdata->flags |= IEEE80211_STA_DISABLE_VHT; |
3100 | cbss->channel->band); | ||
3101 | ht_oper = NULL; | ||
3102 | } else { | ||
3103 | channel_type = NL80211_CHAN_HT20; | ||
3104 | } | 3497 | } |
3105 | } | 3498 | } |
3106 | 3499 | ||
3107 | if (ht_oper && sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) { | 3500 | ifmgd->flags |= ieee80211_determine_chantype(sdata, sband, |
3108 | /* | 3501 | cbss->channel, |
3109 | * cfg80211 already verified that the channel itself can | 3502 | ht_oper, vht_oper, |
3110 | * be used, but it didn't check that we can do the right | 3503 | &chandef); |
3111 | * HT type, so do that here as well. If HT40 isn't allowed | ||
3112 | * on this channel, disable 40 MHz operation. | ||
3113 | */ | ||
3114 | 3504 | ||
3115 | switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { | 3505 | sdata->needed_rx_chains = min(ieee80211_ht_vht_rx_chains(sdata, cbss), |
3116 | case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: | 3506 | local->rx_chains); |
3117 | if (cbss->channel->flags & IEEE80211_CHAN_NO_HT40PLUS) | ||
3118 | ifmgd->flags |= IEEE80211_STA_DISABLE_40MHZ; | ||
3119 | else | ||
3120 | channel_type = NL80211_CHAN_HT40PLUS; | ||
3121 | break; | ||
3122 | case IEEE80211_HT_PARAM_CHA_SEC_BELOW: | ||
3123 | if (cbss->channel->flags & IEEE80211_CHAN_NO_HT40MINUS) | ||
3124 | ifmgd->flags |= IEEE80211_STA_DISABLE_40MHZ; | ||
3125 | else | ||
3126 | channel_type = NL80211_CHAN_HT40MINUS; | ||
3127 | break; | ||
3128 | } | ||
3129 | } | ||
3130 | 3507 | ||
3131 | if (!ieee80211_set_channel_type(local, sdata, channel_type)) { | 3508 | rcu_read_unlock(); |
3132 | /* can only fail due to HT40+/- mismatch */ | ||
3133 | channel_type = NL80211_CHAN_HT20; | ||
3134 | sdata_info(sdata, | ||
3135 | "disabling 40 MHz due to multi-vif mismatch\n"); | ||
3136 | ifmgd->flags |= IEEE80211_STA_DISABLE_40MHZ; | ||
3137 | WARN_ON(!ieee80211_set_channel_type(local, sdata, | ||
3138 | channel_type)); | ||
3139 | } | ||
3140 | 3509 | ||
3141 | local->oper_channel = cbss->channel; | 3510 | /* will change later if needed */ |
3142 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); | 3511 | sdata->smps_mode = IEEE80211_SMPS_OFF; |
3143 | 3512 | ||
3144 | return 0; | 3513 | /* |
3514 | * If this fails (possibly due to channel context sharing | ||
3515 | * on incompatible channels, e.g. 80+80 and 160 sharing the | ||
3516 | * same control channel) try to use a smaller bandwidth. | ||
3517 | */ | ||
3518 | ret = ieee80211_vif_use_channel(sdata, &chandef, | ||
3519 | IEEE80211_CHANCTX_SHARED); | ||
3520 | while (ret && chandef.width != NL80211_CHAN_WIDTH_20_NOHT) | ||
3521 | ifmgd->flags |= chandef_downgrade(&chandef); | ||
3522 | return ret; | ||
3145 | } | 3523 | } |
3146 | 3524 | ||
3147 | static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, | 3525 | static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, |
@@ -3211,7 +3589,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, | |||
3211 | sdata->vif.bss_conf.basic_rates = basic_rates; | 3589 | sdata->vif.bss_conf.basic_rates = basic_rates; |
3212 | 3590 | ||
3213 | /* cf. IEEE 802.11 9.2.12 */ | 3591 | /* cf. IEEE 802.11 9.2.12 */ |
3214 | if (local->oper_channel->band == IEEE80211_BAND_2GHZ && | 3592 | if (cbss->channel->band == IEEE80211_BAND_2GHZ && |
3215 | have_higher_than_11mbit) | 3593 | have_higher_than_11mbit) |
3216 | sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE; | 3594 | sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE; |
3217 | else | 3595 | else |
@@ -3273,19 +3651,33 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, | |||
3273 | case NL80211_AUTHTYPE_NETWORK_EAP: | 3651 | case NL80211_AUTHTYPE_NETWORK_EAP: |
3274 | auth_alg = WLAN_AUTH_LEAP; | 3652 | auth_alg = WLAN_AUTH_LEAP; |
3275 | break; | 3653 | break; |
3654 | case NL80211_AUTHTYPE_SAE: | ||
3655 | auth_alg = WLAN_AUTH_SAE; | ||
3656 | break; | ||
3276 | default: | 3657 | default: |
3277 | return -EOPNOTSUPP; | 3658 | return -EOPNOTSUPP; |
3278 | } | 3659 | } |
3279 | 3660 | ||
3280 | auth_data = kzalloc(sizeof(*auth_data) + req->ie_len, GFP_KERNEL); | 3661 | auth_data = kzalloc(sizeof(*auth_data) + req->sae_data_len + |
3662 | req->ie_len, GFP_KERNEL); | ||
3281 | if (!auth_data) | 3663 | if (!auth_data) |
3282 | return -ENOMEM; | 3664 | return -ENOMEM; |
3283 | 3665 | ||
3284 | auth_data->bss = req->bss; | 3666 | auth_data->bss = req->bss; |
3285 | 3667 | ||
3668 | if (req->sae_data_len >= 4) { | ||
3669 | __le16 *pos = (__le16 *) req->sae_data; | ||
3670 | auth_data->sae_trans = le16_to_cpu(pos[0]); | ||
3671 | auth_data->sae_status = le16_to_cpu(pos[1]); | ||
3672 | memcpy(auth_data->data, req->sae_data + 4, | ||
3673 | req->sae_data_len - 4); | ||
3674 | auth_data->data_len += req->sae_data_len - 4; | ||
3675 | } | ||
3676 | |||
3286 | if (req->ie && req->ie_len) { | 3677 | if (req->ie && req->ie_len) { |
3287 | memcpy(auth_data->ie, req->ie, req->ie_len); | 3678 | memcpy(&auth_data->data[auth_data->data_len], |
3288 | auth_data->ie_len = req->ie_len; | 3679 | req->ie, req->ie_len); |
3680 | auth_data->data_len += req->ie_len; | ||
3289 | } | 3681 | } |
3290 | 3682 | ||
3291 | if (req->key && req->key_len) { | 3683 | if (req->key && req->key_len) { |
@@ -3355,14 +3747,21 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, | |||
3355 | const u8 *ssidie, *ht_ie; | 3747 | const u8 *ssidie, *ht_ie; |
3356 | int i, err; | 3748 | int i, err; |
3357 | 3749 | ||
3358 | ssidie = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID); | ||
3359 | if (!ssidie) | ||
3360 | return -EINVAL; | ||
3361 | |||
3362 | assoc_data = kzalloc(sizeof(*assoc_data) + req->ie_len, GFP_KERNEL); | 3750 | assoc_data = kzalloc(sizeof(*assoc_data) + req->ie_len, GFP_KERNEL); |
3363 | if (!assoc_data) | 3751 | if (!assoc_data) |
3364 | return -ENOMEM; | 3752 | return -ENOMEM; |
3365 | 3753 | ||
3754 | rcu_read_lock(); | ||
3755 | ssidie = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID); | ||
3756 | if (!ssidie) { | ||
3757 | rcu_read_unlock(); | ||
3758 | kfree(assoc_data); | ||
3759 | return -EINVAL; | ||
3760 | } | ||
3761 | memcpy(assoc_data->ssid, ssidie + 2, ssidie[1]); | ||
3762 | assoc_data->ssid_len = ssidie[1]; | ||
3763 | rcu_read_unlock(); | ||
3764 | |||
3366 | mutex_lock(&ifmgd->mtx); | 3765 | mutex_lock(&ifmgd->mtx); |
3367 | 3766 | ||
3368 | if (ifmgd->associated) | 3767 | if (ifmgd->associated) |
@@ -3388,13 +3787,6 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, | |||
3388 | 3787 | ||
3389 | /* prepare assoc data */ | 3788 | /* prepare assoc data */ |
3390 | 3789 | ||
3391 | /* | ||
3392 | * keep only the 40 MHz disable bit set as it might have | ||
3393 | * been set during authentication already, all other bits | ||
3394 | * should be reset for a new connection | ||
3395 | */ | ||
3396 | ifmgd->flags &= IEEE80211_STA_DISABLE_40MHZ; | ||
3397 | |||
3398 | ifmgd->beacon_crc_valid = false; | 3790 | ifmgd->beacon_crc_valid = false; |
3399 | 3791 | ||
3400 | /* | 3792 | /* |
@@ -3408,7 +3800,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, | |||
3408 | if (req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP40 || | 3800 | if (req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP40 || |
3409 | req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_TKIP || | 3801 | req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_TKIP || |
3410 | req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP104) { | 3802 | req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP104) { |
3411 | ifmgd->flags |= IEEE80211_STA_DISABLE_11N; | 3803 | ifmgd->flags |= IEEE80211_STA_DISABLE_HT; |
3412 | ifmgd->flags |= IEEE80211_STA_DISABLE_VHT; | 3804 | ifmgd->flags |= IEEE80211_STA_DISABLE_VHT; |
3413 | netdev_info(sdata->dev, | 3805 | netdev_info(sdata->dev, |
3414 | "disabling HT/VHT due to WEP/TKIP use\n"); | 3806 | "disabling HT/VHT due to WEP/TKIP use\n"); |
@@ -3416,7 +3808,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, | |||
3416 | } | 3808 | } |
3417 | 3809 | ||
3418 | if (req->flags & ASSOC_REQ_DISABLE_HT) { | 3810 | if (req->flags & ASSOC_REQ_DISABLE_HT) { |
3419 | ifmgd->flags |= IEEE80211_STA_DISABLE_11N; | 3811 | ifmgd->flags |= IEEE80211_STA_DISABLE_HT; |
3420 | ifmgd->flags |= IEEE80211_STA_DISABLE_VHT; | 3812 | ifmgd->flags |= IEEE80211_STA_DISABLE_VHT; |
3421 | } | 3813 | } |
3422 | 3814 | ||
@@ -3424,7 +3816,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, | |||
3424 | sband = local->hw.wiphy->bands[req->bss->channel->band]; | 3816 | sband = local->hw.wiphy->bands[req->bss->channel->band]; |
3425 | if (!sband->ht_cap.ht_supported || | 3817 | if (!sband->ht_cap.ht_supported || |
3426 | local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used) { | 3818 | local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used) { |
3427 | ifmgd->flags |= IEEE80211_STA_DISABLE_11N; | 3819 | ifmgd->flags |= IEEE80211_STA_DISABLE_HT; |
3428 | if (!bss->wmm_used) | 3820 | if (!bss->wmm_used) |
3429 | netdev_info(sdata->dev, | 3821 | netdev_info(sdata->dev, |
3430 | "disabling HT as WMM/QoS is not supported by the AP\n"); | 3822 | "disabling HT as WMM/QoS is not supported by the AP\n"); |
@@ -3452,11 +3844,11 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, | |||
3452 | 3844 | ||
3453 | if (ifmgd->req_smps == IEEE80211_SMPS_AUTOMATIC) { | 3845 | if (ifmgd->req_smps == IEEE80211_SMPS_AUTOMATIC) { |
3454 | if (ifmgd->powersave) | 3846 | if (ifmgd->powersave) |
3455 | ifmgd->ap_smps = IEEE80211_SMPS_DYNAMIC; | 3847 | sdata->smps_mode = IEEE80211_SMPS_DYNAMIC; |
3456 | else | 3848 | else |
3457 | ifmgd->ap_smps = IEEE80211_SMPS_OFF; | 3849 | sdata->smps_mode = IEEE80211_SMPS_OFF; |
3458 | } else | 3850 | } else |
3459 | ifmgd->ap_smps = ifmgd->req_smps; | 3851 | sdata->smps_mode = ifmgd->req_smps; |
3460 | 3852 | ||
3461 | assoc_data->capability = req->bss->capability; | 3853 | assoc_data->capability = req->bss->capability; |
3462 | assoc_data->wmm = bss->wmm_used && | 3854 | assoc_data->wmm = bss->wmm_used && |
@@ -3464,12 +3856,14 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, | |||
3464 | assoc_data->supp_rates = bss->supp_rates; | 3856 | assoc_data->supp_rates = bss->supp_rates; |
3465 | assoc_data->supp_rates_len = bss->supp_rates_len; | 3857 | assoc_data->supp_rates_len = bss->supp_rates_len; |
3466 | 3858 | ||
3859 | rcu_read_lock(); | ||
3467 | ht_ie = ieee80211_bss_get_ie(req->bss, WLAN_EID_HT_OPERATION); | 3860 | ht_ie = ieee80211_bss_get_ie(req->bss, WLAN_EID_HT_OPERATION); |
3468 | if (ht_ie && ht_ie[1] >= sizeof(struct ieee80211_ht_operation)) | 3861 | if (ht_ie && ht_ie[1] >= sizeof(struct ieee80211_ht_operation)) |
3469 | assoc_data->ap_ht_param = | 3862 | assoc_data->ap_ht_param = |
3470 | ((struct ieee80211_ht_operation *)(ht_ie + 2))->ht_param; | 3863 | ((struct ieee80211_ht_operation *)(ht_ie + 2))->ht_param; |
3471 | else | 3864 | else |
3472 | ifmgd->flags |= IEEE80211_STA_DISABLE_11N; | 3865 | ifmgd->flags |= IEEE80211_STA_DISABLE_HT; |
3866 | rcu_read_unlock(); | ||
3473 | 3867 | ||
3474 | if (bss->wmm_used && bss->uapsd_supported && | 3868 | if (bss->wmm_used && bss->uapsd_supported && |
3475 | (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)) { | 3869 | (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)) { |
@@ -3480,9 +3874,6 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, | |||
3480 | ifmgd->flags &= ~IEEE80211_STA_UAPSD_ENABLED; | 3874 | ifmgd->flags &= ~IEEE80211_STA_UAPSD_ENABLED; |
3481 | } | 3875 | } |
3482 | 3876 | ||
3483 | memcpy(assoc_data->ssid, ssidie + 2, ssidie[1]); | ||
3484 | assoc_data->ssid_len = ssidie[1]; | ||
3485 | |||
3486 | if (req->prev_bssid) | 3877 | if (req->prev_bssid) |
3487 | memcpy(assoc_data->prev_bssid, req->prev_bssid, ETH_ALEN); | 3878 | memcpy(assoc_data->prev_bssid, req->prev_bssid, ETH_ALEN); |
3488 | 3879 | ||
@@ -3560,40 +3951,44 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, | |||
3560 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | 3951 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
3561 | u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; | 3952 | u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; |
3562 | bool tx = !req->local_state_change; | 3953 | bool tx = !req->local_state_change; |
3954 | bool sent_frame = false; | ||
3563 | 3955 | ||
3564 | mutex_lock(&ifmgd->mtx); | 3956 | mutex_lock(&ifmgd->mtx); |
3565 | 3957 | ||
3566 | if (ifmgd->auth_data) { | ||
3567 | ieee80211_destroy_auth_data(sdata, false); | ||
3568 | mutex_unlock(&ifmgd->mtx); | ||
3569 | return 0; | ||
3570 | } | ||
3571 | |||
3572 | sdata_info(sdata, | 3958 | sdata_info(sdata, |
3573 | "deauthenticating from %pM by local choice (reason=%d)\n", | 3959 | "deauthenticating from %pM by local choice (reason=%d)\n", |
3574 | req->bssid, req->reason_code); | 3960 | req->bssid, req->reason_code); |
3575 | 3961 | ||
3576 | if (ifmgd->associated && | 3962 | if (ifmgd->auth_data) { |
3577 | ether_addr_equal(ifmgd->associated->bssid, req->bssid)) { | ||
3578 | ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, | ||
3579 | req->reason_code, tx, frame_buf); | ||
3580 | } else { | ||
3581 | drv_mgd_prepare_tx(sdata->local, sdata); | 3963 | drv_mgd_prepare_tx(sdata->local, sdata); |
3582 | ieee80211_send_deauth_disassoc(sdata, req->bssid, | 3964 | ieee80211_send_deauth_disassoc(sdata, req->bssid, |
3583 | IEEE80211_STYPE_DEAUTH, | 3965 | IEEE80211_STYPE_DEAUTH, |
3584 | req->reason_code, tx, | 3966 | req->reason_code, tx, |
3585 | frame_buf); | 3967 | frame_buf); |
3968 | ieee80211_destroy_auth_data(sdata, false); | ||
3969 | mutex_unlock(&ifmgd->mtx); | ||
3970 | |||
3971 | sent_frame = tx; | ||
3972 | goto out; | ||
3586 | } | 3973 | } |
3587 | 3974 | ||
3975 | if (ifmgd->associated && | ||
3976 | ether_addr_equal(ifmgd->associated->bssid, req->bssid)) { | ||
3977 | ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, | ||
3978 | req->reason_code, tx, frame_buf); | ||
3979 | sent_frame = tx; | ||
3980 | } | ||
3588 | mutex_unlock(&ifmgd->mtx); | 3981 | mutex_unlock(&ifmgd->mtx); |
3589 | 3982 | ||
3590 | __cfg80211_send_deauth(sdata->dev, frame_buf, | 3983 | out: |
3591 | IEEE80211_DEAUTH_FRAME_LEN); | ||
3592 | |||
3593 | mutex_lock(&sdata->local->mtx); | 3984 | mutex_lock(&sdata->local->mtx); |
3594 | ieee80211_recalc_idle(sdata->local); | 3985 | ieee80211_recalc_idle(sdata->local); |
3595 | mutex_unlock(&sdata->local->mtx); | 3986 | mutex_unlock(&sdata->local->mtx); |
3596 | 3987 | ||
3988 | if (sent_frame) | ||
3989 | __cfg80211_send_deauth(sdata->dev, frame_buf, | ||
3990 | IEEE80211_DEAUTH_FRAME_LEN); | ||
3991 | |||
3597 | return 0; | 3992 | return 0; |
3598 | } | 3993 | } |
3599 | 3994 | ||