diff options
| author | Rui Paulo <rpaulo@FreeBSD.org> | 2010-10-29 08:01:21 +0000 |
|---|---|---|
| committer | Rui Paulo <rpaulo@FreeBSD.org> | 2010-10-29 08:01:21 +0000 |
| commit | 02b5aa59ffe0c7ce093caa72e935418a1b0ae370 (patch) | |
| tree | 0b2308ac0b6239b9bd8d56cb4a658ca35f20a06b /src/drivers | |
| parent | 95ea342968b919b418b028d9360eedf685a55220 (diff) | |
Notes
Diffstat (limited to 'src/drivers')
41 files changed, 17215 insertions, 4982 deletions
diff --git a/src/drivers/.gitignore b/src/drivers/.gitignore new file mode 100644 index 0000000000000..1d9e0e661afee --- /dev/null +++ b/src/drivers/.gitignore @@ -0,0 +1,2 @@ +build.wpa_supplicant +build.hostapd diff --git a/src/drivers/Makefile b/src/drivers/Makefile index cffba620da04f..07600e52c2fde 100644 --- a/src/drivers/Makefile +++ b/src/drivers/Makefile @@ -2,8 +2,8 @@ all: @echo Nothing to be made. clean: - for d in $(SUBDIRS); do make -C $$d clean; done rm -f *~ *.o *.d + rm -f build.wpa_supplicant build.hostapd install: @echo Nothing to be made. diff --git a/src/drivers/driver.h b/src/drivers/driver.h index c2975d2c1215f..fa49da454e83a 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -1,6 +1,6 @@ /* - * WPA Supplicant - driver interface definition - * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> + * Driver interface definition + * Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -10,79 +10,117 @@ * license. * * See README and COPYING for more details. + * + * This file defines a driver interface used by both %wpa_supplicant and + * hostapd. The first part of the file defines data structures used in various + * driver operations. This is followed by the struct wpa_driver_ops that each + * driver wrapper will beed to define with callback functions for requesting + * driver operations. After this, there are definitions for driver event + * reporting with wpa_supplicant_event() and some convenience helper functions + * that can be used to report events. */ #ifndef DRIVER_H #define DRIVER_H -#define WPA_SUPPLICANT_DRIVER_VERSION 3 +#define WPA_SUPPLICANT_DRIVER_VERSION 4 + +#include "common/defs.h" + +#define HOSTAPD_CHAN_DISABLED 0x00000001 +#define HOSTAPD_CHAN_PASSIVE_SCAN 0x00000002 +#define HOSTAPD_CHAN_NO_IBSS 0x00000004 +#define HOSTAPD_CHAN_RADAR 0x00000008 + +/** + * struct hostapd_channel_data - Channel information + */ +struct hostapd_channel_data { + /** + * chan - Channel number (IEEE 802.11) + */ + short chan; + + /** + * freq - Frequency in MHz + */ + short freq; + + /** + * flag - Channel flags (HOSTAPD_CHAN_*) + */ + int flag; + + /** + * max_tx_power - maximum transmit power in dBm + */ + u8 max_tx_power; +}; + +/** + * struct hostapd_hw_modes - Supported hardware mode information + */ +struct hostapd_hw_modes { + /** + * mode - Hardware mode + */ + enum hostapd_hw_mode mode; -#include "defs.h" + /** + * num_channels - Number of entries in the channels array + */ + int num_channels; + + /** + * channels - Array of supported channels + */ + struct hostapd_channel_data *channels; + + /** + * num_rates - Number of entries in the rates array + */ + int num_rates; + + /** + * rates - Array of supported rates in 100 kbps units + */ + int *rates; + + /** + * ht_capab - HT (IEEE 802.11n) capabilities + */ + u16 ht_capab; + + /** + * mcs_set - MCS (IEEE 802.11n) rate parameters + */ + u8 mcs_set[16]; + + /** + * a_mpdu_params - A-MPDU (IEEE 802.11n) parameters + */ + u8 a_mpdu_params; +}; -#define AUTH_ALG_OPEN_SYSTEM 0x01 -#define AUTH_ALG_SHARED_KEY 0x02 -#define AUTH_ALG_LEAP 0x04 #define IEEE80211_MODE_INFRA 0 #define IEEE80211_MODE_IBSS 1 +#define IEEE80211_MODE_AP 2 #define IEEE80211_CAP_ESS 0x0001 #define IEEE80211_CAP_IBSS 0x0002 #define IEEE80211_CAP_PRIVACY 0x0010 -#define SSID_MAX_WPA_IE_LEN 40 -/** - * struct wpa_scan_result - Scan results (old structure) - * @bssid: BSSID - * @ssid: SSID - * @ssid_len: length of the ssid - * @wpa_ie: WPA IE - * @wpa_ie_len: length of the wpa_ie - * @rsn_ie: RSN IE - * @rsn_ie_len: length of the RSN IE - * @freq: frequency of the channel in MHz (e.g., 2412 = channel 1) - * @caps: capability information field in host byte order - * @qual: signal quality - * @noise: noise level - * @level: signal level - * @maxrate: maximum supported rate - * @mdie_present: Whether MDIE was included in Beacon/ProbeRsp frame - * @mdie: Mobility domain identifier IE (IEEE 802.11r MDIE) (starting from - * IE type field) - * @tsf: Timestamp - * - * This structure is used as a generic format for scan results from the - * driver. Each driver interface implementation is responsible for converting - * the driver or OS specific scan results into this format. - * - * This structure is the old data structure used for scan results. It is - * obsoleted by the new struct wpa_scan_res structure and the old version is - * only included for backwards compatibility with existing driver wrapper - * implementations. New implementations are encouraged to implement for struct - * wpa_scan_res. The old structure will be removed at some point. - */ -struct wpa_scan_result { - u8 bssid[ETH_ALEN]; - u8 ssid[32]; - size_t ssid_len; - u8 wpa_ie[SSID_MAX_WPA_IE_LEN]; - size_t wpa_ie_len; - u8 rsn_ie[SSID_MAX_WPA_IE_LEN]; - size_t rsn_ie_len; - int freq; - u16 caps; - int qual; - int noise; - int level; - int maxrate; - int mdie_present; - u8 mdie[5]; - u64 tsf; -}; - +#define WPA_SCAN_QUAL_INVALID BIT(0) +#define WPA_SCAN_NOISE_INVALID BIT(1) +#define WPA_SCAN_LEVEL_INVALID BIT(2) +#define WPA_SCAN_LEVEL_DBM BIT(3) +#define WPA_SCAN_AUTHENTICATED BIT(4) +#define WPA_SCAN_ASSOCIATED BIT(5) /** * struct wpa_scan_res - Scan result for an BSS/IBSS + * @flags: information flags about the BSS/IBSS (WPA_SCAN_*) * @bssid: BSSID * @freq: frequency of the channel in MHz (e.g., 2412 = channel 1) * @beacon_int: beacon interval in TUs (host byte order) @@ -91,7 +129,10 @@ struct wpa_scan_result { * @noise: noise level * @level: signal level * @tsf: Timestamp + * @age: Age of the information in milliseconds (i.e., how many milliseconds + * ago the last Beacon or Probe Response frame was received) * @ie_len: length of the following IE field in octets + * @beacon_ie_len: length of the following Beacon IE field in octets * * This structure is used as a generic format for scan results from the * driver. Each driver interface implementation is responsible for converting @@ -103,6 +144,7 @@ struct wpa_scan_result { * report all IEs to make it easier to support future additions. */ struct wpa_scan_res { + unsigned int flags; u8 bssid[ETH_ALEN]; int freq; u16 beacon_int; @@ -111,8 +153,16 @@ struct wpa_scan_res { int noise; int level; u64 tsf; + unsigned int age; size_t ie_len; - /* followed by ie_len octets of IEs */ + size_t beacon_ie_len; + /* + * Followed by ie_len octets of IEs from Probe Response frame (or if + * the driver does not indicate source of IEs, these may also be from + * Beacon frame). After the first set of IEs, another set of IEs may + * follow (with beacon_ie_len octets of data) if the driver provides + * both IE sets. + */ }; /** @@ -142,6 +192,95 @@ struct wpa_interface_info { const char *drv_name; }; +#define WPAS_MAX_SCAN_SSIDS 4 + +/** + * struct wpa_driver_scan_params - Scan parameters + * Data for struct wpa_driver_ops::scan2(). + */ +struct wpa_driver_scan_params { + /** + * ssids - SSIDs to scan for + */ + struct wpa_driver_scan_ssid { + /** + * ssid - specific SSID to scan for (ProbeReq) + * %NULL or zero-length SSID is used to indicate active scan + * with wildcard SSID. + */ + const u8 *ssid; + /** + * ssid_len: Length of the SSID in octets + */ + size_t ssid_len; + } ssids[WPAS_MAX_SCAN_SSIDS]; + + /** + * num_ssids - Number of entries in ssids array + * Zero indicates a request for a passive scan. + */ + size_t num_ssids; + + /** + * extra_ies - Extra IE(s) to add into Probe Request or %NULL + */ + const u8 *extra_ies; + + /** + * extra_ies_len - Length of extra_ies in octets + */ + size_t extra_ies_len; + + /** + * freqs - Array of frequencies to scan or %NULL for all frequencies + * + * The frequency is set in MHz. The array is zero-terminated. + */ + int *freqs; + + /** + * filter_ssids - Filter for reporting SSIDs + * + * This optional parameter can be used to request the driver wrapper to + * filter scan results to include only the specified SSIDs. %NULL + * indicates that no filtering is to be done. This can be used to + * reduce memory needs for scan results in environments that have large + * number of APs with different SSIDs. + * + * The driver wrapper is allowed to take this allocated buffer into its + * own use by setting the pointer to %NULL. In that case, the driver + * wrapper is responsible for freeing the buffer with os_free() once it + * is not needed anymore. + */ + struct wpa_driver_scan_filter { + u8 ssid[32]; + size_t ssid_len; + } *filter_ssids; + + /** + * num_filter_ssids - Number of entries in filter_ssids array + */ + size_t num_filter_ssids; +}; + +/** + * struct wpa_driver_auth_params - Authentication parameters + * Data for struct wpa_driver_ops::authenticate(). + */ +struct wpa_driver_auth_params { + int freq; + const u8 *bssid; + const u8 *ssid; + size_t ssid_len; + int auth_alg; + const u8 *ie; + size_t ie_len; + const u8 *wep_key[4]; + size_t wep_key_len[4]; + int wep_tx_keyidx; + int local_state_change; +}; + /** * struct wpa_driver_associate_params - Association parameters * Data for struct wpa_driver_ops::associate(). @@ -157,6 +296,10 @@ struct wpa_driver_associate_params { * ssid - The selected SSID */ const u8 *ssid; + + /** + * ssid_len - Length of the SSID (1..32) + */ size_t ssid_len; /** @@ -185,20 +328,36 @@ struct wpa_driver_associate_params { * When using WPS, wpa_ie is used for WPS IE instead of WPA/RSN IE. */ const u8 *wpa_ie; + /** * wpa_ie_len - length of the wpa_ie */ size_t wpa_ie_len; - /* The selected pairwise/group cipher and key management - * suites. These are usually ignored if @wpa_ie is used. */ - wpa_cipher pairwise_suite; - wpa_cipher group_suite; - wpa_key_mgmt key_mgmt_suite; + /** + * pairwise_suite - Selected pairwise cipher suite + * + * This is usually ignored if @wpa_ie is used. + */ + enum wpa_cipher pairwise_suite; + + /** + * group_suite - Selected group cipher suite + * + * This is usually ignored if @wpa_ie is used. + */ + enum wpa_cipher group_suite; + + /** + * key_mgmt_suite - Selected key management suite + * + * This is usually ignored if @wpa_ie is used. + */ + enum wpa_key_mgmt key_mgmt_suite; /** * auth_alg - Allowed authentication algorithms - * Bit field of AUTH_ALG_* + * Bit field of WPA_AUTH_ALG_* */ int auth_alg; @@ -225,11 +384,7 @@ struct wpa_driver_associate_params { /** * mgmt_frame_protection - IEEE 802.11w management frame protection */ - enum { - NO_MGMT_FRAME_PROTECTION, - MGMT_FRAME_PROTECTION_OPTIONAL, - MGMT_FRAME_PROTECTION_REQUIRED - } mgmt_frame_protection; + enum mfp_options mgmt_frame_protection; /** * ft_ies - IEEE 802.11r / FT information elements @@ -288,6 +443,23 @@ struct wpa_driver_associate_params { * be prepared to handle %NULL value as an error. */ const u8 *psk; + + /** + * drop_unencrypted - Enable/disable unencrypted frame filtering + * + * Configure the driver to drop all non-EAPOL frames (both receive and + * transmit paths). Unencrypted EAPOL frames (ethertype 0x888e) must + * still be allowed for key negotiation. + */ + int drop_unencrypted; + + /** + * prev_bssid - Previously used BSSID in this ESS + * + * When not %NULL, this is a request to use reassociation instead of + * association. + */ + const u8 *prev_bssid; }; /** @@ -316,59 +488,119 @@ struct wpa_driver_capa { /* Driver generated WPA/RSN IE */ #define WPA_DRIVER_FLAGS_DRIVER_IE 0x00000001 +/* Driver needs static WEP key setup after association command */ #define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC 0x00000002 #define WPA_DRIVER_FLAGS_USER_SPACE_MLME 0x00000004 /* Driver takes care of RSN 4-way handshake internally; PMK is configured with * struct wpa_driver_ops::set_key using alg = WPA_ALG_PMK */ #define WPA_DRIVER_FLAGS_4WAY_HANDSHAKE 0x00000008 +#define WPA_DRIVER_FLAGS_WIRED 0x00000010 +/* Driver provides separate commands for authentication and association (SME in + * wpa_supplicant). */ +#define WPA_DRIVER_FLAGS_SME 0x00000020 +/* Driver supports AP mode */ +#define WPA_DRIVER_FLAGS_AP 0x00000040 +/* Driver needs static WEP key setup after association has been completed */ +#define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE 0x00000080 unsigned int flags; + + int max_scan_ssids; + + /** + * max_remain_on_chan - Maximum remain-on-channel duration in msec + */ + unsigned int max_remain_on_chan; }; -#define WPA_CHAN_W_SCAN 0x00000001 -#define WPA_CHAN_W_ACTIVE_SCAN 0x00000002 -#define WPA_CHAN_W_IBSS 0x00000004 +struct hostapd_data; -struct wpa_channel_data { - short chan; /* channel number (IEEE 802.11) */ - short freq; /* frequency in MHz */ - int flag; /* flag for user space use (WPA_CHAN_*) */ +struct hostap_sta_driver_data { + unsigned long rx_packets, tx_packets, rx_bytes, tx_bytes; + unsigned long current_tx_rate; + unsigned long inactive_msec; + unsigned long flags; + unsigned long num_ps_buf_frames; + unsigned long tx_retry_failed; + unsigned long tx_retry_count; + int last_rssi; + int last_ack_rssi; }; -#define WPA_RATE_ERP 0x00000001 -#define WPA_RATE_BASIC 0x00000002 -#define WPA_RATE_PREAMBLE2 0x00000004 -#define WPA_RATE_SUPPORTED 0x00000010 -#define WPA_RATE_OFDM 0x00000020 -#define WPA_RATE_CCK 0x00000040 -#define WPA_RATE_MANDATORY 0x00000100 +struct hostapd_sta_add_params { + const u8 *addr; + u16 aid; + u16 capability; + const u8 *supp_rates; + size_t supp_rates_len; + u16 listen_interval; + const struct ieee80211_ht_capabilities *ht_capabilities; +}; -struct wpa_rate_data { - int rate; /* rate in 100 kbps */ - int flags; /* WPA_RATE_ flags */ +struct hostapd_freq_params { + int mode; + int freq; + int channel; + int ht_enabled; + int sec_channel_offset; /* 0 = HT40 disabled, -1 = HT40 enabled, + * secondary channel below primary, 1 = HT40 + * enabled, secondary channel above primary */ }; -typedef enum { - WPA_MODE_IEEE80211B, - WPA_MODE_IEEE80211G, - WPA_MODE_IEEE80211A, - NUM_WPA_MODES -} wpa_hw_mode; +enum wpa_driver_if_type { + /** + * WPA_IF_STATION - Station mode interface + */ + WPA_IF_STATION, -struct wpa_hw_modes { - wpa_hw_mode mode; - int num_channels; - struct wpa_channel_data *channels; - int num_rates; - struct wpa_rate_data *rates; + /** + * WPA_IF_AP_VLAN - AP mode VLAN interface + * + * This interface shares its address and Beacon frame with the main + * BSS. + */ + WPA_IF_AP_VLAN, + + /** + * WPA_IF_AP_BSS - AP mode BSS interface + * + * This interface has its own address and Beacon frame. + */ + WPA_IF_AP_BSS, +}; + +struct wpa_init_params { + const u8 *bssid; + const char *ifname; + const u8 *ssid; + size_t ssid_len; + const char *test_socket; + int use_pae_group_addr; + char **bridge; + size_t num_bridge; + + u8 *own_addr; /* buffer for writing own MAC address */ }; -struct ieee80211_rx_status { - int channel; - int ssi; +struct wpa_bss_params { + /** Interface name (for multi-SSID/VLAN support) */ + const char *ifname; + /** Whether IEEE 802.1X or WPA/WPA2 is enabled */ + int enabled; + + int wpa; + int ieee802_1x; + int wpa_group; + int wpa_pairwise; + int wpa_key_mgmt; + int rsn_preauth; }; +#define WPA_STA_AUTHORIZED BIT(0) +#define WPA_STA_WMM BIT(1) +#define WPA_STA_SHORT_PREAMBLE BIT(2) +#define WPA_STA_MFP BIT(3) /** * struct wpa_driver_ops - Driver interface API definition @@ -414,31 +646,8 @@ struct wpa_driver_ops { int (*get_ssid)(void *priv, u8 *ssid); /** - * set_wpa - Enable/disable WPA support (OBSOLETE) - * @priv: private driver interface data - * @enabled: 1 = enable, 0 = disable - * - * Returns: 0 on success, -1 on failure - * - * Note: This function is included for backwards compatibility. This is - * called only just after init and just before deinit, so these - * functions can be used to implement same functionality and the driver - * interface need not define this function. - * - * Configure the kernel driver to enable/disable WPA support. This may - * be empty function, if WPA support is always enabled. Common - * configuration items are WPA IE (clearing it when WPA support is - * disabled), Privacy flag configuration for capability field (note: - * this the value need to set in associate handler to allow plaintext - * mode to be used) when trying to associate with, roaming mode (can - * allow wpa_supplicant to control roaming if ap_scan=1 is used; - * however, drivers can also implement roaming if desired, especially - * ap_scan=2 mode is used for this). - */ - int (*set_wpa)(void *priv, int enabled); - - /** * set_key - Configure encryption key + * @ifname: Interface name (for multi-SSID/VLAN support) * @priv: private driver interface data * @alg: encryption algorithm (%WPA_ALG_NONE, %WPA_ALG_WEP, * %WPA_ALG_TKIP, %WPA_ALG_CCMP, %WPA_ALG_IGTK, %WPA_ALG_PMK); @@ -480,8 +689,9 @@ struct wpa_driver_ops { * in driver_*.c set_key() implementation, see driver_ndis.c for an * example on how this can be done. */ - int (*set_key)(void *priv, wpa_alg alg, const u8 *addr, - int key_idx, int set_tx, const u8 *seq, size_t seq_len, + int (*set_key)(const char *ifname, void *priv, enum wpa_alg alg, + const u8 *addr, int key_idx, int set_tx, + const u8 *seq, size_t seq_len, const u8 *key, size_t key_len); /** @@ -543,55 +753,6 @@ struct wpa_driver_ops { int (*set_countermeasures)(void *priv, int enabled); /** - * set_drop_unencrypted - Enable/disable unencrypted frame filtering - * @priv: private driver interface data - * @enabled: 1 = unencrypted Tx/Rx frames will be dropped, 0 = disabled - * - * Returns: 0 on success, -1 on failure - * - * Configure the driver to drop all non-EAPOL frames (both receive and - * transmit paths). Unencrypted EAPOL frames (ethertype 0x888e) must - * still be allowed for key negotiation. - */ - int (*set_drop_unencrypted)(void *priv, int enabled); - - /** - * scan - Request the driver to initiate scan - * @priv: private driver interface data - * @ssid: specific SSID to scan for (ProbeReq) or %NULL to scan for - * all SSIDs (either active scan with broadcast SSID or passive - * scan - * @ssid_len: length of the SSID - * - * Returns: 0 on success, -1 on failure - * - * Once the scan results are ready, the driver should report scan - * results event for wpa_supplicant which will eventually request the - * results with wpa_driver_get_scan_results(). - */ - int (*scan)(void *priv, const u8 *ssid, size_t ssid_len); - - /** - * get_scan_results - Fetch the latest scan results (old version) - * @priv: private driver interface data - * @results: pointer to buffer for scan results - * @max_size: maximum number of entries (buffer size) - * - * Returns: Number of scan result entries used on success, -1 on - * failure - * - * If scan results include more than max_size BSSes, max_size will be - * returned and the remaining entries will not be included in the - * buffer. - * - * This function is depracated. New driver wrapper implementations - * should implement support for get_scan_results2(). - */ - int (*get_scan_results)(void *priv, - struct wpa_scan_result *results, - size_t max_size); - - /** * deauthenticate - Request driver to deauthenticate * @priv: private driver interface data * @addr: peer address (BSSID of the AP) @@ -624,27 +785,6 @@ struct wpa_driver_ops { struct wpa_driver_associate_params *params); /** - * set_auth_alg - Set IEEE 802.11 authentication algorithm - * @priv: private driver interface data - * @auth_alg: bit field of AUTH_ALG_* - * - * If the driver supports more than one authentication algorithm at the - * same time, it should configure all supported algorithms. If not, one - * algorithm needs to be selected arbitrarily. Open System - * authentication should be ok for most cases and it is recommended to - * be used if other options are not supported. Static WEP configuration - * may also use Shared Key authentication and LEAP requires its own - * algorithm number. For LEAP, user can make sure that only one - * algorithm is used at a time by configuring LEAP as the only - * supported EAP method. This information is also available in - * associate() params, so set_auth_alg may not be needed in case of - * most drivers. - * - * Returns: 0 on success, -1 on failure - */ - int (*set_auth_alg)(void *priv, int auth_alg); - - /** * add_pmkid - Add PMKSA cache entry to the driver * @priv: private driver interface data * @bssid: BSSID for the PMKSA cache entry @@ -764,9 +904,9 @@ struct wpa_driver_ops { * with driver specific functionality. If this function pointer is set, * l2_packet module is not used at all and the driver interface code is * responsible for receiving and sending all EAPOL packets. The - * received EAPOL packets are sent to core code by calling - * wpa_supplicant_rx_eapol(). The driver interface is required to - * implement get_mac_addr() handler if send_eapol() is used. + * received EAPOL packets are sent to core code with EVENT_EAPOL_RX + * event. The driver interface is required to implement get_mac_addr() + * handler if send_eapol() is used. */ int (*send_eapol)(void *priv, const u8 *dest, u16 proto, const u8 *data, size_t data_len); @@ -813,16 +953,16 @@ struct wpa_driver_ops { * failure. Caller is responsible for freeing this. * * This function is only needed for drivers that export MLME - * (management frame processing) to wpa_supplicant. + * (management frame processing) to %wpa_supplicant or hostapd. */ - struct wpa_hw_modes * (*get_hw_feature_data)(void *priv, - u16 *num_modes, - u16 *flags); + struct hostapd_hw_modes * (*get_hw_feature_data)(void *priv, + u16 *num_modes, + u16 *flags); /** * set_channel - Set channel * @priv: Private driver interface data - * @phymode: WPA_MODE_IEEE80211B, .. + * @phymode: HOSTAPD_MODE_IEEE80211B, .. * @chan: IEEE 802.11 channel number * @freq: Frequency of the channel in MHz * Returns: 0 on success, -1 on failure @@ -830,7 +970,7 @@ struct wpa_driver_ops { * This function is only needed for drivers that export MLME * (management frame processing) to wpa_supplicant. */ - int (*set_channel)(void *priv, wpa_hw_mode phymode, int chan, + int (*set_channel)(void *priv, enum hostapd_hw_mode phymode, int chan, int freq); /** @@ -941,29 +1081,6 @@ struct wpa_driver_ops { struct wpa_scan_results * (*get_scan_results2)(void *priv); /** - * set_probe_req_ie - Set information element(s) for Probe Request - * @priv: private driver interface data - * @ies: Information elements to append or %NULL to remove extra IEs - * @ies_len: Length of the IE buffer in octets - * Returns: 0 on success, -1 on failure - */ - int (*set_probe_req_ie)(void *priv, const u8 *ies, size_t ies_len); - - /** - * set_mode - Request driver to set the operating mode - * @priv: private driver interface data - * @mode: Operation mode (infra/ibss) IEEE80211_MODE_* - * - * This handler will be called before any key configuration and call to - * associate() handler in order to allow the operation mode to be - * configured as early as possible. This information is also available - * in associate() params and as such, some driver wrappers may not need - * to implement set_mode() handler. - * Returns: 0 on success, -1 on failure - */ - int (*set_mode)(void *priv, int mode); - - /** * set_country - Set country * @priv: Private driver interface data * @alpha2: country to which to switch to @@ -1017,19 +1134,644 @@ struct wpa_driver_ops { * failure */ struct wpa_interface_info * (*get_interfaces)(void *global_priv); + + /** + * scan2 - Request the driver to initiate scan + * @priv: private driver interface data + * @params: Scan parameters + * + * Returns: 0 on success, -1 on failure + * + * Once the scan results are ready, the driver should report scan + * results event for wpa_supplicant which will eventually request the + * results with wpa_driver_get_scan_results2(). + */ + int (*scan2)(void *priv, struct wpa_driver_scan_params *params); + + /** + * authenticate - Request driver to authenticate + * @priv: private driver interface data + * @params: authentication parameters + * Returns: 0 on success, -1 on failure + * + * This is an optional function that can be used with drivers that + * support separate authentication and association steps, i.e., when + * wpa_supplicant can act as the SME. If not implemented, associate() + * function is expected to take care of IEEE 802.11 authentication, + * too. + */ + int (*authenticate)(void *priv, + struct wpa_driver_auth_params *params); + + /** + * set_beacon - Set Beacon frame template + * @priv: Private driver interface data + * @head: Beacon head from IEEE 802.11 header to IEs before TIM IE + * @head_len: Length of the head buffer in octets + * @tail: Beacon tail following TIM IE + * @tail_len: Length of the tail buffer in octets + * @dtim_period: DTIM period + * @beacon_int: Beacon interval + * Returns: 0 on success, -1 on failure + * + * This function is used to configure Beacon template for the driver in + * AP mode. The driver is responsible for building the full Beacon + * frame by concatenating the head part with TIM IE generated by the + * driver/firmware and finishing with the tail part. + */ + int (*set_beacon)(void *priv, const u8 *head, size_t head_len, + const u8 *tail, size_t tail_len, int dtim_period, + int beacon_int); + + /** + * hapd_init - Initialize driver interface (hostapd only) + * @hapd: Pointer to hostapd context + * @params: Configuration for the driver wrapper + * Returns: Pointer to private data, %NULL on failure + * + * This function is used instead of init() or init2() when the driver + * wrapper is used withh hostapd. + */ + void * (*hapd_init)(struct hostapd_data *hapd, + struct wpa_init_params *params); + + /** + * hapd_deinit - Deinitialize driver interface (hostapd only) + * @priv: Private driver interface data from hapd_init() + */ + void (*hapd_deinit)(void *priv); + + /** + * set_ieee8021x - Enable/disable IEEE 802.1X support (AP only) + * @priv: Private driver interface data + * @params: BSS parameters + * Returns: 0 on success, -1 on failure + * + * This is an optional function to configure the kernel driver to + * enable/disable IEEE 802.1X support and set WPA/WPA2 parameters. This + * can be left undefined (set to %NULL) if IEEE 802.1X support is + * always enabled and the driver uses set_beacon() to set WPA/RSN IE + * for Beacon frames. + */ + int (*set_ieee8021x)(void *priv, struct wpa_bss_params *params); + + /** + * set_privacy - Enable/disable privacy (AP only) + * @priv: Private driver interface data + * @enabled: 1 = privacy enabled, 0 = disabled + * Returns: 0 on success, -1 on failure + * + * This is an optional function to configure privacy field in the + * kernel driver for Beacon frames. This can be left undefined (set to + * %NULL) if the driver uses the Beacon template from set_beacon(). + */ + int (*set_privacy)(void *priv, int enabled); + + /** + * get_seqnum - Fetch the current TSC/packet number (AP only) + * @ifname: The interface name (main or virtual) + * @priv: Private driver interface data + * @addr: MAC address of the station or %NULL for group keys + * @idx: Key index + * @seq: Buffer for returning the latest used TSC/packet number + * Returns: 0 on success, -1 on failure + * + * This function is used to fetch the last used TSC/packet number for + * a TKIP, CCMP, or BIP/IGTK key. It is mainly used with group keys, so + * there is no strict requirement on implementing support for unicast + * keys (i.e., addr != %NULL). + */ + int (*get_seqnum)(const char *ifname, void *priv, const u8 *addr, + int idx, u8 *seq); + + /** + * flush - Flush all association stations (AP only) + * @priv: Private driver interface data + * Returns: 0 on success, -1 on failure + * + * This function requests the driver to disassociate all associated + * stations. This function does not need to be implemented if the + * driver does not process association frames internally. + */ + int (*flush)(void *priv); + + /** + * set_generic_elem - Add IEs into Beacon/Probe Response frames (AP) + * @priv: Private driver interface data + * @elem: Information elements + * @elem_len: Length of the elem buffer in octets + * Returns: 0 on success, -1 on failure + * + * This is an optional function to add information elements in the + * kernel driver for Beacon and Probe Response frames. This can be left + * undefined (set to %NULL) if the driver uses the Beacon template from + * set_beacon(). + */ + int (*set_generic_elem)(void *priv, const u8 *elem, size_t elem_len); + + /** + * read_sta_data - Fetch station data (AP only) + * @priv: Private driver interface data + * @data: Buffer for returning station information + * @addr: MAC address of the station + * Returns: 0 on success, -1 on failure + */ + int (*read_sta_data)(void *priv, struct hostap_sta_driver_data *data, + const u8 *addr); + + /** + * hapd_send_eapol - Send an EAPOL packet (AP only) + * @priv: private driver interface data + * @addr: Destination MAC address + * @data: EAPOL packet starting with IEEE 802.1X header + * @data_len: Length of the EAPOL packet in octets + * @encrypt: Whether the frame should be encrypted + * @own_addr: Source MAC address + * + * Returns: 0 on success, -1 on failure + */ + int (*hapd_send_eapol)(void *priv, const u8 *addr, const u8 *data, + size_t data_len, int encrypt, + const u8 *own_addr); + + /** + * sta_deauth - Deauthenticate a station (AP only) + * @priv: Private driver interface data + * @own_addr: Source address and BSSID for the Deauthentication frame + * @addr: MAC address of the station to deauthenticate + * @reason: Reason code for the Deauthentiation frame + * Returns: 0 on success, -1 on failure + * + * This function requests a specific station to be deauthenticated and + * a Deauthentication frame to be sent to it. + */ + int (*sta_deauth)(void *priv, const u8 *own_addr, const u8 *addr, + int reason); + + /** + * sta_disassoc - Disassociate a station (AP only) + * @priv: Private driver interface data + * @own_addr: Source address and BSSID for the Disassociation frame + * @addr: MAC address of the station to disassociate + * @reason: Reason code for the Disassociation frame + * Returns: 0 on success, -1 on failure + * + * This function requests a specific station to be disassociated and + * a Disassociation frame to be sent to it. + */ + int (*sta_disassoc)(void *priv, const u8 *own_addr, const u8 *addr, + int reason); + + /** + * sta_remove - Remove a station entry (AP only) + * @priv: Private driver interface data + * @addr: MAC address of the station to be removed + * Returns: 0 on success, -1 on failure + */ + int (*sta_remove)(void *priv, const u8 *addr); + + /** + * hapd_get_ssid - Get the current SSID (AP only) + * @priv: Private driver interface data + * @buf: Buffer for returning the SSID + * @len: Maximum length of the buffer + * Returns: Length of the SSID on success, -1 on failure + * + * This function need not be implemented if the driver uses Beacon + * template from set_beacon() and does not reply to Probe Request + * frames. + */ + int (*hapd_get_ssid)(void *priv, u8 *buf, int len); + + /** + * hapd_set_ssid - Set SSID (AP only) + * @priv: Private driver interface data + * @buf: SSID + * @len: Length of the SSID in octets + * Returns: 0 on success, -1 on failure + */ + int (*hapd_set_ssid)(void *priv, const u8 *buf, int len); + + /** + * hapd_set_countermeasures - Enable/disable TKIP countermeasures (AP) + * @priv: Private driver interface data + * @enabled: 1 = countermeasures enabled, 0 = disabled + * Returns: 0 on success, -1 on failure + * + * This need not be implemented if the driver does not take care of + * association processing. + */ + int (*hapd_set_countermeasures)(void *priv, int enabled); + + /** + * sta_add - Add a station entry + * @priv: Private driver interface data + * @params: Station parameters + * Returns: 0 on success, -1 on failure + * + * This function is used to add a station entry to the driver once the + * station has completed association. This is only used if the driver + * does not take care of association processing. + */ + int (*sta_add)(void *priv, struct hostapd_sta_add_params *params); + + /** + * get_inact_sec - Get station inactivity duration (AP only) + * @priv: Private driver interface data + * @addr: Station address + * Returns: Number of seconds station has been inactive, -1 on failure + */ + int (*get_inact_sec)(void *priv, const u8 *addr); + + /** + * sta_clear_stats - Clear station statistics (AP only) + * @priv: Private driver interface data + * @addr: Station address + * Returns: 0 on success, -1 on failure + */ + int (*sta_clear_stats)(void *priv, const u8 *addr); + + /** + * set_freq - Set channel/frequency (AP only) + * @priv: Private driver interface data + * @freq: Channel parameters + * Returns: 0 on success, -1 on failure + */ + int (*set_freq)(void *priv, struct hostapd_freq_params *freq); + + /** + * set_rts - Set RTS threshold + * @priv: Private driver interface data + * @rts: RTS threshold in octets + * Returns: 0 on success, -1 on failure + */ + int (*set_rts)(void *priv, int rts); + + /** + * set_frag - Set fragmentation threshold + * @priv: Private driver interface data + * @frag: Fragmentation threshold in octets + * Returns: 0 on success, -1 on failure + */ + int (*set_frag)(void *priv, int frag); + + /** + * sta_set_flags - Set station flags (AP only) + * @priv: Private driver interface data + * @addr: Station address + * @total_flags: Bitmap of all WPA_STA_* flags currently set + * @flags_or: Bitmap of WPA_STA_* flags to add + * @flags_and: Bitmap of WPA_STA_* flags to us as a mask + * Returns: 0 on success, -1 on failure + */ + int (*sta_set_flags)(void *priv, const u8 *addr, + int total_flags, int flags_or, int flags_and); + + /** + * set_rate_sets - Set supported and basic rate sets (AP only) + * @priv: Private driver interface data + * @supp_rates: -1 terminated array of supported rates in 100 kbps + * @basic_rates: -1 terminated array of basic rates in 100 kbps + * @mode: hardware mode (HOSTAPD_MODE_*) + * Returns: 0 on success, -1 on failure + */ + int (*set_rate_sets)(void *priv, int *supp_rates, int *basic_rates, + int mode); + + /** + * set_cts_protect - Set CTS protection mode (AP only) + * @priv: Private driver interface data + * @value: Whether CTS protection is enabled + * Returns: 0 on success, -1 on failure + */ + int (*set_cts_protect)(void *priv, int value); + + /** + * set_preamble - Set preamble mode (AP only) + * @priv: Private driver interface data + * @value: Whether short preamble is enabled + * Returns: 0 on success, -1 on failure + */ + int (*set_preamble)(void *priv, int value); + + /** + * set_short_slot_time - Set short slot time (AP only) + * @priv: Private driver interface data + * @value: Whether short slot time is enabled + * Returns: 0 on success, -1 on failure + */ + int (*set_short_slot_time)(void *priv, int value); + + /** + * set_tx_queue_params - Set TX queue parameters + * @priv: Private driver interface data + * @queue: Queue number + * @aifs: AIFS + * @cw_min: cwMin + * @cw_max: cwMax + * @burst_time: Maximum length for bursting in 0.1 msec units + */ + int (*set_tx_queue_params)(void *priv, int queue, int aifs, int cw_min, + int cw_max, int burst_time); + + /** + * valid_bss_mask - Validate BSSID mask + * @priv: Private driver interface data + * @addr: Address + * @mask: Mask + * Returns: 0 if mask is valid, -1 if mask is not valid, 1 if mask can + * be used, but the main interface address must be the first address in + * the block if mask is applied + */ + int (*valid_bss_mask)(void *priv, const u8 *addr, const u8 *mask); + + /** + * if_add - Add a virtual interface + * @priv: Private driver interface data + * @type: Interface type + * @ifname: Interface name for the new virtual interface + * @addr: Local address to use for the interface or %NULL to use the + * parent interface address + * @bss_ctx: BSS context for %WPA_IF_AP_BSS interfaces + * @drv_priv: Pointer for overwriting the driver context or %NULL if + * not allowed (applies only to %WPA_IF_AP_BSS type) + * @force_ifname: Buffer for returning an interface name that the + * driver ended up using if it differs from the requested ifname + * @if_addr: Buffer for returning the allocated interface address + * (this may differ from the requested addr if the driver cannot + * change interface address) + * Returns: 0 on success, -1 on failure + */ + int (*if_add)(void *priv, enum wpa_driver_if_type type, + const char *ifname, const u8 *addr, void *bss_ctx, + void **drv_priv, char *force_ifname, u8 *if_addr); + + /** + * if_remove - Remove a virtual interface + * @priv: Private driver interface data + * @type: Interface type + * @ifname: Interface name of the virtual interface to be removed + * Returns: 0 on success, -1 on failure + */ + int (*if_remove)(void *priv, enum wpa_driver_if_type type, + const char *ifname); + + /** + * set_sta_vlan - Bind a station into a specific interface (AP only) + * @priv: Private driver interface data + * @ifname: Interface (main or virtual BSS or VLAN) + * @addr: MAC address of the associated station + * @vlan_id: VLAN ID + * Returns: 0 on success, -1 on failure + * + * This function is used to bind a station to a specific virtual + * interface. It is only used if when virtual interfaces are supported, + * e.g., to assign stations to different VLAN interfaces based on + * information from a RADIUS server. This allows separate broadcast + * domains to be used with a single BSS. + */ + int (*set_sta_vlan)(void *priv, const u8 *addr, const char *ifname, + int vlan_id); + + /** + * commit - Optional commit changes handler (AP only) + * @priv: driver private data + * Returns: 0 on success, -1 on failure + * + * This optional handler function can be registered if the driver + * interface implementation needs to commit changes (e.g., by setting + * network interface up) at the end of initial configuration. If set, + * this handler will be called after initial setup has been completed. + */ + int (*commit)(void *priv); + + /** + * send_ether - Send an ethernet packet (AP only) + * @priv: private driver interface data + * @dst: Destination MAC address + * @src: Source MAC address + * @proto: Ethertype + * @data: EAPOL packet starting with IEEE 802.1X header + * @data_len: Length of the EAPOL packet in octets + * Returns: 0 on success, -1 on failure + */ + int (*send_ether)(void *priv, const u8 *dst, const u8 *src, u16 proto, + const u8 *data, size_t data_len); + + /** + * set_radius_acl_auth - Notification of RADIUS ACL change + * @priv: Private driver interface data + * @mac: MAC address of the station + * @accepted: Whether the station was accepted + * @session_timeout: Session timeout for the station + * Returns: 0 on success, -1 on failure + */ + int (*set_radius_acl_auth)(void *priv, const u8 *mac, int accepted, + u32 session_timeout); + + /** + * set_radius_acl_expire - Notification of RADIUS ACL expiration + * @priv: Private driver interface data + * @mac: MAC address of the station + * Returns: 0 on success, -1 on failure + */ + int (*set_radius_acl_expire)(void *priv, const u8 *mac); + + /** + * set_ht_params - Set HT parameters (AP only) + * @priv: Private driver interface data + * @ht_capab: HT Capabilities IE + * @ht_capab_len: Length of ht_capab in octets + * @ht_oper: HT Operation IE + * @ht_oper_len: Length of ht_oper in octets + * Returns: 0 on success, -1 on failure + */ + int (*set_ht_params)(void *priv, + const u8 *ht_capab, size_t ht_capab_len, + const u8 *ht_oper, size_t ht_oper_len); + + /** + * set_ap_wps_ie - Add WPS IE(s) into Beacon/Probe Response frames (AP) + * @priv: Private driver interface data + * @beacon: WPS IE(s) for Beacon frames or %NULL to remove extra IE(s) + * @proberesp: WPS IE(s) for Probe Response frames or %NULL to remove + * extra IE(s) + * Returns: 0 on success, -1 on failure + * + * This is an optional function to add WPS IE in the kernel driver for + * Beacon and Probe Response frames. This can be left undefined (set + * to %NULL) if the driver uses the Beacon template from set_beacon() + * and does not process Probe Request frames. + */ + int (*set_ap_wps_ie)(void *priv, const struct wpabuf *beacon, + const struct wpabuf *proberesp); + + /** + * set_supp_port - Set IEEE 802.1X Supplicant Port status + * @priv: Private driver interface data + * @authorized: Whether the port is authorized + * Returns: 0 on success, -1 on failure + */ + int (*set_supp_port)(void *priv, int authorized); + + /** + * set_wds_sta - Bind a station into a 4-address WDS (AP only) + * @priv: Private driver interface data + * @addr: MAC address of the associated station + * @aid: Association ID + * @val: 1 = bind to 4-address WDS; 0 = unbind + * Returns: 0 on success, -1 on failure + */ + int (*set_wds_sta)(void *priv, const u8 *addr, int aid, int val); + + /** + * send_action - Transmit an Action frame + * @priv: Private driver interface data + * @freq: Frequency (in MHz) of the channel + * @dst: Destination MAC address (Address 1) + * @src: Source MAC address (Address 2) + * @bssid: BSSID (Address 3) + * @data: Frame body + * @data_len: data length in octets + * Returns: 0 on success, -1 on failure + * + * This command can be used to request the driver to transmit an action + * frame to the specified destination. If a remain-on-channel duration + * is in progress, the frame is transmitted on that channel. Otherwise, + * the frame is transmitted on the current operational channel if in + * associated state in station mode or if operating as an AP. If none + * of these conditions is in effect, send_action() cannot be used. + */ + int (*send_action)(void *priv, unsigned int freq, + const u8 *dst, const u8 *src, const u8 *bssid, + const u8 *data, size_t data_len); + + /** + * remain_on_channel - Remain awake on a channel + * @priv: Private driver interface data + * @freq: Frequency (in MHz) of the channel + * @duration: Duration in milliseconds + * Returns: 0 on success, -1 on failure + * + * This command is used to request the driver to remain awake on the + * specified channel for the specified duration and report received + * Action frames with EVENT_RX_ACTION events. Optionally, received + * Probe Request frames may also be requested to be reported by calling + * probe_req_report(). These will be reported with EVENT_RX_PROBE_REQ. + * + * The driver may not be at the requested channel when this function + * returns, i.e., the return code is only indicating whether the + * request was accepted. The caller will need to wait until the + * EVENT_REMAIN_ON_CHANNEL event indicates that the driver has + * completed the channel change. This may take some time due to other + * need for the radio and the caller should be prepared to timing out + * its wait since there are no guarantees on when this request can be + * executed. + */ + int (*remain_on_channel)(void *priv, unsigned int freq, + unsigned int duration); + + /** + * cancel_remain_on_channel - Cancel remain-on-channel operation + * @priv: Private driver interface data + * + * This command can be used to cancel a remain-on-channel operation + * before its originally requested duration has passed. This could be + * used, e.g., when remain_on_channel() is used to request extra time + * to receive a response to an Action frame and the response is + * received when there is still unneeded time remaining on the + * remain-on-channel operation. + */ + int (*cancel_remain_on_channel)(void *priv); + + /** + * probe_req_report - Request Probe Request frames to be indicated + * @priv: Private driver interface data + * @report: Whether to report received Probe Request frames + * Returns: 0 on success, -1 on failure (or if not supported) + * + * This command can be used to request the driver to indicate when + * Probe Request frames are received with EVENT_RX_PROBE_REQ events. + * Since this operation may require extra resources, e.g., due to less + * optimal hardware/firmware RX filtering, many drivers may disable + * Probe Request reporting at least in station mode. This command is + * used to notify the driver when the Probe Request frames need to be + * reported, e.g., during remain-on-channel operations. + */ + int (*probe_req_report)(void *priv, int report); + + /** + * disable_11b_rates - Set whether IEEE 802.11b rates are used for TX + * @priv: Private driver interface data + * @disabled: Whether IEEE 802.11b rates are disabled + * Returns: 0 on success, -1 on failure (or if not supported) + * + * This command is used to disable IEEE 802.11b rates (1, 2, 5.5, and + * 11 Mbps) as TX rates for data and management frames. This can be + * used to optimize channel use when there is no need to support IEEE + * 802.11b-only devices. + */ + int (*disable_11b_rates)(void *priv, int disabled); + + /** + * deinit_ap - Deinitialize AP mode + * @priv: Private driver interface data + * Returns: 0 on success, -1 on failure (or if not supported) + * + * This optional function can be used to disable AP mode related + * configuration and change the driver mode to station mode to allow + * normal station operations like scanning to be completed. + */ + int (*deinit_ap)(void *priv); + + /** + * suspend - Notification on system suspend/hibernate event + * @priv: Private driver interface data + */ + void (*suspend)(void *priv); + + /** + * resume - Notification on system resume/thaw event + * @priv: Private driver interface data + */ + void (*resume)(void *priv); + + /** + * signal_monitor - Set signal monitoring parameters + * @priv: Private driver interface data + * @threshold: Threshold value for signal change events; 0 = disabled + * @hysteresis: Minimum change in signal strength before indicating a + * new event + * Returns: 0 on success, -1 on failure (or if not supported) + * + * This function can be used to configure monitoring of signal strength + * with the current AP. Whenever signal strength drops below the + * %threshold value or increases above it, EVENT_SIGNAL_CHANGE event + * should be generated assuming the signal strength has changed at + * least %hysteresis from the previously indicated signal change event. + */ + int (*signal_monitor)(void *priv, int threshold, int hysteresis); + + /** + * send_frame - Send IEEE 802.11 frame (testing use only) + * @priv: Private driver interface data + * @data: IEEE 802.11 frame with IEEE 802.11 header + * @data_len: Size of the frame + * @encrypt: Whether to encrypt the frame (if keys are set) + * Returns: 0 on success, -1 on failure + * + * This function is only used for debugging purposes and is not + * required to be implemented for normal operations. + */ + int (*send_frame)(void *priv, const u8 *data, size_t data_len, + int encrypt); }; -/* Function to check whether a driver is for wired connections */ -static inline int IS_WIRED(const struct wpa_driver_ops *drv) -{ - return os_strcmp(drv->name, "wired") == 0 || - os_strcmp(drv->name, "roboswitch") == 0; -} /** * enum wpa_event_type - Event type for wpa_supplicant_event() calls */ -typedef enum wpa_event_type { +enum wpa_event_type { /** * EVENT_ASSOC - Association completed * @@ -1049,7 +1791,10 @@ typedef enum wpa_event_type { * * This event should be called when association is lost either due to * receiving deauthenticate or disassociate frame from the AP or when - * sending either of these frames to the current AP. + * sending either of these frames to the current AP. If the driver + * supports separate deauthentication event, EVENT_DISASSOC should only + * be used for disassociation and EVENT_DEAUTH for deauthentication. + * In AP mode, union wpa_event_data::disassoc_info is required. */ EVENT_DISASSOC, @@ -1075,7 +1820,8 @@ typedef enum wpa_event_type { * EVENT_SCAN_RESULTS call. If such event is not available from the * driver, the driver wrapper code is expected to use a registered * timeout to generate EVENT_SCAN_RESULTS call after the time that the - * scan is expected to be completed. + * scan is expected to be completed. Optional information about + * completed scan can be provided with union wpa_event_data::scan_info. */ EVENT_SCAN_RESULTS, @@ -1147,8 +1893,161 @@ typedef enum wpa_event_type { * FT authentication sequence from the AP. The FT IEs are included in * the extra information in union wpa_event_data::ft_ies. */ - EVENT_FT_RESPONSE -} wpa_event_type; + EVENT_FT_RESPONSE, + + /** + * EVENT_IBSS_RSN_START - Request RSN authentication in IBSS + * + * The driver can use this event to inform wpa_supplicant about a STA + * in an IBSS with which protected frames could be exchanged. This + * event starts RSN authentication with the other STA to authenticate + * the STA and set up encryption keys with it. + */ + EVENT_IBSS_RSN_START, + + /** + * EVENT_AUTH - Authentication result + * + * This event should be called when authentication attempt has been + * completed. This is only used if the driver supports separate + * authentication step (struct wpa_driver_ops::authenticate). + * Information about authentication result is included in + * union wpa_event_data::auth. + */ + EVENT_AUTH, + + /** + * EVENT_DEAUTH - Authentication lost + * + * This event should be called when authentication is lost either due + * to receiving deauthenticate frame from the AP or when sending that + * frame to the current AP. + * In AP mode, union wpa_event_data::deauth_info is required. + */ + EVENT_DEAUTH, + + /** + * EVENT_ASSOC_REJECT - Association rejected + * + * This event should be called when (re)association attempt has been + * rejected by the AP. Information about authentication result is + * included in union wpa_event_data::assoc_reject. + */ + EVENT_ASSOC_REJECT, + + /** + * EVENT_AUTH_TIMED_OUT - Authentication timed out + */ + EVENT_AUTH_TIMED_OUT, + + /** + * EVENT_ASSOC_TIMED_OUT - Association timed out + */ + EVENT_ASSOC_TIMED_OUT, + + /** + * EVENT_FT_RRB_RX - FT (IEEE 802.11r) RRB frame received + */ + EVENT_FT_RRB_RX, + + /** + * EVENT_WPS_BUTTON_PUSHED - Report hardware push button press for WPS + */ + EVENT_WPS_BUTTON_PUSHED, + + /** + * EVENT_TX_STATUS - Report TX status + */ + EVENT_TX_STATUS, + + /** + * EVENT_RX_FROM_UNKNOWN - Report RX from unknown STA + */ + EVENT_RX_FROM_UNKNOWN, + + /** + * EVENT_RX_MGMT - Report RX of a management frame + */ + EVENT_RX_MGMT, + + /** + * EVENT_RX_ACTION - Action frame received + * + * This event is used to indicate when an Action frame has been + * received. Information about the received frame is included in + * union wpa_event_data::rx_action. + */ + EVENT_RX_ACTION, + + /** + * EVENT_REMAIN_ON_CHANNEL - Remain-on-channel duration started + * + * This event is used to indicate when the driver has started the + * requested remain-on-channel duration. Information about the + * operation is included in union wpa_event_data::remain_on_channel. + */ + EVENT_REMAIN_ON_CHANNEL, + + /** + * EVENT_CANCEL_REMAIN_ON_CHANNEL - Remain-on-channel timed out + * + * This event is used to indicate when the driver has completed + * remain-on-channel duration, i.e., may noot be available on the + * requested channel anymore. Information about the + * operation is included in union wpa_event_data::remain_on_channel. + */ + EVENT_CANCEL_REMAIN_ON_CHANNEL, + + /** + * EVENT_MLME_RX - Report reception of frame for MLME (test use only) + * + * This event is used only by driver_test.c and userspace MLME. + */ + EVENT_MLME_RX, + + /** + * EVENT_RX_PROBE_REQ - Indicate received Probe Request frame + * + * This event is used to indicate when a Probe Request frame has been + * received. Information about the received frame is included in + * union wpa_event_data::rx_probe_req. The driver is required to report + * these events only after successfully completed probe_req_report() + * commands to request the events (i.e., report parameter is non-zero) + * in station mode. In AP mode, Probe Request frames should always be + * reported. + */ + EVENT_RX_PROBE_REQ, + + /** + * EVENT_NEW_STA - New wired device noticed + * + * This event is used to indicate that a new device has been detected + * in a network that does not use association-like functionality (i.e., + * mainly wired Ethernet). This can be used to start EAPOL + * authenticator when receiving a frame from a device. The address of + * the device is included in union wpa_event_data::new_sta. + */ + EVENT_NEW_STA, + + /** + * EVENT_EAPOL_RX - Report received EAPOL frame + * + * When in AP mode with hostapd, this event is required to be used to + * deliver the receive EAPOL frames from the driver. With + * %wpa_supplicant, this event is used only if the send_eapol() handler + * is used to override the use of l2_packet for EAPOL frame TX. + */ + EVENT_EAPOL_RX, + + /** + * EVENT_SIGNAL_CHANGE - Indicate change in signal strength + * + * This event is used to indicate changes in the signal strength + * observed in frames received from the current AP if signal strength + * monitoring has been enabled with signal_monitor(). + */ + EVENT_SIGNAL_CHANGE +}; /** @@ -1175,7 +2074,7 @@ union wpa_event_data { * This should start with the first IE (fixed fields before IEs * are not included). */ - u8 *req_ies; + const u8 *req_ies; /** * req_ies_len - Length of req_ies in bytes @@ -1193,7 +2092,7 @@ union wpa_event_data { * This should start with the first IE (fixed fields before IEs * are not included). */ - u8 *resp_ies; + const u8 *resp_ies; /** * resp_ies_len - Length of resp_ies in bytes @@ -1216,18 +2115,61 @@ union wpa_event_data { * This should start with the first IE (fixed fields before IEs * are not included). */ - u8 *beacon_ies; + const u8 *beacon_ies; /** * beacon_ies_len - Length of beacon_ies */ size_t beacon_ies_len; + + /** + * freq - Frequency of the operational channel in MHz + */ + unsigned int freq; + + /** + * addr - Station address (for AP mode) + */ + const u8 *addr; } assoc_info; /** + * struct disassoc_info - Data for EVENT_DISASSOC events + */ + struct disassoc_info { + /** + * addr - Station address (for AP mode) + */ + const u8 *addr; + + /** + * reason_code - Reason Code (host byte order) used in + * Deauthentication frame + */ + u16 reason_code; + } disassoc_info; + + /** + * struct deauth_info - Data for EVENT_DEAUTH events + */ + struct deauth_info { + /** + * addr - Station address (for AP mode) + */ + const u8 *addr; + + /** + * reason_code - Reason Code (host byte order) used in + * Deauthentication frame + */ + u16 reason_code; + } deauth_info; + + /** * struct michael_mic_failure - Data for EVENT_MICHAEL_MIC_FAILURE */ struct michael_mic_failure { int unicast; + const u8 *src; } michael_mic_failure; /** @@ -1273,7 +2215,228 @@ union wpa_event_data { size_t ies_len; int ft_action; u8 target_ap[ETH_ALEN]; + /** Optional IE(s), e.g., WMM TSPEC(s), for RIC-Request */ + const u8 *ric_ies; + /** Length of ric_ies buffer in octets */ + size_t ric_ies_len; } ft_ies; + + /** + * struct ibss_rsn_start - Data for EVENT_IBSS_RSN_START + */ + struct ibss_rsn_start { + u8 peer[ETH_ALEN]; + } ibss_rsn_start; + + /** + * struct auth_info - Data for EVENT_AUTH events + */ + struct auth_info { + u8 peer[ETH_ALEN]; + u16 auth_type; + u16 status_code; + const u8 *ies; + size_t ies_len; + } auth; + + /** + * struct assoc_reject - Data for EVENT_ASSOC_REJECT events + */ + struct assoc_reject { + /** + * resp_ies - (Re)Association Response IEs + * + * Optional association data from the driver. This data is not + * required WPA, but may be useful for some protocols and as + * such, should be reported if this is available to the driver + * interface. + * + * This should start with the first IE (fixed fields before IEs + * are not included). + */ + u8 *resp_ies; + + /** + * resp_ies_len - Length of resp_ies in bytes + */ + size_t resp_ies_len; + + /** + * status_code - Status Code from (Re)association Response + */ + u16 status_code; + } assoc_reject; + + struct timeout_event { + u8 addr[ETH_ALEN]; + } timeout_event; + + /** + * struct ft_rrb_rx - Data for EVENT_FT_RRB_RX events + */ + struct ft_rrb_rx { + const u8 *src; + const u8 *data; + size_t data_len; + } ft_rrb_rx; + + /** + * struct tx_status - Data for EVENT_TX_STATUS events + */ + struct tx_status { + u16 type; + u16 stype; + const u8 *dst; + const u8 *data; + size_t data_len; + int ack; + } tx_status; + + /** + * struct rx_from_unknown - Data for EVENT_RX_FROM_UNKNOWN events + */ + struct rx_from_unknown { + const u8 *frame; + size_t len; + } rx_from_unknown; + + /** + * struct rx_mgmt - Data for EVENT_RX_MGMT events + */ + struct rx_mgmt { + const u8 *frame; + size_t frame_len; + u32 datarate; + u32 ssi_signal; + } rx_mgmt; + + /** + * struct rx_action - Data for EVENT_RX_ACTION events + */ + struct rx_action { + /** + * da - Destination address of the received Action frame + */ + const u8 *da; + + /** + * sa - Source address of the received Action frame + */ + const u8 *sa; + + /** + * bssid - Address 3 of the received Action frame + */ + const u8 *bssid; + + /** + * category - Action frame category + */ + u8 category; + + /** + * data - Action frame body after category field + */ + const u8 *data; + + /** + * len - Length of data in octets + */ + size_t len; + + /** + * freq - Frequency (in MHz) on which the frame was received + */ + int freq; + } rx_action; + + /** + * struct remain_on_channel - Data for EVENT_REMAIN_ON_CHANNEL events + * + * This is also used with EVENT_CANCEL_REMAIN_ON_CHANNEL events. + */ + struct remain_on_channel { + /** + * freq - Channel frequency in MHz + */ + unsigned int freq; + + /** + * duration - Duration to remain on the channel in milliseconds + */ + unsigned int duration; + } remain_on_channel; + + /** + * struct scan_info - Optional data for EVENT_SCAN_RESULTS events + * @aborted: Whether the scan was aborted + * @freqs: Scanned frequencies in MHz (%NULL = all channels scanned) + * @num_freqs: Number of entries in freqs array + * @ssids: Scanned SSIDs (%NULL or zero-length SSID indicates wildcard + * SSID) + * @num_ssids: Number of entries in ssids array + */ + struct scan_info { + int aborted; + const int *freqs; + size_t num_freqs; + struct wpa_driver_scan_ssid ssids[WPAS_MAX_SCAN_SSIDS]; + size_t num_ssids; + } scan_info; + + /** + * struct mlme_rx - Data for EVENT_MLME_RX events + */ + struct mlme_rx { + const u8 *buf; + size_t len; + int freq; + int channel; + int ssi; + } mlme_rx; + + /** + * struct rx_probe_req - Data for EVENT_RX_PROBE_REQ events + */ + struct rx_probe_req { + /** + * sa - Source address of the received Probe Request frame + */ + const u8 *sa; + + /** + * ie - IEs from the Probe Request body + */ + const u8 *ie; + + /** + * ie_len - Length of ie buffer in octets + */ + size_t ie_len; + } rx_probe_req; + + /** + * struct new_sta - Data for EVENT_NEW_STA events + */ + struct new_sta { + const u8 *addr; + } new_sta; + + /** + * struct eapol_rx - Data for EVENT_EAPOL_RX events + */ + struct eapol_rx { + const u8 *src; + const u8 *data; + size_t data_len; + } eapol_rx; + + /** + * struct signal_change - Data for EVENT_SIGNAL_CHANGE events + */ + struct signal_change { + int above_threshold; + } signal_change; }; /** @@ -1286,40 +2449,43 @@ union wpa_event_data { * Driver wrapper code should call this function whenever an event is received * from the driver. */ -void wpa_supplicant_event(void *ctx, wpa_event_type event, +void wpa_supplicant_event(void *ctx, enum wpa_event_type event, union wpa_event_data *data); -/** - * wpa_supplicant_rx_eapol - Deliver a received EAPOL frame to wpa_supplicant - * @ctx: Context pointer (wpa_s); this is the ctx variable registered - * with struct wpa_driver_ops::init() - * @src_addr: Source address of the EAPOL frame - * @buf: EAPOL data starting from the EAPOL header (i.e., no Ethernet header) - * @len: Length of the EAPOL data - * - * This function is called for each received EAPOL frame. Most driver - * interfaces rely on more generic OS mechanism for receiving frames through - * l2_packet, but if such a mechanism is not available, the driver wrapper may - * take care of received EAPOL frames and deliver them to the core supplicant - * code by calling this function. + +/* + * The following inline functions are provided for convenience to simplify + * event indication for some of the common events. */ -void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, - const u8 *buf, size_t len); -void wpa_supplicant_sta_rx(void *ctx, const u8 *buf, size_t len, - struct ieee80211_rx_status *rx_status); -void wpa_supplicant_sta_free_hw_features(struct wpa_hw_modes *hw_features, - size_t num_hw_features); +static inline void drv_event_assoc(void *ctx, const u8 *addr, const u8 *ie, + size_t ielen) +{ + union wpa_event_data event; + os_memset(&event, 0, sizeof(event)); + event.assoc_info.req_ies = ie; + event.assoc_info.req_ies_len = ielen; + event.assoc_info.addr = addr; + wpa_supplicant_event(ctx, EVENT_ASSOC, &event); +} -const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie); -#define WPA_IE_VENDOR_TYPE 0x0050f201 -#define WPS_IE_VENDOR_TYPE 0x0050f204 -const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res, - u32 vendor_type); -struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res, - u32 vendor_type); -int wpa_scan_get_max_rate(const struct wpa_scan_res *res); -void wpa_scan_results_free(struct wpa_scan_results *res); -void wpa_scan_sort_results(struct wpa_scan_results *res); +static inline void drv_event_disassoc(void *ctx, const u8 *addr) +{ + union wpa_event_data event; + os_memset(&event, 0, sizeof(event)); + event.disassoc_info.addr = addr; + wpa_supplicant_event(ctx, EVENT_DISASSOC, &event); +} + +static inline void drv_event_eapol_rx(void *ctx, const u8 *src, const u8 *data, + size_t data_len) +{ + union wpa_event_data event; + os_memset(&event, 0, sizeof(event)); + event.eapol_rx.src = src; + event.eapol_rx.data = data; + event.eapol_rx.data_len = data_len; + wpa_supplicant_event(ctx, EVENT_EAPOL_RX, &event); +} #endif /* DRIVER_H */ diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c new file mode 100644 index 0000000000000..5c25f00b16431 --- /dev/null +++ b/src/drivers/driver_atheros.c @@ -0,0 +1,1298 @@ +/* + * hostapd / Driver interaction with Atheros driver + * Copyright (c) 2004, Sam Leffler <sam@errno.com> + * Copyright (c) 2004, Video54 Technologies + * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2009, Atheros Communications + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <net/if.h> +#include <sys/ioctl.h> + +#include "common.h" +#ifndef _BYTE_ORDER +#ifdef WORDS_BIGENDIAN +#define _BYTE_ORDER _BIG_ENDIAN +#else +#define _BYTE_ORDER _LITTLE_ENDIAN +#endif +#endif /* _BYTE_ORDER */ + +/* + * Note, the ATH_WPS_IE setting must match with the driver build.. If the + * driver does not include this, the IEEE80211_IOCTL_GETWPAIE ioctl will fail. + */ +#define ATH_WPS_IE + +#include "os/linux/include/ieee80211_external.h" + + +#ifdef CONFIG_WPS +#include <netpacket/packet.h> + +#ifndef ETH_P_80211_RAW +#define ETH_P_80211_RAW 0x0019 +#endif +#endif /* CONFIG_WPS */ + +#include "wireless_copy.h" + +#include "driver.h" +#include "eloop.h" +#include "priv_netlink.h" +#include "l2_packet/l2_packet.h" +#include "common/ieee802_11_defs.h" +#include "netlink.h" +#include "linux_ioctl.h" + + +struct madwifi_driver_data { + struct hostapd_data *hapd; /* back pointer */ + + char iface[IFNAMSIZ + 1]; + int ifindex; + struct l2_packet_data *sock_xmit; /* raw packet xmit socket */ + struct l2_packet_data *sock_recv; /* raw packet recv socket */ + int ioctl_sock; /* socket for ioctl() use */ + struct netlink_data *netlink; + int we_version; + u8 acct_mac[ETH_ALEN]; + struct hostap_sta_driver_data acct_data; + + struct l2_packet_data *sock_raw; /* raw 802.11 management frames */ +}; + +static int madwifi_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, + int reason_code); +static int madwifi_set_privacy(void *priv, int enabled); + +static const char * athr_get_ioctl_name(int op) +{ + switch (op) { + case IEEE80211_IOCTL_SETPARAM: + return "SETPARAM"; + case IEEE80211_IOCTL_GETPARAM: + return "GETPARAM"; + case IEEE80211_IOCTL_SETKEY: + return "SETKEY"; + case IEEE80211_IOCTL_SETWMMPARAMS: + return "SETWMMPARAMS"; + case IEEE80211_IOCTL_DELKEY: + return "DELKEY"; + case IEEE80211_IOCTL_GETWMMPARAMS: + return "GETWMMPARAMS"; + case IEEE80211_IOCTL_SETMLME: + return "SETMLME"; + case IEEE80211_IOCTL_GETCHANINFO: + return "GETCHANINFO"; + case IEEE80211_IOCTL_SETOPTIE: + return "SETOPTIE"; + case IEEE80211_IOCTL_GETOPTIE: + return "GETOPTIE"; + case IEEE80211_IOCTL_ADDMAC: + return "ADDMAC"; + case IEEE80211_IOCTL_DELMAC: + return "DELMAC"; + case IEEE80211_IOCTL_GETCHANLIST: + return "GETCHANLIST"; + case IEEE80211_IOCTL_SETCHANLIST: + return "SETCHANLIST"; + case IEEE80211_IOCTL_KICKMAC: + return "KICKMAC"; + case IEEE80211_IOCTL_CHANSWITCH: + return "CHANSWITCH"; + case IEEE80211_IOCTL_GETMODE: + return "GETMODE"; + case IEEE80211_IOCTL_SETMODE: + return "SETMODE"; + case IEEE80211_IOCTL_GET_APPIEBUF: + return "GET_APPIEBUF"; + case IEEE80211_IOCTL_SET_APPIEBUF: + return "SET_APPIEBUF"; + case IEEE80211_IOCTL_SET_ACPARAMS: + return "SET_ACPARAMS"; + case IEEE80211_IOCTL_FILTERFRAME: + return "FILTERFRAME"; + case IEEE80211_IOCTL_SET_RTPARAMS: + return "SET_RTPARAMS"; + case IEEE80211_IOCTL_SENDADDBA: + return "SENDADDBA"; + case IEEE80211_IOCTL_GETADDBASTATUS: + return "GETADDBASTATUS"; + case IEEE80211_IOCTL_SENDDELBA: + return "SENDDELBA"; + case IEEE80211_IOCTL_SET_MEDENYENTRY: + return "SET_MEDENYENTRY"; + case IEEE80211_IOCTL_SET_ADDBARESP: + return "SET_ADDBARESP"; + case IEEE80211_IOCTL_GET_MACADDR: + return "GET_MACADDR"; + case IEEE80211_IOCTL_SET_HBRPARAMS: + return "SET_HBRPARAMS"; + case IEEE80211_IOCTL_SET_RXTIMEOUT: + return "SET_RXTIMEOUT"; + case IEEE80211_IOCTL_STA_STATS: + return "STA_STATS"; + case IEEE80211_IOCTL_GETWPAIE: + return "GETWPAIE"; + default: + return "??"; + } +} + + +static const char * athr_get_param_name(int op) +{ + switch (op) { + case IEEE80211_IOC_MCASTCIPHER: + return "MCASTCIPHER"; + case IEEE80211_PARAM_MCASTKEYLEN: + return "MCASTKEYLEN"; + case IEEE80211_PARAM_UCASTCIPHERS: + return "UCASTCIPHERS"; + case IEEE80211_PARAM_KEYMGTALGS: + return "KEYMGTALGS"; + case IEEE80211_PARAM_RSNCAPS: + return "RSNCAPS"; + case IEEE80211_PARAM_WPA: + return "WPA"; + case IEEE80211_PARAM_AUTHMODE: + return "AUTHMODE"; + case IEEE80211_PARAM_PRIVACY: + return "PRIVACY"; + case IEEE80211_PARAM_COUNTERMEASURES: + return "COUNTERMEASURES"; + default: + return "??"; + } +} + + +static int +set80211priv(struct madwifi_driver_data *drv, int op, void *data, int len) +{ + struct iwreq iwr; + int do_inline = len < IFNAMSIZ; + + /* Certain ioctls must use the non-inlined method */ + if (op == IEEE80211_IOCTL_SET_APPIEBUF || + op == IEEE80211_IOCTL_FILTERFRAME) + do_inline = 0; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + if (do_inline) { + /* + * Argument data fits inline; put it there. + */ + memcpy(iwr.u.name, data, len); + } else { + /* + * Argument data too big for inline transfer; setup a + * parameter block instead; the kernel will transfer + * the data for the driver. + */ + iwr.u.data.pointer = data; + iwr.u.data.length = len; + } + + if (ioctl(drv->ioctl_sock, op, &iwr) < 0) { + wpa_printf(MSG_DEBUG, "atheros: %s: %s: ioctl op=0x%x " + "(%s) len=%d failed: %d (%s)", + __func__, drv->iface, op, + athr_get_ioctl_name(op), + len, errno, strerror(errno)); + return -1; + } + return 0; +} + +static int +set80211param(struct madwifi_driver_data *drv, int op, int arg) +{ + struct iwreq iwr; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.mode = op; + memcpy(iwr.u.name+sizeof(__u32), &arg, sizeof(arg)); + + if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_SETPARAM, &iwr) < 0) { + perror("ioctl[IEEE80211_IOCTL_SETPARAM]"); + wpa_printf(MSG_DEBUG, "%s: %s: Failed to set parameter (op %d " + "(%s) arg %d)", __func__, drv->iface, op, + athr_get_param_name(op), arg); + return -1; + } + return 0; +} + +#ifndef CONFIG_NO_STDOUT_DEBUG +static const char * +ether_sprintf(const u8 *addr) +{ + static char buf[sizeof(MACSTR)]; + + if (addr != NULL) + snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr)); + else + snprintf(buf, sizeof(buf), MACSTR, 0,0,0,0,0,0); + return buf; +} +#endif /* CONFIG_NO_STDOUT_DEBUG */ + +/* + * Configure WPA parameters. + */ +static int +madwifi_configure_wpa(struct madwifi_driver_data *drv, + struct wpa_bss_params *params) +{ + int v; + + switch (params->wpa_group) { + case WPA_CIPHER_CCMP: + v = IEEE80211_CIPHER_AES_CCM; + break; + case WPA_CIPHER_TKIP: + v = IEEE80211_CIPHER_TKIP; + break; + case WPA_CIPHER_WEP104: + v = IEEE80211_CIPHER_WEP; + break; + case WPA_CIPHER_WEP40: + v = IEEE80211_CIPHER_WEP; + break; + case WPA_CIPHER_NONE: + v = IEEE80211_CIPHER_NONE; + break; + default: + wpa_printf(MSG_ERROR, "Unknown group key cipher %u", + params->wpa_group); + return -1; + } + wpa_printf(MSG_DEBUG, "%s: group key cipher=%d", __func__, v); + if (set80211param(drv, IEEE80211_PARAM_MCASTCIPHER, v)) { + printf("Unable to set group key cipher to %u\n", v); + return -1; + } + if (v == IEEE80211_CIPHER_WEP) { + /* key length is done only for specific ciphers */ + v = (params->wpa_group == WPA_CIPHER_WEP104 ? 13 : 5); + if (set80211param(drv, IEEE80211_PARAM_MCASTKEYLEN, v)) { + printf("Unable to set group key length to %u\n", v); + return -1; + } + } + + v = 0; + if (params->wpa_pairwise & WPA_CIPHER_CCMP) + v |= 1<<IEEE80211_CIPHER_AES_CCM; + if (params->wpa_pairwise & WPA_CIPHER_TKIP) + v |= 1<<IEEE80211_CIPHER_TKIP; + if (params->wpa_pairwise & WPA_CIPHER_NONE) + v |= 1<<IEEE80211_CIPHER_NONE; + wpa_printf(MSG_DEBUG, "%s: pairwise key ciphers=0x%x", __func__, v); + if (set80211param(drv, IEEE80211_PARAM_UCASTCIPHERS, v)) { + printf("Unable to set pairwise key ciphers to 0x%x\n", v); + return -1; + } + + wpa_printf(MSG_DEBUG, "%s: key management algorithms=0x%x", + __func__, params->wpa_key_mgmt); + if (set80211param(drv, IEEE80211_PARAM_KEYMGTALGS, + params->wpa_key_mgmt)) { + printf("Unable to set key management algorithms to 0x%x\n", + params->wpa_key_mgmt); + return -1; + } + + v = 0; + if (params->rsn_preauth) + v |= BIT(0); + wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x", + __func__, params->rsn_preauth); + if (set80211param(drv, IEEE80211_PARAM_RSNCAPS, v)) { + printf("Unable to set RSN capabilities to 0x%x\n", v); + return -1; + } + + wpa_printf(MSG_DEBUG, "%s: enable WPA=0x%x", __func__, params->wpa); + if (set80211param(drv, IEEE80211_PARAM_WPA, params->wpa)) { + printf("Unable to set WPA to %u\n", params->wpa); + return -1; + } + return 0; +} + +static int +madwifi_set_ieee8021x(void *priv, struct wpa_bss_params *params) +{ + struct madwifi_driver_data *drv = priv; + + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, params->enabled); + + if (!params->enabled) { + /* XXX restore state */ + if (set80211param(priv, IEEE80211_PARAM_AUTHMODE, + IEEE80211_AUTH_AUTO) < 0) + return -1; + /* IEEE80211_AUTH_AUTO ends up enabling Privacy; clear that */ + return madwifi_set_privacy(drv, 0); + } + if (!params->wpa && !params->ieee802_1x) { + hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER, + HOSTAPD_LEVEL_WARNING, "No 802.1X or WPA enabled!"); + return -1; + } + if (params->wpa && madwifi_configure_wpa(drv, params) != 0) { + hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER, + HOSTAPD_LEVEL_WARNING, "Error configuring WPA state!"); + return -1; + } + if (set80211param(priv, IEEE80211_PARAM_AUTHMODE, + (params->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) { + hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER, + HOSTAPD_LEVEL_WARNING, "Error enabling WPA/802.1X!"); + return -1; + } + + return 0; +} + +static int +madwifi_set_privacy(void *priv, int enabled) +{ + struct madwifi_driver_data *drv = priv; + + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); + + return set80211param(drv, IEEE80211_PARAM_PRIVACY, enabled); +} + +static int +madwifi_set_sta_authorized(void *priv, const u8 *addr, int authorized) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s authorized=%d", + __func__, ether_sprintf(addr), authorized); + + if (authorized) + mlme.im_op = IEEE80211_MLME_AUTHORIZE; + else + mlme.im_op = IEEE80211_MLME_UNAUTHORIZE; + mlme.im_reason = 0; + memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to %sauthorize STA " MACSTR, + __func__, authorized ? "" : "un", MAC2STR(addr)); + } + + return ret; +} + +static int +madwifi_sta_set_flags(void *priv, const u8 *addr, + int total_flags, int flags_or, int flags_and) +{ + /* For now, only support setting Authorized flag */ + if (flags_or & WPA_STA_AUTHORIZED) + return madwifi_set_sta_authorized(priv, addr, 1); + if (!(flags_and & WPA_STA_AUTHORIZED)) + return madwifi_set_sta_authorized(priv, addr, 0); + return 0; +} + +static int +madwifi_del_key(void *priv, const u8 *addr, int key_idx) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_del_key wk; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s key_idx=%d", + __func__, ether_sprintf(addr), key_idx); + + memset(&wk, 0, sizeof(wk)); + if (addr != NULL) { + memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN); + wk.idk_keyix = (u8) IEEE80211_KEYIX_NONE; + } else { + wk.idk_keyix = key_idx; + } + + ret = set80211priv(drv, IEEE80211_IOCTL_DELKEY, &wk, sizeof(wk)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to delete key (addr %s" + " key_idx %d)", __func__, ether_sprintf(addr), + key_idx); + } + + return ret; +} + +static int +madwifi_set_key(const char *ifname, void *priv, enum wpa_alg alg, + const u8 *addr, int key_idx, int set_tx, const u8 *seq, + size_t seq_len, const u8 *key, size_t key_len) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_key wk; + u_int8_t cipher; + int ret; + + if (alg == WPA_ALG_NONE) + return madwifi_del_key(drv, addr, key_idx); + + wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%s key_idx=%d", + __func__, alg, ether_sprintf(addr), key_idx); + + switch (alg) { + case WPA_ALG_WEP: + cipher = IEEE80211_CIPHER_WEP; + break; + case WPA_ALG_TKIP: + cipher = IEEE80211_CIPHER_TKIP; + break; + case WPA_ALG_CCMP: + cipher = IEEE80211_CIPHER_AES_CCM; + break; + default: + printf("%s: unknown/unsupported algorithm %d\n", + __func__, alg); + return -1; + } + + if (key_len > sizeof(wk.ik_keydata)) { + printf("%s: key length %lu too big\n", __func__, + (unsigned long) key_len); + return -3; + } + + memset(&wk, 0, sizeof(wk)); + wk.ik_type = cipher; + wk.ik_flags = IEEE80211_KEY_RECV | IEEE80211_KEY_XMIT; + if (addr == NULL) { + memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); + wk.ik_keyix = key_idx; + wk.ik_flags |= IEEE80211_KEY_DEFAULT; + } else { + memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); + wk.ik_keyix = IEEE80211_KEYIX_NONE; + } + wk.ik_keylen = key_len; + memcpy(wk.ik_keydata, key, key_len); + + ret = set80211priv(drv, IEEE80211_IOCTL_SETKEY, &wk, sizeof(wk)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to set key (addr %s" + " key_idx %d alg %d key_len %lu set_tx %d)", + __func__, ether_sprintf(wk.ik_macaddr), key_idx, + alg, (unsigned long) key_len, set_tx); + } + + return ret; +} + + +static int +madwifi_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx, + u8 *seq) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_key wk; + + wpa_printf(MSG_DEBUG, "%s: addr=%s idx=%d", + __func__, ether_sprintf(addr), idx); + + memset(&wk, 0, sizeof(wk)); + if (addr == NULL) + memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); + else + memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); + wk.ik_keyix = idx; + + if (set80211priv(drv, IEEE80211_IOCTL_GETKEY, &wk, sizeof(wk))) { + wpa_printf(MSG_DEBUG, "%s: Failed to get encryption data " + "(addr " MACSTR " key_idx %d)", + __func__, MAC2STR(wk.ik_macaddr), idx); + return -1; + } + +#ifdef WORDS_BIGENDIAN + { + /* + * wk.ik_keytsc is in host byte order (big endian), need to + * swap it to match with the byte order used in WPA. + */ + int i; +#ifndef WPA_KEY_RSC_LEN +#define WPA_KEY_RSC_LEN 8 +#endif + u8 tmp[WPA_KEY_RSC_LEN]; + memcpy(tmp, &wk.ik_keytsc, sizeof(wk.ik_keytsc)); + for (i = 0; i < WPA_KEY_RSC_LEN; i++) { + seq[i] = tmp[WPA_KEY_RSC_LEN - i - 1]; + } + } +#else /* WORDS_BIGENDIAN */ + memcpy(seq, &wk.ik_keytsc, sizeof(wk.ik_keytsc)); +#endif /* WORDS_BIGENDIAN */ + return 0; +} + + +static int +madwifi_flush(void *priv) +{ + u8 allsta[IEEE80211_ADDR_LEN]; + memset(allsta, 0xff, IEEE80211_ADDR_LEN); + return madwifi_sta_deauth(priv, NULL, allsta, + IEEE80211_REASON_AUTH_LEAVE); +} + + +static int +madwifi_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data, + const u8 *addr) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_sta_stats stats; + + memset(data, 0, sizeof(*data)); + + /* + * Fetch statistics for station from the system. + */ + memset(&stats, 0, sizeof(stats)); + memcpy(stats.is_u.macaddr, addr, IEEE80211_ADDR_LEN); + if (set80211priv(drv, IEEE80211_IOCTL_STA_STATS, + &stats, sizeof(stats))) { + wpa_printf(MSG_DEBUG, "%s: Failed to fetch STA stats (addr " + MACSTR ")", __func__, MAC2STR(addr)); + if (memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) { + memcpy(data, &drv->acct_data, sizeof(*data)); + return 0; + } + + printf("Failed to get station stats information element.\n"); + return -1; + } + + data->rx_packets = stats.is_stats.ns_rx_data; + data->rx_bytes = stats.is_stats.ns_rx_bytes; + data->tx_packets = stats.is_stats.ns_tx_data; + data->tx_bytes = stats.is_stats.ns_tx_bytes; + return 0; +} + + +static int +madwifi_sta_clear_stats(void *priv, const u8 *addr) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s", __func__, ether_sprintf(addr)); + + mlme.im_op = IEEE80211_MLME_CLEAR_STATS; + memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, + sizeof(mlme)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to clear STA stats (addr " + MACSTR ")", __func__, MAC2STR(addr)); + } + + return ret; +} + + +static int +madwifi_set_opt_ie(void *priv, const u8 *ie, size_t ie_len) +{ + /* + * Do nothing; we setup parameters at startup that define the + * contents of the beacon information element. + */ + return 0; +} + +static int +madwifi_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, + int reason_code) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d", + __func__, ether_sprintf(addr), reason_code); + + mlme.im_op = IEEE80211_MLME_DEAUTH; + mlme.im_reason = reason_code; + memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to deauth STA (addr " MACSTR + " reason %d)", + __func__, MAC2STR(addr), reason_code); + } + + return ret; +} + +static int +madwifi_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, + int reason_code) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d", + __func__, ether_sprintf(addr), reason_code); + + mlme.im_op = IEEE80211_MLME_DISASSOC; + mlme.im_reason = reason_code; + memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to disassoc STA (addr " + MACSTR " reason %d)", + __func__, MAC2STR(addr), reason_code); + } + + return ret; +} + +#ifdef CONFIG_WPS +static void madwifi_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) +{ + struct madwifi_driver_data *drv = ctx; + const struct ieee80211_mgmt *mgmt; + u16 fc; + union wpa_event_data event; + + /* Send Probe Request information to WPS processing */ + + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)) + return; + mgmt = (const struct ieee80211_mgmt *) buf; + + fc = le_to_host16(mgmt->frame_control); + if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT || + WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_PROBE_REQ) + return; + + os_memset(&event, 0, sizeof(event)); + event.rx_probe_req.sa = mgmt->sa; + event.rx_probe_req.ie = mgmt->u.probe_req.variable; + event.rx_probe_req.ie_len = + len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)); + wpa_supplicant_event(drv->hapd, EVENT_RX_PROBE_REQ, &event); +} +#endif /* CONFIG_WPS */ + +static int madwifi_receive_probe_req(struct madwifi_driver_data *drv) +{ + int ret = 0; +#ifdef CONFIG_WPS + struct ieee80211req_set_filter filt; + + wpa_printf(MSG_DEBUG, "%s Enter", __func__); + filt.app_filterype = IEEE80211_FILTER_TYPE_PROBE_REQ; + + ret = set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt, + sizeof(struct ieee80211req_set_filter)); + if (ret) + return ret; + + drv->sock_raw = l2_packet_init(drv->iface, NULL, ETH_P_80211_RAW, + madwifi_raw_receive, drv, 1); + if (drv->sock_raw == NULL) + return -1; +#endif /* CONFIG_WPS */ + return ret; +} + +#ifdef CONFIG_WPS +static int +madwifi_set_wps_ie(void *priv, const u8 *ie, size_t len, u32 frametype) +{ + struct madwifi_driver_data *drv = priv; + u8 buf[256]; + struct ieee80211req_getset_appiebuf *beac_ie; + + wpa_printf(MSG_DEBUG, "%s buflen = %lu", __func__, + (unsigned long) len); + + beac_ie = (struct ieee80211req_getset_appiebuf *) buf; + beac_ie->app_frmtype = frametype; + beac_ie->app_buflen = len; + memcpy(&(beac_ie->app_buf[0]), ie, len); + + return set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, beac_ie, + sizeof(struct ieee80211req_getset_appiebuf) + len); +} + +static int +madwifi_set_ap_wps_ie(void *priv, const struct wpabuf *beacon, + const struct wpabuf *proberesp) +{ + if (madwifi_set_wps_ie(priv, beacon ? wpabuf_head(beacon) : NULL, + beacon ? wpabuf_len(beacon) : 0, + IEEE80211_APPIE_FRAME_BEACON)) + return -1; + return madwifi_set_wps_ie(priv, + proberesp ? wpabuf_head(proberesp) : NULL, + proberesp ? wpabuf_len(proberesp): 0, + IEEE80211_APPIE_FRAME_PROBE_RESP); +} +#else /* CONFIG_WPS */ +#define madwifi_set_ap_wps_ie NULL +#endif /* CONFIG_WPS */ + +static void +madwifi_new_sta(struct madwifi_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) +{ + struct hostapd_data *hapd = drv->hapd; + struct ieee80211req_wpaie ie; + int ielen = 0; + u8 *iebuf = NULL; + + /* + * Fetch negotiated WPA/RSN parameters from the system. + */ + memset(&ie, 0, sizeof(ie)); + memcpy(ie.wpa_macaddr, addr, IEEE80211_ADDR_LEN); + if (set80211priv(drv, IEEE80211_IOCTL_GETWPAIE, &ie, sizeof(ie))) { + /* + * See ATH_WPS_IE comment in the beginning of the file for a + * possible cause for the failure.. + */ + wpa_printf(MSG_DEBUG, "%s: Failed to get WPA/RSN IE: %s", + __func__, strerror(errno)); + goto no_ie; + } + wpa_hexdump(MSG_MSGDUMP, "madwifi req WPA IE", + ie.wpa_ie, IEEE80211_MAX_OPT_IE); + wpa_hexdump(MSG_MSGDUMP, "madwifi req RSN IE", + ie.rsn_ie, IEEE80211_MAX_OPT_IE); + iebuf = ie.wpa_ie; + /* madwifi seems to return some random data if WPA/RSN IE is not set. + * Assume the IE was not included if the IE type is unknown. */ + if (iebuf[0] != WLAN_EID_VENDOR_SPECIFIC) + iebuf[1] = 0; + if (iebuf[1] == 0 && ie.rsn_ie[1] > 0) { + /* madwifi-ng svn #1453 added rsn_ie. Use it, if wpa_ie was not + * set. This is needed for WPA2. */ + iebuf = ie.rsn_ie; + if (iebuf[0] != WLAN_EID_RSN) + iebuf[1] = 0; + } + + ielen = iebuf[1]; + if (ielen == 0) + iebuf = NULL; + else + ielen += 2; + +no_ie: + drv_event_assoc(hapd, addr, iebuf, ielen); + + if (memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) { + /* Cached accounting data is not valid anymore. */ + memset(drv->acct_mac, 0, ETH_ALEN); + memset(&drv->acct_data, 0, sizeof(drv->acct_data)); + } +} + +static void +madwifi_wireless_event_wireless_custom(struct madwifi_driver_data *drv, + char *custom, char *end) +{ + wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom); + + if (strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { + char *pos; + u8 addr[ETH_ALEN]; + pos = strstr(custom, "addr="); + if (pos == NULL) { + wpa_printf(MSG_DEBUG, + "MLME-MICHAELMICFAILURE.indication " + "without sender address ignored"); + return; + } + pos += 5; + if (hwaddr_aton(pos, addr) == 0) { + union wpa_event_data data; + os_memset(&data, 0, sizeof(data)); + data.michael_mic_failure.unicast = 1; + data.michael_mic_failure.src = addr; + wpa_supplicant_event(drv->hapd, + EVENT_MICHAEL_MIC_FAILURE, &data); + } else { + wpa_printf(MSG_DEBUG, + "MLME-MICHAELMICFAILURE.indication " + "with invalid MAC address"); + } + } else if (strncmp(custom, "STA-TRAFFIC-STAT", 16) == 0) { + char *key, *value; + u32 val; + key = custom; + while ((key = strchr(key, '\n')) != NULL) { + key++; + value = strchr(key, '='); + if (value == NULL) + continue; + *value++ = '\0'; + val = strtoul(value, NULL, 10); + if (strcmp(key, "mac") == 0) + hwaddr_aton(value, drv->acct_mac); + else if (strcmp(key, "rx_packets") == 0) + drv->acct_data.rx_packets = val; + else if (strcmp(key, "tx_packets") == 0) + drv->acct_data.tx_packets = val; + else if (strcmp(key, "rx_bytes") == 0) + drv->acct_data.rx_bytes = val; + else if (strcmp(key, "tx_bytes") == 0) + drv->acct_data.tx_bytes = val; + key = value; + } +#ifdef CONFIG_WPS + } else if (strncmp(custom, "PUSH-BUTTON.indication", 22) == 0) { + /* Some atheros kernels send push button as a wireless event */ + /* PROBLEM! this event is received for ALL BSSs ... + * so all are enabled for WPS... ugh. + */ + wpa_supplicant_event(drv->hapd, EVENT_WPS_BUTTON_PUSHED, NULL); + } else if (strncmp(custom, "Manage.prob_req ", 16) == 0) { + /* + * Atheros driver uses a hack to pass Probe Request frames as a + * binary data in the custom wireless event. The old way (using + * packet sniffing) didn't work when bridging. + * Format: "Manage.prob_req <frame len>" | zero padding | frame + */ +#define WPS_FRAM_TAG_SIZE 30 /* hardcoded in driver */ + int len = atoi(custom + 16); + if (len < 0 || custom + WPS_FRAM_TAG_SIZE + len > end) { + wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req event " + "length %d", len); + return; + } + madwifi_raw_receive(drv, NULL, + (u8 *) custom + WPS_FRAM_TAG_SIZE, len); +#endif /* CONFIG_WPS */ + } +} + +static void +madwifi_wireless_event_wireless(struct madwifi_driver_data *drv, + char *data, int len) +{ + struct iw_event iwe_buf, *iwe = &iwe_buf; + char *pos, *end, *custom, *buf; + + pos = data; + end = data + len; + + while (pos + IW_EV_LCP_LEN <= end) { + /* Event data may be unaligned, so make a local, aligned copy + * before processing. */ + memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); + wpa_printf(MSG_MSGDUMP, "Wireless event: cmd=0x%x len=%d", + iwe->cmd, iwe->len); + if (iwe->len <= IW_EV_LCP_LEN) + return; + + custom = pos + IW_EV_POINT_LEN; + if (drv->we_version > 18 && + (iwe->cmd == IWEVMICHAELMICFAILURE || + iwe->cmd == IWEVASSOCREQIE || + iwe->cmd == IWEVCUSTOM)) { + /* WE-19 removed the pointer from struct iw_point */ + char *dpos = (char *) &iwe_buf.u.data.length; + int dlen = dpos - (char *) &iwe_buf; + memcpy(dpos, pos + IW_EV_LCP_LEN, + sizeof(struct iw_event) - dlen); + } else { + memcpy(&iwe_buf, pos, sizeof(struct iw_event)); + custom += IW_EV_POINT_OFF; + } + + switch (iwe->cmd) { + case IWEVEXPIRED: + drv_event_disassoc(drv->hapd, + (u8 *) iwe->u.addr.sa_data); + break; + case IWEVREGISTERED: + madwifi_new_sta(drv, (u8 *) iwe->u.addr.sa_data); + break; + case IWEVASSOCREQIE: + /* Driver hack.. Use IWEVASSOCREQIE to bypass + * IWEVCUSTOM size limitations. Need to handle this + * just like IWEVCUSTOM. + */ + case IWEVCUSTOM: + if (custom + iwe->u.data.length > end) + return; + buf = malloc(iwe->u.data.length + 1); + if (buf == NULL) + return; /* XXX */ + memcpy(buf, custom, iwe->u.data.length); + buf[iwe->u.data.length] = '\0'; + madwifi_wireless_event_wireless_custom( + drv, buf, buf + iwe->u.data.length); + free(buf); + break; + } + + pos += iwe->len; + } +} + + +static void +madwifi_wireless_event_rtm_newlink(void *ctx, + struct ifinfomsg *ifi, u8 *buf, size_t len) +{ + struct madwifi_driver_data *drv = ctx; + int attrlen, rta_len; + struct rtattr *attr; + + if (ifi->ifi_index != drv->ifindex) + return; + + attrlen = len; + attr = (struct rtattr *) buf; + + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_WIRELESS) { + madwifi_wireless_event_wireless( + drv, ((char *) attr) + rta_len, + attr->rta_len - rta_len); + } + attr = RTA_NEXT(attr, attrlen); + } +} + + +static int +madwifi_get_we_version(struct madwifi_driver_data *drv) +{ + struct iw_range *range; + struct iwreq iwr; + int minlen; + size_t buflen; + + drv->we_version = 0; + + /* + * Use larger buffer than struct iw_range in order to allow the + * structure to grow in the future. + */ + buflen = sizeof(struct iw_range) + 500; + range = os_zalloc(buflen); + if (range == NULL) + return -1; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) range; + iwr.u.data.length = buflen; + + minlen = ((char *) &range->enc_capa) - (char *) range + + sizeof(range->enc_capa); + + if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) { + perror("ioctl[SIOCGIWRANGE]"); + free(range); + return -1; + } else if (iwr.u.data.length >= minlen && + range->we_version_compiled >= 18) { + wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d " + "WE(source)=%d enc_capa=0x%x", + range->we_version_compiled, + range->we_version_source, + range->enc_capa); + drv->we_version = range->we_version_compiled; + } + + free(range); + return 0; +} + + +static int +madwifi_wireless_event_init(struct madwifi_driver_data *drv) +{ + struct netlink_config *cfg; + + madwifi_get_we_version(drv); + + cfg = os_zalloc(sizeof(*cfg)); + if (cfg == NULL) + return -1; + cfg->ctx = drv; + cfg->newlink_cb = madwifi_wireless_event_rtm_newlink; + drv->netlink = netlink_init(cfg); + if (drv->netlink == NULL) { + os_free(cfg); + return -1; + } + + return 0; +} + + +static int +madwifi_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, + int encrypt, const u8 *own_addr) +{ + struct madwifi_driver_data *drv = priv; + unsigned char buf[3000]; + unsigned char *bp = buf; + struct l2_ethhdr *eth; + size_t len; + int status; + + /* + * Prepend the Ethernet header. If the caller left us + * space at the front we could just insert it but since + * we don't know we copy to a local buffer. Given the frequency + * and size of frames this probably doesn't matter. + */ + len = data_len + sizeof(struct l2_ethhdr); + if (len > sizeof(buf)) { + bp = malloc(len); + if (bp == NULL) { + printf("EAPOL frame discarded, cannot malloc temp " + "buffer of size %lu!\n", (unsigned long) len); + return -1; + } + } + eth = (struct l2_ethhdr *) bp; + memcpy(eth->h_dest, addr, ETH_ALEN); + memcpy(eth->h_source, own_addr, ETH_ALEN); + eth->h_proto = host_to_be16(ETH_P_EAPOL); + memcpy(eth+1, data, data_len); + + wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", bp, len); + + status = l2_packet_send(drv->sock_xmit, addr, ETH_P_EAPOL, bp, len); + + if (bp != buf) + free(bp); + return status; +} + +static void +handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) +{ + struct madwifi_driver_data *drv = ctx; + drv_event_eapol_rx(drv->hapd, src_addr, buf + sizeof(struct l2_ethhdr), + len - sizeof(struct l2_ethhdr)); +} + +static void * +madwifi_init(struct hostapd_data *hapd, struct wpa_init_params *params) +{ + struct madwifi_driver_data *drv; + struct ifreq ifr; + struct iwreq iwr; + char brname[IFNAMSIZ]; + + drv = os_zalloc(sizeof(struct madwifi_driver_data)); + if (drv == NULL) { + printf("Could not allocate memory for madwifi driver data\n"); + return NULL; + } + + drv->hapd = hapd; + drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->ioctl_sock < 0) { + perror("socket[PF_INET,SOCK_DGRAM]"); + goto bad; + } + memcpy(drv->iface, params->ifname, sizeof(drv->iface)); + + memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->iface, sizeof(ifr.ifr_name)); + if (ioctl(drv->ioctl_sock, SIOCGIFINDEX, &ifr) != 0) { + perror("ioctl(SIOCGIFINDEX)"); + goto bad; + } + drv->ifindex = ifr.ifr_ifindex; + + drv->sock_xmit = l2_packet_init(drv->iface, NULL, ETH_P_EAPOL, + handle_read, drv, 1); + if (drv->sock_xmit == NULL) + goto bad; + if (l2_packet_get_own_addr(drv->sock_xmit, params->own_addr)) + goto bad; + if (params->bridge[0]) { + wpa_printf(MSG_DEBUG, "Configure bridge %s for EAPOL traffic.", + params->bridge[0]); + drv->sock_recv = l2_packet_init(params->bridge[0], NULL, + ETH_P_EAPOL, handle_read, drv, + 1); + if (drv->sock_recv == NULL) + goto bad; + } else if (linux_br_get(brname, drv->iface) == 0) { + wpa_printf(MSG_DEBUG, "Interface in bridge %s; configure for " + "EAPOL receive", brname); + drv->sock_recv = l2_packet_init(brname, NULL, ETH_P_EAPOL, + handle_read, drv, 1); + if (drv->sock_recv == NULL) + goto bad; + } else + drv->sock_recv = drv->sock_xmit; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + + iwr.u.mode = IW_MODE_MASTER; + + if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) < 0) { + perror("ioctl[SIOCSIWMODE]"); + printf("Could not set interface to master mode!\n"); + goto bad; + } + + /* mark down during setup */ + linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0); + madwifi_set_privacy(drv, 0); /* default to no privacy */ + + madwifi_receive_probe_req(drv); + + if (madwifi_wireless_event_init(drv)) + goto bad; + + return drv; +bad: + if (drv->sock_recv != NULL && drv->sock_recv != drv->sock_xmit) + l2_packet_deinit(drv->sock_recv); + if (drv->sock_xmit != NULL) + l2_packet_deinit(drv->sock_xmit); + if (drv->ioctl_sock >= 0) + close(drv->ioctl_sock); + if (drv != NULL) + free(drv); + return NULL; +} + + +static void +madwifi_deinit(void *priv) +{ + struct madwifi_driver_data *drv = priv; + + netlink_deinit(drv->netlink); + (void) linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0); + if (drv->ioctl_sock >= 0) + close(drv->ioctl_sock); + if (drv->sock_recv != NULL && drv->sock_recv != drv->sock_xmit) + l2_packet_deinit(drv->sock_recv); + if (drv->sock_xmit != NULL) + l2_packet_deinit(drv->sock_xmit); + if (drv->sock_raw) + l2_packet_deinit(drv->sock_raw); + free(drv); +} + +static int +madwifi_set_ssid(void *priv, const u8 *buf, int len) +{ + struct madwifi_driver_data *drv = priv; + struct iwreq iwr; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.essid.flags = 1; /* SSID active */ + iwr.u.essid.pointer = (caddr_t) buf; + iwr.u.essid.length = len + 1; + + if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { + perror("ioctl[SIOCSIWESSID]"); + printf("len=%d\n", len); + return -1; + } + return 0; +} + +static int +madwifi_get_ssid(void *priv, u8 *buf, int len) +{ + struct madwifi_driver_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.essid.pointer = (caddr_t) buf; + iwr.u.essid.length = len; + + if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) { + perror("ioctl[SIOCGIWESSID]"); + ret = -1; + } else + ret = iwr.u.essid.length; + + return ret; +} + +static int +madwifi_set_countermeasures(void *priv, int enabled) +{ + struct madwifi_driver_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + return set80211param(drv, IEEE80211_PARAM_COUNTERMEASURES, enabled); +} + +static int +madwifi_commit(void *priv) +{ + struct madwifi_driver_data *drv = priv; + return linux_set_iface_flags(drv->ioctl_sock, drv->iface, 1); +} + +const struct wpa_driver_ops wpa_driver_atheros_ops = { + .name = "atheros", + .hapd_init = madwifi_init, + .hapd_deinit = madwifi_deinit, + .set_ieee8021x = madwifi_set_ieee8021x, + .set_privacy = madwifi_set_privacy, + .set_key = madwifi_set_key, + .get_seqnum = madwifi_get_seqnum, + .flush = madwifi_flush, + .set_generic_elem = madwifi_set_opt_ie, + .sta_set_flags = madwifi_sta_set_flags, + .read_sta_data = madwifi_read_sta_driver_data, + .hapd_send_eapol = madwifi_send_eapol, + .sta_disassoc = madwifi_sta_disassoc, + .sta_deauth = madwifi_sta_deauth, + .hapd_set_ssid = madwifi_set_ssid, + .hapd_get_ssid = madwifi_get_ssid, + .set_countermeasures = madwifi_set_countermeasures, + .sta_clear_stats = madwifi_sta_clear_stats, + .commit = madwifi_commit, + .set_ap_wps_ie = madwifi_set_ap_wps_ie, +}; diff --git a/src/drivers/driver_atmel.c b/src/drivers/driver_atmel.c index 0a7a66ddb5f2e..cbec6c38d78d4 100644 --- a/src/drivers/driver_atmel.c +++ b/src/drivers/driver_atmel.c @@ -188,9 +188,10 @@ static int wpa_driver_atmel_set_wpa(void *priv, int enabled) } -static int wpa_driver_atmel_set_key(void *priv, wpa_alg alg, - const u8 *addr, int key_idx, - int set_tx, const u8 *seq, size_t seq_len, +static int wpa_driver_atmel_set_key(const char *ifname, void *priv, + enum wpa_alg alg, const u8 *addr, + int key_idx, int set_tx, + const u8 *seq, size_t seq_len, const u8 *key, size_t key_len) { struct wpa_driver_atmel_data *drv = priv; @@ -273,16 +274,6 @@ static int wpa_driver_atmel_set_countermeasures(void *priv, } -static int wpa_driver_atmel_set_drop_unencrypted(void *priv, - int enabled) -{ - /* FIX */ - printf("wpa_driver_atmel_set_drop_unencrypted - not yet " - "implemented\n"); - return 0; -} - - static int wpa_driver_atmel_mlme(void *priv, const u8 *addr, int cmd, int reason_code) { @@ -347,7 +338,7 @@ static int wpa_driver_atmel_disassociate(void *priv, const u8 *addr, #if 0 /* Atmel driver uses specific values for each cipher suite */ -static int convertSuiteToDriver(wpa_cipher suite) +static int convertSuiteToDriver(enum wpa_cipher suite) { u8 suite_type; @@ -430,10 +421,11 @@ static int wpa_driver_atmel_get_ssid(void *priv, u8 *ssid) } -static int wpa_driver_atmel_scan(void *priv, const u8 *ssid, size_t ssid_len) +static int wpa_driver_atmel_scan(void *priv, + struct wpa_driver_scan_params *params) { struct wpa_driver_atmel_data *drv = priv; - return wpa_driver_wext_scan(drv->wext, ssid, ssid_len); + return wpa_driver_wext_scan(drv->wext, params); } @@ -473,6 +465,8 @@ static void * wpa_driver_atmel_init(void *ctx, const char *ifname) return NULL; } + wpa_driver_atmel_set_wpa(drv, 1); + return drv; } @@ -480,6 +474,7 @@ static void * wpa_driver_atmel_init(void *ctx, const char *ifname) static void wpa_driver_atmel_deinit(void *priv) { struct wpa_driver_atmel_data *drv = priv; + wpa_driver_atmel_set_wpa(drv, 0); wpa_driver_wext_deinit(drv->wext); close(drv->sock); os_free(drv); @@ -491,13 +486,11 @@ const struct wpa_driver_ops wpa_driver_atmel_ops = { .desc = "ATMEL AT76C5XXx (USB, PCMCIA)", .get_bssid = wpa_driver_atmel_get_bssid, .get_ssid = wpa_driver_atmel_get_ssid, - .set_wpa = wpa_driver_atmel_set_wpa, .set_key = wpa_driver_atmel_set_key, .init = wpa_driver_atmel_init, .deinit = wpa_driver_atmel_deinit, .set_countermeasures = wpa_driver_atmel_set_countermeasures, - .set_drop_unencrypted = wpa_driver_atmel_set_drop_unencrypted, - .scan = wpa_driver_atmel_scan, + .scan2 = wpa_driver_atmel_scan, .get_scan_results2 = wpa_driver_atmel_get_scan_results, .deauthenticate = wpa_driver_atmel_deauthenticate, .disassociate = wpa_driver_atmel_disassociate, diff --git a/src/drivers/driver_broadcom.c b/src/drivers/driver_broadcom.c index 3600dae110468..cb88543c2c36a 100644 --- a/src/drivers/driver_broadcom.c +++ b/src/drivers/driver_broadcom.c @@ -162,7 +162,8 @@ static int wpa_driver_broadcom_set_wpa(void *priv, int enable) return 0; } -static int wpa_driver_broadcom_set_key(void *priv, wpa_alg alg, +static int wpa_driver_broadcom_set_key(const char *ifname, void *priv, + enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len) @@ -232,7 +233,8 @@ static void wpa_driver_broadcom_event_receive(int sock, void *ctx, int left; wl_wpa_header_t *wwh; union wpa_event_data data; - + u8 *resp_ies = NULL; + if ((left = recv(sock, buf, sizeof buf, 0)) < 0) return; @@ -256,21 +258,16 @@ static void wpa_driver_broadcom_event_receive(int sock, void *ctx, wpa_printf(MSG_DEBUG, "BROADCOM: ASSOC MESSAGE (left: %d)", left); if (left > 0) { - data.assoc_info.resp_ies = os_malloc(left); - if (data.assoc_info.resp_ies == NULL) + resp_ies = os_malloc(left); + if (resp_ies == NULL) return; - os_memcpy(data.assoc_info.resp_ies, - buf + WL_WPA_HEADER_LEN, left); + os_memcpy(resp_ies, buf + WL_WPA_HEADER_LEN, left); + data.assoc_info.resp_ies = resp_ies; data.assoc_info.resp_ies_len = left; - wpa_hexdump(MSG_MSGDUMP, "BROADCOM: copying %d bytes " - "into resp_ies", - data.assoc_info.resp_ies, left); } - /* data.assoc_info.req_ies = NULL; */ - /* data.assoc_info.req_ies_len = 0; */ - wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data); - wpa_supplicant_event(ctx, EVENT_ASSOC, NULL); + wpa_supplicant_event(ctx, EVENT_ASSOC, &data); + os_free(resp_ies); break; case WLC_DISASSOC_MSG: wpa_printf(MSG_DEBUG, "BROADCOM: DISASSOC MESSAGE"); @@ -291,7 +288,6 @@ static void wpa_driver_broadcom_event_receive(int sock, void *ctx, wwh->type); break; } - os_free(data.assoc_info.resp_ies); } static void * wpa_driver_broadcom_init(void *ctx, const char *ifname) @@ -348,6 +344,7 @@ static void * wpa_driver_broadcom_init(void *ctx, const char *ifname) eloop_register_read_sock(s, wpa_driver_broadcom_event_receive, ctx, NULL); drv->event_sock = s; + wpa_driver_broadcom_set_wpa(drv, 1); return drv; } @@ -355,6 +352,7 @@ static void * wpa_driver_broadcom_init(void *ctx, const char *ifname) static void wpa_driver_broadcom_deinit(void *priv) { struct wpa_driver_broadcom_data *drv = priv; + wpa_driver_broadcom_set_wpa(drv, 0); eloop_cancel_timeout(wpa_driver_broadcom_scan_timeout, drv, drv->ctx); eloop_unregister_read_sock(drv->event_sock); close(drv->event_sock); @@ -379,12 +377,12 @@ static int wpa_driver_broadcom_set_drop_unencrypted(void *priv, int enabled) { struct wpa_driver_broadcom_data *drv = priv; /* SET_EAP_RESTRICT, SET_WEP_RESTRICT */ - int restrict = (enabled ? 1 : 0); + int _restrict = (enabled ? 1 : 0); if (broadcom_ioctl(drv, WLC_SET_WEP_RESTRICT, - &restrict, sizeof(restrict)) < 0 || + &_restrict, sizeof(_restrict)) < 0 || broadcom_ioctl(drv, WLC_SET_EAP_RESTRICT, - &restrict, sizeof(restrict)) < 0) + &_restrict, sizeof(_restrict)) < 0) return -1; return 0; @@ -397,11 +395,13 @@ static void wpa_driver_broadcom_scan_timeout(void *eloop_ctx, wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); } -static int wpa_driver_broadcom_scan(void *priv, const u8 *ssid, - size_t ssid_len) +static int wpa_driver_broadcom_scan(void *priv, + struct wpa_driver_scan_params *params) { struct wpa_driver_broadcom_data *drv = priv; wlc_ssid_t wst = { 0, "" }; + const u8 *ssid = params->ssids[0].ssid; + size_t ssid_len = params->ssids[0].ssid_len; if (ssid && ssid_len > 0 && ssid_len <= sizeof(wst.SSID)) { wst.SSID_len = ssid_len; @@ -431,20 +431,19 @@ struct bss_ie_hdr { /* u16 version; */ } __attribute__ ((packed)); -static int -wpa_driver_broadcom_get_scan_results(void *priv, - struct wpa_scan_result *results, - size_t max_size) +static struct wpa_scan_results * +wpa_driver_broadcom_get_scan_results(void *priv) { struct wpa_driver_broadcom_data *drv = priv; char *buf; wl_scan_results_t *wsr; wl_bss_info_t *wbi; size_t ap_num; + struct wpa_scan_results *res; buf = os_malloc(WLC_IOCTL_MAXLEN); if (buf == NULL) - return -1; + return NULL; wsr = (wl_scan_results_t *) buf; @@ -454,40 +453,34 @@ wpa_driver_broadcom_get_scan_results(void *priv, if (broadcom_ioctl(drv, WLC_SCAN_RESULTS, buf, WLC_IOCTL_MAXLEN) < 0) { os_free(buf); - return -1; + return NULL; } - os_memset(results, 0, max_size * sizeof(struct wpa_scan_result)); + res = os_zalloc(sizeof(*res)); + if (res == NULL) { + os_free(buf); + return NULL; + } + + res->res = os_zalloc(wsr->count * sizeof(struct wpa_scan_res *)); + if (res->res == NULL) { + os_free(res); + os_free(buf); + return NULL; + } for (ap_num = 0, wbi = wsr->bss_info; ap_num < wsr->count; ++ap_num) { - int left; - struct bss_ie_hdr *ie; - - os_memcpy(results[ap_num].bssid, &wbi->BSSID, ETH_ALEN); - os_memcpy(results[ap_num].ssid, wbi->SSID, wbi->SSID_len); - results[ap_num].ssid_len = wbi->SSID_len; - results[ap_num].freq = frequency_list[wbi->channel - 1]; - /* get ie's */ - wpa_hexdump(MSG_MSGDUMP, "BROADCOM: AP IEs", - (u8 *) wbi + sizeof(*wbi), wbi->ie_length); - ie = (struct bss_ie_hdr *) ((u8 *) wbi + sizeof(*wbi)); - for (left = wbi->ie_length; left > 0; - left -= (ie->len + 2), ie = (struct bss_ie_hdr *) - ((u8 *) ie + 2 + ie->len)) { - wpa_printf(MSG_MSGDUMP, "BROADCOM: IE: id:%x, len:%d", - ie->elem_id, ie->len); - if (ie->len >= 3) - wpa_printf(MSG_MSGDUMP, - "BROADCOM: oui:%02x%02x%02x", - ie->oui[0], ie->oui[1], ie->oui[2]); - if (ie->elem_id != 0xdd || - ie->len < 6 || - os_memcmp(ie->oui, WPA_OUI, 3) != 0) - continue; - os_memcpy(results[ap_num].wpa_ie, ie, ie->len + 2); - results[ap_num].wpa_ie_len = ie->len + 2; + struct wpa_scan_res *r; + r = os_malloc(sizeof(*r) + wbi->ie_length); + if (r == NULL) break; - } + res->res[res->num++] = r; + + os_memcpy(r->bssid, &wbi->BSSID, ETH_ALEN); + r->freq = frequency_list[wbi->channel - 1]; + /* get ie's */ + os_memcpy(r + 1, wbi + 1, wbi->ie_length); + r->ie_len = wbi->ie_length; wbi = (wl_bss_info_t *) ((u8 *) wbi + wbi->length); } @@ -497,8 +490,8 @@ wpa_driver_broadcom_get_scan_results(void *priv, wsr->buflen, (unsigned long) ap_num); os_free(buf); - return ap_num; -} + return res; + } static int wpa_driver_broadcom_deauthenticate(void *priv, const u8 *addr, int reason_code) @@ -516,7 +509,7 @@ static int wpa_driver_broadcom_disassociate(void *priv, const u8 *addr, int reason_code) { struct wpa_driver_broadcom_data *drv = priv; - return broadcom_ioctl(drv, WLC_DISASSOC, 0, 0); + return broadcom_ioctl(drv, WLC_DISASSOC, NULL, 0); } static int @@ -530,7 +523,11 @@ wpa_driver_broadcom_associate(void *priv, int wsec = 4; int dummy; int wpa_auth; - + int ret; + + ret = wpa_driver_broadcom_set_drop_unencrypted( + drv, params->drop_unencrypted); + s.SSID_len = params->ssid_len; os_memcpy(s.SSID, params->ssid, params->ssid_len); @@ -582,7 +579,7 @@ wpa_driver_broadcom_associate(void *priv, broadcom_ioctl(drv, WLC_SET_SSID, &s, sizeof(s)) < 0) return -1; - return 0; + return ret; } const struct wpa_driver_ops wpa_driver_broadcom_ops = { @@ -590,14 +587,12 @@ const struct wpa_driver_ops wpa_driver_broadcom_ops = { .desc = "Broadcom wl.o driver", .get_bssid = wpa_driver_broadcom_get_bssid, .get_ssid = wpa_driver_broadcom_get_ssid, - .set_wpa = wpa_driver_broadcom_set_wpa, .set_key = wpa_driver_broadcom_set_key, .init = wpa_driver_broadcom_init, .deinit = wpa_driver_broadcom_deinit, .set_countermeasures = wpa_driver_broadcom_set_countermeasures, - .set_drop_unencrypted = wpa_driver_broadcom_set_drop_unencrypted, - .scan = wpa_driver_broadcom_scan, - .get_scan_results = wpa_driver_broadcom_get_scan_results, + .scan2 = wpa_driver_broadcom_scan, + .get_scan_results2 = wpa_driver_broadcom_get_scan_results, .deauthenticate = wpa_driver_broadcom_deauthenticate, .disassociate = wpa_driver_broadcom_disassociate, .associate = wpa_driver_broadcom_associate, diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c index 218d913869fd9..99de6c7ff48e4 100644 --- a/src/drivers/driver_bsd.c +++ b/src/drivers/driver_bsd.c @@ -1,6 +1,7 @@ /* * WPA Supplicant - driver interaction with BSD net80211 layer * Copyright (c) 2004, Sam Leffler <sam@errno.com> + * Copyright (c) 2004, 2Wire, Inc * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -18,297 +19,329 @@ #include "common.h" #include "driver.h" #include "eloop.h" -#include "ieee802_11_defs.h" +#include "common/ieee802_11_defs.h" #include <net/if.h> +#include <net/if_media.h> #ifdef __NetBSD__ #include <net/if_ether.h> -#define COMPAT_FREEBSD_NET80211 #else #include <net/ethernet.h> #endif +#include <net/route.h> +#ifdef __DragonFly__ +#include <netproto/802_11/ieee80211_ioctl.h> +#include <netproto/802_11/ieee80211_dragonfly.h> +#else /* __DragonFly__ */ +#ifdef __GLIBC__ +#include <netinet/ether.h> +#endif /* __GLIBC__ */ #include <net80211/ieee80211.h> -#include <net80211/ieee80211_crypto.h> #include <net80211/ieee80211_ioctl.h> +#include <net80211/ieee80211_crypto.h> +#endif /* __DragonFly__ || __GLIBC__ */ +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +#include <net80211/ieee80211_freebsd.h> +#endif +#if __NetBSD__ +#include <net80211/ieee80211_netbsd.h> +#endif + +#include "l2_packet/l2_packet.h" + +struct bsd_driver_data { + struct hostapd_data *hapd; /* back pointer */ -struct wpa_driver_bsd_data { int sock; /* open socket for 802.11 ioctls */ + struct l2_packet_data *sock_xmit;/* raw packet xmit socket */ int route; /* routing socket for events */ char ifname[IFNAMSIZ+1]; /* interface name */ unsigned int ifindex; /* interface index */ void *ctx; - int prev_roaming; /* roaming state to restore on deinit */ - int prev_privacy; /* privacy state to restore on deinit */ - int prev_wpa; /* wpa state to restore on deinit */ + struct wpa_driver_capa capa; /* driver capability */ + int is_ap; /* Access point mode */ + int prev_roaming; /* roaming state to restore on deinit */ + int prev_privacy; /* privacy state to restore on deinit */ + int prev_wpa; /* wpa state to restore on deinit */ }; +/* Generic functions for hostapd and wpa_supplicant */ + static int -set80211var(struct wpa_driver_bsd_data *drv, int op, const void *arg, int arg_len) +bsd_set80211(void *priv, int op, int val, const void *arg, int arg_len) { + struct bsd_driver_data *drv = priv; struct ieee80211req ireq; os_memset(&ireq, 0, sizeof(ireq)); - os_strlcpy(ireq.i_name, drv->ifname, IFNAMSIZ); + os_strlcpy(ireq.i_name, drv->ifname, sizeof(ireq.i_name)); ireq.i_type = op; - ireq.i_len = arg_len; + ireq.i_val = val; ireq.i_data = (void *) arg; + ireq.i_len = arg_len; if (ioctl(drv->sock, SIOCS80211, &ireq) < 0) { - fprintf(stderr, "ioctl[SIOCS80211, op %u, len %u]: %s\n", - op, arg_len, strerror(errno)); + wpa_printf(MSG_ERROR, "ioctl[SIOCS80211, op=%u, val=%u, " + "arg_len=%u]: %s", op, val, arg_len, + strerror(errno)); return -1; } return 0; } static int -get80211var(struct wpa_driver_bsd_data *drv, int op, void *arg, int arg_len) +bsd_get80211(void *priv, struct ieee80211req *ireq, int op, void *arg, + int arg_len) { - struct ieee80211req ireq; + struct bsd_driver_data *drv = priv; - os_memset(&ireq, 0, sizeof(ireq)); - os_strlcpy(ireq.i_name, drv->ifname, IFNAMSIZ); - ireq.i_type = op; - ireq.i_len = arg_len; - ireq.i_data = arg; + os_memset(ireq, 0, sizeof(*ireq)); + os_strlcpy(ireq->i_name, drv->ifname, sizeof(ireq->i_name)); + ireq->i_type = op; + ireq->i_len = arg_len; + ireq->i_data = arg; - if (ioctl(drv->sock, SIOCG80211, &ireq) < 0) { - fprintf(stderr, "ioctl[SIOCG80211, op %u, len %u]: %s\n", - op, arg_len, strerror(errno)); + if (ioctl(drv->sock, SIOCG80211, ireq) < 0) { + wpa_printf(MSG_ERROR, "ioctl[SIOCS80211, op=%u, " + "arg_len=%u]: %s", op, arg_len, strerror(errno)); return -1; } - return ireq.i_len; + return 0; } static int -set80211param(struct wpa_driver_bsd_data *drv, int op, int arg) +get80211var(struct bsd_driver_data *drv, int op, void *arg, int arg_len) { struct ieee80211req ireq; - os_memset(&ireq, 0, sizeof(ireq)); - os_strlcpy(ireq.i_name, drv->ifname, IFNAMSIZ); - ireq.i_type = op; - ireq.i_val = arg; - - if (ioctl(drv->sock, SIOCS80211, &ireq) < 0) { - fprintf(stderr, "ioctl[SIOCS80211, op %u, arg 0x%x]: %s\n", - op, arg, strerror(errno)); + if (bsd_get80211(drv, &ireq, op, arg, arg_len) < 0) return -1; - } - return 0; + return ireq.i_len; } static int -get80211param(struct wpa_driver_bsd_data *drv, int op) +set80211var(struct bsd_driver_data *drv, int op, const void *arg, int arg_len) { - struct ieee80211req ireq; - - os_memset(&ireq, 0, sizeof(ireq)); - os_strlcpy(ireq.i_name, drv->ifname, IFNAMSIZ); - ireq.i_type = op; + return bsd_set80211(drv, op, 0, arg, arg_len); +} - if (ioctl(drv->sock, SIOCG80211, &ireq) < 0) { - fprintf(stderr, "ioctl[SIOCG80211, op %u]: %s\n", - op, strerror(errno)); - return -1; - } - return ireq.i_val; +static int +set80211param(struct bsd_driver_data *drv, int op, int arg) +{ + return bsd_set80211(drv, op, arg, NULL, 0); } static int -getifflags(struct wpa_driver_bsd_data *drv, int *flags) +bsd_get_ssid(void *priv, u8 *ssid, int len) { + struct bsd_driver_data *drv = priv; +#ifdef SIOCG80211NWID + struct ieee80211_nwid nwid; struct ifreq ifr; os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); - if (ioctl(drv->sock, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) { - perror("SIOCGIFFLAGS"); - return errno; - } - *flags = ifr.ifr_flags & 0xffff; - return 0; + ifr.ifr_data = (void *)&nwid; + if (ioctl(drv->sock, SIOCG80211NWID, &ifr) < 0 || + nwid.i_len > IEEE80211_NWID_LEN) + return -1; + os_memcpy(ssid, nwid.i_nwid, nwid.i_len); + return nwid.i_len; +#else + return get80211var(drv, IEEE80211_IOC_SSID, ssid, IEEE80211_NWID_LEN); +#endif } static int -setifflags(struct wpa_driver_bsd_data *drv, int flags) +bsd_set_ssid(void *priv, const u8 *ssid, int ssid_len) { + struct bsd_driver_data *drv = priv; +#ifdef SIOCS80211NWID + struct ieee80211_nwid nwid; struct ifreq ifr; + os_memcpy(nwid.i_nwid, ssid, ssid_len); + nwid.i_len = ssid_len; os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); - ifr.ifr_flags = flags & 0xffff; - if (ioctl(drv->sock, SIOCSIFFLAGS, (caddr_t)&ifr) < 0) { - perror("SIOCSIFFLAGS"); - return errno; - } - return 0; + ifr.ifr_data = (void *)&nwid; + return ioctl(drv->sock, SIOCS80211NWID, &ifr); +#else + return set80211var(drv, IEEE80211_IOC_SSID, ssid, ssid_len); +#endif } static int -wpa_driver_bsd_get_bssid(void *priv, u8 *bssid) +bsd_get_if_media(void *priv) { - struct wpa_driver_bsd_data *drv = priv; + struct bsd_driver_data *drv = priv; + struct ifmediareq ifmr; - return get80211var(drv, IEEE80211_IOC_BSSID, - bssid, IEEE80211_ADDR_LEN) < 0 ? -1 : 0; -} + os_memset(&ifmr, 0, sizeof(ifmr)); + os_strlcpy(ifmr.ifm_name, drv->ifname, sizeof(ifmr.ifm_name)); -#if 0 -static int -wpa_driver_bsd_set_bssid(void *priv, const char *bssid) -{ - struct wpa_driver_bsd_data *drv = priv; + if (ioctl(drv->sock, SIOCGIFMEDIA, &ifmr) < 0) { + wpa_printf(MSG_ERROR, "%s: SIOCGIFMEDIA %s", __func__, + strerror(errno)); + return -1; + } - return set80211var(drv, IEEE80211_IOC_BSSID, - bssid, IEEE80211_ADDR_LEN); + return ifmr.ifm_current; } -#endif static int -wpa_driver_bsd_get_ssid(void *priv, u8 *ssid) +bsd_set_if_media(void *priv, int media) { - struct wpa_driver_bsd_data *drv = priv; + struct bsd_driver_data *drv = priv; + struct ifreq ifr; - return get80211var(drv, IEEE80211_IOC_SSID, - ssid, IEEE80211_NWID_LEN); -} + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); + ifr.ifr_media = media; -static int -wpa_driver_bsd_set_ssid(void *priv, const u8 *ssid, - size_t ssid_len) -{ - struct wpa_driver_bsd_data *drv = priv; + if (ioctl(drv->sock, SIOCSIFMEDIA, &ifr) < 0) { + wpa_printf(MSG_ERROR, "%s: SIOCSIFMEDIA %s", __func__, + strerror(errno)); + return -1; + } - return set80211var(drv, IEEE80211_IOC_SSID, ssid, ssid_len); + return 0; } static int -wpa_driver_bsd_set_wpa_ie(struct wpa_driver_bsd_data *drv, - const u8 *wpa_ie, size_t wpa_ie_len) +bsd_set_mediaopt(void *priv, uint32_t mask, uint32_t mode) { - return set80211var(drv, IEEE80211_IOC_OPTIE, wpa_ie, wpa_ie_len); + int media = bsd_get_if_media(priv); + + if (media < 0) + return -1; + media &= ~mask; + media |= mode; + if (bsd_set_if_media(priv, media) < 0) + return -1; + return 0; } static int -wpa_driver_bsd_set_wpa_internal(void *priv, int wpa, int privacy) +bsd_del_key(void *priv, const u8 *addr, int key_idx) { - struct wpa_driver_bsd_data *drv = priv; - int ret = 0; - - wpa_printf(MSG_DEBUG, "%s: wpa=%d privacy=%d", - __FUNCTION__, wpa, privacy); + struct ieee80211req_del_key wk; - if (!wpa && wpa_driver_bsd_set_wpa_ie(drv, NULL, 0) < 0) - ret = -1; - if (set80211param(drv, IEEE80211_IOC_PRIVACY, privacy) < 0) - ret = -1; - if (set80211param(drv, IEEE80211_IOC_WPA, wpa) < 0) - ret = -1; + os_memset(&wk, 0, sizeof(wk)); + if (addr == NULL) { + wpa_printf(MSG_DEBUG, "%s: key_idx=%d", __func__, key_idx); + wk.idk_keyix = key_idx; + } else { + wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR, __func__, + MAC2STR(addr)); + os_memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN); + wk.idk_keyix = (u_int8_t) IEEE80211_KEYIX_NONE; /* XXX */ + } - return ret; + return set80211var(priv, IEEE80211_IOC_DELKEY, &wk, sizeof(wk)); } static int -wpa_driver_bsd_set_wpa(void *priv, int enabled) +bsd_send_mlme_param(void *priv, const u8 op, const u16 reason, const u8 *addr) { - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + struct ieee80211req_mlme mlme; - return wpa_driver_bsd_set_wpa_internal(priv, enabled ? 3 : 0, enabled); + os_memset(&mlme, 0, sizeof(mlme)); + mlme.im_op = op; + mlme.im_reason = reason; + os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + return set80211var(priv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme)); } static int -wpa_driver_bsd_del_key(struct wpa_driver_bsd_data *drv, int key_idx, - const unsigned char *addr) +bsd_ctrl_iface(void *priv, int enable) { - struct ieee80211req_del_key wk; + struct bsd_driver_data *drv = priv; + struct ifreq ifr; - os_memset(&wk, 0, sizeof(wk)); - if (addr != NULL && - bcmp(addr, "\xff\xff\xff\xff\xff\xff", IEEE80211_ADDR_LEN) != 0) { - struct ether_addr ea; + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); - os_memcpy(&ea, addr, IEEE80211_ADDR_LEN); - wpa_printf(MSG_DEBUG, "%s: addr=%s keyidx=%d", - __func__, ether_ntoa(&ea), key_idx); - os_memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN); - wk.idk_keyix = (uint8_t) IEEE80211_KEYIX_NONE; - } else { - wpa_printf(MSG_DEBUG, "%s: keyidx=%d", __func__, key_idx); - wk.idk_keyix = key_idx; + if (ioctl(drv->sock, SIOCGIFFLAGS, &ifr) < 0) { + perror("ioctl[SIOCGIFFLAGS]"); + return -1; + } + + if (enable) + ifr.ifr_flags |= IFF_UP; + else + ifr.ifr_flags &= ~IFF_UP; + + if (ioctl(drv->sock, SIOCSIFFLAGS, &ifr) < 0) { + perror("ioctl[SIOCSIFFLAGS]"); + return -1; } - return set80211var(drv, IEEE80211_IOC_DELKEY, &wk, sizeof(wk)); + + return 0; } static int -wpa_driver_bsd_set_key(void *priv, wpa_alg alg, - const unsigned char *addr, int key_idx, int set_tx, - const u8 *seq, size_t seq_len, - const u8 *key, size_t key_len) +bsd_set_key(const char *ifname, void *priv, enum wpa_alg alg, + const unsigned char *addr, int key_idx, int set_tx, const u8 *seq, + size_t seq_len, const u8 *key, size_t key_len) { - struct wpa_driver_bsd_data *drv = priv; struct ieee80211req_key wk; - struct ether_addr ea; - char *alg_name; - u_int8_t cipher; - if (alg == WPA_ALG_NONE) - return wpa_driver_bsd_del_key(drv, key_idx, addr); + wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%p key_idx=%d set_tx=%d " + "seq_len=%zu key_len=%zu", __func__, alg, addr, key_idx, + set_tx, seq_len, key_len); + + if (alg == WPA_ALG_NONE) { +#ifndef HOSTAPD + if (addr == NULL || + os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", + IEEE80211_ADDR_LEN) == 0) + return bsd_del_key(priv, NULL, key_idx); + else +#endif /* HOSTAPD */ + return bsd_del_key(priv, addr, key_idx); + } + os_memset(&wk, 0, sizeof(wk)); switch (alg) { case WPA_ALG_WEP: - alg_name = "WEP"; - cipher = IEEE80211_CIPHER_WEP; + wk.ik_type = IEEE80211_CIPHER_WEP; break; case WPA_ALG_TKIP: - alg_name = "TKIP"; - cipher = IEEE80211_CIPHER_TKIP; + wk.ik_type = IEEE80211_CIPHER_TKIP; break; case WPA_ALG_CCMP: - alg_name = "CCMP"; - cipher = IEEE80211_CIPHER_AES_CCM; + wk.ik_type = IEEE80211_CIPHER_AES_CCM; break; default: - wpa_printf(MSG_DEBUG, "%s: unknown/unsupported algorithm %d", - __func__, alg); + wpa_printf(MSG_ERROR, "%s: unknown alg=%d", __func__, alg); return -1; } - os_memcpy(&ea, addr, IEEE80211_ADDR_LEN); - wpa_printf(MSG_DEBUG, - "%s: alg=%s addr=%s key_idx=%d set_tx=%d seq_len=%zu key_len=%zu", - __func__, alg_name, ether_ntoa(&ea), key_idx, set_tx, - seq_len, key_len); - - if (seq_len > sizeof(u_int64_t)) { - wpa_printf(MSG_DEBUG, "%s: seq_len %zu too big", - __func__, seq_len); - return -2; - } - if (key_len > sizeof(wk.ik_keydata)) { - wpa_printf(MSG_DEBUG, "%s: key length %zu too big", - __func__, key_len); - return -3; - } - - os_memset(&wk, 0, sizeof(wk)); - wk.ik_type = cipher; wk.ik_flags = IEEE80211_KEY_RECV; if (set_tx) wk.ik_flags |= IEEE80211_KEY_XMIT; - os_memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); - /* - * Deduce whether group/global or unicast key by checking - * the address (yech). Note also that we can only mark global - * keys default; doing this for a unicast key is an error. - */ - if (bcmp(addr, "\xff\xff\xff\xff\xff\xff", IEEE80211_ADDR_LEN) == 0) { - wk.ik_flags |= IEEE80211_KEY_GROUP; + + if (addr == NULL) { + os_memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); wk.ik_keyix = key_idx; } else { - wk.ik_keyix = (key_idx == 0 ? IEEE80211_KEYIX_NONE : key_idx); + os_memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); + /* + * Deduce whether group/global or unicast key by checking + * the address (yech). Note also that we can only mark global + * keys default; doing this for a unicast key is an error. + */ + if (os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", + IEEE80211_ADDR_LEN) == 0) { + wk.ik_flags |= IEEE80211_KEY_GROUP; + wk.ik_keyix = key_idx; + } else { + wk.ik_keyix = key_idx == 0 ? IEEE80211_KEYIX_NONE : + key_idx; + } } if (wk.ik_keyix != IEEE80211_KEYIX_NONE && set_tx) wk.ik_flags |= IEEE80211_KEY_DEFAULT; @@ -316,73 +349,689 @@ wpa_driver_bsd_set_key(void *priv, wpa_alg alg, os_memcpy(&wk.ik_keyrsc, seq, seq_len); os_memcpy(wk.ik_keydata, key, key_len); - return set80211var(drv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk)); + return set80211var(priv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk)); } static int -wpa_driver_bsd_set_countermeasures(void *priv, int enabled) +bsd_configure_wpa(void *priv, struct wpa_bss_params *params) +{ +#ifndef IEEE80211_IOC_APPIE + static const char *ciphernames[] = + { "WEP", "TKIP", "AES-OCB", "AES-CCM", "CKIP", "NONE" }; + int v; + + switch (params->wpa_group) { + case WPA_CIPHER_CCMP: + v = IEEE80211_CIPHER_AES_CCM; + break; + case WPA_CIPHER_TKIP: + v = IEEE80211_CIPHER_TKIP; + break; + case WPA_CIPHER_WEP104: + v = IEEE80211_CIPHER_WEP; + break; + case WPA_CIPHER_WEP40: + v = IEEE80211_CIPHER_WEP; + break; + case WPA_CIPHER_NONE: + v = IEEE80211_CIPHER_NONE; + break; + default: + printf("Unknown group key cipher %u\n", + params->wpa_group); + return -1; + } + wpa_printf(MSG_DEBUG, "%s: group key cipher=%s (%u)", + __func__, ciphernames[v], v); + if (set80211param(priv, IEEE80211_IOC_MCASTCIPHER, v)) { + printf("Unable to set group key cipher to %u (%s)\n", + v, ciphernames[v]); + return -1; + } + if (v == IEEE80211_CIPHER_WEP) { + /* key length is done only for specific ciphers */ + v = (params->wpa_group == WPA_CIPHER_WEP104 ? 13 : 5); + if (set80211param(priv, IEEE80211_IOC_MCASTKEYLEN, v)) { + printf("Unable to set group key length to %u\n", v); + return -1; + } + } + + v = 0; + if (params->wpa_pairwise & WPA_CIPHER_CCMP) + v |= 1<<IEEE80211_CIPHER_AES_CCM; + if (params->wpa_pairwise & WPA_CIPHER_TKIP) + v |= 1<<IEEE80211_CIPHER_TKIP; + if (params->wpa_pairwise & WPA_CIPHER_NONE) + v |= 1<<IEEE80211_CIPHER_NONE; + wpa_printf(MSG_DEBUG, "%s: pairwise key ciphers=0x%x", __func__, v); + if (set80211param(priv, IEEE80211_IOC_UCASTCIPHERS, v)) { + printf("Unable to set pairwise key ciphers to 0x%x\n", v); + return -1; + } + + wpa_printf(MSG_DEBUG, "%s: key management algorithms=0x%x", + __func__, params->wpa_key_mgmt); + if (set80211param(priv, IEEE80211_IOC_KEYMGTALGS, + params->wpa_key_mgmt)) { + printf("Unable to set key management algorithms to 0x%x\n", + params->wpa_key_mgmt); + return -1; + } + + v = 0; + if (params->rsn_preauth) + v |= BIT(0); + wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x", + __func__, params->rsn_preauth); + if (set80211param(priv, IEEE80211_IOC_RSNCAPS, v)) { + printf("Unable to set RSN capabilities to 0x%x\n", v); + return -1; + } +#endif /* IEEE80211_IOC_APPIE */ + + wpa_printf(MSG_DEBUG, "%s: enable WPA= 0x%x", __func__, params->wpa); + if (set80211param(priv, IEEE80211_IOC_WPA, params->wpa)) { + printf("Unable to set WPA to %u\n", params->wpa); + return -1; + } + return 0; +} + +static int +bsd_set_ieee8021x(void *priv, struct wpa_bss_params *params) +{ + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, params->enabled); + + if (!params->enabled) { + /* XXX restore state */ + return set80211param(priv, IEEE80211_IOC_AUTHMODE, + IEEE80211_AUTH_AUTO); + } + if (!params->wpa && !params->ieee802_1x) { + wpa_printf(MSG_ERROR, "%s: No 802.1X or WPA enabled", + __func__); + return -1; + } + if (params->wpa && bsd_configure_wpa(priv, params) != 0) { + wpa_printf(MSG_ERROR, "%s: Failed to configure WPA state", + __func__); + return -1; + } + if (set80211param(priv, IEEE80211_IOC_AUTHMODE, + (params->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) { + wpa_printf(MSG_ERROR, "%s: Failed to enable WPA/802.1X", + __func__); + return -1; + } + return bsd_ctrl_iface(priv, 1); +} + +static int +bsd_set_sta_authorized(void *priv, const u8 *addr, + int total_flags, int flags_or, int flags_and) +{ + int authorized = -1; + + /* For now, only support setting Authorized flag */ + if (flags_or & WPA_STA_AUTHORIZED) + authorized = 1; + if (!(flags_and & WPA_STA_AUTHORIZED)) + authorized = 0; + + if (authorized < 0) + return 0; + + return bsd_send_mlme_param(priv, authorized ? + IEEE80211_MLME_AUTHORIZE : + IEEE80211_MLME_UNAUTHORIZE, 0, addr); +} + +static void +bsd_new_sta(void *priv, void *ctx, u8 addr[IEEE80211_ADDR_LEN]) +{ + struct ieee80211req_wpaie ie; + int ielen = 0; + u8 *iebuf = NULL; + + /* + * Fetch and validate any negotiated WPA/RSN parameters. + */ + memset(&ie, 0, sizeof(ie)); + memcpy(ie.wpa_macaddr, addr, IEEE80211_ADDR_LEN); + if (get80211var(priv, IEEE80211_IOC_WPAIE, &ie, sizeof(ie)) < 0) { + printf("Failed to get WPA/RSN information element.\n"); + goto no_ie; + } + iebuf = ie.wpa_ie; + ielen = ie.wpa_ie[1]; + if (ielen == 0) + iebuf = NULL; + else + ielen += 2; + +no_ie: + drv_event_assoc(ctx, addr, iebuf, ielen); +} + +static int +bsd_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, + int encrypt, const u8 *own_addr) { - struct wpa_driver_bsd_data *drv = priv; + struct bsd_driver_data *drv = priv; + wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", data, data_len); + + return l2_packet_send(drv->sock_xmit, addr, ETH_P_EAPOL, data, + data_len); +} + +static int +bsd_set_freq(void *priv, u16 channel) +{ + struct bsd_driver_data *drv = priv; +#ifdef SIOCS80211CHANNEL + struct ieee80211chanreq creq; +#endif /* SIOCS80211CHANNEL */ + u32 mode; + + if (channel < 14) + mode = IFM_IEEE80211_11G; + else if (channel == 14) + mode = IFM_IEEE80211_11B; + else + mode = IFM_IEEE80211_11A; + if (bsd_set_mediaopt(drv, IFM_MMASK, mode) < 0) { + wpa_printf(MSG_ERROR, "%s: failed to set modulation mode", + __func__); + return -1; + } + +#ifdef SIOCS80211CHANNEL + os_memset(&creq, 0, sizeof(creq)); + os_strlcpy(creq.i_name, drv->ifname, sizeof(creq.i_name)); + creq.i_channel = channel; + return ioctl(drv->sock, SIOCS80211CHANNEL, &creq); +#else /* SIOCS80211CHANNEL */ + return set80211param(priv, IEEE80211_IOC_CHANNEL, channel); +#endif /* SIOCS80211CHANNEL */ +} + +static int +bsd_set_opt_ie(void *priv, const u8 *ie, size_t ie_len) +{ +#ifdef IEEE80211_IOC_APPIE + wpa_printf(MSG_DEBUG, "%s: set WPA+RSN ie (len %lu)", __func__, + (unsigned long)ie_len); + return bsd_set80211(priv, IEEE80211_IOC_APPIE, IEEE80211_APPIE_WPA, + ie, ie_len); +#endif /* IEEE80211_IOC_APPIE */ + return 0; +} + + +#ifdef HOSTAPD + +/* + * Avoid conflicts with hostapd definitions by undefining couple of defines + * from net80211 header files. + */ +#undef RSN_VERSION +#undef WPA_VERSION +#undef WPA_OUI_TYPE + +static int bsd_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, + int reason_code); + +static const char * +ether_sprintf(const u8 *addr) +{ + static char buf[sizeof(MACSTR)]; + + if (addr != NULL) + snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr)); + else + snprintf(buf, sizeof(buf), MACSTR, 0,0,0,0,0,0); + return buf; +} + +static int +bsd_set_privacy(void *priv, int enabled) +{ wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); - return set80211param(drv, IEEE80211_IOC_COUNTERMEASURES, enabled); + + return set80211param(priv, IEEE80211_IOC_PRIVACY, enabled); +} + +static int +bsd_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx, + u8 *seq) +{ + struct ieee80211req_key wk; + + wpa_printf(MSG_DEBUG, "%s: addr=%s idx=%d", + __func__, ether_sprintf(addr), idx); + + memset(&wk, 0, sizeof(wk)); + if (addr == NULL) + memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); + else + memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); + wk.ik_keyix = idx; + + if (get80211var(priv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk)) < 0) { + printf("Failed to get encryption.\n"); + return -1; + } + +#ifdef WORDS_BIGENDIAN + { + /* + * wk.ik_keytsc is in host byte order (big endian), need to + * swap it to match with the byte order used in WPA. + */ + int i; + u8 tmp[WPA_KEY_RSC_LEN]; + memcpy(tmp, &wk.ik_keytsc, sizeof(wk.ik_keytsc)); + for (i = 0; i < WPA_KEY_RSC_LEN; i++) { + seq[i] = tmp[WPA_KEY_RSC_LEN - i - 1]; + } + } +#else /* WORDS_BIGENDIAN */ + memcpy(seq, &wk.ik_keytsc, sizeof(wk.ik_keytsc)); +#endif /* WORDS_BIGENDIAN */ + return 0; +} + + +static int +bsd_flush(void *priv) +{ + u8 allsta[IEEE80211_ADDR_LEN]; + + memset(allsta, 0xff, IEEE80211_ADDR_LEN); + return bsd_sta_deauth(priv, NULL, allsta, IEEE80211_REASON_AUTH_LEAVE); } static int -wpa_driver_bsd_set_drop_unencrypted(void *priv, int enabled) +bsd_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data, + const u8 *addr) { - struct wpa_driver_bsd_data *drv = priv; + struct ieee80211req_sta_stats stats; + + memcpy(stats.is_u.macaddr, addr, IEEE80211_ADDR_LEN); + if (get80211var(priv, IEEE80211_IOC_STA_STATS, &stats, sizeof(stats)) + > 0) { + /* XXX? do packets counts include non-data frames? */ + data->rx_packets = stats.is_stats.ns_rx_data; + data->rx_bytes = stats.is_stats.ns_rx_bytes; + data->tx_packets = stats.is_stats.ns_tx_data; + data->tx_bytes = stats.is_stats.ns_tx_bytes; + } + return 0; +} +static int +bsd_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, int reason_code) +{ + return bsd_send_mlme_param(priv, IEEE80211_MLME_DEAUTH, reason_code, + addr); +} + +static int +bsd_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, + int reason_code) +{ + return bsd_send_mlme_param(priv, IEEE80211_MLME_DISASSOC, reason_code, + addr); +} + +static void +bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx) +{ + struct bsd_driver_data *drv = ctx; + char buf[2048]; + struct if_announcemsghdr *ifan; + struct rt_msghdr *rtm; + struct ieee80211_michael_event *mic; + struct ieee80211_join_event *join; + struct ieee80211_leave_event *leave; + int n; + union wpa_event_data data; + + n = read(sock, buf, sizeof(buf)); + if (n < 0) { + if (errno != EINTR && errno != EAGAIN) + perror("read(PF_ROUTE)"); + return; + } + + rtm = (struct rt_msghdr *) buf; + if (rtm->rtm_version != RTM_VERSION) { + wpa_printf(MSG_DEBUG, "Routing message version %d not " + "understood\n", rtm->rtm_version); + return; + } + ifan = (struct if_announcemsghdr *) rtm; + switch (rtm->rtm_type) { + case RTM_IEEE80211: + switch (ifan->ifan_what) { + case RTM_IEEE80211_ASSOC: + case RTM_IEEE80211_REASSOC: + case RTM_IEEE80211_DISASSOC: + case RTM_IEEE80211_SCAN: + break; + case RTM_IEEE80211_LEAVE: + leave = (struct ieee80211_leave_event *) &ifan[1]; + drv_event_disassoc(drv->hapd, leave->iev_addr); + break; + case RTM_IEEE80211_JOIN: +#ifdef RTM_IEEE80211_REJOIN + case RTM_IEEE80211_REJOIN: +#endif + join = (struct ieee80211_join_event *) &ifan[1]; + bsd_new_sta(drv, drv->hapd, join->iev_addr); + break; + case RTM_IEEE80211_REPLAY: + /* ignore */ + break; + case RTM_IEEE80211_MICHAEL: + mic = (struct ieee80211_michael_event *) &ifan[1]; + wpa_printf(MSG_DEBUG, + "Michael MIC failure wireless event: " + "keyix=%u src_addr=" MACSTR, mic->iev_keyix, + MAC2STR(mic->iev_src)); + os_memset(&data, 0, sizeof(data)); + data.michael_mic_failure.unicast = 1; + data.michael_mic_failure.src = mic->iev_src; + wpa_supplicant_event(drv->hapd, + EVENT_MICHAEL_MIC_FAILURE, &data); + break; + } + break; + } +} + +static void +handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) +{ + struct bsd_driver_data *drv = ctx; + drv_event_eapol_rx(drv->hapd, src_addr, buf, len); +} + +static int +hostapd_bsd_set_freq(void *priv, struct hostapd_freq_params *freq) +{ + return bsd_set_freq(priv, freq->channel); +} + +static void * +bsd_init(struct hostapd_data *hapd, struct wpa_init_params *params) +{ + struct bsd_driver_data *drv; + + drv = os_zalloc(sizeof(struct bsd_driver_data)); + if (drv == NULL) { + printf("Could not allocate memory for bsd driver data\n"); + goto bad; + } + + drv->hapd = hapd; + drv->sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->sock < 0) { + perror("socket[PF_INET,SOCK_DGRAM]"); + goto bad; + } + os_strlcpy(drv->ifname, params->ifname, sizeof(drv->ifname)); + + drv->sock_xmit = l2_packet_init(drv->ifname, NULL, ETH_P_EAPOL, + handle_read, drv, 0); + if (drv->sock_xmit == NULL) + goto bad; + if (l2_packet_get_own_addr(drv->sock_xmit, params->own_addr)) + goto bad; + + /* mark down during setup */ + if (bsd_ctrl_iface(drv, 0) < 0) + goto bad; + + drv->route = socket(PF_ROUTE, SOCK_RAW, 0); + if (drv->route < 0) { + perror("socket(PF_ROUTE,SOCK_RAW)"); + goto bad; + } + eloop_register_read_sock(drv->route, bsd_wireless_event_receive, drv, + NULL); + + if (bsd_set_mediaopt(drv, IFM_OMASK, IFM_IEEE80211_HOSTAP) < 0) { + wpa_printf(MSG_ERROR, "%s: failed to set operation mode", + __func__); + goto bad; + } + + return drv; +bad: + if (drv->sock_xmit != NULL) + l2_packet_deinit(drv->sock_xmit); + if (drv->sock >= 0) + close(drv->sock); + if (drv != NULL) + os_free(drv); + return NULL; +} + + +static void +bsd_deinit(void *priv) +{ + struct bsd_driver_data *drv = priv; + + if (drv->route >= 0) { + eloop_unregister_read_sock(drv->route); + close(drv->route); + } + bsd_ctrl_iface(drv, 0); + if (drv->sock >= 0) + close(drv->sock); + if (drv->sock_xmit != NULL) + l2_packet_deinit(drv->sock_xmit); + os_free(drv); +} + +#else /* HOSTAPD */ + +static int +get80211param(struct bsd_driver_data *drv, int op) +{ + struct ieee80211req ireq; + + if (bsd_get80211(drv, &ireq, op, NULL, 0) < 0) + return -1; + return ireq.i_val; +} + +static int +wpa_driver_bsd_get_bssid(void *priv, u8 *bssid) +{ + struct bsd_driver_data *drv = priv; +#ifdef SIOCG80211BSSID + struct ieee80211_bssid bs; + + os_strlcpy(bs.i_name, drv->ifname, sizeof(bs.i_name)); + if (ioctl(drv->sock, SIOCG80211BSSID, &bs) < 0) + return -1; + os_memcpy(bssid, bs.i_bssid, sizeof(bs.i_bssid)); + return 0; +#else + return get80211var(drv, IEEE80211_IOC_BSSID, + bssid, IEEE80211_ADDR_LEN) < 0 ? -1 : 0; +#endif +} + +static int +wpa_driver_bsd_get_ssid(void *priv, u8 *ssid) +{ + struct bsd_driver_data *drv = priv; + return bsd_get_ssid(drv, ssid, 0); +} + +static int +wpa_driver_bsd_set_wpa_ie(struct bsd_driver_data *drv, const u8 *wpa_ie, + size_t wpa_ie_len) +{ +#ifdef IEEE80211_IOC_APPIE + return bsd_set_opt_ie(drv, wpa_ie, wpa_ie_len); +#else /* IEEE80211_IOC_APPIE */ + return set80211var(drv, IEEE80211_IOC_OPTIE, wpa_ie, wpa_ie_len); +#endif /* IEEE80211_IOC_APPIE */ +} + +static int +wpa_driver_bsd_set_wpa_internal(void *priv, int wpa, int privacy) +{ + int ret = 0; + + wpa_printf(MSG_DEBUG, "%s: wpa=%d privacy=%d", + __FUNCTION__, wpa, privacy); + + if (!wpa && wpa_driver_bsd_set_wpa_ie(priv, NULL, 0) < 0) + ret = -1; + if (set80211param(priv, IEEE80211_IOC_PRIVACY, privacy) < 0) + ret = -1; + if (set80211param(priv, IEEE80211_IOC_WPA, wpa) < 0) + ret = -1; + + return ret; +} + +static int +wpa_driver_bsd_set_wpa(void *priv, int enabled) +{ + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + + return wpa_driver_bsd_set_wpa_internal(priv, enabled ? 3 : 0, enabled); +} + +static int +wpa_driver_bsd_set_countermeasures(void *priv, int enabled) +{ wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); - return set80211param(drv, IEEE80211_IOC_DROPUNENCRYPTED, enabled); + return set80211param(priv, IEEE80211_IOC_COUNTERMEASURES, enabled); } + static int -wpa_driver_bsd_deauthenticate(void *priv, const u8 *addr, int reason_code) +wpa_driver_bsd_set_drop_unencrypted(void *priv, int enabled) { - struct wpa_driver_bsd_data *drv = priv; - struct ieee80211req_mlme mlme; + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); + return set80211param(priv, IEEE80211_IOC_DROPUNENCRYPTED, enabled); +} - wpa_printf(MSG_DEBUG, "%s", __func__); - os_memset(&mlme, 0, sizeof(mlme)); - mlme.im_op = IEEE80211_MLME_DEAUTH; - mlme.im_reason = reason_code; - os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); - return set80211var(drv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme)); +static int +wpa_driver_bsd_deauthenticate(void *priv, const u8 *addr, int reason_code) +{ + return bsd_send_mlme_param(priv, IEEE80211_MLME_DEAUTH, reason_code, + addr); } static int wpa_driver_bsd_disassociate(void *priv, const u8 *addr, int reason_code) { - struct wpa_driver_bsd_data *drv = priv; - struct ieee80211req_mlme mlme; + return bsd_send_mlme_param(priv, IEEE80211_MLME_DISASSOC, reason_code, + addr); +} - wpa_printf(MSG_DEBUG, "%s", __func__); - os_memset(&mlme, 0, sizeof(mlme)); - mlme.im_op = IEEE80211_MLME_DISASSOC; - mlme.im_reason = reason_code; - os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); - return set80211var(drv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme)); +static int +wpa_driver_bsd_set_auth_alg(void *priv, int auth_alg) +{ + int authmode; + + if ((auth_alg & WPA_AUTH_ALG_OPEN) && + (auth_alg & WPA_AUTH_ALG_SHARED)) + authmode = IEEE80211_AUTH_AUTO; + else if (auth_alg & WPA_AUTH_ALG_SHARED) + authmode = IEEE80211_AUTH_SHARED; + else + authmode = IEEE80211_AUTH_OPEN; + + return set80211param(priv, IEEE80211_IOC_AUTHMODE, authmode); +} + +static void +handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) +{ + struct bsd_driver_data *drv = ctx; + + drv_event_eapol_rx(drv->ctx, src_addr, buf, len); } static int wpa_driver_bsd_associate(void *priv, struct wpa_driver_associate_params *params) { - struct wpa_driver_bsd_data *drv = priv; + struct bsd_driver_data *drv = priv; struct ieee80211req_mlme mlme; + u32 mode; + u16 channel; int privacy; + int ret = 0; wpa_printf(MSG_DEBUG, "%s: ssid '%.*s' wpa ie len %u pairwise %u group %u key mgmt %u" , __func__ - , params->ssid_len, params->ssid - , params->wpa_ie_len + , (unsigned int) params->ssid_len, params->ssid + , (unsigned int) params->wpa_ie_len , params->pairwise_suite , params->group_suite , params->key_mgmt_suite ); + switch (params->mode) { + case IEEE80211_MODE_INFRA: + mode = 0 /* STA */; + break; + case IEEE80211_MODE_IBSS: + mode = IFM_IEEE80211_IBSS; + break; + case IEEE80211_MODE_AP: + mode = IFM_IEEE80211_HOSTAP; + break; + default: + wpa_printf(MSG_ERROR, "%s: unknown operation mode", __func__); + return -1; + } + if (bsd_set_mediaopt(drv, IFM_OMASK, mode) < 0) { + wpa_printf(MSG_ERROR, "%s: failed to set operation mode", + __func__); + return -1; + } + + if (params->mode == IEEE80211_MODE_AP) { + if (params->freq >= 2412 && params->freq <= 2472) + channel = (params->freq - 2407) / 5; + else if (params->freq == 2484) + channel = 14; + else if ((params->freq >= 5180 && params->freq <= 5240) || + (params->freq >= 5745 && params->freq <= 5825)) + channel = (params->freq - 5000) / 5; + else + channel = 0; + if (bsd_set_freq(drv, channel) < 0) + return -1; + + drv->sock_xmit = l2_packet_init(drv->ifname, NULL, ETH_P_EAPOL, + handle_read, drv, 0); + if (drv->sock_xmit == NULL) + return -1; + drv->is_ap = 1; + return 0; + } + + if (wpa_driver_bsd_set_drop_unencrypted(drv, params->drop_unencrypted) + < 0) + ret = -1; + if (wpa_driver_bsd_set_auth_alg(drv, params->auth_alg) < 0) + ret = -1; /* XXX error handling is wrong but unclear what to do... */ if (wpa_driver_bsd_set_wpa_ie(drv, params->wpa_ie, params->wpa_ie_len) < 0) return -1; @@ -410,62 +1059,89 @@ wpa_driver_bsd_associate(void *priv, struct wpa_driver_associate_params *params) os_memcpy(mlme.im_macaddr, params->bssid, IEEE80211_ADDR_LEN); if (set80211var(drv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme)) < 0) return -1; - return 0; + return ret; } static int -wpa_driver_bsd_set_auth_alg(void *priv, int auth_alg) +wpa_driver_bsd_scan(void *priv, struct wpa_driver_scan_params *params) { - struct wpa_driver_bsd_data *drv = priv; - int authmode; + struct bsd_driver_data *drv = priv; +#ifdef IEEE80211_IOC_SCAN_MAX_SSID + struct ieee80211_scan_req sr; + int i; +#endif /* IEEE80211_IOC_SCAN_MAX_SSID */ - if ((auth_alg & AUTH_ALG_OPEN_SYSTEM) && - (auth_alg & AUTH_ALG_SHARED_KEY)) - authmode = IEEE80211_AUTH_AUTO; - else if (auth_alg & AUTH_ALG_SHARED_KEY) - authmode = IEEE80211_AUTH_SHARED; - else - authmode = IEEE80211_AUTH_OPEN; + if (bsd_set_mediaopt(drv, IFM_OMASK, 0 /* STA */) < 0) { + wpa_printf(MSG_ERROR, "%s: failed to set operation mode", + __func__); + return -1; + } - return set80211param(drv, IEEE80211_IOC_AUTHMODE, authmode); -} + if (set80211param(drv, IEEE80211_IOC_ROAMING, + IEEE80211_ROAMING_MANUAL) < 0) { + wpa_printf(MSG_ERROR, "%s: failed to set " + "wpa_supplicant-based roaming: %s", __func__, + strerror(errno)); + return -1; + } -static int -wpa_driver_bsd_scan(void *priv, const u8 *ssid, size_t ssid_len) -{ - struct wpa_driver_bsd_data *drv = priv; - int flags; + if (wpa_driver_bsd_set_wpa(drv, 1) < 0) { + wpa_printf(MSG_ERROR, "%s: failed to set wpa: %s", __func__, + strerror(errno)); + return -1; + } /* NB: interface must be marked UP to do a scan */ - if (getifflags(drv, &flags) != 0 || setifflags(drv, flags | IFF_UP) != 0) + if (bsd_ctrl_iface(drv, 1) < 0) return -1; +#ifdef IEEE80211_IOC_SCAN_MAX_SSID + os_memset(&sr, 0, sizeof(sr)); + sr.sr_flags = IEEE80211_IOC_SCAN_ACTIVE | IEEE80211_IOC_SCAN_ONCE | + IEEE80211_IOC_SCAN_NOJOIN; + sr.sr_duration = IEEE80211_IOC_SCAN_FOREVER; + if (params->num_ssids > 0) { + sr.sr_nssid = params->num_ssids; +#if 0 + /* Boundary check is done by upper layer */ + if (sr.sr_nssid > IEEE80211_IOC_SCAN_MAX_SSID) + sr.sr_nssid = IEEE80211_IOC_SCAN_MAX_SSID; +#endif + + /* NB: check scan cache first */ + sr.sr_flags |= IEEE80211_IOC_SCAN_CHECK; + } + for (i = 0; i < sr.sr_nssid; i++) { + sr.sr_ssid[i].len = params->ssids[i].ssid_len; + os_memcpy(sr.sr_ssid[i].ssid, params->ssids[i].ssid, + sr.sr_ssid[i].len); + } + + /* NB: net80211 delivers a scan complete event so no need to poll */ + return set80211var(drv, IEEE80211_IOC_SCAN_REQ, &sr, sizeof(sr)); +#else /* IEEE80211_IOC_SCAN_MAX_SSID */ /* set desired ssid before scan */ - if (wpa_driver_bsd_set_ssid(drv, ssid, ssid_len) < 0) + if (bsd_set_ssid(drv, params->ssids[0].ssid, + params->ssids[0].ssid_len) < 0) return -1; /* NB: net80211 delivers a scan complete event so no need to poll */ return set80211param(drv, IEEE80211_IOC_SCAN_REQ, 0); +#endif /* IEEE80211_IOC_SCAN_MAX_SSID */ } -#include <net/route.h> -#if __FreeBSD__ -#include <net80211/ieee80211_freebsd.h> -#endif -#if __NetBSD__ -#include <net80211/ieee80211_netbsd.h> -#endif - static void wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx) { - struct wpa_driver_bsd_data *drv = sock_ctx; + struct bsd_driver_data *drv = sock_ctx; char buf[2048]; struct if_announcemsghdr *ifan; struct if_msghdr *ifm; struct rt_msghdr *rtm; union wpa_event_data event; struct ieee80211_michael_event *mic; + struct ieee80211_leave_event *leave; + struct ieee80211_join_event *join; int n; n = read(sock, buf, sizeof(buf)); @@ -487,8 +1163,8 @@ wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx) ifan = (struct if_announcemsghdr *) rtm; if (ifan->ifan_index != drv->ifindex) break; - strlcpy(event.interface_status.ifname, drv->ifname, - sizeof(event.interface_status.ifname)); + os_strlcpy(event.interface_status.ifname, drv->ifname, + sizeof(event.interface_status.ifname)); switch (ifan->ifan_what) { case IFAN_DEPARTURE: event.interface_status.ievent = EVENT_INTERFACE_REMOVED; @@ -508,14 +1184,31 @@ wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx) switch (ifan->ifan_what) { case RTM_IEEE80211_ASSOC: case RTM_IEEE80211_REASSOC: + if (drv->is_ap) + break; wpa_supplicant_event(ctx, EVENT_ASSOC, NULL); break; case RTM_IEEE80211_DISASSOC: + if (drv->is_ap) + break; wpa_supplicant_event(ctx, EVENT_DISASSOC, NULL); break; case RTM_IEEE80211_SCAN: + if (drv->is_ap) + break; wpa_supplicant_event(ctx, EVENT_SCAN_RESULTS, NULL); break; + case RTM_IEEE80211_LEAVE: + leave = (struct ieee80211_leave_event *) &ifan[1]; + drv_event_disassoc(ctx, leave->iev_addr); + break; + case RTM_IEEE80211_JOIN: +#ifdef RTM_IEEE80211_REJOIN + case RTM_IEEE80211_REJOIN: +#endif + join = (struct ieee80211_join_event *) &ifan[1]; + bsd_new_sta(drv, ctx, join->iev_addr); + break; case RTM_IEEE80211_REPLAY: /* ignore */ break; @@ -539,8 +1232,8 @@ wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx) if (ifm->ifm_index != drv->ifindex) break; if ((rtm->rtm_flags & RTF_UP) == 0) { - strlcpy(event.interface_status.ifname, drv->ifname, - sizeof(event.interface_status.ifname)); + os_strlcpy(event.interface_status.ifname, drv->ifname, + sizeof(event.interface_status.ifname)); event.interface_status.ievent = EVENT_INTERFACE_REMOVED; wpa_printf(MSG_DEBUG, "RTM_IFINFO: Interface '%s' DOWN", event.interface_status.ifname); @@ -550,135 +1243,167 @@ wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx) } } -/* Compare function for sorting scan results. Return >0 if @b is consider - * better. */ -static int -wpa_scan_result_compar(const void *a, const void *b) +static void +wpa_driver_bsd_add_scan_entry(struct wpa_scan_results *res, + struct ieee80211req_scan_result *sr) { - const struct wpa_scan_result *wa = a; - const struct wpa_scan_result *wb = b; + struct wpa_scan_res *result, **tmp; + size_t extra_len; + u8 *pos; - /* WPA/WPA2 support preferred */ - if ((wb->wpa_ie_len || wb->rsn_ie_len) && - !(wa->wpa_ie_len || wa->rsn_ie_len)) - return 1; - if (!(wb->wpa_ie_len || wb->rsn_ie_len) && - (wa->wpa_ie_len || wa->rsn_ie_len)) - return -1; + extra_len = 2 + sr->isr_ssid_len; + extra_len += 2 + sr->isr_nrates; + extra_len += 3; /* ERP IE */ + extra_len += sr->isr_ie_len; - /* privacy support preferred */ - if ((wa->caps & IEEE80211_CAPINFO_PRIVACY) && - (wb->caps & IEEE80211_CAPINFO_PRIVACY) == 0) - return 1; - if ((wa->caps & IEEE80211_CAPINFO_PRIVACY) == 0 && - (wb->caps & IEEE80211_CAPINFO_PRIVACY)) - return -1; + result = os_zalloc(sizeof(*result) + extra_len); + if (result == NULL) + return; + os_memcpy(result->bssid, sr->isr_bssid, ETH_ALEN); + result->freq = sr->isr_freq; + result->beacon_int = sr->isr_intval; + result->caps = sr->isr_capinfo; + result->qual = sr->isr_rssi; + result->noise = sr->isr_noise; + + pos = (u8 *)(result + 1); + + *pos++ = WLAN_EID_SSID; + *pos++ = sr->isr_ssid_len; + os_memcpy(pos, sr + 1, sr->isr_ssid_len); + pos += sr->isr_ssid_len; + + /* + * Deal all rates as supported rate. + * Because net80211 doesn't report extended supported rate or not. + */ + *pos++ = WLAN_EID_SUPP_RATES; + *pos++ = sr->isr_nrates; + os_memcpy(pos, sr->isr_rates, sr->isr_nrates); + pos += sr->isr_nrates; + + *pos++ = WLAN_EID_ERP_INFO; + *pos++ = 1; + *pos++ = sr->isr_erp; - /* best/max rate preferred if signal level close enough XXX */ - if (wa->maxrate != wb->maxrate && abs(wb->level - wa->level) < 5) - return wb->maxrate - wa->maxrate; + os_memcpy(pos, (u8 *)(sr + 1) + sr->isr_ssid_len, sr->isr_ie_len); + pos += sr->isr_ie_len; - /* use freq for channel preference */ + result->ie_len = pos - (u8 *)(result + 1); - /* all things being equal, use signal level */ - return wb->level - wa->level; + tmp = os_realloc(res->res, + (res->num + 1) * sizeof(struct wpa_scan_res *)); + if (tmp == NULL) { + os_free(result); + return; + } + tmp[res->num++] = result; + res->res = tmp; } -static int -getmaxrate(uint8_t rates[15], uint8_t nrates) +struct wpa_scan_results * +wpa_driver_bsd_get_scan_results2(void *priv) { - int i, maxrate = -1; + struct ieee80211req_scan_result *sr; + struct wpa_scan_results *res; + int len, rest; + uint8_t buf[24*1024], *pos; + + len = get80211var(priv, IEEE80211_IOC_SCAN_RESULTS, buf, 24*1024); + if (len < 0) + return NULL; + + res = os_zalloc(sizeof(*res)); + if (res == NULL) + return NULL; - for (i = 0; i < nrates; i++) { - int rate = rates[i] & IEEE80211_RATE_VAL; - if (rate > maxrate) - rate = maxrate; + pos = buf; + rest = len; + while (rest >= sizeof(struct ieee80211req_scan_result)) { + sr = (struct ieee80211req_scan_result *)pos; + wpa_driver_bsd_add_scan_entry(res, sr); + pos += sr->isr_len; + rest -= sr->isr_len; } - return maxrate; -} -/* unalligned little endian access */ -#define LE_READ_4(p) \ - ((u_int32_t) \ - ((((const u_int8_t *)(p))[0] ) | \ - (((const u_int8_t *)(p))[1] << 8) | \ - (((const u_int8_t *)(p))[2] << 16) | \ - (((const u_int8_t *)(p))[3] << 24))) + wpa_printf(MSG_DEBUG, "Received %d bytes of scan results (%lu BSSes)", + len, (unsigned long)res->num); -static int __inline -iswpaoui(const u_int8_t *frm) -{ - return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI); + return res; } -static int -wpa_driver_bsd_get_scan_results(void *priv, - struct wpa_scan_result *results, - size_t max_size) +static int wpa_driver_bsd_capa(struct bsd_driver_data *drv) { -#define min(a,b) ((a)>(b)?(b):(a)) - struct wpa_driver_bsd_data *drv = priv; - uint8_t buf[24*1024]; - uint8_t *cp, *vp; - struct ieee80211req_scan_result *sr; - struct wpa_scan_result *wsr; - int len, ielen; - - os_memset(results, 0, max_size * sizeof(struct wpa_scan_result)); +#ifdef IEEE80211_IOC_DEVCAPS +/* kernel definitions copied from net80211/ieee80211_var.h */ +#define IEEE80211_CIPHER_WEP 0 +#define IEEE80211_CIPHER_TKIP 1 +#define IEEE80211_CIPHER_AES_CCM 3 +#define IEEE80211_CRYPTO_WEP (1<<IEEE80211_CIPHER_WEP) +#define IEEE80211_CRYPTO_TKIP (1<<IEEE80211_CIPHER_TKIP) +#define IEEE80211_CRYPTO_AES_CCM (1<<IEEE80211_CIPHER_AES_CCM) +#define IEEE80211_C_HOSTAP 0x00000400 /* CAPABILITY: HOSTAP avail */ +#define IEEE80211_C_WPA1 0x00800000 /* CAPABILITY: WPA1 avail */ +#define IEEE80211_C_WPA2 0x01000000 /* CAPABILITY: WPA2 avail */ + struct ieee80211_devcaps_req devcaps; - len = get80211var(drv, IEEE80211_IOC_SCAN_RESULTS, buf, sizeof(buf)); - if (len < 0) + if (get80211var(drv, IEEE80211_IOC_DEVCAPS, &devcaps, + sizeof(devcaps)) < 0) { + wpa_printf(MSG_ERROR, "failed to IEEE80211_IOC_DEVCAPS: %s", + strerror(errno)); return -1; - cp = buf; - wsr = results; - while (len >= sizeof(struct ieee80211req_scan_result)) { - sr = (struct ieee80211req_scan_result *) cp; - os_memcpy(wsr->bssid, sr->isr_bssid, IEEE80211_ADDR_LEN); - wsr->ssid_len = sr->isr_ssid_len; - wsr->freq = sr->isr_freq; - wsr->noise = sr->isr_noise; - wsr->qual = sr->isr_rssi; - wsr->level = 0; /* XXX? */ - wsr->caps = sr->isr_capinfo; - wsr->maxrate = getmaxrate(sr->isr_rates, sr->isr_nrates); - vp = (u_int8_t *)(sr+1); - os_memcpy(wsr->ssid, vp, sr->isr_ssid_len); - if (sr->isr_ie_len > 0) { - vp += sr->isr_ssid_len; - ielen = sr->isr_ie_len; - while (ielen > 0) { - switch (vp[0]) { - case IEEE80211_ELEMID_VENDOR: - if (!iswpaoui(vp)) - break; - wsr->wpa_ie_len = - min(2+vp[1], SSID_MAX_WPA_IE_LEN); - os_memcpy(wsr->wpa_ie, vp, - wsr->wpa_ie_len); - break; - case IEEE80211_ELEMID_RSN: - wsr->rsn_ie_len = - min(2+vp[1], SSID_MAX_WPA_IE_LEN); - os_memcpy(wsr->rsn_ie, vp, - wsr->rsn_ie_len); - break; - } - ielen -= 2+vp[1]; - vp += 2+vp[1]; - } - } - - cp += sr->isr_len, len -= sr->isr_len; - wsr++; } - qsort(results, wsr - results, sizeof(struct wpa_scan_result), - wpa_scan_result_compar); - wpa_printf(MSG_DEBUG, "Received %d bytes of scan results (%d BSSes)", - len, wsr - results); + wpa_printf(MSG_DEBUG, "%s: drivercaps=0x%08x,cryptocaps=0x%08x", + __func__, devcaps.dc_drivercaps, devcaps.dc_cryptocaps); + + if (devcaps.dc_drivercaps & IEEE80211_C_WPA1) + drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK; + if (devcaps.dc_drivercaps & IEEE80211_C_WPA2) + drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK; - return wsr - results; -#undef min + if (devcaps.dc_cryptocaps & IEEE80211_CRYPTO_WEP) + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 | + WPA_DRIVER_CAPA_ENC_WEP104; + if (devcaps.dc_cryptocaps & IEEE80211_CRYPTO_TKIP) + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP; + if (devcaps.dc_cryptocaps & IEEE80211_CRYPTO_AES_CCM) + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP; + + if (devcaps.dc_drivercaps & IEEE80211_C_HOSTAP) + drv->capa.flags |= WPA_DRIVER_FLAGS_AP; +#undef IEEE80211_CIPHER_WEP +#undef IEEE80211_CIPHER_TKIP +#undef IEEE80211_CIPHER_AES_CCM +#undef IEEE80211_CRYPTO_WEP +#undef IEEE80211_CRYPTO_TKIP +#undef IEEE80211_CRYPTO_AES_CCM +#undef IEEE80211_C_HOSTAP +#undef IEEE80211_C_WPA1 +#undef IEEE80211_C_WPA2 +#else /* IEEE80211_IOC_DEVCAPS */ + /* For now, assume TKIP, CCMP, WPA, WPA2 are supported */ + drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK; + drv->capa.enc = WPA_DRIVER_CAPA_ENC_WEP40 | + WPA_DRIVER_CAPA_ENC_WEP104 | + WPA_DRIVER_CAPA_ENC_TKIP | + WPA_DRIVER_CAPA_ENC_CCMP; + drv->capa.flags |= WPA_DRIVER_FLAGS_AP; +#endif /* IEEE80211_IOC_DEVCAPS */ +#ifdef IEEE80211_IOC_SCAN_MAX_SSID + drv->capa.max_scan_ssids = IEEE80211_IOC_SCAN_MAX_SSID; +#else /* IEEE80211_IOC_SCAN_MAX_SSID */ + drv->capa.max_scan_ssids = 1; +#endif /* IEEE80211_IOC_SCAN_MAX_SSID */ + drv->capa.auth = WPA_DRIVER_AUTH_OPEN | + WPA_DRIVER_AUTH_SHARED | + WPA_DRIVER_AUTH_LEAP; + return 0; } static void * @@ -686,7 +1411,7 @@ wpa_driver_bsd_init(void *ctx, const char *ifname) { #define GETPARAM(drv, param, v) \ (((v) = get80211param(drv, param)) != -1) - struct wpa_driver_bsd_data *drv; + struct bsd_driver_data *drv; drv = os_zalloc(sizeof(*drv)); if (drv == NULL) @@ -715,6 +1440,10 @@ wpa_driver_bsd_init(void *ctx, const char *ifname) drv->ctx = ctx; os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + /* Down interface during setup. */ + if (bsd_ctrl_iface(drv, 0) < 0) + goto fail; + if (!GETPARAM(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming)) { wpa_printf(MSG_DEBUG, "%s: failed to get roaming state: %s", __func__, strerror(errno)); @@ -730,17 +1459,9 @@ wpa_driver_bsd_init(void *ctx, const char *ifname) __func__, strerror(errno)); goto fail; } - if (set80211param(drv, IEEE80211_IOC_ROAMING, IEEE80211_ROAMING_MANUAL) < 0) { - wpa_printf(MSG_DEBUG, "%s: failed to set wpa_supplicant-based " - "roaming: %s", __func__, strerror(errno)); - goto fail; - } - if (set80211param(drv, IEEE80211_IOC_WPA, 1+2) < 0) { - wpa_printf(MSG_DEBUG, "%s: failed to enable WPA support %s", - __func__, strerror(errno)); + if (wpa_driver_bsd_capa(drv)) goto fail; - } return drv; fail: @@ -754,41 +1475,68 @@ fail1: static void wpa_driver_bsd_deinit(void *priv) { - struct wpa_driver_bsd_data *drv = priv; - int flags; + struct bsd_driver_data *drv = priv; + wpa_driver_bsd_set_wpa(drv, 0); eloop_unregister_read_sock(drv->route); /* NB: mark interface down */ - if (getifflags(drv, &flags) == 0) - (void) setifflags(drv, flags &~ IFF_UP); + bsd_ctrl_iface(drv, 0); wpa_driver_bsd_set_wpa_internal(drv, drv->prev_wpa, drv->prev_privacy); if (set80211param(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming) < 0) wpa_printf(MSG_DEBUG, "%s: failed to restore roaming state", __func__); + if (drv->sock_xmit != NULL) + l2_packet_deinit(drv->sock_xmit); (void) close(drv->route); /* ioctl socket */ (void) close(drv->sock); /* event socket */ os_free(drv); } +static int +wpa_driver_bsd_get_capa(void *priv, struct wpa_driver_capa *capa) +{ + struct bsd_driver_data *drv = priv; + + os_memcpy(capa, &drv->capa, sizeof(*capa)); + return 0; +} +#endif /* HOSTAPD */ + const struct wpa_driver_ops wpa_driver_bsd_ops = { .name = "bsd", - .desc = "BSD 802.11 support (Atheros, etc.)", + .desc = "BSD 802.11 support", +#ifdef HOSTAPD + .hapd_init = bsd_init, + .hapd_deinit = bsd_deinit, + .set_privacy = bsd_set_privacy, + .get_seqnum = bsd_get_seqnum, + .flush = bsd_flush, + .read_sta_data = bsd_read_sta_driver_data, + .sta_disassoc = bsd_sta_disassoc, + .sta_deauth = bsd_sta_deauth, + .set_freq = hostapd_bsd_set_freq, +#else /* HOSTAPD */ .init = wpa_driver_bsd_init, .deinit = wpa_driver_bsd_deinit, .get_bssid = wpa_driver_bsd_get_bssid, .get_ssid = wpa_driver_bsd_get_ssid, - .set_wpa = wpa_driver_bsd_set_wpa, - .set_key = wpa_driver_bsd_set_key, .set_countermeasures = wpa_driver_bsd_set_countermeasures, - .set_drop_unencrypted = wpa_driver_bsd_set_drop_unencrypted, - .scan = wpa_driver_bsd_scan, - .get_scan_results = wpa_driver_bsd_get_scan_results, + .scan2 = wpa_driver_bsd_scan, + .get_scan_results2 = wpa_driver_bsd_get_scan_results2, .deauthenticate = wpa_driver_bsd_deauthenticate, .disassociate = wpa_driver_bsd_disassociate, .associate = wpa_driver_bsd_associate, - .set_auth_alg = wpa_driver_bsd_set_auth_alg, + .get_capa = wpa_driver_bsd_get_capa, +#endif /* HOSTAPD */ + .set_key = bsd_set_key, + .set_ieee8021x = bsd_set_ieee8021x, + .hapd_set_ssid = bsd_set_ssid, + .hapd_get_ssid = bsd_get_ssid, + .hapd_send_eapol = bsd_send_eapol, + .sta_set_flags = bsd_set_sta_authorized, + .set_generic_elem = bsd_set_opt_ie, }; diff --git a/src/drivers/driver_hostap.c b/src/drivers/driver_hostap.c index 84ef3bdedc549..952f3891abdeb 100644 --- a/src/drivers/driver_hostap.c +++ b/src/drivers/driver_hostap.c @@ -1,5 +1,5 @@ /* - * WPA Supplicant - driver interaction with Linux Host AP driver + * Driver interaction with Linux Host AP driver * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify @@ -23,6 +23,1099 @@ #include "driver_hostap.h" +#ifdef HOSTAPD + +#include <net/if_arp.h> +#include <netpacket/packet.h> + +#include "priv_netlink.h" +#include "netlink.h" +#include "linux_ioctl.h" +#include "common/ieee802_11_defs.h" + + +/* MTU to be set for the wlan#ap device; this is mainly needed for IEEE 802.1X + * frames that might be longer than normal default MTU and they are not + * fragmented */ +#define HOSTAPD_MTU 2290 + +static const u8 rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; + +struct hostap_driver_data { + struct hostapd_data *hapd; + + char iface[IFNAMSIZ + 1]; + int sock; /* raw packet socket for driver access */ + int ioctl_sock; /* socket for ioctl() use */ + struct netlink_data *netlink; + + int we_version; + + u8 *generic_ie; + size_t generic_ie_len; + u8 *wps_ie; + size_t wps_ie_len; +}; + + +static int hostapd_ioctl(void *priv, struct prism2_hostapd_param *param, + int len); +static int hostap_set_iface_flags(void *priv, int dev_up); + +static void handle_data(struct hostap_driver_data *drv, u8 *buf, size_t len, + u16 stype) +{ + struct ieee80211_hdr *hdr; + u16 fc, ethertype; + u8 *pos, *sa; + size_t left; + union wpa_event_data event; + + if (len < sizeof(struct ieee80211_hdr)) + return; + + hdr = (struct ieee80211_hdr *) buf; + fc = le_to_host16(hdr->frame_control); + + if ((fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) != WLAN_FC_TODS) { + printf("Not ToDS data frame (fc=0x%04x)\n", fc); + return; + } + + sa = hdr->addr2; + os_memset(&event, 0, sizeof(event)); + event.rx_from_unknown.frame = buf; + event.rx_from_unknown.len = len; + wpa_supplicant_event(drv->hapd, EVENT_RX_FROM_UNKNOWN, &event); + + pos = (u8 *) (hdr + 1); + left = len - sizeof(*hdr); + + if (left < sizeof(rfc1042_header)) { + printf("Too short data frame\n"); + return; + } + + if (memcmp(pos, rfc1042_header, sizeof(rfc1042_header)) != 0) { + printf("Data frame with no RFC1042 header\n"); + return; + } + pos += sizeof(rfc1042_header); + left -= sizeof(rfc1042_header); + + if (left < 2) { + printf("No ethertype in data frame\n"); + return; + } + + ethertype = WPA_GET_BE16(pos); + pos += 2; + left -= 2; + switch (ethertype) { + case ETH_P_PAE: + drv_event_eapol_rx(drv->hapd, sa, pos, left); + break; + + default: + printf("Unknown ethertype 0x%04x in data frame\n", ethertype); + break; + } +} + + +static void handle_tx_callback(struct hostap_driver_data *drv, u8 *buf, + size_t len, int ok) +{ + struct ieee80211_hdr *hdr; + u16 fc; + union wpa_event_data event; + + hdr = (struct ieee80211_hdr *) buf; + fc = le_to_host16(hdr->frame_control); + + os_memset(&event, 0, sizeof(event)); + event.tx_status.type = WLAN_FC_GET_TYPE(fc); + event.tx_status.stype = WLAN_FC_GET_STYPE(fc); + event.tx_status.dst = hdr->addr1; + event.tx_status.data = buf; + event.tx_status.data_len = len; + event.tx_status.ack = ok; + wpa_supplicant_event(drv->hapd, EVENT_TX_STATUS, &event); +} + + +static void handle_frame(struct hostap_driver_data *drv, u8 *buf, size_t len) +{ + struct ieee80211_hdr *hdr; + u16 fc, extra_len, type, stype; + unsigned char *extra = NULL; + size_t data_len = len; + int ver; + union wpa_event_data event; + + /* PSPOLL is only 16 bytes, but driver does not (at least yet) pass + * these to user space */ + if (len < 24) { + wpa_printf(MSG_MSGDUMP, "handle_frame: too short (%lu)", + (unsigned long) len); + return; + } + + hdr = (struct ieee80211_hdr *) buf; + fc = le_to_host16(hdr->frame_control); + type = WLAN_FC_GET_TYPE(fc); + stype = WLAN_FC_GET_STYPE(fc); + + if (type != WLAN_FC_TYPE_MGMT || stype != WLAN_FC_STYPE_BEACON) { + wpa_hexdump(MSG_MSGDUMP, "Received management frame", + buf, len); + } + + ver = fc & WLAN_FC_PVER; + + /* protocol version 3 is reserved for indicating extra data after the + * payload, version 2 for indicating ACKed frame (TX callbacks), and + * version 1 for indicating failed frame (no ACK, TX callbacks) */ + if (ver == 3) { + u8 *pos = buf + len - 2; + extra_len = WPA_GET_LE16(pos); + printf("extra data in frame (elen=%d)\n", extra_len); + if ((size_t) extra_len + 2 > len) { + printf(" extra data overflow\n"); + return; + } + len -= extra_len + 2; + extra = buf + len; + } else if (ver == 1 || ver == 2) { + handle_tx_callback(drv, buf, data_len, ver == 2 ? 1 : 0); + return; + } else if (ver != 0) { + printf("unknown protocol version %d\n", ver); + return; + } + + switch (type) { + case WLAN_FC_TYPE_MGMT: + os_memset(&event, 0, sizeof(event)); + event.rx_mgmt.frame = buf; + event.rx_mgmt.frame_len = data_len; + wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event); + break; + case WLAN_FC_TYPE_CTRL: + wpa_printf(MSG_DEBUG, "CTRL"); + break; + case WLAN_FC_TYPE_DATA: + wpa_printf(MSG_DEBUG, "DATA"); + handle_data(drv, buf, data_len, stype); + break; + default: + wpa_printf(MSG_DEBUG, "unknown frame type %d", type); + break; + } +} + + +static void handle_read(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct hostap_driver_data *drv = eloop_ctx; + int len; + unsigned char buf[3000]; + + len = recv(sock, buf, sizeof(buf), 0); + if (len < 0) { + perror("recv"); + return; + } + + handle_frame(drv, buf, len); +} + + +static int hostap_init_sockets(struct hostap_driver_data *drv, u8 *own_addr) +{ + struct ifreq ifr; + struct sockaddr_ll addr; + + drv->sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + if (drv->sock < 0) { + perror("socket[PF_PACKET,SOCK_RAW]"); + return -1; + } + + if (eloop_register_read_sock(drv->sock, handle_read, drv, NULL)) { + printf("Could not register read socket\n"); + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%sap", drv->iface); + if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) { + perror("ioctl(SIOCGIFINDEX)"); + return -1; + } + + if (hostap_set_iface_flags(drv, 1)) { + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sll_family = AF_PACKET; + addr.sll_ifindex = ifr.ifr_ifindex; + wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d", + addr.sll_ifindex); + + if (bind(drv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("bind"); + return -1; + } + + return linux_get_ifhwaddr(drv->sock, drv->iface, own_addr); +} + + +static int hostap_send_mlme(void *priv, const u8 *msg, size_t len) +{ + struct hostap_driver_data *drv = priv; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) msg; + int res; + + /* Request TX callback */ + hdr->frame_control |= host_to_le16(BIT(1)); + res = send(drv->sock, msg, len, 0); + hdr->frame_control &= ~host_to_le16(BIT(1)); + + return res; +} + + +static int hostap_send_eapol(void *priv, const u8 *addr, const u8 *data, + size_t data_len, int encrypt, const u8 *own_addr) +{ + struct hostap_driver_data *drv = priv; + struct ieee80211_hdr *hdr; + size_t len; + u8 *pos; + int res; + + len = sizeof(*hdr) + sizeof(rfc1042_header) + 2 + data_len; + hdr = os_zalloc(len); + if (hdr == NULL) { + printf("malloc() failed for hostapd_send_data(len=%lu)\n", + (unsigned long) len); + return -1; + } + + hdr->frame_control = + IEEE80211_FC(WLAN_FC_TYPE_DATA, WLAN_FC_STYPE_DATA); + hdr->frame_control |= host_to_le16(WLAN_FC_FROMDS); + if (encrypt) + hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP); + memcpy(hdr->IEEE80211_DA_FROMDS, addr, ETH_ALEN); + memcpy(hdr->IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN); + memcpy(hdr->IEEE80211_SA_FROMDS, own_addr, ETH_ALEN); + + pos = (u8 *) (hdr + 1); + memcpy(pos, rfc1042_header, sizeof(rfc1042_header)); + pos += sizeof(rfc1042_header); + *((u16 *) pos) = htons(ETH_P_PAE); + pos += 2; + memcpy(pos, data, data_len); + + res = hostap_send_mlme(drv, (u8 *) hdr, len); + if (res < 0) { + wpa_printf(MSG_ERROR, "hostap_send_eapol - packet len: %lu - " + "failed: %d (%s)", + (unsigned long) len, errno, strerror(errno)); + } + free(hdr); + + return res; +} + + +static int hostap_sta_set_flags(void *priv, const u8 *addr, + int total_flags, int flags_or, int flags_and) +{ + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param param; + + if (flags_or & WPA_STA_AUTHORIZED) + flags_or = BIT(5); /* WLAN_STA_AUTHORIZED */ + if (!(flags_and & WPA_STA_AUTHORIZED)) + flags_and = ~BIT(5); + else + flags_and = ~0; + memset(¶m, 0, sizeof(param)); + param.cmd = PRISM2_HOSTAPD_SET_FLAGS_STA; + memcpy(param.sta_addr, addr, ETH_ALEN); + param.u.set_flags_sta.flags_or = flags_or; + param.u.set_flags_sta.flags_and = flags_and; + return hostapd_ioctl(drv, ¶m, sizeof(param)); +} + + +static int hostap_set_iface_flags(void *priv, int dev_up) +{ + struct hostap_driver_data *drv = priv; + struct ifreq ifr; + char ifname[IFNAMSIZ]; + + os_snprintf(ifname, IFNAMSIZ, "%sap", drv->iface); + if (linux_set_iface_flags(drv->ioctl_sock, ifname, dev_up) < 0) + return -1; + + if (dev_up) { + memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_mtu = HOSTAPD_MTU; + if (ioctl(drv->ioctl_sock, SIOCSIFMTU, &ifr) != 0) { + perror("ioctl[SIOCSIFMTU]"); + printf("Setting MTU failed - trying to survive with " + "current value\n"); + } + } + + return 0; +} + + +static int hostapd_ioctl(void *priv, struct prism2_hostapd_param *param, + int len) +{ + struct hostap_driver_data *drv = priv; + struct iwreq iwr; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) param; + iwr.u.data.length = len; + + if (ioctl(drv->ioctl_sock, PRISM2_IOCTL_HOSTAPD, &iwr) < 0) { + perror("ioctl[PRISM2_IOCTL_HOSTAPD]"); + return -1; + } + + return 0; +} + + +static int wpa_driver_hostap_set_key(const char *ifname, void *priv, + enum wpa_alg alg, const u8 *addr, + int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param *param; + u8 *buf; + size_t blen; + int ret = 0; + + blen = sizeof(*param) + key_len; + buf = os_zalloc(blen); + if (buf == NULL) + return -1; + + param = (struct prism2_hostapd_param *) buf; + param->cmd = PRISM2_SET_ENCRYPTION; + if (addr == NULL) + memset(param->sta_addr, 0xff, ETH_ALEN); + else + memcpy(param->sta_addr, addr, ETH_ALEN); + switch (alg) { + case WPA_ALG_NONE: + os_strlcpy((char *) param->u.crypt.alg, "NONE", + HOSTAP_CRYPT_ALG_NAME_LEN); + break; + case WPA_ALG_WEP: + os_strlcpy((char *) param->u.crypt.alg, "WEP", + HOSTAP_CRYPT_ALG_NAME_LEN); + break; + case WPA_ALG_TKIP: + os_strlcpy((char *) param->u.crypt.alg, "TKIP", + HOSTAP_CRYPT_ALG_NAME_LEN); + break; + case WPA_ALG_CCMP: + os_strlcpy((char *) param->u.crypt.alg, "CCMP", + HOSTAP_CRYPT_ALG_NAME_LEN); + break; + default: + os_free(buf); + return -1; + } + param->u.crypt.flags = set_tx ? HOSTAP_CRYPT_FLAG_SET_TX_KEY : 0; + param->u.crypt.idx = key_idx; + param->u.crypt.key_len = key_len; + memcpy((u8 *) (param + 1), key, key_len); + + if (hostapd_ioctl(drv, param, blen)) { + printf("Failed to set encryption.\n"); + ret = -1; + } + free(buf); + + return ret; +} + + +static int hostap_get_seqnum(const char *ifname, void *priv, const u8 *addr, + int idx, u8 *seq) +{ + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param *param; + u8 *buf; + size_t blen; + int ret = 0; + + blen = sizeof(*param) + 32; + buf = os_zalloc(blen); + if (buf == NULL) + return -1; + + param = (struct prism2_hostapd_param *) buf; + param->cmd = PRISM2_GET_ENCRYPTION; + if (addr == NULL) + memset(param->sta_addr, 0xff, ETH_ALEN); + else + memcpy(param->sta_addr, addr, ETH_ALEN); + param->u.crypt.idx = idx; + + if (hostapd_ioctl(drv, param, blen)) { + printf("Failed to get encryption.\n"); + ret = -1; + } else { + memcpy(seq, param->u.crypt.seq, 8); + } + free(buf); + + return ret; +} + + +static int hostap_ioctl_prism2param(void *priv, int param, int value) +{ + struct hostap_driver_data *drv = priv; + struct iwreq iwr; + int *i; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + i = (int *) iwr.u.name; + *i++ = param; + *i++ = value; + + if (ioctl(drv->ioctl_sock, PRISM2_IOCTL_PRISM2_PARAM, &iwr) < 0) { + perror("ioctl[PRISM2_IOCTL_PRISM2_PARAM]"); + return -1; + } + + return 0; +} + + +static int hostap_set_ieee8021x(void *priv, struct wpa_bss_params *params) +{ + struct hostap_driver_data *drv = priv; + int enabled = params->enabled; + + /* enable kernel driver support for IEEE 802.1X */ + if (hostap_ioctl_prism2param(drv, PRISM2_PARAM_IEEE_802_1X, enabled)) { + printf("Could not setup IEEE 802.1X support in kernel driver." + "\n"); + return -1; + } + + if (!enabled) + return 0; + + /* use host driver implementation of encryption to allow + * individual keys and passing plaintext EAPOL frames */ + if (hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOST_DECRYPT, 1) || + hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOST_ENCRYPT, 1)) { + printf("Could not setup host-based encryption in kernel " + "driver.\n"); + return -1; + } + + return 0; +} + + +static int hostap_set_privacy(void *priv, int enabled) +{ + struct hostap_drvier_data *drv = priv; + + return hostap_ioctl_prism2param(drv, PRISM2_PARAM_PRIVACY_INVOKED, + enabled); +} + + +static int hostap_set_ssid(void *priv, const u8 *buf, int len) +{ + struct hostap_driver_data *drv = priv; + struct iwreq iwr; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.essid.flags = 1; /* SSID active */ + iwr.u.essid.pointer = (caddr_t) buf; + iwr.u.essid.length = len + 1; + + if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { + perror("ioctl[SIOCSIWESSID]"); + printf("len=%d\n", len); + return -1; + } + + return 0; +} + + +static int hostap_flush(void *priv) +{ + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param param; + + memset(¶m, 0, sizeof(param)); + param.cmd = PRISM2_HOSTAPD_FLUSH; + return hostapd_ioctl(drv, ¶m, sizeof(param)); +} + + +static int hostap_read_sta_data(void *priv, + struct hostap_sta_driver_data *data, + const u8 *addr) +{ + struct hostap_driver_data *drv = priv; + char buf[1024], line[128], *pos; + FILE *f; + unsigned long val; + + memset(data, 0, sizeof(*data)); + snprintf(buf, sizeof(buf), "/proc/net/hostap/%s/" MACSTR, + drv->iface, MAC2STR(addr)); + + f = fopen(buf, "r"); + if (!f) + return -1; + /* Need to read proc file with in one piece, so use large enough + * buffer. */ + setbuffer(f, buf, sizeof(buf)); + + while (fgets(line, sizeof(line), f)) { + pos = strchr(line, '='); + if (!pos) + continue; + *pos++ = '\0'; + val = strtoul(pos, NULL, 10); + if (strcmp(line, "rx_packets") == 0) + data->rx_packets = val; + else if (strcmp(line, "tx_packets") == 0) + data->tx_packets = val; + else if (strcmp(line, "rx_bytes") == 0) + data->rx_bytes = val; + else if (strcmp(line, "tx_bytes") == 0) + data->tx_bytes = val; + } + + fclose(f); + + return 0; +} + + +static int hostap_sta_add(void *priv, struct hostapd_sta_add_params *params) +{ + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param param; + int tx_supp_rates = 0; + size_t i; + +#define WLAN_RATE_1M BIT(0) +#define WLAN_RATE_2M BIT(1) +#define WLAN_RATE_5M5 BIT(2) +#define WLAN_RATE_11M BIT(3) + + for (i = 0; i < params->supp_rates_len; i++) { + if ((params->supp_rates[i] & 0x7f) == 2) + tx_supp_rates |= WLAN_RATE_1M; + if ((params->supp_rates[i] & 0x7f) == 4) + tx_supp_rates |= WLAN_RATE_2M; + if ((params->supp_rates[i] & 0x7f) == 11) + tx_supp_rates |= WLAN_RATE_5M5; + if ((params->supp_rates[i] & 0x7f) == 22) + tx_supp_rates |= WLAN_RATE_11M; + } + + memset(¶m, 0, sizeof(param)); + param.cmd = PRISM2_HOSTAPD_ADD_STA; + memcpy(param.sta_addr, params->addr, ETH_ALEN); + param.u.add_sta.aid = params->aid; + param.u.add_sta.capability = params->capability; + param.u.add_sta.tx_supp_rates = tx_supp_rates; + return hostapd_ioctl(drv, ¶m, sizeof(param)); +} + + +static int hostap_sta_remove(void *priv, const u8 *addr) +{ + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param param; + + hostap_sta_set_flags(drv, addr, 0, 0, ~WPA_STA_AUTHORIZED); + + memset(¶m, 0, sizeof(param)); + param.cmd = PRISM2_HOSTAPD_REMOVE_STA; + memcpy(param.sta_addr, addr, ETH_ALEN); + if (hostapd_ioctl(drv, ¶m, sizeof(param))) { + printf("Could not remove station from kernel driver.\n"); + return -1; + } + return 0; +} + + +static int hostap_get_inact_sec(void *priv, const u8 *addr) +{ + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param param; + + memset(¶m, 0, sizeof(param)); + param.cmd = PRISM2_HOSTAPD_GET_INFO_STA; + memcpy(param.sta_addr, addr, ETH_ALEN); + if (hostapd_ioctl(drv, ¶m, sizeof(param))) { + return -1; + } + + return param.u.get_info_sta.inactive_sec; +} + + +static int hostap_sta_clear_stats(void *priv, const u8 *addr) +{ + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param param; + + memset(¶m, 0, sizeof(param)); + param.cmd = PRISM2_HOSTAPD_STA_CLEAR_STATS; + memcpy(param.sta_addr, addr, ETH_ALEN); + if (hostapd_ioctl(drv, ¶m, sizeof(param))) { + return -1; + } + + return 0; +} + + +static int hostapd_ioctl_set_generic_elem(struct hostap_driver_data *drv) +{ + struct prism2_hostapd_param *param; + int res; + size_t blen, elem_len; + + elem_len = drv->generic_ie_len + drv->wps_ie_len; + blen = PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN + elem_len; + if (blen < sizeof(*param)) + blen = sizeof(*param); + + param = os_zalloc(blen); + if (param == NULL) + return -1; + + param->cmd = PRISM2_HOSTAPD_SET_GENERIC_ELEMENT; + param->u.generic_elem.len = elem_len; + if (drv->generic_ie) { + os_memcpy(param->u.generic_elem.data, drv->generic_ie, + drv->generic_ie_len); + } + if (drv->wps_ie) { + os_memcpy(¶m->u.generic_elem.data[drv->generic_ie_len], + drv->wps_ie, drv->wps_ie_len); + } + wpa_hexdump(MSG_DEBUG, "hostap: Set generic IE", + param->u.generic_elem.data, elem_len); + res = hostapd_ioctl(drv, param, blen); + + os_free(param); + + return res; +} + + +static int hostap_set_generic_elem(void *priv, + const u8 *elem, size_t elem_len) +{ + struct hostap_driver_data *drv = priv; + + os_free(drv->generic_ie); + drv->generic_ie = NULL; + drv->generic_ie_len = 0; + if (elem) { + drv->generic_ie = os_malloc(elem_len); + if (drv->generic_ie == NULL) + return -1; + os_memcpy(drv->generic_ie, elem, elem_len); + drv->generic_ie_len = elem_len; + } + + return hostapd_ioctl_set_generic_elem(drv); +} + + +static int hostap_set_ap_wps_ie(void *priv, const struct wpabuf *beacon, + const struct wpabuf *proberesp) +{ + struct hostap_driver_data *drv = priv; + + /* + * Host AP driver supports only one set of extra IEs, so we need to + * use the Probe Response IEs also for Beacon frames since they include + * more information. + */ + + os_free(drv->wps_ie); + drv->wps_ie = NULL; + drv->wps_ie_len = 0; + if (proberesp) { + drv->wps_ie = os_malloc(wpabuf_len(proberesp)); + if (drv->wps_ie == NULL) + return -1; + os_memcpy(drv->wps_ie, wpabuf_head(proberesp), + wpabuf_len(proberesp)); + drv->wps_ie_len = wpabuf_len(proberesp); + } + + return hostapd_ioctl_set_generic_elem(drv); +} + + +static void +hostapd_wireless_event_wireless_custom(struct hostap_driver_data *drv, + char *custom) +{ + wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom); + + if (strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { + char *pos; + u8 addr[ETH_ALEN]; + pos = strstr(custom, "addr="); + if (pos == NULL) { + wpa_printf(MSG_DEBUG, + "MLME-MICHAELMICFAILURE.indication " + "without sender address ignored"); + return; + } + pos += 5; + if (hwaddr_aton(pos, addr) == 0) { + union wpa_event_data data; + os_memset(&data, 0, sizeof(data)); + data.michael_mic_failure.unicast = 1; + data.michael_mic_failure.src = addr; + wpa_supplicant_event(drv->hapd, + EVENT_MICHAEL_MIC_FAILURE, &data); + } else { + wpa_printf(MSG_DEBUG, + "MLME-MICHAELMICFAILURE.indication " + "with invalid MAC address"); + } + } +} + + +static void hostapd_wireless_event_wireless(struct hostap_driver_data *drv, + char *data, int len) +{ + struct iw_event iwe_buf, *iwe = &iwe_buf; + char *pos, *end, *custom, *buf; + + pos = data; + end = data + len; + + while (pos + IW_EV_LCP_LEN <= end) { + /* Event data may be unaligned, so make a local, aligned copy + * before processing. */ + memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); + wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d", + iwe->cmd, iwe->len); + if (iwe->len <= IW_EV_LCP_LEN) + return; + + custom = pos + IW_EV_POINT_LEN; + if (drv->we_version > 18 && + (iwe->cmd == IWEVMICHAELMICFAILURE || + iwe->cmd == IWEVCUSTOM)) { + /* WE-19 removed the pointer from struct iw_point */ + char *dpos = (char *) &iwe_buf.u.data.length; + int dlen = dpos - (char *) &iwe_buf; + memcpy(dpos, pos + IW_EV_LCP_LEN, + sizeof(struct iw_event) - dlen); + } else { + memcpy(&iwe_buf, pos, sizeof(struct iw_event)); + custom += IW_EV_POINT_OFF; + } + + switch (iwe->cmd) { + case IWEVCUSTOM: + if (custom + iwe->u.data.length > end) + return; + buf = malloc(iwe->u.data.length + 1); + if (buf == NULL) + return; + memcpy(buf, custom, iwe->u.data.length); + buf[iwe->u.data.length] = '\0'; + hostapd_wireless_event_wireless_custom(drv, buf); + free(buf); + break; + } + + pos += iwe->len; + } +} + + +static void hostapd_wireless_event_rtm_newlink(void *ctx, + struct ifinfomsg *ifi, + u8 *buf, size_t len) +{ + struct hostap_driver_data *drv = ctx; + int attrlen, rta_len; + struct rtattr *attr; + + /* TODO: use ifi->ifi_index to filter out wireless events from other + * interfaces */ + + attrlen = len; + attr = (struct rtattr *) buf; + + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_WIRELESS) { + hostapd_wireless_event_wireless( + drv, ((char *) attr) + rta_len, + attr->rta_len - rta_len); + } + attr = RTA_NEXT(attr, attrlen); + } +} + + +static int hostap_get_we_version(struct hostap_driver_data *drv) +{ + struct iw_range *range; + struct iwreq iwr; + int minlen; + size_t buflen; + + drv->we_version = 0; + + /* + * Use larger buffer than struct iw_range in order to allow the + * structure to grow in the future. + */ + buflen = sizeof(struct iw_range) + 500; + range = os_zalloc(buflen); + if (range == NULL) + return -1; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) range; + iwr.u.data.length = buflen; + + minlen = ((char *) &range->enc_capa) - (char *) range + + sizeof(range->enc_capa); + + if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) { + perror("ioctl[SIOCGIWRANGE]"); + free(range); + return -1; + } else if (iwr.u.data.length >= minlen && + range->we_version_compiled >= 18) { + wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d " + "WE(source)=%d enc_capa=0x%x", + range->we_version_compiled, + range->we_version_source, + range->enc_capa); + drv->we_version = range->we_version_compiled; + } + + free(range); + return 0; +} + + +static int hostap_wireless_event_init(struct hostap_driver_data *drv) +{ + struct netlink_config *cfg; + + hostap_get_we_version(drv); + + cfg = os_zalloc(sizeof(*cfg)); + if (cfg == NULL) + return -1; + cfg->ctx = drv; + cfg->newlink_cb = hostapd_wireless_event_rtm_newlink; + drv->netlink = netlink_init(cfg); + if (drv->netlink == NULL) { + os_free(cfg); + return -1; + } + + return 0; +} + + +static void * hostap_init(struct hostapd_data *hapd, + struct wpa_init_params *params) +{ + struct hostap_driver_data *drv; + + drv = os_zalloc(sizeof(struct hostap_driver_data)); + if (drv == NULL) { + printf("Could not allocate memory for hostapd driver data\n"); + return NULL; + } + + drv->hapd = hapd; + drv->ioctl_sock = drv->sock = -1; + memcpy(drv->iface, params->ifname, sizeof(drv->iface)); + + drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->ioctl_sock < 0) { + perror("socket[PF_INET,SOCK_DGRAM]"); + free(drv); + return NULL; + } + + if (hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOSTAPD, 1)) { + printf("Could not enable hostapd mode for interface %s\n", + drv->iface); + close(drv->ioctl_sock); + free(drv); + return NULL; + } + + if (hostap_init_sockets(drv, params->own_addr) || + hostap_wireless_event_init(drv)) { + close(drv->ioctl_sock); + free(drv); + return NULL; + } + + return drv; +} + + +static void hostap_driver_deinit(void *priv) +{ + struct hostap_driver_data *drv = priv; + + netlink_deinit(drv->netlink); + (void) hostap_set_iface_flags(drv, 0); + (void) hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOSTAPD, 0); + (void) hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOSTAPD_STA, 0); + + if (drv->ioctl_sock >= 0) + close(drv->ioctl_sock); + + if (drv->sock >= 0) + close(drv->sock); + + os_free(drv->generic_ie); + os_free(drv->wps_ie); + + free(drv); +} + + +static int hostap_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, + int reason) +{ + struct hostap_driver_data *drv = priv; + struct ieee80211_mgmt mgmt; + + memset(&mgmt, 0, sizeof(mgmt)); + mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_DEAUTH); + memcpy(mgmt.da, addr, ETH_ALEN); + memcpy(mgmt.sa, own_addr, ETH_ALEN); + memcpy(mgmt.bssid, own_addr, ETH_ALEN); + mgmt.u.deauth.reason_code = host_to_le16(reason); + return hostap_send_mlme(drv, (u8 *) &mgmt, IEEE80211_HDRLEN + + sizeof(mgmt.u.deauth)); +} + + +static int hostap_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, + int reason) +{ + struct hostap_driver_data *drv = priv; + struct ieee80211_mgmt mgmt; + + memset(&mgmt, 0, sizeof(mgmt)); + mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_DISASSOC); + memcpy(mgmt.da, addr, ETH_ALEN); + memcpy(mgmt.sa, own_addr, ETH_ALEN); + memcpy(mgmt.bssid, own_addr, ETH_ALEN); + mgmt.u.disassoc.reason_code = host_to_le16(reason); + return hostap_send_mlme(drv, (u8 *) &mgmt, IEEE80211_HDRLEN + + sizeof(mgmt.u.disassoc)); +} + + +static struct hostapd_hw_modes * hostap_get_hw_feature_data(void *priv, + u16 *num_modes, + u16 *flags) +{ + struct hostapd_hw_modes *mode; + int i, clen, rlen; + const short chan2freq[14] = { + 2412, 2417, 2422, 2427, 2432, 2437, 2442, + 2447, 2452, 2457, 2462, 2467, 2472, 2484 + }; + + mode = os_zalloc(sizeof(struct hostapd_hw_modes)); + if (mode == NULL) + return NULL; + + *num_modes = 1; + *flags = 0; + + mode->mode = HOSTAPD_MODE_IEEE80211B; + mode->num_channels = 14; + mode->num_rates = 4; + + clen = mode->num_channels * sizeof(struct hostapd_channel_data); + rlen = mode->num_rates * sizeof(int); + + mode->channels = os_zalloc(clen); + mode->rates = os_zalloc(rlen); + if (mode->channels == NULL || mode->rates == NULL) { + os_free(mode->channels); + os_free(mode->rates); + os_free(mode); + return NULL; + } + + for (i = 0; i < 14; i++) { + mode->channels[i].chan = i + 1; + mode->channels[i].freq = chan2freq[i]; + /* TODO: Get allowed channel list from the driver */ + if (i >= 11) + mode->channels[i].flag = HOSTAPD_CHAN_DISABLED; + } + + mode->rates[0] = 10; + mode->rates[1] = 20; + mode->rates[2] = 55; + mode->rates[3] = 110; + + return mode; +} + +#else /* HOSTAPD */ + struct wpa_driver_hostap_data { void *wext; /* private data for driver_wext */ void *ctx; @@ -32,6 +1125,9 @@ struct wpa_driver_hostap_data { }; +static int wpa_driver_hostap_set_auth_alg(void *priv, int auth_alg); + + static int hostapd_ioctl(struct wpa_driver_hostap_data *drv, struct prism2_hostapd_param *param, int len, int show_err) @@ -45,7 +1141,7 @@ static int hostapd_ioctl(struct wpa_driver_hostap_data *drv, if (ioctl(drv->sock, PRISM2_IOCTL_HOSTAPD, &iwr) < 0) { int ret = errno; - if (show_err) + if (show_err) perror("ioctl[PRISM2_IOCTL_HOSTAPD]"); return ret; } @@ -147,9 +1243,10 @@ static void show_set_key_error(struct prism2_hostapd_param *param) } -static int wpa_driver_hostap_set_key(void *priv, wpa_alg alg, - const u8 *addr, int key_idx, - int set_tx, const u8 *seq, size_t seq_len, +static int wpa_driver_hostap_set_key(const char *ifname, void *priv, + enum wpa_alg alg, const u8 *addr, + int key_idx, int set_tx, + const u8 *seq, size_t seq_len, const u8 *key, size_t key_len) { struct wpa_driver_hostap_data *drv = priv; @@ -232,14 +1329,6 @@ static int wpa_driver_hostap_set_countermeasures(void *priv, int enabled) } -static int wpa_driver_hostap_set_drop_unencrypted(void *priv, int enabled) -{ - struct wpa_driver_hostap_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); - return prism2param(drv, PRISM2_PARAM_DROP_UNENCRYPTED, enabled); -} - - static int wpa_driver_hostap_reset(struct wpa_driver_hostap_data *drv, int type) { @@ -315,6 +1404,11 @@ wpa_driver_hostap_associate(void *priv, wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + if (prism2param(drv, PRISM2_PARAM_DROP_UNENCRYPTED, + params->drop_unencrypted) < 0) + ret = -1; + if (wpa_driver_hostap_set_auth_alg(drv, params->auth_alg) < 0) + ret = -1; if (params->mode != drv->current_mode) { /* At the moment, Host AP driver requires host_roaming=2 for * infrastructure mode and host_roaming=0 for adhoc. */ @@ -365,18 +1459,21 @@ wpa_driver_hostap_associate(void *priv, } -static int wpa_driver_hostap_scan(void *priv, const u8 *ssid, size_t ssid_len) +static int wpa_driver_hostap_scan(void *priv, + struct wpa_driver_scan_params *params) { struct wpa_driver_hostap_data *drv = priv; struct prism2_hostapd_param param; int ret; + const u8 *ssid = params->ssids[0].ssid; + size_t ssid_len = params->ssids[0].ssid_len; if (ssid == NULL) { /* Use standard Linux Wireless Extensions ioctl if possible * because some drivers using hostap code in wpa_supplicant * might not support Host AP specific scan request (with SSID * info). */ - return wpa_driver_wext_scan(drv->wext, ssid, ssid_len); + return wpa_driver_wext_scan(drv->wext, params); } if (ssid_len > 32) @@ -404,11 +1501,11 @@ static int wpa_driver_hostap_set_auth_alg(void *priv, int auth_alg) struct wpa_driver_hostap_data *drv = priv; int algs = 0; - if (auth_alg & AUTH_ALG_OPEN_SYSTEM) + if (auth_alg & WPA_AUTH_ALG_OPEN) algs |= 1; - if (auth_alg & AUTH_ALG_SHARED_KEY) + if (auth_alg & WPA_AUTH_ALG_SHARED) algs |= 2; - if (auth_alg & AUTH_ALG_LEAP) + if (auth_alg & WPA_AUTH_ALG_LEAP) algs |= 4; if (algs == 0) algs = 1; /* at least one algorithm should be set */ @@ -479,6 +1576,8 @@ static void * wpa_driver_hostap_init(void *ctx, const char *ifname) wpa_driver_wext_alternative_ifindex(drv->wext, ifname2); } + wpa_driver_hostap_set_wpa(drv, 1); + return drv; } @@ -486,28 +1585,51 @@ static void * wpa_driver_hostap_init(void *ctx, const char *ifname) static void wpa_driver_hostap_deinit(void *priv) { struct wpa_driver_hostap_data *drv = priv; + wpa_driver_hostap_set_wpa(drv, 0); wpa_driver_wext_deinit(drv->wext); close(drv->sock); os_free(drv); } +#endif /* HOSTAPD */ + const struct wpa_driver_ops wpa_driver_hostap_ops = { .name = "hostap", .desc = "Host AP driver (Intersil Prism2/2.5/3)", + .set_key = wpa_driver_hostap_set_key, +#ifdef HOSTAPD + .hapd_init = hostap_init, + .hapd_deinit = hostap_driver_deinit, + .set_ieee8021x = hostap_set_ieee8021x, + .set_privacy = hostap_set_privacy, + .get_seqnum = hostap_get_seqnum, + .flush = hostap_flush, + .set_generic_elem = hostap_set_generic_elem, + .read_sta_data = hostap_read_sta_data, + .hapd_send_eapol = hostap_send_eapol, + .sta_set_flags = hostap_sta_set_flags, + .sta_deauth = hostap_sta_deauth, + .sta_disassoc = hostap_sta_disassoc, + .sta_remove = hostap_sta_remove, + .hapd_set_ssid = hostap_set_ssid, + .send_mlme = hostap_send_mlme, + .sta_add = hostap_sta_add, + .get_inact_sec = hostap_get_inact_sec, + .sta_clear_stats = hostap_sta_clear_stats, + .get_hw_feature_data = hostap_get_hw_feature_data, + .set_ap_wps_ie = hostap_set_ap_wps_ie, +#else /* HOSTAPD */ .get_bssid = wpa_driver_hostap_get_bssid, .get_ssid = wpa_driver_hostap_get_ssid, - .set_wpa = wpa_driver_hostap_set_wpa, - .set_key = wpa_driver_hostap_set_key, .set_countermeasures = wpa_driver_hostap_set_countermeasures, - .set_drop_unencrypted = wpa_driver_hostap_set_drop_unencrypted, - .scan = wpa_driver_hostap_scan, + .scan2 = wpa_driver_hostap_scan, .get_scan_results2 = wpa_driver_hostap_get_scan_results, .deauthenticate = wpa_driver_hostap_deauthenticate, .disassociate = wpa_driver_hostap_disassociate, .associate = wpa_driver_hostap_associate, - .set_auth_alg = wpa_driver_hostap_set_auth_alg, .init = wpa_driver_hostap_init, .deinit = wpa_driver_hostap_deinit, .set_operstate = wpa_driver_hostap_set_operstate, +#endif /* HOSTAPD */ }; diff --git a/src/drivers/driver_hostap.h b/src/drivers/driver_hostap.h index a2508ed924f68..66b2bb39b849c 100644 --- a/src/drivers/driver_hostap.h +++ b/src/drivers/driver_hostap.h @@ -1,6 +1,6 @@ /* - * WPA Supplicant - driver interaction with Linux Host AP driver - * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * Driver interaction with Linux Host AP driver + * Copyright (c) 2002-2006, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -15,10 +15,32 @@ #ifndef HOSTAP_DRIVER_H #define HOSTAP_DRIVER_H +/* netdevice private ioctls (used, e.g., with iwpriv from user space) */ + +/* New wireless extensions API - SET/GET convention (even ioctl numbers are + * root only) + */ #define PRISM2_IOCTL_PRISM2_PARAM (SIOCIWFIRSTPRIV + 0) +#define PRISM2_IOCTL_GET_PRISM2_PARAM (SIOCIWFIRSTPRIV + 1) +#define PRISM2_IOCTL_WRITEMIF (SIOCIWFIRSTPRIV + 2) +#define PRISM2_IOCTL_READMIF (SIOCIWFIRSTPRIV + 3) +#define PRISM2_IOCTL_MONITOR (SIOCIWFIRSTPRIV + 4) #define PRISM2_IOCTL_RESET (SIOCIWFIRSTPRIV + 6) +#define PRISM2_IOCTL_INQUIRE (SIOCIWFIRSTPRIV + 8) +#define PRISM2_IOCTL_WDS_ADD (SIOCIWFIRSTPRIV + 10) +#define PRISM2_IOCTL_WDS_DEL (SIOCIWFIRSTPRIV + 12) +#define PRISM2_IOCTL_SET_RID_WORD (SIOCIWFIRSTPRIV + 14) +#define PRISM2_IOCTL_MACCMD (SIOCIWFIRSTPRIV + 16) +#define PRISM2_IOCTL_ADDMAC (SIOCIWFIRSTPRIV + 18) +#define PRISM2_IOCTL_DELMAC (SIOCIWFIRSTPRIV + 20) +#define PRISM2_IOCTL_KICKMAC (SIOCIWFIRSTPRIV + 22) + +/* following are not in SIOCGIWPRIV list; check permission in the driver code + */ +#define PRISM2_IOCTL_DOWNLOAD (SIOCDEVPRIVATE + 13) #define PRISM2_IOCTL_HOSTAPD (SIOCDEVPRIVATE + 14) + /* PRISM2_IOCTL_PRISM2_PARAM ioctl() subtypes: */ enum { /* PRISM2_PARAM_PTYPE = 1, */ /* REMOVED 2003-10-22 */ @@ -63,6 +85,47 @@ enum { PRISM2_PARAM_SCAN_CHANNEL_MASK = 40, }; +enum { HOSTAP_ANTSEL_DO_NOT_TOUCH = 0, HOSTAP_ANTSEL_DIVERSITY = 1, + HOSTAP_ANTSEL_LOW = 2, HOSTAP_ANTSEL_HIGH = 3 }; + + +/* PRISM2_IOCTL_MACCMD ioctl() subcommands: */ +enum { AP_MAC_CMD_POLICY_OPEN = 0, AP_MAC_CMD_POLICY_ALLOW = 1, + AP_MAC_CMD_POLICY_DENY = 2, AP_MAC_CMD_FLUSH = 3, + AP_MAC_CMD_KICKALL = 4 }; + + +/* PRISM2_IOCTL_DOWNLOAD ioctl() dl_cmd: */ +enum { + PRISM2_DOWNLOAD_VOLATILE = 1 /* RAM */, + /* Note! Old versions of prism2_srec have a fatal error in CRC-16 + * calculation, which will corrupt all non-volatile downloads. + * PRISM2_DOWNLOAD_NON_VOLATILE used to be 2, but it is now 3 to + * prevent use of old versions of prism2_srec for non-volatile + * download. */ + PRISM2_DOWNLOAD_NON_VOLATILE = 3 /* FLASH */, + PRISM2_DOWNLOAD_VOLATILE_GENESIS = 4 /* RAM in Genesis mode */, + /* Persistent versions of volatile download commands (keep firmware + * data in memory and automatically re-download after hw_reset */ + PRISM2_DOWNLOAD_VOLATILE_PERSISTENT = 5, + PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT = 6, +}; + +struct prism2_download_param { + u32 dl_cmd; + u32 start_addr; + u32 num_areas; + struct prism2_download_area { + u32 addr; /* wlan card address */ + u32 len; + caddr_t ptr; /* pointer to data in user space */ + } data[0]; +}; + +#define PRISM2_MAX_DOWNLOAD_AREA_LEN 131072 +#define PRISM2_MAX_DOWNLOAD_LEN 262144 + + /* PRISM2_IOCTL_HOSTAPD ioctl() cmd: */ enum { PRISM2_HOSTAPD_FLUSH = 1, @@ -140,8 +203,8 @@ struct prism2_hostapd_param { } u; }; -#define HOSTAP_CRYPT_FLAG_SET_TX_KEY 0x01 -#define HOSTAP_CRYPT_FLAG_PERMANENT 0x02 +#define HOSTAP_CRYPT_FLAG_SET_TX_KEY BIT(0) +#define HOSTAP_CRYPT_FLAG_PERMANENT BIT(1) #define HOSTAP_CRYPT_ERR_UNKNOWN_ALG 2 #define HOSTAP_CRYPT_ERR_UNKNOWN_ADDR 3 diff --git a/src/drivers/driver_iphone.m b/src/drivers/driver_iphone.m index 8e64ffdcc08a1..8213fdacc32e8 100644 --- a/src/drivers/driver_iphone.m +++ b/src/drivers/driver_iphone.m @@ -20,7 +20,7 @@ #include "common.h" #include "driver.h" #include "eloop.h" -#include "ieee802_11_defs.h" +#include "common/ieee802_11_defs.h" #include "MobileApple80211.h" diff --git a/src/drivers/driver_ipw.c b/src/drivers/driver_ipw.c index 3c19cccf46405..77984f9e5c4cc 100644 --- a/src/drivers/driver_ipw.c +++ b/src/drivers/driver_ipw.c @@ -93,6 +93,8 @@ struct ipw_param { /* end of ipw2100.c and ipw2200.c code */ +static int wpa_driver_ipw_set_auth_alg(void *priv, int auth_alg); + static int ipw_ioctl(struct wpa_driver_ipw_data *drv, struct ipw_param *param, int len, int show_err) { @@ -213,10 +215,11 @@ static int wpa_driver_ipw_set_wpa(void *priv, int enabled) } -static int wpa_driver_ipw_set_key(void *priv, wpa_alg alg, - const u8 *addr, int key_idx, int set_tx, - const u8 *seq, size_t seq_len, - const u8 *key, size_t key_len) +static int wpa_driver_ipw_set_key(const char *ifname, void *priv, + enum wpa_alg alg, const u8 *addr, + int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) { struct wpa_driver_ipw_data *drv = priv; struct ipw_param *param; @@ -318,6 +321,11 @@ wpa_driver_ipw_associate(void *priv, struct wpa_driver_associate_params *params) int ret = 0; int unencrypted_eapol; + if (wpa_driver_ipw_set_auth_alg(drv, params->auth_alg) < 0) + ret = -1; + if (wpa_driver_ipw_set_drop_unencrypted(drv, params->drop_unencrypted) + < 0) + ret = -1; if (ipw_set_wpa_ie(drv, params->wpa_ie, params->wpa_ie_len) < 0) ret = -1; if (wpa_driver_wext_set_ssid(drv->wext, params->ssid, @@ -347,11 +355,11 @@ static int wpa_driver_ipw_set_auth_alg(void *priv, int auth_alg) struct wpa_driver_ipw_data *drv = priv; int algs = 0; - if (auth_alg & AUTH_ALG_OPEN_SYSTEM) + if (auth_alg & WPA_AUTH_ALG_OPEN) algs |= 1; - if (auth_alg & AUTH_ALG_SHARED_KEY) + if (auth_alg & WPA_AUTH_ALG_SHARED) algs |= 2; - if (auth_alg & AUTH_ALG_LEAP) + if (auth_alg & WPA_AUTH_ALG_LEAP) algs |= 4; if (algs == 0) algs = 1; /* at least one algorithm should be set */ @@ -375,10 +383,11 @@ static int wpa_driver_ipw_get_ssid(void *priv, u8 *ssid) } -static int wpa_driver_ipw_scan(void *priv, const u8 *ssid, size_t ssid_len) +static int wpa_driver_ipw_scan(void *priv, + struct wpa_driver_scan_params *params) { struct wpa_driver_ipw_data *drv = priv; - return wpa_driver_wext_scan(drv->wext, ssid, ssid_len); + return wpa_driver_wext_scan(drv->wext, params); } @@ -428,6 +437,8 @@ static void * wpa_driver_ipw_init(void *ctx, const char *ifname) return NULL; } + wpa_driver_ipw_set_wpa(drv, 1); + return drv; } @@ -435,6 +446,7 @@ static void * wpa_driver_ipw_init(void *ctx, const char *ifname) static void wpa_driver_ipw_deinit(void *priv) { struct wpa_driver_ipw_data *drv = priv; + wpa_driver_ipw_set_wpa(drv, 0); wpa_driver_wext_deinit(drv->wext); close(drv->sock); os_free(drv); @@ -447,16 +459,13 @@ const struct wpa_driver_ops wpa_driver_ipw_ops = { "or newer)", .get_bssid = wpa_driver_ipw_get_bssid, .get_ssid = wpa_driver_ipw_get_ssid, - .set_wpa = wpa_driver_ipw_set_wpa, .set_key = wpa_driver_ipw_set_key, .set_countermeasures = wpa_driver_ipw_set_countermeasures, - .set_drop_unencrypted = wpa_driver_ipw_set_drop_unencrypted, - .scan = wpa_driver_ipw_scan, + .scan2 = wpa_driver_ipw_scan, .get_scan_results2 = wpa_driver_ipw_get_scan_results, .deauthenticate = wpa_driver_ipw_deauthenticate, .disassociate = wpa_driver_ipw_disassociate, .associate = wpa_driver_ipw_associate, - .set_auth_alg = wpa_driver_ipw_set_auth_alg, .init = wpa_driver_ipw_init, .deinit = wpa_driver_ipw_deinit, .set_operstate = wpa_driver_ipw_set_operstate, diff --git a/src/drivers/driver_madwifi.c b/src/drivers/driver_madwifi.c index 7521037234ce5..8687404db9026 100644 --- a/src/drivers/driver_madwifi.c +++ b/src/drivers/driver_madwifi.c @@ -1,7 +1,8 @@ /* * WPA Supplicant - driver interaction with MADWIFI 802.11 driver * Copyright (c) 2004, Sam Leffler <sam@errno.com> - * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004, Video54 Technologies + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -12,9 +13,10 @@ * * See README and COPYING for more details. * - * Please note that madwifi supports WPA configuration via Linux wireless - * extensions and if the kernel includes support for this, driver_wext.c should - * be used instead of this driver wrapper. + * While this driver wrapper supports both AP (hostapd) and station + * (wpa_supplicant) operations, the station side is deprecated and + * driver_wext.c should be used instead. This driver wrapper should only be + * used with hostapd for AP mode functionality. */ #include "includes.h" @@ -24,7 +26,7 @@ #include "driver.h" #include "driver_wext.h" #include "eloop.h" -#include "ieee802_11_defs.h" +#include "common/ieee802_11_defs.h" #include "wireless_copy.h" /* @@ -42,12 +44,1236 @@ #include <net80211/ieee80211_crypto.h> #include <net80211/ieee80211_ioctl.h> +#ifdef CONFIG_WPS +#ifdef IEEE80211_IOCTL_FILTERFRAME +#include <netpacket/packet.h> + +#ifndef ETH_P_80211_RAW +#define ETH_P_80211_RAW 0x0019 +#endif +#endif /* IEEE80211_IOCTL_FILTERFRAME */ +#endif /* CONFIG_WPS */ + +/* + * Avoid conflicts with hostapd definitions by undefining couple of defines + * from madwifi header files. + */ +#undef RSN_VERSION +#undef WPA_VERSION +#undef WPA_OUI_TYPE +#undef WME_OUI_TYPE + #ifdef IEEE80211_IOCTL_SETWMMPARAMS /* Assume this is built against madwifi-ng */ #define MADWIFI_NG #endif /* IEEE80211_IOCTL_SETWMMPARAMS */ + +#ifdef HOSTAPD + +#include "priv_netlink.h" +#include "netlink.h" +#include "linux_ioctl.h" +#include "l2_packet/l2_packet.h" + + +struct madwifi_driver_data { + struct hostapd_data *hapd; /* back pointer */ + + char iface[IFNAMSIZ + 1]; + int ifindex; + struct l2_packet_data *sock_xmit; /* raw packet xmit socket */ + struct l2_packet_data *sock_recv; /* raw packet recv socket */ + int ioctl_sock; /* socket for ioctl() use */ + struct netlink_data *netlink; + int we_version; + u8 acct_mac[ETH_ALEN]; + struct hostap_sta_driver_data acct_data; + + struct l2_packet_data *sock_raw; /* raw 802.11 management frames */ +}; + +static int madwifi_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, + int reason_code); + +static int +set80211priv(struct madwifi_driver_data *drv, int op, void *data, int len) +{ + struct iwreq iwr; + int do_inline = len < IFNAMSIZ; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); +#ifdef IEEE80211_IOCTL_FILTERFRAME + /* FILTERFRAME must be NOT inline, regardless of size. */ + if (op == IEEE80211_IOCTL_FILTERFRAME) + do_inline = 0; +#endif /* IEEE80211_IOCTL_FILTERFRAME */ + if (op == IEEE80211_IOCTL_SET_APPIEBUF) + do_inline = 0; + if (do_inline) { + /* + * Argument data fits inline; put it there. + */ + memcpy(iwr.u.name, data, len); + } else { + /* + * Argument data too big for inline transfer; setup a + * parameter block instead; the kernel will transfer + * the data for the driver. + */ + iwr.u.data.pointer = data; + iwr.u.data.length = len; + } + + if (ioctl(drv->ioctl_sock, op, &iwr) < 0) { +#ifdef MADWIFI_NG + int first = IEEE80211_IOCTL_SETPARAM; + static const char *opnames[] = { + "ioctl[IEEE80211_IOCTL_SETPARAM]", + "ioctl[IEEE80211_IOCTL_GETPARAM]", + "ioctl[IEEE80211_IOCTL_SETMODE]", + "ioctl[IEEE80211_IOCTL_GETMODE]", + "ioctl[IEEE80211_IOCTL_SETWMMPARAMS]", + "ioctl[IEEE80211_IOCTL_GETWMMPARAMS]", + "ioctl[IEEE80211_IOCTL_SETCHANLIST]", + "ioctl[IEEE80211_IOCTL_GETCHANLIST]", + "ioctl[IEEE80211_IOCTL_CHANSWITCH]", + "ioctl[IEEE80211_IOCTL_GET_APPIEBUF]", + "ioctl[IEEE80211_IOCTL_SET_APPIEBUF]", + "ioctl[IEEE80211_IOCTL_GETSCANRESULTS]", + "ioctl[IEEE80211_IOCTL_FILTERFRAME]", + "ioctl[IEEE80211_IOCTL_GETCHANINFO]", + "ioctl[IEEE80211_IOCTL_SETOPTIE]", + "ioctl[IEEE80211_IOCTL_GETOPTIE]", + "ioctl[IEEE80211_IOCTL_SETMLME]", + NULL, + "ioctl[IEEE80211_IOCTL_SETKEY]", + NULL, + "ioctl[IEEE80211_IOCTL_DELKEY]", + NULL, + "ioctl[IEEE80211_IOCTL_ADDMAC]", + NULL, + "ioctl[IEEE80211_IOCTL_DELMAC]", + NULL, + "ioctl[IEEE80211_IOCTL_WDSMAC]", + NULL, + "ioctl[IEEE80211_IOCTL_WDSDELMAC]", + NULL, + "ioctl[IEEE80211_IOCTL_KICKMAC]", + }; +#else /* MADWIFI_NG */ + int first = IEEE80211_IOCTL_SETPARAM; + static const char *opnames[] = { + "ioctl[IEEE80211_IOCTL_SETPARAM]", + "ioctl[IEEE80211_IOCTL_GETPARAM]", + "ioctl[IEEE80211_IOCTL_SETKEY]", + "ioctl[SIOCIWFIRSTPRIV+3]", + "ioctl[IEEE80211_IOCTL_DELKEY]", + "ioctl[SIOCIWFIRSTPRIV+5]", + "ioctl[IEEE80211_IOCTL_SETMLME]", + "ioctl[SIOCIWFIRSTPRIV+7]", + "ioctl[IEEE80211_IOCTL_SETOPTIE]", + "ioctl[IEEE80211_IOCTL_GETOPTIE]", + "ioctl[IEEE80211_IOCTL_ADDMAC]", + "ioctl[SIOCIWFIRSTPRIV+11]", + "ioctl[IEEE80211_IOCTL_DELMAC]", + "ioctl[SIOCIWFIRSTPRIV+13]", + "ioctl[IEEE80211_IOCTL_CHANLIST]", + "ioctl[SIOCIWFIRSTPRIV+15]", + "ioctl[IEEE80211_IOCTL_GETRSN]", + "ioctl[SIOCIWFIRSTPRIV+17]", + "ioctl[IEEE80211_IOCTL_GETKEY]", + }; +#endif /* MADWIFI_NG */ + int idx = op - first; + if (first <= op && + idx < (int) (sizeof(opnames) / sizeof(opnames[0])) && + opnames[idx]) + perror(opnames[idx]); + else + perror("ioctl[unknown???]"); + return -1; + } + return 0; +} + +static int +set80211param(struct madwifi_driver_data *drv, int op, int arg) +{ + struct iwreq iwr; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.mode = op; + memcpy(iwr.u.name+sizeof(__u32), &arg, sizeof(arg)); + + if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_SETPARAM, &iwr) < 0) { + perror("ioctl[IEEE80211_IOCTL_SETPARAM]"); + wpa_printf(MSG_DEBUG, "%s: Failed to set parameter (op %d " + "arg %d)", __func__, op, arg); + return -1; + } + return 0; +} + +#ifndef CONFIG_NO_STDOUT_DEBUG +static const char * +ether_sprintf(const u8 *addr) +{ + static char buf[sizeof(MACSTR)]; + + if (addr != NULL) + snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr)); + else + snprintf(buf, sizeof(buf), MACSTR, 0,0,0,0,0,0); + return buf; +} +#endif /* CONFIG_NO_STDOUT_DEBUG */ + +/* + * Configure WPA parameters. + */ +static int +madwifi_configure_wpa(struct madwifi_driver_data *drv, + struct wpa_bss_params *params) +{ + int v; + + switch (params->wpa_group) { + case WPA_CIPHER_CCMP: + v = IEEE80211_CIPHER_AES_CCM; + break; + case WPA_CIPHER_TKIP: + v = IEEE80211_CIPHER_TKIP; + break; + case WPA_CIPHER_WEP104: + v = IEEE80211_CIPHER_WEP; + break; + case WPA_CIPHER_WEP40: + v = IEEE80211_CIPHER_WEP; + break; + case WPA_CIPHER_NONE: + v = IEEE80211_CIPHER_NONE; + break; + default: + wpa_printf(MSG_ERROR, "Unknown group key cipher %u", + params->wpa_group); + return -1; + } + wpa_printf(MSG_DEBUG, "%s: group key cipher=%d", __func__, v); + if (set80211param(drv, IEEE80211_PARAM_MCASTCIPHER, v)) { + printf("Unable to set group key cipher to %u\n", v); + return -1; + } + if (v == IEEE80211_CIPHER_WEP) { + /* key length is done only for specific ciphers */ + v = (params->wpa_group == WPA_CIPHER_WEP104 ? 13 : 5); + if (set80211param(drv, IEEE80211_PARAM_MCASTKEYLEN, v)) { + printf("Unable to set group key length to %u\n", v); + return -1; + } + } + + v = 0; + if (params->wpa_pairwise & WPA_CIPHER_CCMP) + v |= 1<<IEEE80211_CIPHER_AES_CCM; + if (params->wpa_pairwise & WPA_CIPHER_TKIP) + v |= 1<<IEEE80211_CIPHER_TKIP; + if (params->wpa_pairwise & WPA_CIPHER_NONE) + v |= 1<<IEEE80211_CIPHER_NONE; + wpa_printf(MSG_DEBUG, "%s: pairwise key ciphers=0x%x", __func__, v); + if (set80211param(drv, IEEE80211_PARAM_UCASTCIPHERS, v)) { + printf("Unable to set pairwise key ciphers to 0x%x\n", v); + return -1; + } + + wpa_printf(MSG_DEBUG, "%s: key management algorithms=0x%x", + __func__, params->wpa_key_mgmt); + if (set80211param(drv, IEEE80211_PARAM_KEYMGTALGS, + params->wpa_key_mgmt)) { + printf("Unable to set key management algorithms to 0x%x\n", + params->wpa_key_mgmt); + return -1; + } + + v = 0; + if (params->rsn_preauth) + v |= BIT(0); + wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x", + __func__, params->rsn_preauth); + if (set80211param(drv, IEEE80211_PARAM_RSNCAPS, v)) { + printf("Unable to set RSN capabilities to 0x%x\n", v); + return -1; + } + + wpa_printf(MSG_DEBUG, "%s: enable WPA=0x%x", __func__, params->wpa); + if (set80211param(drv, IEEE80211_PARAM_WPA, params->wpa)) { + printf("Unable to set WPA to %u\n", params->wpa); + return -1; + } + return 0; +} + +static int +madwifi_set_ieee8021x(void *priv, struct wpa_bss_params *params) +{ + struct madwifi_driver_data *drv = priv; + + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, params->enabled); + + if (!params->enabled) { + /* XXX restore state */ + return set80211param(priv, IEEE80211_PARAM_AUTHMODE, + IEEE80211_AUTH_AUTO); + } + if (!params->wpa && !params->ieee802_1x) { + hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER, + HOSTAPD_LEVEL_WARNING, "No 802.1X or WPA enabled!"); + return -1; + } + if (params->wpa && madwifi_configure_wpa(drv, params) != 0) { + hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER, + HOSTAPD_LEVEL_WARNING, "Error configuring WPA state!"); + return -1; + } + if (set80211param(priv, IEEE80211_PARAM_AUTHMODE, + (params->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) { + hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER, + HOSTAPD_LEVEL_WARNING, "Error enabling WPA/802.1X!"); + return -1; + } + + return 0; +} + +static int +madwifi_set_privacy(void *priv, int enabled) +{ + struct madwifi_driver_data *drv = priv; + + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); + + return set80211param(drv, IEEE80211_PARAM_PRIVACY, enabled); +} + +static int +madwifi_set_sta_authorized(void *priv, const u8 *addr, int authorized) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s authorized=%d", + __func__, ether_sprintf(addr), authorized); + + if (authorized) + mlme.im_op = IEEE80211_MLME_AUTHORIZE; + else + mlme.im_op = IEEE80211_MLME_UNAUTHORIZE; + mlme.im_reason = 0; + memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to %sauthorize STA " MACSTR, + __func__, authorized ? "" : "un", MAC2STR(addr)); + } + + return ret; +} + +static int +madwifi_sta_set_flags(void *priv, const u8 *addr, + int total_flags, int flags_or, int flags_and) +{ + /* For now, only support setting Authorized flag */ + if (flags_or & WPA_STA_AUTHORIZED) + return madwifi_set_sta_authorized(priv, addr, 1); + if (!(flags_and & WPA_STA_AUTHORIZED)) + return madwifi_set_sta_authorized(priv, addr, 0); + return 0; +} + +static int +madwifi_del_key(void *priv, const u8 *addr, int key_idx) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_del_key wk; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s key_idx=%d", + __func__, ether_sprintf(addr), key_idx); + + memset(&wk, 0, sizeof(wk)); + if (addr != NULL) { + memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN); + wk.idk_keyix = (u8) IEEE80211_KEYIX_NONE; + } else { + wk.idk_keyix = key_idx; + } + + ret = set80211priv(drv, IEEE80211_IOCTL_DELKEY, &wk, sizeof(wk)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to delete key (addr %s" + " key_idx %d)", __func__, ether_sprintf(addr), + key_idx); + } + + return ret; +} + +static int +wpa_driver_madwifi_set_key(const char *ifname, void *priv, enum wpa_alg alg, + const u8 *addr, int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_key wk; + u_int8_t cipher; + int ret; + + if (alg == WPA_ALG_NONE) + return madwifi_del_key(drv, addr, key_idx); + + wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%s key_idx=%d", + __func__, alg, ether_sprintf(addr), key_idx); + + if (alg == WPA_ALG_WEP) + cipher = IEEE80211_CIPHER_WEP; + else if (alg == WPA_ALG_TKIP) + cipher = IEEE80211_CIPHER_TKIP; + else if (alg == WPA_ALG_CCMP) + cipher = IEEE80211_CIPHER_AES_CCM; + else { + printf("%s: unknown/unsupported algorithm %d\n", + __func__, alg); + return -1; + } + + if (key_len > sizeof(wk.ik_keydata)) { + printf("%s: key length %lu too big\n", __func__, + (unsigned long) key_len); + return -3; + } + + memset(&wk, 0, sizeof(wk)); + wk.ik_type = cipher; + wk.ik_flags = IEEE80211_KEY_RECV | IEEE80211_KEY_XMIT; + if (addr == NULL) { + memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); + wk.ik_keyix = key_idx; + wk.ik_flags |= IEEE80211_KEY_DEFAULT; + } else { + memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); + wk.ik_keyix = IEEE80211_KEYIX_NONE; + } + wk.ik_keylen = key_len; + memcpy(wk.ik_keydata, key, key_len); + + ret = set80211priv(drv, IEEE80211_IOCTL_SETKEY, &wk, sizeof(wk)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to set key (addr %s" + " key_idx %d alg %d key_len %lu set_tx %d)", + __func__, ether_sprintf(wk.ik_macaddr), key_idx, + alg, (unsigned long) key_len, set_tx); + } + + return ret; +} + + +static int +madwifi_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx, + u8 *seq) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_key wk; + + wpa_printf(MSG_DEBUG, "%s: addr=%s idx=%d", + __func__, ether_sprintf(addr), idx); + + memset(&wk, 0, sizeof(wk)); + if (addr == NULL) + memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); + else + memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); + wk.ik_keyix = idx; + + if (set80211priv(drv, IEEE80211_IOCTL_GETKEY, &wk, sizeof(wk))) { + wpa_printf(MSG_DEBUG, "%s: Failed to get encryption data " + "(addr " MACSTR " key_idx %d)", + __func__, MAC2STR(wk.ik_macaddr), idx); + return -1; + } + +#ifdef WORDS_BIGENDIAN + { + /* + * wk.ik_keytsc is in host byte order (big endian), need to + * swap it to match with the byte order used in WPA. + */ + int i; + u8 tmp[WPA_KEY_RSC_LEN]; + memcpy(tmp, &wk.ik_keytsc, sizeof(wk.ik_keytsc)); + for (i = 0; i < WPA_KEY_RSC_LEN; i++) { + seq[i] = tmp[WPA_KEY_RSC_LEN - i - 1]; + } + } +#else /* WORDS_BIGENDIAN */ + memcpy(seq, &wk.ik_keytsc, sizeof(wk.ik_keytsc)); +#endif /* WORDS_BIGENDIAN */ + return 0; +} + + +static int +madwifi_flush(void *priv) +{ +#ifdef MADWIFI_BSD + u8 allsta[IEEE80211_ADDR_LEN]; + memset(allsta, 0xff, IEEE80211_ADDR_LEN); + return madwifi_sta_deauth(priv, NULL, allsta, + IEEE80211_REASON_AUTH_LEAVE); +#else /* MADWIFI_BSD */ + return 0; /* XXX */ +#endif /* MADWIFI_BSD */ +} + + +static int +madwifi_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data, + const u8 *addr) +{ + struct madwifi_driver_data *drv = priv; + +#ifdef MADWIFI_BSD + struct ieee80211req_sta_stats stats; + + memset(data, 0, sizeof(*data)); + + /* + * Fetch statistics for station from the system. + */ + memset(&stats, 0, sizeof(stats)); + memcpy(stats.is_u.macaddr, addr, IEEE80211_ADDR_LEN); + if (set80211priv(drv, +#ifdef MADWIFI_NG + IEEE80211_IOCTL_STA_STATS, +#else /* MADWIFI_NG */ + IEEE80211_IOCTL_GETSTASTATS, +#endif /* MADWIFI_NG */ + &stats, sizeof(stats))) { + wpa_printf(MSG_DEBUG, "%s: Failed to fetch STA stats (addr " + MACSTR ")", __func__, MAC2STR(addr)); + if (memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) { + memcpy(data, &drv->acct_data, sizeof(*data)); + return 0; + } + + printf("Failed to get station stats information element.\n"); + return -1; + } + + data->rx_packets = stats.is_stats.ns_rx_data; + data->rx_bytes = stats.is_stats.ns_rx_bytes; + data->tx_packets = stats.is_stats.ns_tx_data; + data->tx_bytes = stats.is_stats.ns_tx_bytes; + return 0; + +#else /* MADWIFI_BSD */ + + char buf[1024], line[128], *pos; + FILE *f; + unsigned long val; + + memset(data, 0, sizeof(*data)); + snprintf(buf, sizeof(buf), "/proc/net/madwifi/%s/" MACSTR, + drv->iface, MAC2STR(addr)); + + f = fopen(buf, "r"); + if (!f) { + if (memcmp(addr, drv->acct_mac, ETH_ALEN) != 0) + return -1; + memcpy(data, &drv->acct_data, sizeof(*data)); + return 0; + } + /* Need to read proc file with in one piece, so use large enough + * buffer. */ + setbuffer(f, buf, sizeof(buf)); + + while (fgets(line, sizeof(line), f)) { + pos = strchr(line, '='); + if (!pos) + continue; + *pos++ = '\0'; + val = strtoul(pos, NULL, 10); + if (strcmp(line, "rx_packets") == 0) + data->rx_packets = val; + else if (strcmp(line, "tx_packets") == 0) + data->tx_packets = val; + else if (strcmp(line, "rx_bytes") == 0) + data->rx_bytes = val; + else if (strcmp(line, "tx_bytes") == 0) + data->tx_bytes = val; + } + + fclose(f); + + return 0; +#endif /* MADWIFI_BSD */ +} + + +static int +madwifi_sta_clear_stats(void *priv, const u8 *addr) +{ +#if defined(MADWIFI_BSD) && defined(IEEE80211_MLME_CLEAR_STATS) + struct madwifi_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s", __func__, ether_sprintf(addr)); + + mlme.im_op = IEEE80211_MLME_CLEAR_STATS; + memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, + sizeof(mlme)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to clear STA stats (addr " + MACSTR ")", __func__, MAC2STR(addr)); + } + + return ret; +#else /* MADWIFI_BSD && IEEE80211_MLME_CLEAR_STATS */ + return 0; /* FIX */ +#endif /* MADWIFI_BSD && IEEE80211_MLME_CLEAR_STATS */ +} + + +static int +madwifi_set_opt_ie(void *priv, const u8 *ie, size_t ie_len) +{ + /* + * Do nothing; we setup parameters at startup that define the + * contents of the beacon information element. + */ + return 0; +} + +static int +madwifi_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, + int reason_code) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d", + __func__, ether_sprintf(addr), reason_code); + + mlme.im_op = IEEE80211_MLME_DEAUTH; + mlme.im_reason = reason_code; + memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to deauth STA (addr " MACSTR + " reason %d)", + __func__, MAC2STR(addr), reason_code); + } + + return ret; +} + +static int +madwifi_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, + int reason_code) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d", + __func__, ether_sprintf(addr), reason_code); + + mlme.im_op = IEEE80211_MLME_DISASSOC; + mlme.im_reason = reason_code; + memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to disassoc STA (addr " + MACSTR " reason %d)", + __func__, MAC2STR(addr), reason_code); + } + + return ret; +} + +#ifdef CONFIG_WPS +#ifdef IEEE80211_IOCTL_FILTERFRAME +static void madwifi_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) +{ + struct madwifi_driver_data *drv = ctx; + const struct ieee80211_mgmt *mgmt; + u16 fc; + union wpa_event_data event; + + /* Send Probe Request information to WPS processing */ + + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)) + return; + mgmt = (const struct ieee80211_mgmt *) buf; + + fc = le_to_host16(mgmt->frame_control); + if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT || + WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_PROBE_REQ) + return; + + os_memset(&event, 0, sizeof(event)); + event.rx_probe_req.sa = mgmt->sa; + event.rx_probe_req.ie = mgmt->u.probe_req.variable; + event.rx_probe_req.ie_len = + len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)); + wpa_supplicant_event(drv->hapd, EVENT_RX_PROBE_REQ, &event); +} +#endif /* IEEE80211_IOCTL_FILTERFRAME */ +#endif /* CONFIG_WPS */ + +static int madwifi_receive_probe_req(struct madwifi_driver_data *drv) +{ + int ret = 0; +#ifdef CONFIG_WPS +#ifdef IEEE80211_IOCTL_FILTERFRAME + struct ieee80211req_set_filter filt; + + wpa_printf(MSG_DEBUG, "%s Enter", __func__); + filt.app_filterype = IEEE80211_FILTER_TYPE_PROBE_REQ; + + ret = set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt, + sizeof(struct ieee80211req_set_filter)); + if (ret) + return ret; + + drv->sock_raw = l2_packet_init(drv->iface, NULL, ETH_P_80211_RAW, + madwifi_raw_receive, drv, 1); + if (drv->sock_raw == NULL) + return -1; +#endif /* IEEE80211_IOCTL_FILTERFRAME */ +#endif /* CONFIG_WPS */ + return ret; +} + +#ifdef CONFIG_WPS +static int +madwifi_set_wps_ie(void *priv, const u8 *ie, size_t len, u32 frametype) +{ + struct madwifi_driver_data *drv = priv; + u8 buf[256]; + struct ieee80211req_getset_appiebuf *beac_ie; + + wpa_printf(MSG_DEBUG, "%s buflen = %lu", __func__, + (unsigned long) len); + + beac_ie = (struct ieee80211req_getset_appiebuf *) buf; + beac_ie->app_frmtype = frametype; + beac_ie->app_buflen = len; + memcpy(&(beac_ie->app_buf[0]), ie, len); + + return set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, beac_ie, + sizeof(struct ieee80211req_getset_appiebuf) + len); +} + +static int +madwifi_set_ap_wps_ie(void *priv, const struct wpabuf *beacon, + const struct wpabuf *proberesp) +{ + if (madwifi_set_wps_ie(priv, beacon ? wpabuf_head(beacon) : NULL, + beacon ? wpabuf_len(beacon) : 0, + IEEE80211_APPIE_FRAME_BEACON) < 0) + return -1; + return madwifi_set_wps_ie(priv, + proberesp ? wpabuf_head(proberesp) : NULL, + proberesp ? wpabuf_len(proberesp) : 0, + IEEE80211_APPIE_FRAME_PROBE_RESP); +} +#else /* CONFIG_WPS */ +#define madwifi_set_ap_wps_ie NULL +#endif /* CONFIG_WPS */ + +static void +madwifi_new_sta(struct madwifi_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) +{ + struct hostapd_data *hapd = drv->hapd; + struct ieee80211req_wpaie ie; + int ielen = 0; + u8 *iebuf = NULL; + + /* + * Fetch negotiated WPA/RSN parameters from the system. + */ + memset(&ie, 0, sizeof(ie)); + memcpy(ie.wpa_macaddr, addr, IEEE80211_ADDR_LEN); + if (set80211priv(drv, IEEE80211_IOCTL_GETWPAIE, &ie, sizeof(ie))) { + wpa_printf(MSG_DEBUG, "%s: Failed to get WPA/RSN IE", + __func__); + goto no_ie; + } + wpa_hexdump(MSG_MSGDUMP, "madwifi req WPA IE", + ie.wpa_ie, IEEE80211_MAX_OPT_IE); + iebuf = ie.wpa_ie; + /* madwifi seems to return some random data if WPA/RSN IE is not set. + * Assume the IE was not included if the IE type is unknown. */ + if (iebuf[0] != WLAN_EID_VENDOR_SPECIFIC) + iebuf[1] = 0; +#ifdef MADWIFI_NG + wpa_hexdump(MSG_MSGDUMP, "madwifi req RSN IE", + ie.rsn_ie, IEEE80211_MAX_OPT_IE); + if (iebuf[1] == 0 && ie.rsn_ie[1] > 0) { + /* madwifi-ng svn #1453 added rsn_ie. Use it, if wpa_ie was not + * set. This is needed for WPA2. */ + iebuf = ie.rsn_ie; + if (iebuf[0] != WLAN_EID_RSN) + iebuf[1] = 0; + } +#endif /* MADWIFI_NG */ + + ielen = iebuf[1]; + if (ielen == 0) + iebuf = NULL; + else + ielen += 2; + +no_ie: + drv_event_assoc(hapd, addr, iebuf, ielen); + + if (memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) { + /* Cached accounting data is not valid anymore. */ + memset(drv->acct_mac, 0, ETH_ALEN); + memset(&drv->acct_data, 0, sizeof(drv->acct_data)); + } +} + +static void +madwifi_wireless_event_wireless_custom(struct madwifi_driver_data *drv, + char *custom) +{ + wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom); + + if (strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { + char *pos; + u8 addr[ETH_ALEN]; + pos = strstr(custom, "addr="); + if (pos == NULL) { + wpa_printf(MSG_DEBUG, + "MLME-MICHAELMICFAILURE.indication " + "without sender address ignored"); + return; + } + pos += 5; + if (hwaddr_aton(pos, addr) == 0) { + union wpa_event_data data; + os_memset(&data, 0, sizeof(data)); + data.michael_mic_failure.unicast = 1; + data.michael_mic_failure.src = addr; + wpa_supplicant_event(drv->hapd, + EVENT_MICHAEL_MIC_FAILURE, &data); + } else { + wpa_printf(MSG_DEBUG, + "MLME-MICHAELMICFAILURE.indication " + "with invalid MAC address"); + } + } else if (strncmp(custom, "STA-TRAFFIC-STAT", 16) == 0) { + char *key, *value; + u32 val; + key = custom; + while ((key = strchr(key, '\n')) != NULL) { + key++; + value = strchr(key, '='); + if (value == NULL) + continue; + *value++ = '\0'; + val = strtoul(value, NULL, 10); + if (strcmp(key, "mac") == 0) + hwaddr_aton(value, drv->acct_mac); + else if (strcmp(key, "rx_packets") == 0) + drv->acct_data.rx_packets = val; + else if (strcmp(key, "tx_packets") == 0) + drv->acct_data.tx_packets = val; + else if (strcmp(key, "rx_bytes") == 0) + drv->acct_data.rx_bytes = val; + else if (strcmp(key, "tx_bytes") == 0) + drv->acct_data.tx_bytes = val; + key = value; + } + } +} + +static void +madwifi_wireless_event_wireless(struct madwifi_driver_data *drv, + char *data, int len) +{ + struct iw_event iwe_buf, *iwe = &iwe_buf; + char *pos, *end, *custom, *buf; + + pos = data; + end = data + len; + + while (pos + IW_EV_LCP_LEN <= end) { + /* Event data may be unaligned, so make a local, aligned copy + * before processing. */ + memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); + wpa_printf(MSG_MSGDUMP, "Wireless event: cmd=0x%x len=%d", + iwe->cmd, iwe->len); + if (iwe->len <= IW_EV_LCP_LEN) + return; + + custom = pos + IW_EV_POINT_LEN; + if (drv->we_version > 18 && + (iwe->cmd == IWEVMICHAELMICFAILURE || + iwe->cmd == IWEVCUSTOM)) { + /* WE-19 removed the pointer from struct iw_point */ + char *dpos = (char *) &iwe_buf.u.data.length; + int dlen = dpos - (char *) &iwe_buf; + memcpy(dpos, pos + IW_EV_LCP_LEN, + sizeof(struct iw_event) - dlen); + } else { + memcpy(&iwe_buf, pos, sizeof(struct iw_event)); + custom += IW_EV_POINT_OFF; + } + + switch (iwe->cmd) { + case IWEVEXPIRED: + drv_event_disassoc(drv->hapd, + (u8 *) iwe->u.addr.sa_data); + break; + case IWEVREGISTERED: + madwifi_new_sta(drv, (u8 *) iwe->u.addr.sa_data); + break; + case IWEVCUSTOM: + if (custom + iwe->u.data.length > end) + return; + buf = malloc(iwe->u.data.length + 1); + if (buf == NULL) + return; /* XXX */ + memcpy(buf, custom, iwe->u.data.length); + buf[iwe->u.data.length] = '\0'; + madwifi_wireless_event_wireless_custom(drv, buf); + free(buf); + break; + } + + pos += iwe->len; + } +} + + +static void +madwifi_wireless_event_rtm_newlink(void *ctx, struct ifinfomsg *ifi, + u8 *buf, size_t len) +{ + struct madwifi_driver_data *drv = ctx; + int attrlen, rta_len; + struct rtattr *attr; + + if (ifi->ifi_index != drv->ifindex) + return; + + attrlen = len; + attr = (struct rtattr *) buf; + + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_WIRELESS) { + madwifi_wireless_event_wireless( + drv, ((char *) attr) + rta_len, + attr->rta_len - rta_len); + } + attr = RTA_NEXT(attr, attrlen); + } +} + + +static int +madwifi_get_we_version(struct madwifi_driver_data *drv) +{ + struct iw_range *range; + struct iwreq iwr; + int minlen; + size_t buflen; + + drv->we_version = 0; + + /* + * Use larger buffer than struct iw_range in order to allow the + * structure to grow in the future. + */ + buflen = sizeof(struct iw_range) + 500; + range = os_zalloc(buflen); + if (range == NULL) + return -1; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) range; + iwr.u.data.length = buflen; + + minlen = ((char *) &range->enc_capa) - (char *) range + + sizeof(range->enc_capa); + + if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) { + perror("ioctl[SIOCGIWRANGE]"); + free(range); + return -1; + } else if (iwr.u.data.length >= minlen && + range->we_version_compiled >= 18) { + wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d " + "WE(source)=%d enc_capa=0x%x", + range->we_version_compiled, + range->we_version_source, + range->enc_capa); + drv->we_version = range->we_version_compiled; + } + + free(range); + return 0; +} + + +static int +madwifi_wireless_event_init(struct madwifi_driver_data *drv) +{ + struct netlink_config *cfg; + + madwifi_get_we_version(drv); + + cfg = os_zalloc(sizeof(*cfg)); + if (cfg == NULL) + return -1; + cfg->ctx = drv; + cfg->newlink_cb = madwifi_wireless_event_rtm_newlink; + drv->netlink = netlink_init(cfg); + if (drv->netlink == NULL) { + os_free(cfg); + return -1; + } + + return 0; +} + + +static int +madwifi_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, + int encrypt, const u8 *own_addr) +{ + struct madwifi_driver_data *drv = priv; + unsigned char buf[3000]; + unsigned char *bp = buf; + struct l2_ethhdr *eth; + size_t len; + int status; + + /* + * Prepend the Ethernet header. If the caller left us + * space at the front we could just insert it but since + * we don't know we copy to a local buffer. Given the frequency + * and size of frames this probably doesn't matter. + */ + len = data_len + sizeof(struct l2_ethhdr); + if (len > sizeof(buf)) { + bp = malloc(len); + if (bp == NULL) { + printf("EAPOL frame discarded, cannot malloc temp " + "buffer of size %lu!\n", (unsigned long) len); + return -1; + } + } + eth = (struct l2_ethhdr *) bp; + memcpy(eth->h_dest, addr, ETH_ALEN); + memcpy(eth->h_source, own_addr, ETH_ALEN); + eth->h_proto = host_to_be16(ETH_P_EAPOL); + memcpy(eth+1, data, data_len); + + wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", bp, len); + + status = l2_packet_send(drv->sock_xmit, addr, ETH_P_EAPOL, bp, len); + + if (bp != buf) + free(bp); + return status; +} + +static void +handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) +{ + struct madwifi_driver_data *drv = ctx; + drv_event_eapol_rx(drv->hapd, src_addr, buf + sizeof(struct l2_ethhdr), + len - sizeof(struct l2_ethhdr)); +} + +static void * +madwifi_init(struct hostapd_data *hapd, struct wpa_init_params *params) +{ + struct madwifi_driver_data *drv; + struct ifreq ifr; + struct iwreq iwr; + char brname[IFNAMSIZ]; + + drv = os_zalloc(sizeof(struct madwifi_driver_data)); + if (drv == NULL) { + printf("Could not allocate memory for madwifi driver data\n"); + return NULL; + } + + drv->hapd = hapd; + drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->ioctl_sock < 0) { + perror("socket[PF_INET,SOCK_DGRAM]"); + goto bad; + } + memcpy(drv->iface, params->ifname, sizeof(drv->iface)); + + memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->iface, sizeof(ifr.ifr_name)); + if (ioctl(drv->ioctl_sock, SIOCGIFINDEX, &ifr) != 0) { + perror("ioctl(SIOCGIFINDEX)"); + goto bad; + } + drv->ifindex = ifr.ifr_ifindex; + + drv->sock_xmit = l2_packet_init(drv->iface, NULL, ETH_P_EAPOL, + handle_read, drv, 1); + if (drv->sock_xmit == NULL) + goto bad; + if (l2_packet_get_own_addr(drv->sock_xmit, params->own_addr)) + goto bad; + if (params->bridge[0]) { + wpa_printf(MSG_DEBUG, "Configure bridge %s for EAPOL traffic.", + params->bridge[0]); + drv->sock_recv = l2_packet_init(params->bridge[0], NULL, + ETH_P_EAPOL, handle_read, drv, + 1); + if (drv->sock_recv == NULL) + goto bad; + } else if (linux_br_get(brname, drv->iface) == 0) { + wpa_printf(MSG_DEBUG, "Interface in bridge %s; configure for " + "EAPOL receive", brname); + drv->sock_recv = l2_packet_init(brname, NULL, ETH_P_EAPOL, + handle_read, drv, 1); + if (drv->sock_recv == NULL) + goto bad; + } else + drv->sock_recv = drv->sock_xmit; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + + iwr.u.mode = IW_MODE_MASTER; + + if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) < 0) { + perror("ioctl[SIOCSIWMODE]"); + printf("Could not set interface to master mode!\n"); + goto bad; + } + + /* mark down during setup */ + linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0); + madwifi_set_privacy(drv, 0); /* default to no privacy */ + + madwifi_receive_probe_req(drv); + + if (madwifi_wireless_event_init(drv)) + goto bad; + + return drv; +bad: + if (drv->sock_xmit != NULL) + l2_packet_deinit(drv->sock_xmit); + if (drv->ioctl_sock >= 0) + close(drv->ioctl_sock); + if (drv != NULL) + free(drv); + return NULL; +} + + +static void +madwifi_deinit(void *priv) +{ + struct madwifi_driver_data *drv = priv; + + netlink_deinit(drv->netlink); + (void) linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0); + if (drv->ioctl_sock >= 0) + close(drv->ioctl_sock); + if (drv->sock_recv != NULL && drv->sock_recv != drv->sock_xmit) + l2_packet_deinit(drv->sock_recv); + if (drv->sock_xmit != NULL) + l2_packet_deinit(drv->sock_xmit); + if (drv->sock_raw) + l2_packet_deinit(drv->sock_raw); + free(drv); +} + +static int +madwifi_set_ssid(void *priv, const u8 *buf, int len) +{ + struct madwifi_driver_data *drv = priv; + struct iwreq iwr; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.essid.flags = 1; /* SSID active */ + iwr.u.essid.pointer = (caddr_t) buf; + iwr.u.essid.length = len + 1; + + if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { + perror("ioctl[SIOCSIWESSID]"); + printf("len=%d\n", len); + return -1; + } + return 0; +} + +static int +madwifi_get_ssid(void *priv, u8 *buf, int len) +{ + struct madwifi_driver_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.essid.pointer = (caddr_t) buf; + iwr.u.essid.length = len; + + if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) { + perror("ioctl[SIOCGIWESSID]"); + ret = -1; + } else + ret = iwr.u.essid.length; + + return ret; +} + +static int +madwifi_set_countermeasures(void *priv, int enabled) +{ + struct madwifi_driver_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + return set80211param(drv, IEEE80211_PARAM_COUNTERMEASURES, enabled); +} + +static int +madwifi_commit(void *priv) +{ + struct madwifi_driver_data *drv = priv; + return linux_set_iface_flags(drv->ioctl_sock, drv->iface, 1); +} + +#else /* HOSTAPD */ + struct wpa_driver_madwifi_data { void *wext; /* private data for driver_wext */ void *ctx; @@ -55,6 +1281,11 @@ struct wpa_driver_madwifi_data { int sock; }; +static int wpa_driver_madwifi_set_auth_alg(void *priv, int auth_alg); +static int wpa_driver_madwifi_set_probe_req_ie(void *priv, const u8 *ies, + size_t ies_len); + + static int set80211priv(struct wpa_driver_madwifi_data *drv, int op, void *data, int len, int show_err) @@ -205,7 +1436,7 @@ wpa_driver_madwifi_del_key(struct wpa_driver_madwifi_data *drv, int key_idx, } static int -wpa_driver_madwifi_set_key(void *priv, wpa_alg alg, +wpa_driver_madwifi_set_key(const char *ifname, void *priv, enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len) @@ -227,8 +1458,8 @@ wpa_driver_madwifi_set_key(void *priv, wpa_alg alg, * configuration with IEEE80211_IOCTL_SETKEY, so use * Linux wireless extensions ioctl for this. */ - return wpa_driver_wext_set_key(drv->wext, alg, addr, - key_idx, set_tx, + return wpa_driver_wext_set_key(ifname, drv->wext, alg, + addr, key_idx, set_tx, seq, seq_len, key, key_len); } @@ -303,15 +1534,6 @@ wpa_driver_madwifi_set_countermeasures(void *priv, int enabled) return set80211param(drv, IEEE80211_PARAM_COUNTERMEASURES, enabled, 1); } - -static int -wpa_driver_madwifi_set_drop_unencrypted(void *priv, int enabled) -{ - struct wpa_driver_madwifi_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); - return set80211param(drv, IEEE80211_PARAM_DROPUNENCRYPTED, enabled, 1); -} - static int wpa_driver_madwifi_deauthenticate(void *priv, const u8 *addr, int reason_code) { @@ -348,6 +1570,12 @@ wpa_driver_madwifi_associate(void *priv, wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + if (set80211param(drv, IEEE80211_PARAM_DROPUNENCRYPTED, + params->drop_unencrypted, 1) < 0) + ret = -1; + if (wpa_driver_madwifi_set_auth_alg(drv, params->auth_alg) < 0) + ret = -1; + /* * NB: Don't need to set the freq or cipher-related state as * this is implied by the bssid which is used to locate @@ -411,10 +1639,10 @@ wpa_driver_madwifi_set_auth_alg(void *priv, int auth_alg) struct wpa_driver_madwifi_data *drv = priv; int authmode; - if ((auth_alg & AUTH_ALG_OPEN_SYSTEM) && - (auth_alg & AUTH_ALG_SHARED_KEY)) + if ((auth_alg & WPA_AUTH_ALG_OPEN) && + (auth_alg & WPA_AUTH_ALG_SHARED)) authmode = IEEE80211_AUTH_AUTO; - else if (auth_alg & AUTH_ALG_SHARED_KEY) + else if (auth_alg & WPA_AUTH_ALG_SHARED) authmode = IEEE80211_AUTH_SHARED; else authmode = IEEE80211_AUTH_OPEN; @@ -423,11 +1651,16 @@ wpa_driver_madwifi_set_auth_alg(void *priv, int auth_alg) } static int -wpa_driver_madwifi_scan(void *priv, const u8 *ssid, size_t ssid_len) +wpa_driver_madwifi_scan(void *priv, struct wpa_driver_scan_params *params) { struct wpa_driver_madwifi_data *drv = priv; struct iwreq iwr; int ret = 0; + const u8 *ssid = params->ssids[0].ssid; + size_t ssid_len = params->ssids[0].ssid_len; + + wpa_driver_madwifi_set_probe_req_ie(drv, params->extra_ies, + params->extra_ies_len); os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); @@ -579,23 +1812,43 @@ static void wpa_driver_madwifi_deinit(void *priv) os_free(drv); } +#endif /* HOSTAPD */ + const struct wpa_driver_ops wpa_driver_madwifi_ops = { .name = "madwifi", .desc = "MADWIFI 802.11 support (Atheros, etc.)", + .set_key = wpa_driver_madwifi_set_key, +#ifdef HOSTAPD + .hapd_init = madwifi_init, + .hapd_deinit = madwifi_deinit, + .set_ieee8021x = madwifi_set_ieee8021x, + .set_privacy = madwifi_set_privacy, + .get_seqnum = madwifi_get_seqnum, + .flush = madwifi_flush, + .set_generic_elem = madwifi_set_opt_ie, + .sta_set_flags = madwifi_sta_set_flags, + .read_sta_data = madwifi_read_sta_driver_data, + .hapd_send_eapol = madwifi_send_eapol, + .sta_disassoc = madwifi_sta_disassoc, + .sta_deauth = madwifi_sta_deauth, + .hapd_set_ssid = madwifi_set_ssid, + .hapd_get_ssid = madwifi_get_ssid, + .hapd_set_countermeasures = madwifi_set_countermeasures, + .sta_clear_stats = madwifi_sta_clear_stats, + .commit = madwifi_commit, + .set_ap_wps_ie = madwifi_set_ap_wps_ie, +#else /* HOSTAPD */ .get_bssid = wpa_driver_madwifi_get_bssid, .get_ssid = wpa_driver_madwifi_get_ssid, - .set_key = wpa_driver_madwifi_set_key, .init = wpa_driver_madwifi_init, .deinit = wpa_driver_madwifi_deinit, .set_countermeasures = wpa_driver_madwifi_set_countermeasures, - .set_drop_unencrypted = wpa_driver_madwifi_set_drop_unencrypted, - .scan = wpa_driver_madwifi_scan, + .scan2 = wpa_driver_madwifi_scan, .get_scan_results2 = wpa_driver_madwifi_get_scan_results, .deauthenticate = wpa_driver_madwifi_deauthenticate, .disassociate = wpa_driver_madwifi_disassociate, .associate = wpa_driver_madwifi_associate, - .set_auth_alg = wpa_driver_madwifi_set_auth_alg, .set_operstate = wpa_driver_madwifi_set_operstate, - .set_probe_req_ie = wpa_driver_madwifi_set_probe_req_ie, +#endif /* HOSTAPD */ }; diff --git a/src/drivers/driver_ndis.c b/src/drivers/driver_ndis.c index a90e277ca04de..462dd81f58af3 100644 --- a/src/drivers/driver_ndis.c +++ b/src/drivers/driver_ndis.c @@ -40,7 +40,7 @@ int close(int fd); #include "common.h" #include "driver.h" #include "eloop.h" -#include "ieee802_11_defs.h" +#include "common/ieee802_11_defs.h" #include "driver_ndis.h" int wpa_driver_register_event_cb(struct wpa_driver_ndis_data *drv); @@ -353,6 +353,47 @@ typedef struct NDIS_802_11_PMKID_CANDIDATE_LIST { #endif /* OID_802_11_CAPABILITY */ +#ifndef OID_DOT11_CURRENT_OPERATION_MODE +/* Native 802.11 OIDs */ +#define OID_DOT11_NDIS_START 0x0D010300 +#define OID_DOT11_CURRENT_OPERATION_MODE (OID_DOT11_NDIS_START + 8) +#define OID_DOT11_SCAN_REQUEST (OID_DOT11_NDIS_START + 11) + +typedef enum _DOT11_BSS_TYPE { + dot11_BSS_type_infrastructure = 1, + dot11_BSS_type_independent = 2, + dot11_BSS_type_any = 3 +} DOT11_BSS_TYPE, * PDOT11_BSS_TYPE; + +typedef UCHAR DOT11_MAC_ADDRESS[6]; +typedef DOT11_MAC_ADDRESS * PDOT11_MAC_ADDRESS; + +typedef enum _DOT11_SCAN_TYPE { + dot11_scan_type_active = 1, + dot11_scan_type_passive = 2, + dot11_scan_type_auto = 3, + dot11_scan_type_forced = 0x80000000 +} DOT11_SCAN_TYPE, * PDOT11_SCAN_TYPE; + +typedef struct _DOT11_SCAN_REQUEST_V2 { + DOT11_BSS_TYPE dot11BSSType; + DOT11_MAC_ADDRESS dot11BSSID; + DOT11_SCAN_TYPE dot11ScanType; + BOOLEAN bRestrictedScan; + ULONG udot11SSIDsOffset; + ULONG uNumOfdot11SSIDs; + BOOLEAN bUseRequestIE; + ULONG uRequestIDsOffset; + ULONG uNumOfRequestIDs; + ULONG uPhyTypeInfosOffset; + ULONG uNumOfPhyTypeInfos; + ULONG uIEsOffset; + ULONG uIEsLength; + UCHAR ucBuffer[1]; +} DOT11_SCAN_REQUEST_V2, * PDOT11_SCAN_REQUEST_V2; + +#endif /* OID_DOT11_CURRENT_OPERATION_MODE */ + #ifdef CONFIG_USE_NDISUIO #ifndef _WIN32_WCE #ifdef __MINGW32_VERSION @@ -698,25 +739,42 @@ static int wpa_driver_ndis_disassociate(void *priv, const u8 *addr, } -static int wpa_driver_ndis_set_wpa(void *priv, int enabled) +static void wpa_driver_ndis_scan_timeout(void *eloop_ctx, void *timeout_ctx) { - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); - return 0; + wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); + wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); } -static void wpa_driver_ndis_scan_timeout(void *eloop_ctx, void *timeout_ctx) +static int wpa_driver_ndis_scan_native80211( + struct wpa_driver_ndis_data *drv, + struct wpa_driver_scan_params *params) { - wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); - wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); + DOT11_SCAN_REQUEST_V2 req; + int res; + + os_memset(&req, 0, sizeof(req)); + req.dot11BSSType = dot11_BSS_type_any; + os_memset(req.dot11BSSID, 0xff, ETH_ALEN); + req.dot11ScanType = dot11_scan_type_auto; + res = ndis_set_oid(drv, OID_DOT11_SCAN_REQUEST, (char *) &req, + sizeof(req)); + eloop_cancel_timeout(wpa_driver_ndis_scan_timeout, drv, drv->ctx); + eloop_register_timeout(7, 0, wpa_driver_ndis_scan_timeout, drv, + drv->ctx); + return res; } -static int wpa_driver_ndis_scan(void *priv, const u8 *ssid, size_t ssid_len) +static int wpa_driver_ndis_scan(void *priv, + struct wpa_driver_scan_params *params) { struct wpa_driver_ndis_data *drv = priv; int res; + if (drv->native80211) + return wpa_driver_ndis_scan_native80211(drv, params); + if (!drv->radio_enabled) { wpa_printf(MSG_DEBUG, "NDIS: turning radio on before the first" " scan"); @@ -734,6 +792,25 @@ static int wpa_driver_ndis_scan(void *priv, const u8 *ssid, size_t ssid_len) } +static const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie) +{ + const u8 *end, *pos; + + pos = (const u8 *) (res + 1); + end = pos + res->ie_len; + + while (pos + 1 < end) { + if (pos + 2 + pos[1] > end) + break; + if (pos[0] == ie) + return pos; + pos += 2 + pos[1]; + } + + return NULL; +} + + static struct wpa_scan_res * wpa_driver_ndis_add_scan_ssid( struct wpa_scan_res *r, NDIS_802_11_SSID *ssid) { @@ -912,7 +989,8 @@ static int wpa_driver_ndis_add_wep(struct wpa_driver_ndis_data *drv, } -static int wpa_driver_ndis_set_key(void *priv, wpa_alg alg, const u8 *addr, +static int wpa_driver_ndis_set_key(const char *ifname, void *priv, + enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len) @@ -1020,7 +1098,8 @@ wpa_driver_ndis_associate(void *priv, continue; wpa_printf(MSG_DEBUG, "NDIS: Re-setting static WEP " "key %d", i); - wpa_driver_ndis_set_key(drv, WPA_ALG_WEP, bcast, i, + wpa_driver_ndis_set_key(drv->ifname, drv, WPA_ALG_WEP, + bcast, i, i == params->wep_tx_keyidx, NULL, 0, params->wep_key[i], params->wep_key_len[i]); @@ -1028,8 +1107,8 @@ wpa_driver_ndis_associate(void *priv, } if (params->wpa_ie == NULL || params->wpa_ie_len == 0) { - if (params->auth_alg & AUTH_ALG_SHARED_KEY) { - if (params->auth_alg & AUTH_ALG_OPEN_SYSTEM) + if (params->auth_alg & WPA_AUTH_ALG_SHARED) { + if (params->auth_alg & WPA_AUTH_ALG_OPEN) auth_mode = Ndis802_11AuthModeAutoSwitch; else auth_mode = Ndis802_11AuthModeShared; @@ -2801,16 +2880,31 @@ static void * wpa_driver_ndis_init(void *ctx, const char *ifname) mode = Ndis802_11Infrastructure; if (ndis_set_oid(drv, OID_802_11_INFRASTRUCTURE_MODE, (char *) &mode, sizeof(mode)) < 0) { + char buf[8]; + int res; wpa_printf(MSG_DEBUG, "NDIS: Failed to set " "OID_802_11_INFRASTRUCTURE_MODE (%d)", (int) mode); /* Try to continue anyway */ - if (!drv->has_capability && drv->capa.enc == 0) { + res = ndis_get_oid(drv, OID_DOT11_CURRENT_OPERATION_MODE, buf, + sizeof(buf)); + if (res > 0) { + wpa_printf(MSG_INFO, "NDIS: The driver seems to use " + "Native 802.11 OIDs. These are not yet " + "fully supported."); + drv->native80211 = 1; + } else if (!drv->has_capability || drv->capa.enc == 0) { + /* + * Note: This will also happen with NDIS 6 drivers with + * Vista. + */ wpa_printf(MSG_DEBUG, "NDIS: Driver did not provide " "any wireless capabilities - assume it is " "a wired interface"); drv->wired = 1; + drv->capa.flags |= WPA_DRIVER_FLAGS_WIRED; + drv->has_capability = 1; ndis_add_multicast(drv); } } @@ -3096,19 +3190,14 @@ const struct wpa_driver_ops wpa_driver_ndis_ops = { "Windows NDIS driver", wpa_driver_ndis_get_bssid, wpa_driver_ndis_get_ssid, - wpa_driver_ndis_set_wpa, wpa_driver_ndis_set_key, wpa_driver_ndis_init, wpa_driver_ndis_deinit, NULL /* set_param */, NULL /* set_countermeasures */, - NULL /* set_drop_unencrypted */, - wpa_driver_ndis_scan, - NULL /* get_scan_results */, wpa_driver_ndis_deauthenticate, wpa_driver_ndis_disassociate, wpa_driver_ndis_associate, - NULL /* set_auth_alg */, wpa_driver_ndis_add_pmkid, wpa_driver_ndis_remove_pmkid, wpa_driver_ndis_flush_pmkid, @@ -3129,11 +3218,61 @@ const struct wpa_driver_ops wpa_driver_ndis_ops = { NULL /* update_ft_ies */, NULL /* send_ft_action */, wpa_driver_ndis_get_scan_results, - NULL /* set_probe_req_ie */, - NULL /* set_mode */, NULL /* set_country */, NULL /* global_init */, NULL /* global_deinit */, NULL /* init2 */, - wpa_driver_ndis_get_interfaces + wpa_driver_ndis_get_interfaces, + wpa_driver_ndis_scan, + NULL /* authenticate */, + NULL /* set_beacon */, + NULL /* hapd_init */, + NULL /* hapd_deinit */, + NULL /* set_ieee8021x */, + NULL /* set_privacy */, + NULL /* get_seqnum */, + NULL /* flush */, + NULL /* set_generic_elem */, + NULL /* read_sta_data */, + NULL /* hapd_send_eapol */, + NULL /* sta_deauth */, + NULL /* sta_disassoc */, + NULL /* sta_remove */, + NULL /* hapd_get_ssid */, + NULL /* hapd_set_ssid */, + NULL /* hapd_set_countermeasures */, + NULL /* sta_add */, + NULL /* get_inact_sec */, + NULL /* sta_clear_stats */, + NULL /* set_freq */, + NULL /* set_rts */, + NULL /* set_frag */, + NULL /* sta_set_flags */, + NULL /* set_rate_sets */, + NULL /* set_cts_protect */, + NULL /* set_preamble */, + NULL /* set_short_slot_time */, + NULL /* set_tx_queue_params */, + NULL /* valid_bss_mask */, + NULL /* if_add */, + NULL /* if_remove */, + NULL /* set_sta_vlan */, + NULL /* commit */, + NULL /* send_ether */, + NULL /* set_radius_acl_auth */, + NULL /* set_radius_acl_expire */, + NULL /* set_ht_params */, + NULL /* set_ap_wps_ie */, + NULL /* set_supp_port */, + NULL /* set_wds_sta */, + NULL /* send_action */, + NULL /* remain_on_channel */, + NULL /* cancel_remain_on_channel */, + NULL /* probe_req_report */, + NULL /* disable_11b_rates */, + NULL /* deinit_ap */, + NULL /* suspend */, + NULL /* resume */, + NULL /* signal_monitor */, + NULL /* send_frame */ }; diff --git a/src/drivers/driver_ndis.h b/src/drivers/driver_ndis.h index cdce4bac85abd..f263f0e435858 100644 --- a/src/drivers/driver_ndis.h +++ b/src/drivers/driver_ndis.h @@ -52,6 +52,7 @@ struct wpa_driver_ndis_data { struct ndis_pmkid_entry *pmkid; char *adapter_desc; int wired; + int native80211; int mode; int wzc_disabled; int oid_bssid_set; diff --git a/src/drivers/driver_ndiswrapper.c b/src/drivers/driver_ndiswrapper.c index b5c534a74d08b..cd2f61e468a04 100644 --- a/src/drivers/driver_ndiswrapper.c +++ b/src/drivers/driver_ndiswrapper.c @@ -33,9 +33,8 @@ struct wpa_driver_ndiswrapper_data { }; -struct wpa_key -{ - wpa_alg alg; +struct wpa_key { + enum wpa_alg alg; const u8 *addr; int key_index; int set_tx; @@ -45,17 +44,16 @@ struct wpa_key size_t key_len; }; -struct wpa_assoc_info -{ +struct wpa_assoc_info { const u8 *bssid; const u8 *ssid; size_t ssid_len; int freq; const u8 *wpa_ie; size_t wpa_ie_len; - wpa_cipher pairwise_suite; - wpa_cipher group_suite; - wpa_key_mgmt key_mgmt_suite; + enum wpa_cipher pairwise_suite; + enum wpa_cipher group_suite; + enum wpa_key_mgmt key_mgmt_suite; int auth_alg; int mode; }; @@ -73,6 +71,8 @@ struct wpa_assoc_info #define WPA_DEINIT SIOCIWFIRSTPRIV+10 #define WPA_GET_CAPA SIOCIWFIRSTPRIV+11 +static int wpa_ndiswrapper_set_auth_alg(void *priv, int auth_alg); + static int get_socket(void) { static const int families[] = { @@ -111,7 +111,8 @@ static int wpa_ndiswrapper_set_wpa(void *priv, int enabled) return ret; } -static int wpa_ndiswrapper_set_key(void *priv, wpa_alg alg, const u8 *addr, +static int wpa_ndiswrapper_set_key(const char *ifname, void *priv, + enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len) @@ -146,8 +147,8 @@ static int wpa_ndiswrapper_set_key(void *priv, wpa_alg alg, const u8 *addr, * did not associate. Try to make sure the keys are cleared so * that plaintext APs can be used in all cases. */ - wpa_driver_wext_set_key(drv->wext, alg, addr, key_idx, set_tx, - seq, seq_len, key, key_len); + wpa_driver_wext_set_key(ifname, drv->wext, alg, addr, key_idx, + set_tx, seq, seq_len, key, key_len); } return ret; @@ -223,6 +224,12 @@ wpa_ndiswrapper_associate(void *priv, struct wpa_assoc_info wpa_assoc_info; struct iwreq priv_req; + if (wpa_ndiswrapper_set_drop_unencrypted(drv, + params->drop_unencrypted) < 0) + ret = -1; + if (wpa_ndiswrapper_set_auth_alg(drv, params->auth_alg) < 0) + ret = -1; + os_memset(&priv_req, 0, sizeof(priv_req)); os_memset(&wpa_assoc_info, 0, sizeof(wpa_assoc_info)); @@ -274,10 +281,11 @@ static int wpa_ndiswrapper_get_ssid(void *priv, u8 *ssid) } -static int wpa_ndiswrapper_scan(void *priv, const u8 *ssid, size_t ssid_len) +static int wpa_ndiswrapper_scan(void *priv, + struct wpa_driver_scan_params *params) { struct wpa_driver_ndiswrapper_data *drv = priv; - return wpa_driver_wext_scan(drv->wext, ssid, ssid_len); + return wpa_driver_wext_scan(drv->wext, params); } @@ -334,6 +342,8 @@ static void * wpa_ndiswrapper_init(void *ctx, const char *ifname) return NULL; } + wpa_ndiswrapper_set_wpa(drv, 1); + return drv; } @@ -341,6 +351,7 @@ static void * wpa_ndiswrapper_init(void *ctx, const char *ifname) static void wpa_ndiswrapper_deinit(void *priv) { struct wpa_driver_ndiswrapper_data *drv = priv; + wpa_ndiswrapper_set_wpa(drv, 0); wpa_driver_wext_deinit(drv->wext); close(drv->sock); os_free(drv); @@ -350,18 +361,15 @@ static void wpa_ndiswrapper_deinit(void *priv) const struct wpa_driver_ops wpa_driver_ndiswrapper_ops = { .name = "ndiswrapper", .desc = "Linux ndiswrapper (deprecated; use wext)", - .set_wpa = wpa_ndiswrapper_set_wpa, .set_key = wpa_ndiswrapper_set_key, .set_countermeasures = wpa_ndiswrapper_set_countermeasures, - .set_drop_unencrypted = wpa_ndiswrapper_set_drop_unencrypted, .deauthenticate = wpa_ndiswrapper_deauthenticate, .disassociate = wpa_ndiswrapper_disassociate, .associate = wpa_ndiswrapper_associate, - .set_auth_alg = wpa_ndiswrapper_set_auth_alg, .get_bssid = wpa_ndiswrapper_get_bssid, .get_ssid = wpa_ndiswrapper_get_ssid, - .scan = wpa_ndiswrapper_scan, + .scan2 = wpa_ndiswrapper_scan, .get_scan_results2 = wpa_ndiswrapper_get_scan_results, .init = wpa_ndiswrapper_init, .deinit = wpa_ndiswrapper_deinit, diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 9ab6d17279a96..364158c7d8e25 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -1,6 +1,10 @@ /* - * WPA Supplicant - driver interaction with Linux nl80211/cfg80211 - * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> + * Driver interaction with Linux nl80211/cfg80211 + * Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2004, Instant802 Networks, Inc. + * Copyright (c) 2005-2006, Devicescape Software, Inc. + * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net> + * Copyright (c) 2009-2010, Atheros Communications * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -14,23 +18,30 @@ #include "includes.h" #include <sys/ioctl.h> -#include <net/if_arp.h> +#include <net/if.h> #include <netlink/genl/genl.h> #include <netlink/genl/family.h> #include <netlink/genl/ctrl.h> -#include "nl80211_copy.h" -#ifdef CONFIG_CLIENT_MLME #include <netpacket/packet.h> -#include <linux/if_ether.h> -#include "radiotap.h" -#include "radiotap_iter.h" -#endif /* CONFIG_CLIENT_MLME */ +#include <linux/filter.h> +#include "nl80211_copy.h" -#include "wireless_copy.h" #include "common.h" -#include "driver.h" #include "eloop.h" -#include "ieee802_11_defs.h" +#include "common/ieee802_11_defs.h" +#include "netlink.h" +#include "linux_ioctl.h" +#include "radiotap.h" +#include "radiotap_iter.h" +#include "driver.h" + +#ifdef CONFIG_LIBNL20 +/* libnl 2.0 compatibility code */ +#define nl_handle nl_sock +#define nl_handle_alloc_cb nl_socket_alloc_cb +#define nl_handle_destroy nl_socket_free +#endif /* CONFIG_LIBNL20 */ + #ifndef IFF_LOWER_UP #define IFF_LOWER_UP 0x10000 /* driver signals L1 up */ @@ -46,51 +57,104 @@ #define IF_OPER_UP 6 #endif +struct i802_bss { + struct wpa_driver_nl80211_data *drv; + struct i802_bss *next; + int ifindex; + char ifname[IFNAMSIZ + 1]; + unsigned int beacon_set:1; +}; struct wpa_driver_nl80211_data { void *ctx; - int wext_event_sock; - int ioctl_sock; - char ifname[IFNAMSIZ + 1]; + struct netlink_data *netlink; + int ioctl_sock; /* socket for ioctl() use */ + char brname[IFNAMSIZ]; int ifindex; int if_removed; - u8 *assoc_req_ies; - size_t assoc_req_ies_len; - u8 *assoc_resp_ies; - size_t assoc_resp_ies_len; struct wpa_driver_capa capa; int has_capability; - int we_version_compiled; - - /* for set_auth_alg fallback */ - int use_crypt; - int auth_alg_fallback; int operstate; - char mlmedev[IFNAMSIZ + 1]; - int scan_complete_events; struct nl_handle *nl_handle; + struct nl_handle *nl_handle_event; struct nl_cache *nl_cache; + struct nl_cache *nl_cache_event; struct nl_cb *nl_cb; struct genl_family *nl80211; -#ifdef CONFIG_CLIENT_MLME - int monitor_sock; /* socket for monitor */ + u8 auth_bssid[ETH_ALEN]; + u8 bssid[ETH_ALEN]; + int associated; + u8 ssid[32]; + size_t ssid_len; + int nlmode; + int ap_scan_as_station; + unsigned int assoc_freq; + + int monitor_sock; int monitor_ifidx; -#endif /* CONFIG_CLIENT_MLME */ + int probe_req_report; + int disable_11b_rates; + + unsigned int pending_remain_on_chan:1; + unsigned int added_bridge:1; + unsigned int added_if_into_bridge:1; + + u64 remain_on_chan_cookie; + u64 send_action_cookie; + + struct wpa_driver_scan_filter *filter_ssids; + size_t num_filter_ssids; + + struct i802_bss first_bss; + +#ifdef HOSTAPD + int eapol_sock; /* socket for EAPOL frames */ + + int default_if_indices[16]; + int *if_indices; + int num_if_indices; + + int last_freq; + int last_freq_ht; +#endif /* HOSTAPD */ }; static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx); static int wpa_driver_nl80211_set_mode(void *priv, int mode); -static int wpa_driver_nl80211_flush_pmkid(void *priv); -static int wpa_driver_nl80211_get_range(void *priv); -static void +static int wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv); +static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, + const u8 *addr, int cmd, u16 reason_code, + int local_state_change); +static void nl80211_remove_monitor_interface( + struct wpa_driver_nl80211_data *drv); + +#ifdef HOSTAPD +static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx); +static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx); +static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx); +static int wpa_driver_nl80211_if_remove(void *priv, + enum wpa_driver_if_type type, + const char *ifname); +#else /* HOSTAPD */ +static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) +{ + return 0; +} +#endif /* HOSTAPD */ + +static int i802_set_freq(void *priv, struct hostapd_freq_params *freq); +static void wpa_driver_nl80211_probe_req_report_timeout(void *eloop_ctx, + void *timeout_ctx); +static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv, + int ifindex, int disabled); /* nl80211 code */ @@ -116,10 +180,17 @@ static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, return NL_SKIP; } -static int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv, - struct nl_msg *msg, - int (*valid_handler)(struct nl_msg *, void *), - void *valid_data) + +static int no_seq_check(struct nl_msg *msg, void *arg) +{ + return NL_OK; +} + + +static int send_and_recv(struct wpa_driver_nl80211_data *drv, + struct nl_handle *nl_handle, struct nl_msg *msg, + int (*valid_handler)(struct nl_msg *, void *), + void *valid_data) { struct nl_cb *cb; int err = -ENOMEM; @@ -128,7 +199,7 @@ static int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv, if (!cb) goto out; - err = nl_send_auto_complete(drv->nl_handle, msg); + err = nl_send_auto_complete(nl_handle, msg); if (err < 0) goto out; @@ -143,7 +214,7 @@ static int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv, valid_handler, valid_data); while (err > 0) - nl_recvmsgs(drv->nl_handle, cb); + nl_recvmsgs(nl_handle, cb); out: nl_cb_put(cb); nlmsg_free(msg); @@ -151,6 +222,16 @@ static int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv, } +static int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv, + struct nl_msg *msg, + int (*valid_handler)(struct nl_msg *, void *), + void *valid_data) +{ + return send_and_recv(drv, drv->nl_handle, msg, valid_handler, + valid_data); +} + + struct family_data { const char *group; int id; @@ -213,752 +294,678 @@ nla_put_failure: } -static int wpa_driver_nl80211_send_oper_ifla( - struct wpa_driver_nl80211_data *drv, - int linkmode, int operstate) +static int wpa_driver_nl80211_get_bssid(void *priv, u8 *bssid) { - struct { - struct nlmsghdr hdr; - struct ifinfomsg ifinfo; - char opts[16]; - } req; - struct rtattr *rta; - static int nl_seq; - ssize_t ret; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + if (!drv->associated) + return -1; + os_memcpy(bssid, drv->bssid, ETH_ALEN); + return 0; +} - os_memset(&req, 0, sizeof(req)); - req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); - req.hdr.nlmsg_type = RTM_SETLINK; - req.hdr.nlmsg_flags = NLM_F_REQUEST; - req.hdr.nlmsg_seq = ++nl_seq; - req.hdr.nlmsg_pid = 0; +static int wpa_driver_nl80211_get_ssid(void *priv, u8 *ssid) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + if (!drv->associated) + return -1; + os_memcpy(ssid, drv->ssid, drv->ssid_len); + return drv->ssid_len; +} - req.ifinfo.ifi_family = AF_UNSPEC; - req.ifinfo.ifi_type = 0; - req.ifinfo.ifi_index = drv->ifindex; - req.ifinfo.ifi_flags = 0; - req.ifinfo.ifi_change = 0; - if (linkmode != -1) { - rta = aliasing_hide_typecast( - ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)), - struct rtattr); - rta->rta_type = IFLA_LINKMODE; - rta->rta_len = RTA_LENGTH(sizeof(char)); - *((char *) RTA_DATA(rta)) = linkmode; - req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + - RTA_LENGTH(sizeof(char)); - } - if (operstate != -1) { - rta = (struct rtattr *) - ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)); - rta->rta_type = IFLA_OPERSTATE; - rta->rta_len = RTA_LENGTH(sizeof(char)); - *((char *) RTA_DATA(rta)) = operstate; - req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + - RTA_LENGTH(sizeof(char)); - } +static void wpa_driver_nl80211_event_link(struct wpa_driver_nl80211_data *drv, + char *buf, size_t len, int del) +{ + union wpa_event_data event; - wpa_printf(MSG_DEBUG, "WEXT: Operstate: linkmode=%d, operstate=%d", - linkmode, operstate); + os_memset(&event, 0, sizeof(event)); + if (len > sizeof(event.interface_status.ifname)) + len = sizeof(event.interface_status.ifname) - 1; + os_memcpy(event.interface_status.ifname, buf, len); + event.interface_status.ievent = del ? EVENT_INTERFACE_REMOVED : + EVENT_INTERFACE_ADDED; - ret = send(drv->wext_event_sock, &req, req.hdr.nlmsg_len, 0); - if (ret < 0) { - wpa_printf(MSG_DEBUG, "WEXT: Sending operstate IFLA failed: " - "%s (assume operstate is not supported)", - strerror(errno)); + wpa_printf(MSG_DEBUG, "RTM_%sLINK, IFLA_IFNAME: Interface '%s' %s", + del ? "DEL" : "NEW", + event.interface_status.ifname, + del ? "removed" : "added"); + + if (os_strcmp(drv->first_bss.ifname, event.interface_status.ifname) == 0) { + if (del) + drv->if_removed = 1; + else + drv->if_removed = 0; } - return ret < 0 ? -1 : 0; + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event); } -static int wpa_driver_nl80211_set_auth_param( - struct wpa_driver_nl80211_data *drv, int idx, u32 value) +static int wpa_driver_nl80211_own_ifname(struct wpa_driver_nl80211_data *drv, + u8 *buf, size_t len) { - struct iwreq iwr; - int ret = 0; + int attrlen, rta_len; + struct rtattr *attr; - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - iwr.u.param.flags = idx & IW_AUTH_INDEX; - iwr.u.param.value = value; + attrlen = len; + attr = (struct rtattr *) buf; - if (ioctl(drv->ioctl_sock, SIOCSIWAUTH, &iwr) < 0) { - if (errno != EOPNOTSUPP) { - wpa_printf(MSG_DEBUG, "WEXT: SIOCSIWAUTH(param %d " - "value 0x%x) failed: %s)", - idx, value, strerror(errno)); + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_IFNAME) { + if (os_strcmp(((char *) attr) + rta_len, drv->first_bss.ifname) + == 0) + return 1; + else + break; } - ret = errno == EOPNOTSUPP ? -2 : -1; + attr = RTA_NEXT(attr, attrlen); } - return ret; + return 0; } -static int wpa_driver_nl80211_get_bssid(void *priv, u8 *bssid) +static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv, + int ifindex, u8 *buf, size_t len) { - struct wpa_driver_nl80211_data *drv = priv; - struct iwreq iwr; - int ret = 0; - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + if (drv->ifindex == ifindex) + return 1; - if (ioctl(drv->ioctl_sock, SIOCGIWAP, &iwr) < 0) { - perror("ioctl[SIOCGIWAP]"); - ret = -1; + if (drv->if_removed && wpa_driver_nl80211_own_ifname(drv, buf, len)) { + drv->first_bss.ifindex = if_nametoindex(drv->first_bss.ifname); + wpa_printf(MSG_DEBUG, "nl80211: Update ifindex for a removed " + "interface"); + wpa_driver_nl80211_finish_drv_init(drv); + return 1; } - os_memcpy(bssid, iwr.u.ap_addr.sa_data, ETH_ALEN); - return ret; + return 0; } -static int wpa_driver_nl80211_set_bssid(void *priv, const u8 *bssid) +static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, + struct ifinfomsg *ifi, + u8 *buf, size_t len) { - struct wpa_driver_nl80211_data *drv = priv; - struct iwreq iwr; - int ret = 0; - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - iwr.u.ap_addr.sa_family = ARPHRD_ETHER; - if (bssid) - os_memcpy(iwr.u.ap_addr.sa_data, bssid, ETH_ALEN); - else - os_memset(iwr.u.ap_addr.sa_data, 0, ETH_ALEN); + struct wpa_driver_nl80211_data *drv = ctx; + int attrlen, rta_len; + struct rtattr *attr; + u32 brid = 0; - if (ioctl(drv->ioctl_sock, SIOCSIWAP, &iwr) < 0) { - perror("ioctl[SIOCSIWAP]"); - ret = -1; + if (!wpa_driver_nl80211_own_ifindex(drv, ifi->ifi_index, buf, len) && + !have_ifidx(drv, ifi->ifi_index)) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore event for foreign " + "ifindex %d", ifi->ifi_index); + return; } - return ret; -} - - -static int wpa_driver_nl80211_get_ssid(void *priv, u8 *ssid) -{ - struct wpa_driver_nl80211_data *drv = priv; - struct iwreq iwr; - int ret = 0; - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - iwr.u.essid.pointer = (caddr_t) ssid; - iwr.u.essid.length = 32; + wpa_printf(MSG_DEBUG, "RTM_NEWLINK: operstate=%d ifi_flags=0x%x " + "(%s%s%s%s)", + drv->operstate, ifi->ifi_flags, + (ifi->ifi_flags & IFF_UP) ? "[UP]" : "", + (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "", + (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", + (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); + /* + * Some drivers send the association event before the operup event--in + * this case, lifting operstate in wpa_driver_nl80211_set_operstate() + * fails. This will hit us when wpa_supplicant does not need to do + * IEEE 802.1X authentication + */ + if (drv->operstate == 1 && + (ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP && + !(ifi->ifi_flags & IFF_RUNNING)) + netlink_send_oper_ifla(drv->netlink, drv->ifindex, + -1, IF_OPER_UP); - if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) { - perror("ioctl[SIOCGIWESSID]"); - ret = -1; - } else { - ret = iwr.u.essid.length; - if (ret > 32) - ret = 32; - /* Some drivers include nul termination in the SSID, so let's - * remove it here before further processing. WE-21 changes this - * to explicitly require the length _not_ to include nul - * termination. */ - if (ret > 0 && ssid[ret - 1] == '\0' && - drv->we_version_compiled < 21) - ret--; + attrlen = len; + attr = (struct rtattr *) buf; + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_IFNAME) { + wpa_driver_nl80211_event_link( + drv, + ((char *) attr) + rta_len, + attr->rta_len - rta_len, 0); + } else if (attr->rta_type == IFLA_MASTER) + brid = nla_get_u32((struct nlattr *) attr); + attr = RTA_NEXT(attr, attrlen); } - return ret; +#ifdef HOSTAPD + if (ifi->ifi_family == AF_BRIDGE && brid) { + /* device has been added to bridge */ + char namebuf[IFNAMSIZ]; + if_indextoname(brid, namebuf); + wpa_printf(MSG_DEBUG, "nl80211: Add ifindex %u for bridge %s", + brid, namebuf); + add_ifidx(drv, brid); + } +#endif /* HOSTAPD */ } -static int wpa_driver_nl80211_set_ssid(void *priv, const u8 *ssid, - size_t ssid_len) +static void wpa_driver_nl80211_event_rtm_dellink(void *ctx, + struct ifinfomsg *ifi, + u8 *buf, size_t len) { - struct wpa_driver_nl80211_data *drv = priv; - struct iwreq iwr; - int ret = 0; - char buf[33]; + struct wpa_driver_nl80211_data *drv = ctx; + int attrlen, rta_len; + struct rtattr *attr; + u32 brid = 0; - if (ssid_len > 32) - return -1; + attrlen = len; + attr = (struct rtattr *) buf; - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - /* flags: 1 = ESSID is active, 0 = not (promiscuous) */ - iwr.u.essid.flags = (ssid_len != 0); - os_memset(buf, 0, sizeof(buf)); - os_memcpy(buf, ssid, ssid_len); - iwr.u.essid.pointer = (caddr_t) buf; - if (drv->we_version_compiled < 21) { - /* For historic reasons, set SSID length to include one extra - * character, C string nul termination, even though SSID is - * really an octet string that should not be presented as a C - * string. Some Linux drivers decrement the length by one and - * can thus end up missing the last octet of the SSID if the - * length is not incremented here. WE-21 changes this to - * explicitly require the length _not_ to include nul - * termination. */ - if (ssid_len) - ssid_len++; + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_IFNAME) { + wpa_driver_nl80211_event_link( + drv, + ((char *) attr) + rta_len, + attr->rta_len - rta_len, 1); + } else if (attr->rta_type == IFLA_MASTER) + brid = nla_get_u32((struct nlattr *) attr); + attr = RTA_NEXT(attr, attrlen); } - iwr.u.essid.length = ssid_len; - if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { - perror("ioctl[SIOCSIWESSID]"); - ret = -1; +#ifdef HOSTAPD + if (ifi->ifi_family == AF_BRIDGE && brid) { + /* device has been removed from bridge */ + char namebuf[IFNAMSIZ]; + if_indextoname(brid, namebuf); + wpa_printf(MSG_DEBUG, "nl80211: Remove ifindex %u for bridge " + "%s", brid, namebuf); + del_ifidx(drv, brid); } - - return ret; +#endif /* HOSTAPD */ } -static int wpa_driver_nl80211_set_freq(void *priv, int freq) +static void mlme_event_auth(struct wpa_driver_nl80211_data *drv, + const u8 *frame, size_t len) { - struct wpa_driver_nl80211_data *drv = priv; - struct iwreq iwr; - int ret = 0; + const struct ieee80211_mgmt *mgmt; + union wpa_event_data event; - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - iwr.u.freq.m = freq * 100000; - iwr.u.freq.e = 1; + mgmt = (const struct ieee80211_mgmt *) frame; + if (len < 24 + sizeof(mgmt->u.auth)) { + wpa_printf(MSG_DEBUG, "nl80211: Too short association event " + "frame"); + return; + } - if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) { - perror("ioctl[SIOCSIWFREQ]"); - ret = -1; + os_memcpy(drv->auth_bssid, mgmt->sa, ETH_ALEN); + os_memset(&event, 0, sizeof(event)); + os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN); + event.auth.auth_type = le_to_host16(mgmt->u.auth.auth_alg); + event.auth.status_code = le_to_host16(mgmt->u.auth.status_code); + if (len > 24 + sizeof(mgmt->u.auth)) { + event.auth.ies = mgmt->u.auth.variable; + event.auth.ies_len = len - 24 - sizeof(mgmt->u.auth); } - return ret; + wpa_supplicant_event(drv->ctx, EVENT_AUTH, &event); } -static void -wpa_driver_nl80211_event_wireless_custom(void *ctx, char *custom) +static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv, + const u8 *frame, size_t len) { - union wpa_event_data data; - - wpa_printf(MSG_MSGDUMP, "WEXT: Custom wireless event: '%s'", - custom); - - os_memset(&data, 0, sizeof(data)); - /* Host AP driver */ - if (os_strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { - data.michael_mic_failure.unicast = - os_strstr(custom, " unicast ") != NULL; - /* TODO: parse parameters(?) */ - wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); - } else if (os_strncmp(custom, "ASSOCINFO(ReqIEs=", 17) == 0) { - char *spos; - int bytes; - - spos = custom + 17; - - bytes = strspn(spos, "0123456789abcdefABCDEF"); - if (!bytes || (bytes & 1)) - return; - bytes /= 2; - - data.assoc_info.req_ies = os_malloc(bytes); - if (data.assoc_info.req_ies == NULL) - return; - - data.assoc_info.req_ies_len = bytes; - hexstr2bin(spos, data.assoc_info.req_ies, bytes); - - spos += bytes * 2; + const struct ieee80211_mgmt *mgmt; + union wpa_event_data event; + u16 status; - data.assoc_info.resp_ies = NULL; - data.assoc_info.resp_ies_len = 0; + mgmt = (const struct ieee80211_mgmt *) frame; + if (len < 24 + sizeof(mgmt->u.assoc_resp)) { + wpa_printf(MSG_DEBUG, "nl80211: Too short association event " + "frame"); + return; + } - if (os_strncmp(spos, " RespIEs=", 9) == 0) { - spos += 9; + status = le_to_host16(mgmt->u.assoc_resp.status_code); + if (status != WLAN_STATUS_SUCCESS) { + os_memset(&event, 0, sizeof(event)); + if (len > 24 + sizeof(mgmt->u.assoc_resp)) { + event.assoc_reject.resp_ies = + (u8 *) mgmt->u.assoc_resp.variable; + event.assoc_reject.resp_ies_len = + len - 24 - sizeof(mgmt->u.assoc_resp); + } + event.assoc_reject.status_code = status; - bytes = strspn(spos, "0123456789abcdefABCDEF"); - if (!bytes || (bytes & 1)) - goto done; - bytes /= 2; + wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event); + return; + } - data.assoc_info.resp_ies = os_malloc(bytes); - if (data.assoc_info.resp_ies == NULL) - goto done; + drv->associated = 1; + os_memcpy(drv->bssid, mgmt->sa, ETH_ALEN); - data.assoc_info.resp_ies_len = bytes; - hexstr2bin(spos, data.assoc_info.resp_ies, bytes); - } + os_memset(&event, 0, sizeof(event)); + if (len > 24 + sizeof(mgmt->u.assoc_resp)) { + event.assoc_info.resp_ies = (u8 *) mgmt->u.assoc_resp.variable; + event.assoc_info.resp_ies_len = + len - 24 - sizeof(mgmt->u.assoc_resp); + } - wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data); + event.assoc_info.freq = drv->assoc_freq; - done: - os_free(data.assoc_info.resp_ies); - os_free(data.assoc_info.req_ies); -#ifdef CONFIG_PEERKEY - } else if (os_strncmp(custom, "STKSTART.request=", 17) == 0) { - if (hwaddr_aton(custom + 17, data.stkstart.peer)) { - wpa_printf(MSG_DEBUG, "WEXT: unrecognized " - "STKSTART.request '%s'", custom + 17); - return; - } - wpa_supplicant_event(ctx, EVENT_STKSTART, &data); -#endif /* CONFIG_PEERKEY */ - } + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event); } -static int wpa_driver_nl80211_event_wireless_michaelmicfailure( - void *ctx, const char *ev, size_t len) +static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, + enum nl80211_commands cmd, struct nlattr *status, + struct nlattr *addr, struct nlattr *req_ie, + struct nlattr *resp_ie) { - const struct iw_michaelmicfailure *mic; - union wpa_event_data data; + union wpa_event_data event; - if (len < sizeof(*mic)) - return -1; + if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) { + /* + * Avoid reporting two association events that would confuse + * the core code. + */ + wpa_printf(MSG_DEBUG, "nl80211: Ignore connect event (cmd=%d) " + "when using userspace SME", cmd); + return; + } - mic = (const struct iw_michaelmicfailure *) ev; + os_memset(&event, 0, sizeof(event)); + if (cmd == NL80211_CMD_CONNECT && + nla_get_u16(status) != WLAN_STATUS_SUCCESS) { + if (resp_ie) { + event.assoc_reject.resp_ies = nla_data(resp_ie); + event.assoc_reject.resp_ies_len = nla_len(resp_ie); + } + event.assoc_reject.status_code = nla_get_u16(status); + wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event); + return; + } - wpa_printf(MSG_DEBUG, "Michael MIC failure wireless event: " - "flags=0x%x src_addr=" MACSTR, mic->flags, - MAC2STR(mic->src_addr.sa_data)); + drv->associated = 1; + if (addr) + os_memcpy(drv->bssid, nla_data(addr), ETH_ALEN); - os_memset(&data, 0, sizeof(data)); - data.michael_mic_failure.unicast = !(mic->flags & IW_MICFAILURE_GROUP); - wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); + if (req_ie) { + event.assoc_info.req_ies = nla_data(req_ie); + event.assoc_info.req_ies_len = nla_len(req_ie); + } + if (resp_ie) { + event.assoc_info.resp_ies = nla_data(resp_ie); + event.assoc_info.resp_ies_len = nla_len(resp_ie); + } - return 0; + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event); } -static int wpa_driver_nl80211_event_wireless_pmkidcand( - struct wpa_driver_nl80211_data *drv, const char *ev, size_t len) +static void mlme_timeout_event(struct wpa_driver_nl80211_data *drv, + enum nl80211_commands cmd, struct nlattr *addr) { - const struct iw_pmkid_cand *cand; - union wpa_event_data data; - const u8 *addr; - - if (len < sizeof(*cand)) - return -1; + union wpa_event_data event; + enum wpa_event_type ev; - cand = (const struct iw_pmkid_cand *) ev; - addr = (const u8 *) cand->bssid.sa_data; + if (nla_len(addr) != ETH_ALEN) + return; - wpa_printf(MSG_DEBUG, "PMKID candidate wireless event: " - "flags=0x%x index=%d bssid=" MACSTR, cand->flags, - cand->index, MAC2STR(addr)); + wpa_printf(MSG_DEBUG, "nl80211: MLME event %d; timeout with " MACSTR, + cmd, MAC2STR((u8 *) nla_data(addr))); - os_memset(&data, 0, sizeof(data)); - os_memcpy(data.pmkid_candidate.bssid, addr, ETH_ALEN); - data.pmkid_candidate.index = cand->index; - data.pmkid_candidate.preauth = cand->flags & IW_PMKID_CAND_PREAUTH; - wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, &data); + if (cmd == NL80211_CMD_AUTHENTICATE) + ev = EVENT_AUTH_TIMED_OUT; + else if (cmd == NL80211_CMD_ASSOCIATE) + ev = EVENT_ASSOC_TIMED_OUT; + else + return; - return 0; + os_memset(&event, 0, sizeof(event)); + os_memcpy(event.timeout_event.addr, nla_data(addr), ETH_ALEN); + wpa_supplicant_event(drv->ctx, ev, &event); } -static int wpa_driver_nl80211_event_wireless_assocreqie( - struct wpa_driver_nl80211_data *drv, const char *ev, int len) +static void mlme_event_action(struct wpa_driver_nl80211_data *drv, + struct nlattr *freq, const u8 *frame, size_t len) { - if (len < 0) - return -1; + const struct ieee80211_mgmt *mgmt; + union wpa_event_data event; + u16 fc, stype; - wpa_hexdump(MSG_DEBUG, "AssocReq IE wireless event", (const u8 *) ev, - len); - os_free(drv->assoc_req_ies); - drv->assoc_req_ies = os_malloc(len); - if (drv->assoc_req_ies == NULL) { - drv->assoc_req_ies_len = 0; - return -1; + mgmt = (const struct ieee80211_mgmt *) frame; + if (len < 24) { + wpa_printf(MSG_DEBUG, "nl80211: Too short action frame"); + return; } - os_memcpy(drv->assoc_req_ies, ev, len); - drv->assoc_req_ies_len = len; - return 0; -} + fc = le_to_host16(mgmt->frame_control); + stype = WLAN_FC_GET_STYPE(fc); - -static int wpa_driver_nl80211_event_wireless_assocrespie( - struct wpa_driver_nl80211_data *drv, const char *ev, int len) -{ - if (len < 0) - return -1; - - wpa_hexdump(MSG_DEBUG, "AssocResp IE wireless event", (const u8 *) ev, - len); - os_free(drv->assoc_resp_ies); - drv->assoc_resp_ies = os_malloc(len); - if (drv->assoc_resp_ies == NULL) { - drv->assoc_resp_ies_len = 0; - return -1; - } - os_memcpy(drv->assoc_resp_ies, ev, len); - drv->assoc_resp_ies_len = len; - - return 0; + os_memset(&event, 0, sizeof(event)); + event.rx_action.da = mgmt->da; + event.rx_action.sa = mgmt->sa; + event.rx_action.bssid = mgmt->bssid; + event.rx_action.category = mgmt->u.action.category; + event.rx_action.data = &mgmt->u.action.category + 1; + event.rx_action.len = frame + len - event.rx_action.data; + if (freq) + event.rx_action.freq = nla_get_u32(freq); + wpa_supplicant_event(drv->ctx, EVENT_RX_ACTION, &event); } -static void wpa_driver_nl80211_event_assoc_ies(struct wpa_driver_nl80211_data *drv) +static void mlme_event_action_tx_status(struct wpa_driver_nl80211_data *drv, + struct nlattr *cookie, const u8 *frame, + size_t len, struct nlattr *ack) { - union wpa_event_data data; + union wpa_event_data event; + const struct ieee80211_hdr *hdr; + u16 fc; + u64 cookie_val; - if (drv->assoc_req_ies == NULL && drv->assoc_resp_ies == NULL) + if (!cookie) return; - os_memset(&data, 0, sizeof(data)); - if (drv->assoc_req_ies) { - data.assoc_info.req_ies = drv->assoc_req_ies; - drv->assoc_req_ies = NULL; - data.assoc_info.req_ies_len = drv->assoc_req_ies_len; - } - if (drv->assoc_resp_ies) { - data.assoc_info.resp_ies = drv->assoc_resp_ies; - drv->assoc_resp_ies = NULL; - data.assoc_info.resp_ies_len = drv->assoc_resp_ies_len; - } + cookie_val = nla_get_u64(cookie); + wpa_printf(MSG_DEBUG, "nl80211: Action TX status: cookie=0%llx%s", + (long long unsigned int) cookie_val, + cookie_val == drv->send_action_cookie ? + " (match)" : " (unknown)"); + if (cookie_val != drv->send_action_cookie) + return; - wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &data); + hdr = (const struct ieee80211_hdr *) frame; + fc = le_to_host16(hdr->frame_control); - os_free(data.assoc_info.req_ies); - os_free(data.assoc_info.resp_ies); + os_memset(&event, 0, sizeof(event)); + event.tx_status.type = WLAN_FC_GET_TYPE(fc); + event.tx_status.stype = WLAN_FC_GET_STYPE(fc); + event.tx_status.dst = hdr->addr1; + event.tx_status.data = frame; + event.tx_status.data_len = len; + event.tx_status.ack = ack != NULL; + wpa_supplicant_event(drv->ctx, EVENT_TX_STATUS, &event); } -static void wpa_driver_nl80211_event_wireless(struct wpa_driver_nl80211_data *drv, - void *ctx, char *data, int len) +static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv, + enum wpa_event_type type, + const u8 *frame, size_t len) { - struct iw_event iwe_buf, *iwe = &iwe_buf; - char *pos, *end, *custom, *buf; + const struct ieee80211_mgmt *mgmt; + union wpa_event_data event; + const u8 *bssid = NULL; + u16 reason_code = 0; - pos = data; - end = data + len; + mgmt = (const struct ieee80211_mgmt *) frame; + if (len >= 24) { + bssid = mgmt->bssid; - while (pos + IW_EV_LCP_LEN <= end) { - /* Event data may be unaligned, so make a local, aligned copy - * before processing. */ - os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); - wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d", - iwe->cmd, iwe->len); - if (iwe->len <= IW_EV_LCP_LEN) + if (drv->associated != 0 && + os_memcmp(bssid, drv->bssid, ETH_ALEN) != 0 && + os_memcmp(bssid, drv->auth_bssid, ETH_ALEN) != 0) { + /* + * We have presumably received this deauth as a + * response to a clear_state_mismatch() outgoing + * deauth. Don't let it take us offline! + */ + wpa_printf(MSG_DEBUG, "nl80211: Deauth received " + "from Unknown BSSID " MACSTR " -- ignoring", + MAC2STR(bssid)); return; - - custom = pos + IW_EV_POINT_LEN; - if (drv->we_version_compiled > 18 && - (iwe->cmd == IWEVMICHAELMICFAILURE || - iwe->cmd == IWEVCUSTOM || - iwe->cmd == IWEVASSOCREQIE || - iwe->cmd == IWEVASSOCRESPIE || - iwe->cmd == IWEVPMKIDCAND)) { - /* WE-19 removed the pointer from struct iw_point */ - char *dpos = (char *) &iwe_buf.u.data.length; - int dlen = dpos - (char *) &iwe_buf; - os_memcpy(dpos, pos + IW_EV_LCP_LEN, - sizeof(struct iw_event) - dlen); - } else { - os_memcpy(&iwe_buf, pos, sizeof(struct iw_event)); - custom += IW_EV_POINT_OFF; } + } - switch (iwe->cmd) { - case SIOCGIWAP: - wpa_printf(MSG_DEBUG, "Wireless event: new AP: " - MACSTR, - MAC2STR((u8 *) iwe->u.ap_addr.sa_data)); - if (is_zero_ether_addr( - (const u8 *) iwe->u.ap_addr.sa_data) || - os_memcmp(iwe->u.ap_addr.sa_data, - "\x44\x44\x44\x44\x44\x44", ETH_ALEN) == - 0) { - os_free(drv->assoc_req_ies); - drv->assoc_req_ies = NULL; - os_free(drv->assoc_resp_ies); - drv->assoc_resp_ies = NULL; - wpa_supplicant_event(ctx, EVENT_DISASSOC, - NULL); - - } else { - wpa_driver_nl80211_event_assoc_ies(drv); - wpa_supplicant_event(ctx, EVENT_ASSOC, NULL); - } - break; - case IWEVMICHAELMICFAILURE: - wpa_driver_nl80211_event_wireless_michaelmicfailure( - ctx, custom, iwe->u.data.length); - break; - case IWEVCUSTOM: - if (custom + iwe->u.data.length > end) - return; - buf = os_malloc(iwe->u.data.length + 1); - if (buf == NULL) - return; - os_memcpy(buf, custom, iwe->u.data.length); - buf[iwe->u.data.length] = '\0'; - wpa_driver_nl80211_event_wireless_custom(ctx, buf); - os_free(buf); - break; - case IWEVASSOCREQIE: - wpa_driver_nl80211_event_wireless_assocreqie( - drv, custom, iwe->u.data.length); - break; - case IWEVASSOCRESPIE: - wpa_driver_nl80211_event_wireless_assocrespie( - drv, custom, iwe->u.data.length); - break; - case IWEVPMKIDCAND: - wpa_driver_nl80211_event_wireless_pmkidcand( - drv, custom, iwe->u.data.length); - break; - } + drv->associated = 0; + os_memset(&event, 0, sizeof(event)); + + /* Note: Same offset for Reason Code in both frame subtypes */ + if (len >= 24 + sizeof(mgmt->u.deauth)) + reason_code = le_to_host16(mgmt->u.deauth.reason_code); - pos += iwe->len; + if (type == EVENT_DISASSOC) { + event.disassoc_info.addr = bssid; + event.disassoc_info.reason_code = reason_code; + } else { + event.deauth_info.addr = bssid; + event.deauth_info.reason_code = reason_code; } + + wpa_supplicant_event(drv->ctx, type, &event); } -static void wpa_driver_nl80211_event_link(struct wpa_driver_nl80211_data *drv, - void *ctx, char *buf, size_t len, - int del) +static void mlme_event(struct wpa_driver_nl80211_data *drv, + enum nl80211_commands cmd, struct nlattr *frame, + struct nlattr *addr, struct nlattr *timed_out, + struct nlattr *freq, struct nlattr *ack, + struct nlattr *cookie) { - union wpa_event_data event; + if (timed_out && addr) { + mlme_timeout_event(drv, cmd, addr); + return; + } - os_memset(&event, 0, sizeof(event)); - if (len > sizeof(event.interface_status.ifname)) - len = sizeof(event.interface_status.ifname) - 1; - os_memcpy(event.interface_status.ifname, buf, len); - event.interface_status.ievent = del ? EVENT_INTERFACE_REMOVED : - EVENT_INTERFACE_ADDED; + if (frame == NULL) { + wpa_printf(MSG_DEBUG, "nl80211: MLME event %d without frame " + "data", cmd); + return; + } - wpa_printf(MSG_DEBUG, "RTM_%sLINK, IFLA_IFNAME: Interface '%s' %s", - del ? "DEL" : "NEW", - event.interface_status.ifname, - del ? "removed" : "added"); + wpa_printf(MSG_DEBUG, "nl80211: MLME event %d", cmd); + wpa_hexdump(MSG_MSGDUMP, "nl80211: MLME event frame", + nla_data(frame), nla_len(frame)); - if (os_strcmp(drv->ifname, event.interface_status.ifname) == 0) { - if (del) - drv->if_removed = 1; - else - drv->if_removed = 0; + switch (cmd) { + case NL80211_CMD_AUTHENTICATE: + mlme_event_auth(drv, nla_data(frame), nla_len(frame)); + break; + case NL80211_CMD_ASSOCIATE: + mlme_event_assoc(drv, nla_data(frame), nla_len(frame)); + break; + case NL80211_CMD_DEAUTHENTICATE: + mlme_event_deauth_disassoc(drv, EVENT_DEAUTH, + nla_data(frame), nla_len(frame)); + break; + case NL80211_CMD_DISASSOCIATE: + mlme_event_deauth_disassoc(drv, EVENT_DISASSOC, + nla_data(frame), nla_len(frame)); + break; + case NL80211_CMD_ACTION: + mlme_event_action(drv, freq, nla_data(frame), nla_len(frame)); + break; + case NL80211_CMD_ACTION_TX_STATUS: + mlme_event_action_tx_status(drv, cookie, nla_data(frame), + nla_len(frame), ack); + break; + default: + break; } - - wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event); } -static int wpa_driver_nl80211_own_ifname(struct wpa_driver_nl80211_data *drv, - struct nlmsghdr *h) +static void mlme_event_michael_mic_failure(struct wpa_driver_nl80211_data *drv, + struct nlattr *tb[]) { - struct ifinfomsg *ifi; - int attrlen, _nlmsg_len, rta_len; - struct rtattr *attr; - - ifi = NLMSG_DATA(h); - - _nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); - - attrlen = h->nlmsg_len - _nlmsg_len; - if (attrlen < 0) - return 0; + union wpa_event_data data; - attr = (struct rtattr *) (((char *) ifi) + _nlmsg_len); + wpa_printf(MSG_DEBUG, "nl80211: MLME event Michael MIC failure"); + os_memset(&data, 0, sizeof(data)); + if (tb[NL80211_ATTR_MAC]) { + wpa_hexdump(MSG_DEBUG, "nl80211: Source MAC address", + nla_data(tb[NL80211_ATTR_MAC]), + nla_len(tb[NL80211_ATTR_MAC])); + data.michael_mic_failure.src = nla_data(tb[NL80211_ATTR_MAC]); + } + if (tb[NL80211_ATTR_KEY_SEQ]) { + wpa_hexdump(MSG_DEBUG, "nl80211: TSC", + nla_data(tb[NL80211_ATTR_KEY_SEQ]), + nla_len(tb[NL80211_ATTR_KEY_SEQ])); + } + if (tb[NL80211_ATTR_KEY_TYPE]) { + enum nl80211_key_type key_type = + nla_get_u32(tb[NL80211_ATTR_KEY_TYPE]); + wpa_printf(MSG_DEBUG, "nl80211: Key Type %d", key_type); + if (key_type == NL80211_KEYTYPE_PAIRWISE) + data.michael_mic_failure.unicast = 1; + } else + data.michael_mic_failure.unicast = 1; - rta_len = RTA_ALIGN(sizeof(struct rtattr)); - while (RTA_OK(attr, attrlen)) { - if (attr->rta_type == IFLA_IFNAME) { - if (os_strcmp(((char *) attr) + rta_len, drv->ifname) - == 0) - return 1; - else - break; - } - attr = RTA_NEXT(attr, attrlen); + if (tb[NL80211_ATTR_KEY_IDX]) { + u8 key_id = nla_get_u8(tb[NL80211_ATTR_KEY_IDX]); + wpa_printf(MSG_DEBUG, "nl80211: Key Id %d", key_id); } - return 0; + wpa_supplicant_event(drv->ctx, EVENT_MICHAEL_MIC_FAILURE, &data); } -static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv, - int ifindex, struct nlmsghdr *h) +static void mlme_event_join_ibss(struct wpa_driver_nl80211_data *drv, + struct nlattr *tb[]) { - if (drv->ifindex == ifindex) - return 1; - - if (drv->if_removed && wpa_driver_nl80211_own_ifname(drv, h)) { - drv->ifindex = if_nametoindex(drv->ifname); - wpa_printf(MSG_DEBUG, "nl80211: Update ifindex for a removed " - "interface"); - wpa_driver_nl80211_finish_drv_init(drv); - return 1; + if (tb[NL80211_ATTR_MAC] == NULL) { + wpa_printf(MSG_DEBUG, "nl80211: No address in IBSS joined " + "event"); + return; } + os_memcpy(drv->bssid, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); + drv->associated = 1; + wpa_printf(MSG_DEBUG, "nl80211: IBSS " MACSTR " joined", + MAC2STR(drv->bssid)); - return 0; + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL); } -static void wpa_driver_nl80211_event_rtm_newlink(struct wpa_driver_nl80211_data *drv, - void *ctx, struct nlmsghdr *h, - size_t len) +static void mlme_event_remain_on_channel(struct wpa_driver_nl80211_data *drv, + int cancel_event, struct nlattr *tb[]) { - struct ifinfomsg *ifi; - int attrlen, _nlmsg_len, rta_len; - struct rtattr * attr; + unsigned int freq, chan_type, duration; + union wpa_event_data data; + u64 cookie; - if (len < sizeof(*ifi)) - return; + if (tb[NL80211_ATTR_WIPHY_FREQ]) + freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]); + else + freq = 0; - ifi = NLMSG_DATA(h); + if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) + chan_type = nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); + else + chan_type = 0; - if (!wpa_driver_nl80211_own_ifindex(drv, ifi->ifi_index, h)) { - wpa_printf(MSG_DEBUG, "Ignore event for foreign ifindex %d", - ifi->ifi_index); - return; - } + if (tb[NL80211_ATTR_DURATION]) + duration = nla_get_u32(tb[NL80211_ATTR_DURATION]); + else + duration = 0; - wpa_printf(MSG_DEBUG, "RTM_NEWLINK: operstate=%d ifi_flags=0x%x " - "(%s%s%s%s)", - drv->operstate, ifi->ifi_flags, - (ifi->ifi_flags & IFF_UP) ? "[UP]" : "", - (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "", - (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", - (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); - /* - * Some drivers send the association event before the operup event--in - * this case, lifting operstate in wpa_driver_nl80211_set_operstate() - * fails. This will hit us when wpa_supplicant does not need to do - * IEEE 802.1X authentication - */ - if (drv->operstate == 1 && - (ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP && - !(ifi->ifi_flags & IFF_RUNNING)) - wpa_driver_nl80211_send_oper_ifla(drv, -1, IF_OPER_UP); + if (tb[NL80211_ATTR_COOKIE]) + cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]); + else + cookie = 0; - _nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); + wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel event (cancel=%d " + "freq=%u channel_type=%u duration=%u cookie=0x%llx (%s))", + cancel_event, freq, chan_type, duration, + (long long unsigned int) cookie, + cookie == drv->remain_on_chan_cookie ? "match" : "unknown"); - attrlen = h->nlmsg_len - _nlmsg_len; - if (attrlen < 0) - return; + if (cookie != drv->remain_on_chan_cookie) + return; /* not for us */ - attr = (struct rtattr *) (((char *) ifi) + _nlmsg_len); + drv->pending_remain_on_chan = !cancel_event; - rta_len = RTA_ALIGN(sizeof(struct rtattr)); - while (RTA_OK(attr, attrlen)) { - if (attr->rta_type == IFLA_WIRELESS) { - wpa_driver_nl80211_event_wireless( - drv, ctx, ((char *) attr) + rta_len, - attr->rta_len - rta_len); - } else if (attr->rta_type == IFLA_IFNAME) { - wpa_driver_nl80211_event_link( - drv, ctx, - ((char *) attr) + rta_len, - attr->rta_len - rta_len, 0); - } - attr = RTA_NEXT(attr, attrlen); - } + os_memset(&data, 0, sizeof(data)); + data.remain_on_channel.freq = freq; + data.remain_on_channel.duration = duration; + wpa_supplicant_event(drv->ctx, cancel_event ? + EVENT_CANCEL_REMAIN_ON_CHANNEL : + EVENT_REMAIN_ON_CHANNEL, &data); } -static void wpa_driver_nl80211_event_rtm_dellink(struct wpa_driver_nl80211_data *drv, - void *ctx, struct nlmsghdr *h, - size_t len) +static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted, + struct nlattr *tb[]) { - struct ifinfomsg *ifi; - int attrlen, _nlmsg_len, rta_len; - struct rtattr * attr; - - if (len < sizeof(*ifi)) - return; - - ifi = NLMSG_DATA(h); - - _nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); - - attrlen = h->nlmsg_len - _nlmsg_len; - if (attrlen < 0) - return; + union wpa_event_data event; + struct nlattr *nl; + int rem; + struct scan_info *info; +#define MAX_REPORT_FREQS 50 + int freqs[MAX_REPORT_FREQS]; + int num_freqs = 0; - attr = (struct rtattr *) (((char *) ifi) + _nlmsg_len); + os_memset(&event, 0, sizeof(event)); + info = &event.scan_info; + info->aborted = aborted; - rta_len = RTA_ALIGN(sizeof(struct rtattr)); - while (RTA_OK(attr, attrlen)) { - if (attr->rta_type == IFLA_IFNAME) { - wpa_driver_nl80211_event_link( - drv, ctx, - ((char *) attr) + rta_len, - attr->rta_len - rta_len, 1); + if (tb[NL80211_ATTR_SCAN_SSIDS]) { + nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_SSIDS], rem) { + struct wpa_driver_scan_ssid *s = + &info->ssids[info->num_ssids]; + s->ssid = nla_data(nl); + s->ssid_len = nla_len(nl); + info->num_ssids++; + if (info->num_ssids == WPAS_MAX_SCAN_SSIDS) + break; } - attr = RTA_NEXT(attr, attrlen); } + if (tb[NL80211_ATTR_SCAN_FREQUENCIES]) { + nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_FREQUENCIES], rem) + { + freqs[num_freqs] = nla_get_u32(nl); + num_freqs++; + if (num_freqs == MAX_REPORT_FREQS - 1) + break; + } + info->freqs = freqs; + info->num_freqs = num_freqs; + } + wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event); } -static void wpa_driver_nl80211_event_receive_wext(int sock, void *eloop_ctx, - void *sock_ctx) +static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv, + struct nlattr *tb[]) { - char buf[8192]; - int left; - struct sockaddr_nl from; - socklen_t fromlen; - struct nlmsghdr *h; - int max_events = 10; + static struct nla_policy cqm_policy[NL80211_ATTR_CQM_MAX + 1] = { + [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 }, + [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U8 }, + [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 }, + }; + struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1]; + enum nl80211_cqm_rssi_threshold_event event; + union wpa_event_data ed; -try_again: - fromlen = sizeof(from); - left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, - (struct sockaddr *) &from, &fromlen); - if (left < 0) { - if (errno != EINTR && errno != EAGAIN) - perror("recvfrom(netlink)"); + if (tb[NL80211_ATTR_CQM] == NULL || + nla_parse_nested(cqm, NL80211_ATTR_CQM_MAX, tb[NL80211_ATTR_CQM], + cqm_policy)) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore invalid CQM event"); return; } - h = (struct nlmsghdr *) buf; - while (left >= (int) sizeof(*h)) { - int len, plen; - - len = h->nlmsg_len; - plen = len - sizeof(*h); - if (len > left || plen < 0) { - wpa_printf(MSG_DEBUG, "Malformed netlink message: " - "len=%d left=%d plen=%d", - len, left, plen); - break; - } - - switch (h->nlmsg_type) { - case RTM_NEWLINK: - wpa_driver_nl80211_event_rtm_newlink(eloop_ctx, sock_ctx, - h, plen); - break; - case RTM_DELLINK: - wpa_driver_nl80211_event_rtm_dellink(eloop_ctx, sock_ctx, - h, plen); - break; - } - - len = NLMSG_ALIGN(len); - left -= len; - h = (struct nlmsghdr *) ((char *) h + len); - } - - if (left > 0) { - wpa_printf(MSG_DEBUG, "%d extra bytes in the end of netlink " - "message", left); - } + if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] == NULL) + return; + event = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]); - if (--max_events > 0) { - /* - * Try to receive all events in one eloop call in order to - * limit race condition on cases where AssocInfo event, Assoc - * event, and EAPOL frames are received more or less at the - * same time. We want to process the event messages first - * before starting EAPOL processing. - */ - goto try_again; - } -} + os_memset(&ed, 0, sizeof(ed)); + if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH) { + wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor " + "event: RSSI high"); + ed.signal_change.above_threshold = 1; + } else if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW) { + wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor " + "event: RSSI low"); + ed.signal_change.above_threshold = 0; + } else + return; -static int no_seq_check(struct nl_msg *msg, void *arg) -{ - return NL_OK; + wpa_supplicant_event(drv->ctx, EVENT_SIGNAL_CHANGE, &ed); } @@ -967,13 +974,14 @@ static int process_event(struct nl_msg *msg, void *arg) struct wpa_driver_nl80211_data *drv = arg; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); struct nlattr *tb[NL80211_ATTR_MAX + 1]; + union wpa_event_data data; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); if (tb[NL80211_ATTR_IFINDEX]) { int ifindex = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); - if (ifindex != drv->ifindex) { + if (ifindex != drv->ifindex && !have_ifidx(drv, ifindex)) { wpa_printf(MSG_DEBUG, "nl80211: Ignored event (cmd=%d)" " for foreign interface (ifindex %d)", gnlh->cmd, ifindex); @@ -981,6 +989,14 @@ static int process_event(struct nl_msg *msg, void *arg) } } + if (drv->ap_scan_as_station && + (gnlh->cmd == NL80211_CMD_NEW_SCAN_RESULTS || + gnlh->cmd == NL80211_CMD_SCAN_ABORTED)) { + wpa_driver_nl80211_set_mode(&drv->first_bss, + IEEE80211_MODE_AP); + drv->ap_scan_as_station = 0; + } + switch (gnlh->cmd) { case NL80211_CMD_TRIGGER_SCAN: wpa_printf(MSG_DEBUG, "nl80211: Scan trigger"); @@ -990,7 +1006,7 @@ static int process_event(struct nl_msg *msg, void *arg) drv->scan_complete_events = 1; eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); - wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL); + send_scan_event(drv, 0, tb); break; case NL80211_CMD_SCAN_ABORTED: wpa_printf(MSG_DEBUG, "nl80211: Scan aborted"); @@ -1000,11 +1016,62 @@ static int process_event(struct nl_msg *msg, void *arg) */ eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); - wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL); + send_scan_event(drv, 1, tb); + break; + case NL80211_CMD_AUTHENTICATE: + case NL80211_CMD_ASSOCIATE: + case NL80211_CMD_DEAUTHENTICATE: + case NL80211_CMD_DISASSOCIATE: + case NL80211_CMD_ACTION: + case NL80211_CMD_ACTION_TX_STATUS: + mlme_event(drv, gnlh->cmd, tb[NL80211_ATTR_FRAME], + tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT], + tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK], + tb[NL80211_ATTR_COOKIE]); + break; + case NL80211_CMD_CONNECT: + case NL80211_CMD_ROAM: + mlme_event_connect(drv, gnlh->cmd, + tb[NL80211_ATTR_STATUS_CODE], + tb[NL80211_ATTR_MAC], + tb[NL80211_ATTR_REQ_IE], + tb[NL80211_ATTR_RESP_IE]); + break; + case NL80211_CMD_DISCONNECT: + if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) { + /* + * Avoid reporting two disassociation events that could + * confuse the core code. + */ + wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect " + "event when using userspace SME"); + break; + } + drv->associated = 0; + os_memset(&data, 0, sizeof(data)); + if (tb[NL80211_ATTR_REASON_CODE]) + data.disassoc_info.reason_code = + nla_get_u16(tb[NL80211_ATTR_REASON_CODE]); + wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, &data); + break; + case NL80211_CMD_MICHAEL_MIC_FAILURE: + mlme_event_michael_mic_failure(drv, tb); + break; + case NL80211_CMD_JOIN_IBSS: + mlme_event_join_ibss(drv, tb); + break; + case NL80211_CMD_REMAIN_ON_CHANNEL: + mlme_event_remain_on_channel(drv, 0, tb); + break; + case NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL: + mlme_event_remain_on_channel(drv, 1, tb); + break; + case NL80211_CMD_NOTIFY_CQM: + nl80211_cqm_event(drv, tb); break; default: - wpa_printf(MSG_DEBUG, "nl0211: Ignored unknown event (cmd=%d)", - gnlh->cmd); + wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event " + "(cmd=%d)", gnlh->cmd); break; } @@ -1025,70 +1092,11 @@ static void wpa_driver_nl80211_event_receive(int sock, void *eloop_ctx, return; nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL); nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, process_event, drv); - nl_recvmsgs(drv->nl_handle, cb); + nl_recvmsgs(drv->nl_handle_event, cb); nl_cb_put(cb); } -static int wpa_driver_nl80211_get_ifflags_ifname(struct wpa_driver_nl80211_data *drv, - const char *ifname, int *flags) -{ - struct ifreq ifr; - - os_memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); - if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { - perror("ioctl[SIOCGIFFLAGS]"); - return -1; - } - *flags = ifr.ifr_flags & 0xffff; - return 0; -} - - -/** - * wpa_driver_nl80211_get_ifflags - Get interface flags (SIOCGIFFLAGS) - * @drv: driver_nl80211 private data - * @flags: Pointer to returned flags value - * Returns: 0 on success, -1 on failure - */ -static int wpa_driver_nl80211_get_ifflags(struct wpa_driver_nl80211_data *drv, - int *flags) -{ - return wpa_driver_nl80211_get_ifflags_ifname(drv, drv->ifname, flags); -} - - -static int wpa_driver_nl80211_set_ifflags_ifname( - struct wpa_driver_nl80211_data *drv, - const char *ifname, int flags) -{ - struct ifreq ifr; - - os_memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); - ifr.ifr_flags = flags & 0xffff; - if (ioctl(drv->ioctl_sock, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { - perror("SIOCSIFFLAGS"); - return -1; - } - return 0; -} - - -/** - * wpa_driver_nl80211_set_ifflags - Set interface flags (SIOCSIFFLAGS) - * @drv: driver_nl80211 private data - * @flags: New value for flags - * Returns: 0 on success, -1 on failure - */ -static int wpa_driver_nl80211_set_ifflags(struct wpa_driver_nl80211_data *drv, - int flags) -{ - return wpa_driver_nl80211_set_ifflags_ifname(drv, drv->ifname, flags); -} - - /** * wpa_driver_nl80211_set_country - ask nl80211 to set the regulatory domain * @priv: driver_nl80211 private data @@ -1100,13 +1108,14 @@ static int wpa_driver_nl80211_set_ifflags(struct wpa_driver_nl80211_data *drv, */ static int wpa_driver_nl80211_set_country(void *priv, const char *alpha2_arg) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; char alpha2[3]; struct nl_msg *msg; msg = nlmsg_alloc(); if (!msg) - goto nla_put_failure; + return -ENOMEM; alpha2[0] = alpha2_arg[0]; alpha2[1] = alpha2_arg[1]; @@ -1124,306 +1133,127 @@ nla_put_failure: } -static int wpa_driver_nl80211_set_probe_req_ie(void *priv, const u8 *ies, - size_t ies_len) -{ - struct wpa_driver_nl80211_data *drv = priv; - struct nl_msg *msg; - int ret = -1; - - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, - NL80211_CMD_SET_MGMT_EXTRA_IE, 0); - - NLA_PUT_U8(msg, NL80211_ATTR_MGMT_SUBTYPE, 4 /* ProbeReq */); - if (ies) - NLA_PUT(msg, NL80211_ATTR_IE, ies_len, ies); - - ret = 0; - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - - ret = send_and_recv_msgs(drv, msg, NULL, NULL); - return ret; - -nla_put_failure: - return -ENOBUFS; -} - +#ifndef HOSTAPD +struct wiphy_info_data { + int max_scan_ssids; + int ap_supported; + int auth_supported; + int connect_supported; +}; -#ifdef CONFIG_CLIENT_MLME -static int nl80211_set_vif(struct wpa_driver_nl80211_data *drv, - int drop_unencrypted, int userspace_mlme) +static int wiphy_info_handler(struct nl_msg *msg, void *arg) { -#ifdef NL80211_CMD_SET_VIF - struct nl_msg *msg; - int ret = -1; - - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, - NL80211_CMD_SET_VIF, 0); - - if (drop_unencrypted >= 0) - NLA_PUT_U8(msg, NL80211_ATTR_VIF_DROP_UNENCRYPTED, - drop_unencrypted); - if (userspace_mlme >= 0) - NLA_PUT_U8(msg, NL80211_ATTR_VIF_USERSPACE_MLME, - userspace_mlme); + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct wiphy_info_data *info = arg; - ret = 0; + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + if (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]) + info->max_scan_ssids = + nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]); - ret = send_and_recv_msgs(drv, msg, NULL, NULL); - return ret; + if (tb[NL80211_ATTR_SUPPORTED_IFTYPES]) { + struct nlattr *nl_mode; + int i; + nla_for_each_nested(nl_mode, + tb[NL80211_ATTR_SUPPORTED_IFTYPES], i) { + if (nl_mode->nla_type == NL80211_IFTYPE_AP) { + info->ap_supported = 1; + break; + } + } + } -nla_put_failure: - return -ENOBUFS; -#else /* NL80211_CMD_SET_VIF */ - return -1; -#endif /* NL80211_CMD_SET_VIF */ -} + if (tb[NL80211_ATTR_SUPPORTED_COMMANDS]) { + struct nlattr *nl_cmd; + int i; + nla_for_each_nested(nl_cmd, + tb[NL80211_ATTR_SUPPORTED_COMMANDS], i) { + u32 cmd = nla_get_u32(nl_cmd); + if (cmd == NL80211_CMD_AUTHENTICATE) + info->auth_supported = 1; + else if (cmd == NL80211_CMD_CONNECT) + info->connect_supported = 1; + } + } -static int wpa_driver_nl80211_set_userspace_mlme( - struct wpa_driver_nl80211_data *drv, int enabled) -{ - return nl80211_set_vif(drv, -1, enabled); + return NL_SKIP; } -static void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, - int ifidx) +static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv, + struct wiphy_info_data *info) { struct nl_msg *msg; - msg = nlmsg_alloc(); - if (!msg) - goto nla_put_failure; - - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_DEL_INTERFACE, 0); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifidx); - if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0) - return; -nla_put_failure: - wpa_printf(MSG_ERROR, "nl80211: Failed to remove interface."); -} - - -static int nl80211_create_iface(struct wpa_driver_nl80211_data *drv, - const char *ifname, enum nl80211_iftype iftype) -{ - struct nl_msg *msg, *flags = NULL; - int ifidx, err; - int ret = -ENOBUFS; - + os_memset(info, 0, sizeof(*info)); msg = nlmsg_alloc(); if (!msg) return -1; genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_NEW_INTERFACE, 0); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->ifname)); - NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, ifname); - NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, iftype); - - if (iftype == NL80211_IFTYPE_MONITOR) { - flags = nlmsg_alloc(); - if (!flags) - goto nla_put_failure; - - NLA_PUT_FLAG(flags, NL80211_MNTR_FLAG_COOK_FRAMES); - - err = nla_put_nested(msg, NL80211_ATTR_MNTR_FLAGS, flags); - - nlmsg_free(flags); - - if (err) - goto nla_put_failure; - } - - ret = send_and_recv_msgs(drv, msg, NULL, NULL); - if (ret) { - nla_put_failure: - wpa_printf(MSG_ERROR, "nl80211: Failed to create interface %d", - ret); - return ret; - } - - ifidx = if_nametoindex(ifname); - if (ifidx <= 0) - return -1; - - return ifidx; -} - - -static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx) -{ - struct wpa_driver_nl80211_data *drv = eloop_ctx; - int len; - unsigned char buf[3000]; - struct ieee80211_radiotap_iterator iter; - int ret; - int injected = 0, failed = 0, rxflags = 0; - struct ieee80211_rx_status rx_status; - - len = recv(sock, buf, sizeof(buf), 0); - if (len < 0) { - perror("recv"); - return; - } - - if (ieee80211_radiotap_iterator_init(&iter, (void *) buf, len)) { - wpa_printf(MSG_DEBUG, "nl80211: received invalid radiotap " - "frame"); - return; - } - - os_memset(&rx_status, 0, sizeof(rx_status)); - - while (1) { - ret = ieee80211_radiotap_iterator_next(&iter); - if (ret == -ENOENT) - break; - if (ret) { - wpa_printf(MSG_DEBUG, "nl80211: received invalid " - "radiotap frame (%d)", ret); - return; - } - switch (iter.this_arg_index) { - case IEEE80211_RADIOTAP_FLAGS: - if (*iter.this_arg & IEEE80211_RADIOTAP_F_FCS) - len -= 4; - break; - case IEEE80211_RADIOTAP_RX_FLAGS: - rxflags = 1; - break; - case IEEE80211_RADIOTAP_TX_FLAGS: - injected = 1; - failed = le_to_host16((*(u16 *) iter.this_arg)) & - IEEE80211_RADIOTAP_F_TX_FAIL; - break; - case IEEE80211_RADIOTAP_DATA_RETRIES: - break; - case IEEE80211_RADIOTAP_CHANNEL: - /* TODO convert from freq/flags to channel number - * rx_status.channel = XXX; - */ - break; - case IEEE80211_RADIOTAP_RATE: - break; - case IEEE80211_RADIOTAP_DB_ANTSIGNAL: - rx_status.ssi = *iter.this_arg; - break; - } - } + 0, NL80211_CMD_GET_WIPHY, 0); - if (rxflags && injected) - return; + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->first_bss.ifindex); - if (!injected) { - wpa_supplicant_sta_rx(drv->ctx, buf + iter.max_length, - len - iter.max_length, &rx_status); - } else if (failed) { - /* TX failure callback */ - } else { - /* TX success (ACK) callback */ - } + if (send_and_recv_msgs(drv, msg, wiphy_info_handler, info) == 0) + return 0; + msg = NULL; +nla_put_failure: + nlmsg_free(msg); + return -1; } -static int wpa_driver_nl80211_create_monitor_interface( - struct wpa_driver_nl80211_data *drv) +static int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) { - char buf[IFNAMSIZ]; - struct sockaddr_ll ll; - int optval, flags; - socklen_t optlen; - - os_snprintf(buf, IFNAMSIZ, "mon.%s", drv->ifname); - buf[IFNAMSIZ - 1] = '\0'; - - drv->monitor_ifidx = - nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR); - - if (drv->monitor_ifidx < 0) + struct wiphy_info_data info; + if (wpa_driver_nl80211_get_info(drv, &info)) return -1; + drv->has_capability = 1; + /* For now, assume TKIP, CCMP, WPA, WPA2 are supported */ + drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK; + drv->capa.enc = WPA_DRIVER_CAPA_ENC_WEP40 | + WPA_DRIVER_CAPA_ENC_WEP104 | + WPA_DRIVER_CAPA_ENC_TKIP | + WPA_DRIVER_CAPA_ENC_CCMP; + drv->capa.auth = WPA_DRIVER_AUTH_OPEN | + WPA_DRIVER_AUTH_SHARED | + WPA_DRIVER_AUTH_LEAP; - if (wpa_driver_nl80211_get_ifflags_ifname(drv, buf, &flags) != 0 || - wpa_driver_nl80211_set_ifflags_ifname(drv, buf, flags | IFF_UP) != - 0) { - wpa_printf(MSG_ERROR, "nl80211: Could not set interface '%s' " - "UP", buf); - goto error; - } + drv->capa.max_scan_ssids = info.max_scan_ssids; + if (info.ap_supported) + drv->capa.flags |= WPA_DRIVER_FLAGS_AP; - os_memset(&ll, 0, sizeof(ll)); - ll.sll_family = AF_PACKET; - ll.sll_ifindex = drv->monitor_ifidx; - drv->monitor_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); - if (drv->monitor_sock < 0) { - perror("socket[PF_PACKET,SOCK_RAW]"); - goto error; - } - - if (bind(drv->monitor_sock, (struct sockaddr *) &ll, - sizeof(ll)) < 0) { - perror("monitor socket bind"); - goto error; - } - - optlen = sizeof(optval); - optval = 20; - if (setsockopt - (drv->monitor_sock, SOL_SOCKET, SO_PRIORITY, &optval, optlen)) { - perror("Failed to set socket priority"); - goto error; + if (info.auth_supported) + drv->capa.flags |= WPA_DRIVER_FLAGS_SME; + else if (!info.connect_supported) { + wpa_printf(MSG_INFO, "nl80211: Driver does not support " + "authentication/association or connect commands"); + return -1; } - if (eloop_register_read_sock(drv->monitor_sock, handle_monitor_read, - drv, NULL)) { - wpa_printf(MSG_ERROR, "nl80211: Could not register monitor " - "read socket"); - goto error; - } + drv->capa.flags |= WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE; + drv->capa.max_remain_on_chan = 5000; return 0; - - error: - nl80211_remove_iface(drv, drv->monitor_ifidx); - return -1; } - -#endif /* CONFIG_CLIENT_MLME */ +#endif /* HOSTAPD */ -/** - * wpa_driver_nl80211_init - Initialize nl80211 driver interface - * @ctx: context to be used when calling wpa_supplicant functions, - * e.g., wpa_supplicant_event() - * @ifname: interface name, e.g., wlan0 - * Returns: Pointer to private data, %NULL on failure - */ -static void * wpa_driver_nl80211_init(void *ctx, const char *ifname) +static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv, + void *ctx) { - int s, ret; - struct sockaddr_nl local; - struct wpa_driver_nl80211_data *drv; + int ret; - drv = os_zalloc(sizeof(*drv)); - if (drv == NULL) - return NULL; - drv->ctx = ctx; - os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + /* Initialize generic netlink and nl80211 */ drv->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); if (drv->nl_cb == NULL) { @@ -1439,18 +1269,51 @@ static void * wpa_driver_nl80211_init(void *ctx, const char *ifname) goto err2; } + drv->nl_handle_event = nl_handle_alloc_cb(drv->nl_cb); + if (drv->nl_handle_event == NULL) { + wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink " + "callbacks (event)"); + goto err2b; + } + if (genl_connect(drv->nl_handle)) { wpa_printf(MSG_ERROR, "nl80211: Failed to connect to generic " "netlink"); goto err3; } + if (genl_connect(drv->nl_handle_event)) { + wpa_printf(MSG_ERROR, "nl80211: Failed to connect to generic " + "netlink (event)"); + goto err3; + } + +#ifdef CONFIG_LIBNL20 + if (genl_ctrl_alloc_cache(drv->nl_handle, &drv->nl_cache) < 0) { + wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic " + "netlink cache"); + goto err3; + } + if (genl_ctrl_alloc_cache(drv->nl_handle_event, &drv->nl_cache_event) < + 0) { + wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic " + "netlink cache (event)"); + goto err3b; + } +#else /* CONFIG_LIBNL20 */ drv->nl_cache = genl_ctrl_alloc_cache(drv->nl_handle); if (drv->nl_cache == NULL) { wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic " "netlink cache"); goto err3; } + drv->nl_cache_event = genl_ctrl_alloc_cache(drv->nl_handle_event); + if (drv->nl_cache_event == NULL) { + wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic " + "netlink cache (event)"); + goto err3b; + } +#endif /* CONFIG_LIBNL20 */ drv->nl80211 = genl_ctrl_search_by_name(drv->nl_cache, "nl80211"); if (drv->nl80211 == NULL) { @@ -1461,89 +1324,212 @@ static void * wpa_driver_nl80211_init(void *ctx, const char *ifname) ret = nl_get_multicast_id(drv, "nl80211", "scan"); if (ret >= 0) - ret = nl_socket_add_membership(drv->nl_handle, ret); + ret = nl_socket_add_membership(drv->nl_handle_event, ret); if (ret < 0) { wpa_printf(MSG_ERROR, "nl80211: Could not add multicast " "membership for scan events: %d (%s)", ret, strerror(-ret)); goto err4; } - eloop_register_read_sock(nl_socket_get_fd(drv->nl_handle), + + ret = nl_get_multicast_id(drv, "nl80211", "mlme"); + if (ret >= 0) + ret = nl_socket_add_membership(drv->nl_handle_event, ret); + if (ret < 0) { + wpa_printf(MSG_ERROR, "nl80211: Could not add multicast " + "membership for mlme events: %d (%s)", + ret, strerror(-ret)); + goto err4; + } + + eloop_register_read_sock(nl_socket_get_fd(drv->nl_handle_event), wpa_driver_nl80211_event_receive, drv, ctx); + return 0; + +err4: + nl_cache_free(drv->nl_cache_event); +err3b: + nl_cache_free(drv->nl_cache); +err3: + nl_handle_destroy(drv->nl_handle_event); +err2b: + nl_handle_destroy(drv->nl_handle); +err2: + nl_cb_put(drv->nl_cb); +err1: + return -1; +} + + +/** + * wpa_driver_nl80211_init - Initialize nl80211 driver interface + * @ctx: context to be used when calling wpa_supplicant functions, + * e.g., wpa_supplicant_event() + * @ifname: interface name, e.g., wlan0 + * Returns: Pointer to private data, %NULL on failure + */ +static void * wpa_driver_nl80211_init(void *ctx, const char *ifname) +{ + struct wpa_driver_nl80211_data *drv; + struct netlink_config *cfg; + struct i802_bss *bss; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->ctx = ctx; + bss = &drv->first_bss; + bss->drv = drv; + os_strlcpy(bss->ifname, ifname, sizeof(bss->ifname)); + drv->monitor_ifidx = -1; + drv->monitor_sock = -1; + drv->ioctl_sock = -1; + + if (wpa_driver_nl80211_init_nl(drv, ctx)) { + os_free(drv); + return NULL; + } + drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); if (drv->ioctl_sock < 0) { perror("socket(PF_INET,SOCK_DGRAM)"); - goto err5; + goto failed; } - s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - if (s < 0) { - perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)"); - goto err6; + cfg = os_zalloc(sizeof(*cfg)); + if (cfg == NULL) + goto failed; + cfg->ctx = drv; + cfg->newlink_cb = wpa_driver_nl80211_event_rtm_newlink; + cfg->dellink_cb = wpa_driver_nl80211_event_rtm_dellink; + drv->netlink = netlink_init(cfg); + if (drv->netlink == NULL) { + os_free(cfg); + goto failed; } + if (wpa_driver_nl80211_finish_drv_init(drv)) + goto failed; - os_memset(&local, 0, sizeof(local)); - local.nl_family = AF_NETLINK; - local.nl_groups = RTMGRP_LINK; - if (bind(s, (struct sockaddr *) &local, sizeof(local)) < 0) { - perror("bind(netlink)"); - close(s); - goto err6; - } - - eloop_register_read_sock(s, wpa_driver_nl80211_event_receive_wext, drv, - ctx); - drv->wext_event_sock = s; - - wpa_driver_nl80211_finish_drv_init(drv); + return bss; - return drv; +failed: + netlink_deinit(drv->netlink); + if (drv->ioctl_sock >= 0) + close(drv->ioctl_sock); -err6: - close(drv->ioctl_sock); -err5: genl_family_put(drv->nl80211); -err4: nl_cache_free(drv->nl_cache); -err3: nl_handle_destroy(drv->nl_handle); -err2: nl_cb_put(drv->nl_cb); -err1: + eloop_unregister_read_sock(nl_socket_get_fd(drv->nl_handle_event)); + os_free(drv); return NULL; } -static void +static int nl80211_register_action_frame(struct wpa_driver_nl80211_data *drv, + const u8 *match, size_t match_len) +{ + struct nl_msg *msg; + int ret = -1; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_REGISTER_ACTION, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT(msg, NL80211_ATTR_FRAME_MATCH, match_len, match); + + ret = send_and_recv(drv, drv->nl_handle_event, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Register Action command " + "failed: ret=%d (%s)", ret, strerror(-ret)); + wpa_hexdump(MSG_DEBUG, "nl80211: Register Action match", + match, match_len); + goto nla_put_failure; + } + ret = 0; +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +static int nl80211_register_action_frames(struct wpa_driver_nl80211_data *drv) +{ + /* FT Action frames */ + if (nl80211_register_action_frame(drv, (u8 *) "\x06", 1) < 0) + return -1; + else + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT | + WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK; + + return 0; +} + + +static int wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv) { - int flags; + struct i802_bss *bss = &drv->first_bss; - drv->ifindex = if_nametoindex(drv->ifname); + drv->ifindex = if_nametoindex(bss->ifname); + drv->first_bss.ifindex = drv->ifindex; - if (wpa_driver_nl80211_set_mode(drv, 0) < 0) { - printf("Could not configure driver to use managed mode\n"); +#ifndef HOSTAPD + if (wpa_driver_nl80211_set_mode(bss, IEEE80211_MODE_INFRA) < 0) { + wpa_printf(MSG_DEBUG, "nl80211: Could not configure driver to " + "use managed mode"); } - if (wpa_driver_nl80211_get_ifflags(drv, &flags) != 0) - printf("Could not get interface '%s' flags\n", drv->ifname); - else if (!(flags & IFF_UP)) { - if (wpa_driver_nl80211_set_ifflags(drv, flags | IFF_UP) != 0) { - printf("Could not set interface '%s' UP\n", - drv->ifname); - } + if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1)) { + wpa_printf(MSG_ERROR, "Could not set interface '%s' UP", + bss->ifname); + return -1; } - /* - * Make sure that the driver does not have any obsolete PMKID entries. - */ - wpa_driver_nl80211_flush_pmkid(drv); + if (wpa_driver_nl80211_capa(drv)) + return -1; - wpa_driver_nl80211_get_range(drv); + netlink_send_oper_ifla(drv->netlink, drv->ifindex, + 1, IF_OPER_DORMANT); +#endif /* HOSTAPD */ - wpa_driver_nl80211_send_oper_ifla(drv, 1, IF_OPER_DORMANT); + if (nl80211_register_action_frames(drv) < 0) { + wpa_printf(MSG_DEBUG, "nl80211: Failed to register Action " + "frame processing - ignore for now"); + /* + * Older kernel versions did not support this, so ignore the + * error for now. Some functionality may not be available + * because of this. + */ + } + + return 0; +} + + +static int wpa_driver_nl80211_del_beacon(struct wpa_driver_nl80211_data *drv) +{ + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_DEL_BEACON, 0); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + nla_put_failure: + return -ENOBUFS; } @@ -1556,54 +1542,80 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv) */ static void wpa_driver_nl80211_deinit(void *priv) { - struct wpa_driver_nl80211_data *drv = priv; - int flags; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; -#ifdef CONFIG_CLIENT_MLME - if (drv->monitor_sock >= 0) { - eloop_unregister_read_sock(drv->monitor_sock); - close(drv->monitor_sock); + if (drv->added_if_into_bridge) { + if (linux_br_del_if(drv->ioctl_sock, drv->brname, bss->ifname) + < 0) + wpa_printf(MSG_INFO, "nl80211: Failed to remove " + "interface %s from bridge %s: %s", + bss->ifname, drv->brname, strerror(errno)); + } + if (drv->added_bridge) { + if (linux_br_del(drv->ioctl_sock, drv->brname) < 0) + wpa_printf(MSG_INFO, "nl80211: Failed to remove " + "bridge %s: %s", + drv->brname, strerror(errno)); } - if (drv->monitor_ifidx > 0) - nl80211_remove_iface(drv, drv->monitor_ifidx); - if (drv->capa.flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) - wpa_driver_nl80211_set_userspace_mlme(drv, 0); -#endif /* CONFIG_CLIENT_MLME */ - eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); + nl80211_remove_monitor_interface(drv); - /* - * Clear possibly configured driver parameters in order to make it - * easier to use the driver after wpa_supplicant has been terminated. - */ - (void) wpa_driver_nl80211_set_bssid(drv, - (u8 *) "\x00\x00\x00\x00\x00\x00"); + if (drv->nlmode == NL80211_IFTYPE_AP) + wpa_driver_nl80211_del_beacon(drv); + +#ifdef HOSTAPD + if (drv->last_freq_ht) { + /* Clear HT flags from the driver */ + struct hostapd_freq_params freq; + os_memset(&freq, 0, sizeof(freq)); + freq.freq = drv->last_freq; + i802_set_freq(priv, &freq); + } - wpa_driver_nl80211_send_oper_ifla(priv, 0, IF_OPER_UP); + if (drv->eapol_sock >= 0) { + eloop_unregister_read_sock(drv->eapol_sock); + close(drv->eapol_sock); + } + + if (drv->if_indices != drv->default_if_indices) + os_free(drv->if_indices); +#endif /* HOSTAPD */ + + if (drv->disable_11b_rates) + nl80211_disable_11b_rates(drv, drv->ifindex, 0); - eloop_unregister_read_sock(drv->wext_event_sock); + netlink_send_oper_ifla(drv->netlink, drv->ifindex, 0, IF_OPER_UP); + netlink_deinit(drv->netlink); + + eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); - if (wpa_driver_nl80211_get_ifflags(drv, &flags) == 0) - (void) wpa_driver_nl80211_set_ifflags(drv, flags & ~IFF_UP); + (void) linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0); + wpa_driver_nl80211_set_mode(bss, IEEE80211_MODE_INFRA); - close(drv->wext_event_sock); - close(drv->ioctl_sock); - os_free(drv->assoc_req_ies); - os_free(drv->assoc_resp_ies); + if (drv->ioctl_sock >= 0) + close(drv->ioctl_sock); - eloop_unregister_read_sock(nl_socket_get_fd(drv->nl_handle)); + eloop_unregister_read_sock(nl_socket_get_fd(drv->nl_handle_event)); genl_family_put(drv->nl80211); nl_cache_free(drv->nl_cache); + nl_cache_free(drv->nl_cache_event); nl_handle_destroy(drv->nl_handle); + nl_handle_destroy(drv->nl_handle_event); nl_cb_put(drv->nl_cb); + eloop_cancel_timeout(wpa_driver_nl80211_probe_req_report_timeout, + drv, NULL); + + os_free(drv->filter_ssids); + os_free(drv); } /** * wpa_driver_nl80211_scan_timeout - Scan timeout to report scan completion - * @eloop_ctx: Unused + * @eloop_ctx: Driver private data * @timeout_ctx: ctx argument given to wpa_driver_nl80211_init() * * This function can be used as registered timeout when starting a scan to @@ -1611,6 +1623,12 @@ static void wpa_driver_nl80211_deinit(void *priv) */ static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx) { + struct wpa_driver_nl80211_data *drv = eloop_ctx; + if (drv->ap_scan_as_station) { + wpa_driver_nl80211_set_mode(&drv->first_bss, + IEEE80211_MODE_AP); + drv->ap_scan_as_station = 0; + } wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); } @@ -1618,47 +1636,94 @@ static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx) /** * wpa_driver_nl80211_scan - Request the driver to initiate scan - * @priv: Pointer to private wext data from wpa_driver_nl80211_init() - * @ssid: Specific SSID to scan for (ProbeReq) or %NULL to scan for - * all SSIDs (either active scan with broadcast SSID or passive - * scan - * @ssid_len: Length of the SSID + * @priv: Pointer to private driver data from wpa_driver_nl80211_init() + * @params: Scan parameters * Returns: 0 on success, -1 on failure */ -static int wpa_driver_nl80211_scan(void *priv, const u8 *ssid, size_t ssid_len) +static int wpa_driver_nl80211_scan(void *priv, + struct wpa_driver_scan_params *params) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; int ret = 0, timeout; - struct nl_msg *msg, *ssids; + struct nl_msg *msg, *ssids, *freqs; + size_t i; msg = nlmsg_alloc(); ssids = nlmsg_alloc(); - if (!msg || !ssids) { + freqs = nlmsg_alloc(); + if (!msg || !ssids || !freqs) { nlmsg_free(msg); nlmsg_free(ssids); + nlmsg_free(freqs); return -1; } + os_free(drv->filter_ssids); + drv->filter_ssids = params->filter_ssids; + params->filter_ssids = NULL; + drv->num_filter_ssids = params->num_filter_ssids; + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, NL80211_CMD_TRIGGER_SCAN, 0); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - if (ssid && ssid_len) { - /* Request an active scan for a specific SSID */ - NLA_PUT(ssids, 1, ssid_len, ssid); - } else { - /* Request an active scan for wildcard SSID */ - NLA_PUT(ssids, 1, 0, ""); + for (i = 0; i < params->num_ssids; i++) { + wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan SSID", + params->ssids[i].ssid, + params->ssids[i].ssid_len); + NLA_PUT(ssids, i + 1, params->ssids[i].ssid_len, + params->ssids[i].ssid); + } + if (params->num_ssids) + nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids); + + if (params->extra_ies) { + wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan extra IEs", + params->extra_ies, params->extra_ies_len); + NLA_PUT(msg, NL80211_ATTR_IE, params->extra_ies_len, + params->extra_ies); + } + + if (params->freqs) { + for (i = 0; params->freqs[i]; i++) { + wpa_printf(MSG_MSGDUMP, "nl80211: Scan frequency %u " + "MHz", params->freqs[i]); + NLA_PUT_U32(freqs, i + 1, params->freqs[i]); + } + nla_put_nested(msg, NL80211_ATTR_SCAN_FREQUENCIES, freqs); } - nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids); ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Scan trigger failed: ret=%d " "(%s)", ret, strerror(-ret)); +#ifdef HOSTAPD + if (drv->nlmode == NL80211_IFTYPE_AP) { + /* + * mac80211 does not allow scan requests in AP mode, so + * try to do this in station mode. + */ + if (wpa_driver_nl80211_set_mode(bss, + IEEE80211_MODE_INFRA)) + goto nla_put_failure; + + if (wpa_driver_nl80211_scan(drv, params)) { + wpa_driver_nl80211_set_mode(bss, + IEEE80211_MODE_AP); + goto nla_put_failure; + } + + /* Restore AP mode when processing scan results */ + drv->ap_scan_as_station = 1; + ret = 0; + } else + goto nla_put_failure; +#else /* HOSTAPD */ goto nla_put_failure; +#endif /* HOSTAPD */ } /* Not all drivers generate "scan completed" wireless event, so try to @@ -1666,9 +1731,9 @@ static int wpa_driver_nl80211_scan(void *priv, const u8 *ssid, size_t ssid_len) timeout = 10; if (drv->scan_complete_events) { /* - * The driver seems to deliver SIOCGIWSCAN events to notify - * when scan is complete, so use longer timeout to avoid race - * conditions with scanning and following association request. + * The driver seems to deliver events to notify when scan is + * complete, so use longer timeout to avoid race conditions + * with scanning and following association request. */ timeout = 30; } @@ -1681,10 +1746,62 @@ static int wpa_driver_nl80211_scan(void *priv, const u8 *ssid, size_t ssid_len) nla_put_failure: nlmsg_free(ssids); nlmsg_free(msg); + nlmsg_free(freqs); return ret; } +static const u8 * nl80211_get_ie(const u8 *ies, size_t ies_len, u8 ie) +{ + const u8 *end, *pos; + + if (ies == NULL) + return NULL; + + pos = ies; + end = ies + ies_len; + + while (pos + 1 < end) { + if (pos + 2 + pos[1] > end) + break; + if (pos[0] == ie) + return pos; + pos += 2 + pos[1]; + } + + return NULL; +} + + +static int nl80211_scan_filtered(struct wpa_driver_nl80211_data *drv, + const u8 *ie, size_t ie_len) +{ + const u8 *ssid; + size_t i; + + if (drv->filter_ssids == NULL) + return 0; + + ssid = nl80211_get_ie(ie, ie_len, WLAN_EID_SSID); + if (ssid == NULL) + return 1; + + for (i = 0; i < drv->num_filter_ssids; i++) { + if (ssid[1] == drv->filter_ssids[i].ssid_len && + os_memcmp(ssid + 2, drv->filter_ssids[i].ssid, ssid[1]) == + 0) + return 0; + } + + return 1; +} + + +struct nl80211_bss_info_arg { + struct wpa_driver_nl80211_data *drv; + struct wpa_scan_results *res; +}; + static int bss_info_handler(struct nl_msg *msg, void *arg) { struct nlattr *tb[NL80211_ATTR_MAX + 1]; @@ -1699,12 +1816,17 @@ static int bss_info_handler(struct nl_msg *msg, void *arg) [NL80211_BSS_INFORMATION_ELEMENTS] = { .type = NLA_UNSPEC }, [NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 }, [NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 }, + [NL80211_BSS_STATUS] = { .type = NLA_U32 }, + [NL80211_BSS_SEEN_MS_AGO] = { .type = NLA_U32 }, + [NL80211_BSS_BEACON_IES] = { .type = NLA_UNSPEC }, }; - struct wpa_scan_results *res = arg; + struct nl80211_bss_info_arg *_arg = arg; + struct wpa_scan_results *res = _arg->res; struct wpa_scan_res **tmp; struct wpa_scan_res *r; - const u8 *ie; - size_t ie_len; + const u8 *ie, *beacon_ie; + size_t ie_len, beacon_ie_len; + u8 *pos; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); @@ -1720,8 +1842,19 @@ static int bss_info_handler(struct nl_msg *msg, void *arg) ie = NULL; ie_len = 0; } + if (bss[NL80211_BSS_BEACON_IES]) { + beacon_ie = nla_data(bss[NL80211_BSS_BEACON_IES]); + beacon_ie_len = nla_len(bss[NL80211_BSS_BEACON_IES]); + } else { + beacon_ie = NULL; + beacon_ie_len = 0; + } - r = os_zalloc(sizeof(*r) + ie_len); + if (nl80211_scan_filtered(_arg->drv, ie ? ie : beacon_ie, + ie ? ie_len : beacon_ie_len)) + return NL_SKIP; + + r = os_zalloc(sizeof(*r) + ie_len + beacon_ie_len); if (r == NULL) return NL_SKIP; if (bss[NL80211_BSS_BSSID]) @@ -1733,15 +1866,44 @@ static int bss_info_handler(struct nl_msg *msg, void *arg) r->beacon_int = nla_get_u16(bss[NL80211_BSS_BEACON_INTERVAL]); if (bss[NL80211_BSS_CAPABILITY]) r->caps = nla_get_u16(bss[NL80211_BSS_CAPABILITY]); - if (bss[NL80211_BSS_SIGNAL_UNSPEC]) - r->qual = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]); - if (bss[NL80211_BSS_SIGNAL_MBM]) + r->flags |= WPA_SCAN_NOISE_INVALID; + if (bss[NL80211_BSS_SIGNAL_MBM]) { r->level = nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]); + r->level /= 100; /* mBm to dBm */ + r->flags |= WPA_SCAN_LEVEL_DBM | WPA_SCAN_QUAL_INVALID; + } else if (bss[NL80211_BSS_SIGNAL_UNSPEC]) { + r->level = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]); + r->flags |= WPA_SCAN_LEVEL_INVALID; + } else + r->flags |= WPA_SCAN_LEVEL_INVALID | WPA_SCAN_QUAL_INVALID; if (bss[NL80211_BSS_TSF]) r->tsf = nla_get_u64(bss[NL80211_BSS_TSF]); + if (bss[NL80211_BSS_SEEN_MS_AGO]) + r->age = nla_get_u32(bss[NL80211_BSS_SEEN_MS_AGO]); r->ie_len = ie_len; - if (ie) - os_memcpy(r + 1, ie, ie_len); + pos = (u8 *) (r + 1); + if (ie) { + os_memcpy(pos, ie, ie_len); + pos += ie_len; + } + r->beacon_ie_len = beacon_ie_len; + if (beacon_ie) + os_memcpy(pos, beacon_ie, beacon_ie_len); + + if (bss[NL80211_BSS_STATUS]) { + enum nl80211_bss_status status; + status = nla_get_u32(bss[NL80211_BSS_STATUS]); + switch (status) { + case NL80211_BSS_STATUS_AUTHENTICATED: + r->flags |= WPA_SCAN_AUTHENTICATED; + break; + case NL80211_BSS_STATUS_ASSOCIATED: + r->flags |= WPA_SCAN_ASSOCIATED; + break; + default: + break; + } + } tmp = os_realloc(res->res, (res->num + 1) * sizeof(struct wpa_scan_res *)); @@ -1756,22 +1918,95 @@ static int bss_info_handler(struct nl_msg *msg, void *arg) } -/** - * wpa_driver_nl80211_get_scan_results - Fetch the latest scan results - * @priv: Pointer to private wext data from wpa_driver_nl80211_init() - * Returns: Scan results on success, -1 on failure - */ +static void clear_state_mismatch(struct wpa_driver_nl80211_data *drv, + const u8 *addr) +{ + if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) { + wpa_printf(MSG_DEBUG, "nl80211: Clear possible state " + "mismatch (" MACSTR ")", MAC2STR(addr)); + wpa_driver_nl80211_mlme(drv, addr, + NL80211_CMD_DEAUTHENTICATE, + WLAN_REASON_PREV_AUTH_NOT_VALID, 1); + } +} + + +static void wpa_driver_nl80211_check_bss_status( + struct wpa_driver_nl80211_data *drv, struct wpa_scan_results *res) +{ + size_t i; + + for (i = 0; i < res->num; i++) { + struct wpa_scan_res *r = res->res[i]; + if (r->flags & WPA_SCAN_AUTHENTICATED) { + wpa_printf(MSG_DEBUG, "nl80211: Scan results " + "indicates BSS status with " MACSTR + " as authenticated", + MAC2STR(r->bssid)); + if (drv->nlmode == NL80211_IFTYPE_STATION && + os_memcmp(r->bssid, drv->bssid, ETH_ALEN) != 0 && + os_memcmp(r->bssid, drv->auth_bssid, ETH_ALEN) != + 0) { + wpa_printf(MSG_DEBUG, "nl80211: Unknown BSSID" + " in local state (auth=" MACSTR + " assoc=" MACSTR ")", + MAC2STR(drv->auth_bssid), + MAC2STR(drv->bssid)); + clear_state_mismatch(drv, r->bssid); + } + } + + if (r->flags & WPA_SCAN_ASSOCIATED) { + wpa_printf(MSG_DEBUG, "nl80211: Scan results " + "indicate BSS status with " MACSTR + " as associated", + MAC2STR(r->bssid)); + if (drv->nlmode == NL80211_IFTYPE_STATION && + !drv->associated) { + wpa_printf(MSG_DEBUG, "nl80211: Local state " + "(not associated) does not match " + "with BSS state"); + clear_state_mismatch(drv, r->bssid); + } else if (drv->nlmode == NL80211_IFTYPE_STATION && + os_memcmp(drv->bssid, r->bssid, ETH_ALEN) != + 0) { + wpa_printf(MSG_DEBUG, "nl80211: Local state " + "(associated with " MACSTR ") does " + "not match with BSS state", + MAC2STR(drv->bssid)); + clear_state_mismatch(drv, r->bssid); + clear_state_mismatch(drv, drv->bssid); + } + } + } +} + + +static void wpa_scan_results_free(struct wpa_scan_results *res) +{ + size_t i; + + if (res == NULL) + return; + + for (i = 0; i < res->num; i++) + os_free(res->res[i]); + os_free(res->res); + os_free(res); +} + + static struct wpa_scan_results * -wpa_driver_nl80211_get_scan_results(void *priv) +nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv) { - struct wpa_driver_nl80211_data *drv = priv; struct nl_msg *msg; struct wpa_scan_results *res; int ret; + struct nl80211_bss_info_arg arg; res = os_zalloc(sizeof(*res)); if (res == NULL) - return 0; + return NULL; msg = nlmsg_alloc(); if (!msg) goto nla_put_failure; @@ -1780,7 +2015,9 @@ wpa_driver_nl80211_get_scan_results(void *priv) NL80211_CMD_GET_SCAN, 0); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - ret = send_and_recv_msgs(drv, msg, bss_info_handler, res); + arg.drv = drv; + arg.res = res; + ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg); msg = NULL; if (ret == 0) { wpa_printf(MSG_DEBUG, "Received scan results (%lu BSSes)", @@ -1796,639 +2033,465 @@ nla_put_failure: } -static int wpa_driver_nl80211_get_range(void *priv) +/** + * wpa_driver_nl80211_get_scan_results - Fetch the latest scan results + * @priv: Pointer to private wext data from wpa_driver_nl80211_init() + * Returns: Scan results on success, -1 on failure + */ +static struct wpa_scan_results * +wpa_driver_nl80211_get_scan_results(void *priv) { - struct wpa_driver_nl80211_data *drv = priv; - struct iw_range *range; - struct iwreq iwr; - int minlen; - size_t buflen; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct wpa_scan_results *res; - /* - * Use larger buffer than struct iw_range in order to allow the - * structure to grow in the future. - */ - buflen = sizeof(struct iw_range) + 500; - range = os_zalloc(buflen); - if (range == NULL) - return -1; + res = nl80211_get_scan_results(drv); + if (res) + wpa_driver_nl80211_check_bss_status(drv, res); + return res; +} - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - iwr.u.data.pointer = (caddr_t) range; - iwr.u.data.length = buflen; - minlen = ((char *) &range->enc_capa) - (char *) range + - sizeof(range->enc_capa); +static void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv) +{ + struct wpa_scan_results *res; + size_t i; - if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) { - perror("ioctl[SIOCGIWRANGE]"); - os_free(range); - return -1; - } else if (iwr.u.data.length >= minlen && - range->we_version_compiled >= 18) { - wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d " - "WE(source)=%d enc_capa=0x%x", - range->we_version_compiled, - range->we_version_source, - range->enc_capa); - drv->has_capability = 1; - drv->we_version_compiled = range->we_version_compiled; - if (range->enc_capa & IW_ENC_CAPA_WPA) { - drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA | - WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK; - } - if (range->enc_capa & IW_ENC_CAPA_WPA2) { - drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | - WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK; - } - drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 | - WPA_DRIVER_CAPA_ENC_WEP104; - if (range->enc_capa & IW_ENC_CAPA_CIPHER_TKIP) - drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP; - if (range->enc_capa & IW_ENC_CAPA_CIPHER_CCMP) - drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP; - drv->capa.auth = WPA_DRIVER_AUTH_OPEN | - WPA_DRIVER_AUTH_SHARED | - WPA_DRIVER_AUTH_LEAP; - wpa_printf(MSG_DEBUG, " capabilities: key_mgmt 0x%x enc 0x%x", - drv->capa.key_mgmt, drv->capa.enc); - } else { - wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: too old (short) data - " - "assuming WPA is not supported"); + res = nl80211_get_scan_results(drv); + if (res == NULL) { + wpa_printf(MSG_DEBUG, "nl80211: Failed to get scan results"); + return; } - os_free(range); - return 0; -} - - -static int wpa_driver_nl80211_set_wpa(void *priv, int enabled) -{ - struct wpa_driver_nl80211_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + wpa_printf(MSG_DEBUG, "nl80211: Scan result dump"); + for (i = 0; i < res->num; i++) { + struct wpa_scan_res *r = res->res[i]; + wpa_printf(MSG_DEBUG, "nl80211: %d/%d " MACSTR "%s%s", + (int) i, (int) res->num, MAC2STR(r->bssid), + r->flags & WPA_SCAN_AUTHENTICATED ? " [auth]" : "", + r->flags & WPA_SCAN_ASSOCIATED ? " [assoc]" : ""); + } - return wpa_driver_nl80211_set_auth_param(drv, IW_AUTH_WPA_ENABLED, - enabled); + wpa_scan_results_free(res); } -static int wpa_driver_nl80211_set_key(void *priv, wpa_alg alg, - const u8 *addr, int key_idx, - int set_tx, const u8 *seq, - size_t seq_len, +static int wpa_driver_nl80211_set_key(const char *ifname, void *priv, + enum wpa_alg alg, const u8 *addr, + int key_idx, int set_tx, + const u8 *seq, size_t seq_len, const u8 *key, size_t key_len) { - struct wpa_driver_nl80211_data *drv = priv; - int err; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + int ifindex = if_nametoindex(ifname); struct nl_msg *msg; + int ret; - wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%p key_idx=%d set_tx=%d " - "seq_len=%lu key_len=%lu", - __func__, alg, addr, key_idx, set_tx, + wpa_printf(MSG_DEBUG, "%s: ifindex=%d alg=%d addr=%p key_idx=%d " + "set_tx=%d seq_len=%lu key_len=%lu", + __func__, ifindex, alg, addr, key_idx, set_tx, (unsigned long) seq_len, (unsigned long) key_len); msg = nlmsg_alloc(); - if (msg == NULL) - return -1; + if (!msg) + return -ENOMEM; if (alg == WPA_ALG_NONE) { - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, - NL80211_CMD_DEL_KEY, 0); + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_DEL_KEY, 0); } else { - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, - NL80211_CMD_NEW_KEY, 0); + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_NEW_KEY, 0); NLA_PUT(msg, NL80211_ATTR_KEY_DATA, key_len, key); switch (alg) { case WPA_ALG_WEP: if (key_len == 5) NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, - 0x000FAC01); + WLAN_CIPHER_SUITE_WEP40); else NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, - 0x000FAC05); + WLAN_CIPHER_SUITE_WEP104); break; case WPA_ALG_TKIP: - NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC02); + NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, + WLAN_CIPHER_SUITE_TKIP); break; case WPA_ALG_CCMP: - NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC04); + NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, + WLAN_CIPHER_SUITE_CCMP); break; -#ifdef CONFIG_IEEE80211W case WPA_ALG_IGTK: - NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC06); + NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, + WLAN_CIPHER_SUITE_AES_CMAC); break; -#endif /* CONFIG_IEEE80211W */ default: + wpa_printf(MSG_ERROR, "%s: Unsupported encryption " + "algorithm %d", __func__, alg); nlmsg_free(msg); return -1; } } + if (seq && seq_len) + NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, seq_len, seq); + if (addr && os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) != 0) { wpa_printf(MSG_DEBUG, " addr=" MACSTR, MAC2STR(addr)); NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); } NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); - err = send_and_recv_msgs(drv, msg, NULL, NULL); - if (err) { - wpa_printf(MSG_DEBUG, "nl80211: set_key failed; err=%d", err); - return -1; - } + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if ((ret == -ENOENT || ret == -ENOLINK) && alg == WPA_ALG_NONE) + ret = 0; + if (ret) + wpa_printf(MSG_DEBUG, "nl80211: set_key failed; err=%d %s)", + ret, strerror(-ret)); - if (set_tx && alg != WPA_ALG_NONE) { - msg = nlmsg_alloc(); - if (msg == NULL) - return -1; + /* + * If we failed or don't need to set the default TX key (below), + * we're done here. + */ + if (ret || !set_tx || alg == WPA_ALG_NONE) + return ret; +#ifdef HOSTAPD + if (addr) + return ret; +#else /* HOSTAPD */ + if (drv->nlmode == NL80211_IFTYPE_AP && addr) + return ret; +#endif /* HOSTAPD */ - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_SET_KEY, 0); - NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT); + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; - err = send_and_recv_msgs(drv, msg, NULL, NULL); - if (err) { - wpa_printf(MSG_DEBUG, "nl80211: set default key " - "failed; err=%d", err); - return -1; - } - } + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_SET_KEY, 0); + NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); + if (alg == WPA_ALG_IGTK) + NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT_MGMT); + else + NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT); - return 0; + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret == -ENOENT) + ret = 0; + if (ret) + wpa_printf(MSG_DEBUG, "nl80211: set_key default failed; " + "err=%d %s)", ret, strerror(-ret)); + return ret; nla_put_failure: return -ENOBUFS; } -static int wpa_driver_nl80211_set_countermeasures(void *priv, - int enabled) -{ - struct wpa_driver_nl80211_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - return wpa_driver_nl80211_set_auth_param(drv, - IW_AUTH_TKIP_COUNTERMEASURES, - enabled); -} - - -static int wpa_driver_nl80211_set_drop_unencrypted(void *priv, - int enabled) +static int nl_add_key(struct nl_msg *msg, enum wpa_alg alg, + int key_idx, int defkey, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) { - struct wpa_driver_nl80211_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - drv->use_crypt = enabled; - return wpa_driver_nl80211_set_auth_param(drv, IW_AUTH_DROP_UNENCRYPTED, - enabled); -} + struct nlattr *key_attr = nla_nest_start(msg, NL80211_ATTR_KEY); + if (!key_attr) + return -1; + if (defkey && alg == WPA_ALG_IGTK) + NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT_MGMT); + else if (defkey) + NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT); -static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, - const u8 *addr, int cmd, int reason_code) -{ - struct iwreq iwr; - struct iw_mlme mlme; - int ret = 0; + NLA_PUT_U8(msg, NL80211_KEY_IDX, key_idx); - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - os_memset(&mlme, 0, sizeof(mlme)); - mlme.cmd = cmd; - mlme.reason_code = reason_code; - mlme.addr.sa_family = ARPHRD_ETHER; - os_memcpy(mlme.addr.sa_data, addr, ETH_ALEN); - iwr.u.data.pointer = (caddr_t) &mlme; - iwr.u.data.length = sizeof(mlme); - - if (ioctl(drv->ioctl_sock, SIOCSIWMLME, &iwr) < 0) { - perror("ioctl[SIOCSIWMLME]"); - ret = -1; + switch (alg) { + case WPA_ALG_WEP: + if (key_len == 5) + NLA_PUT_U32(msg, NL80211_KEY_CIPHER, + WLAN_CIPHER_SUITE_WEP40); + else + NLA_PUT_U32(msg, NL80211_KEY_CIPHER, + WLAN_CIPHER_SUITE_WEP104); + break; + case WPA_ALG_TKIP: + NLA_PUT_U32(msg, NL80211_KEY_CIPHER, WLAN_CIPHER_SUITE_TKIP); + break; + case WPA_ALG_CCMP: + NLA_PUT_U32(msg, NL80211_KEY_CIPHER, WLAN_CIPHER_SUITE_CCMP); + break; + case WPA_ALG_IGTK: + NLA_PUT_U32(msg, NL80211_KEY_CIPHER, + WLAN_CIPHER_SUITE_AES_CMAC); + break; + default: + wpa_printf(MSG_ERROR, "%s: Unsupported encryption " + "algorithm %d", __func__, alg); + return -1; } - return ret; -} - + if (seq && seq_len) + NLA_PUT(msg, NL80211_KEY_SEQ, seq_len, seq); -static int wpa_driver_nl80211_deauthenticate(void *priv, const u8 *addr, - int reason_code) -{ - struct wpa_driver_nl80211_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - return wpa_driver_nl80211_mlme(drv, addr, IW_MLME_DEAUTH, reason_code); -} + NLA_PUT(msg, NL80211_KEY_DATA, key_len, key); + nla_nest_end(msg, key_attr); -static int wpa_driver_nl80211_disassociate(void *priv, const u8 *addr, - int reason_code) -{ - struct wpa_driver_nl80211_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - return wpa_driver_nl80211_mlme(drv, addr, IW_MLME_DISASSOC, - reason_code); + return 0; + nla_put_failure: + return -1; } -static int wpa_driver_nl80211_set_gen_ie(void *priv, const u8 *ie, - size_t ie_len) +static int nl80211_set_conn_keys(struct wpa_driver_associate_params *params, + struct nl_msg *msg) { - struct wpa_driver_nl80211_data *drv = priv; - struct iwreq iwr; - int ret = 0; + int i, privacy = 0; + struct nlattr *nl_keys, *nl_key; - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - iwr.u.data.pointer = (caddr_t) ie; - iwr.u.data.length = ie_len; - - if (ioctl(drv->ioctl_sock, SIOCSIWGENIE, &iwr) < 0) { - perror("ioctl[SIOCSIWGENIE]"); - ret = -1; + for (i = 0; i < 4; i++) { + if (!params->wep_key[i]) + continue; + privacy = 1; + break; } - - return ret; -} - - -static int wpa_driver_nl80211_cipher2wext(int cipher) -{ - switch (cipher) { - case CIPHER_NONE: - return IW_AUTH_CIPHER_NONE; - case CIPHER_WEP40: - return IW_AUTH_CIPHER_WEP40; - case CIPHER_TKIP: - return IW_AUTH_CIPHER_TKIP; - case CIPHER_CCMP: - return IW_AUTH_CIPHER_CCMP; - case CIPHER_WEP104: - return IW_AUTH_CIPHER_WEP104; - default: + if (!privacy) return 0; - } -} + NLA_PUT_FLAG(msg, NL80211_ATTR_PRIVACY); -static int wpa_driver_nl80211_keymgmt2wext(int keymgmt) -{ - switch (keymgmt) { - case KEY_MGMT_802_1X: - case KEY_MGMT_802_1X_NO_WPA: - return IW_AUTH_KEY_MGMT_802_1X; - case KEY_MGMT_PSK: - return IW_AUTH_KEY_MGMT_PSK; - default: - return 0; - } -} - + nl_keys = nla_nest_start(msg, NL80211_ATTR_KEYS); + if (!nl_keys) + goto nla_put_failure; -static int -wpa_driver_nl80211_auth_alg_fallback(struct wpa_driver_nl80211_data *drv, - struct wpa_driver_associate_params *params) -{ - struct iwreq iwr; - int ret = 0; + for (i = 0; i < 4; i++) { + if (!params->wep_key[i]) + continue; - wpa_printf(MSG_DEBUG, "WEXT: Driver did not support " - "SIOCSIWAUTH for AUTH_ALG, trying SIOCSIWENCODE"); + nl_key = nla_nest_start(msg, i); + if (!nl_key) + goto nla_put_failure; - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - /* Just changing mode, not actual keys */ - iwr.u.encoding.flags = 0; - iwr.u.encoding.pointer = (caddr_t) NULL; - iwr.u.encoding.length = 0; + NLA_PUT(msg, NL80211_KEY_DATA, params->wep_key_len[i], + params->wep_key[i]); + if (params->wep_key_len[i] == 5) + NLA_PUT_U32(msg, NL80211_KEY_CIPHER, + WLAN_CIPHER_SUITE_WEP40); + else + NLA_PUT_U32(msg, NL80211_KEY_CIPHER, + WLAN_CIPHER_SUITE_WEP104); - /* - * Note: IW_ENCODE_{OPEN,RESTRICTED} can be interpreted to mean two - * different things. Here they are used to indicate Open System vs. - * Shared Key authentication algorithm. However, some drivers may use - * them to select between open/restricted WEP encrypted (open = allow - * both unencrypted and encrypted frames; restricted = only allow - * encrypted frames). - */ + NLA_PUT_U8(msg, NL80211_KEY_IDX, i); - if (!drv->use_crypt) { - iwr.u.encoding.flags |= IW_ENCODE_DISABLED; - } else { - if (params->auth_alg & AUTH_ALG_OPEN_SYSTEM) - iwr.u.encoding.flags |= IW_ENCODE_OPEN; - if (params->auth_alg & AUTH_ALG_SHARED_KEY) - iwr.u.encoding.flags |= IW_ENCODE_RESTRICTED; - } + if (i == params->wep_tx_keyidx) + NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT); - if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) { - perror("ioctl[SIOCSIWENCODE]"); - ret = -1; + nla_nest_end(msg, nl_key); } + nla_nest_end(msg, nl_keys); - return ret; -} - - -static int wpa_driver_nl80211_associate( - void *priv, struct wpa_driver_associate_params *params) -{ - struct wpa_driver_nl80211_data *drv = priv; - int ret = 0; - int allow_unencrypted_eapol; - int value; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - /* - * If the driver did not support SIOCSIWAUTH, fallback to - * SIOCSIWENCODE here. - */ - if (drv->auth_alg_fallback && - wpa_driver_nl80211_auth_alg_fallback(drv, params) < 0) - ret = -1; - - if (!params->bssid && - wpa_driver_nl80211_set_bssid(drv, NULL) < 0) - ret = -1; - - /* TODO: should consider getting wpa version and cipher/key_mgmt suites - * from configuration, not from here, where only the selected suite is - * available */ - if (wpa_driver_nl80211_set_gen_ie(drv, params->wpa_ie, params->wpa_ie_len) - < 0) - ret = -1; - if (params->wpa_ie == NULL || params->wpa_ie_len == 0) - value = IW_AUTH_WPA_VERSION_DISABLED; - else if (params->wpa_ie[0] == WLAN_EID_RSN) - value = IW_AUTH_WPA_VERSION_WPA2; - else - value = IW_AUTH_WPA_VERSION_WPA; - if (wpa_driver_nl80211_set_auth_param(drv, - IW_AUTH_WPA_VERSION, value) < 0) - ret = -1; - value = wpa_driver_nl80211_cipher2wext(params->pairwise_suite); - if (wpa_driver_nl80211_set_auth_param(drv, - IW_AUTH_CIPHER_PAIRWISE, value) < 0) - ret = -1; - value = wpa_driver_nl80211_cipher2wext(params->group_suite); - if (wpa_driver_nl80211_set_auth_param(drv, - IW_AUTH_CIPHER_GROUP, value) < 0) - ret = -1; - value = wpa_driver_nl80211_keymgmt2wext(params->key_mgmt_suite); - if (wpa_driver_nl80211_set_auth_param(drv, - IW_AUTH_KEY_MGMT, value) < 0) - ret = -1; - value = params->key_mgmt_suite != KEY_MGMT_NONE || - params->pairwise_suite != CIPHER_NONE || - params->group_suite != CIPHER_NONE || - params->wpa_ie_len; - if (wpa_driver_nl80211_set_auth_param(drv, - IW_AUTH_PRIVACY_INVOKED, value) < 0) - ret = -1; - - /* Allow unencrypted EAPOL messages even if pairwise keys are set when - * not using WPA. IEEE 802.1X specifies that these frames are not - * encrypted, but WPA encrypts them when pairwise keys are in use. */ - if (params->key_mgmt_suite == KEY_MGMT_802_1X || - params->key_mgmt_suite == KEY_MGMT_PSK) - allow_unencrypted_eapol = 0; - else - allow_unencrypted_eapol = 1; - - if (wpa_driver_nl80211_set_auth_param(drv, - IW_AUTH_RX_UNENCRYPTED_EAPOL, - allow_unencrypted_eapol) < 0) - ret = -1; - if (params->freq && wpa_driver_nl80211_set_freq(drv, params->freq) < 0) - ret = -1; - if (wpa_driver_nl80211_set_ssid(drv, params->ssid, params->ssid_len) < 0) - ret = -1; - if (params->bssid && - wpa_driver_nl80211_set_bssid(drv, params->bssid) < 0) - ret = -1; - - return ret; -} - - -static int wpa_driver_nl80211_set_auth_alg(void *priv, int auth_alg) -{ - struct wpa_driver_nl80211_data *drv = priv; - int algs = 0, res; - - if (auth_alg & AUTH_ALG_OPEN_SYSTEM) - algs |= IW_AUTH_ALG_OPEN_SYSTEM; - if (auth_alg & AUTH_ALG_SHARED_KEY) - algs |= IW_AUTH_ALG_SHARED_KEY; - if (auth_alg & AUTH_ALG_LEAP) - algs |= IW_AUTH_ALG_LEAP; - if (algs == 0) { - /* at least one algorithm should be set */ - algs = IW_AUTH_ALG_OPEN_SYSTEM; - } + return 0; - res = wpa_driver_nl80211_set_auth_param(drv, IW_AUTH_80211_AUTH_ALG, - algs); - drv->auth_alg_fallback = res == -2; - return res; +nla_put_failure: + return -ENOBUFS; } -/** - * wpa_driver_nl80211_set_mode - Set wireless mode (infra/adhoc), SIOCSIWMODE - * @priv: Pointer to private wext data from wpa_driver_nl80211_init() - * @mode: 0 = infra/BSS (associate with an AP), 1 = adhoc/IBSS - * Returns: 0 on success, -1 on failure - */ -static int wpa_driver_nl80211_set_mode(void *priv, int mode) +static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, + const u8 *addr, int cmd, u16 reason_code, + int local_state_change) { - struct wpa_driver_nl80211_data *drv = priv; - int ret = -1, flags; + int ret = -1; struct nl_msg *msg; msg = nlmsg_alloc(); if (!msg) return -1; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_SET_INTERFACE, 0); + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, cmd, 0); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, - mode ? NL80211_IFTYPE_ADHOC : NL80211_IFTYPE_STATION); + NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason_code); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + if (local_state_change) + NLA_PUT_FLAG(msg, NL80211_ATTR_LOCAL_STATE_CHANGE); ret = send_and_recv_msgs(drv, msg, NULL, NULL); - if (!ret) - return 0; - else - goto try_again; - -nla_put_failure: - wpa_printf(MSG_ERROR, "nl80211: Failed to set interface mode: %d (%s)", - ret, strerror(-ret)); - return -1; - -try_again: - /* mac80211 doesn't allow mode changes while the device is up, so - * take the device down, try to set the mode again, and bring the - * device back up. - */ - if (wpa_driver_nl80211_get_ifflags(drv, &flags) == 0) { - (void) wpa_driver_nl80211_set_ifflags(drv, flags & ~IFF_UP); - - /* Try to set the mode again while the interface is down */ - msg = nlmsg_alloc(); - if (!msg) - return -1; - - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_SET_INTERFACE, 0); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, - mode ? NL80211_IFTYPE_ADHOC : - NL80211_IFTYPE_STATION); - ret = send_and_recv_msgs(drv, msg, NULL, NULL); - if (ret) { - wpa_printf(MSG_ERROR, "Failed to set interface %s " - "mode(try_again): %d (%s)", - drv->ifname, ret, strerror(-ret)); - } - - /* Ignore return value of get_ifflags to ensure that the device - * is always up like it was before this function was called. - */ - (void) wpa_driver_nl80211_get_ifflags(drv, &flags); - (void) wpa_driver_nl80211_set_ifflags(drv, flags | IFF_UP); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: MLME command failed: ret=%d " + "(%s)", ret, strerror(-ret)); + goto nla_put_failure; } + ret = 0; +nla_put_failure: + nlmsg_free(msg); return ret; } -static int wpa_driver_nl80211_pmksa(struct wpa_driver_nl80211_data *drv, - u32 cmd, const u8 *bssid, const u8 *pmkid) +static int wpa_driver_nl80211_disconnect(struct wpa_driver_nl80211_data *drv, + const u8 *addr, int reason_code) { - struct iwreq iwr; - struct iw_pmksa pmksa; - int ret = 0; - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - os_memset(&pmksa, 0, sizeof(pmksa)); - pmksa.cmd = cmd; - pmksa.bssid.sa_family = ARPHRD_ETHER; - if (bssid) - os_memcpy(pmksa.bssid.sa_data, bssid, ETH_ALEN); - if (pmkid) - os_memcpy(pmksa.pmkid, pmkid, IW_PMKID_LEN); - iwr.u.data.pointer = (caddr_t) &pmksa; - iwr.u.data.length = sizeof(pmksa); - - if (ioctl(drv->ioctl_sock, SIOCSIWPMKSA, &iwr) < 0) { - if (errno != EOPNOTSUPP) - perror("ioctl[SIOCSIWPMKSA]"); - ret = -1; - } - - return ret; + wpa_printf(MSG_DEBUG, "%s", __func__); + drv->associated = 0; + return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DISCONNECT, + reason_code, 0); } -static int wpa_driver_nl80211_add_pmkid(void *priv, const u8 *bssid, - const u8 *pmkid) +static int wpa_driver_nl80211_deauthenticate(void *priv, const u8 *addr, + int reason_code) { - struct wpa_driver_nl80211_data *drv = priv; - return wpa_driver_nl80211_pmksa(drv, IW_PMKSA_ADD, bssid, pmkid); + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) + return wpa_driver_nl80211_disconnect(drv, addr, reason_code); + wpa_printf(MSG_DEBUG, "%s", __func__); + drv->associated = 0; + return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DEAUTHENTICATE, + reason_code, 0); } -static int wpa_driver_nl80211_remove_pmkid(void *priv, const u8 *bssid, - const u8 *pmkid) +static int wpa_driver_nl80211_disassociate(void *priv, const u8 *addr, + int reason_code) { - struct wpa_driver_nl80211_data *drv = priv; - return wpa_driver_nl80211_pmksa(drv, IW_PMKSA_REMOVE, bssid, pmkid); + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) + return wpa_driver_nl80211_disconnect(drv, addr, reason_code); + wpa_printf(MSG_DEBUG, "%s", __func__); + drv->associated = 0; + return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DISASSOCIATE, + reason_code, 0); } -static int wpa_driver_nl80211_flush_pmkid(void *priv) +static int wpa_driver_nl80211_authenticate( + void *priv, struct wpa_driver_auth_params *params) { - struct wpa_driver_nl80211_data *drv = priv; - return wpa_driver_nl80211_pmksa(drv, IW_PMKSA_FLUSH, NULL, NULL); -} + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + int ret = -1, i; + struct nl_msg *msg; + enum nl80211_auth_type type; + int count = 0; + drv->associated = 0; + os_memset(drv->auth_bssid, 0, ETH_ALEN); + /* FIX: IBSS mode */ + if (drv->nlmode != NL80211_IFTYPE_STATION) + wpa_driver_nl80211_set_mode(priv, IEEE80211_MODE_INFRA); -static int wpa_driver_nl80211_get_capa(void *priv, - struct wpa_driver_capa *capa) -{ - struct wpa_driver_nl80211_data *drv = priv; - if (!drv->has_capability) + if (wpa_driver_nl80211_set_mode(priv, IEEE80211_MODE_INFRA) < 0) return -1; - os_memcpy(capa, &drv->capa, sizeof(*capa)); - return 0; -} +retry: + msg = nlmsg_alloc(); + if (!msg) + return -1; -static int wpa_driver_nl80211_set_operstate(void *priv, int state) -{ - struct wpa_driver_nl80211_data *drv = priv; + wpa_printf(MSG_DEBUG, "nl80211: Authenticate (ifindex=%d)", + drv->ifindex); - wpa_printf(MSG_DEBUG, "%s: operstate %d->%d (%s)", - __func__, drv->operstate, state, state ? "UP" : "DORMANT"); - drv->operstate = state; - return wpa_driver_nl80211_send_oper_ifla( - drv, -1, state ? IF_OPER_UP : IF_OPER_DORMANT); -} + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_AUTHENTICATE, 0); + for (i = 0; i < 4; i++) { + if (!params->wep_key[i]) + continue; + wpa_driver_nl80211_set_key(bss->ifname, priv, WPA_ALG_WEP, + NULL, i, + i == params->wep_tx_keyidx, NULL, 0, + params->wep_key[i], + params->wep_key_len[i]); + if (params->wep_tx_keyidx != i) + continue; + if (nl_add_key(msg, WPA_ALG_WEP, i, 1, NULL, 0, + params->wep_key[i], params->wep_key_len[i])) { + nlmsg_free(msg); + return -1; + } + } -#ifdef CONFIG_CLIENT_MLME -static int wpa_driver_nl80211_open_mlme(struct wpa_driver_nl80211_data *drv) -{ - if (wpa_driver_nl80211_set_userspace_mlme(drv, 1) < 0) { - wpa_printf(MSG_ERROR, "nl80211: Failed to enable userspace " - "MLME"); - return -1; + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + if (params->bssid) { + wpa_printf(MSG_DEBUG, " * bssid=" MACSTR, + MAC2STR(params->bssid)); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid); } - if (wpa_driver_nl80211_create_monitor_interface(drv)) { - wpa_printf(MSG_ERROR, "nl80211: Failed to create monitor " - "interface"); - return -1; + if (params->freq) { + wpa_printf(MSG_DEBUG, " * freq=%d", params->freq); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq); + } + if (params->ssid) { + wpa_hexdump_ascii(MSG_DEBUG, " * SSID", + params->ssid, params->ssid_len); + NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len, + params->ssid); + } + wpa_hexdump(MSG_DEBUG, " * IEs", params->ie, params->ie_len); + if (params->ie) + NLA_PUT(msg, NL80211_ATTR_IE, params->ie_len, params->ie); + if (params->auth_alg & WPA_AUTH_ALG_OPEN) + type = NL80211_AUTHTYPE_OPEN_SYSTEM; + else if (params->auth_alg & WPA_AUTH_ALG_SHARED) + type = NL80211_AUTHTYPE_SHARED_KEY; + else if (params->auth_alg & WPA_AUTH_ALG_LEAP) + type = NL80211_AUTHTYPE_NETWORK_EAP; + else if (params->auth_alg & WPA_AUTH_ALG_FT) + type = NL80211_AUTHTYPE_FT; + else + goto nla_put_failure; + wpa_printf(MSG_DEBUG, " * Auth Type %d", type); + NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, type); + if (params->local_state_change) { + wpa_printf(MSG_DEBUG, " * Local state change only"); + NLA_PUT_FLAG(msg, NL80211_ATTR_LOCAL_STATE_CHANGE); } - return 0; -} -#endif /* CONFIG_CLIENT_MLME */ - - -static int wpa_driver_nl80211_set_param(void *priv, const char *param) -{ -#ifdef CONFIG_CLIENT_MLME - struct wpa_driver_nl80211_data *drv = priv; - - if (param == NULL) - return 0; - - wpa_printf(MSG_DEBUG, "%s: param='%s'", __func__, param); - - if (os_strstr(param, "use_mlme=1")) { - wpa_printf(MSG_DEBUG, "nl80211: Using user space MLME"); - drv->capa.flags |= WPA_DRIVER_FLAGS_USER_SPACE_MLME; - if (wpa_driver_nl80211_open_mlme(drv)) - return -1; + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: MLME command failed: ret=%d " + "(%s)", ret, strerror(-ret)); + count++; + if (ret == -EALREADY && count == 1 && params->bssid && + !params->local_state_change) { + /* + * mac80211 does not currently accept new + * authentication if we are already authenticated. As a + * workaround, force deauthentication and try again. + */ + wpa_printf(MSG_DEBUG, "nl80211: Retry authentication " + "after forced deauthentication"); + wpa_driver_nl80211_deauthenticate( + bss, params->bssid, + WLAN_REASON_PREV_AUTH_NOT_VALID); + nlmsg_free(msg); + goto retry; + } + goto nla_put_failure; } -#endif /* CONFIG_CLIENT_MLME */ + ret = 0; + wpa_printf(MSG_DEBUG, "nl80211: Authentication request send " + "successfully"); - return 0; +nla_put_failure: + nlmsg_free(msg); + return ret; } -#ifdef CONFIG_CLIENT_MLME - struct phy_info_arg { u16 *num_modes; - struct wpa_hw_modes *modes; + struct hostapd_hw_modes *modes; }; - static int phy_info_handler(struct nl_msg *msg, void *arg) { struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; @@ -2438,27 +2501,26 @@ static int phy_info_handler(struct nl_msg *msg, void *arg) struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1]; struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1]; - static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] - = { + static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = { [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 }, [NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG }, [NL80211_FREQUENCY_ATTR_PASSIVE_SCAN] = { .type = NLA_FLAG }, [NL80211_FREQUENCY_ATTR_NO_IBSS] = { .type = NLA_FLAG }, [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 }, }; struct nlattr *tb_rate[NL80211_BITRATE_ATTR_MAX + 1]; static struct nla_policy rate_policy[NL80211_BITRATE_ATTR_MAX + 1] = { [NL80211_BITRATE_ATTR_RATE] = { .type = NLA_U32 }, - [NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE] = - { .type = NLA_FLAG }, + [NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE] = { .type = NLA_FLAG }, }; struct nlattr *nl_band; struct nlattr *nl_freq; struct nlattr *nl_rate; int rem_band, rem_freq, rem_rate; - struct wpa_hw_modes *mode; + struct hostapd_hw_modes *mode; int idx, mode_is_set; nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), @@ -2467,10 +2529,8 @@ static int phy_info_handler(struct nl_msg *msg, void *arg) if (!tb_msg[NL80211_ATTR_WIPHY_BANDS]) return NL_SKIP; - nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], - rem_band) { - mode = os_realloc(phy_info->modes, - (*phy_info->num_modes + 1) * sizeof(*mode)); + nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band) { + mode = os_realloc(phy_info->modes, (*phy_info->num_modes + 1) * sizeof(*mode)); if (!mode) return NL_SKIP; phy_info->modes = mode; @@ -2478,108 +2538,123 @@ static int phy_info_handler(struct nl_msg *msg, void *arg) mode_is_set = 0; mode = &phy_info->modes[*(phy_info->num_modes)]; - os_memset(mode, 0, sizeof(*mode)); + memset(mode, 0, sizeof(*mode)); *(phy_info->num_modes) += 1; nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band), nla_len(nl_band), NULL); - nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], - rem_freq) { - nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, - nla_data(nl_freq), nla_len(nl_freq), - freq_policy); + if (tb_band[NL80211_BAND_ATTR_HT_CAPA]) { + mode->ht_capab = nla_get_u16( + tb_band[NL80211_BAND_ATTR_HT_CAPA]); + } + + if (tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR]) { + mode->a_mpdu_params |= nla_get_u8( + tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR]) & + 0x03; + } + + if (tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY]) { + mode->a_mpdu_params |= nla_get_u8( + tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY]) << + 2; + } + + if (tb_band[NL80211_BAND_ATTR_HT_MCS_SET] && + nla_len(tb_band[NL80211_BAND_ATTR_HT_MCS_SET])) { + u8 *mcs; + mcs = nla_data(tb_band[NL80211_BAND_ATTR_HT_MCS_SET]); + os_memcpy(mode->mcs_set, mcs, 16); + } + + nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) { + nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq), + nla_len(nl_freq), freq_policy); if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) continue; mode->num_channels++; } - mode->channels = os_zalloc(mode->num_channels * - sizeof(struct wpa_channel_data)); + mode->channels = os_zalloc(mode->num_channels * sizeof(struct hostapd_channel_data)); if (!mode->channels) return NL_SKIP; idx = 0; - nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], - rem_freq) { - nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, - nla_data(nl_freq), nla_len(nl_freq), - freq_policy); + nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) { + nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq), + nla_len(nl_freq), freq_policy); if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) continue; - mode->channels[idx].freq = nla_get_u32( - tb_freq[NL80211_FREQUENCY_ATTR_FREQ]); - mode->channels[idx].flag |= WPA_CHAN_W_SCAN | - WPA_CHAN_W_ACTIVE_SCAN | - WPA_CHAN_W_IBSS; + mode->channels[idx].freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]); + mode->channels[idx].flag = 0; if (!mode_is_set) { /* crude heuristic */ if (mode->channels[idx].freq < 4000) - mode->mode = WPA_MODE_IEEE80211B; + mode->mode = HOSTAPD_MODE_IEEE80211B; else - mode->mode = WPA_MODE_IEEE80211A; + mode->mode = HOSTAPD_MODE_IEEE80211A; mode_is_set = 1; } /* crude heuristic */ - if (mode->channels[idx].freq < 4000) { + if (mode->channels[idx].freq < 4000) if (mode->channels[idx].freq == 2484) mode->channels[idx].chan = 14; else - mode->channels[idx].chan = - (mode->channels[idx].freq - - 2407) / 5; - } else - mode->channels[idx].chan = - mode->channels[idx].freq / 5 - 1000; + mode->channels[idx].chan = (mode->channels[idx].freq - 2407) / 5; + else + mode->channels[idx].chan = mode->channels[idx].freq/5 - 1000; if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED]) - mode->channels[idx].flag &= ~WPA_CHAN_W_SCAN; + mode->channels[idx].flag |= + HOSTAPD_CHAN_DISABLED; if (tb_freq[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN]) - mode->channels[idx].flag &= - ~WPA_CHAN_W_ACTIVE_SCAN; + mode->channels[idx].flag |= + HOSTAPD_CHAN_PASSIVE_SCAN; if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IBSS]) - mode->channels[idx].flag &= ~WPA_CHAN_W_IBSS; + mode->channels[idx].flag |= + HOSTAPD_CHAN_NO_IBSS; + if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR]) + mode->channels[idx].flag |= + HOSTAPD_CHAN_RADAR; + + if (tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER] && + !tb_freq[NL80211_FREQUENCY_ATTR_DISABLED]) + mode->channels[idx].max_tx_power = + nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER]) / 100; + idx++; } - nla_for_each_nested(nl_rate, tb_band[NL80211_BAND_ATTR_RATES], - rem_rate) { - nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, - nla_data(nl_rate), nla_len(nl_rate), - rate_policy); + nla_for_each_nested(nl_rate, tb_band[NL80211_BAND_ATTR_RATES], rem_rate) { + nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, nla_data(nl_rate), + nla_len(nl_rate), rate_policy); if (!tb_rate[NL80211_BITRATE_ATTR_RATE]) continue; mode->num_rates++; } - mode->rates = os_zalloc(mode->num_rates * - sizeof(struct wpa_rate_data)); + mode->rates = os_zalloc(mode->num_rates * sizeof(int)); if (!mode->rates) return NL_SKIP; idx = 0; - nla_for_each_nested(nl_rate, tb_band[NL80211_BAND_ATTR_RATES], - rem_rate) { - nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, - nla_data(nl_rate), nla_len(nl_rate), - rate_policy); + nla_for_each_nested(nl_rate, tb_band[NL80211_BAND_ATTR_RATES], rem_rate) { + nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, nla_data(nl_rate), + nla_len(nl_rate), rate_policy); if (!tb_rate[NL80211_BITRATE_ATTR_RATE]) continue; - mode->rates[idx].rate = nla_get_u32( - tb_rate[NL80211_BITRATE_ATTR_RATE]); + mode->rates[idx] = nla_get_u32(tb_rate[NL80211_BITRATE_ATTR_RATE]); /* crude heuristic */ - if (mode->mode == WPA_MODE_IEEE80211B && - mode->rates[idx].rate > 200) - mode->mode = WPA_MODE_IEEE80211G; - - if (tb_rate[NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE]) - mode->rates[idx].flags |= WPA_RATE_PREAMBLE2; + if (mode->mode == HOSTAPD_MODE_IEEE80211B && + mode->rates[idx] > 200) + mode->mode = HOSTAPD_MODE_IEEE80211G; idx++; } @@ -2588,11 +2663,85 @@ static int phy_info_handler(struct nl_msg *msg, void *arg) return NL_SKIP; } +static struct hostapd_hw_modes * +wpa_driver_nl80211_add_11b(struct hostapd_hw_modes *modes, u16 *num_modes) +{ + u16 m; + struct hostapd_hw_modes *mode11g = NULL, *nmodes, *mode; + int i, mode11g_idx = -1; + + /* If only 802.11g mode is included, use it to construct matching + * 802.11b mode data. */ + + for (m = 0; m < *num_modes; m++) { + if (modes[m].mode == HOSTAPD_MODE_IEEE80211B) + return modes; /* 802.11b already included */ + if (modes[m].mode == HOSTAPD_MODE_IEEE80211G) + mode11g_idx = m; + } + + if (mode11g_idx < 0) + return modes; /* 2.4 GHz band not supported at all */ + + nmodes = os_realloc(modes, (*num_modes + 1) * sizeof(*nmodes)); + if (nmodes == NULL) + return modes; /* Could not add 802.11b mode */ + + mode = &nmodes[*num_modes]; + os_memset(mode, 0, sizeof(*mode)); + (*num_modes)++; + modes = nmodes; + + mode->mode = HOSTAPD_MODE_IEEE80211B; + + mode11g = &modes[mode11g_idx]; + mode->num_channels = mode11g->num_channels; + mode->channels = os_malloc(mode11g->num_channels * + sizeof(struct hostapd_channel_data)); + if (mode->channels == NULL) { + (*num_modes)--; + return modes; /* Could not add 802.11b mode */ + } + os_memcpy(mode->channels, mode11g->channels, + mode11g->num_channels * sizeof(struct hostapd_channel_data)); + + mode->num_rates = 0; + mode->rates = os_malloc(4 * sizeof(int)); + if (mode->rates == NULL) { + os_free(mode->channels); + (*num_modes)--; + return modes; /* Could not add 802.11b mode */ + } + + for (i = 0; i < mode11g->num_rates; i++) { + if (mode11g->rates[i] != 10 && mode11g->rates[i] != 20 && + mode11g->rates[i] != 55 && mode11g->rates[i] != 110) + continue; + mode->rates[mode->num_rates] = mode11g->rates[i]; + mode->num_rates++; + if (mode->num_rates == 4) + break; + } + + if (mode->num_rates == 0) { + os_free(mode->channels); + os_free(mode->rates); + (*num_modes)--; + return modes; /* No 802.11b rates */ + } + + wpa_printf(MSG_DEBUG, "nl80211: Added 802.11b mode based on 802.11g " + "information"); -static struct wpa_hw_modes * + return modes; +} + + +static struct hostapd_hw_modes * wpa_driver_nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; struct phy_info_arg result = { .num_modes = num_modes, @@ -2612,28 +2761,21 @@ wpa_driver_nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) - return result.modes; -nla_put_failure: + return wpa_driver_nl80211_add_11b(result.modes, num_modes); + nla_put_failure: return NULL; } -static int wpa_driver_nl80211_set_channel(void *priv, wpa_hw_mode phymode, - int chan, int freq) -{ - return wpa_driver_nl80211_set_freq(priv, freq); -} - - -static int wpa_driver_nl80211_send_mlme(void *priv, const u8 *data, - size_t data_len) +static int wpa_driver_nl80211_send_frame(struct wpa_driver_nl80211_data *drv, + const void *data, size_t len, + int encrypt) { - struct wpa_driver_nl80211_data *drv = priv; __u8 rtap_hdr[] = { 0x00, 0x00, /* radiotap version */ 0x0e, 0x00, /* radiotap length */ 0x02, 0xc0, 0x00, 0x00, /* bmap: flags, tx and rx flags */ - 0x0c, /* F_WEP | F_FRAG (encrypt/fragment if required) */ + IEEE80211_RADIOTAP_F_FRAG, /* F_FRAG (fragment if required) */ 0x00, /* padding */ 0x00, 0x00, /* RX and TX flags to indicate that */ 0x00, 0x00, /* this is the injected frame directly */ @@ -2645,7 +2787,7 @@ static int wpa_driver_nl80211_send_mlme(void *priv, const u8 *data, }, { .iov_base = (void *) data, - .iov_len = data_len, + .iov_len = len, } }; struct msghdr msg = { @@ -2658,22 +2800,138 @@ static int wpa_driver_nl80211_send_mlme(void *priv, const u8 *data, .msg_flags = 0, }; - if (sendmsg(drv->monitor_sock, &msg, 0) < 0) { - perror("send[MLME]"); + if (encrypt) + rtap_hdr[8] |= IEEE80211_RADIOTAP_F_WEP; + + return sendmsg(drv->monitor_sock, &msg, 0); +} + + +static int wpa_driver_nl80211_send_mlme(void *priv, const u8 *data, + size_t data_len) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct ieee80211_mgmt *mgmt; + int encrypt = 1; + u16 fc; + + mgmt = (struct ieee80211_mgmt *) data; + fc = le_to_host16(mgmt->frame_control); + + if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && + WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_AUTH) { + /* + * Only one of the authentication frame types is encrypted. + * In order for static WEP encryption to work properly (i.e., + * to not encrypt the frame), we need to tell mac80211 about + * the frames that must not be encrypted. + */ + u16 auth_alg = le_to_host16(mgmt->u.auth.auth_alg); + u16 auth_trans = le_to_host16(mgmt->u.auth.auth_transaction); + if (auth_alg != WLAN_AUTH_SHARED_KEY || auth_trans != 3) + encrypt = 0; + } + + return wpa_driver_nl80211_send_frame(drv, data, data_len, encrypt); +} + + +static int wpa_driver_nl80211_set_beacon(void *priv, + const u8 *head, size_t head_len, + const u8 *tail, size_t tail_len, + int dtim_period, int beacon_int) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + u8 cmd = NL80211_CMD_NEW_BEACON; + int ret; + int beacon_set; + int ifindex = if_nametoindex(bss->ifname); + + beacon_set = bss->beacon_set; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + wpa_printf(MSG_DEBUG, "nl80211: Set beacon (beacon_set=%d)", + beacon_set); + if (beacon_set) + cmd = NL80211_CMD_SET_BEACON; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, cmd, 0); + NLA_PUT(msg, NL80211_ATTR_BEACON_HEAD, head_len, head); + NLA_PUT(msg, NL80211_ATTR_BEACON_TAIL, tail_len, tail); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); + NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, beacon_int); + NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, dtim_period); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)", + ret, strerror(-ret)); + } else { + bss->beacon_set = 1; + } + return ret; + nla_put_failure: + return -ENOBUFS; +} + + +static int wpa_driver_nl80211_set_freq(struct wpa_driver_nl80211_data *drv, + int freq, int ht_enabled, + int sec_channel_offset) +{ + struct nl_msg *msg; + int ret; + + msg = nlmsg_alloc(); + if (!msg) return -1; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_SET_WIPHY, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); + if (ht_enabled) { + switch (sec_channel_offset) { + case -1: + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, + NL80211_CHAN_HT40MINUS); + break; + case 1: + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, + NL80211_CHAN_HT40PLUS); + break; + default: + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, + NL80211_CHAN_HT20); + break; + } } - return 0; + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret == 0) + return 0; + wpa_printf(MSG_DEBUG, "nl80211: Failed to set channel (freq=%d): " + "%d (%s)", freq, ret, strerror(-ret)); +nla_put_failure: + return -1; } -static int wpa_driver_nl80211_mlme_add_sta(void *priv, const u8 *addr, - const u8 *supp_rates, - size_t supp_rates_len) +static int wpa_driver_nl80211_sta_add(void *priv, + struct hostapd_sta_add_params *params) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; - int ret = -1; + int ret = -ENOBUFS; msg = nlmsg_alloc(); if (!msg) @@ -2682,50 +2940,2392 @@ static int wpa_driver_nl80211_mlme_add_sta(void *priv, const u8 *addr, genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, NL80211_CMD_NEW_STATION, 0); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->addr); + NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, params->aid); + NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_RATES, params->supp_rates_len, + params->supp_rates); + NLA_PUT_U16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL, + params->listen_interval); + if (params->ht_capabilities) { + NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY, + sizeof(*params->ht_capabilities), + params->ht_capabilities); + } + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) + wpa_printf(MSG_DEBUG, "nl80211: NL80211_CMD_NEW_STATION " + "result: %d (%s)", ret, strerror(-ret)); + if (ret == -EEXIST) + ret = 0; + nla_put_failure: + return ret; +} + + +static int wpa_driver_nl80211_sta_remove(void *priv, const u8 *addr) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_DEL_STATION, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, + if_nametoindex(bss->ifname)); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret == -ENOENT) + return 0; + return ret; + nla_put_failure: + return -ENOBUFS; +} + + +static void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, + int ifidx) +{ + struct nl_msg *msg; + + wpa_printf(MSG_DEBUG, "nl80211: Remove interface ifindex=%d", ifidx); + +#ifdef HOSTAPD + /* stop listening for EAPOL on this interface */ + del_ifidx(drv, ifidx); +#endif /* HOSTAPD */ + + msg = nlmsg_alloc(); + if (!msg) + goto nla_put_failure; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_DEL_INTERFACE, 0); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifidx); + + if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0) + return; + nla_put_failure: + wpa_printf(MSG_ERROR, "Failed to remove interface (ifidx=%d)", ifidx); +} + + +static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv, + const char *ifname, + enum nl80211_iftype iftype, + const u8 *addr, int wds) +{ + struct nl_msg *msg, *flags = NULL; + int ifidx; + int ret = -ENOBUFS; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_NEW_INTERFACE, 0); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, ifname); + NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, iftype); + + if (iftype == NL80211_IFTYPE_MONITOR) { + int err; + + flags = nlmsg_alloc(); + if (!flags) + goto nla_put_failure; + + NLA_PUT_FLAG(flags, NL80211_MNTR_FLAG_COOK_FRAMES); + + err = nla_put_nested(msg, NL80211_ATTR_MNTR_FLAGS, flags); + + nlmsg_free(flags); + + if (err) + goto nla_put_failure; + } else if (wds) { + NLA_PUT_U8(msg, NL80211_ATTR_4ADDR, wds); + } + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) { + nla_put_failure: + wpa_printf(MSG_ERROR, "Failed to create interface %s: %d (%s)", + ifname, ret, strerror(-ret)); + return ret; + } + + ifidx = if_nametoindex(ifname); + wpa_printf(MSG_DEBUG, "nl80211: New interface %s created: ifindex=%d", + ifname, ifidx); + + if (ifidx <= 0) + return -1; + +#ifdef HOSTAPD + /* start listening for EAPOL on this interface */ + add_ifidx(drv, ifidx); +#endif /* HOSTAPD */ + + if (addr && iftype != NL80211_IFTYPE_MONITOR && + linux_set_ifhwaddr(drv->ioctl_sock, ifname, addr)) { + nl80211_remove_iface(drv, ifidx); + return -1; + } + + return ifidx; +} + + +static int nl80211_create_iface(struct wpa_driver_nl80211_data *drv, + const char *ifname, enum nl80211_iftype iftype, + const u8 *addr, int wds) +{ + int ret; + + ret = nl80211_create_iface_once(drv, ifname, iftype, addr, wds); + + /* if error occured and interface exists already */ + if (ret == -ENFILE && if_nametoindex(ifname)) { + wpa_printf(MSG_INFO, "Try to remove and re-create %s", ifname); + + /* Try to remove the interface that was already there. */ + nl80211_remove_iface(drv, if_nametoindex(ifname)); + + /* Try to create the interface again */ + ret = nl80211_create_iface_once(drv, ifname, iftype, addr, + wds); + } + + if (ret >= 0 && drv->disable_11b_rates) + nl80211_disable_11b_rates(drv, ret, 1); + + return ret; +} + + +static void handle_tx_callback(void *ctx, u8 *buf, size_t len, int ok) +{ + struct ieee80211_hdr *hdr; + u16 fc; + union wpa_event_data event; + + hdr = (struct ieee80211_hdr *) buf; + fc = le_to_host16(hdr->frame_control); + + os_memset(&event, 0, sizeof(event)); + event.tx_status.type = WLAN_FC_GET_TYPE(fc); + event.tx_status.stype = WLAN_FC_GET_STYPE(fc); + event.tx_status.dst = hdr->addr1; + event.tx_status.data = buf; + event.tx_status.data_len = len; + event.tx_status.ack = ok; + wpa_supplicant_event(ctx, EVENT_TX_STATUS, &event); +} + + +static void from_unknown_sta(struct wpa_driver_nl80211_data *drv, + u8 *buf, size_t len) +{ + union wpa_event_data event; + os_memset(&event, 0, sizeof(event)); + event.rx_from_unknown.frame = buf; + event.rx_from_unknown.len = len; + wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event); +} + + +static void handle_frame(struct wpa_driver_nl80211_data *drv, + u8 *buf, size_t len, int datarate, int ssi_signal) +{ + struct ieee80211_hdr *hdr; + u16 fc; + union wpa_event_data event; + + hdr = (struct ieee80211_hdr *) buf; + fc = le_to_host16(hdr->frame_control); + + switch (WLAN_FC_GET_TYPE(fc)) { + case WLAN_FC_TYPE_MGMT: + os_memset(&event, 0, sizeof(event)); + event.rx_mgmt.frame = buf; + event.rx_mgmt.frame_len = len; + event.rx_mgmt.datarate = datarate; + event.rx_mgmt.ssi_signal = ssi_signal; + wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event); + break; + case WLAN_FC_TYPE_CTRL: + /* can only get here with PS-Poll frames */ + wpa_printf(MSG_DEBUG, "CTRL"); + from_unknown_sta(drv, buf, len); + break; + case WLAN_FC_TYPE_DATA: + from_unknown_sta(drv, buf, len); + break; + } +} + + +static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct wpa_driver_nl80211_data *drv = eloop_ctx; + int len; + unsigned char buf[3000]; + struct ieee80211_radiotap_iterator iter; + int ret; + int datarate = 0, ssi_signal = 0; + int injected = 0, failed = 0, rxflags = 0; + + len = recv(sock, buf, sizeof(buf), 0); + if (len < 0) { + perror("recv"); + return; + } + + if (drv->nlmode == NL80211_IFTYPE_STATION && !drv->probe_req_report) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore monitor interface " + "frame since Probe Request reporting is disabled"); + return; + } + + if (ieee80211_radiotap_iterator_init(&iter, (void*)buf, len)) { + printf("received invalid radiotap frame\n"); + return; + } + + while (1) { + ret = ieee80211_radiotap_iterator_next(&iter); + if (ret == -ENOENT) + break; + if (ret) { + printf("received invalid radiotap frame (%d)\n", ret); + return; + } + switch (iter.this_arg_index) { + case IEEE80211_RADIOTAP_FLAGS: + if (*iter.this_arg & IEEE80211_RADIOTAP_F_FCS) + len -= 4; + break; + case IEEE80211_RADIOTAP_RX_FLAGS: + rxflags = 1; + break; + case IEEE80211_RADIOTAP_TX_FLAGS: + injected = 1; + failed = le_to_host16((*(uint16_t *) iter.this_arg)) & + IEEE80211_RADIOTAP_F_TX_FAIL; + break; + case IEEE80211_RADIOTAP_DATA_RETRIES: + break; + case IEEE80211_RADIOTAP_CHANNEL: + /* TODO: convert from freq/flags to channel number */ + break; + case IEEE80211_RADIOTAP_RATE: + datarate = *iter.this_arg * 5; + break; + case IEEE80211_RADIOTAP_DB_ANTSIGNAL: + ssi_signal = *iter.this_arg; + break; + } + } + + if (rxflags && injected) + return; + + if (!injected) + handle_frame(drv, buf + iter.max_length, + len - iter.max_length, datarate, ssi_signal); + else + handle_tx_callback(drv->ctx, buf + iter.max_length, + len - iter.max_length, !failed); +} + + +/* + * we post-process the filter code later and rewrite + * this to the offset to the last instruction + */ +#define PASS 0xFF +#define FAIL 0xFE + +static struct sock_filter msock_filter_insns[] = { + /* + * do a little-endian load of the radiotap length field + */ + /* load lower byte into A */ + BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 2), + /* put it into X (== index register) */ + BPF_STMT(BPF_MISC| BPF_TAX, 0), + /* load upper byte into A */ + BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 3), + /* left-shift it by 8 */ + BPF_STMT(BPF_ALU | BPF_LSH | BPF_K, 8), + /* or with X */ + BPF_STMT(BPF_ALU | BPF_OR | BPF_X, 0), + /* put result into X */ + BPF_STMT(BPF_MISC| BPF_TAX, 0), + + /* + * Allow management frames through, this also gives us those + * management frames that we sent ourselves with status + */ + /* load the lower byte of the IEEE 802.11 frame control field */ + BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), + /* mask off frame type and version */ + BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0xF), + /* accept frame if it's both 0, fall through otherwise */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, PASS, 0), + + /* + * TODO: add a bit to radiotap RX flags that indicates + * that the sending station is not associated, then + * add a filter here that filters on our DA and that flag + * to allow us to deauth frames to that bad station. + * + * Not a regression -- we didn't do it before either. + */ + +#if 0 + /* + * drop non-data frames + */ + /* load the lower byte of the frame control field */ + BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), + /* mask off QoS bit */ + BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x0c), + /* drop non-data frames */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 8, 0, FAIL), +#endif + /* load the upper byte of the frame control field */ + BPF_STMT(BPF_LD | BPF_B | BPF_IND, 1), + /* mask off toDS/fromDS */ + BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x03), + /* accept WDS frames */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 3, PASS, 0), + + /* + * add header length to index + */ + /* load the lower byte of the frame control field */ + BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), + /* mask off QoS bit */ + BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x80), + /* right shift it by 6 to give 0 or 2 */ + BPF_STMT(BPF_ALU | BPF_RSH | BPF_K, 6), + /* add data frame header length */ + BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 24), + /* add index, was start of 802.11 header */ + BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0), + /* move to index, now start of LL header */ + BPF_STMT(BPF_MISC | BPF_TAX, 0), + + /* + * Accept empty data frames, we use those for + * polling activity. + */ + BPF_STMT(BPF_LD | BPF_W | BPF_LEN, 0), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_X, 0, PASS, 0), + + /* + * Accept EAPOL frames + */ + BPF_STMT(BPF_LD | BPF_W | BPF_IND, 0), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0xAAAA0300, 0, FAIL), + BPF_STMT(BPF_LD | BPF_W | BPF_IND, 4), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0000888E, PASS, FAIL), + + /* keep these last two statements or change the code below */ + /* return 0 == "DROP" */ + BPF_STMT(BPF_RET | BPF_K, 0), + /* return ~0 == "keep all" */ + BPF_STMT(BPF_RET | BPF_K, ~0), +}; + +static struct sock_fprog msock_filter = { + .len = sizeof(msock_filter_insns)/sizeof(msock_filter_insns[0]), + .filter = msock_filter_insns, +}; + + +static int add_monitor_filter(int s) +{ + int idx; + + /* rewrite all PASS/FAIL jump offsets */ + for (idx = 0; idx < msock_filter.len; idx++) { + struct sock_filter *insn = &msock_filter_insns[idx]; + + if (BPF_CLASS(insn->code) == BPF_JMP) { + if (insn->code == (BPF_JMP|BPF_JA)) { + if (insn->k == PASS) + insn->k = msock_filter.len - idx - 2; + else if (insn->k == FAIL) + insn->k = msock_filter.len - idx - 3; + } + + if (insn->jt == PASS) + insn->jt = msock_filter.len - idx - 2; + else if (insn->jt == FAIL) + insn->jt = msock_filter.len - idx - 3; + + if (insn->jf == PASS) + insn->jf = msock_filter.len - idx - 2; + else if (insn->jf == FAIL) + insn->jf = msock_filter.len - idx - 3; + } + } + + if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, + &msock_filter, sizeof(msock_filter))) { + perror("SO_ATTACH_FILTER"); + return -1; + } + + return 0; +} + + +static void nl80211_remove_monitor_interface( + struct wpa_driver_nl80211_data *drv) +{ + if (drv->monitor_ifidx >= 0) { + nl80211_remove_iface(drv, drv->monitor_ifidx); + drv->monitor_ifidx = -1; + } + if (drv->monitor_sock >= 0) { + eloop_unregister_read_sock(drv->monitor_sock); + close(drv->monitor_sock); + drv->monitor_sock = -1; + } +} + + +static int +nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv) +{ + char buf[IFNAMSIZ]; + struct sockaddr_ll ll; + int optval; + socklen_t optlen; + + snprintf(buf, IFNAMSIZ, "mon.%s", drv->first_bss.ifname); + buf[IFNAMSIZ - 1] = '\0'; + + drv->monitor_ifidx = + nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL, + 0); + + if (drv->monitor_ifidx < 0) + return -1; + + if (linux_set_iface_flags(drv->ioctl_sock, buf, 1)) + goto error; + + memset(&ll, 0, sizeof(ll)); + ll.sll_family = AF_PACKET; + ll.sll_ifindex = drv->monitor_ifidx; + drv->monitor_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + if (drv->monitor_sock < 0) { + perror("socket[PF_PACKET,SOCK_RAW]"); + goto error; + } + + if (add_monitor_filter(drv->monitor_sock)) { + wpa_printf(MSG_INFO, "Failed to set socket filter for monitor " + "interface; do filtering in user space"); + /* This works, but will cost in performance. */ + } + + if (bind(drv->monitor_sock, (struct sockaddr *) &ll, sizeof(ll)) < 0) { + perror("monitor socket bind"); + goto error; + } + + optlen = sizeof(optval); + optval = 20; + if (setsockopt + (drv->monitor_sock, SOL_SOCKET, SO_PRIORITY, &optval, optlen)) { + perror("Failed to set socket priority"); + goto error; + } + + if (eloop_register_read_sock(drv->monitor_sock, handle_monitor_read, + drv, NULL)) { + printf("Could not register monitor read socket\n"); + goto error; + } + + return 0; + error: + nl80211_remove_monitor_interface(drv); + return -1; +} + + +static const u8 rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; + +static int wpa_driver_nl80211_hapd_send_eapol( + void *priv, const u8 *addr, const u8 *data, + size_t data_len, int encrypt, const u8 *own_addr) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct ieee80211_hdr *hdr; + size_t len; + u8 *pos; + int res; +#if 0 /* FIX */ + int qos = sta->flags & WPA_STA_WMM; +#else + int qos = 0; +#endif + + len = sizeof(*hdr) + (qos ? 2 : 0) + sizeof(rfc1042_header) + 2 + + data_len; + hdr = os_zalloc(len); + if (hdr == NULL) { + printf("malloc() failed for i802_send_data(len=%lu)\n", + (unsigned long) len); + return -1; + } + + hdr->frame_control = + IEEE80211_FC(WLAN_FC_TYPE_DATA, WLAN_FC_STYPE_DATA); + hdr->frame_control |= host_to_le16(WLAN_FC_FROMDS); + if (encrypt) + hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP); +#if 0 /* To be enabled if qos determination is added above */ + if (qos) { + hdr->frame_control |= + host_to_le16(WLAN_FC_STYPE_QOS_DATA << 4); + } +#endif + + memcpy(hdr->IEEE80211_DA_FROMDS, addr, ETH_ALEN); + memcpy(hdr->IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN); + memcpy(hdr->IEEE80211_SA_FROMDS, own_addr, ETH_ALEN); + pos = (u8 *) (hdr + 1); + +#if 0 /* To be enabled if qos determination is added above */ + if (qos) { + /* add an empty QoS header if needed */ + pos[0] = 0; + pos[1] = 0; + pos += 2; + } +#endif + + memcpy(pos, rfc1042_header, sizeof(rfc1042_header)); + pos += sizeof(rfc1042_header); + WPA_PUT_BE16(pos, ETH_P_PAE); + pos += 2; + memcpy(pos, data, data_len); + + res = wpa_driver_nl80211_send_frame(drv, (u8 *) hdr, len, encrypt); + if (res < 0) { + wpa_printf(MSG_ERROR, "i802_send_eapol - packet len: %lu - " + "failed: %d (%s)", + (unsigned long) len, errno, strerror(errno)); + } + os_free(hdr); + + return res; +} + + +static u32 sta_flags_nl80211(int flags) +{ + u32 f = 0; + + if (flags & WPA_STA_AUTHORIZED) + f |= BIT(NL80211_STA_FLAG_AUTHORIZED); + if (flags & WPA_STA_WMM) + f |= BIT(NL80211_STA_FLAG_WME); + if (flags & WPA_STA_SHORT_PREAMBLE) + f |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE); + if (flags & WPA_STA_MFP) + f |= BIT(NL80211_STA_FLAG_MFP); + + return f; +} + + +static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr, + int total_flags, + int flags_or, int flags_and) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg, *flags = NULL; + struct nl80211_sta_flag_update upd; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + flags = nlmsg_alloc(); + if (!flags) { + nlmsg_free(msg); + return -ENOMEM; + } + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_SET_STATION, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, + if_nametoindex(bss->ifname)); NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); - /* TODO: Get proper Association ID and listen interval */ - NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, 1); - NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_RATES, supp_rates_len, - supp_rates); - NLA_PUT_U16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL, 1); + /* + * Backwards compatibility version using NL80211_ATTR_STA_FLAGS. This + * can be removed eventually. + */ + if (total_flags & WPA_STA_AUTHORIZED) + NLA_PUT_FLAG(flags, NL80211_STA_FLAG_AUTHORIZED); + + if (total_flags & WPA_STA_WMM) + NLA_PUT_FLAG(flags, NL80211_STA_FLAG_WME); + + if (total_flags & WPA_STA_SHORT_PREAMBLE) + NLA_PUT_FLAG(flags, NL80211_STA_FLAG_SHORT_PREAMBLE); + + if (total_flags & WPA_STA_MFP) + NLA_PUT_FLAG(flags, NL80211_STA_FLAG_MFP); + + if (nla_put_nested(msg, NL80211_ATTR_STA_FLAGS, flags)) + goto nla_put_failure; + + os_memset(&upd, 0, sizeof(upd)); + upd.mask = sta_flags_nl80211(flags_or | ~flags_and); + upd.set = sta_flags_nl80211(flags_or); + NLA_PUT(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd); + + nlmsg_free(flags); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + nla_put_failure: + nlmsg_free(flags); + return -ENOBUFS; +} + + +static int wpa_driver_nl80211_ap(struct wpa_driver_nl80211_data *drv, + struct wpa_driver_associate_params *params) +{ + if (wpa_driver_nl80211_set_mode(&drv->first_bss, params->mode) || + wpa_driver_nl80211_set_freq(drv, params->freq, 0, 0)) { + nl80211_remove_monitor_interface(drv); + return -1; + } + + /* TODO: setup monitor interface (and add code somewhere to remove this + * when AP mode is stopped; associate with mode != 2 or drv_deinit) */ + + return 0; +} + + +static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv) +{ + struct nl_msg *msg; + int ret = -1; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_LEAVE_IBSS, 0); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); ret = send_and_recv_msgs(drv, msg, NULL, NULL); - /* ignore EEXIST, this happens if a STA associates while associated */ - if (ret == -EEXIST || ret >= 0) - ret = 0; + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Leave IBSS failed: ret=%d " + "(%s)", ret, strerror(-ret)); + goto nla_put_failure; + } + + ret = 0; + wpa_printf(MSG_DEBUG, "nl80211: Leave IBSS request sent successfully"); nla_put_failure: + nlmsg_free(msg); return ret; } -static int wpa_driver_nl80211_mlme_remove_sta(void *priv, const u8 *addr) +static int wpa_driver_nl80211_ibss(struct wpa_driver_nl80211_data *drv, + struct wpa_driver_associate_params *params) { - struct wpa_driver_nl80211_data *drv = priv; struct nl_msg *msg; int ret = -1; + int count = 0; + + wpa_printf(MSG_DEBUG, "nl80211: Join IBSS (ifindex=%d)", drv->ifindex); + + if (wpa_driver_nl80211_set_mode(&drv->first_bss, params->mode)) { + wpa_printf(MSG_INFO, "nl80211: Failed to set interface into " + "IBSS mode"); + return -1; + } + +retry: + msg = nlmsg_alloc(); + if (!msg) + return -1; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_JOIN_IBSS, 0); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + if (params->ssid == NULL || params->ssid_len > sizeof(drv->ssid)) + goto nla_put_failure; + + wpa_hexdump_ascii(MSG_DEBUG, " * SSID", + params->ssid, params->ssid_len); + NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len, + params->ssid); + os_memcpy(drv->ssid, params->ssid, params->ssid_len); + drv->ssid_len = params->ssid_len; + + wpa_printf(MSG_DEBUG, " * freq=%d", params->freq); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq); + + ret = nl80211_set_conn_keys(params, msg); + if (ret) + goto nla_put_failure; + + if (params->wpa_ie) { + wpa_hexdump(MSG_DEBUG, + " * Extra IEs for Beacon/Probe Response frames", + params->wpa_ie, params->wpa_ie_len); + NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len, + params->wpa_ie); + } + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Join IBSS failed: ret=%d (%s)", + ret, strerror(-ret)); + count++; + if (ret == -EALREADY && count == 1) { + wpa_printf(MSG_DEBUG, "nl80211: Retry IBSS join after " + "forced leave"); + nl80211_leave_ibss(drv); + nlmsg_free(msg); + goto retry; + } + + goto nla_put_failure; + } + ret = 0; + wpa_printf(MSG_DEBUG, "nl80211: Join IBSS request sent successfully"); + +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +static int wpa_driver_nl80211_connect( + struct wpa_driver_nl80211_data *drv, + struct wpa_driver_associate_params *params) +{ + struct nl_msg *msg; + enum nl80211_auth_type type; + int ret = 0; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + wpa_printf(MSG_DEBUG, "nl80211: Connect (ifindex=%d)", drv->ifindex); + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_CONNECT, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + if (params->bssid) { + wpa_printf(MSG_DEBUG, " * bssid=" MACSTR, + MAC2STR(params->bssid)); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid); + } + if (params->freq) { + wpa_printf(MSG_DEBUG, " * freq=%d", params->freq); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq); + } + if (params->ssid) { + wpa_hexdump_ascii(MSG_DEBUG, " * SSID", + params->ssid, params->ssid_len); + NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len, + params->ssid); + if (params->ssid_len > sizeof(drv->ssid)) + goto nla_put_failure; + os_memcpy(drv->ssid, params->ssid, params->ssid_len); + drv->ssid_len = params->ssid_len; + } + wpa_hexdump(MSG_DEBUG, " * IEs", params->wpa_ie, params->wpa_ie_len); + if (params->wpa_ie) + NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len, + params->wpa_ie); + + if (params->auth_alg & WPA_AUTH_ALG_OPEN) + type = NL80211_AUTHTYPE_OPEN_SYSTEM; + else if (params->auth_alg & WPA_AUTH_ALG_SHARED) + type = NL80211_AUTHTYPE_SHARED_KEY; + else if (params->auth_alg & WPA_AUTH_ALG_LEAP) + type = NL80211_AUTHTYPE_NETWORK_EAP; + else if (params->auth_alg & WPA_AUTH_ALG_FT) + type = NL80211_AUTHTYPE_FT; + else + goto nla_put_failure; + + wpa_printf(MSG_DEBUG, " * Auth Type %d", type); + NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, type); + + if (params->wpa_ie && params->wpa_ie_len) { + enum nl80211_wpa_versions ver; + + if (params->wpa_ie[0] == WLAN_EID_RSN) + ver = NL80211_WPA_VERSION_2; + else + ver = NL80211_WPA_VERSION_1; + + wpa_printf(MSG_DEBUG, " * WPA Version %d", ver); + NLA_PUT_U32(msg, NL80211_ATTR_WPA_VERSIONS, ver); + } + + if (params->pairwise_suite != CIPHER_NONE) { + int cipher; + + switch (params->pairwise_suite) { + case CIPHER_WEP40: + cipher = WLAN_CIPHER_SUITE_WEP40; + break; + case CIPHER_WEP104: + cipher = WLAN_CIPHER_SUITE_WEP104; + break; + case CIPHER_CCMP: + cipher = WLAN_CIPHER_SUITE_CCMP; + break; + case CIPHER_TKIP: + default: + cipher = WLAN_CIPHER_SUITE_TKIP; + break; + } + NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, cipher); + } + + if (params->group_suite != CIPHER_NONE) { + int cipher; + + switch (params->group_suite) { + case CIPHER_WEP40: + cipher = WLAN_CIPHER_SUITE_WEP40; + break; + case CIPHER_WEP104: + cipher = WLAN_CIPHER_SUITE_WEP104; + break; + case CIPHER_CCMP: + cipher = WLAN_CIPHER_SUITE_CCMP; + break; + case CIPHER_TKIP: + default: + cipher = WLAN_CIPHER_SUITE_TKIP; + break; + } + NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, cipher); + } + + if (params->key_mgmt_suite == KEY_MGMT_802_1X || + params->key_mgmt_suite == KEY_MGMT_PSK) { + int mgmt = WLAN_AKM_SUITE_PSK; + + switch (params->key_mgmt_suite) { + case KEY_MGMT_802_1X: + mgmt = WLAN_AKM_SUITE_8021X; + break; + case KEY_MGMT_PSK: + default: + mgmt = WLAN_AKM_SUITE_PSK; + break; + } + NLA_PUT_U32(msg, NL80211_ATTR_AKM_SUITES, mgmt); + } + + ret = nl80211_set_conn_keys(params, msg); + if (ret) + goto nla_put_failure; + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: MLME connect failed: ret=%d " + "(%s)", ret, strerror(-ret)); + goto nla_put_failure; + } + ret = 0; + wpa_printf(MSG_DEBUG, "nl80211: Connect request send successfully"); + +nla_put_failure: + nlmsg_free(msg); + return ret; + +} + + +static int wpa_driver_nl80211_associate( + void *priv, struct wpa_driver_associate_params *params) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + int ret = -1; + struct nl_msg *msg; + + if (params->mode == IEEE80211_MODE_AP) + return wpa_driver_nl80211_ap(drv, params); + + if (params->mode == IEEE80211_MODE_IBSS) + return wpa_driver_nl80211_ibss(drv, params); + + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) { + if (wpa_driver_nl80211_set_mode(priv, params->mode) < 0) + return -1; + return wpa_driver_nl80211_connect(drv, params); + } + + drv->associated = 0; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + wpa_printf(MSG_DEBUG, "nl80211: Associate (ifindex=%d)", + drv->ifindex); + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_ASSOCIATE, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + if (params->bssid) { + wpa_printf(MSG_DEBUG, " * bssid=" MACSTR, + MAC2STR(params->bssid)); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid); + } + if (params->freq) { + wpa_printf(MSG_DEBUG, " * freq=%d", params->freq); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq); + drv->assoc_freq = params->freq; + } else + drv->assoc_freq = 0; + if (params->ssid) { + wpa_hexdump_ascii(MSG_DEBUG, " * SSID", + params->ssid, params->ssid_len); + NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len, + params->ssid); + if (params->ssid_len > sizeof(drv->ssid)) + goto nla_put_failure; + os_memcpy(drv->ssid, params->ssid, params->ssid_len); + drv->ssid_len = params->ssid_len; + } + wpa_hexdump(MSG_DEBUG, " * IEs", params->wpa_ie, params->wpa_ie_len); + if (params->wpa_ie) + NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len, + params->wpa_ie); + +#ifdef CONFIG_IEEE80211W + if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED) + NLA_PUT_U32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED); +#endif /* CONFIG_IEEE80211W */ + + NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT); + + if (params->prev_bssid) { + wpa_printf(MSG_DEBUG, " * prev_bssid=" MACSTR, + MAC2STR(params->prev_bssid)); + NLA_PUT(msg, NL80211_ATTR_PREV_BSSID, ETH_ALEN, + params->prev_bssid); + } + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: MLME command failed: ret=%d " + "(%s)", ret, strerror(-ret)); + nl80211_dump_scan(drv); + goto nla_put_failure; + } + ret = 0; + wpa_printf(MSG_DEBUG, "nl80211: Association request send " + "successfully"); + +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +static int nl80211_set_mode(struct wpa_driver_nl80211_data *drv, + int ifindex, int mode) +{ + struct nl_msg *msg; + int ret = -ENOBUFS; msg = nlmsg_alloc(); if (!msg) return -ENOMEM; genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_DEL_STATION, 0); + 0, NL80211_CMD_SET_INTERFACE, 0); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); + NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, mode); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (!ret) + return 0; +nla_put_failure: + wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface %d to mode %d:" + " %d (%s)", ifindex, mode, ret, strerror(-ret)); + return ret; +} + + +static int wpa_driver_nl80211_set_mode(void *priv, int mode) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + int ret = -1; + int nlmode; + + switch (mode) { + case 0: + nlmode = NL80211_IFTYPE_STATION; + break; + case 1: + nlmode = NL80211_IFTYPE_ADHOC; + break; + case 2: + nlmode = NL80211_IFTYPE_AP; + break; + default: + return -1; + } + + if (nl80211_set_mode(drv, drv->ifindex, nlmode) == 0) { + drv->nlmode = nlmode; + ret = 0; + goto done; + } + + if (nlmode == drv->nlmode) { + wpa_printf(MSG_DEBUG, "nl80211: Interface already in " + "requested mode - ignore error"); + ret = 0; + goto done; /* Already in the requested mode */ + } + + /* mac80211 doesn't allow mode changes while the device is up, so + * take the device down, try to set the mode again, and bring the + * device back up. + */ + if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0) == 0) { + /* Try to set the mode again while the interface is down */ + ret = nl80211_set_mode(drv, drv->ifindex, nlmode); + if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1)) + ret = -1; + } + + if (!ret) { + wpa_printf(MSG_DEBUG, "nl80211: Mode change succeeded while " + "interface is down"); + drv->nlmode = nlmode; + } + +done: + if (!ret && nlmode == NL80211_IFTYPE_AP) { + /* Setup additional AP mode functionality if needed */ + if (drv->monitor_ifidx < 0 && + nl80211_create_monitor_interface(drv)) + return -1; + } else if (!ret && nlmode != NL80211_IFTYPE_AP) { + /* Remove additional AP mode functionality */ + nl80211_remove_monitor_interface(drv); + bss->beacon_set = 0; + } + + if (ret) + wpa_printf(MSG_DEBUG, "nl80211: Interface mode change to %d " + "from %d failed", nlmode, drv->nlmode); + + return ret; +} + + +static int wpa_driver_nl80211_get_capa(void *priv, + struct wpa_driver_capa *capa) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + if (!drv->has_capability) + return -1; + os_memcpy(capa, &drv->capa, sizeof(*capa)); + return 0; +} + + +static int wpa_driver_nl80211_set_operstate(void *priv, int state) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + + wpa_printf(MSG_DEBUG, "%s: operstate %d->%d (%s)", + __func__, drv->operstate, state, state ? "UP" : "DORMANT"); + drv->operstate = state; + return netlink_send_oper_ifla(drv->netlink, drv->ifindex, -1, + state ? IF_OPER_UP : IF_OPER_DORMANT); +} + + +static int wpa_driver_nl80211_set_supp_port(void *priv, int authorized) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + struct nl80211_sta_flag_update upd; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_SET_STATION, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, + if_nametoindex(bss->ifname)); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid); + + os_memset(&upd, 0, sizeof(upd)); + upd.mask = BIT(NL80211_STA_FLAG_AUTHORIZED); + if (authorized) + upd.set = BIT(NL80211_STA_FLAG_AUTHORIZED); + NLA_PUT(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + nla_put_failure: + return -ENOBUFS; +} + + +#ifdef HOSTAPD + +static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) +{ + int i; + int *old; + + wpa_printf(MSG_DEBUG, "nl80211: Add own interface ifindex %d", + ifidx); + for (i = 0; i < drv->num_if_indices; i++) { + if (drv->if_indices[i] == 0) { + drv->if_indices[i] = ifidx; + return; + } + } + + if (drv->if_indices != drv->default_if_indices) + old = drv->if_indices; + else + old = NULL; + + drv->if_indices = os_realloc(old, + sizeof(int) * (drv->num_if_indices + 1)); + if (!drv->if_indices) { + if (!old) + drv->if_indices = drv->default_if_indices; + else + drv->if_indices = old; + wpa_printf(MSG_ERROR, "Failed to reallocate memory for " + "interfaces"); + wpa_printf(MSG_ERROR, "Ignoring EAPOL on interface %d", ifidx); + return; + } else if (!old) + os_memcpy(drv->if_indices, drv->default_if_indices, + sizeof(drv->default_if_indices)); + drv->if_indices[drv->num_if_indices] = ifidx; + drv->num_if_indices++; +} + + +static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) +{ + int i; + + for (i = 0; i < drv->num_if_indices; i++) { + if (drv->if_indices[i] == ifidx) { + drv->if_indices[i] = 0; + break; + } + } +} + + +static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) +{ + int i; + + for (i = 0; i < drv->num_if_indices; i++) + if (drv->if_indices[i] == ifidx) + return 1; + + return 0; +} + + +static inline int min_int(int a, int b) +{ + if (a < b) + return a; + return b; +} + + +static int get_key_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + /* + * TODO: validate the key index and mac address! + * Otherwise, there's a race condition as soon as + * the kernel starts sending key notifications. + */ + + if (tb[NL80211_ATTR_KEY_SEQ]) + memcpy(arg, nla_data(tb[NL80211_ATTR_KEY_SEQ]), + min_int(nla_len(tb[NL80211_ATTR_KEY_SEQ]), 6)); + return NL_SKIP; +} + + +static int i802_get_seqnum(const char *iface, void *priv, const u8 *addr, + int idx, u8 *seq) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_GET_KEY, 0); + if (addr) + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(iface)); + + memset(seq, 0, 6); + + return send_and_recv_msgs(drv, msg, get_key_handler, seq); + nla_put_failure: + return -ENOBUFS; +} + + +static int i802_set_rate_sets(void *priv, int *supp_rates, int *basic_rates, + int mode) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + u8 rates[NL80211_MAX_SUPP_RATES]; + u8 rates_len = 0; + int i; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_SET_BSS, 0); + + for (i = 0; i < NL80211_MAX_SUPP_RATES && basic_rates[i] >= 0; i++) + rates[rates_len++] = basic_rates[i] / 5; + + NLA_PUT(msg, NL80211_ATTR_BSS_BASIC_RATES, rates_len, rates); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + nla_put_failure: + return -ENOBUFS; +} + +#endif /* HOSTAPD */ + + +/* Set kernel driver on given frequency (MHz) */ +static int i802_set_freq(void *priv, struct hostapd_freq_params *freq) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + return wpa_driver_nl80211_set_freq(drv, freq->freq, freq->ht_enabled, + freq->sec_channel_offset); +} + + +#ifdef HOSTAPD + +static int i802_set_rts(void *priv, int rts) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret = -ENOBUFS; + u32 val; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + if (rts >= 2347) + val = (u32) -1; + else + val = rts; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_SET_WIPHY, 0); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, val); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (!ret) + return 0; +nla_put_failure: + wpa_printf(MSG_DEBUG, "nl80211: Failed to set RTS threshold %d: " + "%d (%s)", rts, ret, strerror(-ret)); + return ret; +} + + +static int i802_set_frag(void *priv, int frag) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret = -ENOBUFS; + u32 val; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + if (frag >= 2346) + val = (u32) -1; + else + val = frag; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_SET_WIPHY, 0); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, val); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (!ret) + return 0; +nla_put_failure: + wpa_printf(MSG_DEBUG, "nl80211: Failed to set fragmentation threshold " + "%d: %d (%s)", frag, ret, strerror(-ret)); + return ret; +} + + +static int i802_flush(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_DEL_STATION, 0); + + /* + * XXX: FIX! this needs to flush all VLANs too + */ + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, + if_nametoindex(bss->ifname)); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + nla_put_failure: + return -ENOBUFS; +} + + +static int get_sta_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct hostap_sta_driver_data *data = arg; + struct nlattr *stats[NL80211_STA_INFO_MAX + 1]; + static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = { + [NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 }, + [NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 }, + [NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 }, + [NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 }, + [NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 }, + }; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + /* + * TODO: validate the interface and mac address! + * Otherwise, there's a race condition as soon as + * the kernel starts sending station notifications. + */ + + if (!tb[NL80211_ATTR_STA_INFO]) { + wpa_printf(MSG_DEBUG, "sta stats missing!"); + return NL_SKIP; + } + if (nla_parse_nested(stats, NL80211_STA_INFO_MAX, + tb[NL80211_ATTR_STA_INFO], + stats_policy)) { + wpa_printf(MSG_DEBUG, "failed to parse nested attributes!"); + return NL_SKIP; + } + + if (stats[NL80211_STA_INFO_INACTIVE_TIME]) + data->inactive_msec = + nla_get_u32(stats[NL80211_STA_INFO_INACTIVE_TIME]); + if (stats[NL80211_STA_INFO_RX_BYTES]) + data->rx_bytes = nla_get_u32(stats[NL80211_STA_INFO_RX_BYTES]); + if (stats[NL80211_STA_INFO_TX_BYTES]) + data->tx_bytes = nla_get_u32(stats[NL80211_STA_INFO_TX_BYTES]); + if (stats[NL80211_STA_INFO_RX_PACKETS]) + data->rx_packets = + nla_get_u32(stats[NL80211_STA_INFO_RX_PACKETS]); + if (stats[NL80211_STA_INFO_TX_PACKETS]) + data->tx_packets = + nla_get_u32(stats[NL80211_STA_INFO_TX_PACKETS]); + + return NL_SKIP; +} + +static int i802_read_sta_data(void *priv, struct hostap_sta_driver_data *data, + const u8 *addr) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + + os_memset(data, 0, sizeof(*data)); + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_GET_STATION, 0); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); + + return send_and_recv_msgs(drv, msg, get_sta_handler, data); + nla_put_failure: + return -ENOBUFS; +} + + +static int i802_set_tx_queue_params(void *priv, int queue, int aifs, + int cw_min, int cw_max, int burst_time) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + struct nlattr *txq, *params; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_SET_WIPHY, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); + + txq = nla_nest_start(msg, NL80211_ATTR_WIPHY_TXQ_PARAMS); + if (!txq) + goto nla_put_failure; + + /* We are only sending parameters for a single TXQ at a time */ + params = nla_nest_start(msg, 1); + if (!params) + goto nla_put_failure; + + NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, queue); + /* Burst time is configured in units of 0.1 msec and TXOP parameter in + * 32 usec, so need to convert the value here. */ + NLA_PUT_U16(msg, NL80211_TXQ_ATTR_TXOP, (burst_time * 100 + 16) / 32); + NLA_PUT_U16(msg, NL80211_TXQ_ATTR_CWMIN, cw_min); + NLA_PUT_U16(msg, NL80211_TXQ_ATTR_CWMAX, cw_max); + NLA_PUT_U8(msg, NL80211_TXQ_ATTR_AIFS, aifs); + + nla_nest_end(msg, params); + + nla_nest_end(msg, txq); + + if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0) + return 0; + nla_put_failure: + return -1; +} + + +static int i802_set_bss(void *priv, int cts, int preamble, int slot) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_SET_BSS, 0); + + if (cts >= 0) + NLA_PUT_U8(msg, NL80211_ATTR_BSS_CTS_PROT, cts); + if (preamble >= 0) + NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_PREAMBLE, preamble); + if (slot >= 0) + NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_SLOT_TIME, slot); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + nla_put_failure: + return -ENOBUFS; +} + + +static int i802_set_cts_protect(void *priv, int value) +{ + return i802_set_bss(priv, value, -1, -1); +} + + +static int i802_set_preamble(void *priv, int value) +{ + return i802_set_bss(priv, -1, value, -1); +} + + +static int i802_set_short_slot_time(void *priv, int value) +{ + return i802_set_bss(priv, -1, -1, value); +} + + +static int i802_set_sta_vlan(void *priv, const u8 *addr, + const char *ifname, int vlan_id) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret = -ENOBUFS; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_SET_STATION, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, + if_nametoindex(bss->ifname)); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + NLA_PUT_U32(msg, NL80211_ATTR_STA_VLAN, + if_nametoindex(ifname)); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret < 0) { + wpa_printf(MSG_ERROR, "nl80211: NL80211_ATTR_STA_VLAN (addr=" + MACSTR " ifname=%s vlan_id=%d) failed: %d (%s)", + MAC2STR(addr), ifname, vlan_id, ret, + strerror(-ret)); + } + nla_put_failure: + return ret; +} + + +static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + char name[IFNAMSIZ + 1]; + + os_snprintf(name, sizeof(name), "%s.sta%d", bss->ifname, aid); + wpa_printf(MSG_DEBUG, "nl80211: Set WDS STA addr=" MACSTR + " aid=%d val=%d name=%s", MAC2STR(addr), aid, val, name); + if (val) { + if (nl80211_create_iface(drv, name, NL80211_IFTYPE_AP_VLAN, + NULL, 1) < 0) + return -1; + linux_set_iface_flags(drv->ioctl_sock, name, 1); + return i802_set_sta_vlan(priv, addr, name, 0); + } else { + i802_set_sta_vlan(priv, addr, bss->ifname, 0); + return wpa_driver_nl80211_if_remove(priv, WPA_IF_AP_VLAN, + name); + } +} + + +static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct wpa_driver_nl80211_data *drv = eloop_ctx; + struct sockaddr_ll lladdr; + unsigned char buf[3000]; + int len; + socklen_t fromlen = sizeof(lladdr); + + len = recvfrom(sock, buf, sizeof(buf), 0, + (struct sockaddr *)&lladdr, &fromlen); + if (len < 0) { + perror("recv"); + return; + } + + if (have_ifidx(drv, lladdr.sll_ifindex)) + drv_event_eapol_rx(drv->ctx, lladdr.sll_addr, buf, len); +} + + +static int i802_get_inact_sec(void *priv, const u8 *addr) +{ + struct hostap_sta_driver_data data; + int ret; + + data.inactive_msec = (unsigned long) -1; + ret = i802_read_sta_data(priv, &data, addr); + if (ret || data.inactive_msec == (unsigned long) -1) + return -1; + return data.inactive_msec / 1000; +} + + +static int i802_sta_clear_stats(void *priv, const u8 *addr) +{ +#if 0 + /* TODO */ +#endif + return 0; +} + + +static int i802_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, + int reason) +{ + struct i802_bss *bss = priv; + struct ieee80211_mgmt mgmt; + + memset(&mgmt, 0, sizeof(mgmt)); + mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_DEAUTH); + memcpy(mgmt.da, addr, ETH_ALEN); + memcpy(mgmt.sa, own_addr, ETH_ALEN); + memcpy(mgmt.bssid, own_addr, ETH_ALEN); + mgmt.u.deauth.reason_code = host_to_le16(reason); + return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt, + IEEE80211_HDRLEN + + sizeof(mgmt.u.deauth)); +} + + +static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, + int reason) +{ + struct i802_bss *bss = priv; + struct ieee80211_mgmt mgmt; + + memset(&mgmt, 0, sizeof(mgmt)); + mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_DISASSOC); + memcpy(mgmt.da, addr, ETH_ALEN); + memcpy(mgmt.sa, own_addr, ETH_ALEN); + memcpy(mgmt.bssid, own_addr, ETH_ALEN); + mgmt.u.disassoc.reason_code = host_to_le16(reason); + return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt, + IEEE80211_HDRLEN + + sizeof(mgmt.u.disassoc)); +} + + +static int i802_check_bridge(struct wpa_driver_nl80211_data *drv, + const char *brname, const char *ifname) +{ + int ifindex; + char in_br[IFNAMSIZ]; + + os_strlcpy(drv->brname, brname, IFNAMSIZ); + ifindex = if_nametoindex(brname); + if (ifindex == 0) { + /* + * Bridge was configured, but the bridge device does + * not exist. Try to add it now. + */ + if (linux_br_add(drv->ioctl_sock, brname) < 0) { + wpa_printf(MSG_ERROR, "nl80211: Failed to add the " + "bridge interface %s: %s", + brname, strerror(errno)); + return -1; + } + drv->added_bridge = 1; + add_ifidx(drv, if_nametoindex(brname)); + } + + if (linux_br_get(in_br, ifname) == 0) { + if (os_strcmp(in_br, brname) == 0) + return 0; /* already in the bridge */ + + wpa_printf(MSG_DEBUG, "nl80211: Removing interface %s from " + "bridge %s", ifname, in_br); + if (linux_br_del_if(drv->ioctl_sock, in_br, ifname) < 0) { + wpa_printf(MSG_ERROR, "nl80211: Failed to " + "remove interface %s from bridge " + "%s: %s", + ifname, brname, strerror(errno)); + return -1; + } + } + + wpa_printf(MSG_DEBUG, "nl80211: Adding interface %s into bridge %s", + ifname, brname); + if (linux_br_add_if(drv->ioctl_sock, brname, ifname) < 0) { + wpa_printf(MSG_ERROR, "nl80211: Failed to add interface %s " + "into bridge %s: %s", + ifname, brname, strerror(errno)); + return -1; + } + drv->added_if_into_bridge = 1; + + return 0; +} + + +static void *i802_init(struct hostapd_data *hapd, + struct wpa_init_params *params) +{ + struct wpa_driver_nl80211_data *drv; + struct i802_bss *bss; + size_t i; + char brname[IFNAMSIZ]; + int ifindex, br_ifindex; + int br_added = 0; + + bss = wpa_driver_nl80211_init(hapd, params->ifname); + if (bss == NULL) + return NULL; + + drv = bss->drv; + if (linux_br_get(brname, params->ifname) == 0) { + wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in bridge %s", + params->ifname, brname); + br_ifindex = if_nametoindex(brname); + } else { + brname[0] = '\0'; + br_ifindex = 0; + } + + drv->num_if_indices = sizeof(drv->default_if_indices) / sizeof(int); + drv->if_indices = drv->default_if_indices; + for (i = 0; i < params->num_bridge; i++) { + if (params->bridge[i]) { + ifindex = if_nametoindex(params->bridge[i]); + if (ifindex) + add_ifidx(drv, ifindex); + if (ifindex == br_ifindex) + br_added = 1; + } + } + if (!br_added && br_ifindex && + (params->num_bridge == 0 || !params->bridge[0])) + add_ifidx(drv, br_ifindex); + + /* start listening for EAPOL on the default AP interface */ + add_ifidx(drv, drv->ifindex); + + if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0)) + goto failed; + + if (params->bssid) { + if (linux_set_ifhwaddr(drv->ioctl_sock, bss->ifname, + params->bssid)) + goto failed; + } + + if (wpa_driver_nl80211_set_mode(bss, IEEE80211_MODE_AP)) { + wpa_printf(MSG_ERROR, "nl80211: Failed to set interface %s " + "into AP mode", bss->ifname); + goto failed; + } + + if (params->num_bridge && params->bridge[0] && + i802_check_bridge(drv, params->bridge[0], params->ifname) < 0) + goto failed; + + if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1)) + goto failed; + + drv->eapol_sock = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE)); + if (drv->eapol_sock < 0) { + perror("socket(PF_PACKET, SOCK_DGRAM, ETH_P_PAE)"); + goto failed; + } + + if (eloop_register_read_sock(drv->eapol_sock, handle_eapol, drv, NULL)) + { + printf("Could not register read socket for eapol\n"); + goto failed; + } + + if (linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, params->own_addr)) + goto failed; + + return bss; + +failed: + nl80211_remove_monitor_interface(drv); + if (drv->ioctl_sock >= 0) + close(drv->ioctl_sock); + + genl_family_put(drv->nl80211); + nl_cache_free(drv->nl_cache); + nl_handle_destroy(drv->nl_handle); + nl_cb_put(drv->nl_cb); + + os_free(drv); + return NULL; +} + + +static void i802_deinit(void *priv) +{ + wpa_driver_nl80211_deinit(priv); +} + +#endif /* HOSTAPD */ + + +static enum nl80211_iftype wpa_driver_nl80211_if_type( + enum wpa_driver_if_type type) +{ + switch (type) { + case WPA_IF_STATION: + return NL80211_IFTYPE_STATION; + case WPA_IF_AP_VLAN: + return NL80211_IFTYPE_AP_VLAN; + case WPA_IF_AP_BSS: + return NL80211_IFTYPE_AP; + } + return -1; +} + +static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type, + const char *ifname, const u8 *addr, + void *bss_ctx, void **drv_priv, + char *force_ifname, u8 *if_addr) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + int ifidx; +#ifdef HOSTAPD + struct i802_bss *new_bss = NULL; + + if (type == WPA_IF_AP_BSS) { + new_bss = os_zalloc(sizeof(*new_bss)); + if (new_bss == NULL) + return -1; + } +#endif /* HOSTAPD */ + + if (addr) + os_memcpy(if_addr, addr, ETH_ALEN); + ifidx = nl80211_create_iface(drv, ifname, + wpa_driver_nl80211_if_type(type), addr, + 0); + if (ifidx < 0) { +#ifdef HOSTAPD + os_free(new_bss); +#endif /* HOSTAPD */ + return -1; + } + + if (!addr && + linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, if_addr) < 0) + return -1; + +#ifdef HOSTAPD + if (type == WPA_IF_AP_BSS) { + if (linux_set_iface_flags(drv->ioctl_sock, ifname, 1)) { + nl80211_remove_iface(drv, ifidx); + os_free(new_bss); + return -1; + } + os_strlcpy(new_bss->ifname, ifname, IFNAMSIZ); + new_bss->ifindex = ifidx; + new_bss->drv = drv; + new_bss->next = drv->first_bss.next; + drv->first_bss.next = new_bss; + if (drv_priv) + *drv_priv = new_bss; + } +#endif /* HOSTAPD */ + + return 0; +} + + +static int wpa_driver_nl80211_if_remove(void *priv, + enum wpa_driver_if_type type, + const char *ifname) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + int ifindex = if_nametoindex(ifname); + + wpa_printf(MSG_DEBUG, "nl80211: %s(type=%d ifname=%s) ifindex=%d", + __func__, type, ifname, ifindex); + if (ifindex <= 0) + return -1; + nl80211_remove_iface(drv, ifindex); + +#ifdef HOSTAPD + if (type != WPA_IF_AP_BSS) + return 0; + + if (bss != &drv->first_bss) { + struct i802_bss *tbss = &drv->first_bss; + + while (tbss) { + if (tbss->next != bss) + continue; + + tbss->next = bss->next; + os_free(bss); + break; + } + } +#endif /* HOSTAPD */ + + return 0; +} + + +static int cookie_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + u64 *cookie = arg; + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (tb[NL80211_ATTR_COOKIE]) + *cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]); + return NL_SKIP; +} + + +static int wpa_driver_nl80211_send_action(void *priv, unsigned int freq, + const u8 *dst, const u8 *src, + const u8 *bssid, + const u8 *data, size_t data_len) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + int ret = -1; + struct nl_msg *msg; + u8 *buf; + struct ieee80211_hdr *hdr; + u64 cookie; + + wpa_printf(MSG_DEBUG, "nl80211: Send Action frame (ifindex=%d)", + drv->ifindex); + + buf = os_zalloc(24 + data_len); + if (buf == NULL) + return ret; + os_memcpy(buf + 24, data, data_len); + hdr = (struct ieee80211_hdr *) buf; + hdr->frame_control = + IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION); + os_memcpy(hdr->addr1, dst, ETH_ALEN); + os_memcpy(hdr->addr2, src, ETH_ALEN); + os_memcpy(hdr->addr3, bssid, ETH_ALEN); + + if (drv->nlmode == NL80211_IFTYPE_AP) { + ret = wpa_driver_nl80211_send_mlme(priv, buf, 24 + data_len); + os_free(buf); + return ret; + } + + msg = nlmsg_alloc(); + if (!msg) { + os_free(buf); + return -1; + } + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_ACTION, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); + NLA_PUT(msg, NL80211_ATTR_FRAME, 24 + data_len, buf); + os_free(buf); + buf = NULL; + + cookie = 0; + ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Action command failed: ret=%d " + "(%s)", ret, strerror(-ret)); + goto nla_put_failure; + } + wpa_printf(MSG_DEBUG, "nl80211: Action TX command accepted; " + "cookie 0x%llx", (long long unsigned int) cookie); + drv->send_action_cookie = cookie; ret = 0; +nla_put_failure: + os_free(buf); + nlmsg_free(msg); + return ret; +} + + +static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq, + unsigned int duration) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; + u64 cookie; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_REMAIN_ON_CHANNEL, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); + NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration); + + cookie = 0; + ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie); + if (ret == 0) { + wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel cookie " + "0x%llx for freq=%u MHz duration=%u", + (long long unsigned int) cookie, freq, duration); + drv->remain_on_chan_cookie = cookie; + return 0; + } + wpa_printf(MSG_DEBUG, "nl80211: Failed to request remain-on-channel " + "(freq=%d): %d (%s)", freq, ret, strerror(-ret)); +nla_put_failure: + return -1; +} + + +static int wpa_driver_nl80211_cancel_remain_on_channel(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; + + if (!drv->pending_remain_on_chan) { + wpa_printf(MSG_DEBUG, "nl80211: No pending remain-on-channel " + "to cancel"); + return -1; + } + + wpa_printf(MSG_DEBUG, "nl80211: Cancel remain-on-channel with cookie " + "0x%llx", + (long long unsigned int) drv->remain_on_chan_cookie); + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, drv->remain_on_chan_cookie); + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret == 0) + return 0; + wpa_printf(MSG_DEBUG, "nl80211: Failed to cancel remain-on-channel: " + "%d (%s)", ret, strerror(-ret)); +nla_put_failure: + return -1; +} + + +static void wpa_driver_nl80211_probe_req_report_timeout(void *eloop_ctx, + void *timeout_ctx) +{ + struct wpa_driver_nl80211_data *drv = eloop_ctx; + if (drv->monitor_ifidx < 0) + return; /* monitor interface already removed */ + + if (drv->nlmode != NL80211_IFTYPE_STATION) + return; /* not in station mode anymore */ + + if (drv->probe_req_report) + return; /* reporting enabled */ + + wpa_printf(MSG_DEBUG, "nl80211: Remove monitor interface due to no " + "Probe Request reporting needed anymore"); + nl80211_remove_monitor_interface(drv); +} + + +static int wpa_driver_nl80211_probe_req_report(void *priv, int report) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + + if (drv->nlmode != NL80211_IFTYPE_STATION) { + wpa_printf(MSG_DEBUG, "nl80211: probe_req_report control only " + "allowed in station mode (iftype=%d)", + drv->nlmode); + return -1; + } + drv->probe_req_report = report; + + if (report) { + eloop_cancel_timeout( + wpa_driver_nl80211_probe_req_report_timeout, + drv, NULL); + if (drv->monitor_ifidx < 0 && + nl80211_create_monitor_interface(drv)) + return -1; + } else { + /* + * It takes a while to remove the monitor interface, so try to + * avoid doing this if it is needed again shortly. Instead, + * schedule the interface to be removed later if no need for it + * is seen. + */ + wpa_printf(MSG_DEBUG, "nl80211: Scheduling monitor interface " + "to be removed after 10 seconds of no use"); + eloop_register_timeout( + 10, 0, wpa_driver_nl80211_probe_req_report_timeout, + drv, NULL); + } + + return 0; +} + + +static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv, + int ifindex, int disabled) +{ + struct nl_msg *msg; + struct nlattr *bands, *band; + int ret; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_SET_TX_BITRATE_MASK, 0); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); + + bands = nla_nest_start(msg, NL80211_ATTR_TX_RATES); + if (!bands) + goto nla_put_failure; + + /* + * Disable 2 GHz rates 1, 2, 5.5, 11 Mbps by masking out everything + * else apart from 6, 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS + * rates. All 5 GHz rates are left enabled. + */ + band = nla_nest_start(msg, NL80211_BAND_2GHZ); + if (!band) + goto nla_put_failure; + NLA_PUT(msg, NL80211_TXRATE_LEGACY, 8, + "\x0c\x12\x18\x24\x30\x48\x60\x6c"); + nla_nest_end(msg, band); + + nla_nest_end(msg, bands); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Set TX rates failed: ret=%d " + "(%s)", ret, strerror(-ret)); + } + return ret; nla_put_failure: - return -ENOBUFS; + nlmsg_free(msg); + return -1; +} + + +static int wpa_driver_nl80211_disable_11b_rates(void *priv, int disabled) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + drv->disable_11b_rates = disabled; + return nl80211_disable_11b_rates(drv, drv->ifindex, disabled); +} + + +static int wpa_driver_nl80211_deinit_ap(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + if (drv->nlmode != NL80211_IFTYPE_AP) + return -1; + wpa_driver_nl80211_del_beacon(drv); + return wpa_driver_nl80211_set_mode(priv, IEEE80211_MODE_INFRA); +} + + +static void wpa_driver_nl80211_resume(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1)) { + wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface up on " + "resume event"); + } } -#endif /* CONFIG_CLIENT_MLME */ + +static int nl80211_send_ft_action(void *priv, u8 action, const u8 *target_ap, + const u8 *ies, size_t ies_len) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + int ret; + u8 *data, *pos; + size_t data_len; + u8 own_addr[ETH_ALEN]; + + if (linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, own_addr) < 0) + return -1; + + if (action != 1) { + wpa_printf(MSG_ERROR, "nl80211: Unsupported send_ft_action " + "action %d", action); + return -1; + } + + /* + * Action frame payload: + * Category[1] = 6 (Fast BSS Transition) + * Action[1] = 1 (Fast BSS Transition Request) + * STA Address + * Target AP Address + * FT IEs + */ + + data_len = 2 + 2 * ETH_ALEN + ies_len; + data = os_malloc(data_len); + if (data == NULL) + return -1; + pos = data; + *pos++ = 0x06; /* FT Action category */ + *pos++ = action; + os_memcpy(pos, own_addr, ETH_ALEN); + pos += ETH_ALEN; + os_memcpy(pos, target_ap, ETH_ALEN); + pos += ETH_ALEN; + os_memcpy(pos, ies, ies_len); + + ret = wpa_driver_nl80211_send_action(bss, drv->assoc_freq, drv->bssid, + own_addr, drv->bssid, + data, data_len); + os_free(data); + + return ret; +} + + +static int nl80211_signal_monitor(void *priv, int threshold, int hysteresis) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg, *cqm = NULL; + + wpa_printf(MSG_DEBUG, "nl80211: Signal monitor threshold=%d " + "hysteresis=%d", threshold, hysteresis); + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_SET_CQM, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); + + cqm = nlmsg_alloc(); + if (cqm == NULL) + return -1; + + NLA_PUT_U32(cqm, NL80211_ATTR_CQM_RSSI_THOLD, threshold); + NLA_PUT_U32(cqm, NL80211_ATTR_CQM_RSSI_HYST, hysteresis); + nla_put_nested(msg, NL80211_ATTR_CQM, cqm); + + if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0) + return 0; + msg = NULL; + +nla_put_failure: + if (cqm) + nlmsg_free(cqm); + nlmsg_free(msg); + return -1; +} + + +static int nl80211_send_frame(void *priv, const u8 *data, size_t data_len, + int encrypt) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + return wpa_driver_nl80211_send_frame(drv, data, data_len, encrypt); +} const struct wpa_driver_ops wpa_driver_nl80211_ops = { @@ -2733,34 +5333,58 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .desc = "Linux nl80211/cfg80211", .get_bssid = wpa_driver_nl80211_get_bssid, .get_ssid = wpa_driver_nl80211_get_ssid, - .set_wpa = wpa_driver_nl80211_set_wpa, .set_key = wpa_driver_nl80211_set_key, - .set_countermeasures = wpa_driver_nl80211_set_countermeasures, - .set_drop_unencrypted = wpa_driver_nl80211_set_drop_unencrypted, - .scan = wpa_driver_nl80211_scan, + .scan2 = wpa_driver_nl80211_scan, .get_scan_results2 = wpa_driver_nl80211_get_scan_results, .deauthenticate = wpa_driver_nl80211_deauthenticate, .disassociate = wpa_driver_nl80211_disassociate, - .set_mode = wpa_driver_nl80211_set_mode, + .authenticate = wpa_driver_nl80211_authenticate, .associate = wpa_driver_nl80211_associate, - .set_auth_alg = wpa_driver_nl80211_set_auth_alg, .init = wpa_driver_nl80211_init, .deinit = wpa_driver_nl80211_deinit, - .set_param = wpa_driver_nl80211_set_param, - .add_pmkid = wpa_driver_nl80211_add_pmkid, - .remove_pmkid = wpa_driver_nl80211_remove_pmkid, - .flush_pmkid = wpa_driver_nl80211_flush_pmkid, .get_capa = wpa_driver_nl80211_get_capa, .set_operstate = wpa_driver_nl80211_set_operstate, + .set_supp_port = wpa_driver_nl80211_set_supp_port, .set_country = wpa_driver_nl80211_set_country, - .set_probe_req_ie = wpa_driver_nl80211_set_probe_req_ie, -#ifdef CONFIG_CLIENT_MLME - .get_hw_feature_data = wpa_driver_nl80211_get_hw_feature_data, - .set_channel = wpa_driver_nl80211_set_channel, - .set_ssid = wpa_driver_nl80211_set_ssid, - .set_bssid = wpa_driver_nl80211_set_bssid, + .set_beacon = wpa_driver_nl80211_set_beacon, + .if_add = wpa_driver_nl80211_if_add, + .if_remove = wpa_driver_nl80211_if_remove, .send_mlme = wpa_driver_nl80211_send_mlme, - .mlme_add_sta = wpa_driver_nl80211_mlme_add_sta, - .mlme_remove_sta = wpa_driver_nl80211_mlme_remove_sta, -#endif /* CONFIG_CLIENT_MLME */ + .get_hw_feature_data = wpa_driver_nl80211_get_hw_feature_data, + .sta_add = wpa_driver_nl80211_sta_add, + .sta_remove = wpa_driver_nl80211_sta_remove, + .hapd_send_eapol = wpa_driver_nl80211_hapd_send_eapol, + .sta_set_flags = wpa_driver_nl80211_sta_set_flags, +#ifdef HOSTAPD + .hapd_init = i802_init, + .hapd_deinit = i802_deinit, + .get_seqnum = i802_get_seqnum, + .flush = i802_flush, + .read_sta_data = i802_read_sta_data, + .sta_deauth = i802_sta_deauth, + .sta_disassoc = i802_sta_disassoc, + .get_inact_sec = i802_get_inact_sec, + .sta_clear_stats = i802_sta_clear_stats, + .set_rts = i802_set_rts, + .set_frag = i802_set_frag, + .set_rate_sets = i802_set_rate_sets, + .set_cts_protect = i802_set_cts_protect, + .set_preamble = i802_set_preamble, + .set_short_slot_time = i802_set_short_slot_time, + .set_tx_queue_params = i802_set_tx_queue_params, + .set_sta_vlan = i802_set_sta_vlan, + .set_wds_sta = i802_set_wds_sta, +#endif /* HOSTAPD */ + .set_freq = i802_set_freq, + .send_action = wpa_driver_nl80211_send_action, + .remain_on_channel = wpa_driver_nl80211_remain_on_channel, + .cancel_remain_on_channel = + wpa_driver_nl80211_cancel_remain_on_channel, + .probe_req_report = wpa_driver_nl80211_probe_req_report, + .disable_11b_rates = wpa_driver_nl80211_disable_11b_rates, + .deinit_ap = wpa_driver_nl80211_deinit_ap, + .resume = wpa_driver_nl80211_resume, + .send_ft_action = nl80211_send_ft_action, + .signal_monitor = nl80211_signal_monitor, + .send_frame = nl80211_send_frame, }; diff --git a/src/drivers/driver_none.c b/src/drivers/driver_none.c new file mode 100644 index 0000000000000..aaeacd66435dd --- /dev/null +++ b/src/drivers/driver_none.c @@ -0,0 +1,99 @@ +/* + * Driver interface for RADIUS server or WPS ER only (no driver) + * Copyright (c) 2008, Atheros Communications + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "driver.h" + + +struct none_driver_data { + struct hostapd_data *hapd; + void *ctx; +}; + + +static void * none_driver_hapd_init(struct hostapd_data *hapd, + struct wpa_init_params *params) +{ + struct none_driver_data *drv; + + drv = os_zalloc(sizeof(struct none_driver_data)); + if (drv == NULL) { + wpa_printf(MSG_ERROR, "Could not allocate memory for none " + "driver data"); + return NULL; + } + drv->hapd = hapd; + + return drv; +} + + +static void none_driver_hapd_deinit(void *priv) +{ + struct none_driver_data *drv = priv; + + os_free(drv); +} + + +static int none_driver_send_ether(void *priv, const u8 *dst, const u8 *src, + u16 proto, const u8 *data, size_t data_len) +{ + return 0; +} + + +static void * none_driver_init(void *ctx, const char *ifname) +{ + struct none_driver_data *drv; + + drv = os_zalloc(sizeof(struct none_driver_data)); + if (drv == NULL) { + wpa_printf(MSG_ERROR, "Could not allocate memory for none " + "driver data"); + return NULL; + } + drv->ctx = ctx; + + return drv; +} + + +static void none_driver_deinit(void *priv) +{ + struct none_driver_data *drv = priv; + + os_free(drv); +} + + +static int none_driver_send_eapol(void *priv, const u8 *dest, u16 proto, + const u8 *data, size_t data_len) +{ + return -1; +} + + +const struct wpa_driver_ops wpa_driver_none_ops = { + .name = "none", + .desc = "no driver (RADIUS server/WPS ER)", + .hapd_init = none_driver_hapd_init, + .hapd_deinit = none_driver_hapd_deinit, + .send_ether = none_driver_send_ether, + .init = none_driver_init, + .deinit = none_driver_deinit, + .send_eapol = none_driver_send_eapol, +}; diff --git a/src/drivers/driver_osx.m b/src/drivers/driver_osx.m index 93d7df01adfe0..69ca4b576c3c0 100644 --- a/src/drivers/driver_osx.m +++ b/src/drivers/driver_osx.m @@ -20,6 +20,7 @@ #include "common.h" #include "driver.h" #include "eloop.h" +#include "common/ieee802_11_defs.h" #include "Apple80211.h" @@ -111,10 +112,12 @@ static void wpa_driver_osx_scan_timeout(void *eloop_ctx, void *timeout_ctx) } -static int wpa_driver_osx_scan(void *priv, const u8 *ssid, size_t ssid_len) +static int wpa_driver_osx_scan(void *priv, struct wpa_driver_scan_params *params) { struct wpa_driver_osx_data *drv = priv; WirelessError err; + const u8 *ssid = params->ssids[0].ssid; + size_t ssid_len = params->ssids[0].ssid_len; if (drv->scan_results) { CFRelease(drv->scan_results); @@ -156,43 +159,66 @@ static int wpa_driver_osx_scan(void *priv, const u8 *ssid, size_t ssid_len) } -static int wpa_driver_osx_get_scan_results(void *priv, - struct wpa_scan_result *results, - size_t max_size) +static void wpa_driver_osx_add_scan_entry(struct wpa_scan_results *res, + WirelessNetworkInfo *info) +{ + struct wpa_scan_res *result, **tmp; + size_t extra_len; + u8 *pos; + + extra_len = 2 + info->ssid_len; + + result = os_zalloc(sizeof(*result) + extra_len); + if (result == NULL) + return; + os_memcpy(result->bssid, info->bssid, ETH_ALEN); + result->freq = 2407 + info->channel * 5; + //result->beacon_int =; + result->caps = info->capability; + //result->qual = info->signal; + result->noise = info->noise; + + pos = (u8 *)(result + 1); + + *pos++ = WLAN_EID_SSID; + *pos++ = info->ssid_len; + os_memcpy(pos, info->ssid, info->ssid_len); + pos += info->ssid_len; + + result->ie_len = pos - (u8 *)(result + 1); + + tmp = os_realloc(res->res, + (res->num + 1) * sizeof(struct wpa_scan_res *)); + if (tmp == NULL) { + os_free(result); + return; + } + tmp[res->num++] = result; + res->res = tmp; +} + + +static struct wpa_scan_results * wpa_driver_osx_get_scan_results(void *priv) { struct wpa_driver_osx_data *drv = priv; + struct wpa_scan_results *res; size_t i, num; if (drv->scan_results == NULL) return 0; num = CFArrayGetCount(drv->scan_results); - if (num > max_size) - num = max_size; - os_memset(results, 0, num * sizeof(struct wpa_scan_result)); - for (i = 0; i < num; i++) { - struct wpa_scan_result *res = &results[i]; - WirelessNetworkInfo *info; - info = (WirelessNetworkInfo *) - CFDataGetBytePtr(CFArrayGetValueAtIndex( - drv->scan_results, i)); + res = os_zalloc(sizeof(*res)); + if (res == NULL) + return NULL; - os_memcpy(res->bssid, info->bssid, ETH_ALEN); - if (info->ssid_len > 32) { - wpa_printf(MSG_DEBUG, "OSX: Invalid SSID length %d in " - "scan results", (int) info->ssid_len); - continue; - } - os_memcpy(res->ssid, info->ssid, info->ssid_len); - res->ssid_len = info->ssid_len; - res->caps = info->capability; - res->freq = 2407 + info->channel * 5; - res->level = info->signal; - res->noise = info->noise; - } + for (i = 0; i < num; i++) + wpa_driver_osx_add_scan_entry(res, (WirelessNetworkInfo *) + CFDataGetBytePtr(CFArrayGetValueAtIndex( + drv->scan_results, i))); - return num; + return res; } @@ -277,7 +303,8 @@ static int wpa_driver_osx_associate(void *priv, } -static int wpa_driver_osx_set_key(void *priv, wpa_alg alg, const u8 *addr, +static int wpa_driver_osx_set_key(const char *ifname, void *priv, + enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len) @@ -424,8 +451,8 @@ const struct wpa_driver_ops wpa_driver_osx_ops = { .get_bssid = wpa_driver_osx_get_bssid, .init = wpa_driver_osx_init, .deinit = wpa_driver_osx_deinit, - .scan = wpa_driver_osx_scan, - .get_scan_results = wpa_driver_osx_get_scan_results, + .scan2 = wpa_driver_osx_scan, + .get_scan_results2 = wpa_driver_osx_get_scan_results, .associate = wpa_driver_osx_associate, .set_key = wpa_driver_osx_set_key, .get_capa = wpa_driver_osx_get_capa, diff --git a/src/drivers/driver_prism54.c b/src/drivers/driver_prism54.c deleted file mode 100644 index e64e762eddb54..0000000000000 --- a/src/drivers/driver_prism54.c +++ /dev/null @@ -1,381 +0,0 @@ -/* - * WPA Supplicant - driver interaction with Linux Prism54.org driver - * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> - * Copyright (c) 2004, Luis R. Rodriguez <mcgrof@ruslug.rutgers.edu> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" -#include <sys/ioctl.h> - -#include "wireless_copy.h" -#include "common.h" -#include "driver.h" -#include "driver_wext.h" -#include "driver_hostap.h" - -struct wpa_driver_prism54_data { - void *wext; /* private data for driver_wext */ - void *ctx; - char ifname[IFNAMSIZ + 1]; - int sock; -}; - -#define PRISM54_SET_WPA SIOCIWFIRSTPRIV+12 -#define PRISM54_HOSTAPD SIOCIWFIRSTPRIV+25 -#define PRISM54_DROP_UNENCRYPTED SIOCIWFIRSTPRIV+26 - -static void show_set_key_error(struct prism2_hostapd_param *); - -static int hostapd_ioctl_prism54(struct wpa_driver_prism54_data *drv, - struct prism2_hostapd_param *param, - int len, int show_err) -{ - struct iwreq iwr; - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - iwr.u.data.pointer = (caddr_t) param; - iwr.u.data.length = len; - - if (ioctl(drv->sock, PRISM54_HOSTAPD, &iwr) < 0) { - int ret = errno; - if (show_err) - perror("ioctl[PRISM54_HOSTAPD]"); - return ret; - } - - return 0; -} - - -static int wpa_driver_prism54_set_wpa_ie(struct wpa_driver_prism54_data *drv, - const u8 *wpa_ie, - size_t wpa_ie_len) -{ - struct prism2_hostapd_param *param; - int res; - size_t blen = PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN + wpa_ie_len; - if (blen < sizeof(*param)) - blen = sizeof(*param); - - param = os_zalloc(blen); - if (param == NULL) - return -1; - - param->cmd = PRISM2_HOSTAPD_SET_GENERIC_ELEMENT; - param->u.generic_elem.len = wpa_ie_len; - os_memcpy(param->u.generic_elem.data, wpa_ie, wpa_ie_len); - res = hostapd_ioctl_prism54(drv, param, blen, 1); - - os_free(param); - - return res; -} - - -/* This is called at wpa_supplicant daemon init time */ -static int wpa_driver_prism54_set_wpa(void *priv, int enabled) -{ - struct wpa_driver_prism54_data *drv = priv; - struct prism2_hostapd_param *param; - int res; - size_t blen = PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN; - if (blen < sizeof(*param)) - blen = sizeof(*param); - - param = os_zalloc(blen); - if (param == NULL) - return -1; - - param->cmd = PRISM54_SET_WPA; - param->u.generic_elem.len = 0; - res = hostapd_ioctl_prism54(drv, param, blen, 1); - - os_free(param); - - return res; -} - - -static int wpa_driver_prism54_set_key(void *priv, wpa_alg alg, - const u8 *addr, int key_idx, int set_tx, - const u8 *seq, size_t seq_len, - const u8 *key, size_t key_len) -{ - struct wpa_driver_prism54_data *drv = priv; - struct prism2_hostapd_param *param; - u8 *buf; - size_t blen; - int ret = 0; - char *alg_name; - - switch (alg) { - case WPA_ALG_NONE: - alg_name = "none"; - return -1; - break; - case WPA_ALG_WEP: - alg_name = "WEP"; - return -1; - break; - case WPA_ALG_TKIP: - alg_name = "TKIP"; - break; - case WPA_ALG_CCMP: - alg_name = "CCMP"; - return -1; - break; - default: - return -1; - } - - wpa_printf(MSG_DEBUG, "%s: alg=%s key_idx=%d set_tx=%d seq_len=%lu " - "key_len=%lu", __FUNCTION__, alg_name, key_idx, set_tx, - (unsigned long) seq_len, (unsigned long) key_len); - - if (seq_len > 8) - return -2; - - blen = sizeof(*param) + key_len; - buf = os_zalloc(blen); - if (buf == NULL) - return -1; - - param = (struct prism2_hostapd_param *) buf; - param->cmd = PRISM2_SET_ENCRYPTION; - /* TODO: In theory, STA in client mode can use five keys; four default - * keys for receiving (with keyidx 0..3) and one individual key for - * both transmitting and receiving (keyidx 0) _unicast_ packets. Now, - * keyidx 0 is reserved for this unicast use and default keys can only - * use keyidx 1..3 (i.e., default key with keyidx 0 is not supported). - * This should be fine for more or less all cases, but for completeness - * sake, the driver could be enhanced to support the missing key. */ -#if 0 - if (addr == NULL) - os_memset(param->sta_addr, 0xff, ETH_ALEN); - else - os_memcpy(param->sta_addr, addr, ETH_ALEN); -#else - os_memset(param->sta_addr, 0xff, ETH_ALEN); -#endif - os_strlcpy((char *) param->u.crypt.alg, alg_name, - HOSTAP_CRYPT_ALG_NAME_LEN); - param->u.crypt.flags = set_tx ? HOSTAP_CRYPT_FLAG_SET_TX_KEY : 0; - param->u.crypt.idx = key_idx; - os_memcpy(param->u.crypt.seq, seq, seq_len); - param->u.crypt.key_len = key_len; - os_memcpy((u8 *) (param + 1), key, key_len); - - if (hostapd_ioctl_prism54(drv, param, blen, 1)) { - wpa_printf(MSG_WARNING, "Failed to set encryption."); - show_set_key_error(param); - ret = -1; - } - os_free(buf); - - return ret; -} - - -static int wpa_driver_prism54_set_countermeasures(void *priv, - int enabled) -{ - /* FIX */ - printf("wpa_driver_prism54_set_countermeasures - not yet " - "implemented\n"); - return 0; -} - - -static int wpa_driver_prism54_set_drop_unencrypted(void *priv, - int enabled) -{ - struct wpa_driver_prism54_data *drv = priv; - struct prism2_hostapd_param *param; - int res; - size_t blen = PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN; - if (blen < sizeof(*param)) - blen = sizeof(*param); - - param = os_zalloc(blen); - if (param == NULL) - return -1; - - param->cmd = PRISM54_DROP_UNENCRYPTED; - param->u.generic_elem.len = 0; - res = hostapd_ioctl_prism54(drv, param, blen, 1); - - os_free(param); - - return res; -} - - -static int wpa_driver_prism54_deauthenticate(void *priv, const u8 *addr, - int reason_code) -{ - /* FIX */ - printf("wpa_driver_prism54_deauthenticate - not yet implemented\n"); - return 0; -} - - -static int wpa_driver_prism54_disassociate(void *priv, const u8 *addr, - int reason_code) -{ - /* FIX */ - printf("wpa_driver_prism54_disassociate - not yet implemented\n"); - return 0; -} - - -static int -wpa_driver_prism54_associate(void *priv, - struct wpa_driver_associate_params *params) -{ - struct wpa_driver_prism54_data *drv = priv; - int ret = 0; - - if (wpa_driver_prism54_set_wpa_ie(drv, params->wpa_ie, - params->wpa_ie_len) < 0) - ret = -1; - if (wpa_driver_wext_set_freq(drv->wext, params->freq) < 0) - ret = -1; - if (wpa_driver_wext_set_ssid(drv->wext, params->ssid, - params->ssid_len) < 0) - ret = -1; - if (wpa_driver_wext_set_bssid(drv->wext, params->bssid) < 0) - ret = -1; - - return ret; -} - -static void show_set_key_error(struct prism2_hostapd_param *param) -{ - switch (param->u.crypt.err) { - case HOSTAP_CRYPT_ERR_UNKNOWN_ALG: - wpa_printf(MSG_INFO, "Unknown algorithm '%s'.", - param->u.crypt.alg); - wpa_printf(MSG_INFO, "You may need to load kernel module to " - "register that algorithm."); - wpa_printf(MSG_INFO, "E.g., 'modprobe hostap_crypt_wep' for " - "WEP."); - break; - case HOSTAP_CRYPT_ERR_UNKNOWN_ADDR: - wpa_printf(MSG_INFO, "Unknown address " MACSTR ".", - MAC2STR(param->sta_addr)); - break; - case HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED: - wpa_printf(MSG_INFO, "Crypt algorithm initialization failed."); - break; - case HOSTAP_CRYPT_ERR_KEY_SET_FAILED: - wpa_printf(MSG_INFO, "Key setting failed."); - break; - case HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED: - wpa_printf(MSG_INFO, "TX key index setting failed."); - break; - case HOSTAP_CRYPT_ERR_CARD_CONF_FAILED: - wpa_printf(MSG_INFO, "Card configuration failed."); - break; - } -} - - -static int wpa_driver_prism54_get_bssid(void *priv, u8 *bssid) -{ - struct wpa_driver_prism54_data *drv = priv; - return wpa_driver_wext_get_bssid(drv->wext, bssid); -} - - -static int wpa_driver_prism54_get_ssid(void *priv, u8 *ssid) -{ - struct wpa_driver_prism54_data *drv = priv; - return wpa_driver_wext_get_ssid(drv->wext, ssid); -} - - -static int wpa_driver_prism54_scan(void *priv, const u8 *ssid, size_t ssid_len) -{ - struct wpa_driver_prism54_data *drv = priv; - return wpa_driver_wext_scan(drv->wext, ssid, ssid_len); -} - - -static struct wpa_scan_results * -wpa_driver_prism54_get_scan_results(void *priv) -{ - struct wpa_driver_prism54_data *drv = priv; - return wpa_driver_wext_get_scan_results(drv->wext); -} - - -static int wpa_driver_prism54_set_operstate(void *priv, int state) -{ - struct wpa_driver_prism54_data *drv = priv; - return wpa_driver_wext_set_operstate(drv->wext, state); -} - - -static void * wpa_driver_prism54_init(void *ctx, const char *ifname) -{ - struct wpa_driver_prism54_data *drv; - - drv = os_zalloc(sizeof(*drv)); - if (drv == NULL) - return NULL; - drv->wext = wpa_driver_wext_init(ctx, ifname); - if (drv->wext == NULL) { - os_free(drv); - return NULL; - } - - drv->ctx = ctx; - os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); - drv->sock = socket(PF_INET, SOCK_DGRAM, 0); - if (drv->sock < 0) { - wpa_driver_wext_deinit(drv->wext); - os_free(drv); - return NULL; - } - - return drv; -} - - -static void wpa_driver_prism54_deinit(void *priv) -{ - struct wpa_driver_prism54_data *drv = priv; - wpa_driver_wext_deinit(drv->wext); - close(drv->sock); - os_free(drv); -} - - -const struct wpa_driver_ops wpa_driver_prism54_ops = { - .name = "prism54", - .desc = "Prism54.org driver (Intersil Prism GT/Duette/Indigo)", - .get_bssid = wpa_driver_prism54_get_bssid, - .get_ssid = wpa_driver_prism54_get_ssid, - .set_wpa = wpa_driver_prism54_set_wpa, - .set_key = wpa_driver_prism54_set_key, - .set_countermeasures = wpa_driver_prism54_set_countermeasures, - .set_drop_unencrypted = wpa_driver_prism54_set_drop_unencrypted, - .scan = wpa_driver_prism54_scan, - .get_scan_results2 = wpa_driver_prism54_get_scan_results, - .deauthenticate = wpa_driver_prism54_deauthenticate, - .disassociate = wpa_driver_prism54_disassociate, - .associate = wpa_driver_prism54_associate, - .init = wpa_driver_prism54_init, - .deinit = wpa_driver_prism54_deinit, - .set_operstate = wpa_driver_prism54_set_operstate, -}; diff --git a/src/drivers/driver_privsep.c b/src/drivers/driver_privsep.c index fdf299dc84e44..28485215e2b28 100644 --- a/src/drivers/driver_privsep.c +++ b/src/drivers/driver_privsep.c @@ -18,7 +18,7 @@ #include "common.h" #include "driver.h" #include "eloop.h" -#include "privsep_commands.h" +#include "common/privsep_commands.h" struct wpa_driver_privsep_data { @@ -102,18 +102,12 @@ static int wpa_priv_cmd(struct wpa_driver_privsep_data *drv, int cmd, } -static int wpa_driver_privsep_set_wpa(void *priv, int enabled) -{ - struct wpa_driver_privsep_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); - return wpa_priv_cmd(drv, PRIVSEP_CMD_SET_WPA, &enabled, - sizeof(enabled), NULL, NULL); -} - - -static int wpa_driver_privsep_scan(void *priv, const u8 *ssid, size_t ssid_len) +static int wpa_driver_privsep_scan(void *priv, + struct wpa_driver_scan_params *params) { struct wpa_driver_privsep_data *drv = priv; + const u8 *ssid = params->ssids[0].ssid; + size_t ssid_len = params->ssids[0].ssid_len; wpa_printf(MSG_DEBUG, "%s: priv=%p", __func__, priv); return wpa_priv_cmd(drv, PRIVSEP_CMD_SCAN, ssid, ssid_len, NULL, NULL); @@ -196,10 +190,11 @@ wpa_driver_privsep_get_scan_results2(void *priv) } -static int wpa_driver_privsep_set_key(void *priv, wpa_alg alg, const u8 *addr, - int key_idx, int set_tx, - const u8 *seq, size_t seq_len, - const u8 *key, size_t key_len) +static int wpa_driver_privsep_set_key(const char *ifname, void *priv, + enum wpa_alg alg, const u8 *addr, + int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) { struct wpa_driver_privsep_data *drv = priv; struct privsep_cmd_set_key cmd; @@ -326,7 +321,8 @@ static int wpa_driver_privsep_disassociate(void *priv, const u8 *addr, } -static void wpa_driver_privsep_event_assoc(void *ctx, wpa_event_type event, +static void wpa_driver_privsep_event_assoc(void *ctx, + enum wpa_event_type event, u8 *buf, size_t len) { union wpa_event_data data; @@ -438,24 +434,7 @@ static void wpa_driver_privsep_event_rx_eapol(void *ctx, u8 *buf, size_t len) { if (len < ETH_ALEN) return; - - wpa_supplicant_rx_eapol(ctx, buf, buf + ETH_ALEN, len - ETH_ALEN); -} - - -static void wpa_driver_privsep_event_sta_rx(void *ctx, u8 *buf, size_t len) -{ -#ifdef CONFIG_CLIENT_MLME - struct ieee80211_rx_status *rx_status; - - if (len < sizeof(*rx_status)) - return; - rx_status = (struct ieee80211_rx_status *) buf; - buf += sizeof(*rx_status); - len -= sizeof(*rx_status); - - wpa_supplicant_sta_rx(ctx, buf, len, rx_status); -#endif /* CONFIG_CLIENT_MLME */ + drv_event_eapol_rx(ctx, buf, buf + ETH_ALEN, len - ETH_ALEN); } @@ -535,10 +514,6 @@ static void wpa_driver_privsep_receive(int sock, void *eloop_ctx, wpa_driver_privsep_event_rx_eapol(drv->ctx, event_buf, event_len); break; - case PRIVSEP_EVENT_STA_RX: - wpa_driver_privsep_event_sta_rx(drv->ctx, event_buf, - event_len); - break; } os_free(buf); @@ -747,15 +722,6 @@ static const u8 * wpa_driver_privsep_get_mac_addr(void *priv) } -static int wpa_driver_privsep_set_mode(void *priv, int mode) -{ - struct wpa_driver_privsep_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s mode=%d", __func__, mode); - return wpa_priv_cmd(drv, PRIVSEP_CMD_SET_MODE, &mode, sizeof(mode), - NULL, NULL); -} - - static int wpa_driver_privsep_set_country(void *priv, const char *alpha2) { struct wpa_driver_privsep_data *drv = priv; @@ -768,52 +734,24 @@ static int wpa_driver_privsep_set_country(void *priv, const char *alpha2) struct wpa_driver_ops wpa_driver_privsep_ops = { "privsep", "wpa_supplicant privilege separated driver", - wpa_driver_privsep_get_bssid, - wpa_driver_privsep_get_ssid, - wpa_driver_privsep_set_wpa, - wpa_driver_privsep_set_key, - wpa_driver_privsep_init, - wpa_driver_privsep_deinit, - wpa_driver_privsep_set_param, - NULL /* set_countermeasures */, - NULL /* set_drop_unencrypted */, - wpa_driver_privsep_scan, - NULL /* get_scan_results */, - wpa_driver_privsep_deauthenticate, - wpa_driver_privsep_disassociate, - wpa_driver_privsep_associate, - NULL /* set_auth_alg */, - NULL /* add_pmkid */, - NULL /* remove_pmkid */, - NULL /* flush_pmkid */, - wpa_driver_privsep_get_capa, - NULL /* poll */, - NULL /* get_ifname */, - wpa_driver_privsep_get_mac_addr, - NULL /* send_eapol */, - NULL /* set_operstate */, - NULL /* mlme_setprotection */, - NULL /* get_hw_feature_data */, - NULL /* set_channel */, - NULL /* set_ssid */, - NULL /* set_bssid */, - NULL /* send_mlme */, - NULL /* mlme_add_sta */, - NULL /* mlme_remove_sta */, - NULL /* update_ft_ies */, - NULL /* send_ft_action */, - wpa_driver_privsep_get_scan_results2, - NULL /* set_probe_req_ie */, - wpa_driver_privsep_set_mode, - wpa_driver_privsep_set_country, - NULL /* global_init */, - NULL /* global_deinit */, - NULL /* init2 */, - NULL /* get_interfaces */ + .get_bssid = wpa_driver_privsep_get_bssid, + .get_ssid = wpa_driver_privsep_get_ssid, + .set_key = wpa_driver_privsep_set_key, + .init = wpa_driver_privsep_init, + .deinit = wpa_driver_privsep_deinit, + .set_param = wpa_driver_privsep_set_param, + .scan2 = wpa_driver_privsep_scan, + .deauthenticate = wpa_driver_privsep_deauthenticate, + .disassociate = wpa_driver_privsep_disassociate, + .associate = wpa_driver_privsep_associate, + .get_capa = wpa_driver_privsep_get_capa, + .get_mac_addr = wpa_driver_privsep_get_mac_addr, + .get_scan_results2 = wpa_driver_privsep_get_scan_results2, + .set_country = wpa_driver_privsep_set_country, }; -struct wpa_driver_ops *wpa_supplicant_drivers[] = +struct wpa_driver_ops *wpa_drivers[] = { &wpa_driver_privsep_ops, NULL diff --git a/src/drivers/driver_ps3.c b/src/drivers/driver_ps3.c deleted file mode 100644 index fde3425e281f3..0000000000000 --- a/src/drivers/driver_ps3.c +++ /dev/null @@ -1,186 +0,0 @@ -/* - * WPA Supplicant - PS3 Linux wireless extension driver interface - * Copyright 2007, 2008 Sony Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" -#include <sys/ioctl.h> -#include "wireless_copy.h" -#include "common.h" -#include "wpa_common.h" -#include "driver.h" -#include "eloop.h" -#include "driver_wext.h" -#include "ieee802_11_defs.h" - -static int wpa_driver_ps3_set_wpa_key(struct wpa_driver_wext_data *drv, - struct wpa_driver_associate_params *params) -{ - int ret, i; - struct iwreq iwr; - char *buf, *str; - - if (!params->psk && !params->passphrase) { - wpa_printf(MSG_INFO, "%s:no PSK error", __func__); - return -EINVAL; - } - - os_memset(&iwr, 0, sizeof(iwr)); - if (params->psk) { - /* includes null */ - iwr.u.data.length = PMK_LEN * 2 + 1; - buf = os_malloc(iwr.u.data.length); - if (!buf) - return -ENOMEM; - str = buf; - for (i = 0; i < PMK_LEN; i++) { - str += snprintf(str, iwr.u.data.length - (str - buf), - "%02x", params->psk[i]); - } - } else if (params->passphrase) { - /* including quotations and null */ - iwr.u.data.length = strlen(params->passphrase) + 3; - buf = os_malloc(iwr.u.data.length); - if (!buf) - return -ENOMEM; - buf[0] = '"'; - os_memcpy(buf + 1, params->passphrase, iwr.u.data.length - 3); - buf[iwr.u.data.length - 2] = '"'; - buf[iwr.u.data.length - 1] = '\0'; - } else - return -EINVAL; - iwr.u.data.pointer = (caddr_t) buf; - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - ret = ioctl(drv->ioctl_sock, SIOCIWFIRSTPRIV, &iwr); - os_free(buf); - - return ret; -} - -static int wpa_driver_ps3_set_wep_keys(struct wpa_driver_wext_data *drv, - struct wpa_driver_associate_params *params) -{ - int ret, i; - struct iwreq iwr; - - for (i = 0; i < 4; i++) { - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - iwr.u.encoding.flags = i + 1; - if (params->wep_key_len[i]) { - iwr.u.encoding.pointer = (caddr_t) params->wep_key[i]; - iwr.u.encoding.length = params->wep_key_len[i]; - } else - iwr.u.encoding.flags = IW_ENCODE_NOKEY | - IW_ENCODE_DISABLED; - - if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) { - perror("ioctl[SIOCSIWENCODE]"); - ret = -1; - } - } - return ret; -} - -static int wpa_driver_ps3_associate(void *priv, - struct wpa_driver_associate_params *params) -{ - struct wpa_driver_wext_data *drv = priv; - int ret, value; - - wpa_printf(MSG_DEBUG, "%s: <-", __func__); - - /* clear BSSID */ - if (!params->bssid && - wpa_driver_wext_set_bssid(drv, NULL) < 0) - ret = -1; - - if (wpa_driver_wext_set_mode(drv, params->mode) < 0) - ret = -1; - - if (params->wpa_ie == NULL || params->wpa_ie_len == 0) - value = IW_AUTH_WPA_VERSION_DISABLED; - else if (params->wpa_ie[0] == WLAN_EID_RSN) - value = IW_AUTH_WPA_VERSION_WPA2; - else - value = IW_AUTH_WPA_VERSION_WPA; - if (wpa_driver_wext_set_auth_param(drv, - IW_AUTH_WPA_VERSION, value) < 0) - ret = -1; - value = wpa_driver_wext_cipher2wext(params->pairwise_suite); - if (wpa_driver_wext_set_auth_param(drv, - IW_AUTH_CIPHER_PAIRWISE, value) < 0) - ret = -1; - value = wpa_driver_wext_cipher2wext(params->group_suite); - if (wpa_driver_wext_set_auth_param(drv, - IW_AUTH_CIPHER_GROUP, value) < 0) - ret = -1; - value = wpa_driver_wext_keymgmt2wext(params->key_mgmt_suite); - if (wpa_driver_wext_set_auth_param(drv, IW_AUTH_KEY_MGMT, value) < 0) - ret = -1; - - /* set selected BSSID */ - if (params->bssid && - wpa_driver_wext_set_bssid(drv, params->bssid) < 0) - ret = -1; - - switch (params->group_suite) { - case CIPHER_NONE: - ret = 0; - break; - case CIPHER_WEP40: - case CIPHER_WEP104: - ret = wpa_driver_ps3_set_wep_keys(drv, params); - break; - case CIPHER_TKIP: - case CIPHER_CCMP: - ret = wpa_driver_ps3_set_wpa_key(drv, params); - break; - } - - /* start to associate */ - ret = wpa_driver_wext_set_ssid(drv, params->ssid, params->ssid_len); - - wpa_printf(MSG_DEBUG, "%s: ->", __func__); - - return ret; -} - -static int wpa_driver_ps3_get_capa(void *priv, struct wpa_driver_capa *capa) -{ - int ret; - wpa_printf(MSG_DEBUG, "%s:<-", __func__); - - ret = wpa_driver_wext_get_capa(priv, capa); - if (ret) { - wpa_printf(MSG_INFO, "%s: base wext returns error %d", - __func__, ret); - return ret; - } - /* PS3 hypervisor does association and 4way handshake by itself */ - capa->flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE; - wpa_printf(MSG_DEBUG, "%s:->", __func__); - return 0; -} - -const struct wpa_driver_ops wpa_driver_ps3_ops = { - .name = "ps3", - .desc = "PLAYSTATION3 Linux wireless extension driver", - .get_bssid = wpa_driver_wext_get_bssid, - .get_ssid = wpa_driver_wext_get_ssid, - .scan = wpa_driver_wext_scan, - .get_scan_results2 = wpa_driver_wext_get_scan_results, - .associate = wpa_driver_ps3_associate, /* PS3 */ - .init = wpa_driver_wext_init, - .deinit = wpa_driver_wext_deinit, - .get_capa = wpa_driver_ps3_get_capa, /* PS3 */ -}; diff --git a/src/drivers/driver_ralink.c b/src/drivers/driver_ralink.c index e9313cb33ef09..09d1ef5405090 100644 --- a/src/drivers/driver_ralink.c +++ b/src/drivers/driver_ralink.c @@ -22,8 +22,10 @@ #include "driver.h" #include "l2_packet/l2_packet.h" #include "eloop.h" -#include "ieee802_11_defs.h" +#include "common/ieee802_11_defs.h" #include "priv_netlink.h" +#include "netlink.h" +#include "linux_ioctl.h" #include "driver_ralink.h" static void wpa_driver_ralink_scan_timeout(void *eloop_ctx, void *timeout_ctx); @@ -33,7 +35,7 @@ static void wpa_driver_ralink_scan_timeout(void *eloop_ctx, void *timeout_ctx); struct wpa_driver_ralink_data { void *ctx; int ioctl_sock; - int event_sock; + struct netlink_data *netlink; char ifname[IFNAMSIZ + 1]; u8 *assoc_req_ies; size_t assoc_req_ies_len; @@ -45,6 +47,7 @@ struct wpa_driver_ralink_data { int ap_scan; int scanning_done; u8 g_driver_down; + BOOLEAN bAddWepKey; }; static int ralink_set_oid(struct wpa_driver_ralink_data *drv, @@ -450,6 +453,7 @@ wpa_driver_ralink_event_wireless_custom(struct wpa_driver_ralink_data *drv, void *ctx, char *custom) { union wpa_event_data data; + u8 *req_ies = NULL, *resp_ies = NULL; wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); @@ -478,12 +482,12 @@ wpa_driver_ralink_event_wireless_custom(struct wpa_driver_ralink_data *drv, */ bytes = drv->assoc_req_ies_len; - data.assoc_info.req_ies = os_malloc(bytes); - if (data.assoc_info.req_ies == NULL) + req_ies = os_malloc(bytes); + if (req_ies == NULL) return; - + os_memcpy(req_ies, spos, bytes); + data.assoc_info.req_ies = req_ies; data.assoc_info.req_ies_len = bytes; - os_memcpy(data.assoc_info.req_ies, spos, bytes); /* skip the '\0' byte */ spos += bytes + 1; @@ -499,21 +503,48 @@ wpa_driver_ralink_event_wireless_custom(struct wpa_driver_ralink_data *drv, if (!bytes) goto done; - - data.assoc_info.resp_ies = os_malloc(bytes); - if (data.assoc_info.resp_ies == NULL) + resp_ies = os_malloc(bytes); + if (resp_ies == NULL) goto done; - + os_memcpy(resp_ies, spos, bytes); + data.assoc_info.resp_ies = resp_ies; data.assoc_info.resp_ies_len = bytes; - os_memcpy(data.assoc_info.resp_ies, spos, bytes); } wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data); - /* free allocated memory */ done: - os_free(data.assoc_info.resp_ies); - os_free(data.assoc_info.req_ies); + /* free allocated memory */ + os_free(resp_ies); + os_free(req_ies); + } +} + +static void ralink_interface_up(struct wpa_driver_ralink_data *drv) +{ + union wpa_event_data event; + int enable_wpa_supplicant = 0; + drv->g_driver_down = 0; + os_memset(&event, 0, sizeof(event)); + os_snprintf(event.interface_status.ifname, + sizeof(event.interface_status.ifname), "%s", drv->ifname); + + event.interface_status.ievent = EVENT_INTERFACE_ADDED; + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event); + + if (drv->ap_scan == 1) + enable_wpa_supplicant = 1; + else + enable_wpa_supplicant = 2; + /* trigger driver support wpa_supplicant */ + if (ralink_set_oid(drv, RT_OID_WPA_SUPPLICANT_SUPPORT, + (PCHAR) &enable_wpa_supplicant, sizeof(UCHAR)) < 0) + { + wpa_printf(MSG_INFO, "RALINK: Failed to set " + "RT_OID_WPA_SUPPLICANT_SUPPORT(%d)", + (int) enable_wpa_supplicant); + wpa_printf(MSG_ERROR, "ralink. Driver does not support " + "wpa_supplicant"); } } @@ -580,33 +611,9 @@ wpa_driver_ralink_event_wireless(struct wpa_driver_ralink_data *drv, } if (iwe->u.data.flags == RT_ASSOC_EVENT_FLAG) { + wpa_supplicant_event(ctx, EVENT_ASSOC, NULL); wpa_printf(MSG_DEBUG, "Custom wireless event: " "receive ASSOCIATED_EVENT !!!"); - /* determine whether the dynamic-WEP is used or - * not */ -#if 0 - if (wpa_s && wpa_s->current_ssid && - wpa_s->current_ssid->key_mgmt == - WPA_KEY_MGMT_IEEE8021X_NO_WPA) { - if ((wpa_s->current_ssid->eapol_flags & - (EAPOL_FLAG_REQUIRE_KEY_UNICAST | EAPOL_FLAG_REQUIRE_KEY_BROADCAST))) { - //wpa_printf(MSG_DEBUG, "The current ssid - (%s), eapol_flag = %d.\n", - // wpa_ssid_txt(wpa_s->current_ssid->ssid, wpa_s->current_ssid->ssid_len),wpa_s->current_ssid->eapol_flags); - ieee8021x_required_key = TRUE; - } - - if (ralink_set_oid(drv, OID_802_11_SET_IEEE8021X_REQUIRE_KEY, (char *) &ieee8021x_required_key, sizeof(BOOLEAN)) < 0) - { - wpa_printf(MSG_DEBUG, "ERROR: Failed to set OID_802_11_SET_IEEE8021X_REQUIRE_KEY(%d)", - (int) ieee8021x_required_key); - } - - wpa_printf(MSG_DEBUG, "ieee8021x_required_key is %s and eapol_flag(%d).\n", ieee8021x_required_key ? "TRUE" : "FALSE", - wpa_s->current_ssid->eapol_flags); - } -#endif - - wpa_supplicant_event(ctx, EVENT_ASSOC, NULL); } else if (iwe->u.data.flags == RT_REQIE_EVENT_FLAG) { wpa_printf(MSG_DEBUG, "Custom wireless event: " "receive ReqIEs !!!"); @@ -689,51 +696,8 @@ wpa_driver_ralink_event_wireless(struct wpa_driver_ralink_data *drv, } else if (iwe->u.data.flags == RT_INTERFACE_DOWN) { drv->g_driver_down = 1; eloop_terminate(); - } else if (iwe->u.data.flags == RT_REPORT_AP_INFO) { - if (drv->ap_scan != 1) { - typedef struct PACKED { - UCHAR bssid[MAC_ADDR_LEN]; - UCHAR ssid[MAX_LEN_OF_SSID]; - INT ssid_len; - UCHAR wpa_ie[40]; - INT wpa_ie_len; - UCHAR rsn_ie[40]; - INT rsn_ie_len; - INT freq; - USHORT caps; - } *PAPINFO; - - wpa_printf(MSG_DEBUG, "Custom wireless" - " event: receive " - "RT_REPORT_AP_INFO !!!"); - //printf("iwe->u.data.length = %d\n", iwe->u.data.length); - //wpa_hexdump(MSG_DEBUG, "AP_Info: ", buf, iwe->u.data.length); -#if 0 - wpa_s->num_scan_results = 1; - if (wpa_s->scan_results) - os_free(wpa_s->scan_results); - wpa_s->scan_results = os_malloc(sizeof(struct wpa_scan_result) + 1); - if (wpa_s->scan_results) { - PAPINFO pApInfo = (PAPINFO)buf; - os_memcpy(wpa_s->scan_results[0].bssid, pApInfo->bssid, ETH_ALEN); - os_memcpy(wpa_s->scan_results[0].ssid, pApInfo->ssid, pApInfo->ssid_len); - wpa_s->scan_results[0].ssid_len = pApInfo->ssid_len; - if (pApInfo->wpa_ie_len > 0) { - os_memcpy(wpa_s->scan_results[0].wpa_ie, pApInfo->wpa_ie, pApInfo->wpa_ie_len); - wpa_s->scan_results[0].wpa_ie_len = pApInfo->wpa_ie_len; - } else if (pApInfo->rsn_ie_len > 0) { - os_memcpy(wpa_s->scan_results[0].rsn_ie, pApInfo->rsn_ie, pApInfo->rsn_ie_len); - wpa_s->scan_results[0].rsn_ie_len = pApInfo->rsn_ie_len; - } - wpa_s->scan_results[0].caps = pApInfo->caps; - wpa_s->scan_results[0].freq = pApInfo->freq; - } else { - wpa_printf("wpa_s->scan_" - "results fail to " - "os_malloc!!\n"); - } -#endif - } + } else if (iwe->u.data.flags == RT_INTERFACE_UP) { + ralink_interface_up(drv); } else { wpa_driver_ralink_event_wireless_custom( drv, ctx, buf); @@ -747,29 +711,20 @@ wpa_driver_ralink_event_wireless(struct wpa_driver_ralink_data *drv, } static void -wpa_driver_ralink_event_rtm_newlink(struct wpa_driver_ralink_data *drv, - void *ctx, struct nlmsghdr *h, int len) +wpa_driver_ralink_event_rtm_newlink(void *ctx, struct ifinfomsg *ifi, + u8 *buf, size_t len) { - struct ifinfomsg *ifi; - int attrlen, nlmsg_len, rta_len; - struct rtattr * attr; + struct wpa_driver_ralink_data *drv = ctx; + int attrlen, rta_len; + struct rtattr *attr; wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - if (len < (int) sizeof(*ifi)) - return; - - ifi = NLMSG_DATA(h); wpa_hexdump(MSG_DEBUG, "ifi: ", (u8 *) ifi, sizeof(struct ifinfomsg)); - nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); - - attrlen = h->nlmsg_len - nlmsg_len; + attrlen = len; wpa_printf(MSG_DEBUG, "attrlen=%d", attrlen); - if (attrlen < 0) - return; - - attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); + attr = (struct rtattr *) buf; wpa_hexdump(MSG_DEBUG, "attr1: ", (u8 *) attr, sizeof(struct rtattr)); rta_len = RTA_ALIGN(sizeof(struct rtattr)); wpa_hexdump(MSG_DEBUG, "attr2: ", (u8 *)attr,rta_len); @@ -787,60 +742,6 @@ wpa_driver_ralink_event_rtm_newlink(struct wpa_driver_ralink_data *drv, } } -static void wpa_driver_ralink_event_receive(int sock, void *ctx, - void *sock_ctx) -{ - char buf[8192]; - int left; - struct sockaddr_nl from; - socklen_t fromlen; - struct nlmsghdr *h; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - fromlen = sizeof(from); - left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, - (struct sockaddr *) &from, &fromlen); - - if (left < 0) { - if (errno != EINTR && errno != EAGAIN) - perror("recvfrom(netlink)"); - return; - } - - h = (struct nlmsghdr *) buf; - wpa_hexdump(MSG_DEBUG, "h: ", (u8 *)h, h->nlmsg_len); - - while (left >= (int) sizeof(*h)) { - int len, plen; - - len = h->nlmsg_len; - plen = len - sizeof(*h); - if (len > left || plen < 0) { - wpa_printf(MSG_DEBUG, "Malformed netlink message: " - "len=%d left=%d plen=%d", len, left, plen); - break; - } - - switch (h->nlmsg_type) { - case RTM_NEWLINK: - wpa_driver_ralink_event_rtm_newlink(ctx, sock_ctx, h, - plen); - break; - } - - len = NLMSG_ALIGN(len); - left -= len; - h = (struct nlmsghdr *) ((char *) h + len); - } - - if (left > 0) { - wpa_printf(MSG_DEBUG, "%d extra bytes in the end of netlink " - "message", left); - } - -} - static int ralink_get_we_version_compiled(struct wpa_driver_ralink_data *drv) { @@ -862,45 +763,13 @@ ralink_get_we_version_compiled(struct wpa_driver_ralink_data *drv) return 0; } -static int -ralink_set_iface_flags(void *priv, int dev_up) -{ - struct wpa_driver_ralink_data *drv = priv; - struct ifreq ifr; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - if (drv->ioctl_sock < 0) - return -1; - - os_memset(&ifr, 0, sizeof(ifr)); - os_snprintf(ifr.ifr_name, IFNAMSIZ, "%s", drv->ifname); - - if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, &ifr) != 0) { - perror("ioctl[SIOCGIFFLAGS]"); - return -1; - } - - if (dev_up) - ifr.ifr_flags |= IFF_UP; - else - ifr.ifr_flags &= ~IFF_UP; - - if (ioctl(drv->ioctl_sock, SIOCSIFFLAGS, &ifr) != 0) { - perror("ioctl[SIOCSIFFLAGS]"); - return -1; - } - - return 0; -} - static void * wpa_driver_ralink_init(void *ctx, const char *ifname) { int s; struct wpa_driver_ralink_data *drv; struct ifreq ifr; - struct sockaddr_nl local; UCHAR enable_wpa_supplicant = 0; + struct netlink_config *cfg; wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); @@ -928,31 +797,25 @@ static void * wpa_driver_ralink_init(void *ctx, const char *ifname) drv->ioctl_sock = s; drv->g_driver_down = 0; - s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - if (s < 0) { - perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)"); + cfg = os_zalloc(sizeof(*cfg)); + if (cfg == NULL) { close(drv->ioctl_sock); os_free(drv); return NULL; } - - os_memset(&local, 0, sizeof(local)); - local.nl_family = AF_NETLINK; - local.nl_groups = RTMGRP_LINK; - - if (bind(s, (struct sockaddr *) &local, sizeof(local)) < 0) { - perror("bind(netlink)"); - close(s); + cfg->ctx = drv; + cfg->newlink_cb = wpa_driver_ralink_event_rtm_newlink; + drv->netlink = netlink_init(cfg); + if (drv->netlink == NULL) { + os_free(cfg); close(drv->ioctl_sock); os_free(drv); return NULL; } - eloop_register_read_sock(s, wpa_driver_ralink_event_receive, drv, ctx); - drv->event_sock = s; drv->no_of_pmkid = 4; /* Number of PMKID saved supported */ - ralink_set_iface_flags(drv, 1); /* mark up during setup */ + linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1); ralink_get_we_version_compiled(drv); wpa_driver_ralink_flush_pmkid(drv); @@ -1003,12 +866,11 @@ static void wpa_driver_ralink_deinit(void *priv) wpa_driver_ralink_flush_pmkid(drv); sleep(1); - ralink_set_iface_flags(drv, 0); + /* linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 0); */ } eloop_cancel_timeout(wpa_driver_ralink_scan_timeout, drv, drv->ctx); - eloop_unregister_read_sock(drv->event_sock); - close(drv->event_sock); + netlink_deinit(drv->netlink); close(drv->ioctl_sock); os_free(drv); } @@ -1026,7 +888,8 @@ static void wpa_driver_ralink_scan_timeout(void *eloop_ctx, void *timeout_ctx) } -static int wpa_driver_ralink_scan(void *priv, const u8 *ssid, size_t ssid_len) +static int wpa_driver_ralink_scan(void *priv, + struct wpa_driver_scan_params *params) { struct wpa_driver_ralink_data *drv = priv; struct iwreq iwr; @@ -1037,6 +900,7 @@ static int wpa_driver_ralink_scan(void *priv, const u8 *ssid, size_t ssid_len) wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); +#if 0 if (ssid_len > IW_ESSID_MAX_SIZE) { wpa_printf(MSG_DEBUG, "%s: too long SSID (%lu)", __FUNCTION__, (unsigned long) ssid_len); @@ -1044,6 +908,14 @@ static int wpa_driver_ralink_scan(void *priv, const u8 *ssid, size_t ssid_len) } /* wpa_driver_ralink_set_ssid(drv, ssid, ssid_len); */ +#endif + + if (ralink_set_oid(drv, RT_OID_WPS_PROBE_REQ_IE, + (char *) params->extra_ies, params->extra_ies_len) < + 0) { + wpa_printf(MSG_DEBUG, "RALINK: Failed to set " + "RT_OID_WPS_PROBE_REQ_IE"); + } os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); @@ -1064,96 +936,124 @@ static int wpa_driver_ralink_scan(void *priv, const u8 *ssid, size_t ssid_len) return ret; } -static int -wpa_driver_ralink_get_scan_results(void *priv, - struct wpa_scan_result *results, - size_t max_size) +static struct wpa_scan_results * +wpa_driver_ralink_get_scan_results(void *priv) { struct wpa_driver_ralink_data *drv = priv; UCHAR *buf = NULL; + size_t buf_len; NDIS_802_11_BSSID_LIST_EX *wsr; NDIS_WLAN_BSSID_EX *wbi; struct iwreq iwr; - int rv = 0; size_t ap_num; - u8 *pos, *end; + u8 *pos; + struct wpa_scan_results *res; if (drv->g_driver_down == 1) - return -1; + return NULL; wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - if (drv->we_version_compiled >= 17) { - buf = os_zalloc(8192); - iwr.u.data.length = 8192; - } else { - buf = os_zalloc(4096); - iwr.u.data.length = 4096; - } - if (buf == NULL) - return -1; + if (drv->we_version_compiled >= 17) + buf_len = 8192; + else + buf_len = 4096; - wsr = (NDIS_802_11_BSSID_LIST_EX *) buf; + for (;;) { + buf = os_zalloc(buf_len); + iwr.u.data.length = buf_len; + if (buf == NULL) + return NULL; - wsr->NumberOfItems = 0; - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - iwr.u.data.pointer = (void *) buf; - iwr.u.data.flags = OID_802_11_BSSID_LIST; + wsr = (NDIS_802_11_BSSID_LIST_EX *) buf; + + wsr->NumberOfItems = 0; + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = (void *) buf; + iwr.u.data.flags = OID_802_11_BSSID_LIST; + + if (ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr) == 0) + break; - if ((rv = ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr)) < 0) { - wpa_printf(MSG_DEBUG, "ioctl fail: rv = %d", rv); + if (errno == E2BIG && buf_len < 65535) { + os_free(buf); + buf = NULL; + buf_len *= 2; + if (buf_len > 65535) + buf_len = 65535; /* 16-bit length field */ + wpa_printf(MSG_DEBUG, "Scan results did not fit - " + "trying larger buffer (%lu bytes)", + (unsigned long) buf_len); + } else { + perror("ioctl[RT_PRIV_IOCTL]"); + os_free(buf); + return NULL; + } + } + + res = os_zalloc(sizeof(*res)); + if (res == NULL) { os_free(buf); - return -1; + return NULL; } - os_memset(results, 0, max_size * sizeof(struct wpa_scan_result)); + res->res = os_zalloc(wsr->NumberOfItems * + sizeof(struct wpa_scan_res *)); + if (res->res == NULL) { + os_free(res); + os_free(buf); + return NULL; + } for (ap_num = 0, wbi = wsr->Bssid; ap_num < wsr->NumberOfItems; ++ap_num) { - os_memcpy(results[ap_num].bssid, &wbi->MacAddress, ETH_ALEN); - os_memcpy(results[ap_num].ssid, wbi->Ssid.Ssid, - wbi->Ssid.SsidLength); - results[ap_num].ssid_len = wbi->Ssid.SsidLength; - results[ap_num].freq = (wbi->Configuration.DSConfig / 1000); + struct wpa_scan_res *r = NULL; + size_t extra_len = 0, var_ie_len = 0; + u8 *pos2; + + /* SSID data element */ + extra_len += 2 + wbi->Ssid.SsidLength; + var_ie_len = wbi->IELength - sizeof(NDIS_802_11_FIXED_IEs); + r = os_zalloc(sizeof(*r) + extra_len + var_ie_len); + if (r == NULL) + break; + res->res[res->num++] = r; + wpa_printf(MSG_DEBUG, "SSID - %s", wbi->Ssid.Ssid); /* get ie's */ wpa_hexdump(MSG_DEBUG, "RALINK: AP IEs", - (u8 *) wbi + sizeof(*wbi) - 1, wbi->IELength); + (u8 *) &wbi->IEs[0], wbi->IELength); - pos = (u8 *) wbi + sizeof(*wbi) - 1; - end = (u8 *) wbi + sizeof(*wbi) + wbi->IELength; + os_memcpy(r->bssid, wbi->MacAddress, ETH_ALEN); - if (wbi->IELength < sizeof(NDIS_802_11_FIXED_IEs)) - break; + extra_len += (2 + wbi->Ssid.SsidLength); + r->ie_len = extra_len + var_ie_len; + pos2 = (u8 *) (r + 1); - pos += sizeof(NDIS_802_11_FIXED_IEs) - 2; - os_memcpy(&results[ap_num].caps, pos, 2); - pos += 2; + /* + * Generate a fake SSID IE since the driver did not report + * a full IE list. + */ + *pos2++ = WLAN_EID_SSID; + *pos2++ = wbi->Ssid.SsidLength; + os_memcpy(pos2, wbi->Ssid.Ssid, wbi->Ssid.SsidLength); + pos2 += wbi->Ssid.SsidLength; - while (pos + 1 < end && pos + 2 + pos[1] <= end) { - u8 ielen = 2 + pos[1]; + r->freq = (wbi->Configuration.DSConfig / 1000); - if (ielen > SSID_MAX_WPA_IE_LEN) { - pos += ielen; - continue; - } + pos = (u8 *) wbi + sizeof(*wbi) - 1; - if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && - pos[1] >= 4 && - os_memcmp(pos + 2, "\x00\x50\xf2\x01", 4) == 0) { - os_memcpy(results[ap_num].wpa_ie, pos, ielen); - results[ap_num].wpa_ie_len = ielen; - } else if (pos[0] == WLAN_EID_RSN) { - os_memcpy(results[ap_num].rsn_ie, pos, ielen); - results[ap_num].rsn_ie_len = ielen; - } - pos += ielen; - } + pos += sizeof(NDIS_802_11_FIXED_IEs) - 2; + os_memcpy(&(r->caps), pos, 2); + pos += 2; + + if (wbi->IELength > sizeof(NDIS_802_11_FIXED_IEs)) + os_memcpy(pos2, pos, var_ie_len); wbi = (NDIS_WLAN_BSSID_EX *) ((u8 *) wbi + wbi->Length); } os_free(buf); - return ap_num; + return res; } static int ralink_set_auth_mode(struct wpa_driver_ralink_data *drv, @@ -1173,6 +1073,24 @@ static int ralink_set_auth_mode(struct wpa_driver_ralink_data *drv, return 0; } +static int ralink_set_encr_type(struct wpa_driver_ralink_data *drv, + NDIS_802_11_WEP_STATUS encr_type) +{ + NDIS_802_11_WEP_STATUS wep_status = encr_type; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + if (ralink_set_oid(drv, OID_802_11_WEP_STATUS, + (char *) &wep_status, sizeof(wep_status)) < 0) { + wpa_printf(MSG_DEBUG, "RALINK: Failed to set " + "OID_802_11_WEP_STATUS (%d)", + (int) wep_status); + return -1; + } + return 0; +} + + static int wpa_driver_ralink_remove_key(struct wpa_driver_ralink_data *drv, int key_idx, const u8 *addr, const u8 *bssid, int pairwise) @@ -1241,7 +1159,8 @@ static int wpa_driver_ralink_add_wep(struct wpa_driver_ralink_data *drv, return res; } -static int wpa_driver_ralink_set_key(void *priv, wpa_alg alg, const u8 *addr, +static int wpa_driver_ralink_set_key(const char *ifname, void *priv, + enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len) @@ -1257,6 +1176,8 @@ static int wpa_driver_ralink_set_key(void *priv, wpa_alg alg, const u8 *addr, wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + drv->bAddWepKey = FALSE; + if (addr == NULL || os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0) { /* Group Key */ @@ -1274,6 +1195,7 @@ static int wpa_driver_ralink_set_key(void *priv, wpa_alg alg, const u8 *addr, } if (alg == WPA_ALG_WEP) { + drv->bAddWepKey = TRUE; return wpa_driver_ralink_add_wep(drv, pairwise, key_idx, set_tx, key, key_len); } @@ -1363,6 +1285,29 @@ static int wpa_driver_ralink_deauthenticate(void *priv, const u8 *addr, } } +static int wpa_driver_ralink_set_gen_ie(void *priv, const u8 *ie, + size_t ie_len) +{ + struct wpa_driver_ralink_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) ie; + iwr.u.data.length = ie_len; + + wpa_hexdump(MSG_DEBUG, "wpa_driver_ralink_set_gen_ie: ", + (u8 *) ie, ie_len); + + if (ioctl(drv->ioctl_sock, SIOCSIWGENIE, &iwr) < 0) { + perror("ioctl[SIOCSIWGENIE]"); + ret = -1; + } + + return ret; +} + static int wpa_driver_ralink_associate(void *priv, struct wpa_driver_associate_params *params) @@ -1373,6 +1318,7 @@ wpa_driver_ralink_associate(void *priv, NDIS_802_11_AUTHENTICATION_MODE auth_mode; NDIS_802_11_WEP_STATUS encr; BOOLEAN ieee8021xMode; + BOOLEAN ieee8021x_required_key = TRUE; if (drv->g_driver_down == 1) return -1; @@ -1391,83 +1337,131 @@ wpa_driver_ralink_associate(void *priv, /* Try to continue anyway */ } - if (params->wpa_ie == NULL || params->wpa_ie_len == 0) { - if (params->auth_alg & AUTH_ALG_SHARED_KEY) { - if (params->auth_alg & AUTH_ALG_OPEN_SYSTEM) - auth_mode = Ndis802_11AuthModeAutoSwitch; - else - auth_mode = Ndis802_11AuthModeShared; - } else - auth_mode = Ndis802_11AuthModeOpen; - } else if (params->wpa_ie[0] == WLAN_EID_RSN) { - if (params->key_mgmt_suite == KEY_MGMT_PSK) - auth_mode = Ndis802_11AuthModeWPA2PSK; - else - auth_mode = Ndis802_11AuthModeWPA2; + if (params->key_mgmt_suite == KEY_MGMT_WPS) { + UCHAR enable_wps = 0x80; + /* trigger driver support wpa_supplicant */ + if (ralink_set_oid(drv, RT_OID_WPA_SUPPLICANT_SUPPORT, + (PCHAR) &enable_wps, sizeof(UCHAR)) < 0) { + wpa_printf(MSG_INFO, "RALINK: Failed to set " + "RT_OID_WPA_SUPPLICANT_SUPPORT (%d)", + (int) enable_wps); + } + + wpa_driver_ralink_set_gen_ie(priv, params->wpa_ie, + params->wpa_ie_len); + + ralink_set_auth_mode(drv, Ndis802_11AuthModeOpen); + + ralink_set_encr_type(drv, Ndis802_11EncryptionDisabled); } else { - if (params->key_mgmt_suite == KEY_MGMT_WPA_NONE) - auth_mode = Ndis802_11AuthModeWPANone; - else if (params->key_mgmt_suite == KEY_MGMT_PSK) - auth_mode = Ndis802_11AuthModeWPAPSK; +#ifdef CONFIG_WPS + UCHAR enable_wpa_supplicant; + + if (drv->ap_scan == 1) + enable_wpa_supplicant = 0x01; else - auth_mode = Ndis802_11AuthModeWPA; - } + enable_wpa_supplicant = 0x02; + + /* trigger driver support wpa_supplicant */ + if (ralink_set_oid(drv, RT_OID_WPA_SUPPLICANT_SUPPORT, + (PCHAR) &enable_wpa_supplicant, + sizeof(UCHAR)) < 0) { + wpa_printf(MSG_DEBUG, "RALINK: Failed to set " + "RT_OID_WPA_SUPPLICANT_SUPPORT (%d)", + (int) enable_wpa_supplicant); + } + + wpa_driver_ralink_set_gen_ie(priv, (u8 *) "", 0); +#endif /* CONFIG_WPS */ + + if (params->wpa_ie == NULL || params->wpa_ie_len == 0) { + if (params->auth_alg & WPA_AUTH_ALG_SHARED) { + if (params->auth_alg & WPA_AUTH_ALG_OPEN) + auth_mode = Ndis802_11AuthModeAutoSwitch; + else + auth_mode = Ndis802_11AuthModeShared; + } else + auth_mode = Ndis802_11AuthModeOpen; + } else if (params->wpa_ie[0] == WLAN_EID_RSN) { + if (params->key_mgmt_suite == KEY_MGMT_PSK) + auth_mode = Ndis802_11AuthModeWPA2PSK; + else + auth_mode = Ndis802_11AuthModeWPA2; + } else { + if (params->key_mgmt_suite == KEY_MGMT_WPA_NONE) + auth_mode = Ndis802_11AuthModeWPANone; + else if (params->key_mgmt_suite == KEY_MGMT_PSK) + auth_mode = Ndis802_11AuthModeWPAPSK; + else + auth_mode = Ndis802_11AuthModeWPA; + } - switch (params->pairwise_suite) { - case CIPHER_CCMP: - encr = Ndis802_11Encryption3Enabled; - break; - case CIPHER_TKIP: - encr = Ndis802_11Encryption2Enabled; - break; - case CIPHER_WEP40: - case CIPHER_WEP104: - encr = Ndis802_11Encryption1Enabled; - break; - case CIPHER_NONE: - if (params->group_suite == CIPHER_CCMP) + switch (params->pairwise_suite) { + case CIPHER_CCMP: encr = Ndis802_11Encryption3Enabled; - else if (params->group_suite == CIPHER_TKIP) + break; + case CIPHER_TKIP: encr = Ndis802_11Encryption2Enabled; - else + break; + case CIPHER_WEP40: + case CIPHER_WEP104: + encr = Ndis802_11Encryption1Enabled; + break; + case CIPHER_NONE: + if (params->group_suite == CIPHER_CCMP) + encr = Ndis802_11Encryption3Enabled; + else if (params->group_suite == CIPHER_TKIP) + encr = Ndis802_11Encryption2Enabled; + else + encr = Ndis802_11EncryptionDisabled; + break; + default: encr = Ndis802_11EncryptionDisabled; - break; - default: - encr = Ndis802_11EncryptionDisabled; - break; - } - - ralink_set_auth_mode(drv, auth_mode); + break; + } - /* notify driver that IEEE8021x mode is enabled */ - if (params->key_mgmt_suite == KEY_MGMT_802_1X_NO_WPA) - ieee8021xMode = TRUE; - else - ieee8021xMode = FALSE; + ralink_set_auth_mode(drv, auth_mode); - if (ralink_set_oid(drv, OID_802_11_SET_IEEE8021X, - (char *) &ieee8021xMode, sizeof(BOOLEAN)) < 0) { - wpa_printf(MSG_DEBUG, "RALINK: Failed to set " - "OID_802_11_SET_IEEE8021X(%d)", - (int) ieee8021xMode); - } + /* notify driver that IEEE8021x mode is enabled */ + if (params->key_mgmt_suite == KEY_MGMT_802_1X_NO_WPA) { + ieee8021xMode = TRUE; + if (drv->bAddWepKey) + ieee8021x_required_key = FALSE; + } else + ieee8021xMode = FALSE; - if (ralink_set_oid(drv, OID_802_11_WEP_STATUS, - (char *) &encr, sizeof(encr)) < 0) { - wpa_printf(MSG_DEBUG, "RALINK: Failed to set " - "OID_802_11_WEP_STATUS(%d)", - (int) encr); - } + if (ralink_set_oid(drv, OID_802_11_SET_IEEE8021X_REQUIRE_KEY, + (char *) &ieee8021x_required_key, + sizeof(BOOLEAN)) < 0) { + wpa_printf(MSG_DEBUG, "ERROR: Failed to set " + "OID_802_11_SET_IEEE8021X_REQUIRE_KEY(%d)", + (int) ieee8021x_required_key); + } else { + wpa_printf(MSG_DEBUG, "ieee8021x_required_key is %s", + ieee8021x_required_key ? "TRUE" : "FALSE"); + } - if ((ieee8021xMode == FALSE) && - (encr == Ndis802_11Encryption1Enabled)) { - /* static WEP */ - int enabled = 0; - if (ralink_set_oid(drv, OID_802_11_DROP_UNENCRYPTED, - (char *) &enabled, sizeof(enabled)) < 0) { + if (ralink_set_oid(drv, OID_802_11_SET_IEEE8021X, + (char *) &ieee8021xMode, sizeof(BOOLEAN)) < + 0) { wpa_printf(MSG_DEBUG, "RALINK: Failed to set " - "OID_802_11_DROP_UNENCRYPTED(%d)", - (int) encr); + "OID_802_11_SET_IEEE8021X(%d)", + (int) ieee8021xMode); + } + + ralink_set_encr_type(drv, encr); + + if ((ieee8021xMode == FALSE) && + (encr == Ndis802_11Encryption1Enabled)) { + /* static WEP */ + int enabled = 0; + if (ralink_set_oid(drv, OID_802_11_DROP_UNENCRYPTED, + (char *) &enabled, sizeof(enabled)) + < 0) { + wpa_printf(MSG_DEBUG, "RALINK: Failed to set " + "OID_802_11_DROP_UNENCRYPTED(%d)", + (int) encr); + } } } @@ -1494,8 +1488,8 @@ const struct wpa_driver_ops wpa_driver_ralink_ops = { .init = wpa_driver_ralink_init, .deinit = wpa_driver_ralink_deinit, .set_countermeasures = wpa_driver_ralink_set_countermeasures, - .scan = wpa_driver_ralink_scan, - .get_scan_results = wpa_driver_ralink_get_scan_results, + .scan2 = wpa_driver_ralink_scan, + .get_scan_results2 = wpa_driver_ralink_get_scan_results, .deauthenticate = wpa_driver_ralink_deauthenticate, .disassociate = wpa_driver_ralink_disassociate, .associate = wpa_driver_ralink_associate, diff --git a/src/drivers/driver_ralink.h b/src/drivers/driver_ralink.h index ddf44de232a3b..d13df28de4564 100644 --- a/src/drivers/driver_ralink.h +++ b/src/drivers/driver_ralink.h @@ -54,6 +54,7 @@ #define RT_OID_WPA_SUPPLICANT_SUPPORT 0x0621 // for trigger driver enable/disable wpa_supplicant support #define RT_OID_WE_VERSION_COMPILED 0x0622 #define RT_OID_NEW_DRIVER 0x0623 +#define RT_OID_WPS_PROBE_REQ_IE 0x0625 #define PACKED __attribute__ ((packed)) @@ -65,7 +66,7 @@ #define RT_ASSOCINFO_EVENT_FLAG 0x0105 #define RT_PMKIDCAND_FLAG 0x0106 #define RT_INTERFACE_DOWN 0x0107 -#define RT_REPORT_AP_INFO 0x0108 +#define RT_INTERFACE_UP 0x0108 // // IEEE 802.11 Structures and definitions diff --git a/src/drivers/driver_roboswitch.c b/src/drivers/driver_roboswitch.c index bc11a48484326..6877eda3ed22c 100644 --- a/src/drivers/driver_roboswitch.c +++ b/src/drivers/driver_roboswitch.c @@ -23,10 +23,6 @@ #include "driver.h" #include "l2_packet/l2_packet.h" -#ifndef ETH_P_EAPOL -#define ETH_P_EAPOL 0x888e -#endif - #define ROBO_PHY_ADDR 0x1e /* RoboSwitch PHY address */ /* MII access registers */ @@ -183,10 +179,8 @@ static void wpa_driver_roboswitch_receive(void *priv, const u8 *src_addr, struct wpa_driver_roboswitch_data *drv = priv; if (len > 14 && WPA_GET_BE16(buf + 12) == ETH_P_EAPOL && - os_memcmp(buf, drv->own_addr, ETH_ALEN) == 0) { - wpa_supplicant_rx_eapol(drv->ctx, src_addr, buf + 14, - len - 14); - } + os_memcmp(buf, drv->own_addr, ETH_ALEN) == 0) + drv_event_eapol_rx(drv->ctx, src_addr, buf + 14, len - 14); } @@ -205,6 +199,15 @@ static int wpa_driver_roboswitch_get_bssid(void *priv, u8 *bssid) } +static int wpa_driver_roboswitch_get_capa(void *priv, + struct wpa_driver_capa *capa) +{ + os_memset(capa, 0, sizeof(*capa)); + capa->flags = WPA_DRIVER_FLAGS_WIRED; + return 0; +} + + static int wpa_driver_roboswitch_set_param(void *priv, const char *param) { struct wpa_driver_roboswitch_data *drv = priv; @@ -469,6 +472,7 @@ const struct wpa_driver_ops wpa_driver_roboswitch_ops = { .desc = "wpa_supplicant roboswitch driver", .get_ssid = wpa_driver_roboswitch_get_ssid, .get_bssid = wpa_driver_roboswitch_get_bssid, + .get_capa = wpa_driver_roboswitch_get_capa, .init = wpa_driver_roboswitch_init, .deinit = wpa_driver_roboswitch_deinit, .set_param = wpa_driver_roboswitch_set_param, diff --git a/src/drivers/driver_test.c b/src/drivers/driver_test.c index 2a41cf260a5a9..fb2467350f1ee 100644 --- a/src/drivers/driver_test.c +++ b/src/drivers/driver_test.c @@ -1,6 +1,6 @@ /* - * WPA Supplicant - testing driver interface - * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * Testing driver interface for a simulated network driver + * Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -12,13 +12,13 @@ * See README and COPYING for more details. */ -/* Make dure we get winsock2.h for Windows build to get sockaddr_storage */ +/* Make sure we get winsock2.h for Windows build to get sockaddr_storage */ #include "build_config.h" #ifdef CONFIG_NATIVE_WINDOWS #include <winsock2.h> #endif /* CONFIG_NATIVE_WINDOWS */ -#include "includes.h" +#include "utils/includes.h" #ifndef CONFIG_NATIVE_WINDOWS #include <sys/un.h> @@ -27,21 +27,50 @@ #define DRIVER_TEST_UNIX #endif /* CONFIG_NATIVE_WINDOWS */ -#include "common.h" -#include "driver.h" +#include "utils/common.h" +#include "utils/eloop.h" +#include "utils/list.h" +#include "utils/trace.h" +#include "common/ieee802_11_defs.h" +#include "crypto/sha1.h" #include "l2_packet/l2_packet.h" -#include "eloop.h" -#include "sha1.h" -#include "ieee802_11_defs.h" +#include "driver.h" +struct test_client_socket { + struct test_client_socket *next; + u8 addr[ETH_ALEN]; + struct sockaddr_un un; + socklen_t unlen; + struct test_driver_bss *bss; +}; + +struct test_driver_bss { + struct wpa_driver_test_data *drv; + struct dl_list list; + void *bss_ctx; + char ifname[IFNAMSIZ]; + u8 bssid[ETH_ALEN]; + u8 *ie; + size_t ielen; + u8 *wps_beacon_ie; + size_t wps_beacon_ie_len; + u8 *wps_probe_resp_ie; + size_t wps_probe_resp_ie_len; + u8 ssid[32]; + size_t ssid_len; + int privacy; +}; + struct wpa_driver_test_global { - int dummy; + int bss_add_used; + u8 req_addr[ETH_ALEN]; }; struct wpa_driver_test_data { struct wpa_driver_test_global *global; void *ctx; + WPA_TRACE_REF(ctx); u8 own_addr[ETH_ALEN]; int test_socket; #ifdef DRIVER_TEST_UNIX @@ -52,9 +81,6 @@ struct wpa_driver_test_data { int hostapd_addr_udp_set; char *own_socket_path; char *test_dir; - u8 bssid[ETH_ALEN]; - u8 ssid[32]; - size_t ssid_len; #define MAX_SCAN_RESULTS 30 struct wpa_scan_res *scanres[MAX_SCAN_RESULTS]; size_t num_scanres; @@ -65,9 +91,1164 @@ struct wpa_driver_test_data { int associated; u8 *probe_req_ie; size_t probe_req_ie_len; + u8 probe_req_ssid[32]; + size_t probe_req_ssid_len; + int ibss; + int ap; + + struct test_client_socket *cli; + struct dl_list bss; + int udp_port; + + int alloc_iface_idx; + + int probe_req_report; + unsigned int remain_on_channel_freq; + unsigned int remain_on_channel_duration; + + int current_freq; }; +static void wpa_driver_test_deinit(void *priv); +static int wpa_driver_test_attach(struct wpa_driver_test_data *drv, + const char *dir, int ap); +static void wpa_driver_test_close_test_socket( + struct wpa_driver_test_data *drv); +static void test_remain_on_channel_timeout(void *eloop_ctx, void *timeout_ctx); + + +static void test_driver_free_bss(struct test_driver_bss *bss) +{ + os_free(bss->ie); + os_free(bss->wps_beacon_ie); + os_free(bss->wps_probe_resp_ie); + os_free(bss); +} + + +static void test_driver_free_bsses(struct wpa_driver_test_data *drv) +{ + struct test_driver_bss *bss, *tmp; + + dl_list_for_each_safe(bss, tmp, &drv->bss, struct test_driver_bss, + list) { + dl_list_del(&bss->list); + test_driver_free_bss(bss); + } +} + + +static struct test_client_socket * +test_driver_get_cli(struct wpa_driver_test_data *drv, struct sockaddr_un *from, + socklen_t fromlen) +{ + struct test_client_socket *cli = drv->cli; + + while (cli) { + if (cli->unlen == fromlen && + strncmp(cli->un.sun_path, from->sun_path, + fromlen - sizeof(cli->un.sun_family)) == 0) + return cli; + cli = cli->next; + } + + return NULL; +} + + +static int test_driver_send_eapol(void *priv, const u8 *addr, const u8 *data, + size_t data_len, int encrypt, + const u8 *own_addr) +{ + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; + struct test_client_socket *cli; + struct msghdr msg; + struct iovec io[3]; + struct l2_ethhdr eth; + + if (drv->test_socket < 0) + return -1; + + cli = drv->cli; + while (cli) { + if (memcmp(cli->addr, addr, ETH_ALEN) == 0) + break; + cli = cli->next; + } + + if (!cli) { + wpa_printf(MSG_DEBUG, "%s: no destination client entry", + __func__); + return -1; + } + + memcpy(eth.h_dest, addr, ETH_ALEN); + memcpy(eth.h_source, own_addr, ETH_ALEN); + eth.h_proto = host_to_be16(ETH_P_EAPOL); + + io[0].iov_base = "EAPOL "; + io[0].iov_len = 6; + io[1].iov_base = ð + io[1].iov_len = sizeof(eth); + io[2].iov_base = (u8 *) data; + io[2].iov_len = data_len; + + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = io; + msg.msg_iovlen = 3; + msg.msg_name = &cli->un; + msg.msg_namelen = cli->unlen; + return sendmsg(drv->test_socket, &msg, 0); +} + + +static int test_driver_send_ether(void *priv, const u8 *dst, const u8 *src, + u16 proto, const u8 *data, size_t data_len) +{ + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; + struct msghdr msg; + struct iovec io[3]; + struct l2_ethhdr eth; + char desttxt[30]; + struct sockaddr_un addr; + struct dirent *dent; + DIR *dir; + int ret = 0, broadcast = 0, count = 0; + + if (drv->test_socket < 0 || drv->test_dir == NULL) { + wpa_printf(MSG_DEBUG, "%s: invalid parameters (sock=%d " + "test_dir=%p)", + __func__, drv->test_socket, drv->test_dir); + return -1; + } + + broadcast = memcmp(dst, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0; + snprintf(desttxt, sizeof(desttxt), MACSTR, MAC2STR(dst)); + + memcpy(eth.h_dest, dst, ETH_ALEN); + memcpy(eth.h_source, src, ETH_ALEN); + eth.h_proto = host_to_be16(proto); + + io[0].iov_base = "ETHER "; + io[0].iov_len = 6; + io[1].iov_base = ð + io[1].iov_len = sizeof(eth); + io[2].iov_base = (u8 *) data; + io[2].iov_len = data_len; + + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = io; + msg.msg_iovlen = 3; + + dir = opendir(drv->test_dir); + if (dir == NULL) { + perror("test_driver: opendir"); + return -1; + } + while ((dent = readdir(dir))) { +#ifdef _DIRENT_HAVE_D_TYPE + /* Skip the file if it is not a socket. Also accept + * DT_UNKNOWN (0) in case the C library or underlying file + * system does not support d_type. */ + if (dent->d_type != DT_SOCK && dent->d_type != DT_UNKNOWN) + continue; +#endif /* _DIRENT_HAVE_D_TYPE */ + if (strcmp(dent->d_name, ".") == 0 || + strcmp(dent->d_name, "..") == 0) + continue; + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s", + drv->test_dir, dent->d_name); + + if (strcmp(addr.sun_path, drv->own_socket_path) == 0) + continue; + if (!broadcast && strstr(dent->d_name, desttxt) == NULL) + continue; + + wpa_printf(MSG_DEBUG, "%s: Send ether frame to %s", + __func__, dent->d_name); + + msg.msg_name = &addr; + msg.msg_namelen = sizeof(addr); + ret = sendmsg(drv->test_socket, &msg, 0); + if (ret < 0) + perror("driver_test: sendmsg"); + count++; + } + closedir(dir); + + if (!broadcast && count == 0) { + wpa_printf(MSG_DEBUG, "%s: Destination " MACSTR " not found", + __func__, MAC2STR(dst)); + return -1; + } + + return ret; +} + + +static int wpa_driver_test_send_mlme(void *priv, const u8 *data, + size_t data_len) +{ + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; + struct msghdr msg; + struct iovec io[2]; + const u8 *dest; + struct sockaddr_un addr; + struct dirent *dent; + DIR *dir; + int broadcast; + int ret = 0; + struct ieee80211_hdr *hdr; + u16 fc; + char cmd[50]; + int freq; +#ifdef HOSTAPD + char desttxt[30]; +#endif /* HOSTAPD */ + union wpa_event_data event; + + wpa_hexdump(MSG_MSGDUMP, "test_send_mlme", data, data_len); + if (drv->test_socket < 0 || data_len < 10) { + wpa_printf(MSG_DEBUG, "%s: invalid parameters (sock=%d len=%lu" + " test_dir=%p)", + __func__, drv->test_socket, + (unsigned long) data_len, + drv->test_dir); + return -1; + } + + dest = data + 4; + broadcast = os_memcmp(dest, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0; + +#ifdef HOSTAPD + snprintf(desttxt, sizeof(desttxt), MACSTR, MAC2STR(dest)); +#endif /* HOSTAPD */ + + if (drv->remain_on_channel_freq) + freq = drv->remain_on_channel_freq; + else + freq = drv->current_freq; + wpa_printf(MSG_DEBUG, "test_driver(%s): MLME TX on freq %d MHz", + dbss->ifname, freq); + os_snprintf(cmd, sizeof(cmd), "MLME freq=%d ", freq); + io[0].iov_base = cmd; + io[0].iov_len = os_strlen(cmd); + io[1].iov_base = (void *) data; + io[1].iov_len = data_len; + + os_memset(&msg, 0, sizeof(msg)); + msg.msg_iov = io; + msg.msg_iovlen = 2; + +#ifdef HOSTAPD + if (drv->test_dir == NULL) { + wpa_printf(MSG_DEBUG, "%s: test_dir == NULL", __func__); + return -1; + } + + dir = opendir(drv->test_dir); + if (dir == NULL) { + perror("test_driver: opendir"); + return -1; + } + while ((dent = readdir(dir))) { +#ifdef _DIRENT_HAVE_D_TYPE + /* Skip the file if it is not a socket. Also accept + * DT_UNKNOWN (0) in case the C library or underlying file + * system does not support d_type. */ + if (dent->d_type != DT_SOCK && dent->d_type != DT_UNKNOWN) + continue; +#endif /* _DIRENT_HAVE_D_TYPE */ + if (os_strcmp(dent->d_name, ".") == 0 || + os_strcmp(dent->d_name, "..") == 0) + continue; + + os_memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + os_snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s", + drv->test_dir, dent->d_name); + + if (os_strcmp(addr.sun_path, drv->own_socket_path) == 0) + continue; + if (!broadcast && os_strstr(dent->d_name, desttxt) == NULL) + continue; + + wpa_printf(MSG_DEBUG, "%s: Send management frame to %s", + __func__, dent->d_name); + + msg.msg_name = &addr; + msg.msg_namelen = sizeof(addr); + ret = sendmsg(drv->test_socket, &msg, 0); + if (ret < 0) + perror("driver_test: sendmsg(test_socket)"); + } + closedir(dir); +#else /* HOSTAPD */ + + if (os_memcmp(dest, dbss->bssid, ETH_ALEN) == 0 || + drv->test_dir == NULL) { + if (drv->hostapd_addr_udp_set) { + msg.msg_name = &drv->hostapd_addr_udp; + msg.msg_namelen = sizeof(drv->hostapd_addr_udp); + } else { +#ifdef DRIVER_TEST_UNIX + msg.msg_name = &drv->hostapd_addr; + msg.msg_namelen = sizeof(drv->hostapd_addr); +#endif /* DRIVER_TEST_UNIX */ + } + } else if (broadcast) { + dir = opendir(drv->test_dir); + if (dir == NULL) + return -1; + while ((dent = readdir(dir))) { +#ifdef _DIRENT_HAVE_D_TYPE + /* Skip the file if it is not a socket. + * Also accept DT_UNKNOWN (0) in case + * the C library or underlying file + * system does not support d_type. */ + if (dent->d_type != DT_SOCK && + dent->d_type != DT_UNKNOWN) + continue; +#endif /* _DIRENT_HAVE_D_TYPE */ + if (os_strcmp(dent->d_name, ".") == 0 || + os_strcmp(dent->d_name, "..") == 0) + continue; + wpa_printf(MSG_DEBUG, "%s: Send broadcast MLME to %s", + __func__, dent->d_name); + os_memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + os_snprintf(addr.sun_path, sizeof(addr.sun_path), + "%s/%s", drv->test_dir, dent->d_name); + + msg.msg_name = &addr; + msg.msg_namelen = sizeof(addr); + + ret = sendmsg(drv->test_socket, &msg, 0); + if (ret < 0) + perror("driver_test: sendmsg(test_socket)"); + } + closedir(dir); + return ret; + } else { + struct stat st; + os_memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + os_snprintf(addr.sun_path, sizeof(addr.sun_path), + "%s/AP-" MACSTR, drv->test_dir, MAC2STR(dest)); + if (stat(addr.sun_path, &st) < 0) { + os_snprintf(addr.sun_path, sizeof(addr.sun_path), + "%s/STA-" MACSTR, + drv->test_dir, MAC2STR(dest)); + } + msg.msg_name = &addr; + msg.msg_namelen = sizeof(addr); + } + + if (sendmsg(drv->test_socket, &msg, 0) < 0) { + perror("sendmsg(test_socket)"); + return -1; + } +#endif /* HOSTAPD */ + + hdr = (struct ieee80211_hdr *) data; + fc = le_to_host16(hdr->frame_control); + + os_memset(&event, 0, sizeof(event)); + event.tx_status.type = WLAN_FC_GET_TYPE(fc); + event.tx_status.stype = WLAN_FC_GET_STYPE(fc); + event.tx_status.dst = hdr->addr1; + event.tx_status.data = data; + event.tx_status.data_len = data_len; + event.tx_status.ack = ret >= 0; + wpa_supplicant_event(drv->ctx, EVENT_TX_STATUS, &event); + + return ret; +} + + +static void test_driver_scan(struct wpa_driver_test_data *drv, + struct sockaddr_un *from, socklen_t fromlen, + char *data) +{ + char buf[512], *pos, *end; + int ret; + struct test_driver_bss *bss; + u8 sa[ETH_ALEN]; + u8 ie[512]; + size_t ielen; + union wpa_event_data event; + + /* data: optional [ ' ' | STA-addr | ' ' | IEs(hex) ] */ + + wpa_printf(MSG_DEBUG, "test_driver: SCAN"); + + if (*data) { + if (*data != ' ' || + hwaddr_aton(data + 1, sa)) { + wpa_printf(MSG_DEBUG, "test_driver: Unexpected SCAN " + "command format"); + return; + } + + data += 18; + while (*data == ' ') + data++; + ielen = os_strlen(data) / 2; + if (ielen > sizeof(ie)) + ielen = sizeof(ie); + if (hexstr2bin(data, ie, ielen) < 0) + ielen = 0; + + wpa_printf(MSG_DEBUG, "test_driver: Scan from " MACSTR, + MAC2STR(sa)); + wpa_hexdump(MSG_MSGDUMP, "test_driver: scan IEs", ie, ielen); + + os_memset(&event, 0, sizeof(event)); + event.rx_probe_req.sa = sa; + event.rx_probe_req.ie = ie; + event.rx_probe_req.ie_len = ielen; + wpa_supplicant_event(drv->ctx, EVENT_RX_PROBE_REQ, &event); + } + + dl_list_for_each(bss, &drv->bss, struct test_driver_bss, list) { + pos = buf; + end = buf + sizeof(buf); + + /* reply: SCANRESP BSSID SSID IEs */ + ret = snprintf(pos, end - pos, "SCANRESP " MACSTR " ", + MAC2STR(bss->bssid)); + if (ret < 0 || ret >= end - pos) + return; + pos += ret; + pos += wpa_snprintf_hex(pos, end - pos, + bss->ssid, bss->ssid_len); + ret = snprintf(pos, end - pos, " "); + if (ret < 0 || ret >= end - pos) + return; + pos += ret; + pos += wpa_snprintf_hex(pos, end - pos, bss->ie, bss->ielen); + pos += wpa_snprintf_hex(pos, end - pos, bss->wps_probe_resp_ie, + bss->wps_probe_resp_ie_len); + + if (bss->privacy) { + ret = snprintf(pos, end - pos, " PRIVACY"); + if (ret < 0 || ret >= end - pos) + return; + pos += ret; + } + + sendto(drv->test_socket, buf, pos - buf, 0, + (struct sockaddr *) from, fromlen); + } +} + + +static void test_driver_assoc(struct wpa_driver_test_data *drv, + struct sockaddr_un *from, socklen_t fromlen, + char *data) +{ + struct test_client_socket *cli; + u8 ie[256], ssid[32]; + size_t ielen, ssid_len = 0; + char *pos, *pos2, cmd[50]; + struct test_driver_bss *bss, *tmp; + + /* data: STA-addr SSID(hex) IEs(hex) */ + + cli = os_zalloc(sizeof(*cli)); + if (cli == NULL) + return; + + if (hwaddr_aton(data, cli->addr)) { + printf("test_socket: Invalid MAC address '%s' in ASSOC\n", + data); + os_free(cli); + return; + } + pos = data + 17; + while (*pos == ' ') + pos++; + pos2 = strchr(pos, ' '); + ielen = 0; + if (pos2) { + ssid_len = (pos2 - pos) / 2; + if (hexstr2bin(pos, ssid, ssid_len) < 0) { + wpa_printf(MSG_DEBUG, "%s: Invalid SSID", __func__); + os_free(cli); + return; + } + wpa_hexdump_ascii(MSG_DEBUG, "test_driver_assoc: SSID", + ssid, ssid_len); + + pos = pos2 + 1; + ielen = strlen(pos) / 2; + if (ielen > sizeof(ie)) + ielen = sizeof(ie); + if (hexstr2bin(pos, ie, ielen) < 0) + ielen = 0; + } + + bss = NULL; + dl_list_for_each(tmp, &drv->bss, struct test_driver_bss, list) { + if (tmp->ssid_len == ssid_len && + os_memcmp(tmp->ssid, ssid, ssid_len) == 0) { + bss = tmp; + break; + } + } + if (bss == NULL) { + wpa_printf(MSG_DEBUG, "%s: No matching SSID found from " + "configured BSSes", __func__); + os_free(cli); + return; + } + + cli->bss = bss; + memcpy(&cli->un, from, sizeof(cli->un)); + cli->unlen = fromlen; + cli->next = drv->cli; + drv->cli = cli; + wpa_hexdump_ascii(MSG_DEBUG, "test_socket: ASSOC sun_path", + (const u8 *) cli->un.sun_path, + cli->unlen - sizeof(cli->un.sun_family)); + + snprintf(cmd, sizeof(cmd), "ASSOCRESP " MACSTR " 0", + MAC2STR(bss->bssid)); + sendto(drv->test_socket, cmd, strlen(cmd), 0, + (struct sockaddr *) from, fromlen); + + drv_event_assoc(bss->bss_ctx, cli->addr, ie, ielen); +} + + +static void test_driver_disassoc(struct wpa_driver_test_data *drv, + struct sockaddr_un *from, socklen_t fromlen) +{ + struct test_client_socket *cli; + + cli = test_driver_get_cli(drv, from, fromlen); + if (!cli) + return; + + drv_event_disassoc(drv->ctx, cli->addr); +} + + +static void test_driver_eapol(struct wpa_driver_test_data *drv, + struct sockaddr_un *from, socklen_t fromlen, + u8 *data, size_t datalen) +{ +#ifdef HOSTAPD + struct test_client_socket *cli; +#endif /* HOSTAPD */ + const u8 *src = NULL; + + if (datalen > 14) { + /* Skip Ethernet header */ + src = data + ETH_ALEN; + wpa_printf(MSG_DEBUG, "test_driver: dst=" MACSTR " src=" + MACSTR " proto=%04x", + MAC2STR(data), MAC2STR(src), + WPA_GET_BE16(data + 2 * ETH_ALEN)); + data += 14; + datalen -= 14; + } + +#ifdef HOSTAPD + cli = test_driver_get_cli(drv, from, fromlen); + if (cli) { + drv_event_eapol_rx(cli->bss->bss_ctx, cli->addr, data, + datalen); + } else { + wpa_printf(MSG_DEBUG, "test_socket: EAPOL from unknown " + "client"); + } +#else /* HOSTAPD */ + if (src) + drv_event_eapol_rx(drv->ctx, src, data, datalen); +#endif /* HOSTAPD */ +} + + +static void test_driver_ether(struct wpa_driver_test_data *drv, + struct sockaddr_un *from, socklen_t fromlen, + u8 *data, size_t datalen) +{ + struct l2_ethhdr *eth; + + if (datalen < sizeof(*eth)) + return; + + eth = (struct l2_ethhdr *) data; + wpa_printf(MSG_DEBUG, "test_driver: RX ETHER dst=" MACSTR " src=" + MACSTR " proto=%04x", + MAC2STR(eth->h_dest), MAC2STR(eth->h_source), + be_to_host16(eth->h_proto)); + +#ifdef CONFIG_IEEE80211R + if (be_to_host16(eth->h_proto) == ETH_P_RRB) { + union wpa_event_data ev; + os_memset(&ev, 0, sizeof(ev)); + ev.ft_rrb_rx.src = eth->h_source; + ev.ft_rrb_rx.data = data + sizeof(*eth); + ev.ft_rrb_rx.data_len = datalen - sizeof(*eth); + } +#endif /* CONFIG_IEEE80211R */ +} + + +static void test_driver_mlme(struct wpa_driver_test_data *drv, + struct sockaddr_un *from, socklen_t fromlen, + u8 *data, size_t datalen) +{ + struct ieee80211_hdr *hdr; + u16 fc; + union wpa_event_data event; + int freq = 0, own_freq; + struct test_driver_bss *bss; + + bss = dl_list_first(&drv->bss, struct test_driver_bss, list); + + if (datalen > 6 && os_memcmp(data, "freq=", 5) == 0) { + size_t pos; + for (pos = 5; pos < datalen; pos++) { + if (data[pos] == ' ') + break; + } + if (pos < datalen) { + freq = atoi((const char *) &data[5]); + wpa_printf(MSG_DEBUG, "test_driver(%s): MLME RX on " + "freq %d MHz", bss->ifname, freq); + pos++; + data += pos; + datalen -= pos; + } + } + + if (drv->remain_on_channel_freq) + own_freq = drv->remain_on_channel_freq; + else + own_freq = drv->current_freq; + + if (freq && own_freq && freq != own_freq) { + wpa_printf(MSG_DEBUG, "test_driver(%s): Ignore MLME RX on " + "another frequency %d MHz (own %d MHz)", + bss->ifname, freq, own_freq); + return; + } + + hdr = (struct ieee80211_hdr *) data; + + if (test_driver_get_cli(drv, from, fromlen) == NULL && datalen >= 16) { + struct test_client_socket *cli; + cli = os_zalloc(sizeof(*cli)); + if (cli == NULL) + return; + wpa_printf(MSG_DEBUG, "Adding client entry for " MACSTR, + MAC2STR(hdr->addr2)); + memcpy(cli->addr, hdr->addr2, ETH_ALEN); + memcpy(&cli->un, from, sizeof(cli->un)); + cli->unlen = fromlen; + cli->next = drv->cli; + drv->cli = cli; + } + + wpa_hexdump(MSG_MSGDUMP, "test_driver_mlme: received frame", + data, datalen); + fc = le_to_host16(hdr->frame_control); + if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT) { + wpa_printf(MSG_ERROR, "%s: received non-mgmt frame", + __func__); + return; + } + + os_memset(&event, 0, sizeof(event)); + event.rx_mgmt.frame = data; + event.rx_mgmt.frame_len = datalen; + wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event); +} + + +static void test_driver_receive_unix(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct wpa_driver_test_data *drv = eloop_ctx; + char buf[2000]; + int res; + struct sockaddr_un from; + socklen_t fromlen = sizeof(from); + + res = recvfrom(sock, buf, sizeof(buf) - 1, 0, + (struct sockaddr *) &from, &fromlen); + if (res < 0) { + perror("recvfrom(test_socket)"); + return; + } + buf[res] = '\0'; + + wpa_printf(MSG_DEBUG, "test_driver: received %u bytes", res); + + if (strncmp(buf, "SCAN", 4) == 0) { + test_driver_scan(drv, &from, fromlen, buf + 4); + } else if (strncmp(buf, "ASSOC ", 6) == 0) { + test_driver_assoc(drv, &from, fromlen, buf + 6); + } else if (strcmp(buf, "DISASSOC") == 0) { + test_driver_disassoc(drv, &from, fromlen); + } else if (strncmp(buf, "EAPOL ", 6) == 0) { + test_driver_eapol(drv, &from, fromlen, (u8 *) buf + 6, + res - 6); + } else if (strncmp(buf, "ETHER ", 6) == 0) { + test_driver_ether(drv, &from, fromlen, (u8 *) buf + 6, + res - 6); + } else if (strncmp(buf, "MLME ", 5) == 0) { + test_driver_mlme(drv, &from, fromlen, (u8 *) buf + 5, res - 5); + } else { + wpa_hexdump_ascii(MSG_DEBUG, "Unknown test_socket command", + (u8 *) buf, res); + } +} + + +static int test_driver_set_generic_elem(void *priv, + const u8 *elem, size_t elem_len) +{ + struct test_driver_bss *bss = priv; + + os_free(bss->ie); + + if (elem == NULL) { + bss->ie = NULL; + bss->ielen = 0; + return 0; + } + + bss->ie = os_malloc(elem_len); + if (bss->ie == NULL) { + bss->ielen = 0; + return -1; + } + + memcpy(bss->ie, elem, elem_len); + bss->ielen = elem_len; + return 0; +} + + +static int test_driver_set_ap_wps_ie(void *priv, const struct wpabuf *beacon, + const struct wpabuf *proberesp) +{ + struct test_driver_bss *bss = priv; + + if (beacon == NULL) + wpa_printf(MSG_DEBUG, "test_driver: Clear Beacon WPS IE"); + else + wpa_hexdump_buf(MSG_DEBUG, "test_driver: Beacon WPS IE", + beacon); + + os_free(bss->wps_beacon_ie); + + if (beacon == NULL) { + bss->wps_beacon_ie = NULL; + bss->wps_beacon_ie_len = 0; + } else { + bss->wps_beacon_ie = os_malloc(wpabuf_len(beacon)); + if (bss->wps_beacon_ie == NULL) { + bss->wps_beacon_ie_len = 0; + return -1; + } + + os_memcpy(bss->wps_beacon_ie, wpabuf_head(beacon), + wpabuf_len(beacon)); + bss->wps_beacon_ie_len = wpabuf_len(beacon); + } + + if (proberesp == NULL) + wpa_printf(MSG_DEBUG, "test_driver: Clear Probe Response WPS " + "IE"); + else + wpa_hexdump_buf(MSG_DEBUG, "test_driver: Probe Response WPS " + "IE", proberesp); + + os_free(bss->wps_probe_resp_ie); + + if (proberesp == NULL) { + bss->wps_probe_resp_ie = NULL; + bss->wps_probe_resp_ie_len = 0; + } else { + bss->wps_probe_resp_ie = os_malloc(wpabuf_len(proberesp)); + if (bss->wps_probe_resp_ie == NULL) { + bss->wps_probe_resp_ie_len = 0; + return -1; + } + + os_memcpy(bss->wps_probe_resp_ie, wpabuf_head(proberesp), + wpabuf_len(proberesp)); + bss->wps_probe_resp_ie_len = wpabuf_len(proberesp); + } + + return 0; +} + + +static int test_driver_sta_deauth(void *priv, const u8 *own_addr, + const u8 *addr, int reason) +{ + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; + struct test_client_socket *cli; + + if (drv->test_socket < 0) + return -1; + + cli = drv->cli; + while (cli) { + if (memcmp(cli->addr, addr, ETH_ALEN) == 0) + break; + cli = cli->next; + } + + if (!cli) + return -1; + + return sendto(drv->test_socket, "DEAUTH", 6, 0, + (struct sockaddr *) &cli->un, cli->unlen); +} + + +static int test_driver_sta_disassoc(void *priv, const u8 *own_addr, + const u8 *addr, int reason) +{ + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; + struct test_client_socket *cli; + + if (drv->test_socket < 0) + return -1; + + cli = drv->cli; + while (cli) { + if (memcmp(cli->addr, addr, ETH_ALEN) == 0) + break; + cli = cli->next; + } + + if (!cli) + return -1; + + return sendto(drv->test_socket, "DISASSOC", 8, 0, + (struct sockaddr *) &cli->un, cli->unlen); +} + + +static int test_driver_bss_add(void *priv, const char *ifname, const u8 *bssid, + void *bss_ctx, void **drv_priv) +{ + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; + struct test_driver_bss *bss; + + wpa_printf(MSG_DEBUG, "%s(ifname=%s bssid=" MACSTR ")", + __func__, ifname, MAC2STR(bssid)); + + bss = os_zalloc(sizeof(*bss)); + if (bss == NULL) + return -1; + + bss->bss_ctx = bss_ctx; + bss->drv = drv; + os_strlcpy(bss->ifname, ifname, IFNAMSIZ); + os_memcpy(bss->bssid, bssid, ETH_ALEN); + + dl_list_add(&drv->bss, &bss->list); + if (drv->global) { + drv->global->bss_add_used = 1; + os_memcpy(drv->global->req_addr, bssid, ETH_ALEN); + } + + if (drv_priv) + *drv_priv = bss; + + return 0; +} + + +static int test_driver_bss_remove(void *priv, const char *ifname) +{ + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; + struct test_driver_bss *bss; + struct test_client_socket *cli, *prev_c; + + wpa_printf(MSG_DEBUG, "%s(ifname=%s)", __func__, ifname); + + dl_list_for_each(bss, &drv->bss, struct test_driver_bss, list) { + if (strcmp(bss->ifname, ifname) != 0) + continue; + + for (prev_c = NULL, cli = drv->cli; cli; + prev_c = cli, cli = cli->next) { + if (cli->bss != bss) + continue; + if (prev_c) + prev_c->next = cli->next; + else + drv->cli = cli->next; + os_free(cli); + break; + } + + dl_list_del(&bss->list); + test_driver_free_bss(bss); + return 0; + } + + return -1; +} + + +static int test_driver_if_add(void *priv, enum wpa_driver_if_type type, + const char *ifname, const u8 *addr, + void *bss_ctx, void **drv_priv, + char *force_ifname, u8 *if_addr) +{ + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; + + wpa_printf(MSG_DEBUG, "%s(type=%d ifname=%s bss_ctx=%p)", + __func__, type, ifname, bss_ctx); + if (addr) + os_memcpy(if_addr, addr, ETH_ALEN); + else { + drv->alloc_iface_idx++; + if_addr[0] = 0x02; /* locally administered */ + sha1_prf(drv->own_addr, ETH_ALEN, + "hostapd test addr generation", + (const u8 *) &drv->alloc_iface_idx, + sizeof(drv->alloc_iface_idx), + if_addr + 1, ETH_ALEN - 1); + } + if (type == WPA_IF_AP_BSS) + return test_driver_bss_add(priv, ifname, if_addr, bss_ctx, + drv_priv); + return 0; +} + + +static int test_driver_if_remove(void *priv, enum wpa_driver_if_type type, + const char *ifname) +{ + wpa_printf(MSG_DEBUG, "%s(type=%d ifname=%s)", __func__, type, ifname); + if (type == WPA_IF_AP_BSS) + return test_driver_bss_remove(priv, ifname); + return 0; +} + + +static int test_driver_valid_bss_mask(void *priv, const u8 *addr, + const u8 *mask) +{ + return 0; +} + + +static int test_driver_set_ssid(void *priv, const u8 *buf, int len) +{ + struct test_driver_bss *bss = priv; + + wpa_printf(MSG_DEBUG, "%s(ifname=%s)", __func__, bss->ifname); + wpa_hexdump_ascii(MSG_DEBUG, "test_driver_set_ssid: SSID", buf, len); + + if (len < 0 || (size_t) len > sizeof(bss->ssid)) + return -1; + + os_memcpy(bss->ssid, buf, len); + bss->ssid_len = len; + + return 0; +} + + +static int test_driver_set_privacy(void *priv, int enabled) +{ + struct test_driver_bss *dbss = priv; + + wpa_printf(MSG_DEBUG, "%s(enabled=%d)", __func__, enabled); + dbss->privacy = enabled; + + return 0; +} + + +static int test_driver_set_sta_vlan(void *priv, const u8 *addr, + const char *ifname, int vlan_id) +{ + wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " ifname=%s vlan_id=%d)", + __func__, MAC2STR(addr), ifname, vlan_id); + return 0; +} + + +static int test_driver_sta_add(void *priv, + struct hostapd_sta_add_params *params) +{ + struct test_driver_bss *bss = priv; + struct wpa_driver_test_data *drv = bss->drv; + struct test_client_socket *cli; + + wpa_printf(MSG_DEBUG, "%s(ifname=%s addr=" MACSTR " aid=%d " + "capability=0x%x listen_interval=%d)", + __func__, bss->ifname, MAC2STR(params->addr), params->aid, + params->capability, params->listen_interval); + wpa_hexdump(MSG_DEBUG, "test_driver_sta_add - supp_rates", + params->supp_rates, params->supp_rates_len); + + cli = drv->cli; + while (cli) { + if (os_memcmp(cli->addr, params->addr, ETH_ALEN) == 0) + break; + cli = cli->next; + } + if (!cli) { + wpa_printf(MSG_DEBUG, "%s: no matching client entry", + __func__); + return -1; + } + + cli->bss = bss; + + return 0; +} + + +static struct wpa_driver_test_data * test_alloc_data(void *ctx, + const char *ifname) +{ + struct wpa_driver_test_data *drv; + struct test_driver_bss *bss; + + drv = os_zalloc(sizeof(struct wpa_driver_test_data)); + if (drv == NULL) { + wpa_printf(MSG_ERROR, "Could not allocate memory for test " + "driver data"); + return NULL; + } + + bss = os_zalloc(sizeof(struct test_driver_bss)); + if (bss == NULL) { + os_free(drv); + return NULL; + } + + drv->ctx = ctx; + wpa_trace_add_ref(drv, ctx, ctx); + dl_list_init(&drv->bss); + dl_list_add(&drv->bss, &bss->list); + os_strlcpy(bss->ifname, ifname, IFNAMSIZ); + bss->bss_ctx = ctx; + bss->drv = drv; + + /* Generate a MAC address to help testing with multiple STAs */ + drv->own_addr[0] = 0x02; /* locally administered */ + sha1_prf((const u8 *) ifname, os_strlen(ifname), + "test mac addr generation", + NULL, 0, drv->own_addr + 1, ETH_ALEN - 1); + + return drv; +} + + +static void * test_driver_init(struct hostapd_data *hapd, + struct wpa_init_params *params) +{ + struct wpa_driver_test_data *drv; + struct sockaddr_un addr_un; + struct sockaddr_in addr_in; + struct sockaddr *addr; + socklen_t alen; + struct test_driver_bss *bss; + + drv = test_alloc_data(hapd, params->ifname); + if (drv == NULL) + return NULL; + drv->ap = 1; + bss = dl_list_first(&drv->bss, struct test_driver_bss, list); + + bss->bss_ctx = hapd; + os_memcpy(bss->bssid, drv->own_addr, ETH_ALEN); + os_memcpy(params->own_addr, drv->own_addr, ETH_ALEN); + + if (params->test_socket) { + if (os_strlen(params->test_socket) >= + sizeof(addr_un.sun_path)) { + printf("Too long test_socket path\n"); + wpa_driver_test_deinit(bss); + return NULL; + } + if (strncmp(params->test_socket, "DIR:", 4) == 0) { + size_t len = strlen(params->test_socket) + 30; + drv->test_dir = os_strdup(params->test_socket + 4); + drv->own_socket_path = os_malloc(len); + if (drv->own_socket_path) { + snprintf(drv->own_socket_path, len, + "%s/AP-" MACSTR, + params->test_socket + 4, + MAC2STR(params->own_addr)); + } + } else if (strncmp(params->test_socket, "UDP:", 4) == 0) { + drv->udp_port = atoi(params->test_socket + 4); + } else { + drv->own_socket_path = os_strdup(params->test_socket); + } + if (drv->own_socket_path == NULL && drv->udp_port == 0) { + wpa_driver_test_deinit(bss); + return NULL; + } + + drv->test_socket = socket(drv->udp_port ? PF_INET : PF_UNIX, + SOCK_DGRAM, 0); + if (drv->test_socket < 0) { + perror("socket"); + wpa_driver_test_deinit(bss); + return NULL; + } + + if (drv->udp_port) { + os_memset(&addr_in, 0, sizeof(addr_in)); + addr_in.sin_family = AF_INET; + addr_in.sin_port = htons(drv->udp_port); + addr = (struct sockaddr *) &addr_in; + alen = sizeof(addr_in); + } else { + os_memset(&addr_un, 0, sizeof(addr_un)); + addr_un.sun_family = AF_UNIX; + os_strlcpy(addr_un.sun_path, drv->own_socket_path, + sizeof(addr_un.sun_path)); + addr = (struct sockaddr *) &addr_un; + alen = sizeof(addr_un); + } + if (bind(drv->test_socket, addr, alen) < 0) { + perror("bind(PF_UNIX)"); + close(drv->test_socket); + if (drv->own_socket_path) + unlink(drv->own_socket_path); + wpa_driver_test_deinit(bss); + return NULL; + } + eloop_register_read_sock(drv->test_socket, + test_driver_receive_unix, drv, NULL); + } else + drv->test_socket = -1; + + return bss; +} + + static void wpa_driver_test_poll(void *eloop_ctx, void *timeout_ctx) { struct wpa_driver_test_data *drv = eloop_ctx; @@ -88,13 +1269,6 @@ static void wpa_driver_test_poll(void *eloop_ctx, void *timeout_ctx) } -static int wpa_driver_test_set_wpa(void *priv, int enabled) -{ - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); - return 0; -} - - static void wpa_driver_test_scan_timeout(void *eloop_ctx, void *timeout_ctx) { wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); @@ -129,11 +1303,31 @@ static void wpa_driver_scan_dir(struct wpa_driver_test_data *drv, pos += wpa_snprintf_hex(pos, end - pos, drv->probe_req_ie, drv->probe_req_ie_len); } + if (drv->probe_req_ssid_len) { + /* Add SSID IE */ + ret = os_snprintf(pos, end - pos, "%02x%02x", + WLAN_EID_SSID, + (unsigned int) drv->probe_req_ssid_len); + if (ret >= 0 && ret < end - pos) + pos += ret; + pos += wpa_snprintf_hex(pos, end - pos, drv->probe_req_ssid, + drv->probe_req_ssid_len); + } end[-1] = '\0'; while ((dent = readdir(dir))) { - if (os_strncmp(dent->d_name, "AP-", 3) != 0) + if (os_strncmp(dent->d_name, "AP-", 3) != 0 && + os_strncmp(dent->d_name, "STA-", 4) != 0) continue; + if (drv->own_socket_path) { + size_t olen, dlen; + olen = os_strlen(drv->own_socket_path); + dlen = os_strlen(dent->d_name); + if (olen >= dlen && + os_strcmp(dent->d_name, + drv->own_socket_path + olen - dlen) == 0) + continue; + } wpa_printf(MSG_DEBUG, "%s: SCAN %s", __func__, dent->d_name); os_memset(&addr, 0, sizeof(addr)); @@ -151,11 +1345,42 @@ static void wpa_driver_scan_dir(struct wpa_driver_test_data *drv, #endif /* DRIVER_TEST_UNIX */ -static int wpa_driver_test_scan(void *priv, const u8 *ssid, size_t ssid_len) +static int wpa_driver_test_scan(void *priv, + struct wpa_driver_scan_params *params) { - struct wpa_driver_test_data *drv = priv; + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; + size_t i; + wpa_printf(MSG_DEBUG, "%s: priv=%p", __func__, priv); + os_free(drv->probe_req_ie); + if (params->extra_ies) { + drv->probe_req_ie = os_malloc(params->extra_ies_len); + if (drv->probe_req_ie == NULL) { + drv->probe_req_ie_len = 0; + return -1; + } + os_memcpy(drv->probe_req_ie, params->extra_ies, + params->extra_ies_len); + drv->probe_req_ie_len = params->extra_ies_len; + } else { + drv->probe_req_ie = NULL; + drv->probe_req_ie_len = 0; + } + + for (i = 0; i < params->num_ssids; i++) + wpa_hexdump(MSG_DEBUG, "Scan SSID", + params->ssids[i].ssid, params->ssids[i].ssid_len); + drv->probe_req_ssid_len = 0; + if (params->num_ssids) { + os_memcpy(drv->probe_req_ssid, params->ssids[0].ssid, + params->ssids[0].ssid_len); + drv->probe_req_ssid_len = params->ssids[0].ssid_len; + } + wpa_hexdump(MSG_DEBUG, "Scan extra IE(s)", + params->extra_ies, params->extra_ies_len); + drv->num_scanres = 0; #ifdef DRIVER_TEST_UNIX @@ -186,7 +1411,8 @@ static int wpa_driver_test_scan(void *priv, const u8 *ssid, size_t ssid_len) static struct wpa_scan_results * wpa_driver_test_get_scan_results2(void *priv) { - struct wpa_driver_test_data *drv = priv; + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; struct wpa_scan_results *res; size_t i; @@ -216,22 +1442,37 @@ static struct wpa_scan_results * wpa_driver_test_get_scan_results2(void *priv) } -static int wpa_driver_test_set_key(void *priv, wpa_alg alg, const u8 *addr, +static int wpa_driver_test_set_key(const char *ifname, void *priv, + enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len) { - wpa_printf(MSG_DEBUG, "%s: priv=%p alg=%d key_idx=%d set_tx=%d", - __func__, priv, alg, key_idx, set_tx); - if (addr) { + wpa_printf(MSG_DEBUG, "%s: ifname=%s priv=%p alg=%d key_idx=%d " + "set_tx=%d", + __func__, ifname, priv, alg, key_idx, set_tx); + if (addr) wpa_printf(MSG_DEBUG, " addr=" MACSTR, MAC2STR(addr)); - } - if (seq) { + if (seq) wpa_hexdump(MSG_DEBUG, " seq", seq, seq_len); + if (key) + wpa_hexdump_key(MSG_DEBUG, " key", key, key_len); + return 0; +} + + +static int wpa_driver_update_mode(struct wpa_driver_test_data *drv, int ap) +{ + if (ap && !drv->ap) { + wpa_driver_test_close_test_socket(drv); + wpa_driver_test_attach(drv, drv->test_dir, 1); + drv->ap = 1; + } else if (!ap && drv->ap) { + wpa_driver_test_close_test_socket(drv); + wpa_driver_test_attach(drv, drv->test_dir, 0); + drv->ap = 0; } - if (key) { - wpa_hexdump(MSG_DEBUG, " key", key, key_len); - } + return 0; } @@ -239,7 +1480,8 @@ static int wpa_driver_test_set_key(void *priv, wpa_alg alg, const u8 *addr, static int wpa_driver_test_associate( void *priv, struct wpa_driver_associate_params *params) { - struct wpa_driver_test_data *drv = priv; + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; wpa_printf(MSG_DEBUG, "%s: priv=%p freq=%d pairwise_suite=%d " "group_suite=%d key_mgmt_suite=%d auth_alg=%d mode=%d", __func__, priv, params->freq, params->pairwise_suite, @@ -264,8 +1506,23 @@ static int wpa_driver_test_associate( } else drv->assoc_wpa_ie_len = 0; + wpa_driver_update_mode(drv, params->mode == IEEE80211_MODE_AP); + + drv->ibss = params->mode == IEEE80211_MODE_IBSS; + dbss->privacy = params->key_mgmt_suite & + (WPA_KEY_MGMT_IEEE8021X | + WPA_KEY_MGMT_PSK | + WPA_KEY_MGMT_WPA_NONE | + WPA_KEY_MGMT_FT_IEEE8021X | + WPA_KEY_MGMT_FT_PSK | + WPA_KEY_MGMT_IEEE8021X_SHA256 | + WPA_KEY_MGMT_PSK_SHA256); + if (params->wep_key_len[params->wep_tx_keyidx]) + dbss->privacy = 1; + #ifdef DRIVER_TEST_UNIX - if (drv->test_dir && params->bssid) { + if (drv->test_dir && params->bssid && + params->mode != IEEE80211_MODE_IBSS) { os_memset(&drv->hostapd_addr, 0, sizeof(drv->hostapd_addr)); drv->hostapd_addr.sun_family = AF_UNIX; os_snprintf(drv->hostapd_addr.sun_path, @@ -276,8 +1533,20 @@ static int wpa_driver_test_associate( } #endif /* DRIVER_TEST_UNIX */ - if (drv->test_socket >= 0 && - (drv->hostapd_addr_set || drv->hostapd_addr_udp_set)) { + if (params->mode == IEEE80211_MODE_AP) { + os_memcpy(dbss->ssid, params->ssid, params->ssid_len); + dbss->ssid_len = params->ssid_len; + os_memcpy(dbss->bssid, drv->own_addr, ETH_ALEN); + if (params->wpa_ie && params->wpa_ie_len) { + dbss->ie = os_malloc(params->wpa_ie_len); + if (dbss->ie) { + os_memcpy(dbss->ie, params->wpa_ie, + params->wpa_ie_len); + dbss->ielen = params->wpa_ie_len; + } + } + } else if (drv->test_socket >= 0 && + (drv->hostapd_addr_set || drv->hostapd_addr_udp_set)) { char cmd[200], *pos, *end; int ret; end = cmd + sizeof(cmd); @@ -311,10 +1580,22 @@ static int wpa_driver_test_associate( return -1; } - os_memcpy(drv->ssid, params->ssid, params->ssid_len); - drv->ssid_len = params->ssid_len; + os_memcpy(dbss->ssid, params->ssid, params->ssid_len); + dbss->ssid_len = params->ssid_len; } else { drv->associated = 1; + if (params->mode == IEEE80211_MODE_IBSS) { + os_memcpy(dbss->ssid, params->ssid, params->ssid_len); + dbss->ssid_len = params->ssid_len; + if (params->bssid) + os_memcpy(dbss->bssid, params->bssid, + ETH_ALEN); + else { + os_get_random(dbss->bssid, ETH_ALEN); + dbss->bssid[0] &= ~0x01; + dbss->bssid[0] |= 0x02; + } + } wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL); } @@ -324,17 +1605,17 @@ static int wpa_driver_test_associate( static int wpa_driver_test_get_bssid(void *priv, u8 *bssid) { - struct wpa_driver_test_data *drv = priv; - os_memcpy(bssid, drv->bssid, ETH_ALEN); + struct test_driver_bss *dbss = priv; + os_memcpy(bssid, dbss->bssid, ETH_ALEN); return 0; } static int wpa_driver_test_get_ssid(void *priv, u8 *ssid) { - struct wpa_driver_test_data *drv = priv; - os_memcpy(ssid, drv->ssid, 32); - return drv->ssid_len; + struct test_driver_bss *dbss = priv; + os_memcpy(ssid, dbss->ssid, 32); + return dbss->ssid_len; } @@ -363,10 +1644,11 @@ static int wpa_driver_test_send_disassoc(struct wpa_driver_test_data *drv) static int wpa_driver_test_deauthenticate(void *priv, const u8 *addr, int reason_code) { - struct wpa_driver_test_data *drv = priv; + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d", __func__, MAC2STR(addr), reason_code); - os_memset(drv->bssid, 0, ETH_ALEN); + os_memset(dbss->bssid, 0, ETH_ALEN); drv->associated = 0; wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); return wpa_driver_test_send_disassoc(drv); @@ -376,16 +1658,36 @@ static int wpa_driver_test_deauthenticate(void *priv, const u8 *addr, static int wpa_driver_test_disassociate(void *priv, const u8 *addr, int reason_code) { - struct wpa_driver_test_data *drv = priv; + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d", __func__, MAC2STR(addr), reason_code); - os_memset(drv->bssid, 0, ETH_ALEN); + os_memset(dbss->bssid, 0, ETH_ALEN); drv->associated = 0; wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); return wpa_driver_test_send_disassoc(drv); } +static const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie) +{ + const u8 *end, *pos; + + pos = (const u8 *) (res + 1); + end = pos + res->ie_len; + + while (pos + 1 < end) { + if (pos + 2 + pos[1] > end) + break; + if (pos[0] == ie) + return pos; + pos += 2 + pos[1]; + } + + return NULL; +} + + static void wpa_driver_test_scanresp(struct wpa_driver_test_data *drv, struct sockaddr *from, socklen_t fromlen, @@ -396,6 +1698,7 @@ static void wpa_driver_test_scanresp(struct wpa_driver_test_data *drv, size_t len; u8 *ie_pos, *ie_start, *ie_end; #define MAX_IE_LEN 1000 + const u8 *ds_params; wpa_printf(MSG_DEBUG, "test_driver: SCANRESP %s", data); if (drv->num_scanres >= MAX_SCAN_RESULTS) { @@ -464,8 +1767,16 @@ static void wpa_driver_test_scanresp(struct wpa_driver_test_data *drv, pos = pos2 + 1; while (*pos == ' ') pos++; - if (os_strncmp(pos, "PRIVACY", 7) == 0) + if (os_strstr(pos, "PRIVACY")) res->caps |= IEEE80211_CAP_PRIVACY; + if (os_strstr(pos, "IBSS")) + res->caps |= IEEE80211_CAP_IBSS; + } + + ds_params = wpa_scan_get_ie(res, WLAN_EID_DS_PARAMS); + if (ds_params && ds_params[1] > 0) { + if (ds_params[2] >= 1 && ds_params[2] <= 13) + res->freq = 2407 + ds_params[2] * 5; } os_free(drv->scanres[drv->num_scanres]); @@ -478,8 +1789,12 @@ static void wpa_driver_test_assocresp(struct wpa_driver_test_data *drv, socklen_t fromlen, const char *data) { + struct test_driver_bss *bss; + + bss = dl_list_first(&drv->bss, struct test_driver_bss, list); + /* ASSOCRESP BSSID <res> */ - if (hwaddr_aton(data, drv->bssid)) { + if (hwaddr_aton(data, bss->bssid)) { wpa_printf(MSG_DEBUG, "test_driver: invalid BSSID in " "assocresp"); } @@ -509,15 +1824,20 @@ static void wpa_driver_test_eapol(struct wpa_driver_test_data *drv, socklen_t fromlen, const u8 *data, size_t data_len) { - const u8 *src = drv->bssid; + const u8 *src; + struct test_driver_bss *bss; + + bss = dl_list_first(&drv->bss, struct test_driver_bss, list); if (data_len > 14) { /* Skip Ethernet header */ src = data + ETH_ALEN; data += 14; data_len -= 14; - } - wpa_supplicant_rx_eapol(drv->ctx, src, data, data_len); + } else + src = bss->bssid; + + drv_event_eapol_rx(drv->ctx, src, data, data_len); } @@ -526,11 +1846,113 @@ static void wpa_driver_test_mlme(struct wpa_driver_test_data *drv, socklen_t fromlen, const u8 *data, size_t data_len) { -#ifdef CONFIG_CLIENT_MLME - struct ieee80211_rx_status rx_status; - os_memset(&rx_status, 0, sizeof(rx_status)); - wpa_supplicant_sta_rx(drv->ctx, data, data_len, &rx_status); -#endif /* CONFIG_CLIENT_MLME */ + int freq = 0, own_freq; + union wpa_event_data event; + struct test_driver_bss *bss; + + bss = dl_list_first(&drv->bss, struct test_driver_bss, list); + if (data_len > 6 && os_memcmp(data, "freq=", 5) == 0) { + size_t pos; + for (pos = 5; pos < data_len; pos++) { + if (data[pos] == ' ') + break; + } + if (pos < data_len) { + freq = atoi((const char *) &data[5]); + wpa_printf(MSG_DEBUG, "test_driver(%s): MLME RX on " + "freq %d MHz", bss->ifname, freq); + pos++; + data += pos; + data_len -= pos; + } + } + + if (drv->remain_on_channel_freq) + own_freq = drv->remain_on_channel_freq; + else + own_freq = drv->current_freq; + + if (freq && own_freq && freq != own_freq) { + wpa_printf(MSG_DEBUG, "test_driver(%s): Ignore MLME RX on " + "another frequency %d MHz (own %d MHz)", + bss->ifname, freq, own_freq); + return; + } + + os_memset(&event, 0, sizeof(event)); + event.mlme_rx.buf = data; + event.mlme_rx.len = data_len; + event.mlme_rx.freq = freq; + wpa_supplicant_event(drv->ctx, EVENT_MLME_RX, &event); + + if (drv->probe_req_report && data_len >= 24) { + const struct ieee80211_mgmt *mgmt; + u16 fc; + + mgmt = (const struct ieee80211_mgmt *) data; + fc = le_to_host16(mgmt->frame_control); + if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && + WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_REQ) { + os_memset(&event, 0, sizeof(event)); + event.rx_probe_req.sa = mgmt->sa; + event.rx_probe_req.ie = mgmt->u.probe_req.variable; + event.rx_probe_req.ie_len = + data_len - (mgmt->u.probe_req.variable - data); + wpa_supplicant_event(drv->ctx, EVENT_RX_PROBE_REQ, + &event); + } + } +} + + +static void wpa_driver_test_scan_cmd(struct wpa_driver_test_data *drv, + struct sockaddr *from, + socklen_t fromlen, + const u8 *data, size_t data_len) +{ + char buf[512], *pos, *end; + int ret; + struct test_driver_bss *bss; + + bss = dl_list_first(&drv->bss, struct test_driver_bss, list); + + /* data: optional [ STA-addr | ' ' | IEs(hex) ] */ + + if (!drv->ibss) + return; + + pos = buf; + end = buf + sizeof(buf); + + /* reply: SCANRESP BSSID SSID IEs */ + ret = snprintf(pos, end - pos, "SCANRESP " MACSTR " ", + MAC2STR(bss->bssid)); + if (ret < 0 || ret >= end - pos) + return; + pos += ret; + pos += wpa_snprintf_hex(pos, end - pos, + bss->ssid, bss->ssid_len); + ret = snprintf(pos, end - pos, " "); + if (ret < 0 || ret >= end - pos) + return; + pos += ret; + pos += wpa_snprintf_hex(pos, end - pos, drv->assoc_wpa_ie, + drv->assoc_wpa_ie_len); + + if (bss->privacy) { + ret = snprintf(pos, end - pos, " PRIVACY"); + if (ret < 0 || ret >= end - pos) + return; + pos += ret; + } + + ret = snprintf(pos, end - pos, " IBSS"); + if (ret < 0 || ret >= end - pos) + return; + pos += ret; + + sendto(drv->test_socket, buf, pos - buf, 0, + (struct sockaddr *) from, fromlen); } @@ -544,6 +1966,11 @@ static void wpa_driver_test_receive_unix(int sock, void *eloop_ctx, socklen_t fromlen = sizeof(from); const size_t buflen = 2000; + if (drv->ap) { + test_driver_receive_unix(sock, eloop_ctx, sock_ctx); + return; + } + buf = os_malloc(buflen); if (buf == NULL) return; @@ -576,6 +2003,10 @@ static void wpa_driver_test_receive_unix(int sock, void *eloop_ctx, } else if (os_strncmp(buf, "MLME ", 5) == 0) { wpa_driver_test_mlme(drv, (struct sockaddr *) &from, fromlen, (const u8 *) buf + 5, res - 5); + } else if (os_strncmp(buf, "SCAN ", 5) == 0) { + wpa_driver_test_scan_cmd(drv, (struct sockaddr *) &from, + fromlen, + (const u8 *) buf + 5, res - 5); } else { wpa_hexdump_ascii(MSG_DEBUG, "Unknown test_socket command", (u8 *) buf, res); @@ -588,32 +2019,34 @@ static void * wpa_driver_test_init2(void *ctx, const char *ifname, void *global_priv) { struct wpa_driver_test_data *drv; + struct wpa_driver_test_global *global = global_priv; + struct test_driver_bss *bss; - drv = os_zalloc(sizeof(*drv)); + drv = test_alloc_data(ctx, ifname); if (drv == NULL) return NULL; + bss = dl_list_first(&drv->bss, struct test_driver_bss, list); drv->global = global_priv; - drv->ctx = ctx; drv->test_socket = -1; /* Set dummy BSSID and SSID for testing. */ - drv->bssid[0] = 0x02; - drv->bssid[1] = 0x00; - drv->bssid[2] = 0x00; - drv->bssid[3] = 0x00; - drv->bssid[4] = 0x00; - drv->bssid[5] = 0x01; - os_memcpy(drv->ssid, "test", 5); - drv->ssid_len = 4; + bss->bssid[0] = 0x02; + bss->bssid[1] = 0x00; + bss->bssid[2] = 0x00; + bss->bssid[3] = 0x00; + bss->bssid[4] = 0x00; + bss->bssid[5] = 0x01; + os_memcpy(bss->ssid, "test", 5); + bss->ssid_len = 4; + + if (global->bss_add_used) { + os_memcpy(drv->own_addr, global->req_addr, ETH_ALEN); + global->bss_add_used = 0; + } - /* Generate a MAC address to help testing with multiple STAs */ - drv->own_addr[0] = 0x02; /* locally administered */ - sha1_prf((const u8 *) ifname, os_strlen(ifname), - "wpa_supplicant test mac addr generation", - NULL, 0, drv->own_addr + 1, ETH_ALEN - 1); eloop_register_timeout(1, 0, wpa_driver_test_poll, drv, NULL); - return drv; + return bss; } @@ -635,21 +2068,42 @@ static void wpa_driver_test_close_test_socket(struct wpa_driver_test_data *drv) static void wpa_driver_test_deinit(void *priv) { - struct wpa_driver_test_data *drv = priv; + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; + struct test_client_socket *cli, *prev; int i; + + cli = drv->cli; + while (cli) { + prev = cli; + cli = cli->next; + os_free(prev); + } + +#ifdef HOSTAPD + /* There should be only one BSS remaining at this point. */ + if (dl_list_len(&drv->bss) != 1) + wpa_printf(MSG_ERROR, "%s: %u remaining BSS entries", + __func__, dl_list_len(&drv->bss)); +#endif /* HOSTAPD */ + + test_driver_free_bsses(drv); + wpa_driver_test_close_test_socket(drv); eloop_cancel_timeout(wpa_driver_test_scan_timeout, drv, drv->ctx); eloop_cancel_timeout(wpa_driver_test_poll, drv, NULL); + eloop_cancel_timeout(test_remain_on_channel_timeout, drv, NULL); os_free(drv->test_dir); for (i = 0; i < MAX_SCAN_RESULTS; i++) os_free(drv->scanres[i]); os_free(drv->probe_req_ie); + wpa_trace_remove_ref(drv, ctx, drv->ctx); os_free(drv); } static int wpa_driver_test_attach(struct wpa_driver_test_data *drv, - const char *dir) + const char *dir, int ap) { #ifdef DRIVER_TEST_UNIX static unsigned int counter = 0; @@ -662,8 +2116,8 @@ static int wpa_driver_test_attach(struct wpa_driver_test_data *drv, drv->own_socket_path = os_malloc(len); if (drv->own_socket_path == NULL) return -1; - os_snprintf(drv->own_socket_path, len, "%s/STA-" MACSTR, - dir, MAC2STR(drv->own_addr)); + os_snprintf(drv->own_socket_path, len, "%s/%s-" MACSTR, + dir, ap ? "AP" : "STA", MAC2STR(drv->own_addr)); } else { drv->own_socket_path = os_malloc(100); if (drv->own_socket_path == NULL) @@ -750,7 +2204,8 @@ static int wpa_driver_test_attach_udp(struct wpa_driver_test_data *drv, static int wpa_driver_test_set_param(void *priv, const char *param) { - struct wpa_driver_test_data *drv = priv; + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; const char *pos; wpa_printf(MSG_DEBUG, "%s: param='%s'", __func__, param); @@ -790,7 +2245,7 @@ static int wpa_driver_test_set_param(void *priv, const char *param) end = os_strchr(drv->test_dir, ' '); if (end) *end = '\0'; - if (wpa_driver_test_attach(drv, drv->test_dir)) + if (wpa_driver_test_attach(drv, drv->test_dir, 0)) return -1; } else { pos = os_strstr(param, "test_udp="); @@ -805,7 +2260,7 @@ static int wpa_driver_test_set_param(void *priv, const char *param) if (wpa_driver_test_attach_udp(drv, dst)) return -1; os_free(dst); - } else if (wpa_driver_test_attach(drv, NULL)) + } else if (wpa_driver_test_attach(drv, NULL, 0)) return -1; } @@ -827,7 +2282,8 @@ static int wpa_driver_test_set_param(void *priv, const char *param) static const u8 * wpa_driver_test_get_mac_addr(void *priv) { - struct wpa_driver_test_data *drv = priv; + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; wpa_printf(MSG_DEBUG, "%s", __func__); return drv->own_addr; } @@ -836,7 +2292,8 @@ static const u8 * wpa_driver_test_get_mac_addr(void *priv) static int wpa_driver_test_send_eapol(void *priv, const u8 *dest, u16 proto, const u8 *data, size_t data_len) { - struct wpa_driver_test_data *drv = priv; + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; char *msg; size_t msg_len; struct l2_ethhdr eth; @@ -861,7 +2318,7 @@ static int wpa_driver_test_send_eapol(void *priv, const u8 *dest, u16 proto, os_memcpy(msg + 6, ð, sizeof(eth)); os_memcpy(msg + 6 + sizeof(eth), data, data_len); - if (os_memcmp(dest, drv->bssid, ETH_ALEN) == 0 || + if (os_memcmp(dest, dbss->bssid, ETH_ALEN) == 0 || drv->test_dir == NULL) { if (drv->hostapd_addr_udp_set) { addr = (struct sockaddr *) &drv->hostapd_addr_udp; @@ -908,7 +2365,8 @@ static int wpa_driver_test_send_eapol(void *priv, const u8 *dest, u16 proto, static int wpa_driver_test_get_capa(void *priv, struct wpa_driver_capa *capa) { - struct wpa_driver_test_data *drv = priv; + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; os_memset(capa, 0, sizeof(*capa)); capa->key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA | WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | @@ -926,6 +2384,9 @@ static int wpa_driver_test_get_capa(void *priv, struct wpa_driver_capa *capa) WPA_DRIVER_AUTH_LEAP; if (drv->use_mlme) capa->flags |= WPA_DRIVER_FLAGS_USER_SPACE_MLME; + capa->flags |= WPA_DRIVER_FLAGS_AP; + capa->max_scan_ssids = 2; + capa->max_remain_on_chan = 60000; return 0; } @@ -947,134 +2408,15 @@ static int wpa_driver_test_mlme_setprotection(void *priv, const u8 *addr, } -#ifdef CONFIG_CLIENT_MLME -static struct wpa_hw_modes * -wpa_driver_test_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) -{ - struct wpa_hw_modes *modes; - - *num_modes = 1; - *flags = 0; - modes = os_zalloc(*num_modes * sizeof(struct wpa_hw_modes)); - if (modes == NULL) - return NULL; - modes[0].mode = WPA_MODE_IEEE80211G; - modes[0].num_channels = 1; - modes[0].num_rates = 1; - modes[0].channels = os_zalloc(sizeof(struct wpa_channel_data)); - modes[0].rates = os_zalloc(sizeof(struct wpa_rate_data)); - if (modes[0].channels == NULL || modes[0].rates == NULL) { - wpa_supplicant_sta_free_hw_features(modes, *num_modes); - return NULL; - } - modes[0].channels[0].chan = 1; - modes[0].channels[0].freq = 2412; - modes[0].channels[0].flag = WPA_CHAN_W_SCAN | WPA_CHAN_W_ACTIVE_SCAN; - modes[0].rates[0].rate = 10; - modes[0].rates[0].flags = WPA_RATE_BASIC | WPA_RATE_SUPPORTED | - WPA_RATE_CCK | WPA_RATE_MANDATORY; - - return modes; -} - - -static int wpa_driver_test_set_channel(void *priv, wpa_hw_mode phymode, +static int wpa_driver_test_set_channel(void *priv, + enum hostapd_hw_mode phymode, int chan, int freq) { + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; wpa_printf(MSG_DEBUG, "%s: phymode=%d chan=%d freq=%d", __func__, phymode, chan, freq); - return 0; -} - - -static int wpa_driver_test_send_mlme(void *priv, const u8 *data, - size_t data_len) -{ - struct wpa_driver_test_data *drv = priv; - struct msghdr msg; - struct iovec io[2]; - struct sockaddr_un addr; - const u8 *dest; - struct dirent *dent; - DIR *dir; - - wpa_hexdump(MSG_MSGDUMP, "test_send_mlme", data, data_len); - if (data_len < 10) - return -1; - dest = data + 4; - - io[0].iov_base = "MLME "; - io[0].iov_len = 5; - io[1].iov_base = (u8 *) data; - io[1].iov_len = data_len; - - os_memset(&msg, 0, sizeof(msg)); - msg.msg_iov = io; - msg.msg_iovlen = 2; - if (os_memcmp(dest, drv->bssid, ETH_ALEN) == 0 || - drv->test_dir == NULL) { - if (drv->hostapd_addr_udp_set) { - msg.msg_name = &drv->hostapd_addr_udp; - msg.msg_namelen = sizeof(drv->hostapd_addr_udp); - } else { -#ifdef DRIVER_TEST_UNIX - msg.msg_name = &drv->hostapd_addr; - msg.msg_namelen = sizeof(drv->hostapd_addr); -#endif /* DRIVER_TEST_UNIX */ - } - } else if (os_memcmp(dest, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0) - { - dir = opendir(drv->test_dir); - if (dir == NULL) - return -1; - while ((dent = readdir(dir))) { -#ifdef _DIRENT_HAVE_D_TYPE - /* Skip the file if it is not a socket. - * Also accept DT_UNKNOWN (0) in case - * the C library or underlying file - * system does not support d_type. */ - if (dent->d_type != DT_SOCK && - dent->d_type != DT_UNKNOWN) - continue; -#endif /* _DIRENT_HAVE_D_TYPE */ - if (os_strcmp(dent->d_name, ".") == 0 || - os_strcmp(dent->d_name, "..") == 0) - continue; - wpa_printf(MSG_DEBUG, "%s: Send broadcast MLME to %s", - __func__, dent->d_name); - os_memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - os_snprintf(addr.sun_path, sizeof(addr.sun_path), - "%s/%s", drv->test_dir, dent->d_name); - - msg.msg_name = &addr; - msg.msg_namelen = sizeof(addr); - - if (sendmsg(drv->test_socket, &msg, 0) < 0) - perror("sendmsg(test_socket)"); - } - closedir(dir); - return 0; - } else { - struct stat st; - os_memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - os_snprintf(addr.sun_path, sizeof(addr.sun_path), - "%s/AP-" MACSTR, drv->test_dir, MAC2STR(dest)); - if (stat(addr.sun_path, &st) < 0) { - os_snprintf(addr.sun_path, sizeof(addr.sun_path), - "%s/STA-" MACSTR, - drv->test_dir, MAC2STR(dest)); - } - msg.msg_name = &addr; - msg.msg_namelen = sizeof(addr); - } - - if (sendmsg(drv->test_socket, &msg, 0) < 0) { - perror("sendmsg(test_socket)"); - return -1; - } - + drv->current_freq = freq; return 0; } @@ -1108,29 +2450,6 @@ static int wpa_driver_test_set_bssid(void *priv, const u8 *bssid) wpa_printf(MSG_DEBUG, "%s: bssid=" MACSTR, __func__, MAC2STR(bssid)); return 0; } -#endif /* CONFIG_CLIENT_MLME */ - - -static int wpa_driver_test_set_probe_req_ie(void *priv, const u8 *ies, - size_t ies_len) -{ - struct wpa_driver_test_data *drv = priv; - - os_free(drv->probe_req_ie); - if (ies) { - drv->probe_req_ie = os_malloc(ies_len); - if (drv->probe_req_ie == NULL) { - drv->probe_req_ie_len = 0; - return -1; - } - os_memcpy(drv->probe_req_ie, ies, ies_len); - drv->probe_req_ie_len = ies_len; - } else { - drv->probe_req_ie = NULL; - drv->probe_req_ie_len = 0; - } - return 0; -} static void * wpa_driver_test_global_init(void) @@ -1172,59 +2491,263 @@ wpa_driver_test_get_interfaces(void *global_priv) } +static struct hostapd_hw_modes * +wpa_driver_test_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) +{ + struct hostapd_hw_modes *modes; + size_t i; + + *num_modes = 3; + *flags = 0; + modes = os_zalloc(*num_modes * sizeof(struct hostapd_hw_modes)); + if (modes == NULL) + return NULL; + modes[0].mode = HOSTAPD_MODE_IEEE80211G; + modes[0].num_channels = 11; + modes[0].num_rates = 12; + modes[0].channels = + os_zalloc(11 * sizeof(struct hostapd_channel_data)); + modes[0].rates = os_zalloc(modes[0].num_rates * sizeof(int)); + if (modes[0].channels == NULL || modes[0].rates == NULL) + goto fail; + for (i = 0; i < 11; i++) { + modes[0].channels[i].chan = i + 1; + modes[0].channels[i].freq = 2412 + 5 * i; + modes[0].channels[i].flag = 0; + } + modes[0].rates[0] = 10; + modes[0].rates[1] = 20; + modes[0].rates[2] = 55; + modes[0].rates[3] = 110; + modes[0].rates[4] = 60; + modes[0].rates[5] = 90; + modes[0].rates[6] = 120; + modes[0].rates[7] = 180; + modes[0].rates[8] = 240; + modes[0].rates[9] = 360; + modes[0].rates[10] = 480; + modes[0].rates[11] = 540; + + modes[1].mode = HOSTAPD_MODE_IEEE80211B; + modes[1].num_channels = 11; + modes[1].num_rates = 4; + modes[1].channels = + os_zalloc(11 * sizeof(struct hostapd_channel_data)); + modes[1].rates = os_zalloc(modes[1].num_rates * sizeof(int)); + if (modes[1].channels == NULL || modes[1].rates == NULL) + goto fail; + for (i = 0; i < 11; i++) { + modes[1].channels[i].chan = i + 1; + modes[1].channels[i].freq = 2412 + 5 * i; + modes[1].channels[i].flag = 0; + } + modes[1].rates[0] = 10; + modes[1].rates[1] = 20; + modes[1].rates[2] = 55; + modes[1].rates[3] = 110; + + modes[2].mode = HOSTAPD_MODE_IEEE80211A; + modes[2].num_channels = 1; + modes[2].num_rates = 8; + modes[2].channels = os_zalloc(sizeof(struct hostapd_channel_data)); + modes[2].rates = os_zalloc(modes[2].num_rates * sizeof(int)); + if (modes[2].channels == NULL || modes[2].rates == NULL) + goto fail; + modes[2].channels[0].chan = 60; + modes[2].channels[0].freq = 5300; + modes[2].channels[0].flag = 0; + modes[2].rates[0] = 60; + modes[2].rates[1] = 90; + modes[2].rates[2] = 120; + modes[2].rates[3] = 180; + modes[2].rates[4] = 240; + modes[2].rates[5] = 360; + modes[2].rates[6] = 480; + modes[2].rates[7] = 540; + + return modes; + +fail: + if (modes) { + for (i = 0; i < *num_modes; i++) { + os_free(modes[i].channels); + os_free(modes[i].rates); + } + os_free(modes); + } + return NULL; +} + + +static int wpa_driver_test_set_freq(void *priv, + struct hostapd_freq_params *freq) +{ + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; + wpa_printf(MSG_DEBUG, "test: set_freq %u MHz", freq->freq); + drv->current_freq = freq->freq; + return 0; +} + + +static int wpa_driver_test_send_action(void *priv, unsigned int freq, + const u8 *dst, const u8 *src, + const u8 *bssid, + const u8 *data, size_t data_len) +{ + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; + int ret = -1; + u8 *buf; + struct ieee80211_hdr *hdr; + + wpa_printf(MSG_DEBUG, "test: Send Action frame"); + + if ((drv->remain_on_channel_freq && + freq != drv->remain_on_channel_freq) || + (drv->remain_on_channel_freq == 0 && + freq != (unsigned int) drv->current_freq)) { + wpa_printf(MSG_DEBUG, "test: Reject Action frame TX on " + "unexpected channel: freq=%u MHz (current_freq=%u " + "MHz, remain-on-channel freq=%u MHz)", + freq, drv->current_freq, + drv->remain_on_channel_freq); + return -1; + } + + buf = os_zalloc(24 + data_len); + if (buf == NULL) + return ret; + os_memcpy(buf + 24, data, data_len); + hdr = (struct ieee80211_hdr *) buf; + hdr->frame_control = + IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION); + os_memcpy(hdr->addr1, dst, ETH_ALEN); + os_memcpy(hdr->addr2, src, ETH_ALEN); + os_memcpy(hdr->addr3, bssid, ETH_ALEN); + + ret = wpa_driver_test_send_mlme(priv, buf, 24 + data_len); + os_free(buf); + return ret; +} + + +static void test_remain_on_channel_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_driver_test_data *drv = eloop_ctx; + union wpa_event_data data; + + wpa_printf(MSG_DEBUG, "test: Remain-on-channel timeout"); + + os_memset(&data, 0, sizeof(data)); + data.remain_on_channel.freq = drv->remain_on_channel_freq; + data.remain_on_channel.duration = drv->remain_on_channel_duration; + wpa_supplicant_event(drv->ctx, EVENT_CANCEL_REMAIN_ON_CHANNEL, &data); + + drv->remain_on_channel_freq = 0; +} + + +static int wpa_driver_test_remain_on_channel(void *priv, unsigned int freq, + unsigned int duration) +{ + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; + union wpa_event_data data; + + wpa_printf(MSG_DEBUG, "%s(freq=%u, duration=%u)", + __func__, freq, duration); + if (drv->remain_on_channel_freq && + drv->remain_on_channel_freq != freq) { + wpa_printf(MSG_DEBUG, "test: Refuse concurrent " + "remain_on_channel request"); + return -1; + } + + drv->remain_on_channel_freq = freq; + drv->remain_on_channel_duration = duration; + eloop_cancel_timeout(test_remain_on_channel_timeout, drv, NULL); + eloop_register_timeout(duration / 1000, (duration % 1000) * 1000, + test_remain_on_channel_timeout, drv, NULL); + + os_memset(&data, 0, sizeof(data)); + data.remain_on_channel.freq = freq; + data.remain_on_channel.duration = duration; + wpa_supplicant_event(drv->ctx, EVENT_REMAIN_ON_CHANNEL, &data); + + return 0; +} + + +static int wpa_driver_test_cancel_remain_on_channel(void *priv) +{ + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; + wpa_printf(MSG_DEBUG, "%s", __func__); + if (!drv->remain_on_channel_freq) + return -1; + drv->remain_on_channel_freq = 0; + eloop_cancel_timeout(test_remain_on_channel_timeout, drv, NULL); + return 0; +} + + +static int wpa_driver_test_probe_req_report(void *priv, int report) +{ + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; + wpa_printf(MSG_DEBUG, "%s(report=%d)", __func__, report); + drv->probe_req_report = report; + return 0; +} + + const struct wpa_driver_ops wpa_driver_test_ops = { "test", "wpa_supplicant test driver", - wpa_driver_test_get_bssid, - wpa_driver_test_get_ssid, - wpa_driver_test_set_wpa, - wpa_driver_test_set_key, - NULL /* init */, - wpa_driver_test_deinit, - wpa_driver_test_set_param, - NULL /* set_countermeasures */, - NULL /* set_drop_unencrypted */, - wpa_driver_test_scan, - NULL /* get_scan_results */, - wpa_driver_test_deauthenticate, - wpa_driver_test_disassociate, - wpa_driver_test_associate, - NULL /* set_auth_alg */, - NULL /* add_pmkid */, - NULL /* remove_pmkid */, - NULL /* flush_pmkid */, - wpa_driver_test_get_capa, - NULL /* poll */, - NULL /* get_ifname */, - wpa_driver_test_get_mac_addr, - wpa_driver_test_send_eapol, - NULL /* set_operstate */, - wpa_driver_test_mlme_setprotection, -#ifdef CONFIG_CLIENT_MLME - wpa_driver_test_get_hw_feature_data, - wpa_driver_test_set_channel, - wpa_driver_test_set_ssid, - wpa_driver_test_set_bssid, - wpa_driver_test_send_mlme, - wpa_driver_test_mlme_add_sta, - wpa_driver_test_mlme_remove_sta, -#else /* CONFIG_CLIENT_MLME */ - NULL /* get_hw_feature_data */, - NULL /* set_channel */, - NULL /* set_ssid */, - NULL /* set_bssid */, - NULL /* send_mlme */, - NULL /* mlme_add_sta */, - NULL /* mlme_remove_sta */, -#endif /* CONFIG_CLIENT_MLME */ - NULL /* update_ft_ies */, - NULL /* send_ft_action */, - wpa_driver_test_get_scan_results2, - wpa_driver_test_set_probe_req_ie, - NULL /* set_mode */, - NULL /* set_country */, - wpa_driver_test_global_init, - wpa_driver_test_global_deinit, - wpa_driver_test_init2, - wpa_driver_test_get_interfaces + .hapd_init = test_driver_init, + .hapd_deinit = wpa_driver_test_deinit, + .hapd_send_eapol = test_driver_send_eapol, + .send_mlme = wpa_driver_test_send_mlme, + .set_generic_elem = test_driver_set_generic_elem, + .sta_deauth = test_driver_sta_deauth, + .sta_disassoc = test_driver_sta_disassoc, + .get_hw_feature_data = wpa_driver_test_get_hw_feature_data, + .if_add = test_driver_if_add, + .if_remove = test_driver_if_remove, + .valid_bss_mask = test_driver_valid_bss_mask, + .hapd_set_ssid = test_driver_set_ssid, + .set_privacy = test_driver_set_privacy, + .set_sta_vlan = test_driver_set_sta_vlan, + .sta_add = test_driver_sta_add, + .send_ether = test_driver_send_ether, + .set_ap_wps_ie = test_driver_set_ap_wps_ie, + .get_bssid = wpa_driver_test_get_bssid, + .get_ssid = wpa_driver_test_get_ssid, + .set_key = wpa_driver_test_set_key, + .deinit = wpa_driver_test_deinit, + .set_param = wpa_driver_test_set_param, + .deauthenticate = wpa_driver_test_deauthenticate, + .disassociate = wpa_driver_test_disassociate, + .associate = wpa_driver_test_associate, + .get_capa = wpa_driver_test_get_capa, + .get_mac_addr = wpa_driver_test_get_mac_addr, + .send_eapol = wpa_driver_test_send_eapol, + .mlme_setprotection = wpa_driver_test_mlme_setprotection, + .set_channel = wpa_driver_test_set_channel, + .set_ssid = wpa_driver_test_set_ssid, + .set_bssid = wpa_driver_test_set_bssid, + .mlme_add_sta = wpa_driver_test_mlme_add_sta, + .mlme_remove_sta = wpa_driver_test_mlme_remove_sta, + .get_scan_results2 = wpa_driver_test_get_scan_results2, + .global_init = wpa_driver_test_global_init, + .global_deinit = wpa_driver_test_global_deinit, + .init2 = wpa_driver_test_init2, + .get_interfaces = wpa_driver_test_get_interfaces, + .scan2 = wpa_driver_test_scan, + .set_freq = wpa_driver_test_set_freq, + .send_action = wpa_driver_test_send_action, + .remain_on_channel = wpa_driver_test_remain_on_channel, + .cancel_remain_on_channel = wpa_driver_test_cancel_remain_on_channel, + .probe_req_report = wpa_driver_test_probe_req_report, }; diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c index e771d37dedb78..2614f23093fac 100644 --- a/src/drivers/driver_wext.c +++ b/src/drivers/driver_wext.c @@ -1,6 +1,6 @@ /* - * WPA Supplicant - driver interaction with generic Linux Wireless Extensions - * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> + * Driver interaction with generic Linux Wireless Extensions + * Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -20,82 +20,26 @@ #include "includes.h" #include <sys/ioctl.h> +#include <sys/stat.h> #include <net/if_arp.h> #include "wireless_copy.h" #include "common.h" -#include "driver.h" #include "eloop.h" +#include "common/ieee802_11_defs.h" +#include "common/wpa_common.h" #include "priv_netlink.h" +#include "netlink.h" +#include "linux_ioctl.h" +#include "driver.h" #include "driver_wext.h" -#include "ieee802_11_defs.h" -#include "wpa_common.h" static int wpa_driver_wext_flush_pmkid(void *priv); static int wpa_driver_wext_get_range(void *priv); -static void wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv); +static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv); static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv); - - -static int wpa_driver_wext_send_oper_ifla(struct wpa_driver_wext_data *drv, - int linkmode, int operstate) -{ - struct { - struct nlmsghdr hdr; - struct ifinfomsg ifinfo; - char opts[16]; - } req; - struct rtattr *rta; - static int nl_seq; - ssize_t ret; - - os_memset(&req, 0, sizeof(req)); - - req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); - req.hdr.nlmsg_type = RTM_SETLINK; - req.hdr.nlmsg_flags = NLM_F_REQUEST; - req.hdr.nlmsg_seq = ++nl_seq; - req.hdr.nlmsg_pid = 0; - - req.ifinfo.ifi_family = AF_UNSPEC; - req.ifinfo.ifi_type = 0; - req.ifinfo.ifi_index = drv->ifindex; - req.ifinfo.ifi_flags = 0; - req.ifinfo.ifi_change = 0; - - if (linkmode != -1) { - rta = aliasing_hide_typecast( - ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)), - struct rtattr); - rta->rta_type = IFLA_LINKMODE; - rta->rta_len = RTA_LENGTH(sizeof(char)); - *((char *) RTA_DATA(rta)) = linkmode; - req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + - RTA_LENGTH(sizeof(char)); - } - if (operstate != -1) { - rta = (struct rtattr *) - ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)); - rta->rta_type = IFLA_OPERSTATE; - rta->rta_len = RTA_LENGTH(sizeof(char)); - *((char *) RTA_DATA(rta)) = operstate; - req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + - RTA_LENGTH(sizeof(char)); - } - - wpa_printf(MSG_DEBUG, "WEXT: Operstate: linkmode=%d, operstate=%d", - linkmode, operstate); - - ret = send(drv->event_sock, &req, req.hdr.nlmsg_len, 0); - if (ret < 0) { - wpa_printf(MSG_DEBUG, "WEXT: Sending operstate IFLA failed: " - "%s (assume operstate is not supported)", - strerror(errno)); - } - - return ret < 0 ? -1 : 0; -} +static int wpa_driver_wext_set_auth_alg(void *priv, int auth_alg); int wpa_driver_wext_set_auth_param(struct wpa_driver_wext_data *drv, @@ -304,6 +248,7 @@ wpa_driver_wext_event_wireless_custom(void *ctx, char *custom) } else if (os_strncmp(custom, "ASSOCINFO(ReqIEs=", 17) == 0) { char *spos; int bytes; + u8 *req_ies = NULL, *resp_ies = NULL; spos = custom + 17; @@ -312,12 +257,12 @@ wpa_driver_wext_event_wireless_custom(void *ctx, char *custom) return; bytes /= 2; - data.assoc_info.req_ies = os_malloc(bytes); - if (data.assoc_info.req_ies == NULL) - return; - + req_ies = os_malloc(bytes); + if (req_ies == NULL || + hexstr2bin(spos, req_ies, bytes) < 0) + goto done; + data.assoc_info.req_ies = req_ies; data.assoc_info.req_ies_len = bytes; - hexstr2bin(spos, data.assoc_info.req_ies, bytes); spos += bytes * 2; @@ -332,19 +277,19 @@ wpa_driver_wext_event_wireless_custom(void *ctx, char *custom) goto done; bytes /= 2; - data.assoc_info.resp_ies = os_malloc(bytes); - if (data.assoc_info.resp_ies == NULL) + resp_ies = os_malloc(bytes); + if (resp_ies == NULL || + hexstr2bin(spos, resp_ies, bytes) < 0) goto done; - + data.assoc_info.resp_ies = resp_ies; data.assoc_info.resp_ies_len = bytes; - hexstr2bin(spos, data.assoc_info.resp_ies, bytes); } wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data); done: - os_free(data.assoc_info.resp_ies); - os_free(data.assoc_info.req_ies); + os_free(resp_ies); + os_free(req_ies); #ifdef CONFIG_PEERKEY } else if (os_strncmp(custom, "STKSTART.request=", 17) == 0) { if (hwaddr_aton(custom + 17, data.stkstart.peer)) { @@ -460,24 +405,24 @@ static void wpa_driver_wext_event_assoc_ies(struct wpa_driver_wext_data *drv) os_memset(&data, 0, sizeof(data)); if (drv->assoc_req_ies) { data.assoc_info.req_ies = drv->assoc_req_ies; - drv->assoc_req_ies = NULL; data.assoc_info.req_ies_len = drv->assoc_req_ies_len; } if (drv->assoc_resp_ies) { data.assoc_info.resp_ies = drv->assoc_resp_ies; - drv->assoc_resp_ies = NULL; data.assoc_info.resp_ies_len = drv->assoc_resp_ies_len; } wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &data); - os_free(data.assoc_info.req_ies); - os_free(data.assoc_info.resp_ies); + os_free(drv->assoc_req_ies); + drv->assoc_req_ies = NULL; + os_free(drv->assoc_resp_ies); + drv->assoc_resp_ies = NULL; } static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv, - void *ctx, char *data, int len) + char *data, int len) { struct iw_event iwe_buf, *iwe = &iwe_buf; char *pos, *end, *custom, *buf; @@ -525,12 +470,13 @@ static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv, drv->assoc_req_ies = NULL; os_free(drv->assoc_resp_ies); drv->assoc_resp_ies = NULL; - wpa_supplicant_event(ctx, EVENT_DISASSOC, + wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); } else { wpa_driver_wext_event_assoc_ies(drv); - wpa_supplicant_event(ctx, EVENT_ASSOC, NULL); + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, + NULL); } break; case IWEVMICHAELMICFAILURE: @@ -540,7 +486,7 @@ static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv, return; } wpa_driver_wext_event_wireless_michaelmicfailure( - ctx, custom, iwe->u.data.length); + drv->ctx, custom, iwe->u.data.length); break; case IWEVCUSTOM: if (custom + iwe->u.data.length > end) { @@ -553,14 +499,15 @@ static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv, return; os_memcpy(buf, custom, iwe->u.data.length); buf[iwe->u.data.length] = '\0'; - wpa_driver_wext_event_wireless_custom(ctx, buf); + wpa_driver_wext_event_wireless_custom(drv->ctx, buf); os_free(buf); break; case SIOCGIWSCAN: drv->scan_complete_events = 1; eloop_cancel_timeout(wpa_driver_wext_scan_timeout, - drv, ctx); - wpa_supplicant_event(ctx, EVENT_SCAN_RESULTS, NULL); + drv, drv->ctx); + wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, + NULL); break; case IWEVASSOCREQIE: if (custom + iwe->u.data.length > end) { @@ -597,8 +544,7 @@ static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv, static void wpa_driver_wext_event_link(struct wpa_driver_wext_data *drv, - void *ctx, char *buf, size_t len, - int del) + char *buf, size_t len, int del) { union wpa_event_data event; @@ -621,26 +567,18 @@ static void wpa_driver_wext_event_link(struct wpa_driver_wext_data *drv, drv->if_removed = 0; } - wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event); + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event); } static int wpa_driver_wext_own_ifname(struct wpa_driver_wext_data *drv, - struct nlmsghdr *h) + u8 *buf, size_t len) { - struct ifinfomsg *ifi; - int attrlen, nlmsg_len, rta_len; + int attrlen, rta_len; struct rtattr *attr; - ifi = NLMSG_DATA(h); - - nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); - - attrlen = h->nlmsg_len - nlmsg_len; - if (attrlen < 0) - return 0; - - attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); + attrlen = len; + attr = (struct rtattr *) buf; rta_len = RTA_ALIGN(sizeof(struct rtattr)); while (RTA_OK(attr, attrlen)) { @@ -659,12 +597,12 @@ static int wpa_driver_wext_own_ifname(struct wpa_driver_wext_data *drv, static int wpa_driver_wext_own_ifindex(struct wpa_driver_wext_data *drv, - int ifindex, struct nlmsghdr *h) + int ifindex, u8 *buf, size_t len) { if (drv->ifindex == ifindex || drv->ifindex2 == ifindex) return 1; - if (drv->if_removed && wpa_driver_wext_own_ifname(drv, h)) { + if (drv->if_removed && wpa_driver_wext_own_ifname(drv, buf, len)) { drv->ifindex = if_nametoindex(drv->ifname); wpa_printf(MSG_DEBUG, "WEXT: Update ifindex for a removed " "interface"); @@ -676,20 +614,14 @@ static int wpa_driver_wext_own_ifindex(struct wpa_driver_wext_data *drv, } -static void wpa_driver_wext_event_rtm_newlink(struct wpa_driver_wext_data *drv, - void *ctx, struct nlmsghdr *h, - size_t len) +static void wpa_driver_wext_event_rtm_newlink(void *ctx, struct ifinfomsg *ifi, + u8 *buf, size_t len) { - struct ifinfomsg *ifi; - int attrlen, nlmsg_len, rta_len; - struct rtattr * attr; - - if (len < sizeof(*ifi)) - return; - - ifi = NLMSG_DATA(h); + struct wpa_driver_wext_data *drv = ctx; + int attrlen, rta_len; + struct rtattr *attr; - if (!wpa_driver_wext_own_ifindex(drv, ifi->ifi_index, h)) { + if (!wpa_driver_wext_own_ifindex(drv, ifi->ifi_index, buf, len)) { wpa_printf(MSG_DEBUG, "Ignore event for foreign ifindex %d", ifi->ifi_index); return; @@ -711,24 +643,20 @@ static void wpa_driver_wext_event_rtm_newlink(struct wpa_driver_wext_data *drv, if (drv->operstate == 1 && (ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP && !(ifi->ifi_flags & IFF_RUNNING)) - wpa_driver_wext_send_oper_ifla(drv, -1, IF_OPER_UP); - - nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); - - attrlen = h->nlmsg_len - nlmsg_len; - if (attrlen < 0) - return; + netlink_send_oper_ifla(drv->netlink, drv->ifindex, + -1, IF_OPER_UP); - attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); + attrlen = len; + attr = (struct rtattr *) buf; rta_len = RTA_ALIGN(sizeof(struct rtattr)); while (RTA_OK(attr, attrlen)) { if (attr->rta_type == IFLA_WIRELESS) { wpa_driver_wext_event_wireless( - drv, ctx, ((char *) attr) + rta_len, + drv, ((char *) attr) + rta_len, attr->rta_len - rta_len); } else if (attr->rta_type == IFLA_IFNAME) { - wpa_driver_wext_event_link(drv, ctx, + wpa_driver_wext_event_link(drv, ((char *) attr) + rta_len, attr->rta_len - rta_len, 0); } @@ -737,31 +665,20 @@ static void wpa_driver_wext_event_rtm_newlink(struct wpa_driver_wext_data *drv, } -static void wpa_driver_wext_event_rtm_dellink(struct wpa_driver_wext_data *drv, - void *ctx, struct nlmsghdr *h, - size_t len) +static void wpa_driver_wext_event_rtm_dellink(void *ctx, struct ifinfomsg *ifi, + u8 *buf, size_t len) { - struct ifinfomsg *ifi; - int attrlen, nlmsg_len, rta_len; - struct rtattr * attr; - - if (len < sizeof(*ifi)) - return; - - ifi = NLMSG_DATA(h); - - nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); - - attrlen = h->nlmsg_len - nlmsg_len; - if (attrlen < 0) - return; + struct wpa_driver_wext_data *drv = ctx; + int attrlen, rta_len; + struct rtattr *attr; - attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); + attrlen = len; + attr = (struct rtattr *) buf; rta_len = RTA_ALIGN(sizeof(struct rtattr)); while (RTA_OK(attr, attrlen)) { if (attr->rta_type == IFLA_IFNAME) { - wpa_driver_wext_event_link(drv, ctx, + wpa_driver_wext_event_link(drv, ((char *) attr) + rta_len, attr->rta_len - rta_len, 1); } @@ -770,129 +687,6 @@ static void wpa_driver_wext_event_rtm_dellink(struct wpa_driver_wext_data *drv, } -static void wpa_driver_wext_event_receive(int sock, void *eloop_ctx, - void *sock_ctx) -{ - char buf[8192]; - int left; - struct sockaddr_nl from; - socklen_t fromlen; - struct nlmsghdr *h; - int max_events = 10; - -try_again: - fromlen = sizeof(from); - left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, - (struct sockaddr *) &from, &fromlen); - if (left < 0) { - if (errno != EINTR && errno != EAGAIN) - perror("recvfrom(netlink)"); - return; - } - - h = (struct nlmsghdr *) buf; - while (left >= (int) sizeof(*h)) { - int len, plen; - - len = h->nlmsg_len; - plen = len - sizeof(*h); - if (len > left || plen < 0) { - wpa_printf(MSG_DEBUG, "Malformed netlink message: " - "len=%d left=%d plen=%d", - len, left, plen); - break; - } - - switch (h->nlmsg_type) { - case RTM_NEWLINK: - wpa_driver_wext_event_rtm_newlink(eloop_ctx, sock_ctx, - h, plen); - break; - case RTM_DELLINK: - wpa_driver_wext_event_rtm_dellink(eloop_ctx, sock_ctx, - h, plen); - break; - } - - len = NLMSG_ALIGN(len); - left -= len; - h = (struct nlmsghdr *) ((char *) h + len); - } - - if (left > 0) { - wpa_printf(MSG_DEBUG, "%d extra bytes in the end of netlink " - "message", left); - } - - if (--max_events > 0) { - /* - * Try to receive all events in one eloop call in order to - * limit race condition on cases where AssocInfo event, Assoc - * event, and EAPOL frames are received more or less at the - * same time. We want to process the event messages first - * before starting EAPOL processing. - */ - goto try_again; - } -} - - -static int wpa_driver_wext_get_ifflags_ifname(struct wpa_driver_wext_data *drv, - const char *ifname, int *flags) -{ - struct ifreq ifr; - - os_memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); - if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { - perror("ioctl[SIOCGIFFLAGS]"); - return -1; - } - *flags = ifr.ifr_flags & 0xffff; - return 0; -} - - -/** - * wpa_driver_wext_get_ifflags - Get interface flags (SIOCGIFFLAGS) - * @drv: driver_wext private data - * @flags: Pointer to returned flags value - * Returns: 0 on success, -1 on failure - */ -int wpa_driver_wext_get_ifflags(struct wpa_driver_wext_data *drv, int *flags) -{ - return wpa_driver_wext_get_ifflags_ifname(drv, drv->ifname, flags); -} - - -static int wpa_driver_wext_set_ifflags_ifname(struct wpa_driver_wext_data *drv, - const char *ifname, int flags) -{ - struct ifreq ifr; - - os_memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); - ifr.ifr_flags = flags & 0xffff; - if (ioctl(drv->ioctl_sock, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { - perror("SIOCSIFFLAGS"); - return -1; - } - return 0; -} - - -/** - * wpa_driver_wext_set_ifflags - Set interface flags (SIOCSIFFLAGS) - * @drv: driver_wext private data - * @flags: New value for flags - * Returns: 0 on success, -1 on failure - */ -int wpa_driver_wext_set_ifflags(struct wpa_driver_wext_data *drv, int flags) -{ - return wpa_driver_wext_set_ifflags_ifname(drv, drv->ifname, flags); -} - - /** * wpa_driver_wext_init - Initialize WE driver interface * @ctx: context to be used when calling wpa_supplicant functions, @@ -902,9 +696,10 @@ int wpa_driver_wext_set_ifflags(struct wpa_driver_wext_data *drv, int flags) */ void * wpa_driver_wext_init(void *ctx, const char *ifname) { - int s; - struct sockaddr_nl local; struct wpa_driver_wext_data *drv; + struct netlink_config *cfg; + char path[128]; + struct stat buf; drv = os_zalloc(sizeof(*drv)); if (drv == NULL) @@ -912,66 +707,53 @@ void * wpa_driver_wext_init(void *ctx, const char *ifname) drv->ctx = ctx; os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + os_snprintf(path, sizeof(path), "/sys/class/net/%s/phy80211", ifname); + if (stat(path, &buf) == 0) { + wpa_printf(MSG_DEBUG, "WEXT: cfg80211-based driver detected"); + drv->cfg80211 = 1; + } + drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); if (drv->ioctl_sock < 0) { perror("socket(PF_INET,SOCK_DGRAM)"); - os_free(drv); - return NULL; + goto err1; } - s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - if (s < 0) { - perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)"); - close(drv->ioctl_sock); - os_free(drv); - return NULL; + cfg = os_zalloc(sizeof(*cfg)); + if (cfg == NULL) + goto err1; + cfg->ctx = drv; + cfg->newlink_cb = wpa_driver_wext_event_rtm_newlink; + cfg->dellink_cb = wpa_driver_wext_event_rtm_dellink; + drv->netlink = netlink_init(cfg); + if (drv->netlink == NULL) { + os_free(cfg); + goto err2; } - os_memset(&local, 0, sizeof(local)); - local.nl_family = AF_NETLINK; - local.nl_groups = RTMGRP_LINK; - if (bind(s, (struct sockaddr *) &local, sizeof(local)) < 0) { - perror("bind(netlink)"); - close(s); - close(drv->ioctl_sock); - os_free(drv); - return NULL; - } - - eloop_register_read_sock(s, wpa_driver_wext_event_receive, drv, ctx); - drv->event_sock = s; - drv->mlme_sock = -1; - wpa_driver_wext_finish_drv_init(drv); + if (wpa_driver_wext_finish_drv_init(drv) < 0) + goto err3; + + wpa_driver_wext_set_auth_param(drv, IW_AUTH_WPA_ENABLED, 1); return drv; + +err3: + netlink_deinit(drv->netlink); +err2: + close(drv->ioctl_sock); +err1: + os_free(drv); + return NULL; } -static void wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv) +static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv) { - int flags; - - if (wpa_driver_wext_get_ifflags(drv, &flags) != 0) - printf("Could not get interface '%s' flags\n", drv->ifname); - else if (!(flags & IFF_UP)) { - if (wpa_driver_wext_set_ifflags(drv, flags | IFF_UP) != 0) { - printf("Could not set interface '%s' UP\n", - drv->ifname); - } else { - /* - * Wait some time to allow driver to initialize before - * starting configuring the driver. This seems to be - * needed at least some drivers that load firmware etc. - * when the interface is set up. - */ - wpa_printf(MSG_DEBUG, "Interface %s set UP - waiting " - "a second for the driver to complete " - "initialization", drv->ifname); - sleep(1); - } - } + if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1) < 0) + return -1; /* * Make sure that the driver does not have any obsolete PMKID entries. @@ -979,7 +761,9 @@ static void wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv) wpa_driver_wext_flush_pmkid(drv); if (wpa_driver_wext_set_mode(drv, 0) < 0) { - printf("Could not configure driver to use managed mode\n"); + wpa_printf(MSG_DEBUG, "Could not configure driver to use " + "managed mode"); + /* Try to use it anyway */ } wpa_driver_wext_get_range(drv); @@ -1008,7 +792,10 @@ static void wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv) wpa_driver_wext_alternative_ifindex(drv, ifname2); } - wpa_driver_wext_send_oper_ifla(drv, 1, IF_OPER_DORMANT); + netlink_send_oper_ifla(drv->netlink, drv->ifindex, + 1, IF_OPER_DORMANT); + + return 0; } @@ -1022,7 +809,8 @@ static void wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv) void wpa_driver_wext_deinit(void *priv) { struct wpa_driver_wext_data *drv = priv; - int flags; + + wpa_driver_wext_set_auth_param(drv, IW_AUTH_WPA_ENABLED, 0); eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv, drv->ctx); @@ -1032,16 +820,14 @@ void wpa_driver_wext_deinit(void *priv) */ wpa_driver_wext_disconnect(drv); - wpa_driver_wext_send_oper_ifla(priv, 0, IF_OPER_UP); + netlink_send_oper_ifla(drv->netlink, drv->ifindex, 0, IF_OPER_UP); + netlink_deinit(drv->netlink); - eloop_unregister_read_sock(drv->event_sock); if (drv->mlme_sock >= 0) eloop_unregister_read_sock(drv->mlme_sock); - if (wpa_driver_wext_get_ifflags(drv, &flags) == 0) - (void) wpa_driver_wext_set_ifflags(drv, flags & ~IFF_UP); + (void) linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 0); - close(drv->event_sock); close(drv->ioctl_sock); if (drv->mlme_sock >= 0) close(drv->mlme_sock); @@ -1069,18 +855,17 @@ void wpa_driver_wext_scan_timeout(void *eloop_ctx, void *timeout_ctx) /** * wpa_driver_wext_scan - Request the driver to initiate scan * @priv: Pointer to private wext data from wpa_driver_wext_init() - * @ssid: Specific SSID to scan for (ProbeReq) or %NULL to scan for - * all SSIDs (either active scan with broadcast SSID or passive - * scan - * @ssid_len: Length of the SSID + * @param: Scan parameters (specific SSID to scan for (ProbeReq), etc.) * Returns: 0 on success, -1 on failure */ -int wpa_driver_wext_scan(void *priv, const u8 *ssid, size_t ssid_len) +int wpa_driver_wext_scan(void *priv, struct wpa_driver_scan_params *params) { struct wpa_driver_wext_data *drv = priv; struct iwreq iwr; int ret = 0, timeout; struct iw_scan_req req; + const u8 *ssid = params->ssids[0].ssid; + size_t ssid_len = params->ssids[0].ssid_len; if (ssid_len > IW_ESSID_MAX_SIZE) { wpa_printf(MSG_DEBUG, "%s: too long SSID (%lu)", @@ -1261,6 +1046,14 @@ static void wext_get_scan_qual(struct iw_event *iwe, res->res.qual = iwe->u.qual.qual; res->res.noise = iwe->u.qual.noise; res->res.level = iwe->u.qual.level; + if (iwe->u.qual.updated & IW_QUAL_QUAL_INVALID) + res->res.flags |= WPA_SCAN_QUAL_INVALID; + if (iwe->u.qual.updated & IW_QUAL_LEVEL_INVALID) + res->res.flags |= WPA_SCAN_LEVEL_INVALID; + if (iwe->u.qual.updated & IW_QUAL_NOISE_INVALID) + res->res.flags |= WPA_SCAN_NOISE_INVALID; + if (iwe->u.qual.updated & IW_QUAL_DBM) + res->res.flags |= WPA_SCAN_LEVEL_DBM; } @@ -1349,8 +1142,9 @@ static void wext_get_scan_custom(struct iw_event *iwe, tmp = os_realloc(res->ie, res->ie_len + bytes); if (tmp == NULL) return; - hexstr2bin(spos, tmp + res->ie_len, bytes); res->ie = tmp; + if (hexstr2bin(spos, tmp + res->ie_len, bytes) < 0) + return; res->ie_len += bytes; } else if (clen > 7 && os_strncmp(custom, "rsn_ie=", 7) == 0) { char *spos; @@ -1363,8 +1157,9 @@ static void wext_get_scan_custom(struct iw_event *iwe, tmp = os_realloc(res->ie, res->ie_len + bytes); if (tmp == NULL) return; - hexstr2bin(spos, tmp + res->ie_len, bytes); res->ie = tmp; + if (hexstr2bin(spos, tmp + res->ie_len, bytes) < 0) + return; res->ie_len += bytes; } else if (clen > 4 && os_strncmp(custom, "tsf=", 4) == 0) { char *spos; @@ -1377,7 +1172,10 @@ static void wext_get_scan_custom(struct iw_event *iwe, return; } bytes /= 2; - hexstr2bin(spos, bin, bytes); + if (hexstr2bin(spos, bin, bytes) < 0) { + wpa_printf(MSG_DEBUG, "WEXT: Invalid TSF value"); + return; + } res->res.tsf += WPA_GET_BE64(bin); } } @@ -1619,6 +1417,7 @@ static int wpa_driver_wext_get_range(void *priv) drv->capa.auth = WPA_DRIVER_AUTH_OPEN | WPA_DRIVER_AUTH_SHARED | WPA_DRIVER_AUTH_LEAP; + drv->capa.max_scan_ssids = 1; wpa_printf(MSG_DEBUG, " capabilities: key_mgmt 0x%x enc 0x%x " "flags 0x%x", @@ -1633,16 +1432,6 @@ static int wpa_driver_wext_get_range(void *priv) } -static int wpa_driver_wext_set_wpa(void *priv, int enabled) -{ - struct wpa_driver_wext_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - return wpa_driver_wext_set_auth_param(drv, IW_AUTH_WPA_ENABLED, - enabled); -} - - static int wpa_driver_wext_set_psk(struct wpa_driver_wext_data *drv, const u8 *psk) { @@ -1680,7 +1469,7 @@ static int wpa_driver_wext_set_psk(struct wpa_driver_wext_data *drv, } -static int wpa_driver_wext_set_key_ext(void *priv, wpa_alg alg, +static int wpa_driver_wext_set_key_ext(void *priv, enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, @@ -1800,7 +1589,7 @@ static int wpa_driver_wext_set_key_ext(void *priv, wpa_alg alg, * This function uses SIOCSIWENCODEEXT by default, but tries to use * SIOCSIWENCODE if the extended ioctl fails when configuring a WEP key. */ -int wpa_driver_wext_set_key(void *priv, wpa_alg alg, +int wpa_driver_wext_set_key(const char *ifname, void *priv, enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len) @@ -1929,17 +1718,32 @@ static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv) } if (iwr.u.mode == IW_MODE_INFRA) { + if (drv->cfg80211) { + /* + * cfg80211 supports SIOCSIWMLME commands, so there is + * no need for the random SSID hack, but clear the + * BSSID and SSID. + */ + if (wpa_driver_wext_set_bssid(drv, null_bssid) < 0 || + wpa_driver_wext_set_ssid(drv, (u8 *) "", 0) < 0) { + wpa_printf(MSG_DEBUG, "WEXT: Failed to clear " + "to disconnect"); + } + return; + } /* * Clear the BSSID selection and set a random SSID to make sure * the driver will not be trying to associate with something * even if it does not understand SIOCSIWMLME commands (or * tries to associate automatically after deauth/disassoc). */ - wpa_driver_wext_set_bssid(drv, null_bssid); - for (i = 0; i < 32; i++) ssid[i] = rand() & 0xFF; - wpa_driver_wext_set_ssid(drv, ssid, 32); + if (wpa_driver_wext_set_bssid(drv, null_bssid) < 0 || + wpa_driver_wext_set_ssid(drv, ssid, 32) < 0) { + wpa_printf(MSG_DEBUG, "WEXT: Failed to set bogus " + "BSSID/SSID to disconnect"); + } } } @@ -2051,9 +1855,9 @@ wpa_driver_wext_auth_alg_fallback(struct wpa_driver_wext_data *drv, if (!drv->use_crypt) { iwr.u.encoding.flags |= IW_ENCODE_DISABLED; } else { - if (params->auth_alg & AUTH_ALG_OPEN_SYSTEM) + if (params->auth_alg & WPA_AUTH_ALG_OPEN) iwr.u.encoding.flags |= IW_ENCODE_OPEN; - if (params->auth_alg & AUTH_ALG_SHARED_KEY) + if (params->auth_alg & WPA_AUTH_ALG_SHARED) iwr.u.encoding.flags |= IW_ENCODE_RESTRICTED; } @@ -2076,6 +1880,22 @@ int wpa_driver_wext_associate(void *priv, wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + if (drv->cfg80211) { + /* + * Stop cfg80211 from trying to associate before we are done + * with all parameters. + */ + wpa_driver_wext_set_ssid(drv, (u8 *) "", 0); + } + + if (wpa_driver_wext_set_drop_unencrypted(drv, params->drop_unencrypted) + < 0) + ret = -1; + if (wpa_driver_wext_set_auth_alg(drv, params->auth_alg) < 0) + ret = -1; + if (wpa_driver_wext_set_mode(drv, params->mode) < 0) + ret = -1; + /* * If the driver did not support SIOCSIWAUTH, fallback to * SIOCSIWENCODE here. @@ -2155,11 +1975,15 @@ int wpa_driver_wext_associate(void *priv, #endif /* CONFIG_IEEE80211W */ if (params->freq && wpa_driver_wext_set_freq(drv, params->freq) < 0) ret = -1; - if (wpa_driver_wext_set_ssid(drv, params->ssid, params->ssid_len) < 0) + if (!drv->cfg80211 && + wpa_driver_wext_set_ssid(drv, params->ssid, params->ssid_len) < 0) ret = -1; if (params->bssid && wpa_driver_wext_set_bssid(drv, params->bssid) < 0) ret = -1; + if (drv->cfg80211 && + wpa_driver_wext_set_ssid(drv, params->ssid, params->ssid_len) < 0) + ret = -1; return ret; } @@ -2170,11 +1994,11 @@ static int wpa_driver_wext_set_auth_alg(void *priv, int auth_alg) struct wpa_driver_wext_data *drv = priv; int algs = 0, res; - if (auth_alg & AUTH_ALG_OPEN_SYSTEM) + if (auth_alg & WPA_AUTH_ALG_OPEN) algs |= IW_AUTH_ALG_OPEN_SYSTEM; - if (auth_alg & AUTH_ALG_SHARED_KEY) + if (auth_alg & WPA_AUTH_ALG_SHARED) algs |= IW_AUTH_ALG_SHARED_KEY; - if (auth_alg & AUTH_ALG_LEAP) + if (auth_alg & WPA_AUTH_ALG_LEAP) algs |= IW_AUTH_ALG_LEAP; if (algs == 0) { /* at least one algorithm should be set */ @@ -2198,7 +2022,7 @@ int wpa_driver_wext_set_mode(void *priv, int mode) { struct wpa_driver_wext_data *drv = priv; struct iwreq iwr; - int ret = -1, flags; + int ret = -1; unsigned int new_mode = mode ? IW_MODE_ADHOC : IW_MODE_INFRA; os_memset(&iwr, 0, sizeof(iwr)); @@ -2228,9 +2052,7 @@ int wpa_driver_wext_set_mode(void *priv, int mode) goto done; } - if (wpa_driver_wext_get_ifflags(drv, &flags) == 0) { - (void) wpa_driver_wext_set_ifflags(drv, flags & ~IFF_UP); - + if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 0) == 0) { /* Try to set the mode again while the interface is down */ iwr.u.mode = new_mode; if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) < 0) @@ -2238,11 +2060,7 @@ int wpa_driver_wext_set_mode(void *priv, int mode) else ret = 0; - /* Ignore return value of get_ifflags to ensure that the device - * is always up like it was before this function was called. - */ - (void) wpa_driver_wext_get_ifflags(drv, &flags); - (void) wpa_driver_wext_set_ifflags(drv, flags | IFF_UP); + (void) linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1); } done: @@ -2338,8 +2156,8 @@ int wpa_driver_wext_set_operstate(void *priv, int state) wpa_printf(MSG_DEBUG, "%s: operstate %d->%d (%s)", __func__, drv->operstate, state, state ? "UP" : "DORMANT"); drv->operstate = state; - return wpa_driver_wext_send_oper_ifla( - drv, -1, state ? IF_OPER_UP : IF_OPER_DORMANT); + return netlink_send_oper_ifla(drv->netlink, drv->ifindex, -1, + state ? IF_OPER_UP : IF_OPER_DORMANT); } @@ -2354,17 +2172,13 @@ const struct wpa_driver_ops wpa_driver_wext_ops = { .desc = "Linux wireless extensions (generic)", .get_bssid = wpa_driver_wext_get_bssid, .get_ssid = wpa_driver_wext_get_ssid, - .set_wpa = wpa_driver_wext_set_wpa, .set_key = wpa_driver_wext_set_key, .set_countermeasures = wpa_driver_wext_set_countermeasures, - .set_drop_unencrypted = wpa_driver_wext_set_drop_unencrypted, - .scan = wpa_driver_wext_scan, + .scan2 = wpa_driver_wext_scan, .get_scan_results2 = wpa_driver_wext_get_scan_results, .deauthenticate = wpa_driver_wext_deauthenticate, .disassociate = wpa_driver_wext_disassociate, - .set_mode = wpa_driver_wext_set_mode, .associate = wpa_driver_wext_associate, - .set_auth_alg = wpa_driver_wext_set_auth_alg, .init = wpa_driver_wext_init, .deinit = wpa_driver_wext_deinit, .add_pmkid = wpa_driver_wext_add_pmkid, diff --git a/src/drivers/driver_wext.h b/src/drivers/driver_wext.h index b89c2cb2fdfa8..602c7e1f689c0 100644 --- a/src/drivers/driver_wext.h +++ b/src/drivers/driver_wext.h @@ -19,7 +19,7 @@ struct wpa_driver_wext_data { void *ctx; - int event_sock; + struct netlink_data *netlink; int ioctl_sock; int mlme_sock; char ifname[IFNAMSIZ + 1]; @@ -43,21 +43,21 @@ struct wpa_driver_wext_data { char mlmedev[IFNAMSIZ + 1]; int scan_complete_events; + + int cfg80211; /* whether driver is using cfg80211 */ }; -int wpa_driver_wext_get_ifflags(struct wpa_driver_wext_data *drv, int *flags); -int wpa_driver_wext_set_ifflags(struct wpa_driver_wext_data *drv, int flags); int wpa_driver_wext_get_bssid(void *priv, u8 *bssid); int wpa_driver_wext_set_bssid(void *priv, const u8 *bssid); int wpa_driver_wext_get_ssid(void *priv, u8 *ssid); int wpa_driver_wext_set_ssid(void *priv, const u8 *ssid, size_t ssid_len); int wpa_driver_wext_set_freq(void *priv, int freq); int wpa_driver_wext_set_mode(void *priv, int mode); -int wpa_driver_wext_set_key(void *priv, wpa_alg alg, +int wpa_driver_wext_set_key(const char *ifname, void *priv, enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len); -int wpa_driver_wext_scan(void *priv, const u8 *ssid, size_t ssid_len); +int wpa_driver_wext_scan(void *priv, struct wpa_driver_scan_params *params); struct wpa_scan_results * wpa_driver_wext_get_scan_results(void *priv); void wpa_driver_wext_scan_timeout(void *eloop_ctx, void *timeout_ctx); diff --git a/src/drivers/driver_wired.c b/src/drivers/driver_wired.c index 098991a1a44d3..2b197f0ab6c72 100644 --- a/src/drivers/driver_wired.c +++ b/src/drivers/driver_wired.c @@ -1,6 +1,7 @@ /* - * WPA Supplicant - wired Ethernet driver interface - * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi> + * Wired Ethernet driver interface + * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004, Gunter Burchardt <tira@isx.de> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -17,27 +18,374 @@ #include <net/if.h> #ifdef __linux__ #include <netpacket/packet.h> +#include <net/if_arp.h> +#include <net/if.h> #endif /* __linux__ */ -#if defined(__FreeBSD__) || defined(__DragonFly__) +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) #include <net/if_dl.h> -#endif /* defined(__FreeBSD__) || defined(__DragonFly__) */ +#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) */ #include "common.h" +#include "eloop.h" #include "driver.h" +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +struct ieee8023_hdr { + u8 dest[6]; + u8 src[6]; + u16 ethertype; +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ static const u8 pae_group_addr[ETH_ALEN] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }; struct wpa_driver_wired_data { + char ifname[IFNAMSIZ + 1]; void *ctx; + + int sock; /* raw packet socket for driver access */ + int dhcp_sock; /* socket for dhcp packets */ + int use_pae_group_addr; + int pf_sock; - char ifname[IFNAMSIZ + 1]; int membership, multi, iff_allmulti, iff_up; }; +/* TODO: detecting new devices should eventually be changed from using DHCP + * snooping to trigger on any packet from a new layer 2 MAC address, e.g., + * based on ebtables, etc. */ + +struct dhcp_message { + u_int8_t op; + u_int8_t htype; + u_int8_t hlen; + u_int8_t hops; + u_int32_t xid; + u_int16_t secs; + u_int16_t flags; + u_int32_t ciaddr; + u_int32_t yiaddr; + u_int32_t siaddr; + u_int32_t giaddr; + u_int8_t chaddr[16]; + u_int8_t sname[64]; + u_int8_t file[128]; + u_int32_t cookie; + u_int8_t options[308]; /* 312 - cookie */ +}; + + +static int wired_multicast_membership(int sock, int ifindex, + const u8 *addr, int add) +{ +#ifdef __linux__ + struct packet_mreq mreq; + + if (sock < 0) + return -1; + + os_memset(&mreq, 0, sizeof(mreq)); + mreq.mr_ifindex = ifindex; + mreq.mr_type = PACKET_MR_MULTICAST; + mreq.mr_alen = ETH_ALEN; + os_memcpy(mreq.mr_address, addr, ETH_ALEN); + + if (setsockopt(sock, SOL_PACKET, + add ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP, + &mreq, sizeof(mreq)) < 0) { + perror("setsockopt"); + return -1; + } + return 0; +#else /* __linux__ */ + return -1; +#endif /* __linux__ */ +} + + +#ifdef __linux__ +static void handle_data(void *ctx, unsigned char *buf, size_t len) +{ +#ifdef HOSTAPD + struct ieee8023_hdr *hdr; + u8 *pos, *sa; + size_t left; + union wpa_event_data event; + + /* must contain at least ieee8023_hdr 6 byte source, 6 byte dest, + * 2 byte ethertype */ + if (len < 14) { + wpa_printf(MSG_MSGDUMP, "handle_data: too short (%lu)", + (unsigned long) len); + return; + } + + hdr = (struct ieee8023_hdr *) buf; + + switch (ntohs(hdr->ethertype)) { + case ETH_P_PAE: + wpa_printf(MSG_MSGDUMP, "Received EAPOL packet"); + sa = hdr->src; + os_memset(&event, 0, sizeof(event)); + event.new_sta.addr = sa; + wpa_supplicant_event(ctx, EVENT_NEW_STA, &event); + + pos = (u8 *) (hdr + 1); + left = len - sizeof(*hdr); + drv_event_eapol_rx(ctx, sa, pos, left); + break; + + default: + wpa_printf(MSG_DEBUG, "Unknown ethertype 0x%04x in data frame", + ntohs(hdr->ethertype)); + break; + } +#endif /* HOSTAPD */ +} + + +static void handle_read(int sock, void *eloop_ctx, void *sock_ctx) +{ + int len; + unsigned char buf[3000]; + + len = recv(sock, buf, sizeof(buf), 0); + if (len < 0) { + perror("recv"); + return; + } + + handle_data(eloop_ctx, buf, len); +} + + +static void handle_dhcp(int sock, void *eloop_ctx, void *sock_ctx) +{ + int len; + unsigned char buf[3000]; + struct dhcp_message *msg; + u8 *mac_address; + union wpa_event_data event; + + len = recv(sock, buf, sizeof(buf), 0); + if (len < 0) { + perror("recv"); + return; + } + + /* must contain at least dhcp_message->chaddr */ + if (len < 44) { + wpa_printf(MSG_MSGDUMP, "handle_dhcp: too short (%d)", len); + return; + } + + msg = (struct dhcp_message *) buf; + mac_address = (u8 *) &(msg->chaddr); + + wpa_printf(MSG_MSGDUMP, "Got DHCP broadcast packet from " MACSTR, + MAC2STR(mac_address)); + + os_memset(&event, 0, sizeof(event)); + event.new_sta.addr = mac_address; + wpa_supplicant_event(eloop_ctx, EVENT_NEW_STA, &event); +} +#endif /* __linux__ */ + + +static int wired_init_sockets(struct wpa_driver_wired_data *drv, u8 *own_addr) +{ +#ifdef __linux__ + struct ifreq ifr; + struct sockaddr_ll addr; + struct sockaddr_in addr2; + int n = 1; + + drv->sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PAE)); + if (drv->sock < 0) { + perror("socket[PF_PACKET,SOCK_RAW]"); + return -1; + } + + if (eloop_register_read_sock(drv->sock, handle_read, drv->ctx, NULL)) { + printf("Could not register read socket\n"); + return -1; + } + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); + if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) { + perror("ioctl(SIOCGIFINDEX)"); + return -1; + } + + os_memset(&addr, 0, sizeof(addr)); + addr.sll_family = AF_PACKET; + addr.sll_ifindex = ifr.ifr_ifindex; + wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d", + addr.sll_ifindex); + + if (bind(drv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("bind"); + return -1; + } + + /* filter multicast address */ + if (wired_multicast_membership(drv->sock, ifr.ifr_ifindex, + pae_group_addr, 1) < 0) { + wpa_printf(MSG_ERROR, "wired: Failed to add multicast group " + "membership"); + return -1; + } + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); + if (ioctl(drv->sock, SIOCGIFHWADDR, &ifr) != 0) { + perror("ioctl(SIOCGIFHWADDR)"); + return -1; + } + + if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { + printf("Invalid HW-addr family 0x%04x\n", + ifr.ifr_hwaddr.sa_family); + return -1; + } + os_memcpy(own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); + + /* setup dhcp listen socket for sta detection */ + if ((drv->dhcp_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { + perror("socket call failed for dhcp"); + return -1; + } + + if (eloop_register_read_sock(drv->dhcp_sock, handle_dhcp, drv->ctx, + NULL)) { + printf("Could not register read socket\n"); + return -1; + } + + os_memset(&addr2, 0, sizeof(addr2)); + addr2.sin_family = AF_INET; + addr2.sin_port = htons(67); + addr2.sin_addr.s_addr = INADDR_ANY; + + if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_REUSEADDR, (char *) &n, + sizeof(n)) == -1) { + perror("setsockopt[SOL_SOCKET,SO_REUSEADDR]"); + return -1; + } + if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_BROADCAST, (char *) &n, + sizeof(n)) == -1) { + perror("setsockopt[SOL_SOCKET,SO_BROADCAST]"); + return -1; + } + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_ifrn.ifrn_name, drv->ifname, IFNAMSIZ); + if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_BINDTODEVICE, + (char *) &ifr, sizeof(ifr)) < 0) { + perror("setsockopt[SOL_SOCKET,SO_BINDTODEVICE]"); + return -1; + } + + if (bind(drv->dhcp_sock, (struct sockaddr *) &addr2, + sizeof(struct sockaddr)) == -1) { + perror("bind"); + return -1; + } + + return 0; +#else /* __linux__ */ + return -1; +#endif /* __linux__ */ +} + + +static int wired_send_eapol(void *priv, const u8 *addr, + const u8 *data, size_t data_len, int encrypt, + const u8 *own_addr) +{ + struct wpa_driver_wired_data *drv = priv; + struct ieee8023_hdr *hdr; + size_t len; + u8 *pos; + int res; + + len = sizeof(*hdr) + data_len; + hdr = os_zalloc(len); + if (hdr == NULL) { + printf("malloc() failed for wired_send_eapol(len=%lu)\n", + (unsigned long) len); + return -1; + } + + os_memcpy(hdr->dest, drv->use_pae_group_addr ? pae_group_addr : addr, + ETH_ALEN); + os_memcpy(hdr->src, own_addr, ETH_ALEN); + hdr->ethertype = htons(ETH_P_PAE); + + pos = (u8 *) (hdr + 1); + os_memcpy(pos, data, data_len); + + res = send(drv->sock, (u8 *) hdr, len, 0); + os_free(hdr); + + if (res < 0) { + perror("wired_send_eapol: send"); + printf("wired_send_eapol - packet len: %lu - failed\n", + (unsigned long) len); + } + + return res; +} + + +static void * wired_driver_hapd_init(struct hostapd_data *hapd, + struct wpa_init_params *params) +{ + struct wpa_driver_wired_data *drv; + + drv = os_zalloc(sizeof(struct wpa_driver_wired_data)); + if (drv == NULL) { + printf("Could not allocate memory for wired driver data\n"); + return NULL; + } + + drv->ctx = hapd; + os_strlcpy(drv->ifname, params->ifname, sizeof(drv->ifname)); + drv->use_pae_group_addr = params->use_pae_group_addr; + + if (wired_init_sockets(drv, params->own_addr)) { + os_free(drv); + return NULL; + } + + return drv; +} + + +static void wired_driver_hapd_deinit(void *priv) +{ + struct wpa_driver_wired_data *drv = priv; + + if (drv->sock >= 0) + close(drv->sock); + + if (drv->dhcp_sock >= 0) + close(drv->dhcp_sock); + + os_free(drv); +} + + static int wpa_driver_wired_get_ssid(void *priv, u8 *ssid) { ssid[0] = 0; @@ -53,6 +401,14 @@ static int wpa_driver_wired_get_bssid(void *priv, u8 *bssid) } +static int wpa_driver_wired_get_capa(void *priv, struct wpa_driver_capa *capa) +{ + os_memset(capa, 0, sizeof(*capa)); + capa->flags = WPA_DRIVER_FLAGS_WIRED; + return 0; +} + + static int wpa_driver_wired_get_ifflags(const char *ifname, int *flags) { struct ifreq ifr; @@ -118,7 +474,7 @@ static int wpa_driver_wired_multi(const char *ifname, const u8 *addr, int add) ifr.ifr_hwaddr.sa_family = AF_UNSPEC; os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN); #endif /* __linux__ */ -#if defined(__FreeBSD__) || defined(__DragonFly__) +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) { struct sockaddr_dl *dlp; dlp = (struct sockaddr_dl *) &ifr.ifr_addr; @@ -128,9 +484,9 @@ static int wpa_driver_wired_multi(const char *ifname, const u8 *addr, int add) dlp->sdl_nlen = 0; dlp->sdl_alen = ETH_ALEN; dlp->sdl_slen = 0; - os_memcpy(LLADDR(dlp), addr, ETH_ALEN); + os_memcpy(LLADDR(dlp), addr, ETH_ALEN); } -#endif /* defined(__FreeBSD__) || defined(__DragonFly__) */ +#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */ #if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) { struct sockaddr *sap; @@ -151,34 +507,6 @@ static int wpa_driver_wired_multi(const char *ifname, const u8 *addr, int add) } -static int wpa_driver_wired_membership(struct wpa_driver_wired_data *drv, - const u8 *addr, int add) -{ -#ifdef __linux__ - struct packet_mreq mreq; - - if (drv->pf_sock == -1) - return -1; - - os_memset(&mreq, 0, sizeof(mreq)); - mreq.mr_ifindex = if_nametoindex(drv->ifname); - mreq.mr_type = PACKET_MR_MULTICAST; - mreq.mr_alen = ETH_ALEN; - os_memcpy(mreq.mr_address, addr, ETH_ALEN); - - if (setsockopt(drv->pf_sock, SOL_PACKET, - add ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP, - &mreq, sizeof(mreq)) < 0) { - perror("setsockopt"); - return -1; - } - return 0; -#else /* __linux__ */ - return -1; -#endif /* __linux__ */ -} - - static void * wpa_driver_wired_init(void *ctx, const char *ifname) { struct wpa_driver_wired_data *drv; @@ -195,16 +523,18 @@ static void * wpa_driver_wired_init(void *ctx, const char *ifname) if (drv->pf_sock < 0) perror("socket(PF_PACKET)"); #else /* __linux__ */ - drv->pf_sock = -1; + drv->pf_sock = -1; #endif /* __linux__ */ - + if (wpa_driver_wired_get_ifflags(ifname, &flags) == 0 && !(flags & IFF_UP) && wpa_driver_wired_set_ifflags(ifname, flags | IFF_UP) == 0) { drv->iff_up = 1; } - if (wpa_driver_wired_membership(drv, pae_group_addr, 1) == 0) { + if (wired_multicast_membership(drv->pf_sock, + if_nametoindex(drv->ifname), + pae_group_addr, 1) == 0) { wpa_printf(MSG_DEBUG, "%s: Added multicast membership with " "packet socket", __func__); drv->membership = 1; @@ -242,7 +572,9 @@ static void wpa_driver_wired_deinit(void *priv) int flags; if (drv->membership && - wpa_driver_wired_membership(drv, pae_group_addr, 0) < 0) { + wired_multicast_membership(drv->pf_sock, + if_nametoindex(drv->ifname), + pae_group_addr, 0) < 0) { wpa_printf(MSG_DEBUG, "%s: Failed to remove PAE multicast " "group (PACKET)", __func__); } @@ -271,16 +603,20 @@ static void wpa_driver_wired_deinit(void *priv) if (drv->pf_sock != -1) close(drv->pf_sock); - + os_free(drv); } const struct wpa_driver_ops wpa_driver_wired_ops = { .name = "wired", - .desc = "wpa_supplicant wired Ethernet driver", + .desc = "Wired Ethernet driver", + .hapd_init = wired_driver_hapd_init, + .hapd_deinit = wired_driver_hapd_deinit, + .hapd_send_eapol = wired_send_eapol, .get_ssid = wpa_driver_wired_get_ssid, .get_bssid = wpa_driver_wired_get_bssid, + .get_capa = wpa_driver_wired_get_capa, .init = wpa_driver_wired_init, .deinit = wpa_driver_wired_deinit, }; diff --git a/src/drivers/drivers.c b/src/drivers/drivers.c index d278797d7de2f..bffbbde72b77b 100644 --- a/src/drivers/drivers.c +++ b/src/drivers/drivers.c @@ -1,5 +1,5 @@ /* - * WPA Supplicant / driver interface list + * Driver interface list * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify @@ -24,9 +24,6 @@ extern struct wpa_driver_ops wpa_driver_nl80211_ops; /* driver_nl80211.c */ #ifdef CONFIG_DRIVER_HOSTAP extern struct wpa_driver_ops wpa_driver_hostap_ops; /* driver_hostap.c */ #endif /* CONFIG_DRIVER_HOSTAP */ -#ifdef CONFIG_DRIVER_PRISM54 -extern struct wpa_driver_ops wpa_driver_prism54_ops; /* driver_prism54.c */ -#endif /* CONFIG_DRIVER_PRISM54 */ #ifdef CONFIG_DRIVER_HERMES extern struct wpa_driver_ops wpa_driver_hermes_ops; /* driver_hermes.c */ #endif /* CONFIG_DRIVER_HERMES */ @@ -64,9 +61,6 @@ extern struct wpa_driver_ops wpa_driver_ralink_ops; /* driver_ralink.c */ #ifdef CONFIG_DRIVER_OSX extern struct wpa_driver_ops wpa_driver_osx_ops; /* driver_osx.m */ #endif /* CONFIG_DRIVER_OSX */ -#ifdef CONFIG_DRIVER_PS3 -extern struct wpa_driver_ops wpa_driver_ps3_ops; /* driver_ps3.c */ -#endif /* CONFIG_DRIVER_PS3 */ #ifdef CONFIG_DRIVER_IPHONE extern struct wpa_driver_ops wpa_driver_iphone_ops; /* driver_iphone.m */ #endif /* CONFIG_DRIVER_IPHONE */ @@ -74,9 +68,15 @@ extern struct wpa_driver_ops wpa_driver_iphone_ops; /* driver_iphone.m */ /* driver_roboswitch.c */ extern struct wpa_driver_ops wpa_driver_roboswitch_ops; #endif /* CONFIG_DRIVER_ROBOSWITCH */ +#ifdef CONFIG_DRIVER_ATHEROS +extern struct wpa_driver_ops wpa_driver_atheros_ops; /* driver_atheros.c */ +#endif /* CONFIG_DRIVER_ATHEROS */ +#ifdef CONFIG_DRIVER_NONE +extern struct wpa_driver_ops wpa_driver_none_ops; /* driver_none.c */ +#endif /* CONFIG_DRIVER_NONE */ -struct wpa_driver_ops *wpa_supplicant_drivers[] = +struct wpa_driver_ops *wpa_drivers[] = { #ifdef CONFIG_DRIVER_WEXT &wpa_driver_wext_ops, @@ -87,9 +87,6 @@ struct wpa_driver_ops *wpa_supplicant_drivers[] = #ifdef CONFIG_DRIVER_HOSTAP &wpa_driver_hostap_ops, #endif /* CONFIG_DRIVER_HOSTAP */ -#ifdef CONFIG_DRIVER_PRISM54 - &wpa_driver_prism54_ops, -#endif /* CONFIG_DRIVER_PRISM54 */ #ifdef CONFIG_DRIVER_HERMES &wpa_driver_hermes_ops, #endif /* CONFIG_DRIVER_HERMES */ @@ -126,14 +123,17 @@ struct wpa_driver_ops *wpa_supplicant_drivers[] = #ifdef CONFIG_DRIVER_OSX &wpa_driver_osx_ops, #endif /* CONFIG_DRIVER_OSX */ -#ifdef CONFIG_DRIVER_PS3 - &wpa_driver_ps3_ops, -#endif /* CONFIG_DRIVER_PS3 */ #ifdef CONFIG_DRIVER_IPHONE &wpa_driver_iphone_ops, #endif /* CONFIG_DRIVER_IPHONE */ #ifdef CONFIG_DRIVER_ROBOSWITCH &wpa_driver_roboswitch_ops, #endif /* CONFIG_DRIVER_ROBOSWITCH */ +#ifdef CONFIG_DRIVER_ATHEROS + &wpa_driver_atheros_ops, +#endif /* CONFIG_DRIVER_ATHEROS */ +#ifdef CONFIG_DRIVER_NONE + &wpa_driver_none_ops, +#endif /* CONFIG_DRIVER_NONE */ NULL }; diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak new file mode 100644 index 0000000000000..b76b22953a38e --- /dev/null +++ b/src/drivers/drivers.mak @@ -0,0 +1,181 @@ +##### COMMON DRIVERS + +ifdef CONFIG_DRIVER_HOSTAP +DRV_CFLAGS += -DCONFIG_DRIVER_HOSTAP +DRV_OBJS += ../src/drivers/driver_hostap.o +CONFIG_WIRELESS_EXTENSION=y +NEED_AP_MLME=y +NEED_NETLINK=y +NEED_LINUX_IOCTL=y +endif + +ifdef CONFIG_DRIVER_WIRED +DRV_CFLAGS += -DCONFIG_DRIVER_WIRED +DRV_OBJS += ../src/drivers/driver_wired.o +endif + +ifdef CONFIG_DRIVER_MADWIFI +DRV_CFLAGS += -DCONFIG_DRIVER_MADWIFI +DRV_OBJS += ../src/drivers/driver_madwifi.o +CONFIG_WIRELESS_EXTENSION=y +CONFIG_L2_PACKET=linux +NEED_NETLINK=y +NEED_LINUX_IOCTL=y +endif + +ifdef CONFIG_DRIVER_NL80211 +DRV_CFLAGS += -DCONFIG_DRIVER_NL80211 +DRV_OBJS += ../src/drivers/driver_nl80211.o +DRV_OBJS += ../src/utils/radiotap.o +NEED_SME=y +NEED_AP_MLME=y +NEED_NETLINK=y +NEED_LINUX_IOCTL=y +DRV_LIBS += -lnl + +ifdef CONFIG_LIBNL20 +DRV_LIBS += -lnl-genl +DRV_CFLAGS += -DCONFIG_LIBNL20 +endif +endif + +ifdef CONFIG_DRIVER_BSD +ifndef CONFIG_L2_PACKET +CONFIG_L2_PACKET=freebsd +endif +DRV_CFLAGS += -DCONFIG_DRIVER_BSD +DRV_OBJS += ../src/drivers/driver_bsd.o +CONFIG_L2_FREEBSD=y +CONFIG_DNET_PCAP=y +endif + +ifdef CONFIG_DRIVER_TEST +DRV_CFLAGS += -DCONFIG_DRIVER_TEST +DRV_OBJS += ../src/drivers/driver_test.o +NEED_AP_MLME=y +endif + +ifdef CONFIG_DRIVER_NONE +DRV_CFLAGS += -DCONFIG_DRIVER_NONE +DRV_OBJS += ../src/drivers/driver_none.o +endif + +##### PURE AP DRIVERS + +ifdef CONFIG_DRIVER_ATHEROS +DRV_AP_CFLAGS += -DCONFIG_DRIVER_ATHEROS +DRV_AP_OBJS += ../src/drivers/driver_atheros.o +CONFIG_L2_PACKET=linux +NEED_NETLINK=y +NEED_LINUX_IOCTL=y +endif + +##### PURE CLIENT DRIVERS + +ifdef CONFIG_DRIVER_WEXT +DRV_WPA_CFLAGS += -DCONFIG_DRIVER_WEXT +CONFIG_WIRELESS_EXTENSION=y +NEED_NETLINK=y +NEED_LINUX_IOCTL=y +endif + +ifdef CONFIG_DRIVER_HERMES +DRV_WPA_CFLAGS += -DCONFIG_DRIVER_HERMES +DRV_WPA_OBJS += ../src/drivers/driver_hermes.o +CONFIG_WIRELESS_EXTENSION=y +endif + +ifdef CONFIG_DRIVER_ATMEL +DRV_WPA_CFLAGS += -DCONFIG_DRIVER_ATMEL +DRV_WPA_OBJS += ../src/drivers/driver_atmel.o +CONFIG_WIRELESS_EXTENSION=y +endif + +ifdef CONFIG_DRIVER_NDISWRAPPER +DRV_WPA_CFLAGS += -DCONFIG_DRIVER_NDISWRAPPER +DRV_WPA_OBJS += ../src/drivers/driver_ndiswrapper.o +CONFIG_WIRELESS_EXTENSION=y +endif + +ifdef CONFIG_DRIVER_RALINK +DRV_WPA_CFLAGS += -DCONFIG_DRIVER_RALINK +DRV_WPA_OBJS += ../src/drivers/driver_ralink.o +NEED_NETLINK=y +NEED_LINUX_IOCTL=y +endif + +ifdef CONFIG_DRIVER_BROADCOM +DRV_WPA_CFLAGS += -DCONFIG_DRIVER_BROADCOM +DRV_WPA_OBJS += ../src/drivers/driver_broadcom.o +endif + +ifdef CONFIG_DRIVER_IPW +DRV_WPA_CFLAGS += -DCONFIG_DRIVER_IPW +DRV_WPA_OBJS += ../src/drivers/driver_ipw.o +CONFIG_WIRELESS_EXTENSION=y +endif + +ifdef CONFIG_DRIVER_NDIS +DRV_WPA_CFLAGS += -DCONFIG_DRIVER_NDIS +DRV_WPA_OBJS += ../src/drivers/driver_ndis.o +ifdef CONFIG_NDIS_EVENTS_INTEGRATED +DRV_WPA_OBJS += ../src/drivers/driver_ndis_.o +endif +ifndef CONFIG_L2_PACKET +CONFIG_L2_PACKET=pcap +endif +CONFIG_WINPCAP=y +ifdef CONFIG_USE_NDISUIO +DRV_WPA_CFLAGS += -DCONFIG_USE_NDISUIO +endif +endif + +ifdef CONFIG_DRIVER_OSX +DRV_WPA_CFLAGS += -DCONFIG_DRIVER_OSX +DRV_WPA_OBJS += ../src/drivers/driver_osx.o +DRV_WPA_LDFLAGS += -framework CoreFoundation +DRV_WPA_LDFLAGS += -F/System/Library/PrivateFrameworks -framework Apple80211 +endif + +ifdef CONFIG_DRIVER_IPHONE +DRV_WPA_CFLAGS += -DCONFIG_DRIVER_IPHONE +DRV_WPA_OBJS += ../src/drivers/driver_iphone.o +DRV_WPA_OBJS += ../src/drivers/MobileApple80211.o +DRV_WPA_LDFLAGS += -framework CoreFoundation +endif + +ifdef CONFIG_DRIVER_ROBOSWITCH +DRV_WPA_CFLAGS += -DCONFIG_DRIVER_ROBOSWITCH +DRV_WPA_OBJS += ../src/drivers/driver_roboswitch.o +endif + +ifdef CONFIG_WIRELESS_EXTENSION +DRV_WPA_CFLAGS += -DCONFIG_WIRELESS_EXTENSION +DRV_WPA_OBJS += ../src/drivers/driver_wext.o +endif + +ifdef NEED_NETLINK +DRV_OBJS += ../src/drivers/netlink.o +endif + +ifdef NEED_LINUX_IOCTL +DRV_OBJS += ../src/drivers/linux_ioctl.o +endif + + +##### COMMON VARS +DRV_BOTH_CFLAGS := $(DRV_CFLAGS) $(DRV_WPA_CFLAGS) $(DRV_AP_CFLAGS) +DRV_WPA_CFLAGS += $(DRV_CFLAGS) +DRV_AP_CFLAGS += $(DRV_CFLAGS) + +DRV_BOTH_LIBS := $(DRV_LIBS) $(DRV_WPA_LIBS) $(DRV_AP_LIBS) +DRV_WPA_LIBS += $(DRV_LIBS) +DRV_AP_LIBS += $(DRV_LIBS) + +DRV_BOTH_OBJS := $(DRV_OBJS) $(DRV_WPA_OBJS) $(DRV_AP_OBJS) +DRV_WPA_OBJS += $(DRV_OBJS) +DRV_AP_OBJS += $(DRV_OBJS) + +DRV_BOTH_LDFLAGS := $(DRV_LDFLAGS) $(DRV_WPA_LDFLAGS) $(DRV_AP_LDFLAGS) +DRV_WPA_LDFLAGS += $(DRV_LDFLAGS) +DRV_AP_LDFLAGS += $(DRV_LDFLAGS) diff --git a/src/drivers/linux_ioctl.c b/src/drivers/linux_ioctl.c new file mode 100644 index 0000000000000..0d6cf5416beec --- /dev/null +++ b/src/drivers/linux_ioctl.c @@ -0,0 +1,198 @@ +/* + * Linux ioctl helper functions for driver wrappers + * Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "utils/includes.h" +#include <sys/ioctl.h> +#include <net/if.h> +#include <net/if_arp.h> + +#include "utils/common.h" +#include "linux_ioctl.h" + + +int linux_set_iface_flags(int sock, const char *ifname, int dev_up) +{ + struct ifreq ifr; + + if (sock < 0) + return -1; + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + + if (ioctl(sock, SIOCGIFFLAGS, &ifr) != 0) { + wpa_printf(MSG_ERROR, "Could not read interface %s flags: %s", + ifname, strerror(errno)); + return -1; + } + + if (dev_up) { + if (ifr.ifr_flags & IFF_UP) + return 0; + ifr.ifr_flags |= IFF_UP; + } else { + if (!(ifr.ifr_flags & IFF_UP)) + return 0; + ifr.ifr_flags &= ~IFF_UP; + } + + if (ioctl(sock, SIOCSIFFLAGS, &ifr) != 0) { + wpa_printf(MSG_ERROR, "Could not set interface %s flags: %s", + ifname, strerror(errno)); + return -1; + } + + return 0; +} + + +int linux_get_ifhwaddr(int sock, const char *ifname, u8 *addr) +{ + struct ifreq ifr; + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + if (ioctl(sock, SIOCGIFHWADDR, &ifr)) { + wpa_printf(MSG_ERROR, "Could not get interface %s hwaddr: %s", + ifname, strerror(errno)); + return -1; + } + + if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { + wpa_printf(MSG_ERROR, "%s: Invalid HW-addr family 0x%04x", + ifname, ifr.ifr_hwaddr.sa_family); + return -1; + } + os_memcpy(addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); + + return 0; +} + + +int linux_set_ifhwaddr(int sock, const char *ifname, const u8 *addr) +{ + struct ifreq ifr; + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN); + ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER; + + if (ioctl(sock, SIOCSIFHWADDR, &ifr)) { + wpa_printf(MSG_DEBUG, "Could not set interface %s hwaddr: %s", + ifname, strerror(errno)); + return -1; + } + + return 0; +} + + +#ifndef SIOCBRADDBR +#define SIOCBRADDBR 0x89a0 +#endif +#ifndef SIOCBRDELBR +#define SIOCBRDELBR 0x89a1 +#endif +#ifndef SIOCBRADDIF +#define SIOCBRADDIF 0x89a2 +#endif +#ifndef SIOCBRDELIF +#define SIOCBRDELIF 0x89a3 +#endif + + +int linux_br_add(int sock, const char *brname) +{ + if (ioctl(sock, SIOCBRADDBR, brname) < 0) { + wpa_printf(MSG_DEBUG, "Could not add bridge %s: %s", + brname, strerror(errno)); + return -1; + } + + return 0; +} + + +int linux_br_del(int sock, const char *brname) +{ + if (ioctl(sock, SIOCBRDELBR, brname) < 0) { + wpa_printf(MSG_DEBUG, "Could not remove bridge %s: %s", + brname, strerror(errno)); + return -1; + } + + return 0; +} + + +int linux_br_add_if(int sock, const char *brname, const char *ifname) +{ + struct ifreq ifr; + int ifindex; + + ifindex = if_nametoindex(ifname); + if (ifindex == 0) + return -1; + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, brname, IFNAMSIZ); + ifr.ifr_ifindex = ifindex; + if (ioctl(sock, SIOCBRADDIF, &ifr) < 0) { + wpa_printf(MSG_DEBUG, "Could not add interface %s into bridge " + "%s: %s", ifname, brname, strerror(errno)); + return -1; + } + + return 0; +} + + +int linux_br_del_if(int sock, const char *brname, const char *ifname) +{ + struct ifreq ifr; + int ifindex; + + ifindex = if_nametoindex(ifname); + if (ifindex == 0) + return -1; + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, brname, IFNAMSIZ); + ifr.ifr_ifindex = ifindex; + if (ioctl(sock, SIOCBRDELIF, &ifr) < 0) { + wpa_printf(MSG_DEBUG, "Could not remove interface %s from " + "bridge %s: %s", ifname, brname, strerror(errno)); + return -1; + } + + return 0; +} + + +int linux_br_get(char *brname, const char *ifname) +{ + char path[128], brlink[128], *pos; + os_snprintf(path, sizeof(path), "/sys/class/net/%s/brport/bridge", + ifname); + os_memset(brlink, 0, sizeof(brlink)); + if (readlink(path, brlink, sizeof(brlink) - 1) < 0) + return -1; + pos = os_strrchr(brlink, '/'); + if (pos == NULL) + return -1; + pos++; + os_strlcpy(brname, pos, IFNAMSIZ); + return 0; +} diff --git a/src/drivers/linux_ioctl.h b/src/drivers/linux_ioctl.h new file mode 100644 index 0000000000000..a5557383d8fe1 --- /dev/null +++ b/src/drivers/linux_ioctl.h @@ -0,0 +1,27 @@ +/* + * Linux ioctl helper functions for driver wrappers + * Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef LINUX_IOCTL_H +#define LINUX_IOCTL_H + +int linux_set_iface_flags(int sock, const char *ifname, int dev_up); +int linux_get_ifhwaddr(int sock, const char *ifname, u8 *addr); +int linux_set_ifhwaddr(int sock, const char *ifname, const u8 *addr); +int linux_br_add(int sock, const char *brname); +int linux_br_del(int sock, const char *brname); +int linux_br_add_if(int sock, const char *brname, const char *ifname); +int linux_br_del_if(int sock, const char *brname, const char *ifname); +int linux_br_get(char *brname, const char *ifname); + +#endif /* LINUX_IOCTL_H */ diff --git a/src/drivers/netlink.c b/src/drivers/netlink.c new file mode 100644 index 0000000000000..ad15b1d62a8d8 --- /dev/null +++ b/src/drivers/netlink.c @@ -0,0 +1,204 @@ +/* + * Netlink helper functions for driver wrappers + * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "priv_netlink.h" +#include "netlink.h" + + +struct netlink_data { + struct netlink_config *cfg; + int sock; +}; + + +static void netlink_receive_link(struct netlink_data *netlink, + void (*cb)(void *ctx, struct ifinfomsg *ifi, + u8 *buf, size_t len), + struct nlmsghdr *h) +{ + if (cb == NULL || NLMSG_PAYLOAD(h, 0) < sizeof(struct ifinfomsg)) + return; + cb(netlink->cfg->ctx, NLMSG_DATA(h), + NLMSG_DATA(h) + NLMSG_ALIGN(sizeof(struct ifinfomsg)), + NLMSG_PAYLOAD(h, sizeof(struct ifinfomsg))); +} + + +static void netlink_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct netlink_data *netlink = eloop_ctx; + char buf[8192]; + int left; + struct sockaddr_nl from; + socklen_t fromlen; + struct nlmsghdr *h; + int max_events = 10; + +try_again: + fromlen = sizeof(from); + left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, + (struct sockaddr *) &from, &fromlen); + if (left < 0) { + if (errno != EINTR && errno != EAGAIN) + wpa_printf(MSG_INFO, "netlink: recvfrom failed: %s", + strerror(errno)); + return; + } + + h = (struct nlmsghdr *) buf; + while (NLMSG_OK(h, left)) { + switch (h->nlmsg_type) { + case RTM_NEWLINK: + netlink_receive_link(netlink, netlink->cfg->newlink_cb, + h); + break; + case RTM_DELLINK: + netlink_receive_link(netlink, netlink->cfg->dellink_cb, + h); + break; + } + + h = NLMSG_NEXT(h, left); + } + + if (left > 0) { + wpa_printf(MSG_DEBUG, "netlink: %d extra bytes in the end of " + "netlink message", left); + } + + if (--max_events > 0) { + /* + * Try to receive all events in one eloop call in order to + * limit race condition on cases where AssocInfo event, Assoc + * event, and EAPOL frames are received more or less at the + * same time. We want to process the event messages first + * before starting EAPOL processing. + */ + goto try_again; + } +} + + +struct netlink_data * netlink_init(struct netlink_config *cfg) +{ + struct netlink_data *netlink; + struct sockaddr_nl local; + + netlink = os_zalloc(sizeof(*netlink)); + if (netlink == NULL) + return NULL; + + netlink->cfg = cfg; + + netlink->sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (netlink->sock < 0) { + wpa_printf(MSG_ERROR, "netlink: Failed to open netlink " + "socket: %s", strerror(errno)); + netlink_deinit(netlink); + return NULL; + } + + os_memset(&local, 0, sizeof(local)); + local.nl_family = AF_NETLINK; + local.nl_groups = RTMGRP_LINK; + if (bind(netlink->sock, (struct sockaddr *) &local, sizeof(local)) < 0) + { + wpa_printf(MSG_ERROR, "netlink: Failed to bind netlink " + "socket: %s", strerror(errno)); + netlink_deinit(netlink); + return NULL; + } + + eloop_register_read_sock(netlink->sock, netlink_receive, netlink, + NULL); + + return netlink; +} + + +void netlink_deinit(struct netlink_data *netlink) +{ + if (netlink == NULL) + return; + if (netlink->sock >= 0) { + eloop_unregister_read_sock(netlink->sock); + close(netlink->sock); + } + os_free(netlink->cfg); + os_free(netlink); +} + +int netlink_send_oper_ifla(struct netlink_data *netlink, int ifindex, + int linkmode, int operstate) +{ + struct { + struct nlmsghdr hdr; + struct ifinfomsg ifinfo; + char opts[16]; + } req; + struct rtattr *rta; + static int nl_seq; + ssize_t ret; + + os_memset(&req, 0, sizeof(req)); + + req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + req.hdr.nlmsg_type = RTM_SETLINK; + req.hdr.nlmsg_flags = NLM_F_REQUEST; + req.hdr.nlmsg_seq = ++nl_seq; + req.hdr.nlmsg_pid = 0; + + req.ifinfo.ifi_family = AF_UNSPEC; + req.ifinfo.ifi_type = 0; + req.ifinfo.ifi_index = ifindex; + req.ifinfo.ifi_flags = 0; + req.ifinfo.ifi_change = 0; + + if (linkmode != -1) { + rta = aliasing_hide_typecast( + ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)), + struct rtattr); + rta->rta_type = IFLA_LINKMODE; + rta->rta_len = RTA_LENGTH(sizeof(char)); + *((char *) RTA_DATA(rta)) = linkmode; + req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + + RTA_LENGTH(sizeof(char)); + } + if (operstate != -1) { + rta = aliasing_hide_typecast( + ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)), + struct rtattr); + rta->rta_type = IFLA_OPERSTATE; + rta->rta_len = RTA_LENGTH(sizeof(char)); + *((char *) RTA_DATA(rta)) = operstate; + req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + + RTA_LENGTH(sizeof(char)); + } + + wpa_printf(MSG_DEBUG, "netlink: Operstate: linkmode=%d, operstate=%d", + linkmode, operstate); + + ret = send(netlink->sock, &req, req.hdr.nlmsg_len, 0); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "netlink: Sending operstate IFLA " + "failed: %s (assume operstate is not supported)", + strerror(errno)); + } + + return ret < 0 ? -1 : 0; +} diff --git a/src/drivers/netlink.h b/src/drivers/netlink.h new file mode 100644 index 0000000000000..bcbfbb51fdd70 --- /dev/null +++ b/src/drivers/netlink.h @@ -0,0 +1,33 @@ +/* + * Netlink helper functions for driver wrappers + * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef NETLINK_H +#define NETLINK_H + +struct netlink_data; + +struct netlink_config { + void *ctx; + void (*newlink_cb)(void *ctx, struct ifinfomsg *ifi, u8 *buf, + size_t len); + void (*dellink_cb)(void *ctx, struct ifinfomsg *ifi, u8 *buf, + size_t len); +}; + +struct netlink_data * netlink_init(struct netlink_config *cfg); +void netlink_deinit(struct netlink_data *netlink); +int netlink_send_oper_ifla(struct netlink_data *netlink, int ifindex, + int linkmode, int operstate); + +#endif /* NETLINK_H */ diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h new file mode 100644 index 0000000000000..2ea3edeee7aae --- /dev/null +++ b/src/drivers/nl80211_copy.h @@ -0,0 +1,1644 @@ +#ifndef __LINUX_NL80211_H +#define __LINUX_NL80211_H +/* + * 802.11 netlink interface public header + * + * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2008 Michael Wu <flamingice@sourmilk.net> + * Copyright 2008 Luis Carlos Cobo <luisca@cozybit.com> + * Copyright 2008 Michael Buesch <mb@bu3sch.de> + * Copyright 2008, 2009 Luis R. Rodriguez <lrodriguez@atheros.com> + * Copyright 2008 Jouni Malinen <jouni.malinen@atheros.com> + * Copyright 2008 Colin McCabe <colin@cozybit.com> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include <linux/types.h> + +/** + * DOC: Station handling + * + * Stations are added per interface, but a special case exists with VLAN + * interfaces. When a station is bound to an AP interface, it may be moved + * into a VLAN identified by a VLAN interface index (%NL80211_ATTR_STA_VLAN). + * The station is still assumed to belong to the AP interface it was added + * to. + * + * TODO: need more info? + */ + +/** + * enum nl80211_commands - supported nl80211 commands + * + * @NL80211_CMD_UNSPEC: unspecified command to catch errors + * + * @NL80211_CMD_GET_WIPHY: request information about a wiphy or dump request + * to get a list of all present wiphys. + * @NL80211_CMD_SET_WIPHY: set wiphy parameters, needs %NL80211_ATTR_WIPHY or + * %NL80211_ATTR_IFINDEX; can be used to set %NL80211_ATTR_WIPHY_NAME, + * %NL80211_ATTR_WIPHY_TXQ_PARAMS, %NL80211_ATTR_WIPHY_FREQ, + * %NL80211_ATTR_WIPHY_CHANNEL_TYPE, %NL80211_ATTR_WIPHY_RETRY_SHORT, + * %NL80211_ATTR_WIPHY_RETRY_LONG, %NL80211_ATTR_WIPHY_FRAG_THRESHOLD, + * and/or %NL80211_ATTR_WIPHY_RTS_THRESHOLD. + * @NL80211_CMD_NEW_WIPHY: Newly created wiphy, response to get request + * or rename notification. Has attributes %NL80211_ATTR_WIPHY and + * %NL80211_ATTR_WIPHY_NAME. + * @NL80211_CMD_DEL_WIPHY: Wiphy deleted. Has attributes + * %NL80211_ATTR_WIPHY and %NL80211_ATTR_WIPHY_NAME. + * + * @NL80211_CMD_GET_INTERFACE: Request an interface's configuration; + * either a dump request on a %NL80211_ATTR_WIPHY or a specific get + * on an %NL80211_ATTR_IFINDEX is supported. + * @NL80211_CMD_SET_INTERFACE: Set type of a virtual interface, requires + * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_IFTYPE. + * @NL80211_CMD_NEW_INTERFACE: Newly created virtual interface or response + * to %NL80211_CMD_GET_INTERFACE. Has %NL80211_ATTR_IFINDEX, + * %NL80211_ATTR_WIPHY and %NL80211_ATTR_IFTYPE attributes. Can also + * be sent from userspace to request creation of a new virtual interface, + * then requires attributes %NL80211_ATTR_WIPHY, %NL80211_ATTR_IFTYPE and + * %NL80211_ATTR_IFNAME. + * @NL80211_CMD_DEL_INTERFACE: Virtual interface was deleted, has attributes + * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_WIPHY. Can also be sent from + * userspace to request deletion of a virtual interface, then requires + * attribute %NL80211_ATTR_IFINDEX. + * + * @NL80211_CMD_GET_KEY: Get sequence counter information for a key specified + * by %NL80211_ATTR_KEY_IDX and/or %NL80211_ATTR_MAC. + * @NL80211_CMD_SET_KEY: Set key attributes %NL80211_ATTR_KEY_DEFAULT, + * %NL80211_ATTR_KEY_DEFAULT_MGMT, or %NL80211_ATTR_KEY_THRESHOLD. + * @NL80211_CMD_NEW_KEY: add a key with given %NL80211_ATTR_KEY_DATA, + * %NL80211_ATTR_KEY_IDX, %NL80211_ATTR_MAC, %NL80211_ATTR_KEY_CIPHER, + * and %NL80211_ATTR_KEY_SEQ attributes. + * @NL80211_CMD_DEL_KEY: delete a key identified by %NL80211_ATTR_KEY_IDX + * or %NL80211_ATTR_MAC. + * + * @NL80211_CMD_GET_BEACON: retrieve beacon information (returned in a + * %NL80222_CMD_NEW_BEACON message) + * @NL80211_CMD_SET_BEACON: set the beacon on an access point interface + * using the %NL80211_ATTR_BEACON_INTERVAL, %NL80211_ATTR_DTIM_PERIOD, + * %NL80211_ATTR_BEACON_HEAD and %NL80211_ATTR_BEACON_TAIL attributes. + * @NL80211_CMD_NEW_BEACON: add a new beacon to an access point interface, + * parameters are like for %NL80211_CMD_SET_BEACON. + * @NL80211_CMD_DEL_BEACON: remove the beacon, stop sending it + * + * @NL80211_CMD_GET_STATION: Get station attributes for station identified by + * %NL80211_ATTR_MAC on the interface identified by %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_SET_STATION: Set station attributes for station identified by + * %NL80211_ATTR_MAC on the interface identified by %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_NEW_STATION: Add a station with given attributes to the + * the interface identified by %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_DEL_STATION: Remove a station identified by %NL80211_ATTR_MAC + * or, if no MAC address given, all stations, on the interface identified + * by %NL80211_ATTR_IFINDEX. + * + * @NL80211_CMD_GET_MPATH: Get mesh path attributes for mesh path to + * destination %NL80211_ATTR_MAC on the interface identified by + * %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_SET_MPATH: Set mesh path attributes for mesh path to + * destination %NL80211_ATTR_MAC on the interface identified by + * %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_NEW_PATH: Add a mesh path with given attributes to the + * the interface identified by %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_DEL_PATH: Remove a mesh path identified by %NL80211_ATTR_MAC + * or, if no MAC address given, all mesh paths, on the interface identified + * by %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_SET_BSS: Set BSS attributes for BSS identified by + * %NL80211_ATTR_IFINDEX. + * + * @NL80211_CMD_GET_REG: ask the wireless core to send us its currently set + * regulatory domain. + * @NL80211_CMD_SET_REG: Set current regulatory domain. CRDA sends this command + * after being queried by the kernel. CRDA replies by sending a regulatory + * domain structure which consists of %NL80211_ATTR_REG_ALPHA set to our + * current alpha2 if it found a match. It also provides + * NL80211_ATTR_REG_RULE_FLAGS, and a set of regulatory rules. Each + * regulatory rule is a nested set of attributes given by + * %NL80211_ATTR_REG_RULE_FREQ_[START|END] and + * %NL80211_ATTR_FREQ_RANGE_MAX_BW with an attached power rule given by + * %NL80211_ATTR_REG_RULE_POWER_MAX_ANT_GAIN and + * %NL80211_ATTR_REG_RULE_POWER_MAX_EIRP. + * @NL80211_CMD_REQ_SET_REG: ask the wireless core to set the regulatory domain + * to the the specified ISO/IEC 3166-1 alpha2 country code. The core will + * store this as a valid request and then query userspace for it. + * + * @NL80211_CMD_GET_MESH_PARAMS: Get mesh networking properties for the + * interface identified by %NL80211_ATTR_IFINDEX + * + * @NL80211_CMD_SET_MESH_PARAMS: Set mesh networking properties for the + * interface identified by %NL80211_ATTR_IFINDEX + * + * @NL80211_CMD_SET_MGMT_EXTRA_IE: Set extra IEs for management frames. The + * interface is identified with %NL80211_ATTR_IFINDEX and the management + * frame subtype with %NL80211_ATTR_MGMT_SUBTYPE. The extra IE data to be + * added to the end of the specified management frame is specified with + * %NL80211_ATTR_IE. If the command succeeds, the requested data will be + * added to all specified management frames generated by + * kernel/firmware/driver. + * Note: This command has been removed and it is only reserved at this + * point to avoid re-using existing command number. The functionality this + * command was planned for has been provided with cleaner design with the + * option to specify additional IEs in NL80211_CMD_TRIGGER_SCAN, + * NL80211_CMD_AUTHENTICATE, NL80211_CMD_ASSOCIATE, + * NL80211_CMD_DEAUTHENTICATE, and NL80211_CMD_DISASSOCIATE. + * + * @NL80211_CMD_GET_SCAN: get scan results + * @NL80211_CMD_TRIGGER_SCAN: trigger a new scan with the given parameters + * @NL80211_CMD_NEW_SCAN_RESULTS: scan notification (as a reply to + * NL80211_CMD_GET_SCAN and on the "scan" multicast group) + * @NL80211_CMD_SCAN_ABORTED: scan was aborted, for unspecified reasons, + * partial scan results may be available + * + * @NL80211_CMD_GET_SURVEY: get survey resuls, e.g. channel occupation + * or noise level + * @NL80211_CMD_NEW_SURVEY_RESULTS: survey data notification (as a reply to + * NL80211_CMD_GET_SURVEY and on the "scan" multicast group) + * + * @NL80211_CMD_REG_CHANGE: indicates to userspace the regulatory domain + * has been changed and provides details of the request information + * that caused the change such as who initiated the regulatory request + * (%NL80211_ATTR_REG_INITIATOR), the wiphy_idx + * (%NL80211_ATTR_REG_ALPHA2) on which the request was made from if + * the initiator was %NL80211_REGDOM_SET_BY_COUNTRY_IE or + * %NL80211_REGDOM_SET_BY_DRIVER, the type of regulatory domain + * set (%NL80211_ATTR_REG_TYPE), if the type of regulatory domain is + * %NL80211_REG_TYPE_COUNTRY the alpha2 to which we have moved on + * to (%NL80211_ATTR_REG_ALPHA2). + * @NL80211_CMD_REG_BEACON_HINT: indicates to userspace that an AP beacon + * has been found while world roaming thus enabling active scan or + * any mode of operation that initiates TX (beacons) on a channel + * where we would not have been able to do either before. As an example + * if you are world roaming (regulatory domain set to world or if your + * driver is using a custom world roaming regulatory domain) and while + * doing a passive scan on the 5 GHz band you find an AP there (if not + * on a DFS channel) you will now be able to actively scan for that AP + * or use AP mode on your card on that same channel. Note that this will + * never be used for channels 1-11 on the 2 GHz band as they are always + * enabled world wide. This beacon hint is only sent if your device had + * either disabled active scanning or beaconing on a channel. We send to + * userspace the wiphy on which we removed a restriction from + * (%NL80211_ATTR_WIPHY) and the channel on which this occurred + * before (%NL80211_ATTR_FREQ_BEFORE) and after (%NL80211_ATTR_FREQ_AFTER) + * the beacon hint was processed. + * + * @NL80211_CMD_AUTHENTICATE: authentication request and notification. + * This command is used both as a command (request to authenticate) and + * as an event on the "mlme" multicast group indicating completion of the + * authentication process. + * When used as a command, %NL80211_ATTR_IFINDEX is used to identify the + * interface. %NL80211_ATTR_MAC is used to specify PeerSTAAddress (and + * BSSID in case of station mode). %NL80211_ATTR_SSID is used to specify + * the SSID (mainly for association, but is included in authentication + * request, too, to help BSS selection. %NL80211_ATTR_WIPHY_FREQ is used + * to specify the frequence of the channel in MHz. %NL80211_ATTR_AUTH_TYPE + * is used to specify the authentication type. %NL80211_ATTR_IE is used to + * define IEs (VendorSpecificInfo, but also including RSN IE and FT IEs) + * to be added to the frame. + * When used as an event, this reports reception of an Authentication + * frame in station and IBSS modes when the local MLME processed the + * frame, i.e., it was for the local STA and was received in correct + * state. This is similar to MLME-AUTHENTICATE.confirm primitive in the + * MLME SAP interface (kernel providing MLME, userspace SME). The + * included %NL80211_ATTR_FRAME attribute contains the management frame + * (including both the header and frame body, but not FCS). This event is + * also used to indicate if the authentication attempt timed out. In that + * case the %NL80211_ATTR_FRAME attribute is replaced with a + * %NL80211_ATTR_TIMED_OUT flag (and %NL80211_ATTR_MAC to indicate which + * pending authentication timed out). + * @NL80211_CMD_ASSOCIATE: association request and notification; like + * NL80211_CMD_AUTHENTICATE but for Association and Reassociation + * (similar to MLME-ASSOCIATE.request, MLME-REASSOCIATE.request, + * MLME-ASSOCIATE.confirm or MLME-REASSOCIATE.confirm primitives). + * @NL80211_CMD_DEAUTHENTICATE: deauthentication request and notification; like + * NL80211_CMD_AUTHENTICATE but for Deauthentication frames (similar to + * MLME-DEAUTHENTICATION.request and MLME-DEAUTHENTICATE.indication + * primitives). + * @NL80211_CMD_DISASSOCIATE: disassociation request and notification; like + * NL80211_CMD_AUTHENTICATE but for Disassociation frames (similar to + * MLME-DISASSOCIATE.request and MLME-DISASSOCIATE.indication primitives). + * + * @NL80211_CMD_MICHAEL_MIC_FAILURE: notification of a locally detected Michael + * MIC (part of TKIP) failure; sent on the "mlme" multicast group; the + * event includes %NL80211_ATTR_MAC to describe the source MAC address of + * the frame with invalid MIC, %NL80211_ATTR_KEY_TYPE to show the key + * type, %NL80211_ATTR_KEY_IDX to indicate the key identifier, and + * %NL80211_ATTR_KEY_SEQ to indicate the TSC value of the frame; this + * event matches with MLME-MICHAELMICFAILURE.indication() primitive + * + * @NL80211_CMD_JOIN_IBSS: Join a new IBSS -- given at least an SSID and a + * FREQ attribute (for the initial frequency if no peer can be found) + * and optionally a MAC (as BSSID) and FREQ_FIXED attribute if those + * should be fixed rather than automatically determined. Can only be + * executed on a network interface that is UP, and fixed BSSID/FREQ + * may be rejected. Another optional parameter is the beacon interval, + * given in the %NL80211_ATTR_BEACON_INTERVAL attribute, which if not + * given defaults to 100 TU (102.4ms). + * @NL80211_CMD_LEAVE_IBSS: Leave the IBSS -- no special arguments, the IBSS is + * determined by the network interface. + * + * @NL80211_CMD_TESTMODE: testmode command, takes a wiphy (or ifindex) attribute + * to identify the device, and the TESTDATA blob attribute to pass through + * to the driver. + * + * @NL80211_CMD_CONNECT: connection request and notification; this command + * requests to connect to a specified network but without separating + * auth and assoc steps. For this, you need to specify the SSID in a + * %NL80211_ATTR_SSID attribute, and can optionally specify the association + * IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_MAC, + * %NL80211_ATTR_WIPHY_FREQ and %NL80211_ATTR_CONTROL_PORT. + * It is also sent as an event, with the BSSID and response IEs when the + * connection is established or failed to be established. This can be + * determined by the STATUS_CODE attribute. + * @NL80211_CMD_ROAM: request that the card roam (currently not implemented), + * sent as an event when the card/driver roamed by itself. + * @NL80211_CMD_DISCONNECT: drop a given connection; also used to notify + * userspace that a connection was dropped by the AP or due to other + * reasons, for this the %NL80211_ATTR_DISCONNECTED_BY_AP and + * %NL80211_ATTR_REASON_CODE attributes are used. + * + * @NL80211_CMD_SET_WIPHY_NETNS: Set a wiphy's netns. Note that all devices + * associated with this wiphy must be down and will follow. + * + * @NL80211_CMD_REMAIN_ON_CHANNEL: Request to remain awake on the specified + * channel for the specified amount of time. This can be used to do + * off-channel operations like transmit a Public Action frame and wait for + * a response while being associated to an AP on another channel. + * %NL80211_ATTR_WIPHY or %NL80211_ATTR_IFINDEX is used to specify which + * radio is used. %NL80211_ATTR_WIPHY_FREQ is used to specify the + * frequency for the operation and %NL80211_ATTR_WIPHY_CHANNEL_TYPE may be + * optionally used to specify additional channel parameters. + * %NL80211_ATTR_DURATION is used to specify the duration in milliseconds + * to remain on the channel. This command is also used as an event to + * notify when the requested duration starts (it may take a while for the + * driver to schedule this time due to other concurrent needs for the + * radio). + * When called, this operation returns a cookie (%NL80211_ATTR_COOKIE) + * that will be included with any events pertaining to this request; + * the cookie is also used to cancel the request. + * @NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL: This command can be used to cancel a + * pending remain-on-channel duration if the desired operation has been + * completed prior to expiration of the originally requested duration. + * %NL80211_ATTR_WIPHY or %NL80211_ATTR_IFINDEX is used to specify the + * radio. The %NL80211_ATTR_COOKIE attribute must be given as well to + * uniquely identify the request. + * This command is also used as an event to notify when a requested + * remain-on-channel duration has expired. + * + * @NL80211_CMD_SET_TX_BITRATE_MASK: Set the mask of rates to be used in TX + * rate selection. %NL80211_ATTR_IFINDEX is used to specify the interface + * and @NL80211_ATTR_TX_RATES the set of allowed rates. + * + * @NL80211_CMD_REGISTER_ACTION: Register for receiving certain action frames + * (via @NL80211_CMD_ACTION) for processing in userspace. This command + * requires an interface index and a match attribute containing the first + * few bytes of the frame that should match, e.g. a single byte for only + * a category match or four bytes for vendor frames including the OUI. + * The registration cannot be dropped, but is removed automatically + * when the netlink socket is closed. Multiple registrations can be made. + * @NL80211_CMD_ACTION: Action frame TX request and RX notification. This + * command is used both as a request to transmit an Action frame and as an + * event indicating reception of an Action frame that was not processed in + * kernel code, but is for us (i.e., which may need to be processed in a + * user space application). %NL80211_ATTR_FRAME is used to specify the + * frame contents (including header). %NL80211_ATTR_WIPHY_FREQ (and + * optionally %NL80211_ATTR_WIPHY_CHANNEL_TYPE) is used to indicate on + * which channel the frame is to be transmitted or was received. This + * channel has to be the current channel (remain-on-channel or the + * operational channel). When called, this operation returns a cookie + * (%NL80211_ATTR_COOKIE) that will be included with the TX status event + * pertaining to the TX request. + * @NL80211_CMD_ACTION_TX_STATUS: Report TX status of an Action frame + * transmitted with %NL80211_CMD_ACTION. %NL80211_ATTR_COOKIE identifies + * the TX command and %NL80211_ATTR_FRAME includes the contents of the + * frame. %NL80211_ATTR_ACK flag is included if the recipient acknowledged + * the frame. + * @NL80211_CMD_SET_CQM: Connection quality monitor configuration. This command + * is used to configure connection quality monitoring notification trigger + * levels. + * @NL80211_CMD_NOTIFY_CQM: Connection quality monitor notification. This + * command is used as an event to indicate the that a trigger level was + * reached. + * + * @NL80211_CMD_MAX: highest used command number + * @__NL80211_CMD_AFTER_LAST: internal use + */ +enum nl80211_commands { +/* don't change the order or add anything inbetween, this is ABI! */ + NL80211_CMD_UNSPEC, + + NL80211_CMD_GET_WIPHY, /* can dump */ + NL80211_CMD_SET_WIPHY, + NL80211_CMD_NEW_WIPHY, + NL80211_CMD_DEL_WIPHY, + + NL80211_CMD_GET_INTERFACE, /* can dump */ + NL80211_CMD_SET_INTERFACE, + NL80211_CMD_NEW_INTERFACE, + NL80211_CMD_DEL_INTERFACE, + + NL80211_CMD_GET_KEY, + NL80211_CMD_SET_KEY, + NL80211_CMD_NEW_KEY, + NL80211_CMD_DEL_KEY, + + NL80211_CMD_GET_BEACON, + NL80211_CMD_SET_BEACON, + NL80211_CMD_NEW_BEACON, + NL80211_CMD_DEL_BEACON, + + NL80211_CMD_GET_STATION, + NL80211_CMD_SET_STATION, + NL80211_CMD_NEW_STATION, + NL80211_CMD_DEL_STATION, + + NL80211_CMD_GET_MPATH, + NL80211_CMD_SET_MPATH, + NL80211_CMD_NEW_MPATH, + NL80211_CMD_DEL_MPATH, + + NL80211_CMD_SET_BSS, + + NL80211_CMD_SET_REG, + NL80211_CMD_REQ_SET_REG, + + NL80211_CMD_GET_MESH_PARAMS, + NL80211_CMD_SET_MESH_PARAMS, + + NL80211_CMD_SET_MGMT_EXTRA_IE /* reserved; not used */, + + NL80211_CMD_GET_REG, + + NL80211_CMD_GET_SCAN, + NL80211_CMD_TRIGGER_SCAN, + NL80211_CMD_NEW_SCAN_RESULTS, + NL80211_CMD_SCAN_ABORTED, + + NL80211_CMD_REG_CHANGE, + + NL80211_CMD_AUTHENTICATE, + NL80211_CMD_ASSOCIATE, + NL80211_CMD_DEAUTHENTICATE, + NL80211_CMD_DISASSOCIATE, + + NL80211_CMD_MICHAEL_MIC_FAILURE, + + NL80211_CMD_REG_BEACON_HINT, + + NL80211_CMD_JOIN_IBSS, + NL80211_CMD_LEAVE_IBSS, + + NL80211_CMD_TESTMODE, + + NL80211_CMD_CONNECT, + NL80211_CMD_ROAM, + NL80211_CMD_DISCONNECT, + + NL80211_CMD_SET_WIPHY_NETNS, + + NL80211_CMD_GET_SURVEY, + NL80211_CMD_NEW_SURVEY_RESULTS, + + NL80211_CMD_SET_PMKSA, + NL80211_CMD_DEL_PMKSA, + NL80211_CMD_FLUSH_PMKSA, + + NL80211_CMD_REMAIN_ON_CHANNEL, + NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, + + NL80211_CMD_SET_TX_BITRATE_MASK, + + NL80211_CMD_REGISTER_ACTION, + NL80211_CMD_ACTION, + NL80211_CMD_ACTION_TX_STATUS, + + NL80211_CMD_SET_POWER_SAVE, + NL80211_CMD_GET_POWER_SAVE, + + NL80211_CMD_SET_CQM, + NL80211_CMD_NOTIFY_CQM, + + /* add new commands above here */ + + /* used to define NL80211_CMD_MAX below */ + __NL80211_CMD_AFTER_LAST, + NL80211_CMD_MAX = __NL80211_CMD_AFTER_LAST - 1 +}; + +/* + * Allow user space programs to use #ifdef on new commands by defining them + * here + */ +#define NL80211_CMD_SET_BSS NL80211_CMD_SET_BSS +#define NL80211_CMD_SET_MGMT_EXTRA_IE NL80211_CMD_SET_MGMT_EXTRA_IE +#define NL80211_CMD_REG_CHANGE NL80211_CMD_REG_CHANGE +#define NL80211_CMD_AUTHENTICATE NL80211_CMD_AUTHENTICATE +#define NL80211_CMD_ASSOCIATE NL80211_CMD_ASSOCIATE +#define NL80211_CMD_DEAUTHENTICATE NL80211_CMD_DEAUTHENTICATE +#define NL80211_CMD_DISASSOCIATE NL80211_CMD_DISASSOCIATE +#define NL80211_CMD_REG_BEACON_HINT NL80211_CMD_REG_BEACON_HINT + +/** + * enum nl80211_attrs - nl80211 netlink attributes + * + * @NL80211_ATTR_UNSPEC: unspecified attribute to catch errors + * + * @NL80211_ATTR_WIPHY: index of wiphy to operate on, cf. + * /sys/class/ieee80211/<phyname>/index + * @NL80211_ATTR_WIPHY_NAME: wiphy name (used for renaming) + * @NL80211_ATTR_WIPHY_TXQ_PARAMS: a nested array of TX queue parameters + * @NL80211_ATTR_WIPHY_FREQ: frequency of the selected channel in MHz + * @NL80211_ATTR_WIPHY_CHANNEL_TYPE: included with NL80211_ATTR_WIPHY_FREQ + * if HT20 or HT40 are allowed (i.e., 802.11n disabled if not included): + * NL80211_CHAN_NO_HT = HT not allowed (i.e., same as not including + * this attribute) + * NL80211_CHAN_HT20 = HT20 only + * NL80211_CHAN_HT40MINUS = secondary channel is below the primary channel + * NL80211_CHAN_HT40PLUS = secondary channel is above the primary channel + * @NL80211_ATTR_WIPHY_RETRY_SHORT: TX retry limit for frames whose length is + * less than or equal to the RTS threshold; allowed range: 1..255; + * dot11ShortRetryLimit; u8 + * @NL80211_ATTR_WIPHY_RETRY_LONG: TX retry limit for frames whose length is + * greater than the RTS threshold; allowed range: 1..255; + * dot11ShortLongLimit; u8 + * @NL80211_ATTR_WIPHY_FRAG_THRESHOLD: fragmentation threshold, i.e., maximum + * length in octets for frames; allowed range: 256..8000, disable + * fragmentation with (u32)-1; dot11FragmentationThreshold; u32 + * @NL80211_ATTR_WIPHY_RTS_THRESHOLD: RTS threshold (TX frames with length + * larger than or equal to this use RTS/CTS handshake); allowed range: + * 0..65536, disable with (u32)-1; dot11RTSThreshold; u32 + * @NL80211_ATTR_WIPHY_COVERAGE_CLASS: Coverage Class as defined by IEEE 802.11 + * section 7.3.2.9; dot11CoverageClass; u8 + * + * @NL80211_ATTR_IFINDEX: network interface index of the device to operate on + * @NL80211_ATTR_IFNAME: network interface name + * @NL80211_ATTR_IFTYPE: type of virtual interface, see &enum nl80211_iftype + * + * @NL80211_ATTR_MAC: MAC address (various uses) + * + * @NL80211_ATTR_KEY_DATA: (temporal) key data; for TKIP this consists of + * 16 bytes encryption key followed by 8 bytes each for TX and RX MIC + * keys + * @NL80211_ATTR_KEY_IDX: key ID (u8, 0-3) + * @NL80211_ATTR_KEY_CIPHER: key cipher suite (u32, as defined by IEEE 802.11 + * section 7.3.2.25.1, e.g. 0x000FAC04) + * @NL80211_ATTR_KEY_SEQ: transmit key sequence number (IV/PN) for TKIP and + * CCMP keys, each six bytes in little endian + * + * @NL80211_ATTR_BEACON_INTERVAL: beacon interval in TU + * @NL80211_ATTR_DTIM_PERIOD: DTIM period for beaconing + * @NL80211_ATTR_BEACON_HEAD: portion of the beacon before the TIM IE + * @NL80211_ATTR_BEACON_TAIL: portion of the beacon after the TIM IE + * + * @NL80211_ATTR_STA_AID: Association ID for the station (u16) + * @NL80211_ATTR_STA_FLAGS: flags, nested element with NLA_FLAG attributes of + * &enum nl80211_sta_flags (deprecated, use %NL80211_ATTR_STA_FLAGS2) + * @NL80211_ATTR_STA_LISTEN_INTERVAL: listen interval as defined by + * IEEE 802.11 7.3.1.6 (u16). + * @NL80211_ATTR_STA_SUPPORTED_RATES: supported rates, array of supported + * rates as defined by IEEE 802.11 7.3.2.2 but without the length + * restriction (at most %NL80211_MAX_SUPP_RATES). + * @NL80211_ATTR_STA_VLAN: interface index of VLAN interface to move station + * to, or the AP interface the station was originally added to to. + * @NL80211_ATTR_STA_INFO: information about a station, part of station info + * given for %NL80211_CMD_GET_STATION, nested attribute containing + * info as possible, see &enum nl80211_sta_info. + * + * @NL80211_ATTR_WIPHY_BANDS: Information about an operating bands, + * consisting of a nested array. + * + * @NL80211_ATTR_MESH_ID: mesh id (1-32 bytes). + * @NL80211_ATTR_PLINK_ACTION: action to perform on the mesh peer link. + * @NL80211_ATTR_MPATH_NEXT_HOP: MAC address of the next hop for a mesh path. + * @NL80211_ATTR_MPATH_INFO: information about a mesh_path, part of mesh path + * info given for %NL80211_CMD_GET_MPATH, nested attribute described at + * &enum nl80211_mpath_info. + * + * @NL80211_ATTR_MNTR_FLAGS: flags, nested element with NLA_FLAG attributes of + * &enum nl80211_mntr_flags. + * + * @NL80211_ATTR_REG_ALPHA2: an ISO-3166-alpha2 country code for which the + * current regulatory domain should be set to or is already set to. + * For example, 'CR', for Costa Rica. This attribute is used by the kernel + * to query the CRDA to retrieve one regulatory domain. This attribute can + * also be used by userspace to query the kernel for the currently set + * regulatory domain. We chose an alpha2 as that is also used by the + * IEEE-802.11d country information element to identify a country. + * Users can also simply ask the wireless core to set regulatory domain + * to a specific alpha2. + * @NL80211_ATTR_REG_RULES: a nested array of regulatory domain regulatory + * rules. + * + * @NL80211_ATTR_BSS_CTS_PROT: whether CTS protection is enabled (u8, 0 or 1) + * @NL80211_ATTR_BSS_SHORT_PREAMBLE: whether short preamble is enabled + * (u8, 0 or 1) + * @NL80211_ATTR_BSS_SHORT_SLOT_TIME: whether short slot time enabled + * (u8, 0 or 1) + * @NL80211_ATTR_BSS_BASIC_RATES: basic rates, array of basic + * rates in format defined by IEEE 802.11 7.3.2.2 but without the length + * restriction (at most %NL80211_MAX_SUPP_RATES). + * + * @NL80211_ATTR_HT_CAPABILITY: HT Capability information element (from + * association request when used with NL80211_CMD_NEW_STATION) + * + * @NL80211_ATTR_SUPPORTED_IFTYPES: nested attribute containing all + * supported interface types, each a flag attribute with the number + * of the interface mode. + * + * @NL80211_ATTR_MGMT_SUBTYPE: Management frame subtype for + * %NL80211_CMD_SET_MGMT_EXTRA_IE. + * + * @NL80211_ATTR_IE: Information element(s) data (used, e.g., with + * %NL80211_CMD_SET_MGMT_EXTRA_IE). + * + * @NL80211_ATTR_MAX_NUM_SCAN_SSIDS: number of SSIDs you can scan with + * a single scan request, a wiphy attribute. + * @NL80211_ATTR_MAX_SCAN_IE_LEN: maximum length of information elements + * that can be added to a scan request + * + * @NL80211_ATTR_SCAN_FREQUENCIES: nested attribute with frequencies (in MHz) + * @NL80211_ATTR_SCAN_SSIDS: nested attribute with SSIDs, leave out for passive + * scanning and include a zero-length SSID (wildcard) for wildcard scan + * @NL80211_ATTR_BSS: scan result BSS + * + * @NL80211_ATTR_REG_INITIATOR: indicates who requested the regulatory domain + * currently in effect. This could be any of the %NL80211_REGDOM_SET_BY_* + * @NL80211_ATTR_REG_TYPE: indicates the type of the regulatory domain currently + * set. This can be one of the nl80211_reg_type (%NL80211_REGDOM_TYPE_*) + * + * @NL80211_ATTR_SUPPORTED_COMMANDS: wiphy attribute that specifies + * an array of command numbers (i.e. a mapping index to command number) + * that the driver for the given wiphy supports. + * + * @NL80211_ATTR_FRAME: frame data (binary attribute), including frame header + * and body, but not FCS; used, e.g., with NL80211_CMD_AUTHENTICATE and + * NL80211_CMD_ASSOCIATE events + * @NL80211_ATTR_SSID: SSID (binary attribute, 0..32 octets) + * @NL80211_ATTR_AUTH_TYPE: AuthenticationType, see &enum nl80211_auth_type, + * represented as a u32 + * @NL80211_ATTR_REASON_CODE: ReasonCode for %NL80211_CMD_DEAUTHENTICATE and + * %NL80211_CMD_DISASSOCIATE, u16 + * + * @NL80211_ATTR_KEY_TYPE: Key Type, see &enum nl80211_key_type, represented as + * a u32 + * + * @NL80211_ATTR_FREQ_BEFORE: A channel which has suffered a regulatory change + * due to considerations from a beacon hint. This attribute reflects + * the state of the channel _before_ the beacon hint processing. This + * attributes consists of a nested attribute containing + * NL80211_FREQUENCY_ATTR_* + * @NL80211_ATTR_FREQ_AFTER: A channel which has suffered a regulatory change + * due to considerations from a beacon hint. This attribute reflects + * the state of the channel _after_ the beacon hint processing. This + * attributes consists of a nested attribute containing + * NL80211_FREQUENCY_ATTR_* + * + * @NL80211_ATTR_CIPHER_SUITES: a set of u32 values indicating the supported + * cipher suites + * + * @NL80211_ATTR_FREQ_FIXED: a flag indicating the IBSS should not try to look + * for other networks on different channels + * + * @NL80211_ATTR_TIMED_OUT: a flag indicating than an operation timed out; this + * is used, e.g., with %NL80211_CMD_AUTHENTICATE event + * + * @NL80211_ATTR_USE_MFP: Whether management frame protection (IEEE 802.11w) is + * used for the association (&enum nl80211_mfp, represented as a u32); + * this attribute can be used + * with %NL80211_CMD_ASSOCIATE request + * + * @NL80211_ATTR_STA_FLAGS2: Attribute containing a + * &struct nl80211_sta_flag_update. + * + * @NL80211_ATTR_CONTROL_PORT: A flag indicating whether user space controls + * IEEE 802.1X port, i.e., sets/clears %NL80211_STA_FLAG_AUTHORIZED, in + * station mode. If the flag is included in %NL80211_CMD_ASSOCIATE + * request, the driver will assume that the port is unauthorized until + * authorized by user space. Otherwise, port is marked authorized by + * default in station mode. + * + * @NL80211_ATTR_TESTDATA: Testmode data blob, passed through to the driver. + * We recommend using nested, driver-specific attributes within this. + * + * @NL80211_ATTR_DISCONNECTED_BY_AP: A flag indicating that the DISCONNECT + * event was due to the AP disconnecting the station, and not due to + * a local disconnect request. + * @NL80211_ATTR_STATUS_CODE: StatusCode for the %NL80211_CMD_CONNECT + * event (u16) + * @NL80211_ATTR_PRIVACY: Flag attribute, used with connect(), indicating + * that protected APs should be used. + * + * @NL80211_ATTR_CIPHERS_PAIRWISE: Used with CONNECT and ASSOCIATE to + * indicate which unicast key ciphers will be used with the connection + * (an array of u32). + * @NL80211_ATTR_CIPHER_GROUP: Used with CONNECT and ASSOCIATE to indicate + * which group key cipher will be used with the connection (a u32). + * @NL80211_ATTR_WPA_VERSIONS: Used with CONNECT and ASSOCIATE to indicate + * which WPA version(s) the AP we want to associate with is using + * (a u32 with flags from &enum nl80211_wpa_versions). + * @NL80211_ATTR_AKM_SUITES: Used with CONNECT and ASSOCIATE to indicate + * which key management algorithm(s) to use (an array of u32). + * + * @NL80211_ATTR_REQ_IE: (Re)association request information elements as + * sent out by the card, for ROAM and successful CONNECT events. + * @NL80211_ATTR_RESP_IE: (Re)association response information elements as + * sent by peer, for ROAM and successful CONNECT events. + * + * @NL80211_ATTR_PREV_BSSID: previous BSSID, to be used by in ASSOCIATE + * commands to specify using a reassociate frame + * + * @NL80211_ATTR_KEY: key information in a nested attribute with + * %NL80211_KEY_* sub-attributes + * @NL80211_ATTR_KEYS: array of keys for static WEP keys for connect() + * and join_ibss(), key information is in a nested attribute each + * with %NL80211_KEY_* sub-attributes + * + * @NL80211_ATTR_PID: Process ID of a network namespace. + * + * @NL80211_ATTR_GENERATION: Used to indicate consistent snapshots for + * dumps. This number increases whenever the object list being + * dumped changes, and as such userspace can verify that it has + * obtained a complete and consistent snapshot by verifying that + * all dump messages contain the same generation number. If it + * changed then the list changed and the dump should be repeated + * completely from scratch. + * + * @NL80211_ATTR_4ADDR: Use 4-address frames on a virtual interface + * + * @NL80211_ATTR_SURVEY_INFO: survey information about a channel, part of + * the survey response for %NL80211_CMD_GET_SURVEY, nested attribute + * containing info as possible, see &enum survey_info. + * + * @NL80211_ATTR_PMKID: PMK material for PMKSA caching. + * @NL80211_ATTR_MAX_NUM_PMKIDS: maximum number of PMKIDs a firmware can + * cache, a wiphy attribute. + * + * @NL80211_ATTR_DURATION: Duration of an operation in milliseconds, u32. + * + * @NL80211_ATTR_COOKIE: Generic 64-bit cookie to identify objects. + * + * @NL80211_ATTR_TX_RATES: Nested set of attributes + * (enum nl80211_tx_rate_attributes) describing TX rates per band. The + * enum nl80211_band value is used as the index (nla_type() of the nested + * data. If a band is not included, it will be configured to allow all + * rates based on negotiated supported rates information. This attribute + * is used with %NL80211_CMD_SET_TX_BITRATE_MASK. + * + * @NL80211_ATTR_FRAME_MATCH: A binary attribute which typically must contain + * at least one byte, currently used with @NL80211_CMD_REGISTER_ACTION. + * + * @NL80211_ATTR_ACK: Flag attribute indicating that the frame was + * acknowledged by the recipient. + * + * @NL80211_ATTR_CQM: connection quality monitor configuration in a + * nested attribute with %NL80211_ATTR_CQM_* sub-attributes. + * + * @NL80211_ATTR_LOCAL_STATE_CHANGE: Flag attribute to indicate that a command + * is requesting a local authentication/association state change without + * invoking actual management frame exchange. This can be used with + * NL80211_CMD_AUTHENTICATE, NL80211_CMD_DEAUTHENTICATE, + * NL80211_CMD_DISASSOCIATE. + * + * @NL80211_ATTR_MAX: highest attribute number currently defined + * @__NL80211_ATTR_AFTER_LAST: internal use + */ +enum nl80211_attrs { +/* don't change the order or add anything inbetween, this is ABI! */ + NL80211_ATTR_UNSPEC, + + NL80211_ATTR_WIPHY, + NL80211_ATTR_WIPHY_NAME, + + NL80211_ATTR_IFINDEX, + NL80211_ATTR_IFNAME, + NL80211_ATTR_IFTYPE, + + NL80211_ATTR_MAC, + + NL80211_ATTR_KEY_DATA, + NL80211_ATTR_KEY_IDX, + NL80211_ATTR_KEY_CIPHER, + NL80211_ATTR_KEY_SEQ, + NL80211_ATTR_KEY_DEFAULT, + + NL80211_ATTR_BEACON_INTERVAL, + NL80211_ATTR_DTIM_PERIOD, + NL80211_ATTR_BEACON_HEAD, + NL80211_ATTR_BEACON_TAIL, + + NL80211_ATTR_STA_AID, + NL80211_ATTR_STA_FLAGS, + NL80211_ATTR_STA_LISTEN_INTERVAL, + NL80211_ATTR_STA_SUPPORTED_RATES, + NL80211_ATTR_STA_VLAN, + NL80211_ATTR_STA_INFO, + + NL80211_ATTR_WIPHY_BANDS, + + NL80211_ATTR_MNTR_FLAGS, + + NL80211_ATTR_MESH_ID, + NL80211_ATTR_STA_PLINK_ACTION, + NL80211_ATTR_MPATH_NEXT_HOP, + NL80211_ATTR_MPATH_INFO, + + NL80211_ATTR_BSS_CTS_PROT, + NL80211_ATTR_BSS_SHORT_PREAMBLE, + NL80211_ATTR_BSS_SHORT_SLOT_TIME, + + NL80211_ATTR_HT_CAPABILITY, + + NL80211_ATTR_SUPPORTED_IFTYPES, + + NL80211_ATTR_REG_ALPHA2, + NL80211_ATTR_REG_RULES, + + NL80211_ATTR_MESH_PARAMS, + + NL80211_ATTR_BSS_BASIC_RATES, + + NL80211_ATTR_WIPHY_TXQ_PARAMS, + NL80211_ATTR_WIPHY_FREQ, + NL80211_ATTR_WIPHY_CHANNEL_TYPE, + + NL80211_ATTR_KEY_DEFAULT_MGMT, + + NL80211_ATTR_MGMT_SUBTYPE, + NL80211_ATTR_IE, + + NL80211_ATTR_MAX_NUM_SCAN_SSIDS, + + NL80211_ATTR_SCAN_FREQUENCIES, + NL80211_ATTR_SCAN_SSIDS, + NL80211_ATTR_GENERATION, /* replaces old SCAN_GENERATION */ + NL80211_ATTR_BSS, + + NL80211_ATTR_REG_INITIATOR, + NL80211_ATTR_REG_TYPE, + + NL80211_ATTR_SUPPORTED_COMMANDS, + + NL80211_ATTR_FRAME, + NL80211_ATTR_SSID, + NL80211_ATTR_AUTH_TYPE, + NL80211_ATTR_REASON_CODE, + + NL80211_ATTR_KEY_TYPE, + + NL80211_ATTR_MAX_SCAN_IE_LEN, + NL80211_ATTR_CIPHER_SUITES, + + NL80211_ATTR_FREQ_BEFORE, + NL80211_ATTR_FREQ_AFTER, + + NL80211_ATTR_FREQ_FIXED, + + + NL80211_ATTR_WIPHY_RETRY_SHORT, + NL80211_ATTR_WIPHY_RETRY_LONG, + NL80211_ATTR_WIPHY_FRAG_THRESHOLD, + NL80211_ATTR_WIPHY_RTS_THRESHOLD, + + NL80211_ATTR_TIMED_OUT, + + NL80211_ATTR_USE_MFP, + + NL80211_ATTR_STA_FLAGS2, + + NL80211_ATTR_CONTROL_PORT, + + NL80211_ATTR_TESTDATA, + + NL80211_ATTR_PRIVACY, + + NL80211_ATTR_DISCONNECTED_BY_AP, + NL80211_ATTR_STATUS_CODE, + + NL80211_ATTR_CIPHER_SUITES_PAIRWISE, + NL80211_ATTR_CIPHER_SUITE_GROUP, + NL80211_ATTR_WPA_VERSIONS, + NL80211_ATTR_AKM_SUITES, + + NL80211_ATTR_REQ_IE, + NL80211_ATTR_RESP_IE, + + NL80211_ATTR_PREV_BSSID, + + NL80211_ATTR_KEY, + NL80211_ATTR_KEYS, + + NL80211_ATTR_PID, + + NL80211_ATTR_4ADDR, + + NL80211_ATTR_SURVEY_INFO, + + NL80211_ATTR_PMKID, + NL80211_ATTR_MAX_NUM_PMKIDS, + + NL80211_ATTR_DURATION, + + NL80211_ATTR_COOKIE, + + NL80211_ATTR_WIPHY_COVERAGE_CLASS, + + NL80211_ATTR_TX_RATES, + + NL80211_ATTR_FRAME_MATCH, + + NL80211_ATTR_ACK, + + NL80211_ATTR_PS_STATE, + + NL80211_ATTR_CQM, + + NL80211_ATTR_LOCAL_STATE_CHANGE, + + /* add attributes here, update the policy in nl80211.c */ + + __NL80211_ATTR_AFTER_LAST, + NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1 +}; + +/* source-level API compatibility */ +#define NL80211_ATTR_SCAN_GENERATION NL80211_ATTR_GENERATION + +/* + * Allow user space programs to use #ifdef on new attributes by defining them + * here + */ +#define NL80211_CMD_CONNECT NL80211_CMD_CONNECT +#define NL80211_ATTR_HT_CAPABILITY NL80211_ATTR_HT_CAPABILITY +#define NL80211_ATTR_BSS_BASIC_RATES NL80211_ATTR_BSS_BASIC_RATES +#define NL80211_ATTR_WIPHY_TXQ_PARAMS NL80211_ATTR_WIPHY_TXQ_PARAMS +#define NL80211_ATTR_WIPHY_FREQ NL80211_ATTR_WIPHY_FREQ +#define NL80211_ATTR_WIPHY_CHANNEL_TYPE NL80211_ATTR_WIPHY_CHANNEL_TYPE +#define NL80211_ATTR_MGMT_SUBTYPE NL80211_ATTR_MGMT_SUBTYPE +#define NL80211_ATTR_IE NL80211_ATTR_IE +#define NL80211_ATTR_REG_INITIATOR NL80211_ATTR_REG_INITIATOR +#define NL80211_ATTR_REG_TYPE NL80211_ATTR_REG_TYPE +#define NL80211_ATTR_FRAME NL80211_ATTR_FRAME +#define NL80211_ATTR_SSID NL80211_ATTR_SSID +#define NL80211_ATTR_AUTH_TYPE NL80211_ATTR_AUTH_TYPE +#define NL80211_ATTR_REASON_CODE NL80211_ATTR_REASON_CODE +#define NL80211_ATTR_CIPHER_SUITES_PAIRWISE NL80211_ATTR_CIPHER_SUITES_PAIRWISE +#define NL80211_ATTR_CIPHER_SUITE_GROUP NL80211_ATTR_CIPHER_SUITE_GROUP +#define NL80211_ATTR_WPA_VERSIONS NL80211_ATTR_WPA_VERSIONS +#define NL80211_ATTR_AKM_SUITES NL80211_ATTR_AKM_SUITES +#define NL80211_ATTR_KEY NL80211_ATTR_KEY +#define NL80211_ATTR_KEYS NL80211_ATTR_KEYS + +#define NL80211_MAX_SUPP_RATES 32 +#define NL80211_MAX_SUPP_REG_RULES 32 +#define NL80211_TKIP_DATA_OFFSET_ENCR_KEY 0 +#define NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY 16 +#define NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY 24 +#define NL80211_HT_CAPABILITY_LEN 26 + +#define NL80211_MAX_NR_CIPHER_SUITES 5 +#define NL80211_MAX_NR_AKM_SUITES 2 + +/** + * enum nl80211_iftype - (virtual) interface types + * + * @NL80211_IFTYPE_UNSPECIFIED: unspecified type, driver decides + * @NL80211_IFTYPE_ADHOC: independent BSS member + * @NL80211_IFTYPE_STATION: managed BSS member + * @NL80211_IFTYPE_AP: access point + * @NL80211_IFTYPE_AP_VLAN: VLAN interface for access points + * @NL80211_IFTYPE_WDS: wireless distribution interface + * @NL80211_IFTYPE_MONITOR: monitor interface receiving all frames + * @NL80211_IFTYPE_MESH_POINT: mesh point + * @NL80211_IFTYPE_MAX: highest interface type number currently defined + * @__NL80211_IFTYPE_AFTER_LAST: internal use + * + * These values are used with the %NL80211_ATTR_IFTYPE + * to set the type of an interface. + * + */ +enum nl80211_iftype { + NL80211_IFTYPE_UNSPECIFIED, + NL80211_IFTYPE_ADHOC, + NL80211_IFTYPE_STATION, + NL80211_IFTYPE_AP, + NL80211_IFTYPE_AP_VLAN, + NL80211_IFTYPE_WDS, + NL80211_IFTYPE_MONITOR, + NL80211_IFTYPE_MESH_POINT, + + /* keep last */ + __NL80211_IFTYPE_AFTER_LAST, + NL80211_IFTYPE_MAX = __NL80211_IFTYPE_AFTER_LAST - 1 +}; + +/** + * enum nl80211_sta_flags - station flags + * + * Station flags. When a station is added to an AP interface, it is + * assumed to be already associated (and hence authenticated.) + * + * @NL80211_STA_FLAG_AUTHORIZED: station is authorized (802.1X) + * @NL80211_STA_FLAG_SHORT_PREAMBLE: station is capable of receiving frames + * with short barker preamble + * @NL80211_STA_FLAG_WME: station is WME/QoS capable + * @NL80211_STA_FLAG_MFP: station uses management frame protection + */ +enum nl80211_sta_flags { + __NL80211_STA_FLAG_INVALID, + NL80211_STA_FLAG_AUTHORIZED, + NL80211_STA_FLAG_SHORT_PREAMBLE, + NL80211_STA_FLAG_WME, + NL80211_STA_FLAG_MFP, + + /* keep last */ + __NL80211_STA_FLAG_AFTER_LAST, + NL80211_STA_FLAG_MAX = __NL80211_STA_FLAG_AFTER_LAST - 1 +}; + +/** + * struct nl80211_sta_flag_update - station flags mask/set + * @mask: mask of station flags to set + * @set: which values to set them to + * + * Both mask and set contain bits as per &enum nl80211_sta_flags. + */ +struct nl80211_sta_flag_update { + __u32 mask; + __u32 set; +} __attribute__((packed)); + +/** + * enum nl80211_rate_info - bitrate information + * + * These attribute types are used with %NL80211_STA_INFO_TXRATE + * when getting information about the bitrate of a station. + * + * @__NL80211_RATE_INFO_INVALID: attribute number 0 is reserved + * @NL80211_RATE_INFO_BITRATE: total bitrate (u16, 100kbit/s) + * @NL80211_RATE_INFO_MCS: mcs index for 802.11n (u8) + * @NL80211_RATE_INFO_40_MHZ_WIDTH: 40 Mhz dualchannel bitrate + * @NL80211_RATE_INFO_SHORT_GI: 400ns guard interval + * @NL80211_RATE_INFO_MAX: highest rate_info number currently defined + * @__NL80211_RATE_INFO_AFTER_LAST: internal use + */ +enum nl80211_rate_info { + __NL80211_RATE_INFO_INVALID, + NL80211_RATE_INFO_BITRATE, + NL80211_RATE_INFO_MCS, + NL80211_RATE_INFO_40_MHZ_WIDTH, + NL80211_RATE_INFO_SHORT_GI, + + /* keep last */ + __NL80211_RATE_INFO_AFTER_LAST, + NL80211_RATE_INFO_MAX = __NL80211_RATE_INFO_AFTER_LAST - 1 +}; + +/** + * enum nl80211_sta_info - station information + * + * These attribute types are used with %NL80211_ATTR_STA_INFO + * when getting information about a station. + * + * @__NL80211_STA_INFO_INVALID: attribute number 0 is reserved + * @NL80211_STA_INFO_INACTIVE_TIME: time since last activity (u32, msecs) + * @NL80211_STA_INFO_RX_BYTES: total received bytes (u32, from this station) + * @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (u32, to this station) + * @__NL80211_STA_INFO_AFTER_LAST: internal + * @NL80211_STA_INFO_MAX: highest possible station info attribute + * @NL80211_STA_INFO_SIGNAL: signal strength of last received PPDU (u8, dBm) + * @NL80211_STA_INFO_TX_BITRATE: current unicast tx rate, nested attribute + * containing info as possible, see &enum nl80211_sta_info_txrate. + * @NL80211_STA_INFO_RX_PACKETS: total received packet (u32, from this station) + * @NL80211_STA_INFO_TX_PACKETS: total transmitted packets (u32, to this + * station) + */ +enum nl80211_sta_info { + __NL80211_STA_INFO_INVALID, + NL80211_STA_INFO_INACTIVE_TIME, + NL80211_STA_INFO_RX_BYTES, + NL80211_STA_INFO_TX_BYTES, + NL80211_STA_INFO_LLID, + NL80211_STA_INFO_PLID, + NL80211_STA_INFO_PLINK_STATE, + NL80211_STA_INFO_SIGNAL, + NL80211_STA_INFO_TX_BITRATE, + NL80211_STA_INFO_RX_PACKETS, + NL80211_STA_INFO_TX_PACKETS, + + /* keep last */ + __NL80211_STA_INFO_AFTER_LAST, + NL80211_STA_INFO_MAX = __NL80211_STA_INFO_AFTER_LAST - 1 +}; + +/** + * enum nl80211_mpath_flags - nl80211 mesh path flags + * + * @NL80211_MPATH_FLAG_ACTIVE: the mesh path is active + * @NL80211_MPATH_FLAG_RESOLVING: the mesh path discovery process is running + * @NL80211_MPATH_FLAG_SN_VALID: the mesh path contains a valid SN + * @NL80211_MPATH_FLAG_FIXED: the mesh path has been manually set + * @NL80211_MPATH_FLAG_RESOLVED: the mesh path discovery process succeeded + */ +enum nl80211_mpath_flags { + NL80211_MPATH_FLAG_ACTIVE = 1<<0, + NL80211_MPATH_FLAG_RESOLVING = 1<<1, + NL80211_MPATH_FLAG_SN_VALID = 1<<2, + NL80211_MPATH_FLAG_FIXED = 1<<3, + NL80211_MPATH_FLAG_RESOLVED = 1<<4, +}; + +/** + * enum nl80211_mpath_info - mesh path information + * + * These attribute types are used with %NL80211_ATTR_MPATH_INFO when getting + * information about a mesh path. + * + * @__NL80211_MPATH_INFO_INVALID: attribute number 0 is reserved + * @NL80211_ATTR_MPATH_FRAME_QLEN: number of queued frames for this destination + * @NL80211_ATTR_MPATH_SN: destination sequence number + * @NL80211_ATTR_MPATH_METRIC: metric (cost) of this mesh path + * @NL80211_ATTR_MPATH_EXPTIME: expiration time for the path, in msec from now + * @NL80211_ATTR_MPATH_FLAGS: mesh path flags, enumerated in + * &enum nl80211_mpath_flags; + * @NL80211_ATTR_MPATH_DISCOVERY_TIMEOUT: total path discovery timeout, in msec + * @NL80211_ATTR_MPATH_DISCOVERY_RETRIES: mesh path discovery retries + */ +enum nl80211_mpath_info { + __NL80211_MPATH_INFO_INVALID, + NL80211_MPATH_INFO_FRAME_QLEN, + NL80211_MPATH_INFO_SN, + NL80211_MPATH_INFO_METRIC, + NL80211_MPATH_INFO_EXPTIME, + NL80211_MPATH_INFO_FLAGS, + NL80211_MPATH_INFO_DISCOVERY_TIMEOUT, + NL80211_MPATH_INFO_DISCOVERY_RETRIES, + + /* keep last */ + __NL80211_MPATH_INFO_AFTER_LAST, + NL80211_MPATH_INFO_MAX = __NL80211_MPATH_INFO_AFTER_LAST - 1 +}; + +/** + * enum nl80211_band_attr - band attributes + * @__NL80211_BAND_ATTR_INVALID: attribute number 0 is reserved + * @NL80211_BAND_ATTR_FREQS: supported frequencies in this band, + * an array of nested frequency attributes + * @NL80211_BAND_ATTR_RATES: supported bitrates in this band, + * an array of nested bitrate attributes + * @NL80211_BAND_ATTR_HT_MCS_SET: 16-byte attribute containing the MCS set as + * defined in 802.11n + * @NL80211_BAND_ATTR_HT_CAPA: HT capabilities, as in the HT information IE + * @NL80211_BAND_ATTR_HT_AMPDU_FACTOR: A-MPDU factor, as in 11n + * @NL80211_BAND_ATTR_HT_AMPDU_DENSITY: A-MPDU density, as in 11n + */ +enum nl80211_band_attr { + __NL80211_BAND_ATTR_INVALID, + NL80211_BAND_ATTR_FREQS, + NL80211_BAND_ATTR_RATES, + + NL80211_BAND_ATTR_HT_MCS_SET, + NL80211_BAND_ATTR_HT_CAPA, + NL80211_BAND_ATTR_HT_AMPDU_FACTOR, + NL80211_BAND_ATTR_HT_AMPDU_DENSITY, + + /* keep last */ + __NL80211_BAND_ATTR_AFTER_LAST, + NL80211_BAND_ATTR_MAX = __NL80211_BAND_ATTR_AFTER_LAST - 1 +}; + +#define NL80211_BAND_ATTR_HT_CAPA NL80211_BAND_ATTR_HT_CAPA + +/** + * enum nl80211_frequency_attr - frequency attributes + * @NL80211_FREQUENCY_ATTR_FREQ: Frequency in MHz + * @NL80211_FREQUENCY_ATTR_DISABLED: Channel is disabled in current + * regulatory domain. + * @NL80211_FREQUENCY_ATTR_PASSIVE_SCAN: Only passive scanning is + * permitted on this channel in current regulatory domain. + * @NL80211_FREQUENCY_ATTR_NO_IBSS: IBSS networks are not permitted + * on this channel in current regulatory domain. + * @NL80211_FREQUENCY_ATTR_RADAR: Radar detection is mandatory + * on this channel in current regulatory domain. + * @NL80211_FREQUENCY_ATTR_MAX_TX_POWER: Maximum transmission power in mBm + * (100 * dBm). + */ +enum nl80211_frequency_attr { + __NL80211_FREQUENCY_ATTR_INVALID, + NL80211_FREQUENCY_ATTR_FREQ, + NL80211_FREQUENCY_ATTR_DISABLED, + NL80211_FREQUENCY_ATTR_PASSIVE_SCAN, + NL80211_FREQUENCY_ATTR_NO_IBSS, + NL80211_FREQUENCY_ATTR_RADAR, + NL80211_FREQUENCY_ATTR_MAX_TX_POWER, + + /* keep last */ + __NL80211_FREQUENCY_ATTR_AFTER_LAST, + NL80211_FREQUENCY_ATTR_MAX = __NL80211_FREQUENCY_ATTR_AFTER_LAST - 1 +}; + +#define NL80211_FREQUENCY_ATTR_MAX_TX_POWER NL80211_FREQUENCY_ATTR_MAX_TX_POWER + +/** + * enum nl80211_bitrate_attr - bitrate attributes + * @NL80211_BITRATE_ATTR_RATE: Bitrate in units of 100 kbps + * @NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE: Short preamble supported + * in 2.4 GHz band. + */ +enum nl80211_bitrate_attr { + __NL80211_BITRATE_ATTR_INVALID, + NL80211_BITRATE_ATTR_RATE, + NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE, + + /* keep last */ + __NL80211_BITRATE_ATTR_AFTER_LAST, + NL80211_BITRATE_ATTR_MAX = __NL80211_BITRATE_ATTR_AFTER_LAST - 1 +}; + +/** + * enum nl80211_initiator - Indicates the initiator of a reg domain request + * @NL80211_REGDOM_SET_BY_CORE: Core queried CRDA for a dynamic world + * regulatory domain. + * @NL80211_REGDOM_SET_BY_USER: User asked the wireless core to set the + * regulatory domain. + * @NL80211_REGDOM_SET_BY_DRIVER: a wireless drivers has hinted to the + * wireless core it thinks its knows the regulatory domain we should be in. + * @NL80211_REGDOM_SET_BY_COUNTRY_IE: the wireless core has received an + * 802.11 country information element with regulatory information it + * thinks we should consider. + */ +enum nl80211_reg_initiator { + NL80211_REGDOM_SET_BY_CORE, + NL80211_REGDOM_SET_BY_USER, + NL80211_REGDOM_SET_BY_DRIVER, + NL80211_REGDOM_SET_BY_COUNTRY_IE, +}; + +/** + * enum nl80211_reg_type - specifies the type of regulatory domain + * @NL80211_REGDOM_TYPE_COUNTRY: the regulatory domain set is one that pertains + * to a specific country. When this is set you can count on the + * ISO / IEC 3166 alpha2 country code being valid. + * @NL80211_REGDOM_TYPE_WORLD: the regulatory set domain is the world regulatory + * domain. + * @NL80211_REGDOM_TYPE_CUSTOM_WORLD: the regulatory domain set is a custom + * driver specific world regulatory domain. These do not apply system-wide + * and are only applicable to the individual devices which have requested + * them to be applied. + * @NL80211_REGDOM_TYPE_INTERSECTION: the regulatory domain set is the product + * of an intersection between two regulatory domains -- the previously + * set regulatory domain on the system and the last accepted regulatory + * domain request to be processed. + */ +enum nl80211_reg_type { + NL80211_REGDOM_TYPE_COUNTRY, + NL80211_REGDOM_TYPE_WORLD, + NL80211_REGDOM_TYPE_CUSTOM_WORLD, + NL80211_REGDOM_TYPE_INTERSECTION, +}; + +/** + * enum nl80211_reg_rule_attr - regulatory rule attributes + * @NL80211_ATTR_REG_RULE_FLAGS: a set of flags which specify additional + * considerations for a given frequency range. These are the + * &enum nl80211_reg_rule_flags. + * @NL80211_ATTR_FREQ_RANGE_START: starting frequencry for the regulatory + * rule in KHz. This is not a center of frequency but an actual regulatory + * band edge. + * @NL80211_ATTR_FREQ_RANGE_END: ending frequency for the regulatory rule + * in KHz. This is not a center a frequency but an actual regulatory + * band edge. + * @NL80211_ATTR_FREQ_RANGE_MAX_BW: maximum allowed bandwidth for this + * frequency range, in KHz. + * @NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN: the maximum allowed antenna gain + * for a given frequency range. The value is in mBi (100 * dBi). + * If you don't have one then don't send this. + * @NL80211_ATTR_POWER_RULE_MAX_EIRP: the maximum allowed EIRP for + * a given frequency range. The value is in mBm (100 * dBm). + */ +enum nl80211_reg_rule_attr { + __NL80211_REG_RULE_ATTR_INVALID, + NL80211_ATTR_REG_RULE_FLAGS, + + NL80211_ATTR_FREQ_RANGE_START, + NL80211_ATTR_FREQ_RANGE_END, + NL80211_ATTR_FREQ_RANGE_MAX_BW, + + NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN, + NL80211_ATTR_POWER_RULE_MAX_EIRP, + + /* keep last */ + __NL80211_REG_RULE_ATTR_AFTER_LAST, + NL80211_REG_RULE_ATTR_MAX = __NL80211_REG_RULE_ATTR_AFTER_LAST - 1 +}; + +/** + * enum nl80211_reg_rule_flags - regulatory rule flags + * + * @NL80211_RRF_NO_OFDM: OFDM modulation not allowed + * @NL80211_RRF_NO_CCK: CCK modulation not allowed + * @NL80211_RRF_NO_INDOOR: indoor operation not allowed + * @NL80211_RRF_NO_OUTDOOR: outdoor operation not allowed + * @NL80211_RRF_DFS: DFS support is required to be used + * @NL80211_RRF_PTP_ONLY: this is only for Point To Point links + * @NL80211_RRF_PTMP_ONLY: this is only for Point To Multi Point links + * @NL80211_RRF_PASSIVE_SCAN: passive scan is required + * @NL80211_RRF_NO_IBSS: no IBSS is allowed + */ +enum nl80211_reg_rule_flags { + NL80211_RRF_NO_OFDM = 1<<0, + NL80211_RRF_NO_CCK = 1<<1, + NL80211_RRF_NO_INDOOR = 1<<2, + NL80211_RRF_NO_OUTDOOR = 1<<3, + NL80211_RRF_DFS = 1<<4, + NL80211_RRF_PTP_ONLY = 1<<5, + NL80211_RRF_PTMP_ONLY = 1<<6, + NL80211_RRF_PASSIVE_SCAN = 1<<7, + NL80211_RRF_NO_IBSS = 1<<8, +}; + +/** + * enum nl80211_survey_info - survey information + * + * These attribute types are used with %NL80211_ATTR_SURVEY_INFO + * when getting information about a survey. + * + * @__NL80211_SURVEY_INFO_INVALID: attribute number 0 is reserved + * @NL80211_SURVEY_INFO_FREQUENCY: center frequency of channel + * @NL80211_SURVEY_INFO_NOISE: noise level of channel (u8, dBm) + */ +enum nl80211_survey_info { + __NL80211_SURVEY_INFO_INVALID, + NL80211_SURVEY_INFO_FREQUENCY, + NL80211_SURVEY_INFO_NOISE, + + /* keep last */ + __NL80211_SURVEY_INFO_AFTER_LAST, + NL80211_SURVEY_INFO_MAX = __NL80211_SURVEY_INFO_AFTER_LAST - 1 +}; + +/** + * enum nl80211_mntr_flags - monitor configuration flags + * + * Monitor configuration flags. + * + * @__NL80211_MNTR_FLAG_INVALID: reserved + * + * @NL80211_MNTR_FLAG_FCSFAIL: pass frames with bad FCS + * @NL80211_MNTR_FLAG_PLCPFAIL: pass frames with bad PLCP + * @NL80211_MNTR_FLAG_CONTROL: pass control frames + * @NL80211_MNTR_FLAG_OTHER_BSS: disable BSSID filtering + * @NL80211_MNTR_FLAG_COOK_FRAMES: report frames after processing. + * overrides all other flags. + * + * @__NL80211_MNTR_FLAG_AFTER_LAST: internal use + * @NL80211_MNTR_FLAG_MAX: highest possible monitor flag + */ +enum nl80211_mntr_flags { + __NL80211_MNTR_FLAG_INVALID, + NL80211_MNTR_FLAG_FCSFAIL, + NL80211_MNTR_FLAG_PLCPFAIL, + NL80211_MNTR_FLAG_CONTROL, + NL80211_MNTR_FLAG_OTHER_BSS, + NL80211_MNTR_FLAG_COOK_FRAMES, + + /* keep last */ + __NL80211_MNTR_FLAG_AFTER_LAST, + NL80211_MNTR_FLAG_MAX = __NL80211_MNTR_FLAG_AFTER_LAST - 1 +}; + +/** + * enum nl80211_meshconf_params - mesh configuration parameters + * + * Mesh configuration parameters + * + * @__NL80211_MESHCONF_INVALID: internal use + * + * @NL80211_MESHCONF_RETRY_TIMEOUT: specifies the initial retry timeout in + * millisecond units, used by the Peer Link Open message + * + * @NL80211_MESHCONF_CONFIRM_TIMEOUT: specifies the inital confirm timeout, in + * millisecond units, used by the peer link management to close a peer link + * + * @NL80211_MESHCONF_HOLDING_TIMEOUT: specifies the holding timeout, in + * millisecond units + * + * @NL80211_MESHCONF_MAX_PEER_LINKS: maximum number of peer links allowed + * on this mesh interface + * + * @NL80211_MESHCONF_MAX_RETRIES: specifies the maximum number of peer link + * open retries that can be sent to establish a new peer link instance in a + * mesh + * + * @NL80211_MESHCONF_TTL: specifies the value of TTL field set at a source mesh + * point. + * + * @NL80211_MESHCONF_AUTO_OPEN_PLINKS: whether we should automatically + * open peer links when we detect compatible mesh peers. + * + * @NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES: the number of action frames + * containing a PREQ that an MP can send to a particular destination (path + * target) + * + * @NL80211_MESHCONF_PATH_REFRESH_TIME: how frequently to refresh mesh paths + * (in milliseconds) + * + * @NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT: minimum length of time to wait + * until giving up on a path discovery (in milliseconds) + * + * @NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT: The time (in TUs) for which mesh + * points receiving a PREQ shall consider the forwarding information from the + * root to be valid. (TU = time unit) + * + * @NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL: The minimum interval of time (in + * TUs) during which an MP can send only one action frame containing a PREQ + * reference element + * + * @NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME: The interval of time (in TUs) + * that it takes for an HWMP information element to propagate across the mesh + * + * @NL80211_MESHCONF_ROOTMODE: whether root mode is enabled or not + * + * @NL80211_MESHCONF_ATTR_MAX: highest possible mesh configuration attribute + * + * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use + */ +enum nl80211_meshconf_params { + __NL80211_MESHCONF_INVALID, + NL80211_MESHCONF_RETRY_TIMEOUT, + NL80211_MESHCONF_CONFIRM_TIMEOUT, + NL80211_MESHCONF_HOLDING_TIMEOUT, + NL80211_MESHCONF_MAX_PEER_LINKS, + NL80211_MESHCONF_MAX_RETRIES, + NL80211_MESHCONF_TTL, + NL80211_MESHCONF_AUTO_OPEN_PLINKS, + NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, + NL80211_MESHCONF_PATH_REFRESH_TIME, + NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, + NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, + NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, + NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, + NL80211_MESHCONF_HWMP_ROOTMODE, + + /* keep last */ + __NL80211_MESHCONF_ATTR_AFTER_LAST, + NL80211_MESHCONF_ATTR_MAX = __NL80211_MESHCONF_ATTR_AFTER_LAST - 1 +}; + +/** + * enum nl80211_txq_attr - TX queue parameter attributes + * @__NL80211_TXQ_ATTR_INVALID: Attribute number 0 is reserved + * @NL80211_TXQ_ATTR_QUEUE: TX queue identifier (NL80211_TXQ_Q_*) + * @NL80211_TXQ_ATTR_TXOP: Maximum burst time in units of 32 usecs, 0 meaning + * disabled + * @NL80211_TXQ_ATTR_CWMIN: Minimum contention window [a value of the form + * 2^n-1 in the range 1..32767] + * @NL80211_TXQ_ATTR_CWMAX: Maximum contention window [a value of the form + * 2^n-1 in the range 1..32767] + * @NL80211_TXQ_ATTR_AIFS: Arbitration interframe space [0..255] + * @__NL80211_TXQ_ATTR_AFTER_LAST: Internal + * @NL80211_TXQ_ATTR_MAX: Maximum TXQ attribute number + */ +enum nl80211_txq_attr { + __NL80211_TXQ_ATTR_INVALID, + NL80211_TXQ_ATTR_QUEUE, + NL80211_TXQ_ATTR_TXOP, + NL80211_TXQ_ATTR_CWMIN, + NL80211_TXQ_ATTR_CWMAX, + NL80211_TXQ_ATTR_AIFS, + + /* keep last */ + __NL80211_TXQ_ATTR_AFTER_LAST, + NL80211_TXQ_ATTR_MAX = __NL80211_TXQ_ATTR_AFTER_LAST - 1 +}; + +enum nl80211_txq_q { + NL80211_TXQ_Q_VO, + NL80211_TXQ_Q_VI, + NL80211_TXQ_Q_BE, + NL80211_TXQ_Q_BK +}; + +enum nl80211_channel_type { + NL80211_CHAN_NO_HT, + NL80211_CHAN_HT20, + NL80211_CHAN_HT40MINUS, + NL80211_CHAN_HT40PLUS +}; + +/** + * enum nl80211_bss - netlink attributes for a BSS + * + * @__NL80211_BSS_INVALID: invalid + * @NL80211_BSS_FREQUENCY: frequency in MHz (u32) + * @NL80211_BSS_TSF: TSF of the received probe response/beacon (u64) + * @NL80211_BSS_BEACON_INTERVAL: beacon interval of the (I)BSS (u16) + * @NL80211_BSS_CAPABILITY: capability field (CPU order, u16) + * @NL80211_BSS_INFORMATION_ELEMENTS: binary attribute containing the + * raw information elements from the probe response/beacon (bin); + * if the %NL80211_BSS_BEACON_IES attribute is present, the IEs here are + * from a Probe Response frame; otherwise they are from a Beacon frame. + * However, if the driver does not indicate the source of the IEs, these + * IEs may be from either frame subtype. + * @NL80211_BSS_SIGNAL_MBM: signal strength of probe response/beacon + * in mBm (100 * dBm) (s32) + * @NL80211_BSS_SIGNAL_UNSPEC: signal strength of the probe response/beacon + * in unspecified units, scaled to 0..100 (u8) + * @NL80211_BSS_STATUS: status, if this BSS is "used" + * @NL80211_BSS_SEEN_MS_AGO: age of this BSS entry in ms + * @NL80211_BSS_BEACON_IES: binary attribute containing the raw information + * elements from a Beacon frame (bin); not present if no Beacon frame has + * yet been received + * @__NL80211_BSS_AFTER_LAST: internal + * @NL80211_BSS_MAX: highest BSS attribute + */ +enum nl80211_bss { + __NL80211_BSS_INVALID, + NL80211_BSS_BSSID, + NL80211_BSS_FREQUENCY, + NL80211_BSS_TSF, + NL80211_BSS_BEACON_INTERVAL, + NL80211_BSS_CAPABILITY, + NL80211_BSS_INFORMATION_ELEMENTS, + NL80211_BSS_SIGNAL_MBM, + NL80211_BSS_SIGNAL_UNSPEC, + NL80211_BSS_STATUS, + NL80211_BSS_SEEN_MS_AGO, + NL80211_BSS_BEACON_IES, + + /* keep last */ + __NL80211_BSS_AFTER_LAST, + NL80211_BSS_MAX = __NL80211_BSS_AFTER_LAST - 1 +}; + +/** + * enum nl80211_bss_status - BSS "status" + */ +enum nl80211_bss_status { + NL80211_BSS_STATUS_AUTHENTICATED, + NL80211_BSS_STATUS_ASSOCIATED, + NL80211_BSS_STATUS_IBSS_JOINED, +}; + +/** + * enum nl80211_auth_type - AuthenticationType + * + * @NL80211_AUTHTYPE_OPEN_SYSTEM: Open System authentication + * @NL80211_AUTHTYPE_SHARED_KEY: Shared Key authentication (WEP only) + * @NL80211_AUTHTYPE_FT: Fast BSS Transition (IEEE 802.11r) + * @NL80211_AUTHTYPE_NETWORK_EAP: Network EAP (some Cisco APs and mainly LEAP) + * @__NL80211_AUTHTYPE_NUM: internal + * @NL80211_AUTHTYPE_MAX: maximum valid auth algorithm + * @NL80211_AUTHTYPE_AUTOMATIC: determine automatically (if necessary by + * trying multiple times); this is invalid in netlink -- leave out + * the attribute for this on CONNECT commands. + */ +enum nl80211_auth_type { + NL80211_AUTHTYPE_OPEN_SYSTEM, + NL80211_AUTHTYPE_SHARED_KEY, + NL80211_AUTHTYPE_FT, + NL80211_AUTHTYPE_NETWORK_EAP, + + /* keep last */ + __NL80211_AUTHTYPE_NUM, + NL80211_AUTHTYPE_MAX = __NL80211_AUTHTYPE_NUM - 1, + NL80211_AUTHTYPE_AUTOMATIC +}; + +/** + * enum nl80211_key_type - Key Type + * @NL80211_KEYTYPE_GROUP: Group (broadcast/multicast) key + * @NL80211_KEYTYPE_PAIRWISE: Pairwise (unicast/individual) key + * @NL80211_KEYTYPE_PEERKEY: PeerKey (DLS) + */ +enum nl80211_key_type { + NL80211_KEYTYPE_GROUP, + NL80211_KEYTYPE_PAIRWISE, + NL80211_KEYTYPE_PEERKEY, +}; + +/** + * enum nl80211_mfp - Management frame protection state + * @NL80211_MFP_NO: Management frame protection not used + * @NL80211_MFP_REQUIRED: Management frame protection required + */ +enum nl80211_mfp { + NL80211_MFP_NO, + NL80211_MFP_REQUIRED, +}; + +enum nl80211_wpa_versions { + NL80211_WPA_VERSION_1 = 1 << 0, + NL80211_WPA_VERSION_2 = 1 << 1, +}; + +/** + * enum nl80211_key_attributes - key attributes + * @__NL80211_KEY_INVALID: invalid + * @NL80211_KEY_DATA: (temporal) key data; for TKIP this consists of + * 16 bytes encryption key followed by 8 bytes each for TX and RX MIC + * keys + * @NL80211_KEY_IDX: key ID (u8, 0-3) + * @NL80211_KEY_CIPHER: key cipher suite (u32, as defined by IEEE 802.11 + * section 7.3.2.25.1, e.g. 0x000FAC04) + * @NL80211_KEY_SEQ: transmit key sequence number (IV/PN) for TKIP and + * CCMP keys, each six bytes in little endian + * @NL80211_KEY_DEFAULT: flag indicating default key + * @NL80211_KEY_DEFAULT_MGMT: flag indicating default management key + * @__NL80211_KEY_AFTER_LAST: internal + * @NL80211_KEY_MAX: highest key attribute + */ +enum nl80211_key_attributes { + __NL80211_KEY_INVALID, + NL80211_KEY_DATA, + NL80211_KEY_IDX, + NL80211_KEY_CIPHER, + NL80211_KEY_SEQ, + NL80211_KEY_DEFAULT, + NL80211_KEY_DEFAULT_MGMT, + + /* keep last */ + __NL80211_KEY_AFTER_LAST, + NL80211_KEY_MAX = __NL80211_KEY_AFTER_LAST - 1 +}; + +/** + * enum nl80211_tx_rate_attributes - TX rate set attributes + * @__NL80211_TXRATE_INVALID: invalid + * @NL80211_TXRATE_LEGACY: Legacy (non-MCS) rates allowed for TX rate selection + * in an array of rates as defined in IEEE 802.11 7.3.2.2 (u8 values with + * 1 = 500 kbps) but without the IE length restriction (at most + * %NL80211_MAX_SUPP_RATES in a single array). + * @__NL80211_TXRATE_AFTER_LAST: internal + * @NL80211_TXRATE_MAX: highest TX rate attribute + */ +enum nl80211_tx_rate_attributes { + __NL80211_TXRATE_INVALID, + NL80211_TXRATE_LEGACY, + + /* keep last */ + __NL80211_TXRATE_AFTER_LAST, + NL80211_TXRATE_MAX = __NL80211_TXRATE_AFTER_LAST - 1 +}; + +/** + * enum nl80211_band - Frequency band + * @NL80211_BAND_2GHZ - 2.4 GHz ISM band + * @NL80211_BAND_5GHZ - around 5 GHz band (4.9 - 5.7 GHz) + */ +enum nl80211_band { + NL80211_BAND_2GHZ, + NL80211_BAND_5GHZ, +}; + +enum nl80211_ps_state { + NL80211_PS_DISABLED, + NL80211_PS_ENABLED, +}; + +/** + * enum nl80211_attr_cqm - connection quality monitor attributes + * @__NL80211_ATTR_CQM_INVALID: invalid + * @NL80211_ATTR_CQM_RSSI_THOLD: RSSI threshold in dBm. This value specifies + * the threshold for the RSSI level at which an event will be sent. Zero + * to disable. + * @NL80211_ATTR_CQM_RSSI_HYST: RSSI hysteresis in dBm. This value specifies + * the minimum amount the RSSI level must change after an event before a + * new event may be issued (to reduce effects of RSSI oscillation). + * @NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT: RSSI threshold event + * @__NL80211_ATTR_CQM_AFTER_LAST: internal + * @NL80211_ATTR_CQM_MAX: highest key attribute + */ +enum nl80211_attr_cqm { + __NL80211_ATTR_CQM_INVALID, + NL80211_ATTR_CQM_RSSI_THOLD, + NL80211_ATTR_CQM_RSSI_HYST, + NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT, + + /* keep last */ + __NL80211_ATTR_CQM_AFTER_LAST, + NL80211_ATTR_CQM_MAX = __NL80211_ATTR_CQM_AFTER_LAST - 1 +}; + +/** + * enum nl80211_cqm_rssi_threshold_event - RSSI threshold event + * @NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW - The RSSI level is lower than the + * configured threshold + * @NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH - The RSSI is higher than the + * configured threshold + */ +enum nl80211_cqm_rssi_threshold_event { + NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, + NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, +}; + +#endif /* __LINUX_NL80211_H */ diff --git a/src/drivers/priv_netlink.h b/src/drivers/priv_netlink.h index 2a31e251a0138..23eff83fadd43 100644 --- a/src/drivers/priv_netlink.h +++ b/src/drivers/priv_netlink.h @@ -53,8 +53,17 @@ #define NLMSG_ALIGNTO 4 #define NLMSG_ALIGN(len) (((len) + NLMSG_ALIGNTO - 1) & ~(NLMSG_ALIGNTO - 1)) +#define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr))) #define NLMSG_LENGTH(len) ((len) + NLMSG_ALIGN(sizeof(struct nlmsghdr))) +#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len)) #define NLMSG_DATA(nlh) ((void*) (((char*) nlh) + NLMSG_LENGTH(0))) +#define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \ + (struct nlmsghdr *) \ + (((char *)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len))) +#define NLMSG_OK(nlh,len) ((len) >= (int) sizeof(struct nlmsghdr) && \ + (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \ + (int) (nlh)->nlmsg_len <= (len)) +#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len))) #define RTA_ALIGNTO 4 #define RTA_ALIGN(len) (((len) + RTA_ALIGNTO - 1) & ~(RTA_ALIGNTO - 1)) diff --git a/src/drivers/radiotap.c b/src/drivers/radiotap.c deleted file mode 100644 index 804473fa4bfb9..0000000000000 --- a/src/drivers/radiotap.c +++ /dev/null @@ -1,287 +0,0 @@ -/* - * Radiotap parser - * - * Copyright 2007 Andy Green <andy@warmcat.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - * - * - * Modified for userspace by Johannes Berg <johannes@sipsolutions.net> - * I only modified some things on top to ease syncing should bugs be found. - */ - -#include "includes.h" - -#include "common.h" -#include "radiotap_iter.h" - -#define le16_to_cpu le_to_host16 -#define le32_to_cpu le_to_host32 -#define __le32 uint32_t -#define ulong unsigned long -#define unlikely(cond) (cond) -#define get_unaligned(p) \ -({ \ - struct packed_dummy_struct { \ - typeof(*(p)) __val; \ - } __attribute__((packed)) *__ptr = (void *) (p); \ - \ - __ptr->__val; \ -}) - -/* function prototypes and related defs are in radiotap_iter.h */ - -/** - * ieee80211_radiotap_iterator_init - radiotap parser iterator initialization - * @iterator: radiotap_iterator to initialize - * @radiotap_header: radiotap header to parse - * @max_length: total length we can parse into (eg, whole packet length) - * - * Returns: 0 or a negative error code if there is a problem. - * - * This function initializes an opaque iterator struct which can then - * be passed to ieee80211_radiotap_iterator_next() to visit every radiotap - * argument which is present in the header. It knows about extended - * present headers and handles them. - * - * How to use: - * call __ieee80211_radiotap_iterator_init() to init a semi-opaque iterator - * struct ieee80211_radiotap_iterator (no need to init the struct beforehand) - * checking for a good 0 return code. Then loop calling - * __ieee80211_radiotap_iterator_next()... it returns either 0, - * -ENOENT if there are no more args to parse, or -EINVAL if there is a problem. - * The iterator's @this_arg member points to the start of the argument - * associated with the current argument index that is present, which can be - * found in the iterator's @this_arg_index member. This arg index corresponds - * to the IEEE80211_RADIOTAP_... defines. - * - * Radiotap header length: - * You can find the CPU-endian total radiotap header length in - * iterator->max_length after executing ieee80211_radiotap_iterator_init() - * successfully. - * - * Alignment Gotcha: - * You must take care when dereferencing iterator.this_arg - * for multibyte types... the pointer is not aligned. Use - * get_unaligned((type *)iterator.this_arg) to dereference - * iterator.this_arg for type "type" safely on all arches. - * - * Example code: - * See Documentation/networking/radiotap-headers.txt - */ - -int ieee80211_radiotap_iterator_init( - struct ieee80211_radiotap_iterator *iterator, - struct ieee80211_radiotap_header *radiotap_header, - int max_length) -{ - /* Linux only supports version 0 radiotap format */ - if (radiotap_header->it_version) - return -EINVAL; - - /* sanity check for allowed length and radiotap length field */ - if (max_length < le16_to_cpu(get_unaligned(&radiotap_header->it_len))) - return -EINVAL; - - iterator->rtheader = radiotap_header; - iterator->max_length = le16_to_cpu(get_unaligned( - &radiotap_header->it_len)); - iterator->arg_index = 0; - iterator->bitmap_shifter = le32_to_cpu(get_unaligned( - &radiotap_header->it_present)); - iterator->arg = (u8 *)radiotap_header + sizeof(*radiotap_header); - iterator->this_arg = NULL; - - /* find payload start allowing for extended bitmap(s) */ - - if (unlikely(iterator->bitmap_shifter & (1<<IEEE80211_RADIOTAP_EXT))) { - while (le32_to_cpu(get_unaligned((__le32 *)iterator->arg)) & - (1<<IEEE80211_RADIOTAP_EXT)) { - iterator->arg += sizeof(u32); - - /* - * check for insanity where the present bitmaps - * keep claiming to extend up to or even beyond the - * stated radiotap header length - */ - - if (((ulong)iterator->arg - (ulong)iterator->rtheader) - > (ulong)iterator->max_length) - return -EINVAL; - } - - iterator->arg += sizeof(u32); - - /* - * no need to check again for blowing past stated radiotap - * header length, because ieee80211_radiotap_iterator_next - * checks it before it is dereferenced - */ - } - - /* we are all initialized happily */ - - return 0; -} - - -/** - * ieee80211_radiotap_iterator_next - return next radiotap parser iterator arg - * @iterator: radiotap_iterator to move to next arg (if any) - * - * Returns: 0 if there is an argument to handle, - * -ENOENT if there are no more args or -EINVAL - * if there is something else wrong. - * - * This function provides the next radiotap arg index (IEEE80211_RADIOTAP_*) - * in @this_arg_index and sets @this_arg to point to the - * payload for the field. It takes care of alignment handling and extended - * present fields. @this_arg can be changed by the caller (eg, - * incremented to move inside a compound argument like - * IEEE80211_RADIOTAP_CHANNEL). The args pointed to are in - * little-endian format whatever the endianess of your CPU. - * - * Alignment Gotcha: - * You must take care when dereferencing iterator.this_arg - * for multibyte types... the pointer is not aligned. Use - * get_unaligned((type *)iterator.this_arg) to dereference - * iterator.this_arg for type "type" safely on all arches. - */ - -int ieee80211_radiotap_iterator_next( - struct ieee80211_radiotap_iterator *iterator) -{ - - /* - * small length lookup table for all radiotap types we heard of - * starting from b0 in the bitmap, so we can walk the payload - * area of the radiotap header - * - * There is a requirement to pad args, so that args - * of a given length must begin at a boundary of that length - * -- but note that compound args are allowed (eg, 2 x u16 - * for IEEE80211_RADIOTAP_CHANNEL) so total arg length is not - * a reliable indicator of alignment requirement. - * - * upper nybble: content alignment for arg - * lower nybble: content length for arg - */ - - static const u8 rt_sizes[] = { - [IEEE80211_RADIOTAP_TSFT] = 0x88, - [IEEE80211_RADIOTAP_FLAGS] = 0x11, - [IEEE80211_RADIOTAP_RATE] = 0x11, - [IEEE80211_RADIOTAP_CHANNEL] = 0x24, - [IEEE80211_RADIOTAP_FHSS] = 0x22, - [IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = 0x11, - [IEEE80211_RADIOTAP_DBM_ANTNOISE] = 0x11, - [IEEE80211_RADIOTAP_LOCK_QUALITY] = 0x22, - [IEEE80211_RADIOTAP_TX_ATTENUATION] = 0x22, - [IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = 0x22, - [IEEE80211_RADIOTAP_DBM_TX_POWER] = 0x11, - [IEEE80211_RADIOTAP_ANTENNA] = 0x11, - [IEEE80211_RADIOTAP_DB_ANTSIGNAL] = 0x11, - [IEEE80211_RADIOTAP_DB_ANTNOISE] = 0x11, - [IEEE80211_RADIOTAP_RX_FLAGS] = 0x22, - [IEEE80211_RADIOTAP_TX_FLAGS] = 0x22, - [IEEE80211_RADIOTAP_RTS_RETRIES] = 0x11, - [IEEE80211_RADIOTAP_DATA_RETRIES] = 0x11, - /* - * add more here as they are defined in - * include/net/ieee80211_radiotap.h - */ - }; - - /* - * for every radiotap entry we can at - * least skip (by knowing the length)... - */ - - while (iterator->arg_index < (int) sizeof(rt_sizes)) { - int hit = 0; - int pad; - - if (!(iterator->bitmap_shifter & 1)) - goto next_entry; /* arg not present */ - - /* - * arg is present, account for alignment padding - * 8-bit args can be at any alignment - * 16-bit args must start on 16-bit boundary - * 32-bit args must start on 32-bit boundary - * 64-bit args must start on 64-bit boundary - * - * note that total arg size can differ from alignment of - * elements inside arg, so we use upper nybble of length - * table to base alignment on - * - * also note: these alignments are ** relative to the - * start of the radiotap header **. There is no guarantee - * that the radiotap header itself is aligned on any - * kind of boundary. - * - * the above is why get_unaligned() is used to dereference - * multibyte elements from the radiotap area - */ - - pad = (((ulong)iterator->arg) - - ((ulong)iterator->rtheader)) & - ((rt_sizes[iterator->arg_index] >> 4) - 1); - - if (pad) - iterator->arg += - (rt_sizes[iterator->arg_index] >> 4) - pad; - - /* - * this is what we will return to user, but we need to - * move on first so next call has something fresh to test - */ - iterator->this_arg_index = iterator->arg_index; - iterator->this_arg = iterator->arg; - hit = 1; - - /* internally move on the size of this arg */ - iterator->arg += rt_sizes[iterator->arg_index] & 0x0f; - - /* - * check for insanity where we are given a bitmap that - * claims to have more arg content than the length of the - * radiotap section. We will normally end up equalling this - * max_length on the last arg, never exceeding it. - */ - - if (((ulong)iterator->arg - (ulong)iterator->rtheader) > - (ulong) iterator->max_length) - return -EINVAL; - - next_entry: - iterator->arg_index++; - if (unlikely((iterator->arg_index & 31) == 0)) { - /* completed current u32 bitmap */ - if (iterator->bitmap_shifter & 1) { - /* b31 was set, there is more */ - /* move to next u32 bitmap */ - iterator->bitmap_shifter = le32_to_cpu( - get_unaligned(iterator->next_bitmap)); - iterator->next_bitmap++; - } else - /* no more bitmaps: end */ - iterator->arg_index = sizeof(rt_sizes); - } else /* just try the next bit */ - iterator->bitmap_shifter >>= 1; - - /* if we found a valid arg earlier, return it now */ - if (hit) - return 0; - } - - /* we don't know how to handle any more args, we're done */ - return -ENOENT; -} diff --git a/src/drivers/radiotap.h b/src/drivers/radiotap.h deleted file mode 100644 index 508264c4cf333..0000000000000 --- a/src/drivers/radiotap.h +++ /dev/null @@ -1,242 +0,0 @@ -/* $FreeBSD: src/sys/net80211/ieee80211_radiotap.h,v 1.5 2005/01/22 20:12:05 sam Exp $ */ -/* $NetBSD: ieee80211_radiotap.h,v 1.11 2005/06/22 06:16:02 dyoung Exp $ */ - -/*- - * Copyright (c) 2003, 2004 David Young. 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 David Young may not be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY DAVID YOUNG ``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 DAVID - * YOUNG 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. - */ - -/* - * Modifications to fit into the linux IEEE 802.11 stack, - * Mike Kershaw (dragorn@kismetwireless.net) - */ - -#ifndef IEEE80211RADIOTAP_H -#define IEEE80211RADIOTAP_H - -#include <stdint.h> - -/* Base version of the radiotap packet header data */ -#define PKTHDR_RADIOTAP_VERSION 0 - -/* A generic radio capture format is desirable. There is one for - * Linux, but it is neither rigidly defined (there were not even - * units given for some fields) nor easily extensible. - * - * I suggest the following extensible radio capture format. It is - * based on a bitmap indicating which fields are present. - * - * I am trying to describe precisely what the application programmer - * should expect in the following, and for that reason I tell the - * units and origin of each measurement (where it applies), or else I - * use sufficiently weaselly language ("is a monotonically nondecreasing - * function of...") that I cannot set false expectations for lawyerly - * readers. - */ - -/* The radio capture header precedes the 802.11 header. - * All data in the header is little endian on all platforms. - */ -struct ieee80211_radiotap_header { - uint8_t it_version; /* Version 0. Only increases - * for drastic changes, - * introduction of compatible - * new fields does not count. - */ - uint8_t it_pad; - uint16_t it_len; /* length of the whole - * header in bytes, including - * it_version, it_pad, - * it_len, and data fields. - */ - uint32_t it_present; /* A bitmap telling which - * fields are present. Set bit 31 - * (0x80000000) to extend the - * bitmap by another 32 bits. - * Additional extensions are made - * by setting bit 31. - */ -}; - -/* Name Data type Units - * ---- --------- ----- - * - * IEEE80211_RADIOTAP_TSFT __le64 microseconds - * - * Value in microseconds of the MAC's 64-bit 802.11 Time - * Synchronization Function timer when the first bit of the - * MPDU arrived at the MAC. For received frames, only. - * - * IEEE80211_RADIOTAP_CHANNEL 2 x uint16_t MHz, bitmap - * - * Tx/Rx frequency in MHz, followed by flags (see below). - * - * IEEE80211_RADIOTAP_FHSS uint16_t see below - * - * For frequency-hopping radios, the hop set (first byte) - * and pattern (second byte). - * - * IEEE80211_RADIOTAP_RATE u8 500kb/s - * - * Tx/Rx data rate - * - * IEEE80211_RADIOTAP_DBM_ANTSIGNAL s8 decibels from - * one milliwatt (dBm) - * - * RF signal power at the antenna, decibel difference from - * one milliwatt. - * - * IEEE80211_RADIOTAP_DBM_ANTNOISE s8 decibels from - * one milliwatt (dBm) - * - * RF noise power at the antenna, decibel difference from one - * milliwatt. - * - * IEEE80211_RADIOTAP_DB_ANTSIGNAL u8 decibel (dB) - * - * RF signal power at the antenna, decibel difference from an - * arbitrary, fixed reference. - * - * IEEE80211_RADIOTAP_DB_ANTNOISE u8 decibel (dB) - * - * RF noise power at the antenna, decibel difference from an - * arbitrary, fixed reference point. - * - * IEEE80211_RADIOTAP_LOCK_QUALITY uint16_t unitless - * - * Quality of Barker code lock. Unitless. Monotonically - * nondecreasing with "better" lock strength. Called "Signal - * Quality" in datasheets. (Is there a standard way to measure - * this?) - * - * IEEE80211_RADIOTAP_TX_ATTENUATION uint16_t unitless - * - * Transmit power expressed as unitless distance from max - * power set at factory calibration. 0 is max power. - * Monotonically nondecreasing with lower power levels. - * - * IEEE80211_RADIOTAP_DB_TX_ATTENUATION uint16_t decibels (dB) - * - * Transmit power expressed as decibel distance from max power - * set at factory calibration. 0 is max power. Monotonically - * nondecreasing with lower power levels. - * - * IEEE80211_RADIOTAP_DBM_TX_POWER s8 decibels from - * one milliwatt (dBm) - * - * Transmit power expressed as dBm (decibels from a 1 milliwatt - * reference). This is the absolute power level measured at - * the antenna port. - * - * IEEE80211_RADIOTAP_FLAGS u8 bitmap - * - * Properties of transmitted and received frames. See flags - * defined below. - * - * IEEE80211_RADIOTAP_ANTENNA u8 antenna index - * - * Unitless indication of the Rx/Tx antenna for this packet. - * The first antenna is antenna 0. - * - * IEEE80211_RADIOTAP_RX_FLAGS uint16_t bitmap - * - * Properties of received frames. See flags defined below. - * - * IEEE80211_RADIOTAP_TX_FLAGS uint16_t bitmap - * - * Properties of transmitted frames. See flags defined below. - * - * IEEE80211_RADIOTAP_RTS_RETRIES u8 data - * - * Number of rts retries a transmitted frame used. - * - * IEEE80211_RADIOTAP_DATA_RETRIES u8 data - * - * Number of unicast retries a transmitted frame used. - * - */ -enum ieee80211_radiotap_type { - IEEE80211_RADIOTAP_TSFT = 0, - IEEE80211_RADIOTAP_FLAGS = 1, - IEEE80211_RADIOTAP_RATE = 2, - IEEE80211_RADIOTAP_CHANNEL = 3, - IEEE80211_RADIOTAP_FHSS = 4, - IEEE80211_RADIOTAP_DBM_ANTSIGNAL = 5, - IEEE80211_RADIOTAP_DBM_ANTNOISE = 6, - IEEE80211_RADIOTAP_LOCK_QUALITY = 7, - IEEE80211_RADIOTAP_TX_ATTENUATION = 8, - IEEE80211_RADIOTAP_DB_TX_ATTENUATION = 9, - IEEE80211_RADIOTAP_DBM_TX_POWER = 10, - IEEE80211_RADIOTAP_ANTENNA = 11, - IEEE80211_RADIOTAP_DB_ANTSIGNAL = 12, - IEEE80211_RADIOTAP_DB_ANTNOISE = 13, - IEEE80211_RADIOTAP_RX_FLAGS = 14, - IEEE80211_RADIOTAP_TX_FLAGS = 15, - IEEE80211_RADIOTAP_RTS_RETRIES = 16, - IEEE80211_RADIOTAP_DATA_RETRIES = 17, - IEEE80211_RADIOTAP_EXT = 31 -}; - -/* Channel flags. */ -#define IEEE80211_CHAN_TURBO 0x0010 /* Turbo channel */ -#define IEEE80211_CHAN_CCK 0x0020 /* CCK channel */ -#define IEEE80211_CHAN_OFDM 0x0040 /* OFDM channel */ -#define IEEE80211_CHAN_2GHZ 0x0080 /* 2 GHz spectrum channel. */ -#define IEEE80211_CHAN_5GHZ 0x0100 /* 5 GHz spectrum channel */ -#define IEEE80211_CHAN_PASSIVE 0x0200 /* Only passive scan allowed */ -#define IEEE80211_CHAN_DYN 0x0400 /* Dynamic CCK-OFDM channel */ -#define IEEE80211_CHAN_GFSK 0x0800 /* GFSK channel (FHSS PHY) */ - -/* For IEEE80211_RADIOTAP_FLAGS */ -#define IEEE80211_RADIOTAP_F_CFP 0x01 /* sent/received - * during CFP - */ -#define IEEE80211_RADIOTAP_F_SHORTPRE 0x02 /* sent/received - * with short - * preamble - */ -#define IEEE80211_RADIOTAP_F_WEP 0x04 /* sent/received - * with WEP encryption - */ -#define IEEE80211_RADIOTAP_F_FRAG 0x08 /* sent/received - * with fragmentation - */ -#define IEEE80211_RADIOTAP_F_FCS 0x10 /* frame includes FCS */ -#define IEEE80211_RADIOTAP_F_DATAPAD 0x20 /* frame has padding between - * 802.11 header and payload - * (to 32-bit boundary) - */ -/* For IEEE80211_RADIOTAP_RX_FLAGS */ -#define IEEE80211_RADIOTAP_F_RX_BADFCS 0x0001 /* frame failed crc check */ - -/* For IEEE80211_RADIOTAP_TX_FLAGS */ -#define IEEE80211_RADIOTAP_F_TX_FAIL 0x0001 /* failed due to excessive - * retries */ -#define IEEE80211_RADIOTAP_F_TX_CTS 0x0002 /* used cts 'protection' */ -#define IEEE80211_RADIOTAP_F_TX_RTS 0x0004 /* used rts/cts handshake */ - -#endif /* IEEE80211_RADIOTAP_H */ diff --git a/src/drivers/radiotap_iter.h b/src/drivers/radiotap_iter.h deleted file mode 100644 index 92a798a67023a..0000000000000 --- a/src/drivers/radiotap_iter.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef __RADIOTAP_ITER_H -#define __RADIOTAP_ITER_H - -#include "radiotap.h" - -/* Radiotap header iteration - * implemented in radiotap.c - */ -/** - * struct ieee80211_radiotap_iterator - tracks walk thru present radiotap args - * @rtheader: pointer to the radiotap header we are walking through - * @max_length: length of radiotap header in cpu byte ordering - * @this_arg_index: IEEE80211_RADIOTAP_... index of current arg - * @this_arg: pointer to current radiotap arg - * @arg_index: internal next argument index - * @arg: internal next argument pointer - * @next_bitmap: internal pointer to next present u32 - * @bitmap_shifter: internal shifter for curr u32 bitmap, b0 set == arg present - */ - -struct ieee80211_radiotap_iterator { - struct ieee80211_radiotap_header *rtheader; - int max_length; - int this_arg_index; - unsigned char *this_arg; - - int arg_index; - unsigned char *arg; - uint32_t *next_bitmap; - uint32_t bitmap_shifter; -}; - -extern int ieee80211_radiotap_iterator_init( - struct ieee80211_radiotap_iterator *iterator, - struct ieee80211_radiotap_header *radiotap_header, - int max_length); - -extern int ieee80211_radiotap_iterator_next( - struct ieee80211_radiotap_iterator *iterator); - -#endif /* __RADIOTAP_ITER_H */ diff --git a/src/drivers/scan_helpers.c b/src/drivers/scan_helpers.c deleted file mode 100644 index 63387701ea9aa..0000000000000 --- a/src/drivers/scan_helpers.c +++ /dev/null @@ -1,182 +0,0 @@ -/* - * WPA Supplicant - Helper functions for scan result processing - * Copyright (c) 2007-2008, Jouni Malinen <j@w1.fi> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" - -#include "common.h" -#include "drivers/driver.h" -#include "ieee802_11_defs.h" - - -const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie) -{ - const u8 *end, *pos; - - pos = (const u8 *) (res + 1); - end = pos + res->ie_len; - - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) - break; - if (pos[0] == ie) - return pos; - pos += 2 + pos[1]; - } - - return NULL; -} - - -const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res, - u32 vendor_type) -{ - const u8 *end, *pos; - - pos = (const u8 *) (res + 1); - end = pos + res->ie_len; - - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) - break; - if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && - vendor_type == WPA_GET_BE32(&pos[2])) - return pos; - pos += 2 + pos[1]; - } - - return NULL; -} - - -struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res, - u32 vendor_type) -{ - struct wpabuf *buf; - const u8 *end, *pos; - - buf = wpabuf_alloc(res->ie_len); - if (buf == NULL) - return NULL; - - pos = (const u8 *) (res + 1); - end = pos + res->ie_len; - - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) - break; - if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && - vendor_type == WPA_GET_BE32(&pos[2])) - wpabuf_put_data(buf, pos + 2 + 4, pos[1] - 4); - pos += 2 + pos[1]; - } - - if (wpabuf_len(buf) == 0) { - wpabuf_free(buf); - buf = NULL; - } - - return buf; -} - - -int wpa_scan_get_max_rate(const struct wpa_scan_res *res) -{ - int rate = 0; - const u8 *ie; - int i; - - ie = wpa_scan_get_ie(res, WLAN_EID_SUPP_RATES); - for (i = 0; ie && i < ie[1]; i++) { - if ((ie[i + 2] & 0x7f) > rate) - rate = ie[i + 2] & 0x7f; - } - - ie = wpa_scan_get_ie(res, WLAN_EID_EXT_SUPP_RATES); - for (i = 0; ie && i < ie[1]; i++) { - if ((ie[i + 2] & 0x7f) > rate) - rate = ie[i + 2] & 0x7f; - } - - return rate; -} - - -void wpa_scan_results_free(struct wpa_scan_results *res) -{ - size_t i; - - if (res == NULL) - return; - - for (i = 0; i < res->num; i++) - os_free(res->res[i]); - os_free(res->res); - os_free(res); -} - - -/* Compare function for sorting scan results. Return >0 if @b is considered - * better. */ -static int wpa_scan_result_compar(const void *a, const void *b) -{ - struct wpa_scan_res **_wa = (void *) a; - struct wpa_scan_res **_wb = (void *) b; - struct wpa_scan_res *wa = *_wa; - struct wpa_scan_res *wb = *_wb; - int wpa_a, wpa_b, maxrate_a, maxrate_b; - - /* WPA/WPA2 support preferred */ - wpa_a = wpa_scan_get_vendor_ie(wa, WPA_IE_VENDOR_TYPE) != NULL || - wpa_scan_get_ie(wa, WLAN_EID_RSN) != NULL; - wpa_b = wpa_scan_get_vendor_ie(wb, WPA_IE_VENDOR_TYPE) != NULL || - wpa_scan_get_ie(wb, WLAN_EID_RSN) != NULL; - - if (wpa_b && !wpa_a) - return 1; - if (!wpa_b && wpa_a) - return -1; - - /* privacy support preferred */ - if ((wa->caps & IEEE80211_CAP_PRIVACY) == 0 && - (wb->caps & IEEE80211_CAP_PRIVACY)) - return 1; - if ((wa->caps & IEEE80211_CAP_PRIVACY) && - (wb->caps & IEEE80211_CAP_PRIVACY) == 0) - return -1; - - /* best/max rate preferred if signal level close enough XXX */ - if ((wa->level && wb->level && abs(wb->level - wa->level) < 5) || - (wa->qual && wb->qual && abs(wb->qual - wa->qual) < 10)) { - maxrate_a = wpa_scan_get_max_rate(wa); - maxrate_b = wpa_scan_get_max_rate(wb); - if (maxrate_a != maxrate_b) - return maxrate_b - maxrate_a; - } - - /* use freq for channel preference */ - - /* all things being equal, use signal level; if signal levels are - * identical, use quality values since some drivers may only report - * that value and leave the signal level zero */ - if (wb->level == wa->level) - return wb->qual - wa->qual; - return wb->level - wa->level; -} - - -void wpa_scan_sort_results(struct wpa_scan_results *res) -{ - qsort(res->res, res->num, sizeof(struct wpa_scan_res *), - wpa_scan_result_compar); -} diff --git a/src/drivers/wireless_copy.h b/src/drivers/wireless_copy.h new file mode 100644 index 0000000000000..ad764663766f8 --- /dev/null +++ b/src/drivers/wireless_copy.h @@ -0,0 +1,1099 @@ +/* This is based on Linux Wireless Extensions header file from WIRELESS_EXT 18. + * I have just removed kernel related headers and added some typedefs etc. to + * make this easier to include into user space programs. + * Jouni Malinen, 2005-03-12. + */ + + +/* + * This file define a set of standard wireless extensions + * + * Version : 19 18.3.05 + * + * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> + * Copyright (c) 1997-2005 Jean Tourrilhes, All Rights Reserved. + */ + +#ifndef _LINUX_WIRELESS_H +#define _LINUX_WIRELESS_H + +/************************** DOCUMENTATION **************************/ +/* + * Initial APIs (1996 -> onward) : + * ----------------------------- + * Basically, the wireless extensions are for now a set of standard ioctl + * call + /proc/net/wireless + * + * The entry /proc/net/wireless give statistics and information on the + * driver. + * This is better than having each driver having its entry because + * its centralised and we may remove the driver module safely. + * + * Ioctl are used to configure the driver and issue commands. This is + * better than command line options of insmod because we may want to + * change dynamically (while the driver is running) some parameters. + * + * The ioctl mechanimsm are copied from standard devices ioctl. + * We have the list of command plus a structure descibing the + * data exchanged... + * Note that to add these ioctl, I was obliged to modify : + * # net/core/dev.c (two place + add include) + * # net/ipv4/af_inet.c (one place + add include) + * + * /proc/net/wireless is a copy of /proc/net/dev. + * We have a structure for data passed from the driver to /proc/net/wireless + * Too add this, I've modified : + * # net/core/dev.c (two other places) + * # include/linux/netdevice.h (one place) + * # include/linux/proc_fs.h (one place) + * + * New driver API (2002 -> onward) : + * ------------------------------- + * This file is only concerned with the user space API and common definitions. + * The new driver API is defined and documented in : + * # include/net/iw_handler.h + * + * Note as well that /proc/net/wireless implementation has now moved in : + * # net/core/wireless.c + * + * Wireless Events (2002 -> onward) : + * -------------------------------- + * Events are defined at the end of this file, and implemented in : + * # net/core/wireless.c + * + * Other comments : + * -------------- + * Do not add here things that are redundant with other mechanisms + * (drivers init, ifconfig, /proc/net/dev, ...) and with are not + * wireless specific. + * + * These wireless extensions are not magic : each driver has to provide + * support for them... + * + * IMPORTANT NOTE : As everything in the kernel, this is very much a + * work in progress. Contact me if you have ideas of improvements... + */ + +/***************************** INCLUDES *****************************/ + + /* jkm - replaced linux headers with C library headers, added typedefs */ +#if 0 +/* To minimise problems in user space, I might remove those headers + * at some point. Jean II */ +#include <linux/types.h> /* for "caddr_t" et al */ +#include <linux/socket.h> /* for "struct sockaddr" et al */ +#include <linux/if.h> /* for IFNAMSIZ and co... */ +#else +#include <sys/types.h> +#include <net/if.h> +typedef __uint32_t __u32; +typedef __int32_t __s32; +typedef __uint16_t __u16; +typedef __int16_t __s16; +typedef __uint8_t __u8; +#ifndef __user +#define __user +#endif /* __user */ +#endif + +/***************************** VERSION *****************************/ +/* + * This constant is used to know the availability of the wireless + * extensions and to know which version of wireless extensions it is + * (there is some stuff that will be added in the future...) + * I just plan to increment with each new version. + */ +#define WIRELESS_EXT 19 + +/* + * Changes : + * + * V2 to V3 + * -------- + * Alan Cox start some incompatibles changes. I've integrated a bit more. + * - Encryption renamed to Encode to avoid US regulation problems + * - Frequency changed from float to struct to avoid problems on old 386 + * + * V3 to V4 + * -------- + * - Add sensitivity + * + * V4 to V5 + * -------- + * - Missing encoding definitions in range + * - Access points stuff + * + * V5 to V6 + * -------- + * - 802.11 support (ESSID ioctls) + * + * V6 to V7 + * -------- + * - define IW_ESSID_MAX_SIZE and IW_MAX_AP + * + * V7 to V8 + * -------- + * - Changed my e-mail address + * - More 802.11 support (nickname, rate, rts, frag) + * - List index in frequencies + * + * V8 to V9 + * -------- + * - Support for 'mode of operation' (ad-hoc, managed...) + * - Support for unicast and multicast power saving + * - Change encoding to support larger tokens (>64 bits) + * - Updated iw_params (disable, flags) and use it for NWID + * - Extracted iw_point from iwreq for clarity + * + * V9 to V10 + * --------- + * - Add PM capability to range structure + * - Add PM modifier : MAX/MIN/RELATIVE + * - Add encoding option : IW_ENCODE_NOKEY + * - Add TxPower ioctls (work like TxRate) + * + * V10 to V11 + * ---------- + * - Add WE version in range (help backward/forward compatibility) + * - Add retry ioctls (work like PM) + * + * V11 to V12 + * ---------- + * - Add SIOCSIWSTATS to get /proc/net/wireless programatically + * - Add DEV PRIVATE IOCTL to avoid collisions in SIOCDEVPRIVATE space + * - Add new statistics (frag, retry, beacon) + * - Add average quality (for user space calibration) + * + * V12 to V13 + * ---------- + * - Document creation of new driver API. + * - Extract union iwreq_data from struct iwreq (for new driver API). + * - Rename SIOCSIWNAME as SIOCSIWCOMMIT + * + * V13 to V14 + * ---------- + * - Wireless Events support : define struct iw_event + * - Define additional specific event numbers + * - Add "addr" and "param" fields in union iwreq_data + * - AP scanning stuff (SIOCSIWSCAN and friends) + * + * V14 to V15 + * ---------- + * - Add IW_PRIV_TYPE_ADDR for struct sockaddr private arg + * - Make struct iw_freq signed (both m & e), add explicit padding + * - Add IWEVCUSTOM for driver specific event/scanning token + * - Add IW_MAX_GET_SPY for driver returning a lot of addresses + * - Add IW_TXPOW_RANGE for range of Tx Powers + * - Add IWEVREGISTERED & IWEVEXPIRED events for Access Points + * - Add IW_MODE_MONITOR for passive monitor + * + * V15 to V16 + * ---------- + * - Increase the number of bitrates in iw_range to 32 (for 802.11g) + * - Increase the number of frequencies in iw_range to 32 (for 802.11b+a) + * - Reshuffle struct iw_range for increases, add filler + * - Increase IW_MAX_AP to 64 for driver returning a lot of addresses + * - Remove IW_MAX_GET_SPY because conflict with enhanced spy support + * - Add SIOCSIWTHRSPY/SIOCGIWTHRSPY and "struct iw_thrspy" + * - Add IW_ENCODE_TEMP and iw_range->encoding_login_index + * + * V16 to V17 + * ---------- + * - Add flags to frequency -> auto/fixed + * - Document (struct iw_quality *)->updated, add new flags (INVALID) + * - Wireless Event capability in struct iw_range + * - Add support for relative TxPower (yick !) + * + * V17 to V18 (From Jouni Malinen <j@w1.fi>) + * ---------- + * - Add support for WPA/WPA2 + * - Add extended encoding configuration (SIOCSIWENCODEEXT and + * SIOCGIWENCODEEXT) + * - Add SIOCSIWGENIE/SIOCGIWGENIE + * - Add SIOCSIWMLME + * - Add SIOCSIWPMKSA + * - Add struct iw_range bit field for supported encoding capabilities + * - Add optional scan request parameters for SIOCSIWSCAN + * - Add SIOCSIWAUTH/SIOCGIWAUTH for setting authentication and WPA + * related parameters (extensible up to 4096 parameter values) + * - Add wireless events: IWEVGENIE, IWEVMICHAELMICFAILURE, + * IWEVASSOCREQIE, IWEVASSOCRESPIE, IWEVPMKIDCAND + * + * V18 to V19 + * ---------- + * - Remove (struct iw_point *)->pointer from events and streams + * - Remove header includes to help user space + * - Increase IW_ENCODING_TOKEN_MAX from 32 to 64 + * - Add IW_QUAL_ALL_UPDATED and IW_QUAL_ALL_INVALID macros + * - Add explicit flag to tell stats are in dBm : IW_QUAL_DBM + * - Add IW_IOCTL_IDX() and IW_EVENT_IDX() macros + */ + +/**************************** CONSTANTS ****************************/ + +/* -------------------------- IOCTL LIST -------------------------- */ + +/* Wireless Identification */ +#define SIOCSIWCOMMIT 0x8B00 /* Commit pending changes to driver */ +#define SIOCGIWNAME 0x8B01 /* get name == wireless protocol */ +/* SIOCGIWNAME is used to verify the presence of Wireless Extensions. + * Common values : "IEEE 802.11-DS", "IEEE 802.11-FH", "IEEE 802.11b"... + * Don't put the name of your driver there, it's useless. */ + +/* Basic operations */ +#define SIOCSIWNWID 0x8B02 /* set network id (pre-802.11) */ +#define SIOCGIWNWID 0x8B03 /* get network id (the cell) */ +#define SIOCSIWFREQ 0x8B04 /* set channel/frequency (Hz) */ +#define SIOCGIWFREQ 0x8B05 /* get channel/frequency (Hz) */ +#define SIOCSIWMODE 0x8B06 /* set operation mode */ +#define SIOCGIWMODE 0x8B07 /* get operation mode */ +#define SIOCSIWSENS 0x8B08 /* set sensitivity (dBm) */ +#define SIOCGIWSENS 0x8B09 /* get sensitivity (dBm) */ + +/* Informative stuff */ +#define SIOCSIWRANGE 0x8B0A /* Unused */ +#define SIOCGIWRANGE 0x8B0B /* Get range of parameters */ +#define SIOCSIWPRIV 0x8B0C /* Unused */ +#define SIOCGIWPRIV 0x8B0D /* get private ioctl interface info */ +#define SIOCSIWSTATS 0x8B0E /* Unused */ +#define SIOCGIWSTATS 0x8B0F /* Get /proc/net/wireless stats */ +/* SIOCGIWSTATS is strictly used between user space and the kernel, and + * is never passed to the driver (i.e. the driver will never see it). */ + +/* Spy support (statistics per MAC address - used for Mobile IP support) */ +#define SIOCSIWSPY 0x8B10 /* set spy addresses */ +#define SIOCGIWSPY 0x8B11 /* get spy info (quality of link) */ +#define SIOCSIWTHRSPY 0x8B12 /* set spy threshold (spy event) */ +#define SIOCGIWTHRSPY 0x8B13 /* get spy threshold */ + +/* Access Point manipulation */ +#define SIOCSIWAP 0x8B14 /* set access point MAC addresses */ +#define SIOCGIWAP 0x8B15 /* get access point MAC addresses */ +#define SIOCGIWAPLIST 0x8B17 /* Deprecated in favor of scanning */ +#define SIOCSIWSCAN 0x8B18 /* trigger scanning (list cells) */ +#define SIOCGIWSCAN 0x8B19 /* get scanning results */ + +/* 802.11 specific support */ +#define SIOCSIWESSID 0x8B1A /* set ESSID (network name) */ +#define SIOCGIWESSID 0x8B1B /* get ESSID */ +#define SIOCSIWNICKN 0x8B1C /* set node name/nickname */ +#define SIOCGIWNICKN 0x8B1D /* get node name/nickname */ +/* As the ESSID and NICKN are strings up to 32 bytes long, it doesn't fit + * within the 'iwreq' structure, so we need to use the 'data' member to + * point to a string in user space, like it is done for RANGE... */ + +/* Other parameters useful in 802.11 and some other devices */ +#define SIOCSIWRATE 0x8B20 /* set default bit rate (bps) */ +#define SIOCGIWRATE 0x8B21 /* get default bit rate (bps) */ +#define SIOCSIWRTS 0x8B22 /* set RTS/CTS threshold (bytes) */ +#define SIOCGIWRTS 0x8B23 /* get RTS/CTS threshold (bytes) */ +#define SIOCSIWFRAG 0x8B24 /* set fragmentation thr (bytes) */ +#define SIOCGIWFRAG 0x8B25 /* get fragmentation thr (bytes) */ +#define SIOCSIWTXPOW 0x8B26 /* set transmit power (dBm) */ +#define SIOCGIWTXPOW 0x8B27 /* get transmit power (dBm) */ +#define SIOCSIWRETRY 0x8B28 /* set retry limits and lifetime */ +#define SIOCGIWRETRY 0x8B29 /* get retry limits and lifetime */ + +/* Encoding stuff (scrambling, hardware security, WEP...) */ +#define SIOCSIWENCODE 0x8B2A /* set encoding token & mode */ +#define SIOCGIWENCODE 0x8B2B /* get encoding token & mode */ +/* Power saving stuff (power management, unicast and multicast) */ +#define SIOCSIWPOWER 0x8B2C /* set Power Management settings */ +#define SIOCGIWPOWER 0x8B2D /* get Power Management settings */ + +/* WPA : Generic IEEE 802.11 informatiom element (e.g., for WPA/RSN/WMM). + * This ioctl uses struct iw_point and data buffer that includes IE id and len + * fields. More than one IE may be included in the request. Setting the generic + * IE to empty buffer (len=0) removes the generic IE from the driver. Drivers + * are allowed to generate their own WPA/RSN IEs, but in these cases, drivers + * are required to report the used IE as a wireless event, e.g., when + * associating with an AP. */ +#define SIOCSIWGENIE 0x8B30 /* set generic IE */ +#define SIOCGIWGENIE 0x8B31 /* get generic IE */ + +/* WPA : IEEE 802.11 MLME requests */ +#define SIOCSIWMLME 0x8B16 /* request MLME operation; uses + * struct iw_mlme */ +/* WPA : Authentication mode parameters */ +#define SIOCSIWAUTH 0x8B32 /* set authentication mode params */ +#define SIOCGIWAUTH 0x8B33 /* get authentication mode params */ + +/* WPA : Extended version of encoding configuration */ +#define SIOCSIWENCODEEXT 0x8B34 /* set encoding token & mode */ +#define SIOCGIWENCODEEXT 0x8B35 /* get encoding token & mode */ + +/* WPA2 : PMKSA cache management */ +#define SIOCSIWPMKSA 0x8B36 /* PMKSA cache operation */ + +/* -------------------- DEV PRIVATE IOCTL LIST -------------------- */ + +/* These 32 ioctl are wireless device private, for 16 commands. + * Each driver is free to use them for whatever purpose it chooses, + * however the driver *must* export the description of those ioctls + * with SIOCGIWPRIV and *must* use arguments as defined below. + * If you don't follow those rules, DaveM is going to hate you (reason : + * it make mixed 32/64bit operation impossible). + */ +#define SIOCIWFIRSTPRIV 0x8BE0 +#define SIOCIWLASTPRIV 0x8BFF +/* Previously, we were using SIOCDEVPRIVATE, but we now have our + * separate range because of collisions with other tools such as + * 'mii-tool'. + * We now have 32 commands, so a bit more space ;-). + * Also, all 'odd' commands are only usable by root and don't return the + * content of ifr/iwr to user (but you are not obliged to use the set/get + * convention, just use every other two command). More details in iwpriv.c. + * And I repeat : you are not forced to use them with iwpriv, but you + * must be compliant with it. + */ + +/* ------------------------- IOCTL STUFF ------------------------- */ + +/* The first and the last (range) */ +#define SIOCIWFIRST 0x8B00 +#define SIOCIWLAST SIOCIWLASTPRIV /* 0x8BFF */ +#define IW_IOCTL_IDX(cmd) ((cmd) - SIOCIWFIRST) + +/* Even : get (world access), odd : set (root access) */ +#define IW_IS_SET(cmd) (!((cmd) & 0x1)) +#define IW_IS_GET(cmd) ((cmd) & 0x1) + +/* ----------------------- WIRELESS EVENTS ----------------------- */ +/* Those are *NOT* ioctls, do not issue request on them !!! */ +/* Most events use the same identifier as ioctl requests */ + +#define IWEVTXDROP 0x8C00 /* Packet dropped to excessive retry */ +#define IWEVQUAL 0x8C01 /* Quality part of statistics (scan) */ +#define IWEVCUSTOM 0x8C02 /* Driver specific ascii string */ +#define IWEVREGISTERED 0x8C03 /* Discovered a new node (AP mode) */ +#define IWEVEXPIRED 0x8C04 /* Expired a node (AP mode) */ +#define IWEVGENIE 0x8C05 /* Generic IE (WPA, RSN, WMM, ..) + * (scan results); This includes id and + * length fields. One IWEVGENIE may + * contain more than one IE. Scan + * results may contain one or more + * IWEVGENIE events. */ +#define IWEVMICHAELMICFAILURE 0x8C06 /* Michael MIC failure + * (struct iw_michaelmicfailure) + */ +#define IWEVASSOCREQIE 0x8C07 /* IEs used in (Re)Association Request. + * The data includes id and length + * fields and may contain more than one + * IE. This event is required in + * Managed mode if the driver + * generates its own WPA/RSN IE. This + * should be sent just before + * IWEVREGISTERED event for the + * association. */ +#define IWEVASSOCRESPIE 0x8C08 /* IEs used in (Re)Association + * Response. The data includes id and + * length fields and may contain more + * than one IE. This may be sent + * between IWEVASSOCREQIE and + * IWEVREGISTERED events for the + * association. */ +#define IWEVPMKIDCAND 0x8C09 /* PMKID candidate for RSN + * pre-authentication + * (struct iw_pmkid_cand) */ + +#define IWEVFIRST 0x8C00 +#define IW_EVENT_IDX(cmd) ((cmd) - IWEVFIRST) + +/* ------------------------- PRIVATE INFO ------------------------- */ +/* + * The following is used with SIOCGIWPRIV. It allow a driver to define + * the interface (name, type of data) for its private ioctl. + * Privates ioctl are SIOCIWFIRSTPRIV -> SIOCIWLASTPRIV + */ + +#define IW_PRIV_TYPE_MASK 0x7000 /* Type of arguments */ +#define IW_PRIV_TYPE_NONE 0x0000 +#define IW_PRIV_TYPE_BYTE 0x1000 /* Char as number */ +#define IW_PRIV_TYPE_CHAR 0x2000 /* Char as character */ +#define IW_PRIV_TYPE_INT 0x4000 /* 32 bits int */ +#define IW_PRIV_TYPE_FLOAT 0x5000 /* struct iw_freq */ +#define IW_PRIV_TYPE_ADDR 0x6000 /* struct sockaddr */ + +#define IW_PRIV_SIZE_FIXED 0x0800 /* Variable or fixed number of args */ + +#define IW_PRIV_SIZE_MASK 0x07FF /* Max number of those args */ + +/* + * Note : if the number of args is fixed and the size < 16 octets, + * instead of passing a pointer we will put args in the iwreq struct... + */ + +/* ----------------------- OTHER CONSTANTS ----------------------- */ + +/* Maximum frequencies in the range struct */ +#define IW_MAX_FREQUENCIES 32 +/* Note : if you have something like 80 frequencies, + * don't increase this constant and don't fill the frequency list. + * The user will be able to set by channel anyway... */ + +/* Maximum bit rates in the range struct */ +#define IW_MAX_BITRATES 32 + +/* Maximum tx powers in the range struct */ +#define IW_MAX_TXPOWER 8 +/* Note : if you more than 8 TXPowers, just set the max and min or + * a few of them in the struct iw_range. */ + +/* Maximum of address that you may set with SPY */ +#define IW_MAX_SPY 8 + +/* Maximum of address that you may get in the + list of access points in range */ +#define IW_MAX_AP 64 + +/* Maximum size of the ESSID and NICKN strings */ +#define IW_ESSID_MAX_SIZE 32 + +/* Modes of operation */ +#define IW_MODE_AUTO 0 /* Let the driver decides */ +#define IW_MODE_ADHOC 1 /* Single cell network */ +#define IW_MODE_INFRA 2 /* Multi cell network, roaming, ... */ +#define IW_MODE_MASTER 3 /* Synchronisation master or Access Point */ +#define IW_MODE_REPEAT 4 /* Wireless Repeater (forwarder) */ +#define IW_MODE_SECOND 5 /* Secondary master/repeater (backup) */ +#define IW_MODE_MONITOR 6 /* Passive monitor (listen only) */ + +/* Statistics flags (bitmask in updated) */ +#define IW_QUAL_QUAL_UPDATED 0x01 /* Value was updated since last read */ +#define IW_QUAL_LEVEL_UPDATED 0x02 +#define IW_QUAL_NOISE_UPDATED 0x04 +#define IW_QUAL_ALL_UPDATED 0x07 +#define IW_QUAL_DBM 0x08 /* Level + Noise are dBm */ +#define IW_QUAL_QUAL_INVALID 0x10 /* Driver doesn't provide value */ +#define IW_QUAL_LEVEL_INVALID 0x20 +#define IW_QUAL_NOISE_INVALID 0x40 +#define IW_QUAL_ALL_INVALID 0x70 + +/* Frequency flags */ +#define IW_FREQ_AUTO 0x00 /* Let the driver decides */ +#define IW_FREQ_FIXED 0x01 /* Force a specific value */ + +/* Maximum number of size of encoding token available + * they are listed in the range structure */ +#define IW_MAX_ENCODING_SIZES 8 + +/* Maximum size of the encoding token in bytes */ +#define IW_ENCODING_TOKEN_MAX 64 /* 512 bits (for now) */ + +/* Flags for encoding (along with the token) */ +#define IW_ENCODE_INDEX 0x00FF /* Token index (if needed) */ +#define IW_ENCODE_FLAGS 0xFF00 /* Flags defined below */ +#define IW_ENCODE_MODE 0xF000 /* Modes defined below */ +#define IW_ENCODE_DISABLED 0x8000 /* Encoding disabled */ +#define IW_ENCODE_ENABLED 0x0000 /* Encoding enabled */ +#define IW_ENCODE_RESTRICTED 0x4000 /* Refuse non-encoded packets */ +#define IW_ENCODE_OPEN 0x2000 /* Accept non-encoded packets */ +#define IW_ENCODE_NOKEY 0x0800 /* Key is write only, so not present */ +#define IW_ENCODE_TEMP 0x0400 /* Temporary key */ + +/* Power management flags available (along with the value, if any) */ +#define IW_POWER_ON 0x0000 /* No details... */ +#define IW_POWER_TYPE 0xF000 /* Type of parameter */ +#define IW_POWER_PERIOD 0x1000 /* Value is a period/duration of */ +#define IW_POWER_TIMEOUT 0x2000 /* Value is a timeout (to go asleep) */ +#define IW_POWER_MODE 0x0F00 /* Power Management mode */ +#define IW_POWER_UNICAST_R 0x0100 /* Receive only unicast messages */ +#define IW_POWER_MULTICAST_R 0x0200 /* Receive only multicast messages */ +#define IW_POWER_ALL_R 0x0300 /* Receive all messages though PM */ +#define IW_POWER_FORCE_S 0x0400 /* Force PM procedure for sending unicast */ +#define IW_POWER_REPEATER 0x0800 /* Repeat broadcast messages in PM period */ +#define IW_POWER_MODIFIER 0x000F /* Modify a parameter */ +#define IW_POWER_MIN 0x0001 /* Value is a minimum */ +#define IW_POWER_MAX 0x0002 /* Value is a maximum */ +#define IW_POWER_RELATIVE 0x0004 /* Value is not in seconds/ms/us */ + +/* Transmit Power flags available */ +#define IW_TXPOW_TYPE 0x00FF /* Type of value */ +#define IW_TXPOW_DBM 0x0000 /* Value is in dBm */ +#define IW_TXPOW_MWATT 0x0001 /* Value is in mW */ +#define IW_TXPOW_RELATIVE 0x0002 /* Value is in arbitrary units */ +#define IW_TXPOW_RANGE 0x1000 /* Range of value between min/max */ + +/* Retry limits and lifetime flags available */ +#define IW_RETRY_ON 0x0000 /* No details... */ +#define IW_RETRY_TYPE 0xF000 /* Type of parameter */ +#define IW_RETRY_LIMIT 0x1000 /* Maximum number of retries*/ +#define IW_RETRY_LIFETIME 0x2000 /* Maximum duration of retries in us */ +#define IW_RETRY_MODIFIER 0x000F /* Modify a parameter */ +#define IW_RETRY_MIN 0x0001 /* Value is a minimum */ +#define IW_RETRY_MAX 0x0002 /* Value is a maximum */ +#define IW_RETRY_RELATIVE 0x0004 /* Value is not in seconds/ms/us */ + +/* Scanning request flags */ +#define IW_SCAN_DEFAULT 0x0000 /* Default scan of the driver */ +#define IW_SCAN_ALL_ESSID 0x0001 /* Scan all ESSIDs */ +#define IW_SCAN_THIS_ESSID 0x0002 /* Scan only this ESSID */ +#define IW_SCAN_ALL_FREQ 0x0004 /* Scan all Frequencies */ +#define IW_SCAN_THIS_FREQ 0x0008 /* Scan only this Frequency */ +#define IW_SCAN_ALL_MODE 0x0010 /* Scan all Modes */ +#define IW_SCAN_THIS_MODE 0x0020 /* Scan only this Mode */ +#define IW_SCAN_ALL_RATE 0x0040 /* Scan all Bit-Rates */ +#define IW_SCAN_THIS_RATE 0x0080 /* Scan only this Bit-Rate */ +/* struct iw_scan_req scan_type */ +#define IW_SCAN_TYPE_ACTIVE 0 +#define IW_SCAN_TYPE_PASSIVE 1 +/* Maximum size of returned data */ +#define IW_SCAN_MAX_DATA 4096 /* In bytes */ + +/* Max number of char in custom event - use multiple of them if needed */ +#define IW_CUSTOM_MAX 256 /* In bytes */ + +/* Generic information element */ +#define IW_GENERIC_IE_MAX 1024 + +/* MLME requests (SIOCSIWMLME / struct iw_mlme) */ +#define IW_MLME_DEAUTH 0 +#define IW_MLME_DISASSOC 1 + +/* SIOCSIWAUTH/SIOCGIWAUTH struct iw_param flags */ +#define IW_AUTH_INDEX 0x0FFF +#define IW_AUTH_FLAGS 0xF000 +/* SIOCSIWAUTH/SIOCGIWAUTH parameters (0 .. 4095) + * (IW_AUTH_INDEX mask in struct iw_param flags; this is the index of the + * parameter that is being set/get to; value will be read/written to + * struct iw_param value field) */ +#define IW_AUTH_WPA_VERSION 0 +#define IW_AUTH_CIPHER_PAIRWISE 1 +#define IW_AUTH_CIPHER_GROUP 2 +#define IW_AUTH_KEY_MGMT 3 +#define IW_AUTH_TKIP_COUNTERMEASURES 4 +#define IW_AUTH_DROP_UNENCRYPTED 5 +#define IW_AUTH_80211_AUTH_ALG 6 +#define IW_AUTH_WPA_ENABLED 7 +#define IW_AUTH_RX_UNENCRYPTED_EAPOL 8 +#define IW_AUTH_ROAMING_CONTROL 9 +#define IW_AUTH_PRIVACY_INVOKED 10 +#define IW_AUTH_CIPHER_GROUP_MGMT 11 +#define IW_AUTH_MFP 12 + +/* IW_AUTH_WPA_VERSION values (bit field) */ +#define IW_AUTH_WPA_VERSION_DISABLED 0x00000001 +#define IW_AUTH_WPA_VERSION_WPA 0x00000002 +#define IW_AUTH_WPA_VERSION_WPA2 0x00000004 + +/* IW_AUTH_PAIRWISE_CIPHER and IW_AUTH_GROUP_CIPHER values (bit field) */ +#define IW_AUTH_CIPHER_NONE 0x00000001 +#define IW_AUTH_CIPHER_WEP40 0x00000002 +#define IW_AUTH_CIPHER_TKIP 0x00000004 +#define IW_AUTH_CIPHER_CCMP 0x00000008 +#define IW_AUTH_CIPHER_WEP104 0x00000010 + +/* IW_AUTH_KEY_MGMT values (bit field) */ +#define IW_AUTH_KEY_MGMT_802_1X 1 +#define IW_AUTH_KEY_MGMT_PSK 2 + +/* IW_AUTH_80211_AUTH_ALG values (bit field) */ +#define IW_AUTH_ALG_OPEN_SYSTEM 0x00000001 +#define IW_AUTH_ALG_SHARED_KEY 0x00000002 +#define IW_AUTH_ALG_LEAP 0x00000004 + +/* IW_AUTH_ROAMING_CONTROL values */ +#define IW_AUTH_ROAMING_ENABLE 0 /* driver/firmware based roaming */ +#define IW_AUTH_ROAMING_DISABLE 1 /* user space program used for roaming + * control */ + +/* IW_AUTH_MFP (management frame protection) values */ +#define IW_AUTH_MFP_DISABLED 0 /* MFP disabled */ +#define IW_AUTH_MFP_OPTIONAL 1 /* MFP optional */ +#define IW_AUTH_MFP_REQUIRED 2 /* MFP required */ + +/* SIOCSIWENCODEEXT definitions */ +#define IW_ENCODE_SEQ_MAX_SIZE 8 +/* struct iw_encode_ext ->alg */ +#define IW_ENCODE_ALG_NONE 0 +#define IW_ENCODE_ALG_WEP 1 +#define IW_ENCODE_ALG_TKIP 2 +#define IW_ENCODE_ALG_CCMP 3 +#define IW_ENCODE_ALG_PMK 4 +#define IW_ENCODE_ALG_AES_CMAC 5 +/* struct iw_encode_ext ->ext_flags */ +#define IW_ENCODE_EXT_TX_SEQ_VALID 0x00000001 +#define IW_ENCODE_EXT_RX_SEQ_VALID 0x00000002 +#define IW_ENCODE_EXT_GROUP_KEY 0x00000004 +#define IW_ENCODE_EXT_SET_TX_KEY 0x00000008 + +/* IWEVMICHAELMICFAILURE : struct iw_michaelmicfailure ->flags */ +#define IW_MICFAILURE_KEY_ID 0x00000003 /* Key ID 0..3 */ +#define IW_MICFAILURE_GROUP 0x00000004 +#define IW_MICFAILURE_PAIRWISE 0x00000008 +#define IW_MICFAILURE_STAKEY 0x00000010 +#define IW_MICFAILURE_COUNT 0x00000060 /* 1 or 2 (0 = count not supported) + */ + +/* Bit field values for enc_capa in struct iw_range */ +#define IW_ENC_CAPA_WPA 0x00000001 +#define IW_ENC_CAPA_WPA2 0x00000002 +#define IW_ENC_CAPA_CIPHER_TKIP 0x00000004 +#define IW_ENC_CAPA_CIPHER_CCMP 0x00000008 +#define IW_ENC_CAPA_4WAY_HANDSHAKE 0x00000010 + +/* Event capability macros - in (struct iw_range *)->event_capa + * Because we have more than 32 possible events, we use an array of + * 32 bit bitmasks. Note : 32 bits = 0x20 = 2^5. */ +#define IW_EVENT_CAPA_BASE(cmd) ((cmd >= SIOCIWFIRSTPRIV) ? \ + (cmd - SIOCIWFIRSTPRIV + 0x60) : \ + (cmd - SIOCSIWCOMMIT)) +#define IW_EVENT_CAPA_INDEX(cmd) (IW_EVENT_CAPA_BASE(cmd) >> 5) +#define IW_EVENT_CAPA_MASK(cmd) (1 << (IW_EVENT_CAPA_BASE(cmd) & 0x1F)) +/* Event capability constants - event autogenerated by the kernel + * This list is valid for most 802.11 devices, customise as needed... */ +#define IW_EVENT_CAPA_K_0 (IW_EVENT_CAPA_MASK(0x8B04) | \ + IW_EVENT_CAPA_MASK(0x8B06) | \ + IW_EVENT_CAPA_MASK(0x8B1A)) +#define IW_EVENT_CAPA_K_1 (IW_EVENT_CAPA_MASK(0x8B2A)) +/* "Easy" macro to set events in iw_range (less efficient) */ +#define IW_EVENT_CAPA_SET(event_capa, cmd) (event_capa[IW_EVENT_CAPA_INDEX(cmd)] |= IW_EVENT_CAPA_MASK(cmd)) +#define IW_EVENT_CAPA_SET_KERNEL(event_capa) {event_capa[0] |= IW_EVENT_CAPA_K_0; event_capa[1] |= IW_EVENT_CAPA_K_1; } + + +/****************************** TYPES ******************************/ + +/* --------------------------- SUBTYPES --------------------------- */ +/* + * Generic format for most parameters that fit in an int + */ +struct iw_param +{ + __s32 value; /* The value of the parameter itself */ + __u8 fixed; /* Hardware should not use auto select */ + __u8 disabled; /* Disable the feature */ + __u16 flags; /* Various specifc flags (if any) */ +}; + +/* + * For all data larger than 16 octets, we need to use a + * pointer to memory allocated in user space. + */ +struct iw_point +{ + void __user *pointer; /* Pointer to the data (in user space) */ + __u16 length; /* number of fields or size in bytes */ + __u16 flags; /* Optional params */ +}; + +/* + * A frequency + * For numbers lower than 10^9, we encode the number in 'm' and + * set 'e' to 0 + * For number greater than 10^9, we divide it by the lowest power + * of 10 to get 'm' lower than 10^9, with 'm'= f / (10^'e')... + * The power of 10 is in 'e', the result of the division is in 'm'. + */ +struct iw_freq +{ + __s32 m; /* Mantissa */ + __s16 e; /* Exponent */ + __u8 i; /* List index (when in range struct) */ + __u8 flags; /* Flags (fixed/auto) */ +}; + +/* + * Quality of the link + */ +struct iw_quality +{ + __u8 qual; /* link quality (%retries, SNR, + %missed beacons or better...) */ + __u8 level; /* signal level (dBm) */ + __u8 noise; /* noise level (dBm) */ + __u8 updated; /* Flags to know if updated */ +}; + +/* + * Packet discarded in the wireless adapter due to + * "wireless" specific problems... + * Note : the list of counter and statistics in net_device_stats + * is already pretty exhaustive, and you should use that first. + * This is only additional stats... + */ +struct iw_discarded +{ + __u32 nwid; /* Rx : Wrong nwid/essid */ + __u32 code; /* Rx : Unable to code/decode (WEP) */ + __u32 fragment; /* Rx : Can't perform MAC reassembly */ + __u32 retries; /* Tx : Max MAC retries num reached */ + __u32 misc; /* Others cases */ +}; + +/* + * Packet/Time period missed in the wireless adapter due to + * "wireless" specific problems... + */ +struct iw_missed +{ + __u32 beacon; /* Missed beacons/superframe */ +}; + +/* + * Quality range (for spy threshold) + */ +struct iw_thrspy +{ + struct sockaddr addr; /* Source address (hw/mac) */ + struct iw_quality qual; /* Quality of the link */ + struct iw_quality low; /* Low threshold */ + struct iw_quality high; /* High threshold */ +}; + +/* + * Optional data for scan request + * + * Note: these optional parameters are controlling parameters for the + * scanning behavior, these do not apply to getting scan results + * (SIOCGIWSCAN). Drivers are expected to keep a local BSS table and + * provide a merged results with all BSSes even if the previous scan + * request limited scanning to a subset, e.g., by specifying an SSID. + * Especially, scan results are required to include an entry for the + * current BSS if the driver is in Managed mode and associated with an AP. + */ +struct iw_scan_req +{ + __u8 scan_type; /* IW_SCAN_TYPE_{ACTIVE,PASSIVE} */ + __u8 essid_len; + __u8 num_channels; /* num entries in channel_list; + * 0 = scan all allowed channels */ + __u8 flags; /* reserved as padding; use zero, this may + * be used in the future for adding flags + * to request different scan behavior */ + struct sockaddr bssid; /* ff:ff:ff:ff:ff:ff for broadcast BSSID or + * individual address of a specific BSS */ + + /* + * Use this ESSID if IW_SCAN_THIS_ESSID flag is used instead of using + * the current ESSID. This allows scan requests for specific ESSID + * without having to change the current ESSID and potentially breaking + * the current association. + */ + __u8 essid[IW_ESSID_MAX_SIZE]; + + /* + * Optional parameters for changing the default scanning behavior. + * These are based on the MLME-SCAN.request from IEEE Std 802.11. + * TU is 1.024 ms. If these are set to 0, driver is expected to use + * reasonable default values. min_channel_time defines the time that + * will be used to wait for the first reply on each channel. If no + * replies are received, next channel will be scanned after this. If + * replies are received, total time waited on the channel is defined by + * max_channel_time. + */ + __u32 min_channel_time; /* in TU */ + __u32 max_channel_time; /* in TU */ + + struct iw_freq channel_list[IW_MAX_FREQUENCIES]; +}; + +/* ------------------------- WPA SUPPORT ------------------------- */ + +/* + * Extended data structure for get/set encoding (this is used with + * SIOCSIWENCODEEXT/SIOCGIWENCODEEXT. struct iw_point and IW_ENCODE_* + * flags are used in the same way as with SIOCSIWENCODE/SIOCGIWENCODE and + * only the data contents changes (key data -> this structure, including + * key data). + * + * If the new key is the first group key, it will be set as the default + * TX key. Otherwise, default TX key index is only changed if + * IW_ENCODE_EXT_SET_TX_KEY flag is set. + * + * Key will be changed with SIOCSIWENCODEEXT in all cases except for + * special "change TX key index" operation which is indicated by setting + * key_len = 0 and ext_flags |= IW_ENCODE_EXT_SET_TX_KEY. + * + * tx_seq/rx_seq are only used when respective + * IW_ENCODE_EXT_{TX,RX}_SEQ_VALID flag is set in ext_flags. Normal + * TKIP/CCMP operation is to set RX seq with SIOCSIWENCODEEXT and start + * TX seq from zero whenever key is changed. SIOCGIWENCODEEXT is normally + * used only by an Authenticator (AP or an IBSS station) to get the + * current TX sequence number. Using TX_SEQ_VALID for SIOCSIWENCODEEXT and + * RX_SEQ_VALID for SIOCGIWENCODEEXT are optional, but can be useful for + * debugging/testing. + */ +struct iw_encode_ext +{ + __u32 ext_flags; /* IW_ENCODE_EXT_* */ + __u8 tx_seq[IW_ENCODE_SEQ_MAX_SIZE]; /* LSB first */ + __u8 rx_seq[IW_ENCODE_SEQ_MAX_SIZE]; /* LSB first */ + struct sockaddr addr; /* ff:ff:ff:ff:ff:ff for broadcast/multicast + * (group) keys or unicast address for + * individual keys */ + __u16 alg; /* IW_ENCODE_ALG_* */ + __u16 key_len; + __u8 key[0]; +}; + +/* SIOCSIWMLME data */ +struct iw_mlme +{ + __u16 cmd; /* IW_MLME_* */ + __u16 reason_code; + struct sockaddr addr; +}; + +/* SIOCSIWPMKSA data */ +#define IW_PMKSA_ADD 1 +#define IW_PMKSA_REMOVE 2 +#define IW_PMKSA_FLUSH 3 + +#define IW_PMKID_LEN 16 + +struct iw_pmksa +{ + __u32 cmd; /* IW_PMKSA_* */ + struct sockaddr bssid; + __u8 pmkid[IW_PMKID_LEN]; +}; + +/* IWEVMICHAELMICFAILURE data */ +struct iw_michaelmicfailure +{ + __u32 flags; + struct sockaddr src_addr; + __u8 tsc[IW_ENCODE_SEQ_MAX_SIZE]; /* LSB first */ +}; + +/* IWEVPMKIDCAND data */ +#define IW_PMKID_CAND_PREAUTH 0x00000001 /* RNS pre-authentication enabled */ +struct iw_pmkid_cand +{ + __u32 flags; /* IW_PMKID_CAND_* */ + __u32 index; /* the smaller the index, the higher the + * priority */ + struct sockaddr bssid; +}; + +/* ------------------------ WIRELESS STATS ------------------------ */ +/* + * Wireless statistics (used for /proc/net/wireless) + */ +struct iw_statistics +{ + __u16 status; /* Status + * - device dependent for now */ + + struct iw_quality qual; /* Quality of the link + * (instant/mean/max) */ + struct iw_discarded discard; /* Packet discarded counts */ + struct iw_missed miss; /* Packet missed counts */ +}; + +/* ------------------------ IOCTL REQUEST ------------------------ */ +/* + * This structure defines the payload of an ioctl, and is used + * below. + * + * Note that this structure should fit on the memory footprint + * of iwreq (which is the same as ifreq), which mean a max size of + * 16 octets = 128 bits. Warning, pointers might be 64 bits wide... + * You should check this when increasing the structures defined + * above in this file... + */ +union iwreq_data +{ + /* Config - generic */ + char name[IFNAMSIZ]; + /* Name : used to verify the presence of wireless extensions. + * Name of the protocol/provider... */ + + struct iw_point essid; /* Extended network name */ + struct iw_param nwid; /* network id (or domain - the cell) */ + struct iw_freq freq; /* frequency or channel : + * 0-1000 = channel + * > 1000 = frequency in Hz */ + + struct iw_param sens; /* signal level threshold */ + struct iw_param bitrate; /* default bit rate */ + struct iw_param txpower; /* default transmit power */ + struct iw_param rts; /* RTS threshold threshold */ + struct iw_param frag; /* Fragmentation threshold */ + __u32 mode; /* Operation mode */ + struct iw_param retry; /* Retry limits & lifetime */ + + struct iw_point encoding; /* Encoding stuff : tokens */ + struct iw_param power; /* PM duration/timeout */ + struct iw_quality qual; /* Quality part of statistics */ + + struct sockaddr ap_addr; /* Access point address */ + struct sockaddr addr; /* Destination address (hw/mac) */ + + struct iw_param param; /* Other small parameters */ + struct iw_point data; /* Other large parameters */ +}; + +/* + * The structure to exchange data for ioctl. + * This structure is the same as 'struct ifreq', but (re)defined for + * convenience... + * Do I need to remind you about structure size (32 octets) ? + */ +struct iwreq +{ + union + { + char ifrn_name[IFNAMSIZ]; /* if name, e.g. "eth0" */ + } ifr_ifrn; + + /* Data part (defined just above) */ + union iwreq_data u; +}; + +/* -------------------------- IOCTL DATA -------------------------- */ +/* + * For those ioctl which want to exchange mode data that what could + * fit in the above structure... + */ + +/* + * Range of parameters + */ + +struct iw_range +{ + /* Informative stuff (to choose between different interface) */ + __u32 throughput; /* To give an idea... */ + /* In theory this value should be the maximum benchmarked + * TCP/IP throughput, because with most of these devices the + * bit rate is meaningless (overhead an co) to estimate how + * fast the connection will go and pick the fastest one. + * I suggest people to play with Netperf or any benchmark... + */ + + /* NWID (or domain id) */ + __u32 min_nwid; /* Minimal NWID we are able to set */ + __u32 max_nwid; /* Maximal NWID we are able to set */ + + /* Old Frequency (backward compat - moved lower ) */ + __u16 old_num_channels; + __u8 old_num_frequency; + + /* Wireless event capability bitmasks */ + __u32 event_capa[6]; + + /* signal level threshold range */ + __s32 sensitivity; + + /* Quality of link & SNR stuff */ + /* Quality range (link, level, noise) + * If the quality is absolute, it will be in the range [0 ; max_qual], + * if the quality is dBm, it will be in the range [max_qual ; 0]. + * Don't forget that we use 8 bit arithmetics... */ + struct iw_quality max_qual; /* Quality of the link */ + /* This should contain the average/typical values of the quality + * indicator. This should be the threshold between a "good" and + * a "bad" link (example : monitor going from green to orange). + * Currently, user space apps like quality monitors don't have any + * way to calibrate the measurement. With this, they can split + * the range between 0 and max_qual in different quality level + * (using a geometric subdivision centered on the average). + * I expect that people doing the user space apps will feedback + * us on which value we need to put in each driver... */ + struct iw_quality avg_qual; /* Quality of the link */ + + /* Rates */ + __u8 num_bitrates; /* Number of entries in the list */ + __s32 bitrate[IW_MAX_BITRATES]; /* list, in bps */ + + /* RTS threshold */ + __s32 min_rts; /* Minimal RTS threshold */ + __s32 max_rts; /* Maximal RTS threshold */ + + /* Frag threshold */ + __s32 min_frag; /* Minimal frag threshold */ + __s32 max_frag; /* Maximal frag threshold */ + + /* Power Management duration & timeout */ + __s32 min_pmp; /* Minimal PM period */ + __s32 max_pmp; /* Maximal PM period */ + __s32 min_pmt; /* Minimal PM timeout */ + __s32 max_pmt; /* Maximal PM timeout */ + __u16 pmp_flags; /* How to decode max/min PM period */ + __u16 pmt_flags; /* How to decode max/min PM timeout */ + __u16 pm_capa; /* What PM options are supported */ + + /* Encoder stuff */ + __u16 encoding_size[IW_MAX_ENCODING_SIZES]; /* Different token sizes */ + __u8 num_encoding_sizes; /* Number of entry in the list */ + __u8 max_encoding_tokens; /* Max number of tokens */ + /* For drivers that need a "login/passwd" form */ + __u8 encoding_login_index; /* token index for login token */ + + /* Transmit power */ + __u16 txpower_capa; /* What options are supported */ + __u8 num_txpower; /* Number of entries in the list */ + __s32 txpower[IW_MAX_TXPOWER]; /* list, in bps */ + + /* Wireless Extension version info */ + __u8 we_version_compiled; /* Must be WIRELESS_EXT */ + __u8 we_version_source; /* Last update of source */ + + /* Retry limits and lifetime */ + __u16 retry_capa; /* What retry options are supported */ + __u16 retry_flags; /* How to decode max/min retry limit */ + __u16 r_time_flags; /* How to decode max/min retry life */ + __s32 min_retry; /* Minimal number of retries */ + __s32 max_retry; /* Maximal number of retries */ + __s32 min_r_time; /* Minimal retry lifetime */ + __s32 max_r_time; /* Maximal retry lifetime */ + + /* Frequency */ + __u16 num_channels; /* Number of channels [0; num - 1] */ + __u8 num_frequency; /* Number of entry in the list */ + struct iw_freq freq[IW_MAX_FREQUENCIES]; /* list */ + /* Note : this frequency list doesn't need to fit channel numbers, + * because each entry contain its channel index */ + + __u32 enc_capa; /* IW_ENC_CAPA_* bit field */ +}; + +/* + * Private ioctl interface information + */ + +struct iw_priv_args +{ + __u32 cmd; /* Number of the ioctl to issue */ + __u16 set_args; /* Type and number of args */ + __u16 get_args; /* Type and number of args */ + char name[IFNAMSIZ]; /* Name of the extension */ +}; + +/* ----------------------- WIRELESS EVENTS ----------------------- */ +/* + * Wireless events are carried through the rtnetlink socket to user + * space. They are encapsulated in the IFLA_WIRELESS field of + * a RTM_NEWLINK message. + */ + +/* + * A Wireless Event. Contains basically the same data as the ioctl... + */ +struct iw_event +{ + __u16 len; /* Real lenght of this stuff */ + __u16 cmd; /* Wireless IOCTL */ + union iwreq_data u; /* IOCTL fixed payload */ +}; + +/* Size of the Event prefix (including padding and alignement junk) */ +#define IW_EV_LCP_LEN (sizeof(struct iw_event) - sizeof(union iwreq_data)) +/* Size of the various events */ +#define IW_EV_CHAR_LEN (IW_EV_LCP_LEN + IFNAMSIZ) +#define IW_EV_UINT_LEN (IW_EV_LCP_LEN + sizeof(__u32)) +#define IW_EV_FREQ_LEN (IW_EV_LCP_LEN + sizeof(struct iw_freq)) +#define IW_EV_PARAM_LEN (IW_EV_LCP_LEN + sizeof(struct iw_param)) +#define IW_EV_ADDR_LEN (IW_EV_LCP_LEN + sizeof(struct sockaddr)) +#define IW_EV_QUAL_LEN (IW_EV_LCP_LEN + sizeof(struct iw_quality)) + +/* iw_point events are special. First, the payload (extra data) come at + * the end of the event, so they are bigger than IW_EV_POINT_LEN. Second, + * we omit the pointer, so start at an offset. */ +#define IW_EV_POINT_OFF (((char *) &(((struct iw_point *) NULL)->length)) - \ + (char *) NULL) +#define IW_EV_POINT_LEN (IW_EV_LCP_LEN + sizeof(struct iw_point) - \ + IW_EV_POINT_OFF) + +#endif /* _LINUX_WIRELESS_H */ |
