📄 ieee80211_scan.c
字号:
/*- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU General Public License ("GPL") version 2 as published by the Free * Software Foundation. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $Id: ieee80211_scan.c 1520 2006-04-21 16:57:59Z dyqith $ */#ifndef EXPORT_SYMTAB#define EXPORT_SYMTAB#endif/* * IEEE 802.11 scanning support. */#include <linux/config.h>#include <linux/version.h>#include <linux/module.h>#include <linux/skbuff.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/random.h>#include <linux/interrupt.h>#include <linux/delay.h>#include "if_media.h"#include <net80211/ieee80211_var.h>#include <net80211/if_athproto.h>struct scan_state { struct ieee80211_scan_state base; /* public state */ u_int ss_iflags; /* flags used internally */#define ISCAN_MINDWELL 0x0001 /* min dwell time reached */#define ISCAN_DISCARD 0x0002 /* discard rx'd frames */#define ISCAN_CANCEL 0x0004 /* cancel current scan */#define ISCAN_START 0x0008 /* 1st time through next_scan */ unsigned long ss_chanmindwell; /* min dwell on curchan */ unsigned long ss_scanend; /* time scan must stop */ u_int ss_duration; /* duration for next scan */ struct tasklet_struct ss_pwrsav; /* sta ps ena tasklet */ struct timer_list ss_scan_timer; /* scan timer */};#define SCAN_PRIVATE(ss) ((struct scan_state *) ss)/* * Amount of time to go off-channel during a background * scan. This value should be large enough to catch most * ap's but short enough that we can return on-channel * before our listen interval expires. * * XXX tunable * XXX check against configured listen interval */#define IEEE80211_SCAN_OFFCHANNEL msecs_to_jiffies(150)/* * Roaming-related defaults. RSSI thresholds are as returned by the * driver (dBm). Transmit rate thresholds are IEEE rate codes (i.e * .5M units). */#define SCAN_VALID_DEFAULT 60 /* scan cache valid age (secs) */#define ROAM_RSSI_11A_DEFAULT 24 /* rssi threshold for 11a bss */#define ROAM_RSSI_11B_DEFAULT 24 /* rssi threshold for 11b bss */#define ROAM_RSSI_11BONLY_DEFAULT 24 /* rssi threshold for 11b-only bss */#define ROAM_RATE_11A_DEFAULT 2*24 /* tx rate threshold for 11a bss */#define ROAM_RATE_11B_DEFAULT 2*9 /* tx rate threshold for 11b bss */#define ROAM_RATE_11BONLY_DEFAULT 2*5 /* tx rate threshold for 11b-only bss */static void scan_restart_pwrsav(unsigned long);static void scan_next(unsigned long);voidieee80211_scan_attach(struct ieee80211com *ic){ struct scan_state *ss; ic->ic_roaming = IEEE80211_ROAMING_AUTO; MALLOC(ss, struct scan_state *, sizeof(struct scan_state), M_80211_SCAN, M_NOWAIT | M_ZERO); if (ss != NULL) { init_timer(&ss->ss_scan_timer); ss->ss_scan_timer.function = scan_next; ss->ss_scan_timer.data = (unsigned long) ss; tasklet_init(&ss->ss_pwrsav, scan_restart_pwrsav, (unsigned long) ss); ic->ic_scan = &ss->base; } else ic->ic_scan = NULL;}voidieee80211_scan_detach(struct ieee80211com *ic){ struct ieee80211_scan_state *ss = ic->ic_scan; if (ss != NULL) { del_timer(&SCAN_PRIVATE(ss)->ss_scan_timer); tasklet_kill(&SCAN_PRIVATE(ss)->ss_pwrsav); if (ss->ss_ops != NULL) { ss->ss_ops->scan_detach(ss); ss->ss_ops = NULL; } ic->ic_flags &= ~IEEE80211_F_SCAN; ic->ic_scan = NULL; FREE(SCAN_PRIVATE(ss), M_80211_SCAN); }}voidieee80211_scan_vattach(struct ieee80211vap *vap){ vap->iv_bgscanidle = msecs_to_jiffies(IEEE80211_BGSCAN_IDLE_DEFAULT); vap->iv_bgscanintvl = IEEE80211_BGSCAN_INTVAL_DEFAULT * HZ; vap->iv_scanvalid = SCAN_VALID_DEFAULT * HZ; vap->iv_roam.rssi11a = ROAM_RSSI_11A_DEFAULT; vap->iv_roam.rssi11b = ROAM_RSSI_11B_DEFAULT; vap->iv_roam.rssi11bOnly = ROAM_RSSI_11BONLY_DEFAULT; vap->iv_roam.rate11a = ROAM_RATE_11A_DEFAULT; vap->iv_roam.rate11b = ROAM_RATE_11B_DEFAULT; vap->iv_roam.rate11bOnly = ROAM_RATE_11BONLY_DEFAULT;}voidieee80211_scan_vdetach(struct ieee80211vap *vap){ struct ieee80211com *ic = vap->iv_ic; struct ieee80211_scan_state *ss = ic->ic_scan; IEEE80211_LOCK_IRQ(ic); if (ss->ss_vap == vap) { if (ic->ic_flags & IEEE80211_F_SCAN) { del_timer(&SCAN_PRIVATE(ss)->ss_scan_timer); ic->ic_flags &= ~IEEE80211_F_SCAN; } if (ss->ss_ops != NULL) { ss->ss_ops->scan_detach(ss); ss->ss_ops = NULL; } } IEEE80211_UNLOCK_IRQ(ic);}/* * Simple-minded scanner module support. */#define IEEE80211_SCANNER_MAX (IEEE80211_M_MONITOR+1)static const char *scan_modnames[IEEE80211_SCANNER_MAX] = { "wlan_scan_sta", /* IEEE80211_M_IBSS */ "wlan_scan_sta", /* IEEE80211_M_STA */ "wlan_scan_wds", /* IEEE80211_M_WDS */ "wlan_scan_sta", /* IEEE80211_M_AHDEMO */ "wlan_scan_4", /* n/a */ "wlan_scan_5", /* n/a */ "wlan_scan_ap", /* IEEE80211_M_HOSTAP */ "wlan_scan_7", /* n/a */ "wlan_scan_monitor", /* IEEE80211_M_MONITOR */};static const struct ieee80211_scanner *scanners[IEEE80211_SCANNER_MAX];const struct ieee80211_scanner *ieee80211_scanner_get(enum ieee80211_opmode mode, int tryload){ int err; if (mode >= IEEE80211_SCANNER_MAX) return NULL; if (scanners[mode] == NULL && tryload) { err = ieee80211_load_module(scan_modnames[mode]); if (scanners[mode] == NULL || err) printk(KERN_WARNING "unable to load %s\n", scan_modnames[mode]); } return scanners[mode];}EXPORT_SYMBOL(ieee80211_scanner_get);voidieee80211_scanner_register(enum ieee80211_opmode mode, const struct ieee80211_scanner *scan){ if (mode >= IEEE80211_SCANNER_MAX) return; scanners[mode] = scan;}EXPORT_SYMBOL(ieee80211_scanner_register);voidieee80211_scanner_unregister(enum ieee80211_opmode mode, const struct ieee80211_scanner *scan){ if (mode >= IEEE80211_SCANNER_MAX) return; if (scanners[mode] == scan) scanners[mode] = NULL;}EXPORT_SYMBOL(ieee80211_scanner_unregister);voidieee80211_scanner_unregister_all(const struct ieee80211_scanner *scan){ int m; for (m = 0; m < IEEE80211_SCANNER_MAX; m++) if (scanners[m] == scan) scanners[m] = NULL;}EXPORT_SYMBOL(ieee80211_scanner_unregister_all);static voidchange_channel(struct ieee80211com *ic, struct ieee80211_channel *chan){ ic->ic_curchan = chan; ic->ic_set_channel(ic);}static charchannel_type(const struct ieee80211_channel *c){ if (IEEE80211_IS_CHAN_ST(c)) return 'S'; if (IEEE80211_IS_CHAN_108A(c)) return 'T'; if (IEEE80211_IS_CHAN_108G(c)) return 'G'; if (IEEE80211_IS_CHAN_A(c)) return 'a'; if (IEEE80211_IS_CHAN_ANYG(c)) return 'g'; if (IEEE80211_IS_CHAN_B(c)) return 'b'; return 'f';}voidieee80211_scan_dump_channels(const struct ieee80211_scan_state *ss){ struct ieee80211com *ic = ss->ss_vap->iv_ic; const char *sep; int i; sep = ""; for (i = ss->ss_next; i < ss->ss_last; i++) { const struct ieee80211_channel *c = ss->ss_chans[i]; printf("%s%u%c", sep, ieee80211_chan2ieee(ic, c), channel_type(c)); sep = ", "; }}EXPORT_SYMBOL(ieee80211_scan_dump_channels);/* * Enable station power save mode and start/restart the scanning thread. */static voidscan_restart_pwrsav(unsigned long arg){ struct scan_state *ss = (struct scan_state *) arg; struct ieee80211vap *vap = ss->base.ss_vap; struct ieee80211com *ic = vap->iv_ic; int delay; ieee80211_sta_pwrsave(vap, 1); /* * Use an initial 1ms delay to ensure the null * data frame has a chance to go out. * XXX 1ms is a lot, better to trigger scan * on tx complete. */ delay = msecs_to_jiffies(1); if (delay < 1) delay = 1; ic->ic_scan_start(ic); /* notify driver */ ss->ss_scanend = jiffies + delay + ss->ss_duration; ss->ss_iflags |= ISCAN_START; mod_timer(&ss->ss_scan_timer, jiffies + delay);}/* * Start/restart scanning. If we're operating in station mode * and associated notify the ap we're going into power save mode * and schedule a callback to initiate the work (where there's a * better context for doing the work). Otherwise, start the scan * directly. */static intscan_restart(struct scan_state *ss, u_int duration){ struct ieee80211vap *vap = ss->base.ss_vap; struct ieee80211com *ic = vap->iv_ic; int defer = 0; if (ss->base.ss_next == ss->base.ss_last) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: no channels to scan\n", __func__); return 0; } if (vap->iv_opmode == IEEE80211_M_STA && vap->iv_state == IEEE80211_S_RUN) { if ((vap->iv_bss->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) { /* * Initiate power save before going off-channel. * Note that we cannot do this directly because * of locking issues; instead we defer it to a * tasklet. */ ss->ss_duration = duration; tasklet_schedule(&ss->ss_pwrsav); defer = 1; } } if (!defer) { ic->ic_scan_start(ic); /* notify driver */ ss->ss_scanend = jiffies + duration; ss->ss_iflags |= ISCAN_START; mod_timer(&ss->ss_scan_timer, jiffies); } return 1;}static voidcopy_ssid(struct ieee80211vap *vap, struct ieee80211_scan_state *ss, int nssid, const struct ieee80211_scan_ssid ssids[]){ if (nssid > IEEE80211_SCAN_MAX_SSID) { /* XXX printf */ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: too many ssid %d, ignoring all of them\n", __func__, nssid); return; } memcpy(ss->ss_ssid, ssids, nssid * sizeof(ssids[0])); ss->ss_nssid = nssid;}/* * Start a scan unless one is already going. */intieee80211_start_scan(struct ieee80211vap *vap, int flags, u_int duration, u_int nssid, const struct ieee80211_scan_ssid ssids[]){ struct ieee80211com *ic = vap->iv_ic; const struct ieee80211_scanner *scan; struct ieee80211_scan_state *ss = ic->ic_scan; scan = ieee80211_scanner_get(vap->iv_opmode, 0); if (scan == NULL) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: no scanner support for mode %u\n", __func__, vap->iv_opmode); /* XXX stat */ return 0; } IEEE80211_LOCK_IRQ(ic); if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: %s scan, duration %lu, desired mode %s, %s%s%s%s\n", __func__, flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive", duration, ieee80211_phymode_name[vap->iv_des_mode], flags & IEEE80211_SCAN_FLUSH ? "flush" : "append", flags & IEEE80211_SCAN_NOPICK ? ", nopick" : "", flags & IEEE80211_SCAN_PICK1ST ? ", pick1st" : "", flags & IEEE80211_SCAN_ONCE ? ", once" : ""); ss->ss_vap = vap; if (ss->ss_ops != scan) { /* switch scanners; detach old, attach new */ if (ss->ss_ops != NULL) ss->ss_ops->scan_detach(ss); if (!scan->scan_attach(ss)) { /* XXX attach failure */ /* XXX stat+msg */ ss->ss_ops = NULL; } else ss->ss_ops = scan; } if (ss->ss_ops != NULL) { if ((flags & IEEE80211_SCAN_NOSSID) == 0) copy_ssid(vap, ss, nssid, ssids); /* NB: top 4 bits for internal use */ ss->ss_flags = flags & 0xfff; if (ss->ss_flags & IEEE80211_SCAN_ACTIVE) vap->iv_stats.is_scan_active++; else vap->iv_stats.is_scan_passive++; if (flags & IEEE80211_SCAN_FLUSH) ss->ss_ops->scan_flush(ss); /* NB: flush frames rx'd before 1st channel change */ SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD; ss->ss_ops->scan_start(ss, vap); if (scan_restart(SCAN_PRIVATE(ss), duration)) ic->ic_flags |= IEEE80211_F_SCAN; } } else { IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: %s scan already in progress\n", __func__, ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive"); } IEEE80211_UNLOCK_IRQ(ic); /* NB: racey, does it matter? */ return (ic->ic_flags & IEEE80211_F_SCAN);}EXPORT_SYMBOL(ieee80211_start_scan);/* * Check the scan cache for an ap/channel to use; if that * fails then kick off a new scan. */intieee80211_check_scan(struct ieee80211vap *vap, int flags, u_int duration, u_int nssid, const struct ieee80211_scan_ssid ssids[], int (*action)(struct ieee80211vap *, const struct ieee80211_scan_entry *)){ struct ieee80211com *ic = vap->iv_ic; struct ieee80211_scan_state *ss = ic->ic_scan; int checkscanlist = 0; /* * Check if there's a list of scan candidates already. * XXX want more than the ap we're currently associated with */ IEEE80211_LOCK_IRQ(ic); IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: %s scan, duration %lu, desired mode %s, %s%s%s%s\n", __func__, flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive", duration, ieee80211_phymode_name[vap->iv_des_mode], flags & IEEE80211_SCAN_FLUSH ? "flush" : "append", flags & IEEE80211_SCAN_NOPICK ? ", nopick" : "", flags & IEEE80211_SCAN_PICK1ST ? ", pick1st" : "", flags & IEEE80211_SCAN_ONCE ? ", once" : "", flags & IEEE80211_SCAN_USECACHE ? ", usecache" : ""); if (ss->ss_ops != NULL) { /* XXX verify ss_ops matches vap->iv_opmode */ if ((flags & IEEE80211_SCAN_NOSSID) == 0) { /* * Update the ssid list and mark flags so if * we call start_scan it doesn't duplicate work. */ copy_ssid(vap, ss, nssid, ssids); flags |= IEEE80211_SCAN_NOSSID; } if ((ic->ic_flags & IEEE80211_F_SCAN) == 0 && time_before(jiffies, ic->ic_lastscan + vap->iv_scanvalid)) { /* * We're not currently scanning and the cache is * deemed hot enough to consult. Lock out others * by marking IEEE80211_F_SCAN while we decide if * something is already in the scan cache we can * use. Also discard any frames that might come * in while temporarily marked as scanning. */ SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD; ic->ic_flags |= IEEE80211_F_SCAN; checkscanlist = 1; } } IEEE80211_UNLOCK_IRQ(ic); if (checkscanlist) { /* * ss must be filled out so scan may be restarted "outside" * of the current callstack. */ ss->ss_flags = flags; ss->ss_duration = duration; if (ss->ss_ops->scan_end(ss, ss->ss_vap, action, flags & IEEE80211_SCAN_KEEPMODE)) { /* found an ap, just clear the flag */ ic->ic_flags &= ~IEEE80211_F_SCAN; return 1; } /* no ap, clear the flag before starting a scan */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -