diff options
Diffstat (limited to 'net/mac80211/ibss.c')
-rw-r--r-- | net/mac80211/ibss.c | 608 |
1 files changed, 520 insertions, 88 deletions
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index a12afe77bb26..21a0b8835cb3 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c | |||
@@ -39,7 +39,8 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata, | |||
39 | const int beacon_int, const u32 basic_rates, | 39 | const int beacon_int, const u32 basic_rates, |
40 | const u16 capability, u64 tsf, | 40 | const u16 capability, u64 tsf, |
41 | struct cfg80211_chan_def *chandef, | 41 | struct cfg80211_chan_def *chandef, |
42 | bool *have_higher_than_11mbit) | 42 | bool *have_higher_than_11mbit, |
43 | struct cfg80211_csa_settings *csa_settings) | ||
43 | { | 44 | { |
44 | struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; | 45 | struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; |
45 | struct ieee80211_local *local = sdata->local; | 46 | struct ieee80211_local *local = sdata->local; |
@@ -59,6 +60,7 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata, | |||
59 | 2 + 8 /* max Supported Rates */ + | 60 | 2 + 8 /* max Supported Rates */ + |
60 | 3 /* max DS params */ + | 61 | 3 /* max DS params */ + |
61 | 4 /* IBSS params */ + | 62 | 4 /* IBSS params */ + |
63 | 5 /* Channel Switch Announcement */ + | ||
62 | 2 + (IEEE80211_MAX_SUPP_RATES - 8) + | 64 | 2 + (IEEE80211_MAX_SUPP_RATES - 8) + |
63 | 2 + sizeof(struct ieee80211_ht_cap) + | 65 | 2 + sizeof(struct ieee80211_ht_cap) + |
64 | 2 + sizeof(struct ieee80211_ht_operation) + | 66 | 2 + sizeof(struct ieee80211_ht_operation) + |
@@ -135,6 +137,16 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata, | |||
135 | *pos++ = 0; | 137 | *pos++ = 0; |
136 | *pos++ = 0; | 138 | *pos++ = 0; |
137 | 139 | ||
140 | if (csa_settings) { | ||
141 | *pos++ = WLAN_EID_CHANNEL_SWITCH; | ||
142 | *pos++ = 3; | ||
143 | *pos++ = csa_settings->block_tx ? 1 : 0; | ||
144 | *pos++ = ieee80211_frequency_to_channel( | ||
145 | csa_settings->chandef.chan->center_freq); | ||
146 | sdata->csa_counter_offset_beacon = (pos - presp->head); | ||
147 | *pos++ = csa_settings->count; | ||
148 | } | ||
149 | |||
138 | /* put the remaining rates in WLAN_EID_EXT_SUPP_RATES */ | 150 | /* put the remaining rates in WLAN_EID_EXT_SUPP_RATES */ |
139 | if (rates_n > 8) { | 151 | if (rates_n > 8) { |
140 | *pos++ = WLAN_EID_EXT_SUPP_RATES; | 152 | *pos++ = WLAN_EID_EXT_SUPP_RATES; |
@@ -217,6 +229,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, | |||
217 | struct beacon_data *presp; | 229 | struct beacon_data *presp; |
218 | enum nl80211_bss_scan_width scan_width; | 230 | enum nl80211_bss_scan_width scan_width; |
219 | bool have_higher_than_11mbit; | 231 | bool have_higher_than_11mbit; |
232 | int err; | ||
220 | 233 | ||
221 | sdata_assert_lock(sdata); | 234 | sdata_assert_lock(sdata); |
222 | 235 | ||
@@ -235,6 +248,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, | |||
235 | ieee80211_bss_info_change_notify(sdata, | 248 | ieee80211_bss_info_change_notify(sdata, |
236 | BSS_CHANGED_IBSS | | 249 | BSS_CHANGED_IBSS | |
237 | BSS_CHANGED_BEACON_ENABLED); | 250 | BSS_CHANGED_BEACON_ENABLED); |
251 | drv_leave_ibss(local, sdata); | ||
238 | } | 252 | } |
239 | 253 | ||
240 | presp = rcu_dereference_protected(ifibss->presp, | 254 | presp = rcu_dereference_protected(ifibss->presp, |
@@ -276,7 +290,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, | |||
276 | 290 | ||
277 | presp = ieee80211_ibss_build_presp(sdata, beacon_int, basic_rates, | 291 | presp = ieee80211_ibss_build_presp(sdata, beacon_int, basic_rates, |
278 | capability, tsf, &chandef, | 292 | capability, tsf, &chandef, |
279 | &have_higher_than_11mbit); | 293 | &have_higher_than_11mbit, NULL); |
280 | if (!presp) | 294 | if (!presp) |
281 | return; | 295 | return; |
282 | 296 | ||
@@ -317,11 +331,26 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, | |||
317 | else | 331 | else |
318 | sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE; | 332 | sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE; |
319 | 333 | ||
334 | ieee80211_set_wmm_default(sdata, true); | ||
335 | |||
320 | sdata->vif.bss_conf.ibss_joined = true; | 336 | sdata->vif.bss_conf.ibss_joined = true; |
321 | sdata->vif.bss_conf.ibss_creator = creator; | 337 | sdata->vif.bss_conf.ibss_creator = creator; |
322 | ieee80211_bss_info_change_notify(sdata, bss_change); | ||
323 | 338 | ||
324 | ieee80211_set_wmm_default(sdata, true); | 339 | err = drv_join_ibss(local, sdata); |
340 | if (err) { | ||
341 | sdata->vif.bss_conf.ibss_joined = false; | ||
342 | sdata->vif.bss_conf.ibss_creator = false; | ||
343 | sdata->vif.bss_conf.enable_beacon = false; | ||
344 | sdata->vif.bss_conf.ssid_len = 0; | ||
345 | RCU_INIT_POINTER(ifibss->presp, NULL); | ||
346 | kfree_rcu(presp, rcu_head); | ||
347 | ieee80211_vif_release_channel(sdata); | ||
348 | sdata_info(sdata, "Failed to join IBSS, driver failure: %d\n", | ||
349 | err); | ||
350 | return; | ||
351 | } | ||
352 | |||
353 | ieee80211_bss_info_change_notify(sdata, bss_change); | ||
325 | 354 | ||
326 | ifibss->state = IEEE80211_IBSS_MLME_JOINED; | 355 | ifibss->state = IEEE80211_IBSS_MLME_JOINED; |
327 | mod_timer(&ifibss->timer, | 356 | mod_timer(&ifibss->timer, |
@@ -416,6 +445,169 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, | |||
416 | tsf, false); | 445 | tsf, false); |
417 | } | 446 | } |
418 | 447 | ||
448 | static int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata, | ||
449 | struct cfg80211_csa_settings *csa_settings) | ||
450 | { | ||
451 | struct sk_buff *skb; | ||
452 | struct ieee80211_mgmt *mgmt; | ||
453 | struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; | ||
454 | struct ieee80211_local *local = sdata->local; | ||
455 | int freq; | ||
456 | int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.chan_switch) + | ||
457 | sizeof(mgmt->u.action.u.chan_switch); | ||
458 | u8 *pos; | ||
459 | |||
460 | skb = dev_alloc_skb(local->tx_headroom + hdr_len + | ||
461 | 5 + /* channel switch announcement element */ | ||
462 | 3); /* secondary channel offset element */ | ||
463 | if (!skb) | ||
464 | return -1; | ||
465 | |||
466 | skb_reserve(skb, local->tx_headroom); | ||
467 | mgmt = (struct ieee80211_mgmt *)skb_put(skb, hdr_len); | ||
468 | memset(mgmt, 0, hdr_len); | ||
469 | mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | | ||
470 | IEEE80211_STYPE_ACTION); | ||
471 | |||
472 | eth_broadcast_addr(mgmt->da); | ||
473 | memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); | ||
474 | memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN); | ||
475 | mgmt->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT; | ||
476 | mgmt->u.action.u.chan_switch.action_code = WLAN_ACTION_SPCT_CHL_SWITCH; | ||
477 | pos = skb_put(skb, 5); | ||
478 | *pos++ = WLAN_EID_CHANNEL_SWITCH; /* EID */ | ||
479 | *pos++ = 3; /* IE length */ | ||
480 | *pos++ = csa_settings->block_tx ? 1 : 0; /* CSA mode */ | ||
481 | freq = csa_settings->chandef.chan->center_freq; | ||
482 | *pos++ = ieee80211_frequency_to_channel(freq); /* channel */ | ||
483 | *pos++ = csa_settings->count; /* count */ | ||
484 | |||
485 | if (csa_settings->chandef.width == NL80211_CHAN_WIDTH_40) { | ||
486 | enum nl80211_channel_type ch_type; | ||
487 | |||
488 | skb_put(skb, 3); | ||
489 | *pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET; /* EID */ | ||
490 | *pos++ = 1; /* IE length */ | ||
491 | ch_type = cfg80211_get_chandef_type(&csa_settings->chandef); | ||
492 | if (ch_type == NL80211_CHAN_HT40PLUS) | ||
493 | *pos++ = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; | ||
494 | else | ||
495 | *pos++ = IEEE80211_HT_PARAM_CHA_SEC_BELOW; | ||
496 | } | ||
497 | |||
498 | ieee80211_tx_skb(sdata, skb); | ||
499 | return 0; | ||
500 | } | ||
501 | |||
502 | int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata, | ||
503 | struct cfg80211_csa_settings *csa_settings) | ||
504 | { | ||
505 | struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; | ||
506 | struct beacon_data *presp, *old_presp; | ||
507 | struct cfg80211_bss *cbss; | ||
508 | const struct cfg80211_bss_ies *ies; | ||
509 | u16 capability; | ||
510 | u64 tsf; | ||
511 | int ret = 0; | ||
512 | |||
513 | sdata_assert_lock(sdata); | ||
514 | |||
515 | capability = WLAN_CAPABILITY_IBSS; | ||
516 | |||
517 | if (ifibss->privacy) | ||
518 | capability |= WLAN_CAPABILITY_PRIVACY; | ||
519 | |||
520 | cbss = cfg80211_get_bss(sdata->local->hw.wiphy, ifibss->chandef.chan, | ||
521 | ifibss->bssid, ifibss->ssid, | ||
522 | ifibss->ssid_len, WLAN_CAPABILITY_IBSS | | ||
523 | WLAN_CAPABILITY_PRIVACY, | ||
524 | capability); | ||
525 | |||
526 | if (WARN_ON(!cbss)) { | ||
527 | ret = -EINVAL; | ||
528 | goto out; | ||
529 | } | ||
530 | |||
531 | rcu_read_lock(); | ||
532 | ies = rcu_dereference(cbss->ies); | ||
533 | tsf = ies->tsf; | ||
534 | rcu_read_unlock(); | ||
535 | cfg80211_put_bss(sdata->local->hw.wiphy, cbss); | ||
536 | |||
537 | old_presp = rcu_dereference_protected(ifibss->presp, | ||
538 | lockdep_is_held(&sdata->wdev.mtx)); | ||
539 | |||
540 | presp = ieee80211_ibss_build_presp(sdata, | ||
541 | sdata->vif.bss_conf.beacon_int, | ||
542 | sdata->vif.bss_conf.basic_rates, | ||
543 | capability, tsf, &ifibss->chandef, | ||
544 | NULL, csa_settings); | ||
545 | if (!presp) { | ||
546 | ret = -ENOMEM; | ||
547 | goto out; | ||
548 | } | ||
549 | |||
550 | rcu_assign_pointer(ifibss->presp, presp); | ||
551 | if (old_presp) | ||
552 | kfree_rcu(old_presp, rcu_head); | ||
553 | |||
554 | /* it might not send the beacon for a while. send an action frame | ||
555 | * immediately to announce the channel switch. | ||
556 | */ | ||
557 | if (csa_settings) | ||
558 | ieee80211_send_action_csa(sdata, csa_settings); | ||
559 | |||
560 | ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); | ||
561 | out: | ||
562 | return ret; | ||
563 | } | ||
564 | |||
565 | int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata) | ||
566 | { | ||
567 | struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; | ||
568 | struct cfg80211_bss *cbss; | ||
569 | int err; | ||
570 | u16 capability; | ||
571 | |||
572 | sdata_lock(sdata); | ||
573 | /* update cfg80211 bss information with the new channel */ | ||
574 | if (!is_zero_ether_addr(ifibss->bssid)) { | ||
575 | capability = WLAN_CAPABILITY_IBSS; | ||
576 | |||
577 | if (ifibss->privacy) | ||
578 | capability |= WLAN_CAPABILITY_PRIVACY; | ||
579 | |||
580 | cbss = cfg80211_get_bss(sdata->local->hw.wiphy, | ||
581 | ifibss->chandef.chan, | ||
582 | ifibss->bssid, ifibss->ssid, | ||
583 | ifibss->ssid_len, WLAN_CAPABILITY_IBSS | | ||
584 | WLAN_CAPABILITY_PRIVACY, | ||
585 | capability); | ||
586 | /* XXX: should not really modify cfg80211 data */ | ||
587 | if (cbss) { | ||
588 | cbss->channel = sdata->local->csa_chandef.chan; | ||
589 | cfg80211_put_bss(sdata->local->hw.wiphy, cbss); | ||
590 | } | ||
591 | } | ||
592 | |||
593 | ifibss->chandef = sdata->local->csa_chandef; | ||
594 | |||
595 | /* generate the beacon */ | ||
596 | err = ieee80211_ibss_csa_beacon(sdata, NULL); | ||
597 | sdata_unlock(sdata); | ||
598 | if (err < 0) | ||
599 | return err; | ||
600 | |||
601 | return 0; | ||
602 | } | ||
603 | |||
604 | void ieee80211_ibss_stop(struct ieee80211_sub_if_data *sdata) | ||
605 | { | ||
606 | struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; | ||
607 | |||
608 | cancel_work_sync(&ifibss->csa_connection_drop_work); | ||
609 | } | ||
610 | |||
419 | static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta) | 611 | static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta) |
420 | __acquires(RCU) | 612 | __acquires(RCU) |
421 | { | 613 | { |
@@ -499,6 +691,295 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, const u8 *bssid, | |||
499 | return ieee80211_ibss_finish_sta(sta); | 691 | return ieee80211_ibss_finish_sta(sta); |
500 | } | 692 | } |
501 | 693 | ||
694 | static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata) | ||
695 | { | ||
696 | struct ieee80211_local *local = sdata->local; | ||
697 | int active = 0; | ||
698 | struct sta_info *sta; | ||
699 | |||
700 | sdata_assert_lock(sdata); | ||
701 | |||
702 | rcu_read_lock(); | ||
703 | |||
704 | list_for_each_entry_rcu(sta, &local->sta_list, list) { | ||
705 | if (sta->sdata == sdata && | ||
706 | time_after(sta->last_rx + IEEE80211_IBSS_MERGE_INTERVAL, | ||
707 | jiffies)) { | ||
708 | active++; | ||
709 | break; | ||
710 | } | ||
711 | } | ||
712 | |||
713 | rcu_read_unlock(); | ||
714 | |||
715 | return active; | ||
716 | } | ||
717 | |||
718 | static void ieee80211_ibss_disconnect(struct ieee80211_sub_if_data *sdata) | ||
719 | { | ||
720 | struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; | ||
721 | struct ieee80211_local *local = sdata->local; | ||
722 | struct cfg80211_bss *cbss; | ||
723 | struct beacon_data *presp; | ||
724 | struct sta_info *sta; | ||
725 | int active_ibss; | ||
726 | u16 capability; | ||
727 | |||
728 | active_ibss = ieee80211_sta_active_ibss(sdata); | ||
729 | |||
730 | if (!active_ibss && !is_zero_ether_addr(ifibss->bssid)) { | ||
731 | capability = WLAN_CAPABILITY_IBSS; | ||
732 | |||
733 | if (ifibss->privacy) | ||
734 | capability |= WLAN_CAPABILITY_PRIVACY; | ||
735 | |||
736 | cbss = cfg80211_get_bss(local->hw.wiphy, ifibss->chandef.chan, | ||
737 | ifibss->bssid, ifibss->ssid, | ||
738 | ifibss->ssid_len, WLAN_CAPABILITY_IBSS | | ||
739 | WLAN_CAPABILITY_PRIVACY, | ||
740 | capability); | ||
741 | |||
742 | if (cbss) { | ||
743 | cfg80211_unlink_bss(local->hw.wiphy, cbss); | ||
744 | cfg80211_put_bss(sdata->local->hw.wiphy, cbss); | ||
745 | } | ||
746 | } | ||
747 | |||
748 | ifibss->state = IEEE80211_IBSS_MLME_SEARCH; | ||
749 | |||
750 | sta_info_flush(sdata); | ||
751 | |||
752 | spin_lock_bh(&ifibss->incomplete_lock); | ||
753 | while (!list_empty(&ifibss->incomplete_stations)) { | ||
754 | sta = list_first_entry(&ifibss->incomplete_stations, | ||
755 | struct sta_info, list); | ||
756 | list_del(&sta->list); | ||
757 | spin_unlock_bh(&ifibss->incomplete_lock); | ||
758 | |||
759 | sta_info_free(local, sta); | ||
760 | spin_lock_bh(&ifibss->incomplete_lock); | ||
761 | } | ||
762 | spin_unlock_bh(&ifibss->incomplete_lock); | ||
763 | |||
764 | netif_carrier_off(sdata->dev); | ||
765 | |||
766 | sdata->vif.bss_conf.ibss_joined = false; | ||
767 | sdata->vif.bss_conf.ibss_creator = false; | ||
768 | sdata->vif.bss_conf.enable_beacon = false; | ||
769 | sdata->vif.bss_conf.ssid_len = 0; | ||
770 | |||
771 | /* remove beacon */ | ||
772 | presp = rcu_dereference_protected(ifibss->presp, | ||
773 | lockdep_is_held(&sdata->wdev.mtx)); | ||
774 | RCU_INIT_POINTER(sdata->u.ibss.presp, NULL); | ||
775 | if (presp) | ||
776 | kfree_rcu(presp, rcu_head); | ||
777 | |||
778 | clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state); | ||
779 | ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED | | ||
780 | BSS_CHANGED_IBSS); | ||
781 | drv_leave_ibss(local, sdata); | ||
782 | ieee80211_vif_release_channel(sdata); | ||
783 | } | ||
784 | |||
785 | static void ieee80211_csa_connection_drop_work(struct work_struct *work) | ||
786 | { | ||
787 | struct ieee80211_sub_if_data *sdata = | ||
788 | container_of(work, struct ieee80211_sub_if_data, | ||
789 | u.ibss.csa_connection_drop_work); | ||
790 | |||
791 | ieee80211_ibss_disconnect(sdata); | ||
792 | synchronize_rcu(); | ||
793 | skb_queue_purge(&sdata->skb_queue); | ||
794 | |||
795 | /* trigger a scan to find another IBSS network to join */ | ||
796 | ieee80211_queue_work(&sdata->local->hw, &sdata->work); | ||
797 | } | ||
798 | |||
799 | static bool | ||
800 | ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata, | ||
801 | struct ieee802_11_elems *elems, | ||
802 | bool beacon) | ||
803 | { | ||
804 | struct cfg80211_csa_settings params; | ||
805 | struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; | ||
806 | struct ieee80211_chanctx_conf *chanctx_conf; | ||
807 | struct ieee80211_chanctx *chanctx; | ||
808 | enum nl80211_channel_type ch_type; | ||
809 | int err, num_chanctx; | ||
810 | u32 sta_flags; | ||
811 | u8 mode; | ||
812 | |||
813 | if (sdata->vif.csa_active) | ||
814 | return true; | ||
815 | |||
816 | if (!sdata->vif.bss_conf.ibss_joined) | ||
817 | return false; | ||
818 | |||
819 | sta_flags = IEEE80211_STA_DISABLE_VHT; | ||
820 | switch (ifibss->chandef.width) { | ||
821 | case NL80211_CHAN_WIDTH_5: | ||
822 | case NL80211_CHAN_WIDTH_10: | ||
823 | case NL80211_CHAN_WIDTH_20_NOHT: | ||
824 | sta_flags |= IEEE80211_STA_DISABLE_HT; | ||
825 | /* fall through */ | ||
826 | case NL80211_CHAN_WIDTH_20: | ||
827 | sta_flags |= IEEE80211_STA_DISABLE_40MHZ; | ||
828 | break; | ||
829 | default: | ||
830 | break; | ||
831 | } | ||
832 | |||
833 | memset(¶ms, 0, sizeof(params)); | ||
834 | err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, | ||
835 | ifibss->chandef.chan->band, | ||
836 | sta_flags, ifibss->bssid, | ||
837 | ¶ms.count, &mode, | ||
838 | ¶ms.chandef); | ||
839 | |||
840 | /* can't switch to destination channel, fail */ | ||
841 | if (err < 0) | ||
842 | goto disconnect; | ||
843 | |||
844 | /* did not contain a CSA */ | ||
845 | if (err) | ||
846 | return false; | ||
847 | |||
848 | if (ifibss->chandef.chan->band != params.chandef.chan->band) | ||
849 | goto disconnect; | ||
850 | |||
851 | switch (ifibss->chandef.width) { | ||
852 | case NL80211_CHAN_WIDTH_20_NOHT: | ||
853 | case NL80211_CHAN_WIDTH_20: | ||
854 | case NL80211_CHAN_WIDTH_40: | ||
855 | /* keep our current HT mode (HT20/HT40+/HT40-), even if | ||
856 | * another mode has been announced. The mode is not adopted | ||
857 | * within the beacon while doing CSA and we should therefore | ||
858 | * keep the mode which we announce. | ||
859 | */ | ||
860 | ch_type = cfg80211_get_chandef_type(&ifibss->chandef); | ||
861 | cfg80211_chandef_create(¶ms.chandef, params.chandef.chan, | ||
862 | ch_type); | ||
863 | break; | ||
864 | case NL80211_CHAN_WIDTH_5: | ||
865 | case NL80211_CHAN_WIDTH_10: | ||
866 | if (params.chandef.width != ifibss->chandef.width) { | ||
867 | sdata_info(sdata, | ||
868 | "IBSS %pM received channel switch from incompatible channel width (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n", | ||
869 | ifibss->bssid, | ||
870 | params.chandef.chan->center_freq, | ||
871 | params.chandef.width, | ||
872 | params.chandef.center_freq1, | ||
873 | params.chandef.center_freq2); | ||
874 | goto disconnect; | ||
875 | } | ||
876 | break; | ||
877 | default: | ||
878 | /* should not happen, sta_flags should prevent VHT modes. */ | ||
879 | WARN_ON(1); | ||
880 | goto disconnect; | ||
881 | } | ||
882 | |||
883 | if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, ¶ms.chandef, | ||
884 | IEEE80211_CHAN_DISABLED)) { | ||
885 | sdata_info(sdata, | ||
886 | "IBSS %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n", | ||
887 | ifibss->bssid, | ||
888 | params.chandef.chan->center_freq, | ||
889 | params.chandef.width, | ||
890 | params.chandef.center_freq1, | ||
891 | params.chandef.center_freq2); | ||
892 | goto disconnect; | ||
893 | } | ||
894 | |||
895 | err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy, | ||
896 | ¶ms.chandef); | ||
897 | if (err < 0) | ||
898 | goto disconnect; | ||
899 | if (err) { | ||
900 | params.radar_required = true; | ||
901 | |||
902 | /* TODO: IBSS-DFS not (yet) supported, disconnect. */ | ||
903 | goto disconnect; | ||
904 | } | ||
905 | |||
906 | rcu_read_lock(); | ||
907 | chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); | ||
908 | if (!chanctx_conf) { | ||
909 | rcu_read_unlock(); | ||
910 | goto disconnect; | ||
911 | } | ||
912 | |||
913 | /* don't handle for multi-VIF cases */ | ||
914 | chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf); | ||
915 | if (chanctx->refcount > 1) { | ||
916 | rcu_read_unlock(); | ||
917 | goto disconnect; | ||
918 | } | ||
919 | num_chanctx = 0; | ||
920 | list_for_each_entry_rcu(chanctx, &sdata->local->chanctx_list, list) | ||
921 | num_chanctx++; | ||
922 | |||
923 | if (num_chanctx > 1) { | ||
924 | rcu_read_unlock(); | ||
925 | goto disconnect; | ||
926 | } | ||
927 | rcu_read_unlock(); | ||
928 | |||
929 | /* all checks done, now perform the channel switch. */ | ||
930 | ibss_dbg(sdata, | ||
931 | "received channel switch announcement to go to channel %d MHz\n", | ||
932 | params.chandef.chan->center_freq); | ||
933 | |||
934 | params.block_tx = !!mode; | ||
935 | |||
936 | ieee80211_ibss_csa_beacon(sdata, ¶ms); | ||
937 | sdata->csa_radar_required = params.radar_required; | ||
938 | |||
939 | if (params.block_tx) | ||
940 | ieee80211_stop_queues_by_reason(&sdata->local->hw, | ||
941 | IEEE80211_MAX_QUEUE_MAP, | ||
942 | IEEE80211_QUEUE_STOP_REASON_CSA); | ||
943 | |||
944 | sdata->local->csa_chandef = params.chandef; | ||
945 | sdata->vif.csa_active = true; | ||
946 | |||
947 | ieee80211_bss_info_change_notify(sdata, err); | ||
948 | drv_channel_switch_beacon(sdata, ¶ms.chandef); | ||
949 | |||
950 | return true; | ||
951 | disconnect: | ||
952 | ibss_dbg(sdata, "Can't handle channel switch, disconnect\n"); | ||
953 | ieee80211_queue_work(&sdata->local->hw, | ||
954 | &ifibss->csa_connection_drop_work); | ||
955 | |||
956 | return true; | ||
957 | } | ||
958 | |||
959 | static void | ||
960 | ieee80211_rx_mgmt_spectrum_mgmt(struct ieee80211_sub_if_data *sdata, | ||
961 | struct ieee80211_mgmt *mgmt, size_t len, | ||
962 | struct ieee80211_rx_status *rx_status, | ||
963 | struct ieee802_11_elems *elems) | ||
964 | { | ||
965 | int required_len; | ||
966 | |||
967 | if (len < IEEE80211_MIN_ACTION_SIZE + 1) | ||
968 | return; | ||
969 | |||
970 | /* CSA is the only action we handle for now */ | ||
971 | if (mgmt->u.action.u.measurement.action_code != | ||
972 | WLAN_ACTION_SPCT_CHL_SWITCH) | ||
973 | return; | ||
974 | |||
975 | required_len = IEEE80211_MIN_ACTION_SIZE + | ||
976 | sizeof(mgmt->u.action.u.chan_switch); | ||
977 | if (len < required_len) | ||
978 | return; | ||
979 | |||
980 | ieee80211_ibss_process_chanswitch(sdata, elems, false); | ||
981 | } | ||
982 | |||
502 | static void ieee80211_rx_mgmt_deauth_ibss(struct ieee80211_sub_if_data *sdata, | 983 | static void ieee80211_rx_mgmt_deauth_ibss(struct ieee80211_sub_if_data *sdata, |
503 | struct ieee80211_mgmt *mgmt, | 984 | struct ieee80211_mgmt *mgmt, |
504 | size_t len) | 985 | size_t len) |
@@ -661,10 +1142,6 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, | |||
661 | 1142 | ||
662 | /* check if we need to merge IBSS */ | 1143 | /* check if we need to merge IBSS */ |
663 | 1144 | ||
664 | /* we use a fixed BSSID */ | ||
665 | if (sdata->u.ibss.fixed_bssid) | ||
666 | goto put_bss; | ||
667 | |||
668 | /* not an IBSS */ | 1145 | /* not an IBSS */ |
669 | if (!(cbss->capability & WLAN_CAPABILITY_IBSS)) | 1146 | if (!(cbss->capability & WLAN_CAPABILITY_IBSS)) |
670 | goto put_bss; | 1147 | goto put_bss; |
@@ -680,10 +1157,18 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, | |||
680 | sdata->u.ibss.ssid_len)) | 1157 | sdata->u.ibss.ssid_len)) |
681 | goto put_bss; | 1158 | goto put_bss; |
682 | 1159 | ||
1160 | /* process channel switch */ | ||
1161 | if (ieee80211_ibss_process_chanswitch(sdata, elems, true)) | ||
1162 | goto put_bss; | ||
1163 | |||
683 | /* same BSSID */ | 1164 | /* same BSSID */ |
684 | if (ether_addr_equal(cbss->bssid, sdata->u.ibss.bssid)) | 1165 | if (ether_addr_equal(cbss->bssid, sdata->u.ibss.bssid)) |
685 | goto put_bss; | 1166 | goto put_bss; |
686 | 1167 | ||
1168 | /* we use a fixed BSSID */ | ||
1169 | if (sdata->u.ibss.fixed_bssid) | ||
1170 | goto put_bss; | ||
1171 | |||
687 | if (ieee80211_have_rx_timestamp(rx_status)) { | 1172 | if (ieee80211_have_rx_timestamp(rx_status)) { |
688 | /* time when timestamp field was received */ | 1173 | /* time when timestamp field was received */ |
689 | rx_timestamp = | 1174 | rx_timestamp = |
@@ -775,30 +1260,6 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata, | |||
775 | ieee80211_queue_work(&local->hw, &sdata->work); | 1260 | ieee80211_queue_work(&local->hw, &sdata->work); |
776 | } | 1261 | } |
777 | 1262 | ||
778 | static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata) | ||
779 | { | ||
780 | struct ieee80211_local *local = sdata->local; | ||
781 | int active = 0; | ||
782 | struct sta_info *sta; | ||
783 | |||
784 | sdata_assert_lock(sdata); | ||
785 | |||
786 | rcu_read_lock(); | ||
787 | |||
788 | list_for_each_entry_rcu(sta, &local->sta_list, list) { | ||
789 | if (sta->sdata == sdata && | ||
790 | time_after(sta->last_rx + IEEE80211_IBSS_MERGE_INTERVAL, | ||
791 | jiffies)) { | ||
792 | active++; | ||
793 | break; | ||
794 | } | ||
795 | } | ||
796 | |||
797 | rcu_read_unlock(); | ||
798 | |||
799 | return active; | ||
800 | } | ||
801 | |||
802 | static void ieee80211_ibss_sta_expire(struct ieee80211_sub_if_data *sdata) | 1263 | static void ieee80211_ibss_sta_expire(struct ieee80211_sub_if_data *sdata) |
803 | { | 1264 | { |
804 | struct ieee80211_local *local = sdata->local; | 1265 | struct ieee80211_local *local = sdata->local; |
@@ -1076,6 +1537,8 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, | |||
1076 | struct ieee80211_rx_status *rx_status; | 1537 | struct ieee80211_rx_status *rx_status; |
1077 | struct ieee80211_mgmt *mgmt; | 1538 | struct ieee80211_mgmt *mgmt; |
1078 | u16 fc; | 1539 | u16 fc; |
1540 | struct ieee802_11_elems elems; | ||
1541 | int ies_len; | ||
1079 | 1542 | ||
1080 | rx_status = IEEE80211_SKB_RXCB(skb); | 1543 | rx_status = IEEE80211_SKB_RXCB(skb); |
1081 | mgmt = (struct ieee80211_mgmt *) skb->data; | 1544 | mgmt = (struct ieee80211_mgmt *) skb->data; |
@@ -1101,6 +1564,27 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, | |||
1101 | case IEEE80211_STYPE_DEAUTH: | 1564 | case IEEE80211_STYPE_DEAUTH: |
1102 | ieee80211_rx_mgmt_deauth_ibss(sdata, mgmt, skb->len); | 1565 | ieee80211_rx_mgmt_deauth_ibss(sdata, mgmt, skb->len); |
1103 | break; | 1566 | break; |
1567 | case IEEE80211_STYPE_ACTION: | ||
1568 | switch (mgmt->u.action.category) { | ||
1569 | case WLAN_CATEGORY_SPECTRUM_MGMT: | ||
1570 | ies_len = skb->len - | ||
1571 | offsetof(struct ieee80211_mgmt, | ||
1572 | u.action.u.chan_switch.variable); | ||
1573 | |||
1574 | if (ies_len < 0) | ||
1575 | break; | ||
1576 | |||
1577 | ieee802_11_parse_elems( | ||
1578 | mgmt->u.action.u.chan_switch.variable, | ||
1579 | ies_len, true, &elems); | ||
1580 | |||
1581 | if (elems.parse_error) | ||
1582 | break; | ||
1583 | |||
1584 | ieee80211_rx_mgmt_spectrum_mgmt(sdata, mgmt, skb->len, | ||
1585 | rx_status, &elems); | ||
1586 | break; | ||
1587 | } | ||
1104 | } | 1588 | } |
1105 | 1589 | ||
1106 | mgmt_out: | 1590 | mgmt_out: |
@@ -1167,6 +1651,8 @@ void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata) | |||
1167 | (unsigned long) sdata); | 1651 | (unsigned long) sdata); |
1168 | INIT_LIST_HEAD(&ifibss->incomplete_stations); | 1652 | INIT_LIST_HEAD(&ifibss->incomplete_stations); |
1169 | spin_lock_init(&ifibss->incomplete_lock); | 1653 | spin_lock_init(&ifibss->incomplete_lock); |
1654 | INIT_WORK(&ifibss->csa_connection_drop_work, | ||
1655 | ieee80211_csa_connection_drop_work); | ||
1170 | } | 1656 | } |
1171 | 1657 | ||
1172 | /* scan finished notification */ | 1658 | /* scan finished notification */ |
@@ -1265,73 +1751,19 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, | |||
1265 | int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata) | 1751 | int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata) |
1266 | { | 1752 | { |
1267 | struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; | 1753 | struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; |
1268 | struct ieee80211_local *local = sdata->local; | ||
1269 | struct cfg80211_bss *cbss; | ||
1270 | u16 capability; | ||
1271 | int active_ibss; | ||
1272 | struct sta_info *sta; | ||
1273 | struct beacon_data *presp; | ||
1274 | |||
1275 | active_ibss = ieee80211_sta_active_ibss(sdata); | ||
1276 | |||
1277 | if (!active_ibss && !is_zero_ether_addr(ifibss->bssid)) { | ||
1278 | capability = WLAN_CAPABILITY_IBSS; | ||
1279 | |||
1280 | if (ifibss->privacy) | ||
1281 | capability |= WLAN_CAPABILITY_PRIVACY; | ||
1282 | |||
1283 | cbss = cfg80211_get_bss(local->hw.wiphy, ifibss->chandef.chan, | ||
1284 | ifibss->bssid, ifibss->ssid, | ||
1285 | ifibss->ssid_len, WLAN_CAPABILITY_IBSS | | ||
1286 | WLAN_CAPABILITY_PRIVACY, | ||
1287 | capability); | ||
1288 | 1754 | ||
1289 | if (cbss) { | 1755 | ieee80211_ibss_disconnect(sdata); |
1290 | cfg80211_unlink_bss(local->hw.wiphy, cbss); | ||
1291 | cfg80211_put_bss(local->hw.wiphy, cbss); | ||
1292 | } | ||
1293 | } | ||
1294 | |||
1295 | ifibss->state = IEEE80211_IBSS_MLME_SEARCH; | ||
1296 | memset(ifibss->bssid, 0, ETH_ALEN); | ||
1297 | ifibss->ssid_len = 0; | 1756 | ifibss->ssid_len = 0; |
1298 | 1757 | memset(ifibss->bssid, 0, ETH_ALEN); | |
1299 | sta_info_flush(sdata); | ||
1300 | |||
1301 | spin_lock_bh(&ifibss->incomplete_lock); | ||
1302 | while (!list_empty(&ifibss->incomplete_stations)) { | ||
1303 | sta = list_first_entry(&ifibss->incomplete_stations, | ||
1304 | struct sta_info, list); | ||
1305 | list_del(&sta->list); | ||
1306 | spin_unlock_bh(&ifibss->incomplete_lock); | ||
1307 | |||
1308 | sta_info_free(local, sta); | ||
1309 | spin_lock_bh(&ifibss->incomplete_lock); | ||
1310 | } | ||
1311 | spin_unlock_bh(&ifibss->incomplete_lock); | ||
1312 | |||
1313 | netif_carrier_off(sdata->dev); | ||
1314 | 1758 | ||
1315 | /* remove beacon */ | 1759 | /* remove beacon */ |
1316 | kfree(sdata->u.ibss.ie); | 1760 | kfree(sdata->u.ibss.ie); |
1317 | presp = rcu_dereference_protected(ifibss->presp, | ||
1318 | lockdep_is_held(&sdata->wdev.mtx)); | ||
1319 | RCU_INIT_POINTER(sdata->u.ibss.presp, NULL); | ||
1320 | 1761 | ||
1321 | /* on the next join, re-program HT parameters */ | 1762 | /* on the next join, re-program HT parameters */ |
1322 | memset(&ifibss->ht_capa, 0, sizeof(ifibss->ht_capa)); | 1763 | memset(&ifibss->ht_capa, 0, sizeof(ifibss->ht_capa)); |
1323 | memset(&ifibss->ht_capa_mask, 0, sizeof(ifibss->ht_capa_mask)); | 1764 | memset(&ifibss->ht_capa_mask, 0, sizeof(ifibss->ht_capa_mask)); |
1324 | 1765 | ||
1325 | sdata->vif.bss_conf.ibss_joined = false; | ||
1326 | sdata->vif.bss_conf.ibss_creator = false; | ||
1327 | sdata->vif.bss_conf.enable_beacon = false; | ||
1328 | sdata->vif.bss_conf.ssid_len = 0; | ||
1329 | clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state); | ||
1330 | ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED | | ||
1331 | BSS_CHANGED_IBSS); | ||
1332 | ieee80211_vif_release_channel(sdata); | ||
1333 | synchronize_rcu(); | 1766 | synchronize_rcu(); |
1334 | kfree(presp); | ||
1335 | 1767 | ||
1336 | skb_queue_purge(&sdata->skb_queue); | 1768 | skb_queue_purge(&sdata->skb_queue); |
1337 | 1769 | ||