diff options
Diffstat (limited to 'net/wireless/scan.c')
-rw-r--r-- | net/wireless/scan.c | 630 |
1 files changed, 335 insertions, 295 deletions
diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 45f1618c8e23..674aadca0079 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c | |||
@@ -19,55 +19,142 @@ | |||
19 | #include "wext-compat.h" | 19 | #include "wext-compat.h" |
20 | #include "rdev-ops.h" | 20 | #include "rdev-ops.h" |
21 | 21 | ||
22 | /** | ||
23 | * DOC: BSS tree/list structure | ||
24 | * | ||
25 | * At the top level, the BSS list is kept in both a list in each | ||
26 | * registered device (@bss_list) as well as an RB-tree for faster | ||
27 | * lookup. In the RB-tree, entries can be looked up using their | ||
28 | * channel, MESHID, MESHCONF (for MBSSes) or channel, BSSID, SSID | ||
29 | * for other BSSes. | ||
30 | * | ||
31 | * Due to the possibility of hidden SSIDs, there's a second level | ||
32 | * structure, the "hidden_list" and "hidden_beacon_bss" pointer. | ||
33 | * The hidden_list connects all BSSes belonging to a single AP | ||
34 | * that has a hidden SSID, and connects beacon and probe response | ||
35 | * entries. For a probe response entry for a hidden SSID, the | ||
36 | * hidden_beacon_bss pointer points to the BSS struct holding the | ||
37 | * beacon's information. | ||
38 | * | ||
39 | * Reference counting is done for all these references except for | ||
40 | * the hidden_list, so that a beacon BSS struct that is otherwise | ||
41 | * not referenced has one reference for being on the bss_list and | ||
42 | * one for each probe response entry that points to it using the | ||
43 | * hidden_beacon_bss pointer. When a BSS struct that has such a | ||
44 | * pointer is get/put, the refcount update is also propagated to | ||
45 | * the referenced struct, this ensure that it cannot get removed | ||
46 | * while somebody is using the probe response version. | ||
47 | * | ||
48 | * Note that the hidden_beacon_bss pointer never changes, due to | ||
49 | * the reference counting. Therefore, no locking is needed for | ||
50 | * it. | ||
51 | * | ||
52 | * Also note that the hidden_beacon_bss pointer is only relevant | ||
53 | * if the driver uses something other than the IEs, e.g. private | ||
54 | * data stored stored in the BSS struct, since the beacon IEs are | ||
55 | * also linked into the probe response struct. | ||
56 | */ | ||
57 | |||
22 | #define IEEE80211_SCAN_RESULT_EXPIRE (30 * HZ) | 58 | #define IEEE80211_SCAN_RESULT_EXPIRE (30 * HZ) |
23 | 59 | ||
24 | static void bss_release(struct kref *ref) | 60 | static void bss_free(struct cfg80211_internal_bss *bss) |
25 | { | 61 | { |
26 | struct cfg80211_bss_ies *ies; | 62 | struct cfg80211_bss_ies *ies; |
27 | struct cfg80211_internal_bss *bss; | ||
28 | |||
29 | bss = container_of(ref, struct cfg80211_internal_bss, ref); | ||
30 | 63 | ||
31 | if (WARN_ON(atomic_read(&bss->hold))) | 64 | if (WARN_ON(atomic_read(&bss->hold))) |
32 | return; | 65 | return; |
33 | 66 | ||
34 | if (bss->pub.free_priv) | ||
35 | bss->pub.free_priv(&bss->pub); | ||
36 | |||
37 | ies = (void *)rcu_access_pointer(bss->pub.beacon_ies); | 67 | ies = (void *)rcu_access_pointer(bss->pub.beacon_ies); |
38 | if (ies) | 68 | if (ies && !bss->pub.hidden_beacon_bss) |
39 | kfree_rcu(ies, rcu_head); | 69 | kfree_rcu(ies, rcu_head); |
40 | ies = (void *)rcu_access_pointer(bss->pub.proberesp_ies); | 70 | ies = (void *)rcu_access_pointer(bss->pub.proberesp_ies); |
41 | if (ies) | 71 | if (ies) |
42 | kfree_rcu(ies, rcu_head); | 72 | kfree_rcu(ies, rcu_head); |
43 | 73 | ||
74 | /* | ||
75 | * This happens when the module is removed, it doesn't | ||
76 | * really matter any more save for completeness | ||
77 | */ | ||
78 | if (!list_empty(&bss->hidden_list)) | ||
79 | list_del(&bss->hidden_list); | ||
80 | |||
44 | kfree(bss); | 81 | kfree(bss); |
45 | } | 82 | } |
46 | 83 | ||
47 | /* must hold dev->bss_lock! */ | 84 | static inline void bss_ref_get(struct cfg80211_registered_device *dev, |
48 | static void __cfg80211_unlink_bss(struct cfg80211_registered_device *dev, | 85 | struct cfg80211_internal_bss *bss) |
86 | { | ||
87 | lockdep_assert_held(&dev->bss_lock); | ||
88 | |||
89 | bss->refcount++; | ||
90 | if (bss->pub.hidden_beacon_bss) { | ||
91 | bss = container_of(bss->pub.hidden_beacon_bss, | ||
92 | struct cfg80211_internal_bss, | ||
93 | pub); | ||
94 | bss->refcount++; | ||
95 | } | ||
96 | } | ||
97 | |||
98 | static inline void bss_ref_put(struct cfg80211_registered_device *dev, | ||
99 | struct cfg80211_internal_bss *bss) | ||
100 | { | ||
101 | lockdep_assert_held(&dev->bss_lock); | ||
102 | |||
103 | if (bss->pub.hidden_beacon_bss) { | ||
104 | struct cfg80211_internal_bss *hbss; | ||
105 | hbss = container_of(bss->pub.hidden_beacon_bss, | ||
106 | struct cfg80211_internal_bss, | ||
107 | pub); | ||
108 | hbss->refcount--; | ||
109 | if (hbss->refcount == 0) | ||
110 | bss_free(hbss); | ||
111 | } | ||
112 | bss->refcount--; | ||
113 | if (bss->refcount == 0) | ||
114 | bss_free(bss); | ||
115 | } | ||
116 | |||
117 | static bool __cfg80211_unlink_bss(struct cfg80211_registered_device *dev, | ||
49 | struct cfg80211_internal_bss *bss) | 118 | struct cfg80211_internal_bss *bss) |
50 | { | 119 | { |
120 | lockdep_assert_held(&dev->bss_lock); | ||
121 | |||
122 | if (!list_empty(&bss->hidden_list)) { | ||
123 | /* | ||
124 | * don't remove the beacon entry if it has | ||
125 | * probe responses associated with it | ||
126 | */ | ||
127 | if (!bss->pub.hidden_beacon_bss) | ||
128 | return false; | ||
129 | /* | ||
130 | * if it's a probe response entry break its | ||
131 | * link to the other entries in the group | ||
132 | */ | ||
133 | list_del_init(&bss->hidden_list); | ||
134 | } | ||
135 | |||
51 | list_del_init(&bss->list); | 136 | list_del_init(&bss->list); |
52 | rb_erase(&bss->rbn, &dev->bss_tree); | 137 | rb_erase(&bss->rbn, &dev->bss_tree); |
53 | kref_put(&bss->ref, bss_release); | 138 | bss_ref_put(dev, bss); |
139 | return true; | ||
54 | } | 140 | } |
55 | 141 | ||
56 | /* must hold dev->bss_lock! */ | ||
57 | static void __cfg80211_bss_expire(struct cfg80211_registered_device *dev, | 142 | static void __cfg80211_bss_expire(struct cfg80211_registered_device *dev, |
58 | unsigned long expire_time) | 143 | unsigned long expire_time) |
59 | { | 144 | { |
60 | struct cfg80211_internal_bss *bss, *tmp; | 145 | struct cfg80211_internal_bss *bss, *tmp; |
61 | bool expired = false; | 146 | bool expired = false; |
62 | 147 | ||
148 | lockdep_assert_held(&dev->bss_lock); | ||
149 | |||
63 | list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) { | 150 | list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) { |
64 | if (atomic_read(&bss->hold)) | 151 | if (atomic_read(&bss->hold)) |
65 | continue; | 152 | continue; |
66 | if (!time_after(expire_time, bss->ts)) | 153 | if (!time_after(expire_time, bss->ts)) |
67 | continue; | 154 | continue; |
68 | 155 | ||
69 | __cfg80211_unlink_bss(dev, bss); | 156 | if (__cfg80211_unlink_bss(dev, bss)) |
70 | expired = true; | 157 | expired = true; |
71 | } | 158 | } |
72 | 159 | ||
73 | if (expired) | 160 | if (expired) |
@@ -234,15 +321,16 @@ int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, | |||
234 | return 0; | 321 | return 0; |
235 | } | 322 | } |
236 | 323 | ||
237 | /* must hold dev->bss_lock! */ | ||
238 | void cfg80211_bss_age(struct cfg80211_registered_device *dev, | 324 | void cfg80211_bss_age(struct cfg80211_registered_device *dev, |
239 | unsigned long age_secs) | 325 | unsigned long age_secs) |
240 | { | 326 | { |
241 | struct cfg80211_internal_bss *bss; | 327 | struct cfg80211_internal_bss *bss; |
242 | unsigned long age_jiffies = msecs_to_jiffies(age_secs * MSEC_PER_SEC); | 328 | unsigned long age_jiffies = msecs_to_jiffies(age_secs * MSEC_PER_SEC); |
243 | 329 | ||
330 | spin_lock_bh(&dev->bss_lock); | ||
244 | list_for_each_entry(bss, &dev->bss_list, list) | 331 | list_for_each_entry(bss, &dev->bss_list, list) |
245 | bss->ts -= age_jiffies; | 332 | bss->ts -= age_jiffies; |
333 | spin_unlock_bh(&dev->bss_lock); | ||
246 | } | 334 | } |
247 | 335 | ||
248 | void cfg80211_bss_expire(struct cfg80211_registered_device *dev) | 336 | void cfg80211_bss_expire(struct cfg80211_registered_device *dev) |
@@ -277,40 +365,24 @@ const u8 *cfg80211_find_vendor_ie(unsigned int oui, u8 oui_type, | |||
277 | if (!pos) | 365 | if (!pos) |
278 | return NULL; | 366 | return NULL; |
279 | 367 | ||
280 | if (end - pos < sizeof(*ie)) | ||
281 | return NULL; | ||
282 | |||
283 | ie = (struct ieee80211_vendor_ie *)pos; | 368 | ie = (struct ieee80211_vendor_ie *)pos; |
369 | |||
370 | /* make sure we can access ie->len */ | ||
371 | BUILD_BUG_ON(offsetof(struct ieee80211_vendor_ie, len) != 1); | ||
372 | |||
373 | if (ie->len < sizeof(*ie)) | ||
374 | goto cont; | ||
375 | |||
284 | ie_oui = ie->oui[0] << 16 | ie->oui[1] << 8 | ie->oui[2]; | 376 | ie_oui = ie->oui[0] << 16 | ie->oui[1] << 8 | ie->oui[2]; |
285 | if (ie_oui == oui && ie->oui_type == oui_type) | 377 | if (ie_oui == oui && ie->oui_type == oui_type) |
286 | return pos; | 378 | return pos; |
287 | 379 | cont: | |
288 | pos += 2 + ie->len; | 380 | pos += 2 + ie->len; |
289 | } | 381 | } |
290 | return NULL; | 382 | return NULL; |
291 | } | 383 | } |
292 | EXPORT_SYMBOL(cfg80211_find_vendor_ie); | 384 | EXPORT_SYMBOL(cfg80211_find_vendor_ie); |
293 | 385 | ||
294 | static int cmp_ies(u8 num, const u8 *ies1, int len1, const u8 *ies2, int len2) | ||
295 | { | ||
296 | const u8 *ie1 = cfg80211_find_ie(num, ies1, len1); | ||
297 | const u8 *ie2 = cfg80211_find_ie(num, ies2, len2); | ||
298 | |||
299 | /* equal if both missing */ | ||
300 | if (!ie1 && !ie2) | ||
301 | return 0; | ||
302 | /* sort missing IE before (left of) present IE */ | ||
303 | if (!ie1) | ||
304 | return -1; | ||
305 | if (!ie2) | ||
306 | return 1; | ||
307 | |||
308 | /* sort by length first, then by contents */ | ||
309 | if (ie1[1] != ie2[1]) | ||
310 | return ie2[1] - ie1[1]; | ||
311 | return memcmp(ie1 + 2, ie2 + 2, ie1[1]); | ||
312 | } | ||
313 | |||
314 | static bool is_bss(struct cfg80211_bss *a, const u8 *bssid, | 386 | static bool is_bss(struct cfg80211_bss *a, const u8 *bssid, |
315 | const u8 *ssid, size_t ssid_len) | 387 | const u8 *ssid, size_t ssid_len) |
316 | { | 388 | { |
@@ -334,109 +406,30 @@ static bool is_bss(struct cfg80211_bss *a, const u8 *bssid, | |||
334 | return memcmp(ssidie + 2, ssid, ssid_len) == 0; | 406 | return memcmp(ssidie + 2, ssid, ssid_len) == 0; |
335 | } | 407 | } |
336 | 408 | ||
337 | static bool is_mesh_bss(struct cfg80211_bss *a) | 409 | /** |
338 | { | 410 | * enum bss_compare_mode - BSS compare mode |
339 | const struct cfg80211_bss_ies *ies; | 411 | * @BSS_CMP_REGULAR: regular compare mode (for insertion and normal find) |
340 | const u8 *ie; | 412 | * @BSS_CMP_HIDE_ZLEN: find hidden SSID with zero-length mode |
341 | 413 | * @BSS_CMP_HIDE_NUL: find hidden SSID with NUL-ed out mode | |
342 | if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability)) | 414 | */ |
343 | return false; | 415 | enum bss_compare_mode { |
344 | 416 | BSS_CMP_REGULAR, | |
345 | ies = rcu_access_pointer(a->ies); | 417 | BSS_CMP_HIDE_ZLEN, |
346 | if (!ies) | 418 | BSS_CMP_HIDE_NUL, |
347 | return false; | 419 | }; |
348 | |||
349 | ie = cfg80211_find_ie(WLAN_EID_MESH_ID, ies->data, ies->len); | ||
350 | if (!ie) | ||
351 | return false; | ||
352 | |||
353 | ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, ies->data, ies->len); | ||
354 | if (!ie) | ||
355 | return false; | ||
356 | |||
357 | return true; | ||
358 | } | ||
359 | |||
360 | static bool is_mesh(struct cfg80211_bss *a, | ||
361 | const u8 *meshid, size_t meshidlen, | ||
362 | const u8 *meshcfg) | ||
363 | { | ||
364 | const struct cfg80211_bss_ies *ies; | ||
365 | const u8 *ie; | ||
366 | |||
367 | if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability)) | ||
368 | return false; | ||
369 | |||
370 | ies = rcu_access_pointer(a->ies); | ||
371 | if (!ies) | ||
372 | return false; | ||
373 | |||
374 | ie = cfg80211_find_ie(WLAN_EID_MESH_ID, ies->data, ies->len); | ||
375 | if (!ie) | ||
376 | return false; | ||
377 | if (ie[1] != meshidlen) | ||
378 | return false; | ||
379 | if (memcmp(ie + 2, meshid, meshidlen)) | ||
380 | return false; | ||
381 | |||
382 | ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, ies->data, ies->len); | ||
383 | if (!ie) | ||
384 | return false; | ||
385 | if (ie[1] != sizeof(struct ieee80211_meshconf_ie)) | ||
386 | return false; | ||
387 | |||
388 | /* | ||
389 | * Ignore mesh capability (last two bytes of the IE) when | ||
390 | * comparing since that may differ between stations taking | ||
391 | * part in the same mesh. | ||
392 | */ | ||
393 | return memcmp(ie + 2, meshcfg, | ||
394 | sizeof(struct ieee80211_meshconf_ie) - 2) == 0; | ||
395 | } | ||
396 | 420 | ||
397 | static int cmp_bss_core(struct cfg80211_bss *a, struct cfg80211_bss *b) | 421 | static int cmp_bss(struct cfg80211_bss *a, |
422 | struct cfg80211_bss *b, | ||
423 | enum bss_compare_mode mode) | ||
398 | { | 424 | { |
399 | const struct cfg80211_bss_ies *a_ies, *b_ies; | 425 | const struct cfg80211_bss_ies *a_ies, *b_ies; |
400 | int r; | 426 | const u8 *ie1 = NULL; |
427 | const u8 *ie2 = NULL; | ||
428 | int i, r; | ||
401 | 429 | ||
402 | if (a->channel != b->channel) | 430 | if (a->channel != b->channel) |
403 | return b->channel->center_freq - a->channel->center_freq; | 431 | return b->channel->center_freq - a->channel->center_freq; |
404 | 432 | ||
405 | if (is_mesh_bss(a) && is_mesh_bss(b)) { | ||
406 | a_ies = rcu_access_pointer(a->ies); | ||
407 | if (!a_ies) | ||
408 | return -1; | ||
409 | b_ies = rcu_access_pointer(b->ies); | ||
410 | if (!b_ies) | ||
411 | return 1; | ||
412 | |||
413 | r = cmp_ies(WLAN_EID_MESH_ID, | ||
414 | a_ies->data, a_ies->len, | ||
415 | b_ies->data, b_ies->len); | ||
416 | if (r) | ||
417 | return r; | ||
418 | return cmp_ies(WLAN_EID_MESH_CONFIG, | ||
419 | a_ies->data, a_ies->len, | ||
420 | b_ies->data, b_ies->len); | ||
421 | } | ||
422 | |||
423 | /* | ||
424 | * we can't use compare_ether_addr here since we need a < > operator. | ||
425 | * The binary return value of compare_ether_addr isn't enough | ||
426 | */ | ||
427 | return memcmp(a->bssid, b->bssid, sizeof(a->bssid)); | ||
428 | } | ||
429 | |||
430 | static int cmp_bss(struct cfg80211_bss *a, | ||
431 | struct cfg80211_bss *b) | ||
432 | { | ||
433 | const struct cfg80211_bss_ies *a_ies, *b_ies; | ||
434 | int r; | ||
435 | |||
436 | r = cmp_bss_core(a, b); | ||
437 | if (r) | ||
438 | return r; | ||
439 | |||
440 | a_ies = rcu_access_pointer(a->ies); | 433 | a_ies = rcu_access_pointer(a->ies); |
441 | if (!a_ies) | 434 | if (!a_ies) |
442 | return -1; | 435 | return -1; |
@@ -444,42 +437,51 @@ static int cmp_bss(struct cfg80211_bss *a, | |||
444 | if (!b_ies) | 437 | if (!b_ies) |
445 | return 1; | 438 | return 1; |
446 | 439 | ||
447 | return cmp_ies(WLAN_EID_SSID, | 440 | if (WLAN_CAPABILITY_IS_STA_BSS(a->capability)) |
448 | a_ies->data, a_ies->len, | 441 | ie1 = cfg80211_find_ie(WLAN_EID_MESH_ID, |
449 | b_ies->data, b_ies->len); | 442 | a_ies->data, a_ies->len); |
450 | } | 443 | if (WLAN_CAPABILITY_IS_STA_BSS(b->capability)) |
451 | 444 | ie2 = cfg80211_find_ie(WLAN_EID_MESH_ID, | |
452 | static int cmp_hidden_bss(struct cfg80211_bss *a, struct cfg80211_bss *b) | 445 | b_ies->data, b_ies->len); |
453 | { | 446 | if (ie1 && ie2) { |
454 | const struct cfg80211_bss_ies *a_ies, *b_ies; | 447 | int mesh_id_cmp; |
455 | const u8 *ie1; | 448 | |
456 | const u8 *ie2; | 449 | if (ie1[1] == ie2[1]) |
457 | int i; | 450 | mesh_id_cmp = memcmp(ie1 + 2, ie2 + 2, ie1[1]); |
458 | int r; | 451 | else |
452 | mesh_id_cmp = ie2[1] - ie1[1]; | ||
453 | |||
454 | ie1 = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, | ||
455 | a_ies->data, a_ies->len); | ||
456 | ie2 = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, | ||
457 | b_ies->data, b_ies->len); | ||
458 | if (ie1 && ie2) { | ||
459 | if (mesh_id_cmp) | ||
460 | return mesh_id_cmp; | ||
461 | if (ie1[1] != ie2[1]) | ||
462 | return ie2[1] - ie1[1]; | ||
463 | return memcmp(ie1 + 2, ie2 + 2, ie1[1]); | ||
464 | } | ||
465 | } | ||
459 | 466 | ||
460 | r = cmp_bss_core(a, b); | 467 | /* |
468 | * we can't use compare_ether_addr here since we need a < > operator. | ||
469 | * The binary return value of compare_ether_addr isn't enough | ||
470 | */ | ||
471 | r = memcmp(a->bssid, b->bssid, sizeof(a->bssid)); | ||
461 | if (r) | 472 | if (r) |
462 | return r; | 473 | return r; |
463 | 474 | ||
464 | a_ies = rcu_access_pointer(a->ies); | ||
465 | if (!a_ies) | ||
466 | return -1; | ||
467 | b_ies = rcu_access_pointer(b->ies); | ||
468 | if (!b_ies) | ||
469 | return 1; | ||
470 | |||
471 | ie1 = cfg80211_find_ie(WLAN_EID_SSID, a_ies->data, a_ies->len); | 475 | ie1 = cfg80211_find_ie(WLAN_EID_SSID, a_ies->data, a_ies->len); |
472 | ie2 = cfg80211_find_ie(WLAN_EID_SSID, b_ies->data, b_ies->len); | 476 | ie2 = cfg80211_find_ie(WLAN_EID_SSID, b_ies->data, b_ies->len); |
473 | 477 | ||
478 | if (!ie1 && !ie2) | ||
479 | return 0; | ||
480 | |||
474 | /* | 481 | /* |
475 | * Key comparator must use same algorithm in any rb-tree | 482 | * Note that with "hide_ssid", the function returns a match if |
476 | * search function (order is important), otherwise ordering | 483 | * the already-present BSS ("b") is a hidden SSID beacon for |
477 | * of items in the tree is broken and search gives incorrect | 484 | * the new BSS ("a"). |
478 | * results. This code uses same order as cmp_ies() does. | ||
479 | * | ||
480 | * Note that due to the differring behaviour with hidden SSIDs | ||
481 | * this function only works when "b" is the tree element and | ||
482 | * "a" is the key we're looking for. | ||
483 | */ | 485 | */ |
484 | 486 | ||
485 | /* sort missing IE before (left of) present IE */ | 487 | /* sort missing IE before (left of) present IE */ |
@@ -488,24 +490,36 @@ static int cmp_hidden_bss(struct cfg80211_bss *a, struct cfg80211_bss *b) | |||
488 | if (!ie2) | 490 | if (!ie2) |
489 | return 1; | 491 | return 1; |
490 | 492 | ||
491 | /* zero-size SSID is used as an indication of the hidden bss */ | 493 | switch (mode) { |
492 | if (!ie2[1]) | 494 | case BSS_CMP_HIDE_ZLEN: |
495 | /* | ||
496 | * In ZLEN mode we assume the BSS entry we're | ||
497 | * looking for has a zero-length SSID. So if | ||
498 | * the one we're looking at right now has that, | ||
499 | * return 0. Otherwise, return the difference | ||
500 | * in length, but since we're looking for the | ||
501 | * 0-length it's really equivalent to returning | ||
502 | * the length of the one we're looking at. | ||
503 | * | ||
504 | * No content comparison is needed as we assume | ||
505 | * the content length is zero. | ||
506 | */ | ||
507 | return ie2[1]; | ||
508 | case BSS_CMP_REGULAR: | ||
509 | default: | ||
510 | /* sort by length first, then by contents */ | ||
511 | if (ie1[1] != ie2[1]) | ||
512 | return ie2[1] - ie1[1]; | ||
513 | return memcmp(ie1 + 2, ie2 + 2, ie1[1]); | ||
514 | case BSS_CMP_HIDE_NUL: | ||
515 | if (ie1[1] != ie2[1]) | ||
516 | return ie2[1] - ie1[1]; | ||
517 | /* this is equivalent to memcmp(zeroes, ie2 + 2, len) */ | ||
518 | for (i = 0; i < ie2[1]; i++) | ||
519 | if (ie2[i + 2]) | ||
520 | return -1; | ||
493 | return 0; | 521 | return 0; |
494 | 522 | } | |
495 | /* sort by length first, then by contents */ | ||
496 | if (ie1[1] != ie2[1]) | ||
497 | return ie2[1] - ie1[1]; | ||
498 | |||
499 | /* | ||
500 | * zeroed SSID ie is another indication of a hidden bss; | ||
501 | * if it isn't zeroed just return the regular sort value | ||
502 | * to find the next candidate | ||
503 | */ | ||
504 | for (i = 0; i < ie2[1]; i++) | ||
505 | if (ie2[i + 2]) | ||
506 | return memcmp(ie1 + 2, ie2 + 2, ie1[1]); | ||
507 | |||
508 | return 0; | ||
509 | } | 523 | } |
510 | 524 | ||
511 | struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, | 525 | struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, |
@@ -534,7 +548,7 @@ struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, | |||
534 | continue; | 548 | continue; |
535 | if (is_bss(&bss->pub, bssid, ssid, ssid_len)) { | 549 | if (is_bss(&bss->pub, bssid, ssid, ssid_len)) { |
536 | res = bss; | 550 | res = bss; |
537 | kref_get(&res->ref); | 551 | bss_ref_get(dev, res); |
538 | break; | 552 | break; |
539 | } | 553 | } |
540 | } | 554 | } |
@@ -547,34 +561,6 @@ struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, | |||
547 | } | 561 | } |
548 | EXPORT_SYMBOL(cfg80211_get_bss); | 562 | EXPORT_SYMBOL(cfg80211_get_bss); |
549 | 563 | ||
550 | struct cfg80211_bss *cfg80211_get_mesh(struct wiphy *wiphy, | ||
551 | struct ieee80211_channel *channel, | ||
552 | const u8 *meshid, size_t meshidlen, | ||
553 | const u8 *meshcfg) | ||
554 | { | ||
555 | struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy); | ||
556 | struct cfg80211_internal_bss *bss, *res = NULL; | ||
557 | |||
558 | spin_lock_bh(&dev->bss_lock); | ||
559 | |||
560 | list_for_each_entry(bss, &dev->bss_list, list) { | ||
561 | if (channel && bss->pub.channel != channel) | ||
562 | continue; | ||
563 | if (is_mesh(&bss->pub, meshid, meshidlen, meshcfg)) { | ||
564 | res = bss; | ||
565 | kref_get(&res->ref); | ||
566 | break; | ||
567 | } | ||
568 | } | ||
569 | |||
570 | spin_unlock_bh(&dev->bss_lock); | ||
571 | if (!res) | ||
572 | return NULL; | ||
573 | return &res->pub; | ||
574 | } | ||
575 | EXPORT_SYMBOL(cfg80211_get_mesh); | ||
576 | |||
577 | |||
578 | static void rb_insert_bss(struct cfg80211_registered_device *dev, | 564 | static void rb_insert_bss(struct cfg80211_registered_device *dev, |
579 | struct cfg80211_internal_bss *bss) | 565 | struct cfg80211_internal_bss *bss) |
580 | { | 566 | { |
@@ -587,7 +573,7 @@ static void rb_insert_bss(struct cfg80211_registered_device *dev, | |||
587 | parent = *p; | 573 | parent = *p; |
588 | tbss = rb_entry(parent, struct cfg80211_internal_bss, rbn); | 574 | tbss = rb_entry(parent, struct cfg80211_internal_bss, rbn); |
589 | 575 | ||
590 | cmp = cmp_bss(&bss->pub, &tbss->pub); | 576 | cmp = cmp_bss(&bss->pub, &tbss->pub, BSS_CMP_REGULAR); |
591 | 577 | ||
592 | if (WARN_ON(!cmp)) { | 578 | if (WARN_ON(!cmp)) { |
593 | /* will sort of leak this BSS */ | 579 | /* will sort of leak this BSS */ |
@@ -606,7 +592,8 @@ static void rb_insert_bss(struct cfg80211_registered_device *dev, | |||
606 | 592 | ||
607 | static struct cfg80211_internal_bss * | 593 | static struct cfg80211_internal_bss * |
608 | rb_find_bss(struct cfg80211_registered_device *dev, | 594 | rb_find_bss(struct cfg80211_registered_device *dev, |
609 | struct cfg80211_internal_bss *res) | 595 | struct cfg80211_internal_bss *res, |
596 | enum bss_compare_mode mode) | ||
610 | { | 597 | { |
611 | struct rb_node *n = dev->bss_tree.rb_node; | 598 | struct rb_node *n = dev->bss_tree.rb_node; |
612 | struct cfg80211_internal_bss *bss; | 599 | struct cfg80211_internal_bss *bss; |
@@ -614,7 +601,7 @@ rb_find_bss(struct cfg80211_registered_device *dev, | |||
614 | 601 | ||
615 | while (n) { | 602 | while (n) { |
616 | bss = rb_entry(n, struct cfg80211_internal_bss, rbn); | 603 | bss = rb_entry(n, struct cfg80211_internal_bss, rbn); |
617 | r = cmp_bss(&res->pub, &bss->pub); | 604 | r = cmp_bss(&res->pub, &bss->pub, mode); |
618 | 605 | ||
619 | if (r == 0) | 606 | if (r == 0) |
620 | return bss; | 607 | return bss; |
@@ -627,46 +614,67 @@ rb_find_bss(struct cfg80211_registered_device *dev, | |||
627 | return NULL; | 614 | return NULL; |
628 | } | 615 | } |
629 | 616 | ||
630 | static struct cfg80211_internal_bss * | 617 | static bool cfg80211_combine_bsses(struct cfg80211_registered_device *dev, |
631 | rb_find_hidden_bss(struct cfg80211_registered_device *dev, | 618 | struct cfg80211_internal_bss *new) |
632 | struct cfg80211_internal_bss *res) | ||
633 | { | 619 | { |
634 | struct rb_node *n = dev->bss_tree.rb_node; | 620 | const struct cfg80211_bss_ies *ies; |
635 | struct cfg80211_internal_bss *bss; | 621 | struct cfg80211_internal_bss *bss; |
636 | int r; | 622 | const u8 *ie; |
623 | int i, ssidlen; | ||
624 | u8 fold = 0; | ||
637 | 625 | ||
638 | while (n) { | 626 | ies = rcu_access_pointer(new->pub.beacon_ies); |
639 | bss = rb_entry(n, struct cfg80211_internal_bss, rbn); | 627 | if (WARN_ON(!ies)) |
640 | r = cmp_hidden_bss(&res->pub, &bss->pub); | 628 | return false; |
641 | 629 | ||
642 | if (r == 0) | 630 | ie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len); |
643 | return bss; | 631 | if (!ie) { |
644 | else if (r < 0) | 632 | /* nothing to do */ |
645 | n = n->rb_left; | 633 | return true; |
646 | else | ||
647 | n = n->rb_right; | ||
648 | } | 634 | } |
649 | 635 | ||
650 | return NULL; | 636 | ssidlen = ie[1]; |
651 | } | 637 | for (i = 0; i < ssidlen; i++) |
638 | fold |= ie[2 + i]; | ||
652 | 639 | ||
653 | static void | 640 | if (fold) { |
654 | copy_hidden_ies(struct cfg80211_internal_bss *res, | 641 | /* not a hidden SSID */ |
655 | struct cfg80211_internal_bss *hidden) | 642 | return true; |
656 | { | 643 | } |
657 | const struct cfg80211_bss_ies *ies; | ||
658 | 644 | ||
659 | if (rcu_access_pointer(res->pub.beacon_ies)) | 645 | /* This is the bad part ... */ |
660 | return; | ||
661 | 646 | ||
662 | ies = rcu_access_pointer(hidden->pub.beacon_ies); | 647 | list_for_each_entry(bss, &dev->bss_list, list) { |
663 | if (WARN_ON(!ies)) | 648 | if (!ether_addr_equal(bss->pub.bssid, new->pub.bssid)) |
664 | return; | 649 | continue; |
650 | if (bss->pub.channel != new->pub.channel) | ||
651 | continue; | ||
652 | if (rcu_access_pointer(bss->pub.beacon_ies)) | ||
653 | continue; | ||
654 | ies = rcu_access_pointer(bss->pub.ies); | ||
655 | if (!ies) | ||
656 | continue; | ||
657 | ie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len); | ||
658 | if (!ie) | ||
659 | continue; | ||
660 | if (ssidlen && ie[1] != ssidlen) | ||
661 | continue; | ||
662 | /* that would be odd ... */ | ||
663 | if (bss->pub.beacon_ies) | ||
664 | continue; | ||
665 | if (WARN_ON_ONCE(bss->pub.hidden_beacon_bss)) | ||
666 | continue; | ||
667 | if (WARN_ON_ONCE(!list_empty(&bss->hidden_list))) | ||
668 | list_del(&bss->hidden_list); | ||
669 | /* combine them */ | ||
670 | list_add(&bss->hidden_list, &new->hidden_list); | ||
671 | bss->pub.hidden_beacon_bss = &new->pub; | ||
672 | new->refcount += bss->refcount; | ||
673 | rcu_assign_pointer(bss->pub.beacon_ies, | ||
674 | new->pub.beacon_ies); | ||
675 | } | ||
665 | 676 | ||
666 | ies = kmemdup(ies, sizeof(*ies) + ies->len, GFP_ATOMIC); | 677 | return true; |
667 | if (unlikely(!ies)) | ||
668 | return; | ||
669 | rcu_assign_pointer(res->pub.beacon_ies, ies); | ||
670 | } | 678 | } |
671 | 679 | ||
672 | static struct cfg80211_internal_bss * | 680 | static struct cfg80211_internal_bss * |
@@ -687,11 +695,10 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, | |||
687 | return NULL; | 695 | return NULL; |
688 | } | 696 | } |
689 | 697 | ||
690 | found = rb_find_bss(dev, tmp); | 698 | found = rb_find_bss(dev, tmp, BSS_CMP_REGULAR); |
691 | 699 | ||
692 | if (found) { | 700 | if (found) { |
693 | found->pub.beacon_interval = tmp->pub.beacon_interval; | 701 | found->pub.beacon_interval = tmp->pub.beacon_interval; |
694 | found->pub.tsf = tmp->pub.tsf; | ||
695 | found->pub.signal = tmp->pub.signal; | 702 | found->pub.signal = tmp->pub.signal; |
696 | found->pub.capability = tmp->pub.capability; | 703 | found->pub.capability = tmp->pub.capability; |
697 | found->ts = tmp->ts; | 704 | found->ts = tmp->ts; |
@@ -711,19 +718,45 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, | |||
711 | kfree_rcu((struct cfg80211_bss_ies *)old, | 718 | kfree_rcu((struct cfg80211_bss_ies *)old, |
712 | rcu_head); | 719 | rcu_head); |
713 | } else if (rcu_access_pointer(tmp->pub.beacon_ies)) { | 720 | } else if (rcu_access_pointer(tmp->pub.beacon_ies)) { |
714 | const struct cfg80211_bss_ies *old, *ies; | 721 | const struct cfg80211_bss_ies *old; |
722 | struct cfg80211_internal_bss *bss; | ||
723 | |||
724 | if (found->pub.hidden_beacon_bss && | ||
725 | !list_empty(&found->hidden_list)) { | ||
726 | /* | ||
727 | * The found BSS struct is one of the probe | ||
728 | * response members of a group, but we're | ||
729 | * receiving a beacon (beacon_ies in the tmp | ||
730 | * bss is used). This can only mean that the | ||
731 | * AP changed its beacon from not having an | ||
732 | * SSID to showing it, which is confusing so | ||
733 | * drop this information. | ||
734 | */ | ||
735 | goto drop; | ||
736 | } | ||
715 | 737 | ||
716 | old = rcu_access_pointer(found->pub.beacon_ies); | 738 | old = rcu_access_pointer(found->pub.beacon_ies); |
717 | ies = rcu_access_pointer(found->pub.ies); | ||
718 | 739 | ||
719 | rcu_assign_pointer(found->pub.beacon_ies, | 740 | rcu_assign_pointer(found->pub.beacon_ies, |
720 | tmp->pub.beacon_ies); | 741 | tmp->pub.beacon_ies); |
721 | 742 | ||
722 | /* Override IEs if they were from a beacon before */ | 743 | /* Override IEs if they were from a beacon before */ |
723 | if (old == ies) | 744 | if (old == rcu_access_pointer(found->pub.ies)) |
724 | rcu_assign_pointer(found->pub.ies, | 745 | rcu_assign_pointer(found->pub.ies, |
725 | tmp->pub.beacon_ies); | 746 | tmp->pub.beacon_ies); |
726 | 747 | ||
748 | /* Assign beacon IEs to all sub entries */ | ||
749 | list_for_each_entry(bss, &found->hidden_list, | ||
750 | hidden_list) { | ||
751 | const struct cfg80211_bss_ies *ies; | ||
752 | |||
753 | ies = rcu_access_pointer(bss->pub.beacon_ies); | ||
754 | WARN_ON(ies != old); | ||
755 | |||
756 | rcu_assign_pointer(bss->pub.beacon_ies, | ||
757 | tmp->pub.beacon_ies); | ||
758 | } | ||
759 | |||
727 | if (old) | 760 | if (old) |
728 | kfree_rcu((struct cfg80211_bss_ies *)old, | 761 | kfree_rcu((struct cfg80211_bss_ies *)old, |
729 | rcu_head); | 762 | rcu_head); |
@@ -733,19 +766,6 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, | |||
733 | struct cfg80211_internal_bss *hidden; | 766 | struct cfg80211_internal_bss *hidden; |
734 | struct cfg80211_bss_ies *ies; | 767 | struct cfg80211_bss_ies *ies; |
735 | 768 | ||
736 | /* First check if the beacon is a probe response from | ||
737 | * a hidden bss. If so, copy beacon ies (with nullified | ||
738 | * ssid) into the probe response bss entry (with real ssid). | ||
739 | * It is required basically for PSM implementation | ||
740 | * (probe responses do not contain tim ie) */ | ||
741 | |||
742 | /* TODO: The code is not trying to update existing probe | ||
743 | * response bss entries when beacon ies are | ||
744 | * getting changed. */ | ||
745 | hidden = rb_find_hidden_bss(dev, tmp); | ||
746 | if (hidden) | ||
747 | copy_hidden_ies(tmp, hidden); | ||
748 | |||
749 | /* | 769 | /* |
750 | * create a copy -- the "res" variable that is passed in | 770 | * create a copy -- the "res" variable that is passed in |
751 | * is allocated on the stack since it's not needed in the | 771 | * is allocated on the stack since it's not needed in the |
@@ -760,21 +780,51 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, | |||
760 | ies = (void *)rcu_dereference(tmp->pub.proberesp_ies); | 780 | ies = (void *)rcu_dereference(tmp->pub.proberesp_ies); |
761 | if (ies) | 781 | if (ies) |
762 | kfree_rcu(ies, rcu_head); | 782 | kfree_rcu(ies, rcu_head); |
763 | spin_unlock_bh(&dev->bss_lock); | 783 | goto drop; |
764 | return NULL; | ||
765 | } | 784 | } |
766 | memcpy(new, tmp, sizeof(*new)); | 785 | memcpy(new, tmp, sizeof(*new)); |
767 | kref_init(&new->ref); | 786 | new->refcount = 1; |
787 | INIT_LIST_HEAD(&new->hidden_list); | ||
788 | |||
789 | if (rcu_access_pointer(tmp->pub.proberesp_ies)) { | ||
790 | hidden = rb_find_bss(dev, tmp, BSS_CMP_HIDE_ZLEN); | ||
791 | if (!hidden) | ||
792 | hidden = rb_find_bss(dev, tmp, | ||
793 | BSS_CMP_HIDE_NUL); | ||
794 | if (hidden) { | ||
795 | new->pub.hidden_beacon_bss = &hidden->pub; | ||
796 | list_add(&new->hidden_list, | ||
797 | &hidden->hidden_list); | ||
798 | hidden->refcount++; | ||
799 | rcu_assign_pointer(new->pub.beacon_ies, | ||
800 | hidden->pub.beacon_ies); | ||
801 | } | ||
802 | } else { | ||
803 | /* | ||
804 | * Ok so we found a beacon, and don't have an entry. If | ||
805 | * it's a beacon with hidden SSID, we might be in for an | ||
806 | * expensive search for any probe responses that should | ||
807 | * be grouped with this beacon for updates ... | ||
808 | */ | ||
809 | if (!cfg80211_combine_bsses(dev, new)) { | ||
810 | kfree(new); | ||
811 | goto drop; | ||
812 | } | ||
813 | } | ||
814 | |||
768 | list_add_tail(&new->list, &dev->bss_list); | 815 | list_add_tail(&new->list, &dev->bss_list); |
769 | rb_insert_bss(dev, new); | 816 | rb_insert_bss(dev, new); |
770 | found = new; | 817 | found = new; |
771 | } | 818 | } |
772 | 819 | ||
773 | dev->bss_generation++; | 820 | dev->bss_generation++; |
821 | bss_ref_get(dev, found); | ||
774 | spin_unlock_bh(&dev->bss_lock); | 822 | spin_unlock_bh(&dev->bss_lock); |
775 | 823 | ||
776 | kref_get(&found->ref); | ||
777 | return found; | 824 | return found; |
825 | drop: | ||
826 | spin_unlock_bh(&dev->bss_lock); | ||
827 | return NULL; | ||
778 | } | 828 | } |
779 | 829 | ||
780 | static struct ieee80211_channel * | 830 | static struct ieee80211_channel * |
@@ -833,7 +883,6 @@ cfg80211_inform_bss(struct wiphy *wiphy, | |||
833 | memcpy(tmp.pub.bssid, bssid, ETH_ALEN); | 883 | memcpy(tmp.pub.bssid, bssid, ETH_ALEN); |
834 | tmp.pub.channel = channel; | 884 | tmp.pub.channel = channel; |
835 | tmp.pub.signal = signal; | 885 | tmp.pub.signal = signal; |
836 | tmp.pub.tsf = tsf; | ||
837 | tmp.pub.beacon_interval = beacon_interval; | 886 | tmp.pub.beacon_interval = beacon_interval; |
838 | tmp.pub.capability = capability; | 887 | tmp.pub.capability = capability; |
839 | /* | 888 | /* |
@@ -841,16 +890,14 @@ cfg80211_inform_bss(struct wiphy *wiphy, | |||
841 | * Response frame, we need to pick one of the options and only use it | 890 | * Response frame, we need to pick one of the options and only use it |
842 | * with the driver that does not provide the full Beacon/Probe Response | 891 | * with the driver that does not provide the full Beacon/Probe Response |
843 | * frame. Use Beacon frame pointer to avoid indicating that this should | 892 | * frame. Use Beacon frame pointer to avoid indicating that this should |
844 | * override the iies pointer should we have received an earlier | 893 | * override the IEs pointer should we have received an earlier |
845 | * indication of Probe Response data. | 894 | * indication of Probe Response data. |
846 | * | ||
847 | * The initial buffer for the IEs is allocated with the BSS entry and | ||
848 | * is located after the private area. | ||
849 | */ | 895 | */ |
850 | ies = kmalloc(sizeof(*ies) + ielen, gfp); | 896 | ies = kmalloc(sizeof(*ies) + ielen, gfp); |
851 | if (!ies) | 897 | if (!ies) |
852 | return NULL; | 898 | return NULL; |
853 | ies->len = ielen; | 899 | ies->len = ielen; |
900 | ies->tsf = tsf; | ||
854 | memcpy(ies->data, ie, ielen); | 901 | memcpy(ies->data, ie, ielen); |
855 | 902 | ||
856 | rcu_assign_pointer(tmp.pub.beacon_ies, ies); | 903 | rcu_assign_pointer(tmp.pub.beacon_ies, ies); |
@@ -907,6 +954,7 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, | |||
907 | if (!ies) | 954 | if (!ies) |
908 | return NULL; | 955 | return NULL; |
909 | ies->len = ielen; | 956 | ies->len = ielen; |
957 | ies->tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp); | ||
910 | memcpy(ies->data, mgmt->u.probe_resp.variable, ielen); | 958 | memcpy(ies->data, mgmt->u.probe_resp.variable, ielen); |
911 | 959 | ||
912 | if (ieee80211_is_probe_resp(mgmt->frame_control)) | 960 | if (ieee80211_is_probe_resp(mgmt->frame_control)) |
@@ -918,7 +966,6 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, | |||
918 | memcpy(tmp.pub.bssid, mgmt->bssid, ETH_ALEN); | 966 | memcpy(tmp.pub.bssid, mgmt->bssid, ETH_ALEN); |
919 | tmp.pub.channel = channel; | 967 | tmp.pub.channel = channel; |
920 | tmp.pub.signal = signal; | 968 | tmp.pub.signal = signal; |
921 | tmp.pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp); | ||
922 | tmp.pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int); | 969 | tmp.pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int); |
923 | tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info); | 970 | tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info); |
924 | 971 | ||
@@ -935,27 +982,35 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, | |||
935 | } | 982 | } |
936 | EXPORT_SYMBOL(cfg80211_inform_bss_frame); | 983 | EXPORT_SYMBOL(cfg80211_inform_bss_frame); |
937 | 984 | ||
938 | void cfg80211_ref_bss(struct cfg80211_bss *pub) | 985 | void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) |
939 | { | 986 | { |
987 | struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy); | ||
940 | struct cfg80211_internal_bss *bss; | 988 | struct cfg80211_internal_bss *bss; |
941 | 989 | ||
942 | if (!pub) | 990 | if (!pub) |
943 | return; | 991 | return; |
944 | 992 | ||
945 | bss = container_of(pub, struct cfg80211_internal_bss, pub); | 993 | bss = container_of(pub, struct cfg80211_internal_bss, pub); |
946 | kref_get(&bss->ref); | 994 | |
995 | spin_lock_bh(&dev->bss_lock); | ||
996 | bss_ref_get(dev, bss); | ||
997 | spin_unlock_bh(&dev->bss_lock); | ||
947 | } | 998 | } |
948 | EXPORT_SYMBOL(cfg80211_ref_bss); | 999 | EXPORT_SYMBOL(cfg80211_ref_bss); |
949 | 1000 | ||
950 | void cfg80211_put_bss(struct cfg80211_bss *pub) | 1001 | void cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) |
951 | { | 1002 | { |
1003 | struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy); | ||
952 | struct cfg80211_internal_bss *bss; | 1004 | struct cfg80211_internal_bss *bss; |
953 | 1005 | ||
954 | if (!pub) | 1006 | if (!pub) |
955 | return; | 1007 | return; |
956 | 1008 | ||
957 | bss = container_of(pub, struct cfg80211_internal_bss, pub); | 1009 | bss = container_of(pub, struct cfg80211_internal_bss, pub); |
958 | kref_put(&bss->ref, bss_release); | 1010 | |
1011 | spin_lock_bh(&dev->bss_lock); | ||
1012 | bss_ref_put(dev, bss); | ||
1013 | spin_unlock_bh(&dev->bss_lock); | ||
959 | } | 1014 | } |
960 | EXPORT_SYMBOL(cfg80211_put_bss); | 1015 | EXPORT_SYMBOL(cfg80211_put_bss); |
961 | 1016 | ||
@@ -971,8 +1026,8 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) | |||
971 | 1026 | ||
972 | spin_lock_bh(&dev->bss_lock); | 1027 | spin_lock_bh(&dev->bss_lock); |
973 | if (!list_empty(&bss->list)) { | 1028 | if (!list_empty(&bss->list)) { |
974 | __cfg80211_unlink_bss(dev, bss); | 1029 | if (__cfg80211_unlink_bss(dev, bss)) |
975 | dev->bss_generation++; | 1030 | dev->bss_generation++; |
976 | } | 1031 | } |
977 | spin_unlock_bh(&dev->bss_lock); | 1032 | spin_unlock_bh(&dev->bss_lock); |
978 | } | 1033 | } |
@@ -1155,16 +1210,6 @@ static void ieee80211_scan_add_ies(struct iw_request_info *info, | |||
1155 | } | 1210 | } |
1156 | } | 1211 | } |
1157 | 1212 | ||
1158 | static inline unsigned int elapsed_jiffies_msecs(unsigned long start) | ||
1159 | { | ||
1160 | unsigned long end = jiffies; | ||
1161 | |||
1162 | if (end >= start) | ||
1163 | return jiffies_to_msecs(end - start); | ||
1164 | |||
1165 | return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1); | ||
1166 | } | ||
1167 | |||
1168 | static char * | 1213 | static char * |
1169 | ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info, | 1214 | ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info, |
1170 | struct cfg80211_internal_bss *bss, char *current_ev, | 1215 | struct cfg80211_internal_bss *bss, char *current_ev, |
@@ -1241,15 +1286,10 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info, | |||
1241 | 1286 | ||
1242 | rcu_read_lock(); | 1287 | rcu_read_lock(); |
1243 | ies = rcu_dereference(bss->pub.ies); | 1288 | ies = rcu_dereference(bss->pub.ies); |
1244 | if (ies) { | 1289 | rem = ies->len; |
1245 | rem = ies->len; | 1290 | ie = ies->data; |
1246 | ie = ies->data; | ||
1247 | } else { | ||
1248 | rem = 0; | ||
1249 | ie = NULL; | ||
1250 | } | ||
1251 | 1291 | ||
1252 | while (ies && rem >= 2) { | 1292 | while (rem >= 2) { |
1253 | /* invalid data */ | 1293 | /* invalid data */ |
1254 | if (ie[1] > rem - 2) | 1294 | if (ie[1] > rem - 2) |
1255 | break; | 1295 | break; |
@@ -1362,7 +1402,7 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info, | |||
1362 | if (buf) { | 1402 | if (buf) { |
1363 | memset(&iwe, 0, sizeof(iwe)); | 1403 | memset(&iwe, 0, sizeof(iwe)); |
1364 | iwe.cmd = IWEVCUSTOM; | 1404 | iwe.cmd = IWEVCUSTOM; |
1365 | sprintf(buf, "tsf=%016llx", (unsigned long long)(bss->pub.tsf)); | 1405 | sprintf(buf, "tsf=%016llx", (unsigned long long)(ies->tsf)); |
1366 | iwe.u.data.length = strlen(buf); | 1406 | iwe.u.data.length = strlen(buf); |
1367 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | 1407 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, |
1368 | &iwe, buf); | 1408 | &iwe, buf); |