/* * cfg80211 scan result handling * * Copyright 2008 Johannes Berg <johannes@sipsolutions.net> */#include <linux/kernel.h>#include <linux/slab.h>#include <linux/module.h>#include <linux/netdevice.h>#include <linux/wireless.h>#include <linux/nl80211.h>#include <linux/etherdevice.h>#include <net/arp.h>#include <net/cfg80211.h>#include <net/cfg80211-wext.h>#include <net/iw_handler.h>#include "core.h"#include "nl80211.h"#include "wext-compat.h" #define IEEE80211_SCAN_RESULT_EXPIRE (30 * HZ) void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak){ struct cfg80211_scan_request *request; struct net_device *dev;#ifdef CONFIG_CFG80211_WEXT union iwreq_data wrqu;#endif ASSERT_RDEV_LOCK(rdev); request = rdev->scan_req; if (!request) return; dev = request->dev; /* * This must be before sending the other events! * Otherwise, wpa_supplicant gets completely confused with * wext events. */ cfg80211_sme_scan_done(dev); if (request->aborted) nl80211_send_scan_aborted(rdev, dev); else nl80211_send_scan_done(rdev, dev); #ifdef CONFIG_CFG80211_WEXT if (!request->aborted) { memset(&wrqu, 0, sizeof(wrqu)); wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL); }#endif dev_put(dev); rdev->scan_req = NULL; /* * OK. If this is invoked with "leak" then we can't * free this ... but we've cleaned it up anyway. The * driver failed to call the scan_done callback, so * all bets are off, it might still be trying to use * the scan request or not ... if it accesses the dev * in there (it shouldn't anyway) then it may crash. */ if (!leak) kfree(request);} void __cfg80211_scan_done(struct work_struct *wk){ struct cfg80211_registered_device *rdev; rdev = container_of(wk, struct cfg80211_registered_device, scan_done_wk); cfg80211_lock_rdev(rdev); ___cfg80211_scan_done(rdev, false); cfg80211_unlock_rdev(rdev);} void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted){ WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req); request->aborted = aborted; queue_work(cfg80211_wq, &wiphy_to_dev(request->wiphy)->scan_done_wk);}EXPORT_SYMBOL(cfg80211_scan_done); void __cfg80211_sched_scan_results(struct work_struct *wk){ struct cfg80211_registered_device *rdev; rdev = container_of(wk, struct cfg80211_registered_device, sched_scan_results_wk); mutex_lock(&rdev->sched_scan_mtx); /* we don't have sched_scan_req anymore if the scan is stopping */ if (rdev->sched_scan_req) nl80211_send_sched_scan_results(rdev, rdev->sched_scan_req->dev); mutex_unlock(&rdev->sched_scan_mtx);} void cfg80211_sched_scan_results(struct wiphy *wiphy){ /* ignore if we're not scanning */ if (wiphy_to_dev(wiphy)->sched_scan_req) queue_work(cfg80211_wq, &wiphy_to_dev(wiphy)->sched_scan_results_wk);}EXPORT_SYMBOL(cfg80211_sched_scan_results); void cfg80211_sched_scan_stopped(struct wiphy *wiphy){ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); mutex_lock(&rdev->sched_scan_mtx); __cfg80211_stop_sched_scan(rdev, true); mutex_unlock(&rdev->sched_scan_mtx);}EXPORT_SYMBOL(cfg80211_sched_scan_stopped); int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, bool driver_initiated){ struct net_device *dev; lockdep_assert_held(&rdev->sched_scan_mtx); if (!rdev->sched_scan_req) return -ENOENT; dev = rdev->sched_scan_req->dev; if (!driver_initiated) { int err = rdev->ops->sched_scan_stop(&rdev->wiphy, dev); if (err) return err; } nl80211_send_sched_scan(rdev, dev, NL80211_CMD_SCHED_SCAN_STOPPED); kfree(rdev->sched_scan_req); rdev->sched_scan_req = NULL; return 0;} static void bss_release(struct kref *ref){ struct cfg80211_internal_bss *bss; bss = container_of(ref, struct cfg80211_internal_bss, ref); if (bss->pub.free_priv) bss->pub.free_priv(&bss->pub); if (bss->beacon_ies_allocated) kfree(bss->pub.beacon_ies); if (bss->proberesp_ies_allocated) kfree(bss->pub.proberesp_ies); BUG_ON(atomic_read(&bss->hold)); kfree(bss);} /* must hold dev->bss_lock! */void cfg80211_bss_age(struct cfg80211_registered_device *dev, unsigned long age_secs){ struct cfg80211_internal_bss *bss; unsigned long age_jiffies = msecs_to_jiffies(age_secs * MSEC_PER_SEC); list_for_each_entry(bss, &dev->bss_list, list) { bss->ts -= age_jiffies; }} /* must hold dev->bss_lock! */static void __cfg80211_unlink_bss(struct cfg80211_registered_device *dev, struct cfg80211_internal_bss *bss){ list_del_init(&bss->list); rb_erase(&bss->rbn, &dev->bss_tree); kref_put(&bss->ref, bss_release);} /* must hold dev->bss_lock! */void cfg80211_bss_expire(struct cfg80211_registered_device *dev){ struct cfg80211_internal_bss *bss, *tmp; bool expired = false; list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) { if (atomic_read(&bss->hold)) continue; if (!time_after(jiffies, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE)) continue; __cfg80211_unlink_bss(dev, bss); expired = true; } if (expired) dev->bss_generation++;} const u8 *cfg80211_find_ie(u8 eid, const u8 *ies, int len){ while (len > 2 && ies[0] != eid) { len -= ies[1] + 2; ies += ies[1] + 2; } if (len < 2) return NULL; if (len < 2 + ies[1]) return NULL; return ies;}EXPORT_SYMBOL(cfg80211_find_ie); const u8 *cfg80211_find_vendor_ie(unsigned int oui, u8 oui_type, const u8 *ies, int len){ struct ieee80211_vendor_ie *ie; const u8 *pos = ies, *end = ies + len; int ie_oui; while (pos < end) { pos = cfg80211_find_ie(WLAN_EID_VENDOR_SPECIFIC, pos, end - pos); if (!pos) return NULL; if (end - pos < sizeof(*ie)) return NULL; ie = (struct ieee80211_vendor_ie *)pos; ie_oui = ie->oui[0] << 16 | ie->oui[1] << 8 | ie->oui[2]; if (ie_oui == oui && ie->oui_type == oui_type) return pos; pos += 2 + ie->len; } return NULL;}EXPORT_SYMBOL(cfg80211_find_vendor_ie); static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2){ const u8 *ie1 = cfg80211_find_ie(num, ies1, len1); const u8 *ie2 = cfg80211_find_ie(num, ies2, len2); /* equal if both missing */ if (!ie1 && !ie2) return 0; /* sort missing IE before (left of) present IE */ if (!ie1) return -1; if (!ie2) return 1; /* sort by length first, then by contents */ if (ie1[1] != ie2[1]) return ie2[1] - ie1[1]; return memcmp(ie1 + 2, ie2 + 2, ie1[1]);} static bool is_bss(struct cfg80211_bss *a, const u8 *bssid, const u8 *ssid, size_t ssid_len){ const u8 *ssidie; if (bssid && !ether_addr_equal(a->bssid, bssid)) return false; if (!ssid) return true; ssidie = cfg80211_find_ie(WLAN_EID_SSID, a->information_elements, a->len_information_elements); if (!ssidie) return false; if (ssidie[1] != ssid_len) return false; return memcmp(ssidie + 2, ssid, ssid_len) == 0;} static bool is_mesh_bss(struct cfg80211_bss *a){ const u8 *ie; if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability)) return false; ie = cfg80211_find_ie(WLAN_EID_MESH_ID, a->information_elements, a->len_information_elements); if (!ie) return false; ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, a->information_elements, a->len_information_elements); if (!ie) return false; return true;} static bool is_mesh(struct cfg80211_bss *a, const u8 *meshid, size_t meshidlen, const u8 *meshcfg){ const u8 *ie; if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability)) return false; ie = cfg80211_find_ie(WLAN_EID_MESH_ID, a->information_elements, a->len_information_elements); if (!ie) return false; if (ie[1] != meshidlen) return false; if (memcmp(ie + 2, meshid, meshidlen)) return false; ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, a->information_elements, a->len_information_elements); if (!ie) return false; if (ie[1] != sizeof(struct ieee80211_meshconf_ie)) return false; /* * Ignore mesh capability (last two bytes of the IE) when * comparing since that may differ between stations taking * part in the same mesh. */ return memcmp(ie + 2, meshcfg, sizeof(struct ieee80211_meshconf_ie) - 2) == 0;} static int cmp_bss_core(struct cfg80211_bss *a, struct cfg80211_bss *b){ int r; if (a->channel != b->channel) return b->channel->center_freq - a->channel->center_freq; if (is_mesh_bss(a) && is_mesh_bss(b)) { r = cmp_ies(WLAN_EID_MESH_ID, a->information_elements, a->len_information_elements, b->information_elements, b->len_information_elements); if (r) return r; return cmp_ies(WLAN_EID_MESH_CONFIG, a->information_elements, a->len_information_elements, b->information_elements, b->len_information_elements); } /* * we can't use compare_ether_addr here since we need a < > operator. * The binary return value of compare_ether_addr isn't enough */ return memcmp(a->bssid, b->bssid, sizeof(a->bssid));} static int cmp_bss(struct cfg80211_bss *a, struct cfg80211_bss *b){ int r; r = cmp_bss_core(a, b); if (r) return r; return cmp_ies(WLAN_EID_SSID, a->information_elements, a->len_information_elements, b->information_elements, b->len_information_elements);} static int cmp_hidden_bss(struct cfg80211_bss *a, struct cfg80211_bss *b){ const u8 *ie1; const u8 *ie2; int i; int r; r = cmp_bss_core(a, b); if (r) return r; ie1 = cfg80211_find_ie(WLAN_EID_SSID, a->information_elements, a->len_information_elements); ie2 = cfg80211_find_ie(WLAN_EID_SSID, b->information_elements, b->len_information_elements); /* Key comparator must use same algorithm in any rb-tree * search function (order is important), otherwise ordering * of items in the tree is broken and search gives incorrect * results. This code uses same order as cmp_ies() does. */ /* sort missing IE before (left of) present IE */ if (!ie1) return -1; if (!ie2) return 1; /* zero-size SSID is used as an indication of the hidden bss */ if (!ie2[1]) return 0; /* sort by length first, then by contents */ if (ie1[1] != ie2[1]) return ie2[1] - ie1[1]; /* zeroed SSID ie is another indication of a hidden bss */ for (i = 0; i < ie2[1]; i++) if (ie2[i + 2]) return -1; return 0;} struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, struct ieee80211_channel *channel, const u8 *bssid, const u8 *ssid, size_t ssid_len, u16 capa_mask, u16 capa_val){ struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy); struct cfg80211_internal_bss *bss, *res = NULL; unsigned long now = jiffies; spin_lock_bh(&dev->bss_lock); list_for_each_entry(bss, &dev->bss_list, list) { if ((bss->pub.capability & capa_mask) != capa_val) continue; if (channel && bss->pub.channel != channel) continue; /* Don't get expired BSS structs */ if (time_after(now, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE) && !atomic_read(&bss->hold)) continue; if (is_bss(&bss->pub, bssid, ssid, ssid_len)) { res = bss; kref_get(&res->ref); break; } } spin_unlock_bh(&dev->bss_lock); if (!res) return NULL; return &res->pub;}EXPORT_SYMBOL(cfg80211_get_bss); struct cfg80211_bss *cfg80211_get_mesh(struct wiphy *wiphy, struct ieee80211_channel *channel, const u8 *meshid, size_t meshidlen, const u8 *meshcfg){ struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy); struct cfg80211_internal_bss *bss, *res = NULL; spin_lock_bh(&dev->bss_lock); list_for_each_entry(bss, &dev->bss_list, list) { if (channel && bss->pub.channel != channel) continue; if (is_mesh(&bss->pub, meshid, meshidlen, meshcfg)) { res = bss; kref_get(&res->ref);Lines 1–500 of 1357. Load more
/* * cfg80211 scan result handling * * Copyright 2008 Johannes Berg <johannes@sipsolutions.net> */ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/netdevice.h> #include <linux/wireless.h> #include <linux/nl80211.h> #include <linux/etherdevice.h> #include <net/arp.h> #include <net/cfg80211.h> #include <net/cfg80211-wext.h> #include <net/iw_handler.h> #include "core.h" #include "nl80211.h" #include "wext-compat.h" #define IEEE80211_SCAN_RESULT_EXPIRE (30 * HZ) void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak) { struct cfg80211_scan_request *request; struct net_device *dev; #ifdef CONFIG_CFG80211_WEXT union iwreq_data wrqu; #endif ASSERT_RDEV_LOCK(rdev); request = rdev->scan_req; if (!request) return; dev = request->dev; /* * This must be before sending the other events! * Otherwise, wpa_supplicant gets completely confused with * wext events. */ cfg80211_sme_scan_done(dev); if (request->aborted) nl80211_send_scan_aborted(rdev, dev); else nl80211_send_scan_done(rdev, dev); #ifdef CONFIG_CFG80211_WEXT if (!request->aborted) { memset(&wrqu, 0, sizeof(wrqu)); wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL); } #endif dev_put(dev); rdev->scan_req = NULL; /* * OK. If this is invoked with "leak" then we can't * free this ... but we've cleaned it up anyway. The * driver failed to call the scan_done callback, so * all bets are off, it might still be trying to use * the scan request or not ... if it accesses the dev * in there (it shouldn't anyway) then it may crash. */ if (!leak) kfree(request); } void __cfg80211_scan_done(struct work_struct *wk) { struct cfg80211_registered_device *rdev; rdev = container_of(wk, struct cfg80211_registered_device, scan_done_wk); cfg80211_lock_rdev(rdev); ___cfg80211_scan_done(rdev, false); cfg80211_unlock_rdev(rdev); } void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) { WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req); request->aborted = aborted; queue_work(cfg80211_wq, &wiphy_to_dev(request->wiphy)->scan_done_wk); } EXPORT_SYMBOL(cfg80211_scan_done); void __cfg80211_sched_scan_results(struct work_struct *wk) { struct cfg80211_registered_device *rdev; rdev = container_of(wk, struct cfg80211_registered_device, sched_scan_results_wk); mutex_lock(&rdev->sched_scan_mtx); /* we don't have sched_scan_req anymore if the scan is stopping */ if (rdev->sched_scan_req) nl80211_send_sched_scan_results(rdev, rdev->sched_scan_req->dev); mutex_unlock(&rdev->sched_scan_mtx); } void cfg80211_sched_scan_results(struct wiphy *wiphy) { /* ignore if we're not scanning */ if (wiphy_to_dev(wiphy)->sched_scan_req) queue_work(cfg80211_wq, &wiphy_to_dev(wiphy)->sched_scan_results_wk); } EXPORT_SYMBOL(cfg80211_sched_scan_results); void cfg80211_sched_scan_stopped(struct wiphy *wiphy) { struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); mutex_lock(&rdev->sched_scan_mtx); __cfg80211_stop_sched_scan(rdev, true); mutex_unlock(&rdev->sched_scan_mtx); } EXPORT_SYMBOL(cfg80211_sched_scan_stopped); int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, bool driver_initiated) { struct net_device *dev; lockdep_assert_held(&rdev->sched_scan_mtx); if (!rdev->sched_scan_req) return -ENOENT; dev = rdev->sched_scan_req->dev; if (!driver_initiated) { int err = rdev->ops->sched_scan_stop(&rdev->wiphy, dev); if (err) return err; } nl80211_send_sched_scan(rdev, dev, NL80211_CMD_SCHED_SCAN_STOPPED); kfree(rdev->sched_scan_req); rdev->sched_scan_req = NULL; return 0; } static void bss_release(struct kref *ref) { struct cfg80211_internal_bss *bss; bss = container_of(ref, struct cfg80211_internal_bss, ref); if (bss->pub.free_priv) bss->pub.free_priv(&bss->pub); if (bss->beacon_ies_allocated) kfree(bss->pub.beacon_ies); if (bss->proberesp_ies_allocated) kfree(bss->pub.proberesp_ies); BUG_ON(atomic_read(&bss->hold)); kfree(bss); } /* must hold dev->bss_lock! */ void cfg80211_bss_age(struct cfg80211_registered_device *dev, unsigned long age_secs) { struct cfg80211_internal_bss *bss; unsigned long age_jiffies = msecs_to_jiffies(age_secs * MSEC_PER_SEC); list_for_each_entry(bss, &dev->bss_list, list) { bss->ts -= age_jiffies; } } /* must hold dev->bss_lock! */ static void __cfg80211_unlink_bss(struct cfg80211_registered_device *dev, struct cfg80211_internal_bss *bss) { list_del_init(&bss->list); rb_erase(&bss->rbn, &dev->bss_tree); kref_put(&bss->ref, bss_release); } /* must hold dev->bss_lock! */ void cfg80211_bss_expire(struct cfg80211_registered_device *dev) { struct cfg80211_internal_bss *bss, *tmp; bool expired = false; list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) { if (atomic_read(&bss->hold)) continue; if (!time_after(jiffies, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE)) continue; __cfg80211_unlink_bss(dev, bss); expired = true; } if (expired) dev->bss_generation++; } const u8 *cfg80211_find_ie(u8 eid, const u8 *ies, int len) { while (len > 2 && ies[0] != eid) { len -= ies[1] + 2; ies += ies[1] + 2; } if (len < 2) return NULL; if (len < 2 + ies[1]) return NULL; return ies; } EXPORT_SYMBOL(cfg80211_find_ie); const u8 *cfg80211_find_vendor_ie(unsigned int oui, u8 oui_type, const u8 *ies, int len) { struct ieee80211_vendor_ie *ie; const u8 *pos = ies, *end = ies + len; int ie_oui; while (pos < end) { pos = cfg80211_find_ie(WLAN_EID_VENDOR_SPECIFIC, pos, end - pos); if (!pos) return NULL; if (end - pos < sizeof(*ie)) return NULL; ie = (struct ieee80211_vendor_ie *)pos; ie_oui = ie->oui[0] << 16 | ie->oui[1] << 8 | ie->oui[2]; if (ie_oui == oui && ie->oui_type == oui_type) return pos; pos += 2 + ie->len; } return NULL; } EXPORT_SYMBOL(cfg80211_find_vendor_ie); static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2) { const u8 *ie1 = cfg80211_find_ie(num, ies1, len1); const u8 *ie2 = cfg80211_find_ie(num, ies2, len2); /* equal if both missing */ if (!ie1 && !ie2) return 0; /* sort missing IE before (left of) present IE */ if (!ie1) return -1; if (!ie2) return 1; /* sort by length first, then by contents */ if (ie1[1] != ie2[1]) return ie2[1] - ie1[1]; return memcmp(ie1 + 2, ie2 + 2, ie1[1]); } static bool is_bss(struct cfg80211_bss *a, const u8 *bssid, const u8 *ssid, size_t ssid_len) { const u8 *ssidie; if (bssid && !ether_addr_equal(a->bssid, bssid)) return false; if (!ssid) return true; ssidie = cfg80211_find_ie(WLAN_EID_SSID, a->information_elements, a->len_information_elements); if (!ssidie) return false; if (ssidie[1] != ssid_len) return false; return memcmp(ssidie + 2, ssid, ssid_len) == 0; } static bool is_mesh_bss(struct cfg80211_bss *a) { const u8 *ie; if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability)) return false; ie = cfg80211_find_ie(WLAN_EID_MESH_ID, a->information_elements, a->len_information_elements); if (!ie) return false; ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, a->information_elements, a->len_information_elements); if (!ie) return false; return true; } static bool is_mesh(struct cfg80211_bss *a, const u8 *meshid, size_t meshidlen, const u8 *meshcfg) { const u8 *ie; if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability)) return false; ie = cfg80211_find_ie(WLAN_EID_MESH_ID, a->information_elements, a->len_information_elements); if (!ie) return false; if (ie[1] != meshidlen) return false; if (memcmp(ie + 2, meshid, meshidlen)) return false; ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, a->information_elements, a->len_information_elements); if (!ie) return false; if (ie[1] != sizeof(struct ieee80211_meshconf_ie)) return false; /* * Ignore mesh capability (last two bytes of the IE) when * comparing since that may differ between stations taking * part in the same mesh. */ return memcmp(ie + 2, meshcfg, sizeof(struct ieee80211_meshconf_ie) - 2) == 0; } static int cmp_bss_core(struct cfg80211_bss *a, struct cfg80211_bss *b) { int r; if (a->channel != b->channel) return b->channel->center_freq - a->channel->center_freq; if (is_mesh_bss(a) && is_mesh_bss(b)) { r = cmp_ies(WLAN_EID_MESH_ID, a->information_elements, a->len_information_elements, b->information_elements, b->len_information_elements); if (r) return r; return cmp_ies(WLAN_EID_MESH_CONFIG, a->information_elements, a->len_information_elements, b->information_elements, b->len_information_elements); } /* * we can't use compare_ether_addr here since we need a < > operator. * The binary return value of compare_ether_addr isn't enough */ return memcmp(a->bssid, b->bssid, sizeof(a->bssid)); } static int cmp_bss(struct cfg80211_bss *a, struct cfg80211_bss *b) { int r; r = cmp_bss_core(a, b); if (r) return r; return cmp_ies(WLAN_EID_SSID, a->information_elements, a->len_information_elements, b->information_elements, b->len_information_elements); } static int cmp_hidden_bss(struct cfg80211_bss *a, struct cfg80211_bss *b) { const u8 *ie1; const u8 *ie2; int i; int r; r = cmp_bss_core(a, b); if (r) return r; ie1 = cfg80211_find_ie(WLAN_EID_SSID, a->information_elements, a->len_information_elements); ie2 = cfg80211_find_ie(WLAN_EID_SSID, b->information_elements, b->len_information_elements); /* Key comparator must use same algorithm in any rb-tree * search function (order is important), otherwise ordering * of items in the tree is broken and search gives incorrect * results. This code uses same order as cmp_ies() does. */ /* sort missing IE before (left of) present IE */ if (!ie1) return -1; if (!ie2) return 1; /* zero-size SSID is used as an indication of the hidden bss */ if (!ie2[1]) return 0; /* sort by length first, then by contents */ if (ie1[1] != ie2[1]) return ie2[1] - ie1[1]; /* zeroed SSID ie is another indication of a hidden bss */ for (i = 0; i < ie2[1]; i++) if (ie2[i + 2]) return -1; return 0; } struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, struct ieee80211_channel *channel, const u8 *bssid, const u8 *ssid, size_t ssid_len, u16 capa_mask, u16 capa_val) { struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy); struct cfg80211_internal_bss *bss, *res = NULL; unsigned long now = jiffies; spin_lock_bh(&dev->bss_lock); list_for_each_entry(bss, &dev->bss_list, list) { if ((bss->pub.capability & capa_mask) != capa_val) continue; if (channel && bss->pub.channel != channel) continue; /* Don't get expired BSS structs */ if (time_after(now, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE) && !atomic_read(&bss->hold)) continue; if (is_bss(&bss->pub, bssid, ssid, ssid_len)) { res = bss; kref_get(&res->ref); break; } } spin_unlock_bh(&dev->bss_lock); if (!res) return NULL; return &res->pub; } EXPORT_SYMBOL(cfg80211_get_bss); struct cfg80211_bss *cfg80211_get_mesh(struct wiphy *wiphy, struct ieee80211_channel *channel, const u8 *meshid, size_t meshidlen, const u8 *meshcfg) { struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy); struct cfg80211_internal_bss *bss, *res = NULL; spin_lock_bh(&dev->bss_lock); list_for_each_entry(bss, &dev->bss_list, list) { if (channel && bss->pub.channel != channel) continue; if (is_mesh(&bss->pub, meshid, meshidlen, meshcfg)) { res = bss; kref_get(&res->ref);