/*
* 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"
#include "rdev-ops.h"
/**
* DOC: BSS tree/list structure
*
* At the top level, the BSS list is kept in both a list in each
* registered device (@bss_list) as well as an RB-tree for faster
* lookup. In the RB-tree, entries can be looked up using their
* channel, MESHID, MESHCONF (for MBSSes) or channel, BSSID, SSID
* for other BSSes.
*
* Due to the possibility of hidden SSIDs, there's a second level
* structure, the "hidden_list" and "hidden_beacon_bss" pointer.
* The hidden_list connects all BSSes belonging to a single AP
* that has a hidden SSID, and connects beacon and probe response
* entries. For a probe response entry for a hidden SSID, the
* hidden_beacon_bss pointer points to the BSS struct holding the
* beacon's information.
*
* Reference counting is done for all these references except for
* the hidden_list, so that a beacon BSS struct that is otherwise
* not referenced has one reference for being on the bss_list and
* one for each probe response entry that points to it using the
* hidden_beacon_bss pointer. When a BSS struct that has such a
* pointer is get/put, the refcount update is also propagated to
* the referenced struct, this ensure that it cannot get removed
* while somebody is using the probe response version.
*
* Note that the hidden_beacon_bss pointer never changes, due to
* the reference counting. Therefore, no locking is needed for
* it.
*
* Also note that the hidden_beacon_bss pointer is only relevant
* if the driver uses something other than the IEs, e.g. private
* data stored stored in the BSS struct, since the beacon IEs are
* also linked into the probe response struct.
*/
#define IEEE80211_SCAN_RESULT_EXPIRE (30 * HZ)
static void bss_free(struct cfg80211_internal_bss *bss)
{
struct cfg80211_bss_ies *ies;
if (WARN_ON(atomic_read(&bss->hold)))
return;
ies = (void *)rcu_access_pointer(bss->pub.beacon_ies);
if (ies && !bss->pub.hidden_beacon_bss)
kfree_rcu(ies, rcu_head);
ies = (void *)rcu_access_pointer(bss->pub.proberesp_ies);
if (ies)
kfree_rcu(ies, rcu_head);
/*
* This happens when the module is removed, it doesn't
* really matter any more save for completeness
*/
if (!list_empty(&bss->hidden_list))
list_del(&bss->hidden_list);
kfree(bss);
}
static inline void bss_ref_get(struct cfg80211_registered_device *dev,
struct cfg80211_internal_bss *bss)
{
lockdep_assert_held(&dev->bss_lock);
bss->refcount++;
if (bss->pub.hidden_beacon_bss) {
bss = container_of(bss->pub.hidden_beacon_bss,
struct cfg80211_internal_bss,
pub);
bss->refcount++;
}
}
static inline void bss_ref_put(struct cfg80211_registered_device *dev,
struct cfg80211_internal_bss *bss)
{
lockdep_assert_held(&dev->bss_lock);
if (bss->pub.hidden_beacon_bss) {
struct cfg80211_internal_bss *hbss;
hbss = container_of(bss->pub.hidden_beacon_bss,
struct cfg80211_internal_bss,
pub);
hbss->refcount--;
if (hbss->refcount == 0)
bss_free(hbss);
}
bss->refcount--;
if (bss->refcount == 0)
bss_free(bss);
}
static bool __cfg80211_unlink_bss(struct cfg80211_registered_device *dev,
struct cfg80211_internal_bss *bss)
{
lockdep_assert_held(&dev->bss_lock);
if (!list_empty(&bss->hidden_list)) {
/*
* don't remove the beacon entry if it has
* probe responses associated with it
*/
if (!bss->pub.hidden_beacon_bss)
return false;
/*
* if it's a probe response entry break its
* link to the other entries in the group
*/
list_del_init(&bss->hidden_list);
}
list_del_init(&bss->list);
rb_erase(&bss->rbn, &dev->bss_tree);
bss_ref_put(dev, bss);
return true;
}
static void __cfg80211_bss_expire(struct cfg80211_registered_device *dev,
unsigned long expire_time)
{
struct cfg80211_internal_bss *bss, *tmp;
bool expired = false;
lockdep_assert_held(&dev->bss_lock);
list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) {
if (atomic_read(&bss->hold))
continue;
if (!time_after(expire_time, bss->ts))
continue;
if (__cfg80211_unlink_bss(dev, bss))
expired = true;
}
if (expired)
dev->bss_generation++;
}
void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev,
bool send_message)
{
struct cfg80211_scan_request *request;
struct wireless_dev *wdev;
struct sk_buff *msg;
#ifdef CONFIG_CFG80211_WEXT
union iwreq_data wrqu;
#endif
ASSERT_RTNL();
if (rdev->scan_msg) {
nl80211_send_scan_result(rdev, rdev->scan_msg);
rdev->scan_msg = NULL;
return;
}
request = rdev->scan_req;
if (!request)
return;
wdev = request->wdev;
/*
* This must be before sending the other events!
* Otherwise, wpa_supplicant gets completely confused with
* wext events.
*/
if (wdev->netdev)
cfg80211_sme_scan_done(wdev->netdev);
if (!request->aborted &&
request->flags & NL80211_SCAN_FLAG_FLUSH) {
/* flush entries from previous scans */
spin_lock_bh(&rdev->bss_lock);
__cfg80211_bss_expire(rdev, request->scan_start);
spin_unlock_bh(&rdev->bss_lock);
}
msg = nl80211_build_scan_msg(rdev, wdev, request->aborted);
#ifdef CONFIG_CFG80211_WEXT
if (wdev->netdev && !request->aborted) {
memset(&wrqu, 0, sizeof(wrqu));
wireless_send_event(wdev->netdev, SIOCGIWSCAN, &wrqu, NULL);
}
#endif
if (wdev->netdev)
dev_put(wdev->netdev);
rdev->scan_req = NULL;
kfree(request);
if (!send_message)
rdev->scan_msg = msg;
else
nl80211_send_scan_result(rdev, msg);
}
void __cfg80211_scan_done(struct work_struct *wk)
{
struct cfg80211_registered_device *rdev;
rdev = container_of(wk, struct cfg80211_registered_device,
scan_done_wk);
rtnl_lock();
___cfg80211_scan_done(rdev, true);
rtnl_unlock();
}
void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted)
{
trace_cfg80211_scan_done(request, aborted);
WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req);
request->aborted = aborted;
request->notified = true;
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;
struct cfg80211_sched_scan_request *request;
rdev = container_of(wk, struct cfg80211_registered_device,
sched_scan_results_wk);
rtnl_lock();
request = rdev->sched_scan_req;
/* we don't have sched_scan_req anymore if the scan is stopping */
if (request) {
if (request->flags & NL80211_SCAN_FLAG_FLUSH) {
/* flush entries from previous scans */
spin_lock_bh(&rdev->bss_lock);
__cfg80211_bss_expire(rdev, request->scan_start);
spin_unlock_bh(&rdev->bss_lock);
request->scan_start =
jiffies + msecs_to_jiffies(request->interval);
}
nl80211_send_sched_scan_results(rdev, request->dev);
}
rtnl_unlock();
}
void cfg80211_sched_scan_results(struct wiphy *wiphy)
{
trace_cfg80211_sched_scan_results(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);
trace_cfg80211_sched_scan_stopped(wiphy);
rtnl_lock();
__cfg80211_stop_sched_scan(rdev, true);
rtnl_unlock();
}
EXPORT_SYMBOL(cfg80211_sched_scan_stopped);
int __cfg80211_stop_sched_scan(str
|