wpa_supplicant: Initial Revision 0.8.X
Based on:
commit 0725cc7b7efc434910e89865c42eda7ce61bbf08
Author: Jouni Malinen <j@w1.fi>
Date: Thu Apr 21 20:41:01 2011 +0300
Enable CONFIG_DRIVER_NL80211=y in the default configuration
nl80211 should be preferred over WEXT with any recent Linux
kernel version.
Change-Id: I26aec5afbbd4f4a1f5fd900912545b6f5050de64
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/src/drivers/.gitignore b/src/drivers/.gitignore
new file mode 100644
index 0000000..1d9e0e6
--- /dev/null
+++ b/src/drivers/.gitignore
@@ -0,0 +1,2 @@
+build.wpa_supplicant
+build.hostapd
diff --git a/src/drivers/Apple80211.h b/src/drivers/Apple80211.h
new file mode 100644
index 0000000..2a612e7
--- /dev/null
+++ b/src/drivers/Apple80211.h
@@ -0,0 +1,156 @@
+#ifndef APPLE80211_H
+#define APPLE80211_H
+
+/*
+ * Apple80211 framework definitions
+ * This is an undocumented interface and the definitions here are based on
+ * information from MacStumbler (http://www.macstumbler.com/Apple80211.h) and
+ * whatever related information can be found with google and experiments ;-).
+ */
+
+typedef struct __WirelessRef *WirelessRef;
+typedef SInt32 WirelessError;
+#define errWirelessNoError 0
+
+typedef struct WirelessInfo {
+ UInt16 link_qual;
+ UInt16 comms_qual;
+ UInt16 signal;
+ UInt16 noise;
+ UInt16 port_stat;
+ UInt16 client_mode;
+ UInt16 res1;
+ UInt16 power;
+ UInt16 res2;
+ UInt8 bssID[6];
+ UInt8 ssid[34];
+} WirelessInfo;
+
+typedef struct WirelessInfo2 {
+ /* TODO - these are probably not in correct order or complete */
+ WirelessInfo info1;
+ UInt8 macAddress[6];
+} WirelessInfo2;
+
+typedef struct WirelessNetworkInfo {
+ UInt16 channel;
+ UInt16 noise;
+ UInt16 signal;
+ UInt8 bssid[6];
+ UInt16 beacon_int;
+ UInt16 capability;
+ UInt16 ssid_len;
+ UInt8 ssid[32];
+} WirelessNetworkInfo;
+
+typedef int wirelessKeyType; /* TODO */
+
+int WirelessIsAvailable(void);
+WirelessError WirelessAttach(WirelessRef *ref, UInt32 res);
+WirelessError WirelessDetach(WirelessRef ref);
+WirelessError WirelessPrivate(WirelessRef ref, void *in_ptr, int in_bytes,
+ void *out_ptr, int out_bytes);
+WirelessError WirelessSetEnabled(WirelessRef ref, UInt8 enabled);
+WirelessError WirelessGetEnabled(WirelessRef ref, UInt8 *enabled);
+WirelessError WirelessSetPower(WirelessRef ref, UInt8 power);
+WirelessError WirelessGetPower(WirelessRef ref, UInt8 *power);
+WirelessError WirelessGetInfo(WirelessRef ref, WirelessInfo *info);
+WirelessError WirelessGetInfo2(WirelessRef ref, WirelessInfo2 *info);
+WirelessError WirelessScan(WirelessRef ref, CFArrayRef *results,
+ UInt32 strip_dups);
+WirelessError WirelessScanSplit(WirelessRef ref, CFArrayRef *ap_results,
+ CFArrayRef *ibss_results, UInt32 strip_dups);
+WirelessError WirelessDirectedScan(WirelessRef ref, CFArrayRef *results,
+ UInt32 strip_dups, CFStringRef ssid);
+WirelessError WirelessDirectedScan2(WirelessRef ref, CFDataRef ssid,
+ UInt32 strip_dups, CFArrayRef *results);
+WirelessError WirelessJoin(WirelessRef ref, CFStringRef ssid);
+WirelessError WirelessJoinWEP(WirelessRef ref, CFStringRef ssid,
+ CFStringRef passwd);
+WirelessError WirelessJoin8021x(WirelessRef ref, CFStringRef ssid);
+/*
+ * Set WEP key
+ * ref: wireless reference from WirelessAttach()
+ * type: ?
+ * key_idx: 0..3
+ * key_len: 13 for WEP-104 or 0 for clearing the key
+ * key: Pointer to the key or %NULL if key_len = 0
+ */
+WirelessError WirelessSetKey(WirelessRef ref, wirelessKeyType type,
+ int key_idx, int key_len,
+ const unsigned char *key);
+/*
+ * Set WPA key (e.g., PMK for 4-way handshake)
+ * ref: wireless reference from WirelessAttach()
+ * type: 0..4; 1 = PMK
+ * key_len: 16, 32, or 0
+ * key: Pointer to the key or %NULL if key_len = 0
+ */
+WirelessError WirelessSetWPAKey(WirelessRef ref, wirelessKeyType type,
+ int key_len, const unsigned char *key);
+WirelessError WirelessAssociate(WirelessRef ref, int type, CFDataRef ssid,
+ CFStringRef key);
+WirelessError WirelessAssociate2(WirelessRef ref, CFDictionaryRef scan_res,
+ CFStringRef key);
+WirelessError WirelessDisassociate(WirelessRef ref);
+
+/*
+ * Get a copy of scan results for the given SSID
+ * The returned dictionary includes following entries:
+ * beaconInterval: CFNumber(kCFNumberSInt32Type)
+ * SSID: CFData buffer of the SSID
+ * isWPA: CFNumber(kCFNumberSInt32Type); 0 = not used, 1 = WPA, -128 = WPA2
+ * name: Name of the network (SSID string)
+ * BSSID: CFData buffer of the BSSID
+ * channel: CFNumber(kCFNumberSInt32Type)
+ * signal: CFNumber(kCFNumberSInt32Type)
+ * appleIE: CFData
+ * WPSNOPINRequired: CFBoolean
+ * noise: CFNumber(kCFNumberSInt32Type)
+ * capability: CFNumber(kCFNumberSInt32Type)
+ * uniCipher: CFArray of CFNumber(kCFNumberSInt32Type)
+ * appleIE_Version: CFNumber(kCFNumberSInt32Type)
+ * appleIE_Robust: CFBoolean
+ * WPSConfigured: CFBoolean
+ * scanWasDirected: CFBoolean
+ * appleIE_Product: CFNumber(kCFNumberSInt32Type)
+ * authModes: CFArray of CFNumber(kCFNumberSInt32Type)
+ * multiCipher: CFNumber(kCFNumberSInt32Type)
+ */
+CFDictionaryRef WirelessSafeDirectedScanCopy(WirelessRef ref, CFDataRef ssid);
+
+/*
+ * Get information about the current association
+ * The returned dictionary includes following entries:
+ * keyData: CFData buffer of the key (e.g., 32-octet PSK)
+ * multiCipher: CFNumber(kCFNumberSInt32Type); 0 = none, 5 = CCMP?
+ * channel: CFNumber(kCFNumberSInt32Type)
+ * isIBSS: CFBoolean
+ * authMode: CFNumber(kCFNumberSInt32Type); 2 = WPA-Personal; 3 = open,
+ * 129 = WPA2-Enterprise
+ * isWPA: CFNumber(kCFNumberSInt32Type); 0 = not used, 1 = WPA, -128 == WPA2
+ * SSID: CFData buffer of the SSID
+ * cipherMode: CFNumber(kCFNumberSInt32Type); 0 = none, 4 = CCMP?
+ */
+CFDictionaryRef WirelessGetAssociationInfo(WirelessRef ref);
+
+WirelessError WirelessConfigure(WirelessRef ref);
+
+/*
+ * Get ASP information
+ * The returned dictionary includes following entries:
+ * Version: version number (e.g., 3.0)
+ * Channel: channel (e.g., 1)
+ * Vendor: vendor (e.g., 2)
+ */
+CFDictionaryRef WirelessGetInfoASP(void);
+
+/*
+ * Get a copy of the interface dictionary
+ * The returned dictionary has a key,value pairs for wireless interfaces.
+ * The key is the interface name and the value is the driver identifier, e.g.,
+ * en1: com.apple.driver.AirPort.Atheros
+ */
+CFDictionaryRef WirelessCopyInterfaceDict(void);
+
+#endif /* APPLE80211_H */
diff --git a/src/drivers/Makefile b/src/drivers/Makefile
new file mode 100644
index 0000000..07600e5
--- /dev/null
+++ b/src/drivers/Makefile
@@ -0,0 +1,9 @@
+all:
+ @echo Nothing to be made.
+
+clean:
+ rm -f *~ *.o *.d
+ rm -f build.wpa_supplicant build.hostapd
+
+install:
+ @echo Nothing to be made.
diff --git a/src/drivers/MobileApple80211.c b/src/drivers/MobileApple80211.c
new file mode 100644
index 0000000..ce004fe
--- /dev/null
+++ b/src/drivers/MobileApple80211.c
@@ -0,0 +1,189 @@
+#include "includes.h"
+#include <dlfcn.h>
+
+#include "common.h"
+
+#include <CoreFoundation/CoreFoundation.h>
+#include "MobileApple80211.h"
+
+/*
+ * Code for dynamically loading Apple80211 functions from Aeropuerto to avoid
+ * having to link with full Preferences.framework.
+ */
+
+static void *aeropuerto = NULL;
+
+
+int _Apple80211Initialized(void)
+{
+ return aeropuerto ? 1 : 0;
+}
+
+
+static int (*__Apple80211Open)(Apple80211Ref *ctx) = NULL;
+
+int Apple80211Open(Apple80211Ref *ctx)
+{
+ return __Apple80211Open(ctx);
+}
+
+
+static int (*__Apple80211Close)(Apple80211Ref ctx) = NULL;
+
+int Apple80211Close(Apple80211Ref ctx)
+{
+ return __Apple80211Close(ctx);
+}
+
+
+static int (*__Apple80211GetIfListCopy)(Apple80211Ref handle, CFArrayRef *list)
+ = NULL;
+
+int Apple80211GetIfListCopy(Apple80211Ref handle, CFArrayRef *list)
+{
+ return __Apple80211GetIfListCopy(handle, list);
+}
+
+
+static int (*__Apple80211BindToInterface)(Apple80211Ref handle,
+ CFStringRef interface) = NULL;
+
+int Apple80211BindToInterface(Apple80211Ref handle,
+ CFStringRef interface)
+{
+ return __Apple80211BindToInterface(handle, interface);
+}
+
+
+static int (*__Apple80211GetInterfaceNameCopy)(Apple80211Ref handle,
+ CFStringRef *name) = NULL;
+
+int Apple80211GetInterfaceNameCopy(Apple80211Ref handle,
+ CFStringRef *name)
+{
+ return __Apple80211GetInterfaceNameCopy(handle, name);
+}
+
+
+static int (*__Apple80211GetInfoCopy)(Apple80211Ref handle,
+ CFDictionaryRef *info) = NULL;
+
+int Apple80211GetInfoCopy(Apple80211Ref handle,
+ CFDictionaryRef *info)
+{
+ return __Apple80211GetInfoCopy(handle, info);
+}
+
+
+static int (*__Apple80211GetPower)(Apple80211Ref handle, char *pwr) = NULL;
+
+int Apple80211GetPower(Apple80211Ref handle, char *pwr)
+{
+ return __Apple80211GetPower(handle, pwr);
+}
+
+
+static int (*__Apple80211SetPower)(Apple80211Ref handle, char pwr) = NULL;
+
+int Apple80211SetPower(Apple80211Ref handle, char pwr)
+{
+ return __Apple80211SetPower(handle, pwr);
+}
+
+
+static int (*__Apple80211Scan)(Apple80211Ref handle, CFArrayRef *list,
+ CFDictionaryRef parameters) = NULL;
+
+int Apple80211Scan(Apple80211Ref handle, CFArrayRef *list,
+ CFDictionaryRef parameters)
+{
+ return __Apple80211Scan(handle, list, parameters);
+}
+
+
+static int (*__Apple80211Associate)(Apple80211Ref handle, CFDictionaryRef bss,
+ CFStringRef password) = NULL;
+
+int Apple80211Associate(Apple80211Ref handle, CFDictionaryRef bss,
+ CFStringRef password)
+{
+ return __Apple80211Associate(handle, bss, password);
+}
+
+
+static int (*__Apple80211AssociateAndCopyInfo)(Apple80211Ref handle,
+ CFDictionaryRef bss,
+ CFStringRef password,
+ CFDictionaryRef *info) =
+ NULL;
+
+int Apple80211AssociateAndCopyInfo(Apple80211Ref handle, CFDictionaryRef bss,
+ CFStringRef password, CFDictionaryRef *info)
+{
+ return __Apple80211AssociateAndCopyInfo(handle, bss, password, info);
+}
+
+
+static int (*__Apple80211CopyValue)(Apple80211Ref handle, int field,
+ CFDictionaryRef arg2, void *value) = NULL;
+
+int Apple80211CopyValue(Apple80211Ref handle, int field, CFDictionaryRef arg2,
+ void *value)
+{
+ return __Apple80211CopyValue(handle, field, arg2, value);
+}
+
+
+#define DLSYM(s) \
+do { \
+ __ ## s = dlsym(aeropuerto, #s); \
+ if (__ ## s == NULL) { \
+ wpa_printf(MSG_ERROR, "MobileApple80211: Could not resolve " \
+ "symbol '" #s "' (%s)", dlerror()); \
+ err = 1; \
+ } \
+} while (0)
+
+
+__attribute__ ((constructor))
+void _Apple80211_constructor(void)
+{
+ const char *fname = "/System/Library/SystemConfiguration/"
+ "Aeropuerto.bundle/Aeropuerto";
+ int err = 0;
+
+ aeropuerto = dlopen(fname, RTLD_LAZY);
+ if (!aeropuerto) {
+ wpa_printf(MSG_ERROR, "MobileApple80211: Failed to open %s "
+ "for symbols", fname);
+ return;
+ }
+
+ DLSYM(Apple80211Open);
+ DLSYM(Apple80211Close);
+ DLSYM(Apple80211GetIfListCopy);
+ DLSYM(Apple80211BindToInterface);
+ DLSYM(Apple80211GetInterfaceNameCopy);
+ DLSYM(Apple80211GetInfoCopy);
+ DLSYM(Apple80211GetPower);
+ DLSYM(Apple80211SetPower);
+ DLSYM(Apple80211Scan);
+ DLSYM(Apple80211Associate);
+ DLSYM(Apple80211AssociateAndCopyInfo);
+ DLSYM(Apple80211CopyValue);
+
+ if (err) {
+ dlclose(aeropuerto);
+ aeropuerto = NULL;
+ }
+}
+
+
+__attribute__ ((destructor))
+void _Apple80211_destructor(void)
+{
+ if (aeropuerto) {
+ dlclose(aeropuerto);
+ aeropuerto = NULL;
+ }
+}
diff --git a/src/drivers/MobileApple80211.h b/src/drivers/MobileApple80211.h
new file mode 100644
index 0000000..64d439d
--- /dev/null
+++ b/src/drivers/MobileApple80211.h
@@ -0,0 +1,43 @@
+#ifndef MOBILEAPPLE80211_H
+#define MOBILEAPPLE80211_H
+
+/*
+ * MobileApple80211 interface for iPhone/iPod touch
+ * These functions are available from Aeropuerto.
+ */
+
+struct Apple80211;
+typedef struct Apple80211 *Apple80211Ref;
+
+int Apple80211Open(Apple80211Ref *ctx);
+int Apple80211Close(Apple80211Ref ctx);
+int Apple80211GetIfListCopy(Apple80211Ref handle, CFArrayRef *list);
+int Apple80211BindToInterface(Apple80211Ref handle,
+ CFStringRef interface);
+int Apple80211GetInterfaceNameCopy(Apple80211Ref handle,
+ CFStringRef *name);
+int Apple80211GetInfoCopy(Apple80211Ref handle,
+ CFDictionaryRef *info);
+int Apple80211GetPower(Apple80211Ref handle, char *pwr);
+int Apple80211SetPower(Apple80211Ref handle, char pwr);
+
+/* parameters can be NULL; returns scan results in CFArrayRef *list;
+ * caller will need to free with CFRelease() */
+int Apple80211Scan(Apple80211Ref handle, CFArrayRef *list,
+ CFDictionaryRef parameters);
+
+int Apple80211Associate(Apple80211Ref handle, CFDictionaryRef bss,
+ CFStringRef password);
+int Apple80211AssociateAndCopyInfo(Apple80211Ref handle, CFDictionaryRef bss,
+ CFStringRef password,
+ CFDictionaryRef *info);
+
+enum {
+ APPLE80211_VALUE_SSID = 1,
+ APPLE80211_VALUE_BSSID = 9
+};
+
+int Apple80211CopyValue(Apple80211Ref handle, int field, CFDictionaryRef arg2,
+ void *value);
+
+#endif /* MOBILEAPPLE80211_H */
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
new file mode 100644
index 0000000..8efd697
--- /dev/null
+++ b/src/drivers/driver.h
@@ -0,0 +1,3230 @@
+/*
+ * 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
+ * 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.
+ *
+ * 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 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
+#define HOSTAPD_CHAN_HT40PLUS 0x00000010
+#define HOSTAPD_CHAN_HT40MINUS 0x00000020
+#define HOSTAPD_CHAN_HT40 0x00000040
+
+/**
+ * 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;
+
+ /**
+ * 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 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 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)
+ * @caps: capability information field in host byte order
+ * @qual: signal quality
+ * @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
+ * the driver or OS specific scan results into this format.
+ *
+ * If the driver does not support reporting all IEs, the IE data structure is
+ * constructed of the IEs that are available. This field will also need to
+ * include SSID in IE format. All drivers are encouraged to be extended to
+ * 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;
+ u16 caps;
+ int qual;
+ int noise;
+ int level;
+ u64 tsf;
+ unsigned int age;
+ size_t ie_len;
+ 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.
+ */
+};
+
+/**
+ * struct wpa_scan_results - Scan results
+ * @res: Array of pointers to allocated variable length scan result entries
+ * @num: Number of entries in the scan result array
+ */
+struct wpa_scan_results {
+ struct wpa_scan_res **res;
+ size_t num;
+};
+
+/**
+ * struct wpa_interface_info - Network interface information
+ * @next: Pointer to the next interface or NULL if this is the last one
+ * @ifname: Interface name that can be used with init() or init2()
+ * @desc: Human readable adapter description (e.g., vendor/model) or NULL if
+ * not available
+ * @drv_name: struct wpa_driver_ops::name (note: unlike other strings, this one
+ * is not an allocated copy, i.e., get_interfaces() caller will not free
+ * this)
+ */
+struct wpa_interface_info {
+ struct wpa_interface_info *next;
+ char *ifname;
+ char *desc;
+ 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;
+};
+
+enum wps_mode {
+ WPS_MODE_NONE /* no WPS provisioning being used */,
+ WPS_MODE_OPEN /* WPS provisioning with AP that is in open mode */,
+ WPS_MODE_PRIVACY /* WPS provisioning with AP that is using protection
+ */
+};
+
+/**
+ * struct wpa_driver_associate_params - Association parameters
+ * Data for struct wpa_driver_ops::associate().
+ */
+struct wpa_driver_associate_params {
+ /**
+ * bssid - BSSID of the selected AP
+ * This can be %NULL, if ap_scan=2 mode is used and the driver is
+ * responsible for selecting with which BSS to associate. */
+ const u8 *bssid;
+
+ /**
+ * ssid - The selected SSID
+ */
+ const u8 *ssid;
+
+ /**
+ * ssid_len - Length of the SSID (1..32)
+ */
+ size_t ssid_len;
+
+ /**
+ * freq - Frequency of the channel the selected AP is using
+ * Frequency that the selected AP is using (in MHz as
+ * reported in the scan results)
+ */
+ int freq;
+
+ /**
+ * wpa_ie - WPA information element for (Re)Association Request
+ * WPA information element to be included in (Re)Association
+ * Request (including information element id and length). Use
+ * of this WPA IE is optional. If the driver generates the WPA
+ * IE, it can use pairwise_suite, group_suite, and
+ * key_mgmt_suite to select proper algorithms. In this case,
+ * the driver has to notify wpa_supplicant about the used WPA
+ * IE by generating an event that the interface code will
+ * convert into EVENT_ASSOCINFO data (see below).
+ *
+ * When using WPA2/IEEE 802.11i, wpa_ie is used for RSN IE
+ * instead. The driver can determine which version is used by
+ * looking at the first byte of the IE (0xdd for WPA, 0x30 for
+ * WPA2/RSN).
+ *
+ * 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;
+
+ /**
+ * 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 WPA_AUTH_ALG_*
+ */
+ int auth_alg;
+
+ /**
+ * mode - Operation mode (infra/ibss) IEEE80211_MODE_*
+ */
+ int mode;
+
+ /**
+ * wep_key - WEP keys for static WEP configuration
+ */
+ const u8 *wep_key[4];
+
+ /**
+ * wep_key_len - WEP key length for static WEP configuration
+ */
+ size_t wep_key_len[4];
+
+ /**
+ * wep_tx_keyidx - WEP TX key index for static WEP configuration
+ */
+ int wep_tx_keyidx;
+
+ /**
+ * mgmt_frame_protection - IEEE 802.11w management frame protection
+ */
+ enum mfp_options mgmt_frame_protection;
+
+ /**
+ * ft_ies - IEEE 802.11r / FT information elements
+ * If the supplicant is using IEEE 802.11r (FT) and has the needed keys
+ * for fast transition, this parameter is set to include the IEs that
+ * are to be sent in the next FT Authentication Request message.
+ * update_ft_ies() handler is called to update the IEs for further
+ * FT messages in the sequence.
+ *
+ * The driver should use these IEs only if the target AP is advertising
+ * the same mobility domain as the one included in the MDIE here.
+ *
+ * In ap_scan=2 mode, the driver can use these IEs when moving to a new
+ * AP after the initial association. These IEs can only be used if the
+ * target AP is advertising support for FT and is using the same MDIE
+ * and SSID as the current AP.
+ *
+ * The driver is responsible for reporting the FT IEs received from the
+ * AP's response using wpa_supplicant_event() with EVENT_FT_RESPONSE
+ * type. update_ft_ies() handler will then be called with the FT IEs to
+ * include in the next frame in the authentication sequence.
+ */
+ const u8 *ft_ies;
+
+ /**
+ * ft_ies_len - Length of ft_ies in bytes
+ */
+ size_t ft_ies_len;
+
+ /**
+ * ft_md - FT Mobility domain (6 octets) (also included inside ft_ies)
+ *
+ * This value is provided to allow the driver interface easier access
+ * to the current mobility domain. This value is set to %NULL if no
+ * mobility domain is currently active.
+ */
+ const u8 *ft_md;
+
+ /**
+ * passphrase - RSN passphrase for PSK
+ *
+ * This value is made available only for WPA/WPA2-Personal (PSK) and
+ * only for drivers that set WPA_DRIVER_FLAGS_4WAY_HANDSHAKE. This is
+ * the 8..63 character ASCII passphrase, if available. Please note that
+ * this can be %NULL if passphrase was not used to generate the PSK. In
+ * that case, the psk field must be used to fetch the PSK.
+ */
+ const char *passphrase;
+
+ /**
+ * psk - RSN PSK (alternative for passphrase for PSK)
+ *
+ * This value is made available only for WPA/WPA2-Personal (PSK) and
+ * only for drivers that set WPA_DRIVER_FLAGS_4WAY_HANDSHAKE. This is
+ * the 32-octet (256-bit) PSK, if available. The driver wrapper should
+ * 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;
+
+ /**
+ * wps - WPS mode
+ *
+ * If the driver needs to do special configuration for WPS association,
+ * this variable provides more information on what type of association
+ * is being requested. Most drivers should not need ot use this.
+ */
+ enum wps_mode wps;
+
+ /**
+ * p2p - Whether this connection is a P2P group
+ */
+ int p2p;
+
+ /**
+ * uapsd - UAPSD parameters for the network
+ * -1 = do not change defaults
+ * AP mode: 1 = enabled, 0 = disabled
+ * STA mode: bits 0..3 UAPSD enabled for VO,VI,BK,BE
+ */
+ int uapsd;
+};
+
+/**
+ * struct wpa_driver_capa - Driver capability information
+ */
+struct wpa_driver_capa {
+#define WPA_DRIVER_CAPA_KEY_MGMT_WPA 0x00000001
+#define WPA_DRIVER_CAPA_KEY_MGMT_WPA2 0x00000002
+#define WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK 0x00000004
+#define WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK 0x00000008
+#define WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE 0x00000010
+#define WPA_DRIVER_CAPA_KEY_MGMT_FT 0x00000020
+#define WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK 0x00000040
+ unsigned int key_mgmt;
+
+#define WPA_DRIVER_CAPA_ENC_WEP40 0x00000001
+#define WPA_DRIVER_CAPA_ENC_WEP104 0x00000002
+#define WPA_DRIVER_CAPA_ENC_TKIP 0x00000004
+#define WPA_DRIVER_CAPA_ENC_CCMP 0x00000008
+ unsigned int enc;
+
+#define WPA_DRIVER_AUTH_OPEN 0x00000001
+#define WPA_DRIVER_AUTH_SHARED 0x00000002
+#define WPA_DRIVER_AUTH_LEAP 0x00000004
+ unsigned int auth;
+
+/* 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
+/* Driver takes care of P2P management operations */
+#define WPA_DRIVER_FLAGS_P2P_MGMT 0x00000100
+/* Driver supports concurrent P2P operations */
+#define WPA_DRIVER_FLAGS_P2P_CONCURRENT 0x00000200
+/*
+ * Driver uses the initial interface as a dedicated management interface, i.e.,
+ * it cannot be used for P2P group operations or non-P2P purposes.
+ */
+#define WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE 0x00000400
+/* This interface is P2P capable (P2P Device, GO, or P2P Client */
+#define WPA_DRIVER_FLAGS_P2P_CAPABLE 0x00000800
+/* Driver supports concurrent operations on multiple channels */
+#define WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT 0x00001000
+/*
+ * Driver uses the initial interface for P2P management interface and non-P2P
+ * purposes (e.g., connect to infra AP), but this interface cannot be used for
+ * P2P group operations.
+ */
+#define WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P 0x00002000
+/*
+ * Driver is known to use sane error codes, i.e., when it indicates that
+ * something (e.g., association) fails, there was indeed a failure and the
+ * operation does not end up getting completed successfully later.
+ */
+#define WPA_DRIVER_FLAGS_SANE_ERROR_CODES 0x00004000
+/* Driver supports off-channel TX */
+#define WPA_DRIVER_FLAGS_OFFCHANNEL_TX 0x00008000
+/* Driver indicates TX status events for EAPOL Data frames */
+#define WPA_DRIVER_FLAGS_EAPOL_TX_STATUS 0x00010000
+ unsigned int flags;
+
+ int max_scan_ssids;
+
+ /**
+ * max_remain_on_chan - Maximum remain-on-channel duration in msec
+ */
+ unsigned int max_remain_on_chan;
+
+ /**
+ * max_stations - Maximum number of associated stations the driver
+ * supports in AP mode
+ */
+ unsigned int max_stations;
+};
+
+
+struct hostapd_data;
+
+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;
+};
+
+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 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 */
+};
+
+enum wpa_driver_if_type {
+ /**
+ * WPA_IF_STATION - Station mode interface
+ */
+ WPA_IF_STATION,
+
+ /**
+ * 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,
+
+ /**
+ * WPA_IF_P2P_GO - P2P Group Owner
+ */
+ WPA_IF_P2P_GO,
+
+ /**
+ * WPA_IF_P2P_CLIENT - P2P Client
+ */
+ WPA_IF_P2P_CLIENT,
+
+ /**
+ * WPA_IF_P2P_GROUP - P2P Group interface (will become either
+ * WPA_IF_P2P_GO or WPA_IF_P2P_CLIENT, but the role is not yet known)
+ */
+ WPA_IF_P2P_GROUP
+};
+
+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 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;
+ enum mfp_options ieee80211w;
+};
+
+#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 p2p_params - P2P parameters for driver-based P2P management
+ */
+struct p2p_params {
+ const char *dev_name;
+ u8 pri_dev_type[8];
+#define DRV_MAX_SEC_DEV_TYPES 5
+ u8 sec_dev_type[DRV_MAX_SEC_DEV_TYPES][8];
+ size_t num_sec_dev_types;
+};
+
+enum tdls_oper {
+ TDLS_DISCOVERY_REQ,
+ TDLS_SETUP,
+ TDLS_TEARDOWN,
+ TDLS_ENABLE_LINK,
+ TDLS_DISABLE_LINK,
+ TDLS_ENABLE,
+ TDLS_DISABLE
+};
+
+/**
+ * struct wpa_signal_info - Information about channel signal quality
+ */
+struct wpa_signal_info {
+ u32 frequency;
+ int above_threshold;
+ int current_signal;
+ int current_noise;
+ int current_txrate;
+};
+
+/**
+ * struct wpa_driver_ops - Driver interface API definition
+ *
+ * This structure defines the API that each driver interface needs to implement
+ * for core wpa_supplicant code. All driver specific functionality is captured
+ * in this wrapper.
+ */
+struct wpa_driver_ops {
+ /** Name of the driver interface */
+ const char *name;
+ /** One line description of the driver interface */
+ const char *desc;
+
+ /**
+ * get_bssid - Get the current BSSID
+ * @priv: private driver interface data
+ * @bssid: buffer for BSSID (ETH_ALEN = 6 bytes)
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * Query kernel driver for the current BSSID and copy it to bssid.
+ * Setting bssid to 00:00:00:00:00:00 is recommended if the STA is not
+ * associated.
+ */
+ int (*get_bssid)(void *priv, u8 *bssid);
+
+ /**
+ * get_ssid - Get the current SSID
+ * @priv: private driver interface data
+ * @ssid: buffer for SSID (at least 32 bytes)
+ *
+ * Returns: Length of the SSID on success, -1 on failure
+ *
+ * Query kernel driver for the current SSID and copy it to ssid.
+ * Returning zero is recommended if the STA is not associated.
+ *
+ * Note: SSID is an array of octets, i.e., it is not nul terminated and
+ * can, at least in theory, contain control characters (including nul)
+ * and as such, should be processed as binary data, not a printable
+ * string.
+ */
+ int (*get_ssid)(void *priv, u8 *ssid);
+
+ /**
+ * 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);
+ * %WPA_ALG_NONE clears the key.
+ * @addr: Address of the peer STA (BSSID of the current AP when setting
+ * pairwise key in station mode), ff:ff:ff:ff:ff:ff for
+ * broadcast keys, %NULL for default keys that are used both for
+ * broadcast and unicast; when clearing keys, %NULL is used to
+ * indicate that both the broadcast-only and default key of the
+ * specified key index is to be cleared
+ * @key_idx: key index (0..3), usually 0 for unicast keys; 0..4095 for
+ * IGTK
+ * @set_tx: configure this key as the default Tx key (only used when
+ * driver does not support separate unicast/individual key
+ * @seq: sequence number/packet number, seq_len octets, the next
+ * packet number to be used for in replay protection; configured
+ * for Rx keys (in most cases, this is only used with broadcast
+ * keys and set to zero for unicast keys); %NULL if not set
+ * @seq_len: length of the seq, depends on the algorithm:
+ * TKIP: 6 octets, CCMP: 6 octets, IGTK: 6 octets
+ * @key: key buffer; TKIP: 16-byte temporal key, 8-byte Tx Mic key,
+ * 8-byte Rx Mic Key
+ * @key_len: length of the key buffer in octets (WEP: 5 or 13,
+ * TKIP: 32, CCMP: 16, IGTK: 16)
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * Configure the given key for the kernel driver. If the driver
+ * supports separate individual keys (4 default keys + 1 individual),
+ * addr can be used to determine whether the key is default or
+ * individual. If only 4 keys are supported, the default key with key
+ * index 0 is used as the individual key. STA must be configured to use
+ * it as the default Tx key (set_tx is set) and accept Rx for all the
+ * key indexes. In most cases, WPA uses only key indexes 1 and 2 for
+ * broadcast keys, so key index 0 is available for this kind of
+ * configuration.
+ *
+ * Please note that TKIP keys include separate TX and RX MIC keys and
+ * some drivers may expect them in different order than wpa_supplicant
+ * is using. If the TX/RX keys are swapped, all TKIP encrypted packets
+ * will trigger Michael MIC errors. This can be fixed by changing the
+ * order of MIC keys by swapping te bytes 16..23 and 24..31 of the key
+ * in driver_*.c set_key() implementation, see driver_ndis.c for an
+ * example on how this can be done.
+ */
+ 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);
+
+ /**
+ * init - Initialize 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
+ *
+ * Initialize driver interface, including event processing for kernel
+ * driver events (e.g., associated, scan results, Michael MIC failure).
+ * This function can allocate a private configuration data area for
+ * @ctx, file descriptor, interface name, etc. information that may be
+ * needed in future driver operations. If this is not used, non-NULL
+ * value will need to be returned because %NULL is used to indicate
+ * failure. The returned value will be used as 'void *priv' data for
+ * all other driver_ops functions.
+ *
+ * The main event loop (eloop.c) of wpa_supplicant can be used to
+ * register callback for read sockets (eloop_register_read_sock()).
+ *
+ * See below for more information about events and
+ * wpa_supplicant_event() function.
+ */
+ void * (*init)(void *ctx, const char *ifname);
+
+ /**
+ * deinit - Deinitialize driver interface
+ * @priv: private driver interface data from init()
+ *
+ * Shut down driver interface and processing of driver events. Free
+ * private data buffer if one was allocated in init() handler.
+ */
+ void (*deinit)(void *priv);
+
+ /**
+ * set_param - Set driver configuration parameters
+ * @priv: private driver interface data from init()
+ * @param: driver specific configuration parameters
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * Optional handler for notifying driver interface about configuration
+ * parameters (driver_param).
+ */
+ int (*set_param)(void *priv, const char *param);
+
+ /**
+ * set_countermeasures - Enable/disable TKIP countermeasures
+ * @priv: private driver interface data
+ * @enabled: 1 = countermeasures enabled, 0 = disabled
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * Configure TKIP countermeasures. When these are enabled, the driver
+ * should drop all received and queued frames that are using TKIP.
+ */
+ int (*set_countermeasures)(void *priv, int enabled);
+
+ /**
+ * deauthenticate - Request driver to deauthenticate
+ * @priv: private driver interface data
+ * @addr: peer address (BSSID of the AP)
+ * @reason_code: 16-bit reason code to be sent in the deauthentication
+ * frame
+ *
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*deauthenticate)(void *priv, const u8 *addr, int reason_code);
+
+ /**
+ * disassociate - Request driver to disassociate
+ * @priv: private driver interface data
+ * @addr: peer address (BSSID of the AP)
+ * @reason_code: 16-bit reason code to be sent in the disassociation
+ * frame
+ *
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*disassociate)(void *priv, const u8 *addr, int reason_code);
+
+ /**
+ * associate - Request driver to associate
+ * @priv: private driver interface data
+ * @params: association parameters
+ *
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*associate)(void *priv,
+ struct wpa_driver_associate_params *params);
+
+ /**
+ * add_pmkid - Add PMKSA cache entry to the driver
+ * @priv: private driver interface data
+ * @bssid: BSSID for the PMKSA cache entry
+ * @pmkid: PMKID for the PMKSA cache entry
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is called when a new PMK is received, as a result of
+ * either normal authentication or RSN pre-authentication.
+ *
+ * If the driver generates RSN IE, i.e., it does not use wpa_ie in
+ * associate(), add_pmkid() can be used to add new PMKSA cache entries
+ * in the driver. If the driver uses wpa_ie from wpa_supplicant, this
+ * driver_ops function does not need to be implemented. Likewise, if
+ * the driver does not support WPA, this function is not needed.
+ */
+ int (*add_pmkid)(void *priv, const u8 *bssid, const u8 *pmkid);
+
+ /**
+ * remove_pmkid - Remove PMKSA cache entry to the driver
+ * @priv: private driver interface data
+ * @bssid: BSSID for the PMKSA cache entry
+ * @pmkid: PMKID for the PMKSA cache entry
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is called when the supplicant drops a PMKSA cache
+ * entry for any reason.
+ *
+ * If the driver generates RSN IE, i.e., it does not use wpa_ie in
+ * associate(), remove_pmkid() can be used to synchronize PMKSA caches
+ * between the driver and wpa_supplicant. If the driver uses wpa_ie
+ * from wpa_supplicant, this driver_ops function does not need to be
+ * implemented. Likewise, if the driver does not support WPA, this
+ * function is not needed.
+ */
+ int (*remove_pmkid)(void *priv, const u8 *bssid, const u8 *pmkid);
+
+ /**
+ * flush_pmkid - Flush PMKSA cache
+ * @priv: private driver interface data
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is called when the supplicant drops all PMKSA cache
+ * entries for any reason.
+ *
+ * If the driver generates RSN IE, i.e., it does not use wpa_ie in
+ * associate(), remove_pmkid() can be used to synchronize PMKSA caches
+ * between the driver and wpa_supplicant. If the driver uses wpa_ie
+ * from wpa_supplicant, this driver_ops function does not need to be
+ * implemented. Likewise, if the driver does not support WPA, this
+ * function is not needed.
+ */
+ int (*flush_pmkid)(void *priv);
+
+ /**
+ * get_capa - Get driver capabilities
+ * @priv: private driver interface data
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * Get driver/firmware/hardware capabilities.
+ */
+ int (*get_capa)(void *priv, struct wpa_driver_capa *capa);
+
+ /**
+ * poll - Poll driver for association information
+ * @priv: private driver interface data
+ *
+ * This is an option callback that can be used when the driver does not
+ * provide event mechanism for association events. This is called when
+ * receiving WPA EAPOL-Key messages that require association
+ * information. The driver interface is supposed to generate associnfo
+ * event before returning from this callback function. In addition, the
+ * driver interface should generate an association event after having
+ * sent out associnfo.
+ */
+ void (*poll)(void *priv);
+
+ /**
+ * get_ifname - Get interface name
+ * @priv: private driver interface data
+ *
+ * Returns: Pointer to the interface name. This can differ from the
+ * interface name used in init() call. Init() is called first.
+ *
+ * This optional function can be used to allow the driver interface to
+ * replace the interface name with something else, e.g., based on an
+ * interface mapping from a more descriptive name.
+ */
+ const char * (*get_ifname)(void *priv);
+
+ /**
+ * get_mac_addr - Get own MAC address
+ * @priv: private driver interface data
+ *
+ * Returns: Pointer to own MAC address or %NULL on failure
+ *
+ * This optional function can be used to get the own MAC address of the
+ * device from the driver interface code. This is only needed if the
+ * l2_packet implementation for the OS does not provide easy access to
+ * a MAC address. */
+ const u8 * (*get_mac_addr)(void *priv);
+
+ /**
+ * send_eapol - Optional function for sending EAPOL packets
+ * @priv: private driver interface data
+ * @dest: Destination MAC address
+ * @proto: Ethertype
+ * @data: EAPOL packet starting with IEEE 802.1X header
+ * @data_len: Size of the EAPOL packet
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * This optional function can be used to override l2_packet operations
+ * 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 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);
+
+ /**
+ * set_operstate - Sets device operating state to DORMANT or UP
+ * @priv: private driver interface data
+ * @state: 0 = dormant, 1 = up
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is an optional function that can be used on operating systems
+ * that support a concept of controlling network device state from user
+ * space applications. This function, if set, gets called with
+ * state = 1 when authentication has been completed and with state = 0
+ * when connection is lost.
+ */
+ int (*set_operstate)(void *priv, int state);
+
+ /**
+ * mlme_setprotection - MLME-SETPROTECTION.request primitive
+ * @priv: Private driver interface data
+ * @addr: Address of the station for which to set protection (may be
+ * %NULL for group keys)
+ * @protect_type: MLME_SETPROTECTION_PROTECT_TYPE_*
+ * @key_type: MLME_SETPROTECTION_KEY_TYPE_*
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is an optional function that can be used to set the driver to
+ * require protection for Tx and/or Rx frames. This uses the layer
+ * interface defined in IEEE 802.11i-2004 clause 10.3.22.1
+ * (MLME-SETPROTECTION.request). Many drivers do not use explicit
+ * set protection operation; instead, they set protection implicitly
+ * based on configured keys.
+ */
+ int (*mlme_setprotection)(void *priv, const u8 *addr, int protect_type,
+ int key_type);
+
+ /**
+ * get_hw_feature_data - Get hardware support data (channels and rates)
+ * @priv: Private driver interface data
+ * @num_modes: Variable for returning the number of returned modes
+ * flags: Variable for returning hardware feature flags
+ * Returns: Pointer to allocated hardware data on success or %NULL on
+ * failure. Caller is responsible for freeing this.
+ *
+ * This function is only needed for drivers that export MLME
+ * (management frame processing) to %wpa_supplicant or hostapd.
+ */
+ 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: HOSTAPD_MODE_IEEE80211B, ..
+ * @chan: IEEE 802.11 channel number
+ * @freq: Frequency of the channel in MHz
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only needed for drivers that export MLME
+ * (management frame processing) to wpa_supplicant.
+ */
+ int (*set_channel)(void *priv, enum hostapd_hw_mode phymode, int chan,
+ int freq);
+
+ /**
+ * set_ssid - Set SSID
+ * @priv: Private driver interface data
+ * @ssid: SSID
+ * @ssid_len: SSID length
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only needed for drivers that export MLME
+ * (management frame processing) to wpa_supplicant.
+ */
+ int (*set_ssid)(void *priv, const u8 *ssid, size_t ssid_len);
+
+ /**
+ * set_bssid - Set BSSID
+ * @priv: Private driver interface data
+ * @bssid: BSSID
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only needed for drivers that export MLME
+ * (management frame processing) to wpa_supplicant.
+ */
+ int (*set_bssid)(void *priv, const u8 *bssid);
+
+ /**
+ * send_mlme - Send management frame from MLME
+ * @priv: Private driver interface data
+ * @data: IEEE 802.11 management frame with IEEE 802.11 header
+ * @data_len: Size of the management frame
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only needed for drivers that export MLME
+ * (management frame processing) to wpa_supplicant.
+ */
+ int (*send_mlme)(void *priv, const u8 *data, size_t data_len);
+
+ /**
+ * mlme_add_sta - Add a STA entry into the driver/netstack
+ * @priv: Private driver interface data
+ * @addr: MAC address of the STA (e.g., BSSID of the AP)
+ * @supp_rates: Supported rate set (from (Re)AssocResp); in IEEE 802.11
+ * format (one octet per rate, 1 = 0.5 Mbps)
+ * @supp_rates_len: Number of entries in supp_rates
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only needed for drivers that export MLME
+ * (management frame processing) to wpa_supplicant. When the MLME code
+ * completes association with an AP, this function is called to
+ * configure the driver/netstack with a STA entry for data frame
+ * processing (TX rate control, encryption/decryption).
+ */
+ int (*mlme_add_sta)(void *priv, const u8 *addr, const u8 *supp_rates,
+ size_t supp_rates_len);
+
+ /**
+ * mlme_remove_sta - Remove a STA entry from the driver/netstack
+ * @priv: Private driver interface data
+ * @addr: MAC address of the STA (e.g., BSSID of the AP)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only needed for drivers that export MLME
+ * (management frame processing) to wpa_supplicant.
+ */
+ int (*mlme_remove_sta)(void *priv, const u8 *addr);
+
+ /**
+ * update_ft_ies - Update FT (IEEE 802.11r) IEs
+ * @priv: Private driver interface data
+ * @md: Mobility domain (2 octets) (also included inside ies)
+ * @ies: FT IEs (MDIE, FTIE, ...) or %NULL to remove IEs
+ * @ies_len: Length of FT IEs in bytes
+ * Returns: 0 on success, -1 on failure
+ *
+ * The supplicant uses this callback to let the driver know that keying
+ * material for FT is available and that the driver can use the
+ * provided IEs in the next message in FT authentication sequence.
+ *
+ * This function is only needed for driver that support IEEE 802.11r
+ * (Fast BSS Transition).
+ */
+ int (*update_ft_ies)(void *priv, const u8 *md, const u8 *ies,
+ size_t ies_len);
+
+ /**
+ * send_ft_action - Send FT Action frame (IEEE 802.11r)
+ * @priv: Private driver interface data
+ * @action: Action field value
+ * @target_ap: Target AP address
+ * @ies: FT IEs (MDIE, FTIE, ...) (FT Request action frame body)
+ * @ies_len: Length of FT IEs in bytes
+ * Returns: 0 on success, -1 on failure
+ *
+ * The supplicant uses this callback to request the driver to transmit
+ * an FT Action frame (action category 6) for over-the-DS fast BSS
+ * transition.
+ */
+ int (*send_ft_action)(void *priv, u8 action, const u8 *target_ap,
+ const u8 *ies, size_t ies_len);
+
+ /**
+ * get_scan_results2 - Fetch the latest scan results
+ * @priv: private driver interface data
+ *
+ * Returns: Allocated buffer of scan results (caller is responsible for
+ * freeing the data structure) on success, NULL on failure
+ */
+ struct wpa_scan_results * (*get_scan_results2)(void *priv);
+
+ /**
+ * set_country - Set country
+ * @priv: Private driver interface data
+ * @alpha2: country to which to switch to
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is for drivers which support some form
+ * of setting a regulatory domain.
+ */
+ int (*set_country)(void *priv, const char *alpha2);
+
+ /**
+ * global_init - Global driver initialization
+ * Returns: Pointer to private data (global), %NULL on failure
+ *
+ * This optional function is called to initialize the driver wrapper
+ * for global data, i.e., data that applies to all interfaces. If this
+ * function is implemented, global_deinit() will also need to be
+ * implemented to free the private data. The driver will also likely
+ * use init2() function instead of init() to get the pointer to global
+ * data available to per-interface initializer.
+ */
+ void * (*global_init)(void);
+
+ /**
+ * global_deinit - Global driver deinitialization
+ * @priv: private driver global data from global_init()
+ *
+ * Terminate any global driver related functionality and free the
+ * global data structure.
+ */
+ void (*global_deinit)(void *priv);
+
+ /**
+ * init2 - Initialize driver interface (with global data)
+ * @ctx: context to be used when calling wpa_supplicant functions,
+ * e.g., wpa_supplicant_event()
+ * @ifname: interface name, e.g., wlan0
+ * @global_priv: private driver global data from global_init()
+ * Returns: Pointer to private data, %NULL on failure
+ *
+ * This function can be used instead of init() if the driver wrapper
+ * uses global data.
+ */
+ void * (*init2)(void *ctx, const char *ifname, void *global_priv);
+
+ /**
+ * get_interfaces - Get information about available interfaces
+ * @global_priv: private driver global data from global_init()
+ * Returns: Allocated buffer of interface information (caller is
+ * responsible for freeing the data structure) on success, NULL on
+ * 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
+ * @flags: WPA_STA_* flags for the destination station
+ *
+ * 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, u32 flags);
+
+ /**
+ * 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 (0 = VO, 1 = VI, 2 = BE, 3 = BK)
+ * @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)
+ * @bridge: Bridge interface to use or %NULL if no bridge configured
+ * 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,
+ const char *bridge);
+
+ /**
+ * 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)
+ * @assocresp: WPS IE(s) for (Re)Association 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. If the driver takes care
+ * of (Re)Association frame processing, the assocresp buffer includes
+ * WPS IE(s) that need to be added to (Re)Association Response frames
+ * whenever a (Re)Association Request frame indicated use of WPS.
+ *
+ * This will also be used to add P2P IE(s) into Beacon/Probe Response
+ * frames when operating as a GO. The driver is responsible for adding
+ * timing related attributes (e.g., NoA) in addition to the IEs
+ * included here by appending them after these buffers. This call is
+ * also used to provide Probe Response IEs for P2P Listen state
+ * operations for drivers that generate the Probe Response frames
+ * internally.
+ */
+ int (*set_ap_wps_ie)(void *priv, const struct wpabuf *beacon,
+ const struct wpabuf *proberesp,
+ const struct wpabuf *assocresp);
+
+ /**
+ * 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
+ * @bridge_ifname: Bridge interface to use for the WDS station or %NULL
+ * to indicate that bridge is not to be used
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*set_wds_sta)(void *priv, const u8 *addr, int aid, int val,
+ const char *bridge_ifname);
+
+ /**
+ * send_action - Transmit an Action frame
+ * @priv: Private driver interface data
+ * @freq: Frequency (in MHz) of the channel
+ * @wait: Time to wait off-channel for a response (in ms), or zero
+ * @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 the %WPA_DRIVER_FLAGS_OFFCHANNEL_TX flag is set, the frame will
+ * be transmitted on the given channel and the device will wait for a
+ * response on that channel for the given wait time.
+ *
+ * If the flag is not set, the wait time will be ignored. In this case,
+ * if a remain-on-channel duration is in progress, the frame must be
+ * transmitted on that channel; alternatively the frame may be sent on
+ * the current operational channel (if in associated state in station
+ * mode or while operating as an AP.)
+ */
+ int (*send_action)(void *priv, unsigned int freq, unsigned int wait,
+ const u8 *dst, const u8 *src, const u8 *bssid,
+ const u8 *data, size_t data_len);
+
+ /**
+ * send_action_cancel_wait - Cancel action frame TX wait
+ * @priv: Private driver interface data
+ *
+ * This command cancels the wait time associated with sending an action
+ * frame. It is only available when %WPA_DRIVER_FLAGS_OFFCHANNEL_TX is
+ * set in the driver flags.
+ */
+ void (*send_action_cancel_wait)(void *priv);
+
+ /**
+ * 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);
+
+ /**
+ * shared_freq - Get operating frequency of shared interface(s)
+ * @priv: Private driver interface data
+ * Returns: Operating frequency in MHz, 0 if no shared operation in
+ * use, or -1 on failure
+ *
+ * This command can be used to request the current operating frequency
+ * of any virtual interface that shares the same radio to provide
+ * information for channel selection for other virtual interfaces.
+ */
+ int (*shared_freq)(void *priv);
+
+ /**
+ * get_noa - Get current Notice of Absence attribute payload
+ * @priv: Private driver interface data
+ * @buf: Buffer for returning NoA
+ * @buf_len: Buffer length in octets
+ * Returns: Number of octets used in buf, 0 to indicate no NoA is being
+ * advertized, or -1 on failure
+ *
+ * This function is used to fetch the current Notice of Absence
+ * attribute value from GO.
+ */
+ int (*get_noa)(void *priv, u8 *buf, size_t buf_len);
+
+ /**
+ * set_noa - Set Notice of Absence parameters for GO (testing)
+ * @priv: Private driver interface data
+ * @count: Count
+ * @start: Start time in ms from next TBTT
+ * @duration: Duration in ms
+ * Returns: 0 on success or -1 on failure
+ *
+ * This function is used to set Notice of Absence parameters for GO. It
+ * is used only for testing. To disable NoA, all parameters are set to
+ * 0.
+ */
+ int (*set_noa)(void *priv, u8 count, int start, int duration);
+
+ /**
+ * set_p2p_powersave - Set P2P power save options
+ * @priv: Private driver interface data
+ * @legacy_ps: 0 = disable, 1 = enable, 2 = maximum PS, -1 = no change
+ * @opp_ps: 0 = disable, 1 = enable, -1 = no change
+ * @ctwindow: 0.. = change (msec), -1 = no change
+ * Returns: 0 on success or -1 on failure
+ */
+ int (*set_p2p_powersave)(void *priv, int legacy_ps, int opp_ps,
+ int ctwindow);
+
+ /**
+ * ampdu - Enable/disable aggregation
+ * @priv: Private driver interface data
+ * @ampdu: 1/0 = enable/disable A-MPDU aggregation
+ * Returns: 0 on success or -1 on failure
+ */
+ int (*ampdu)(void *priv, int ampdu);
+
+ /**
+ * set_intra_bss - Enables/Disables intra BSS bridging
+ */
+ int (*set_intra_bss)(void *priv, int enabled);
+
+ /**
+ * get_radio_name - Get physical radio name for the device
+ * @priv: Private driver interface data
+ * Returns: Radio name or %NULL if not known
+ *
+ * The returned data must not be modified by the caller. It is assumed
+ * that any interface that has the same radio name as another is
+ * sharing the same physical radio. This information can be used to
+ * share scan results etc. information between the virtual interfaces
+ * to speed up various operations.
+ */
+ const char * (*get_radio_name)(void *priv);
+
+ /**
+ * p2p_find - Start P2P Device Discovery
+ * @priv: Private driver interface data
+ * @timeout: Timeout for find operation in seconds or 0 for no timeout
+ * @type: Device Discovery type (enum p2p_discovery_type)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used if the driver implements P2P management,
+ * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+ * struct wpa_driver_capa.
+ */
+ int (*p2p_find)(void *priv, unsigned int timeout, int type);
+
+ /**
+ * p2p_stop_find - Stop P2P Device Discovery
+ * @priv: Private driver interface data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used if the driver implements P2P management,
+ * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+ * struct wpa_driver_capa.
+ */
+ int (*p2p_stop_find)(void *priv);
+
+ /**
+ * p2p_listen - Start P2P Listen state for specified duration
+ * @priv: Private driver interface data
+ * @timeout: Listen state duration in milliseconds
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to request the P2P module to keep the
+ * device discoverable on the listen channel for an extended set of
+ * time. At least in its current form, this is mainly used for testing
+ * purposes and may not be of much use for normal P2P operations.
+ *
+ * This function is only used if the driver implements P2P management,
+ * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+ * struct wpa_driver_capa.
+ */
+ int (*p2p_listen)(void *priv, unsigned int timeout);
+
+ /**
+ * p2p_connect - Start P2P group formation (GO negotiation)
+ * @priv: Private driver interface data
+ * @peer_addr: MAC address of the peer P2P client
+ * @wps_method: enum p2p_wps_method value indicating config method
+ * @go_intent: Local GO intent value (1..15)
+ * @own_interface_addr: Intended interface address to use with the
+ * group
+ * @force_freq: The only allowed channel frequency in MHz or 0
+ * @persistent_group: Whether to create persistent group
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used if the driver implements P2P management,
+ * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+ * struct wpa_driver_capa.
+ */
+ int (*p2p_connect)(void *priv, const u8 *peer_addr, int wps_method,
+ int go_intent, const u8 *own_interface_addr,
+ unsigned int force_freq, int persistent_group);
+
+ /**
+ * wps_success_cb - Report successfully completed WPS provisioning
+ * @priv: Private driver interface data
+ * @peer_addr: Peer address
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to report successfully completed WPS
+ * provisioning during group formation in both GO/Registrar and
+ * client/Enrollee roles.
+ *
+ * This function is only used if the driver implements P2P management,
+ * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+ * struct wpa_driver_capa.
+ */
+ int (*wps_success_cb)(void *priv, const u8 *peer_addr);
+
+ /**
+ * p2p_group_formation_failed - Report failed WPS provisioning
+ * @priv: Private driver interface data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to report failed group formation. This can
+ * happen either due to failed WPS provisioning or due to 15 second
+ * timeout during the provisioning phase.
+ *
+ * This function is only used if the driver implements P2P management,
+ * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+ * struct wpa_driver_capa.
+ */
+ int (*p2p_group_formation_failed)(void *priv);
+
+ /**
+ * p2p_set_params - Set P2P parameters
+ * @priv: Private driver interface data
+ * @params: P2P parameters
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used if the driver implements P2P management,
+ * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+ * struct wpa_driver_capa.
+ */
+ int (*p2p_set_params)(void *priv, const struct p2p_params *params);
+
+ /**
+ * p2p_prov_disc_req - Send Provision Discovery Request
+ * @priv: Private driver interface data
+ * @peer_addr: MAC address of the peer P2P client
+ * @config_methods: WPS Config Methods value (only one bit set)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to request a discovered P2P peer to
+ * display a PIN (config_methods = WPS_CONFIG_DISPLAY) or be prepared
+ * to enter a PIN from us (config_methods = WPS_CONFIG_KEYPAD). The
+ * Provision Discovery Request frame is transmitted once immediately
+ * and if no response is received, the frame will be sent again
+ * whenever the target device is discovered during device dsicovery
+ * (start with a p2p_find() call). Response from the peer is indicated
+ * with the EVENT_P2P_PROV_DISC_RESPONSE event.
+ *
+ * This function is only used if the driver implements P2P management,
+ * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+ * struct wpa_driver_capa.
+ */
+ int (*p2p_prov_disc_req)(void *priv, const u8 *peer_addr,
+ u16 config_methods);
+
+ /**
+ * p2p_sd_request - Schedule a service discovery query
+ * @priv: Private driver interface data
+ * @dst: Destination peer or %NULL to apply for all peers
+ * @tlvs: P2P Service Query TLV(s)
+ * Returns: Reference to the query or 0 on failure
+ *
+ * Response to the query is indicated with the
+ * EVENT_P2P_SD_RESPONSE driver event.
+ *
+ * This function is only used if the driver implements P2P management,
+ * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+ * struct wpa_driver_capa.
+ */
+ u64 (*p2p_sd_request)(void *priv, const u8 *dst,
+ const struct wpabuf *tlvs);
+
+ /**
+ * p2p_sd_cancel_request - Cancel a pending service discovery query
+ * @priv: Private driver interface data
+ * @req: Query reference from p2p_sd_request()
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used if the driver implements P2P management,
+ * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+ * struct wpa_driver_capa.
+ */
+ int (*p2p_sd_cancel_request)(void *priv, u64 req);
+
+ /**
+ * p2p_sd_response - Send response to a service discovery query
+ * @priv: Private driver interface data
+ * @freq: Frequency from EVENT_P2P_SD_REQUEST event
+ * @dst: Destination address from EVENT_P2P_SD_REQUEST event
+ * @dialog_token: Dialog token from EVENT_P2P_SD_REQUEST event
+ * @resp_tlvs: P2P Service Response TLV(s)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is called as a response to the request indicated with
+ * the EVENT_P2P_SD_REQUEST driver event.
+ *
+ * This function is only used if the driver implements P2P management,
+ * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+ * struct wpa_driver_capa.
+ */
+ int (*p2p_sd_response)(void *priv, int freq, const u8 *dst,
+ u8 dialog_token,
+ const struct wpabuf *resp_tlvs);
+
+ /**
+ * p2p_service_update - Indicate a change in local services
+ * @priv: Private driver interface data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function needs to be called whenever there is a change in
+ * availability of the local services. This will increment the
+ * Service Update Indicator value which will be used in SD Request and
+ * Response frames.
+ *
+ * This function is only used if the driver implements P2P management,
+ * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+ * struct wpa_driver_capa.
+ */
+ int (*p2p_service_update)(void *priv);
+
+ /**
+ * p2p_reject - Reject peer device (explicitly block connections)
+ * @priv: Private driver interface data
+ * @addr: MAC address of the peer
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*p2p_reject)(void *priv, const u8 *addr);
+
+ /**
+ * p2p_invite - Invite a P2P Device into a group
+ * @priv: Private driver interface data
+ * @peer: Device Address of the peer P2P Device
+ * @role: Local role in the group
+ * @bssid: Group BSSID or %NULL if not known
+ * @ssid: Group SSID
+ * @ssid_len: Length of ssid in octets
+ * @go_dev_addr: Forced GO Device Address or %NULL if none
+ * @persistent_group: Whether this is to reinvoke a persistent group
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*p2p_invite)(void *priv, const u8 *peer, int role,
+ const u8 *bssid, const u8 *ssid, size_t ssid_len,
+ const u8 *go_dev_addr, int persistent_group);
+
+ /**
+ * send_tdls_mgmt - for sending TDLS management packets
+ * @priv: private driver interface data
+ * @dst: Destination (peer) MAC address
+ * @action_code: TDLS action code for the mssage
+ * @dialog_token: Dialog Token to use in the message (if needed)
+ * @status_code: Status Code or Reason Code to use (if needed)
+ * @buf: TDLS IEs to add to the message
+ * @len: Length of buf in octets
+ * Returns: 0 on success, -1 on failure
+ *
+ * This optional function can be used to send packet to driver which is
+ * responsible for receiving and sending all TDLS packets.
+ */
+ int (*send_tdls_mgmt)(void *priv, const u8 *dst, u8 action_code,
+ u8 dialog_token, u16 status_code,
+ const u8 *buf, size_t len);
+
+ int (*tdls_oper)(void *priv, enum tdls_oper oper, const u8 *peer);
+
+ /**
+ * signal_poll - Get current connection information
+ * @priv: Private driver interface data
+ * @signal_info: Connection info structure
+ */
+ int (*signal_poll)(void *priv, struct wpa_signal_info *signal_info);
+};
+
+
+/**
+ * enum wpa_event_type - Event type for wpa_supplicant_event() calls
+ */
+enum wpa_event_type {
+ /**
+ * EVENT_ASSOC - Association completed
+ *
+ * This event needs to be delivered when the driver completes IEEE
+ * 802.11 association or reassociation successfully.
+ * wpa_driver_ops::get_bssid() is expected to provide the current BSSID
+ * after this event has been generated. In addition, optional
+ * EVENT_ASSOCINFO may be generated just before EVENT_ASSOC to provide
+ * more information about the association. If the driver interface gets
+ * both of these events at the same time, it can also include the
+ * assoc_info data in EVENT_ASSOC call.
+ */
+ EVENT_ASSOC,
+
+ /**
+ * EVENT_DISASSOC - Association lost
+ *
+ * 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. 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,
+
+ /**
+ * EVENT_MICHAEL_MIC_FAILURE - Michael MIC (TKIP) detected
+ *
+ * This event must be delivered when a Michael MIC error is detected by
+ * the local driver. Additional data for event processing is
+ * provided with union wpa_event_data::michael_mic_failure. This
+ * information is used to request new encyption key and to initiate
+ * TKIP countermeasures if needed.
+ */
+ EVENT_MICHAEL_MIC_FAILURE,
+
+ /**
+ * EVENT_SCAN_RESULTS - Scan results available
+ *
+ * This event must be called whenever scan results are available to be
+ * fetched with struct wpa_driver_ops::get_scan_results(). This event
+ * is expected to be used some time after struct wpa_driver_ops::scan()
+ * is called. If the driver provides an unsolicited event when the scan
+ * has been completed, this event can be used to trigger
+ * 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. Optional information about
+ * completed scan can be provided with union wpa_event_data::scan_info.
+ */
+ EVENT_SCAN_RESULTS,
+
+ /**
+ * EVENT_ASSOCINFO - Report optional extra information for association
+ *
+ * This event can be used to report extra association information for
+ * EVENT_ASSOC processing. This extra information includes IEs from
+ * association frames and Beacon/Probe Response frames in union
+ * wpa_event_data::assoc_info. EVENT_ASSOCINFO must be send just before
+ * EVENT_ASSOC. Alternatively, the driver interface can include
+ * assoc_info data in the EVENT_ASSOC call if it has all the
+ * information available at the same point.
+ */
+ EVENT_ASSOCINFO,
+
+ /**
+ * EVENT_INTERFACE_STATUS - Report interface status changes
+ *
+ * This optional event can be used to report changes in interface
+ * status (interface added/removed) using union
+ * wpa_event_data::interface_status. This can be used to trigger
+ * wpa_supplicant to stop and re-start processing for the interface,
+ * e.g., when a cardbus card is ejected/inserted.
+ */
+ EVENT_INTERFACE_STATUS,
+
+ /**
+ * EVENT_PMKID_CANDIDATE - Report a candidate AP for pre-authentication
+ *
+ * This event can be used to inform wpa_supplicant about candidates for
+ * RSN (WPA2) pre-authentication. If wpa_supplicant is not responsible
+ * for scan request (ap_scan=2 mode), this event is required for
+ * pre-authentication. If wpa_supplicant is performing scan request
+ * (ap_scan=1), this event is optional since scan results can be used
+ * to add pre-authentication candidates. union
+ * wpa_event_data::pmkid_candidate is used to report the BSSID of the
+ * candidate and priority of the candidate, e.g., based on the signal
+ * strength, in order to try to pre-authenticate first with candidates
+ * that are most likely targets for re-association.
+ *
+ * EVENT_PMKID_CANDIDATE can be called whenever the driver has updates
+ * on the candidate list. In addition, it can be called for the current
+ * AP and APs that have existing PMKSA cache entries. wpa_supplicant
+ * will automatically skip pre-authentication in cases where a valid
+ * PMKSA exists. When more than one candidate exists, this event should
+ * be generated once for each candidate.
+ *
+ * Driver will be notified about successful pre-authentication with
+ * struct wpa_driver_ops::add_pmkid() calls.
+ */
+ EVENT_PMKID_CANDIDATE,
+
+ /**
+ * EVENT_STKSTART - Request STK handshake (MLME-STKSTART.request)
+ *
+ * This event can be used to inform wpa_supplicant about desire to set
+ * up secure direct link connection between two stations as defined in
+ * IEEE 802.11e with a new PeerKey mechanism that replaced the original
+ * STAKey negotiation. The caller will need to set peer address for the
+ * event.
+ */
+ EVENT_STKSTART,
+
+ /**
+ * EVENT_TDLS - Request TDLS operation
+ *
+ * This event can be used to request a TDLS operation to be performed.
+ */
+ EVENT_TDLS,
+
+ /**
+ * EVENT_FT_RESPONSE - Report FT (IEEE 802.11r) response IEs
+ *
+ * The driver is expected to report the received FT IEs from
+ * 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,
+
+ /**
+ * 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 the association response 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,
+
+ /**
+ * EVENT_INTERFACE_ENABLED - Notify that interface was enabled
+ *
+ * This event is used to indicate that the interface was enabled after
+ * having been previously disabled, e.g., due to rfkill.
+ */
+ EVENT_INTERFACE_ENABLED,
+
+ /**
+ * EVENT_INTERFACE_DISABLED - Notify that interface was disabled
+ *
+ * This event is used to indicate that the interface was disabled,
+ * e.g., due to rfkill.
+ */
+ EVENT_INTERFACE_DISABLED,
+
+ /**
+ * EVENT_CHANNEL_LIST_CHANGED - Channel list changed
+ *
+ * This event is used to indicate that the channel list has changed,
+ * e.g., because of a regulatory domain change triggered by scan
+ * results including an AP advertising a country code.
+ */
+ EVENT_CHANNEL_LIST_CHANGED,
+
+ /**
+ * EVENT_INTERFACE_UNAVAILABLE - Notify that interface is unavailable
+ *
+ * This event is used to indicate that the driver cannot maintain this
+ * interface in its operation mode anymore. The most likely use for
+ * this is to indicate that AP mode operation is not available due to
+ * operating channel would need to be changed to a DFS channel when
+ * the driver does not support radar detection and another virtual
+ * interfaces caused the operating channel to change. Other similar
+ * resource conflicts could also trigger this for station mode
+ * interfaces.
+ */
+ EVENT_INTERFACE_UNAVAILABLE,
+
+ /**
+ * EVENT_BEST_CHANNEL
+ *
+ * Driver generates this event whenever it detects a better channel
+ * (e.g., based on RSSI or channel use). This information can be used
+ * to improve channel selection for a new AP/P2P group.
+ */
+ EVENT_BEST_CHANNEL,
+
+ /**
+ * EVENT_UNPROT_DEAUTH - Unprotected Deauthentication frame received
+ *
+ * This event should be called when a Deauthentication frame is dropped
+ * due to it not being protected (MFP/IEEE 802.11w).
+ * union wpa_event_data::unprot_deauth is required to provide more
+ * details of the frame.
+ */
+ EVENT_UNPROT_DEAUTH,
+
+ /**
+ * EVENT_UNPROT_DISASSOC - Unprotected Disassociation frame received
+ *
+ * This event should be called when a Disassociation frame is dropped
+ * due to it not being protected (MFP/IEEE 802.11w).
+ * union wpa_event_data::unprot_disassoc is required to provide more
+ * details of the frame.
+ */
+ EVENT_UNPROT_DISASSOC,
+
+ /**
+ * EVENT_STATION_LOW_ACK
+ *
+ * Driver generates this event whenever it detected that a particular
+ * station was lost. Detection can be through massive transmission
+ * failures for example.
+ */
+ EVENT_STATION_LOW_ACK,
+
+ /**
+ * EVENT_P2P_DEV_FOUND - Report a discovered P2P device
+ *
+ * This event is used only if the driver implements P2P management
+ * internally. Event data is stored in
+ * union wpa_event_data::p2p_dev_found.
+ */
+ EVENT_P2P_DEV_FOUND,
+
+ /**
+ * EVENT_P2P_GO_NEG_REQ_RX - Report reception of GO Negotiation Request
+ *
+ * This event is used only if the driver implements P2P management
+ * internally. Event data is stored in
+ * union wpa_event_data::p2p_go_neg_req_rx.
+ */
+ EVENT_P2P_GO_NEG_REQ_RX,
+
+ /**
+ * EVENT_P2P_GO_NEG_COMPLETED - Report completion of GO Negotiation
+ *
+ * This event is used only if the driver implements P2P management
+ * internally. Event data is stored in
+ * union wpa_event_data::p2p_go_neg_completed.
+ */
+ EVENT_P2P_GO_NEG_COMPLETED,
+
+ EVENT_P2P_PROV_DISC_REQUEST,
+ EVENT_P2P_PROV_DISC_RESPONSE,
+ EVENT_P2P_SD_REQUEST,
+ EVENT_P2P_SD_RESPONSE,
+
+ /**
+ * EVENT_IBSS_PEER_LOST - IBSS peer not reachable anymore
+ */
+ EVENT_IBSS_PEER_LOST
+};
+
+
+/**
+ * union wpa_event_data - Additional data for wpa_supplicant_event() calls
+ */
+union wpa_event_data {
+ /**
+ * struct assoc_info - Data for EVENT_ASSOC and EVENT_ASSOCINFO events
+ *
+ * This structure is optional for EVENT_ASSOC calls and required for
+ * EVENT_ASSOCINFO calls. By using EVENT_ASSOC with this data, the
+ * driver interface does not need to generate separate EVENT_ASSOCINFO
+ * calls.
+ */
+ struct assoc_info {
+ /**
+ * reassoc - Flag to indicate association or reassociation
+ */
+ int reassoc;
+
+ /**
+ * req_ies - (Re)Association Request IEs
+ *
+ * If the driver generates WPA/RSN IE, this event data must be
+ * returned for WPA handshake to have needed information. If
+ * wpa_supplicant-generated WPA/RSN IE is used, this
+ * information event is optional.
+ *
+ * This should start with the first IE (fixed fields before IEs
+ * are not included).
+ */
+ const u8 *req_ies;
+
+ /**
+ * req_ies_len - Length of req_ies in bytes
+ */
+ size_t req_ies_len;
+
+ /**
+ * 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).
+ */
+ const u8 *resp_ies;
+
+ /**
+ * resp_ies_len - Length of resp_ies in bytes
+ */
+ size_t resp_ies_len;
+
+ /**
+ * beacon_ies - Beacon or Probe Response IEs
+ *
+ * Optional Beacon/ProbeResp data: IEs included in Beacon or
+ * Probe Response frames from the current AP (i.e., the one
+ * that the client just associated with). This information is
+ * used to update WPA/RSN IE for the AP. If this field is not
+ * set, the results from previous scan will be used. If no
+ * data for the new AP is found, scan results will be requested
+ * again (without scan request). At this point, the driver is
+ * expected to provide WPA/RSN IE for the AP (if WPA/WPA2 is
+ * used).
+ *
+ * This should start with the first IE (fixed fields before IEs
+ * are not included).
+ */
+ 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;
+
+ /**
+ * ie - Optional IE(s) in Disassociation frame
+ */
+ const u8 *ie;
+
+ /**
+ * ie_len - Length of ie buffer in octets
+ */
+ size_t ie_len;
+ } 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;
+
+ /**
+ * ie - Optional IE(s) in Deauthentication frame
+ */
+ const u8 *ie;
+
+ /**
+ * ie_len - Length of ie buffer in octets
+ */
+ size_t ie_len;
+ } deauth_info;
+
+ /**
+ * struct michael_mic_failure - Data for EVENT_MICHAEL_MIC_FAILURE
+ */
+ struct michael_mic_failure {
+ int unicast;
+ const u8 *src;
+ } michael_mic_failure;
+
+ /**
+ * struct interface_status - Data for EVENT_INTERFACE_STATUS
+ */
+ struct interface_status {
+ char ifname[100];
+ enum {
+ EVENT_INTERFACE_ADDED, EVENT_INTERFACE_REMOVED
+ } ievent;
+ } interface_status;
+
+ /**
+ * struct pmkid_candidate - Data for EVENT_PMKID_CANDIDATE
+ */
+ struct pmkid_candidate {
+ /** BSSID of the PMKID candidate */
+ u8 bssid[ETH_ALEN];
+ /** Smaller the index, higher the priority */
+ int index;
+ /** Whether RSN IE includes pre-authenticate flag */
+ int preauth;
+ } pmkid_candidate;
+
+ /**
+ * struct stkstart - Data for EVENT_STKSTART
+ */
+ struct stkstart {
+ u8 peer[ETH_ALEN];
+ } stkstart;
+
+ /**
+ * struct tdls - Data for EVENT_TDLS
+ */
+ struct tdls {
+ u8 peer[ETH_ALEN];
+ enum {
+ TDLS_REQUEST_SETUP,
+ TDLS_REQUEST_TEARDOWN
+ } oper;
+ u16 reason_code; /* for teardown */
+ } tdls;
+
+ /**
+ * struct ft_ies - FT information elements (EVENT_FT_RESPONSE)
+ *
+ * During FT (IEEE 802.11r) authentication sequence, the driver is
+ * expected to use this event to report received FT IEs (MDIE, FTIE,
+ * RSN IE, TIE, possible resource request) to the supplicant. The FT
+ * IEs for the next message will be delivered through the
+ * struct wpa_driver_ops::update_ft_ies() callback.
+ */
+ struct ft_ies {
+ const u8 *ies;
+ 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 {
+ /**
+ * bssid - BSSID of the AP that rejected association
+ */
+ const u8 *bssid;
+
+ /**
+ * 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).
+ */
+ const 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;
+
+ /**
+ * signal_change - Data for EVENT_SIGNAL_CHANGE events
+ */
+ struct wpa_signal_info signal_change;
+
+ /**
+ * struct best_channel - Data for EVENT_BEST_CHANNEL events
+ * @freq_24: Best 2.4 GHz band channel frequency in MHz
+ * @freq_5: Best 5 GHz band channel frequency in MHz
+ * @freq_overall: Best channel frequency in MHz
+ *
+ * 0 can be used to indicate no preference in either band.
+ */
+ struct best_channel {
+ int freq_24;
+ int freq_5;
+ int freq_overall;
+ } best_chan;
+
+ struct unprot_deauth {
+ const u8 *sa;
+ const u8 *da;
+ u16 reason_code;
+ } unprot_deauth;
+
+ struct unprot_disassoc {
+ const u8 *sa;
+ const u8 *da;
+ u16 reason_code;
+ } unprot_disassoc;
+
+ /**
+ * struct low_ack - Data for EVENT_STATION_LOW_ACK events
+ * @addr: station address
+ */
+ struct low_ack {
+ u8 addr[ETH_ALEN];
+ } low_ack;
+
+ /**
+ * struct p2p_dev_found - Data for EVENT_P2P_DEV_FOUND
+ */
+ struct p2p_dev_found {
+ const u8 *addr;
+ const u8 *dev_addr;
+ const u8 *pri_dev_type;
+ const char *dev_name;
+ u16 config_methods;
+ u8 dev_capab;
+ u8 group_capab;
+ } p2p_dev_found;
+
+ /**
+ * struct p2p_go_neg_req_rx - Data for EVENT_P2P_GO_NEG_REQ_RX
+ */
+ struct p2p_go_neg_req_rx {
+ const u8 *src;
+ u16 dev_passwd_id;
+ } p2p_go_neg_req_rx;
+
+ /**
+ * struct p2p_go_neg_completed - Data for EVENT_P2P_GO_NEG_COMPLETED
+ */
+ struct p2p_go_neg_completed {
+ struct p2p_go_neg_results *res;
+ } p2p_go_neg_completed;
+
+ struct p2p_prov_disc_req {
+ const u8 *peer;
+ u16 config_methods;
+ const u8 *dev_addr;
+ const u8 *pri_dev_type;
+ const char *dev_name;
+ u16 supp_config_methods;
+ u8 dev_capab;
+ u8 group_capab;
+ } p2p_prov_disc_req;
+
+ struct p2p_prov_disc_resp {
+ const u8 *peer;
+ u16 config_methods;
+ } p2p_prov_disc_resp;
+
+ struct p2p_sd_req {
+ int freq;
+ const u8 *sa;
+ u8 dialog_token;
+ u16 update_indic;
+ const u8 *tlvs;
+ size_t tlvs_len;
+ } p2p_sd_req;
+
+ struct p2p_sd_resp {
+ const u8 *sa;
+ u16 update_indic;
+ const u8 *tlvs;
+ size_t tlvs_len;
+ } p2p_sd_resp;
+
+ /**
+ * struct ibss_peer_lost - Data for EVENT_IBSS_PEER_LOST
+ */
+ struct ibss_peer_lost {
+ u8 peer[ETH_ALEN];
+ } ibss_peer_lost;
+};
+
+/**
+ * wpa_supplicant_event - Report a driver event for wpa_supplicant
+ * @ctx: Context pointer (wpa_s); this is the ctx variable registered
+ * with struct wpa_driver_ops::init()
+ * @event: event type (defined above)
+ * @data: possible extra data for the event
+ *
+ * Driver wrapper code should call this function whenever an event is received
+ * from the driver.
+ */
+void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+ union wpa_event_data *data);
+
+
+/*
+ * The following inline functions are provided for convenience to simplify
+ * event indication for some of the common events.
+ */
+
+static inline void drv_event_assoc(void *ctx, const u8 *addr, const u8 *ie,
+ size_t ielen, int reassoc)
+{
+ union wpa_event_data event;
+ os_memset(&event, 0, sizeof(event));
+ event.assoc_info.reassoc = reassoc;
+ 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);
+}
+
+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 0000000..6ac1cea
--- /dev/null
+++ b/src/drivers/driver_atheros.c
@@ -0,0 +1,1381 @@
+/*
+ * 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 atheros_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 */
+ struct wpabuf *wpa_ie;
+ struct wpabuf *wps_beacon_ie;
+ struct wpabuf *wps_probe_resp_ie;
+};
+
+static int atheros_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
+ int reason_code);
+static int atheros_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_SET_MEDENYENTRY:
+ return "SET_MEDENYENTRY";
+ 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 atheros_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 atheros_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
+atheros_configure_wpa(struct atheros_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);
+#ifdef CONFIG_IEEE80211W
+ if (params->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+ v |= BIT(7);
+ if (params->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED)
+ v |= BIT(6);
+ }
+#endif /* CONFIG_IEEE80211W */
+
+ 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
+atheros_set_ieee8021x(void *priv, struct wpa_bss_params *params)
+{
+ struct atheros_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 atheros_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 && atheros_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
+atheros_set_privacy(void *priv, int enabled)
+{
+ struct atheros_driver_data *drv = priv;
+
+ wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
+
+ return set80211param(drv, IEEE80211_PARAM_PRIVACY, enabled);
+}
+
+static int
+atheros_set_sta_authorized(void *priv, const u8 *addr, int authorized)
+{
+ struct atheros_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
+atheros_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 atheros_set_sta_authorized(priv, addr, 1);
+ if (!(flags_and & WPA_STA_AUTHORIZED))
+ return atheros_set_sta_authorized(priv, addr, 0);
+ return 0;
+}
+
+static int
+atheros_del_key(void *priv, const u8 *addr, int key_idx)
+{
+ struct atheros_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
+atheros_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 atheros_driver_data *drv = priv;
+ struct ieee80211req_key wk;
+ u_int8_t cipher;
+ int ret;
+
+ if (alg == WPA_ALG_NONE)
+ return atheros_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;
+#ifdef CONFIG_IEEE80211W
+ case WPA_ALG_IGTK:
+ cipher = IEEE80211_CIPHER_AES_CMAC;
+ break;
+#endif /* CONFIG_IEEE80211W */
+ 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 || is_broadcast_ether_addr(addr)) {
+ memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN);
+ wk.ik_keyix = key_idx;
+ if (set_tx)
+ 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
+atheros_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx,
+ u8 *seq)
+{
+ struct atheros_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
+atheros_flush(void *priv)
+{
+ u8 allsta[IEEE80211_ADDR_LEN];
+ memset(allsta, 0xff, IEEE80211_ADDR_LEN);
+ return atheros_sta_deauth(priv, NULL, allsta,
+ IEEE80211_REASON_AUTH_LEAVE);
+}
+
+
+static int
+atheros_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data,
+ const u8 *addr)
+{
+ struct atheros_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
+atheros_sta_clear_stats(void *priv, const u8 *addr)
+{
+ struct atheros_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
+atheros_set_opt_ie(void *priv, const u8 *ie, size_t ie_len)
+{
+ struct atheros_driver_data *drv = priv;
+ u8 buf[512];
+ struct ieee80211req_getset_appiebuf *app_ie;
+
+ wpa_printf(MSG_DEBUG, "%s buflen = %lu", __func__,
+ (unsigned long) ie_len);
+
+ wpabuf_free(drv->wpa_ie);
+ drv->wpa_ie = wpabuf_alloc_copy(ie, ie_len);
+
+ app_ie = (struct ieee80211req_getset_appiebuf *) buf;
+ os_memcpy(&(app_ie->app_buf[0]), ie, ie_len);
+ app_ie->app_buflen = ie_len;
+
+ app_ie->app_frmtype = IEEE80211_APPIE_FRAME_BEACON;
+
+ /* append WPS IE for Beacon */
+ if (drv->wps_beacon_ie != NULL) {
+ os_memcpy(&(app_ie->app_buf[ie_len]),
+ wpabuf_head(drv->wps_beacon_ie),
+ wpabuf_len(drv->wps_beacon_ie));
+ app_ie->app_buflen = ie_len + wpabuf_len(drv->wps_beacon_ie);
+ }
+ set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, app_ie,
+ sizeof(struct ieee80211req_getset_appiebuf) +
+ app_ie->app_buflen);
+
+ /* append WPS IE for Probe Response */
+ app_ie->app_frmtype = IEEE80211_APPIE_FRAME_PROBE_RESP;
+ if (drv->wps_probe_resp_ie != NULL) {
+ os_memcpy(&(app_ie->app_buf[ie_len]),
+ wpabuf_head(drv->wps_probe_resp_ie),
+ wpabuf_len(drv->wps_probe_resp_ie));
+ app_ie->app_buflen = ie_len +
+ wpabuf_len(drv->wps_probe_resp_ie);
+ } else
+ app_ie->app_buflen = ie_len;
+ set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, app_ie,
+ sizeof(struct ieee80211req_getset_appiebuf) +
+ app_ie->app_buflen);
+ return 0;
+}
+
+static int
+atheros_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
+ int reason_code)
+{
+ struct atheros_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
+atheros_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
+ int reason_code)
+{
+ struct atheros_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 atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf,
+ size_t len)
+{
+ struct atheros_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 atheros_receive_probe_req(struct atheros_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,
+ atheros_raw_receive, drv, 1);
+ if (drv->sock_raw == NULL)
+ return -1;
+#endif /* CONFIG_WPS */
+ return ret;
+}
+
+#ifdef CONFIG_WPS
+static int
+atheros_set_wps_ie(void *priv, const u8 *ie, size_t len, u32 frametype)
+{
+ struct atheros_driver_data *drv = priv;
+ u8 buf[512];
+ 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;
+ os_memcpy(&(beac_ie->app_buf[0]), ie, len);
+
+ /* append the WPA/RSN IE if it is set already */
+ if (((frametype == IEEE80211_APPIE_FRAME_BEACON) ||
+ (frametype == IEEE80211_APPIE_FRAME_PROBE_RESP)) &&
+ (drv->wpa_ie != NULL)) {
+ os_memcpy(&(beac_ie->app_buf[len]), wpabuf_head(drv->wpa_ie),
+ wpabuf_len(drv->wpa_ie));
+ beac_ie->app_buflen += wpabuf_len(drv->wpa_ie);
+ }
+
+ return set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, beac_ie,
+ sizeof(struct ieee80211req_getset_appiebuf) +
+ beac_ie->app_buflen);
+}
+
+static int
+atheros_set_ap_wps_ie(void *priv, const struct wpabuf *beacon,
+ const struct wpabuf *proberesp,
+ const struct wpabuf *assocresp)
+{
+ struct atheros_driver_data *drv = priv;
+
+ wpabuf_free(drv->wps_beacon_ie);
+ drv->wps_beacon_ie = beacon ? wpabuf_dup(beacon) : NULL;
+ wpabuf_free(drv->wps_probe_resp_ie);
+ drv->wps_probe_resp_ie = proberesp ? wpabuf_dup(proberesp) : NULL;
+
+ atheros_set_wps_ie(priv, assocresp ? wpabuf_head(assocresp) : NULL,
+ assocresp ? wpabuf_len(assocresp) : 0,
+ IEEE80211_APPIE_FRAME_ASSOC_RESP);
+ if (atheros_set_wps_ie(priv, beacon ? wpabuf_head(beacon) : NULL,
+ beacon ? wpabuf_len(beacon) : 0,
+ IEEE80211_APPIE_FRAME_BEACON))
+ return -1;
+ return atheros_set_wps_ie(priv,
+ proberesp ? wpabuf_head(proberesp) : NULL,
+ proberesp ? wpabuf_len(proberesp): 0,
+ IEEE80211_APPIE_FRAME_PROBE_RESP);
+}
+#else /* CONFIG_WPS */
+#define atheros_set_ap_wps_ie NULL
+#endif /* CONFIG_WPS */
+
+static void
+atheros_new_sta(struct atheros_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, "atheros req WPA IE",
+ ie.wpa_ie, IEEE80211_MAX_OPT_IE);
+ wpa_hexdump(MSG_MSGDUMP, "atheros req RSN IE",
+ ie.rsn_ie, IEEE80211_MAX_OPT_IE);
+#ifdef ATH_WPS_IE
+ wpa_hexdump(MSG_MSGDUMP, "atheros req WPS IE",
+ ie.wps_ie, IEEE80211_MAX_OPT_IE);
+#endif /* ATH_WPS_IE */
+ iebuf = ie.wpa_ie;
+ /* atheros 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) {
+ /* atheros-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];
+
+#ifdef ATH_WPS_IE
+ /* if WPS IE is present, preference is given to WPS */
+ if (ie.wps_ie &&
+ (ie.wps_ie[1] > 0 && (ie.wps_ie[0] == WLAN_EID_VENDOR_SPECIFIC))) {
+ iebuf = ie.wps_ie;
+ ielen = ie.wps_ie[1];
+ }
+#endif /* ATH_WPS_IE */
+
+ if (ielen == 0)
+ iebuf = NULL;
+ else
+ ielen += 2;
+
+no_ie:
+ drv_event_assoc(hapd, addr, iebuf, ielen, 0);
+
+ 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
+atheros_wireless_event_wireless_custom(struct atheros_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;
+ }
+ atheros_raw_receive(drv, NULL,
+ (u8 *) custom + WPS_FRAM_TAG_SIZE, len);
+#endif /* CONFIG_WPS */
+ }
+}
+
+static void
+atheros_wireless_event_wireless(struct atheros_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:
+ atheros_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';
+ atheros_wireless_event_wireless_custom(
+ drv, buf, buf + iwe->u.data.length);
+ free(buf);
+ break;
+ }
+
+ pos += iwe->len;
+ }
+}
+
+
+static void
+atheros_wireless_event_rtm_newlink(void *ctx,
+ struct ifinfomsg *ifi, u8 *buf, size_t len)
+{
+ struct atheros_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) {
+ atheros_wireless_event_wireless(
+ drv, ((char *) attr) + rta_len,
+ attr->rta_len - rta_len);
+ }
+ attr = RTA_NEXT(attr, attrlen);
+ }
+}
+
+
+static int
+atheros_get_we_version(struct atheros_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
+atheros_wireless_event_init(struct atheros_driver_data *drv)
+{
+ struct netlink_config *cfg;
+
+ atheros_get_we_version(drv);
+
+ cfg = os_zalloc(sizeof(*cfg));
+ if (cfg == NULL)
+ return -1;
+ cfg->ctx = drv;
+ cfg->newlink_cb = atheros_wireless_event_rtm_newlink;
+ drv->netlink = netlink_init(cfg);
+ if (drv->netlink == NULL) {
+ os_free(cfg);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int
+atheros_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len,
+ int encrypt, const u8 *own_addr, u32 flags)
+{
+ struct atheros_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 atheros_driver_data *drv = ctx;
+ drv_event_eapol_rx(drv->hapd, src_addr, buf + sizeof(struct l2_ethhdr),
+ len - sizeof(struct l2_ethhdr));
+}
+
+static void *
+atheros_init(struct hostapd_data *hapd, struct wpa_init_params *params)
+{
+ struct atheros_driver_data *drv;
+ struct ifreq ifr;
+ struct iwreq iwr;
+ char brname[IFNAMSIZ];
+
+ drv = os_zalloc(sizeof(struct atheros_driver_data));
+ if (drv == NULL) {
+ printf("Could not allocate memory for atheros 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);
+ atheros_set_privacy(drv, 0); /* default to no privacy */
+
+ atheros_receive_probe_req(drv);
+
+ if (atheros_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
+atheros_deinit(void *priv)
+{
+ struct atheros_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);
+ wpabuf_free(drv->wpa_ie);
+ wpabuf_free(drv->wps_beacon_ie);
+ wpabuf_free(drv->wps_probe_resp_ie);
+ free(drv);
+}
+
+static int
+atheros_set_ssid(void *priv, const u8 *buf, int len)
+{
+ struct atheros_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
+atheros_get_ssid(void *priv, u8 *buf, int len)
+{
+ struct atheros_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
+atheros_set_countermeasures(void *priv, int enabled)
+{
+ struct atheros_driver_data *drv = priv;
+ wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled);
+ return set80211param(drv, IEEE80211_PARAM_COUNTERMEASURES, enabled);
+}
+
+static int
+atheros_commit(void *priv)
+{
+ struct atheros_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 = atheros_init,
+ .hapd_deinit = atheros_deinit,
+ .set_ieee8021x = atheros_set_ieee8021x,
+ .set_privacy = atheros_set_privacy,
+ .set_key = atheros_set_key,
+ .get_seqnum = atheros_get_seqnum,
+ .flush = atheros_flush,
+ .set_generic_elem = atheros_set_opt_ie,
+ .sta_set_flags = atheros_sta_set_flags,
+ .read_sta_data = atheros_read_sta_driver_data,
+ .hapd_send_eapol = atheros_send_eapol,
+ .sta_disassoc = atheros_sta_disassoc,
+ .sta_deauth = atheros_sta_deauth,
+ .hapd_set_ssid = atheros_set_ssid,
+ .hapd_get_ssid = atheros_get_ssid,
+ .set_countermeasures = atheros_set_countermeasures,
+ .sta_clear_stats = atheros_sta_clear_stats,
+ .commit = atheros_commit,
+ .set_ap_wps_ie = atheros_set_ap_wps_ie,
+};
diff --git a/src/drivers/driver_broadcom.c b/src/drivers/driver_broadcom.c
new file mode 100644
index 0000000..cb88543
--- /dev/null
+++ b/src/drivers/driver_broadcom.c
@@ -0,0 +1,599 @@
+/*
+ * WPA Supplicant - driver interaction with old Broadcom wl.o driver
+ * Copyright (c) 2004, Nikki Chumkov <nikki@gattaca.ru>
+ * Copyright (c) 2004, 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.
+ *
+ * Please note that the newer Broadcom driver ("hybrid Linux driver") supports
+ * Linux wireless extensions and does not need (or even work) with this old
+ * driver wrapper. Use driver_wext.c with that driver.
+ */
+
+#include "includes.h"
+
+#include <sys/ioctl.h>
+
+#include "common.h"
+
+#if 0
+#include <netpacket/packet.h>
+#include <net/ethernet.h> /* the L2 protocols */
+#else
+#include <linux/if_packet.h>
+#include <linux/if_ether.h> /* The L2 protocols */
+#endif
+#include <net/if.h>
+#include <typedefs.h>
+
+/* wlioctl.h is a Broadcom header file and it is available, e.g., from Linksys
+ * WRT54G GPL tarball. */
+#include <wlioctl.h>
+
+#include "driver.h"
+#include "eloop.h"
+
+struct wpa_driver_broadcom_data {
+ void *ctx;
+ int ioctl_sock;
+ int event_sock;
+ char ifname[IFNAMSIZ + 1];
+};
+
+
+#ifndef WLC_DEAUTHENTICATE
+#define WLC_DEAUTHENTICATE 143
+#endif
+#ifndef WLC_DEAUTHENTICATE_WITH_REASON
+#define WLC_DEAUTHENTICATE_WITH_REASON 201
+#endif
+#ifndef WLC_SET_TKIP_COUNTERMEASURES
+#define WLC_SET_TKIP_COUNTERMEASURES 202
+#endif
+
+#if !defined(PSK_ENABLED) /* NEW driver interface */
+#define WL_VERSION 360130
+/* wireless authentication bit vector */
+#define WPA_ENABLED 1
+#define PSK_ENABLED 2
+
+#define WAUTH_WPA_ENABLED(wauth) ((wauth) & WPA_ENABLED)
+#define WAUTH_PSK_ENABLED(wauth) ((wauth) & PSK_ENABLED)
+#define WAUTH_ENABLED(wauth) ((wauth) & (WPA_ENABLED | PSK_ENABLED))
+
+#define WSEC_PRIMARY_KEY WL_PRIMARY_KEY
+
+typedef wl_wsec_key_t wsec_key_t;
+#endif
+
+typedef struct {
+ uint32 val;
+ struct ether_addr ea;
+ uint16 res;
+} wlc_deauth_t;
+
+
+static void wpa_driver_broadcom_scan_timeout(void *eloop_ctx,
+ void *timeout_ctx);
+
+static int broadcom_ioctl(struct wpa_driver_broadcom_data *drv, int cmd,
+ void *buf, int len)
+{
+ struct ifreq ifr;
+ wl_ioctl_t ioc;
+ int ret = 0;
+
+ wpa_printf(MSG_MSGDUMP, "BROADCOM: wlioctl(%s,%d,len=%d,val=%p)",
+ drv->ifname, cmd, len, buf);
+ /* wpa_hexdump(MSG_MSGDUMP, "BROADCOM: wlioctl buf", buf, len); */
+
+ ioc.cmd = cmd;
+ ioc.buf = buf;
+ ioc.len = len;
+ os_strlcpy(ifr.ifr_name, drv->ifname, IFNAMSIZ);
+ ifr.ifr_data = (caddr_t) &ioc;
+ if ((ret = ioctl(drv->ioctl_sock, SIOCDEVPRIVATE, &ifr)) < 0) {
+ if (cmd != WLC_GET_MAGIC)
+ perror(ifr.ifr_name);
+ wpa_printf(MSG_MSGDUMP, "BROADCOM: wlioctl cmd=%d res=%d",
+ cmd, ret);
+ }
+
+ return ret;
+}
+
+static int wpa_driver_broadcom_get_bssid(void *priv, u8 *bssid)
+{
+ struct wpa_driver_broadcom_data *drv = priv;
+ if (broadcom_ioctl(drv, WLC_GET_BSSID, bssid, ETH_ALEN) == 0)
+ return 0;
+
+ os_memset(bssid, 0, ETH_ALEN);
+ return -1;
+}
+
+static int wpa_driver_broadcom_get_ssid(void *priv, u8 *ssid)
+{
+ struct wpa_driver_broadcom_data *drv = priv;
+ wlc_ssid_t s;
+
+ if (broadcom_ioctl(drv, WLC_GET_SSID, &s, sizeof(s)) == -1)
+ return -1;
+
+ os_memcpy(ssid, s.SSID, s.SSID_len);
+ return s.SSID_len;
+}
+
+static int wpa_driver_broadcom_set_wpa(void *priv, int enable)
+{
+ struct wpa_driver_broadcom_data *drv = priv;
+ unsigned int wauth, wsec;
+ struct ether_addr ea;
+
+ os_memset(&ea, enable ? 0xff : 0, sizeof(ea));
+ if (broadcom_ioctl(drv, WLC_GET_WPA_AUTH, &wauth, sizeof(wauth)) ==
+ -1 ||
+ broadcom_ioctl(drv, WLC_GET_WSEC, &wsec, sizeof(wsec)) == -1)
+ return -1;
+
+ if (enable) {
+ wauth = PSK_ENABLED;
+ wsec = TKIP_ENABLED;
+ } else {
+ wauth = 255;
+ wsec &= ~(TKIP_ENABLED | AES_ENABLED);
+ }
+
+ if (broadcom_ioctl(drv, WLC_SET_WPA_AUTH, &wauth, sizeof(wauth)) ==
+ -1 ||
+ broadcom_ioctl(drv, WLC_SET_WSEC, &wsec, sizeof(wsec)) == -1)
+ return -1;
+
+ /* FIX: magic number / error handling? */
+ broadcom_ioctl(drv, 122, &ea, sizeof(ea));
+
+ return 0;
+}
+
+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)
+{
+ struct wpa_driver_broadcom_data *drv = priv;
+ int ret;
+ wsec_key_t wkt;
+
+ os_memset(&wkt, 0, sizeof wkt);
+ wpa_printf(MSG_MSGDUMP, "BROADCOM: SET %sKEY[%d] alg=%d",
+ set_tx ? "PRIMARY " : "", key_idx, alg);
+ if (key && key_len > 0)
+ wpa_hexdump_key(MSG_MSGDUMP, "BROADCOM: key", key, key_len);
+
+ switch (alg) {
+ case WPA_ALG_NONE:
+ wkt.algo = CRYPTO_ALGO_OFF;
+ break;
+ case WPA_ALG_WEP:
+ wkt.algo = CRYPTO_ALGO_WEP128; /* CRYPTO_ALGO_WEP1? */
+ break;
+ case WPA_ALG_TKIP:
+ wkt.algo = 0; /* CRYPTO_ALGO_TKIP? */
+ break;
+ case WPA_ALG_CCMP:
+ wkt.algo = 0; /* CRYPTO_ALGO_AES_CCM;
+ * AES_OCB_MSDU, AES_OCB_MPDU? */
+ break;
+ default:
+ wkt.algo = CRYPTO_ALGO_NALG;
+ break;
+ }
+
+ if (seq && seq_len > 0)
+ wpa_hexdump(MSG_MSGDUMP, "BROADCOM: SEQ", seq, seq_len);
+
+ if (addr)
+ wpa_hexdump(MSG_MSGDUMP, "BROADCOM: addr", addr, ETH_ALEN);
+
+ wkt.index = key_idx;
+ wkt.len = key_len;
+ if (key && key_len > 0) {
+ os_memcpy(wkt.data, key, key_len);
+ if (key_len == 32) {
+ /* hack hack hack XXX */
+ os_memcpy(&wkt.data[16], &key[24], 8);
+ os_memcpy(&wkt.data[24], &key[16], 8);
+ }
+ }
+ /* wkt.algo = CRYPTO_ALGO_...; */
+ wkt.flags = set_tx ? 0 : WSEC_PRIMARY_KEY;
+ if (addr && set_tx)
+ os_memcpy(&wkt.ea, addr, sizeof(wkt.ea));
+ ret = broadcom_ioctl(drv, WLC_SET_KEY, &wkt, sizeof(wkt));
+ if (addr && set_tx) {
+ /* FIX: magic number / error handling? */
+ broadcom_ioctl(drv, 121, &wkt.ea, sizeof(wkt.ea));
+ }
+ return ret;
+}
+
+
+static void wpa_driver_broadcom_event_receive(int sock, void *ctx,
+ void *sock_ctx)
+{
+ char buf[8192];
+ 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;
+
+ wpa_hexdump(MSG_DEBUG, "RECEIVE EVENT", (u8 *) buf, left);
+
+ if ((size_t) left < sizeof(wl_wpa_header_t))
+ return;
+
+ wwh = (wl_wpa_header_t *) buf;
+
+ if (wwh->snap.type != WL_WPA_ETHER_TYPE)
+ return;
+ if (os_memcmp(&wwh->snap, wl_wpa_snap_template, 6) != 0)
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+
+ switch (wwh->type) {
+ case WLC_ASSOC_MSG:
+ left -= WL_WPA_HEADER_LEN;
+ wpa_printf(MSG_DEBUG, "BROADCOM: ASSOC MESSAGE (left: %d)",
+ left);
+ if (left > 0) {
+ resp_ies = os_malloc(left);
+ if (resp_ies == NULL)
+ return;
+ 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_supplicant_event(ctx, EVENT_ASSOC, &data);
+ os_free(resp_ies);
+ break;
+ case WLC_DISASSOC_MSG:
+ wpa_printf(MSG_DEBUG, "BROADCOM: DISASSOC MESSAGE");
+ wpa_supplicant_event(ctx, EVENT_DISASSOC, NULL);
+ break;
+ case WLC_PTK_MIC_MSG:
+ wpa_printf(MSG_DEBUG, "BROADCOM: PTK MIC MSG MESSAGE");
+ data.michael_mic_failure.unicast = 1;
+ wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
+ break;
+ case WLC_GTK_MIC_MSG:
+ wpa_printf(MSG_DEBUG, "BROADCOM: GTK MIC MSG MESSAGE");
+ data.michael_mic_failure.unicast = 0;
+ wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "BROADCOM: UNKNOWN MESSAGE (%d)",
+ wwh->type);
+ break;
+ }
+}
+
+static void * wpa_driver_broadcom_init(void *ctx, const char *ifname)
+{
+ int s;
+ struct sockaddr_ll ll;
+ struct wpa_driver_broadcom_data *drv;
+ struct ifreq ifr;
+
+ /* open socket to kernel */
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ perror("socket");
+ return NULL;
+ }
+ /* do it */
+ os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+ if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
+ perror(ifr.ifr_name);
+ return NULL;
+ }
+
+
+ drv = os_zalloc(sizeof(*drv));
+ if (drv == NULL)
+ return NULL;
+ drv->ctx = ctx;
+ os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+ drv->ioctl_sock = s;
+
+ s = socket(PF_PACKET, SOCK_RAW, ntohs(ETH_P_802_2));
+ if (s < 0) {
+ perror("socket(PF_PACKET, SOCK_RAW, ntohs(ETH_P_802_2))");
+ close(drv->ioctl_sock);
+ os_free(drv);
+ return NULL;
+ }
+
+ os_memset(&ll, 0, sizeof(ll));
+ ll.sll_family = AF_PACKET;
+ ll.sll_protocol = ntohs(ETH_P_802_2);
+ ll.sll_ifindex = ifr.ifr_ifindex;
+ ll.sll_hatype = 0;
+ ll.sll_pkttype = PACKET_HOST;
+ ll.sll_halen = 0;
+
+ if (bind(s, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
+ perror("bind(netlink)");
+ close(s);
+ close(drv->ioctl_sock);
+ os_free(drv);
+ return NULL;
+ }
+
+ 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;
+}
+
+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);
+ close(drv->ioctl_sock);
+ os_free(drv);
+}
+
+static int wpa_driver_broadcom_set_countermeasures(void *priv,
+ int enabled)
+{
+#if 0
+ struct wpa_driver_broadcom_data *drv = priv;
+ /* FIX: ? */
+ return broadcom_ioctl(drv, WLC_SET_TKIP_COUNTERMEASURES, &enabled,
+ sizeof(enabled));
+#else
+ return 0;
+#endif
+}
+
+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);
+
+ if (broadcom_ioctl(drv, WLC_SET_WEP_RESTRICT,
+ &_restrict, sizeof(_restrict)) < 0 ||
+ broadcom_ioctl(drv, WLC_SET_EAP_RESTRICT,
+ &_restrict, sizeof(_restrict)) < 0)
+ return -1;
+
+ return 0;
+}
+
+static void wpa_driver_broadcom_scan_timeout(void *eloop_ctx,
+ void *timeout_ctx)
+{
+ wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
+ wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
+}
+
+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;
+ os_memcpy(wst.SSID, ssid, ssid_len);
+ }
+
+ if (broadcom_ioctl(drv, WLC_SCAN, &wst, sizeof(wst)) < 0)
+ return -1;
+
+ eloop_cancel_timeout(wpa_driver_broadcom_scan_timeout, drv, drv->ctx);
+ eloop_register_timeout(3, 0, wpa_driver_broadcom_scan_timeout, drv,
+ drv->ctx);
+ return 0;
+}
+
+
+static const int frequency_list[] = {
+ 2412, 2417, 2422, 2427, 2432, 2437, 2442,
+ 2447, 2452, 2457, 2462, 2467, 2472, 2484
+};
+
+struct bss_ie_hdr {
+ u8 elem_id;
+ u8 len;
+ u8 oui[3];
+ /* u8 oui_type; */
+ /* u16 version; */
+} __attribute__ ((packed));
+
+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 NULL;
+
+ wsr = (wl_scan_results_t *) buf;
+
+ wsr->buflen = WLC_IOCTL_MAXLEN - sizeof(wsr);
+ wsr->version = 107;
+ wsr->count = 0;
+
+ if (broadcom_ioctl(drv, WLC_SCAN_RESULTS, buf, WLC_IOCTL_MAXLEN) < 0) {
+ os_free(buf);
+ return NULL;
+ }
+
+ 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) {
+ 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);
+ }
+
+ wpa_printf(MSG_MSGDUMP, "Received %d bytes of scan results (%lu "
+ "BSSes)",
+ wsr->buflen, (unsigned long) ap_num);
+
+ os_free(buf);
+ return res;
+ }
+
+static int wpa_driver_broadcom_deauthenticate(void *priv, const u8 *addr,
+ int reason_code)
+{
+ struct wpa_driver_broadcom_data *drv = priv;
+ wlc_deauth_t wdt;
+ wdt.val = reason_code;
+ os_memcpy(&wdt.ea, addr, sizeof wdt.ea);
+ wdt.res = 0x7fff;
+ return broadcom_ioctl(drv, WLC_DEAUTHENTICATE_WITH_REASON, &wdt,
+ sizeof(wdt));
+}
+
+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, NULL, 0);
+}
+
+static int
+wpa_driver_broadcom_associate(void *priv,
+ struct wpa_driver_associate_params *params)
+{
+ struct wpa_driver_broadcom_data *drv = priv;
+ wlc_ssid_t s;
+ int infra = 1;
+ int auth = 0;
+ 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);
+
+ switch (params->pairwise_suite) {
+ case CIPHER_WEP40:
+ case CIPHER_WEP104:
+ wsec = 1;
+ break;
+
+ case CIPHER_TKIP:
+ wsec = 2;
+ break;
+
+ case CIPHER_CCMP:
+ wsec = 4;
+ break;
+
+ default:
+ wsec = 0;
+ break;
+ }
+
+ switch (params->key_mgmt_suite) {
+ case KEY_MGMT_802_1X:
+ wpa_auth = 1;
+ break;
+
+ case KEY_MGMT_PSK:
+ wpa_auth = 2;
+ break;
+
+ default:
+ wpa_auth = 255;
+ break;
+ }
+
+ /* printf("broadcom_associate: %u %u %u\n", pairwise_suite,
+ * group_suite, key_mgmt_suite);
+ * broadcom_ioctl(ifname, WLC_GET_WSEC, &wsec, sizeof(wsec));
+ * wl join uses wlc_sec_wep here, not wlc_set_wsec */
+
+ if (broadcom_ioctl(drv, WLC_SET_WSEC, &wsec, sizeof(wsec)) < 0 ||
+ broadcom_ioctl(drv, WLC_SET_WPA_AUTH, &wpa_auth,
+ sizeof(wpa_auth)) < 0 ||
+ broadcom_ioctl(drv, WLC_GET_WEP, &dummy, sizeof(dummy)) < 0 ||
+ broadcom_ioctl(drv, WLC_SET_INFRA, &infra, sizeof(infra)) < 0 ||
+ broadcom_ioctl(drv, WLC_SET_AUTH, &auth, sizeof(auth)) < 0 ||
+ broadcom_ioctl(drv, WLC_SET_WEP, &wsec, sizeof(wsec)) < 0 ||
+ broadcom_ioctl(drv, WLC_SET_SSID, &s, sizeof(s)) < 0)
+ return -1;
+
+ return ret;
+}
+
+const struct wpa_driver_ops wpa_driver_broadcom_ops = {
+ .name = "broadcom",
+ .desc = "Broadcom wl.o driver",
+ .get_bssid = wpa_driver_broadcom_get_bssid,
+ .get_ssid = wpa_driver_broadcom_get_ssid,
+ .set_key = wpa_driver_broadcom_set_key,
+ .init = wpa_driver_broadcom_init,
+ .deinit = wpa_driver_broadcom_deinit,
+ .set_countermeasures = wpa_driver_broadcom_set_countermeasures,
+ .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
new file mode 100644
index 0000000..1b52865
--- /dev/null
+++ b/src/drivers/driver_bsd.c
@@ -0,0 +1,1573 @@
+/*
+ * 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
+ * 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 <sys/sysctl.h>
+
+#include "common.h"
+#include "driver.h"
+#include "eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_common.h"
+
+#include <net/if.h>
+#include <net/if_media.h>
+
+#ifdef __NetBSD__
+#include <net/if_ether.h>
+#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_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 */
+
+ 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;
+ 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
+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, sizeof(ireq.i_name));
+ ireq.i_type = op;
+ ireq.i_val = val;
+ ireq.i_data = (void *) arg;
+ ireq.i_len = arg_len;
+
+ if (ioctl(drv->sock, SIOCS80211, &ireq) < 0) {
+ 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
+bsd_get80211(void *priv, struct ieee80211req *ireq, int op, void *arg,
+ int arg_len)
+{
+ struct bsd_driver_data *drv = priv;
+
+ 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) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCS80211, op=%u, "
+ "arg_len=%u]: %s", op, arg_len, strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+static int
+get80211var(struct bsd_driver_data *drv, int op, void *arg, int arg_len)
+{
+ struct ieee80211req ireq;
+
+ if (bsd_get80211(drv, &ireq, op, arg, arg_len) < 0)
+ return -1;
+ return ireq.i_len;
+}
+
+static int
+set80211var(struct bsd_driver_data *drv, int op, const void *arg, int arg_len)
+{
+ return bsd_set80211(drv, op, 0, arg, arg_len);
+}
+
+static int
+set80211param(struct bsd_driver_data *drv, int op, int arg)
+{
+ return bsd_set80211(drv, op, arg, NULL, 0);
+}
+
+static int
+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));
+ 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
+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_data = (void *)&nwid;
+ return ioctl(drv->sock, SIOCS80211NWID, &ifr);
+#else
+ return set80211var(drv, IEEE80211_IOC_SSID, ssid, ssid_len);
+#endif
+}
+
+static int
+bsd_get_if_media(void *priv)
+{
+ struct bsd_driver_data *drv = priv;
+ struct ifmediareq ifmr;
+
+ os_memset(&ifmr, 0, sizeof(ifmr));
+ os_strlcpy(ifmr.ifm_name, drv->ifname, sizeof(ifmr.ifm_name));
+
+ if (ioctl(drv->sock, SIOCGIFMEDIA, &ifmr) < 0) {
+ wpa_printf(MSG_ERROR, "%s: SIOCGIFMEDIA %s", __func__,
+ strerror(errno));
+ return -1;
+ }
+
+ return ifmr.ifm_current;
+}
+
+static int
+bsd_set_if_media(void *priv, int media)
+{
+ struct bsd_driver_data *drv = priv;
+ struct ifreq ifr;
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
+ ifr.ifr_media = media;
+
+ if (ioctl(drv->sock, SIOCSIFMEDIA, &ifr) < 0) {
+ wpa_printf(MSG_ERROR, "%s: SIOCSIFMEDIA %s", __func__,
+ strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+bsd_set_mediaopt(void *priv, uint32_t mask, uint32_t mode)
+{
+ 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
+bsd_del_key(void *priv, const u8 *addr, int key_idx)
+{
+ struct ieee80211req_del_key wk;
+
+ 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 set80211var(priv, IEEE80211_IOC_DELKEY, &wk, sizeof(wk));
+}
+
+static int
+bsd_send_mlme_param(void *priv, const u8 op, const u16 reason, const u8 *addr)
+{
+ struct ieee80211req_mlme mlme;
+
+ 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
+bsd_ctrl_iface(void *priv, int enable)
+{
+ struct bsd_driver_data *drv = priv;
+ 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, &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 0;
+}
+
+static int
+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 ieee80211req_key wk;
+
+ 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 || is_broadcast_ether_addr(addr))
+ 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:
+ wk.ik_type = IEEE80211_CIPHER_WEP;
+ break;
+ case WPA_ALG_TKIP:
+ wk.ik_type = IEEE80211_CIPHER_TKIP;
+ break;
+ case WPA_ALG_CCMP:
+ wk.ik_type = IEEE80211_CIPHER_AES_CCM;
+ break;
+ default:
+ wpa_printf(MSG_ERROR, "%s: unknown alg=%d", __func__, alg);
+ return -1;
+ }
+
+ wk.ik_flags = IEEE80211_KEY_RECV;
+ if (set_tx)
+ wk.ik_flags |= IEEE80211_KEY_XMIT;
+
+ if (addr == NULL) {
+ os_memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN);
+ wk.ik_keyix = key_idx;
+ } else {
+ 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 (is_broadcast_ether_addr(addr)) {
+ 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;
+ wk.ik_keylen = key_len;
+ if (seq)
+ os_memcpy(&wk.ik_keyrsc, seq, seq_len);
+ os_memcpy(wk.ik_keydata, key, key_len);
+
+ return set80211var(priv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk));
+}
+
+static int
+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, 0);
+}
+
+static int
+bsd_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len,
+ int encrypt, const u8 *own_addr, u32 flags)
+{
+ 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, struct hostapd_freq_params *freq)
+{
+ struct bsd_driver_data *drv = priv;
+#ifdef SIOCS80211CHANNEL
+ struct ieee80211chanreq creq;
+#endif /* SIOCS80211CHANNEL */
+ u32 mode;
+ int channel = freq->channel;
+
+ if (channel < 14) {
+ mode =
+#ifdef CONFIG_IEEE80211N
+ freq->ht_enabled ? IFM_IEEE80211_11NG :
+#endif /* CONFIG_IEEE80211N */
+ IFM_IEEE80211_11G;
+ } else if (channel == 14) {
+ mode = IFM_IEEE80211_11B;
+ } else {
+ mode =
+#ifdef CONFIG_IEEE80211N
+ freq->ht_enabled ? IFM_IEEE80211_11NA :
+#endif /* CONFIG_IEEE80211N */
+ 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 = (u_int16_t)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;
+}
+
+static int
+rtbuf_len(void)
+{
+ size_t len;
+
+ int mib[6] = {CTL_NET, AF_ROUTE, 0, AF_INET, NET_RT_DUMP, 0};
+
+ if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
+ wpa_printf(MSG_WARNING, "%s failed: %s\n", __func__,
+ strerror(errno));
+ len = 2048;
+ }
+
+ return len;
+}
+
+#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(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
+bsd_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data,
+ const u8 *addr)
+{
+ 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;
+ 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, len;
+ union wpa_event_data data;
+
+ len = rtbuf_len();
+
+ buf = os_malloc(len);
+ if (buf == NULL) {
+ wpa_printf(MSG_ERROR, "%s os_malloc() failed\n", __func__);
+ return;
+ }
+
+ n = read(sock, buf, len);
+ if (n < 0) {
+ if (errno != EINTR && errno != EAGAIN)
+ wpa_printf(MSG_ERROR, "%s read() failed: %s\n",
+ __func__, strerror(errno));
+ os_free(buf);
+ return;
+ }
+
+ rtm = (struct rt_msghdr *) buf;
+ if (rtm->rtm_version != RTM_VERSION) {
+ wpa_printf(MSG_DEBUG, "Invalid routing message version=%d",
+ rtm->rtm_version);
+ os_free(buf);
+ 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;
+ }
+ os_free(buf);
+}
+
+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 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(priv, IEEE80211_IOC_COUNTERMEASURES, enabled);
+}
+
+
+static int
+wpa_driver_bsd_set_drop_unencrypted(void *priv, int enabled)
+{
+ wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
+ return set80211param(priv, IEEE80211_IOC_DROPUNENCRYPTED, enabled);
+}
+
+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)
+{
+ return bsd_send_mlme_param(priv, IEEE80211_MLME_DISASSOC, reason_code,
+ addr);
+}
+
+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 bsd_driver_data *drv = priv;
+ struct ieee80211req_mlme mlme;
+ u32 mode;
+ int privacy;
+ int ret = 0;
+
+ wpa_printf(MSG_DEBUG,
+ "%s: ssid '%.*s' wpa ie len %u pairwise %u group %u key mgmt %u"
+ , __func__
+ , (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) {
+ 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;
+
+ privacy = !(params->pairwise_suite == CIPHER_NONE &&
+ params->group_suite == CIPHER_NONE &&
+ params->key_mgmt_suite == KEY_MGMT_NONE &&
+ params->wpa_ie_len == 0);
+ wpa_printf(MSG_DEBUG, "%s: set PRIVACY %u", __func__, privacy);
+
+ if (set80211param(drv, IEEE80211_IOC_PRIVACY, privacy) < 0)
+ return -1;
+
+ if (params->wpa_ie_len &&
+ set80211param(drv, IEEE80211_IOC_WPA,
+ params->wpa_ie[0] == WLAN_EID_RSN ? 2 : 1) < 0)
+ return -1;
+
+ os_memset(&mlme, 0, sizeof(mlme));
+ mlme.im_op = IEEE80211_MLME_ASSOC;
+ if (params->ssid != NULL)
+ os_memcpy(mlme.im_ssid, params->ssid, params->ssid_len);
+ mlme.im_ssid_len = params->ssid_len;
+ if (params->bssid != NULL)
+ os_memcpy(mlme.im_macaddr, params->bssid, IEEE80211_ADDR_LEN);
+ if (set80211var(drv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme)) < 0)
+ return -1;
+ return ret;
+}
+
+static int
+wpa_driver_bsd_scan(void *priv, struct wpa_driver_scan_params *params)
+{
+ 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 (bsd_set_mediaopt(drv, IFM_OMASK, 0 /* STA */) < 0) {
+ wpa_printf(MSG_ERROR, "%s: failed to set operation mode",
+ __func__);
+ return -1;
+ }
+
+ 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;
+ }
+
+ 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 (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 (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 */
+}
+
+static void
+wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx)
+{
+ struct bsd_driver_data *drv = sock_ctx;
+ char *buf;
+ 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, len;
+
+ len = rtbuf_len();
+
+ buf = os_malloc(len);
+ if (buf == NULL) {
+ wpa_printf(MSG_ERROR, "%s os_malloc() failed\n", __func__);
+ return;
+ }
+
+ n = read(sock, buf, len);
+ if (n < 0) {
+ if (errno != EINTR && errno != EAGAIN)
+ wpa_printf(MSG_ERROR, "%s read() failed: %s\n",
+ __func__, strerror(errno));
+ os_free(buf);
+ return;
+ }
+
+ rtm = (struct rt_msghdr *) buf;
+ if (rtm->rtm_version != RTM_VERSION) {
+ wpa_printf(MSG_DEBUG, "Invalid routing message version=%d",
+ rtm->rtm_version);
+ os_free(buf);
+ return;
+ }
+ os_memset(&event, 0, sizeof(event));
+ switch (rtm->rtm_type) {
+ case RTM_IFANNOUNCE:
+ ifan = (struct if_announcemsghdr *) rtm;
+ if (ifan->ifan_index != drv->ifindex)
+ break;
+ 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;
+ default:
+ os_free(buf);
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "RTM_IFANNOUNCE: Interface '%s' %s",
+ event.interface_status.ifname,
+ ifan->ifan_what == IFAN_DEPARTURE ?
+ "removed" : "added");
+ wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event);
+ break;
+ case RTM_IEEE80211:
+ ifan = (struct if_announcemsghdr *) rtm;
+ if (ifan->ifan_index != drv->ifindex)
+ break;
+ 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;
+ 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(&event, 0, sizeof(event));
+ event.michael_mic_failure.unicast =
+ !IEEE80211_IS_MULTICAST(mic->iev_dst);
+ wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE,
+ &event);
+ break;
+ }
+ break;
+ case RTM_IFINFO:
+ ifm = (struct if_msghdr *) rtm;
+ if (ifm->ifm_index != drv->ifindex)
+ break;
+ if ((rtm->rtm_flags & RTF_UP) == 0) {
+ 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);
+ wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event);
+ }
+ break;
+ }
+ os_free(buf);
+}
+
+static void
+wpa_driver_bsd_add_scan_entry(struct wpa_scan_results *res,
+ struct ieee80211req_scan_result *sr)
+{
+ struct wpa_scan_res *result, **tmp;
+ size_t extra_len;
+ u8 *pos;
+
+ extra_len = 2 + sr->isr_ssid_len;
+ extra_len += 2 + sr->isr_nrates;
+ extra_len += 3; /* ERP IE */
+ extra_len += sr->isr_ie_len;
+
+ 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;
+
+ os_memcpy(pos, (u8 *)(sr + 1) + sr->isr_ssid_len, sr->isr_ie_len);
+ pos += sr->isr_ie_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;
+}
+
+struct wpa_scan_results *
+wpa_driver_bsd_get_scan_results2(void *priv)
+{
+ 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;
+
+ 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;
+ }
+
+ wpa_printf(MSG_DEBUG, "Received %d bytes of scan results (%lu BSSes)",
+ len, (unsigned long)res->num);
+
+ return res;
+}
+
+static int wpa_driver_bsd_capa(struct bsd_driver_data *drv)
+{
+#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;
+
+ if (get80211var(drv, IEEE80211_IOC_DEVCAPS, &devcaps,
+ sizeof(devcaps)) < 0) {
+ wpa_printf(MSG_ERROR, "failed to IEEE80211_IOC_DEVCAPS: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ 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;
+
+ 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 *
+wpa_driver_bsd_init(void *ctx, const char *ifname)
+{
+#define GETPARAM(drv, param, v) \
+ (((v) = get80211param(drv, param)) != -1)
+ struct bsd_driver_data *drv;
+
+ drv = os_zalloc(sizeof(*drv));
+ if (drv == NULL)
+ return NULL;
+ /*
+ * NB: We require the interface name be mappable to an index.
+ * This implies we do not support having wpa_supplicant
+ * wait for an interface to appear. This seems ok; that
+ * doesn't belong here; it's really the job of devd.
+ */
+ drv->ifindex = if_nametoindex(ifname);
+ if (drv->ifindex == 0) {
+ wpa_printf(MSG_DEBUG, "%s: interface %s does not exist",
+ __func__, ifname);
+ goto fail1;
+ }
+ drv->sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (drv->sock < 0)
+ goto fail1;
+ drv->route = socket(PF_ROUTE, SOCK_RAW, 0);
+ if (drv->route < 0)
+ goto fail;
+ eloop_register_read_sock(drv->route,
+ wpa_driver_bsd_event_receive, ctx, drv);
+
+ 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));
+ goto fail;
+ }
+ if (!GETPARAM(drv, IEEE80211_IOC_PRIVACY, drv->prev_privacy)) {
+ wpa_printf(MSG_DEBUG, "%s: failed to get privacy state: %s",
+ __func__, strerror(errno));
+ goto fail;
+ }
+ if (!GETPARAM(drv, IEEE80211_IOC_WPA, drv->prev_wpa)) {
+ wpa_printf(MSG_DEBUG, "%s: failed to get wpa state: %s",
+ __func__, strerror(errno));
+ goto fail;
+ }
+
+ if (wpa_driver_bsd_capa(drv))
+ goto fail;
+
+ return drv;
+fail:
+ close(drv->sock);
+fail1:
+ os_free(drv);
+ return NULL;
+#undef GETPARAM
+}
+
+static void
+wpa_driver_bsd_deinit(void *priv)
+{
+ struct bsd_driver_data *drv = priv;
+
+ wpa_driver_bsd_set_wpa(drv, 0);
+ eloop_unregister_read_sock(drv->route);
+
+ /* NB: mark interface down */
+ 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",
+#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,
+#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_countermeasures = wpa_driver_bsd_set_countermeasures,
+ .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,
+ .get_capa = wpa_driver_bsd_get_capa,
+#endif /* HOSTAPD */
+ .set_freq = bsd_set_freq,
+ .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
new file mode 100644
index 0000000..e855c1b
--- /dev/null
+++ b/src/drivers/driver_hostap.c
@@ -0,0 +1,1648 @@
+/*
+ * 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
+ * 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 "eloop.h"
+#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,
+ u32 flags)
+{
+ 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,
+ const struct wpabuf *assocresp)
+{
+ 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;
+
+ if (is_broadcast_ether_addr(addr)) {
+ /*
+ * New Prism2.5/3 STA firmware versions seem to have issues
+ * with this broadcast deauth frame. This gets the firmware in
+ * odd state where nothing works correctly, so let's skip
+ * sending this for the hostap driver.
+ */
+ return 0;
+ }
+
+ 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;
+ char ifname[IFNAMSIZ + 1];
+ int sock;
+ int current_mode; /* infra/adhoc */
+};
+
+
+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)
+{
+ 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, PRISM2_IOCTL_HOSTAPD, &iwr) < 0) {
+ int ret = errno;
+ if (show_err)
+ perror("ioctl[PRISM2_IOCTL_HOSTAPD]");
+ return ret;
+ }
+
+ return 0;
+}
+
+
+static int wpa_driver_hostap_set_wpa_ie(struct wpa_driver_hostap_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(drv, param, blen, 1);
+
+ os_free(param);
+
+ return res;
+}
+
+
+static int prism2param(struct wpa_driver_hostap_data *drv, int param,
+ int value)
+{
+ struct iwreq iwr;
+ int *i, ret = 0;
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+ i = (int *) iwr.u.name;
+ *i++ = param;
+ *i++ = value;
+
+ if (ioctl(drv->sock, PRISM2_IOCTL_PRISM2_PARAM, &iwr) < 0) {
+ perror("ioctl[PRISM2_IOCTL_PRISM2_PARAM]");
+ ret = -1;
+ }
+ return ret;
+}
+
+
+static int wpa_driver_hostap_set_wpa(void *priv, int enabled)
+{
+ struct wpa_driver_hostap_data *drv = priv;
+ int ret = 0;
+
+ wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled);
+
+ if (!enabled && wpa_driver_hostap_set_wpa_ie(drv, NULL, 0) < 0)
+ ret = -1;
+ if (prism2param(drv, PRISM2_PARAM_HOST_ROAMING, enabled ? 2 : 0) < 0)
+ ret = -1;
+ if (prism2param(drv, PRISM2_PARAM_WPA, enabled) < 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_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;
+ 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";
+ break;
+ case WPA_ALG_WEP:
+ alg_name = "WEP";
+ break;
+ case WPA_ALG_TKIP:
+ alg_name = "TKIP";
+ break;
+ case WPA_ALG_CCMP:
+ alg_name = "CCMP";
+ 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;
+ if (seq)
+ 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(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_hostap_set_countermeasures(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_TKIP_COUNTERMEASURES, enabled);
+}
+
+
+static int wpa_driver_hostap_reset(struct wpa_driver_hostap_data *drv,
+ int type)
+{
+ struct iwreq iwr;
+ int *i, ret = 0;
+
+ wpa_printf(MSG_DEBUG, "%s: type=%d", __FUNCTION__, type);
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+ i = (int *) iwr.u.name;
+ *i++ = type;
+
+ if (ioctl(drv->sock, PRISM2_IOCTL_RESET, &iwr) < 0) {
+ perror("ioctl[PRISM2_IOCTL_RESET]");
+ ret = -1;
+ }
+ return ret;
+}
+
+
+static int wpa_driver_hostap_mlme(struct wpa_driver_hostap_data *drv,
+ const u8 *addr, int cmd, int reason_code)
+{
+ struct prism2_hostapd_param param;
+ int ret;
+
+ /* There does not seem to be a better way of deauthenticating or
+ * disassociating with Prism2/2.5/3 than sending the management frame
+ * and then resetting the Port0 to make sure both the AP and the STA
+ * end up in disconnected state. */
+ os_memset(¶m, 0, sizeof(param));
+ param.cmd = PRISM2_HOSTAPD_MLME;
+ os_memcpy(param.sta_addr, addr, ETH_ALEN);
+ param.u.mlme.cmd = cmd;
+ param.u.mlme.reason_code = reason_code;
+ ret = hostapd_ioctl(drv, ¶m, sizeof(param), 1);
+ if (ret == 0) {
+ os_sleep(0, 100000);
+ ret = wpa_driver_hostap_reset(drv, 2);
+ }
+ return ret;
+}
+
+
+static int wpa_driver_hostap_deauthenticate(void *priv, const u8 *addr,
+ int reason_code)
+{
+ struct wpa_driver_hostap_data *drv = priv;
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+ return wpa_driver_hostap_mlme(drv, addr, MLME_STA_DEAUTH,
+ reason_code);
+}
+
+
+static int wpa_driver_hostap_disassociate(void *priv, const u8 *addr,
+ int reason_code)
+{
+ struct wpa_driver_hostap_data *drv = priv;
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+ return wpa_driver_hostap_mlme(drv, addr, MLME_STA_DISASSOC,
+ reason_code);
+}
+
+
+static int
+wpa_driver_hostap_associate(void *priv,
+ struct wpa_driver_associate_params *params)
+{
+ struct wpa_driver_hostap_data *drv = priv;
+ int ret = 0;
+ int allow_unencrypted_eapol;
+
+ 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. */
+ if (prism2param(drv, PRISM2_PARAM_HOST_ROAMING,
+ params->mode == IEEE80211_MODE_IBSS ? 0 : 2) <
+ 0) {
+ wpa_printf(MSG_DEBUG, "%s: failed to set host_roaming",
+ __func__);
+ }
+ drv->current_mode = params->mode;
+ }
+
+ if (prism2param(drv, PRISM2_PARAM_PRIVACY_INVOKED,
+ params->key_mgmt_suite != KEY_MGMT_NONE) < 0)
+ ret = -1;
+ if (wpa_driver_hostap_set_wpa_ie(drv, params->wpa_ie,
+ params->wpa_ie_len) < 0)
+ ret = -1;
+ if (wpa_driver_wext_set_mode(drv->wext, params->mode) < 0)
+ ret = -1;
+ if (params->freq &&
+ 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;
+
+ /* 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 (prism2param(drv, PRISM2_PARAM_IEEE_802_1X,
+ allow_unencrypted_eapol) < 0) {
+ wpa_printf(MSG_DEBUG, "hostap: Failed to configure "
+ "ieee_802_1x param");
+ /* Ignore this error.. driver_hostap.c can also be used with
+ * other drivers that do not support this prism2_param. */
+ }
+
+ return ret;
+}
+
+
+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, params);
+ }
+
+ if (ssid_len > 32)
+ ssid_len = 32;
+
+ os_memset(¶m, 0, sizeof(param));
+ param.cmd = PRISM2_HOSTAPD_SCAN_REQ;
+ param.u.scan_req.ssid_len = ssid_len;
+ os_memcpy(param.u.scan_req.ssid, ssid, ssid_len);
+ ret = hostapd_ioctl(drv, ¶m, sizeof(param), 1);
+
+ /* Not all drivers generate "scan completed" wireless event, so try to
+ * read results after a timeout. */
+ eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv->wext,
+ drv->ctx);
+ eloop_register_timeout(3, 0, wpa_driver_wext_scan_timeout, drv->wext,
+ drv->ctx);
+
+ return ret;
+}
+
+
+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 & WPA_AUTH_ALG_OPEN)
+ algs |= 1;
+ if (auth_alg & WPA_AUTH_ALG_SHARED)
+ algs |= 2;
+ if (auth_alg & WPA_AUTH_ALG_LEAP)
+ algs |= 4;
+ if (algs == 0)
+ algs = 1; /* at least one algorithm should be set */
+
+ return prism2param(drv, PRISM2_PARAM_AP_AUTH_ALGS, algs);
+}
+
+
+static int wpa_driver_hostap_get_bssid(void *priv, u8 *bssid)
+{
+ struct wpa_driver_hostap_data *drv = priv;
+ return wpa_driver_wext_get_bssid(drv->wext, bssid);
+}
+
+
+static int wpa_driver_hostap_get_ssid(void *priv, u8 *ssid)
+{
+ struct wpa_driver_hostap_data *drv = priv;
+ return wpa_driver_wext_get_ssid(drv->wext, ssid);
+}
+
+
+static struct wpa_scan_results * wpa_driver_hostap_get_scan_results(void *priv)
+{
+ struct wpa_driver_hostap_data *drv = priv;
+ return wpa_driver_wext_get_scan_results(drv->wext);
+}
+
+
+static int wpa_driver_hostap_set_operstate(void *priv, int state)
+{
+ struct wpa_driver_hostap_data *drv = priv;
+ return wpa_driver_wext_set_operstate(drv->wext, state);
+}
+
+
+static void * wpa_driver_hostap_init(void *ctx, const char *ifname)
+{
+ struct wpa_driver_hostap_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) {
+ perror("socket");
+ wpa_driver_wext_deinit(drv->wext);
+ os_free(drv);
+ return NULL;
+ }
+
+ if (os_strncmp(ifname, "wlan", 4) == 0) {
+ /*
+ * Host AP driver may use both wlan# and wifi# interface in
+ * wireless events.
+ */
+ char ifname2[IFNAMSIZ + 1];
+ os_strlcpy(ifname2, ifname, sizeof(ifname2));
+ os_memcpy(ifname2, "wifi", 4);
+ wpa_driver_wext_alternative_ifindex(drv->wext, ifname2);
+ }
+
+ wpa_driver_hostap_set_wpa(drv, 1);
+
+ return drv;
+}
+
+
+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_countermeasures = wpa_driver_hostap_set_countermeasures,
+ .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,
+ .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
new file mode 100644
index 0000000..66b2bb3
--- /dev/null
+++ b/src/drivers/driver_hostap.h
@@ -0,0 +1,216 @@
+/*
+ * 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
+ * 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 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 */
+ PRISM2_PARAM_TXRATECTRL = 2,
+ PRISM2_PARAM_BEACON_INT = 3,
+ PRISM2_PARAM_PSEUDO_IBSS = 4,
+ PRISM2_PARAM_ALC = 5,
+ /* PRISM2_PARAM_TXPOWER = 6, */ /* REMOVED 2003-10-22 */
+ PRISM2_PARAM_DUMP = 7,
+ PRISM2_PARAM_OTHER_AP_POLICY = 8,
+ PRISM2_PARAM_AP_MAX_INACTIVITY = 9,
+ PRISM2_PARAM_AP_BRIDGE_PACKETS = 10,
+ PRISM2_PARAM_DTIM_PERIOD = 11,
+ PRISM2_PARAM_AP_NULLFUNC_ACK = 12,
+ PRISM2_PARAM_MAX_WDS = 13,
+ PRISM2_PARAM_AP_AUTOM_AP_WDS = 14,
+ PRISM2_PARAM_AP_AUTH_ALGS = 15,
+ PRISM2_PARAM_MONITOR_ALLOW_FCSERR = 16,
+ PRISM2_PARAM_HOST_ENCRYPT = 17,
+ PRISM2_PARAM_HOST_DECRYPT = 18,
+ PRISM2_PARAM_BUS_MASTER_THRESHOLD_RX = 19,
+ PRISM2_PARAM_BUS_MASTER_THRESHOLD_TX = 20,
+ PRISM2_PARAM_HOST_ROAMING = 21,
+ PRISM2_PARAM_BCRX_STA_KEY = 22,
+ PRISM2_PARAM_IEEE_802_1X = 23,
+ PRISM2_PARAM_ANTSEL_TX = 24,
+ PRISM2_PARAM_ANTSEL_RX = 25,
+ PRISM2_PARAM_MONITOR_TYPE = 26,
+ PRISM2_PARAM_WDS_TYPE = 27,
+ PRISM2_PARAM_HOSTSCAN = 28,
+ PRISM2_PARAM_AP_SCAN = 29,
+ PRISM2_PARAM_ENH_SEC = 30,
+ PRISM2_PARAM_IO_DEBUG = 31,
+ PRISM2_PARAM_BASIC_RATES = 32,
+ PRISM2_PARAM_OPER_RATES = 33,
+ PRISM2_PARAM_HOSTAPD = 34,
+ PRISM2_PARAM_HOSTAPD_STA = 35,
+ PRISM2_PARAM_WPA = 36,
+ PRISM2_PARAM_PRIVACY_INVOKED = 37,
+ PRISM2_PARAM_TKIP_COUNTERMEASURES = 38,
+ PRISM2_PARAM_DROP_UNENCRYPTED = 39,
+ 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,
+ PRISM2_HOSTAPD_ADD_STA = 2,
+ PRISM2_HOSTAPD_REMOVE_STA = 3,
+ PRISM2_HOSTAPD_GET_INFO_STA = 4,
+ /* REMOVED: PRISM2_HOSTAPD_RESET_TXEXC_STA = 5, */
+ PRISM2_SET_ENCRYPTION = 6,
+ PRISM2_GET_ENCRYPTION = 7,
+ PRISM2_HOSTAPD_SET_FLAGS_STA = 8,
+ PRISM2_HOSTAPD_GET_RID = 9,
+ PRISM2_HOSTAPD_SET_RID = 10,
+ PRISM2_HOSTAPD_SET_ASSOC_AP_ADDR = 11,
+ PRISM2_HOSTAPD_SET_GENERIC_ELEMENT = 12,
+ PRISM2_HOSTAPD_MLME = 13,
+ PRISM2_HOSTAPD_SCAN_REQ = 14,
+ PRISM2_HOSTAPD_STA_CLEAR_STATS = 15,
+};
+
+#define PRISM2_HOSTAPD_MAX_BUF_SIZE 1024
+#define PRISM2_HOSTAPD_RID_HDR_LEN \
+((size_t) (&((struct prism2_hostapd_param *) 0)->u.rid.data))
+#define PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN \
+((size_t) (&((struct prism2_hostapd_param *) 0)->u.generic_elem.data))
+
+/* Maximum length for algorithm names (-1 for nul termination) used in ioctl()
+ */
+#define HOSTAP_CRYPT_ALG_NAME_LEN 16
+
+
+struct prism2_hostapd_param {
+ u32 cmd;
+ u8 sta_addr[ETH_ALEN];
+ union {
+ struct {
+ u16 aid;
+ u16 capability;
+ u8 tx_supp_rates;
+ } add_sta;
+ struct {
+ u32 inactive_sec;
+ } get_info_sta;
+ struct {
+ u8 alg[HOSTAP_CRYPT_ALG_NAME_LEN];
+ u32 flags;
+ u32 err;
+ u8 idx;
+ u8 seq[8]; /* sequence counter (set: RX, get: TX) */
+ u16 key_len;
+ u8 key[0];
+ } crypt;
+ struct {
+ u32 flags_and;
+ u32 flags_or;
+ } set_flags_sta;
+ struct {
+ u16 rid;
+ u16 len;
+ u8 data[0];
+ } rid;
+ struct {
+ u8 len;
+ u8 data[0];
+ } generic_elem;
+ struct {
+#define MLME_STA_DEAUTH 0
+#define MLME_STA_DISASSOC 1
+ u16 cmd;
+ u16 reason_code;
+ } mlme;
+ struct {
+ u8 ssid_len;
+ u8 ssid[32];
+ } scan_req;
+ } u;
+};
+
+#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
+#define HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED 4
+#define HOSTAP_CRYPT_ERR_KEY_SET_FAILED 5
+#define HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED 6
+#define HOSTAP_CRYPT_ERR_CARD_CONF_FAILED 7
+
+#endif /* HOSTAP_DRIVER_H */
diff --git a/src/drivers/driver_iphone.m b/src/drivers/driver_iphone.m
new file mode 100644
index 0000000..8213fda
--- /dev/null
+++ b/src/drivers/driver_iphone.m
@@ -0,0 +1,466 @@
+/*
+ * WPA Supplicant - iPhone/iPod touch Apple80211 driver interface
+ * Copyright (c) 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
+ * 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"
+#define Boolean __DummyBoolean
+#include <CoreFoundation/CoreFoundation.h>
+#undef Boolean
+
+#include "common.h"
+#include "driver.h"
+#include "eloop.h"
+#include "common/ieee802_11_defs.h"
+
+#include "MobileApple80211.h"
+
+struct wpa_driver_iphone_data {
+ void *ctx;
+ Apple80211Ref wireless_ctx;
+ CFArrayRef scan_results;
+ int ctrl_power;
+};
+
+
+static const void * cfdict_get_key_str(CFDictionaryRef dict, const char *key)
+{
+ const void *res;
+ CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, key,
+ kCFStringEncodingMacRoman);
+ if (str == NULL)
+ return NULL;
+
+ res = CFDictionaryGetValue(dict, str);
+ CFRelease(str);
+ return res;
+}
+
+
+static int wpa_driver_iphone_get_ssid(void *priv, u8 *ssid)
+{
+ struct wpa_driver_iphone_data *drv = priv;
+ CFDataRef data;
+ int err, len;
+
+ err = Apple80211CopyValue(drv->wireless_ctx, APPLE80211_VALUE_SSID, 0,
+ &data);
+ if (err != 0) {
+ wpa_printf(MSG_DEBUG, "iPhone: Apple80211CopyValue(SSID) "
+ "failed: %d", err);
+ return -1;
+ }
+
+ len = CFDataGetLength(data);
+ if (len > 32) {
+ CFRelease(data);
+ return -1;
+ }
+ os_memcpy(ssid, CFDataGetBytePtr(data), len);
+ CFRelease(data);
+
+ return len;
+}
+
+
+static int wpa_driver_iphone_get_bssid(void *priv, u8 *bssid)
+{
+ struct wpa_driver_iphone_data *drv = priv;
+ CFStringRef data;
+ int err;
+ int a1, a2, a3, a4, a5, a6;
+
+ err = Apple80211CopyValue(drv->wireless_ctx, APPLE80211_VALUE_BSSID, 0,
+ &data);
+ if (err != 0) {
+ wpa_printf(MSG_DEBUG, "iPhone: Apple80211CopyValue(BSSID) "
+ "failed: %d", err);
+ return -1;
+ }
+
+ sscanf(CFStringGetCStringPtr(data, kCFStringEncodingMacRoman),
+ "%x:%x:%x:%x:%x:%x", &a1, &a2, &a3, &a4, &a5, &a6);
+ bssid[0] = a1;
+ bssid[1] = a2;
+ bssid[2] = a3;
+ bssid[3] = a4;
+ bssid[4] = a5;
+ bssid[5] = a6;
+
+ CFRelease(data);
+
+ return 0;
+}
+
+
+static void wpa_driver_iphone_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
+}
+
+
+static int wpa_driver_iphone_scan(void *priv, const u8 *ssid, size_t ssid_len)
+{
+ struct wpa_driver_iphone_data *drv = priv;
+ int err;
+
+ if (drv->scan_results) {
+ CFRelease(drv->scan_results);
+ drv->scan_results = NULL;
+ }
+
+ err = Apple80211Scan(drv->wireless_ctx, &drv->scan_results, NULL);
+ if (err) {
+ wpa_printf(MSG_DEBUG, "iPhone: Apple80211Scan failed: %d",
+ err);
+ return -1;
+ }
+
+ eloop_register_timeout(0, 0, wpa_driver_iphone_scan_timeout, drv,
+ drv->ctx);
+ return 0;
+}
+
+
+static int wpa_driver_iphone_get_scan_results(void *priv,
+ struct wpa_scan_result *results,
+ size_t max_size)
+{
+ struct wpa_driver_iphone_data *drv = priv;
+ 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];
+ CFDictionaryRef dict =
+ CFArrayGetValueAtIndex(drv->scan_results, i);
+ CFDataRef data;
+ CFStringRef str;
+ CFNumberRef num;
+ int val;
+
+ data = cfdict_get_key_str(dict, "SSID");
+ if (data) {
+ res->ssid_len = CFDataGetLength(data);
+ if (res->ssid_len > 32)
+ res->ssid_len = 32;
+ os_memcpy(res->ssid, CFDataGetBytePtr(data),
+ res->ssid_len);
+ }
+
+ str = cfdict_get_key_str(dict, "BSSID");
+ if (str) {
+ int a1, a2, a3, a4, a5, a6;
+ sscanf(CFStringGetCStringPtr(
+ str, kCFStringEncodingMacRoman),
+ "%x:%x:%x:%x:%x:%x",
+ &a1, &a2, &a3, &a4, &a5, &a6);
+ res->bssid[0] = a1;
+ res->bssid[1] = a2;
+ res->bssid[2] = a3;
+ res->bssid[3] = a4;
+ res->bssid[4] = a5;
+ res->bssid[5] = a6;
+ }
+
+ num = cfdict_get_key_str(dict, "CAPABILITIES");
+ if (num) {
+ if (CFNumberGetValue(num, kCFNumberSInt32Type, &val))
+ res->caps = val;
+ }
+
+ num = cfdict_get_key_str(dict, "CHANNEL");
+ if (num) {
+ if (CFNumberGetValue(num, kCFNumberSInt32Type, &val))
+ res->freq = 2407 + val * 5;
+ }
+
+ num = cfdict_get_key_str(dict, "RSSI");
+ if (num) {
+ if (CFNumberGetValue(num, kCFNumberSInt32Type, &val))
+ res->level = val;
+ }
+
+ num = cfdict_get_key_str(dict, "NOISE");
+ if (num) {
+ if (CFNumberGetValue(num, kCFNumberSInt32Type, &val))
+ res->noise = val;
+ }
+
+ data = cfdict_get_key_str(dict, "IE");
+ if (data) {
+ u8 *ptr = (u8 *) CFDataGetBytePtr(data);
+ int len = CFDataGetLength(data);
+ u8 *pos = ptr, *end = ptr + len;
+
+ while (pos + 2 < end) {
+ if (pos + 2 + pos[1] > end)
+ break;
+ if (pos[0] == WLAN_EID_RSN &&
+ pos[1] <= SSID_MAX_WPA_IE_LEN) {
+ os_memcpy(res->rsn_ie, pos,
+ 2 + pos[1]);
+ res->rsn_ie_len = 2 + pos[1];
+ }
+ if (pos[0] == WLAN_EID_VENDOR_SPECIFIC &&
+ pos[1] > 4 && pos[2] == 0x00 &&
+ pos[3] == 0x50 && pos[4] == 0xf2 &&
+ pos[5] == 0x01) {
+ os_memcpy(res->wpa_ie, pos,
+ 2 + pos[1]);
+ res->wpa_ie_len = 2 + pos[1];
+ }
+
+ pos = pos + 2 + pos[1];
+ }
+ }
+ }
+
+ return num;
+}
+
+
+static void wpa_driver_iphone_assoc_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_driver_iphone_data *drv = eloop_ctx;
+ u8 bssid[ETH_ALEN];
+
+ if (wpa_driver_iphone_get_bssid(drv, bssid) != 0) {
+ eloop_register_timeout(1, 0, wpa_driver_iphone_assoc_timeout,
+ drv, drv->ctx);
+ return;
+ }
+
+ wpa_supplicant_event(timeout_ctx, EVENT_ASSOC, NULL);
+}
+
+
+static int wpa_driver_iphone_associate(
+ void *priv, struct wpa_driver_associate_params *params)
+{
+ struct wpa_driver_iphone_data *drv = priv;
+ int i, num, err;
+ size_t ssid_len;
+ CFDictionaryRef bss = NULL;
+
+ /*
+ * TODO: Consider generating parameters instead of just using an entry
+ * from scan results in order to support ap_scan=2.
+ */
+
+ if (drv->scan_results == NULL) {
+ wpa_printf(MSG_DEBUG, "iPhone: No scan results - cannot "
+ "associate");
+ return -1;
+ }
+
+ num = CFArrayGetCount(drv->scan_results);
+
+ for (i = 0; i < num; i++) {
+ CFDictionaryRef dict =
+ CFArrayGetValueAtIndex(drv->scan_results, i);
+ CFDataRef data;
+
+ data = cfdict_get_key_str(dict, "SSID");
+ if (data == NULL)
+ continue;
+
+ ssid_len = CFDataGetLength(data);
+ if (ssid_len != params->ssid_len ||
+ os_memcmp(CFDataGetBytePtr(data), params->ssid, ssid_len)
+ != 0)
+ continue;
+
+ bss = dict;
+ break;
+ }
+
+ if (bss == NULL) {
+ wpa_printf(MSG_DEBUG, "iPhone: Could not find SSID from scan "
+ "results - cannot associate");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "iPhone: Trying to associate with a BSS found "
+ "from scan results");
+
+ err = Apple80211Associate(drv->wireless_ctx, bss, NULL);
+ if (err) {
+ wpa_printf(MSG_DEBUG, "iPhone: Apple80211Associate() failed: "
+ "%d", err);
+ return -1;
+ }
+
+ /*
+ * Driver is actually already associated; report association from an
+ * eloop callback.
+ */
+ eloop_cancel_timeout(wpa_driver_iphone_assoc_timeout, drv, drv->ctx);
+ eloop_register_timeout(0, 0, wpa_driver_iphone_assoc_timeout, drv,
+ drv->ctx);
+
+ return 0;
+}
+
+
+static int wpa_driver_iphone_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)
+{
+ /*
+ * TODO: Need to either support configuring PMK for 4-way handshake or
+ * PTK for TKIP/CCMP.
+ */
+ return -1;
+}
+
+
+static int wpa_driver_iphone_get_capa(void *priv, struct wpa_driver_capa *capa)
+{
+ os_memset(capa, 0, sizeof(*capa));
+
+ capa->key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
+ capa->enc = WPA_DRIVER_CAPA_ENC_WEP40 | WPA_DRIVER_CAPA_ENC_WEP104 |
+ WPA_DRIVER_CAPA_ENC_TKIP | WPA_DRIVER_CAPA_ENC_CCMP;
+ capa->auth = WPA_DRIVER_AUTH_OPEN | WPA_DRIVER_AUTH_SHARED |
+ WPA_DRIVER_AUTH_LEAP;
+ capa->flags = WPA_DRIVER_FLAGS_4WAY_HANDSHAKE;
+
+ return 0;
+}
+
+
+static void * wpa_driver_iphone_init(void *ctx, const char *ifname)
+{
+ struct wpa_driver_iphone_data *drv;
+ int err;
+ char power;
+ CFStringRef name;
+ CFDictionaryRef dict;
+
+ drv = os_zalloc(sizeof(*drv));
+ if (drv == NULL)
+ return NULL;
+ drv->ctx = ctx;
+ err = Apple80211Open(&drv->wireless_ctx);
+ if (err) {
+ wpa_printf(MSG_ERROR, "iPhone: Apple80211Open failed: %d",
+ err);
+ os_free(drv);
+ return NULL;
+ }
+
+ name = CFStringCreateWithCString(kCFAllocatorDefault, ifname,
+ kCFStringEncodingISOLatin1);
+ if (name == NULL) {
+ wpa_printf(MSG_ERROR, "iPhone: ifname -> CFString failed");
+ Apple80211Close(drv->wireless_ctx);
+ os_free(drv);
+ return NULL;
+ }
+
+ err = Apple80211BindToInterface(drv->wireless_ctx, name);
+ CFRelease(name);
+
+ if (err) {
+ wpa_printf(MSG_ERROR, "iPhone: Apple80211BindToInterface "
+ "failed: %d", err);
+ Apple80211Close(drv->wireless_ctx);
+ os_free(drv);
+ return NULL;
+ }
+
+ err = Apple80211GetPower(drv->wireless_ctx, &power);
+ if (err)
+ wpa_printf(MSG_DEBUG, "iPhone: Apple80211GetPower failed: %d",
+ err);
+
+ wpa_printf(MSG_DEBUG, "iPhone: Power=%d", power);
+
+ if (!power) {
+ drv->ctrl_power = 1;
+ err = Apple80211SetPower(drv->wireless_ctx, 1);
+ if (err) {
+ wpa_printf(MSG_DEBUG, "iPhone: Apple80211SetPower "
+ "failed: %d", err);
+ Apple80211Close(drv->wireless_ctx);
+ os_free(drv);
+ return NULL;
+ }
+ }
+
+ err = Apple80211GetInfoCopy(drv->wireless_ctx, &dict);
+ if (err == 0) {
+ CFShow(dict);
+ CFRelease(dict);
+ } else {
+ printf("Apple80211GetInfoCopy: %d\n", err);
+ }
+
+ return drv;
+}
+
+
+static void wpa_driver_iphone_deinit(void *priv)
+{
+ struct wpa_driver_iphone_data *drv = priv;
+ int err;
+
+ eloop_cancel_timeout(wpa_driver_iphone_scan_timeout, drv, drv->ctx);
+ eloop_cancel_timeout(wpa_driver_iphone_assoc_timeout, drv, drv->ctx);
+
+ if (drv->ctrl_power) {
+ wpa_printf(MSG_DEBUG, "iPhone: Power down the interface");
+ err = Apple80211SetPower(drv->wireless_ctx, 0);
+ if (err) {
+ wpa_printf(MSG_DEBUG, "iPhone: Apple80211SetPower(0) "
+ "failed: %d", err);
+ }
+ }
+
+ err = Apple80211Close(drv->wireless_ctx);
+ if (err) {
+ wpa_printf(MSG_DEBUG, "iPhone: Apple80211Close failed: %d",
+ err);
+ }
+
+ if (drv->scan_results)
+ CFRelease(drv->scan_results);
+
+ os_free(drv);
+}
+
+
+const struct wpa_driver_ops wpa_driver_iphone_ops = {
+ .name = "iphone",
+ .desc = "iPhone/iPod touch Apple80211 driver",
+ .get_ssid = wpa_driver_iphone_get_ssid,
+ .get_bssid = wpa_driver_iphone_get_bssid,
+ .init = wpa_driver_iphone_init,
+ .deinit = wpa_driver_iphone_deinit,
+ .scan = wpa_driver_iphone_scan,
+ .get_scan_results = wpa_driver_iphone_get_scan_results,
+ .associate = wpa_driver_iphone_associate,
+ .set_key = wpa_driver_iphone_set_key,
+ .get_capa = wpa_driver_iphone_get_capa,
+};
diff --git a/src/drivers/driver_madwifi.c b/src/drivers/driver_madwifi.c
new file mode 100644
index 0000000..630fbf4
--- /dev/null
+++ b/src/drivers/driver_madwifi.c
@@ -0,0 +1,1856 @@
+/*
+ * WPA Supplicant - driver interaction with MADWIFI 802.11 driver
+ * Copyright (c) 2004, Sam Leffler <sam@errno.com>
+ * 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
+ * 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.
+ *
+ * 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"
+#include <sys/ioctl.h>
+
+#include "common.h"
+#include "driver.h"
+#include "driver_wext.h"
+#include "eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "wireless_copy.h"
+
+/*
+ * Avoid conflicts with wpa_supplicant definitions by undefining a definition.
+ */
+#undef WME_OUI_TYPE
+
+#include <include/compat.h>
+#include <net80211/ieee80211.h>
+#ifdef WME_NUM_AC
+/* Assume this is built against BSD branch of madwifi driver. */
+#define MADWIFI_BSD
+#include <net80211/_ieee80211.h>
+#endif /* WME_NUM_AC */
+#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 */
+
+#define WPA_KEY_RSC_LEN 8
+
+#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 || is_broadcast_ether_addr(addr)) {
+ 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,
+ const struct wpabuf *assocresp)
+{
+ 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, 0);
+
+ 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, u32 flags)
+{
+ 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;
+ char ifname[IFNAMSIZ + 1];
+ 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)
+{
+ struct iwreq iwr;
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+ if (len < IFNAMSIZ &&
+ op != IEEE80211_IOCTL_SET_APPIEBUF) {
+ /*
+ * Argument data fits inline; put it there.
+ */
+ os_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->sock, op, &iwr) < 0) {
+ if (show_err) {
+#ifdef MADWIFI_NG
+ int first = IEEE80211_IOCTL_SETPARAM;
+ int last = IEEE80211_IOCTL_KICKMAC;
+ 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]",
+ NULL,
+ "ioctl[IEEE80211_IOCTL_SET_APPIEBUF]",
+ "ioctl[IEEE80211_IOCTL_GETSCANRESULTS]",
+ NULL,
+ "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;
+ int last = IEEE80211_IOCTL_CHANLIST;
+ static const char *opnames[] = {
+ "ioctl[IEEE80211_IOCTL_SETPARAM]",
+ "ioctl[IEEE80211_IOCTL_GETPARAM]",
+ "ioctl[IEEE80211_IOCTL_SETKEY]",
+ "ioctl[IEEE80211_IOCTL_GETKEY]",
+ "ioctl[IEEE80211_IOCTL_DELKEY]",
+ NULL,
+ "ioctl[IEEE80211_IOCTL_SETMLME]",
+ NULL,
+ "ioctl[IEEE80211_IOCTL_SETOPTIE]",
+ "ioctl[IEEE80211_IOCTL_GETOPTIE]",
+ "ioctl[IEEE80211_IOCTL_ADDMAC]",
+ NULL,
+ "ioctl[IEEE80211_IOCTL_DELMAC]",
+ NULL,
+ "ioctl[IEEE80211_IOCTL_CHANLIST]",
+ };
+#endif /* MADWIFI_NG */
+ int idx = op - first;
+ if (first <= op && op <= last &&
+ idx < (int) (sizeof(opnames) / sizeof(opnames[0]))
+ && opnames[idx])
+ perror(opnames[idx]);
+ else
+ perror("ioctl[unknown???]");
+ }
+ return -1;
+ }
+ return 0;
+}
+
+static int
+set80211param(struct wpa_driver_madwifi_data *drv, int op, int arg,
+ int show_err)
+{
+ struct iwreq iwr;
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+ iwr.u.mode = op;
+ os_memcpy(iwr.u.name+sizeof(u32), &arg, sizeof(arg));
+
+ if (ioctl(drv->sock, IEEE80211_IOCTL_SETPARAM, &iwr) < 0) {
+ if (show_err)
+ perror("ioctl[IEEE80211_IOCTL_SETPARAM]");
+ return -1;
+ }
+ return 0;
+}
+
+static int
+wpa_driver_madwifi_set_wpa_ie(struct wpa_driver_madwifi_data *drv,
+ const u8 *wpa_ie, size_t wpa_ie_len)
+{
+ struct iwreq iwr;
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+ /* NB: SETOPTIE is not fixed-size so must not be inlined */
+ iwr.u.data.pointer = (void *) wpa_ie;
+ iwr.u.data.length = wpa_ie_len;
+
+ if (ioctl(drv->sock, IEEE80211_IOCTL_SETOPTIE, &iwr) < 0) {
+ perror("ioctl[IEEE80211_IOCTL_SETOPTIE]");
+ return -1;
+ }
+ return 0;
+}
+
+static int
+wpa_driver_madwifi_del_key(struct wpa_driver_madwifi_data *drv, int key_idx,
+ const u8 *addr)
+{
+ struct ieee80211req_del_key wk;
+
+ wpa_printf(MSG_DEBUG, "%s: keyidx=%d", __FUNCTION__, key_idx);
+ os_memset(&wk, 0, sizeof(wk));
+ wk.idk_keyix = key_idx;
+ if (addr != NULL)
+ os_memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN);
+
+ return set80211priv(drv, IEEE80211_IOCTL_DELKEY, &wk, sizeof(wk), 1);
+}
+
+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 wpa_driver_madwifi_data *drv = priv;
+ struct ieee80211req_key wk;
+ char *alg_name;
+ u_int8_t cipher;
+
+ if (alg == WPA_ALG_NONE)
+ return wpa_driver_madwifi_del_key(drv, key_idx, addr);
+
+ switch (alg) {
+ case WPA_ALG_WEP:
+ if (addr == NULL || os_memcmp(addr, "\xff\xff\xff\xff\xff\xff",
+ ETH_ALEN) == 0) {
+ /*
+ * madwifi did not seem to like static WEP key
+ * configuration with IEEE80211_IOCTL_SETKEY, so use
+ * Linux wireless extensions ioctl for this.
+ */
+ return wpa_driver_wext_set_key(ifname, drv->wext, alg,
+ addr, key_idx, set_tx,
+ seq, seq_len,
+ key, key_len);
+ }
+ alg_name = "WEP";
+ cipher = IEEE80211_CIPHER_WEP;
+ break;
+ case WPA_ALG_TKIP:
+ alg_name = "TKIP";
+ cipher = IEEE80211_CIPHER_TKIP;
+ break;
+ case WPA_ALG_CCMP:
+ alg_name = "CCMP";
+ cipher = IEEE80211_CIPHER_AES_CCM;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "%s: unknown/unsupported algorithm %d",
+ __FUNCTION__, alg);
+ 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 > sizeof(u_int64_t)) {
+ wpa_printf(MSG_DEBUG, "%s: seq_len %lu too big",
+ __FUNCTION__, (unsigned long) seq_len);
+ return -2;
+ }
+ if (key_len > sizeof(wk.ik_keydata)) {
+ wpa_printf(MSG_DEBUG, "%s: key length %lu too big",
+ __FUNCTION__, (unsigned long) key_len);
+ return -3;
+ }
+
+ os_memset(&wk, 0, sizeof(wk));
+ wk.ik_type = cipher;
+ wk.ik_flags = IEEE80211_KEY_RECV;
+ if (addr == NULL ||
+ os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0)
+ wk.ik_flags |= IEEE80211_KEY_GROUP;
+ if (set_tx) {
+ wk.ik_flags |= IEEE80211_KEY_XMIT | IEEE80211_KEY_DEFAULT;
+ os_memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN);
+ } else
+ os_memset(wk.ik_macaddr, 0, IEEE80211_ADDR_LEN);
+ wk.ik_keyix = key_idx;
+ wk.ik_keylen = key_len;
+#ifdef WORDS_BIGENDIAN
+ if (seq) {
+ size_t i;
+ u8 tmp[WPA_KEY_RSC_LEN];
+ os_memset(tmp, 0, sizeof(tmp));
+ for (i = 0; i < seq_len; i++)
+ tmp[WPA_KEY_RSC_LEN - i - 1] = seq[i];
+ os_memcpy(&wk.ik_keyrsc, tmp, WPA_KEY_RSC_LEN);
+ }
+#else /* WORDS_BIGENDIAN */
+ if (seq)
+ os_memcpy(&wk.ik_keyrsc, seq, seq_len);
+#endif /* WORDS_BIGENDIAN */
+ os_memcpy(wk.ik_keydata, key, key_len);
+
+ return set80211priv(drv, IEEE80211_IOCTL_SETKEY, &wk, sizeof(wk), 1);
+}
+
+static int
+wpa_driver_madwifi_set_countermeasures(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_COUNTERMEASURES, enabled, 1);
+}
+
+static int
+wpa_driver_madwifi_deauthenticate(void *priv, const u8 *addr, int reason_code)
+{
+ struct wpa_driver_madwifi_data *drv = priv;
+ struct ieee80211req_mlme mlme;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+ mlme.im_op = IEEE80211_MLME_DEAUTH;
+ mlme.im_reason = reason_code;
+ os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+ return set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme), 1);
+}
+
+static int
+wpa_driver_madwifi_disassociate(void *priv, const u8 *addr, int reason_code)
+{
+ struct wpa_driver_madwifi_data *drv = priv;
+ struct ieee80211req_mlme mlme;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+ mlme.im_op = IEEE80211_MLME_DISASSOC;
+ mlme.im_reason = reason_code;
+ os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+ return set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme), 1);
+}
+
+static int
+wpa_driver_madwifi_associate(void *priv,
+ struct wpa_driver_associate_params *params)
+{
+ struct wpa_driver_madwifi_data *drv = priv;
+ struct ieee80211req_mlme mlme;
+ int ret = 0, privacy = 1;
+
+ 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
+ * the scanned node state which holds it. The ssid is
+ * needed to disambiguate an AP that broadcasts multiple
+ * ssid's but uses the same bssid.
+ */
+ /* XXX error handling is wrong but unclear what to do... */
+ if (wpa_driver_madwifi_set_wpa_ie(drv, params->wpa_ie,
+ params->wpa_ie_len) < 0)
+ ret = -1;
+
+ if (params->pairwise_suite == CIPHER_NONE &&
+ params->group_suite == CIPHER_NONE &&
+ params->key_mgmt_suite == KEY_MGMT_NONE &&
+ params->wpa_ie_len == 0)
+ privacy = 0;
+
+ if (set80211param(drv, IEEE80211_PARAM_PRIVACY, privacy, 1) < 0)
+ ret = -1;
+
+ if (params->wpa_ie_len &&
+ set80211param(drv, IEEE80211_PARAM_WPA,
+ params->wpa_ie[0] == WLAN_EID_RSN ? 2 : 1, 1) < 0)
+ ret = -1;
+
+ if (params->bssid == NULL) {
+ /* ap_scan=2 mode - driver takes care of AP selection and
+ * roaming */
+ /* FIX: this does not seem to work; would probably need to
+ * change something in the driver */
+ if (set80211param(drv, IEEE80211_PARAM_ROAMING, 0, 1) < 0)
+ ret = -1;
+
+ if (wpa_driver_wext_set_ssid(drv->wext, params->ssid,
+ params->ssid_len) < 0)
+ ret = -1;
+ } else {
+ if (set80211param(drv, IEEE80211_PARAM_ROAMING, 2, 1) < 0)
+ ret = -1;
+ if (wpa_driver_wext_set_ssid(drv->wext, params->ssid,
+ params->ssid_len) < 0)
+ ret = -1;
+ os_memset(&mlme, 0, sizeof(mlme));
+ mlme.im_op = IEEE80211_MLME_ASSOC;
+ os_memcpy(mlme.im_macaddr, params->bssid, IEEE80211_ADDR_LEN);
+ if (set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme,
+ sizeof(mlme), 1) < 0) {
+ wpa_printf(MSG_DEBUG, "%s: SETMLME[ASSOC] failed",
+ __func__);
+ ret = -1;
+ }
+ }
+
+ return ret;
+}
+
+static int
+wpa_driver_madwifi_set_auth_alg(void *priv, int auth_alg)
+{
+ struct wpa_driver_madwifi_data *drv = priv;
+ 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(drv, IEEE80211_PARAM_AUTHMODE, authmode, 1);
+}
+
+static int
+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);
+
+ /* set desired ssid before scan */
+ /* FIX: scan should not break the current association, so using
+ * set_ssid may not be the best way of doing this.. */
+ if (wpa_driver_wext_set_ssid(drv->wext, ssid, ssid_len) < 0)
+ ret = -1;
+
+ if (ioctl(drv->sock, SIOCSIWSCAN, &iwr) < 0) {
+ perror("ioctl[SIOCSIWSCAN]");
+ ret = -1;
+ }
+
+ /*
+ * madwifi delivers a scan complete event so no need to poll, but
+ * register a backup timeout anyway to make sure that we recover even
+ * if the driver does not send this event for any reason. This timeout
+ * will only be used if the event is not delivered (event handler will
+ * cancel the timeout).
+ */
+ eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv->wext,
+ drv->ctx);
+ eloop_register_timeout(30, 0, wpa_driver_wext_scan_timeout, drv->wext,
+ drv->ctx);
+
+ return ret;
+}
+
+static int wpa_driver_madwifi_get_bssid(void *priv, u8 *bssid)
+{
+ struct wpa_driver_madwifi_data *drv = priv;
+ return wpa_driver_wext_get_bssid(drv->wext, bssid);
+}
+
+
+static int wpa_driver_madwifi_get_ssid(void *priv, u8 *ssid)
+{
+ struct wpa_driver_madwifi_data *drv = priv;
+ return wpa_driver_wext_get_ssid(drv->wext, ssid);
+}
+
+
+static struct wpa_scan_results *
+wpa_driver_madwifi_get_scan_results(void *priv)
+{
+ struct wpa_driver_madwifi_data *drv = priv;
+ return wpa_driver_wext_get_scan_results(drv->wext);
+}
+
+
+static int wpa_driver_madwifi_set_operstate(void *priv, int state)
+{
+ struct wpa_driver_madwifi_data *drv = priv;
+ return wpa_driver_wext_set_operstate(drv->wext, state);
+}
+
+
+static int wpa_driver_madwifi_set_probe_req_ie(void *priv, const u8 *ies,
+ size_t ies_len)
+{
+ struct ieee80211req_getset_appiebuf *probe_req_ie;
+ int ret;
+
+ probe_req_ie = os_malloc(sizeof(*probe_req_ie) + ies_len);
+ if (probe_req_ie == NULL)
+ return -1;
+
+ probe_req_ie->app_frmtype = IEEE80211_APPIE_FRAME_PROBE_REQ;
+ probe_req_ie->app_buflen = ies_len;
+ os_memcpy(probe_req_ie->app_buf, ies, ies_len);
+
+ ret = set80211priv(priv, IEEE80211_IOCTL_SET_APPIEBUF, probe_req_ie,
+ sizeof(struct ieee80211req_getset_appiebuf) +
+ ies_len, 1);
+
+ os_free(probe_req_ie);
+
+ return ret;
+}
+
+
+static void * wpa_driver_madwifi_init(void *ctx, const char *ifname)
+{
+ struct wpa_driver_madwifi_data *drv;
+
+ drv = os_zalloc(sizeof(*drv));
+ if (drv == NULL)
+ return NULL;
+ drv->wext = wpa_driver_wext_init(ctx, ifname);
+ if (drv->wext == NULL)
+ goto fail;
+
+ drv->ctx = ctx;
+ os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+ drv->sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (drv->sock < 0)
+ goto fail2;
+
+ if (set80211param(drv, IEEE80211_PARAM_ROAMING, 2, 1) < 0) {
+ wpa_printf(MSG_DEBUG, "%s: failed to set wpa_supplicant-based "
+ "roaming", __FUNCTION__);
+ goto fail3;
+ }
+
+ if (set80211param(drv, IEEE80211_PARAM_WPA, 3, 1) < 0) {
+ wpa_printf(MSG_DEBUG, "%s: failed to enable WPA support",
+ __FUNCTION__);
+ goto fail3;
+ }
+
+ return drv;
+
+fail3:
+ close(drv->sock);
+fail2:
+ wpa_driver_wext_deinit(drv->wext);
+fail:
+ os_free(drv);
+ return NULL;
+}
+
+
+static void wpa_driver_madwifi_deinit(void *priv)
+{
+ struct wpa_driver_madwifi_data *drv = priv;
+
+ if (wpa_driver_madwifi_set_wpa_ie(drv, NULL, 0) < 0) {
+ wpa_printf(MSG_DEBUG, "%s: failed to clear WPA IE",
+ __FUNCTION__);
+ }
+ if (set80211param(drv, IEEE80211_PARAM_ROAMING, 0, 1) < 0) {
+ wpa_printf(MSG_DEBUG, "%s: failed to enable driver-based "
+ "roaming", __FUNCTION__);
+ }
+ if (set80211param(drv, IEEE80211_PARAM_PRIVACY, 0, 1) < 0) {
+ wpa_printf(MSG_DEBUG, "%s: failed to disable forced Privacy "
+ "flag", __FUNCTION__);
+ }
+ if (set80211param(drv, IEEE80211_PARAM_WPA, 0, 1) < 0) {
+ wpa_printf(MSG_DEBUG, "%s: failed to disable WPA",
+ __FUNCTION__);
+ }
+
+ wpa_driver_wext_deinit(drv->wext);
+
+ close(drv->sock);
+ 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,
+ .init = wpa_driver_madwifi_init,
+ .deinit = wpa_driver_madwifi_deinit,
+ .set_countermeasures = wpa_driver_madwifi_set_countermeasures,
+ .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_operstate = wpa_driver_madwifi_set_operstate,
+#endif /* HOSTAPD */
+};
diff --git a/src/drivers/driver_ndis.c b/src/drivers/driver_ndis.c
new file mode 100644
index 0000000..aeb7304
--- /dev/null
+++ b/src/drivers/driver_ndis.c
@@ -0,0 +1,3331 @@
+/*
+ * WPA Supplicant - Windows/NDIS driver interface
+ * 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
+ * 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.
+ */
+
+#ifdef __CYGWIN__
+/* Avoid some header file conflicts by not including standard headers for
+ * cygwin builds when Packet32.h is included. */
+#include "build_config.h"
+int close(int fd);
+#else /* __CYGWIN__ */
+#include "includes.h"
+#endif /* __CYGWIN__ */
+#ifdef CONFIG_USE_NDISUIO
+#include <winsock2.h>
+#else /* CONFIG_USE_NDISUIO */
+#include <Packet32.h>
+#endif /* CONFIG_USE_NDISUIO */
+#ifdef __MINGW32_VERSION
+#include <ddk/ntddndis.h>
+#else /* __MINGW32_VERSION */
+#include <ntddndis.h>
+#endif /* __MINGW32_VERSION */
+
+#ifdef _WIN32_WCE
+#include <winioctl.h>
+#include <nuiouser.h>
+#include <devload.h>
+#endif /* _WIN32_WCE */
+
+#include "common.h"
+#include "driver.h"
+#include "eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "driver_ndis.h"
+
+int wpa_driver_register_event_cb(struct wpa_driver_ndis_data *drv);
+#ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+void wpa_driver_ndis_event_pipe_cb(void *eloop_data, void *user_data);
+#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
+
+static void wpa_driver_ndis_deinit(void *priv);
+static void wpa_driver_ndis_poll(void *drv);
+static void wpa_driver_ndis_poll_timeout(void *eloop_ctx, void *timeout_ctx);
+static int wpa_driver_ndis_adapter_init(struct wpa_driver_ndis_data *drv);
+static int wpa_driver_ndis_adapter_open(struct wpa_driver_ndis_data *drv);
+static void wpa_driver_ndis_adapter_close(struct wpa_driver_ndis_data *drv);
+
+
+static const u8 pae_group_addr[ETH_ALEN] =
+{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
+
+
+/* FIX: to be removed once this can be compiled with the complete NDIS
+ * header files */
+#ifndef OID_802_11_BSSID
+#define OID_802_11_BSSID 0x0d010101
+#define OID_802_11_SSID 0x0d010102
+#define OID_802_11_INFRASTRUCTURE_MODE 0x0d010108
+#define OID_802_11_ADD_WEP 0x0D010113
+#define OID_802_11_REMOVE_WEP 0x0D010114
+#define OID_802_11_DISASSOCIATE 0x0D010115
+#define OID_802_11_BSSID_LIST 0x0d010217
+#define OID_802_11_AUTHENTICATION_MODE 0x0d010118
+#define OID_802_11_PRIVACY_FILTER 0x0d010119
+#define OID_802_11_BSSID_LIST_SCAN 0x0d01011A
+#define OID_802_11_WEP_STATUS 0x0d01011B
+#define OID_802_11_ENCRYPTION_STATUS OID_802_11_WEP_STATUS
+#define OID_802_11_ADD_KEY 0x0d01011D
+#define OID_802_11_REMOVE_KEY 0x0d01011E
+#define OID_802_11_ASSOCIATION_INFORMATION 0x0d01011F
+#define OID_802_11_TEST 0x0d010120
+#define OID_802_11_CAPABILITY 0x0d010122
+#define OID_802_11_PMKID 0x0d010123
+
+#define NDIS_802_11_LENGTH_SSID 32
+#define NDIS_802_11_LENGTH_RATES 8
+#define NDIS_802_11_LENGTH_RATES_EX 16
+
+typedef UCHAR NDIS_802_11_MAC_ADDRESS[6];
+
+typedef struct NDIS_802_11_SSID {
+ ULONG SsidLength;
+ UCHAR Ssid[NDIS_802_11_LENGTH_SSID];
+} NDIS_802_11_SSID;
+
+typedef LONG NDIS_802_11_RSSI;
+
+typedef enum NDIS_802_11_NETWORK_TYPE {
+ Ndis802_11FH,
+ Ndis802_11DS,
+ Ndis802_11OFDM5,
+ Ndis802_11OFDM24,
+ Ndis802_11NetworkTypeMax
+} NDIS_802_11_NETWORK_TYPE;
+
+typedef struct NDIS_802_11_CONFIGURATION_FH {
+ ULONG Length;
+ ULONG HopPattern;
+ ULONG HopSet;
+ ULONG DwellTime;
+} NDIS_802_11_CONFIGURATION_FH;
+
+typedef struct NDIS_802_11_CONFIGURATION {
+ ULONG Length;
+ ULONG BeaconPeriod;
+ ULONG ATIMWindow;
+ ULONG DSConfig;
+ NDIS_802_11_CONFIGURATION_FH FHConfig;
+} NDIS_802_11_CONFIGURATION;
+
+typedef enum NDIS_802_11_NETWORK_INFRASTRUCTURE {
+ Ndis802_11IBSS,
+ Ndis802_11Infrastructure,
+ Ndis802_11AutoUnknown,
+ Ndis802_11InfrastructureMax
+} NDIS_802_11_NETWORK_INFRASTRUCTURE;
+
+typedef enum NDIS_802_11_AUTHENTICATION_MODE {
+ Ndis802_11AuthModeOpen,
+ Ndis802_11AuthModeShared,
+ Ndis802_11AuthModeAutoSwitch,
+ Ndis802_11AuthModeWPA,
+ Ndis802_11AuthModeWPAPSK,
+ Ndis802_11AuthModeWPANone,
+ Ndis802_11AuthModeWPA2,
+ Ndis802_11AuthModeWPA2PSK,
+ Ndis802_11AuthModeMax
+} NDIS_802_11_AUTHENTICATION_MODE;
+
+typedef enum NDIS_802_11_WEP_STATUS {
+ Ndis802_11WEPEnabled,
+ Ndis802_11Encryption1Enabled = Ndis802_11WEPEnabled,
+ Ndis802_11WEPDisabled,
+ Ndis802_11EncryptionDisabled = Ndis802_11WEPDisabled,
+ Ndis802_11WEPKeyAbsent,
+ Ndis802_11Encryption1KeyAbsent = Ndis802_11WEPKeyAbsent,
+ Ndis802_11WEPNotSupported,
+ Ndis802_11EncryptionNotSupported = Ndis802_11WEPNotSupported,
+ Ndis802_11Encryption2Enabled,
+ Ndis802_11Encryption2KeyAbsent,
+ Ndis802_11Encryption3Enabled,
+ Ndis802_11Encryption3KeyAbsent
+} NDIS_802_11_WEP_STATUS, NDIS_802_11_ENCRYPTION_STATUS;
+
+typedef enum NDIS_802_11_PRIVACY_FILTER {
+ Ndis802_11PrivFilterAcceptAll,
+ Ndis802_11PrivFilter8021xWEP
+} NDIS_802_11_PRIVACY_FILTER;
+
+typedef UCHAR NDIS_802_11_RATES[NDIS_802_11_LENGTH_RATES];
+typedef UCHAR NDIS_802_11_RATES_EX[NDIS_802_11_LENGTH_RATES_EX];
+
+typedef struct NDIS_WLAN_BSSID_EX {
+ ULONG Length;
+ NDIS_802_11_MAC_ADDRESS MacAddress; /* BSSID */
+ UCHAR Reserved[2];
+ NDIS_802_11_SSID Ssid;
+ ULONG Privacy;
+ NDIS_802_11_RSSI Rssi;
+ NDIS_802_11_NETWORK_TYPE NetworkTypeInUse;
+ NDIS_802_11_CONFIGURATION Configuration;
+ NDIS_802_11_NETWORK_INFRASTRUCTURE InfrastructureMode;
+ NDIS_802_11_RATES_EX SupportedRates;
+ ULONG IELength;
+ UCHAR IEs[1];
+} NDIS_WLAN_BSSID_EX;
+
+typedef struct NDIS_802_11_BSSID_LIST_EX {
+ ULONG NumberOfItems;
+ NDIS_WLAN_BSSID_EX Bssid[1];
+} NDIS_802_11_BSSID_LIST_EX;
+
+typedef struct NDIS_802_11_FIXED_IEs {
+ UCHAR Timestamp[8];
+ USHORT BeaconInterval;
+ USHORT Capabilities;
+} NDIS_802_11_FIXED_IEs;
+
+typedef struct NDIS_802_11_WEP {
+ ULONG Length;
+ ULONG KeyIndex;
+ ULONG KeyLength;
+ UCHAR KeyMaterial[1];
+} NDIS_802_11_WEP;
+
+typedef ULONG NDIS_802_11_KEY_INDEX;
+typedef ULONGLONG NDIS_802_11_KEY_RSC;
+
+typedef struct NDIS_802_11_KEY {
+ ULONG Length;
+ ULONG KeyIndex;
+ ULONG KeyLength;
+ NDIS_802_11_MAC_ADDRESS BSSID;
+ NDIS_802_11_KEY_RSC KeyRSC;
+ UCHAR KeyMaterial[1];
+} NDIS_802_11_KEY;
+
+typedef struct NDIS_802_11_REMOVE_KEY {
+ ULONG Length;
+ ULONG KeyIndex;
+ NDIS_802_11_MAC_ADDRESS BSSID;
+} NDIS_802_11_REMOVE_KEY;
+
+typedef struct NDIS_802_11_AI_REQFI {
+ USHORT Capabilities;
+ USHORT ListenInterval;
+ NDIS_802_11_MAC_ADDRESS CurrentAPAddress;
+} NDIS_802_11_AI_REQFI;
+
+typedef struct NDIS_802_11_AI_RESFI {
+ USHORT Capabilities;
+ USHORT StatusCode;
+ USHORT AssociationId;
+} NDIS_802_11_AI_RESFI;
+
+typedef struct NDIS_802_11_ASSOCIATION_INFORMATION {
+ ULONG Length;
+ USHORT AvailableRequestFixedIEs;
+ NDIS_802_11_AI_REQFI RequestFixedIEs;
+ ULONG RequestIELength;
+ ULONG OffsetRequestIEs;
+ USHORT AvailableResponseFixedIEs;
+ NDIS_802_11_AI_RESFI ResponseFixedIEs;
+ ULONG ResponseIELength;
+ ULONG OffsetResponseIEs;
+} NDIS_802_11_ASSOCIATION_INFORMATION;
+
+typedef struct NDIS_802_11_AUTHENTICATION_ENCRYPTION {
+ NDIS_802_11_AUTHENTICATION_MODE AuthModeSupported;
+ NDIS_802_11_ENCRYPTION_STATUS EncryptStatusSupported;
+} NDIS_802_11_AUTHENTICATION_ENCRYPTION;
+
+typedef struct NDIS_802_11_CAPABILITY {
+ ULONG Length;
+ ULONG Version;
+ ULONG NoOfPMKIDs;
+ ULONG NoOfAuthEncryptPairsSupported;
+ NDIS_802_11_AUTHENTICATION_ENCRYPTION
+ AuthenticationEncryptionSupported[1];
+} NDIS_802_11_CAPABILITY;
+
+typedef UCHAR NDIS_802_11_PMKID_VALUE[16];
+
+typedef struct BSSID_INFO {
+ NDIS_802_11_MAC_ADDRESS BSSID;
+ NDIS_802_11_PMKID_VALUE PMKID;
+} BSSID_INFO;
+
+typedef struct NDIS_802_11_PMKID {
+ ULONG Length;
+ ULONG BSSIDInfoCount;
+ BSSID_INFO BSSIDInfo[1];
+} NDIS_802_11_PMKID;
+
+typedef enum NDIS_802_11_STATUS_TYPE {
+ Ndis802_11StatusType_Authentication,
+ Ndis802_11StatusType_PMKID_CandidateList = 2,
+ Ndis802_11StatusTypeMax
+} NDIS_802_11_STATUS_TYPE;
+
+typedef struct NDIS_802_11_STATUS_INDICATION {
+ NDIS_802_11_STATUS_TYPE StatusType;
+} NDIS_802_11_STATUS_INDICATION;
+
+typedef struct PMKID_CANDIDATE {
+ NDIS_802_11_MAC_ADDRESS BSSID;
+ ULONG Flags;
+} PMKID_CANDIDATE;
+
+#define NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED 0x01
+
+typedef struct NDIS_802_11_PMKID_CANDIDATE_LIST {
+ ULONG Version;
+ ULONG NumCandidates;
+ PMKID_CANDIDATE CandidateList[1];
+} NDIS_802_11_PMKID_CANDIDATE_LIST;
+
+typedef struct NDIS_802_11_AUTHENTICATION_REQUEST {
+ ULONG Length;
+ NDIS_802_11_MAC_ADDRESS Bssid;
+ ULONG Flags;
+} NDIS_802_11_AUTHENTICATION_REQUEST;
+
+#define NDIS_802_11_AUTH_REQUEST_REAUTH 0x01
+#define NDIS_802_11_AUTH_REQUEST_KEYUPDATE 0x02
+#define NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR 0x06
+#define NDIS_802_11_AUTH_REQUEST_GROUP_ERROR 0x0E
+
+#endif /* OID_802_11_BSSID */
+
+
+#ifndef OID_802_11_PMKID
+/* Platform SDK for XP did not include WPA2, so add needed definitions */
+
+#define OID_802_11_CAPABILITY 0x0d010122
+#define OID_802_11_PMKID 0x0d010123
+
+#define Ndis802_11AuthModeWPA2 6
+#define Ndis802_11AuthModeWPA2PSK 7
+
+#define Ndis802_11StatusType_PMKID_CandidateList 2
+
+typedef struct NDIS_802_11_AUTHENTICATION_ENCRYPTION {
+ NDIS_802_11_AUTHENTICATION_MODE AuthModeSupported;
+ NDIS_802_11_ENCRYPTION_STATUS EncryptStatusSupported;
+} NDIS_802_11_AUTHENTICATION_ENCRYPTION;
+
+typedef struct NDIS_802_11_CAPABILITY {
+ ULONG Length;
+ ULONG Version;
+ ULONG NoOfPMKIDs;
+ ULONG NoOfAuthEncryptPairsSupported;
+ NDIS_802_11_AUTHENTICATION_ENCRYPTION
+ AuthenticationEncryptionSupported[1];
+} NDIS_802_11_CAPABILITY;
+
+typedef UCHAR NDIS_802_11_PMKID_VALUE[16];
+
+typedef struct BSSID_INFO {
+ NDIS_802_11_MAC_ADDRESS BSSID;
+ NDIS_802_11_PMKID_VALUE PMKID;
+} BSSID_INFO;
+
+typedef struct NDIS_802_11_PMKID {
+ ULONG Length;
+ ULONG BSSIDInfoCount;
+ BSSID_INFO BSSIDInfo[1];
+} NDIS_802_11_PMKID;
+
+typedef struct PMKID_CANDIDATE {
+ NDIS_802_11_MAC_ADDRESS BSSID;
+ ULONG Flags;
+} PMKID_CANDIDATE;
+
+#define NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED 0x01
+
+typedef struct NDIS_802_11_PMKID_CANDIDATE_LIST {
+ ULONG Version;
+ ULONG NumCandidates;
+ PMKID_CANDIDATE CandidateList[1];
+} 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
+typedef ULONG NDIS_OID;
+#endif /* __MINGW32_VERSION */
+/* from nuiouser.h */
+#define FSCTL_NDISUIO_BASE FILE_DEVICE_NETWORK
+
+#define _NDISUIO_CTL_CODE(_Function, _Method, _Access) \
+ CTL_CODE(FSCTL_NDISUIO_BASE, _Function, _Method, _Access)
+
+#define IOCTL_NDISUIO_OPEN_DEVICE \
+ _NDISUIO_CTL_CODE(0x200, METHOD_BUFFERED, \
+ FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+#define IOCTL_NDISUIO_QUERY_OID_VALUE \
+ _NDISUIO_CTL_CODE(0x201, METHOD_BUFFERED, \
+ FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+#define IOCTL_NDISUIO_SET_OID_VALUE \
+ _NDISUIO_CTL_CODE(0x205, METHOD_BUFFERED, \
+ FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+#define IOCTL_NDISUIO_SET_ETHER_TYPE \
+ _NDISUIO_CTL_CODE(0x202, METHOD_BUFFERED, \
+ FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+#define IOCTL_NDISUIO_QUERY_BINDING \
+ _NDISUIO_CTL_CODE(0x203, METHOD_BUFFERED, \
+ FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+#define IOCTL_NDISUIO_BIND_WAIT \
+ _NDISUIO_CTL_CODE(0x204, METHOD_BUFFERED, \
+ FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+typedef struct _NDISUIO_QUERY_OID
+{
+ NDIS_OID Oid;
+ UCHAR Data[sizeof(ULONG)];
+} NDISUIO_QUERY_OID, *PNDISUIO_QUERY_OID;
+
+typedef struct _NDISUIO_SET_OID
+{
+ NDIS_OID Oid;
+ UCHAR Data[sizeof(ULONG)];
+} NDISUIO_SET_OID, *PNDISUIO_SET_OID;
+
+typedef struct _NDISUIO_QUERY_BINDING
+{
+ ULONG BindingIndex;
+ ULONG DeviceNameOffset;
+ ULONG DeviceNameLength;
+ ULONG DeviceDescrOffset;
+ ULONG DeviceDescrLength;
+} NDISUIO_QUERY_BINDING, *PNDISUIO_QUERY_BINDING;
+#endif /* _WIN32_WCE */
+#endif /* CONFIG_USE_NDISUIO */
+
+
+static int ndis_get_oid(struct wpa_driver_ndis_data *drv, unsigned int oid,
+ char *data, size_t len)
+{
+#ifdef CONFIG_USE_NDISUIO
+ NDISUIO_QUERY_OID *o;
+ size_t buflen = sizeof(*o) + len;
+ DWORD written;
+ int ret;
+ size_t hdrlen;
+
+ o = os_zalloc(buflen);
+ if (o == NULL)
+ return -1;
+ o->Oid = oid;
+#ifdef _WIN32_WCE
+ o->ptcDeviceName = drv->adapter_name;
+#endif /* _WIN32_WCE */
+ if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_QUERY_OID_VALUE,
+ o, sizeof(NDISUIO_QUERY_OID), o, buflen, &written,
+ NULL)) {
+ wpa_printf(MSG_DEBUG, "NDIS: IOCTL_NDISUIO_QUERY_OID_VALUE "
+ "failed (oid=%08x): %d", oid, (int) GetLastError());
+ os_free(o);
+ return -1;
+ }
+ hdrlen = sizeof(NDISUIO_QUERY_OID) - sizeof(o->Data);
+ if (written < hdrlen) {
+ wpa_printf(MSG_DEBUG, "NDIS: query oid=%08x written (%d); "
+ "too short", oid, (unsigned int) written);
+ os_free(o);
+ return -1;
+ }
+ written -= hdrlen;
+ if (written > len) {
+ wpa_printf(MSG_DEBUG, "NDIS: query oid=%08x written (%d) > "
+ "len (%d)",oid, (unsigned int) written, len);
+ os_free(o);
+ return -1;
+ }
+ os_memcpy(data, o->Data, written);
+ ret = written;
+ os_free(o);
+ return ret;
+#else /* CONFIG_USE_NDISUIO */
+ char *buf;
+ PACKET_OID_DATA *o;
+ int ret;
+
+ buf = os_zalloc(sizeof(*o) + len);
+ if (buf == NULL)
+ return -1;
+ o = (PACKET_OID_DATA *) buf;
+ o->Oid = oid;
+ o->Length = len;
+
+ if (!PacketRequest(drv->adapter, FALSE, o)) {
+ wpa_printf(MSG_DEBUG, "%s: oid=0x%x len (%d) failed",
+ __func__, oid, len);
+ os_free(buf);
+ return -1;
+ }
+ if (o->Length > len) {
+ wpa_printf(MSG_DEBUG, "%s: oid=0x%x Length (%d) > len (%d)",
+ __func__, oid, (unsigned int) o->Length, len);
+ os_free(buf);
+ return -1;
+ }
+ os_memcpy(data, o->Data, o->Length);
+ ret = o->Length;
+ os_free(buf);
+ return ret;
+#endif /* CONFIG_USE_NDISUIO */
+}
+
+
+static int ndis_set_oid(struct wpa_driver_ndis_data *drv, unsigned int oid,
+ const char *data, size_t len)
+{
+#ifdef CONFIG_USE_NDISUIO
+ NDISUIO_SET_OID *o;
+ size_t buflen, reallen;
+ DWORD written;
+ char txt[50];
+
+ os_snprintf(txt, sizeof(txt), "NDIS: Set OID %08x", oid);
+ wpa_hexdump_key(MSG_MSGDUMP, txt, (const u8 *) data, len);
+
+ buflen = sizeof(*o) + len;
+ reallen = buflen - sizeof(o->Data);
+ o = os_zalloc(buflen);
+ if (o == NULL)
+ return -1;
+ o->Oid = oid;
+#ifdef _WIN32_WCE
+ o->ptcDeviceName = drv->adapter_name;
+#endif /* _WIN32_WCE */
+ if (data)
+ os_memcpy(o->Data, data, len);
+ if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_SET_OID_VALUE,
+ o, reallen, NULL, 0, &written, NULL)) {
+ wpa_printf(MSG_DEBUG, "NDIS: IOCTL_NDISUIO_SET_OID_VALUE "
+ "(oid=%08x) failed: %d", oid, (int) GetLastError());
+ os_free(o);
+ return -1;
+ }
+ os_free(o);
+ return 0;
+#else /* CONFIG_USE_NDISUIO */
+ char *buf;
+ PACKET_OID_DATA *o;
+ char txt[50];
+
+ os_snprintf(txt, sizeof(txt), "NDIS: Set OID %08x", oid);
+ wpa_hexdump_key(MSG_MSGDUMP, txt, (const u8 *) data, len);
+
+ buf = os_zalloc(sizeof(*o) + len);
+ if (buf == NULL)
+ return -1;
+ o = (PACKET_OID_DATA *) buf;
+ o->Oid = oid;
+ o->Length = len;
+ if (data)
+ os_memcpy(o->Data, data, len);
+
+ if (!PacketRequest(drv->adapter, TRUE, o)) {
+ wpa_printf(MSG_DEBUG, "%s: oid=0x%x len (%d) failed",
+ __func__, oid, len);
+ os_free(buf);
+ return -1;
+ }
+ os_free(buf);
+ return 0;
+#endif /* CONFIG_USE_NDISUIO */
+}
+
+
+static int ndis_set_auth_mode(struct wpa_driver_ndis_data *drv, int mode)
+{
+ u32 auth_mode = mode;
+ if (ndis_set_oid(drv, OID_802_11_AUTHENTICATION_MODE,
+ (char *) &auth_mode, sizeof(auth_mode)) < 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: Failed to set "
+ "OID_802_11_AUTHENTICATION_MODE (%d)",
+ (int) auth_mode);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int ndis_get_auth_mode(struct wpa_driver_ndis_data *drv)
+{
+ u32 auth_mode;
+ int res;
+ res = ndis_get_oid(drv, OID_802_11_AUTHENTICATION_MODE,
+ (char *) &auth_mode, sizeof(auth_mode));
+ if (res != sizeof(auth_mode)) {
+ wpa_printf(MSG_DEBUG, "NDIS: Failed to get "
+ "OID_802_11_AUTHENTICATION_MODE");
+ return -1;
+ }
+ return auth_mode;
+}
+
+
+static int ndis_set_encr_status(struct wpa_driver_ndis_data *drv, int encr)
+{
+ u32 encr_status = encr;
+ if (ndis_set_oid(drv, OID_802_11_ENCRYPTION_STATUS,
+ (char *) &encr_status, sizeof(encr_status)) < 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: Failed to set "
+ "OID_802_11_ENCRYPTION_STATUS (%d)", encr);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int ndis_get_encr_status(struct wpa_driver_ndis_data *drv)
+{
+ u32 encr;
+ int res;
+ res = ndis_get_oid(drv, OID_802_11_ENCRYPTION_STATUS,
+ (char *) &encr, sizeof(encr));
+ if (res != sizeof(encr)) {
+ wpa_printf(MSG_DEBUG, "NDIS: Failed to get "
+ "OID_802_11_ENCRYPTION_STATUS");
+ return -1;
+ }
+ return encr;
+}
+
+
+static int wpa_driver_ndis_get_bssid(void *priv, u8 *bssid)
+{
+ struct wpa_driver_ndis_data *drv = priv;
+
+ if (drv->wired) {
+ /*
+ * Report PAE group address as the "BSSID" for wired
+ * connection.
+ */
+ os_memcpy(bssid, pae_group_addr, ETH_ALEN);
+ return 0;
+ }
+
+ return ndis_get_oid(drv, OID_802_11_BSSID, (char *) bssid, ETH_ALEN) <
+ 0 ? -1 : 0;
+}
+
+
+static int wpa_driver_ndis_get_ssid(void *priv, u8 *ssid)
+{
+ struct wpa_driver_ndis_data *drv = priv;
+ NDIS_802_11_SSID buf;
+ int res;
+
+ res = ndis_get_oid(drv, OID_802_11_SSID, (char *) &buf, sizeof(buf));
+ if (res < 4) {
+ wpa_printf(MSG_DEBUG, "NDIS: Failed to get SSID");
+ if (drv->wired) {
+ wpa_printf(MSG_DEBUG, "NDIS: Allow get_ssid failure "
+ "with a wired interface");
+ return 0;
+ }
+ return -1;
+ }
+ os_memcpy(ssid, buf.Ssid, buf.SsidLength);
+ return buf.SsidLength;
+}
+
+
+static int wpa_driver_ndis_set_ssid(struct wpa_driver_ndis_data *drv,
+ const u8 *ssid, size_t ssid_len)
+{
+ NDIS_802_11_SSID buf;
+
+ os_memset(&buf, 0, sizeof(buf));
+ buf.SsidLength = ssid_len;
+ os_memcpy(buf.Ssid, ssid, ssid_len);
+ /*
+ * Make sure radio is marked enabled here so that scan request will not
+ * force SSID to be changed to a random one in order to enable radio at
+ * that point.
+ */
+ drv->radio_enabled = 1;
+ return ndis_set_oid(drv, OID_802_11_SSID, (char *) &buf, sizeof(buf));
+}
+
+
+/* Disconnect using OID_802_11_DISASSOCIATE. This will also turn the radio off.
+ */
+static int wpa_driver_ndis_radio_off(struct wpa_driver_ndis_data *drv)
+{
+ drv->radio_enabled = 0;
+ return ndis_set_oid(drv, OID_802_11_DISASSOCIATE, " ", 4);
+}
+
+
+/* Disconnect by setting SSID to random (i.e., likely not used). */
+static int wpa_driver_ndis_disconnect(struct wpa_driver_ndis_data *drv)
+{
+ char ssid[32];
+ int i;
+ for (i = 0; i < 32; i++)
+ ssid[i] = rand() & 0xff;
+ return wpa_driver_ndis_set_ssid(drv, (u8 *) ssid, 32);
+}
+
+
+static int wpa_driver_ndis_deauthenticate(void *priv, const u8 *addr,
+ int reason_code)
+{
+ struct wpa_driver_ndis_data *drv = priv;
+ return wpa_driver_ndis_disconnect(drv);
+}
+
+
+static int wpa_driver_ndis_disassociate(void *priv, const u8 *addr,
+ int reason_code)
+{
+ struct wpa_driver_ndis_data *drv = priv;
+ return wpa_driver_ndis_disconnect(drv);
+}
+
+
+static void wpa_driver_ndis_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
+ wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
+}
+
+
+static int wpa_driver_ndis_scan_native80211(
+ struct wpa_driver_ndis_data *drv,
+ struct wpa_driver_scan_params *params)
+{
+ 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,
+ 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");
+ if (wpa_driver_ndis_disconnect(drv) < 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: failed to enable radio");
+ }
+ drv->radio_enabled = 1;
+ }
+
+ res = ndis_set_oid(drv, OID_802_11_BSSID_LIST_SCAN, " ", 4);
+ 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 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)
+{
+ struct wpa_scan_res *nr;
+ u8 *pos;
+
+ if (wpa_scan_get_ie(r, WLAN_EID_SSID))
+ return r; /* SSID IE already present */
+
+ if (ssid->SsidLength == 0 || ssid->SsidLength > 32)
+ return r; /* No valid SSID inside scan data */
+
+ nr = os_realloc(r, sizeof(*r) + r->ie_len + 2 + ssid->SsidLength);
+ if (nr == NULL)
+ return r;
+
+ pos = ((u8 *) (nr + 1)) + nr->ie_len;
+ *pos++ = WLAN_EID_SSID;
+ *pos++ = ssid->SsidLength;
+ os_memcpy(pos, ssid->Ssid, ssid->SsidLength);
+ nr->ie_len += 2 + ssid->SsidLength;
+
+ return nr;
+}
+
+
+static struct wpa_scan_results * wpa_driver_ndis_get_scan_results(void *priv)
+{
+ struct wpa_driver_ndis_data *drv = priv;
+ NDIS_802_11_BSSID_LIST_EX *b;
+ size_t blen, count, i;
+ int len;
+ char *pos;
+ struct wpa_scan_results *results;
+ struct wpa_scan_res *r;
+
+ blen = 65535;
+ b = os_zalloc(blen);
+ if (b == NULL)
+ return NULL;
+ len = ndis_get_oid(drv, OID_802_11_BSSID_LIST, (char *) b, blen);
+ if (len < 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: failed to get scan results");
+ os_free(b);
+ return NULL;
+ }
+ count = b->NumberOfItems;
+
+ results = os_zalloc(sizeof(*results));
+ if (results == NULL) {
+ os_free(b);
+ return NULL;
+ }
+ results->res = os_zalloc(count * sizeof(struct wpa_scan_res *));
+ if (results->res == NULL) {
+ os_free(results);
+ os_free(b);
+ return NULL;
+ }
+
+ pos = (char *) &b->Bssid[0];
+ for (i = 0; i < count; i++) {
+ NDIS_WLAN_BSSID_EX *bss = (NDIS_WLAN_BSSID_EX *) pos;
+ NDIS_802_11_FIXED_IEs *fixed;
+
+ if (bss->IELength < sizeof(NDIS_802_11_FIXED_IEs)) {
+ wpa_printf(MSG_DEBUG, "NDIS: too small IELength=%d",
+ (int) bss->IELength);
+ break;
+ }
+ if (((char *) bss->IEs) + bss->IELength > (char *) b + blen) {
+ /*
+ * Some NDIS drivers have been reported to include an
+ * entry with an invalid IELength in scan results and
+ * this has crashed wpa_supplicant, so validate the
+ * returned value before using it.
+ */
+ wpa_printf(MSG_DEBUG, "NDIS: skipped invalid scan "
+ "result IE (BSSID=" MACSTR ") IELength=%d",
+ MAC2STR(bss->MacAddress),
+ (int) bss->IELength);
+ break;
+ }
+
+ r = os_zalloc(sizeof(*r) + bss->IELength -
+ sizeof(NDIS_802_11_FIXED_IEs));
+ if (r == NULL)
+ break;
+
+ os_memcpy(r->bssid, bss->MacAddress, ETH_ALEN);
+ r->level = (int) bss->Rssi;
+ r->freq = bss->Configuration.DSConfig / 1000;
+ fixed = (NDIS_802_11_FIXED_IEs *) bss->IEs;
+ r->beacon_int = WPA_GET_LE16((u8 *) &fixed->BeaconInterval);
+ r->caps = WPA_GET_LE16((u8 *) &fixed->Capabilities);
+ r->tsf = WPA_GET_LE64(fixed->Timestamp);
+ os_memcpy(r + 1, bss->IEs + sizeof(NDIS_802_11_FIXED_IEs),
+ bss->IELength - sizeof(NDIS_802_11_FIXED_IEs));
+ r->ie_len = bss->IELength - sizeof(NDIS_802_11_FIXED_IEs);
+ r = wpa_driver_ndis_add_scan_ssid(r, &bss->Ssid);
+
+ results->res[results->num++] = r;
+
+ pos += bss->Length;
+ if (pos > (char *) b + blen)
+ break;
+ }
+
+ os_free(b);
+
+ return results;
+}
+
+
+static int wpa_driver_ndis_remove_key(struct wpa_driver_ndis_data *drv,
+ int key_idx, const u8 *addr,
+ const u8 *bssid, int pairwise)
+{
+ NDIS_802_11_REMOVE_KEY rkey;
+ NDIS_802_11_KEY_INDEX index;
+ int res, res2;
+
+ os_memset(&rkey, 0, sizeof(rkey));
+
+ rkey.Length = sizeof(rkey);
+ rkey.KeyIndex = key_idx;
+ if (pairwise)
+ rkey.KeyIndex |= 1 << 30;
+ os_memcpy(rkey.BSSID, bssid, ETH_ALEN);
+
+ res = ndis_set_oid(drv, OID_802_11_REMOVE_KEY, (char *) &rkey,
+ sizeof(rkey));
+ if (!pairwise) {
+ index = key_idx;
+ res2 = ndis_set_oid(drv, OID_802_11_REMOVE_WEP,
+ (char *) &index, sizeof(index));
+ } else
+ res2 = 0;
+
+ if (res < 0 && res2 < 0)
+ return -1;
+ return 0;
+}
+
+
+static int wpa_driver_ndis_add_wep(struct wpa_driver_ndis_data *drv,
+ int pairwise, int key_idx, int set_tx,
+ const u8 *key, size_t key_len)
+{
+ NDIS_802_11_WEP *wep;
+ size_t len;
+ int res;
+
+ len = 12 + key_len;
+ wep = os_zalloc(len);
+ if (wep == NULL)
+ return -1;
+ wep->Length = len;
+ wep->KeyIndex = key_idx;
+ if (set_tx)
+ wep->KeyIndex |= 1 << 31;
+#if 0 /* Setting bit30 does not seem to work with some NDIS drivers */
+ if (pairwise)
+ wep->KeyIndex |= 1 << 30;
+#endif
+ wep->KeyLength = key_len;
+ os_memcpy(wep->KeyMaterial, key, key_len);
+
+ wpa_hexdump_key(MSG_MSGDUMP, "NDIS: OID_802_11_ADD_WEP",
+ (u8 *) wep, len);
+ res = ndis_set_oid(drv, OID_802_11_ADD_WEP, (char *) wep, len);
+
+ os_free(wep);
+
+ return res;
+}
+
+
+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)
+{
+ struct wpa_driver_ndis_data *drv = priv;
+ size_t len, i;
+ NDIS_802_11_KEY *nkey;
+ int res, pairwise;
+ u8 bssid[ETH_ALEN];
+
+ if (addr == NULL || is_broadcast_ether_addr(addr)) {
+ /* Group Key */
+ pairwise = 0;
+ if (wpa_driver_ndis_get_bssid(drv, bssid) < 0)
+ os_memset(bssid, 0xff, ETH_ALEN);
+ } else {
+ /* Pairwise Key */
+ pairwise = 1;
+ os_memcpy(bssid, addr, ETH_ALEN);
+ }
+
+ if (alg == WPA_ALG_NONE || key_len == 0) {
+ return wpa_driver_ndis_remove_key(drv, key_idx, addr, bssid,
+ pairwise);
+ }
+
+ if (alg == WPA_ALG_WEP) {
+ return wpa_driver_ndis_add_wep(drv, pairwise, key_idx, set_tx,
+ key, key_len);
+ }
+
+ len = 12 + 6 + 6 + 8 + key_len;
+
+ nkey = os_zalloc(len);
+ if (nkey == NULL)
+ return -1;
+
+ nkey->Length = len;
+ nkey->KeyIndex = key_idx;
+ if (set_tx)
+ nkey->KeyIndex |= 1 << 31;
+ if (pairwise)
+ nkey->KeyIndex |= 1 << 30;
+ if (seq && seq_len)
+ nkey->KeyIndex |= 1 << 29;
+ nkey->KeyLength = key_len;
+ os_memcpy(nkey->BSSID, bssid, ETH_ALEN);
+ if (seq && seq_len) {
+ for (i = 0; i < seq_len; i++)
+ nkey->KeyRSC |= (ULONGLONG) seq[i] << (i * 8);
+ }
+ if (alg == WPA_ALG_TKIP && key_len == 32) {
+ os_memcpy(nkey->KeyMaterial, key, 16);
+ os_memcpy(nkey->KeyMaterial + 16, key + 24, 8);
+ os_memcpy(nkey->KeyMaterial + 24, key + 16, 8);
+ } else {
+ os_memcpy(nkey->KeyMaterial, key, key_len);
+ }
+
+ wpa_hexdump_key(MSG_MSGDUMP, "NDIS: OID_802_11_ADD_KEY",
+ (u8 *) nkey, len);
+ res = ndis_set_oid(drv, OID_802_11_ADD_KEY, (char *) nkey, len);
+ os_free(nkey);
+
+ return res;
+}
+
+
+static int
+wpa_driver_ndis_associate(void *priv,
+ struct wpa_driver_associate_params *params)
+{
+ struct wpa_driver_ndis_data *drv = priv;
+ u32 auth_mode, encr, priv_mode, mode;
+ u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+ drv->mode = params->mode;
+
+ /* Note: Setting OID_802_11_INFRASTRUCTURE_MODE clears current keys,
+ * so static WEP keys needs to be set again after this. */
+ if (params->mode == IEEE80211_MODE_IBSS) {
+ mode = Ndis802_11IBSS;
+ /* Need to make sure that BSSID polling is enabled for
+ * IBSS mode. */
+ eloop_cancel_timeout(wpa_driver_ndis_poll_timeout, drv, NULL);
+ eloop_register_timeout(1, 0, wpa_driver_ndis_poll_timeout,
+ drv, NULL);
+ } else
+ mode = Ndis802_11Infrastructure;
+ if (ndis_set_oid(drv, OID_802_11_INFRASTRUCTURE_MODE,
+ (char *) &mode, sizeof(mode)) < 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: Failed to set "
+ "OID_802_11_INFRASTRUCTURE_MODE (%d)",
+ (int) mode);
+ /* Try to continue anyway */
+ }
+
+ if (params->key_mgmt_suite == KEY_MGMT_NONE ||
+ params->key_mgmt_suite == KEY_MGMT_802_1X_NO_WPA) {
+ /* Re-set WEP keys if static WEP configuration is used. */
+ int i;
+ for (i = 0; i < 4; i++) {
+ if (!params->wep_key[i])
+ continue;
+ wpa_printf(MSG_DEBUG, "NDIS: Re-setting static WEP "
+ "key %d", 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]);
+ }
+ }
+
+ 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;
+ priv_mode = Ndis802_11PrivFilterAcceptAll;
+ } else if (params->wpa_ie[0] == WLAN_EID_RSN) {
+ priv_mode = Ndis802_11PrivFilter8021xWEP;
+ if (params->key_mgmt_suite == KEY_MGMT_PSK)
+ auth_mode = Ndis802_11AuthModeWPA2PSK;
+ else
+ auth_mode = Ndis802_11AuthModeWPA2;
+#ifdef CONFIG_WPS
+ } else if (params->key_mgmt_suite == KEY_MGMT_WPS) {
+ auth_mode = Ndis802_11AuthModeOpen;
+ priv_mode = Ndis802_11PrivFilterAcceptAll;
+ if (params->wps == WPS_MODE_PRIVACY) {
+ u8 dummy_key[5] = { 0x11, 0x22, 0x33, 0x44, 0x55 };
+ /*
+ * Some NDIS drivers refuse to associate in open mode
+ * configuration due to Privacy field mismatch, so use
+ * a workaround to make the configuration look like
+ * matching one for WPS provisioning.
+ */
+ wpa_printf(MSG_DEBUG, "NDIS: Set dummy WEP key as a "
+ "workaround to allow driver to associate "
+ "for WPS");
+ wpa_driver_ndis_set_key(drv->ifname, drv, WPA_ALG_WEP,
+ bcast, 0, 1,
+ NULL, 0, dummy_key,
+ sizeof(dummy_key));
+ }
+#endif /* CONFIG_WPS */
+ } else {
+ priv_mode = Ndis802_11PrivFilter8021xWEP;
+ 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:
+#ifdef CONFIG_WPS
+ if (params->wps == WPS_MODE_PRIVACY) {
+ encr = Ndis802_11Encryption1Enabled;
+ break;
+ }
+#endif /* CONFIG_WPS */
+ 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:
+#ifdef CONFIG_WPS
+ if (params->wps == WPS_MODE_PRIVACY) {
+ encr = Ndis802_11Encryption1Enabled;
+ break;
+ }
+#endif /* CONFIG_WPS */
+ encr = Ndis802_11EncryptionDisabled;
+ break;
+ };
+
+ if (ndis_set_oid(drv, OID_802_11_PRIVACY_FILTER,
+ (char *) &priv_mode, sizeof(priv_mode)) < 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: Failed to set "
+ "OID_802_11_PRIVACY_FILTER (%d)",
+ (int) priv_mode);
+ /* Try to continue anyway */
+ }
+
+ ndis_set_auth_mode(drv, auth_mode);
+ ndis_set_encr_status(drv, encr);
+
+ if (params->bssid) {
+ ndis_set_oid(drv, OID_802_11_BSSID, (char *) params->bssid,
+ ETH_ALEN);
+ drv->oid_bssid_set = 1;
+ } else if (drv->oid_bssid_set) {
+ ndis_set_oid(drv, OID_802_11_BSSID, "\xff\xff\xff\xff\xff\xff",
+ ETH_ALEN);
+ drv->oid_bssid_set = 0;
+ }
+
+ return wpa_driver_ndis_set_ssid(drv, params->ssid, params->ssid_len);
+}
+
+
+static int wpa_driver_ndis_set_pmkid(struct wpa_driver_ndis_data *drv)
+{
+ int len, count, i, ret;
+ struct ndis_pmkid_entry *entry;
+ NDIS_802_11_PMKID *p;
+
+ count = 0;
+ entry = drv->pmkid;
+ while (entry) {
+ count++;
+ if (count >= drv->no_of_pmkid)
+ break;
+ entry = entry->next;
+ }
+ len = 8 + count * sizeof(BSSID_INFO);
+ p = os_zalloc(len);
+ if (p == NULL)
+ return -1;
+
+ p->Length = len;
+ p->BSSIDInfoCount = count;
+ entry = drv->pmkid;
+ for (i = 0; i < count; i++) {
+ os_memcpy(&p->BSSIDInfo[i].BSSID, entry->bssid, ETH_ALEN);
+ os_memcpy(&p->BSSIDInfo[i].PMKID, entry->pmkid, 16);
+ entry = entry->next;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "NDIS: OID_802_11_PMKID", (u8 *) p, len);
+ ret = ndis_set_oid(drv, OID_802_11_PMKID, (char *) p, len);
+ os_free(p);
+ return ret;
+}
+
+
+static int wpa_driver_ndis_add_pmkid(void *priv, const u8 *bssid,
+ const u8 *pmkid)
+{
+ struct wpa_driver_ndis_data *drv = priv;
+ struct ndis_pmkid_entry *entry, *prev;
+
+ if (drv->no_of_pmkid == 0)
+ return 0;
+
+ prev = NULL;
+ entry = drv->pmkid;
+ while (entry) {
+ if (os_memcmp(entry->bssid, bssid, ETH_ALEN) == 0)
+ break;
+ prev = entry;
+ entry = entry->next;
+ }
+
+ if (entry) {
+ /* Replace existing entry for this BSSID and move it into the
+ * beginning of the list. */
+ os_memcpy(entry->pmkid, pmkid, 16);
+ if (prev) {
+ prev->next = entry->next;
+ entry->next = drv->pmkid;
+ drv->pmkid = entry;
+ }
+ } else {
+ entry = os_malloc(sizeof(*entry));
+ if (entry) {
+ os_memcpy(entry->bssid, bssid, ETH_ALEN);
+ os_memcpy(entry->pmkid, pmkid, 16);
+ entry->next = drv->pmkid;
+ drv->pmkid = entry;
+ }
+ }
+
+ return wpa_driver_ndis_set_pmkid(drv);
+}
+
+
+static int wpa_driver_ndis_remove_pmkid(void *priv, const u8 *bssid,
+ const u8 *pmkid)
+{
+ struct wpa_driver_ndis_data *drv = priv;
+ struct ndis_pmkid_entry *entry, *prev;
+
+ if (drv->no_of_pmkid == 0)
+ return 0;
+
+ entry = drv->pmkid;
+ prev = NULL;
+ while (entry) {
+ if (os_memcmp(entry->bssid, bssid, ETH_ALEN) == 0 &&
+ os_memcmp(entry->pmkid, pmkid, 16) == 0) {
+ if (prev)
+ prev->next = entry->next;
+ else
+ drv->pmkid = entry->next;
+ os_free(entry);
+ break;
+ }
+ prev = entry;
+ entry = entry->next;
+ }
+ return wpa_driver_ndis_set_pmkid(drv);
+}
+
+
+static int wpa_driver_ndis_flush_pmkid(void *priv)
+{
+ struct wpa_driver_ndis_data *drv = priv;
+ NDIS_802_11_PMKID p;
+ struct ndis_pmkid_entry *pmkid, *prev;
+ int prev_authmode, ret;
+
+ if (drv->no_of_pmkid == 0)
+ return 0;
+
+ pmkid = drv->pmkid;
+ drv->pmkid = NULL;
+ while (pmkid) {
+ prev = pmkid;
+ pmkid = pmkid->next;
+ os_free(prev);
+ }
+
+ /*
+ * Some drivers may refuse OID_802_11_PMKID if authMode is not set to
+ * WPA2, so change authMode temporarily, if needed.
+ */
+ prev_authmode = ndis_get_auth_mode(drv);
+ if (prev_authmode != Ndis802_11AuthModeWPA2)
+ ndis_set_auth_mode(drv, Ndis802_11AuthModeWPA2);
+
+ os_memset(&p, 0, sizeof(p));
+ p.Length = 8;
+ p.BSSIDInfoCount = 0;
+ wpa_hexdump(MSG_MSGDUMP, "NDIS: OID_802_11_PMKID (flush)",
+ (u8 *) &p, 8);
+ ret = ndis_set_oid(drv, OID_802_11_PMKID, (char *) &p, 8);
+
+ if (prev_authmode != Ndis802_11AuthModeWPA2)
+ ndis_set_auth_mode(drv, prev_authmode);
+
+ return ret;
+}
+
+
+static int wpa_driver_ndis_get_associnfo(struct wpa_driver_ndis_data *drv)
+{
+ char buf[512], *pos;
+ NDIS_802_11_ASSOCIATION_INFORMATION *ai;
+ int len;
+ union wpa_event_data data;
+ NDIS_802_11_BSSID_LIST_EX *b;
+ size_t blen, i;
+
+ len = ndis_get_oid(drv, OID_802_11_ASSOCIATION_INFORMATION, buf,
+ sizeof(buf));
+ if (len < 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: failed to get association "
+ "information");
+ return -1;
+ }
+ if (len > sizeof(buf)) {
+ /* Some drivers seem to be producing incorrect length for this
+ * data. Limit the length to the current buffer size to avoid
+ * crashing in hexdump. The data seems to be otherwise valid,
+ * so better try to use it. */
+ wpa_printf(MSG_DEBUG, "NDIS: ignored bogus association "
+ "information length %d", len);
+ len = ndis_get_oid(drv, OID_802_11_ASSOCIATION_INFORMATION,
+ buf, sizeof(buf));
+ if (len < -1) {
+ wpa_printf(MSG_DEBUG, "NDIS: re-reading association "
+ "information failed");
+ return -1;
+ }
+ if (len > sizeof(buf)) {
+ wpa_printf(MSG_DEBUG, "NDIS: ignored bogus association"
+ " information length %d (re-read)", len);
+ len = sizeof(buf);
+ }
+ }
+ wpa_hexdump(MSG_MSGDUMP, "NDIS: association information",
+ (u8 *) buf, len);
+ if (len < sizeof(*ai)) {
+ wpa_printf(MSG_DEBUG, "NDIS: too short association "
+ "information");
+ return -1;
+ }
+ ai = (NDIS_802_11_ASSOCIATION_INFORMATION *) buf;
+ wpa_printf(MSG_DEBUG, "NDIS: ReqFixed=0x%x RespFixed=0x%x off_req=%d "
+ "off_resp=%d len_req=%d len_resp=%d",
+ ai->AvailableRequestFixedIEs, ai->AvailableResponseFixedIEs,
+ (int) ai->OffsetRequestIEs, (int) ai->OffsetResponseIEs,
+ (int) ai->RequestIELength, (int) ai->ResponseIELength);
+
+ if (ai->OffsetRequestIEs + ai->RequestIELength > (unsigned) len ||
+ ai->OffsetResponseIEs + ai->ResponseIELength > (unsigned) len) {
+ wpa_printf(MSG_DEBUG, "NDIS: association information - "
+ "IE overflow");
+ return -1;
+ }
+
+ wpa_hexdump(MSG_MSGDUMP, "NDIS: Request IEs",
+ (u8 *) buf + ai->OffsetRequestIEs, ai->RequestIELength);
+ wpa_hexdump(MSG_MSGDUMP, "NDIS: Response IEs",
+ (u8 *) buf + ai->OffsetResponseIEs, ai->ResponseIELength);
+
+ os_memset(&data, 0, sizeof(data));
+ data.assoc_info.req_ies = (u8 *) buf + ai->OffsetRequestIEs;
+ data.assoc_info.req_ies_len = ai->RequestIELength;
+ data.assoc_info.resp_ies = (u8 *) buf + ai->OffsetResponseIEs;
+ data.assoc_info.resp_ies_len = ai->ResponseIELength;
+
+ blen = 65535;
+ b = os_zalloc(blen);
+ if (b == NULL)
+ goto skip_scan_results;
+ len = ndis_get_oid(drv, OID_802_11_BSSID_LIST, (char *) b, blen);
+ if (len < 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: failed to get scan results");
+ os_free(b);
+ b = NULL;
+ goto skip_scan_results;
+ }
+ wpa_printf(MSG_DEBUG, "NDIS: %d BSSID items to process for AssocInfo",
+ (unsigned int) b->NumberOfItems);
+
+ pos = (char *) &b->Bssid[0];
+ for (i = 0; i < b->NumberOfItems; i++) {
+ NDIS_WLAN_BSSID_EX *bss = (NDIS_WLAN_BSSID_EX *) pos;
+ if (os_memcmp(drv->bssid, bss->MacAddress, ETH_ALEN) == 0 &&
+ bss->IELength > sizeof(NDIS_802_11_FIXED_IEs)) {
+ data.assoc_info.beacon_ies =
+ ((u8 *) bss->IEs) +
+ sizeof(NDIS_802_11_FIXED_IEs);
+ data.assoc_info.beacon_ies_len =
+ bss->IELength - sizeof(NDIS_802_11_FIXED_IEs);
+ wpa_hexdump(MSG_MSGDUMP, "NDIS: Beacon IEs",
+ data.assoc_info.beacon_ies,
+ data.assoc_info.beacon_ies_len);
+ break;
+ }
+ pos += bss->Length;
+ if (pos > (char *) b + blen)
+ break;
+ }
+
+skip_scan_results:
+ wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &data);
+
+ os_free(b);
+
+ return 0;
+}
+
+
+static void wpa_driver_ndis_poll_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_driver_ndis_data *drv = eloop_ctx;
+ u8 bssid[ETH_ALEN];
+ int poll;
+
+ if (drv->wired)
+ return;
+
+ if (wpa_driver_ndis_get_bssid(drv, bssid)) {
+ /* Disconnected */
+ if (!is_zero_ether_addr(drv->bssid)) {
+ os_memset(drv->bssid, 0, ETH_ALEN);
+ wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
+ }
+ } else {
+ /* Connected */
+ if (os_memcmp(drv->bssid, bssid, ETH_ALEN) != 0) {
+ os_memcpy(drv->bssid, bssid, ETH_ALEN);
+ wpa_driver_ndis_get_associnfo(drv);
+ wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL);
+ }
+ }
+
+ /* When using integrated NDIS event receiver, we can skip BSSID
+ * polling when using infrastructure network. However, when using
+ * IBSS mode, many driver do not seem to generate connection event,
+ * so we need to enable BSSID polling to figure out when IBSS network
+ * has been formed.
+ */
+ poll = drv->mode == IEEE80211_MODE_IBSS;
+#ifndef CONFIG_NDIS_EVENTS_INTEGRATED
+#ifndef _WIN32_WCE
+ poll = 1;
+#endif /* _WIN32_WCE */
+#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
+
+ if (poll) {
+ eloop_register_timeout(1, 0, wpa_driver_ndis_poll_timeout,
+ drv, NULL);
+ }
+}
+
+
+static void wpa_driver_ndis_poll(void *priv)
+{
+ struct wpa_driver_ndis_data *drv = priv;
+ eloop_cancel_timeout(wpa_driver_ndis_poll_timeout, drv, NULL);
+ wpa_driver_ndis_poll_timeout(drv, NULL);
+}
+
+
+/* Called when driver generates Media Connect Event by calling
+ * NdisMIndicateStatus() with NDIS_STATUS_MEDIA_CONNECT */
+void wpa_driver_ndis_event_connect(struct wpa_driver_ndis_data *drv)
+{
+ wpa_printf(MSG_DEBUG, "NDIS: Media Connect Event");
+ if (wpa_driver_ndis_get_bssid(drv, drv->bssid) == 0) {
+ wpa_driver_ndis_get_associnfo(drv);
+ wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL);
+ }
+}
+
+
+/* Called when driver generates Media Disconnect Event by calling
+ * NdisMIndicateStatus() with NDIS_STATUS_MEDIA_DISCONNECT */
+void wpa_driver_ndis_event_disconnect(struct wpa_driver_ndis_data *drv)
+{
+ wpa_printf(MSG_DEBUG, "NDIS: Media Disconnect Event");
+ os_memset(drv->bssid, 0, ETH_ALEN);
+ wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
+}
+
+
+static void wpa_driver_ndis_event_auth(struct wpa_driver_ndis_data *drv,
+ const u8 *data, size_t data_len)
+{
+ NDIS_802_11_AUTHENTICATION_REQUEST *req;
+ int pairwise = 0, group = 0;
+ union wpa_event_data event;
+
+ if (data_len < sizeof(*req)) {
+ wpa_printf(MSG_DEBUG, "NDIS: Too short Authentication Request "
+ "Event (len=%d)", data_len);
+ return;
+ }
+ req = (NDIS_802_11_AUTHENTICATION_REQUEST *) data;
+
+ wpa_printf(MSG_DEBUG, "NDIS: Authentication Request Event: "
+ "Bssid " MACSTR " Flags 0x%x",
+ MAC2STR(req->Bssid), (int) req->Flags);
+
+ if ((req->Flags & NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR) ==
+ NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR)
+ pairwise = 1;
+ else if ((req->Flags & NDIS_802_11_AUTH_REQUEST_GROUP_ERROR) ==
+ NDIS_802_11_AUTH_REQUEST_GROUP_ERROR)
+ group = 1;
+
+ if (pairwise || group) {
+ os_memset(&event, 0, sizeof(event));
+ event.michael_mic_failure.unicast = pairwise;
+ wpa_supplicant_event(drv->ctx, EVENT_MICHAEL_MIC_FAILURE,
+ &event);
+ }
+}
+
+
+static void wpa_driver_ndis_event_pmkid(struct wpa_driver_ndis_data *drv,
+ const u8 *data, size_t data_len)
+{
+ NDIS_802_11_PMKID_CANDIDATE_LIST *pmkid;
+ size_t i;
+ union wpa_event_data event;
+
+ if (data_len < 8) {
+ wpa_printf(MSG_DEBUG, "NDIS: Too short PMKID Candidate List "
+ "Event (len=%d)", data_len);
+ return;
+ }
+ pmkid = (NDIS_802_11_PMKID_CANDIDATE_LIST *) data;
+ wpa_printf(MSG_DEBUG, "NDIS: PMKID Candidate List Event - Version %d "
+ "NumCandidates %d",
+ (int) pmkid->Version, (int) pmkid->NumCandidates);
+
+ if (pmkid->Version != 1) {
+ wpa_printf(MSG_DEBUG, "NDIS: Unsupported PMKID Candidate List "
+ "Version %d", (int) pmkid->Version);
+ return;
+ }
+
+ if (data_len < 8 + pmkid->NumCandidates * sizeof(PMKID_CANDIDATE)) {
+ wpa_printf(MSG_DEBUG, "NDIS: PMKID Candidate List underflow");
+ return;
+ }
+
+ os_memset(&event, 0, sizeof(event));
+ for (i = 0; i < pmkid->NumCandidates; i++) {
+ PMKID_CANDIDATE *p = &pmkid->CandidateList[i];
+ wpa_printf(MSG_DEBUG, "NDIS: %d: " MACSTR " Flags 0x%x",
+ i, MAC2STR(p->BSSID), (int) p->Flags);
+ os_memcpy(event.pmkid_candidate.bssid, p->BSSID, ETH_ALEN);
+ event.pmkid_candidate.index = i;
+ event.pmkid_candidate.preauth =
+ p->Flags & NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED;
+ wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE,
+ &event);
+ }
+}
+
+
+/* Called when driver calls NdisMIndicateStatus() with
+ * NDIS_STATUS_MEDIA_SPECIFIC_INDICATION */
+void wpa_driver_ndis_event_media_specific(struct wpa_driver_ndis_data *drv,
+ const u8 *data, size_t data_len)
+{
+ NDIS_802_11_STATUS_INDICATION *status;
+
+ if (data == NULL || data_len < sizeof(*status))
+ return;
+
+ wpa_hexdump(MSG_DEBUG, "NDIS: Media Specific Indication",
+ data, data_len);
+
+ status = (NDIS_802_11_STATUS_INDICATION *) data;
+ data += sizeof(status);
+ data_len -= sizeof(status);
+
+ switch (status->StatusType) {
+ case Ndis802_11StatusType_Authentication:
+ wpa_driver_ndis_event_auth(drv, data, data_len);
+ break;
+ case Ndis802_11StatusType_PMKID_CandidateList:
+ wpa_driver_ndis_event_pmkid(drv, data, data_len);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "NDIS: Unknown StatusType %d",
+ (int) status->StatusType);
+ break;
+ }
+}
+
+
+/* Called when an adapter is added */
+void wpa_driver_ndis_event_adapter_arrival(struct wpa_driver_ndis_data *drv)
+{
+ union wpa_event_data event;
+ int i;
+
+ wpa_printf(MSG_DEBUG, "NDIS: Notify Adapter Arrival");
+
+ for (i = 0; i < 30; i++) {
+ /* Re-open Packet32/NDISUIO connection */
+ wpa_driver_ndis_adapter_close(drv);
+ if (wpa_driver_ndis_adapter_init(drv) < 0 ||
+ wpa_driver_ndis_adapter_open(drv) < 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: Driver re-initialization "
+ "(%d) failed", i);
+ os_sleep(1, 0);
+ } else {
+ wpa_printf(MSG_DEBUG, "NDIS: Driver re-initialized");
+ break;
+ }
+ }
+
+ os_memset(&event, 0, sizeof(event));
+ os_strlcpy(event.interface_status.ifname, drv->ifname,
+ sizeof(event.interface_status.ifname));
+ event.interface_status.ievent = EVENT_INTERFACE_ADDED;
+ wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
+}
+
+
+/* Called when an adapter is removed */
+void wpa_driver_ndis_event_adapter_removal(struct wpa_driver_ndis_data *drv)
+{
+ union wpa_event_data event;
+
+ wpa_printf(MSG_DEBUG, "NDIS: Notify Adapter Removal");
+ os_memset(&event, 0, sizeof(event));
+ os_strlcpy(event.interface_status.ifname, drv->ifname,
+ sizeof(event.interface_status.ifname));
+ event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
+ wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
+}
+
+
+static void
+wpa_driver_ndis_get_wpa_capability(struct wpa_driver_ndis_data *drv)
+{
+ wpa_printf(MSG_DEBUG, "NDIS: verifying driver WPA capability");
+
+ if (ndis_set_auth_mode(drv, Ndis802_11AuthModeWPA) == 0 &&
+ ndis_get_auth_mode(drv) == Ndis802_11AuthModeWPA) {
+ wpa_printf(MSG_DEBUG, "NDIS: WPA key management supported");
+ drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA;
+ }
+
+ if (ndis_set_auth_mode(drv, Ndis802_11AuthModeWPAPSK) == 0 &&
+ ndis_get_auth_mode(drv) == Ndis802_11AuthModeWPAPSK) {
+ wpa_printf(MSG_DEBUG, "NDIS: WPA-PSK key management "
+ "supported");
+ drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK;
+ }
+
+ if (ndis_set_encr_status(drv, Ndis802_11Encryption3Enabled) == 0 &&
+ ndis_get_encr_status(drv) == Ndis802_11Encryption3KeyAbsent) {
+ wpa_printf(MSG_DEBUG, "NDIS: CCMP encryption supported");
+ drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP;
+ }
+
+ if (ndis_set_encr_status(drv, Ndis802_11Encryption2Enabled) == 0 &&
+ ndis_get_encr_status(drv) == Ndis802_11Encryption2KeyAbsent) {
+ wpa_printf(MSG_DEBUG, "NDIS: TKIP encryption supported");
+ drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP;
+ }
+
+ if (ndis_set_encr_status(drv, Ndis802_11Encryption1Enabled) == 0 &&
+ ndis_get_encr_status(drv) == Ndis802_11Encryption1KeyAbsent) {
+ wpa_printf(MSG_DEBUG, "NDIS: WEP encryption supported");
+ drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 |
+ WPA_DRIVER_CAPA_ENC_WEP104;
+ }
+
+ if (ndis_set_auth_mode(drv, Ndis802_11AuthModeShared) == 0 &&
+ ndis_get_auth_mode(drv) == Ndis802_11AuthModeShared) {
+ drv->capa.auth |= WPA_DRIVER_AUTH_SHARED;
+ }
+
+ if (ndis_set_auth_mode(drv, Ndis802_11AuthModeOpen) == 0 &&
+ ndis_get_auth_mode(drv) == Ndis802_11AuthModeOpen) {
+ drv->capa.auth |= WPA_DRIVER_AUTH_OPEN;
+ }
+
+ ndis_set_encr_status(drv, Ndis802_11EncryptionDisabled);
+
+ /* Could also verify OID_802_11_ADD_KEY error reporting and
+ * support for OID_802_11_ASSOCIATION_INFORMATION. */
+
+ if (drv->capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA &&
+ drv->capa.enc & (WPA_DRIVER_CAPA_ENC_TKIP |
+ WPA_DRIVER_CAPA_ENC_CCMP)) {
+ wpa_printf(MSG_DEBUG, "NDIS: driver supports WPA");
+ drv->has_capability = 1;
+ } else {
+ wpa_printf(MSG_DEBUG, "NDIS: no WPA support found");
+ }
+
+ wpa_printf(MSG_DEBUG, "NDIS: driver capabilities: key_mgmt 0x%x "
+ "enc 0x%x auth 0x%x",
+ drv->capa.key_mgmt, drv->capa.enc, drv->capa.auth);
+}
+
+
+static void wpa_driver_ndis_get_capability(struct wpa_driver_ndis_data *drv)
+{
+ char buf[512];
+ int len;
+ size_t i;
+ NDIS_802_11_CAPABILITY *c;
+
+ drv->capa.flags = WPA_DRIVER_FLAGS_DRIVER_IE;
+
+ len = ndis_get_oid(drv, OID_802_11_CAPABILITY, buf, sizeof(buf));
+ if (len < 0) {
+ wpa_driver_ndis_get_wpa_capability(drv);
+ return;
+ }
+
+ wpa_hexdump(MSG_MSGDUMP, "OID_802_11_CAPABILITY", (u8 *) buf, len);
+ c = (NDIS_802_11_CAPABILITY *) buf;
+ if (len < sizeof(*c) || c->Version != 2) {
+ wpa_printf(MSG_DEBUG, "NDIS: unsupported "
+ "OID_802_11_CAPABILITY data");
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "NDIS: Driver supports OID_802_11_CAPABILITY - "
+ "NoOfPMKIDs %d NoOfAuthEncrPairs %d",
+ (int) c->NoOfPMKIDs,
+ (int) c->NoOfAuthEncryptPairsSupported);
+ drv->has_capability = 1;
+ drv->no_of_pmkid = c->NoOfPMKIDs;
+ for (i = 0; i < c->NoOfAuthEncryptPairsSupported; i++) {
+ NDIS_802_11_AUTHENTICATION_ENCRYPTION *ae;
+ ae = &c->AuthenticationEncryptionSupported[i];
+ if ((char *) (ae + 1) > buf + len) {
+ wpa_printf(MSG_DEBUG, "NDIS: auth/encr pair list "
+ "overflow");
+ break;
+ }
+ wpa_printf(MSG_MSGDUMP, "NDIS: %d - auth %d encr %d",
+ i, (int) ae->AuthModeSupported,
+ (int) ae->EncryptStatusSupported);
+ switch (ae->AuthModeSupported) {
+ case Ndis802_11AuthModeOpen:
+ drv->capa.auth |= WPA_DRIVER_AUTH_OPEN;
+ break;
+ case Ndis802_11AuthModeShared:
+ drv->capa.auth |= WPA_DRIVER_AUTH_SHARED;
+ break;
+ case Ndis802_11AuthModeWPA:
+ drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA;
+ break;
+ case Ndis802_11AuthModeWPAPSK:
+ drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK;
+ break;
+ case Ndis802_11AuthModeWPA2:
+ drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA2;
+ break;
+ case Ndis802_11AuthModeWPA2PSK:
+ drv->capa.key_mgmt |=
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
+ break;
+ case Ndis802_11AuthModeWPANone:
+ drv->capa.key_mgmt |=
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE;
+ break;
+ default:
+ break;
+ }
+ switch (ae->EncryptStatusSupported) {
+ case Ndis802_11Encryption1Enabled:
+ drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40;
+ drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP104;
+ break;
+ case Ndis802_11Encryption2Enabled:
+ drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP;
+ break;
+ case Ndis802_11Encryption3Enabled:
+ drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP;
+ break;
+ default:
+ break;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "NDIS: driver capabilities: key_mgmt 0x%x "
+ "enc 0x%x auth 0x%x",
+ drv->capa.key_mgmt, drv->capa.enc, drv->capa.auth);
+}
+
+
+static int wpa_driver_ndis_get_capa(void *priv, struct wpa_driver_capa *capa)
+{
+ struct wpa_driver_ndis_data *drv = priv;
+ if (!drv->has_capability)
+ return -1;
+ os_memcpy(capa, &drv->capa, sizeof(*capa));
+ return 0;
+}
+
+
+static const char * wpa_driver_ndis_get_ifname(void *priv)
+{
+ struct wpa_driver_ndis_data *drv = priv;
+ return drv->ifname;
+}
+
+
+static const u8 * wpa_driver_ndis_get_mac_addr(void *priv)
+{
+ struct wpa_driver_ndis_data *drv = priv;
+ return drv->own_addr;
+}
+
+
+#ifdef _WIN32_WCE
+
+#define NDISUIO_MSG_SIZE (sizeof(NDISUIO_DEVICE_NOTIFICATION) + 512)
+
+static void ndisuio_notification_receive(void *eloop_data, void *user_ctx)
+{
+ struct wpa_driver_ndis_data *drv = eloop_data;
+ NDISUIO_DEVICE_NOTIFICATION *hdr;
+ u8 buf[NDISUIO_MSG_SIZE];
+ DWORD len, flags;
+
+ if (!ReadMsgQueue(drv->event_queue, buf, NDISUIO_MSG_SIZE, &len, 0,
+ &flags)) {
+ wpa_printf(MSG_DEBUG, "ndisuio_notification_receive: "
+ "ReadMsgQueue failed: %d", (int) GetLastError());
+ return;
+ }
+
+ if (len < sizeof(NDISUIO_DEVICE_NOTIFICATION)) {
+ wpa_printf(MSG_DEBUG, "ndisuio_notification_receive: "
+ "Too short message (len=%d)", (int) len);
+ return;
+ }
+
+ hdr = (NDISUIO_DEVICE_NOTIFICATION *) buf;
+ wpa_printf(MSG_DEBUG, "NDIS: Notification received: len=%d type=0x%x",
+ (int) len, hdr->dwNotificationType);
+
+ switch (hdr->dwNotificationType) {
+#ifdef NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL
+ case NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL:
+ wpa_printf(MSG_DEBUG, "NDIS: ADAPTER_ARRIVAL");
+ wpa_driver_ndis_event_adapter_arrival(drv);
+ break;
+#endif
+#ifdef NDISUIO_NOTIFICATION_ADAPTER_REMOVAL
+ case NDISUIO_NOTIFICATION_ADAPTER_REMOVAL:
+ wpa_printf(MSG_DEBUG, "NDIS: ADAPTER_REMOVAL");
+ wpa_driver_ndis_event_adapter_removal(drv);
+ break;
+#endif
+ case NDISUIO_NOTIFICATION_MEDIA_CONNECT:
+ wpa_printf(MSG_DEBUG, "NDIS: MEDIA_CONNECT");
+ SetEvent(drv->connected_event);
+ wpa_driver_ndis_event_connect(drv);
+ break;
+ case NDISUIO_NOTIFICATION_MEDIA_DISCONNECT:
+ ResetEvent(drv->connected_event);
+ wpa_printf(MSG_DEBUG, "NDIS: MEDIA_DISCONNECT");
+ wpa_driver_ndis_event_disconnect(drv);
+ break;
+ case NDISUIO_NOTIFICATION_MEDIA_SPECIFIC_NOTIFICATION:
+ wpa_printf(MSG_DEBUG, "NDIS: MEDIA_SPECIFIC_NOTIFICATION");
+#if _WIN32_WCE == 420 || _WIN32_WCE == 0x420
+ wpa_driver_ndis_event_media_specific(
+ drv, hdr->pvStatusBuffer, hdr->uiStatusBufferSize);
+#else
+ wpa_driver_ndis_event_media_specific(
+ drv, ((const u8 *) hdr) + hdr->uiOffsetToStatusBuffer,
+ (size_t) hdr->uiStatusBufferSize);
+#endif
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "NDIS: Unknown notification type 0x%x",
+ hdr->dwNotificationType);
+ break;
+ }
+}
+
+
+static void ndisuio_notification_deinit(struct wpa_driver_ndis_data *drv)
+{
+ NDISUIO_REQUEST_NOTIFICATION req;
+
+ memset(&req, 0, sizeof(req));
+ req.hMsgQueue = drv->event_queue;
+ req.dwNotificationTypes = 0;
+
+ if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_REQUEST_NOTIFICATION,
+ &req, sizeof(req), NULL, 0, NULL, NULL)) {
+ wpa_printf(MSG_INFO, "ndisuio_notification_deinit: "
+ "IOCTL_NDISUIO_REQUEST_NOTIFICATION failed: %d",
+ (int) GetLastError());
+ }
+
+ if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_CANCEL_NOTIFICATION,
+ NULL, 0, NULL, 0, NULL, NULL)) {
+ wpa_printf(MSG_INFO, "ndisuio_notification_deinit: "
+ "IOCTL_NDISUIO_CANCEL_NOTIFICATION failed: %d",
+ (int) GetLastError());
+ }
+
+ if (drv->event_queue) {
+ eloop_unregister_event(drv->event_queue,
+ sizeof(drv->event_queue));
+ CloseHandle(drv->event_queue);
+ drv->event_queue = NULL;
+ }
+
+ if (drv->connected_event) {
+ CloseHandle(drv->connected_event);
+ drv->connected_event = NULL;
+ }
+}
+
+
+static int ndisuio_notification_init(struct wpa_driver_ndis_data *drv)
+{
+ MSGQUEUEOPTIONS opt;
+ NDISUIO_REQUEST_NOTIFICATION req;
+
+ drv->connected_event =
+ CreateEvent(NULL, TRUE, FALSE, TEXT("WpaSupplicantConnected"));
+ if (drv->connected_event == NULL) {
+ wpa_printf(MSG_INFO, "ndisuio_notification_init: "
+ "CreateEvent failed: %d",
+ (int) GetLastError());
+ return -1;
+ }
+
+ memset(&opt, 0, sizeof(opt));
+ opt.dwSize = sizeof(opt);
+ opt.dwMaxMessages = 5;
+ opt.cbMaxMessage = NDISUIO_MSG_SIZE;
+ opt.bReadAccess = TRUE;
+
+ drv->event_queue = CreateMsgQueue(NULL, &opt);
+ if (drv->event_queue == NULL) {
+ wpa_printf(MSG_INFO, "ndisuio_notification_init: "
+ "CreateMsgQueue failed: %d",
+ (int) GetLastError());
+ ndisuio_notification_deinit(drv);
+ return -1;
+ }
+
+ memset(&req, 0, sizeof(req));
+ req.hMsgQueue = drv->event_queue;
+ req.dwNotificationTypes =
+#ifdef NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL
+ NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL |
+#endif
+#ifdef NDISUIO_NOTIFICATION_ADAPTER_REMOVAL
+ NDISUIO_NOTIFICATION_ADAPTER_REMOVAL |
+#endif
+ NDISUIO_NOTIFICATION_MEDIA_CONNECT |
+ NDISUIO_NOTIFICATION_MEDIA_DISCONNECT |
+ NDISUIO_NOTIFICATION_MEDIA_SPECIFIC_NOTIFICATION;
+
+ if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_REQUEST_NOTIFICATION,
+ &req, sizeof(req), NULL, 0, NULL, NULL)) {
+ wpa_printf(MSG_INFO, "ndisuio_notification_init: "
+ "IOCTL_NDISUIO_REQUEST_NOTIFICATION failed: %d",
+ (int) GetLastError());
+ ndisuio_notification_deinit(drv);
+ return -1;
+ }
+
+ eloop_register_event(drv->event_queue, sizeof(drv->event_queue),
+ ndisuio_notification_receive, drv, NULL);
+
+ return 0;
+}
+#endif /* _WIN32_WCE */
+
+
+static int wpa_driver_ndis_get_names(struct wpa_driver_ndis_data *drv)
+{
+#ifdef CONFIG_USE_NDISUIO
+ NDISUIO_QUERY_BINDING *b;
+ size_t blen = sizeof(*b) + 1024;
+ int i, error, found = 0;
+ DWORD written;
+ char name[256], desc[256], *dpos;
+ WCHAR *pos;
+ size_t j, len, dlen;
+
+ b = os_malloc(blen);
+ if (b == NULL)
+ return -1;
+
+ for (i = 0; ; i++) {
+ os_memset(b, 0, blen);
+ b->BindingIndex = i;
+ if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_QUERY_BINDING,
+ b, sizeof(NDISUIO_QUERY_BINDING), b, blen,
+ &written, NULL)) {
+ error = (int) GetLastError();
+ if (error == ERROR_NO_MORE_ITEMS)
+ break;
+ wpa_printf(MSG_DEBUG, "IOCTL_NDISUIO_QUERY_BINDING "
+ "failed: %d", error);
+ break;
+ }
+
+ pos = (WCHAR *) ((char *) b + b->DeviceNameOffset);
+ len = b->DeviceNameLength;
+ if (len >= sizeof(name))
+ len = sizeof(name) - 1;
+ for (j = 0; j < len; j++)
+ name[j] = (char) pos[j];
+ name[len] = '\0';
+
+ pos = (WCHAR *) ((char *) b + b->DeviceDescrOffset);
+ len = b->DeviceDescrLength;
+ if (len >= sizeof(desc))
+ len = sizeof(desc) - 1;
+ for (j = 0; j < len; j++)
+ desc[j] = (char) pos[j];
+ desc[len] = '\0';
+
+ wpa_printf(MSG_DEBUG, "NDIS: %d - %s - %s", i, name, desc);
+
+ if (os_strstr(name, drv->ifname)) {
+ wpa_printf(MSG_DEBUG, "NDIS: Interface name match");
+ found = 1;
+ break;
+ }
+
+ if (os_strncmp(desc, drv->ifname, os_strlen(drv->ifname)) == 0)
+ {
+ wpa_printf(MSG_DEBUG, "NDIS: Interface description "
+ "match");
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ wpa_printf(MSG_DEBUG, "NDIS: Could not find interface '%s'",
+ drv->ifname);
+ os_free(b);
+ return -1;
+ }
+
+ os_strlcpy(drv->ifname,
+ os_strncmp(name, "\\DEVICE\\", 8) == 0 ? name + 8 : name,
+ sizeof(drv->ifname));
+#ifdef _WIN32_WCE
+ drv->adapter_name = wpa_strdup_tchar(drv->ifname);
+ if (drv->adapter_name == NULL) {
+ wpa_printf(MSG_ERROR, "NDIS: Failed to allocate memory for "
+ "adapter name");
+ os_free(b);
+ return -1;
+ }
+#endif /* _WIN32_WCE */
+
+ dpos = os_strstr(desc, " - ");
+ if (dpos)
+ dlen = dpos - desc;
+ else
+ dlen = os_strlen(desc);
+ drv->adapter_desc = os_malloc(dlen + 1);
+ if (drv->adapter_desc) {
+ os_memcpy(drv->adapter_desc, desc, dlen);
+ drv->adapter_desc[dlen] = '\0';
+ }
+
+ os_free(b);
+
+ if (drv->adapter_desc == NULL)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "NDIS: Adapter description prefix '%s'",
+ drv->adapter_desc);
+
+ return 0;
+#else /* CONFIG_USE_NDISUIO */
+ PTSTR _names;
+ char *names, *pos, *pos2;
+ ULONG len;
+ BOOLEAN res;
+#define MAX_ADAPTERS 32
+ char *name[MAX_ADAPTERS];
+ char *desc[MAX_ADAPTERS];
+ int num_name, num_desc, i, found_name, found_desc;
+ size_t dlen;
+
+ wpa_printf(MSG_DEBUG, "NDIS: Packet.dll version: %s",
+ PacketGetVersion());
+
+ len = 8192;
+ _names = os_zalloc(len);
+ if (_names == NULL)
+ return -1;
+
+ res = PacketGetAdapterNames(_names, &len);
+ if (!res && len > 8192) {
+ os_free(_names);
+ _names = os_zalloc(len);
+ if (_names == NULL)
+ return -1;
+ res = PacketGetAdapterNames(_names, &len);
+ }
+
+ if (!res) {
+ wpa_printf(MSG_ERROR, "NDIS: Failed to get adapter list "
+ "(PacketGetAdapterNames)");
+ os_free(_names);
+ return -1;
+ }
+
+ names = (char *) _names;
+ if (names[0] && names[1] == '\0' && names[2] && names[3] == '\0') {
+ wpa_printf(MSG_DEBUG, "NDIS: Looks like adapter names are in "
+ "UNICODE");
+ /* Convert to ASCII */
+ pos2 = pos = names;
+ while (pos2 < names + len) {
+ if (pos2[0] == '\0' && pos2[1] == '\0' &&
+ pos2[2] == '\0' && pos2[3] == '\0') {
+ pos2 += 4;
+ break;
+ }
+ *pos++ = pos2[0];
+ pos2 += 2;
+ }
+ os_memcpy(pos + 2, names, pos - names);
+ pos += 2;
+ } else
+ pos = names;
+
+ num_name = 0;
+ while (pos < names + len) {
+ name[num_name] = pos;
+ while (*pos && pos < names + len)
+ pos++;
+ if (pos + 1 >= names + len) {
+ os_free(names);
+ return -1;
+ }
+ pos++;
+ num_name++;
+ if (num_name >= MAX_ADAPTERS) {
+ wpa_printf(MSG_DEBUG, "NDIS: Too many adapters");
+ os_free(names);
+ return -1;
+ }
+ if (*pos == '\0') {
+ wpa_printf(MSG_DEBUG, "NDIS: %d adapter names found",
+ num_name);
+ pos++;
+ break;
+ }
+ }
+
+ num_desc = 0;
+ while (pos < names + len) {
+ desc[num_desc] = pos;
+ while (*pos && pos < names + len)
+ pos++;
+ if (pos + 1 >= names + len) {
+ os_free(names);
+ return -1;
+ }
+ pos++;
+ num_desc++;
+ if (num_desc >= MAX_ADAPTERS) {
+ wpa_printf(MSG_DEBUG, "NDIS: Too many adapter "
+ "descriptions");
+ os_free(names);
+ return -1;
+ }
+ if (*pos == '\0') {
+ wpa_printf(MSG_DEBUG, "NDIS: %d adapter descriptions "
+ "found", num_name);
+ pos++;
+ break;
+ }
+ }
+
+ /*
+ * Windows 98 with Packet.dll 3.0 alpha3 does not include adapter
+ * descriptions. Fill in dummy descriptors to work around this.
+ */
+ while (num_desc < num_name)
+ desc[num_desc++] = "dummy description";
+
+ if (num_name != num_desc) {
+ wpa_printf(MSG_DEBUG, "NDIS: mismatch in adapter name and "
+ "description counts (%d != %d)",
+ num_name, num_desc);
+ os_free(names);
+ return -1;
+ }
+
+ found_name = found_desc = -1;
+ for (i = 0; i < num_name; i++) {
+ wpa_printf(MSG_DEBUG, "NDIS: %d - %s - %s",
+ i, name[i], desc[i]);
+ if (found_name == -1 && os_strstr(name[i], drv->ifname))
+ found_name = i;
+ if (found_desc == -1 &&
+ os_strncmp(desc[i], drv->ifname, os_strlen(drv->ifname)) ==
+ 0)
+ found_desc = i;
+ }
+
+ if (found_name < 0 && found_desc >= 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: Matched interface '%s' based on "
+ "description '%s'",
+ name[found_desc], desc[found_desc]);
+ found_name = found_desc;
+ os_strlcpy(drv->ifname,
+ os_strncmp(name[found_desc], "\\Device\\NPF_", 12)
+ == 0 ? name[found_desc] + 12 : name[found_desc],
+ sizeof(drv->ifname));
+ }
+
+ if (found_name < 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: Could not find interface '%s'",
+ drv->ifname);
+ os_free(names);
+ return -1;
+ }
+
+ i = found_name;
+ pos = os_strrchr(desc[i], '(');
+ if (pos) {
+ dlen = pos - desc[i];
+ pos--;
+ if (pos > desc[i] && *pos == ' ')
+ dlen--;
+ } else {
+ dlen = os_strlen(desc[i]);
+ }
+ drv->adapter_desc = os_malloc(dlen + 1);
+ if (drv->adapter_desc) {
+ os_memcpy(drv->adapter_desc, desc[i], dlen);
+ drv->adapter_desc[dlen] = '\0';
+ }
+
+ os_free(names);
+
+ if (drv->adapter_desc == NULL)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "NDIS: Adapter description prefix '%s'",
+ drv->adapter_desc);
+
+ return 0;
+#endif /* CONFIG_USE_NDISUIO */
+}
+
+
+#if defined(CONFIG_NATIVE_WINDOWS) || defined(__CYGWIN__)
+#ifndef _WIN32_WCE
+/*
+ * These structures are undocumented for WinXP; only WinCE version is
+ * documented. These would be included wzcsapi.h if it were available. Some
+ * changes here have been needed to make the structures match with WinXP SP2.
+ * It is unclear whether these work with any other version.
+ */
+
+typedef struct {
+ LPWSTR wszGuid;
+} INTF_KEY_ENTRY, *PINTF_KEY_ENTRY;
+
+typedef struct {
+ DWORD dwNumIntfs;
+ PINTF_KEY_ENTRY pIntfs;
+} INTFS_KEY_TABLE, *PINTFS_KEY_TABLE;
+
+typedef struct {
+ DWORD dwDataLen;
+ LPBYTE pData;
+} RAW_DATA, *PRAW_DATA;
+
+typedef struct {
+ LPWSTR wszGuid;
+ LPWSTR wszDescr;
+ ULONG ulMediaState;
+ ULONG ulMediaType;
+ ULONG ulPhysicalMediaType;
+ INT nInfraMode;
+ INT nAuthMode;
+ INT nWepStatus;
+#ifndef _WIN32_WCE
+ u8 pad[2]; /* why is this needed? */
+#endif /* _WIN32_WCE */
+ DWORD dwCtlFlags;
+ DWORD dwCapabilities; /* something added for WinXP SP2(?) */
+ RAW_DATA rdSSID;
+ RAW_DATA rdBSSID;
+ RAW_DATA rdBSSIDList;
+ RAW_DATA rdStSSIDList;
+ RAW_DATA rdCtrlData;
+#ifdef UNDER_CE
+ BOOL bInitialized;
+#endif
+ DWORD nWPAMCastCipher;
+ /* add some extra buffer for later additions since this interface is
+ * far from stable */
+ u8 later_additions[100];
+} INTF_ENTRY, *PINTF_ENTRY;
+
+#define INTF_ALL 0xffffffff
+#define INTF_ALL_FLAGS 0x0000ffff
+#define INTF_CTLFLAGS 0x00000010
+#define INTFCTL_ENABLED 0x8000
+#endif /* _WIN32_WCE */
+
+
+#ifdef _WIN32_WCE
+static int wpa_driver_ndis_rebind_adapter(struct wpa_driver_ndis_data *drv)
+{
+ HANDLE ndis;
+ TCHAR multi[100];
+ int len;
+
+ len = _tcslen(drv->adapter_name);
+ if (len > 80)
+ return -1;
+
+ ndis = CreateFile(DD_NDIS_DEVICE_NAME, GENERIC_READ | GENERIC_WRITE,
+ 0, NULL, OPEN_EXISTING, 0, NULL);
+ if (ndis == INVALID_HANDLE_VALUE) {
+ wpa_printf(MSG_DEBUG, "NDIS: Failed to open file to NDIS "
+ "device: %d", (int) GetLastError());
+ return -1;
+ }
+
+ len++;
+ memcpy(multi, drv->adapter_name, len * sizeof(TCHAR));
+ memcpy(&multi[len], TEXT("NDISUIO\0"), 9 * sizeof(TCHAR));
+ len += 9;
+
+ if (!DeviceIoControl(ndis, IOCTL_NDIS_REBIND_ADAPTER,
+ multi, len * sizeof(TCHAR), NULL, 0, NULL, NULL))
+ {
+ wpa_printf(MSG_DEBUG, "NDIS: IOCTL_NDIS_REBIND_ADAPTER "
+ "failed: 0x%x", (int) GetLastError());
+ wpa_hexdump_ascii(MSG_DEBUG, "NDIS: rebind multi_sz",
+ (u8 *) multi, len * sizeof(TCHAR));
+ CloseHandle(ndis);
+ return -1;
+ }
+
+ CloseHandle(ndis);
+
+ wpa_printf(MSG_DEBUG, "NDIS: Requested NDIS rebind of NDISUIO "
+ "protocol");
+
+ return 0;
+}
+#endif /* _WIN32_WCE */
+
+
+static int wpa_driver_ndis_set_wzc(struct wpa_driver_ndis_data *drv,
+ int enable)
+{
+#ifdef _WIN32_WCE
+ HKEY hk, hk2;
+ LONG ret;
+ DWORD i, hnd, len;
+ TCHAR keyname[256], devname[256];
+
+#define WZC_DRIVER TEXT("Drivers\\BuiltIn\\ZeroConfig")
+
+ if (enable) {
+ HANDLE h;
+ h = ActivateDeviceEx(WZC_DRIVER, NULL, 0, NULL);
+ if (h == INVALID_HANDLE_VALUE || h == 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: Failed to re-enable WZC "
+ "- ActivateDeviceEx failed: %d",
+ (int) GetLastError());
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "NDIS: WZC re-enabled");
+ return wpa_driver_ndis_rebind_adapter(drv);
+ }
+
+ /*
+ * Unfortunately, just disabling the WZC for an interface is not enough
+ * to free NDISUIO for us, so need to disable and unload WZC completely
+ * for now when using WinCE with NDISUIO. In addition, must request
+ * NDISUIO protocol to be rebound to the adapter in order to free the
+ * NDISUIO binding that WZC hold before us.
+ */
+
+ /* Enumerate HKLM\Drivers\Active\* to find a handle to WZC. */
+ ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, DEVLOAD_ACTIVE_KEY, 0, 0, &hk);
+ if (ret != ERROR_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "NDIS: RegOpenKeyEx(DEVLOAD_ACTIVE_KEY) "
+ "failed: %d %d", (int) ret, (int) GetLastError());
+ return -1;
+ }
+
+ for (i = 0; ; i++) {
+ len = sizeof(keyname);
+ ret = RegEnumKeyEx(hk, i, keyname, &len, NULL, NULL, NULL,
+ NULL);
+ if (ret != ERROR_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "NDIS: Could not find active "
+ "WZC - assuming it is not running.");
+ RegCloseKey(hk);
+ return -1;
+ }
+
+ ret = RegOpenKeyEx(hk, keyname, 0, 0, &hk2);
+ if (ret != ERROR_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "NDIS: RegOpenKeyEx(active dev) "
+ "failed: %d %d",
+ (int) ret, (int) GetLastError());
+ continue;
+ }
+
+ len = sizeof(devname);
+ ret = RegQueryValueEx(hk2, DEVLOAD_DEVKEY_VALNAME, NULL, NULL,
+ (LPBYTE) devname, &len);
+ if (ret != ERROR_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "NDIS: RegQueryValueEx("
+ "DEVKEY_VALNAME) failed: %d %d",
+ (int) ret, (int) GetLastError());
+ RegCloseKey(hk2);
+ continue;
+ }
+
+ if (_tcscmp(devname, WZC_DRIVER) == 0)
+ break;
+
+ RegCloseKey(hk2);
+ }
+
+ RegCloseKey(hk);
+
+ /* Found WZC - get handle to it. */
+ len = sizeof(hnd);
+ ret = RegQueryValueEx(hk2, DEVLOAD_HANDLE_VALNAME, NULL, NULL,
+ (PUCHAR) &hnd, &len);
+ if (ret != ERROR_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "NDIS: RegQueryValueEx(HANDLE_VALNAME) "
+ "failed: %d %d", (int) ret, (int) GetLastError());
+ RegCloseKey(hk2);
+ return -1;
+ }
+
+ RegCloseKey(hk2);
+
+ /* Deactivate WZC */
+ if (!DeactivateDevice((HANDLE) hnd)) {
+ wpa_printf(MSG_DEBUG, "NDIS: DeactivateDevice failed: %d",
+ (int) GetLastError());
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "NDIS: Disabled WZC temporarily");
+ drv->wzc_disabled = 1;
+ return wpa_driver_ndis_rebind_adapter(drv);
+
+#else /* _WIN32_WCE */
+
+ HMODULE hm;
+ DWORD (WINAPI *wzc_enum_interf)(LPWSTR pSrvAddr,
+ PINTFS_KEY_TABLE pIntfs);
+ DWORD (WINAPI *wzc_query_interf)(LPWSTR pSrvAddr, DWORD dwInFlags,
+ PINTF_ENTRY pIntf,
+ LPDWORD pdwOutFlags);
+ DWORD (WINAPI *wzc_set_interf)(LPWSTR pSrvAddr, DWORD dwInFlags,
+ PINTF_ENTRY pIntf, LPDWORD pdwOutFlags);
+ int ret = -1, j;
+ DWORD res;
+ INTFS_KEY_TABLE guids;
+ INTF_ENTRY intf;
+ char guid[128];
+ WCHAR *pos;
+ DWORD flags, i;
+
+ hm = LoadLibrary(TEXT("wzcsapi.dll"));
+ if (hm == NULL) {
+ wpa_printf(MSG_DEBUG, "NDIS: Failed to load wzcsapi.dll (%u) "
+ "- WZC probably not running",
+ (unsigned int) GetLastError());
+ return -1;
+ }
+
+#ifdef _WIN32_WCE
+ wzc_enum_interf = (void *) GetProcAddressA(hm, "WZCEnumInterfaces");
+ wzc_query_interf = (void *) GetProcAddressA(hm, "WZCQueryInterface");
+ wzc_set_interf = (void *) GetProcAddressA(hm, "WZCSetInterface");
+#else /* _WIN32_WCE */
+ wzc_enum_interf = (void *) GetProcAddress(hm, "WZCEnumInterfaces");
+ wzc_query_interf = (void *) GetProcAddress(hm, "WZCQueryInterface");
+ wzc_set_interf = (void *) GetProcAddress(hm, "WZCSetInterface");
+#endif /* _WIN32_WCE */
+
+ if (wzc_enum_interf == NULL || wzc_query_interf == NULL ||
+ wzc_set_interf == NULL) {
+ wpa_printf(MSG_DEBUG, "NDIS: WZCEnumInterfaces, "
+ "WZCQueryInterface, or WZCSetInterface not found "
+ "in wzcsapi.dll");
+ goto fail;
+ }
+
+ os_memset(&guids, 0, sizeof(guids));
+ res = wzc_enum_interf(NULL, &guids);
+ if (res != 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: WZCEnumInterfaces failed: %d; "
+ "WZC service is apparently not running",
+ (int) res);
+ goto fail;
+ }
+
+ wpa_printf(MSG_DEBUG, "NDIS: WZCEnumInterfaces: %d interfaces",
+ (int) guids.dwNumIntfs);
+
+ for (i = 0; i < guids.dwNumIntfs; i++) {
+ pos = guids.pIntfs[i].wszGuid;
+ for (j = 0; j < sizeof(guid); j++) {
+ guid[j] = (char) *pos;
+ if (*pos == 0)
+ break;
+ pos++;
+ }
+ guid[sizeof(guid) - 1] = '\0';
+ wpa_printf(MSG_DEBUG, "NDIS: intfs %d GUID '%s'",
+ (int) i, guid);
+ if (os_strstr(drv->ifname, guid) == NULL)
+ continue;
+
+ wpa_printf(MSG_DEBUG, "NDIS: Current interface found from "
+ "WZC");
+ break;
+ }
+
+ if (i >= guids.dwNumIntfs) {
+ wpa_printf(MSG_DEBUG, "NDIS: Current interface not found from "
+ "WZC");
+ goto fail;
+ }
+
+ os_memset(&intf, 0, sizeof(intf));
+ intf.wszGuid = guids.pIntfs[i].wszGuid;
+ /* Set flags to verify that the structure has not changed. */
+ intf.dwCtlFlags = -1;
+ flags = 0;
+ res = wzc_query_interf(NULL, INTFCTL_ENABLED, &intf, &flags);
+ if (res != 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: Could not query flags for the "
+ "WZC interface: %d (0x%x)",
+ (int) res, (int) res);
+ wpa_printf(MSG_DEBUG, "NDIS: GetLastError: %u",
+ (unsigned int) GetLastError());
+ goto fail;
+ }
+
+ wpa_printf(MSG_DEBUG, "NDIS: WZC interface flags 0x%x dwCtlFlags 0x%x",
+ (int) flags, (int) intf.dwCtlFlags);
+
+ if (intf.dwCtlFlags == -1) {
+ wpa_printf(MSG_DEBUG, "NDIS: Looks like wzcsapi has changed "
+ "again - could not disable WZC");
+ wpa_hexdump(MSG_MSGDUMP, "NDIS: intf",
+ (u8 *) &intf, sizeof(intf));
+ goto fail;
+ }
+
+ if (enable) {
+ if (!(intf.dwCtlFlags & INTFCTL_ENABLED)) {
+ wpa_printf(MSG_DEBUG, "NDIS: Enabling WZC for this "
+ "interface");
+ intf.dwCtlFlags |= INTFCTL_ENABLED;
+ res = wzc_set_interf(NULL, INTFCTL_ENABLED, &intf,
+ &flags);
+ if (res != 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: Failed to enable "
+ "WZC: %d (0x%x)",
+ (int) res, (int) res);
+ wpa_printf(MSG_DEBUG, "NDIS: GetLastError: %u",
+ (unsigned int) GetLastError());
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "NDIS: Re-enabled WZC for this "
+ "interface");
+ drv->wzc_disabled = 0;
+ }
+ } else {
+ if (intf.dwCtlFlags & INTFCTL_ENABLED) {
+ wpa_printf(MSG_DEBUG, "NDIS: Disabling WZC for this "
+ "interface");
+ intf.dwCtlFlags &= ~INTFCTL_ENABLED;
+ res = wzc_set_interf(NULL, INTFCTL_ENABLED, &intf,
+ &flags);
+ if (res != 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: Failed to "
+ "disable WZC: %d (0x%x)",
+ (int) res, (int) res);
+ wpa_printf(MSG_DEBUG, "NDIS: GetLastError: %u",
+ (unsigned int) GetLastError());
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "NDIS: Disabled WZC temporarily "
+ "for this interface");
+ drv->wzc_disabled = 1;
+ } else {
+ wpa_printf(MSG_DEBUG, "NDIS: WZC was not enabled for "
+ "this interface");
+ }
+ }
+
+ ret = 0;
+
+fail:
+ FreeLibrary(hm);
+
+ return ret;
+#endif /* _WIN32_WCE */
+}
+
+#else /* CONFIG_NATIVE_WINDOWS || __CYGWIN__ */
+
+static int wpa_driver_ndis_set_wzc(struct wpa_driver_ndis_data *drv,
+ int enable)
+{
+ return 0;
+}
+
+#endif /* CONFIG_NATIVE_WINDOWS || __CYGWIN__ */
+
+
+#ifdef CONFIG_USE_NDISUIO
+/*
+ * l2_packet_ndis.c is sharing the same handle to NDISUIO, so we must be able
+ * to export this handle. This is somewhat ugly, but there is no better
+ * mechanism available to pass data from driver interface to l2_packet wrapper.
+ */
+static HANDLE driver_ndis_ndisuio_handle = INVALID_HANDLE_VALUE;
+
+HANDLE driver_ndis_get_ndisuio_handle(void)
+{
+ return driver_ndis_ndisuio_handle;
+}
+#endif /* CONFIG_USE_NDISUIO */
+
+
+static int wpa_driver_ndis_adapter_init(struct wpa_driver_ndis_data *drv)
+{
+#ifdef CONFIG_USE_NDISUIO
+#ifndef _WIN32_WCE
+#define NDISUIO_DEVICE_NAME TEXT("\\\\.\\\\Ndisuio")
+ DWORD written;
+#endif /* _WIN32_WCE */
+ drv->ndisuio = CreateFile(NDISUIO_DEVICE_NAME,
+ GENERIC_READ | GENERIC_WRITE, 0, NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
+ INVALID_HANDLE_VALUE);
+ if (drv->ndisuio == INVALID_HANDLE_VALUE) {
+ wpa_printf(MSG_ERROR, "NDIS: Failed to open connection to "
+ "NDISUIO: %d", (int) GetLastError());
+ return -1;
+ }
+ driver_ndis_ndisuio_handle = drv->ndisuio;
+
+#ifndef _WIN32_WCE
+ if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_BIND_WAIT, NULL, 0,
+ NULL, 0, &written, NULL)) {
+ wpa_printf(MSG_ERROR, "NDIS: IOCTL_NDISUIO_BIND_WAIT failed: "
+ "%d", (int) GetLastError());
+ CloseHandle(drv->ndisuio);
+ drv->ndisuio = INVALID_HANDLE_VALUE;
+ return -1;
+ }
+#endif /* _WIN32_WCE */
+
+ return 0;
+#else /* CONFIG_USE_NDISUIO */
+ return 0;
+#endif /* CONFIG_USE_NDISUIO */
+}
+
+
+static int wpa_driver_ndis_adapter_open(struct wpa_driver_ndis_data *drv)
+{
+#ifdef CONFIG_USE_NDISUIO
+ DWORD written;
+#define MAX_NDIS_DEVICE_NAME_LEN 256
+ WCHAR ifname[MAX_NDIS_DEVICE_NAME_LEN];
+ size_t len, i, pos;
+ const char *prefix = "\\DEVICE\\";
+
+#ifdef _WIN32_WCE
+ pos = 0;
+#else /* _WIN32_WCE */
+ pos = 8;
+#endif /* _WIN32_WCE */
+ len = pos + os_strlen(drv->ifname);
+ if (len >= MAX_NDIS_DEVICE_NAME_LEN)
+ return -1;
+ for (i = 0; i < pos; i++)
+ ifname[i] = (WCHAR) prefix[i];
+ for (i = pos; i < len; i++)
+ ifname[i] = (WCHAR) drv->ifname[i - pos];
+ ifname[i] = L'\0';
+
+ if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_OPEN_DEVICE,
+ ifname, len * sizeof(WCHAR), NULL, 0, &written,
+ NULL)) {
+ wpa_printf(MSG_ERROR, "NDIS: IOCTL_NDISUIO_OPEN_DEVICE "
+ "failed: %d", (int) GetLastError());
+ wpa_hexdump_ascii(MSG_DEBUG, "NDIS: ifname",
+ (const u8 *) ifname, len * sizeof(WCHAR));
+ CloseHandle(drv->ndisuio);
+ drv->ndisuio = INVALID_HANDLE_VALUE;
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "NDIS: Opened NDISUIO device successfully");
+
+ return 0;
+#else /* CONFIG_USE_NDISUIO */
+ char ifname[128];
+ os_snprintf(ifname, sizeof(ifname), "\\Device\\NPF_%s", drv->ifname);
+ drv->adapter = PacketOpenAdapter(ifname);
+ if (drv->adapter == NULL) {
+ wpa_printf(MSG_DEBUG, "NDIS: PacketOpenAdapter failed for "
+ "'%s'", ifname);
+ return -1;
+ }
+ return 0;
+#endif /* CONFIG_USE_NDISUIO */
+}
+
+
+static void wpa_driver_ndis_adapter_close(struct wpa_driver_ndis_data *drv)
+{
+#ifdef CONFIG_USE_NDISUIO
+ driver_ndis_ndisuio_handle = INVALID_HANDLE_VALUE;
+ if (drv->ndisuio != INVALID_HANDLE_VALUE)
+ CloseHandle(drv->ndisuio);
+#else /* CONFIG_USE_NDISUIO */
+ if (drv->adapter)
+ PacketCloseAdapter(drv->adapter);
+#endif /* CONFIG_USE_NDISUIO */
+}
+
+
+static int ndis_add_multicast(struct wpa_driver_ndis_data *drv)
+{
+ if (ndis_set_oid(drv, OID_802_3_MULTICAST_LIST,
+ (const char *) pae_group_addr, ETH_ALEN) < 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: Failed to add PAE group address "
+ "to the multicast list");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void * wpa_driver_ndis_init(void *ctx, const char *ifname)
+{
+ struct wpa_driver_ndis_data *drv;
+ u32 mode;
+
+ drv = os_zalloc(sizeof(*drv));
+ if (drv == NULL)
+ return NULL;
+ drv->ctx = ctx;
+ /*
+ * Compatibility code to strip possible prefix from the GUID. Previous
+ * versions include \Device\NPF_ prefix for all names, but the internal
+ * interface name is now only the GUI. Both Packet32 and NDISUIO
+ * prefixes are supported.
+ */
+ if (os_strncmp(ifname, "\\Device\\NPF_", 12) == 0)
+ ifname += 12;
+ else if (os_strncmp(ifname, "\\DEVICE\\", 8) == 0)
+ ifname += 8;
+ os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+
+ if (wpa_driver_ndis_adapter_init(drv) < 0) {
+ os_free(drv);
+ return NULL;
+ }
+
+ if (wpa_driver_ndis_get_names(drv) < 0) {
+ wpa_driver_ndis_adapter_close(drv);
+ os_free(drv);
+ return NULL;
+ }
+
+ wpa_driver_ndis_set_wzc(drv, 0);
+
+ if (wpa_driver_ndis_adapter_open(drv) < 0) {
+ wpa_driver_ndis_adapter_close(drv);
+ os_free(drv);
+ return NULL;
+ }
+
+ if (ndis_get_oid(drv, OID_802_3_CURRENT_ADDRESS,
+ (char *) drv->own_addr, ETH_ALEN) < 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: Get OID_802_3_CURRENT_ADDRESS "
+ "failed");
+ wpa_driver_ndis_adapter_close(drv);
+ os_free(drv);
+ return NULL;
+ }
+ wpa_driver_ndis_get_capability(drv);
+
+ /* Make sure that the driver does not have any obsolete PMKID entries.
+ */
+ wpa_driver_ndis_flush_pmkid(drv);
+
+ /*
+ * Disconnect to make sure that driver re-associates if it was
+ * connected.
+ */
+ wpa_driver_ndis_disconnect(drv);
+
+ eloop_register_timeout(1, 0, wpa_driver_ndis_poll_timeout, drv, NULL);
+
+#ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+ drv->events = ndis_events_init(&drv->events_pipe, &drv->event_avail,
+ drv->ifname, drv->adapter_desc);
+ if (drv->events == NULL) {
+ wpa_driver_ndis_deinit(drv);
+ return NULL;
+ }
+ eloop_register_event(drv->event_avail, sizeof(drv->event_avail),
+ wpa_driver_ndis_event_pipe_cb, drv, NULL);
+#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
+
+#ifdef _WIN32_WCE
+ if (ndisuio_notification_init(drv) < 0) {
+ wpa_driver_ndis_deinit(drv);
+ return NULL;
+ }
+#endif /* _WIN32_WCE */
+
+ /* Set mode here in case card was configured for ad-hoc mode
+ * previously. */
+ 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 */
+
+ 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);
+ }
+ }
+
+ return drv;
+}
+
+
+static void wpa_driver_ndis_deinit(void *priv)
+{
+ struct wpa_driver_ndis_data *drv = priv;
+
+#ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+ if (drv->events) {
+ eloop_unregister_event(drv->event_avail,
+ sizeof(drv->event_avail));
+ ndis_events_deinit(drv->events);
+ }
+#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
+
+#ifdef _WIN32_WCE
+ ndisuio_notification_deinit(drv);
+#endif /* _WIN32_WCE */
+
+ eloop_cancel_timeout(wpa_driver_ndis_scan_timeout, drv, drv->ctx);
+ eloop_cancel_timeout(wpa_driver_ndis_poll_timeout, drv, NULL);
+ wpa_driver_ndis_flush_pmkid(drv);
+ wpa_driver_ndis_disconnect(drv);
+ if (wpa_driver_ndis_radio_off(drv) < 0) {
+ wpa_printf(MSG_DEBUG, "NDIS: failed to disassociate and turn "
+ "radio off");
+ }
+
+ wpa_driver_ndis_adapter_close(drv);
+
+ if (drv->wzc_disabled)
+ wpa_driver_ndis_set_wzc(drv, 1);
+
+#ifdef _WIN32_WCE
+ os_free(drv->adapter_name);
+#endif /* _WIN32_WCE */
+ os_free(drv->adapter_desc);
+ os_free(drv);
+}
+
+
+static struct wpa_interface_info *
+wpa_driver_ndis_get_interfaces(void *global_priv)
+{
+ struct wpa_interface_info *iface = NULL, *niface;
+
+#ifdef CONFIG_USE_NDISUIO
+ NDISUIO_QUERY_BINDING *b;
+ size_t blen = sizeof(*b) + 1024;
+ int i, error;
+ DWORD written;
+ char name[256], desc[256];
+ WCHAR *pos;
+ size_t j, len;
+ HANDLE ndisuio;
+
+ ndisuio = CreateFile(NDISUIO_DEVICE_NAME,
+ GENERIC_READ | GENERIC_WRITE, 0, NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
+ INVALID_HANDLE_VALUE);
+ if (ndisuio == INVALID_HANDLE_VALUE) {
+ wpa_printf(MSG_ERROR, "NDIS: Failed to open connection to "
+ "NDISUIO: %d", (int) GetLastError());
+ return NULL;
+ }
+
+#ifndef _WIN32_WCE
+ if (!DeviceIoControl(ndisuio, IOCTL_NDISUIO_BIND_WAIT, NULL, 0,
+ NULL, 0, &written, NULL)) {
+ wpa_printf(MSG_ERROR, "NDIS: IOCTL_NDISUIO_BIND_WAIT failed: "
+ "%d", (int) GetLastError());
+ CloseHandle(ndisuio);
+ return NULL;
+ }
+#endif /* _WIN32_WCE */
+
+ b = os_malloc(blen);
+ if (b == NULL) {
+ CloseHandle(ndisuio);
+ return NULL;
+ }
+
+ for (i = 0; ; i++) {
+ os_memset(b, 0, blen);
+ b->BindingIndex = i;
+ if (!DeviceIoControl(ndisuio, IOCTL_NDISUIO_QUERY_BINDING,
+ b, sizeof(NDISUIO_QUERY_BINDING), b, blen,
+ &written, NULL)) {
+ error = (int) GetLastError();
+ if (error == ERROR_NO_MORE_ITEMS)
+ break;
+ wpa_printf(MSG_DEBUG, "IOCTL_NDISUIO_QUERY_BINDING "
+ "failed: %d", error);
+ break;
+ }
+
+ pos = (WCHAR *) ((char *) b + b->DeviceNameOffset);
+ len = b->DeviceNameLength;
+ if (len >= sizeof(name))
+ len = sizeof(name) - 1;
+ for (j = 0; j < len; j++)
+ name[j] = (char) pos[j];
+ name[len] = '\0';
+
+ pos = (WCHAR *) ((char *) b + b->DeviceDescrOffset);
+ len = b->DeviceDescrLength;
+ if (len >= sizeof(desc))
+ len = sizeof(desc) - 1;
+ for (j = 0; j < len; j++)
+ desc[j] = (char) pos[j];
+ desc[len] = '\0';
+
+ wpa_printf(MSG_DEBUG, "NDIS: %d - %s - %s", i, name, desc);
+
+ niface = os_zalloc(sizeof(*niface));
+ if (niface == NULL)
+ break;
+ niface->drv_name = "ndis";
+ if (os_strncmp(name, "\\DEVICE\\", 8) == 0)
+ niface->ifname = os_strdup(name + 8);
+ else
+ niface->ifname = os_strdup(name);
+ if (niface->ifname == NULL) {
+ os_free(niface);
+ break;
+ }
+ niface->desc = os_strdup(desc);
+ niface->next = iface;
+ iface = niface;
+ }
+
+ os_free(b);
+ CloseHandle(ndisuio);
+#else /* CONFIG_USE_NDISUIO */
+ PTSTR _names;
+ char *names, *pos, *pos2;
+ ULONG len;
+ BOOLEAN res;
+ char *name[MAX_ADAPTERS];
+ char *desc[MAX_ADAPTERS];
+ int num_name, num_desc, i;
+
+ wpa_printf(MSG_DEBUG, "NDIS: Packet.dll version: %s",
+ PacketGetVersion());
+
+ len = 8192;
+ _names = os_zalloc(len);
+ if (_names == NULL)
+ return NULL;
+
+ res = PacketGetAdapterNames(_names, &len);
+ if (!res && len > 8192) {
+ os_free(_names);
+ _names = os_zalloc(len);
+ if (_names == NULL)
+ return NULL;
+ res = PacketGetAdapterNames(_names, &len);
+ }
+
+ if (!res) {
+ wpa_printf(MSG_ERROR, "NDIS: Failed to get adapter list "
+ "(PacketGetAdapterNames)");
+ os_free(_names);
+ return NULL;
+ }
+
+ names = (char *) _names;
+ if (names[0] && names[1] == '\0' && names[2] && names[3] == '\0') {
+ wpa_printf(MSG_DEBUG, "NDIS: Looks like adapter names are in "
+ "UNICODE");
+ /* Convert to ASCII */
+ pos2 = pos = names;
+ while (pos2 < names + len) {
+ if (pos2[0] == '\0' && pos2[1] == '\0' &&
+ pos2[2] == '\0' && pos2[3] == '\0') {
+ pos2 += 4;
+ break;
+ }
+ *pos++ = pos2[0];
+ pos2 += 2;
+ }
+ os_memcpy(pos + 2, names, pos - names);
+ pos += 2;
+ } else
+ pos = names;
+
+ num_name = 0;
+ while (pos < names + len) {
+ name[num_name] = pos;
+ while (*pos && pos < names + len)
+ pos++;
+ if (pos + 1 >= names + len) {
+ os_free(names);
+ return NULL;
+ }
+ pos++;
+ num_name++;
+ if (num_name >= MAX_ADAPTERS) {
+ wpa_printf(MSG_DEBUG, "NDIS: Too many adapters");
+ os_free(names);
+ return NULL;
+ }
+ if (*pos == '\0') {
+ wpa_printf(MSG_DEBUG, "NDIS: %d adapter names found",
+ num_name);
+ pos++;
+ break;
+ }
+ }
+
+ num_desc = 0;
+ while (pos < names + len) {
+ desc[num_desc] = pos;
+ while (*pos && pos < names + len)
+ pos++;
+ if (pos + 1 >= names + len) {
+ os_free(names);
+ return NULL;
+ }
+ pos++;
+ num_desc++;
+ if (num_desc >= MAX_ADAPTERS) {
+ wpa_printf(MSG_DEBUG, "NDIS: Too many adapter "
+ "descriptions");
+ os_free(names);
+ return NULL;
+ }
+ if (*pos == '\0') {
+ wpa_printf(MSG_DEBUG, "NDIS: %d adapter descriptions "
+ "found", num_name);
+ pos++;
+ break;
+ }
+ }
+
+ /*
+ * Windows 98 with Packet.dll 3.0 alpha3 does not include adapter
+ * descriptions. Fill in dummy descriptors to work around this.
+ */
+ while (num_desc < num_name)
+ desc[num_desc++] = "dummy description";
+
+ if (num_name != num_desc) {
+ wpa_printf(MSG_DEBUG, "NDIS: mismatch in adapter name and "
+ "description counts (%d != %d)",
+ num_name, num_desc);
+ os_free(names);
+ return NULL;
+ }
+
+ for (i = 0; i < num_name; i++) {
+ niface = os_zalloc(sizeof(*niface));
+ if (niface == NULL)
+ break;
+ niface->drv_name = "ndis";
+ if (os_strncmp(name[i], "\\Device\\NPF_", 12) == 0)
+ niface->ifname = os_strdup(name[i] + 12);
+ else
+ niface->ifname = os_strdup(name[i]);
+ if (niface->ifname == NULL) {
+ os_free(niface);
+ break;
+ }
+ niface->desc = os_strdup(desc[i]);
+ niface->next = iface;
+ iface = niface;
+ }
+
+#endif /* CONFIG_USE_NDISUIO */
+
+ return iface;
+}
+
+
+const struct wpa_driver_ops wpa_driver_ndis_ops = {
+ "ndis",
+ "Windows NDIS driver",
+ wpa_driver_ndis_get_bssid,
+ wpa_driver_ndis_get_ssid,
+ wpa_driver_ndis_set_key,
+ wpa_driver_ndis_init,
+ wpa_driver_ndis_deinit,
+ NULL /* set_param */,
+ NULL /* set_countermeasures */,
+ wpa_driver_ndis_deauthenticate,
+ wpa_driver_ndis_disassociate,
+ wpa_driver_ndis_associate,
+ wpa_driver_ndis_add_pmkid,
+ wpa_driver_ndis_remove_pmkid,
+ wpa_driver_ndis_flush_pmkid,
+ wpa_driver_ndis_get_capa,
+ wpa_driver_ndis_poll,
+ wpa_driver_ndis_get_ifname,
+ wpa_driver_ndis_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_ndis_get_scan_results,
+ NULL /* set_country */,
+ NULL /* global_init */,
+ NULL /* global_deinit */,
+ NULL /* init2 */,
+ 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 /* send_action_cancel_wait */,
+ 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 */,
+ NULL /* shared_freq */,
+ NULL /* get_noa */,
+ NULL /* set_noa */,
+ NULL /* set_p2p_powersave */,
+ NULL /* ampdu */,
+ NULL /* set_intra_bss */,
+ NULL /* get_radio_name */,
+ NULL /* p2p_find */,
+ NULL /* p2p_stop_find */,
+ NULL /* p2p_listen */,
+ NULL /* p2p_connect */,
+ NULL /* wps_success_cb */,
+ NULL /* p2p_group_formation_failed */,
+ NULL /* p2p_set_params */,
+ NULL /* p2p_prov_disc_req */,
+ NULL /* p2p_sd_request */,
+ NULL /* p2p_sd_cancel_request */,
+ NULL /* p2p_sd_response */,
+ NULL /* p2p_service_update */,
+ NULL /* p2p_reject */,
+ NULL /* p2p_invite */,
+ NULL /* send_tdls_mgmt */,
+ NULL /* tdls_oper */,
+ NULL /* signal_poll */
+};
diff --git a/src/drivers/driver_ndis.h b/src/drivers/driver_ndis.h
new file mode 100644
index 0000000..f263f0e
--- /dev/null
+++ b/src/drivers/driver_ndis.h
@@ -0,0 +1,65 @@
+/*
+ * WPA Supplicant - Windows/NDIS driver interface
+ * Copyright (c) 2004-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
+ * 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 DRIVER_NDIS_H
+#define DRIVER_NDIS_H
+
+#ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+struct ndis_events_data;
+struct ndis_events_data * ndis_events_init(HANDLE *read_pipe, HANDLE *event,
+ const char *ifname,
+ const char *desc);
+void ndis_events_deinit(struct ndis_events_data *events);
+#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
+
+struct ndis_pmkid_entry {
+ struct ndis_pmkid_entry *next;
+ u8 bssid[ETH_ALEN];
+ u8 pmkid[16];
+};
+
+struct wpa_driver_ndis_data {
+ void *ctx;
+ char ifname[100]; /* GUID: {7EE3EFE5-C165-472F-986D-F6FBEDFE8C8D} */
+#ifdef _WIN32_WCE
+ TCHAR *adapter_name;
+ HANDLE event_queue; /* NDISUIO notifier MsgQueue */
+ HANDLE connected_event; /* WpaSupplicantConnected event */
+#endif /* _WIN32_WCE */
+ u8 own_addr[ETH_ALEN];
+#ifdef CONFIG_USE_NDISUIO
+ HANDLE ndisuio;
+#else /* CONFIG_USE_NDISUIO */
+ LPADAPTER adapter;
+#endif /* CONFIG_USE_NDISUIO */
+ u8 bssid[ETH_ALEN];
+
+ int has_capability;
+ int no_of_pmkid;
+ int radio_enabled;
+ struct wpa_driver_capa capa;
+ struct ndis_pmkid_entry *pmkid;
+ char *adapter_desc;
+ int wired;
+ int native80211;
+ int mode;
+ int wzc_disabled;
+ int oid_bssid_set;
+#ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+ HANDLE events_pipe, event_avail;
+ struct ndis_events_data *events;
+#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
+};
+
+#endif /* DRIVER_NDIS_H */
diff --git a/src/drivers/driver_ndis_.c b/src/drivers/driver_ndis_.c
new file mode 100644
index 0000000..4bee9aa
--- /dev/null
+++ b/src/drivers/driver_ndis_.c
@@ -0,0 +1,105 @@
+/*
+ * WPA Supplicant - Windows/NDIS driver interface - event processing
+ * Copyright (c) 2004-2005, 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 "driver.h"
+#include "eloop.h"
+
+/* Keep this event processing in a separate file and without WinPcap headers to
+ * avoid conflicts with some of the header files. */
+struct _ADAPTER;
+typedef struct _ADAPTER * LPADAPTER;
+#include "driver_ndis.h"
+
+
+void wpa_driver_ndis_event_connect(struct wpa_driver_ndis_data *drv);
+void wpa_driver_ndis_event_disconnect(struct wpa_driver_ndis_data *drv);
+void wpa_driver_ndis_event_media_specific(struct wpa_driver_ndis_data *drv,
+ const u8 *data, size_t data_len);
+void wpa_driver_ndis_event_adapter_arrival(struct wpa_driver_ndis_data *drv);
+void wpa_driver_ndis_event_adapter_removal(struct wpa_driver_ndis_data *drv);
+
+
+enum event_types { EVENT_CONNECT, EVENT_DISCONNECT,
+ EVENT_MEDIA_SPECIFIC, EVENT_ADAPTER_ARRIVAL,
+ EVENT_ADAPTER_REMOVAL };
+
+/* Event data:
+ * enum event_types (as int, i.e., 4 octets)
+ * data length (2 octets (big endian), optional)
+ * data (variable len, optional)
+ */
+
+
+static void wpa_driver_ndis_event_process(struct wpa_driver_ndis_data *drv,
+ u8 *buf, size_t len)
+{
+ u8 *pos, *data = NULL;
+ enum event_types type;
+ size_t data_len = 0;
+
+ wpa_hexdump(MSG_MSGDUMP, "NDIS: received event data", buf, len);
+ if (len < sizeof(int))
+ return;
+ type = *((int *) buf);
+ pos = buf + sizeof(int);
+ wpa_printf(MSG_DEBUG, "NDIS: event - type %d", type);
+
+ if (buf + len - pos > 2) {
+ data_len = (int) *pos++ << 8;
+ data_len += *pos++;
+ if (data_len > (size_t) (buf + len - pos)) {
+ wpa_printf(MSG_DEBUG, "NDIS: event data overflow");
+ return;
+ }
+ data = pos;
+ wpa_hexdump(MSG_MSGDUMP, "NDIS: event data", data, data_len);
+ }
+
+ switch (type) {
+ case EVENT_CONNECT:
+ wpa_driver_ndis_event_connect(drv);
+ break;
+ case EVENT_DISCONNECT:
+ wpa_driver_ndis_event_disconnect(drv);
+ break;
+ case EVENT_MEDIA_SPECIFIC:
+ wpa_driver_ndis_event_media_specific(drv, data, data_len);
+ break;
+ case EVENT_ADAPTER_ARRIVAL:
+ wpa_driver_ndis_event_adapter_arrival(drv);
+ break;
+ case EVENT_ADAPTER_REMOVAL:
+ wpa_driver_ndis_event_adapter_removal(drv);
+ break;
+ }
+}
+
+
+void wpa_driver_ndis_event_pipe_cb(void *eloop_data, void *user_data)
+{
+ struct wpa_driver_ndis_data *drv = eloop_data;
+ u8 buf[512];
+ DWORD len;
+
+ ResetEvent(drv->event_avail);
+ if (ReadFile(drv->events_pipe, buf, sizeof(buf), &len, NULL))
+ wpa_driver_ndis_event_process(drv, buf, len);
+ else {
+ wpa_printf(MSG_DEBUG, "%s: ReadFile() failed: %d", __func__,
+ (int) GetLastError());
+ }
+}
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
new file mode 100644
index 0000000..90b3f20
--- /dev/null
+++ b/src/drivers/driver_nl80211.c
@@ -0,0 +1,6550 @@
+/*
+ * 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
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <net/if.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/family.h>
+#include <netlink/genl/ctrl.h>
+#include <linux/rtnetlink.h>
+#include <netpacket/packet.h>
+#include <linux/filter.h>
+#include "nl80211_copy.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "utils/list.h"
+#include "common/ieee802_11_defs.h"
+#include "netlink.h"
+#include "linux_ioctl.h"
+#include "radiotap.h"
+#include "radiotap_iter.h"
+#include "rfkill.h"
+#include "driver.h"
+
+#ifdef CONFIG_LIBNL20
+/* libnl 2.0 compatibility code */
+#define nl_handle nl_sock
+#define nl80211_handle_alloc nl_socket_alloc_cb
+#define nl80211_handle_destroy nl_socket_free
+#else
+/*
+ * libnl 1.1 has a bug, it tries to allocate socket numbers densely
+ * but when you free a socket again it will mess up its bitmap and
+ * and use the wrong number the next time it needs a socket ID.
+ * Therefore, we wrap the handle alloc/destroy and add our own pid
+ * accounting.
+ */
+static uint32_t port_bitmap[32] = { 0 };
+
+static struct nl_handle *nl80211_handle_alloc(void *cb)
+{
+ struct nl_handle *handle;
+ uint32_t pid = getpid() & 0x3FFFFF;
+ int i;
+
+ handle = nl_handle_alloc_cb(cb);
+
+ for (i = 0; i < 1024; i++) {
+ if (port_bitmap[i / 32] & (1 << (i % 32)))
+ continue;
+ port_bitmap[i / 32] |= 1 << (i % 32);
+ pid += i << 22;
+ break;
+ }
+
+ nl_socket_set_local_port(handle, pid);
+
+ return handle;
+}
+
+static void nl80211_handle_destroy(struct nl_handle *handle)
+{
+ uint32_t port = nl_socket_get_local_port(handle);
+
+ port >>= 22;
+ port_bitmap[port / 32] &= ~(1 << (port % 32));
+
+ nl_handle_destroy(handle);
+}
+#endif /* CONFIG_LIBNL20 */
+
+
+#ifndef IFF_LOWER_UP
+#define IFF_LOWER_UP 0x10000 /* driver signals L1 up */
+#endif
+#ifndef IFF_DORMANT
+#define IFF_DORMANT 0x20000 /* driver signals dormant */
+#endif
+
+#ifndef IF_OPER_DORMANT
+#define IF_OPER_DORMANT 5
+#endif
+#ifndef IF_OPER_UP
+#define IF_OPER_UP 6
+#endif
+
+struct nl80211_global {
+ struct dl_list interfaces;
+};
+
+struct i802_bss {
+ struct wpa_driver_nl80211_data *drv;
+ struct i802_bss *next;
+ int ifindex;
+ char ifname[IFNAMSIZ + 1];
+ char brname[IFNAMSIZ];
+ unsigned int beacon_set:1;
+ unsigned int added_if_into_bridge:1;
+ unsigned int added_bridge:1;
+};
+
+struct wpa_driver_nl80211_data {
+ struct nl80211_global *global;
+ struct dl_list list;
+ u8 addr[ETH_ALEN];
+ char phyname[32];
+ void *ctx;
+ struct netlink_data *netlink;
+ int ioctl_sock; /* socket for ioctl() use */
+ int ifindex;
+ int if_removed;
+ int if_disabled;
+ struct rfkill_data *rfkill;
+ struct wpa_driver_capa capa;
+ int has_capability;
+
+ int operstate;
+
+ int scan_complete_events;
+
+ struct nl_handle *nl_handle;
+ struct nl_handle *nl_handle_event;
+ struct nl_handle *nl_handle_preq;
+ struct nl_cache *nl_cache;
+ struct nl_cache *nl_cache_event;
+ struct nl_cache *nl_cache_preq;
+ struct nl_cb *nl_cb;
+ struct genl_family *nl80211;
+
+ 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;
+ int disable_11b_rates;
+
+ unsigned int pending_remain_on_chan:1;
+
+ u64 remain_on_chan_cookie;
+ u64 send_action_cookie;
+
+ unsigned int last_mgmt_freq;
+
+ 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_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);
+static int nl80211_send_frame_cmd(struct wpa_driver_nl80211_data *drv,
+ unsigned int freq, unsigned int wait,
+ const u8 *buf, size_t buf_len, u64 *cookie);
+static int wpa_driver_nl80211_probe_req_report(void *priv, int report);
+
+#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 int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv,
+ int ifindex, int disabled);
+
+static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv);
+
+
+/* nl80211 code */
+static int ack_handler(struct nl_msg *msg, void *arg)
+{
+ int *err = arg;
+ *err = 0;
+ return NL_STOP;
+}
+
+static int finish_handler(struct nl_msg *msg, void *arg)
+{
+ int *ret = arg;
+ *ret = 0;
+ return NL_SKIP;
+}
+
+static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
+ void *arg)
+{
+ int *ret = arg;
+ *ret = err->error;
+ return NL_SKIP;
+}
+
+
+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;
+
+ cb = nl_cb_clone(drv->nl_cb);
+ if (!cb)
+ goto out;
+
+ err = nl_send_auto_complete(nl_handle, msg);
+ if (err < 0)
+ goto out;
+
+ err = 1;
+
+ nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err);
+ nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err);
+ nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err);
+
+ if (valid_handler)
+ nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM,
+ valid_handler, valid_data);
+
+ while (err > 0)
+ nl_recvmsgs(nl_handle, cb);
+ out:
+ nl_cb_put(cb);
+ nlmsg_free(msg);
+ return err;
+}
+
+
+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;
+};
+
+
+static int family_handler(struct nl_msg *msg, void *arg)
+{
+ struct family_data *res = arg;
+ struct nlattr *tb[CTRL_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct nlattr *mcgrp;
+ int i;
+
+ nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+ if (!tb[CTRL_ATTR_MCAST_GROUPS])
+ return NL_SKIP;
+
+ nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], i) {
+ struct nlattr *tb2[CTRL_ATTR_MCAST_GRP_MAX + 1];
+ nla_parse(tb2, CTRL_ATTR_MCAST_GRP_MAX, nla_data(mcgrp),
+ nla_len(mcgrp), NULL);
+ if (!tb2[CTRL_ATTR_MCAST_GRP_NAME] ||
+ !tb2[CTRL_ATTR_MCAST_GRP_ID] ||
+ os_strncmp(nla_data(tb2[CTRL_ATTR_MCAST_GRP_NAME]),
+ res->group,
+ nla_len(tb2[CTRL_ATTR_MCAST_GRP_NAME])) != 0)
+ continue;
+ res->id = nla_get_u32(tb2[CTRL_ATTR_MCAST_GRP_ID]);
+ break;
+ };
+
+ return NL_SKIP;
+}
+
+
+static int nl_get_multicast_id(struct wpa_driver_nl80211_data *drv,
+ const char *family, const char *group)
+{
+ struct nl_msg *msg;
+ int ret = -1;
+ struct family_data res = { group, -ENOENT };
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+ genlmsg_put(msg, 0, 0, genl_ctrl_resolve(drv->nl_handle, "nlctrl"),
+ 0, 0, CTRL_CMD_GETFAMILY, 0);
+ NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family);
+
+ ret = send_and_recv_msgs(drv, msg, family_handler, &res);
+ msg = NULL;
+ if (ret == 0)
+ ret = res.id;
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+static int wpa_driver_nl80211_get_bssid(void *priv, u8 *bssid)
+{
+ 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;
+}
+
+
+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;
+}
+
+
+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;
+
+ 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;
+
+ 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;
+ }
+
+ wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
+}
+
+
+static int wpa_driver_nl80211_own_ifname(struct wpa_driver_nl80211_data *drv,
+ u8 *buf, size_t len)
+{
+ int attrlen, rta_len;
+ struct rtattr *attr;
+
+ attrlen = len;
+ attr = (struct rtattr *) buf;
+
+ 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;
+ }
+ attr = RTA_NEXT(attr, attrlen);
+ }
+
+ return 0;
+}
+
+
+static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv,
+ int ifindex, u8 *buf, size_t len)
+{
+ if (drv->ifindex == ifindex)
+ return 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;
+ }
+
+ return 0;
+}
+
+
+static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
+ struct ifinfomsg *ifi,
+ u8 *buf, size_t len)
+{
+ struct wpa_driver_nl80211_data *drv = ctx;
+ int attrlen, rta_len;
+ struct rtattr *attr;
+ u32 brid = 0;
+
+ 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;
+ }
+
+ 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]" : "");
+
+ if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Interface down");
+ drv->if_disabled = 1;
+ wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED, NULL);
+ }
+
+ if (drv->if_disabled && (ifi->ifi_flags & IFF_UP)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Interface up");
+ drv->if_disabled = 0;
+ wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED, NULL);
+ }
+
+ /*
+ * 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);
+
+ 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);
+ }
+
+#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 void wpa_driver_nl80211_event_rtm_dellink(void *ctx,
+ struct ifinfomsg *ifi,
+ u8 *buf, size_t len)
+{
+ struct wpa_driver_nl80211_data *drv = ctx;
+ int attrlen, rta_len;
+ struct rtattr *attr;
+ u32 brid = 0;
+
+ 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, 1);
+ } else if (attr->rta_type == IFLA_MASTER)
+ brid = nla_get_u32((struct nlattr *) attr);
+ attr = RTA_NEXT(attr, attrlen);
+ }
+
+#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);
+ }
+#endif /* HOSTAPD */
+}
+
+
+static void mlme_event_auth(struct wpa_driver_nl80211_data *drv,
+ const u8 *frame, size_t len)
+{
+ const struct ieee80211_mgmt *mgmt;
+ union wpa_event_data event;
+
+ mgmt = (const struct ieee80211_mgmt *) frame;
+ if (len < 24 + sizeof(mgmt->u.auth)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Too short association event "
+ "frame");
+ return;
+ }
+
+ 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);
+ }
+
+ wpa_supplicant_event(drv->ctx, EVENT_AUTH, &event);
+}
+
+
+static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
+ const u8 *frame, size_t len)
+{
+ const struct ieee80211_mgmt *mgmt;
+ union wpa_event_data event;
+ u16 status;
+
+ 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;
+ }
+
+ status = le_to_host16(mgmt->u.assoc_resp.status_code);
+ if (status != WLAN_STATUS_SUCCESS) {
+ os_memset(&event, 0, sizeof(event));
+ event.assoc_reject.bssid = mgmt->bssid;
+ 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;
+
+ wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event);
+ return;
+ }
+
+ drv->associated = 1;
+ os_memcpy(drv->bssid, mgmt->sa, ETH_ALEN);
+
+ 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);
+ }
+
+ event.assoc_info.freq = drv->assoc_freq;
+
+ wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
+}
+
+
+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)
+{
+ union wpa_event_data event;
+
+ 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;
+ }
+
+ os_memset(&event, 0, sizeof(event));
+ if (cmd == NL80211_CMD_CONNECT &&
+ nla_get_u16(status) != WLAN_STATUS_SUCCESS) {
+ if (addr)
+ event.assoc_reject.bssid = nla_data(addr);
+ 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;
+ }
+
+ drv->associated = 1;
+ if (addr)
+ os_memcpy(drv->bssid, nla_data(addr), ETH_ALEN);
+
+ 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);
+ }
+
+ wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
+}
+
+
+static void mlme_timeout_event(struct wpa_driver_nl80211_data *drv,
+ enum nl80211_commands cmd, struct nlattr *addr)
+{
+ union wpa_event_data event;
+ enum wpa_event_type ev;
+
+ if (nla_len(addr) != ETH_ALEN)
+ return;
+
+ wpa_printf(MSG_DEBUG, "nl80211: MLME event %d; timeout with " MACSTR,
+ cmd, MAC2STR((u8 *) nla_data(addr)));
+
+ if (cmd == NL80211_CMD_AUTHENTICATE)
+ ev = EVENT_AUTH_TIMED_OUT;
+ else if (cmd == NL80211_CMD_ASSOCIATE)
+ ev = EVENT_ASSOC_TIMED_OUT;
+ else
+ return;
+
+ 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 void mlme_event_mgmt(struct wpa_driver_nl80211_data *drv,
+ struct nlattr *freq, const u8 *frame, size_t len)
+{
+ const struct ieee80211_mgmt *mgmt;
+ union wpa_event_data event;
+ u16 fc, stype;
+
+ mgmt = (const struct ieee80211_mgmt *) frame;
+ if (len < 24) {
+ wpa_printf(MSG_DEBUG, "nl80211: Too short action frame");
+ return;
+ }
+
+ fc = le_to_host16(mgmt->frame_control);
+ stype = WLAN_FC_GET_STYPE(fc);
+
+ os_memset(&event, 0, sizeof(event));
+ if (freq) {
+ event.rx_action.freq = nla_get_u32(freq);
+ drv->last_mgmt_freq = event.rx_action.freq;
+ }
+ if (stype == WLAN_FC_STYPE_ACTION) {
+ 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;
+ wpa_supplicant_event(drv->ctx, EVENT_RX_ACTION, &event);
+ } else {
+ event.rx_mgmt.frame = frame;
+ event.rx_mgmt.frame_len = len;
+ wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
+ }
+}
+
+
+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 event;
+ const struct ieee80211_hdr *hdr;
+ u16 fc;
+ u64 cookie_val;
+
+ if (!cookie)
+ return;
+
+ cookie_val = nla_get_u64(cookie);
+ wpa_printf(MSG_DEBUG, "nl80211: Action TX status: cookie=0%llx%s "
+ "(ack=%d)",
+ (long long unsigned int) cookie_val,
+ cookie_val == drv->send_action_cookie ?
+ " (match)" : " (unknown)", ack != NULL);
+ if (cookie_val != drv->send_action_cookie)
+ return;
+
+ hdr = (const struct ieee80211_hdr *) frame;
+ 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 = frame;
+ event.tx_status.data_len = len;
+ event.tx_status.ack = ack != NULL;
+ wpa_supplicant_event(drv->ctx, EVENT_TX_STATUS, &event);
+}
+
+
+static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv,
+ enum wpa_event_type type,
+ const u8 *frame, size_t len)
+{
+ const struct ieee80211_mgmt *mgmt;
+ union wpa_event_data event;
+ const u8 *bssid = NULL;
+ u16 reason_code = 0;
+
+ mgmt = (const struct ieee80211_mgmt *) frame;
+ if (len >= 24) {
+ bssid = mgmt->bssid;
+
+ 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;
+ }
+ }
+
+ 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);
+
+ if (type == EVENT_DISASSOC) {
+ event.disassoc_info.addr = bssid;
+ event.disassoc_info.reason_code = reason_code;
+ if (frame + len > mgmt->u.disassoc.variable) {
+ event.disassoc_info.ie = mgmt->u.disassoc.variable;
+ event.disassoc_info.ie_len = frame + len -
+ mgmt->u.disassoc.variable;
+ }
+ } else {
+ event.deauth_info.addr = bssid;
+ event.deauth_info.reason_code = reason_code;
+ if (frame + len > mgmt->u.deauth.variable) {
+ event.deauth_info.ie = mgmt->u.deauth.variable;
+ event.deauth_info.ie_len = frame + len -
+ mgmt->u.deauth.variable;
+ }
+ }
+
+ wpa_supplicant_event(drv->ctx, type, &event);
+}
+
+
+static void mlme_event_unprot_disconnect(struct wpa_driver_nl80211_data *drv,
+ enum wpa_event_type type,
+ const u8 *frame, size_t len)
+{
+ const struct ieee80211_mgmt *mgmt;
+ union wpa_event_data event;
+ u16 reason_code = 0;
+
+ if (len < 24)
+ return;
+
+ mgmt = (const struct ieee80211_mgmt *) frame;
+
+ 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);
+
+ if (type == EVENT_UNPROT_DISASSOC) {
+ event.unprot_disassoc.sa = mgmt->sa;
+ event.unprot_disassoc.da = mgmt->da;
+ event.unprot_disassoc.reason_code = reason_code;
+ } else {
+ event.unprot_deauth.sa = mgmt->sa;
+ event.unprot_deauth.da = mgmt->da;
+ event.unprot_deauth.reason_code = reason_code;
+ }
+
+ wpa_supplicant_event(drv->ctx, type, &event);
+}
+
+
+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)
+{
+ if (timed_out && addr) {
+ mlme_timeout_event(drv, cmd, addr);
+ return;
+ }
+
+ if (frame == NULL) {
+ wpa_printf(MSG_DEBUG, "nl80211: MLME event %d without frame "
+ "data", cmd);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "nl80211: MLME event %d", cmd);
+ wpa_hexdump(MSG_MSGDUMP, "nl80211: MLME event frame",
+ nla_data(frame), nla_len(frame));
+
+ 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_FRAME:
+ mlme_event_mgmt(drv, freq, nla_data(frame), nla_len(frame));
+ break;
+ case NL80211_CMD_FRAME_TX_STATUS:
+ mlme_event_action_tx_status(drv, cookie, nla_data(frame),
+ nla_len(frame), ack);
+ break;
+ case NL80211_CMD_UNPROT_DEAUTHENTICATE:
+ mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DEAUTH,
+ nla_data(frame), nla_len(frame));
+ break;
+ case NL80211_CMD_UNPROT_DISASSOCIATE:
+ mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DISASSOC,
+ nla_data(frame), nla_len(frame));
+ break;
+ default:
+ break;
+ }
+}
+
+
+static void mlme_event_michael_mic_failure(struct wpa_driver_nl80211_data *drv,
+ struct nlattr *tb[])
+{
+ union wpa_event_data data;
+
+ 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;
+
+ 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);
+ }
+
+ wpa_supplicant_event(drv->ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
+}
+
+
+static void mlme_event_join_ibss(struct wpa_driver_nl80211_data *drv,
+ struct nlattr *tb[])
+{
+ 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));
+
+ wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL);
+}
+
+
+static void mlme_event_remain_on_channel(struct wpa_driver_nl80211_data *drv,
+ int cancel_event, struct nlattr *tb[])
+{
+ unsigned int freq, chan_type, duration;
+ union wpa_event_data data;
+ u64 cookie;
+
+ if (tb[NL80211_ATTR_WIPHY_FREQ])
+ freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
+ else
+ freq = 0;
+
+ if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE])
+ chan_type = nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
+ else
+ chan_type = 0;
+
+ if (tb[NL80211_ATTR_DURATION])
+ duration = nla_get_u32(tb[NL80211_ATTR_DURATION]);
+ else
+ duration = 0;
+
+ if (tb[NL80211_ATTR_COOKIE])
+ cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]);
+ else
+ cookie = 0;
+
+ 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");
+
+ if (cookie != drv->remain_on_chan_cookie)
+ return; /* not for us */
+
+ drv->pending_remain_on_chan = !cancel_event;
+
+ 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 send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted,
+ struct nlattr *tb[])
+{
+ 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;
+
+ os_memset(&event, 0, sizeof(event));
+ info = &event.scan_info;
+ info->aborted = aborted;
+
+ 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;
+ }
+ }
+ 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 int get_link_signal(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1];
+ static struct nla_policy policy[NL80211_STA_INFO_MAX + 1] = {
+ [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
+ };
+ struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1];
+ static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
+ [NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 },
+ [NL80211_RATE_INFO_MCS] = { .type = NLA_U8 },
+ [NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG },
+ [NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG },
+ };
+ struct wpa_signal_info *sig_change = arg;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+ if (!tb[NL80211_ATTR_STA_INFO] ||
+ nla_parse_nested(sinfo, NL80211_STA_INFO_MAX,
+ tb[NL80211_ATTR_STA_INFO], policy))
+ return NL_SKIP;
+ if (!sinfo[NL80211_STA_INFO_SIGNAL])
+ return NL_SKIP;
+
+ sig_change->current_signal =
+ (s8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]);
+
+ if (sinfo[NL80211_STA_INFO_TX_BITRATE]) {
+ if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX,
+ sinfo[NL80211_STA_INFO_TX_BITRATE],
+ rate_policy)) {
+ sig_change->current_txrate = 0;
+ } else {
+ if (rinfo[NL80211_RATE_INFO_BITRATE]) {
+ sig_change->current_txrate =
+ nla_get_u16(rinfo[
+ NL80211_RATE_INFO_BITRATE]) * 100;
+ }
+ }
+ }
+
+ return NL_SKIP;
+}
+
+
+static int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv,
+ struct wpa_signal_info *sig)
+{
+ struct nl_msg *msg;
+
+ sig->current_signal = -9999;
+ sig->current_txrate = 0;
+
+ 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_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid);
+
+ return send_and_recv_msgs(drv, msg, get_link_signal, sig);
+ nla_put_failure:
+ return -ENOBUFS;
+}
+
+
+static int get_link_noise(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1];
+ static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = {
+ [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 },
+ [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 },
+ };
+ struct wpa_signal_info *sig_change = arg;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ if (!tb[NL80211_ATTR_SURVEY_INFO]) {
+ wpa_printf(MSG_DEBUG, "nl80211: survey data missing!");
+ return NL_SKIP;
+ }
+
+ if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX,
+ tb[NL80211_ATTR_SURVEY_INFO],
+ survey_policy)) {
+ wpa_printf(MSG_DEBUG, "nl80211: failed to parse nested "
+ "attributes!");
+ return NL_SKIP;
+ }
+
+ if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY])
+ return NL_SKIP;
+
+ if (nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) !=
+ sig_change->frequency)
+ return NL_SKIP;
+
+ if (!sinfo[NL80211_SURVEY_INFO_NOISE])
+ return NL_SKIP;
+
+ sig_change->current_noise =
+ (s8) nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]);
+
+ return NL_SKIP;
+}
+
+
+static int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv,
+ struct wpa_signal_info *sig_change)
+{
+ struct nl_msg *msg;
+
+ sig_change->current_noise = 9999;
+ sig_change->frequency = drv->assoc_freq;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ NLM_F_DUMP, NL80211_CMD_GET_SURVEY, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+ return send_and_recv_msgs(drv, msg, get_link_noise, sig_change);
+ nla_put_failure:
+ return -ENOBUFS;
+}
+
+
+static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv,
+ struct nlattr *tb[])
+{
+ 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 },
+ [NL80211_ATTR_CQM_PKT_LOSS_EVENT] = { .type = NLA_U32 },
+ };
+ struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1];
+ enum nl80211_cqm_rssi_threshold_event event;
+ union wpa_event_data ed;
+ struct wpa_signal_info sig;
+ int res;
+
+ 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;
+ }
+
+ os_memset(&ed, 0, sizeof(ed));
+
+ if (cqm[NL80211_ATTR_CQM_PKT_LOSS_EVENT]) {
+ if (!tb[NL80211_ATTR_MAC])
+ return;
+ os_memcpy(ed.low_ack.addr, nla_data(tb[NL80211_ATTR_MAC]),
+ ETH_ALEN);
+ wpa_supplicant_event(drv->ctx, EVENT_STATION_LOW_ACK, &ed);
+ return;
+ }
+
+ if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] == NULL)
+ return;
+ event = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]);
+
+ 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;
+
+ res = nl80211_get_link_signal(drv, &sig);
+ if (res == 0) {
+ ed.signal_change.current_signal = sig.current_signal;
+ ed.signal_change.current_txrate = sig.current_txrate;
+ wpa_printf(MSG_DEBUG, "nl80211: Signal: %d dBm txrate: %d",
+ sig.current_signal, sig.current_txrate);
+ }
+
+ res = nl80211_get_link_noise(drv, &sig);
+ if (res == 0) {
+ ed.signal_change.current_noise = sig.current_noise;
+ wpa_printf(MSG_DEBUG, "nl80211: Noise: %d dBm",
+ sig.current_noise);
+ }
+
+ wpa_supplicant_event(drv->ctx, EVENT_SIGNAL_CHANGE, &ed);
+}
+
+
+static void nl80211_new_station_event(struct wpa_driver_nl80211_data *drv,
+ struct nlattr **tb)
+{
+ u8 *addr;
+ union wpa_event_data data;
+
+ if (tb[NL80211_ATTR_MAC] == NULL)
+ return;
+ addr = nla_data(tb[NL80211_ATTR_MAC]);
+ wpa_printf(MSG_DEBUG, "nl80211: New station " MACSTR, MAC2STR(addr));
+ if (drv->nlmode != NL80211_IFTYPE_ADHOC)
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+ os_memcpy(data.ibss_rsn_start.peer, addr, ETH_ALEN);
+ wpa_supplicant_event(drv->ctx, EVENT_IBSS_RSN_START, &data);
+}
+
+
+static void nl80211_del_station_event(struct wpa_driver_nl80211_data *drv,
+ struct nlattr **tb)
+{
+ u8 *addr;
+ union wpa_event_data data;
+
+ if (tb[NL80211_ATTR_MAC] == NULL)
+ return;
+ addr = nla_data(tb[NL80211_ATTR_MAC]);
+ wpa_printf(MSG_DEBUG, "nl80211: Delete station " MACSTR,
+ MAC2STR(addr));
+ if (drv->nlmode != NL80211_IFTYPE_ADHOC)
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+ os_memcpy(data.ibss_peer_lost.peer, addr, ETH_ALEN);
+ wpa_supplicant_event(drv->ctx, EVENT_IBSS_PEER_LOST, &data);
+}
+
+
+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 && !have_ifidx(drv, ifindex)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Ignored event (cmd=%d)"
+ " for foreign interface (ifindex %d)",
+ gnlh->cmd, ifindex);
+ return NL_SKIP;
+ }
+ }
+
+ 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");
+ break;
+ case NL80211_CMD_NEW_SCAN_RESULTS:
+ wpa_printf(MSG_DEBUG, "nl80211: New scan results available");
+ drv->scan_complete_events = 1;
+ eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv,
+ drv->ctx);
+ send_scan_event(drv, 0, tb);
+ break;
+ case NL80211_CMD_SCAN_ABORTED:
+ wpa_printf(MSG_DEBUG, "nl80211: Scan aborted");
+ /*
+ * Need to indicate that scan results are available in order
+ * not to make wpa_supplicant stop its scanning.
+ */
+ eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv,
+ drv->ctx);
+ 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_FRAME:
+ case NL80211_CMD_FRAME_TX_STATUS:
+ case NL80211_CMD_UNPROT_DEAUTHENTICATE:
+ case NL80211_CMD_UNPROT_DISASSOCIATE:
+ 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;
+ case NL80211_CMD_REG_CHANGE:
+ wpa_printf(MSG_DEBUG, "nl80211: Regulatory domain change");
+ wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED,
+ NULL);
+ break;
+ case NL80211_CMD_REG_BEACON_HINT:
+ wpa_printf(MSG_DEBUG, "nl80211: Regulatory beacon hint");
+ wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED,
+ NULL);
+ break;
+ case NL80211_CMD_NEW_STATION:
+ nl80211_new_station_event(drv, tb);
+ break;
+ case NL80211_CMD_DEL_STATION:
+ nl80211_del_station_event(drv, tb);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event "
+ "(cmd=%d)", gnlh->cmd);
+ break;
+ }
+
+ return NL_SKIP;
+}
+
+
+static void wpa_driver_nl80211_event_receive(int sock, void *eloop_ctx,
+ void *handle)
+{
+ struct nl_cb *cb;
+ struct wpa_driver_nl80211_data *drv = eloop_ctx;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Event message available");
+
+ cb = nl_cb_clone(drv->nl_cb);
+ if (!cb)
+ 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(handle, cb);
+ nl_cb_put(cb);
+}
+
+
+/**
+ * wpa_driver_nl80211_set_country - ask nl80211 to set the regulatory domain
+ * @priv: driver_nl80211 private data
+ * @alpha2_arg: country to which to switch to
+ * Returns: 0 on success, -1 on failure
+ *
+ * This asks nl80211 to set the regulatory domain for given
+ * country ISO / IEC alpha2.
+ */
+static int wpa_driver_nl80211_set_country(void *priv, const char *alpha2_arg)
+{
+ 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)
+ return -ENOMEM;
+
+ alpha2[0] = alpha2_arg[0];
+ alpha2[1] = alpha2_arg[1];
+ alpha2[2] = '\0';
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, NL80211_CMD_REQ_SET_REG, 0);
+
+ NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, alpha2);
+ if (send_and_recv_msgs(drv, msg, NULL, NULL))
+ return -EINVAL;
+ return 0;
+nla_put_failure:
+ return -EINVAL;
+}
+
+
+struct wiphy_info_data {
+ int max_scan_ssids;
+ int ap_supported;
+ int p2p_supported;
+ int auth_supported;
+ int connect_supported;
+ int offchan_tx_supported;
+ int max_remain_on_chan;
+};
+
+
+static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct wiphy_info_data *info = arg;
+ int p2p_go_supported = 0, p2p_client_supported = 0;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ if (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS])
+ info->max_scan_ssids =
+ nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]);
+
+ if (tb[NL80211_ATTR_SUPPORTED_IFTYPES]) {
+ struct nlattr *nl_mode;
+ int i;
+ nla_for_each_nested(nl_mode,
+ tb[NL80211_ATTR_SUPPORTED_IFTYPES], i) {
+ switch (nla_type(nl_mode)) {
+ case NL80211_IFTYPE_AP:
+ info->ap_supported = 1;
+ break;
+ case NL80211_IFTYPE_P2P_GO:
+ p2p_go_supported = 1;
+ break;
+ case NL80211_IFTYPE_P2P_CLIENT:
+ p2p_client_supported = 1;
+ break;
+ }
+ }
+ }
+
+ info->p2p_supported = p2p_go_supported && p2p_client_supported;
+
+ 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;
+ }
+ }
+
+ if (tb[NL80211_ATTR_OFFCHANNEL_TX_OK])
+ info->offchan_tx_supported = 1;
+
+ if (tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION])
+ info->max_remain_on_chan =
+ nla_get_u32(tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION]);
+
+ return NL_SKIP;
+}
+
+
+static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv,
+ struct wiphy_info_data *info)
+{
+ struct nl_msg *msg;
+
+ os_memset(info, 0, sizeof(*info));
+
+ /* default to 5000 since early versions of mac80211 don't set it */
+ info->max_remain_on_chan = 5000;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, NL80211_CMD_GET_WIPHY, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->first_bss.ifindex);
+
+ 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_capa(struct wpa_driver_nl80211_data *drv)
+{
+ 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;
+
+ drv->capa.max_scan_ssids = info.max_scan_ssids;
+ if (info.ap_supported)
+ drv->capa.flags |= WPA_DRIVER_FLAGS_AP;
+
+ 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 (info.offchan_tx_supported) {
+ wpa_printf(MSG_DEBUG, "nl80211: Using driver-based "
+ "off-channel TX");
+ drv->capa.flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_TX;
+ }
+
+ drv->capa.flags |= WPA_DRIVER_FLAGS_SANE_ERROR_CODES;
+ drv->capa.flags |= WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE;
+ if (info.p2p_supported)
+ drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE;
+ drv->capa.flags |= WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
+ drv->capa.max_remain_on_chan = info.max_remain_on_chan;
+
+ return 0;
+}
+
+
+static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv)
+{
+ int ret;
+
+ /* Initialize generic netlink and nl80211 */
+
+ drv->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
+ if (drv->nl_cb == NULL) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink "
+ "callbacks");
+ goto err1;
+ }
+
+ drv->nl_handle = nl80211_handle_alloc(drv->nl_cb);
+ if (drv->nl_handle == NULL) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink "
+ "callbacks");
+ goto err2;
+ }
+
+ drv->nl_handle_event = nl80211_handle_alloc(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) {
+ wpa_printf(MSG_ERROR, "nl80211: 'nl80211' generic netlink not "
+ "found");
+ goto err4;
+ }
+
+ ret = nl_get_multicast_id(drv, "nl80211", "scan");
+ 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 scan events: %d (%s)",
+ ret, strerror(-ret));
+ goto err4;
+ }
+
+ 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;
+ }
+
+ ret = nl_get_multicast_id(drv, "nl80211", "regulatory");
+ if (ret >= 0)
+ ret = nl_socket_add_membership(drv->nl_handle_event, ret);
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Could not add multicast "
+ "membership for regulatory events: %d (%s)",
+ ret, strerror(-ret));
+ /* Continue without regulatory events */
+ }
+
+ eloop_register_read_sock(nl_socket_get_fd(drv->nl_handle_event),
+ wpa_driver_nl80211_event_receive, drv,
+ drv->nl_handle_event);
+
+ return 0;
+
+err4:
+ nl_cache_free(drv->nl_cache_event);
+err3b:
+ nl_cache_free(drv->nl_cache);
+err3:
+ nl80211_handle_destroy(drv->nl_handle_event);
+err2b:
+ nl80211_handle_destroy(drv->nl_handle);
+err2:
+ nl_cb_put(drv->nl_cb);
+err1:
+ return -1;
+}
+
+
+static void wpa_driver_nl80211_rfkill_blocked(void *ctx)
+{
+ wpa_printf(MSG_DEBUG, "nl80211: RFKILL blocked");
+ /*
+ * This may be for any interface; use ifdown event to disable
+ * interface.
+ */
+}
+
+
+static void wpa_driver_nl80211_rfkill_unblocked(void *ctx)
+{
+ struct wpa_driver_nl80211_data *drv = ctx;
+ wpa_printf(MSG_DEBUG, "nl80211: RFKILL unblocked");
+ if (linux_set_iface_flags(drv->ioctl_sock, drv->first_bss.ifname, 1)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Could not set interface UP "
+ "after rfkill unblock");
+ return;
+ }
+ /* rtnetlink ifup handler will report interface as enabled */
+}
+
+
+static void nl80211_get_phy_name(struct wpa_driver_nl80211_data *drv)
+{
+ /* Find phy (radio) to which this interface belongs */
+ char buf[90], *pos;
+ int f, rv;
+
+ drv->phyname[0] = '\0';
+ snprintf(buf, sizeof(buf) - 1, "/sys/class/net/%s/phy80211/name",
+ drv->first_bss.ifname);
+ f = open(buf, O_RDONLY);
+ if (f < 0) {
+ wpa_printf(MSG_DEBUG, "Could not open file %s: %s",
+ buf, strerror(errno));
+ return;
+ }
+
+ rv = read(f, drv->phyname, sizeof(drv->phyname) - 1);
+ close(f);
+ if (rv < 0) {
+ wpa_printf(MSG_DEBUG, "Could not read file %s: %s",
+ buf, strerror(errno));
+ return;
+ }
+
+ drv->phyname[rv] = '\0';
+ pos = os_strchr(drv->phyname, '\n');
+ if (pos)
+ *pos = '\0';
+ wpa_printf(MSG_DEBUG, "nl80211: interface %s in phy %s",
+ drv->first_bss.ifname, drv->phyname);
+}
+
+
+/**
+ * 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
+ * @global_priv: private driver global data from global_init()
+ * Returns: Pointer to private data, %NULL on failure
+ */
+static void * wpa_driver_nl80211_init(void *ctx, const char *ifname,
+ void *global_priv)
+{
+ struct wpa_driver_nl80211_data *drv;
+ struct netlink_config *cfg;
+ struct rfkill_config *rcfg;
+ struct i802_bss *bss;
+
+ drv = os_zalloc(sizeof(*drv));
+ if (drv == NULL)
+ return NULL;
+ drv->global = global_priv;
+ 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)) {
+ os_free(drv);
+ return NULL;
+ }
+
+ nl80211_get_phy_name(drv);
+
+ drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (drv->ioctl_sock < 0) {
+ perror("socket(PF_INET,SOCK_DGRAM)");
+ goto failed;
+ }
+
+ 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;
+ }
+
+ rcfg = os_zalloc(sizeof(*rcfg));
+ if (rcfg == NULL)
+ goto failed;
+ rcfg->ctx = drv;
+ os_strlcpy(rcfg->ifname, ifname, sizeof(rcfg->ifname));
+ rcfg->blocked_cb = wpa_driver_nl80211_rfkill_blocked;
+ rcfg->unblocked_cb = wpa_driver_nl80211_rfkill_unblocked;
+ drv->rfkill = rfkill_init(rcfg);
+ if (drv->rfkill == NULL) {
+ wpa_printf(MSG_DEBUG, "nl80211: RFKILL status not available");
+ os_free(rcfg);
+ }
+
+ if (wpa_driver_nl80211_finish_drv_init(drv))
+ goto failed;
+
+ if (drv->global)
+ dl_list_add(&drv->global->interfaces, &drv->list);
+
+ return bss;
+
+failed:
+ rfkill_deinit(drv->rfkill);
+ netlink_deinit(drv->netlink);
+ if (drv->ioctl_sock >= 0)
+ close(drv->ioctl_sock);
+
+ genl_family_put(drv->nl80211);
+ nl_cache_free(drv->nl_cache);
+ nl80211_handle_destroy(drv->nl_handle);
+ nl_cb_put(drv->nl_cb);
+ eloop_unregister_read_sock(nl_socket_get_fd(drv->nl_handle_event));
+
+ os_free(drv);
+ return NULL;
+}
+
+
+static int nl80211_register_frame(struct wpa_driver_nl80211_data *drv,
+ struct nl_handle *nl_handle,
+ u16 type, 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_U16(msg, NL80211_ATTR_FRAME_TYPE, type);
+ NLA_PUT(msg, NL80211_ATTR_FRAME_MATCH, match_len, match);
+
+ ret = send_and_recv(drv, nl_handle, msg, NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Register frame command "
+ "failed (type=%u): ret=%d (%s)",
+ type, ret, strerror(-ret));
+ wpa_hexdump(MSG_DEBUG, "nl80211: Register frame match",
+ match, match_len);
+ goto nla_put_failure;
+ }
+ ret = 0;
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+static int nl80211_register_action_frame(struct wpa_driver_nl80211_data *drv,
+ const u8 *match, size_t match_len)
+{
+ u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_ACTION << 4);
+ return nl80211_register_frame(drv, drv->nl_handle_event,
+ type, match, match_len);
+}
+
+
+static int nl80211_register_action_frames(struct wpa_driver_nl80211_data *drv)
+{
+#ifdef CONFIG_P2P
+ /* GAS Initial Request */
+ if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0a", 2) < 0)
+ return -1;
+ /* GAS Initial Response */
+ if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0b", 2) < 0)
+ return -1;
+ /* GAS Comeback Request */
+ if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0c", 2) < 0)
+ return -1;
+ /* GAS Comeback Response */
+ if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0d", 2) < 0)
+ return -1;
+ /* P2P Public Action */
+ if (nl80211_register_action_frame(drv,
+ (u8 *) "\x04\x09\x50\x6f\x9a\x09",
+ 6) < 0)
+ return -1;
+ /* P2P Action */
+ if (nl80211_register_action_frame(drv,
+ (u8 *) "\x7f\x50\x6f\x9a\x09",
+ 5) < 0)
+ return -1;
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_IEEE80211W
+ /* SA Query Response */
+ if (nl80211_register_action_frame(drv, (u8 *) "\x08\x01", 2) < 0)
+ return -1;
+#endif /* CONFIG_IEEE80211W */
+
+ /* 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 void wpa_driver_nl80211_send_rfkill(void *eloop_ctx, void *timeout_ctx)
+{
+ wpa_supplicant_event(timeout_ctx, EVENT_INTERFACE_DISABLED, NULL);
+}
+
+
+static int
+wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv)
+{
+ struct i802_bss *bss = &drv->first_bss;
+ int send_rfkill_event = 0;
+
+ drv->ifindex = if_nametoindex(bss->ifname);
+ drv->first_bss.ifindex = drv->ifindex;
+
+#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 (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1)) {
+ if (rfkill_is_blocked(drv->rfkill)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Could not yet enable "
+ "interface '%s' due to rfkill",
+ bss->ifname);
+ drv->if_disabled = 1;
+ send_rfkill_event = 1;
+ } else {
+ wpa_printf(MSG_ERROR, "nl80211: Could not set "
+ "interface '%s' UP", bss->ifname);
+ return -1;
+ }
+ }
+
+ netlink_send_oper_ifla(drv->netlink, drv->ifindex,
+ 1, IF_OPER_DORMANT);
+#endif /* HOSTAPD */
+
+ if (wpa_driver_nl80211_capa(drv))
+ return -1;
+
+ if (linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, drv->addr))
+ return -1;
+
+ 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.
+ */
+ }
+
+ if (send_rfkill_event) {
+ eloop_register_timeout(0, 0, wpa_driver_nl80211_send_rfkill,
+ drv, drv->ctx);
+ }
+
+ 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;
+}
+
+
+/**
+ * wpa_driver_nl80211_deinit - Deinitialize nl80211 driver interface
+ * @priv: Pointer to private nl80211 data from wpa_driver_nl80211_init()
+ *
+ * Shut down driver interface and processing of driver events. Free
+ * private data buffer if one was allocated in wpa_driver_nl80211_init().
+ */
+static void wpa_driver_nl80211_deinit(void *priv)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ if (drv->nl_handle_preq)
+ wpa_driver_nl80211_probe_req_report(bss, 0);
+ if (bss->added_if_into_bridge) {
+ if (linux_br_del_if(drv->ioctl_sock, bss->brname, bss->ifname)
+ < 0)
+ wpa_printf(MSG_INFO, "nl80211: Failed to remove "
+ "interface %s from bridge %s: %s",
+ bss->ifname, bss->brname, strerror(errno));
+ }
+ if (bss->added_bridge) {
+ if (linux_br_del(drv->ioctl_sock, bss->brname) < 0)
+ wpa_printf(MSG_INFO, "nl80211: Failed to remove "
+ "bridge %s: %s",
+ bss->brname, strerror(errno));
+ }
+
+ nl80211_remove_monitor_interface(drv);
+
+ 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);
+ }
+
+ 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);
+
+ netlink_send_oper_ifla(drv->netlink, drv->ifindex, 0, IF_OPER_UP);
+ netlink_deinit(drv->netlink);
+ rfkill_deinit(drv->rfkill);
+
+ eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
+
+ (void) linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0);
+ wpa_driver_nl80211_set_mode(bss, IEEE80211_MODE_INFRA);
+
+ if (drv->ioctl_sock >= 0)
+ close(drv->ioctl_sock);
+
+ 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);
+ nl80211_handle_destroy(drv->nl_handle);
+ nl80211_handle_destroy(drv->nl_handle_event);
+ nl_cb_put(drv->nl_cb);
+
+ os_free(drv->filter_ssids);
+
+ if (drv->global)
+ dl_list_del(&drv->list);
+
+ os_free(drv);
+}
+
+
+/**
+ * wpa_driver_nl80211_scan_timeout - Scan timeout to report scan completion
+ * @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
+ * generate a scan completed event if the driver does not report this.
+ */
+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);
+}
+
+
+/**
+ * wpa_driver_nl80211_scan - Request the driver to initiate scan
+ * @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,
+ struct wpa_driver_scan_params *params)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int ret = 0, timeout;
+ struct nl_msg *msg, *ssids, *freqs;
+ size_t i;
+
+ msg = nlmsg_alloc();
+ ssids = nlmsg_alloc();
+ 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);
+
+ 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);
+ }
+
+ 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
+ * read results after a timeout. */
+ timeout = 10;
+ if (drv->scan_complete_events) {
+ /*
+ * 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;
+ }
+ wpa_printf(MSG_DEBUG, "Scan requested (ret=%d) - scan timeout %d "
+ "seconds", ret, timeout);
+ eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
+ eloop_register_timeout(timeout, 0, wpa_driver_nl80211_scan_timeout,
+ drv, drv->ctx);
+
+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];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct nlattr *bss[NL80211_BSS_MAX + 1];
+ static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
+ [NL80211_BSS_BSSID] = { .type = NLA_UNSPEC },
+ [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
+ [NL80211_BSS_TSF] = { .type = NLA_U64 },
+ [NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 },
+ [NL80211_BSS_CAPABILITY] = { .type = NLA_U16 },
+ [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 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, *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);
+ if (!tb[NL80211_ATTR_BSS])
+ return NL_SKIP;
+ if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS],
+ bss_policy))
+ return NL_SKIP;
+ if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) {
+ ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
+ ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
+ } else {
+ 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;
+ }
+
+ 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])
+ os_memcpy(r->bssid, nla_data(bss[NL80211_BSS_BSSID]),
+ ETH_ALEN);
+ if (bss[NL80211_BSS_FREQUENCY])
+ r->freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
+ if (bss[NL80211_BSS_BEACON_INTERVAL])
+ 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]);
+ 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;
+ 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 *));
+ if (tmp == NULL) {
+ os_free(r);
+ return NL_SKIP;
+ }
+ tmp[res->num++] = r;
+ res->res = tmp;
+
+ return NL_SKIP;
+}
+
+
+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 *
+nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv)
+{
+ 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 NULL;
+ msg = nlmsg_alloc();
+ if (!msg)
+ goto nla_put_failure;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, NLM_F_DUMP,
+ NL80211_CMD_GET_SCAN, 0);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+ 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)",
+ (unsigned long) res->num);
+ return res;
+ }
+ wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d "
+ "(%s)", ret, strerror(-ret));
+nla_put_failure:
+ nlmsg_free(msg);
+ wpa_scan_results_free(res);
+ return NULL;
+}
+
+
+/**
+ * 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 i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct wpa_scan_results *res;
+
+ res = nl80211_get_scan_results(drv);
+ if (res)
+ wpa_driver_nl80211_check_bss_status(drv, res);
+ return res;
+}
+
+
+static void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv)
+{
+ struct wpa_scan_results *res;
+ size_t i;
+
+ res = nl80211_get_scan_results(drv);
+ if (res == NULL) {
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to get scan results");
+ return;
+ }
+
+ 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]" : "");
+ }
+
+ wpa_scan_results_free(res);
+}
+
+
+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 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: 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)
+ 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);
+ } else {
+ 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,
+ WLAN_CIPHER_SUITE_WEP40);
+ else
+ NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
+ WLAN_CIPHER_SUITE_WEP104);
+ break;
+ case WPA_ALG_TKIP:
+ 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,
+ WLAN_CIPHER_SUITE_CCMP);
+ break;
+ case WPA_ALG_IGTK:
+ NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
+ WLAN_CIPHER_SUITE_AES_CMAC);
+ break;
+ 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 && !is_broadcast_ether_addr(addr)) {
+ wpa_printf(MSG_DEBUG, " addr=" MACSTR, MAC2STR(addr));
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
+
+ if (alg != WPA_ALG_WEP && key_idx && !set_tx) {
+ wpa_printf(MSG_DEBUG, " RSN IBSS RX GTK");
+ NLA_PUT_U32(msg, NL80211_ATTR_KEY_TYPE,
+ NL80211_KEYTYPE_GROUP);
+ }
+ } else if (addr && is_broadcast_ether_addr(addr)) {
+ struct nl_msg *types;
+ int err;
+ wpa_printf(MSG_DEBUG, " broadcast key");
+ types = nlmsg_alloc();
+ if (!types)
+ goto nla_put_failure;
+ NLA_PUT_FLAG(types, NL80211_KEY_DEFAULT_TYPE_MULTICAST);
+ err = nla_put_nested(msg, NL80211_ATTR_KEY_DEFAULT_TYPES,
+ types);
+ nlmsg_free(types);
+ if (err)
+ goto nla_put_failure;
+ }
+ NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
+
+ 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 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;
+ if (drv->nlmode == NL80211_IFTYPE_AP && addr &&
+ !is_broadcast_ether_addr(addr))
+ return ret;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ 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);
+ if (addr && is_broadcast_ether_addr(addr)) {
+ struct nl_msg *types;
+ int err;
+ types = nlmsg_alloc();
+ if (!types)
+ goto nla_put_failure;
+ NLA_PUT_FLAG(types, NL80211_KEY_DEFAULT_TYPE_MULTICAST);
+ err = nla_put_nested(msg, NL80211_ATTR_KEY_DEFAULT_TYPES,
+ types);
+ nlmsg_free(types);
+ if (err)
+ goto nla_put_failure;
+ } else if (addr) {
+ struct nl_msg *types;
+ int err;
+ types = nlmsg_alloc();
+ if (!types)
+ goto nla_put_failure;
+ NLA_PUT_FLAG(types, NL80211_KEY_DEFAULT_TYPE_UNICAST);
+ err = nla_put_nested(msg, NL80211_ATTR_KEY_DEFAULT_TYPES,
+ types);
+ nlmsg_free(types);
+ if (err)
+ goto nla_put_failure;
+ }
+
+ 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 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 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);
+
+ NLA_PUT_U8(msg, NL80211_KEY_IDX, key_idx);
+
+ 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;
+ }
+
+ if (seq && seq_len)
+ NLA_PUT(msg, NL80211_KEY_SEQ, seq_len, seq);
+
+ NLA_PUT(msg, NL80211_KEY_DATA, key_len, key);
+
+ nla_nest_end(msg, key_attr);
+
+ return 0;
+ nla_put_failure:
+ return -1;
+}
+
+
+static int nl80211_set_conn_keys(struct wpa_driver_associate_params *params,
+ struct nl_msg *msg)
+{
+ int i, privacy = 0;
+ struct nlattr *nl_keys, *nl_key;
+
+ for (i = 0; i < 4; i++) {
+ if (!params->wep_key[i])
+ continue;
+ privacy = 1;
+ break;
+ }
+ if (params->wps == WPS_MODE_PRIVACY)
+ privacy = 1;
+ if (params->pairwise_suite &&
+ params->pairwise_suite != WPA_CIPHER_NONE)
+ privacy = 1;
+
+ if (!privacy)
+ return 0;
+
+ NLA_PUT_FLAG(msg, NL80211_ATTR_PRIVACY);
+
+ nl_keys = nla_nest_start(msg, NL80211_ATTR_KEYS);
+ if (!nl_keys)
+ goto nla_put_failure;
+
+ for (i = 0; i < 4; i++) {
+ if (!params->wep_key[i])
+ continue;
+
+ nl_key = nla_nest_start(msg, i);
+ if (!nl_key)
+ goto nla_put_failure;
+
+ 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);
+
+ NLA_PUT_U8(msg, NL80211_KEY_IDX, i);
+
+ if (i == params->wep_tx_keyidx)
+ NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT);
+
+ nla_nest_end(msg, nl_key);
+ }
+ nla_nest_end(msg, nl_keys);
+
+ return 0;
+
+nla_put_failure:
+ return -ENOBUFS;
+}
+
+
+static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
+ const u8 *addr, int cmd, u16 reason_code,
+ int local_state_change)
+{
+ 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, cmd, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ 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);
+ 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_disconnect(struct wpa_driver_nl80211_data *drv,
+ const u8 *addr, int reason_code)
+{
+ wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " reason_code=%d)",
+ __func__, MAC2STR(addr), reason_code);
+ drv->associated = 0;
+ return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DISCONNECT,
+ reason_code, 0);
+}
+
+
+static int wpa_driver_nl80211_deauthenticate(void *priv, const u8 *addr,
+ int reason_code)
+{
+ 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(addr=" MACSTR " reason_code=%d)",
+ __func__, MAC2STR(addr), reason_code);
+ drv->associated = 0;
+ if (drv->nlmode == NL80211_IFTYPE_ADHOC)
+ return nl80211_leave_ibss(drv);
+ return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DEAUTHENTICATE,
+ reason_code, 0);
+}
+
+
+static int wpa_driver_nl80211_disassociate(void *priv, const u8 *addr,
+ int reason_code)
+{
+ 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_authenticate(
+ void *priv, struct wpa_driver_auth_params *params)
+{
+ 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) < 0)
+ return -1;
+
+retry:
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Authenticate (ifindex=%d)",
+ drv->ifindex);
+
+ 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;
+ }
+ }
+
+ 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);
+ }
+ 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);
+ }
+
+ 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;
+ }
+ ret = 0;
+ wpa_printf(MSG_DEBUG, "nl80211: Authentication request send "
+ "successfully");
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+struct phy_info_arg {
+ u16 *num_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];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct phy_info_arg *phy_info = 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] = {
+ [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 },
+ };
+
+ struct nlattr *nl_band;
+ struct nlattr *nl_freq;
+ struct nlattr *nl_rate;
+ int rem_band, rem_freq, rem_rate;
+ struct hostapd_hw_modes *mode;
+ int idx, mode_is_set;
+
+ nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ 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));
+ if (!mode)
+ return NL_SKIP;
+ phy_info->modes = mode;
+
+ mode_is_set = 0;
+
+ mode = &phy_info->modes[*(phy_info->num_modes)];
+ 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);
+
+ 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 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);
+ 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 = 0;
+
+ if (!mode_is_set) {
+ /* crude heuristic */
+ if (mode->channels[idx].freq < 4000)
+ mode->mode = HOSTAPD_MODE_IEEE80211B;
+ else
+ mode->mode = HOSTAPD_MODE_IEEE80211A;
+ mode_is_set = 1;
+ }
+
+ /* crude heuristic */
+ 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;
+
+ if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
+ mode->channels[idx].flag |=
+ HOSTAPD_CHAN_DISABLED;
+ if (tb_freq[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN])
+ mode->channels[idx].flag |=
+ HOSTAPD_CHAN_PASSIVE_SCAN;
+ if (tb_freq[NL80211_FREQUENCY_ATTR_NO_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);
+ if (!tb_rate[NL80211_BITRATE_ATTR_RATE])
+ continue;
+ mode->num_rates++;
+ }
+
+ 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);
+ if (!tb_rate[NL80211_BITRATE_ATTR_RATE])
+ continue;
+ mode->rates[idx] = nla_get_u32(tb_rate[NL80211_BITRATE_ATTR_RATE]);
+
+ /* crude heuristic */
+ if (mode->mode == HOSTAPD_MODE_IEEE80211B &&
+ mode->rates[idx] > 200)
+ mode->mode = HOSTAPD_MODE_IEEE80211G;
+
+ idx++;
+ }
+ }
+
+ 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");
+
+ return modes;
+}
+
+
+static void nl80211_set_ht40_mode(struct hostapd_hw_modes *mode, int start,
+ int end)
+{
+ int c;
+
+ for (c = 0; c < mode->num_channels; c++) {
+ struct hostapd_channel_data *chan = &mode->channels[c];
+ if (chan->freq - 10 >= start && chan->freq + 10 <= end)
+ chan->flag |= HOSTAPD_CHAN_HT40;
+ }
+}
+
+
+static void nl80211_set_ht40_mode_sec(struct hostapd_hw_modes *mode, int start,
+ int end)
+{
+ int c;
+
+ for (c = 0; c < mode->num_channels; c++) {
+ struct hostapd_channel_data *chan = &mode->channels[c];
+ if (!(chan->flag & HOSTAPD_CHAN_HT40))
+ continue;
+ if (chan->freq - 30 >= start && chan->freq - 10 <= end)
+ chan->flag |= HOSTAPD_CHAN_HT40MINUS;
+ if (chan->freq + 10 >= start && chan->freq + 30 <= end)
+ chan->flag |= HOSTAPD_CHAN_HT40PLUS;
+ }
+}
+
+
+static void nl80211_reg_rule_ht40(struct nlattr *tb[],
+ struct phy_info_arg *results)
+{
+ u32 start, end, max_bw;
+ u16 m;
+
+ if (tb[NL80211_ATTR_FREQ_RANGE_START] == NULL ||
+ tb[NL80211_ATTR_FREQ_RANGE_END] == NULL ||
+ tb[NL80211_ATTR_FREQ_RANGE_MAX_BW] == NULL)
+ return;
+
+ start = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]) / 1000;
+ end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000;
+ max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000;
+
+ wpa_printf(MSG_DEBUG, "nl80211: %u-%u @ %u MHz",
+ start, end, max_bw);
+ if (max_bw < 40)
+ return;
+
+ for (m = 0; m < *results->num_modes; m++) {
+ if (!(results->modes[m].ht_capab &
+ HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
+ continue;
+ nl80211_set_ht40_mode(&results->modes[m], start, end);
+ }
+}
+
+
+static void nl80211_reg_rule_sec(struct nlattr *tb[],
+ struct phy_info_arg *results)
+{
+ u32 start, end, max_bw;
+ u16 m;
+
+ if (tb[NL80211_ATTR_FREQ_RANGE_START] == NULL ||
+ tb[NL80211_ATTR_FREQ_RANGE_END] == NULL ||
+ tb[NL80211_ATTR_FREQ_RANGE_MAX_BW] == NULL)
+ return;
+
+ start = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]) / 1000;
+ end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000;
+ max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000;
+
+ if (max_bw < 20)
+ return;
+
+ for (m = 0; m < *results->num_modes; m++) {
+ if (!(results->modes[m].ht_capab &
+ HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
+ continue;
+ nl80211_set_ht40_mode_sec(&results->modes[m], start, end);
+ }
+}
+
+
+static int nl80211_get_reg(struct nl_msg *msg, void *arg)
+{
+ struct phy_info_arg *results = arg;
+ struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct nlattr *nl_rule;
+ struct nlattr *tb_rule[NL80211_FREQUENCY_ATTR_MAX + 1];
+ int rem_rule;
+ static struct nla_policy reg_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
+ [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 },
+ [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 },
+ [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 },
+ [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 },
+ [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 },
+ [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 },
+ };
+
+ nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+ if (!tb_msg[NL80211_ATTR_REG_ALPHA2] ||
+ !tb_msg[NL80211_ATTR_REG_RULES]) {
+ wpa_printf(MSG_DEBUG, "nl80211: No regulatory information "
+ "available");
+ return NL_SKIP;
+ }
+
+ wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s",
+ (char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]));
+
+ nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule)
+ {
+ nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX,
+ nla_data(nl_rule), nla_len(nl_rule), reg_policy);
+ nl80211_reg_rule_ht40(tb_rule, results);
+ }
+
+ nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule)
+ {
+ nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX,
+ nla_data(nl_rule), nla_len(nl_rule), reg_policy);
+ nl80211_reg_rule_sec(tb_rule, results);
+ }
+
+ return NL_SKIP;
+}
+
+
+static int nl80211_set_ht40_flags(struct wpa_driver_nl80211_data *drv,
+ struct phy_info_arg *results)
+{
+ 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_REG, 0);
+ return send_and_recv_msgs(drv, msg, nl80211_get_reg, results);
+}
+
+
+static struct hostapd_hw_modes *
+wpa_driver_nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags)
+{
+ 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,
+ .modes = NULL,
+ };
+
+ *num_modes = 0;
+ *flags = 0;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return NULL;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+ 0, NL80211_CMD_GET_WIPHY, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+ if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) {
+ nl80211_set_ht40_flags(drv, &result);
+ return wpa_driver_nl80211_add_11b(result.modes, num_modes);
+ }
+ nla_put_failure:
+ return NULL;
+}
+
+
+static int wpa_driver_nl80211_send_frame(struct wpa_driver_nl80211_data *drv,
+ const void *data, size_t len,
+ int encrypt)
+{
+ __u8 rtap_hdr[] = {
+ 0x00, 0x00, /* radiotap version */
+ 0x0e, 0x00, /* radiotap length */
+ 0x02, 0xc0, 0x00, 0x00, /* bmap: flags, tx and rx flags */
+ 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 */
+ };
+ struct iovec iov[2] = {
+ {
+ .iov_base = &rtap_hdr,
+ .iov_len = sizeof(rtap_hdr),
+ },
+ {
+ .iov_base = (void *) data,
+ .iov_len = len,
+ }
+ };
+ struct msghdr msg = {
+ .msg_name = NULL,
+ .msg_namelen = 0,
+ .msg_iov = iov,
+ .msg_iovlen = 2,
+ .msg_control = NULL,
+ .msg_controllen = 0,
+ .msg_flags = 0,
+ };
+ int res;
+
+ if (encrypt)
+ rtap_hdr[8] |= IEEE80211_RADIOTAP_F_WEP;
+
+ res = sendmsg(drv->monitor_sock, &msg, 0);
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "nl80211: sendmsg: %s", strerror(errno));
+ return -1;
+ }
+ return 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 (drv->nlmode == NL80211_IFTYPE_STATION &&
+ WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+ WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_RESP) {
+ /*
+ * The use of last_mgmt_freq is a bit of a hack,
+ * but it works due to the single-threaded nature
+ * of wpa_supplicant.
+ */
+ return nl80211_send_frame_cmd(drv, drv->last_mgmt_freq, 0,
+ data, data_len, NULL);
+ }
+
+ 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;
+ }
+ }
+
+ 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_sta_add(void *priv,
+ struct hostapd_sta_add_params *params)
+{
+ 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_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 (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.
+ *
+ * For now allow all To DS data frames through.
+ */
+ /* load the IEEE 802.11 frame control field */
+ BPF_STMT(BPF_LD | BPF_H | BPF_IND, 0),
+ /* mask off frame type, version and DS status */
+ BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x0F03),
+ /* accept frame if version 0, type 2 and To DS, fall through otherwise
+ */
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0801, PASS, 0),
+
+#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, u32 flags)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct ieee80211_hdr *hdr;
+ size_t len;
+ u8 *pos;
+ int res;
+ int qos = flags & WPA_STA_WMM;
+
+ 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 (qos) {
+ hdr->frame_control |=
+ host_to_le16(WLAN_FC_STYPE_QOS_DATA << 4);
+ }
+
+ 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 (qos) {
+ /* add an empty QoS header if needed */
+ pos[0] = 0;
+ pos[1] = 0;
+ pos += 2;
+ }
+
+ 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);
+
+ /*
+ * 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 (params->p2p)
+ wpa_printf(MSG_DEBUG, "nl80211: Setup AP operations for P2P "
+ "group (GO)");
+ 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);
+ 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_ibss(struct wpa_driver_nl80211_data *drv,
+ struct wpa_driver_associate_params *params)
+{
+ 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;
+ int algs;
+
+ 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);
+
+ algs = 0;
+ if (params->auth_alg & WPA_AUTH_ALG_OPEN)
+ algs++;
+ if (params->auth_alg & WPA_AUTH_ALG_SHARED)
+ algs++;
+ if (params->auth_alg & WPA_AUTH_ALG_LEAP)
+ algs++;
+ if (algs > 1) {
+ wpa_printf(MSG_DEBUG, " * Leave out Auth Type for automatic "
+ "selection");
+ goto skip_auth_type;
+ }
+
+ 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);
+
+skip_auth_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);
+
+ 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;
+ }
+ wpa_printf(MSG_DEBUG, " * pairwise=0x%x", cipher);
+ 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;
+ }
+ wpa_printf(MSG_DEBUG, " * group=0x%x", cipher);
+ NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, cipher);
+ }
+
+#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);
+ }
+
+ if (params->p2p)
+ wpa_printf(MSG_DEBUG, " * P2P group");
+
+ 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_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;
+ int i;
+
+ 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.
+ */
+ wpa_printf(MSG_DEBUG, "nl80211: Try mode change after setting "
+ "interface down");
+ for (i = 0; i < 10; i++) {
+ 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)
+ break;
+ } else
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to set "
+ "interface down");
+ os_sleep(0, 100000);
+ }
+
+ 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;
+
+ switch (queue) {
+ case 0:
+ NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_VO);
+ break;
+ case 1:
+ NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_VI);
+ break;
+ case 2:
+ NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_BE);
+ break;
+ case 3:
+ NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_BK);
+ break;
+ }
+ /* 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,
+ int ht_opmode)
+{
+ 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);
+ if (ht_opmode >= 0)
+ NLA_PUT_U16(msg, NL80211_ATTR_BSS_HT_OPMODE, ht_opmode);
+ 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, -1);
+}
+
+
+static int i802_set_preamble(void *priv, int value)
+{
+ return i802_set_bss(priv, -1, value, -1, -1);
+}
+
+
+static int i802_set_short_slot_time(void *priv, int value)
+{
+ return i802_set_bss(priv, -1, -1, value, -1);
+}
+
+
+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,
+ const char *bridge_ifname)
+{
+ 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 (!if_nametoindex(name)) {
+ if (nl80211_create_iface(drv, name,
+ NL80211_IFTYPE_AP_VLAN,
+ NULL, 1) < 0)
+ return -1;
+ if (bridge_ifname &&
+ linux_br_add_if(drv->ioctl_sock, bridge_ifname,
+ name) < 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 int i802_set_ht_params(void *priv, const u8 *ht_capab,
+ size_t ht_capab_len, const u8 *ht_oper,
+ size_t ht_oper_len)
+{
+ if (ht_oper_len >= 6) {
+ /* ht opmode uses 16bit in octet 5 & 6 */
+ u16 ht_opmode = le_to_host16(((u16 *) ht_oper)[2]);
+ return i802_set_bss(priv, -1, -1, -1, ht_opmode);
+ } else
+ return -1;
+}
+
+
+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;
+}
+
+#endif /* HOSTAPD */
+
+#if defined(HOSTAPD) || defined(CONFIG_AP)
+
+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));
+}
+
+#endif /* HOSTAPD || CONFIG_AP */
+
+#ifdef HOSTAPD
+
+static int i802_check_bridge(struct wpa_driver_nl80211_data *drv,
+ struct i802_bss *bss,
+ const char *brname, const char *ifname)
+{
+ int ifindex;
+ char in_br[IFNAMSIZ];
+
+ os_strlcpy(bss->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;
+ }
+ bss->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;
+ }
+ bss->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, NULL);
+ if (bss == NULL)
+ return NULL;
+
+ drv = bss->drv;
+ drv->nlmode = NL80211_IFTYPE_AP;
+ 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, bss, 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);
+ rfkill_deinit(drv->rfkill);
+ netlink_deinit(drv->netlink);
+ if (drv->ioctl_sock >= 0)
+ close(drv->ioctl_sock);
+
+ genl_family_put(drv->nl80211);
+ nl_cache_free(drv->nl_cache);
+ nl80211_handle_destroy(drv->nl_handle);
+ nl_cb_put(drv->nl_cb);
+ eloop_unregister_read_sock(nl_socket_get_fd(drv->nl_handle_event));
+
+ 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_P2P_CLIENT:
+ case WPA_IF_P2P_GROUP:
+ return NL80211_IFTYPE_P2P_CLIENT;
+ case WPA_IF_AP_VLAN:
+ return NL80211_IFTYPE_AP_VLAN;
+ case WPA_IF_AP_BSS:
+ return NL80211_IFTYPE_AP;
+ case WPA_IF_P2P_GO:
+ return NL80211_IFTYPE_P2P_GO;
+ }
+ return -1;
+}
+
+
+#ifdef CONFIG_P2P
+
+static int nl80211_addr_in_use(struct nl80211_global *global, const u8 *addr)
+{
+ struct wpa_driver_nl80211_data *drv;
+ dl_list_for_each(drv, &global->interfaces,
+ struct wpa_driver_nl80211_data, list) {
+ if (os_memcmp(addr, drv->addr, ETH_ALEN) == 0)
+ return 1;
+ }
+ return 0;
+}
+
+
+static int nl80211_p2p_interface_addr(struct wpa_driver_nl80211_data *drv,
+ u8 *new_addr)
+{
+ unsigned int idx;
+
+ if (!drv->global)
+ return -1;
+
+ os_memcpy(new_addr, drv->addr, ETH_ALEN);
+ for (idx = 0; idx < 64; idx++) {
+ new_addr[0] = drv->addr[0] | 0x02;
+ new_addr[0] ^= idx << 2;
+ if (!nl80211_addr_in_use(drv->global, new_addr))
+ break;
+ }
+ if (idx == 64)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Assigned new P2P Interface Address "
+ MACSTR, MAC2STR(new_addr));
+
+ return 0;
+}
+
+#endif /* CONFIG_P2P */
+
+
+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,
+ const char *bridge)
+{
+ 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) {
+ nl80211_remove_iface(drv, ifidx);
+ return -1;
+ }
+
+#ifdef CONFIG_P2P
+ if (!addr &&
+ (type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP ||
+ type == WPA_IF_P2P_GO)) {
+ /* Enforce unique P2P Interface Address */
+ u8 new_addr[ETH_ALEN], own_addr[ETH_ALEN];
+
+ if (linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, own_addr)
+ < 0 ||
+ linux_get_ifhwaddr(drv->ioctl_sock, ifname, new_addr) < 0)
+ {
+ nl80211_remove_iface(drv, ifidx);
+ return -1;
+ }
+ if (os_memcmp(own_addr, new_addr, ETH_ALEN) == 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Allocate new address "
+ "for P2P group interface");
+ if (nl80211_p2p_interface_addr(drv, new_addr) < 0) {
+ nl80211_remove_iface(drv, ifidx);
+ return -1;
+ }
+ if (linux_set_ifhwaddr(drv->ioctl_sock, ifname,
+ new_addr) < 0) {
+ nl80211_remove_iface(drv, ifidx);
+ return -1;
+ }
+ os_memcpy(if_addr, new_addr, ETH_ALEN);
+ }
+ }
+#endif /* CONFIG_P2P */
+
+#ifdef HOSTAPD
+ if (bridge &&
+ i802_check_bridge(drv, new_bss, bridge, ifname) < 0) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to add the new "
+ "interface %s to a bridge %s", ifname, bridge);
+ nl80211_remove_iface(drv, ifidx);
+ os_free(new_bss);
+ return -1;
+ }
+
+ 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;
+
+#ifdef HOSTAPD
+ if (bss->added_if_into_bridge) {
+ if (linux_br_del_if(drv->ioctl_sock, bss->brname, bss->ifname)
+ < 0)
+ wpa_printf(MSG_INFO, "nl80211: Failed to remove "
+ "interface %s from bridge %s: %s",
+ bss->ifname, bss->brname, strerror(errno));
+ }
+ if (bss->added_bridge) {
+ if (linux_br_del(drv->ioctl_sock, bss->brname) < 0)
+ wpa_printf(MSG_INFO, "nl80211: Failed to remove "
+ "bridge %s: %s",
+ bss->brname, strerror(errno));
+ }
+#endif /* HOSTAPD */
+
+ nl80211_remove_iface(drv, ifindex);
+
+#ifdef HOSTAPD
+ if (type != WPA_IF_AP_BSS)
+ return 0;
+
+ if (bss != &drv->first_bss) {
+ struct i802_bss *tbss;
+
+ for (tbss = &drv->first_bss; tbss; tbss = tbss->next) {
+ if (tbss->next == bss) {
+ tbss->next = bss->next;
+ os_free(bss);
+ bss = NULL;
+ break;
+ }
+ }
+ if (bss)
+ wpa_printf(MSG_INFO, "nl80211: %s - could not find "
+ "BSS %p in the list", __func__, bss);
+ }
+#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 nl80211_send_frame_cmd(struct wpa_driver_nl80211_data *drv,
+ unsigned int freq, unsigned int wait,
+ const u8 *buf, size_t buf_len,
+ u64 *cookie_out)
+{
+ struct nl_msg *msg;
+ u64 cookie;
+ 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_FRAME, 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, wait);
+ NLA_PUT_FLAG(msg, NL80211_ATTR_OFFCHANNEL_TX_OK);
+ NLA_PUT(msg, NL80211_ATTR_FRAME, buf_len, buf);
+
+ cookie = 0;
+ ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Frame command failed: ret=%d "
+ "(%s)", ret, strerror(-ret));
+ goto nla_put_failure;
+ }
+ wpa_printf(MSG_DEBUG, "nl80211: Frame TX command accepted; "
+ "cookie 0x%llx", (long long unsigned int) cookie);
+
+ if (cookie_out)
+ *cookie_out = cookie;
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+static int wpa_driver_nl80211_send_action(void *priv, unsigned int freq,
+ unsigned int wait_time,
+ 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;
+ u8 *buf;
+ struct ieee80211_hdr *hdr;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Send Action frame (ifindex=%d, "
+ "wait=%d ms)", drv->ifindex, wait_time);
+
+ 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);
+ else
+ ret = nl80211_send_frame_cmd(drv, freq, wait_time, buf,
+ 24 + data_len,
+ &drv->send_action_cookie);
+
+ os_free(buf);
+ return ret;
+}
+
+
+static void wpa_driver_nl80211_send_action_cancel_wait(void *priv)
+{
+ 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;
+
+ genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+ NL80211_CMD_FRAME_WAIT_CANCEL, 0);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, drv->send_action_cookie);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
+ if (ret)
+ wpa_printf(MSG_DEBUG, "nl80211: wait cancel failed: ret=%d "
+ "(%s)", ret, strerror(-ret));
+
+ nla_put_failure:
+ nlmsg_free(msg);
+}
+
+
+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 duration=%u): %d (%s)",
+ freq, duration, 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 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;
+ }
+
+ if (!report) {
+ if (drv->nl_handle_preq) {
+ eloop_unregister_read_sock(
+ nl_socket_get_fd(drv->nl_handle_preq));
+ nl_cache_free(drv->nl_cache_preq);
+ nl80211_handle_destroy(drv->nl_handle_preq);
+ drv->nl_handle_preq = NULL;
+ }
+ return 0;
+ }
+
+ if (drv->nl_handle_preq) {
+ wpa_printf(MSG_DEBUG, "nl80211: Probe Request reporting "
+ "already on!");
+ return 0;
+ }
+
+ drv->nl_handle_preq = nl80211_handle_alloc(drv->nl_cb);
+ if (drv->nl_handle_preq == NULL) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to allocate "
+ "netlink callbacks (preq)");
+ goto out_err1;
+ }
+
+ if (genl_connect(drv->nl_handle_preq)) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to connect to "
+ "generic netlink (preq)");
+ goto out_err2;
+ return -1;
+ }
+
+#ifdef CONFIG_LIBNL20
+ if (genl_ctrl_alloc_cache(drv->nl_handle_preq,
+ &drv->nl_cache_preq) < 0) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic "
+ "netlink cache (preq)");
+ goto out_err2;
+ }
+#else /* CONFIG_LIBNL20 */
+ drv->nl_cache_preq = genl_ctrl_alloc_cache(drv->nl_handle_preq);
+ if (drv->nl_cache_preq == NULL) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic "
+ "netlink cache (preq)");
+ goto out_err2;
+ }
+#endif /* CONFIG_LIBNL20 */
+
+ if (nl80211_register_frame(drv, drv->nl_handle_preq,
+ (WLAN_FC_TYPE_MGMT << 2) |
+ (WLAN_FC_STYPE_PROBE_REQ << 4),
+ NULL, 0) < 0) {
+ goto out_err3;
+ }
+
+ eloop_register_read_sock(nl_socket_get_fd(drv->nl_handle_preq),
+ wpa_driver_nl80211_event_receive, drv,
+ drv->nl_handle_preq);
+
+ return 0;
+
+ out_err3:
+ nl_cache_free(drv->nl_cache_preq);
+ out_err2:
+ nl80211_handle_destroy(drv->nl_handle_preq);
+ drv->nl_handle_preq = NULL;
+ out_err1:
+ return -1;
+}
+
+
+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:
+ 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");
+ }
+}
+
+
+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, 0,
+ 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_signal_poll(void *priv, struct wpa_signal_info *si)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int res;
+
+ os_memset(si, 0, sizeof(*si));
+ res = nl80211_get_link_signal(drv, si);
+ if (res != 0)
+ return res;
+
+ return nl80211_get_link_noise(drv, si);
+}
+
+
+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);
+}
+
+
+static int nl80211_set_intra_bss(void *priv, int enabled)
+{
+ 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);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
+ NLA_PUT_U8(msg, NL80211_ATTR_AP_ISOLATE, !enabled);
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL);
+ nla_put_failure:
+ return -ENOBUFS;
+}
+
+
+static int nl80211_set_param(void *priv, const char *param)
+{
+ wpa_printf(MSG_DEBUG, "nl80211: driver param='%s'", param);
+ if (param == NULL)
+ return 0;
+
+#ifdef CONFIG_P2P
+ if (os_strstr(param, "use_p2p_group_interface=1")) {
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group "
+ "interface");
+ drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT;
+ drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P;
+ }
+#endif /* CONFIG_P2P */
+
+ return 0;
+}
+
+
+static void * nl80211_global_init(void)
+{
+ struct nl80211_global *global;
+ global = os_zalloc(sizeof(*global));
+ if (global == NULL)
+ return NULL;
+ dl_list_init(&global->interfaces);
+ return global;
+}
+
+
+static void nl80211_global_deinit(void *priv)
+{
+ struct nl80211_global *global = priv;
+ if (global == NULL)
+ return;
+ if (!dl_list_empty(&global->interfaces)) {
+ wpa_printf(MSG_ERROR, "nl80211: %u interface(s) remain at "
+ "nl80211_global_deinit",
+ dl_list_len(&global->interfaces));
+ }
+ os_free(global);
+}
+
+
+static const char * nl80211_get_radio_name(void *priv)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ return drv->phyname;
+}
+
+
+const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ .name = "nl80211",
+ .desc = "Linux nl80211/cfg80211",
+ .get_bssid = wpa_driver_nl80211_get_bssid,
+ .get_ssid = wpa_driver_nl80211_get_ssid,
+ .set_key = wpa_driver_nl80211_set_key,
+ .scan2 = wpa_driver_nl80211_scan,
+ .get_scan_results2 = wpa_driver_nl80211_get_scan_results,
+ .deauthenticate = wpa_driver_nl80211_deauthenticate,
+ .disassociate = wpa_driver_nl80211_disassociate,
+ .authenticate = wpa_driver_nl80211_authenticate,
+ .associate = wpa_driver_nl80211_associate,
+ .global_init = nl80211_global_init,
+ .global_deinit = nl80211_global_deinit,
+ .init2 = wpa_driver_nl80211_init,
+ .deinit = wpa_driver_nl80211_deinit,
+ .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_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,
+ .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,
+ .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,
+ .set_ht_params = i802_set_ht_params,
+#endif /* HOSTAPD */
+#if defined(HOSTAPD) || defined(CONFIG_AP)
+ .sta_deauth = i802_sta_deauth,
+ .sta_disassoc = i802_sta_disassoc,
+#endif /* HOSTAPD || CONFIG_AP */
+ .set_freq = i802_set_freq,
+ .send_action = wpa_driver_nl80211_send_action,
+ .send_action_cancel_wait = wpa_driver_nl80211_send_action_cancel_wait,
+ .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,
+ .signal_poll = nl80211_signal_poll,
+ .send_frame = nl80211_send_frame,
+ .set_intra_bss = nl80211_set_intra_bss,
+ .set_param = nl80211_set_param,
+ .get_radio_name = nl80211_get_radio_name,
+};
diff --git a/src/drivers/driver_none.c b/src/drivers/driver_none.c
new file mode 100644
index 0000000..aaeacd6
--- /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
new file mode 100644
index 0000000..69ca4b5
--- /dev/null
+++ b/src/drivers/driver_osx.m
@@ -0,0 +1,459 @@
+/*
+ * WPA Supplicant - Mac OS X Apple80211 driver interface
+ * Copyright (c) 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
+ * 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"
+#define Boolean __DummyBoolean
+#include <CoreFoundation/CoreFoundation.h>
+#undef Boolean
+
+#include "common.h"
+#include "driver.h"
+#include "eloop.h"
+#include "common/ieee802_11_defs.h"
+
+#include "Apple80211.h"
+
+struct wpa_driver_osx_data {
+ void *ctx;
+ WirelessRef wireless_ctx;
+ CFArrayRef scan_results;
+};
+
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+extern int wpa_debug_level;
+
+static void dump_dict_cb(const void *key, const void *value, void *context)
+{
+ if (MSG_DEBUG < wpa_debug_level)
+ return;
+
+ wpa_printf(MSG_DEBUG, "Key:");
+ CFShow(key);
+ wpa_printf(MSG_DEBUG, "Value:");
+ CFShow(value);
+}
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+static void wpa_driver_osx_dump_dict(CFDictionaryRef dict, const char *title)
+{
+#ifndef CONFIG_NO_STDOUT_DEBUG
+ wpa_printf(MSG_DEBUG, "OSX: Dump dictionary %s - %u entries",
+ title, (unsigned int) CFDictionaryGetCount(dict));
+ CFDictionaryApplyFunction(dict, dump_dict_cb, NULL);
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+}
+
+
+static int wpa_driver_osx_get_ssid(void *priv, u8 *ssid)
+{
+ struct wpa_driver_osx_data *drv = priv;
+ WirelessError err;
+ WirelessInfo info;
+ int len;
+
+ err = WirelessGetInfo(drv->wireless_ctx, &info);
+ if (err) {
+ wpa_printf(MSG_DEBUG, "OSX: WirelessGetInfo failed: %d",
+ (int) err);
+ return -1;
+ }
+ if (!info.power) {
+ wpa_printf(MSG_DEBUG, "OSX: Wireless device power off");
+ return -1;
+ }
+
+ for (len = 0; len < 32; len++)
+ if (info.ssid[len] == 0)
+ break;
+
+ os_memcpy(ssid, info.ssid, len);
+ return len;
+}
+
+
+static int wpa_driver_osx_get_bssid(void *priv, u8 *bssid)
+{
+ struct wpa_driver_osx_data *drv = priv;
+ WirelessError err;
+ WirelessInfo info;
+
+ err = WirelessGetInfo(drv->wireless_ctx, &info);
+ if (err) {
+ wpa_printf(MSG_DEBUG, "OSX: WirelessGetInfo failed: %d",
+ (int) err);
+ return -1;
+ }
+ if (!info.power) {
+ wpa_printf(MSG_DEBUG, "OSX: Wireless device power off");
+ return -1;
+ }
+
+ os_memcpy(bssid, info.bssID, ETH_ALEN);
+ return 0;
+}
+
+
+static void wpa_driver_osx_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
+}
+
+
+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);
+ drv->scan_results = NULL;
+ }
+
+ if (ssid) {
+ CFStringRef data;
+ data = CFStringCreateWithBytes(kCFAllocatorDefault,
+ ssid, ssid_len,
+ kCFStringEncodingISOLatin1,
+ FALSE);
+ if (data == NULL) {
+ wpa_printf(MSG_DEBUG, "CFStringCreateWithBytes "
+ "failed");
+ return -1;
+ }
+
+ err = WirelessDirectedScan(drv->wireless_ctx,
+ &drv->scan_results, 0, data);
+ CFRelease(data);
+ if (err) {
+ wpa_printf(MSG_DEBUG, "OSX: WirelessDirectedScan "
+ "failed: 0x%08x", (unsigned int) err);
+ return -1;
+ }
+ } else {
+ err = WirelessScan(drv->wireless_ctx, &drv->scan_results, 0);
+ if (err) {
+ wpa_printf(MSG_DEBUG, "OSX: WirelessScan failed: "
+ "0x%08x", (unsigned int) err);
+ return -1;
+ }
+ }
+
+ eloop_register_timeout(0, 0, wpa_driver_osx_scan_timeout, drv,
+ drv->ctx);
+ return 0;
+}
+
+
+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);
+
+ res = os_zalloc(sizeof(*res));
+ if (res == NULL)
+ return NULL;
+
+ for (i = 0; i < num; i++)
+ wpa_driver_osx_add_scan_entry(res, (WirelessNetworkInfo *)
+ CFDataGetBytePtr(CFArrayGetValueAtIndex(
+ drv->scan_results, i)));
+
+ return res;
+}
+
+
+static void wpa_driver_osx_assoc_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_driver_osx_data *drv = eloop_ctx;
+ u8 bssid[ETH_ALEN];
+ CFDictionaryRef ai;
+
+ if (wpa_driver_osx_get_bssid(drv, bssid) != 0) {
+ eloop_register_timeout(1, 0, wpa_driver_osx_assoc_timeout,
+ drv, drv->ctx);
+ return;
+ }
+
+ ai = WirelessGetAssociationInfo(drv->wireless_ctx);
+ if (ai) {
+ wpa_driver_osx_dump_dict(ai, "WirelessGetAssociationInfo");
+ CFRelease(ai);
+ } else {
+ wpa_printf(MSG_DEBUG, "OSX: Failed to get association info");
+ }
+
+ wpa_supplicant_event(timeout_ctx, EVENT_ASSOC, NULL);
+}
+
+
+static int wpa_driver_osx_associate(void *priv,
+ struct wpa_driver_associate_params *params)
+{
+ struct wpa_driver_osx_data *drv = priv;
+ WirelessError err;
+ CFDataRef ssid;
+ CFStringRef key;
+ int assoc_type;
+
+ ssid = CFDataCreate(kCFAllocatorDefault, params->ssid,
+ params->ssid_len);
+ if (ssid == NULL)
+ return -1;
+
+ /* TODO: support for WEP */
+ if (params->key_mgmt_suite == KEY_MGMT_PSK) {
+ if (params->passphrase == NULL)
+ return -1;
+ key = CFStringCreateWithCString(kCFAllocatorDefault,
+ params->passphrase,
+ kCFStringEncodingISOLatin1);
+ if (key == NULL) {
+ CFRelease(ssid);
+ return -1;
+ }
+ } else
+ key = NULL;
+
+ if (params->key_mgmt_suite == KEY_MGMT_NONE)
+ assoc_type = 0;
+ else
+ assoc_type = 4;
+
+ wpa_printf(MSG_DEBUG, "OSX: WirelessAssociate(type=%d key=%p)",
+ assoc_type, key);
+ err = WirelessAssociate(drv->wireless_ctx, assoc_type, ssid, key);
+ CFRelease(ssid);
+ if (key)
+ CFRelease(key);
+ if (err) {
+ wpa_printf(MSG_DEBUG, "OSX: WirelessAssociate failed: 0x%08x",
+ (unsigned int) err);
+ return -1;
+ }
+
+ /*
+ * Driver is actually already associated; report association from an
+ * eloop callback.
+ */
+ eloop_cancel_timeout(wpa_driver_osx_assoc_timeout, drv, drv->ctx);
+ eloop_register_timeout(0, 0, wpa_driver_osx_assoc_timeout, drv,
+ drv->ctx);
+
+ return 0;
+}
+
+
+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)
+{
+ struct wpa_driver_osx_data *drv = priv;
+ WirelessError err;
+
+ if (alg == WPA_ALG_WEP) {
+ err = WirelessSetKey(drv->wireless_ctx, 1, key_idx, key_len,
+ key);
+ if (err != 0) {
+ wpa_printf(MSG_DEBUG, "OSX: WirelessSetKey failed: "
+ "0x%08x", (unsigned int) err);
+ return -1;
+ }
+
+ return 0;
+ }
+
+ if (alg == WPA_ALG_PMK) {
+ err = WirelessSetWPAKey(drv->wireless_ctx, 1, key_len, key);
+ if (err != 0) {
+ wpa_printf(MSG_DEBUG, "OSX: WirelessSetWPAKey failed: "
+ "0x%08x", (unsigned int) err);
+ return -1;
+ }
+ return 0;
+ }
+
+ wpa_printf(MSG_DEBUG, "OSX: Unsupported set_key alg %d", alg);
+ return -1;
+}
+
+
+static int wpa_driver_osx_get_capa(void *priv, struct wpa_driver_capa *capa)
+{
+ os_memset(capa, 0, sizeof(*capa));
+
+ capa->key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
+ capa->enc = WPA_DRIVER_CAPA_ENC_WEP40 | WPA_DRIVER_CAPA_ENC_WEP104 |
+ WPA_DRIVER_CAPA_ENC_TKIP | WPA_DRIVER_CAPA_ENC_CCMP;
+ capa->auth = WPA_DRIVER_AUTH_OPEN | WPA_DRIVER_AUTH_SHARED |
+ WPA_DRIVER_AUTH_LEAP;
+ capa->flags = WPA_DRIVER_FLAGS_4WAY_HANDSHAKE;
+
+ return 0;
+}
+
+
+static void * wpa_driver_osx_init(void *ctx, const char *ifname)
+{
+ struct wpa_driver_osx_data *drv;
+ WirelessError err;
+ u8 enabled, power;
+
+ if (!WirelessIsAvailable()) {
+ wpa_printf(MSG_ERROR, "OSX: No wireless interface available");
+ return NULL;
+ }
+
+ drv = os_zalloc(sizeof(*drv));
+ if (drv == NULL)
+ return NULL;
+ drv->ctx = ctx;
+ err = WirelessAttach(&drv->wireless_ctx, 0);
+ if (err) {
+ wpa_printf(MSG_ERROR, "OSX: WirelessAttach failed: %d",
+ (int) err);
+ os_free(drv);
+ return NULL;
+ }
+
+ err = WirelessGetEnabled(drv->wireless_ctx, &enabled);
+ if (err)
+ wpa_printf(MSG_DEBUG, "OSX: WirelessGetEnabled failed: 0x%08x",
+ (unsigned int) err);
+ err = WirelessGetPower(drv->wireless_ctx, &power);
+ if (err)
+ wpa_printf(MSG_DEBUG, "OSX: WirelessGetPower failed: 0x%08x",
+ (unsigned int) err);
+
+ wpa_printf(MSG_DEBUG, "OSX: Enabled=%d Power=%d", enabled, power);
+
+ if (!enabled) {
+ err = WirelessSetEnabled(drv->wireless_ctx, 1);
+ if (err) {
+ wpa_printf(MSG_DEBUG, "OSX: WirelessSetEnabled failed:"
+ " 0x%08x", (unsigned int) err);
+ WirelessDetach(drv->wireless_ctx);
+ os_free(drv);
+ return NULL;
+ }
+ }
+
+ if (!power) {
+ err = WirelessSetPower(drv->wireless_ctx, 1);
+ if (err) {
+ wpa_printf(MSG_DEBUG, "OSX: WirelessSetPower failed: "
+ "0x%08x", (unsigned int) err);
+ WirelessDetach(drv->wireless_ctx);
+ os_free(drv);
+ return NULL;
+ }
+ }
+
+ return drv;
+}
+
+
+static void wpa_driver_osx_deinit(void *priv)
+{
+ struct wpa_driver_osx_data *drv = priv;
+ WirelessError err;
+
+ eloop_cancel_timeout(wpa_driver_osx_scan_timeout, drv, drv->ctx);
+ eloop_cancel_timeout(wpa_driver_osx_assoc_timeout, drv, drv->ctx);
+
+ err = WirelessSetPower(drv->wireless_ctx, 0);
+ if (err) {
+ wpa_printf(MSG_DEBUG, "OSX: WirelessSetPower(0) failed: "
+ "0x%08x", (unsigned int) err);
+ }
+
+ err = WirelessDetach(drv->wireless_ctx);
+ if (err) {
+ wpa_printf(MSG_DEBUG, "OSX: WirelessDetach failed: 0x%08x",
+ (unsigned int) err);
+ }
+
+ if (drv->scan_results)
+ CFRelease(drv->scan_results);
+
+ os_free(drv);
+}
+
+
+const struct wpa_driver_ops wpa_driver_osx_ops = {
+ .name = "osx",
+ .desc = "Mac OS X Apple80211 driver",
+ .get_ssid = wpa_driver_osx_get_ssid,
+ .get_bssid = wpa_driver_osx_get_bssid,
+ .init = wpa_driver_osx_init,
+ .deinit = wpa_driver_osx_deinit,
+ .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_privsep.c b/src/drivers/driver_privsep.c
new file mode 100644
index 0000000..2848521
--- /dev/null
+++ b/src/drivers/driver_privsep.c
@@ -0,0 +1,758 @@
+/*
+ * WPA Supplicant - privilege separated driver interface
+ * Copyright (c) 2007-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 <sys/un.h>
+
+#include "common.h"
+#include "driver.h"
+#include "eloop.h"
+#include "common/privsep_commands.h"
+
+
+struct wpa_driver_privsep_data {
+ void *ctx;
+ u8 own_addr[ETH_ALEN];
+ int priv_socket;
+ char *own_socket_path;
+ int cmd_socket;
+ char *own_cmd_path;
+ struct sockaddr_un priv_addr;
+ char ifname[16];
+};
+
+
+static int wpa_priv_reg_cmd(struct wpa_driver_privsep_data *drv, int cmd)
+{
+ int res;
+
+ res = sendto(drv->priv_socket, &cmd, sizeof(cmd), 0,
+ (struct sockaddr *) &drv->priv_addr,
+ sizeof(drv->priv_addr));
+ if (res < 0)
+ perror("sendto");
+ return res < 0 ? -1 : 0;
+}
+
+
+static int wpa_priv_cmd(struct wpa_driver_privsep_data *drv, int cmd,
+ const void *data, size_t data_len,
+ void *reply, size_t *reply_len)
+{
+ struct msghdr msg;
+ struct iovec io[2];
+
+ io[0].iov_base = &cmd;
+ io[0].iov_len = sizeof(cmd);
+ 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 = data ? 2 : 1;
+ msg.msg_name = &drv->priv_addr;
+ msg.msg_namelen = sizeof(drv->priv_addr);
+
+ if (sendmsg(drv->cmd_socket, &msg, 0) < 0) {
+ perror("sendmsg(cmd_socket)");
+ return -1;
+ }
+
+ if (reply) {
+ fd_set rfds;
+ struct timeval tv;
+ int res;
+
+ FD_ZERO(&rfds);
+ FD_SET(drv->cmd_socket, &rfds);
+ tv.tv_sec = 5;
+ tv.tv_usec = 0;
+ res = select(drv->cmd_socket + 1, &rfds, NULL, NULL, &tv);
+ if (res < 0 && errno != EINTR) {
+ perror("select");
+ return -1;
+ }
+
+ if (FD_ISSET(drv->cmd_socket, &rfds)) {
+ res = recv(drv->cmd_socket, reply, *reply_len, 0);
+ if (res < 0) {
+ perror("recv");
+ return -1;
+ }
+ *reply_len = res;
+ } else {
+ wpa_printf(MSG_DEBUG, "PRIVSEP: Timeout while waiting "
+ "for reply (cmd=%d)", cmd);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+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);
+}
+
+
+static struct wpa_scan_results *
+wpa_driver_privsep_get_scan_results2(void *priv)
+{
+ struct wpa_driver_privsep_data *drv = priv;
+ int res, num;
+ u8 *buf, *pos, *end;
+ size_t reply_len = 60000;
+ struct wpa_scan_results *results;
+ struct wpa_scan_res *r;
+
+ buf = os_malloc(reply_len);
+ if (buf == NULL)
+ return NULL;
+ res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_SCAN_RESULTS,
+ NULL, 0, buf, &reply_len);
+ if (res < 0) {
+ os_free(buf);
+ return NULL;
+ }
+
+ wpa_printf(MSG_DEBUG, "privsep: Received %lu bytes of scan results",
+ (unsigned long) reply_len);
+ if (reply_len < sizeof(int)) {
+ wpa_printf(MSG_DEBUG, "privsep: Invalid scan result len %lu",
+ (unsigned long) reply_len);
+ os_free(buf);
+ return NULL;
+ }
+
+ pos = buf;
+ end = buf + reply_len;
+ os_memcpy(&num, pos, sizeof(int));
+ if (num < 0 || num > 1000) {
+ os_free(buf);
+ return NULL;
+ }
+ pos += sizeof(int);
+
+ results = os_zalloc(sizeof(*results));
+ if (results == NULL) {
+ os_free(buf);
+ return NULL;
+ }
+
+ results->res = os_zalloc(num * sizeof(struct wpa_scan_res *));
+ if (results->res == NULL) {
+ os_free(results);
+ os_free(buf);
+ return NULL;
+ }
+
+ while (results->num < (size_t) num && pos + sizeof(int) < end) {
+ int len;
+ os_memcpy(&len, pos, sizeof(int));
+ pos += sizeof(int);
+ if (len < 0 || len > 10000 || pos + len > end)
+ break;
+
+ r = os_malloc(len);
+ if (r == NULL)
+ break;
+ os_memcpy(r, pos, len);
+ pos += len;
+ if (sizeof(*r) + r->ie_len > (size_t) len) {
+ os_free(r);
+ break;
+ }
+
+ results->res[results->num++] = r;
+ }
+
+ os_free(buf);
+ return results;
+}
+
+
+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;
+
+ wpa_printf(MSG_DEBUG, "%s: priv=%p alg=%d key_idx=%d set_tx=%d",
+ __func__, priv, alg, key_idx, set_tx);
+
+ os_memset(&cmd, 0, sizeof(cmd));
+ cmd.alg = alg;
+ if (addr)
+ os_memcpy(cmd.addr, addr, ETH_ALEN);
+ else
+ os_memset(cmd.addr, 0xff, ETH_ALEN);
+ cmd.key_idx = key_idx;
+ cmd.set_tx = set_tx;
+ if (seq && seq_len > 0 && seq_len < sizeof(cmd.seq)) {
+ os_memcpy(cmd.seq, seq, seq_len);
+ cmd.seq_len = seq_len;
+ }
+ if (key && key_len > 0 && key_len < sizeof(cmd.key)) {
+ os_memcpy(cmd.key, key, key_len);
+ cmd.key_len = key_len;
+ }
+
+ return wpa_priv_cmd(drv, PRIVSEP_CMD_SET_KEY, &cmd, sizeof(cmd),
+ NULL, NULL);
+}
+
+
+static int wpa_driver_privsep_associate(
+ void *priv, struct wpa_driver_associate_params *params)
+{
+ struct wpa_driver_privsep_data *drv = priv;
+ struct privsep_cmd_associate *data;
+ int res;
+ size_t buflen;
+
+ 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,
+ params->group_suite, params->key_mgmt_suite,
+ params->auth_alg, params->mode);
+
+ buflen = sizeof(*data) + params->wpa_ie_len;
+ data = os_zalloc(buflen);
+ if (data == NULL)
+ return -1;
+
+ if (params->bssid)
+ os_memcpy(data->bssid, params->bssid, ETH_ALEN);
+ os_memcpy(data->ssid, params->ssid, params->ssid_len);
+ data->ssid_len = params->ssid_len;
+ data->freq = params->freq;
+ data->pairwise_suite = params->pairwise_suite;
+ data->group_suite = params->group_suite;
+ data->key_mgmt_suite = params->key_mgmt_suite;
+ data->auth_alg = params->auth_alg;
+ data->mode = params->mode;
+ data->wpa_ie_len = params->wpa_ie_len;
+ if (params->wpa_ie)
+ os_memcpy(data + 1, params->wpa_ie, params->wpa_ie_len);
+ /* TODO: add support for other assoc parameters */
+
+ res = wpa_priv_cmd(drv, PRIVSEP_CMD_ASSOCIATE, data, buflen,
+ NULL, NULL);
+ os_free(data);
+
+ return res;
+}
+
+
+static int wpa_driver_privsep_get_bssid(void *priv, u8 *bssid)
+{
+ struct wpa_driver_privsep_data *drv = priv;
+ int res;
+ size_t len = ETH_ALEN;
+
+ res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_BSSID, NULL, 0, bssid, &len);
+ if (res < 0 || len != ETH_ALEN)
+ return -1;
+ return 0;
+}
+
+
+static int wpa_driver_privsep_get_ssid(void *priv, u8 *ssid)
+{
+ struct wpa_driver_privsep_data *drv = priv;
+ int res, ssid_len;
+ u8 reply[sizeof(int) + 32];
+ size_t len = sizeof(reply);
+
+ res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_SSID, NULL, 0, reply, &len);
+ if (res < 0 || len < sizeof(int))
+ return -1;
+ os_memcpy(&ssid_len, reply, sizeof(int));
+ if (ssid_len < 0 || ssid_len > 32 || sizeof(int) + ssid_len > len) {
+ wpa_printf(MSG_DEBUG, "privsep: Invalid get SSID reply");
+ return -1;
+ }
+ os_memcpy(ssid, &reply[sizeof(int)], ssid_len);
+ return ssid_len;
+}
+
+
+static int wpa_driver_privsep_deauthenticate(void *priv, const u8 *addr,
+ int reason_code)
+{
+ //struct wpa_driver_privsep_data *drv = priv;
+ wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d",
+ __func__, MAC2STR(addr), reason_code);
+ wpa_printf(MSG_DEBUG, "%s - TODO", __func__);
+ return 0;
+}
+
+
+static int wpa_driver_privsep_disassociate(void *priv, const u8 *addr,
+ int reason_code)
+{
+ //struct wpa_driver_privsep_data *drv = priv;
+ wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d",
+ __func__, MAC2STR(addr), reason_code);
+ wpa_printf(MSG_DEBUG, "%s - TODO", __func__);
+ return 0;
+}
+
+
+static void wpa_driver_privsep_event_assoc(void *ctx,
+ enum wpa_event_type event,
+ u8 *buf, size_t len)
+{
+ union wpa_event_data data;
+ int inc_data = 0;
+ u8 *pos, *end;
+ int ie_len;
+
+ os_memset(&data, 0, sizeof(data));
+
+ pos = buf;
+ end = buf + len;
+
+ if (end - pos < (int) sizeof(int))
+ return;
+ os_memcpy(&ie_len, pos, sizeof(int));
+ pos += sizeof(int);
+ if (ie_len < 0 || ie_len > end - pos)
+ return;
+ if (ie_len) {
+ data.assoc_info.req_ies = pos;
+ data.assoc_info.req_ies_len = ie_len;
+ pos += ie_len;
+ inc_data = 1;
+ }
+
+ wpa_supplicant_event(ctx, event, inc_data ? &data : NULL);
+}
+
+
+static void wpa_driver_privsep_event_interface_status(void *ctx, u8 *buf,
+ size_t len)
+{
+ union wpa_event_data data;
+ int ievent;
+
+ if (len < sizeof(int) ||
+ len - sizeof(int) > sizeof(data.interface_status.ifname))
+ return;
+
+ os_memcpy(&ievent, buf, sizeof(int));
+
+ os_memset(&data, 0, sizeof(data));
+ data.interface_status.ievent = ievent;
+ os_memcpy(data.interface_status.ifname, buf + sizeof(int),
+ len - sizeof(int));
+ wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &data);
+}
+
+
+static void wpa_driver_privsep_event_michael_mic_failure(
+ void *ctx, u8 *buf, size_t len)
+{
+ union wpa_event_data data;
+
+ if (len != sizeof(int))
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+ os_memcpy(&data.michael_mic_failure.unicast, buf, sizeof(int));
+ wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
+}
+
+
+static void wpa_driver_privsep_event_pmkid_candidate(void *ctx, u8 *buf,
+ size_t len)
+{
+ union wpa_event_data data;
+
+ if (len != sizeof(struct pmkid_candidate))
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+ os_memcpy(&data.pmkid_candidate, buf, len);
+ wpa_supplicant_event(ctx, EVENT_PMKID_CANDIDATE, &data);
+}
+
+
+static void wpa_driver_privsep_event_stkstart(void *ctx, u8 *buf, size_t len)
+{
+ union wpa_event_data data;
+
+ if (len != ETH_ALEN)
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+ os_memcpy(data.stkstart.peer, buf, ETH_ALEN);
+ wpa_supplicant_event(ctx, EVENT_STKSTART, &data);
+}
+
+
+static void wpa_driver_privsep_event_ft_response(void *ctx, u8 *buf,
+ size_t len)
+{
+ union wpa_event_data data;
+
+ if (len < sizeof(int) + ETH_ALEN)
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+ os_memcpy(&data.ft_ies.ft_action, buf, sizeof(int));
+ os_memcpy(data.ft_ies.target_ap, buf + sizeof(int), ETH_ALEN);
+ data.ft_ies.ies = buf + sizeof(int) + ETH_ALEN;
+ data.ft_ies.ies_len = len - sizeof(int) - ETH_ALEN;
+ wpa_supplicant_event(ctx, EVENT_FT_RESPONSE, &data);
+}
+
+
+static void wpa_driver_privsep_event_rx_eapol(void *ctx, u8 *buf, size_t len)
+{
+ if (len < ETH_ALEN)
+ return;
+ drv_event_eapol_rx(ctx, buf, buf + ETH_ALEN, len - ETH_ALEN);
+}
+
+
+static void wpa_driver_privsep_receive(int sock, void *eloop_ctx,
+ void *sock_ctx)
+{
+ struct wpa_driver_privsep_data *drv = eloop_ctx;
+ u8 *buf, *event_buf;
+ size_t event_len;
+ int res, event;
+ enum privsep_event e;
+ struct sockaddr_un from;
+ socklen_t fromlen = sizeof(from);
+ const size_t buflen = 2000;
+
+ buf = os_malloc(buflen);
+ if (buf == NULL)
+ return;
+ res = recvfrom(sock, buf, buflen, 0,
+ (struct sockaddr *) &from, &fromlen);
+ if (res < 0) {
+ perror("recvfrom(priv_socket)");
+ os_free(buf);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "privsep_driver: received %u bytes", res);
+
+ if (res < (int) sizeof(int)) {
+ wpa_printf(MSG_DEBUG, "Too short event message (len=%d)", res);
+ return;
+ }
+
+ os_memcpy(&event, buf, sizeof(int));
+ event_buf = &buf[sizeof(int)];
+ event_len = res - sizeof(int);
+ wpa_printf(MSG_DEBUG, "privsep: Event %d received (len=%lu)",
+ event, (unsigned long) event_len);
+
+ e = event;
+ switch (e) {
+ case PRIVSEP_EVENT_SCAN_RESULTS:
+ wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL);
+ break;
+ case PRIVSEP_EVENT_ASSOC:
+ wpa_driver_privsep_event_assoc(drv->ctx, EVENT_ASSOC,
+ event_buf, event_len);
+ break;
+ case PRIVSEP_EVENT_DISASSOC:
+ wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
+ break;
+ case PRIVSEP_EVENT_ASSOCINFO:
+ wpa_driver_privsep_event_assoc(drv->ctx, EVENT_ASSOCINFO,
+ event_buf, event_len);
+ break;
+ case PRIVSEP_EVENT_MICHAEL_MIC_FAILURE:
+ wpa_driver_privsep_event_michael_mic_failure(
+ drv->ctx, event_buf, event_len);
+ break;
+ case PRIVSEP_EVENT_INTERFACE_STATUS:
+ wpa_driver_privsep_event_interface_status(drv->ctx, event_buf,
+ event_len);
+ break;
+ case PRIVSEP_EVENT_PMKID_CANDIDATE:
+ wpa_driver_privsep_event_pmkid_candidate(drv->ctx, event_buf,
+ event_len);
+ break;
+ case PRIVSEP_EVENT_STKSTART:
+ wpa_driver_privsep_event_stkstart(drv->ctx, event_buf,
+ event_len);
+ break;
+ case PRIVSEP_EVENT_FT_RESPONSE:
+ wpa_driver_privsep_event_ft_response(drv->ctx, event_buf,
+ event_len);
+ break;
+ case PRIVSEP_EVENT_RX_EAPOL:
+ wpa_driver_privsep_event_rx_eapol(drv->ctx, event_buf,
+ event_len);
+ break;
+ }
+
+ os_free(buf);
+}
+
+
+static void * wpa_driver_privsep_init(void *ctx, const char *ifname)
+{
+ struct wpa_driver_privsep_data *drv;
+
+ drv = os_zalloc(sizeof(*drv));
+ if (drv == NULL)
+ return NULL;
+ drv->ctx = ctx;
+ drv->priv_socket = -1;
+ drv->cmd_socket = -1;
+ os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+
+ return drv;
+}
+
+
+static void wpa_driver_privsep_deinit(void *priv)
+{
+ struct wpa_driver_privsep_data *drv = priv;
+
+ if (drv->priv_socket >= 0) {
+ wpa_priv_reg_cmd(drv, PRIVSEP_CMD_UNREGISTER);
+ eloop_unregister_read_sock(drv->priv_socket);
+ close(drv->priv_socket);
+ }
+
+ if (drv->own_socket_path) {
+ unlink(drv->own_socket_path);
+ os_free(drv->own_socket_path);
+ }
+
+ if (drv->cmd_socket >= 0) {
+ eloop_unregister_read_sock(drv->cmd_socket);
+ close(drv->cmd_socket);
+ }
+
+ if (drv->own_cmd_path) {
+ unlink(drv->own_cmd_path);
+ os_free(drv->own_cmd_path);
+ }
+
+ os_free(drv);
+}
+
+
+static int wpa_driver_privsep_set_param(void *priv, const char *param)
+{
+ struct wpa_driver_privsep_data *drv = priv;
+ const char *pos;
+ char *own_dir, *priv_dir;
+ static unsigned int counter = 0;
+ size_t len;
+ struct sockaddr_un addr;
+
+ wpa_printf(MSG_DEBUG, "%s: param='%s'", __func__, param);
+ if (param == NULL)
+ pos = NULL;
+ else
+ pos = os_strstr(param, "own_dir=");
+ if (pos) {
+ char *end;
+ own_dir = os_strdup(pos + 8);
+ if (own_dir == NULL)
+ return -1;
+ end = os_strchr(own_dir, ' ');
+ if (end)
+ *end = '\0';
+ } else {
+ own_dir = os_strdup("/tmp");
+ if (own_dir == NULL)
+ return -1;
+ }
+
+ if (param == NULL)
+ pos = NULL;
+ else
+ pos = os_strstr(param, "priv_dir=");
+ if (pos) {
+ char *end;
+ priv_dir = os_strdup(pos + 9);
+ if (priv_dir == NULL) {
+ os_free(own_dir);
+ return -1;
+ }
+ end = os_strchr(priv_dir, ' ');
+ if (end)
+ *end = '\0';
+ } else {
+ priv_dir = os_strdup("/var/run/wpa_priv");
+ if (priv_dir == NULL) {
+ os_free(own_dir);
+ return -1;
+ }
+ }
+
+ len = os_strlen(own_dir) + 50;
+ drv->own_socket_path = os_malloc(len);
+ if (drv->own_socket_path == NULL) {
+ os_free(priv_dir);
+ os_free(own_dir);
+ return -1;
+ }
+ os_snprintf(drv->own_socket_path, len, "%s/wpa_privsep-%d-%d",
+ own_dir, getpid(), counter++);
+
+ len = os_strlen(own_dir) + 50;
+ drv->own_cmd_path = os_malloc(len);
+ if (drv->own_cmd_path == NULL) {
+ os_free(drv->own_socket_path);
+ drv->own_socket_path = NULL;
+ os_free(priv_dir);
+ os_free(own_dir);
+ return -1;
+ }
+ os_snprintf(drv->own_cmd_path, len, "%s/wpa_privsep-%d-%d",
+ own_dir, getpid(), counter++);
+
+ os_free(own_dir);
+
+ drv->priv_addr.sun_family = AF_UNIX;
+ os_snprintf(drv->priv_addr.sun_path, sizeof(drv->priv_addr.sun_path),
+ "%s/%s", priv_dir, drv->ifname);
+ os_free(priv_dir);
+
+ drv->priv_socket = socket(PF_UNIX, SOCK_DGRAM, 0);
+ if (drv->priv_socket < 0) {
+ perror("socket(PF_UNIX)");
+ os_free(drv->own_socket_path);
+ drv->own_socket_path = NULL;
+ return -1;
+ }
+
+ os_memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ os_strlcpy(addr.sun_path, drv->own_socket_path, sizeof(addr.sun_path));
+ if (bind(drv->priv_socket, (struct sockaddr *) &addr, sizeof(addr)) <
+ 0) {
+ perror("bind(PF_UNIX)");
+ close(drv->priv_socket);
+ drv->priv_socket = -1;
+ unlink(drv->own_socket_path);
+ os_free(drv->own_socket_path);
+ drv->own_socket_path = NULL;
+ return -1;
+ }
+
+ eloop_register_read_sock(drv->priv_socket, wpa_driver_privsep_receive,
+ drv, NULL);
+
+ drv->cmd_socket = socket(PF_UNIX, SOCK_DGRAM, 0);
+ if (drv->cmd_socket < 0) {
+ perror("socket(PF_UNIX)");
+ os_free(drv->own_cmd_path);
+ drv->own_cmd_path = NULL;
+ return -1;
+ }
+
+ os_memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ os_strlcpy(addr.sun_path, drv->own_cmd_path, sizeof(addr.sun_path));
+ if (bind(drv->cmd_socket, (struct sockaddr *) &addr, sizeof(addr)) < 0)
+ {
+ perror("bind(PF_UNIX)");
+ close(drv->cmd_socket);
+ drv->cmd_socket = -1;
+ unlink(drv->own_cmd_path);
+ os_free(drv->own_cmd_path);
+ drv->own_cmd_path = NULL;
+ return -1;
+ }
+
+ if (wpa_priv_reg_cmd(drv, PRIVSEP_CMD_REGISTER) < 0) {
+ wpa_printf(MSG_ERROR, "Failed to register with wpa_priv");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wpa_driver_privsep_get_capa(void *priv,
+ struct wpa_driver_capa *capa)
+{
+ struct wpa_driver_privsep_data *drv = priv;
+ int res;
+ size_t len = sizeof(*capa);
+
+ res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_CAPA, NULL, 0, capa, &len);
+ if (res < 0 || len != sizeof(*capa))
+ return -1;
+ return 0;
+}
+
+
+static const u8 * wpa_driver_privsep_get_mac_addr(void *priv)
+{
+ struct wpa_driver_privsep_data *drv = priv;
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+ return drv->own_addr;
+}
+
+
+static int wpa_driver_privsep_set_country(void *priv, const char *alpha2)
+{
+ struct wpa_driver_privsep_data *drv = priv;
+ wpa_printf(MSG_DEBUG, "%s country='%s'", __func__, alpha2);
+ return wpa_priv_cmd(drv, PRIVSEP_CMD_SET_COUNTRY, alpha2,
+ os_strlen(alpha2), NULL, NULL);
+}
+
+
+struct wpa_driver_ops wpa_driver_privsep_ops = {
+ "privsep",
+ "wpa_supplicant privilege separated driver",
+ .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_drivers[] =
+{
+ &wpa_driver_privsep_ops,
+ NULL
+};
diff --git a/src/drivers/driver_ralink.c b/src/drivers/driver_ralink.c
new file mode 100644
index 0000000..a1e27be
--- /dev/null
+++ b/src/drivers/driver_ralink.c
@@ -0,0 +1,1498 @@
+/*
+ * WPA Supplicant - driver interaction with Ralink Wireless Client
+ * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007, Snowpin Lee <snowpin_lee@ralinktech.com.tw>
+ *
+ * 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 "l2_packet/l2_packet.h"
+#include "eloop.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);
+
+#define MAX_SSID_LEN 32
+
+struct wpa_driver_ralink_data {
+ void *ctx;
+ int ioctl_sock;
+ struct netlink_data *netlink;
+ char ifname[IFNAMSIZ + 1];
+ u8 *assoc_req_ies;
+ size_t assoc_req_ies_len;
+ u8 *assoc_resp_ies;
+ size_t assoc_resp_ies_len;
+ int no_of_pmkid;
+ struct ndis_pmkid_entry *pmkid;
+ int we_version_compiled;
+ int ap_scan;
+ int scanning_done;
+ u8 g_driver_down;
+ BOOLEAN bAddWepKey;
+};
+
+static int ralink_set_oid(struct wpa_driver_ralink_data *drv,
+ unsigned short oid, char *data, int len)
+{
+ char *buf;
+ struct iwreq iwr;
+
+ buf = os_zalloc(len);
+ if (buf == NULL)
+ return -1;
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+ iwr.u.data.flags = oid;
+ iwr.u.data.flags |= OID_GET_SET_TOGGLE;
+
+ if (data)
+ os_memcpy(buf, data, len);
+
+ iwr.u.data.pointer = (caddr_t) buf;
+ iwr.u.data.length = len;
+
+ if (ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr) < 0) {
+ wpa_printf(MSG_DEBUG, "%s: oid=0x%x len (%d) failed",
+ __func__, oid, len);
+ os_free(buf);
+ return -1;
+ }
+ os_free(buf);
+ return 0;
+}
+
+static int
+ralink_get_new_driver_flag(struct wpa_driver_ralink_data *drv)
+{
+ struct iwreq iwr;
+ UCHAR enabled = 0;
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+ iwr.u.data.pointer = (UCHAR*) &enabled;
+ iwr.u.data.flags = RT_OID_NEW_DRIVER;
+
+ if (ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr) < 0) {
+ wpa_printf(MSG_DEBUG, "%s: failed", __func__);
+ return 0;
+ }
+
+ return (enabled == 1) ? 1 : 0;
+}
+
+static int wpa_driver_ralink_get_bssid(void *priv, u8 *bssid)
+{
+ struct wpa_driver_ralink_data *drv = priv;
+ struct iwreq iwr;
+ int ret = 0;
+
+ if (drv->g_driver_down == 1)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+
+ if (ioctl(drv->ioctl_sock, SIOCGIWAP, &iwr) < 0) {
+ perror("ioctl[SIOCGIWAP]");
+ ret = -1;
+ }
+ os_memcpy(bssid, iwr.u.ap_addr.sa_data, ETH_ALEN);
+
+ return ret;
+}
+
+static int wpa_driver_ralink_get_ssid(void *priv, u8 *ssid)
+{
+ struct wpa_driver_ralink_data *drv = priv;
+#if 0
+ struct wpa_supplicant *wpa_s = drv->ctx;
+ struct wpa_ssid *entry;
+#endif
+ int ssid_len;
+ u8 bssid[ETH_ALEN];
+ u8 ssid_str[MAX_SSID_LEN];
+ struct iwreq iwr;
+#if 0
+ int result = 0;
+#endif
+ int ret = 0;
+#if 0
+ BOOLEAN ieee8021x_mode = FALSE;
+ BOOLEAN ieee8021x_required_key = FALSE;
+#endif
+
+ if (drv->g_driver_down == 1)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ 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;
+
+ if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) {
+ perror("ioctl[SIOCGIWESSID]");
+ ret = -1;
+ } else
+ ret = iwr.u.essid.length;
+
+ if (ret <= 0)
+ return ret;
+
+ ssid_len = ret;
+ os_memset(ssid_str, 0, MAX_SSID_LEN);
+ os_memcpy(ssid_str, ssid, ssid_len);
+
+ if (drv->ap_scan == 0) {
+ /* Read BSSID form driver */
+ if (wpa_driver_ralink_get_bssid(priv, bssid) < 0) {
+ wpa_printf(MSG_WARNING, "Could not read BSSID from "
+ "driver.");
+ return ret;
+ }
+
+#if 0
+ entry = wpa_s->conf->ssid;
+ while (entry) {
+ if (!entry->disabled && ssid_len == entry->ssid_len &&
+ os_memcmp(ssid_str, entry->ssid, ssid_len) == 0 &&
+ (!entry->bssid_set ||
+ os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0)) {
+ /* match the config of driver */
+ result = 1;
+ break;
+ }
+ entry = entry->next;
+ }
+
+ if (result) {
+ wpa_printf(MSG_DEBUG, "Ready to set 802.1x mode and "
+ "ieee_required_keys parameters to driver");
+
+ /* set 802.1x mode and ieee_required_keys parameter */
+ if (entry->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+ if ((entry->eapol_flags & (EAPOL_FLAG_REQUIRE_KEY_UNICAST | EAPOL_FLAG_REQUIRE_KEY_BROADCAST)))
+ ieee8021x_required_key = TRUE;
+ ieee8021x_mode = TRUE;
+ }
+
+ if (ralink_set_oid(drv, OID_802_11_SET_IEEE8021X, (char *) &ieee8021x_mode, sizeof(BOOLEAN)) < 0)
+ {
+ wpa_printf(MSG_DEBUG, "RALINK: Failed to set OID_802_11_SET_IEEE8021X(%d)", (int) ieee8021x_mode);
+ }
+ else
+ {
+ wpa_printf(MSG_DEBUG, "ieee8021x_mode is %s", ieee8021x_mode ? "TRUE" : "FALSE");
+ }
+
+ 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 and eapol_flag(%d)", ieee8021x_required_key ? "TRUE" : "FALSE",
+ entry->eapol_flags);
+ }
+ }
+#endif
+ }
+
+ return ret;
+}
+
+static int wpa_driver_ralink_set_ssid(struct wpa_driver_ralink_data *drv,
+ const u8 *ssid, size_t ssid_len)
+{
+ NDIS_802_11_SSID *buf;
+ int ret = 0;
+ struct iwreq iwr;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ buf = os_zalloc(sizeof(NDIS_802_11_SSID));
+ if (buf == NULL)
+ return -1;
+ os_memset(buf, 0, sizeof(buf));
+ buf->SsidLength = ssid_len;
+ os_memcpy(buf->Ssid, ssid, ssid_len);
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+
+ iwr.u.data.flags = OID_802_11_SSID;
+ iwr.u.data.flags |= OID_GET_SET_TOGGLE;
+ iwr.u.data.pointer = (caddr_t) buf;
+ iwr.u.data.length = sizeof(NDIS_802_11_SSID);
+
+ if (ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr) < 0) {
+ perror("ioctl[RT_PRIV_IOCTL] -- OID_802_11_SSID");
+ ret = -1;
+ }
+ os_free(buf);
+ return ret;
+}
+
+static void wpa_driver_ralink_event_pmkid(struct wpa_driver_ralink_data *drv,
+ const u8 *data, size_t data_len)
+{
+ NDIS_802_11_PMKID_CANDIDATE_LIST *pmkid;
+ size_t i;
+ union wpa_event_data event;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ if (data_len < 8) {
+ wpa_printf(MSG_DEBUG, "RALINK: Too short PMKID Candidate List "
+ "Event (len=%lu)", (unsigned long) data_len);
+ return;
+ }
+ pmkid = (NDIS_802_11_PMKID_CANDIDATE_LIST *) data;
+ wpa_printf(MSG_DEBUG, "RALINK: PMKID Candidate List Event - Version %d"
+ " NumCandidates %d",
+ (int) pmkid->Version, (int) pmkid->NumCandidates);
+
+ if (pmkid->Version != 1) {
+ wpa_printf(MSG_DEBUG, "RALINK: Unsupported PMKID Candidate "
+ "List Version %d", (int) pmkid->Version);
+ return;
+ }
+
+ if (data_len < 8 + pmkid->NumCandidates * sizeof(PMKID_CANDIDATE)) {
+ wpa_printf(MSG_DEBUG, "RALINK: PMKID Candidate List "
+ "underflow");
+
+ return;
+ }
+
+
+
+ os_memset(&event, 0, sizeof(event));
+ for (i = 0; i < pmkid->NumCandidates; i++) {
+ PMKID_CANDIDATE *p = &pmkid->CandidateList[i];
+ wpa_printf(MSG_DEBUG, "RALINK: %lu: " MACSTR " Flags 0x%x",
+ (unsigned long) i, MAC2STR(p->BSSID),
+ (int) p->Flags);
+ os_memcpy(event.pmkid_candidate.bssid, p->BSSID, ETH_ALEN);
+ event.pmkid_candidate.index = i;
+ event.pmkid_candidate.preauth =
+ p->Flags & NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED;
+ wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE,
+ &event);
+ }
+}
+
+static int wpa_driver_ralink_set_pmkid(struct wpa_driver_ralink_data *drv)
+{
+ int len, count, i, ret;
+ struct ndis_pmkid_entry *entry;
+ NDIS_802_11_PMKID *p;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ count = 0;
+ entry = drv->pmkid;
+ while (entry) {
+ count++;
+ if (count >= drv->no_of_pmkid)
+ break;
+ entry = entry->next;
+ }
+ len = 8 + count * sizeof(BSSID_INFO);
+ p = os_zalloc(len);
+ if (p == NULL)
+ return -1;
+ p->Length = len;
+ p->BSSIDInfoCount = count;
+ entry = drv->pmkid;
+ for (i = 0; i < count; i++) {
+ os_memcpy(&p->BSSIDInfo[i].BSSID, entry->bssid, ETH_ALEN);
+ os_memcpy(&p->BSSIDInfo[i].PMKID, entry->pmkid, 16);
+ entry = entry->next;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "NDIS: OID_802_11_PMKID",
+ (const u8 *) p, len);
+ ret = ralink_set_oid(drv, OID_802_11_PMKID, (char *) p, len);
+ os_free(p);
+ return ret;
+}
+
+static int wpa_driver_ralink_add_pmkid(void *priv, const u8 *bssid,
+ const u8 *pmkid)
+{
+ struct wpa_driver_ralink_data *drv = priv;
+ struct ndis_pmkid_entry *entry, *prev;
+
+ if (drv->g_driver_down == 1)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ if (drv->no_of_pmkid == 0)
+ return 0;
+
+ prev = NULL;
+ entry = drv->pmkid;
+ while (entry) {
+ if (os_memcmp(entry->bssid, bssid, ETH_ALEN) == 0)
+ break;
+ prev = entry;
+ entry = entry->next;
+ }
+
+ if (entry) {
+ /* Replace existing entry for this BSSID and move it into the
+ * beginning of the list. */
+ os_memcpy(entry->pmkid, pmkid, 16);
+ if (prev) {
+ prev->next = entry->next;
+ entry->next = drv->pmkid;
+ drv->pmkid = entry;
+ }
+ } else {
+ entry = os_malloc(sizeof(*entry));
+ if (entry) {
+ os_memcpy(entry->bssid, bssid, ETH_ALEN);
+ os_memcpy(entry->pmkid, pmkid, 16);
+ entry->next = drv->pmkid;
+ drv->pmkid = entry;
+ }
+ }
+
+ return wpa_driver_ralink_set_pmkid(drv);
+}
+
+
+static int wpa_driver_ralink_remove_pmkid(void *priv, const u8 *bssid,
+ const u8 *pmkid)
+{
+ struct wpa_driver_ralink_data *drv = priv;
+ struct ndis_pmkid_entry *entry, *prev;
+
+ if (drv->g_driver_down == 1)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ if (drv->no_of_pmkid == 0)
+ return 0;
+
+ entry = drv->pmkid;
+ prev = NULL;
+ drv->pmkid = NULL;
+ while (entry) {
+ if (os_memcmp(entry->bssid, bssid, ETH_ALEN) == 0 &&
+ os_memcmp(entry->pmkid, pmkid, 16) == 0) {
+ if (prev)
+ prev->next = entry->next;
+ else
+ drv->pmkid = entry->next;
+ os_free(entry);
+ break;
+ }
+ prev = entry;
+ entry = entry->next;
+ }
+ return wpa_driver_ralink_set_pmkid(drv);
+}
+
+
+static int wpa_driver_ralink_flush_pmkid(void *priv)
+{
+ struct wpa_driver_ralink_data *drv = priv;
+ NDIS_802_11_PMKID p;
+ struct ndis_pmkid_entry *pmkid, *prev;
+
+ if (drv->g_driver_down == 1)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ if (drv->no_of_pmkid == 0)
+ return 0;
+
+ pmkid = drv->pmkid;
+ drv->pmkid = NULL;
+ while (pmkid) {
+ prev = pmkid;
+ pmkid = pmkid->next;
+ os_free(prev);
+ }
+
+ os_memset(&p, 0, sizeof(p));
+ p.Length = 8;
+ p.BSSIDInfoCount = 0;
+ wpa_hexdump(MSG_MSGDUMP, "NDIS: OID_802_11_PMKID (flush)",
+ (const u8 *) &p, 8);
+ return ralink_set_oid(drv, OID_802_11_PMKID, (char *) &p, 8);
+}
+
+static void
+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__);
+
+ wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom);
+
+ os_memset(&data, 0, sizeof(data));
+ /* Host AP driver */
+ if (os_strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) {
+ /* receive a MICFAILURE report */
+ 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) {
+ /* receive assoc. req. IEs */
+ char *spos;
+ int bytes;
+
+ spos = custom + 17;
+ /*get IE's length */
+ /*
+ * bytes = strlen(spos); ==> bug, bytes may less than original
+ * size by using this way to get size. snowpin 20070312
+ * if (!bytes)
+ * return;
+ */
+ bytes = drv->assoc_req_ies_len;
+
+ 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;
+
+ /* skip the '\0' byte */
+ spos += bytes + 1;
+
+ data.assoc_info.resp_ies = NULL;
+ data.assoc_info.resp_ies_len = 0;
+
+ if (os_strncmp(spos, " RespIEs=", 9) == 0) {
+ /* receive assoc. resp. IEs */
+ spos += 9;
+ /* get IE's length */
+ bytes = os_strlen(spos);
+ if (!bytes)
+ goto done;
+
+ 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;
+ }
+
+ wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data);
+
+ done:
+ /* 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");
+ }
+}
+
+static void
+wpa_driver_ralink_event_wireless(struct wpa_driver_ralink_data *drv,
+ void *ctx, char *data, int len)
+{
+ struct iw_event iwe_buf, *iwe = &iwe_buf;
+ char *pos, *end, *custom, *buf, *assoc_info_buf, *info_pos;
+#if 0
+ BOOLEAN ieee8021x_required_key = FALSE;
+#endif
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ assoc_info_buf = info_pos = NULL;
+ 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. */
+ 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)
+ return;
+
+ custom = pos + IW_EV_POINT_LEN;
+
+ if (drv->we_version_compiled > 18 && 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;
+ 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 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';
+
+ if (drv->ap_scan == 1) {
+ if ((iwe->u.data.flags == RT_ASSOC_EVENT_FLAG)
+ || (iwe->u.data.flags ==
+ RT_REQIE_EVENT_FLAG) ||
+ (iwe->u.data.flags == RT_RESPIE_EVENT_FLAG)
+ || (iwe->u.data.flags ==
+ RT_ASSOCINFO_EVENT_FLAG)) {
+ if (drv->scanning_done == 0) {
+ os_free(buf);
+ return;
+ }
+ }
+ }
+
+ 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 !!!");
+ } else if (iwe->u.data.flags == RT_REQIE_EVENT_FLAG) {
+ wpa_printf(MSG_DEBUG, "Custom wireless event: "
+ "receive ReqIEs !!!");
+ drv->assoc_req_ies =
+ os_malloc(iwe->u.data.length);
+ if (drv->assoc_req_ies == NULL) {
+ os_free(buf);
+ return;
+ }
+
+ drv->assoc_req_ies_len = iwe->u.data.length;
+ os_memcpy(drv->assoc_req_ies, custom,
+ iwe->u.data.length);
+ } else if (iwe->u.data.flags == RT_RESPIE_EVENT_FLAG) {
+ wpa_printf(MSG_DEBUG, "Custom wireless event: "
+ "receive RespIEs !!!");
+ drv->assoc_resp_ies =
+ os_malloc(iwe->u.data.length);
+ if (drv->assoc_resp_ies == NULL) {
+ os_free(drv->assoc_req_ies);
+ drv->assoc_req_ies = NULL;
+ os_free(buf);
+ return;
+ }
+
+ drv->assoc_resp_ies_len = iwe->u.data.length;
+ os_memcpy(drv->assoc_resp_ies, custom,
+ iwe->u.data.length);
+ } else if (iwe->u.data.flags ==
+ RT_ASSOCINFO_EVENT_FLAG) {
+ wpa_printf(MSG_DEBUG, "Custom wireless event: "
+ "receive ASSOCINFO_EVENT !!!");
+
+ assoc_info_buf =
+ os_zalloc(drv->assoc_req_ies_len +
+ drv->assoc_resp_ies_len + 1);
+
+ if (assoc_info_buf == NULL) {
+ os_free(drv->assoc_req_ies);
+ drv->assoc_req_ies = NULL;
+ os_free(drv->assoc_resp_ies);
+ drv->assoc_resp_ies = NULL;
+ os_free(buf);
+ return;
+ }
+
+ if (drv->assoc_req_ies) {
+ os_memcpy(assoc_info_buf,
+ drv->assoc_req_ies,
+ drv->assoc_req_ies_len);
+ }
+ info_pos = assoc_info_buf +
+ drv->assoc_req_ies_len;
+ if (drv->assoc_resp_ies) {
+ os_memcpy(info_pos,
+ drv->assoc_resp_ies,
+ drv->assoc_resp_ies_len);
+ }
+ assoc_info_buf[drv->assoc_req_ies_len +
+ drv->assoc_resp_ies_len] = '\0';
+ wpa_driver_ralink_event_wireless_custom(
+ drv, ctx, assoc_info_buf);
+ os_free(drv->assoc_req_ies);
+ drv->assoc_req_ies = NULL;
+ os_free(drv->assoc_resp_ies);
+ drv->assoc_resp_ies = NULL;
+ os_free(assoc_info_buf);
+ } else if (iwe->u.data.flags == RT_DISASSOC_EVENT_FLAG)
+ {
+ wpa_printf(MSG_DEBUG, "Custom wireless event: "
+ "receive DISASSOCIATED_EVENT !!!");
+ wpa_supplicant_event(ctx, EVENT_DISASSOC,
+ NULL);
+ } else if (iwe->u.data.flags == RT_PMKIDCAND_FLAG) {
+ wpa_printf(MSG_DEBUG, "Custom wireless event: "
+ "receive PMKIDCAND_EVENT !!!");
+ wpa_driver_ralink_event_pmkid(
+ drv, (const u8 *) custom,
+ iwe->u.data.length);
+ } else if (iwe->u.data.flags == RT_INTERFACE_DOWN) {
+ drv->g_driver_down = 1;
+ eloop_terminate();
+ } else if (iwe->u.data.flags == RT_INTERFACE_UP) {
+ ralink_interface_up(drv);
+ } else {
+ wpa_driver_ralink_event_wireless_custom(
+ drv, ctx, buf);
+ }
+ os_free(buf);
+ break;
+ }
+
+ pos += iwe->len;
+ }
+}
+
+static void
+wpa_driver_ralink_event_rtm_newlink(void *ctx, struct ifinfomsg *ifi,
+ u8 *buf, size_t len)
+{
+ struct wpa_driver_ralink_data *drv = ctx;
+ int attrlen, rta_len;
+ struct rtattr *attr;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ wpa_hexdump(MSG_DEBUG, "ifi: ", (u8 *) ifi, sizeof(struct ifinfomsg));
+
+ attrlen = len;
+ wpa_printf(MSG_DEBUG, "attrlen=%d", attrlen);
+ 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);
+ while (RTA_OK(attr, attrlen)) {
+ wpa_printf(MSG_DEBUG, "rta_type=%02x\n", attr->rta_type);
+ if (attr->rta_type == IFLA_WIRELESS) {
+ wpa_driver_ralink_event_wireless(
+ drv, ctx,
+ ((char *) attr) + rta_len,
+ attr->rta_len - rta_len);
+ }
+ attr = RTA_NEXT(attr, attrlen);
+ wpa_hexdump(MSG_DEBUG, "attr3: ",
+ (u8 *) attr, sizeof(struct rtattr));
+ }
+}
+
+static int
+ralink_get_we_version_compiled(struct wpa_driver_ralink_data *drv)
+{
+ struct iwreq iwr;
+ UINT we_version_compiled = 0;
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+ iwr.u.data.pointer = (caddr_t) &we_version_compiled;
+ iwr.u.data.flags = RT_OID_WE_VERSION_COMPILED;
+
+ if (ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr) < 0) {
+ wpa_printf(MSG_DEBUG, "%s: failed", __func__);
+ return -1;
+ }
+
+ drv->we_version_compiled = we_version_compiled;
+
+ return 0;
+}
+
+static void * wpa_driver_ralink_init(void *ctx, const char *ifname)
+{
+ int s;
+ struct wpa_driver_ralink_data *drv;
+ struct ifreq ifr;
+ UCHAR enable_wpa_supplicant = 0;
+ struct netlink_config *cfg;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ /* open socket to kernel */
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ perror("socket");
+ return NULL;
+ }
+ /* do it */
+ os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+
+ if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
+ perror(ifr.ifr_name);
+ return NULL;
+ }
+
+ drv = os_zalloc(sizeof(*drv));
+ if (drv == NULL)
+ return NULL;
+
+ drv->scanning_done = 1;
+ drv->ap_scan = 1; /* for now - let's assume ap_scan=1 is used */
+ drv->ctx = ctx;
+ os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+ drv->ioctl_sock = s;
+ drv->g_driver_down = 0;
+
+ cfg = os_zalloc(sizeof(*cfg));
+ if (cfg == NULL) {
+ close(drv->ioctl_sock);
+ os_free(drv);
+ return NULL;
+ }
+ 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;
+ }
+
+ drv->no_of_pmkid = 4; /* Number of PMKID saved supported */
+
+ linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1);
+ ralink_get_we_version_compiled(drv);
+ wpa_driver_ralink_flush_pmkid(drv);
+
+ 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_DEBUG, "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");
+ close(s);
+ close(drv->ioctl_sock);
+ os_free(drv);
+ return NULL;
+ }
+
+ if (drv->ap_scan == 1)
+ drv->scanning_done = 0;
+
+ return drv;
+}
+
+static void wpa_driver_ralink_deinit(void *priv)
+{
+ struct wpa_driver_ralink_data *drv = priv;
+ UCHAR enable_wpa_supplicant;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ enable_wpa_supplicant = 0;
+
+ if (drv->g_driver_down == 0) {
+ /* trigger driver disable wpa_supplicant support */
+ if (ralink_set_oid(drv, RT_OID_WPA_SUPPLICANT_SUPPORT,
+ (char *) &enable_wpa_supplicant,
+ sizeof(BOOLEAN)) < 0) {
+ wpa_printf(MSG_DEBUG, "RALINK: Failed to set "
+ "RT_OID_WPA_SUPPLICANT_SUPPORT(%d)",
+ (int) enable_wpa_supplicant);
+ }
+
+ wpa_driver_ralink_flush_pmkid(drv);
+
+ sleep(1);
+ /* linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 0); */
+ }
+
+ eloop_cancel_timeout(wpa_driver_ralink_scan_timeout, drv, drv->ctx);
+ netlink_deinit(drv->netlink);
+ close(drv->ioctl_sock);
+ os_free(drv);
+}
+
+static void wpa_driver_ralink_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_driver_ralink_data *drv = eloop_ctx;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
+ wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
+
+ drv->scanning_done = 1;
+
+}
+
+static int wpa_driver_ralink_scan(void *priv,
+ struct wpa_driver_scan_params *params)
+{
+ struct wpa_driver_ralink_data *drv = priv;
+ struct iwreq iwr;
+ int ret = 0;
+
+ if (drv->g_driver_down == 1)
+ return -1;
+
+ 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);
+ return -1;
+ }
+
+ /* 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);
+
+ if (ioctl(drv->ioctl_sock, SIOCSIWSCAN, &iwr) < 0) {
+ perror("ioctl[SIOCSIWSCAN]");
+ ret = -1;
+ }
+
+ /* Not all drivers generate "scan completed" wireless event, so try to
+ * read results after a timeout. */
+ eloop_cancel_timeout(wpa_driver_ralink_scan_timeout, drv, drv->ctx);
+ eloop_register_timeout(4, 0, wpa_driver_ralink_scan_timeout, drv,
+ drv->ctx);
+
+ drv->scanning_done = 0;
+
+ return ret;
+}
+
+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;
+ size_t ap_num;
+ u8 *pos;
+ struct wpa_scan_results *res;
+
+ if (drv->g_driver_down == 1)
+ return NULL;
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ if (drv->we_version_compiled >= 17)
+ buf_len = 8192;
+ else
+ buf_len = 4096;
+
+ for (;;) {
+ buf = os_zalloc(buf_len);
+ iwr.u.data.length = buf_len;
+ if (buf == NULL)
+ return NULL;
+
+ 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 (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 NULL;
+ }
+
+ 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) {
+ 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->IEs[0], wbi->IELength);
+
+ os_memcpy(r->bssid, wbi->MacAddress, ETH_ALEN);
+
+ extra_len += (2 + wbi->Ssid.SsidLength);
+ r->ie_len = extra_len + var_ie_len;
+ pos2 = (u8 *) (r + 1);
+
+ /*
+ * 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;
+
+ r->freq = (wbi->Configuration.DSConfig / 1000);
+
+ pos = (u8 *) wbi + sizeof(*wbi) - 1;
+
+ 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 res;
+}
+
+static int ralink_set_auth_mode(struct wpa_driver_ralink_data *drv,
+ NDIS_802_11_AUTHENTICATION_MODE mode)
+{
+ NDIS_802_11_AUTHENTICATION_MODE auth_mode = mode;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ if (ralink_set_oid(drv, OID_802_11_AUTHENTICATION_MODE,
+ (char *) &auth_mode, sizeof(auth_mode)) < 0) {
+ wpa_printf(MSG_DEBUG, "RALINK: Failed to set "
+ "OID_802_11_AUTHENTICATION_MODE (%d)",
+ (int) auth_mode);
+ return -1;
+ }
+ 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)
+{
+ NDIS_802_11_REMOVE_KEY rkey;
+ NDIS_802_11_KEY_INDEX _index;
+ int res, res2;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ os_memset(&rkey, 0, sizeof(rkey));
+
+ rkey.Length = sizeof(rkey);
+ rkey.KeyIndex = key_idx;
+
+ if (pairwise)
+ rkey.KeyIndex |= 1 << 30;
+
+ os_memcpy(rkey.BSSID, bssid, ETH_ALEN);
+
+ res = ralink_set_oid(drv, OID_802_11_REMOVE_KEY, (char *) &rkey,
+ sizeof(rkey));
+
+ /* AlbertY@20060210 removed it */
+ if (0 /* !pairwise */) {
+ res2 = ralink_set_oid(drv, OID_802_11_REMOVE_WEP,
+ (char *) &_index, sizeof(_index));
+ } else
+ res2 = 0;
+
+ if (res < 0 && res2 < 0)
+ return res;
+ return 0;
+}
+
+static int wpa_driver_ralink_add_wep(struct wpa_driver_ralink_data *drv,
+ int pairwise, int key_idx, int set_tx,
+ const u8 *key, size_t key_len)
+{
+ NDIS_802_11_WEP *wep;
+ size_t len;
+ int res;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ len = 12 + key_len;
+ wep = os_zalloc(len);
+ if (wep == NULL)
+ return -1;
+
+ wep->Length = len;
+ wep->KeyIndex = key_idx;
+
+ if (set_tx)
+ wep->KeyIndex |= 0x80000000;
+
+ wep->KeyLength = key_len;
+ os_memcpy(wep->KeyMaterial, key, key_len);
+
+ wpa_hexdump_key(MSG_MSGDUMP, "RALINK: OID_802_11_ADD_WEP",
+ (const u8 *) wep, len);
+ res = ralink_set_oid(drv, OID_802_11_ADD_WEP, (char *) wep, len);
+
+ os_free(wep);
+
+ return res;
+}
+
+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)
+{
+ struct wpa_driver_ralink_data *drv = priv;
+ size_t len, i;
+ NDIS_802_11_KEY *nkey;
+ int res, pairwise;
+ u8 bssid[ETH_ALEN];
+
+ if (drv->g_driver_down == 1)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ drv->bAddWepKey = FALSE;
+
+ if (addr == NULL || is_broadcast_ether_addr(addr)) {
+ /* Group Key */
+ pairwise = 0;
+ wpa_driver_ralink_get_bssid(drv, bssid);
+ } else {
+ /* Pairwise Key */
+ pairwise = 1;
+ os_memcpy(bssid, addr, ETH_ALEN);
+ }
+
+ if (alg == WPA_ALG_NONE || key_len == 0) {
+ return wpa_driver_ralink_remove_key(drv, key_idx, addr, bssid,
+ pairwise);
+ }
+
+ if (alg == WPA_ALG_WEP) {
+ drv->bAddWepKey = TRUE;
+ return wpa_driver_ralink_add_wep(drv, pairwise, key_idx,
+ set_tx, key, key_len);
+ }
+
+ len = 12 + 6 + 6 + 8 + key_len;
+
+ nkey = os_zalloc(len);
+ if (nkey == NULL)
+ return -1;
+
+ nkey->Length = len;
+ nkey->KeyIndex = key_idx;
+
+ if (set_tx)
+ nkey->KeyIndex |= 1 << 31;
+
+ if (pairwise)
+ nkey->KeyIndex |= 1 << 30;
+
+ if (seq && seq_len)
+ nkey->KeyIndex |= 1 << 29;
+
+ nkey->KeyLength = key_len;
+ os_memcpy(nkey->BSSID, bssid, ETH_ALEN);
+
+ if (seq && seq_len) {
+ for (i = 0; i < seq_len; i++)
+ nkey->KeyRSC |= seq[i] << (i * 8);
+ }
+ if (alg == WPA_ALG_TKIP && key_len == 32) {
+ os_memcpy(nkey->KeyMaterial, key, 16);
+ os_memcpy(nkey->KeyMaterial + 16, key + 24, 8);
+ os_memcpy(nkey->KeyMaterial + 24, key + 16, 8);
+ } else {
+ os_memcpy(nkey->KeyMaterial, key, key_len);
+ }
+
+ wpa_printf(MSG_DEBUG, "%s: alg=%d key_idx=%d set_tx=%d seq_len=%lu "
+ "key_len=%lu", __FUNCTION__, alg, key_idx, set_tx,
+ (unsigned long) seq_len, (unsigned long) key_len);
+
+ wpa_hexdump_key(MSG_MSGDUMP, "RALINK: OID_802_11_ADD_KEY",
+ (const u8 *) nkey, len);
+ res = ralink_set_oid(drv, OID_802_11_ADD_KEY, (char *) nkey, len);
+ os_free(nkey);
+
+ return res;
+}
+
+static int wpa_driver_ralink_disassociate(void *priv, const u8 *addr,
+ int reason_code)
+{
+ struct wpa_driver_ralink_data *drv = priv;
+
+ if (drv->g_driver_down == 1)
+ return -1;
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+ if (ralink_set_oid(drv, OID_802_11_DISASSOCIATE, " ", 4) < 0) {
+ wpa_printf(MSG_DEBUG, "RALINK: Failed to set "
+ "OID_802_11_DISASSOCIATE");
+ }
+
+ return 0;
+}
+
+static int wpa_driver_ralink_deauthenticate(void *priv, const u8 *addr,
+ int reason_code)
+{
+ struct wpa_driver_ralink_data *drv = priv;
+
+ wpa_printf(MSG_DEBUG, "g_driver_down = %d", drv->g_driver_down);
+
+ if (drv->g_driver_down == 1)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+ if (ralink_get_new_driver_flag(drv) == 0) {
+ return wpa_driver_ralink_disassociate(priv, addr, reason_code);
+ } else {
+ MLME_DEAUTH_REQ_STRUCT mlme;
+ os_memset(&mlme, 0, sizeof(MLME_DEAUTH_REQ_STRUCT));
+ mlme.Reason = reason_code;
+ os_memcpy(mlme.Addr, addr, MAC_ADDR_LEN);
+ return ralink_set_oid(drv, OID_802_11_DEAUTHENTICATION,
+ (char *) &mlme,
+ sizeof(MLME_DEAUTH_REQ_STRUCT));
+ }
+}
+
+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)
+{
+ struct wpa_driver_ralink_data *drv = priv;
+
+ NDIS_802_11_NETWORK_INFRASTRUCTURE mode;
+ 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;
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ if (params->mode == IEEE80211_MODE_IBSS)
+ mode = Ndis802_11IBSS;
+ else
+ mode = Ndis802_11Infrastructure;
+
+ if (ralink_set_oid(drv, OID_802_11_INFRASTRUCTURE_MODE,
+ (char *) &mode, sizeof(mode)) < 0) {
+ wpa_printf(MSG_DEBUG, "RALINK: Failed to set "
+ "OID_802_11_INFRASTRUCTURE_MODE (%d)",
+ (int) mode);
+ /* Try to continue anyway */
+ }
+
+ 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 {
+#ifdef CONFIG_WPS
+ UCHAR enable_wpa_supplicant;
+
+ if (drv->ap_scan == 1)
+ enable_wpa_supplicant = 0x01;
+ else
+ 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)
+ encr = Ndis802_11Encryption3Enabled;
+ else if (params->group_suite == CIPHER_TKIP)
+ encr = Ndis802_11Encryption2Enabled;
+ else
+ encr = Ndis802_11EncryptionDisabled;
+ break;
+ default:
+ encr = Ndis802_11EncryptionDisabled;
+ break;
+ }
+
+ ralink_set_auth_mode(drv, auth_mode);
+
+ /* 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_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 (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);
+ }
+
+ 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);
+ }
+ }
+ }
+
+ return wpa_driver_ralink_set_ssid(drv, params->ssid, params->ssid_len);
+}
+
+static int
+wpa_driver_ralink_set_countermeasures(void *priv, int enabled)
+{
+ struct wpa_driver_ralink_data *drv = priv;
+ if (drv->g_driver_down == 1)
+ return -1;
+ wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled);
+ return ralink_set_oid(drv, OID_SET_COUNTERMEASURES, (char *) &enabled,
+ sizeof(int));
+}
+
+const struct wpa_driver_ops wpa_driver_ralink_ops = {
+ .name = "ralink",
+ .desc = "Ralink Wireless Client driver",
+ .get_bssid = wpa_driver_ralink_get_bssid,
+ .get_ssid = wpa_driver_ralink_get_ssid,
+ .set_key = wpa_driver_ralink_set_key,
+ .init = wpa_driver_ralink_init,
+ .deinit = wpa_driver_ralink_deinit,
+ .set_countermeasures = wpa_driver_ralink_set_countermeasures,
+ .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,
+ .add_pmkid = wpa_driver_ralink_add_pmkid,
+ .remove_pmkid = wpa_driver_ralink_remove_pmkid,
+ .flush_pmkid = wpa_driver_ralink_flush_pmkid,
+};
diff --git a/src/drivers/driver_ralink.h b/src/drivers/driver_ralink.h
new file mode 100644
index 0000000..d13df28
--- /dev/null
+++ b/src/drivers/driver_ralink.h
@@ -0,0 +1,383 @@
+/*
+ * WPA Supplicant - driver_ralink exported functions
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007, Snowpin Lee <snowpin_lee@ralinktech.com.tw>
+ *
+ * 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.
+ */
+
+// Ralink defined OIDs
+#if WIRELESS_EXT <= 11
+#ifndef SIOCDEVPRIVATE
+#define SIOCDEVPRIVATE 0x8BE0
+#endif
+#define SIOCIWFIRSTPRIV SIOCDEVPRIVATE
+#endif
+
+#define RT_PRIV_IOCTL (SIOCIWFIRSTPRIV + 0x0E)
+#define RTPRIV_IOCTL_SET (SIOCIWFIRSTPRIV + 0x02)
+
+// IEEE 802.11 OIDs & Ralink defined OIDs ******
+
+// (RaConfig Set/QueryInform) ==>
+#define OID_GET_SET_TOGGLE 0x8000
+
+#define OID_802_11_ADD_WEP 0x0112
+#define OID_802_11_REMOVE_WEP 0x0113
+#define OID_802_11_DISASSOCIATE 0x0114
+#define OID_802_11_PRIVACY_FILTER 0x0118
+#define OID_802_11_ASSOCIATION_INFORMATION 0x011E
+#define OID_802_11_BSSID_LIST_SCAN 0x0508
+#define OID_802_11_SSID 0x0509
+#define OID_802_11_BSSID 0x050A
+#define OID_802_11_WEP_STATUS 0x0510
+#define OID_802_11_AUTHENTICATION_MODE 0x0511
+#define OID_802_11_INFRASTRUCTURE_MODE 0x0512
+#define OID_802_11_TX_POWER_LEVEL 0x0517
+#define OID_802_11_REMOVE_KEY 0x0519
+#define OID_802_11_ADD_KEY 0x0520
+#define OID_802_11_DEAUTHENTICATION 0x0526
+#define OID_802_11_DROP_UNENCRYPTED 0x0527
+#define OID_802_11_BSSID_LIST 0x0609
+#define OID_802_3_CURRENT_ADDRESS 0x060A
+#define OID_SET_COUNTERMEASURES 0x0616
+#define OID_802_11_SET_IEEE8021X 0x0617 // For IEEE8021x mode
+#define OID_802_11_SET_IEEE8021X_REQUIRE_KEY 0x0618 // For DynamicWEP in IEEE802.1x mode
+#define OID_802_11_PMKID 0x0620
+#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))
+
+//wpa_supplicant event flags
+#define RT_ASSOC_EVENT_FLAG 0x0101
+#define RT_DISASSOC_EVENT_FLAG 0x0102
+#define RT_REQIE_EVENT_FLAG 0x0103
+#define RT_RESPIE_EVENT_FLAG 0x0104
+#define RT_ASSOCINFO_EVENT_FLAG 0x0105
+#define RT_PMKIDCAND_FLAG 0x0106
+#define RT_INTERFACE_DOWN 0x0107
+#define RT_INTERFACE_UP 0x0108
+
+//
+// IEEE 802.11 Structures and definitions
+//
+// new types for Media Specific Indications
+
+#ifndef ULONG
+#define CHAR char
+#define INT int
+#define SHORT int
+#define UINT u32
+#undef ULONG
+//#define ULONG u32
+#define ULONG unsigned long /* 32-bit in 32-bit CPU or 64-bit in 64-bit CPU */
+#define USHORT unsigned short
+#define UCHAR unsigned char
+
+#define uint32 u32
+#define uint8 u8
+
+
+#define BOOLEAN u8
+//#define LARGE_INTEGER s64
+#define VOID void
+#define LONG long
+#define LONGLONG s64
+#define ULONGLONG u64
+typedef VOID *PVOID;
+typedef CHAR *PCHAR;
+typedef UCHAR *PUCHAR;
+typedef USHORT *PUSHORT;
+typedef LONG *PLONG;
+typedef ULONG *PULONG;
+
+typedef union _LARGE_INTEGER {
+ struct {
+ ULONG LowPart;
+ LONG HighPart;
+ }vv;
+ struct {
+ ULONG LowPart;
+ LONG HighPart;
+ } u;
+ s64 QuadPart;
+} LARGE_INTEGER;
+
+#endif
+
+#define NDIS_802_11_LENGTH_SSID 32
+#define NDIS_802_11_LENGTH_RATES 8
+#define NDIS_802_11_LENGTH_RATES_EX 16
+#define MAX_LEN_OF_SSID 32
+#define MAC_ADDR_LEN 6
+
+typedef UCHAR NDIS_802_11_MAC_ADDRESS[6];
+
+// mask for authentication/integrity fields
+#define NDIS_802_11_AUTH_REQUEST_AUTH_FIELDS 0x0f
+
+#define NDIS_802_11_AUTH_REQUEST_REAUTH 0x01
+#define NDIS_802_11_AUTH_REQUEST_KEYUPDATE 0x02
+#define NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR 0x06
+#define NDIS_802_11_AUTH_REQUEST_GROUP_ERROR 0x0E
+
+// Added new types for OFDM 5G and 2.4G
+typedef enum _NDIS_802_11_NETWORK_TYPE
+{
+ Ndis802_11FH,
+ Ndis802_11DS,
+ Ndis802_11OFDM5,
+ Ndis802_11OFDM24,
+ Ndis802_11Automode,
+ Ndis802_11NetworkTypeMax // not a real type, defined as an upper bound
+} NDIS_802_11_NETWORK_TYPE, *PNDIS_802_11_NETWORK_TYPE;
+
+//
+// Received Signal Strength Indication
+//
+typedef LONG NDIS_802_11_RSSI; // in dBm
+
+typedef struct _NDIS_802_11_CONFIGURATION_FH
+{
+ ULONG Length; // Length of structure
+ ULONG HopPattern; // As defined by 802.11, MSB set
+ ULONG HopSet; // to one if non-802.11
+ ULONG DwellTime; // units are Kusec
+} NDIS_802_11_CONFIGURATION_FH, *PNDIS_802_11_CONFIGURATION_FH;
+
+typedef struct _NDIS_802_11_CONFIGURATION
+{
+ ULONG Length; // Length of structure
+ ULONG BeaconPeriod; // units are Kusec
+ ULONG ATIMWindow; // units are Kusec
+ ULONG DSConfig; // Frequency, units are kHz
+ NDIS_802_11_CONFIGURATION_FH FHConfig;
+} NDIS_802_11_CONFIGURATION, *PNDIS_802_11_CONFIGURATION;
+
+typedef ULONG NDIS_802_11_KEY_INDEX;
+typedef ULONGLONG NDIS_802_11_KEY_RSC;
+
+// Key mapping keys require a BSSID
+typedef struct _NDIS_802_11_KEY
+{
+ UINT Length; // Length of this structure
+ UINT KeyIndex;
+ UINT KeyLength; // length of key in bytes
+ NDIS_802_11_MAC_ADDRESS BSSID;
+ NDIS_802_11_KEY_RSC KeyRSC;
+ UCHAR KeyMaterial[1]; // variable length depending on above field
+} NDIS_802_11_KEY, *PNDIS_802_11_KEY;
+
+typedef struct _NDIS_802_11_REMOVE_KEY
+{
+ UINT Length; // Length of this structure
+ UINT KeyIndex;
+ NDIS_802_11_MAC_ADDRESS BSSID;
+} NDIS_802_11_REMOVE_KEY, *PNDIS_802_11_REMOVE_KEY;
+
+typedef struct PACKED _NDIS_802_11_WEP
+{
+ UINT Length; // Length of this structure
+ UINT KeyIndex; // 0 is the per-client key, 1-N are the
+ // global keys
+ UINT KeyLength; // length of key in bytes
+ UCHAR KeyMaterial[1];// variable length depending on above field
+} NDIS_802_11_WEP, *PNDIS_802_11_WEP;
+
+
+typedef enum _NDIS_802_11_NETWORK_INFRASTRUCTURE
+{
+ Ndis802_11IBSS,
+ Ndis802_11Infrastructure,
+ Ndis802_11AutoUnknown,
+ Ndis802_11InfrastructureMax // Not a real value, defined as upper bound
+} NDIS_802_11_NETWORK_INFRASTRUCTURE, *PNDIS_802_11_NETWORK_INFRASTRUCTURE;
+
+// PMKID Structures
+typedef UCHAR NDIS_802_11_PMKID_VALUE[16];
+
+typedef struct _BSSID_INFO
+{
+ NDIS_802_11_MAC_ADDRESS BSSID;
+ NDIS_802_11_PMKID_VALUE PMKID;
+} BSSID_INFO, *PBSSID_INFO;
+
+typedef struct _NDIS_802_11_PMKID
+{
+ ULONG Length;
+ ULONG BSSIDInfoCount;
+ BSSID_INFO BSSIDInfo[1];
+} NDIS_802_11_PMKID, *PNDIS_802_11_PMKID;
+
+//Added new types for PMKID Candidate lists.
+typedef struct _PMKID_CANDIDATE {
+ NDIS_802_11_MAC_ADDRESS BSSID;
+ ULONG Flags;
+} PMKID_CANDIDATE, *PPMKID_CANDIDATE;
+
+typedef struct _NDIS_802_11_PMKID_CANDIDATE_LIST
+{
+ ULONG Version; // Version of the structure
+ ULONG NumCandidates; // No. of pmkid candidates
+ PMKID_CANDIDATE CandidateList[1];
+} NDIS_802_11_PMKID_CANDIDATE_LIST, *PNDIS_802_11_PMKID_CANDIDATE_LIST;
+
+//Flags for PMKID Candidate list structure
+#define NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED 0x01
+
+// Add new authentication modes
+typedef enum _NDIS_802_11_AUTHENTICATION_MODE
+{
+ Ndis802_11AuthModeOpen,
+ Ndis802_11AuthModeShared,
+ Ndis802_11AuthModeAutoSwitch,
+ Ndis802_11AuthModeWPA,
+ Ndis802_11AuthModeWPAPSK,
+ Ndis802_11AuthModeWPANone,
+ Ndis802_11AuthModeWPA2,
+ Ndis802_11AuthModeWPA2PSK,
+ Ndis802_11AuthModeMax // Not a real mode, defined as upper bound
+} NDIS_802_11_AUTHENTICATION_MODE, *PNDIS_802_11_AUTHENTICATION_MODE;
+
+typedef UCHAR NDIS_802_11_RATES[NDIS_802_11_LENGTH_RATES]; // Set of 8 data rates
+typedef UCHAR NDIS_802_11_RATES_EX[NDIS_802_11_LENGTH_RATES_EX]; // Set of 16 data rates
+
+typedef struct PACKED _NDIS_802_11_SSID
+{
+ INT SsidLength; // length of SSID field below, in bytes;
+ // this can be zero.
+ UCHAR Ssid[NDIS_802_11_LENGTH_SSID]; // SSID information field
+} NDIS_802_11_SSID, *PNDIS_802_11_SSID;
+
+
+typedef struct PACKED _NDIS_WLAN_BSSID
+{
+ ULONG Length; // Length of this structure
+ NDIS_802_11_MAC_ADDRESS MacAddress; // BSSID
+ UCHAR Reserved[2];
+ NDIS_802_11_SSID Ssid; // SSID
+ ULONG Privacy; // WEP encryption requirement
+ NDIS_802_11_RSSI Rssi; // receive signal
+ // strength in dBm
+ NDIS_802_11_NETWORK_TYPE NetworkTypeInUse;
+ NDIS_802_11_CONFIGURATION Configuration;
+ NDIS_802_11_NETWORK_INFRASTRUCTURE InfrastructureMode;
+ NDIS_802_11_RATES SupportedRates;
+} NDIS_WLAN_BSSID, *PNDIS_WLAN_BSSID;
+
+typedef struct PACKED _NDIS_802_11_BSSID_LIST
+{
+ UINT NumberOfItems; // in list below, at least 1
+ NDIS_WLAN_BSSID Bssid[1];
+} NDIS_802_11_BSSID_LIST, *PNDIS_802_11_BSSID_LIST;
+
+// Added Capabilities, IELength and IEs for each BSSID
+typedef struct PACKED _NDIS_WLAN_BSSID_EX
+{
+ ULONG Length; // Length of this structure
+ NDIS_802_11_MAC_ADDRESS MacAddress; // BSSID
+ UCHAR Reserved[2];
+ NDIS_802_11_SSID Ssid; // SSID
+ UINT Privacy; // WEP encryption requirement
+ NDIS_802_11_RSSI Rssi; // receive signal
+ // strength in dBm
+ NDIS_802_11_NETWORK_TYPE NetworkTypeInUse;
+ NDIS_802_11_CONFIGURATION Configuration;
+ NDIS_802_11_NETWORK_INFRASTRUCTURE InfrastructureMode;
+ NDIS_802_11_RATES_EX SupportedRates;
+ ULONG IELength;
+ UCHAR IEs[1];
+} NDIS_WLAN_BSSID_EX, *PNDIS_WLAN_BSSID_EX;
+
+typedef struct PACKED _NDIS_802_11_BSSID_LIST_EX
+{
+ UINT NumberOfItems; // in list below, at least 1
+ NDIS_WLAN_BSSID_EX Bssid[1];
+} NDIS_802_11_BSSID_LIST_EX, *PNDIS_802_11_BSSID_LIST_EX;
+
+typedef struct PACKED _NDIS_802_11_FIXED_IEs
+{
+ UCHAR Timestamp[8];
+ USHORT BeaconInterval;
+ USHORT Capabilities;
+} NDIS_802_11_FIXED_IEs, *PNDIS_802_11_FIXED_IEs;
+
+// Added new encryption types
+// Also aliased typedef to new name
+typedef enum _NDIS_802_11_WEP_STATUS
+{
+ Ndis802_11WEPEnabled,
+ Ndis802_11Encryption1Enabled = Ndis802_11WEPEnabled,
+ Ndis802_11WEPDisabled,
+ Ndis802_11EncryptionDisabled = Ndis802_11WEPDisabled,
+ Ndis802_11WEPKeyAbsent,
+ Ndis802_11Encryption1KeyAbsent = Ndis802_11WEPKeyAbsent,
+ Ndis802_11WEPNotSupported,
+ Ndis802_11EncryptionNotSupported = Ndis802_11WEPNotSupported,
+ Ndis802_11Encryption2Enabled,
+ Ndis802_11Encryption2KeyAbsent,
+ Ndis802_11Encryption3Enabled,
+ Ndis802_11Encryption3KeyAbsent
+} NDIS_802_11_WEP_STATUS, *PNDIS_802_11_WEP_STATUS,
+ NDIS_802_11_ENCRYPTION_STATUS, *PNDIS_802_11_ENCRYPTION_STATUS;
+
+typedef enum _NDIS_802_11_RELOAD_DEFAULTS
+{
+ Ndis802_11ReloadWEPKeys
+} NDIS_802_11_RELOAD_DEFAULTS, *PNDIS_802_11_RELOAD_DEFAULTS;
+
+#define NDIS_802_11_AI_REQFI_CAPABILITIES 1
+#define NDIS_802_11_AI_REQFI_LISTENINTERVAL 2
+#define NDIS_802_11_AI_REQFI_CURRENTAPADDRESS 4
+
+#define NDIS_802_11_AI_RESFI_CAPABILITIES 1
+#define NDIS_802_11_AI_RESFI_STATUSCODE 2
+#define NDIS_802_11_AI_RESFI_ASSOCIATIONID 4
+
+typedef struct _NDIS_802_11_AI_REQFI
+{
+ USHORT Capabilities;
+ USHORT ListenInterval;
+ NDIS_802_11_MAC_ADDRESS CurrentAPAddress;
+} NDIS_802_11_AI_REQFI, *PNDIS_802_11_AI_REQFI;
+
+typedef struct _NDIS_802_11_AI_RESFI
+{
+ USHORT Capabilities;
+ USHORT StatusCode;
+ USHORT AssociationId;
+} NDIS_802_11_AI_RESFI, *PNDIS_802_11_AI_RESFI;
+
+typedef struct _NDIS_802_11_ASSOCIATION_INFORMATION
+{
+ ULONG Length;
+ USHORT AvailableRequestFixedIEs;
+ NDIS_802_11_AI_REQFI RequestFixedIEs;
+ ULONG RequestIELength;
+ ULONG OffsetRequestIEs;
+ USHORT AvailableResponseFixedIEs;
+ NDIS_802_11_AI_RESFI ResponseFixedIEs;
+ ULONG ResponseIELength;
+ ULONG OffsetResponseIEs;
+} NDIS_802_11_ASSOCIATION_INFORMATION, *PNDIS_802_11_ASSOCIATION_INFORMATION;
+
+struct ndis_pmkid_entry {
+ struct ndis_pmkid_entry *next;
+ u8 bssid[ETH_ALEN];
+ u8 pmkid[16];
+};
+
+typedef struct _MLME_DEAUTH_REQ_STRUCT {
+ UCHAR Addr[MAC_ADDR_LEN];
+ USHORT Reason;
+} MLME_DEAUTH_REQ_STRUCT, *PMLME_DEAUTH_REQ_STRUCT;
diff --git a/src/drivers/driver_roboswitch.c b/src/drivers/driver_roboswitch.c
new file mode 100644
index 0000000..c014b96
--- /dev/null
+++ b/src/drivers/driver_roboswitch.c
@@ -0,0 +1,480 @@
+/*
+ * WPA Supplicant - roboswitch driver interface
+ * Copyright (c) 2008-2009 Jouke Witteveen
+ *
+ * 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 <linux/sockios.h>
+#include <linux/if_ether.h>
+#include <linux/mii.h>
+#include <net/if.h>
+
+#include "common.h"
+#include "driver.h"
+#include "l2_packet/l2_packet.h"
+
+#define ROBO_PHY_ADDR 0x1e /* RoboSwitch PHY address */
+
+/* MII access registers */
+#define ROBO_MII_PAGE 0x10 /* MII page register */
+#define ROBO_MII_ADDR 0x11 /* MII address register */
+#define ROBO_MII_DATA_OFFSET 0x18 /* Start of MII data registers */
+
+#define ROBO_MII_PAGE_ENABLE 0x01 /* MII page op code */
+#define ROBO_MII_ADDR_WRITE 0x01 /* MII address write op code */
+#define ROBO_MII_ADDR_READ 0x02 /* MII address read op code */
+#define ROBO_MII_DATA_MAX 4 /* Consecutive MII data registers */
+#define ROBO_MII_RETRY_MAX 10 /* Read attempts before giving up */
+
+/* Page numbers */
+#define ROBO_ARLCTRL_PAGE 0x04 /* ARL control page */
+#define ROBO_VLAN_PAGE 0x34 /* VLAN page */
+
+/* ARL control page registers */
+#define ROBO_ARLCTRL_CONF 0x00 /* ARL configuration register */
+#define ROBO_ARLCTRL_ADDR_1 0x10 /* Multiport address 1 */
+#define ROBO_ARLCTRL_VEC_1 0x16 /* Multiport vector 1 */
+#define ROBO_ARLCTRL_ADDR_2 0x20 /* Multiport address 2 */
+#define ROBO_ARLCTRL_VEC_2 0x26 /* Multiport vector 2 */
+
+/* VLAN page registers */
+#define ROBO_VLAN_ACCESS 0x08 /* VLAN table access register */
+#define ROBO_VLAN_ACCESS_5350 0x06 /* VLAN table access register (5350) */
+#define ROBO_VLAN_READ 0x0c /* VLAN read register */
+#define ROBO_VLAN_MAX 0xff /* Maximum number of VLANs */
+
+
+static const u8 pae_group_addr[ETH_ALEN] =
+{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
+
+
+struct wpa_driver_roboswitch_data {
+ void *ctx;
+ struct l2_packet_data *l2;
+ char ifname[IFNAMSIZ + 1];
+ u8 own_addr[ETH_ALEN];
+ struct ifreq ifr;
+ int fd, is_5350;
+ u16 ports;
+};
+
+
+/* Copied from the kernel-only part of mii.h. */
+static inline struct mii_ioctl_data *if_mii(struct ifreq *rq)
+{
+ return (struct mii_ioctl_data *) &rq->ifr_ifru;
+}
+
+
+/*
+ * RoboSwitch uses 16-bit Big Endian addresses.
+ * The ordering of the words is reversed in the MII registers.
+ */
+static void wpa_driver_roboswitch_addr_be16(const u8 addr[ETH_ALEN], u16 *be)
+{
+ int i;
+ for (i = 0; i < ETH_ALEN; i += 2)
+ be[(ETH_ALEN - i) / 2 - 1] = WPA_GET_BE16(addr + i);
+}
+
+
+static u16 wpa_driver_roboswitch_mdio_read(
+ struct wpa_driver_roboswitch_data *drv, u8 reg)
+{
+ struct mii_ioctl_data *mii = if_mii(&drv->ifr);
+
+ mii->phy_id = ROBO_PHY_ADDR;
+ mii->reg_num = reg;
+
+ if (ioctl(drv->fd, SIOCGMIIREG, &drv->ifr) < 0) {
+ perror("ioctl[SIOCGMIIREG]");
+ return 0x00;
+ }
+ return mii->val_out;
+}
+
+
+static void wpa_driver_roboswitch_mdio_write(
+ struct wpa_driver_roboswitch_data *drv, u8 reg, u16 val)
+{
+ struct mii_ioctl_data *mii = if_mii(&drv->ifr);
+
+ mii->phy_id = ROBO_PHY_ADDR;
+ mii->reg_num = reg;
+ mii->val_in = val;
+
+ if (ioctl(drv->fd, SIOCSMIIREG, &drv->ifr) < 0) {
+ perror("ioctl[SIOCSMIIREG");
+ }
+}
+
+
+static int wpa_driver_roboswitch_reg(struct wpa_driver_roboswitch_data *drv,
+ u8 page, u8 reg, u8 op)
+{
+ int i;
+
+ /* set page number */
+ wpa_driver_roboswitch_mdio_write(drv, ROBO_MII_PAGE,
+ (page << 8) | ROBO_MII_PAGE_ENABLE);
+ /* set register address */
+ wpa_driver_roboswitch_mdio_write(drv, ROBO_MII_ADDR, (reg << 8) | op);
+
+ /* check if operation completed */
+ for (i = 0; i < ROBO_MII_RETRY_MAX; ++i) {
+ if ((wpa_driver_roboswitch_mdio_read(drv, ROBO_MII_ADDR) & 3)
+ == 0)
+ return 0;
+ }
+ /* timeout */
+ return -1;
+}
+
+
+static int wpa_driver_roboswitch_read(struct wpa_driver_roboswitch_data *drv,
+ u8 page, u8 reg, u16 *val, int len)
+{
+ int i;
+
+ if (len > ROBO_MII_DATA_MAX ||
+ wpa_driver_roboswitch_reg(drv, page, reg, ROBO_MII_ADDR_READ) < 0)
+ return -1;
+
+ for (i = 0; i < len; ++i) {
+ val[i] = wpa_driver_roboswitch_mdio_read(
+ drv, ROBO_MII_DATA_OFFSET + i);
+ }
+
+ return 0;
+}
+
+
+static int wpa_driver_roboswitch_write(struct wpa_driver_roboswitch_data *drv,
+ u8 page, u8 reg, u16 *val, int len)
+{
+ int i;
+
+ if (len > ROBO_MII_DATA_MAX) return -1;
+ for (i = 0; i < len; ++i) {
+ wpa_driver_roboswitch_mdio_write(drv, ROBO_MII_DATA_OFFSET + i,
+ val[i]);
+ }
+ return wpa_driver_roboswitch_reg(drv, page, reg, ROBO_MII_ADDR_WRITE);
+}
+
+
+static void wpa_driver_roboswitch_receive(void *priv, const u8 *src_addr,
+ const u8 *buf, size_t len)
+{
+ 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)
+ drv_event_eapol_rx(drv->ctx, src_addr, buf + 14, len - 14);
+}
+
+
+static int wpa_driver_roboswitch_get_ssid(void *priv, u8 *ssid)
+{
+ ssid[0] = 0;
+ return 0;
+}
+
+
+static int wpa_driver_roboswitch_get_bssid(void *priv, u8 *bssid)
+{
+ /* Report PAE group address as the "BSSID" for wired connection. */
+ os_memcpy(bssid, pae_group_addr, ETH_ALEN);
+ return 0;
+}
+
+
+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;
+ char *sep;
+
+ if (param == NULL || os_strstr(param, "multicast_only=1") == NULL) {
+ sep = drv->ifname + os_strlen(drv->ifname);
+ *sep = '.';
+ drv->l2 = l2_packet_init(drv->ifname, NULL, ETH_P_ALL,
+ wpa_driver_roboswitch_receive, drv,
+ 1);
+ if (drv->l2 == NULL) {
+ wpa_printf(MSG_INFO, "%s: Unable to listen on %s",
+ __func__, drv->ifname);
+ return -1;
+ }
+ *sep = '\0';
+ l2_packet_get_own_addr(drv->l2, drv->own_addr);
+ } else {
+ wpa_printf(MSG_DEBUG, "%s: Ignoring unicast frames", __func__);
+ drv->l2 = NULL;
+ }
+ return 0;
+}
+
+
+static const char * wpa_driver_roboswitch_get_ifname(void *priv)
+{
+ struct wpa_driver_roboswitch_data *drv = priv;
+ return drv->ifname;
+}
+
+
+static int wpa_driver_roboswitch_join(struct wpa_driver_roboswitch_data *drv,
+ u16 ports, const u8 *addr)
+{
+ u16 read1[3], read2[3], addr_be16[3];
+
+ wpa_driver_roboswitch_addr_be16(addr, addr_be16);
+
+ if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_CONF, read1, 1) < 0)
+ return -1;
+ if (!(read1[0] & (1 << 4))) {
+ /* multiport addresses are not yet enabled */
+ read1[0] |= 1 << 4;
+ wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_ADDR_1, addr_be16, 3);
+ wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_VEC_1, &ports, 1);
+ wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_ADDR_2, addr_be16, 3);
+ wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_VEC_2, &ports, 1);
+ wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_CONF, read1, 1);
+ } else {
+ /* if both multiport addresses are the same we can add */
+ wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_ADDR_1, read1, 3);
+ wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_ADDR_2, read2, 3);
+ if (os_memcmp(read1, read2, 6) != 0)
+ return -1;
+ wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_VEC_1, read1, 1);
+ wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_VEC_2, read2, 1);
+ if (read1[0] != read2[0])
+ return -1;
+ wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_ADDR_1, addr_be16, 3);
+ wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_VEC_1, &ports, 1);
+ }
+ return 0;
+}
+
+
+static int wpa_driver_roboswitch_leave(struct wpa_driver_roboswitch_data *drv,
+ u16 ports, const u8 *addr)
+{
+ u16 _read, addr_be16[3], addr_read[3], ports_read;
+
+ wpa_driver_roboswitch_addr_be16(addr, addr_be16);
+
+ wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_CONF,
+ &_read, 1);
+ /* If ARL control is disabled, there is nothing to leave. */
+ if (!(_read & (1 << 4))) return -1;
+
+ wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_ADDR_1, addr_read, 3);
+ wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_VEC_1,
+ &ports_read, 1);
+ /* check if we occupy multiport address 1 */
+ if (os_memcmp(addr_read, addr_be16, 6) == 0 && ports_read == ports) {
+ wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_ADDR_2, addr_read, 3);
+ wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_VEC_2, &ports_read, 1);
+ /* and multiport address 2 */
+ if (os_memcmp(addr_read, addr_be16, 6) == 0 &&
+ ports_read == ports) {
+ _read &= ~(1 << 4);
+ wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_CONF, &_read,
+ 1);
+ } else {
+ wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_ADDR_1,
+ addr_read, 3);
+ wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_VEC_1,
+ &ports_read, 1);
+ wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_ADDR_2,
+ addr_read, 3);
+ wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_VEC_2,
+ &ports_read, 1);
+ }
+ } else {
+ wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_ADDR_2, addr_read, 3);
+ wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_VEC_2, &ports_read, 1);
+ /* or multiport address 2 */
+ if (os_memcmp(addr_read, addr_be16, 6) == 0 &&
+ ports_read == ports) {
+ wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_ADDR_1,
+ addr_read, 3);
+ wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_VEC_1,
+ &ports_read, 1);
+ } else return -1;
+ }
+ return 0;
+}
+
+
+static void * wpa_driver_roboswitch_init(void *ctx, const char *ifname)
+{
+ struct wpa_driver_roboswitch_data *drv;
+ char *sep;
+ u16 vlan = 0, _read[2];
+
+ drv = os_zalloc(sizeof(*drv));
+ if (drv == NULL) return NULL;
+ drv->ctx = ctx;
+ drv->own_addr[0] = '\0';
+
+ /* copy ifname and take a pointer to the second to last character */
+ sep = drv->ifname +
+ os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)) - 2;
+ /* find the '.' seperating <interface> and <vlan> */
+ while (sep > drv->ifname && *sep != '.') sep--;
+ if (sep <= drv->ifname) {
+ wpa_printf(MSG_INFO, "%s: No <interface>.<vlan> pair in "
+ "interface name %s", __func__, drv->ifname);
+ os_free(drv);
+ return NULL;
+ }
+ *sep = '\0';
+ while (*++sep) {
+ if (*sep < '0' || *sep > '9') {
+ wpa_printf(MSG_INFO, "%s: Invalid vlan specification "
+ "in interface name %s", __func__, ifname);
+ os_free(drv);
+ return NULL;
+ }
+ vlan *= 10;
+ vlan += *sep - '0';
+ if (vlan > ROBO_VLAN_MAX) {
+ wpa_printf(MSG_INFO, "%s: VLAN out of range in "
+ "interface name %s", __func__, ifname);
+ os_free(drv);
+ return NULL;
+ }
+ }
+
+ drv->fd = socket(PF_INET, SOCK_DGRAM, 0);
+ if (drv->fd < 0) {
+ wpa_printf(MSG_INFO, "%s: Unable to create socket", __func__);
+ os_free(drv);
+ return NULL;
+ }
+
+ os_memset(&drv->ifr, 0, sizeof(drv->ifr));
+ os_strlcpy(drv->ifr.ifr_name, drv->ifname, IFNAMSIZ);
+ if (ioctl(drv->fd, SIOCGMIIPHY, &drv->ifr) < 0) {
+ perror("ioctl[SIOCGMIIPHY]");
+ os_free(drv);
+ return NULL;
+ }
+ if (if_mii(&drv->ifr)->phy_id != ROBO_PHY_ADDR) {
+ wpa_printf(MSG_INFO, "%s: Invalid phy address (not a "
+ "RoboSwitch?)", __func__);
+ os_free(drv);
+ return NULL;
+ }
+
+ /* set and read back to see if the register can be used */
+ _read[0] = ROBO_VLAN_MAX;
+ wpa_driver_roboswitch_write(drv, ROBO_VLAN_PAGE, ROBO_VLAN_ACCESS_5350,
+ _read, 1);
+ wpa_driver_roboswitch_read(drv, ROBO_VLAN_PAGE, ROBO_VLAN_ACCESS_5350,
+ _read + 1, 1);
+ drv->is_5350 = _read[0] == _read[1];
+
+ /* set the read bit */
+ vlan |= 1 << 13;
+ wpa_driver_roboswitch_write(drv, ROBO_VLAN_PAGE,
+ drv->is_5350 ? ROBO_VLAN_ACCESS_5350
+ : ROBO_VLAN_ACCESS,
+ &vlan, 1);
+ wpa_driver_roboswitch_read(drv, ROBO_VLAN_PAGE, ROBO_VLAN_READ, _read,
+ drv->is_5350 ? 2 : 1);
+ if (!(drv->is_5350 ? _read[1] & (1 << 4) : _read[0] & (1 << 14))) {
+ wpa_printf(MSG_INFO, "%s: Could not get port information for "
+ "VLAN %d", __func__, vlan & ~(1 << 13));
+ os_free(drv);
+ return NULL;
+ }
+ drv->ports = _read[0] & 0x001F;
+ /* add the MII port */
+ drv->ports |= 1 << 8;
+ if (wpa_driver_roboswitch_join(drv, drv->ports, pae_group_addr) < 0) {
+ wpa_printf(MSG_INFO, "%s: Unable to join PAE group", __func__);
+ os_free(drv);
+ return NULL;
+ } else {
+ wpa_printf(MSG_DEBUG, "%s: Added PAE group address to "
+ "RoboSwitch ARL", __func__);
+ }
+
+ return drv;
+}
+
+
+static void wpa_driver_roboswitch_deinit(void *priv)
+{
+ struct wpa_driver_roboswitch_data *drv = priv;
+
+ if (drv->l2) {
+ l2_packet_deinit(drv->l2);
+ drv->l2 = NULL;
+ }
+ if (wpa_driver_roboswitch_leave(drv, drv->ports, pae_group_addr) < 0) {
+ wpa_printf(MSG_DEBUG, "%s: Unable to leave PAE group",
+ __func__);
+ }
+
+ close(drv->fd);
+ os_free(drv);
+}
+
+
+const struct wpa_driver_ops wpa_driver_roboswitch_ops = {
+ .name = "roboswitch",
+ .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,
+ .get_ifname = wpa_driver_roboswitch_get_ifname,
+};
diff --git a/src/drivers/driver_test.c b/src/drivers/driver_test.c
new file mode 100644
index 0000000..6bfa46d
--- /dev/null
+++ b/src/drivers/driver_test.c
@@ -0,0 +1,3391 @@
+/*
+ * 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
+ * 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.
+ */
+
+/* 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 "utils/includes.h"
+
+#ifndef CONFIG_NATIVE_WINDOWS
+#include <sys/un.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#define DRIVER_TEST_UNIX
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+#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 "p2p/p2p.h"
+#include "wps/wps.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 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
+ struct sockaddr_un hostapd_addr;
+#endif /* DRIVER_TEST_UNIX */
+ int hostapd_addr_set;
+ struct sockaddr_in hostapd_addr_udp;
+ int hostapd_addr_udp_set;
+ char *own_socket_path;
+ char *test_dir;
+#define MAX_SCAN_RESULTS 30
+ struct wpa_scan_res *scanres[MAX_SCAN_RESULTS];
+ size_t num_scanres;
+ int use_associnfo;
+ u8 assoc_wpa_ie[80];
+ size_t assoc_wpa_ie_len;
+ int use_mlme;
+ 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;
+
+ struct p2p_data *p2p;
+ unsigned int off_channel_freq;
+ struct wpabuf *pending_action_tx;
+ u8 pending_action_src[ETH_ALEN];
+ u8 pending_action_dst[ETH_ALEN];
+ u8 pending_action_bssid[ETH_ALEN];
+ unsigned int pending_action_freq;
+ unsigned int pending_listen_freq;
+ unsigned int pending_listen_duration;
+ int pending_p2p_scan;
+ struct sockaddr *probe_from;
+ socklen_t probe_from_len;
+};
+
+
+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 int wpa_driver_test_init_p2p(struct wpa_driver_test_data *drv);
+
+
+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, u32 flags)
+{
+ 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);
+
+#ifdef CONFIG_P2P
+ if (drv->p2p &&
+ WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+ WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION) {
+ if (drv->pending_action_tx == NULL) {
+ wpa_printf(MSG_DEBUG, "P2P: Ignore Action TX status - "
+ "no pending operation");
+ return ret;
+ }
+
+ if (os_memcmp(hdr->addr1, drv->pending_action_dst, ETH_ALEN) !=
+ 0) {
+ wpa_printf(MSG_DEBUG, "P2P: Ignore Action TX status - "
+ "unknown destination address");
+ return ret;
+ }
+
+ wpabuf_free(drv->pending_action_tx);
+ drv->pending_action_tx = NULL;
+
+ p2p_send_action_cb(drv->p2p, drv->pending_action_freq,
+ drv->pending_action_dst,
+ drv->pending_action_src,
+ drv->pending_action_bssid,
+ ret >= 0);
+ }
+#endif /* CONFIG_P2P */
+
+ 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);
+#ifdef CONFIG_P2P
+ if (drv->p2p)
+ p2p_probe_req_rx(drv->p2p, sa, ie, ielen);
+#endif /* CONFIG_P2P */
+ }
+
+ 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, 0);
+}
+
+
+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,
+ const struct wpabuf *assocresp)
+{
+ 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,
+ const char *bridge)
+{
+ 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 || type == WPA_IF_P2P_GO ||
+ type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP)
+ 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 || type == WPA_IF_P2P_GO ||
+ type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP)
+ 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);
+ if (len < 0)
+ return -1;
+ wpa_hexdump_ascii(MSG_DEBUG, "test_driver_set_ssid: SSID", buf, len);
+
+ if ((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;
+
+#ifdef DRIVER_TEST_UNIX
+ if (drv->associated && drv->hostapd_addr_set) {
+ struct stat st;
+ if (stat(drv->hostapd_addr.sun_path, &st) < 0) {
+ wpa_printf(MSG_DEBUG, "%s: lost connection to AP: %s",
+ __func__, strerror(errno));
+ drv->associated = 0;
+ wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
+ }
+ }
+#endif /* DRIVER_TEST_UNIX */
+
+ eloop_register_timeout(1, 0, wpa_driver_test_poll, drv, NULL);
+}
+
+
+static void wpa_driver_test_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_driver_test_data *drv = eloop_ctx;
+ wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
+ if (drv->pending_p2p_scan && drv->p2p) {
+#ifdef CONFIG_P2P
+ size_t i;
+ for (i = 0; i < drv->num_scanres; i++) {
+ struct wpa_scan_res *bss = drv->scanres[i];
+ if (p2p_scan_res_handler(drv->p2p, bss->bssid,
+ bss->freq, bss->level,
+ (const u8 *) (bss + 1),
+ bss->ie_len) > 0)
+ return;
+ }
+ p2p_scan_res_handled(drv->p2p);
+#endif /* CONFIG_P2P */
+ return;
+ }
+ wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
+}
+
+
+#ifdef DRIVER_TEST_UNIX
+static void wpa_driver_scan_dir(struct wpa_driver_test_data *drv,
+ const char *path)
+{
+ struct dirent *dent;
+ DIR *dir;
+ struct sockaddr_un addr;
+ char cmd[512], *pos, *end;
+ int ret;
+
+ dir = opendir(path);
+ if (dir == NULL)
+ return;
+
+ end = cmd + sizeof(cmd);
+ pos = cmd;
+ ret = os_snprintf(pos, end - pos, "SCAN " MACSTR,
+ MAC2STR(drv->own_addr));
+ if (ret >= 0 && ret < end - pos)
+ pos += ret;
+ if (drv->probe_req_ie) {
+ ret = os_snprintf(pos, end - pos, " ");
+ if (ret >= 0 && ret < end - pos)
+ pos += ret;
+ 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 &&
+ 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));
+ addr.sun_family = AF_UNIX;
+ os_snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s",
+ path, dent->d_name);
+
+ if (sendto(drv->test_socket, cmd, os_strlen(cmd), 0,
+ (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("sendto(test_socket)");
+ }
+ }
+ closedir(dir);
+}
+#endif /* DRIVER_TEST_UNIX */
+
+
+static int wpa_driver_test_scan(void *priv,
+ struct wpa_driver_scan_params *params)
+{
+ 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
+ if (drv->test_socket >= 0 && drv->test_dir)
+ wpa_driver_scan_dir(drv, drv->test_dir);
+
+ if (drv->test_socket >= 0 && drv->hostapd_addr_set &&
+ sendto(drv->test_socket, "SCAN", 4, 0,
+ (struct sockaddr *) &drv->hostapd_addr,
+ sizeof(drv->hostapd_addr)) < 0) {
+ perror("sendto(test_socket)");
+ }
+#endif /* DRIVER_TEST_UNIX */
+
+ if (drv->test_socket >= 0 && drv->hostapd_addr_udp_set &&
+ sendto(drv->test_socket, "SCAN", 4, 0,
+ (struct sockaddr *) &drv->hostapd_addr_udp,
+ sizeof(drv->hostapd_addr_udp)) < 0) {
+ perror("sendto(test_socket)");
+ }
+
+ eloop_cancel_timeout(wpa_driver_test_scan_timeout, drv, drv->ctx);
+ eloop_register_timeout(1, 0, wpa_driver_test_scan_timeout, drv,
+ drv->ctx);
+ return 0;
+}
+
+
+static struct wpa_scan_results * wpa_driver_test_get_scan_results2(void *priv)
+{
+ struct test_driver_bss *dbss = priv;
+ struct wpa_driver_test_data *drv = dbss->drv;
+ struct wpa_scan_results *res;
+ size_t i;
+
+ res = os_zalloc(sizeof(*res));
+ if (res == NULL)
+ return NULL;
+
+ res->res = os_zalloc(drv->num_scanres * sizeof(struct wpa_scan_res *));
+ if (res->res == NULL) {
+ os_free(res);
+ return NULL;
+ }
+
+ for (i = 0; i < drv->num_scanres; i++) {
+ struct wpa_scan_res *r;
+ if (drv->scanres[i] == NULL)
+ continue;
+ r = os_malloc(sizeof(*r) + drv->scanres[i]->ie_len);
+ if (r == NULL)
+ break;
+ os_memcpy(r, drv->scanres[i],
+ sizeof(*r) + drv->scanres[i]->ie_len);
+ res->res[res->num++] = r;
+ }
+
+ return res;
+}
+
+
+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: 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)
+ 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;
+ }
+
+ return 0;
+}
+
+
+static int wpa_driver_test_associate(
+ void *priv, struct wpa_driver_associate_params *params)
+{
+ 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,
+ params->group_suite, params->key_mgmt_suite,
+ params->auth_alg, params->mode);
+ wpa_driver_update_mode(drv, params->mode == IEEE80211_MODE_AP);
+ if (params->bssid) {
+ wpa_printf(MSG_DEBUG, " bssid=" MACSTR,
+ MAC2STR(params->bssid));
+ }
+ if (params->ssid) {
+ wpa_hexdump_ascii(MSG_DEBUG, " ssid",
+ params->ssid, params->ssid_len);
+ }
+ if (params->wpa_ie) {
+ wpa_hexdump(MSG_DEBUG, " wpa_ie",
+ params->wpa_ie, params->wpa_ie_len);
+ drv->assoc_wpa_ie_len = params->wpa_ie_len;
+ if (drv->assoc_wpa_ie_len > sizeof(drv->assoc_wpa_ie))
+ drv->assoc_wpa_ie_len = sizeof(drv->assoc_wpa_ie);
+ os_memcpy(drv->assoc_wpa_ie, params->wpa_ie,
+ drv->assoc_wpa_ie_len);
+ } 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 &&
+ 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,
+ sizeof(drv->hostapd_addr.sun_path),
+ "%s/AP-" MACSTR,
+ drv->test_dir, MAC2STR(params->bssid));
+ drv->hostapd_addr_set = 1;
+ }
+#endif /* DRIVER_TEST_UNIX */
+
+ 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);
+ pos = cmd;
+ ret = os_snprintf(pos, end - pos, "ASSOC " MACSTR " ",
+ MAC2STR(drv->own_addr));
+ if (ret >= 0 && ret < end - pos)
+ pos += ret;
+ pos += wpa_snprintf_hex(pos, end - pos, params->ssid,
+ params->ssid_len);
+ ret = os_snprintf(pos, end - pos, " ");
+ if (ret >= 0 && ret < end - pos)
+ pos += ret;
+ pos += wpa_snprintf_hex(pos, end - pos, params->wpa_ie,
+ params->wpa_ie_len);
+ end[-1] = '\0';
+#ifdef DRIVER_TEST_UNIX
+ if (drv->hostapd_addr_set &&
+ sendto(drv->test_socket, cmd, os_strlen(cmd), 0,
+ (struct sockaddr *) &drv->hostapd_addr,
+ sizeof(drv->hostapd_addr)) < 0) {
+ perror("sendto(test_socket)");
+ return -1;
+ }
+#endif /* DRIVER_TEST_UNIX */
+ if (drv->hostapd_addr_udp_set &&
+ sendto(drv->test_socket, cmd, os_strlen(cmd), 0,
+ (struct sockaddr *) &drv->hostapd_addr_udp,
+ sizeof(drv->hostapd_addr_udp)) < 0) {
+ perror("sendto(test_socket)");
+ return -1;
+ }
+
+ 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);
+ }
+
+ return 0;
+}
+
+
+static int wpa_driver_test_get_bssid(void *priv, u8 *bssid)
+{
+ 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 test_driver_bss *dbss = priv;
+ os_memcpy(ssid, dbss->ssid, 32);
+ return dbss->ssid_len;
+}
+
+
+static int wpa_driver_test_send_disassoc(struct wpa_driver_test_data *drv)
+{
+#ifdef DRIVER_TEST_UNIX
+ if (drv->test_socket >= 0 &&
+ sendto(drv->test_socket, "DISASSOC", 8, 0,
+ (struct sockaddr *) &drv->hostapd_addr,
+ sizeof(drv->hostapd_addr)) < 0) {
+ perror("sendto(test_socket)");
+ return -1;
+ }
+#endif /* DRIVER_TEST_UNIX */
+ if (drv->test_socket >= 0 && drv->hostapd_addr_udp_set &&
+ sendto(drv->test_socket, "DISASSOC", 8, 0,
+ (struct sockaddr *) &drv->hostapd_addr_udp,
+ sizeof(drv->hostapd_addr_udp)) < 0) {
+ perror("sendto(test_socket)");
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wpa_driver_test_deauthenticate(void *priv, const u8 *addr,
+ int reason_code)
+{
+ 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(dbss->bssid, 0, ETH_ALEN);
+ drv->associated = 0;
+ wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
+ return wpa_driver_test_send_disassoc(drv);
+}
+
+
+static int wpa_driver_test_disassociate(void *priv, const u8 *addr,
+ int reason_code)
+{
+ 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(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,
+ const char *data)
+{
+ struct wpa_scan_res *res;
+ const char *pos, *pos2;
+ 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) {
+ wpa_printf(MSG_DEBUG, "test_driver: No room for the new scan "
+ "result");
+ return;
+ }
+
+ /* SCANRESP BSSID SSID IEs */
+
+ res = os_zalloc(sizeof(*res) + MAX_IE_LEN);
+ if (res == NULL)
+ return;
+ ie_start = ie_pos = (u8 *) (res + 1);
+ ie_end = ie_pos + MAX_IE_LEN;
+
+ if (hwaddr_aton(data, res->bssid)) {
+ wpa_printf(MSG_DEBUG, "test_driver: invalid BSSID in scanres");
+ os_free(res);
+ return;
+ }
+
+ pos = data + 17;
+ while (*pos == ' ')
+ pos++;
+ pos2 = os_strchr(pos, ' ');
+ if (pos2 == NULL) {
+ wpa_printf(MSG_DEBUG, "test_driver: invalid SSID termination "
+ "in scanres");
+ os_free(res);
+ return;
+ }
+ len = (pos2 - pos) / 2;
+ if (len > 32)
+ len = 32;
+ /*
+ * Generate SSID IE from the SSID field since this IE is not included
+ * in the main IE field.
+ */
+ *ie_pos++ = WLAN_EID_SSID;
+ *ie_pos++ = len;
+ if (hexstr2bin(pos, ie_pos, len) < 0) {
+ wpa_printf(MSG_DEBUG, "test_driver: invalid SSID in scanres");
+ os_free(res);
+ return;
+ }
+ ie_pos += len;
+
+ pos = pos2 + 1;
+ pos2 = os_strchr(pos, ' ');
+ if (pos2 == NULL)
+ len = os_strlen(pos) / 2;
+ else
+ len = (pos2 - pos) / 2;
+ if ((int) len > ie_end - ie_pos)
+ len = ie_end - ie_pos;
+ if (hexstr2bin(pos, ie_pos, len) < 0) {
+ wpa_printf(MSG_DEBUG, "test_driver: invalid IEs in scanres");
+ os_free(res);
+ return;
+ }
+ ie_pos += len;
+ res->ie_len = ie_pos - ie_start;
+
+ if (pos2) {
+ pos = pos2 + 1;
+ while (*pos == ' ')
+ pos++;
+ 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]);
+ drv->scanres[drv->num_scanres++] = res;
+}
+
+
+static void wpa_driver_test_assocresp(struct wpa_driver_test_data *drv,
+ struct sockaddr *from,
+ 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, bss->bssid)) {
+ wpa_printf(MSG_DEBUG, "test_driver: invalid BSSID in "
+ "assocresp");
+ }
+ if (drv->use_associnfo) {
+ union wpa_event_data event;
+ os_memset(&event, 0, sizeof(event));
+ event.assoc_info.req_ies = drv->assoc_wpa_ie;
+ event.assoc_info.req_ies_len = drv->assoc_wpa_ie_len;
+ wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &event);
+ }
+ drv->associated = 1;
+ wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL);
+}
+
+
+static void wpa_driver_test_disassoc(struct wpa_driver_test_data *drv,
+ struct sockaddr *from,
+ socklen_t fromlen)
+{
+ drv->associated = 0;
+ wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
+}
+
+
+static void wpa_driver_test_eapol(struct wpa_driver_test_data *drv,
+ struct sockaddr *from,
+ socklen_t fromlen,
+ const u8 *data, size_t data_len)
+{
+ 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;
+ } else
+ src = bss->bssid;
+
+ drv_event_eapol_rx(drv->ctx, src, data, data_len);
+}
+
+
+static void wpa_driver_test_mlme(struct wpa_driver_test_data *drv,
+ struct sockaddr *from,
+ socklen_t fromlen,
+ const u8 *data, size_t data_len)
+{
+ int freq = 0, own_freq;
+ union wpa_event_data event;
+ const struct ieee80211_mgmt *mgmt;
+ u16 fc;
+ 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);
+
+ mgmt = (const struct ieee80211_mgmt *) data;
+ fc = le_to_host16(mgmt->frame_control);
+
+ if (drv->probe_req_report && data_len >= 24) {
+ 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);
+#ifdef CONFIG_P2P
+ if (drv->p2p)
+ p2p_probe_req_rx(drv->p2p, mgmt->sa,
+ event.rx_probe_req.ie,
+ event.rx_probe_req.ie_len);
+#endif /* CONFIG_P2P */
+ }
+ }
+
+#ifdef CONFIG_P2P
+ if (drv->p2p &&
+ WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+ WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION) {
+ size_t hdr_len;
+ hdr_len = (const u8 *)
+ &mgmt->u.action.u.vs_public_action.action - data;
+ p2p_rx_action(drv->p2p, mgmt->da, mgmt->sa, mgmt->bssid,
+ mgmt->u.action.category,
+ &mgmt->u.action.u.vs_public_action.action,
+ data_len - hdr_len, freq);
+ }
+#endif /* CONFIG_P2P */
+
+}
+
+
+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) ] */
+#ifdef CONFIG_P2P
+ if (drv->probe_req_report && drv->p2p && data_len) {
+ const char *d = (const char *) data;
+ u8 sa[ETH_ALEN];
+ u8 ie[512];
+ size_t ielen;
+
+ if (hwaddr_aton(d, sa))
+ return;
+ d += 18;
+ while (*d == ' ')
+ d++;
+ ielen = os_strlen(d) / 2;
+ if (ielen > sizeof(ie))
+ ielen = sizeof(ie);
+ if (hexstr2bin(d, ie, ielen) < 0)
+ ielen = 0;
+ drv->probe_from = from;
+ drv->probe_from_len = fromlen;
+ p2p_probe_req_rx(drv->p2p, sa, ie, ielen);
+ drv->probe_from = NULL;
+ }
+#endif /* CONFIG_P2P */
+
+ 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);
+}
+
+
+static void wpa_driver_test_receive_unix(int sock, void *eloop_ctx,
+ void *sock_ctx)
+{
+ struct wpa_driver_test_data *drv = eloop_ctx;
+ char *buf;
+ int res;
+ struct sockaddr_storage from;
+ 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;
+ res = recvfrom(sock, buf, buflen - 1, 0,
+ (struct sockaddr *) &from, &fromlen);
+ if (res < 0) {
+ perror("recvfrom(test_socket)");
+ os_free(buf);
+ return;
+ }
+ buf[res] = '\0';
+
+ wpa_printf(MSG_DEBUG, "test_driver: received %u bytes", res);
+
+ if (os_strncmp(buf, "SCANRESP ", 9) == 0) {
+ wpa_driver_test_scanresp(drv, (struct sockaddr *) &from,
+ fromlen, buf + 9);
+ } else if (os_strncmp(buf, "ASSOCRESP ", 10) == 0) {
+ wpa_driver_test_assocresp(drv, (struct sockaddr *) &from,
+ fromlen, buf + 10);
+ } else if (os_strcmp(buf, "DISASSOC") == 0) {
+ wpa_driver_test_disassoc(drv, (struct sockaddr *) &from,
+ fromlen);
+ } else if (os_strcmp(buf, "DEAUTH") == 0) {
+ wpa_driver_test_disassoc(drv, (struct sockaddr *) &from,
+ fromlen);
+ } else if (os_strncmp(buf, "EAPOL ", 6) == 0) {
+ wpa_driver_test_eapol(drv, (struct sockaddr *) &from, fromlen,
+ (const u8 *) buf + 6, res - 6);
+ } 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);
+ }
+ os_free(buf);
+}
+
+
+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 = 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->test_socket = -1;
+
+ /* Set dummy BSSID and SSID for testing. */
+ 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;
+ }
+
+ eloop_register_timeout(1, 0, wpa_driver_test_poll, drv, NULL);
+
+ return bss;
+}
+
+
+static void wpa_driver_test_close_test_socket(struct wpa_driver_test_data *drv)
+{
+ if (drv->test_socket >= 0) {
+ eloop_unregister_read_sock(drv->test_socket);
+ close(drv->test_socket);
+ drv->test_socket = -1;
+ }
+
+ if (drv->own_socket_path) {
+ unlink(drv->own_socket_path);
+ os_free(drv->own_socket_path);
+ drv->own_socket_path = NULL;
+ }
+}
+
+
+static void wpa_driver_test_deinit(void *priv)
+{
+ struct test_driver_bss *dbss = priv;
+ struct wpa_driver_test_data *drv = dbss->drv;
+ struct test_client_socket *cli, *prev;
+ int i;
+
+#ifdef CONFIG_P2P
+ if (drv->p2p)
+ p2p_deinit(drv->p2p);
+ wpabuf_free(drv->pending_action_tx);
+#endif /* CONFIG_P2P */
+
+ 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, int ap)
+{
+#ifdef DRIVER_TEST_UNIX
+ static unsigned int counter = 0;
+ struct sockaddr_un addr;
+ size_t len;
+
+ os_free(drv->own_socket_path);
+ if (dir) {
+ len = os_strlen(dir) + 30;
+ drv->own_socket_path = os_malloc(len);
+ if (drv->own_socket_path == NULL)
+ return -1;
+ 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)
+ return -1;
+ os_snprintf(drv->own_socket_path, 100,
+ "/tmp/wpa_supplicant_test-%d-%d",
+ getpid(), counter++);
+ }
+
+ drv->test_socket = socket(PF_UNIX, SOCK_DGRAM, 0);
+ if (drv->test_socket < 0) {
+ perror("socket(PF_UNIX)");
+ os_free(drv->own_socket_path);
+ drv->own_socket_path = NULL;
+ return -1;
+ }
+
+ os_memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ os_strlcpy(addr.sun_path, drv->own_socket_path, sizeof(addr.sun_path));
+ if (bind(drv->test_socket, (struct sockaddr *) &addr,
+ sizeof(addr)) < 0) {
+ perror("bind(PF_UNIX)");
+ close(drv->test_socket);
+ unlink(drv->own_socket_path);
+ os_free(drv->own_socket_path);
+ drv->own_socket_path = NULL;
+ return -1;
+ }
+
+ eloop_register_read_sock(drv->test_socket,
+ wpa_driver_test_receive_unix, drv, NULL);
+
+ return 0;
+#else /* DRIVER_TEST_UNIX */
+ return -1;
+#endif /* DRIVER_TEST_UNIX */
+}
+
+
+static int wpa_driver_test_attach_udp(struct wpa_driver_test_data *drv,
+ char *dst)
+{
+ char *pos;
+
+ pos = os_strchr(dst, ':');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+ wpa_printf(MSG_DEBUG, "%s: addr=%s port=%s", __func__, dst, pos);
+
+ drv->test_socket = socket(PF_INET, SOCK_DGRAM, 0);
+ if (drv->test_socket < 0) {
+ perror("socket(PF_INET)");
+ return -1;
+ }
+
+ os_memset(&drv->hostapd_addr_udp, 0, sizeof(drv->hostapd_addr_udp));
+ drv->hostapd_addr_udp.sin_family = AF_INET;
+#if defined(CONFIG_NATIVE_WINDOWS) || defined(CONFIG_ANSI_C_EXTRA)
+ {
+ int a[4];
+ u8 *pos;
+ sscanf(dst, "%d.%d.%d.%d", &a[0], &a[1], &a[2], &a[3]);
+ pos = (u8 *) &drv->hostapd_addr_udp.sin_addr;
+ *pos++ = a[0];
+ *pos++ = a[1];
+ *pos++ = a[2];
+ *pos++ = a[3];
+ }
+#else /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */
+ inet_aton(dst, &drv->hostapd_addr_udp.sin_addr);
+#endif /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */
+ drv->hostapd_addr_udp.sin_port = htons(atoi(pos));
+
+ drv->hostapd_addr_udp_set = 1;
+
+ eloop_register_read_sock(drv->test_socket,
+ wpa_driver_test_receive_unix, drv, NULL);
+
+ return 0;
+}
+
+
+static int wpa_driver_test_set_param(void *priv, const char *param)
+{
+ 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);
+ if (param == NULL)
+ return 0;
+
+ wpa_driver_test_close_test_socket(drv);
+
+#ifdef DRIVER_TEST_UNIX
+ pos = os_strstr(param, "test_socket=");
+ if (pos) {
+ const char *pos2;
+ size_t len;
+
+ pos += 12;
+ pos2 = os_strchr(pos, ' ');
+ if (pos2)
+ len = pos2 - pos;
+ else
+ len = os_strlen(pos);
+ if (len > sizeof(drv->hostapd_addr.sun_path))
+ return -1;
+ os_memset(&drv->hostapd_addr, 0, sizeof(drv->hostapd_addr));
+ drv->hostapd_addr.sun_family = AF_UNIX;
+ os_memcpy(drv->hostapd_addr.sun_path, pos, len);
+ drv->hostapd_addr_set = 1;
+ }
+#endif /* DRIVER_TEST_UNIX */
+
+ pos = os_strstr(param, "test_dir=");
+ if (pos) {
+ char *end;
+ os_free(drv->test_dir);
+ drv->test_dir = os_strdup(pos + 9);
+ if (drv->test_dir == NULL)
+ return -1;
+ end = os_strchr(drv->test_dir, ' ');
+ if (end)
+ *end = '\0';
+ if (wpa_driver_test_attach(drv, drv->test_dir, 0))
+ return -1;
+ } else {
+ pos = os_strstr(param, "test_udp=");
+ if (pos) {
+ char *dst, *epos;
+ dst = os_strdup(pos + 9);
+ if (dst == NULL)
+ return -1;
+ epos = os_strchr(dst, ' ');
+ if (epos)
+ *epos = '\0';
+ if (wpa_driver_test_attach_udp(drv, dst))
+ return -1;
+ os_free(dst);
+ } else if (wpa_driver_test_attach(drv, NULL, 0))
+ return -1;
+ }
+
+ if (os_strstr(param, "use_associnfo=1")) {
+ wpa_printf(MSG_DEBUG, "test_driver: Use AssocInfo events");
+ drv->use_associnfo = 1;
+ }
+
+#ifdef CONFIG_CLIENT_MLME
+ if (os_strstr(param, "use_mlme=1")) {
+ wpa_printf(MSG_DEBUG, "test_driver: Use internal MLME");
+ drv->use_mlme = 1;
+ }
+#endif /* CONFIG_CLIENT_MLME */
+
+ if (os_strstr(param, "p2p_mgmt=1")) {
+ wpa_printf(MSG_DEBUG, "test_driver: Use internal P2P "
+ "management");
+ if (wpa_driver_test_init_p2p(drv) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static const u8 * wpa_driver_test_get_mac_addr(void *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;
+}
+
+
+static int wpa_driver_test_send_eapol(void *priv, const u8 *dest, u16 proto,
+ const u8 *data, size_t data_len)
+{
+ struct test_driver_bss *dbss = priv;
+ struct wpa_driver_test_data *drv = dbss->drv;
+ char *msg;
+ size_t msg_len;
+ struct l2_ethhdr eth;
+ struct sockaddr *addr;
+ socklen_t alen;
+#ifdef DRIVER_TEST_UNIX
+ struct sockaddr_un addr_un;
+#endif /* DRIVER_TEST_UNIX */
+
+ wpa_hexdump(MSG_MSGDUMP, "test_send_eapol TX frame", data, data_len);
+
+ os_memset(ð, 0, sizeof(eth));
+ os_memcpy(eth.h_dest, dest, ETH_ALEN);
+ os_memcpy(eth.h_source, drv->own_addr, ETH_ALEN);
+ eth.h_proto = host_to_be16(proto);
+
+ msg_len = 6 + sizeof(eth) + data_len;
+ msg = os_malloc(msg_len);
+ if (msg == NULL)
+ return -1;
+ os_memcpy(msg, "EAPOL ", 6);
+ os_memcpy(msg + 6, ð, sizeof(eth));
+ os_memcpy(msg + 6 + sizeof(eth), data, data_len);
+
+ 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;
+ alen = sizeof(drv->hostapd_addr_udp);
+ } else {
+#ifdef DRIVER_TEST_UNIX
+ addr = (struct sockaddr *) &drv->hostapd_addr;
+ alen = sizeof(drv->hostapd_addr);
+#else /* DRIVER_TEST_UNIX */
+ os_free(msg);
+ return -1;
+#endif /* DRIVER_TEST_UNIX */
+ }
+ } else {
+#ifdef DRIVER_TEST_UNIX
+ struct stat st;
+ os_memset(&addr_un, 0, sizeof(addr_un));
+ addr_un.sun_family = AF_UNIX;
+ os_snprintf(addr_un.sun_path, sizeof(addr_un.sun_path),
+ "%s/STA-" MACSTR, drv->test_dir, MAC2STR(dest));
+ if (stat(addr_un.sun_path, &st) < 0) {
+ os_snprintf(addr_un.sun_path, sizeof(addr_un.sun_path),
+ "%s/AP-" MACSTR,
+ drv->test_dir, MAC2STR(dest));
+ }
+ addr = (struct sockaddr *) &addr_un;
+ alen = sizeof(addr_un);
+#else /* DRIVER_TEST_UNIX */
+ os_free(msg);
+ return -1;
+#endif /* DRIVER_TEST_UNIX */
+ }
+
+ if (sendto(drv->test_socket, msg, msg_len, 0, addr, alen) < 0) {
+ perror("sendmsg(test_socket)");
+ os_free(msg);
+ return -1;
+ }
+
+ os_free(msg);
+ return 0;
+}
+
+
+static int wpa_driver_test_get_capa(void *priv, struct wpa_driver_capa *capa)
+{
+ 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 |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE |
+ WPA_DRIVER_CAPA_KEY_MGMT_FT |
+ WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK;
+ capa->enc = WPA_DRIVER_CAPA_ENC_WEP40 |
+ WPA_DRIVER_CAPA_ENC_WEP104 |
+ WPA_DRIVER_CAPA_ENC_TKIP |
+ WPA_DRIVER_CAPA_ENC_CCMP;
+ capa->auth = WPA_DRIVER_AUTH_OPEN |
+ WPA_DRIVER_AUTH_SHARED |
+ WPA_DRIVER_AUTH_LEAP;
+ if (drv->use_mlme)
+ capa->flags |= WPA_DRIVER_FLAGS_USER_SPACE_MLME;
+ if (drv->p2p)
+ capa->flags |= WPA_DRIVER_FLAGS_P2P_MGMT;
+ capa->flags |= WPA_DRIVER_FLAGS_AP;
+ capa->flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT;
+ capa->flags |= WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE;
+ capa->flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE;
+ capa->max_scan_ssids = 2;
+ capa->max_remain_on_chan = 60000;
+
+ return 0;
+}
+
+
+static int wpa_driver_test_mlme_setprotection(void *priv, const u8 *addr,
+ int protect_type,
+ int key_type)
+{
+ wpa_printf(MSG_DEBUG, "%s: protect_type=%d key_type=%d",
+ __func__, protect_type, key_type);
+
+ if (addr) {
+ wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR,
+ __func__, MAC2STR(addr));
+ }
+
+ return 0;
+}
+
+
+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);
+ drv->current_freq = freq;
+ return 0;
+}
+
+
+static int wpa_driver_test_mlme_add_sta(void *priv, const u8 *addr,
+ const u8 *supp_rates,
+ size_t supp_rates_len)
+{
+ wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR, __func__, MAC2STR(addr));
+ return 0;
+}
+
+
+static int wpa_driver_test_mlme_remove_sta(void *priv, const u8 *addr)
+{
+ wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR, __func__, MAC2STR(addr));
+ return 0;
+}
+
+
+static int wpa_driver_test_set_ssid(void *priv, const u8 *ssid,
+ size_t ssid_len)
+{
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+ return 0;
+}
+
+
+static int wpa_driver_test_set_bssid(void *priv, const u8 *bssid)
+{
+ wpa_printf(MSG_DEBUG, "%s: bssid=" MACSTR, __func__, MAC2STR(bssid));
+ return 0;
+}
+
+
+static void * wpa_driver_test_global_init(void)
+{
+ struct wpa_driver_test_global *global;
+
+ global = os_zalloc(sizeof(*global));
+ return global;
+}
+
+
+static void wpa_driver_test_global_deinit(void *priv)
+{
+ struct wpa_driver_test_global *global = priv;
+ os_free(global);
+}
+
+
+static struct wpa_interface_info *
+wpa_driver_test_get_interfaces(void *global_priv)
+{
+ /* struct wpa_driver_test_global *global = priv; */
+ struct wpa_interface_info *iface;
+
+ iface = os_zalloc(sizeof(*iface));
+ if (iface == NULL)
+ return iface;
+ iface->ifname = os_strdup("sta0");
+ iface->desc = os_strdup("test interface 0");
+ iface->drv_name = "test";
+ iface->next = os_zalloc(sizeof(*iface));
+ if (iface->next) {
+ iface->next->ifname = os_strdup("sta1");
+ iface->next->desc = os_strdup("test interface 1");
+ iface->next->drv_name = "test";
+ }
+
+ return iface;
+}
+
+
+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,
+ unsigned int wait,
+ 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;
+}
+
+
+#ifdef CONFIG_P2P
+static void test_send_action_cb(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_driver_test_data *drv = eloop_ctx;
+ int res;
+
+ if (drv->pending_action_tx == NULL)
+ return;
+
+ if (drv->off_channel_freq != drv->pending_action_freq) {
+ wpa_printf(MSG_DEBUG, "P2P: Pending Action frame TX "
+ "waiting for another freq=%u",
+ drv->pending_action_freq);
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "P2P: Sending pending Action frame to "
+ MACSTR, MAC2STR(drv->pending_action_dst));
+ res = wpa_driver_test_send_action(drv, drv->pending_action_freq, 0,
+ drv->pending_action_dst,
+ drv->pending_action_src,
+ drv->pending_action_bssid,
+ wpabuf_head(drv->pending_action_tx),
+ wpabuf_len(drv->pending_action_tx));
+}
+#endif /* CONFIG_P2P */
+
+
+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;
+
+ if (drv->p2p)
+ drv->off_channel_freq = 0;
+
+ drv->remain_on_channel_freq = 0;
+
+ wpa_supplicant_event(drv->ctx, EVENT_CANCEL_REMAIN_ON_CHANNEL, &data);
+}
+
+
+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);
+
+#ifdef CONFIG_P2P
+ if (drv->p2p) {
+ drv->off_channel_freq = drv->remain_on_channel_freq;
+ test_send_action_cb(drv, NULL);
+ if (drv->off_channel_freq == drv->pending_listen_freq) {
+ p2p_listen_cb(drv->p2p, drv->pending_listen_freq,
+ drv->pending_listen_duration);
+ drv->pending_listen_freq = 0;
+ }
+ }
+#endif /* CONFIG_P2P */
+
+ 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;
+}
+
+
+#ifdef CONFIG_P2P
+
+static int wpa_driver_test_p2p_find(void *priv, unsigned int timeout, int type)
+{
+ struct wpa_driver_test_data *drv = priv;
+ wpa_printf(MSG_DEBUG, "%s(timeout=%u)", __func__, timeout);
+ if (!drv->p2p)
+ return -1;
+ return p2p_find(drv->p2p, timeout, type, 0, NULL);
+}
+
+
+static int wpa_driver_test_p2p_stop_find(void *priv)
+{
+ struct wpa_driver_test_data *drv = priv;
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+ if (!drv->p2p)
+ return -1;
+ p2p_stop_find(drv->p2p);
+ return 0;
+}
+
+
+static int wpa_driver_test_p2p_listen(void *priv, unsigned int timeout)
+{
+ struct wpa_driver_test_data *drv = priv;
+ wpa_printf(MSG_DEBUG, "%s(timeout=%u)", __func__, timeout);
+ if (!drv->p2p)
+ return -1;
+ return p2p_listen(drv->p2p, timeout);
+}
+
+
+static int wpa_driver_test_p2p_connect(void *priv, const u8 *peer_addr,
+ int wps_method, int go_intent,
+ const u8 *own_interface_addr,
+ unsigned int force_freq,
+ int persistent_group)
+{
+ struct wpa_driver_test_data *drv = priv;
+ wpa_printf(MSG_DEBUG, "%s(peer_addr=" MACSTR " wps_method=%d "
+ "go_intent=%d "
+ "own_interface_addr=" MACSTR " force_freq=%u "
+ "persistent_group=%d)",
+ __func__, MAC2STR(peer_addr), wps_method, go_intent,
+ MAC2STR(own_interface_addr), force_freq, persistent_group);
+ if (!drv->p2p)
+ return -1;
+ return p2p_connect(drv->p2p, peer_addr, wps_method, go_intent,
+ own_interface_addr, force_freq, persistent_group);
+}
+
+
+static int wpa_driver_test_wps_success_cb(void *priv, const u8 *peer_addr)
+{
+ struct wpa_driver_test_data *drv = priv;
+ wpa_printf(MSG_DEBUG, "%s(peer_addr=" MACSTR ")",
+ __func__, MAC2STR(peer_addr));
+ if (!drv->p2p)
+ return -1;
+ p2p_wps_success_cb(drv->p2p, peer_addr);
+ return 0;
+}
+
+
+static int wpa_driver_test_p2p_group_formation_failed(void *priv)
+{
+ struct wpa_driver_test_data *drv = priv;
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+ if (!drv->p2p)
+ return -1;
+ p2p_group_formation_failed(drv->p2p);
+ return 0;
+}
+
+
+static int wpa_driver_test_p2p_set_params(void *priv,
+ const struct p2p_params *params)
+{
+ struct wpa_driver_test_data *drv = priv;
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+ if (!drv->p2p)
+ return -1;
+ if (p2p_set_dev_name(drv->p2p, params->dev_name) < 0 ||
+ p2p_set_pri_dev_type(drv->p2p, params->pri_dev_type) < 0 ||
+ p2p_set_sec_dev_types(drv->p2p, params->sec_dev_type,
+ params->num_sec_dev_types) < 0)
+ return -1;
+ return 0;
+}
+
+
+static int test_p2p_scan(void *ctx, enum p2p_scan_type type, int freq,
+ unsigned int num_req_dev_types,
+ const u8 *req_dev_types)
+{
+ struct wpa_driver_test_data *drv = ctx;
+ struct wpa_driver_scan_params params;
+ int ret;
+ struct wpabuf *wps_ie, *ies;
+ int social_channels[] = { 2412, 2437, 2462, 0, 0 };
+
+ wpa_printf(MSG_DEBUG, "%s(type=%d freq=%d)",
+ __func__, type, freq);
+
+ os_memset(¶ms, 0, sizeof(params));
+
+ /* P2P Wildcard SSID */
+ params.num_ssids = 1;
+ params.ssids[0].ssid = (u8 *) P2P_WILDCARD_SSID;
+ params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN;
+
+#if 0 /* TODO: WPS IE */
+ wpa_s->wps->dev.p2p = 1;
+ wps_ie = wps_build_probe_req_ie(0, &wpa_s->wps->dev, wpa_s->wps->uuid,
+ WPS_REQ_ENROLLEE);
+#else
+ wps_ie = wpabuf_alloc(1);
+#endif
+ if (wps_ie == NULL)
+ return -1;
+
+ ies = wpabuf_alloc(wpabuf_len(wps_ie) + 100);
+ if (ies == NULL) {
+ wpabuf_free(wps_ie);
+ return -1;
+ }
+ wpabuf_put_buf(ies, wps_ie);
+ wpabuf_free(wps_ie);
+
+ p2p_scan_ie(drv->p2p, ies);
+
+ params.extra_ies = wpabuf_head(ies);
+ params.extra_ies_len = wpabuf_len(ies);
+
+ switch (type) {
+ case P2P_SCAN_SOCIAL:
+ params.freqs = social_channels;
+ break;
+ case P2P_SCAN_FULL:
+ break;
+ case P2P_SCAN_SPECIFIC:
+ social_channels[0] = freq;
+ social_channels[1] = 0;
+ params.freqs = social_channels;
+ break;
+ case P2P_SCAN_SOCIAL_PLUS_ONE:
+ social_channels[3] = freq;
+ params.freqs = social_channels;
+ break;
+ }
+
+ drv->pending_p2p_scan = 1;
+ ret = wpa_driver_test_scan(drv, ¶ms);
+
+ wpabuf_free(ies);
+
+ return ret;
+}
+
+
+static int test_send_action(void *ctx, unsigned int freq, const u8 *dst,
+ const u8 *src, const u8 *bssid, const u8 *buf,
+ size_t len, unsigned int wait_time)
+{
+ struct wpa_driver_test_data *drv = ctx;
+
+ wpa_printf(MSG_DEBUG, "%s(freq=%u dst=" MACSTR " src=" MACSTR
+ " bssid=" MACSTR " len=%d",
+ __func__, freq, MAC2STR(dst), MAC2STR(src), MAC2STR(bssid),
+ (int) len);
+ if (freq <= 0) {
+ wpa_printf(MSG_WARNING, "P2P: No frequency specified for "
+ "action frame TX");
+ return -1;
+ }
+
+ if (drv->pending_action_tx) {
+ wpa_printf(MSG_DEBUG, "P2P: Dropped pending Action frame TX "
+ "to " MACSTR, MAC2STR(drv->pending_action_dst));
+ wpabuf_free(drv->pending_action_tx);
+ }
+ drv->pending_action_tx = wpabuf_alloc(len);
+ if (drv->pending_action_tx == NULL)
+ return -1;
+ wpabuf_put_data(drv->pending_action_tx, buf, len);
+ os_memcpy(drv->pending_action_src, src, ETH_ALEN);
+ os_memcpy(drv->pending_action_dst, dst, ETH_ALEN);
+ os_memcpy(drv->pending_action_bssid, bssid, ETH_ALEN);
+ drv->pending_action_freq = freq;
+
+ if (drv->off_channel_freq == freq) {
+ /* Already on requested channel; send immediately */
+ /* TODO: Would there ever be need to extend the current
+ * duration on the channel? */
+ eloop_cancel_timeout(test_send_action_cb, drv, NULL);
+ eloop_register_timeout(0, 0, test_send_action_cb, drv, NULL);
+ return 0;
+ }
+
+ wpa_printf(MSG_DEBUG, "P2P: Schedule Action frame to be transmitted "
+ "once the driver gets to the requested channel");
+ if (wpa_driver_test_remain_on_channel(drv, freq, wait_time) < 0) {
+ wpa_printf(MSG_DEBUG, "P2P: Failed to request driver "
+ "to remain on channel (%u MHz) for Action "
+ "Frame TX", freq);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void test_send_action_done(void *ctx)
+{
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+ /* TODO */
+}
+
+
+static void test_go_neg_completed(void *ctx, struct p2p_go_neg_results *res)
+{
+ struct wpa_driver_test_data *drv = ctx;
+ union wpa_event_data event;
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+ os_memset(&event, 0, sizeof(event));
+ event.p2p_go_neg_completed.res = res;
+ wpa_supplicant_event(drv->ctx, EVENT_P2P_GO_NEG_COMPLETED, &event);
+}
+
+
+static void test_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id)
+{
+ struct wpa_driver_test_data *drv = ctx;
+ union wpa_event_data event;
+ wpa_printf(MSG_DEBUG, "%s(src=" MACSTR ")", __func__, MAC2STR(src));
+ os_memset(&event, 0, sizeof(event));
+ event.p2p_go_neg_req_rx.src = src;
+ event.p2p_go_neg_req_rx.dev_passwd_id = dev_passwd_id;
+ wpa_supplicant_event(drv->ctx, EVENT_P2P_GO_NEG_REQ_RX, &event);
+}
+
+
+static void test_dev_found(void *ctx, const u8 *addr,
+ const struct p2p_peer_info *info, int new_device)
+{
+ struct wpa_driver_test_data *drv = ctx;
+ union wpa_event_data event;
+ char devtype[WPS_DEV_TYPE_BUFSIZE];
+ wpa_printf(MSG_DEBUG, "%s(" MACSTR " p2p_dev_addr=" MACSTR
+ " pri_dev_type=%s name='%s' config_methods=0x%x "
+ "dev_capab=0x%x group_capab=0x%x)",
+ __func__, MAC2STR(addr), MAC2STR(info->p2p_device_addr),
+ wps_dev_type_bin2str(info->pri_dev_type, devtype,
+ sizeof(devtype)),
+ info->device_name, info->config_methods, info->dev_capab,
+ info->group_capab);
+
+ os_memset(&event, 0, sizeof(event));
+ event.p2p_dev_found.addr = addr;
+ event.p2p_dev_found.dev_addr = info->p2p_device_addr;
+ event.p2p_dev_found.pri_dev_type = info->pri_dev_type;
+ event.p2p_dev_found.dev_name = info->device_name;
+ event.p2p_dev_found.config_methods = info->config_methods;
+ event.p2p_dev_found.dev_capab = info->dev_capab;
+ event.p2p_dev_found.group_capab = info->group_capab;
+ wpa_supplicant_event(drv->ctx, EVENT_P2P_DEV_FOUND, &event);
+}
+
+
+static int test_start_listen(void *ctx, unsigned int freq,
+ unsigned int duration,
+ const struct wpabuf *probe_resp_ie)
+{
+ struct wpa_driver_test_data *drv = ctx;
+
+ wpa_printf(MSG_DEBUG, "%s(freq=%u duration=%u)",
+ __func__, freq, duration);
+
+ if (wpa_driver_test_probe_req_report(drv, 1) < 0)
+ return -1;
+
+ drv->pending_listen_freq = freq;
+ drv->pending_listen_duration = duration;
+
+ if (wpa_driver_test_remain_on_channel(drv, freq, duration) < 0) {
+ drv->pending_listen_freq = 0;
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void test_stop_listen(void *ctx)
+{
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+ /* TODO */
+}
+
+
+static int test_send_probe_resp(void *ctx, const struct wpabuf *buf)
+{
+ struct wpa_driver_test_data *drv = ctx;
+ char resp[512], *pos, *end;
+ int ret;
+ const struct ieee80211_mgmt *mgmt;
+ const u8 *ie, *ie_end;
+
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+ wpa_hexdump_buf(MSG_MSGDUMP, "Probe Response", buf);
+ if (wpabuf_len(buf) < 24)
+ return -1;
+ if (!drv->probe_from) {
+ wpa_printf(MSG_DEBUG, "%s: probe_from not set", __func__);
+ return -1;
+ }
+
+ pos = resp;
+ end = resp + sizeof(resp);
+
+ mgmt = wpabuf_head(buf);
+
+ /* reply: SCANRESP BSSID SSID IEs */
+ ret = os_snprintf(pos, end - pos, "SCANRESP " MACSTR " ",
+ MAC2STR(mgmt->bssid));
+ if (ret < 0 || ret >= end - pos)
+ return -1;
+ pos += ret;
+
+ ie = mgmt->u.probe_resp.variable;
+ ie_end = wpabuf_head_u8(buf) + wpabuf_len(buf);
+ if (ie_end - ie < 2 || ie[0] != WLAN_EID_SSID ||
+ ie + 2 + ie[1] > ie_end)
+ return -1;
+ pos += wpa_snprintf_hex(pos, end - pos, ie + 2, ie[1]);
+
+ ret = os_snprintf(pos, end - pos, " ");
+ if (ret < 0 || ret >= end - pos)
+ return -1;
+ pos += ret;
+ pos += wpa_snprintf_hex(pos, end - pos, ie, ie_end - ie);
+
+ sendto(drv->test_socket, resp, pos - resp, 0,
+ drv->probe_from, drv->probe_from_len);
+
+ return 0;
+}
+
+
+static void test_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token,
+ u16 update_indic, const u8 *tlvs, size_t tlvs_len)
+{
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+ /* TODO */
+}
+
+
+static void test_sd_response(void *ctx, const u8 *sa, u16 update_indic,
+ const u8 *tlvs, size_t tlvs_len)
+{
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+ /* TODO */
+}
+
+
+static void test_prov_disc_req(void *ctx, const u8 *peer, u16 config_methods,
+ const u8 *dev_addr, const u8 *pri_dev_type,
+ const char *dev_name, u16 supp_config_methods,
+ u8 dev_capab, u8 group_capab)
+{
+ wpa_printf(MSG_DEBUG, "%s(peer=" MACSTR " config_methods=0x%x)",
+ __func__, MAC2STR(peer), config_methods);
+ /* TODO */
+}
+
+
+static void test_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods)
+{
+ wpa_printf(MSG_DEBUG, "%s(peer=" MACSTR " config_methods=0x%x)",
+ __func__, MAC2STR(peer), config_methods);
+ /* TODO */
+}
+
+#endif /* CONFIG_P2P */
+
+
+static int wpa_driver_test_init_p2p(struct wpa_driver_test_data *drv)
+{
+#ifdef CONFIG_P2P
+ struct p2p_config p2p;
+ unsigned int r;
+ int i;
+
+ os_memset(&p2p, 0, sizeof(p2p));
+ p2p.msg_ctx = drv->ctx;
+ p2p.cb_ctx = drv;
+ p2p.p2p_scan = test_p2p_scan;
+ p2p.send_action = test_send_action;
+ p2p.send_action_done = test_send_action_done;
+ p2p.go_neg_completed = test_go_neg_completed;
+ p2p.go_neg_req_rx = test_go_neg_req_rx;
+ p2p.dev_found = test_dev_found;
+ p2p.start_listen = test_start_listen;
+ p2p.stop_listen = test_stop_listen;
+ p2p.send_probe_resp = test_send_probe_resp;
+ p2p.sd_request = test_sd_request;
+ p2p.sd_response = test_sd_response;
+ p2p.prov_disc_req = test_prov_disc_req;
+ p2p.prov_disc_resp = test_prov_disc_resp;
+
+ os_memcpy(p2p.dev_addr, drv->own_addr, ETH_ALEN);
+
+ p2p.reg_class = 12; /* TODO: change depending on location */
+ /*
+ * Pick one of the social channels randomly as the listen
+ * channel.
+ */
+ os_get_random((u8 *) &r, sizeof(r));
+ p2p.channel = 1 + (r % 3) * 5;
+
+ /* TODO: change depending on location */
+ p2p.op_reg_class = 12;
+ /*
+ * For initial tests, pick the operation channel randomly.
+ * TODO: Use scan results (etc.) to select the best channel.
+ */
+ p2p.op_channel = 1 + r % 11;
+
+ os_memcpy(p2p.country, "US ", 3);
+
+ /* FIX: fetch available channels from the driver */
+ p2p.channels.reg_classes = 1;
+ p2p.channels.reg_class[0].reg_class = 12; /* US/12 = 2.4 GHz band */
+ p2p.channels.reg_class[0].channels = 11;
+ for (i = 0; i < 11; i++)
+ p2p.channels.reg_class[0].channel[i] = i + 1;
+
+ p2p.max_peers = 100;
+
+ drv->p2p = p2p_init(&p2p);
+ if (drv->p2p == NULL)
+ return -1;
+ return 0;
+#else /* CONFIG_P2P */
+ wpa_printf(MSG_INFO, "driver_test: P2P support not included");
+ return -1;
+#endif /* CONFIG_P2P */
+}
+
+
+const struct wpa_driver_ops wpa_driver_test_ops = {
+ "test",
+ "wpa_supplicant test driver",
+ .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,
+#ifdef CONFIG_P2P
+ .p2p_find = wpa_driver_test_p2p_find,
+ .p2p_stop_find = wpa_driver_test_p2p_stop_find,
+ .p2p_listen = wpa_driver_test_p2p_listen,
+ .p2p_connect = wpa_driver_test_p2p_connect,
+ .wps_success_cb = wpa_driver_test_wps_success_cb,
+ .p2p_group_formation_failed =
+ wpa_driver_test_p2p_group_formation_failed,
+ .p2p_set_params = wpa_driver_test_p2p_set_params,
+#endif /* CONFIG_P2P */
+};
diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c
new file mode 100644
index 0000000..e731101
--- /dev/null
+++ b/src/drivers/driver_wext.c
@@ -0,0 +1,2320 @@
+/*
+ * 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
+ * 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.
+ *
+ * This file implements a driver interface for the Linux Wireless Extensions.
+ * When used with WE-18 or newer, this interface can be used as-is with number
+ * of drivers. In addition to this, some of the common functions in this file
+ * can be used by other driver interface implementations that use generic WE
+ * ioctls, but require private ioctls for some of the functionality.
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <net/if_arp.h>
+
+#include "wireless_copy.h"
+#include "common.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 "rfkill.h"
+#include "driver.h"
+#include "driver_wext.h"
+
+
+static int wpa_driver_wext_flush_pmkid(void *priv);
+static int wpa_driver_wext_get_range(void *priv);
+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_set_auth_alg(void *priv, int auth_alg);
+
+
+int wpa_driver_wext_set_auth_param(struct wpa_driver_wext_data *drv,
+ int idx, u32 value)
+{
+ struct iwreq iwr;
+ int ret = 0;
+
+ 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;
+
+ 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));
+ }
+ ret = errno == EOPNOTSUPP ? -2 : -1;
+ }
+
+ return ret;
+}
+
+
+/**
+ * wpa_driver_wext_get_bssid - Get BSSID, SIOCGIWAP
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * @bssid: Buffer for BSSID
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_driver_wext_get_bssid(void *priv, u8 *bssid)
+{
+ struct wpa_driver_wext_data *drv = priv;
+ struct iwreq iwr;
+ int ret = 0;
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+
+ if (ioctl(drv->ioctl_sock, SIOCGIWAP, &iwr) < 0) {
+ perror("ioctl[SIOCGIWAP]");
+ ret = -1;
+ }
+ os_memcpy(bssid, iwr.u.ap_addr.sa_data, ETH_ALEN);
+
+ return ret;
+}
+
+
+/**
+ * wpa_driver_wext_set_bssid - Set BSSID, SIOCSIWAP
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * @bssid: BSSID
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_driver_wext_set_bssid(void *priv, const u8 *bssid)
+{
+ struct wpa_driver_wext_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);
+
+ if (ioctl(drv->ioctl_sock, SIOCSIWAP, &iwr) < 0) {
+ perror("ioctl[SIOCSIWAP]");
+ ret = -1;
+ }
+
+ return ret;
+}
+
+
+/**
+ * wpa_driver_wext_get_ssid - Get SSID, SIOCGIWESSID
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * @ssid: Buffer for the SSID; must be at least 32 bytes long
+ * Returns: SSID length on success, -1 on failure
+ */
+int wpa_driver_wext_get_ssid(void *priv, u8 *ssid)
+{
+ struct wpa_driver_wext_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;
+
+ 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--;
+ }
+
+ return ret;
+}
+
+
+/**
+ * wpa_driver_wext_set_ssid - Set SSID, SIOCSIWESSID
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * @ssid: SSID
+ * @ssid_len: Length of SSID (0..32)
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_driver_wext_set_ssid(void *priv, const u8 *ssid, size_t ssid_len)
+{
+ struct wpa_driver_wext_data *drv = priv;
+ struct iwreq iwr;
+ int ret = 0;
+ char buf[33];
+
+ if (ssid_len > 32)
+ return -1;
+
+ 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++;
+ }
+ iwr.u.essid.length = ssid_len;
+
+ if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) {
+ perror("ioctl[SIOCSIWESSID]");
+ ret = -1;
+ }
+
+ return ret;
+}
+
+
+/**
+ * wpa_driver_wext_set_freq - Set frequency/channel, SIOCSIWFREQ
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * @freq: Frequency in MHz
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_driver_wext_set_freq(void *priv, int freq)
+{
+ struct wpa_driver_wext_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.freq.m = freq * 100000;
+ iwr.u.freq.e = 1;
+
+ if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) {
+ perror("ioctl[SIOCSIWFREQ]");
+ ret = -1;
+ }
+
+ return ret;
+}
+
+
+static void
+wpa_driver_wext_event_wireless_custom(void *ctx, char *custom)
+{
+ 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;
+ u8 *req_ies = NULL, *resp_ies = NULL;
+
+ spos = custom + 17;
+
+ bytes = strspn(spos, "0123456789abcdefABCDEF");
+ if (!bytes || (bytes & 1))
+ return;
+ bytes /= 2;
+
+ 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;
+
+ spos += bytes * 2;
+
+ data.assoc_info.resp_ies = NULL;
+ data.assoc_info.resp_ies_len = 0;
+
+ if (os_strncmp(spos, " RespIEs=", 9) == 0) {
+ spos += 9;
+
+ bytes = strspn(spos, "0123456789abcdefABCDEF");
+ if (!bytes || (bytes & 1))
+ goto done;
+ bytes /= 2;
+
+ 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;
+ }
+
+ wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data);
+
+ done:
+ 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)) {
+ wpa_printf(MSG_DEBUG, "WEXT: unrecognized "
+ "STKSTART.request '%s'", custom + 17);
+ return;
+ }
+ wpa_supplicant_event(ctx, EVENT_STKSTART, &data);
+#endif /* CONFIG_PEERKEY */
+ }
+}
+
+
+static int wpa_driver_wext_event_wireless_michaelmicfailure(
+ void *ctx, const char *ev, size_t len)
+{
+ const struct iw_michaelmicfailure *mic;
+ union wpa_event_data data;
+
+ if (len < sizeof(*mic))
+ return -1;
+
+ mic = (const struct iw_michaelmicfailure *) ev;
+
+ wpa_printf(MSG_DEBUG, "Michael MIC failure wireless event: "
+ "flags=0x%x src_addr=" MACSTR, mic->flags,
+ MAC2STR(mic->src_addr.sa_data));
+
+ 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);
+
+ return 0;
+}
+
+
+static int wpa_driver_wext_event_wireless_pmkidcand(
+ struct wpa_driver_wext_data *drv, const char *ev, size_t len)
+{
+ const struct iw_pmkid_cand *cand;
+ union wpa_event_data data;
+ const u8 *addr;
+
+ if (len < sizeof(*cand))
+ return -1;
+
+ cand = (const struct iw_pmkid_cand *) ev;
+ addr = (const u8 *) cand->bssid.sa_data;
+
+ wpa_printf(MSG_DEBUG, "PMKID candidate wireless event: "
+ "flags=0x%x index=%d bssid=" MACSTR, cand->flags,
+ cand->index, MAC2STR(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);
+
+ return 0;
+}
+
+
+static int wpa_driver_wext_event_wireless_assocreqie(
+ struct wpa_driver_wext_data *drv, const char *ev, int len)
+{
+ if (len < 0)
+ return -1;
+
+ 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;
+ }
+ os_memcpy(drv->assoc_req_ies, ev, len);
+ drv->assoc_req_ies_len = len;
+
+ return 0;
+}
+
+
+static int wpa_driver_wext_event_wireless_assocrespie(
+ struct wpa_driver_wext_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;
+}
+
+
+static void wpa_driver_wext_event_assoc_ies(struct wpa_driver_wext_data *drv)
+{
+ union wpa_event_data data;
+
+ if (drv->assoc_req_ies == NULL && drv->assoc_resp_ies == NULL)
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+ if (drv->assoc_req_ies) {
+ data.assoc_info.req_ies = drv->assoc_req_ies;
+ 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;
+ data.assoc_info.resp_ies_len = drv->assoc_resp_ies_len;
+ }
+
+ wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &data);
+
+ 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,
+ 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. */
+ 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)
+ 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(drv->ctx, EVENT_DISASSOC,
+ NULL);
+
+ } else {
+ wpa_driver_wext_event_assoc_ies(drv);
+ wpa_supplicant_event(drv->ctx, EVENT_ASSOC,
+ NULL);
+ }
+ break;
+ case IWEVMICHAELMICFAILURE:
+ if (custom + iwe->u.data.length > end) {
+ wpa_printf(MSG_DEBUG, "WEXT: Invalid "
+ "IWEVMICHAELMICFAILURE length");
+ return;
+ }
+ wpa_driver_wext_event_wireless_michaelmicfailure(
+ drv->ctx, custom, iwe->u.data.length);
+ break;
+ case IWEVCUSTOM:
+ if (custom + iwe->u.data.length > end) {
+ wpa_printf(MSG_DEBUG, "WEXT: Invalid "
+ "IWEVCUSTOM length");
+ 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_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, drv->ctx);
+ wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS,
+ NULL);
+ break;
+ case IWEVASSOCREQIE:
+ if (custom + iwe->u.data.length > end) {
+ wpa_printf(MSG_DEBUG, "WEXT: Invalid "
+ "IWEVASSOCREQIE length");
+ return;
+ }
+ wpa_driver_wext_event_wireless_assocreqie(
+ drv, custom, iwe->u.data.length);
+ break;
+ case IWEVASSOCRESPIE:
+ if (custom + iwe->u.data.length > end) {
+ wpa_printf(MSG_DEBUG, "WEXT: Invalid "
+ "IWEVASSOCRESPIE length");
+ return;
+ }
+ wpa_driver_wext_event_wireless_assocrespie(
+ drv, custom, iwe->u.data.length);
+ break;
+ case IWEVPMKIDCAND:
+ if (custom + iwe->u.data.length > end) {
+ wpa_printf(MSG_DEBUG, "WEXT: Invalid "
+ "IWEVPMKIDCAND length");
+ return;
+ }
+ wpa_driver_wext_event_wireless_pmkidcand(
+ drv, custom, iwe->u.data.length);
+ break;
+ }
+
+ pos += iwe->len;
+ }
+}
+
+
+static void wpa_driver_wext_event_link(struct wpa_driver_wext_data *drv,
+ char *buf, size_t len, int del)
+{
+ union wpa_event_data event;
+
+ 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;
+
+ 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->ifname, event.interface_status.ifname) == 0) {
+ if (del)
+ drv->if_removed = 1;
+ else
+ drv->if_removed = 0;
+ }
+
+ wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
+}
+
+
+static int wpa_driver_wext_own_ifname(struct wpa_driver_wext_data *drv,
+ u8 *buf, size_t len)
+{
+ int attrlen, rta_len;
+ struct rtattr *attr;
+
+ attrlen = len;
+ attr = (struct rtattr *) buf;
+
+ 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);
+ }
+
+ return 0;
+}
+
+
+static int wpa_driver_wext_own_ifindex(struct wpa_driver_wext_data *drv,
+ 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, buf, len)) {
+ drv->ifindex = if_nametoindex(drv->ifname);
+ wpa_printf(MSG_DEBUG, "WEXT: Update ifindex for a removed "
+ "interface");
+ wpa_driver_wext_finish_drv_init(drv);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void wpa_driver_wext_event_rtm_newlink(void *ctx, struct ifinfomsg *ifi,
+ u8 *buf, size_t len)
+{
+ struct wpa_driver_wext_data *drv = ctx;
+ int attrlen, rta_len;
+ struct rtattr *attr;
+
+ 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;
+ }
+
+ 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]" : "");
+
+ if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) {
+ wpa_printf(MSG_DEBUG, "WEXT: Interface down");
+ drv->if_disabled = 1;
+ wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED, NULL);
+ }
+
+ if (drv->if_disabled && (ifi->ifi_flags & IFF_UP)) {
+ wpa_printf(MSG_DEBUG, "WEXT: Interface up");
+ drv->if_disabled = 0;
+ wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED, NULL);
+ }
+
+ /*
+ * Some drivers send the association event before the operup event--in
+ * this case, lifting operstate in wpa_driver_wext_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);
+
+ 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, ((char *) attr) + rta_len,
+ attr->rta_len - rta_len);
+ } else if (attr->rta_type == IFLA_IFNAME) {
+ wpa_driver_wext_event_link(drv,
+ ((char *) attr) + rta_len,
+ attr->rta_len - rta_len, 0);
+ }
+ attr = RTA_NEXT(attr, attrlen);
+ }
+}
+
+
+static void wpa_driver_wext_event_rtm_dellink(void *ctx, struct ifinfomsg *ifi,
+ u8 *buf, size_t len)
+{
+ struct wpa_driver_wext_data *drv = ctx;
+ int attrlen, rta_len;
+ struct rtattr *attr;
+
+ 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,
+ ((char *) attr) + rta_len,
+ attr->rta_len - rta_len, 1);
+ }
+ attr = RTA_NEXT(attr, attrlen);
+ }
+}
+
+
+static void wpa_driver_wext_rfkill_blocked(void *ctx)
+{
+ wpa_printf(MSG_DEBUG, "WEXT: RFKILL blocked");
+ /*
+ * This may be for any interface; use ifdown event to disable
+ * interface.
+ */
+}
+
+
+static void wpa_driver_wext_rfkill_unblocked(void *ctx)
+{
+ struct wpa_driver_wext_data *drv = ctx;
+ wpa_printf(MSG_DEBUG, "WEXT: RFKILL unblocked");
+ if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1)) {
+ wpa_printf(MSG_DEBUG, "WEXT: Could not set interface UP "
+ "after rfkill unblock");
+ return;
+ }
+ /* rtnetlink ifup handler will report interface as enabled */
+}
+
+
+static void wext_get_phy_name(struct wpa_driver_wext_data *drv)
+{
+ /* Find phy (radio) to which this interface belongs */
+ char buf[90], *pos;
+ int f, rv;
+
+ drv->phyname[0] = '\0';
+ snprintf(buf, sizeof(buf) - 1, "/sys/class/net/%s/phy80211/name",
+ drv->ifname);
+ f = open(buf, O_RDONLY);
+ if (f < 0) {
+ wpa_printf(MSG_DEBUG, "Could not open file %s: %s",
+ buf, strerror(errno));
+ return;
+ }
+
+ rv = read(f, drv->phyname, sizeof(drv->phyname) - 1);
+ close(f);
+ if (rv < 0) {
+ wpa_printf(MSG_DEBUG, "Could not read file %s: %s",
+ buf, strerror(errno));
+ return;
+ }
+
+ drv->phyname[rv] = '\0';
+ pos = os_strchr(drv->phyname, '\n');
+ if (pos)
+ *pos = '\0';
+ wpa_printf(MSG_DEBUG, "wext: interface %s phy: %s",
+ drv->ifname, drv->phyname);
+}
+
+
+/**
+ * wpa_driver_wext_init - Initialize WE 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
+ */
+void * wpa_driver_wext_init(void *ctx, const char *ifname)
+{
+ struct wpa_driver_wext_data *drv;
+ struct netlink_config *cfg;
+ struct rfkill_config *rcfg;
+ char path[128];
+ struct stat buf;
+
+ drv = os_zalloc(sizeof(*drv));
+ if (drv == NULL)
+ return NULL;
+ 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;
+ wext_get_phy_name(drv);
+ }
+
+ drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (drv->ioctl_sock < 0) {
+ perror("socket(PF_INET,SOCK_DGRAM)");
+ goto err1;
+ }
+
+ 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;
+ }
+
+ rcfg = os_zalloc(sizeof(*rcfg));
+ if (rcfg == NULL)
+ goto err3;
+ rcfg->ctx = drv;
+ os_strlcpy(rcfg->ifname, ifname, sizeof(rcfg->ifname));
+ rcfg->blocked_cb = wpa_driver_wext_rfkill_blocked;
+ rcfg->unblocked_cb = wpa_driver_wext_rfkill_unblocked;
+ drv->rfkill = rfkill_init(rcfg);
+ if (drv->rfkill == NULL) {
+ wpa_printf(MSG_DEBUG, "WEXT: RFKILL status not available");
+ os_free(rcfg);
+ }
+
+ drv->mlme_sock = -1;
+
+ 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:
+ rfkill_deinit(drv->rfkill);
+ netlink_deinit(drv->netlink);
+err2:
+ close(drv->ioctl_sock);
+err1:
+ os_free(drv);
+ return NULL;
+}
+
+
+static void wpa_driver_wext_send_rfkill(void *eloop_ctx, void *timeout_ctx)
+{
+ wpa_supplicant_event(timeout_ctx, EVENT_INTERFACE_DISABLED, NULL);
+}
+
+
+static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv)
+{
+ int send_rfkill_event = 0;
+
+ if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1) < 0) {
+ if (rfkill_is_blocked(drv->rfkill)) {
+ wpa_printf(MSG_DEBUG, "WEXT: Could not yet enable "
+ "interface '%s' due to rfkill",
+ drv->ifname);
+ drv->if_disabled = 1;
+ send_rfkill_event = 1;
+ } else {
+ wpa_printf(MSG_ERROR, "WEXT: Could not set "
+ "interface '%s' UP", drv->ifname);
+ return -1;
+ }
+ }
+
+ /*
+ * Make sure that the driver does not have any obsolete PMKID entries.
+ */
+ wpa_driver_wext_flush_pmkid(drv);
+
+ if (wpa_driver_wext_set_mode(drv, 0) < 0) {
+ wpa_printf(MSG_DEBUG, "Could not configure driver to use "
+ "managed mode");
+ /* Try to use it anyway */
+ }
+
+ wpa_driver_wext_get_range(drv);
+
+ /*
+ * Unlock the driver's BSSID and force to a random SSID to clear any
+ * previous association the driver might have when the supplicant
+ * starts up.
+ */
+ wpa_driver_wext_disconnect(drv);
+
+ drv->ifindex = if_nametoindex(drv->ifname);
+
+ if (os_strncmp(drv->ifname, "wlan", 4) == 0) {
+ /*
+ * Host AP driver may use both wlan# and wifi# interface in
+ * wireless events. Since some of the versions included WE-18
+ * support, let's add the alternative ifindex also from
+ * driver_wext.c for the time being. This may be removed at
+ * some point once it is believed that old versions of the
+ * driver are not in use anymore.
+ */
+ char ifname2[IFNAMSIZ + 1];
+ os_strlcpy(ifname2, drv->ifname, sizeof(ifname2));
+ os_memcpy(ifname2, "wifi", 4);
+ wpa_driver_wext_alternative_ifindex(drv, ifname2);
+ }
+
+ netlink_send_oper_ifla(drv->netlink, drv->ifindex,
+ 1, IF_OPER_DORMANT);
+
+ if (send_rfkill_event) {
+ eloop_register_timeout(0, 0, wpa_driver_wext_send_rfkill,
+ drv, drv->ctx);
+ }
+
+ return 0;
+}
+
+
+/**
+ * wpa_driver_wext_deinit - Deinitialize WE driver interface
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ *
+ * Shut down driver interface and processing of driver events. Free
+ * private data buffer if one was allocated in wpa_driver_wext_init().
+ */
+void wpa_driver_wext_deinit(void *priv)
+{
+ struct wpa_driver_wext_data *drv = priv;
+
+ wpa_driver_wext_set_auth_param(drv, IW_AUTH_WPA_ENABLED, 0);
+
+ eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv, drv->ctx);
+
+ /*
+ * Clear possibly configured driver parameters in order to make it
+ * easier to use the driver after wpa_supplicant has been terminated.
+ */
+ wpa_driver_wext_disconnect(drv);
+
+ netlink_send_oper_ifla(drv->netlink, drv->ifindex, 0, IF_OPER_UP);
+ netlink_deinit(drv->netlink);
+ rfkill_deinit(drv->rfkill);
+
+ if (drv->mlme_sock >= 0)
+ eloop_unregister_read_sock(drv->mlme_sock);
+
+ (void) linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 0);
+
+ close(drv->ioctl_sock);
+ if (drv->mlme_sock >= 0)
+ close(drv->mlme_sock);
+ os_free(drv->assoc_req_ies);
+ os_free(drv->assoc_resp_ies);
+ os_free(drv);
+}
+
+
+/**
+ * wpa_driver_wext_scan_timeout - Scan timeout to report scan completion
+ * @eloop_ctx: Unused
+ * @timeout_ctx: ctx argument given to wpa_driver_wext_init()
+ *
+ * This function can be used as registered timeout when starting a scan to
+ * generate a scan completed event if the driver does not report this.
+ */
+void wpa_driver_wext_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
+ wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
+}
+
+
+/**
+ * wpa_driver_wext_scan - Request the driver to initiate scan
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * @param: Scan parameters (specific SSID to scan for (ProbeReq), etc.)
+ * Returns: 0 on success, -1 on failure
+ */
+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)",
+ __FUNCTION__, (unsigned long) ssid_len);
+ return -1;
+ }
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+
+ if (ssid && ssid_len) {
+ os_memset(&req, 0, sizeof(req));
+ req.essid_len = ssid_len;
+ req.bssid.sa_family = ARPHRD_ETHER;
+ os_memset(req.bssid.sa_data, 0xff, ETH_ALEN);
+ os_memcpy(req.essid, ssid, ssid_len);
+ iwr.u.data.pointer = (caddr_t) &req;
+ iwr.u.data.length = sizeof(req);
+ iwr.u.data.flags = IW_SCAN_THIS_ESSID;
+ }
+
+ if (ioctl(drv->ioctl_sock, SIOCSIWSCAN, &iwr) < 0) {
+ perror("ioctl[SIOCSIWSCAN]");
+ ret = -1;
+ }
+
+ /* Not all drivers generate "scan completed" wireless event, so try to
+ * read results after a timeout. */
+ timeout = 5;
+ 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.
+ */
+ timeout = 30;
+ }
+ wpa_printf(MSG_DEBUG, "Scan requested (ret=%d) - scan timeout %d "
+ "seconds", ret, timeout);
+ eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv, drv->ctx);
+ eloop_register_timeout(timeout, 0, wpa_driver_wext_scan_timeout, drv,
+ drv->ctx);
+
+ return ret;
+}
+
+
+static u8 * wpa_driver_wext_giwscan(struct wpa_driver_wext_data *drv,
+ size_t *len)
+{
+ struct iwreq iwr;
+ u8 *res_buf;
+ size_t res_buf_len;
+
+ res_buf_len = IW_SCAN_MAX_DATA;
+ for (;;) {
+ res_buf = os_malloc(res_buf_len);
+ if (res_buf == NULL)
+ return NULL;
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+ iwr.u.data.pointer = res_buf;
+ iwr.u.data.length = res_buf_len;
+
+ if (ioctl(drv->ioctl_sock, SIOCGIWSCAN, &iwr) == 0)
+ break;
+
+ if (errno == E2BIG && res_buf_len < 65535) {
+ os_free(res_buf);
+ res_buf = NULL;
+ res_buf_len *= 2;
+ if (res_buf_len > 65535)
+ res_buf_len = 65535; /* 16-bit length field */
+ wpa_printf(MSG_DEBUG, "Scan results did not fit - "
+ "trying larger buffer (%lu bytes)",
+ (unsigned long) res_buf_len);
+ } else {
+ perror("ioctl[SIOCGIWSCAN]");
+ os_free(res_buf);
+ return NULL;
+ }
+ }
+
+ if (iwr.u.data.length > res_buf_len) {
+ os_free(res_buf);
+ return NULL;
+ }
+ *len = iwr.u.data.length;
+
+ return res_buf;
+}
+
+
+/*
+ * Data structure for collecting WEXT scan results. This is needed to allow
+ * the various methods of reporting IEs to be combined into a single IE buffer.
+ */
+struct wext_scan_data {
+ struct wpa_scan_res res;
+ u8 *ie;
+ size_t ie_len;
+ u8 ssid[32];
+ size_t ssid_len;
+ int maxrate;
+};
+
+
+static void wext_get_scan_mode(struct iw_event *iwe,
+ struct wext_scan_data *res)
+{
+ if (iwe->u.mode == IW_MODE_ADHOC)
+ res->res.caps |= IEEE80211_CAP_IBSS;
+ else if (iwe->u.mode == IW_MODE_MASTER || iwe->u.mode == IW_MODE_INFRA)
+ res->res.caps |= IEEE80211_CAP_ESS;
+}
+
+
+static void wext_get_scan_ssid(struct iw_event *iwe,
+ struct wext_scan_data *res, char *custom,
+ char *end)
+{
+ int ssid_len = iwe->u.essid.length;
+ if (custom + ssid_len > end)
+ return;
+ if (iwe->u.essid.flags &&
+ ssid_len > 0 &&
+ ssid_len <= IW_ESSID_MAX_SIZE) {
+ os_memcpy(res->ssid, custom, ssid_len);
+ res->ssid_len = ssid_len;
+ }
+}
+
+
+static void wext_get_scan_freq(struct iw_event *iwe,
+ struct wext_scan_data *res)
+{
+ int divi = 1000000, i;
+
+ if (iwe->u.freq.e == 0) {
+ /*
+ * Some drivers do not report frequency, but a channel.
+ * Try to map this to frequency by assuming they are using
+ * IEEE 802.11b/g. But don't overwrite a previously parsed
+ * frequency if the driver sends both frequency and channel,
+ * since the driver may be sending an A-band channel that we
+ * don't handle here.
+ */
+
+ if (res->res.freq)
+ return;
+
+ if (iwe->u.freq.m >= 1 && iwe->u.freq.m <= 13) {
+ res->res.freq = 2407 + 5 * iwe->u.freq.m;
+ return;
+ } else if (iwe->u.freq.m == 14) {
+ res->res.freq = 2484;
+ return;
+ }
+ }
+
+ if (iwe->u.freq.e > 6) {
+ wpa_printf(MSG_DEBUG, "Invalid freq in scan results (BSSID="
+ MACSTR " m=%d e=%d)",
+ MAC2STR(res->res.bssid), iwe->u.freq.m,
+ iwe->u.freq.e);
+ return;
+ }
+
+ for (i = 0; i < iwe->u.freq.e; i++)
+ divi /= 10;
+ res->res.freq = iwe->u.freq.m / divi;
+}
+
+
+static void wext_get_scan_qual(struct wpa_driver_wext_data *drv,
+ struct iw_event *iwe,
+ struct wext_scan_data *res)
+{
+ 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;
+ if ((iwe->u.qual.updated & IW_QUAL_DBM) ||
+ ((iwe->u.qual.level != 0) &&
+ (iwe->u.qual.level > drv->max_level))) {
+ if (iwe->u.qual.level >= 64)
+ res->res.level -= 0x100;
+ if (iwe->u.qual.noise >= 64)
+ res->res.noise -= 0x100;
+ }
+}
+
+
+static void wext_get_scan_encode(struct iw_event *iwe,
+ struct wext_scan_data *res)
+{
+ if (!(iwe->u.data.flags & IW_ENCODE_DISABLED))
+ res->res.caps |= IEEE80211_CAP_PRIVACY;
+}
+
+
+static void wext_get_scan_rate(struct iw_event *iwe,
+ struct wext_scan_data *res, char *pos,
+ char *end)
+{
+ int maxrate;
+ char *custom = pos + IW_EV_LCP_LEN;
+ struct iw_param p;
+ size_t clen;
+
+ clen = iwe->len;
+ if (custom + clen > end)
+ return;
+ maxrate = 0;
+ while (((ssize_t) clen) >= (ssize_t) sizeof(struct iw_param)) {
+ /* Note: may be misaligned, make a local, aligned copy */
+ os_memcpy(&p, custom, sizeof(struct iw_param));
+ if (p.value > maxrate)
+ maxrate = p.value;
+ clen -= sizeof(struct iw_param);
+ custom += sizeof(struct iw_param);
+ }
+
+ /* Convert the maxrate from WE-style (b/s units) to
+ * 802.11 rates (500000 b/s units).
+ */
+ res->maxrate = maxrate / 500000;
+}
+
+
+static void wext_get_scan_iwevgenie(struct iw_event *iwe,
+ struct wext_scan_data *res, char *custom,
+ char *end)
+{
+ char *genie, *gpos, *gend;
+ u8 *tmp;
+
+ if (iwe->u.data.length == 0)
+ return;
+
+ gpos = genie = custom;
+ gend = genie + iwe->u.data.length;
+ if (gend > end) {
+ wpa_printf(MSG_INFO, "IWEVGENIE overflow");
+ return;
+ }
+
+ tmp = os_realloc(res->ie, res->ie_len + gend - gpos);
+ if (tmp == NULL)
+ return;
+ os_memcpy(tmp + res->ie_len, gpos, gend - gpos);
+ res->ie = tmp;
+ res->ie_len += gend - gpos;
+}
+
+
+static void wext_get_scan_custom(struct iw_event *iwe,
+ struct wext_scan_data *res, char *custom,
+ char *end)
+{
+ size_t clen;
+ u8 *tmp;
+
+ clen = iwe->u.data.length;
+ if (custom + clen > end)
+ return;
+
+ if (clen > 7 && os_strncmp(custom, "wpa_ie=", 7) == 0) {
+ char *spos;
+ int bytes;
+ spos = custom + 7;
+ bytes = custom + clen - spos;
+ if (bytes & 1 || bytes == 0)
+ return;
+ bytes /= 2;
+ tmp = os_realloc(res->ie, res->ie_len + bytes);
+ if (tmp == NULL)
+ return;
+ 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;
+ int bytes;
+ spos = custom + 7;
+ bytes = custom + clen - spos;
+ if (bytes & 1 || bytes == 0)
+ return;
+ bytes /= 2;
+ tmp = os_realloc(res->ie, res->ie_len + bytes);
+ if (tmp == NULL)
+ return;
+ 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;
+ int bytes;
+ u8 bin[8];
+ spos = custom + 4;
+ bytes = custom + clen - spos;
+ if (bytes != 16) {
+ wpa_printf(MSG_INFO, "Invalid TSF length (%d)", bytes);
+ return;
+ }
+ bytes /= 2;
+ if (hexstr2bin(spos, bin, bytes) < 0) {
+ wpa_printf(MSG_DEBUG, "WEXT: Invalid TSF value");
+ return;
+ }
+ res->res.tsf += WPA_GET_BE64(bin);
+ }
+}
+
+
+static int wext_19_iw_point(struct wpa_driver_wext_data *drv, u16 cmd)
+{
+ return drv->we_version_compiled > 18 &&
+ (cmd == SIOCGIWESSID || cmd == SIOCGIWENCODE ||
+ cmd == IWEVGENIE || cmd == IWEVCUSTOM);
+}
+
+
+static void wpa_driver_wext_add_scan_entry(struct wpa_scan_results *res,
+ struct wext_scan_data *data)
+{
+ struct wpa_scan_res **tmp;
+ struct wpa_scan_res *r;
+ size_t extra_len;
+ u8 *pos, *end, *ssid_ie = NULL, *rate_ie = NULL;
+
+ /* Figure out whether we need to fake any IEs */
+ pos = data->ie;
+ end = pos + data->ie_len;
+ while (pos && pos + 1 < end) {
+ if (pos + 2 + pos[1] > end)
+ break;
+ if (pos[0] == WLAN_EID_SSID)
+ ssid_ie = pos;
+ else if (pos[0] == WLAN_EID_SUPP_RATES)
+ rate_ie = pos;
+ else if (pos[0] == WLAN_EID_EXT_SUPP_RATES)
+ rate_ie = pos;
+ pos += 2 + pos[1];
+ }
+
+ extra_len = 0;
+ if (ssid_ie == NULL)
+ extra_len += 2 + data->ssid_len;
+ if (rate_ie == NULL && data->maxrate)
+ extra_len += 3;
+
+ r = os_zalloc(sizeof(*r) + extra_len + data->ie_len);
+ if (r == NULL)
+ return;
+ os_memcpy(r, &data->res, sizeof(*r));
+ r->ie_len = extra_len + data->ie_len;
+ pos = (u8 *) (r + 1);
+ if (ssid_ie == NULL) {
+ /*
+ * Generate a fake SSID IE since the driver did not report
+ * a full IE list.
+ */
+ *pos++ = WLAN_EID_SSID;
+ *pos++ = data->ssid_len;
+ os_memcpy(pos, data->ssid, data->ssid_len);
+ pos += data->ssid_len;
+ }
+ if (rate_ie == NULL && data->maxrate) {
+ /*
+ * Generate a fake Supported Rates IE since the driver did not
+ * report a full IE list.
+ */
+ *pos++ = WLAN_EID_SUPP_RATES;
+ *pos++ = 1;
+ *pos++ = data->maxrate;
+ }
+ if (data->ie)
+ os_memcpy(pos, data->ie, data->ie_len);
+
+ tmp = os_realloc(res->res,
+ (res->num + 1) * sizeof(struct wpa_scan_res *));
+ if (tmp == NULL) {
+ os_free(r);
+ return;
+ }
+ tmp[res->num++] = r;
+ res->res = tmp;
+}
+
+
+/**
+ * wpa_driver_wext_get_scan_results - Fetch the latest scan results
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * Returns: Scan results on success, -1 on failure
+ */
+struct wpa_scan_results * wpa_driver_wext_get_scan_results(void *priv)
+{
+ struct wpa_driver_wext_data *drv = priv;
+ size_t ap_num = 0, len;
+ int first;
+ u8 *res_buf;
+ struct iw_event iwe_buf, *iwe = &iwe_buf;
+ char *pos, *end, *custom;
+ struct wpa_scan_results *res;
+ struct wext_scan_data data;
+
+ res_buf = wpa_driver_wext_giwscan(drv, &len);
+ if (res_buf == NULL)
+ return NULL;
+
+ ap_num = 0;
+ first = 1;
+
+ res = os_zalloc(sizeof(*res));
+ if (res == NULL) {
+ os_free(res_buf);
+ return NULL;
+ }
+
+ pos = (char *) res_buf;
+ end = (char *) res_buf + len;
+ os_memset(&data, 0, sizeof(data));
+
+ 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);
+ if (iwe->len <= IW_EV_LCP_LEN)
+ break;
+
+ custom = pos + IW_EV_POINT_LEN;
+ if (wext_19_iw_point(drv, iwe->cmd)) {
+ /* 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:
+ if (!first)
+ wpa_driver_wext_add_scan_entry(res, &data);
+ first = 0;
+ os_free(data.ie);
+ os_memset(&data, 0, sizeof(data));
+ os_memcpy(data.res.bssid,
+ iwe->u.ap_addr.sa_data, ETH_ALEN);
+ break;
+ case SIOCGIWMODE:
+ wext_get_scan_mode(iwe, &data);
+ break;
+ case SIOCGIWESSID:
+ wext_get_scan_ssid(iwe, &data, custom, end);
+ break;
+ case SIOCGIWFREQ:
+ wext_get_scan_freq(iwe, &data);
+ break;
+ case IWEVQUAL:
+ wext_get_scan_qual(drv, iwe, &data);
+ break;
+ case SIOCGIWENCODE:
+ wext_get_scan_encode(iwe, &data);
+ break;
+ case SIOCGIWRATE:
+ wext_get_scan_rate(iwe, &data, pos, end);
+ break;
+ case IWEVGENIE:
+ wext_get_scan_iwevgenie(iwe, &data, custom, end);
+ break;
+ case IWEVCUSTOM:
+ wext_get_scan_custom(iwe, &data, custom, end);
+ break;
+ }
+
+ pos += iwe->len;
+ }
+ os_free(res_buf);
+ res_buf = NULL;
+ if (!first)
+ wpa_driver_wext_add_scan_entry(res, &data);
+ os_free(data.ie);
+
+ wpa_printf(MSG_DEBUG, "Received %lu bytes of scan results (%lu BSSes)",
+ (unsigned long) len, (unsigned long) res->num);
+
+ return res;
+}
+
+
+static int wpa_driver_wext_get_range(void *priv)
+{
+ struct wpa_driver_wext_data *drv = priv;
+ struct iw_range *range;
+ struct iwreq iwr;
+ int minlen;
+ size_t buflen;
+
+ /*
+ * 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;
+
+ 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);
+
+ 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;
+ if (range->enc_capa & IW_ENC_CAPA_4WAY_HANDSHAKE)
+ drv->capa.flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE;
+ 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",
+ drv->capa.key_mgmt, drv->capa.enc, drv->capa.flags);
+ } else {
+ wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: too old (short) data - "
+ "assuming WPA is not supported");
+ }
+
+ drv->max_level = range->max_qual.level;
+
+ os_free(range);
+ return 0;
+}
+
+
+static int wpa_driver_wext_set_psk(struct wpa_driver_wext_data *drv,
+ const u8 *psk)
+{
+ struct iw_encode_ext *ext;
+ struct iwreq iwr;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+ if (!(drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE))
+ return 0;
+
+ if (!psk)
+ return 0;
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+
+ ext = os_zalloc(sizeof(*ext) + PMK_LEN);
+ if (ext == NULL)
+ return -1;
+
+ iwr.u.encoding.pointer = (caddr_t) ext;
+ iwr.u.encoding.length = sizeof(*ext) + PMK_LEN;
+ ext->key_len = PMK_LEN;
+ os_memcpy(&ext->key, psk, ext->key_len);
+ ext->alg = IW_ENCODE_ALG_PMK;
+
+ ret = ioctl(drv->ioctl_sock, SIOCSIWENCODEEXT, &iwr);
+ if (ret < 0)
+ perror("ioctl[SIOCSIWENCODEEXT] PMK");
+ os_free(ext);
+
+ return ret;
+}
+
+
+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,
+ const u8 *key, size_t key_len)
+{
+ struct wpa_driver_wext_data *drv = priv;
+ struct iwreq iwr;
+ int ret = 0;
+ struct iw_encode_ext *ext;
+
+ if (seq_len > IW_ENCODE_SEQ_MAX_SIZE) {
+ wpa_printf(MSG_DEBUG, "%s: Invalid seq_len %lu",
+ __FUNCTION__, (unsigned long) seq_len);
+ return -1;
+ }
+
+ ext = os_zalloc(sizeof(*ext) + key_len);
+ if (ext == NULL)
+ return -1;
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+ iwr.u.encoding.flags = key_idx + 1;
+ iwr.u.encoding.flags |= IW_ENCODE_TEMP;
+ if (alg == WPA_ALG_NONE)
+ iwr.u.encoding.flags |= IW_ENCODE_DISABLED;
+ iwr.u.encoding.pointer = (caddr_t) ext;
+ iwr.u.encoding.length = sizeof(*ext) + key_len;
+
+ if (addr == NULL || is_broadcast_ether_addr(addr))
+ ext->ext_flags |= IW_ENCODE_EXT_GROUP_KEY;
+ if (set_tx)
+ ext->ext_flags |= IW_ENCODE_EXT_SET_TX_KEY;
+
+ ext->addr.sa_family = ARPHRD_ETHER;
+ if (addr)
+ os_memcpy(ext->addr.sa_data, addr, ETH_ALEN);
+ else
+ os_memset(ext->addr.sa_data, 0xff, ETH_ALEN);
+ if (key && key_len) {
+ os_memcpy(ext + 1, key, key_len);
+ ext->key_len = key_len;
+ }
+ switch (alg) {
+ case WPA_ALG_NONE:
+ ext->alg = IW_ENCODE_ALG_NONE;
+ break;
+ case WPA_ALG_WEP:
+ ext->alg = IW_ENCODE_ALG_WEP;
+ break;
+ case WPA_ALG_TKIP:
+ ext->alg = IW_ENCODE_ALG_TKIP;
+ break;
+ case WPA_ALG_CCMP:
+ ext->alg = IW_ENCODE_ALG_CCMP;
+ break;
+ case WPA_ALG_PMK:
+ ext->alg = IW_ENCODE_ALG_PMK;
+ break;
+#ifdef CONFIG_IEEE80211W
+ case WPA_ALG_IGTK:
+ ext->alg = IW_ENCODE_ALG_AES_CMAC;
+ break;
+#endif /* CONFIG_IEEE80211W */
+ default:
+ wpa_printf(MSG_DEBUG, "%s: Unknown algorithm %d",
+ __FUNCTION__, alg);
+ os_free(ext);
+ return -1;
+ }
+
+ if (seq && seq_len) {
+ ext->ext_flags |= IW_ENCODE_EXT_RX_SEQ_VALID;
+ os_memcpy(ext->rx_seq, seq, seq_len);
+ }
+
+ if (ioctl(drv->ioctl_sock, SIOCSIWENCODEEXT, &iwr) < 0) {
+ ret = errno == EOPNOTSUPP ? -2 : -1;
+ if (errno == ENODEV) {
+ /*
+ * ndiswrapper seems to be returning incorrect error
+ * code.. */
+ ret = -2;
+ }
+
+ perror("ioctl[SIOCSIWENCODEEXT]");
+ }
+
+ os_free(ext);
+ return ret;
+}
+
+
+/**
+ * wpa_driver_wext_set_key - Configure encryption key
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * @priv: Private driver interface data
+ * @alg: Encryption algorithm (%WPA_ALG_NONE, %WPA_ALG_WEP,
+ * %WPA_ALG_TKIP, %WPA_ALG_CCMP); %WPA_ALG_NONE clears the key.
+ * @addr: Address of the peer STA or ff:ff:ff:ff:ff:ff for
+ * broadcast/default keys
+ * @key_idx: key index (0..3), usually 0 for unicast keys
+ * @set_tx: Configure this key as the default Tx key (only used when
+ * driver does not support separate unicast/individual key
+ * @seq: Sequence number/packet number, seq_len octets, the next
+ * packet number to be used for in replay protection; configured
+ * for Rx keys (in most cases, this is only used with broadcast
+ * keys and set to zero for unicast keys)
+ * @seq_len: Length of the seq, depends on the algorithm:
+ * TKIP: 6 octets, CCMP: 6 octets
+ * @key: Key buffer; TKIP: 16-byte temporal key, 8-byte Tx Mic key,
+ * 8-byte Rx Mic Key
+ * @key_len: Length of the key buffer in octets (WEP: 5 or 13,
+ * TKIP: 32, CCMP: 16)
+ * Returns: 0 on success, -1 on failure
+ *
+ * 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(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_wext_data *drv = priv;
+ struct iwreq iwr;
+ int ret = 0;
+
+ wpa_printf(MSG_DEBUG, "%s: alg=%d key_idx=%d set_tx=%d seq_len=%lu "
+ "key_len=%lu",
+ __FUNCTION__, alg, key_idx, set_tx,
+ (unsigned long) seq_len, (unsigned long) key_len);
+
+ ret = wpa_driver_wext_set_key_ext(drv, alg, addr, key_idx, set_tx,
+ seq, seq_len, key, key_len);
+ if (ret == 0)
+ return 0;
+
+ if (ret == -2 &&
+ (alg == WPA_ALG_NONE || alg == WPA_ALG_WEP)) {
+ wpa_printf(MSG_DEBUG, "Driver did not support "
+ "SIOCSIWENCODEEXT, trying SIOCSIWENCODE");
+ ret = 0;
+ } else {
+ wpa_printf(MSG_DEBUG, "Driver did not support "
+ "SIOCSIWENCODEEXT");
+ return ret;
+ }
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+ iwr.u.encoding.flags = key_idx + 1;
+ iwr.u.encoding.flags |= IW_ENCODE_TEMP;
+ if (alg == WPA_ALG_NONE)
+ iwr.u.encoding.flags |= IW_ENCODE_DISABLED;
+ iwr.u.encoding.pointer = (caddr_t) key;
+ iwr.u.encoding.length = key_len;
+
+ if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) {
+ perror("ioctl[SIOCSIWENCODE]");
+ ret = -1;
+ }
+
+ if (set_tx && alg != WPA_ALG_NONE) {
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+ iwr.u.encoding.flags = key_idx + 1;
+ iwr.u.encoding.flags |= IW_ENCODE_TEMP;
+ iwr.u.encoding.pointer = (caddr_t) NULL;
+ iwr.u.encoding.length = 0;
+ if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) {
+ perror("ioctl[SIOCSIWENCODE] (set_tx)");
+ ret = -1;
+ }
+ }
+
+ return ret;
+}
+
+
+static int wpa_driver_wext_set_countermeasures(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_TKIP_COUNTERMEASURES,
+ enabled);
+}
+
+
+static int wpa_driver_wext_set_drop_unencrypted(void *priv,
+ int enabled)
+{
+ struct wpa_driver_wext_data *drv = priv;
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+ drv->use_crypt = enabled;
+ return wpa_driver_wext_set_auth_param(drv, IW_AUTH_DROP_UNENCRYPTED,
+ enabled);
+}
+
+
+static int wpa_driver_wext_mlme(struct wpa_driver_wext_data *drv,
+ const u8 *addr, int cmd, int reason_code)
+{
+ struct iwreq iwr;
+ struct iw_mlme mlme;
+ int ret = 0;
+
+ 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;
+ }
+
+ return ret;
+}
+
+
+static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv)
+{
+ struct iwreq iwr;
+ const u8 null_bssid[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
+ u8 ssid[32];
+ int i;
+
+ /*
+ * Only force-disconnect when the card is in infrastructure mode,
+ * otherwise the driver might interpret the cleared BSSID and random
+ * SSID as an attempt to create a new ad-hoc network.
+ */
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+ if (ioctl(drv->ioctl_sock, SIOCGIWMODE, &iwr) < 0) {
+ perror("ioctl[SIOCGIWMODE]");
+ iwr.u.mode = IW_MODE_INFRA;
+ }
+
+ 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).
+ */
+ for (i = 0; i < 32; i++)
+ ssid[i] = rand() & 0xFF;
+ 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");
+ }
+ }
+}
+
+
+static int wpa_driver_wext_deauthenticate(void *priv, const u8 *addr,
+ int reason_code)
+{
+ struct wpa_driver_wext_data *drv = priv;
+ int ret;
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+ ret = wpa_driver_wext_mlme(drv, addr, IW_MLME_DEAUTH, reason_code);
+ wpa_driver_wext_disconnect(drv);
+ return ret;
+}
+
+
+static int wpa_driver_wext_disassociate(void *priv, const u8 *addr,
+ int reason_code)
+{
+ struct wpa_driver_wext_data *drv = priv;
+ int ret;
+ wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+ ret = wpa_driver_wext_mlme(drv, addr, IW_MLME_DISASSOC, reason_code);
+ wpa_driver_wext_disconnect(drv);
+ return ret;
+}
+
+
+static int wpa_driver_wext_set_gen_ie(void *priv, const u8 *ie,
+ size_t ie_len)
+{
+ struct wpa_driver_wext_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;
+
+ if (ioctl(drv->ioctl_sock, SIOCSIWGENIE, &iwr) < 0) {
+ perror("ioctl[SIOCSIWGENIE]");
+ ret = -1;
+ }
+
+ return ret;
+}
+
+
+int wpa_driver_wext_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:
+ return 0;
+ }
+}
+
+
+int wpa_driver_wext_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;
+ }
+}
+
+
+static int
+wpa_driver_wext_auth_alg_fallback(struct wpa_driver_wext_data *drv,
+ struct wpa_driver_associate_params *params)
+{
+ struct iwreq iwr;
+ int ret = 0;
+
+ wpa_printf(MSG_DEBUG, "WEXT: Driver did not support "
+ "SIOCSIWAUTH for AUTH_ALG, trying SIOCSIWENCODE");
+
+ 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;
+
+ /*
+ * 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).
+ */
+
+ if (!drv->use_crypt) {
+ iwr.u.encoding.flags |= IW_ENCODE_DISABLED;
+ } else {
+ if (params->auth_alg & WPA_AUTH_ALG_OPEN)
+ iwr.u.encoding.flags |= IW_ENCODE_OPEN;
+ if (params->auth_alg & WPA_AUTH_ALG_SHARED)
+ iwr.u.encoding.flags |= IW_ENCODE_RESTRICTED;
+ }
+
+ if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) {
+ perror("ioctl[SIOCSIWENCODE]");
+ ret = -1;
+ }
+
+ return ret;
+}
+
+
+int wpa_driver_wext_associate(void *priv,
+ struct wpa_driver_associate_params *params)
+{
+ struct wpa_driver_wext_data *drv = priv;
+ int ret = 0;
+ int allow_unencrypted_eapol;
+ int value;
+
+ 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.
+ */
+ if (drv->auth_alg_fallback &&
+ wpa_driver_wext_auth_alg_fallback(drv, params) < 0)
+ ret = -1;
+
+ if (!params->bssid &&
+ wpa_driver_wext_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_wext_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_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;
+ 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_wext_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_wext_set_psk(drv, params->psk) < 0)
+ ret = -1;
+ if (wpa_driver_wext_set_auth_param(drv,
+ IW_AUTH_RX_UNENCRYPTED_EAPOL,
+ allow_unencrypted_eapol) < 0)
+ ret = -1;
+#ifdef CONFIG_IEEE80211W
+ switch (params->mgmt_frame_protection) {
+ case NO_MGMT_FRAME_PROTECTION:
+ value = IW_AUTH_MFP_DISABLED;
+ break;
+ case MGMT_FRAME_PROTECTION_OPTIONAL:
+ value = IW_AUTH_MFP_OPTIONAL;
+ break;
+ case MGMT_FRAME_PROTECTION_REQUIRED:
+ value = IW_AUTH_MFP_REQUIRED;
+ break;
+ };
+ if (wpa_driver_wext_set_auth_param(drv, IW_AUTH_MFP, value) < 0)
+ ret = -1;
+#endif /* CONFIG_IEEE80211W */
+ if (params->freq && wpa_driver_wext_set_freq(drv, params->freq) < 0)
+ ret = -1;
+ 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;
+}
+
+
+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 & WPA_AUTH_ALG_OPEN)
+ algs |= IW_AUTH_ALG_OPEN_SYSTEM;
+ if (auth_alg & WPA_AUTH_ALG_SHARED)
+ algs |= IW_AUTH_ALG_SHARED_KEY;
+ if (auth_alg & WPA_AUTH_ALG_LEAP)
+ algs |= IW_AUTH_ALG_LEAP;
+ if (algs == 0) {
+ /* at least one algorithm should be set */
+ algs = IW_AUTH_ALG_OPEN_SYSTEM;
+ }
+
+ res = wpa_driver_wext_set_auth_param(drv, IW_AUTH_80211_AUTH_ALG,
+ algs);
+ drv->auth_alg_fallback = res == -2;
+ return res;
+}
+
+
+/**
+ * wpa_driver_wext_set_mode - Set wireless mode (infra/adhoc), SIOCSIWMODE
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * @mode: 0 = infra/BSS (associate with an AP), 1 = adhoc/IBSS
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_driver_wext_set_mode(void *priv, int mode)
+{
+ struct wpa_driver_wext_data *drv = priv;
+ struct iwreq iwr;
+ int ret = -1;
+ unsigned int new_mode = mode ? IW_MODE_ADHOC : IW_MODE_INFRA;
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+ iwr.u.mode = new_mode;
+ if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) == 0) {
+ ret = 0;
+ goto done;
+ }
+
+ if (errno != EBUSY) {
+ perror("ioctl[SIOCSIWMODE]");
+ goto done;
+ }
+
+ /* mac80211 doesn't allow mode changes while the device is up, so if
+ * the device isn't in the mode we're about to change to, take device
+ * down, try to set the mode again, and bring it back up.
+ */
+ if (ioctl(drv->ioctl_sock, SIOCGIWMODE, &iwr) < 0) {
+ perror("ioctl[SIOCGIWMODE]");
+ goto done;
+ }
+
+ if (iwr.u.mode == new_mode) {
+ ret = 0;
+ goto done;
+ }
+
+ 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)
+ perror("ioctl[SIOCSIWMODE]");
+ else
+ ret = 0;
+
+ (void) linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1);
+ }
+
+done:
+ return ret;
+}
+
+
+static int wpa_driver_wext_pmksa(struct wpa_driver_wext_data *drv,
+ u32 cmd, const u8 *bssid, const u8 *pmkid)
+{
+ 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;
+}
+
+
+static int wpa_driver_wext_add_pmkid(void *priv, const u8 *bssid,
+ const u8 *pmkid)
+{
+ struct wpa_driver_wext_data *drv = priv;
+ return wpa_driver_wext_pmksa(drv, IW_PMKSA_ADD, bssid, pmkid);
+}
+
+
+static int wpa_driver_wext_remove_pmkid(void *priv, const u8 *bssid,
+ const u8 *pmkid)
+{
+ struct wpa_driver_wext_data *drv = priv;
+ return wpa_driver_wext_pmksa(drv, IW_PMKSA_REMOVE, bssid, pmkid);
+}
+
+
+static int wpa_driver_wext_flush_pmkid(void *priv)
+{
+ struct wpa_driver_wext_data *drv = priv;
+ return wpa_driver_wext_pmksa(drv, IW_PMKSA_FLUSH, NULL, NULL);
+}
+
+
+int wpa_driver_wext_get_capa(void *priv, struct wpa_driver_capa *capa)
+{
+ struct wpa_driver_wext_data *drv = priv;
+ if (!drv->has_capability)
+ return -1;
+ os_memcpy(capa, &drv->capa, sizeof(*capa));
+ return 0;
+}
+
+
+int wpa_driver_wext_alternative_ifindex(struct wpa_driver_wext_data *drv,
+ const char *ifname)
+{
+ if (ifname == NULL) {
+ drv->ifindex2 = -1;
+ return 0;
+ }
+
+ drv->ifindex2 = if_nametoindex(ifname);
+ if (drv->ifindex2 <= 0)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "Added alternative ifindex %d (%s) for "
+ "wireless events", drv->ifindex2, ifname);
+
+ return 0;
+}
+
+
+int wpa_driver_wext_set_operstate(void *priv, int state)
+{
+ struct wpa_driver_wext_data *drv = priv;
+
+ 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);
+}
+
+
+int wpa_driver_wext_get_version(struct wpa_driver_wext_data *drv)
+{
+ return drv->we_version_compiled;
+}
+
+
+static const char * wext_get_radio_name(void *priv)
+{
+ struct wpa_driver_wext_data *drv = priv;
+ return drv->phyname;
+}
+
+
+const struct wpa_driver_ops wpa_driver_wext_ops = {
+ .name = "wext",
+ .desc = "Linux wireless extensions (generic)",
+ .get_bssid = wpa_driver_wext_get_bssid,
+ .get_ssid = wpa_driver_wext_get_ssid,
+ .set_key = wpa_driver_wext_set_key,
+ .set_countermeasures = wpa_driver_wext_set_countermeasures,
+ .scan2 = wpa_driver_wext_scan,
+ .get_scan_results2 = wpa_driver_wext_get_scan_results,
+ .deauthenticate = wpa_driver_wext_deauthenticate,
+ .disassociate = wpa_driver_wext_disassociate,
+ .associate = wpa_driver_wext_associate,
+ .init = wpa_driver_wext_init,
+ .deinit = wpa_driver_wext_deinit,
+ .add_pmkid = wpa_driver_wext_add_pmkid,
+ .remove_pmkid = wpa_driver_wext_remove_pmkid,
+ .flush_pmkid = wpa_driver_wext_flush_pmkid,
+ .get_capa = wpa_driver_wext_get_capa,
+ .set_operstate = wpa_driver_wext_set_operstate,
+ .get_radio_name = wext_get_radio_name,
+};
diff --git a/src/drivers/driver_wext.h b/src/drivers/driver_wext.h
new file mode 100644
index 0000000..89c13eb
--- /dev/null
+++ b/src/drivers/driver_wext.h
@@ -0,0 +1,87 @@
+/*
+ * WPA Supplicant - driver_wext exported functions
+ * Copyright (c) 2003-2005, 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 DRIVER_WEXT_H
+#define DRIVER_WEXT_H
+
+#include <net/if.h>
+
+struct wpa_driver_wext_data {
+ void *ctx;
+ struct netlink_data *netlink;
+ int ioctl_sock;
+ int mlme_sock;
+ char ifname[IFNAMSIZ + 1];
+ char phyname[32];
+ int ifindex;
+ int ifindex2;
+ int if_removed;
+ int if_disabled;
+ struct rfkill_data *rfkill;
+ 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;
+
+ int cfg80211; /* whether driver is using cfg80211 */
+
+ u8 max_level;
+};
+
+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(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, 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);
+
+int wpa_driver_wext_alternative_ifindex(struct wpa_driver_wext_data *drv,
+ const char *ifname);
+
+void * wpa_driver_wext_init(void *ctx, const char *ifname);
+void wpa_driver_wext_deinit(void *priv);
+
+int wpa_driver_wext_set_operstate(void *priv, int state);
+int wpa_driver_wext_get_version(struct wpa_driver_wext_data *drv);
+
+int wpa_driver_wext_associate(void *priv,
+ struct wpa_driver_associate_params *params);
+int wpa_driver_wext_get_capa(void *priv, struct wpa_driver_capa *capa);
+int wpa_driver_wext_set_auth_param(struct wpa_driver_wext_data *drv,
+ int idx, u32 value);
+int wpa_driver_wext_cipher2wext(int cipher);
+int wpa_driver_wext_keymgmt2wext(int keymgmt);
+
+#endif /* DRIVER_WEXT_H */
diff --git a/src/drivers/driver_wired.c b/src/drivers/driver_wired.c
new file mode 100644
index 0000000..618db26
--- /dev/null
+++ b/src/drivers/driver_wired.c
@@ -0,0 +1,629 @@
+/*
+ * 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
+ * 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 <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__) || defined(__FreeBSD_kernel__)
+#include <net/if_dl.h>
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) */
+#ifdef __sun__
+#include <sys/sockio.h>
+#endif /* __sun__ */
+
+#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;
+ 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, u32 flags)
+{
+ 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;
+ return 0;
+}
+
+
+static int wpa_driver_wired_get_bssid(void *priv, u8 *bssid)
+{
+ /* Report PAE group address as the "BSSID" for wired connection. */
+ os_memcpy(bssid, pae_group_addr, ETH_ALEN);
+ return 0;
+}
+
+
+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;
+ int s;
+
+ s = socket(PF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ perror("socket");
+ return -1;
+ }
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+ if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
+ perror("ioctl[SIOCGIFFLAGS]");
+ close(s);
+ return -1;
+ }
+ close(s);
+ *flags = ifr.ifr_flags & 0xffff;
+ return 0;
+}
+
+
+static int wpa_driver_wired_set_ifflags(const char *ifname, int flags)
+{
+ struct ifreq ifr;
+ int s;
+
+ s = socket(PF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ perror("socket");
+ return -1;
+ }
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+ ifr.ifr_flags = flags & 0xffff;
+ if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
+ perror("ioctl[SIOCSIFFLAGS]");
+ close(s);
+ return -1;
+ }
+ close(s);
+ return 0;
+}
+
+
+static int wpa_driver_wired_multi(const char *ifname, const u8 *addr, int add)
+{
+ struct ifreq ifr;
+ int s;
+
+#ifdef __sun__
+ return -1;
+#endif /* __sun__ */
+
+ s = socket(PF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ perror("socket");
+ return -1;
+ }
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+#ifdef __linux__
+ ifr.ifr_hwaddr.sa_family = AF_UNSPEC;
+ os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN);
+#endif /* __linux__ */
+#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+ {
+ struct sockaddr_dl *dlp;
+ dlp = (struct sockaddr_dl *) &ifr.ifr_addr;
+ dlp->sdl_len = sizeof(struct sockaddr_dl);
+ dlp->sdl_family = AF_LINK;
+ dlp->sdl_index = 0;
+ dlp->sdl_nlen = 0;
+ dlp->sdl_alen = ETH_ALEN;
+ dlp->sdl_slen = 0;
+ os_memcpy(LLADDR(dlp), addr, ETH_ALEN);
+ }
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
+#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
+ {
+ struct sockaddr *sap;
+ sap = (struct sockaddr *) &ifr.ifr_addr;
+ sap->sa_len = sizeof(struct sockaddr);
+ sap->sa_family = AF_UNSPEC;
+ os_memcpy(sap->sa_data, addr, ETH_ALEN);
+ }
+#endif /* defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) */
+
+ if (ioctl(s, add ? SIOCADDMULTI : SIOCDELMULTI, (caddr_t) &ifr) < 0) {
+ perror("ioctl[SIOC{ADD/DEL}MULTI]");
+ close(s);
+ return -1;
+ }
+ close(s);
+ return 0;
+}
+
+
+static void * wpa_driver_wired_init(void *ctx, const char *ifname)
+{
+ struct wpa_driver_wired_data *drv;
+ int flags;
+
+ drv = os_zalloc(sizeof(*drv));
+ if (drv == NULL)
+ return NULL;
+ os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+ drv->ctx = ctx;
+
+#ifdef __linux__
+ drv->pf_sock = socket(PF_PACKET, SOCK_DGRAM, 0);
+ if (drv->pf_sock < 0)
+ perror("socket(PF_PACKET)");
+#else /* __linux__ */
+ 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 (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;
+ } else if (wpa_driver_wired_multi(ifname, pae_group_addr, 1) == 0) {
+ wpa_printf(MSG_DEBUG, "%s: Added multicast membership with "
+ "SIOCADDMULTI", __func__);
+ drv->multi = 1;
+ } else if (wpa_driver_wired_get_ifflags(ifname, &flags) < 0) {
+ wpa_printf(MSG_INFO, "%s: Could not get interface "
+ "flags", __func__);
+ os_free(drv);
+ return NULL;
+ } else if (flags & IFF_ALLMULTI) {
+ wpa_printf(MSG_DEBUG, "%s: Interface is already configured "
+ "for multicast", __func__);
+ } else if (wpa_driver_wired_set_ifflags(ifname,
+ flags | IFF_ALLMULTI) < 0) {
+ wpa_printf(MSG_INFO, "%s: Failed to enable allmulti",
+ __func__);
+ os_free(drv);
+ return NULL;
+ } else {
+ wpa_printf(MSG_DEBUG, "%s: Enabled allmulti mode",
+ __func__);
+ drv->iff_allmulti = 1;
+ }
+
+ return drv;
+}
+
+
+static void wpa_driver_wired_deinit(void *priv)
+{
+ struct wpa_driver_wired_data *drv = priv;
+ int flags;
+
+ if (drv->membership &&
+ 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__);
+ }
+
+ if (drv->multi &&
+ wpa_driver_wired_multi(drv->ifname, pae_group_addr, 0) < 0) {
+ wpa_printf(MSG_DEBUG, "%s: Failed to remove PAE multicast "
+ "group (SIOCDELMULTI)", __func__);
+ }
+
+ if (drv->iff_allmulti &&
+ (wpa_driver_wired_get_ifflags(drv->ifname, &flags) < 0 ||
+ wpa_driver_wired_set_ifflags(drv->ifname,
+ flags & ~IFF_ALLMULTI) < 0)) {
+ wpa_printf(MSG_DEBUG, "%s: Failed to disable allmulti mode",
+ __func__);
+ }
+
+ if (drv->iff_up &&
+ wpa_driver_wired_get_ifflags(drv->ifname, &flags) == 0 &&
+ (flags & IFF_UP) &&
+ wpa_driver_wired_set_ifflags(drv->ifname, flags & ~IFF_UP) < 0) {
+ wpa_printf(MSG_DEBUG, "%s: Failed to set the interface down",
+ __func__);
+ }
+
+ if (drv->pf_sock != -1)
+ close(drv->pf_sock);
+
+ os_free(drv);
+}
+
+
+const struct wpa_driver_ops wpa_driver_wired_ops = {
+ .name = "wired",
+ .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
new file mode 100644
index 0000000..b710778
--- /dev/null
+++ b/src/drivers/drivers.c
@@ -0,0 +1,114 @@
+/*
+ * Driver interface list
+ * Copyright (c) 2004-2005, 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"
+
+
+#ifdef CONFIG_DRIVER_WEXT
+extern struct wpa_driver_ops wpa_driver_wext_ops; /* driver_wext.c */
+#endif /* CONFIG_DRIVER_WEXT */
+#ifdef CONFIG_DRIVER_NL80211
+extern struct wpa_driver_ops wpa_driver_nl80211_ops; /* driver_nl80211.c */
+#endif /* CONFIG_DRIVER_NL80211 */
+#ifdef CONFIG_DRIVER_HOSTAP
+extern struct wpa_driver_ops wpa_driver_hostap_ops; /* driver_hostap.c */
+#endif /* CONFIG_DRIVER_HOSTAP */
+#ifdef CONFIG_DRIVER_MADWIFI
+extern struct wpa_driver_ops wpa_driver_madwifi_ops; /* driver_madwifi.c */
+#endif /* CONFIG_DRIVER_MADWIFI */
+#ifdef CONFIG_DRIVER_BROADCOM
+extern struct wpa_driver_ops wpa_driver_broadcom_ops; /* driver_broadcom.c */
+#endif /* CONFIG_DRIVER_BROADCOM */
+#ifdef CONFIG_DRIVER_BSD
+extern struct wpa_driver_ops wpa_driver_bsd_ops; /* driver_bsd.c */
+#endif /* CONFIG_DRIVER_BSD */
+#ifdef CONFIG_DRIVER_NDIS
+extern struct wpa_driver_ops wpa_driver_ndis_ops; /* driver_ndis.c */
+#endif /* CONFIG_DRIVER_NDIS */
+#ifdef CONFIG_DRIVER_WIRED
+extern struct wpa_driver_ops wpa_driver_wired_ops; /* driver_wired.c */
+#endif /* CONFIG_DRIVER_WIRED */
+#ifdef CONFIG_DRIVER_TEST
+extern struct wpa_driver_ops wpa_driver_test_ops; /* driver_test.c */
+#endif /* CONFIG_DRIVER_TEST */
+#ifdef CONFIG_DRIVER_RALINK
+extern struct wpa_driver_ops wpa_driver_ralink_ops; /* driver_ralink.c */
+#endif /* CONFIG_DRIVER_RALINK */
+#ifdef CONFIG_DRIVER_OSX
+extern struct wpa_driver_ops wpa_driver_osx_ops; /* driver_osx.m */
+#endif /* CONFIG_DRIVER_OSX */
+#ifdef CONFIG_DRIVER_IPHONE
+extern struct wpa_driver_ops wpa_driver_iphone_ops; /* driver_iphone.m */
+#endif /* CONFIG_DRIVER_IPHONE */
+#ifdef CONFIG_DRIVER_ROBOSWITCH
+/* 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_drivers[] =
+{
+#ifdef CONFIG_DRIVER_WEXT
+ &wpa_driver_wext_ops,
+#endif /* CONFIG_DRIVER_WEXT */
+#ifdef CONFIG_DRIVER_NL80211
+ &wpa_driver_nl80211_ops,
+#endif /* CONFIG_DRIVER_NL80211 */
+#ifdef CONFIG_DRIVER_HOSTAP
+ &wpa_driver_hostap_ops,
+#endif /* CONFIG_DRIVER_HOSTAP */
+#ifdef CONFIG_DRIVER_MADWIFI
+ &wpa_driver_madwifi_ops,
+#endif /* CONFIG_DRIVER_MADWIFI */
+#ifdef CONFIG_DRIVER_BROADCOM
+ &wpa_driver_broadcom_ops,
+#endif /* CONFIG_DRIVER_BROADCOM */
+#ifdef CONFIG_DRIVER_BSD
+ &wpa_driver_bsd_ops,
+#endif /* CONFIG_DRIVER_BSD */
+#ifdef CONFIG_DRIVER_NDIS
+ &wpa_driver_ndis_ops,
+#endif /* CONFIG_DRIVER_NDIS */
+#ifdef CONFIG_DRIVER_WIRED
+ &wpa_driver_wired_ops,
+#endif /* CONFIG_DRIVER_WIRED */
+#ifdef CONFIG_DRIVER_TEST
+ &wpa_driver_test_ops,
+#endif /* CONFIG_DRIVER_TEST */
+#ifdef CONFIG_DRIVER_RALINK
+ &wpa_driver_ralink_ops,
+#endif /* CONFIG_DRIVER_RALINK */
+#ifdef CONFIG_DRIVER_OSX
+ &wpa_driver_osx_ops,
+#endif /* CONFIG_DRIVER_OSX */
+#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 0000000..ebf39c8
--- /dev/null
+++ b/src/drivers/drivers.mak
@@ -0,0 +1,180 @@
+##### CLEAR VARS
+
+DRV_CFLAGS =
+DRV_WPA_CFLAGS =
+DRV_AP_CFLAGS =
+DRV_OBJS =
+DRV_WPA_OBJS =
+DRV_AP_OBJS =
+DRV_LIBS =
+DRV_WPA_LIBS =
+DRV_AP_LIBS =
+
+##### 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
+NEED_RFKILL=y
+ifdef CONFIG_LIBNL_TINY
+DRV_LIBS += -lnl-tiny
+else
+DRV_LIBS += -lnl
+endif
+
+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
+NEED_RFKILL=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_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
+NEED_RFKILL=y
+endif
+
+ifdef NEED_NETLINK
+DRV_OBJS += ../src/drivers/netlink.o
+endif
+
+ifdef NEED_LINUX_IOCTL
+DRV_OBJS += ../src/drivers/linux_ioctl.o
+endif
+
+ifdef NEED_RFKILL
+DRV_OBJS += ../src/drivers/rfkill.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/drivers.mk b/src/drivers/drivers.mk
new file mode 100644
index 0000000..c690e1c
--- /dev/null
+++ b/src/drivers/drivers.mk
@@ -0,0 +1,183 @@
+##### CLEAR VARS
+
+DRV_CFLAGS =
+DRV_WPA_CFLAGS =
+DRV_AP_CFLAGS =
+DRV_OBJS =
+DRV_WPA_OBJS =
+DRV_AP_OBJS =
+DRV_LIBS =
+DRV_WPA_LIBS =
+DRV_AP_LIBS =
+
+##### COMMON DRIVERS
+
+ifdef CONFIG_DRIVER_HOSTAP
+DRV_CFLAGS += -DCONFIG_DRIVER_HOSTAP
+DRV_OBJS += src/drivers/driver_hostap.c
+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.c
+endif
+
+ifdef CONFIG_DRIVER_MADWIFI
+DRV_CFLAGS += -DCONFIG_DRIVER_MADWIFI
+DRV_OBJS += src/drivers/driver_madwifi.c
+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.c
+DRV_OBJS += src/utils/radiotap.c
+NEED_SME=y
+NEED_AP_MLME=y
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+NEED_RFKILL=y
+ifdef CONFIG_LIBNL_TINY
+DRV_LIBS += -lnl-tiny
+else
+DRV_LIBS += -lnl
+endif
+
+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.c
+CONFIG_L2_FREEBSD=y
+CONFIG_DNET_PCAP=y
+endif
+
+ifdef CONFIG_DRIVER_TEST
+DRV_CFLAGS += -DCONFIG_DRIVER_TEST
+DRV_OBJS += src/drivers/driver_test.c
+NEED_AP_MLME=y
+endif
+
+ifdef CONFIG_DRIVER_NONE
+DRV_CFLAGS += -DCONFIG_DRIVER_NONE
+DRV_OBJS += src/drivers/driver_none.c
+endif
+
+##### PURE AP DRIVERS
+
+ifdef CONFIG_DRIVER_ATHEROS
+DRV_AP_CFLAGS += -DCONFIG_DRIVER_ATHEROS
+DRV_AP_OBJS += src/drivers/driver_atheros.c
+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
+NEED_RFKILL=y
+endif
+
+ifdef CONFIG_DRIVER_RALINK
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_RALINK
+DRV_WPA_OBJS += src/drivers/driver_ralink.c
+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.c
+endif
+
+ifdef CONFIG_DRIVER_NDIS
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_NDIS
+DRV_WPA_OBJS += src/drivers/driver_ndis.c
+ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+DRV_WPA_OBJS += src/drivers/driver_ndis_.c
+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.c
+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.c
+DRV_WPA_OBJS += src/drivers/MobileApple80211.c
+DRV_WPA_LDFLAGS += -framework CoreFoundation
+endif
+
+ifdef CONFIG_DRIVER_ROBOSWITCH
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_ROBOSWITCH
+DRV_WPA_OBJS += src/drivers/driver_roboswitch.c
+endif
+
+ifdef CONFIG_WIRELESS_EXTENSION
+DRV_WPA_CFLAGS += -DCONFIG_WIRELESS_EXTENSION
+DRV_WPA_OBJS += src/drivers/driver_wext.c
+NEED_RFKILL=y
+endif
+
+ifdef NEED_NETLINK
+DRV_OBJS += src/drivers/netlink.c
+endif
+
+ifdef NEED_LINUX_IOCTL
+DRV_OBJS += src/drivers/linux_ioctl.c
+endif
+
+ifdef NEED_RFKILL
+DRV_OBJS += src/drivers/rfkill.c
+endif
+
+ifdef CONFIG_DRIVER_CUSTOM
+DRV_CFLAGS += -DCONFIG_DRIVER_CUSTOM
+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 0000000..0d6cf54
--- /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 0000000..a555738
--- /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/ndis_events.c b/src/drivers/ndis_events.c
new file mode 100644
index 0000000..f6eaa7c
--- /dev/null
+++ b/src/drivers/ndis_events.c
@@ -0,0 +1,808 @@
+/*
+ * ndis_events - Receive NdisMIndicateStatus() events using WMI
+ * Copyright (c) 2004-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
+ * 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.
+ */
+
+#define _WIN32_WINNT 0x0400
+
+#include "includes.h"
+
+#ifndef COBJMACROS
+#define COBJMACROS
+#endif /* COBJMACROS */
+#include <wbemidl.h>
+
+#include "common.h"
+
+
+static int wmi_refcnt = 0;
+static int wmi_first = 1;
+
+struct ndis_events_data {
+ IWbemObjectSink sink;
+ IWbemObjectSinkVtbl sink_vtbl;
+
+ IWbemServices *pSvc;
+ IWbemLocator *pLoc;
+
+ HANDLE read_pipe, write_pipe, event_avail;
+ UINT ref;
+ int terminating;
+ char *ifname; /* {GUID..} */
+ WCHAR *adapter_desc;
+};
+
+#define BstrAlloc(x) (x) ? SysAllocString(x) : NULL
+#define BstrFree(x) if (x) SysFreeString(x)
+
+/* WBEM / WMI wrapper functions, to perform in-place conversion of WCHARs to
+ * BSTRs */
+HRESULT STDMETHODCALLTYPE call_IWbemServices_ExecQuery(
+ IWbemServices *pSvc, LPCWSTR strQueryLanguage, LPCWSTR strQuery,
+ long lFlags, IWbemContext *pCtx, IEnumWbemClassObject **ppEnum)
+{
+ BSTR bsQueryLanguage, bsQuery;
+ HRESULT hr;
+
+ bsQueryLanguage = BstrAlloc(strQueryLanguage);
+ bsQuery = BstrAlloc(strQuery);
+
+ hr = IWbemServices_ExecQuery(pSvc, bsQueryLanguage, bsQuery, lFlags,
+ pCtx, ppEnum);
+
+ BstrFree(bsQueryLanguage);
+ BstrFree(bsQuery);
+
+ return hr;
+}
+
+
+HRESULT STDMETHODCALLTYPE call_IWbemServices_ExecNotificationQueryAsync(
+ IWbemServices *pSvc, LPCWSTR strQueryLanguage, LPCWSTR strQuery,
+ long lFlags, IWbemContext *pCtx, IWbemObjectSink *pResponseHandler)
+{
+ BSTR bsQueryLanguage, bsQuery;
+ HRESULT hr;
+
+ bsQueryLanguage = BstrAlloc(strQueryLanguage);
+ bsQuery = BstrAlloc(strQuery);
+
+ hr = IWbemServices_ExecNotificationQueryAsync(pSvc, bsQueryLanguage,
+ bsQuery, lFlags, pCtx,
+ pResponseHandler);
+
+ BstrFree(bsQueryLanguage);
+ BstrFree(bsQuery);
+
+ return hr;
+}
+
+
+HRESULT STDMETHODCALLTYPE call_IWbemLocator_ConnectServer(
+ IWbemLocator *pLoc, LPCWSTR strNetworkResource, LPCWSTR strUser,
+ LPCWSTR strPassword, LPCWSTR strLocale, long lSecurityFlags,
+ LPCWSTR strAuthority, IWbemContext *pCtx, IWbemServices **ppNamespace)
+{
+ BSTR bsNetworkResource, bsUser, bsPassword, bsLocale, bsAuthority;
+ HRESULT hr;
+
+ bsNetworkResource = BstrAlloc(strNetworkResource);
+ bsUser = BstrAlloc(strUser);
+ bsPassword = BstrAlloc(strPassword);
+ bsLocale = BstrAlloc(strLocale);
+ bsAuthority = BstrAlloc(strAuthority);
+
+ hr = IWbemLocator_ConnectServer(pLoc, bsNetworkResource, bsUser,
+ bsPassword, bsLocale, lSecurityFlags,
+ bsAuthority, pCtx, ppNamespace);
+
+ BstrFree(bsNetworkResource);
+ BstrFree(bsUser);
+ BstrFree(bsPassword);
+ BstrFree(bsLocale);
+ BstrFree(bsAuthority);
+
+ return hr;
+}
+
+
+enum event_types { EVENT_CONNECT, EVENT_DISCONNECT, EVENT_MEDIA_SPECIFIC,
+ EVENT_ADAPTER_ARRIVAL, EVENT_ADAPTER_REMOVAL };
+
+static int ndis_events_get_adapter(struct ndis_events_data *events,
+ const char *ifname, const char *desc);
+
+
+static int ndis_events_constructor(struct ndis_events_data *events)
+{
+ events->ref = 1;
+
+ if (!CreatePipe(&events->read_pipe, &events->write_pipe, NULL, 512)) {
+ wpa_printf(MSG_ERROR, "CreatePipe() failed: %d",
+ (int) GetLastError());
+ return -1;
+ }
+ events->event_avail = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (events->event_avail == NULL) {
+ wpa_printf(MSG_ERROR, "CreateEvent() failed: %d",
+ (int) GetLastError());
+ CloseHandle(events->read_pipe);
+ CloseHandle(events->write_pipe);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void ndis_events_destructor(struct ndis_events_data *events)
+{
+ CloseHandle(events->read_pipe);
+ CloseHandle(events->write_pipe);
+ CloseHandle(events->event_avail);
+ IWbemServices_Release(events->pSvc);
+ IWbemLocator_Release(events->pLoc);
+ if (--wmi_refcnt == 0)
+ CoUninitialize();
+}
+
+
+static HRESULT STDMETHODCALLTYPE
+ndis_events_query_interface(IWbemObjectSink *this, REFIID riid, void **obj)
+{
+ *obj = NULL;
+
+ if (IsEqualIID(riid, &IID_IUnknown) ||
+ IsEqualIID(riid, &IID_IWbemObjectSink)) {
+ *obj = this;
+ IWbemObjectSink_AddRef(this);
+ return NOERROR;
+ }
+
+ return E_NOINTERFACE;
+}
+
+
+static ULONG STDMETHODCALLTYPE ndis_events_add_ref(IWbemObjectSink *this)
+{
+ struct ndis_events_data *events = (struct ndis_events_data *) this;
+ return ++events->ref;
+}
+
+
+static ULONG STDMETHODCALLTYPE ndis_events_release(IWbemObjectSink *this)
+{
+ struct ndis_events_data *events = (struct ndis_events_data *) this;
+
+ if (--events->ref != 0)
+ return events->ref;
+
+ ndis_events_destructor(events);
+ wpa_printf(MSG_DEBUG, "ndis_events: terminated");
+ os_free(events->adapter_desc);
+ os_free(events->ifname);
+ os_free(events);
+ return 0;
+}
+
+
+static int ndis_events_send_event(struct ndis_events_data *events,
+ enum event_types type,
+ char *data, size_t data_len)
+{
+ char buf[512], *pos, *end;
+ int _type;
+ DWORD written;
+
+ end = buf + sizeof(buf);
+ _type = (int) type;
+ os_memcpy(buf, &_type, sizeof(_type));
+ pos = buf + sizeof(_type);
+
+ if (data) {
+ if (2 + data_len > (size_t) (end - pos)) {
+ wpa_printf(MSG_DEBUG, "Not enough room for send_event "
+ "data (%d)", data_len);
+ return -1;
+ }
+ *pos++ = data_len >> 8;
+ *pos++ = data_len & 0xff;
+ os_memcpy(pos, data, data_len);
+ pos += data_len;
+ }
+
+ if (WriteFile(events->write_pipe, buf, pos - buf, &written, NULL)) {
+ SetEvent(events->event_avail);
+ return 0;
+ }
+ wpa_printf(MSG_INFO, "WriteFile() failed: %d", (int) GetLastError());
+ return -1;
+}
+
+
+static void ndis_events_media_connect(struct ndis_events_data *events)
+{
+ wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaConnect");
+ ndis_events_send_event(events, EVENT_CONNECT, NULL, 0);
+}
+
+
+static void ndis_events_media_disconnect(struct ndis_events_data *events)
+{
+ wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaDisconnect");
+ ndis_events_send_event(events, EVENT_DISCONNECT, NULL, 0);
+}
+
+
+static void ndis_events_media_specific(struct ndis_events_data *events,
+ IWbemClassObject *pObj)
+{
+ VARIANT vt;
+ HRESULT hr;
+ LONG lower, upper, k;
+ UCHAR ch;
+ char *data, *pos;
+ size_t data_len;
+
+ wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaSpecificIndication");
+
+ /* This is the StatusBuffer from NdisMIndicateStatus() call */
+ hr = IWbemClassObject_Get(pObj, L"NdisStatusMediaSpecificIndication",
+ 0, &vt, NULL, NULL);
+ if (FAILED(hr)) {
+ wpa_printf(MSG_DEBUG, "Could not get "
+ "NdisStatusMediaSpecificIndication from "
+ "the object?!");
+ return;
+ }
+
+ SafeArrayGetLBound(V_ARRAY(&vt), 1, &lower);
+ SafeArrayGetUBound(V_ARRAY(&vt), 1, &upper);
+ data_len = upper - lower + 1;
+ data = os_malloc(data_len);
+ if (data == NULL) {
+ wpa_printf(MSG_DEBUG, "Failed to allocate buffer for event "
+ "data");
+ VariantClear(&vt);
+ return;
+ }
+
+ pos = data;
+ for (k = lower; k <= upper; k++) {
+ SafeArrayGetElement(V_ARRAY(&vt), &k, &ch);
+ *pos++ = ch;
+ }
+ wpa_hexdump(MSG_DEBUG, "MediaSpecificEvent", (u8 *) data, data_len);
+
+ VariantClear(&vt);
+
+ ndis_events_send_event(events, EVENT_MEDIA_SPECIFIC, data, data_len);
+
+ os_free(data);
+}
+
+
+static void ndis_events_adapter_arrival(struct ndis_events_data *events)
+{
+ wpa_printf(MSG_DEBUG, "MSNdis_NotifyAdapterArrival");
+ ndis_events_send_event(events, EVENT_ADAPTER_ARRIVAL, NULL, 0);
+}
+
+
+static void ndis_events_adapter_removal(struct ndis_events_data *events)
+{
+ wpa_printf(MSG_DEBUG, "MSNdis_NotifyAdapterRemoval");
+ ndis_events_send_event(events, EVENT_ADAPTER_REMOVAL, NULL, 0);
+}
+
+
+static HRESULT STDMETHODCALLTYPE
+ndis_events_indicate(IWbemObjectSink *this, long lObjectCount,
+ IWbemClassObject __RPC_FAR *__RPC_FAR *ppObjArray)
+{
+ struct ndis_events_data *events = (struct ndis_events_data *) this;
+ long i;
+
+ if (events->terminating) {
+ wpa_printf(MSG_DEBUG, "ndis_events_indicate: Ignore "
+ "indication - terminating");
+ return WBEM_NO_ERROR;
+ }
+ /* wpa_printf(MSG_DEBUG, "Notification received - %d object(s)",
+ lObjectCount); */
+
+ for (i = 0; i < lObjectCount; i++) {
+ IWbemClassObject *pObj = ppObjArray[i];
+ HRESULT hr;
+ VARIANT vtClass, vt;
+
+ hr = IWbemClassObject_Get(pObj, L"__CLASS", 0, &vtClass, NULL,
+ NULL);
+ if (FAILED(hr)) {
+ wpa_printf(MSG_DEBUG, "Failed to get __CLASS from "
+ "event.");
+ break;
+ }
+ /* wpa_printf(MSG_DEBUG, "CLASS: '%S'", vtClass.bstrVal); */
+
+ hr = IWbemClassObject_Get(pObj, L"InstanceName", 0, &vt, NULL,
+ NULL);
+ if (FAILED(hr)) {
+ wpa_printf(MSG_DEBUG, "Failed to get InstanceName "
+ "from event.");
+ VariantClear(&vtClass);
+ break;
+ }
+
+ if (wcscmp(vtClass.bstrVal,
+ L"MSNdis_NotifyAdapterArrival") == 0) {
+ wpa_printf(MSG_DEBUG, "ndis_events_indicate: Try to "
+ "update adapter description since it may "
+ "have changed with new adapter instance");
+ ndis_events_get_adapter(events, events->ifname, NULL);
+ }
+
+ if (wcscmp(events->adapter_desc, vt.bstrVal) != 0) {
+ wpa_printf(MSG_DEBUG, "ndis_events_indicate: Ignore "
+ "indication for foreign adapter: "
+ "InstanceName: '%S' __CLASS: '%S'",
+ vt.bstrVal, vtClass.bstrVal);
+ VariantClear(&vtClass);
+ VariantClear(&vt);
+ continue;
+ }
+ VariantClear(&vt);
+
+ if (wcscmp(vtClass.bstrVal,
+ L"MSNdis_StatusMediaSpecificIndication") == 0) {
+ ndis_events_media_specific(events, pObj);
+ } else if (wcscmp(vtClass.bstrVal,
+ L"MSNdis_StatusMediaConnect") == 0) {
+ ndis_events_media_connect(events);
+ } else if (wcscmp(vtClass.bstrVal,
+ L"MSNdis_StatusMediaDisconnect") == 0) {
+ ndis_events_media_disconnect(events);
+ } else if (wcscmp(vtClass.bstrVal,
+ L"MSNdis_NotifyAdapterArrival") == 0) {
+ ndis_events_adapter_arrival(events);
+ } else if (wcscmp(vtClass.bstrVal,
+ L"MSNdis_NotifyAdapterRemoval") == 0) {
+ ndis_events_adapter_removal(events);
+ } else {
+ wpa_printf(MSG_DEBUG, "Unepected event - __CLASS: "
+ "'%S'", vtClass.bstrVal);
+ }
+
+ VariantClear(&vtClass);
+ }
+
+ return WBEM_NO_ERROR;
+}
+
+
+static HRESULT STDMETHODCALLTYPE
+ndis_events_set_status(IWbemObjectSink *this, long lFlags, HRESULT hResult,
+ BSTR strParam, IWbemClassObject __RPC_FAR *pObjParam)
+{
+ return WBEM_NO_ERROR;
+}
+
+
+static int notification_query(IWbemObjectSink *pDestSink,
+ IWbemServices *pSvc, const char *class_name)
+{
+ HRESULT hr;
+ WCHAR query[256];
+
+ _snwprintf(query, 256,
+ L"SELECT * FROM %S", class_name);
+ wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
+ hr = call_IWbemServices_ExecNotificationQueryAsync(
+ pSvc, L"WQL", query, 0, 0, pDestSink);
+ if (FAILED(hr)) {
+ wpa_printf(MSG_DEBUG, "ExecNotificationQueryAsync for %s "
+ "failed with hresult of 0x%x",
+ class_name, (int) hr);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int register_async_notification(IWbemObjectSink *pDestSink,
+ IWbemServices *pSvc)
+{
+ int i;
+ const char *class_list[] = {
+ "MSNdis_StatusMediaConnect",
+ "MSNdis_StatusMediaDisconnect",
+ "MSNdis_StatusMediaSpecificIndication",
+ "MSNdis_NotifyAdapterArrival",
+ "MSNdis_NotifyAdapterRemoval",
+ NULL
+ };
+
+ for (i = 0; class_list[i]; i++) {
+ if (notification_query(pDestSink, pSvc, class_list[i]) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void ndis_events_deinit(struct ndis_events_data *events)
+{
+ events->terminating = 1;
+ IWbemServices_CancelAsyncCall(events->pSvc, &events->sink);
+ IWbemObjectSink_Release(&events->sink);
+ /*
+ * Rest of deinitialization is done in ndis_events_destructor() once
+ * all reference count drops to zero.
+ */
+}
+
+
+static int ndis_events_use_desc(struct ndis_events_data *events,
+ const char *desc)
+{
+ char *tmp, *pos;
+ size_t len;
+
+ if (desc == NULL) {
+ if (events->adapter_desc == NULL)
+ return -1;
+ /* Continue using old description */
+ return 0;
+ }
+
+ tmp = os_strdup(desc);
+ if (tmp == NULL)
+ return -1;
+
+ pos = os_strstr(tmp, " (Microsoft's Packet Scheduler)");
+ if (pos)
+ *pos = '\0';
+
+ len = os_strlen(tmp);
+ events->adapter_desc = os_malloc((len + 1) * sizeof(WCHAR));
+ if (events->adapter_desc == NULL) {
+ os_free(tmp);
+ return -1;
+ }
+ _snwprintf(events->adapter_desc, len + 1, L"%S", tmp);
+ os_free(tmp);
+ return 0;
+}
+
+
+static int ndis_events_get_adapter(struct ndis_events_data *events,
+ const char *ifname, const char *desc)
+{
+ HRESULT hr;
+ IWbemServices *pSvc;
+#define MAX_QUERY_LEN 256
+ WCHAR query[MAX_QUERY_LEN];
+ IEnumWbemClassObject *pEnumerator;
+ IWbemClassObject *pObj;
+ ULONG uReturned;
+ VARIANT vt;
+ int len, pos;
+
+ /*
+ * Try to get adapter descriptor through WMI CIMv2 Win32_NetworkAdapter
+ * to have better probability of matching with InstanceName from
+ * MSNdis events. If this fails, use the provided description.
+ */
+
+ os_free(events->adapter_desc);
+ events->adapter_desc = NULL;
+
+ hr = call_IWbemLocator_ConnectServer(
+ events->pLoc, L"ROOT\\CIMV2", NULL, NULL, 0, 0, 0, 0, &pSvc);
+ if (FAILED(hr)) {
+ wpa_printf(MSG_ERROR, "ndis_events: Could not connect to WMI "
+ "server (ROOT\\CIMV2) - error 0x%x", (int) hr);
+ return ndis_events_use_desc(events, desc);
+ }
+ wpa_printf(MSG_DEBUG, "ndis_events: Connected to ROOT\\CIMV2.");
+
+ _snwprintf(query, MAX_QUERY_LEN,
+ L"SELECT Index FROM Win32_NetworkAdapterConfiguration "
+ L"WHERE SettingID='%S'", ifname);
+ wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
+
+ hr = call_IWbemServices_ExecQuery(
+ pSvc, L"WQL", query,
+ WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
+ NULL, &pEnumerator);
+ if (!SUCCEEDED(hr)) {
+ wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface "
+ "GUID from Win32_NetworkAdapterConfiguration: "
+ "0x%x", (int) hr);
+ IWbemServices_Release(pSvc);
+ return ndis_events_use_desc(events, desc);
+ }
+
+ uReturned = 0;
+ hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1,
+ &pObj, &uReturned);
+ if (!SUCCEEDED(hr) || uReturned == 0) {
+ wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface "
+ "GUID from Win32_NetworkAdapterConfiguration: "
+ "0x%x", (int) hr);
+ IEnumWbemClassObject_Release(pEnumerator);
+ IWbemServices_Release(pSvc);
+ return ndis_events_use_desc(events, desc);
+ }
+ IEnumWbemClassObject_Release(pEnumerator);
+
+ VariantInit(&vt);
+ hr = IWbemClassObject_Get(pObj, L"Index", 0, &vt, NULL, NULL);
+ if (!SUCCEEDED(hr)) {
+ wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Index from "
+ "Win32_NetworkAdapterConfiguration: 0x%x",
+ (int) hr);
+ IWbemServices_Release(pSvc);
+ return ndis_events_use_desc(events, desc);
+ }
+
+ _snwprintf(query, MAX_QUERY_LEN,
+ L"SELECT Name,PNPDeviceID FROM Win32_NetworkAdapter WHERE "
+ L"Index=%d",
+ vt.uintVal);
+ wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
+ VariantClear(&vt);
+ IWbemClassObject_Release(pObj);
+
+ hr = call_IWbemServices_ExecQuery(
+ pSvc, L"WQL", query,
+ WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
+ NULL, &pEnumerator);
+ if (!SUCCEEDED(hr)) {
+ wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface "
+ "from Win32_NetworkAdapter: 0x%x", (int) hr);
+ IWbemServices_Release(pSvc);
+ return ndis_events_use_desc(events, desc);
+ }
+
+ uReturned = 0;
+ hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1,
+ &pObj, &uReturned);
+ if (!SUCCEEDED(hr) || uReturned == 0) {
+ wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface "
+ "from Win32_NetworkAdapter: 0x%x", (int) hr);
+ IEnumWbemClassObject_Release(pEnumerator);
+ IWbemServices_Release(pSvc);
+ return ndis_events_use_desc(events, desc);
+ }
+ IEnumWbemClassObject_Release(pEnumerator);
+
+ hr = IWbemClassObject_Get(pObj, L"Name", 0, &vt, NULL, NULL);
+ if (!SUCCEEDED(hr)) {
+ wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Name from "
+ "Win32_NetworkAdapter: 0x%x", (int) hr);
+ IWbemClassObject_Release(pObj);
+ IWbemServices_Release(pSvc);
+ return ndis_events_use_desc(events, desc);
+ }
+
+ wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::Name='%S'",
+ vt.bstrVal);
+ events->adapter_desc = _wcsdup(vt.bstrVal);
+ VariantClear(&vt);
+
+ /*
+ * Try to get even better candidate for matching with InstanceName
+ * from Win32_PnPEntity. This is needed at least for some USB cards
+ * that can change the InstanceName whenever being unplugged and
+ * plugged again.
+ */
+
+ hr = IWbemClassObject_Get(pObj, L"PNPDeviceID", 0, &vt, NULL, NULL);
+ if (!SUCCEEDED(hr)) {
+ wpa_printf(MSG_DEBUG, "ndis_events: Failed to get PNPDeviceID "
+ "from Win32_NetworkAdapter: 0x%x", (int) hr);
+ IWbemClassObject_Release(pObj);
+ IWbemServices_Release(pSvc);
+ if (events->adapter_desc == NULL)
+ return ndis_events_use_desc(events, desc);
+ return 0; /* use Win32_NetworkAdapter::Name */
+ }
+
+ wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::PNPDeviceID="
+ "'%S'", vt.bstrVal);
+
+ len = _snwprintf(query, MAX_QUERY_LEN,
+ L"SELECT Name FROM Win32_PnPEntity WHERE DeviceID='");
+ if (len < 0 || len >= MAX_QUERY_LEN - 1) {
+ VariantClear(&vt);
+ IWbemClassObject_Release(pObj);
+ IWbemServices_Release(pSvc);
+ if (events->adapter_desc == NULL)
+ return ndis_events_use_desc(events, desc);
+ return 0; /* use Win32_NetworkAdapter::Name */
+ }
+
+ /* Escape \ as \\ */
+ for (pos = 0; vt.bstrVal[pos] && len < MAX_QUERY_LEN - 2; pos++) {
+ if (vt.bstrVal[pos] == '\\') {
+ if (len >= MAX_QUERY_LEN - 3)
+ break;
+ query[len++] = '\\';
+ }
+ query[len++] = vt.bstrVal[pos];
+ }
+ query[len++] = L'\'';
+ query[len] = L'\0';
+ VariantClear(&vt);
+ IWbemClassObject_Release(pObj);
+ wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
+
+ hr = call_IWbemServices_ExecQuery(
+ pSvc, L"WQL", query,
+ WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
+ NULL, &pEnumerator);
+ if (!SUCCEEDED(hr)) {
+ wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface "
+ "Name from Win32_PnPEntity: 0x%x", (int) hr);
+ IWbemServices_Release(pSvc);
+ if (events->adapter_desc == NULL)
+ return ndis_events_use_desc(events, desc);
+ return 0; /* use Win32_NetworkAdapter::Name */
+ }
+
+ uReturned = 0;
+ hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1,
+ &pObj, &uReturned);
+ if (!SUCCEEDED(hr) || uReturned == 0) {
+ wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface "
+ "from Win32_PnPEntity: 0x%x", (int) hr);
+ IEnumWbemClassObject_Release(pEnumerator);
+ IWbemServices_Release(pSvc);
+ if (events->adapter_desc == NULL)
+ return ndis_events_use_desc(events, desc);
+ return 0; /* use Win32_NetworkAdapter::Name */
+ }
+ IEnumWbemClassObject_Release(pEnumerator);
+
+ hr = IWbemClassObject_Get(pObj, L"Name", 0, &vt, NULL, NULL);
+ if (!SUCCEEDED(hr)) {
+ wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Name from "
+ "Win32_PnPEntity: 0x%x", (int) hr);
+ IWbemClassObject_Release(pObj);
+ IWbemServices_Release(pSvc);
+ if (events->adapter_desc == NULL)
+ return ndis_events_use_desc(events, desc);
+ return 0; /* use Win32_NetworkAdapter::Name */
+ }
+
+ wpa_printf(MSG_DEBUG, "ndis_events: Win32_PnPEntity::Name='%S'",
+ vt.bstrVal);
+ os_free(events->adapter_desc);
+ events->adapter_desc = _wcsdup(vt.bstrVal);
+ VariantClear(&vt);
+
+ IWbemClassObject_Release(pObj);
+
+ IWbemServices_Release(pSvc);
+
+ if (events->adapter_desc == NULL)
+ return ndis_events_use_desc(events, desc);
+
+ return 0;
+}
+
+
+struct ndis_events_data *
+ndis_events_init(HANDLE *read_pipe, HANDLE *event_avail,
+ const char *ifname, const char *desc)
+{
+ HRESULT hr;
+ IWbemObjectSink *pSink;
+ struct ndis_events_data *events;
+
+ events = os_zalloc(sizeof(*events));
+ if (events == NULL) {
+ wpa_printf(MSG_ERROR, "Could not allocate sink for events.");
+ return NULL;
+ }
+ events->ifname = os_strdup(ifname);
+ if (events->ifname == NULL) {
+ os_free(events);
+ return NULL;
+ }
+
+ if (wmi_refcnt++ == 0) {
+ hr = CoInitializeEx(0, COINIT_MULTITHREADED);
+ if (FAILED(hr)) {
+ wpa_printf(MSG_ERROR, "CoInitializeEx() failed - "
+ "returned 0x%x", (int) hr);
+ os_free(events);
+ return NULL;
+ }
+ }
+
+ if (wmi_first) {
+ /* CoInitializeSecurity() must be called once and only once
+ * per process, so let's use wmi_first flag to protect against
+ * multiple calls. */
+ wmi_first = 0;
+
+ hr = CoInitializeSecurity(NULL, -1, NULL, NULL,
+ RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
+ RPC_C_IMP_LEVEL_IMPERSONATE,
+ NULL, EOAC_SECURE_REFS, NULL);
+ if (FAILED(hr)) {
+ wpa_printf(MSG_ERROR, "CoInitializeSecurity() failed "
+ "- returned 0x%x", (int) hr);
+ os_free(events);
+ return NULL;
+ }
+ }
+
+ hr = CoCreateInstance(&CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER,
+ &IID_IWbemLocator,
+ (LPVOID *) (void *) &events->pLoc);
+ if (FAILED(hr)) {
+ wpa_printf(MSG_ERROR, "CoCreateInstance() failed - returned "
+ "0x%x", (int) hr);
+ CoUninitialize();
+ os_free(events);
+ return NULL;
+ }
+
+ if (ndis_events_get_adapter(events, ifname, desc) < 0) {
+ CoUninitialize();
+ os_free(events);
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "ndis_events: use adapter descriptor '%S'",
+ events->adapter_desc);
+
+ hr = call_IWbemLocator_ConnectServer(
+ events->pLoc, L"ROOT\\WMI", NULL, NULL,
+ 0, 0, 0, 0, &events->pSvc);
+ if (FAILED(hr)) {
+ wpa_printf(MSG_ERROR, "Could not connect to server - error "
+ "0x%x", (int) hr);
+ CoUninitialize();
+ os_free(events->adapter_desc);
+ os_free(events);
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "Connected to ROOT\\WMI.");
+
+ ndis_events_constructor(events);
+ pSink = &events->sink;
+ pSink->lpVtbl = &events->sink_vtbl;
+ events->sink_vtbl.QueryInterface = ndis_events_query_interface;
+ events->sink_vtbl.AddRef = ndis_events_add_ref;
+ events->sink_vtbl.Release = ndis_events_release;
+ events->sink_vtbl.Indicate = ndis_events_indicate;
+ events->sink_vtbl.SetStatus = ndis_events_set_status;
+
+ if (register_async_notification(pSink, events->pSvc) < 0) {
+ wpa_printf(MSG_DEBUG, "Failed to register async "
+ "notifications");
+ ndis_events_destructor(events);
+ os_free(events->adapter_desc);
+ os_free(events);
+ return NULL;
+ }
+
+ *read_pipe = events->read_pipe;
+ *event_avail = events->event_avail;
+
+ return events;
+}
diff --git a/src/drivers/netlink.c b/src/drivers/netlink.c
new file mode 100644
index 0000000..ad15b1d
--- /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 0000000..ccf12a5
--- /dev/null
+++ b/src/drivers/netlink.h
@@ -0,0 +1,34 @@
+/*
+ * 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 ifinfomsg;
+
+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 0000000..7483a89
--- /dev/null
+++ b/src/drivers/nl80211_copy.h
@@ -0,0 +1,1939 @@
+#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?
+ */
+
+/**
+ * DOC: Frame transmission/registration support
+ *
+ * Frame transmission and registration support exists to allow userspace
+ * management entities such as wpa_supplicant react to management frames
+ * that are not being handled by the kernel. This includes, for example,
+ * certain classes of action frames that cannot be handled in the kernel
+ * for various reasons.
+ *
+ * Frame registration is done on a per-interface basis and registrations
+ * cannot be removed other than by closing the socket. It is possible to
+ * specify a registration filter to register, for example, only for a
+ * certain type of action frame. In particular with action frames, those
+ * that userspace registers for will not be returned as unhandled by the
+ * driver, so that the registered application has to take responsibility
+ * for doing that.
+ *
+ * The type of frame that can be registered for is also dependent on the
+ * driver and interface type. The frame types are advertised in wiphy
+ * attributes so applications know what to expect.
+ *
+ * NOTE: When an interface changes type while registrations are active,
+ * these registrations are ignored until the interface type is
+ * changed again. This means that changing the interface type can
+ * lead to a situation that couldn't otherwise be produced, but
+ * any such registrations will be dormant in the sense that they
+ * will not be serviced, i.e. they will not receive any frames.
+ *
+ * Frame transmission allows userspace to send for example the required
+ * responses to action frames. It is subject to some sanity checking,
+ * but many frames can be transmitted. When a frame was transmitted, its
+ * status is indicated to the sending socket.
+ *
+ * For more technical details, see the corresponding command descriptions
+ * below.
+ */
+
+/**
+ * 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.
+ * However, for setting the channel, see %NL80211_CMD_SET_CHANNEL
+ * instead, the support here is for backward compatibility only.
+ * @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 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, %NL80211_ATTR_CONTROL_PORT,
+ * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE and
+ * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT.
+ * 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_IFINDEX is used to specify which interface (and thus
+ * 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_FRAME: Register for receiving certain mgmt frames
+ * (via @NL80211_CMD_FRAME) for processing in userspace. This command
+ * requires an interface index, a frame type attribute (optional for
+ * backward compatibility reasons, if not given assumes action frames)
+ * 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_REGISTER_ACTION: Alias for @NL80211_CMD_REGISTER_FRAME for
+ * backward compatibility
+ * @NL80211_CMD_FRAME: Management frame TX request and RX notification. This
+ * command is used both as a request to transmit a management frame and
+ * as an event indicating reception of a 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. If this
+ * channel is not the current channel (remain-on-channel or the
+ * operational channel) the device will switch to the given channel and
+ * transmit the frame, optionally waiting for a response for the time
+ * specified using %NL80211_ATTR_DURATION. 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_FRAME_WAIT_CANCEL: When an off-channel TX was requested, this
+ * command may be used with the corresponding cookie to cancel the wait
+ * time if it is known that it is no longer necessary.
+ * @NL80211_CMD_ACTION: Alias for @NL80211_CMD_FRAME for backward compatibility.
+ * @NL80211_CMD_FRAME_TX_STATUS: Report TX status of a management frame
+ * transmitted with %NL80211_CMD_FRAME. %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_ACTION_TX_STATUS: Alias for @NL80211_CMD_FRAME_TX_STATUS for
+ * backward compatibility.
+ * @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_SET_CHANNEL: Set the channel (using %NL80211_ATTR_WIPHY_FREQ
+ * and %NL80211_ATTR_WIPHY_CHANNEL_TYPE) the given interface (identifed
+ * by %NL80211_ATTR_IFINDEX) shall operate on.
+ * In case multiple channels are supported by the device, the mechanism
+ * with which it switches channels is implementation-defined.
+ * When a monitor interface is given, it can only switch channel while
+ * no other interfaces are operating to avoid disturbing the operation
+ * of any other interfaces, and other interfaces will again take
+ * precedence when they are used.
+ *
+ * @NL80211_CMD_SET_WDS_PEER: Set the MAC address of the peer on a WDS interface.
+ *
+ * @NL80211_CMD_JOIN_MESH: Join a mesh. The mesh ID must be given, and initial
+ * mesh config parameters may be given.
+ * @NL80211_CMD_LEAVE_MESH: Leave the mesh network -- no special arguments, the
+ * network is determined by the network interface.
+ *
+ * @NL80211_CMD_UNPROT_DEAUTHENTICATE: Unprotected deauthentication frame
+ * notification. This event is used to indicate that an unprotected
+ * deauthentication frame was dropped when MFP is in use.
+ * @NL80211_CMD_UNPROT_DISASSOCIATE: Unprotected disassociation frame
+ * notification. This event is used to indicate that an unprotected
+ * disassociation frame was dropped when MFP is in use.
+ *
+ * @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_FRAME,
+ NL80211_CMD_REGISTER_ACTION = NL80211_CMD_REGISTER_FRAME,
+ NL80211_CMD_FRAME,
+ NL80211_CMD_ACTION = NL80211_CMD_FRAME,
+ NL80211_CMD_FRAME_TX_STATUS,
+ NL80211_CMD_ACTION_TX_STATUS = NL80211_CMD_FRAME_TX_STATUS,
+
+ NL80211_CMD_SET_POWER_SAVE,
+ NL80211_CMD_GET_POWER_SAVE,
+
+ NL80211_CMD_SET_CQM,
+ NL80211_CMD_NOTIFY_CQM,
+
+ NL80211_CMD_SET_CHANNEL,
+ NL80211_CMD_SET_WDS_PEER,
+
+ NL80211_CMD_FRAME_WAIT_CANCEL,
+
+ NL80211_CMD_JOIN_MESH,
+ NL80211_CMD_LEAVE_MESH,
+
+ NL80211_CMD_UNPROT_DEAUTHENTICATE,
+ NL80211_CMD_UNPROT_DISASSOCIATE,
+
+ /* 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_CONTROL_PORT_ETHERTYPE: A 16-bit value indicating the
+ * ethertype that will be used for key negotiation. It can be
+ * specified with the associate and connect commands. If it is not
+ * specified, the value defaults to 0x888E (PAE, 802.1X). This
+ * attribute is also used as a flag in the wiphy information to
+ * indicate that protocols other than PAE are supported.
+ * @NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT: When included along with
+ * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE, indicates that the custom
+ * ethertype frames used for key negotiation must not be encrypted.
+ *
+ * @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_MAX_REMAIN_ON_CHANNEL_DURATION: Device attribute that
+ * specifies the maximum duration that can be requested with the
+ * remain-on-channel 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_FRAME.
+ * @NL80211_ATTR_FRAME_TYPE: A u16 indicating the frame type/subtype for the
+ * @NL80211_CMD_REGISTER_FRAME command.
+ * @NL80211_ATTR_TX_FRAME_TYPES: wiphy capability attribute, which is a
+ * nested attribute of %NL80211_ATTR_FRAME_TYPE attributes, containing
+ * information about which frame types can be transmitted with
+ * %NL80211_CMD_FRAME.
+ * @NL80211_ATTR_RX_FRAME_TYPES: wiphy capability attribute, which is a
+ * nested attribute of %NL80211_ATTR_FRAME_TYPE attributes, containing
+ * information about which frame types can be registered for RX.
+ *
+ * @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_AP_ISOLATE: (AP mode) Do not forward traffic between stations
+ * connected to this BSS.
+ *
+ * @NL80211_ATTR_WIPHY_TX_POWER_SETTING: Transmit power setting type. See
+ * &enum nl80211_tx_power_setting for possible values.
+ * @NL80211_ATTR_WIPHY_TX_POWER_LEVEL: Transmit power level in signed mBm units.
+ * This is used in association with @NL80211_ATTR_WIPHY_TX_POWER_SETTING
+ * for non-automatic settings.
+ *
+ * @NL80211_ATTR_SUPPORT_IBSS_RSN: The device supports IBSS RSN, which mostly
+ * means support for per-station GTKs.
+ *
+ * @NL80211_ATTR_WIPHY_ANTENNA_TX: Bitmap of allowed antennas for transmitting.
+ * This can be used to mask out antennas which are not attached or should
+ * not be used for transmitting. If an antenna is not selected in this
+ * bitmap the hardware is not allowed to transmit on this antenna.
+ *
+ * Each bit represents one antenna, starting with antenna 1 at the first
+ * bit. Depending on which antennas are selected in the bitmap, 802.11n
+ * drivers can derive which chainmasks to use (if all antennas belonging to
+ * a particular chain are disabled this chain should be disabled) and if
+ * a chain has diversity antennas wether diversity should be used or not.
+ * HT capabilities (STBC, TX Beamforming, Antenna selection) can be
+ * derived from the available chains after applying the antenna mask.
+ * Non-802.11n drivers can derive wether to use diversity or not.
+ * Drivers may reject configurations or RX/TX mask combinations they cannot
+ * support by returning -EINVAL.
+ *
+ * @NL80211_ATTR_WIPHY_ANTENNA_RX: Bitmap of allowed antennas for receiving.
+ * This can be used to mask out antennas which are not attached or should
+ * not be used for receiving. If an antenna is not selected in this bitmap
+ * the hardware should not be configured to receive on this antenna.
+ * For a more detailed descripton see @NL80211_ATTR_WIPHY_ANTENNA_TX.
+ *
+ * @NL80211_ATTR_MCAST_RATE: Multicast tx rate (in 100 kbps) for IBSS
+ *
+ * @NL80211_ATTR_OFFCHANNEL_TX_OK: For management frame TX, the frame may be
+ * transmitted on another channel when the channel given doesn't match
+ * the current channel. If the current channel doesn't match and this
+ * flag isn't set, the frame will be rejected. This is also used as an
+ * nl80211 capability flag.
+ *
+ * @NL80211_ATTR_BSS_HTOPMODE: HT operation mode (u16)
+ *
+ * @NL80211_ATTR_KEY_DEFAULT_TYPES: A nested attribute containing flags
+ * attributes, specifying what a key should be set as default as.
+ * See &enum nl80211_key_default_types.
+ *
+ * @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,
+
+ NL80211_ATTR_AP_ISOLATE,
+
+ NL80211_ATTR_WIPHY_TX_POWER_SETTING,
+ NL80211_ATTR_WIPHY_TX_POWER_LEVEL,
+
+ NL80211_ATTR_TX_FRAME_TYPES,
+ NL80211_ATTR_RX_FRAME_TYPES,
+ NL80211_ATTR_FRAME_TYPE,
+
+ NL80211_ATTR_CONTROL_PORT_ETHERTYPE,
+ NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT,
+
+ NL80211_ATTR_SUPPORT_IBSS_RSN,
+
+ NL80211_ATTR_WIPHY_ANTENNA_TX,
+ NL80211_ATTR_WIPHY_ANTENNA_RX,
+
+ NL80211_ATTR_MCAST_RATE,
+
+ NL80211_ATTR_OFFCHANNEL_TX_OK,
+
+ NL80211_ATTR_BSS_HT_OPMODE,
+
+ NL80211_ATTR_KEY_DEFAULT_TYPES,
+
+ NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION,
+
+ /* 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_P2P_CLIENT: P2P client
+ * @NL80211_IFTYPE_P2P_GO: P2P group owner
+ * @NL80211_IFTYPE_MAX: highest interface type number currently defined
+ * @NUM_NL80211_IFTYPES: number of defined interface types
+ *
+ * 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,
+ NL80211_IFTYPE_P2P_CLIENT,
+ NL80211_IFTYPE_P2P_GO,
+
+ /* keep last */
+ NUM_NL80211_IFTYPES,
+ NL80211_IFTYPE_MAX = NUM_NL80211_IFTYPES - 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_INVALID: attribute number 0 is reserved
+ * @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
+ * @NL80211_STA_FLAG_MAX: highest station flag number currently defined
+ * @__NL80211_STA_FLAG_AFTER_LAST: internal use
+ */
+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)
+ * @NL80211_STA_INFO_TX_RETRIES: total retries (u32, to this station)
+ * @NL80211_STA_INFO_TX_FAILED: total failed packets (u32, to this station)
+ * @NL80211_STA_INFO_SIGNAL_AVG: signal strength average (u8, dBm)
+ */
+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,
+ NL80211_STA_INFO_TX_RETRIES,
+ NL80211_STA_INFO_TX_FAILED,
+ NL80211_STA_INFO_SIGNAL_AVG,
+
+ /* 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_MPATH_INFO_FRAME_QLEN: number of queued frames for this destination
+ * @NL80211_MPATH_INFO_SN: destination sequence number
+ * @NL80211_MPATH_INFO_METRIC: metric (cost) of this mesh path
+ * @NL80211_MPATH_INFO_EXPTIME: expiration time for the path, in msec from now
+ * @NL80211_MPATH_INFO_FLAGS: mesh path flags, enumerated in
+ * &enum nl80211_mpath_flags;
+ * @NL80211_MPATH_INFO_DISCOVERY_TIMEOUT: total path discovery timeout, in msec
+ * @NL80211_MPATH_INFO_DISCOVERY_RETRIES: mesh path discovery retries
+ * @NL80211_MPATH_INFO_MAX: highest mesh path information attribute number
+ * currently defind
+ * @__NL80211_MPATH_INFO_AFTER_LAST: internal use
+ */
+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
+ * @NL80211_BAND_ATTR_MAX: highest band attribute currently defined
+ * @__NL80211_BAND_ATTR_AFTER_LAST: internal use
+ */
+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_INVALID: attribute number 0 is reserved
+ * @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).
+ * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number
+ * currently defined
+ * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use
+ */
+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_INVALID: attribute number 0 is reserved
+ * @NL80211_BITRATE_ATTR_RATE: Bitrate in units of 100 kbps
+ * @NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE: Short preamble supported
+ * in 2.4 GHz band.
+ * @NL80211_BITRATE_ATTR_MAX: highest bitrate attribute number
+ * currently defined
+ * @__NL80211_BITRATE_ATTR_AFTER_LAST: internal use
+ */
+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. cfg80211 only processes the country
+ * code from the IE, and relies on the regulatory domain information
+ * structure pased by userspace (CRDA) from our wireless-regdb.
+ * If a channel is enabled but the country code indicates it should
+ * be disabled we disable the channel and re-enable it upon disassociation.
+ */
+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_REG_RULE_ATTR_INVALID: attribute number 0 is reserved
+ * @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).
+ * @NL80211_REG_RULE_ATTR_MAX: highest regulatory rule attribute number
+ * currently defined
+ * @__NL80211_REG_RULE_ATTR_AFTER_LAST: internal use
+ */
+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)
+ * @NL80211_SURVEY_INFO_IN_USE: channel is currently being used
+ * @NL80211_SURVEY_INFO_CHANNEL_TIME: amount of time (in ms) that the radio
+ * spent on this channel
+ * @NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY: amount of the time the primary
+ * channel was sensed busy (either due to activity or energy detect)
+ * @NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY: amount of time the extension
+ * channel was sensed busy
+ * @NL80211_SURVEY_INFO_CHANNEL_TIME_RX: amount of time the radio spent
+ * receiving data
+ * @NL80211_SURVEY_INFO_CHANNEL_TIME_TX: amount of time the radio spent
+ * transmitting data
+ * @NL80211_SURVEY_INFO_MAX: highest survey info attribute number
+ * currently defined
+ * @__NL80211_SURVEY_INFO_AFTER_LAST: internal use
+ */
+enum nl80211_survey_info {
+ __NL80211_SURVEY_INFO_INVALID,
+ NL80211_SURVEY_INFO_FREQUENCY,
+ NL80211_SURVEY_INFO_NOISE,
+ NL80211_SURVEY_INFO_IN_USE,
+ NL80211_SURVEY_INFO_CHANNEL_TIME,
+ NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY,
+ NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY,
+ NL80211_SURVEY_INFO_CHANNEL_TIME_RX,
+ NL80211_SURVEY_INFO_CHANNEL_TIME_TX,
+
+ /* 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_ELEMENT_TTL: specifies the value of TTL field set at a
+ * source mesh point for path selection elements.
+ *
+ * @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,
+ NL80211_MESHCONF_ELEMENT_TTL,
+
+ /* 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_BSSID: BSSID of the BSS (6 octets)
+ * @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"
+ * @NL80211_BSS_STATUS_AUTHENTICATED: Authenticated with this BSS.
+ * @NL80211_BSS_STATUS_ASSOCIATED: Associated with this BSS.
+ * @NL80211_BSS_STATUS_IBSS_JOINED: Joined to this IBSS.
+ *
+ * The BSS status is a BSS attribute in scan dumps, which
+ * indicates the status the interface has wrt. this BSS.
+ */
+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)
+ * @NUM_NL80211_KEYTYPES: number of defined key types
+ */
+enum nl80211_key_type {
+ NL80211_KEYTYPE_GROUP,
+ NL80211_KEYTYPE_PAIRWISE,
+ NL80211_KEYTYPE_PEERKEY,
+
+ NUM_NL80211_KEYTYPES
+};
+
+/**
+ * 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_default_types - key default types
+ * @__NL80211_KEY_DEFAULT_TYPE_INVALID: invalid
+ * @NL80211_KEY_DEFAULT_TYPE_UNICAST: key should be used as default
+ * unicast key
+ * @NL80211_KEY_DEFAULT_TYPE_MULTICAST: key should be used as default
+ * multicast key
+ * @NUM_NL80211_KEY_DEFAULT_TYPES: number of default types
+ */
+enum nl80211_key_default_types {
+ __NL80211_KEY_DEFAULT_TYPE_INVALID,
+ NL80211_KEY_DEFAULT_TYPE_UNICAST,
+ NL80211_KEY_DEFAULT_TYPE_MULTICAST,
+
+ NUM_NL80211_KEY_DEFAULT_TYPES
+};
+
+/**
+ * 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_TYPE: the key type from enum nl80211_key_type, if not
+ * specified the default depends on whether a MAC address was
+ * given with the command using the key or not (u32)
+ * @NL80211_KEY_DEFAULT_TYPES: A nested attribute containing flags
+ * attributes, specifying what a key should be set as default as.
+ * See &enum nl80211_key_default_types.
+ * @__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,
+ NL80211_KEY_TYPE,
+ NL80211_KEY_DEFAULT_TYPES,
+
+ /* 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_PKT_LOSS_EVENT: a u32 value indicating that this many
+ * consecutive packets were not acknowledged by the peer
+ * @__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,
+ NL80211_ATTR_CQM_PKT_LOSS_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,
+};
+
+
+/**
+ * enum nl80211_tx_power_setting - TX power adjustment
+ * @NL80211_TX_POWER_AUTOMATIC: automatically determine transmit power
+ * @NL80211_TX_POWER_LIMITED: limit TX power by the mBm parameter
+ * @NL80211_TX_POWER_FIXED: fix TX power to the mBm parameter
+ */
+enum nl80211_tx_power_setting {
+ NL80211_TX_POWER_AUTOMATIC,
+ NL80211_TX_POWER_LIMITED,
+ NL80211_TX_POWER_FIXED,
+};
+
+#endif /* __LINUX_NL80211_H */
diff --git a/src/drivers/priv_netlink.h b/src/drivers/priv_netlink.h
new file mode 100644
index 0000000..23eff83
--- /dev/null
+++ b/src/drivers/priv_netlink.h
@@ -0,0 +1,113 @@
+/*
+ * wpa_supplicant - Private copy of Linux netlink/rtnetlink definitions.
+ * Copyright (c) 2003-2005, 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 PRIV_NETLINK_H
+#define PRIV_NETLINK_H
+
+/*
+ * This should be replaced with user space header once one is available with C
+ * library, etc..
+ */
+
+#ifndef IFF_LOWER_UP
+#define IFF_LOWER_UP 0x10000 /* driver signals L1 up */
+#endif
+#ifndef IFF_DORMANT
+#define IFF_DORMANT 0x20000 /* driver signals dormant */
+#endif
+
+#ifndef IFLA_IFNAME
+#define IFLA_IFNAME 3
+#endif
+#ifndef IFLA_WIRELESS
+#define IFLA_WIRELESS 11
+#endif
+#ifndef IFLA_OPERSTATE
+#define IFLA_OPERSTATE 16
+#endif
+#ifndef IFLA_LINKMODE
+#define IFLA_LINKMODE 17
+#define IF_OPER_DORMANT 5
+#define IF_OPER_UP 6
+#endif
+
+#define NLM_F_REQUEST 1
+
+#define NETLINK_ROUTE 0
+#define RTMGRP_LINK 1
+#define RTM_BASE 0x10
+#define RTM_NEWLINK (RTM_BASE + 0)
+#define RTM_DELLINK (RTM_BASE + 1)
+#define RTM_SETLINK (RTM_BASE + 3)
+
+#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))
+#define RTA_OK(rta,len) \
+((len) > 0 && (rta)->rta_len >= sizeof(struct rtattr) && \
+(rta)->rta_len <= (len))
+#define RTA_NEXT(rta,attrlen) \
+((attrlen) -= RTA_ALIGN((rta)->rta_len), \
+(struct rtattr *) (((char *)(rta)) + RTA_ALIGN((rta)->rta_len)))
+#define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len))
+#define RTA_DATA(rta) ((void *) (((char *) (rta)) + RTA_LENGTH(0)))
+
+
+struct sockaddr_nl
+{
+ sa_family_t nl_family;
+ unsigned short nl_pad;
+ u32 nl_pid;
+ u32 nl_groups;
+};
+
+struct nlmsghdr
+{
+ u32 nlmsg_len;
+ u16 nlmsg_type;
+ u16 nlmsg_flags;
+ u32 nlmsg_seq;
+ u32 nlmsg_pid;
+};
+
+struct ifinfomsg
+{
+ unsigned char ifi_family;
+ unsigned char __ifi_pad;
+ unsigned short ifi_type;
+ int ifi_index;
+ unsigned ifi_flags;
+ unsigned ifi_change;
+};
+
+struct rtattr
+{
+ unsigned short rta_len;
+ unsigned short rta_type;
+};
+
+#endif /* PRIV_NETLINK_H */
diff --git a/src/drivers/rfkill.c b/src/drivers/rfkill.c
new file mode 100644
index 0000000..8818311
--- /dev/null
+++ b/src/drivers/rfkill.c
@@ -0,0 +1,194 @@
+/*
+ * Linux rfkill helper functions for driver wrappers
+ * Copyright (c) 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 "includes.h"
+#include <fcntl.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "rfkill.h"
+
+#define RFKILL_EVENT_SIZE_V1 8
+
+struct rfkill_event {
+ u32 idx;
+ u8 type;
+ u8 op;
+ u8 soft;
+ u8 hard;
+} STRUCT_PACKED;
+
+enum rfkill_operation {
+ RFKILL_OP_ADD = 0,
+ RFKILL_OP_DEL,
+ RFKILL_OP_CHANGE,
+ RFKILL_OP_CHANGE_ALL,
+};
+
+enum rfkill_type {
+ RFKILL_TYPE_ALL = 0,
+ RFKILL_TYPE_WLAN,
+ RFKILL_TYPE_BLUETOOTH,
+ RFKILL_TYPE_UWB,
+ RFKILL_TYPE_WIMAX,
+ RFKILL_TYPE_WWAN,
+ RFKILL_TYPE_GPS,
+ RFKILL_TYPE_FM,
+ NUM_RFKILL_TYPES,
+};
+
+
+struct rfkill_data {
+ struct rfkill_config *cfg;
+ int fd;
+ int blocked;
+};
+
+
+static void rfkill_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct rfkill_data *rfkill = eloop_ctx;
+ struct rfkill_event event;
+ ssize_t len;
+ int new_blocked;
+
+ len = read(rfkill->fd, &event, sizeof(event));
+ if (len < 0) {
+ wpa_printf(MSG_ERROR, "rfkill: Event read failed: %s",
+ strerror(errno));
+ return;
+ }
+ if (len != RFKILL_EVENT_SIZE_V1) {
+ wpa_printf(MSG_DEBUG, "rfkill: Unexpected event size "
+ "%d (expected %d)",
+ (int) len, RFKILL_EVENT_SIZE_V1);
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "rfkill: event: idx=%u type=%d "
+ "op=%u soft=%u hard=%u",
+ event.idx, event.type, event.op, event.soft,
+ event.hard);
+ if (event.op != RFKILL_OP_CHANGE || event.type != RFKILL_TYPE_WLAN)
+ return;
+
+ if (event.hard) {
+ wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked");
+ new_blocked = 1;
+ } else if (event.soft) {
+ wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked");
+ new_blocked = 1;
+ } else {
+ wpa_printf(MSG_INFO, "rfkill: WLAN unblocked");
+ new_blocked = 0;
+ }
+
+ if (new_blocked != rfkill->blocked) {
+ rfkill->blocked = new_blocked;
+ if (new_blocked)
+ rfkill->cfg->blocked_cb(rfkill->cfg->ctx);
+ else
+ rfkill->cfg->unblocked_cb(rfkill->cfg->ctx);
+ }
+}
+
+
+struct rfkill_data * rfkill_init(struct rfkill_config *cfg)
+{
+ struct rfkill_data *rfkill;
+ struct rfkill_event event;
+ ssize_t len;
+
+ rfkill = os_zalloc(sizeof(*rfkill));
+ if (rfkill == NULL)
+ return NULL;
+
+ rfkill->cfg = cfg;
+ rfkill->fd = open("/dev/rfkill", O_RDONLY);
+ if (rfkill->fd < 0) {
+ wpa_printf(MSG_INFO, "rfkill: Cannot open RFKILL control "
+ "device");
+ goto fail;
+ }
+
+ if (fcntl(rfkill->fd, F_SETFL, O_NONBLOCK) < 0) {
+ wpa_printf(MSG_ERROR, "rfkill: Cannot set non-blocking mode: "
+ "%s", strerror(errno));
+ goto fail2;
+ }
+
+ for (;;) {
+ len = read(rfkill->fd, &event, sizeof(event));
+ if (len < 0) {
+ if (errno == EAGAIN)
+ break; /* No more entries */
+ wpa_printf(MSG_ERROR, "rfkill: Event read failed: %s",
+ strerror(errno));
+ break;
+ }
+ if (len != RFKILL_EVENT_SIZE_V1) {
+ wpa_printf(MSG_DEBUG, "rfkill: Unexpected event size "
+ "%d (expected %d)",
+ (int) len, RFKILL_EVENT_SIZE_V1);
+ continue;
+ }
+ wpa_printf(MSG_DEBUG, "rfkill: initial event: idx=%u type=%d "
+ "op=%u soft=%u hard=%u",
+ event.idx, event.type, event.op, event.soft,
+ event.hard);
+ if (event.op != RFKILL_OP_ADD ||
+ event.type != RFKILL_TYPE_WLAN)
+ continue;
+ if (event.hard) {
+ wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked");
+ rfkill->blocked = 1;
+ } else if (event.soft) {
+ wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked");
+ rfkill->blocked = 1;
+ }
+ }
+
+ eloop_register_read_sock(rfkill->fd, rfkill_receive, rfkill, NULL);
+
+ return rfkill;
+
+fail2:
+ close(rfkill->fd);
+fail:
+ os_free(rfkill);
+ return NULL;
+}
+
+
+void rfkill_deinit(struct rfkill_data *rfkill)
+{
+ if (rfkill == NULL)
+ return;
+
+ if (rfkill->fd >= 0) {
+ eloop_unregister_read_sock(rfkill->fd);
+ close(rfkill->fd);
+ }
+
+ os_free(rfkill->cfg);
+ os_free(rfkill);
+}
+
+
+int rfkill_is_blocked(struct rfkill_data *rfkill)
+{
+ if (rfkill == NULL)
+ return 0;
+
+ return rfkill->blocked;
+}
diff --git a/src/drivers/rfkill.h b/src/drivers/rfkill.h
new file mode 100644
index 0000000..7a984a6
--- /dev/null
+++ b/src/drivers/rfkill.h
@@ -0,0 +1,31 @@
+/*
+ * Linux rfkill helper functions for driver wrappers
+ * Copyright (c) 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 RFKILL_H
+#define RFKILL_H
+
+struct rfkill_data;
+
+struct rfkill_config {
+ void *ctx;
+ char ifname[IFNAMSIZ];
+ void (*blocked_cb)(void *ctx);
+ void (*unblocked_cb)(void *ctx);
+};
+
+struct rfkill_data * rfkill_init(struct rfkill_config *cfg);
+void rfkill_deinit(struct rfkill_data *rfkill);
+int rfkill_is_blocked(struct rfkill_data *rfkill);
+
+#endif /* RFKILL_H */
diff --git a/src/drivers/wireless_copy.h b/src/drivers/wireless_copy.h
new file mode 100644
index 0000000..201719b
--- /dev/null
+++ b/src/drivers/wireless_copy.h
@@ -0,0 +1,1185 @@
+/* This is based on Linux Wireless Extensions header file from WIRELESS_EXT 22.
+ * 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 : 22 16.3.07
+ *
+ * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
+ * Copyright (c) 1997-2007 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
+#include <linux/types.h> /* for __u* and __s* typedefs */
+#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>
+#ifndef ANDROID
+typedef __uint32_t __u32;
+typedef __int32_t __s32;
+typedef __uint16_t __u16;
+typedef __int16_t __s16;
+typedef __uint8_t __u8;
+#endif /* ANDROID */
+#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 22
+
+/*
+ * 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
+ *
+ * V19 to V20
+ * ----------
+ * - RtNetlink requests support (SET/GET)
+ *
+ * V20 to V21
+ * ----------
+ * - Remove (struct net_device *)->get_wireless_stats()
+ * - Change length in ESSID and NICK to strlen() instead of strlen()+1
+ * - Add IW_RETRY_SHORT/IW_RETRY_LONG retry modifiers
+ * - Power/Retry relative values no longer * 100000
+ * - Add explicit flag to tell stats are in 802.11k RCPI : IW_QUAL_RCPI
+ *
+ * V21 to V22
+ * ----------
+ * - Prevent leaking of kernel space in stream on 64 bits.
+ */
+
+/**************************** 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 'even' 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)
+#define IW_HANDLER(id, func) \
+ [IW_IOCTL_IDX(id)] = func
+
+/* Odd : get (world access), even : 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) */
+#define IW_MODE_MESH 7 /* Mesh (IEEE 802.11s) network */
+
+/* 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_RCPI 0x80 /* Level + Noise are 802.11k RCPI */
+#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 0x00FF /* 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 */
+#define IW_RETRY_SHORT 0x0010 /* Value is for short packets */
+#define IW_RETRY_LONG 0x0020 /* Value is for long packets */
+
+/* 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 */
+
+/* Scan capability flags - in (struct iw_range *)->scan_capa */
+#define IW_SCAN_CAPA_NONE 0x00
+#define IW_SCAN_CAPA_ESSID 0x01
+#define IW_SCAN_CAPA_BSSID 0x02
+#define IW_SCAN_CAPA_CHANNEL 0x04
+#define IW_SCAN_CAPA_MODE 0x08
+#define IW_SCAN_CAPA_RATE 0x10
+#define IW_SCAN_CAPA_TYPE 0x20
+#define IW_SCAN_CAPA_TIME 0x40
+
+/* 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
+#define IW_MLME_AUTH 2
+#define IW_MLME_ASSOC 3
+
+/* 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, IW_AUTH_GROUP_CIPHER, and IW_AUTH_CIPHER_GROUP_MGMT
+ * 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
+#define IW_AUTH_CIPHER_AES_CMAC 0x00000020
+
+/* 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 - SIOCIWFIRST))
+#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 */
+};
+
+#ifdef __KERNEL__
+#ifdef CONFIG_COMPAT
+
+#include <linux/compat.h>
+
+struct compat_iw_point {
+ compat_caddr_t pointer;
+ __u16 length;
+ __u16 flags;
+};
+#endif
+#endif
+
+/*
+ * 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;
+
+ /* Scan capabilities */
+ __u8 scan_capa; /* IW_SCAN_CAPA_* bit field */
+
+ /* 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 length 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)
+
+#ifdef __KERNEL__
+#ifdef CONFIG_COMPAT
+struct __compat_iw_event {
+ __u16 len; /* Real length of this stuff */
+ __u16 cmd; /* Wireless IOCTL */
+ compat_caddr_t pointer;
+};
+#define IW_EV_COMPAT_LCP_LEN offsetof(struct __compat_iw_event, pointer)
+#define IW_EV_COMPAT_POINT_OFF offsetof(struct compat_iw_point, length)
+
+/* Size of the various events for compat */
+#define IW_EV_COMPAT_CHAR_LEN (IW_EV_COMPAT_LCP_LEN + IFNAMSIZ)
+#define IW_EV_COMPAT_UINT_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(__u32))
+#define IW_EV_COMPAT_FREQ_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(struct iw_freq))
+#define IW_EV_COMPAT_PARAM_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(struct iw_param))
+#define IW_EV_COMPAT_ADDR_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(struct sockaddr))
+#define IW_EV_COMPAT_QUAL_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(struct iw_quality))
+#define IW_EV_COMPAT_POINT_LEN \
+ (IW_EV_COMPAT_LCP_LEN + sizeof(struct compat_iw_point) - \
+ IW_EV_COMPAT_POINT_OFF)
+#endif
+#endif
+
+/* Size of the Event prefix when packed in stream */
+#define IW_EV_LCP_PK_LEN (4)
+/* Size of the various events when packed in stream */
+#define IW_EV_CHAR_PK_LEN (IW_EV_LCP_PK_LEN + IFNAMSIZ)
+#define IW_EV_UINT_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(__u32))
+#define IW_EV_FREQ_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(struct iw_freq))
+#define IW_EV_PARAM_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(struct iw_param))
+#define IW_EV_ADDR_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(struct sockaddr))
+#define IW_EV_QUAL_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(struct iw_quality))
+#define IW_EV_POINT_PK_LEN (IW_EV_LCP_PK_LEN + 4)
+
+#endif /* _LINUX_WIRELESS_H */