📄 scan.c
字号:
/** * Functions implementing wlan scan IOCTL and firmware command APIs * * IOCTL handlers as well as command preperation and response routines * for sending scan commands to the firmware. */#include <linux/ctype.h>#include <linux/if.h>#include <linux/netdevice.h>#include <linux/wireless.h>#include <linux/etherdevice.h>#include <net/ieee80211.h>#include <net/iw_handler.h>#include <asm/unaligned.h>#include "host.h"#include "decl.h"#include "dev.h"#include "scan.h"#include "join.h"//! Approximate amount of data needed to pass a scan result back to iwlist#define MAX_SCAN_CELL_SIZE (IW_EV_ADDR_LEN \ + IW_ESSID_MAX_SIZE \ + IW_EV_UINT_LEN \ + IW_EV_FREQ_LEN \ + IW_EV_QUAL_LEN \ + IW_ESSID_MAX_SIZE \ + IW_EV_PARAM_LEN \ + 40) /* 40 for WPAIE *///! Memory needed to store a max sized channel List TLV for a firmware scan#define CHAN_TLV_MAX_SIZE (sizeof(struct mrvlietypesheader) \ + (MRVDRV_MAX_CHANNELS_PER_SCAN \ * sizeof(struct chanscanparamset)))//! Memory needed to store a max number/size SSID TLV for a firmware scan#define SSID_TLV_MAX_SIZE (1 * sizeof(struct mrvlietypes_ssidparamset))//! Maximum memory needed for a wlan_scan_cmd_config with all TLVs at max#define MAX_SCAN_CFG_ALLOC (sizeof(struct wlan_scan_cmd_config) \ + sizeof(struct mrvlietypes_numprobes) \ + CHAN_TLV_MAX_SIZE \ + SSID_TLV_MAX_SIZE)//! The maximum number of channels the firmware can scan per command#define MRVDRV_MAX_CHANNELS_PER_SCAN 14/** * @brief Number of channels to scan per firmware scan command issuance. * * Number restricted to prevent hitting the limit on the amount of scan data * returned in a single firmware scan command. */#define MRVDRV_CHANNELS_PER_SCAN_CMD 4//! Scan time specified in the channel TLV for each channel for passive scans#define MRVDRV_PASSIVE_SCAN_CHAN_TIME 100//! Scan time specified in the channel TLV for each channel for active scans#define MRVDRV_ACTIVE_SCAN_CHAN_TIME 100static const u8 zeromac[ETH_ALEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };static const u8 bcastmac[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };/*********************************************************************//* *//* Misc helper functions *//* *//*********************************************************************/static inline void clear_bss_descriptor (struct bss_descriptor * bss){ /* Don't blow away ->list, just BSS data */ memset(bss, 0, offsetof(struct bss_descriptor, list));}static inline int match_bss_no_security(struct wlan_802_11_security * secinfo, struct bss_descriptor * match_bss){ if ( !secinfo->wep_enabled && !secinfo->WPAenabled && !secinfo->WPA2enabled && match_bss->wpa_ie[0] != MFIE_TYPE_GENERIC && match_bss->rsn_ie[0] != MFIE_TYPE_RSN && !(match_bss->capability & WLAN_CAPABILITY_PRIVACY)) { return 1; } return 0;}static inline int match_bss_static_wep(struct wlan_802_11_security * secinfo, struct bss_descriptor * match_bss){ if ( secinfo->wep_enabled && !secinfo->WPAenabled && !secinfo->WPA2enabled && (match_bss->capability & WLAN_CAPABILITY_PRIVACY)) { return 1; } return 0;}static inline int match_bss_wpa(struct wlan_802_11_security * secinfo, struct bss_descriptor * match_bss){ if ( !secinfo->wep_enabled && secinfo->WPAenabled && (match_bss->wpa_ie[0] == MFIE_TYPE_GENERIC) /* privacy bit may NOT be set in some APs like LinkSys WRT54G && (match_bss->capability & WLAN_CAPABILITY_PRIVACY)) { */ ) { return 1; } return 0;}static inline int match_bss_wpa2(struct wlan_802_11_security * secinfo, struct bss_descriptor * match_bss){ if ( !secinfo->wep_enabled && secinfo->WPA2enabled && (match_bss->rsn_ie[0] == MFIE_TYPE_RSN) /* privacy bit may NOT be set in some APs like LinkSys WRT54G && (match_bss->capability & WLAN_CAPABILITY_PRIVACY)) { */ ) { return 1; } return 0;}static inline int match_bss_dynamic_wep(struct wlan_802_11_security * secinfo, struct bss_descriptor * match_bss){ if ( !secinfo->wep_enabled && !secinfo->WPAenabled && !secinfo->WPA2enabled && (match_bss->wpa_ie[0] != MFIE_TYPE_GENERIC) && (match_bss->rsn_ie[0] != MFIE_TYPE_RSN) && (match_bss->capability & WLAN_CAPABILITY_PRIVACY)) { return 1; } return 0;}/** * @brief Check if a scanned network compatible with the driver settings * * WEP WPA WPA2 ad-hoc encrypt Network * enabled enabled enabled AES mode privacy WPA WPA2 Compatible * 0 0 0 0 NONE 0 0 0 yes No security * 1 0 0 0 NONE 1 0 0 yes Static WEP * 0 1 0 0 x 1x 1 x yes WPA * 0 0 1 0 x 1x x 1 yes WPA2 * 0 0 0 1 NONE 1 0 0 yes Ad-hoc AES * 0 0 0 0 !=NONE 1 0 0 yes Dynamic WEP * * * @param adapter A pointer to wlan_adapter * @param index Index in scantable to check against current driver settings * @param mode Network mode: Infrastructure or IBSS * * @return Index in scantable, or error code if negative */static int is_network_compatible(wlan_adapter * adapter, struct bss_descriptor * bss, u8 mode){ int matched = 0; lbs_deb_enter(LBS_DEB_SCAN); if (bss->mode != mode) goto done; if ((matched = match_bss_no_security(&adapter->secinfo, bss))) { goto done; } else if ((matched = match_bss_static_wep(&adapter->secinfo, bss))) { goto done; } else if ((matched = match_bss_wpa(&adapter->secinfo, bss))) { lbs_deb_scan( "is_network_compatible() WPA: wpa_ie=%#x " "wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s " "privacy=%#x\n", bss->wpa_ie[0], bss->rsn_ie[0], adapter->secinfo.wep_enabled ? "e" : "d", adapter->secinfo.WPAenabled ? "e" : "d", adapter->secinfo.WPA2enabled ? "e" : "d", (bss->capability & WLAN_CAPABILITY_PRIVACY)); goto done; } else if ((matched = match_bss_wpa2(&adapter->secinfo, bss))) { lbs_deb_scan( "is_network_compatible() WPA2: wpa_ie=%#x " "wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s " "privacy=%#x\n", bss->wpa_ie[0], bss->rsn_ie[0], adapter->secinfo.wep_enabled ? "e" : "d", adapter->secinfo.WPAenabled ? "e" : "d", adapter->secinfo.WPA2enabled ? "e" : "d", (bss->capability & WLAN_CAPABILITY_PRIVACY)); goto done; } else if ((matched = match_bss_dynamic_wep(&adapter->secinfo, bss))) { lbs_deb_scan( "is_network_compatible() dynamic WEP: " "wpa_ie=%#x wpa2_ie=%#x privacy=%#x\n", bss->wpa_ie[0], bss->rsn_ie[0], (bss->capability & WLAN_CAPABILITY_PRIVACY)); goto done; } /* bss security settings don't match those configured on card */ lbs_deb_scan( "is_network_compatible() FAILED: wpa_ie=%#x " "wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s privacy=%#x\n", bss->wpa_ie[0], bss->rsn_ie[0], adapter->secinfo.wep_enabled ? "e" : "d", adapter->secinfo.WPAenabled ? "e" : "d", adapter->secinfo.WPA2enabled ? "e" : "d", (bss->capability & WLAN_CAPABILITY_PRIVACY));done: lbs_deb_leave_args(LBS_DEB_SCAN, "matched: %d", matched); return matched;}/** * @brief Compare two SSIDs * * @param ssid1 A pointer to ssid to compare * @param ssid2 A pointer to ssid to compare * * @return 0--ssid is same, otherwise is different */int libertas_ssid_cmp(u8 *ssid1, u8 ssid1_len, u8 *ssid2, u8 ssid2_len){ if (ssid1_len != ssid2_len) return -1; return memcmp(ssid1, ssid2, ssid1_len);}/*********************************************************************//* *//* Main scanning support *//* *//*********************************************************************//** * @brief Create a channel list for the driver to scan based on region info * * Only used from wlan_scan_setup_scan_config() * * Use the driver region/band information to construct a comprehensive list * of channels to scan. This routine is used for any scan that is not * provided a specific channel list to scan. * * @param priv A pointer to wlan_private structure * @param scanchanlist Output parameter: resulting channel list to scan * @param filteredscan Flag indicating whether or not a BSSID or SSID filter * is being sent in the command to firmware. Used to * increase the number of channels sent in a scan * command and to disable the firmware channel scan * filter. * * @return void */static void wlan_scan_create_channel_list(wlan_private * priv, struct chanscanparamset * scanchanlist, u8 filteredscan){ wlan_adapter *adapter = priv->adapter; struct region_channel *scanregion; struct chan_freq_power *cfp; int rgnidx; int chanidx; int nextchan; u8 scantype; lbs_deb_enter_args(LBS_DEB_SCAN, "filteredscan %d", filteredscan); chanidx = 0; /* Set the default scan type to the user specified type, will later * be changed to passive on a per channel basis if restricted by * regulatory requirements (11d or 11h) */ scantype = CMD_SCAN_TYPE_ACTIVE; for (rgnidx = 0; rgnidx < ARRAY_SIZE(adapter->region_channel); rgnidx++) { if (priv->adapter->enable11d && adapter->connect_status != LIBERTAS_CONNECTED) { /* Scan all the supported chan for the first scan */ if (!adapter->universal_channel[rgnidx].valid) continue; scanregion = &adapter->universal_channel[rgnidx]; /* clear the parsed_region_chan for the first scan */ memset(&adapter->parsed_region_chan, 0x00, sizeof(adapter->parsed_region_chan)); } else { if (!adapter->region_channel[rgnidx].valid) continue; scanregion = &adapter->region_channel[rgnidx]; } for (nextchan = 0; nextchan < scanregion->nrcfp; nextchan++, chanidx++) { cfp = scanregion->CFP + nextchan; if (priv->adapter->enable11d) { scantype = libertas_get_scan_type_11d(cfp->channel, &adapter-> parsed_region_chan); } switch (scanregion->band) { case BAND_B: case BAND_G: default: scanchanlist[chanidx].radiotype = CMD_SCAN_RADIO_TYPE_BG; break; } if (scantype == CMD_SCAN_TYPE_PASSIVE) { scanchanlist[chanidx].maxscantime = cpu_to_le16(MRVDRV_PASSIVE_SCAN_CHAN_TIME); scanchanlist[chanidx].chanscanmode.passivescan = 1; } else { scanchanlist[chanidx].maxscantime = cpu_to_le16(MRVDRV_ACTIVE_SCAN_CHAN_TIME); scanchanlist[chanidx].chanscanmode.passivescan = 0; } scanchanlist[chanidx].channumber = cfp->channel; if (filteredscan) { scanchanlist[chanidx].chanscanmode. disablechanfilt = 1; } } }}/* Delayed partial scan worker */void libertas_scan_worker(struct work_struct *work){ wlan_private *priv = container_of(work, wlan_private, scan_work.work); wlan_scan_networks(priv, NULL, 0);}/** * @brief Construct a wlan_scan_cmd_config structure to use in issue scan cmds * * Application layer or other functions can invoke wlan_scan_networks * with a scan configuration supplied in a wlan_ioctl_user_scan_cfg struct. * This structure is used as the basis of one or many wlan_scan_cmd_config * commands that are sent to the command processing module and sent to * firmware. * * Create a wlan_scan_cmd_config based on the following user supplied * parameters (if present): * - SSID filter * - BSSID filter * - Number of Probes to be sent * - channel list * * If the SSID or BSSID filter is not present, disable/clear the filter. * If the number of probes is not set, use the adapter default setting * Qualify the channel * * @param priv A pointer to wlan_private structure * @param puserscanin NULL or pointer to scan configuration parameters * @param ppchantlvout Output parameter: Pointer to the start of the * channel TLV portion of the output scan config * @param pscanchanlist Output parameter: Pointer to the resulting channel * list to scan * @param pmaxchanperscan Output parameter: Number of channels to scan for * each issuance of the firmware scan command * @param pfilteredscan Output parameter: Flag indicating whether or not * a BSSID or SSID filter is being sent in the * command to firmware. Used to increase the number * of channels sent in a scan command and to * disable the firmware channel scan filter. * @param pscancurrentonly Output parameter: Flag indicating whether or not * we are only scanning our current active channel * * @return resulting scan configuration */static struct wlan_scan_cmd_config *wlan_scan_setup_scan_config(wlan_private * priv, const struct wlan_ioctl_user_scan_cfg * puserscanin, struct mrvlietypes_chanlistparamset ** ppchantlvout, struct chanscanparamset * pscanchanlist, int *pmaxchanperscan, u8 * pfilteredscan, u8 * pscancurrentonly){ struct mrvlietypes_numprobes *pnumprobestlv; struct mrvlietypes_ssidparamset *pssidtlv; struct wlan_scan_cmd_config * pscancfgout = NULL; u8 *ptlvpos; u16 numprobes; int chanidx; int scantype; int scandur; int channel; int radiotype; lbs_deb_enter(LBS_DEB_SCAN); pscancfgout = kzalloc(MAX_SCAN_CFG_ALLOC, GFP_KERNEL); if (pscancfgout == NULL) goto out; /* The tlvbufferlen is calculated for each scan command. The TLVs added * in this routine will be preserved since the routine that sends * the command will append channelTLVs at *ppchantlvout. The difference * between the *ppchantlvout and the tlvbuffer start will be used * to calculate the size of anything we add in this routine. */ pscancfgout->tlvbufferlen = 0; /* Running tlv pointer. Assigned to ppchantlvout at end of function * so later routines know where channels can be added to the command buf */ ptlvpos = pscancfgout->tlvbuffer; /* * Set the initial scan paramters for progressive scanning. If a specific * BSSID or SSID is used, the number of channels in the scan command * will be increased to the absolute maximum */ *pmaxchanperscan = MRVDRV_CHANNELS_PER_SCAN_CMD; /* Initialize the scan as un-filtered by firmware, set to TRUE below if * a SSID or BSSID filter is sent in the command */ *pfilteredscan = 0; /* Initialize the scan as not being only on the current channel. If * the channel list is customized, only contains one channel, and * is the active channel, this is set true and data flow is not halted. */ *pscancurrentonly = 0; if (puserscanin) { /* Set the bss type scan filter, use adapter setting if unset */ pscancfgout->bsstype = puserscanin->bsstype ? puserscanin->bsstype : CMD_BSS_TYPE_ANY; /* Set the number of probes to send, use adapter setting if unset */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -