diff options
Diffstat (limited to 'net/wireless/scan.c')
-rw-r--r-- | net/wireless/scan.c | 159 |
1 files changed, 105 insertions, 54 deletions
diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 7e595ce24eeb..4c210c2debc6 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c | |||
@@ -14,29 +14,41 @@ | |||
14 | #include <net/iw_handler.h> | 14 | #include <net/iw_handler.h> |
15 | #include "core.h" | 15 | #include "core.h" |
16 | #include "nl80211.h" | 16 | #include "nl80211.h" |
17 | #include "wext-compat.h" | ||
17 | 18 | ||
18 | #define IEEE80211_SCAN_RESULT_EXPIRE (10 * HZ) | 19 | #define IEEE80211_SCAN_RESULT_EXPIRE (15 * HZ) |
19 | 20 | ||
20 | void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) | 21 | void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak) |
21 | { | 22 | { |
23 | struct cfg80211_scan_request *request; | ||
22 | struct net_device *dev; | 24 | struct net_device *dev; |
23 | #ifdef CONFIG_WIRELESS_EXT | 25 | #ifdef CONFIG_WIRELESS_EXT |
24 | union iwreq_data wrqu; | 26 | union iwreq_data wrqu; |
25 | #endif | 27 | #endif |
26 | 28 | ||
27 | dev = dev_get_by_index(&init_net, request->ifidx); | 29 | ASSERT_RDEV_LOCK(rdev); |
28 | if (!dev) | ||
29 | goto out; | ||
30 | 30 | ||
31 | WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req); | 31 | request = rdev->scan_req; |
32 | |||
33 | if (!request) | ||
34 | return; | ||
35 | |||
36 | dev = request->dev; | ||
37 | |||
38 | /* | ||
39 | * This must be before sending the other events! | ||
40 | * Otherwise, wpa_supplicant gets completely confused with | ||
41 | * wext events. | ||
42 | */ | ||
43 | cfg80211_sme_scan_done(dev); | ||
32 | 44 | ||
33 | if (aborted) | 45 | if (request->aborted) |
34 | nl80211_send_scan_aborted(wiphy_to_dev(request->wiphy), dev); | 46 | nl80211_send_scan_aborted(rdev, dev); |
35 | else | 47 | else |
36 | nl80211_send_scan_done(wiphy_to_dev(request->wiphy), dev); | 48 | nl80211_send_scan_done(rdev, dev); |
37 | 49 | ||
38 | #ifdef CONFIG_WIRELESS_EXT | 50 | #ifdef CONFIG_WIRELESS_EXT |
39 | if (!aborted) { | 51 | if (!request->aborted) { |
40 | memset(&wrqu, 0, sizeof(wrqu)); | 52 | memset(&wrqu, 0, sizeof(wrqu)); |
41 | 53 | ||
42 | wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL); | 54 | wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL); |
@@ -45,9 +57,38 @@ void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) | |||
45 | 57 | ||
46 | dev_put(dev); | 58 | dev_put(dev); |
47 | 59 | ||
48 | out: | 60 | rdev->scan_req = NULL; |
49 | wiphy_to_dev(request->wiphy)->scan_req = NULL; | 61 | |
50 | kfree(request); | 62 | /* |
63 | * OK. If this is invoked with "leak" then we can't | ||
64 | * free this ... but we've cleaned it up anyway. The | ||
65 | * driver failed to call the scan_done callback, so | ||
66 | * all bets are off, it might still be trying to use | ||
67 | * the scan request or not ... if it accesses the dev | ||
68 | * in there (it shouldn't anyway) then it may crash. | ||
69 | */ | ||
70 | if (!leak) | ||
71 | kfree(request); | ||
72 | } | ||
73 | |||
74 | void __cfg80211_scan_done(struct work_struct *wk) | ||
75 | { | ||
76 | struct cfg80211_registered_device *rdev; | ||
77 | |||
78 | rdev = container_of(wk, struct cfg80211_registered_device, | ||
79 | scan_done_wk); | ||
80 | |||
81 | cfg80211_lock_rdev(rdev); | ||
82 | ___cfg80211_scan_done(rdev, false); | ||
83 | cfg80211_unlock_rdev(rdev); | ||
84 | } | ||
85 | |||
86 | void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) | ||
87 | { | ||
88 | WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req); | ||
89 | |||
90 | request->aborted = aborted; | ||
91 | schedule_work(&wiphy_to_dev(request->wiphy)->scan_done_wk); | ||
51 | } | 92 | } |
52 | EXPORT_SYMBOL(cfg80211_scan_done); | 93 | EXPORT_SYMBOL(cfg80211_scan_done); |
53 | 94 | ||
@@ -62,6 +103,8 @@ static void bss_release(struct kref *ref) | |||
62 | if (bss->ies_allocated) | 103 | if (bss->ies_allocated) |
63 | kfree(bss->pub.information_elements); | 104 | kfree(bss->pub.information_elements); |
64 | 105 | ||
106 | BUG_ON(atomic_read(&bss->hold)); | ||
107 | |||
65 | kfree(bss); | 108 | kfree(bss); |
66 | } | 109 | } |
67 | 110 | ||
@@ -84,8 +127,9 @@ void cfg80211_bss_expire(struct cfg80211_registered_device *dev) | |||
84 | bool expired = false; | 127 | bool expired = false; |
85 | 128 | ||
86 | list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) { | 129 | list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) { |
87 | if (bss->hold || | 130 | if (atomic_read(&bss->hold)) |
88 | !time_after(jiffies, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE)) | 131 | continue; |
132 | if (!time_after(jiffies, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE)) | ||
89 | continue; | 133 | continue; |
90 | list_del(&bss->list); | 134 | list_del(&bss->list); |
91 | rb_erase(&bss->rbn, &dev->bss_tree); | 135 | rb_erase(&bss->rbn, &dev->bss_tree); |
@@ -97,7 +141,7 @@ void cfg80211_bss_expire(struct cfg80211_registered_device *dev) | |||
97 | dev->bss_generation++; | 141 | dev->bss_generation++; |
98 | } | 142 | } |
99 | 143 | ||
100 | static u8 *find_ie(u8 num, u8 *ies, size_t len) | 144 | static u8 *find_ie(u8 num, u8 *ies, int len) |
101 | { | 145 | { |
102 | while (len > 2 && ies[0] != num) { | 146 | while (len > 2 && ies[0] != num) { |
103 | len -= ies[1] + 2; | 147 | len -= ies[1] + 2; |
@@ -539,6 +583,7 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) | |||
539 | spin_lock_bh(&dev->bss_lock); | 583 | spin_lock_bh(&dev->bss_lock); |
540 | 584 | ||
541 | list_del(&bss->list); | 585 | list_del(&bss->list); |
586 | dev->bss_generation++; | ||
542 | rb_erase(&bss->rbn, &dev->bss_tree); | 587 | rb_erase(&bss->rbn, &dev->bss_tree); |
543 | 588 | ||
544 | spin_unlock_bh(&dev->bss_lock); | 589 | spin_unlock_bh(&dev->bss_lock); |
@@ -547,30 +592,6 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) | |||
547 | } | 592 | } |
548 | EXPORT_SYMBOL(cfg80211_unlink_bss); | 593 | EXPORT_SYMBOL(cfg80211_unlink_bss); |
549 | 594 | ||
550 | void cfg80211_hold_bss(struct cfg80211_bss *pub) | ||
551 | { | ||
552 | struct cfg80211_internal_bss *bss; | ||
553 | |||
554 | if (!pub) | ||
555 | return; | ||
556 | |||
557 | bss = container_of(pub, struct cfg80211_internal_bss, pub); | ||
558 | bss->hold = true; | ||
559 | } | ||
560 | EXPORT_SYMBOL(cfg80211_hold_bss); | ||
561 | |||
562 | void cfg80211_unhold_bss(struct cfg80211_bss *pub) | ||
563 | { | ||
564 | struct cfg80211_internal_bss *bss; | ||
565 | |||
566 | if (!pub) | ||
567 | return; | ||
568 | |||
569 | bss = container_of(pub, struct cfg80211_internal_bss, pub); | ||
570 | bss->hold = false; | ||
571 | } | ||
572 | EXPORT_SYMBOL(cfg80211_unhold_bss); | ||
573 | |||
574 | #ifdef CONFIG_WIRELESS_EXT | 595 | #ifdef CONFIG_WIRELESS_EXT |
575 | int cfg80211_wext_siwscan(struct net_device *dev, | 596 | int cfg80211_wext_siwscan(struct net_device *dev, |
576 | struct iw_request_info *info, | 597 | struct iw_request_info *info, |
@@ -586,7 +607,10 @@ int cfg80211_wext_siwscan(struct net_device *dev, | |||
586 | if (!netif_running(dev)) | 607 | if (!netif_running(dev)) |
587 | return -ENETDOWN; | 608 | return -ENETDOWN; |
588 | 609 | ||
589 | rdev = cfg80211_get_dev_from_ifindex(dev->ifindex); | 610 | if (wrqu->data.length == sizeof(struct iw_scan_req)) |
611 | wreq = (struct iw_scan_req *)extra; | ||
612 | |||
613 | rdev = cfg80211_get_dev_from_ifindex(dev_net(dev), dev->ifindex); | ||
590 | 614 | ||
591 | if (IS_ERR(rdev)) | 615 | if (IS_ERR(rdev)) |
592 | return PTR_ERR(rdev); | 616 | return PTR_ERR(rdev); |
@@ -598,9 +622,14 @@ int cfg80211_wext_siwscan(struct net_device *dev, | |||
598 | 622 | ||
599 | wiphy = &rdev->wiphy; | 623 | wiphy = &rdev->wiphy; |
600 | 624 | ||
601 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) | 625 | /* Determine number of channels, needed to allocate creq */ |
602 | if (wiphy->bands[band]) | 626 | if (wreq && wreq->num_channels) |
603 | n_channels += wiphy->bands[band]->n_channels; | 627 | n_channels = wreq->num_channels; |
628 | else { | ||
629 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) | ||
630 | if (wiphy->bands[band]) | ||
631 | n_channels += wiphy->bands[band]->n_channels; | ||
632 | } | ||
604 | 633 | ||
605 | creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) + | 634 | creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) + |
606 | n_channels * sizeof(void *), | 635 | n_channels * sizeof(void *), |
@@ -611,28 +640,47 @@ int cfg80211_wext_siwscan(struct net_device *dev, | |||
611 | } | 640 | } |
612 | 641 | ||
613 | creq->wiphy = wiphy; | 642 | creq->wiphy = wiphy; |
614 | creq->ifidx = dev->ifindex; | 643 | creq->dev = dev; |
615 | creq->ssids = (void *)(creq + 1); | 644 | /* SSIDs come after channels */ |
616 | creq->channels = (void *)(creq->ssids + 1); | 645 | creq->ssids = (void *)&creq->channels[n_channels]; |
617 | creq->n_channels = n_channels; | 646 | creq->n_channels = n_channels; |
618 | creq->n_ssids = 1; | 647 | creq->n_ssids = 1; |
619 | 648 | ||
620 | /* all channels */ | 649 | /* translate "Scan on frequencies" request */ |
621 | i = 0; | 650 | i = 0; |
622 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { | 651 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { |
623 | int j; | 652 | int j; |
624 | if (!wiphy->bands[band]) | 653 | if (!wiphy->bands[band]) |
625 | continue; | 654 | continue; |
626 | for (j = 0; j < wiphy->bands[band]->n_channels; j++) { | 655 | for (j = 0; j < wiphy->bands[band]->n_channels; j++) { |
656 | |||
657 | /* If we have a wireless request structure and the | ||
658 | * wireless request specifies frequencies, then search | ||
659 | * for the matching hardware channel. | ||
660 | */ | ||
661 | if (wreq && wreq->num_channels) { | ||
662 | int k; | ||
663 | int wiphy_freq = wiphy->bands[band]->channels[j].center_freq; | ||
664 | for (k = 0; k < wreq->num_channels; k++) { | ||
665 | int wext_freq = wreq->channel_list[k].m / 100000; | ||
666 | if (wext_freq == wiphy_freq) | ||
667 | goto wext_freq_found; | ||
668 | } | ||
669 | goto wext_freq_not_found; | ||
670 | } | ||
671 | |||
672 | wext_freq_found: | ||
627 | creq->channels[i] = &wiphy->bands[band]->channels[j]; | 673 | creq->channels[i] = &wiphy->bands[band]->channels[j]; |
628 | i++; | 674 | i++; |
675 | wext_freq_not_found: ; | ||
629 | } | 676 | } |
630 | } | 677 | } |
631 | 678 | ||
632 | /* translate scan request */ | 679 | /* Set real number of channels specified in creq->channels[] */ |
633 | if (wrqu->data.length == sizeof(struct iw_scan_req)) { | 680 | creq->n_channels = i; |
634 | wreq = (struct iw_scan_req *)extra; | ||
635 | 681 | ||
682 | /* translate "Scan for SSID" request */ | ||
683 | if (wreq) { | ||
636 | if (wrqu->data.flags & IW_SCAN_THIS_ESSID) { | 684 | if (wrqu->data.flags & IW_SCAN_THIS_ESSID) { |
637 | if (wreq->essid_len > IEEE80211_MAX_SSID_LEN) | 685 | if (wreq->essid_len > IEEE80211_MAX_SSID_LEN) |
638 | return -EINVAL; | 686 | return -EINVAL; |
@@ -648,9 +696,12 @@ int cfg80211_wext_siwscan(struct net_device *dev, | |||
648 | if (err) { | 696 | if (err) { |
649 | rdev->scan_req = NULL; | 697 | rdev->scan_req = NULL; |
650 | kfree(creq); | 698 | kfree(creq); |
699 | } else { | ||
700 | nl80211_send_scan_start(rdev, dev); | ||
701 | dev_hold(dev); | ||
651 | } | 702 | } |
652 | out: | 703 | out: |
653 | cfg80211_put_dev(rdev); | 704 | cfg80211_unlock_rdev(rdev); |
654 | return err; | 705 | return err; |
655 | } | 706 | } |
656 | EXPORT_SYMBOL_GPL(cfg80211_wext_siwscan); | 707 | EXPORT_SYMBOL_GPL(cfg80211_wext_siwscan); |
@@ -941,7 +992,7 @@ int cfg80211_wext_giwscan(struct net_device *dev, | |||
941 | if (!netif_running(dev)) | 992 | if (!netif_running(dev)) |
942 | return -ENETDOWN; | 993 | return -ENETDOWN; |
943 | 994 | ||
944 | rdev = cfg80211_get_dev_from_ifindex(dev->ifindex); | 995 | rdev = cfg80211_get_dev_from_ifindex(dev_net(dev), dev->ifindex); |
945 | 996 | ||
946 | if (IS_ERR(rdev)) | 997 | if (IS_ERR(rdev)) |
947 | return PTR_ERR(rdev); | 998 | return PTR_ERR(rdev); |
@@ -959,7 +1010,7 @@ int cfg80211_wext_giwscan(struct net_device *dev, | |||
959 | } | 1010 | } |
960 | 1011 | ||
961 | out: | 1012 | out: |
962 | cfg80211_put_dev(rdev); | 1013 | cfg80211_unlock_rdev(rdev); |
963 | return res; | 1014 | return res; |
964 | } | 1015 | } |
965 | EXPORT_SYMBOL_GPL(cfg80211_wext_giwscan); | 1016 | EXPORT_SYMBOL_GPL(cfg80211_wext_giwscan); |