diff options
author | Johannes Berg <johannes.berg@intel.com> | 2012-07-30 03:13:03 -0400 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2012-07-30 03:13:03 -0400 |
commit | fcb06702f023a0e7b1e6ebf9746f34b610ca0508 (patch) | |
tree | db022324c4978dd9af059be38822d23455a45f55 /net/mac80211/scan.c | |
parent | 5e31fc0815a4e2c72b1b495fe7a0d8f9bfb9e4b4 (diff) | |
parent | 9dbf5f55f8d35ff9aedc75267f4e4042aaf89755 (diff) |
Merge remote-tracking branch 'wireless/master' into mac80211
Diffstat (limited to 'net/mac80211/scan.c')
-rw-r--r-- | net/mac80211/scan.c | 123 |
1 files changed, 65 insertions, 58 deletions
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 7644181cb6b4..df36280ed78f 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c | |||
@@ -83,13 +83,14 @@ ieee80211_bss_info_update(struct ieee80211_local *local, | |||
83 | 83 | ||
84 | cbss = cfg80211_inform_bss_frame(local->hw.wiphy, channel, | 84 | cbss = cfg80211_inform_bss_frame(local->hw.wiphy, channel, |
85 | mgmt, len, signal, GFP_ATOMIC); | 85 | mgmt, len, signal, GFP_ATOMIC); |
86 | |||
87 | if (!cbss) | 86 | if (!cbss) |
88 | return NULL; | 87 | return NULL; |
89 | 88 | ||
90 | cbss->free_priv = ieee80211_rx_bss_free; | 89 | cbss->free_priv = ieee80211_rx_bss_free; |
91 | bss = (void *)cbss->priv; | 90 | bss = (void *)cbss->priv; |
92 | 91 | ||
92 | bss->device_ts = rx_status->device_timestamp; | ||
93 | |||
93 | if (elems->parse_error) { | 94 | if (elems->parse_error) { |
94 | if (beacon) | 95 | if (beacon) |
95 | bss->corrupt_data |= IEEE80211_BSS_CORRUPT_BEACON; | 96 | bss->corrupt_data |= IEEE80211_BSS_CORRUPT_BEACON; |
@@ -114,8 +115,7 @@ ieee80211_bss_info_update(struct ieee80211_local *local, | |||
114 | 115 | ||
115 | if (elems->tim && (!elems->parse_error || | 116 | if (elems->tim && (!elems->parse_error || |
116 | !(bss->valid_data & IEEE80211_BSS_VALID_DTIM))) { | 117 | !(bss->valid_data & IEEE80211_BSS_VALID_DTIM))) { |
117 | struct ieee80211_tim_ie *tim_ie = | 118 | struct ieee80211_tim_ie *tim_ie = elems->tim; |
118 | (struct ieee80211_tim_ie *)elems->tim; | ||
119 | bss->dtim_period = tim_ie->dtim_period; | 119 | bss->dtim_period = tim_ie->dtim_period; |
120 | if (!elems->parse_error) | 120 | if (!elems->parse_error) |
121 | bss->valid_data |= IEEE80211_BSS_VALID_DTIM; | 121 | bss->valid_data |= IEEE80211_BSS_VALID_DTIM; |
@@ -165,52 +165,47 @@ ieee80211_bss_info_update(struct ieee80211_local *local, | |||
165 | return bss; | 165 | return bss; |
166 | } | 166 | } |
167 | 167 | ||
168 | ieee80211_rx_result | 168 | void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb) |
169 | ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) | ||
170 | { | 169 | { |
171 | struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); | 170 | struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); |
172 | struct ieee80211_mgmt *mgmt; | 171 | struct ieee80211_sub_if_data *sdata1, *sdata2; |
172 | struct ieee80211_mgmt *mgmt = (void *)skb->data; | ||
173 | struct ieee80211_bss *bss; | 173 | struct ieee80211_bss *bss; |
174 | u8 *elements; | 174 | u8 *elements; |
175 | struct ieee80211_channel *channel; | 175 | struct ieee80211_channel *channel; |
176 | size_t baselen; | 176 | size_t baselen; |
177 | int freq; | 177 | int freq; |
178 | __le16 fc; | 178 | bool beacon; |
179 | bool presp, beacon = false; | ||
180 | struct ieee802_11_elems elems; | 179 | struct ieee802_11_elems elems; |
181 | 180 | ||
182 | if (skb->len < 2) | 181 | if (skb->len < 24 || |
183 | return RX_DROP_UNUSABLE; | 182 | (!ieee80211_is_probe_resp(mgmt->frame_control) && |
184 | 183 | !ieee80211_is_beacon(mgmt->frame_control))) | |
185 | mgmt = (struct ieee80211_mgmt *) skb->data; | 184 | return; |
186 | fc = mgmt->frame_control; | ||
187 | 185 | ||
188 | if (ieee80211_is_ctl(fc)) | 186 | sdata1 = rcu_dereference(local->scan_sdata); |
189 | return RX_CONTINUE; | 187 | sdata2 = rcu_dereference(local->sched_scan_sdata); |
190 | 188 | ||
191 | if (skb->len < 24) | 189 | if (likely(!sdata1 && !sdata2)) |
192 | return RX_CONTINUE; | 190 | return; |
193 | 191 | ||
194 | presp = ieee80211_is_probe_resp(fc); | 192 | if (ieee80211_is_probe_resp(mgmt->frame_control)) { |
195 | if (presp) { | ||
196 | /* ignore ProbeResp to foreign address */ | 193 | /* ignore ProbeResp to foreign address */ |
197 | if (!ether_addr_equal(mgmt->da, sdata->vif.addr)) | 194 | if ((!sdata1 || !ether_addr_equal(mgmt->da, sdata1->vif.addr)) && |
198 | return RX_DROP_MONITOR; | 195 | (!sdata2 || !ether_addr_equal(mgmt->da, sdata2->vif.addr))) |
196 | return; | ||
199 | 197 | ||
200 | presp = true; | ||
201 | elements = mgmt->u.probe_resp.variable; | 198 | elements = mgmt->u.probe_resp.variable; |
202 | baselen = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); | 199 | baselen = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); |
200 | beacon = false; | ||
203 | } else { | 201 | } else { |
204 | beacon = ieee80211_is_beacon(fc); | ||
205 | baselen = offsetof(struct ieee80211_mgmt, u.beacon.variable); | 202 | baselen = offsetof(struct ieee80211_mgmt, u.beacon.variable); |
206 | elements = mgmt->u.beacon.variable; | 203 | elements = mgmt->u.beacon.variable; |
204 | beacon = true; | ||
207 | } | 205 | } |
208 | 206 | ||
209 | if (!presp && !beacon) | ||
210 | return RX_CONTINUE; | ||
211 | |||
212 | if (baselen > skb->len) | 207 | if (baselen > skb->len) |
213 | return RX_DROP_MONITOR; | 208 | return; |
214 | 209 | ||
215 | ieee802_11_parse_elems(elements, skb->len - baselen, &elems); | 210 | ieee802_11_parse_elems(elements, skb->len - baselen, &elems); |
216 | 211 | ||
@@ -220,22 +215,16 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) | |||
220 | else | 215 | else |
221 | freq = rx_status->freq; | 216 | freq = rx_status->freq; |
222 | 217 | ||
223 | channel = ieee80211_get_channel(sdata->local->hw.wiphy, freq); | 218 | channel = ieee80211_get_channel(local->hw.wiphy, freq); |
224 | 219 | ||
225 | if (!channel || channel->flags & IEEE80211_CHAN_DISABLED) | 220 | if (!channel || channel->flags & IEEE80211_CHAN_DISABLED) |
226 | return RX_DROP_MONITOR; | 221 | return; |
227 | 222 | ||
228 | bss = ieee80211_bss_info_update(sdata->local, rx_status, | 223 | bss = ieee80211_bss_info_update(local, rx_status, |
229 | mgmt, skb->len, &elems, | 224 | mgmt, skb->len, &elems, |
230 | channel, beacon); | 225 | channel, beacon); |
231 | if (bss) | 226 | if (bss) |
232 | ieee80211_rx_bss_put(sdata->local, bss); | 227 | ieee80211_rx_bss_put(local, bss); |
233 | |||
234 | if (channel == sdata->local->oper_channel) | ||
235 | return RX_CONTINUE; | ||
236 | |||
237 | dev_kfree_skb(skb); | ||
238 | return RX_QUEUED; | ||
239 | } | 228 | } |
240 | 229 | ||
241 | /* return false if no more work */ | 230 | /* return false if no more work */ |
@@ -293,7 +282,13 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted, | |||
293 | return; | 282 | return; |
294 | 283 | ||
295 | if (was_hw_scan && !aborted && ieee80211_prep_hw_scan(local)) { | 284 | if (was_hw_scan && !aborted && ieee80211_prep_hw_scan(local)) { |
296 | int rc = drv_hw_scan(local, local->scan_sdata, local->hw_scan_req); | 285 | int rc; |
286 | |||
287 | rc = drv_hw_scan(local, | ||
288 | rcu_dereference_protected(local->scan_sdata, | ||
289 | lockdep_is_held(&local->mtx)), | ||
290 | local->hw_scan_req); | ||
291 | |||
297 | if (rc == 0) | 292 | if (rc == 0) |
298 | return; | 293 | return; |
299 | } | 294 | } |
@@ -323,7 +318,7 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted, | |||
323 | ieee80211_mlme_notify_scan_completed(local); | 318 | ieee80211_mlme_notify_scan_completed(local); |
324 | ieee80211_ibss_notify_scan_completed(local); | 319 | ieee80211_ibss_notify_scan_completed(local); |
325 | ieee80211_mesh_notify_scan_completed(local); | 320 | ieee80211_mesh_notify_scan_completed(local); |
326 | ieee80211_queue_work(&local->hw, &local->work_work); | 321 | ieee80211_start_next_roc(local); |
327 | } | 322 | } |
328 | 323 | ||
329 | void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) | 324 | void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) |
@@ -376,7 +371,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local) | |||
376 | static bool ieee80211_can_scan(struct ieee80211_local *local, | 371 | static bool ieee80211_can_scan(struct ieee80211_local *local, |
377 | struct ieee80211_sub_if_data *sdata) | 372 | struct ieee80211_sub_if_data *sdata) |
378 | { | 373 | { |
379 | if (!list_empty(&local->work_list)) | 374 | if (!list_empty(&local->roc_list)) |
380 | return false; | 375 | return false; |
381 | 376 | ||
382 | if (sdata->vif.type == NL80211_IFTYPE_STATION && | 377 | if (sdata->vif.type == NL80211_IFTYPE_STATION && |
@@ -394,7 +389,10 @@ void ieee80211_run_deferred_scan(struct ieee80211_local *local) | |||
394 | if (!local->scan_req || local->scanning) | 389 | if (!local->scan_req || local->scanning) |
395 | return; | 390 | return; |
396 | 391 | ||
397 | if (!ieee80211_can_scan(local, local->scan_sdata)) | 392 | if (!ieee80211_can_scan(local, |
393 | rcu_dereference_protected( | ||
394 | local->scan_sdata, | ||
395 | lockdep_is_held(&local->mtx)))) | ||
398 | return; | 396 | return; |
399 | 397 | ||
400 | ieee80211_queue_delayed_work(&local->hw, &local->scan_work, | 398 | ieee80211_queue_delayed_work(&local->hw, &local->scan_work, |
@@ -405,9 +403,12 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local, | |||
405 | unsigned long *next_delay) | 403 | unsigned long *next_delay) |
406 | { | 404 | { |
407 | int i; | 405 | int i; |
408 | struct ieee80211_sub_if_data *sdata = local->scan_sdata; | 406 | struct ieee80211_sub_if_data *sdata; |
409 | enum ieee80211_band band = local->hw.conf.channel->band; | 407 | enum ieee80211_band band = local->hw.conf.channel->band; |
410 | 408 | ||
409 | sdata = rcu_dereference_protected(local->scan_sdata, | ||
410 | lockdep_is_held(&local->mtx));; | ||
411 | |||
411 | for (i = 0; i < local->scan_req->n_ssids; i++) | 412 | for (i = 0; i < local->scan_req->n_ssids; i++) |
412 | ieee80211_send_probe_req( | 413 | ieee80211_send_probe_req( |
413 | sdata, NULL, | 414 | sdata, NULL, |
@@ -439,7 +440,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, | |||
439 | if (!ieee80211_can_scan(local, sdata)) { | 440 | if (!ieee80211_can_scan(local, sdata)) { |
440 | /* wait for the work to finish/time out */ | 441 | /* wait for the work to finish/time out */ |
441 | local->scan_req = req; | 442 | local->scan_req = req; |
442 | local->scan_sdata = sdata; | 443 | rcu_assign_pointer(local->scan_sdata, sdata); |
443 | return 0; | 444 | return 0; |
444 | } | 445 | } |
445 | 446 | ||
@@ -473,7 +474,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, | |||
473 | } | 474 | } |
474 | 475 | ||
475 | local->scan_req = req; | 476 | local->scan_req = req; |
476 | local->scan_sdata = sdata; | 477 | rcu_assign_pointer(local->scan_sdata, sdata); |
477 | 478 | ||
478 | if (local->ops->hw_scan) { | 479 | if (local->ops->hw_scan) { |
479 | __set_bit(SCAN_HW_SCANNING, &local->scanning); | 480 | __set_bit(SCAN_HW_SCANNING, &local->scanning); |
@@ -533,7 +534,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, | |||
533 | ieee80211_recalc_idle(local); | 534 | ieee80211_recalc_idle(local); |
534 | 535 | ||
535 | local->scan_req = NULL; | 536 | local->scan_req = NULL; |
536 | local->scan_sdata = NULL; | 537 | rcu_assign_pointer(local->scan_sdata, NULL); |
537 | } | 538 | } |
538 | 539 | ||
539 | return rc; | 540 | return rc; |
@@ -720,7 +721,8 @@ void ieee80211_scan_work(struct work_struct *work) | |||
720 | 721 | ||
721 | mutex_lock(&local->mtx); | 722 | mutex_lock(&local->mtx); |
722 | 723 | ||
723 | sdata = local->scan_sdata; | 724 | sdata = rcu_dereference_protected(local->scan_sdata, |
725 | lockdep_is_held(&local->mtx)); | ||
724 | 726 | ||
725 | /* When scanning on-channel, the first-callback means completed. */ | 727 | /* When scanning on-channel, the first-callback means completed. */ |
726 | if (test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning)) { | 728 | if (test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning)) { |
@@ -741,7 +743,7 @@ void ieee80211_scan_work(struct work_struct *work) | |||
741 | int rc; | 743 | int rc; |
742 | 744 | ||
743 | local->scan_req = NULL; | 745 | local->scan_req = NULL; |
744 | local->scan_sdata = NULL; | 746 | rcu_assign_pointer(local->scan_sdata, NULL); |
745 | 747 | ||
746 | rc = __ieee80211_start_scan(sdata, req); | 748 | rc = __ieee80211_start_scan(sdata, req); |
747 | if (rc) { | 749 | if (rc) { |
@@ -893,7 +895,9 @@ void ieee80211_scan_cancel(struct ieee80211_local *local) | |||
893 | 895 | ||
894 | if (test_bit(SCAN_HW_SCANNING, &local->scanning)) { | 896 | if (test_bit(SCAN_HW_SCANNING, &local->scanning)) { |
895 | if (local->ops->cancel_hw_scan) | 897 | if (local->ops->cancel_hw_scan) |
896 | drv_cancel_hw_scan(local, local->scan_sdata); | 898 | drv_cancel_hw_scan(local, |
899 | rcu_dereference_protected(local->scan_sdata, | ||
900 | lockdep_is_held(&local->mtx))); | ||
897 | goto out; | 901 | goto out; |
898 | } | 902 | } |
899 | 903 | ||
@@ -915,9 +919,9 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, | |||
915 | struct ieee80211_local *local = sdata->local; | 919 | struct ieee80211_local *local = sdata->local; |
916 | int ret, i; | 920 | int ret, i; |
917 | 921 | ||
918 | mutex_lock(&sdata->local->mtx); | 922 | mutex_lock(&local->mtx); |
919 | 923 | ||
920 | if (local->sched_scanning) { | 924 | if (rcu_access_pointer(local->sched_scan_sdata)) { |
921 | ret = -EBUSY; | 925 | ret = -EBUSY; |
922 | goto out; | 926 | goto out; |
923 | } | 927 | } |
@@ -928,6 +932,9 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, | |||
928 | } | 932 | } |
929 | 933 | ||
930 | for (i = 0; i < IEEE80211_NUM_BANDS; i++) { | 934 | for (i = 0; i < IEEE80211_NUM_BANDS; i++) { |
935 | if (!local->hw.wiphy->bands[i]) | ||
936 | continue; | ||
937 | |||
931 | local->sched_scan_ies.ie[i] = kzalloc(2 + | 938 | local->sched_scan_ies.ie[i] = kzalloc(2 + |
932 | IEEE80211_MAX_SSID_LEN + | 939 | IEEE80211_MAX_SSID_LEN + |
933 | local->scan_ies_len + | 940 | local->scan_ies_len + |
@@ -948,7 +955,7 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, | |||
948 | ret = drv_sched_scan_start(local, sdata, req, | 955 | ret = drv_sched_scan_start(local, sdata, req, |
949 | &local->sched_scan_ies); | 956 | &local->sched_scan_ies); |
950 | if (ret == 0) { | 957 | if (ret == 0) { |
951 | local->sched_scanning = true; | 958 | rcu_assign_pointer(local->sched_scan_sdata, sdata); |
952 | goto out; | 959 | goto out; |
953 | } | 960 | } |
954 | 961 | ||
@@ -956,7 +963,7 @@ out_free: | |||
956 | while (i > 0) | 963 | while (i > 0) |
957 | kfree(local->sched_scan_ies.ie[--i]); | 964 | kfree(local->sched_scan_ies.ie[--i]); |
958 | out: | 965 | out: |
959 | mutex_unlock(&sdata->local->mtx); | 966 | mutex_unlock(&local->mtx); |
960 | return ret; | 967 | return ret; |
961 | } | 968 | } |
962 | 969 | ||
@@ -965,22 +972,22 @@ int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata) | |||
965 | struct ieee80211_local *local = sdata->local; | 972 | struct ieee80211_local *local = sdata->local; |
966 | int ret = 0, i; | 973 | int ret = 0, i; |
967 | 974 | ||
968 | mutex_lock(&sdata->local->mtx); | 975 | mutex_lock(&local->mtx); |
969 | 976 | ||
970 | if (!local->ops->sched_scan_stop) { | 977 | if (!local->ops->sched_scan_stop) { |
971 | ret = -ENOTSUPP; | 978 | ret = -ENOTSUPP; |
972 | goto out; | 979 | goto out; |
973 | } | 980 | } |
974 | 981 | ||
975 | if (local->sched_scanning) { | 982 | if (rcu_access_pointer(local->sched_scan_sdata)) { |
976 | for (i = 0; i < IEEE80211_NUM_BANDS; i++) | 983 | for (i = 0; i < IEEE80211_NUM_BANDS; i++) |
977 | kfree(local->sched_scan_ies.ie[i]); | 984 | kfree(local->sched_scan_ies.ie[i]); |
978 | 985 | ||
979 | drv_sched_scan_stop(local, sdata); | 986 | drv_sched_scan_stop(local, sdata); |
980 | local->sched_scanning = false; | 987 | rcu_assign_pointer(local->sched_scan_sdata, NULL); |
981 | } | 988 | } |
982 | out: | 989 | out: |
983 | mutex_unlock(&sdata->local->mtx); | 990 | mutex_unlock(&local->mtx); |
984 | 991 | ||
985 | return ret; | 992 | return ret; |
986 | } | 993 | } |
@@ -1004,7 +1011,7 @@ void ieee80211_sched_scan_stopped_work(struct work_struct *work) | |||
1004 | 1011 | ||
1005 | mutex_lock(&local->mtx); | 1012 | mutex_lock(&local->mtx); |
1006 | 1013 | ||
1007 | if (!local->sched_scanning) { | 1014 | if (!rcu_access_pointer(local->sched_scan_sdata)) { |
1008 | mutex_unlock(&local->mtx); | 1015 | mutex_unlock(&local->mtx); |
1009 | return; | 1016 | return; |
1010 | } | 1017 | } |
@@ -1012,7 +1019,7 @@ void ieee80211_sched_scan_stopped_work(struct work_struct *work) | |||
1012 | for (i = 0; i < IEEE80211_NUM_BANDS; i++) | 1019 | for (i = 0; i < IEEE80211_NUM_BANDS; i++) |
1013 | kfree(local->sched_scan_ies.ie[i]); | 1020 | kfree(local->sched_scan_ies.ie[i]); |
1014 | 1021 | ||
1015 | local->sched_scanning = false; | 1022 | rcu_assign_pointer(local->sched_scan_sdata, NULL); |
1016 | 1023 | ||
1017 | mutex_unlock(&local->mtx); | 1024 | mutex_unlock(&local->mtx); |
1018 | 1025 | ||