ASR_BASE
Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/Makefile b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/Makefile
new file mode 100644
index 0000000..5721154
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/Makefile
@@ -0,0 +1,9 @@
+all:
+ @echo Nothing to be made.
+
+clean:
+ rm -f *~ *.o *.d *.gcno *.gcda *.gcov
+ rm -f build.wpa_supplicant build.hostapd
+
+install:
+ @echo Nothing to be made.
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/android_drv.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/android_drv.h
new file mode 100644
index 0000000..31d9440
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/android_drv.h
@@ -0,0 +1,56 @@
+/*
+ * Android driver interface
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef ANDROID_DRV_H
+#define ANDROID_DRV_H
+
+#define WPA_EVENT_DRIVER_STATE "CTRL-EVENT-DRIVER-STATE "
+
+#define MAX_SSID_LEN 32
+
+#define MAX_DRV_CMD_SIZE 248
+#define DRV_NUMBER_SEQUENTIAL_ERRORS 4
+
+#define WEXT_PNOSETUP_HEADER "PNOSETUP "
+#define WEXT_PNOSETUP_HEADER_SIZE 9
+#define WEXT_PNO_TLV_PREFIX 'S'
+#define WEXT_PNO_TLV_VERSION '1'
+#define WEXT_PNO_TLV_SUBVERSION '2'
+#define WEXT_PNO_TLV_RESERVED '0'
+#define WEXT_PNO_VERSION_SIZE 4
+#define WEXT_PNO_AMOUNT 16
+#define WEXT_PNO_SSID_SECTION 'S'
+/* SSID header size is SSID section type above + SSID length */
+#define WEXT_PNO_SSID_HEADER_SIZE 2
+#define WEXT_PNO_SCAN_INTERVAL_SECTION 'T'
+#define WEXT_PNO_SCAN_INTERVAL_LENGTH 2
+#define WEXT_PNO_SCAN_INTERVAL 30
+/* Scan interval size is scan interval section type + scan interval length
+ * above */
+#define WEXT_PNO_SCAN_INTERVAL_SIZE (1 + WEXT_PNO_SCAN_INTERVAL_LENGTH)
+#define WEXT_PNO_REPEAT_SECTION 'R'
+#define WEXT_PNO_REPEAT_LENGTH 1
+#define WEXT_PNO_REPEAT 4
+/* Repeat section size is Repeat section type + Repeat value length above */
+#define WEXT_PNO_REPEAT_SIZE (1 + WEXT_PNO_REPEAT_LENGTH)
+#define WEXT_PNO_MAX_REPEAT_SECTION 'M'
+#define WEXT_PNO_MAX_REPEAT_LENGTH 1
+#define WEXT_PNO_MAX_REPEAT 3
+/* Max Repeat section size is Max Repeat section type + Max Repeat value length
+ * above */
+#define WEXT_PNO_MAX_REPEAT_SIZE (1 + WEXT_PNO_MAX_REPEAT_LENGTH)
+/* This corresponds to the size of all sections expect SSIDs */
+#define WEXT_PNO_NONSSID_SECTIONS_SIZE \
+(WEXT_PNO_SCAN_INTERVAL_SIZE + WEXT_PNO_REPEAT_SIZE + WEXT_PNO_MAX_REPEAT_SIZE)
+/* PNO Max command size is total of header, version, ssid and other sections +
+ * Null termination */
+#define WEXT_PNO_MAX_COMMAND_SIZE \
+ (WEXT_PNOSETUP_HEADER_SIZE + WEXT_PNO_VERSION_SIZE \
+ + WEXT_PNO_AMOUNT * (WEXT_PNO_SSID_HEADER_SIZE + MAX_SSID_LEN) \
+ + WEXT_PNO_NONSSID_SECTIONS_SIZE + 1)
+
+#endif /* ANDROID_DRV_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver.h
new file mode 100644
index 0000000..969cbda
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver.h
@@ -0,0 +1,6802 @@
+/*
+ * Driver interface definition
+ * Copyright (c) 2003-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README 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 "ap/sta_info.h"
+#include "common/defs.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_common.h"
+#ifdef CONFIG_MACSEC
+#include "pae/ieee802_1x_kay.h"
+#endif /* CONFIG_MACSEC */
+#include "utils/list.h"
+
+#define HOSTAPD_CHAN_DISABLED 0x00000001
+#define HOSTAPD_CHAN_NO_IR 0x00000002
+#define HOSTAPD_CHAN_RADAR 0x00000008
+#define HOSTAPD_CHAN_HT40PLUS 0x00000010
+#define HOSTAPD_CHAN_HT40MINUS 0x00000020
+#define HOSTAPD_CHAN_HT40 0x00000040
+#define HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED 0x00000080
+
+#define HOSTAPD_CHAN_DFS_UNKNOWN 0x00000000
+#define HOSTAPD_CHAN_DFS_USABLE 0x00000100
+#define HOSTAPD_CHAN_DFS_UNAVAILABLE 0x00000200
+#define HOSTAPD_CHAN_DFS_AVAILABLE 0x00000300
+#define HOSTAPD_CHAN_DFS_MASK 0x00000300
+
+#define HOSTAPD_CHAN_VHT_80MHZ_SUBCHANNEL 0x00000800
+#define HOSTAPD_CHAN_VHT_160MHZ_SUBCHANNEL 0x00001000
+
+#define HOSTAPD_CHAN_INDOOR_ONLY 0x00010000
+#define HOSTAPD_CHAN_GO_CONCURRENT 0x00020000
+
+/* Allowed bandwidth mask */
+enum hostapd_chan_width_attr {
+ HOSTAPD_CHAN_WIDTH_10 = BIT(0),
+ HOSTAPD_CHAN_WIDTH_20 = BIT(1),
+ HOSTAPD_CHAN_WIDTH_40P = BIT(2),
+ HOSTAPD_CHAN_WIDTH_40M = BIT(3),
+ HOSTAPD_CHAN_WIDTH_80 = BIT(4),
+ HOSTAPD_CHAN_WIDTH_160 = BIT(5),
+ HOSTAPD_CHAN_WIDTH_320 = BIT(6),
+};
+
+/* Filter gratuitous ARP */
+#define WPA_DATA_FRAME_FILTER_FLAG_ARP BIT(0)
+/* Filter unsolicited Neighbor Advertisement */
+#define WPA_DATA_FRAME_FILTER_FLAG_NA BIT(1)
+/* Filter unicast IP packets encrypted using the GTK */
+#define WPA_DATA_FRAME_FILTER_FLAG_GTK BIT(2)
+
+#define HOSTAPD_DFS_REGION_FCC 1
+#define HOSTAPD_DFS_REGION_ETSI 2
+#define HOSTAPD_DFS_REGION_JP 3
+
+/**
+ * enum reg_change_initiator - Regulatory change initiator
+ */
+enum reg_change_initiator {
+ REGDOM_SET_BY_CORE,
+ REGDOM_SET_BY_USER,
+ REGDOM_SET_BY_DRIVER,
+ REGDOM_SET_BY_COUNTRY_IE,
+ REGDOM_BEACON_HINT,
+};
+
+/**
+ * enum reg_type - Regulatory change types
+ */
+enum reg_type {
+ REGDOM_TYPE_UNKNOWN,
+ REGDOM_TYPE_COUNTRY,
+ REGDOM_TYPE_WORLD,
+ REGDOM_TYPE_CUSTOM_WORLD,
+ REGDOM_TYPE_INTERSECTION,
+};
+
+/**
+ * struct hostapd_wmm_rule - WMM regulatory rule
+ * @min_cwmin: Lower bound of CW_min value
+ * @min_cwmax: Lower bound of CW_max value
+ * @min_aifs: Lower bound of AIFS value
+ * @max_txop: Upper bound of TXOP, value in units of 32 usec
+ */
+struct hostapd_wmm_rule {
+ int min_cwmin;
+ int min_cwmax;
+ int min_aifs;
+ int max_txop;
+};
+
+/**
+ * struct hostapd_channel_data - Channel information
+ */
+struct hostapd_channel_data {
+ /**
+ * chan - Channel number (IEEE 802.11)
+ */
+ short chan;
+
+ /**
+ * freq - Frequency in MHz
+ */
+ int freq;
+
+ /**
+ * flag - Channel flags (HOSTAPD_CHAN_*)
+ */
+ int flag;
+
+ /**
+ * allowed_bw - Allowed channel width bitmask
+ *
+ * See enum hostapd_chan_width_attr.
+ */
+ u32 allowed_bw;
+
+ /**
+ * max_tx_power - Regulatory transmit power limit in dBm
+ */
+ u8 max_tx_power;
+
+ /**
+ * survey_list - Linked list of surveys (struct freq_survey)
+ */
+ struct dl_list survey_list;
+
+ /**
+ * min_nf - Minimum observed noise floor, in dBm, based on all
+ * surveyed channel data
+ */
+ s8 min_nf;
+
+#ifdef CONFIG_ACS
+ /**
+ * interference_factor - Computed interference factor on this
+ * channel (used internally in src/ap/acs.c; driver wrappers do not
+ * need to set this)
+ */
+ long double interference_factor;
+#endif /* CONFIG_ACS */
+
+ /**
+ * dfs_cac_ms - DFS CAC time in milliseconds
+ */
+ unsigned int dfs_cac_ms;
+
+ /**
+ * wmm_rules_valid - Indicates wmm_rules state
+ */
+ int wmm_rules_valid;
+
+ /**
+ * wmm_rules - WMM regulatory rules
+ */
+ struct hostapd_wmm_rule wmm_rules[WMM_AC_NUM];
+
+ /**
+ * punct_bitmap - RU puncturing bitmap
+ */
+ u16 punct_bitmap;
+};
+
+#define HE_MAC_CAPAB_0 0
+#define HE_MAX_MAC_CAPAB_SIZE 6
+#define HE_MAX_PHY_CAPAB_SIZE 11
+#define HE_MAX_MCS_CAPAB_SIZE 12
+#define HE_MAX_PPET_CAPAB_SIZE 25
+
+/**
+ * struct he_capabilities - IEEE 802.11ax HE capabilities
+ */
+struct he_capabilities {
+ u8 he_supported;
+ u8 phy_cap[HE_MAX_PHY_CAPAB_SIZE];
+ u8 mac_cap[HE_MAX_MAC_CAPAB_SIZE];
+ u8 mcs[HE_MAX_MCS_CAPAB_SIZE];
+ u8 ppet[HE_MAX_PPET_CAPAB_SIZE];
+ u16 he_6ghz_capa;
+};
+
+/* struct eht_capabilities - IEEE 802.11be EHT capabilities */
+struct eht_capabilities {
+ bool eht_supported;
+ u16 mac_cap;
+ u8 phy_cap[EHT_PHY_CAPAB_LEN];
+ u8 mcs[EHT_MCS_NSS_CAPAB_LEN];
+ u8 ppet[EHT_PPE_THRESH_CAPAB_LEN];
+};
+
+#define HOSTAPD_MODE_FLAG_HT_INFO_KNOWN BIT(0)
+#define HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN BIT(1)
+#define HOSTAPD_MODE_FLAG_HE_INFO_KNOWN BIT(2)
+
+
+enum ieee80211_op_mode {
+ IEEE80211_MODE_INFRA = 0,
+ IEEE80211_MODE_IBSS = 1,
+ IEEE80211_MODE_AP = 2,
+ IEEE80211_MODE_MESH = 5,
+
+ /* only add new entries before IEEE80211_MODE_NUM */
+ IEEE80211_MODE_NUM
+};
+
+/**
+ * struct ieee80211_edmg_config - EDMG configuration
+ *
+ * This structure describes most essential parameters needed
+ * for IEEE 802.11ay EDMG configuration
+ *
+ * @channels: Bitmap that indicates the 2.16 GHz channel(s)
+ * that are allowed to be used for transmissions.
+ * Bit 0 indicates channel 1, bit 1 indicates channel 2, etc.
+ * Set to 0 to indicate EDMG not supported.
+ * @bw_config: Channel BW Configuration subfield encodes
+ * the allowed channel bandwidth configurations
+ */
+struct ieee80211_edmg_config {
+ u8 channels;
+ enum edmg_bw_config bw_config;
+};
+
+/**
+ * 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;
+
+ /**
+ * vht_capab - VHT (IEEE 802.11ac) capabilities
+ */
+ u32 vht_capab;
+
+ /**
+ * vht_mcs_set - VHT MCS (IEEE 802.11ac) rate parameters
+ */
+ u8 vht_mcs_set[8];
+
+ unsigned int flags; /* HOSTAPD_MODE_FLAG_* */
+
+ /**
+ * he_capab - HE (IEEE 802.11ax) capabilities
+ */
+ struct he_capabilities he_capab[IEEE80211_MODE_NUM];
+
+ /**
+ * This structure describes the most essential parameters needed
+ * for IEEE 802.11ay EDMG configuration.
+ */
+ struct ieee80211_edmg_config edmg;
+
+ /**
+ * eht_capab - EHT (IEEE 802.11be) capabilities
+ */
+ struct eht_capabilities eht_capab[IEEE80211_MODE_NUM];
+};
+
+
+#define IEEE80211_CAP_ESS 0x0001
+#define IEEE80211_CAP_IBSS 0x0002
+#define IEEE80211_CAP_PRIVACY 0x0010
+#define IEEE80211_CAP_RRM 0x1000
+
+/* DMG (60 GHz) IEEE 802.11ad */
+/* type - bits 0..1 */
+#define IEEE80211_CAP_DMG_MASK 0x0003
+#define IEEE80211_CAP_DMG_IBSS 0x0001 /* Tx by: STA */
+#define IEEE80211_CAP_DMG_PBSS 0x0002 /* Tx by: PCP */
+#define IEEE80211_CAP_DMG_AP 0x0003 /* Tx by: AP */
+
+#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_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)
+ * @est_throughput: Estimated throughput in kbps (this is calculated during
+ * scan result processing if left zero by the driver wrapper)
+ * @snr: Signal-to-noise ratio in dB (calculated during scan result processing)
+ * @parent_tsf: Time when the Beacon/Probe Response frame was received in terms
+ * of TSF of the BSS specified by %tsf_bssid.
+ * @tsf_bssid: The BSS that %parent_tsf TSF time refers to.
+ * @beacon_newer: Whether the Beacon frame data is known to be newer
+ * @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.
+ *
+ * This structure data is 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_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;
+ unsigned int est_throughput;
+ int snr;
+ u64 parent_tsf;
+ u8 tsf_bssid[ETH_ALEN];
+ bool beacon_newer;
+ size_t ie_len;
+ size_t beacon_ie_len;
+ /* Followed by ie_len + beacon_ie_len octets of IE data */
+};
+
+/**
+ * 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
+ * @fetch_time: Time when the results were fetched from the driver
+ */
+struct wpa_scan_results {
+ struct wpa_scan_res **res;
+ size_t num;
+ struct os_reltime fetch_time;
+};
+
+/**
+ * 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 16
+
+/**
+ * struct wpa_driver_scan_ssid - SSIDs to scan for
+ * @ssid - specific SSID to scan for (ProbeReq)
+ * %NULL or zero-length SSID is used to indicate active scan
+ * with wildcard SSID.
+ * @ssid_len - Length of the SSID in octets
+ */
+struct wpa_driver_scan_ssid {
+ const u8 *ssid;
+ size_t ssid_len;
+};
+
+struct t2lm_mapping {
+ /**
+ * downlink - Bitmap of TIDs mapped with a link in downlink direction
+ */
+ u8 downlink;
+
+ /**
+ * uplink - Bitmap of TIDs mapped with a link in uplink direction
+ */
+ u8 uplink;
+};
+
+/**
+ * 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 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[SSID_MAX_LEN];
+ size_t ssid_len;
+ } *filter_ssids;
+
+ /**
+ * num_filter_ssids - Number of entries in filter_ssids array
+ */
+ size_t num_filter_ssids;
+
+ /**
+ * filter_rssi - Filter by RSSI
+ *
+ * The driver may filter scan results in firmware to reduce host
+ * wakeups and thereby save power. Specify the RSSI threshold in s32
+ * dBm.
+ */
+ s32 filter_rssi;
+
+ /**
+ * p2p_probe - Used to disable CCK (802.11b) rates for P2P probes
+ *
+ * When set, the driver is expected to remove rates 1, 2, 5.5, and 11
+ * Mbps from the support rates element(s) in the Probe Request frames
+ * and not to transmit the frames at any of those rates.
+ */
+ unsigned int p2p_probe:1;
+
+ /**
+ * only_new_results - Request driver to report only new results
+ *
+ * This is used to request the driver to report only BSSes that have
+ * been detected after this scan request has been started, i.e., to
+ * flush old cached BSS entries.
+ */
+ unsigned int only_new_results:1;
+
+ /**
+ * low_priority - Requests driver to use a lower scan priority
+ *
+ * This is used to request the driver to use a lower scan priority
+ * if it supports such a thing.
+ */
+ unsigned int low_priority:1;
+
+ /**
+ * mac_addr_rand - Requests driver to randomize MAC address
+ */
+ unsigned int mac_addr_rand:1;
+
+ /**
+ * mac_addr - MAC address used with randomization. The address cannot be
+ * a multicast one, i.e., bit 0 of byte 0 should not be set.
+ */
+ u8 *mac_addr;
+
+ /**
+ * mac_addr_mask - MAC address mask used with randomization.
+ *
+ * Bits that are 0 in the mask should be randomized. Bits that are 1 in
+ * the mask should be taken as is from mac_addr. The mask should not
+ * allow the generation of a multicast address, i.e., bit 0 of byte 0
+ * must be set.
+ */
+ const u8 *mac_addr_mask;
+
+ /**
+ * sched_scan_plans - Scan plans for scheduled scan
+ *
+ * Each scan plan consists of the number of iterations to scan and the
+ * interval between scans. When a scan plan finishes (i.e., it was run
+ * for the specified number of iterations), the next scan plan is
+ * executed. The scan plans are executed in the order they appear in
+ * the array (lower index first). The last scan plan will run infinitely
+ * (until requested to stop), thus must not specify the number of
+ * iterations. All other scan plans must specify the number of
+ * iterations.
+ */
+ struct sched_scan_plan {
+ u32 interval; /* In seconds */
+ u32 iterations; /* Zero to run infinitely */
+ } *sched_scan_plans;
+
+ /**
+ * sched_scan_plans_num - Number of scan plans in sched_scan_plans array
+ */
+ unsigned int sched_scan_plans_num;
+
+ /**
+ * sched_scan_start_delay - Delay to use before starting the first scan
+ *
+ * Delay (in seconds) before scheduling first scan plan cycle. The
+ * driver may ignore this parameter and start immediately (or at any
+ * other time), if this feature is not supported.
+ */
+ u32 sched_scan_start_delay;
+
+ /**
+ * bssid - Specific BSSID to scan for
+ *
+ * This optional parameter can be used to replace the default wildcard
+ * BSSID with a specific BSSID to scan for if results are needed from
+ * only a single BSS.
+ */
+ const u8 *bssid;
+
+ /**
+ * scan_cookie - Unique identification representing the scan request
+ *
+ * This scan_cookie carries a unique identification representing the
+ * scan request if the host driver/kernel supports concurrent scan
+ * requests. This cookie is returned from the corresponding driver
+ * interface.
+ *
+ * Note: Unlike other parameters in this structure, scan_cookie is used
+ * only to return information instead of setting parameters for the
+ * scan.
+ */
+ u64 scan_cookie;
+
+ /**
+ * duration - Dwell time on each channel
+ *
+ * This optional parameter can be used to set the dwell time on each
+ * channel. In TUs.
+ */
+ u16 duration;
+
+ /**
+ * duration_mandatory - Whether the specified duration is mandatory
+ *
+ * If this is set, the duration specified by the %duration field is
+ * mandatory (and the driver should reject the scan request if it is
+ * unable to comply with the specified duration), otherwise it is the
+ * maximum duration and the actual duration may be shorter.
+ */
+ unsigned int duration_mandatory:1;
+
+ /**
+ * relative_rssi_set - Whether relative RSSI parameters are set
+ */
+ unsigned int relative_rssi_set:1;
+
+ /**
+ * relative_rssi - Relative RSSI for reporting better BSSs
+ *
+ * Amount of RSSI by which a BSS should be better than the current
+ * connected BSS to report the new BSS to user space.
+ */
+ s8 relative_rssi;
+
+ /**
+ * relative_adjust_band - Band to which RSSI should be adjusted
+ *
+ * The relative_adjust_rssi should be added to the band specified
+ * by relative_adjust_band.
+ */
+ enum set_band relative_adjust_band;
+
+ /**
+ * relative_adjust_rssi - RSSI to be added to relative_adjust_band
+ *
+ * An amount of relative_band_rssi should be added to the BSSs that
+ * belong to the band specified by relative_adjust_band while comparing
+ * with other bands for BSS reporting.
+ */
+ s8 relative_adjust_rssi;
+
+ /**
+ * oce_scan
+ *
+ * Enable the following OCE scan features: (WFA OCE TechSpec v1.0)
+ * - Accept broadcast Probe Response frame.
+ * - Probe Request frame deferral and suppression.
+ * - Max Channel Time - driver fills FILS request params IE with
+ * Maximum Channel Time.
+ * - Send 1st Probe Request frame in rate of minimum 5.5 Mbps.
+ */
+ unsigned int oce_scan:1;
+
+ /**
+ * p2p_include_6ghz - Include 6 GHz channels for P2P full scan
+ *
+ */
+ unsigned int p2p_include_6ghz:1;
+
+ /**
+ * non_coloc_6ghz - Force scanning of non-PSC 6 GHz channels
+ *
+ * If this is set, the driver should scan non-PSC channels from the
+ * scan request even if neighbor reports from 2.4/5 GHz APs did not
+ * report a co-located AP on these channels. The default is to scan
+ * non-PSC channels only if a co-located AP was reported on the channel.
+ */
+ unsigned int non_coloc_6ghz:1;
+
+ /*
+ * NOTE: Whenever adding new parameters here, please make sure
+ * wpa_scan_clone_params() and wpa_scan_free_params() get updated with
+ * matching changes.
+ */
+};
+
+/**
+ * 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;
+
+ /**
+ * p2p - Whether this connection is a P2P group
+ */
+ int p2p;
+
+ /**
+ * auth_data - Additional elements for Authentication frame
+ *
+ * This buffer starts with the Authentication transaction sequence
+ * number field. If no special handling of such elements is needed, this
+ * pointer is %NULL. This is used with SAE and FILS.
+ */
+ const u8 *auth_data;
+
+ /**
+ * auth_data_len - Length of auth_data buffer in octets
+ */
+ size_t auth_data_len;
+
+ /**
+ * mld - Establish an MLD connection
+ */
+ bool mld;
+
+ /**
+ * mld_link_id - The link ID of the MLD AP to which we are associating
+ */
+ u8 mld_link_id;
+
+ /**
+ * The MLD AP address
+ */
+ const u8 *ap_mld_addr;
+};
+
+/**
+ * enum wps_mode - WPS mode
+ */
+enum wps_mode {
+ /**
+ * WPS_MODE_NONE - No WPS provisioning being used
+ */
+ WPS_MODE_NONE,
+
+ /**
+ * WPS_MODE_OPEN - WPS provisioning with AP that is in open mode
+ */
+ WPS_MODE_OPEN,
+
+ /**
+ * WPS_MODE_PRIVACY - WPS provisioning with AP that is using protection
+ */
+ WPS_MODE_PRIVACY
+};
+
+/**
+ * struct hostapd_freq_params - Channel parameters
+ */
+struct hostapd_freq_params {
+ /**
+ * mode - Mode/band (HOSTAPD_MODE_IEEE80211A, ..)
+ */
+ enum hostapd_hw_mode mode;
+
+ /**
+ * freq - Primary channel center frequency in MHz
+ */
+ int freq;
+
+ /**
+ * channel - Channel number
+ */
+ int channel;
+
+ /**
+ * ht_enabled - Whether HT is enabled
+ */
+ int ht_enabled;
+
+ /**
+ * sec_channel_offset - Secondary channel offset for HT40
+ *
+ * 0 = HT40 disabled,
+ * -1 = HT40 enabled, secondary channel below primary,
+ * 1 = HT40 enabled, secondary channel above primary
+ */
+ int sec_channel_offset;
+
+ /**
+ * vht_enabled - Whether VHT is enabled
+ */
+ int vht_enabled;
+
+ /**
+ * he_enabled - Whether HE is enabled
+ */
+ int he_enabled;
+
+ /**
+ * center_freq1 - Segment 0 center frequency in MHz
+ *
+ * Valid for both HT and VHT.
+ */
+ int center_freq1;
+
+ /**
+ * center_freq2 - Segment 1 center frequency in MHz
+ *
+ * Non-zero only for bandwidth 80 and an 80+80 channel
+ */
+ int center_freq2;
+
+ /**
+ * bandwidth - Channel bandwidth in MHz (20, 40, 80, 160)
+ */
+ int bandwidth;
+
+ /**
+ * This structure describes the most essential parameters needed
+ * for IEEE 802.11ay EDMG configuration.
+ */
+ struct ieee80211_edmg_config edmg;
+
+ /**
+ * radar_background - Whether radar/CAC background is requested
+ */
+ bool radar_background;
+
+ /**
+ * eht_enabled - Whether EHT is enabled
+ */
+ bool eht_enabled;
+
+ /**
+ * link_id: If >=0 indicates the link of the AP MLD to configure
+ */
+ int link_id;
+};
+
+/**
+ * struct wpa_driver_sta_auth_params - Authentication parameters
+ * Data for struct wpa_driver_ops::sta_auth().
+ */
+struct wpa_driver_sta_auth_params {
+
+ /**
+ * own_addr - Source address and BSSID for authentication frame
+ */
+ const u8 *own_addr;
+
+ /**
+ * addr - MAC address of the station to associate
+ */
+ const u8 *addr;
+
+ /**
+ * seq - authentication sequence number
+ */
+ u16 seq;
+
+ /**
+ * status - authentication response status code
+ */
+ u16 status;
+
+ /**
+ * ie - authentication frame ie buffer
+ */
+ const u8 *ie;
+
+ /**
+ * len - ie buffer length
+ */
+ size_t len;
+
+ /**
+ * fils_auth - Indicates whether FILS authentication is being performed
+ */
+ int fils_auth;
+
+ /**
+ * fils_anonce - ANonce (required for FILS)
+ */
+ u8 fils_anonce[WPA_NONCE_LEN];
+
+ /**
+ * fils_snonce - SNonce (required for FILS)
+ */
+ u8 fils_snonce[WPA_NONCE_LEN];
+
+ /**
+ * fils_kek - key for encryption (required for FILS)
+ */
+ u8 fils_kek[WPA_KEK_MAX_LEN];
+
+ /**
+ * fils_kek_len - Length of the fils_kek in octets (required for FILS)
+ */
+ size_t fils_kek_len;
+};
+
+struct wpa_driver_mld_params {
+ /**
+ * mld_addr - AP's MLD address
+ */
+ const u8 *mld_addr;
+
+ /**
+ * valid_links - The valid links including the association link
+ */
+ u16 valid_links;
+
+ /**
+ * assoc_link_id - The link on which the association is performed
+ */
+ u8 assoc_link_id;
+
+ /**
+ * mld_links - Link information
+ *
+ * Should include information on all the requested links for association
+ * including the link on which the association should take place.
+ * For the association link, the ies and ies_len should be NULL and
+ * 0 respectively.
+ */
+ struct {
+ int freq;
+ const u8 *bssid;
+ const u8 *ies;
+ size_t ies_len;
+ } mld_links[MAX_NUM_MLD_LINKS];
+};
+
+/**
+ * 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;
+
+ unsigned char rates[WLAN_SUPP_RATES_MAX];
+ int mcast_rate;
+
+ /**
+ * bssid_hint - BSSID of a proposed AP
+ *
+ * This indicates which BSS has been found a suitable candidate for
+ * initial association for drivers that use driver/firmwate-based BSS
+ * selection. Unlike the @bssid parameter, @bssid_hint does not limit
+ * the driver from selecting other BSSes in the ESS.
+ */
+ const u8 *bssid_hint;
+
+ /**
+ * ssid - The selected SSID
+ */
+ const u8 *ssid;
+
+ /**
+ * ssid_len - Length of the SSID (1..32)
+ */
+ size_t ssid_len;
+
+ /**
+ * freq - channel parameters
+ */
+ struct hostapd_freq_params freq;
+
+ /**
+ * freq_hint - Frequency of the channel the proposed AP is using
+ *
+ * This provides a channel on which a suitable BSS has been found as a
+ * hint for the driver. Unlike the @freq parameter, @freq_hint does not
+ * limit the driver from selecting other channels for
+ * driver/firmware-based BSS selection.
+ */
+ int freq_hint;
+
+ /**
+ * bg_scan_period - Background scan period in seconds, 0 to disable
+ * background scan, or -1 to indicate no change to default driver
+ * configuration
+ */
+ int bg_scan_period;
+
+ /**
+ * beacon_int - Beacon interval for IBSS or 0 to use driver default
+ */
+ int beacon_int;
+
+ /**
+ * 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, group_mgmt_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;
+
+ /**
+ * wpa_proto - Bitfield of WPA_PROTO_* values to indicate WPA/WPA2
+ */
+ unsigned int wpa_proto;
+
+ /**
+ * pairwise_suite - Selected pairwise cipher suite (WPA_CIPHER_*)
+ *
+ * This is usually ignored if @wpa_ie is used.
+ */
+ unsigned int pairwise_suite;
+
+ /**
+ * group_suite - Selected group cipher suite (WPA_CIPHER_*)
+ *
+ * This is usually ignored if @wpa_ie is used.
+ */
+ unsigned int group_suite;
+
+ /**
+ * mgmt_group_suite - Selected group management cipher suite (WPA_CIPHER_*)
+ *
+ * This is usually ignored if @wpa_ie is used.
+ */
+ unsigned int mgmt_group_suite;
+
+ /**
+ * key_mgmt_suite - Selected key management suite (WPA_KEY_MGMT_*)
+ *
+ * This is usually ignored if @wpa_ie is used.
+ */
+ unsigned int key_mgmt_suite;
+
+ /**
+ * allowed_key_mgmts - Bitfield of allowed key management suites
+ * (WPA_KEY_MGMT_*) other than @key_mgmt_suite for the current
+ * connection
+ *
+ * SME in the driver may choose key_mgmt from this list for the initial
+ * connection or roaming. The driver which doesn't support this
+ * ignores this parameter.
+ */
+ unsigned int allowed_key_mgmts;
+
+ /**
+ * 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;
+
+ /**
+ * 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_PSK. 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_PSK. 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 to 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;
+
+ /**
+ * fixed_bssid - Whether to force this BSSID in IBSS mode
+ * 1 = Fix this BSSID and prevent merges.
+ * 0 = Do not fix BSSID.
+ */
+ int fixed_bssid;
+
+ /**
+ * fixed_freq - Fix control channel in IBSS mode
+ * 0 = don't fix control channel (default)
+ * 1 = fix control channel; this prevents IBSS merging with another
+ * channel
+ */
+ int fixed_freq;
+
+ /**
+ * disable_ht - Disable HT (IEEE 802.11n) for this connection
+ */
+ int disable_ht;
+
+ /**
+ * htcaps - HT Capabilities over-rides
+ *
+ * Only bits set in the mask will be used, and not all values are used
+ * by the kernel anyway. Currently, MCS, MPDU and MSDU fields are used.
+ *
+ * Pointer to struct ieee80211_ht_capabilities.
+ */
+ const u8 *htcaps;
+
+ /**
+ * htcaps_mask - HT Capabilities over-rides mask
+ *
+ * Pointer to struct ieee80211_ht_capabilities.
+ */
+ const u8 *htcaps_mask;
+
+#ifdef CONFIG_VHT_OVERRIDES
+ /**
+ * disable_vht - Disable VHT for this connection
+ */
+ int disable_vht;
+
+ /**
+ * VHT capability overrides.
+ */
+ const struct ieee80211_vht_capabilities *vhtcaps;
+ const struct ieee80211_vht_capabilities *vhtcaps_mask;
+#endif /* CONFIG_VHT_OVERRIDES */
+
+#ifdef CONFIG_HE_OVERRIDES
+ /**
+ * disable_he - Disable HE for this connection
+ */
+ int disable_he;
+#endif /* CONFIG_HE_OVERRIDES */
+
+ /**
+ * req_key_mgmt_offload - Request key management offload for connection
+ *
+ * Request key management offload for this connection if the device
+ * supports it.
+ */
+ int req_key_mgmt_offload;
+
+ /**
+ * req_handshake_offload - Request EAPOL handshake offload
+ *
+ * Request EAPOL handshake offload for this connection if the device
+ * supports it.
+ */
+ int req_handshake_offload;
+
+ /**
+ * Flag for indicating whether this association includes support for
+ * RRM (Radio Resource Measurements)
+ */
+ int rrm_used;
+
+ /**
+ * pbss - If set, connect to a PCP in a PBSS. Otherwise, connect to an
+ * AP as usual. Valid for DMG network only.
+ */
+ int pbss;
+
+ /**
+ * fils_kek - KEK for FILS association frame protection (AES-SIV)
+ */
+ const u8 *fils_kek;
+
+ /**
+ * fils_kek_len: Length of fils_kek in bytes
+ */
+ size_t fils_kek_len;
+
+ /**
+ * fils_nonces - Nonces for FILS association frame protection
+ * (AES-SIV AAD)
+ */
+ const u8 *fils_nonces;
+
+ /**
+ * fils_nonces_len: Length of fils_nonce in bytes
+ */
+ size_t fils_nonces_len;
+
+ /**
+ * fils_erp_username - Username part of keyName-NAI
+ */
+ const u8 *fils_erp_username;
+
+ /**
+ * fils_erp_username_len - Length of fils_erp_username in bytes
+ */
+ size_t fils_erp_username_len;
+
+ /**
+ * fils_erp_realm - Realm/domain name to use in FILS ERP
+ */
+ const u8 *fils_erp_realm;
+
+ /**
+ * fils_erp_realm_len - Length of fils_erp_realm in bytes
+ */
+ size_t fils_erp_realm_len;
+
+ /**
+ * fils_erp_next_seq_num - The next sequence number to use in FILS ERP
+ * messages
+ */
+ u16 fils_erp_next_seq_num;
+
+ /**
+ * fils_erp_rrk - Re-authentication root key (rRK) for the keyName-NAI
+ * specified by fils_erp_username@fils_erp_realm.
+ */
+ const u8 *fils_erp_rrk;
+
+ /**
+ * fils_erp_rrk_len - Length of fils_erp_rrk in bytes
+ */
+ size_t fils_erp_rrk_len;
+
+ /**
+ * sae_pwe - SAE mechanism for PWE derivation
+ * 0 = hunting-and-pecking loop only
+ * 1 = hash-to-element only
+ * 2 = both hunting-and-pecking loop and hash-to-element enabled
+ */
+ enum sae_pwe sae_pwe;
+
+ /**
+ * disable_eht - Disable EHT for this connection
+ */
+ int disable_eht;
+
+ /*
+ * mld_params - MLD association parameters
+ */
+ struct wpa_driver_mld_params mld_params;
+};
+
+enum hide_ssid {
+ NO_SSID_HIDING,
+ HIDDEN_SSID_ZERO_LEN,
+ HIDDEN_SSID_ZERO_CONTENTS
+};
+
+enum ch_switch_state {
+ CH_SW_STARTED,
+ CH_SW_FINISHED
+};
+
+struct wowlan_triggers {
+ u8 any;
+ u8 disconnect;
+ u8 magic_pkt;
+ u8 gtk_rekey_failure;
+ u8 eap_identity_req;
+ u8 four_way_handshake;
+ u8 rfkill_release;
+};
+
+struct wpa_driver_ap_params {
+ /**
+ * head - Beacon head from IEEE 802.11 header to IEs before TIM IE
+ */
+ u8 *head;
+
+ /**
+ * head_len - Length of the head buffer in octets
+ */
+ size_t head_len;
+
+ /**
+ * tail - Beacon tail following TIM IE
+ */
+ u8 *tail;
+
+ /**
+ * tail_len - Length of the tail buffer in octets
+ */
+ size_t tail_len;
+
+ /**
+ * dtim_period - DTIM period
+ */
+ int dtim_period;
+
+ /**
+ * beacon_int - Beacon interval
+ */
+ int beacon_int;
+
+ /**
+ * basic_rates: -1 terminated array of basic rates in 100 kbps
+ *
+ * This parameter can be used to set a specific basic rate set for the
+ * BSS. If %NULL, default basic rate set is used.
+ */
+ int *basic_rates;
+
+ /**
+ * beacon_rate: Beacon frame data rate
+ *
+ * This parameter can be used to set a specific Beacon frame data rate
+ * for the BSS. The interpretation of this value depends on the
+ * rate_type (legacy: in 100 kbps units, HT: HT-MCS, VHT: VHT-MCS,
+ * HE: HE-MCS). If beacon_rate == 0 and rate_type == 0
+ * (BEACON_RATE_LEGACY), the default Beacon frame data rate is used.
+ */
+ unsigned int beacon_rate;
+
+ /**
+ * beacon_rate_type: Beacon data rate type (legacy/HT/VHT/HE)
+ */
+ enum beacon_rate_type rate_type;
+
+ /**
+ * proberesp - Probe Response template
+ *
+ * This is used by drivers that reply to Probe Requests internally in
+ * AP mode and require the full Probe Response template.
+ */
+ u8 *proberesp;
+
+ /**
+ * proberesp_len - Length of the proberesp buffer in octets
+ */
+ size_t proberesp_len;
+
+ /**
+ * ssid - The SSID to use in Beacon/Probe Response frames
+ */
+ const u8 *ssid;
+
+ /**
+ * ssid_len - Length of the SSID (1..32)
+ */
+ size_t ssid_len;
+
+ /**
+ * hide_ssid - Whether to hide the SSID
+ */
+ enum hide_ssid hide_ssid;
+
+ /**
+ * pairwise_ciphers - WPA_CIPHER_* bitfield
+ */
+ unsigned int pairwise_ciphers;
+
+ /**
+ * group_cipher - WPA_CIPHER_*
+ */
+ unsigned int group_cipher;
+
+ /**
+ * key_mgmt_suites - WPA_KEY_MGMT_* bitfield
+ */
+ unsigned int key_mgmt_suites;
+
+ /**
+ * auth_algs - WPA_AUTH_ALG_* bitfield
+ */
+ unsigned int auth_algs;
+
+ /**
+ * wpa_version - WPA_PROTO_* bitfield
+ */
+ unsigned int wpa_version;
+
+ /**
+ * privacy - Whether privacy is used in the BSS
+ */
+ int privacy;
+
+ /**
+ * beacon_ies - WPS/P2P IE(s) for Beacon frames
+ *
+ * This is used to add IEs like WPS IE and P2P IE by drivers that do
+ * not use the full Beacon template.
+ */
+ const struct wpabuf *beacon_ies;
+
+ /**
+ * proberesp_ies - P2P/WPS IE(s) for Probe Response frames
+ *
+ * This is used to add IEs like WPS IE and P2P IE by drivers that
+ * reply to Probe Request frames internally.
+ */
+ const struct wpabuf *proberesp_ies;
+
+ /**
+ * assocresp_ies - WPS IE(s) for (Re)Association Response frames
+ *
+ * This is used to add IEs like WPS IE by drivers that reply to
+ * (Re)Association Request frames internally.
+ */
+ const struct wpabuf *assocresp_ies;
+
+ /**
+ * isolate - Whether to isolate frames between associated stations
+ *
+ * If this is non-zero, the AP is requested to disable forwarding of
+ * frames between associated stations.
+ */
+ int isolate;
+
+ /**
+ * cts_protect - Whether CTS protection is enabled
+ */
+ int cts_protect;
+
+ /**
+ * preamble - Whether short preamble is enabled
+ */
+ int preamble;
+
+ /**
+ * short_slot_time - Whether short slot time is enabled
+ *
+ * 0 = short slot time disable, 1 = short slot time enabled, -1 = do
+ * not set (e.g., when 802.11g mode is not in use)
+ */
+ int short_slot_time;
+
+ /**
+ * ht_opmode - HT operation mode or -1 if HT not in use
+ */
+ int ht_opmode;
+
+ /**
+ * interworking - Whether Interworking is enabled
+ */
+ int interworking;
+
+ /**
+ * hessid - Homogeneous ESS identifier or %NULL if not set
+ */
+ const u8 *hessid;
+
+ /**
+ * access_network_type - Access Network Type (0..15)
+ *
+ * This is used for filtering Probe Request frames when Interworking is
+ * enabled.
+ */
+ u8 access_network_type;
+
+ /**
+ * ap_max_inactivity - Timeout in seconds to detect STA's inactivity
+ *
+ * This is used by driver which advertises this capability.
+ */
+ int ap_max_inactivity;
+
+ /**
+ * ctwindow - Client Traffic Window (in TUs)
+ */
+ u8 p2p_go_ctwindow;
+
+ /**
+ * disable_dgaf - Whether group-addressed frames are disabled
+ */
+ int disable_dgaf;
+
+ /**
+ * osen - Whether OSEN security is enabled
+ */
+ int osen;
+
+ /**
+ * freq - Channel parameters for dynamic bandwidth changes
+ */
+ struct hostapd_freq_params *freq;
+
+ /**
+ * reenable - Whether this is to re-enable beaconing
+ */
+ int reenable;
+
+ /**
+ * pbss - Whether to start a PCP (in PBSS) instead of an AP in
+ * infrastructure BSS. Valid only for DMG network.
+ */
+ int pbss;
+
+ /**
+ * multicast_to_unicast - Whether to use multicast_to_unicast
+ *
+ * If this is non-zero, the AP is requested to perform multicast to
+ * unicast conversion for ARP, IPv4, and IPv6 frames (possibly within
+ * 802.1Q). If enabled, such frames are to be sent to each station
+ * separately, with the DA replaced by their own MAC address rather
+ * than the group address.
+ *
+ * Note that this may break certain expectations of the receiver, such
+ * as the ability to drop unicast IP packets received within multicast
+ * L2 frames, or the ability to not send ICMP destination unreachable
+ * messages for packets received in L2 multicast (which is required,
+ * but the receiver can't tell the difference if this new option is
+ * enabled.)
+ *
+ * This also doesn't implement the 802.11 DMS (directed multicast
+ * service).
+ */
+ int multicast_to_unicast;
+
+ /**
+ * ftm_responder - Whether FTM responder is enabled
+ */
+ int ftm_responder;
+
+ /**
+ * lci - Binary data, the content of an LCI report IE with type 8 as
+ * defined in IEEE Std 802.11-2016, 9.4.2.22.10
+ */
+ const struct wpabuf *lci;
+
+ /**
+ * civic - Binary data, the content of a measurement report IE with
+ * type 11 as defined in IEEE Std 802.11-2016, 9.4.2.22.13
+ */
+ const struct wpabuf *civic;
+
+ /**
+ * he_spr_ctrl - Spatial Reuse control field of SPR element
+ */
+ u8 he_spr_ctrl;
+
+ /**
+ * he_spr_non_srg_obss_pd_max_offset - Non-SRG Maximum TX power offset
+ */
+ u8 he_spr_non_srg_obss_pd_max_offset;
+
+ /**
+ * he_spr_srg_obss_pd_min_offset - Minimum TX power offset
+ */
+ u8 he_spr_srg_obss_pd_min_offset;
+
+ /**
+ * he_spr_srg_obss_pd_max_offset - Maximum TX power offset
+ */
+ u8 he_spr_srg_obss_pd_max_offset;
+
+ /**
+ * he_spr_bss_color_bitmap - BSS color values used by members of the
+ * SRG.
+ */
+ u8 he_spr_bss_color_bitmap[8];
+
+ /**
+ * he_spr_partial_bssid_bitmap - Partial BSSID values used by members
+ * of the SRG.
+ */
+ u8 he_spr_partial_bssid_bitmap[8];
+
+ /**
+ * he_bss_color - Whether the BSS Color is disabled
+ */
+ int he_bss_color_disabled;
+
+ /**
+ * he_bss_color_partial - The BSS Color AID equation
+ */
+ int he_bss_color_partial;
+
+ /**
+ * he_bss_color - The BSS Color of the AP
+ */
+ int he_bss_color;
+
+ /**
+ * twt_responder - Whether Target Wait Time responder is enabled
+ */
+ int twt_responder;
+
+ /**
+ * sae_pwe - SAE mechanism for PWE derivation
+ * 0 = hunting-and-pecking loop only
+ * 1 = hash-to-element only
+ * 2 = both hunting-and-pecking loop and hash-to-element enabled
+ */
+ enum sae_pwe sae_pwe;
+
+ /**
+ * FILS Discovery frame minimum interval in TUs
+ */
+ u32 fd_min_int;
+
+ /**
+ * FILS Discovery frame maximum interval in TUs (0 = FD frame disabled)
+ */
+ u32 fd_max_int;
+
+ /**
+ * FILS Discovery frame template data
+ */
+ u8 *fd_frame_tmpl;
+
+ /**
+ * FILS Discovery frame template length
+ */
+ size_t fd_frame_tmpl_len;
+
+ /**
+ * Unsolicited broadcast Probe Response interval in TUs
+ */
+ unsigned int unsol_bcast_probe_resp_interval;
+
+ /**
+ * Unsolicited broadcast Probe Response template data
+ */
+ u8 *unsol_bcast_probe_resp_tmpl;
+
+ /**
+ * Unsolicited broadcast Probe Response template length
+ */
+ size_t unsol_bcast_probe_resp_tmpl_len;
+
+ /**
+ * mbssid_tx_iface - Transmitting interface of the MBSSID set
+ */
+ const char *mbssid_tx_iface;
+
+ /**
+ * mbssid_index - The index of this BSS in the MBSSID set
+ */
+ unsigned int mbssid_index;
+
+ /**
+ * mbssid_elem - Buffer containing all MBSSID elements
+ */
+ u8 *mbssid_elem;
+
+ /**
+ * mbssid_elem_len - Total length of all MBSSID elements
+ */
+ size_t mbssid_elem_len;
+
+ /**
+ * mbssid_elem_count - The number of MBSSID elements
+ */
+ u8 mbssid_elem_count;
+
+ /**
+ * mbssid_elem_offset - Offsets to elements in mbssid_elem.
+ * Kernel will use these offsets to generate multiple BSSID beacons.
+ */
+ u8 **mbssid_elem_offset;
+
+ /**
+ * ema - Enhanced MBSSID advertisements support.
+ */
+ bool ema;
+
+ /**
+ * punct_bitmap - Preamble puncturing bitmap
+ * Each bit corresponds to a 20 MHz subchannel, the lowest bit for the
+ * channel with the lowest frequency. A bit set to 1 indicates that the
+ * subchannel is punctured, otherwise active.
+ */
+ u16 punct_bitmap;
+
+ /**
+ * rnr_elem - This buffer contains all of reduced neighbor report (RNR)
+ * elements
+ */
+ u8 *rnr_elem;
+
+ /**
+ * rnr_elem_len - Length of rnr_elem buffer
+ */
+ size_t rnr_elem_len;
+
+ /**
+ * rnr_elem_count - Number of RNR elements
+ */
+ unsigned int rnr_elem_count;
+
+ /**
+ * rnr_elem_offset - The offsets to the elements in rnr_elem.
+ * The driver will use these to include RNR elements in EMA beacons.
+ */
+ u8 **rnr_elem_offset;
+
+ /**
+ * allowed_freqs - List of allowed 20 MHz channel center frequencies in
+ * MHz for AP operation. Drivers which support this parameter will
+ * generate a new list based on this provided list by filtering out
+ * channels that cannot be used at that time due to regulatory or other
+ * constraints. The resulting list is used as the list of all allowed
+ * channels whenever performing operations like ACS and DFS.
+ */
+ int *allowed_freqs;
+
+ /*
+ * mld_ap - Whether operating as an AP MLD
+ */
+ bool mld_ap;
+
+ /**
+ * mld_link_id - Link id for MLD BSS's
+ */
+ u8 mld_link_id;
+};
+
+struct wpa_driver_mesh_bss_params {
+#define WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS 0x00000001
+#define WPA_DRIVER_MESH_CONF_FLAG_PEER_LINK_TIMEOUT 0x00000002
+#define WPA_DRIVER_MESH_CONF_FLAG_MAX_PEER_LINKS 0x00000004
+#define WPA_DRIVER_MESH_CONF_FLAG_HT_OP_MODE 0x00000008
+#define WPA_DRIVER_MESH_CONF_FLAG_RSSI_THRESHOLD 0x00000010
+#define WPA_DRIVER_MESH_CONF_FLAG_FORWARDING 0x00000020
+ /*
+ * TODO: Other mesh configuration parameters would go here.
+ * See NL80211_MESHCONF_* for all the mesh config parameters.
+ */
+ unsigned int flags;
+ int auto_plinks;
+ int peer_link_timeout;
+ int max_peer_links;
+ int rssi_threshold;
+ int forwarding;
+ u16 ht_opmode;
+};
+
+struct wpa_driver_mesh_join_params {
+ const u8 *meshid;
+ int meshid_len;
+ const int *basic_rates;
+ const u8 *ies;
+ int ie_len;
+ struct hostapd_freq_params freq;
+ int beacon_int;
+ int dtim_period;
+ struct wpa_driver_mesh_bss_params conf;
+#define WPA_DRIVER_MESH_FLAG_USER_MPM 0x00000001
+#define WPA_DRIVER_MESH_FLAG_DRIVER_MPM 0x00000002
+#define WPA_DRIVER_MESH_FLAG_SAE_AUTH 0x00000004
+#define WPA_DRIVER_MESH_FLAG_AMPE 0x00000008
+ unsigned int flags;
+ bool handle_dfs;
+ int mcast_rate;
+};
+
+struct wpa_driver_set_key_params {
+ /**
+ * ifname - Interface name (for multi-SSID/VLAN support) */
+ const char *ifname;
+
+ /**
+ * alg - Encryption algorithm
+ *
+ * (%WPA_ALG_NONE, %WPA_ALG_WEP, %WPA_ALG_TKIP, %WPA_ALG_CCMP,
+ * %WPA_ALG_BIP_AES_CMAC_128, %WPA_ALG_GCMP, %WPA_ALG_GCMP_256,
+ * %WPA_ALG_CCMP_256, %WPA_ALG_BIP_GMAC_128, %WPA_ALG_BIP_GMAC_256,
+ * %WPA_ALG_BIP_CMAC_256);
+ * %WPA_ALG_NONE clears the key. */
+ enum wpa_alg alg;
+
+ /**
+ * 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 */
+ const u8 *addr;
+
+ /**
+ * key_idx - Key index
+ *
+ * (0..3), usually 0 for unicast keys; 4..5 for IGTK; 6..7 for BIGTK */
+ int key_idx;
+
+ /**
+ * set_tx - Configure this key as the default Tx key
+ *
+ * Only used when driver does not support separate unicast/individual
+ * key */
+ int set_tx;
+
+ /**
+ * 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 */
+ const u8 *seq;
+
+ /**
+ * seq_len - Length of the seq, depends on the algorithm
+ *
+ * TKIP: 6 octets, CCMP/GCMP: 6 octets, IGTK: 6 octets */
+ size_t seq_len;
+
+ /**
+ * key - Key buffer
+ *
+ * TKIP: 16-byte temporal key, 8-byte Tx Mic key, 8-byte Rx Mic Key */
+ const u8 *key;
+
+ /**
+ * key_len - Length of the key buffer in octets
+ *
+ * WEP: 5 or 13, TKIP: 32, CCMP/GCMP: 16, IGTK: 16 */
+ size_t key_len;
+
+ /**
+ * vlan_id - VLAN index (0..4095) for VLAN offload cases */
+ int vlan_id;
+
+ /**
+ * key_flag - Additional key flags
+ *
+ * %KEY_FLAG_MODIFY
+ * Set when an already installed key must be updated.
+ * So far the only use-case is changing RX/TX status for
+ * pairwise keys. Must not be set when deleting a key.
+ * %KEY_FLAG_DEFAULT
+ * Set when the key is also a default key. Must not be set when
+ * deleting a key.
+ * %KEY_FLAG_RX
+ * The key is valid for RX. Must not be set when deleting a key.
+ * %KEY_FLAG_TX
+ * The key is valid for TX. Must not be set when deleting a key.
+ * %KEY_FLAG_GROUP
+ * The key is a broadcast or group key.
+ * %KEY_FLAG_PAIRWISE
+ * The key is a pairwise key.
+ * %KEY_FLAG_PMK
+ * The key is a Pairwise Master Key (PMK).
+ *
+ * Valid and pre-defined combinations are:
+ * %KEY_FLAG_GROUP_RX_TX
+ * WEP key not to be installed as default key.
+ * %KEY_FLAG_GROUP_RX_TX_DEFAULT
+ * Default WEP or WPA-NONE key.
+ * %KEY_FLAG_GROUP_RX
+ * GTK key valid for RX only.
+ * %KEY_FLAG_GROUP_TX_DEFAULT
+ * GTK key valid for TX only, immediately taking over TX.
+ * %KEY_FLAG_PAIRWISE_RX_TX
+ * Pairwise key immediately becoming the active pairwise key.
+ * %KEY_FLAG_PAIRWISE_RX
+ * Pairwise key not yet valid for TX. (Only usable when Extended
+ * Key ID is supported by the driver.)
+ * %KEY_FLAG_PAIRWISE_RX_TX_MODIFY
+ * Enable TX for a pairwise key installed with
+ * KEY_FLAG_PAIRWISE_RX.
+ *
+ * Not a valid standalone key type but pre-defined to be combined
+ * with other key_flags:
+ * %KEY_FLAG_RX_TX
+ * RX/TX key. */
+ enum key_flag key_flag;
+
+ /**
+ * link_id - MLO Link ID
+ *
+ * Set to a valid Link ID (0-14) when applicable, otherwise -1. */
+ int link_id;
+};
+
+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,
+
+ /**
+ * WPA_IF_P2P_DEVICE - P2P Device interface is used to identify the
+ * abstracted P2P Device function in the driver
+ */
+ WPA_IF_P2P_DEVICE,
+
+ /*
+ * WPA_IF_MESH - Mesh interface
+ */
+ WPA_IF_MESH,
+
+ /*
+ * WPA_IF_TDLS - TDLS offchannel interface (used for pref freq only)
+ */
+ WPA_IF_TDLS,
+
+ /*
+ * WPA_IF_IBSS - IBSS interface (used for pref freq only)
+ */
+ WPA_IF_IBSS,
+
+ /*
+ * WPA_IF_NAN - NAN Device
+ */
+ WPA_IF_NAN,
+
+ /* keep last */
+ WPA_IF_MAX
+};
+
+/**
+ * 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
+#define WPA_DRIVER_CAPA_KEY_MGMT_WAPI_PSK 0x00000080
+#define WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B 0x00000100
+#define WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192 0x00000200
+#define WPA_DRIVER_CAPA_KEY_MGMT_OWE 0x00000400
+#define WPA_DRIVER_CAPA_KEY_MGMT_DPP 0x00000800
+#define WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 0x00001000
+#define WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384 0x00002000
+#define WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256 0x00004000
+#define WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384 0x00008000
+#define WPA_DRIVER_CAPA_KEY_MGMT_SAE 0x00010000
+#define WPA_DRIVER_CAPA_KEY_MGMT_802_1X_SHA256 0x00020000
+#define WPA_DRIVER_CAPA_KEY_MGMT_PSK_SHA256 0x00040000
+#define WPA_DRIVER_CAPA_KEY_MGMT_TPK_HANDSHAKE 0x00080000
+#define WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE 0x00100000
+#define WPA_DRIVER_CAPA_KEY_MGMT_FT_802_1X_SHA384 0x00200000
+#define WPA_DRIVER_CAPA_KEY_MGMT_CCKM 0x00400000
+#define WPA_DRIVER_CAPA_KEY_MGMT_OSEN 0x00800000
+#define WPA_DRIVER_CAPA_KEY_MGMT_SAE_EXT_KEY 0x01000000
+#define WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE_EXT_KEY 0x02000000
+ /** Bitfield of supported key management suites */
+ unsigned int key_mgmt;
+ unsigned int key_mgmt_iftype[WPA_IF_MAX];
+
+#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
+#define WPA_DRIVER_CAPA_ENC_WEP128 0x00000010
+#define WPA_DRIVER_CAPA_ENC_GCMP 0x00000020
+#define WPA_DRIVER_CAPA_ENC_GCMP_256 0x00000040
+#define WPA_DRIVER_CAPA_ENC_CCMP_256 0x00000080
+#define WPA_DRIVER_CAPA_ENC_BIP 0x00000100
+#define WPA_DRIVER_CAPA_ENC_BIP_GMAC_128 0x00000200
+#define WPA_DRIVER_CAPA_ENC_BIP_GMAC_256 0x00000400
+#define WPA_DRIVER_CAPA_ENC_BIP_CMAC_256 0x00000800
+#define WPA_DRIVER_CAPA_ENC_GTK_NOT_USED 0x00001000
+ /** Bitfield of supported cipher suites */
+ unsigned int enc;
+
+#define WPA_DRIVER_AUTH_OPEN 0x00000001
+#define WPA_DRIVER_AUTH_SHARED 0x00000002
+#define WPA_DRIVER_AUTH_LEAP 0x00000004
+ /** Bitfield of supported IEEE 802.11 authentication algorithms */
+ 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
+/** Driver takes care of all DFS operations */
+#define WPA_DRIVER_FLAGS_DFS_OFFLOAD 0x00000004
+/** Driver takes care of RSN 4-way handshake internally; PMK is configured with
+ * struct wpa_driver_ops::set_key using key_flag = KEY_FLAG_PMK */
+#define WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X 0x00000008
+/** Driver is for a wired Ethernet interface */
+#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 supports dynamic HT 20/40 MHz channel changes during BSS lifetime */
+#define WPA_DRIVER_FLAGS_HT_2040_COEX 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 GO or P2P Client) */
+#define WPA_DRIVER_FLAGS_P2P_CAPABLE 0x00000800
+/** Driver supports station and key removal when stopping an AP */
+#define WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT 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 valid 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_VALID_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
+/** Driver indicates TX status events for Deauth/Disassoc frames */
+#define WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS 0x00020000
+/** Driver supports roaming (BSS selection) in firmware */
+#define WPA_DRIVER_FLAGS_BSS_SELECTION 0x00040000
+/** Driver supports operating as a TDLS peer */
+#define WPA_DRIVER_FLAGS_TDLS_SUPPORT 0x00080000
+/** Driver requires external TDLS setup/teardown/discovery */
+#define WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP 0x00100000
+/** Driver indicates support for Probe Response offloading in AP mode */
+#define WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD 0x00200000
+/** Driver supports U-APSD in AP mode */
+#define WPA_DRIVER_FLAGS_AP_UAPSD 0x00400000
+/** Driver supports inactivity timer in AP mode */
+#define WPA_DRIVER_FLAGS_INACTIVITY_TIMER 0x00800000
+/** Driver expects user space implementation of MLME in AP mode */
+#define WPA_DRIVER_FLAGS_AP_MLME 0x01000000
+/** Driver supports SAE with user space SME */
+#define WPA_DRIVER_FLAGS_SAE 0x02000000
+/** Driver makes use of OBSS scan mechanism in wpa_supplicant */
+#define WPA_DRIVER_FLAGS_OBSS_SCAN 0x04000000
+/** Driver supports IBSS (Ad-hoc) mode */
+#define WPA_DRIVER_FLAGS_IBSS 0x08000000
+/** Driver supports radar detection */
+#define WPA_DRIVER_FLAGS_RADAR 0x10000000
+/** Driver supports a dedicated interface for P2P Device */
+#define WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE 0x20000000
+/** Driver supports QoS Mapping */
+#define WPA_DRIVER_FLAGS_QOS_MAPPING 0x40000000
+/** Driver supports CSA in AP mode */
+#define WPA_DRIVER_FLAGS_AP_CSA 0x80000000
+/** Driver supports mesh */
+#define WPA_DRIVER_FLAGS_MESH 0x0000000100000000ULL
+/** Driver support ACS offload */
+#define WPA_DRIVER_FLAGS_ACS_OFFLOAD 0x0000000200000000ULL
+/** Driver supports key management offload */
+#define WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD 0x0000000400000000ULL
+/** Driver supports TDLS channel switching */
+#define WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH 0x0000000800000000ULL
+/** Driver supports IBSS with HT datarates */
+#define WPA_DRIVER_FLAGS_HT_IBSS 0x0000001000000000ULL
+/** Driver supports IBSS with VHT datarates */
+#define WPA_DRIVER_FLAGS_VHT_IBSS 0x0000002000000000ULL
+/** Driver supports automatic band selection */
+#define WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY 0x0000004000000000ULL
+/** Driver supports simultaneous off-channel operations */
+#define WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS 0x0000008000000000ULL
+/** Driver supports full AP client state */
+#define WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE 0x0000010000000000ULL
+/** Driver supports P2P Listen offload */
+#define WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD 0x0000020000000000ULL
+/** Driver supports FILS */
+#define WPA_DRIVER_FLAGS_SUPPORT_FILS 0x0000040000000000ULL
+/** Driver supports Beacon frame TX rate configuration (legacy rates) */
+#define WPA_DRIVER_FLAGS_BEACON_RATE_LEGACY 0x0000080000000000ULL
+/** Driver supports Beacon frame TX rate configuration (HT rates) */
+#define WPA_DRIVER_FLAGS_BEACON_RATE_HT 0x0000100000000000ULL
+/** Driver supports Beacon frame TX rate configuration (VHT rates) */
+#define WPA_DRIVER_FLAGS_BEACON_RATE_VHT 0x0000200000000000ULL
+/** Driver supports mgmt_tx with random TX address in non-connected state */
+#define WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA 0x0000400000000000ULL
+/** Driver supports mgmt_tx with random TX addr in connected state */
+#define WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA_CONNECTED 0x0000800000000000ULL
+/** Driver supports better BSS reporting with sched_scan in connected mode */
+#define WPA_DRIVER_FLAGS_SCHED_SCAN_RELATIVE_RSSI 0x0001000000000000ULL
+/** Driver supports HE capabilities */
+#define WPA_DRIVER_FLAGS_HE_CAPABILITIES 0x0002000000000000ULL
+/** Driver supports FILS shared key offload */
+#define WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD 0x0004000000000000ULL
+/** Driver supports all OCE STA specific mandatory features */
+#define WPA_DRIVER_FLAGS_OCE_STA 0x0008000000000000ULL
+/** Driver supports all OCE AP specific mandatory features */
+#define WPA_DRIVER_FLAGS_OCE_AP 0x0010000000000000ULL
+/**
+ * Driver supports all OCE STA-CFON specific mandatory features only.
+ * If a driver sets this bit but not the %WPA_DRIVER_FLAGS_OCE_AP, the
+ * userspace shall assume that this driver may not support all OCE AP
+ * functionality but can support only OCE STA-CFON functionality.
+ */
+#define WPA_DRIVER_FLAGS_OCE_STA_CFON 0x0020000000000000ULL
+/** Driver supports MFP-optional in the connect command */
+#define WPA_DRIVER_FLAGS_MFP_OPTIONAL 0x0040000000000000ULL
+/** Driver is a self-managed regulatory device */
+#define WPA_DRIVER_FLAGS_SELF_MANAGED_REGULATORY 0x0080000000000000ULL
+/** Driver supports FTM responder functionality */
+#define WPA_DRIVER_FLAGS_FTM_RESPONDER 0x0100000000000000ULL
+/** Driver support 4-way handshake offload for WPA-Personal */
+#define WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK 0x0200000000000000ULL
+/** Driver supports a separate control port TX for EAPOL frames */
+#define WPA_DRIVER_FLAGS_CONTROL_PORT 0x0400000000000000ULL
+/** Driver supports VLAN offload */
+#define WPA_DRIVER_FLAGS_VLAN_OFFLOAD 0x0800000000000000ULL
+/** Driver supports UPDATE_FT_IES command */
+#define WPA_DRIVER_FLAGS_UPDATE_FT_IES 0x1000000000000000ULL
+/** Driver can correctly rekey PTKs without Extended Key ID */
+#define WPA_DRIVER_FLAGS_SAFE_PTK0_REKEYS 0x2000000000000000ULL
+/** Driver supports Beacon protection */
+#define WPA_DRIVER_FLAGS_BEACON_PROTECTION 0x4000000000000000ULL
+/** Driver supports Extended Key ID */
+#define WPA_DRIVER_FLAGS_EXTENDED_KEY_ID 0x8000000000000000ULL
+ u64 flags;
+
+/** Driver supports a separate control port RX for EAPOL frames */
+#define WPA_DRIVER_FLAGS2_CONTROL_PORT_RX 0x0000000000000001ULL
+/** Driver supports TX status reports for EAPOL frames through control port */
+#define WPA_DRIVER_FLAGS2_CONTROL_PORT_TX_STATUS 0x0000000000000002ULL
+/** Driver supports secure LTF in AP mode */
+#define WPA_DRIVER_FLAGS2_SEC_LTF_AP 0x0000000000000004ULL
+/** Driver supports secure RTT measurement exchange in AP mode */
+#define WPA_DRIVER_FLAGS2_SEC_RTT_AP 0x0000000000000008ULL
+/**
+ * Driver supports protection of range negotiation and measurement management
+ * frames in AP mode
+ */
+#define WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP 0x0000000000000010ULL
+/** Driver supports Beacon frame TX rate configuration (HE rates) */
+#define WPA_DRIVER_FLAGS2_BEACON_RATE_HE 0x0000000000000020ULL
+/** Driver supports Beacon protection only in client mode */
+#define WPA_DRIVER_FLAGS2_BEACON_PROTECTION_CLIENT 0x0000000000000040ULL
+/** Driver supports Operating Channel Validation */
+#define WPA_DRIVER_FLAGS2_OCV 0x0000000000000080ULL
+/** Driver expects user space implementation of SME in AP mode */
+#define WPA_DRIVER_FLAGS2_AP_SME 0x0000000000000100ULL
+/** Driver handles SA Query procedures in AP mode */
+#define WPA_DRIVER_FLAGS2_SA_QUERY_OFFLOAD_AP 0x0000000000000200ULL
+/** Driver supports background radar/CAC detection */
+#define WPA_DRIVER_RADAR_BACKGROUND 0x0000000000000400ULL
+/** Driver supports secure LTF in STA mode */
+#define WPA_DRIVER_FLAGS2_SEC_LTF_STA 0x0000000000000800ULL
+/** Driver supports secure RTT measurement exchange in STA mode */
+#define WPA_DRIVER_FLAGS2_SEC_RTT_STA 0x0000000000001000ULL
+/**
+ * Driver supports protection of range negotiation and measurement management
+ * frames in STA mode
+ */
+#define WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_STA 0x0000000000002000ULL
+/** Driver supports MLO in station/AP mode */
+#define WPA_DRIVER_FLAGS2_MLO 0x0000000000004000ULL
+ u64 flags2;
+
+#define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \
+ (drv_flags & WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE)
+
+ unsigned int wmm_ac_supported:1;
+
+ unsigned int mac_addr_rand_scan_supported:1;
+ unsigned int mac_addr_rand_sched_scan_supported:1;
+
+ /** Maximum number of supported active probe SSIDs */
+ int max_scan_ssids;
+
+ /** Maximum number of supported active probe SSIDs for sched_scan */
+ int max_sched_scan_ssids;
+
+ /** Maximum number of supported scan plans for scheduled scan */
+ unsigned int max_sched_scan_plans;
+
+ /** Maximum interval in a scan plan. In seconds */
+ u32 max_sched_scan_plan_interval;
+
+ /** Maximum number of iterations in a single scan plan */
+ u32 max_sched_scan_plan_iterations;
+
+ /** Whether sched_scan (offloaded scanning) is supported */
+ int sched_scan_supported;
+
+ /** Maximum number of supported match sets for sched_scan */
+ int max_match_sets;
+
+ /**
+ * 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;
+
+ /**
+ * probe_resp_offloads - Bitmap of supported protocols by the driver
+ * for Probe Response offloading.
+ */
+/** Driver Probe Response offloading support for WPS ver. 1 */
+#define WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS 0x00000001
+/** Driver Probe Response offloading support for WPS ver. 2 */
+#define WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2 0x00000002
+/** Driver Probe Response offloading support for P2P */
+#define WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P 0x00000004
+/** Driver Probe Response offloading support for IEEE 802.11u (Interworking) */
+#define WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING 0x00000008
+ unsigned int probe_resp_offloads;
+
+ unsigned int max_acl_mac_addrs;
+
+ /**
+ * Number of supported concurrent channels
+ */
+ unsigned int num_multichan_concurrent;
+
+ /**
+ * extended_capa - extended capabilities in driver/device
+ *
+ * Must be allocated and freed by driver and the pointers must be
+ * valid for the lifetime of the driver, i.e., freed in deinit()
+ */
+ const u8 *extended_capa, *extended_capa_mask;
+ unsigned int extended_capa_len;
+
+ struct wowlan_triggers wowlan_triggers;
+
+/** Driver adds the DS Params Set IE in Probe Request frames */
+#define WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES 0x00000001
+/** Driver adds the WFA TPC IE in Probe Request frames */
+#define WPA_DRIVER_FLAGS_WFA_TPC_IE_IN_PROBES 0x00000002
+/** Driver handles quiet period requests */
+#define WPA_DRIVER_FLAGS_QUIET 0x00000004
+/**
+ * Driver is capable of inserting the current TX power value into the body of
+ * transmitted frames.
+ * Background: Some Action frames include a TPC Report IE. This IE contains a
+ * TX power field, which has to be updated by lower layers. One such Action
+ * frame is Link Measurement Report (part of RRM). Another is TPC Report (part
+ * of spectrum management). Note that this insertion takes place at a fixed
+ * offset, namely the 6th byte in the Action frame body.
+ */
+#define WPA_DRIVER_FLAGS_TX_POWER_INSERTION 0x00000008
+/**
+ * Driver supports RRM. With this support, the driver will accept to use RRM in
+ * (Re)Association Request frames, without supporting quiet period.
+ */
+#define WPA_DRIVER_FLAGS_SUPPORT_RRM 0x00000010
+
+/** Driver supports setting the scan dwell time */
+#define WPA_DRIVER_FLAGS_SUPPORT_SET_SCAN_DWELL 0x00000020
+/** Driver supports Beacon Report Measurement */
+#define WPA_DRIVER_FLAGS_SUPPORT_BEACON_REPORT 0x00000040
+
+ u32 rrm_flags;
+
+ /* Driver concurrency capabilities */
+ unsigned int conc_capab;
+ /* Maximum number of concurrent channels on 2.4 GHz */
+ unsigned int max_conc_chan_2_4;
+ /* Maximum number of concurrent channels on 5 GHz */
+ unsigned int max_conc_chan_5_0;
+
+ /* Maximum number of supported CSA counters */
+ u16 max_csa_counters;
+
+ /* Maximum number of supported AKM suites in commands */
+ unsigned int max_num_akms;
+
+ /* Maximum number of interfaces supported for MBSSID advertisement */
+ unsigned int mbssid_max_interfaces;
+ /* Maximum profile periodicity for enhanced MBSSID advertisement */
+ unsigned int ema_max_periodicity;
+};
+
+
+struct hostapd_data;
+
+enum guard_interval {
+ GUARD_INTERVAL_0_4 = 1,
+ GUARD_INTERVAL_0_8 = 2,
+ GUARD_INTERVAL_1_6 = 3,
+ GUARD_INTERVAL_3_2 = 4,
+};
+
+#define STA_DRV_DATA_TX_MCS BIT(0)
+#define STA_DRV_DATA_RX_MCS BIT(1)
+#define STA_DRV_DATA_TX_VHT_MCS BIT(2)
+#define STA_DRV_DATA_RX_VHT_MCS BIT(3)
+#define STA_DRV_DATA_TX_VHT_NSS BIT(4)
+#define STA_DRV_DATA_RX_VHT_NSS BIT(5)
+#define STA_DRV_DATA_TX_SHORT_GI BIT(6)
+#define STA_DRV_DATA_RX_SHORT_GI BIT(7)
+#define STA_DRV_DATA_LAST_ACK_RSSI BIT(8)
+#define STA_DRV_DATA_CONN_TIME BIT(9)
+#define STA_DRV_DATA_TX_HE_MCS BIT(10)
+#define STA_DRV_DATA_RX_HE_MCS BIT(11)
+#define STA_DRV_DATA_TX_HE_NSS BIT(12)
+#define STA_DRV_DATA_RX_HE_NSS BIT(13)
+#define STA_DRV_DATA_TX_HE_DCM BIT(14)
+#define STA_DRV_DATA_RX_HE_DCM BIT(15)
+#define STA_DRV_DATA_TX_HE_GI BIT(16)
+#define STA_DRV_DATA_RX_HE_GI BIT(17)
+
+struct hostap_sta_driver_data {
+ unsigned long rx_packets, tx_packets;
+ unsigned long long rx_bytes, tx_bytes;
+ unsigned long long rx_airtime, tx_airtime;
+ unsigned long long beacons_count;
+ int bytes_64bit; /* whether 64-bit byte counters are supported */
+ unsigned long current_tx_rate; /* in kbps */
+ unsigned long current_rx_rate; /* in kbps */
+ unsigned long inactive_msec;
+ unsigned long connected_sec;
+ unsigned long flags; /* bitfield of STA_DRV_DATA_* */
+ unsigned long num_ps_buf_frames;
+ unsigned long tx_retry_failed;
+ unsigned long tx_retry_count;
+ s8 last_ack_rssi;
+ unsigned long backlog_packets;
+ unsigned long backlog_bytes;
+ unsigned long fcs_error_count;
+ unsigned long beacon_loss_count;
+ unsigned long expected_throughput;
+ unsigned long rx_drop_misc;
+ unsigned long rx_mpdus;
+ int signal; /* dBm; or -WPA_INVALID_NOISE */
+ u8 rx_hemcs;
+ u8 tx_hemcs;
+ u8 rx_vhtmcs;
+ u8 tx_vhtmcs;
+ u8 rx_mcs;
+ u8 tx_mcs;
+ u8 rx_he_nss;
+ u8 tx_he_nss;
+ u8 rx_vht_nss;
+ u8 tx_vht_nss;
+ s8 avg_signal; /* dBm */
+ s8 avg_beacon_signal; /* dBm */
+ s8 avg_ack_signal; /* dBm */
+ enum guard_interval rx_guard_interval, tx_guard_interval;
+ u8 rx_dcm, tx_dcm;
+};
+
+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;
+ const struct ieee80211_vht_capabilities *vht_capabilities;
+ int vht_opmode_enabled;
+ u8 vht_opmode;
+ const struct ieee80211_he_capabilities *he_capab;
+ size_t he_capab_len;
+ const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab;
+ const struct ieee80211_eht_capabilities *eht_capab;
+ size_t eht_capab_len;
+ u32 flags; /* bitmask of WPA_STA_* flags */
+ u32 flags_mask; /* unset bits in flags */
+#ifdef CONFIG_MESH
+ enum mesh_plink_state plink_state;
+ u16 peer_aid;
+#endif /* CONFIG_MESH */
+ int set; /* Set STA parameters instead of add */
+ u8 qosinfo;
+ const u8 *ext_capab;
+ size_t ext_capab_len;
+ const u8 *supp_channels;
+ size_t supp_channels_len;
+ const u8 *supp_oper_classes;
+ size_t supp_oper_classes_len;
+ int support_p2p_ps;
+
+ bool mld_link_sta;
+ s8 mld_link_id;
+ const u8 *mld_link_addr;
+};
+
+struct mac_address {
+ u8 addr[ETH_ALEN];
+};
+
+struct hostapd_acl_params {
+ u8 acl_policy;
+ unsigned int num_mac_acl;
+ struct mac_address mac_acl[0];
+};
+
+struct wpa_init_params {
+ void *global_priv;
+ const u8 *bssid;
+ const char *ifname;
+ const char *driver_params;
+ 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)
+#define WPA_STA_TDLS_PEER BIT(4)
+#define WPA_STA_AUTHENTICATED BIT(5)
+#define WPA_STA_ASSOCIATED BIT(6)
+
+enum tdls_oper {
+ TDLS_DISCOVERY_REQ,
+ TDLS_SETUP,
+ TDLS_TEARDOWN,
+ TDLS_ENABLE_LINK,
+ TDLS_DISABLE_LINK,
+ TDLS_ENABLE,
+ TDLS_DISABLE
+};
+
+enum wnm_oper {
+ WNM_SLEEP_ENTER_CONFIRM,
+ WNM_SLEEP_ENTER_FAIL,
+ WNM_SLEEP_EXIT_CONFIRM,
+ WNM_SLEEP_EXIT_FAIL,
+ WNM_SLEEP_TFS_REQ_IE_ADD, /* STA requests driver to add TFS req IE */
+ WNM_SLEEP_TFS_REQ_IE_NONE, /* STA requests empty TFS req IE */
+ WNM_SLEEP_TFS_REQ_IE_SET, /* AP requests driver to set TFS req IE for
+ * a STA */
+ WNM_SLEEP_TFS_RESP_IE_ADD, /* AP requests driver to add TFS resp IE
+ * for a STA */
+ WNM_SLEEP_TFS_RESP_IE_NONE, /* AP requests empty TFS resp IE */
+ WNM_SLEEP_TFS_RESP_IE_SET, /* AP requests driver to set TFS resp IE
+ * for a STA */
+ WNM_SLEEP_TFS_IE_DEL /* AP delete the TFS IE */
+};
+
+/* enum smps_mode - SMPS mode definitions */
+enum smps_mode {
+ SMPS_AUTOMATIC,
+ SMPS_OFF,
+ SMPS_DYNAMIC,
+ SMPS_STATIC,
+
+ /* Keep last */
+ SMPS_INVALID,
+};
+
+#define WPA_INVALID_NOISE 9999
+
+/**
+ * struct wpa_signal_info - Information about channel signal quality
+ * @frequency: control frequency
+ * @above_threshold: true if the above threshold was crossed
+ * (relevant for a CQM event)
+ * @data: STA information
+ * @current_noise: %WPA_INVALID_NOISE if not supported
+ * @chanwidth: channel width
+ * @center_frq1: center frequency for the first segment
+ * @center_frq2: center frequency for the second segment (if relevant)
+ */
+struct wpa_signal_info {
+ u32 frequency;
+ int above_threshold;
+ struct hostap_sta_driver_data data;
+ int current_noise;
+ enum chan_width chanwidth;
+ int center_frq1;
+ int center_frq2;
+};
+
+struct wpa_mlo_signal_info {
+ u16 valid_links;
+ struct wpa_signal_info links[MAX_NUM_MLD_LINKS];
+};
+
+/**
+ * struct wpa_channel_info - Information about the current channel
+ * @frequency: Center frequency of the primary 20 MHz channel
+ * @chanwidth: Width of the current operating channel
+ * @sec_channel: Location of the secondary 20 MHz channel (either +1 or -1).
+ * This field is only filled in when using a 40 MHz channel.
+ * @center_frq1: Center frequency of frequency segment 0
+ * @center_frq2: Center frequency of frequency segment 1 (for 80+80 channels)
+ * @seg1_idx: Frequency segment 1 index when using a 80+80 channel. This is
+ * derived from center_frq2 for convenience.
+ */
+struct wpa_channel_info {
+ u32 frequency;
+ enum chan_width chanwidth;
+ int sec_channel;
+ int center_frq1;
+ int center_frq2;
+ u8 seg1_idx;
+};
+
+/**
+ * struct beacon_data - Beacon data
+ * @head: Head portion of Beacon frame (before TIM IE)
+ * @tail: Tail portion of Beacon frame (after TIM IE)
+ * @beacon_ies: Extra information element(s) to add into Beacon frames or %NULL
+ * @proberesp_ies: Extra information element(s) to add into Probe Response
+ * frames or %NULL
+ * @assocresp_ies: Extra information element(s) to add into (Re)Association
+ * Response frames or %NULL
+ * @probe_resp: Probe Response frame template
+ * @head_len: Length of @head
+ * @tail_len: Length of @tail
+ * @beacon_ies_len: Length of beacon_ies in octets
+ * @proberesp_ies_len: Length of proberesp_ies in octets
+ * @proberesp_ies_len: Length of proberesp_ies in octets
+ * @probe_resp_len: Length of probe response template (@probe_resp)
+ */
+struct beacon_data {
+ u8 *head, *tail;
+ u8 *beacon_ies;
+ u8 *proberesp_ies;
+ u8 *assocresp_ies;
+ u8 *probe_resp;
+
+ size_t head_len, tail_len;
+ size_t beacon_ies_len;
+ size_t proberesp_ies_len;
+ size_t assocresp_ies_len;
+ size_t probe_resp_len;
+};
+
+/**
+ * struct csa_settings - Settings for channel switch command
+ * @cs_count: Count in Beacon frames (TBTT) to perform the switch
+ * @block_tx: 1 - block transmission for CSA period
+ * @freq_params: Next channel frequency parameter
+ * @beacon_csa: Beacon/probe resp/asooc resp info for CSA period
+ * @beacon_after: Next beacon/probe resp/asooc resp info
+ * @counter_offset_beacon: Offset to the count field in beacon's tail
+ * @counter_offset_presp: Offset to the count field in probe resp.
+ * @punct_bitmap - Preamble puncturing bitmap
+ */
+struct csa_settings {
+ u8 cs_count;
+ u8 block_tx;
+
+ struct hostapd_freq_params freq_params;
+ struct beacon_data beacon_csa;
+ struct beacon_data beacon_after;
+
+ u16 counter_offset_beacon[2];
+ u16 counter_offset_presp[2];
+
+ u16 punct_bitmap;
+};
+
+/**
+ * struct cca_settings - Settings for color switch command
+ * @cca_count: Count in Beacon frames (TBTT) to perform the switch
+ * @cca_color: The new color that we are switching to
+ * @beacon_cca: Beacon/Probe Response/(Re)Association Response frame info for
+ * color switch period
+ * @beacon_after: Next Beacon/Probe Response/(Re)Association Response frame info
+ * @counter_offset_beacon: Offset to the count field in Beacon frame tail
+ * @counter_offset_presp: Offset to the count field in Probe Response frame
+ */
+struct cca_settings {
+ u8 cca_count;
+ u8 cca_color;
+
+ struct beacon_data beacon_cca;
+ struct beacon_data beacon_after;
+
+ u16 counter_offset_beacon;
+ u16 counter_offset_presp;
+};
+
+/* TDLS peer capabilities for send_tdls_mgmt() */
+enum tdls_peer_capability {
+ TDLS_PEER_HT = BIT(0),
+ TDLS_PEER_VHT = BIT(1),
+ TDLS_PEER_WMM = BIT(2),
+ TDLS_PEER_HE = BIT(3),
+};
+
+/* valid info in the wmm_params struct */
+enum wmm_params_valid_info {
+ WMM_PARAMS_UAPSD_QUEUES_INFO = BIT(0),
+};
+
+/**
+ * struct wmm_params - WMM parameterss configured for this association
+ * @info_bitmap: Bitmap of valid wmm_params info; indicates what fields
+ * of the struct contain valid information.
+ * @uapsd_queues: Bitmap of ACs configured for uapsd (valid only if
+ * %WMM_PARAMS_UAPSD_QUEUES_INFO is set)
+ */
+struct wmm_params {
+ u8 info_bitmap;
+ u8 uapsd_queues;
+};
+
+#ifdef CONFIG_MACSEC
+struct macsec_init_params {
+ bool always_include_sci;
+ bool use_es;
+ bool use_scb;
+};
+#endif /* CONFIG_MACSEC */
+
+enum drv_br_port_attr {
+ DRV_BR_PORT_ATTR_PROXYARP,
+ DRV_BR_PORT_ATTR_HAIRPIN_MODE,
+ DRV_BR_PORT_ATTR_MCAST2UCAST,
+};
+
+enum drv_br_net_param {
+ DRV_BR_NET_PARAM_GARP_ACCEPT,
+ DRV_BR_MULTICAST_SNOOPING,
+};
+
+struct drv_acs_params {
+ /* Selected mode (HOSTAPD_MODE_*) */
+ enum hostapd_hw_mode hw_mode;
+
+ /* Indicates whether HT is enabled */
+ int ht_enabled;
+
+ /* Indicates whether HT40 is enabled */
+ int ht40_enabled;
+
+ /* Indicates whether VHT is enabled */
+ int vht_enabled;
+
+ /* Configured ACS channel width */
+ u16 ch_width;
+
+ /* ACS frequency list info */
+ const int *freq_list;
+
+ /* Indicates whether EDMG is enabled */
+ int edmg_enabled;
+
+ /* Indicates whether EHT is enabled */
+ bool eht_enabled;
+};
+
+struct wpa_bss_trans_info {
+ u8 mbo_transition_reason;
+ u8 n_candidates;
+ u8 *bssid;
+};
+
+struct wpa_bss_candidate_info {
+ u8 num;
+ struct candidate_list {
+ u8 bssid[ETH_ALEN];
+ u8 is_accept;
+ u32 reject_reason;
+ } *candidates;
+};
+
+struct wpa_pmkid_params {
+ const u8 *bssid;
+ const u8 *ssid;
+ size_t ssid_len;
+ const u8 *fils_cache_id;
+ const u8 *pmkid;
+ const u8 *pmk;
+ size_t pmk_len;
+ u32 pmk_lifetime;
+ u8 pmk_reauth_threshold;
+};
+
+/* Mask used to specify which connection parameters have to be updated */
+enum wpa_drv_update_connect_params_mask {
+ WPA_DRV_UPDATE_ASSOC_IES = BIT(0),
+ WPA_DRV_UPDATE_FILS_ERP_INFO = BIT(1),
+ WPA_DRV_UPDATE_AUTH_TYPE = BIT(2),
+};
+
+/**
+ * struct external_auth - External authentication trigger parameters
+ *
+ * These are used across the external authentication request and event
+ * interfaces.
+ * @action: Action type / trigger for external authentication. Only significant
+ * for the event interface.
+ * @bssid: BSSID of the peer with which the authentication has to happen. Used
+ * by both the request and event interface.
+ * @ssid: SSID of the AP. Used by both the request and event interface.
+ * @ssid_len: SSID length in octets.
+ * @key_mgmt_suite: AKM suite of the respective authentication. Optional for
+ * the request interface.
+ * @status: Status code, %WLAN_STATUS_SUCCESS for successful authentication,
+ * use %WLAN_STATUS_UNSPECIFIED_FAILURE if wpa_supplicant cannot give
+ * the real status code for failures. Used only for the request interface
+ * from user space to the driver.
+ * @pmkid: Generated PMKID as part of external auth exchange (e.g., SAE).
+ * @mld_addr: AP's MLD address or %NULL if MLO is not used
+ */
+struct external_auth {
+ enum {
+ EXT_AUTH_START,
+ EXT_AUTH_ABORT,
+ } action;
+ const u8 *bssid;
+ const u8 *ssid;
+ size_t ssid_len;
+ unsigned int key_mgmt_suite;
+ u16 status;
+ const u8 *pmkid;
+ const u8 *mld_addr;
+};
+
+#define WPAS_MAX_PASN_PEERS 10
+
+enum pasn_status {
+ PASN_STATUS_SUCCESS = 0,
+ PASN_STATUS_FAILURE = 1,
+};
+
+/**
+ * struct pasn_peer - PASN peer parameters
+ *
+ * Used to process the PASN authentication event from the driver to
+ * userspace and to send a response back.
+ * @own_addr: Own MAC address specified by the driver to use for PASN
+ * handshake.
+ * @peer_addr: MAC address of the peer with which PASN authentication is to be
+ * performed.
+ * @network_id: Unique id for the network.
+ * This identifier is used as a unique identifier for each network
+ * block when using the control interface. Each network is allocated an
+ * id when it is being created, either when reading the configuration
+ * file or when a new network is added through the control interface.
+ * @akmp: Authentication key management protocol type supported.
+ * @cipher: Cipher suite.
+ * @group: Finite cyclic group. Default group used is 19 (ECC).
+ * @ltf_keyseed_required: Indicates whether LTF keyseed generation is required
+ * @status: PASN response status, %PASN_STATUS_SUCCESS for successful
+ * authentication, use %PASN_STATUS_FAILURE if PASN authentication
+ * fails or if wpa_supplicant fails to set the security ranging context to
+ * the driver
+ */
+struct pasn_peer {
+ u8 own_addr[ETH_ALEN];
+ u8 peer_addr[ETH_ALEN];
+ int network_id;
+ int akmp;
+ int cipher;
+ int group;
+ bool ltf_keyseed_required;
+ enum pasn_status status;
+};
+
+/**
+ * struct pasn_auth - PASN authentication trigger parameters
+ *
+ * These are used across the PASN authentication event from the driver to
+ * userspace and to send a response to it.
+ * @action: Action type. Only significant for the event interface.
+ * @num_peers: The number of peers for which the PASN handshake is requested
+ * for.
+ * @peer: Holds the peer details.
+ */
+struct pasn_auth {
+ enum {
+ PASN_ACTION_AUTH,
+ PASN_ACTION_DELETE_SECURE_RANGING_CONTEXT,
+ } action;
+ unsigned int num_peers;
+ struct pasn_peer peer[WPAS_MAX_PASN_PEERS];
+};
+
+/**
+ * struct secure_ranging_params - Parameters required to set secure ranging
+ * context for a peer.
+ *
+ * @action: Add or delete a security context to the driver.
+ * @own_addr: Own MAC address used during key derivation.
+ * @peer_addr: Address of the peer device.
+ * @cipher: Cipher suite.
+ * @tk_len: Length of temporal key.
+ * @tk: Temporal key buffer.
+ * @ltf_keyseed_len: Length of LTF keyseed.
+ * @ltf_keyeed: LTF keyseed buffer.
+ */
+struct secure_ranging_params {
+ u32 action;
+ const u8 *own_addr;
+ const u8 *peer_addr;
+ u32 cipher;
+ u8 tk_len;
+ const u8 *tk;
+ u8 ltf_keyseed_len;
+ const u8 *ltf_keyseed;
+};
+
+/* enum nested_attr - Used to specify if subcommand uses nested attributes */
+enum nested_attr {
+ NESTED_ATTR_NOT_USED = 0,
+ NESTED_ATTR_USED = 1,
+ NESTED_ATTR_UNSPECIFIED = 2,
+};
+
+/* Preferred channel list information */
+
+/* GO role */
+#define WEIGHTED_PCL_GO BIT(0)
+/* P2P Client role */
+#define WEIGHTED_PCL_CLI BIT(1)
+/* Must be considered for operating channel */
+#define WEIGHTED_PCL_MUST_CONSIDER BIT(2)
+/* Should be excluded in GO negotiation */
+#define WEIGHTED_PCL_EXCLUDE BIT(3)
+
+/* Preferred channel list with weight */
+struct weighted_pcl {
+ u32 freq; /* MHz */
+ u8 weight;
+ u32 flag; /* bitmap for WEIGHTED_PCL_* */
+};
+
+struct driver_sta_mlo_info {
+ bool default_map;
+ u16 req_links; /* bitmap of requested link IDs */
+ u16 valid_links; /* bitmap of accepted link IDs */
+ u8 assoc_link_id;
+ u8 ap_mld_addr[ETH_ALEN];
+ struct {
+ u8 addr[ETH_ALEN];
+ u8 bssid[ETH_ALEN];
+ unsigned int freq;
+ struct t2lm_mapping t2lmap;
+ } links[MAX_NUM_MLD_LINKS];
+};
+
+/**
+ * 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
+ * @priv: private driver interface data
+ * @params: Key parameters
+ * 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 the 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)(void *priv, struct wpa_driver_set_key_params *params);
+
+ /**
+ * 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, u16 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
+ * @params: PMKSA parameters
+ *
+ * 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. The PMKSA
+ * parameters are either a set of bssid, pmkid, and pmk; or a set of
+ * ssid, fils_cache_id, pmkid, and pmk.
+ *
+ * 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, struct wpa_pmkid_params *params);
+
+ /**
+ * remove_pmkid - Remove PMKSA cache entry to the driver
+ * @priv: private driver interface data
+ * @params: PMKSA parameters
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is called when the supplicant drops a PMKSA cache
+ * entry for any reason. The PMKSA parameters are either a set of
+ * bssid and pmkid; or a set of ssid, fils_cache_id, and pmkid.
+ *
+ * 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, struct wpa_pmkid_params *params);
+
+ /**
+ * 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 optional callback that can be used when the driver does
+ * not provide event mechanism for association events. This is called
+ * when receiving WPA/RSN 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_ifindex - Get interface index
+ * @priv: private driver interface data
+ *
+ * Returns: Interface index
+ */
+ unsigned int (*get_ifindex)(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);
+
+ /**
+ * 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
+ * @dfs: Variable for returning DFS region (HOSTAPD_DFS_REGION_*)
+ * Returns: Pointer to allocated hardware data on success or %NULL on
+ * failure. Caller is responsible for freeing this.
+ */
+ struct hostapd_hw_modes * (*get_hw_feature_data)(void *priv,
+ u16 *num_modes,
+ u16 *flags, u8 *dfs);
+
+ /**
+ * 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
+ * @noack: Do not wait for this frame to be acked (disable retries)
+ * @freq: Frequency (in MHz) to send the frame on, or 0 to let the
+ * driver decide
+ * @csa_offs: Array of CSA offsets or %NULL
+ * @csa_offs_len: Number of elements in csa_offs
+ * @no_encrypt: Do not encrypt frame even if appropriate key exists
+ * (used only for testing purposes)
+ * @wait: Time to wait off-channel for a response (in ms), or zero
+ * @link_id: Link ID to use for TX, or -1 if not set
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*send_mlme)(void *priv, const u8 *data, size_t data_len,
+ int noack, unsigned int freq, const u16 *csa_offs,
+ size_t csa_offs_len, int no_encrypt,
+ unsigned int wait, int link_id);
+
+ /**
+ * 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);
+
+ /**
+ * 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);
+
+ /**
+ * get_country - Get country
+ * @priv: Private driver interface data
+ * @alpha2: Buffer for returning country code (at least 3 octets)
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*get_country)(void *priv, char *alpha2);
+
+ /**
+ * global_init - Global driver initialization
+ * @ctx: wpa_global pointer
+ * 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 *ctx);
+
+ /**
+ * 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_ap - Set Beacon and Probe Response information for AP mode
+ * @priv: Private driver interface data
+ * @params: Parameters to use in AP mode
+ *
+ * This function is used to configure Beacon template and/or extra IEs
+ * to add for Beacon and Probe Response frames 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. Depending on the
+ * driver architectue, this can be done either by using the full
+ * template or the set of additional IEs (e.g., WPS and P2P IE).
+ * Similarly, Probe Response processing depends on the driver design.
+ * If the driver (or firmware) takes care of replying to Probe Request
+ * frames, the extra IEs provided here needs to be added to the Probe
+ * Response frames.
+ *
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*set_ap)(void *priv, struct wpa_driver_ap_params *params);
+
+ /**
+ * set_acl - Set ACL in AP mode
+ * @priv: Private driver interface data
+ * @params: Parameters to configure ACL
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is used only for the drivers which support MAC address ACL.
+ */
+ int (*set_acl)(void *priv, struct hostapd_acl_params *params);
+
+ /**
+ * 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 with 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_ap() to set WPA/RSN IE
+ * for Beacon frames.
+ *
+ * DEPRECATED - use set_ap() instead
+ */
+ 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_ap().
+ *
+ * DEPRECATED - use set_ap() instead
+ */
+ 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
+ * @link_id: Link ID for a group key, or -1 if not set
+ * @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, GCMP, 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, int link_id, 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_ap().
+ *
+ * DEPRECATED - use set_ap() instead
+ */
+ int (*set_generic_elem)(void *priv, const u8 *elem, size_t elem_len);
+
+ /**
+ * read_sta_data - Fetch station data
+ * @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);
+
+ /**
+ * tx_control_port - Send a frame over the 802.1X controlled port
+ * @priv: Private driver interface data
+ * @dest: Destination MAC address
+ * @proto: Ethertype in host byte order
+ * @buf: Frame payload starting from IEEE 802.1X header
+ * @len: Frame payload length
+ * @no_encrypt: Do not encrypt frame
+ * @link_id: Link ID to use for TX, or -1 if not set
+ *
+ * Returns 0 on success, else an error
+ *
+ * This is like a normal Ethernet send except that the driver is aware
+ * (by other means than the Ethertype) that this frame is special,
+ * and more importantly it gains an ordering between the transmission of
+ * the frame and other driver management operations such as key
+ * installations. This can be used to work around known limitations in
+ * IEEE 802.11 protocols such as race conditions between rekeying 4-way
+ * handshake message 4/4 and a PTK being overwritten.
+ *
+ * This function is only used for a given interface if the driver
+ * instance reports WPA_DRIVER_FLAGS_CONTROL_PORT capability. Otherwise,
+ * API users will fall back to sending the frame via a normal socket.
+ */
+ int (*tx_control_port)(void *priv, const u8 *dest,
+ u16 proto, const u8 *buf, size_t len,
+ int no_encrypt, int link_id);
+
+ /**
+ * 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
+ * @link_id: Link ID to use for TX, or -1 if not set
+ *
+ * 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, int link_id);
+
+ /**
+ * 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 Deauthentication frame
+ * @link_id: Link ID to use for Deauthentication frame, or -1 if not set
+ * 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,
+ u16 reason, int link_id);
+
+ /**
+ * 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,
+ u16 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_ap() 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
+ *
+ * DEPRECATED - use set_ap() instead
+ */
+ 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 or set (params->set 1) a station
+ * entry in the driver. Adding STA entries is used only if the driver
+ * does not take care of association processing.
+ *
+ * With drivers that don't support full AP client state, this function
+ * is used to add a station entry to the driver once the station has
+ * completed association.
+ *
+ * With TDLS, this function is used to add or set (params->set 1)
+ * TDLS peer entries (even with drivers that do not support full AP
+ * client state).
+ */
+ 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,
+ unsigned int total_flags, unsigned int flags_or,
+ unsigned int flags_and);
+
+ /**
+ * sta_set_airtime_weight - Set station airtime weight (AP only)
+ * @priv: Private driver interface data
+ * @addr: Station address
+ * @weight: New weight for station airtime assignment
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*sta_set_airtime_weight)(void *priv, const u8 *addr,
+ unsigned int weight);
+
+ /**
+ * 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
+ * @link_id: Link ID to use, or -1 for non MLD.
+ */
+ int (*set_tx_queue_params)(void *priv, int queue, int aifs, int cw_min,
+ int cw_max, int burst_time, int link_id);
+
+ /**
+ * 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
+ * @use_existing: Whether to allow existing interface to be used
+ * @setup_ap: Whether to setup AP for %WPA_IF_AP_BSS interfaces
+ * 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, int use_existing, int setup_ap);
+
+ /**
+ * 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
+ * @link_id: The link ID or -1 for non-MLO
+ * 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, int link_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);
+
+ /**
+ * 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_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_ap()
+ * 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.
+ *
+ * DEPRECATED - use set_ap() instead
+ */
+ 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
+ * @ifname_wds: Buffer to return the interface name for the new WDS
+ * station or %NULL to indicate name is not returned.
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*set_wds_sta)(void *priv, const u8 *addr, int aid, int val,
+ const char *bridge_ifname, char *ifname_wds);
+
+ /**
+ * 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
+ @ @no_cck: Whether CCK rates must not be used to transmit this frame
+ * 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.)
+ *
+ * If @src differs from the device MAC address, use of a random
+ * transmitter address is requested for this message exchange.
+ */
+ 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, int no_cck);
+
+ /**
+ * 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_MGMT 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);
+
+ /**
+ * 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. If the interface was not dynamically added,
+ * change the driver mode to station mode to allow normal station
+ * operations like scanning to be completed.
+ */
+ int (*deinit_ap)(void *priv);
+
+ /**
+ * deinit_p2p_cli - Deinitialize P2P client 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 P2P client mode. If the
+ * interface was not dynamically added, change the interface type back
+ * to station mode.
+ */
+ int (*deinit_p2p_cli)(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);
+
+ /**
+ * 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);
+
+ /**
+ * 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);
+
+ /**
+ * 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)
+ * @peer_capab: TDLS peer capability (TDLS_PEER_* bitfield)
+ * @initiator: Is the current end the TDLS link initiator
+ * @buf: TDLS IEs to add to the message
+ * @len: Length of buf in octets
+ * Returns: 0 on success, negative (<0) 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, u32 peer_capab,
+ int initiator, const u8 *buf, size_t len);
+
+ /**
+ * tdls_oper - Ask the driver to perform high-level TDLS operations
+ * @priv: Private driver interface data
+ * @oper: TDLS high-level operation. See %enum tdls_oper
+ * @peer: Destination (peer) MAC address
+ * Returns: 0 on success, negative (<0) on failure
+ *
+ * This optional function can be used to send high-level TDLS commands
+ * to the driver.
+ */
+ int (*tdls_oper)(void *priv, enum tdls_oper oper, const u8 *peer);
+
+ /**
+ * wnm_oper - Notify driver of the WNM frame reception
+ * @priv: Private driver interface data
+ * @oper: WNM operation. See %enum wnm_oper
+ * @peer: Destination (peer) MAC address
+ * @buf: Buffer for the driver to fill in (for getting IE)
+ * @buf_len: Return the len of buf
+ * Returns: 0 on success, negative (<0) on failure
+ */
+ int (*wnm_oper)(void *priv, enum wnm_oper oper, const u8 *peer,
+ u8 *buf, u16 *buf_len);
+
+ /**
+ * set_qos_map - Set QoS Map
+ * @priv: Private driver interface data
+ * @qos_map_set: QoS Map
+ * @qos_map_set_len: Length of QoS Map
+ */
+ int (*set_qos_map)(void *priv, const u8 *qos_map_set,
+ u8 qos_map_set_len);
+
+ /**
+ * br_add_ip_neigh - Add a neigh to the bridge ip neigh table
+ * @priv: Private driver interface data
+ * @version: IP version of the IP address, 4 or 6
+ * @ipaddr: IP address for the neigh entry
+ * @prefixlen: IP address prefix length
+ * @addr: Corresponding MAC address
+ * Returns: 0 on success, negative (<0) on failure
+ */
+ int (*br_add_ip_neigh)(void *priv, u8 version, const u8 *ipaddr,
+ int prefixlen, const u8 *addr);
+
+ /**
+ * br_delete_ip_neigh - Remove a neigh from the bridge ip neigh table
+ * @priv: Private driver interface data
+ * @version: IP version of the IP address, 4 or 6
+ * @ipaddr: IP address for the neigh entry
+ * Returns: 0 on success, negative (<0) on failure
+ */
+ int (*br_delete_ip_neigh)(void *priv, u8 version, const u8 *ipaddr);
+
+ /**
+ * br_port_set_attr - Set a bridge port attribute
+ * @attr: Bridge port attribute to set
+ * @val: Value to be set
+ * Returns: 0 on success, negative (<0) on failure
+ */
+ int (*br_port_set_attr)(void *priv, enum drv_br_port_attr attr,
+ unsigned int val);
+
+ /**
+ * br_port_set_attr - Set a bridge network parameter
+ * @param: Bridge parameter to set
+ * @val: Value to be set
+ * Returns: 0 on success, negative (<0) on failure
+ */
+ int (*br_set_net_param)(void *priv, enum drv_br_net_param param,
+ unsigned int val);
+
+ /**
+ * get_wowlan - Get wake-on-wireless status
+ * @priv: Private driver interface data
+ */
+ int (*get_wowlan)(void *priv);
+
+ /**
+ * set_wowlan - Set wake-on-wireless triggers
+ * @priv: Private driver interface data
+ * @triggers: wowlan triggers
+ */
+ int (*set_wowlan)(void *priv, const struct wowlan_triggers *triggers);
+
+ /**
+ * 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);
+
+ /**
+ * mlo_signal_poll - Get current MLO connection information
+ * @priv: Private driver interface data
+ * @mlo_signal_info: MLO connection info structure
+ */
+ int (*mlo_signal_poll)(void *priv,
+ struct wpa_mlo_signal_info *mlo_signal_info);
+
+ /**
+ * channel_info - Get parameters of the current operating channel
+ * @priv: Private driver interface data
+ * @channel_info: Channel info structure
+ * Returns: 0 on success, negative (<0) on failure
+ */
+ int (*channel_info)(void *priv, struct wpa_channel_info *channel_info);
+
+ /**
+ * set_authmode - Set authentication algorithm(s) for static WEP
+ * @priv: Private driver interface data
+ * @authmode: 1=Open System, 2=Shared Key, 3=both
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to set authentication algorithms for AP
+ * mode when static WEP is used. If the driver uses user space MLME/SME
+ * implementation, there is no need to implement this function.
+ *
+ * DEPRECATED - use set_ap() instead
+ */
+ int (*set_authmode)(void *priv, int authmode);
+
+#ifdef ANDROID
+ /**
+ * driver_cmd - Execute driver-specific command
+ * @priv: Private driver interface data
+ * @cmd: Command to execute
+ * @buf: Return buffer
+ * @buf_len: Buffer length
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*driver_cmd)(void *priv, char *cmd, char *buf, size_t buf_len);
+#endif /* ANDROID */
+
+ /**
+ * vendor_cmd - Execute vendor specific command
+ * @priv: Private driver interface data
+ * @vendor_id: Vendor id
+ * @subcmd: Vendor command id
+ * @nested_attr_flag: Specifies if vendor subcommand uses nested
+ * attributes or not
+ * @data: Vendor command parameters (%NULL if no parameters)
+ * @data_len: Data length
+ * @buf: Return buffer (%NULL to ignore reply)
+ * Returns: 0 on success, negative (<0) on failure
+ *
+ * This function handles vendor specific commands that are passed to
+ * the driver/device. The command is identified by vendor id and
+ * command id. The nested_attr_flag specifies whether the subcommand
+ * uses nested attributes or not. Parameters can be passed
+ * as argument to the command in the data buffer. Reply (if any) will be
+ * filled in the supplied return buffer.
+ *
+ * The exact driver behavior is driver interface and vendor specific. As
+ * an example, this will be converted to a vendor specific cfg80211
+ * command in case of the nl80211 driver interface.
+ */
+ int (*vendor_cmd)(void *priv, unsigned int vendor_id,
+ unsigned int subcmd, const u8 *data, size_t data_len,
+ enum nested_attr nested_attr_flag,
+ struct wpabuf *buf);
+
+ /**
+ * set_rekey_info - Set rekey information
+ * @priv: Private driver interface data
+ * @kek: Current KEK
+ * @kek_len: KEK length in octets
+ * @kck: Current KCK
+ * @kck_len: KCK length in octets
+ * @replay_ctr: Current EAPOL-Key Replay Counter
+ *
+ * This optional function can be used to provide information for the
+ * driver/firmware to process EAPOL-Key frames in Group Key Handshake
+ * while the host (including wpa_supplicant) is sleeping.
+ */
+ void (*set_rekey_info)(void *priv, const u8 *kek, size_t kek_len,
+ const u8 *kck, size_t kck_len,
+ const u8 *replay_ctr);
+
+ /**
+ * sta_assoc - Station association indication
+ * @priv: Private driver interface data
+ * @own_addr: Source address and BSSID for association frame
+ * @addr: MAC address of the station to associate
+ * @reassoc: flag to indicate re-association
+ * @status: association response status code
+ * @ie: assoc response ie buffer
+ * @len: ie buffer length
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function indicates the driver to send (Re)Association
+ * Response frame to the station.
+ */
+ int (*sta_assoc)(void *priv, const u8 *own_addr, const u8 *addr,
+ int reassoc, u16 status, const u8 *ie, size_t len);
+
+ /**
+ * sta_auth - Station authentication indication
+ * @priv: private driver interface data
+ * @params: Station authentication parameters
+ *
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*sta_auth)(void *priv,
+ struct wpa_driver_sta_auth_params *params);
+
+ /**
+ * add_tspec - Add traffic stream
+ * @priv: Private driver interface data
+ * @addr: MAC address of the station to associate
+ * @tspec_ie: tspec ie buffer
+ * @tspec_ielen: tspec ie length
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function adds the traffic steam for the station
+ * and fills the medium_time in tspec_ie.
+ */
+ int (*add_tspec)(void *priv, const u8 *addr, u8 *tspec_ie,
+ size_t tspec_ielen);
+
+ /**
+ * add_sta_node - Add a station node in the driver
+ * @priv: Private driver interface data
+ * @addr: MAC address of the station to add
+ * @auth_alg: authentication algorithm used by the station
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function adds the station node in the driver, when
+ * the station gets added by FT-over-DS.
+ */
+ int (*add_sta_node)(void *priv, const u8 *addr, u16 auth_alg);
+
+ /**
+ * sched_scan - Request the driver to initiate scheduled scan
+ * @priv: Private driver interface data
+ * @params: Scan parameters
+ * Returns: 0 on success, -1 on failure
+ *
+ * This operation should be used for scheduled scan offload to
+ * the hardware. Every time scan results are available, the
+ * driver should report scan results event for wpa_supplicant
+ * which will eventually request the results with
+ * wpa_driver_get_scan_results2(). This operation is optional
+ * and if not provided or if it returns -1, we fall back to
+ * normal host-scheduled scans.
+ */
+ int (*sched_scan)(void *priv, struct wpa_driver_scan_params *params);
+
+ /**
+ * stop_sched_scan - Request the driver to stop a scheduled scan
+ * @priv: Private driver interface data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This should cause the scheduled scan to be stopped and
+ * results should stop being sent. Must be supported if
+ * sched_scan is supported.
+ */
+ int (*stop_sched_scan)(void *priv);
+
+ /**
+ * poll_client - Probe (null data or such) the given station
+ * @priv: Private driver interface data
+ * @own_addr: MAC address of sending interface
+ * @addr: MAC address of the station to probe
+ * @qos: Indicates whether station is QoS station
+ *
+ * This function is used to verify whether an associated station is
+ * still present. This function does not need to be implemented if the
+ * driver provides such inactivity polling mechanism.
+ */
+ void (*poll_client)(void *priv, const u8 *own_addr,
+ const u8 *addr, int qos);
+
+ /**
+ * radio_disable - Disable/enable radio
+ * @priv: Private driver interface data
+ * @disabled: 1=disable 0=enable radio
+ * Returns: 0 on success, -1 on failure
+ *
+ * This optional command is for testing purposes. It can be used to
+ * disable the radio on a testbed device to simulate out-of-radio-range
+ * conditions.
+ */
+ int (*radio_disable)(void *priv, int disabled);
+
+ /**
+ * switch_channel - Announce channel switch and migrate the GO to the
+ * given frequency
+ * @priv: Private driver interface data
+ * @settings: Settings for CSA period and new channel
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to move the GO to the legacy STA channel to
+ * avoid frequency conflict in single channel concurrency.
+ */
+ int (*switch_channel)(void *priv, struct csa_settings *settings);
+
+ /**
+ * switch_color - Announce color switch and migrate the BSS to the
+ * given color
+ * @priv: Private driver interface data
+ * @settings: Settings for CCA period and new color
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to move the BSS to its new color.
+ */
+ int (*switch_color)(void *priv, struct cca_settings *settings);
+
+ /**
+ * add_tx_ts - Add traffic stream
+ * @priv: Private driver interface data
+ * @tsid: Traffic stream ID
+ * @addr: Receiver address
+ * @user_prio: User priority of the traffic stream
+ * @admitted_time: Admitted time for this TS in units of
+ * 32 microsecond periods (per second).
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*add_tx_ts)(void *priv, u8 tsid, const u8 *addr, u8 user_prio,
+ u16 admitted_time);
+
+ /**
+ * del_tx_ts - Delete traffic stream
+ * @priv: Private driver interface data
+ * @tsid: Traffic stream ID
+ * @addr: Receiver address
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*del_tx_ts)(void *priv, u8 tsid, const u8 *addr);
+
+ /**
+ * Enable channel-switching with TDLS peer
+ * @priv: Private driver interface data
+ * @addr: MAC address of the TDLS peer
+ * @oper_class: Operating class of the switch channel
+ * @params: Channel specification
+ * Returns: 0 on success, -1 on failure
+ *
+ * The function indicates to driver that it can start switching to a
+ * different channel with a specified TDLS peer. The switching is
+ * assumed on until canceled with tdls_disable_channel_switch().
+ */
+ int (*tdls_enable_channel_switch)(
+ void *priv, const u8 *addr, u8 oper_class,
+ const struct hostapd_freq_params *params);
+
+ /**
+ * Disable channel switching with TDLS peer
+ * @priv: Private driver interface data
+ * @addr: MAC address of the TDLS peer
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function indicates to the driver that it should stop switching
+ * with a given TDLS peer.
+ */
+ int (*tdls_disable_channel_switch)(void *priv, const u8 *addr);
+
+ /**
+ * start_dfs_cac - Listen for radar interference on the channel
+ * @priv: Private driver interface data
+ * @freq: Channel parameters
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*start_dfs_cac)(void *priv, struct hostapd_freq_params *freq);
+
+ /**
+ * stop_ap - Removes beacon from AP
+ * @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. Unlike deinit_ap, it does not change to station
+ * mode.
+ */
+ int (*stop_ap)(void *priv);
+
+ /**
+ * get_survey - Retrieve survey data
+ * @priv: Private driver interface data
+ * @freq: If set, survey data for the specified frequency is only
+ * being requested. If not set, all survey data is requested.
+ * Returns: 0 on success, -1 on failure
+ *
+ * Use this to retrieve:
+ *
+ * - the observed channel noise floor
+ * - the amount of time we have spent on the channel
+ * - the amount of time during which we have spent on the channel that
+ * the radio has determined the medium is busy and we cannot
+ * transmit
+ * - the amount of time we have spent receiving data
+ * - the amount of time we have spent transmitting data
+ *
+ * This data can be used for spectrum heuristics. One example is
+ * Automatic Channel Selection (ACS). The channel survey data is
+ * kept on a linked list on the channel data, one entry is added
+ * for each survey. The min_nf of the channel is updated for each
+ * survey.
+ */
+ int (*get_survey)(void *priv, unsigned int freq);
+
+ /**
+ * status - Get driver interface status information
+ * @priv: Private driver interface data
+ * @buf: Buffer for printing the status information
+ * @buflen: Maximum length of the buffer
+ * Returns: Length of written status information or -1 on failure
+ */
+ int (*status)(void *priv, char *buf, size_t buflen);
+
+ /**
+ * roaming - Set roaming policy for driver-based BSS selection
+ * @priv: Private driver interface data
+ * @allowed: Whether roaming within ESS is allowed
+ * @bssid: Forced BSSID if roaming is disabled or %NULL if not set
+ * Returns: Length of written status information or -1 on failure
+ *
+ * This optional callback can be used to update roaming policy from the
+ * associate() command (bssid being set there indicates that the driver
+ * should not roam before getting this roaming() call to allow roaming.
+ * If the driver does not indicate WPA_DRIVER_FLAGS_BSS_SELECTION
+ * capability, roaming policy is handled within wpa_supplicant and there
+ * is no need to implement or react to this callback.
+ */
+ int (*roaming)(void *priv, int allowed, const u8 *bssid);
+
+ /**
+ * disable_fils - Enable/disable FILS feature
+ * @priv: Private driver interface data
+ * @disable: 0-enable and 1-disable FILS feature
+ * Returns: 0 on success, -1 on failure
+ *
+ * This callback can be used to configure driver and below layers to
+ * enable/disable all FILS features.
+ */
+ int (*disable_fils)(void *priv, int disable);
+
+ /**
+ * set_mac_addr - Set MAC address
+ * @priv: Private driver interface data
+ * @addr: MAC address to use or %NULL for setting back to permanent
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*set_mac_addr)(void *priv, const u8 *addr);
+
+#ifdef CONFIG_MACSEC
+ int (*macsec_init)(void *priv, struct macsec_init_params *params);
+
+ int (*macsec_deinit)(void *priv);
+
+ /**
+ * macsec_get_capability - Inform MKA of this driver's capability
+ * @priv: Private driver interface data
+ * @cap: Driver's capability
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*macsec_get_capability)(void *priv, enum macsec_cap *cap);
+
+ /**
+ * enable_protect_frames - Set protect frames status
+ * @priv: Private driver interface data
+ * @enabled: true = protect frames enabled
+ * false = protect frames disabled
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+ int (*enable_protect_frames)(void *priv, bool enabled);
+
+ /**
+ * enable_encrypt - Set encryption status
+ * @priv: Private driver interface data
+ * @enabled: true = encrypt outgoing traffic
+ * false = integrity-only protection on outgoing traffic
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+ int (*enable_encrypt)(void *priv, bool enabled);
+
+ /**
+ * set_replay_protect - Set replay protect status and window size
+ * @priv: Private driver interface data
+ * @enabled: true = replay protect enabled
+ * false = replay protect disabled
+ * @window: replay window size, valid only when replay protect enabled
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+ int (*set_replay_protect)(void *priv, bool enabled, u32 window);
+
+ /**
+ * set_offload - Set MACsec hardware offload
+ * @priv: Private driver interface data
+ * @offload: 0 = MACSEC_OFFLOAD_OFF
+ * 1 = MACSEC_OFFLOAD_PHY
+ * 2 = MACSEC_OFFLOAD_MAC
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+ int (*set_offload)(void *priv, u8 offload);
+
+ /**
+ * set_current_cipher_suite - Set current cipher suite
+ * @priv: Private driver interface data
+ * @cs: EUI64 identifier
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+ int (*set_current_cipher_suite)(void *priv, u64 cs);
+
+ /**
+ * enable_controlled_port - Set controlled port status
+ * @priv: Private driver interface data
+ * @enabled: true = controlled port enabled
+ * false = controlled port disabled
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+ int (*enable_controlled_port)(void *priv, bool enabled);
+
+ /**
+ * get_receive_lowest_pn - Get receive lowest pn
+ * @priv: Private driver interface data
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+ int (*get_receive_lowest_pn)(void *priv, struct receive_sa *sa);
+
+ /**
+ * get_transmit_next_pn - Get transmit next pn
+ * @priv: Private driver interface data
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+ int (*get_transmit_next_pn)(void *priv, struct transmit_sa *sa);
+
+ /**
+ * set_transmit_next_pn - Set transmit next pn
+ * @priv: Private driver interface data
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+ int (*set_transmit_next_pn)(void *priv, struct transmit_sa *sa);
+
+ /**
+ * set_receive_lowest_pn - Set receive lowest PN
+ * @priv: Private driver interface data
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+ int (*set_receive_lowest_pn)(void *priv, struct receive_sa *sa);
+
+ /**
+ * create_receive_sc - create secure channel for receiving
+ * @priv: Private driver interface data
+ * @sc: secure channel
+ * @conf_offset: confidentiality offset (0, 30, or 50)
+ * @validation: frame validation policy (0 = Disabled, 1 = Checked,
+ * 2 = Strict)
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+ int (*create_receive_sc)(void *priv, struct receive_sc *sc,
+ unsigned int conf_offset,
+ int validation);
+
+ /**
+ * delete_receive_sc - delete secure connection for receiving
+ * @priv: private driver interface data from init()
+ * @sc: secure channel
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*delete_receive_sc)(void *priv, struct receive_sc *sc);
+
+ /**
+ * create_receive_sa - create secure association for receive
+ * @priv: private driver interface data from init()
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*create_receive_sa)(void *priv, struct receive_sa *sa);
+
+ /**
+ * delete_receive_sa - Delete secure association for receive
+ * @priv: Private driver interface data from init()
+ * @sa: Secure association
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*delete_receive_sa)(void *priv, struct receive_sa *sa);
+
+ /**
+ * enable_receive_sa - enable the SA for receive
+ * @priv: private driver interface data from init()
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*enable_receive_sa)(void *priv, struct receive_sa *sa);
+
+ /**
+ * disable_receive_sa - disable SA for receive
+ * @priv: private driver interface data from init()
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*disable_receive_sa)(void *priv, struct receive_sa *sa);
+
+ /**
+ * create_transmit_sc - create secure connection for transmit
+ * @priv: private driver interface data from init()
+ * @sc: secure channel
+ * @conf_offset: confidentiality offset (0, 30, or 50)
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*create_transmit_sc)(void *priv, struct transmit_sc *sc,
+ unsigned int conf_offset);
+
+ /**
+ * delete_transmit_sc - delete secure connection for transmit
+ * @priv: private driver interface data from init()
+ * @sc: secure channel
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*delete_transmit_sc)(void *priv, struct transmit_sc *sc);
+
+ /**
+ * create_transmit_sa - create secure association for transmit
+ * @priv: private driver interface data from init()
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*create_transmit_sa)(void *priv, struct transmit_sa *sa);
+
+ /**
+ * delete_transmit_sa - Delete secure association for transmit
+ * @priv: Private driver interface data from init()
+ * @sa: Secure association
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*delete_transmit_sa)(void *priv, struct transmit_sa *sa);
+
+ /**
+ * enable_transmit_sa - enable SA for transmit
+ * @priv: private driver interface data from init()
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*enable_transmit_sa)(void *priv, struct transmit_sa *sa);
+
+ /**
+ * disable_transmit_sa - disable SA for transmit
+ * @priv: private driver interface data from init()
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*disable_transmit_sa)(void *priv, struct transmit_sa *sa);
+#endif /* CONFIG_MACSEC */
+
+ /**
+ * init_mesh - Driver specific initialization for mesh
+ * @priv: Private driver interface data
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*init_mesh)(void *priv);
+
+ /**
+ * join_mesh - Join a mesh network
+ * @priv: Private driver interface data
+ * @params: Mesh configuration parameters
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*join_mesh)(void *priv,
+ struct wpa_driver_mesh_join_params *params);
+
+ /**
+ * leave_mesh - Leave a mesh network
+ * @priv: Private driver interface data
+ * Returns 0 on success, -1 on failure
+ */
+ int (*leave_mesh)(void *priv);
+
+ /**
+ * probe_mesh_link - Inject a frame over direct mesh link to a given
+ * peer skipping the next_hop lookup from mpath table.
+ * @priv: Private driver interface data
+ * @addr: Peer MAC address
+ * @eth: Ethernet frame to be sent
+ * @len: Ethernet frame lengtn in bytes
+ * Returns 0 on success, -1 on failure
+ */
+ int (*probe_mesh_link)(void *priv, const u8 *addr, const u8 *eth,
+ size_t len);
+
+ /**
+ * do_acs - Automatically select channel
+ * @priv: Private driver interface data
+ * @params: Parameters for ACS
+ * Returns 0 on success, -1 on failure
+ *
+ * This command can be used to offload ACS to the driver if the driver
+ * indicates support for such offloading (WPA_DRIVER_FLAGS_ACS_OFFLOAD).
+ */
+ int (*do_acs)(void *priv, struct drv_acs_params *params);
+
+ /**
+ * set_band - Notify driver of band(s) selection
+ * @priv: Private driver interface data
+ * @band_mask: The selected band(s) bit mask (from enum set_band)
+ * Returns 0 on success, -1 on failure
+ */
+ int (*set_band)(void *priv, u32 band_mask);
+
+ /**
+ * get_pref_freq_list - Get preferred frequency list for an interface
+ * @priv: Private driver interface data
+ * @if_type: Interface type
+ * @num: Number of channels
+ * @freq_list: Weighted preferred channel list
+ * Returns 0 on success, -1 on failure
+ *
+ * This command can be used to query the preferred frequency list from
+ * the driver specific to a particular interface type. The freq_list
+ * array needs to have room for *num entries. *num will be updated to
+ * indicate the number of entries fetched from the driver.
+ */
+ int (*get_pref_freq_list)(void *priv, enum wpa_driver_if_type if_type,
+ unsigned int *num,
+ struct weighted_pcl *freq_list);
+
+ /**
+ * set_prob_oper_freq - Indicate probable P2P operating channel
+ * @priv: Private driver interface data
+ * @freq: Channel frequency in MHz
+ * Returns 0 on success, -1 on failure
+ *
+ * This command can be used to inform the driver of the operating
+ * frequency that an ongoing P2P group formation is likely to come up
+ * on. Local device is assuming P2P Client role.
+ */
+ int (*set_prob_oper_freq)(void *priv, unsigned int freq);
+
+ /**
+ * abort_scan - Request the driver to abort an ongoing scan
+ * @priv: Private driver interface data
+ * @scan_cookie: Cookie identifying the scan request. This is used only
+ * when the vendor interface QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN
+ * was used to trigger scan. Otherwise, 0 is used.
+ * Returns 0 on success, -1 on failure
+ */
+ int (*abort_scan)(void *priv, u64 scan_cookie);
+
+ /**
+ * configure_data_frame_filters - Request to configure frame filters
+ * @priv: Private driver interface data
+ * @filter_flags: The type of frames to filter (bitfield of
+ * WPA_DATA_FRAME_FILTER_FLAG_*)
+ * Returns: 0 on success or -1 on failure
+ */
+ int (*configure_data_frame_filters)(void *priv, u32 filter_flags);
+
+ /**
+ * get_ext_capab - Get extended capabilities for the specified interface
+ * @priv: Private driver interface data
+ * @type: Interface type for which to get extended capabilities
+ * @ext_capab: Extended capabilities fetched
+ * @ext_capab_mask: Extended capabilities mask
+ * @ext_capab_len: Length of the extended capabilities
+ * Returns: 0 on success or -1 on failure
+ */
+ int (*get_ext_capab)(void *priv, enum wpa_driver_if_type type,
+ const u8 **ext_capab, const u8 **ext_capab_mask,
+ unsigned int *ext_capab_len);
+
+ /**
+ * get_mld_capab - Get MLD capabilities for the specified interface
+ * @priv: Private driver interface data
+ * @type: Interface type for which to get MLD capabilities
+ * @eml_capa: EML capabilities
+ * @mld_capa_and_ops: MLD Capabilities and Operations
+ * Returns: 0 on success or -1 on failure
+ */
+ int (*get_mld_capab)(void *priv, enum wpa_driver_if_type type,
+ u16 *eml_capa, u16 *mld_capa_and_ops);
+
+ /**
+ * p2p_lo_start - Start offloading P2P listen to device
+ * @priv: Private driver interface data
+ * @freq: Listening frequency (MHz) for P2P listen
+ * @period: Length of the listen operation in milliseconds
+ * @interval: Interval for running the listen operation in milliseconds
+ * @count: Number of times to run the listen operation
+ * @device_types: Device primary and secondary types
+ * @dev_types_len: Number of bytes for device_types
+ * @ies: P2P IE and WSC IE for Probe Response frames
+ * @ies_len: Length of ies in bytes
+ * Returns: 0 on success or -1 on failure
+ */
+ int (*p2p_lo_start)(void *priv, unsigned int freq,
+ unsigned int period, unsigned int interval,
+ unsigned int count,
+ const u8 *device_types, size_t dev_types_len,
+ const u8 *ies, size_t ies_len);
+
+ /**
+ * p2p_lo_stop - Stop P2P listen offload
+ * @priv: Private driver interface data
+ * Returns: 0 on success or -1 on failure
+ */
+ int (*p2p_lo_stop)(void *priv);
+
+ /**
+ * set_default_scan_ies - Set default scan IEs
+ * @priv: Private driver interface data
+ * @ies: Scan default IEs buffer
+ * @ies_len: Length of IEs in bytes
+ * Returns: 0 on success or -1 on failure
+ *
+ * The driver can use these by default when there are no scan IEs coming
+ * in the subsequent scan requests. Also in case of one or more of IEs
+ * given in set_default_scan_ies() are missing in the subsequent scan
+ * request, the driver should merge the missing scan IEs in the scan
+ * request from the IEs set by set_default_scan_ies() in the Probe
+ * Request frames sent.
+ */
+ int (*set_default_scan_ies)(void *priv, const u8 *ies, size_t ies_len);
+
+ /**
+ * set_tdls_mode - Set TDLS trigger mode to the host driver
+ * @priv: Private driver interface data
+ * @tdls_external_control: Represents if TDLS external trigger control
+ * mode is enabled/disabled.
+ *
+ * This optional callback can be used to configure the TDLS external
+ * trigger control mode to the host driver.
+ */
+ int (*set_tdls_mode)(void *priv, int tdls_external_control);
+
+ /**
+ * get_bss_transition_status - Get candidate BSS's transition status
+ * @priv: Private driver interface data
+ * @params: Candidate BSS list
+ *
+ * Get the accept or reject reason code for a list of BSS transition
+ * candidates.
+ */
+ struct wpa_bss_candidate_info *
+ (*get_bss_transition_status)(void *priv,
+ struct wpa_bss_trans_info *params);
+ /**
+ * ignore_assoc_disallow - Configure driver to ignore assoc_disallow
+ * @priv: Private driver interface data
+ * @ignore_disallow: 0 to not ignore, 1 to ignore
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*ignore_assoc_disallow)(void *priv, int ignore_disallow);
+
+ /**
+ * set_bssid_tmp_disallow - Set disallowed BSSIDs to the driver
+ * @priv: Private driver interface data
+ * @num_bssid: Number of temporarily disallowed BSSIDs
+ * @bssids: List of temporarily disallowed BSSIDs
+ */
+ int (*set_bssid_tmp_disallow)(void *priv, unsigned int num_bssid,
+ const u8 *bssid);
+
+ /**
+ * update_connect_params - Update the connection parameters
+ * @priv: Private driver interface data
+ * @params: Association parameters
+ * @mask: Bit mask indicating which parameters in @params have to be
+ * updated
+ * Returns: 0 on success, -1 on failure
+ *
+ * Update the connection parameters when in connected state so that the
+ * driver uses the updated parameters for subsequent roaming. This is
+ * used only with drivers that implement internal BSS selection and
+ * roaming.
+ */
+ int (*update_connect_params)(
+ void *priv, struct wpa_driver_associate_params *params,
+ enum wpa_drv_update_connect_params_mask mask);
+
+ /**
+ * send_external_auth_status - Indicate the status of external
+ * authentication processing to the host driver.
+ * @priv: Private driver interface data
+ * @params: Status of authentication processing.
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*send_external_auth_status)(void *priv,
+ struct external_auth *params);
+
+ /**
+ * set_4addr_mode - Set 4-address mode
+ * @priv: Private driver interface data
+ * @bridge_ifname: Bridge interface name
+ * @val: 0 - disable 4addr mode, 1 - enable 4addr mode
+ * Returns: 0 on success, < 0 on failure
+ */
+ int (*set_4addr_mode)(void *priv, const char *bridge_ifname, int val);
+
+ /**
+ * update_dh_ie - Update DH IE
+ * @priv: Private driver interface data
+ * @peer_mac: Peer MAC address
+ * @reason_code: Reacon code
+ * @ie: DH IE
+ * @ie_len: DH IE length in bytes
+ * Returns: 0 on success, -1 on failure
+ *
+ * This callback is used to let the driver know the DH processing result
+ * and DH IE for a pending association.
+ */
+ int (*update_dh_ie)(void *priv, const u8 *peer_mac, u16 reason_code,
+ const u8 *ie, size_t ie_len);
+
+ /**
+ * dpp_listen - Notify driver about start/stop of DPP listen
+ * @priv: Private driver interface data
+ * @enable: Whether listen state is enabled (or disabled)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This optional callback can be used to update RX frame filtering to
+ * explicitly allow reception of broadcast Public Action frames.
+ */
+ int (*dpp_listen)(void *priv, bool enable);
+
+ /**
+ * set_secure_ranging_ctx - Add or delete secure ranging parameters of
+ * the specified peer to the driver.
+ * @priv: Private driver interface data
+ * @params: Secure ranging parameters
+ * Returns: 0 on success, -1 on failure
+ *
+ */
+ int (*set_secure_ranging_ctx)(void *priv,
+ struct secure_ranging_params *params);
+
+ /**
+ * send_pasn_resp - Send PASN response for a set of peers to the
+ * driver.
+ * @priv: Private driver interface data
+ * @params: Parameters holding peers and respective status.
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*send_pasn_resp)(void *priv, struct pasn_auth *params);
+
+ /**
+ * get_sta_mlo_info - Get the current multi-link association info
+ * @priv: Private driver interface data
+ * @mlo: Pointer to fill multi-link association info
+ * Returns: 0 on success, -1 on failure
+ *
+ * This callback is used to fetch multi-link of the current association.
+ */
+ int (*get_sta_mlo_info)(void *priv,
+ struct driver_sta_mlo_info *mlo_info);
+
+ /**
+ * link_add - Add a link to the AP MLD interface
+ * @priv: Private driver interface data
+ * @link_id: The link ID
+ * @addr: The MAC address to use for the link
+ * Returns: 0 on success, negative value on failure
+ */
+ int (*link_add)(void *priv, u8 link_id, const u8 *addr);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ int (*register_frame)(void *priv, u16 type,
+ const u8 *match, size_t match_len,
+ bool multicast);
+#endif /* CONFIG_TESTING_OPTIONS */
+};
+
+/**
+ * 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 encryption 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_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_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_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_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.
+ */
+ 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. This event can be propagated when channel switching
+ * fails.
+ */
+ 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_IBSS_PEER_LOST - IBSS peer not reachable anymore
+ */
+ EVENT_IBSS_PEER_LOST,
+
+ /**
+ * EVENT_DRIVER_GTK_REKEY - Device/driver did GTK rekey
+ *
+ * This event carries the new replay counter to notify wpa_supplicant
+ * of the current EAPOL-Key Replay Counter in case the driver/firmware
+ * completed Group Key Handshake while the host (including
+ * wpa_supplicant was sleeping).
+ */
+ EVENT_DRIVER_GTK_REKEY,
+
+ /**
+ * EVENT_SCHED_SCAN_STOPPED - Scheduled scan was stopped
+ */
+ EVENT_SCHED_SCAN_STOPPED,
+
+ /**
+ * EVENT_DRIVER_CLIENT_POLL_OK - Station responded to poll
+ *
+ * This event indicates that the station responded to the poll
+ * initiated with @poll_client.
+ */
+ EVENT_DRIVER_CLIENT_POLL_OK,
+
+ /**
+ * EVENT_EAPOL_TX_STATUS - notify of EAPOL TX status
+ */
+ EVENT_EAPOL_TX_STATUS,
+
+ /**
+ * EVENT_CH_SWITCH - AP or GO decided to switch channels
+ *
+ * Described in wpa_event_data.ch_switch
+ * */
+ EVENT_CH_SWITCH,
+
+ /**
+ * EVENT_CH_SWITCH_STARTED - AP or GO started to switch channels
+ *
+ * This is a pre-switch event indicating the shortly following switch
+ * of operating channels.
+ *
+ * Described in wpa_event_data.ch_switch
+ */
+ EVENT_CH_SWITCH_STARTED,
+ /**
+ * EVENT_WNM - Request WNM operation
+ *
+ * This event can be used to request a WNM operation to be performed.
+ */
+ EVENT_WNM,
+
+ /**
+ * EVENT_CONNECT_FAILED_REASON - Connection failure reason in AP mode
+ *
+ * This event indicates that the driver reported a connection failure
+ * with the specified client (for example, max client reached, etc.) in
+ * AP mode.
+ */
+ EVENT_CONNECT_FAILED_REASON,
+
+ /**
+ * EVENT_DFS_RADAR_DETECTED - Notify of radar detection
+ *
+ * A radar has been detected on the supplied frequency, hostapd should
+ * react accordingly (e.g., change channel).
+ */
+ EVENT_DFS_RADAR_DETECTED,
+
+ /**
+ * EVENT_DFS_CAC_FINISHED - Notify that channel availability check has been completed
+ *
+ * After a successful CAC, the channel can be marked clear and used.
+ */
+ EVENT_DFS_CAC_FINISHED,
+
+ /**
+ * EVENT_DFS_CAC_ABORTED - Notify that channel availability check has been aborted
+ *
+ * The CAC was not successful, and the channel remains in the previous
+ * state. This may happen due to a radar being detected or other
+ * external influences.
+ */
+ EVENT_DFS_CAC_ABORTED,
+
+ /**
+ * EVENT_DFS_NOP_FINISHED - Notify that non-occupancy period is over
+ *
+ * The channel which was previously unavailable is now available again.
+ */
+ EVENT_DFS_NOP_FINISHED,
+
+ /**
+ * EVENT_SURVEY - Received survey data
+ *
+ * This event gets triggered when a driver query is issued for survey
+ * data and the requested data becomes available. The returned data is
+ * stored in struct survey_results. The results provide at most one
+ * survey entry for each frequency and at minimum will provide one
+ * survey entry for one frequency. The survey data can be os_malloc()'d
+ * and then os_free()'d, so the event callback must only copy data.
+ */
+ EVENT_SURVEY,
+
+ /**
+ * EVENT_SCAN_STARTED - Scan started
+ *
+ * This indicates that driver has started a scan operation either based
+ * on a request from wpa_supplicant/hostapd or from another application.
+ * EVENT_SCAN_RESULTS is used to indicate when the scan has been
+ * completed (either successfully or by getting cancelled).
+ */
+ EVENT_SCAN_STARTED,
+
+ /**
+ * EVENT_AVOID_FREQUENCIES - Received avoid frequency range
+ *
+ * This event indicates a set of frequency ranges that should be avoided
+ * to reduce issues due to interference or internal co-existence
+ * information in the driver.
+ */
+ EVENT_AVOID_FREQUENCIES,
+
+ /**
+ * EVENT_NEW_PEER_CANDIDATE - new (unknown) mesh peer notification
+ */
+ EVENT_NEW_PEER_CANDIDATE,
+
+ /**
+ * EVENT_ACS_CHANNEL_SELECTED - Received selected channels by ACS
+ *
+ * Indicates a pair of primary and secondary channels chosen by ACS
+ * in device.
+ */
+ EVENT_ACS_CHANNEL_SELECTED,
+
+ /**
+ * EVENT_DFS_CAC_STARTED - Notify that channel availability check has
+ * been started.
+ *
+ * This event indicates that channel availability check has been started
+ * on a DFS frequency by a driver that supports DFS Offload.
+ */
+ EVENT_DFS_CAC_STARTED,
+
+ /**
+ * EVENT_P2P_LO_STOP - Notify that P2P listen offload is stopped
+ */
+ EVENT_P2P_LO_STOP,
+
+ /**
+ * EVENT_BEACON_LOSS - Beacon loss detected
+ *
+ * This event indicates that no Beacon frames has been received from
+ * the current AP. This may indicate that the AP is not anymore in
+ * range.
+ */
+ EVENT_BEACON_LOSS,
+
+ /**
+ * EVENT_DFS_PRE_CAC_EXPIRED - Notify that channel availability check
+ * done previously (Pre-CAC) on the channel has expired. This would
+ * normally be on a non-ETSI DFS regulatory domain. DFS state of the
+ * channel will be moved from available to usable. A new CAC has to be
+ * performed before start operating on this channel.
+ */
+ EVENT_DFS_PRE_CAC_EXPIRED,
+
+ /**
+ * EVENT_EXTERNAL_AUTH - This event interface is used by host drivers
+ * that do not define separate commands for authentication and
+ * association (~WPA_DRIVER_FLAGS_SME) but offload the 802.11
+ * authentication to wpa_supplicant. This event carries all the
+ * necessary information from the host driver for the authentication to
+ * happen.
+ */
+ EVENT_EXTERNAL_AUTH,
+
+ /**
+ * EVENT_PORT_AUTHORIZED - Notification that a connection is authorized
+ *
+ * This event should be indicated when the driver completes the 4-way
+ * handshake. This event should be preceded by an EVENT_ASSOC that
+ * indicates the completion of IEEE 802.11 association.
+ */
+ EVENT_PORT_AUTHORIZED,
+
+ /**
+ * EVENT_STATION_OPMODE_CHANGED - Notify STA's HT/VHT operation mode
+ * change event.
+ */
+ EVENT_STATION_OPMODE_CHANGED,
+
+ /**
+ * EVENT_INTERFACE_MAC_CHANGED - Notify that interface MAC changed
+ *
+ * This event is emitted when the MAC changes while the interface is
+ * enabled. When an interface was disabled and becomes enabled, it
+ * must be always assumed that the MAC possibly changed.
+ */
+ EVENT_INTERFACE_MAC_CHANGED,
+
+ /**
+ * EVENT_WDS_STA_INTERFACE_STATUS - Notify WDS STA interface status
+ *
+ * This event is emitted when an interface is added/removed for WDS STA.
+ */
+ EVENT_WDS_STA_INTERFACE_STATUS,
+
+ /**
+ * EVENT_UPDATE_DH - Notification of updated DH information
+ */
+ EVENT_UPDATE_DH,
+
+ /**
+ * EVENT_UNPROT_BEACON - Unprotected Beacon frame received
+ *
+ * This event should be called when a Beacon frame is dropped due to it
+ * not being protected correctly. union wpa_event_data::unprot_beacon
+ * is required to provide more details of the frame.
+ */
+ EVENT_UNPROT_BEACON,
+
+ /**
+ * EVENT_TX_WAIT_EXPIRE - TX wait timed out
+ *
+ * This event is used to indicate when the driver has completed
+ * wait for a response frame based on a TX request that specified a
+ * non-zero wait time and that has not been explicitly cancelled.
+ */
+ EVENT_TX_WAIT_EXPIRE,
+
+ /**
+ * EVENT_BSS_COLOR_COLLISION - Notification of a BSS color collision
+ */
+ EVENT_BSS_COLOR_COLLISION,
+
+ /**
+ * EVENT_CCA_STARTED_NOTIFY - Notification that CCA has started
+ */
+ EVENT_CCA_STARTED_NOTIFY,
+
+ /**
+ * EVENT_CCA_ABORTED_NOTIFY - Notification that CCA has aborted
+ */
+ EVENT_CCA_ABORTED_NOTIFY,
+
+ /**
+ * EVENT_CCA_NOTIFY - Notification that CCA has completed
+ */
+ EVENT_CCA_NOTIFY,
+
+ /**
+ * EVENT_PASN_AUTH - This event is used by the driver that requests
+ * PASN authentication and secure ranging context for multiple peers.
+ */
+ EVENT_PASN_AUTH,
+
+ /**
+ * EVENT_LINK_CH_SWITCH - MLD AP link decided to switch channels
+ *
+ * Described in wpa_event_data.ch_switch.
+ *
+ */
+ EVENT_LINK_CH_SWITCH,
+
+ /**
+ * EVENT_LINK_CH_SWITCH_STARTED - MLD AP link started to switch channels
+ *
+ * This is a pre-switch event indicating the shortly following switch
+ * of operating channels.
+ *
+ * Described in wpa_event_data.ch_switch.
+ */
+ EVENT_LINK_CH_SWITCH_STARTED,
+
+ /**
+ * EVENT_TID_LINK_MAP - MLD event to set TID-to-link mapping
+ *
+ * This event is used by the driver to indicate the received TID-to-link
+ * mapping response from the associated AP MLD.
+ *
+ * Described in wpa_event_data.t2l_map_info.
+ */
+ EVENT_TID_LINK_MAP,
+
+ /**
+ * EVENT_LINK_RECONFIG - Notification that AP links removed
+ */
+ EVENT_LINK_RECONFIG,
+};
+
+
+/**
+ * struct freq_survey - Channel survey info
+ *
+ * @ifidx: Interface index in which this survey was observed
+ * @freq: Center of frequency of the surveyed channel
+ * @nf: Channel noise floor in dBm
+ * @channel_time: Amount of time in ms the radio spent on the channel
+ * @channel_time_busy: Amount of time in ms the radio detected some signal
+ * that indicated to the radio the channel was not clear
+ * @channel_time_rx: Amount of time the radio spent receiving data
+ * @channel_time_tx: Amount of time the radio spent transmitting data
+ * @filled: bitmask indicating which fields have been reported, see
+ * SURVEY_HAS_* defines.
+ * @list: Internal list pointers
+ */
+struct freq_survey {
+ u32 ifidx;
+ unsigned int freq;
+ s8 nf;
+ u64 channel_time;
+ u64 channel_time_busy;
+ u64 channel_time_rx;
+ u64 channel_time_tx;
+ unsigned int filled;
+ struct dl_list list;
+};
+
+#define SURVEY_HAS_NF BIT(0)
+#define SURVEY_HAS_CHAN_TIME BIT(1)
+#define SURVEY_HAS_CHAN_TIME_BUSY BIT(2)
+#define SURVEY_HAS_CHAN_TIME_RX BIT(3)
+#define SURVEY_HAS_CHAN_TIME_TX BIT(4)
+
+/**
+ * enum sta_connect_fail_reason_codes - STA connect failure reason code values
+ * @STA_CONNECT_FAIL_REASON_UNSPECIFIED: No reason code specified for
+ * connection failure.
+ * @STA_CONNECT_FAIL_REASON_NO_BSS_FOUND: No Probe Response frame received
+ * for unicast Probe Request frame.
+ * @STA_CONNECT_FAIL_REASON_AUTH_TX_FAIL: STA failed to send auth request.
+ * @STA_CONNECT_FAIL_REASON_AUTH_NO_ACK_RECEIVED: AP didn't send ACK for
+ * auth request.
+ * @STA_CONNECT_FAIL_REASON_AUTH_NO_RESP_RECEIVED: Auth response is not
+ * received from AP.
+ * @STA_CONNECT_FAIL_REASON_ASSOC_REQ_TX_FAIL: STA failed to send
+ * Association Request frame.
+ * @STA_CONNECT_FAIL_REASON_ASSOC_NO_ACK_RECEIVED: AP didn't send ACK for
+ * Association Request frame.
+ * @STA_CONNECT_FAIL_REASON_ASSOC_NO_RESP_RECEIVED: Association Response
+ * frame is not received from AP.
+ */
+enum sta_connect_fail_reason_codes {
+ STA_CONNECT_FAIL_REASON_UNSPECIFIED = 0,
+ STA_CONNECT_FAIL_REASON_NO_BSS_FOUND = 1,
+ STA_CONNECT_FAIL_REASON_AUTH_TX_FAIL = 2,
+ STA_CONNECT_FAIL_REASON_AUTH_NO_ACK_RECEIVED = 3,
+ STA_CONNECT_FAIL_REASON_AUTH_NO_RESP_RECEIVED = 4,
+ STA_CONNECT_FAIL_REASON_ASSOC_REQ_TX_FAIL = 5,
+ STA_CONNECT_FAIL_REASON_ASSOC_NO_ACK_RECEIVED = 6,
+ STA_CONNECT_FAIL_REASON_ASSOC_NO_RESP_RECEIVED = 7,
+};
+
+/**
+ * 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;
+
+ /**
+ * resp_frame - (Re)Association Response frame
+ */
+ const u8 *resp_frame;
+
+ /**
+ * resp_frame_len - (Re)Association Response frame length
+ */
+ size_t resp_frame_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;
+
+ /**
+ * wmm_params - WMM parameters used in this association.
+ */
+ struct wmm_params wmm_params;
+
+ /**
+ * addr - Station address (for AP mode)
+ */
+ const u8 *addr;
+
+ /**
+ * The following is the key management offload information
+ * @authorized
+ * @key_replay_ctr
+ * @key_replay_ctr_len
+ * @ptk_kck
+ * @ptk_kek_len
+ * @ptk_kek
+ * @ptk_kek_len
+ */
+
+ /**
+ * authorized - Status of key management offload,
+ * 1 = successful
+ */
+ int authorized;
+
+ /**
+ * key_replay_ctr - Key replay counter value last used
+ * in a valid EAPOL-Key frame
+ */
+ const u8 *key_replay_ctr;
+
+ /**
+ * key_replay_ctr_len - The length of key_replay_ctr
+ */
+ size_t key_replay_ctr_len;
+
+ /**
+ * ptk_kck - The derived PTK KCK
+ */
+ const u8 *ptk_kck;
+
+ /**
+ * ptk_kek_len - The length of ptk_kck
+ */
+ size_t ptk_kck_len;
+
+ /**
+ * ptk_kek - The derived PTK KEK
+ * This is used in key management offload and also in FILS SK
+ * offload.
+ */
+ const u8 *ptk_kek;
+
+ /**
+ * ptk_kek_len - The length of ptk_kek
+ */
+ size_t ptk_kek_len;
+
+ /**
+ * subnet_status - The subnet status:
+ * 0 = unknown, 1 = unchanged, 2 = changed
+ */
+ u8 subnet_status;
+
+ /**
+ * The following information is used in FILS SK offload
+ * @fils_erp_next_seq_num
+ * @fils_pmk
+ * @fils_pmk_len
+ * @fils_pmkid
+ */
+
+ /**
+ * fils_erp_next_seq_num - The next sequence number to use in
+ * FILS ERP messages
+ */
+ u16 fils_erp_next_seq_num;
+
+ /**
+ * fils_pmk - A new PMK if generated in case of FILS
+ * authentication
+ */
+ const u8 *fils_pmk;
+
+ /**
+ * fils_pmk_len - Length of fils_pmk
+ */
+ size_t fils_pmk_len;
+
+ /**
+ * fils_pmkid - PMKID used or generated in FILS authentication
+ */
+ const u8 *fils_pmkid;
+ } 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;
+
+ /**
+ * locally_generated - Whether the frame was locally generated
+ */
+ int locally_generated;
+ } 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;
+
+ /**
+ * locally_generated - Whether the frame was locally generated
+ */
+ int locally_generated;
+ } 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 {
+ unsigned int ifindex;
+ 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 tdls - Data for EVENT_TDLS
+ */
+ struct tdls {
+ u8 peer[ETH_ALEN];
+ enum {
+ TDLS_REQUEST_SETUP,
+ TDLS_REQUEST_TEARDOWN,
+ TDLS_REQUEST_DISCOVER,
+ } oper;
+ u16 reason_code; /* for teardown */
+ } tdls;
+
+ /**
+ * struct wnm - Data for EVENT_WNM
+ */
+ struct wnm {
+ u8 addr[ETH_ALEN];
+ enum {
+ WNM_OPER_SLEEP,
+ } oper;
+ enum {
+ WNM_SLEEP_ENTER,
+ WNM_SLEEP_EXIT
+ } sleep_action;
+ int sleep_intval;
+ u16 reason_code;
+ u8 *buf;
+ u16 buf_len;
+ } wnm;
+
+ /**
+ * 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];
+ u8 bssid[ETH_ALEN];
+ u16 auth_type;
+ u16 auth_transaction;
+ 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;
+
+ /**
+ * timed_out - Whether failure is due to timeout (etc.) rather
+ * than explicit rejection response from the AP.
+ */
+ int timed_out;
+
+ /**
+ * timeout_reason - Reason for the timeout
+ */
+ const char *timeout_reason;
+
+ /**
+ * fils_erp_next_seq_num - The next sequence number to use in
+ * FILS ERP messages
+ */
+ u16 fils_erp_next_seq_num;
+
+ /**
+ * reason_code - Connection failure reason code from the driver
+ */
+ enum sta_connect_fail_reason_codes reason_code;
+ } assoc_reject;
+
+ struct timeout_event {
+ u8 addr[ETH_ALEN];
+ } timeout_event;
+
+ /**
+ * 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;
+ int link_id;
+ } tx_status;
+
+ /**
+ * struct rx_from_unknown - Data for EVENT_RX_FROM_UNKNOWN events
+ */
+ struct rx_from_unknown {
+ const u8 *bssid;
+ const u8 *addr;
+ int wds;
+ } rx_from_unknown;
+
+ /**
+ * struct rx_mgmt - Data for EVENT_RX_MGMT events
+ */
+ struct rx_mgmt {
+ const u8 *frame;
+ size_t frame_len;
+ u32 datarate;
+
+ /**
+ * drv_priv - Pointer to store driver private BSS information
+ *
+ * If not set to NULL, this is used for comparison with
+ * hostapd_data->drv_priv to determine which BSS should process
+ * the frame.
+ */
+ void *drv_priv;
+
+ /**
+ * freq - Frequency (in MHz) on which the frame was received
+ */
+ int freq;
+
+ /**
+ * ssi_signal - Signal strength in dBm (or 0 if not available)
+ */
+ int ssi_signal;
+
+ /**
+ * link_id - MLO link on which the frame was received or -1 for
+ * non MLD.
+ */
+ int link_id;
+ } rx_mgmt;
+
+ /**
+ * 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
+ * @external_scan: Whether the scan info is for an external scan
+ * @nl_scan_event: 1 if the source of this scan event is a normal scan,
+ * 0 if the source of the scan event is a vendor scan
+ * @scan_start_tsf: Time when the scan started in terms of TSF of the
+ * BSS that the interface that requested the scan is connected to
+ * (if available).
+ * @scan_start_tsf_bssid: The BSSID according to which %scan_start_tsf
+ * is set.
+ */
+ 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;
+ int external_scan;
+ int nl_scan_event;
+ u64 scan_start_tsf;
+ u8 scan_start_tsf_bssid[ETH_ALEN];
+ } scan_info;
+
+ /**
+ * 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;
+
+ /**
+ * da - Destination address of the received Probe Request frame
+ * or %NULL if not available
+ */
+ const u8 *da;
+
+ /**
+ * bssid - BSSID of the received Probe Request frame or %NULL
+ * if not available
+ */
+ const u8 *bssid;
+
+ /**
+ * ie - IEs from the Probe Request body
+ */
+ const u8 *ie;
+
+ /**
+ * ie_len - Length of ie buffer in octets
+ */
+ size_t ie_len;
+
+ /**
+ * signal - signal strength in dBm (or 0 if not available)
+ */
+ int ssi_signal;
+ } 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;
+ enum frame_encryption encrypted;
+ int link_id;
+ } 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
+ * @num_packets: Number of packets lost (consecutive packets not
+ * acknowledged)
+ */
+ struct low_ack {
+ u8 addr[ETH_ALEN];
+ u32 num_packets;
+ } low_ack;
+
+ /**
+ * struct ibss_peer_lost - Data for EVENT_IBSS_PEER_LOST
+ */
+ struct ibss_peer_lost {
+ u8 peer[ETH_ALEN];
+ } ibss_peer_lost;
+
+ /**
+ * struct driver_gtk_rekey - Data for EVENT_DRIVER_GTK_REKEY
+ */
+ struct driver_gtk_rekey {
+ const u8 *bssid;
+ const u8 *replay_ctr;
+ } driver_gtk_rekey;
+
+ /**
+ * struct client_poll - Data for EVENT_DRIVER_CLIENT_POLL_OK events
+ * @addr: station address
+ */
+ struct client_poll {
+ u8 addr[ETH_ALEN];
+ } client_poll;
+
+ /**
+ * struct eapol_tx_status
+ * @dst: Original destination
+ * @data: Data starting with IEEE 802.1X header (!)
+ * @data_len: Length of data
+ * @ack: Indicates ack or lost frame
+ * @link_id: MLD link id used to transmit the frame or -1 for non MLO
+ *
+ * This corresponds to hapd_send_eapol if the frame sent
+ * there isn't just reported as EVENT_TX_STATUS.
+ */
+ struct eapol_tx_status {
+ const u8 *dst;
+ const u8 *data;
+ int data_len;
+ int ack;
+ int link_id;
+ } eapol_tx_status;
+
+ /**
+ * struct ch_switch
+ * @count: Count until channel switch activates
+ * @freq: Frequency of new channel in MHz
+ * @ht_enabled: Whether this is an HT channel
+ * @ch_offset: Secondary channel offset
+ * @ch_width: Channel width
+ * @cf1: Center frequency 1
+ * @cf2: Center frequency 2
+ * @link_id: Link ID of the MLO link
+ * @punct_bitmap: Puncturing bitmap
+ */
+ struct ch_switch {
+ int count;
+ int freq;
+ int ht_enabled;
+ int ch_offset;
+ enum chan_width ch_width;
+ int cf1;
+ int cf2;
+ int link_id;
+ u16 punct_bitmap;
+ } ch_switch;
+
+ /**
+ * struct connect_failed - Data for EVENT_CONNECT_FAILED_REASON
+ * @addr: Remote client address
+ * @code: Reason code for connection failure
+ */
+ struct connect_failed_reason {
+ u8 addr[ETH_ALEN];
+ enum {
+ MAX_CLIENT_REACHED,
+ BLOCKED_CLIENT
+ } code;
+ } connect_failed_reason;
+
+ /**
+ * struct dfs_event - Data for radar detected events
+ * @freq: Frequency of the channel in MHz
+ */
+ struct dfs_event {
+ int freq;
+ int ht_enabled;
+ int chan_offset;
+ enum chan_width chan_width;
+ int cf1;
+ int cf2;
+ } dfs_event;
+
+ /**
+ * survey_results - Survey result data for EVENT_SURVEY
+ * @freq_filter: Requested frequency survey filter, 0 if request
+ * was for all survey data
+ * @survey_list: Linked list of survey data (struct freq_survey)
+ */
+ struct survey_results {
+ unsigned int freq_filter;
+ struct dl_list survey_list; /* struct freq_survey */
+ } survey_results;
+
+ /**
+ * channel_list_changed - Data for EVENT_CHANNEL_LIST_CHANGED
+ * @initiator: Initiator of the regulatory change
+ * @type: Regulatory change type
+ * @alpha2: Country code (or "" if not available)
+ */
+ struct channel_list_changed {
+ enum reg_change_initiator initiator;
+ enum reg_type type;
+ char alpha2[3];
+ } channel_list_changed;
+
+ /**
+ * freq_range - List of frequency ranges
+ *
+ * This is used as the data with EVENT_AVOID_FREQUENCIES.
+ */
+ struct wpa_freq_range_list freq_range;
+
+ /**
+ * struct mesh_peer
+ *
+ * @peer: Peer address
+ * @ies: Beacon IEs
+ * @ie_len: Length of @ies
+ *
+ * Notification of new candidate mesh peer.
+ */
+ struct mesh_peer {
+ const u8 *peer;
+ const u8 *ies;
+ size_t ie_len;
+ } mesh_peer;
+
+ /**
+ * struct acs_selected_channels - Data for EVENT_ACS_CHANNEL_SELECTED
+ * @pri_freq: Selected primary frequency
+ * @sec_freq: Selected secondary frequency
+ * @edmg_channel: Selected EDMG channel
+ * @vht_seg0_center_ch: VHT mode Segment0 center channel
+ * The value is the index of the channel center frequency for
+ * 20 MHz, 40 MHz, and 80 MHz channels. The value is the center
+ * frequency index of the primary 80 MHz segment for 160 MHz and
+ * 80+80 MHz channels.
+ * @vht_seg1_center_ch: VHT mode Segment1 center channel
+ * The value is zero for 20 MHz, 40 MHz, and 80 MHz channels. The
+ * value is the index of the channel center frequency for 160 MHz
+ * channels and the center frequency index of the secondary 80 MHz
+ * segment for 80+80 MHz channels.
+ * @ch_width: Selected Channel width by driver. Driver may choose to
+ * change hostapd configured ACS channel width due driver internal
+ * channel restrictions.
+ * hw_mode: Selected band (used with hw_mode=any)
+ */
+ struct acs_selected_channels {
+ unsigned int pri_freq;
+ unsigned int sec_freq;
+ u8 edmg_channel;
+ u8 vht_seg0_center_ch;
+ u8 vht_seg1_center_ch;
+ u16 ch_width;
+ enum hostapd_hw_mode hw_mode;
+ u16 puncture_bitmap;
+ } acs_selected_channels;
+
+ /**
+ * struct p2p_lo_stop - Reason code for P2P Listen offload stop event
+ * @reason_code: Reason for stopping offload
+ * P2P_LO_STOPPED_REASON_COMPLETE: Listen offload finished as
+ * scheduled.
+ * P2P_LO_STOPPED_REASON_RECV_STOP_CMD: Host requested offload to
+ * be stopped.
+ * P2P_LO_STOPPED_REASON_INVALID_PARAM: Invalid listen offload
+ * parameters.
+ * P2P_LO_STOPPED_REASON_NOT_SUPPORTED: Listen offload not
+ * supported by device.
+ */
+ struct p2p_lo_stop {
+ enum {
+ P2P_LO_STOPPED_REASON_COMPLETE = 0,
+ P2P_LO_STOPPED_REASON_RECV_STOP_CMD,
+ P2P_LO_STOPPED_REASON_INVALID_PARAM,
+ P2P_LO_STOPPED_REASON_NOT_SUPPORTED,
+ } reason_code;
+ } p2p_lo_stop;
+
+ /* For EVENT_EXTERNAL_AUTH */
+ struct external_auth external_auth;
+
+ /**
+ * struct sta_opmode - Station's operation mode change event
+ * @addr: The station MAC address
+ * @smps_mode: SMPS mode of the station
+ * @chan_width: Channel width of the station
+ * @rx_nss: RX_NSS of the station
+ *
+ * This is used as data with EVENT_STATION_OPMODE_CHANGED.
+ */
+ struct sta_opmode {
+ const u8 *addr;
+ enum smps_mode smps_mode;
+ enum chan_width chan_width;
+ u8 rx_nss;
+ } sta_opmode;
+
+ /**
+ * struct wds_sta_interface - Data for EVENT_WDS_STA_INTERFACE_STATUS.
+ */
+ struct wds_sta_interface {
+ const u8 *sta_addr;
+ const char *ifname;
+ enum {
+ INTERFACE_ADDED,
+ INTERFACE_REMOVED
+ } istatus;
+ } wds_sta_interface;
+
+ /**
+ * struct update_dh - Data for EVENT_UPDATE_DH
+ */
+ struct update_dh {
+ const u8 *peer;
+ const u8 *ie;
+ size_t ie_len;
+ } update_dh;
+
+ /**
+ * struct unprot_beacon - Data for EVENT_UNPROT_BEACON
+ */
+ struct unprot_beacon {
+ const u8 *sa;
+ } unprot_beacon;
+
+ /**
+ * struct bss_color_collision - Data for EVENT_BSS_COLOR_COLLISION
+ */
+ struct bss_color_collision {
+ u64 bitmap;
+ } bss_color_collision;
+
+ /**
+ * struct pasn_auth - Data for EVENT_PASN_AUTH
+ */
+ struct pasn_auth pasn_auth;
+
+ /**
+ * struct port_authorized - Data for EVENT_PORT_AUTHORIZED
+ */
+ struct port_authorized {
+ const u8 *td_bitmap;
+ size_t td_bitmap_len;
+ } port_authorized;
+
+ /**
+ * struct tid_link_map_info - Data for EVENT_TID_LINK_MAP
+ */
+ struct tid_link_map_info {
+ bool default_map;
+ u8 valid_links;
+ struct t2lm_mapping t2lmap[MAX_NUM_MLD_LINKS];
+ } t2l_map_info;
+};
+
+/**
+ * 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.
+ */
+extern void (*wpa_supplicant_event)(void *ctx, enum wpa_event_type event,
+ union wpa_event_data *data);
+
+/**
+ * wpa_supplicant_event_global - 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
+ *
+ * Same as wpa_supplicant_event(), but we search for the interface in
+ * wpa_global.
+ */
+extern void (*wpa_supplicant_event_global)(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;
+ event.eapol_rx.encrypted = FRAME_ENCRYPTION_UNKNOWN;
+ event.eapol_rx.link_id = -1;
+ wpa_supplicant_event(ctx, EVENT_EAPOL_RX, &event);
+}
+
+static inline void drv_event_eapol_rx2(void *ctx, const u8 *src, const u8 *data,
+ size_t data_len,
+ enum frame_encryption encrypted,
+ int link_id)
+{
+ 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;
+ event.eapol_rx.encrypted = encrypted;
+ event.eapol_rx.link_id = link_id;
+ wpa_supplicant_event(ctx, EVENT_EAPOL_RX, &event);
+}
+
+/* driver_common.c */
+void wpa_scan_results_free(struct wpa_scan_results *res);
+
+/* Convert wpa_event_type to a string for logging */
+const char * event_to_string(enum wpa_event_type event);
+
+/* Convert chan_width to a string for logging and control interfaces */
+const char * channel_width_to_string(enum chan_width width);
+
+int channel_width_to_int(enum chan_width width);
+
+int ht_supported(const struct hostapd_hw_modes *mode);
+int vht_supported(const struct hostapd_hw_modes *mode);
+bool he_supported(const struct hostapd_hw_modes *hw_mode,
+ enum ieee80211_op_mode op_mode);
+
+struct wowlan_triggers *
+wpa_get_wowlan_triggers(const char *wowlan_triggers,
+ const struct wpa_driver_capa *capa);
+/* Convert driver flag to string */
+const char * driver_flag_to_string(u64 flag);
+const char * driver_flag2_to_string(u64 flag2);
+
+/* NULL terminated array of linked in driver wrappers */
+extern const struct wpa_driver_ops *const wpa_drivers[];
+
+
+/* Available drivers */
+
+#ifdef CONFIG_DRIVER_WEXT
+extern const struct wpa_driver_ops wpa_driver_wext_ops; /* driver_wext.c */
+#endif /* CONFIG_DRIVER_WEXT */
+#ifdef CONFIG_DRIVER_NL80211
+/* driver_nl80211.c */
+extern const struct wpa_driver_ops wpa_driver_nl80211_ops;
+#endif /* CONFIG_DRIVER_NL80211 */
+#ifdef CONFIG_DRIVER_HOSTAP
+extern const struct wpa_driver_ops wpa_driver_hostap_ops; /* driver_hostap.c */
+#endif /* CONFIG_DRIVER_HOSTAP */
+#ifdef CONFIG_DRIVER_BSD
+extern const struct wpa_driver_ops wpa_driver_bsd_ops; /* driver_bsd.c */
+#endif /* CONFIG_DRIVER_BSD */
+#ifdef CONFIG_DRIVER_OPENBSD
+/* driver_openbsd.c */
+extern const struct wpa_driver_ops wpa_driver_openbsd_ops;
+#endif /* CONFIG_DRIVER_OPENBSD */
+#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 const struct wpa_driver_ops wpa_driver_wired_ops; /* driver_wired.c */
+#endif /* CONFIG_DRIVER_WIRED */
+#ifdef CONFIG_DRIVER_MACSEC_QCA
+/* driver_macsec_qca.c */
+extern const struct wpa_driver_ops wpa_driver_macsec_qca_ops;
+#endif /* CONFIG_DRIVER_MACSEC_QCA */
+#ifdef CONFIG_DRIVER_MACSEC_LINUX
+/* driver_macsec_linux.c */
+extern const struct wpa_driver_ops wpa_driver_macsec_linux_ops;
+#endif /* CONFIG_DRIVER_MACSEC_LINUX */
+#ifdef CONFIG_DRIVER_ROBOSWITCH
+/* driver_roboswitch.c */
+extern const struct wpa_driver_ops wpa_driver_roboswitch_ops;
+#endif /* CONFIG_DRIVER_ROBOSWITCH */
+#ifdef CONFIG_DRIVER_ATHEROS
+/* driver_atheros.c */
+extern const struct wpa_driver_ops wpa_driver_atheros_ops;
+#endif /* CONFIG_DRIVER_ATHEROS */
+#ifdef CONFIG_DRIVER_NONE
+extern const struct wpa_driver_ops wpa_driver_none_ops; /* driver_none.c */
+#endif /* CONFIG_DRIVER_NONE */
+
+#endif /* DRIVER_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_atheros.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_atheros.c
new file mode 100644
index 0000000..08ff915
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_atheros.c
@@ -0,0 +1,2284 @@
+/*
+ * 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 software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <net/if.h>
+#include <sys/ioctl.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "l2_packet/l2_packet.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 "ieee80211_external.h"
+
+/* Avoid conflicting definition from the driver header files with
+ * common/wpa_common.h */
+#undef WPA_OUI_TYPE
+
+
+#ifdef CONFIG_WPS
+#include <netpacket/packet.h>
+#endif /* CONFIG_WPS */
+
+#ifndef ETH_P_80211_RAW
+#define ETH_P_80211_RAW 0x0019
+#endif
+
+#include "linux_wext.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;
+ int fils_en; /* FILS enable/disable in driver */
+ 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;
+ u8 own_addr[ETH_ALEN];
+};
+
+static int atheros_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
+ u16 reason_code, int link_id);
+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 "??";
+ }
+}
+
+
+#ifdef CONFIG_FILS
+static int
+get80211param(struct atheros_driver_data *drv, int op, int *data)
+{
+ struct iwreq iwr;
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+ iwr.u.mode = op;
+
+ if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_GETPARAM, &iwr) < 0)
+ return -1;
+
+ *data = iwr.u.mode;
+ return 0;
+}
+#endif /* CONFIG_FILS */
+
+
+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;
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+ if (do_inline) {
+ /*
+ * 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->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;
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+ iwr.u.mode = op;
+ os_memcpy(iwr.u.name + sizeof(__u32), &arg, sizeof(arg));
+
+ if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_SETPARAM, &iwr) < 0) {
+ wpa_printf(MSG_INFO,
+ "%s: %s: Failed to set parameter (op %d (%s) arg %d): ioctl[IEEE80211_IOCTL_SETPARAM]: %s",
+ __func__, drv->iface, op, athr_get_param_name(op),
+ arg, strerror(errno));
+ 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)
+ os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr));
+ else
+ os_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;
+#ifdef ATH_GCM_SUPPORT
+ case WPA_CIPHER_CCMP_256:
+ v = IEEE80211_CIPHER_AES_CCM_256;
+ break;
+ case WPA_CIPHER_GCMP:
+ v = IEEE80211_CIPHER_AES_GCM;
+ break;
+ case WPA_CIPHER_GCMP_256:
+ v = IEEE80211_CIPHER_AES_GCM_256;
+ break;
+#endif /* ATH_GCM_SUPPORT */
+ 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)) {
+ wpa_printf(MSG_INFO, "Unable to set group key cipher to %u", 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)) {
+ wpa_printf(MSG_INFO,
+ "Unable to set group key length to %u", v);
+ return -1;
+ }
+ }
+
+ v = 0;
+ if (params->wpa_pairwise & WPA_CIPHER_CCMP)
+ v |= 1<<IEEE80211_CIPHER_AES_CCM;
+#ifdef ATH_GCM_SUPPORT
+ if (params->wpa_pairwise & WPA_CIPHER_CCMP_256)
+ v |= 1<<IEEE80211_CIPHER_AES_CCM_256;
+ if (params->wpa_pairwise & WPA_CIPHER_GCMP)
+ v |= 1<<IEEE80211_CIPHER_AES_GCM;
+ if (params->wpa_pairwise & WPA_CIPHER_GCMP_256)
+ v |= 1<<IEEE80211_CIPHER_AES_GCM_256;
+#endif /* ATH_GCM_SUPPORT */
+ 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)) {
+ wpa_printf(MSG_INFO,
+ "Unable to set pairwise key ciphers to 0x%x", 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)) {
+ wpa_printf(MSG_INFO,
+ "Unable to set key management algorithms to 0x%x",
+ params->wpa_key_mgmt);
+ return -1;
+ }
+
+ v = 0;
+ if (params->rsn_preauth)
+ v |= BIT(0);
+ if (params->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+ v |= BIT(7);
+ if (params->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED)
+ v |= BIT(6);
+ }
+
+ wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x", __func__, v);
+ if (set80211param(drv, IEEE80211_PARAM_RSNCAPS, v)) {
+ wpa_printf(MSG_INFO, "Unable to set RSN capabilities to 0x%x",
+ v);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "%s: enable WPA=0x%x", __func__, params->wpa);
+ if (set80211param(drv, IEEE80211_PARAM_WPA, params->wpa)) {
+ wpa_printf(MSG_INFO, "Unable to set WPA to %u", 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) {
+ wpa_printf(MSG_WARNING, "No 802.1X or WPA enabled!");
+ return -1;
+ }
+ if (params->wpa && atheros_configure_wpa(drv, params) != 0) {
+ wpa_printf(MSG_WARNING, "Error configuring WPA state!");
+ return -1;
+ }
+ if (set80211param(priv, IEEE80211_PARAM_AUTHMODE,
+ (params->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) {
+ wpa_printf(MSG_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;
+ os_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,
+ unsigned int total_flags, unsigned int flags_or,
+ unsigned 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);
+
+ os_memset(&wk, 0, sizeof(wk));
+ if (addr != NULL) {
+ os_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(void *priv, struct wpa_driver_set_key_params *params)
+{
+ struct atheros_driver_data *drv = priv;
+ struct ieee80211req_key wk;
+ u_int8_t cipher;
+ int ret;
+ enum wpa_alg alg = params->alg;
+ const u8 *addr = params->addr;
+ int key_idx = params->key_idx;
+ int set_tx = params->set_tx;
+ const u8 *key = params->key;
+ size_t key_len = params->key_len;
+
+ 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 ATH_GCM_SUPPORT
+ case WPA_ALG_CCMP_256:
+ cipher = IEEE80211_CIPHER_AES_CCM_256;
+ break;
+ case WPA_ALG_GCMP:
+ cipher = IEEE80211_CIPHER_AES_GCM;
+ break;
+ case WPA_ALG_GCMP_256:
+ cipher = IEEE80211_CIPHER_AES_GCM_256;
+ break;
+#endif /* ATH_GCM_SUPPORT */
+ case WPA_ALG_BIP_CMAC_128:
+ cipher = IEEE80211_CIPHER_AES_CMAC;
+ break;
+#ifdef ATH_GCM_SUPPORT
+ case WPA_ALG_BIP_CMAC_256:
+ cipher = IEEE80211_CIPHER_AES_CMAC_256;
+ break;
+ case WPA_ALG_BIP_GMAC_128:
+ cipher = IEEE80211_CIPHER_AES_GMAC;
+ break;
+ case WPA_ALG_BIP_GMAC_256:
+ cipher = IEEE80211_CIPHER_AES_GMAC_256;
+ break;
+#endif /* ATH_GCM_SUPPORT */
+ default:
+ wpa_printf(MSG_INFO, "%s: unknown/unsupported algorithm %d",
+ __func__, alg);
+ return -1;
+ }
+
+ if (key_len > sizeof(wk.ik_keydata)) {
+ wpa_printf(MSG_INFO, "%s: key length %lu too big", __func__,
+ (unsigned long) key_len);
+ return -3;
+ }
+
+ os_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)) {
+ os_memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN);
+ wk.ik_keyix = key_idx;
+ if (set_tx)
+ wk.ik_flags |= IEEE80211_KEY_DEFAULT;
+ } else {
+ os_memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN);
+ wk.ik_keyix = IEEE80211_KEYIX_NONE;
+ }
+ wk.ik_keylen = key_len;
+ os_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,
+ int link_id, 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);
+
+ os_memset(&wk, 0, sizeof(wk));
+ if (addr == NULL)
+ os_memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN);
+ else
+ os_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];
+ os_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 */
+ os_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];
+ os_memset(allsta, 0xff, IEEE80211_ADDR_LEN);
+ return atheros_sta_deauth(priv, NULL, allsta,
+ IEEE80211_REASON_AUTH_LEAVE, -1);
+}
+
+
+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;
+
+ os_memset(data, 0, sizeof(*data));
+
+ /*
+ * Fetch statistics for station from the system.
+ */
+ os_memset(&stats, 0, sizeof(stats));
+ os_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 (os_memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) {
+ os_memcpy(data, &drv->acct_data, sizeof(*data));
+ return 0;
+ }
+
+ wpa_printf(MSG_INFO,
+ "Failed to get station stats information element");
+ 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;
+ os_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);
+ wpa_hexdump(MSG_DEBUG, "atheros: set_generic_elem", ie, ie_len);
+
+ wpabuf_free(drv->wpa_ie);
+ if (ie)
+ drv->wpa_ie = wpabuf_alloc_copy(ie, ie_len);
+ else
+ drv->wpa_ie = NULL;
+
+ app_ie = (struct ieee80211req_getset_appiebuf *) buf;
+ if (ie)
+ 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);
+ }
+ wpa_hexdump(MSG_DEBUG, "atheros: SET_APPIEBUF(Beacon)",
+ app_ie->app_buf, app_ie->app_buflen);
+ 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;
+ wpa_hexdump(MSG_DEBUG, "atheros: SET_APPIEBUF(ProbeResp)",
+ app_ie->app_buf, app_ie->app_buflen);
+ 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,
+ u16 reason_code, int link_id)
+{
+ 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;
+ os_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,
+ u16 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;
+ os_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;
+}
+
+static int atheros_set_qos_map(void *ctx, const u8 *qos_map_set,
+ u8 qos_map_set_len)
+{
+#ifdef CONFIG_ATHEROS_QOS_MAP
+ struct atheros_driver_data *drv = ctx;
+ struct ieee80211req_athdbg req;
+ struct ieee80211_qos_map *qos_map = &req.data.qos_map;
+ struct iwreq iwr;
+ int i, up_start;
+
+ if (qos_map_set_len < 16 || qos_map_set_len > 58 ||
+ qos_map_set_len & 1) {
+ wpa_printf(MSG_ERROR, "Invalid QoS Map");
+ return -1;
+ } else {
+ os_memset(&req, 0, sizeof(struct ieee80211req_athdbg));
+ req.cmd = IEEE80211_DBGREQ_SETQOSMAPCONF;
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->iface, sizeof(iwr.ifr_name));
+ iwr.u.data.pointer = (void *) &req;
+ iwr.u.data.length = sizeof(struct ieee80211req_athdbg);
+ }
+
+ qos_map->valid = 1;
+ qos_map->num_dscp_except = (qos_map_set_len - 16) / 2;
+ if (qos_map->num_dscp_except) {
+ for (i = 0; i < qos_map->num_dscp_except; i++) {
+ qos_map->dscp_exception[i].dscp = qos_map_set[i * 2];
+ qos_map->dscp_exception[i].up = qos_map_set[i * 2 + 1];
+ }
+ }
+
+ up_start = qos_map_set_len - 16;
+ for (i = 0; i < IEEE80211_MAX_QOS_UP_RANGE; i++) {
+ qos_map->up[i].low = qos_map_set[up_start + (i * 2)];
+ qos_map->up[i].high = qos_map_set[up_start + (i * 2) + 1];
+ }
+
+ if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_DBGREQ, &iwr) < 0) {
+ wpa_printf(MSG_ERROR,
+ "%s: %s: Failed to set QoS Map: ioctl[IEEE80211_IOCTL_DBGREQ]: %s",
+ __func__, drv->iface, strerror(errno));
+ return -1;
+ }
+#endif /* CONFIG_ATHEROS_QOS_MAP */
+
+ return 0;
+}
+
+
+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;
+ union wpa_event_data event;
+ u16 fc, stype;
+ int ielen;
+ const u8 *iebuf;
+
+ if (len < IEEE80211_HDRLEN)
+ return;
+
+ mgmt = (const struct ieee80211_mgmt *) buf;
+
+ fc = le_to_host16(mgmt->frame_control);
+
+ if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT)
+ return;
+
+ stype = WLAN_FC_GET_STYPE(fc);
+
+ wpa_printf(MSG_DEBUG, "%s: subtype 0x%x len %d", __func__, stype,
+ (int) len);
+
+ if (stype == WLAN_FC_STYPE_PROBE_REQ) {
+ if (len < IEEE80211_HDRLEN)
+ return;
+
+ os_memset(&event, 0, sizeof(event));
+ event.rx_probe_req.sa = mgmt->sa;
+ event.rx_probe_req.da = mgmt->da;
+ event.rx_probe_req.bssid = mgmt->bssid;
+ event.rx_probe_req.ie = buf + IEEE80211_HDRLEN;
+ event.rx_probe_req.ie_len = len - IEEE80211_HDRLEN;
+ wpa_supplicant_event(drv->hapd, EVENT_RX_PROBE_REQ, &event);
+ return;
+ }
+
+ if (stype == WLAN_FC_STYPE_ACTION &&
+ (os_memcmp(drv->own_addr, mgmt->bssid, ETH_ALEN) == 0 ||
+ is_broadcast_ether_addr(mgmt->bssid))) {
+ os_memset(&event, 0, sizeof(event));
+ event.rx_mgmt.frame = buf;
+ event.rx_mgmt.frame_len = len;
+ wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event);
+ return;
+ }
+
+ if (os_memcmp(drv->own_addr, mgmt->bssid, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "%s: BSSID does not match - ignore",
+ __func__);
+ return;
+ }
+
+ switch (stype) {
+ case WLAN_FC_STYPE_ASSOC_REQ:
+ if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req))
+ break;
+ ielen = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req));
+ iebuf = mgmt->u.assoc_req.variable;
+ drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, 0);
+ break;
+ case WLAN_FC_STYPE_REASSOC_REQ:
+ if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req))
+ break;
+ ielen = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req));
+ iebuf = mgmt->u.reassoc_req.variable;
+ drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, 1);
+ break;
+ case WLAN_FC_STYPE_AUTH:
+ if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth))
+ break;
+ os_memset(&event, 0, sizeof(event));
+ if (le_to_host16(mgmt->u.auth.auth_alg) == WLAN_AUTH_SAE) {
+ event.rx_mgmt.frame = buf;
+ event.rx_mgmt.frame_len = len;
+ wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event);
+ break;
+ }
+ os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN);
+ os_memcpy(event.auth.bssid, mgmt->bssid, 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);
+ event.auth.auth_transaction =
+ le_to_host16(mgmt->u.auth.auth_transaction);
+ event.auth.ies = mgmt->u.auth.variable;
+ event.auth.ies_len = len - IEEE80211_HDRLEN -
+ sizeof(mgmt->u.auth);
+ wpa_supplicant_event(drv->hapd, EVENT_AUTH, &event);
+ break;
+ default:
+ break;
+ }
+}
+
+
+static int atheros_receive_pkt(struct atheros_driver_data *drv)
+{
+ int ret = 0;
+ struct ieee80211req_set_filter filt;
+
+ wpa_printf(MSG_DEBUG, "%s Enter", __func__);
+ filt.app_filterype = 0;
+#ifdef CONFIG_WPS
+ filt.app_filterype |= IEEE80211_FILTER_TYPE_PROBE_REQ;
+#endif /* CONFIG_WPS */
+ filt.app_filterype |= (IEEE80211_FILTER_TYPE_ASSOC_REQ |
+ IEEE80211_FILTER_TYPE_AUTH |
+ IEEE80211_FILTER_TYPE_ACTION);
+#ifdef CONFIG_WNM
+ filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION;
+#endif /* CONFIG_WNM */
+#ifdef CONFIG_HS20
+ filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION;
+#endif /* CONFIG_HS20 */
+ if (filt.app_filterype) {
+ ret = set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt,
+ sizeof(struct ieee80211req_set_filter));
+ if (ret)
+ return ret;
+ }
+
+#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_FILS)
+ 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 || CONFIG_IEEE80211R || CONFIG_FILS */
+ return ret;
+}
+
+static int atheros_reset_appfilter(struct atheros_driver_data *drv)
+{
+ struct ieee80211req_set_filter filt;
+ filt.app_filterype = 0;
+ return set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt,
+ sizeof(struct ieee80211req_set_filter));
+}
+
+#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 frametype=%u", __func__,
+ (unsigned long) len, frametype);
+ wpa_hexdump(MSG_DEBUG, "atheros: IE", ie, len);
+
+ beac_ie = (struct ieee80211req_getset_appiebuf *) buf;
+ beac_ie->app_frmtype = frametype;
+ beac_ie->app_buflen = len;
+ if (ie)
+ 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)) {
+ wpa_hexdump_buf(MSG_DEBUG, "atheros: Append WPA/RSN IE",
+ drv->wpa_ie);
+ 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);
+ }
+
+ wpa_hexdump(MSG_DEBUG, "atheros: SET_APPIEBUF",
+ beac_ie->app_buf, beac_ie->app_buflen);
+ 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;
+
+ wpa_hexdump_buf(MSG_DEBUG, "atheros: set_ap_wps_ie - beacon", beacon);
+ wpa_hexdump_buf(MSG_DEBUG, "atheros: set_ap_wps_ie - proberesp",
+ proberesp);
+ wpa_hexdump_buf(MSG_DEBUG, "atheros: set_ap_wps_ie - assocresp",
+ assocresp);
+ 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 int
+atheros_sta_auth(void *priv, struct wpa_driver_sta_auth_params *params)
+{
+ struct atheros_driver_data *drv = priv;
+ struct ieee80211req_mlme mlme;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "%s: addr=%s status_code=%d",
+ __func__, ether_sprintf(params->addr), params->status);
+
+#ifdef CONFIG_FILS
+ /* Copy FILS AAD parameters if the driver supports FILS */
+ if (params->fils_auth && drv->fils_en) {
+ wpa_printf(MSG_DEBUG, "%s: im_op IEEE80211_MLME_AUTH_FILS",
+ __func__);
+ os_memcpy(mlme.fils_aad.ANonce, params->fils_anonce,
+ IEEE80211_FILS_NONCE_LEN);
+ os_memcpy(mlme.fils_aad.SNonce, params->fils_snonce,
+ IEEE80211_FILS_NONCE_LEN);
+ os_memcpy(mlme.fils_aad.kek, params->fils_kek,
+ IEEE80211_MAX_WPA_KEK_LEN);
+ mlme.fils_aad.kek_len = params->fils_kek_len;
+ mlme.im_op = IEEE80211_MLME_AUTH_FILS;
+ wpa_hexdump(MSG_DEBUG, "FILS: ANonce",
+ mlme.fils_aad.ANonce, FILS_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FILS: SNonce",
+ mlme.fils_aad.SNonce, FILS_NONCE_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "FILS: KEK",
+ mlme.fils_aad.kek, mlme.fils_aad.kek_len);
+ } else {
+ mlme.im_op = IEEE80211_MLME_AUTH;
+ }
+#else /* CONFIG_FILS */
+ mlme.im_op = IEEE80211_MLME_AUTH;
+#endif /* CONFIG_FILS */
+
+ mlme.im_reason = params->status;
+ mlme.im_seq = params->seq;
+ os_memcpy(mlme.im_macaddr, params->addr, IEEE80211_ADDR_LEN);
+ mlme.im_optie_len = params->len;
+ if (params->len) {
+ if (params->len < IEEE80211_MAX_OPT_IE) {
+ os_memcpy(mlme.im_optie, params->ie, params->len);
+ } else {
+ wpa_printf(MSG_DEBUG, "%s: Not enough space to copy "
+ "opt_ie STA (addr " MACSTR " reason %d, "
+ "ie_len %d)",
+ __func__, MAC2STR(params->addr),
+ params->status, (int) params->len);
+ return -1;
+ }
+ }
+ ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme));
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG, "%s: Failed to auth STA (addr " MACSTR
+ " reason %d)",
+ __func__, MAC2STR(params->addr), params->status);
+ }
+ return ret;
+}
+
+static int
+atheros_sta_assoc(void *priv, const u8 *own_addr, const u8 *addr,
+ int reassoc, u16 status_code, const u8 *ie, size_t len)
+{
+ struct atheros_driver_data *drv = priv;
+ struct ieee80211req_mlme mlme;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "%s: addr=%s status_code=%d reassoc %d",
+ __func__, ether_sprintf(addr), status_code, reassoc);
+
+ if (reassoc)
+ mlme.im_op = IEEE80211_MLME_REASSOC;
+ else
+ mlme.im_op = IEEE80211_MLME_ASSOC;
+ mlme.im_reason = status_code;
+ os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+ mlme.im_optie_len = len;
+ if (len) {
+ if (len < IEEE80211_MAX_OPT_IE) {
+ os_memcpy(mlme.im_optie, ie, len);
+ } else {
+ wpa_printf(MSG_DEBUG, "%s: Not enough space to copy "
+ "opt_ie STA (addr " MACSTR " reason %d, "
+ "ie_len %d)",
+ __func__, MAC2STR(addr), status_code,
+ (int) len);
+ return -1;
+ }
+ }
+ ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme));
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG, "%s: Failed to assoc STA (addr " MACSTR
+ " reason %d)",
+ __func__, MAC2STR(addr), status_code);
+ }
+ return ret;
+}
+
+
+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.
+ */
+ os_memset(&ie, 0, sizeof(ie));
+ os_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[0] == WLAN_EID_VENDOR_SPECIFIC && ie.wps_ie[1] > 0) {
+ 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 (os_memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) {
+ /* Cached accounting data is not valid anymore. */
+ os_memset(drv->acct_mac, 0, ETH_ALEN);
+ os_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)
+{
+#define MGMT_FRAM_TAG_SIZE 30 /* hardcoded in driver */
+ wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom);
+
+ if (os_strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) {
+ char *pos;
+ u8 addr[ETH_ALEN];
+ pos = os_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 = os_strchr(key, '\n')) != NULL) {
+ key++;
+ value = os_strchr(key, '=');
+ if (value == NULL)
+ continue;
+ *value++ = '\0';
+ val = strtoul(value, NULL, 10);
+ if (os_strcmp(key, "mac") == 0)
+ hwaddr_aton(value, drv->acct_mac);
+ else if (os_strcmp(key, "rx_packets") == 0)
+ drv->acct_data.rx_packets = val;
+ else if (os_strcmp(key, "tx_packets") == 0)
+ drv->acct_data.tx_packets = val;
+ else if (os_strcmp(key, "rx_bytes") == 0)
+ drv->acct_data.rx_bytes = val;
+ else if (os_strcmp(key, "tx_bytes") == 0)
+ drv->acct_data.tx_bytes = val;
+ key = value;
+ }
+#ifdef CONFIG_WPS
+ } else if (os_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 (os_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
+ */
+ int len = atoi(custom + 16);
+ if (len < 0 || MGMT_FRAM_TAG_SIZE + len > end - custom) {
+ wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req event "
+ "length %d", len);
+ return;
+ }
+ atheros_raw_receive(drv, NULL,
+ (u8 *) custom + MGMT_FRAM_TAG_SIZE, len);
+#endif /* CONFIG_WPS */
+ } else if (os_strncmp(custom, "Manage.assoc_req ", 17) == 0) {
+ /* Format: "Manage.assoc_req <frame len>" | zero padding |
+ * frame */
+ int len = atoi(custom + 17);
+ if (len < 0 || MGMT_FRAM_TAG_SIZE + len > end - custom) {
+ wpa_printf(MSG_DEBUG,
+ "Invalid Manage.assoc_req event length %d",
+ len);
+ return;
+ }
+ atheros_raw_receive(drv, NULL,
+ (u8 *) custom + MGMT_FRAM_TAG_SIZE, len);
+ } else if (os_strncmp(custom, "Manage.auth ", 12) == 0) {
+ /* Format: "Manage.auth <frame len>" | zero padding | frame */
+ int len = atoi(custom + 12);
+ if (len < 0 ||
+ MGMT_FRAM_TAG_SIZE + len > end - custom) {
+ wpa_printf(MSG_DEBUG,
+ "Invalid Manage.auth event length %d", len);
+ return;
+ }
+ atheros_raw_receive(drv, NULL,
+ (u8 *) custom + MGMT_FRAM_TAG_SIZE, len);
+ } else if (os_strncmp(custom, "Manage.action ", 14) == 0) {
+ /* Format: "Manage.assoc_req <frame len>" | zero padding | frame
+ */
+ int len = atoi(custom + 14);
+ if (len < 0 || MGMT_FRAM_TAG_SIZE + len > end - custom) {
+ wpa_printf(MSG_DEBUG,
+ "Invalid Manage.action event length %d",
+ len);
+ return;
+ }
+ atheros_raw_receive(drv, NULL,
+ (u8 *) custom + MGMT_FRAM_TAG_SIZE, len);
+ }
+}
+
+
+static void send_action_cb_event(struct atheros_driver_data *drv,
+ char *data, size_t data_len)
+{
+ union wpa_event_data event;
+ struct ieee80211_send_action_cb *sa;
+ const struct ieee80211_hdr *hdr;
+ u16 fc;
+
+ if (data_len < sizeof(*sa) + 24) {
+ wpa_printf(MSG_DEBUG,
+ "athr: Too short event message (data_len=%d sizeof(*sa)=%d)",
+ (int) data_len, (int) sizeof(*sa));
+ wpa_hexdump(MSG_DEBUG, "athr: Short event message",
+ data, data_len);
+ return;
+ }
+
+ sa = (struct ieee80211_send_action_cb *) data;
+
+ hdr = (const struct ieee80211_hdr *) (sa + 1);
+ 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 = sa->dst_addr;
+ event.tx_status.data = (const u8 *) hdr;
+ event.tx_status.data_len = data_len - sizeof(*sa);
+ event.tx_status.ack = sa->ack;
+ wpa_supplicant_event(drv->hapd, EVENT_TX_STATUS, &event);
+}
+
+
+/*
+* Handle size of data problem. WEXT only allows data of 256 bytes for custom
+* events, and p2p data can be much bigger. So the athr driver sends a small
+* event telling me to collect the big data with an ioctl.
+* On the first event, send all pending events to supplicant.
+*/
+static void fetch_pending_big_events(struct atheros_driver_data *drv)
+{
+ union wpa_event_data event;
+ const struct ieee80211_mgmt *mgmt;
+ u8 tbuf[IW_PRIV_SIZE_MASK]; /* max size is 2047 bytes */
+ u16 fc, stype;
+ struct iwreq iwr;
+ size_t data_len;
+ u32 freq, frame_type;
+
+ while (1) {
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+
+ iwr.u.data.pointer = (void *) tbuf;
+ iwr.u.data.length = sizeof(tbuf);
+ iwr.u.data.flags = IEEE80211_IOC_P2P_FETCH_FRAME;
+
+ if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_P2P_BIG_PARAM, &iwr)
+ < 0) {
+ if (errno == ENOSPC) {
+ wpa_printf(MSG_DEBUG, "%s:%d exit",
+ __func__, __LINE__);
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "athr: %s: P2P_BIG_PARAM["
+ "P2P_FETCH_FRAME] failed: %s",
+ __func__, strerror(errno));
+ return;
+ }
+ data_len = iwr.u.data.length;
+ wpa_hexdump(MSG_DEBUG, "athr: P2P_FETCH_FRAME data",
+ (u8 *) tbuf, data_len);
+ if (data_len < sizeof(freq) + sizeof(frame_type) + 24) {
+ wpa_printf(MSG_DEBUG, "athr: frame too short");
+ continue;
+ }
+ os_memcpy(&freq, tbuf, sizeof(freq));
+ os_memcpy(&frame_type, &tbuf[sizeof(freq)],
+ sizeof(frame_type));
+ mgmt = (void *) &tbuf[sizeof(freq) + sizeof(frame_type)];
+ data_len -= sizeof(freq) + sizeof(frame_type);
+
+ if (frame_type == IEEE80211_EV_RX_MGMT) {
+ fc = le_to_host16(mgmt->frame_control);
+ stype = WLAN_FC_GET_STYPE(fc);
+
+ wpa_printf(MSG_DEBUG, "athr: EV_RX_MGMT stype=%u "
+ "freq=%u len=%u", stype, freq, (int) data_len);
+
+ if (stype == WLAN_FC_STYPE_ACTION) {
+ os_memset(&event, 0, sizeof(event));
+ event.rx_mgmt.frame = (const u8 *) mgmt;
+ event.rx_mgmt.frame_len = data_len;
+ wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT,
+ &event);
+ continue;
+ }
+ } else if (frame_type == IEEE80211_EV_P2P_SEND_ACTION_CB) {
+ wpa_printf(MSG_DEBUG,
+ "%s: ACTION_CB frame_type=%u len=%zu",
+ __func__, frame_type, data_len);
+ send_action_cb_event(drv, (void *) mgmt, data_len);
+ } else {
+ wpa_printf(MSG_DEBUG, "athr: %s unknown type %d",
+ __func__, frame_type);
+ continue;
+ }
+ }
+}
+
+static void
+atheros_wireless_event_atheros_custom(struct atheros_driver_data *drv,
+ int opcode, char *buf, int len)
+{
+ switch (opcode) {
+ case IEEE80211_EV_P2P_SEND_ACTION_CB:
+ wpa_printf(MSG_DEBUG, "WEXT: EV_P2P_SEND_ACTION_CB");
+ fetch_pending_big_events(drv);
+ break;
+ case IEEE80211_EV_RX_MGMT:
+ wpa_printf(MSG_DEBUG, "WEXT: EV_RX_MGMT");
+ fetch_pending_big_events(drv);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+atheros_wireless_event_wireless(struct atheros_driver_data *drv,
+ char *data, unsigned int len)
+{
+ struct iw_event iwe_buf, *iwe = &iwe_buf;
+ char *pos, *end, *custom, *buf;
+
+ pos = data;
+ end = data + len;
+
+ while ((size_t) (end - pos) >= IW_EV_LCP_LEN) {
+ /* 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_MSGDUMP, "Wireless event: cmd=0x%x len=%d",
+ iwe->cmd, iwe->len);
+ if (iwe->len <= IW_EV_LCP_LEN || iwe->len > end - pos)
+ 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;
+ 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 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 (iwe->u.data.length > end - custom)
+ return;
+ buf = os_malloc(iwe->u.data.length + 1);
+ if (buf == NULL)
+ return; /* XXX */
+ os_memcpy(buf, custom, iwe->u.data.length);
+ buf[iwe->u.data.length] = '\0';
+
+ if (iwe->u.data.flags != 0) {
+ atheros_wireless_event_atheros_custom(
+ drv, (int) iwe->u.data.flags,
+ buf, len);
+ } else {
+ atheros_wireless_event_wireless_custom(
+ drv, buf, buf + iwe->u.data.length);
+ }
+ os_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;
+
+ os_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) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCGIWRANGE]: %s",
+ strerror(errno));
+ 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->we_version = range->we_version_compiled;
+ }
+
+ os_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, int link_id)
+{
+ 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 = os_malloc(len);
+ if (bp == NULL) {
+ wpa_printf(MSG_INFO,
+ "EAPOL frame discarded, cannot malloc temp buffer of size %lu!",
+ (unsigned long) len);
+ return -1;
+ }
+ }
+ eth = (struct l2_ethhdr *) bp;
+ os_memcpy(eth->h_dest, addr, ETH_ALEN);
+ os_memcpy(eth->h_source, own_addr, ETH_ALEN);
+ eth->h_proto = host_to_be16(ETH_P_EAPOL);
+ os_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)
+ os_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_read_fils_cap(struct atheros_driver_data *drv)
+{
+ int fils = 0;
+
+#ifdef CONFIG_FILS
+ /* TODO: Would be better to have #ifdef on the IEEE80211_PARAM_* value
+ * to automatically check this against the driver header files. */
+ if (get80211param(drv, IEEE80211_PARAM_ENABLE_FILS, &fils) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "%s: Failed to get FILS capability from driver",
+ __func__);
+ /* Assume driver does not support FILS */
+ fils = 0;
+ }
+#endif /* CONFIG_FILS */
+ drv->fils_en = fils;
+ wpa_printf(MSG_DEBUG, "atheros: fils_en=%d", drv->fils_en);
+}
+
+
+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) {
+ wpa_printf(MSG_INFO,
+ "Could not allocate memory for atheros driver data");
+ return NULL;
+ }
+
+ drv->hapd = hapd;
+ drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (drv->ioctl_sock < 0) {
+ wpa_printf(MSG_ERROR, "socket[PF_INET,SOCK_DGRAM]: %s",
+ strerror(errno));
+ goto bad;
+ }
+ os_memcpy(drv->iface, params->ifname, sizeof(drv->iface));
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, drv->iface, sizeof(ifr.ifr_name));
+ if (ioctl(drv->ioctl_sock, SIOCGIFINDEX, &ifr) != 0) {
+ wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s",
+ strerror(errno));
+ 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;
+ os_memcpy(drv->own_addr, params->own_addr, ETH_ALEN);
+ 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;
+
+ os_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) {
+ wpa_printf(MSG_ERROR,
+ "Could not set interface to master mode! ioctl[SIOCSIWMODE]: %s",
+ strerror(errno));
+ 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 */
+
+ if (atheros_receive_pkt(drv))
+ goto bad;
+
+ if (atheros_wireless_event_init(drv))
+ goto bad;
+
+ /* Read FILS capability from the driver */
+ atheros_read_fils_cap(drv);
+
+ return drv;
+bad:
+ atheros_reset_appfilter(drv);
+ if (drv->sock_raw)
+ l2_packet_deinit(drv->sock_raw);
+ 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);
+ os_free(drv);
+ return NULL;
+}
+
+
+static void
+atheros_deinit(void *priv)
+{
+ struct atheros_driver_data *drv = priv;
+
+ atheros_reset_appfilter(drv);
+
+ if (drv->wpa_ie || drv->wps_beacon_ie || drv->wps_probe_resp_ie) {
+ atheros_set_opt_ie(priv, NULL, 0);
+ wpabuf_free(drv->wpa_ie);
+ wpabuf_free(drv->wps_beacon_ie);
+ wpabuf_free(drv->wps_probe_resp_ie);
+ }
+ 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);
+ os_free(drv);
+}
+
+static int
+atheros_set_ssid(void *priv, const u8 *buf, int len)
+{
+ struct atheros_driver_data *drv = priv;
+ struct iwreq iwr;
+
+ os_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;
+
+ if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCSIWESSID,len=%d]: %s",
+ len, strerror(errno));
+ 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;
+
+ os_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 > IW_ESSID_MAX_SIZE) ?
+ IW_ESSID_MAX_SIZE : len;
+
+ if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCGIWESSID]: %s",
+ strerror(errno));
+ 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);
+}
+
+static int atheros_set_authmode(void *priv, int auth_algs)
+{
+ int authmode;
+
+ if ((auth_algs & WPA_AUTH_ALG_OPEN) &&
+ (auth_algs & WPA_AUTH_ALG_SHARED))
+ authmode = IEEE80211_AUTH_AUTO;
+ else if (auth_algs & WPA_AUTH_ALG_OPEN)
+ authmode = IEEE80211_AUTH_OPEN;
+ else if (auth_algs & WPA_AUTH_ALG_SHARED)
+ authmode = IEEE80211_AUTH_SHARED;
+ else
+ return -1;
+
+ return set80211param(priv, IEEE80211_PARAM_AUTHMODE, authmode);
+}
+
+static int atheros_set_ap(void *priv, struct wpa_driver_ap_params *params)
+{
+ /*
+ * TODO: Use this to replace set_authmode, set_privacy, set_ieee8021x,
+ * set_generic_elem, and hapd_set_ssid.
+ */
+
+ wpa_printf(MSG_DEBUG, "atheros: set_ap - pairwise_ciphers=0x%x "
+ "group_cipher=0x%x key_mgmt_suites=0x%x auth_algs=0x%x "
+ "wpa_version=0x%x privacy=%d interworking=%d",
+ params->pairwise_ciphers, params->group_cipher,
+ params->key_mgmt_suites, params->auth_algs,
+ params->wpa_version, params->privacy, params->interworking);
+ wpa_hexdump_ascii(MSG_DEBUG, "atheros: SSID",
+ params->ssid, params->ssid_len);
+ if (params->hessid)
+ wpa_printf(MSG_DEBUG, "atheros: HESSID " MACSTR,
+ MAC2STR(params->hessid));
+ wpa_hexdump_buf(MSG_DEBUG, "atheros: beacon_ies",
+ params->beacon_ies);
+ wpa_hexdump_buf(MSG_DEBUG, "atheros: proberesp_ies",
+ params->proberesp_ies);
+ wpa_hexdump_buf(MSG_DEBUG, "atheros: assocresp_ies",
+ params->assocresp_ies);
+
+#if defined(CONFIG_HS20) && (defined(IEEE80211_PARAM_OSEN) || defined(CONFIG_ATHEROS_OSEN))
+ if (params->osen) {
+ struct wpa_bss_params bss_params;
+
+ os_memset(&bss_params, 0, sizeof(struct wpa_bss_params));
+ bss_params.enabled = 1;
+ bss_params.wpa = 2;
+ bss_params.wpa_pairwise = WPA_CIPHER_CCMP;
+ bss_params.wpa_group = WPA_CIPHER_CCMP;
+ bss_params.ieee802_1x = 1;
+
+ if (atheros_set_privacy(priv, 1) ||
+ set80211param(priv, IEEE80211_PARAM_OSEN, 1))
+ return -1;
+
+ return atheros_set_ieee8021x(priv, &bss_params);
+ }
+#endif /* CONFIG_HS20 && IEEE80211_PARAM_OSEN */
+
+ return 0;
+}
+
+
+static int atheros_send_mgmt(void *priv, const u8 *frm, size_t data_len,
+ int noack, unsigned int freq,
+ const u16 *csa_offs, size_t csa_offs_len,
+ int no_encrypt, unsigned int wait, int link_id)
+{
+ struct atheros_driver_data *drv = priv;
+ u8 buf[1510];
+ const struct ieee80211_mgmt *mgmt;
+ struct ieee80211req_mgmtbuf *mgmt_frm;
+
+ mgmt = (const struct ieee80211_mgmt *) frm;
+ wpa_printf(MSG_DEBUG, "%s frmlen = %lu " MACSTR, __func__,
+ (unsigned long) data_len, MAC2STR(mgmt->da));
+ mgmt_frm = (struct ieee80211req_mgmtbuf *) buf;
+ os_memcpy(mgmt_frm->macaddr, (u8 *)mgmt->da, IEEE80211_ADDR_LEN);
+ mgmt_frm->buflen = data_len;
+ if (&mgmt_frm->buf[0] + data_len > buf + sizeof(buf)) {
+ wpa_printf(MSG_INFO, "atheros: Too long frame for "
+ "atheros_send_mgmt (%u)", (unsigned int) data_len);
+ return -1;
+ }
+ os_memcpy(&mgmt_frm->buf[0], frm, data_len);
+ return set80211priv(drv, IEEE80211_IOCTL_SEND_MGMT, mgmt_frm,
+ sizeof(struct ieee80211req_mgmtbuf) + data_len);
+}
+
+
+#ifdef CONFIG_IEEE80211R
+
+static int atheros_add_tspec(void *priv, const u8 *addr, u8 *tspec_ie,
+ size_t tspec_ielen)
+{
+ struct atheros_driver_data *drv = priv;
+ int retv;
+ struct ieee80211req_res req;
+ struct ieee80211req_res_addts *addts = &req.u.addts;
+
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+ req.type = IEEE80211_RESREQ_ADDTS;
+ os_memcpy(&req.macaddr[0], addr, IEEE80211_ADDR_LEN);
+ os_memcpy(addts->tspecie, tspec_ie, tspec_ielen);
+ retv = set80211priv(drv, IEEE80211_IOCTL_RES_REQ, &req,
+ sizeof(struct ieee80211req_res));
+ if (retv < 0) {
+ wpa_printf(MSG_DEBUG, "%s IEEE80211_IOCTL_RES_REQ FAILED "
+ "retv = %d", __func__, retv);
+ return -1;
+ }
+ os_memcpy(tspec_ie, addts->tspecie, tspec_ielen);
+ return addts->status;
+}
+
+
+static int atheros_add_sta_node(void *priv, const u8 *addr, u16 auth_alg)
+{
+ struct atheros_driver_data *drv = priv;
+ struct ieee80211req_res req;
+ struct ieee80211req_res_addnode *addnode = &req.u.addnode;
+
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+ req.type = IEEE80211_RESREQ_ADDNODE;
+ os_memcpy(&req.macaddr[0], addr, IEEE80211_ADDR_LEN);
+ addnode->auth_alg = auth_alg;
+ return set80211priv(drv, IEEE80211_IOCTL_RES_REQ, &req,
+ sizeof(struct ieee80211req_res));
+}
+
+#endif /* CONFIG_IEEE80211R */
+
+
+/* Use only to set a big param, get will not work. */
+static int
+set80211big(struct atheros_driver_data *drv, int op, const void *data, int len)
+{
+ struct iwreq iwr;
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+
+ iwr.u.data.pointer = (void *) data;
+ iwr.u.data.length = len;
+ iwr.u.data.flags = op;
+ wpa_printf(MSG_DEBUG, "%s: op=0x%x=%d (%s) len=0x%x",
+ __func__, op, op, athr_get_param_name(op), len);
+
+ if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_P2P_BIG_PARAM, &iwr) < 0) {
+ wpa_printf(MSG_DEBUG, "%s: op=0x%x (%s) subop=0x%x=%d "
+ "value=0x%x,0x%x failed: %d (%s)",
+ __func__, op, athr_get_ioctl_name(op), iwr.u.mode,
+ iwr.u.mode, iwr.u.data.length,
+ iwr.u.data.flags, errno, strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+
+static int atheros_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, int no_cck)
+{
+ struct atheros_driver_data *drv = priv;
+ struct ieee80211_p2p_send_action *act;
+ int res;
+
+ act = os_zalloc(sizeof(*act) + data_len);
+ if (act == NULL)
+ return -1;
+ act->freq = freq;
+ os_memcpy(act->dst_addr, dst, ETH_ALEN);
+ os_memcpy(act->src_addr, src, ETH_ALEN);
+ os_memcpy(act->bssid, bssid, ETH_ALEN);
+ os_memcpy(act + 1, data, data_len);
+ wpa_printf(MSG_DEBUG, "%s: freq=%d, wait=%u, dst=" MACSTR ", src="
+ MACSTR ", bssid=" MACSTR,
+ __func__, act->freq, wait, MAC2STR(act->dst_addr),
+ MAC2STR(act->src_addr), MAC2STR(act->bssid));
+ wpa_hexdump(MSG_MSGDUMP, "athr: act", (u8 *) act, sizeof(*act));
+ wpa_hexdump(MSG_MSGDUMP, "athr: data", data, data_len);
+
+ res = set80211big(drv, IEEE80211_IOC_P2P_SEND_ACTION,
+ act, sizeof(*act) + data_len);
+ os_free(act);
+ return res;
+}
+
+
+#if defined(CONFIG_WNM) && defined(IEEE80211_APPIE_FRAME_WNM)
+static int athr_wnm_tfs(struct atheros_driver_data *drv, const u8* peer,
+ u8 *ie, u16 *len, enum wnm_oper oper)
+{
+#define IEEE80211_APPIE_MAX 1024 /* max appie buffer size */
+ u8 buf[IEEE80211_APPIE_MAX];
+ struct ieee80211req_getset_appiebuf *tfs_ie;
+ u16 val;
+
+ wpa_printf(MSG_DEBUG, "atheros: ifname=%s, WNM TFS IE oper=%d " MACSTR,
+ drv->iface, oper, MAC2STR(peer));
+
+ switch (oper) {
+ case WNM_SLEEP_TFS_REQ_IE_SET:
+ if (*len > IEEE80211_APPIE_MAX -
+ sizeof(struct ieee80211req_getset_appiebuf)) {
+ wpa_printf(MSG_DEBUG, "TFS Req IE(s) too large");
+ return -1;
+ }
+ tfs_ie = (struct ieee80211req_getset_appiebuf *) buf;
+ tfs_ie->app_frmtype = IEEE80211_APPIE_FRAME_WNM;
+ tfs_ie->app_buflen = ETH_ALEN + 2 + 2 + *len;
+
+ /* Command header for driver */
+ os_memcpy(&(tfs_ie->app_buf[0]), peer, ETH_ALEN);
+ val = oper;
+ os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN, &val, 2);
+ val = *len;
+ os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2, &val, 2);
+
+ /* copy the ie */
+ os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2 + 2, ie, *len);
+
+ if (set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, tfs_ie,
+ IEEE80211_APPIE_MAX)) {
+ wpa_printf(MSG_DEBUG, "%s: Failed to set WNM TFS IE: "
+ "%s", __func__, strerror(errno));
+ return -1;
+ }
+ break;
+ case WNM_SLEEP_TFS_RESP_IE_ADD:
+ tfs_ie = (struct ieee80211req_getset_appiebuf *) buf;
+ tfs_ie->app_frmtype = IEEE80211_APPIE_FRAME_WNM;
+ tfs_ie->app_buflen = IEEE80211_APPIE_MAX -
+ sizeof(struct ieee80211req_getset_appiebuf);
+ /* Command header for driver */
+ os_memcpy(&(tfs_ie->app_buf[0]), peer, ETH_ALEN);
+ val = oper;
+ os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN, &val, 2);
+ val = 0;
+ os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2, &val, 2);
+
+ if (set80211priv(drv, IEEE80211_IOCTL_GET_APPIEBUF, tfs_ie,
+ IEEE80211_APPIE_MAX)) {
+ wpa_printf(MSG_DEBUG, "%s: Failed to get WNM TFS IE: "
+ "%s", __func__, strerror(errno));
+ return -1;
+ }
+
+ *len = tfs_ie->app_buflen;
+ os_memcpy(ie, &(tfs_ie->app_buf[0]), *len);
+ wpa_printf(MSG_DEBUG, "atheros: %c len=%d", tfs_ie->app_buf[0],
+ *len);
+ break;
+ case WNM_SLEEP_TFS_RESP_IE_NONE:
+ *len = 0;
+ break;
+ case WNM_SLEEP_TFS_IE_DEL:
+ tfs_ie = (struct ieee80211req_getset_appiebuf *) buf;
+ tfs_ie->app_frmtype = IEEE80211_APPIE_FRAME_WNM;
+ tfs_ie->app_buflen = IEEE80211_APPIE_MAX -
+ sizeof(struct ieee80211req_getset_appiebuf);
+ /* Command header for driver */
+ os_memcpy(&(tfs_ie->app_buf[0]), peer, ETH_ALEN);
+ val = oper;
+ os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN, &val, 2);
+ val = 0;
+ os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2, &val, 2);
+
+ if (set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, tfs_ie,
+ IEEE80211_APPIE_MAX)) {
+ wpa_printf(MSG_DEBUG, "%s: Failed to set WNM TFS IE: "
+ "%s", __func__, strerror(errno));
+ return -1;
+ }
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "Unsupported TFS oper %d", oper);
+ break;
+ }
+
+ return 0;
+}
+
+
+static int atheros_wnm_sleep(struct atheros_driver_data *drv,
+ const u8 *peer, enum wnm_oper oper)
+{
+ u8 *data, *pos;
+ size_t dlen;
+ int ret;
+ u16 val;
+
+ wpa_printf(MSG_DEBUG, "atheros: WNM-Sleep Oper %d, " MACSTR,
+ oper, MAC2STR(peer));
+
+ dlen = ETH_ALEN + 2 + 2;
+ data = os_malloc(dlen);
+ if (data == NULL)
+ return -1;
+
+ /* Command header for driver */
+ pos = data;
+ os_memcpy(pos, peer, ETH_ALEN);
+ pos += ETH_ALEN;
+
+ val = oper;
+ os_memcpy(pos, &val, 2);
+ pos += 2;
+
+ val = 0;
+ os_memcpy(pos, &val, 2);
+
+ ret = atheros_set_wps_ie(drv, data, dlen, IEEE80211_APPIE_FRAME_WNM);
+
+ os_free(data);
+
+ return ret;
+}
+
+
+static int atheros_wnm_oper(void *priv, enum wnm_oper oper, const u8 *peer,
+ u8 *buf, u16 *buf_len)
+{
+ struct atheros_driver_data *drv = priv;
+
+ switch (oper) {
+ case WNM_SLEEP_ENTER_CONFIRM:
+ case WNM_SLEEP_ENTER_FAIL:
+ case WNM_SLEEP_EXIT_CONFIRM:
+ case WNM_SLEEP_EXIT_FAIL:
+ return atheros_wnm_sleep(drv, peer, oper);
+ case WNM_SLEEP_TFS_REQ_IE_SET:
+ case WNM_SLEEP_TFS_RESP_IE_ADD:
+ case WNM_SLEEP_TFS_RESP_IE_NONE:
+ case WNM_SLEEP_TFS_IE_DEL:
+ return athr_wnm_tfs(drv, peer, buf, buf_len, oper);
+ default:
+ wpa_printf(MSG_DEBUG, "atheros: Unsupported WNM operation %d",
+ oper);
+ return -1;
+ }
+}
+#endif /* CONFIG_WNM && IEEE80211_APPIE_FRAME_WNM */
+
+
+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,
+ .set_authmode = atheros_set_authmode,
+ .set_ap = atheros_set_ap,
+ .sta_assoc = atheros_sta_assoc,
+ .sta_auth = atheros_sta_auth,
+ .send_mlme = atheros_send_mgmt,
+#ifdef CONFIG_IEEE80211R
+ .add_tspec = atheros_add_tspec,
+ .add_sta_node = atheros_add_sta_node,
+#endif /* CONFIG_IEEE80211R */
+ .send_action = atheros_send_action,
+#if defined(CONFIG_WNM) && defined(IEEE80211_APPIE_FRAME_WNM)
+ .wnm_oper = atheros_wnm_oper,
+#endif /* CONFIG_WNM && IEEE80211_APPIE_FRAME_WNM */
+ .set_qos_map = atheros_set_qos_map,
+};
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_bsd.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_bsd.c
new file mode 100644
index 0000000..c32d05e
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_bsd.c
@@ -0,0 +1,1769 @@
+/*
+ * WPA Supplicant - driver interaction with BSD net80211 layer
+ * Copyright (c) 2004, Sam Leffler <sam@errno.com>
+ * Copyright (c) 2004, 2Wire, Inc
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+
+#include "common.h"
+#include "driver.h"
+#include "eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_common.h"
+
+#include <ifaddrs.h>
+#include <net/if.h>
+#include <net/if_dl.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_global {
+ void *ctx;
+ int sock; /* socket for 802.11 ioctls */
+ int route; /* routing socket for events */
+ struct dl_list ifaces; /* list of interfaces */
+};
+
+struct bsd_driver_data {
+ struct dl_list list;
+ struct bsd_driver_global *global;
+ void *ctx;
+
+ struct l2_packet_data *sock_xmit;/* raw packet xmit socket */
+ char ifname[IFNAMSIZ+1]; /* interface name */
+ int flags;
+ unsigned int ifindex; /* interface index */
+ int if_removed; /* has the interface been removed? */
+ 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 */
+ enum ieee80211_opmode opmode; /* operation mode */
+};
+
+/* Generic functions for hostapd and wpa_supplicant */
+
+static struct bsd_driver_data *
+bsd_get_drvindex(void *priv, unsigned int ifindex)
+{
+ struct bsd_driver_global *global = priv;
+ struct bsd_driver_data *drv;
+
+ dl_list_for_each(drv, &global->ifaces, struct bsd_driver_data, list) {
+ if (drv->ifindex == ifindex)
+ return drv;
+ }
+ return NULL;
+}
+
+static struct bsd_driver_data *
+bsd_get_drvname(void *priv, const char *ifname)
+{
+ struct bsd_driver_global *global = priv;
+ struct bsd_driver_data *drv;
+
+ dl_list_for_each(drv, &global->ifaces, struct bsd_driver_data, list) {
+ if (os_strcmp(drv->ifname, ifname) == 0)
+ return drv;
+ }
+ return NULL;
+}
+
+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;
+
+ if (drv->ifindex == 0 || drv->if_removed)
+ return -1;
+
+ 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->global->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->global->sock, SIOCG80211, ireq) < 0) {
+ int level = drv->if_removed ? MSG_DEBUG : MSG_ERROR;
+
+ wpa_printf(level, "ioctl[SIOCG80211, 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->global->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->global->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->global->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->global->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_get_iface_flags(struct bsd_driver_data *drv)
+{
+ struct ifreq ifr;
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
+
+ if (ioctl(drv->global->sock, SIOCGIFFLAGS, &ifr) < 0) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCGIFFLAGS]: %s",
+ strerror(errno));
+ return -1;
+ }
+ drv->flags = ifr.ifr_flags;
+ return 0;
+}
+
+static int
+bsd_set_key(void *priv, struct wpa_driver_set_key_params *params)
+{
+ struct ieee80211req_key wk;
+#ifdef IEEE80211_KEY_NOREPLAY
+ struct bsd_driver_data *drv = priv;
+#endif /* IEEE80211_KEY_NOREPLAY */
+ enum wpa_alg alg = params->alg;
+ const u8 *addr = params->addr;
+ int key_idx = params->key_idx;
+ int set_tx = params->set_tx;
+ const u8 *seq = params->seq;
+ size_t seq_len = params->seq_len;
+ const u8 *key = params->key;
+ size_t key_len = params->key_len;
+
+ 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;
+#ifndef HOSTAPD
+#ifdef IEEE80211_KEY_NOREPLAY
+ /*
+ * Ignore replay failures in IBSS and AHDEMO mode.
+ */
+ if (drv->opmode == IEEE80211_M_IBSS ||
+ drv->opmode == IEEE80211_M_AHDEMO)
+ wk.ik_flags |= IEEE80211_KEY_NOREPLAY;
+#endif /* IEEE80211_KEY_NOREPLAY */
+#endif /* HOSTAPD */
+ wk.ik_keylen = key_len;
+ if (seq) {
+#ifdef WORDS_BIGENDIAN
+ /*
+ * wk.ik_keyrsc is in host byte order (big endian), need to
+ * swap it to match with the byte order used in WPA.
+ */
+ int i;
+ u8 *keyrsc = (u8 *) &wk.ik_keyrsc;
+ for (i = 0; i < seq_len; i++)
+ keyrsc[WPA_KEY_RSC_LEN - i - 1] = seq[i];
+#else /* WORDS_BIGENDIAN */
+ os_memcpy(&wk.ik_keyrsc, seq, seq_len);
+#endif /* WORDS_BIGENDIAN */
+ }
+ 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:
+ wpa_printf(MSG_INFO, "Unknown group key cipher %u",
+ 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)) {
+ wpa_printf(MSG_INFO,
+ "Unable to set group key cipher to %u (%s)",
+ 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)) {
+ wpa_printf(MSG_INFO,
+ "Unable to set group key length to %u", 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)) {
+ wpa_printf(MSG_INFO,
+ "Unable to set pairwise key ciphers to 0x%x", 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)) {
+ wpa_printf(MSG_INFO,
+ "Unable to set key management algorithms to 0x%x",
+ 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)) {
+ wpa_printf(MSG_INFO, "Unable to set RSN capabilities to 0x%x",
+ 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)) {
+ wpa_printf(MSG_INFO, "Unable to set WPA to %u", 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 0;
+}
+
+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) {
+ wpa_printf(MSG_INFO,
+ "Failed to get WPA/RSN information element");
+ 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, int link_id)
+{
+ 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 =
+ freq->ht_enabled ? IFM_IEEE80211_11NG :
+ IFM_IEEE80211_11G;
+ } else if (channel == 14) {
+ mode = IFM_IEEE80211_11B;
+ } else {
+ mode =
+ freq->ht_enabled ? IFM_IEEE80211_11NA :
+ 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->global->sock, SIOCS80211CHANNEL, &creq);
+#else /* SIOCS80211CHANNEL */
+ return set80211param(priv, IEEE80211_IOC_CHANNEL, channel);
+#endif /* SIOCS80211CHANNEL */
+}
+
+static int
+bsd_set_opt_ie(void *priv, const u8 *ie, size_t ie_len)
+{
+#ifdef IEEE80211_IOC_APPIE
+ wpa_printf(MSG_DEBUG, "%s: set WPA+RSN ie (len %lu)", __func__,
+ (unsigned long)ie_len);
+ return bsd_set80211(priv, IEEE80211_IOC_APPIE, IEEE80211_APPIE_WPA,
+ ie, ie_len);
+#endif /* IEEE80211_IOC_APPIE */
+ return 0;
+}
+
+#ifdef SO_RERROR
+static void
+bsd_route_overflow(int sock, void *ctx, struct bsd_driver_global *global)
+{
+ char event_buf[2048]; /* max size of a single route(4) msg */
+ int n;
+ struct ifaddrs *ifaddrs, *ifa;
+ struct bsd_driver_data *drv;
+ struct sockaddr_dl *sdl;
+ union wpa_event_data event;
+
+ /* We need to match the system state, so drain the route
+ * socket to avoid stale messages. */
+ do {
+ n = read(sock, event_buf, sizeof(event_buf));
+ } while (n != -1 || errno == ENOBUFS);
+
+ if (getifaddrs(&ifaddrs) == -1) {
+ wpa_printf(MSG_ERROR, "%s getifaddrs() failed: %s",
+ __func__, strerror(errno));
+ return;
+ }
+
+ /* add or update existing interfaces */
+ for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
+ if (ifa->ifa_addr == NULL ||
+ ifa->ifa_addr->sa_family != AF_LINK)
+ continue;
+ sdl = (struct sockaddr_dl *) (void *) ifa->ifa_addr;
+ drv = bsd_get_drvname(global, ifa->ifa_name);
+ if (drv != NULL &&
+ (drv->ifindex != sdl->sdl_index || drv->if_removed)) {
+ wpa_printf(MSG_DEBUG,
+ "RTM_IFANNOUNCE: Interface '%s' added",
+ drv->ifname);
+ drv->ifindex = sdl->sdl_index;
+ drv->if_removed = 0;
+ event.interface_status.ievent = EVENT_INTERFACE_ADDED;
+ os_strlcpy(event.interface_status.ifname, ifa->ifa_name,
+ sizeof(event.interface_status.ifname));
+ wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS,
+ &event);
+ }
+ if (!drv &&
+ (drv = bsd_get_drvindex(global, sdl->sdl_index)) != NULL) {
+ /* Driver name is invalid */
+ wpa_printf(MSG_DEBUG,
+ "RTM_IFANNOUNCE: Interface '%s' removed",
+ drv->ifname);
+ drv->if_removed = 1;
+ event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
+ os_strlcpy(event.interface_status.ifname, drv->ifname,
+ sizeof(event.interface_status.ifname));
+ wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS,
+ &event);
+ }
+ }
+
+ /* punt missing interfaces and update flags */
+ dl_list_for_each(drv, &global->ifaces, struct bsd_driver_data, list) {
+ for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
+ if (ifa->ifa_addr == NULL ||
+ ifa->ifa_addr->sa_family != AF_LINK)
+ continue;
+ sdl = (struct sockaddr_dl *) (void *) ifa->ifa_addr;
+ if (os_strcmp(drv->ifname, ifa->ifa_name) == 0)
+ break;
+ }
+ if (ifa == NULL && !drv->if_removed) {
+ wpa_printf(MSG_DEBUG,
+ "RTM_IFANNOUNCE: Interface '%s' removed",
+ drv->ifname);
+ drv->if_removed = 1;
+ event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
+ os_strlcpy(event.interface_status.ifname, drv->ifname,
+ sizeof(event.interface_status.ifname));
+ wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS,
+ &event);
+ }
+ if (!ifa)
+ continue;
+
+ if ((ifa->ifa_flags & IFF_UP) == 0 &&
+ (drv->flags & IFF_UP) != 0) {
+ wpa_printf(MSG_DEBUG, "RTM_IFINFO: Interface '%s' DOWN",
+ drv->ifname);
+ wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED,
+ NULL);
+ } else if ((ifa->ifa_flags & IFF_UP) != 0 &&
+ (drv->flags & IFF_UP) == 0) {
+ wpa_printf(MSG_DEBUG, "RTM_IFINFO: Interface '%s' UP",
+ drv->ifname);
+ wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED,
+ NULL);
+ }
+ drv->flags = ifa->ifa_flags;
+ }
+
+ freeifaddrs(ifaddrs);
+}
+#endif /* SO_RERROR */
+
+static void
+bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx)
+{
+ char event_buf[2048]; /* max size of a single route(4) msg */
+ struct bsd_driver_global *global = sock_ctx;
+ struct bsd_driver_data *drv;
+ struct if_announcemsghdr *ifan;
+ struct if_msghdr *ifm;
+ struct rt_msghdr *rtm;
+ union wpa_event_data event;
+ struct ieee80211_michael_event *mic;
+ struct ieee80211_leave_event *leave;
+ struct ieee80211_join_event *join;
+ int n;
+
+ n = read(sock, event_buf, sizeof(event_buf));
+ if (n < 0) {
+ if (errno != EINTR && errno != EAGAIN)
+ wpa_printf(MSG_ERROR, "%s read() failed: %s",
+ __func__, strerror(errno));
+#ifdef SO_RERROR
+ if (errno == ENOBUFS)
+ bsd_route_overflow(sock, ctx, sock_ctx);
+#endif /* SO_RERROR */
+ return;
+ }
+
+ rtm = (struct rt_msghdr *) event_buf;
+ if (rtm->rtm_version != RTM_VERSION) {
+ wpa_printf(MSG_DEBUG, "Invalid routing message version=%d",
+ rtm->rtm_version);
+ return;
+ }
+ os_memset(&event, 0, sizeof(event));
+ switch (rtm->rtm_type) {
+ case RTM_IEEE80211:
+ ifan = (struct if_announcemsghdr *) rtm;
+ drv = bsd_get_drvindex(global, ifan->ifan_index);
+ if (drv == NULL)
+ return;
+ switch (ifan->ifan_what) {
+ case RTM_IEEE80211_ASSOC:
+ case RTM_IEEE80211_REASSOC:
+ if (drv->is_ap)
+ break;
+ wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL);
+ break;
+ case RTM_IEEE80211_DISASSOC:
+ if (drv->is_ap)
+ break;
+ wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
+ break;
+ case RTM_IEEE80211_SCAN:
+ if (drv->is_ap)
+ break;
+ wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS,
+ NULL);
+ break;
+ case RTM_IEEE80211_LEAVE:
+ leave = (struct ieee80211_leave_event *) &ifan[1];
+ drv_event_disassoc(drv->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, 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);
+ event.michael_mic_failure.src = mic->iev_src;
+ wpa_supplicant_event(drv->ctx,
+ EVENT_MICHAEL_MIC_FAILURE, &event);
+ break;
+ }
+ break;
+ case RTM_IFANNOUNCE:
+ ifan = (struct if_announcemsghdr *) rtm;
+ switch (ifan->ifan_what) {
+ case IFAN_DEPARTURE:
+ drv = bsd_get_drvindex(global, ifan->ifan_index);
+ if (drv)
+ drv->if_removed = 1;
+ event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
+ break;
+ case IFAN_ARRIVAL:
+ drv = bsd_get_drvname(global, ifan->ifan_name);
+ if (drv) {
+ drv->ifindex = ifan->ifan_index;
+ drv->if_removed = 0;
+ }
+ event.interface_status.ievent = EVENT_INTERFACE_ADDED;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "RTM_IFANNOUNCE: unknown action");
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "RTM_IFANNOUNCE: Interface '%s' %s",
+ ifan->ifan_name,
+ ifan->ifan_what == IFAN_DEPARTURE ?
+ "removed" : "added");
+ os_strlcpy(event.interface_status.ifname, ifan->ifan_name,
+ sizeof(event.interface_status.ifname));
+ if (drv) {
+ wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS,
+ &event);
+ /*
+ * Set ifindex to zero after sending the event as the
+ * event might query the driver to ensure a match.
+ */
+ if (ifan->ifan_what == IFAN_DEPARTURE)
+ drv->ifindex = 0;
+ } else {
+ wpa_supplicant_event_global(global->ctx,
+ EVENT_INTERFACE_STATUS,
+ &event);
+ }
+ break;
+ case RTM_IFINFO:
+ ifm = (struct if_msghdr *) rtm;
+ drv = bsd_get_drvindex(global, ifm->ifm_index);
+ if (drv == NULL)
+ return;
+ if ((ifm->ifm_flags & IFF_UP) == 0 &&
+ (drv->flags & IFF_UP) != 0) {
+ wpa_printf(MSG_DEBUG, "RTM_IFINFO: Interface '%s' DOWN",
+ drv->ifname);
+ wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED,
+ NULL);
+ } else if ((ifm->ifm_flags & IFF_UP) != 0 &&
+ (drv->flags & IFF_UP) == 0) {
+ wpa_printf(MSG_DEBUG, "RTM_IFINFO: Interface '%s' UP",
+ drv->ifname);
+ wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED,
+ NULL);
+ }
+ drv->flags = ifm->ifm_flags;
+ break;
+ }
+}
+
+#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,
+ u16 reason_code, int link_id);
+
+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,
+ int link_id, 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) {
+ wpa_printf(MSG_INFO, "Failed to get encryption");
+ 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,
+ -1);
+}
+
+
+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, u16 reason_code,
+ int link_id)
+{
+ 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,
+ u16 reason_code)
+{
+ return bsd_send_mlme_param(priv, IEEE80211_MLME_DISASSOC, reason_code,
+ addr);
+}
+
+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 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) {
+ wpa_printf(MSG_ERROR, "Could not allocate memory for bsd driver data");
+ return NULL;
+ }
+
+ drv->ifindex = if_nametoindex(params->ifname);
+ if (drv->ifindex == 0) {
+ wpa_printf(MSG_DEBUG, "%s: interface %s does not exist",
+ __func__, params->ifname);
+ goto bad;
+ }
+
+ drv->ctx = hapd;
+ drv->is_ap = 1;
+ drv->global = params->global_priv;
+ 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;
+
+ if (bsd_get_iface_flags(drv) < 0)
+ goto bad;
+
+ if (bsd_set_mediaopt(drv, IFM_OMASK, IFM_IEEE80211_HOSTAP) < 0) {
+ wpa_printf(MSG_ERROR, "%s: failed to set operation mode",
+ __func__);
+ goto bad;
+ }
+
+ dl_list_add(&drv->global->ifaces, &drv->list);
+
+ return drv;
+bad:
+ if (drv->sock_xmit != NULL)
+ l2_packet_deinit(drv->sock_xmit);
+ os_free(drv);
+ return NULL;
+}
+
+
+static void
+bsd_deinit(void *priv)
+{
+ struct bsd_driver_data *drv = priv;
+
+ if (drv->sock_xmit != NULL)
+ l2_packet_deinit(drv->sock_xmit);
+ os_free(drv);
+}
+
+
+static int
+bsd_set_sta_authorized(void *priv, const u8 *addr,
+ unsigned int total_flags, unsigned int flags_or,
+ unsigned 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);
+}
+#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->global->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",
+ __func__, 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", __func__, 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, u16 reason_code)
+{
+ return bsd_send_mlme_param(priv, IEEE80211_MLME_DEAUTH, 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 == WPA_CIPHER_NONE &&
+ params->group_suite == WPA_CIPHER_NONE &&
+ params->key_mgmt_suite == WPA_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 (!(drv->flags & IFF_UP)) {
+ wpa_printf(MSG_DEBUG, "%s: interface is not up, cannot scan",
+ __func__);
+ 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_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;
+
+#ifdef __FreeBSD__
+ /*
+ * the rssi value reported by the kernel is in 0.5dB steps relative to
+ * the reported noise floor. see ieee80211_node.h for details.
+ */
+ result->level = sr->isr_rssi / 2 + sr->isr_noise;
+#else
+ result->level = sr->isr_rssi;
+#endif
+
+ 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;
+
+#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+ os_memcpy(pos, (u8 *)(sr + 1) + sr->isr_ssid_len + sr->isr_meshid_len,
+ sr->isr_ie_len);
+#else
+ os_memcpy(pos, (u8 *)(sr + 1) + sr->isr_ssid_len, sr->isr_ie_len);
+#endif
+ pos += sr->isr_ie_len;
+
+ result->ie_len = pos - (u8 *)(result + 1);
+
+ tmp = os_realloc_array(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 enum ieee80211_opmode
+get80211opmode(struct bsd_driver_data *drv)
+{
+ struct ifmediareq ifmr;
+
+ (void) memset(&ifmr, 0, sizeof(ifmr));
+ (void) os_strlcpy(ifmr.ifm_name, drv->ifname, sizeof(ifmr.ifm_name));
+
+ if (ioctl(drv->global->sock, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) {
+ if (ifmr.ifm_current & IFM_IEEE80211_ADHOC) {
+ if (ifmr.ifm_current & IFM_FLAG0)
+ return IEEE80211_M_AHDEMO;
+ else
+ return IEEE80211_M_IBSS;
+ }
+ if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP)
+ return IEEE80211_M_HOSTAP;
+ if (ifmr.ifm_current & IFM_IEEE80211_MONITOR)
+ return IEEE80211_M_MONITOR;
+#ifdef IEEE80211_M_MBSS
+ if (ifmr.ifm_current & IFM_IEEE80211_MBSS)
+ return IEEE80211_M_MBSS;
+#endif /* IEEE80211_M_MBSS */
+ }
+ return IEEE80211_M_STA;
+}
+
+static void *
+wpa_driver_bsd_init(void *ctx, const char *ifname, void *priv)
+{
+#define GETPARAM(drv, param, v) \
+ (((v) = get80211param(drv, param)) != -1)
+ struct bsd_driver_data *drv;
+ int i;
+
+ drv = os_zalloc(sizeof(*drv));
+ if (drv == NULL)
+ return NULL;
+
+ drv->ifindex = if_nametoindex(ifname);
+ if (drv->ifindex == 0) {
+ wpa_printf(MSG_DEBUG, "%s: interface %s does not exist",
+ __func__, ifname);
+ goto fail;
+ }
+
+ drv->ctx = ctx;
+ drv->global = priv;
+ os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+
+ /* Set the interface as removed until proven to work. */
+ drv->if_removed = 1;
+
+ 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;
+
+ /* Update per interface supported AKMs */
+ for (i = 0; i < WPA_IF_MAX; i++)
+ drv->capa.key_mgmt_iftype[i] = drv->capa.key_mgmt;
+
+ /* Down interface during setup. */
+ if (bsd_get_iface_flags(drv) < 0)
+ goto fail;
+
+ /* Proven to work, lets go! */
+ drv->if_removed = 0;
+
+ drv->opmode = get80211opmode(drv);
+ dl_list_add(&drv->global->ifaces, &drv->list);
+
+ return drv;
+fail:
+ os_free(drv);
+ return NULL;
+#undef GETPARAM
+}
+
+static void
+wpa_driver_bsd_deinit(void *priv)
+{
+ struct bsd_driver_data *drv = priv;
+
+ if (drv->ifindex != 0 && !drv->if_removed) {
+ wpa_driver_bsd_set_wpa(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);
+ dl_list_del(&drv->list);
+ 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 */
+
+static void *
+bsd_global_init(void *ctx)
+{
+ struct bsd_driver_global *global;
+#if defined(RO_MSGFILTER) || defined(ROUTE_MSGFILTER)
+ unsigned char msgfilter[] = {
+ RTM_IEEE80211,
+ RTM_IFINFO, RTM_IFANNOUNCE,
+ };
+#endif
+#ifdef ROUTE_MSGFILTER
+ unsigned int i, msgfilter_mask;
+#endif
+
+ global = os_zalloc(sizeof(*global));
+ if (global == NULL)
+ return NULL;
+
+ global->ctx = ctx;
+ dl_list_init(&global->ifaces);
+
+ global->sock = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+ if (global->sock < 0) {
+ wpa_printf(MSG_ERROR, "socket[PF_INET,SOCK_DGRAM]: %s",
+ strerror(errno));
+ goto fail1;
+ }
+
+ global->route = socket(PF_ROUTE,
+ SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
+ if (global->route < 0) {
+ wpa_printf(MSG_ERROR, "socket[PF_ROUTE,SOCK_RAW]: %s",
+ strerror(errno));
+ goto fail;
+ }
+
+#if defined(RO_MSGFILTER)
+ if (setsockopt(global->route, PF_ROUTE, RO_MSGFILTER,
+ &msgfilter, sizeof(msgfilter)) < 0)
+ wpa_printf(MSG_ERROR, "socket[PF_ROUTE,RO_MSGFILTER]: %s",
+ strerror(errno));
+#elif defined(ROUTE_MSGFILTER)
+ msgfilter_mask = 0;
+ for (i = 0; i < (sizeof(msgfilter) / sizeof(msgfilter[0])); i++)
+ msgfilter_mask |= ROUTE_FILTER(msgfilter[i]);
+ if (setsockopt(global->route, PF_ROUTE, ROUTE_MSGFILTER,
+ &msgfilter_mask, sizeof(msgfilter_mask)) < 0)
+ wpa_printf(MSG_ERROR, "socket[PF_ROUTE,ROUTE_MSGFILTER]: %s",
+ strerror(errno));
+#endif
+
+ eloop_register_read_sock(global->route, bsd_wireless_event_receive,
+ NULL, global);
+
+ return global;
+
+fail:
+ close(global->sock);
+fail1:
+ os_free(global);
+ return NULL;
+}
+
+static void
+bsd_global_deinit(void *priv)
+{
+ struct bsd_driver_global *global = priv;
+
+ eloop_unregister_read_sock(global->route);
+ (void) close(global->route);
+ (void) close(global->sock);
+ os_free(global);
+}
+
+
+const struct wpa_driver_ops wpa_driver_bsd_ops = {
+ .name = "bsd",
+ .desc = "BSD 802.11 support",
+ .global_init = bsd_global_init,
+ .global_deinit = bsd_global_deinit,
+#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,
+ .sta_set_flags = bsd_set_sta_authorized,
+#else /* HOSTAPD */
+ .init2 = 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,
+ .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,
+ .set_generic_elem = bsd_set_opt_ie,
+};
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_common.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_common.c
new file mode 100644
index 0000000..f3625e8
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_common.c
@@ -0,0 +1,364 @@
+/*
+ * Common driver-related functions
+ * Copyright (c) 2003-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include "utils/common.h"
+#include "driver.h"
+
+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);
+}
+
+
+const char * event_to_string(enum wpa_event_type event)
+{
+#define E2S(n) case EVENT_ ## n: return #n
+ switch (event) {
+ E2S(ASSOC);
+ E2S(DISASSOC);
+ E2S(MICHAEL_MIC_FAILURE);
+ E2S(SCAN_RESULTS);
+ E2S(ASSOCINFO);
+ E2S(INTERFACE_STATUS);
+ E2S(PMKID_CANDIDATE);
+ E2S(TDLS);
+ E2S(FT_RESPONSE);
+ E2S(IBSS_RSN_START);
+ E2S(AUTH);
+ E2S(DEAUTH);
+ E2S(ASSOC_REJECT);
+ E2S(AUTH_TIMED_OUT);
+ E2S(ASSOC_TIMED_OUT);
+ E2S(WPS_BUTTON_PUSHED);
+ E2S(TX_STATUS);
+ E2S(RX_FROM_UNKNOWN);
+ E2S(RX_MGMT);
+ E2S(REMAIN_ON_CHANNEL);
+ E2S(CANCEL_REMAIN_ON_CHANNEL);
+ E2S(RX_PROBE_REQ);
+ E2S(NEW_STA);
+ E2S(EAPOL_RX);
+ E2S(SIGNAL_CHANGE);
+ E2S(INTERFACE_ENABLED);
+ E2S(INTERFACE_DISABLED);
+ E2S(CHANNEL_LIST_CHANGED);
+ E2S(INTERFACE_UNAVAILABLE);
+ E2S(BEST_CHANNEL);
+ E2S(UNPROT_DEAUTH);
+ E2S(UNPROT_DISASSOC);
+ E2S(STATION_LOW_ACK);
+ E2S(IBSS_PEER_LOST);
+ E2S(DRIVER_GTK_REKEY);
+ E2S(SCHED_SCAN_STOPPED);
+ E2S(DRIVER_CLIENT_POLL_OK);
+ E2S(EAPOL_TX_STATUS);
+ E2S(CH_SWITCH);
+ E2S(CH_SWITCH_STARTED);
+ E2S(WNM);
+ E2S(CONNECT_FAILED_REASON);
+ E2S(DFS_RADAR_DETECTED);
+ E2S(DFS_CAC_FINISHED);
+ E2S(DFS_CAC_ABORTED);
+ E2S(DFS_NOP_FINISHED);
+ E2S(SURVEY);
+ E2S(SCAN_STARTED);
+ E2S(AVOID_FREQUENCIES);
+ E2S(NEW_PEER_CANDIDATE);
+ E2S(ACS_CHANNEL_SELECTED);
+ E2S(DFS_CAC_STARTED);
+ E2S(P2P_LO_STOP);
+ E2S(BEACON_LOSS);
+ E2S(DFS_PRE_CAC_EXPIRED);
+ E2S(EXTERNAL_AUTH);
+ E2S(PORT_AUTHORIZED);
+ E2S(STATION_OPMODE_CHANGED);
+ E2S(INTERFACE_MAC_CHANGED);
+ E2S(WDS_STA_INTERFACE_STATUS);
+ E2S(UPDATE_DH);
+ E2S(UNPROT_BEACON);
+ E2S(TX_WAIT_EXPIRE);
+ E2S(BSS_COLOR_COLLISION);
+ E2S(CCA_STARTED_NOTIFY);
+ E2S(CCA_ABORTED_NOTIFY);
+ E2S(CCA_NOTIFY);
+ E2S(PASN_AUTH);
+ E2S(LINK_CH_SWITCH);
+ E2S(LINK_CH_SWITCH_STARTED);
+ E2S(TID_LINK_MAP);
+ E2S(LINK_RECONFIG);
+ }
+
+ return "UNKNOWN";
+#undef E2S
+}
+
+
+const char * channel_width_to_string(enum chan_width width)
+{
+ switch (width) {
+ case CHAN_WIDTH_20_NOHT:
+ return "20 MHz (no HT)";
+ case CHAN_WIDTH_20:
+ return "20 MHz";
+ case CHAN_WIDTH_40:
+ return "40 MHz";
+ case CHAN_WIDTH_80:
+ return "80 MHz";
+ case CHAN_WIDTH_80P80:
+ return "80+80 MHz";
+ case CHAN_WIDTH_160:
+ return "160 MHz";
+ case CHAN_WIDTH_320:
+ return "320 MHz";
+ default:
+ return "unknown";
+ }
+}
+
+
+int channel_width_to_int(enum chan_width width)
+{
+ switch (width) {
+ case CHAN_WIDTH_20_NOHT:
+ case CHAN_WIDTH_20:
+ return 20;
+ case CHAN_WIDTH_40:
+ return 40;
+ case CHAN_WIDTH_80:
+ return 80;
+ case CHAN_WIDTH_80P80:
+ case CHAN_WIDTH_160:
+ return 160;
+ case CHAN_WIDTH_320:
+ return 320;
+ default:
+ return 0;
+ }
+}
+
+
+int ht_supported(const struct hostapd_hw_modes *mode)
+{
+ if (!(mode->flags & HOSTAPD_MODE_FLAG_HT_INFO_KNOWN)) {
+ /*
+ * The driver did not indicate whether it supports HT. Assume
+ * it does to avoid connection issues.
+ */
+ return 1;
+ }
+
+ /*
+ * IEEE Std 802.11n-2009 20.1.1:
+ * An HT non-AP STA shall support all EQM rates for one spatial stream.
+ */
+ return mode->mcs_set[0] == 0xff;
+}
+
+
+int vht_supported(const struct hostapd_hw_modes *mode)
+{
+ if (!(mode->flags & HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN)) {
+ /*
+ * The driver did not indicate whether it supports VHT. Assume
+ * it does to avoid connection issues.
+ */
+ return 1;
+ }
+
+ /*
+ * A VHT non-AP STA shall support MCS 0-7 for one spatial stream.
+ * TODO: Verify if this complies with the standard
+ */
+ return (mode->vht_mcs_set[0] & 0x3) != 3;
+}
+
+
+bool he_supported(const struct hostapd_hw_modes *hw_mode,
+ enum ieee80211_op_mode op_mode)
+{
+ if (!(hw_mode->flags & HOSTAPD_MODE_FLAG_HE_INFO_KNOWN)) {
+ /*
+ * The driver did not indicate whether it supports HE. Assume
+ * it does to avoid connection issues.
+ */
+ return true;
+ }
+
+ return hw_mode->he_capab[op_mode].he_supported;
+}
+
+
+static int wpa_check_wowlan_trigger(const char *start, const char *trigger,
+ int capa_trigger, u8 *param_trigger)
+{
+ if (os_strcmp(start, trigger) != 0)
+ return 0;
+ if (!capa_trigger)
+ return 0;
+
+ *param_trigger = 1;
+ return 1;
+}
+
+
+struct wowlan_triggers *
+wpa_get_wowlan_triggers(const char *wowlan_triggers,
+ const struct wpa_driver_capa *capa)
+{
+ struct wowlan_triggers *triggers;
+ char *start, *end, *buf;
+ int last;
+
+ if (!wowlan_triggers)
+ return NULL;
+
+ buf = os_strdup(wowlan_triggers);
+ if (buf == NULL)
+ return NULL;
+
+ triggers = os_zalloc(sizeof(*triggers));
+ if (triggers == NULL)
+ goto out;
+
+#define CHECK_TRIGGER(trigger) \
+ wpa_check_wowlan_trigger(start, #trigger, \
+ capa->wowlan_triggers.trigger, \
+ &triggers->trigger)
+
+ start = buf;
+ while (*start != '\0') {
+ while (isblank((unsigned char) *start))
+ start++;
+ if (*start == '\0')
+ break;
+ end = start;
+ while (!isblank((unsigned char) *end) && *end != '\0')
+ end++;
+ last = *end == '\0';
+ *end = '\0';
+
+ if (!CHECK_TRIGGER(any) &&
+ !CHECK_TRIGGER(disconnect) &&
+ !CHECK_TRIGGER(magic_pkt) &&
+ !CHECK_TRIGGER(gtk_rekey_failure) &&
+ !CHECK_TRIGGER(eap_identity_req) &&
+ !CHECK_TRIGGER(four_way_handshake) &&
+ !CHECK_TRIGGER(rfkill_release)) {
+ wpa_printf(MSG_DEBUG,
+ "Unknown/unsupported wowlan trigger '%s'",
+ start);
+ os_free(triggers);
+ triggers = NULL;
+ goto out;
+ }
+
+ if (last)
+ break;
+ start = end + 1;
+ }
+#undef CHECK_TRIGGER
+
+out:
+ os_free(buf);
+ return triggers;
+}
+
+
+const char * driver_flag_to_string(u64 flag)
+{
+#define DF2S(x) case WPA_DRIVER_FLAGS_ ## x: return #x
+ switch (flag) {
+ DF2S(DRIVER_IE);
+ DF2S(SET_KEYS_AFTER_ASSOC);
+ DF2S(DFS_OFFLOAD);
+ DF2S(4WAY_HANDSHAKE_PSK);
+ DF2S(4WAY_HANDSHAKE_8021X);
+ DF2S(WIRED);
+ DF2S(SME);
+ DF2S(AP);
+ DF2S(SET_KEYS_AFTER_ASSOC_DONE);
+ DF2S(HT_2040_COEX);
+ DF2S(P2P_CONCURRENT);
+ DF2S(P2P_DEDICATED_INTERFACE);
+ DF2S(P2P_CAPABLE);
+ DF2S(AP_TEARDOWN_SUPPORT);
+ DF2S(P2P_MGMT_AND_NON_P2P);
+ DF2S(VALID_ERROR_CODES);
+ DF2S(OFFCHANNEL_TX);
+ DF2S(EAPOL_TX_STATUS);
+ DF2S(DEAUTH_TX_STATUS);
+ DF2S(BSS_SELECTION);
+ DF2S(TDLS_SUPPORT);
+ DF2S(TDLS_EXTERNAL_SETUP);
+ DF2S(PROBE_RESP_OFFLOAD);
+ DF2S(AP_UAPSD);
+ DF2S(INACTIVITY_TIMER);
+ DF2S(AP_MLME);
+ DF2S(SAE);
+ DF2S(OBSS_SCAN);
+ DF2S(IBSS);
+ DF2S(RADAR);
+ DF2S(DEDICATED_P2P_DEVICE);
+ DF2S(QOS_MAPPING);
+ DF2S(AP_CSA);
+ DF2S(MESH);
+ DF2S(ACS_OFFLOAD);
+ DF2S(KEY_MGMT_OFFLOAD);
+ DF2S(TDLS_CHANNEL_SWITCH);
+ DF2S(HT_IBSS);
+ DF2S(VHT_IBSS);
+ DF2S(SUPPORT_HW_MODE_ANY);
+ DF2S(OFFCHANNEL_SIMULTANEOUS);
+ DF2S(FULL_AP_CLIENT_STATE);
+ DF2S(P2P_LISTEN_OFFLOAD);
+ DF2S(SUPPORT_FILS);
+ DF2S(BEACON_RATE_LEGACY);
+ DF2S(BEACON_RATE_HT);
+ DF2S(BEACON_RATE_VHT);
+ DF2S(MGMT_TX_RANDOM_TA);
+ DF2S(MGMT_TX_RANDOM_TA_CONNECTED);
+ DF2S(SCHED_SCAN_RELATIVE_RSSI);
+ DF2S(HE_CAPABILITIES);
+ DF2S(FILS_SK_OFFLOAD);
+ DF2S(OCE_STA);
+ DF2S(OCE_AP);
+ DF2S(OCE_STA_CFON);
+ DF2S(MFP_OPTIONAL);
+ DF2S(SELF_MANAGED_REGULATORY);
+ DF2S(FTM_RESPONDER);
+ DF2S(CONTROL_PORT);
+ DF2S(VLAN_OFFLOAD);
+ DF2S(UPDATE_FT_IES);
+ DF2S(SAFE_PTK0_REKEYS);
+ DF2S(BEACON_PROTECTION);
+ DF2S(EXTENDED_KEY_ID);
+ }
+ return "UNKNOWN";
+#undef DF2S
+}
+
+
+const char * driver_flag2_to_string(u64 flag2)
+{
+#define DF2S(x) case WPA_DRIVER_FLAGS2_ ## x: return #x
+ switch (flag2) {
+ DF2S(CONTROL_PORT_RX);
+ DF2S(CONTROL_PORT_TX_STATUS);
+ }
+ return "UNKNOWN";
+#undef DF2S
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_hostap.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_hostap.c
new file mode 100644
index 0000000..d3520aa
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_hostap.c
@@ -0,0 +1,1208 @@
+/*
+ * Driver interaction with Linux Host AP driver
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+
+#include "linux_wext.h"
+#include "common.h"
+#include "driver.h"
+#include "driver_wext.h"
+#include "eloop.h"
+#include "driver_hostap.h"
+
+
+#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"
+#include "common/ieee802_11_common.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.bssid = get_hdr_bssid(hdr, len);
+ event.rx_from_unknown.addr = sa;
+ 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, type, stype;
+ 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 2 is reserved for indicating ACKed frame (TX
+ * callbacks), and version 1 for indicating failed frame (no ACK, TX
+ * callbacks) */
+ 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) {
+ wpa_printf(MSG_ERROR, "recv: %s", strerror(errno));
+ 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) {
+ wpa_printf(MSG_ERROR, "socket[PF_PACKET,SOCK_RAW]: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ if (eloop_register_read_sock(drv->sock, handle_read, drv, NULL)) {
+ wpa_printf(MSG_ERROR, "Could not register read socket");
+ return -1;
+ }
+
+ memset(&ifr, 0, sizeof(ifr));
+ if (os_snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%sap",
+ drv->iface) >= (int) sizeof(ifr.ifr_name)) {
+ wpa_printf(MSG_ERROR, "hostap: AP interface name truncated");
+ return -1;
+ }
+ if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) {
+ wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s",
+ strerror(errno));
+ 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) {
+ wpa_printf(MSG_ERROR, "bind: %s", strerror(errno));
+ 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, int noack,
+ unsigned int freq,
+ const u16 *csa_offs, size_t csa_offs_len,
+ int no_encrypt, unsigned int wait, int link_id)
+{
+ 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, int link_id)
+{
+ 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, 0, 0, NULL, 0, 0, 0, -1);
+ if (res < 0) {
+ wpa_printf(MSG_ERROR, "hostap_send_eapol - packet len: %lu - "
+ "failed: %d (%s)",
+ (unsigned long) len, errno, strerror(errno));
+ }
+ os_free(hdr);
+
+ return res;
+}
+
+
+static int hostap_sta_set_flags(void *priv, const u8 *addr,
+ unsigned int total_flags, unsigned int flags_or,
+ unsigned 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];
+
+ if (os_snprintf(ifname, IFNAMSIZ, "%sap", drv->iface) >= IFNAMSIZ) {
+ wpa_printf(MSG_ERROR, "hostap: AP interface name truncated");
+ return -1;
+ }
+ 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) {
+ wpa_printf(MSG_INFO,
+ "Setting MTU failed - trying to survive with current value: ioctl[SIOCSIFMTU]: %s",
+ strerror(errno));
+ }
+ }
+
+ 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) {
+ wpa_printf(MSG_ERROR, "ioctl[PRISM2_IOCTL_HOSTAPD]: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wpa_driver_hostap_set_key(void *priv,
+ struct wpa_driver_set_key_params *params)
+{
+ struct hostap_driver_data *drv = priv;
+ struct prism2_hostapd_param *param;
+ u8 *buf;
+ size_t blen;
+ int ret = 0;
+ enum wpa_alg alg = params->alg;
+ const u8 *addr = params->addr;
+ int key_idx = params->key_idx;
+ int set_tx = params->set_tx;
+ const u8 *key = params->key;
+ size_t key_len = params->key_len;
+
+ 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, int link_id, 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)
+ os_memset(param->sta_addr, 0xff, ETH_ALEN);
+ else
+ os_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 {
+ os_memcpy(seq, param->u.crypt.seq, 8);
+ }
+ os_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) {
+ wpa_printf(MSG_ERROR, "ioctl[PRISM2_IOCTL_PRISM2_PARAM]: %s",
+ strerror(errno));
+ 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) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCSIWESSID,len=%d]: %s",
+ len, strerror(errno));
+ 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_memdup(elem, elem_len);
+ if (drv->generic_ie == NULL)
+ return -1;
+ 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_memdup(wpabuf_head(proberesp),
+ wpabuf_len(proberesp));
+ if (drv->wps_ie == NULL)
+ return -1;
+ 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, unsigned int len)
+{
+ struct iw_event iwe_buf, *iwe = &iwe_buf;
+ char *pos, *end, *custom, *buf;
+
+ pos = data;
+ end = data + len;
+
+ while ((size_t) (end - pos) >= IW_EV_LCP_LEN) {
+ /* 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 || iwe->len > end - pos)
+ 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 (iwe->u.data.length > end - custom)
+ 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) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCGIWRANGE]: %s",
+ strerror(errno));
+ 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->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) {
+ wpa_printf(MSG_ERROR, "socket[PF_INET,SOCK_DGRAM]: %s",
+ strerror(errno));
+ os_free(drv);
+ return NULL;
+ }
+
+ if (hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOSTAPD, 1)) {
+ wpa_printf(MSG_ERROR,
+ "Could not enable hostapd mode for interface %s",
+ drv->iface);
+ close(drv->ioctl_sock);
+ os_free(drv);
+ return NULL;
+ }
+
+ if (hostap_init_sockets(drv, params->own_addr) ||
+ hostap_wireless_event_init(drv)) {
+ close(drv->ioctl_sock);
+ os_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,
+ u16 reason, int link_id)
+{
+ 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), 0, 0, NULL, 0, 0, 0, -1);
+}
+
+
+static int hostap_set_freq(void *priv, struct hostapd_freq_params *freq)
+{
+ struct hostap_driver_data *drv = priv;
+ struct iwreq iwr;
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+ iwr.u.freq.m = freq->channel;
+ iwr.u.freq.e = 0;
+
+ if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCSIWFREQ]: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int hostap_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
+ u16 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), 0, 0, NULL, 0, 0, 0,
+ -1);
+}
+
+
+static struct hostapd_hw_modes * hostap_get_hw_feature_data(void *priv,
+ u16 *num_modes,
+ u16 *flags, u8 *dfs)
+{
+ 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;
+ *dfs = 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];
+ mode->channels[i].allowed_bw = HOSTAPD_CHAN_WIDTH_20;
+ /* 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;
+}
+
+
+static void wpa_driver_hostap_poll_client(void *priv, const u8 *own_addr,
+ const u8 *addr, int qos)
+{
+ struct ieee80211_hdr hdr;
+
+ os_memset(&hdr, 0, sizeof(hdr));
+
+ /*
+ * WLAN_FC_STYPE_NULLFUNC would be more appropriate,
+ * but it is apparently not retried so TX Exc events
+ * are not received for it.
+ * This is the reason the driver overrides the default
+ * handling.
+ */
+ hdr.frame_control = IEEE80211_FC(WLAN_FC_TYPE_DATA,
+ WLAN_FC_STYPE_DATA);
+
+ hdr.frame_control |=
+ host_to_le16(WLAN_FC_FROMDS);
+ os_memcpy(hdr.IEEE80211_DA_FROMDS, addr, ETH_ALEN);
+ os_memcpy(hdr.IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN);
+ os_memcpy(hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
+
+ hostap_send_mlme(priv, (u8 *)&hdr, sizeof(hdr), 0, 0, NULL, 0, 0, 0,
+ -1);
+}
+
+
+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,
+ .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,
+ .set_freq = hostap_set_freq,
+ .poll_client = wpa_driver_hostap_poll_client,
+};
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_hostap.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_hostap.h
new file mode 100644
index 0000000..ac0b83a
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_hostap.h
@@ -0,0 +1,208 @@
+/*
+ * Driver interaction with Linux Host AP driver
+ * Copyright (c) 2002-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README 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_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[SSID_MAX_LEN];
+ } 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/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_macsec_linux.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_macsec_linux.c
new file mode 100644
index 0000000..c867154
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_macsec_linux.c
@@ -0,0 +1,1720 @@
+/*
+ * Driver interaction with Linux MACsec kernel module
+ * Copyright (c) 2016, Sabrina Dubroca <sd@queasysnail.net> and Red Hat, Inc.
+ * Copyright (c) 2019, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <netpacket/packet.h>
+#include <net/if_arp.h>
+#include <net/if.h>
+#include <netlink/netlink.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/ctrl.h>
+#include <netlink/route/link.h>
+#include <netlink/route/link/macsec.h>
+#include <linux/if_macsec.h>
+#include <inttypes.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/eapol_common.h"
+#include "pae/ieee802_1x_kay.h"
+#include "driver.h"
+#include "driver_wired_common.h"
+
+#define DRV_PREFIX "macsec_linux: "
+
+#define UNUSED_SCI 0xffffffffffffffff
+
+#if LIBNL_VER_NUM >= LIBNL_VER(3, 6)
+#define LIBNL_HAS_OFFLOAD
+#endif
+
+struct cb_arg {
+ struct macsec_drv_data *drv;
+ u32 *pn;
+ int ifindex;
+ u8 txsa;
+ u8 rxsa;
+ u64 rxsci;
+};
+
+struct macsec_genl_ctx {
+ struct nl_sock *sk;
+ int macsec_genl_id;
+ struct cb_arg cb_arg;
+};
+
+struct macsec_drv_data {
+ struct driver_wired_common_data common;
+ struct rtnl_link *link;
+ struct nl_cache *link_cache;
+ struct nl_sock *sk;
+ struct macsec_genl_ctx ctx;
+
+ char ifname[IFNAMSIZ + 1];
+ int ifi;
+ int parent_ifi;
+ int use_pae_group_addr;
+
+ bool created_link;
+
+ bool controlled_port_enabled;
+ bool controlled_port_enabled_set;
+
+ bool protect_frames;
+ bool protect_frames_set;
+
+ bool encrypt;
+ bool encrypt_set;
+
+ bool replay_protect;
+ bool replay_protect_set;
+
+#ifdef LIBNL_HAS_OFFLOAD
+ enum macsec_offload offload;
+ bool offload_set;
+#endif /* LIBNL_HAS_OFFLOAD */
+
+ u32 replay_window;
+
+ u8 encoding_sa;
+ bool encoding_sa_set;
+
+ u64 cipher_suite;
+ bool cipher_suite_set;
+};
+
+
+static int dump_callback(struct nl_msg *msg, void *argp);
+
+
+static struct nl_msg * msg_prepare(enum macsec_nl_commands cmd,
+ const struct macsec_genl_ctx *ctx,
+ unsigned int ifindex)
+{
+ struct nl_msg *msg;
+
+ msg = nlmsg_alloc();
+ if (!msg) {
+ wpa_printf(MSG_ERROR, DRV_PREFIX "failed to alloc message");
+ return NULL;
+ }
+
+ if (!genlmsg_put(msg, 0, 0, ctx->macsec_genl_id, 0, 0, cmd, 0)) {
+ wpa_printf(MSG_ERROR, DRV_PREFIX "failed to put header");
+ goto nla_put_failure;
+ }
+
+ NLA_PUT_U32(msg, MACSEC_ATTR_IFINDEX, ifindex);
+
+ return msg;
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return NULL;
+}
+
+
+static int nla_put_rxsc_config(struct nl_msg *msg, u64 sci)
+{
+ struct nlattr *nest = nla_nest_start(msg, MACSEC_ATTR_RXSC_CONFIG);
+
+ if (!nest)
+ return -1;
+
+ NLA_PUT_U64(msg, MACSEC_RXSC_ATTR_SCI, sci);
+
+ nla_nest_end(msg, nest);
+
+ return 0;
+
+nla_put_failure:
+ return -1;
+}
+
+
+static int init_genl_ctx(struct macsec_drv_data *drv)
+{
+ struct macsec_genl_ctx *ctx = &drv->ctx;
+
+ ctx->sk = nl_socket_alloc();
+ if (!ctx->sk) {
+ wpa_printf(MSG_ERROR, DRV_PREFIX "failed to alloc genl socket");
+ return -1;
+ }
+
+ if (genl_connect(ctx->sk) < 0) {
+ wpa_printf(MSG_ERROR,
+ DRV_PREFIX "connection to genl socket failed");
+ goto out_free;
+ }
+
+ ctx->macsec_genl_id = genl_ctrl_resolve(ctx->sk, "macsec");
+ if (ctx->macsec_genl_id < 0) {
+ wpa_printf(MSG_ERROR, DRV_PREFIX "genl resolve failed");
+ goto out_free;
+ }
+
+ memset(&ctx->cb_arg, 0, sizeof(ctx->cb_arg));
+ ctx->cb_arg.drv = drv;
+
+ nl_socket_modify_cb(ctx->sk, NL_CB_VALID, NL_CB_CUSTOM, dump_callback,
+ &ctx->cb_arg);
+
+ return 0;
+
+out_free:
+ nl_socket_free(ctx->sk);
+ ctx->sk = NULL;
+ return -1;
+}
+
+
+static int try_commit(struct macsec_drv_data *drv)
+{
+ int err;
+
+ if (!drv->sk)
+ return 0;
+
+ if (!drv->link)
+ return 0;
+
+ if (drv->controlled_port_enabled_set) {
+ struct rtnl_link *change = rtnl_link_alloc();
+
+ wpa_printf(MSG_DEBUG, DRV_PREFIX
+ "%s: try_commit controlled_port_enabled=%d",
+ drv->ifname, drv->controlled_port_enabled);
+ if (!change)
+ return -1;
+
+ rtnl_link_set_name(change, drv->ifname);
+
+ if (drv->controlled_port_enabled)
+ rtnl_link_set_flags(change, IFF_UP);
+ else
+ rtnl_link_unset_flags(change, IFF_UP);
+
+ err = rtnl_link_change(drv->sk, change, change, 0);
+ if (err < 0)
+ return err;
+
+ rtnl_link_put(change);
+
+ drv->controlled_port_enabled_set = false;
+ }
+
+ if (drv->protect_frames_set) {
+ wpa_printf(MSG_DEBUG, DRV_PREFIX
+ "%s: try_commit protect_frames=%d",
+ drv->ifname, drv->protect_frames);
+ rtnl_link_macsec_set_protect(drv->link, drv->protect_frames);
+ }
+
+ if (drv->encrypt_set) {
+ wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: try_commit encrypt=%d",
+ drv->ifname, drv->encrypt);
+ rtnl_link_macsec_set_encrypt(drv->link, drv->encrypt);
+ }
+
+ if (drv->replay_protect_set) {
+ wpa_printf(MSG_DEBUG, DRV_PREFIX
+ "%s: try_commit replay_protect=%d replay_window=%d",
+ drv->ifname, drv->replay_protect,
+ drv->replay_window);
+ rtnl_link_macsec_set_replay_protect(drv->link,
+ drv->replay_protect);
+ if (drv->replay_protect)
+ rtnl_link_macsec_set_window(drv->link,
+ drv->replay_window);
+ }
+
+#ifdef LIBNL_HAS_OFFLOAD
+ if (drv->offload_set) {
+ wpa_printf(MSG_DEBUG, DRV_PREFIX
+ "%s: try_commit offload=%d",
+ drv->ifname, drv->offload);
+ rtnl_link_macsec_set_offload(drv->link, drv->offload);
+ }
+#endif /* LIBNL_HAS_OFFLOAD */
+
+ if (drv->encoding_sa_set) {
+ wpa_printf(MSG_DEBUG, DRV_PREFIX
+ "%s: try_commit encoding_sa=%d",
+ drv->ifname, drv->encoding_sa);
+ rtnl_link_macsec_set_encoding_sa(drv->link, drv->encoding_sa);
+ }
+
+ err = rtnl_link_add(drv->sk, drv->link, 0);
+ if (err < 0)
+ return err;
+
+ drv->protect_frames_set = false;
+ drv->encrypt_set = false;
+ drv->replay_protect_set = false;
+
+ return 0;
+}
+
+
+static void macsec_drv_wpa_deinit(void *priv)
+{
+ struct macsec_drv_data *drv = priv;
+
+ driver_wired_deinit_common(&drv->common);
+ os_free(drv);
+}
+
+
+static int macsec_check_macsec(void)
+{
+ struct nl_sock *sk;
+ int err = -1;
+
+ sk = nl_socket_alloc();
+ if (!sk) {
+ wpa_printf(MSG_ERROR, DRV_PREFIX "failed to alloc genl socket");
+ return -1;
+ }
+
+ if (genl_connect(sk) < 0) {
+ wpa_printf(MSG_ERROR,
+ DRV_PREFIX "connection to genl socket failed");
+ goto out_free;
+ }
+
+ if (genl_ctrl_resolve(sk, "macsec") < 0) {
+ wpa_printf(MSG_ERROR,
+ DRV_PREFIX "genl resolve failed - macsec kernel module not present?");
+ goto out_free;
+ }
+
+ err = 0;
+
+out_free:
+ nl_socket_free(sk);
+ return err;
+}
+
+
+static void * macsec_drv_wpa_init(void *ctx, const char *ifname)
+{
+ struct macsec_drv_data *drv;
+
+ if (macsec_check_macsec() < 0)
+ return NULL;
+
+ drv = os_zalloc(sizeof(*drv));
+ if (!drv)
+ return NULL;
+
+ if (driver_wired_init_common(&drv->common, ifname, ctx) < 0) {
+ os_free(drv);
+ return NULL;
+ }
+
+ return drv;
+}
+
+
+static int macsec_drv_macsec_init(void *priv, struct macsec_init_params *params)
+{
+ struct macsec_drv_data *drv = priv;
+ int err;
+
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+
+ drv->sk = nl_socket_alloc();
+ if (!drv->sk)
+ return -1;
+
+ err = nl_connect(drv->sk, NETLINK_ROUTE);
+ if (err < 0) {
+ wpa_printf(MSG_ERROR, DRV_PREFIX
+ "Unable to connect NETLINK_ROUTE socket: %s",
+ nl_geterror(err));
+ goto sock;
+ }
+
+ err = rtnl_link_alloc_cache(drv->sk, AF_UNSPEC, &drv->link_cache);
+ if (err < 0) {
+ wpa_printf(MSG_ERROR, DRV_PREFIX "Unable to get link cache: %s",
+ nl_geterror(err));
+ goto sock;
+ }
+
+ drv->parent_ifi = rtnl_link_name2i(drv->link_cache, drv->common.ifname);
+ if (drv->parent_ifi == 0) {
+ wpa_printf(MSG_ERROR, DRV_PREFIX
+ "couldn't find ifindex for interface %s",
+ drv->common.ifname);
+ goto cache;
+ }
+ wpa_printf(MSG_DEBUG, DRV_PREFIX "ifname=%s parent_ifi=%d",
+ drv->common.ifname, drv->parent_ifi);
+
+ err = init_genl_ctx(drv);
+ if (err < 0)
+ goto cache;
+
+ return 0;
+
+cache:
+ nl_cache_free(drv->link_cache);
+ drv->link_cache = NULL;
+sock:
+ nl_socket_free(drv->sk);
+ drv->sk = NULL;
+ return -1;
+}
+
+
+static int macsec_drv_macsec_deinit(void *priv)
+{
+ struct macsec_drv_data *drv = priv;
+
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+
+ if (drv->sk)
+ nl_socket_free(drv->sk);
+ drv->sk = NULL;
+
+ if (drv->link_cache)
+ nl_cache_free(drv->link_cache);
+ drv->link_cache = NULL;
+
+ if (drv->ctx.sk)
+ nl_socket_free(drv->ctx.sk);
+
+ return 0;
+}
+
+
+static int macsec_drv_get_capability(void *priv, enum macsec_cap *cap)
+{
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+
+ *cap = MACSEC_CAP_INTEG_AND_CONF;
+
+ return 0;
+}
+
+
+/**
+ * macsec_drv_enable_protect_frames - Set protect frames status
+ * @priv: Private driver interface data
+ * @enabled: true = protect frames enabled
+ * false = protect frames disabled
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_enable_protect_frames(void *priv, bool enabled)
+{
+ struct macsec_drv_data *drv = priv;
+
+ wpa_printf(MSG_DEBUG, "%s -> %s", __func__, enabled ? "TRUE" : "FALSE");
+
+ drv->protect_frames_set = true;
+ drv->protect_frames = enabled;
+
+ return try_commit(drv);
+}
+
+
+/**
+ * macsec_drv_enable_encrypt - Set protect frames status
+ * @priv: Private driver interface data
+ * @enabled: true = protect frames enabled
+ * false = protect frames disabled
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_enable_encrypt(void *priv, bool enabled)
+{
+ struct macsec_drv_data *drv = priv;
+
+ wpa_printf(MSG_DEBUG, "%s -> %s", __func__, enabled ? "TRUE" : "FALSE");
+
+ drv->encrypt_set = true;
+ drv->encrypt = enabled;
+
+ return try_commit(drv);
+}
+
+
+/**
+ * macsec_drv_set_replay_protect - Set replay protect status and window size
+ * @priv: Private driver interface data
+ * @enabled: true = replay protect enabled
+ * false = replay protect disabled
+ * @window: replay window size, valid only when replay protect enabled
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_set_replay_protect(void *priv, bool enabled,
+ u32 window)
+{
+ struct macsec_drv_data *drv = priv;
+
+ wpa_printf(MSG_DEBUG, "%s -> %s, %u", __func__,
+ enabled ? "TRUE" : "FALSE", window);
+
+ drv->replay_protect_set = true;
+ drv->replay_protect = enabled;
+ if (enabled)
+ drv->replay_window = window;
+
+ return try_commit(drv);
+}
+
+
+/**
+ * macsec_drv_set_offload - Set offload status
+ * @priv: Private driver interface data
+ * @offload: 0 = MACSEC_OFFLOAD_OFF
+ * 1 = MACSEC_OFFLOAD_PHY
+ * 2 = MACSEC_OFFLOAD_MAC
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_set_offload(void *priv, u8 offload)
+{
+#ifdef LIBNL_HAS_OFFLOAD
+ struct macsec_drv_data *drv = priv;
+
+ wpa_printf(MSG_DEBUG, "%s -> %02" PRIx8, __func__, offload);
+
+ drv->offload_set = true;
+ drv->offload = offload;
+
+ return try_commit(drv);
+#else /* LIBNL_HAS_OFFLOAD */
+ if (offload == 0)
+ return 0;
+ wpa_printf(MSG_INFO,
+ "%s: libnl version does not include support for MACsec offload",
+ __func__);
+ return -1;
+#endif /* LIBNL_HAS_OFFLOAD */
+}
+
+
+/**
+ * macsec_drv_set_current_cipher_suite - Set current cipher suite
+ * @priv: Private driver interface data
+ * @cs: EUI64 identifier
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_set_current_cipher_suite(void *priv, u64 cs)
+{
+ struct macsec_drv_data *drv = priv;
+
+ wpa_printf(MSG_DEBUG, "%s -> %016" PRIx64, __func__, cs);
+
+ drv->cipher_suite_set = true;
+ drv->cipher_suite = cs;
+
+ return try_commit(drv);
+}
+
+
+/**
+ * macsec_drv_enable_controlled_port - Set controlled port status
+ * @priv: Private driver interface data
+ * @enabled: true = controlled port enabled
+ * false = controlled port disabled
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_enable_controlled_port(void *priv, bool enabled)
+{
+ struct macsec_drv_data *drv = priv;
+
+ wpa_printf(MSG_DEBUG, "%s -> %s", __func__, enabled ? "TRUE" : "FALSE");
+
+ drv->controlled_port_enabled = enabled;
+ drv->controlled_port_enabled_set = true;
+
+ return try_commit(drv);
+}
+
+
+static struct nla_policy sa_policy[MACSEC_SA_ATTR_MAX + 1] = {
+ [MACSEC_SA_ATTR_AN] = { .type = NLA_U8 },
+ [MACSEC_SA_ATTR_ACTIVE] = { .type = NLA_U8 },
+ [MACSEC_SA_ATTR_PN] = { .type = NLA_U32 },
+ [MACSEC_SA_ATTR_KEYID] = { .type = NLA_BINARY },
+};
+
+static struct nla_policy sc_policy[MACSEC_RXSC_ATTR_MAX + 1] = {
+ [MACSEC_RXSC_ATTR_SCI] = { .type = NLA_U64 },
+ [MACSEC_RXSC_ATTR_ACTIVE] = { .type = NLA_U8 },
+ [MACSEC_RXSC_ATTR_SA_LIST] = { .type = NLA_NESTED },
+};
+
+static struct nla_policy main_policy[MACSEC_ATTR_MAX + 1] = {
+ [MACSEC_ATTR_IFINDEX] = { .type = NLA_U32 },
+ [MACSEC_ATTR_SECY] = { .type = NLA_NESTED },
+ [MACSEC_ATTR_TXSA_LIST] = { .type = NLA_NESTED },
+ [MACSEC_ATTR_RXSC_LIST] = { .type = NLA_NESTED },
+};
+
+static int dump_callback(struct nl_msg *msg, void *argp)
+{
+ struct nlmsghdr *ret_hdr = nlmsg_hdr(msg);
+ struct nlattr *tb_msg[MACSEC_ATTR_MAX + 1];
+ struct cb_arg *arg = (struct cb_arg *) argp;
+ struct genlmsghdr *gnlh = (struct genlmsghdr *) nlmsg_data(ret_hdr);
+ int err;
+
+ if (ret_hdr->nlmsg_type != arg->drv->ctx.macsec_genl_id)
+ return 0;
+
+ err = nla_parse(tb_msg, MACSEC_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), main_policy);
+ if (err < 0)
+ return 0;
+
+ if (!tb_msg[MACSEC_ATTR_IFINDEX])
+ return 0;
+
+ if (nla_get_u32(tb_msg[MACSEC_ATTR_IFINDEX]) != (u32) arg->ifindex)
+ return 0;
+
+ if (arg->txsa < 4 && !tb_msg[MACSEC_ATTR_TXSA_LIST]) {
+ return 0;
+ } else if (arg->txsa < 4) {
+ struct nlattr *nla;
+ int rem;
+
+ nla_for_each_nested(nla, tb_msg[MACSEC_ATTR_TXSA_LIST], rem) {
+ struct nlattr *tb[MACSEC_SA_ATTR_MAX + 1];
+
+ err = nla_parse_nested(tb, MACSEC_SA_ATTR_MAX, nla,
+ sa_policy);
+ if (err < 0)
+ continue;
+ if (!tb[MACSEC_SA_ATTR_AN])
+ continue;
+ if (nla_get_u8(tb[MACSEC_SA_ATTR_AN]) != arg->txsa)
+ continue;
+ if (!tb[MACSEC_SA_ATTR_PN])
+ return 0;
+ *arg->pn = nla_get_u32(tb[MACSEC_SA_ATTR_PN]);
+ return 0;
+ }
+
+ return 0;
+ }
+
+ if (arg->rxsci == UNUSED_SCI)
+ return 0;
+
+ if (tb_msg[MACSEC_ATTR_RXSC_LIST]) {
+ struct nlattr *nla;
+ int rem;
+
+ nla_for_each_nested(nla, tb_msg[MACSEC_ATTR_RXSC_LIST], rem) {
+ struct nlattr *tb[MACSEC_RXSC_ATTR_MAX + 1];
+
+ err = nla_parse_nested(tb, MACSEC_RXSC_ATTR_MAX, nla,
+ sc_policy);
+ if (err < 0)
+ return 0;
+ if (!tb[MACSEC_RXSC_ATTR_SCI])
+ continue;
+ if (nla_get_u64(tb[MACSEC_RXSC_ATTR_SCI]) != arg->rxsci)
+ continue;
+ if (!tb[MACSEC_RXSC_ATTR_SA_LIST])
+ return 0;
+
+ nla_for_each_nested(nla, tb[MACSEC_RXSC_ATTR_SA_LIST],
+ rem) {
+ struct nlattr *tb_sa[MACSEC_SA_ATTR_MAX + 1];
+
+ err = nla_parse_nested(tb_sa,
+ MACSEC_SA_ATTR_MAX, nla,
+ sa_policy);
+ if (err < 0)
+ continue;
+ if (!tb_sa[MACSEC_SA_ATTR_AN])
+ continue;
+ if (nla_get_u8(tb_sa[MACSEC_SA_ATTR_AN]) !=
+ arg->rxsa)
+ continue;
+ if (!tb_sa[MACSEC_SA_ATTR_PN])
+ return 0;
+ *arg->pn =
+ nla_get_u32(tb_sa[MACSEC_SA_ATTR_PN]);
+
+ return 0;
+ }
+
+ return 0;
+ }
+
+ return 0;
+ }
+
+ return 0;
+}
+
+
+static int nl_send_recv(struct nl_sock *sk, struct nl_msg *msg)
+{
+ int ret;
+
+ ret = nl_send_auto_complete(sk, msg);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, DRV_PREFIX "%s: failed to send: %d (%s)",
+ __func__, ret, nl_geterror(-ret));
+ return ret;
+ }
+
+ ret = nl_recvmsgs_default(sk);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, DRV_PREFIX "%s: failed to recv: %d (%s)",
+ __func__, ret, nl_geterror(-ret));
+ }
+
+ return ret;
+}
+
+
+static int do_dump(struct macsec_drv_data *drv, u8 txsa, u64 rxsci, u8 rxsa,
+ u32 *pn)
+{
+ struct macsec_genl_ctx *ctx = &drv->ctx;
+ struct nl_msg *msg;
+ int ret = 1;
+
+ ctx->cb_arg.ifindex = drv->ifi;
+ ctx->cb_arg.rxsci = rxsci;
+ ctx->cb_arg.rxsa = rxsa;
+ ctx->cb_arg.txsa = txsa;
+ ctx->cb_arg.pn = pn;
+
+ msg = nlmsg_alloc();
+ if (!msg) {
+ wpa_printf(MSG_ERROR, DRV_PREFIX "%s: failed to alloc message",
+ __func__);
+ return 1;
+ }
+
+ if (!genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, ctx->macsec_genl_id, 0,
+ NLM_F_DUMP, MACSEC_CMD_GET_TXSC, 0)) {
+ wpa_printf(MSG_ERROR, DRV_PREFIX "%s: failed to put header",
+ __func__);
+ goto out_free_msg;
+ }
+
+ ret = nl_send_recv(ctx->sk, msg);
+ if (ret < 0)
+ wpa_printf(MSG_ERROR,
+ DRV_PREFIX "failed to communicate: %d (%s)",
+ ret, nl_geterror(-ret));
+
+ ctx->cb_arg.pn = NULL;
+
+out_free_msg:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+/**
+ * macsec_drv_get_receive_lowest_pn - Get receive lowest PN
+ * @priv: Private driver interface data
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_get_receive_lowest_pn(void *priv, struct receive_sa *sa)
+{
+ struct macsec_drv_data *drv = priv;
+ int err;
+
+ wpa_printf(MSG_DEBUG, DRV_PREFIX "%s", __func__);
+
+ err = do_dump(drv, 0xff, mka_sci_u64(&sa->sc->sci), sa->an,
+ &sa->lowest_pn);
+ wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: result %d", __func__,
+ sa->lowest_pn);
+
+ return err;
+}
+
+
+/**
+ * macsec_drv_set_receive_lowest_pn - Set receive lowest PN
+ * @priv: Private driver interface data
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_set_receive_lowest_pn(void *priv, struct receive_sa *sa)
+{
+ struct macsec_drv_data *drv = priv;
+ struct macsec_genl_ctx *ctx = &drv->ctx;
+ struct nl_msg *msg;
+ struct nlattr *nest;
+ int ret = -1;
+
+ wpa_printf(MSG_DEBUG,
+ DRV_PREFIX "%s: set_receive_lowest_pn -> %d: %d",
+ drv->ifname, sa->an, sa->next_pn);
+
+ msg = msg_prepare(MACSEC_CMD_UPD_RXSA, ctx, drv->ifi);
+ if (!msg)
+ return ret;
+
+ if (nla_put_rxsc_config(msg, mka_sci_u64(&sa->sc->sci)))
+ goto nla_put_failure;
+
+ nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
+ if (!nest)
+ goto nla_put_failure;
+
+ NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an);
+ NLA_PUT_U32(msg, MACSEC_SA_ATTR_PN, sa->next_pn);
+
+ nla_nest_end(msg, nest);
+
+ ret = nl_send_recv(ctx->sk, msg);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR,
+ DRV_PREFIX "failed to communicate: %d (%s)",
+ ret, nl_geterror(-ret));
+ }
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+/**
+ * macsec_drv_get_transmit_next_pn - Get transmit next PN
+ * @priv: Private driver interface data
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_get_transmit_next_pn(void *priv, struct transmit_sa *sa)
+{
+ struct macsec_drv_data *drv = priv;
+ int err;
+
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+
+ err = do_dump(drv, sa->an, UNUSED_SCI, 0xff, &sa->next_pn);
+ wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: err %d result %d", __func__, err,
+ sa->next_pn);
+ return err;
+}
+
+
+/**
+ * macsec_drv_set_transmit_next_pn - Set transmit next pn
+ * @priv: Private driver interface data
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_set_transmit_next_pn(void *priv, struct transmit_sa *sa)
+{
+ struct macsec_drv_data *drv = priv;
+ struct macsec_genl_ctx *ctx = &drv->ctx;
+ struct nl_msg *msg;
+ struct nlattr *nest;
+ int ret = -1;
+
+ wpa_printf(MSG_DEBUG, "%s -> %d: %d", __func__, sa->an, sa->next_pn);
+
+ msg = msg_prepare(MACSEC_CMD_UPD_TXSA, ctx, drv->ifi);
+ if (!msg)
+ return ret;
+
+ nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
+ if (!nest)
+ goto nla_put_failure;
+
+ NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an);
+ NLA_PUT_U32(msg, MACSEC_SA_ATTR_PN, sa->next_pn);
+
+ nla_nest_end(msg, nest);
+
+ ret = nl_send_recv(ctx->sk, msg);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR,
+ DRV_PREFIX "failed to communicate: %d (%s)",
+ ret, nl_geterror(-ret));
+ }
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+#define SCISTR MACSTR "::%hx"
+#define SCI2STR(addr, port) MAC2STR(addr), htons(port)
+
+/**
+ * macsec_drv_create_receive_sc - Create secure channel for receiving
+ * @priv: Private driver interface data
+ * @sc: secure channel
+ * @sci_addr: secure channel identifier - address
+ * @sci_port: secure channel identifier - port
+ * @conf_offset: confidentiality offset (0, 30, or 50)
+ * @validation: frame validation policy (0 = Disabled, 1 = Checked,
+ * 2 = Strict)
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_create_receive_sc(void *priv, struct receive_sc *sc,
+ unsigned int conf_offset,
+ int validation)
+{
+ struct macsec_drv_data *drv = priv;
+ struct macsec_genl_ctx *ctx = &drv->ctx;
+ struct nl_msg *msg;
+ int ret = -1;
+
+ wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: create_receive_sc -> " SCISTR
+ " (conf_offset=%u validation=%d)",
+ drv->ifname, SCI2STR(sc->sci.addr, sc->sci.port),
+ conf_offset, validation);
+
+ msg = msg_prepare(MACSEC_CMD_ADD_RXSC, ctx, drv->ifi);
+ if (!msg)
+ return ret;
+
+ if (nla_put_rxsc_config(msg, mka_sci_u64(&sc->sci)))
+ goto nla_put_failure;
+
+ ret = nl_send_recv(ctx->sk, msg);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR,
+ DRV_PREFIX "%s: failed to communicate: %d (%s)",
+ __func__, ret, nl_geterror(-ret));
+ }
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+/**
+ * macsec_drv_delete_receive_sc - Delete secure connection for receiving
+ * @priv: private driver interface data from init()
+ * @sc: secure channel
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_delete_receive_sc(void *priv, struct receive_sc *sc)
+{
+ struct macsec_drv_data *drv = priv;
+ struct macsec_genl_ctx *ctx = &drv->ctx;
+ struct nl_msg *msg;
+ int ret = -1;
+
+ wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: delete_receive_sc -> " SCISTR,
+ drv->ifname, SCI2STR(sc->sci.addr, sc->sci.port));
+
+ msg = msg_prepare(MACSEC_CMD_DEL_RXSC, ctx, drv->ifi);
+ if (!msg)
+ return ret;
+
+ if (nla_put_rxsc_config(msg, mka_sci_u64(&sc->sci)))
+ goto nla_put_failure;
+
+ ret = nl_send_recv(ctx->sk, msg);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR,
+ DRV_PREFIX "%s: failed to communicate: %d (%s)",
+ __func__, ret, nl_geterror(-ret));
+ }
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+/**
+ * macsec_drv_create_receive_sa - Create secure association for receive
+ * @priv: private driver interface data from init()
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_create_receive_sa(void *priv, struct receive_sa *sa)
+{
+ struct macsec_drv_data *drv = priv;
+ struct macsec_genl_ctx *ctx = &drv->ctx;
+ struct nl_msg *msg;
+ struct nlattr *nest;
+ int ret = -1;
+
+ wpa_printf(MSG_DEBUG,
+ DRV_PREFIX "%s: create_receive_sa -> %d on " SCISTR
+ " (enable_receive=%d next_pn=%u)",
+ drv->ifname, sa->an,
+ SCI2STR(sa->sc->sci.addr, sa->sc->sci.port),
+ sa->enable_receive, sa->next_pn);
+ wpa_hexdump(MSG_DEBUG, DRV_PREFIX "SA keyid",
+ &sa->pkey->key_identifier,
+ sizeof(sa->pkey->key_identifier));
+ wpa_hexdump_key(MSG_DEBUG, DRV_PREFIX "SA key",
+ sa->pkey->key, sa->pkey->key_len);
+
+ msg = msg_prepare(MACSEC_CMD_ADD_RXSA, ctx, drv->ifi);
+ if (!msg)
+ return ret;
+
+ if (nla_put_rxsc_config(msg, mka_sci_u64(&sa->sc->sci)))
+ goto nla_put_failure;
+
+ nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
+ if (!nest)
+ goto nla_put_failure;
+
+ NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an);
+ NLA_PUT_U8(msg, MACSEC_SA_ATTR_ACTIVE, sa->enable_receive);
+ NLA_PUT_U32(msg, MACSEC_SA_ATTR_PN, sa->next_pn);
+ NLA_PUT(msg, MACSEC_SA_ATTR_KEYID, sizeof(sa->pkey->key_identifier),
+ &sa->pkey->key_identifier);
+ NLA_PUT(msg, MACSEC_SA_ATTR_KEY, sa->pkey->key_len, sa->pkey->key);
+
+ nla_nest_end(msg, nest);
+
+ ret = nl_send_recv(ctx->sk, msg);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR,
+ DRV_PREFIX "%s: failed to communicate: %d (%s)",
+ __func__, ret, nl_geterror(-ret));
+ }
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+/**
+ * macsec_drv_delete_receive_sa - Delete secure association for receive
+ * @priv: private driver interface data from init()
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_delete_receive_sa(void *priv, struct receive_sa *sa)
+{
+ struct macsec_drv_data *drv = priv;
+ struct macsec_genl_ctx *ctx = &drv->ctx;
+ struct nl_msg *msg;
+ struct nlattr *nest;
+ int ret = -1;
+
+ wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: delete_receive_sa -> %d on "
+ SCISTR, drv->ifname, sa->an,
+ SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
+
+ msg = msg_prepare(MACSEC_CMD_DEL_RXSA, ctx, drv->ifi);
+ if (!msg)
+ return ret;
+
+ if (nla_put_rxsc_config(msg, mka_sci_u64(&sa->sc->sci)))
+ goto nla_put_failure;
+
+ nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
+ if (!nest)
+ goto nla_put_failure;
+
+ NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an);
+
+ nla_nest_end(msg, nest);
+
+ ret = nl_send_recv(ctx->sk, msg);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR,
+ DRV_PREFIX "%s: failed to communicate: %d (%s)",
+ __func__, ret, nl_geterror(-ret));
+ }
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+static int set_active_rx_sa(const struct macsec_genl_ctx *ctx, int ifindex,
+ u64 sci, unsigned char an, bool state)
+{
+ struct nl_msg *msg;
+ struct nlattr *nest;
+ int ret = -1;
+
+ msg = msg_prepare(MACSEC_CMD_UPD_RXSA, ctx, ifindex);
+ if (!msg)
+ return ret;
+
+ if (nla_put_rxsc_config(msg, sci))
+ goto nla_put_failure;
+
+ nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
+ if (!nest)
+ goto nla_put_failure;
+
+ NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, an);
+ NLA_PUT_U8(msg, MACSEC_SA_ATTR_ACTIVE, !!state);
+
+ nla_nest_end(msg, nest);
+
+ ret = nl_send_recv(ctx->sk, msg);
+ if (ret < 0)
+ wpa_printf(MSG_ERROR,
+ DRV_PREFIX "%s: failed to communicate: %d (%s)",
+ __func__, ret, nl_geterror(-ret));
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+/**
+ * macsec_drv_enable_receive_sa - Enable the SA for receive
+ * @priv: private driver interface data from init()
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_enable_receive_sa(void *priv, struct receive_sa *sa)
+{
+ struct macsec_drv_data *drv = priv;
+ struct macsec_genl_ctx *ctx = &drv->ctx;
+
+ wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: enable_receive_sa -> %d on "
+ SCISTR, drv->ifname, sa->an,
+ SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
+
+ return set_active_rx_sa(ctx, drv->ifi, mka_sci_u64(&sa->sc->sci),
+ sa->an, true);
+}
+
+
+/**
+ * macsec_drv_disable_receive_sa - Disable SA for receive
+ * @priv: private driver interface data from init()
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_disable_receive_sa(void *priv, struct receive_sa *sa)
+{
+ struct macsec_drv_data *drv = priv;
+ struct macsec_genl_ctx *ctx = &drv->ctx;
+
+ wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: disable_receive_sa -> %d on "
+ SCISTR, drv->ifname, sa->an,
+ SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
+
+ return set_active_rx_sa(ctx, drv->ifi, mka_sci_u64(&sa->sc->sci),
+ sa->an, false);
+}
+
+
+static struct rtnl_link * lookup_sc(struct nl_cache *cache, int parent, u64 sci,
+ u64 cs)
+{
+ struct rtnl_link *needle;
+ void *match;
+
+ needle = rtnl_link_macsec_alloc();
+ if (!needle)
+ return NULL;
+
+ rtnl_link_set_link(needle, parent);
+ rtnl_link_macsec_set_sci(needle, sci);
+ if (cs)
+ rtnl_link_macsec_set_cipher_suite(needle, cs);
+
+ match = nl_cache_find(cache, (struct nl_object *) needle);
+ rtnl_link_put(needle);
+
+ return (struct rtnl_link *) match;
+}
+
+
+/**
+ * macsec_drv_create_transmit_sc - Create secure connection for transmit
+ * @priv: private driver interface data from init()
+ * @sc: secure channel
+ * @conf_offset: confidentiality offset
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_create_transmit_sc(
+ void *priv, struct transmit_sc *sc,
+ unsigned int conf_offset)
+{
+ struct macsec_drv_data *drv = priv;
+ struct rtnl_link *link;
+ char *ifname;
+ u64 sci;
+ int err;
+ u64 cs = 0;
+
+ wpa_printf(MSG_DEBUG, DRV_PREFIX
+ "%s: create_transmit_sc -> " SCISTR " (conf_offset=%d)",
+ drv->common.ifname, SCI2STR(sc->sci.addr, sc->sci.port),
+ conf_offset);
+
+ if (!drv->sk) {
+ wpa_printf(MSG_ERROR, DRV_PREFIX "NULL rtnl socket");
+ return -1;
+ }
+
+ link = rtnl_link_macsec_alloc();
+ if (!link) {
+ wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't allocate link");
+ return -1;
+ }
+
+ rtnl_link_set_link(link, drv->parent_ifi);
+
+ sci = mka_sci_u64(&sc->sci);
+ rtnl_link_macsec_set_sci(link, sci);
+
+ drv->created_link = true;
+
+ if (drv->cipher_suite_set) {
+ cs = drv->cipher_suite;
+ drv->cipher_suite_set = false;
+ rtnl_link_macsec_set_cipher_suite(link, cs);
+ }
+
+ err = rtnl_link_add(drv->sk, link, NLM_F_CREATE);
+ if (err == -NLE_BUSY) {
+ wpa_printf(MSG_INFO,
+ DRV_PREFIX "link already exists, using it");
+ drv->created_link = false;
+ } else if (err < 0) {
+ rtnl_link_put(link);
+ wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't create link: err %d",
+ err);
+ return err;
+ }
+
+ rtnl_link_put(link);
+
+ nl_cache_refill(drv->sk, drv->link_cache);
+ link = lookup_sc(drv->link_cache, drv->parent_ifi, sci, cs);
+ if (!link) {
+ wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't find link");
+ return -1;
+ }
+
+ drv->ifi = rtnl_link_get_ifindex(link);
+ ifname = rtnl_link_get_name(link);
+ wpa_printf(MSG_DEBUG,
+ DRV_PREFIX "%s: create_transmit_sc: ifi=%d ifname=%s",
+ drv->common.ifname, drv->ifi, ifname);
+ os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+ rtnl_link_put(link);
+
+ drv->link = rtnl_link_macsec_alloc();
+ if (!drv->link) {
+ wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't allocate link");
+ return -1;
+ }
+
+ rtnl_link_set_name(drv->link, drv->ifname);
+
+ /* In case some settings have already been done but we couldn't apply
+ * them. */
+ return try_commit(drv);
+}
+
+
+/**
+ * macsec_drv_delete_transmit_sc - Delete secure connection for transmit
+ * @priv: private driver interface data from init()
+ * @sc: secure channel
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_delete_transmit_sc(void *priv, struct transmit_sc *sc)
+{
+ struct macsec_drv_data *drv = priv;
+ int err;
+
+ wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: delete_transmit_sc -> " SCISTR,
+ drv->ifname, SCI2STR(sc->sci.addr, sc->sci.port));
+
+ if (!drv->sk)
+ return 0;
+
+ if (!drv->created_link) {
+ rtnl_link_put(drv->link);
+ drv->link = NULL;
+ wpa_printf(MSG_DEBUG, DRV_PREFIX
+ "we didn't create the link, leave it alone");
+ return 0;
+ }
+
+ err = rtnl_link_delete(drv->sk, drv->link);
+ if (err < 0)
+ wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't delete link");
+ rtnl_link_put(drv->link);
+ drv->link = NULL;
+
+ return err;
+}
+
+
+/**
+ * macsec_drv_create_transmit_sa - Create secure association for transmit
+ * @priv: private driver interface data from init()
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_create_transmit_sa(void *priv, struct transmit_sa *sa)
+{
+ struct macsec_drv_data *drv = priv;
+ struct macsec_genl_ctx *ctx = &drv->ctx;
+ struct nl_msg *msg;
+ struct nlattr *nest;
+ int ret = -1;
+
+ wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: create_transmit_sa -> %d on "
+ SCISTR " (enable_transmit=%d next_pn=%u)",
+ drv->ifname, sa->an,
+ SCI2STR(sa->sc->sci.addr, sa->sc->sci.port),
+ sa->enable_transmit, sa->next_pn);
+ wpa_hexdump(MSG_DEBUG, DRV_PREFIX "SA keyid",
+ &sa->pkey->key_identifier,
+ sizeof(sa->pkey->key_identifier));
+ wpa_hexdump_key(MSG_DEBUG, DRV_PREFIX "SA key",
+ sa->pkey->key, sa->pkey->key_len);
+
+ msg = msg_prepare(MACSEC_CMD_ADD_TXSA, ctx, drv->ifi);
+ if (!msg)
+ return ret;
+
+ nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
+ if (!nest)
+ goto nla_put_failure;
+
+ NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an);
+ NLA_PUT_U32(msg, MACSEC_SA_ATTR_PN, sa->next_pn);
+ NLA_PUT(msg, MACSEC_SA_ATTR_KEYID, sizeof(sa->pkey->key_identifier),
+ &sa->pkey->key_identifier);
+ NLA_PUT(msg, MACSEC_SA_ATTR_KEY, sa->pkey->key_len, sa->pkey->key);
+ NLA_PUT_U8(msg, MACSEC_SA_ATTR_ACTIVE, sa->enable_transmit);
+
+ nla_nest_end(msg, nest);
+
+ ret = nl_send_recv(ctx->sk, msg);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR,
+ DRV_PREFIX "%s: failed to communicate: %d (%s)",
+ __func__, ret, nl_geterror(-ret));
+ }
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+/**
+ * macsec_drv_delete_transmit_sa - Delete secure association for transmit
+ * @priv: private driver interface data from init()
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_delete_transmit_sa(void *priv, struct transmit_sa *sa)
+{
+ struct macsec_drv_data *drv = priv;
+ struct macsec_genl_ctx *ctx = &drv->ctx;
+ struct nl_msg *msg;
+ struct nlattr *nest;
+ int ret = -1;
+
+ wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: delete_transmit_sa -> %d on "
+ SCISTR, drv->ifname, sa->an,
+ SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
+
+ msg = msg_prepare(MACSEC_CMD_DEL_TXSA, ctx, drv->ifi);
+ if (!msg)
+ return ret;
+
+ nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
+ if (!nest)
+ goto nla_put_failure;
+
+ NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an);
+
+ nla_nest_end(msg, nest);
+
+ ret = nl_send_recv(ctx->sk, msg);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR,
+ DRV_PREFIX "%s: failed to communicate: %d (%s)",
+ __func__, ret, nl_geterror(-ret));
+ }
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+static int set_active_tx_sa(const struct macsec_genl_ctx *ctx, int ifindex,
+ unsigned char an, bool state)
+{
+ struct nl_msg *msg;
+ struct nlattr *nest;
+ int ret = -1;
+
+ msg = msg_prepare(MACSEC_CMD_UPD_TXSA, ctx, ifindex);
+ if (!msg)
+ return ret;
+
+ nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
+ if (!nest)
+ goto nla_put_failure;
+
+ NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, an);
+ NLA_PUT_U8(msg, MACSEC_SA_ATTR_ACTIVE, !!state);
+
+ nla_nest_end(msg, nest);
+
+ ret = nl_send_recv(ctx->sk, msg);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR,
+ DRV_PREFIX "%s: failed to communicate: %d (%s)",
+ __func__, ret, nl_geterror(-ret));
+ }
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+/**
+ * macsec_drv_enable_transmit_sa - Enable SA for transmit
+ * @priv: private driver interface data from init()
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_enable_transmit_sa(void *priv, struct transmit_sa *sa)
+{
+ struct macsec_drv_data *drv = priv;
+ struct macsec_genl_ctx *ctx = &drv->ctx;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: enable_transmit_sa -> %d on "
+ SCISTR, drv->ifname, sa->an,
+ SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
+
+ ret = set_active_tx_sa(ctx, drv->ifi, sa->an, true);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, DRV_PREFIX "failed to enable txsa");
+ return ret;
+ }
+
+ drv->encoding_sa_set = true;
+ drv->encoding_sa = sa->an;
+
+ return try_commit(drv);
+}
+
+
+/**
+ * macsec_drv_disable_transmit_sa - Disable SA for transmit
+ * @priv: private driver interface data from init()
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_disable_transmit_sa(void *priv, struct transmit_sa *sa)
+{
+ struct macsec_drv_data *drv = priv;
+ struct macsec_genl_ctx *ctx = &drv->ctx;
+
+ wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: disable_transmit_sa -> %d on "
+ SCISTR, drv->ifname, sa->an,
+ SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
+
+ return set_active_tx_sa(ctx, drv->ifi, sa->an, false);
+}
+
+
+static int macsec_drv_status(void *priv, char *buf, size_t buflen)
+{
+ struct macsec_drv_data *drv = priv;
+ int res;
+ char *pos, *end;
+
+ pos = buf;
+ end = buf + buflen;
+
+ res = os_snprintf(pos, end - pos,
+ "ifname=%s\n"
+ "ifi=%d\n"
+ "parent_ifname=%s\n"
+ "parent_ifi=%d\n",
+ drv->common.ifname, drv->ifi,
+ drv->ifname, drv->parent_ifi);
+ if (os_snprintf_error(end - pos, res))
+ return pos - buf;
+ pos += res;
+
+ return pos - buf;
+}
+
+
+#ifdef __linux__
+
+static void macsec_drv_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, "%s: too short (%lu)",
+ __func__, (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 macsec_drv_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) {
+ wpa_printf(MSG_ERROR, "macsec_linux: recv: %s",
+ strerror(errno));
+ return;
+ }
+
+ macsec_drv_handle_data(eloop_ctx, buf, len);
+}
+
+#endif /* __linux__ */
+
+
+static int macsec_drv_init_sockets(struct macsec_drv_data *drv, u8 *own_addr)
+{
+#ifdef __linux__
+ struct ifreq ifr;
+ struct sockaddr_ll addr;
+
+ drv->common.sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PAE));
+ if (drv->common.sock < 0) {
+ wpa_printf(MSG_ERROR, "socket[PF_PACKET,SOCK_RAW]: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ if (eloop_register_read_sock(drv->common.sock, macsec_drv_handle_read,
+ drv->common.ctx, NULL)) {
+ wpa_printf(MSG_INFO, "Could not register read socket");
+ return -1;
+ }
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, drv->common.ifname, sizeof(ifr.ifr_name));
+ if (ioctl(drv->common.sock, SIOCGIFINDEX, &ifr) != 0) {
+ wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s",
+ strerror(errno));
+ 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->common.sock, (struct sockaddr *) &addr, sizeof(addr)) < 0)
+ {
+ wpa_printf(MSG_ERROR, "bind: %s", strerror(errno));
+ return -1;
+ }
+
+ /* filter multicast address */
+ if (wired_multicast_membership(drv->common.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->common.ifname, sizeof(ifr.ifr_name));
+ if (ioctl(drv->common.sock, SIOCGIFHWADDR, &ifr) != 0) {
+ wpa_printf(MSG_ERROR, "ioctl(SIOCGIFHWADDR): %s",
+ strerror(errno));
+ return -1;
+ }
+
+ if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
+ wpa_printf(MSG_INFO, "Invalid HW-addr family 0x%04x",
+ ifr.ifr_hwaddr.sa_family);
+ return -1;
+ }
+ os_memcpy(own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+
+ return 0;
+#else /* __linux__ */
+ return -1;
+#endif /* __linux__ */
+}
+
+
+static void * macsec_drv_hapd_init(struct hostapd_data *hapd,
+ struct wpa_init_params *params)
+{
+ struct macsec_drv_data *drv;
+
+ drv = os_zalloc(sizeof(struct macsec_drv_data));
+ if (drv == NULL) {
+ wpa_printf(MSG_INFO,
+ "Could not allocate memory for wired driver data");
+ return NULL;
+ }
+
+ drv->common.ctx = hapd;
+ os_strlcpy(drv->common.ifname, params->ifname,
+ sizeof(drv->common.ifname));
+ drv->use_pae_group_addr = params->use_pae_group_addr;
+
+ if (macsec_drv_init_sockets(drv, params->own_addr)) {
+ os_free(drv);
+ return NULL;
+ }
+
+ return drv;
+}
+
+
+static void macsec_drv_hapd_deinit(void *priv)
+{
+ struct macsec_drv_data *drv = priv;
+
+ if (drv->common.sock >= 0) {
+ eloop_unregister_read_sock(drv->common.sock);
+ close(drv->common.sock);
+ }
+
+ os_free(drv);
+}
+
+
+static int macsec_drv_send_eapol(void *priv, const u8 *addr,
+ const u8 *data, size_t data_len, int encrypt,
+ const u8 *own_addr, u32 flags, int link_id)
+{
+ struct macsec_drv_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) {
+ wpa_printf(MSG_INFO,
+ "%s: malloc() failed (len=%lu)",
+ __func__, (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->common.sock, (u8 *) hdr, len, 0);
+ os_free(hdr);
+
+ if (res < 0) {
+ wpa_printf(MSG_ERROR,
+ "%s: packet len: %lu - failed: send: %s",
+ __func__, (unsigned long) len, strerror(errno));
+ }
+
+ return res;
+}
+
+
+const struct wpa_driver_ops wpa_driver_macsec_linux_ops = {
+ .name = "macsec_linux",
+ .desc = "MACsec Ethernet driver for Linux",
+ .get_ssid = driver_wired_get_ssid,
+ .get_bssid = driver_wired_get_bssid,
+ .get_capa = driver_wired_get_capa,
+ .init = macsec_drv_wpa_init,
+ .deinit = macsec_drv_wpa_deinit,
+ .hapd_init = macsec_drv_hapd_init,
+ .hapd_deinit = macsec_drv_hapd_deinit,
+ .hapd_send_eapol = macsec_drv_send_eapol,
+
+ .macsec_init = macsec_drv_macsec_init,
+ .macsec_deinit = macsec_drv_macsec_deinit,
+ .macsec_get_capability = macsec_drv_get_capability,
+ .enable_protect_frames = macsec_drv_enable_protect_frames,
+ .enable_encrypt = macsec_drv_enable_encrypt,
+ .set_replay_protect = macsec_drv_set_replay_protect,
+ .set_offload = macsec_drv_set_offload,
+ .set_current_cipher_suite = macsec_drv_set_current_cipher_suite,
+ .enable_controlled_port = macsec_drv_enable_controlled_port,
+ .get_receive_lowest_pn = macsec_drv_get_receive_lowest_pn,
+ .set_receive_lowest_pn = macsec_drv_set_receive_lowest_pn,
+ .get_transmit_next_pn = macsec_drv_get_transmit_next_pn,
+ .set_transmit_next_pn = macsec_drv_set_transmit_next_pn,
+ .create_receive_sc = macsec_drv_create_receive_sc,
+ .delete_receive_sc = macsec_drv_delete_receive_sc,
+ .create_receive_sa = macsec_drv_create_receive_sa,
+ .delete_receive_sa = macsec_drv_delete_receive_sa,
+ .enable_receive_sa = macsec_drv_enable_receive_sa,
+ .disable_receive_sa = macsec_drv_disable_receive_sa,
+ .create_transmit_sc = macsec_drv_create_transmit_sc,
+ .delete_transmit_sc = macsec_drv_delete_transmit_sc,
+ .create_transmit_sa = macsec_drv_create_transmit_sa,
+ .delete_transmit_sa = macsec_drv_delete_transmit_sa,
+ .enable_transmit_sa = macsec_drv_enable_transmit_sa,
+ .disable_transmit_sa = macsec_drv_disable_transmit_sa,
+
+ .status = macsec_drv_status,
+};
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_macsec_qca.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_macsec_qca.c
new file mode 100644
index 0000000..58c48c2
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_macsec_qca.c
@@ -0,0 +1,1071 @@
+/*
+ * Wired Ethernet driver interface for QCA MACsec driver
+ * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004, Gunter Burchardt <tira@isx.de>
+ * Copyright (c) 2013-2014, Qualcomm Atheros, Inc.
+ * Copyright (c) 2019, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <inttypes.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>
+#include <net/if_media.h>
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) */
+#ifdef __sun__
+#include <sys/sockio.h>
+#endif /* __sun__ */
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/defs.h"
+#include "common/ieee802_1x_defs.h"
+#include "common/eapol_common.h"
+#include "pae/ieee802_1x_kay.h"
+#include "driver.h"
+#include "driver_wired_common.h"
+
+#include "nss_macsec_secy.h"
+#include "nss_macsec_secy_rx.h"
+#include "nss_macsec_secy_tx.h"
+
+#define MAXSC 16
+
+#define SAK_128_LEN 16
+#define SAK_256_LEN 32
+
+/* TCI field definition */
+#define TCI_ES 0x40
+#define TCI_SC 0x20
+#define TCI_SCB 0x10
+#define TCI_E 0x08
+#define TCI_C 0x04
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+struct channel_map {
+ struct ieee802_1x_mka_sci sci;
+};
+
+struct macsec_qca_data {
+ struct driver_wired_common_data common;
+
+ int use_pae_group_addr;
+ u32 secy_id;
+
+ /* shadow */
+ bool always_include_sci;
+ bool use_es;
+ bool use_scb;
+ bool protect_frames;
+ bool replay_protect;
+ u32 replay_window;
+
+ struct channel_map receive_channel_map[MAXSC];
+ struct channel_map transmit_channel_map[MAXSC];
+};
+
+
+static void __macsec_drv_init(struct macsec_qca_data *drv)
+{
+ int ret = 0;
+ fal_rx_ctl_filt_t rx_ctl_filt;
+ fal_tx_ctl_filt_t tx_ctl_filt;
+
+ wpa_printf(MSG_INFO, "%s: secy_id=%d", __func__, drv->secy_id);
+
+ /* Enable Secy and Let EAPoL bypass */
+ ret = nss_macsec_secy_en_set(drv->secy_id, true);
+ if (ret)
+ wpa_printf(MSG_ERROR, "nss_macsec_secy_en_set: FAIL");
+
+ ret = nss_macsec_secy_sc_sa_mapping_mode_set(drv->secy_id,
+ FAL_SC_SA_MAP_1_4);
+ if (ret)
+ wpa_printf(MSG_ERROR,
+ "nss_macsec_secy_sc_sa_mapping_mode_set: FAIL");
+
+ os_memset(&rx_ctl_filt, 0, sizeof(rx_ctl_filt));
+ rx_ctl_filt.bypass = 1;
+ rx_ctl_filt.match_type = IG_CTL_COMPARE_ETHER_TYPE;
+ rx_ctl_filt.match_mask = 0xffff;
+ rx_ctl_filt.ether_type_da_range = 0x888e;
+ ret = nss_macsec_secy_rx_ctl_filt_set(drv->secy_id, 0, &rx_ctl_filt);
+ if (ret)
+ wpa_printf(MSG_ERROR, "nss_macsec_secy_rx_ctl_filt_set: FAIL");
+
+ os_memset(&tx_ctl_filt, 0, sizeof(tx_ctl_filt));
+ tx_ctl_filt.bypass = 1;
+ tx_ctl_filt.match_type = EG_CTL_COMPARE_ETHER_TYPE;
+ tx_ctl_filt.match_mask = 0xffff;
+ tx_ctl_filt.ether_type_da_range = 0x888e;
+ ret = nss_macsec_secy_tx_ctl_filt_set(drv->secy_id, 0, &tx_ctl_filt);
+ if (ret)
+ wpa_printf(MSG_ERROR, "nss_macsec_secy_tx_ctl_filt_set: FAIL");
+}
+
+
+static void __macsec_drv_deinit(struct macsec_qca_data *drv)
+{
+ nss_macsec_secy_en_set(drv->secy_id, false);
+ nss_macsec_secy_rx_sc_del_all(drv->secy_id);
+ nss_macsec_secy_tx_sc_del_all(drv->secy_id);
+}
+
+
+#ifdef __linux__
+
+static void macsec_qca_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;
+
+ /* at least 6 bytes src macaddress, 6 bytes dst macaddress
+ * and 2 bytes ethertype
+ */
+ if (len < 14) {
+ wpa_printf(MSG_MSGDUMP,
+ "macsec_qca_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 macsec_qca_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) {
+ wpa_printf(MSG_ERROR, "macsec_qca: recv: %s", strerror(errno));
+ return;
+ }
+
+ macsec_qca_handle_data(eloop_ctx, buf, len);
+}
+
+#endif /* __linux__ */
+
+
+static int macsec_qca_init_sockets(struct macsec_qca_data *drv, u8 *own_addr)
+{
+#ifdef __linux__
+ struct ifreq ifr;
+ struct sockaddr_ll addr;
+
+ drv->common.sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PAE));
+ if (drv->common.sock < 0) {
+ wpa_printf(MSG_ERROR, "socket[PF_PACKET,SOCK_RAW]: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ if (eloop_register_read_sock(drv->common.sock, macsec_qca_handle_read,
+ drv->common.ctx, NULL)) {
+ wpa_printf(MSG_INFO, "Could not register read socket");
+ return -1;
+ }
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, drv->common.ifname, sizeof(ifr.ifr_name));
+ if (ioctl(drv->common.sock, SIOCGIFINDEX, &ifr) != 0) {
+ wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s",
+ strerror(errno));
+ 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->common.sock, (struct sockaddr *) &addr,
+ sizeof(addr)) < 0) {
+ wpa_printf(MSG_ERROR, "macsec_qca: bind: %s", strerror(errno));
+ return -1;
+ }
+
+ /* filter multicast address */
+ if (wired_multicast_membership(drv->common.sock, ifr.ifr_ifindex,
+ pae_group_addr, 1) < 0) {
+ wpa_printf(MSG_ERROR,
+ "macsec_qca_init_sockets: Failed to add multicast group membership");
+ return -1;
+ }
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, drv->common.ifname, sizeof(ifr.ifr_name));
+ if (ioctl(drv->common.sock, SIOCGIFHWADDR, &ifr) != 0) {
+ wpa_printf(MSG_ERROR, "ioctl(SIOCGIFHWADDR): %s",
+ strerror(errno));
+ return -1;
+ }
+
+ if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
+ wpa_printf(MSG_INFO, "Invalid HW-addr family 0x%04x",
+ ifr.ifr_hwaddr.sa_family);
+ return -1;
+ }
+ os_memcpy(own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+
+ return 0;
+#else /* __linux__ */
+ return -1;
+#endif /* __linux__ */
+}
+
+
+static int macsec_qca_secy_id_get(const char *ifname, u32 *secy_id)
+{
+#ifdef NSS_MACSEC_SECY_ID_GET_FUNC
+ /* Get secy id from nss macsec driver */
+ return nss_macsec_secy_id_get((u8 *) ifname, secy_id);
+#else /* NSS_MACSEC_SECY_ID_GET_FUNC */
+ /* Board specific settings */
+ if (os_strcmp(ifname, "eth2") == 0) {
+ *secy_id = 1;
+ } else if (os_strcmp(ifname, "eth3") == 0) {
+ *secy_id = 2;
+ } else if (os_strcmp(ifname, "eth4") == 0 ||
+ os_strcmp(ifname, "eth0") == 0) {
+ *secy_id = 0;
+ } else if (os_strcmp(ifname, "eth5") == 0 ||
+ os_strcmp(ifname, "eth1") == 0) {
+ *secy_id = 1;
+ } else {
+ *secy_id = -1;
+ return -1;
+ }
+
+ return 0;
+#endif /* NSS_MACSEC_SECY_ID_GET_FUNC */
+}
+
+
+static void * macsec_qca_init(void *ctx, const char *ifname)
+{
+ struct macsec_qca_data *drv;
+
+ drv = os_zalloc(sizeof(*drv));
+ if (drv == NULL)
+ return NULL;
+
+ if (macsec_qca_secy_id_get(ifname, &drv->secy_id)) {
+ wpa_printf(MSG_ERROR,
+ "macsec_qca: Failed to get secy_id for %s", ifname);
+ os_free(drv);
+ return NULL;
+ }
+
+ if (driver_wired_init_common(&drv->common, ifname, ctx) < 0) {
+ os_free(drv);
+ return NULL;
+ }
+
+ return drv;
+}
+
+
+static void macsec_qca_deinit(void *priv)
+{
+ struct macsec_qca_data *drv = priv;
+
+ driver_wired_deinit_common(&drv->common);
+ os_free(drv);
+}
+
+
+static void * macsec_qca_hapd_init(struct hostapd_data *hapd,
+ struct wpa_init_params *params)
+{
+ struct macsec_qca_data *drv;
+
+ drv = os_zalloc(sizeof(struct macsec_qca_data));
+ if (!drv) {
+ wpa_printf(MSG_INFO,
+ "Could not allocate memory for macsec_qca driver data");
+ return NULL;
+ }
+
+ if (macsec_qca_secy_id_get(params->ifname, &drv->secy_id)) {
+ wpa_printf(MSG_ERROR,
+ "macsec_qca: Failed to get secy_id for %s",
+ params->ifname);
+ os_free(drv);
+ return NULL;
+ }
+
+ drv->common.ctx = hapd;
+ os_strlcpy(drv->common.ifname, params->ifname,
+ sizeof(drv->common.ifname));
+ drv->use_pae_group_addr = params->use_pae_group_addr;
+
+ if (macsec_qca_init_sockets(drv, params->own_addr)) {
+ os_free(drv);
+ return NULL;
+ }
+
+ return drv;
+}
+
+
+static void macsec_qca_hapd_deinit(void *priv)
+{
+ struct macsec_qca_data *drv = priv;
+
+ if (drv->common.sock >= 0) {
+ eloop_unregister_read_sock(drv->common.sock);
+ close(drv->common.sock);
+ }
+
+ os_free(drv);
+}
+
+
+static int macsec_qca_send_eapol(void *priv, const u8 *addr,
+ const u8 *data, size_t data_len, int encrypt,
+ const u8 *own_addr, u32 flags, int link_id)
+{
+ struct macsec_qca_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) {
+ wpa_printf(MSG_INFO,
+ "malloc() failed for macsec_qca_send_eapol(len=%lu)",
+ (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->common.sock, (u8 *) hdr, len, 0);
+ os_free(hdr);
+
+ if (res < 0) {
+ wpa_printf(MSG_ERROR,
+ "macsec_qca_send_eapol - packet len: %lu - failed: send: %s",
+ (unsigned long) len, strerror(errno));
+ }
+
+ return res;
+}
+
+
+static int macsec_qca_macsec_init(void *priv, struct macsec_init_params *params)
+{
+ struct macsec_qca_data *drv = priv;
+
+ drv->always_include_sci = params->always_include_sci;
+ drv->use_es = params->use_es;
+ drv->use_scb = params->use_scb;
+
+ wpa_printf(MSG_DEBUG, "%s: es=%d, scb=%d, sci=%d",
+ __func__, drv->use_es, drv->use_scb,
+ drv->always_include_sci);
+
+ __macsec_drv_init(drv);
+
+ return 0;
+}
+
+
+static int macsec_qca_macsec_deinit(void *priv)
+{
+ struct macsec_qca_data *drv = priv;
+
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+
+ __macsec_drv_deinit(drv);
+
+ return 0;
+}
+
+
+static int macsec_qca_get_capability(void *priv, enum macsec_cap *cap)
+{
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+
+ *cap = MACSEC_CAP_INTEG_AND_CONF_0_30_50;
+
+ return 0;
+}
+
+
+static int macsec_qca_enable_protect_frames(void *priv, bool enabled)
+{
+ struct macsec_qca_data *drv = priv;
+ int ret = 0;
+
+ wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
+
+ drv->protect_frames = enabled;
+
+ return ret;
+}
+
+
+static int macsec_qca_set_replay_protect(void *priv, bool enabled,
+ unsigned int window)
+{
+ struct macsec_qca_data *drv = priv;
+ int ret = 0;
+
+ wpa_printf(MSG_DEBUG, "%s: enabled=%d, win=%u",
+ __func__, enabled, window);
+
+ drv->replay_protect = enabled;
+ drv->replay_window = window;
+
+ return ret;
+}
+
+
+static fal_cipher_suite_e macsec_qca_cs_type_get(u64 cs)
+{
+ if (cs == CS_ID_GCM_AES_128)
+ return FAL_CIPHER_SUITE_AES_GCM_128;
+ if (cs == CS_ID_GCM_AES_256)
+ return FAL_CIPHER_SUITE_AES_GCM_256;
+ return FAL_CIPHER_SUITE_MAX;
+}
+
+
+static int macsec_qca_set_current_cipher_suite(void *priv, u64 cs)
+{
+ struct macsec_qca_data *drv = priv;
+ fal_cipher_suite_e cs_type;
+
+ if (cs != CS_ID_GCM_AES_128 && cs != CS_ID_GCM_AES_256) {
+ wpa_printf(MSG_ERROR,
+ "%s: NOT supported CipherSuite: %016" PRIx64,
+ __func__, cs);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "%s: CipherSuite: %016" PRIx64, __func__, cs);
+
+ cs_type = macsec_qca_cs_type_get(cs);
+ return nss_macsec_secy_cipher_suite_set(drv->secy_id, cs_type);
+}
+
+
+static int macsec_qca_enable_controlled_port(void *priv, bool enabled)
+{
+ struct macsec_qca_data *drv = priv;
+ int ret = 0;
+
+ wpa_printf(MSG_DEBUG, "%s: enable=%d", __func__, enabled);
+
+ ret += nss_macsec_secy_controlled_port_en_set(drv->secy_id, enabled);
+
+ return ret;
+}
+
+
+static int macsec_qca_lookup_channel(struct channel_map *map,
+ struct ieee802_1x_mka_sci *sci,
+ u32 *channel)
+{
+ u32 i;
+
+ for (i = 0; i < MAXSC; i++) {
+ if (os_memcmp(&map[i].sci, sci,
+ sizeof(struct ieee802_1x_mka_sci)) == 0) {
+ *channel = i;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+
+static void macsec_qca_register_channel(struct channel_map *map,
+ struct ieee802_1x_mka_sci *sci,
+ u32 channel)
+{
+ os_memcpy(&map[channel].sci, sci, sizeof(struct ieee802_1x_mka_sci));
+}
+
+
+static int macsec_qca_lookup_receive_channel(struct macsec_qca_data *drv,
+ struct receive_sc *sc,
+ u32 *channel)
+{
+ return macsec_qca_lookup_channel(drv->receive_channel_map, &sc->sci,
+ channel);
+}
+
+
+static void macsec_qca_register_receive_channel(struct macsec_qca_data *drv,
+ struct receive_sc *sc,
+ u32 channel)
+{
+ macsec_qca_register_channel(drv->receive_channel_map, &sc->sci,
+ channel);
+}
+
+
+static int macsec_qca_lookup_transmit_channel(struct macsec_qca_data *drv,
+ struct transmit_sc *sc,
+ u32 *channel)
+{
+ return macsec_qca_lookup_channel(drv->transmit_channel_map, &sc->sci,
+ channel);
+}
+
+
+static void macsec_qca_register_transmit_channel(struct macsec_qca_data *drv,
+ struct transmit_sc *sc,
+ u32 channel)
+{
+ macsec_qca_register_channel(drv->transmit_channel_map, &sc->sci,
+ channel);
+}
+
+
+static int macsec_qca_get_receive_lowest_pn(void *priv, struct receive_sa *sa)
+{
+ struct macsec_qca_data *drv = priv;
+ int ret = 0;
+ u32 next_pn = 0;
+ bool enabled = false;
+ u32 win;
+ u32 channel;
+
+ ret = macsec_qca_lookup_receive_channel(priv, sa->sc, &channel);
+ if (ret != 0)
+ return ret;
+
+ ret += nss_macsec_secy_rx_sa_next_pn_get(drv->secy_id, channel, sa->an,
+ &next_pn);
+ ret += nss_macsec_secy_rx_sc_replay_protect_get(drv->secy_id, channel,
+ &enabled);
+ ret += nss_macsec_secy_rx_sc_anti_replay_window_get(drv->secy_id,
+ channel, &win);
+
+ if (enabled)
+ sa->lowest_pn = (next_pn > win) ? (next_pn - win) : 1;
+ else
+ sa->lowest_pn = next_pn;
+
+ wpa_printf(MSG_DEBUG, "%s: lpn=0x%x", __func__, sa->lowest_pn);
+
+ return ret;
+}
+
+
+static int macsec_qca_get_transmit_next_pn(void *priv, struct transmit_sa *sa)
+{
+ struct macsec_qca_data *drv = priv;
+ int ret = 0;
+ u32 channel;
+
+ ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel);
+ if (ret != 0)
+ return ret;
+
+ ret += nss_macsec_secy_tx_sa_next_pn_get(drv->secy_id, channel, sa->an,
+ &sa->next_pn);
+
+ wpa_printf(MSG_DEBUG, "%s: npn=0x%x", __func__, sa->next_pn);
+
+ return ret;
+}
+
+
+static int macsec_qca_set_transmit_next_pn(void *priv, struct transmit_sa *sa)
+{
+ struct macsec_qca_data *drv = priv;
+ int ret = 0;
+ u32 channel;
+
+ ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel);
+ if (ret != 0)
+ return ret;
+
+ ret += nss_macsec_secy_tx_sa_next_pn_set(drv->secy_id, channel, sa->an,
+ sa->next_pn);
+
+ wpa_printf(MSG_INFO, "%s: npn=0x%x", __func__, sa->next_pn);
+
+ return ret;
+}
+
+
+static int macsec_qca_get_available_receive_sc(void *priv, u32 *channel)
+{
+ struct macsec_qca_data *drv = priv;
+ int ret = 0;
+ u32 sc_ch = 0;
+ bool in_use = false;
+
+ for (sc_ch = 0; sc_ch < MAXSC; sc_ch++) {
+ ret = nss_macsec_secy_rx_sc_in_used_get(drv->secy_id, sc_ch,
+ &in_use);
+ if (ret)
+ continue;
+
+ if (!in_use) {
+ *channel = sc_ch;
+ wpa_printf(MSG_DEBUG, "%s: channel=%d",
+ __func__, *channel);
+ return 0;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "%s: no available channel", __func__);
+
+ return -1;
+}
+
+
+static int macsec_qca_create_receive_sc(void *priv, struct receive_sc *sc,
+ unsigned int conf_offset,
+ int validation)
+{
+ struct macsec_qca_data *drv = priv;
+ int ret = 0;
+ fal_rx_prc_lut_t entry;
+ fal_rx_sc_validate_frame_e vf;
+ enum validate_frames validate_frames = validation;
+ u32 channel;
+ const u8 *sci_addr = sc->sci.addr;
+ u16 sci_port = be_to_host16(sc->sci.port);
+
+ ret = macsec_qca_get_available_receive_sc(priv, &channel);
+ if (ret != 0)
+ return ret;
+
+ wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel);
+
+ /* rx prc lut */
+ os_memset(&entry, 0, sizeof(entry));
+
+ os_memcpy(entry.sci, sci_addr, ETH_ALEN);
+ entry.sci[6] = (sci_port >> 8) & 0xff;
+ entry.sci[7] = sci_port & 0xff;
+ entry.sci_mask = 0xf;
+
+ entry.valid = 1;
+ entry.channel = channel;
+ entry.action = FAL_RX_PRC_ACTION_PROCESS;
+ entry.offset = conf_offset;
+
+ /* rx validate frame */
+ if (validate_frames == Strict)
+ vf = FAL_RX_SC_VALIDATE_FRAME_STRICT;
+ else if (validate_frames == Checked)
+ vf = FAL_RX_SC_VALIDATE_FRAME_CHECK;
+ else
+ vf = FAL_RX_SC_VALIDATE_FRAME_DISABLED;
+
+ ret += nss_macsec_secy_rx_prc_lut_set(drv->secy_id, channel, &entry);
+ ret += nss_macsec_secy_rx_sc_create(drv->secy_id, channel);
+ ret += nss_macsec_secy_rx_sc_validate_frame_set(drv->secy_id, channel,
+ vf);
+ ret += nss_macsec_secy_rx_sc_replay_protect_set(drv->secy_id, channel,
+ drv->replay_protect);
+ ret += nss_macsec_secy_rx_sc_anti_replay_window_set(drv->secy_id,
+ channel,
+ drv->replay_window);
+
+ macsec_qca_register_receive_channel(drv, sc, channel);
+
+ return ret;
+}
+
+
+static int macsec_qca_delete_receive_sc(void *priv, struct receive_sc *sc)
+{
+ struct macsec_qca_data *drv = priv;
+ int ret;
+ fal_rx_prc_lut_t entry;
+ u32 channel;
+
+ ret = macsec_qca_lookup_receive_channel(priv, sc, &channel);
+ if (ret != 0)
+ return ret;
+
+ wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel);
+
+ /* rx prc lut */
+ os_memset(&entry, 0, sizeof(entry));
+
+ ret += nss_macsec_secy_rx_sc_del(drv->secy_id, channel);
+ ret += nss_macsec_secy_rx_prc_lut_set(drv->secy_id, channel, &entry);
+
+ return ret;
+}
+
+
+static int macsec_qca_create_receive_sa(void *priv, struct receive_sa *sa)
+{
+ struct macsec_qca_data *drv = priv;
+ int ret;
+ fal_rx_sak_t rx_sak;
+ int i = 0;
+ u32 channel;
+ fal_rx_prc_lut_t entry;
+ u32 offset;
+
+ ret = macsec_qca_lookup_receive_channel(priv, sa->sc, &channel);
+ if (ret != 0)
+ return ret;
+
+ wpa_printf(MSG_DEBUG, "%s, channel=%d, an=%d, lpn=0x%x",
+ __func__, channel, sa->an, sa->lowest_pn);
+
+ os_memset(&rx_sak, 0, sizeof(rx_sak));
+ rx_sak.sak_len = sa->pkey->key_len;
+ if (sa->pkey->key_len == SAK_128_LEN) {
+ for (i = 0; i < 16; i++)
+ rx_sak.sak[i] = sa->pkey->key[15 - i];
+ } else if (sa->pkey->key_len == SAK_256_LEN) {
+ for (i = 0; i < 16; i++) {
+ rx_sak.sak1[i] = sa->pkey->key[15 - i];
+ rx_sak.sak[i] = sa->pkey->key[31 - i];
+ }
+ } else {
+ return -1;
+ }
+
+ if (sa->pkey->confidentiality_offset == CONFIDENTIALITY_OFFSET_0)
+ offset = 0;
+ else if (sa->pkey->confidentiality_offset == CONFIDENTIALITY_OFFSET_30)
+ offset = 30;
+ else if (sa->pkey->confidentiality_offset == CONFIDENTIALITY_OFFSET_50)
+ offset = 50;
+ else
+ return -1;
+ ret += nss_macsec_secy_rx_prc_lut_get(drv->secy_id, channel, &entry);
+ entry.offset = offset;
+ ret += nss_macsec_secy_rx_prc_lut_set(drv->secy_id, channel, &entry);
+ ret += nss_macsec_secy_rx_sa_create(drv->secy_id, channel, sa->an);
+ ret += nss_macsec_secy_rx_sak_set(drv->secy_id, channel, sa->an,
+ &rx_sak);
+
+ return ret;
+}
+
+
+static int macsec_qca_enable_receive_sa(void *priv, struct receive_sa *sa)
+{
+ struct macsec_qca_data *drv = priv;
+ int ret;
+ u32 channel;
+
+ ret = macsec_qca_lookup_receive_channel(priv, sa->sc, &channel);
+ if (ret != 0)
+ return ret;
+
+ wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel,
+ sa->an);
+
+ ret += nss_macsec_secy_rx_sa_en_set(drv->secy_id, channel, sa->an,
+ true);
+
+ return ret;
+}
+
+
+static int macsec_qca_disable_receive_sa(void *priv, struct receive_sa *sa)
+{
+ struct macsec_qca_data *drv = priv;
+ int ret;
+ u32 channel;
+
+ ret = macsec_qca_lookup_receive_channel(priv, sa->sc, &channel);
+ if (ret != 0)
+ return ret;
+
+ wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel,
+ sa->an);
+
+ ret += nss_macsec_secy_rx_sa_en_set(drv->secy_id, channel, sa->an,
+ false);
+
+ return ret;
+}
+
+
+static int macsec_qca_get_available_transmit_sc(void *priv, u32 *channel)
+{
+ struct macsec_qca_data *drv = priv;
+ u32 sc_ch = 0;
+ bool in_use = false;
+
+ for (sc_ch = 0; sc_ch < MAXSC; sc_ch++) {
+ if (nss_macsec_secy_tx_sc_in_used_get(drv->secy_id, sc_ch,
+ &in_use))
+ continue;
+
+ if (!in_use) {
+ *channel = sc_ch;
+ wpa_printf(MSG_DEBUG, "%s: channel=%d",
+ __func__, *channel);
+ return 0;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "%s: no available channel", __func__);
+
+ return -1;
+}
+
+
+static int macsec_qca_create_transmit_sc(void *priv, struct transmit_sc *sc,
+ unsigned int conf_offset)
+{
+ struct macsec_qca_data *drv = priv;
+ int ret;
+ fal_tx_class_lut_t entry;
+ u8 psci[ETH_ALEN + 2];
+ u32 channel;
+ u16 sci_port = be_to_host16(sc->sci.port);
+
+ ret = macsec_qca_get_available_transmit_sc(priv, &channel);
+ if (ret != 0)
+ return ret;
+
+ wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel);
+
+ /* class lut */
+ os_memset(&entry, 0, sizeof(entry));
+
+ entry.valid = 1;
+ entry.action = FAL_TX_CLASS_ACTION_FORWARD;
+ entry.channel = channel;
+
+ os_memcpy(psci, sc->sci.addr, ETH_ALEN);
+ psci[6] = (sci_port >> 8) & 0xff;
+ psci[7] = sci_port & 0xff;
+
+ ret += nss_macsec_secy_tx_class_lut_set(drv->secy_id, channel, &entry);
+ ret += nss_macsec_secy_tx_sc_create(drv->secy_id, channel, psci, 8);
+ ret += nss_macsec_secy_tx_sc_protect_set(drv->secy_id, channel,
+ drv->protect_frames);
+ ret += nss_macsec_secy_tx_sc_confidentiality_offset_set(drv->secy_id,
+ channel,
+ conf_offset);
+
+ macsec_qca_register_transmit_channel(drv, sc, channel);
+
+ return ret;
+}
+
+
+static int macsec_qca_delete_transmit_sc(void *priv, struct transmit_sc *sc)
+{
+ struct macsec_qca_data *drv = priv;
+ int ret;
+ fal_tx_class_lut_t entry;
+ u32 channel;
+
+ ret = macsec_qca_lookup_transmit_channel(priv, sc, &channel);
+ if (ret != 0)
+ return ret;
+
+ wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel);
+
+ /* class lut */
+ os_memset(&entry, 0, sizeof(entry));
+
+ ret += nss_macsec_secy_tx_class_lut_set(drv->secy_id, channel, &entry);
+ ret += nss_macsec_secy_tx_sc_del(drv->secy_id, channel);
+
+ return ret;
+}
+
+
+static int macsec_qca_create_transmit_sa(void *priv, struct transmit_sa *sa)
+{
+ struct macsec_qca_data *drv = priv;
+ int ret;
+ u8 tci = 0;
+ fal_tx_sak_t tx_sak;
+ int i;
+ u32 channel;
+ u32 offset;
+
+ ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel);
+ if (ret != 0)
+ return ret;
+
+ wpa_printf(MSG_DEBUG,
+ "%s: channel=%d, an=%d, next_pn=0x%x, confidentiality=%d",
+ __func__, channel, sa->an, sa->next_pn, sa->confidentiality);
+
+ if (drv->always_include_sci)
+ tci |= TCI_SC;
+ else if (drv->use_es)
+ tci |= TCI_ES;
+ else if (drv->use_scb)
+ tci |= TCI_SCB;
+
+ if (sa->confidentiality)
+ tci |= TCI_E | TCI_C;
+
+ os_memset(&tx_sak, 0, sizeof(tx_sak));
+ tx_sak.sak_len = sa->pkey->key_len;
+ if (sa->pkey->key_len == SAK_128_LEN) {
+ for (i = 0; i < 16; i++)
+ tx_sak.sak[i] = sa->pkey->key[15 - i];
+ } else if (sa->pkey->key_len == SAK_256_LEN) {
+ for (i = 0; i < 16; i++) {
+ tx_sak.sak1[i] = sa->pkey->key[15 - i];
+ tx_sak.sak[i] = sa->pkey->key[31 - i];
+ }
+ } else {
+ return -1;
+ }
+
+ if (sa->pkey->confidentiality_offset == CONFIDENTIALITY_OFFSET_0)
+ offset = 0;
+ else if (sa->pkey->confidentiality_offset == CONFIDENTIALITY_OFFSET_30)
+ offset = 30;
+ else if (sa->pkey->confidentiality_offset == CONFIDENTIALITY_OFFSET_50)
+ offset = 50;
+ else
+ return -1;
+ ret += nss_macsec_secy_tx_sc_confidentiality_offset_set(drv->secy_id,
+ channel,
+ offset);
+ ret += nss_macsec_secy_tx_sa_next_pn_set(drv->secy_id, channel, sa->an,
+ sa->next_pn);
+ ret += nss_macsec_secy_tx_sak_set(drv->secy_id, channel, sa->an,
+ &tx_sak);
+ ret += nss_macsec_secy_tx_sc_tci_7_2_set(drv->secy_id, channel,
+ (tci >> 2));
+ ret += nss_macsec_secy_tx_sc_an_set(drv->secy_id, channel, sa->an);
+
+ return ret;
+}
+
+
+static int macsec_qca_enable_transmit_sa(void *priv, struct transmit_sa *sa)
+{
+ struct macsec_qca_data *drv = priv;
+ int ret;
+ u32 channel;
+
+ ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel);
+ if (ret != 0)
+ return ret;
+
+ wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel,
+ sa->an);
+
+ ret += nss_macsec_secy_tx_sa_en_set(drv->secy_id, channel, sa->an,
+ true);
+
+ return ret;
+}
+
+
+static int macsec_qca_disable_transmit_sa(void *priv, struct transmit_sa *sa)
+{
+ struct macsec_qca_data *drv = priv;
+ int ret;
+ u32 channel;
+
+ ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel);
+ if (ret != 0)
+ return ret;
+
+ wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel,
+ sa->an);
+
+ ret += nss_macsec_secy_tx_sa_en_set(drv->secy_id, channel, sa->an,
+ false);
+
+ return ret;
+}
+
+
+const struct wpa_driver_ops wpa_driver_macsec_qca_ops = {
+ .name = "macsec_qca",
+ .desc = "QCA MACsec Ethernet driver",
+ .get_ssid = driver_wired_get_ssid,
+ .get_bssid = driver_wired_get_bssid,
+ .get_capa = driver_wired_get_capa,
+ .init = macsec_qca_init,
+ .deinit = macsec_qca_deinit,
+ .hapd_init = macsec_qca_hapd_init,
+ .hapd_deinit = macsec_qca_hapd_deinit,
+ .hapd_send_eapol = macsec_qca_send_eapol,
+
+ .macsec_init = macsec_qca_macsec_init,
+ .macsec_deinit = macsec_qca_macsec_deinit,
+ .macsec_get_capability = macsec_qca_get_capability,
+ .enable_protect_frames = macsec_qca_enable_protect_frames,
+ .set_replay_protect = macsec_qca_set_replay_protect,
+ .set_current_cipher_suite = macsec_qca_set_current_cipher_suite,
+ .enable_controlled_port = macsec_qca_enable_controlled_port,
+ .get_receive_lowest_pn = macsec_qca_get_receive_lowest_pn,
+ .get_transmit_next_pn = macsec_qca_get_transmit_next_pn,
+ .set_transmit_next_pn = macsec_qca_set_transmit_next_pn,
+ .create_receive_sc = macsec_qca_create_receive_sc,
+ .delete_receive_sc = macsec_qca_delete_receive_sc,
+ .create_receive_sa = macsec_qca_create_receive_sa,
+ .enable_receive_sa = macsec_qca_enable_receive_sa,
+ .disable_receive_sa = macsec_qca_disable_receive_sa,
+ .create_transmit_sc = macsec_qca_create_transmit_sc,
+ .delete_transmit_sc = macsec_qca_delete_transmit_sc,
+ .create_transmit_sa = macsec_qca_create_transmit_sa,
+ .enable_transmit_sa = macsec_qca_enable_transmit_sa,
+ .disable_transmit_sa = macsec_qca_disable_transmit_sa,
+};
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_ndis.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_ndis.c
new file mode 100644
index 0000000..b32e009
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_ndis.c
@@ -0,0 +1,3232 @@
+/*
+ * WPA Supplicant - Windows/NDIS driver interface
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README 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 "common/ieee802_11_common.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[SSID_MAX_LEN];
+ int i;
+ for (i = 0; i < SSID_MAX_LEN; i++)
+ ssid[i] = rand() & 0xff;
+ return wpa_driver_ndis_set_ssid(drv, (u8 *) ssid, SSID_MAX_LEN);
+}
+
+
+static int wpa_driver_ndis_deauthenticate(void *priv, const u8 *addr,
+ u16 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)
+{
+ return get_ie((const u8 *) (res + 1), res->ie_len, ie);
+}
+
+
+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 > SSID_MAX_LEN)
+ 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_calloc(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_set_key_wrapper(void *priv,
+ struct wpa_driver_set_key_params *params)
+{
+ return wpa_driver_ndis_set_key(params->ifname, priv,
+ params->alg, params->addr,
+ params->key_idx, params->set_tx,
+ params->seq, params->seq_len,
+ params->key, params->key_len);
+}
+
+
+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 == WPA_KEY_MGMT_NONE ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_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 == WPA_KEY_MGMT_PSK)
+ auth_mode = Ndis802_11AuthModeWPA2PSK;
+ else
+ auth_mode = Ndis802_11AuthModeWPA2;
+#ifdef CONFIG_WPS
+ } else if (params->key_mgmt_suite == WPA_KEY_MGMT_WPS) {
+ auth_mode = Ndis802_11AuthModeOpen;
+ priv_mode = Ndis802_11PrivFilterAcceptAll;
+ if (params->wps == WPS_MODE_PRIVACY) {
+ u8 stub_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 stub 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, stub_key,
+ sizeof(stub_key));
+ }
+#endif /* CONFIG_WPS */
+ } else {
+ priv_mode = Ndis802_11PrivFilter8021xWEP;
+ if (params->key_mgmt_suite == WPA_KEY_MGMT_WPA_NONE)
+ auth_mode = Ndis802_11AuthModeWPANone;
+ else if (params->key_mgmt_suite == WPA_KEY_MGMT_PSK)
+ auth_mode = Ndis802_11AuthModeWPAPSK;
+ else
+ auth_mode = Ndis802_11AuthModeWPA;
+ }
+
+ switch (params->pairwise_suite) {
+ case WPA_CIPHER_CCMP:
+ encr = Ndis802_11Encryption3Enabled;
+ break;
+ case WPA_CIPHER_TKIP:
+ encr = Ndis802_11Encryption2Enabled;
+ break;
+ case WPA_CIPHER_WEP40:
+ case WPA_CIPHER_WEP104:
+ encr = Ndis802_11Encryption1Enabled;
+ break;
+ case WPA_CIPHER_NONE:
+#ifdef CONFIG_WPS
+ if (params->wps == WPS_MODE_PRIVACY) {
+ encr = Ndis802_11Encryption1Enabled;
+ break;
+ }
+#endif /* CONFIG_WPS */
+ if (params->group_suite == WPA_CIPHER_CCMP)
+ encr = Ndis802_11Encryption3Enabled;
+ else if (params->group_suite == WPA_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,
+ struct wpa_pmkid_params *params)
+{
+ struct wpa_driver_ndis_data *drv = priv;
+ struct ndis_pmkid_entry *entry, *prev;
+ const u8 *bssid = params->bssid;
+ const u8 *pmkid = params->pmkid;
+
+ if (!bssid || !pmkid)
+ return -1;
+ 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,
+ struct wpa_pmkid_params *params)
+{
+ struct wpa_driver_ndis_data *drv = priv;
+ struct ndis_pmkid_entry *entry, *prev;
+ const u8 *bssid = params->bssid;
+ const u8 *pmkid = params->pmkid;
+
+ if (!bssid || !pmkid)
+ return -1;
+ 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 = dup_binstr(desc, dlen);
+ 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 stub descriptors to work around this.
+ */
+ while (num_desc < num_name)
+ desc[num_desc++] = "stub 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 = dup_binstr(desc[i], dlen);
+ 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;
+ int i;
+
+ 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);
+
+ /* Update per interface supported AKMs */
+ for (i = 0; i < WPA_IF_MAX; i++)
+ drv->capa.key_mgmt_iftype[i] = drv->capa.key_mgmt;
+
+
+ /* 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 stub descriptors to work around this.
+ */
+ while (num_desc < num_name)
+ desc[num_desc++] = "stub 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;
+}
+
+
+static const char *ndis_drv_name = "ndis";
+static const char *ndis_drv_desc = "Windows NDIS driver";
+
+struct wpa_driver_ops wpa_driver_ndis_ops;
+
+void driver_ndis_init_ops(void)
+{
+ os_memset(&wpa_driver_ndis_ops, 0, sizeof(wpa_driver_ndis_ops));
+ wpa_driver_ndis_ops.name = ndis_drv_name;
+ wpa_driver_ndis_ops.desc = ndis_drv_desc;
+ wpa_driver_ndis_ops.get_bssid = wpa_driver_ndis_get_bssid;
+ wpa_driver_ndis_ops.get_ssid = wpa_driver_ndis_get_ssid;
+ wpa_driver_ndis_ops.set_key = wpa_driver_ndis_set_key_wrapper;
+ wpa_driver_ndis_ops.init = wpa_driver_ndis_init;
+ wpa_driver_ndis_ops.deinit = wpa_driver_ndis_deinit;
+ wpa_driver_ndis_ops.deauthenticate = wpa_driver_ndis_deauthenticate;
+ wpa_driver_ndis_ops.associate = wpa_driver_ndis_associate;
+ wpa_driver_ndis_ops.add_pmkid = wpa_driver_ndis_add_pmkid;
+ wpa_driver_ndis_ops.remove_pmkid = wpa_driver_ndis_remove_pmkid;
+ wpa_driver_ndis_ops.flush_pmkid = wpa_driver_ndis_flush_pmkid;
+ wpa_driver_ndis_ops.get_capa = wpa_driver_ndis_get_capa;
+ wpa_driver_ndis_ops.poll = wpa_driver_ndis_poll;
+ wpa_driver_ndis_ops.get_ifname = wpa_driver_ndis_get_ifname;
+ wpa_driver_ndis_ops.get_mac_addr = wpa_driver_ndis_get_mac_addr;
+ wpa_driver_ndis_ops.get_scan_results2 =
+ wpa_driver_ndis_get_scan_results;
+ wpa_driver_ndis_ops.get_interfaces = wpa_driver_ndis_get_interfaces;
+ wpa_driver_ndis_ops.scan2 = wpa_driver_ndis_scan;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_ndis.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_ndis.h
new file mode 100644
index 0000000..89d136d
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_ndis.h
@@ -0,0 +1,59 @@
+/*
+ * WPA Supplicant - Windows/NDIS driver interface
+ * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README 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/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_ndis_.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_ndis_.c
new file mode 100644
index 0000000..4d23001
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_ndis_.c
@@ -0,0 +1,99 @@
+/*
+ * WPA Supplicant - Windows/NDIS driver interface - event processing
+ * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README 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/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_nl80211.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_nl80211.c
new file mode 100644
index 0000000..eb46e4d
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_nl80211.c
@@ -0,0 +1,13795 @@
+/*
+ * Driver interaction with Linux nl80211/cfg80211
+ * Copyright (c) 2002-2015, 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 software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <sys/types.h>
+#include <fcntl.h>
+#include <net/if.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/ctrl.h>
+#include <linux/rtnetlink.h>
+#include <netpacket/packet.h>
+#include <linux/errqueue.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "common/qca-vendor.h"
+#include "common/qca-vendor-attr.h"
+#include "common/brcm_vendor.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/wpa_common.h"
+#include "crypto/sha256.h"
+#include "crypto/sha384.h"
+#include "netlink.h"
+#include "linux_defines.h"
+#include "linux_ioctl.h"
+#include "radiotap.h"
+#include "radiotap_iter.h"
+#include "rfkill.h"
+#include "driver_nl80211.h"
+
+
+#ifndef NETLINK_CAP_ACK
+#define NETLINK_CAP_ACK 10
+#endif /* NETLINK_CAP_ACK */
+/* support for extack if compilation headers are too old */
+#ifndef NETLINK_EXT_ACK
+#define NETLINK_EXT_ACK 11
+enum nlmsgerr_attrs {
+ NLMSGERR_ATTR_UNUSED,
+ NLMSGERR_ATTR_MSG,
+ NLMSGERR_ATTR_OFFS,
+ NLMSGERR_ATTR_COOKIE,
+
+ __NLMSGERR_ATTR_MAX,
+ NLMSGERR_ATTR_MAX = __NLMSGERR_ATTR_MAX - 1
+};
+#endif
+#ifndef NLM_F_CAPPED
+#define NLM_F_CAPPED 0x100
+#endif
+#ifndef NLM_F_ACK_TLVS
+#define NLM_F_ACK_TLVS 0x200
+#endif
+#ifndef SOL_NETLINK
+#define SOL_NETLINK 270
+#endif
+
+
+#ifdef ANDROID
+/* system/core/libnl_2 does not include nl_socket_set_nonblocking() */
+#undef nl_socket_set_nonblocking
+#define nl_socket_set_nonblocking(h) android_nl_socket_set_nonblocking(h)
+
+#endif /* ANDROID */
+
+
+static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
+{
+ struct nl_sock *handle;
+
+ handle = nl_socket_alloc_cb(cb);
+ if (handle == NULL) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink "
+ "callbacks (%s)", dbg);
+ return NULL;
+ }
+
+ if (genl_connect(handle)) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to connect to generic "
+ "netlink (%s)", dbg);
+ nl_socket_free(handle);
+ return NULL;
+ }
+
+ return handle;
+}
+
+
+static void nl_destroy_handles(struct nl_sock **handle)
+{
+ if (*handle == NULL)
+ return;
+ nl_socket_free(*handle);
+ *handle = NULL;
+}
+
+
+#if __WORDSIZE == 64
+#define ELOOP_SOCKET_INVALID (intptr_t) 0x8888888888888889ULL
+#else
+#define ELOOP_SOCKET_INVALID (intptr_t) 0x88888889ULL
+#endif
+
+static void nl80211_register_eloop_read(struct nl_sock **handle,
+ eloop_sock_handler handler,
+ void *eloop_data, int persist)
+{
+ /*
+ * libnl uses a pretty small buffer (32 kB that gets converted to 64 kB)
+ * by default. It is possible to hit that limit in some cases where
+ * operations are blocked, e.g., with a burst of Deauthentication frames
+ * to hostapd and STA entry deletion. Try to increase the buffer to make
+ * this less likely to occur.
+ */
+ int err;
+
+ err = nl_socket_set_buffer_size(*handle, 262144, 0);
+ if (err < 0) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Could not set nl_socket RX buffer size: %s",
+ nl_geterror(err));
+ /* continue anyway with the default (smaller) buffer */
+ }
+
+ nl_socket_set_nonblocking(*handle);
+ eloop_register_read_sock(nl_socket_get_fd(*handle), handler,
+ eloop_data, *handle);
+ if (!persist)
+ *handle = (void *) (((intptr_t) *handle) ^
+ ELOOP_SOCKET_INVALID);
+}
+
+
+static void nl80211_destroy_eloop_handle(struct nl_sock **handle, int persist)
+{
+ if (!persist)
+ *handle = (void *) (((intptr_t) *handle) ^
+ ELOOP_SOCKET_INVALID);
+ eloop_unregister_read_sock(nl_socket_get_fd(*handle));
+ nl_destroy_handles(handle);
+}
+
+
+static void nl80211_global_deinit(void *priv);
+static void nl80211_check_global(struct nl80211_global *global);
+
+static void wpa_driver_nl80211_deinit(struct i802_bss *bss);
+static int wpa_driver_nl80211_set_mode_ibss(struct i802_bss *bss,
+ struct hostapd_freq_params *freq);
+
+static int
+wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv,
+ const u8 *set_addr, int first,
+ const char *driver_params);
+static int nl80211_send_frame_cmd(struct i802_bss *bss,
+ unsigned int freq, unsigned int wait,
+ const u8 *buf, size_t buf_len,
+ int save_cookie,
+ int no_cck, int no_ack, int offchanok,
+ const u16 *csa_offs, size_t csa_offs_len);
+static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss,
+ int report);
+
+#define IFIDX_ANY -1
+
+static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
+ int ifidx_reason);
+static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
+ int ifidx_reason);
+static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
+ int ifidx_reason);
+
+static int nl80211_set_channel(struct i802_bss *bss,
+ struct hostapd_freq_params *freq, int set_chan);
+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,
+ int reset_mode);
+
+static int i802_set_iface_flags(struct i802_bss *bss, int up);
+static int nl80211_set_param(void *priv, const char *param);
+#ifdef CONFIG_MESH
+static int nl80211_put_mesh_config(struct nl_msg *msg,
+ struct wpa_driver_mesh_bss_params *params);
+#endif /* CONFIG_MESH */
+static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
+ u16 reason);
+
+
+/* Converts nl80211_chan_width to a common format */
+enum chan_width convert2width(int width)
+{
+ switch (width) {
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ return CHAN_WIDTH_20_NOHT;
+ case NL80211_CHAN_WIDTH_20:
+ return CHAN_WIDTH_20;
+ case NL80211_CHAN_WIDTH_40:
+ return CHAN_WIDTH_40;
+ case NL80211_CHAN_WIDTH_80:
+ return CHAN_WIDTH_80;
+ case NL80211_CHAN_WIDTH_80P80:
+ return CHAN_WIDTH_80P80;
+ case NL80211_CHAN_WIDTH_160:
+ return CHAN_WIDTH_160;
+ case NL80211_CHAN_WIDTH_320:
+ return CHAN_WIDTH_320;
+ default:
+ return CHAN_WIDTH_UNKNOWN;
+ }
+}
+
+
+int is_ap_interface(enum nl80211_iftype nlmode)
+{
+ return nlmode == NL80211_IFTYPE_AP ||
+ nlmode == NL80211_IFTYPE_P2P_GO;
+}
+
+
+int is_sta_interface(enum nl80211_iftype nlmode)
+{
+ return nlmode == NL80211_IFTYPE_STATION ||
+ nlmode == NL80211_IFTYPE_P2P_CLIENT;
+}
+
+
+static int is_p2p_net_interface(enum nl80211_iftype nlmode)
+{
+ return nlmode == NL80211_IFTYPE_P2P_CLIENT ||
+ nlmode == NL80211_IFTYPE_P2P_GO;
+}
+
+
+struct i802_bss * get_bss_ifindex(struct wpa_driver_nl80211_data *drv,
+ int ifindex)
+{
+ struct i802_bss *bss;
+
+ for (bss = drv->first_bss; bss; bss = bss->next) {
+ if (bss->ifindex == ifindex)
+ return bss;
+ }
+
+ return NULL;
+}
+
+
+static int is_mesh_interface(enum nl80211_iftype nlmode)
+{
+ return nlmode == NL80211_IFTYPE_MESH_POINT;
+}
+
+
+void nl80211_mark_disconnected(struct wpa_driver_nl80211_data *drv)
+{
+ if (drv->associated)
+ os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN);
+ drv->associated = 0;
+ os_memset(&drv->sta_mlo_info, 0, sizeof(drv->sta_mlo_info));
+ os_memset(drv->bssid, 0, ETH_ALEN);
+ drv->first_bss->flink->freq = 0;
+#ifdef CONFIG_DRIVER_NL80211_QCA
+ os_free(drv->pending_roam_data);
+ drv->pending_roam_data = NULL;
+ os_free(drv->pending_t2lm_data);
+ drv->pending_t2lm_data = NULL;
+ os_free(drv->pending_link_reconfig_data);
+ drv->pending_link_reconfig_data = NULL;
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+ drv->auth_mld = false;
+ drv->auth_mld_link_id = -1;
+ os_memset(drv->auth_ap_mld_addr, 0, ETH_ALEN);
+}
+
+
+/* nl80211 code */
+static int ack_handler(struct nl_msg *msg, void *arg)
+{
+ int *err = arg;
+ *err = 0;
+ return NL_STOP;
+}
+
+
+struct nl80211_ack_ext_arg {
+ int *err;
+ void *ext_data;
+};
+
+
+static int ack_handler_cookie(struct nl_msg *msg, void *arg)
+{
+ struct nl80211_ack_ext_arg *ext_arg = arg;
+ struct nlattr *tb[NLMSGERR_ATTR_MAX + 1];
+ u64 *cookie = ext_arg->ext_data;
+ struct nlattr *attrs;
+ size_t ack_len, attr_len;
+
+ *ext_arg->err = 0;
+ ack_len = sizeof(struct nlmsghdr) + sizeof(int) +
+ sizeof(struct nlmsghdr);
+ attrs = (struct nlattr *)
+ ((u8 *) nlmsg_data(nlmsg_hdr(msg)) + sizeof(struct nlmsghdr) +
+ sizeof(int));
+ if (nlmsg_hdr(msg)->nlmsg_len <= ack_len)
+ return NL_STOP;
+
+ attr_len = nlmsg_hdr(msg)->nlmsg_len - ack_len;
+
+ if(!(nlmsg_hdr(msg)->nlmsg_flags & NLM_F_ACK_TLVS))
+ return NL_STOP;
+
+ nla_parse(tb, NLMSGERR_ATTR_MAX, attrs, attr_len, NULL);
+ if (tb[NLMSGERR_ATTR_COOKIE])
+ *cookie = nla_get_u64(tb[NLMSGERR_ATTR_COOKIE]);
+
+ 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)
+{
+ struct nlmsghdr *nlh = (struct nlmsghdr *) err - 1;
+ int len = nlh->nlmsg_len;
+ struct nlattr *attrs;
+ struct nlattr *tb[NLMSGERR_ATTR_MAX + 1];
+ int *ret = arg;
+ int ack_len = sizeof(*nlh) + sizeof(int) + sizeof(*nlh);
+
+ *ret = err->error;
+
+ if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS))
+ return NL_SKIP;
+
+ if (!(nlh->nlmsg_flags & NLM_F_CAPPED))
+ ack_len += err->msg.nlmsg_len - sizeof(*nlh);
+
+ if (len <= ack_len)
+ return NL_STOP;
+
+ attrs = (void *) ((unsigned char *) nlh + ack_len);
+ len -= ack_len;
+
+ nla_parse(tb, NLMSGERR_ATTR_MAX, attrs, len, NULL);
+ if (tb[NLMSGERR_ATTR_MSG]) {
+ len = strnlen((char *) nla_data(tb[NLMSGERR_ATTR_MSG]),
+ nla_len(tb[NLMSGERR_ATTR_MSG]));
+ wpa_printf(MSG_ERROR, "nl80211: kernel reports: %*s",
+ len, (char *) nla_data(tb[NLMSGERR_ATTR_MSG]));
+ }
+
+ return NL_SKIP;
+}
+
+
+static int no_seq_check(struct nl_msg *msg, void *arg)
+{
+ return NL_OK;
+}
+
+
+static void nl80211_nlmsg_clear(struct nl_msg *msg)
+{
+ /*
+ * Clear nlmsg data, e.g., to make sure key material is not left in
+ * heap memory for unnecessarily long time.
+ */
+ if (msg) {
+ struct nlmsghdr *hdr = nlmsg_hdr(msg);
+ void *data = nlmsg_data(hdr);
+ /*
+ * This would use nlmsg_datalen() or the older nlmsg_len() if
+ * only libnl were to maintain a stable API.. Neither will work
+ * with all released versions, so just calculate the length
+ * here.
+ */
+ int len = hdr->nlmsg_len - NLMSG_HDRLEN;
+
+ os_memset(data, 0, len);
+ }
+}
+
+
+static int send_and_recv(struct nl80211_global *global,
+ struct nl_sock *nl_handle, struct nl_msg *msg,
+ int (*valid_handler)(struct nl_msg *, void *),
+ void *valid_data,
+ int (*ack_handler_custom)(struct nl_msg *, void *),
+ void *ack_data)
+{
+ struct nl_cb *cb;
+ int err = -ENOMEM, opt;
+
+ if (!msg)
+ return -ENOMEM;
+
+ cb = nl_cb_clone(global->nl_cb);
+ if (!cb)
+ goto out;
+
+ /* try to set NETLINK_EXT_ACK to 1, ignoring errors */
+ opt = 1;
+ setsockopt(nl_socket_get_fd(nl_handle), SOL_NETLINK,
+ NETLINK_EXT_ACK, &opt, sizeof(opt));
+
+ /* try to set NETLINK_CAP_ACK to 1, ignoring errors */
+ opt = 1;
+ setsockopt(nl_socket_get_fd(nl_handle), SOL_NETLINK,
+ NETLINK_CAP_ACK, &opt, sizeof(opt));
+
+ err = nl_send_auto_complete(nl_handle, msg);
+ if (err < 0) {
+ wpa_printf(MSG_INFO,
+ "nl80211: nl_send_auto_complete() failed: %s",
+ nl_geterror(err));
+ /* Need to convert libnl error code to an errno value. For now,
+ * just hardcode this to EBADF; the real error reason is shown
+ * in that error print above. */
+ err = -EBADF;
+ 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);
+ if (ack_handler_custom) {
+ struct nl80211_ack_ext_arg *ext_arg = ack_data;
+
+ ext_arg->err = &err;
+ nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM,
+ ack_handler_custom, ack_data);
+ } else {
+ 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) {
+ int res = nl_recvmsgs(nl_handle, cb);
+
+ if (res == -NLE_DUMP_INTR) {
+ /* Most likely one of the nl80211 dump routines hit a
+ * case where internal results changed while the dump
+ * was being sent. The most common known case for this
+ * is scan results fetching while associated were every
+ * received Beacon frame from the AP may end up
+ * incrementing bss_generation. This
+ * NL80211_CMD_GET_SCAN case tries again in the caller;
+ * other cases (of which there are no known common ones)
+ * will stop and return an error. */
+ wpa_printf(MSG_DEBUG, "nl80211: %s; convert to -EAGAIN",
+ nl_geterror(res));
+ err = -EAGAIN;
+ } else if (res < 0) {
+ wpa_printf(MSG_INFO,
+ "nl80211: %s->nl_recvmsgs failed: %d (%s)",
+ __func__, res, nl_geterror(res));
+ }
+ }
+ out:
+ nl_cb_put(cb);
+ /* Always clear the message as it can potentially contain keys */
+ nl80211_nlmsg_clear(msg);
+ nlmsg_free(msg);
+ return err;
+}
+
+
+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,
+ int (*ack_handler_custom)(struct nl_msg *, void *),
+ void *ack_data)
+{
+ return send_and_recv(drv->global, drv->global->nl, msg,
+ valid_handler, valid_data,
+ ack_handler_custom, ack_data);
+}
+
+
+/* Use this method to mark that it is necessary to own the connection/interface
+ * for this operation.
+ * handle may be set to NULL, to get the same behavior as send_and_recv_msgs().
+ * set_owner can be used to mark this socket for receiving control port frames.
+ */
+static int send_and_recv_msgs_owner(struct wpa_driver_nl80211_data *drv,
+ struct nl_msg *msg,
+ struct nl_sock *handle, int set_owner,
+ int (*valid_handler)(struct nl_msg *,
+ void *),
+ void *valid_data,
+ int (*ack_handler_custom)(struct nl_msg *,
+ void *),
+ void *ack_data)
+{
+ if (!msg)
+ return -ENOMEM;
+
+ /* Control port over nl80211 needs the flags and attributes below.
+ *
+ * The Linux kernel has initial checks for them (in nl80211.c) like:
+ * validate_pae_over_nl80211(...)
+ * or final checks like:
+ * dev->ieee80211_ptr->conn_owner_nlportid != info->snd_portid
+ *
+ * Final operations (e.g., disassociate) don't need to set these
+ * attributes, but they have to be performed on the socket, which has
+ * the connection owner property set in the kernel.
+ */
+ if ((drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_RX) &&
+ handle && set_owner &&
+ (nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_OVER_NL80211) ||
+ nla_put_flag(msg, NL80211_ATTR_SOCKET_OWNER) ||
+ nla_put_u16(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE, ETH_P_PAE) ||
+ nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_PREAUTH)))
+ return -1;
+
+ return send_and_recv(drv->global, handle ? handle : drv->global->nl,
+ msg, valid_handler, valid_data,
+ ack_handler_custom, ack_data);
+}
+
+
+static int
+send_and_recv_msgs_connect_handle(struct wpa_driver_nl80211_data *drv,
+ struct nl_msg *msg, struct i802_bss *bss,
+ int set_owner)
+{
+ struct nl_sock *nl_connect = get_connect_handle(bss);
+
+ if (nl_connect)
+ return send_and_recv_msgs_owner(drv, msg, nl_connect, set_owner,
+ process_bss_event, bss, NULL,
+ NULL);
+ else
+ return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+}
+
+
+struct nl_sock * get_connect_handle(struct i802_bss *bss)
+{
+ if ((bss->drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_RX) ||
+ bss->use_nl_connect)
+ return bss->nl_connect;
+
+ return NULL;
+}
+
+
+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 nl80211_global *global,
+ const char *family, const char *group)
+{
+ struct nl_msg *msg;
+ int ret;
+ struct family_data res = { group, -ENOENT };
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+ if (!genlmsg_put(msg, 0, 0, global->nlctrl_id,
+ 0, 0, CTRL_CMD_GETFAMILY, 0) ||
+ nla_put_string(msg, CTRL_ATTR_FAMILY_NAME, family)) {
+ nlmsg_free(msg);
+ return -1;
+ }
+
+ ret = send_and_recv(global, global->nl, msg, family_handler, &res,
+ NULL, NULL);
+ if (ret == 0)
+ ret = res.id;
+ return ret;
+}
+
+
+void * nl80211_cmd(struct wpa_driver_nl80211_data *drv,
+ struct nl_msg *msg, int flags, uint8_t cmd)
+{
+ if (TEST_FAIL())
+ return NULL;
+ return genlmsg_put(msg, 0, 0, drv->global->nl80211_id,
+ 0, flags, cmd, 0);
+}
+
+
+static int nl80211_set_iface_id(struct nl_msg *msg, struct i802_bss *bss)
+{
+ if (bss->wdev_id_set)
+ return nla_put_u64(msg, NL80211_ATTR_WDEV, bss->wdev_id);
+ return nla_put_u32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
+}
+
+
+struct nl_msg * nl80211_cmd_msg(struct i802_bss *bss, int flags, uint8_t cmd)
+{
+ struct nl_msg *msg;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return NULL;
+
+ if (!nl80211_cmd(bss->drv, msg, flags, cmd) ||
+ nl80211_set_iface_id(msg, bss) < 0) {
+ nlmsg_free(msg);
+ return NULL;
+ }
+
+ return msg;
+}
+
+
+static struct nl_msg *
+nl80211_ifindex_msg_build(struct wpa_driver_nl80211_data *drv,
+ struct nl_msg *msg, int ifindex, int flags,
+ uint8_t cmd)
+{
+ if (!msg)
+ return NULL;
+
+ if (!nl80211_cmd(drv, msg, flags, cmd) ||
+ nla_put_u32(msg, NL80211_ATTR_IFINDEX, ifindex)) {
+ nlmsg_free(msg);
+ return NULL;
+ }
+
+ return msg;
+}
+
+
+static struct nl_msg *
+nl80211_ifindex_msg(struct wpa_driver_nl80211_data *drv, int ifindex,
+ int flags, uint8_t cmd)
+{
+ return nl80211_ifindex_msg_build(drv, nlmsg_alloc(), ifindex, flags,
+ cmd);
+}
+
+
+struct nl_msg * nl80211_drv_msg(struct wpa_driver_nl80211_data *drv, int flags,
+ uint8_t cmd)
+{
+ return nl80211_ifindex_msg(drv, drv->ifindex, flags, cmd);
+}
+
+
+struct nl_msg * nl80211_bss_msg(struct i802_bss *bss, int flags, uint8_t cmd)
+{
+ return nl80211_ifindex_msg(bss->drv, bss->ifindex, flags, cmd);
+}
+
+
+struct wiphy_idx_data {
+ int wiphy_idx;
+ enum nl80211_iftype nlmode;
+ u8 *macaddr;
+ u8 use_4addr;
+};
+
+
+static int netdev_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_idx_data *info = arg;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ if (tb[NL80211_ATTR_WIPHY])
+ info->wiphy_idx = nla_get_u32(tb[NL80211_ATTR_WIPHY]);
+
+ if (tb[NL80211_ATTR_IFTYPE])
+ info->nlmode = nla_get_u32(tb[NL80211_ATTR_IFTYPE]);
+
+ if (tb[NL80211_ATTR_MAC] && info->macaddr)
+ os_memcpy(info->macaddr, nla_data(tb[NL80211_ATTR_MAC]),
+ ETH_ALEN);
+
+ if (tb[NL80211_ATTR_4ADDR])
+ info->use_4addr = nla_get_u8(tb[NL80211_ATTR_4ADDR]);
+
+ return NL_SKIP;
+}
+
+
+int nl80211_get_wiphy_index(struct i802_bss *bss)
+{
+ struct nl_msg *msg;
+ struct wiphy_idx_data data = {
+ .wiphy_idx = -1,
+ .macaddr = NULL,
+ };
+
+ if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_GET_INTERFACE)))
+ return -1;
+
+ if (send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data,
+ NULL, NULL) == 0)
+ return data.wiphy_idx;
+ return -1;
+}
+
+
+static enum nl80211_iftype nl80211_get_ifmode(struct i802_bss *bss)
+{
+ struct nl_msg *msg;
+ struct wiphy_idx_data data = {
+ .nlmode = NL80211_IFTYPE_UNSPECIFIED,
+ .macaddr = NULL,
+ };
+
+ if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_GET_INTERFACE)))
+ return NL80211_IFTYPE_UNSPECIFIED;
+
+ if (send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data,
+ NULL, NULL) == 0)
+ return data.nlmode;
+ return NL80211_IFTYPE_UNSPECIFIED;
+}
+
+
+static int nl80211_get_macaddr(struct i802_bss *bss)
+{
+ struct nl_msg *msg;
+ struct wiphy_idx_data data = {
+ .macaddr = bss->addr,
+ };
+
+ if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_GET_INTERFACE)))
+ return -1;
+
+ return send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data,
+ NULL, NULL);
+}
+
+
+static int nl80211_get_4addr(struct i802_bss *bss)
+{
+ struct nl_msg *msg;
+ struct wiphy_idx_data data = {
+ .use_4addr = 0,
+ };
+
+ if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_GET_INTERFACE)) ||
+ send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data,
+ NULL, NULL))
+ return -1;
+ return data.use_4addr;
+}
+
+
+static int nl80211_register_beacons(struct wpa_driver_nl80211_data *drv,
+ struct nl80211_wiphy_data *w)
+{
+ struct nl_msg *msg;
+ int ret;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ if (!nl80211_cmd(drv, msg, 0, NL80211_CMD_REGISTER_BEACONS) ||
+ nla_put_u32(msg, NL80211_ATTR_WIPHY, w->wiphy_idx)) {
+ nlmsg_free(msg);
+ return -1;
+ }
+
+ ret = send_and_recv(drv->global, w->nl_beacons, msg, NULL, NULL,
+ NULL, NULL);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Register beacons command "
+ "failed: ret=%d (%s)",
+ ret, strerror(-ret));
+ }
+ return ret;
+}
+
+
+static void nl80211_recv_beacons(int sock, void *eloop_ctx, void *handle)
+{
+ struct nl80211_wiphy_data *w = eloop_ctx;
+ int res;
+
+ wpa_printf(MSG_EXCESSIVE, "nl80211: Beacon event message available");
+
+ res = nl_recvmsgs(handle, w->nl_cb);
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "nl80211: %s->nl_recvmsgs failed: %d",
+ __func__, res);
+ }
+}
+
+
+static int process_beacon_event(struct nl_msg *msg, void *arg)
+{
+ struct nl80211_wiphy_data *w = arg;
+ struct wpa_driver_nl80211_data *drv;
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ union wpa_event_data event;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ if (gnlh->cmd != NL80211_CMD_FRAME) {
+ wpa_printf(MSG_DEBUG, "nl80211: Unexpected beacon event? (%d)",
+ gnlh->cmd);
+ return NL_SKIP;
+ }
+
+ if (!tb[NL80211_ATTR_FRAME])
+ return NL_SKIP;
+
+ dl_list_for_each(drv, &w->drvs, struct wpa_driver_nl80211_data,
+ wiphy_list) {
+ os_memset(&event, 0, sizeof(event));
+ event.rx_mgmt.frame = nla_data(tb[NL80211_ATTR_FRAME]);
+ event.rx_mgmt.frame_len = nla_len(tb[NL80211_ATTR_FRAME]);
+
+ // Add RSSI and freq info for Beacon frame handling. Added by Liangyu Chu
+ if (tb[NL80211_ATTR_WIPHY_FREQ])
+ event.rx_mgmt.freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
+ if (tb[NL80211_ATTR_RX_SIGNAL_DBM])
+ event.rx_mgmt.ssi_signal = (s32) nla_get_u32(tb[NL80211_ATTR_RX_SIGNAL_DBM]);
+
+ wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
+ }
+
+ return NL_SKIP;
+}
+
+
+static struct nl80211_wiphy_data *
+nl80211_get_wiphy_data_ap(struct i802_bss *bss)
+{
+ static DEFINE_DL_LIST(nl80211_wiphys);
+ struct nl80211_wiphy_data *w;
+ int wiphy_idx, found = 0;
+ struct i802_bss *tmp_bss;
+ u8 channel;
+
+ if (bss->wiphy_data != NULL)
+ return bss->wiphy_data;
+
+ wiphy_idx = nl80211_get_wiphy_index(bss);
+
+ dl_list_for_each(w, &nl80211_wiphys, struct nl80211_wiphy_data, list) {
+ if (w->wiphy_idx == wiphy_idx)
+ goto add;
+ }
+
+ /* alloc new one */
+ w = os_zalloc(sizeof(*w));
+ if (w == NULL)
+ return NULL;
+ w->wiphy_idx = wiphy_idx;
+ dl_list_init(&w->bsss);
+ dl_list_init(&w->drvs);
+
+ /* Beacon frames not supported in IEEE 802.11ad */
+ if (ieee80211_freq_to_chan(bss->flink->freq, &channel) !=
+ HOSTAPD_MODE_IEEE80211AD) {
+ w->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
+ if (!w->nl_cb) {
+ os_free(w);
+ return NULL;
+ }
+ nl_cb_set(w->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
+ no_seq_check, NULL);
+ nl_cb_set(w->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
+ process_beacon_event, w);
+
+ w->nl_beacons = nl_create_handle(bss->drv->global->nl_cb,
+ "wiphy beacons");
+ if (w->nl_beacons == NULL) {
+ os_free(w);
+ return NULL;
+ }
+
+ if (nl80211_register_beacons(bss->drv, w)) {
+ nl_destroy_handles(&w->nl_beacons);
+ os_free(w);
+ return NULL;
+ }
+
+ nl80211_register_eloop_read(&w->nl_beacons,
+ nl80211_recv_beacons, w, 0);
+ }
+
+ dl_list_add(&nl80211_wiphys, &w->list);
+
+add:
+ /* drv entry for this bss already there? */
+ dl_list_for_each(tmp_bss, &w->bsss, struct i802_bss, wiphy_list) {
+ if (tmp_bss->drv == bss->drv) {
+ found = 1;
+ break;
+ }
+ }
+ /* if not add it */
+ if (!found)
+ dl_list_add(&w->drvs, &bss->drv->wiphy_list);
+
+ dl_list_add(&w->bsss, &bss->wiphy_list);
+ bss->wiphy_data = w;
+ return w;
+}
+
+
+static void nl80211_put_wiphy_data_ap(struct i802_bss *bss)
+{
+ struct nl80211_wiphy_data *w = bss->wiphy_data;
+ struct i802_bss *tmp_bss;
+ int found = 0;
+
+ if (w == NULL)
+ return;
+ bss->wiphy_data = NULL;
+ dl_list_del(&bss->wiphy_list);
+
+ /* still any for this drv present? */
+ dl_list_for_each(tmp_bss, &w->bsss, struct i802_bss, wiphy_list) {
+ if (tmp_bss->drv == bss->drv) {
+ found = 1;
+ break;
+ }
+ }
+ /* if not remove it */
+ if (!found)
+ dl_list_del(&bss->drv->wiphy_list);
+
+ if (!dl_list_empty(&w->bsss))
+ return;
+
+ if (w->nl_beacons)
+ nl80211_destroy_eloop_handle(&w->nl_beacons, 0);
+
+ nl_cb_put(w->nl_cb);
+ dl_list_del(&w->list);
+ os_free(w);
+}
+
+
+static unsigned int nl80211_get_ifindex(void *priv)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ return drv->ifindex;
+}
+
+
+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 int get_mlo_info(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct nlattr *link_attr, *link_data[NL80211_ATTR_MAX + 1];
+ static struct nla_policy link_policy[NL80211_ATTR_MAX + 1] = {
+ [NL80211_ATTR_MLO_LINK_ID] = { .type = NLA_U8 },
+ [NL80211_ATTR_MAC] = { .minlen = ETH_ALEN, .maxlen = ETH_ALEN },
+ };
+ struct driver_sta_mlo_info *info = arg;
+ int rem;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ if (!tb[NL80211_ATTR_MLO_LINKS])
+ return NL_SKIP;
+
+ info->valid_links = 0;
+ nla_for_each_nested(link_attr, tb[NL80211_ATTR_MLO_LINKS], rem) {
+ u8 link_id;
+
+ if (nla_parse_nested(link_data, NL80211_ATTR_MAX,
+ link_attr, link_policy) != 0)
+ continue;
+
+ if (!link_data[NL80211_ATTR_MLO_LINK_ID] ||
+ !link_data[NL80211_ATTR_MAC])
+ continue;
+
+ link_id = nla_get_u8(link_data[NL80211_ATTR_MLO_LINK_ID]);
+ if (link_id >= MAX_NUM_MLD_LINKS)
+ continue;
+ info->valid_links |= BIT(link_id);
+ os_memcpy(info->links[link_id].addr,
+ nla_data(link_data[NL80211_ATTR_MAC]), ETH_ALEN);
+ if (link_data[NL80211_ATTR_WIPHY_FREQ])
+ info->links[link_id].freq =
+ nla_get_u32(link_data[NL80211_ATTR_WIPHY_FREQ]);
+ }
+
+ return NL_SKIP;
+}
+
+
+static int nl80211_get_sta_mlo_info(void *priv,
+ struct driver_sta_mlo_info *mlo_info)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ if (!drv->associated)
+ return -1;
+
+ if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
+ struct nl_msg *msg;
+
+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE);
+ if (send_and_recv_msgs(drv, msg, get_mlo_info,
+ &drv->sta_mlo_info, NULL, NULL))
+ return -1;
+ }
+
+ os_memcpy(mlo_info, &drv->sta_mlo_info, sizeof(*mlo_info));
+ return 0;
+}
+
+
+static void wpa_driver_nl80211_event_newlink(
+ struct nl80211_global *global, struct wpa_driver_nl80211_data *drv,
+ int ifindex, const char *ifname)
+{
+ union wpa_event_data event;
+
+ if (drv && os_strcmp(drv->first_bss->ifname, ifname) == 0) {
+ if (if_nametoindex(drv->first_bss->ifname) == 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Interface %s does not exist - ignore RTM_NEWLINK",
+ drv->first_bss->ifname);
+ return;
+ }
+ if (!drv->if_removed)
+ return;
+ wpa_printf(MSG_DEBUG, "nl80211: Mark if_removed=0 for %s based on RTM_NEWLINK event",
+ drv->first_bss->ifname);
+ drv->if_removed = 0;
+ }
+
+ os_memset(&event, 0, sizeof(event));
+ event.interface_status.ifindex = ifindex;
+ os_strlcpy(event.interface_status.ifname, ifname,
+ sizeof(event.interface_status.ifname));
+ event.interface_status.ievent = EVENT_INTERFACE_ADDED;
+ if (drv)
+ wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
+ else
+ wpa_supplicant_event_global(global->ctx, EVENT_INTERFACE_STATUS,
+ &event);
+}
+
+
+static void wpa_driver_nl80211_event_dellink(
+ struct nl80211_global *global, struct wpa_driver_nl80211_data *drv,
+ int ifindex, const char *ifname)
+{
+ union wpa_event_data event;
+
+ if (drv && os_strcmp(drv->first_bss->ifname, ifname) == 0) {
+ if (drv->if_removed) {
+ wpa_printf(MSG_DEBUG, "nl80211: if_removed already set - ignore RTM_DELLINK event for %s",
+ ifname);
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "RTM_DELLINK: Interface '%s' removed - mark if_removed=1",
+ ifname);
+ drv->if_removed = 1;
+ } else {
+ wpa_printf(MSG_DEBUG, "RTM_DELLINK: Interface '%s' removed",
+ ifname);
+ }
+
+ os_memset(&event, 0, sizeof(event));
+ event.interface_status.ifindex = ifindex;
+ os_strlcpy(event.interface_status.ifname, ifname,
+ sizeof(event.interface_status.ifname));
+ event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
+ if (drv)
+ wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
+ else
+ wpa_supplicant_event_global(global->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)) {
+ nl80211_check_global(drv->global);
+ wpa_printf(MSG_DEBUG, "nl80211: Update ifindex for a removed "
+ "interface");
+ if (wpa_driver_nl80211_finish_drv_init(drv, NULL, 0, NULL) < 0)
+ return -1;
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static struct wpa_driver_nl80211_data *
+nl80211_find_drv(struct nl80211_global *global, int idx, u8 *buf, size_t len,
+ int *init_failed)
+{
+ struct wpa_driver_nl80211_data *drv;
+ int res;
+
+ if (init_failed)
+ *init_failed = 0;
+ dl_list_for_each(drv, &global->interfaces,
+ struct wpa_driver_nl80211_data, list) {
+ res = wpa_driver_nl80211_own_ifindex(drv, idx, buf, len);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Found matching own interface, but failed to complete reinitialization");
+ if (init_failed)
+ *init_failed = 1;
+ return drv;
+ }
+ if (res > 0 || have_ifidx(drv, idx, IFIDX_ANY))
+ return drv;
+ }
+ return NULL;
+}
+
+
+static void nl80211_refresh_mac(struct wpa_driver_nl80211_data *drv,
+ int ifindex, int notify)
+{
+ struct i802_bss *bss;
+ u8 addr[ETH_ALEN];
+
+ bss = get_bss_ifindex(drv, ifindex);
+ if (bss &&
+ linux_get_ifhwaddr(drv->global->ioctl_sock,
+ bss->ifname, addr) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: %s: failed to re-read MAC address",
+ bss->ifname);
+ } else if (bss && os_memcmp(addr, bss->addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Own MAC address on ifindex %d (%s) changed from "
+ MACSTR " to " MACSTR,
+ ifindex, bss->ifname,
+ MAC2STR(bss->addr), MAC2STR(addr));
+ os_memcpy(bss->prev_addr, bss->addr, ETH_ALEN);
+ os_memcpy(bss->addr, addr, ETH_ALEN);
+ if (notify)
+ wpa_supplicant_event(drv->ctx,
+ EVENT_INTERFACE_MAC_CHANGED, NULL);
+ }
+}
+
+
+static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
+ struct ifinfomsg *ifi,
+ u8 *buf, size_t len)
+{
+ struct nl80211_global *global = ctx;
+ struct wpa_driver_nl80211_data *drv;
+ int attrlen;
+ struct rtattr *attr;
+ u32 brid = 0;
+ char namebuf[IFNAMSIZ];
+ char ifname[IFNAMSIZ + 1];
+ char extra[100], *pos, *end;
+ int init_failed;
+
+ extra[0] = '\0';
+ pos = extra;
+ end = pos + sizeof(extra);
+ ifname[0] = '\0';
+
+ attrlen = len;
+ attr = (struct rtattr *) buf;
+ while (RTA_OK(attr, attrlen)) {
+ switch (attr->rta_type) {
+ case IFLA_IFNAME:
+ if (RTA_PAYLOAD(attr) > IFNAMSIZ)
+ break;
+ os_memcpy(ifname, RTA_DATA(attr), RTA_PAYLOAD(attr));
+ ifname[RTA_PAYLOAD(attr)] = '\0';
+ break;
+ case IFLA_MASTER:
+ brid = nla_get_u32((struct nlattr *) attr);
+ pos += os_snprintf(pos, end - pos, " master=%u", brid);
+ break;
+ case IFLA_WIRELESS:
+ pos += os_snprintf(pos, end - pos, " wext");
+ break;
+ case IFLA_OPERSTATE:
+ pos += os_snprintf(pos, end - pos, " operstate=%u",
+ nla_get_u32((struct nlattr *) attr));
+ break;
+ case IFLA_LINKMODE:
+ pos += os_snprintf(pos, end - pos, " linkmode=%u",
+ nla_get_u32((struct nlattr *) attr));
+ break;
+ }
+ attr = RTA_NEXT(attr, attrlen);
+ }
+ extra[sizeof(extra) - 1] = '\0';
+
+ wpa_printf(MSG_DEBUG, "RTM_NEWLINK: ifi_index=%d ifname=%s%s ifi_family=%d ifi_flags=0x%x (%s%s%s%s)",
+ ifi->ifi_index, ifname, extra, ifi->ifi_family,
+ 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]" : "");
+
+ drv = nl80211_find_drv(global, ifi->ifi_index, buf, len, &init_failed);
+ if (!drv)
+ goto event_newlink;
+ if (init_failed)
+ return; /* do not update interface state */
+
+ if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) {
+ namebuf[0] = '\0';
+ if (if_indextoname(ifi->ifi_index, namebuf) &&
+ linux_iface_up(drv->global->ioctl_sock, namebuf) > 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down "
+ "event since interface %s is up", namebuf);
+ drv->ignore_if_down_event = 0;
+ /* Re-read MAC address as it may have changed */
+ nl80211_refresh_mac(drv, ifi->ifi_index, 1);
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "nl80211: Interface down (%s/%s)",
+ namebuf, ifname);
+ if (os_strcmp(drv->first_bss->ifname, ifname) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Not the main interface (%s) - do not indicate interface down",
+ drv->first_bss->ifname);
+ } else if (drv->ignore_if_down_event) {
+ wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down "
+ "event generated by mode change");
+ drv->ignore_if_down_event = 0;
+ } else {
+ drv->if_disabled = 1;
+ wpa_supplicant_event(drv->ctx,
+ EVENT_INTERFACE_DISABLED, NULL);
+
+ /*
+ * Try to get drv again, since it may be removed as
+ * part of the EVENT_INTERFACE_DISABLED handling for
+ * dynamic interfaces
+ */
+ drv = nl80211_find_drv(global, ifi->ifi_index,
+ buf, len, NULL);
+ if (!drv)
+ return;
+ }
+ }
+
+ if (drv->if_disabled && (ifi->ifi_flags & IFF_UP)) {
+ namebuf[0] = '\0';
+ if (if_indextoname(ifi->ifi_index, namebuf) &&
+ linux_iface_up(drv->global->ioctl_sock, namebuf) == 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up "
+ "event since interface %s is down",
+ namebuf);
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "nl80211: Interface up (%s/%s)",
+ namebuf, ifname);
+ if (os_strcmp(drv->first_bss->ifname, ifname) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Not the main interface (%s) - do not indicate interface up",
+ drv->first_bss->ifname);
+ } else if (if_nametoindex(drv->first_bss->ifname) == 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up "
+ "event since interface %s does not exist",
+ drv->first_bss->ifname);
+ } else if (drv->if_removed) {
+ wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up "
+ "event since interface %s is marked "
+ "removed", drv->first_bss->ifname);
+ } else {
+ /* Re-read MAC address as it may have changed */
+ nl80211_refresh_mac(drv, ifi->ifi_index, 0);
+
+ 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)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Set IF_OPER_UP again based on ifi_flags and expected operstate");
+ netlink_send_oper_ifla(drv->global->netlink, drv->ifindex,
+ -1, IF_OPER_UP);
+ }
+
+event_newlink:
+ if (ifname[0])
+ wpa_driver_nl80211_event_newlink(global, drv, ifi->ifi_index,
+ ifname);
+
+ if (ifi->ifi_family == AF_BRIDGE && brid && drv) {
+ struct i802_bss *bss;
+
+ /* device has been added to bridge */
+ if (!if_indextoname(brid, namebuf)) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Could not find bridge ifname for ifindex %u",
+ brid);
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "nl80211: Add ifindex %u for bridge %s",
+ brid, namebuf);
+ add_ifidx(drv, brid, ifi->ifi_index);
+
+ for (bss = drv->first_bss; bss; bss = bss->next) {
+ if (os_strcmp(ifname, bss->ifname) == 0) {
+ os_strlcpy(bss->brname, namebuf, IFNAMSIZ);
+ break;
+ }
+ }
+ }
+}
+
+
+static void wpa_driver_nl80211_event_rtm_dellink(void *ctx,
+ struct ifinfomsg *ifi,
+ u8 *buf, size_t len)
+{
+ struct nl80211_global *global = ctx;
+ struct wpa_driver_nl80211_data *drv;
+ int attrlen;
+ struct rtattr *attr;
+ u32 brid = 0;
+ char ifname[IFNAMSIZ + 1];
+ char extra[100], *pos, *end;
+
+ extra[0] = '\0';
+ pos = extra;
+ end = pos + sizeof(extra);
+ ifname[0] = '\0';
+
+ attrlen = len;
+ attr = (struct rtattr *) buf;
+ while (RTA_OK(attr, attrlen)) {
+ switch (attr->rta_type) {
+ case IFLA_IFNAME:
+ if (RTA_PAYLOAD(attr) > IFNAMSIZ)
+ break;
+ os_memcpy(ifname, RTA_DATA(attr), RTA_PAYLOAD(attr));
+ ifname[RTA_PAYLOAD(attr)] = '\0';
+ break;
+ case IFLA_MASTER:
+ brid = nla_get_u32((struct nlattr *) attr);
+ pos += os_snprintf(pos, end - pos, " master=%u", brid);
+ break;
+ case IFLA_OPERSTATE:
+ pos += os_snprintf(pos, end - pos, " operstate=%u",
+ nla_get_u32((struct nlattr *) attr));
+ break;
+ case IFLA_LINKMODE:
+ pos += os_snprintf(pos, end - pos, " linkmode=%u",
+ nla_get_u32((struct nlattr *) attr));
+ break;
+ }
+ attr = RTA_NEXT(attr, attrlen);
+ }
+ extra[sizeof(extra) - 1] = '\0';
+
+ wpa_printf(MSG_DEBUG, "RTM_DELLINK: ifi_index=%d ifname=%s%s ifi_family=%d ifi_flags=0x%x (%s%s%s%s)",
+ ifi->ifi_index, ifname, extra, ifi->ifi_family,
+ 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]" : "");
+
+ drv = nl80211_find_drv(global, ifi->ifi_index, buf, len, NULL);
+
+ if (ifi->ifi_family == AF_BRIDGE && brid && drv) {
+ /* device has been removed from bridge */
+ char namebuf[IFNAMSIZ];
+
+ if (!if_indextoname(brid, namebuf)) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Could not find bridge ifname for ifindex %u",
+ brid);
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Remove ifindex %u for bridge %s",
+ brid, namebuf);
+ }
+ del_ifidx(drv, brid, ifi->ifi_index);
+ }
+
+ if (ifi->ifi_family != AF_BRIDGE || !brid)
+ wpa_driver_nl80211_event_dellink(global, drv, ifi->ifi_index,
+ ifname);
+}
+
+
+struct nl80211_get_assoc_freq_arg {
+ struct wpa_driver_nl80211_data *drv;
+ unsigned int assoc_freq;
+ unsigned int ibss_freq;
+ u8 assoc_bssid[ETH_ALEN];
+ u8 assoc_ssid[SSID_MAX_LEN];
+ u8 assoc_ssid_len;
+ u8 bssid[MAX_NUM_MLD_LINKS][ETH_ALEN];
+ unsigned int freq[MAX_NUM_MLD_LINKS];
+};
+
+static int nl80211_get_assoc_freq_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_INFORMATION_ELEMENTS] = { .type = NLA_UNSPEC },
+ [NL80211_BSS_STATUS] = { .type = NLA_U32 },
+ [NL80211_BSS_MLO_LINK_ID] = { .type = NLA_U8 },
+ };
+ struct nl80211_get_assoc_freq_arg *ctx = arg;
+ enum nl80211_bss_status status;
+ struct wpa_driver_nl80211_data *drv = ctx->drv;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+ if (!tb[NL80211_ATTR_BSS] ||
+ nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS],
+ bss_policy) ||
+ !bss[NL80211_BSS_STATUS])
+ return NL_SKIP;
+
+ status = nla_get_u32(bss[NL80211_BSS_STATUS]);
+ if (status == NL80211_BSS_STATUS_ASSOCIATED &&
+ bss[NL80211_BSS_FREQUENCY]) {
+ int link_id = -1;
+ u32 freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
+
+ if (bss[NL80211_BSS_MLO_LINK_ID])
+ link_id = nla_get_u8(bss[NL80211_BSS_MLO_LINK_ID]);
+
+ if (link_id >= 0 && link_id < MAX_NUM_MLD_LINKS) {
+ ctx->freq[link_id] = freq;
+ wpa_printf(MSG_DEBUG,
+ "nl80211: MLO link %d associated on %u MHz",
+ link_id, ctx->freq[link_id]);
+ }
+
+ if (!drv->sta_mlo_info.valid_links ||
+ drv->sta_mlo_info.assoc_link_id == link_id) {
+ ctx->assoc_freq = freq;
+ wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz",
+ ctx->assoc_freq);
+ }
+ }
+ if (status == NL80211_BSS_STATUS_IBSS_JOINED &&
+ bss[NL80211_BSS_FREQUENCY]) {
+ ctx->ibss_freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
+ wpa_printf(MSG_DEBUG, "nl80211: IBSS-joined on %u MHz",
+ ctx->ibss_freq);
+ }
+ if (status == NL80211_BSS_STATUS_ASSOCIATED &&
+ bss[NL80211_BSS_BSSID]) {
+ int link_id = -1;
+ const u8 *bssid = nla_data(bss[NL80211_BSS_BSSID]);
+
+ if (bss[NL80211_BSS_MLO_LINK_ID])
+ link_id = nla_get_u8(bss[NL80211_BSS_MLO_LINK_ID]);
+
+ if (link_id >= 0 && link_id < MAX_NUM_MLD_LINKS) {
+ os_memcpy(ctx->bssid[link_id], bssid, ETH_ALEN);
+ wpa_printf(MSG_DEBUG,
+ "nl80211: MLO link %d associated with "
+ MACSTR, link_id, MAC2STR(bssid));
+ }
+
+ if (!drv->sta_mlo_info.valid_links ||
+ drv->sta_mlo_info.assoc_link_id == link_id) {
+ os_memcpy(ctx->assoc_bssid, bssid, ETH_ALEN);
+ wpa_printf(MSG_DEBUG, "nl80211: Associated with "
+ MACSTR, MAC2STR(bssid));
+ }
+
+ }
+
+ if (status == NL80211_BSS_STATUS_ASSOCIATED &&
+ bss[NL80211_BSS_INFORMATION_ELEMENTS]) {
+ const u8 *ie, *ssid;
+ size_t ie_len;
+
+ ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
+ ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
+ ssid = get_ie(ie, ie_len, WLAN_EID_SSID);
+ if (ssid && ssid[1] > 0 && ssid[1] <= SSID_MAX_LEN) {
+ ctx->assoc_ssid_len = ssid[1];
+ os_memcpy(ctx->assoc_ssid, ssid + 2, ssid[1]);
+ }
+ }
+
+ return NL_SKIP;
+}
+
+
+int nl80211_get_assoc_ssid(struct wpa_driver_nl80211_data *drv, u8 *ssid)
+{
+ struct nl_msg *msg;
+ int ret;
+ struct nl80211_get_assoc_freq_arg arg;
+ int count = 0;
+
+try_again:
+ msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
+ os_memset(&arg, 0, sizeof(arg));
+ arg.drv = drv;
+ ret = send_and_recv_msgs(drv, msg, nl80211_get_assoc_freq_handler,
+ &arg, NULL, NULL);
+ if (ret == -EAGAIN) {
+ count++;
+ if (count >= 10) {
+ wpa_printf(MSG_INFO,
+ "nl80211: Failed to receive consistent scan result dump for get_assoc_ssid");
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Failed to receive consistent scan result dump for get_assoc_ssid - try again");
+ goto try_again;
+ }
+ }
+ if (ret == 0) {
+ os_memcpy(ssid, arg.assoc_ssid, arg.assoc_ssid_len);
+ return arg.assoc_ssid_len;
+ }
+ wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d (%s)",
+ ret, strerror(-ret));
+ return ret;
+}
+
+
+unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv)
+{
+ struct nl_msg *msg;
+ int ret;
+ struct nl80211_get_assoc_freq_arg arg;
+ int count = 0;
+
+try_again:
+ msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
+ os_memset(&arg, 0, sizeof(arg));
+ arg.drv = drv;
+ ret = send_and_recv_msgs(drv, msg, nl80211_get_assoc_freq_handler,
+ &arg, NULL, NULL);
+ if (ret == -EAGAIN) {
+ count++;
+ if (count >= 10) {
+ wpa_printf(MSG_INFO,
+ "nl80211: Failed to receive consistent scan result dump for get_assoc_freq");
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Failed to receive consistent scan result dump for get_assoc_freq - try again");
+ goto try_again;
+ }
+ }
+ if (ret == 0) {
+ unsigned int freq = drv->nlmode == NL80211_IFTYPE_ADHOC ?
+ arg.ibss_freq : arg.assoc_freq;
+ wpa_printf(MSG_DEBUG, "nl80211: Operating frequency for the "
+ "associated BSS from scan results: %u MHz", freq);
+ if (freq)
+ drv->assoc_freq = freq;
+
+ if (drv->sta_mlo_info.valid_links) {
+ int i;
+
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++)
+ drv->sta_mlo_info.links[i].freq = arg.freq[i];
+ }
+
+ return drv->assoc_freq;
+ }
+ wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d "
+ "(%s)", ret, strerror(-ret));
+ return drv->assoc_freq;
+}
+
+
+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;
+}
+
+
+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 = WPA_INVALID_NOISE;
+ sig_change->frequency = drv->assoc_freq;
+
+ msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
+ return send_and_recv_msgs(drv, msg, get_link_noise, sig_change,
+ NULL, NULL);
+}
+
+
+static int get_channel_info(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1] = { 0 };
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct wpa_channel_info *chan_info = arg;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ os_memset(chan_info, 0, sizeof(struct wpa_channel_info));
+ chan_info->chanwidth = CHAN_WIDTH_UNKNOWN;
+
+ if (tb[NL80211_ATTR_WIPHY_FREQ])
+ chan_info->frequency =
+ nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
+ if (tb[NL80211_ATTR_CHANNEL_WIDTH])
+ chan_info->chanwidth = convert2width(
+ nla_get_u32(tb[NL80211_ATTR_CHANNEL_WIDTH]));
+ if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
+ enum nl80211_channel_type ct =
+ nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
+
+ switch (ct) {
+ case NL80211_CHAN_HT40MINUS:
+ chan_info->sec_channel = -1;
+ break;
+ case NL80211_CHAN_HT40PLUS:
+ chan_info->sec_channel = 1;
+ break;
+ default:
+ chan_info->sec_channel = 0;
+ break;
+ }
+ }
+ if (tb[NL80211_ATTR_CENTER_FREQ1])
+ chan_info->center_frq1 =
+ nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]);
+ if (tb[NL80211_ATTR_CENTER_FREQ2])
+ chan_info->center_frq2 =
+ nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]);
+
+ if (chan_info->center_frq2) {
+ u8 seg1_idx = 0;
+
+ if (ieee80211_freq_to_chan(chan_info->center_frq2, &seg1_idx) !=
+ NUM_HOSTAPD_MODES)
+ chan_info->seg1_idx = seg1_idx;
+ }
+
+ return NL_SKIP;
+}
+
+
+static int nl80211_channel_info(void *priv, struct wpa_channel_info *ci)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+
+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE);
+ return send_and_recv_msgs(drv, msg, get_channel_info, ci, NULL, NULL);
+}
+
+
+static void wpa_driver_nl80211_event_receive(int sock, void *eloop_ctx,
+ void *handle)
+{
+ struct nl_cb *cb = eloop_ctx;
+ int res;
+
+ wpa_printf(MSG_MSGDUMP, "nl80211: Event message available");
+
+ res = nl_recvmsgs(handle, cb);
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "nl80211: %s->nl_recvmsgs failed: %d",
+ __func__, res);
+ }
+}
+
+
+/**
+ * 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';
+
+ if (!nl80211_cmd(drv, msg, 0, NL80211_CMD_REQ_SET_REG) ||
+ nla_put_string(msg, NL80211_ATTR_REG_ALPHA2, alpha2)) {
+ nlmsg_free(msg);
+ return -EINVAL;
+ }
+ if (send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL))
+ return -EINVAL;
+ return 0;
+}
+
+
+static int nl80211_get_country(struct nl_msg *msg, void *arg)
+{
+ char *alpha2 = arg;
+ struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+
+ nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+ if (!tb_msg[NL80211_ATTR_REG_ALPHA2]) {
+ wpa_printf(MSG_DEBUG, "nl80211: No country information available");
+ return NL_SKIP;
+ }
+ os_strlcpy(alpha2, nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]), 3);
+ return NL_SKIP;
+}
+
+
+static int wpa_driver_nl80211_get_country(void *priv, char *alpha2)
+{
+ 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;
+
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG);
+
+ if (drv->capa.flags & WPA_DRIVER_FLAGS_SELF_MANAGED_REGULATORY) {
+ /* put wiphy idx to get the interface specific country code
+ * instead of the global one. */
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, drv->wiphy_idx)) {
+ nlmsg_free(msg);
+ return -1;
+ }
+ }
+
+ alpha2[0] = '\0';
+ ret = send_and_recv_msgs(drv, msg, nl80211_get_country, alpha2,
+ NULL, NULL);
+ if (!alpha2[0])
+ ret = -1;
+
+ return ret;
+}
+
+
+static int wpa_driver_nl80211_init_nl_global(struct nl80211_global *global)
+{
+ int ret;
+
+ global->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
+ if (global->nl_cb == NULL) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink "
+ "callbacks");
+ return -1;
+ }
+
+ global->nl = nl_create_handle(global->nl_cb, "nl");
+ if (global->nl == NULL)
+ goto err;
+
+ global->nl80211_id = genl_ctrl_resolve(global->nl, "nl80211");
+ if (global->nl80211_id < 0) {
+ wpa_printf(MSG_ERROR, "nl80211: 'nl80211' generic netlink not "
+ "found");
+ goto err;
+ }
+
+ global->nlctrl_id = genl_ctrl_resolve(global->nl, "nlctrl");
+ if (global->nlctrl_id < 0) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: 'nlctrl' generic netlink not found");
+ goto err;
+ }
+
+ global->nl_event = nl_create_handle(global->nl_cb, "event");
+ if (global->nl_event == NULL)
+ goto err;
+
+ ret = nl_get_multicast_id(global, "nl80211", "scan");
+ if (ret >= 0)
+ ret = nl_socket_add_membership(global->nl_event, ret);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, "nl80211: Could not add multicast "
+ "membership for scan events: %d (%s)",
+ ret, nl_geterror(ret));
+ goto err;
+ }
+
+ ret = nl_get_multicast_id(global, "nl80211", "mlme");
+ if (ret >= 0)
+ ret = nl_socket_add_membership(global->nl_event, ret);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, "nl80211: Could not add multicast "
+ "membership for mlme events: %d (%s)",
+ ret, nl_geterror(ret));
+ goto err;
+ }
+
+ ret = nl_get_multicast_id(global, "nl80211", "regulatory");
+ if (ret >= 0)
+ ret = nl_socket_add_membership(global->nl_event, ret);
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Could not add multicast "
+ "membership for regulatory events: %d (%s)",
+ ret, nl_geterror(ret));
+ /* Continue without regulatory events */
+ }
+
+ ret = nl_get_multicast_id(global, "nl80211", "vendor");
+ if (ret >= 0)
+ ret = nl_socket_add_membership(global->nl_event, ret);
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Could not add multicast "
+ "membership for vendor events: %d (%s)",
+ ret, nl_geterror(ret));
+ /* Continue without vendor events */
+ }
+
+ nl_cb_set(global->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
+ no_seq_check, NULL);
+ nl_cb_set(global->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
+ process_global_event, global);
+
+ nl80211_register_eloop_read(&global->nl_event,
+ wpa_driver_nl80211_event_receive,
+ global->nl_cb, 0);
+
+ return 0;
+
+err:
+ nl_destroy_handles(&global->nl_event);
+ nl_destroy_handles(&global->nl);
+ nl_cb_put(global->nl_cb);
+ global->nl_cb = NULL;
+ return -1;
+}
+
+
+static void nl80211_check_global(struct nl80211_global *global)
+{
+ struct nl_sock *handle;
+ const char *groups[] = { "scan", "mlme", "regulatory", "vendor", NULL };
+ int ret;
+ unsigned int i;
+
+ /*
+ * Try to re-add memberships to handle case of cfg80211 getting reloaded
+ * and all registration having been cleared.
+ */
+ handle = (void *) (((intptr_t) global->nl_event) ^
+ ELOOP_SOCKET_INVALID);
+
+ for (i = 0; groups[i]; i++) {
+ ret = nl_get_multicast_id(global, "nl80211", groups[i]);
+ if (ret >= 0)
+ ret = nl_socket_add_membership(handle, ret);
+ if (ret < 0) {
+ wpa_printf(MSG_INFO,
+ "nl80211: Could not re-add multicast membership for %s events: %d (%s)",
+ groups[i], ret, nl_geterror(ret));
+ }
+ }
+}
+
+
+static void wpa_driver_nl80211_rfkill_blocked(void *ctx)
+{
+ struct wpa_driver_nl80211_data *drv = ctx;
+
+ wpa_printf(MSG_DEBUG, "nl80211: RFKILL blocked");
+
+ /*
+ * rtnetlink ifdown handler will report interfaces other than the P2P
+ * Device interface as disabled.
+ */
+ if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE)
+ wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED, NULL);
+}
+
+
+static void wpa_driver_nl80211_rfkill_unblocked(void *ctx)
+{
+ struct wpa_driver_nl80211_data *drv = ctx;
+ wpa_printf(MSG_DEBUG, "nl80211: RFKILL unblocked");
+ if (i802_set_iface_flags(drv->first_bss, 1)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Could not set interface UP "
+ "after rfkill unblock");
+ return;
+ }
+
+ if (is_p2p_net_interface(drv->nlmode))
+ nl80211_disable_11b_rates(drv, drv->ifindex, 1);
+
+ /*
+ * rtnetlink ifup handler will report interfaces other than the P2P
+ * Device interface as enabled.
+ */
+ if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE)
+ wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED, NULL);
+}
+
+
+static void wpa_driver_nl80211_handle_eapol_tx_status(int sock,
+ void *eloop_ctx,
+ void *handle)
+{
+ struct wpa_driver_nl80211_data *drv = eloop_ctx;
+ u8 data[2048];
+ struct msghdr msg;
+ struct iovec entry;
+ u8 control[512];
+ struct cmsghdr *cmsg;
+ int res, found_ee = 0, found_wifi = 0, acked = 0;
+ union wpa_event_data event;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = &entry;
+ msg.msg_iovlen = 1;
+ entry.iov_base = data;
+ entry.iov_len = sizeof(data);
+ msg.msg_control = &control;
+ msg.msg_controllen = sizeof(control);
+
+ res = recvmsg(sock, &msg, MSG_ERRQUEUE);
+ /* if error or not fitting 802.3 header, return */
+ if (res < 14)
+ return;
+
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg))
+ {
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_WIFI_STATUS) {
+ int *ack;
+
+ found_wifi = 1;
+ ack = (void *)CMSG_DATA(cmsg);
+ acked = *ack;
+ }
+
+ if (cmsg->cmsg_level == SOL_PACKET &&
+ cmsg->cmsg_type == PACKET_TX_TIMESTAMP) {
+ struct sock_extended_err *err =
+ (struct sock_extended_err *)CMSG_DATA(cmsg);
+
+ if (err->ee_origin == SO_EE_ORIGIN_TXSTATUS)
+ found_ee = 1;
+ }
+ }
+
+ if (!found_ee || !found_wifi)
+ return;
+
+ memset(&event, 0, sizeof(event));
+ event.eapol_tx_status.dst = data;
+ event.eapol_tx_status.data = data + 14;
+ event.eapol_tx_status.data_len = res - 14;
+ event.eapol_tx_status.ack = acked;
+ wpa_supplicant_event(drv->ctx, EVENT_EAPOL_TX_STATUS, &event);
+}
+
+
+static int nl80211_init_connect_handle(struct i802_bss *bss)
+{
+ if (bss->nl_connect) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Connect handle already created (nl_connect=%p)",
+ bss->nl_connect);
+ return -1;
+ }
+
+ bss->nl_connect = nl_create_handle(bss->nl_cb, "connect");
+ if (!bss->nl_connect)
+ return -1;
+ nl80211_register_eloop_read(&bss->nl_connect,
+ wpa_driver_nl80211_event_receive,
+ bss->nl_cb, 1);
+ return 0;
+}
+
+
+static int nl80211_init_bss(struct i802_bss *bss)
+{
+ bss->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
+ if (!bss->nl_cb)
+ return -1;
+
+ nl_cb_set(bss->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
+ no_seq_check, NULL);
+ nl_cb_set(bss->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
+ process_bss_event, bss);
+
+ nl80211_init_connect_handle(bss);
+
+ return 0;
+}
+
+
+static void nl80211_destroy_bss(struct i802_bss *bss)
+{
+ nl_cb_put(bss->nl_cb);
+ bss->nl_cb = NULL;
+
+ if (bss->nl_connect)
+ nl80211_destroy_eloop_handle(&bss->nl_connect, 1);
+}
+
+
+static void
+wpa_driver_nl80211_drv_init_rfkill(struct wpa_driver_nl80211_data *drv)
+{
+ struct rfkill_config *rcfg;
+
+ if (drv->rfkill)
+ return;
+
+ rcfg = os_zalloc(sizeof(*rcfg));
+ if (!rcfg)
+ return;
+
+ rcfg->ctx = drv;
+
+ /* rfkill uses netdev sysfs for initialization. However, P2P Device is
+ * not associated with a netdev, so use the name of some other interface
+ * sharing the same wiphy as the P2P Device interface.
+ *
+ * Note: This is valid, as a P2P Device interface is always dynamically
+ * created and is created only once another wpa_s interface was added.
+ */
+ if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) {
+ struct nl80211_global *global = drv->global;
+ struct wpa_driver_nl80211_data *tmp1;
+
+ dl_list_for_each(tmp1, &global->interfaces,
+ struct wpa_driver_nl80211_data, list) {
+ if (drv == tmp1 || drv->wiphy_idx != tmp1->wiphy_idx ||
+ !tmp1->rfkill)
+ continue;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Use (%s) to initialize P2P Device rfkill",
+ tmp1->first_bss->ifname);
+ os_strlcpy(rcfg->ifname, tmp1->first_bss->ifname,
+ sizeof(rcfg->ifname));
+ break;
+ }
+ } else {
+ os_strlcpy(rcfg->ifname, drv->first_bss->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) {
+ wpa_printf(MSG_DEBUG, "nl80211: RFKILL status not available");
+ os_free(rcfg);
+ }
+}
+
+
+static void * wpa_driver_nl80211_drv_init(void *ctx, const char *ifname,
+ void *global_priv, int hostapd,
+ const u8 *set_addr,
+ const char *driver_params)
+{
+ struct wpa_driver_nl80211_data *drv;
+ struct i802_bss *bss;
+ unsigned int i;
+
+ if (global_priv == NULL)
+ return NULL;
+ drv = os_zalloc(sizeof(*drv));
+ if (drv == NULL)
+ return NULL;
+ drv->global = global_priv;
+ drv->ctx = ctx;
+ drv->hostapd = !!hostapd;
+ drv->eapol_sock = -1;
+
+ /*
+ * There is no driver capability flag for this, so assume it is
+ * supported and disable this on first attempt to use if the driver
+ * rejects the command due to missing support.
+ */
+ drv->set_rekey_offload = 1;
+
+ drv->num_if_indices = ARRAY_SIZE(drv->default_if_indices);
+ drv->if_indices = drv->default_if_indices;
+
+ drv->first_bss = os_zalloc(sizeof(*drv->first_bss));
+ if (!drv->first_bss) {
+ os_free(drv);
+ return NULL;
+ }
+ bss = drv->first_bss;
+ bss->drv = drv;
+ bss->ctx = ctx;
+
+ os_strlcpy(bss->ifname, ifname, sizeof(bss->ifname));
+ drv->monitor_ifidx = -1;
+ drv->monitor_sock = -1;
+ drv->eapol_tx_sock = -1;
+ drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
+
+ if (nl80211_init_bss(bss))
+ goto failed;
+
+ if (wpa_driver_nl80211_finish_drv_init(drv, set_addr, 1, driver_params))
+ goto failed;
+
+ if (drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_TX_STATUS) {
+ drv->control_port_ap = 1;
+ goto skip_wifi_status;
+ }
+
+ drv->eapol_tx_sock = socket(PF_PACKET, SOCK_DGRAM, 0);
+ if (drv->eapol_tx_sock < 0)
+ goto failed;
+
+ if (drv->data_tx_status) {
+ int enabled = 1;
+
+ if (setsockopt(drv->eapol_tx_sock, SOL_SOCKET, SO_WIFI_STATUS,
+ &enabled, sizeof(enabled)) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: wifi status sockopt failed: %s",
+ strerror(errno));
+ drv->data_tx_status = 0;
+ if (!drv->use_monitor)
+ drv->capa.flags &=
+ ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
+ } else {
+ eloop_register_read_sock(
+ drv->eapol_tx_sock,
+ wpa_driver_nl80211_handle_eapol_tx_status,
+ drv, NULL);
+ }
+ }
+skip_wifi_status:
+
+ if (drv->global) {
+ nl80211_check_global(drv->global);
+ dl_list_add(&drv->global->interfaces, &drv->list);
+ drv->in_interface_list = 1;
+ }
+
+ /*
+ * Set the default link to be the first one, and set its address to that
+ * of the interface.
+ */
+ bss->flink = &bss->links[0];
+ bss->n_links = 1;
+ os_memcpy(bss->flink->addr, bss->addr, ETH_ALEN);
+
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++)
+ bss->links[i].link_id = NL80211_DRV_LINK_ID_NA;
+
+ return bss;
+
+failed:
+ wpa_driver_nl80211_deinit(bss);
+ return NULL;
+}
+
+
+/**
+ * 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)
+{
+ return wpa_driver_nl80211_drv_init(ctx, ifname, global_priv, 0, NULL,
+ NULL);
+}
+
+
+static int nl80211_register_frame(struct i802_bss *bss,
+ struct nl_sock *nl_handle,
+ u16 type, const u8 *match, size_t match_len,
+ bool multicast)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret;
+ char buf[30];
+
+ buf[0] = '\0';
+ wpa_snprintf_hex(buf, sizeof(buf), match, match_len);
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Register frame type=0x%x (%s) nl_handle=%p match=%s multicast=%d",
+ type, fc2str(type), nl_handle, buf, multicast);
+
+ if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_REGISTER_FRAME)) ||
+ (multicast && nla_put_flag(msg, NL80211_ATTR_RECEIVE_MULTICAST)) ||
+ nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE, type) ||
+ nla_put(msg, NL80211_ATTR_FRAME_MATCH, match_len, match)) {
+ nlmsg_free(msg);
+ return -1;
+ }
+
+ ret = send_and_recv(drv->global, nl_handle, msg, NULL, NULL,
+ NULL, 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);
+ }
+ return ret;
+}
+
+
+static int nl80211_alloc_mgmt_handle(struct i802_bss *bss)
+{
+ if (bss->nl_mgmt) {
+ wpa_printf(MSG_DEBUG, "nl80211: Mgmt reporting "
+ "already on! (nl_mgmt=%p)", bss->nl_mgmt);
+ return -1;
+ }
+
+ bss->nl_mgmt = nl_create_handle(bss->nl_cb, "mgmt");
+ if (bss->nl_mgmt == NULL)
+ return -1;
+
+ return 0;
+}
+
+
+static void nl80211_mgmt_handle_register_eloop(struct i802_bss *bss)
+{
+ nl80211_register_eloop_read(&bss->nl_mgmt,
+ wpa_driver_nl80211_event_receive,
+ bss->nl_cb, 0);
+}
+
+
+static int nl80211_register_action_frame(struct i802_bss *bss,
+ const u8 *match, size_t match_len)
+{
+ u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_ACTION << 4);
+ return nl80211_register_frame(bss, bss->nl_mgmt,
+ type, match, match_len, false);
+}
+
+
+static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_AUTH << 4);
+ int ret = 0;
+
+ if (nl80211_alloc_mgmt_handle(bss))
+ return -1;
+ wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with non-AP "
+ "handle %p", bss->nl_mgmt);
+
+ if (drv->nlmode == NL80211_IFTYPE_ADHOC) {
+ /* register for any AUTH message */
+ nl80211_register_frame(bss, bss->nl_mgmt, type, NULL, 0, false);
+ } else if ((drv->capa.flags & WPA_DRIVER_FLAGS_SAE) &&
+ !(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) {
+ /* register for SAE Authentication frames */
+ nl80211_register_frame(bss, bss->nl_mgmt, type,
+ (u8 *) "\x03\x00", 2, false);
+ }
+
+#ifdef CONFIG_PASN
+ /* register for PASN Authentication frames */
+ if (nl80211_register_frame(bss, bss->nl_mgmt, type,
+ (u8 *) "\x07\x00", 2, false))
+ ret = -1;
+#endif /* CONFIG_PASN */
+
+#ifdef CONFIG_INTERWORKING
+ /* QoS Map Configure */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x01\x04", 2) < 0)
+ ret = -1;
+#endif /* CONFIG_INTERWORKING */
+#if defined(CONFIG_P2P) || defined(CONFIG_INTERWORKING) || defined(CONFIG_DPP)
+ /* GAS Initial Request */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0a", 2) < 0)
+ ret = -1;
+ /* GAS Initial Response */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0b", 2) < 0)
+ ret = -1;
+ /* GAS Comeback Request */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0c", 2) < 0)
+ ret = -1;
+ /* GAS Comeback Response */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0d", 2) < 0)
+ ret = -1;
+ /* Protected GAS Initial Request */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x09\x0a", 2) < 0)
+ ret = -1;
+ /* Protected GAS Initial Response */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x09\x0b", 2) < 0)
+ ret = -1;
+ /* Protected GAS Comeback Request */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x09\x0c", 2) < 0)
+ ret = -1;
+ /* Protected GAS Comeback Response */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x09\x0d", 2) < 0)
+ ret = -1;
+#endif /* CONFIG_P2P || CONFIG_INTERWORKING || CONFIG_DPP */
+#ifdef CONFIG_P2P
+ /* P2P Public Action */
+ if (nl80211_register_action_frame(bss,
+ (u8 *) "\x04\x09\x50\x6f\x9a\x09",
+ 6) < 0)
+ ret = -1;
+ /* P2P Action */
+ if (nl80211_register_action_frame(bss,
+ (u8 *) "\x7f\x50\x6f\x9a\x09",
+ 5) < 0)
+ ret = -1;
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_DPP
+ /* DPP Public Action */
+ if (nl80211_register_action_frame(bss,
+ (u8 *) "\x04\x09\x50\x6f\x9a\x1a",
+ 6) < 0)
+ ret = -1;
+#endif /* CONFIG_DPP */
+#ifdef CONFIG_OCV
+ /* SA Query Request */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x08\x00", 2) < 0)
+ ret = -1;
+#endif /* CONFIG_OCV */
+ /* SA Query Response */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x08\x01", 2) < 0)
+ ret = -1;
+#ifdef CONFIG_TDLS
+ if ((drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) {
+ /* TDLS Discovery Response */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0e", 2) <
+ 0)
+ ret = -1;
+ }
+#endif /* CONFIG_TDLS */
+#ifdef CONFIG_FST
+ /* FST Action frames */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x12", 1) < 0)
+ ret = -1;
+#endif /* CONFIG_FST */
+
+ /* FT Action frames */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x06", 1) < 0)
+ ret = -1;
+ else if (!drv->has_driver_key_mgmt) {
+ int i;
+
+ /* Update supported AKMs only if the driver doesn't advertize
+ * any AKM capabilities. */
+ drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT |
+ WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK;
+
+ /* Update per interface supported AKMs */
+ for (i = 0; i < WPA_IF_MAX; i++)
+ drv->capa.key_mgmt_iftype[i] = drv->capa.key_mgmt;
+ }
+
+ /* WNM - BSS Transition Management Request */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x07", 2) < 0)
+ ret = -1;
+ /* WNM-Sleep Mode Response */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x11", 2) < 0)
+ ret = -1;
+#ifdef CONFIG_WNM
+ /* WNM - Collocated Interference Request */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x0b", 2) < 0)
+ ret = -1;
+#endif /* CONFIG_WNM */
+
+#ifdef CONFIG_HS20
+ /* WNM-Notification */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x1a", 2) < 0)
+ ret = -1;
+#endif /* CONFIG_HS20 */
+
+ /* WMM-AC ADDTS Response */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x11\x01", 2) < 0)
+ ret = -1;
+
+ /* WMM-AC DELTS */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x11\x02", 2) < 0)
+ ret = -1;
+
+ /* Radio Measurement - Neighbor Report Response */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x05\x05", 2) < 0)
+ ret = -1;
+
+ /* Radio Measurement - Radio Measurement Request */
+ if (!drv->no_rrm &&
+ nl80211_register_action_frame(bss, (u8 *) "\x05\x00", 2) < 0)
+ ret = -1;
+
+ /* Radio Measurement - Link Measurement Request */
+ if ((drv->capa.rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION) &&
+ (nl80211_register_action_frame(bss, (u8 *) "\x05\x02", 2) < 0))
+ ret = -1;
+
+ /* Robust AV SCS Response */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x13\x01", 2) < 0)
+ ret = -1;
+
+ /* Robust AV MSCS Response */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x13\x05", 2) < 0)
+ ret = -1;
+
+ /* Protected QoS Management Action frame */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x7e\x50\x6f\x9a\x1a",
+ 5) < 0)
+ ret = -1;
+
+ nl80211_mgmt_handle_register_eloop(bss);
+
+ return ret;
+}
+
+
+static int nl80211_mgmt_subscribe_mesh(struct i802_bss *bss)
+{
+ int ret = 0;
+
+ if (nl80211_alloc_mgmt_handle(bss))
+ return -1;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Subscribe to mgmt frames with mesh handle %p",
+ bss->nl_mgmt);
+
+ /* Auth frames for mesh SAE */
+ if (nl80211_register_frame(bss, bss->nl_mgmt,
+ (WLAN_FC_TYPE_MGMT << 2) |
+ (WLAN_FC_STYPE_AUTH << 4),
+ NULL, 0, false) < 0)
+ ret = -1;
+
+ /* Mesh peering open */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x0f\x01", 2) < 0)
+ ret = -1;
+ /* Mesh peering confirm */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x0f\x02", 2) < 0)
+ ret = -1;
+ /* Mesh peering close */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x0f\x03", 2) < 0)
+ ret = -1;
+
+ nl80211_mgmt_handle_register_eloop(bss);
+
+ return ret;
+}
+
+
+static int nl80211_register_spurious_class3(struct i802_bss *bss)
+{
+ struct nl_msg *msg;
+ int ret;
+
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_UNEXPECTED_FRAME);
+ ret = send_and_recv(bss->drv->global, bss->nl_mgmt, msg, NULL, NULL,
+ NULL, NULL);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Register spurious class3 "
+ "failed: ret=%d (%s)",
+ ret, strerror(-ret));
+ }
+ return ret;
+}
+
+
+static int nl80211_action_subscribe_ap(struct i802_bss *bss)
+{
+ int ret = 0;
+
+ /* Public Action frames */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x04", 1) < 0)
+ ret = -1;
+ /* RRM Measurement Report */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x05\x01", 2) < 0)
+ ret = -1;
+ /* RRM Link Measurement Report */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x05\x03", 2) < 0)
+ ret = -1;
+ /* RRM Neighbor Report Request */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x05\x04", 2) < 0)
+ ret = -1;
+ /* FT Action frames */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x06", 1) < 0)
+ ret = -1;
+ /* SA Query */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x08", 1) < 0)
+ ret = -1;
+ /* Protected Dual of Public Action */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x09", 1) < 0)
+ ret = -1;
+ /* WNM */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x0a", 1) < 0)
+ ret = -1;
+ /* WMM */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x11", 1) < 0)
+ ret = -1;
+#ifdef CONFIG_FST
+ /* FST Action frames */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x12", 1) < 0)
+ ret = -1;
+#endif /* CONFIG_FST */
+ /* Vendor-specific */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x7f", 1) < 0)
+ ret = -1;
+
+ return ret;
+}
+
+
+static int nl80211_mgmt_subscribe_ap(struct i802_bss *bss)
+{
+ static const int stypes[] = {
+ WLAN_FC_STYPE_AUTH,
+ WLAN_FC_STYPE_ASSOC_REQ,
+ WLAN_FC_STYPE_REASSOC_REQ,
+ WLAN_FC_STYPE_DISASSOC,
+ WLAN_FC_STYPE_DEAUTH,
+ WLAN_FC_STYPE_PROBE_REQ,
+/* Beacon doesn't work as mac80211 doesn't currently allow
+ * it, but it wouldn't really be the right thing anyway as
+ * it isn't per interface ... maybe just dump the scan
+ * results periodically for OLBC?
+ */
+ /* WLAN_FC_STYPE_BEACON, */
+ };
+ unsigned int i;
+
+ if (nl80211_alloc_mgmt_handle(bss))
+ return -1;
+ wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with AP "
+ "handle %p", bss->nl_mgmt);
+
+ for (i = 0; i < ARRAY_SIZE(stypes); i++) {
+ if (nl80211_register_frame(bss, bss->nl_mgmt,
+ (WLAN_FC_TYPE_MGMT << 2) |
+ (stypes[i] << 4),
+ NULL, 0, false) < 0) {
+ goto out_err;
+ }
+ }
+
+ if (nl80211_action_subscribe_ap(bss))
+ goto out_err;
+
+ if (nl80211_register_spurious_class3(bss))
+ goto out_err;
+
+ nl80211_mgmt_handle_register_eloop(bss);
+ return 0;
+
+out_err:
+ nl_destroy_handles(&bss->nl_mgmt);
+ return -1;
+}
+
+
+static int nl80211_mgmt_subscribe_ap_dev_sme(struct i802_bss *bss)
+{
+ if (nl80211_alloc_mgmt_handle(bss))
+ return -1;
+ wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with AP "
+ "handle %p (device SME)", bss->nl_mgmt);
+
+ if (nl80211_action_subscribe_ap(bss))
+ goto out_err;
+
+ if (bss->drv->device_ap_sme) {
+ u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_AUTH << 4);
+
+ /* Register for all Authentication frames */
+ if (nl80211_register_frame(bss, bss->nl_mgmt, type, NULL, 0,
+ false) < 0)
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Failed to subscribe to handle Authentication frames - SAE offload may not work");
+ }
+
+ nl80211_mgmt_handle_register_eloop(bss);
+ return 0;
+
+out_err:
+ nl_destroy_handles(&bss->nl_mgmt);
+ return -1;
+}
+
+
+static void nl80211_mgmt_unsubscribe(struct i802_bss *bss, const char *reason)
+{
+ if (bss->nl_mgmt == NULL)
+ return;
+ wpa_printf(MSG_DEBUG, "nl80211: Unsubscribe mgmt frames handle %p "
+ "(%s)", bss->nl_mgmt, reason);
+ nl80211_destroy_eloop_handle(&bss->nl_mgmt, 0);
+
+ nl80211_put_wiphy_data_ap(bss);
+}
+
+
+static void wpa_driver_nl80211_send_rfkill(void *eloop_ctx, void *timeout_ctx)
+{
+ wpa_supplicant_event(timeout_ctx, EVENT_INTERFACE_DISABLED, NULL);
+}
+
+
+static void nl80211_del_p2pdev(struct i802_bss *bss)
+{
+ struct nl_msg *msg;
+ int ret;
+
+ msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_DEL_INTERFACE);
+ ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
+
+ wpa_printf(MSG_DEBUG, "nl80211: Delete P2P Device %s (0x%llx): %s",
+ bss->ifname, (long long unsigned int) bss->wdev_id,
+ strerror(-ret));
+}
+
+
+static int nl80211_set_p2pdev(struct i802_bss *bss, int start)
+{
+ struct nl_msg *msg;
+ int ret;
+
+ msg = nl80211_cmd_msg(bss, 0, start ? NL80211_CMD_START_P2P_DEVICE :
+ NL80211_CMD_STOP_P2P_DEVICE);
+ ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
+
+ wpa_printf(MSG_DEBUG, "nl80211: %s P2P Device %s (0x%llx): %s",
+ start ? "Start" : "Stop",
+ bss->ifname, (long long unsigned int) bss->wdev_id,
+ strerror(-ret));
+ return ret;
+}
+
+
+static int i802_set_iface_flags(struct i802_bss *bss, int up)
+{
+ enum nl80211_iftype nlmode;
+
+ nlmode = nl80211_get_ifmode(bss);
+ if (nlmode != NL80211_IFTYPE_P2P_DEVICE) {
+ return linux_set_iface_flags(bss->drv->global->ioctl_sock,
+ bss->ifname, up);
+ }
+
+ /* P2P Device has start/stop which is equivalent */
+ return nl80211_set_p2pdev(bss, up);
+}
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+static int qca_vendor_test_cmd_handler(struct nl_msg *msg, void *arg)
+{
+ /* struct wpa_driver_nl80211_data *drv = arg; */
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: QCA vendor test command response received");
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+ if (!tb[NL80211_ATTR_VENDOR_DATA]) {
+ wpa_printf(MSG_DEBUG, "nl80211: No vendor data attribute");
+ return NL_SKIP;
+ }
+
+ wpa_hexdump(MSG_DEBUG,
+ "nl80211: Received QCA vendor test command response",
+ nla_data(tb[NL80211_ATTR_VENDOR_DATA]),
+ nla_len(tb[NL80211_ATTR_VENDOR_DATA]));
+
+ return NL_SKIP;
+}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+static void qca_vendor_test(struct wpa_driver_nl80211_data *drv)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+ struct nl_msg *msg;
+ struct nlattr *params;
+ int ret;
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_TEST) ||
+ !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+ nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_TEST, 123)) {
+ nlmsg_free(msg);
+ return;
+ }
+ nla_nest_end(msg, params);
+
+ ret = send_and_recv_msgs(drv, msg, qca_vendor_test_cmd_handler, drv,
+ NULL, NULL);
+ wpa_printf(MSG_DEBUG,
+ "nl80211: QCA vendor test command returned %d (%s)",
+ ret, strerror(-ret));
+#endif /* CONFIG_TESTING_OPTIONS */
+}
+
+
+static int
+wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv,
+ const u8 *set_addr, int first,
+ const char *driver_params)
+{
+ struct i802_bss *bss = drv->first_bss;
+ int send_rfkill_event = 0;
+ enum nl80211_iftype nlmode;
+
+ drv->ifindex = if_nametoindex(bss->ifname);
+ bss->ifindex = drv->ifindex;
+ bss->wdev_id = drv->global->if_add_wdevid;
+ bss->wdev_id_set = drv->global->if_add_wdevid_set;
+
+ bss->if_dynamic = drv->ifindex == drv->global->if_add_ifindex;
+ bss->if_dynamic = bss->if_dynamic || drv->global->if_add_wdevid_set;
+ drv->global->if_add_wdevid_set = 0;
+
+ if (!bss->if_dynamic && nl80211_get_ifmode(bss) == NL80211_IFTYPE_AP)
+ bss->static_ap = 1;
+
+ if (first &&
+ nl80211_get_ifmode(bss) != NL80211_IFTYPE_P2P_DEVICE &&
+ linux_iface_up(drv->global->ioctl_sock, bss->ifname) > 0)
+ drv->start_iface_up = 1;
+
+ if (wpa_driver_nl80211_capa(drv))
+ return -1;
+
+ if (driver_params && nl80211_set_param(bss, driver_params) < 0)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "nl80211: interface %s in phy %s",
+ bss->ifname, drv->phyname);
+
+ if (set_addr &&
+ (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0) ||
+ linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
+ set_addr)))
+ return -1;
+
+ if (first && nl80211_get_ifmode(bss) == NL80211_IFTYPE_STATION)
+ drv->start_mode_sta = 1;
+
+ if (drv->hostapd || bss->static_ap)
+ nlmode = NL80211_IFTYPE_AP;
+ else if (bss->if_dynamic ||
+ nl80211_get_ifmode(bss) == NL80211_IFTYPE_MESH_POINT)
+ nlmode = nl80211_get_ifmode(bss);
+ else
+ nlmode = NL80211_IFTYPE_STATION;
+
+ if (wpa_driver_nl80211_set_mode(bss, nlmode) < 0) {
+ wpa_printf(MSG_ERROR, "nl80211: Could not configure driver mode");
+ return -1;
+ }
+
+ if (nlmode == NL80211_IFTYPE_P2P_DEVICE)
+ nl80211_get_macaddr(bss);
+
+ wpa_driver_nl80211_drv_init_rfkill(drv);
+
+ if (!rfkill_is_blocked(drv->rfkill)) {
+ int ret = i802_set_iface_flags(bss, 1);
+ if (ret) {
+ wpa_printf(MSG_ERROR, "nl80211: Could not set "
+ "interface '%s' UP", bss->ifname);
+ return ret;
+ }
+
+ if (is_p2p_net_interface(nlmode))
+ nl80211_disable_11b_rates(bss->drv,
+ bss->drv->ifindex, 1);
+
+ if (nlmode == NL80211_IFTYPE_P2P_DEVICE)
+ return ret;
+ } else {
+ wpa_printf(MSG_DEBUG, "nl80211: Could not yet enable "
+ "interface '%s' due to rfkill", bss->ifname);
+ if (nlmode != NL80211_IFTYPE_P2P_DEVICE)
+ drv->if_disabled = 1;
+
+ send_rfkill_event = 1;
+ }
+
+ if (!drv->hostapd && nlmode != NL80211_IFTYPE_P2P_DEVICE)
+ netlink_send_oper_ifla(drv->global->netlink, drv->ifindex,
+ 1, IF_OPER_DORMANT);
+
+ if (nlmode != NL80211_IFTYPE_P2P_DEVICE) {
+ if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
+ bss->addr))
+ return -1;
+ os_memcpy(drv->perm_addr, bss->addr, ETH_ALEN);
+ }
+
+ if (send_rfkill_event) {
+ eloop_register_timeout(0, 0, wpa_driver_nl80211_send_rfkill,
+ drv, drv->ctx);
+ }
+
+ if (drv->vendor_cmd_test_avail)
+ qca_vendor_test(drv);
+
+ return 0;
+}
+
+
+static int wpa_driver_nl80211_del_beacon(struct i802_bss *bss,
+ struct i802_link *link)
+{
+ struct nl_msg *msg;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ if (!link->beacon_set)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Remove beacon (ifindex=%d)",
+ bss->ifindex);
+ link->beacon_set = 0;
+ link->freq = 0;
+
+ nl80211_put_wiphy_data_ap(bss);
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_DEL_BEACON);
+ if (!msg)
+ return -ENOBUFS;
+
+ if (link->link_id != NL80211_DRV_LINK_ID_NA) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: MLD: stop beaconing on link=%u",
+ link->link_id);
+
+ if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID,
+ link->link_id)) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+ }
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+}
+
+
+static void wpa_driver_nl80211_del_beacon_all(struct i802_bss *bss)
+{
+ unsigned int i;
+
+ for (i = 0; i < bss->n_links; i++)
+ wpa_driver_nl80211_del_beacon(bss, &bss->links[i]);
+}
+
+
+/**
+ * wpa_driver_nl80211_deinit - Deinitialize nl80211 driver interface
+ * @bss: 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(struct i802_bss *bss)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ unsigned int i;
+
+ wpa_printf(MSG_INFO, "nl80211: deinit ifname=%s disabled_11b_rates=%d",
+ bss->ifname, drv->disabled_11b_rates);
+
+ bss->in_deinit = 1;
+ if (drv->data_tx_status)
+ eloop_unregister_read_sock(drv->eapol_tx_sock);
+ if (drv->eapol_tx_sock >= 0)
+ close(drv->eapol_tx_sock);
+
+ if (bss->nl_preq)
+ wpa_driver_nl80211_probe_req_report(bss, 0);
+ if (bss->added_if_into_bridge) {
+ if (linux_br_del_if(drv->global->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 (drv->rtnl_sk)
+ nl_socket_free(drv->rtnl_sk);
+
+ if (bss->added_bridge) {
+ if (linux_set_iface_flags(drv->global->ioctl_sock, bss->brname,
+ 0) < 0)
+ wpa_printf(MSG_INFO,
+ "nl80211: Could not set bridge %s down",
+ bss->brname);
+ if (linux_br_del(drv->global->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 (is_ap_interface(drv->nlmode))
+ wpa_driver_nl80211_del_beacon_all(bss);
+
+ 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);
+
+ if (drv->disabled_11b_rates)
+ nl80211_disable_11b_rates(drv, drv->ifindex, 0);
+
+ netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, 0,
+ IF_OPER_UP);
+ eloop_cancel_timeout(wpa_driver_nl80211_send_rfkill, drv, drv->ctx);
+ rfkill_deinit(drv->rfkill);
+
+ eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
+
+ if (!drv->start_iface_up)
+ (void) i802_set_iface_flags(bss, 0);
+
+ if (drv->addr_changed) {
+ if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname,
+ 0) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Could not set interface down to restore permanent MAC address");
+ }
+ if (linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
+ drv->perm_addr) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Could not restore permanent MAC address");
+ }
+ }
+
+ if (drv->nlmode != NL80211_IFTYPE_P2P_DEVICE) {
+ if (drv->start_mode_sta)
+ wpa_driver_nl80211_set_mode(bss,
+ NL80211_IFTYPE_STATION);
+ nl80211_mgmt_unsubscribe(bss, "deinit");
+ } else {
+ nl80211_mgmt_unsubscribe(bss, "deinit");
+ nl80211_del_p2pdev(bss);
+ }
+
+ nl80211_destroy_bss(drv->first_bss);
+
+ os_free(drv->filter_ssids);
+
+ os_free(drv->auth_ie);
+ os_free(drv->auth_data);
+
+ if (drv->in_interface_list)
+ dl_list_del(&drv->list);
+
+ os_free(drv->extended_capa);
+ os_free(drv->extended_capa_mask);
+ for (i = 0; i < drv->num_iface_capa; i++) {
+ os_free(drv->iface_capa[i].ext_capa);
+ os_free(drv->iface_capa[i].ext_capa_mask);
+ }
+ os_free(drv->first_bss);
+#ifdef CONFIG_DRIVER_NL80211_QCA
+ os_free(drv->pending_roam_data);
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+ os_free(drv);
+}
+
+
+static u32 wpa_alg_to_cipher_suite(enum wpa_alg alg, size_t key_len)
+{
+ switch (alg) {
+ case WPA_ALG_WEP:
+ if (key_len == 5)
+ return RSN_CIPHER_SUITE_WEP40;
+ return RSN_CIPHER_SUITE_WEP104;
+ case WPA_ALG_TKIP:
+ return RSN_CIPHER_SUITE_TKIP;
+ case WPA_ALG_CCMP:
+ return RSN_CIPHER_SUITE_CCMP;
+ case WPA_ALG_GCMP:
+ return RSN_CIPHER_SUITE_GCMP;
+ case WPA_ALG_CCMP_256:
+ return RSN_CIPHER_SUITE_CCMP_256;
+ case WPA_ALG_GCMP_256:
+ return RSN_CIPHER_SUITE_GCMP_256;
+ case WPA_ALG_BIP_CMAC_128:
+ return RSN_CIPHER_SUITE_AES_128_CMAC;
+ case WPA_ALG_BIP_GMAC_128:
+ return RSN_CIPHER_SUITE_BIP_GMAC_128;
+ case WPA_ALG_BIP_GMAC_256:
+ return RSN_CIPHER_SUITE_BIP_GMAC_256;
+ case WPA_ALG_BIP_CMAC_256:
+ return RSN_CIPHER_SUITE_BIP_CMAC_256;
+ case WPA_ALG_SMS4:
+ return RSN_CIPHER_SUITE_SMS4;
+ case WPA_ALG_KRK:
+ return RSN_CIPHER_SUITE_KRK;
+ case WPA_ALG_NONE:
+ wpa_printf(MSG_ERROR, "nl80211: Unexpected encryption algorithm %d",
+ alg);
+ return 0;
+ }
+
+ wpa_printf(MSG_ERROR, "nl80211: Unsupported encryption algorithm %d",
+ alg);
+ return 0;
+}
+
+
+static u32 wpa_cipher_to_cipher_suite(unsigned int cipher)
+{
+ switch (cipher) {
+ case WPA_CIPHER_CCMP_256:
+ return RSN_CIPHER_SUITE_CCMP_256;
+ case WPA_CIPHER_GCMP_256:
+ return RSN_CIPHER_SUITE_GCMP_256;
+ case WPA_CIPHER_CCMP:
+ return RSN_CIPHER_SUITE_CCMP;
+ case WPA_CIPHER_GCMP:
+ return RSN_CIPHER_SUITE_GCMP;
+ case WPA_CIPHER_TKIP:
+ return RSN_CIPHER_SUITE_TKIP;
+ case WPA_CIPHER_WEP104:
+ return RSN_CIPHER_SUITE_WEP104;
+ case WPA_CIPHER_WEP40:
+ return RSN_CIPHER_SUITE_WEP40;
+ case WPA_CIPHER_GTK_NOT_USED:
+ return RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED;
+ default:
+ return 0;
+ }
+}
+
+
+static int wpa_cipher_to_cipher_suites(unsigned int ciphers, u32 suites[],
+ int max_suites)
+{
+ int num_suites = 0;
+
+ if (num_suites < max_suites && ciphers & WPA_CIPHER_CCMP_256)
+ suites[num_suites++] = RSN_CIPHER_SUITE_CCMP_256;
+ if (num_suites < max_suites && ciphers & WPA_CIPHER_GCMP_256)
+ suites[num_suites++] = RSN_CIPHER_SUITE_GCMP_256;
+ if (num_suites < max_suites && ciphers & WPA_CIPHER_CCMP)
+ suites[num_suites++] = RSN_CIPHER_SUITE_CCMP;
+ if (num_suites < max_suites && ciphers & WPA_CIPHER_GCMP)
+ suites[num_suites++] = RSN_CIPHER_SUITE_GCMP;
+ if (num_suites < max_suites && ciphers & WPA_CIPHER_TKIP)
+ suites[num_suites++] = RSN_CIPHER_SUITE_TKIP;
+ if (num_suites < max_suites && ciphers & WPA_CIPHER_WEP104)
+ suites[num_suites++] = RSN_CIPHER_SUITE_WEP104;
+ if (num_suites < max_suites && ciphers & WPA_CIPHER_WEP40)
+ suites[num_suites++] = RSN_CIPHER_SUITE_WEP40;
+
+ return num_suites;
+}
+
+
+static int wpa_key_mgmt_to_suites(unsigned int key_mgmt_suites, u32 suites[],
+ int max_suites)
+{
+ int num_suites = 0;
+
+#define __AKM(a, b) \
+ if (num_suites < max_suites && \
+ (key_mgmt_suites & (WPA_KEY_MGMT_ ## a))) \
+ suites[num_suites++] = (RSN_AUTH_KEY_MGMT_ ## b)
+ __AKM(IEEE8021X, UNSPEC_802_1X);
+ __AKM(PSK, PSK_OVER_802_1X);
+ __AKM(FT_IEEE8021X, FT_802_1X);
+ __AKM(FT_PSK, FT_PSK);
+ __AKM(IEEE8021X_SHA256, 802_1X_SHA256);
+ __AKM(PSK_SHA256, PSK_SHA256);
+ __AKM(SAE, SAE);
+ __AKM(SAE_EXT_KEY, SAE_EXT_KEY);
+ __AKM(FT_SAE, FT_SAE);
+ __AKM(FT_SAE_EXT_KEY, FT_SAE_EXT_KEY);
+ __AKM(CCKM, CCKM);
+ __AKM(OSEN, OSEN);
+ __AKM(IEEE8021X_SUITE_B, 802_1X_SUITE_B);
+ __AKM(IEEE8021X_SUITE_B_192, 802_1X_SUITE_B_192);
+ __AKM(FILS_SHA256, FILS_SHA256);
+ __AKM(FILS_SHA384, FILS_SHA384);
+ __AKM(FT_FILS_SHA256, FT_FILS_SHA256);
+ __AKM(FT_FILS_SHA384, FT_FILS_SHA384);
+ __AKM(OWE, OWE);
+ __AKM(DPP, DPP);
+ __AKM(FT_IEEE8021X_SHA384, FT_802_1X_SHA384);
+#undef __AKM
+
+ return num_suites;
+}
+
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+static int issue_key_mgmt_set_key(struct wpa_driver_nl80211_data *drv,
+ const u8 *key, size_t key_len)
+{
+ struct nl_msg *msg;
+ int ret;
+
+ if (!(drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD))
+ return 0;
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY) ||
+ nla_put(msg, NL80211_ATTR_VENDOR_DATA, key_len, key)) {
+ nl80211_nlmsg_clear(msg);
+ nlmsg_free(msg);
+ return -1;
+ }
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Key management set key failed: ret=%d (%s)",
+ ret, strerror(-ret));
+ }
+
+ return ret;
+}
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+
+static int nl80211_set_pmk(struct wpa_driver_nl80211_data *drv,
+ const u8 *key, size_t key_len,
+ const u8 *addr)
+{
+ struct nl_msg *msg = NULL;
+ int ret;
+
+ /*
+ * If the authenticator address is not set, assume it is
+ * the current BSSID.
+ */
+ if (!addr && drv->associated)
+ addr = drv->bssid;
+ else if (!addr)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Set PMK to the driver for " MACSTR,
+ MAC2STR(addr));
+ wpa_hexdump_key(MSG_DEBUG, "nl80211: PMK", key, key_len);
+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_PMK);
+ if (!msg ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
+ nla_put(msg, NL80211_ATTR_PMK, key_len, key)) {
+ nl80211_nlmsg_clear(msg);
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Set PMK failed: ret=%d (%s)",
+ ret, strerror(-ret));
+ }
+
+ return ret;
+}
+
+
+static int wpa_driver_nl80211_set_key(struct i802_bss *bss,
+ struct wpa_driver_set_key_params *params)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int ifindex;
+ struct nl_msg *msg;
+ struct nl_msg *key_msg;
+ int ret;
+ int skip_set_key = 1;
+ const char *ifname = params->ifname;
+ enum wpa_alg alg = params->alg;
+ const u8 *addr = params->addr;
+ int key_idx = params->key_idx;
+ int set_tx = params->set_tx;
+ const u8 *seq = params->seq;
+ size_t seq_len = params->seq_len;
+ const u8 *key = params->key;
+ size_t key_len = params->key_len;
+ int vlan_id = params->vlan_id;
+ enum key_flag key_flag = params->key_flag;
+ int link_id = params->link_id;
+
+ /* Ignore for P2P Device */
+ if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE)
+ return 0;
+
+ ifindex = if_nametoindex(ifname);
+ wpa_printf(MSG_DEBUG, "%s: ifindex=%d (%s) alg=%d addr=%p key_idx=%d "
+ "set_tx=%d seq_len=%lu key_len=%lu key_flag=0x%x link_id=%d",
+ __func__, ifindex, ifname, alg, addr, key_idx, set_tx,
+ (unsigned long) seq_len, (unsigned long) key_len, key_flag,
+ link_id);
+
+ if (check_key_flag(key_flag)) {
+ wpa_printf(MSG_DEBUG, "%s: invalid key_flag", __func__);
+ return -EINVAL;
+ }
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+ if ((key_flag & KEY_FLAG_PMK) &&
+ (drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD)) {
+ wpa_printf(MSG_DEBUG, "%s: calling issue_key_mgmt_set_key",
+ __func__);
+ ret = issue_key_mgmt_set_key(drv, key, key_len);
+ return ret;
+ }
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+ if (key_flag & KEY_FLAG_PMK) {
+ if (drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X)
+ return nl80211_set_pmk(drv, key, key_len, addr);
+ /* The driver does not have any offload mechanism for PMK, so
+ * there is no need to configure this key. */
+ return 0;
+ }
+
+ ret = -ENOBUFS;
+ key_msg = nlmsg_alloc();
+ if (!key_msg)
+ return ret;
+
+ if ((key_flag & KEY_FLAG_PAIRWISE_MASK) ==
+ KEY_FLAG_PAIRWISE_RX_TX_MODIFY) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: SET_KEY (pairwise RX/TX modify)");
+ msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_SET_KEY);
+ if (!msg)
+ goto fail2;
+ } else if (alg == WPA_ALG_NONE && (key_flag & KEY_FLAG_RX_TX)) {
+ wpa_printf(MSG_DEBUG, "%s: invalid key_flag to delete key",
+ __func__);
+ ret = -EINVAL;
+ goto fail2;
+ } else if (alg == WPA_ALG_NONE) {
+ wpa_printf(MSG_DEBUG, "nl80211: DEL_KEY");
+ msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_DEL_KEY);
+ if (!msg)
+ goto fail2;
+ } else {
+ u32 suite;
+
+ suite = wpa_alg_to_cipher_suite(alg, key_len);
+ if (!suite) {
+ ret = -EINVAL;
+ goto fail2;
+ }
+ wpa_printf(MSG_DEBUG, "nl80211: NEW_KEY");
+ msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_NEW_KEY);
+ if (!msg)
+ goto fail2;
+ if (nla_put(key_msg, NL80211_KEY_DATA, key_len, key) ||
+ nla_put_u32(key_msg, NL80211_KEY_CIPHER, suite))
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG, "nl80211: KEY_DATA", key, key_len);
+
+ if (seq && seq_len) {
+ if (nla_put(key_msg, NL80211_KEY_SEQ, seq_len, seq))
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "nl80211: KEY_SEQ",
+ seq, seq_len);
+ }
+ }
+
+ if (addr && !is_broadcast_ether_addr(addr)) {
+ wpa_printf(MSG_DEBUG, " addr=" MACSTR, MAC2STR(addr));
+ if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr))
+ goto fail;
+
+ if ((key_flag & KEY_FLAG_PAIRWISE_MASK) ==
+ KEY_FLAG_PAIRWISE_RX ||
+ (key_flag & KEY_FLAG_PAIRWISE_MASK) ==
+ KEY_FLAG_PAIRWISE_RX_TX_MODIFY) {
+ if (nla_put_u8(key_msg, NL80211_KEY_MODE,
+ key_flag == KEY_FLAG_PAIRWISE_RX ?
+ NL80211_KEY_NO_TX : NL80211_KEY_SET_TX))
+ goto fail;
+ } else if ((key_flag & KEY_FLAG_GROUP_MASK) ==
+ KEY_FLAG_GROUP_RX) {
+ wpa_printf(MSG_DEBUG, " RSN IBSS RX GTK");
+ if (nla_put_u32(key_msg, NL80211_KEY_TYPE,
+ NL80211_KEYTYPE_GROUP))
+ goto fail;
+ } else if (!(key_flag & KEY_FLAG_PAIRWISE)) {
+ wpa_printf(MSG_DEBUG,
+ " key_flag missing PAIRWISE when setting a pairwise key");
+ ret = -EINVAL;
+ goto fail;
+ } else if (alg == WPA_ALG_WEP &&
+ (key_flag & KEY_FLAG_RX_TX) == KEY_FLAG_RX_TX) {
+ wpa_printf(MSG_DEBUG, " unicast WEP key");
+ skip_set_key = 0;
+ } else {
+ wpa_printf(MSG_DEBUG, " pairwise key");
+ }
+ } else if ((key_flag & KEY_FLAG_PAIRWISE) ||
+ !(key_flag & KEY_FLAG_GROUP)) {
+ wpa_printf(MSG_DEBUG,
+ " invalid key_flag for a broadcast key");
+ ret = -EINVAL;
+ goto fail;
+ } else {
+ wpa_printf(MSG_DEBUG, " broadcast key");
+ if (key_flag & KEY_FLAG_DEFAULT)
+ skip_set_key = 0;
+ }
+ if (nla_put_u8(key_msg, NL80211_KEY_IDX, key_idx) ||
+ nla_put_nested(msg, NL80211_ATTR_KEY, key_msg))
+ goto fail;
+ nl80211_nlmsg_clear(key_msg);
+ nlmsg_free(key_msg);
+ key_msg = NULL;
+
+ if (vlan_id && (drv->capa.flags & WPA_DRIVER_FLAGS_VLAN_OFFLOAD)) {
+ wpa_printf(MSG_DEBUG, "nl80211: VLAN ID %d", vlan_id);
+ if (nla_put_u16(msg, NL80211_ATTR_VLAN_ID, vlan_id))
+ goto fail;
+ }
+
+ if (link_id != -1) {
+ wpa_printf(MSG_DEBUG, "nl80211: Link ID %d", link_id);
+ if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id))
+ goto fail;
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, 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 key as default (below),
+ * we're done here.
+ */
+ if (ret || skip_set_key)
+ return ret;
+ wpa_printf(MSG_DEBUG, "nl80211: NL80211_CMD_SET_KEY - default key");
+
+ ret = -ENOBUFS;
+ key_msg = nlmsg_alloc();
+ if (!key_msg)
+ return ret;
+
+ msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_SET_KEY);
+ if (!msg)
+ goto fail2;
+ if (!key_msg ||
+ nla_put_u8(key_msg, NL80211_KEY_IDX, key_idx) ||
+ nla_put_flag(key_msg, wpa_alg_bip(alg) ?
+ (key_idx == 6 || key_idx == 7 ?
+ NL80211_KEY_DEFAULT_BEACON :
+ NL80211_KEY_DEFAULT_MGMT) :
+ NL80211_KEY_DEFAULT))
+ goto fail;
+ if (addr && is_broadcast_ether_addr(addr)) {
+ struct nlattr *types;
+
+ types = nla_nest_start(key_msg, NL80211_KEY_DEFAULT_TYPES);
+ if (!types ||
+ nla_put_flag(key_msg, NL80211_KEY_DEFAULT_TYPE_MULTICAST))
+ goto fail;
+ nla_nest_end(key_msg, types);
+ } else if (addr) {
+ struct nlattr *types;
+
+ types = nla_nest_start(key_msg, NL80211_KEY_DEFAULT_TYPES);
+ if (!types ||
+ nla_put_flag(key_msg, NL80211_KEY_DEFAULT_TYPE_UNICAST))
+ goto fail;
+ nla_nest_end(key_msg, types);
+ }
+
+ if (nla_put_nested(msg, NL80211_ATTR_KEY, key_msg))
+ goto fail;
+ nl80211_nlmsg_clear(key_msg);
+ nlmsg_free(key_msg);
+ key_msg = NULL;
+
+ if (vlan_id && (drv->capa.flags & WPA_DRIVER_FLAGS_VLAN_OFFLOAD)) {
+ wpa_printf(MSG_DEBUG, "nl80211: set_key default - VLAN ID %d",
+ vlan_id);
+ if (nla_put_u16(msg, NL80211_ATTR_VLAN_ID, vlan_id))
+ goto fail;
+ }
+
+ if (link_id != -1) {
+ wpa_printf(MSG_DEBUG, "nl80211: set_key default - Link ID %d",
+ link_id);
+ if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id))
+ goto fail;
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ if (ret)
+ wpa_printf(MSG_DEBUG,
+ "nl80211: set_key default failed; err=%d %s",
+ ret, strerror(-ret));
+ return ret;
+
+fail:
+ nl80211_nlmsg_clear(msg);
+ nlmsg_free(msg);
+fail2:
+ nl80211_nlmsg_clear(key_msg);
+ nlmsg_free(key_msg);
+ return ret;
+}
+
+
+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);
+ u32 suite;
+
+ if (!key_attr)
+ return -1;
+
+ suite = wpa_alg_to_cipher_suite(alg, key_len);
+ if (!suite)
+ return -1;
+
+ if (defkey && wpa_alg_bip(alg)) {
+ if (nla_put_flag(msg, NL80211_KEY_DEFAULT_MGMT))
+ return -1;
+ } else if (defkey) {
+ if (nla_put_flag(msg, NL80211_KEY_DEFAULT))
+ return -1;
+ }
+
+ if (nla_put_u8(msg, NL80211_KEY_IDX, key_idx) ||
+ nla_put_u32(msg, NL80211_KEY_CIPHER, suite) ||
+ (seq && seq_len &&
+ nla_put(msg, NL80211_KEY_SEQ, seq_len, seq)) ||
+ nla_put(msg, NL80211_KEY_DATA, key_len, key))
+ return -1;
+
+ nla_nest_end(msg, key_attr);
+
+ return 0;
+}
+
+
+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;
+
+ if (nla_put_flag(msg, NL80211_ATTR_PRIVACY))
+ return -ENOBUFS;
+
+ nl_keys = nla_nest_start(msg, NL80211_ATTR_KEYS);
+ if (!nl_keys)
+ return -ENOBUFS;
+
+ for (i = 0; i < 4; i++) {
+ if (!params->wep_key[i])
+ continue;
+
+ nl_key = nla_nest_start(msg, i);
+ if (!nl_key ||
+ nla_put(msg, NL80211_KEY_DATA, params->wep_key_len[i],
+ params->wep_key[i]) ||
+ nla_put_u32(msg, NL80211_KEY_CIPHER,
+ params->wep_key_len[i] == 5 ?
+ RSN_CIPHER_SUITE_WEP40 :
+ RSN_CIPHER_SUITE_WEP104) ||
+ nla_put_u8(msg, NL80211_KEY_IDX, i) ||
+ (i == params->wep_tx_keyidx &&
+ nla_put_flag(msg, NL80211_KEY_DEFAULT)))
+ return -ENOBUFS;
+
+ nla_nest_end(msg, nl_key);
+ }
+ nla_nest_end(msg, nl_keys);
+
+ return 0;
+}
+
+
+int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
+ const u8 *addr, int cmd, u16 reason_code,
+ int local_state_change,
+ struct i802_bss *bss)
+{
+ int ret;
+ struct nl_msg *msg;
+ struct nl_sock *nl_connect = get_connect_handle(bss);
+
+ if (!(msg = nl80211_drv_msg(drv, 0, cmd)) ||
+ nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason_code) ||
+ (addr && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) ||
+ (local_state_change &&
+ nla_put_flag(msg, NL80211_ATTR_LOCAL_STATE_CHANGE))) {
+ nlmsg_free(msg);
+ return -1;
+ }
+
+ if (nl_connect)
+ ret = send_and_recv(drv->global, nl_connect, msg,
+ process_bss_event, bss, NULL, NULL);
+ else
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ if (ret) {
+ wpa_dbg(drv->ctx, MSG_DEBUG,
+ "nl80211: MLME command failed: reason=%u ret=%d (%s)",
+ reason_code, ret, strerror(-ret));
+ }
+ return ret;
+}
+
+
+static int wpa_driver_nl80211_disconnect(struct wpa_driver_nl80211_data *drv,
+ u16 reason_code,
+ struct i802_bss *bss)
+{
+ int ret;
+ int drv_associated = drv->associated;
+
+ wpa_printf(MSG_DEBUG, "%s(reason_code=%d)", __func__, reason_code);
+ nl80211_mark_disconnected(drv);
+ /* Disconnect command doesn't need BSSID - it uses cached value */
+ ret = wpa_driver_nl80211_mlme(drv, NULL, NL80211_CMD_DISCONNECT,
+ reason_code, 0, bss);
+ /*
+ * For locally generated disconnect, supplicant already generates a
+ * DEAUTH event, so ignore the event from NL80211.
+ */
+ drv->ignore_next_local_disconnect = drv_associated && (ret == 0);
+
+ return ret;
+}
+
+
+static int wpa_driver_nl80211_deauthenticate(struct i802_bss *bss,
+ const u8 *addr, u16 reason_code)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int ret;
+ int drv_associated = drv->associated;
+
+ if (drv->nlmode == NL80211_IFTYPE_ADHOC) {
+ nl80211_mark_disconnected(drv);
+ return nl80211_leave_ibss(drv, 1);
+ }
+ if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) {
+ return wpa_driver_nl80211_disconnect(drv, reason_code, bss);
+ }
+ wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " reason_code=%d)",
+ __func__, MAC2STR(addr), reason_code);
+ nl80211_mark_disconnected(drv);
+ ret = wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DEAUTHENTICATE,
+ reason_code, 0, bss);
+ /*
+ * For locally generated deauthenticate, supplicant already generates a
+ * DEAUTH event, so ignore the event from NL80211.
+ */
+ drv->ignore_next_local_deauth = drv_associated && (ret == 0);
+
+ return ret;
+}
+
+
+static void nl80211_copy_auth_params(struct wpa_driver_nl80211_data *drv,
+ struct wpa_driver_auth_params *params)
+{
+ int i;
+
+ drv->auth_freq = params->freq;
+ drv->auth_alg = params->auth_alg;
+ drv->auth_wep_tx_keyidx = params->wep_tx_keyidx;
+ drv->auth_local_state_change = params->local_state_change;
+ drv->auth_p2p = params->p2p;
+
+ if (params->bssid)
+ os_memcpy(drv->auth_bssid_, params->bssid, ETH_ALEN);
+ else
+ os_memset(drv->auth_bssid_, 0, ETH_ALEN);
+
+ if (params->ssid) {
+ os_memcpy(drv->auth_ssid, params->ssid, params->ssid_len);
+ drv->auth_ssid_len = params->ssid_len;
+ } else
+ drv->auth_ssid_len = 0;
+
+
+ os_free(drv->auth_ie);
+ drv->auth_ie = NULL;
+ drv->auth_ie_len = 0;
+ if (params->ie) {
+ drv->auth_ie = os_malloc(params->ie_len);
+ if (drv->auth_ie) {
+ os_memcpy(drv->auth_ie, params->ie, params->ie_len);
+ drv->auth_ie_len = params->ie_len;
+ }
+ }
+
+ if (params->mld && params->ap_mld_addr) {
+ drv->auth_mld = params->mld;
+ drv->auth_mld_link_id = params->mld_link_id;
+ os_memcpy(drv->auth_ap_mld_addr, params->ap_mld_addr, ETH_ALEN);
+ } else {
+ drv->auth_mld = false;
+ drv->auth_mld_link_id = -1;
+ }
+
+ os_free(drv->auth_data);
+ drv->auth_data = NULL;
+ drv->auth_data_len = 0;
+ if (params->auth_data) {
+ drv->auth_data = os_memdup(params->auth_data,
+ params->auth_data_len);
+ if (drv->auth_data)
+ drv->auth_data_len = params->auth_data_len;
+ }
+
+ for (i = 0; i < 4; i++) {
+ if (params->wep_key[i] && params->wep_key_len[i] &&
+ params->wep_key_len[i] <= 16) {
+ os_memcpy(drv->auth_wep_key[i], params->wep_key[i],
+ params->wep_key_len[i]);
+ drv->auth_wep_key_len[i] = params->wep_key_len[i];
+ } else
+ drv->auth_wep_key_len[i] = 0;
+ }
+}
+
+
+static void nl80211_unmask_11b_rates(struct i802_bss *bss)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ if (is_p2p_net_interface(drv->nlmode) || !drv->disabled_11b_rates)
+ return;
+
+ /*
+ * Looks like we failed to unmask 11b rates previously. This could
+ * happen, e.g., if the interface was down at the point in time when a
+ * P2P group was terminated.
+ */
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Interface %s mode is for non-P2P, but 11b rates were disabled - re-enable them",
+ bss->ifname);
+ nl80211_disable_11b_rates(drv, drv->ifindex, 0);
+}
+
+
+static enum nl80211_auth_type get_nl_auth_type(int wpa_auth_alg)
+{
+ if (wpa_auth_alg & WPA_AUTH_ALG_OPEN)
+ return NL80211_AUTHTYPE_OPEN_SYSTEM;
+ if (wpa_auth_alg & WPA_AUTH_ALG_SHARED)
+ return NL80211_AUTHTYPE_SHARED_KEY;
+ if (wpa_auth_alg & WPA_AUTH_ALG_LEAP)
+ return NL80211_AUTHTYPE_NETWORK_EAP;
+ if (wpa_auth_alg & WPA_AUTH_ALG_FT)
+ return NL80211_AUTHTYPE_FT;
+ if (wpa_auth_alg & WPA_AUTH_ALG_SAE)
+ return NL80211_AUTHTYPE_SAE;
+ if (wpa_auth_alg & WPA_AUTH_ALG_FILS)
+ return NL80211_AUTHTYPE_FILS_SK;
+ if (wpa_auth_alg & WPA_AUTH_ALG_FILS_SK_PFS)
+ return NL80211_AUTHTYPE_FILS_SK_PFS;
+
+ return NL80211_AUTHTYPE_MAX;
+}
+
+
+static int wpa_driver_nl80211_authenticate(
+ struct i802_bss *bss, struct wpa_driver_auth_params *params)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int ret = -1, i;
+ struct nl_msg *msg;
+ enum nl80211_auth_type type;
+ enum nl80211_iftype nlmode;
+ int count = 0;
+ int is_retry;
+ struct wpa_driver_set_key_params p;
+
+ nl80211_unmask_11b_rates(bss);
+
+ is_retry = drv->retry_auth;
+ drv->retry_auth = 0;
+ drv->ignore_deauth_event = 0;
+
+ nl80211_mark_disconnected(drv);
+ os_memset(drv->auth_bssid, 0, ETH_ALEN);
+ if (params->bssid)
+ os_memcpy(drv->auth_attempt_bssid, params->bssid, ETH_ALEN);
+ else
+ os_memset(drv->auth_attempt_bssid, 0, ETH_ALEN);
+ /* FIX: IBSS mode */
+ nlmode = params->p2p ?
+ NL80211_IFTYPE_P2P_CLIENT : NL80211_IFTYPE_STATION;
+ if (drv->nlmode != nlmode &&
+ wpa_driver_nl80211_set_mode(bss, nlmode) < 0)
+ return -1;
+
+retry:
+ wpa_printf(MSG_DEBUG, "nl80211: Authenticate (ifindex=%d)",
+ drv->ifindex);
+
+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_AUTHENTICATE);
+ if (!msg)
+ goto fail;
+
+ os_memset(&p, 0, sizeof(p));
+ p.ifname = bss->ifname;
+ p.alg = WPA_ALG_WEP;
+ p.link_id = -1;
+ for (i = 0; i < 4; i++) {
+ if (!params->wep_key[i])
+ continue;
+ p.key_idx = i;
+ p.set_tx = i == params->wep_tx_keyidx;
+ p.key = params->wep_key[i];
+ p.key_len = params->wep_key_len[i];
+ p.key_flag = i == params->wep_tx_keyidx ?
+ KEY_FLAG_GROUP_RX_TX_DEFAULT :
+ KEY_FLAG_GROUP_RX_TX;
+ wpa_driver_nl80211_set_key(bss, &p);
+ 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]))
+ goto fail;
+ }
+
+ if (params->bssid) {
+ wpa_printf(MSG_DEBUG, " * bssid=" MACSTR,
+ MAC2STR(params->bssid));
+ if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid))
+ goto fail;
+ }
+ if (params->freq) {
+ wpa_printf(MSG_DEBUG, " * freq=%d", params->freq);
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq))
+ goto fail;
+ }
+ if (params->ssid) {
+ wpa_printf(MSG_DEBUG, " * SSID=%s",
+ wpa_ssid_txt(params->ssid, params->ssid_len));
+ if (nla_put(msg, NL80211_ATTR_SSID, params->ssid_len,
+ params->ssid))
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, " * IEs", params->ie, params->ie_len);
+ if (params->ie &&
+ nla_put(msg, NL80211_ATTR_IE, params->ie_len, params->ie))
+ goto fail;
+ if (params->auth_data) {
+ wpa_hexdump(MSG_DEBUG, " * auth_data", params->auth_data,
+ params->auth_data_len);
+ if (nla_put(msg, NL80211_ATTR_SAE_DATA, params->auth_data_len,
+ params->auth_data))
+ goto fail;
+ }
+ type = get_nl_auth_type(params->auth_alg);
+ wpa_printf(MSG_DEBUG, " * Auth Type %d", type);
+ if (type == NL80211_AUTHTYPE_MAX ||
+ nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type))
+ goto fail;
+ if (params->local_state_change) {
+ wpa_printf(MSG_DEBUG, " * Local state change only");
+ if (nla_put_flag(msg, NL80211_ATTR_LOCAL_STATE_CHANGE))
+ goto fail;
+ }
+
+ if (params->mld && params->ap_mld_addr) {
+ wpa_printf(MSG_DEBUG, " * MLD: link_id=%u, MLD addr=" MACSTR,
+ params->mld_link_id, MAC2STR(params->ap_mld_addr));
+
+ if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID,
+ params->mld_link_id) ||
+ nla_put(msg, NL80211_ATTR_MLD_ADDR, ETH_ALEN,
+ params->ap_mld_addr))
+ goto fail;
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ wpa_dbg(drv->ctx, MSG_DEBUG,
+ "nl80211: MLME command failed (auth): count=%d ret=%d (%s)",
+ count, ret, strerror(-ret));
+ count++;
+ if ((ret == -EALREADY || ret == -EEXIST) && 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");
+ drv->ignore_deauth_event = 1;
+ wpa_driver_nl80211_deauthenticate(
+ bss, params->bssid,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ nlmsg_free(msg);
+ goto retry;
+ }
+
+ if (ret == -ENOENT && params->freq && !is_retry) {
+ /*
+ * cfg80211 has likely expired the BSS entry even
+ * though it was previously available in our internal
+ * BSS table. To recover quickly, start a single
+ * channel scan on the specified channel.
+ */
+ struct wpa_driver_scan_params scan;
+ int freqs[2];
+
+ os_memset(&scan, 0, sizeof(scan));
+ scan.num_ssids = 1;
+ if (params->ssid) {
+ scan.ssids[0].ssid = params->ssid;
+ scan.ssids[0].ssid_len = params->ssid_len;
+ }
+ freqs[0] = params->freq;
+ freqs[1] = 0;
+ scan.freqs = freqs;
+ wpa_printf(MSG_DEBUG, "nl80211: Trigger single "
+ "channel scan to refresh cfg80211 BSS "
+ "entry");
+ ret = wpa_driver_nl80211_scan(bss, &scan);
+ if (ret == 0) {
+ nl80211_copy_auth_params(drv, params);
+ drv->scan_for_auth = 1;
+ }
+ } else if (is_retry) {
+ /*
+ * Need to indicate this with an event since the return
+ * value from the retry is not delivered to core code.
+ */
+ union wpa_event_data event;
+ wpa_printf(MSG_DEBUG, "nl80211: Authentication retry "
+ "failed");
+ os_memset(&event, 0, sizeof(event));
+ os_memcpy(event.timeout_event.addr, drv->auth_bssid_,
+ ETH_ALEN);
+ wpa_supplicant_event(drv->ctx, EVENT_AUTH_TIMED_OUT,
+ &event);
+ }
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Authentication request send successfully");
+ }
+
+fail:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+int wpa_driver_nl80211_authenticate_retry(struct wpa_driver_nl80211_data *drv)
+{
+ struct wpa_driver_auth_params params;
+ struct i802_bss *bss = drv->first_bss;
+ int i;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Try to authenticate again");
+
+ os_memset(¶ms, 0, sizeof(params));
+ params.freq = drv->auth_freq;
+ params.auth_alg = drv->auth_alg;
+ params.wep_tx_keyidx = drv->auth_wep_tx_keyidx;
+ params.local_state_change = drv->auth_local_state_change;
+ params.p2p = drv->auth_p2p;
+
+ if (!is_zero_ether_addr(drv->auth_bssid_))
+ params.bssid = drv->auth_bssid_;
+
+ if (drv->auth_ssid_len) {
+ params.ssid = drv->auth_ssid;
+ params.ssid_len = drv->auth_ssid_len;
+ }
+
+ params.ie = drv->auth_ie;
+ params.ie_len = drv->auth_ie_len;
+ params.auth_data = drv->auth_data;
+ params.auth_data_len = drv->auth_data_len;
+ params.mld = drv->auth_mld;
+ params.mld_link_id = drv->auth_mld_link_id;
+ if (drv->auth_mld)
+ params.ap_mld_addr = drv->auth_ap_mld_addr;
+
+ for (i = 0; i < 4; i++) {
+ if (drv->auth_wep_key_len[i]) {
+ params.wep_key[i] = drv->auth_wep_key[i];
+ params.wep_key_len[i] = drv->auth_wep_key_len[i];
+ }
+ }
+
+ drv->retry_auth = 1;
+ return wpa_driver_nl80211_authenticate(bss, ¶ms);
+}
+
+
+struct i802_link * nl80211_get_link(struct i802_bss *bss, s8 link_id)
+{
+ unsigned int i;
+
+ for (i = 0; i < bss->n_links; i++) {
+ if (bss->links[i].link_id != link_id)
+ continue;
+
+ return &bss->links[i];
+ }
+
+ return bss->flink;
+}
+
+
+static void nl80211_link_set_freq(struct i802_bss *bss, s8 link_id, int freq)
+{
+ struct i802_link *link = nl80211_get_link(bss, link_id);
+
+ link->freq = freq;
+}
+
+
+static int nl80211_get_link_freq(struct i802_bss *bss, const u8 *addr,
+ bool bss_freq_debug)
+{
+ size_t i;
+
+ for (i = 0; i < bss->n_links; i++) {
+ if (os_memcmp(bss->links[i].addr, addr, ETH_ALEN) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Use link freq=%d for address "
+ MACSTR,
+ bss->links[i].freq, MAC2STR(addr));
+ return bss->links[i].freq;
+ }
+ }
+
+ if (bss_freq_debug)
+ wpa_printf(MSG_DEBUG, "nl80211: Use bss->freq=%d",
+ bss->flink->freq);
+
+ return bss->flink->freq;
+}
+
+
+static int wpa_driver_nl80211_send_mlme(struct i802_bss *bss, const u8 *data,
+ size_t data_len, int noack,
+ unsigned int freq, int no_cck,
+ int offchanok,
+ unsigned int wait_time,
+ const u16 *csa_offs,
+ size_t csa_offs_len, int no_encrypt,
+ int link_id)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct ieee80211_mgmt *mgmt;
+ int encrypt = !no_encrypt;
+ u16 fc;
+ int use_cookie = 1;
+ int res;
+ struct i802_link *link = nl80211_get_link(bss, link_id);
+
+ mgmt = (struct ieee80211_mgmt *) data;
+ fc = le_to_host16(mgmt->frame_control);
+ wpa_printf(MSG_DEBUG, "nl80211: send_mlme - da=" MACSTR
+ " noack=%d freq=%u no_cck=%d offchanok=%d wait_time=%u no_encrypt=%d fc=0x%x (%s) nlmode=%d",
+ MAC2STR(mgmt->da), noack, freq, no_cck, offchanok, wait_time,
+ no_encrypt, fc, fc2str(fc), drv->nlmode);
+
+ if ((is_sta_interface(drv->nlmode) ||
+ drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) &&
+ 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.
+ */
+ if (freq == 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Use last_mgmt_freq=%d",
+ drv->last_mgmt_freq);
+ freq = drv->last_mgmt_freq;
+ }
+ wait_time = 0;
+ use_cookie = 0;
+ no_cck = 1;
+ offchanok = 1;
+ goto send_frame_cmd;
+ }
+
+ if (drv->device_ap_sme && is_ap_interface(drv->nlmode)) {
+ unsigned int link_freq = nl80211_get_link_freq(bss, mgmt->sa,
+ !freq);
+
+ if (!freq)
+ freq = link_freq;
+
+ if (freq == link_freq)
+ wait_time = 0;
+
+ goto send_frame_cmd;
+ }
+
+ 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;
+ }
+
+ if (is_sta_interface(drv->nlmode) &&
+ WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+ WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_AUTH) {
+ if (freq == 0 &&
+ (drv->capa.flags & WPA_DRIVER_FLAGS_SAE) &&
+ !(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) {
+ freq = nl80211_get_assoc_freq(drv);
+ wpa_printf(MSG_DEBUG,
+ "nl80211: send_mlme - Use assoc_freq=%u for external auth",
+ freq);
+ }
+
+ /* Allow off channel for PASN authentication */
+ if (data_len >= IEEE80211_HDRLEN + 2 &&
+ WPA_GET_LE16(data + IEEE80211_HDRLEN) == WLAN_AUTH_PASN &&
+ !offchanok) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: send_mlme: allow off channel for PASN");
+ offchanok = 1;
+ }
+ }
+
+#ifdef CONFIG_PASN
+ if (is_sta_interface(drv->nlmode) &&
+ WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+ WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_DEAUTH) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: send_mlme: allow Deauthentication frame for PASN");
+
+ use_cookie = 0;
+ offchanok = 1;
+ goto send_frame_cmd;
+ }
+#endif /* CONFIG_PASN */
+
+ if (freq == 0 && drv->nlmode == NL80211_IFTYPE_ADHOC) {
+ freq = nl80211_get_assoc_freq(drv);
+ wpa_printf(MSG_DEBUG,
+ "nl80211: send_mlme - Use assoc_freq=%u for IBSS",
+ freq);
+ }
+ if (freq == 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: send_mlme - Use bss->freq=%u",
+ link->freq);
+ freq = link->freq;
+ }
+
+ if (drv->use_monitor && is_ap_interface(drv->nlmode)) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: send_frame(freq=%u bss->freq=%u) -> send_monitor",
+ freq, link->freq);
+ return nl80211_send_monitor(drv, data, data_len, encrypt,
+ noack);
+ }
+
+ if ((noack || WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT ||
+ WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_ACTION) &&
+ link_id == NL80211_DRV_LINK_ID_NA)
+ use_cookie = 0;
+send_frame_cmd:
+#ifdef CONFIG_TESTING_OPTIONS
+ if (no_encrypt && !encrypt && !drv->use_monitor) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Request to send an unencrypted frame - use a monitor interface for this");
+ if (nl80211_create_monitor_interface(drv) < 0)
+ return -1;
+ res = nl80211_send_monitor(drv, data, data_len, encrypt,
+ noack);
+ nl80211_remove_monitor_interface(drv);
+ return res;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wpa_printf(MSG_DEBUG, "nl80211: send_mlme -> send_frame_cmd");
+ res = nl80211_send_frame_cmd(bss, freq, wait_time, data, data_len,
+ use_cookie, no_cck, noack, offchanok,
+ csa_offs, csa_offs_len);
+ if (!res)
+ drv->send_frame_link_id = link_id;
+
+ return res;
+}
+
+
+static int nl80211_put_basic_rates(struct nl_msg *msg, const int *basic_rates)
+{
+ u8 rates[NL80211_MAX_SUPP_RATES];
+ u8 rates_len = 0;
+ int i;
+
+ if (!basic_rates)
+ return 0;
+
+ for (i = 0; i < NL80211_MAX_SUPP_RATES && basic_rates[i] >= 0; i++)
+ rates[rates_len++] = basic_rates[i] / 5;
+
+ return nla_put(msg, NL80211_ATTR_BSS_BASIC_RATES, rates_len, rates);
+}
+
+
+static int nl80211_set_bss(struct i802_bss *bss, int cts, int preamble,
+ int slot, int ht_opmode, int ap_isolate,
+ const int *basic_rates)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_BSS)) ||
+ (cts >= 0 &&
+ nla_put_u8(msg, NL80211_ATTR_BSS_CTS_PROT, cts)) ||
+ (preamble >= 0 &&
+ nla_put_u8(msg, NL80211_ATTR_BSS_SHORT_PREAMBLE, preamble)) ||
+ (slot >= 0 &&
+ nla_put_u8(msg, NL80211_ATTR_BSS_SHORT_SLOT_TIME, slot)) ||
+ (ht_opmode >= 0 &&
+ nla_put_u16(msg, NL80211_ATTR_BSS_HT_OPMODE, ht_opmode)) ||
+ (ap_isolate >= 0 &&
+ nla_put_u8(msg, NL80211_ATTR_AP_ISOLATE, ap_isolate)) ||
+ nl80211_put_basic_rates(msg, basic_rates)) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+}
+
+
+static int wpa_driver_nl80211_set_acl(void *priv,
+ struct hostapd_acl_params *params)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ struct nl_msg *acl;
+ unsigned int i;
+ int ret;
+ size_t acl_nla_sz, acl_nlmsg_sz, nla_sz, nlmsg_sz;
+
+ if (!(drv->capa.max_acl_mac_addrs))
+ return -ENOTSUP;
+
+ if (params->num_mac_acl > drv->capa.max_acl_mac_addrs)
+ return -ENOTSUP;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Set %s ACL (num_mac_acl=%u)",
+ params->acl_policy ? "Accept" : "Deny", params->num_mac_acl);
+
+ acl_nla_sz = nla_total_size(ETH_ALEN) * params->num_mac_acl;
+ acl_nlmsg_sz = nlmsg_total_size(acl_nla_sz);
+ acl = nlmsg_alloc_size(acl_nlmsg_sz);
+ if (!acl)
+ return -ENOMEM;
+ for (i = 0; i < params->num_mac_acl; i++) {
+ if (nla_put(acl, i + 1, ETH_ALEN, params->mac_acl[i].addr)) {
+ nlmsg_free(acl);
+ return -ENOMEM;
+ }
+ }
+
+ /*
+ * genetlink message header (Length of user header is 0) +
+ * u32 attr: NL80211_ATTR_IFINDEX +
+ * u32 attr: NL80211_ATTR_ACL_POLICY +
+ * nested acl attr
+ */
+ nla_sz = GENL_HDRLEN +
+ nla_total_size(4) * 2 +
+ nla_total_size(acl_nla_sz);
+ nlmsg_sz = nlmsg_total_size(nla_sz);
+ if (!(msg = nl80211_ifindex_msg_build(drv, nlmsg_alloc_size(nlmsg_sz),
+ drv->ifindex, 0,
+ NL80211_CMD_SET_MAC_ACL)) ||
+ nla_put_u32(msg, NL80211_ATTR_ACL_POLICY, params->acl_policy ?
+ NL80211_ACL_POLICY_DENY_UNLESS_LISTED :
+ NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED) ||
+ nla_put_nested(msg, NL80211_ATTR_MAC_ADDRS, acl)) {
+ nlmsg_free(msg);
+ nlmsg_free(acl);
+ return -ENOMEM;
+ }
+ nlmsg_free(acl);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to set MAC ACL: %d (%s)",
+ ret, strerror(-ret));
+ }
+
+ return ret;
+}
+
+
+static int nl80211_put_beacon_int(struct nl_msg *msg, int beacon_int)
+{
+ if (beacon_int > 0) {
+ wpa_printf(MSG_DEBUG, " * beacon_int=%d", beacon_int);
+ return nla_put_u32(msg, NL80211_ATTR_BEACON_INTERVAL,
+ beacon_int);
+ }
+
+ return 0;
+}
+
+
+static int nl80211_put_dtim_period(struct nl_msg *msg, int dtim_period)
+{
+ if (dtim_period > 0) {
+ wpa_printf(MSG_DEBUG, " * dtim_period=%d", dtim_period);
+ return nla_put_u32(msg, NL80211_ATTR_DTIM_PERIOD, dtim_period);
+ }
+
+ return 0;
+}
+
+
+#ifdef CONFIG_MESH
+static int nl80211_set_mesh_config(void *priv,
+ struct wpa_driver_mesh_bss_params *params)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret;
+
+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_MESH_CONFIG);
+ if (!msg)
+ return -1;
+
+ ret = nl80211_put_mesh_config(msg, params);
+ if (ret < 0) {
+ nlmsg_free(msg);
+ return ret;
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ if (ret) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: Mesh config set failed: %d (%s)",
+ ret, strerror(-ret));
+ return ret;
+ }
+ return 0;
+}
+#endif /* CONFIG_MESH */
+
+
+static int nl80211_put_beacon_rate(struct nl_msg *msg, u64 flags, u64 flags2,
+ struct wpa_driver_ap_params *params)
+{
+ struct nlattr *bands, *band;
+ struct nl80211_txrate_vht vht_rate;
+ struct nl80211_txrate_he he_rate;
+
+ if (!params->freq ||
+ (params->beacon_rate == 0 &&
+ params->rate_type == BEACON_RATE_LEGACY))
+ return 0;
+
+ bands = nla_nest_start(msg, NL80211_ATTR_TX_RATES);
+ if (!bands)
+ return -1;
+
+ switch (params->freq->mode) {
+ case HOSTAPD_MODE_IEEE80211B:
+ case HOSTAPD_MODE_IEEE80211G:
+ band = nla_nest_start(msg, NL80211_BAND_2GHZ);
+ break;
+ case HOSTAPD_MODE_IEEE80211A:
+ if (is_6ghz_freq(params->freq->freq))
+ band = nla_nest_start(msg, NL80211_BAND_6GHZ);
+ else
+ band = nla_nest_start(msg, NL80211_BAND_5GHZ);
+ break;
+ case HOSTAPD_MODE_IEEE80211AD:
+ band = nla_nest_start(msg, NL80211_BAND_60GHZ);
+ break;
+ default:
+ return 0;
+ }
+
+ if (!band)
+ return -1;
+
+ os_memset(&vht_rate, 0, sizeof(vht_rate));
+ os_memset(&he_rate, 0, sizeof(he_rate));
+
+ switch (params->rate_type) {
+ case BEACON_RATE_LEGACY:
+ if (!(flags & WPA_DRIVER_FLAGS_BEACON_RATE_LEGACY)) {
+ wpa_printf(MSG_INFO,
+ "nl80211: Driver does not support setting Beacon frame rate (legacy)");
+ return -1;
+ }
+
+ if (nla_put_u8(msg, NL80211_TXRATE_LEGACY,
+ (u8) params->beacon_rate / 5) ||
+ nla_put(msg, NL80211_TXRATE_HT, 0, NULL) ||
+ (params->freq->vht_enabled &&
+ nla_put(msg, NL80211_TXRATE_VHT, sizeof(vht_rate),
+ &vht_rate)))
+ return -1;
+
+ wpa_printf(MSG_DEBUG, " * beacon_rate = legacy:%u (* 100 kbps)",
+ params->beacon_rate);
+ break;
+ case BEACON_RATE_HT:
+ if (!(flags & WPA_DRIVER_FLAGS_BEACON_RATE_HT)) {
+ wpa_printf(MSG_INFO,
+ "nl80211: Driver does not support setting Beacon frame rate (HT)");
+ return -1;
+ }
+ if (nla_put(msg, NL80211_TXRATE_LEGACY, 0, NULL) ||
+ nla_put_u8(msg, NL80211_TXRATE_HT, params->beacon_rate) ||
+ (params->freq->vht_enabled &&
+ nla_put(msg, NL80211_TXRATE_VHT, sizeof(vht_rate),
+ &vht_rate)))
+ return -1;
+ wpa_printf(MSG_DEBUG, " * beacon_rate = HT-MCS %u",
+ params->beacon_rate);
+ break;
+ case BEACON_RATE_VHT:
+ if (!(flags & WPA_DRIVER_FLAGS_BEACON_RATE_VHT)) {
+ wpa_printf(MSG_INFO,
+ "nl80211: Driver does not support setting Beacon frame rate (VHT)");
+ return -1;
+ }
+ vht_rate.mcs[0] = BIT(params->beacon_rate);
+ if (nla_put(msg, NL80211_TXRATE_LEGACY, 0, NULL))
+ return -1;
+ if (nla_put(msg, NL80211_TXRATE_HT, 0, NULL))
+ return -1;
+ if (nla_put(msg, NL80211_TXRATE_VHT, sizeof(vht_rate),
+ &vht_rate))
+ return -1;
+ wpa_printf(MSG_DEBUG, " * beacon_rate = VHT-MCS %u",
+ params->beacon_rate);
+ break;
+ case BEACON_RATE_HE:
+ if (!(flags2 & WPA_DRIVER_FLAGS2_BEACON_RATE_HE)) {
+ wpa_printf(MSG_INFO,
+ "nl80211: Driver does not support setting Beacon frame rate (HE)");
+ return -1;
+ }
+ he_rate.mcs[0] = BIT(params->beacon_rate);
+ if (nla_put(msg, NL80211_TXRATE_LEGACY, 0, NULL) ||
+ nla_put(msg, NL80211_TXRATE_HT, 0, NULL) ||
+ nla_put(msg, NL80211_TXRATE_VHT, sizeof(vht_rate),
+ &vht_rate) ||
+ nla_put(msg, NL80211_TXRATE_HE, sizeof(he_rate), &he_rate))
+ return -1;
+ wpa_printf(MSG_DEBUG, " * beacon_rate = HE-MCS %u",
+ params->beacon_rate);
+ break;
+ }
+
+ nla_nest_end(msg, band);
+ nla_nest_end(msg, bands);
+
+ return 0;
+}
+
+
+static int nl80211_set_multicast_to_unicast(struct i802_bss *bss,
+ int multicast_to_unicast)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret;
+
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_MULTICAST_TO_UNICAST);
+ if (!msg ||
+ (multicast_to_unicast &&
+ nla_put_flag(msg, NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED))) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: Failed to build NL80211_CMD_SET_MULTICAST_TO_UNICAST msg for %s",
+ bss->ifname);
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+
+ switch (ret) {
+ case 0:
+ wpa_printf(MSG_DEBUG,
+ "nl80211: multicast to unicast %s on interface %s",
+ multicast_to_unicast ? "enabled" : "disabled",
+ bss->ifname);
+ break;
+ case -EOPNOTSUPP:
+ if (!multicast_to_unicast)
+ break;
+ wpa_printf(MSG_INFO,
+ "nl80211: multicast to unicast not supported on interface %s",
+ bss->ifname);
+ break;
+ default:
+ wpa_printf(MSG_ERROR,
+ "nl80211: %s multicast to unicast failed with %d (%s) on interface %s",
+ multicast_to_unicast ? "enabling" : "disabling",
+ ret, strerror(-ret), bss->ifname);
+ break;
+ }
+
+ return ret;
+}
+
+
+#ifdef CONFIG_SAE
+static int nl80211_put_sae_pwe(struct nl_msg *msg, enum sae_pwe pwe)
+{
+ u8 sae_pwe;
+
+ wpa_printf(MSG_DEBUG, "nl802111: sae_pwe=%d", pwe);
+ if (pwe == SAE_PWE_HUNT_AND_PECK)
+ sae_pwe = NL80211_SAE_PWE_HUNT_AND_PECK;
+ else if (pwe == SAE_PWE_HASH_TO_ELEMENT)
+ sae_pwe = NL80211_SAE_PWE_HASH_TO_ELEMENT;
+ else if (pwe == SAE_PWE_BOTH)
+ sae_pwe = NL80211_SAE_PWE_BOTH;
+ else if (pwe == SAE_PWE_FORCE_HUNT_AND_PECK)
+ return 0; /* special test mode */
+ else
+ return -1;
+ if (nla_put_u8(msg, NL80211_ATTR_SAE_PWE, sae_pwe))
+ return -1;
+
+ return 0;
+}
+#endif /* CONFIG_SAE */
+
+
+#ifdef CONFIG_FILS
+static int nl80211_fils_discovery(struct i802_bss *bss, struct nl_msg *msg,
+ struct wpa_driver_ap_params *params)
+{
+ struct nlattr *attr;
+
+ if (!bss->drv->fils_discovery) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: Driver does not support FILS Discovery frame transmission for %s",
+ bss->ifname);
+ return -1;
+ }
+
+ attr = nla_nest_start(msg, NL80211_ATTR_FILS_DISCOVERY);
+ if (!attr ||
+ nla_put_u32(msg, NL80211_FILS_DISCOVERY_ATTR_INT_MIN,
+ params->fd_min_int) ||
+ nla_put_u32(msg, NL80211_FILS_DISCOVERY_ATTR_INT_MAX,
+ params->fd_max_int) ||
+ (params->fd_frame_tmpl &&
+ nla_put(msg, NL80211_FILS_DISCOVERY_ATTR_TMPL,
+ params->fd_frame_tmpl_len, params->fd_frame_tmpl)))
+ return -1;
+
+ nla_nest_end(msg, attr);
+ return 0;
+}
+#endif /* CONFIG_FILS */
+
+
+#ifdef CONFIG_IEEE80211AX
+
+static int nl80211_unsol_bcast_probe_resp(struct i802_bss *bss,
+ struct nl_msg *msg,
+ struct wpa_driver_ap_params *params)
+{
+ struct nlattr *attr;
+
+ if (!bss->drv->unsol_bcast_probe_resp) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: Driver does not support unsolicited broadcast Probe Response frame transmission for %s",
+ bss->ifname);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Unsolicited broadcast Probe Response frame interval: %u",
+ params->unsol_bcast_probe_resp_interval);
+ attr = nla_nest_start(msg, NL80211_ATTR_UNSOL_BCAST_PROBE_RESP);
+ if (!attr ||
+ nla_put_u32(msg, NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INT,
+ params->unsol_bcast_probe_resp_interval) ||
+ (params->unsol_bcast_probe_resp_tmpl &&
+ nla_put(msg, NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_TMPL,
+ params->unsol_bcast_probe_resp_tmpl_len,
+ params->unsol_bcast_probe_resp_tmpl)))
+ return -1;
+
+ nla_nest_end(msg, attr);
+ return 0;
+}
+
+
+static int nl80211_mbssid(struct nl_msg *msg,
+ struct wpa_driver_ap_params *params)
+{
+ struct nlattr *config, *elems;
+ int ifidx;
+
+ if (!params->mbssid_tx_iface)
+ return 0;
+
+ config = nla_nest_start(msg, NL80211_ATTR_MBSSID_CONFIG);
+ if (!config ||
+ nla_put_u8(msg, NL80211_MBSSID_CONFIG_ATTR_INDEX,
+ params->mbssid_index))
+ return -1;
+
+ if (params->mbssid_tx_iface) {
+ ifidx = if_nametoindex(params->mbssid_tx_iface);
+ if (ifidx <= 0 ||
+ nla_put_u32(msg, NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX,
+ ifidx))
+ return -1;
+ }
+
+ if (params->ema && nla_put_flag(msg, NL80211_MBSSID_CONFIG_ATTR_EMA))
+ return -1;
+
+ nla_nest_end(msg, config);
+
+ if (params->mbssid_elem_count && params->mbssid_elem_len &&
+ params->mbssid_elem_offset && *params->mbssid_elem_offset) {
+ u8 i, **offs = params->mbssid_elem_offset;
+
+ elems = nla_nest_start(msg, NL80211_ATTR_MBSSID_ELEMS);
+ if (!elems)
+ return -1;
+
+ for (i = 0; i < params->mbssid_elem_count - 1; i++) {
+ if (nla_put(msg, i + 1, offs[i + 1] - offs[i], offs[i]))
+ return -1;
+ }
+
+ if (nla_put(msg, i + 1,
+ *offs + params->mbssid_elem_len - offs[i],
+ offs[i]))
+ return -1;
+
+ nla_nest_end(msg, elems);
+ }
+
+ if (!params->ema)
+ return 0;
+
+ if (params->rnr_elem_count && params->rnr_elem_len &&
+ params->rnr_elem_offset && *params->rnr_elem_offset) {
+ u8 i, **offs = params->rnr_elem_offset;
+
+ elems = nla_nest_start(msg, NL80211_ATTR_EMA_RNR_ELEMS);
+ if (!elems)
+ return -1;
+
+ for (i = 0; i < params->rnr_elem_count - 1; i++) {
+ if (nla_put(msg, i + 1, offs[i + 1] - offs[i], offs[i]))
+ return -1;
+ }
+
+ if (nla_put(msg, i + 1, *offs + params->rnr_elem_len - offs[i],
+ offs[i]))
+ return -1;
+ nla_nest_end(msg, elems);
+ }
+
+ return 0;
+}
+
+#endif /* CONFIG_IEEE80211AX */
+
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+static void qca_set_allowed_ap_freqs(struct wpa_driver_nl80211_data *drv,
+ const int *freqs, int num_freqs)
+{
+ struct nl_msg *msg;
+ struct nlattr *params, *freqs_list;
+ int i, ret;
+
+ if (!drv->set_wifi_conf_vendor_cmd_avail || !drv->qca_ap_allowed_freqs)
+ return;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Set AP allowed frequency list");
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION) ||
+ !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)))
+ goto err;
+
+ freqs_list = nla_nest_start(
+ msg, QCA_WLAN_VENDOR_ATTR_CONFIG_AP_ALLOWED_FREQ_LIST);
+ if (!freqs_list)
+ goto err;
+
+ for (i = 0; i < num_freqs; i++) {
+ if (nla_put_u32(msg, i, freqs[i]))
+ goto err;
+ }
+
+ nla_nest_end(msg, freqs_list);
+ nla_nest_end(msg, params);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ if (ret)
+ wpa_printf(MSG_ERROR,
+ "nl80211: Failed set AP alllowed frequency list: %d (%s)",
+ ret, strerror(-ret));
+
+ return;
+err:
+ nlmsg_free(msg);
+}
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+
+static int nl80211_put_freq_params(struct nl_msg *msg,
+ const struct hostapd_freq_params *freq)
+{
+ enum hostapd_hw_mode hw_mode;
+ int is_24ghz;
+ u8 channel;
+
+ wpa_printf(MSG_DEBUG, " * freq=%d", freq->freq);
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq->freq))
+ return -ENOBUFS;
+
+ wpa_printf(MSG_DEBUG, " * eht_enabled=%d", freq->eht_enabled);
+ wpa_printf(MSG_DEBUG, " * he_enabled=%d", freq->he_enabled);
+ wpa_printf(MSG_DEBUG, " * vht_enabled=%d", freq->vht_enabled);
+ wpa_printf(MSG_DEBUG, " * ht_enabled=%d", freq->ht_enabled);
+ wpa_printf(MSG_DEBUG, " * radar_background=%d",
+ freq->radar_background);
+
+ hw_mode = ieee80211_freq_to_chan(freq->freq, &channel);
+ is_24ghz = hw_mode == HOSTAPD_MODE_IEEE80211G ||
+ hw_mode == HOSTAPD_MODE_IEEE80211B;
+
+ if (freq->vht_enabled ||
+ ((freq->he_enabled || freq->eht_enabled) && !is_24ghz)) {
+ enum nl80211_chan_width cw;
+
+ wpa_printf(MSG_DEBUG, " * bandwidth=%d", freq->bandwidth);
+ switch (freq->bandwidth) {
+ case 20:
+ cw = NL80211_CHAN_WIDTH_20;
+ break;
+ case 40:
+ cw = NL80211_CHAN_WIDTH_40;
+ break;
+ case 80:
+ if (freq->center_freq2)
+ cw = NL80211_CHAN_WIDTH_80P80;
+ else
+ cw = NL80211_CHAN_WIDTH_80;
+ break;
+ case 160:
+ cw = NL80211_CHAN_WIDTH_160;
+ break;
+ case 320:
+ cw = NL80211_CHAN_WIDTH_320;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ wpa_printf(MSG_DEBUG, " * channel_width=%d", cw);
+ wpa_printf(MSG_DEBUG, " * center_freq1=%d",
+ freq->center_freq1);
+ wpa_printf(MSG_DEBUG, " * center_freq2=%d",
+ freq->center_freq2);
+ if (nla_put_u32(msg, NL80211_ATTR_CHANNEL_WIDTH, cw) ||
+ nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ1,
+ freq->center_freq1) ||
+ (freq->center_freq2 &&
+ nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ2,
+ freq->center_freq2)))
+ return -ENOBUFS;
+ } else if (freq->ht_enabled) {
+ enum nl80211_channel_type ct;
+
+ wpa_printf(MSG_DEBUG, " * sec_channel_offset=%d",
+ freq->sec_channel_offset);
+ switch (freq->sec_channel_offset) {
+ case -1:
+ ct = NL80211_CHAN_HT40MINUS;
+ break;
+ case 1:
+ ct = NL80211_CHAN_HT40PLUS;
+ break;
+ default:
+ ct = NL80211_CHAN_HT20;
+ break;
+ }
+
+ wpa_printf(MSG_DEBUG, " * channel_type=%d", ct);
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, ct))
+ return -ENOBUFS;
+ } else if (freq->edmg.channels && freq->edmg.bw_config) {
+ wpa_printf(MSG_DEBUG,
+ " * EDMG configuration: channels=0x%x bw_config=%d",
+ freq->edmg.channels, freq->edmg.bw_config);
+ if (nla_put_u8(msg, NL80211_ATTR_WIPHY_EDMG_CHANNELS,
+ freq->edmg.channels) ||
+ nla_put_u8(msg, NL80211_ATTR_WIPHY_EDMG_BW_CONFIG,
+ freq->edmg.bw_config))
+ return -1;
+ } else {
+ wpa_printf(MSG_DEBUG, " * channel_type=%d",
+ NL80211_CHAN_NO_HT);
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
+ NL80211_CHAN_NO_HT))
+ return -ENOBUFS;
+ }
+ if (freq->radar_background &&
+ nla_put_flag(msg, NL80211_ATTR_RADAR_BACKGROUND))
+ return -ENOBUFS;
+
+ return 0;
+}
+
+
+static int wpa_driver_nl80211_set_ap(void *priv,
+ struct wpa_driver_ap_params *params)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct i802_link *link = bss->flink;
+ struct nl_msg *msg;
+ u8 cmd = NL80211_CMD_NEW_BEACON;
+ int ret = -ENOBUFS;
+ int beacon_set;
+ int num_suites;
+ u32 suites[20], suite;
+ u32 ver;
+#ifdef CONFIG_MESH
+ struct wpa_driver_mesh_bss_params mesh_params;
+#endif /* CONFIG_MESH */
+
+ if (params->mld_ap) {
+ size_t i;
+
+ for (i = 0; i < bss->n_links; i++) {
+ if (bss->links[i].link_id == params->mld_link_id) {
+ link = &bss->links[i];
+ break;
+ }
+ }
+
+ if (i == bss->n_links) {
+ wpa_printf(MSG_DEBUG, "nl80211: Link ID=%u not found",
+ params->mld_link_id);
+ return -EINVAL;
+ }
+ }
+
+ beacon_set = params->reenable ? 0 : link->beacon_set;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Set beacon (beacon_set=%d)",
+ beacon_set);
+ if (beacon_set)
+ cmd = NL80211_CMD_SET_BEACON;
+ else if (!drv->device_ap_sme && !drv->use_monitor &&
+ !nl80211_get_wiphy_data_ap(bss))
+ return -ENOBUFS;
+
+ wpa_hexdump(MSG_DEBUG, "nl80211: Beacon head",
+ params->head, params->head_len);
+ wpa_hexdump(MSG_DEBUG, "nl80211: Beacon tail",
+ params->tail, params->tail_len);
+ wpa_printf(MSG_DEBUG, "nl80211: ifindex=%d", bss->ifindex);
+ wpa_printf(MSG_DEBUG, "nl80211: beacon_int=%d", params->beacon_int);
+ wpa_printf(MSG_DEBUG, "nl80211: beacon_rate=%u", params->beacon_rate);
+ wpa_printf(MSG_DEBUG, "nl80211: rate_type=%d", params->rate_type);
+ wpa_printf(MSG_DEBUG, "nl80211: dtim_period=%d", params->dtim_period);
+ wpa_printf(MSG_DEBUG, "nl80211: ssid=%s",
+ wpa_ssid_txt(params->ssid, params->ssid_len));
+ if (!(msg = nl80211_bss_msg(bss, 0, cmd)) ||
+ nla_put(msg, NL80211_ATTR_BEACON_HEAD, params->head_len,
+ params->head) ||
+ nla_put(msg, NL80211_ATTR_BEACON_TAIL, params->tail_len,
+ params->tail) ||
+ nl80211_put_beacon_int(msg, params->beacon_int) ||
+ nl80211_put_beacon_rate(msg, drv->capa.flags, drv->capa.flags2,
+ params) ||
+ nl80211_put_dtim_period(msg, params->dtim_period) ||
+ nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid))
+ goto fail;
+
+ if (params->mld_ap) {
+ wpa_printf(MSG_DEBUG, "nl80211: link_id=%u",
+ params->mld_link_id);
+
+ if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID,
+ params->mld_link_id) ||
+ (params->freq &&
+ nl80211_put_freq_params(msg, params->freq) < 0))
+ goto fail;
+
+ nl80211_link_set_freq(bss, params->mld_link_id,
+ params->freq->freq);
+ }
+
+ if (params->proberesp && params->proberesp_len) {
+ wpa_hexdump(MSG_DEBUG, "nl80211: proberesp (offload)",
+ params->proberesp, params->proberesp_len);
+ if (nla_put(msg, NL80211_ATTR_PROBE_RESP, params->proberesp_len,
+ params->proberesp))
+ goto fail;
+ }
+ switch (params->hide_ssid) {
+ case NO_SSID_HIDING:
+ wpa_printf(MSG_DEBUG, "nl80211: hidden SSID not in use");
+ if (nla_put_u32(msg, NL80211_ATTR_HIDDEN_SSID,
+ NL80211_HIDDEN_SSID_NOT_IN_USE))
+ goto fail;
+ break;
+ case HIDDEN_SSID_ZERO_LEN:
+ wpa_printf(MSG_DEBUG, "nl80211: hidden SSID zero len");
+ if (nla_put_u32(msg, NL80211_ATTR_HIDDEN_SSID,
+ NL80211_HIDDEN_SSID_ZERO_LEN))
+ goto fail;
+ break;
+ case HIDDEN_SSID_ZERO_CONTENTS:
+ wpa_printf(MSG_DEBUG, "nl80211: hidden SSID zero contents");
+ if (nla_put_u32(msg, NL80211_ATTR_HIDDEN_SSID,
+ NL80211_HIDDEN_SSID_ZERO_CONTENTS))
+ goto fail;
+ break;
+ }
+ wpa_printf(MSG_DEBUG, "nl80211: privacy=%d", params->privacy);
+ if (params->privacy &&
+ nla_put_flag(msg, NL80211_ATTR_PRIVACY))
+ goto fail;
+ wpa_printf(MSG_DEBUG, "nl80211: auth_algs=0x%x", params->auth_algs);
+ if ((params->auth_algs & (WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED)) ==
+ (WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED)) {
+ /* Leave out the attribute */
+ } else if (params->auth_algs & WPA_AUTH_ALG_SHARED) {
+ if (nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE,
+ NL80211_AUTHTYPE_SHARED_KEY))
+ goto fail;
+ } else {
+ if (nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE,
+ NL80211_AUTHTYPE_OPEN_SYSTEM))
+ goto fail;
+ }
+
+ wpa_printf(MSG_DEBUG, "nl80211: wpa_version=0x%x", params->wpa_version);
+ ver = 0;
+ if (params->wpa_version & WPA_PROTO_WPA)
+ ver |= NL80211_WPA_VERSION_1;
+ if (params->wpa_version & WPA_PROTO_RSN)
+ ver |= NL80211_WPA_VERSION_2;
+ if (ver &&
+ nla_put_u32(msg, NL80211_ATTR_WPA_VERSIONS, ver))
+ goto fail;
+
+ wpa_printf(MSG_DEBUG, "nl80211: key_mgmt_suites=0x%x",
+ params->key_mgmt_suites);
+ num_suites = wpa_key_mgmt_to_suites(params->key_mgmt_suites,
+ suites, ARRAY_SIZE(suites));
+ if (num_suites > NL80211_MAX_NR_AKM_SUITES)
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Not enough room for all AKM suites (num_suites=%d > NL80211_MAX_NR_AKM_SUITES)",
+ num_suites);
+ else if (num_suites &&
+ nla_put(msg, NL80211_ATTR_AKM_SUITES, num_suites * sizeof(u32),
+ suites))
+ goto fail;
+
+ if (params->key_mgmt_suites & WPA_KEY_MGMT_IEEE8021X_NO_WPA &&
+ (!params->pairwise_ciphers ||
+ params->pairwise_ciphers & (WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40)) &&
+ (nla_put_u16(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE, ETH_P_PAE) ||
+ nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT)))
+ goto fail;
+
+ if (drv->device_ap_sme) {
+ u32 flags = 0;
+
+ if (params->key_mgmt_suites & (WPA_KEY_MGMT_SAE |
+ WPA_KEY_MGMT_SAE_EXT_KEY)) {
+ /* Add the previously used flag attribute to support
+ * older kernel versions and the newer flag bit for
+ * newer kernels. */
+ if (nla_put_flag(msg,
+ NL80211_ATTR_EXTERNAL_AUTH_SUPPORT))
+ goto fail;
+ flags |= NL80211_AP_SETTINGS_EXTERNAL_AUTH_SUPPORT;
+ }
+
+ flags |= NL80211_AP_SETTINGS_SA_QUERY_OFFLOAD_SUPPORT;
+
+ if (nla_put_u32(msg, NL80211_ATTR_AP_SETTINGS_FLAGS, flags))
+ goto fail;
+ }
+
+ wpa_printf(MSG_DEBUG, "nl80211: pairwise_ciphers=0x%x",
+ params->pairwise_ciphers);
+ num_suites = wpa_cipher_to_cipher_suites(params->pairwise_ciphers,
+ suites, ARRAY_SIZE(suites));
+ if (num_suites &&
+ nla_put(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE,
+ num_suites * sizeof(u32), suites))
+ goto fail;
+
+ wpa_printf(MSG_DEBUG, "nl80211: group_cipher=0x%x",
+ params->group_cipher);
+ suite = wpa_cipher_to_cipher_suite(params->group_cipher);
+ if (suite &&
+ nla_put_u32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, suite))
+ goto fail;
+
+ if (params->beacon_ies) {
+ wpa_hexdump_buf(MSG_DEBUG, "nl80211: beacon_ies",
+ params->beacon_ies);
+ if (nla_put(msg, NL80211_ATTR_IE,
+ wpabuf_len(params->beacon_ies),
+ wpabuf_head(params->beacon_ies)))
+ goto fail;
+ }
+ if (params->proberesp_ies) {
+ wpa_hexdump_buf(MSG_DEBUG, "nl80211: proberesp_ies",
+ params->proberesp_ies);
+ if (nla_put(msg, NL80211_ATTR_IE_PROBE_RESP,
+ wpabuf_len(params->proberesp_ies),
+ wpabuf_head(params->proberesp_ies)))
+ goto fail;
+ }
+ if (params->assocresp_ies) {
+ wpa_hexdump_buf(MSG_DEBUG, "nl80211: assocresp_ies",
+ params->assocresp_ies);
+ if (nla_put(msg, NL80211_ATTR_IE_ASSOC_RESP,
+ wpabuf_len(params->assocresp_ies),
+ wpabuf_head(params->assocresp_ies)))
+ goto fail;
+ }
+
+ if (drv->capa.flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER) {
+ wpa_printf(MSG_DEBUG, "nl80211: ap_max_inactivity=%d",
+ params->ap_max_inactivity);
+ if (nla_put_u16(msg, NL80211_ATTR_INACTIVITY_TIMEOUT,
+ params->ap_max_inactivity))
+ goto fail;
+ }
+
+#ifdef CONFIG_P2P
+ if (params->p2p_go_ctwindow > 0) {
+ if (drv->p2p_go_ctwindow_supported) {
+ wpa_printf(MSG_DEBUG, "nl80211: P2P GO ctwindow=%d",
+ params->p2p_go_ctwindow);
+ if (nla_put_u8(msg, NL80211_ATTR_P2P_CTWINDOW,
+ params->p2p_go_ctwindow))
+ goto fail;
+ } else {
+ wpa_printf(MSG_INFO,
+ "nl80211: Driver does not support CTWindow configuration - ignore this parameter");
+ }
+ }
+#endif /* CONFIG_P2P */
+
+ if (params->pbss) {
+ wpa_printf(MSG_DEBUG, "nl80211: PBSS");
+ if (nla_put_flag(msg, NL80211_ATTR_PBSS))
+ goto fail;
+ }
+
+ if (params->ftm_responder) {
+ struct nlattr *ftm;
+
+ if (!(drv->capa.flags & WPA_DRIVER_FLAGS_FTM_RESPONDER)) {
+ ret = -ENOTSUP;
+ goto fail;
+ }
+
+ ftm = nla_nest_start(msg, NL80211_ATTR_FTM_RESPONDER);
+ if (!ftm ||
+ nla_put_flag(msg, NL80211_FTM_RESP_ATTR_ENABLED) ||
+ (params->lci &&
+ nla_put(msg, NL80211_FTM_RESP_ATTR_LCI,
+ wpabuf_len(params->lci),
+ wpabuf_head(params->lci))) ||
+ (params->civic &&
+ nla_put(msg, NL80211_FTM_RESP_ATTR_CIVICLOC,
+ wpabuf_len(params->civic),
+ wpabuf_head(params->civic))))
+ goto fail;
+ nla_nest_end(msg, ftm);
+ }
+
+#ifdef CONFIG_IEEE80211AX
+ if (params->he_spr_ctrl) {
+ struct nlattr *spr;
+
+ spr = nla_nest_start(msg, NL80211_ATTR_HE_OBSS_PD);
+ wpa_printf(MSG_DEBUG, "nl80211: he_spr_ctrl=0x%x",
+ params->he_spr_ctrl);
+
+ if (!spr ||
+ nla_put_u8(msg, NL80211_HE_OBSS_PD_ATTR_SR_CTRL,
+ params->he_spr_ctrl) ||
+ ((params->he_spr_ctrl &
+ SPATIAL_REUSE_NON_SRG_OFFSET_PRESENT) &&
+ nla_put_u8(msg, NL80211_HE_OBSS_PD_ATTR_NON_SRG_MAX_OFFSET,
+ params->he_spr_non_srg_obss_pd_max_offset)))
+ goto fail;
+
+ if ((params->he_spr_ctrl &
+ SPATIAL_REUSE_SRG_INFORMATION_PRESENT) &&
+ (nla_put_u8(msg, NL80211_HE_OBSS_PD_ATTR_MIN_OFFSET,
+ params->he_spr_srg_obss_pd_min_offset) ||
+ nla_put_u8(msg, NL80211_HE_OBSS_PD_ATTR_MAX_OFFSET,
+ params->he_spr_srg_obss_pd_max_offset) ||
+ nla_put(msg, NL80211_HE_OBSS_PD_ATTR_BSS_COLOR_BITMAP,
+ sizeof(params->he_spr_bss_color_bitmap),
+ params->he_spr_bss_color_bitmap) ||
+ nla_put(msg, NL80211_HE_OBSS_PD_ATTR_PARTIAL_BSSID_BITMAP,
+ sizeof(params->he_spr_partial_bssid_bitmap),
+ params->he_spr_partial_bssid_bitmap)))
+ goto fail;
+
+ nla_nest_end(msg, spr);
+ }
+
+ if (params->freq && nl80211_put_freq_params(msg, params->freq) < 0)
+ goto fail;
+
+ if (params->freq && params->freq->he_enabled) {
+ struct nlattr *bss_color;
+
+ bss_color = nla_nest_start(msg, NL80211_ATTR_HE_BSS_COLOR);
+ if (!bss_color ||
+ (params->he_bss_color_disabled &&
+ nla_put_flag(msg, NL80211_HE_BSS_COLOR_ATTR_DISABLED)) ||
+ (params->he_bss_color_partial &&
+ nla_put_flag(msg, NL80211_HE_BSS_COLOR_ATTR_PARTIAL)) ||
+ nla_put_u8(msg, NL80211_HE_BSS_COLOR_ATTR_COLOR,
+ params->he_bss_color))
+ goto fail;
+ nla_nest_end(msg, bss_color);
+ }
+
+ if (params->twt_responder) {
+ wpa_printf(MSG_DEBUG, "nl80211: twt_responder=%d",
+ params->twt_responder);
+ if (nla_put_flag(msg, NL80211_ATTR_TWT_RESPONDER))
+ goto fail;
+ }
+
+ if (params->unsol_bcast_probe_resp_interval &&
+ nl80211_unsol_bcast_probe_resp(bss, msg, params) < 0)
+ goto fail;
+
+ if (nl80211_mbssid(msg, params) < 0)
+ goto fail;
+#endif /* CONFIG_IEEE80211AX */
+
+#ifdef CONFIG_SAE
+ if (wpa_key_mgmt_sae(params->key_mgmt_suites) &&
+ nl80211_put_sae_pwe(msg, params->sae_pwe) < 0)
+ goto fail;
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_FILS
+ if (params->fd_max_int && nl80211_fils_discovery(bss, msg, params) < 0)
+ goto fail;
+#endif /* CONFIG_FILS */
+
+ if (params->punct_bitmap) {
+ wpa_printf(MSG_DEBUG, "nl80211: Puncturing bitmap=0x%04x",
+ params->punct_bitmap);
+ if (nla_put_u32(msg, NL80211_ATTR_PUNCT_BITMAP,
+ params->punct_bitmap))
+ goto fail;
+ }
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+ if (cmd == NL80211_CMD_NEW_BEACON && params->allowed_freqs)
+ qca_set_allowed_ap_freqs(drv, params->allowed_freqs,
+ int_array_len(params->allowed_freqs));
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+ ret = send_and_recv_msgs_connect_handle(drv, msg, bss, 1);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)",
+ ret, strerror(-ret));
+ if (!bss->flink->beacon_set)
+ ret = 0;
+ bss->flink->beacon_set = 0;
+ } else {
+ link->beacon_set = 1;
+ nl80211_set_bss(bss, params->cts_protect, params->preamble,
+ params->short_slot_time, params->ht_opmode,
+ params->isolate, params->basic_rates);
+ nl80211_set_multicast_to_unicast(bss,
+ params->multicast_to_unicast);
+ if (beacon_set && params->freq &&
+ params->freq->bandwidth != link->bandwidth) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Update BSS %s bandwidth: %d -> %d",
+ bss->ifname, link->bandwidth,
+ params->freq->bandwidth);
+ ret = nl80211_set_channel(bss, params->freq, 1);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Frequency set failed: %d (%s)",
+ ret, strerror(-ret));
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Frequency set succeeded for ht2040 coex");
+ link->bandwidth = params->freq->bandwidth;
+ }
+ } else if (!beacon_set && params->freq) {
+ /*
+ * cfg80211 updates the driver on frequence change in AP
+ * mode only at the point when beaconing is started, so
+ * set the initial value here.
+ */
+ link->bandwidth = params->freq->bandwidth;
+ }
+ }
+
+#ifdef CONFIG_MESH
+ if (is_mesh_interface(drv->nlmode) && params->ht_opmode != -1) {
+ os_memset(&mesh_params, 0, sizeof(mesh_params));
+ mesh_params.flags |= WPA_DRIVER_MESH_CONF_FLAG_HT_OP_MODE;
+ mesh_params.ht_opmode = params->ht_opmode;
+ ret = nl80211_set_mesh_config(priv, &mesh_params);
+ if (ret < 0)
+ return ret;
+ }
+#endif /* CONFIG_MESH */
+
+ return ret;
+fail:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+static bool nl80211_link_valid(struct i802_bss *bss, s8 link_id)
+{
+ unsigned int i;
+
+ if (link_id < 0)
+ return false;
+
+ for (i = 0; i < bss->n_links; i++) {
+ wpa_printf(MSG_DEBUG, "nl80211: %s - i=%u, link_id=%u",
+ __func__, i, bss->links[i].link_id);
+ if (bss->links[i].link_id == NL80211_DRV_LINK_ID_NA)
+ continue;
+
+ if (bss->links[i].link_id == link_id)
+ return true;
+ }
+
+ return false;
+}
+
+
+static int nl80211_set_channel(struct i802_bss *bss,
+ struct hostapd_freq_params *freq, int set_chan)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Set freq %d, channel %d(ht_enabled=%d, vht_enabled=%d, he_enabled=%d, eht_enabled=%d, bandwidth=%d MHz, cf1=%d MHz, cf2=%d MHz)",
+ freq->freq, freq->channel, freq->ht_enabled, freq->vht_enabled,
+ freq->he_enabled, freq->eht_enabled, freq->bandwidth,
+ freq->center_freq1, freq->center_freq2);
+
+ msg = nl80211_bss_msg(bss, 0, set_chan ? NL80211_CMD_SET_CHANNEL :
+ NL80211_CMD_SET_WIPHY);
+ if (!msg || nl80211_put_freq_params(msg, freq) < 0) {
+ nlmsg_free(msg);
+ return -1;
+ }
+
+ if (nl80211_link_valid(bss, freq->link_id)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Set link_id=%u for freq",
+ freq->link_id);
+
+ if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, freq->link_id)) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ if (ret == 0) {
+ nl80211_link_set_freq(bss, freq->link_id, freq->freq);
+ return 0;
+ }
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to set channel (freq=%d): "
+ "%d (%s)", freq->freq, ret, strerror(-ret));
+ return -1;
+}
+
+
+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);
+ if (flags & WPA_STA_TDLS_PEER)
+ f |= BIT(NL80211_STA_FLAG_TDLS_PEER);
+ if (flags & WPA_STA_AUTHENTICATED)
+ f |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
+ if (flags & WPA_STA_ASSOCIATED)
+ f |= BIT(NL80211_STA_FLAG_ASSOCIATED);
+
+ return f;
+}
+
+
+#ifdef CONFIG_MESH
+static u32 sta_plink_state_nl80211(enum mesh_plink_state state)
+{
+ switch (state) {
+ case PLINK_IDLE:
+ return NL80211_PLINK_LISTEN;
+ case PLINK_OPN_SNT:
+ return NL80211_PLINK_OPN_SNT;
+ case PLINK_OPN_RCVD:
+ return NL80211_PLINK_OPN_RCVD;
+ case PLINK_CNF_RCVD:
+ return NL80211_PLINK_CNF_RCVD;
+ case PLINK_ESTAB:
+ return NL80211_PLINK_ESTAB;
+ case PLINK_HOLDING:
+ return NL80211_PLINK_HOLDING;
+ case PLINK_BLOCKED:
+ return NL80211_PLINK_BLOCKED;
+ default:
+ wpa_printf(MSG_ERROR, "nl80211: Invalid mesh plink state %d",
+ state);
+ }
+ return -1;
+}
+#endif /* CONFIG_MESH */
+
+
+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;
+ struct nl80211_sta_flag_update upd;
+ int ret = -ENOBUFS;
+ u8 cmd;
+ const char *cmd_string;
+
+ if ((params->flags & WPA_STA_TDLS_PEER) &&
+ !(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT))
+ return -EOPNOTSUPP;
+
+ if (params->mld_link_sta) {
+ cmd = params->set ? NL80211_CMD_MODIFY_LINK_STA :
+ NL80211_CMD_ADD_LINK_STA;
+ cmd_string = params->set ? "NL80211_CMD_MODIFY_LINK_STA" :
+ "NL80211_CMD_ADD_LINK_STA";
+ } else {
+ cmd = params->set ? NL80211_CMD_SET_STATION :
+ NL80211_CMD_NEW_STATION;
+ cmd_string = params->set ? "NL80211_CMD_SET_STATION" :
+ "NL80211_CMD_NEW_STATION";
+ }
+
+ wpa_printf(MSG_DEBUG, "nl80211: %s STA " MACSTR,
+ cmd_string, MAC2STR(params->addr));
+ msg = nl80211_bss_msg(bss, 0, cmd);
+ if (!msg)
+ goto fail;
+
+ /*
+ * Set the below properties only in one of the following cases:
+ * 1. New station is added, already associated.
+ * 2. Set WPA_STA_TDLS_PEER station.
+ * 3. Set an already added unassociated station, if driver supports
+ * full AP client state. (Set these properties after station became
+ * associated will be rejected by the driver).
+ */
+ if (!params->set || (params->flags & WPA_STA_TDLS_PEER) ||
+ (params->set && FULL_AP_CLIENT_STATE_SUPP(drv->capa.flags) &&
+ (params->flags & WPA_STA_ASSOCIATED))) {
+ wpa_hexdump(MSG_DEBUG, " * supported rates",
+ params->supp_rates, params->supp_rates_len);
+ wpa_printf(MSG_DEBUG, " * capability=0x%x",
+ params->capability);
+ if (nla_put(msg, NL80211_ATTR_STA_SUPPORTED_RATES,
+ params->supp_rates_len, params->supp_rates) ||
+ nla_put_u16(msg, NL80211_ATTR_STA_CAPABILITY,
+ params->capability))
+ goto fail;
+
+ if (params->ht_capabilities) {
+ wpa_hexdump(MSG_DEBUG, " * ht_capabilities",
+ (u8 *) params->ht_capabilities,
+ sizeof(*params->ht_capabilities));
+ if (nla_put(msg, NL80211_ATTR_HT_CAPABILITY,
+ sizeof(*params->ht_capabilities),
+ params->ht_capabilities))
+ goto fail;
+ }
+
+ if (params->vht_capabilities) {
+ wpa_hexdump(MSG_DEBUG, " * vht_capabilities",
+ (u8 *) params->vht_capabilities,
+ sizeof(*params->vht_capabilities));
+ if (nla_put(msg, NL80211_ATTR_VHT_CAPABILITY,
+ sizeof(*params->vht_capabilities),
+ params->vht_capabilities))
+ goto fail;
+ }
+
+ if (params->he_capab) {
+ wpa_hexdump(MSG_DEBUG, " * he_capab",
+ params->he_capab, params->he_capab_len);
+ if (nla_put(msg, NL80211_ATTR_HE_CAPABILITY,
+ params->he_capab_len, params->he_capab))
+ goto fail;
+ }
+
+ if (params->he_6ghz_capab) {
+ wpa_hexdump(MSG_DEBUG, " * he_6ghz_capab",
+ params->he_6ghz_capab,
+ sizeof(*params->he_6ghz_capab));
+ if (nla_put(msg, NL80211_ATTR_HE_6GHZ_CAPABILITY,
+ sizeof(*params->he_6ghz_capab),
+ params->he_6ghz_capab))
+ goto fail;
+ }
+
+ if (params->eht_capab) {
+ wpa_hexdump(MSG_DEBUG, " * eht_capab",
+ params->eht_capab, params->eht_capab_len);
+ if (nla_put(msg, NL80211_ATTR_EHT_CAPABILITY,
+ params->eht_capab_len, params->eht_capab))
+ goto fail;
+ }
+
+ if (params->ext_capab) {
+ wpa_hexdump(MSG_DEBUG, " * ext_capab",
+ params->ext_capab, params->ext_capab_len);
+ if (nla_put(msg, NL80211_ATTR_STA_EXT_CAPABILITY,
+ params->ext_capab_len, params->ext_capab))
+ goto fail;
+ }
+
+ if (is_ap_interface(drv->nlmode) &&
+ nla_put_u8(msg, NL80211_ATTR_STA_SUPPORT_P2P_PS,
+ params->support_p2p_ps ?
+ NL80211_P2P_PS_SUPPORTED :
+ NL80211_P2P_PS_UNSUPPORTED))
+ goto fail;
+ }
+ if (!params->set) {
+ if (params->aid) {
+ wpa_printf(MSG_DEBUG, " * aid=%u", params->aid);
+ if (nla_put_u16(msg, NL80211_ATTR_STA_AID, params->aid))
+ goto fail;
+ } else {
+ /*
+ * cfg80211 validates that AID is non-zero, so we have
+ * to make this a non-zero value for the TDLS case where
+ * a stub STA entry is used for now and for a station
+ * that is still not associated.
+ */
+ wpa_printf(MSG_DEBUG, " * aid=1 (%s workaround)",
+ (params->flags & WPA_STA_TDLS_PEER) ?
+ "TDLS" : "UNASSOC_STA");
+ if (nla_put_u16(msg, NL80211_ATTR_STA_AID, 1))
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, " * listen_interval=%u",
+ params->listen_interval);
+ if (nla_put_u16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL,
+ params->listen_interval))
+ goto fail;
+ } else if (params->aid && (params->flags & WPA_STA_TDLS_PEER)) {
+ wpa_printf(MSG_DEBUG, " * peer_aid=%u", params->aid);
+ if (nla_put_u16(msg, NL80211_ATTR_PEER_AID, params->aid))
+ goto fail;
+ } else if (FULL_AP_CLIENT_STATE_SUPP(drv->capa.flags) &&
+ (params->flags & WPA_STA_ASSOCIATED)) {
+ wpa_printf(MSG_DEBUG, " * aid=%u", params->aid);
+ wpa_printf(MSG_DEBUG, " * listen_interval=%u",
+ params->listen_interval);
+ if (nla_put_u16(msg, NL80211_ATTR_STA_AID, params->aid) ||
+ nla_put_u16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL,
+ params->listen_interval))
+ goto fail;
+ }
+
+ if (params->vht_opmode_enabled) {
+ wpa_printf(MSG_DEBUG, " * opmode=%u", params->vht_opmode);
+ if (nla_put_u8(msg, NL80211_ATTR_OPMODE_NOTIF,
+ params->vht_opmode))
+ goto fail;
+ }
+
+ if (params->supp_channels) {
+ wpa_hexdump(MSG_DEBUG, " * supported channels",
+ params->supp_channels, params->supp_channels_len);
+ if (nla_put(msg, NL80211_ATTR_STA_SUPPORTED_CHANNELS,
+ params->supp_channels_len, params->supp_channels))
+ goto fail;
+ }
+
+ if (params->supp_oper_classes) {
+ wpa_hexdump(MSG_DEBUG, " * supported operating classes",
+ params->supp_oper_classes,
+ params->supp_oper_classes_len);
+ if (nla_put(msg, NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES,
+ params->supp_oper_classes_len,
+ params->supp_oper_classes))
+ goto fail;
+ }
+
+ os_memset(&upd, 0, sizeof(upd));
+ upd.set = sta_flags_nl80211(params->flags);
+ upd.mask = upd.set | sta_flags_nl80211(params->flags_mask);
+
+ /*
+ * If the driver doesn't support full AP client state, ignore ASSOC/AUTH
+ * flags, as nl80211 driver moves a new station, by default, into
+ * associated state.
+ *
+ * On the other hand, if the driver supports that feature and the
+ * station is added in unauthenticated state, set the
+ * authenticated/associated bits in the mask to prevent moving this
+ * station to associated state before it is actually associated.
+ *
+ * This is irrelevant for mesh mode where the station is added to the
+ * driver as authenticated already, and ASSOCIATED isn't part of the
+ * nl80211 API.
+ */
+ if (!is_mesh_interface(drv->nlmode)) {
+ if (!FULL_AP_CLIENT_STATE_SUPP(drv->capa.flags)) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Ignore ASSOC/AUTH flags since driver doesn't support full AP client state");
+ upd.mask &= ~(BIT(NL80211_STA_FLAG_ASSOCIATED) |
+ BIT(NL80211_STA_FLAG_AUTHENTICATED));
+ } else if (!params->set &&
+ !(params->flags & WPA_STA_TDLS_PEER)) {
+ if (!(params->flags & WPA_STA_AUTHENTICATED))
+ upd.mask |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
+ if (!(params->flags & WPA_STA_ASSOCIATED))
+ upd.mask |= BIT(NL80211_STA_FLAG_ASSOCIATED);
+ }
+#ifdef CONFIG_MESH
+ } else {
+ if (params->plink_state == PLINK_ESTAB && params->peer_aid) {
+ ret = nla_put_u16(msg, NL80211_ATTR_MESH_PEER_AID,
+ params->peer_aid);
+ if (ret)
+ goto fail;
+ }
+#endif /* CONFIG_MESH */
+ }
+
+ wpa_printf(MSG_DEBUG, " * flags set=0x%x mask=0x%x",
+ upd.set, upd.mask);
+ if (nla_put(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd))
+ goto fail;
+
+#ifdef CONFIG_MESH
+ if (params->plink_state &&
+ nla_put_u8(msg, NL80211_ATTR_STA_PLINK_STATE,
+ sta_plink_state_nl80211(params->plink_state)))
+ goto fail;
+#endif /* CONFIG_MESH */
+
+ if ((!params->set || (params->flags & WPA_STA_TDLS_PEER) ||
+ FULL_AP_CLIENT_STATE_SUPP(drv->capa.flags)) &&
+ (params->flags & WPA_STA_WMM)) {
+ struct nlattr *wme = nla_nest_start(msg, NL80211_ATTR_STA_WME);
+
+ wpa_printf(MSG_DEBUG, " * qosinfo=0x%x", params->qosinfo);
+ if (!wme ||
+ nla_put_u8(msg, NL80211_STA_WME_UAPSD_QUEUES,
+ params->qosinfo & WMM_QOSINFO_STA_AC_MASK) ||
+ nla_put_u8(msg, NL80211_STA_WME_MAX_SP,
+ (params->qosinfo >> WMM_QOSINFO_STA_SP_SHIFT) &
+ WMM_QOSINFO_STA_SP_MASK))
+ goto fail;
+ nla_nest_end(msg, wme);
+ }
+
+ /* In case we are an AP MLD need to always specify the link ID */
+ if (params->mld_link_id >= 0) {
+ wpa_printf(MSG_DEBUG, " * mld_link_id=%d",
+ params->mld_link_id);
+ if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID,
+ params->mld_link_id))
+ goto fail;
+
+ /*
+ * If the link address is specified the station is a non-AP MLD
+ * and thus need to provide the MLD address as the station
+ * address, and the non-AP MLD link address as the link address.
+ */
+ if (params->mld_link_addr) {
+ wpa_printf(MSG_DEBUG, " * mld_link_addr=" MACSTR,
+ MAC2STR(params->mld_link_addr));
+
+ if (nla_put(msg, NL80211_ATTR_MLD_ADDR,
+ ETH_ALEN, params->addr) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN,
+ params->mld_link_addr))
+ goto fail;
+ } else {
+ if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN,
+ params->addr))
+ goto fail;
+ }
+ } else {
+ if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->addr))
+ goto fail;
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ msg = NULL;
+ if (ret)
+ wpa_printf(MSG_DEBUG, "nl80211: %s result: %d (%s)",
+ cmd_string, ret, strerror(-ret));
+ if (ret == -EEXIST)
+ ret = 0;
+fail:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+static void rtnl_neigh_delete_fdb_entry(struct i802_bss *bss, const u8 *addr)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct ndmsg nhdr = {
+ .ndm_state = NUD_PERMANENT,
+ .ndm_ifindex = bss->ifindex,
+ .ndm_family = AF_BRIDGE,
+ };
+ struct nl_msg *msg;
+ int err;
+
+ msg = nlmsg_alloc_simple(RTM_DELNEIGH, NLM_F_CREATE);
+ if (!msg)
+ return;
+
+ if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0)
+ goto errout;
+
+ if (nla_put(msg, NDA_LLADDR, ETH_ALEN, (void *)addr))
+ goto errout;
+
+ if (nl_send_auto_complete(drv->rtnl_sk, msg) < 0)
+ goto errout;
+
+ err = nl_wait_for_ack(drv->rtnl_sk);
+ if (err < 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: bridge FDB entry delete for "
+ MACSTR " ifindex=%d failed: %s", MAC2STR(addr),
+ bss->ifindex, nl_geterror(err));
+ } else {
+ wpa_printf(MSG_DEBUG, "nl80211: deleted bridge FDB entry for "
+ MACSTR, MAC2STR(addr));
+ }
+
+errout:
+ nlmsg_free(msg);
+}
+
+
+static int wpa_driver_nl80211_sta_remove(struct i802_bss *bss, const u8 *addr,
+ int deauth, u16 reason_code)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret;
+
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_DEL_STATION)) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
+ (deauth == 0 &&
+ nla_put_u8(msg, NL80211_ATTR_MGMT_SUBTYPE,
+ WLAN_FC_STYPE_DISASSOC)) ||
+ (deauth == 1 &&
+ nla_put_u8(msg, NL80211_ATTR_MGMT_SUBTYPE,
+ WLAN_FC_STYPE_DEAUTH)) ||
+ (reason_code &&
+ nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason_code))) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ wpa_printf(MSG_DEBUG, "nl80211: sta_remove -> DEL_STATION %s " MACSTR
+ " --> %d (%s)",
+ bss->ifname, MAC2STR(addr), ret, strerror(-ret));
+
+ if (drv->rtnl_sk)
+ rtnl_neigh_delete_fdb_entry(bss, addr);
+
+ if (ret == -ENOENT)
+ return 0;
+ return ret;
+}
+
+
+void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, int ifidx)
+{
+ struct nl_msg *msg;
+ struct wpa_driver_nl80211_data *drv2;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Remove interface ifindex=%d", ifidx);
+
+ /* stop listening for EAPOL on this interface */
+ dl_list_for_each(drv2, &drv->global->interfaces,
+ struct wpa_driver_nl80211_data, list)
+ {
+ del_ifidx(drv2, ifidx, IFIDX_ANY);
+ /* Remove all bridges learned for this iface */
+ del_ifidx(drv2, IFIDX_ANY, ifidx);
+ }
+
+ msg = nl80211_ifindex_msg(drv, ifidx, 0, NL80211_CMD_DEL_INTERFACE);
+ if (send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL) == 0)
+ return;
+ wpa_printf(MSG_ERROR, "Failed to remove interface (ifidx=%d)", ifidx);
+}
+
+
+const char * nl80211_iftype_str(enum nl80211_iftype mode)
+{
+ switch (mode) {
+ case NL80211_IFTYPE_ADHOC:
+ return "ADHOC";
+ case NL80211_IFTYPE_STATION:
+ return "STATION";
+ case NL80211_IFTYPE_AP:
+ return "AP";
+ case NL80211_IFTYPE_AP_VLAN:
+ return "AP_VLAN";
+ case NL80211_IFTYPE_WDS:
+ return "WDS";
+ case NL80211_IFTYPE_MONITOR:
+ return "MONITOR";
+ case NL80211_IFTYPE_MESH_POINT:
+ return "MESH_POINT";
+ case NL80211_IFTYPE_P2P_CLIENT:
+ return "P2P_CLIENT";
+ case NL80211_IFTYPE_P2P_GO:
+ return "P2P_GO";
+ case NL80211_IFTYPE_P2P_DEVICE:
+ return "P2P_DEVICE";
+ case NL80211_IFTYPE_OCB:
+ return "OCB";
+ case NL80211_IFTYPE_NAN:
+ return "NAN";
+ default:
+ return "unknown";
+ }
+}
+
+
+static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv,
+ const char *ifname,
+ enum nl80211_iftype iftype,
+ const u8 *addr, int wds,
+ int (*handler)(struct nl_msg *, void *),
+ void *arg)
+{
+ struct nl_msg *msg;
+ int ifidx;
+ int ret = -ENOBUFS;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Create interface iftype %d (%s)",
+ iftype, nl80211_iftype_str(iftype));
+
+ msg = nl80211_cmd_msg(drv->first_bss, 0, NL80211_CMD_NEW_INTERFACE);
+ if (!msg ||
+ nla_put_string(msg, NL80211_ATTR_IFNAME, ifname) ||
+ nla_put_u32(msg, NL80211_ATTR_IFTYPE, iftype))
+ goto fail;
+
+ if (iftype == NL80211_IFTYPE_MONITOR) {
+ struct nlattr *flags;
+
+ flags = nla_nest_start(msg, NL80211_ATTR_MNTR_FLAGS);
+ if (!flags ||
+ nla_put_flag(msg, NL80211_MNTR_FLAG_COOK_FRAMES))
+ goto fail;
+
+ nla_nest_end(msg, flags);
+ } else if (wds) {
+ if (nla_put_u8(msg, NL80211_ATTR_4ADDR, wds))
+ goto fail;
+ }
+
+ /*
+ * Tell cfg80211 that the interface belongs to the socket that created
+ * it, and the interface should be deleted when the socket is closed.
+ */
+ if (nla_put_flag(msg, NL80211_ATTR_IFACE_SOCKET_OWNER))
+ goto fail;
+
+ if ((addr && iftype == NL80211_IFTYPE_P2P_DEVICE) &&
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr))
+ goto fail;
+
+ ret = send_and_recv_msgs(drv, msg, handler, arg, NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ fail:
+ nlmsg_free(msg);
+ wpa_printf(MSG_ERROR, "Failed to create interface %s: %d (%s)",
+ ifname, ret, strerror(-ret));
+ return ret;
+ }
+
+ if (iftype == NL80211_IFTYPE_P2P_DEVICE)
+ return 0;
+
+ ifidx = if_nametoindex(ifname);
+ wpa_printf(MSG_DEBUG, "nl80211: New interface %s created: ifindex=%d",
+ ifname, ifidx);
+
+ if (ifidx <= 0)
+ return -1;
+
+ /*
+ * Some virtual interfaces need to process EAPOL packets and events on
+ * the parent interface. This is used mainly with hostapd.
+ */
+ if (drv->hostapd ||
+ iftype == NL80211_IFTYPE_AP_VLAN ||
+ iftype == NL80211_IFTYPE_WDS ||
+ iftype == NL80211_IFTYPE_MONITOR) {
+ /* start listening for EAPOL on this interface */
+ add_ifidx(drv, ifidx, IFIDX_ANY);
+ }
+
+ if (addr && iftype != NL80211_IFTYPE_MONITOR &&
+ linux_set_ifhwaddr(drv->global->ioctl_sock, ifname, addr)) {
+ nl80211_remove_iface(drv, ifidx);
+ return -1;
+ }
+
+ return ifidx;
+}
+
+
+int nl80211_create_iface(struct wpa_driver_nl80211_data *drv,
+ const char *ifname, enum nl80211_iftype iftype,
+ const u8 *addr, int wds,
+ int (*handler)(struct nl_msg *, void *),
+ void *arg, int use_existing)
+{
+ int ret;
+
+ ret = nl80211_create_iface_once(drv, ifname, iftype, addr, wds, handler,
+ arg);
+
+ /* if error occurred and interface exists already */
+ if (ret == -ENFILE && if_nametoindex(ifname)) {
+ if (use_existing) {
+ wpa_printf(MSG_DEBUG, "nl80211: Continue using existing interface %s",
+ ifname);
+ if (addr && iftype != NL80211_IFTYPE_MONITOR &&
+ linux_set_ifhwaddr(drv->global->ioctl_sock, ifname,
+ addr) < 0 &&
+ (linux_set_iface_flags(drv->global->ioctl_sock,
+ ifname, 0) < 0 ||
+ linux_set_ifhwaddr(drv->global->ioctl_sock, ifname,
+ addr) < 0 ||
+ linux_set_iface_flags(drv->global->ioctl_sock,
+ ifname, 1) < 0))
+ return -1;
+ return -ENFILE;
+ }
+ 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, handler, arg);
+ }
+
+ if (ret >= 0 && is_p2p_net_interface(iftype)) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Interface %s created for P2P - disable 11b rates",
+ ifname);
+ nl80211_disable_11b_rates(drv, ret, 1);
+ }
+
+ return ret;
+}
+
+
+static int nl80211_setup_ap(struct i802_bss *bss)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Setup AP(%s) - device_ap_sme=%d use_monitor=%d",
+ bss->ifname, drv->device_ap_sme, drv->use_monitor);
+
+ /*
+ * Disable Probe Request reporting unless we need it in this way for
+ * devices that include the AP SME, in the other case (unless using
+ * monitor iface) we'll get it through the nl_mgmt socket instead.
+ */
+ if (!drv->device_ap_sme)
+ wpa_driver_nl80211_probe_req_report(bss, 0);
+
+ if (!drv->device_ap_sme && !drv->use_monitor)
+ if (nl80211_mgmt_subscribe_ap(bss))
+ return -1;
+
+ if (drv->device_ap_sme && !drv->use_monitor)
+ if (nl80211_mgmt_subscribe_ap_dev_sme(bss))
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Failed to subscribe for mgmt frames from SME driver - trying to run without it");
+
+ if (!drv->device_ap_sme && drv->use_monitor &&
+ nl80211_create_monitor_interface(drv) &&
+ !drv->device_ap_sme)
+ return -1;
+
+ if (drv->device_ap_sme &&
+ wpa_driver_nl80211_probe_req_report(bss, 1) < 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to enable "
+ "Probe Request frame reporting in AP mode");
+ /* Try to survive without this */
+ }
+
+ return 0;
+}
+
+
+static void nl80211_teardown_ap(struct i802_bss *bss)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Teardown AP(%s) - device_ap_sme=%d use_monitor=%d",
+ bss->ifname, drv->device_ap_sme, drv->use_monitor);
+ if (drv->device_ap_sme) {
+ wpa_driver_nl80211_probe_req_report(bss, 0);
+ if (!drv->use_monitor)
+ nl80211_mgmt_unsubscribe(bss, "AP teardown (dev SME)");
+ } else if (drv->use_monitor)
+ nl80211_remove_monitor_interface(drv);
+ else
+ nl80211_mgmt_unsubscribe(bss, "AP teardown");
+
+ nl80211_put_wiphy_data_ap(bss);
+ wpa_driver_nl80211_del_beacon_all(bss);
+}
+
+
+static int nl80211_tx_control_port(void *priv, const u8 *dest,
+ u16 proto, const u8 *buf, size_t len,
+ int no_encrypt, int link_id)
+{
+ struct nl80211_ack_ext_arg ext_arg;
+ struct i802_bss *bss = priv;
+ struct nl_msg *msg;
+ u64 cookie = 0;
+ int ret;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Send over control port dest=" MACSTR
+ " proto=0x%04x len=%u no_encrypt=%d",
+ MAC2STR(dest), proto, (unsigned int) len, no_encrypt);
+
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_CONTROL_PORT_FRAME);
+ if (!msg ||
+ nla_put_u16(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE, proto) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, dest) ||
+ nla_put(msg, NL80211_ATTR_FRAME, len, buf) ||
+ (no_encrypt &&
+ nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT)) ||
+ (link_id != NL80211_DRV_LINK_ID_NA &&
+ nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id))) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ os_memset(&ext_arg, 0, sizeof(struct nl80211_ack_ext_arg));
+ ext_arg.ext_data = &cookie;
+ ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL,
+ ack_handler_cookie, &ext_arg);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: tx_control_port failed: ret=%d (%s)",
+ ret, strerror(-ret));
+ } else {
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: tx_control_port cookie=0x%llx",
+ (long long unsigned int) cookie);
+ drv->eapol_tx_cookie = cookie;
+ drv->eapol_tx_link_id = link_id;
+ }
+
+ return ret;
+}
+
+
+static int nl80211_send_eapol_data(struct i802_bss *bss,
+ const u8 *addr, const u8 *data,
+ size_t data_len)
+{
+ struct sockaddr_ll ll;
+ int ret;
+
+ if (bss->drv->eapol_tx_sock < 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: No socket to send EAPOL");
+ return -1;
+ }
+
+ os_memset(&ll, 0, sizeof(ll));
+ ll.sll_family = AF_PACKET;
+ ll.sll_ifindex = bss->ifindex;
+ ll.sll_protocol = htons(ETH_P_PAE);
+ ll.sll_halen = ETH_ALEN;
+ os_memcpy(ll.sll_addr, addr, ETH_ALEN);
+ ret = sendto(bss->drv->eapol_tx_sock, data, data_len, 0,
+ (struct sockaddr *) &ll, sizeof(ll));
+ if (ret < 0)
+ wpa_printf(MSG_ERROR, "nl80211: EAPOL TX: %s",
+ strerror(errno));
+
+ return ret;
+}
+
+
+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,
+ int link_id)
+{
+ 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;
+
+ /* For now, disable EAPOL TX over control port in AP mode by default
+ * since it does not provide TX status notifications. */
+ if (drv->control_port_ap &&
+ (drv->capa.flags & WPA_DRIVER_FLAGS_CONTROL_PORT))
+ return nl80211_tx_control_port(bss, addr, ETH_P_EAPOL,
+ data, data_len, !encrypt,
+ link_id);
+
+ if (drv->device_ap_sme || !drv->use_monitor)
+ return nl80211_send_eapol_data(bss, addr, data, data_len);
+
+ len = sizeof(*hdr) + (qos ? 2 : 0) + sizeof(rfc1042_header) + 2 +
+ data_len;
+ hdr = os_zalloc(len);
+ if (hdr == NULL) {
+ wpa_printf(MSG_INFO, "nl80211: Failed to allocate EAPOL buffer(len=%lu)",
+ (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) {
+ /* Set highest priority in QoS header */
+ pos[0] = 7;
+ 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 = nl80211_send_monitor(drv, hdr, len, encrypt, 0);
+ if (res < 0) {
+ wpa_printf(MSG_ERROR,
+ "hapd_send_eapol - packet len: %lu - failed",
+ (unsigned long) len);
+ }
+ os_free(hdr);
+
+ return res;
+}
+
+
+static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr,
+ unsigned int total_flags,
+ unsigned int flags_or,
+ unsigned int flags_and)
+{
+ struct i802_bss *bss = priv;
+ struct nl_msg *msg;
+ struct nlattr *flags;
+ struct nl80211_sta_flag_update upd;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Set STA flags - ifname=%s addr=" MACSTR
+ " total_flags=0x%x flags_or=0x%x flags_and=0x%x authorized=%d",
+ bss->ifname, MAC2STR(addr), total_flags, flags_or, flags_and,
+ !!(total_flags & WPA_STA_AUTHORIZED));
+
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_STATION)) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr))
+ goto fail;
+
+ /*
+ * Backwards compatibility version using NL80211_ATTR_STA_FLAGS. This
+ * can be removed eventually.
+ */
+ flags = nla_nest_start(msg, NL80211_ATTR_STA_FLAGS);
+ if (!flags ||
+ ((total_flags & WPA_STA_AUTHORIZED) &&
+ nla_put_flag(msg, NL80211_STA_FLAG_AUTHORIZED)) ||
+ ((total_flags & WPA_STA_WMM) &&
+ nla_put_flag(msg, NL80211_STA_FLAG_WME)) ||
+ ((total_flags & WPA_STA_SHORT_PREAMBLE) &&
+ nla_put_flag(msg, NL80211_STA_FLAG_SHORT_PREAMBLE)) ||
+ ((total_flags & WPA_STA_MFP) &&
+ nla_put_flag(msg, NL80211_STA_FLAG_MFP)) ||
+ ((total_flags & WPA_STA_TDLS_PEER) &&
+ nla_put_flag(msg, NL80211_STA_FLAG_TDLS_PEER)))
+ goto fail;
+
+ nla_nest_end(msg, flags);
+
+ os_memset(&upd, 0, sizeof(upd));
+ upd.mask = sta_flags_nl80211(flags_or | ~flags_and);
+ upd.set = sta_flags_nl80211(flags_or);
+ if (nla_put(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd))
+ goto fail;
+
+ return send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
+fail:
+ nlmsg_free(msg);
+ return -ENOBUFS;
+}
+
+
+static int driver_nl80211_sta_set_airtime_weight(void *priv, const u8 *addr,
+ unsigned int weight)
+{
+ struct i802_bss *bss = priv;
+ struct nl_msg *msg;
+ int ret;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Set STA airtime weight - ifname=%s addr=" MACSTR
+ " weight=%u", bss->ifname, MAC2STR(addr), weight);
+
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_STATION)) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
+ nla_put_u16(msg, NL80211_ATTR_AIRTIME_WEIGHT, weight))
+ goto fail;
+
+ ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: SET_STATION[AIRTIME_WEIGHT] failed: ret=%d (%s)",
+ ret, strerror(-ret));
+ }
+ return ret;
+fail:
+ nlmsg_free(msg);
+ return -ENOBUFS;
+}
+
+
+static int wpa_driver_nl80211_ap(struct wpa_driver_nl80211_data *drv,
+ struct wpa_driver_associate_params *params)
+{
+ enum nl80211_iftype nlmode, old_mode;
+
+ if (params->p2p) {
+ wpa_printf(MSG_DEBUG, "nl80211: Setup AP operations for P2P "
+ "group (GO)");
+ nlmode = NL80211_IFTYPE_P2P_GO;
+ } else
+ nlmode = NL80211_IFTYPE_AP;
+
+ old_mode = drv->nlmode;
+ if (wpa_driver_nl80211_set_mode(drv->first_bss, nlmode)) {
+ nl80211_remove_monitor_interface(drv);
+ return -1;
+ }
+
+ if (params->freq.freq &&
+ nl80211_set_channel(drv->first_bss, ¶ms->freq, 0)) {
+ if (old_mode != nlmode)
+ wpa_driver_nl80211_set_mode(drv->first_bss, old_mode);
+ nl80211_remove_monitor_interface(drv);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv,
+ int reset_mode)
+{
+ struct nl_msg *msg;
+ int ret;
+
+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_LEAVE_IBSS);
+ ret = send_and_recv_msgs_connect_handle(drv, msg, drv->first_bss, 1);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Leave IBSS failed: ret=%d "
+ "(%s)", ret, strerror(-ret));
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Leave IBSS request sent successfully");
+ }
+
+ if (reset_mode &&
+ wpa_driver_nl80211_set_mode(drv->first_bss,
+ NL80211_IFTYPE_STATION)) {
+ wpa_printf(MSG_INFO, "nl80211: Failed to set interface into "
+ "station mode");
+ }
+
+ return ret;
+}
+
+
+static int nl80211_ht_vht_overrides(struct nl_msg *msg,
+ struct wpa_driver_associate_params *params)
+{
+ if (params->disable_ht && nla_put_flag(msg, NL80211_ATTR_DISABLE_HT))
+ return -1;
+
+ if (params->htcaps && params->htcaps_mask) {
+ int sz = sizeof(struct ieee80211_ht_capabilities);
+ wpa_hexdump(MSG_DEBUG, " * htcaps", params->htcaps, sz);
+ wpa_hexdump(MSG_DEBUG, " * htcaps_mask",
+ params->htcaps_mask, sz);
+ if (nla_put(msg, NL80211_ATTR_HT_CAPABILITY, sz,
+ params->htcaps) ||
+ nla_put(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sz,
+ params->htcaps_mask))
+ return -1;
+ }
+
+#ifdef CONFIG_VHT_OVERRIDES
+ if (params->disable_vht) {
+ wpa_printf(MSG_DEBUG, " * VHT disabled");
+ if (nla_put_flag(msg, NL80211_ATTR_DISABLE_VHT))
+ return -1;
+ }
+
+ if (params->vhtcaps && params->vhtcaps_mask) {
+ int sz = sizeof(struct ieee80211_vht_capabilities);
+ wpa_hexdump(MSG_DEBUG, " * vhtcaps", params->vhtcaps, sz);
+ wpa_hexdump(MSG_DEBUG, " * vhtcaps_mask",
+ params->vhtcaps_mask, sz);
+ if (nla_put(msg, NL80211_ATTR_VHT_CAPABILITY, sz,
+ params->vhtcaps) ||
+ nla_put(msg, NL80211_ATTR_VHT_CAPABILITY_MASK, sz,
+ params->vhtcaps_mask))
+ return -1;
+ }
+#endif /* CONFIG_VHT_OVERRIDES */
+
+#ifdef CONFIG_HE_OVERRIDES
+ if (params->disable_he) {
+ wpa_printf(MSG_DEBUG, " * HE disabled");
+ if (nla_put_flag(msg, NL80211_ATTR_DISABLE_HE))
+ return -1;
+ }
+#endif /* CONFIG_HE_OVERRIDES */
+
+ if (params->disable_eht) {
+ wpa_printf(MSG_DEBUG, " * EHT disabled");
+ if (nla_put_flag(msg, NL80211_ATTR_DISABLE_EHT))
+ return -1;
+ }
+
+ return 0;
+}
+
+
+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_ibss(drv->first_bss, ¶ms->freq)) {
+ wpa_printf(MSG_INFO, "nl80211: Failed to set interface into "
+ "IBSS mode");
+ return -1;
+ }
+
+retry:
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_JOIN_IBSS)) ||
+ params->ssid == NULL || params->ssid_len > sizeof(drv->ssid))
+ goto fail;
+
+ wpa_printf(MSG_DEBUG, " * SSID=%s",
+ wpa_ssid_txt(params->ssid, params->ssid_len));
+ if (nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid))
+ goto fail;
+ os_memcpy(drv->ssid, params->ssid, params->ssid_len);
+ drv->ssid_len = params->ssid_len;
+
+ if (nl80211_put_freq_params(msg, ¶ms->freq) < 0 ||
+ nl80211_put_beacon_int(msg, params->beacon_int))
+ goto fail;
+
+ ret = nl80211_set_conn_keys(params, msg);
+ if (ret)
+ goto fail;
+
+ if (params->bssid && params->fixed_bssid) {
+ wpa_printf(MSG_DEBUG, " * BSSID=" MACSTR,
+ MAC2STR(params->bssid));
+ if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid))
+ goto fail;
+ }
+
+ if (params->fixed_freq) {
+ wpa_printf(MSG_DEBUG, " * fixed_freq");
+ if (nla_put_flag(msg, NL80211_ATTR_FREQ_FIXED))
+ goto fail;
+ }
+
+ if (params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_PSK ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256) {
+ wpa_printf(MSG_DEBUG, " * control port");
+ if (nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT))
+ goto fail;
+ }
+
+ if (params->wpa_ie) {
+ wpa_hexdump(MSG_DEBUG,
+ " * Extra IEs for Beacon/Probe Response frames",
+ params->wpa_ie, params->wpa_ie_len);
+ if (nla_put(msg, NL80211_ATTR_IE, params->wpa_ie_len,
+ params->wpa_ie))
+ goto fail;
+ }
+
+ ret = nl80211_ht_vht_overrides(msg, params);
+ if (ret < 0)
+ goto fail;
+
+ ret = send_and_recv_msgs_connect_handle(drv, msg, drv->first_bss, 1);
+ 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, 0);
+ nlmsg_free(msg);
+ goto retry;
+ }
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Join IBSS request sent successfully");
+ }
+
+fail:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+static int nl80211_put_fils_connect_params(struct wpa_driver_nl80211_data *drv,
+ struct wpa_driver_associate_params *params,
+ struct nl_msg *msg)
+{
+ if (params->fils_erp_username_len) {
+ wpa_hexdump_ascii(MSG_DEBUG, " * FILS ERP EMSKname/username",
+ params->fils_erp_username,
+ params->fils_erp_username_len);
+ if (nla_put(msg, NL80211_ATTR_FILS_ERP_USERNAME,
+ params->fils_erp_username_len,
+ params->fils_erp_username))
+ return -1;
+ }
+
+ if (params->fils_erp_realm_len) {
+ wpa_hexdump_ascii(MSG_DEBUG, " * FILS ERP Realm",
+ params->fils_erp_realm,
+ params->fils_erp_realm_len);
+ if (nla_put(msg, NL80211_ATTR_FILS_ERP_REALM,
+ params->fils_erp_realm_len, params->fils_erp_realm))
+ return -1;
+ }
+
+ if (params->fils_erp_rrk_len) {
+ wpa_printf(MSG_DEBUG, " * FILS ERP next seq %u",
+ params->fils_erp_next_seq_num);
+ if (nla_put_u16(msg, NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM,
+ params->fils_erp_next_seq_num))
+ return -1;
+
+ wpa_printf(MSG_DEBUG, " * FILS ERP rRK (len=%lu)",
+ (unsigned long) params->fils_erp_rrk_len);
+ if (nla_put(msg, NL80211_ATTR_FILS_ERP_RRK,
+ params->fils_erp_rrk_len, params->fils_erp_rrk))
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static unsigned int num_bits_set(u32 val)
+{
+ unsigned int c;
+
+ for (c = 0; val; c++)
+ val &= val - 1;
+
+ return c;
+}
+
+
+static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv,
+ struct wpa_driver_associate_params *params,
+ struct nl_msg *msg)
+{
+ if (params->mld_params.mld_addr && params->mld_params.valid_links > 0) {
+ struct wpa_driver_mld_params *mld_params = ¶ms->mld_params;
+ struct nlattr *links, *attr;
+ int i;
+ u8 link_id;
+
+ wpa_printf(MSG_DEBUG, " * MLD: MLD addr=" MACSTR,
+ MAC2STR(mld_params->mld_addr));
+
+ if (nla_put(msg, NL80211_ATTR_MLD_ADDR, ETH_ALEN,
+ mld_params->mld_addr) ||
+ nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID,
+ mld_params->assoc_link_id))
+ return -1;
+
+ links = nla_nest_start(msg, NL80211_ATTR_MLO_LINKS);
+ if (!links)
+ return -1;
+
+ attr = nla_nest_start(msg, 0);
+ if (!attr)
+ return -1;
+
+ /* First add the association link ID */
+ link_id = mld_params->assoc_link_id;
+ if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN,
+ mld_params->mld_links[link_id].bssid) ||
+ nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
+ mld_params->mld_links[link_id].freq))
+ return -1;
+
+ os_memcpy(drv->sta_mlo_info.links[link_id].bssid,
+ mld_params->mld_links[link_id].bssid, ETH_ALEN);
+
+ nla_nest_end(msg, attr);
+
+ for (i = 1, link_id = 0; link_id < MAX_NUM_MLD_LINKS;
+ link_id++) {
+ if (!(mld_params->valid_links & BIT(link_id)) ||
+ link_id == mld_params->assoc_link_id)
+ continue;
+
+ attr = nla_nest_start(msg, i);
+ if (!attr)
+ return -1;
+
+ if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID,
+ link_id) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN,
+ mld_params->mld_links[link_id].bssid) ||
+ nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
+ mld_params->mld_links[link_id].freq) ||
+ (mld_params->mld_links[link_id].ies &&
+ mld_params->mld_links[i].ies_len &&
+ nla_put(msg, NL80211_ATTR_IE,
+ mld_params->mld_links[link_id].ies_len,
+ mld_params->mld_links[link_id].ies)))
+ return -1;
+
+ os_memcpy(drv->sta_mlo_info.links[link_id].bssid,
+ mld_params->mld_links[link_id].bssid,
+ ETH_ALEN);
+ nla_nest_end(msg, attr);
+ i++;
+ }
+
+ nla_nest_end(msg, links);
+
+ os_memcpy(drv->sta_mlo_info.ap_mld_addr,
+ params->mld_params.mld_addr, ETH_ALEN);
+ drv->sta_mlo_info.assoc_link_id = mld_params->assoc_link_id;
+ drv->sta_mlo_info.req_links = mld_params->valid_links;
+ }
+
+ if (nla_put_flag(msg, NL80211_ATTR_IFACE_SOCKET_OWNER))
+ return -1;
+
+ if (params->bssid && !params->mld_params.mld_addr) {
+ wpa_printf(MSG_DEBUG, " * bssid=" MACSTR,
+ MAC2STR(params->bssid));
+ if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid))
+ return -1;
+ }
+
+ if (params->bssid_hint) {
+ wpa_printf(MSG_DEBUG, " * bssid_hint=" MACSTR,
+ MAC2STR(params->bssid_hint));
+ if (nla_put(msg, NL80211_ATTR_MAC_HINT, ETH_ALEN,
+ params->bssid_hint))
+ return -1;
+ }
+
+ if (params->freq.freq && !params->mld_params.mld_addr) {
+ wpa_printf(MSG_DEBUG, " * freq=%d", params->freq.freq);
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
+ params->freq.freq))
+ return -1;
+ drv->assoc_freq = params->freq.freq;
+ } else
+ drv->assoc_freq = 0;
+
+ if (params->freq_hint) {
+ wpa_printf(MSG_DEBUG, " * freq_hint=%d", params->freq_hint);
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ_HINT,
+ params->freq_hint))
+ return -1;
+ }
+
+ if (params->freq.edmg.channels && params->freq.edmg.bw_config) {
+ wpa_printf(MSG_DEBUG,
+ " * EDMG configuration: channels=0x%x bw_config=%d",
+ params->freq.edmg.channels,
+ params->freq.edmg.bw_config);
+ if (nla_put_u8(msg, NL80211_ATTR_WIPHY_EDMG_CHANNELS,
+ params->freq.edmg.channels) ||
+ nla_put_u8(msg, NL80211_ATTR_WIPHY_EDMG_BW_CONFIG,
+ params->freq.edmg.bw_config))
+ return -1;
+ }
+
+ if (params->bg_scan_period >= 0) {
+ wpa_printf(MSG_DEBUG, " * bg scan period=%d",
+ params->bg_scan_period);
+ if (nla_put_u16(msg, NL80211_ATTR_BG_SCAN_PERIOD,
+ params->bg_scan_period))
+ return -1;
+ }
+
+ if (params->ssid) {
+ wpa_printf(MSG_DEBUG, " * SSID=%s",
+ wpa_ssid_txt(params->ssid, params->ssid_len));
+ if (nla_put(msg, NL80211_ATTR_SSID, params->ssid_len,
+ params->ssid))
+ return -1;
+ if (params->ssid_len > sizeof(drv->ssid))
+ return -1;
+ 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))
+ return -1;
+
+ if (params->wpa_proto) {
+ enum nl80211_wpa_versions ver = 0;
+
+ if (params->wpa_proto & WPA_PROTO_WPA)
+ ver |= NL80211_WPA_VERSION_1;
+ if (params->wpa_proto & WPA_PROTO_RSN)
+ ver |= NL80211_WPA_VERSION_2;
+
+ wpa_printf(MSG_DEBUG, " * WPA Versions 0x%x", ver);
+ if (nla_put_u32(msg, NL80211_ATTR_WPA_VERSIONS, ver))
+ return -1;
+ }
+
+ if (params->pairwise_suite != WPA_CIPHER_NONE) {
+ u32 cipher = wpa_cipher_to_cipher_suite(params->pairwise_suite);
+ wpa_printf(MSG_DEBUG, " * pairwise=0x%x", cipher);
+ if (nla_put_u32(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE,
+ cipher))
+ return -1;
+ }
+
+ if (params->group_suite == WPA_CIPHER_GTK_NOT_USED &&
+ !(drv->capa.enc & WPA_DRIVER_CAPA_ENC_GTK_NOT_USED)) {
+ /*
+ * This is likely to work even though many drivers do not
+ * advertise support for operations without GTK.
+ */
+ wpa_printf(MSG_DEBUG, " * skip group cipher configuration for GTK_NOT_USED due to missing driver support advertisement");
+ } else if (params->group_suite != WPA_CIPHER_NONE) {
+ u32 cipher = wpa_cipher_to_cipher_suite(params->group_suite);
+ wpa_printf(MSG_DEBUG, " * group=0x%x", cipher);
+ if (nla_put_u32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, cipher))
+ return -1;
+ }
+
+ if (params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_PSK ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_FT_IEEE8021X ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_FT_PSK ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_CCKM ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_OSEN ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_SAE ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_SAE_EXT_KEY ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE_EXT_KEY ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_FT_IEEE8021X_SHA384 ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_FILS_SHA256 ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_FILS_SHA384 ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_FT_FILS_SHA256 ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_FT_FILS_SHA384 ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_OWE ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_DPP) {
+ u32 *mgmt;
+ unsigned int akm_count = 1, i;
+
+ /*
+ * Make sure the driver has capability to handle default AKM in
+ * key_mgmt_suite plus allowed AKMs in allowed_key_mgmts.
+ */
+ if (drv->capa.max_num_akms <=
+ num_bits_set(params->allowed_key_mgmts)) {
+ wpa_printf(MSG_INFO,
+ "nl80211: Not enough support for the allowed AKMs (max_num_akms=%u <= num_bits_set=%u)",
+ drv->capa.max_num_akms,
+ num_bits_set(params->allowed_key_mgmts));
+ return -1;
+ }
+
+ mgmt = os_malloc(sizeof(u32) * drv->capa.max_num_akms);
+ if (!mgmt)
+ return -1;
+
+ mgmt[0] = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
+
+ switch (params->key_mgmt_suite) {
+ case WPA_KEY_MGMT_CCKM:
+ mgmt[0] = RSN_AUTH_KEY_MGMT_CCKM;
+ break;
+ case WPA_KEY_MGMT_IEEE8021X:
+ mgmt[0] = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
+ break;
+ case WPA_KEY_MGMT_FT_IEEE8021X:
+ mgmt[0] = RSN_AUTH_KEY_MGMT_FT_802_1X;
+ break;
+ case WPA_KEY_MGMT_FT_PSK:
+ mgmt[0] = RSN_AUTH_KEY_MGMT_FT_PSK;
+ break;
+ case WPA_KEY_MGMT_IEEE8021X_SHA256:
+ mgmt[0] = RSN_AUTH_KEY_MGMT_802_1X_SHA256;
+ break;
+ case WPA_KEY_MGMT_PSK_SHA256:
+ mgmt[0] = RSN_AUTH_KEY_MGMT_PSK_SHA256;
+ break;
+ case WPA_KEY_MGMT_OSEN:
+ mgmt[0] = RSN_AUTH_KEY_MGMT_OSEN;
+ break;
+ case WPA_KEY_MGMT_SAE:
+ mgmt[0] = RSN_AUTH_KEY_MGMT_SAE;
+ break;
+ case WPA_KEY_MGMT_SAE_EXT_KEY:
+ mgmt[0] = RSN_AUTH_KEY_MGMT_SAE_EXT_KEY;
+ break;
+ case WPA_KEY_MGMT_FT_SAE:
+ mgmt[0] = RSN_AUTH_KEY_MGMT_FT_SAE;
+ break;
+ case WPA_KEY_MGMT_FT_SAE_EXT_KEY:
+ mgmt[0] = RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY;
+ break;
+ case WPA_KEY_MGMT_IEEE8021X_SUITE_B:
+ mgmt[0] = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B;
+ break;
+ case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
+ mgmt[0] = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192;
+ break;
+ case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
+ mgmt[0] = RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384;
+ break;
+ case WPA_KEY_MGMT_FILS_SHA256:
+ mgmt[0] = RSN_AUTH_KEY_MGMT_FILS_SHA256;
+ break;
+ case WPA_KEY_MGMT_FILS_SHA384:
+ mgmt[0] = RSN_AUTH_KEY_MGMT_FILS_SHA384;
+ break;
+ case WPA_KEY_MGMT_FT_FILS_SHA256:
+ mgmt[0] = RSN_AUTH_KEY_MGMT_FT_FILS_SHA256;
+ break;
+ case WPA_KEY_MGMT_FT_FILS_SHA384:
+ mgmt[0] = RSN_AUTH_KEY_MGMT_FT_FILS_SHA384;
+ break;
+ case WPA_KEY_MGMT_OWE:
+ mgmt[0] = RSN_AUTH_KEY_MGMT_OWE;
+ break;
+ case WPA_KEY_MGMT_DPP:
+ mgmt[0] = RSN_AUTH_KEY_MGMT_DPP;
+ break;
+ case WPA_KEY_MGMT_PSK:
+ default:
+ mgmt[0] = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
+ break;
+ }
+
+ if (drv->capa.max_num_akms > 1) {
+ akm_count += wpa_key_mgmt_to_suites(
+ params->allowed_key_mgmts, &mgmt[1],
+ drv->capa.max_num_akms - 1);
+ }
+
+ for (i = 0; i < akm_count; i++)
+ wpa_printf(MSG_DEBUG, " * akm[%d]=0x%x", i, mgmt[i]);
+
+ if (nla_put(msg, NL80211_ATTR_AKM_SUITES,
+ akm_count * sizeof(u32), mgmt)) {
+ os_free(mgmt);
+ return -1;
+ }
+
+ os_free(mgmt);
+ }
+
+ if (params->req_handshake_offload &&
+ (drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X)) {
+ wpa_printf(MSG_DEBUG, " * WANT_1X_4WAY_HS");
+ if (nla_put_flag(msg, NL80211_ATTR_WANT_1X_4WAY_HS))
+ return -1;
+ }
+
+ /* Add PSK in case of 4-way handshake offload */
+ if (params->psk &&
+ (drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK)) {
+ wpa_hexdump_key(MSG_DEBUG, " * PSK", params->psk, 32);
+ if (nla_put(msg, NL80211_ATTR_PMK, 32, params->psk))
+ return -1;
+ }
+
+ if (nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT))
+ return -1;
+
+ if (params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_NO_WPA &&
+ (params->pairwise_suite == WPA_CIPHER_NONE ||
+ params->pairwise_suite == WPA_CIPHER_WEP104 ||
+ params->pairwise_suite == WPA_CIPHER_WEP40) &&
+ (nla_put_u16(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE, ETH_P_PAE) ||
+ nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT)))
+ return -1;
+
+ if (params->rrm_used) {
+ u32 drv_rrm_flags = drv->capa.rrm_flags;
+ if ((!((drv_rrm_flags &
+ WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES) &&
+ (drv_rrm_flags & WPA_DRIVER_FLAGS_QUIET)) &&
+ !(drv_rrm_flags & WPA_DRIVER_FLAGS_SUPPORT_RRM)) ||
+ nla_put_flag(msg, NL80211_ATTR_USE_RRM))
+ return -1;
+ }
+
+ if (nl80211_ht_vht_overrides(msg, params) < 0)
+ return -1;
+
+ if (params->p2p)
+ wpa_printf(MSG_DEBUG, " * P2P group");
+
+ if (params->pbss) {
+ wpa_printf(MSG_DEBUG, " * PBSS");
+ if (nla_put_flag(msg, NL80211_ATTR_PBSS))
+ return -1;
+ }
+
+ drv->connect_reassoc = 0;
+ if (params->prev_bssid) {
+ wpa_printf(MSG_DEBUG, " * prev_bssid=" MACSTR,
+ MAC2STR(params->prev_bssid));
+ if (nla_put(msg, NL80211_ATTR_PREV_BSSID, ETH_ALEN,
+ params->prev_bssid))
+ return -1;
+ drv->connect_reassoc = 1;
+ }
+
+ if ((params->auth_alg & WPA_AUTH_ALG_FILS) &&
+ nl80211_put_fils_connect_params(drv, params, msg) != 0)
+ return -1;
+
+ if ((wpa_key_mgmt_sae(params->key_mgmt_suite) ||
+ wpa_key_mgmt_sae(params->allowed_key_mgmts)) &&
+ (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) &&
+ nla_put_flag(msg, NL80211_ATTR_EXTERNAL_AUTH_SUPPORT))
+ return -1;
+
+ if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) &&
+ nla_put_flag(msg, NL80211_ATTR_MLO_SUPPORT))
+ return -1;
+
+ return 0;
+}
+
+
+static int wpa_driver_nl80211_try_connect(
+ struct wpa_driver_nl80211_data *drv,
+ struct wpa_driver_associate_params *params,
+ struct i802_bss *bss)
+{
+ struct nl_msg *msg;
+ enum nl80211_auth_type type;
+ int ret;
+ int algs;
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+ if (params->req_key_mgmt_offload && params->psk &&
+ (wpa_key_mgmt_wpa_psk_no_sae(params->key_mgmt_suite) ||
+ wpa_key_mgmt_wpa_psk_no_sae(params->allowed_key_mgmts))) {
+ wpa_printf(MSG_DEBUG, "nl80211: Key management set PSK");
+ ret = issue_key_mgmt_set_key(drv, params->psk, 32);
+ if (ret)
+ return ret;
+ }
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+ wpa_printf(MSG_DEBUG, "nl80211: Connect (ifindex=%d)", drv->ifindex);
+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_CONNECT);
+ if (!msg)
+ return -1;
+
+ ret = nl80211_connect_common(drv, params, msg);
+ if (ret)
+ goto fail;
+
+ if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED &&
+ nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED))
+ goto fail;
+
+ if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_OPTIONAL &&
+ (drv->capa.flags & WPA_DRIVER_FLAGS_MFP_OPTIONAL) &&
+ nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_OPTIONAL))
+ goto fail;
+
+#ifdef CONFIG_SAE
+ if ((wpa_key_mgmt_sae(params->key_mgmt_suite) ||
+ wpa_key_mgmt_sae(params->allowed_key_mgmts)) &&
+ nl80211_put_sae_pwe(msg, params->sae_pwe) < 0)
+ goto fail;
+#endif /* CONFIG_SAE */
+
+ 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 (params->auth_alg & WPA_AUTH_ALG_FILS)
+ algs++;
+ if (params->auth_alg & WPA_AUTH_ALG_FT)
+ algs++;
+ if (algs > 1) {
+ wpa_printf(MSG_DEBUG, " * Leave out Auth Type for automatic "
+ "selection");
+ goto skip_auth_type;
+ }
+
+ type = get_nl_auth_type(params->auth_alg);
+ wpa_printf(MSG_DEBUG, " * Auth Type %d", type);
+ if (type == NL80211_AUTHTYPE_MAX ||
+ nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type))
+ goto fail;
+
+skip_auth_type:
+ ret = nl80211_set_conn_keys(params, msg);
+ if (ret)
+ goto fail;
+
+ ret = send_and_recv_msgs_connect_handle(drv, msg, bss, 1);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: MLME connect failed: ret=%d "
+ "(%s)", ret, strerror(-ret));
+ } else {
+#ifdef CONFIG_DRIVER_NL80211_QCA
+ drv->roam_indication_done = false;
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Connect request send successfully");
+ }
+
+fail:
+ nl80211_nlmsg_clear(msg);
+ nlmsg_free(msg);
+ return ret;
+
+}
+
+
+static int wpa_driver_nl80211_connect(
+ struct wpa_driver_nl80211_data *drv,
+ struct wpa_driver_associate_params *params,
+ struct i802_bss *bss)
+{
+ int ret;
+
+ /* Store the connection attempted bssid for future use */
+ if (params->bssid)
+ os_memcpy(drv->auth_attempt_bssid, params->bssid, ETH_ALEN);
+ else
+ os_memset(drv->auth_attempt_bssid, 0, ETH_ALEN);
+
+ ret = wpa_driver_nl80211_try_connect(drv, params, bss);
+ if (ret == -EALREADY) {
+ /*
+ * cfg80211 does not currently accept new connections if
+ * we are already connected. As a workaround, force
+ * disconnection and try again.
+ */
+ wpa_printf(MSG_DEBUG, "nl80211: Explicitly "
+ "disconnecting before reassociation "
+ "attempt");
+ if (wpa_driver_nl80211_disconnect(
+ drv, WLAN_REASON_PREV_AUTH_NOT_VALID, bss))
+ return -1;
+ ret = wpa_driver_nl80211_try_connect(drv, params, bss);
+ }
+ 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;
+
+ nl80211_unmask_11b_rates(bss);
+
+ 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)) {
+ enum nl80211_iftype nlmode = params->p2p ?
+ NL80211_IFTYPE_P2P_CLIENT : NL80211_IFTYPE_STATION;
+
+ if (wpa_driver_nl80211_set_mode(priv, nlmode) < 0)
+ return -1;
+ if (wpa_key_mgmt_sae(params->key_mgmt_suite) ||
+ wpa_key_mgmt_sae(params->allowed_key_mgmts))
+ bss->use_nl_connect = 1;
+ else
+ bss->use_nl_connect = 0;
+
+ return wpa_driver_nl80211_connect(drv, params, bss);
+ }
+
+ nl80211_mark_disconnected(drv);
+
+ wpa_printf(MSG_DEBUG, "nl80211: Associate (ifindex=%d)",
+ drv->ifindex);
+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_ASSOCIATE);
+ if (!msg)
+ return -1;
+
+ ret = nl80211_connect_common(drv, params, msg);
+ if (ret)
+ goto fail;
+
+ if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED &&
+ nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED))
+ goto fail;
+
+ if (params->fils_kek) {
+ wpa_printf(MSG_DEBUG, " * FILS KEK (len=%u)",
+ (unsigned int) params->fils_kek_len);
+ if (nla_put(msg, NL80211_ATTR_FILS_KEK, params->fils_kek_len,
+ params->fils_kek))
+ goto fail;
+ }
+ if (params->fils_nonces) {
+ wpa_hexdump(MSG_DEBUG, " * FILS nonces (for AAD)",
+ params->fils_nonces,
+ params->fils_nonces_len);
+ if (nla_put(msg, NL80211_ATTR_FILS_NONCES,
+ params->fils_nonces_len, params->fils_nonces))
+ goto fail;
+ }
+
+ ret = send_and_recv_msgs_connect_handle(drv, msg, drv->first_bss, 1);
+ msg = NULL;
+ if (ret) {
+ wpa_dbg(drv->ctx, MSG_DEBUG,
+ "nl80211: MLME command failed (assoc): ret=%d (%s)",
+ ret, strerror(-ret));
+ nl80211_dump_scan(drv);
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Association request send successfully");
+ }
+
+fail:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+static int nl80211_set_mode(struct wpa_driver_nl80211_data *drv,
+ int ifindex, enum nl80211_iftype mode)
+{
+ struct nl_msg *msg;
+ int ret = -ENOBUFS;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Set mode ifindex %d iftype %d (%s)",
+ ifindex, mode, nl80211_iftype_str(mode));
+
+ msg = nl80211_cmd_msg(drv->first_bss, 0, NL80211_CMD_SET_INTERFACE);
+ if (!msg || nla_put_u32(msg, NL80211_ATTR_IFTYPE, mode))
+ goto fail;
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ msg = NULL;
+ if (!ret)
+ return 0;
+fail:
+ nlmsg_free(msg);
+ 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_impl(
+ struct i802_bss *bss,
+ enum nl80211_iftype nlmode,
+ struct hostapd_freq_params *desired_freq_params)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int ret = -1;
+ int i;
+ int was_ap = is_ap_interface(drv->nlmode);
+ int res;
+ int mode_switch_res;
+
+ if (TEST_FAIL())
+ return -1;
+
+ mode_switch_res = nl80211_set_mode(drv, drv->ifindex, nlmode);
+ if (mode_switch_res && nlmode == nl80211_get_ifmode(bss))
+ mode_switch_res = 0;
+
+ if (mode_switch_res == 0) {
+ drv->nlmode = nlmode;
+ ret = 0;
+ goto done;
+ }
+
+ if (mode_switch_res == -ENODEV)
+ return -1;
+
+ 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++) {
+ res = i802_set_iface_flags(bss, 0);
+ if (res == -EACCES || res == -ENODEV)
+ break;
+ if (res != 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to set "
+ "interface down");
+ os_sleep(0, 100000);
+ continue;
+ }
+
+ /*
+ * Setting the mode will fail for some drivers if the phy is
+ * on a frequency that the mode is disallowed in.
+ */
+ if (desired_freq_params) {
+ res = nl80211_set_channel(bss, desired_freq_params, 0);
+ if (res) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Failed to set frequency on interface");
+ }
+ }
+
+ if (i == 0 && was_ap && !is_ap_interface(nlmode) &&
+ bss->brname[0] &&
+ (bss->added_if_into_bridge || bss->already_in_bridge)) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Remove AP interface %s temporarily from the bridge %s to allow its mode to be set to STATION",
+ bss->ifname, bss->brname);
+ if (linux_br_del_if(drv->global->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));
+ }
+
+ /* Try to set the mode again while the interface is down */
+ mode_switch_res = nl80211_set_mode(drv, drv->ifindex, nlmode);
+ if (mode_switch_res == -EBUSY) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Delaying mode set while interface going down");
+ os_sleep(0, 100000);
+ continue;
+ }
+ ret = mode_switch_res;
+ break;
+ }
+
+ if (!ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Mode change succeeded while "
+ "interface is down");
+ drv->nlmode = nlmode;
+ drv->ignore_if_down_event = 1;
+ }
+
+ /* Bring the interface back up */
+ res = linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1);
+ if (res != 0) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Failed to set interface up after switching mode");
+ ret = -1;
+ }
+
+done:
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Interface mode change to %d "
+ "from %d failed", nlmode, drv->nlmode);
+ return ret;
+ }
+
+ if (is_p2p_net_interface(nlmode)) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Interface %s mode change to P2P - disable 11b rates",
+ bss->ifname);
+ nl80211_disable_11b_rates(drv, drv->ifindex, 1);
+ } else if (drv->disabled_11b_rates) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Interface %s mode changed to non-P2P - re-enable 11b rates",
+ bss->ifname);
+ nl80211_disable_11b_rates(drv, drv->ifindex, 0);
+ }
+
+ if (is_ap_interface(nlmode)) {
+ nl80211_mgmt_unsubscribe(bss, "start AP");
+ /* Setup additional AP mode functionality if needed */
+ if (nl80211_setup_ap(bss))
+ return -1;
+ } else if (was_ap) {
+ /* Remove additional AP mode functionality */
+ nl80211_teardown_ap(bss);
+ } else {
+ nl80211_mgmt_unsubscribe(bss, "mode change");
+ }
+
+ if (is_mesh_interface(nlmode) &&
+ nl80211_mgmt_subscribe_mesh(bss))
+ return -1;
+
+ if (!bss->in_deinit && !is_ap_interface(nlmode) &&
+ !is_mesh_interface(nlmode) &&
+ nl80211_mgmt_subscribe_non_ap(bss) < 0)
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to register Action "
+ "frame processing - ignore for now");
+
+ return 0;
+}
+
+
+void nl80211_restore_ap_mode(struct i802_bss *bss)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int was_ap = is_ap_interface(drv->nlmode);
+
+ wpa_driver_nl80211_set_mode(bss, drv->ap_scan_as_station);
+ if (!was_ap && is_ap_interface(drv->ap_scan_as_station) &&
+ bss->brname[0] &&
+ (bss->added_if_into_bridge || bss->already_in_bridge)) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Add AP interface %s back into the bridge %s",
+ bss->ifname, bss->brname);
+ if (linux_br_add_if(drv->global->ioctl_sock, bss->brname,
+ bss->ifname) < 0) {
+ wpa_printf(MSG_WARNING,
+ "nl80211: Failed to add interface %s into bridge %s: %s",
+ bss->ifname, bss->brname, strerror(errno));
+ }
+ }
+ drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
+}
+
+
+int wpa_driver_nl80211_set_mode(struct i802_bss *bss,
+ enum nl80211_iftype nlmode)
+{
+ return wpa_driver_nl80211_set_mode_impl(bss, nlmode, NULL);
+}
+
+
+static int wpa_driver_nl80211_set_mode_ibss(struct i802_bss *bss,
+ struct hostapd_freq_params *freq)
+{
+ return wpa_driver_nl80211_set_mode_impl(bss, NL80211_IFTYPE_ADHOC,
+ freq);
+}
+
+
+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));
+ if (drv->extended_capa && drv->extended_capa_mask) {
+ capa->extended_capa = drv->extended_capa;
+ capa->extended_capa_mask = drv->extended_capa_mask;
+ capa->extended_capa_len = drv->extended_capa_len;
+ }
+
+ 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, "nl80211: Set %s operstate %d->%d (%s)",
+ bss->ifname, drv->operstate, state,
+ state ? "UP" : "DORMANT");
+ drv->operstate = state;
+ return netlink_send_oper_ifla(drv->global->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;
+ int ret;
+ const u8 *connected_addr = drv->sta_mlo_info.valid_links ?
+ drv->sta_mlo_info.ap_mld_addr : drv->bssid;
+
+ if (!drv->associated && is_zero_ether_addr(connected_addr) &&
+ !authorized) {
+ wpa_printf(MSG_DEBUG, "nl80211: Skip set_supp_port(unauthorized) while not associated");
+ return 0;
+ }
+
+ wpa_printf(MSG_DEBUG, "nl80211: Set supplicant port %sauthorized for "
+ MACSTR, authorized ? "" : "un", MAC2STR(connected_addr));
+
+ os_memset(&upd, 0, sizeof(upd));
+ upd.mask = BIT(NL80211_STA_FLAG_AUTHORIZED);
+ if (authorized)
+ upd.set = BIT(NL80211_STA_FLAG_AUTHORIZED);
+
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_STATION)) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, connected_addr) ||
+ nla_put(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd)) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ if (!ret)
+ return 0;
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to set STA flag: %d (%s)",
+ ret, strerror(-ret));
+ return ret;
+}
+
+
+/* Set kernel driver on given frequency (MHz) */
+static int i802_set_freq(void *priv, struct hostapd_freq_params *freq)
+{
+ struct i802_bss *bss = priv;
+ return nl80211_set_channel(bss, freq, 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));
+ nl80211_nlmsg_clear(msg);
+ return NL_SKIP;
+}
+
+
+static int i802_get_seqnum(const char *iface, void *priv, const u8 *addr,
+ int idx, int link_id, u8 *seq)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int res;
+
+ msg = nl80211_ifindex_msg(drv, if_nametoindex(iface), 0,
+ NL80211_CMD_GET_KEY);
+ if (!msg ||
+ (addr && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) ||
+ (link_id != NL80211_DRV_LINK_ID_NA &&
+ nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id)) ||
+ nla_put_u8(msg, NL80211_ATTR_KEY_IDX, idx)) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ os_memset(seq, 0, 6);
+ res = send_and_recv_msgs(drv, msg, get_key_handler, seq, NULL, NULL);
+ if (res) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Failed to get current TX sequence for a key (link_id=%d idx=%d): %d (%s)",
+ link_id, idx, res, strerror(-res));
+ }
+
+ return res;
+}
+
+
+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;
+ u32 val;
+
+ if (rts >= 2347 || rts == -1)
+ val = (u32) -1;
+ else
+ val = rts;
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_WIPHY)) ||
+ nla_put_u32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, val)) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ if (!ret)
+ return 0;
+ 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;
+ u32 val;
+
+ if (frag >= 2346 || frag == -1)
+ val = (u32) -1;
+ else
+ val = frag;
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_WIPHY)) ||
+ nla_put_u32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, val)) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ if (!ret)
+ return 0;
+ 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 nl_msg *msg;
+ int res;
+
+ wpa_printf(MSG_DEBUG, "nl80211: flush -> DEL_STATION %s (all)",
+ bss->ifname);
+
+ /*
+ * XXX: FIX! this needs to flush all VLANs too
+ */
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_DEL_STATION);
+ res = send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
+ if (res) {
+ wpa_printf(MSG_DEBUG, "nl80211: Station flush failed: ret=%d "
+ "(%s)", res, strerror(-res));
+ }
+ return res;
+}
+
+
+static void get_sta_tid_stats(struct hostap_sta_driver_data *data,
+ struct nlattr *attr)
+{
+ struct nlattr *tid_stats[NL80211_TID_STATS_MAX + 1], *tidattr;
+ struct nlattr *txq_stats[NL80211_TXQ_STATS_MAX + 1];
+ static struct nla_policy txq_stats_policy[NL80211_TXQ_STATS_MAX + 1] = {
+ [NL80211_TXQ_STATS_BACKLOG_BYTES] = { .type = NLA_U32 },
+ [NL80211_TXQ_STATS_BACKLOG_PACKETS] = { .type = NLA_U32 },
+ };
+ int rem;
+
+ nla_for_each_nested(tidattr, attr, rem) {
+ if (nla_parse_nested(tid_stats, NL80211_TID_STATS_MAX,
+ tidattr, NULL) != 0 ||
+ !tid_stats[NL80211_TID_STATS_TXQ_STATS] ||
+ nla_parse_nested(txq_stats, NL80211_TXQ_STATS_MAX,
+ tid_stats[NL80211_TID_STATS_TXQ_STATS],
+ txq_stats_policy) != 0)
+ continue;
+ /* sum the backlogs over all TIDs for station */
+ if (txq_stats[NL80211_TXQ_STATS_BACKLOG_BYTES])
+ data->backlog_bytes += nla_get_u32(
+ txq_stats[NL80211_TXQ_STATS_BACKLOG_BYTES]);
+ if (txq_stats[NL80211_TXQ_STATS_BACKLOG_PACKETS])
+ data->backlog_bytes += nla_get_u32(
+ txq_stats[NL80211_TXQ_STATS_BACKLOG_PACKETS]);
+ }
+}
+
+
+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_SIGNAL] = { .type = NLA_U8 },
+ [NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 },
+ [NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 },
+ [NL80211_STA_INFO_TX_RETRIES] = { .type = NLA_U32 },
+ [NL80211_STA_INFO_TX_FAILED] = { .type = NLA_U32 },
+ [NL80211_STA_INFO_SIGNAL_AVG] = { .type = NLA_U8 },
+ [NL80211_STA_INFO_CONNECTED_TIME] = { .type = NLA_U32 },
+ [NL80211_STA_INFO_BEACON_LOSS] = { .type = NLA_U32 },
+ [NL80211_STA_INFO_RX_BYTES64] = { .type = NLA_U64 },
+ [NL80211_STA_INFO_TX_BYTES64] = { .type = NLA_U64 },
+ [NL80211_STA_INFO_EXPECTED_THROUGHPUT] = { .type = NLA_U32 },
+ [NL80211_STA_INFO_RX_DROP_MISC] = { .type = NLA_U64 },
+ [NL80211_STA_INFO_BEACON_RX] = { .type = NLA_U64 },
+ [NL80211_STA_INFO_BEACON_SIGNAL_AVG] = { .type = NLA_U8},
+ [NL80211_STA_INFO_RX_DURATION] = { .type = NLA_U64 },
+ [NL80211_STA_INFO_ACK_SIGNAL] = { .type = NLA_U8 },
+ [NL80211_STA_INFO_ACK_SIGNAL_AVG] = { .type = NLA_S8 },
+ [NL80211_STA_INFO_RX_MPDUS] = { .type = NLA_U32 },
+ [NL80211_STA_INFO_FCS_ERROR_COUNT] = { .type = NLA_U32 },
+ [NL80211_STA_INFO_TX_DURATION] = { .type = NLA_U64 },
+ };
+ struct nlattr *rate[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_BITRATE32] = { .type = NLA_U32 },
+ [NL80211_RATE_INFO_MCS] = { .type = NLA_U8 },
+ [NL80211_RATE_INFO_VHT_MCS] = { .type = NLA_U8 },
+ [NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG },
+ [NL80211_RATE_INFO_VHT_NSS] = { .type = NLA_U8 },
+ [NL80211_RATE_INFO_HE_MCS] = { .type = NLA_U8 },
+ [NL80211_RATE_INFO_HE_NSS] = { .type = NLA_U8 },
+ [NL80211_RATE_INFO_HE_GI] = { .type = NLA_U8 },
+ [NL80211_RATE_INFO_HE_DCM] = { .type = NLA_U8 },
+ };
+
+ 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]);
+ /* For backwards compatibility, fetch the 32-bit counters first. */
+ 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_BYTES64] &&
+ stats[NL80211_STA_INFO_TX_BYTES64]) {
+ /*
+ * The driver supports 64-bit counters, so use them to override
+ * the 32-bit values.
+ */
+ data->rx_bytes =
+ nla_get_u64(stats[NL80211_STA_INFO_RX_BYTES64]);
+ data->tx_bytes =
+ nla_get_u64(stats[NL80211_STA_INFO_TX_BYTES64]);
+ data->bytes_64bit = 1;
+ }
+ if (stats[NL80211_STA_INFO_SIGNAL])
+ data->signal = (s8) nla_get_u8(stats[NL80211_STA_INFO_SIGNAL]);
+ 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]);
+ if (stats[NL80211_STA_INFO_TX_RETRIES])
+ data->tx_retry_count =
+ nla_get_u32(stats[NL80211_STA_INFO_TX_RETRIES]);
+ if (stats[NL80211_STA_INFO_TX_FAILED])
+ data->tx_retry_failed =
+ nla_get_u32(stats[NL80211_STA_INFO_TX_FAILED]);
+ if (stats[NL80211_STA_INFO_SIGNAL_AVG])
+ data->avg_signal =
+ (s8) nla_get_u8(stats[NL80211_STA_INFO_SIGNAL_AVG]);
+ if (stats[NL80211_STA_INFO_CONNECTED_TIME]) {
+ data->connected_sec =
+ nla_get_u32(stats[NL80211_STA_INFO_CONNECTED_TIME]);
+ data->flags |= STA_DRV_DATA_CONN_TIME;
+ }
+ if (stats[NL80211_STA_INFO_BEACON_LOSS])
+ data->beacon_loss_count =
+ nla_get_u32(stats[NL80211_STA_INFO_BEACON_LOSS]);
+ if (stats[NL80211_STA_INFO_EXPECTED_THROUGHPUT])
+ data->expected_throughput =
+ nla_get_u32(stats[NL80211_STA_INFO_EXPECTED_THROUGHPUT]);
+ if (stats[NL80211_STA_INFO_RX_DROP_MISC])
+ data->rx_drop_misc =
+ nla_get_u64(stats[NL80211_STA_INFO_RX_DROP_MISC]);
+ if (stats[NL80211_STA_INFO_BEACON_RX])
+ data->beacons_count =
+ nla_get_u64(stats[NL80211_STA_INFO_BEACON_RX]);
+ if (stats[NL80211_STA_INFO_BEACON_SIGNAL_AVG])
+ data->avg_beacon_signal =
+ (s8) nla_get_u8(stats[NL80211_STA_INFO_BEACON_SIGNAL_AVG]);
+ if (stats[NL80211_STA_INFO_RX_DURATION])
+ data->rx_airtime =
+ nla_get_u64(stats[NL80211_STA_INFO_RX_DURATION]);
+ if (stats[NL80211_STA_INFO_ACK_SIGNAL]) {
+ data->last_ack_rssi =
+ nla_get_u8(stats[NL80211_STA_INFO_ACK_SIGNAL]);
+ data->flags |= STA_DRV_DATA_LAST_ACK_RSSI;
+ }
+ if (stats[NL80211_STA_INFO_ACK_SIGNAL_AVG])
+ data->avg_ack_signal =
+ nla_get_s8(stats[NL80211_STA_INFO_ACK_SIGNAL_AVG]);
+ if (stats[NL80211_STA_INFO_RX_MPDUS])
+ data->rx_mpdus = nla_get_u32(stats[NL80211_STA_INFO_RX_MPDUS]);
+ if (stats[NL80211_STA_INFO_FCS_ERROR_COUNT])
+ data->fcs_error_count =
+ nla_get_u32(stats[NL80211_STA_INFO_FCS_ERROR_COUNT]);
+ if (stats[NL80211_STA_INFO_TX_DURATION])
+ data->tx_airtime =
+ nla_get_u64(stats[NL80211_STA_INFO_TX_DURATION]);
+
+ if (stats[NL80211_STA_INFO_TX_BITRATE] &&
+ nla_parse_nested(rate, NL80211_RATE_INFO_MAX,
+ stats[NL80211_STA_INFO_TX_BITRATE],
+ rate_policy) == 0) {
+ if (rate[NL80211_RATE_INFO_BITRATE32])
+ data->current_tx_rate =
+ nla_get_u32(rate[NL80211_RATE_INFO_BITRATE32]);
+ else if (rate[NL80211_RATE_INFO_BITRATE])
+ data->current_tx_rate =
+ nla_get_u16(rate[NL80211_RATE_INFO_BITRATE]);
+
+ /* Convert from 100 kbps to kbps; it's a more convenient unit.
+ * It's also safe up until ~1Tbps. */
+ data->current_tx_rate = data->current_tx_rate * 100;
+
+ if (rate[NL80211_RATE_INFO_MCS]) {
+ data->tx_mcs = nla_get_u8(rate[NL80211_RATE_INFO_MCS]);
+ data->flags |= STA_DRV_DATA_TX_MCS;
+ }
+ if (rate[NL80211_RATE_INFO_VHT_MCS]) {
+ data->tx_vhtmcs =
+ nla_get_u8(rate[NL80211_RATE_INFO_VHT_MCS]);
+ data->flags |= STA_DRV_DATA_TX_VHT_MCS;
+ }
+ if (rate[NL80211_RATE_INFO_SHORT_GI]) {
+ data->tx_guard_interval = GUARD_INTERVAL_0_4;
+ data->flags |= STA_DRV_DATA_TX_SHORT_GI;
+ }
+ if (rate[NL80211_RATE_INFO_VHT_NSS]) {
+ data->tx_vht_nss =
+ nla_get_u8(rate[NL80211_RATE_INFO_VHT_NSS]);
+ data->flags |= STA_DRV_DATA_TX_VHT_NSS;
+ }
+ if (rate[NL80211_RATE_INFO_HE_MCS]) {
+ data->tx_hemcs =
+ nla_get_u8(rate[NL80211_RATE_INFO_HE_MCS]);
+ data->flags |= STA_DRV_DATA_TX_HE_MCS;
+ }
+ if (rate[NL80211_RATE_INFO_HE_NSS]) {
+ data->tx_he_nss =
+ nla_get_u8(rate[NL80211_RATE_INFO_HE_NSS]);
+ data->flags |= STA_DRV_DATA_TX_HE_NSS;
+ }
+ if (rate[NL80211_RATE_INFO_HE_GI]) {
+ switch (nla_get_u8(rate[NL80211_RATE_INFO_HE_GI])) {
+ case NL80211_RATE_INFO_HE_GI_0_8:
+ data->tx_guard_interval = GUARD_INTERVAL_0_8;
+ break;
+ case NL80211_RATE_INFO_HE_GI_1_6:
+ data->tx_guard_interval = GUARD_INTERVAL_1_6;
+ break;
+ case NL80211_RATE_INFO_HE_GI_3_2:
+ data->tx_guard_interval = GUARD_INTERVAL_3_2;
+ break;
+ }
+ data->flags |= STA_DRV_DATA_TX_HE_GI;
+ }
+ if (rate[NL80211_RATE_INFO_HE_DCM]) {
+ data->tx_dcm =
+ nla_get_u8(rate[NL80211_RATE_INFO_HE_DCM]);
+ data->flags |= STA_DRV_DATA_TX_HE_DCM;
+ }
+ }
+
+ if (stats[NL80211_STA_INFO_RX_BITRATE] &&
+ nla_parse_nested(rate, NL80211_RATE_INFO_MAX,
+ stats[NL80211_STA_INFO_RX_BITRATE],
+ rate_policy) == 0) {
+ if (rate[NL80211_RATE_INFO_BITRATE32])
+ data->current_rx_rate =
+ nla_get_u32(rate[NL80211_RATE_INFO_BITRATE32]);
+ else if (rate[NL80211_RATE_INFO_BITRATE])
+ data->current_rx_rate =
+ nla_get_u16(rate[NL80211_RATE_INFO_BITRATE]);
+
+ /* Convert from 100 kbps to kbps; it's a more convenient unit.
+ * It's also safe up until ~1Tbps. */
+ data->current_rx_rate = data->current_rx_rate * 100;
+
+ if (rate[NL80211_RATE_INFO_MCS]) {
+ data->rx_mcs = nla_get_u8(rate[NL80211_RATE_INFO_MCS]);
+ data->flags |= STA_DRV_DATA_RX_MCS;
+ }
+ if (rate[NL80211_RATE_INFO_VHT_MCS]) {
+ data->rx_vhtmcs =
+ nla_get_u8(rate[NL80211_RATE_INFO_VHT_MCS]);
+ data->flags |= STA_DRV_DATA_RX_VHT_MCS;
+ }
+ if (rate[NL80211_RATE_INFO_SHORT_GI]) {
+ data->rx_guard_interval = GUARD_INTERVAL_0_4;
+ data->flags |= STA_DRV_DATA_RX_SHORT_GI;
+ }
+ if (rate[NL80211_RATE_INFO_VHT_NSS]) {
+ data->rx_vht_nss =
+ nla_get_u8(rate[NL80211_RATE_INFO_VHT_NSS]);
+ data->flags |= STA_DRV_DATA_RX_VHT_NSS;
+ }
+ if (rate[NL80211_RATE_INFO_HE_MCS]) {
+ data->rx_hemcs =
+ nla_get_u8(rate[NL80211_RATE_INFO_HE_MCS]);
+ data->flags |= STA_DRV_DATA_RX_HE_MCS;
+ }
+ if (rate[NL80211_RATE_INFO_HE_NSS]) {
+ data->rx_he_nss =
+ nla_get_u8(rate[NL80211_RATE_INFO_HE_NSS]);
+ data->flags |= STA_DRV_DATA_RX_HE_NSS;
+ }
+ if (rate[NL80211_RATE_INFO_HE_GI]) {
+ switch (nla_get_u8(rate[NL80211_RATE_INFO_HE_GI])) {
+ case NL80211_RATE_INFO_HE_GI_0_8:
+ data->rx_guard_interval = GUARD_INTERVAL_0_8;
+ break;
+ case NL80211_RATE_INFO_HE_GI_1_6:
+ data->rx_guard_interval = GUARD_INTERVAL_1_6;
+ break;
+ case NL80211_RATE_INFO_HE_GI_3_2:
+ data->rx_guard_interval = GUARD_INTERVAL_3_2;
+ break;
+ }
+ data->flags |= STA_DRV_DATA_RX_HE_GI;
+ }
+ if (rate[NL80211_RATE_INFO_HE_DCM]) {
+ data->rx_dcm =
+ nla_get_u8(rate[NL80211_RATE_INFO_HE_DCM]);
+ data->flags |= STA_DRV_DATA_RX_HE_DCM;
+ }
+ }
+
+ if (stats[NL80211_STA_INFO_TID_STATS])
+ get_sta_tid_stats(data, stats[NL80211_STA_INFO_TID_STATS]);
+
+ return NL_SKIP;
+}
+
+
+int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv,
+ const u8 *bssid,
+ struct hostap_sta_driver_data *data)
+{
+ struct nl_msg *msg;
+
+ data->signal = -WPA_INVALID_NOISE;
+ data->current_tx_rate = 0;
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_STATION)) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid)) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ return send_and_recv_msgs(drv, msg, get_sta_handler, data, NULL, NULL);
+}
+
+
+static int i802_read_sta_data(struct i802_bss *bss,
+ struct hostap_sta_driver_data *data,
+ const u8 *addr)
+{
+ struct nl_msg *msg;
+
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_GET_STATION)) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ return send_and_recv_msgs(bss->drv, msg, get_sta_handler, data,
+ NULL, NULL);
+}
+
+
+static int i802_set_tx_queue_params(void *priv, int queue, int aifs,
+ int cw_min, int cw_max, int burst_time,
+ int link_id)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ struct nlattr *txq, *params;
+ int res;
+
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_WIPHY);
+ if (!msg)
+ return -1;
+
+ txq = nla_nest_start(msg, NL80211_ATTR_WIPHY_TXQ_PARAMS);
+ if (!txq)
+ goto fail;
+
+ /* We are only sending parameters for a single TXQ at a time */
+ params = nla_nest_start(msg, 1);
+ if (!params)
+ goto fail;
+
+ switch (queue) {
+ case 0:
+ if (nla_put_u8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_VO))
+ goto fail;
+ break;
+ case 1:
+ if (nla_put_u8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_VI))
+ goto fail;
+ break;
+ case 2:
+ if (nla_put_u8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_BE))
+ goto fail;
+ break;
+ case 3:
+ if (nla_put_u8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_BK))
+ goto fail;
+ break;
+ }
+ /* Burst time is configured in units of 0.1 msec and TXOP parameter in
+ * 32 usec, so need to convert the value here. */
+ if (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))
+ goto fail;
+
+ nla_nest_end(msg, params);
+
+ nla_nest_end(msg, txq);
+
+ if (link_id != NL80211_DRV_LINK_ID_NA &&
+ nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id))
+ goto fail;
+
+ res = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ wpa_printf(MSG_DEBUG,
+ "nl80211: TX queue param set: queue=%d aifs=%d cw_min=%d cw_max=%d burst_time=%d --> res=%d",
+ queue, aifs, cw_min, cw_max, burst_time, res);
+ if (res == 0)
+ return 0;
+ msg = NULL;
+fail:
+ nlmsg_free(msg);
+ return -1;
+}
+
+
+static int i802_set_sta_vlan(struct i802_bss *bss, const u8 *addr,
+ const char *ifname, int vlan_id, int link_id)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "nl80211: %s[%d]: set_sta_vlan(" MACSTR
+ ", ifname=%s[%d], vlan_id=%d)",
+ bss->ifname, if_nametoindex(bss->ifname),
+ MAC2STR(addr), ifname, if_nametoindex(ifname), vlan_id);
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_STATION)) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
+ (vlan_id && (drv->capa.flags & WPA_DRIVER_FLAGS_VLAN_OFFLOAD) &&
+ nla_put_u16(msg, NL80211_ATTR_VLAN_ID, vlan_id)) ||
+ (link_id != NL80211_DRV_LINK_ID_NA &&
+ nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id)) ||
+ nla_put_u32(msg, NL80211_ATTR_STA_VLAN, if_nametoindex(ifname))) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, 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));
+ }
+ return ret;
+}
+
+
+static int i802_get_inact_sec(void *priv, const u8 *addr)
+{
+ struct hostap_sta_driver_data data;
+ int ret;
+
+ os_memset(&data, 0, sizeof(data));
+ data.inactive_msec = (unsigned long) -1;
+ ret = i802_read_sta_data(priv, &data, addr);
+ if (ret == -ENOENT)
+ return -ENOENT;
+ if (ret || data.inactive_msec == (unsigned long) -1)
+ return -1;
+ return data.inactive_msec / 1000;
+}
+
+
+static int i802_sta_clear_stats(void *priv, const u8 *addr)
+{
+#if 0
+ /* TODO */
+#endif
+ return 0;
+}
+
+
+static int i802_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
+ u16 reason, int link_id)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct ieee80211_mgmt mgmt;
+ u8 channel;
+ struct i802_link *link = nl80211_get_link(bss, link_id);
+
+ if (ieee80211_freq_to_chan(link->freq, &channel) ==
+ HOSTAPD_MODE_IEEE80211AD) {
+ /* Deauthentication is not used in DMG/IEEE 802.11ad;
+ * disassociate the STA instead. */
+ return i802_sta_disassoc(priv, own_addr, addr, reason);
+ }
+
+ if (is_mesh_interface(drv->nlmode))
+ return -1;
+
+ if (drv->device_ap_sme)
+ return wpa_driver_nl80211_sta_remove(bss, addr, 1, reason);
+
+ 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), 0, 0, 0, 0,
+ 0, NULL, 0, 0, -1);
+}
+
+
+static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
+ u16 reason)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct ieee80211_mgmt mgmt;
+
+ if (is_mesh_interface(drv->nlmode))
+ return -1;
+
+ if (drv->device_ap_sme)
+ return wpa_driver_nl80211_sta_remove(bss, addr, 0, reason);
+
+ 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), 0, 0, 0, 0,
+ 0, NULL, 0, 0, -1);
+}
+
+
+static void dump_ifidx(struct wpa_driver_nl80211_data *drv)
+{
+ char buf[200], *pos, *end;
+ int i, res;
+
+ pos = buf;
+ end = pos + sizeof(buf);
+
+ for (i = 0; i < drv->num_if_indices; i++) {
+ if (!drv->if_indices[i].ifindex)
+ continue;
+ res = os_snprintf(pos, end - pos, " %d(%d)",
+ drv->if_indices[i].ifindex,
+ drv->if_indices[i].reason);
+ if (os_snprintf_error(end - pos, res))
+ break;
+ pos += res;
+ }
+ *pos = '\0';
+
+ wpa_printf(MSG_DEBUG, "nl80211: if_indices[%d]:%s",
+ drv->num_if_indices, buf);
+}
+
+
+static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
+ int ifidx_reason)
+{
+ int i;
+ struct drv_nl80211_if_info *old;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Add own interface ifindex %d (ifidx_reason %d)",
+ ifidx, ifidx_reason);
+ if (have_ifidx(drv, ifidx, ifidx_reason)) {
+ wpa_printf(MSG_DEBUG, "nl80211: ifindex %d already in the list",
+ ifidx);
+ return;
+ }
+ for (i = 0; i < drv->num_if_indices; i++) {
+ if (drv->if_indices[i].ifindex == 0) {
+ drv->if_indices[i].ifindex = ifidx;
+ drv->if_indices[i].reason = ifidx_reason;
+ dump_ifidx(drv);
+ return;
+ }
+ }
+
+ if (drv->if_indices != drv->default_if_indices)
+ old = drv->if_indices;
+ else
+ old = NULL;
+
+ drv->if_indices = os_realloc_array(old, drv->num_if_indices + 1,
+ sizeof(*old));
+ 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;
+ }
+ if (!old)
+ os_memcpy(drv->if_indices, drv->default_if_indices,
+ sizeof(drv->default_if_indices));
+ drv->if_indices[drv->num_if_indices].ifindex = ifidx;
+ drv->if_indices[drv->num_if_indices].reason = ifidx_reason;
+ drv->num_if_indices++;
+ dump_ifidx(drv);
+}
+
+
+static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
+ int ifidx_reason)
+{
+ int i;
+
+ for (i = 0; i < drv->num_if_indices; i++) {
+ if ((drv->if_indices[i].ifindex == ifidx ||
+ ifidx == IFIDX_ANY) &&
+ (drv->if_indices[i].reason == ifidx_reason ||
+ ifidx_reason == IFIDX_ANY)) {
+ drv->if_indices[i].ifindex = 0;
+ drv->if_indices[i].reason = 0;
+ break;
+ }
+ }
+ dump_ifidx(drv);
+}
+
+
+static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
+ int ifidx_reason)
+{
+ int i;
+
+ for (i = 0; i < drv->num_if_indices; i++)
+ if (drv->if_indices[i].ifindex == ifidx &&
+ (drv->if_indices[i].reason == ifidx_reason ||
+ ifidx_reason == IFIDX_ANY))
+ return 1;
+
+ return 0;
+}
+
+
+static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val,
+ const char *bridge_ifname, char *ifname_wds)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ char name[IFNAMSIZ + 1];
+ union wpa_event_data event;
+ int ret;
+
+ ret = os_snprintf(name, sizeof(name), "%s.sta%d", bss->ifname, aid);
+ if (ret >= (int) sizeof(name))
+ wpa_printf(MSG_WARNING,
+ "nl80211: WDS interface name was truncated");
+ else if (ret < 0)
+ return ret;
+
+ if (ifname_wds)
+ os_strlcpy(ifname_wds, name, IFNAMSIZ + 1);
+
+ 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,
+ bss->addr, 1, NULL, NULL, 0) <
+ 0)
+ return -1;
+ if (bridge_ifname &&
+ linux_br_add_if(drv->global->ioctl_sock,
+ bridge_ifname, name) < 0)
+ return -1;
+
+ os_memset(&event, 0, sizeof(event));
+ event.wds_sta_interface.sta_addr = addr;
+ event.wds_sta_interface.ifname = name;
+ event.wds_sta_interface.istatus = INTERFACE_ADDED;
+ wpa_supplicant_event(bss->ctx,
+ EVENT_WDS_STA_INTERFACE_STATUS,
+ &event);
+ }
+ if (linux_set_iface_flags(drv->global->ioctl_sock, name, 1)) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to set WDS STA "
+ "interface %s up", name);
+ }
+ return i802_set_sta_vlan(priv, addr, name, 0,
+ NL80211_DRV_LINK_ID_NA);
+ } else {
+ if (bridge_ifname &&
+ linux_br_del_if(drv->global->ioctl_sock, bridge_ifname,
+ name) < 0)
+ wpa_printf(MSG_INFO,
+ "nl80211: Failed to remove interface %s from bridge %s: %s",
+ name, bridge_ifname, strerror(errno));
+
+ i802_set_sta_vlan(priv, addr, bss->ifname, 0,
+ NL80211_DRV_LINK_ID_NA);
+ nl80211_remove_iface(drv, if_nametoindex(name));
+ os_memset(&event, 0, sizeof(event));
+ event.wds_sta_interface.sta_addr = addr;
+ event.wds_sta_interface.ifname = name;
+ event.wds_sta_interface.istatus = INTERFACE_REMOVED;
+ wpa_supplicant_event(bss->ctx, EVENT_WDS_STA_INTERFACE_STATUS,
+ &event);
+ return 0;
+ }
+}
+
+
+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) {
+ wpa_printf(MSG_ERROR, "nl80211: EAPOL recv failed: %s",
+ strerror(errno));
+ return;
+ }
+
+ if (have_ifidx(drv, lladdr.sll_ifindex, IFIDX_ANY))
+ drv_event_eapol_rx(drv->ctx, lladdr.sll_addr, buf, len);
+}
+
+
+static int i802_check_bridge(struct wpa_driver_nl80211_data *drv,
+ struct i802_bss *bss,
+ const char *brname, const char *ifname)
+{
+ int br_ifindex;
+ char in_br[IFNAMSIZ];
+
+ os_strlcpy(bss->brname, brname, IFNAMSIZ);
+ br_ifindex = if_nametoindex(brname);
+ if (br_ifindex == 0) {
+ /*
+ * Bridge was configured, but the bridge device does
+ * not exist. Try to add it now.
+ */
+ if (linux_br_add(drv->global->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;
+ br_ifindex = if_nametoindex(brname);
+ add_ifidx(drv, br_ifindex, drv->ifindex);
+ }
+ bss->br_ifindex = br_ifindex;
+
+ if (linux_br_get(in_br, ifname) == 0) {
+ if (os_strcmp(in_br, brname) == 0) {
+ bss->already_in_bridge = 1;
+ 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->global->ioctl_sock, in_br, ifname) <
+ 0) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to "
+ "remove interface %s from bridge "
+ "%s: %s",
+ ifname, in_br, strerror(errno));
+ return -1;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "nl80211: Adding interface %s into bridge %s",
+ ifname, brname);
+ if (linux_br_add_if(drv->global->ioctl_sock, brname, ifname) < 0) {
+ wpa_printf(MSG_WARNING,
+ "nl80211: Failed to add interface %s into bridge %s: %s",
+ ifname, brname, strerror(errno));
+ /* Try to continue without the interface being in a bridge. This
+ * may be needed for some cases, e.g., with Open vSwitch, where
+ * an external component will need to handle bridge
+ * configuration. */
+ return 0;
+ }
+ 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 master_ifname[IFNAMSIZ];
+ int ifindex, br_ifindex = 0;
+ int br_added = 0;
+
+ bss = wpa_driver_nl80211_drv_init(hapd, params->ifname,
+ params->global_priv, 1,
+ params->bssid, params->driver_params);
+ if (bss == NULL)
+ return NULL;
+
+ drv = bss->drv;
+
+ if (linux_br_get(master_ifname, params->ifname) == 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in bridge %s",
+ params->ifname, master_ifname);
+ br_ifindex = if_nametoindex(master_ifname);
+ os_strlcpy(bss->brname, master_ifname, IFNAMSIZ);
+ } else if ((params->num_bridge == 0 || !params->bridge[0]) &&
+ linux_master_get(master_ifname, params->ifname) == 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in master %s",
+ params->ifname, master_ifname);
+ /* start listening for EAPOL on the master interface */
+ add_ifidx(drv, if_nametoindex(master_ifname), drv->ifindex);
+
+ /* check if master itself is under bridge */
+ if (linux_br_get(master_ifname, master_ifname) == 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: which is in bridge %s",
+ master_ifname);
+ br_ifindex = if_nametoindex(master_ifname);
+ os_strlcpy(bss->brname, master_ifname, IFNAMSIZ);
+ }
+ } else {
+ master_ifname[0] = '\0';
+ }
+
+ bss->br_ifindex = br_ifindex;
+
+ for (i = 0; i < params->num_bridge; i++) {
+ if (params->bridge[i]) {
+ ifindex = if_nametoindex(params->bridge[i]);
+ if (ifindex)
+ add_ifidx(drv, ifindex, drv->ifindex);
+ if (ifindex == br_ifindex)
+ br_added = 1;
+ }
+ }
+
+ /* start listening for EAPOL on the default AP interface */
+ add_ifidx(drv, drv->ifindex, IFIDX_ANY);
+
+ if (params->num_bridge && params->bridge[0]) {
+ if (i802_check_bridge(drv, bss, params->bridge[0],
+ params->ifname) < 0)
+ goto failed;
+ if (os_strcmp(params->bridge[0], master_ifname) != 0)
+ br_added = 1;
+ }
+
+ if (!br_added && br_ifindex &&
+ (params->num_bridge == 0 || !params->bridge[0]))
+ add_ifidx(drv, br_ifindex, drv->ifindex);
+
+ if (bss->added_if_into_bridge || bss->already_in_bridge) {
+ int err;
+
+ drv->rtnl_sk = nl_socket_alloc();
+ if (drv->rtnl_sk == NULL) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to allocate nl_sock");
+ goto failed;
+ }
+
+ err = nl_connect(drv->rtnl_sk, NETLINK_ROUTE);
+ if (err) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to connect nl_sock to NETLINK_ROUTE: %s",
+ nl_geterror(err));
+ goto failed;
+ }
+ }
+
+ if (drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_RX) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Do not open EAPOL RX socket - using control port for RX");
+ goto skip_eapol_sock;
+ }
+
+ drv->eapol_sock = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE));
+ if (drv->eapol_sock < 0) {
+ wpa_printf(MSG_ERROR, "nl80211: socket(PF_PACKET, SOCK_DGRAM, ETH_P_PAE) failed: %s",
+ strerror(errno));
+ goto failed;
+ }
+
+ if (eloop_register_read_sock(drv->eapol_sock, handle_eapol, drv, NULL))
+ {
+ wpa_printf(MSG_INFO, "nl80211: Could not register read socket for eapol");
+ goto failed;
+ }
+skip_eapol_sock:
+
+ if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
+ params->own_addr))
+ goto failed;
+ os_memcpy(drv->perm_addr, params->own_addr, ETH_ALEN);
+
+ memcpy(bss->addr, params->own_addr, ETH_ALEN);
+
+ return bss;
+
+failed:
+ wpa_driver_nl80211_deinit(bss);
+ return NULL;
+}
+
+
+static void i802_deinit(void *priv)
+{
+ struct i802_bss *bss = priv;
+ wpa_driver_nl80211_deinit(bss);
+}
+
+
+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;
+ case WPA_IF_P2P_DEVICE:
+ return NL80211_IFTYPE_P2P_DEVICE;
+ case WPA_IF_MESH:
+ return NL80211_IFTYPE_MESH_POINT;
+ default:
+ return -1;
+ }
+}
+
+
+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->first_bss->addr, ETH_ALEN) == 0)
+ return 1;
+ }
+ return 0;
+}
+
+
+static int nl80211_vif_addr(struct wpa_driver_nl80211_data *drv, u8 *new_addr)
+{
+ unsigned int idx;
+
+ if (!drv->global)
+ return -1;
+
+ os_memcpy(new_addr, drv->first_bss->addr, ETH_ALEN);
+ for (idx = 0; idx < 64; idx++) {
+ new_addr[0] = drv->first_bss->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 virtual interface address "
+ MACSTR, MAC2STR(new_addr));
+
+ return 0;
+}
+
+
+struct wdev_info {
+ u64 wdev_id;
+ int wdev_id_set;
+ u8 macaddr[ETH_ALEN];
+};
+
+static int nl80211_wdev_handler(struct nl_msg *msg, void *arg)
+{
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct wdev_info *wi = arg;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+ if (tb[NL80211_ATTR_WDEV]) {
+ wi->wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]);
+ wi->wdev_id_set = 1;
+ }
+
+ if (tb[NL80211_ATTR_MAC])
+ os_memcpy(wi->macaddr, nla_data(tb[NL80211_ATTR_MAC]),
+ ETH_ALEN);
+
+ return NL_SKIP;
+}
+
+
+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, int use_existing,
+ int setup_ap)
+{
+ enum nl80211_iftype nlmode;
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int ifidx;
+ int added = 1;
+
+ if (addr)
+ os_memcpy(if_addr, addr, ETH_ALEN);
+ nlmode = wpa_driver_nl80211_if_type(type);
+ if (nlmode == NL80211_IFTYPE_P2P_DEVICE) {
+ struct wdev_info p2pdev_info;
+
+ os_memset(&p2pdev_info, 0, sizeof(p2pdev_info));
+ ifidx = nl80211_create_iface(drv, ifname, nlmode, addr,
+ 0, nl80211_wdev_handler,
+ &p2pdev_info, use_existing);
+ if (!p2pdev_info.wdev_id_set || ifidx != 0) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to create a P2P Device interface %s",
+ ifname);
+ return -1;
+ }
+
+ drv->global->if_add_wdevid = p2pdev_info.wdev_id;
+ drv->global->if_add_wdevid_set = p2pdev_info.wdev_id_set;
+ if (!is_zero_ether_addr(p2pdev_info.macaddr))
+ os_memcpy(if_addr, p2pdev_info.macaddr, ETH_ALEN);
+ wpa_printf(MSG_DEBUG, "nl80211: New P2P Device interface %s (0x%llx) created",
+ ifname,
+ (long long unsigned int) p2pdev_info.wdev_id);
+ } else {
+ ifidx = nl80211_create_iface(drv, ifname, nlmode, addr,
+ 0, NULL, NULL, use_existing);
+ if (use_existing && ifidx == -ENFILE) {
+ added = 0;
+ ifidx = if_nametoindex(ifname);
+ } else if (ifidx < 0) {
+ return -1;
+ }
+ }
+
+ if (!addr) {
+ if (nlmode == NL80211_IFTYPE_P2P_DEVICE)
+ os_memcpy(if_addr, bss->addr, ETH_ALEN);
+ else if (linux_get_ifhwaddr(drv->global->ioctl_sock,
+ ifname, if_addr) < 0) {
+ if (added)
+ nl80211_remove_iface(drv, ifidx);
+ return -1;
+ }
+ }
+
+ if (!addr &&
+ (type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP ||
+ type == WPA_IF_P2P_GO || type == WPA_IF_MESH ||
+ type == WPA_IF_STATION || type == WPA_IF_AP_BSS)) {
+ /* Enforce unique address */
+ u8 new_addr[ETH_ALEN];
+
+ if (linux_get_ifhwaddr(drv->global->ioctl_sock, ifname,
+ new_addr) < 0) {
+ if (added)
+ nl80211_remove_iface(drv, ifidx);
+ return -1;
+ }
+ if (nl80211_addr_in_use(drv->global, new_addr)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Allocate new address "
+ "for interface %s type %d", ifname, type);
+ if (nl80211_vif_addr(drv, new_addr) < 0) {
+ if (added)
+ nl80211_remove_iface(drv, ifidx);
+ return -1;
+ }
+ if (linux_set_ifhwaddr(drv->global->ioctl_sock, ifname,
+ new_addr) < 0) {
+ if (added)
+ nl80211_remove_iface(drv, ifidx);
+ return -1;
+ }
+ }
+ os_memcpy(if_addr, new_addr, ETH_ALEN);
+ }
+
+ if (type == WPA_IF_AP_BSS && setup_ap) {
+ struct i802_bss *new_bss = os_zalloc(sizeof(*new_bss));
+ unsigned int i;
+
+ if (new_bss == NULL) {
+ if (added)
+ nl80211_remove_iface(drv, ifidx);
+ return -1;
+ }
+
+ /* Initialize here before any failure path */
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++)
+ new_bss->links[i].link_id = NL80211_DRV_LINK_ID_NA;
+
+ 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);
+ if (added)
+ nl80211_remove_iface(drv, ifidx);
+ os_free(new_bss);
+ return -1;
+ }
+
+ if (linux_set_iface_flags(drv->global->ioctl_sock, ifname, 1))
+ {
+ if (added)
+ nl80211_remove_iface(drv, ifidx);
+ os_free(new_bss);
+ return -1;
+ }
+ os_strlcpy(new_bss->ifname, ifname, IFNAMSIZ);
+ os_memcpy(new_bss->addr, if_addr, ETH_ALEN);
+ new_bss->ifindex = ifidx;
+ new_bss->drv = drv;
+ new_bss->next = drv->first_bss->next;
+ new_bss->flink = &new_bss->links[0];
+ new_bss->n_links = 1;
+ os_memcpy(new_bss->flink->addr, new_bss->addr, ETH_ALEN);
+
+ new_bss->flink->freq = drv->first_bss->flink->freq;
+ new_bss->ctx = bss_ctx;
+ new_bss->added_if = added;
+ drv->first_bss->next = new_bss;
+ if (drv_priv)
+ *drv_priv = new_bss;
+ nl80211_init_bss(new_bss);
+
+ /* Subscribe management frames for this WPA_IF_AP_BSS */
+ if (nl80211_setup_ap(new_bss))
+ return -1;
+ }
+
+ if (drv->global)
+ drv->global->if_add_ifindex = ifidx;
+
+ /*
+ * Some virtual interfaces need to process EAPOL packets and events on
+ * the parent interface. This is used mainly with hostapd.
+ */
+ if (ifidx > 0 &&
+ (drv->hostapd ||
+ nlmode == NL80211_IFTYPE_AP_VLAN ||
+ nlmode == NL80211_IFTYPE_WDS ||
+ nlmode == NL80211_IFTYPE_MONITOR))
+ add_ifidx(drv, ifidx, IFIDX_ANY);
+
+ return 0;
+}
+
+
+static int wpa_driver_nl80211_if_remove(struct i802_bss *bss,
+ enum wpa_driver_if_type type,
+ const char *ifname)
+{
+ 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 added_if=%d",
+ __func__, type, ifname, ifindex, bss->added_if);
+ if (ifindex > 0 && (bss->added_if || bss->ifindex != ifindex))
+ nl80211_remove_iface(drv, ifindex);
+ else if (ifindex > 0 && !bss->added_if) {
+ struct wpa_driver_nl80211_data *drv2;
+ dl_list_for_each(drv2, &drv->global->interfaces,
+ struct wpa_driver_nl80211_data, list) {
+ del_ifidx(drv2, ifindex, IFIDX_ANY);
+ del_ifidx(drv2, IFIDX_ANY, ifindex);
+ }
+ }
+
+ if (type != WPA_IF_AP_BSS)
+ return 0;
+
+ if (bss->added_if_into_bridge) {
+ if (linux_br_del_if(drv->global->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->global->ioctl_sock, bss->brname) < 0)
+ wpa_printf(MSG_INFO, "nl80211: Failed to remove "
+ "bridge %s: %s",
+ bss->brname, strerror(errno));
+ }
+
+ if (bss != drv->first_bss) {
+ struct i802_bss *tbss;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Not the first BSS - remove it");
+ for (tbss = drv->first_bss; tbss; tbss = tbss->next) {
+ if (tbss->next == bss) {
+ tbss->next = bss->next;
+ /* Unsubscribe management frames */
+ nl80211_teardown_ap(bss);
+ nl80211_destroy_bss(bss);
+ if (!bss->added_if)
+ i802_set_iface_flags(bss, 0);
+ os_free(bss);
+ bss = NULL;
+ break;
+ }
+ }
+ if (bss)
+ wpa_printf(MSG_INFO, "nl80211: %s - could not find "
+ "BSS %p in the list", __func__, bss);
+ } else {
+ wpa_printf(MSG_DEBUG, "nl80211: First BSS - reassign context");
+ nl80211_teardown_ap(bss);
+ nl80211_destroy_bss(bss);
+ if (!bss->added_if)
+ i802_set_iface_flags(bss, 0);
+ if (drv->first_bss->next) {
+ drv->first_bss = drv->first_bss->next;
+ drv->ctx = drv->first_bss->ctx;
+ os_free(bss);
+ } else {
+ wpa_printf(MSG_DEBUG, "nl80211: No second BSS to reassign context to");
+ }
+ }
+
+ 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 i802_bss *bss,
+ unsigned int freq, unsigned int wait,
+ const u8 *buf, size_t buf_len,
+ int save_cookie, int no_cck, int no_ack,
+ int offchanok, const u16 *csa_offs,
+ size_t csa_offs_len)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ u64 cookie;
+ int ret = -1;
+
+ wpa_printf(MSG_MSGDUMP, "nl80211: CMD_FRAME freq=%u wait=%u no_cck=%d "
+ "no_ack=%d offchanok=%d",
+ freq, wait, no_cck, no_ack, offchanok);
+ wpa_hexdump(MSG_MSGDUMP, "CMD_FRAME", buf, buf_len);
+
+ if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_FRAME)) ||
+ (freq && nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq)) ||
+ (wait && nla_put_u32(msg, NL80211_ATTR_DURATION, wait)) ||
+ (offchanok && ((drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) ||
+ drv->test_use_roc_tx) &&
+ nla_put_flag(msg, NL80211_ATTR_OFFCHANNEL_TX_OK)) ||
+ (no_cck && nla_put_flag(msg, NL80211_ATTR_TX_NO_CCK_RATE)) ||
+ (no_ack && nla_put_flag(msg, NL80211_ATTR_DONT_WAIT_FOR_ACK)) ||
+ (csa_offs && nla_put(msg, NL80211_ATTR_CSA_C_OFFSETS_TX,
+ csa_offs_len * sizeof(u16), csa_offs)) ||
+ nla_put(msg, NL80211_ATTR_FRAME, buf_len, buf))
+ goto fail;
+
+ cookie = 0;
+ ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie, NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Frame command failed: ret=%d "
+ "(%s) (freq=%u wait=%u)", ret, strerror(-ret),
+ freq, wait);
+ } else {
+ wpa_printf(MSG_MSGDUMP, "nl80211: Frame TX command accepted%s; "
+ "cookie 0x%llx", no_ack ? " (no ACK)" : "",
+ (long long unsigned int) cookie);
+
+ if (save_cookie)
+ drv->send_frame_cookie = no_ack ? (u64) -1 : cookie;
+
+ if (!wait) {
+ /* There is no need to store this cookie since there
+ * is no wait that could be canceled later. */
+ goto fail;
+ }
+ if (drv->num_send_frame_cookies == MAX_SEND_FRAME_COOKIES) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Drop oldest pending send frame cookie 0x%llx",
+ (long long unsigned int)
+ drv->send_frame_cookies[0]);
+ os_memmove(&drv->send_frame_cookies[0],
+ &drv->send_frame_cookies[1],
+ (MAX_SEND_FRAME_COOKIES - 1) *
+ sizeof(u64));
+ drv->num_send_frame_cookies--;
+ }
+ drv->send_frame_cookies[drv->num_send_frame_cookies] = cookie;
+ drv->num_send_frame_cookies++;
+ }
+
+fail:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+static int wpa_driver_nl80211_send_action(struct i802_bss *bss,
+ unsigned int freq,
+ unsigned int wait_time,
+ const u8 *dst, const u8 *src,
+ const u8 *bssid,
+ const u8 *data, size_t data_len,
+ int no_cck)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int ret = -1;
+ u8 *buf;
+ struct ieee80211_hdr *hdr;
+ int offchanok = 1;
+
+ if (is_ap_interface(drv->nlmode) && (int) freq == bss->flink->freq &&
+ bss->flink->beacon_set)
+ offchanok = 0;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Send Action frame (ifindex=%d, "
+ "freq=%u MHz wait=%d ms no_cck=%d offchanok=%d)",
+ drv->ifindex, freq, wait_time, no_cck, offchanok);
+
+ 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 (os_memcmp(bss->addr, src, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Use random TA " MACSTR,
+ MAC2STR(src));
+ os_memcpy(bss->rand_addr, src, ETH_ALEN);
+ } else {
+ os_memset(bss->rand_addr, 0, ETH_ALEN);
+ }
+
+#ifdef CONFIG_MESH
+ if (is_mesh_interface(drv->nlmode)) {
+ struct hostapd_hw_modes *modes;
+ u16 num_modes, flags;
+ u8 dfs_domain;
+ int i;
+
+ modes = nl80211_get_hw_feature_data(bss, &num_modes,
+ &flags, &dfs_domain);
+ if (dfs_domain != HOSTAPD_DFS_REGION_ETSI &&
+ ieee80211_is_dfs(bss->flink->freq, modes, num_modes))
+ offchanok = 0;
+ if (modes) {
+ for (i = 0; i < num_modes; i++) {
+ os_free(modes[i].channels);
+ os_free(modes[i].rates);
+ }
+ os_free(modes);
+ }
+ }
+#endif /* CONFIG_MESH */
+
+ if (is_ap_interface(drv->nlmode) &&
+ (!(drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) ||
+ (int) freq == bss->flink->freq || drv->device_ap_sme ||
+ !drv->use_monitor))
+ ret = wpa_driver_nl80211_send_mlme(bss, buf, 24 + data_len,
+ 0, freq, no_cck, offchanok,
+ wait_time, NULL, 0, 0, -1);
+ else
+ ret = nl80211_send_frame_cmd(bss, freq, wait_time, buf,
+ 24 + data_len,
+ 1, no_cck, 0, offchanok, NULL, 0);
+
+ os_free(buf);
+ return ret;
+}
+
+
+static void nl80211_frame_wait_cancel(struct i802_bss *bss, u64 cookie)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Cancel TX frame wait: cookie=0x%llx",
+ (long long unsigned int) cookie);
+ if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_FRAME_WAIT_CANCEL)) ||
+ nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie)) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ if (ret)
+ wpa_printf(MSG_DEBUG, "nl80211: wait cancel failed: ret=%d "
+ "(%s)", ret, strerror(-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;
+ unsigned int i;
+ u64 cookie;
+
+ /* Cancel the last pending TX cookie */
+ if (drv->send_frame_cookie != (u64) -1)
+ nl80211_frame_wait_cancel(bss, drv->send_frame_cookie);
+
+ /*
+ * Cancel the other pending TX cookies, if any. This is needed since
+ * the driver may keep a list of all pending offchannel TX operations
+ * and free up the radio only once they have expired or cancelled.
+ */
+ for (i = drv->num_send_frame_cookies; i > 0; i--) {
+ cookie = drv->send_frame_cookies[i - 1];
+ if (cookie != drv->send_frame_cookie)
+ nl80211_frame_wait_cancel(bss, cookie);
+ }
+ drv->num_send_frame_cookies = 0;
+}
+
+
+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;
+
+ if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_REMAIN_ON_CHANNEL)) ||
+ nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq) ||
+ nla_put_u32(msg, NL80211_ATTR_DURATION, duration)) {
+ nlmsg_free(msg);
+ return -1;
+ }
+
+ cookie = 0;
+ ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie, NULL, NULL);
+ 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;
+ drv->pending_remain_on_chan = 1;
+ return 0;
+ }
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to request remain-on-channel "
+ "(freq=%d duration=%u): %d (%s)",
+ freq, duration, ret, strerror(-ret));
+ 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 = nl80211_cmd_msg(bss, 0, NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL);
+ if (!msg ||
+ nla_put_u64(msg, NL80211_ATTR_COOKIE, drv->remain_on_chan_cookie)) {
+ nlmsg_free(msg);
+ return -1;
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ if (ret == 0)
+ return 0;
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to cancel remain-on-channel: "
+ "%d (%s)", ret, strerror(-ret));
+ return -1;
+}
+
+
+static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss, int report)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ if (!report) {
+ if (bss->nl_preq && drv->device_ap_sme &&
+ is_ap_interface(drv->nlmode) && !bss->in_deinit &&
+ !bss->static_ap) {
+ /*
+ * Do not disable Probe Request reporting that was
+ * enabled in nl80211_setup_ap().
+ */
+ wpa_printf(MSG_DEBUG, "nl80211: Skip disabling of "
+ "Probe Request reporting nl_preq=%p while "
+ "in AP mode", bss->nl_preq);
+ } else if (bss->nl_preq) {
+ wpa_printf(MSG_DEBUG, "nl80211: Disable Probe Request "
+ "reporting nl_preq=%p", bss->nl_preq);
+ nl80211_destroy_eloop_handle(&bss->nl_preq, 0);
+ }
+ return 0;
+ }
+
+ if (bss->nl_preq) {
+ wpa_printf(MSG_DEBUG, "nl80211: Probe Request reporting "
+ "already on! nl_preq=%p", bss->nl_preq);
+ return 0;
+ }
+
+ bss->nl_preq = nl_create_handle(drv->global->nl_cb, "preq");
+ if (bss->nl_preq == NULL)
+ return -1;
+ wpa_printf(MSG_DEBUG, "nl80211: Enable Probe Request "
+ "reporting nl_preq=%p", bss->nl_preq);
+
+ if (nl80211_register_frame(bss, bss->nl_preq,
+ (WLAN_FC_TYPE_MGMT << 2) |
+ (WLAN_FC_STYPE_PROBE_REQ << 4),
+ NULL, 0, false) < 0)
+ goto out_err;
+
+ nl80211_register_eloop_read(&bss->nl_preq,
+ wpa_driver_nl80211_event_receive,
+ bss->nl_cb, 0);
+
+ return 0;
+
+ out_err:
+ nl_destroy_handles(&bss->nl_preq);
+ 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;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: NL80211_CMD_SET_TX_BITRATE_MASK (ifindex=%d %s)",
+ ifindex, disabled ? "NL80211_TXRATE_LEGACY=OFDM-only" :
+ "no NL80211_TXRATE_LEGACY constraint");
+
+ msg = nl80211_ifindex_msg(drv, ifindex, 0,
+ NL80211_CMD_SET_TX_BITRATE_MASK);
+ if (!msg)
+ return -1;
+
+ bands = nla_nest_start(msg, NL80211_ATTR_TX_RATES);
+ if (!bands)
+ goto fail;
+
+ /*
+ * 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 ||
+ (disabled && nla_put(msg, NL80211_TXRATE_LEGACY, 8,
+ "\x0c\x12\x18\x24\x30\x48\x60\x6c")))
+ goto fail;
+ nla_nest_end(msg, band);
+
+ nla_nest_end(msg, bands);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Set TX rates failed: ret=%d "
+ "(%s)", ret, strerror(-ret));
+ } else
+ drv->disabled_11b_rates = disabled;
+
+ return ret;
+
+fail:
+ nlmsg_free(msg);
+ return -1;
+}
+
+
+static void nl80211_remove_links(struct i802_bss *bss)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret;
+ u8 link_id;
+
+ while (bss->links[0].link_id != NL80211_DRV_LINK_ID_NA) {
+ struct i802_link *link = &bss->links[0];
+
+ wpa_printf(MSG_DEBUG, "nl80211: MLD: remove link_id=%u",
+ link->link_id);
+
+ wpa_driver_nl80211_del_beacon(bss, link);
+
+ link_id = link->link_id;
+
+ /* First remove the link locally */
+ if (bss->n_links == 1) {
+ bss->flink->link_id = NL80211_DRV_LINK_ID_NA;
+ os_memcpy(bss->flink->addr, bss->addr, ETH_ALEN);
+ } else {
+ struct i802_link *other = &bss->links[bss->n_links - 1];
+
+ os_memcpy(link, other, sizeof(*link));
+ other->link_id = NL80211_DRV_LINK_ID_NA;
+ os_memset(other->addr, 0, ETH_ALEN);
+
+ bss->n_links--;
+ }
+
+ /* Remove the link from the kernel */
+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_REMOVE_LINK);
+ if (!msg ||
+ nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id)) {
+ nlmsg_free(msg);
+ wpa_printf(MSG_ERROR,
+ "nl80211: remove link (%d) failed",
+ link_id);
+ return;
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ if (ret) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: remove link (%d) failed. ret=%d (%s)",
+ link_id, ret, strerror(-ret));
+ return;
+ }
+ }
+}
+
+
+static int wpa_driver_nl80211_deinit_ap(void *priv)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ if (!is_ap_interface(drv->nlmode))
+ return -1;
+
+ /* Stop beaconing */
+ wpa_driver_nl80211_del_beacon(bss, bss->flink);
+
+ nl80211_remove_links(bss);
+
+ /*
+ * If the P2P GO interface was dynamically added, then it is
+ * possible that the interface change to station is not possible.
+ */
+ if (drv->nlmode == NL80211_IFTYPE_P2P_GO && bss->if_dynamic)
+ return 0;
+
+ return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION);
+}
+
+
+static int wpa_driver_nl80211_stop_ap(void *priv)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ if (!is_ap_interface(drv->nlmode))
+ return -1;
+
+ wpa_driver_nl80211_del_beacon_all(bss);
+
+ return 0;
+}
+
+
+static int wpa_driver_nl80211_deinit_p2p_cli(void *priv)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ if (drv->nlmode != NL80211_IFTYPE_P2P_CLIENT)
+ return -1;
+
+ /*
+ * If the P2P Client interface was dynamically added, then it is
+ * possible that the interface change to station is not possible.
+ */
+ if (bss->if_dynamic)
+ return 0;
+
+ return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION);
+}
+
+
+static void wpa_driver_nl80211_resume(void *priv)
+{
+ struct i802_bss *bss = priv;
+ enum nl80211_iftype nlmode = nl80211_get_ifmode(bss);
+
+ if (i802_set_iface_flags(bss, 1))
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface up on resume event");
+
+ if (is_p2p_net_interface(nlmode))
+ nl80211_disable_11b_rates(bss->drv, bss->drv->ifindex, 1);
+}
+
+
+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;
+ struct nlattr *cqm;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Signal monitor threshold=%d "
+ "hysteresis=%d", threshold, hysteresis);
+
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_CQM)) ||
+ !(cqm = nla_nest_start(msg, NL80211_ATTR_CQM)) ||
+ nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_THOLD, threshold) ||
+ nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_HYST, hysteresis)) {
+ nlmsg_free(msg);
+ return -1;
+ }
+ nla_nest_end(msg, cqm);
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+}
+
+
+static int get_channel_width(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct wpa_signal_info *sig_change = arg;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ sig_change->center_frq1 = -1;
+ sig_change->center_frq2 = -1;
+ sig_change->chanwidth = CHAN_WIDTH_UNKNOWN;
+
+ if (tb[NL80211_ATTR_CHANNEL_WIDTH]) {
+ sig_change->chanwidth = convert2width(
+ nla_get_u32(tb[NL80211_ATTR_CHANNEL_WIDTH]));
+ if (tb[NL80211_ATTR_CENTER_FREQ1])
+ sig_change->center_frq1 =
+ nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]);
+ if (tb[NL80211_ATTR_CENTER_FREQ2])
+ sig_change->center_frq2 =
+ nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]);
+ }
+
+ return NL_SKIP;
+}
+
+
+static int nl80211_get_channel_width(struct wpa_driver_nl80211_data *drv,
+ struct wpa_signal_info *sig)
+{
+ struct nl_msg *msg;
+
+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE);
+ return send_and_recv_msgs(drv, msg, get_channel_width, sig, NULL, NULL);
+}
+
+
+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, drv->bssid, &si->data);
+ if (res) {
+ if (drv->nlmode != NL80211_IFTYPE_ADHOC &&
+ drv->nlmode != NL80211_IFTYPE_MESH_POINT)
+ return res;
+ si->data.signal = 0;
+ }
+
+ res = nl80211_get_channel_width(drv, si);
+ if (res != 0)
+ return res;
+
+ return nl80211_get_link_noise(drv, si);
+}
+
+
+static int get_links_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_mlo_signal_info *mlo_sig = arg;
+ int i;
+
+ 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 (!sinfo[NL80211_SURVEY_INFO_NOISE])
+ return NL_SKIP;
+
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ if (!(mlo_sig->valid_links & BIT(i)))
+ continue;
+
+ if (nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) !=
+ mlo_sig->links[i].frequency)
+ continue;
+
+ mlo_sig->links[i].current_noise =
+ (s8) nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]);
+ break;
+ }
+
+ return NL_SKIP;
+}
+
+
+static int nl80211_get_links_noise(struct wpa_driver_nl80211_data *drv,
+ struct wpa_mlo_signal_info *mlo_sig)
+{
+ struct nl_msg *msg;
+
+ msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
+ return send_and_recv_msgs(drv, msg, get_links_noise, mlo_sig,
+ NULL, NULL);
+}
+
+
+static int get_links_channel_width(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct wpa_mlo_signal_info *mlo_sig = arg;
+ struct nlattr *link;
+ int rem_links;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ if (!tb[NL80211_ATTR_MLO_LINKS])
+ return NL_SKIP;
+
+ nla_for_each_nested(link, tb[NL80211_ATTR_MLO_LINKS], rem_links) {
+ struct nlattr *tb2[NL80211_ATTR_MAX + 1];
+ int link_id;
+
+ nla_parse(tb2, NL80211_ATTR_MAX, nla_data(link), nla_len(link),
+ NULL);
+
+ if (!tb2[NL80211_ATTR_MLO_LINK_ID])
+ continue;
+
+ link_id = nla_get_u8(tb2[NL80211_ATTR_MLO_LINK_ID]);
+ if (link_id >= MAX_NUM_MLD_LINKS)
+ continue;
+
+ if (!tb2[NL80211_ATTR_CHANNEL_WIDTH])
+ continue;
+ mlo_sig->links[link_id].chanwidth = convert2width(
+ nla_get_u32(tb2[NL80211_ATTR_CHANNEL_WIDTH]));
+ if (tb2[NL80211_ATTR_CENTER_FREQ1])
+ mlo_sig->links[link_id].center_frq1 =
+ nla_get_u32(tb2[NL80211_ATTR_CENTER_FREQ1]);
+ if (tb2[NL80211_ATTR_CENTER_FREQ2])
+ mlo_sig->links[link_id].center_frq2 =
+ nla_get_u32(tb2[NL80211_ATTR_CENTER_FREQ2]);
+ }
+
+ return NL_SKIP;
+}
+
+
+static int nl80211_get_links_channel_width(struct wpa_driver_nl80211_data *drv,
+ struct wpa_mlo_signal_info *mlo_sig)
+{
+ struct nl_msg *msg;
+
+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE);
+ return send_and_recv_msgs(drv, msg, get_links_channel_width, mlo_sig,
+ NULL, NULL);
+}
+
+
+static int nl80211_mlo_signal_poll(void *priv,
+ struct wpa_mlo_signal_info *mlo_si)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int res;
+ int i;
+
+ if (drv->nlmode != NL80211_IFTYPE_STATION ||
+ !drv->sta_mlo_info.valid_links)
+ return -1;
+
+ os_memset(mlo_si, 0, sizeof(*mlo_si));
+ mlo_si->valid_links = drv->sta_mlo_info.valid_links;
+
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ if (!(mlo_si->valid_links & BIT(i)))
+ continue;
+
+ res = nl80211_get_link_signal(drv,
+ drv->sta_mlo_info.links[i].bssid,
+ &mlo_si->links[i].data);
+ if (res != 0)
+ return res;
+
+ mlo_si->links[i].center_frq1 = -1;
+ mlo_si->links[i].center_frq2 = -1;
+ mlo_si->links[i].chanwidth = CHAN_WIDTH_UNKNOWN;
+ mlo_si->links[i].current_noise = WPA_INVALID_NOISE;
+ mlo_si->links[i].frequency = drv->sta_mlo_info.links[i].freq;
+ }
+
+ res = nl80211_get_links_channel_width(drv, mlo_si);
+ if (res != 0)
+ return res;
+
+ return nl80211_get_links_noise(drv, mlo_si);
+}
+
+
+static int nl80211_set_param(void *priv, const char *param)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ if (param == NULL)
+ return 0;
+ wpa_printf(MSG_DEBUG, "nl80211: driver param='%s'", param);
+
+#ifdef CONFIG_P2P
+ if (os_strstr(param, "use_p2p_group_interface=1")) {
+ 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 */
+
+ if (os_strstr(param, "use_monitor=1"))
+ drv->use_monitor = 1;
+
+ if (os_strstr(param, "force_connect_cmd=1")) {
+ drv->capa.flags &= ~WPA_DRIVER_FLAGS_SME;
+ drv->force_connect_cmd = 1;
+ }
+
+ if (os_strstr(param, "force_bss_selection=1"))
+ drv->capa.flags |= WPA_DRIVER_FLAGS_BSS_SELECTION;
+
+ if (os_strstr(param, "no_offchannel_tx=1")) {
+ drv->capa.flags &= ~WPA_DRIVER_FLAGS_OFFCHANNEL_TX;
+ drv->test_use_roc_tx = 1;
+ }
+
+ if (os_strstr(param, "control_port=0")) {
+ drv->capa.flags &= ~WPA_DRIVER_FLAGS_CONTROL_PORT;
+ drv->capa.flags2 &= ~(WPA_DRIVER_FLAGS2_CONTROL_PORT_RX |
+ WPA_DRIVER_FLAGS2_CONTROL_PORT_TX_STATUS);
+ drv->control_port_ap = 0;
+ }
+
+ if (os_strstr(param, "control_port_ap=1"))
+ drv->control_port_ap = 1;
+
+ if (os_strstr(param, "control_port_ap=0")) {
+ drv->capa.flags2 &= ~WPA_DRIVER_FLAGS2_CONTROL_PORT_TX_STATUS;
+ drv->control_port_ap = 0;
+ }
+
+ if (os_strstr(param, "full_ap_client_state=0"))
+ drv->capa.flags &= ~WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE;
+
+ if (os_strstr(param, "no_rrm=1")) {
+ drv->no_rrm = 1;
+
+ if (!bss->in_deinit && !is_ap_interface(drv->nlmode) &&
+ !is_mesh_interface(drv->nlmode)) {
+ nl80211_mgmt_unsubscribe(bss, "no_rrm=1");
+ if (nl80211_mgmt_subscribe_non_ap(bss) < 0)
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Failed to re-register Action frame processing - ignore for now");
+ }
+ }
+
+ if (os_strstr(param, "secure_ltf=1")) {
+ drv->capa.flags2 |= WPA_DRIVER_FLAGS2_SEC_LTF_STA |
+ WPA_DRIVER_FLAGS2_SEC_LTF_AP;
+ }
+
+ return 0;
+}
+
+
+static void * nl80211_global_init(void *ctx)
+{
+ struct nl80211_global *global;
+ struct netlink_config *cfg;
+
+ global = os_zalloc(sizeof(*global));
+ if (global == NULL)
+ return NULL;
+ global->ctx = ctx;
+ global->ioctl_sock = -1;
+ dl_list_init(&global->interfaces);
+ global->if_add_ifindex = -1;
+
+ cfg = os_zalloc(sizeof(*cfg));
+ if (cfg == NULL)
+ goto err;
+
+ cfg->ctx = global;
+ cfg->newlink_cb = wpa_driver_nl80211_event_rtm_newlink;
+ cfg->dellink_cb = wpa_driver_nl80211_event_rtm_dellink;
+ global->netlink = netlink_init(cfg);
+ if (global->netlink == NULL) {
+ os_free(cfg);
+ goto err;
+ }
+
+ if (wpa_driver_nl80211_init_nl_global(global) < 0)
+ goto err;
+
+ global->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (global->ioctl_sock < 0) {
+ wpa_printf(MSG_ERROR, "nl80211: socket(PF_INET,SOCK_DGRAM) failed: %s",
+ strerror(errno));
+ goto err;
+ }
+
+ return global;
+
+err:
+ nl80211_global_deinit(global);
+ return NULL;
+}
+
+
+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));
+ }
+
+ if (global->netlink)
+ netlink_deinit(global->netlink);
+
+ nl_destroy_handles(&global->nl);
+
+ if (global->nl_event)
+ nl80211_destroy_eloop_handle(&global->nl_event, 0);
+
+ nl_cb_put(global->nl_cb);
+
+ if (global->ioctl_sock >= 0)
+ close(global->ioctl_sock);
+
+ 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;
+}
+
+
+static int nl80211_pmkid(struct i802_bss *bss, int cmd,
+ struct wpa_pmkid_params *params, bool skip_pmk)
+{
+ struct nl_msg *msg;
+
+ if (cmd == NL80211_CMD_SET_PMKSA)
+ wpa_printf(MSG_DEBUG,
+ "nl80211: NL80211_CMD_SET_PMKSA with skip_pmk=%s pmk_len=%zu",
+ skip_pmk ? "true" : "false", params->pmk_len);
+
+ if (!(msg = nl80211_bss_msg(bss, 0, cmd)) ||
+ (params->pmkid &&
+ nla_put(msg, NL80211_ATTR_PMKID, 16, params->pmkid)) ||
+ (params->bssid &&
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid)) ||
+ (params->ssid_len &&
+ nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid)) ||
+ (params->fils_cache_id &&
+ nla_put(msg, NL80211_ATTR_FILS_CACHE_ID, 2,
+ params->fils_cache_id)) ||
+ (params->pmk_lifetime &&
+ nla_put_u32(msg, NL80211_ATTR_PMK_LIFETIME,
+ params->pmk_lifetime)) ||
+ (params->pmk_reauth_threshold &&
+ nla_put_u8(msg, NL80211_ATTR_PMK_REAUTH_THRESHOLD,
+ params->pmk_reauth_threshold)) ||
+ (cmd != NL80211_CMD_DEL_PMKSA &&
+ params->pmk_len && !skip_pmk &&
+ nla_put(msg, NL80211_ATTR_PMK, params->pmk_len, params->pmk))) {
+ nl80211_nlmsg_clear(msg);
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ return send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
+}
+
+
+static int nl80211_add_pmkid(void *priv, struct wpa_pmkid_params *params)
+{
+ struct i802_bss *bss = priv;
+ const size_t PMK_MAX_LEN = 64; /* current cfg80211 limit */
+ const size_t LEGACY_PMK_MAX_LEN = 48; /* old cfg80211 limit */
+ bool skip_pmk = params->pmk_len > PMK_MAX_LEN;
+ int ret;
+
+ if (params->bssid)
+ wpa_printf(MSG_DEBUG, "nl80211: Add PMKID for " MACSTR,
+ MAC2STR(params->bssid));
+ else if (params->fils_cache_id && params->ssid_len) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Add PMKSA for cache id %02x%02x SSID %s",
+ params->fils_cache_id[0], params->fils_cache_id[1],
+ wpa_ssid_txt(params->ssid, params->ssid_len));
+ }
+
+ ret = nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, params, skip_pmk);
+ /*
+ * Try again by skipping PMK if the first attempt failed with ERANGE
+ * error, PMK was not skipped, and PMK length is greater than the
+ * legacy kernel maximum allowed limit.
+ */
+ if (ret == -ERANGE && !skip_pmk &&
+ params->pmk_len > LEGACY_PMK_MAX_LEN)
+ ret = nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, params, true);
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: NL80211_CMD_SET_PMKSA failed: %d (%s)",
+ ret, strerror(-ret));
+ }
+
+ return ret;
+}
+
+
+static int nl80211_remove_pmkid(void *priv, struct wpa_pmkid_params *params)
+{
+ struct i802_bss *bss = priv;
+ int ret;
+
+ if (params->bssid)
+ wpa_printf(MSG_DEBUG, "nl80211: Delete PMKID for " MACSTR,
+ MAC2STR(params->bssid));
+ else if (params->fils_cache_id && params->ssid_len) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Delete PMKSA for cache id %02x%02x SSID %s",
+ params->fils_cache_id[0], params->fils_cache_id[1],
+ wpa_ssid_txt(params->ssid, params->ssid_len));
+ }
+
+ ret = nl80211_pmkid(bss, NL80211_CMD_DEL_PMKSA, params, true);
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: NL80211_CMD_DEL_PMKSA failed: %d (%s)",
+ ret, strerror(-ret));
+ }
+
+ return ret;
+}
+
+
+static int nl80211_flush_pmkid(void *priv)
+{
+ struct i802_bss *bss = priv;
+ struct nl_msg *msg;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Flush PMKIDs");
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_FLUSH_PMKSA);
+ if (!msg)
+ return -ENOBUFS;
+ return send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
+}
+
+
+static void clean_survey_results(struct survey_results *survey_results)
+{
+ struct freq_survey *survey, *tmp;
+
+ if (dl_list_empty(&survey_results->survey_list))
+ return;
+
+ dl_list_for_each_safe(survey, tmp, &survey_results->survey_list,
+ struct freq_survey, list) {
+ dl_list_del(&survey->list);
+ os_free(survey);
+ }
+}
+
+
+static void add_survey(struct nlattr **sinfo, u32 ifidx,
+ struct dl_list *survey_list)
+{
+ struct freq_survey *survey;
+
+ survey = os_zalloc(sizeof(struct freq_survey));
+ if (!survey)
+ return;
+
+ survey->ifidx = ifidx;
+ survey->freq = nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]);
+ survey->filled = 0;
+
+ if (sinfo[NL80211_SURVEY_INFO_NOISE]) {
+ survey->nf = (int8_t)
+ nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]);
+ survey->filled |= SURVEY_HAS_NF;
+ }
+
+ if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME]) {
+ survey->channel_time =
+ nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME]);
+ survey->filled |= SURVEY_HAS_CHAN_TIME;
+ }
+
+ if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY]) {
+ survey->channel_time_busy =
+ nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY]);
+ survey->filled |= SURVEY_HAS_CHAN_TIME_BUSY;
+ }
+
+ if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_RX]) {
+ survey->channel_time_rx =
+ nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_RX]);
+ survey->filled |= SURVEY_HAS_CHAN_TIME_RX;
+ }
+
+ if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_TX]) {
+ survey->channel_time_tx =
+ nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_TX]);
+ survey->filled |= SURVEY_HAS_CHAN_TIME_TX;
+ }
+
+ wpa_printf(MSG_DEBUG, "nl80211: Freq survey dump event (freq=%d MHz noise=%d channel_time=%ld busy_time=%ld tx_time=%ld rx_time=%ld filled=%04x)",
+ survey->freq,
+ survey->nf,
+ (unsigned long int) survey->channel_time,
+ (unsigned long int) survey->channel_time_busy,
+ (unsigned long int) survey->channel_time_tx,
+ (unsigned long int) survey->channel_time_rx,
+ survey->filled);
+
+ dl_list_add_tail(survey_list, &survey->list);
+}
+
+
+static int check_survey_ok(struct nlattr **sinfo, u32 surveyed_freq,
+ unsigned int freq_filter)
+{
+ if (!freq_filter)
+ return 1;
+
+ return freq_filter == surveyed_freq;
+}
+
+
+static int survey_handler(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];
+ struct survey_results *survey_results;
+ u32 surveyed_freq = 0;
+ u32 ifidx;
+
+ 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 },
+ };
+
+ survey_results = (struct survey_results *) arg;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ if (!tb[NL80211_ATTR_IFINDEX])
+ return NL_SKIP;
+
+ ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
+
+ if (!tb[NL80211_ATTR_SURVEY_INFO])
+ return NL_SKIP;
+
+ if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX,
+ tb[NL80211_ATTR_SURVEY_INFO],
+ survey_policy))
+ return NL_SKIP;
+
+ if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY]) {
+ wpa_printf(MSG_ERROR, "nl80211: Invalid survey data");
+ return NL_SKIP;
+ }
+
+ surveyed_freq = nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]);
+
+ if (!check_survey_ok(sinfo, surveyed_freq,
+ survey_results->freq_filter))
+ return NL_SKIP;
+
+ if (survey_results->freq_filter &&
+ survey_results->freq_filter != surveyed_freq) {
+ wpa_printf(MSG_EXCESSIVE, "nl80211: Ignoring survey data for freq %d MHz",
+ surveyed_freq);
+ return NL_SKIP;
+ }
+
+ add_survey(sinfo, ifidx, &survey_results->survey_list);
+
+ return NL_SKIP;
+}
+
+
+static int wpa_driver_nl80211_get_survey(void *priv, unsigned int freq)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int err;
+ union wpa_event_data data;
+ struct survey_results *survey_results;
+
+ os_memset(&data, 0, sizeof(data));
+ survey_results = &data.survey_results;
+
+ dl_list_init(&survey_results->survey_list);
+
+ msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
+ if (!msg)
+ return -ENOBUFS;
+
+ if (freq)
+ data.survey_results.freq_filter = freq;
+
+ do {
+ wpa_printf(MSG_DEBUG, "nl80211: Fetch survey data");
+ err = send_and_recv_msgs(drv, msg, survey_handler,
+ survey_results, NULL, NULL);
+ } while (err > 0);
+
+ if (err)
+ wpa_printf(MSG_ERROR, "nl80211: Failed to process survey data");
+ else
+ wpa_supplicant_event(drv->ctx, EVENT_SURVEY, &data);
+
+ clean_survey_results(survey_results);
+ return err;
+}
+
+
+static void nl80211_set_rekey_info(void *priv, const u8 *kek, size_t kek_len,
+ const u8 *kck, size_t kck_len,
+ const u8 *replay_ctr)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nlattr *replay_nested;
+ struct nl_msg *msg;
+ int ret;
+
+ if (!drv->set_rekey_offload)
+ return;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Set rekey offload");
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_REKEY_OFFLOAD)) ||
+ !(replay_nested = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA)) ||
+ nla_put(msg, NL80211_REKEY_DATA_KEK, kek_len, kek) ||
+ (kck_len && nla_put(msg, NL80211_REKEY_DATA_KCK, kck_len, kck)) ||
+ nla_put(msg, NL80211_REKEY_DATA_REPLAY_CTR, NL80211_REPLAY_CTR_LEN,
+ replay_ctr)) {
+ nl80211_nlmsg_clear(msg);
+ nlmsg_free(msg);
+ return;
+ }
+
+ nla_nest_end(msg, replay_nested);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ if (ret == -EOPNOTSUPP) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Driver does not support rekey offload");
+ drv->set_rekey_offload = 0;
+ }
+}
+
+
+static void nl80211_send_null_frame(struct i802_bss *bss, const u8 *own_addr,
+ const u8 *addr, int qos)
+{
+ /* send data frame to poll STA and check whether
+ * this frame is ACKed */
+ struct {
+ struct ieee80211_hdr hdr;
+ u16 qos_ctl;
+ } STRUCT_PACKED nulldata;
+ size_t size;
+
+ /* Send data frame to poll STA and check whether this frame is ACKed */
+
+ os_memset(&nulldata, 0, sizeof(nulldata));
+
+ if (qos) {
+ nulldata.hdr.frame_control =
+ IEEE80211_FC(WLAN_FC_TYPE_DATA,
+ WLAN_FC_STYPE_QOS_NULL);
+ size = sizeof(nulldata);
+ } else {
+ nulldata.hdr.frame_control =
+ IEEE80211_FC(WLAN_FC_TYPE_DATA,
+ WLAN_FC_STYPE_NULLFUNC);
+ size = sizeof(struct ieee80211_hdr);
+ }
+
+ nulldata.hdr.frame_control |= host_to_le16(WLAN_FC_FROMDS);
+ os_memcpy(nulldata.hdr.IEEE80211_DA_FROMDS, addr, ETH_ALEN);
+ os_memcpy(nulldata.hdr.IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN);
+ os_memcpy(nulldata.hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
+
+ if (wpa_driver_nl80211_send_mlme(bss, (u8 *) &nulldata, size, 0, 0, 0,
+ 0, 0, NULL, 0, 0, -1) < 0)
+ wpa_printf(MSG_DEBUG, "nl80211_send_null_frame: Failed to "
+ "send poll frame");
+}
+
+static void nl80211_poll_client(void *priv, const u8 *own_addr, const u8 *addr,
+ int qos)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ u64 cookie;
+ int ret;
+
+ if (!drv->poll_command_supported) {
+ nl80211_send_null_frame(bss, own_addr, addr, qos);
+ return;
+ }
+
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_PROBE_CLIENT)) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie, NULL, NULL);
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Client probe request for "
+ MACSTR " failed: ret=%d (%s)",
+ MAC2STR(addr), ret, strerror(-ret));
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Client probe request addr=" MACSTR
+ " cookie=%llu", MAC2STR(addr),
+ (long long unsigned int) cookie);
+ }
+}
+
+
+static int nl80211_set_power_save(struct i802_bss *bss, int enabled)
+{
+ struct nl_msg *msg;
+ int ret;
+
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_POWER_SAVE)) ||
+ nla_put_u32(msg, NL80211_ATTR_PS_STATE,
+ enabled ? NL80211_PS_ENABLED : NL80211_PS_DISABLED)) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Setting PS state %s failed: %d (%s)",
+ enabled ? "enabled" : "disabled",
+ ret, strerror(-ret));
+ }
+ return ret;
+}
+
+
+static int nl80211_set_p2p_powersave(void *priv, int legacy_ps, int opp_ps,
+ int ctwindow)
+{
+ struct i802_bss *bss = priv;
+
+ wpa_printf(MSG_DEBUG, "nl80211: set_p2p_powersave (legacy_ps=%d "
+ "opp_ps=%d ctwindow=%d)", legacy_ps, opp_ps, ctwindow);
+
+ if (opp_ps != -1 || ctwindow != -1) {
+#ifdef ANDROID_P2P
+ wpa_driver_set_p2p_ps(priv, legacy_ps, opp_ps, ctwindow);
+#else /* ANDROID_P2P */
+ return -1; /* Not yet supported */
+#endif /* ANDROID_P2P */
+ }
+
+ if (legacy_ps == -1)
+ return 0;
+ if (legacy_ps != 0 && legacy_ps != 1)
+ return -1; /* Not yet supported */
+
+ return nl80211_set_power_save(bss, legacy_ps);
+}
+
+
+static int nl80211_start_radar_detection(void *priv,
+ struct hostapd_freq_params *freq)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Start radar detection (CAC) %d MHz (ht_enabled=%d, vht_enabled=%d, he_enabled=%d, bandwidth=%d MHz, cf1=%d MHz, cf2=%d MHz)",
+ freq->freq, freq->ht_enabled, freq->vht_enabled, freq->he_enabled,
+ freq->bandwidth, freq->center_freq1, freq->center_freq2);
+
+ if (!(drv->capa.flags & WPA_DRIVER_FLAGS_RADAR)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Driver does not support radar "
+ "detection");
+ return -1;
+ }
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_RADAR_DETECT)) ||
+ nl80211_put_freq_params(msg, freq) < 0) {
+ nlmsg_free(msg);
+ return -1;
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ if (ret == 0)
+ return 0;
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to start radar detection: "
+ "%d (%s)", ret, strerror(-ret));
+ return -1;
+}
+
+#ifdef CONFIG_TDLS
+
+static int nl80211_add_peer_capab(struct nl_msg *msg,
+ enum tdls_peer_capability capa)
+{
+ u32 peer_capab = 0;
+
+ if (!capa)
+ return 0;
+
+ if (capa & TDLS_PEER_HT)
+ peer_capab |= NL80211_TDLS_PEER_HT;
+ if (capa & TDLS_PEER_VHT)
+ peer_capab |= NL80211_TDLS_PEER_VHT;
+ if (capa & TDLS_PEER_WMM)
+ peer_capab |= NL80211_TDLS_PEER_WMM;
+ if (capa & TDLS_PEER_HE)
+ peer_capab |= NL80211_TDLS_PEER_HE;
+
+ return nla_put_u32(msg, NL80211_ATTR_TDLS_PEER_CAPABILITY,
+ peer_capab);
+}
+
+
+static int nl80211_send_tdls_mgmt(void *priv, const u8 *dst, u8 action_code,
+ u8 dialog_token, u16 status_code,
+ u32 peer_capab, int initiator, const u8 *buf,
+ size_t len)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+
+ if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT))
+ return -EOPNOTSUPP;
+
+ if (!dst)
+ return -EINVAL;
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_TDLS_MGMT)) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, dst) ||
+ nla_put_u8(msg, NL80211_ATTR_TDLS_ACTION, action_code) ||
+ nla_put_u8(msg, NL80211_ATTR_TDLS_DIALOG_TOKEN, dialog_token) ||
+ nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, status_code) ||
+ nl80211_add_peer_capab(msg, peer_capab) ||
+ (initiator && nla_put_flag(msg, NL80211_ATTR_TDLS_INITIATOR)) ||
+ nla_put(msg, NL80211_ATTR_IE, len, buf))
+ goto fail;
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+
+fail:
+ nlmsg_free(msg);
+ return -ENOBUFS;
+}
+
+
+static int nl80211_tdls_oper(void *priv, enum tdls_oper oper, const u8 *peer)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ enum nl80211_tdls_operation nl80211_oper;
+ int res;
+
+ if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT))
+ return -EOPNOTSUPP;
+
+ switch (oper) {
+ case TDLS_DISCOVERY_REQ:
+ nl80211_oper = NL80211_TDLS_DISCOVERY_REQ;
+ break;
+ case TDLS_SETUP:
+ nl80211_oper = NL80211_TDLS_SETUP;
+ break;
+ case TDLS_TEARDOWN:
+ nl80211_oper = NL80211_TDLS_TEARDOWN;
+ break;
+ case TDLS_ENABLE_LINK:
+ nl80211_oper = NL80211_TDLS_ENABLE_LINK;
+ break;
+ case TDLS_DISABLE_LINK:
+ nl80211_oper = NL80211_TDLS_DISABLE_LINK;
+ break;
+ case TDLS_ENABLE:
+ return 0;
+ case TDLS_DISABLE:
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_TDLS_OPER)) ||
+ nla_put_u8(msg, NL80211_ATTR_TDLS_OPERATION, nl80211_oper) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer)) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ res = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ wpa_printf(MSG_DEBUG, "nl80211: TDLS_OPER: oper=%d mac=" MACSTR
+ " --> res=%d (%s)", nl80211_oper, MAC2STR(peer), res,
+ strerror(-res));
+ return res;
+}
+
+
+static int
+nl80211_tdls_enable_channel_switch(void *priv, const u8 *addr, u8 oper_class,
+ const struct hostapd_freq_params *params)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret = -ENOBUFS;
+
+ if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT) ||
+ !(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH))
+ return -EOPNOTSUPP;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Enable TDLS channel switch " MACSTR
+ " oper_class=%u freq=%u",
+ MAC2STR(addr), oper_class, params->freq);
+ msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_TDLS_CHANNEL_SWITCH);
+ if (!msg ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
+ nla_put_u8(msg, NL80211_ATTR_OPER_CLASS, oper_class) ||
+ (ret = nl80211_put_freq_params(msg, params))) {
+ nlmsg_free(msg);
+ wpa_printf(MSG_DEBUG, "nl80211: Could not build TDLS chan switch");
+ return ret;
+ }
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+}
+
+
+static int
+nl80211_tdls_disable_channel_switch(void *priv, const u8 *addr)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+
+ if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT) ||
+ !(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH))
+ return -EOPNOTSUPP;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Disable TDLS channel switch " MACSTR,
+ MAC2STR(addr));
+ msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH);
+ if (!msg ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) {
+ nlmsg_free(msg);
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Could not build TDLS cancel chan switch");
+ return -ENOBUFS;
+ }
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+}
+
+#endif /* CONFIG TDLS */
+
+
+static int driver_nl80211_set_key(void *priv,
+ struct wpa_driver_set_key_params *params)
+{
+ struct i802_bss *bss = priv;
+
+ return wpa_driver_nl80211_set_key(bss, params);
+}
+
+
+static int driver_nl80211_scan2(void *priv,
+ struct wpa_driver_scan_params *params)
+{
+ struct i802_bss *bss = priv;
+#ifdef CONFIG_DRIVER_NL80211_QCA
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ /*
+ * Do a vendor specific scan if possible. If only_new_results is
+ * set, do a normal scan since a kernel (cfg80211) BSS cache flush
+ * cannot be achieved through a vendor scan. The below condition may
+ * need to be modified if new scan flags are added in the future whose
+ * functionality can only be achieved through a normal scan.
+ */
+ if (drv->scan_vendor_cmd_avail && !params->only_new_results)
+ return wpa_driver_nl80211_vendor_scan(bss, params);
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+ return wpa_driver_nl80211_scan(bss, params);
+}
+
+
+static int driver_nl80211_deauthenticate(void *priv, const u8 *addr,
+ u16 reason_code)
+{
+ struct i802_bss *bss = priv;
+ return wpa_driver_nl80211_deauthenticate(bss, addr, reason_code);
+}
+
+
+static int driver_nl80211_authenticate(void *priv,
+ struct wpa_driver_auth_params *params)
+{
+ struct i802_bss *bss = priv;
+ return wpa_driver_nl80211_authenticate(bss, params);
+}
+
+
+static void driver_nl80211_deinit(void *priv)
+{
+ struct i802_bss *bss = priv;
+ wpa_driver_nl80211_deinit(bss);
+}
+
+
+static int driver_nl80211_if_remove(void *priv, enum wpa_driver_if_type type,
+ const char *ifname)
+{
+ struct i802_bss *bss = priv;
+ return wpa_driver_nl80211_if_remove(bss, type, ifname);
+}
+
+
+static int driver_nl80211_send_mlme(void *priv, const u8 *data,
+ size_t data_len, int noack,
+ unsigned int freq,
+ const u16 *csa_offs, size_t csa_offs_len,
+ int no_encrypt, unsigned int wait,
+ int link_id)
+{
+ struct i802_bss *bss = priv;
+ return wpa_driver_nl80211_send_mlme(bss, data, data_len, noack,
+ freq, 0, 0, wait, csa_offs,
+ csa_offs_len, no_encrypt, link_id);
+}
+
+
+static int driver_nl80211_sta_remove(void *priv, const u8 *addr)
+{
+ struct i802_bss *bss = priv;
+ return wpa_driver_nl80211_sta_remove(bss, addr, -1, 0);
+}
+
+
+static int driver_nl80211_set_sta_vlan(void *priv, const u8 *addr,
+ const char *ifname, int vlan_id,
+ int link_id)
+{
+ struct i802_bss *bss = priv;
+ return i802_set_sta_vlan(bss, addr, ifname, vlan_id, link_id);
+}
+
+
+static int driver_nl80211_read_sta_data(void *priv,
+ struct hostap_sta_driver_data *data,
+ const u8 *addr)
+{
+ struct i802_bss *bss = priv;
+
+ os_memset(data, 0, sizeof(*data));
+ return i802_read_sta_data(bss, data, addr);
+}
+
+
+static int 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,
+ int no_cck)
+{
+ struct i802_bss *bss = priv;
+ return wpa_driver_nl80211_send_action(bss, freq, wait_time, dst, src,
+ bssid, data, data_len, no_cck);
+}
+
+
+static int driver_nl80211_probe_req_report(void *priv, int report)
+{
+ struct i802_bss *bss = priv;
+ return wpa_driver_nl80211_probe_req_report(bss, report);
+}
+
+
+static int wpa_driver_nl80211_update_ft_ies(void *priv, const u8 *md,
+ const u8 *ies, size_t ies_len)
+{
+ int ret;
+ struct nl_msg *msg;
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ u16 mdid = WPA_GET_LE16(md);
+
+ wpa_printf(MSG_DEBUG, "nl80211: Updating FT IEs");
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_UPDATE_FT_IES)) ||
+ nla_put(msg, NL80211_ATTR_IE, ies_len, ies) ||
+ nla_put_u16(msg, NL80211_ATTR_MDID, mdid)) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: update_ft_ies failed "
+ "err=%d (%s)", ret, strerror(-ret));
+ }
+
+ return ret;
+}
+
+
+static int nl80211_update_dh_ie(void *priv, const u8 *peer_mac,
+ u16 reason_code, const u8 *ie, size_t ie_len)
+{
+ int ret;
+ struct nl_msg *msg;
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Updating DH IE peer: " MACSTR
+ " reason %u", MAC2STR(peer_mac), reason_code);
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_UPDATE_OWE_INFO)) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer_mac) ||
+ nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, reason_code) ||
+ (ie && nla_put(msg, NL80211_ATTR_IE, ie_len, ie))) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: update_dh_ie failed err=%d (%s)",
+ ret, strerror(-ret));
+ }
+
+ return ret;
+}
+
+
+static const u8 * wpa_driver_nl80211_get_macaddr(void *priv)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ if (drv->nlmode != NL80211_IFTYPE_P2P_DEVICE)
+ return NULL;
+
+ return bss->addr;
+}
+
+
+static const char * scan_state_str(enum scan_states scan_state)
+{
+ switch (scan_state) {
+ case NO_SCAN:
+ return "NO_SCAN";
+ case SCAN_REQUESTED:
+ return "SCAN_REQUESTED";
+ case SCAN_STARTED:
+ return "SCAN_STARTED";
+ case SCAN_COMPLETED:
+ return "SCAN_COMPLETED";
+ case SCAN_ABORTED:
+ return "SCAN_ABORTED";
+ case SCHED_SCAN_STARTED:
+ return "SCHED_SCAN_STARTED";
+ case SCHED_SCAN_STOPPED:
+ return "SCHED_SCAN_STOPPED";
+ case SCHED_SCAN_RESULTS:
+ return "SCHED_SCAN_RESULTS";
+ }
+
+ return "??";
+}
+
+
+static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int res;
+ char *pos, *end;
+ struct nl_msg *msg;
+ char alpha2[3] = { 0, 0, 0 };
+
+ pos = buf;
+ end = buf + buflen;
+
+ res = os_snprintf(pos, end - pos,
+ "ifindex=%d\n"
+ "ifname=%s\n"
+ "brname=%s\n"
+ "addr=" MACSTR "\n"
+ "freq=%d\n"
+ "%s%s%s%s%s%s",
+ bss->ifindex,
+ bss->ifname,
+ bss->brname,
+ MAC2STR(bss->addr),
+ bss->flink->freq,
+ bss->flink->beacon_set ? "beacon_set=1\n" : "",
+ bss->added_if_into_bridge ?
+ "added_if_into_bridge=1\n" : "",
+ bss->already_in_bridge ? "already_in_bridge=1\n" : "",
+ bss->added_bridge ? "added_bridge=1\n" : "",
+ bss->in_deinit ? "in_deinit=1\n" : "",
+ bss->if_dynamic ? "if_dynamic=1\n" : "");
+ if (os_snprintf_error(end - pos, res))
+ return pos - buf;
+ pos += res;
+
+ if (bss->wdev_id_set) {
+ res = os_snprintf(pos, end - pos, "wdev_id=%llu\n",
+ (unsigned long long) bss->wdev_id);
+ if (os_snprintf_error(end - pos, res))
+ return pos - buf;
+ pos += res;
+ }
+
+ res = os_snprintf(pos, end - pos,
+ "phyname=%s\n"
+ "perm_addr=" MACSTR "\n"
+ "drv_ifindex=%d\n"
+ "operstate=%d\n"
+ "scan_state=%s\n"
+ "auth_bssid=" MACSTR "\n"
+ "auth_attempt_bssid=" MACSTR "\n"
+ "bssid=" MACSTR "\n"
+ "prev_bssid=" MACSTR "\n"
+ "associated=%d\n"
+ "assoc_freq=%u\n"
+ "monitor_sock=%d\n"
+ "monitor_ifidx=%d\n"
+ "monitor_refcount=%d\n"
+ "last_mgmt_freq=%u\n"
+ "eapol_tx_sock=%d\n"
+ "%s%s%s%s%s%s%s%s%s%s%s%s%s",
+ drv->phyname,
+ MAC2STR(drv->perm_addr),
+ drv->ifindex,
+ drv->operstate,
+ scan_state_str(drv->scan_state),
+ MAC2STR(drv->auth_bssid),
+ MAC2STR(drv->auth_attempt_bssid),
+ MAC2STR(drv->bssid),
+ MAC2STR(drv->prev_bssid),
+ drv->associated,
+ drv->assoc_freq,
+ drv->monitor_sock,
+ drv->monitor_ifidx,
+ drv->monitor_refcount,
+ drv->last_mgmt_freq,
+ drv->eapol_tx_sock,
+ drv->ignore_if_down_event ?
+ "ignore_if_down_event=1\n" : "",
+ drv->scan_complete_events ?
+ "scan_complete_events=1\n" : "",
+ drv->disabled_11b_rates ?
+ "disabled_11b_rates=1\n" : "",
+ drv->pending_remain_on_chan ?
+ "pending_remain_on_chan=1\n" : "",
+ drv->in_interface_list ? "in_interface_list=1\n" : "",
+ drv->device_ap_sme ? "device_ap_sme=1\n" : "",
+ drv->poll_command_supported ?
+ "poll_command_supported=1\n" : "",
+ drv->data_tx_status ? "data_tx_status=1\n" : "",
+ drv->scan_for_auth ? "scan_for_auth=1\n" : "",
+ drv->retry_auth ? "retry_auth=1\n" : "",
+ drv->use_monitor ? "use_monitor=1\n" : "",
+ drv->ignore_next_local_disconnect ?
+ "ignore_next_local_disconnect=1\n" : "",
+ drv->ignore_next_local_deauth ?
+ "ignore_next_local_deauth=1\n" : "");
+ if (os_snprintf_error(end - pos, res))
+ return pos - buf;
+ pos += res;
+
+ if (drv->sta_mlo_info.valid_links) {
+ int i;
+ struct driver_sta_mlo_info *mlo = &drv->sta_mlo_info;
+
+ res = os_snprintf(pos, end - pos,
+ "ap_mld_addr=" MACSTR "\n"
+ "default_map=%d\n",
+ MAC2STR(mlo->ap_mld_addr),
+ mlo->default_map);
+ if (os_snprintf_error(end - pos, res))
+ return pos - buf;
+ pos += res;
+
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ if (!(mlo->valid_links & BIT(i)))
+ continue;
+
+ res = os_snprintf(pos, end - pos,
+ "link_addr[%u]=" MACSTR "\n"
+ "link_bssid[%u]=" MACSTR "\n"
+ "link_freq[%u]=%u\n",
+ i, MAC2STR(mlo->links[i].addr),
+ i, MAC2STR(mlo->links[i].bssid),
+ i, mlo->links[i].freq);
+ if (os_snprintf_error(end - pos, res))
+ return pos - buf;
+ pos += res;
+
+ if (!mlo->default_map) {
+ res = os_snprintf(
+ pos, end - pos,
+ "uplink_map[%u]=%x\n"
+ "downlink_map[%u]=%x\n",
+ i, mlo->links[i].t2lmap.uplink,
+ i, mlo->links[i].t2lmap.downlink);
+ if (os_snprintf_error(end - pos, res))
+ return pos - buf;
+ pos += res;
+ }
+ }
+ }
+
+ if (drv->has_capability) {
+ res = os_snprintf(pos, end - pos,
+ "capa.key_mgmt=0x%x\n"
+ "capa.enc=0x%x\n"
+ "capa.auth=0x%x\n"
+ "capa.flags=0x%llx\n"
+ "capa.rrm_flags=0x%x\n"
+ "capa.max_scan_ssids=%d\n"
+ "capa.max_sched_scan_ssids=%d\n"
+ "capa.sched_scan_supported=%d\n"
+ "capa.max_match_sets=%d\n"
+ "capa.max_remain_on_chan=%u\n"
+ "capa.max_stations=%u\n"
+ "capa.probe_resp_offloads=0x%x\n"
+ "capa.max_acl_mac_addrs=%u\n"
+ "capa.num_multichan_concurrent=%u\n"
+ "capa.mac_addr_rand_sched_scan_supported=%d\n"
+ "capa.mac_addr_rand_scan_supported=%d\n"
+ "capa.conc_capab=%u\n"
+ "capa.max_conc_chan_2_4=%u\n"
+ "capa.max_conc_chan_5_0=%u\n"
+ "capa.max_sched_scan_plans=%u\n"
+ "capa.max_sched_scan_plan_interval=%u\n"
+ "capa.max_sched_scan_plan_iterations=%u\n"
+ "capa.mbssid_max_interfaces=%u\n"
+ "capa.ema_max_periodicity=%u\n",
+ drv->capa.key_mgmt,
+ drv->capa.enc,
+ drv->capa.auth,
+ (unsigned long long) drv->capa.flags,
+ drv->capa.rrm_flags,
+ drv->capa.max_scan_ssids,
+ drv->capa.max_sched_scan_ssids,
+ drv->capa.sched_scan_supported,
+ drv->capa.max_match_sets,
+ drv->capa.max_remain_on_chan,
+ drv->capa.max_stations,
+ drv->capa.probe_resp_offloads,
+ drv->capa.max_acl_mac_addrs,
+ drv->capa.num_multichan_concurrent,
+ drv->capa.mac_addr_rand_sched_scan_supported,
+ drv->capa.mac_addr_rand_scan_supported,
+ drv->capa.conc_capab,
+ drv->capa.max_conc_chan_2_4,
+ drv->capa.max_conc_chan_5_0,
+ drv->capa.max_sched_scan_plans,
+ drv->capa.max_sched_scan_plan_interval,
+ drv->capa.max_sched_scan_plan_iterations,
+ drv->capa.mbssid_max_interfaces,
+ drv->capa.ema_max_periodicity);
+ if (os_snprintf_error(end - pos, res))
+ return pos - buf;
+ pos += res;
+ }
+
+ msg = nlmsg_alloc();
+ if (msg &&
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG) &&
+ nla_put_u32(msg, NL80211_ATTR_WIPHY, drv->wiphy_idx) == 0) {
+ if (send_and_recv_msgs(drv, msg, nl80211_get_country,
+ alpha2, NULL, NULL) == 0 &&
+ alpha2[0]) {
+ res = os_snprintf(pos, end - pos, "country=%s\n",
+ alpha2);
+ if (os_snprintf_error(end - pos, res))
+ return pos - buf;
+ pos += res;
+ }
+ } else {
+ nlmsg_free(msg);
+ }
+
+ return pos - buf;
+}
+
+
+static int set_beacon_data(struct nl_msg *msg, struct beacon_data *settings)
+{
+ if ((settings->head &&
+ nla_put(msg, NL80211_ATTR_BEACON_HEAD,
+ settings->head_len, settings->head)) ||
+ (settings->tail &&
+ nla_put(msg, NL80211_ATTR_BEACON_TAIL,
+ settings->tail_len, settings->tail)) ||
+ (settings->beacon_ies &&
+ nla_put(msg, NL80211_ATTR_IE,
+ settings->beacon_ies_len, settings->beacon_ies)) ||
+ (settings->proberesp_ies &&
+ nla_put(msg, NL80211_ATTR_IE_PROBE_RESP,
+ settings->proberesp_ies_len, settings->proberesp_ies)) ||
+ (settings->assocresp_ies &&
+ nla_put(msg, NL80211_ATTR_IE_ASSOC_RESP,
+ settings->assocresp_ies_len, settings->assocresp_ies)) ||
+ (settings->probe_resp &&
+ nla_put(msg, NL80211_ATTR_PROBE_RESP,
+ settings->probe_resp_len, settings->probe_resp)))
+ return -ENOBUFS;
+
+ return 0;
+}
+
+
+static int nl80211_switch_channel(void *priv, struct csa_settings *settings)
+{
+ struct nl_msg *msg;
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nlattr *beacon_csa;
+ int ret = -ENOBUFS;
+ int csa_off_len = 0;
+ int i;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Channel switch request (cs_count=%u block_tx=%u freq=%d channel=%d sec_channel_offset=%d width=%d cf1=%d cf2=%d puncturing_bitmap=0x%04x%s%s%s)",
+ settings->cs_count, settings->block_tx,
+ settings->freq_params.freq,
+ settings->freq_params.channel,
+ settings->freq_params.sec_channel_offset,
+ settings->freq_params.bandwidth,
+ settings->freq_params.center_freq1,
+ settings->freq_params.center_freq2,
+ settings->punct_bitmap,
+ settings->freq_params.ht_enabled ? " ht" : "",
+ settings->freq_params.vht_enabled ? " vht" : "",
+ settings->freq_params.he_enabled ? " he" : "");
+
+ if (!(drv->capa.flags & WPA_DRIVER_FLAGS_AP_CSA)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Driver does not support channel switch command");
+ return -EOPNOTSUPP;
+ }
+
+ if (drv->nlmode != NL80211_IFTYPE_AP &&
+ drv->nlmode != NL80211_IFTYPE_P2P_GO &&
+ drv->nlmode != NL80211_IFTYPE_MESH_POINT)
+ return -EOPNOTSUPP;
+
+ /*
+ * Remove empty counters, assuming Probe Response and Beacon frame
+ * counters match. This implementation assumes that there are only two
+ * counters.
+ */
+ if (settings->counter_offset_beacon[0] &&
+ !settings->counter_offset_beacon[1]) {
+ csa_off_len = 1;
+ } else if (settings->counter_offset_beacon[1] &&
+ !settings->counter_offset_beacon[0]) {
+ csa_off_len = 1;
+ settings->counter_offset_beacon[0] =
+ settings->counter_offset_beacon[1];
+ settings->counter_offset_presp[0] =
+ settings->counter_offset_presp[1];
+ } else if (settings->counter_offset_beacon[1] &&
+ settings->counter_offset_beacon[0]) {
+ csa_off_len = 2;
+ } else {
+ wpa_printf(MSG_ERROR, "nl80211: No CSA counters provided");
+ return -EINVAL;
+ }
+
+ /* Check CSA counters validity */
+ if (drv->capa.max_csa_counters &&
+ csa_off_len > drv->capa.max_csa_counters) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: Too many CSA counters provided");
+ return -EINVAL;
+ }
+
+ if (!settings->beacon_csa.tail)
+ return -EINVAL;
+
+ for (i = 0; i < csa_off_len; i++) {
+ u16 csa_c_off_bcn = settings->counter_offset_beacon[i];
+ u16 csa_c_off_presp = settings->counter_offset_presp[i];
+
+ if ((settings->beacon_csa.tail_len <= csa_c_off_bcn) ||
+ (settings->beacon_csa.tail[csa_c_off_bcn] !=
+ settings->cs_count))
+ return -EINVAL;
+
+ if (settings->beacon_csa.probe_resp &&
+ ((settings->beacon_csa.probe_resp_len <=
+ csa_c_off_presp) ||
+ (settings->beacon_csa.probe_resp[csa_c_off_presp] !=
+ settings->cs_count)))
+ return -EINVAL;
+ }
+
+ if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_CHANNEL_SWITCH)) ||
+ nla_put_u32(msg, NL80211_ATTR_CH_SWITCH_COUNT,
+ settings->cs_count) ||
+ (ret = nl80211_put_freq_params(msg, &settings->freq_params)) ||
+ (settings->block_tx &&
+ nla_put_flag(msg, NL80211_ATTR_CH_SWITCH_BLOCK_TX)) ||
+ (settings->punct_bitmap &&
+ nla_put_u32(msg, NL80211_ATTR_PUNCT_BITMAP,
+ settings->punct_bitmap)))
+ goto error;
+
+ /* beacon_after params */
+ ret = set_beacon_data(msg, &settings->beacon_after);
+ if (ret)
+ goto error;
+
+ if (drv->nlmode == NL80211_IFTYPE_MESH_POINT) {
+ nla_put_flag(msg, NL80211_ATTR_HANDLE_DFS);
+ }
+
+ /* beacon_csa params */
+ beacon_csa = nla_nest_start(msg, NL80211_ATTR_CSA_IES);
+ if (!beacon_csa)
+ goto fail;
+
+ ret = set_beacon_data(msg, &settings->beacon_csa);
+ if (ret)
+ goto error;
+
+ if (nla_put(msg, NL80211_ATTR_CSA_C_OFF_BEACON,
+ csa_off_len * sizeof(u16),
+ settings->counter_offset_beacon) ||
+ (settings->beacon_csa.probe_resp &&
+ nla_put(msg, NL80211_ATTR_CSA_C_OFF_PRESP,
+ csa_off_len * sizeof(u16),
+ settings->counter_offset_presp)))
+ goto fail;
+
+ nla_nest_end(msg, beacon_csa);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: switch_channel failed err=%d (%s)",
+ ret, strerror(-ret));
+ }
+ return ret;
+
+fail:
+ ret = -ENOBUFS;
+error:
+ nlmsg_free(msg);
+ wpa_printf(MSG_DEBUG, "nl80211: Could not build channel switch request");
+ return ret;
+}
+
+
+#ifdef CONFIG_IEEE80211AX
+static int nl80211_switch_color(void *priv, struct cca_settings *settings)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nlattr *beacon_cca;
+ struct nl_msg *msg;
+ int ret = -ENOBUFS;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Color change request (cca_count=%u color=%d)",
+ settings->cca_count, settings->cca_color);
+
+ if (drv->nlmode != NL80211_IFTYPE_AP)
+ return -EOPNOTSUPP;
+
+ if (!settings->beacon_cca.tail)
+ return -EINVAL;
+
+ if (settings->beacon_cca.tail_len <= settings->counter_offset_beacon ||
+ settings->beacon_cca.tail[settings->counter_offset_beacon] !=
+ settings->cca_count)
+ return -EINVAL;
+
+ if (settings->beacon_cca.probe_resp &&
+ (settings->beacon_cca.probe_resp_len <=
+ settings->counter_offset_presp ||
+ settings->beacon_cca.probe_resp[settings->counter_offset_presp] !=
+ settings->cca_count))
+ return -EINVAL;
+
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_COLOR_CHANGE_REQUEST);
+ if (!msg ||
+ nla_put_u8(msg, NL80211_ATTR_COLOR_CHANGE_COUNT,
+ settings->cca_count) ||
+ nla_put_u8(msg, NL80211_ATTR_COLOR_CHANGE_COLOR,
+ settings->cca_color))
+ goto error;
+
+ /* beacon_after params */
+ ret = set_beacon_data(msg, &settings->beacon_after);
+ if (ret)
+ goto error;
+
+ /* beacon_csa params */
+ beacon_cca = nla_nest_start(msg, NL80211_ATTR_COLOR_CHANGE_ELEMS);
+ if (!beacon_cca) {
+ ret = -ENOBUFS;
+ goto error;
+ }
+
+ ret = set_beacon_data(msg, &settings->beacon_cca);
+ if (ret)
+ goto error;
+
+ if (nla_put_u16(msg, NL80211_ATTR_CNTDWN_OFFS_BEACON,
+ settings->counter_offset_beacon) ||
+ (settings->beacon_cca.probe_resp &&
+ nla_put_u16(msg, NL80211_ATTR_CNTDWN_OFFS_PRESP,
+ settings->counter_offset_presp))) {
+ ret = -ENOBUFS;
+ goto error;
+ }
+
+ nla_nest_end(msg, beacon_cca);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: switch_color failed err=%d (%s)",
+ ret, strerror(-ret));
+ }
+ return ret;
+
+error:
+ nlmsg_free(msg);
+ wpa_printf(MSG_DEBUG, "nl80211: Could not build color switch request");
+ return ret;
+}
+#endif /* CONFIG_IEEE80211AX */
+
+
+static int nl80211_add_ts(void *priv, u8 tsid, const u8 *addr,
+ u8 user_priority, u16 admitted_time)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: add_ts request: tsid=%u admitted_time=%u up=%d",
+ tsid, admitted_time, user_priority);
+
+ if (!is_sta_interface(drv->nlmode))
+ return -ENOTSUP;
+
+ msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_ADD_TX_TS);
+ if (!msg ||
+ nla_put_u8(msg, NL80211_ATTR_TSID, tsid) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
+ nla_put_u8(msg, NL80211_ATTR_USER_PRIO, user_priority) ||
+ nla_put_u16(msg, NL80211_ATTR_ADMITTED_TIME, admitted_time)) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ if (ret)
+ wpa_printf(MSG_DEBUG, "nl80211: add_ts failed err=%d (%s)",
+ ret, strerror(-ret));
+ return ret;
+}
+
+
+static int nl80211_del_ts(void *priv, u8 tsid, const u8 *addr)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "nl80211: del_ts request: tsid=%u", tsid);
+
+ if (!is_sta_interface(drv->nlmode))
+ return -ENOTSUP;
+
+ if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_DEL_TX_TS)) ||
+ nla_put_u8(msg, NL80211_ATTR_TSID, tsid) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ if (ret)
+ wpa_printf(MSG_DEBUG, "nl80211: del_ts failed err=%d (%s)",
+ ret, strerror(-ret));
+ return ret;
+}
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+static int cmd_reply_handler(struct nl_msg *msg, void *arg)
+{
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct wpabuf *buf = arg;
+
+ if (!buf)
+ return NL_SKIP;
+
+ if ((size_t) genlmsg_attrlen(gnlh, 0) > wpabuf_tailroom(buf)) {
+ wpa_printf(MSG_INFO, "nl80211: insufficient buffer space for reply");
+ return NL_SKIP;
+ }
+
+ wpabuf_put_data(buf, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0));
+
+ return NL_SKIP;
+}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+static int vendor_reply_handler(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct nlattr *nl_vendor_reply, *nl;
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct wpabuf *buf = arg;
+ int rem;
+
+ if (!buf)
+ return NL_SKIP;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+ nl_vendor_reply = tb[NL80211_ATTR_VENDOR_DATA];
+
+ if (!nl_vendor_reply)
+ return NL_SKIP;
+
+ if ((size_t) nla_len(nl_vendor_reply) > wpabuf_tailroom(buf)) {
+ wpa_printf(MSG_INFO, "nl80211: Vendor command: insufficient buffer space for reply");
+ return NL_SKIP;
+ }
+
+ nla_for_each_nested(nl, nl_vendor_reply, rem) {
+ wpabuf_put_data(buf, nla_data(nl), nla_len(nl));
+ }
+
+ return NL_SKIP;
+}
+
+
+static bool is_cmd_with_nested_attrs(unsigned int vendor_id,
+ unsigned int subcmd)
+{
+ if (vendor_id != OUI_QCA)
+ return true;
+
+ switch (subcmd) {
+ case QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY:
+ case QCA_NL80211_VENDOR_SUBCMD_STATS_EXT:
+ case QCA_NL80211_VENDOR_SUBCMD_SCANNING_MAC_OUI:
+ case QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY:
+ case QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_STATUS:
+ case QCA_NL80211_VENDOR_SUBCMD_NAN:
+ return false;
+ default:
+ return true;
+ }
+}
+
+
+static int nl80211_vendor_cmd(void *priv, unsigned int vendor_id,
+ unsigned int subcmd, const u8 *data,
+ size_t data_len, enum nested_attr nested_attr,
+ struct wpabuf *buf)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret, nla_flag;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (vendor_id == 0xffffffff) {
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ nl80211_cmd(drv, msg, 0, subcmd);
+ if (nlmsg_append(msg, (void *) data, data_len, NLMSG_ALIGNTO) <
+ 0)
+ goto fail;
+ /* This test vendor_cmd can be used with nl80211 commands that
+ * need the connect nl_sock, so use the owner-setting variant
+ * of send_and_recv_msgs(). */
+ ret = send_and_recv_msgs_owner(drv, msg,
+ get_connect_handle(bss), 0,
+ cmd_reply_handler, buf,
+ NULL, NULL);
+ if (ret)
+ wpa_printf(MSG_DEBUG, "nl80211: command failed err=%d",
+ ret);
+ return ret;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (nested_attr == NESTED_ATTR_USED)
+ nla_flag = NLA_F_NESTED;
+ else if (nested_attr == NESTED_ATTR_UNSPECIFIED &&
+ is_cmd_with_nested_attrs(vendor_id, subcmd))
+ nla_flag = NLA_F_NESTED;
+ else
+ nla_flag = 0;
+
+ if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, vendor_id) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, subcmd) ||
+ (data &&
+ nla_put(msg, nla_flag | NL80211_ATTR_VENDOR_DATA,
+ data_len, data)))
+ goto fail;
+
+ ret = send_and_recv_msgs(drv, msg, vendor_reply_handler, buf,
+ NULL, NULL);
+ if (ret)
+ wpa_printf(MSG_DEBUG, "nl80211: vendor command failed err=%d",
+ ret);
+ return ret;
+
+fail:
+ nlmsg_free(msg);
+ return -ENOBUFS;
+}
+
+
+static int nl80211_set_qos_map(void *priv, const u8 *qos_map_set,
+ u8 qos_map_set_len)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret;
+
+ wpa_hexdump(MSG_DEBUG, "nl80211: Setting QoS Map",
+ qos_map_set, qos_map_set_len);
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_QOS_MAP)) ||
+ nla_put(msg, NL80211_ATTR_QOS_MAP, qos_map_set_len, qos_map_set)) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ if (ret)
+ wpa_printf(MSG_DEBUG, "nl80211: Setting QoS Map failed");
+
+ return ret;
+}
+
+
+static int get_wowlan_handler(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ int *wowlan_enabled = arg;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ *wowlan_enabled = !!tb[NL80211_ATTR_WOWLAN_TRIGGERS];
+
+ return NL_SKIP;
+}
+
+
+static int nl80211_get_wowlan(void *priv)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int wowlan_enabled;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Getting wowlan status");
+
+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_WOWLAN);
+
+ ret = send_and_recv_msgs(drv, msg, get_wowlan_handler, &wowlan_enabled,
+ NULL, NULL);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Getting wowlan status failed");
+ return 0;
+ }
+
+ wpa_printf(MSG_DEBUG, "nl80211: wowlan is %s",
+ wowlan_enabled ? "enabled" : "disabled");
+
+ return wowlan_enabled;
+}
+
+
+static int nl80211_set_wowlan(void *priv,
+ const struct wowlan_triggers *triggers)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ struct nlattr *wowlan_triggers;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Setting wowlan");
+
+ if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_SET_WOWLAN)) ||
+ !(wowlan_triggers = nla_nest_start(msg,
+ NL80211_ATTR_WOWLAN_TRIGGERS)) ||
+ (triggers->any &&
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) ||
+ (triggers->disconnect &&
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) ||
+ (triggers->magic_pkt &&
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) ||
+ (triggers->gtk_rekey_failure &&
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) ||
+ (triggers->eap_identity_req &&
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) ||
+ (triggers->four_way_handshake &&
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) ||
+ (triggers->rfkill_release &&
+ nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE))) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ nla_nest_end(msg, wowlan_triggers);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ if (ret)
+ wpa_printf(MSG_DEBUG, "nl80211: Setting wowlan failed");
+
+ return ret;
+}
+
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+static int nl80211_roaming(void *priv, int allowed, const u8 *bssid)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ struct nlattr *params;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Roaming policy: allowed=%d", allowed);
+
+ if (!drv->roaming_vendor_cmd_avail) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Ignore roaming policy change since driver does not provide command for setting it");
+ return -1;
+ }
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_ROAMING) ||
+ !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+ nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY,
+ allowed ? QCA_ROAMING_ALLOWED_WITHIN_ESS :
+ QCA_ROAMING_NOT_ALLOWED) ||
+ (bssid &&
+ nla_put(msg, QCA_WLAN_VENDOR_ATTR_MAC_ADDR, ETH_ALEN, bssid))) {
+ nlmsg_free(msg);
+ return -1;
+ }
+ nla_nest_end(msg, params);
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+}
+
+
+static int nl80211_disable_fils(void *priv, int disable)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ struct nlattr *params;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Disable FILS=%d", disable);
+
+ if (!drv->set_wifi_conf_vendor_cmd_avail)
+ return -1;
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION) ||
+ !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+ nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_CONFIG_DISABLE_FILS,
+ disable)) {
+ nlmsg_free(msg);
+ return -1;
+ }
+ nla_nest_end(msg, params);
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+}
+
+
+/* Reserved QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID value for wpa_supplicant */
+#define WPA_SUPPLICANT_CLIENT_ID 1
+
+static int nl80211_set_bssid_tmp_disallow(void *priv, unsigned int num_bssid,
+ const u8 *bssid)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ struct nlattr *params, *nlbssids, *attr;
+ unsigned int i;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Set temporarily disallowed BSSIDs (num=%u)",
+ num_bssid);
+
+ if (!drv->roam_vendor_cmd_avail)
+ return -1;
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_ROAM) ||
+ !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+ nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD,
+ QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_BLACKLIST_BSSID) ||
+ nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID,
+ WPA_SUPPLICANT_CLIENT_ID) ||
+ nla_put_u32(msg,
+ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID,
+ num_bssid))
+ goto fail;
+
+ nlbssids = nla_nest_start(
+ msg, QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS);
+ if (!nlbssids)
+ goto fail;
+
+ for (i = 0; i < num_bssid; i++) {
+ attr = nla_nest_start(msg, i);
+ if (!attr)
+ goto fail;
+ if (nla_put(msg,
+ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID,
+ ETH_ALEN, &bssid[i * ETH_ALEN]))
+ goto fail;
+ wpa_printf(MSG_DEBUG, "nl80211: BSSID[%u]: " MACSTR, i,
+ MAC2STR(&bssid[i * ETH_ALEN]));
+ nla_nest_end(msg, attr);
+ }
+ nla_nest_end(msg, nlbssids);
+ nla_nest_end(msg, params);
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+
+fail:
+ nlmsg_free(msg);
+ return -1;
+}
+
+
+static int nl80211_add_sta_node(void *priv, const u8 *addr, u16 auth_alg)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ struct nlattr *params;
+
+ if (!drv->add_sta_node_vendor_cmd_avail)
+ return -EOPNOTSUPP;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Add STA node");
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_ADD_STA_NODE) ||
+ !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+ (addr &&
+ nla_put(msg, QCA_WLAN_VENDOR_ATTR_ADD_STA_NODE_MAC_ADDR, ETH_ALEN,
+ addr)) ||
+ nla_put_u16(msg, QCA_WLAN_VENDOR_ATTR_ADD_STA_NODE_AUTH_ALGO,
+ auth_alg)) {
+ nlmsg_free(msg);
+ wpa_printf(MSG_ERROR,
+ "%s: err in adding vendor_cmd and vendor_data",
+ __func__);
+ return -1;
+ }
+ nla_nest_end(msg, params);
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+}
+
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+
+static int nl80211_set_mac_addr(void *priv, const u8 *addr)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int new_addr = addr != NULL;
+
+ if (TEST_FAIL())
+ return -1;
+
+ if (!addr)
+ addr = drv->perm_addr;
+
+ if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0) < 0)
+ return -1;
+
+ if (linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname, addr) < 0)
+ {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: failed to set_mac_addr for %s to " MACSTR,
+ bss->ifname, MAC2STR(addr));
+ if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname,
+ 1) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Could not restore interface UP after failed set_mac_addr");
+ }
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "nl80211: set_mac_addr for %s to " MACSTR,
+ bss->ifname, MAC2STR(addr));
+ drv->addr_changed = new_addr;
+ os_memcpy(bss->prev_addr, bss->addr, ETH_ALEN);
+ os_memcpy(bss->addr, addr, ETH_ALEN);
+
+ if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1) < 0)
+ {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Could not restore interface UP after set_mac_addr");
+ }
+
+ return 0;
+}
+
+
+#ifdef CONFIG_MESH
+
+static int wpa_driver_nl80211_init_mesh(void *priv)
+{
+ if (wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_MESH_POINT)) {
+ wpa_printf(MSG_INFO,
+ "nl80211: Failed to set interface into mesh mode");
+ return -1;
+ }
+ return 0;
+}
+
+
+static int nl80211_put_mesh_id(struct nl_msg *msg, const u8 *mesh_id,
+ size_t mesh_id_len)
+{
+ if (mesh_id) {
+ wpa_printf(MSG_DEBUG, " * Mesh ID (SSID)=%s",
+ wpa_ssid_txt(mesh_id, mesh_id_len));
+ return nla_put(msg, NL80211_ATTR_MESH_ID, mesh_id_len, mesh_id);
+ }
+
+ return 0;
+}
+
+
+static int nl80211_put_mcast_rate(struct nl_msg *msg, int mcast_rate)
+{
+ if (mcast_rate > 0) {
+ wpa_printf(MSG_DEBUG, " * mcast_rate=%.1f",
+ (double)mcast_rate / 10);
+ return nla_put_u32(msg, NL80211_ATTR_MCAST_RATE, mcast_rate);
+ }
+
+ return 0;
+}
+
+
+static int nl80211_put_mesh_config(struct nl_msg *msg,
+ struct wpa_driver_mesh_bss_params *params)
+{
+ struct nlattr *container;
+
+ container = nla_nest_start(msg, NL80211_ATTR_MESH_CONFIG);
+ if (!container)
+ return -1;
+
+ if (((params->flags & WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS) &&
+ nla_put_u8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
+ params->auto_plinks)) ||
+ ((params->flags & WPA_DRIVER_MESH_CONF_FLAG_FORWARDING) &&
+ nla_put_u8(msg, NL80211_MESHCONF_FORWARDING,
+ params->forwarding)) ||
+ ((params->flags & WPA_DRIVER_MESH_CONF_FLAG_MAX_PEER_LINKS) &&
+ nla_put_u16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
+ params->max_peer_links)) ||
+ ((params->flags & WPA_DRIVER_MESH_CONF_FLAG_RSSI_THRESHOLD) &&
+ nla_put_u32(msg, NL80211_MESHCONF_RSSI_THRESHOLD,
+ params->rssi_threshold)))
+ return -1;
+
+ /*
+ * Set NL80211_MESHCONF_PLINK_TIMEOUT even if user mpm is used because
+ * the timer could disconnect stations even in that case.
+ */
+ if ((params->flags & WPA_DRIVER_MESH_CONF_FLAG_PEER_LINK_TIMEOUT) &&
+ nla_put_u32(msg, NL80211_MESHCONF_PLINK_TIMEOUT,
+ params->peer_link_timeout)) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to set PLINK_TIMEOUT");
+ return -1;
+ }
+
+ if ((params->flags & WPA_DRIVER_MESH_CONF_FLAG_HT_OP_MODE) &&
+ nla_put_u16(msg, NL80211_MESHCONF_HT_OPMODE, params->ht_opmode)) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to set HT_OP_MODE");
+ return -1;
+ }
+
+ nla_nest_end(msg, container);
+
+ return 0;
+}
+
+
+static int nl80211_join_mesh(struct i802_bss *bss,
+ struct wpa_driver_mesh_join_params *params)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ struct nlattr *container;
+ int ret = -1;
+
+ wpa_printf(MSG_DEBUG, "nl80211: mesh join (ifindex=%d)", drv->ifindex);
+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_JOIN_MESH);
+ if (!msg ||
+ nl80211_put_freq_params(msg, ¶ms->freq) ||
+ nl80211_put_basic_rates(msg, params->basic_rates) ||
+ nl80211_put_mesh_id(msg, params->meshid, params->meshid_len) ||
+ nl80211_put_beacon_int(msg, params->beacon_int) ||
+ nl80211_put_mcast_rate(msg, params->mcast_rate) ||
+ nl80211_put_dtim_period(msg, params->dtim_period))
+ goto fail;
+
+ wpa_printf(MSG_DEBUG, " * flags=%08X", params->flags);
+
+ if (params->handle_dfs && nla_put_flag(msg, NL80211_ATTR_HANDLE_DFS))
+ goto fail;
+
+ container = nla_nest_start(msg, NL80211_ATTR_MESH_SETUP);
+ if (!container)
+ goto fail;
+
+ if (params->ies) {
+ wpa_hexdump(MSG_DEBUG, " * IEs", params->ies, params->ie_len);
+ if (nla_put(msg, NL80211_MESH_SETUP_IE, params->ie_len,
+ params->ies))
+ goto fail;
+ }
+ /* WPA_DRIVER_MESH_FLAG_OPEN_AUTH is treated as default by nl80211 */
+ if (params->flags & WPA_DRIVER_MESH_FLAG_SAE_AUTH) {
+ if (nla_put_u8(msg, NL80211_MESH_SETUP_AUTH_PROTOCOL, 0x1) ||
+ nla_put_flag(msg, NL80211_MESH_SETUP_USERSPACE_AUTH))
+ goto fail;
+ }
+ if ((params->flags & WPA_DRIVER_MESH_FLAG_AMPE) &&
+ nla_put_flag(msg, NL80211_MESH_SETUP_USERSPACE_AMPE))
+ goto fail;
+ if ((params->flags & WPA_DRIVER_MESH_FLAG_USER_MPM) &&
+ nla_put_flag(msg, NL80211_MESH_SETUP_USERSPACE_MPM))
+ goto fail;
+ nla_nest_end(msg, container);
+
+ params->conf.flags |= WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS;
+ params->conf.flags |= WPA_DRIVER_MESH_CONF_FLAG_PEER_LINK_TIMEOUT;
+ params->conf.flags |= WPA_DRIVER_MESH_CONF_FLAG_MAX_PEER_LINKS;
+ if (nl80211_put_mesh_config(msg, ¶ms->conf) < 0)
+ goto fail;
+
+ ret = send_and_recv_msgs_connect_handle(drv, msg, bss, 1);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: mesh join failed: ret=%d (%s)",
+ ret, strerror(-ret));
+ goto fail;
+ }
+ ret = 0;
+ drv->assoc_freq = bss->flink->freq = params->freq.freq;
+ wpa_printf(MSG_DEBUG, "nl80211: mesh join request send successfully");
+
+fail:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+static int
+wpa_driver_nl80211_join_mesh(void *priv,
+ struct wpa_driver_mesh_join_params *params)
+{
+ struct i802_bss *bss = priv;
+ int ret, timeout;
+
+ timeout = params->conf.peer_link_timeout;
+
+ /* Disable kernel inactivity timer */
+ if (params->flags & WPA_DRIVER_MESH_FLAG_USER_MPM)
+ params->conf.peer_link_timeout = 0;
+
+ ret = nl80211_join_mesh(bss, params);
+ if (ret == -EINVAL && params->conf.peer_link_timeout == 0) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Mesh join retry for peer_link_timeout");
+ /*
+ * Old kernel does not support setting
+ * NL80211_MESHCONF_PLINK_TIMEOUT to zero, so set 60 seconds
+ * into future from peer_link_timeout.
+ */
+ params->conf.peer_link_timeout = timeout + 60;
+ ret = nl80211_join_mesh(priv, params);
+ }
+
+ params->conf.peer_link_timeout = timeout;
+ return ret;
+}
+
+
+static int wpa_driver_nl80211_leave_mesh(void *priv)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "nl80211: mesh leave (ifindex=%d)", drv->ifindex);
+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_LEAVE_MESH);
+ ret = send_and_recv_msgs_connect_handle(drv, msg, bss, 0);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: mesh leave failed: ret=%d (%s)",
+ ret, strerror(-ret));
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: mesh leave request send successfully");
+ drv->first_bss->flink->freq = 0;
+ }
+
+ if (drv->start_mode_sta &&
+ wpa_driver_nl80211_set_mode(drv->first_bss,
+ NL80211_IFTYPE_STATION)) {
+ wpa_printf(MSG_INFO,
+ "nl80211: Failed to set interface into station mode");
+ }
+ return ret;
+}
+
+
+static int nl80211_probe_mesh_link(void *priv, const u8 *addr, const u8 *eth,
+ size_t len)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret;
+
+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_PROBE_MESH_LINK);
+ if (!msg ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
+ nla_put(msg, NL80211_ATTR_FRAME, len, eth)) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: mesh link probe to " MACSTR
+ " failed: ret=%d (%s)",
+ MAC2STR(addr), ret, strerror(-ret));
+ } else {
+ wpa_printf(MSG_DEBUG, "nl80211: Mesh link to " MACSTR
+ " probed successfully", MAC2STR(addr));
+ }
+
+ return ret;
+}
+
+#endif /* CONFIG_MESH */
+
+
+static int wpa_driver_br_add_ip_neigh(void *priv, u8 version,
+ const u8 *ipaddr, int prefixlen,
+ const u8 *addr)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct ndmsg nhdr = {
+ .ndm_state = NUD_PERMANENT,
+ .ndm_ifindex = bss->br_ifindex,
+ };
+ struct nl_msg *msg;
+ int addrsize;
+ int res;
+
+ if (!ipaddr || prefixlen == 0 || !addr)
+ return -EINVAL;
+
+ if (bss->br_ifindex == 0) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: bridge must be set before adding an ip neigh to it");
+ return -1;
+ }
+
+ if (!drv->rtnl_sk) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: nl_sock for NETLINK_ROUTE is not initialized");
+ return -1;
+ }
+
+ if (version == 4) {
+ nhdr.ndm_family = AF_INET;
+ addrsize = 4;
+ } else if (version == 6) {
+ nhdr.ndm_family = AF_INET6;
+ addrsize = 16;
+ } else {
+ return -EINVAL;
+ }
+
+ msg = nlmsg_alloc_simple(RTM_NEWNEIGH, NLM_F_CREATE);
+ if (!msg)
+ return -ENOMEM;
+
+ res = -ENOMEM;
+ if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0)
+ goto errout;
+
+ if (nla_put(msg, NDA_DST, addrsize, (void *)ipaddr))
+ goto errout;
+
+ if (nla_put(msg, NDA_LLADDR, ETH_ALEN, (void *)addr))
+ goto errout;
+
+ res = nl_send_auto_complete(drv->rtnl_sk, msg);
+ if (res < 0)
+ goto errout;
+
+ res = nl_wait_for_ack(drv->rtnl_sk);
+ if (res) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Adding bridge ip neigh failed: %s",
+ nl_geterror(res));
+ }
+errout:
+ nlmsg_free(msg);
+ return res;
+}
+
+
+static int wpa_driver_br_delete_ip_neigh(void *priv, u8 version,
+ const u8 *ipaddr)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct ndmsg nhdr = {
+ .ndm_state = NUD_PERMANENT,
+ .ndm_ifindex = bss->br_ifindex,
+ };
+ struct nl_msg *msg;
+ int addrsize;
+ int res;
+
+ if (!ipaddr)
+ return -EINVAL;
+
+ if (version == 4) {
+ nhdr.ndm_family = AF_INET;
+ addrsize = 4;
+ } else if (version == 6) {
+ nhdr.ndm_family = AF_INET6;
+ addrsize = 16;
+ } else {
+ return -EINVAL;
+ }
+
+ if (bss->br_ifindex == 0) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: bridge must be set to delete an ip neigh");
+ return -1;
+ }
+
+ if (!drv->rtnl_sk) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: nl_sock for NETLINK_ROUTE is not initialized");
+ return -1;
+ }
+
+ msg = nlmsg_alloc_simple(RTM_DELNEIGH, NLM_F_CREATE);
+ if (!msg)
+ return -ENOMEM;
+
+ res = -ENOMEM;
+ if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0)
+ goto errout;
+
+ if (nla_put(msg, NDA_DST, addrsize, (void *)ipaddr))
+ goto errout;
+
+ res = nl_send_auto_complete(drv->rtnl_sk, msg);
+ if (res < 0)
+ goto errout;
+
+ res = nl_wait_for_ack(drv->rtnl_sk);
+ if (res) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Deleting bridge ip neigh failed: %s",
+ nl_geterror(res));
+ }
+errout:
+ nlmsg_free(msg);
+ return res;
+}
+
+
+static int linux_write_system_file(const char *path, unsigned int val)
+{
+ char buf[50];
+ int fd, len;
+
+ len = os_snprintf(buf, sizeof(buf), "%u\n", val);
+ if (os_snprintf_error(sizeof(buf), len))
+ return -1;
+
+ fd = open(path, O_WRONLY);
+ if (fd < 0)
+ return -1;
+
+ if (write(fd, buf, len) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Failed to write Linux system file: %s with the value of %d",
+ path, val);
+ close(fd);
+ return -1;
+ }
+ close(fd);
+
+ return 0;
+}
+
+
+static const char * drv_br_port_attr_str(enum drv_br_port_attr attr)
+{
+ switch (attr) {
+ case DRV_BR_PORT_ATTR_PROXYARP:
+ return "proxyarp_wifi";
+ case DRV_BR_PORT_ATTR_HAIRPIN_MODE:
+ return "hairpin_mode";
+ case DRV_BR_PORT_ATTR_MCAST2UCAST:
+ return "multicast_to_unicast";
+ }
+
+ return NULL;
+}
+
+
+static int wpa_driver_br_port_set_attr(void *priv, enum drv_br_port_attr attr,
+ unsigned int val)
+{
+ struct i802_bss *bss = priv;
+ char path[128];
+ const char *attr_txt;
+
+ attr_txt = drv_br_port_attr_str(attr);
+ if (attr_txt == NULL)
+ return -EINVAL;
+
+ os_snprintf(path, sizeof(path), "/sys/class/net/%s/brport/%s",
+ bss->ifname, attr_txt);
+
+ if (linux_write_system_file(path, val))
+ return -1;
+
+ return 0;
+}
+
+
+static const char * drv_br_net_param_str(enum drv_br_net_param param)
+{
+ switch (param) {
+ case DRV_BR_NET_PARAM_GARP_ACCEPT:
+ return "arp_accept";
+ default:
+ return NULL;
+ }
+}
+
+
+static int wpa_driver_br_set_net_param(void *priv, enum drv_br_net_param param,
+ unsigned int val)
+{
+ struct i802_bss *bss = priv;
+ char path[128];
+ const char *param_txt;
+ int ip_version = 4;
+
+ if (param == DRV_BR_MULTICAST_SNOOPING) {
+ os_snprintf(path, sizeof(path),
+ "/sys/devices/virtual/net/%s/bridge/multicast_snooping",
+ bss->brname);
+ goto set_val;
+ }
+
+ param_txt = drv_br_net_param_str(param);
+ if (param_txt == NULL)
+ return -EINVAL;
+
+ switch (param) {
+ case DRV_BR_NET_PARAM_GARP_ACCEPT:
+ ip_version = 4;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ os_snprintf(path, sizeof(path), "/proc/sys/net/ipv%d/conf/%s/%s",
+ ip_version, bss->brname, param_txt);
+
+set_val:
+ if (linux_write_system_file(path, val))
+ return -1;
+
+ return 0;
+}
+
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+
+static int hw_mode_to_qca_acs(enum hostapd_hw_mode hw_mode)
+{
+ switch (hw_mode) {
+ case HOSTAPD_MODE_IEEE80211B:
+ return QCA_ACS_MODE_IEEE80211B;
+ case HOSTAPD_MODE_IEEE80211G:
+ return QCA_ACS_MODE_IEEE80211G;
+ case HOSTAPD_MODE_IEEE80211A:
+ return QCA_ACS_MODE_IEEE80211A;
+ case HOSTAPD_MODE_IEEE80211AD:
+ return QCA_ACS_MODE_IEEE80211AD;
+ case HOSTAPD_MODE_IEEE80211ANY:
+ return QCA_ACS_MODE_IEEE80211ANY;
+ default:
+ return -1;
+ }
+}
+
+
+static int add_acs_ch_list(struct nl_msg *msg, const int *freq_list)
+{
+ int num_channels = 0, num_freqs;
+ u8 *ch_list;
+ enum hostapd_hw_mode hw_mode;
+ int ret = 0;
+ int i;
+
+ if (!freq_list)
+ return 0;
+
+ num_freqs = int_array_len(freq_list);
+ ch_list = os_malloc(sizeof(u8) * num_freqs);
+ if (!ch_list)
+ return -1;
+
+ for (i = 0; i < num_freqs; i++) {
+ const int freq = freq_list[i];
+
+ if (freq == 0)
+ break;
+ /* Send 2.4 GHz and 5 GHz channels with
+ * QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST to maintain backwards
+ * compatibility.
+ */
+ if (!(freq >= 2412 && freq <= 2484) &&
+ !(freq >= 5180 && freq <= 5900) &&
+ !(freq >= 5945 && freq <= 7115))
+ continue;
+ hw_mode = ieee80211_freq_to_chan(freq, &ch_list[num_channels]);
+ if (hw_mode != NUM_HOSTAPD_MODES)
+ num_channels++;
+ }
+
+ if (num_channels)
+ ret = nla_put(msg, QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST,
+ num_channels, ch_list);
+
+ os_free(ch_list);
+ return ret;
+}
+
+
+static int add_acs_freq_list(struct nl_msg *msg, const int *freq_list)
+{
+ int i, len, ret;
+ u32 *freqs;
+
+ if (!freq_list)
+ return 0;
+ len = int_array_len(freq_list);
+ freqs = os_malloc(sizeof(u32) * len);
+ if (!freqs)
+ return -1;
+ for (i = 0; i < len; i++)
+ freqs[i] = freq_list[i];
+ ret = nla_put(msg, QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST,
+ sizeof(u32) * len, freqs);
+ os_free(freqs);
+ return ret;
+}
+
+
+static int nl80211_qca_do_acs(struct wpa_driver_nl80211_data *drv,
+ struct drv_acs_params *params)
+{
+ struct nl_msg *msg;
+ struct nlattr *data;
+ int ret;
+ int mode;
+
+ mode = hw_mode_to_qca_acs(params->hw_mode);
+ if (mode < 0)
+ return -1;
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_DO_ACS) ||
+ !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+ nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE, mode) ||
+ (params->ht_enabled &&
+ nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED)) ||
+ (params->ht40_enabled &&
+ nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED)) ||
+ (params->vht_enabled &&
+ nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_VHT_ENABLED)) ||
+ (params->eht_enabled &&
+ nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_EHT_ENABLED)) ||
+ nla_put_u16(msg, QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH,
+ params->ch_width) ||
+ add_acs_ch_list(msg, params->freq_list) ||
+ add_acs_freq_list(msg, params->freq_list) ||
+ (params->edmg_enabled &&
+ nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_EDMG_ENABLED))) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+ nla_nest_end(msg, data);
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: ACS Params: HW_MODE: %d HT: %d HT40: %d VHT: %d EHT: %d BW: %d EDMG: %d",
+ params->hw_mode, params->ht_enabled, params->ht40_enabled,
+ params->vht_enabled, params->eht_enabled, params->ch_width,
+ params->edmg_enabled);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Failed to invoke driver ACS function: %s",
+ strerror(-ret));
+ }
+ return ret;
+}
+
+
+static int nl80211_set_band(void *priv, u32 band_mask)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ struct nlattr *data;
+ int ret;
+ enum qca_set_band qca_band_value;
+ u32 qca_band_mask = QCA_SETBAND_AUTO;
+
+ if (!drv->setband_vendor_cmd_avail ||
+ (band_mask > (WPA_SETBAND_2G | WPA_SETBAND_5G | WPA_SETBAND_6G)))
+ return -1;
+
+ if (band_mask & WPA_SETBAND_5G)
+ qca_band_mask |= QCA_SETBAND_5G;
+ if (band_mask & WPA_SETBAND_2G)
+ qca_band_mask |= QCA_SETBAND_2G;
+ if (band_mask & WPA_SETBAND_6G)
+ qca_band_mask |= QCA_SETBAND_6G;
+
+ /*
+ * QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE is a legacy interface hence make
+ * it suite to its values (AUTO/5G/2G) for backwards compatibility.
+ */
+ qca_band_value = ((qca_band_mask & QCA_SETBAND_5G) &&
+ (qca_band_mask & QCA_SETBAND_2G)) ?
+ QCA_SETBAND_AUTO :
+ qca_band_mask & ~QCA_SETBAND_6G;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: QCA_BAND_MASK = 0x%x, QCA_BAND_VALUE = %d",
+ qca_band_mask, qca_band_value);
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_SETBAND) ||
+ !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+ nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE,
+ qca_band_value) ||
+ nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_SETBAND_MASK,
+ qca_band_mask)) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+ nla_nest_end(msg, data);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Driver setband function failed: %s",
+ strerror(-ret));
+ }
+ return ret;
+}
+
+
+struct nl80211_pcl {
+ unsigned int num;
+ struct weighted_pcl *freq_list;
+};
+
+static void get_pcl_attr_values(struct weighted_pcl *wpcl, struct nlattr *nl[])
+{
+ if (nl[QCA_WLAN_VENDOR_ATTR_PCL_FREQ])
+ wpcl->freq = nla_get_u32(nl[QCA_WLAN_VENDOR_ATTR_PCL_FREQ]);
+ if (nl[QCA_WLAN_VENDOR_ATTR_PCL_WEIGHT])
+ wpcl->weight = nla_get_u8(nl[QCA_WLAN_VENDOR_ATTR_PCL_WEIGHT]);
+ if (nl[QCA_WLAN_VENDOR_ATTR_PCL_FLAG]) {
+ u32 flags = nla_get_u32(nl[QCA_WLAN_VENDOR_ATTR_PCL_FLAG]);
+
+ wpcl->flag = 0;
+ if (flags & BIT(0))
+ wpcl->flag |= WEIGHTED_PCL_GO;
+ if (flags & BIT(1))
+ wpcl->flag |= WEIGHTED_PCL_CLI;
+ if (flags & BIT(2))
+ wpcl->flag |= WEIGHTED_PCL_MUST_CONSIDER;
+ if (flags & BIT(3))
+ wpcl->flag |= WEIGHTED_PCL_EXCLUDE;
+ } else {
+ wpcl->flag = WEIGHTED_PCL_GO | WEIGHTED_PCL_CLI;
+ }
+}
+
+
+static int preferred_freq_info_handler(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct nl80211_pcl *param = arg;
+ struct nlattr *nl_vend, *attr;
+ enum qca_iface_type iface_type;
+ struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1];
+ struct nlattr *nl_pcl[QCA_WLAN_VENDOR_ATTR_PCL_MAX + 1];
+ unsigned int num, max_num;
+ u32 *freqs;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
+ if (!nl_vend)
+ return NL_SKIP;
+
+ nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX,
+ nla_data(nl_vend), nla_len(nl_vend), NULL);
+
+ attr = tb_vendor[
+ QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE];
+ if (!attr) {
+ wpa_printf(MSG_ERROR, "nl80211: iface_type couldn't be found");
+ param->num = 0;
+ return NL_SKIP;
+ }
+
+ iface_type = (enum qca_iface_type) nla_get_u32(attr);
+ wpa_printf(MSG_DEBUG, "nl80211: Driver returned iface_type=%d",
+ iface_type);
+
+ attr = tb_vendor[
+ QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_WEIGHED_PCL];
+ if (attr) {
+ int rem;
+ struct nlattr *wpcl = attr;
+ unsigned int i;
+
+ num = 0;
+ nla_for_each_nested(attr, wpcl, rem) {
+ if (num == param->num)
+ break; /* not enough room for all entries */
+ if (nla_parse(nl_pcl, QCA_WLAN_VENDOR_ATTR_PCL_MAX,
+ nla_data(attr), nla_len(attr), NULL)) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: Failed to parse PCL info");
+ param->num = 0;
+ return NL_SKIP;
+ }
+ get_pcl_attr_values(¶m->freq_list[num], nl_pcl);
+ num++;
+ }
+ param->num = num;
+
+ /* Sort frequencies based on their weight */
+ for (i = 0; i < num; i++) {
+ unsigned int j;
+
+ for (j = i + 1; j < num; j++) {
+ if (param->freq_list[i].weight <
+ param->freq_list[j].weight) {
+ struct weighted_pcl tmp;
+
+ tmp = param->freq_list[i];
+ param->freq_list[i] =
+ param->freq_list[j];
+ param->freq_list[j] = tmp;
+ }
+ }
+ }
+ } else if (tb_vendor[QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST]) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Driver does not provide weighted PCL; use the non-weighted variant");
+ attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST];
+ /*
+ * param->num has the maximum number of entries for which there
+ * is room in the freq_list provided by the caller.
+ */
+ freqs = nla_data(attr);
+ max_num = nla_len(attr) / sizeof(u32);
+ if (max_num > param->num)
+ max_num = param->num;
+ for (num = 0; num < max_num; num++) {
+ param->freq_list[num].freq = freqs[num];
+ param->freq_list[num].flag =
+ WEIGHTED_PCL_GO | WEIGHTED_PCL_CLI;
+ }
+ param->num = num;
+ } else {
+ wpa_printf(MSG_ERROR,
+ "nl80211: preferred_freq_list couldn't be found");
+ param->num = 0;
+ return NL_SKIP;
+ }
+ return NL_SKIP;
+}
+
+
+static int nl80211_get_pref_freq_list(void *priv,
+ enum wpa_driver_if_type if_type,
+ unsigned int *num,
+ struct weighted_pcl *freq_list)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret;
+ unsigned int i;
+ struct nlattr *params;
+ struct nl80211_pcl param;
+ enum qca_iface_type iface_type;
+
+ if (!drv->get_pref_freq_list)
+ return -1;
+
+ switch (if_type) {
+ case WPA_IF_STATION:
+ iface_type = QCA_IFACE_TYPE_STA;
+ break;
+ case WPA_IF_AP_BSS:
+ iface_type = QCA_IFACE_TYPE_AP;
+ break;
+ case WPA_IF_P2P_GO:
+ iface_type = QCA_IFACE_TYPE_P2P_GO;
+ break;
+ case WPA_IF_P2P_CLIENT:
+ iface_type = QCA_IFACE_TYPE_P2P_CLIENT;
+ break;
+ case WPA_IF_IBSS:
+ iface_type = QCA_IFACE_TYPE_IBSS;
+ break;
+ case WPA_IF_TDLS:
+ iface_type = QCA_IFACE_TYPE_TDLS;
+ break;
+ default:
+ return -1;
+ }
+
+ param.num = *num;
+ param.freq_list = freq_list;
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_IFINDEX, drv->ifindex) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST) ||
+ !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+ nla_put_u32(msg,
+ QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE,
+ iface_type)) {
+ wpa_printf(MSG_ERROR,
+ "%s: err in adding vendor_cmd and vendor_data",
+ __func__);
+ nlmsg_free(msg);
+ return -1;
+ }
+ nla_nest_end(msg, params);
+
+ if (freq_list)
+ os_memset(freq_list, 0, *num * sizeof(struct weighted_pcl));
+ ret = send_and_recv_msgs(drv, msg, preferred_freq_info_handler, ¶m,
+ NULL, NULL);
+ if (ret) {
+ wpa_printf(MSG_ERROR,
+ "%s: err in send_and_recv_msgs", __func__);
+ return ret;
+ }
+
+ *num = param.num;
+
+ for (i = 0; i < *num; i++) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: preferred_channel_list[%d]=%d[%d]:0x%x",
+ i, freq_list[i].freq, freq_list[i].weight,
+ freq_list[i].flag);
+ }
+
+ return 0;
+}
+
+
+static int nl80211_set_prob_oper_freq(void *priv, unsigned int freq)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret;
+ struct nlattr *params;
+
+ if (!drv->set_prob_oper_freq)
+ return -1;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Set P2P probable operating freq %u for ifindex %d",
+ freq, bss->ifindex);
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL) ||
+ !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+ nla_put_u32(msg,
+ QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_IFACE_TYPE,
+ QCA_IFACE_TYPE_P2P_CLIENT) ||
+ nla_put_u32(msg,
+ QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_FREQ,
+ freq)) {
+ wpa_printf(MSG_ERROR,
+ "%s: err in adding vendor_cmd and vendor_data",
+ __func__);
+ nlmsg_free(msg);
+ return -1;
+ }
+ nla_nest_end(msg, params);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_ERROR, "%s: err in send_and_recv_msgs",
+ __func__);
+ return ret;
+ }
+ nlmsg_free(msg);
+ return 0;
+}
+
+
+static int nl80211_p2p_lo_start(void *priv, unsigned int freq,
+ unsigned int period, unsigned int interval,
+ unsigned int count, const u8 *device_types,
+ size_t dev_types_len,
+ const u8 *ies, size_t ies_len)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ struct nlattr *container;
+ int ret;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Start P2P Listen offload: freq=%u, period=%u, interval=%u, count=%u",
+ freq, period, interval, count);
+
+ if (!(drv->capa.flags & WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD))
+ return -1;
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_START))
+ goto fail;
+
+ container = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+ if (!container)
+ goto fail;
+
+ if (nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_CHANNEL,
+ freq) ||
+ nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_PERIOD,
+ period) ||
+ nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_INTERVAL,
+ interval) ||
+ nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_COUNT,
+ count) ||
+ nla_put(msg, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_DEVICE_TYPES,
+ dev_types_len, device_types) ||
+ nla_put(msg, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_VENDOR_IE,
+ ies_len, ies))
+ goto fail;
+
+ nla_nest_end(msg, container);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Failed to send P2P Listen offload vendor command");
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ nlmsg_free(msg);
+ return -1;
+}
+
+
+static int nl80211_p2p_lo_stop(void *priv)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Stop P2P Listen offload");
+
+ if (!(drv->capa.flags & WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD))
+ return -1;
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP)) {
+ nlmsg_free(msg);
+ return -1;
+ }
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+}
+
+
+static int nl80211_set_tdls_mode(void *priv, int tdls_external_control)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ struct nlattr *params;
+ int ret;
+ u32 tdls_mode;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Set TDKS mode: tdls_external_control=%d",
+ tdls_external_control);
+
+ if (tdls_external_control == 1)
+ tdls_mode = QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_IMPLICIT |
+ QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXTERNAL;
+ else
+ tdls_mode = QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT;
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_CONFIGURE_TDLS))
+ goto fail;
+
+ params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+ if (!params)
+ goto fail;
+
+ if (nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TRIGGER_MODE,
+ tdls_mode))
+ goto fail;
+
+ nla_nest_end(msg, params);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: Set TDLS mode failed: ret=%d (%s)",
+ ret, strerror(-ret));
+ goto fail;
+ }
+ return 0;
+fail:
+ nlmsg_free(msg);
+ return -1;
+}
+
+
+#ifdef CONFIG_MBO
+
+static enum mbo_transition_reject_reason
+nl80211_mbo_reject_reason_mapping(enum qca_wlan_btm_candidate_status status)
+{
+ switch (status) {
+ case QCA_STATUS_REJECT_EXCESSIVE_FRAME_LOSS_EXPECTED:
+ return MBO_TRANSITION_REJECT_REASON_FRAME_LOSS;
+ case QCA_STATUS_REJECT_EXCESSIVE_DELAY_EXPECTED:
+ return MBO_TRANSITION_REJECT_REASON_DELAY;
+ case QCA_STATUS_REJECT_INSUFFICIENT_QOS_CAPACITY:
+ return MBO_TRANSITION_REJECT_REASON_QOS_CAPACITY;
+ case QCA_STATUS_REJECT_LOW_RSSI:
+ return MBO_TRANSITION_REJECT_REASON_RSSI;
+ case QCA_STATUS_REJECT_HIGH_INTERFERENCE:
+ return MBO_TRANSITION_REJECT_REASON_INTERFERENCE;
+ case QCA_STATUS_REJECT_UNKNOWN:
+ default:
+ return MBO_TRANSITION_REJECT_REASON_UNSPECIFIED;
+ }
+}
+
+
+static void nl80211_parse_btm_candidate_info(struct candidate_list *candidate,
+ struct nlattr *tb[], int num)
+{
+ enum qca_wlan_btm_candidate_status status;
+ char buf[50];
+
+ os_memcpy(candidate->bssid,
+ nla_data(tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID]),
+ ETH_ALEN);
+
+ status = nla_get_u32(
+ tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS]);
+ candidate->is_accept = status == QCA_STATUS_ACCEPT;
+ candidate->reject_reason = nl80211_mbo_reject_reason_mapping(status);
+
+ if (candidate->is_accept)
+ os_snprintf(buf, sizeof(buf), "Accepted");
+ else
+ os_snprintf(buf, sizeof(buf),
+ "Rejected, Reject_reason: %d",
+ candidate->reject_reason);
+ wpa_printf(MSG_DEBUG, "nl80211: BSSID[%d]: " MACSTR " %s",
+ num, MAC2STR(candidate->bssid), buf);
+}
+
+
+static int
+nl80211_get_bss_transition_status_handler(struct nl_msg *msg, void *arg)
+{
+ struct wpa_bss_candidate_info *info = arg;
+ struct candidate_list *candidate = info->candidates;
+ struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
+ struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1];
+ struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX + 1];
+ static struct nla_policy policy[
+ QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX + 1] = {
+ [QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID] = {
+ .minlen = ETH_ALEN
+ },
+ [QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS] = {
+ .type = NLA_U32,
+ },
+ };
+ struct nlattr *attr;
+ int rem;
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ u8 num;
+
+ num = info->num; /* number of candidates sent to driver */
+ info->num = 0;
+ nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ if (!tb_msg[NL80211_ATTR_VENDOR_DATA] ||
+ nla_parse_nested(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX,
+ tb_msg[NL80211_ATTR_VENDOR_DATA], NULL) ||
+ !tb_vendor[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO])
+ return NL_SKIP;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: WNM Candidate list received from driver");
+ nla_for_each_nested(attr,
+ tb_vendor[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO],
+ rem) {
+ if (info->num >= num ||
+ nla_parse_nested(
+ tb, QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX,
+ attr, policy) ||
+ !tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID] ||
+ !tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS])
+ break;
+
+ nl80211_parse_btm_candidate_info(candidate, tb, info->num);
+
+ candidate++;
+ info->num++;
+ }
+
+ return NL_SKIP;
+}
+
+
+static struct wpa_bss_candidate_info *
+nl80211_get_bss_transition_status(void *priv, struct wpa_bss_trans_info *params)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ struct nlattr *attr, *attr1, *attr2;
+ struct wpa_bss_candidate_info *info;
+ u8 i;
+ int ret;
+ u8 *pos;
+
+ if (!drv->fetch_bss_trans_status)
+ return NULL;
+
+ info = os_zalloc(sizeof(*info));
+ if (!info)
+ return NULL;
+ /* Allocate memory for number of candidates sent to driver */
+ info->candidates = os_calloc(params->n_candidates,
+ sizeof(*info->candidates));
+ if (!info->candidates) {
+ os_free(info);
+ return NULL;
+ }
+
+ /* Copy the number of candidates being sent to driver. This is used in
+ * nl80211_get_bss_transition_status_handler() to limit the number of
+ * candidates that can be populated in info->candidates and will be
+ * later overwritten with the actual number of candidates received from
+ * the driver.
+ */
+ info->num = params->n_candidates;
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS))
+ goto fail;
+
+ attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+ if (!attr)
+ goto fail;
+
+ if (nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON,
+ params->mbo_transition_reason))
+ goto fail;
+
+ attr1 = nla_nest_start(msg, QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO);
+ if (!attr1)
+ goto fail;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: WNM Candidate list info sending to driver: mbo_transition_reason: %d n_candidates: %d",
+ params->mbo_transition_reason, params->n_candidates);
+ pos = params->bssid;
+ for (i = 0; i < params->n_candidates; i++) {
+ wpa_printf(MSG_DEBUG, "nl80211: BSSID[%d]: " MACSTR, i,
+ MAC2STR(pos));
+ attr2 = nla_nest_start(msg, i);
+ if (!attr2 ||
+ nla_put(msg, QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID,
+ ETH_ALEN, pos))
+ goto fail;
+ pos += ETH_ALEN;
+ nla_nest_end(msg, attr2);
+ }
+
+ nla_nest_end(msg, attr1);
+ nla_nest_end(msg, attr);
+
+ ret = send_and_recv_msgs(drv, msg,
+ nl80211_get_bss_transition_status_handler,
+ info, NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: WNM Get BSS transition status failed: ret=%d (%s)",
+ ret, strerror(-ret));
+ goto fail;
+ }
+ return info;
+
+fail:
+ nlmsg_free(msg);
+ os_free(info->candidates);
+ os_free(info);
+ return NULL;
+}
+
+
+/**
+ * nl80211_ignore_assoc_disallow - Configure driver to ignore assoc_disallow
+ * @priv: Pointer to private driver data from wpa_driver_nl80211_init()
+ * @ignore_assoc_disallow: 0 to not ignore, 1 to ignore
+ * Returns: 0 on success, -1 on failure
+ */
+static int nl80211_ignore_assoc_disallow(void *priv, int ignore_disallow)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ struct nlattr *attr;
+ int ret = -1;
+
+ if (!drv->set_wifi_conf_vendor_cmd_avail)
+ return -1;
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION))
+ goto fail;
+
+ attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+ if (!attr)
+ goto fail;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Set ignore_assoc_disallow %d",
+ ignore_disallow);
+ if (nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_CONFIG_IGNORE_ASSOC_DISALLOWED,
+ ignore_disallow))
+ goto fail;
+
+ nla_nest_end(msg, attr);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: Set ignore_assoc_disallow failed: ret=%d (%s)",
+ ret, strerror(-ret));
+ goto fail;
+ }
+
+fail:
+ nlmsg_free(msg);
+ return ret;
+}
+
+#endif /* CONFIG_MBO */
+
+
+#ifdef CONFIG_PASN
+
+static int nl80211_send_pasn_resp(void *priv, struct pasn_auth *params)
+{
+ unsigned int i;
+ struct i802_bss *bss = priv;
+ struct nl_msg *msg = NULL;
+ struct nlattr *nlpeers, *attr, *attr1;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ wpa_dbg(drv->ctx, MSG_DEBUG,
+ "nl80211: PASN authentication response for %d entries",
+ params->num_peers);
+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
+ if (!msg ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_PASN))
+ goto fail;
+
+ attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+ if (!attr)
+ goto fail;
+
+ nlpeers = nla_nest_start(msg, QCA_WLAN_VENDOR_ATTR_PASN_PEERS);
+ if (!nlpeers)
+ goto fail;
+
+ for (i = 0; i < params->num_peers; i++) {
+ attr1 = nla_nest_start(msg, i);
+ if (!attr1 ||
+ nla_put(msg, QCA_WLAN_VENDOR_ATTR_PASN_PEER_SRC_ADDR,
+ ETH_ALEN, params->peer[i].own_addr) ||
+ nla_put(msg, QCA_WLAN_VENDOR_ATTR_PASN_PEER_MAC_ADDR,
+ ETH_ALEN, params->peer[i].peer_addr))
+ goto fail;
+
+ if (params->peer[i].status == 0 &&
+ nla_put_flag(msg,
+ QCA_WLAN_VENDOR_ATTR_PASN_PEER_STATUS_SUCCESS))
+ goto fail;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Own address[%u]: " MACSTR
+ " Peer address[%u]: " MACSTR " Status: %s",
+ i, MAC2STR(params->peer[i].own_addr), i,
+ MAC2STR(params->peer[i].peer_addr),
+ params->peer[i].status ? "Fail" : "Success");
+ nla_nest_end(msg, attr1);
+ }
+
+ nla_nest_end(msg, nlpeers);
+ nla_nest_end(msg, attr);
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+
+fail:
+ nlmsg_free(msg);
+ return -1;
+}
+
+
+static u32 wpa_ltf_keyseed_len_to_sha_type(size_t len)
+{
+ if (len == SHA384_MAC_LEN)
+ return QCA_WLAN_VENDOR_SHA_384;
+ if (len == SHA256_MAC_LEN)
+ return QCA_WLAN_VENDOR_SHA_256;
+
+ wpa_printf(MSG_ERROR, "nl80211: Unexpected LTF keyseed len %zu", len);
+ return (u32) -1;
+}
+
+
+static int nl80211_set_secure_ranging_ctx(void *priv,
+ struct secure_ranging_params *params)
+{
+ int ret;
+ u32 suite;
+ struct nlattr *attr;
+ struct nl_msg *msg = NULL;
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ /* Configure secure ranging context only to the drivers that support it.
+ */
+ if (!drv->secure_ranging_ctx_vendor_cmd_avail)
+ return 0;
+
+ if (!params->peer_addr || !params->own_addr)
+ return -1;
+
+ wpa_dbg(drv->ctx, MSG_DEBUG,
+ "nl80211: Secure ranging context for " MACSTR,
+ MAC2STR(params->peer_addr));
+
+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
+ if (!msg ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_SECURE_RANGING_CONTEXT))
+ goto fail;
+
+ attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+ if (!attr)
+ goto fail;
+
+ if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_PEER_MAC_ADDR,
+ ETH_ALEN, params->peer_addr) ||
+ nla_put(msg, QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_SRC_ADDR,
+ ETH_ALEN, params->own_addr) ||
+ nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_ACTION,
+ params->action))
+ goto fail;
+
+ if (params->cipher) {
+ suite = wpa_cipher_to_cipher_suite(params->cipher);
+ if (!suite ||
+ nla_put_u32(msg,
+ QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_CIPHER,
+ suite))
+ goto fail;
+ }
+
+ if (params->tk_len && params->tk) {
+ if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_TK,
+ params->tk_len, params->tk))
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG, "nl80211: TK",
+ params->tk, params->tk_len);
+ }
+
+ if (params->ltf_keyseed_len && params->ltf_keyseed) {
+ u32 sha_type = wpa_ltf_keyseed_len_to_sha_type(
+ params->ltf_keyseed_len);
+
+ if (sha_type == (u32) -1 ||
+ nla_put_u32(
+ msg,
+ QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_SHA_TYPE,
+ sha_type) ||
+ nla_put(msg,
+ QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_LTF_KEYSEED,
+ params->ltf_keyseed_len, params->ltf_keyseed))
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG, "nl80211: LTF keyseed",
+ params->ltf_keyseed, params->ltf_keyseed_len);
+ }
+ nla_nest_end(msg, attr);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ if (ret)
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Set secure ranging context failed: ret=%d (%s)",
+ ret, strerror(-ret));
+ return ret;
+fail:
+ nlmsg_free(msg);
+ return -1;
+}
+
+#endif /* CONFIG_PASN */
+
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+
+#ifdef CONFIG_DRIVER_NL80211_BRCM
+static int wpa_driver_do_broadcom_acs(struct wpa_driver_nl80211_data *drv,
+ struct drv_acs_params *params)
+{
+ struct nl_msg *msg;
+ struct nlattr *data;
+ int freq_list_len;
+ int ret = -1;
+
+ freq_list_len = int_array_len(params->freq_list);
+ wpa_printf(MSG_DEBUG, "%s: freq_list_len=%d",
+ __func__, freq_list_len);
+
+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
+ if (!msg ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_BRCM) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ BRCM_VENDOR_SCMD_ACS) ||
+ !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+ nla_put_u8(msg, BRCM_VENDOR_ATTR_ACS_HW_MODE, params->hw_mode) ||
+ nla_put_u8(msg, BRCM_VENDOR_ATTR_ACS_HT_ENABLED,
+ params->ht_enabled) ||
+ nla_put_u8(msg, BRCM_VENDOR_ATTR_ACS_HT40_ENABLED,
+ params->ht40_enabled) ||
+ nla_put_u8(msg, BRCM_VENDOR_ATTR_ACS_VHT_ENABLED,
+ params->vht_enabled) ||
+ nla_put_u16(msg, BRCM_VENDOR_ATTR_ACS_CHWIDTH, params->ch_width) ||
+ (freq_list_len > 0 &&
+ nla_put(msg, BRCM_VENDOR_ATTR_ACS_FREQ_LIST,
+ sizeof(int) * freq_list_len, params->freq_list)))
+ goto fail;
+ nla_nest_end(msg, data);
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: ACS Params: HW_MODE: %d HT: %d HT40: %d VHT: %d BW: %d",
+ params->hw_mode, params->ht_enabled, params->ht40_enabled,
+ params->vht_enabled, params->ch_width);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ if (ret) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: BRCM Failed to invoke driver ACS function: %s",
+ strerror(errno));
+ }
+
+ msg = NULL;
+fail:
+ nlmsg_free(msg);
+ return ret;
+}
+#endif /* CONFIG_DRIVER_NL80211_BRCM */
+
+
+static int nl80211_do_acs(void *priv, struct drv_acs_params *params)
+{
+#if defined(CONFIG_DRIVER_NL80211_QCA) || defined(CONFIG_DRIVER_NL80211_BRCM)
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+#endif /* CONFIG_DRIVER_NL80211_QCA || CONFIG_DRIVER_NL80211_BRCM */
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+ if (drv->qca_do_acs)
+ return nl80211_qca_do_acs(drv, params);
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+#ifdef CONFIG_DRIVER_NL80211_BRCM
+ if (drv->brcm_do_acs)
+ return wpa_driver_do_broadcom_acs(drv, params);
+#endif /* CONFIG_DRIVER_NL80211_BRCM */
+
+ return -1;
+}
+
+
+static int nl80211_write_to_file(const char *name, unsigned int val)
+{
+ int fd, len;
+ char tmp[128];
+ int ret = 0;
+
+ fd = open(name, O_RDWR);
+ if (fd < 0) {
+ int level;
+ /*
+ * Flags may not exist on older kernels, or while we're tearing
+ * down a disappearing device.
+ */
+ if (errno == ENOENT) {
+ ret = 0;
+ level = MSG_DEBUG;
+ } else {
+ ret = -1;
+ level = MSG_ERROR;
+ }
+ wpa_printf(level, "nl80211: Failed to open %s: %s",
+ name, strerror(errno));
+ return ret;
+ }
+
+ len = os_snprintf(tmp, sizeof(tmp), "%u\n", val);
+ len = write(fd, tmp, len);
+ if (len < 0) {
+ ret = -1;
+ wpa_printf(MSG_ERROR, "nl80211: Failed to write to %s: %s",
+ name, strerror(errno));
+ }
+ close(fd);
+
+ return ret;
+}
+
+
+static int nl80211_configure_data_frame_filters(void *priv, u32 filter_flags)
+{
+ struct i802_bss *bss = priv;
+ char path[128];
+ int ret;
+
+ /* P2P-Device has no netdev that can (or should) be configured here */
+ if (nl80211_get_ifmode(bss) == NL80211_IFTYPE_P2P_DEVICE)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Data frame filter flags=0x%x",
+ filter_flags);
+
+ /* Configure filtering of unicast frame encrypted using GTK */
+ ret = os_snprintf(path, sizeof(path),
+ "/proc/sys/net/ipv4/conf/%s/drop_unicast_in_l2_multicast",
+ bss->ifname);
+ if (os_snprintf_error(sizeof(path), ret))
+ return -1;
+
+ ret = nl80211_write_to_file(path,
+ !!(filter_flags &
+ WPA_DATA_FRAME_FILTER_FLAG_GTK));
+ if (ret) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: Failed to set IPv4 unicast in multicast filter");
+ return ret;
+ }
+
+ os_snprintf(path, sizeof(path),
+ "/proc/sys/net/ipv6/conf/%s/drop_unicast_in_l2_multicast",
+ bss->ifname);
+ ret = nl80211_write_to_file(path,
+ !!(filter_flags &
+ WPA_DATA_FRAME_FILTER_FLAG_GTK));
+
+ if (ret) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: Failed to set IPv6 unicast in multicast filter");
+ return ret;
+ }
+
+ /* Configure filtering of unicast frame encrypted using GTK */
+ os_snprintf(path, sizeof(path),
+ "/proc/sys/net/ipv4/conf/%s/drop_gratuitous_arp",
+ bss->ifname);
+ ret = nl80211_write_to_file(path,
+ !!(filter_flags &
+ WPA_DATA_FRAME_FILTER_FLAG_ARP));
+ if (ret) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: Failed set gratuitous ARP filter");
+ return ret;
+ }
+
+ /* Configure filtering of IPv6 NA frames */
+ os_snprintf(path, sizeof(path),
+ "/proc/sys/net/ipv6/conf/%s/drop_unsolicited_na",
+ bss->ifname);
+ ret = nl80211_write_to_file(path,
+ !!(filter_flags &
+ WPA_DATA_FRAME_FILTER_FLAG_NA));
+ if (ret) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: Failed to set unsolicited NA filter");
+ return ret;
+ }
+
+ return 0;
+}
+
+
+static int nl80211_get_ext_capab(void *priv, enum wpa_driver_if_type type,
+ const u8 **ext_capa, const u8 **ext_capa_mask,
+ unsigned int *ext_capa_len)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ enum nl80211_iftype nlmode;
+ unsigned int i;
+
+ if (!ext_capa || !ext_capa_mask || !ext_capa_len)
+ return -1;
+
+ nlmode = wpa_driver_nl80211_if_type(type);
+
+ /* By default, use the per-radio values */
+ *ext_capa = drv->extended_capa;
+ *ext_capa_mask = drv->extended_capa_mask;
+ *ext_capa_len = drv->extended_capa_len;
+
+ /* Replace the default value if a per-interface type value exists */
+ for (i = 0; i < drv->num_iface_capa; i++) {
+ if (nlmode == drv->iface_capa[i].iftype) {
+ *ext_capa = drv->iface_capa[i].ext_capa;
+ *ext_capa_mask = drv->iface_capa[i].ext_capa_mask;
+ *ext_capa_len = drv->iface_capa[i].ext_capa_len;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+
+static int nl80211_get_mld_capab(void *priv, enum wpa_driver_if_type type,
+ u16 *eml_capa, u16 *mld_capa_and_ops)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ enum nl80211_iftype nlmode;
+ unsigned int i;
+
+ if (!eml_capa || !mld_capa_and_ops)
+ return -1;
+
+ nlmode = wpa_driver_nl80211_if_type(type);
+
+ /* By default, set to zero */
+ *eml_capa = 0;
+ *mld_capa_and_ops = 0;
+
+ /* Replace the default value if a per-interface type value exists */
+ for (i = 0; i < drv->num_iface_capa; i++) {
+ if (nlmode == drv->iface_capa[i].iftype) {
+ *eml_capa = drv->iface_capa[i].eml_capa;
+ *mld_capa_and_ops =
+ drv->iface_capa[i].mld_capa_and_ops;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+
+static int nl80211_update_connection_params(
+ void *priv, struct wpa_driver_associate_params *params,
+ enum wpa_drv_update_connect_params_mask mask)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret = -1;
+ enum nl80211_auth_type type;
+
+ /* Update Connection Params is intended for drivers that implement
+ * internal SME and expect these updated connection params from
+ * wpa_supplicant. Do not send this request for the drivers using
+ * SME from wpa_supplicant.
+ */
+ if (drv->capa.flags & WPA_DRIVER_FLAGS_SME)
+ return 0;
+
+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_UPDATE_CONNECT_PARAMS);
+ if (!msg)
+ goto fail;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Update connection params (ifindex=%d)",
+ drv->ifindex);
+
+ if ((mask & WPA_DRV_UPDATE_ASSOC_IES) && params->wpa_ie) {
+ if (nla_put(msg, NL80211_ATTR_IE, params->wpa_ie_len,
+ params->wpa_ie))
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, " * IEs", params->wpa_ie,
+ params->wpa_ie_len);
+ }
+
+ if (mask & WPA_DRV_UPDATE_AUTH_TYPE) {
+ type = get_nl_auth_type(params->auth_alg);
+ if (type == NL80211_AUTHTYPE_MAX ||
+ nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type))
+ goto fail;
+ wpa_printf(MSG_DEBUG, " * Auth Type %d", type);
+ }
+
+ if ((mask & WPA_DRV_UPDATE_FILS_ERP_INFO) &&
+ nl80211_put_fils_connect_params(drv, params, msg))
+ goto fail;
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ msg = NULL;
+ if (ret)
+ wpa_dbg(drv->ctx, MSG_DEBUG,
+ "nl80211: Update connect params command failed: ret=%d (%s)",
+ ret, strerror(-ret));
+
+fail:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+static int nl80211_send_external_auth_status(void *priv,
+ struct external_auth *params)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg = NULL;
+ int ret = -1;
+
+ /* External auth command/status is intended for drivers that implement
+ * internal SME but want to offload authentication processing (e.g.,
+ * SAE) to hostapd/wpa_supplicant. Do not send the status to drivers
+ * which do not support AP SME or use wpa_supplicant/hostapd SME.
+ */
+ if ((is_ap_interface(drv->nlmode) && !bss->drv->device_ap_sme) ||
+ (drv->capa.flags & WPA_DRIVER_FLAGS_SME))
+ return -1;
+
+ wpa_dbg(drv->ctx, MSG_DEBUG,
+ "nl80211: External auth status: %u", params->status);
+
+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_EXTERNAL_AUTH);
+ if (!msg ||
+ nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, params->status) ||
+ (params->ssid && params->ssid_len &&
+ nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid)) ||
+ (params->pmkid &&
+ nla_put(msg, NL80211_ATTR_PMKID, PMKID_LEN, params->pmkid)) ||
+ (params->bssid &&
+ nla_put(msg, NL80211_ATTR_BSSID, ETH_ALEN, params->bssid)))
+ goto fail;
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: External Auth status update failed: ret=%d (%s)",
+ ret, strerror(-ret));
+ goto fail;
+ }
+fail:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+static int nl80211_set_4addr_mode(void *priv, const char *bridge_ifname,
+ int val)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret = -ENOBUFS;
+
+ wpa_printf(MSG_DEBUG, "nl80211: %s 4addr mode (bridge_ifname: %s)",
+ val ? "Enable" : "Disable", bridge_ifname);
+
+ msg = nl80211_cmd_msg(drv->first_bss, 0, NL80211_CMD_SET_INTERFACE);
+ if (!msg || nla_put_u8(msg, NL80211_ATTR_4ADDR, val))
+ goto fail;
+
+ if (bridge_ifname[0] && bss->added_if_into_bridge && !val) {
+ if (linux_br_del_if(drv->global->ioctl_sock,
+ bridge_ifname, bss->ifname)) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: Failed to remove interface %s from bridge %s",
+ bss->ifname, bridge_ifname);
+ return -1;
+ }
+ bss->added_if_into_bridge = 0;
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ msg = NULL;
+ if (ret && val && nl80211_get_4addr(bss) == 1) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: 4addr mode was already enabled");
+ ret = 0;
+ }
+ if (!ret) {
+ if (bridge_ifname[0] && val &&
+ i802_check_bridge(drv, bss, bridge_ifname, bss->ifname) < 0)
+ return -1;
+ return 0;
+ }
+
+fail:
+ nlmsg_free(msg);
+ wpa_printf(MSG_ERROR, "nl80211: Failed to enable/disable 4addr");
+
+ return ret;
+}
+
+
+#ifdef CONFIG_DPP
+static int nl80211_dpp_listen(void *priv, bool enable)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_ACTION << 4);
+ struct nl_sock *handle;
+
+ if (!drv->multicast_registrations || !bss->nl_mgmt)
+ return 0; /* cannot do more than hope broadcast RX works */
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Update DPP Public Action frame registration (%s multicast RX)",
+ enable ? "enable" : "disable");
+ handle = (void *) (((intptr_t) bss->nl_mgmt) ^ ELOOP_SOCKET_INVALID);
+ return nl80211_register_frame(bss, handle, type,
+ (u8 *) "\x04\x09\x50\x6f\x9a\x1a", 6,
+ enable);
+}
+#endif /* CONFIG_DPP */
+
+
+static int nl80211_link_add(void *priv, u8 link_id, const u8 *addr)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ unsigned int idx, i;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "nl80211: MLD: add link_id=%u, addr=" MACSTR,
+ link_id, MAC2STR(addr));
+
+ if (drv->nlmode != NL80211_IFTYPE_AP) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: MLD: cannot add link to iftype=%u",
+ drv->nlmode);
+ return -EINVAL;
+ }
+
+ if (bss->n_links >= MAX_NUM_MLD_LINKS) {
+ wpa_printf(MSG_DEBUG, "nl80211: MLD: already have n_links=%zu",
+ bss->n_links);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < bss->n_links; i++) {
+ if (bss->links[i].link_id == link_id &&
+ bss->links[i].beacon_set) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: MLD: link already set");
+ return -EINVAL;
+ }
+ }
+
+ /* try using the first link entry, assuming it is not beaconing yet */
+ if (bss->n_links == 1 &&
+ bss->flink->link_id == NL80211_DRV_LINK_ID_NA) {
+ if (bss->flink->beacon_set) {
+ wpa_printf(MSG_DEBUG, "nl80211: BSS already beaconing");
+ return -EINVAL;
+ }
+
+ idx = 0;
+ } else {
+ idx = bss->n_links;
+ }
+
+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_ADD_LINK);
+ if (!msg ||
+ nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: add link failed. ret=%d (%s)",
+ ret, strerror(-ret));
+ return ret;
+ }
+
+ bss->links[idx].link_id = link_id;
+ os_memcpy(bss->links[idx].addr, addr, ETH_ALEN);
+
+ bss->n_links = idx + 1;
+
+ wpa_printf(MSG_DEBUG, "nl80211: MLD: n_links=%zu", bss->n_links);
+ return 0;
+}
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+
+static int testing_nl80211_register_frame(void *priv, u16 type,
+ const u8 *match, size_t match_len,
+ bool multicast)
+{
+ struct i802_bss *bss = priv;
+ struct nl_sock *handle;
+
+ if (!bss->nl_mgmt)
+ return -1;
+ handle = (void *) (((intptr_t) bss->nl_mgmt) ^ ELOOP_SOCKET_INVALID);
+ return nl80211_register_frame(bss, handle, type, match, match_len,
+ multicast);
+}
+
+
+static int testing_nl80211_radio_disable(void *priv, int disabled)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ /* For now, this is supported only partially in station mode with
+ * SME-in-wpa_supplicant case where the NL80211_ATTR_LOCAL_STATE_CHANGE
+ * attribute can be used to avoid sending out the Deauthentication frame
+ * to the currently associated AP. */
+
+ if (!disabled)
+ return 0;
+
+ if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME))
+ return -1;
+
+ if (!drv->associated)
+ return 0;
+
+ return wpa_driver_nl80211_mlme(drv, drv->bssid,
+ NL80211_CMD_DEAUTHENTICATE,
+ WLAN_REASON_PREV_AUTH_NOT_VALID, 1,
+ drv->first_bss);
+}
+
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+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 = driver_nl80211_set_key,
+ .scan2 = driver_nl80211_scan2,
+ .sched_scan = wpa_driver_nl80211_sched_scan,
+ .stop_sched_scan = wpa_driver_nl80211_stop_sched_scan,
+ .get_scan_results2 = wpa_driver_nl80211_get_scan_results,
+ .abort_scan = wpa_driver_nl80211_abort_scan,
+ .deauthenticate = driver_nl80211_deauthenticate,
+ .authenticate = driver_nl80211_authenticate,
+ .associate = wpa_driver_nl80211_associate,
+ .global_init = nl80211_global_init,
+ .global_deinit = nl80211_global_deinit,
+ .init2 = wpa_driver_nl80211_init,
+ .deinit = 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,
+ .get_country = wpa_driver_nl80211_get_country,
+ .set_ap = wpa_driver_nl80211_set_ap,
+ .set_acl = wpa_driver_nl80211_set_acl,
+ .if_add = wpa_driver_nl80211_if_add,
+ .if_remove = driver_nl80211_if_remove,
+ .send_mlme = driver_nl80211_send_mlme,
+ .get_hw_feature_data = nl80211_get_hw_feature_data,
+ .sta_add = wpa_driver_nl80211_sta_add,
+ .sta_remove = driver_nl80211_sta_remove,
+ .tx_control_port = nl80211_tx_control_port,
+ .hapd_send_eapol = wpa_driver_nl80211_hapd_send_eapol,
+ .sta_set_flags = wpa_driver_nl80211_sta_set_flags,
+ .sta_set_airtime_weight = driver_nl80211_sta_set_airtime_weight,
+ .hapd_init = i802_init,
+ .hapd_deinit = i802_deinit,
+ .set_wds_sta = i802_set_wds_sta,
+ .get_seqnum = i802_get_seqnum,
+ .flush = i802_flush,
+ .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_tx_queue_params = i802_set_tx_queue_params,
+ .set_sta_vlan = driver_nl80211_set_sta_vlan,
+ .sta_deauth = i802_sta_deauth,
+ .sta_disassoc = i802_sta_disassoc,
+ .read_sta_data = driver_nl80211_read_sta_data,
+ .set_freq = i802_set_freq,
+ .send_action = 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 = driver_nl80211_probe_req_report,
+ .deinit_ap = wpa_driver_nl80211_deinit_ap,
+ .deinit_p2p_cli = wpa_driver_nl80211_deinit_p2p_cli,
+ .resume = wpa_driver_nl80211_resume,
+ .signal_monitor = nl80211_signal_monitor,
+ .signal_poll = nl80211_signal_poll,
+ .mlo_signal_poll = nl80211_mlo_signal_poll,
+ .channel_info = nl80211_channel_info,
+ .set_param = nl80211_set_param,
+ .get_radio_name = nl80211_get_radio_name,
+ .add_pmkid = nl80211_add_pmkid,
+ .remove_pmkid = nl80211_remove_pmkid,
+ .flush_pmkid = nl80211_flush_pmkid,
+ .set_rekey_info = nl80211_set_rekey_info,
+ .poll_client = nl80211_poll_client,
+ .set_p2p_powersave = nl80211_set_p2p_powersave,
+ .start_dfs_cac = nl80211_start_radar_detection,
+ .stop_ap = wpa_driver_nl80211_stop_ap,
+#ifdef CONFIG_TDLS
+ .send_tdls_mgmt = nl80211_send_tdls_mgmt,
+ .tdls_oper = nl80211_tdls_oper,
+ .tdls_enable_channel_switch = nl80211_tdls_enable_channel_switch,
+ .tdls_disable_channel_switch = nl80211_tdls_disable_channel_switch,
+#endif /* CONFIG_TDLS */
+ .update_ft_ies = wpa_driver_nl80211_update_ft_ies,
+ .update_dh_ie = nl80211_update_dh_ie,
+ .get_mac_addr = wpa_driver_nl80211_get_macaddr,
+ .get_survey = wpa_driver_nl80211_get_survey,
+ .status = wpa_driver_nl80211_status,
+ .switch_channel = nl80211_switch_channel,
+#ifdef CONFIG_IEEE80211AX
+ .switch_color = nl80211_switch_color,
+#endif /* CONFIG_IEEE80211AX */
+#ifdef ANDROID_P2P
+ .set_noa = wpa_driver_set_p2p_noa,
+ .get_noa = wpa_driver_get_p2p_noa,
+ .set_ap_wps_ie = wpa_driver_set_ap_wps_p2p_ie,
+#endif /* ANDROID_P2P */
+#ifdef ANDROID
+#ifndef ANDROID_LIB_STUB
+ .driver_cmd = wpa_driver_nl80211_driver_cmd,
+#endif /* !ANDROID_LIB_STUB */
+#endif /* ANDROID */
+ .vendor_cmd = nl80211_vendor_cmd,
+ .set_qos_map = nl80211_set_qos_map,
+ .get_wowlan = nl80211_get_wowlan,
+ .set_wowlan = nl80211_set_wowlan,
+ .set_mac_addr = nl80211_set_mac_addr,
+#ifdef CONFIG_MESH
+ .init_mesh = wpa_driver_nl80211_init_mesh,
+ .join_mesh = wpa_driver_nl80211_join_mesh,
+ .leave_mesh = wpa_driver_nl80211_leave_mesh,
+ .probe_mesh_link = nl80211_probe_mesh_link,
+#endif /* CONFIG_MESH */
+ .br_add_ip_neigh = wpa_driver_br_add_ip_neigh,
+ .br_delete_ip_neigh = wpa_driver_br_delete_ip_neigh,
+ .br_port_set_attr = wpa_driver_br_port_set_attr,
+ .br_set_net_param = wpa_driver_br_set_net_param,
+ .add_tx_ts = nl80211_add_ts,
+ .del_tx_ts = nl80211_del_ts,
+ .get_ifindex = nl80211_get_ifindex,
+#ifdef CONFIG_DRIVER_NL80211_QCA
+ .roaming = nl80211_roaming,
+ .disable_fils = nl80211_disable_fils,
+ .set_band = nl80211_set_band,
+ .get_pref_freq_list = nl80211_get_pref_freq_list,
+ .set_prob_oper_freq = nl80211_set_prob_oper_freq,
+ .p2p_lo_start = nl80211_p2p_lo_start,
+ .p2p_lo_stop = nl80211_p2p_lo_stop,
+ .set_default_scan_ies = nl80211_set_default_scan_ies,
+ .set_tdls_mode = nl80211_set_tdls_mode,
+#ifdef CONFIG_MBO
+ .get_bss_transition_status = nl80211_get_bss_transition_status,
+ .ignore_assoc_disallow = nl80211_ignore_assoc_disallow,
+#endif /* CONFIG_MBO */
+ .set_bssid_tmp_disallow = nl80211_set_bssid_tmp_disallow,
+ .add_sta_node = nl80211_add_sta_node,
+#ifdef CONFIG_PASN
+ .send_pasn_resp = nl80211_send_pasn_resp,
+ .set_secure_ranging_ctx = nl80211_set_secure_ranging_ctx,
+#endif /* CONFIG_PASN */
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+ .do_acs = nl80211_do_acs,
+ .configure_data_frame_filters = nl80211_configure_data_frame_filters,
+ .get_ext_capab = nl80211_get_ext_capab,
+ .get_mld_capab = nl80211_get_mld_capab,
+ .update_connect_params = nl80211_update_connection_params,
+ .send_external_auth_status = nl80211_send_external_auth_status,
+ .set_4addr_mode = nl80211_set_4addr_mode,
+#ifdef CONFIG_DPP
+ .dpp_listen = nl80211_dpp_listen,
+#endif /* CONFIG_DPP */
+ .get_sta_mlo_info = nl80211_get_sta_mlo_info,
+ .link_add = nl80211_link_add,
+#ifdef CONFIG_TESTING_OPTIONS
+ .register_frame = testing_nl80211_register_frame,
+ .radio_disable = testing_nl80211_radio_disable,
+#endif /* CONFIG_TESTING_OPTIONS */
+};
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_nl80211.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_nl80211.h
new file mode 100644
index 0000000..aee8c45
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_nl80211.h
@@ -0,0 +1,368 @@
+/*
+ * Driver interaction with Linux nl80211/cfg80211 - definitions
+ * Copyright (c) 2002-2014, 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 software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DRIVER_NL80211_H
+#define DRIVER_NL80211_H
+
+#include "nl80211_copy.h"
+#include "utils/list.h"
+#include "driver.h"
+
+#ifndef NL_CAPABILITY_VERSION_3_5_0
+#define nla_nest_start(msg, attrtype) \
+ nla_nest_start(msg, NLA_F_NESTED | (attrtype))
+#endif
+
+struct nl80211_global {
+ void *ctx;
+ struct dl_list interfaces;
+ int if_add_ifindex;
+ u64 if_add_wdevid;
+ int if_add_wdevid_set;
+ struct netlink_data *netlink;
+ struct nl_cb *nl_cb;
+ struct nl_sock *nl;
+ int nl80211_id;
+ int nlctrl_id;
+ int ioctl_sock; /* socket for ioctl() use */
+
+ struct nl_sock *nl_event;
+};
+
+struct nl80211_wiphy_data {
+ struct dl_list list;
+ struct dl_list bsss;
+ struct dl_list drvs;
+
+ struct nl_sock *nl_beacons;
+ struct nl_cb *nl_cb;
+
+ int wiphy_idx;
+};
+
+#define NL80211_DRV_LINK_ID_NA (-1)
+
+struct i802_link {
+ unsigned int beacon_set:1;
+
+ s8 link_id;
+ int freq;
+ int bandwidth;
+ u8 addr[ETH_ALEN];
+ void *ctx;
+};
+
+struct i802_bss {
+ struct wpa_driver_nl80211_data *drv;
+ struct i802_bss *next;
+
+ size_t n_links;
+ struct i802_link links[MAX_NUM_MLD_LINKS];
+ struct i802_link *flink;
+
+ int ifindex;
+ int br_ifindex;
+ u64 wdev_id;
+ char ifname[IFNAMSIZ + 1];
+ char brname[IFNAMSIZ];
+ unsigned int added_if_into_bridge:1;
+ unsigned int already_in_bridge:1;
+ unsigned int added_bridge:1;
+ unsigned int in_deinit:1;
+ unsigned int wdev_id_set:1;
+ unsigned int added_if:1;
+ unsigned int static_ap:1;
+ unsigned int use_nl_connect:1;
+
+ u8 addr[ETH_ALEN];
+ u8 prev_addr[ETH_ALEN];
+
+ int if_dynamic;
+
+ void *ctx;
+ struct nl_sock *nl_preq, *nl_mgmt, *nl_connect;
+ struct nl_cb *nl_cb;
+
+ struct nl80211_wiphy_data *wiphy_data;
+ struct dl_list wiphy_list;
+ u8 rand_addr[ETH_ALEN];
+};
+
+struct drv_nl80211_if_info {
+ int ifindex;
+ /* the AP/AP_VLAN iface that is in this bridge */
+ int reason;
+};
+
+struct wpa_driver_nl80211_data {
+ struct nl80211_global *global;
+ struct dl_list list;
+ struct dl_list wiphy_list;
+ char phyname[32];
+ unsigned int wiphy_idx;
+ u8 perm_addr[ETH_ALEN];
+ void *ctx;
+ int ifindex;
+ int if_removed;
+ int if_disabled;
+ int ignore_if_down_event;
+ struct rfkill_data *rfkill;
+ struct wpa_driver_capa capa;
+ u8 *extended_capa, *extended_capa_mask;
+ unsigned int extended_capa_len;
+ struct drv_nl80211_iface_capa {
+ enum nl80211_iftype iftype;
+ u8 *ext_capa, *ext_capa_mask;
+ unsigned int ext_capa_len;
+ u16 eml_capa;
+ u16 mld_capa_and_ops;
+ } iface_capa[NL80211_IFTYPE_MAX];
+ unsigned int num_iface_capa;
+
+ int has_capability;
+ int has_driver_key_mgmt;
+
+ int operstate;
+
+ int scan_complete_events;
+ enum scan_states {
+ NO_SCAN, SCAN_REQUESTED, SCAN_STARTED, SCAN_COMPLETED,
+ SCAN_ABORTED, SCHED_SCAN_STARTED, SCHED_SCAN_STOPPED,
+ SCHED_SCAN_RESULTS
+ } scan_state;
+
+ u8 auth_bssid[ETH_ALEN];
+ u8 auth_attempt_bssid[ETH_ALEN];
+ u8 bssid[ETH_ALEN];
+ u8 prev_bssid[ETH_ALEN];
+ int associated;
+ struct driver_sta_mlo_info sta_mlo_info;
+ u8 ssid[SSID_MAX_LEN];
+ size_t ssid_len;
+ enum nl80211_iftype nlmode;
+ enum nl80211_iftype ap_scan_as_station;
+ unsigned int assoc_freq;
+
+ int monitor_sock;
+ int monitor_ifidx;
+ int monitor_refcount;
+
+ unsigned int disabled_11b_rates:1;
+ unsigned int pending_remain_on_chan:1;
+ unsigned int in_interface_list:1;
+ unsigned int device_ap_sme:1;
+ unsigned int poll_command_supported:1;
+ unsigned int data_tx_status:1;
+ unsigned int scan_for_auth:1;
+ unsigned int retry_auth:1;
+ unsigned int use_monitor:1;
+ unsigned int ignore_next_local_disconnect:1;
+ unsigned int ignore_next_local_deauth:1;
+ unsigned int hostapd:1;
+ unsigned int start_mode_sta:1;
+ unsigned int start_iface_up:1;
+ unsigned int test_use_roc_tx:1;
+ unsigned int ignore_deauth_event:1;
+ unsigned int vendor_cmd_test_avail:1;
+ unsigned int roaming_vendor_cmd_avail:1;
+ unsigned int dfs_vendor_cmd_avail:1;
+ unsigned int have_low_prio_scan:1;
+ unsigned int force_connect_cmd:1;
+ unsigned int addr_changed:1;
+ unsigned int get_features_vendor_cmd_avail:1;
+ unsigned int set_rekey_offload:1;
+ unsigned int p2p_go_ctwindow_supported:1;
+ unsigned int setband_vendor_cmd_avail:1;
+ unsigned int get_pref_freq_list:1;
+ unsigned int set_prob_oper_freq:1;
+ unsigned int scan_vendor_cmd_avail:1;
+ unsigned int connect_reassoc:1;
+ unsigned int set_wifi_conf_vendor_cmd_avail:1;
+ unsigned int fetch_bss_trans_status:1;
+ unsigned int roam_vendor_cmd_avail:1;
+ unsigned int add_sta_node_vendor_cmd_avail:1;
+ unsigned int control_port_ap:1;
+ unsigned int multicast_registrations:1;
+ unsigned int no_rrm:1;
+ unsigned int get_sta_info_vendor_cmd_avail:1;
+ unsigned int fils_discovery:1;
+ unsigned int unsol_bcast_probe_resp:1;
+ unsigned int qca_do_acs:1;
+ unsigned int brcm_do_acs:1;
+ unsigned int uses_6ghz:1;
+ unsigned int secure_ranging_ctx_vendor_cmd_avail:1;
+ unsigned int puncturing:1;
+ unsigned int qca_ap_allowed_freqs:1;
+
+ u64 vendor_scan_cookie;
+ u64 remain_on_chan_cookie;
+ u64 send_frame_cookie;
+ int send_frame_link_id;
+#define MAX_SEND_FRAME_COOKIES 20
+ u64 send_frame_cookies[MAX_SEND_FRAME_COOKIES];
+ unsigned int num_send_frame_cookies;
+ u64 eapol_tx_cookie;
+ int eapol_tx_link_id;
+
+ unsigned int last_mgmt_freq;
+
+ struct wpa_driver_scan_filter *filter_ssids;
+ size_t num_filter_ssids;
+
+ struct i802_bss *first_bss;
+
+ int eapol_tx_sock;
+
+ int eapol_sock; /* socket for EAPOL frames */
+
+ struct nl_sock *rtnl_sk; /* nl_sock for NETLINK_ROUTE */
+
+ struct drv_nl80211_if_info default_if_indices[16];
+ struct drv_nl80211_if_info *if_indices;
+ int num_if_indices;
+
+ /* From failed authentication command */
+ int auth_freq;
+ u8 auth_bssid_[ETH_ALEN];
+ u8 auth_ssid[SSID_MAX_LEN];
+ size_t auth_ssid_len;
+ int auth_alg;
+ u8 *auth_ie;
+ size_t auth_ie_len;
+ u8 *auth_data;
+ size_t auth_data_len;
+ u8 auth_wep_key[4][16];
+ size_t auth_wep_key_len[4];
+ int auth_wep_tx_keyidx;
+ int auth_local_state_change;
+ int auth_p2p;
+ bool auth_mld;
+ u8 auth_mld_link_id;
+ u8 auth_ap_mld_addr[ETH_ALEN];
+
+ /*
+ * Tells whether the last scan issued from wpa_supplicant was a normal
+ * scan (NL80211_CMD_TRIGGER_SCAN) or a vendor scan
+ * (NL80211_CMD_VENDOR). 0 if no pending scan request.
+ */
+ int last_scan_cmd;
+#ifdef CONFIG_DRIVER_NL80211_QCA
+ bool roam_indication_done;
+ u8 *pending_roam_data;
+ size_t pending_roam_data_len;
+ u8 *pending_t2lm_data;
+ size_t pending_t2lm_data_len;
+ u8 *pending_link_reconfig_data;
+ size_t pending_link_reconfig_data_len;
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+};
+
+struct nl_msg;
+
+void * nl80211_cmd(struct wpa_driver_nl80211_data *drv,
+ struct nl_msg *msg, int flags, uint8_t cmd);
+struct nl_msg * nl80211_cmd_msg(struct i802_bss *bss, int flags, uint8_t cmd);
+struct nl_msg * nl80211_drv_msg(struct wpa_driver_nl80211_data *drv, int flags,
+ uint8_t cmd);
+struct nl_msg * nl80211_bss_msg(struct i802_bss *bss, int flags, uint8_t cmd);
+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,
+ int (*ack_handler_custom)(struct nl_msg *, void *),
+ void *ack_data);
+struct nl_sock * get_connect_handle(struct i802_bss *bss);
+int nl80211_create_iface(struct wpa_driver_nl80211_data *drv,
+ const char *ifname, enum nl80211_iftype iftype,
+ const u8 *addr, int wds,
+ int (*handler)(struct nl_msg *, void *),
+ void *arg, int use_existing);
+void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, int ifidx);
+unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv);
+int nl80211_get_assoc_ssid(struct wpa_driver_nl80211_data *drv, u8 *ssid);
+enum chan_width convert2width(int width);
+void nl80211_mark_disconnected(struct wpa_driver_nl80211_data *drv);
+struct i802_bss * get_bss_ifindex(struct wpa_driver_nl80211_data *drv,
+ int ifindex);
+int is_ap_interface(enum nl80211_iftype nlmode);
+int is_sta_interface(enum nl80211_iftype nlmode);
+int wpa_driver_nl80211_authenticate_retry(struct wpa_driver_nl80211_data *drv);
+int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv,
+ const u8 *bssid,
+ struct hostap_sta_driver_data *data);
+int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv,
+ struct wpa_signal_info *sig_change);
+int nl80211_get_wiphy_index(struct i802_bss *bss);
+int wpa_driver_nl80211_set_mode(struct i802_bss *bss,
+ enum nl80211_iftype nlmode);
+int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
+ const u8 *addr, int cmd, u16 reason_code,
+ int local_state_change,
+ struct i802_bss *bss);
+
+int nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv);
+void nl80211_remove_monitor_interface(struct wpa_driver_nl80211_data *drv);
+int nl80211_send_monitor(struct wpa_driver_nl80211_data *drv,
+ const void *data, size_t len,
+ int encrypt, int noack);
+
+int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv);
+struct hostapd_hw_modes *
+nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags,
+ u8 *dfs_domain);
+
+int process_global_event(struct nl_msg *msg, void *arg);
+int process_bss_event(struct nl_msg *msg, void *arg);
+
+const char * nl80211_iftype_str(enum nl80211_iftype mode);
+
+void nl80211_restore_ap_mode(struct i802_bss *bss);
+struct i802_link * nl80211_get_link(struct i802_bss *bss, s8 link_id);
+
+#ifdef ANDROID
+int android_nl_socket_set_nonblocking(struct nl_sock *handle);
+int android_pno_start(struct i802_bss *bss,
+ struct wpa_driver_scan_params *params);
+int android_pno_stop(struct i802_bss *bss);
+extern int wpa_driver_nl80211_driver_cmd(void *priv, char *cmd, char *buf,
+ size_t buf_len);
+extern int wpa_driver_nl80211_driver_event(struct wpa_driver_nl80211_data *drv,
+ u32 vendor_id, u32 subcmd,
+ u8 *data, size_t len);
+
+
+#ifdef ANDROID_P2P
+int wpa_driver_set_p2p_noa(void *priv, u8 count, int start, int duration);
+int wpa_driver_get_p2p_noa(void *priv, u8 *buf, size_t len);
+int wpa_driver_set_p2p_ps(void *priv, int legacy_ps, int opp_ps, int ctwindow);
+int wpa_driver_set_ap_wps_p2p_ie(void *priv, const struct wpabuf *beacon,
+ const struct wpabuf *proberesp,
+ const struct wpabuf *assocresp);
+#endif /* ANDROID_P2P */
+#endif /* ANDROID */
+
+
+/* driver_nl80211_scan.c */
+
+void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx);
+int wpa_driver_nl80211_scan(struct i802_bss *bss,
+ struct wpa_driver_scan_params *params);
+int wpa_driver_nl80211_sched_scan(void *priv,
+ struct wpa_driver_scan_params *params);
+int wpa_driver_nl80211_stop_sched_scan(void *priv);
+struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv);
+void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv);
+int wpa_driver_nl80211_abort_scan(void *priv, u64 scan_cookie);
+int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss,
+ struct wpa_driver_scan_params *params);
+int nl80211_set_default_scan_ies(void *priv, const u8 *ies, size_t ies_len);
+
+#endif /* DRIVER_NL80211_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_nl80211_android.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_nl80211_android.c
new file mode 100644
index 0000000..9431a12
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_nl80211_android.c
@@ -0,0 +1,188 @@
+/*
+ * Driver interaction with Linux nl80211/cfg80211 - Android specific
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/family.h>
+#include <netlink/genl/ctrl.h>
+#include <fcntl.h>
+
+#include "utils/common.h"
+#include "driver_nl80211.h"
+#include "android_drv.h"
+
+
+typedef struct android_wifi_priv_cmd {
+ char *buf;
+ int used_len;
+ int total_len;
+} android_wifi_priv_cmd;
+
+static int drv_errors = 0;
+
+static void wpa_driver_send_hang_msg(struct wpa_driver_nl80211_data *drv)
+{
+ drv_errors++;
+ if (drv_errors > DRV_NUMBER_SEQUENTIAL_ERRORS) {
+ drv_errors = 0;
+ wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED");
+ }
+}
+
+
+static int android_priv_cmd(struct i802_bss *bss, const char *cmd)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct ifreq ifr;
+ android_wifi_priv_cmd priv_cmd;
+ char buf[MAX_DRV_CMD_SIZE];
+ int ret;
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_memset(&priv_cmd, 0, sizeof(priv_cmd));
+ os_strlcpy(ifr.ifr_name, bss->ifname, IFNAMSIZ);
+
+ os_memset(buf, 0, sizeof(buf));
+ os_strlcpy(buf, cmd, sizeof(buf));
+
+ priv_cmd.buf = buf;
+ priv_cmd.used_len = sizeof(buf);
+ priv_cmd.total_len = sizeof(buf);
+ ifr.ifr_data = &priv_cmd;
+
+ ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, "%s: failed to issue private commands",
+ __func__);
+ wpa_driver_send_hang_msg(drv);
+ return ret;
+ }
+
+ drv_errors = 0;
+ return 0;
+}
+
+
+int android_pno_start(struct i802_bss *bss,
+ struct wpa_driver_scan_params *params)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct ifreq ifr;
+ android_wifi_priv_cmd priv_cmd;
+ int ret = 0, i = 0, bp;
+ char buf[WEXT_PNO_MAX_COMMAND_SIZE];
+
+ bp = WEXT_PNOSETUP_HEADER_SIZE;
+ os_memcpy(buf, WEXT_PNOSETUP_HEADER, bp);
+ buf[bp++] = WEXT_PNO_TLV_PREFIX;
+ buf[bp++] = WEXT_PNO_TLV_VERSION;
+ buf[bp++] = WEXT_PNO_TLV_SUBVERSION;
+ buf[bp++] = WEXT_PNO_TLV_RESERVED;
+
+ while (i < WEXT_PNO_AMOUNT && (size_t) i < params->num_ssids) {
+ /* Check that there is enough space needed for 1 more SSID, the
+ * other sections and null termination */
+ if ((bp + WEXT_PNO_SSID_HEADER_SIZE + MAX_SSID_LEN +
+ WEXT_PNO_NONSSID_SECTIONS_SIZE + 1) >= (int) sizeof(buf))
+ break;
+ wpa_hexdump_ascii(MSG_DEBUG, "For PNO Scan",
+ params->ssids[i].ssid,
+ params->ssids[i].ssid_len);
+ buf[bp++] = WEXT_PNO_SSID_SECTION;
+ buf[bp++] = params->ssids[i].ssid_len;
+ os_memcpy(&buf[bp], params->ssids[i].ssid,
+ params->ssids[i].ssid_len);
+ bp += params->ssids[i].ssid_len;
+ i++;
+ }
+
+ buf[bp++] = WEXT_PNO_SCAN_INTERVAL_SECTION;
+ os_snprintf(&buf[bp], WEXT_PNO_SCAN_INTERVAL_LENGTH + 1, "%x",
+ WEXT_PNO_SCAN_INTERVAL);
+ bp += WEXT_PNO_SCAN_INTERVAL_LENGTH;
+
+ buf[bp++] = WEXT_PNO_REPEAT_SECTION;
+ os_snprintf(&buf[bp], WEXT_PNO_REPEAT_LENGTH + 1, "%x",
+ WEXT_PNO_REPEAT);
+ bp += WEXT_PNO_REPEAT_LENGTH;
+
+ buf[bp++] = WEXT_PNO_MAX_REPEAT_SECTION;
+ os_snprintf(&buf[bp], WEXT_PNO_MAX_REPEAT_LENGTH + 1, "%x",
+ WEXT_PNO_MAX_REPEAT);
+ bp += WEXT_PNO_MAX_REPEAT_LENGTH + 1;
+
+ memset(&ifr, 0, sizeof(ifr));
+ memset(&priv_cmd, 0, sizeof(priv_cmd));
+ os_strlcpy(ifr.ifr_name, bss->ifname, IFNAMSIZ);
+
+ priv_cmd.buf = buf;
+ priv_cmd.used_len = bp;
+ priv_cmd.total_len = bp;
+ ifr.ifr_data = &priv_cmd;
+
+ ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr);
+
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCSIWPRIV] (pnosetup): %d",
+ ret);
+ wpa_driver_send_hang_msg(drv);
+ return ret;
+ }
+
+ drv_errors = 0;
+
+ return android_priv_cmd(bss, "PNOFORCE 1");
+}
+
+
+int android_pno_stop(struct i802_bss *bss)
+{
+ return android_priv_cmd(bss, "PNOFORCE 0");
+}
+
+
+#ifdef ANDROID_P2P
+#ifdef ANDROID_LIB_STUB
+
+int wpa_driver_set_p2p_noa(void *priv, u8 count, int start, int duration)
+{
+ return 0;
+}
+
+
+int wpa_driver_get_p2p_noa(void *priv, u8 *buf, size_t len)
+{
+ return 0;
+}
+
+
+int wpa_driver_set_p2p_ps(void *priv, int legacy_ps, int opp_ps, int ctwindow)
+{
+ return -1;
+}
+
+
+int wpa_driver_set_ap_wps_p2p_ie(void *priv, const struct wpabuf *beacon,
+ const struct wpabuf *proberesp,
+ const struct wpabuf *assocresp)
+{
+ return 0;
+}
+
+#endif /* ANDROID_LIB_STUB */
+#endif /* ANDROID_P2P */
+
+
+int android_nl_socket_set_nonblocking(struct nl_sock *handle)
+{
+ return fcntl(nl_socket_get_fd(handle), F_SETFL, O_NONBLOCK);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_nl80211_capa.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_nl80211_capa.c
new file mode 100644
index 0000000..04393ef
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_nl80211_capa.c
@@ -0,0 +1,2646 @@
+/*
+ * Driver interaction with Linux nl80211/cfg80211 - Capabilities
+ * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <netlink/genl/genl.h>
+
+#include "utils/common.h"
+#include "common/ieee802_11_common.h"
+#include "common/wpa_common.h"
+#include "common/qca-vendor.h"
+#include "common/qca-vendor-attr.h"
+#include "common/brcm_vendor.h"
+#include "driver_nl80211.h"
+
+
+static int protocol_feature_handler(struct nl_msg *msg, void *arg)
+{
+ u32 *feat = arg;
+ struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+
+ nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ if (tb_msg[NL80211_ATTR_PROTOCOL_FEATURES])
+ *feat = nla_get_u32(tb_msg[NL80211_ATTR_PROTOCOL_FEATURES]);
+
+ return NL_SKIP;
+}
+
+
+static u32 get_nl80211_protocol_features(struct wpa_driver_nl80211_data *drv)
+{
+ u32 feat = 0;
+ struct nl_msg *msg;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return 0;
+
+ if (!nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_PROTOCOL_FEATURES)) {
+ nlmsg_free(msg);
+ return 0;
+ }
+
+ if (send_and_recv_msgs(drv, msg, protocol_feature_handler, &feat,
+ NULL, NULL) == 0)
+ return feat;
+
+ return 0;
+}
+
+
+struct wiphy_info_data {
+ struct wpa_driver_nl80211_data *drv;
+ struct wpa_driver_capa *capa;
+
+ unsigned int num_multichan_concurrent;
+
+ unsigned int error:1;
+ unsigned int device_ap_sme:1;
+ unsigned int poll_command_supported:1;
+ unsigned int data_tx_status:1;
+ unsigned int auth_supported:1;
+ unsigned int connect_supported:1;
+ unsigned int p2p_go_supported:1;
+ unsigned int p2p_client_supported:1;
+ unsigned int p2p_go_ctwindow_supported:1;
+ unsigned int p2p_concurrent:1;
+ unsigned int channel_switch_supported:1;
+ unsigned int set_qos_map_supported:1;
+ unsigned int have_low_prio_scan:1;
+ unsigned int wmm_ac_supported:1;
+ unsigned int mac_addr_rand_scan_supported:1;
+ unsigned int mac_addr_rand_sched_scan_supported:1;
+ unsigned int update_ft_ies_supported:1;
+ unsigned int has_key_mgmt:1;
+ unsigned int has_key_mgmt_iftype:1;
+};
+
+
+static unsigned int probe_resp_offload_support(int supp_protocols)
+{
+ unsigned int prot = 0;
+
+ if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS)
+ prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS;
+ if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2)
+ prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2;
+ if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P)
+ prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P;
+ if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U)
+ prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING;
+
+ return prot;
+}
+
+
+static void wiphy_info_supported_iftypes(struct wiphy_info_data *info,
+ struct nlattr *tb)
+{
+ struct nlattr *nl_mode;
+ int i;
+
+ if (tb == NULL)
+ return;
+
+ nla_for_each_nested(nl_mode, tb, i) {
+ switch (nla_type(nl_mode)) {
+ case NL80211_IFTYPE_AP:
+ info->capa->flags |= WPA_DRIVER_FLAGS_AP;
+ break;
+ case NL80211_IFTYPE_MESH_POINT:
+ info->capa->flags |= WPA_DRIVER_FLAGS_MESH;
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ info->capa->flags |= WPA_DRIVER_FLAGS_IBSS;
+ break;
+ case NL80211_IFTYPE_P2P_DEVICE:
+ info->capa->flags |=
+ WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE;
+ break;
+ case NL80211_IFTYPE_P2P_GO:
+ info->p2p_go_supported = 1;
+ break;
+ case NL80211_IFTYPE_P2P_CLIENT:
+ info->p2p_client_supported = 1;
+ break;
+ }
+ }
+}
+
+
+static int wiphy_info_iface_comb_process(struct wiphy_info_data *info,
+ struct nlattr *nl_combi)
+{
+ struct nlattr *tb_comb[NUM_NL80211_IFACE_COMB];
+ struct nlattr *tb_limit[NUM_NL80211_IFACE_LIMIT];
+ struct nlattr *nl_limit, *nl_mode;
+ int err, rem_limit, rem_mode;
+ int combination_has_p2p = 0, combination_has_mgd = 0;
+ static struct nla_policy
+ iface_combination_policy[NUM_NL80211_IFACE_COMB] = {
+ [NL80211_IFACE_COMB_LIMITS] = { .type = NLA_NESTED },
+ [NL80211_IFACE_COMB_MAXNUM] = { .type = NLA_U32 },
+ [NL80211_IFACE_COMB_STA_AP_BI_MATCH] = { .type = NLA_FLAG },
+ [NL80211_IFACE_COMB_NUM_CHANNELS] = { .type = NLA_U32 },
+ [NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS] = { .type = NLA_U32 },
+ },
+ iface_limit_policy[NUM_NL80211_IFACE_LIMIT] = {
+ [NL80211_IFACE_LIMIT_TYPES] = { .type = NLA_NESTED },
+ [NL80211_IFACE_LIMIT_MAX] = { .type = NLA_U32 },
+ };
+
+ err = nla_parse_nested(tb_comb, MAX_NL80211_IFACE_COMB,
+ nl_combi, iface_combination_policy);
+ if (err || !tb_comb[NL80211_IFACE_COMB_LIMITS] ||
+ !tb_comb[NL80211_IFACE_COMB_MAXNUM] ||
+ !tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS])
+ return 0; /* broken combination */
+
+ if (tb_comb[NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS])
+ info->capa->flags |= WPA_DRIVER_FLAGS_RADAR;
+
+ nla_for_each_nested(nl_limit, tb_comb[NL80211_IFACE_COMB_LIMITS],
+ rem_limit) {
+ err = nla_parse_nested(tb_limit, MAX_NL80211_IFACE_LIMIT,
+ nl_limit, iface_limit_policy);
+ if (err || !tb_limit[NL80211_IFACE_LIMIT_TYPES])
+ return 0; /* broken combination */
+
+ nla_for_each_nested(nl_mode,
+ tb_limit[NL80211_IFACE_LIMIT_TYPES],
+ rem_mode) {
+ int ift = nla_type(nl_mode);
+ if (ift == NL80211_IFTYPE_P2P_GO ||
+ ift == NL80211_IFTYPE_P2P_CLIENT)
+ combination_has_p2p = 1;
+ if (ift == NL80211_IFTYPE_STATION)
+ combination_has_mgd = 1;
+ }
+ if (combination_has_p2p && combination_has_mgd)
+ break;
+ }
+
+ if (combination_has_p2p && combination_has_mgd) {
+ unsigned int num_channels =
+ nla_get_u32(tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]);
+
+ info->p2p_concurrent = 1;
+ if (info->num_multichan_concurrent < num_channels)
+ info->num_multichan_concurrent = num_channels;
+ }
+
+ return 0;
+}
+
+
+static void wiphy_info_iface_comb(struct wiphy_info_data *info,
+ struct nlattr *tb)
+{
+ struct nlattr *nl_combi;
+ int rem_combi;
+
+ if (tb == NULL)
+ return;
+
+ nla_for_each_nested(nl_combi, tb, rem_combi) {
+ if (wiphy_info_iface_comb_process(info, nl_combi) > 0)
+ break;
+ }
+}
+
+
+static void wiphy_info_supp_cmds(struct wiphy_info_data *info,
+ struct nlattr *tb)
+{
+ struct nlattr *nl_cmd;
+ int i;
+
+ if (tb == NULL)
+ return;
+
+ nla_for_each_nested(nl_cmd, tb, i) {
+ switch (nla_get_u32(nl_cmd)) {
+ case NL80211_CMD_AUTHENTICATE:
+ info->auth_supported = 1;
+ break;
+ case NL80211_CMD_CONNECT:
+ info->connect_supported = 1;
+ break;
+ case NL80211_CMD_START_SCHED_SCAN:
+ info->capa->sched_scan_supported = 1;
+ break;
+ case NL80211_CMD_PROBE_CLIENT:
+ info->poll_command_supported = 1;
+ break;
+ case NL80211_CMD_CHANNEL_SWITCH:
+ info->channel_switch_supported = 1;
+ break;
+ case NL80211_CMD_SET_QOS_MAP:
+ info->set_qos_map_supported = 1;
+ break;
+ case NL80211_CMD_UPDATE_FT_IES:
+ info->update_ft_ies_supported = 1;
+ break;
+ }
+ }
+}
+
+
+static unsigned int get_akm_suites_info(struct nlattr *tb)
+{
+ int i, num;
+ unsigned int key_mgmt = 0;
+ u32 *akms;
+
+ if (!tb)
+ return 0;
+
+ num = nla_len(tb) / sizeof(u32);
+ akms = nla_data(tb);
+ for (i = 0; i < num; i++) {
+ switch (akms[i]) {
+ case RSN_AUTH_KEY_MGMT_UNSPEC_802_1X:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2;
+ break;
+ case RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
+ break;
+ case RSN_AUTH_KEY_MGMT_FT_802_1X:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT;
+ break;
+ case RSN_AUTH_KEY_MGMT_FT_PSK:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK;
+ break;
+ case RSN_AUTH_KEY_MGMT_802_1X_SHA256:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_802_1X_SHA256;
+ break;
+ case RSN_AUTH_KEY_MGMT_PSK_SHA256:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_PSK_SHA256;
+ break;
+ case RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_TPK_HANDSHAKE;
+ break;
+ case RSN_AUTH_KEY_MGMT_FT_SAE:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE;
+ break;
+ case RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE_EXT_KEY;
+ break;
+ case RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_802_1X_SHA384;
+ break;
+ case RSN_AUTH_KEY_MGMT_CCKM:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_CCKM;
+ break;
+ case RSN_AUTH_KEY_MGMT_OSEN:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_OSEN;
+ break;
+ case RSN_AUTH_KEY_MGMT_802_1X_SUITE_B:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B;
+ break;
+ case RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192;
+ break;
+ case RSN_AUTH_KEY_MGMT_OWE:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_OWE;
+ break;
+ case RSN_AUTH_KEY_MGMT_DPP:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_DPP;
+ break;
+ case RSN_AUTH_KEY_MGMT_FILS_SHA256:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256;
+ break;
+ case RSN_AUTH_KEY_MGMT_FILS_SHA384:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384;
+ break;
+ case RSN_AUTH_KEY_MGMT_FT_FILS_SHA256:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256;
+ break;
+ case RSN_AUTH_KEY_MGMT_FT_FILS_SHA384:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384;
+ break;
+ case RSN_AUTH_KEY_MGMT_SAE:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SAE;
+ break;
+ case RSN_AUTH_KEY_MGMT_SAE_EXT_KEY:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SAE_EXT_KEY;
+ break;
+ }
+ }
+
+ return key_mgmt;
+}
+
+
+static void get_iface_akm_suites_info(struct wiphy_info_data *info,
+ struct nlattr *nl_akms)
+{
+ struct nlattr *tb[NL80211_IFTYPE_AKM_ATTR_MAX + 1];
+ struct nlattr *nl_iftype;
+ unsigned int key_mgmt;
+ int i;
+
+ if (!nl_akms)
+ return;
+
+ nla_parse(tb, NL80211_IFTYPE_AKM_ATTR_MAX,
+ nla_data(nl_akms), nla_len(nl_akms), NULL);
+
+ if (!tb[NL80211_IFTYPE_AKM_ATTR_IFTYPES] ||
+ !tb[NL80211_IFTYPE_AKM_ATTR_SUITES])
+ return;
+
+ info->has_key_mgmt_iftype = 1;
+ key_mgmt = get_akm_suites_info(tb[NL80211_IFTYPE_AKM_ATTR_SUITES]);
+
+ nla_for_each_nested(nl_iftype, tb[NL80211_IFTYPE_AKM_ATTR_IFTYPES], i) {
+ switch (nla_type(nl_iftype)) {
+ case NL80211_IFTYPE_ADHOC:
+ info->drv->capa.key_mgmt_iftype[WPA_IF_IBSS] = key_mgmt;
+ break;
+ case NL80211_IFTYPE_STATION:
+ info->drv->capa.key_mgmt_iftype[WPA_IF_STATION] =
+ key_mgmt;
+ break;
+ case NL80211_IFTYPE_AP:
+ info->drv->capa.key_mgmt_iftype[WPA_IF_AP_BSS] =
+ key_mgmt;
+ break;
+ case NL80211_IFTYPE_AP_VLAN:
+ info->drv->capa.key_mgmt_iftype[WPA_IF_AP_VLAN] =
+ key_mgmt;
+ break;
+ case NL80211_IFTYPE_MESH_POINT:
+ info->drv->capa.key_mgmt_iftype[WPA_IF_MESH] = key_mgmt;
+ break;
+ case NL80211_IFTYPE_P2P_CLIENT:
+ info->drv->capa.key_mgmt_iftype[WPA_IF_P2P_CLIENT] =
+ key_mgmt;
+ break;
+ case NL80211_IFTYPE_P2P_GO:
+ info->drv->capa.key_mgmt_iftype[WPA_IF_P2P_GO] =
+ key_mgmt;
+ break;
+ case NL80211_IFTYPE_P2P_DEVICE:
+ info->drv->capa.key_mgmt_iftype[WPA_IF_P2P_DEVICE] =
+ key_mgmt;
+ break;
+ case NL80211_IFTYPE_NAN:
+ info->drv->capa.key_mgmt_iftype[WPA_IF_NAN] = key_mgmt;
+ break;
+ }
+ wpa_printf(MSG_DEBUG, "nl80211: %s supported key_mgmt 0x%x",
+ nl80211_iftype_str(nla_type(nl_iftype)),
+ key_mgmt);
+ }
+}
+
+
+static void wiphy_info_iftype_akm_suites(struct wiphy_info_data *info,
+ struct nlattr *tb)
+{
+ struct nlattr *nl_if;
+ int rem_if;
+
+ if (!tb)
+ return;
+
+ nla_for_each_nested(nl_if, tb, rem_if)
+ get_iface_akm_suites_info(info, nl_if);
+}
+
+
+static void wiphy_info_akm_suites(struct wiphy_info_data *info,
+ struct nlattr *tb)
+{
+ if (!tb)
+ return;
+
+ info->has_key_mgmt = 1;
+ info->capa->key_mgmt = get_akm_suites_info(tb);
+ wpa_printf(MSG_DEBUG, "nl80211: wiphy supported key_mgmt 0x%x",
+ info->capa->key_mgmt);
+}
+
+
+static void wiphy_info_cipher_suites(struct wiphy_info_data *info,
+ struct nlattr *tb)
+{
+ int i, num;
+ u32 *ciphers;
+
+ if (tb == NULL)
+ return;
+
+ num = nla_len(tb) / sizeof(u32);
+ ciphers = nla_data(tb);
+ for (i = 0; i < num; i++) {
+ u32 c = ciphers[i];
+
+ wpa_printf(MSG_DEBUG, "nl80211: Supported cipher %02x-%02x-%02x:%d",
+ c >> 24, (c >> 16) & 0xff,
+ (c >> 8) & 0xff, c & 0xff);
+ switch (c) {
+ case RSN_CIPHER_SUITE_CCMP_256:
+ info->capa->enc |= WPA_DRIVER_CAPA_ENC_CCMP_256;
+ break;
+ case RSN_CIPHER_SUITE_GCMP_256:
+ info->capa->enc |= WPA_DRIVER_CAPA_ENC_GCMP_256;
+ break;
+ case RSN_CIPHER_SUITE_CCMP:
+ info->capa->enc |= WPA_DRIVER_CAPA_ENC_CCMP;
+ break;
+ case RSN_CIPHER_SUITE_GCMP:
+ info->capa->enc |= WPA_DRIVER_CAPA_ENC_GCMP;
+ break;
+ case RSN_CIPHER_SUITE_TKIP:
+ info->capa->enc |= WPA_DRIVER_CAPA_ENC_TKIP;
+ break;
+ case RSN_CIPHER_SUITE_WEP104:
+ info->capa->enc |= WPA_DRIVER_CAPA_ENC_WEP104;
+ break;
+ case RSN_CIPHER_SUITE_WEP40:
+ info->capa->enc |= WPA_DRIVER_CAPA_ENC_WEP40;
+ break;
+ case RSN_CIPHER_SUITE_AES_128_CMAC:
+ info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP;
+ break;
+ case RSN_CIPHER_SUITE_BIP_GMAC_128:
+ info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_GMAC_128;
+ break;
+ case RSN_CIPHER_SUITE_BIP_GMAC_256:
+ info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_GMAC_256;
+ break;
+ case RSN_CIPHER_SUITE_BIP_CMAC_256:
+ info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_CMAC_256;
+ break;
+ case RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED:
+ info->capa->enc |= WPA_DRIVER_CAPA_ENC_GTK_NOT_USED;
+ break;
+ }
+ }
+}
+
+
+static void wiphy_info_max_roc(struct wpa_driver_capa *capa,
+ struct nlattr *tb)
+{
+ if (tb)
+ capa->max_remain_on_chan = nla_get_u32(tb);
+}
+
+
+static void wiphy_info_tdls(struct wpa_driver_capa *capa, struct nlattr *tdls,
+ struct nlattr *ext_setup)
+{
+ if (tdls == NULL)
+ return;
+
+ wpa_printf(MSG_DEBUG, "nl80211: TDLS supported");
+ capa->flags |= WPA_DRIVER_FLAGS_TDLS_SUPPORT;
+
+ if (ext_setup) {
+ wpa_printf(MSG_DEBUG, "nl80211: TDLS external setup");
+ capa->flags |= WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP;
+ }
+}
+
+
+static int ext_feature_isset(const u8 *ext_features, int ext_features_len,
+ enum nl80211_ext_feature_index ftidx)
+{
+ u8 ft_byte;
+
+ if ((int) ftidx / 8 >= ext_features_len)
+ return 0;
+
+ ft_byte = ext_features[ftidx / 8];
+ return (ft_byte & BIT(ftidx % 8)) != 0;
+}
+
+
+static void wiphy_info_ext_feature_flags(struct wiphy_info_data *info,
+ struct nlattr *tb)
+{
+ struct wpa_driver_capa *capa = info->capa;
+ u8 *ext_features;
+ int len;
+
+ if (tb == NULL)
+ return;
+
+ ext_features = nla_data(tb);
+ len = nla_len(tb);
+
+ if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_VHT_IBSS))
+ capa->flags |= WPA_DRIVER_FLAGS_VHT_IBSS;
+
+ if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_RRM))
+ capa->rrm_flags |= WPA_DRIVER_FLAGS_SUPPORT_RRM;
+
+ if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_FILS_STA))
+ capa->flags |= WPA_DRIVER_FLAGS_SUPPORT_FILS;
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_BEACON_RATE_LEGACY))
+ capa->flags |= WPA_DRIVER_FLAGS_BEACON_RATE_LEGACY;
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_BEACON_RATE_HT))
+ capa->flags |= WPA_DRIVER_FLAGS_BEACON_RATE_HT;
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_BEACON_RATE_VHT))
+ capa->flags |= WPA_DRIVER_FLAGS_BEACON_RATE_VHT;
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_BEACON_RATE_HE))
+ capa->flags2 |= WPA_DRIVER_FLAGS2_BEACON_RATE_HE;
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_SET_SCAN_DWELL))
+ capa->rrm_flags |= WPA_DRIVER_FLAGS_SUPPORT_SET_SCAN_DWELL;
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_SCAN_START_TIME) &&
+ ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_BSS_PARENT_TSF) &&
+ ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_SET_SCAN_DWELL))
+ capa->rrm_flags |= WPA_DRIVER_FLAGS_SUPPORT_BEACON_REPORT;
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA))
+ capa->flags |= WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA;
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED))
+ capa->flags |= WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA_CONNECTED;
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI))
+ capa->flags |= WPA_DRIVER_FLAGS_SCHED_SCAN_RELATIVE_RSSI;
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_FILS_SK_OFFLOAD))
+ capa->flags |= WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD;
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK))
+ capa->flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK;
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X))
+ capa->flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X;
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_MFP_OPTIONAL))
+ capa->flags |= WPA_DRIVER_FLAGS_MFP_OPTIONAL;
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_DFS_OFFLOAD))
+ capa->flags |= WPA_DRIVER_FLAGS_DFS_OFFLOAD;
+
+#ifdef CONFIG_MBO
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME) &&
+ ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP) &&
+ ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE) &&
+ ext_feature_isset(
+ ext_features, len,
+ NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION))
+ capa->flags |= WPA_DRIVER_FLAGS_OCE_STA;
+#endif /* CONFIG_MBO */
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER))
+ capa->flags |= WPA_DRIVER_FLAGS_FTM_RESPONDER;
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211))
+ capa->flags |= WPA_DRIVER_FLAGS_CONTROL_PORT;
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_CONTROL_PORT_NO_PREAUTH))
+ capa->flags2 |= WPA_DRIVER_FLAGS2_CONTROL_PORT_RX;
+ if (ext_feature_isset(
+ ext_features, len,
+ NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211_TX_STATUS))
+ capa->flags2 |= WPA_DRIVER_FLAGS2_CONTROL_PORT_TX_STATUS;
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_VLAN_OFFLOAD))
+ capa->flags |= WPA_DRIVER_FLAGS_VLAN_OFFLOAD;
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_CAN_REPLACE_PTK0))
+ capa->flags |= WPA_DRIVER_FLAGS_SAFE_PTK0_REKEYS;
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_BEACON_PROTECTION))
+ capa->flags |= WPA_DRIVER_FLAGS_BEACON_PROTECTION;
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_EXT_KEY_ID))
+ capa->flags |= WPA_DRIVER_FLAGS_EXTENDED_KEY_ID;
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS))
+ info->drv->multicast_registrations = 1;
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_FILS_DISCOVERY))
+ info->drv->fils_discovery = 1;
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_UNSOL_BCAST_PROBE_RESP))
+ info->drv->unsol_bcast_probe_resp = 1;
+
+ if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_PUNCT))
+ info->drv->puncturing = 1;
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_BEACON_PROTECTION_CLIENT))
+ capa->flags2 |= WPA_DRIVER_FLAGS2_BEACON_PROTECTION_CLIENT;
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_OPERATING_CHANNEL_VALIDATION))
+ capa->flags2 |= WPA_DRIVER_FLAGS2_OCV;
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_RADAR_BACKGROUND))
+ capa->flags2 |= WPA_DRIVER_RADAR_BACKGROUND;
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_SECURE_LTF)) {
+ capa->flags2 |= WPA_DRIVER_FLAGS2_SEC_LTF_STA;
+ capa->flags2 |= WPA_DRIVER_FLAGS2_SEC_LTF_AP;
+ }
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_SECURE_RTT)) {
+ capa->flags2 |= WPA_DRIVER_FLAGS2_SEC_RTT_STA;
+ capa->flags2 |= WPA_DRIVER_FLAGS2_SEC_RTT_AP;
+ }
+
+ if (ext_feature_isset(
+ ext_features, len,
+ NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE)) {
+ capa->flags2 |= WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_STA;
+ capa->flags2 |= WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP;
+ }
+}
+
+
+static void wiphy_info_feature_flags(struct wiphy_info_data *info,
+ struct nlattr *tb)
+{
+ u32 flags;
+ struct wpa_driver_capa *capa = info->capa;
+
+ if (tb == NULL)
+ return;
+
+ flags = nla_get_u32(tb);
+
+ if (flags & NL80211_FEATURE_SK_TX_STATUS)
+ info->data_tx_status = 1;
+
+ if (flags & NL80211_FEATURE_INACTIVITY_TIMER)
+ capa->flags |= WPA_DRIVER_FLAGS_INACTIVITY_TIMER;
+
+ if (flags & NL80211_FEATURE_SAE)
+ capa->flags |= WPA_DRIVER_FLAGS_SAE;
+
+ if (flags & NL80211_FEATURE_NEED_OBSS_SCAN)
+ capa->flags |= WPA_DRIVER_FLAGS_OBSS_SCAN;
+
+ if (flags & NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE)
+ capa->flags |= WPA_DRIVER_FLAGS_HT_2040_COEX;
+
+ if (flags & NL80211_FEATURE_TDLS_CHANNEL_SWITCH) {
+ wpa_printf(MSG_DEBUG, "nl80211: TDLS channel switch");
+ capa->flags |= WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH;
+ }
+
+ if (flags & NL80211_FEATURE_P2P_GO_CTWIN)
+ info->p2p_go_ctwindow_supported = 1;
+
+ if (flags & NL80211_FEATURE_LOW_PRIORITY_SCAN)
+ info->have_low_prio_scan = 1;
+
+ if (flags & NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR)
+ info->mac_addr_rand_scan_supported = 1;
+
+ if (flags & NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR)
+ info->mac_addr_rand_sched_scan_supported = 1;
+
+ if (flags & NL80211_FEATURE_SUPPORTS_WMM_ADMISSION)
+ info->wmm_ac_supported = 1;
+
+ if (flags & NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES)
+ capa->rrm_flags |= WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES;
+
+ if (flags & NL80211_FEATURE_WFA_TPC_IE_IN_PROBES)
+ capa->rrm_flags |= WPA_DRIVER_FLAGS_WFA_TPC_IE_IN_PROBES;
+
+ if (flags & NL80211_FEATURE_QUIET)
+ capa->rrm_flags |= WPA_DRIVER_FLAGS_QUIET;
+
+ if (flags & NL80211_FEATURE_TX_POWER_INSERTION)
+ capa->rrm_flags |= WPA_DRIVER_FLAGS_TX_POWER_INSERTION;
+
+ if (flags & NL80211_FEATURE_HT_IBSS)
+ capa->flags |= WPA_DRIVER_FLAGS_HT_IBSS;
+
+ if (flags & NL80211_FEATURE_FULL_AP_CLIENT_STATE)
+ capa->flags |= WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE;
+}
+
+
+static void wiphy_info_probe_resp_offload(struct wpa_driver_capa *capa,
+ struct nlattr *tb)
+{
+ u32 protocols;
+
+ if (tb == NULL)
+ return;
+
+ protocols = nla_get_u32(tb);
+ wpa_printf(MSG_DEBUG, "nl80211: Supports Probe Response offload in AP "
+ "mode");
+ capa->flags |= WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD;
+ capa->probe_resp_offloads = probe_resp_offload_support(protocols);
+}
+
+
+static void wiphy_info_wowlan_triggers(struct wpa_driver_capa *capa,
+ struct nlattr *tb)
+{
+ struct nlattr *triggers[MAX_NL80211_WOWLAN_TRIG + 1];
+
+ if (tb == NULL)
+ return;
+
+ if (nla_parse_nested(triggers, MAX_NL80211_WOWLAN_TRIG,
+ tb, NULL))
+ return;
+
+ if (triggers[NL80211_WOWLAN_TRIG_ANY])
+ capa->wowlan_triggers.any = 1;
+ if (triggers[NL80211_WOWLAN_TRIG_DISCONNECT])
+ capa->wowlan_triggers.disconnect = 1;
+ if (triggers[NL80211_WOWLAN_TRIG_MAGIC_PKT])
+ capa->wowlan_triggers.magic_pkt = 1;
+ if (triggers[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE])
+ capa->wowlan_triggers.gtk_rekey_failure = 1;
+ if (triggers[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST])
+ capa->wowlan_triggers.eap_identity_req = 1;
+ if (triggers[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE])
+ capa->wowlan_triggers.four_way_handshake = 1;
+ if (triggers[NL80211_WOWLAN_TRIG_RFKILL_RELEASE])
+ capa->wowlan_triggers.rfkill_release = 1;
+}
+
+
+static void wiphy_info_extended_capab(struct wpa_driver_nl80211_data *drv,
+ struct nlattr *tb)
+{
+ int rem = 0, i;
+ struct nlattr *tb1[NL80211_ATTR_MAX + 1], *attr;
+
+ if (!tb || drv->num_iface_capa == NL80211_IFTYPE_MAX)
+ return;
+
+ nla_for_each_nested(attr, tb, rem) {
+ unsigned int len;
+ struct drv_nl80211_iface_capa *capa;
+
+ nla_parse(tb1, NL80211_ATTR_MAX, nla_data(attr),
+ nla_len(attr), NULL);
+
+ if (!tb1[NL80211_ATTR_IFTYPE] ||
+ !tb1[NL80211_ATTR_EXT_CAPA] ||
+ !tb1[NL80211_ATTR_EXT_CAPA_MASK])
+ continue;
+
+ capa = &drv->iface_capa[drv->num_iface_capa];
+ capa->iftype = nla_get_u32(tb1[NL80211_ATTR_IFTYPE]);
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Driver-advertised extended capabilities for interface type %s",
+ nl80211_iftype_str(capa->iftype));
+
+ len = nla_len(tb1[NL80211_ATTR_EXT_CAPA]);
+ capa->ext_capa = os_memdup(nla_data(tb1[NL80211_ATTR_EXT_CAPA]),
+ len);
+ if (!capa->ext_capa)
+ goto err;
+
+ capa->ext_capa_len = len;
+ wpa_hexdump(MSG_DEBUG, "nl80211: Extended capabilities",
+ capa->ext_capa, capa->ext_capa_len);
+
+ len = nla_len(tb1[NL80211_ATTR_EXT_CAPA_MASK]);
+ capa->ext_capa_mask =
+ os_memdup(nla_data(tb1[NL80211_ATTR_EXT_CAPA_MASK]),
+ len);
+ if (!capa->ext_capa_mask)
+ goto err;
+
+ wpa_hexdump(MSG_DEBUG, "nl80211: Extended capabilities mask",
+ capa->ext_capa_mask, capa->ext_capa_len);
+
+ if (tb1[NL80211_ATTR_EML_CAPABILITY] &&
+ tb1[NL80211_ATTR_MLD_CAPA_AND_OPS]) {
+ capa->eml_capa =
+ nla_get_u16(tb1[NL80211_ATTR_EML_CAPABILITY]);
+ capa->mld_capa_and_ops =
+ nla_get_u16(tb1[NL80211_ATTR_MLD_CAPA_AND_OPS]);
+ }
+
+ drv->num_iface_capa++;
+ if (drv->num_iface_capa == NL80211_IFTYPE_MAX)
+ break;
+ }
+
+ return;
+
+err:
+ /* Cleanup allocated memory on error */
+ for (i = 0; i < NL80211_IFTYPE_MAX; i++) {
+ os_free(drv->iface_capa[i].ext_capa);
+ drv->iface_capa[i].ext_capa = NULL;
+ os_free(drv->iface_capa[i].ext_capa_mask);
+ drv->iface_capa[i].ext_capa_mask = NULL;
+ drv->iface_capa[i].ext_capa_len = 0;
+ }
+ drv->num_iface_capa = 0;
+}
+
+
+static void wiphy_info_mbssid(struct wpa_driver_capa *cap, struct nlattr *attr)
+{
+ struct nlattr *config[NL80211_MBSSID_CONFIG_ATTR_MAX + 1];
+
+ if (nla_parse_nested(config, NL80211_MBSSID_CONFIG_ATTR_MAX, attr,
+ NULL))
+ return;
+
+ if (!config[NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES])
+ return;
+
+ cap->mbssid_max_interfaces =
+ nla_get_u8(config[NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES]);
+
+ if (config[NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY])
+ cap->ema_max_periodicity =
+ nla_get_u8(config[NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY]);
+
+ wpa_printf(MSG_DEBUG,
+ "mbssid: max interfaces %u, max profile periodicity %u",
+ cap->mbssid_max_interfaces, cap->ema_max_periodicity);
+}
+
+
+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;
+ struct wpa_driver_capa *capa = info->capa;
+ struct wpa_driver_nl80211_data *drv = info->drv;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ if (tb[NL80211_ATTR_WIPHY])
+ drv->wiphy_idx = nla_get_u32(tb[NL80211_ATTR_WIPHY]);
+
+ if (tb[NL80211_ATTR_WIPHY_NAME])
+ os_strlcpy(drv->phyname,
+ nla_get_string(tb[NL80211_ATTR_WIPHY_NAME]),
+ sizeof(drv->phyname));
+ if (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS])
+ capa->max_scan_ssids =
+ nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]);
+
+ if (tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS])
+ capa->max_sched_scan_ssids =
+ nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]);
+
+ if (tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS] &&
+ tb[NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL] &&
+ tb[NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS]) {
+ capa->max_sched_scan_plans =
+ nla_get_u32(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS]);
+
+ capa->max_sched_scan_plan_interval =
+ nla_get_u32(tb[NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL]);
+
+ capa->max_sched_scan_plan_iterations =
+ nla_get_u32(tb[NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS]);
+ }
+
+ if (tb[NL80211_ATTR_MAX_MATCH_SETS])
+ capa->max_match_sets =
+ nla_get_u8(tb[NL80211_ATTR_MAX_MATCH_SETS]);
+
+ if (tb[NL80211_ATTR_MAC_ACL_MAX])
+ capa->max_acl_mac_addrs =
+ nla_get_u32(tb[NL80211_ATTR_MAC_ACL_MAX]);
+
+ wiphy_info_supported_iftypes(info, tb[NL80211_ATTR_SUPPORTED_IFTYPES]);
+ wiphy_info_iface_comb(info, tb[NL80211_ATTR_INTERFACE_COMBINATIONS]);
+ wiphy_info_supp_cmds(info, tb[NL80211_ATTR_SUPPORTED_COMMANDS]);
+ wiphy_info_cipher_suites(info, tb[NL80211_ATTR_CIPHER_SUITES]);
+ wiphy_info_akm_suites(info, tb[NL80211_ATTR_AKM_SUITES]);
+ wiphy_info_iftype_akm_suites(info, tb[NL80211_ATTR_IFTYPE_AKM_SUITES]);
+
+ if (tb[NL80211_ATTR_OFFCHANNEL_TX_OK]) {
+ wpa_printf(MSG_DEBUG, "nl80211: Using driver-based "
+ "off-channel TX");
+ capa->flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_TX;
+ }
+
+ if (tb[NL80211_ATTR_ROAM_SUPPORT]) {
+ wpa_printf(MSG_DEBUG, "nl80211: Using driver-based roaming");
+ capa->flags |= WPA_DRIVER_FLAGS_BSS_SELECTION;
+ }
+
+ wiphy_info_max_roc(capa,
+ tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION]);
+
+ if (tb[NL80211_ATTR_SUPPORT_AP_UAPSD])
+ capa->flags |= WPA_DRIVER_FLAGS_AP_UAPSD;
+
+ wiphy_info_tdls(capa, tb[NL80211_ATTR_TDLS_SUPPORT],
+ tb[NL80211_ATTR_TDLS_EXTERNAL_SETUP]);
+
+ if (tb[NL80211_ATTR_DEVICE_AP_SME]) {
+ u32 ap_sme_features_flags =
+ nla_get_u32(tb[NL80211_ATTR_DEVICE_AP_SME]);
+
+ if (ap_sme_features_flags & NL80211_AP_SME_SA_QUERY_OFFLOAD)
+ capa->flags2 |= WPA_DRIVER_FLAGS2_SA_QUERY_OFFLOAD_AP;
+
+ info->device_ap_sme = 1;
+ }
+
+ wiphy_info_feature_flags(info, tb[NL80211_ATTR_FEATURE_FLAGS]);
+ wiphy_info_ext_feature_flags(info, tb[NL80211_ATTR_EXT_FEATURES]);
+ wiphy_info_probe_resp_offload(capa,
+ tb[NL80211_ATTR_PROBE_RESP_OFFLOAD]);
+
+ if (tb[NL80211_ATTR_EXT_CAPA] && tb[NL80211_ATTR_EXT_CAPA_MASK] &&
+ drv->extended_capa == NULL) {
+ drv->extended_capa =
+ os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA]));
+ if (drv->extended_capa) {
+ os_memcpy(drv->extended_capa,
+ nla_data(tb[NL80211_ATTR_EXT_CAPA]),
+ nla_len(tb[NL80211_ATTR_EXT_CAPA]));
+ drv->extended_capa_len =
+ nla_len(tb[NL80211_ATTR_EXT_CAPA]);
+ wpa_hexdump(MSG_DEBUG,
+ "nl80211: Driver-advertised extended capabilities (default)",
+ drv->extended_capa, drv->extended_capa_len);
+ }
+ drv->extended_capa_mask =
+ os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA_MASK]));
+ if (drv->extended_capa_mask) {
+ os_memcpy(drv->extended_capa_mask,
+ nla_data(tb[NL80211_ATTR_EXT_CAPA_MASK]),
+ nla_len(tb[NL80211_ATTR_EXT_CAPA_MASK]));
+ wpa_hexdump(MSG_DEBUG,
+ "nl80211: Driver-advertised extended capabilities mask (default)",
+ drv->extended_capa_mask,
+ drv->extended_capa_len);
+ } else {
+ os_free(drv->extended_capa);
+ drv->extended_capa = NULL;
+ drv->extended_capa_len = 0;
+ }
+ }
+
+ wiphy_info_extended_capab(drv, tb[NL80211_ATTR_IFTYPE_EXT_CAPA]);
+
+ if (tb[NL80211_ATTR_VENDOR_DATA]) {
+ struct nlattr *nl;
+ int rem;
+
+ nla_for_each_nested(nl, tb[NL80211_ATTR_VENDOR_DATA], rem) {
+ struct nl80211_vendor_cmd_info *vinfo;
+ if (nla_len(nl) != sizeof(*vinfo)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Unexpected vendor data info");
+ continue;
+ }
+ vinfo = nla_data(nl);
+ if (vinfo->vendor_id == OUI_QCA) {
+ switch (vinfo->subcmd) {
+ case QCA_NL80211_VENDOR_SUBCMD_TEST:
+ drv->vendor_cmd_test_avail = 1;
+ break;
+#ifdef CONFIG_DRIVER_NL80211_QCA
+ case QCA_NL80211_VENDOR_SUBCMD_ROAMING:
+ drv->roaming_vendor_cmd_avail = 1;
+ break;
+ case QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY:
+ drv->dfs_vendor_cmd_avail = 1;
+ break;
+ case QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES:
+ drv->get_features_vendor_cmd_avail = 1;
+ break;
+ case QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST:
+ drv->get_pref_freq_list = 1;
+ break;
+ case QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL:
+ drv->set_prob_oper_freq = 1;
+ break;
+ case QCA_NL80211_VENDOR_SUBCMD_DO_ACS:
+ drv->capa.flags |=
+ WPA_DRIVER_FLAGS_ACS_OFFLOAD;
+ drv->qca_do_acs = 1;
+ break;
+ case QCA_NL80211_VENDOR_SUBCMD_SETBAND:
+ drv->setband_vendor_cmd_avail = 1;
+ break;
+ case QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN:
+ drv->scan_vendor_cmd_avail = 1;
+ break;
+ case QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION:
+ drv->set_wifi_conf_vendor_cmd_avail = 1;
+ break;
+ case QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS:
+ drv->fetch_bss_trans_status = 1;
+ break;
+ case QCA_NL80211_VENDOR_SUBCMD_ROAM:
+ drv->roam_vendor_cmd_avail = 1;
+ break;
+ case QCA_NL80211_VENDOR_SUBCMD_ADD_STA_NODE:
+ drv->add_sta_node_vendor_cmd_avail = 1;
+ break;
+ case QCA_NL80211_VENDOR_SUBCMD_GET_STA_INFO:
+ drv->get_sta_info_vendor_cmd_avail = 1;
+ break;
+ case QCA_NL80211_VENDOR_SUBCMD_SECURE_RANGING_CONTEXT:
+ drv->secure_ranging_ctx_vendor_cmd_avail = 1;
+ break;
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+ }
+#ifdef CONFIG_DRIVER_NL80211_BRCM
+ } else if (vinfo->vendor_id == OUI_BRCM) {
+ switch (vinfo->subcmd) {
+ case BRCM_VENDOR_SCMD_ACS:
+ drv->capa.flags |=
+ WPA_DRIVER_FLAGS_ACS_OFFLOAD;
+ wpa_printf(MSG_DEBUG,
+ "Enabled BRCM ACS");
+ drv->brcm_do_acs = 1;
+ break;
+ }
+#endif /* CONFIG_DRIVER_NL80211_BRCM */
+ }
+
+ wpa_printf(MSG_DEBUG, "nl80211: Supported vendor command: vendor_id=0x%x subcmd=%u",
+ vinfo->vendor_id, vinfo->subcmd);
+ }
+ }
+
+ if (tb[NL80211_ATTR_VENDOR_EVENTS]) {
+ struct nlattr *nl;
+ int rem;
+
+ nla_for_each_nested(nl, tb[NL80211_ATTR_VENDOR_EVENTS], rem) {
+ struct nl80211_vendor_cmd_info *vinfo;
+ if (nla_len(nl) != sizeof(*vinfo)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Unexpected vendor data info");
+ continue;
+ }
+ vinfo = nla_data(nl);
+ wpa_printf(MSG_DEBUG, "nl80211: Supported vendor event: vendor_id=0x%x subcmd=%u",
+ vinfo->vendor_id, vinfo->subcmd);
+ }
+ }
+
+ wiphy_info_wowlan_triggers(capa,
+ tb[NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED]);
+
+ if (tb[NL80211_ATTR_MAX_AP_ASSOC_STA])
+ capa->max_stations =
+ nla_get_u32(tb[NL80211_ATTR_MAX_AP_ASSOC_STA]);
+
+ if (tb[NL80211_ATTR_MAX_CSA_COUNTERS])
+ capa->max_csa_counters =
+ nla_get_u8(tb[NL80211_ATTR_MAX_CSA_COUNTERS]);
+
+ if (tb[NL80211_ATTR_WIPHY_SELF_MANAGED_REG])
+ capa->flags |= WPA_DRIVER_FLAGS_SELF_MANAGED_REGULATORY;
+
+ if (tb[NL80211_ATTR_MAX_NUM_AKM_SUITES])
+ capa->max_num_akms =
+ nla_get_u16(tb[NL80211_ATTR_MAX_NUM_AKM_SUITES]);
+
+ if (tb[NL80211_ATTR_MBSSID_CONFIG])
+ wiphy_info_mbssid(capa, tb[NL80211_ATTR_MBSSID_CONFIG]);
+
+ if (tb[NL80211_ATTR_MLO_SUPPORT])
+ capa->flags2 |= WPA_DRIVER_FLAGS2_MLO;
+
+ return NL_SKIP;
+}
+
+
+static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv,
+ struct wiphy_info_data *info)
+{
+ u32 feat;
+ struct nl_msg *msg;
+ int flags = 0;
+
+ os_memset(info, 0, sizeof(*info));
+ info->capa = &drv->capa;
+ info->drv = drv;
+
+ feat = get_nl80211_protocol_features(drv);
+ if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP)
+ flags = NLM_F_DUMP;
+ msg = nl80211_cmd_msg(drv->first_bss, flags, NL80211_CMD_GET_WIPHY);
+ if (!msg || nla_put_flag(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP)) {
+ nlmsg_free(msg);
+ return -1;
+ }
+
+ if (send_and_recv_msgs(drv, msg, wiphy_info_handler, info, NULL, NULL))
+ return -1;
+
+ 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");
+ info->error = 1;
+ }
+
+ if (info->p2p_go_supported && info->p2p_client_supported)
+ drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE;
+ if (info->p2p_concurrent) {
+ wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group "
+ "interface (driver advertised support)");
+ drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT;
+ drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P;
+ }
+ if (info->num_multichan_concurrent > 1) {
+ wpa_printf(MSG_DEBUG, "nl80211: Enable multi-channel "
+ "concurrent (driver advertised support)");
+ drv->capa.num_multichan_concurrent =
+ info->num_multichan_concurrent;
+ }
+ if (drv->capa.flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)
+ wpa_printf(MSG_DEBUG, "nl80211: use P2P_DEVICE support");
+
+ /* default to 5000 since early versions of mac80211 don't set it */
+ if (!drv->capa.max_remain_on_chan)
+ drv->capa.max_remain_on_chan = 5000;
+
+ drv->capa.wmm_ac_supported = info->wmm_ac_supported;
+
+ drv->capa.mac_addr_rand_sched_scan_supported =
+ info->mac_addr_rand_sched_scan_supported;
+ drv->capa.mac_addr_rand_scan_supported =
+ info->mac_addr_rand_scan_supported;
+
+ if (info->channel_switch_supported) {
+ drv->capa.flags |= WPA_DRIVER_FLAGS_AP_CSA;
+ if (!drv->capa.max_csa_counters)
+ drv->capa.max_csa_counters = 1;
+ }
+
+ if (!drv->capa.max_sched_scan_plans) {
+ drv->capa.max_sched_scan_plans = 1;
+ drv->capa.max_sched_scan_plan_interval = UINT32_MAX;
+ drv->capa.max_sched_scan_plan_iterations = 0;
+ }
+
+ if (info->update_ft_ies_supported)
+ drv->capa.flags |= WPA_DRIVER_FLAGS_UPDATE_FT_IES;
+
+ if (!drv->capa.max_num_akms)
+ drv->capa.max_num_akms = NL80211_MAX_NR_AKM_SUITES;
+
+ return 0;
+}
+
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+
+static int dfs_info_handler(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ int *dfs_capability_ptr = arg;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ if (tb[NL80211_ATTR_VENDOR_DATA]) {
+ struct nlattr *nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
+ struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1];
+
+ nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX,
+ nla_data(nl_vend), nla_len(nl_vend), NULL);
+
+ if (tb_vendor[QCA_WLAN_VENDOR_ATTR_DFS]) {
+ u32 val;
+ val = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_DFS]);
+ wpa_printf(MSG_DEBUG, "nl80211: DFS offload capability: %u",
+ val);
+ *dfs_capability_ptr = val;
+ }
+ }
+
+ return NL_SKIP;
+}
+
+
+static void qca_nl80211_check_dfs_capa(struct wpa_driver_nl80211_data *drv)
+{
+ struct nl_msg *msg;
+ int dfs_capability = 0;
+ int ret;
+
+ if (!drv->dfs_vendor_cmd_avail)
+ return;
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY)) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ ret = send_and_recv_msgs(drv, msg, dfs_info_handler, &dfs_capability,
+ NULL, NULL);
+ if (!ret && dfs_capability)
+ drv->capa.flags |= WPA_DRIVER_FLAGS_DFS_OFFLOAD;
+}
+
+
+struct features_info {
+ u8 *flags;
+ size_t flags_len;
+ struct wpa_driver_capa *capa;
+};
+
+
+static int features_info_handler(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct features_info *info = arg;
+ struct nlattr *nl_vend, *attr;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
+ if (nl_vend) {
+ struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1];
+
+ nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX,
+ nla_data(nl_vend), nla_len(nl_vend), NULL);
+
+ attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_FEATURE_FLAGS];
+ if (attr) {
+ int len = nla_len(attr);
+ info->flags = os_malloc(len);
+ if (info->flags != NULL) {
+ os_memcpy(info->flags, nla_data(attr), len);
+ info->flags_len = len;
+ }
+ }
+ attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_CONCURRENCY_CAPA];
+ if (attr)
+ info->capa->conc_capab = nla_get_u32(attr);
+
+ attr = tb_vendor[
+ QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_2_4_BAND];
+ if (attr)
+ info->capa->max_conc_chan_2_4 = nla_get_u32(attr);
+
+ attr = tb_vendor[
+ QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_5_0_BAND];
+ if (attr)
+ info->capa->max_conc_chan_5_0 = nla_get_u32(attr);
+ }
+
+ return NL_SKIP;
+}
+
+
+static int check_feature(enum qca_wlan_vendor_features feature,
+ struct features_info *info)
+{
+ size_t idx = feature / 8;
+
+ return (idx < info->flags_len) &&
+ (info->flags[idx] & BIT(feature % 8));
+}
+
+
+static void qca_nl80211_get_features(struct wpa_driver_nl80211_data *drv)
+{
+ struct nl_msg *msg;
+ struct features_info info;
+ int ret;
+
+ if (!drv->get_features_vendor_cmd_avail)
+ return;
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES)) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ os_memset(&info, 0, sizeof(info));
+ info.capa = &drv->capa;
+ ret = send_and_recv_msgs(drv, msg, features_info_handler, &info,
+ NULL, NULL);
+ if (ret || !info.flags)
+ return;
+
+ if (check_feature(QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD, &info))
+ drv->capa.flags |= WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD;
+
+ if (check_feature(QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY, &info))
+ drv->capa.flags |= WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY;
+
+ if (check_feature(QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS,
+ &info))
+ drv->capa.flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS;
+ if (check_feature(QCA_WLAN_VENDOR_FEATURE_P2P_LISTEN_OFFLOAD, &info))
+ drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD;
+ if (check_feature(QCA_WLAN_VENDOR_FEATURE_OCE_STA, &info))
+ drv->capa.flags |= WPA_DRIVER_FLAGS_OCE_STA;
+ if (check_feature(QCA_WLAN_VENDOR_FEATURE_OCE_AP, &info))
+ drv->capa.flags |= WPA_DRIVER_FLAGS_OCE_AP;
+ if (check_feature(QCA_WLAN_VENDOR_FEATURE_OCE_STA_CFON, &info))
+ drv->capa.flags |= WPA_DRIVER_FLAGS_OCE_STA_CFON;
+ if (check_feature(QCA_WLAN_VENDOR_FEATURE_SECURE_LTF_STA, &info))
+ drv->capa.flags2 |= WPA_DRIVER_FLAGS2_SEC_LTF_STA;
+ if (check_feature(QCA_WLAN_VENDOR_FEATURE_SECURE_LTF_AP, &info))
+ drv->capa.flags2 |= WPA_DRIVER_FLAGS2_SEC_LTF_AP;
+ if (check_feature(QCA_WLAN_VENDOR_FEATURE_SECURE_RTT_STA, &info))
+ drv->capa.flags2 |= WPA_DRIVER_FLAGS2_SEC_RTT_STA;
+ if (check_feature(QCA_WLAN_VENDOR_FEATURE_SECURE_RTT_AP, &info))
+ drv->capa.flags2 |= WPA_DRIVER_FLAGS2_SEC_RTT_AP;
+ if (check_feature(
+ QCA_WLAN_VENDOR_FEATURE_PROT_RANGE_NEGO_AND_MEASURE_STA,
+ &info))
+ drv->capa.flags2 |= WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_STA;
+ if (check_feature(
+ QCA_WLAN_VENDOR_FEATURE_PROT_RANGE_NEGO_AND_MEASURE_AP,
+ &info))
+ drv->capa.flags2 |= WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP;
+ if (check_feature(QCA_WLAN_VENDOR_FEATURE_AP_ALLOWED_FREQ_LIST,
+ &info))
+ drv->qca_ap_allowed_freqs = 1;
+ os_free(info.flags);
+}
+
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+
+int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
+{
+ struct wiphy_info_data info;
+ int i;
+
+ if (wpa_driver_nl80211_get_info(drv, &info))
+ return -1;
+
+ if (info.error)
+ return -1;
+
+ drv->has_capability = 1;
+ drv->has_driver_key_mgmt = info.has_key_mgmt | info.has_key_mgmt_iftype;
+
+ /* Fallback to hardcoded defaults if the driver does not advertise any
+ * AKM capabilities. */
+ if (!drv->has_driver_key_mgmt) {
+ 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 |
+ WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B |
+ WPA_DRIVER_CAPA_KEY_MGMT_OWE |
+ WPA_DRIVER_CAPA_KEY_MGMT_DPP;
+
+ if (drv->capa.enc & (WPA_DRIVER_CAPA_ENC_CCMP_256 |
+ WPA_DRIVER_CAPA_ENC_GCMP_256))
+ drv->capa.key_mgmt |=
+ WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192;
+
+ if (drv->capa.flags & WPA_DRIVER_FLAGS_SME)
+ drv->capa.key_mgmt |=
+ WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 |
+ WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384 |
+ WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256 |
+ WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384 |
+ WPA_DRIVER_CAPA_KEY_MGMT_SAE;
+ else if (drv->capa.flags & WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD)
+ drv->capa.key_mgmt |=
+ WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 |
+ WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384;
+ }
+
+ if (!info.has_key_mgmt_iftype) {
+ /* If the driver does not advertize per interface AKM
+ * capabilities, consider all interfaces to support default AKMs
+ * in key_mgmt. */
+ for (i = 0; i < WPA_IF_MAX; i++)
+ drv->capa.key_mgmt_iftype[i] = drv->capa.key_mgmt;
+ } else if (info.has_key_mgmt_iftype && !info.has_key_mgmt) {
+ /* If the driver advertizes only per interface supported AKMs
+ * but does not advertize per wiphy AKM capabilities, consider
+ * the default key_mgmt as a mask of per interface supported
+ * AKMs. */
+ drv->capa.key_mgmt = 0;
+ for (i = 0; i < WPA_IF_MAX; i++)
+ drv->capa.key_mgmt |= drv->capa.key_mgmt_iftype[i];
+ } else if (info.has_key_mgmt_iftype && info.has_key_mgmt) {
+ /* If the driver advertizes AKM capabilities both per wiphy and
+ * per interface, consider the interfaces for which per
+ * interface AKM capabilities were not received to support the
+ * default key_mgmt capabilities.
+ */
+ for (i = 0; i < WPA_IF_MAX; i++)
+ if (!drv->capa.key_mgmt_iftype[i])
+ drv->capa.key_mgmt_iftype[i] =
+ drv->capa.key_mgmt;
+ }
+
+ drv->capa.auth = WPA_DRIVER_AUTH_OPEN |
+ WPA_DRIVER_AUTH_SHARED |
+ WPA_DRIVER_AUTH_LEAP;
+
+ drv->capa.flags |= WPA_DRIVER_FLAGS_VALID_ERROR_CODES;
+ drv->capa.flags |= WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE;
+ drv->capa.flags |= WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
+
+ /*
+ * As all cfg80211 drivers must support cases where the AP interface is
+ * removed without the knowledge of wpa_supplicant/hostapd, e.g., in
+ * case that the user space daemon has crashed, they must be able to
+ * cleanup all stations and key entries in the AP tear down flow. Thus,
+ * this flag can/should always be set for cfg80211 drivers.
+ */
+ drv->capa.flags |= WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT;
+
+ if (!info.device_ap_sme) {
+ drv->capa.flags |= WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS;
+ drv->capa.flags2 |= WPA_DRIVER_FLAGS2_AP_SME;
+
+ /*
+ * No AP SME is currently assumed to also indicate no AP MLME
+ * in the driver/firmware.
+ */
+ drv->capa.flags |= WPA_DRIVER_FLAGS_AP_MLME;
+ }
+
+ drv->device_ap_sme = info.device_ap_sme;
+ drv->poll_command_supported = info.poll_command_supported;
+ drv->data_tx_status = info.data_tx_status;
+ drv->p2p_go_ctwindow_supported = info.p2p_go_ctwindow_supported;
+ if (info.set_qos_map_supported)
+ drv->capa.flags |= WPA_DRIVER_FLAGS_QOS_MAPPING;
+ drv->have_low_prio_scan = info.have_low_prio_scan;
+
+ /*
+ * If poll command and tx status are supported, mac80211 is new enough
+ * to have everything we need to not need monitor interfaces.
+ */
+ drv->use_monitor = !info.device_ap_sme &&
+ (!info.poll_command_supported || !info.data_tx_status);
+
+ /*
+ * If we aren't going to use monitor interfaces, but the
+ * driver doesn't support data TX status, we won't get TX
+ * status for EAPOL frames.
+ */
+ if (!drv->use_monitor && !info.data_tx_status)
+ drv->capa.flags &= ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+ if (!(info.capa->flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD))
+ qca_nl80211_check_dfs_capa(drv);
+ qca_nl80211_get_features(drv);
+
+ /*
+ * To enable offchannel simultaneous support in wpa_supplicant, the
+ * underlying driver needs to support the same along with offchannel TX.
+ * Offchannel TX support is needed since remain_on_channel and
+ * action_tx use some common data structures and hence cannot be
+ * scheduled simultaneously.
+ */
+ if (!(drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX))
+ drv->capa.flags &= ~WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS;
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: key_mgmt=0x%x enc=0x%x auth=0x%x flags=0x%llx rrm_flags=0x%x probe_resp_offloads=0x%x max_stations=%u max_remain_on_chan=%u max_scan_ssids=%d",
+ drv->capa.key_mgmt, drv->capa.enc, drv->capa.auth,
+ (unsigned long long) drv->capa.flags, drv->capa.rrm_flags,
+ drv->capa.probe_resp_offloads, drv->capa.max_stations,
+ drv->capa.max_remain_on_chan, drv->capa.max_scan_ssids);
+ return 0;
+}
+
+
+struct phy_info_arg {
+ u16 *num_modes;
+ struct hostapd_hw_modes *modes;
+ int last_mode, last_chan_idx;
+ int failed;
+ u8 dfs_domain;
+};
+
+static void phy_info_ht_capa(struct hostapd_hw_modes *mode, struct nlattr *capa,
+ struct nlattr *ampdu_factor,
+ struct nlattr *ampdu_density,
+ struct nlattr *mcs_set)
+{
+ if (capa)
+ mode->ht_capab = nla_get_u16(capa);
+
+ if (ampdu_factor)
+ mode->a_mpdu_params |= nla_get_u8(ampdu_factor) & 0x03;
+
+ if (ampdu_density)
+ mode->a_mpdu_params |= nla_get_u8(ampdu_density) << 2;
+
+ if (mcs_set && nla_len(mcs_set) >= 16) {
+ u8 *mcs;
+ mcs = nla_data(mcs_set);
+ os_memcpy(mode->mcs_set, mcs, 16);
+ }
+}
+
+
+static void phy_info_vht_capa(struct hostapd_hw_modes *mode,
+ struct nlattr *capa,
+ struct nlattr *mcs_set)
+{
+ if (capa)
+ mode->vht_capab = nla_get_u32(capa);
+
+ if (mcs_set && nla_len(mcs_set) >= 8) {
+ u8 *mcs;
+ mcs = nla_data(mcs_set);
+ os_memcpy(mode->vht_mcs_set, mcs, 8);
+ }
+}
+
+
+static int phy_info_edmg_capa(struct hostapd_hw_modes *mode,
+ struct nlattr *bw_config,
+ struct nlattr *channels)
+{
+ if (!bw_config || !channels)
+ return NL_OK;
+
+ mode->edmg.bw_config = nla_get_u8(bw_config);
+ mode->edmg.channels = nla_get_u8(channels);
+
+ if (!mode->edmg.channels || !mode->edmg.bw_config)
+ return NL_STOP;
+
+ return NL_OK;
+}
+
+
+static int cw2ecw(unsigned int cw)
+{
+ int bit;
+
+ if (cw == 0)
+ return 0;
+
+ for (bit = 1; cw != 1; bit++)
+ cw >>= 1;
+
+ return bit;
+}
+
+
+static void phy_info_freq(struct hostapd_hw_modes *mode,
+ struct hostapd_channel_data *chan,
+ struct nlattr *tb_freq[])
+{
+ u8 channel;
+
+ os_memset(chan, 0, sizeof(*chan));
+ chan->freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
+ chan->flag = 0;
+ chan->allowed_bw = ~0;
+ chan->dfs_cac_ms = 0;
+ if (ieee80211_freq_to_chan(chan->freq, &channel) != NUM_HOSTAPD_MODES)
+ chan->chan = channel;
+ else
+ wpa_printf(MSG_DEBUG,
+ "nl80211: No channel number found for frequency %u MHz",
+ chan->freq);
+
+ if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
+ chan->flag |= HOSTAPD_CHAN_DISABLED;
+ if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IR])
+ chan->flag |= HOSTAPD_CHAN_NO_IR;
+ if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR])
+ chan->flag |= HOSTAPD_CHAN_RADAR;
+ if (tb_freq[NL80211_FREQUENCY_ATTR_INDOOR_ONLY])
+ chan->flag |= HOSTAPD_CHAN_INDOOR_ONLY;
+ if (tb_freq[NL80211_FREQUENCY_ATTR_GO_CONCURRENT])
+ chan->flag |= HOSTAPD_CHAN_GO_CONCURRENT;
+
+ if (tb_freq[NL80211_FREQUENCY_ATTR_NO_10MHZ])
+ chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_10;
+ if (tb_freq[NL80211_FREQUENCY_ATTR_NO_20MHZ])
+ chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_20;
+ if (tb_freq[NL80211_FREQUENCY_ATTR_NO_HT40_PLUS])
+ chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_40P;
+ if (tb_freq[NL80211_FREQUENCY_ATTR_NO_HT40_MINUS])
+ chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_40M;
+ if (tb_freq[NL80211_FREQUENCY_ATTR_NO_80MHZ])
+ chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_80;
+ if (tb_freq[NL80211_FREQUENCY_ATTR_NO_160MHZ])
+ chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_160;
+
+ if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]) {
+ enum nl80211_dfs_state state =
+ nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]);
+
+ switch (state) {
+ case NL80211_DFS_USABLE:
+ chan->flag |= HOSTAPD_CHAN_DFS_USABLE;
+ break;
+ case NL80211_DFS_AVAILABLE:
+ chan->flag |= HOSTAPD_CHAN_DFS_AVAILABLE;
+ break;
+ case NL80211_DFS_UNAVAILABLE:
+ chan->flag |= HOSTAPD_CHAN_DFS_UNAVAILABLE;
+ break;
+ }
+ }
+
+ if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_CAC_TIME]) {
+ chan->dfs_cac_ms = nla_get_u32(
+ tb_freq[NL80211_FREQUENCY_ATTR_DFS_CAC_TIME]);
+ }
+
+ chan->wmm_rules_valid = 0;
+ if (tb_freq[NL80211_FREQUENCY_ATTR_WMM]) {
+ static struct nla_policy wmm_policy[NL80211_WMMR_MAX + 1] = {
+ [NL80211_WMMR_CW_MIN] = { .type = NLA_U16 },
+ [NL80211_WMMR_CW_MAX] = { .type = NLA_U16 },
+ [NL80211_WMMR_AIFSN] = { .type = NLA_U8 },
+ [NL80211_WMMR_TXOP] = { .type = NLA_U16 },
+ };
+ static const u8 wmm_map[4] = {
+ [NL80211_AC_BE] = WMM_AC_BE,
+ [NL80211_AC_BK] = WMM_AC_BK,
+ [NL80211_AC_VI] = WMM_AC_VI,
+ [NL80211_AC_VO] = WMM_AC_VO,
+ };
+ struct nlattr *nl_wmm;
+ struct nlattr *tb_wmm[NL80211_WMMR_MAX + 1];
+ int rem_wmm, ac, count = 0;
+
+ nla_for_each_nested(nl_wmm, tb_freq[NL80211_FREQUENCY_ATTR_WMM],
+ rem_wmm) {
+ if (nla_parse_nested(tb_wmm, NL80211_WMMR_MAX, nl_wmm,
+ wmm_policy)) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Failed to parse WMM rules attribute");
+ return;
+ }
+ if (!tb_wmm[NL80211_WMMR_CW_MIN] ||
+ !tb_wmm[NL80211_WMMR_CW_MAX] ||
+ !tb_wmm[NL80211_WMMR_AIFSN] ||
+ !tb_wmm[NL80211_WMMR_TXOP]) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Channel is missing WMM rule attribute");
+ return;
+ }
+ ac = nl_wmm->nla_type;
+ if ((unsigned int) ac >= ARRAY_SIZE(wmm_map)) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Invalid AC value %d", ac);
+ return;
+ }
+
+ ac = wmm_map[ac];
+ chan->wmm_rules[ac].min_cwmin =
+ cw2ecw(nla_get_u16(
+ tb_wmm[NL80211_WMMR_CW_MIN]));
+ chan->wmm_rules[ac].min_cwmax =
+ cw2ecw(nla_get_u16(
+ tb_wmm[NL80211_WMMR_CW_MAX]));
+ chan->wmm_rules[ac].min_aifs =
+ nla_get_u8(tb_wmm[NL80211_WMMR_AIFSN]);
+ chan->wmm_rules[ac].max_txop =
+ nla_get_u16(tb_wmm[NL80211_WMMR_TXOP]) / 32;
+ count++;
+ }
+
+ /* Set valid flag if all the AC rules are present */
+ if (count == WMM_AC_NUM)
+ chan->wmm_rules_valid = 1;
+ }
+}
+
+
+static int phy_info_freqs(struct phy_info_arg *phy_info,
+ struct hostapd_hw_modes *mode, struct nlattr *tb)
+{
+ 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_NO_IR] = { .type = NLA_FLAG },
+ [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG },
+ [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 },
+ [NL80211_FREQUENCY_ATTR_DFS_STATE] = { .type = NLA_U32 },
+ [NL80211_FREQUENCY_ATTR_NO_10MHZ] = { .type = NLA_FLAG },
+ [NL80211_FREQUENCY_ATTR_NO_20MHZ] = { .type = NLA_FLAG },
+ [NL80211_FREQUENCY_ATTR_NO_HT40_PLUS] = { .type = NLA_FLAG },
+ [NL80211_FREQUENCY_ATTR_NO_HT40_MINUS] = { .type = NLA_FLAG },
+ [NL80211_FREQUENCY_ATTR_NO_80MHZ] = { .type = NLA_FLAG },
+ [NL80211_FREQUENCY_ATTR_NO_160MHZ] = { .type = NLA_FLAG },
+ };
+ int new_channels = 0;
+ struct hostapd_channel_data *channel;
+ struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1];
+ struct nlattr *nl_freq;
+ int rem_freq, idx;
+
+ if (tb == NULL)
+ return NL_OK;
+
+ nla_for_each_nested(nl_freq, tb, 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;
+ new_channels++;
+ }
+
+ channel = os_realloc_array(mode->channels,
+ mode->num_channels + new_channels,
+ sizeof(struct hostapd_channel_data));
+ if (!channel)
+ return NL_STOP;
+
+ mode->channels = channel;
+ mode->num_channels += new_channels;
+
+ idx = phy_info->last_chan_idx;
+
+ nla_for_each_nested(nl_freq, tb, 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;
+ phy_info_freq(mode, &mode->channels[idx], tb_freq);
+ idx++;
+ }
+ phy_info->last_chan_idx = idx;
+
+ return NL_OK;
+}
+
+
+static int phy_info_rates(struct hostapd_hw_modes *mode, struct nlattr *tb)
+{
+ 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 *tb_rate[NL80211_BITRATE_ATTR_MAX + 1];
+ struct nlattr *nl_rate;
+ int rem_rate, idx;
+
+ if (tb == NULL)
+ return NL_OK;
+
+ nla_for_each_nested(nl_rate, tb, 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_calloc(mode->num_rates, sizeof(int));
+ if (!mode->rates)
+ return NL_STOP;
+
+ idx = 0;
+
+ nla_for_each_nested(nl_rate, tb, 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]);
+ idx++;
+ }
+
+ return NL_OK;
+}
+
+
+static void phy_info_iftype_copy(struct hostapd_hw_modes *mode,
+ enum ieee80211_op_mode opmode,
+ struct nlattr **tb, struct nlattr **tb_flags)
+{
+ enum nl80211_iftype iftype;
+ size_t len;
+ struct he_capabilities *he_capab = &mode->he_capab[opmode];
+ struct eht_capabilities *eht_capab = &mode->eht_capab[opmode];
+
+ switch (opmode) {
+ case IEEE80211_MODE_INFRA:
+ iftype = NL80211_IFTYPE_STATION;
+ break;
+ case IEEE80211_MODE_IBSS:
+ iftype = NL80211_IFTYPE_ADHOC;
+ break;
+ case IEEE80211_MODE_AP:
+ iftype = NL80211_IFTYPE_AP;
+ break;
+ case IEEE80211_MODE_MESH:
+ iftype = NL80211_IFTYPE_MESH_POINT;
+ break;
+ default:
+ return;
+ }
+
+ if (!nla_get_flag(tb_flags[iftype]))
+ return;
+
+ he_capab->he_supported = 1;
+
+ if (tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY]) {
+ len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY]);
+
+ if (len > sizeof(he_capab->phy_cap))
+ len = sizeof(he_capab->phy_cap);
+ os_memcpy(he_capab->phy_cap,
+ nla_data(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY]),
+ len);
+ }
+
+ if (tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC]) {
+ len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC]);
+
+ if (len > sizeof(he_capab->mac_cap))
+ len = sizeof(he_capab->mac_cap);
+ os_memcpy(he_capab->mac_cap,
+ nla_data(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC]),
+ len);
+ }
+
+ if (tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET]) {
+ len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET]);
+
+ if (len > sizeof(he_capab->mcs))
+ len = sizeof(he_capab->mcs);
+ os_memcpy(he_capab->mcs,
+ nla_data(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET]),
+ len);
+ }
+
+ if (tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE]) {
+ len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE]);
+
+ if (len > sizeof(he_capab->ppet))
+ len = sizeof(he_capab->ppet);
+ os_memcpy(&he_capab->ppet,
+ nla_data(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE]),
+ len);
+ }
+
+ if (tb[NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA]) {
+ u16 capa;
+
+ capa = nla_get_u16(tb[NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA]);
+ he_capab->he_6ghz_capa = le_to_host16(capa);
+ }
+
+ if (!tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC] ||
+ !tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY])
+ return;
+
+ eht_capab->eht_supported = true;
+
+ if (tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC] &&
+ nla_len(tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC]) >= 2) {
+ const u8 *pos;
+
+ pos = nla_data(tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC]);
+ eht_capab->mac_cap = WPA_GET_LE16(pos);
+ }
+
+ if (tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY]) {
+ len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY]);
+ if (len > sizeof(eht_capab->phy_cap))
+ len = sizeof(eht_capab->phy_cap);
+ os_memcpy(eht_capab->phy_cap,
+ nla_data(tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY]),
+ len);
+ }
+
+ if (tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MCS_SET]) {
+ len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MCS_SET]);
+ if (len > sizeof(eht_capab->mcs))
+ len = sizeof(eht_capab->mcs);
+ os_memcpy(eht_capab->mcs,
+ nla_data(tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MCS_SET]),
+ len);
+ }
+
+ if (tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PPE]) {
+ len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PPE]);
+ if (len > sizeof(eht_capab->ppet))
+ len = sizeof(eht_capab->ppet);
+ os_memcpy(&eht_capab->ppet,
+ nla_data(tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PPE]),
+ len);
+ }
+}
+
+
+static int phy_info_iftype(struct hostapd_hw_modes *mode,
+ struct nlattr *nl_iftype)
+{
+ struct nlattr *tb[NL80211_BAND_IFTYPE_ATTR_MAX + 1];
+ struct nlattr *tb_flags[NL80211_IFTYPE_MAX + 1];
+ unsigned int i;
+
+ nla_parse(tb, NL80211_BAND_IFTYPE_ATTR_MAX,
+ nla_data(nl_iftype), nla_len(nl_iftype), NULL);
+
+ if (!tb[NL80211_BAND_IFTYPE_ATTR_IFTYPES])
+ return NL_STOP;
+
+ if (nla_parse_nested(tb_flags, NL80211_IFTYPE_MAX,
+ tb[NL80211_BAND_IFTYPE_ATTR_IFTYPES], NULL))
+ return NL_STOP;
+
+ for (i = 0; i < IEEE80211_MODE_NUM; i++)
+ phy_info_iftype_copy(mode, i, tb, tb_flags);
+
+ return NL_OK;
+}
+
+
+static int phy_info_band(struct phy_info_arg *phy_info, struct nlattr *nl_band)
+{
+ struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1];
+ struct hostapd_hw_modes *mode;
+ int ret;
+
+ if (phy_info->last_mode != nl_band->nla_type) {
+ mode = os_realloc_array(phy_info->modes,
+ *phy_info->num_modes + 1,
+ sizeof(*mode));
+ if (!mode) {
+ phy_info->failed = 1;
+ return NL_STOP;
+ }
+ phy_info->modes = mode;
+
+ mode = &phy_info->modes[*(phy_info->num_modes)];
+ os_memset(mode, 0, sizeof(*mode));
+ mode->mode = NUM_HOSTAPD_MODES;
+ mode->flags = HOSTAPD_MODE_FLAG_HT_INFO_KNOWN |
+ HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN |
+ HOSTAPD_MODE_FLAG_HE_INFO_KNOWN;
+
+ /*
+ * Unsupported VHT MCS stream is defined as value 3, so the VHT
+ * MCS RX/TX map must be initialized with 0xffff to mark all 8
+ * possible streams as unsupported. This will be overridden if
+ * driver advertises VHT support.
+ */
+ mode->vht_mcs_set[0] = 0xff;
+ mode->vht_mcs_set[1] = 0xff;
+ mode->vht_mcs_set[4] = 0xff;
+ mode->vht_mcs_set[5] = 0xff;
+
+ *(phy_info->num_modes) += 1;
+ phy_info->last_mode = nl_band->nla_type;
+ phy_info->last_chan_idx = 0;
+ } else
+ mode = &phy_info->modes[*(phy_info->num_modes) - 1];
+
+ nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band),
+ nla_len(nl_band), NULL);
+
+ phy_info_ht_capa(mode, tb_band[NL80211_BAND_ATTR_HT_CAPA],
+ tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR],
+ tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY],
+ tb_band[NL80211_BAND_ATTR_HT_MCS_SET]);
+ phy_info_vht_capa(mode, tb_band[NL80211_BAND_ATTR_VHT_CAPA],
+ tb_band[NL80211_BAND_ATTR_VHT_MCS_SET]);
+ ret = phy_info_edmg_capa(mode,
+ tb_band[NL80211_BAND_ATTR_EDMG_BW_CONFIG],
+ tb_band[NL80211_BAND_ATTR_EDMG_CHANNELS]);
+ if (ret == NL_OK)
+ ret = phy_info_freqs(phy_info, mode,
+ tb_band[NL80211_BAND_ATTR_FREQS]);
+ if (ret == NL_OK)
+ ret = phy_info_rates(mode, tb_band[NL80211_BAND_ATTR_RATES]);
+ if (ret != NL_OK) {
+ phy_info->failed = 1;
+ return ret;
+ }
+
+ if (tb_band[NL80211_BAND_ATTR_IFTYPE_DATA]) {
+ struct nlattr *nl_iftype;
+ int rem_band;
+
+ nla_for_each_nested(nl_iftype,
+ tb_band[NL80211_BAND_ATTR_IFTYPE_DATA],
+ rem_band) {
+ ret = phy_info_iftype(mode, nl_iftype);
+ if (ret != NL_OK)
+ return ret;
+ }
+ }
+
+ return NL_OK;
+}
+
+
+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 *nl_band;
+ int rem_band;
+
+ 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)
+ {
+ int res = phy_info_band(phy_info, nl_band);
+ if (res != NL_OK)
+ return res;
+ }
+
+ return NL_SKIP;
+}
+
+
+static struct hostapd_hw_modes *
+wpa_driver_nl80211_postprocess_modes(struct hostapd_hw_modes *modes,
+ u16 *num_modes)
+{
+ u16 m;
+ struct hostapd_hw_modes *mode11g = NULL, *nmodes, *mode;
+ int i, mode11g_idx = -1;
+
+ /* heuristic to set up modes */
+ for (m = 0; m < *num_modes; m++) {
+ if (!modes[m].num_channels)
+ continue;
+ if (modes[m].channels[0].freq < 2000) {
+ modes[m].num_channels = 0;
+ continue;
+ } else if (modes[m].channels[0].freq < 4000) {
+ modes[m].mode = HOSTAPD_MODE_IEEE80211B;
+ for (i = 0; i < modes[m].num_rates; i++) {
+ if (modes[m].rates[i] > 200) {
+ modes[m].mode = HOSTAPD_MODE_IEEE80211G;
+ break;
+ }
+ }
+ } else if (modes[m].channels[0].freq > 50000)
+ modes[m].mode = HOSTAPD_MODE_IEEE80211AD;
+ else
+ modes[m].mode = HOSTAPD_MODE_IEEE80211A;
+ }
+
+ /* Remove unsupported bands */
+ m = 0;
+ while (m < *num_modes) {
+ if (modes[m].mode == NUM_HOSTAPD_MODES) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Remove unsupported mode");
+ os_free(modes[m].channels);
+ os_free(modes[m].rates);
+ if (m + 1 < *num_modes)
+ os_memmove(&modes[m], &modes[m + 1],
+ sizeof(struct hostapd_hw_modes) *
+ (*num_modes - (m + 1)));
+ (*num_modes)--;
+ continue;
+ }
+ m++;
+ }
+
+ /* 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_array(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_memdup(mode11g->channels,
+ mode11g->num_channels *
+ sizeof(struct hostapd_channel_data));
+ if (mode->channels == NULL) {
+ (*num_modes)--;
+ return modes; /* Could not add 802.11b mode */
+ }
+
+ 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 */
+ }
+
+ /* channel 14 is only for IEEE 802.11b */
+ for (i = 0; i < mode11g->num_channels; i++) {
+ if (mode11g->channels[i].freq == 2484) {
+ mode11g->channels[i].flag |= HOSTAPD_CHAN_DISABLED;
+ break;
+ }
+ }
+
+ 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_max_eirp(u32 start, u32 end, u32 max_eirp,
+ struct phy_info_arg *results)
+{
+ u16 m;
+
+ for (m = 0; m < *results->num_modes; m++) {
+ int c;
+ struct hostapd_hw_modes *mode = &results->modes[m];
+
+ for (c = 0; c < mode->num_channels; c++) {
+ struct hostapd_channel_data *chan = &mode->channels[c];
+ if ((u32) chan->freq - 10 >= start &&
+ (u32) chan->freq + 10 <= end)
+ chan->max_tx_power = max_eirp;
+ }
+ }
+}
+
+
+static void nl80211_reg_rule_ht40(u32 start, u32 end,
+ struct phy_info_arg *results)
+{
+ u16 m;
+
+ 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 void nl80211_set_vht_mode(struct hostapd_hw_modes *mode, int start,
+ int end, int max_bw)
+{
+ 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)
+ continue;
+
+ if (max_bw >= 80)
+ chan->flag |= HOSTAPD_CHAN_VHT_80MHZ_SUBCHANNEL;
+
+ if (max_bw >= 160)
+ chan->flag |= HOSTAPD_CHAN_VHT_160MHZ_SUBCHANNEL;
+ }
+}
+
+
+static void nl80211_reg_rule_vht(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 < 80)
+ return;
+
+ for (m = 0; m < *results->num_modes; m++) {
+ if (!(results->modes[m].ht_capab &
+ HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
+ continue;
+ /* TODO: use a real VHT support indication */
+ if (!results->modes[m].vht_capab)
+ continue;
+
+ nl80211_set_vht_mode(&results->modes[m], start, end, max_bw);
+ }
+}
+
+
+static void nl80211_set_dfs_domain(enum nl80211_dfs_regions region,
+ u8 *dfs_domain)
+{
+ if (region == NL80211_DFS_FCC)
+ *dfs_domain = HOSTAPD_DFS_REGION_FCC;
+ else if (region == NL80211_DFS_ETSI)
+ *dfs_domain = HOSTAPD_DFS_REGION_ETSI;
+ else if (region == NL80211_DFS_JP)
+ *dfs_domain = HOSTAPD_DFS_REGION_JP;
+ else
+ *dfs_domain = 0;
+}
+
+
+static const char * dfs_domain_name(enum nl80211_dfs_regions region)
+{
+ switch (region) {
+ case NL80211_DFS_UNSET:
+ return "DFS-UNSET";
+ case NL80211_DFS_FCC:
+ return "DFS-FCC";
+ case NL80211_DFS_ETSI:
+ return "DFS-ETSI";
+ case NL80211_DFS_JP:
+ return "DFS-JP";
+ default:
+ return "DFS-invalid";
+ }
+}
+
+
+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;
+ }
+
+ if (tb_msg[NL80211_ATTR_DFS_REGION]) {
+ enum nl80211_dfs_regions dfs_domain;
+ dfs_domain = nla_get_u8(tb_msg[NL80211_ATTR_DFS_REGION]);
+ nl80211_set_dfs_domain(dfs_domain, &results->dfs_domain);
+ wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s (%s)",
+ (char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]),
+ dfs_domain_name(dfs_domain));
+ } else {
+ 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)
+ {
+ u32 start, end, max_eirp = 0, max_bw = 0, flags = 0;
+ nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX,
+ nla_data(nl_rule), nla_len(nl_rule), reg_policy);
+ if (tb_rule[NL80211_ATTR_FREQ_RANGE_START] == NULL ||
+ tb_rule[NL80211_ATTR_FREQ_RANGE_END] == NULL)
+ continue;
+ start = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_START]) / 1000;
+ end = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_END]) / 1000;
+ if (tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP])
+ max_eirp = nla_get_u32(tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP]) / 100;
+ if (tb_rule[NL80211_ATTR_FREQ_RANGE_MAX_BW])
+ max_bw = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000;
+ if (tb_rule[NL80211_ATTR_REG_RULE_FLAGS])
+ flags = nla_get_u32(tb_rule[NL80211_ATTR_REG_RULE_FLAGS]);
+
+ wpa_printf(MSG_DEBUG, "nl80211: %u-%u @ %u MHz %u mBm%s%s%s%s%s%s%s%s",
+ start, end, max_bw, max_eirp,
+ flags & NL80211_RRF_NO_OFDM ? " (no OFDM)" : "",
+ flags & NL80211_RRF_NO_CCK ? " (no CCK)" : "",
+ flags & NL80211_RRF_NO_INDOOR ? " (no indoor)" : "",
+ flags & NL80211_RRF_NO_OUTDOOR ? " (no outdoor)" :
+ "",
+ flags & NL80211_RRF_DFS ? " (DFS)" : "",
+ flags & NL80211_RRF_PTP_ONLY ? " (PTP only)" : "",
+ flags & NL80211_RRF_PTMP_ONLY ? " (PTMP only)" : "",
+ flags & NL80211_RRF_NO_IR ? " (no IR)" : "");
+ if (max_bw >= 40)
+ nl80211_reg_rule_ht40(start, end, results);
+ if (tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP])
+ nl80211_reg_rule_max_eirp(start, end, max_eirp,
+ 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);
+ }
+
+ 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_vht(tb_rule, results);
+ }
+
+ return NL_SKIP;
+}
+
+
+static int nl80211_set_regulatory_flags(struct wpa_driver_nl80211_data *drv,
+ struct phy_info_arg *results)
+{
+ struct nl_msg *msg;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG);
+ if (drv->capa.flags & WPA_DRIVER_FLAGS_SELF_MANAGED_REGULATORY) {
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, drv->wiphy_idx)) {
+ nlmsg_free(msg);
+ return -1;
+ }
+ }
+
+ return send_and_recv_msgs(drv, msg, nl80211_get_reg, results,
+ NULL, NULL);
+}
+
+
+static const char * modestr(enum hostapd_hw_mode mode)
+{
+ switch (mode) {
+ case HOSTAPD_MODE_IEEE80211B:
+ return "802.11b";
+ case HOSTAPD_MODE_IEEE80211G:
+ return "802.11g";
+ case HOSTAPD_MODE_IEEE80211A:
+ return "802.11a";
+ case HOSTAPD_MODE_IEEE80211AD:
+ return "802.11ad";
+ default:
+ return "?";
+ }
+}
+
+
+static void nl80211_dump_chan_list(struct wpa_driver_nl80211_data *drv,
+ struct hostapd_hw_modes *modes,
+ u16 num_modes)
+{
+ int i;
+
+ if (!modes)
+ return;
+
+ for (i = 0; i < num_modes; i++) {
+ struct hostapd_hw_modes *mode = &modes[i];
+ char str[1000];
+ char *pos = str;
+ char *end = pos + sizeof(str);
+ int j, res;
+
+ for (j = 0; j < mode->num_channels; j++) {
+ struct hostapd_channel_data *chan = &mode->channels[j];
+
+ if (chan->freq >= 5925 && chan->freq <= 7125 &&
+ !(chan->flag & HOSTAPD_CHAN_DISABLED))
+ drv->uses_6ghz = true;
+ res = os_snprintf(pos, end - pos, " %d%s%s%s",
+ chan->freq,
+ (chan->flag & HOSTAPD_CHAN_DISABLED) ?
+ "[DISABLED]" : "",
+ (chan->flag & HOSTAPD_CHAN_NO_IR) ?
+ "[NO_IR]" : "",
+ (chan->flag & HOSTAPD_CHAN_RADAR) ?
+ "[RADAR]" : "");
+ if (os_snprintf_error(end - pos, res))
+ break;
+ pos += res;
+ }
+
+ *pos = '\0';
+ wpa_printf(MSG_DEBUG, "nl80211: Mode IEEE %s:%s",
+ modestr(mode->mode), str);
+ }
+}
+
+
+struct hostapd_hw_modes *
+nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags,
+ u8 *dfs_domain)
+{
+ u32 feat;
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int nl_flags = 0;
+ struct nl_msg *msg;
+ struct phy_info_arg result = {
+ .num_modes = num_modes,
+ .modes = NULL,
+ .last_mode = -1,
+ .failed = 0,
+ .dfs_domain = 0,
+ };
+
+ *num_modes = 0;
+ *flags = 0;
+ *dfs_domain = 0;
+
+ feat = get_nl80211_protocol_features(drv);
+ if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP)
+ nl_flags = NLM_F_DUMP;
+ if (!(msg = nl80211_cmd_msg(bss, nl_flags, NL80211_CMD_GET_WIPHY)) ||
+ nla_put_flag(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP)) {
+ nlmsg_free(msg);
+ return NULL;
+ }
+
+ if (send_and_recv_msgs(drv, msg, phy_info_handler, &result,
+ NULL, NULL) == 0) {
+ struct hostapd_hw_modes *modes;
+
+ nl80211_set_regulatory_flags(drv, &result);
+ if (result.failed) {
+ int i;
+
+ for (i = 0; result.modes && i < *num_modes; i++) {
+ os_free(result.modes[i].channels);
+ os_free(result.modes[i].rates);
+ }
+ os_free(result.modes);
+ *num_modes = 0;
+ return NULL;
+ }
+
+ *dfs_domain = result.dfs_domain;
+
+ modes = wpa_driver_nl80211_postprocess_modes(result.modes,
+ num_modes);
+ nl80211_dump_chan_list(drv, modes, *num_modes);
+ return modes;
+ }
+
+ return NULL;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_nl80211_event.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_nl80211_event.c
new file mode 100644
index 0000000..3e8ace0
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_nl80211_event.c
@@ -0,0 +1,4077 @@
+/*
+ * Driver interaction with Linux nl80211/cfg80211 - Event processing
+ * Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <netlink/genl/genl.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/qca-vendor.h"
+#include "common/qca-vendor-attr.h"
+#include "common/brcm_vendor.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "driver_nl80211.h"
+
+
+static void
+nl80211_control_port_frame_tx_status(struct wpa_driver_nl80211_data *drv,
+ const u8 *frame, size_t len,
+ struct nlattr *ack, struct nlattr *cookie);
+
+
+static const char * nl80211_command_to_string(enum nl80211_commands cmd)
+{
+#define C2S(x) case x: return #x;
+ switch (cmd) {
+ C2S(NL80211_CMD_UNSPEC)
+ C2S(NL80211_CMD_GET_WIPHY)
+ C2S(NL80211_CMD_SET_WIPHY)
+ C2S(NL80211_CMD_NEW_WIPHY)
+ C2S(NL80211_CMD_DEL_WIPHY)
+ C2S(NL80211_CMD_GET_INTERFACE)
+ C2S(NL80211_CMD_SET_INTERFACE)
+ C2S(NL80211_CMD_NEW_INTERFACE)
+ C2S(NL80211_CMD_DEL_INTERFACE)
+ C2S(NL80211_CMD_GET_KEY)
+ C2S(NL80211_CMD_SET_KEY)
+ C2S(NL80211_CMD_NEW_KEY)
+ C2S(NL80211_CMD_DEL_KEY)
+ C2S(NL80211_CMD_GET_BEACON)
+ C2S(NL80211_CMD_SET_BEACON)
+ C2S(NL80211_CMD_START_AP)
+ C2S(NL80211_CMD_STOP_AP)
+ C2S(NL80211_CMD_GET_STATION)
+ C2S(NL80211_CMD_SET_STATION)
+ C2S(NL80211_CMD_NEW_STATION)
+ C2S(NL80211_CMD_DEL_STATION)
+ C2S(NL80211_CMD_GET_MPATH)
+ C2S(NL80211_CMD_SET_MPATH)
+ C2S(NL80211_CMD_NEW_MPATH)
+ C2S(NL80211_CMD_DEL_MPATH)
+ C2S(NL80211_CMD_SET_BSS)
+ C2S(NL80211_CMD_SET_REG)
+ C2S(NL80211_CMD_REQ_SET_REG)
+ C2S(NL80211_CMD_GET_MESH_CONFIG)
+ C2S(NL80211_CMD_SET_MESH_CONFIG)
+ C2S(NL80211_CMD_SET_MGMT_EXTRA_IE)
+ C2S(NL80211_CMD_GET_REG)
+ C2S(NL80211_CMD_GET_SCAN)
+ C2S(NL80211_CMD_TRIGGER_SCAN)
+ C2S(NL80211_CMD_NEW_SCAN_RESULTS)
+ C2S(NL80211_CMD_SCAN_ABORTED)
+ C2S(NL80211_CMD_REG_CHANGE)
+ C2S(NL80211_CMD_AUTHENTICATE)
+ C2S(NL80211_CMD_ASSOCIATE)
+ C2S(NL80211_CMD_DEAUTHENTICATE)
+ C2S(NL80211_CMD_DISASSOCIATE)
+ C2S(NL80211_CMD_MICHAEL_MIC_FAILURE)
+ C2S(NL80211_CMD_REG_BEACON_HINT)
+ C2S(NL80211_CMD_JOIN_IBSS)
+ C2S(NL80211_CMD_LEAVE_IBSS)
+ C2S(NL80211_CMD_TESTMODE)
+ C2S(NL80211_CMD_CONNECT)
+ C2S(NL80211_CMD_ROAM)
+ C2S(NL80211_CMD_DISCONNECT)
+ C2S(NL80211_CMD_SET_WIPHY_NETNS)
+ C2S(NL80211_CMD_GET_SURVEY)
+ C2S(NL80211_CMD_NEW_SURVEY_RESULTS)
+ C2S(NL80211_CMD_SET_PMKSA)
+ C2S(NL80211_CMD_DEL_PMKSA)
+ C2S(NL80211_CMD_FLUSH_PMKSA)
+ C2S(NL80211_CMD_REMAIN_ON_CHANNEL)
+ C2S(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL)
+ C2S(NL80211_CMD_SET_TX_BITRATE_MASK)
+ C2S(NL80211_CMD_REGISTER_FRAME)
+ C2S(NL80211_CMD_FRAME)
+ C2S(NL80211_CMD_FRAME_TX_STATUS)
+ C2S(NL80211_CMD_SET_POWER_SAVE)
+ C2S(NL80211_CMD_GET_POWER_SAVE)
+ C2S(NL80211_CMD_SET_CQM)
+ C2S(NL80211_CMD_NOTIFY_CQM)
+ C2S(NL80211_CMD_SET_CHANNEL)
+ C2S(NL80211_CMD_SET_WDS_PEER)
+ C2S(NL80211_CMD_FRAME_WAIT_CANCEL)
+ C2S(NL80211_CMD_JOIN_MESH)
+ C2S(NL80211_CMD_LEAVE_MESH)
+ C2S(NL80211_CMD_UNPROT_DEAUTHENTICATE)
+ C2S(NL80211_CMD_UNPROT_DISASSOCIATE)
+ C2S(NL80211_CMD_NEW_PEER_CANDIDATE)
+ C2S(NL80211_CMD_GET_WOWLAN)
+ C2S(NL80211_CMD_SET_WOWLAN)
+ C2S(NL80211_CMD_START_SCHED_SCAN)
+ C2S(NL80211_CMD_STOP_SCHED_SCAN)
+ C2S(NL80211_CMD_SCHED_SCAN_RESULTS)
+ C2S(NL80211_CMD_SCHED_SCAN_STOPPED)
+ C2S(NL80211_CMD_SET_REKEY_OFFLOAD)
+ C2S(NL80211_CMD_PMKSA_CANDIDATE)
+ C2S(NL80211_CMD_TDLS_OPER)
+ C2S(NL80211_CMD_TDLS_MGMT)
+ C2S(NL80211_CMD_UNEXPECTED_FRAME)
+ C2S(NL80211_CMD_PROBE_CLIENT)
+ C2S(NL80211_CMD_REGISTER_BEACONS)
+ C2S(NL80211_CMD_UNEXPECTED_4ADDR_FRAME)
+ C2S(NL80211_CMD_SET_NOACK_MAP)
+ C2S(NL80211_CMD_CH_SWITCH_NOTIFY)
+ C2S(NL80211_CMD_START_P2P_DEVICE)
+ C2S(NL80211_CMD_STOP_P2P_DEVICE)
+ C2S(NL80211_CMD_CONN_FAILED)
+ C2S(NL80211_CMD_SET_MCAST_RATE)
+ C2S(NL80211_CMD_SET_MAC_ACL)
+ C2S(NL80211_CMD_RADAR_DETECT)
+ C2S(NL80211_CMD_GET_PROTOCOL_FEATURES)
+ C2S(NL80211_CMD_UPDATE_FT_IES)
+ C2S(NL80211_CMD_FT_EVENT)
+ C2S(NL80211_CMD_CRIT_PROTOCOL_START)
+ C2S(NL80211_CMD_CRIT_PROTOCOL_STOP)
+ C2S(NL80211_CMD_GET_COALESCE)
+ C2S(NL80211_CMD_SET_COALESCE)
+ C2S(NL80211_CMD_CHANNEL_SWITCH)
+ C2S(NL80211_CMD_VENDOR)
+ C2S(NL80211_CMD_SET_QOS_MAP)
+ C2S(NL80211_CMD_ADD_TX_TS)
+ C2S(NL80211_CMD_DEL_TX_TS)
+ C2S(NL80211_CMD_GET_MPP)
+ C2S(NL80211_CMD_JOIN_OCB)
+ C2S(NL80211_CMD_LEAVE_OCB)
+ C2S(NL80211_CMD_CH_SWITCH_STARTED_NOTIFY)
+ C2S(NL80211_CMD_TDLS_CHANNEL_SWITCH)
+ C2S(NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH)
+ C2S(NL80211_CMD_WIPHY_REG_CHANGE)
+ C2S(NL80211_CMD_ABORT_SCAN)
+ C2S(NL80211_CMD_START_NAN)
+ C2S(NL80211_CMD_STOP_NAN)
+ C2S(NL80211_CMD_ADD_NAN_FUNCTION)
+ C2S(NL80211_CMD_DEL_NAN_FUNCTION)
+ C2S(NL80211_CMD_CHANGE_NAN_CONFIG)
+ C2S(NL80211_CMD_NAN_MATCH)
+ C2S(NL80211_CMD_SET_MULTICAST_TO_UNICAST)
+ C2S(NL80211_CMD_UPDATE_CONNECT_PARAMS)
+ C2S(NL80211_CMD_SET_PMK)
+ C2S(NL80211_CMD_DEL_PMK)
+ C2S(NL80211_CMD_PORT_AUTHORIZED)
+ C2S(NL80211_CMD_RELOAD_REGDB)
+ C2S(NL80211_CMD_EXTERNAL_AUTH)
+ C2S(NL80211_CMD_STA_OPMODE_CHANGED)
+ C2S(NL80211_CMD_CONTROL_PORT_FRAME)
+ C2S(NL80211_CMD_GET_FTM_RESPONDER_STATS)
+ C2S(NL80211_CMD_PEER_MEASUREMENT_START)
+ C2S(NL80211_CMD_PEER_MEASUREMENT_RESULT)
+ C2S(NL80211_CMD_PEER_MEASUREMENT_COMPLETE)
+ C2S(NL80211_CMD_NOTIFY_RADAR)
+ C2S(NL80211_CMD_UPDATE_OWE_INFO)
+ C2S(NL80211_CMD_PROBE_MESH_LINK)
+ C2S(NL80211_CMD_SET_TID_CONFIG)
+ C2S(NL80211_CMD_UNPROT_BEACON)
+ C2S(NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS)
+ C2S(NL80211_CMD_SET_SAR_SPECS)
+ C2S(NL80211_CMD_OBSS_COLOR_COLLISION)
+ C2S(NL80211_CMD_COLOR_CHANGE_REQUEST)
+ C2S(NL80211_CMD_COLOR_CHANGE_STARTED)
+ C2S(NL80211_CMD_COLOR_CHANGE_ABORTED)
+ C2S(NL80211_CMD_COLOR_CHANGE_COMPLETED)
+ C2S(NL80211_CMD_SET_FILS_AAD)
+ C2S(NL80211_CMD_ASSOC_COMEBACK)
+ C2S(NL80211_CMD_ADD_LINK)
+ C2S(NL80211_CMD_REMOVE_LINK)
+ C2S(NL80211_CMD_ADD_LINK_STA)
+ C2S(NL80211_CMD_MODIFY_LINK_STA)
+ C2S(NL80211_CMD_REMOVE_LINK_STA)
+ C2S(NL80211_CMD_SET_HW_TIMESTAMP)
+ C2S(__NL80211_CMD_AFTER_LAST)
+ }
+#undef C2S
+
+ return "NL80211_CMD_UNKNOWN";
+}
+
+
+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;
+
+ if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) &&
+ drv->force_connect_cmd) {
+ /*
+ * Avoid reporting two association events that would confuse
+ * the core code.
+ */
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Ignore auth event when using driver SME");
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "nl80211: Authenticate 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(drv->auth_attempt_bssid, 0, 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.auth_transaction =
+ le_to_host16(mgmt->u.auth.auth_transaction);
+ 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 nl80211_parse_wmm_params(struct nlattr *wmm_attr,
+ struct wmm_params *wmm_params)
+{
+ struct nlattr *wmm_info[NL80211_STA_WME_MAX + 1];
+ static struct nla_policy wme_policy[NL80211_STA_WME_MAX + 1] = {
+ [NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 },
+ };
+
+ if (!wmm_attr ||
+ nla_parse_nested(wmm_info, NL80211_STA_WME_MAX, wmm_attr,
+ wme_policy) ||
+ !wmm_info[NL80211_STA_WME_UAPSD_QUEUES])
+ return;
+
+ wmm_params->uapsd_queues =
+ nla_get_u8(wmm_info[NL80211_STA_WME_UAPSD_QUEUES]);
+ wmm_params->info_bitmap |= WMM_PARAMS_UAPSD_QUEUES_INFO;
+}
+
+
+static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
+ const u8 *frame, size_t len, struct nlattr *wmm,
+ struct nlattr *req_ie)
+{
+ const struct ieee80211_mgmt *mgmt;
+ union wpa_event_data event;
+ u16 status;
+ int ssid_len;
+
+ if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) &&
+ drv->force_connect_cmd) {
+ /*
+ * Avoid reporting two association events that would confuse
+ * the core code.
+ */
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Ignore assoc event when using driver SME");
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "nl80211: Associate event");
+ 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_memcpy(drv->prev_bssid, mgmt->sa, ETH_ALEN);
+
+ os_memset(&event, 0, sizeof(event));
+ event.assoc_info.resp_frame = frame;
+ event.assoc_info.resp_frame_len = len;
+ 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);
+ }
+
+ if (req_ie) {
+ event.assoc_info.req_ies = nla_data(req_ie);
+ event.assoc_info.req_ies_len = nla_len(req_ie);
+ }
+
+ /* When this association was initiated outside of wpa_supplicant,
+ * drv->ssid needs to be set here to satisfy later checking. */
+ ssid_len = nl80211_get_assoc_ssid(drv, drv->ssid);
+ if (ssid_len > 0) {
+ drv->ssid_len = ssid_len;
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Set drv->ssid based on scan res info to '%s'",
+ wpa_ssid_txt(drv->ssid, drv->ssid_len));
+ }
+
+ event.assoc_info.freq = drv->assoc_freq;
+ drv->first_bss->flink->freq = drv->assoc_freq;
+
+ nl80211_parse_wmm_params(wmm, &event.assoc_info.wmm_params);
+
+ wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
+}
+
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+
+static int qca_drv_connect_fail_reason_code_handler(struct nl_msg *msg,
+ void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct nlattr *tb_sta_info[QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ u32 *reason_code = arg;
+
+ *reason_code = 0;
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ if (!tb[NL80211_ATTR_VENDOR_DATA]) {
+ wpa_printf(MSG_ERROR, "%s: Vendor data not found", __func__);
+ return NL_SKIP;
+ }
+
+ nla_parse(tb_sta_info, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MAX,
+ nla_data(tb[NL80211_ATTR_VENDOR_DATA]),
+ nla_len(tb[NL80211_ATTR_VENDOR_DATA]), NULL);
+
+ if (!tb_sta_info[QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_CONNECT_FAIL_REASON_CODE]) {
+ wpa_printf(MSG_INFO, "%s: Vendor attr not found", __func__);
+ return NL_SKIP;
+ }
+
+ *reason_code = nla_get_u32(tb_sta_info[QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_CONNECT_FAIL_REASON_CODE]);
+
+ return NL_SKIP;
+}
+
+
+static enum qca_sta_connect_fail_reason_codes
+drv_get_connect_fail_reason_code(struct wpa_driver_nl80211_data *drv)
+{
+ enum qca_sta_connect_fail_reason_codes reason_code;
+ struct nl_msg *msg;
+ int ret;
+
+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
+ if (!msg || nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_GET_STA_INFO)) {
+ nlmsg_free(msg);
+ return 0;
+ }
+
+ ret = send_and_recv_msgs(drv, msg,
+ qca_drv_connect_fail_reason_code_handler,
+ &reason_code, NULL, NULL);
+ if (ret)
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Get connect fail reason_code failed: ret=%d (%s)",
+ ret, strerror(-ret));
+
+ return reason_code;
+}
+
+
+static enum sta_connect_fail_reason_codes
+convert_connect_fail_reason_codes(enum qca_sta_connect_fail_reason_codes
+ reason_code)
+{
+ switch (reason_code) {
+ case QCA_STA_CONNECT_FAIL_REASON_NO_BSS_FOUND:
+ return STA_CONNECT_FAIL_REASON_NO_BSS_FOUND;
+ case QCA_STA_CONNECT_FAIL_REASON_AUTH_TX_FAIL:
+ return STA_CONNECT_FAIL_REASON_AUTH_TX_FAIL;
+ case QCA_STA_CONNECT_FAIL_REASON_AUTH_NO_ACK_RECEIVED:
+ return STA_CONNECT_FAIL_REASON_AUTH_NO_ACK_RECEIVED;
+ case QCA_STA_CONNECT_FAIL_REASON_AUTH_NO_RESP_RECEIVED:
+ return STA_CONNECT_FAIL_REASON_AUTH_NO_RESP_RECEIVED;
+ case QCA_STA_CONNECT_FAIL_REASON_ASSOC_REQ_TX_FAIL:
+ return STA_CONNECT_FAIL_REASON_ASSOC_REQ_TX_FAIL;
+ case QCA_STA_CONNECT_FAIL_REASON_ASSOC_NO_ACK_RECEIVED:
+ return STA_CONNECT_FAIL_REASON_ASSOC_NO_ACK_RECEIVED;
+ case QCA_STA_CONNECT_FAIL_REASON_ASSOC_NO_RESP_RECEIVED:
+ return STA_CONNECT_FAIL_REASON_ASSOC_NO_RESP_RECEIVED;
+ default:
+ return STA_CONNECT_FAIL_REASON_UNSPECIFIED;
+ }
+}
+
+
+static void qca_nl80211_link_reconfig_event(struct wpa_driver_nl80211_data *drv,
+ u8 *data, size_t len)
+{
+ struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_LINK_RECONFIG_MAX + 1];
+ u16 removed_links;
+ u8 *ap_mld;
+ int i;
+
+ if (!data)
+ return;
+
+ if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_LINK_RECONFIG_MAX,
+ (struct nlattr *) data, len, NULL) ||
+ !tb[QCA_WLAN_VENDOR_ATTR_LINK_RECONFIG_AP_MLD_ADDR])
+ return;
+
+ ap_mld = nla_data(tb[QCA_WLAN_VENDOR_ATTR_LINK_RECONFIG_AP_MLD_ADDR]);
+ wpa_printf(MSG_DEBUG, "nl80211: AP MLD address " MACSTR
+ " received in link reconfig event", MAC2STR(ap_mld));
+ if (!drv->sta_mlo_info.valid_links ||
+ os_memcmp(drv->sta_mlo_info.ap_mld_addr, ap_mld, ETH_ALEN) != 0) {
+ if (drv->pending_link_reconfig_data == data) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Drop pending link reconfig event since AP MLD not matched even after new connect/roam event");
+ os_free(drv->pending_link_reconfig_data);
+ drv->pending_link_reconfig_data = NULL;
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Cache new link reconfig event till next connect/roam event");
+ if (drv->pending_link_reconfig_data) {
+ wpa_printf(MSG_DEBUG, "nl80211: Override old link reconfig event data");
+ os_free(drv->pending_link_reconfig_data);
+ }
+ drv->pending_link_reconfig_data = os_memdup(data, len);
+ if (!drv->pending_link_reconfig_data)
+ return;
+ drv->pending_link_reconfig_data_len = len;
+ return;
+ }
+
+ if (!tb[QCA_WLAN_VENDOR_ATTR_LINK_RECONFIG_REMOVED_LINKS])
+ return;
+ removed_links = nla_get_u16(
+ tb[QCA_WLAN_VENDOR_ATTR_LINK_RECONFIG_REMOVED_LINKS]);
+
+ drv->sta_mlo_info.valid_links &= ~removed_links;
+
+ /*
+ * Set default BSSID to the BSSID of the lowest link ID of remaining
+ * links when the link used for (re)association is removed.
+ */
+ if (removed_links & BIT(drv->sta_mlo_info.assoc_link_id)) {
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ if (!(drv->sta_mlo_info.valid_links & BIT(i)))
+ continue;
+
+ os_memcpy(drv->bssid, drv->sta_mlo_info.links[i].bssid,
+ ETH_ALEN);
+ drv->sta_mlo_info.assoc_link_id = i;
+ break;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "nl80211: Removed MLO links bitmap: 0x%x",
+ removed_links);
+
+ wpa_supplicant_event(drv->ctx, EVENT_LINK_RECONFIG, NULL);
+}
+
+
+static void
+nl80211_parse_qca_vendor_mlo_link_info(struct driver_sta_mlo_info *mlo,
+ struct nlattr *mlo_links)
+{
+ struct nlattr *link;
+ int rem_links;
+
+ nla_for_each_nested(link, mlo_links, rem_links) {
+ struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MLO_LINK_MAX + 1];
+ int link_id;
+
+ nla_parse(tb,QCA_WLAN_VENDOR_ATTR_MLO_LINK_MAX, nla_data(link),
+ nla_len(link), NULL);
+
+ if (!tb[QCA_WLAN_VENDOR_ATTR_MLO_LINK_ID] ||
+ !tb[QCA_WLAN_VENDOR_ATTR_MLO_LINK_MAC_ADDR] ||
+ !tb[QCA_WLAN_VENDOR_ATTR_MLO_LINK_BSSID])
+ continue;
+
+ link_id = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_MLO_LINK_ID]);
+ if (link_id >= MAX_NUM_MLD_LINKS)
+ continue;
+
+ mlo->valid_links |= BIT(link_id);
+ os_memcpy(mlo->links[link_id].addr,
+ nla_data(tb[QCA_WLAN_VENDOR_ATTR_MLO_LINK_MAC_ADDR]),
+ ETH_ALEN);
+ os_memcpy(mlo->links[link_id].bssid,
+ nla_data(tb[QCA_WLAN_VENDOR_ATTR_MLO_LINK_BSSID]),
+ ETH_ALEN);
+ wpa_printf(MSG_DEBUG, "nl80211: MLO link[%u] addr " MACSTR
+ " bssid " MACSTR,
+ link_id, MAC2STR(mlo->links[link_id].addr),
+ MAC2STR(mlo->links[link_id].bssid));
+ }
+}
+
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+
+static void nl80211_parse_mlo_link_info(struct driver_sta_mlo_info *mlo,
+ struct nlattr *mlo_links)
+{
+ struct nlattr *link;
+ int rem_links;
+
+ nla_for_each_nested(link, mlo_links, rem_links) {
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ int link_id;
+
+ nla_parse(tb, NL80211_ATTR_MAX, nla_data(link), nla_len(link),
+ NULL);
+
+ if (!tb[NL80211_ATTR_MLO_LINK_ID] || !tb[NL80211_ATTR_MAC] ||
+ !tb[NL80211_ATTR_BSSID])
+ continue;
+
+ link_id = nla_get_u8(tb[NL80211_ATTR_MLO_LINK_ID]);
+ if (link_id >= MAX_NUM_MLD_LINKS)
+ continue;
+
+ if (tb[NL80211_ATTR_STATUS_CODE]) {
+ /* Set requested links only when status indicated */
+ mlo->req_links |= BIT(link_id);
+ if (nla_get_u16(tb[NL80211_ATTR_STATUS_CODE]) ==
+ WLAN_STATUS_SUCCESS)
+ mlo->valid_links |= BIT(link_id);
+ } else {
+ mlo->valid_links |= BIT(link_id);
+ }
+
+ os_memcpy(mlo->links[link_id].addr,
+ nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
+ os_memcpy(mlo->links[link_id].bssid,
+ nla_data(tb[NL80211_ATTR_BSSID]), ETH_ALEN);
+ wpa_printf(MSG_DEBUG, "nl80211: MLO link[%u] addr " MACSTR
+ " bssid " MACSTR,
+ link_id, MAC2STR(mlo->links[link_id].addr),
+ MAC2STR(mlo->links[link_id].bssid));
+ }
+}
+
+
+struct links_info {
+ /* bitmap of link IDs in Per-STA profile subelements */
+ u16 non_assoc_links;
+ u8 addr[MAX_NUM_MLD_LINKS][ETH_ALEN];
+};
+
+
+static void nl80211_get_basic_mle_links_info(const u8 *mle, size_t mle_len,
+ struct links_info *info)
+{
+ size_t rem_len;
+ const u8 *pos;
+
+ if (mle_len < MULTI_LINK_CONTROL_LEN + 1 ||
+ mle_len - MULTI_LINK_CONTROL_LEN < mle[MULTI_LINK_CONTROL_LEN])
+ return;
+
+ /* Skip Common Info */
+ pos = mle + MULTI_LINK_CONTROL_LEN + mle[MULTI_LINK_CONTROL_LEN];
+ rem_len = mle_len -
+ (MULTI_LINK_CONTROL_LEN + mle[MULTI_LINK_CONTROL_LEN]);
+
+ /* Parse Subelements */
+ while (rem_len > 2) {
+ size_t ie_len = 2 + pos[1];
+
+ if (rem_len < ie_len)
+ break;
+
+ if (pos[0] == MULTI_LINK_SUB_ELEM_ID_PER_STA_PROFILE) {
+ u8 link_id;
+ const u8 *sta_profile;
+ u16 sta_ctrl;
+
+ if (pos[1] < BASIC_MLE_STA_PROF_STA_MAC_IDX + ETH_ALEN)
+ goto next_subelem;
+
+ sta_profile = &pos[2];
+ sta_ctrl = WPA_GET_LE16(sta_profile);
+ link_id = sta_ctrl & BASIC_MLE_STA_CTRL_LINK_ID_MASK;
+ if (link_id >= MAX_NUM_MLD_LINKS)
+ goto next_subelem;
+
+ if (!(sta_ctrl & BASIC_MLE_STA_CTRL_PRES_STA_MAC))
+ goto next_subelem;
+
+ info->non_assoc_links |= BIT(link_id);
+ os_memcpy(info->addr[link_id],
+ &sta_profile[BASIC_MLE_STA_PROF_STA_MAC_IDX],
+ ETH_ALEN);
+ }
+next_subelem:
+ pos += ie_len;
+ rem_len -= ie_len;
+ }
+}
+
+
+static int nl80211_update_rejected_links_info(struct driver_sta_mlo_info *mlo,
+ struct nlattr *req_ie,
+ struct nlattr *resp_ie)
+{
+ int i;
+ struct wpabuf *mle;
+ struct ieee802_11_elems req_elems, resp_elems;
+ struct links_info req_info, resp_info;
+
+ if (!req_ie || !resp_ie) {
+ wpa_printf(MSG_INFO,
+ "nl80211: MLO: (Re)Association Request/Response frame elements not available");
+ return -1;
+ }
+
+ if (ieee802_11_parse_elems(nla_data(req_ie), nla_len(req_ie),
+ &req_elems, 0) == ParseFailed ||
+ ieee802_11_parse_elems(nla_data(resp_ie), nla_len(resp_ie),
+ &resp_elems, 0) == ParseFailed) {
+ wpa_printf(MSG_INFO,
+ "nl80211: MLO: Failed to parse (Re)Association Request/Response elements");
+ return -1;
+ }
+
+ mle = ieee802_11_defrag_mle(&req_elems, MULTI_LINK_CONTROL_TYPE_BASIC);
+ if (!mle) {
+ wpa_printf(MSG_INFO,
+ "nl80211: MLO: Basic Multi-Link element not found in Association Request");
+ return -1;
+ }
+ os_memset(&req_info, 0, sizeof(req_info));
+ nl80211_get_basic_mle_links_info(wpabuf_head(mle), wpabuf_len(mle),
+ &req_info);
+ wpabuf_free(mle);
+
+ mle = ieee802_11_defrag_mle(&resp_elems, MULTI_LINK_CONTROL_TYPE_BASIC);
+ if (!mle) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: MLO: Basic Multi-Link element not found in Association Response");
+ return -1;
+ }
+ os_memset(&resp_info, 0, sizeof(resp_info));
+ nl80211_get_basic_mle_links_info(wpabuf_head(mle), wpabuf_len(mle),
+ &resp_info);
+ wpabuf_free(mle);
+
+ if (req_info.non_assoc_links != resp_info.non_assoc_links) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: MLO: Association Request and Response links bitmaps not equal (0x%x != 0x%x)",
+ req_info.non_assoc_links,
+ resp_info.non_assoc_links);
+ return -1;
+ }
+
+ mlo->req_links = BIT(mlo->assoc_link_id) | req_info.non_assoc_links;
+ if ((mlo->req_links & mlo->valid_links) != mlo->valid_links) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: MLO: Accepted links are not a subset of requested links (req_links=0x%x valid_links=0x%x non_assoc_links=0x%x assoc_link_id=0x%x)",
+ mlo->req_links, mlo->valid_links,
+ req_info.non_assoc_links, BIT(mlo->assoc_link_id));
+ return -1;
+ }
+
+ /* Get MLO links info for rejected links */
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ if (!((mlo->req_links & ~mlo->valid_links) & BIT(i)))
+ continue;
+
+ os_memcpy(mlo->links[i].bssid, resp_info.addr[i], ETH_ALEN);
+ os_memcpy(mlo->links[i].addr, req_info.addr[i], ETH_ALEN);
+ }
+
+ return 0;
+}
+
+
+static int nl80211_get_assoc_link_id(const u8 *data, u8 len)
+{
+ u16 control;
+
+ if (len < 2)
+ return -1;
+
+ control = WPA_GET_LE16(data);
+ if (!(control & BASIC_MULTI_LINK_CTRL_PRES_LINK_ID))
+ return -1;
+
+#define BASIC_ML_IE_COMMON_INFO_LINK_ID_IDX \
+ (2 + /* Multi-Link Control field */ \
+ 1 + /* Common Info Length field (Basic) */ \
+ ETH_ALEN) /* MLD MAC Address field (Basic) */
+ if (len <= BASIC_ML_IE_COMMON_INFO_LINK_ID_IDX)
+ return -1;
+
+ return data[BASIC_ML_IE_COMMON_INFO_LINK_ID_IDX] & 0x0F;
+}
+
+
+static void nl80211_parse_mlo_info(struct wpa_driver_nl80211_data *drv,
+ bool qca_roam_auth,
+ struct nlattr *addr,
+ struct nlattr *mlo_links,
+ struct nlattr *req_ie,
+ struct nlattr *resp_ie)
+{
+ const u8 *ml_ie;
+ struct driver_sta_mlo_info *mlo = &drv->sta_mlo_info;
+ int res;
+
+ if (!addr || !mlo_links || !resp_ie)
+ return;
+
+ ml_ie = get_ml_ie(nla_data(resp_ie), nla_len(resp_ie),
+ MULTI_LINK_CONTROL_TYPE_BASIC);
+ if (!ml_ie)
+ return;
+
+ res = nl80211_get_assoc_link_id(&ml_ie[3], ml_ie[1] - 1);
+ if (res < 0 || res >= MAX_NUM_MLD_LINKS) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Could not find a valid association Link ID (res=%d)",
+ res);
+ return;
+ }
+ drv->sta_mlo_info.assoc_link_id = res;
+
+ os_memcpy(mlo->ap_mld_addr, nla_data(addr), ETH_ALEN);
+ wpa_printf(MSG_DEBUG, "nl80211: AP MLD MAC Address " MACSTR,
+ MAC2STR(mlo->ap_mld_addr));
+
+ if (!qca_roam_auth)
+ nl80211_parse_mlo_link_info(mlo, mlo_links);
+#ifdef CONFIG_DRIVER_NL80211_QCA
+ if (qca_roam_auth)
+ nl80211_parse_qca_vendor_mlo_link_info(mlo, mlo_links);
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+ if (!(mlo->valid_links & BIT(mlo->assoc_link_id)) ||
+ (!mlo->req_links &&
+ nl80211_update_rejected_links_info(mlo, req_ie, resp_ie))) {
+ wpa_printf(MSG_INFO, "nl80211: Invalid MLO connection info");
+ mlo->valid_links = 0;
+ return;
+ }
+
+ os_memcpy(drv->bssid, mlo->links[drv->sta_mlo_info.assoc_link_id].bssid,
+ ETH_ALEN);
+ os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN);
+}
+
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+static void
+qca_nl80211_tid_to_link_map_event(struct wpa_driver_nl80211_data *drv,
+ u8 *data, size_t len)
+{
+ struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_TID_TO_LINK_MAP_MAX + 1];
+ struct nlattr *tids;
+ union wpa_event_data event;
+ u8 *ap_mld;
+ int i, rem, tidnum = 0;
+
+ os_memset(&event, 0, sizeof(event));
+
+ if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_TID_TO_LINK_MAP_MAX,
+ (struct nlattr *) data, len, NULL) ||
+ !tb[QCA_WLAN_VENDOR_ATTR_TID_TO_LINK_MAP_AP_MLD_ADDR])
+ return;
+
+ ap_mld = nla_data(tb[QCA_WLAN_VENDOR_ATTR_TID_TO_LINK_MAP_AP_MLD_ADDR]);
+
+ wpa_printf(MSG_DEBUG, "nl80211: AP MLD address " MACSTR
+ " received in TID to link mapping event", MAC2STR(ap_mld));
+ if (!drv->sta_mlo_info.valid_links ||
+ os_memcmp(drv->sta_mlo_info.ap_mld_addr, ap_mld, ETH_ALEN) != 0) {
+ if (drv->pending_t2lm_data == data) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Drop pending TID-to-link mapping event since AP MLD not matched even after new connect/roam event");
+ os_free(drv->pending_t2lm_data);
+ drv->pending_t2lm_data = NULL;
+ return;
+ }
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Cache new TID-to-link map event until the next connect/roam event");
+ if (drv->pending_t2lm_data) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Override old TID-to-link map event data");
+ os_free(drv->pending_t2lm_data);
+ }
+ drv->pending_t2lm_data = os_memdup(data, len);
+ if (!drv->pending_t2lm_data)
+ return;
+ drv->pending_t2lm_data_len = len;
+ return;
+ }
+
+ if (!tb[QCA_WLAN_VENDOR_ATTR_TID_TO_LINK_MAP_STATUS]) {
+ wpa_printf(MSG_DEBUG, "nl80211: Default TID-to-link map");
+ event.t2l_map_info.default_map = true;
+ goto out;
+ }
+
+ event.t2l_map_info.default_map = false;
+
+ nla_for_each_nested(tids,
+ tb[QCA_WLAN_VENDOR_ATTR_TID_TO_LINK_MAP_STATUS],
+ rem) {
+ u16 uplink, downlink;
+ struct nlattr *tid[QCA_WLAN_VENDOR_ATTR_LINK_TID_MAP_STATUS_MAX + 1];
+
+ if (nla_parse_nested(
+ tid, QCA_WLAN_VENDOR_ATTR_LINK_TID_MAP_STATUS_MAX,
+ tids, NULL)) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: TID-to-link: nla_parse_nested() failed");
+ return;
+ }
+
+ if (!tid[QCA_WLAN_VENDOR_ATTR_LINK_TID_MAP_STATUS_UPLINK]) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: TID-to-link: uplink not present for tid: %d",
+ tidnum);
+ return;
+ }
+ uplink = nla_get_u16(tid[QCA_WLAN_VENDOR_ATTR_LINK_TID_MAP_STATUS_UPLINK]);
+
+ if (!tid[QCA_WLAN_VENDOR_ATTR_LINK_TID_MAP_STATUS_DOWNLINK]) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: TID-to-link: downlink not present for tid: %d",
+ tidnum);
+ return;
+ }
+ downlink = nla_get_u16(tid[QCA_WLAN_VENDOR_ATTR_LINK_TID_MAP_STATUS_DOWNLINK]);
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: TID-to-link: Received uplink %x downlink %x",
+ uplink, downlink);
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ if (!(drv->sta_mlo_info.valid_links & BIT(i)))
+ continue;
+ if (uplink & BIT(i))
+ event.t2l_map_info.t2lmap[i].uplink |=
+ BIT(tidnum);
+ if (downlink & BIT(i))
+ event.t2l_map_info.t2lmap[i].downlink |=
+ BIT(tidnum);
+ }
+
+ tidnum++;
+ }
+
+out:
+ drv->sta_mlo_info.default_map = event.t2l_map_info.default_map;
+
+ event.t2l_map_info.valid_links = drv->sta_mlo_info.valid_links;
+ for (i = 0; i < MAX_NUM_MLD_LINKS && !drv->sta_mlo_info.default_map;
+ i++) {
+ if (!(drv->sta_mlo_info.valid_links & BIT(i)))
+ continue;
+
+ drv->sta_mlo_info.links[i].t2lmap.uplink =
+ event.t2l_map_info.t2lmap[i].uplink;
+ drv->sta_mlo_info.links[i].t2lmap.downlink =
+ event.t2l_map_info.t2lmap[i].downlink;
+ }
+
+ wpa_supplicant_event(drv->ctx, EVENT_TID_LINK_MAP, &event);
+}
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+
+static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
+ enum nl80211_commands cmd, bool qca_roam_auth,
+ struct nlattr *status,
+ struct nlattr *addr, struct nlattr *req_ie,
+ struct nlattr *resp_ie,
+ struct nlattr *timed_out,
+ struct nlattr *timeout_reason,
+ struct nlattr *authorized,
+ struct nlattr *key_replay_ctr,
+ struct nlattr *ptk_kck,
+ struct nlattr *ptk_kek,
+ struct nlattr *subnet_status,
+ struct nlattr *fils_erp_next_seq_num,
+ struct nlattr *fils_pmk,
+ struct nlattr *fils_pmkid,
+ struct nlattr *mlo_links)
+{
+ union wpa_event_data event;
+ const u8 *ssid = NULL;
+ u16 status_code;
+ int ssid_len;
+
+ 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;
+ }
+
+ drv->connect_reassoc = 0;
+
+ status_code = status ? nla_get_u16(status) : WLAN_STATUS_SUCCESS;
+
+ if (cmd == NL80211_CMD_CONNECT) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Connect event (status=%u ignore_next_local_disconnect=%d)",
+ status_code, drv->ignore_next_local_disconnect);
+ } else if (cmd == NL80211_CMD_ROAM) {
+ wpa_printf(MSG_DEBUG, "nl80211: Roam event");
+ }
+
+ os_memset(&event, 0, sizeof(event));
+ if (cmd == NL80211_CMD_CONNECT && status_code != WLAN_STATUS_SUCCESS) {
+ if (addr)
+ event.assoc_reject.bssid = nla_data(addr);
+ if (drv->ignore_next_local_disconnect) {
+ drv->ignore_next_local_disconnect = 0;
+ if (!event.assoc_reject.bssid ||
+ (os_memcmp(event.assoc_reject.bssid,
+ drv->auth_attempt_bssid,
+ ETH_ALEN) != 0)) {
+ /*
+ * Ignore the event that came without a BSSID or
+ * for the old connection since this is likely
+ * not relevant to the new Connect command.
+ */
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Ignore connection failure event triggered during reassociation");
+ return;
+ }
+ }
+ 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 = status_code;
+ event.assoc_reject.timed_out = timed_out != NULL;
+ if (timed_out && timeout_reason) {
+ enum nl80211_timeout_reason reason;
+
+ reason = nla_get_u32(timeout_reason);
+ switch (reason) {
+ case NL80211_TIMEOUT_SCAN:
+ event.assoc_reject.timeout_reason = "scan";
+ break;
+ case NL80211_TIMEOUT_AUTH:
+ event.assoc_reject.timeout_reason = "auth";
+ break;
+ case NL80211_TIMEOUT_ASSOC:
+ event.assoc_reject.timeout_reason = "assoc";
+ break;
+ default:
+ break;
+ }
+ }
+ if (fils_erp_next_seq_num)
+ event.assoc_reject.fils_erp_next_seq_num =
+ nla_get_u16(fils_erp_next_seq_num);
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+ if (drv->get_sta_info_vendor_cmd_avail) {
+ enum qca_sta_connect_fail_reason_codes reason_code;
+
+ reason_code = drv_get_connect_fail_reason_code(drv);
+ event.assoc_reject.reason_code =
+ convert_connect_fail_reason_codes(reason_code);
+ }
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+ wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event);
+ return;
+ }
+
+ drv->associated = 1;
+ os_memset(&drv->sta_mlo_info, 0, sizeof(drv->sta_mlo_info));
+ nl80211_parse_mlo_info(drv, qca_roam_auth, addr, mlo_links, req_ie,
+ resp_ie);
+ if (!drv->sta_mlo_info.valid_links && addr) {
+ os_memcpy(drv->bssid, nla_data(addr), ETH_ALEN);
+ os_memcpy(drv->prev_bssid, drv->bssid, 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 (cmd == NL80211_CMD_ROAM) {
+ ssid = get_ie(event.assoc_info.req_ies,
+ event.assoc_info.req_ies_len,
+ WLAN_EID_SSID);
+ if (ssid && ssid[1] > 0 && ssid[1] <= 32) {
+ drv->ssid_len = ssid[1];
+ os_memcpy(drv->ssid, ssid + 2, ssid[1]);
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Set drv->ssid based on req_ie to '%s'",
+ wpa_ssid_txt(drv->ssid,
+ drv->ssid_len));
+ }
+ }
+ }
+ if (resp_ie) {
+ event.assoc_info.resp_ies = nla_data(resp_ie);
+ event.assoc_info.resp_ies_len = nla_len(resp_ie);
+ }
+
+ event.assoc_info.freq = nl80211_get_assoc_freq(drv);
+ drv->first_bss->flink->freq = drv->assoc_freq;
+
+ if ((!ssid || ssid[1] == 0 || ssid[1] > 32) &&
+ (ssid_len = nl80211_get_assoc_ssid(drv, drv->ssid)) > 0) {
+ /* When this connection was initiated outside of wpa_supplicant,
+ * drv->ssid needs to be set here to satisfy later checking. */
+ drv->ssid_len = ssid_len;
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Set drv->ssid based on scan res info to '%s'",
+ wpa_ssid_txt(drv->ssid, drv->ssid_len));
+ }
+
+ if (authorized && nla_get_u8(authorized)) {
+ event.assoc_info.authorized = 1;
+ wpa_printf(MSG_DEBUG, "nl80211: connection authorized");
+ }
+ if (key_replay_ctr) {
+ event.assoc_info.key_replay_ctr = nla_data(key_replay_ctr);
+ event.assoc_info.key_replay_ctr_len = nla_len(key_replay_ctr);
+ }
+ if (ptk_kck) {
+ event.assoc_info.ptk_kck = nla_data(ptk_kck);
+ event.assoc_info.ptk_kck_len = nla_len(ptk_kck);
+ }
+ if (ptk_kek) {
+ event.assoc_info.ptk_kek = nla_data(ptk_kek);
+ event.assoc_info.ptk_kek_len = nla_len(ptk_kek);
+ }
+
+ if (subnet_status) {
+ /*
+ * At least for now, this is only available from
+ * QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS and that
+ * attribute has the same values 0, 1, 2 as are used in the
+ * variable here, so no mapping between different values are
+ * needed.
+ */
+ event.assoc_info.subnet_status = nla_get_u8(subnet_status);
+ }
+
+ if (fils_erp_next_seq_num)
+ event.assoc_info.fils_erp_next_seq_num =
+ nla_get_u16(fils_erp_next_seq_num);
+
+ if (fils_pmk) {
+ event.assoc_info.fils_pmk = nla_data(fils_pmk);
+ event.assoc_info.fils_pmk_len = nla_len(fils_pmk);
+ }
+
+ if (fils_pmkid)
+ event.assoc_info.fils_pmkid = nla_data(fils_pmkid);
+
+ wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
+
+ /* Avoid a race condition by stopping to ignore any following
+ * disconnection events now that the driver has indicated it is
+ * connected since that connection could have been triggered by a roam
+ * operation that happened in parallel with the disconnection request.
+ */
+ drv->ignore_next_local_disconnect = 0;
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+ if (drv->pending_t2lm_data)
+ qca_nl80211_tid_to_link_map_event(drv, drv->pending_t2lm_data,
+ drv->pending_t2lm_data_len);
+ else
+ drv->sta_mlo_info.default_map = true;
+
+ if (drv->pending_link_reconfig_data)
+ qca_nl80211_link_reconfig_event(
+ drv, drv->pending_link_reconfig_data,
+ drv->pending_link_reconfig_data_len);
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+}
+
+
+static void mlme_event_disconnect(struct wpa_driver_nl80211_data *drv,
+ struct nlattr *reason, struct nlattr *addr,
+ struct nlattr *by_ap)
+{
+ union wpa_event_data data;
+ unsigned int locally_generated = by_ap == NULL;
+
+ 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");
+ return;
+ }
+
+ if (drv->ignore_next_local_disconnect) {
+ drv->ignore_next_local_disconnect = 0;
+ if (locally_generated) {
+ wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect "
+ "event triggered during reassociation");
+ return;
+ }
+ wpa_printf(MSG_WARNING, "nl80211: Was expecting local "
+ "disconnect but got another disconnect "
+ "event first");
+ }
+
+ wpa_printf(MSG_DEBUG, "nl80211: Disconnect event");
+ nl80211_mark_disconnected(drv);
+ os_memset(&data, 0, sizeof(data));
+ if (reason)
+ data.deauth_info.reason_code = nla_get_u16(reason);
+ data.deauth_info.locally_generated = by_ap == NULL;
+ wpa_supplicant_event(drv->ctx, EVENT_DEAUTH, &data);
+}
+
+
+static int calculate_chan_offset(int width, int freq, int cf1, int cf2)
+{
+ int freq1 = 0;
+
+ switch (convert2width(width)) {
+ case CHAN_WIDTH_20_NOHT:
+ case CHAN_WIDTH_20:
+ return 0;
+ case CHAN_WIDTH_40:
+ freq1 = cf1 - 10;
+ break;
+ case CHAN_WIDTH_80:
+ freq1 = cf1 - 30;
+ break;
+ case CHAN_WIDTH_160:
+ freq1 = cf1 - 70;
+ break;
+ case CHAN_WIDTH_80P80:
+ freq1 = cf1 - 30;
+ break;
+ case CHAN_WIDTH_320:
+ freq1 = cf1 - 150;
+ break;
+ case CHAN_WIDTH_UNKNOWN:
+ case CHAN_WIDTH_2160:
+ case CHAN_WIDTH_4320:
+ case CHAN_WIDTH_6480:
+ case CHAN_WIDTH_8640:
+ /* FIXME: implement this */
+ return 0;
+ }
+
+ return (abs(freq - freq1) / 20) % 2 == 0 ? 1 : -1;
+}
+
+
+static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv,
+ struct nlattr *ifindex, struct nlattr *link,
+ struct nlattr *freq, struct nlattr *type,
+ struct nlattr *bw, struct nlattr *cf1,
+ struct nlattr *cf2,
+ struct nlattr *punct_bitmap,
+ struct nlattr *count,
+ int finished)
+{
+ struct i802_bss *bss;
+ union wpa_event_data data;
+ int ht_enabled = 1;
+ int chan_offset = 0;
+ int ifidx;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Channel switch%s event",
+ finished ? "" : " started");
+
+ if (!freq)
+ return;
+
+ ifidx = nla_get_u32(ifindex);
+ bss = get_bss_ifindex(drv, ifidx);
+ if (bss == NULL) {
+ wpa_printf(MSG_WARNING, "nl80211: Unknown ifindex (%d) for channel switch, ignoring",
+ ifidx);
+ return;
+ }
+
+ if (type) {
+ enum nl80211_channel_type ch_type = nla_get_u32(type);
+
+ wpa_printf(MSG_DEBUG, "nl80211: Channel type: %d", ch_type);
+ switch (ch_type) {
+ case NL80211_CHAN_NO_HT:
+ ht_enabled = 0;
+ break;
+ case NL80211_CHAN_HT20:
+ break;
+ case NL80211_CHAN_HT40PLUS:
+ chan_offset = 1;
+ break;
+ case NL80211_CHAN_HT40MINUS:
+ chan_offset = -1;
+ break;
+ }
+ } else if (bw && cf1) {
+ /* This can happen for example with VHT80 ch switch */
+ chan_offset = calculate_chan_offset(nla_get_u32(bw),
+ nla_get_u32(freq),
+ nla_get_u32(cf1),
+ cf2 ? nla_get_u32(cf2) : 0);
+ wpa_printf(MSG_DEBUG, "nl80211: Calculated channel offset: %d",
+ chan_offset);
+ } else {
+ wpa_printf(MSG_WARNING, "nl80211: Unknown secondary channel information - following channel definition calculations may fail");
+ }
+
+ os_memset(&data, 0, sizeof(data));
+ data.ch_switch.freq = nla_get_u32(freq);
+ data.ch_switch.ht_enabled = ht_enabled;
+ data.ch_switch.ch_offset = chan_offset;
+ if (punct_bitmap)
+ data.ch_switch.punct_bitmap = (u16) nla_get_u32(punct_bitmap);
+ if (bw)
+ data.ch_switch.ch_width = convert2width(nla_get_u32(bw));
+ if (cf1)
+ data.ch_switch.cf1 = nla_get_u32(cf1);
+ if (cf2)
+ data.ch_switch.cf2 = nla_get_u32(cf2);
+ if (count)
+ data.ch_switch.count = nla_get_u32(count);
+
+ if (finished)
+ bss->flink->freq = data.ch_switch.freq;
+
+ if (link) {
+ u8 link_id = nla_get_u8(link);
+
+ if (link_id < MAX_NUM_MLD_LINKS &&
+ drv->sta_mlo_info.valid_links & BIT(link_id)) {
+ data.ch_switch.link_id = link_id;
+ drv->sta_mlo_info.links[link_id].freq =
+ data.ch_switch.freq;
+ wpa_supplicant_event(
+ bss->ctx,
+ finished ? EVENT_LINK_CH_SWITCH :
+ EVENT_LINK_CH_SWITCH_STARTED, &data);
+ }
+
+ if (link_id != drv->sta_mlo_info.assoc_link_id)
+ return;
+ }
+
+ drv->assoc_freq = data.ch_switch.freq;
+
+ wpa_supplicant_event(bss->ctx, finished ?
+ EVENT_CH_SWITCH : EVENT_CH_SWITCH_STARTED, &data);
+}
+
+
+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 i802_bss *bss,
+ struct nlattr *freq, struct nlattr *sig,
+ const u8 *frame, size_t len,
+ int link_id)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ const struct ieee80211_mgmt *mgmt;
+ union wpa_event_data event;
+ u16 fc, stype;
+ int ssi_signal = 0;
+ int rx_freq = 0;
+
+ wpa_printf(MSG_MSGDUMP, "nl80211: Frame event");
+ mgmt = (const struct ieee80211_mgmt *) frame;
+ if (len < 24) {
+ wpa_printf(MSG_DEBUG, "nl80211: Too short management frame");
+ return;
+ }
+
+ fc = le_to_host16(mgmt->frame_control);
+ stype = WLAN_FC_GET_STYPE(fc);
+
+ if (sig)
+ ssi_signal = (s32) nla_get_u32(sig);
+
+ os_memset(&event, 0, sizeof(event));
+ if (freq) {
+ event.rx_mgmt.freq = nla_get_u32(freq);
+ rx_freq = drv->last_mgmt_freq = event.rx_mgmt.freq;
+ }
+ wpa_printf(MSG_DEBUG,
+ "nl80211: RX frame da=" MACSTR " sa=" MACSTR " bssid=" MACSTR
+ " freq=%d ssi_signal=%d fc=0x%x seq_ctrl=0x%x stype=%u (%s) len=%u",
+ MAC2STR(mgmt->da), MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid),
+ rx_freq, ssi_signal, fc,
+ le_to_host16(mgmt->seq_ctrl), stype, fc2str(fc),
+ (unsigned int) len);
+ event.rx_mgmt.frame = frame;
+ event.rx_mgmt.frame_len = len;
+ event.rx_mgmt.ssi_signal = ssi_signal;
+ event.rx_mgmt.drv_priv = bss;
+ event.rx_mgmt.link_id = link_id;
+
+ wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
+}
+
+
+static void mlme_event_mgmt_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 = (const struct ieee80211_hdr *) frame;
+ u16 fc = le_to_host16(hdr->frame_control);
+ u64 cookie_val = 0;
+
+ if (cookie)
+ cookie_val = nla_get_u64(cookie);
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Frame TX status event A1=" MACSTR
+ " %sstype=%d cookie=0x%llx%s ack=%d",
+ MAC2STR(hdr->addr1),
+ WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT ? "not-mgmt " : "",
+ WLAN_FC_GET_STYPE(fc), (long long unsigned int) cookie_val,
+ cookie ? "" : "(N/A)", ack != NULL);
+
+ if (cookie_val && cookie_val == drv->eapol_tx_cookie &&
+ len >= ETH_HLEN &&
+ WPA_GET_BE16(frame + 2 * ETH_ALEN) == ETH_P_PAE) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Work around misdelivered control port TX status for EAPOL");
+ nl80211_control_port_frame_tx_status(drv, frame, len, ack,
+ cookie);
+ return;
+ }
+
+ if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT)
+ return;
+
+ if (!is_ap_interface(drv->nlmode) &&
+ WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION) {
+ if (!cookie)
+ return;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Frame TX status: cookie=0x%llx%s (ack=%d)",
+ (long long unsigned int) cookie_val,
+ cookie_val == drv->send_frame_cookie ?
+ " (match)" : " (unknown)", ack != NULL);
+ if (cookie_val != drv->send_frame_cookie)
+ return;
+ } else if (!is_ap_interface(drv->nlmode) &&
+ WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_AUTH) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Authentication frame TX status: ack=%d",
+ !!ack);
+ }
+
+ 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;
+ event.tx_status.link_id = cookie_val == drv->send_frame_cookie ?
+ drv->send_frame_link_id : NL80211_DRV_LINK_ID_NA;
+ 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;
+
+ if (type == EVENT_DEAUTH)
+ wpa_printf(MSG_DEBUG, "nl80211: Deauthenticate event");
+ else
+ wpa_printf(MSG_DEBUG, "nl80211: Disassociate event");
+
+ mgmt = (const struct ieee80211_mgmt *) frame;
+ if (len >= 24) {
+ bssid = mgmt->bssid;
+
+ if ((drv->capa.flags & WPA_DRIVER_FLAGS_SME) &&
+ !drv->associated &&
+ os_memcmp(bssid, drv->auth_bssid, ETH_ALEN) != 0 &&
+ os_memcmp(bssid, drv->auth_attempt_bssid, ETH_ALEN) != 0 &&
+ os_memcmp(bssid, drv->prev_bssid, ETH_ALEN) == 0) {
+ /*
+ * Avoid issues with some roaming cases where
+ * disconnection event for the old AP may show up after
+ * we have started connection with the new AP.
+ * In case of locally generated event clear
+ * ignore_next_local_deauth as well, to avoid next local
+ * deauth event be wrongly ignored.
+ */
+ if (os_memcmp(mgmt->sa, drv->first_bss->addr,
+ ETH_ALEN) == 0 ||
+ (!is_zero_ether_addr(drv->first_bss->prev_addr) &&
+ os_memcmp(mgmt->sa, drv->first_bss->prev_addr,
+ ETH_ALEN) == 0)) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Received a locally generated deauth event. Clear ignore_next_local_deauth flag");
+ drv->ignore_next_local_deauth = 0;
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Ignore deauth/disassoc event from old AP " MACSTR " when already authenticating with " MACSTR,
+ MAC2STR(bssid),
+ MAC2STR(drv->auth_attempt_bssid));
+ }
+ return;
+ }
+
+ if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) &&
+ drv->connect_reassoc && drv->associated &&
+ os_memcmp(bssid, drv->prev_bssid, ETH_ALEN) == 0 &&
+ os_memcmp(bssid, drv->auth_attempt_bssid, ETH_ALEN) != 0) {
+ /*
+ * Avoid issues with some roaming cases where
+ * disconnection event for the old AP may show up after
+ * we have started connection with the new AP.
+ */
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Ignore deauth/disassoc event from old AP "
+ MACSTR
+ " when already connecting with " MACSTR,
+ MAC2STR(bssid),
+ MAC2STR(drv->auth_attempt_bssid));
+ return;
+ }
+
+ 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;
+ }
+ }
+
+ nl80211_mark_disconnected(drv);
+ 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.locally_generated =
+ !os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN);
+ 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.locally_generated =
+ !os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN);
+ if (drv->ignore_deauth_event) {
+ wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth event due to previous forced deauth-during-auth");
+ drv->ignore_deauth_event = 0;
+ if (event.deauth_info.locally_generated)
+ drv->ignore_next_local_deauth = 0;
+ return;
+ }
+ if (drv->ignore_next_local_deauth) {
+ drv->ignore_next_local_deauth = 0;
+ if (event.deauth_info.locally_generated) {
+ wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth event triggered due to own deauth request");
+ return;
+ }
+ wpa_printf(MSG_WARNING, "nl80211: Was expecting local deauth but got another disconnect event first");
+ }
+ 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 (type == EVENT_UNPROT_DEAUTH)
+ wpa_printf(MSG_DEBUG, "nl80211: Unprot Deauthenticate event");
+ else
+ wpa_printf(MSG_DEBUG, "nl80211: Unprot Disassociate event");
+
+ 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_unprot_beacon(struct wpa_driver_nl80211_data *drv,
+ const u8 *frame, size_t len)
+{
+ const struct ieee80211_mgmt *mgmt;
+ union wpa_event_data event;
+
+ if (len < 24)
+ return;
+
+ mgmt = (const struct ieee80211_mgmt *) frame;
+
+ os_memset(&event, 0, sizeof(event));
+ event.unprot_beacon.sa = mgmt->sa;
+ wpa_supplicant_event(drv->ctx, EVENT_UNPROT_BEACON, &event);
+}
+
+
+static struct i802_link *
+nl80211_get_mld_link_by_freq(struct i802_bss *bss, unsigned int freq)
+{
+ unsigned int i;
+
+ for (i = 0; i < bss->n_links; i++) {
+ if ((unsigned int) bss->links[i].freq == freq &&
+ bss->links[i].link_id != -1)
+ return &bss->links[i];
+ }
+
+ return NULL;
+}
+
+
+static void mlme_event(struct i802_bss *bss,
+ enum nl80211_commands cmd, struct nlattr *frame,
+ struct nlattr *addr, struct nlattr *timed_out,
+ struct nlattr *freq, struct nlattr *ack,
+ struct nlattr *cookie, struct nlattr *sig,
+ struct nlattr *wmm, struct nlattr *req_ie,
+ struct nlattr *link)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ u16 stype = 0, auth_type = 0;
+ const u8 *data;
+ size_t len;
+ int link_id = -1;
+ struct i802_link *mld_link = NULL;
+
+ if (timed_out && addr) {
+ mlme_timeout_event(drv, cmd, addr);
+ return;
+ }
+
+ if (frame == NULL) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: MLME event %d (%s) without frame data",
+ cmd, nl80211_command_to_string(cmd));
+ return;
+ }
+
+ /* Determine the MLD link either by an explicitly provided link id or
+ * finding a match based on the frequency. */
+ if (link)
+ mld_link = nl80211_get_link(bss, nla_get_u8(link));
+ else if (freq)
+ mld_link = nl80211_get_mld_link_by_freq(bss, nla_get_u32(freq));
+
+ if (mld_link)
+ link_id = mld_link->link_id;
+
+ data = nla_data(frame);
+ len = nla_len(frame);
+ if (len < 4 + 2 * ETH_ALEN) {
+ wpa_printf(MSG_MSGDUMP, "nl80211: MLME event %d (%s) on %s("
+ MACSTR ") - too short",
+ cmd, nl80211_command_to_string(cmd), bss->ifname,
+ MAC2STR(bss->addr));
+ return;
+ }
+ wpa_printf(MSG_MSGDUMP, "nl80211: MLME event %d (%s) on %s(" MACSTR
+ ") A1=" MACSTR " A2=" MACSTR " on link_id=%d", cmd,
+ nl80211_command_to_string(cmd), bss->ifname,
+ MAC2STR(bss->addr), MAC2STR(data + 4),
+ MAC2STR(data + 4 + ETH_ALEN), link_id);
+
+ /* PASN Authentication frame can be received with a different source MAC
+ * address. Allow NL80211_CMD_FRAME event with foreign addresses also.
+ */
+ if (cmd == NL80211_CMD_FRAME && len >= 24) {
+ const struct ieee80211_mgmt *mgmt;
+ u16 fc;
+
+ mgmt = (const struct ieee80211_mgmt *) data;
+ fc = le_to_host16(mgmt->frame_control);
+ stype = WLAN_FC_GET_STYPE(fc);
+ auth_type = le_to_host16(mgmt->u.auth.auth_alg);
+ }
+
+ if (cmd == NL80211_CMD_FRAME && stype == WLAN_FC_STYPE_AUTH &&
+ auth_type == host_to_le16(WLAN_AUTH_PASN)) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: %s: Allow PASN frame for foreign address",
+ bss->ifname);
+ } else if (cmd != NL80211_CMD_FRAME_TX_STATUS &&
+ !(data[4] & 0x01) &&
+ os_memcmp(bss->addr, data + 4, ETH_ALEN) != 0 &&
+ (is_zero_ether_addr(bss->rand_addr) ||
+ os_memcmp(bss->rand_addr, data + 4, ETH_ALEN) != 0) &&
+ os_memcmp(bss->addr, data + 4 + ETH_ALEN, ETH_ALEN) != 0 &&
+ (is_zero_ether_addr(drv->first_bss->prev_addr) ||
+ os_memcmp(bss->prev_addr, data + 4 + ETH_ALEN,
+ ETH_ALEN) != 0) &&
+ (!mld_link ||
+ os_memcmp(mld_link->addr, data + 4, ETH_ALEN) != 0)) {
+ wpa_printf(MSG_MSGDUMP, "nl80211: %s: Ignore MLME frame event "
+ "for foreign address", bss->ifname);
+ return;
+ }
+ 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), wmm,
+ req_ie);
+ 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(bss, freq, sig, nla_data(frame),
+ nla_len(frame), link_id);
+ break;
+ case NL80211_CMD_FRAME_TX_STATUS:
+ mlme_event_mgmt_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;
+ case NL80211_CMD_UNPROT_BEACON:
+ mlme_event_unprot_beacon(drv, nla_data(frame), nla_len(frame));
+ break;
+ default:
+ break;
+ }
+}
+
+
+static void mlme_event_michael_mic_failure(struct i802_bss *bss,
+ 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(bss->ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
+}
+
+
+static void mlme_event_join_ibss(struct wpa_driver_nl80211_data *drv,
+ struct nlattr *tb[])
+{
+ unsigned int freq;
+ union wpa_event_data event;
+
+ 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));
+
+ freq = nl80211_get_assoc_freq(drv);
+ if (freq) {
+ wpa_printf(MSG_DEBUG, "nl80211: IBSS on frequency %u MHz",
+ freq);
+ drv->first_bss->flink->freq = freq;
+ }
+
+ os_memset(&event, 0, sizeof(event));
+ event.assoc_info.freq = freq;
+
+ wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
+}
+
+
+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 */
+
+ if (cancel_event)
+ drv->pending_remain_on_chan = 0;
+
+ 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 mlme_event_ft_event(struct wpa_driver_nl80211_data *drv,
+ struct nlattr *tb[])
+{
+ union wpa_event_data data;
+
+ os_memset(&data, 0, sizeof(data));
+
+ if (tb[NL80211_ATTR_IE]) {
+ data.ft_ies.ies = nla_data(tb[NL80211_ATTR_IE]);
+ data.ft_ies.ies_len = nla_len(tb[NL80211_ATTR_IE]);
+ }
+
+ if (tb[NL80211_ATTR_IE_RIC]) {
+ data.ft_ies.ric_ies = nla_data(tb[NL80211_ATTR_IE_RIC]);
+ data.ft_ies.ric_ies_len = nla_len(tb[NL80211_ATTR_IE_RIC]);
+ }
+
+ if (tb[NL80211_ATTR_MAC])
+ os_memcpy(data.ft_ies.target_ap,
+ nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
+
+ wpa_printf(MSG_DEBUG, "nl80211: FT event target_ap " MACSTR,
+ MAC2STR(data.ft_ies.target_ap));
+
+ wpa_supplicant_event(drv->ctx, EVENT_FT_RESPONSE, &data);
+}
+
+
+static void mlme_event_dh_event(struct wpa_driver_nl80211_data *drv,
+ struct i802_bss *bss,
+ struct nlattr *tb[])
+{
+ union wpa_event_data data;
+
+ if (!is_ap_interface(drv->nlmode))
+ return;
+ if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_IE])
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+ data.update_dh.peer = nla_data(tb[NL80211_ATTR_MAC]);
+ data.update_dh.ie = nla_data(tb[NL80211_ATTR_IE]);
+ data.update_dh.ie_len = nla_len(tb[NL80211_ATTR_IE]);
+
+ wpa_printf(MSG_DEBUG, "nl80211: DH event - peer " MACSTR,
+ MAC2STR(data.update_dh.peer));
+
+ wpa_supplicant_event(bss->ctx, EVENT_UPDATE_DH, &data);
+}
+
+
+static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted,
+ struct nlattr *tb[], int external_scan)
+{
+ union wpa_event_data event;
+ struct nlattr *nl;
+ int rem;
+ struct scan_info *info;
+#define MAX_REPORT_FREQS 110
+ int freqs[MAX_REPORT_FREQS];
+ int num_freqs = 0;
+
+ if (!external_scan && drv->scan_for_auth) {
+ drv->scan_for_auth = 0;
+ wpa_printf(MSG_DEBUG, "nl80211: Scan results for missing "
+ "cfg80211 BSS entry");
+ wpa_driver_nl80211_authenticate_retry(drv);
+ return;
+ }
+
+ os_memset(&event, 0, sizeof(event));
+ info = &event.scan_info;
+ info->aborted = aborted;
+ info->external_scan = external_scan;
+ info->nl_scan_event = 1;
+
+ if (tb[NL80211_ATTR_SCAN_SSIDS]) {
+ nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_SSIDS], rem) {
+ struct wpa_driver_scan_ssid *s =
+ &info->ssids[info->num_ssids];
+ s->ssid = nla_data(nl);
+ s->ssid_len = nla_len(nl);
+ wpa_printf(MSG_DEBUG, "nl80211: Scan probed for SSID '%s'",
+ wpa_ssid_txt(s->ssid, s->ssid_len));
+ info->num_ssids++;
+ if (info->num_ssids == WPAS_MAX_SCAN_SSIDS)
+ break;
+ }
+ }
+ if (tb[NL80211_ATTR_SCAN_FREQUENCIES]) {
+ char msg[MAX_REPORT_FREQS * 5 + 1], *pos, *end;
+ int res;
+
+ pos = msg;
+ end = pos + sizeof(msg);
+ *pos = '\0';
+
+ nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_FREQUENCIES], rem)
+ {
+ freqs[num_freqs] = nla_get_u32(nl);
+ res = os_snprintf(pos, end - pos, " %d",
+ freqs[num_freqs]);
+ if (!os_snprintf_error(end - pos, res))
+ pos += res;
+ num_freqs++;
+ if (num_freqs == MAX_REPORT_FREQS)
+ break;
+ }
+ info->freqs = freqs;
+ info->num_freqs = num_freqs;
+ msg[sizeof(msg) - 1] = '\0';
+ wpa_printf(MSG_DEBUG, "nl80211: Scan included frequencies:%s",
+ msg);
+ }
+
+ if (tb[NL80211_ATTR_SCAN_START_TIME_TSF] &&
+ tb[NL80211_ATTR_SCAN_START_TIME_TSF_BSSID]) {
+ info->scan_start_tsf =
+ nla_get_u64(tb[NL80211_ATTR_SCAN_START_TIME_TSF]);
+ os_memcpy(info->scan_start_tsf_bssid,
+ nla_data(tb[NL80211_ATTR_SCAN_START_TIME_TSF_BSSID]),
+ ETH_ALEN);
+ }
+
+ wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event);
+}
+
+
+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 },
+ [NL80211_ATTR_CQM_TXE_RATE] = { .type = NLA_U32 },
+ [NL80211_ATTR_CQM_TXE_PKTS] = { .type = NLA_U32 },
+ [NL80211_ATTR_CQM_TXE_INTVL] = { .type = NLA_U32 },
+ [NL80211_ATTR_CQM_BEACON_LOSS_EVENT] = { .type = NLA_FLAG },
+ };
+ struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1];
+ enum nl80211_cqm_rssi_threshold_event event;
+ union wpa_event_data ed;
+ 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);
+ ed.low_ack.num_packets =
+ nla_get_u32(cqm[NL80211_ATTR_CQM_PKT_LOSS_EVENT]);
+ wpa_printf(MSG_DEBUG, "nl80211: Packet loss event for " MACSTR
+ " (num_packets %u)",
+ MAC2STR(ed.low_ack.addr), ed.low_ack.num_packets);
+ wpa_supplicant_event(drv->ctx, EVENT_STATION_LOW_ACK, &ed);
+ return;
+ }
+
+ if (cqm[NL80211_ATTR_CQM_BEACON_LOSS_EVENT]) {
+ wpa_printf(MSG_DEBUG, "nl80211: Beacon loss event");
+ wpa_supplicant_event(drv->ctx, EVENT_BEACON_LOSS, NULL);
+ return;
+ }
+
+ if (cqm[NL80211_ATTR_CQM_TXE_RATE] &&
+ cqm[NL80211_ATTR_CQM_TXE_PKTS] &&
+ cqm[NL80211_ATTR_CQM_TXE_INTVL] &&
+ cqm[NL80211_ATTR_MAC]) {
+ wpa_printf(MSG_DEBUG, "nl80211: CQM TXE event for " MACSTR
+ " (rate: %u pkts: %u interval: %u)",
+ MAC2STR((u8 *) nla_data(cqm[NL80211_ATTR_MAC])),
+ nla_get_u32(cqm[NL80211_ATTR_CQM_TXE_RATE]),
+ nla_get_u32(cqm[NL80211_ATTR_CQM_TXE_PKTS]),
+ nla_get_u32(cqm[NL80211_ATTR_CQM_TXE_INTVL]));
+ return;
+ }
+
+ if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] == NULL) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Not a CQM RSSI threshold event");
+ 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 {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Unknown CQM RSSI threshold event: %d",
+ event);
+ return;
+ }
+
+ /*
+ * nl80211_get_link_signal() and nl80211_get_link_noise() set default
+ * values in case querying the driver fails.
+ */
+ res = nl80211_get_link_signal(drv, drv->bssid, &ed.signal_change.data);
+ if (res == 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Signal: %d dBm txrate: %lu",
+ ed.signal_change.data.signal,
+ ed.signal_change.data.current_tx_rate);
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Querying the driver for signal info failed");
+ }
+
+ res = nl80211_get_link_noise(drv, &ed.signal_change);
+ if (res == 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Noise: %d dBm",
+ ed.signal_change.current_noise);
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Querying the driver for noise info failed");
+ }
+
+ wpa_supplicant_event(drv->ctx, EVENT_SIGNAL_CHANGE, &ed);
+}
+
+
+static void nl80211_new_peer_candidate(struct wpa_driver_nl80211_data *drv,
+ struct nlattr **tb)
+{
+ const u8 *addr;
+ union wpa_event_data data;
+
+ if (drv->nlmode != NL80211_IFTYPE_MESH_POINT ||
+ !tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_IE])
+ return;
+
+ addr = nla_data(tb[NL80211_ATTR_MAC]);
+ wpa_printf(MSG_DEBUG, "nl80211: New peer candidate " MACSTR,
+ MAC2STR(addr));
+
+ os_memset(&data, 0, sizeof(data));
+ data.mesh_peer.peer = addr;
+ data.mesh_peer.ies = nla_data(tb[NL80211_ATTR_IE]);
+ data.mesh_peer.ie_len = nla_len(tb[NL80211_ATTR_IE]);
+ wpa_supplicant_event(drv->ctx, EVENT_NEW_PEER_CANDIDATE, &data);
+}
+
+
+static void nl80211_new_station_event(struct wpa_driver_nl80211_data *drv,
+ struct i802_bss *bss,
+ 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 (is_ap_interface(drv->nlmode) && drv->device_ap_sme) {
+ u8 *ies = NULL;
+ size_t ies_len = 0;
+ if (tb[NL80211_ATTR_IE]) {
+ ies = nla_data(tb[NL80211_ATTR_IE]);
+ ies_len = nla_len(tb[NL80211_ATTR_IE]);
+ }
+ wpa_hexdump(MSG_DEBUG, "nl80211: Assoc Req IEs", ies, ies_len);
+ drv_event_assoc(bss->ctx, addr, ies, ies_len, 0);
+ return;
+ }
+
+ 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(bss->ctx, EVENT_IBSS_RSN_START, &data);
+}
+
+
+static void nl80211_del_station_event(struct wpa_driver_nl80211_data *drv,
+ struct i802_bss *bss,
+ 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 (is_ap_interface(drv->nlmode) && drv->device_ap_sme) {
+ drv_event_disassoc(bss->ctx, addr);
+ return;
+ }
+
+ 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(bss->ctx, EVENT_IBSS_PEER_LOST, &data);
+}
+
+
+static void nl80211_rekey_offload_event(struct wpa_driver_nl80211_data *drv,
+ struct nlattr **tb)
+{
+ struct nlattr *rekey_info[NUM_NL80211_REKEY_DATA];
+ static struct nla_policy rekey_policy[NUM_NL80211_REKEY_DATA] = {
+ [NL80211_REKEY_DATA_KEK] = {
+ .minlen = NL80211_KEK_LEN,
+ .maxlen = NL80211_KEK_LEN,
+ },
+ [NL80211_REKEY_DATA_KCK] = {
+ .minlen = NL80211_KCK_LEN,
+ .maxlen = NL80211_KCK_LEN,
+ },
+ [NL80211_REKEY_DATA_REPLAY_CTR] = {
+ .minlen = NL80211_REPLAY_CTR_LEN,
+ .maxlen = NL80211_REPLAY_CTR_LEN,
+ },
+ };
+ union wpa_event_data data;
+
+ if (!tb[NL80211_ATTR_MAC] ||
+ !tb[NL80211_ATTR_REKEY_DATA] ||
+ nla_parse_nested(rekey_info, MAX_NL80211_REKEY_DATA,
+ tb[NL80211_ATTR_REKEY_DATA], rekey_policy) ||
+ !rekey_info[NL80211_REKEY_DATA_REPLAY_CTR])
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+ data.driver_gtk_rekey.bssid = nla_data(tb[NL80211_ATTR_MAC]);
+ wpa_printf(MSG_DEBUG, "nl80211: Rekey offload event for BSSID " MACSTR,
+ MAC2STR(data.driver_gtk_rekey.bssid));
+ data.driver_gtk_rekey.replay_ctr =
+ nla_data(rekey_info[NL80211_REKEY_DATA_REPLAY_CTR]);
+ wpa_hexdump(MSG_DEBUG, "nl80211: Rekey offload - Replay Counter",
+ data.driver_gtk_rekey.replay_ctr, NL80211_REPLAY_CTR_LEN);
+ wpa_supplicant_event(drv->ctx, EVENT_DRIVER_GTK_REKEY, &data);
+}
+
+
+static void nl80211_pmksa_candidate_event(struct wpa_driver_nl80211_data *drv,
+ struct nlattr **tb)
+{
+ struct nlattr *cand[NUM_NL80211_PMKSA_CANDIDATE];
+ static struct nla_policy cand_policy[NUM_NL80211_PMKSA_CANDIDATE] = {
+ [NL80211_PMKSA_CANDIDATE_INDEX] = { .type = NLA_U32 },
+ [NL80211_PMKSA_CANDIDATE_BSSID] = {
+ .minlen = ETH_ALEN,
+ .maxlen = ETH_ALEN,
+ },
+ [NL80211_PMKSA_CANDIDATE_PREAUTH] = { .type = NLA_FLAG },
+ };
+ union wpa_event_data data;
+
+ wpa_printf(MSG_DEBUG, "nl80211: PMKSA candidate event");
+
+ if (!tb[NL80211_ATTR_PMKSA_CANDIDATE] ||
+ nla_parse_nested(cand, MAX_NL80211_PMKSA_CANDIDATE,
+ tb[NL80211_ATTR_PMKSA_CANDIDATE], cand_policy) ||
+ !cand[NL80211_PMKSA_CANDIDATE_INDEX] ||
+ !cand[NL80211_PMKSA_CANDIDATE_BSSID])
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+ os_memcpy(data.pmkid_candidate.bssid,
+ nla_data(cand[NL80211_PMKSA_CANDIDATE_BSSID]), ETH_ALEN);
+ data.pmkid_candidate.index =
+ nla_get_u32(cand[NL80211_PMKSA_CANDIDATE_INDEX]);
+ data.pmkid_candidate.preauth =
+ cand[NL80211_PMKSA_CANDIDATE_PREAUTH] != NULL;
+ wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, &data);
+}
+
+
+static void nl80211_client_probe_event(struct wpa_driver_nl80211_data *drv,
+ struct nlattr **tb)
+{
+ union wpa_event_data data;
+ const u8 *addr;
+ u64 cookie = 0;
+
+ addr = nla_data(tb[NL80211_ATTR_MAC]);
+ if (!addr)
+ return;
+ if (tb[NL80211_ATTR_COOKIE])
+ cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]);
+ wpa_printf(MSG_DEBUG, "nl80211: Probe client event (addr=" MACSTR
+ " ack=%d cookie=%llu)", MAC2STR(addr),
+ tb[NL80211_ATTR_ACK] != NULL,
+ (long long unsigned int) cookie);
+ if (!tb[NL80211_ATTR_ACK])
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+ os_memcpy(data.client_poll.addr, addr, ETH_ALEN);
+ wpa_supplicant_event(drv->ctx, EVENT_DRIVER_CLIENT_POLL_OK, &data);
+}
+
+
+static void nl80211_tdls_oper_event(struct wpa_driver_nl80211_data *drv,
+ struct nlattr **tb)
+{
+ union wpa_event_data data;
+
+ wpa_printf(MSG_DEBUG, "nl80211: TDLS operation event");
+
+ if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_TDLS_OPERATION])
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+ os_memcpy(data.tdls.peer, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
+ switch (nla_get_u8(tb[NL80211_ATTR_TDLS_OPERATION])) {
+ case NL80211_TDLS_SETUP:
+ wpa_printf(MSG_DEBUG, "nl80211: TDLS setup request for peer "
+ MACSTR, MAC2STR(data.tdls.peer));
+ data.tdls.oper = TDLS_REQUEST_SETUP;
+ break;
+ case NL80211_TDLS_TEARDOWN:
+ wpa_printf(MSG_DEBUG, "nl80211: TDLS teardown request for peer "
+ MACSTR, MAC2STR(data.tdls.peer));
+ data.tdls.oper = TDLS_REQUEST_TEARDOWN;
+ break;
+ case NL80211_TDLS_DISCOVERY_REQ:
+ wpa_printf(MSG_DEBUG,
+ "nl80211: TDLS discovery request for peer " MACSTR,
+ MAC2STR(data.tdls.peer));
+ data.tdls.oper = TDLS_REQUEST_DISCOVER;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "nl80211: Unsupported TDLS operatione "
+ "event");
+ return;
+ }
+ if (tb[NL80211_ATTR_REASON_CODE]) {
+ data.tdls.reason_code =
+ nla_get_u16(tb[NL80211_ATTR_REASON_CODE]);
+ }
+
+ wpa_supplicant_event(drv->ctx, EVENT_TDLS, &data);
+}
+
+
+static void nl80211_stop_ap(struct wpa_driver_nl80211_data *drv,
+ struct nlattr **tb)
+{
+ wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_UNAVAILABLE, NULL);
+}
+
+
+static void nl80211_connect_failed_event(struct wpa_driver_nl80211_data *drv,
+ struct nlattr **tb)
+{
+ union wpa_event_data data;
+ u32 reason;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Connect failed event");
+
+ if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_CONN_FAILED_REASON])
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+ os_memcpy(data.connect_failed_reason.addr,
+ nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
+
+ reason = nla_get_u32(tb[NL80211_ATTR_CONN_FAILED_REASON]);
+ switch (reason) {
+ case NL80211_CONN_FAIL_MAX_CLIENTS:
+ wpa_printf(MSG_DEBUG, "nl80211: Max client reached");
+ data.connect_failed_reason.code = MAX_CLIENT_REACHED;
+ break;
+ case NL80211_CONN_FAIL_BLOCKED_CLIENT:
+ wpa_printf(MSG_DEBUG, "nl80211: Blocked client " MACSTR
+ " tried to connect",
+ MAC2STR(data.connect_failed_reason.addr));
+ data.connect_failed_reason.code = BLOCKED_CLIENT;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "nl8021l: Unknown connect failed reason "
+ "%u", reason);
+ return;
+ }
+
+ wpa_supplicant_event(drv->ctx, EVENT_CONNECT_FAILED_REASON, &data);
+}
+
+
+static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv,
+ struct nlattr **tb)
+{
+ union wpa_event_data data;
+ enum nl80211_radar_event event_type;
+
+ if (!tb[NL80211_ATTR_WIPHY_FREQ] || !tb[NL80211_ATTR_RADAR_EVENT])
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+ data.dfs_event.freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
+ event_type = nla_get_u32(tb[NL80211_ATTR_RADAR_EVENT]);
+
+ /* Check HT params */
+ if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
+ data.dfs_event.ht_enabled = 1;
+ data.dfs_event.chan_offset = 0;
+
+ switch (nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE])) {
+ case NL80211_CHAN_NO_HT:
+ data.dfs_event.ht_enabled = 0;
+ break;
+ case NL80211_CHAN_HT20:
+ break;
+ case NL80211_CHAN_HT40PLUS:
+ data.dfs_event.chan_offset = 1;
+ break;
+ case NL80211_CHAN_HT40MINUS:
+ data.dfs_event.chan_offset = -1;
+ break;
+ }
+ }
+
+ /* Get VHT params */
+ if (tb[NL80211_ATTR_CHANNEL_WIDTH])
+ data.dfs_event.chan_width =
+ convert2width(nla_get_u32(
+ tb[NL80211_ATTR_CHANNEL_WIDTH]));
+ if (tb[NL80211_ATTR_CENTER_FREQ1])
+ data.dfs_event.cf1 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]);
+ if (tb[NL80211_ATTR_CENTER_FREQ2])
+ data.dfs_event.cf2 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]);
+
+ wpa_printf(MSG_DEBUG, "nl80211: DFS event on freq %d MHz, ht: %d, offset: %d, width: %d, cf1: %dMHz, cf2: %dMHz",
+ data.dfs_event.freq, data.dfs_event.ht_enabled,
+ data.dfs_event.chan_offset, data.dfs_event.chan_width,
+ data.dfs_event.cf1, data.dfs_event.cf2);
+
+ switch (event_type) {
+ case NL80211_RADAR_DETECTED:
+ wpa_supplicant_event(drv->ctx, EVENT_DFS_RADAR_DETECTED, &data);
+ break;
+ case NL80211_RADAR_CAC_FINISHED:
+ wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_FINISHED, &data);
+ break;
+ case NL80211_RADAR_CAC_ABORTED:
+ wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_ABORTED, &data);
+ break;
+ case NL80211_RADAR_NOP_FINISHED:
+ wpa_supplicant_event(drv->ctx, EVENT_DFS_NOP_FINISHED, &data);
+ break;
+ case NL80211_RADAR_PRE_CAC_EXPIRED:
+ wpa_supplicant_event(drv->ctx, EVENT_DFS_PRE_CAC_EXPIRED,
+ &data);
+ break;
+ case NL80211_RADAR_CAC_STARTED:
+ wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_STARTED, &data);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "nl80211: Unknown radar event %d "
+ "received", event_type);
+ break;
+ }
+}
+
+
+static void nl80211_spurious_frame(struct i802_bss *bss, struct nlattr **tb,
+ int wds)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ union wpa_event_data event;
+
+ if (!tb[NL80211_ATTR_MAC])
+ return;
+
+ os_memset(&event, 0, sizeof(event));
+ event.rx_from_unknown.bssid = bss->addr;
+ event.rx_from_unknown.addr = nla_data(tb[NL80211_ATTR_MAC]);
+ event.rx_from_unknown.wds = wds;
+
+ wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event);
+}
+
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+
+static void qca_nl80211_avoid_freq(struct wpa_driver_nl80211_data *drv,
+ const u8 *data, size_t len)
+{
+ u32 i, count;
+ union wpa_event_data event;
+ struct wpa_freq_range *range = NULL;
+ const struct qca_avoid_freq_list *freq_range;
+
+ freq_range = (const struct qca_avoid_freq_list *) data;
+ if (len < sizeof(freq_range->count))
+ return;
+
+ count = freq_range->count;
+ if (len < sizeof(freq_range->count) +
+ count * sizeof(struct qca_avoid_freq_range)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Ignored too short avoid frequency list (len=%u)",
+ (unsigned int) len);
+ return;
+ }
+
+ if (count > 0) {
+ range = os_calloc(count, sizeof(struct wpa_freq_range));
+ if (range == NULL)
+ return;
+ }
+
+ os_memset(&event, 0, sizeof(event));
+ for (i = 0; i < count; i++) {
+ unsigned int idx = event.freq_range.num;
+ range[idx].min = freq_range->range[i].start_freq;
+ range[idx].max = freq_range->range[i].end_freq;
+ wpa_printf(MSG_DEBUG, "nl80211: Avoid frequency range: %u-%u",
+ range[idx].min, range[idx].max);
+ if (range[idx].min > range[idx].max) {
+ wpa_printf(MSG_DEBUG, "nl80211: Ignore invalid frequency range");
+ continue;
+ }
+ event.freq_range.num++;
+ }
+ event.freq_range.range = range;
+
+ wpa_supplicant_event(drv->ctx, EVENT_AVOID_FREQUENCIES, &event);
+
+ os_free(range);
+}
+
+
+static enum hostapd_hw_mode get_qca_hw_mode(u8 hw_mode)
+{
+ switch (hw_mode) {
+ case QCA_ACS_MODE_IEEE80211B:
+ return HOSTAPD_MODE_IEEE80211B;
+ case QCA_ACS_MODE_IEEE80211G:
+ return HOSTAPD_MODE_IEEE80211G;
+ case QCA_ACS_MODE_IEEE80211A:
+ return HOSTAPD_MODE_IEEE80211A;
+ case QCA_ACS_MODE_IEEE80211AD:
+ return HOSTAPD_MODE_IEEE80211AD;
+ case QCA_ACS_MODE_IEEE80211ANY:
+ return HOSTAPD_MODE_IEEE80211ANY;
+ default:
+ return NUM_HOSTAPD_MODES;
+ }
+}
+
+
+static unsigned int chan_to_freq(struct wpa_driver_nl80211_data *drv,
+ u8 chan, enum hostapd_hw_mode hw_mode)
+{
+ if (hw_mode == NUM_HOSTAPD_MODES) {
+ /* For drivers that do not report ACS_HW_MODE */
+ u16 num_modes, flags;
+ struct hostapd_hw_modes *modes;
+ u8 dfs_domain;
+ int i;
+
+ modes = nl80211_get_hw_feature_data(drv->first_bss, &num_modes,
+ &flags, &dfs_domain);
+ if (!modes) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Fetching hardware mode failed");
+ goto try_2_4_or_5;
+ }
+ if (num_modes == 1)
+ hw_mode = modes[0].mode;
+
+ for (i = 0; i < num_modes; i++) {
+ os_free(modes[i].channels);
+ os_free(modes[i].rates);
+ }
+
+ os_free(modes);
+ }
+
+ if (hw_mode == HOSTAPD_MODE_IEEE80211AD) {
+ if (chan >= 1 && chan <= 6)
+ return 56160 + (2160 * chan);
+ return 0;
+ }
+
+try_2_4_or_5:
+ if (chan >= 1 && chan <= 13)
+ return 2407 + 5 * chan;
+ if (chan == 14)
+ return 2484;
+ if (chan >= 36 && chan <= 177)
+ return 5000 + 5 * chan;
+
+ return 0;
+}
+
+
+static void qca_nl80211_acs_select_ch(struct wpa_driver_nl80211_data *drv,
+ const u8 *data, size_t len)
+{
+ struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_ACS_MAX + 1];
+ union wpa_event_data event;
+ u8 chan;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: ACS channel selection vendor event received");
+
+ if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ACS_MAX,
+ (struct nlattr *) data, len, NULL) ||
+ (!tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_FREQUENCY] &&
+ !tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL]) ||
+ (!tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_FREQUENCY] &&
+ !tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL]))
+ return;
+
+ os_memset(&event, 0, sizeof(event));
+ event.acs_selected_channels.hw_mode = NUM_HOSTAPD_MODES;
+
+ if (tb[QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE]) {
+ u8 hw_mode = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE]);
+
+ event.acs_selected_channels.hw_mode = get_qca_hw_mode(hw_mode);
+ if (event.acs_selected_channels.hw_mode == NUM_HOSTAPD_MODES ||
+ event.acs_selected_channels.hw_mode ==
+ HOSTAPD_MODE_IEEE80211ANY) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Invalid hw_mode %d in ACS selection event",
+ hw_mode);
+ return;
+ }
+ }
+
+ if (tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_FREQUENCY]) {
+ event.acs_selected_channels.pri_freq = nla_get_u32(
+ tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_FREQUENCY]);
+ } else {
+ chan = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL]);
+ event.acs_selected_channels.pri_freq =
+ chan_to_freq(drv, chan,
+ event.acs_selected_channels.hw_mode);
+ }
+
+ if (tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_FREQUENCY]) {
+ event.acs_selected_channels.sec_freq = nla_get_u32(
+ tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_FREQUENCY]);
+ } else {
+ chan = nla_get_u8(
+ tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL]);
+ event.acs_selected_channels.sec_freq =
+ chan_to_freq(drv, chan,
+ event.acs_selected_channels.hw_mode);
+ }
+
+ if (tb[QCA_WLAN_VENDOR_ATTR_ACS_EDMG_CHANNEL])
+ event.acs_selected_channels.edmg_channel =
+ nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_EDMG_CHANNEL]);
+ if (tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL])
+ event.acs_selected_channels.vht_seg0_center_ch =
+ nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL]);
+ if (tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL])
+ event.acs_selected_channels.vht_seg1_center_ch =
+ nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL]);
+ if (tb[QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH])
+ event.acs_selected_channels.ch_width =
+ nla_get_u16(tb[QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH]);
+ if (tb[QCA_WLAN_VENDOR_ATTR_ACS_PUNCTURE_BITMAP])
+ event.acs_selected_channels.puncture_bitmap =
+ nla_get_u16(tb[QCA_WLAN_VENDOR_ATTR_ACS_PUNCTURE_BITMAP]);
+ wpa_printf(MSG_INFO,
+ "nl80211: ACS Results: PFreq: %d SFreq: %d BW: %d VHT0: %d VHT1: %d HW_MODE: %d EDMGCH: %d PUNCBITMAP: 0x%x",
+ event.acs_selected_channels.pri_freq,
+ event.acs_selected_channels.sec_freq,
+ event.acs_selected_channels.ch_width,
+ event.acs_selected_channels.vht_seg0_center_ch,
+ event.acs_selected_channels.vht_seg1_center_ch,
+ event.acs_selected_channels.hw_mode,
+ event.acs_selected_channels.edmg_channel,
+ event.acs_selected_channels.puncture_bitmap);
+
+ /* Ignore ACS channel list check for backwards compatibility */
+
+ wpa_supplicant_event(drv->ctx, EVENT_ACS_CHANNEL_SELECTED, &event);
+}
+
+
+static void qca_nl80211_key_mgmt_auth(struct wpa_driver_nl80211_data *drv,
+ const u8 *data, size_t len)
+{
+ struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX + 1];
+ u8 *bssid;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Key management roam+auth vendor event received");
+
+ if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX,
+ (struct nlattr *) data, len, NULL) ||
+ !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID] ||
+ nla_len(tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID]) != ETH_ALEN ||
+ !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE] ||
+ !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE] ||
+ !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED])
+ return;
+
+ bssid = nla_data(tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID]);
+ wpa_printf(MSG_DEBUG, " * roam BSSID " MACSTR, MAC2STR(bssid));
+
+ mlme_event_connect(drv, NL80211_CMD_ROAM, true, NULL,
+ tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID],
+ tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE],
+ tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE],
+ NULL, NULL,
+ tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED],
+ tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR],
+ tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK],
+ tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK],
+ tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS],
+ tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_FILS_ERP_NEXT_SEQ_NUM],
+ tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMK],
+ tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMKID],
+ tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MLO_LINKS]);
+
+#ifdef ANDROID
+#ifdef ANDROID_LIB_EVENT
+ wpa_driver_nl80211_driver_event(
+ drv, OUI_QCA, QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH,
+ data, len);
+#endif /* ANDROID_LIB_EVENT */
+#endif /* ANDROID */
+}
+
+
+static void
+qca_nl80211_key_mgmt_auth_handler(struct wpa_driver_nl80211_data *drv,
+ const u8 *data, size_t len)
+{
+ if (!drv->roam_indication_done) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Pending roam indication, delay processing roam+auth vendor event");
+
+ os_free(drv->pending_roam_data);
+ drv->pending_roam_data = os_memdup(data, len);
+ if (!drv->pending_roam_data)
+ return;
+ drv->pending_roam_data_len = len;
+ return;
+ }
+ drv->roam_indication_done = false;
+ qca_nl80211_key_mgmt_auth(drv, data, len);
+}
+
+
+static void qca_nl80211_dfs_offload_radar_event(
+ struct wpa_driver_nl80211_data *drv, u32 subcmd, u8 *msg, int length)
+{
+ union wpa_event_data data;
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: DFS offload radar vendor event received");
+
+ if (nla_parse(tb, NL80211_ATTR_MAX,
+ (struct nlattr *) msg, length, NULL))
+ return;
+
+ if (!tb[NL80211_ATTR_WIPHY_FREQ]) {
+ wpa_printf(MSG_INFO,
+ "nl80211: Error parsing WIPHY_FREQ in FS offload radar vendor event");
+ return;
+ }
+
+ os_memset(&data, 0, sizeof(data));
+ data.dfs_event.freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
+
+ wpa_printf(MSG_DEBUG, "nl80211: DFS event on freq %d MHz",
+ data.dfs_event.freq);
+
+ /* Check HT params */
+ if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
+ data.dfs_event.ht_enabled = 1;
+ data.dfs_event.chan_offset = 0;
+
+ switch (nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE])) {
+ case NL80211_CHAN_NO_HT:
+ data.dfs_event.ht_enabled = 0;
+ break;
+ case NL80211_CHAN_HT20:
+ break;
+ case NL80211_CHAN_HT40PLUS:
+ data.dfs_event.chan_offset = 1;
+ break;
+ case NL80211_CHAN_HT40MINUS:
+ data.dfs_event.chan_offset = -1;
+ break;
+ }
+ }
+
+ /* Get VHT params */
+ if (tb[NL80211_ATTR_CHANNEL_WIDTH])
+ data.dfs_event.chan_width =
+ convert2width(nla_get_u32(
+ tb[NL80211_ATTR_CHANNEL_WIDTH]));
+ if (tb[NL80211_ATTR_CENTER_FREQ1])
+ data.dfs_event.cf1 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]);
+ if (tb[NL80211_ATTR_CENTER_FREQ2])
+ data.dfs_event.cf2 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]);
+
+ wpa_printf(MSG_DEBUG, "nl80211: DFS event on freq %d MHz, ht: %d, "
+ "offset: %d, width: %d, cf1: %dMHz, cf2: %dMHz",
+ data.dfs_event.freq, data.dfs_event.ht_enabled,
+ data.dfs_event.chan_offset, data.dfs_event.chan_width,
+ data.dfs_event.cf1, data.dfs_event.cf2);
+
+ switch (subcmd) {
+ case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED:
+ wpa_supplicant_event(drv->ctx, EVENT_DFS_RADAR_DETECTED, &data);
+ break;
+ case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED:
+ wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_STARTED, &data);
+ break;
+ case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_FINISHED:
+ wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_FINISHED, &data);
+ break;
+ case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED:
+ wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_ABORTED, &data);
+ break;
+ case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED:
+ wpa_supplicant_event(drv->ctx, EVENT_DFS_NOP_FINISHED, &data);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Unknown DFS offload radar event %d received",
+ subcmd);
+ break;
+ }
+}
+
+
+static void qca_nl80211_scan_trigger_event(struct wpa_driver_nl80211_data *drv,
+ u8 *data, size_t len)
+{
+ struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1];
+ u64 cookie = 0;
+ union wpa_event_data event;
+ struct scan_info *info;
+
+ if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SCAN_MAX,
+ (struct nlattr *) data, len, NULL) ||
+ !tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE])
+ return;
+
+ cookie = nla_get_u64(tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]);
+ if (cookie != drv->vendor_scan_cookie) {
+ /* External scan trigger event, ignore */
+ return;
+ }
+
+ /* Cookie match, own scan */
+ os_memset(&event, 0, sizeof(event));
+ info = &event.scan_info;
+ info->external_scan = 0;
+ info->nl_scan_event = 0;
+
+ drv->scan_state = SCAN_STARTED;
+ wpa_supplicant_event(drv->ctx, EVENT_SCAN_STARTED, &event);
+}
+
+
+static void send_vendor_scan_event(struct wpa_driver_nl80211_data *drv,
+ int aborted, struct nlattr *tb[],
+ int external_scan)
+{
+ union wpa_event_data event;
+ struct nlattr *nl;
+ int rem;
+ struct scan_info *info;
+ int freqs[MAX_REPORT_FREQS];
+ int num_freqs = 0;
+
+ os_memset(&event, 0, sizeof(event));
+ info = &event.scan_info;
+ info->aborted = aborted;
+ info->external_scan = external_scan;
+
+ if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS]) {
+ nla_for_each_nested(nl,
+ tb[QCA_WLAN_VENDOR_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);
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Scan probed for SSID '%s'",
+ wpa_ssid_txt(s->ssid, s->ssid_len));
+ info->num_ssids++;
+ if (info->num_ssids == WPAS_MAX_SCAN_SSIDS)
+ break;
+ }
+ }
+
+ if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES]) {
+ char msg[500], *pos, *end;
+ int res;
+
+ pos = msg;
+ end = pos + sizeof(msg);
+ *pos = '\0';
+
+ nla_for_each_nested(nl,
+ tb[QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES],
+ rem) {
+ freqs[num_freqs] = nla_get_u32(nl);
+ res = os_snprintf(pos, end - pos, " %d",
+ freqs[num_freqs]);
+ if (!os_snprintf_error(end - pos, res))
+ pos += res;
+ num_freqs++;
+ if (num_freqs == MAX_REPORT_FREQS - 1)
+ break;
+ }
+
+ info->freqs = freqs;
+ info->num_freqs = num_freqs;
+ wpa_printf(MSG_DEBUG, "nl80211: Scan included frequencies:%s",
+ msg);
+ }
+ wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event);
+}
+
+
+static void qca_nl80211_scan_done_event(struct wpa_driver_nl80211_data *drv,
+ u8 *data, size_t len)
+{
+ struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1];
+ u64 cookie = 0;
+ enum scan_status status;
+ int external_scan;
+
+ if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SCAN_MAX,
+ (struct nlattr *) data, len, NULL) ||
+ !tb[QCA_WLAN_VENDOR_ATTR_SCAN_STATUS] ||
+ !tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE])
+ return;
+
+ status = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_SCAN_STATUS]);
+ if (status >= VENDOR_SCAN_STATUS_MAX)
+ return; /* invalid status */
+
+ cookie = nla_get_u64(tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]);
+ if (cookie != drv->vendor_scan_cookie) {
+ /* Event from an external scan, get scan results */
+ external_scan = 1;
+ } else {
+ external_scan = 0;
+ if (status == VENDOR_SCAN_STATUS_NEW_RESULTS)
+ drv->scan_state = SCAN_COMPLETED;
+ else
+ drv->scan_state = SCAN_ABORTED;
+
+ eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv,
+ drv->ctx);
+ drv->vendor_scan_cookie = 0;
+ drv->last_scan_cmd = 0;
+ }
+
+ send_vendor_scan_event(drv, (status == VENDOR_SCAN_STATUS_ABORTED), tb,
+ external_scan);
+}
+
+
+static void qca_nl80211_p2p_lo_stop_event(struct wpa_driver_nl80211_data *drv,
+ u8 *data, size_t len)
+{
+ struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_MAX + 1];
+ union wpa_event_data event;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: P2P listen offload stop vendor event received");
+
+ if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_MAX,
+ (struct nlattr *) data, len, NULL) ||
+ !tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_STOP_REASON])
+ return;
+
+ os_memset(&event, 0, sizeof(event));
+ event.p2p_lo_stop.reason_code =
+ nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_STOP_REASON]);
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: P2P Listen offload stop reason: %d",
+ event.p2p_lo_stop.reason_code);
+ wpa_supplicant_event(drv->ctx, EVENT_P2P_LO_STOP, &event);
+}
+
+
+#ifdef CONFIG_PASN
+
+static void qca_nl80211_pasn_auth(struct wpa_driver_nl80211_data *drv,
+ u8 *data, size_t len)
+{
+ int ret = -EINVAL;
+ struct nlattr *attr;
+ struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MAX + 1];
+ struct nlattr *cfg[QCA_WLAN_VENDOR_ATTR_PASN_PEER_MAX + 1];
+ unsigned int n_peers = 0, idx = 0;
+ int rem_conf;
+ enum qca_wlan_vendor_pasn_action action;
+ union wpa_event_data event;
+
+ if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_PASN_MAX,
+ (struct nlattr *) data, len, NULL) ||
+ !tb[QCA_WLAN_VENDOR_ATTR_PASN_PEERS] ||
+ !tb[QCA_WLAN_VENDOR_ATTR_PASN_ACTION]) {
+ return;
+ }
+
+ os_memset(&event, 0, sizeof(event));
+ action = nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_PASN_ACTION]);
+ switch (action) {
+ case QCA_WLAN_VENDOR_PASN_ACTION_AUTH:
+ event.pasn_auth.action = PASN_ACTION_AUTH;
+ break;
+ case QCA_WLAN_VENDOR_PASN_ACTION_DELETE_SECURE_RANGING_CONTEXT:
+ event.pasn_auth.action =
+ PASN_ACTION_DELETE_SECURE_RANGING_CONTEXT;
+ break;
+ default:
+ return;
+ }
+
+ nla_for_each_nested(attr, tb[QCA_WLAN_VENDOR_ATTR_PASN_PEERS], rem_conf)
+ n_peers++;
+
+ if (n_peers > WPAS_MAX_PASN_PEERS) {
+ wpa_printf(MSG_DEBUG, "nl80211: PASN auth: too many peers (%d)",
+ n_peers);
+ return;
+ }
+
+ nla_for_each_nested(attr, tb[QCA_WLAN_VENDOR_ATTR_PASN_PEERS],
+ rem_conf) {
+ struct nlattr *nl_src, *nl_peer;
+
+ ret = nla_parse_nested(cfg, QCA_WLAN_VENDOR_ATTR_PASN_PEER_MAX,
+ attr, NULL);
+ if (ret)
+ return;
+ nl_src = cfg[QCA_WLAN_VENDOR_ATTR_PASN_PEER_SRC_ADDR];
+ nl_peer = cfg[QCA_WLAN_VENDOR_ATTR_PASN_PEER_MAC_ADDR];
+ if (nl_src)
+ os_memcpy(event.pasn_auth.peer[idx].own_addr,
+ nla_data(nl_src), ETH_ALEN);
+ if (nl_peer)
+ os_memcpy(event.pasn_auth.peer[idx].peer_addr,
+ nla_data(nl_peer), ETH_ALEN);
+ if (cfg[QCA_WLAN_VENDOR_ATTR_PASN_PEER_LTF_KEYSEED_REQUIRED])
+ event.pasn_auth.peer[idx].ltf_keyseed_required = true;
+ idx++;
+ }
+ event.pasn_auth.num_peers = n_peers;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: PASN auth action: %u, num_bssids: %d",
+ event.pasn_auth.action,
+ event.pasn_auth.num_peers);
+ wpa_supplicant_event(drv->ctx, EVENT_PASN_AUTH, &event);
+}
+
+#endif /* CONFIG_PASN */
+
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+
+static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv,
+ u32 subcmd, u8 *data, size_t len)
+{
+ switch (subcmd) {
+ case QCA_NL80211_VENDOR_SUBCMD_TEST:
+ wpa_hexdump(MSG_DEBUG, "nl80211: QCA test event", data, len);
+ break;
+#ifdef CONFIG_DRIVER_NL80211_QCA
+ case QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY:
+ qca_nl80211_avoid_freq(drv, data, len);
+ break;
+ case QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH:
+ qca_nl80211_key_mgmt_auth_handler(drv, data, len);
+ break;
+ case QCA_NL80211_VENDOR_SUBCMD_DO_ACS:
+ qca_nl80211_acs_select_ch(drv, data, len);
+ break;
+ case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED:
+ case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_FINISHED:
+ case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED:
+ case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED:
+ case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED:
+ qca_nl80211_dfs_offload_radar_event(drv, subcmd, data, len);
+ break;
+ case QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN:
+ qca_nl80211_scan_trigger_event(drv, data, len);
+ break;
+ case QCA_NL80211_VENDOR_SUBCMD_SCAN_DONE:
+ qca_nl80211_scan_done_event(drv, data, len);
+ break;
+ case QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP:
+ qca_nl80211_p2p_lo_stop_event(drv, data, len);
+ break;
+#ifdef CONFIG_PASN
+ case QCA_NL80211_VENDOR_SUBCMD_PASN:
+ qca_nl80211_pasn_auth(drv, data, len);
+ break;
+#endif /* CONFIG_PASN */
+ case QCA_NL80211_VENDOR_SUBCMD_TID_TO_LINK_MAP:
+ qca_nl80211_tid_to_link_map_event(drv, data, len);
+ break;
+ case QCA_NL80211_VENDOR_SUBCMD_LINK_RECONFIG:
+ qca_nl80211_link_reconfig_event(drv, data, len);
+ break;
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+ default:
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Ignore unsupported QCA vendor event %u",
+ subcmd);
+ break;
+ }
+}
+
+
+#ifdef CONFIG_DRIVER_NL80211_BRCM
+
+static void brcm_nl80211_acs_select_ch(struct wpa_driver_nl80211_data *drv,
+ const u8 *data, size_t len)
+{
+ struct nlattr *tb[BRCM_VENDOR_ATTR_ACS_LAST + 1];
+ union wpa_event_data event;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: BRCM ACS channel selection vendor event received");
+
+ if (nla_parse(tb, BRCM_VENDOR_ATTR_ACS_LAST, (struct nlattr *) data,
+ len, NULL) ||
+ !tb[BRCM_VENDOR_ATTR_ACS_PRIMARY_FREQ] ||
+ !tb[BRCM_VENDOR_ATTR_ACS_SECONDARY_FREQ])
+ return;
+
+ os_memset(&event, 0, sizeof(event));
+ if (tb[BRCM_VENDOR_ATTR_ACS_PRIMARY_FREQ])
+ event.acs_selected_channels.pri_freq =
+ nla_get_u32(tb[BRCM_VENDOR_ATTR_ACS_PRIMARY_FREQ]);
+ if (tb[BRCM_VENDOR_ATTR_ACS_SECONDARY_FREQ])
+ event.acs_selected_channels.sec_freq =
+ nla_get_u32(tb[BRCM_VENDOR_ATTR_ACS_SECONDARY_FREQ]);
+ if (tb[BRCM_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL])
+ event.acs_selected_channels.vht_seg0_center_ch =
+ nla_get_u8(tb[BRCM_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL]);
+ if (tb[BRCM_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL])
+ event.acs_selected_channels.vht_seg1_center_ch =
+ nla_get_u8(tb[BRCM_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL]);
+ if (tb[BRCM_VENDOR_ATTR_ACS_CHWIDTH])
+ event.acs_selected_channels.ch_width =
+ nla_get_u16(tb[BRCM_VENDOR_ATTR_ACS_CHWIDTH]);
+ if (tb[BRCM_VENDOR_ATTR_ACS_HW_MODE]) {
+ event.acs_selected_channels.hw_mode = nla_get_u8(tb[BRCM_VENDOR_ATTR_ACS_HW_MODE]);
+ if (event.acs_selected_channels.hw_mode == NUM_HOSTAPD_MODES ||
+ event.acs_selected_channels.hw_mode ==
+ HOSTAPD_MODE_IEEE80211ANY) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Invalid hw_mode %d in ACS selection event",
+ event.acs_selected_channels.hw_mode);
+ return;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: ACS Results: PCH: %d SCH: %d BW: %d VHT0: %d VHT1: %d HW_MODE: %d",
+ event.acs_selected_channels.pri_freq,
+ event.acs_selected_channels.sec_freq,
+ event.acs_selected_channels.ch_width,
+ event.acs_selected_channels.vht_seg0_center_ch,
+ event.acs_selected_channels.vht_seg1_center_ch,
+ event.acs_selected_channels.hw_mode);
+ wpa_supplicant_event(drv->ctx, EVENT_ACS_CHANNEL_SELECTED, &event);
+}
+
+
+static void nl80211_vendor_event_brcm(struct wpa_driver_nl80211_data *drv,
+ u32 subcmd, u8 *data, size_t len)
+{
+ wpa_printf(MSG_DEBUG, "nl80211: Got BRCM vendor event %u", subcmd);
+ switch (subcmd) {
+ case BRCM_VENDOR_EVENT_PRIV_STR:
+ case BRCM_VENDOR_EVENT_HANGED:
+ /* Dump the event on to the console */
+ wpa_msg(NULL, MSG_INFO, "%s", data);
+ break;
+ case BRCM_VENDOR_EVENT_ACS:
+ brcm_nl80211_acs_select_ch(drv, data, len);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG,
+ "%s: Ignore unsupported BRCM vendor event %u",
+ __func__, subcmd);
+ break;
+ }
+}
+
+#endif /* CONFIG_DRIVER_NL80211_BRCM */
+
+
+static void nl80211_vendor_event(struct wpa_driver_nl80211_data *drv,
+ struct nlattr **tb)
+{
+ u32 vendor_id, subcmd, wiphy = 0;
+ int wiphy_idx;
+ u8 *data = NULL;
+ size_t len = 0;
+
+ if (!tb[NL80211_ATTR_VENDOR_ID] ||
+ !tb[NL80211_ATTR_VENDOR_SUBCMD])
+ return;
+
+ vendor_id = nla_get_u32(tb[NL80211_ATTR_VENDOR_ID]);
+ subcmd = nla_get_u32(tb[NL80211_ATTR_VENDOR_SUBCMD]);
+
+ if (tb[NL80211_ATTR_WIPHY])
+ wiphy = nla_get_u32(tb[NL80211_ATTR_WIPHY]);
+
+ wpa_printf(MSG_DEBUG, "nl80211: Vendor event: wiphy=%u vendor_id=0x%x subcmd=%u",
+ wiphy, vendor_id, subcmd);
+
+ if (tb[NL80211_ATTR_VENDOR_DATA]) {
+ data = nla_data(tb[NL80211_ATTR_VENDOR_DATA]);
+ len = nla_len(tb[NL80211_ATTR_VENDOR_DATA]);
+ wpa_hexdump(MSG_MSGDUMP, "nl80211: Vendor data", data, len);
+ }
+
+ wiphy_idx = nl80211_get_wiphy_index(drv->first_bss);
+ if (wiphy_idx >= 0 && wiphy_idx != (int) wiphy) {
+ wpa_printf(MSG_DEBUG, "nl80211: Ignore vendor event for foreign wiphy %u (own: %d)",
+ wiphy, wiphy_idx);
+ return;
+ }
+
+#ifdef ANDROID
+#ifdef ANDROID_LIB_EVENT
+ /* Postpone QCA roam+auth event indication to the point when both that
+ * and the NL80211_CMD_ROAM event have been received (see calls to
+ * qca_nl80211_key_mgmt_auth() and drv->pending_roam_data). */
+ if (!(vendor_id == OUI_QCA &&
+ subcmd == QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH))
+ wpa_driver_nl80211_driver_event(drv, vendor_id, subcmd, data,
+ len);
+#endif /* ANDROID_LIB_EVENT */
+#endif /* ANDROID */
+
+ switch (vendor_id) {
+ case OUI_QCA:
+ nl80211_vendor_event_qca(drv, subcmd, data, len);
+ break;
+#ifdef CONFIG_DRIVER_NL80211_BRCM
+ case OUI_BRCM:
+ nl80211_vendor_event_brcm(drv, subcmd, data, len);
+ break;
+#endif /* CONFIG_DRIVER_NL80211_BRCM */
+ default:
+ wpa_printf(MSG_DEBUG, "nl80211: Ignore unsupported vendor event");
+ break;
+ }
+}
+
+
+static void nl80211_reg_change_event(struct wpa_driver_nl80211_data *drv,
+ struct nlattr *tb[])
+{
+ union wpa_event_data data;
+ enum nl80211_reg_initiator init;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Regulatory domain change");
+
+ if (tb[NL80211_ATTR_REG_INITIATOR] == NULL)
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+ init = nla_get_u8(tb[NL80211_ATTR_REG_INITIATOR]);
+ wpa_printf(MSG_DEBUG, " * initiator=%d", init);
+ switch (init) {
+ case NL80211_REGDOM_SET_BY_CORE:
+ data.channel_list_changed.initiator = REGDOM_SET_BY_CORE;
+ break;
+ case NL80211_REGDOM_SET_BY_USER:
+ data.channel_list_changed.initiator = REGDOM_SET_BY_USER;
+ break;
+ case NL80211_REGDOM_SET_BY_DRIVER:
+ data.channel_list_changed.initiator = REGDOM_SET_BY_DRIVER;
+ break;
+ case NL80211_REGDOM_SET_BY_COUNTRY_IE:
+ data.channel_list_changed.initiator = REGDOM_SET_BY_COUNTRY_IE;
+ break;
+ }
+
+ if (tb[NL80211_ATTR_REG_TYPE]) {
+ enum nl80211_reg_type type;
+ type = nla_get_u8(tb[NL80211_ATTR_REG_TYPE]);
+ wpa_printf(MSG_DEBUG, " * type=%d", type);
+ switch (type) {
+ case NL80211_REGDOM_TYPE_COUNTRY:
+ data.channel_list_changed.type = REGDOM_TYPE_COUNTRY;
+ break;
+ case NL80211_REGDOM_TYPE_WORLD:
+ data.channel_list_changed.type = REGDOM_TYPE_WORLD;
+ break;
+ case NL80211_REGDOM_TYPE_CUSTOM_WORLD:
+ data.channel_list_changed.type =
+ REGDOM_TYPE_CUSTOM_WORLD;
+ break;
+ case NL80211_REGDOM_TYPE_INTERSECTION:
+ data.channel_list_changed.type =
+ REGDOM_TYPE_INTERSECTION;
+ break;
+ }
+ }
+
+ if (tb[NL80211_ATTR_REG_ALPHA2]) {
+ os_strlcpy(data.channel_list_changed.alpha2,
+ nla_get_string(tb[NL80211_ATTR_REG_ALPHA2]),
+ sizeof(data.channel_list_changed.alpha2));
+ wpa_printf(MSG_DEBUG, " * alpha2=%s",
+ data.channel_list_changed.alpha2);
+ }
+
+ wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED, &data);
+}
+
+
+static void nl80211_dump_freq(const char *title, struct nlattr *nl_freq)
+{
+ 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_NO_IR] = { .type = NLA_FLAG },
+ [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG },
+ [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 },
+ };
+ struct nlattr *tb[NL80211_FREQUENCY_ATTR_MAX + 1];
+ u32 freq = 0, max_tx_power = 0;
+
+ nla_parse(tb, NL80211_FREQUENCY_ATTR_MAX,
+ nla_data(nl_freq), nla_len(nl_freq), freq_policy);
+
+ if (tb[NL80211_FREQUENCY_ATTR_FREQ])
+ freq = nla_get_u32(tb[NL80211_FREQUENCY_ATTR_FREQ]);
+ if (tb[NL80211_FREQUENCY_ATTR_MAX_TX_POWER])
+ max_tx_power =
+ nla_get_u32(tb[NL80211_FREQUENCY_ATTR_MAX_TX_POWER]);
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Channel (%s): freq=%u max_tx_power=%u%s%s%s",
+ title, freq, max_tx_power,
+ tb[NL80211_FREQUENCY_ATTR_DISABLED] ? " disabled" : "",
+ tb[NL80211_FREQUENCY_ATTR_NO_IR] ? " no-IR" : "",
+ tb[NL80211_FREQUENCY_ATTR_RADAR] ? " radar" : "");
+}
+
+
+static void nl80211_reg_beacon_hint_event(struct wpa_driver_nl80211_data *drv,
+ struct nlattr *tb[])
+{
+ union wpa_event_data data;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Regulatory beacon hint");
+ os_memset(&data, 0, sizeof(data));
+ data.channel_list_changed.initiator = REGDOM_BEACON_HINT;
+
+ if (tb[NL80211_ATTR_FREQ_BEFORE])
+ nl80211_dump_freq("before", tb[NL80211_ATTR_FREQ_BEFORE]);
+ if (tb[NL80211_ATTR_FREQ_AFTER])
+ nl80211_dump_freq("after", tb[NL80211_ATTR_FREQ_AFTER]);
+
+ wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED, &data);
+}
+
+
+static void nl80211_external_auth(struct wpa_driver_nl80211_data *drv,
+ struct nlattr **tb)
+{
+ union wpa_event_data event;
+ enum nl80211_external_auth_action act;
+ char mld_addr[50];
+
+ if (!tb[NL80211_ATTR_AKM_SUITES] ||
+ !tb[NL80211_ATTR_EXTERNAL_AUTH_ACTION] ||
+ !tb[NL80211_ATTR_BSSID] ||
+ !tb[NL80211_ATTR_SSID])
+ return;
+
+ os_memset(&event, 0, sizeof(event));
+ act = nla_get_u32(tb[NL80211_ATTR_EXTERNAL_AUTH_ACTION]);
+ switch (act) {
+ case NL80211_EXTERNAL_AUTH_START:
+ event.external_auth.action = EXT_AUTH_START;
+ break;
+ case NL80211_EXTERNAL_AUTH_ABORT:
+ event.external_auth.action = EXT_AUTH_ABORT;
+ break;
+ default:
+ return;
+ }
+
+ event.external_auth.key_mgmt_suite =
+ nla_get_u32(tb[NL80211_ATTR_AKM_SUITES]);
+
+ event.external_auth.ssid_len = nla_len(tb[NL80211_ATTR_SSID]);
+ if (event.external_auth.ssid_len > SSID_MAX_LEN)
+ return;
+ event.external_auth.ssid = nla_data(tb[NL80211_ATTR_SSID]);
+
+ event.external_auth.bssid = nla_data(tb[NL80211_ATTR_BSSID]);
+
+ mld_addr[0] = '\0';
+ if (tb[NL80211_ATTR_MLD_ADDR]) {
+ event.external_auth.mld_addr =
+ nla_data(tb[NL80211_ATTR_MLD_ADDR]);
+ os_snprintf(mld_addr, sizeof(mld_addr), ", MLD ADDR: " MACSTR,
+ MAC2STR(event.external_auth.mld_addr));
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: External auth action: %u, AKM: 0x%x, SSID: %s, BSSID: " MACSTR "%s",
+ event.external_auth.action,
+ event.external_auth.key_mgmt_suite,
+ wpa_ssid_txt(event.external_auth.ssid,
+ event.external_auth.ssid_len),
+ MAC2STR(event.external_auth.bssid), mld_addr);
+ wpa_supplicant_event(drv->ctx, EVENT_EXTERNAL_AUTH, &event);
+}
+
+
+static void nl80211_port_authorized(struct wpa_driver_nl80211_data *drv,
+ struct nlattr **tb)
+{
+ const u8 *addr;
+ union wpa_event_data event;
+
+ os_memset(&event, 0, sizeof(event));
+
+ if (!tb[NL80211_ATTR_MAC] ||
+ nla_len(tb[NL80211_ATTR_MAC]) != ETH_ALEN) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Ignore port authorized event without BSSID");
+ return;
+ }
+
+ addr = nla_data(tb[NL80211_ATTR_MAC]);
+ if (os_memcmp(addr, drv->bssid, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Ignore port authorized event for " MACSTR
+ " (not the currently connected BSSID " MACSTR ")",
+ MAC2STR(addr), MAC2STR(drv->bssid));
+ return;
+ }
+
+ if (tb[NL80211_ATTR_TD_BITMAP]) {
+ event.port_authorized.td_bitmap_len =
+ nla_len(tb[NL80211_ATTR_TD_BITMAP]);
+ if (event.port_authorized.td_bitmap_len > 0)
+ event.port_authorized.td_bitmap =
+ nla_data(tb[NL80211_ATTR_TD_BITMAP]);
+ }
+
+ wpa_supplicant_event(drv->ctx, EVENT_PORT_AUTHORIZED, &event);
+}
+
+
+static void nl80211_sta_opmode_change_event(struct wpa_driver_nl80211_data *drv,
+ struct nlattr **tb)
+{
+ union wpa_event_data ed;
+ u8 smps_mode, max_bw;
+
+ if (!tb[NL80211_ATTR_MAC] ||
+ (!tb[NL80211_ATTR_CHANNEL_WIDTH] &&
+ !tb[NL80211_ATTR_SMPS_MODE] &&
+ !tb[NL80211_ATTR_NSS]))
+ return;
+
+ ed.sta_opmode.smps_mode = SMPS_INVALID;
+ ed.sta_opmode.chan_width = CHAN_WIDTH_UNKNOWN;
+ ed.sta_opmode.rx_nss = 0xff;
+ ed.sta_opmode.addr = nla_data(tb[NL80211_ATTR_MAC]);
+
+ if (tb[NL80211_ATTR_SMPS_MODE]) {
+ smps_mode = nla_get_u8(tb[NL80211_ATTR_SMPS_MODE]);
+ switch (smps_mode) {
+ case NL80211_SMPS_OFF:
+ ed.sta_opmode.smps_mode = SMPS_OFF;
+ break;
+ case NL80211_SMPS_STATIC:
+ ed.sta_opmode.smps_mode = SMPS_STATIC;
+ break;
+ case NL80211_SMPS_DYNAMIC:
+ ed.sta_opmode.smps_mode = SMPS_DYNAMIC;
+ break;
+ default:
+ ed.sta_opmode.smps_mode = SMPS_INVALID;
+ break;
+ }
+ }
+
+ if (tb[NL80211_ATTR_CHANNEL_WIDTH]) {
+ max_bw = nla_get_u32(tb[NL80211_ATTR_CHANNEL_WIDTH]);
+ switch (max_bw) {
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ ed.sta_opmode.chan_width = CHAN_WIDTH_20_NOHT;
+ break;
+ case NL80211_CHAN_WIDTH_20:
+ ed.sta_opmode.chan_width = CHAN_WIDTH_20;
+ break;
+ case NL80211_CHAN_WIDTH_40:
+ ed.sta_opmode.chan_width = CHAN_WIDTH_40;
+ break;
+ case NL80211_CHAN_WIDTH_80:
+ ed.sta_opmode.chan_width = CHAN_WIDTH_80;
+ break;
+ case NL80211_CHAN_WIDTH_80P80:
+ ed.sta_opmode.chan_width = CHAN_WIDTH_80P80;
+ break;
+ case NL80211_CHAN_WIDTH_160:
+ ed.sta_opmode.chan_width = CHAN_WIDTH_160;
+ break;
+ case NL80211_CHAN_WIDTH_320:
+ ed.sta_opmode.chan_width = CHAN_WIDTH_320;
+ break;
+ default:
+ ed.sta_opmode.chan_width = CHAN_WIDTH_UNKNOWN;
+ break;
+
+ }
+ }
+
+ if (tb[NL80211_ATTR_NSS])
+ ed.sta_opmode.rx_nss = nla_get_u8(tb[NL80211_ATTR_NSS]);
+
+ wpa_supplicant_event(drv->ctx, EVENT_STATION_OPMODE_CHANGED, &ed);
+}
+
+
+static void nl80211_control_port_frame(struct wpa_driver_nl80211_data *drv,
+ struct nlattr **tb)
+{
+ u8 *src_addr;
+ u16 ethertype;
+ enum frame_encryption encrypted;
+ int link_id;
+
+ if (!tb[NL80211_ATTR_MAC] ||
+ !tb[NL80211_ATTR_FRAME] ||
+ !tb[NL80211_ATTR_CONTROL_PORT_ETHERTYPE])
+ return;
+
+ src_addr = nla_data(tb[NL80211_ATTR_MAC]);
+ ethertype = nla_get_u16(tb[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]);
+ encrypted = nla_get_flag(tb[NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT]) ?
+ FRAME_NOT_ENCRYPTED : FRAME_ENCRYPTED;
+
+ if (tb[NL80211_ATTR_MLO_LINK_ID])
+ link_id = nla_get_u8(tb[NL80211_ATTR_MLO_LINK_ID]);
+ else
+ link_id = -1;
+
+ switch (ethertype) {
+ case ETH_P_RSN_PREAUTH:
+ wpa_printf(MSG_INFO, "nl80211: Got pre-auth frame from "
+ MACSTR " over control port unexpectedly",
+ MAC2STR(src_addr));
+ break;
+ case ETH_P_PAE:
+ drv_event_eapol_rx2(drv->ctx, src_addr,
+ nla_data(tb[NL80211_ATTR_FRAME]),
+ nla_len(tb[NL80211_ATTR_FRAME]),
+ encrypted, link_id);
+ break;
+ default:
+ wpa_printf(MSG_INFO,
+ "nl80211: Unexpected ethertype 0x%04x from "
+ MACSTR " over control port",
+ ethertype, MAC2STR(src_addr));
+ break;
+ }
+}
+
+
+static void
+nl80211_control_port_frame_tx_status(struct wpa_driver_nl80211_data *drv,
+ const u8 *frame, size_t len,
+ struct nlattr *ack, struct nlattr *cookie)
+{
+ union wpa_event_data event;
+
+ if (!cookie || len < ETH_HLEN)
+ return;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Control port TX status (ack=%d), cookie=%llu",
+ ack != NULL, (long long unsigned int) nla_get_u64(cookie));
+
+ os_memset(&event, 0, sizeof(event));
+ event.eapol_tx_status.dst = frame;
+ event.eapol_tx_status.data = frame + ETH_HLEN;
+ event.eapol_tx_status.data_len = len - ETH_HLEN;
+ event.eapol_tx_status.ack = ack != NULL;
+ event.eapol_tx_status.link_id =
+ nla_get_u64(cookie) == drv->eapol_tx_cookie ?
+ drv->eapol_tx_link_id : NL80211_DRV_LINK_ID_NA;
+
+ wpa_supplicant_event(drv->ctx, EVENT_EAPOL_TX_STATUS, &event);
+}
+
+
+static void nl80211_frame_wait_cancel(struct wpa_driver_nl80211_data *drv,
+ struct nlattr *cookie_attr)
+{
+ unsigned int i;
+ u64 cookie;
+ bool match = false;
+
+ if (!cookie_attr)
+ return;
+ cookie = nla_get_u64(cookie_attr);
+
+ for (i = 0; i < drv->num_send_frame_cookies; i++) {
+ if (cookie == drv->send_frame_cookies[i]) {
+ match = true;
+ break;
+ }
+ }
+ wpa_printf(MSG_DEBUG,
+ "nl80211: TX frame wait expired for cookie 0x%llx%s%s",
+ (long long unsigned int) cookie,
+ match ? " (match)" : "",
+ drv->send_frame_cookie == cookie ? " (match-saved)" : "");
+ if (drv->send_frame_cookie == cookie)
+ drv->send_frame_cookie = (u64) -1;
+ if (!match)
+ return;
+
+ if (i < drv->num_send_frame_cookies - 1)
+ os_memmove(&drv->send_frame_cookies[i],
+ &drv->send_frame_cookies[i + 1],
+ (drv->num_send_frame_cookies - i - 1) * sizeof(u64));
+ drv->num_send_frame_cookies--;
+
+ wpa_supplicant_event(drv->ctx, EVENT_TX_WAIT_EXPIRE, NULL);
+}
+
+
+static void nl80211_assoc_comeback(struct wpa_driver_nl80211_data *drv,
+ struct nlattr *mac, struct nlattr *timeout)
+{
+ if (!mac || !timeout)
+ return;
+ wpa_printf(MSG_DEBUG, "nl80211: Association comeback requested by "
+ MACSTR " (timeout: %u ms)",
+ MAC2STR((u8 *) nla_data(mac)), nla_get_u32(timeout));
+}
+
+
+#ifdef CONFIG_IEEE80211AX
+
+static void nl80211_obss_color_collision(struct wpa_driver_nl80211_data *drv,
+ struct nlattr *tb[])
+{
+ union wpa_event_data data;
+
+ if (!tb[NL80211_ATTR_OBSS_COLOR_BITMAP])
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+ data.bss_color_collision.bitmap =
+ nla_get_u64(tb[NL80211_ATTR_OBSS_COLOR_BITMAP]);
+
+ wpa_printf(MSG_DEBUG, "nl80211: BSS color collision - bitmap %08llx",
+ (long long unsigned int) data.bss_color_collision.bitmap);
+ wpa_supplicant_event(drv->ctx, EVENT_BSS_COLOR_COLLISION, &data);
+}
+
+
+static void
+nl80211_color_change_announcement_started(struct wpa_driver_nl80211_data *drv)
+{
+ union wpa_event_data data = {};
+
+ wpa_printf(MSG_DEBUG, "nl80211: CCA started");
+ wpa_supplicant_event(drv->ctx, EVENT_CCA_STARTED_NOTIFY, &data);
+}
+
+
+static void
+nl80211_color_change_announcement_aborted(struct wpa_driver_nl80211_data *drv)
+{
+ union wpa_event_data data = {};
+
+ wpa_printf(MSG_DEBUG, "nl80211: CCA aborted");
+ wpa_supplicant_event(drv->ctx, EVENT_CCA_ABORTED_NOTIFY, &data);
+}
+
+
+static void
+nl80211_color_change_announcement_completed(struct wpa_driver_nl80211_data *drv)
+{
+ union wpa_event_data data = {};
+
+ wpa_printf(MSG_DEBUG, "nl80211: CCA completed");
+ wpa_supplicant_event(drv->ctx, EVENT_CCA_NOTIFY, &data);
+}
+
+#endif /* CONFIG_IEEE80211AX */
+
+
+static void do_process_drv_event(struct i802_bss *bss, int cmd,
+ struct nlattr **tb)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int external_scan_event = 0;
+ struct nlattr *frame = tb[NL80211_ATTR_FRAME];
+
+ wpa_printf(MSG_DEBUG, "nl80211: Drv Event %d (%s) received for %s",
+ cmd, nl80211_command_to_string(cmd), bss->ifname);
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+ if (cmd == NL80211_CMD_ROAM &&
+ (drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD)) {
+ if (drv->pending_roam_data) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Process pending roam+auth vendor event");
+ qca_nl80211_key_mgmt_auth(drv, drv->pending_roam_data,
+ drv->pending_roam_data_len);
+ os_free(drv->pending_roam_data);
+ drv->pending_roam_data = NULL;
+ return;
+ }
+ /*
+ * Device will use roam+auth vendor event to indicate
+ * roaming, so ignore the regular roam event.
+ */
+ drv->roam_indication_done = true;
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Ignore roam event (cmd=%d), device will use vendor event roam+auth",
+ cmd);
+ return;
+ }
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+ if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED &&
+ (cmd == NL80211_CMD_NEW_SCAN_RESULTS ||
+ cmd == NL80211_CMD_SCAN_ABORTED))
+ nl80211_restore_ap_mode(bss);
+
+ switch (cmd) {
+ case NL80211_CMD_TRIGGER_SCAN:
+ wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan trigger");
+ drv->scan_state = SCAN_STARTED;
+ if (drv->scan_for_auth) {
+ /*
+ * Cannot indicate EVENT_SCAN_STARTED here since we skip
+ * EVENT_SCAN_RESULTS in scan_for_auth case and the
+ * upper layer implementation could get confused about
+ * scanning state.
+ */
+ wpa_printf(MSG_DEBUG, "nl80211: Do not indicate scan-start event due to internal scan_for_auth");
+ break;
+ }
+ wpa_supplicant_event(drv->ctx, EVENT_SCAN_STARTED, NULL);
+ break;
+ case NL80211_CMD_START_SCHED_SCAN:
+ wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Sched scan started");
+ drv->scan_state = SCHED_SCAN_STARTED;
+ break;
+ case NL80211_CMD_SCHED_SCAN_STOPPED:
+ wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Sched scan stopped");
+ drv->scan_state = SCHED_SCAN_STOPPED;
+ wpa_supplicant_event(drv->ctx, EVENT_SCHED_SCAN_STOPPED, NULL);
+ break;
+ case NL80211_CMD_NEW_SCAN_RESULTS:
+ wpa_dbg(drv->ctx, MSG_DEBUG,
+ "nl80211: New scan results available");
+ if (drv->last_scan_cmd != NL80211_CMD_VENDOR)
+ drv->scan_state = SCAN_COMPLETED;
+ drv->scan_complete_events = 1;
+ if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) {
+ eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout,
+ drv, drv->ctx);
+ drv->last_scan_cmd = 0;
+ } else {
+ external_scan_event = 1;
+ }
+ send_scan_event(drv, 0, tb, external_scan_event);
+ break;
+ case NL80211_CMD_SCHED_SCAN_RESULTS:
+ wpa_dbg(drv->ctx, MSG_DEBUG,
+ "nl80211: New sched scan results available");
+ drv->scan_state = SCHED_SCAN_RESULTS;
+ send_scan_event(drv, 0, tb, 0);
+ break;
+ case NL80211_CMD_SCAN_ABORTED:
+ wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan aborted");
+ if (drv->last_scan_cmd != NL80211_CMD_VENDOR)
+ drv->scan_state = SCAN_ABORTED;
+ if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) {
+ /*
+ * 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);
+ drv->last_scan_cmd = 0;
+ } else {
+ external_scan_event = 1;
+ }
+ send_scan_event(drv, 1, tb, external_scan_event);
+ break;
+ case NL80211_CMD_AUTHENTICATE:
+ case NL80211_CMD_ASSOCIATE:
+ case NL80211_CMD_DEAUTHENTICATE:
+ case NL80211_CMD_DISASSOCIATE:
+ case NL80211_CMD_FRAME_TX_STATUS:
+ case NL80211_CMD_UNPROT_DEAUTHENTICATE:
+ case NL80211_CMD_UNPROT_DISASSOCIATE:
+ mlme_event(bss, 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],
+ tb[NL80211_ATTR_RX_SIGNAL_DBM],
+ tb[NL80211_ATTR_STA_WME],
+ tb[NL80211_ATTR_REQ_IE],
+ tb[NL80211_ATTR_MLO_LINK_ID]);
+ break;
+ case NL80211_CMD_CONNECT:
+ case NL80211_CMD_ROAM:
+ mlme_event_connect(drv, cmd, false,
+ tb[NL80211_ATTR_STATUS_CODE],
+ tb[NL80211_ATTR_MAC],
+ tb[NL80211_ATTR_REQ_IE],
+ tb[NL80211_ATTR_RESP_IE],
+ tb[NL80211_ATTR_TIMED_OUT],
+ tb[NL80211_ATTR_TIMEOUT_REASON],
+ NULL, NULL, NULL,
+ tb[NL80211_ATTR_FILS_KEK],
+ NULL,
+ tb[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM],
+ tb[NL80211_ATTR_PMK],
+ tb[NL80211_ATTR_PMKID],
+ tb[NL80211_ATTR_MLO_LINKS]);
+ break;
+ case NL80211_CMD_CH_SWITCH_STARTED_NOTIFY:
+ mlme_event_ch_switch(drv,
+ tb[NL80211_ATTR_IFINDEX],
+ tb[NL80211_ATTR_MLO_LINK_ID],
+ tb[NL80211_ATTR_WIPHY_FREQ],
+ tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE],
+ tb[NL80211_ATTR_CHANNEL_WIDTH],
+ tb[NL80211_ATTR_CENTER_FREQ1],
+ tb[NL80211_ATTR_CENTER_FREQ2],
+ tb[NL80211_ATTR_PUNCT_BITMAP],
+ tb[NL80211_ATTR_CH_SWITCH_COUNT],
+ 0);
+ break;
+ case NL80211_CMD_CH_SWITCH_NOTIFY:
+ mlme_event_ch_switch(drv,
+ tb[NL80211_ATTR_IFINDEX],
+ tb[NL80211_ATTR_MLO_LINK_ID],
+ tb[NL80211_ATTR_WIPHY_FREQ],
+ tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE],
+ tb[NL80211_ATTR_CHANNEL_WIDTH],
+ tb[NL80211_ATTR_CENTER_FREQ1],
+ tb[NL80211_ATTR_CENTER_FREQ2],
+ tb[NL80211_ATTR_PUNCT_BITMAP],
+ NULL,
+ 1);
+ break;
+ case NL80211_CMD_DISCONNECT:
+ mlme_event_disconnect(drv, tb[NL80211_ATTR_REASON_CODE],
+ tb[NL80211_ATTR_MAC],
+ tb[NL80211_ATTR_DISCONNECTED_BY_AP]);
+ break;
+ case NL80211_CMD_MICHAEL_MIC_FAILURE:
+ mlme_event_michael_mic_failure(bss, 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:
+ case NL80211_CMD_WIPHY_REG_CHANGE:
+ nl80211_reg_change_event(drv, tb);
+ break;
+ case NL80211_CMD_REG_BEACON_HINT:
+ nl80211_reg_beacon_hint_event(drv, tb);
+ break;
+ case NL80211_CMD_NEW_STATION:
+ nl80211_new_station_event(drv, bss, tb);
+ break;
+ case NL80211_CMD_DEL_STATION:
+ nl80211_del_station_event(drv, bss, tb);
+ break;
+ case NL80211_CMD_SET_REKEY_OFFLOAD:
+ nl80211_rekey_offload_event(drv, tb);
+ break;
+ case NL80211_CMD_PMKSA_CANDIDATE:
+ nl80211_pmksa_candidate_event(drv, tb);
+ break;
+ case NL80211_CMD_PROBE_CLIENT:
+ nl80211_client_probe_event(drv, tb);
+ break;
+ case NL80211_CMD_TDLS_OPER:
+ nl80211_tdls_oper_event(drv, tb);
+ break;
+ case NL80211_CMD_CONN_FAILED:
+ nl80211_connect_failed_event(drv, tb);
+ break;
+ case NL80211_CMD_FT_EVENT:
+ mlme_event_ft_event(drv, tb);
+ break;
+ case NL80211_CMD_RADAR_DETECT:
+ nl80211_radar_event(drv, tb);
+ break;
+ case NL80211_CMD_STOP_AP:
+ nl80211_stop_ap(drv, tb);
+ break;
+ case NL80211_CMD_VENDOR:
+ nl80211_vendor_event(drv, tb);
+ break;
+ case NL80211_CMD_NEW_PEER_CANDIDATE:
+ nl80211_new_peer_candidate(drv, tb);
+ break;
+ case NL80211_CMD_PORT_AUTHORIZED:
+ nl80211_port_authorized(drv, tb);
+ break;
+ case NL80211_CMD_STA_OPMODE_CHANGED:
+ nl80211_sta_opmode_change_event(drv, tb);
+ break;
+ case NL80211_CMD_UPDATE_OWE_INFO:
+ mlme_event_dh_event(drv, bss, tb);
+ break;
+ case NL80211_CMD_UNPROT_BEACON:
+ if (frame)
+ mlme_event_unprot_beacon(drv, nla_data(frame),
+ nla_len(frame));
+ break;
+ case NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS:
+ if (!frame)
+ break;
+ nl80211_control_port_frame_tx_status(drv,
+ nla_data(frame),
+ nla_len(frame),
+ tb[NL80211_ATTR_ACK],
+ tb[NL80211_ATTR_COOKIE]);
+ break;
+ case NL80211_CMD_FRAME_WAIT_CANCEL:
+ nl80211_frame_wait_cancel(drv, tb[NL80211_ATTR_COOKIE]);
+ break;
+ case NL80211_CMD_ASSOC_COMEBACK:
+ nl80211_assoc_comeback(drv, tb[NL80211_ATTR_MAC],
+ tb[NL80211_ATTR_TIMEOUT]);
+ break;
+#ifdef CONFIG_IEEE80211AX
+ case NL80211_CMD_OBSS_COLOR_COLLISION:
+ nl80211_obss_color_collision(drv, tb);
+ break;
+ case NL80211_CMD_COLOR_CHANGE_STARTED:
+ nl80211_color_change_announcement_started(drv);
+ break;
+ case NL80211_CMD_COLOR_CHANGE_ABORTED:
+ nl80211_color_change_announcement_aborted(drv);
+ break;
+ case NL80211_CMD_COLOR_CHANGE_COMPLETED:
+ nl80211_color_change_announcement_completed(drv);
+ break;
+#endif /* CONFIG_IEEE80211AX */
+ default:
+ wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Ignored unknown event "
+ "(cmd=%d)", cmd);
+ break;
+ }
+}
+
+
+int process_global_event(struct nl_msg *msg, void *arg)
+{
+ struct nl80211_global *global = arg;
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct wpa_driver_nl80211_data *drv, *tmp;
+ int ifidx = -1, wiphy_idx = -1, wiphy_idx_rx = -1;
+ struct i802_bss *bss;
+ u64 wdev_id = 0;
+ int wdev_id_set = 0;
+ int wiphy_idx_set = 0;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ if (tb[NL80211_ATTR_IFINDEX])
+ ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
+ else if (tb[NL80211_ATTR_WDEV]) {
+ wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]);
+ wdev_id_set = 1;
+ } else if (tb[NL80211_ATTR_WIPHY]) {
+ wiphy_idx_rx = nla_get_u32(tb[NL80211_ATTR_WIPHY]);
+ wiphy_idx_set = 1;
+ }
+
+ dl_list_for_each_safe(drv, tmp, &global->interfaces,
+ struct wpa_driver_nl80211_data, list) {
+ for (bss = drv->first_bss; bss; bss = bss->next) {
+ if (wiphy_idx_set)
+ wiphy_idx = nl80211_get_wiphy_index(bss);
+ if ((ifidx == -1 && !wiphy_idx_set && !wdev_id_set) ||
+ ifidx == bss->ifindex ||
+ (wiphy_idx_set && wiphy_idx == wiphy_idx_rx) ||
+ (wdev_id_set && bss->wdev_id_set &&
+ wdev_id == bss->wdev_id)) {
+ do_process_drv_event(bss, gnlh->cmd, tb);
+ return NL_SKIP;
+ }
+ }
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Ignored event %d (%s) for foreign interface (ifindex %d wdev 0x%llx)",
+ gnlh->cmd, nl80211_command_to_string(gnlh->cmd),
+ ifidx, (long long unsigned int) wdev_id);
+ }
+
+ return NL_SKIP;
+}
+
+
+int process_bss_event(struct nl_msg *msg, void *arg)
+{
+ struct i802_bss *bss = arg;
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ wpa_printf(MSG_DEBUG, "nl80211: BSS Event %d (%s) received for %s",
+ gnlh->cmd, nl80211_command_to_string(gnlh->cmd),
+ bss->ifname);
+
+ switch (gnlh->cmd) {
+ case NL80211_CMD_FRAME:
+ case NL80211_CMD_FRAME_TX_STATUS:
+ mlme_event(bss, 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],
+ tb[NL80211_ATTR_RX_SIGNAL_DBM],
+ tb[NL80211_ATTR_STA_WME], NULL,
+ tb[NL80211_ATTR_MLO_LINK_ID]);
+ break;
+ case NL80211_CMD_UNEXPECTED_FRAME:
+ nl80211_spurious_frame(bss, tb, 0);
+ break;
+ case NL80211_CMD_UNEXPECTED_4ADDR_FRAME:
+ nl80211_spurious_frame(bss, tb, 1);
+ break;
+ case NL80211_CMD_EXTERNAL_AUTH:
+ nl80211_external_auth(bss->drv, tb);
+ break;
+ case NL80211_CMD_CONTROL_PORT_FRAME:
+ nl80211_control_port_frame(bss->drv, tb);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event "
+ "(cmd=%d)", gnlh->cmd);
+ break;
+ }
+
+ return NL_SKIP;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_nl80211_monitor.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_nl80211_monitor.c
new file mode 100644
index 0000000..7ff55f1
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_nl80211_monitor.c
@@ -0,0 +1,503 @@
+/*
+ * Driver interaction with Linux nl80211/cfg80211 - AP monitor interface
+ * Copyright (c) 2002-2014, 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 software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <netpacket/packet.h>
+#include <linux/filter.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "linux_ioctl.h"
+#include "radiotap_iter.h"
+#include "driver_nl80211.h"
+
+
+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)
+{
+ struct ieee80211_hdr *hdr = (void *)buf;
+ u16 fc;
+ union wpa_event_data event;
+
+ if (len < sizeof(*hdr))
+ return;
+
+ fc = le_to_host16(hdr->frame_control);
+
+ os_memset(&event, 0, sizeof(event));
+ event.rx_from_unknown.bssid = get_hdr_bssid(hdr, len);
+ event.rx_from_unknown.addr = hdr->addr2;
+ event.rx_from_unknown.wds = (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) ==
+ (WLAN_FC_FROMDS | WLAN_FC_TODS);
+ 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;
+
+ if (!drv->use_monitor)
+ return;
+
+ 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) {
+ wpa_printf(MSG_ERROR, "nl80211: Monitor socket recv failed: %s",
+ strerror(errno));
+ return;
+ }
+
+ if (ieee80211_radiotap_iterator_init(&iter, (void *) buf, len, NULL)) {
+ wpa_printf(MSG_INFO, "nl80211: received invalid radiotap frame");
+ return;
+ }
+
+ while (1) {
+ ret = ieee80211_radiotap_iterator_next(&iter);
+ if (ret == -ENOENT)
+ break;
+ if (ret) {
+ wpa_printf(MSG_INFO, "nl80211: received invalid radiotap frame (%d)",
+ ret);
+ return;
+ }
+ switch (iter.this_arg_index) {
+ case IEEE80211_RADIOTAP_FLAGS:
+ if (*iter.this_arg & IEEE80211_RADIOTAP_F_FCS)
+ len -= 4;
+ break;
+ case IEEE80211_RADIOTAP_RX_FLAGS:
+ rxflags = 1;
+ break;
+ case IEEE80211_RADIOTAP_TX_FLAGS:
+ injected = 1;
+ failed = le_to_host16((*(le16 *) 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_DBM_ANTSIGNAL:
+ ssi_signal = (s8) *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 = ARRAY_SIZE(msock_filter_insns),
+ .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))) {
+ wpa_printf(MSG_ERROR, "nl80211: setsockopt(SO_ATTACH_FILTER) failed: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void nl80211_remove_monitor_interface(struct wpa_driver_nl80211_data *drv)
+{
+ if (drv->monitor_refcount > 0)
+ drv->monitor_refcount--;
+ wpa_printf(MSG_DEBUG, "nl80211: Remove monitor interface: refcount=%d",
+ drv->monitor_refcount);
+ if (drv->monitor_refcount > 0)
+ return;
+
+ 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;
+ }
+}
+
+
+int nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv)
+{
+ char buf[IFNAMSIZ];
+ struct sockaddr_ll ll;
+ int optval;
+ socklen_t optlen;
+
+ if (drv->monitor_ifidx >= 0) {
+ drv->monitor_refcount++;
+ wpa_printf(MSG_DEBUG, "nl80211: Re-use existing monitor interface: refcount=%d",
+ drv->monitor_refcount);
+ return 0;
+ }
+
+ if (os_strncmp(drv->first_bss->ifname, "p2p-", 4) == 0) {
+ /*
+ * P2P interface name is of the format p2p-%s-%d. For monitor
+ * interface name corresponding to P2P GO, replace "p2p-" with
+ * "mon-" to retain the same interface name length and to
+ * indicate that it is a monitor interface.
+ */
+ snprintf(buf, IFNAMSIZ, "mon-%s", drv->first_bss->ifname + 4);
+ } else {
+ int ret;
+
+ /* Non-P2P interface with AP functionality. */
+ ret = os_snprintf(buf, IFNAMSIZ, "mon.%s",
+ drv->first_bss->ifname);
+ if (ret >= (int) sizeof(buf))
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Monitor interface name has been truncated to %s",
+ buf);
+ else if (ret < 0)
+ return ret;
+ }
+
+ buf[IFNAMSIZ - 1] = '\0';
+
+ drv->monitor_ifidx =
+ nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL,
+ 0, NULL, NULL, 0);
+
+ if (drv->monitor_ifidx == -EOPNOTSUPP) {
+ /*
+ * This is backward compatibility for a few versions of
+ * the kernel only that didn't advertise the right
+ * attributes for the only driver that then supported
+ * AP mode w/o monitor -- ath6kl.
+ */
+ wpa_printf(MSG_DEBUG, "nl80211: Driver does not support "
+ "monitor interface type - try to run without it");
+ drv->device_ap_sme = 1;
+ }
+
+ if (drv->monitor_ifidx < 0)
+ return -1;
+
+ if (linux_set_iface_flags(drv->global->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) {
+ wpa_printf(MSG_ERROR, "nl80211: socket[PF_PACKET,SOCK_RAW] failed: %s",
+ strerror(errno));
+ 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) {
+ wpa_printf(MSG_ERROR, "nl80211: monitor socket bind failed: %s",
+ strerror(errno));
+ goto error;
+ }
+
+ optlen = sizeof(optval);
+ optval = 20;
+ if (setsockopt
+ (drv->monitor_sock, SOL_SOCKET, SO_PRIORITY, &optval, optlen)) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to set socket priority: %s",
+ strerror(errno));
+ goto error;
+ }
+
+ if (eloop_register_read_sock(drv->monitor_sock, handle_monitor_read,
+ drv, NULL)) {
+ wpa_printf(MSG_INFO, "nl80211: Could not register monitor read socket");
+ goto error;
+ }
+
+ drv->monitor_refcount++;
+ return 0;
+ error:
+ nl80211_remove_monitor_interface(drv);
+ return -1;
+}
+
+
+int nl80211_send_monitor(struct wpa_driver_nl80211_data *drv,
+ const void *data, size_t len,
+ int encrypt, int noack)
+{
+ __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;
+ u16 txflags = 0;
+
+ if (encrypt)
+ rtap_hdr[8] |= IEEE80211_RADIOTAP_F_WEP;
+
+ if (drv->monitor_sock < 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: No monitor socket available "
+ "for %s", __func__);
+ return -1;
+ }
+
+ if (noack)
+ txflags |= IEEE80211_RADIOTAP_F_TX_NOACK;
+ WPA_PUT_LE16(&rtap_hdr[12], txflags);
+
+ res = sendmsg(drv->monitor_sock, &msg, 0);
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "nl80211: sendmsg: %s", strerror(errno));
+ return -1;
+ }
+ return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_nl80211_scan.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_nl80211_scan.c
new file mode 100644
index 0000000..461d688
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_nl80211_scan.c
@@ -0,0 +1,1335 @@
+/*
+ * Driver interaction with Linux nl80211/cfg80211 - Scanning
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <time.h>
+#include <netlink/genl/genl.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/qca-vendor.h"
+#include "driver_nl80211.h"
+
+
+#define MAX_NL80211_NOISE_FREQS 100
+
+struct nl80211_noise_info {
+ u32 freq[MAX_NL80211_NOISE_FREQS];
+ s8 noise[MAX_NL80211_NOISE_FREQS];
+ unsigned int count;
+};
+
+static int get_noise_for_scan_results(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 nl80211_noise_info *info = arg;
+
+ if (info->count >= MAX_NL80211_NOISE_FREQS)
+ return NL_SKIP;
+
+ 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_NOISE])
+ return NL_SKIP;
+
+ if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY])
+ return NL_SKIP;
+
+ info->freq[info->count] =
+ nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]);
+ info->noise[info->count] =
+ (s8) nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]);
+ info->count++;
+
+ return NL_SKIP;
+}
+
+
+static int nl80211_get_noise_for_scan_results(
+ struct wpa_driver_nl80211_data *drv, struct nl80211_noise_info *info)
+{
+ struct nl_msg *msg;
+
+ os_memset(info, 0, sizeof(*info));
+ msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
+ return send_and_recv_msgs(drv, msg, get_noise_for_scan_results, info,
+ NULL, NULL);
+}
+
+
+static int nl80211_abort_scan(struct i802_bss *bss)
+{
+ int ret;
+ struct nl_msg *msg;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Abort scan");
+ msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_ABORT_SCAN);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Abort scan failed: ret=%d (%s)",
+ ret, strerror(-ret));
+ }
+ return ret;
+}
+
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+static int nl80211_abort_vendor_scan(struct wpa_driver_nl80211_data *drv,
+ u64 scan_cookie)
+{
+ struct nl_msg *msg;
+ struct nlattr *params;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Abort vendor scan with cookie 0x%llx",
+ (long long unsigned int) scan_cookie);
+
+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
+ if (!msg ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_ABORT_SCAN) ||
+ !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+ nla_put_u64(msg, QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE, scan_cookie))
+ goto fail;
+
+ nla_nest_end(msg, params);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_INFO,
+ "nl80211: Aborting vendor scan with cookie 0x%llx failed: ret=%d (%s)",
+ (long long unsigned int) scan_cookie, ret,
+ strerror(-ret));
+ goto fail;
+ }
+ return 0;
+fail:
+ nlmsg_free(msg);
+ return -1;
+}
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+
+/**
+ * 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.
+ */
+void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_driver_nl80211_data *drv = eloop_ctx;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Scan timeout - try to abort it");
+#ifdef CONFIG_DRIVER_NL80211_QCA
+ if (drv->vendor_scan_cookie &&
+ nl80211_abort_vendor_scan(drv, drv->vendor_scan_cookie) == 0)
+ return;
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+ if (!drv->vendor_scan_cookie &&
+ nl80211_abort_scan(drv->first_bss) == 0)
+ return;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to abort scan");
+
+ if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED)
+ nl80211_restore_ap_mode(drv->first_bss);
+
+ wpa_printf(MSG_DEBUG, "nl80211: Try to get scan results");
+ wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
+}
+
+
+static struct nl_msg *
+nl80211_scan_common(struct i802_bss *bss, u8 cmd,
+ struct wpa_driver_scan_params *params)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ size_t i;
+ u32 scan_flags = 0;
+
+ msg = nl80211_cmd_msg(bss, 0, cmd);
+ if (!msg)
+ return NULL;
+
+ if (params->num_ssids) {
+ struct nlattr *ssids;
+
+ ssids = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS);
+ if (ssids == NULL)
+ goto fail;
+ for (i = 0; i < params->num_ssids; i++) {
+ wpa_printf(MSG_MSGDUMP, "nl80211: Scan SSID %s",
+ wpa_ssid_txt(params->ssids[i].ssid,
+ params->ssids[i].ssid_len));
+ if (nla_put(msg, i + 1, params->ssids[i].ssid_len,
+ params->ssids[i].ssid))
+ goto fail;
+ }
+ nla_nest_end(msg, ssids);
+
+ /*
+ * If allowed, scan for 6 GHz APs that are reported by other
+ * APs. Note that if the flag is not set and 6 GHz channels are
+ * to be scanned, it is highly likely that non-PSC channels
+ * would be scanned passively (due to the Probe Request frame
+ * transmission restrictions mandated in IEEE Std 802.11ax-2021,
+ * 26.17.2.3 (Scanning in the 6 GHz band). Passive scanning of
+ * all non-PSC channels would take a significant amount of time.
+ */
+ if (!params->non_coloc_6ghz) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Scan co-located APs on 6 GHz");
+ scan_flags |= NL80211_SCAN_FLAG_COLOCATED_6GHZ;
+ }
+ } else {
+ wpa_printf(MSG_DEBUG, "nl80211: Passive scan requested");
+ }
+
+ if (params->extra_ies) {
+ wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan extra IEs",
+ params->extra_ies, params->extra_ies_len);
+ if (nla_put(msg, NL80211_ATTR_IE, params->extra_ies_len,
+ params->extra_ies))
+ goto fail;
+ }
+
+ if (params->freqs) {
+ struct nlattr *freqs;
+ freqs = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES);
+ if (freqs == NULL)
+ goto fail;
+ for (i = 0; params->freqs[i]; i++) {
+ wpa_printf(MSG_MSGDUMP, "nl80211: Scan frequency %u "
+ "MHz", params->freqs[i]);
+ if (nla_put_u32(msg, i + 1, params->freqs[i]))
+ goto fail;
+ }
+ nla_nest_end(msg, freqs);
+ }
+
+ os_free(drv->filter_ssids);
+ drv->filter_ssids = params->filter_ssids;
+ params->filter_ssids = NULL;
+ drv->num_filter_ssids = params->num_filter_ssids;
+
+ if (!drv->hostapd && is_ap_interface(drv->nlmode)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Add NL80211_SCAN_FLAG_AP");
+ scan_flags |= NL80211_SCAN_FLAG_AP;
+ }
+
+ if (params->only_new_results) {
+ wpa_printf(MSG_DEBUG, "nl80211: Add NL80211_SCAN_FLAG_FLUSH");
+ scan_flags |= NL80211_SCAN_FLAG_FLUSH;
+ }
+
+ if (params->low_priority && drv->have_low_prio_scan) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Add NL80211_SCAN_FLAG_LOW_PRIORITY");
+ scan_flags |= NL80211_SCAN_FLAG_LOW_PRIORITY;
+ }
+
+ if (params->mac_addr_rand) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Add NL80211_SCAN_FLAG_RANDOM_ADDR");
+ scan_flags |= NL80211_SCAN_FLAG_RANDOM_ADDR;
+
+ if (params->mac_addr) {
+ wpa_printf(MSG_DEBUG, "nl80211: MAC address: " MACSTR,
+ MAC2STR(params->mac_addr));
+ if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN,
+ params->mac_addr))
+ goto fail;
+ }
+
+ if (params->mac_addr_mask) {
+ wpa_printf(MSG_DEBUG, "nl80211: MAC address mask: "
+ MACSTR, MAC2STR(params->mac_addr_mask));
+ if (nla_put(msg, NL80211_ATTR_MAC_MASK, ETH_ALEN,
+ params->mac_addr_mask))
+ goto fail;
+ }
+ }
+
+ if (params->duration) {
+ if (!(drv->capa.rrm_flags &
+ WPA_DRIVER_FLAGS_SUPPORT_SET_SCAN_DWELL) ||
+ nla_put_u16(msg, NL80211_ATTR_MEASUREMENT_DURATION,
+ params->duration))
+ goto fail;
+
+ if (params->duration_mandatory &&
+ nla_put_flag(msg,
+ NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY))
+ goto fail;
+ }
+
+ if (params->oce_scan) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Add NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME");
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Add NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP");
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Add NL80211_SCAN_FLAG_OCE_PROBE_REQ_MIN_TX_RATE");
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Add NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION");
+ scan_flags |= NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME |
+ NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP |
+ NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE |
+ NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION;
+ }
+
+ if (scan_flags &&
+ nla_put_u32(msg, NL80211_ATTR_SCAN_FLAGS, scan_flags))
+ goto fail;
+
+ return msg;
+
+fail:
+ nlmsg_free(msg);
+ return NULL;
+}
+
+
+/**
+ * wpa_driver_nl80211_scan - Request the driver to initiate scan
+ * @bss: Pointer to private driver data from wpa_driver_nl80211_init()
+ * @params: Scan parameters
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_driver_nl80211_scan(struct i802_bss *bss,
+ struct wpa_driver_scan_params *params)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int ret = -1, timeout;
+ struct nl_msg *msg = NULL;
+
+ wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: scan request");
+ drv->scan_for_auth = 0;
+
+ if (TEST_FAIL())
+ return -1;
+
+ msg = nl80211_scan_common(bss, NL80211_CMD_TRIGGER_SCAN, params);
+ if (!msg)
+ return -1;
+
+ if (params->p2p_probe) {
+ struct nlattr *rates;
+
+ wpa_printf(MSG_DEBUG, "nl80211: P2P probe - mask SuppRates");
+
+ rates = nla_nest_start(msg, NL80211_ATTR_SCAN_SUPP_RATES);
+ if (rates == NULL)
+ goto fail;
+
+ /*
+ * Remove 2.4 GHz rates 1, 2, 5.5, 11 Mbps from supported rates
+ * by masking out everything else apart from the OFDM rates 6,
+ * 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS rates. All 5 GHz
+ * rates are left enabled.
+ */
+ if (nla_put(msg, NL80211_BAND_2GHZ, 8,
+ "\x0c\x12\x18\x24\x30\x48\x60\x6c"))
+ goto fail;
+ nla_nest_end(msg, rates);
+
+ if (nla_put_flag(msg, NL80211_ATTR_TX_NO_CCK_RATE))
+ goto fail;
+ }
+
+ if (params->bssid) {
+ wpa_printf(MSG_DEBUG, "nl80211: Scan for a specific BSSID: "
+ MACSTR, MAC2STR(params->bssid));
+ if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid))
+ goto fail;
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Scan trigger failed: ret=%d "
+ "(%s)", ret, strerror(-ret));
+ if (drv->hostapd && is_ap_interface(drv->nlmode)) {
+ enum nl80211_iftype old_mode = drv->nlmode;
+
+ /*
+ * mac80211 does not allow scan requests in AP mode, so
+ * try to do this in station mode.
+ */
+ if (wpa_driver_nl80211_set_mode(
+ bss, NL80211_IFTYPE_STATION))
+ goto fail;
+
+ if (wpa_driver_nl80211_scan(bss, params)) {
+ wpa_driver_nl80211_set_mode(bss, old_mode);
+ goto fail;
+ }
+
+ /* Restore AP mode when processing scan results */
+ drv->ap_scan_as_station = old_mode;
+ ret = 0;
+ } else
+ goto fail;
+ }
+
+ drv->scan_state = SCAN_REQUESTED;
+ /* Not all drivers generate "scan completed" wireless event, so try to
+ * read results after a timeout. */
+ timeout = drv->uses_6ghz ? 15 : 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);
+ drv->last_scan_cmd = NL80211_CMD_TRIGGER_SCAN;
+
+fail:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+static int
+nl80211_sched_scan_add_scan_plans(struct wpa_driver_nl80211_data *drv,
+ struct nl_msg *msg,
+ struct wpa_driver_scan_params *params)
+{
+ struct nlattr *plans;
+ struct sched_scan_plan *scan_plans = params->sched_scan_plans;
+ unsigned int i;
+
+ plans = nla_nest_start(msg, NL80211_ATTR_SCHED_SCAN_PLANS);
+ if (!plans)
+ return -1;
+
+ for (i = 0; i < params->sched_scan_plans_num; i++) {
+ struct nlattr *plan = nla_nest_start(msg, i + 1);
+
+ if (!plan)
+ return -1;
+
+ if (!scan_plans[i].interval ||
+ scan_plans[i].interval >
+ drv->capa.max_sched_scan_plan_interval) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: sched scan plan no. %u: Invalid interval: %u",
+ i, scan_plans[i].interval);
+ return -1;
+ }
+
+ if (nla_put_u32(msg, NL80211_SCHED_SCAN_PLAN_INTERVAL,
+ scan_plans[i].interval))
+ return -1;
+
+ if (scan_plans[i].iterations >
+ drv->capa.max_sched_scan_plan_iterations) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: sched scan plan no. %u: Invalid number of iterations: %u",
+ i, scan_plans[i].iterations);
+ return -1;
+ }
+
+ if (scan_plans[i].iterations &&
+ nla_put_u32(msg, NL80211_SCHED_SCAN_PLAN_ITERATIONS,
+ scan_plans[i].iterations))
+ return -1;
+
+ nla_nest_end(msg, plan);
+
+ /*
+ * All the scan plans must specify the number of iterations
+ * except the last plan, which will run infinitely. So if the
+ * number of iterations is not specified, this ought to be the
+ * last scan plan.
+ */
+ if (!scan_plans[i].iterations)
+ break;
+ }
+
+ if (i != params->sched_scan_plans_num - 1) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: All sched scan plans but the last must specify number of iterations");
+ return -1;
+ }
+
+ nla_nest_end(msg, plans);
+ return 0;
+}
+
+
+/**
+ * wpa_driver_nl80211_sched_scan - Initiate a scheduled scan
+ * @priv: Pointer to private driver data from wpa_driver_nl80211_init()
+ * @params: Scan parameters
+ * Returns: 0 on success, -1 on failure or if not supported
+ */
+int wpa_driver_nl80211_sched_scan(void *priv,
+ struct wpa_driver_scan_params *params)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int ret = -1;
+ struct nl_msg *msg;
+ size_t i;
+
+ wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: sched_scan request");
+
+#ifdef ANDROID
+ if (!drv->capa.sched_scan_supported)
+ return android_pno_start(bss, params);
+#endif /* ANDROID */
+
+ if (!params->sched_scan_plans_num ||
+ params->sched_scan_plans_num > drv->capa.max_sched_scan_plans) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: Invalid number of sched scan plans: %u",
+ params->sched_scan_plans_num);
+ return -1;
+ }
+
+ msg = nl80211_scan_common(bss, NL80211_CMD_START_SCHED_SCAN, params);
+ if (!msg)
+ goto fail;
+
+ if (drv->capa.max_sched_scan_plan_iterations) {
+ if (nl80211_sched_scan_add_scan_plans(drv, msg, params))
+ goto fail;
+ } else {
+ if (nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL,
+ params->sched_scan_plans[0].interval * 1000))
+ goto fail;
+ }
+
+ if ((drv->num_filter_ssids &&
+ (int) drv->num_filter_ssids <= drv->capa.max_match_sets) ||
+ params->filter_rssi) {
+ struct nlattr *match_sets;
+ match_sets = nla_nest_start(msg, NL80211_ATTR_SCHED_SCAN_MATCH);
+ if (match_sets == NULL)
+ goto fail;
+
+ for (i = 0; i < drv->num_filter_ssids; i++) {
+ struct nlattr *match_set_ssid;
+ wpa_printf(MSG_MSGDUMP,
+ "nl80211: Sched scan filter SSID %s",
+ wpa_ssid_txt(drv->filter_ssids[i].ssid,
+ drv->filter_ssids[i].ssid_len));
+
+ match_set_ssid = nla_nest_start(msg, i + 1);
+ if (match_set_ssid == NULL ||
+ nla_put(msg, NL80211_ATTR_SCHED_SCAN_MATCH_SSID,
+ drv->filter_ssids[i].ssid_len,
+ drv->filter_ssids[i].ssid) ||
+ (params->filter_rssi &&
+ nla_put_u32(msg,
+ NL80211_SCHED_SCAN_MATCH_ATTR_RSSI,
+ params->filter_rssi)))
+ goto fail;
+
+ nla_nest_end(msg, match_set_ssid);
+ }
+
+ /*
+ * Due to backward compatibility code, newer kernels treat this
+ * matchset (with only an RSSI filter) as the default for all
+ * other matchsets, unless it's the only one, in which case the
+ * matchset will actually allow all SSIDs above the RSSI.
+ */
+ if (params->filter_rssi) {
+ struct nlattr *match_set_rssi;
+ match_set_rssi = nla_nest_start(msg, 0);
+ if (match_set_rssi == NULL ||
+ nla_put_u32(msg, NL80211_SCHED_SCAN_MATCH_ATTR_RSSI,
+ params->filter_rssi))
+ goto fail;
+ wpa_printf(MSG_MSGDUMP,
+ "nl80211: Sched scan RSSI filter %d dBm",
+ params->filter_rssi);
+ nla_nest_end(msg, match_set_rssi);
+ }
+
+ nla_nest_end(msg, match_sets);
+ }
+
+ if (params->relative_rssi_set) {
+ struct nl80211_bss_select_rssi_adjust rssi_adjust;
+
+ os_memset(&rssi_adjust, 0, sizeof(rssi_adjust));
+ wpa_printf(MSG_DEBUG, "nl80211: Relative RSSI: %d",
+ params->relative_rssi);
+ if (nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI,
+ params->relative_rssi))
+ goto fail;
+
+ if (params->relative_adjust_rssi) {
+ int pref_band_set = 1;
+
+ switch (params->relative_adjust_band) {
+ case WPA_SETBAND_5G:
+ rssi_adjust.band = NL80211_BAND_5GHZ;
+ break;
+ case WPA_SETBAND_2G:
+ rssi_adjust.band = NL80211_BAND_2GHZ;
+ break;
+ default:
+ pref_band_set = 0;
+ break;
+ }
+ rssi_adjust.delta = params->relative_adjust_rssi;
+
+ if (pref_band_set &&
+ nla_put(msg, NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST,
+ sizeof(rssi_adjust), &rssi_adjust))
+ goto fail;
+ }
+ }
+
+ if (params->sched_scan_start_delay &&
+ nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_DELAY,
+ params->sched_scan_start_delay))
+ goto fail;
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+
+ /* TODO: if we get an error here, we should fall back to normal scan */
+
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Sched scan start failed: "
+ "ret=%d (%s)", ret, strerror(-ret));
+ goto fail;
+ }
+
+ wpa_printf(MSG_DEBUG, "nl80211: Sched scan requested (ret=%d)", ret);
+
+fail:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+/**
+ * wpa_driver_nl80211_stop_sched_scan - Stop a scheduled scan
+ * @priv: Pointer to private driver data from wpa_driver_nl80211_init()
+ * Returns: 0 on success, -1 on failure or if not supported
+ */
+int wpa_driver_nl80211_stop_sched_scan(void *priv)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int ret;
+ struct nl_msg *msg;
+
+#ifdef ANDROID
+ if (!drv->capa.sched_scan_supported)
+ return android_pno_stop(bss);
+#endif /* ANDROID */
+
+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_STOP_SCHED_SCAN);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Sched scan stop failed: ret=%d (%s)",
+ ret, strerror(-ret));
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Sched scan stop sent");
+ }
+
+ return ret;
+}
+
+
+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 = 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;
+}
+
+
+static struct wpa_scan_res *
+nl80211_parse_bss_info(struct wpa_driver_nl80211_data *drv,
+ struct nl_msg *msg)
+{
+ 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 },
+ [NL80211_BSS_BEACON_TSF] = { .type = NLA_U64 },
+ [NL80211_BSS_PARENT_TSF] = { .type = NLA_U64 },
+ [NL80211_BSS_PARENT_BSSID] = { .type = NLA_UNSPEC },
+ [NL80211_BSS_LAST_SEEN_BOOTTIME] = { .type = NLA_U64 },
+ };
+ 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 NULL;
+ if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS],
+ bss_policy))
+ return NULL;
+ 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(drv, ie ? ie : beacon_ie,
+ ie ? ie_len : beacon_ie_len))
+ return NULL;
+
+ r = os_zalloc(sizeof(*r) + ie_len + beacon_ie_len);
+ if (r == NULL)
+ return NULL;
+ 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_QUAL_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_BEACON_TSF]) {
+ u64 tsf = nla_get_u64(bss[NL80211_BSS_BEACON_TSF]);
+ if (tsf > r->tsf) {
+ r->tsf = tsf;
+ r->beacon_newer = true;
+ }
+ }
+ if (bss[NL80211_BSS_SEEN_MS_AGO])
+ r->age = nla_get_u32(bss[NL80211_BSS_SEEN_MS_AGO]);
+ if (bss[NL80211_BSS_LAST_SEEN_BOOTTIME]) {
+ u64 boottime;
+ struct timespec ts;
+
+#ifndef CLOCK_BOOTTIME
+#define CLOCK_BOOTTIME 7
+#endif
+ if (clock_gettime(CLOCK_BOOTTIME, &ts) == 0) {
+ /* Use more accurate boottime information to update the
+ * scan result age since the driver reports this and
+ * CLOCK_BOOTTIME is available. */
+ boottime = nla_get_u64(
+ bss[NL80211_BSS_LAST_SEEN_BOOTTIME]);
+ r->age = ((u64) ts.tv_sec * 1000000000 +
+ ts.tv_nsec - boottime) / 1000000;
+ }
+ }
+ 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_ASSOCIATED:
+ r->flags |= WPA_SCAN_ASSOCIATED;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (bss[NL80211_BSS_PARENT_TSF] && bss[NL80211_BSS_PARENT_BSSID]) {
+ r->parent_tsf = nla_get_u64(bss[NL80211_BSS_PARENT_TSF]);
+ os_memcpy(r->tsf_bssid, nla_data(bss[NL80211_BSS_PARENT_BSSID]),
+ ETH_ALEN);
+ }
+
+ return r;
+}
+
+
+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 nl80211_bss_info_arg *_arg = arg;
+ struct wpa_scan_results *res = _arg->res;
+ struct wpa_scan_res **tmp;
+ struct wpa_scan_res *r;
+
+ r = nl80211_parse_bss_info(_arg->drv, msg);
+ if (!r)
+ return NL_SKIP;
+
+ if (!res) {
+ os_free(r);
+ return NL_SKIP;
+ }
+ tmp = os_realloc_array(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,
+ drv->first_bss);
+ }
+}
+
+
+static void nl80211_check_bss_status(struct wpa_driver_nl80211_data *drv,
+ struct wpa_scan_res *r)
+{
+ if (!(r->flags & WPA_SCAN_ASSOCIATED))
+ return;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Scan results indicate BSS status with "
+ MACSTR " as associated", MAC2STR(r->bssid));
+ if (is_sta_interface(drv->nlmode) && !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 (is_sta_interface(drv->nlmode) &&
+ 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));
+
+ if (os_memcmp(drv->sta_mlo_info.ap_mld_addr, drv->bssid,
+ ETH_ALEN) != 0) {
+ clear_state_mismatch(drv, r->bssid);
+
+ if (!is_zero_ether_addr(drv->sta_mlo_info.ap_mld_addr))
+ clear_state_mismatch(
+ drv, drv->sta_mlo_info.ap_mld_addr);
+ else
+ clear_state_mismatch(drv, drv->bssid);
+
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: BSSID is the MLD address");
+ }
+ }
+}
+
+
+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++)
+ nl80211_check_bss_status(drv, res->res[i]);
+}
+
+
+static void nl80211_update_scan_res_noise(struct wpa_scan_res *res,
+ struct nl80211_noise_info *info)
+{
+ unsigned int i;
+
+ for (i = 0; res && i < info->count; i++) {
+ if ((int) info->freq[i] != res->freq ||
+ !(res->flags & WPA_SCAN_NOISE_INVALID))
+ continue;
+ res->noise = info->noise[i];
+ res->flags &= ~WPA_SCAN_NOISE_INVALID;
+ }
+}
+
+
+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;
+ int count = 0;
+
+try_again:
+ res = os_zalloc(sizeof(*res));
+ if (res == NULL)
+ return NULL;
+ if (!(msg = nl80211_cmd_msg(drv->first_bss, NLM_F_DUMP,
+ NL80211_CMD_GET_SCAN))) {
+ wpa_scan_results_free(res);
+ return NULL;
+ }
+
+ arg.drv = drv;
+ arg.res = res;
+ ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg, NULL, NULL);
+ if (ret == -EAGAIN) {
+ count++;
+ if (count >= 10) {
+ wpa_printf(MSG_INFO,
+ "nl80211: Failed to receive consistent scan result dump");
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Failed to receive consistent scan result dump - try again");
+ wpa_scan_results_free(res);
+ goto try_again;
+ }
+ }
+ if (ret == 0) {
+ struct nl80211_noise_info info;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Received scan results (%lu "
+ "BSSes)", (unsigned long) res->num);
+ if (nl80211_get_noise_for_scan_results(drv, &info) == 0) {
+ size_t i;
+
+ for (i = 0; i < res->num; ++i)
+ nl80211_update_scan_res_noise(res->res[i],
+ &info);
+ }
+ return res;
+ }
+ wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d "
+ "(%s)", ret, strerror(-ret));
+ 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
+ */
+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;
+}
+
+
+struct nl80211_dump_scan_ctx {
+ struct wpa_driver_nl80211_data *drv;
+ int idx;
+};
+
+static int nl80211_dump_scan_handler(struct nl_msg *msg, void *arg)
+{
+ struct nl80211_dump_scan_ctx *ctx = arg;
+ struct wpa_scan_res *r;
+
+ r = nl80211_parse_bss_info(ctx->drv, msg);
+ if (!r)
+ return NL_SKIP;
+ wpa_printf(MSG_DEBUG, "nl80211: %d " MACSTR " %d%s",
+ ctx->idx, MAC2STR(r->bssid), r->freq,
+ r->flags & WPA_SCAN_ASSOCIATED ? " [assoc]" : "");
+ ctx->idx++;
+ os_free(r);
+ return NL_SKIP;
+}
+
+
+void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv)
+{
+ struct nl_msg *msg;
+ struct nl80211_dump_scan_ctx ctx;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Scan result dump");
+ ctx.drv = drv;
+ ctx.idx = 0;
+ msg = nl80211_cmd_msg(drv->first_bss, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
+ if (msg)
+ send_and_recv_msgs(drv, msg, nl80211_dump_scan_handler, &ctx,
+ NULL, NULL);
+}
+
+
+int wpa_driver_nl80211_abort_scan(void *priv, u64 scan_cookie)
+{
+ struct i802_bss *bss = priv;
+#ifdef CONFIG_DRIVER_NL80211_QCA
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ /*
+ * If scan_cookie is zero, a normal scan through kernel (cfg80211)
+ * was triggered, hence abort the cfg80211 scan instead of the vendor
+ * scan.
+ */
+ if (drv->scan_vendor_cmd_avail && scan_cookie)
+ return nl80211_abort_vendor_scan(drv, scan_cookie);
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+ return nl80211_abort_scan(bss);
+}
+
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+
+static int scan_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_VENDOR_DATA]) {
+ struct nlattr *nl_vendor = tb[NL80211_ATTR_VENDOR_DATA];
+ struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1];
+
+ nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_SCAN_MAX,
+ nla_data(nl_vendor), nla_len(nl_vendor), NULL);
+
+ if (tb_vendor[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE])
+ *cookie = nla_get_u64(
+ tb_vendor[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]);
+ }
+
+ return NL_SKIP;
+}
+
+
+/**
+ * wpa_driver_nl80211_vendor_scan - Request the driver to initiate a vendor scan
+ * @bss: Pointer to private driver data from wpa_driver_nl80211_init()
+ * @params: Scan parameters
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss,
+ struct wpa_driver_scan_params *params)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg = NULL;
+ struct nlattr *attr;
+ size_t i;
+ u32 scan_flags = 0;
+ int ret = -1;
+ u64 cookie = 0;
+
+ wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: vendor scan request");
+ drv->scan_for_auth = 0;
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN) )
+ goto fail;
+
+ attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+ if (attr == NULL)
+ goto fail;
+
+ if (params->num_ssids) {
+ struct nlattr *ssids;
+
+ ssids = nla_nest_start(msg, QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS);
+ if (ssids == NULL)
+ goto fail;
+ for (i = 0; i < params->num_ssids; i++) {
+ wpa_printf(MSG_MSGDUMP, "nl80211: Scan SSID %s",
+ wpa_ssid_txt(params->ssids[i].ssid,
+ params->ssids[i].ssid_len));
+ if (nla_put(msg, i + 1, params->ssids[i].ssid_len,
+ params->ssids[i].ssid))
+ goto fail;
+ }
+ nla_nest_end(msg, ssids);
+ }
+
+ if (params->extra_ies) {
+ wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan extra IEs",
+ params->extra_ies, params->extra_ies_len);
+ if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_SCAN_IE,
+ params->extra_ies_len, params->extra_ies))
+ goto fail;
+ }
+
+ if (params->freqs) {
+ struct nlattr *freqs;
+
+ freqs = nla_nest_start(msg,
+ QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES);
+ if (freqs == NULL)
+ goto fail;
+ for (i = 0; params->freqs[i]; i++) {
+ wpa_printf(MSG_MSGDUMP,
+ "nl80211: Scan frequency %u MHz",
+ params->freqs[i]);
+ if (nla_put_u32(msg, i + 1, params->freqs[i]))
+ goto fail;
+ }
+ nla_nest_end(msg, freqs);
+ }
+
+ os_free(drv->filter_ssids);
+ drv->filter_ssids = params->filter_ssids;
+ params->filter_ssids = NULL;
+ drv->num_filter_ssids = params->num_filter_ssids;
+
+ if (params->low_priority && drv->have_low_prio_scan) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Add NL80211_SCAN_FLAG_LOW_PRIORITY");
+ scan_flags |= NL80211_SCAN_FLAG_LOW_PRIORITY;
+ }
+
+ if (params->mac_addr_rand) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Add NL80211_SCAN_FLAG_RANDOM_ADDR");
+ scan_flags |= NL80211_SCAN_FLAG_RANDOM_ADDR;
+
+ if (params->mac_addr) {
+ wpa_printf(MSG_DEBUG, "nl80211: MAC address: " MACSTR,
+ MAC2STR(params->mac_addr));
+ if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_SCAN_MAC,
+ ETH_ALEN, params->mac_addr))
+ goto fail;
+ }
+
+ if (params->mac_addr_mask) {
+ wpa_printf(MSG_DEBUG, "nl80211: MAC address mask: "
+ MACSTR, MAC2STR(params->mac_addr_mask));
+ if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK,
+ ETH_ALEN, params->mac_addr_mask))
+ goto fail;
+ }
+ }
+
+ if (scan_flags &&
+ nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS, scan_flags))
+ goto fail;
+
+ if (params->p2p_probe) {
+ struct nlattr *rates;
+
+ wpa_printf(MSG_DEBUG, "nl80211: P2P probe - mask SuppRates");
+
+ rates = nla_nest_start(msg,
+ QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES);
+ if (rates == NULL)
+ goto fail;
+
+ /*
+ * Remove 2.4 GHz rates 1, 2, 5.5, 11 Mbps from supported rates
+ * by masking out everything else apart from the OFDM rates 6,
+ * 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS rates. All 5 GHz
+ * rates are left enabled.
+ */
+ if (nla_put(msg, NL80211_BAND_2GHZ, 8,
+ "\x0c\x12\x18\x24\x30\x48\x60\x6c"))
+ goto fail;
+ nla_nest_end(msg, rates);
+
+ if (nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE))
+ goto fail;
+ }
+
+ if (params->bssid) {
+ wpa_printf(MSG_DEBUG, "nl80211: Scan for a specific BSSID: "
+ MACSTR, MAC2STR(params->bssid));
+ if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_SCAN_BSSID, ETH_ALEN,
+ params->bssid))
+ goto fail;
+ }
+
+ nla_nest_end(msg, attr);
+
+ ret = send_and_recv_msgs(drv, msg, scan_cookie_handler, &cookie,
+ NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Vendor scan trigger failed: ret=%d (%s)",
+ ret, strerror(-ret));
+ goto fail;
+ }
+
+ drv->vendor_scan_cookie = cookie;
+ drv->scan_state = SCAN_REQUESTED;
+ /* Pass the cookie to the caller to help distinguish the scans. */
+ params->scan_cookie = cookie;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Vendor scan requested (ret=%d) - scan timeout 30 seconds, scan cookie:0x%llx",
+ ret, (long long unsigned int) cookie);
+ eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
+ eloop_register_timeout(30, 0, wpa_driver_nl80211_scan_timeout,
+ drv, drv->ctx);
+ drv->last_scan_cmd = NL80211_CMD_VENDOR;
+
+fail:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+/**
+ * nl80211_set_default_scan_ies - Set the scan default IEs to the driver
+ * @priv: Pointer to private driver data from wpa_driver_nl80211_init()
+ * @ies: Pointer to IEs buffer
+ * @ies_len: Length of IEs in bytes
+ * Returns: 0 on success, -1 on failure
+ */
+int nl80211_set_default_scan_ies(void *priv, const u8 *ies, size_t ies_len)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg = NULL;
+ struct nlattr *attr;
+ int ret = -1;
+
+ if (!drv->set_wifi_conf_vendor_cmd_avail)
+ return -1;
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION))
+ goto fail;
+
+ attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+ if (attr == NULL)
+ goto fail;
+
+ wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan default IEs", ies, ies_len);
+ if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_CONFIG_SCAN_DEFAULT_IES,
+ ies_len, ies))
+ goto fail;
+
+ nla_nest_end(msg, attr);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: Set scan default IEs failed: ret=%d (%s)",
+ ret, strerror(-ret));
+ goto fail;
+ }
+
+fail:
+ nlmsg_free(msg);
+ return ret;
+}
+
+#endif /* CONFIG_DRIVER_NL80211_QCA */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_none.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_none.c
new file mode 100644
index 0000000..ccd2d9d
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_none.c
@@ -0,0 +1,77 @@
+/*
+ * Driver interface for RADIUS server or WPS ER only (no driver)
+ * Copyright (c) 2008, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README 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 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);
+}
+
+
+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,
+ .init = none_driver_init,
+ .deinit = none_driver_deinit,
+};
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_openbsd.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_openbsd.c
new file mode 100644
index 0000000..bfc2311
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_openbsd.c
@@ -0,0 +1,139 @@
+/*
+ * Driver interaction with OpenBSD net80211 layer
+ * Copyright (c) 2013, Mark Kettenis
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+
+#include <net/if.h>
+#include <net80211/ieee80211.h>
+#include <net80211/ieee80211_crypto.h>
+#include <net80211/ieee80211_ioctl.h>
+
+#include "common.h"
+#include "driver.h"
+
+struct openbsd_driver_data {
+ char ifname[IFNAMSIZ + 1];
+ void *ctx;
+
+ int sock; /* open socket for 802.11 ioctls */
+};
+
+
+static int
+wpa_driver_openbsd_get_ssid(void *priv, u8 *ssid)
+{
+ struct openbsd_driver_data *drv = priv;
+ 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;
+}
+
+static int
+wpa_driver_openbsd_get_bssid(void *priv, u8 *bssid)
+{
+ struct openbsd_driver_data *drv = priv;
+ struct ieee80211_bssid id;
+
+ os_strlcpy(id.i_name, drv->ifname, sizeof(id.i_name));
+ if (ioctl(drv->sock, SIOCG80211BSSID, &id) < 0)
+ return -1;
+
+ os_memcpy(bssid, id.i_bssid, IEEE80211_ADDR_LEN);
+ return 0;
+}
+
+
+static int
+wpa_driver_openbsd_get_capa(void *priv, struct wpa_driver_capa *capa)
+{
+ os_memset(capa, 0, sizeof(*capa));
+ capa->flags = WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK |
+ WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X;
+ return 0;
+}
+
+
+static int
+wpa_driver_openbsd_set_key(void *priv, struct wpa_driver_set_key_params *params)
+{
+ struct openbsd_driver_data *drv = priv;
+ struct ieee80211_keyavail keyavail;
+ enum key_flag key_flag = params->key_flag;
+ const u8 *key = params->key;
+ size_t key_len = params->key_len;
+
+ if (key_len > IEEE80211_PMK_LEN ||
+ (key_flag & KEY_FLAG_PMK_MASK) != KEY_FLAG_PMK) {
+ return -1;
+
+ memset(&keyavail, 0, sizeof(keyavail));
+ os_strlcpy(keyavail.i_name, drv->ifname, sizeof(keyavail.i_name));
+ if (wpa_driver_openbsd_get_bssid(priv, keyavail.i_macaddr) < 0)
+ return -1;
+ memcpy(keyavail.i_key, key, key_len);
+
+ if (ioctl(drv->sock, SIOCS80211KEYAVAIL, &keyavail) < 0)
+ return -1;
+
+ return 0;
+}
+
+static void *
+wpa_driver_openbsd_init(void *ctx, const char *ifname)
+{
+ struct openbsd_driver_data *drv;
+
+ drv = os_zalloc(sizeof(*drv));
+ if (drv == NULL)
+ return NULL;
+
+ drv->sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (drv->sock < 0)
+ goto fail;
+
+ drv->ctx = ctx;
+ os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+
+ return drv;
+
+fail:
+ os_free(drv);
+ return NULL;
+}
+
+
+static void
+wpa_driver_openbsd_deinit(void *priv)
+{
+ struct openbsd_driver_data *drv = priv;
+
+ close(drv->sock);
+ os_free(drv);
+}
+
+
+const struct wpa_driver_ops wpa_driver_openbsd_ops = {
+ .name = "openbsd",
+ .desc = "OpenBSD 802.11 support",
+ .get_ssid = wpa_driver_openbsd_get_ssid,
+ .get_bssid = wpa_driver_openbsd_get_bssid,
+ .get_capa = wpa_driver_openbsd_get_capa,
+ .set_key = wpa_driver_openbsd_set_key,
+ .init = wpa_driver_openbsd_init,
+ .deinit = wpa_driver_openbsd_deinit,
+};
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_privsep.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_privsep.c
new file mode 100644
index 0000000..d6735b4
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_privsep.c
@@ -0,0 +1,847 @@
+/*
+ * WPA Supplicant - privilege separated driver interface
+ * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README 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)
+ wpa_printf(MSG_ERROR, "sendto: %s", strerror(errno));
+ 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) {
+ wpa_printf(MSG_ERROR, "sendmsg(cmd_socket): %s",
+ strerror(errno));
+ 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) {
+ wpa_printf(MSG_ERROR, "select: %s", strerror(errno));
+ return -1;
+ }
+
+ if (FD_ISSET(drv->cmd_socket, &rfds)) {
+ res = recv(drv->cmd_socket, reply, *reply_len, 0);
+ if (res < 0) {
+ wpa_printf(MSG_ERROR, "recv: %s",
+ strerror(errno));
+ 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;
+ struct privsep_cmd_scan scan;
+ size_t i;
+
+ wpa_printf(MSG_DEBUG, "%s: priv=%p", __func__, priv);
+ os_memset(&scan, 0, sizeof(scan));
+ scan.num_ssids = params->num_ssids;
+ for (i = 0; i < params->num_ssids; i++) {
+ if (!params->ssids[i].ssid)
+ continue;
+ scan.ssid_lens[i] = params->ssids[i].ssid_len;
+ os_memcpy(scan.ssids[i], params->ssids[i].ssid,
+ scan.ssid_lens[i]);
+ }
+
+ for (i = 0; i < PRIVSEP_MAX_SCAN_FREQS &&
+ params->freqs && params->freqs[i]; i++)
+ scan.freqs[i] = params->freqs[i];
+ scan.num_freqs = i;
+
+ return wpa_priv_cmd(drv, PRIVSEP_CMD_SCAN, &scan, sizeof(scan),
+ 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_calloc(num, sizeof(struct wpa_scan_res *));
+ if (results->res == NULL) {
+ os_free(results);
+ os_free(buf);
+ return NULL;
+ }
+
+ while (results->num < (size_t) num && end - pos > (int) sizeof(int)) {
+ int len;
+ os_memcpy(&len, pos, sizeof(int));
+ pos += sizeof(int);
+ if (len < 0 || len > 10000 || len > end - pos)
+ break;
+
+ r = os_memdup(pos, len);
+ if (r == NULL)
+ break;
+ pos += len;
+ if (sizeof(*r) + r->ie_len + r->beacon_ie_len > (size_t) len) {
+ wpa_printf(MSG_ERROR,
+ "privsep: Invalid scan result len (%d + %d + %d > %d)",
+ (int) sizeof(*r), (int) r->ie_len,
+ (int) r->beacon_ie_len, len);
+ os_free(r);
+ break;
+ }
+
+ results->res[results->num++] = r;
+ }
+
+ os_free(buf);
+ return results;
+}
+
+
+static int wpa_driver_privsep_set_key(void *priv,
+ struct wpa_driver_set_key_params *params)
+{
+ struct wpa_driver_privsep_data *drv = priv;
+ struct privsep_cmd_set_key cmd;
+ enum wpa_alg alg = params->alg;
+ const u8 *addr = params->addr;
+ int key_idx = params->key_idx;
+ int set_tx = params->set_tx;
+ const u8 *seq = params->seq;
+ size_t seq_len = params->seq_len;
+ const u8 *key = params->key;
+ size_t key_len = params->key_len;
+
+ 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;
+ cmd.key_flag = params->key_flag;
+ 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_authenticate(
+ void *priv, struct wpa_driver_auth_params *params)
+{
+ struct wpa_driver_privsep_data *drv = priv;
+ struct privsep_cmd_authenticate *data;
+ int i, res;
+ size_t buflen;
+ u8 *pos;
+
+ wpa_printf(MSG_DEBUG, "%s: priv=%p freq=%d bssid=" MACSTR
+ " auth_alg=%d local_state_change=%d p2p=%d",
+ __func__, priv, params->freq, MAC2STR(params->bssid),
+ params->auth_alg, params->local_state_change, params->p2p);
+
+ buflen = sizeof(*data) + params->ie_len + params->auth_data_len;
+ data = os_zalloc(buflen);
+ if (data == NULL)
+ return -1;
+
+ data->freq = params->freq;
+ os_memcpy(data->bssid, params->bssid, ETH_ALEN);
+ os_memcpy(data->ssid, params->ssid, params->ssid_len);
+ data->ssid_len = params->ssid_len;
+ data->auth_alg = params->auth_alg;
+ data->ie_len = params->ie_len;
+ for (i = 0; i < 4; i++) {
+ if (params->wep_key[i])
+ os_memcpy(data->wep_key[i], params->wep_key[i],
+ params->wep_key_len[i]);
+ data->wep_key_len[i] = params->wep_key_len[i];
+ }
+ data->wep_tx_keyidx = params->wep_tx_keyidx;
+ data->local_state_change = params->local_state_change;
+ data->p2p = params->p2p;
+ pos = (u8 *) (data + 1);
+ if (params->ie_len) {
+ os_memcpy(pos, params->ie, params->ie_len);
+ pos += params->ie_len;
+ }
+ if (params->auth_data_len)
+ os_memcpy(pos, params->auth_data, params->auth_data_len);
+
+ res = wpa_priv_cmd(drv, PRIVSEP_CMD_AUTHENTICATE, data, buflen,
+ NULL, NULL);
+ os_free(data);
+
+ return res;
+}
+
+
+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.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->hwmode = params->freq.mode;
+ data->freq = params->freq.freq;
+ data->channel = params->freq.channel;
+ 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) + SSID_MAX_LEN];
+ 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 > SSID_MAX_LEN ||
+ 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,
+ u16 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_auth(void *ctx, u8 *buf, size_t len)
+{
+ union wpa_event_data data;
+ struct privsep_event_auth *auth;
+
+ os_memset(&data, 0, sizeof(data));
+ if (len < sizeof(*auth))
+ return;
+ auth = (struct privsep_event_auth *) buf;
+ if (len < sizeof(*auth) + auth->ies_len)
+ return;
+
+ os_memcpy(data.auth.peer, auth->peer, ETH_ALEN);
+ os_memcpy(data.auth.bssid, auth->bssid, ETH_ALEN);
+ data.auth.auth_type = auth->auth_type;
+ data.auth.auth_transaction = auth->auth_transaction;
+ data.auth.status_code = auth->status_code;
+ if (auth->ies_len) {
+ data.auth.ies = (u8 *) (auth + 1);
+ data.auth.ies_len = auth->ies_len;
+ }
+
+ wpa_supplicant_event(ctx, EVENT_AUTH, &data);
+}
+
+
+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_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) {
+ wpa_printf(MSG_ERROR, "recvfrom(priv_socket): %s",
+ strerror(errno));
+ 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_SCAN_STARTED:
+ wpa_supplicant_event(drv->ctx, EVENT_SCAN_STARTED, 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_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;
+ case PRIVSEP_EVENT_AUTH:
+ wpa_driver_privsep_event_auth(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) {
+ wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
+ 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) {
+ wpa_printf(MSG_ERROR,
+ "privsep-set-params priv-sock: bind(PF_UNIX): %s",
+ strerror(errno));
+ 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) {
+ wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
+ 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)
+ {
+ wpa_printf(MSG_ERROR,
+ "privsep-set-params cmd-sock: bind(PF_UNIX): %s",
+ strerror(errno));
+ 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;
+ /* For now, no support for passing extended_capa pointers */
+ capa->extended_capa = NULL;
+ capa->extended_capa_mask = NULL;
+ capa->extended_capa_len = 0;
+ /* Control port is not yet supported */
+ capa->flags &= ~WPA_DRIVER_FLAGS_CONTROL_PORT;
+ 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,
+ .authenticate = wpa_driver_privsep_authenticate,
+ .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,
+};
+
+
+const struct wpa_driver_ops *const wpa_drivers[] =
+{
+ &wpa_driver_privsep_ops,
+ NULL
+};
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_roboswitch.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_roboswitch.c
new file mode 100644
index 0000000..9beb6c4
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_roboswitch.c
@@ -0,0 +1,487 @@
+/*
+ * WPA Supplicant - roboswitch driver interface
+ * Copyright (c) 2008-2012 Jouke Witteveen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README 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) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCGMIIREG]: %s",
+ strerror(errno));
+ 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) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCSMIIREG]: %s",
+ strerror(errno));
+ }
+}
+
+
+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 */
+ if (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) ||
+ os_memcmp(read1, read2, 6) != 0)
+ return -1;
+ if (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) ||
+ 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);
+
+ if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_CONF, &_read, 1) < 0)
+ return -1;
+ /* If ARL control is disabled, there is nothing to leave. */
+ if (!(_read & (1 << 4))) return -1;
+
+ if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_ADDR_1, addr_read, 3) < 0 ||
+ wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_VEC_1, &ports_read, 1) < 0)
+ return -1;
+ /* check if we occupy multiport address 1 */
+ if (os_memcmp(addr_read, addr_be16, 6) == 0 && ports_read == ports) {
+ if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_ADDR_2, addr_read,
+ 3) < 0 ||
+ wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_VEC_2, &ports_read,
+ 1) < 0)
+ return -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 {
+ if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_ADDR_2, addr_read,
+ 3) < 0 ||
+ wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_VEC_2, &ports_read,
+ 1) < 0)
+ return -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 '.' separating <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) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCGMIIPHY]: %s",
+ strerror(errno));
+ os_free(drv);
+ return NULL;
+ }
+ /* BCM63xx devices provide 0 here */
+ if (if_mii(&drv->ifr)->phy_id != ROBO_PHY_ADDR &&
+ if_mii(&drv->ifr)->phy_id != 0) {
+ 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/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_wext.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_wext.c
new file mode 100644
index 0000000..cf201fe
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_wext.c
@@ -0,0 +1,2499 @@
+/*
+ * Driver interaction with generic Linux Wireless Extensions
+ * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README 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 <dirent.h>
+
+#include "linux_wext.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) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCGIWAP]: %s", strerror(errno));
+ 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) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCSIWAP]: %s", strerror(errno));
+ 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 = SSID_MAX_LEN;
+
+ if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCGIWESSID]: %s",
+ strerror(errno));
+ ret = -1;
+ } else {
+ ret = iwr.u.essid.length;
+ if (ret > SSID_MAX_LEN)
+ ret = SSID_MAX_LEN;
+ /* 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 > SSID_MAX_LEN)
+ 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) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCSIWESSID]: %s",
+ strerror(errno));
+ 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) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCSIWFREQ]: %s",
+ strerror(errno));
+ 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);
+ }
+}
+
+
+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_memdup(ev, len);
+ if (drv->assoc_req_ies == NULL) {
+ drv->assoc_req_ies_len = 0;
+ return -1;
+ }
+ 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_memdup(ev, len);
+ if (drv->assoc_resp_ies == NULL) {
+ drv->assoc_resp_ies_len = 0;
+ return -1;
+ }
+ 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, unsigned int len)
+{
+ struct iw_event iwe_buf, *iwe = &iwe_buf;
+ char *pos, *end, *custom, *buf;
+
+ pos = data;
+ end = data + len;
+
+ while ((size_t) (end - pos) >= IW_EV_LCP_LEN) {
+ /* 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 || iwe->len > end - pos)
+ 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 (iwe->u.data.length > end - custom) {
+ 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 (iwe->u.data.length > end - custom) {
+ wpa_printf(MSG_DEBUG, "WEXT: Invalid "
+ "IWEVCUSTOM length");
+ return;
+ }
+ buf = dup_binstr(custom, iwe->u.data.length);
+ if (buf == NULL)
+ return;
+ 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 (iwe->u.data.length > end - custom) {
+ 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 (iwe->u.data.length > end - custom) {
+ 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 (iwe->u.data.length > end - custom) {
+ 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) {
+ if (drv->if_removed) {
+ wpa_printf(MSG_DEBUG, "WEXT: if_removed "
+ "already set - ignore event");
+ return;
+ }
+ drv->if_removed = 1;
+ } else {
+ if (if_nametoindex(drv->ifname) == 0) {
+ wpa_printf(MSG_DEBUG, "WEXT: Interface %s "
+ "does not exist - ignore "
+ "RTM_NEWLINK",
+ drv->ifname);
+ return;
+ }
+ if (!drv->if_removed) {
+ wpa_printf(MSG_DEBUG, "WEXT: if_removed "
+ "already cleared - ignore event");
+ return;
+ }
+ 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;
+ char namebuf[IFNAMSIZ];
+
+ 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)) {
+ if (if_indextoname(ifi->ifi_index, namebuf) &&
+ linux_iface_up(drv->ioctl_sock, drv->ifname) == 0) {
+ wpa_printf(MSG_DEBUG, "WEXT: Ignore interface up "
+ "event since interface %s is down",
+ namebuf);
+ } else if (if_nametoindex(drv->ifname) == 0) {
+ wpa_printf(MSG_DEBUG, "WEXT: Ignore interface up "
+ "event since interface %s does not exist",
+ drv->ifname);
+ } else if (drv->if_removed) {
+ wpa_printf(MSG_DEBUG, "WEXT: Ignore interface up "
+ "event since interface %s is marked "
+ "removed", drv->ifname);
+ } else {
+ 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) {
+ wpa_printf(MSG_ERROR, "socket(PF_INET,SOCK_DGRAM): %s",
+ strerror(errno));
+ 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 wext_hostap_ifname(struct wpa_driver_wext_data *drv,
+ const char *ifname)
+{
+ char buf[200], *res;
+ int type, ret;
+ FILE *f;
+
+ if (strcmp(ifname, ".") == 0 || strcmp(ifname, "..") == 0)
+ return -1;
+
+ ret = snprintf(buf, sizeof(buf), "/sys/class/net/%s/device/net/%s/type",
+ drv->ifname, ifname);
+ if (os_snprintf_error(sizeof(buf), ret))
+ return -1;
+
+ f = fopen(buf, "r");
+ if (!f)
+ return -1;
+ res = fgets(buf, sizeof(buf), f);
+ fclose(f);
+
+ type = res ? atoi(res) : -1;
+ wpa_printf(MSG_DEBUG, "WEXT: hostap ifname %s type %d", ifname, type);
+
+ if (type == ARPHRD_IEEE80211) {
+ wpa_printf(MSG_DEBUG,
+ "WEXT: Found hostap driver wifi# interface (%s)",
+ ifname);
+ wpa_driver_wext_alternative_ifindex(drv, ifname);
+ return 0;
+ }
+ return -1;
+}
+
+
+static int wext_add_hostap(struct wpa_driver_wext_data *drv)
+{
+ char buf[200];
+ int n;
+ struct dirent **names;
+ int ret = -1;
+
+ snprintf(buf, sizeof(buf), "/sys/class/net/%s/device/net", drv->ifname);
+ n = scandir(buf, &names, NULL, alphasort);
+ if (n < 0)
+ return -1;
+
+ while (n--) {
+ if (ret < 0 && wext_hostap_ifname(drv, names[n]->d_name) == 0)
+ ret = 0;
+ free(names[n]);
+ }
+ free(names);
+
+ return ret;
+}
+
+
+static void wext_check_hostap(struct wpa_driver_wext_data *drv)
+{
+ char path[200], buf[200], *pos;
+ ssize_t res;
+
+ /*
+ * 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. However, it looks like
+ * the wifi# interface is still used in the current kernel tree, so it
+ * may not really be possible to remove this before the Host AP driver
+ * gets removed from the kernel.
+ */
+
+ /* First, try to see if driver information is available from sysfs */
+ snprintf(path, sizeof(path), "/sys/class/net/%s/device/driver",
+ drv->ifname);
+ res = readlink(path, buf, sizeof(buf) - 1);
+ if (res > 0) {
+ buf[res] = '\0';
+ pos = strrchr(buf, '/');
+ if (pos)
+ pos++;
+ else
+ pos = buf;
+ wpa_printf(MSG_DEBUG, "WEXT: Driver: %s", pos);
+ if (os_strncmp(pos, "hostap", 6) == 0 &&
+ wext_add_hostap(drv) == 0)
+ return;
+ }
+
+ /* Second, use the old design with hardcoded ifname */
+ if (os_strncmp(drv->ifname, "wlan", 4) == 0) {
+ char ifname2[IFNAMSIZ + 1];
+ os_strlcpy(ifname2, drv->ifname, sizeof(ifname2));
+ os_memcpy(ifname2, "wifi", 4);
+ wpa_driver_wext_alternative_ifindex(drv, ifname2);
+ }
+}
+
+
+static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv)
+{
+ int send_rfkill_event = 0;
+ int i;
+
+ 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);
+
+ /* Update per interface supported AKMs */
+ for (i = 0; i < WPA_IF_MAX; i++)
+ drv->capa.key_mgmt_iftype[i] = drv->capa.key_mgmt;
+
+ /*
+ * 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);
+
+ wext_check_hostap(drv);
+
+ 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);
+ eloop_cancel_timeout(wpa_driver_wext_send_rfkill, 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) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCSIWSCAN]: %s",
+ strerror(errno));
+ ret = -1;
+ }
+
+ /* 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 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 {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCGIWSCAN]: %s",
+ strerror(errno));
+ 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[SSID_MAX_LEN];
+ 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 (ssid_len > end - custom)
+ 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 (clen > (size_t) (end - custom))
+ 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 (clen > (size_t) (end - custom))
+ 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 && end - pos > 1) {
+ if (2 + pos[1] > end - pos)
+ 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_array(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 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;
+
+ 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 ((size_t) (end - pos) >= IW_EV_LCP_LEN) {
+ /* 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 || iwe->len > end - pos)
+ 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) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCGIWRANGE]: %s",
+ strerror(errno));
+ 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;
+ drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP128;
+ 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_PSK |
+ WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X;
+ 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%llx",
+ drv->capa.key_mgmt, drv->capa.enc,
+ (unsigned long long) 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_8021X))
+ 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)
+ wpa_printf(MSG_ERROR, "ioctl[SIOCSIWENCODEEXT] PMK: %s",
+ strerror(errno));
+ 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,
+ enum key_flag key_flag)
+{
+ 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;
+ }
+ if (key_flag & KEY_FLAG_PMK) {
+ ext->alg = IW_ENCODE_ALG_PMK;
+ } else {
+ 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_BIP_CMAC_128:
+ ext->alg = IW_ENCODE_ALG_AES_CMAC;
+ break;
+ 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;
+ }
+
+ wpa_printf(MSG_ERROR, "ioctl[SIOCSIWENCODEEXT]: %s",
+ strerror(errno));
+ }
+
+ os_free(ext);
+ return ret;
+}
+
+
+/**
+ * wpa_driver_wext_set_key - Configure encryption key
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * @params: Key parameters
+ * 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.
+ */
+static int wpa_driver_wext_set_key(void *priv,
+ struct wpa_driver_set_key_params *params)
+{
+ struct wpa_driver_wext_data *drv = priv;
+ struct iwreq iwr;
+ int ret = 0;
+ enum wpa_alg alg = params->alg;
+ enum key_flag key_flag = params->key_flag;
+ const u8 *addr = params->addr;
+ int key_idx = params->key_idx;
+ int set_tx = params->set_tx;
+ const u8 *seq = params->seq;
+ size_t seq_len = params->seq_len;
+ const u8 *key = params->key;
+ size_t key_len = params->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);
+
+ ret = wpa_driver_wext_set_key_ext(drv, alg, addr, key_idx, set_tx,
+ seq, seq_len, key, key_len, key_flag);
+ 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) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCSIWENCODE]: %s",
+ strerror(errno));
+ 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) {
+ wpa_printf(MSG_ERROR,
+ "ioctl[SIOCSIWENCODE] (set_tx): %s",
+ strerror(errno));
+ 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, u16 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) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCSIWMLME]: %s",
+ strerror(errno));
+ 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[SSID_MAX_LEN];
+ 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) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCGIWMODE]: %s",
+ strerror(errno));
+ iwr.u.mode = IW_MODE_INFRA;
+ }
+
+ if (iwr.u.mode == IW_MODE_INFRA) {
+ /* Clear the BSSID selection */
+ if (wpa_driver_wext_set_bssid(drv, null_bssid) < 0) {
+ wpa_printf(MSG_DEBUG, "WEXT: Failed to clear BSSID "
+ "selection on disconnect");
+ }
+
+ if (drv->cfg80211) {
+ /*
+ * cfg80211 supports SIOCSIWMLME commands, so there is
+ * no need for the random SSID hack, but clear the
+ * SSID.
+ */
+ if (wpa_driver_wext_set_ssid(drv, (u8 *) "", 0) < 0) {
+ wpa_printf(MSG_DEBUG, "WEXT: Failed to clear "
+ "SSID on disconnect");
+ }
+ return;
+ }
+
+ /*
+ * 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 < SSID_MAX_LEN; i++)
+ ssid[i] = rand() & 0xFF;
+ if (wpa_driver_wext_set_ssid(drv, ssid, SSID_MAX_LEN) < 0) {
+ wpa_printf(MSG_DEBUG, "WEXT: Failed to set bogus "
+ "SSID to disconnect");
+ }
+ }
+}
+
+
+static int wpa_driver_wext_deauthenticate(void *priv, const u8 *addr,
+ u16 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_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) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCSIWGENIE]: %s",
+ strerror(errno));
+ ret = -1;
+ }
+
+ return ret;
+}
+
+
+int wpa_driver_wext_cipher2wext(int cipher)
+{
+ switch (cipher) {
+ case WPA_CIPHER_NONE:
+ return IW_AUTH_CIPHER_NONE;
+ case WPA_CIPHER_WEP40:
+ return IW_AUTH_CIPHER_WEP40;
+ case WPA_CIPHER_TKIP:
+ return IW_AUTH_CIPHER_TKIP;
+ case WPA_CIPHER_CCMP:
+ return IW_AUTH_CIPHER_CCMP;
+ case WPA_CIPHER_WEP104:
+ return IW_AUTH_CIPHER_WEP104;
+ default:
+ return 0;
+ }
+}
+
+
+int wpa_driver_wext_keymgmt2wext(int keymgmt)
+{
+ switch (keymgmt) {
+ case WPA_KEY_MGMT_IEEE8021X:
+ case WPA_KEY_MGMT_IEEE8021X_NO_WPA:
+ return IW_AUTH_KEY_MGMT_802_1X;
+ case WPA_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) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCSIWENCODE]: %s",
+ strerror(errno));
+ 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.
+ */
+ if (wpa_driver_wext_set_ssid(drv, (u8 *) "", 0) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "WEXT: Failed to clear SSID to stop pending cfg80211 association attempts (if any)");
+ /* continue anyway */
+ }
+ }
+
+ 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_proto & WPA_PROTO_RSN)
+ value = IW_AUTH_WPA_VERSION_WPA2;
+ else if (params->wpa_proto & WPA_PROTO_WPA)
+ value = IW_AUTH_WPA_VERSION_WPA;
+ else
+ value = IW_AUTH_WPA_VERSION_DISABLED;
+ 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 != WPA_KEY_MGMT_NONE ||
+ params->pairwise_suite != WPA_CIPHER_NONE ||
+ params->group_suite != WPA_CIPHER_NONE ||
+ (params->wpa_proto & (WPA_PROTO_RSN | WPA_PROTO_WPA));
+ 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 == WPA_KEY_MGMT_IEEE8021X ||
+ params->key_mgmt_suite == WPA_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;
+ 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;
+ if (params->freq.freq &&
+ wpa_driver_wext_set_freq(drv, params->freq.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) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCSIWMODE]: %s",
+ strerror(errno));
+ 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) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCGIWMODE]: %s",
+ strerror(errno));
+ 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)
+ wpa_printf(MSG_ERROR, "ioctl[SIOCSIWMODE]: %s",
+ strerror(errno));
+ 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)
+ wpa_printf(MSG_ERROR, "ioctl[SIOCSIWPMKSA]: %s",
+ strerror(errno));
+ ret = -1;
+ }
+
+ return ret;
+}
+
+
+static int wpa_driver_wext_add_pmkid(void *priv,
+ struct wpa_pmkid_params *params)
+{
+ struct wpa_driver_wext_data *drv = priv;
+ return wpa_driver_wext_pmksa(drv, IW_PMKSA_ADD, params->bssid,
+ params->pmkid);
+}
+
+
+static int wpa_driver_wext_remove_pmkid(void *priv,
+ struct wpa_pmkid_params *params)
+{
+ struct wpa_driver_wext_data *drv = priv;
+ return wpa_driver_wext_pmksa(drv, IW_PMKSA_REMOVE, params->bssid,
+ params->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;
+}
+
+
+static int wpa_driver_wext_signal_poll(void *priv, struct wpa_signal_info *si)
+{
+ struct wpa_driver_wext_data *drv = priv;
+ struct iw_statistics stats;
+ struct iwreq iwr;
+
+ os_memset(si, 0, sizeof(*si));
+ si->data.signal = -WPA_INVALID_NOISE;
+ si->current_noise = WPA_INVALID_NOISE;
+ si->chanwidth = CHAN_WIDTH_UNKNOWN;
+
+ os_memset(&iwr, 0, sizeof(iwr));
+ os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+ iwr.u.data.pointer = (caddr_t) &stats;
+ iwr.u.data.length = sizeof(stats);
+ iwr.u.data.flags = 1;
+
+ if (ioctl(drv->ioctl_sock, SIOCGIWSTATS, &iwr) < 0) {
+ wpa_printf(MSG_ERROR, "WEXT: SIOCGIWSTATS: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ si->data.signal = stats.qual.level -
+ ((stats.qual.updated & IW_QUAL_DBM) ? 0x100 : 0);
+ si->current_noise = stats.qual.noise -
+ ((stats.qual.updated & IW_QUAL_DBM) ? 0x100 : 0);
+ return 0;
+}
+
+
+static int wpa_driver_wext_status(void *priv, char *buf, size_t buflen)
+{
+ struct wpa_driver_wext_data *drv = priv;
+ int res;
+ char *pos, *end;
+ unsigned char addr[ETH_ALEN];
+
+ pos = buf;
+ end = buf + buflen;
+
+ if (linux_get_ifhwaddr(drv->ioctl_sock, drv->ifname, addr))
+ return -1;
+
+ res = os_snprintf(pos, end - pos,
+ "ifindex=%d\n"
+ "ifname=%s\n"
+ "addr=" MACSTR "\n",
+ drv->ifindex,
+ drv->ifname,
+ MAC2STR(addr));
+ if (os_snprintf_error(end - pos, res))
+ return pos - buf;
+ pos += res;
+
+ return pos - buf;
+}
+
+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,
+ .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,
+ .signal_poll = wpa_driver_wext_signal_poll,
+ .status = wpa_driver_wext_status,
+};
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_wext.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_wext.h
new file mode 100644
index 0000000..6214cdf
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_wext.h
@@ -0,0 +1,77 @@
+/*
+ * WPA Supplicant - driver_wext exported functions
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README 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_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/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_wired.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_wired.c
new file mode 100644
index 0000000..03366b8
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_wired.c
@@ -0,0 +1,407 @@
+/*
+ * Wired Ethernet driver interface
+ * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004, Gunter Burchardt <tira@isx.de>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "driver.h"
+#include "driver_wired_common.h"
+
+#include <sys/ioctl.h>
+#undef IFNAMSIZ
+#include <net/if.h>
+#ifdef __linux__
+#include <netpacket/packet.h>
+#include <net/if_arp.h>
+#endif /* __linux__ */
+#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) */
+#ifdef __sun__
+#include <sys/sockio.h>
+#endif /* __sun__ */
+
+#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 */
+
+
+struct wpa_driver_wired_data {
+ struct driver_wired_common_data common;
+
+ int dhcp_sock; /* socket for dhcp packets */
+ int use_pae_group_addr;
+};
+
+
+/* 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 */
+};
+
+
+#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) {
+ wpa_printf(MSG_ERROR, "recv: %s", strerror(errno));
+ 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) {
+ wpa_printf(MSG_ERROR, "recv: %s", strerror(errno));
+ 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->common.sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PAE));
+ if (drv->common.sock < 0) {
+ wpa_printf(MSG_ERROR, "socket[PF_PACKET,SOCK_RAW]: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ if (eloop_register_read_sock(drv->common.sock, handle_read,
+ drv->common.ctx, NULL)) {
+ wpa_printf(MSG_INFO, "Could not register read socket");
+ return -1;
+ }
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, drv->common.ifname, sizeof(ifr.ifr_name));
+ if (ioctl(drv->common.sock, SIOCGIFINDEX, &ifr) != 0) {
+ wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s",
+ strerror(errno));
+ 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->common.sock, (struct sockaddr *) &addr, sizeof(addr)) < 0)
+ {
+ wpa_printf(MSG_ERROR, "bind: %s", strerror(errno));
+ return -1;
+ }
+
+ /* filter multicast address */
+ if (wired_multicast_membership(drv->common.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->common.ifname, sizeof(ifr.ifr_name));
+ if (ioctl(drv->common.sock, SIOCGIFHWADDR, &ifr) != 0) {
+ wpa_printf(MSG_ERROR, "ioctl(SIOCGIFHWADDR): %s",
+ strerror(errno));
+ return -1;
+ }
+
+ if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
+ wpa_printf(MSG_INFO, "Invalid HW-addr family 0x%04x",
+ 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) {
+ wpa_printf(MSG_ERROR, "socket call failed for dhcp: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ if (eloop_register_read_sock(drv->dhcp_sock, handle_dhcp,
+ drv->common.ctx, NULL)) {
+ wpa_printf(MSG_INFO, "Could not register read socket");
+ 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) {
+ wpa_printf(MSG_ERROR, "setsockopt[SOL_SOCKET,SO_REUSEADDR]: %s",
+ strerror(errno));
+ return -1;
+ }
+ if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_BROADCAST, (char *) &n,
+ sizeof(n)) == -1) {
+ wpa_printf(MSG_ERROR, "setsockopt[SOL_SOCKET,SO_BROADCAST]: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_ifrn.ifrn_name, drv->common.ifname, IFNAMSIZ);
+ if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_BINDTODEVICE,
+ (char *) &ifr, sizeof(ifr)) < 0) {
+ wpa_printf(MSG_ERROR,
+ "setsockopt[SOL_SOCKET,SO_BINDTODEVICE]: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ if (bind(drv->dhcp_sock, (struct sockaddr *) &addr2,
+ sizeof(struct sockaddr)) == -1) {
+ wpa_printf(MSG_ERROR, "bind: %s", strerror(errno));
+ 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, int link_id)
+{
+ 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) {
+ wpa_printf(MSG_INFO,
+ "malloc() failed for wired_send_eapol(len=%lu)",
+ (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->common.sock, (u8 *) hdr, len, 0);
+ os_free(hdr);
+
+ if (res < 0) {
+ wpa_printf(MSG_ERROR,
+ "wired_send_eapol - packet len: %lu - failed: send: %s",
+ (unsigned long) len, strerror(errno));
+ }
+
+ 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) {
+ wpa_printf(MSG_INFO,
+ "Could not allocate memory for wired driver data");
+ return NULL;
+ }
+
+ drv->common.ctx = hapd;
+ os_strlcpy(drv->common.ifname, params->ifname,
+ sizeof(drv->common.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->common.sock >= 0) {
+ eloop_unregister_read_sock(drv->common.sock);
+ close(drv->common.sock);
+ }
+
+ if (drv->dhcp_sock >= 0) {
+ eloop_unregister_read_sock(drv->dhcp_sock);
+ close(drv->dhcp_sock);
+ }
+
+ os_free(drv);
+}
+
+
+static void * wpa_driver_wired_init(void *ctx, const char *ifname)
+{
+ struct wpa_driver_wired_data *drv;
+
+ drv = os_zalloc(sizeof(*drv));
+ if (drv == NULL)
+ return NULL;
+
+ if (driver_wired_init_common(&drv->common, ifname, ctx) < 0) {
+ os_free(drv);
+ return NULL;
+ }
+
+ return drv;
+}
+
+
+static void wpa_driver_wired_deinit(void *priv)
+{
+ struct wpa_driver_wired_data *drv = priv;
+
+ driver_wired_deinit_common(&drv->common);
+ 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 = driver_wired_get_ssid,
+ .get_bssid = driver_wired_get_bssid,
+ .get_capa = driver_wired_get_capa,
+ .init = wpa_driver_wired_init,
+ .deinit = wpa_driver_wired_deinit,
+};
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_wired_common.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_wired_common.c
new file mode 100644
index 0000000..a860b1c
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_wired_common.c
@@ -0,0 +1,322 @@
+/*
+ * Common functions for Wired Ethernet driver interfaces
+ * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004, Gunter Burchardt <tira@isx.de>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "driver.h"
+#include "driver_wired_common.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>
+#include <net/if_media.h>
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) */
+#ifdef __sun__
+#include <sys/sockio.h>
+#endif /* __sun__ */
+
+
+static int driver_wired_get_ifflags(const char *ifname, int *flags)
+{
+ struct ifreq ifr;
+ int s;
+
+ s = socket(PF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
+ return -1;
+ }
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+ if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCGIFFLAGS]: %s",
+ strerror(errno));
+ close(s);
+ return -1;
+ }
+ close(s);
+ *flags = ifr.ifr_flags & 0xffff;
+ return 0;
+}
+
+
+static int driver_wired_set_ifflags(const char *ifname, int flags)
+{
+ struct ifreq ifr;
+ int s;
+
+ s = socket(PF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
+ 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) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCSIFFLAGS]: %s",
+ strerror(errno));
+ close(s);
+ return -1;
+ }
+ close(s);
+ return 0;
+}
+
+
+static int 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) {
+ wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
+ 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) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOC{ADD/DEL}MULTI]: %s",
+ strerror(errno));
+ close(s);
+ return -1;
+ }
+ close(s);
+ return 0;
+}
+
+
+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) {
+ wpa_printf(MSG_ERROR, "setsockopt: %s", strerror(errno));
+ return -1;
+ }
+ return 0;
+#else /* __linux__ */
+ return -1;
+#endif /* __linux__ */
+}
+
+
+int driver_wired_get_ssid(void *priv, u8 *ssid)
+{
+ ssid[0] = 0;
+ return 0;
+}
+
+
+int 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;
+}
+
+
+int 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;
+}
+
+
+#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+static int driver_wired_get_ifstatus(const char *ifname, int *status)
+{
+ struct ifmediareq ifmr;
+ int s;
+
+ s = socket(PF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
+ return -1;
+ }
+
+ os_memset(&ifmr, 0, sizeof(ifmr));
+ os_strlcpy(ifmr.ifm_name, ifname, IFNAMSIZ);
+ if (ioctl(s, SIOCGIFMEDIA, (caddr_t) &ifmr) < 0) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCGIFMEDIA]: %s",
+ strerror(errno));
+ close(s);
+ return -1;
+ }
+ close(s);
+ *status = (ifmr.ifm_status & (IFM_ACTIVE | IFM_AVALID)) ==
+ (IFM_ACTIVE | IFM_AVALID);
+
+ return 0;
+}
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
+
+
+int driver_wired_init_common(struct driver_wired_common_data *common,
+ const char *ifname, void *ctx)
+{
+ int flags;
+
+ os_strlcpy(common->ifname, ifname, sizeof(common->ifname));
+ common->ctx = ctx;
+
+#ifdef __linux__
+ common->pf_sock = socket(PF_PACKET, SOCK_DGRAM, 0);
+ if (common->pf_sock < 0)
+ wpa_printf(MSG_ERROR, "socket(PF_PACKET): %s", strerror(errno));
+#else /* __linux__ */
+ common->pf_sock = -1;
+#endif /* __linux__ */
+
+ if (driver_wired_get_ifflags(ifname, &flags) == 0 &&
+ !(flags & IFF_UP) &&
+ driver_wired_set_ifflags(ifname, flags | IFF_UP) == 0)
+ common->iff_up = 1;
+
+ if (wired_multicast_membership(common->pf_sock,
+ if_nametoindex(common->ifname),
+ pae_group_addr, 1) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "%s: Added multicast membership with packet socket",
+ __func__);
+ common->membership = 1;
+ } else if (driver_wired_multi(ifname, pae_group_addr, 1) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "%s: Added multicast membership with SIOCADDMULTI",
+ __func__);
+ common->multi = 1;
+ } else if (driver_wired_get_ifflags(ifname, &flags) < 0) {
+ wpa_printf(MSG_INFO, "%s: Could not get interface flags",
+ __func__);
+ return -1;
+ } else if (flags & IFF_ALLMULTI) {
+ wpa_printf(MSG_DEBUG,
+ "%s: Interface is already configured for multicast",
+ __func__);
+ } else if (driver_wired_set_ifflags(ifname,
+ flags | IFF_ALLMULTI) < 0) {
+ wpa_printf(MSG_INFO, "%s: Failed to enable allmulti", __func__);
+ return -1;
+ } else {
+ wpa_printf(MSG_DEBUG, "%s: Enabled allmulti mode", __func__);
+ common->iff_allmulti = 1;
+ }
+#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+ {
+ int status;
+
+ wpa_printf(MSG_DEBUG, "%s: waiting for link to become active",
+ __func__);
+ while (driver_wired_get_ifstatus(ifname, &status) == 0 &&
+ status == 0)
+ sleep(1);
+ }
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
+
+ return 0;
+}
+
+
+void driver_wired_deinit_common(struct driver_wired_common_data *common)
+{
+ int flags;
+
+ if (common->membership &&
+ wired_multicast_membership(common->pf_sock,
+ if_nametoindex(common->ifname),
+ pae_group_addr, 0) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "%s: Failed to remove PAE multicast group (PACKET)",
+ __func__);
+ }
+
+ if (common->multi &&
+ driver_wired_multi(common->ifname, pae_group_addr, 0) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "%s: Failed to remove PAE multicast group (SIOCDELMULTI)",
+ __func__);
+ }
+
+ if (common->iff_allmulti &&
+ (driver_wired_get_ifflags(common->ifname, &flags) < 0 ||
+ driver_wired_set_ifflags(common->ifname,
+ flags & ~IFF_ALLMULTI) < 0)) {
+ wpa_printf(MSG_DEBUG, "%s: Failed to disable allmulti mode",
+ __func__);
+ }
+
+ if (common->iff_up &&
+ driver_wired_get_ifflags(common->ifname, &flags) == 0 &&
+ (flags & IFF_UP) &&
+ driver_wired_set_ifflags(common->ifname, flags & ~IFF_UP) < 0) {
+ wpa_printf(MSG_DEBUG, "%s: Failed to set the interface down",
+ __func__);
+ }
+
+ if (common->pf_sock != -1)
+ close(common->pf_sock);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_wired_common.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_wired_common.h
new file mode 100644
index 0000000..2bb0710
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_wired_common.h
@@ -0,0 +1,34 @@
+/*
+ * Common definitions for Wired Ethernet driver interfaces
+ * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004, Gunter Burchardt <tira@isx.de>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DRIVER_WIRED_COMMON_H
+#define DRIVER_WIRED_COMMON_H
+
+struct driver_wired_common_data {
+ char ifname[IFNAMSIZ + 1];
+ void *ctx;
+
+ int sock; /* raw packet socket for driver access */
+ int pf_sock;
+ int membership, multi, iff_allmulti, iff_up;
+};
+
+static const u8 pae_group_addr[ETH_ALEN] =
+{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
+
+int wired_multicast_membership(int sock, int ifindex, const u8 *addr, int add);
+int driver_wired_get_ssid(void *priv, u8 *ssid);
+int driver_wired_get_bssid(void *priv, u8 *bssid);
+int driver_wired_get_capa(void *priv, struct wpa_driver_capa *capa);
+
+int driver_wired_init_common(struct driver_wired_common_data *common,
+ const char *ifname, void *ctx);
+void driver_wired_deinit_common(struct driver_wired_common_data *common);
+
+#endif /* DRIVER_WIRED_COMMON_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/drivers.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/drivers.c
new file mode 100644
index 0000000..9071da3
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/drivers.c
@@ -0,0 +1,57 @@
+/*
+ * Driver interface list
+ * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "driver.h"
+
+void (*wpa_supplicant_event)(void *ctx, enum wpa_event_type event,
+ union wpa_event_data *data);
+void (*wpa_supplicant_event_global)(void *ctx, enum wpa_event_type event,
+ union wpa_event_data *data);
+
+const struct wpa_driver_ops *const wpa_drivers[] =
+{
+#ifdef CONFIG_DRIVER_NL80211
+ &wpa_driver_nl80211_ops,
+#endif /* CONFIG_DRIVER_NL80211 */
+#ifdef CONFIG_DRIVER_WEXT
+ &wpa_driver_wext_ops,
+#endif /* CONFIG_DRIVER_WEXT */
+#ifdef CONFIG_DRIVER_HOSTAP
+ &wpa_driver_hostap_ops,
+#endif /* CONFIG_DRIVER_HOSTAP */
+#ifdef CONFIG_DRIVER_BSD
+ &wpa_driver_bsd_ops,
+#endif /* CONFIG_DRIVER_BSD */
+#ifdef CONFIG_DRIVER_OPENBSD
+ &wpa_driver_openbsd_ops,
+#endif /* CONFIG_DRIVER_OPENBSD */
+#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_MACSEC_LINUX
+ &wpa_driver_macsec_linux_ops,
+#endif /* CONFIG_DRIVER_MACSEC_LINUX */
+#ifdef CONFIG_DRIVER_MACSEC_QCA
+ &wpa_driver_macsec_qca_ops,
+#endif /* CONFIG_DRIVER_MACSEC_QCA */
+#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/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/drivers.mak b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/drivers.mak
new file mode 100644
index 0000000..8da44d9
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/drivers.mak
@@ -0,0 +1,218 @@
+##### 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_WIRED
+DRV_CFLAGS += -DCONFIG_DRIVER_WIRED
+DRV_OBJS += ../src/drivers/driver_wired.o
+NEED_DRV_WIRED_COMMON=1
+endif
+
+ifdef CONFIG_DRIVER_MACSEC_LINUX
+DRV_CFLAGS += -DCONFIG_DRIVER_MACSEC_LINUX
+DRV_OBJS += ../src/drivers/driver_macsec_linux.o
+NEED_DRV_WIRED_COMMON=1
+NEED_LIBNL=y
+CONFIG_LIBNL3_ROUTE=y
+endif
+
+ifdef CONFIG_DRIVER_NL80211_BRCM
+DRV_CFLAGS += -DCONFIG_DRIVER_NL80211_BRCM
+endif
+
+ifdef CONFIG_DRIVER_MACSEC_QCA
+DRV_CFLAGS += -DCONFIG_DRIVER_MACSEC_QCA
+DRV_OBJS += ../src/drivers/driver_macsec_qca.o
+NEED_DRV_WIRED_COMMON=1
+endif
+
+ifdef NEED_DRV_WIRED_COMMON
+DRV_OBJS += ../src/drivers/driver_wired_common.o
+endif
+
+ifdef CONFIG_DRIVER_NL80211
+DRV_CFLAGS += -DCONFIG_DRIVER_NL80211
+DRV_OBJS += ../src/drivers/driver_nl80211.o
+DRV_OBJS += ../src/drivers/driver_nl80211_capa.o
+DRV_OBJS += ../src/drivers/driver_nl80211_event.o
+DRV_OBJS += ../src/drivers/driver_nl80211_monitor.o
+DRV_OBJS += ../src/drivers/driver_nl80211_scan.o
+ifdef CONFIG_DRIVER_NL80211_QCA
+DRV_CFLAGS += -DCONFIG_DRIVER_NL80211_QCA
+endif
+NEED_SME=y
+NEED_AP_MLME=y
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+NEED_RADIOTAP=y
+NEED_LIBNL=y
+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_OPENBSD
+ifndef CONFIG_L2_PACKET
+CONFIG_L2_PACKET=freebsd
+endif
+DRV_CFLAGS += -DCONFIG_DRIVER_OPENBSD
+DRV_OBJS += ../src/drivers/driver_openbsd.o
+endif
+
+ifdef CONFIG_DRIVER_NONE
+DRV_CFLAGS += -DCONFIG_DRIVER_NONE
+DRV_OBJS += ../src/drivers/driver_none.o
+endif
+
+##### PURE AP DRIVERS
+
+ifdef CONFIG_DRIVER_HOSTAP
+DRV_AP_CFLAGS += -DCONFIG_DRIVER_HOSTAP
+DRV_AP_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_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
+ifdef ATH_GCM_SUPPORT
+CFLAGS += -DATH_GCM_SUPPORT
+endif
+endif
+
+##### PURE CLIENT DRIVERS
+
+ifdef CONFIG_DRIVER_WEXT
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_WEXT
+CONFIG_WIRELESS_EXTENSION=y
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+endif
+
+ifdef CONFIG_DRIVER_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_ROBOSWITCH
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_ROBOSWITCH
+DRV_WPA_OBJS += ../src/drivers/driver_roboswitch.o
+endif
+
+ifdef CONFIG_WIRELESS_EXTENSION
+DRV_WPA_CFLAGS += -DCONFIG_WIRELESS_EXTENSION
+DRV_WPA_OBJS += ../src/drivers/driver_wext.o
+endif
+
+ifdef NEED_NETLINK
+DRV_OBJS += ../src/drivers/netlink.o
+endif
+
+ifdef NEED_RFKILL
+DRV_OBJS += ../src/drivers/rfkill.o
+DRV_WPA_CFLAGS += -DCONFIG_RFKILL
+endif
+
+ifdef NEED_RADIOTAP
+DRV_OBJS += ../src/utils/radiotap.o
+endif
+
+ifdef CONFIG_FULL_DYNAMIC_VLAN
+NEED_LINUX_IOCTL=y
+ifdef CONFIG_VLAN_NETLINK
+NEED_LIBNL=y
+CONFIG_LIBNL3_ROUTE=y
+endif
+endif
+
+ifdef NEED_LINUX_IOCTL
+DRV_OBJS += ../src/drivers/linux_ioctl.o
+endif
+
+ifdef NEED_LIBNL
+ifndef CONFIG_LIBNL32
+ifndef CONFIG_LIBNL20
+ifndef CONFIG_LIBNL_TINY
+PKG_CONFIG ?= pkg-config
+HAVE_LIBNL3 := $(shell $(PKG_CONFIG) --exists libnl-3.0; echo $$?)
+ifeq ($(HAVE_LIBNL3),0)
+CONFIG_LIBNL32=y
+endif
+endif
+endif
+endif
+
+ifdef CONFIG_LIBNL32
+ DRV_LIBS += -lnl-3
+ DRV_LIBS += -lnl-genl-3
+ ifdef LIBNL_INC
+ DRV_CFLAGS += -I$(LIBNL_INC)
+ else
+ PKG_CONFIG ?= pkg-config
+ DRV_CFLAGS += $(shell $(PKG_CONFIG) --cflags libnl-3.0)
+ endif
+ ifdef CONFIG_LIBNL3_ROUTE
+ DRV_LIBS += -lnl-route-3
+ DRV_CFLAGS += -DCONFIG_LIBNL3_ROUTE
+ endif
+else
+ ifdef CONFIG_LIBNL_TINY
+ DRV_LIBS += -lnl-tiny
+ else
+ ifndef CONFIG_OSX
+ DRV_LIBS += -lnl
+ DRV_LIBS += -lnl-genl
+ endif
+ endif
+endif
+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/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/drivers.mk b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/drivers.mk
new file mode 100644
index 0000000..10eab6a
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/drivers.mk
@@ -0,0 +1,196 @@
+##### 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_WIRED
+DRV_CFLAGS += -DCONFIG_DRIVER_WIRED
+DRV_OBJS += src/drivers/driver_wired.c
+NEED_DRV_WIRED_COMMON=1
+endif
+
+ifdef CONFIG_DRIVER_MACSEC_LINUX
+DRV_CFLAGS += -DCONFIG_DRIVER_MACSEC_LINUX
+DRV_OBJS += src/drivers/driver_macsec_linux.c
+NEED_DRV_WIRED_COMMON=1
+CONFIG_LIBNL3_ROUTE=y
+NEED_LIBNL=y
+endif
+
+ifdef NEED_DRV_WIRED_COMMON
+DRV_OBJS += src/drivers/driver_wired_common.c
+endif
+
+ifdef CONFIG_DRIVER_NL80211
+DRV_CFLAGS += -DCONFIG_DRIVER_NL80211
+DRV_OBJS += src/drivers/driver_nl80211.c
+DRV_OBJS += src/drivers/driver_nl80211_android.c
+DRV_OBJS += src/drivers/driver_nl80211_capa.c
+DRV_OBJS += src/drivers/driver_nl80211_event.c
+DRV_OBJS += src/drivers/driver_nl80211_monitor.c
+DRV_OBJS += src/drivers/driver_nl80211_scan.c
+ifdef CONFIG_DRIVER_NL80211_QCA
+DRV_CFLAGS += -DCONFIG_DRIVER_NL80211_QCA
+endif
+ifdef CONFIG_DRIVER_NL80211_BRCM
+DRV_CFLAGS += -DCONFIG_DRIVER_NL80211_BRCM
+endif
+NEED_SME=y
+NEED_AP_MLME=y
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+NEED_RFKILL=y
+NEED_RADIOTAP=y
+NEED_LIBNL=y
+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_OPENBSD
+ifndef CONFIG_L2_PACKET
+CONFIG_L2_PACKET=freebsd
+endif
+DRV_CFLAGS += -DCONFIG_DRIVER_OPENBSD
+DRV_OBJS += src/drivers/driver_openbsd.c
+endif
+
+ifdef CONFIG_DRIVER_NONE
+DRV_CFLAGS += -DCONFIG_DRIVER_NONE
+DRV_OBJS += src/drivers/driver_none.c
+endif
+
+##### PURE AP DRIVERS
+
+ifdef CONFIG_DRIVER_HOSTAP
+DRV_AP_CFLAGS += -DCONFIG_DRIVER_HOSTAP
+DRV_AP_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_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_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_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_RFKILL
+DRV_OBJS += src/drivers/rfkill.c
+endif
+
+ifdef NEED_RADIOTAP
+DRV_OBJS += src/utils/radiotap.c
+endif
+
+ifdef CONFIG_DRIVER_CUSTOM
+DRV_CFLAGS += -DCONFIG_DRIVER_CUSTOM
+endif
+
+ifdef CONFIG_FULL_DYNAMIC_VLAN
+NEED_LINUX_IOCTL=y
+ifdef CONFIG_VLAN_NETLINK
+NEED_LIBNL=y
+CONFIG_LIBNL3_ROUTE=y
+endif
+endif
+
+ifdef NEED_LINUX_IOCTL
+DRV_OBJS += src/drivers/linux_ioctl.c
+endif
+
+ifdef NEED_LIBNL
+ifdef CONFIG_LIBNL32
+ DRV_LIBS += -lnl-3
+ DRV_LIBS += -lnl-genl-3
+ DRV_CFLAGS += -I/usr/include/libnl3
+ifdef CONFIG_LIBNL3_ROUTE
+ DRV_LIBS += -lnl-route-3
+ DRV_CFLAGS += -DCONFIG_LIBNL3_ROUTE
+endif
+else
+ ifdef CONFIG_LIBNL_TINY
+ DRV_LIBS += -lnl-tiny
+ else
+ DRV_LIBS += -lnl
+ DRV_LIBS += -lnl-genl
+ endif
+endif
+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/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/linux_defines.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/linux_defines.h
new file mode 100644
index 0000000..a107479
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/linux_defines.h
@@ -0,0 +1,46 @@
+/*
+ * Linux defines for values that are not yet included in common C libraries
+ * Copyright (c) 2014, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef LINUX_DEFINES_H
+#define LINUX_DEFINES_H
+
+#ifndef SO_WIFI_STATUS
+# if defined(__sparc__)
+# define SO_WIFI_STATUS 0x0025
+# elif defined(__parisc__)
+# define SO_WIFI_STATUS 0x4022
+# else
+# define SO_WIFI_STATUS 41
+# endif
+
+# define SCM_WIFI_STATUS SO_WIFI_STATUS
+#endif
+
+#ifndef SO_EE_ORIGIN_TXSTATUS
+#define SO_EE_ORIGIN_TXSTATUS 4
+#endif
+
+#ifndef PACKET_TX_TIMESTAMP
+#define PACKET_TX_TIMESTAMP 16
+#endif
+
+#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
+
+#endif /* LINUX_DEFINES_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/linux_ioctl.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/linux_ioctl.c
new file mode 100644
index 0000000..7edb9df
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/linux_ioctl.c
@@ -0,0 +1,237 @@
+/*
+ * Linux ioctl helper functions for driver wrappers
+ * Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README 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 "common/linux_bridge.h"
+#include "linux_ioctl.h"
+
+
+int linux_set_iface_flags(int sock, const char *ifname, int dev_up)
+{
+ struct ifreq ifr;
+ int ret;
+
+ if (sock < 0)
+ return -1;
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+
+ if (ioctl(sock, SIOCGIFFLAGS, &ifr) != 0) {
+ ret = errno ? -errno : -999;
+ wpa_printf(MSG_ERROR, "Could not read interface %s flags: %s",
+ ifname, strerror(errno));
+ return ret;
+ }
+
+ 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) {
+ ret = errno ? -errno : -999;
+ wpa_printf(MSG_ERROR, "Could not set interface %s flags (%s): "
+ "%s",
+ ifname, dev_up ? "UP" : "DOWN", strerror(errno));
+ return ret;
+ }
+
+ return 0;
+}
+
+
+int linux_iface_up(int sock, const char *ifname)
+{
+ struct ifreq ifr;
+ int ret;
+
+ if (sock < 0)
+ return -1;
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+
+ if (ioctl(sock, SIOCGIFFLAGS, &ifr) != 0) {
+ ret = errno ? -errno : -999;
+ wpa_printf(MSG_ERROR, "Could not read interface %s flags: %s",
+ ifname, strerror(errno));
+ return ret;
+ }
+
+ return !!(ifr.ifr_flags & IFF_UP);
+}
+
+
+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;
+}
+
+
+int linux_br_add(int sock, const char *brname)
+{
+ if (ioctl(sock, SIOCBRADDBR, brname) < 0) {
+ int saved_errno = errno;
+
+ wpa_printf(MSG_DEBUG, "Could not add bridge %s: %s",
+ brname, strerror(errno));
+ errno = saved_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) {
+ int saved_errno = errno;
+
+ wpa_printf(MSG_DEBUG, "Could not add interface %s into bridge "
+ "%s: %s", ifname, brname, strerror(errno));
+ errno = saved_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;
+ ssize_t res;
+
+ os_snprintf(path, sizeof(path), "/sys/class/net/%s/brport/bridge",
+ ifname);
+ res = readlink(path, brlink, sizeof(brlink));
+ if (res < 0 || (size_t) res >= sizeof(brlink))
+ return -1;
+ brlink[res] = '\0';
+ pos = os_strrchr(brlink, '/');
+ if (pos == NULL)
+ return -1;
+ pos++;
+ os_strlcpy(brname, pos, IFNAMSIZ);
+ return 0;
+}
+
+
+int linux_master_get(char *master_ifname, const char *ifname)
+{
+ char buf[128], masterlink[128], *pos;
+ ssize_t res;
+
+ /* check whether there is a master */
+ os_snprintf(buf, sizeof(buf), "/sys/class/net/%s/master", ifname);
+
+ res = readlink(buf, masterlink, sizeof(masterlink));
+ if (res < 0 || (size_t) res >= sizeof(masterlink))
+ return -1;
+
+ masterlink[res] = '\0';
+
+ pos = os_strrchr(masterlink, '/');
+ if (pos == NULL)
+ return -1;
+ pos++;
+ os_strlcpy(master_ifname, pos, IFNAMSIZ);
+ return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/linux_ioctl.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/linux_ioctl.h
new file mode 100644
index 0000000..6de4d9b
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/linux_ioctl.h
@@ -0,0 +1,23 @@
+/*
+ * Linux ioctl helper functions for driver wrappers
+ * Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README 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_iface_up(int sock, const char *ifname);
+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);
+int linux_master_get(char *master_ifname, const char *ifname);
+
+#endif /* LINUX_IOCTL_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/linux_wext.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/linux_wext.h
new file mode 100644
index 0000000..e7c7001
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/linux_wext.h
@@ -0,0 +1,45 @@
+/*
+ * Driver interaction with generic Linux Wireless Extensions
+ * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef LINUX_WEXT_H
+#define LINUX_WEXT_H
+
+#ifndef ANDROID
+
+/*
+ * Avoid including other kernel header to avoid conflicts with C library
+ * headers.
+ */
+#define _LINUX_TYPES_H
+#define _LINUX_SOCKET_H
+#define _LINUX_IF_H
+
+#include <stdint.h>
+#include <net/if.h>
+typedef uint32_t __u32;
+typedef int32_t __s32;
+typedef uint16_t __u16;
+typedef int16_t __s16;
+typedef uint8_t __u8;
+#ifndef __user
+#define __user
+#endif /* __user */
+
+#endif /* ANDROID */
+
+#include <linux/wireless.h>
+
+#ifndef IW_ENCODE_ALG_PMK
+#define IW_ENCODE_ALG_PMK 4
+#endif
+
+#ifndef IW_ENC_CAPA_4WAY_HANDSHAKE
+#define IW_ENC_CAPA_4WAY_HANDSHAKE 0x00000010
+#endif
+
+#endif /* LINUX_WEXT_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/ndis_events.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/ndis_events.c
new file mode 100644
index 0000000..4d4ec81
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/ndis_events.c
@@ -0,0 +1,803 @@
+/*
+ * ndis_events - Receive NdisMIndicateStatus() events using WMI
+ * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README 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,
+ "Unexpected 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/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/netlink.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/netlink.c
new file mode 100644
index 0000000..7780479
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/netlink.c
@@ -0,0 +1,228 @@
+/*
+ * Netlink helper functions for driver wrappers
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README 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),
+ (u8 *) 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->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);
+
+ netlink->cfg = cfg;
+
+ 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);
+}
+
+
+static const char * linkmode_str(int mode)
+{
+ switch (mode) {
+ case -1:
+ return "no change";
+ case 0:
+ return "kernel-control";
+ case 1:
+ return "userspace-control";
+ default:
+ return "?";
+ }
+}
+
+
+static const char * operstate_str(int state)
+{
+ switch (state) {
+ case -1:
+ return "no change";
+ case IF_OPER_DORMANT:
+ return "IF_OPER_DORMANT";
+ case IF_OPER_UP:
+ return "IF_OPER_UP";
+ default:
+ return "?";
+ }
+}
+
+
+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 += RTA_SPACE(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 += RTA_SPACE(sizeof(char));
+ }
+
+ wpa_printf(MSG_DEBUG, "netlink: Operstate: ifindex=%d linkmode=%d (%s), operstate=%d (%s)",
+ ifindex, linkmode, linkmode_str(linkmode),
+ operstate, operstate_str(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/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/netlink.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/netlink.h
new file mode 100644
index 0000000..3a7340e
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/netlink.h
@@ -0,0 +1,28 @@
+/*
+ * Netlink helper functions for driver wrappers
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README 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/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/nl80211_copy.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/nl80211_copy.h
new file mode 100644
index 0000000..c59fec4
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/nl80211_copy.h
@@ -0,0 +1,7815 @@
+#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 <m@bues.ch>
+ * Copyright 2008, 2009 Luis R. Rodriguez <lrodriguez@atheros.com>
+ * Copyright 2008 Jouni Malinen <jouni.malinen@atheros.com>
+ * Copyright 2008 Colin McCabe <colin@cozybit.com>
+ * Copyright 2015-2017 Intel Deutschland GmbH
+ * Copyright (C) 2018-2022 Intel Corporation
+ *
+ * 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.
+ *
+ */
+
+/*
+ * This header file defines the userspace API to the wireless stack. Please
+ * be careful not to break things - i.e. don't move anything around or so
+ * unless you can demonstrate that it breaks neither API nor ABI.
+ *
+ * Additions to the API should be accompanied by actual implementations in
+ * an upstream driver, so that example implementations exist in case there
+ * are ever concerns about the precise semantics of the API or changes are
+ * needed, and to ensure that code for dead (no longer implemented) API
+ * can actually be identified and removed.
+ * Nonetheless, semantics should also be documented carefully in this file.
+ */
+
+#include <linux/types.h>
+
+#define NL80211_GENL_NAME "nl80211"
+
+#define NL80211_MULTICAST_GROUP_CONFIG "config"
+#define NL80211_MULTICAST_GROUP_SCAN "scan"
+#define NL80211_MULTICAST_GROUP_REG "regulatory"
+#define NL80211_MULTICAST_GROUP_MLME "mlme"
+#define NL80211_MULTICAST_GROUP_VENDOR "vendor"
+#define NL80211_MULTICAST_GROUP_NAN "nan"
+#define NL80211_MULTICAST_GROUP_TESTMODE "testmode"
+
+#define NL80211_EDMG_BW_CONFIG_MIN 4
+#define NL80211_EDMG_BW_CONFIG_MAX 15
+#define NL80211_EDMG_CHANNELS_MIN 1
+#define NL80211_EDMG_CHANNELS_MAX 0x3c /* 0b00111100 */
+
+/**
+ * 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.
+ *
+ * Station handling varies per interface type and depending on the driver's
+ * capabilities.
+ *
+ * For drivers supporting TDLS with external setup (WIPHY_FLAG_SUPPORTS_TDLS
+ * and WIPHY_FLAG_TDLS_EXTERNAL_SETUP), the station lifetime is as follows:
+ * - a setup station entry is added, not yet authorized, without any rate
+ * or capability information, this just exists to avoid race conditions
+ * - when the TDLS setup is done, a single NL80211_CMD_SET_STATION is valid
+ * to add rate and capability information to the station and at the same
+ * time mark it authorized.
+ * - %NL80211_TDLS_ENABLE_LINK is then used
+ * - after this, the only valid operation is to remove it by tearing down
+ * the TDLS link (%NL80211_TDLS_DISABLE_LINK)
+ *
+ * TODO: need more info for other interface types
+ */
+
+/**
+ * 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.
+ */
+
+/**
+ * DOC: Virtual interface / concurrency capabilities
+ *
+ * Some devices are able to operate with virtual MACs, they can have
+ * more than one virtual interface. The capability handling for this
+ * is a bit complex though, as there may be a number of restrictions
+ * on the types of concurrency that are supported.
+ *
+ * To start with, each device supports the interface types listed in
+ * the %NL80211_ATTR_SUPPORTED_IFTYPES attribute, but by listing the
+ * types there no concurrency is implied.
+ *
+ * Once concurrency is desired, more attributes must be observed:
+ * To start with, since some interface types are purely managed in
+ * software, like the AP-VLAN type in mac80211 for example, there's
+ * an additional list of these, they can be added at any time and
+ * are only restricted by some semantic restrictions (e.g. AP-VLAN
+ * cannot be added without a corresponding AP interface). This list
+ * is exported in the %NL80211_ATTR_SOFTWARE_IFTYPES attribute.
+ *
+ * Further, the list of supported combinations is exported. This is
+ * in the %NL80211_ATTR_INTERFACE_COMBINATIONS attribute. Basically,
+ * it exports a list of "groups", and at any point in time the
+ * interfaces that are currently active must fall into any one of
+ * the advertised groups. Within each group, there are restrictions
+ * on the number of interfaces of different types that are supported
+ * and also the number of different channels, along with potentially
+ * some other restrictions. See &enum nl80211_if_combination_attrs.
+ *
+ * All together, these attributes define the concurrency of virtual
+ * interfaces that a given device supports.
+ */
+
+/**
+ * DOC: packet coalesce support
+ *
+ * In most cases, host that receives IPv4 and IPv6 multicast/broadcast
+ * packets does not do anything with these packets. Therefore the
+ * reception of these unwanted packets causes unnecessary processing
+ * and power consumption.
+ *
+ * Packet coalesce feature helps to reduce number of received interrupts
+ * to host by buffering these packets in firmware/hardware for some
+ * predefined time. Received interrupt will be generated when one of the
+ * following events occur.
+ * a) Expiration of hardware timer whose expiration time is set to maximum
+ * coalescing delay of matching coalesce rule.
+ * b) Coalescing buffer in hardware reaches it's limit.
+ * c) Packet doesn't match any of the configured coalesce rules.
+ *
+ * User needs to configure following parameters for creating a coalesce
+ * rule.
+ * a) Maximum coalescing delay
+ * b) List of packet patterns which needs to be matched
+ * c) Condition for coalescence. pattern 'match' or 'no match'
+ * Multiple such rules can be created.
+ */
+
+/**
+ * DOC: WPA/WPA2 EAPOL handshake offload
+ *
+ * By setting @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK flag drivers
+ * can indicate they support offloading EAPOL handshakes for WPA/WPA2
+ * preshared key authentication in station mode. In %NL80211_CMD_CONNECT
+ * the preshared key should be specified using %NL80211_ATTR_PMK. Drivers
+ * supporting this offload may reject the %NL80211_CMD_CONNECT when no
+ * preshared key material is provided, for example when that driver does
+ * not support setting the temporal keys through %NL80211_CMD_NEW_KEY.
+ *
+ * Similarly @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X flag can be
+ * set by drivers indicating offload support of the PTK/GTK EAPOL
+ * handshakes during 802.1X authentication in station mode. In order to
+ * use the offload the %NL80211_CMD_CONNECT should have
+ * %NL80211_ATTR_WANT_1X_4WAY_HS attribute flag. Drivers supporting this
+ * offload may reject the %NL80211_CMD_CONNECT when the attribute flag is
+ * not present.
+ *
+ * By setting @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_AP_PSK flag drivers
+ * can indicate they support offloading EAPOL handshakes for WPA/WPA2
+ * preshared key authentication in AP mode. In %NL80211_CMD_START_AP
+ * the preshared key should be specified using %NL80211_ATTR_PMK. Drivers
+ * supporting this offload may reject the %NL80211_CMD_START_AP when no
+ * preshared key material is provided, for example when that driver does
+ * not support setting the temporal keys through %NL80211_CMD_NEW_KEY.
+ *
+ * For 802.1X the PMK or PMK-R0 are set by providing %NL80211_ATTR_PMK
+ * using %NL80211_CMD_SET_PMK. For offloaded FT support also
+ * %NL80211_ATTR_PMKR0_NAME must be provided.
+ */
+
+/**
+ * DOC: FILS shared key authentication offload
+ *
+ * FILS shared key authentication offload can be advertized by drivers by
+ * setting @NL80211_EXT_FEATURE_FILS_SK_OFFLOAD flag. The drivers that support
+ * FILS shared key authentication offload should be able to construct the
+ * authentication and association frames for FILS shared key authentication and
+ * eventually do a key derivation as per IEEE 802.11ai. The below additional
+ * parameters should be given to driver in %NL80211_CMD_CONNECT and/or in
+ * %NL80211_CMD_UPDATE_CONNECT_PARAMS.
+ * %NL80211_ATTR_FILS_ERP_USERNAME - used to construct keyname_nai
+ * %NL80211_ATTR_FILS_ERP_REALM - used to construct keyname_nai
+ * %NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM - used to construct erp message
+ * %NL80211_ATTR_FILS_ERP_RRK - used to generate the rIK and rMSK
+ * rIK should be used to generate an authentication tag on the ERP message and
+ * rMSK should be used to derive a PMKSA.
+ * rIK, rMSK should be generated and keyname_nai, sequence number should be used
+ * as specified in IETF RFC 6696.
+ *
+ * When FILS shared key authentication is completed, driver needs to provide the
+ * below additional parameters to userspace, which can be either after setting
+ * up a connection or after roaming.
+ * %NL80211_ATTR_FILS_KEK - used for key renewal
+ * %NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM - used in further EAP-RP exchanges
+ * %NL80211_ATTR_PMKID - used to identify the PMKSA used/generated
+ * %Nl80211_ATTR_PMK - used to update PMKSA cache in userspace
+ * The PMKSA can be maintained in userspace persistently so that it can be used
+ * later after reboots or wifi turn off/on also.
+ *
+ * %NL80211_ATTR_FILS_CACHE_ID is the cache identifier advertized by a FILS
+ * capable AP supporting PMK caching. It specifies the scope within which the
+ * PMKSAs are cached in an ESS. %NL80211_CMD_SET_PMKSA and
+ * %NL80211_CMD_DEL_PMKSA are enhanced to allow support for PMKSA caching based
+ * on FILS cache identifier. Additionally %NL80211_ATTR_PMK is used with
+ * %NL80211_SET_PMKSA to specify the PMK corresponding to a PMKSA for driver to
+ * use in a FILS shared key connection with PMKSA caching.
+ */
+
+/**
+ * DOC: SAE authentication offload
+ *
+ * By setting @NL80211_EXT_FEATURE_SAE_OFFLOAD flag drivers can indicate they
+ * support offloading SAE authentication for WPA3-Personal networks in station
+ * mode. Similarly @NL80211_EXT_FEATURE_SAE_OFFLOAD_AP flag can be set by
+ * drivers indicating the offload support in AP mode.
+ *
+ * The password for SAE should be specified using %NL80211_ATTR_SAE_PASSWORD in
+ * %NL80211_CMD_CONNECT and %NL80211_CMD_START_AP for station and AP mode
+ * respectively.
+ */
+
+/**
+ * DOC: VLAN offload support for setting group keys and binding STAs to VLANs
+ *
+ * By setting @NL80211_EXT_FEATURE_VLAN_OFFLOAD flag drivers can indicate they
+ * support offloading VLAN functionality in a manner where the driver exposes a
+ * single netdev that uses VLAN tagged frames and separate VLAN-specific netdevs
+ * can then be added using RTM_NEWLINK/IFLA_VLAN_ID similarly to the Ethernet
+ * case. Frames received from stations that are not assigned to any VLAN are
+ * delivered on the main netdev and frames to such stations can be sent through
+ * that main netdev.
+ *
+ * %NL80211_CMD_NEW_KEY (for group keys), %NL80211_CMD_NEW_STATION, and
+ * %NL80211_CMD_SET_STATION will optionally specify vlan_id using
+ * %NL80211_ATTR_VLAN_ID.
+ */
+
+/**
+ * DOC: TID configuration
+ *
+ * TID config support can be checked in the %NL80211_ATTR_TID_CONFIG
+ * attribute given in wiphy capabilities.
+ *
+ * The necessary configuration parameters are mentioned in
+ * &enum nl80211_tid_config_attr and it will be passed to the
+ * %NL80211_CMD_SET_TID_CONFIG command in %NL80211_ATTR_TID_CONFIG.
+ *
+ * If the configuration needs to be applied for specific peer then the MAC
+ * address of the peer needs to be passed in %NL80211_ATTR_MAC, otherwise the
+ * configuration will be applied for all the connected peers in the vif except
+ * any peers that have peer specific configuration for the TID by default; if
+ * the %NL80211_TID_CONFIG_ATTR_OVERRIDE flag is set, peer specific values
+ * will be overwritten.
+ *
+ * All this configuration is valid only for STA's current connection
+ * i.e. the configuration will be reset to default when the STA connects back
+ * after disconnection/roaming, and this configuration will be cleared when
+ * the interface goes down.
+ */
+
+/**
+ * DOC: FILS shared key crypto offload
+ *
+ * This feature is applicable to drivers running in AP mode.
+ *
+ * FILS shared key crypto offload can be advertised by drivers by setting
+ * @NL80211_EXT_FEATURE_FILS_CRYPTO_OFFLOAD flag. The drivers that support
+ * FILS shared key crypto offload should be able to encrypt and decrypt
+ * association frames for FILS shared key authentication as per IEEE 802.11ai.
+ * With this capability, for FILS key derivation, drivers depend on userspace.
+ *
+ * After FILS key derivation, userspace shares the FILS AAD details with the
+ * driver and the driver stores the same to use in decryption of association
+ * request and in encryption of association response. The below parameters
+ * should be given to the driver in %NL80211_CMD_SET_FILS_AAD.
+ * %NL80211_ATTR_MAC - STA MAC address, used for storing FILS AAD per STA
+ * %NL80211_ATTR_FILS_KEK - Used for encryption or decryption
+ * %NL80211_ATTR_FILS_NONCES - Used for encryption or decryption
+ * (STA Nonce 16 bytes followed by AP Nonce 16 bytes)
+ *
+ * Once the association is done, the driver cleans the FILS AAD data.
+ */
+
+/**
+ * DOC: Multi-Link Operation
+ *
+ * In Multi-Link Operation, a connection between to MLDs utilizes multiple
+ * links. To use this in nl80211, various commands and responses now need
+ * to or will include the new %NL80211_ATTR_MLO_LINKS attribute.
+ * Additionally, various commands that need to operate on a specific link
+ * now need to be given the %NL80211_ATTR_MLO_LINK_ID attribute, e.g. to
+ * use %NL80211_CMD_START_AP or similar functions.
+ */
+
+/**
+ * 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_FREQ_OFFSET (and the attributes determining the
+ * channel width; this is used for setting monitor mode channel),
+ * %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 for all interfaces or a specific get with a
+ * single %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. If multiple BSSID advertisements are
+ * enabled using %NL80211_ATTR_MBSSID_CONFIG, %NL80211_ATTR_MBSSID_ELEMS,
+ * and if this command is used for the transmitting interface, then all
+ * the non-transmitting interfaces are deleted as well.
+ *
+ * @NL80211_CMD_GET_KEY: Get sequence counter information for a key specified
+ * by %NL80211_ATTR_KEY_IDX and/or %NL80211_ATTR_MAC. %NL80211_ATTR_MAC
+ * represents peer's MLD address for MLO pairwise key. For MLO group key,
+ * the link is identified by %NL80211_ATTR_MLO_LINK_ID.
+ * @NL80211_CMD_SET_KEY: Set key attributes %NL80211_ATTR_KEY_DEFAULT,
+ * %NL80211_ATTR_KEY_DEFAULT_MGMT, or %NL80211_ATTR_KEY_THRESHOLD.
+ * For MLO connection, the link to set default key is identified by
+ * %NL80211_ATTR_MLO_LINK_ID.
+ * @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_ATTR_MAC represents
+ * peer's MLD address for MLO pairwise key. The link to add MLO
+ * group key is identified by %NL80211_ATTR_MLO_LINK_ID.
+ * @NL80211_CMD_DEL_KEY: delete a key identified by %NL80211_ATTR_KEY_IDX
+ * or %NL80211_ATTR_MAC. %NL80211_ATTR_MAC represents peer's MLD address
+ * for MLO pairwise key. The link to delete group key is identified by
+ * %NL80211_ATTR_MLO_LINK_ID.
+ *
+ * @NL80211_CMD_GET_BEACON: (not used)
+ * @NL80211_CMD_SET_BEACON: change the beacon on an access point interface
+ * using the %NL80211_ATTR_BEACON_HEAD and %NL80211_ATTR_BEACON_TAIL
+ * attributes. For drivers that generate the beacon and probe responses
+ * internally, the following attributes must be provided: %NL80211_ATTR_IE,
+ * %NL80211_ATTR_IE_PROBE_RESP and %NL80211_ATTR_IE_ASSOC_RESP.
+ * @NL80211_CMD_START_AP: Start AP operation on an AP interface, parameters
+ * are like for %NL80211_CMD_SET_BEACON, and additionally parameters that
+ * do not change are used, these include %NL80211_ATTR_BEACON_INTERVAL,
+ * %NL80211_ATTR_DTIM_PERIOD, %NL80211_ATTR_SSID,
+ * %NL80211_ATTR_HIDDEN_SSID, %NL80211_ATTR_CIPHERS_PAIRWISE,
+ * %NL80211_ATTR_CIPHER_GROUP, %NL80211_ATTR_WPA_VERSIONS,
+ * %NL80211_ATTR_AKM_SUITES, %NL80211_ATTR_PRIVACY,
+ * %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_INACTIVITY_TIMEOUT,
+ * %NL80211_ATTR_ACL_POLICY and %NL80211_ATTR_MAC_ADDRS.
+ * The channel to use can be set on the interface or be given using the
+ * %NL80211_ATTR_WIPHY_FREQ and %NL80211_ATTR_WIPHY_FREQ_OFFSET, and the
+ * attributes determining channel width.
+ * @NL80211_CMD_NEW_BEACON: old alias for %NL80211_CMD_START_AP
+ * @NL80211_CMD_STOP_AP: Stop AP operation on the given interface
+ * @NL80211_CMD_DEL_BEACON: old alias for %NL80211_CMD_STOP_AP
+ *
+ * @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
+ * 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. For MLD station, MLD address is used in
+ * %NL80211_ATTR_MAC. %NL80211_ATTR_MGMT_SUBTYPE and
+ * %NL80211_ATTR_REASON_CODE can optionally be used to specify which type
+ * of disconnection indication should be sent to the station
+ * (Deauthentication or Disassociation frame and reason code for that
+ * frame).
+ *
+ * @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_MPATH: Create a new mesh path for the destination given by
+ * %NL80211_ATTR_MAC via %NL80211_ATTR_MPATH_NEXT_HOP.
+ * @NL80211_CMD_DEL_MPATH: Delete a mesh path to the destination given by
+ * %NL80211_ATTR_MAC.
+ * @NL80211_CMD_NEW_PATH: Add a mesh path with given attributes to 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. If %NL80211_ATTR_WIPHY is specified and the device
+ * has a private regulatory domain, it will be returned. Otherwise, the
+ * global regdomain will be returned.
+ * A device will have a private regulatory domain if it uses the
+ * regulatory_hint() API. Even when a private regdomain is used the channel
+ * information will still be mended according to further hints from
+ * the regulatory core to help with compliance. A dump version of this API
+ * is now available which will returns the global regdomain as well as
+ * all private regdomains of present wiphys (for those that have it).
+ * If a wiphy is self-managed (%NL80211_ATTR_WIPHY_SELF_MANAGED_REG), then
+ * its private regdomain is the only valid one for it. The regulatory
+ * core is not used to help with compliance in this case.
+ * @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_CONFIG: Get mesh networking properties for the
+ * interface identified by %NL80211_ATTR_IFINDEX
+ *
+ * @NL80211_CMD_SET_MESH_CONFIG: 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_ATTR_TX_NO_CCK_RATE is used to decide whether to send the
+ * probe requests at CCK rate or not. %NL80211_ATTR_BSSID can be used to
+ * specify a BSSID to scan for; if not included, the wildcard BSSID will
+ * be used.
+ * @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_START_SCHED_SCAN: start a scheduled scan at certain
+ * intervals and certain number of cycles, as specified by
+ * %NL80211_ATTR_SCHED_SCAN_PLANS. If %NL80211_ATTR_SCHED_SCAN_PLANS is
+ * not specified and only %NL80211_ATTR_SCHED_SCAN_INTERVAL is specified,
+ * scheduled scan will run in an infinite loop with the specified interval.
+ * These attributes are mutually exculsive,
+ * i.e. NL80211_ATTR_SCHED_SCAN_INTERVAL must not be passed if
+ * NL80211_ATTR_SCHED_SCAN_PLANS is defined.
+ * If for some reason scheduled scan is aborted by the driver, all scan
+ * plans are canceled (including scan plans that did not start yet).
+ * Like with normal scans, if SSIDs (%NL80211_ATTR_SCAN_SSIDS)
+ * are passed, they are used in the probe requests. For
+ * broadcast, a broadcast SSID must be passed (ie. an empty
+ * string). If no SSID is passed, no probe requests are sent and
+ * a passive scan is performed. %NL80211_ATTR_SCAN_FREQUENCIES,
+ * if passed, define which channels should be scanned; if not
+ * passed, all channels allowed for the current regulatory domain
+ * are used. Extra IEs can also be passed from the userspace by
+ * using the %NL80211_ATTR_IE attribute. The first cycle of the
+ * scheduled scan can be delayed by %NL80211_ATTR_SCHED_SCAN_DELAY
+ * is supplied. If the device supports multiple concurrent scheduled
+ * scans, it will allow such when the caller provides the flag attribute
+ * %NL80211_ATTR_SCHED_SCAN_MULTI to indicate user-space support for it.
+ * @NL80211_CMD_STOP_SCHED_SCAN: stop a scheduled scan. Returns -ENOENT if
+ * scheduled scan is not running. The caller may assume that as soon
+ * as the call returns, it is safe to start a new scheduled scan again.
+ * @NL80211_CMD_SCHED_SCAN_RESULTS: indicates that there are scheduled scan
+ * results available.
+ * @NL80211_CMD_SCHED_SCAN_STOPPED: indicates that the scheduled scan has
+ * stopped. The driver may issue this event at any time during a
+ * scheduled scan. One reason for stopping the scan is if the hardware
+ * does not support starting an association or a normal scan while running
+ * a scheduled scan. This event is also sent when the
+ * %NL80211_CMD_STOP_SCHED_SCAN command is received or when the interface
+ * is brought down while a scheduled scan was running.
+ *
+ * @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_SET_PMKSA: Add a PMKSA cache entry using %NL80211_ATTR_MAC
+ * (for the BSSID), %NL80211_ATTR_PMKID, and optionally %NL80211_ATTR_PMK
+ * (PMK is used for PTKSA derivation in case of FILS shared key offload) or
+ * using %NL80211_ATTR_SSID, %NL80211_ATTR_FILS_CACHE_ID,
+ * %NL80211_ATTR_PMKID, and %NL80211_ATTR_PMK in case of FILS
+ * authentication where %NL80211_ATTR_FILS_CACHE_ID is the identifier
+ * advertized by a FILS capable AP identifying the scope of PMKSA in an
+ * ESS.
+ * @NL80211_CMD_DEL_PMKSA: Delete a PMKSA cache entry, using %NL80211_ATTR_MAC
+ * (for the BSSID) and %NL80211_ATTR_PMKID or using %NL80211_ATTR_SSID,
+ * %NL80211_ATTR_FILS_CACHE_ID, and %NL80211_ATTR_PMKID in case of FILS
+ * authentication.
+ * @NL80211_CMD_FLUSH_PMKSA: Flush all PMKSA cache entries.
+ *
+ * @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 +
+ * %NL80211_ATTR_WIPHY_FREQ_OFFSET 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). The
+ * %NL80211_ATTR_PREV_BSSID attribute is used to specify whether the
+ * request is for the initial association to an ESS (that attribute not
+ * included) or for reassociation within the ESS (that attribute is
+ * included).
+ * @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_USE_MFP, %NL80211_ATTR_MAC, %NL80211_ATTR_WIPHY_FREQ,
+ * %NL80211_ATTR_WIPHY_FREQ_OFFSET, %NL80211_ATTR_CONTROL_PORT,
+ * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE,
+ * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT,
+ * %NL80211_ATTR_CONTROL_PORT_OVER_NL80211, %NL80211_ATTR_MAC_HINT, and
+ * %NL80211_ATTR_WIPHY_FREQ_HINT.
+ * If included, %NL80211_ATTR_MAC and %NL80211_ATTR_WIPHY_FREQ are
+ * restrictions on BSS selection, i.e., they effectively prevent roaming
+ * within the ESS. %NL80211_ATTR_MAC_HINT and %NL80211_ATTR_WIPHY_FREQ_HINT
+ * can be included to provide a recommendation of the initial BSS while
+ * allowing the driver to roam to other BSSes within the ESS and also to
+ * ignore this recommendation if the indicated BSS is not ideal. Only one
+ * set of BSSID,frequency parameters is used (i.e., either the enforcing
+ * %NL80211_ATTR_MAC,%NL80211_ATTR_WIPHY_FREQ or the less strict
+ * %NL80211_ATTR_MAC_HINT and %NL80211_ATTR_WIPHY_FREQ_HINT).
+ * Driver shall not modify the IEs specified through %NL80211_ATTR_IE if
+ * %NL80211_ATTR_MAC is included. However, if %NL80211_ATTR_MAC_HINT is
+ * included, these IEs through %NL80211_ATTR_IE are specified by the user
+ * space based on the best possible BSS selected. Thus, if the driver ends
+ * up selecting a different BSS, it can modify these IEs accordingly (e.g.
+ * userspace asks the driver to perform PMKSA caching with BSS1 and the
+ * driver ends up selecting BSS2 with different PMKSA cache entry; RSNIE
+ * has to get updated with the apt PMKID).
+ * %NL80211_ATTR_PREV_BSSID can be used to request a reassociation within
+ * the ESS in case the device is already associated and an association with
+ * a different BSS is desired.
+ * Background scan period can optionally be
+ * specified in %NL80211_ATTR_BG_SCAN_PERIOD,
+ * if not specified default background scan configuration
+ * in driver is used and if period value is 0, bg scan will be disabled.
+ * This attribute is ignored if driver does not support roam scan.
+ * 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 %NL80211_ATTR_STATUS_CODE attribute (0 = success,
+ * non-zero = failure). If %NL80211_ATTR_TIMED_OUT is included in the
+ * event, the connection attempt failed due to not being able to initiate
+ * authentication/association or not receiving a response from the AP.
+ * Non-zero %NL80211_ATTR_STATUS_CODE value is indicated in that case as
+ * well to remain backwards compatible.
+ * @NL80211_CMD_ROAM: Notification indicating the card/driver roamed by itself.
+ * When a security association was established on an 802.1X network using
+ * fast transition, this event should be followed by an
+ * %NL80211_CMD_PORT_AUTHORIZED event.
+ * Following a %NL80211_CMD_ROAM event userspace can issue
+ * %NL80211_CMD_GET_SCAN in order to obtain the scan information for the
+ * new BSS the card/driver roamed to.
+ * @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.
+ * %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.
+ * The %NL80211_ATTR_RECEIVE_MULTICAST flag attribute can be given if
+ * %NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS is available, in which
+ * case the registration can also be modified to include/exclude the
+ * flag, rather than requiring unregistration to change it.
+ * @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 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_ATTR_TX_NO_CCK_RATE is used to decide whether to send the
+ * management frames at CCK rate or not in 2GHz band.
+ * %NL80211_ATTR_CSA_C_OFFSETS_TX is an array of offsets to CSA
+ * counters which will be updated to the current value. This attribute
+ * is used during CSA period.
+ * For TX on an MLD, the frequency can be omitted and the link ID be
+ * specified, or if transmitting to a known peer MLD (with MLD addresses
+ * in the frame) both can be omitted and the link will be selected by
+ * lower layers.
+ * For RX notification, %NL80211_ATTR_RX_HW_TIMESTAMP may be included to
+ * indicate the frame RX timestamp and %NL80211_ATTR_TX_HW_TIMESTAMP may
+ * be included to indicate the ack TX timestamp.
+ * @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. This command is
+ * also sent as an event whenever the driver has completed the off-channel
+ * wait time.
+ * @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_ATTR_TX_HW_TIMESTAMP may be included to indicate the
+ * tx timestamp and %NL80211_ATTR_RX_HW_TIMESTAMP may be included to
+ * indicate the ack RX timestamp.
+ * @NL80211_CMD_ACTION_TX_STATUS: Alias for @NL80211_CMD_FRAME_TX_STATUS for
+ * backward compatibility.
+ *
+ * @NL80211_CMD_SET_POWER_SAVE: Set powersave, using %NL80211_ATTR_PS_STATE
+ * @NL80211_CMD_GET_POWER_SAVE: Get powersave status in %NL80211_ATTR_PS_STATE
+ *
+ * @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 the attributes determining channel width) 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
+ * (no longer supported).
+ *
+ * @NL80211_CMD_SET_MULTICAST_TO_UNICAST: Configure if this AP should perform
+ * multicast to unicast conversion. When enabled, all multicast packets
+ * with ethertype ARP, IPv4 or IPv6 (possibly within an 802.1Q header)
+ * will be sent out to each station once with the destination (multicast)
+ * MAC address replaced by the station's MAC address. Note that this may
+ * break certain expectations of the receiver, e.g. the ability to drop
+ * unicast IP packets encapsulated in multicast L2 frames, or the ability
+ * to not send destination unreachable messages in such cases.
+ * This can only be toggled per BSS. Configure this on an interface of
+ * type %NL80211_IFTYPE_AP. It applies to all its VLAN interfaces
+ * (%NL80211_IFTYPE_AP_VLAN), except for those in 4addr (WDS) mode.
+ * If %NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED is not present with this
+ * command, the feature is disabled.
+ *
+ * @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_NEW_PEER_CANDIDATE: Notification on the reception of a
+ * beacon or probe response from a compatible mesh peer. This is only
+ * sent while no station information (sta_info) exists for the new peer
+ * candidate and when @NL80211_MESH_SETUP_USERSPACE_AUTH,
+ * @NL80211_MESH_SETUP_USERSPACE_AMPE, or
+ * @NL80211_MESH_SETUP_USERSPACE_MPM is set. On reception of this
+ * notification, userspace may decide to create a new station
+ * (@NL80211_CMD_NEW_STATION). To stop this notification from
+ * reoccurring, the userspace authentication daemon may want to create the
+ * new station with the AUTHENTICATED flag unset and maybe change it later
+ * depending on the authentication result.
+ *
+ * @NL80211_CMD_GET_WOWLAN: get Wake-on-Wireless-LAN (WoWLAN) settings.
+ * @NL80211_CMD_SET_WOWLAN: set Wake-on-Wireless-LAN (WoWLAN) settings.
+ * Since wireless is more complex than wired ethernet, it supports
+ * various triggers. These triggers can be configured through this
+ * command with the %NL80211_ATTR_WOWLAN_TRIGGERS attribute. For
+ * more background information, see
+ * https://wireless.wiki.kernel.org/en/users/Documentation/WoWLAN.
+ * The @NL80211_CMD_SET_WOWLAN command can also be used as a notification
+ * from the driver reporting the wakeup reason. In this case, the
+ * @NL80211_ATTR_WOWLAN_TRIGGERS attribute will contain the reason
+ * for the wakeup, if it was caused by wireless. If it is not present
+ * in the wakeup notification, the wireless device didn't cause the
+ * wakeup but reports that it was woken up.
+ *
+ * @NL80211_CMD_SET_REKEY_OFFLOAD: This command is used give the driver
+ * the necessary information for supporting GTK rekey offload. This
+ * feature is typically used during WoWLAN. The configuration data
+ * is contained in %NL80211_ATTR_REKEY_DATA (which is nested and
+ * contains the data in sub-attributes). After rekeying happened,
+ * this command may also be sent by the driver as an MLME event to
+ * inform userspace of the new replay counter.
+ *
+ * @NL80211_CMD_PMKSA_CANDIDATE: This is used as an event to inform userspace
+ * of PMKSA caching dandidates.
+ *
+ * @NL80211_CMD_TDLS_OPER: Perform a high-level TDLS command (e.g. link setup).
+ * In addition, this can be used as an event to request userspace to take
+ * actions on TDLS links (set up a new link or tear down an existing one).
+ * In such events, %NL80211_ATTR_TDLS_OPERATION indicates the requested
+ * operation, %NL80211_ATTR_MAC contains the peer MAC address, and
+ * %NL80211_ATTR_REASON_CODE the reason code to be used (only with
+ * %NL80211_TDLS_TEARDOWN).
+ * @NL80211_CMD_TDLS_MGMT: Send a TDLS management frame. The
+ * %NL80211_ATTR_TDLS_ACTION attribute determines the type of frame to be
+ * sent. Public Action codes (802.11-2012 8.1.5.1) will be sent as
+ * 802.11 management frames, while TDLS action codes (802.11-2012
+ * 8.5.13.1) will be encapsulated and sent as data frames. The currently
+ * supported Public Action code is %WLAN_PUB_ACTION_TDLS_DISCOVER_RES
+ * and the currently supported TDLS actions codes are given in
+ * &enum ieee80211_tdls_actioncode.
+ *
+ * @NL80211_CMD_UNEXPECTED_FRAME: Used by an application controlling an AP
+ * (or GO) interface (i.e. hostapd) to ask for unexpected frames to
+ * implement sending deauth to stations that send unexpected class 3
+ * frames. Also used as the event sent by the kernel when such a frame
+ * is received.
+ * For the event, the %NL80211_ATTR_MAC attribute carries the TA and
+ * other attributes like the interface index are present.
+ * If used as the command it must have an interface index and you can
+ * only unsubscribe from the event by closing the socket. Subscription
+ * is also for %NL80211_CMD_UNEXPECTED_4ADDR_FRAME events.
+ *
+ * @NL80211_CMD_UNEXPECTED_4ADDR_FRAME: Sent as an event indicating that the
+ * associated station identified by %NL80211_ATTR_MAC sent a 4addr frame
+ * and wasn't already in a 4-addr VLAN. The event will be sent similarly
+ * to the %NL80211_CMD_UNEXPECTED_FRAME event, to the same listener.
+ *
+ * @NL80211_CMD_PROBE_CLIENT: Probe an associated station on an AP interface
+ * by sending a null data frame to it and reporting when the frame is
+ * acknowleged. This is used to allow timing out inactive clients. Uses
+ * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_MAC. The command returns a
+ * direct reply with an %NL80211_ATTR_COOKIE that is later used to match
+ * up the event with the request. The event includes the same data and
+ * has %NL80211_ATTR_ACK set if the frame was ACKed.
+ *
+ * @NL80211_CMD_REGISTER_BEACONS: Register this socket to receive beacons from
+ * other BSSes when any interfaces are in AP mode. This helps implement
+ * OLBC handling in hostapd. Beacons are reported in %NL80211_CMD_FRAME
+ * messages. Note that per PHY only one application may register.
+ *
+ * @NL80211_CMD_SET_NOACK_MAP: sets a bitmap for the individual TIDs whether
+ * No Acknowledgement Policy should be applied.
+ *
+ * @NL80211_CMD_CH_SWITCH_NOTIFY: An AP or GO may decide to switch channels
+ * independently of the userspace SME, send this event indicating
+ * %NL80211_ATTR_IFINDEX is now on %NL80211_ATTR_WIPHY_FREQ and the
+ * attributes determining channel width. This indication may also be
+ * sent when a remotely-initiated switch (e.g., when a STA receives a CSA
+ * from the remote AP) is completed;
+ *
+ * @NL80211_CMD_CH_SWITCH_STARTED_NOTIFY: Notify that a channel switch
+ * has been started on an interface, regardless of the initiator
+ * (ie. whether it was requested from a remote device or
+ * initiated on our own). It indicates that
+ * %NL80211_ATTR_IFINDEX will be on %NL80211_ATTR_WIPHY_FREQ
+ * after %NL80211_ATTR_CH_SWITCH_COUNT TBTT's. The userspace may
+ * decide to react to this indication by requesting other
+ * interfaces to change channel as well.
+ *
+ * @NL80211_CMD_START_P2P_DEVICE: Start the given P2P Device, identified by
+ * its %NL80211_ATTR_WDEV identifier. It must have been created with
+ * %NL80211_CMD_NEW_INTERFACE previously. After it has been started, the
+ * P2P Device can be used for P2P operations, e.g. remain-on-channel and
+ * public action frame TX.
+ * @NL80211_CMD_STOP_P2P_DEVICE: Stop the given P2P Device, identified by
+ * its %NL80211_ATTR_WDEV identifier.
+ *
+ * @NL80211_CMD_CONN_FAILED: connection request to an AP failed; used to
+ * notify userspace that AP has rejected the connection request from a
+ * station, due to particular reason. %NL80211_ATTR_CONN_FAILED_REASON
+ * is used for this.
+ *
+ * @NL80211_CMD_SET_MCAST_RATE: Change the rate used to send multicast frames
+ * for IBSS or MESH vif.
+ *
+ * @NL80211_CMD_SET_MAC_ACL: sets ACL for MAC address based access control.
+ * This is to be used with the drivers advertising the support of MAC
+ * address based access control. List of MAC addresses is passed in
+ * %NL80211_ATTR_MAC_ADDRS and ACL policy is passed in
+ * %NL80211_ATTR_ACL_POLICY. Driver will enable ACL with this list, if it
+ * is not already done. The new list will replace any existing list. Driver
+ * will clear its ACL when the list of MAC addresses passed is empty. This
+ * command is used in AP/P2P GO mode. Driver has to make sure to clear its
+ * ACL list during %NL80211_CMD_STOP_AP.
+ *
+ * @NL80211_CMD_RADAR_DETECT: Start a Channel availability check (CAC). Once
+ * a radar is detected or the channel availability scan (CAC) has finished
+ * or was aborted, or a radar was detected, usermode will be notified with
+ * this event. This command is also used to notify userspace about radars
+ * while operating on this channel.
+ * %NL80211_ATTR_RADAR_EVENT is used to inform about the type of the
+ * event.
+ *
+ * @NL80211_CMD_GET_PROTOCOL_FEATURES: Get global nl80211 protocol features,
+ * i.e. features for the nl80211 protocol rather than device features.
+ * Returns the features in the %NL80211_ATTR_PROTOCOL_FEATURES bitmap.
+ *
+ * @NL80211_CMD_UPDATE_FT_IES: Pass down the most up-to-date Fast Transition
+ * Information Element to the WLAN driver
+ *
+ * @NL80211_CMD_FT_EVENT: Send a Fast transition event from the WLAN driver
+ * to the supplicant. This will carry the target AP's MAC address along
+ * with the relevant Information Elements. This event is used to report
+ * received FT IEs (MDIE, FTIE, RSN IE, TIE, RICIE).
+ *
+ * @NL80211_CMD_CRIT_PROTOCOL_START: Indicates user-space will start running
+ * a critical protocol that needs more reliability in the connection to
+ * complete.
+ *
+ * @NL80211_CMD_CRIT_PROTOCOL_STOP: Indicates the connection reliability can
+ * return back to normal.
+ *
+ * @NL80211_CMD_GET_COALESCE: Get currently supported coalesce rules.
+ * @NL80211_CMD_SET_COALESCE: Configure coalesce rules or clear existing rules.
+ *
+ * @NL80211_CMD_CHANNEL_SWITCH: Perform a channel switch by announcing the
+ * new channel information (Channel Switch Announcement - CSA)
+ * in the beacon for some time (as defined in the
+ * %NL80211_ATTR_CH_SWITCH_COUNT parameter) and then change to the
+ * new channel. Userspace provides the new channel information (using
+ * %NL80211_ATTR_WIPHY_FREQ and the attributes determining channel
+ * width). %NL80211_ATTR_CH_SWITCH_BLOCK_TX may be supplied to inform
+ * other station that transmission must be blocked until the channel
+ * switch is complete.
+ *
+ * @NL80211_CMD_VENDOR: Vendor-specified command/event. The command is specified
+ * by the %NL80211_ATTR_VENDOR_ID attribute and a sub-command in
+ * %NL80211_ATTR_VENDOR_SUBCMD. Parameter(s) can be transported in
+ * %NL80211_ATTR_VENDOR_DATA.
+ * For feature advertisement, the %NL80211_ATTR_VENDOR_DATA attribute is
+ * used in the wiphy data as a nested attribute containing descriptions
+ * (&struct nl80211_vendor_cmd_info) of the supported vendor commands.
+ * This may also be sent as an event with the same attributes.
+ *
+ * @NL80211_CMD_SET_QOS_MAP: Set Interworking QoS mapping for IP DSCP values.
+ * The QoS mapping information is included in %NL80211_ATTR_QOS_MAP. If
+ * that attribute is not included, QoS mapping is disabled. Since this
+ * QoS mapping is relevant for IP packets, it is only valid during an
+ * association. This is cleared on disassociation and AP restart.
+ *
+ * @NL80211_CMD_ADD_TX_TS: Ask the kernel to add a traffic stream for the given
+ * %NL80211_ATTR_TSID and %NL80211_ATTR_MAC with %NL80211_ATTR_USER_PRIO
+ * and %NL80211_ATTR_ADMITTED_TIME parameters.
+ * Note that the action frame handshake with the AP shall be handled by
+ * userspace via the normal management RX/TX framework, this only sets
+ * up the TX TS in the driver/device.
+ * If the admitted time attribute is not added then the request just checks
+ * if a subsequent setup could be successful, the intent is to use this to
+ * avoid setting up a session with the AP when local restrictions would
+ * make that impossible. However, the subsequent "real" setup may still
+ * fail even if the check was successful.
+ * @NL80211_CMD_DEL_TX_TS: Remove an existing TS with the %NL80211_ATTR_TSID
+ * and %NL80211_ATTR_MAC parameters. It isn't necessary to call this
+ * before removing a station entry entirely, or before disassociating
+ * or similar, cleanup will happen in the driver/device in this case.
+ *
+ * @NL80211_CMD_GET_MPP: Get mesh path attributes for mesh proxy path to
+ * destination %NL80211_ATTR_MAC on the interface identified by
+ * %NL80211_ATTR_IFINDEX.
+ *
+ * @NL80211_CMD_JOIN_OCB: Join the OCB network. The center frequency and
+ * bandwidth of a channel must be given.
+ * @NL80211_CMD_LEAVE_OCB: Leave the OCB network -- no special arguments, the
+ * network is determined by the network interface.
+ *
+ * @NL80211_CMD_TDLS_CHANNEL_SWITCH: Start channel-switching with a TDLS peer,
+ * identified by the %NL80211_ATTR_MAC parameter. A target channel is
+ * provided via %NL80211_ATTR_WIPHY_FREQ and other attributes determining
+ * channel width/type. The target operating class is given via
+ * %NL80211_ATTR_OPER_CLASS.
+ * The driver is responsible for continually initiating channel-switching
+ * operations and returning to the base channel for communication with the
+ * AP.
+ * @NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH: Stop channel-switching with a TDLS
+ * peer given by %NL80211_ATTR_MAC. Both peers must be on the base channel
+ * when this command completes.
+ *
+ * @NL80211_CMD_WIPHY_REG_CHANGE: Similar to %NL80211_CMD_REG_CHANGE, but used
+ * as an event to indicate changes for devices with wiphy-specific regdom
+ * management.
+ *
+ * @NL80211_CMD_ABORT_SCAN: Stop an ongoing scan. Returns -ENOENT if a scan is
+ * not running. The driver indicates the status of the scan through
+ * cfg80211_scan_done().
+ *
+ * @NL80211_CMD_START_NAN: Start NAN operation, identified by its
+ * %NL80211_ATTR_WDEV interface. This interface must have been
+ * previously created with %NL80211_CMD_NEW_INTERFACE. After it
+ * has been started, the NAN interface will create or join a
+ * cluster. This command must have a valid
+ * %NL80211_ATTR_NAN_MASTER_PREF attribute and optional
+ * %NL80211_ATTR_BANDS attributes. If %NL80211_ATTR_BANDS is
+ * omitted or set to 0, it means don't-care and the device will
+ * decide what to use. After this command NAN functions can be
+ * added.
+ * @NL80211_CMD_STOP_NAN: Stop the NAN operation, identified by
+ * its %NL80211_ATTR_WDEV interface.
+ * @NL80211_CMD_ADD_NAN_FUNCTION: Add a NAN function. The function is defined
+ * with %NL80211_ATTR_NAN_FUNC nested attribute. When called, this
+ * operation returns the strictly positive and unique instance id
+ * (%NL80211_ATTR_NAN_FUNC_INST_ID) and a cookie (%NL80211_ATTR_COOKIE)
+ * of the function upon success.
+ * Since instance ID's can be re-used, this cookie is the right
+ * way to identify the function. This will avoid races when a termination
+ * event is handled by the user space after it has already added a new
+ * function that got the same instance id from the kernel as the one
+ * which just terminated.
+ * This cookie may be used in NAN events even before the command
+ * returns, so userspace shouldn't process NAN events until it processes
+ * the response to this command.
+ * Look at %NL80211_ATTR_SOCKET_OWNER as well.
+ * @NL80211_CMD_DEL_NAN_FUNCTION: Delete a NAN function by cookie.
+ * This command is also used as a notification sent when a NAN function is
+ * terminated. This will contain a %NL80211_ATTR_NAN_FUNC_INST_ID
+ * and %NL80211_ATTR_COOKIE attributes.
+ * @NL80211_CMD_CHANGE_NAN_CONFIG: Change current NAN
+ * configuration. NAN must be operational (%NL80211_CMD_START_NAN
+ * was executed). It must contain at least one of the following
+ * attributes: %NL80211_ATTR_NAN_MASTER_PREF,
+ * %NL80211_ATTR_BANDS. If %NL80211_ATTR_BANDS is omitted, the
+ * current configuration is not changed. If it is present but
+ * set to zero, the configuration is changed to don't-care
+ * (i.e. the device can decide what to do).
+ * @NL80211_CMD_NAN_FUNC_MATCH: Notification sent when a match is reported.
+ * This will contain a %NL80211_ATTR_NAN_MATCH nested attribute and
+ * %NL80211_ATTR_COOKIE.
+ *
+ * @NL80211_CMD_UPDATE_CONNECT_PARAMS: Update one or more connect parameters
+ * for subsequent roaming cases if the driver or firmware uses internal
+ * BSS selection. This command can be issued only while connected and it
+ * does not result in a change for the current association. Currently,
+ * only the %NL80211_ATTR_IE data is used and updated with this command.
+ *
+ * @NL80211_CMD_SET_PMK: For offloaded 4-Way handshake, set the PMK or PMK-R0
+ * for the given authenticator address (specified with %NL80211_ATTR_MAC).
+ * When %NL80211_ATTR_PMKR0_NAME is set, %NL80211_ATTR_PMK specifies the
+ * PMK-R0, otherwise it specifies the PMK.
+ * @NL80211_CMD_DEL_PMK: For offloaded 4-Way handshake, delete the previously
+ * configured PMK for the authenticator address identified by
+ * %NL80211_ATTR_MAC.
+ * @NL80211_CMD_PORT_AUTHORIZED: An event that indicates an 802.1X FT roam was
+ * completed successfully. Drivers that support 4 way handshake offload
+ * should send this event after indicating 802.1X FT assocation with
+ * %NL80211_CMD_ROAM. If the 4 way handshake failed %NL80211_CMD_DISCONNECT
+ * should be indicated instead.
+ * @NL80211_CMD_CONTROL_PORT_FRAME: Control Port (e.g. PAE) frame TX request
+ * and RX notification. This command is used both as a request to transmit
+ * a control port frame and as a notification that a control port frame
+ * has been received. %NL80211_ATTR_FRAME is used to specify the
+ * frame contents. The frame is the raw EAPoL data, without ethernet or
+ * 802.11 headers.
+ * For an MLD transmitter, the %NL80211_ATTR_MLO_LINK_ID may be given and
+ * its effect will depend on the destination: If the destination is known
+ * to be an MLD, this will be used as a hint to select the link to transmit
+ * the frame on. If the destination is not an MLD, this will select both
+ * the link to transmit on and the source address will be set to the link
+ * address of that link.
+ * When used as an event indication %NL80211_ATTR_CONTROL_PORT_ETHERTYPE,
+ * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT and %NL80211_ATTR_MAC are added
+ * indicating the protocol type of the received frame; whether the frame
+ * was received unencrypted and the MAC address of the peer respectively.
+ *
+ * @NL80211_CMD_RELOAD_REGDB: Request that the regdb firmware file is reloaded.
+ *
+ * @NL80211_CMD_EXTERNAL_AUTH: This interface is exclusively defined for host
+ * drivers that do not define separate commands for authentication and
+ * association, but rely on user space for the authentication to happen.
+ * This interface acts both as the event request (driver to user space)
+ * to trigger the authentication and command response (userspace to
+ * driver) to indicate the authentication status.
+ *
+ * User space uses the %NL80211_CMD_CONNECT command to the host driver to
+ * trigger a connection. The host driver selects a BSS and further uses
+ * this interface to offload only the authentication part to the user
+ * space. Authentication frames are passed between the driver and user
+ * space through the %NL80211_CMD_FRAME interface. Host driver proceeds
+ * further with the association after getting successful authentication
+ * status. User space indicates the authentication status through
+ * %NL80211_ATTR_STATUS_CODE attribute in %NL80211_CMD_EXTERNAL_AUTH
+ * command interface.
+ *
+ * Host driver sends MLD address of the AP with %NL80211_ATTR_MLD_ADDR in
+ * %NL80211_CMD_EXTERNAL_AUTH event to indicate user space to enable MLO
+ * during the authentication offload in STA mode while connecting to MLD
+ * APs. Host driver should check %NL80211_ATTR_MLO_SUPPORT flag capability
+ * in %NL80211_CMD_CONNECT to know whether the user space supports enabling
+ * MLO during the authentication offload or not.
+ * User space should enable MLO during the authentication only when it
+ * receives the AP MLD address in authentication offload request. User
+ * space shouldn't enable MLO when the authentication offload request
+ * doesn't indicate the AP MLD address even if the AP is MLO capable.
+ * User space should use %NL80211_ATTR_MLD_ADDR as peer's MLD address and
+ * interface address identified by %NL80211_ATTR_IFINDEX as self MLD
+ * address. User space and host driver to use MLD addresses in RA, TA and
+ * BSSID fields of the frames between them, and host driver translates the
+ * MLD addresses to/from link addresses based on the link chosen for the
+ * authentication.
+ *
+ * Host driver reports this status on an authentication failure to the
+ * user space through the connect result as the user space would have
+ * initiated the connection through the connect request.
+ *
+ * @NL80211_CMD_STA_OPMODE_CHANGED: An event that notify station's
+ * ht opmode or vht opmode changes using any of %NL80211_ATTR_SMPS_MODE,
+ * %NL80211_ATTR_CHANNEL_WIDTH,%NL80211_ATTR_NSS attributes with its
+ * address(specified in %NL80211_ATTR_MAC).
+ *
+ * @NL80211_CMD_GET_FTM_RESPONDER_STATS: Retrieve FTM responder statistics, in
+ * the %NL80211_ATTR_FTM_RESPONDER_STATS attribute.
+ *
+ * @NL80211_CMD_PEER_MEASUREMENT_START: start a (set of) peer measurement(s)
+ * with the given parameters, which are encapsulated in the nested
+ * %NL80211_ATTR_PEER_MEASUREMENTS attribute. Optionally, MAC address
+ * randomization may be enabled and configured by specifying the
+ * %NL80211_ATTR_MAC and %NL80211_ATTR_MAC_MASK attributes.
+ * If a timeout is requested, use the %NL80211_ATTR_TIMEOUT attribute.
+ * A u64 cookie for further %NL80211_ATTR_COOKIE use is returned in
+ * the netlink extended ack message.
+ *
+ * To cancel a measurement, close the socket that requested it.
+ *
+ * Measurement results are reported to the socket that requested the
+ * measurement using @NL80211_CMD_PEER_MEASUREMENT_RESULT when they
+ * become available, so applications must ensure a large enough socket
+ * buffer size.
+ *
+ * Depending on driver support it may or may not be possible to start
+ * multiple concurrent measurements.
+ * @NL80211_CMD_PEER_MEASUREMENT_RESULT: This command number is used for the
+ * result notification from the driver to the requesting socket.
+ * @NL80211_CMD_PEER_MEASUREMENT_COMPLETE: Notification only, indicating that
+ * the measurement completed, using the measurement cookie
+ * (%NL80211_ATTR_COOKIE).
+ *
+ * @NL80211_CMD_NOTIFY_RADAR: Notify the kernel that a radar signal was
+ * detected and reported by a neighboring device on the channel
+ * indicated by %NL80211_ATTR_WIPHY_FREQ and other attributes
+ * determining the width and type.
+ *
+ * @NL80211_CMD_UPDATE_OWE_INFO: This interface allows the host driver to
+ * offload OWE processing to user space. This intends to support
+ * OWE AKM by the host drivers that implement SME but rely
+ * on the user space for the cryptographic/DH IE processing in AP mode.
+ *
+ * @NL80211_CMD_PROBE_MESH_LINK: The requirement for mesh link metric
+ * refreshing, is that from one mesh point we be able to send some data
+ * frames to other mesh points which are not currently selected as a
+ * primary traffic path, but which are only 1 hop away. The absence of
+ * the primary path to the chosen node makes it necessary to apply some
+ * form of marking on a chosen packet stream so that the packets can be
+ * properly steered to the selected node for testing, and not by the
+ * regular mesh path lookup. Further, the packets must be of type data
+ * so that the rate control (often embedded in firmware) is used for
+ * rate selection.
+ *
+ * Here attribute %NL80211_ATTR_MAC is used to specify connected mesh
+ * peer MAC address and %NL80211_ATTR_FRAME is used to specify the frame
+ * content. The frame is ethernet data.
+ *
+ * @NL80211_CMD_SET_TID_CONFIG: Data frame TID specific configuration
+ * is passed using %NL80211_ATTR_TID_CONFIG attribute.
+ *
+ * @NL80211_CMD_UNPROT_BEACON: Unprotected or incorrectly protected Beacon
+ * frame. This event is used to indicate that a received Beacon frame was
+ * dropped because it did not include a valid MME MIC while beacon
+ * protection was enabled (BIGTK configured in station mode).
+ *
+ * @NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS: Report TX status of a control
+ * port frame transmitted with %NL80211_CMD_CONTROL_PORT_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_SET_SAR_SPECS: SAR power limitation configuration is
+ * passed using %NL80211_ATTR_SAR_SPEC. %NL80211_ATTR_WIPHY is used to
+ * specify the wiphy index to be applied to.
+ *
+ * @NL80211_CMD_OBSS_COLOR_COLLISION: This notification is sent out whenever
+ * mac80211/drv detects a bss color collision.
+ *
+ * @NL80211_CMD_COLOR_CHANGE_REQUEST: This command is used to indicate that
+ * userspace wants to change the BSS color.
+ *
+ * @NL80211_CMD_COLOR_CHANGE_STARTED: Notify userland, that a color change has
+ * started
+ *
+ * @NL80211_CMD_COLOR_CHANGE_ABORTED: Notify userland, that the color change has
+ * been aborted
+ *
+ * @NL80211_CMD_COLOR_CHANGE_COMPLETED: Notify userland that the color change
+ * has completed
+ *
+ * @NL80211_CMD_SET_FILS_AAD: Set FILS AAD data to the driver using -
+ * &NL80211_ATTR_MAC - for STA MAC address
+ * &NL80211_ATTR_FILS_KEK - for KEK
+ * &NL80211_ATTR_FILS_NONCES - for FILS Nonces
+ * (STA Nonce 16 bytes followed by AP Nonce 16 bytes)
+ *
+ * @NL80211_CMD_ASSOC_COMEBACK: notification about an association
+ * temporal rejection with comeback. The event includes %NL80211_ATTR_MAC
+ * to describe the BSSID address of the AP and %NL80211_ATTR_TIMEOUT to
+ * specify the timeout value.
+ *
+ * @NL80211_CMD_ADD_LINK: Add a new link to an interface. The
+ * %NL80211_ATTR_MLO_LINK_ID attribute is used for the new link.
+ * @NL80211_CMD_REMOVE_LINK: Remove a link from an interface. This may come
+ * without %NL80211_ATTR_MLO_LINK_ID as an easy way to remove all links
+ * in preparation for e.g. roaming to a regular (non-MLO) AP.
+ *
+ * @NL80211_CMD_ADD_LINK_STA: Add a link to an MLD station
+ * @NL80211_CMD_MODIFY_LINK_STA: Modify a link of an MLD station
+ * @NL80211_CMD_REMOVE_LINK_STA: Remove a link of an MLD station
+ *
+ * @NL80211_CMD_SET_HW_TIMESTAMP: Enable/disable HW timestamping of Timing
+ * measurement and Fine timing measurement frames. If %NL80211_ATTR_MAC
+ * is included, enable/disable HW timestamping only for frames to/from the
+ * specified MAC address. Otherwise enable/disable HW timestamping for
+ * all TM/FTM frames (including ones that were enabled with specific MAC
+ * address). If %NL80211_ATTR_HW_TIMESTAMP_ENABLED is not included, disable
+ * HW timestamping.
+ * The number of peers that HW timestamping can be enabled for concurrently
+ * is indicated by %NL80211_ATTR_MAX_HW_TIMESTAMP_PEERS.
+ *
+ * @NL80211_CMD_MAX: highest used command number
+ * @__NL80211_CMD_AFTER_LAST: internal use
+ */
+enum nl80211_commands {
+/* don't change the order or add anything between, 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_START_AP,
+ NL80211_CMD_NEW_BEACON = NL80211_CMD_START_AP,
+ NL80211_CMD_STOP_AP,
+ NL80211_CMD_DEL_BEACON = NL80211_CMD_STOP_AP,
+
+ 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_CONFIG,
+ NL80211_CMD_SET_MESH_CONFIG,
+
+ 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,
+
+ NL80211_CMD_NEW_PEER_CANDIDATE,
+
+ NL80211_CMD_GET_WOWLAN,
+ NL80211_CMD_SET_WOWLAN,
+
+ NL80211_CMD_START_SCHED_SCAN,
+ NL80211_CMD_STOP_SCHED_SCAN,
+ NL80211_CMD_SCHED_SCAN_RESULTS,
+ NL80211_CMD_SCHED_SCAN_STOPPED,
+
+ NL80211_CMD_SET_REKEY_OFFLOAD,
+
+ NL80211_CMD_PMKSA_CANDIDATE,
+
+ NL80211_CMD_TDLS_OPER,
+ NL80211_CMD_TDLS_MGMT,
+
+ NL80211_CMD_UNEXPECTED_FRAME,
+
+ NL80211_CMD_PROBE_CLIENT,
+
+ NL80211_CMD_REGISTER_BEACONS,
+
+ NL80211_CMD_UNEXPECTED_4ADDR_FRAME,
+
+ NL80211_CMD_SET_NOACK_MAP,
+
+ NL80211_CMD_CH_SWITCH_NOTIFY,
+
+ NL80211_CMD_START_P2P_DEVICE,
+ NL80211_CMD_STOP_P2P_DEVICE,
+
+ NL80211_CMD_CONN_FAILED,
+
+ NL80211_CMD_SET_MCAST_RATE,
+
+ NL80211_CMD_SET_MAC_ACL,
+
+ NL80211_CMD_RADAR_DETECT,
+
+ NL80211_CMD_GET_PROTOCOL_FEATURES,
+
+ NL80211_CMD_UPDATE_FT_IES,
+ NL80211_CMD_FT_EVENT,
+
+ NL80211_CMD_CRIT_PROTOCOL_START,
+ NL80211_CMD_CRIT_PROTOCOL_STOP,
+
+ NL80211_CMD_GET_COALESCE,
+ NL80211_CMD_SET_COALESCE,
+
+ NL80211_CMD_CHANNEL_SWITCH,
+
+ NL80211_CMD_VENDOR,
+
+ NL80211_CMD_SET_QOS_MAP,
+
+ NL80211_CMD_ADD_TX_TS,
+ NL80211_CMD_DEL_TX_TS,
+
+ NL80211_CMD_GET_MPP,
+
+ NL80211_CMD_JOIN_OCB,
+ NL80211_CMD_LEAVE_OCB,
+
+ NL80211_CMD_CH_SWITCH_STARTED_NOTIFY,
+
+ NL80211_CMD_TDLS_CHANNEL_SWITCH,
+ NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH,
+
+ NL80211_CMD_WIPHY_REG_CHANGE,
+
+ NL80211_CMD_ABORT_SCAN,
+
+ NL80211_CMD_START_NAN,
+ NL80211_CMD_STOP_NAN,
+ NL80211_CMD_ADD_NAN_FUNCTION,
+ NL80211_CMD_DEL_NAN_FUNCTION,
+ NL80211_CMD_CHANGE_NAN_CONFIG,
+ NL80211_CMD_NAN_MATCH,
+
+ NL80211_CMD_SET_MULTICAST_TO_UNICAST,
+
+ NL80211_CMD_UPDATE_CONNECT_PARAMS,
+
+ NL80211_CMD_SET_PMK,
+ NL80211_CMD_DEL_PMK,
+
+ NL80211_CMD_PORT_AUTHORIZED,
+
+ NL80211_CMD_RELOAD_REGDB,
+
+ NL80211_CMD_EXTERNAL_AUTH,
+
+ NL80211_CMD_STA_OPMODE_CHANGED,
+
+ NL80211_CMD_CONTROL_PORT_FRAME,
+
+ NL80211_CMD_GET_FTM_RESPONDER_STATS,
+
+ NL80211_CMD_PEER_MEASUREMENT_START,
+ NL80211_CMD_PEER_MEASUREMENT_RESULT,
+ NL80211_CMD_PEER_MEASUREMENT_COMPLETE,
+
+ NL80211_CMD_NOTIFY_RADAR,
+
+ NL80211_CMD_UPDATE_OWE_INFO,
+
+ NL80211_CMD_PROBE_MESH_LINK,
+
+ NL80211_CMD_SET_TID_CONFIG,
+
+ NL80211_CMD_UNPROT_BEACON,
+
+ NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS,
+
+ NL80211_CMD_SET_SAR_SPECS,
+
+ NL80211_CMD_OBSS_COLOR_COLLISION,
+
+ NL80211_CMD_COLOR_CHANGE_REQUEST,
+
+ NL80211_CMD_COLOR_CHANGE_STARTED,
+ NL80211_CMD_COLOR_CHANGE_ABORTED,
+ NL80211_CMD_COLOR_CHANGE_COMPLETED,
+
+ NL80211_CMD_SET_FILS_AAD,
+
+ NL80211_CMD_ASSOC_COMEBACK,
+
+ NL80211_CMD_ADD_LINK,
+ NL80211_CMD_REMOVE_LINK,
+
+ NL80211_CMD_ADD_LINK_STA,
+ NL80211_CMD_MODIFY_LINK_STA,
+ NL80211_CMD_REMOVE_LINK_STA,
+
+ NL80211_CMD_SET_HW_TIMESTAMP,
+
+ /* 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
+
+#define NL80211_ATTR_FEATURE_FLAGS NL80211_ATTR_FEATURE_FLAGS
+
+/* source-level API compatibility */
+#define NL80211_CMD_GET_MESH_PARAMS NL80211_CMD_GET_MESH_CONFIG
+#define NL80211_CMD_SET_MESH_PARAMS NL80211_CMD_SET_MESH_CONFIG
+#define NL80211_MESH_SETUP_VENDOR_PATH_SEL_IE NL80211_MESH_SETUP_IE
+
+/**
+ * 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,
+ * defines the channel together with the (deprecated)
+ * %NL80211_ATTR_WIPHY_CHANNEL_TYPE attribute or the attributes
+ * %NL80211_ATTR_CHANNEL_WIDTH and if needed %NL80211_ATTR_CENTER_FREQ1
+ * and %NL80211_ATTR_CENTER_FREQ2
+ * @NL80211_ATTR_CHANNEL_WIDTH: u32 attribute containing one of the values
+ * of &enum nl80211_chan_width, describing the channel width. See the
+ * documentation of the enum for more information.
+ * @NL80211_ATTR_CENTER_FREQ1: Center frequency of the first part of the
+ * channel, used for anything but 20 MHz bandwidth. In S1G this is the
+ * operating channel center frequency.
+ * @NL80211_ATTR_CENTER_FREQ2: Center frequency of the second part of the
+ * channel, used only for 80+80 MHz bandwidth
+ * @NL80211_ATTR_WIPHY_CHANNEL_TYPE: included with NL80211_ATTR_WIPHY_FREQ
+ * if HT20 or HT40 are to be used (i.e., HT 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
+ * This attribute is now deprecated.
+ * @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_WDEV: wireless device identifier, used for pseudo-devices
+ * that don't have a netdev (u64)
+ *
+ * @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_KEY_DEFAULT: Flag attribute indicating the key is default key
+ * @NL80211_ATTR_KEY_DEFAULT_MGMT: Flag attribute indicating the key is the
+ * default management key
+ * @NL80211_ATTR_CIPHER_SUITES_PAIRWISE: For crypto settings for connect or
+ * other commands, indicates which pairwise cipher suites are used
+ * @NL80211_ATTR_CIPHER_SUITE_GROUP: For crypto settings for connect or
+ * other commands, indicates which group cipher suite is used
+ *
+ * @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.
+ * @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_STA_PLINK_ACTION: action to perform on the mesh peer link
+ * (see &enum nl80211_plink_action).
+ * @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.11 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_NUM_SCHED_SCAN_SSIDS: number of SSIDs you can
+ * scan with a single scheduled 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_MAX_SCHED_SCAN_IE_LEN: maximum length of information
+ * elements that can be added to a scheduled scan request
+ * @NL80211_ATTR_MAX_MATCH_SETS: maximum number of sets that can be
+ * used with @NL80211_ATTR_SCHED_SCAN_MATCH, a wiphy attribute.
+ *
+ * @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 and
+ * %NL80211_CMD_CONNECT requests. %NL80211_MFP_OPTIONAL is not allowed for
+ * %NL80211_CMD_ASSOCIATE since user space SME is expected and hence, it
+ * must have decided whether to use management frame protection or not.
+ * Setting %NL80211_MFP_OPTIONAL with a %NL80211_CMD_CONNECT request will
+ * let the driver (or the firmware) decide whether to use MFP or not.
+ *
+ * @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_CONTROL_PORT_OVER_NL80211: A flag indicating whether control
+ * port frames (e.g. of type given in %NL80211_ATTR_CONTROL_PORT_ETHERTYPE)
+ * will be sent directly to the network interface or sent via the NL80211
+ * socket. If this attribute is missing, then legacy behavior of sending
+ * control port frames directly to the network interface is used. If the
+ * flag is included, then control port frames are sent over NL80211 instead
+ * using %CMD_CONTROL_PORT_FRAME. If control port routing over NL80211 is
+ * to be used then userspace must also use the %NL80211_ATTR_SOCKET_OWNER
+ * flag. When used with %NL80211_ATTR_CONTROL_PORT_NO_PREAUTH, pre-auth
+ * frames are not forwared over the control port.
+ *
+ * @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. This is also used with NEW_BEACON to
+ * indicate that the BSS is to use protection.
+ *
+ * @NL80211_ATTR_CIPHERS_PAIRWISE: Used with CONNECT, ASSOCIATE, and NEW_BEACON
+ * to indicate which unicast key ciphers will be used with the connection
+ * (an array of u32).
+ * @NL80211_ATTR_CIPHER_GROUP: Used with CONNECT, ASSOCIATE, and NEW_BEACON to
+ * indicate which group key cipher will be used with the connection (a
+ * u32).
+ * @NL80211_ATTR_WPA_VERSIONS: Used with CONNECT, ASSOCIATE, and NEW_BEACON 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, ASSOCIATE, and NEW_BEACON to
+ * indicate which key management algorithm(s) to use (an array of u32).
+ * This attribute is also sent in response to @NL80211_CMD_GET_WIPHY,
+ * indicating the supported AKM suites, intended for specific drivers which
+ * implement SME and have constraints on which AKMs are supported and also
+ * the cases where an AKM support is offloaded to the driver/firmware.
+ * If there is no such notification from the driver, user space should
+ * assume the driver supports all the AKM suites.
+ *
+ * @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 in ASSOCIATE and CONNECT
+ * commands to specify a request to reassociate within an ESS, i.e., to use
+ * Reassociate Request frame (with the value of this attribute in the
+ * Current AP address field) instead of Association Request frame which is
+ * used for the initial association to an ESS.
+ *
+ * @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 and with starting AP,
+ * and joining mesh networks (not IBSS yet). In the later case, it must
+ * specify just a single bitrate, which is to be used for the beacon.
+ * The driver must also specify support for this with the extended
+ * features NL80211_EXT_FEATURE_BEACON_RATE_LEGACY,
+ * NL80211_EXT_FEATURE_BEACON_RATE_HT,
+ * NL80211_EXT_FEATURE_BEACON_RATE_VHT and
+ * NL80211_EXT_FEATURE_BEACON_RATE_HE.
+ *
+ * @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_PS_STATE: powersave state, using &enum nl80211_ps_state values.
+ *
+ * @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 description see @NL80211_ATTR_WIPHY_ANTENNA_TX.
+ *
+ * @NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX: Bitmap of antennas which are available
+ * for configuration as TX antennas via the above parameters.
+ *
+ * @NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX: Bitmap of antennas which are available
+ * for configuration as RX antennas via the above parameters.
+ *
+ * @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_HT_OPMODE: 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_MESH_SETUP: Optional mesh setup parameters. These cannot be
+ * changed once the mesh is active.
+ * @NL80211_ATTR_MESH_CONFIG: Mesh configuration parameters, a nested attribute
+ * containing attributes from &enum nl80211_meshconf_params.
+ * @NL80211_ATTR_SUPPORT_MESH_AUTH: Currently, this means the underlying driver
+ * allows auth frames in a mesh to be passed to userspace for processing via
+ * the @NL80211_MESH_SETUP_USERSPACE_AUTH flag.
+ * @NL80211_ATTR_STA_PLINK_STATE: The state of a mesh peer link as defined in
+ * &enum nl80211_plink_state. Used when userspace is driving the peer link
+ * management state machine. @NL80211_MESH_SETUP_USERSPACE_AMPE or
+ * @NL80211_MESH_SETUP_USERSPACE_MPM must be enabled.
+ *
+ * @NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED: indicates, as part of the wiphy
+ * capabilities, the supported WoWLAN triggers
+ * @NL80211_ATTR_WOWLAN_TRIGGERS: used by %NL80211_CMD_SET_WOWLAN to
+ * indicate which WoW triggers should be enabled. This is also
+ * used by %NL80211_CMD_GET_WOWLAN to get the currently enabled WoWLAN
+ * triggers.
+ *
+ * @NL80211_ATTR_SCHED_SCAN_INTERVAL: Interval between scheduled scan
+ * cycles, in msecs.
+ *
+ * @NL80211_ATTR_SCHED_SCAN_MATCH: Nested attribute with one or more
+ * sets of attributes to match during scheduled scans. Only BSSs
+ * that match any of the sets will be reported. These are
+ * pass-thru filter rules.
+ * For a match to succeed, the BSS must match all attributes of a
+ * set. Since not every hardware supports matching all types of
+ * attributes, there is no guarantee that the reported BSSs are
+ * fully complying with the match sets and userspace needs to be
+ * able to ignore them by itself.
+ * Thus, the implementation is somewhat hardware-dependent, but
+ * this is only an optimization and the userspace application
+ * needs to handle all the non-filtered results anyway.
+ * If the match attributes don't make sense when combined with
+ * the values passed in @NL80211_ATTR_SCAN_SSIDS (eg. if an SSID
+ * is included in the probe request, but the match attributes
+ * will never let it go through), -EINVAL may be returned.
+ * If omitted, no filtering is done.
+ *
+ * @NL80211_ATTR_INTERFACE_COMBINATIONS: Nested attribute listing the supported
+ * interface combinations. In each nested item, it contains attributes
+ * defined in &enum nl80211_if_combination_attrs.
+ * @NL80211_ATTR_SOFTWARE_IFTYPES: Nested attribute (just like
+ * %NL80211_ATTR_SUPPORTED_IFTYPES) containing the interface types that
+ * are managed in software: interfaces of these types aren't subject to
+ * any restrictions in their number or combinations.
+ *
+ * @NL80211_ATTR_REKEY_DATA: nested attribute containing the information
+ * necessary for GTK rekeying in the device, see &enum nl80211_rekey_data.
+ *
+ * @NL80211_ATTR_SCAN_SUPP_RATES: rates per to be advertised as supported in scan,
+ * nested array attribute containing an entry for each band, with the entry
+ * being a list 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_HIDDEN_SSID: indicates whether SSID is to be hidden from Beacon
+ * and Probe Response (when response to wildcard Probe Request); see
+ * &enum nl80211_hidden_ssid, represented as a u32
+ *
+ * @NL80211_ATTR_IE_PROBE_RESP: Information element(s) for Probe Response frame.
+ * This is used with %NL80211_CMD_NEW_BEACON and %NL80211_CMD_SET_BEACON to
+ * provide extra IEs (e.g., WPS/P2P IE) into Probe Response frames when the
+ * driver (or firmware) replies to Probe Request frames.
+ * @NL80211_ATTR_IE_ASSOC_RESP: Information element(s) for (Re)Association
+ * Response frames. This is used with %NL80211_CMD_NEW_BEACON and
+ * %NL80211_CMD_SET_BEACON to provide extra IEs (e.g., WPS/P2P IE) into
+ * (Re)Association Response frames when the driver (or firmware) replies to
+ * (Re)Association Request frames.
+ *
+ * @NL80211_ATTR_STA_WME: Nested attribute containing the wme configuration
+ * of the station, see &enum nl80211_sta_wme_attr.
+ * @NL80211_ATTR_SUPPORT_AP_UAPSD: the device supports uapsd when working
+ * as AP.
+ *
+ * @NL80211_ATTR_ROAM_SUPPORT: Indicates whether the firmware is capable of
+ * roaming to another AP in the same ESS if the signal lever is low.
+ *
+ * @NL80211_ATTR_PMKSA_CANDIDATE: Nested attribute containing the PMKSA caching
+ * candidate information, see &enum nl80211_pmksa_candidate_attr.
+ *
+ * @NL80211_ATTR_TX_NO_CCK_RATE: Indicates whether to use CCK rate or not
+ * for management frames transmission. In order to avoid p2p probe/action
+ * frames are being transmitted at CCK rate in 2GHz band, the user space
+ * applications use this attribute.
+ * This attribute is used with %NL80211_CMD_TRIGGER_SCAN and
+ * %NL80211_CMD_FRAME commands.
+ *
+ * @NL80211_ATTR_TDLS_ACTION: Low level TDLS action code (e.g. link setup
+ * request, link setup confirm, link teardown, etc.). Values are
+ * described in the TDLS (802.11z) specification.
+ * @NL80211_ATTR_TDLS_DIALOG_TOKEN: Non-zero token for uniquely identifying a
+ * TDLS conversation between two devices.
+ * @NL80211_ATTR_TDLS_OPERATION: High level TDLS operation; see
+ * &enum nl80211_tdls_operation, represented as a u8.
+ * @NL80211_ATTR_TDLS_SUPPORT: A flag indicating the device can operate
+ * as a TDLS peer sta.
+ * @NL80211_ATTR_TDLS_EXTERNAL_SETUP: The TDLS discovery/setup and teardown
+ * procedures should be performed by sending TDLS packets via
+ * %NL80211_CMD_TDLS_MGMT. Otherwise %NL80211_CMD_TDLS_OPER should be
+ * used for asking the driver to perform a TDLS operation.
+ *
+ * @NL80211_ATTR_DEVICE_AP_SME: This u32 attribute may be listed for devices
+ * that have AP support to indicate that they have the AP SME integrated
+ * with support for the features listed in this attribute, see
+ * &enum nl80211_ap_sme_features.
+ *
+ * @NL80211_ATTR_DONT_WAIT_FOR_ACK: Used with %NL80211_CMD_FRAME, this tells
+ * the driver to not wait for an acknowledgement. Note that due to this,
+ * it will also not give a status callback nor return a cookie. This is
+ * mostly useful for probe responses to save airtime.
+ *
+ * @NL80211_ATTR_FEATURE_FLAGS: This u32 attribute contains flags from
+ * &enum nl80211_feature_flags and is advertised in wiphy information.
+ * @NL80211_ATTR_PROBE_RESP_OFFLOAD: Indicates that the HW responds to probe
+ * requests while operating in AP-mode.
+ * This attribute holds a bitmap of the supported protocols for
+ * offloading (see &enum nl80211_probe_resp_offload_support_attr).
+ *
+ * @NL80211_ATTR_PROBE_RESP: Probe Response template data. Contains the entire
+ * probe-response frame. The DA field in the 802.11 header is zero-ed out,
+ * to be filled by the FW.
+ * @NL80211_ATTR_DISABLE_HT: Force HT capable interfaces to disable
+ * this feature during association. This is a flag attribute.
+ * Currently only supported in mac80211 drivers.
+ * @NL80211_ATTR_DISABLE_VHT: Force VHT capable interfaces to disable
+ * this feature during association. This is a flag attribute.
+ * Currently only supported in mac80211 drivers.
+ * @NL80211_ATTR_DISABLE_HE: Force HE capable interfaces to disable
+ * this feature during association. This is a flag attribute.
+ * Currently only supported in mac80211 drivers.
+ * @NL80211_ATTR_HT_CAPABILITY_MASK: Specify which bits of the
+ * ATTR_HT_CAPABILITY to which attention should be paid.
+ * Currently, only mac80211 NICs support this feature.
+ * The values that may be configured are:
+ * MCS rates, MAX-AMSDU, HT-20-40 and HT_CAP_SGI_40
+ * AMPDU density and AMPDU factor.
+ * All values are treated as suggestions and may be ignored
+ * by the driver as required. The actual values may be seen in
+ * the station debugfs ht_caps file.
+ *
+ * @NL80211_ATTR_DFS_REGION: region for regulatory rules which this country
+ * abides to when initiating radiation on DFS channels. A country maps
+ * to one DFS region.
+ *
+ * @NL80211_ATTR_NOACK_MAP: This u16 bitmap contains the No Ack Policy of
+ * up to 16 TIDs.
+ *
+ * @NL80211_ATTR_INACTIVITY_TIMEOUT: timeout value in seconds, this can be
+ * used by the drivers which has MLME in firmware and does not have support
+ * to report per station tx/rx activity to free up the station entry from
+ * the list. This needs to be used when the driver advertises the
+ * capability to timeout the stations.
+ *
+ * @NL80211_ATTR_RX_SIGNAL_DBM: signal strength in dBm (as a 32-bit int);
+ * this attribute is (depending on the driver capabilities) added to
+ * received frames indicated with %NL80211_CMD_FRAME.
+ *
+ * @NL80211_ATTR_BG_SCAN_PERIOD: Background scan period in seconds
+ * or 0 to disable background scan.
+ *
+ * @NL80211_ATTR_USER_REG_HINT_TYPE: type of regulatory hint passed from
+ * userspace. If unset it is assumed the hint comes directly from
+ * a user. If set code could specify exactly what type of source
+ * was used to provide the hint. For the different types of
+ * allowed user regulatory hints see nl80211_user_reg_hint_type.
+ *
+ * @NL80211_ATTR_CONN_FAILED_REASON: The reason for which AP has rejected
+ * the connection request from a station. nl80211_connect_failed_reason
+ * enum has different reasons of connection failure.
+ *
+ * @NL80211_ATTR_AUTH_DATA: Fields and elements in Authentication frames.
+ * This contains the authentication frame body (non-IE and IE data),
+ * excluding the Authentication algorithm number, i.e., starting at the
+ * Authentication transaction sequence number field. It is used with
+ * authentication algorithms that need special fields to be added into
+ * the frames (SAE and FILS). Currently, only the SAE cases use the
+ * initial two fields (Authentication transaction sequence number and
+ * Status code). However, those fields are included in the attribute data
+ * for all authentication algorithms to keep the attribute definition
+ * consistent.
+ *
+ * @NL80211_ATTR_VHT_CAPABILITY: VHT Capability information element (from
+ * association request when used with NL80211_CMD_NEW_STATION)
+ *
+ * @NL80211_ATTR_SCAN_FLAGS: scan request control flags (u32)
+ *
+ * @NL80211_ATTR_P2P_CTWINDOW: P2P GO Client Traffic Window (u8), used with
+ * the START_AP and SET_BSS commands
+ * @NL80211_ATTR_P2P_OPPPS: P2P GO opportunistic PS (u8), used with the
+ * START_AP and SET_BSS commands. This can have the values 0 or 1;
+ * if not given in START_AP 0 is assumed, if not given in SET_BSS
+ * no change is made.
+ *
+ * @NL80211_ATTR_LOCAL_MESH_POWER_MODE: local mesh STA link-specific power mode
+ * defined in &enum nl80211_mesh_power_mode.
+ *
+ * @NL80211_ATTR_ACL_POLICY: ACL policy, see &enum nl80211_acl_policy,
+ * carried in a u32 attribute
+ *
+ * @NL80211_ATTR_MAC_ADDRS: Array of nested MAC addresses, used for
+ * MAC ACL.
+ *
+ * @NL80211_ATTR_MAC_ACL_MAX: u32 attribute to advertise the maximum
+ * number of MAC addresses that a device can support for MAC
+ * ACL.
+ *
+ * @NL80211_ATTR_RADAR_EVENT: Type of radar event for notification to userspace,
+ * contains a value of enum nl80211_radar_event (u32).
+ *
+ * @NL80211_ATTR_EXT_CAPA: 802.11 extended capabilities that the kernel driver
+ * has and handles. The format is the same as the IE contents. See
+ * 802.11-2012 8.4.2.29 for more information.
+ * @NL80211_ATTR_EXT_CAPA_MASK: Extended capabilities that the kernel driver
+ * has set in the %NL80211_ATTR_EXT_CAPA value, for multibit fields.
+ *
+ * @NL80211_ATTR_STA_CAPABILITY: Station capabilities (u16) are advertised to
+ * the driver, e.g., to enable TDLS power save (PU-APSD).
+ *
+ * @NL80211_ATTR_STA_EXT_CAPABILITY: Station extended capabilities are
+ * advertised to the driver, e.g., to enable TDLS off channel operations
+ * and PU-APSD.
+ *
+ * @NL80211_ATTR_PROTOCOL_FEATURES: global nl80211 feature flags, see
+ * &enum nl80211_protocol_features, the attribute is a u32.
+ *
+ * @NL80211_ATTR_SPLIT_WIPHY_DUMP: flag attribute, userspace supports
+ * receiving the data for a single wiphy split across multiple
+ * messages, given with wiphy dump message
+ *
+ * @NL80211_ATTR_MDID: Mobility Domain Identifier
+ *
+ * @NL80211_ATTR_IE_RIC: Resource Information Container Information
+ * Element
+ *
+ * @NL80211_ATTR_CRIT_PROT_ID: critical protocol identifier requiring increased
+ * reliability, see &enum nl80211_crit_proto_id (u16).
+ * @NL80211_ATTR_MAX_CRIT_PROT_DURATION: duration in milliseconds in which
+ * the connection should have increased reliability (u16).
+ *
+ * @NL80211_ATTR_PEER_AID: Association ID for the peer TDLS station (u16).
+ * This is similar to @NL80211_ATTR_STA_AID but with a difference of being
+ * allowed to be used with the first @NL80211_CMD_SET_STATION command to
+ * update a TDLS peer STA entry.
+ *
+ * @NL80211_ATTR_COALESCE_RULE: Coalesce rule information.
+ *
+ * @NL80211_ATTR_CH_SWITCH_COUNT: u32 attribute specifying the number of TBTT's
+ * until the channel switch event.
+ * @NL80211_ATTR_CH_SWITCH_BLOCK_TX: flag attribute specifying that transmission
+ * must be blocked on the current channel (before the channel switch
+ * operation). Also included in the channel switch started event if quiet
+ * was requested by the AP.
+ * @NL80211_ATTR_CSA_IES: Nested set of attributes containing the IE information
+ * for the time while performing a channel switch.
+ * @NL80211_ATTR_CNTDWN_OFFS_BEACON: An array of offsets (u16) to the channel
+ * switch or color change counters in the beacons tail (%NL80211_ATTR_BEACON_TAIL).
+ * @NL80211_ATTR_CNTDWN_OFFS_PRESP: An array of offsets (u16) to the channel
+ * switch or color change counters in the probe response (%NL80211_ATTR_PROBE_RESP).
+ *
+ * @NL80211_ATTR_RXMGMT_FLAGS: flags for nl80211_send_mgmt(), u32.
+ * As specified in the &enum nl80211_rxmgmt_flags.
+ *
+ * @NL80211_ATTR_STA_SUPPORTED_CHANNELS: array of supported channels.
+ *
+ * @NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES: array of supported
+ * operating classes.
+ *
+ * @NL80211_ATTR_HANDLE_DFS: A flag indicating whether user space
+ * controls DFS operation in IBSS mode. If the flag is included in
+ * %NL80211_CMD_JOIN_IBSS request, the driver will allow use of DFS
+ * channels and reports radar events to userspace. Userspace is required
+ * to react to radar events, e.g. initiate a channel switch or leave the
+ * IBSS network.
+ *
+ * @NL80211_ATTR_SUPPORT_5_MHZ: A flag indicating that the device supports
+ * 5 MHz channel bandwidth.
+ * @NL80211_ATTR_SUPPORT_10_MHZ: A flag indicating that the device supports
+ * 10 MHz channel bandwidth.
+ *
+ * @NL80211_ATTR_OPMODE_NOTIF: Operating mode field from Operating Mode
+ * Notification Element based on association request when used with
+ * %NL80211_CMD_NEW_STATION or %NL80211_CMD_SET_STATION (only when
+ * %NL80211_FEATURE_FULL_AP_CLIENT_STATE is supported, or with TDLS);
+ * u8 attribute.
+ *
+ * @NL80211_ATTR_VENDOR_ID: The vendor ID, either a 24-bit OUI or, if
+ * %NL80211_VENDOR_ID_IS_LINUX is set, a special Linux ID (not used yet)
+ * @NL80211_ATTR_VENDOR_SUBCMD: vendor sub-command
+ * @NL80211_ATTR_VENDOR_DATA: data for the vendor command, if any; this
+ * attribute is also used for vendor command feature advertisement
+ * @NL80211_ATTR_VENDOR_EVENTS: used for event list advertising in the wiphy
+ * info, containing a nested array of possible events
+ *
+ * @NL80211_ATTR_QOS_MAP: IP DSCP mapping for Interworking QoS mapping. This
+ * data is in the format defined for the payload of the QoS Map Set element
+ * in IEEE Std 802.11-2012, 8.4.2.97.
+ *
+ * @NL80211_ATTR_MAC_HINT: MAC address recommendation as initial BSS
+ * @NL80211_ATTR_WIPHY_FREQ_HINT: frequency of the recommended initial BSS
+ *
+ * @NL80211_ATTR_MAX_AP_ASSOC_STA: Device attribute that indicates how many
+ * associated stations are supported in AP mode (including P2P GO); u32.
+ * Since drivers may not have a fixed limit on the maximum number (e.g.,
+ * other concurrent operations may affect this), drivers are allowed to
+ * advertise values that cannot always be met. In such cases, an attempt
+ * to add a new station entry with @NL80211_CMD_NEW_STATION may fail.
+ *
+ * @NL80211_ATTR_CSA_C_OFFSETS_TX: An array of csa counter offsets (u16) which
+ * should be updated when the frame is transmitted.
+ * @NL80211_ATTR_MAX_CSA_COUNTERS: U8 attribute used to advertise the maximum
+ * supported number of csa counters.
+ *
+ * @NL80211_ATTR_TDLS_PEER_CAPABILITY: flags for TDLS peer capabilities, u32.
+ * As specified in the &enum nl80211_tdls_peer_capability.
+ *
+ * @NL80211_ATTR_SOCKET_OWNER: Flag attribute, if set during interface
+ * creation then the new interface will be owned by the netlink socket
+ * that created it and will be destroyed when the socket is closed.
+ * If set during scheduled scan start then the new scan req will be
+ * owned by the netlink socket that created it and the scheduled scan will
+ * be stopped when the socket is closed.
+ * If set during configuration of regulatory indoor operation then the
+ * regulatory indoor configuration would be owned by the netlink socket
+ * that configured the indoor setting, and the indoor operation would be
+ * cleared when the socket is closed.
+ * If set during NAN interface creation, the interface will be destroyed
+ * if the socket is closed just like any other interface. Moreover, NAN
+ * notifications will be sent in unicast to that socket. Without this
+ * attribute, the notifications will be sent to the %NL80211_MCGRP_NAN
+ * multicast group.
+ * If set during %NL80211_CMD_ASSOCIATE or %NL80211_CMD_CONNECT the
+ * station will deauthenticate when the socket is closed.
+ * If set during %NL80211_CMD_JOIN_IBSS the IBSS will be automatically
+ * torn down when the socket is closed.
+ * If set during %NL80211_CMD_JOIN_MESH the mesh setup will be
+ * automatically torn down when the socket is closed.
+ * If set during %NL80211_CMD_START_AP the AP will be automatically
+ * disabled when the socket is closed.
+ *
+ * @NL80211_ATTR_TDLS_INITIATOR: flag attribute indicating the current end is
+ * the TDLS link initiator.
+ *
+ * @NL80211_ATTR_USE_RRM: flag for indicating whether the current connection
+ * shall support Radio Resource Measurements (11k). This attribute can be
+ * used with %NL80211_CMD_ASSOCIATE and %NL80211_CMD_CONNECT requests.
+ * User space applications are expected to use this flag only if the
+ * underlying device supports these minimal RRM features:
+ * %NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES,
+ * %NL80211_FEATURE_QUIET,
+ * Or, if global RRM is supported, see:
+ * %NL80211_EXT_FEATURE_RRM
+ * If this flag is used, driver must add the Power Capabilities IE to the
+ * association request. In addition, it must also set the RRM capability
+ * flag in the association request's Capability Info field.
+ *
+ * @NL80211_ATTR_WIPHY_DYN_ACK: flag attribute used to enable ACK timeout
+ * estimation algorithm (dynack). In order to activate dynack
+ * %NL80211_FEATURE_ACKTO_ESTIMATION feature flag must be set by lower
+ * drivers to indicate dynack capability. Dynack is automatically disabled
+ * setting valid value for coverage class.
+ *
+ * @NL80211_ATTR_TSID: a TSID value (u8 attribute)
+ * @NL80211_ATTR_USER_PRIO: user priority value (u8 attribute)
+ * @NL80211_ATTR_ADMITTED_TIME: admitted time in units of 32 microseconds
+ * (per second) (u16 attribute)
+ *
+ * @NL80211_ATTR_SMPS_MODE: SMPS mode to use (ap mode). see
+ * &enum nl80211_smps_mode.
+ *
+ * @NL80211_ATTR_OPER_CLASS: operating class
+ *
+ * @NL80211_ATTR_MAC_MASK: MAC address mask
+ *
+ * @NL80211_ATTR_WIPHY_SELF_MANAGED_REG: flag attribute indicating this device
+ * is self-managing its regulatory information and any regulatory domain
+ * obtained from it is coming from the device's wiphy and not the global
+ * cfg80211 regdomain.
+ *
+ * @NL80211_ATTR_EXT_FEATURES: extended feature flags contained in a byte
+ * array. The feature flags are identified by their bit index (see &enum
+ * nl80211_ext_feature_index). The bit index is ordered starting at the
+ * least-significant bit of the first byte in the array, ie. bit index 0
+ * is located at bit 0 of byte 0. bit index 25 would be located at bit 1
+ * of byte 3 (u8 array).
+ *
+ * @NL80211_ATTR_SURVEY_RADIO_STATS: Request overall radio statistics to be
+ * returned along with other survey data. If set, @NL80211_CMD_GET_SURVEY
+ * may return a survey entry without a channel indicating global radio
+ * statistics (only some values are valid and make sense.)
+ * For devices that don't return such an entry even then, the information
+ * should be contained in the result as the sum of the respective counters
+ * over all channels.
+ *
+ * @NL80211_ATTR_SCHED_SCAN_DELAY: delay before the first cycle of a
+ * scheduled scan is started. Or the delay before a WoWLAN
+ * net-detect scan is started, counting from the moment the
+ * system is suspended. This value is a u32, in seconds.
+
+ * @NL80211_ATTR_REG_INDOOR: flag attribute, if set indicates that the device
+ * is operating in an indoor environment.
+ *
+ * @NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS: maximum number of scan plans for
+ * scheduled scan supported by the device (u32), a wiphy attribute.
+ * @NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL: maximum interval (in seconds) for
+ * a scan plan (u32), a wiphy attribute.
+ * @NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS: maximum number of iterations in
+ * a scan plan (u32), a wiphy attribute.
+ * @NL80211_ATTR_SCHED_SCAN_PLANS: a list of scan plans for scheduled scan.
+ * Each scan plan defines the number of scan iterations and the interval
+ * between scans. The last scan plan will always run infinitely,
+ * thus it must not specify the number of iterations, only the interval
+ * between scans. The scan plans are executed sequentially.
+ * Each scan plan is a nested attribute of &enum nl80211_sched_scan_plan.
+ * @NL80211_ATTR_PBSS: flag attribute. If set it means operate
+ * in a PBSS. Specified in %NL80211_CMD_CONNECT to request
+ * connecting to a PCP, and in %NL80211_CMD_START_AP to start
+ * a PCP instead of AP. Relevant for DMG networks only.
+ * @NL80211_ATTR_BSS_SELECT: nested attribute for driver supporting the
+ * BSS selection feature. When used with %NL80211_CMD_GET_WIPHY it contains
+ * attributes according &enum nl80211_bss_select_attr to indicate what
+ * BSS selection behaviours are supported. When used with %NL80211_CMD_CONNECT
+ * it contains the behaviour-specific attribute containing the parameters for
+ * BSS selection to be done by driver and/or firmware.
+ *
+ * @NL80211_ATTR_STA_SUPPORT_P2P_PS: whether P2P PS mechanism supported
+ * or not. u8, one of the values of &enum nl80211_sta_p2p_ps_status
+ *
+ * @NL80211_ATTR_PAD: attribute used for padding for 64-bit alignment
+ *
+ * @NL80211_ATTR_IFTYPE_EXT_CAPA: Nested attribute of the following attributes:
+ * %NL80211_ATTR_IFTYPE, %NL80211_ATTR_EXT_CAPA,
+ * %NL80211_ATTR_EXT_CAPA_MASK, to specify the extended capabilities and
+ * other interface-type specific capabilities per interface type. For MLO,
+ * %NL80211_ATTR_EML_CAPABILITY and %NL80211_ATTR_MLD_CAPA_AND_OPS are
+ * present.
+ *
+ * @NL80211_ATTR_MU_MIMO_GROUP_DATA: array of 24 bytes that defines a MU-MIMO
+ * groupID for monitor mode.
+ * The first 8 bytes are a mask that defines the membership in each
+ * group (there are 64 groups, group 0 and 63 are reserved),
+ * each bit represents a group and set to 1 for being a member in
+ * that group and 0 for not being a member.
+ * The remaining 16 bytes define the position in each group: 2 bits for
+ * each group.
+ * (smaller group numbers represented on most significant bits and bigger
+ * group numbers on least significant bits.)
+ * This attribute is used only if all interfaces are in monitor mode.
+ * Set this attribute in order to monitor packets using the given MU-MIMO
+ * groupID data.
+ * to turn off that feature set all the bits of the groupID to zero.
+ * @NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR: mac address for the sniffer to follow
+ * when using MU-MIMO air sniffer.
+ * to turn that feature off set an invalid mac address
+ * (e.g. FF:FF:FF:FF:FF:FF)
+ *
+ * @NL80211_ATTR_SCAN_START_TIME_TSF: The time at which the scan was actually
+ * started (u64). The time is the TSF of the BSS the interface that
+ * requested the scan is connected to (if available, otherwise this
+ * attribute must not be included).
+ * @NL80211_ATTR_SCAN_START_TIME_TSF_BSSID: The BSS according to which
+ * %NL80211_ATTR_SCAN_START_TIME_TSF is set.
+ * @NL80211_ATTR_MEASUREMENT_DURATION: measurement duration in TUs (u16). If
+ * %NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY is not set, this is the
+ * maximum measurement duration allowed. This attribute is used with
+ * measurement requests. It can also be used with %NL80211_CMD_TRIGGER_SCAN
+ * if the scan is used for beacon report radio measurement.
+ * @NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY: flag attribute that indicates
+ * that the duration specified with %NL80211_ATTR_MEASUREMENT_DURATION is
+ * mandatory. If this flag is not set, the duration is the maximum duration
+ * and the actual measurement duration may be shorter.
+ *
+ * @NL80211_ATTR_MESH_PEER_AID: Association ID for the mesh peer (u16). This is
+ * used to pull the stored data for mesh peer in power save state.
+ *
+ * @NL80211_ATTR_NAN_MASTER_PREF: the master preference to be used by
+ * %NL80211_CMD_START_NAN and optionally with
+ * %NL80211_CMD_CHANGE_NAN_CONFIG. Its type is u8 and it can't be 0.
+ * Also, values 1 and 255 are reserved for certification purposes and
+ * should not be used during a normal device operation.
+ * @NL80211_ATTR_BANDS: operating bands configuration. This is a u32
+ * bitmask of BIT(NL80211_BAND_*) as described in %enum
+ * nl80211_band. For instance, for NL80211_BAND_2GHZ, bit 0
+ * would be set. This attribute is used with
+ * %NL80211_CMD_START_NAN and %NL80211_CMD_CHANGE_NAN_CONFIG, and
+ * it is optional. If no bands are set, it means don't-care and
+ * the device will decide what to use.
+ * @NL80211_ATTR_NAN_FUNC: a function that can be added to NAN. See
+ * &enum nl80211_nan_func_attributes for description of this nested
+ * attribute.
+ * @NL80211_ATTR_NAN_MATCH: used to report a match. This is a nested attribute.
+ * See &enum nl80211_nan_match_attributes.
+ * @NL80211_ATTR_FILS_KEK: KEK for FILS (Re)Association Request/Response frame
+ * protection.
+ * @NL80211_ATTR_FILS_NONCES: Nonces (part of AAD) for FILS (Re)Association
+ * Request/Response frame protection. This attribute contains the 16 octet
+ * STA Nonce followed by 16 octets of AP Nonce.
+ *
+ * @NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED: Indicates whether or not multicast
+ * packets should be send out as unicast to all stations (flag attribute).
+ *
+ * @NL80211_ATTR_BSSID: The BSSID of the AP. Note that %NL80211_ATTR_MAC is also
+ * used in various commands/events for specifying the BSSID.
+ *
+ * @NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI: Relative RSSI threshold by which
+ * other BSSs has to be better or slightly worse than the current
+ * connected BSS so that they get reported to user space.
+ * This will give an opportunity to userspace to consider connecting to
+ * other matching BSSs which have better or slightly worse RSSI than
+ * the current connected BSS by using an offloaded operation to avoid
+ * unnecessary wakeups.
+ *
+ * @NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST: When present the RSSI level for BSSs in
+ * the specified band is to be adjusted before doing
+ * %NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI based comparison to figure out
+ * better BSSs. The attribute value is a packed structure
+ * value as specified by &struct nl80211_bss_select_rssi_adjust.
+ *
+ * @NL80211_ATTR_TIMEOUT_REASON: The reason for which an operation timed out.
+ * u32 attribute with an &enum nl80211_timeout_reason value. This is used,
+ * e.g., with %NL80211_CMD_CONNECT event.
+ *
+ * @NL80211_ATTR_FILS_ERP_USERNAME: EAP Re-authentication Protocol (ERP)
+ * username part of NAI used to refer keys rRK and rIK. This is used with
+ * %NL80211_CMD_CONNECT.
+ *
+ * @NL80211_ATTR_FILS_ERP_REALM: EAP Re-authentication Protocol (ERP) realm part
+ * of NAI specifying the domain name of the ER server. This is used with
+ * %NL80211_CMD_CONNECT.
+ *
+ * @NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM: Unsigned 16-bit ERP next sequence number
+ * to use in ERP messages. This is used in generating the FILS wrapped data
+ * for FILS authentication and is used with %NL80211_CMD_CONNECT.
+ *
+ * @NL80211_ATTR_FILS_ERP_RRK: ERP re-authentication Root Key (rRK) for the
+ * NAI specified by %NL80211_ATTR_FILS_ERP_USERNAME and
+ * %NL80211_ATTR_FILS_ERP_REALM. This is used for generating rIK and rMSK
+ * from successful FILS authentication and is used with
+ * %NL80211_CMD_CONNECT.
+ *
+ * @NL80211_ATTR_FILS_CACHE_ID: A 2-octet identifier advertized by a FILS AP
+ * identifying the scope of PMKSAs. This is used with
+ * @NL80211_CMD_SET_PMKSA and @NL80211_CMD_DEL_PMKSA.
+ *
+ * @NL80211_ATTR_PMK: attribute for passing PMK key material. Used with
+ * %NL80211_CMD_SET_PMKSA for the PMKSA identified by %NL80211_ATTR_PMKID.
+ * For %NL80211_CMD_CONNECT and %NL80211_CMD_START_AP it is used to provide
+ * PSK for offloading 4-way handshake for WPA/WPA2-PSK networks. For 802.1X
+ * authentication it is used with %NL80211_CMD_SET_PMK. For offloaded FT
+ * support this attribute specifies the PMK-R0 if NL80211_ATTR_PMKR0_NAME
+ * is included as well.
+ *
+ * @NL80211_ATTR_SCHED_SCAN_MULTI: flag attribute which user-space shall use to
+ * indicate that it supports multiple active scheduled scan requests.
+ * @NL80211_ATTR_SCHED_SCAN_MAX_REQS: indicates maximum number of scheduled
+ * scan request that may be active for the device (u32).
+ *
+ * @NL80211_ATTR_WANT_1X_4WAY_HS: flag attribute which user-space can include
+ * in %NL80211_CMD_CONNECT to indicate that for 802.1X authentication it
+ * wants to use the supported offload of the 4-way handshake.
+ * @NL80211_ATTR_PMKR0_NAME: PMK-R0 Name for offloaded FT.
+ * @NL80211_ATTR_PORT_AUTHORIZED: (reserved)
+ *
+ * @NL80211_ATTR_EXTERNAL_AUTH_ACTION: Identify the requested external
+ * authentication operation (u32 attribute with an
+ * &enum nl80211_external_auth_action value). This is used with the
+ * %NL80211_CMD_EXTERNAL_AUTH request event.
+ * @NL80211_ATTR_EXTERNAL_AUTH_SUPPORT: Flag attribute indicating that the user
+ * space supports external authentication. This attribute shall be used
+ * with %NL80211_CMD_CONNECT and %NL80211_CMD_START_AP request. The driver
+ * may offload authentication processing to user space if this capability
+ * is indicated in the respective requests from the user space. (This flag
+ * attribute deprecated for %NL80211_CMD_START_AP, use
+ * %NL80211_ATTR_AP_SETTINGS_FLAGS)
+ *
+ * @NL80211_ATTR_NSS: Station's New/updated RX_NSS value notified using this
+ * u8 attribute. This is used with %NL80211_CMD_STA_OPMODE_CHANGED.
+ *
+ * @NL80211_ATTR_TXQ_STATS: TXQ statistics (nested attribute, see &enum
+ * nl80211_txq_stats)
+ * @NL80211_ATTR_TXQ_LIMIT: Total packet limit for the TXQ queues for this phy.
+ * The smaller of this and the memory limit is enforced.
+ * @NL80211_ATTR_TXQ_MEMORY_LIMIT: Total memory limit (in bytes) for the
+ * TXQ queues for this phy. The smaller of this and the packet limit is
+ * enforced.
+ * @NL80211_ATTR_TXQ_QUANTUM: TXQ scheduler quantum (bytes). Number of bytes
+ * a flow is assigned on each round of the DRR scheduler.
+ * @NL80211_ATTR_HE_CAPABILITY: HE Capability information element (from
+ * association request when used with NL80211_CMD_NEW_STATION). Can be set
+ * only if %NL80211_STA_FLAG_WME is set.
+ *
+ * @NL80211_ATTR_FTM_RESPONDER: nested attribute which user-space can include
+ * in %NL80211_CMD_START_AP or %NL80211_CMD_SET_BEACON for fine timing
+ * measurement (FTM) responder functionality and containing parameters as
+ * possible, see &enum nl80211_ftm_responder_attr
+ *
+ * @NL80211_ATTR_FTM_RESPONDER_STATS: Nested attribute with FTM responder
+ * statistics, see &enum nl80211_ftm_responder_stats.
+ *
+ * @NL80211_ATTR_TIMEOUT: Timeout for the given operation in milliseconds (u32),
+ * if the attribute is not given no timeout is requested. Note that 0 is an
+ * invalid value.
+ *
+ * @NL80211_ATTR_PEER_MEASUREMENTS: peer measurements request (and result)
+ * data, uses nested attributes specified in
+ * &enum nl80211_peer_measurement_attrs.
+ * This is also used for capability advertisement in the wiphy information,
+ * with the appropriate sub-attributes.
+ *
+ * @NL80211_ATTR_AIRTIME_WEIGHT: Station's weight when scheduled by the airtime
+ * scheduler.
+ *
+ * @NL80211_ATTR_STA_TX_POWER_SETTING: Transmit power setting type (u8) for
+ * station associated with the AP. See &enum nl80211_tx_power_setting for
+ * possible values.
+ * @NL80211_ATTR_STA_TX_POWER: Transmit power level (s16) in dBm units. This
+ * allows to set Tx power for a station. If this attribute is not included,
+ * the default per-interface tx power setting will be overriding. Driver
+ * should be picking up the lowest tx power, either tx power per-interface
+ * or per-station.
+ *
+ * @NL80211_ATTR_SAE_PASSWORD: attribute for passing SAE password material. It
+ * is used with %NL80211_CMD_CONNECT to provide password for offloading
+ * SAE authentication for WPA3-Personal networks.
+ *
+ * @NL80211_ATTR_TWT_RESPONDER: Enable target wait time responder support.
+ *
+ * @NL80211_ATTR_HE_OBSS_PD: nested attribute for OBSS Packet Detection
+ * functionality.
+ *
+ * @NL80211_ATTR_WIPHY_EDMG_CHANNELS: bitmap that indicates the 2.16 GHz
+ * channel(s) that are allowed to be used for EDMG transmissions.
+ * Defined by IEEE P802.11ay/D4.0 section 9.4.2.251. (u8 attribute)
+ * @NL80211_ATTR_WIPHY_EDMG_BW_CONFIG: Channel BW Configuration subfield encodes
+ * the allowed channel bandwidth configurations. (u8 attribute)
+ * Defined by IEEE P802.11ay/D4.0 section 9.4.2.251, Table 13.
+ *
+ * @NL80211_ATTR_VLAN_ID: VLAN ID (1..4094) for the station and VLAN group key
+ * (u16).
+ *
+ * @NL80211_ATTR_HE_BSS_COLOR: nested attribute for BSS Color Settings.
+ *
+ * @NL80211_ATTR_IFTYPE_AKM_SUITES: nested array attribute, with each entry
+ * using attributes from &enum nl80211_iftype_akm_attributes. This
+ * attribute is sent in a response to %NL80211_CMD_GET_WIPHY indicating
+ * supported AKM suites capability per interface. AKMs advertised in
+ * %NL80211_ATTR_AKM_SUITES are default capabilities if AKM suites not
+ * advertised for a specific interface type.
+ *
+ * @NL80211_ATTR_TID_CONFIG: TID specific configuration in a
+ * nested attribute with &enum nl80211_tid_config_attr sub-attributes;
+ * on output (in wiphy attributes) it contains only the feature sub-
+ * attributes.
+ *
+ * @NL80211_ATTR_CONTROL_PORT_NO_PREAUTH: disable preauth frame rx on control
+ * port in order to forward/receive them as ordinary data frames.
+ *
+ * @NL80211_ATTR_PMK_LIFETIME: Maximum lifetime for PMKSA in seconds (u32,
+ * dot11RSNAConfigPMKReauthThreshold; 0 is not a valid value).
+ * An optional parameter configured through %NL80211_CMD_SET_PMKSA.
+ * Drivers that trigger roaming need to know the lifetime of the
+ * configured PMKSA for triggering the full vs. PMKSA caching based
+ * authentication. This timeout helps authentication methods like SAE,
+ * where PMK gets updated only by going through a full (new SAE)
+ * authentication instead of getting updated during an association for EAP
+ * authentication. No new full authentication within the PMK expiry shall
+ * result in a disassociation at the end of the lifetime.
+ *
+ * @NL80211_ATTR_PMK_REAUTH_THRESHOLD: Reauthentication threshold time, in
+ * terms of percentage of %NL80211_ATTR_PMK_LIFETIME
+ * (u8, dot11RSNAConfigPMKReauthThreshold, 1..100). This is an optional
+ * parameter configured through %NL80211_CMD_SET_PMKSA. Requests the
+ * driver to trigger a full authentication roam (without PMKSA caching)
+ * after the reauthentication threshold time, but before the PMK lifetime
+ * has expired.
+ *
+ * Authentication methods like SAE need to be able to generate a new PMKSA
+ * entry without having to force a disconnection after the PMK timeout. If
+ * no roaming occurs between the reauth threshold and PMK expiration,
+ * disassociation is still forced.
+ * @NL80211_ATTR_RECEIVE_MULTICAST: multicast flag for the
+ * %NL80211_CMD_REGISTER_FRAME command, see the description there.
+ * @NL80211_ATTR_WIPHY_FREQ_OFFSET: offset of the associated
+ * %NL80211_ATTR_WIPHY_FREQ in positive KHz. Only valid when supplied with
+ * an %NL80211_ATTR_WIPHY_FREQ_OFFSET.
+ * @NL80211_ATTR_CENTER_FREQ1_OFFSET: Center frequency offset in KHz for the
+ * first channel segment specified in %NL80211_ATTR_CENTER_FREQ1.
+ * @NL80211_ATTR_SCAN_FREQ_KHZ: nested attribute with KHz frequencies
+ *
+ * @NL80211_ATTR_HE_6GHZ_CAPABILITY: HE 6 GHz Band Capability element (from
+ * association request when used with NL80211_CMD_NEW_STATION).
+ *
+ * @NL80211_ATTR_FILS_DISCOVERY: Optional parameter to configure FILS
+ * discovery. It is a nested attribute, see
+ * &enum nl80211_fils_discovery_attributes.
+ *
+ * @NL80211_ATTR_UNSOL_BCAST_PROBE_RESP: Optional parameter to configure
+ * unsolicited broadcast probe response. It is a nested attribute, see
+ * &enum nl80211_unsol_bcast_probe_resp_attributes.
+ *
+ * @NL80211_ATTR_S1G_CAPABILITY: S1G Capability information element (from
+ * association request when used with NL80211_CMD_NEW_STATION)
+ * @NL80211_ATTR_S1G_CAPABILITY_MASK: S1G Capability Information element
+ * override mask. Used with NL80211_ATTR_S1G_CAPABILITY in
+ * NL80211_CMD_ASSOCIATE or NL80211_CMD_CONNECT.
+ *
+ * @NL80211_ATTR_SAE_PWE: Indicates the mechanism(s) allowed for SAE PWE
+ * derivation in WPA3-Personal networks which are using SAE authentication.
+ * This is a u8 attribute that encapsulates one of the values from
+ * &enum nl80211_sae_pwe_mechanism.
+ *
+ * @NL80211_ATTR_SAR_SPEC: SAR power limitation specification when
+ * used with %NL80211_CMD_SET_SAR_SPECS. The message contains fields
+ * of %nl80211_sar_attrs which specifies the sar type and related
+ * sar specs. Sar specs contains array of %nl80211_sar_specs_attrs.
+ *
+ * @NL80211_ATTR_RECONNECT_REQUESTED: flag attribute, used with deauth and
+ * disassoc events to indicate that an immediate reconnect to the AP
+ * is desired.
+ *
+ * @NL80211_ATTR_OBSS_COLOR_BITMAP: bitmap of the u64 BSS colors for the
+ * %NL80211_CMD_OBSS_COLOR_COLLISION event.
+ *
+ * @NL80211_ATTR_COLOR_CHANGE_COUNT: u8 attribute specifying the number of TBTT's
+ * until the color switch event.
+ * @NL80211_ATTR_COLOR_CHANGE_COLOR: u8 attribute specifying the color that we are
+ * switching to
+ * @NL80211_ATTR_COLOR_CHANGE_ELEMS: Nested set of attributes containing the IE
+ * information for the time while performing a color switch.
+ *
+ * @NL80211_ATTR_MBSSID_CONFIG: Nested attribute for multiple BSSID
+ * advertisements (MBSSID) parameters in AP mode.
+ * Kernel uses this attribute to indicate the driver's support for MBSSID
+ * and enhanced multi-BSSID advertisements (EMA AP) to the userspace.
+ * Userspace should use this attribute to configure per interface MBSSID
+ * parameters.
+ * See &enum nl80211_mbssid_config_attributes for details.
+ *
+ * @NL80211_ATTR_MBSSID_ELEMS: Nested parameter to pass multiple BSSID elements.
+ * Mandatory parameter for the transmitting interface to enable MBSSID.
+ * Optional for the non-transmitting interfaces.
+ *
+ * @NL80211_ATTR_RADAR_BACKGROUND: Configure dedicated offchannel chain
+ * available for radar/CAC detection on some hw. This chain can't be used
+ * to transmit or receive frames and it is bounded to a running wdev.
+ * Background radar/CAC detection allows to avoid the CAC downtime
+ * switching on a different channel during CAC detection on the selected
+ * radar channel.
+ *
+ * @NL80211_ATTR_AP_SETTINGS_FLAGS: u32 attribute contains ap settings flags,
+ * enumerated in &enum nl80211_ap_settings_flags. This attribute shall be
+ * used with %NL80211_CMD_START_AP request.
+ *
+ * @NL80211_ATTR_EHT_CAPABILITY: EHT Capability information element (from
+ * association request when used with NL80211_CMD_NEW_STATION). Can be set
+ * only if %NL80211_STA_FLAG_WME is set.
+ *
+ * @NL80211_ATTR_MLO_LINK_ID: A (u8) link ID for use with MLO, to be used with
+ * various commands that need a link ID to operate.
+ * @NL80211_ATTR_MLO_LINKS: A nested array of links, each containing some
+ * per-link information and a link ID.
+ * @NL80211_ATTR_MLD_ADDR: An MLD address, used with various commands such as
+ * authenticate/associate.
+ *
+ * @NL80211_ATTR_MLO_SUPPORT: Flag attribute to indicate user space supports MLO
+ * connection. Used with %NL80211_CMD_CONNECT. If this attribute is not
+ * included in NL80211_CMD_CONNECT drivers must not perform MLO connection.
+ *
+ * @NL80211_ATTR_MAX_NUM_AKM_SUITES: U16 attribute. Indicates maximum number of
+ * AKM suites allowed for %NL80211_CMD_CONNECT, %NL80211_CMD_ASSOCIATE and
+ * %NL80211_CMD_START_AP in %NL80211_CMD_GET_WIPHY response. If this
+ * attribute is not present userspace shall consider maximum number of AKM
+ * suites allowed as %NL80211_MAX_NR_AKM_SUITES which is the legacy maximum
+ * number prior to the introduction of this attribute.
+ *
+ * @NL80211_ATTR_EML_CAPABILITY: EML Capability information (u16)
+ * @NL80211_ATTR_MLD_CAPA_AND_OPS: MLD Capabilities and Operations (u16)
+ *
+ * @NL80211_ATTR_TX_HW_TIMESTAMP: Hardware timestamp for TX operation in
+ * nanoseconds (u64). This is the device clock timestamp so it will
+ * probably reset when the device is stopped or the firmware is reset.
+ * When used with %NL80211_CMD_FRAME_TX_STATUS, indicates the frame TX
+ * timestamp. When used with %NL80211_CMD_FRAME RX notification, indicates
+ * the ack TX timestamp.
+ * @NL80211_ATTR_RX_HW_TIMESTAMP: Hardware timestamp for RX operation in
+ * nanoseconds (u64). This is the device clock timestamp so it will
+ * probably reset when the device is stopped or the firmware is reset.
+ * When used with %NL80211_CMD_FRAME_TX_STATUS, indicates the ack RX
+ * timestamp. When used with %NL80211_CMD_FRAME RX notification, indicates
+ * the incoming frame RX timestamp.
+ * @NL80211_ATTR_TD_BITMAP: Transition Disable bitmap, for subsequent
+ * (re)associations.
+ *
+ * @NL80211_ATTR_PUNCT_BITMAP: (u32) Preamble puncturing bitmap, lowest
+ * bit corresponds to the lowest 20 MHz channel. Each bit set to 1
+ * indicates that the sub-channel is punctured. Higher 16 bits are
+ * reserved.
+ *
+ * @NL80211_ATTR_MAX_HW_TIMESTAMP_PEERS: Maximum number of peers that HW
+ * timestamping can be enabled for concurrently (u16), a wiphy attribute.
+ * A value of 0xffff indicates setting for all peers (i.e. not specifying
+ * an address with %NL80211_CMD_SET_HW_TIMESTAMP) is supported.
+ * @NL80211_ATTR_HW_TIMESTAMP_ENABLED: Indicates whether HW timestamping should
+ * be enabled or not (flag attribute).
+ *
+ * @NL80211_ATTR_EMA_RNR_ELEMS: Optional nested attribute for
+ * reduced neighbor report (RNR) elements. This attribute can be used
+ * only when NL80211_MBSSID_CONFIG_ATTR_EMA is enabled.
+ * Userspace is responsible for splitting the RNR into multiple
+ * elements such that each element excludes the non-transmitting
+ * profiles already included in the MBSSID element
+ * (%NL80211_ATTR_MBSSID_ELEMS) at the same index. Each EMA beacon
+ * will be generated by adding MBSSID and RNR elements at the same
+ * index. If the userspace includes more RNR elements than number of
+ * MBSSID elements then these will be added in every EMA beacon.
+ *
+ * @NUM_NL80211_ATTR: total number of nl80211_attrs available
+ * @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 between, 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_CONFIG,
+
+ 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,
+
+ NL80211_ATTR_MESH_SETUP,
+
+ NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX,
+ NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX,
+
+ NL80211_ATTR_SUPPORT_MESH_AUTH,
+ NL80211_ATTR_STA_PLINK_STATE,
+
+ NL80211_ATTR_WOWLAN_TRIGGERS,
+ NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED,
+
+ NL80211_ATTR_SCHED_SCAN_INTERVAL,
+
+ NL80211_ATTR_INTERFACE_COMBINATIONS,
+ NL80211_ATTR_SOFTWARE_IFTYPES,
+
+ NL80211_ATTR_REKEY_DATA,
+
+ NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS,
+ NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN,
+
+ NL80211_ATTR_SCAN_SUPP_RATES,
+
+ NL80211_ATTR_HIDDEN_SSID,
+
+ NL80211_ATTR_IE_PROBE_RESP,
+ NL80211_ATTR_IE_ASSOC_RESP,
+
+ NL80211_ATTR_STA_WME,
+ NL80211_ATTR_SUPPORT_AP_UAPSD,
+
+ NL80211_ATTR_ROAM_SUPPORT,
+
+ NL80211_ATTR_SCHED_SCAN_MATCH,
+ NL80211_ATTR_MAX_MATCH_SETS,
+
+ NL80211_ATTR_PMKSA_CANDIDATE,
+
+ NL80211_ATTR_TX_NO_CCK_RATE,
+
+ NL80211_ATTR_TDLS_ACTION,
+ NL80211_ATTR_TDLS_DIALOG_TOKEN,
+ NL80211_ATTR_TDLS_OPERATION,
+ NL80211_ATTR_TDLS_SUPPORT,
+ NL80211_ATTR_TDLS_EXTERNAL_SETUP,
+
+ NL80211_ATTR_DEVICE_AP_SME,
+
+ NL80211_ATTR_DONT_WAIT_FOR_ACK,
+
+ NL80211_ATTR_FEATURE_FLAGS,
+
+ NL80211_ATTR_PROBE_RESP_OFFLOAD,
+
+ NL80211_ATTR_PROBE_RESP,
+
+ NL80211_ATTR_DFS_REGION,
+
+ NL80211_ATTR_DISABLE_HT,
+ NL80211_ATTR_HT_CAPABILITY_MASK,
+
+ NL80211_ATTR_NOACK_MAP,
+
+ NL80211_ATTR_INACTIVITY_TIMEOUT,
+
+ NL80211_ATTR_RX_SIGNAL_DBM,
+
+ NL80211_ATTR_BG_SCAN_PERIOD,
+
+ NL80211_ATTR_WDEV,
+
+ NL80211_ATTR_USER_REG_HINT_TYPE,
+
+ NL80211_ATTR_CONN_FAILED_REASON,
+
+ NL80211_ATTR_AUTH_DATA,
+
+ NL80211_ATTR_VHT_CAPABILITY,
+
+ NL80211_ATTR_SCAN_FLAGS,
+
+ NL80211_ATTR_CHANNEL_WIDTH,
+ NL80211_ATTR_CENTER_FREQ1,
+ NL80211_ATTR_CENTER_FREQ2,
+
+ NL80211_ATTR_P2P_CTWINDOW,
+ NL80211_ATTR_P2P_OPPPS,
+
+ NL80211_ATTR_LOCAL_MESH_POWER_MODE,
+
+ NL80211_ATTR_ACL_POLICY,
+
+ NL80211_ATTR_MAC_ADDRS,
+
+ NL80211_ATTR_MAC_ACL_MAX,
+
+ NL80211_ATTR_RADAR_EVENT,
+
+ NL80211_ATTR_EXT_CAPA,
+ NL80211_ATTR_EXT_CAPA_MASK,
+
+ NL80211_ATTR_STA_CAPABILITY,
+ NL80211_ATTR_STA_EXT_CAPABILITY,
+
+ NL80211_ATTR_PROTOCOL_FEATURES,
+ NL80211_ATTR_SPLIT_WIPHY_DUMP,
+
+ NL80211_ATTR_DISABLE_VHT,
+ NL80211_ATTR_VHT_CAPABILITY_MASK,
+
+ NL80211_ATTR_MDID,
+ NL80211_ATTR_IE_RIC,
+
+ NL80211_ATTR_CRIT_PROT_ID,
+ NL80211_ATTR_MAX_CRIT_PROT_DURATION,
+
+ NL80211_ATTR_PEER_AID,
+
+ NL80211_ATTR_COALESCE_RULE,
+
+ NL80211_ATTR_CH_SWITCH_COUNT,
+ NL80211_ATTR_CH_SWITCH_BLOCK_TX,
+ NL80211_ATTR_CSA_IES,
+ NL80211_ATTR_CNTDWN_OFFS_BEACON,
+ NL80211_ATTR_CNTDWN_OFFS_PRESP,
+
+ NL80211_ATTR_RXMGMT_FLAGS,
+
+ NL80211_ATTR_STA_SUPPORTED_CHANNELS,
+
+ NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES,
+
+ NL80211_ATTR_HANDLE_DFS,
+
+ NL80211_ATTR_SUPPORT_5_MHZ,
+ NL80211_ATTR_SUPPORT_10_MHZ,
+
+ NL80211_ATTR_OPMODE_NOTIF,
+
+ NL80211_ATTR_VENDOR_ID,
+ NL80211_ATTR_VENDOR_SUBCMD,
+ NL80211_ATTR_VENDOR_DATA,
+ NL80211_ATTR_VENDOR_EVENTS,
+
+ NL80211_ATTR_QOS_MAP,
+
+ NL80211_ATTR_MAC_HINT,
+ NL80211_ATTR_WIPHY_FREQ_HINT,
+
+ NL80211_ATTR_MAX_AP_ASSOC_STA,
+
+ NL80211_ATTR_TDLS_PEER_CAPABILITY,
+
+ NL80211_ATTR_SOCKET_OWNER,
+
+ NL80211_ATTR_CSA_C_OFFSETS_TX,
+ NL80211_ATTR_MAX_CSA_COUNTERS,
+
+ NL80211_ATTR_TDLS_INITIATOR,
+
+ NL80211_ATTR_USE_RRM,
+
+ NL80211_ATTR_WIPHY_DYN_ACK,
+
+ NL80211_ATTR_TSID,
+ NL80211_ATTR_USER_PRIO,
+ NL80211_ATTR_ADMITTED_TIME,
+
+ NL80211_ATTR_SMPS_MODE,
+
+ NL80211_ATTR_OPER_CLASS,
+
+ NL80211_ATTR_MAC_MASK,
+
+ NL80211_ATTR_WIPHY_SELF_MANAGED_REG,
+
+ NL80211_ATTR_EXT_FEATURES,
+
+ NL80211_ATTR_SURVEY_RADIO_STATS,
+
+ NL80211_ATTR_NETNS_FD,
+
+ NL80211_ATTR_SCHED_SCAN_DELAY,
+
+ NL80211_ATTR_REG_INDOOR,
+
+ NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS,
+ NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL,
+ NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS,
+ NL80211_ATTR_SCHED_SCAN_PLANS,
+
+ NL80211_ATTR_PBSS,
+
+ NL80211_ATTR_BSS_SELECT,
+
+ NL80211_ATTR_STA_SUPPORT_P2P_PS,
+
+ NL80211_ATTR_PAD,
+
+ NL80211_ATTR_IFTYPE_EXT_CAPA,
+
+ NL80211_ATTR_MU_MIMO_GROUP_DATA,
+ NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR,
+
+ NL80211_ATTR_SCAN_START_TIME_TSF,
+ NL80211_ATTR_SCAN_START_TIME_TSF_BSSID,
+ NL80211_ATTR_MEASUREMENT_DURATION,
+ NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY,
+
+ NL80211_ATTR_MESH_PEER_AID,
+
+ NL80211_ATTR_NAN_MASTER_PREF,
+ NL80211_ATTR_BANDS,
+ NL80211_ATTR_NAN_FUNC,
+ NL80211_ATTR_NAN_MATCH,
+
+ NL80211_ATTR_FILS_KEK,
+ NL80211_ATTR_FILS_NONCES,
+
+ NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED,
+
+ NL80211_ATTR_BSSID,
+
+ NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI,
+ NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST,
+
+ NL80211_ATTR_TIMEOUT_REASON,
+
+ NL80211_ATTR_FILS_ERP_USERNAME,
+ NL80211_ATTR_FILS_ERP_REALM,
+ NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM,
+ NL80211_ATTR_FILS_ERP_RRK,
+ NL80211_ATTR_FILS_CACHE_ID,
+
+ NL80211_ATTR_PMK,
+
+ NL80211_ATTR_SCHED_SCAN_MULTI,
+ NL80211_ATTR_SCHED_SCAN_MAX_REQS,
+
+ NL80211_ATTR_WANT_1X_4WAY_HS,
+ NL80211_ATTR_PMKR0_NAME,
+ NL80211_ATTR_PORT_AUTHORIZED,
+
+ NL80211_ATTR_EXTERNAL_AUTH_ACTION,
+ NL80211_ATTR_EXTERNAL_AUTH_SUPPORT,
+
+ NL80211_ATTR_NSS,
+ NL80211_ATTR_ACK_SIGNAL,
+
+ NL80211_ATTR_CONTROL_PORT_OVER_NL80211,
+
+ NL80211_ATTR_TXQ_STATS,
+ NL80211_ATTR_TXQ_LIMIT,
+ NL80211_ATTR_TXQ_MEMORY_LIMIT,
+ NL80211_ATTR_TXQ_QUANTUM,
+
+ NL80211_ATTR_HE_CAPABILITY,
+
+ NL80211_ATTR_FTM_RESPONDER,
+
+ NL80211_ATTR_FTM_RESPONDER_STATS,
+
+ NL80211_ATTR_TIMEOUT,
+
+ NL80211_ATTR_PEER_MEASUREMENTS,
+
+ NL80211_ATTR_AIRTIME_WEIGHT,
+ NL80211_ATTR_STA_TX_POWER_SETTING,
+ NL80211_ATTR_STA_TX_POWER,
+
+ NL80211_ATTR_SAE_PASSWORD,
+
+ NL80211_ATTR_TWT_RESPONDER,
+
+ NL80211_ATTR_HE_OBSS_PD,
+
+ NL80211_ATTR_WIPHY_EDMG_CHANNELS,
+ NL80211_ATTR_WIPHY_EDMG_BW_CONFIG,
+
+ NL80211_ATTR_VLAN_ID,
+
+ NL80211_ATTR_HE_BSS_COLOR,
+
+ NL80211_ATTR_IFTYPE_AKM_SUITES,
+
+ NL80211_ATTR_TID_CONFIG,
+
+ NL80211_ATTR_CONTROL_PORT_NO_PREAUTH,
+
+ NL80211_ATTR_PMK_LIFETIME,
+ NL80211_ATTR_PMK_REAUTH_THRESHOLD,
+
+ NL80211_ATTR_RECEIVE_MULTICAST,
+ NL80211_ATTR_WIPHY_FREQ_OFFSET,
+ NL80211_ATTR_CENTER_FREQ1_OFFSET,
+ NL80211_ATTR_SCAN_FREQ_KHZ,
+
+ NL80211_ATTR_HE_6GHZ_CAPABILITY,
+
+ NL80211_ATTR_FILS_DISCOVERY,
+
+ NL80211_ATTR_UNSOL_BCAST_PROBE_RESP,
+
+ NL80211_ATTR_S1G_CAPABILITY,
+ NL80211_ATTR_S1G_CAPABILITY_MASK,
+
+ NL80211_ATTR_SAE_PWE,
+
+ NL80211_ATTR_RECONNECT_REQUESTED,
+
+ NL80211_ATTR_SAR_SPEC,
+
+ NL80211_ATTR_DISABLE_HE,
+
+ NL80211_ATTR_OBSS_COLOR_BITMAP,
+
+ NL80211_ATTR_COLOR_CHANGE_COUNT,
+ NL80211_ATTR_COLOR_CHANGE_COLOR,
+ NL80211_ATTR_COLOR_CHANGE_ELEMS,
+
+ NL80211_ATTR_MBSSID_CONFIG,
+ NL80211_ATTR_MBSSID_ELEMS,
+
+ NL80211_ATTR_RADAR_BACKGROUND,
+
+ NL80211_ATTR_AP_SETTINGS_FLAGS,
+
+ NL80211_ATTR_EHT_CAPABILITY,
+
+ NL80211_ATTR_DISABLE_EHT,
+
+ NL80211_ATTR_MLO_LINKS,
+ NL80211_ATTR_MLO_LINK_ID,
+ NL80211_ATTR_MLD_ADDR,
+
+ NL80211_ATTR_MLO_SUPPORT,
+
+ NL80211_ATTR_MAX_NUM_AKM_SUITES,
+
+ NL80211_ATTR_EML_CAPABILITY,
+ NL80211_ATTR_MLD_CAPA_AND_OPS,
+
+ NL80211_ATTR_TX_HW_TIMESTAMP,
+ NL80211_ATTR_RX_HW_TIMESTAMP,
+ NL80211_ATTR_TD_BITMAP,
+
+ NL80211_ATTR_PUNCT_BITMAP,
+
+ NL80211_ATTR_MAX_HW_TIMESTAMP_PEERS,
+ NL80211_ATTR_HW_TIMESTAMP_ENABLED,
+
+ NL80211_ATTR_EMA_RNR_ELEMS,
+
+ /* add attributes here, update the policy in nl80211.c */
+
+ __NL80211_ATTR_AFTER_LAST,
+ NUM_NL80211_ATTR = __NL80211_ATTR_AFTER_LAST,
+ NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1
+};
+
+/* source-level API compatibility */
+#define NL80211_ATTR_SCAN_GENERATION NL80211_ATTR_GENERATION
+#define NL80211_ATTR_MESH_PARAMS NL80211_ATTR_MESH_CONFIG
+#define NL80211_ATTR_IFACE_SOCKET_OWNER NL80211_ATTR_SOCKET_OWNER
+#define NL80211_ATTR_SAE_DATA NL80211_ATTR_AUTH_DATA
+#define NL80211_ATTR_CSA_C_OFF_BEACON NL80211_ATTR_CNTDWN_OFFS_BEACON
+#define NL80211_ATTR_CSA_C_OFF_PRESP NL80211_ATTR_CNTDWN_OFFS_PRESP
+
+/*
+ * 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_ATTR_FEATURE_FLAGS NL80211_ATTR_FEATURE_FLAGS
+
+#define NL80211_WIPHY_NAME_MAXLEN 64
+
+#define NL80211_MAX_SUPP_RATES 32
+#define NL80211_MAX_SUPP_HT_RATES 77
+#define NL80211_MAX_SUPP_REG_RULES 128
+#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_VHT_CAPABILITY_LEN 12
+#define NL80211_HE_MIN_CAPABILITY_LEN 16
+#define NL80211_HE_MAX_CAPABILITY_LEN 54
+#define NL80211_MAX_NR_CIPHER_SUITES 5
+
+/*
+ * NL80211_MAX_NR_AKM_SUITES is obsolete when %NL80211_ATTR_MAX_NUM_AKM_SUITES
+ * present in %NL80211_CMD_GET_WIPHY response.
+ */
+#define NL80211_MAX_NR_AKM_SUITES 2
+#define NL80211_EHT_MIN_CAPABILITY_LEN 13
+#define NL80211_EHT_MAX_CAPABILITY_LEN 51
+
+#define NL80211_MIN_REMAIN_ON_CHANNEL_TIME 10
+
+/* default RSSI threshold for scan results if none specified. */
+#define NL80211_SCAN_RSSI_THOLD_OFF -300
+
+#define NL80211_CQM_TXE_MAX_INTVL 1800
+
+/**
+ * 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; VLAN interfaces
+ * are a bit special in that they must always be tied to a pre-existing
+ * AP type interface.
+ * @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_P2P_DEVICE: P2P device interface type, this is not a netdev
+ * and therefore can't be created in the normal ways, use the
+ * %NL80211_CMD_START_P2P_DEVICE and %NL80211_CMD_STOP_P2P_DEVICE
+ * commands to create and destroy one
+ * @NL80211_IFTYPE_OCB: Outside Context of a BSS
+ * This mode corresponds to the MIB variable dot11OCBActivated=true
+ * @NL80211_IFTYPE_NAN: NAN device interface type (not a netdev)
+ * @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,
+ NL80211_IFTYPE_P2P_DEVICE,
+ NL80211_IFTYPE_OCB,
+ NL80211_IFTYPE_NAN,
+
+ /* 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_AUTHENTICATED: station is authenticated
+ * @NL80211_STA_FLAG_TDLS_PEER: station is a TDLS peer -- this flag should
+ * only be used in managed mode (even in the flags mask). Note that the
+ * flag can't be changed, it is only valid while adding a station, and
+ * attempts to change it will silently be ignored (rather than rejected
+ * as errors.)
+ * @NL80211_STA_FLAG_ASSOCIATED: station is associated; used with drivers
+ * that support %NL80211_FEATURE_FULL_AP_CLIENT_STATE to transition a
+ * previously added station into associated state
+ * @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,
+ NL80211_STA_FLAG_AUTHENTICATED,
+ NL80211_STA_FLAG_TDLS_PEER,
+ NL80211_STA_FLAG_ASSOCIATED,
+
+ /* keep last */
+ __NL80211_STA_FLAG_AFTER_LAST,
+ NL80211_STA_FLAG_MAX = __NL80211_STA_FLAG_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_sta_p2p_ps_status - station support of P2P PS
+ *
+ * @NL80211_P2P_PS_UNSUPPORTED: station doesn't support P2P PS mechanism
+ * @@NL80211_P2P_PS_SUPPORTED: station supports P2P PS mechanism
+ * @NUM_NL80211_P2P_PS_STATUS: number of values
+ */
+enum nl80211_sta_p2p_ps_status {
+ NL80211_P2P_PS_UNSUPPORTED = 0,
+ NL80211_P2P_PS_SUPPORTED,
+
+ NUM_NL80211_P2P_PS_STATUS,
+};
+
+#define NL80211_STA_FLAG_MAX_OLD_API NL80211_STA_FLAG_TDLS_PEER
+
+/**
+ * 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_he_gi - HE guard interval
+ * @NL80211_RATE_INFO_HE_GI_0_8: 0.8 usec
+ * @NL80211_RATE_INFO_HE_GI_1_6: 1.6 usec
+ * @NL80211_RATE_INFO_HE_GI_3_2: 3.2 usec
+ */
+enum nl80211_he_gi {
+ NL80211_RATE_INFO_HE_GI_0_8,
+ NL80211_RATE_INFO_HE_GI_1_6,
+ NL80211_RATE_INFO_HE_GI_3_2,
+};
+
+/**
+ * enum nl80211_he_ltf - HE long training field
+ * @NL80211_RATE_INFO_HE_1xLTF: 3.2 usec
+ * @NL80211_RATE_INFO_HE_2xLTF: 6.4 usec
+ * @NL80211_RATE_INFO_HE_4xLTF: 12.8 usec
+ */
+enum nl80211_he_ltf {
+ NL80211_RATE_INFO_HE_1XLTF,
+ NL80211_RATE_INFO_HE_2XLTF,
+ NL80211_RATE_INFO_HE_4XLTF,
+};
+
+/**
+ * enum nl80211_he_ru_alloc - HE RU allocation values
+ * @NL80211_RATE_INFO_HE_RU_ALLOC_26: 26-tone RU allocation
+ * @NL80211_RATE_INFO_HE_RU_ALLOC_52: 52-tone RU allocation
+ * @NL80211_RATE_INFO_HE_RU_ALLOC_106: 106-tone RU allocation
+ * @NL80211_RATE_INFO_HE_RU_ALLOC_242: 242-tone RU allocation
+ * @NL80211_RATE_INFO_HE_RU_ALLOC_484: 484-tone RU allocation
+ * @NL80211_RATE_INFO_HE_RU_ALLOC_996: 996-tone RU allocation
+ * @NL80211_RATE_INFO_HE_RU_ALLOC_2x996: 2x996-tone RU allocation
+ */
+enum nl80211_he_ru_alloc {
+ NL80211_RATE_INFO_HE_RU_ALLOC_26,
+ NL80211_RATE_INFO_HE_RU_ALLOC_52,
+ NL80211_RATE_INFO_HE_RU_ALLOC_106,
+ NL80211_RATE_INFO_HE_RU_ALLOC_242,
+ NL80211_RATE_INFO_HE_RU_ALLOC_484,
+ NL80211_RATE_INFO_HE_RU_ALLOC_996,
+ NL80211_RATE_INFO_HE_RU_ALLOC_2x996,
+};
+
+/**
+ * enum nl80211_eht_gi - EHT guard interval
+ * @NL80211_RATE_INFO_EHT_GI_0_8: 0.8 usec
+ * @NL80211_RATE_INFO_EHT_GI_1_6: 1.6 usec
+ * @NL80211_RATE_INFO_EHT_GI_3_2: 3.2 usec
+ */
+enum nl80211_eht_gi {
+ NL80211_RATE_INFO_EHT_GI_0_8,
+ NL80211_RATE_INFO_EHT_GI_1_6,
+ NL80211_RATE_INFO_EHT_GI_3_2,
+};
+
+/**
+ * enum nl80211_eht_ru_alloc - EHT RU allocation values
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_26: 26-tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_52: 52-tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_52P26: 52+26-tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_106: 106-tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_106P26: 106+26 tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_242: 242-tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_484: 484-tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_484P242: 484+242 tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_996: 996-tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_996P484: 996+484 tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_996P484P242: 996+484+242 tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_2x996: 2x996-tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_2x996P484: 2x996+484 tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_3x996: 3x996-tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_3x996P484: 3x996+484 tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_4x996: 4x996-tone RU allocation
+ */
+enum nl80211_eht_ru_alloc {
+ NL80211_RATE_INFO_EHT_RU_ALLOC_26,
+ NL80211_RATE_INFO_EHT_RU_ALLOC_52,
+ NL80211_RATE_INFO_EHT_RU_ALLOC_52P26,
+ NL80211_RATE_INFO_EHT_RU_ALLOC_106,
+ NL80211_RATE_INFO_EHT_RU_ALLOC_106P26,
+ NL80211_RATE_INFO_EHT_RU_ALLOC_242,
+ NL80211_RATE_INFO_EHT_RU_ALLOC_484,
+ NL80211_RATE_INFO_EHT_RU_ALLOC_484P242,
+ NL80211_RATE_INFO_EHT_RU_ALLOC_996,
+ NL80211_RATE_INFO_EHT_RU_ALLOC_996P484,
+ NL80211_RATE_INFO_EHT_RU_ALLOC_996P484P242,
+ NL80211_RATE_INFO_EHT_RU_ALLOC_2x996,
+ NL80211_RATE_INFO_EHT_RU_ALLOC_2x996P484,
+ NL80211_RATE_INFO_EHT_RU_ALLOC_3x996,
+ NL80211_RATE_INFO_EHT_RU_ALLOC_3x996P484,
+ NL80211_RATE_INFO_EHT_RU_ALLOC_4x996,
+};
+
+/**
+ * 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.
+ * There are 2 attributes for bitrate, a legacy one that represents
+ * a 16-bit value, and new one that represents a 32-bit value.
+ * If the rate value fits into 16 bit, both attributes are reported
+ * with the same value. If the rate is too high to fit into 16 bits
+ * (>6.5535Gbps) only 32-bit attribute is included.
+ * User space tools encouraged to use the 32-bit attribute and fall
+ * back to the 16-bit one for compatibility with older kernels.
+ *
+ * @__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_BITRATE32: total bitrate (u32, 100kbit/s)
+ * @NL80211_RATE_INFO_MAX: highest rate_info number currently defined
+ * @NL80211_RATE_INFO_VHT_MCS: MCS index for VHT (u8)
+ * @NL80211_RATE_INFO_VHT_NSS: number of streams in VHT (u8)
+ * @NL80211_RATE_INFO_80_MHZ_WIDTH: 80 MHz VHT rate
+ * @NL80211_RATE_INFO_80P80_MHZ_WIDTH: unused - 80+80 is treated the
+ * same as 160 for purposes of the bitrates
+ * @NL80211_RATE_INFO_160_MHZ_WIDTH: 160 MHz VHT rate
+ * @NL80211_RATE_INFO_10_MHZ_WIDTH: 10 MHz width - note that this is
+ * a legacy rate and will be reported as the actual bitrate, i.e.
+ * half the base (20 MHz) rate
+ * @NL80211_RATE_INFO_5_MHZ_WIDTH: 5 MHz width - note that this is
+ * a legacy rate and will be reported as the actual bitrate, i.e.
+ * a quarter of the base (20 MHz) rate
+ * @NL80211_RATE_INFO_HE_MCS: HE MCS index (u8, 0-11)
+ * @NL80211_RATE_INFO_HE_NSS: HE NSS value (u8, 1-8)
+ * @NL80211_RATE_INFO_HE_GI: HE guard interval identifier
+ * (u8, see &enum nl80211_he_gi)
+ * @NL80211_RATE_INFO_HE_DCM: HE DCM value (u8, 0/1)
+ * @NL80211_RATE_INFO_RU_ALLOC: HE RU allocation, if not present then
+ * non-OFDMA was used (u8, see &enum nl80211_he_ru_alloc)
+ * @NL80211_RATE_INFO_320_MHZ_WIDTH: 320 MHz bitrate
+ * @NL80211_RATE_INFO_EHT_MCS: EHT MCS index (u8, 0-15)
+ * @NL80211_RATE_INFO_EHT_NSS: EHT NSS value (u8, 1-8)
+ * @NL80211_RATE_INFO_EHT_GI: EHT guard interval identifier
+ * (u8, see &enum nl80211_eht_gi)
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC: EHT RU allocation, if not present then
+ * non-OFDMA was used (u8, see &enum nl80211_eht_ru_alloc)
+ * @__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,
+ NL80211_RATE_INFO_BITRATE32,
+ NL80211_RATE_INFO_VHT_MCS,
+ NL80211_RATE_INFO_VHT_NSS,
+ NL80211_RATE_INFO_80_MHZ_WIDTH,
+ NL80211_RATE_INFO_80P80_MHZ_WIDTH,
+ NL80211_RATE_INFO_160_MHZ_WIDTH,
+ NL80211_RATE_INFO_10_MHZ_WIDTH,
+ NL80211_RATE_INFO_5_MHZ_WIDTH,
+ NL80211_RATE_INFO_HE_MCS,
+ NL80211_RATE_INFO_HE_NSS,
+ NL80211_RATE_INFO_HE_GI,
+ NL80211_RATE_INFO_HE_DCM,
+ NL80211_RATE_INFO_HE_RU_ALLOC,
+ NL80211_RATE_INFO_320_MHZ_WIDTH,
+ NL80211_RATE_INFO_EHT_MCS,
+ NL80211_RATE_INFO_EHT_NSS,
+ NL80211_RATE_INFO_EHT_GI,
+ NL80211_RATE_INFO_EHT_RU_ALLOC,
+
+ /* keep last */
+ __NL80211_RATE_INFO_AFTER_LAST,
+ NL80211_RATE_INFO_MAX = __NL80211_RATE_INFO_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_sta_bss_param - BSS information collected by STA
+ *
+ * These attribute types are used with %NL80211_STA_INFO_BSS_PARAM
+ * when getting information about the bitrate of a station.
+ *
+ * @__NL80211_STA_BSS_PARAM_INVALID: attribute number 0 is reserved
+ * @NL80211_STA_BSS_PARAM_CTS_PROT: whether CTS protection is enabled (flag)
+ * @NL80211_STA_BSS_PARAM_SHORT_PREAMBLE: whether short preamble is enabled
+ * (flag)
+ * @NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME: whether short slot time is enabled
+ * (flag)
+ * @NL80211_STA_BSS_PARAM_DTIM_PERIOD: DTIM period for beaconing (u8)
+ * @NL80211_STA_BSS_PARAM_BEACON_INTERVAL: Beacon interval (u16)
+ * @NL80211_STA_BSS_PARAM_MAX: highest sta_bss_param number currently defined
+ * @__NL80211_STA_BSS_PARAM_AFTER_LAST: internal use
+ */
+enum nl80211_sta_bss_param {
+ __NL80211_STA_BSS_PARAM_INVALID,
+ NL80211_STA_BSS_PARAM_CTS_PROT,
+ NL80211_STA_BSS_PARAM_SHORT_PREAMBLE,
+ NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME,
+ NL80211_STA_BSS_PARAM_DTIM_PERIOD,
+ NL80211_STA_BSS_PARAM_BEACON_INTERVAL,
+
+ /* keep last */
+ __NL80211_STA_BSS_PARAM_AFTER_LAST,
+ NL80211_STA_BSS_PARAM_MAX = __NL80211_STA_BSS_PARAM_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 (MPDU length)
+ * (u32, from this station)
+ * @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (MPDU length)
+ * (u32, to this station)
+ * @NL80211_STA_INFO_RX_BYTES64: total received bytes (MPDU length)
+ * (u64, from this station)
+ * @NL80211_STA_INFO_TX_BYTES64: total transmitted bytes (MPDU length)
+ * (u64, to this station)
+ * @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_rate_info
+ * @NL80211_STA_INFO_RX_PACKETS: total received packet (MSDUs and MMPDUs)
+ * (u32, from this station)
+ * @NL80211_STA_INFO_TX_PACKETS: total transmitted packets (MSDUs and MMPDUs)
+ * (u32, to this station)
+ * @NL80211_STA_INFO_TX_RETRIES: total retries (MPDUs) (u32, to this station)
+ * @NL80211_STA_INFO_TX_FAILED: total failed packets (MPDUs)
+ * (u32, to this station)
+ * @NL80211_STA_INFO_SIGNAL_AVG: signal strength average (u8, dBm)
+ * @NL80211_STA_INFO_LLID: the station's mesh LLID
+ * @NL80211_STA_INFO_PLID: the station's mesh PLID
+ * @NL80211_STA_INFO_PLINK_STATE: peer link state for the station
+ * (see %enum nl80211_plink_state)
+ * @NL80211_STA_INFO_RX_BITRATE: last unicast data frame rx rate, nested
+ * attribute, like NL80211_STA_INFO_TX_BITRATE.
+ * @NL80211_STA_INFO_BSS_PARAM: current station's view of BSS, nested attribute
+ * containing info as possible, see &enum nl80211_sta_bss_param
+ * @NL80211_STA_INFO_CONNECTED_TIME: time since the station is last connected
+ * @NL80211_STA_INFO_STA_FLAGS: Contains a struct nl80211_sta_flag_update.
+ * @NL80211_STA_INFO_BEACON_LOSS: count of times beacon loss was detected (u32)
+ * @NL80211_STA_INFO_T_OFFSET: timing offset with respect to this STA (s64)
+ * @NL80211_STA_INFO_LOCAL_PM: local mesh STA link-specific power mode
+ * @NL80211_STA_INFO_PEER_PM: peer mesh STA link-specific power mode
+ * @NL80211_STA_INFO_NONPEER_PM: neighbor mesh STA power save mode towards
+ * non-peer STA
+ * @NL80211_STA_INFO_CHAIN_SIGNAL: per-chain signal strength of last PPDU
+ * Contains a nested array of signal strength attributes (u8, dBm)
+ * @NL80211_STA_INFO_CHAIN_SIGNAL_AVG: per-chain signal strength average
+ * Same format as NL80211_STA_INFO_CHAIN_SIGNAL.
+ * @NL80211_STA_EXPECTED_THROUGHPUT: expected throughput considering also the
+ * 802.11 header (u32, kbps)
+ * @NL80211_STA_INFO_RX_DROP_MISC: RX packets dropped for unspecified reasons
+ * (u64)
+ * @NL80211_STA_INFO_BEACON_RX: number of beacons received from this peer (u64)
+ * @NL80211_STA_INFO_BEACON_SIGNAL_AVG: signal strength average
+ * for beacons only (u8, dBm)
+ * @NL80211_STA_INFO_TID_STATS: per-TID statistics (see &enum nl80211_tid_stats)
+ * This is a nested attribute where each the inner attribute number is the
+ * TID+1 and the special TID 16 (i.e. value 17) is used for non-QoS frames;
+ * each one of those is again nested with &enum nl80211_tid_stats
+ * attributes carrying the actual values.
+ * @NL80211_STA_INFO_RX_DURATION: aggregate PPDU duration for all frames
+ * received from the station (u64, usec)
+ * @NL80211_STA_INFO_PAD: attribute used for padding for 64-bit alignment
+ * @NL80211_STA_INFO_ACK_SIGNAL: signal strength of the last ACK frame(u8, dBm)
+ * @NL80211_STA_INFO_ACK_SIGNAL_AVG: avg signal strength of ACK frames (s8, dBm)
+ * @NL80211_STA_INFO_RX_MPDUS: total number of received packets (MPDUs)
+ * (u32, from this station)
+ * @NL80211_STA_INFO_FCS_ERROR_COUNT: total number of packets (MPDUs) received
+ * with an FCS error (u32, from this station). This count may not include
+ * some packets with an FCS error due to TA corruption. Hence this counter
+ * might not be fully accurate.
+ * @NL80211_STA_INFO_CONNECTED_TO_GATE: set to true if STA has a path to a
+ * mesh gate (u8, 0 or 1)
+ * @NL80211_STA_INFO_TX_DURATION: aggregate PPDU duration for all frames
+ * sent to the station (u64, usec)
+ * @NL80211_STA_INFO_AIRTIME_WEIGHT: current airtime weight for station (u16)
+ * @NL80211_STA_INFO_AIRTIME_LINK_METRIC: airtime link metric for mesh station
+ * @NL80211_STA_INFO_ASSOC_AT_BOOTTIME: Timestamp (CLOCK_BOOTTIME, nanoseconds)
+ * of STA's association
+ * @NL80211_STA_INFO_CONNECTED_TO_AS: set to true if STA has a path to a
+ * authentication server (u8, 0 or 1)
+ * @__NL80211_STA_INFO_AFTER_LAST: internal
+ * @NL80211_STA_INFO_MAX: highest possible station info attribute
+ */
+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,
+ NL80211_STA_INFO_RX_BITRATE,
+ NL80211_STA_INFO_BSS_PARAM,
+ NL80211_STA_INFO_CONNECTED_TIME,
+ NL80211_STA_INFO_STA_FLAGS,
+ NL80211_STA_INFO_BEACON_LOSS,
+ NL80211_STA_INFO_T_OFFSET,
+ NL80211_STA_INFO_LOCAL_PM,
+ NL80211_STA_INFO_PEER_PM,
+ NL80211_STA_INFO_NONPEER_PM,
+ NL80211_STA_INFO_RX_BYTES64,
+ NL80211_STA_INFO_TX_BYTES64,
+ NL80211_STA_INFO_CHAIN_SIGNAL,
+ NL80211_STA_INFO_CHAIN_SIGNAL_AVG,
+ NL80211_STA_INFO_EXPECTED_THROUGHPUT,
+ NL80211_STA_INFO_RX_DROP_MISC,
+ NL80211_STA_INFO_BEACON_RX,
+ NL80211_STA_INFO_BEACON_SIGNAL_AVG,
+ NL80211_STA_INFO_TID_STATS,
+ NL80211_STA_INFO_RX_DURATION,
+ NL80211_STA_INFO_PAD,
+ NL80211_STA_INFO_ACK_SIGNAL,
+ NL80211_STA_INFO_ACK_SIGNAL_AVG,
+ NL80211_STA_INFO_RX_MPDUS,
+ NL80211_STA_INFO_FCS_ERROR_COUNT,
+ NL80211_STA_INFO_CONNECTED_TO_GATE,
+ NL80211_STA_INFO_TX_DURATION,
+ NL80211_STA_INFO_AIRTIME_WEIGHT,
+ NL80211_STA_INFO_AIRTIME_LINK_METRIC,
+ NL80211_STA_INFO_ASSOC_AT_BOOTTIME,
+ NL80211_STA_INFO_CONNECTED_TO_AS,
+
+ /* keep last */
+ __NL80211_STA_INFO_AFTER_LAST,
+ NL80211_STA_INFO_MAX = __NL80211_STA_INFO_AFTER_LAST - 1
+};
+
+/* we renamed this - stay compatible */
+#define NL80211_STA_INFO_DATA_ACK_SIGNAL_AVG NL80211_STA_INFO_ACK_SIGNAL_AVG
+
+
+/**
+ * enum nl80211_tid_stats - per TID statistics attributes
+ * @__NL80211_TID_STATS_INVALID: attribute number 0 is reserved
+ * @NL80211_TID_STATS_RX_MSDU: number of MSDUs received (u64)
+ * @NL80211_TID_STATS_TX_MSDU: number of MSDUs transmitted (or
+ * attempted to transmit; u64)
+ * @NL80211_TID_STATS_TX_MSDU_RETRIES: number of retries for
+ * transmitted MSDUs (not counting the first attempt; u64)
+ * @NL80211_TID_STATS_TX_MSDU_FAILED: number of failed transmitted
+ * MSDUs (u64)
+ * @NL80211_TID_STATS_PAD: attribute used for padding for 64-bit alignment
+ * @NL80211_TID_STATS_TXQ_STATS: TXQ stats (nested attribute)
+ * @NUM_NL80211_TID_STATS: number of attributes here
+ * @NL80211_TID_STATS_MAX: highest numbered attribute here
+ */
+enum nl80211_tid_stats {
+ __NL80211_TID_STATS_INVALID,
+ NL80211_TID_STATS_RX_MSDU,
+ NL80211_TID_STATS_TX_MSDU,
+ NL80211_TID_STATS_TX_MSDU_RETRIES,
+ NL80211_TID_STATS_TX_MSDU_FAILED,
+ NL80211_TID_STATS_PAD,
+ NL80211_TID_STATS_TXQ_STATS,
+
+ /* keep last */
+ NUM_NL80211_TID_STATS,
+ NL80211_TID_STATS_MAX = NUM_NL80211_TID_STATS - 1
+};
+
+/**
+ * enum nl80211_txq_stats - per TXQ statistics attributes
+ * @__NL80211_TXQ_STATS_INVALID: attribute number 0 is reserved
+ * @NUM_NL80211_TXQ_STATS: number of attributes here
+ * @NL80211_TXQ_STATS_BACKLOG_BYTES: number of bytes currently backlogged
+ * @NL80211_TXQ_STATS_BACKLOG_PACKETS: number of packets currently
+ * backlogged
+ * @NL80211_TXQ_STATS_FLOWS: total number of new flows seen
+ * @NL80211_TXQ_STATS_DROPS: total number of packet drops
+ * @NL80211_TXQ_STATS_ECN_MARKS: total number of packet ECN marks
+ * @NL80211_TXQ_STATS_OVERLIMIT: number of drops due to queue space overflow
+ * @NL80211_TXQ_STATS_OVERMEMORY: number of drops due to memory limit overflow
+ * (only for per-phy stats)
+ * @NL80211_TXQ_STATS_COLLISIONS: number of hash collisions
+ * @NL80211_TXQ_STATS_TX_BYTES: total number of bytes dequeued from TXQ
+ * @NL80211_TXQ_STATS_TX_PACKETS: total number of packets dequeued from TXQ
+ * @NL80211_TXQ_STATS_MAX_FLOWS: number of flow buckets for PHY
+ * @NL80211_TXQ_STATS_MAX: highest numbered attribute here
+ */
+enum nl80211_txq_stats {
+ __NL80211_TXQ_STATS_INVALID,
+ NL80211_TXQ_STATS_BACKLOG_BYTES,
+ NL80211_TXQ_STATS_BACKLOG_PACKETS,
+ NL80211_TXQ_STATS_FLOWS,
+ NL80211_TXQ_STATS_DROPS,
+ NL80211_TXQ_STATS_ECN_MARKS,
+ NL80211_TXQ_STATS_OVERLIMIT,
+ NL80211_TXQ_STATS_OVERMEMORY,
+ NL80211_TXQ_STATS_COLLISIONS,
+ NL80211_TXQ_STATS_TX_BYTES,
+ NL80211_TXQ_STATS_TX_PACKETS,
+ NL80211_TXQ_STATS_MAX_FLOWS,
+
+ /* keep last */
+ NUM_NL80211_TXQ_STATS,
+ NL80211_TXQ_STATS_MAX = NUM_NL80211_TXQ_STATS - 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_HOP_COUNT: hop count to destination
+ * @NL80211_MPATH_INFO_PATH_CHANGE: total number of path changes to destination
+ * @NL80211_MPATH_INFO_MAX: highest mesh path information attribute number
+ * currently defined
+ * @__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,
+ NL80211_MPATH_INFO_HOP_COUNT,
+ NL80211_MPATH_INFO_PATH_CHANGE,
+
+ /* keep last */
+ __NL80211_MPATH_INFO_AFTER_LAST,
+ NL80211_MPATH_INFO_MAX = __NL80211_MPATH_INFO_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_band_iftype_attr - Interface type data attributes
+ *
+ * @__NL80211_BAND_IFTYPE_ATTR_INVALID: attribute number 0 is reserved
+ * @NL80211_BAND_IFTYPE_ATTR_IFTYPES: nested attribute containing a flag attribute
+ * for each interface type that supports the band data
+ * @NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC: HE MAC capabilities as in HE
+ * capabilities IE
+ * @NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY: HE PHY capabilities as in HE
+ * capabilities IE
+ * @NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET: HE supported NSS/MCS as in HE
+ * capabilities IE
+ * @NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE: HE PPE thresholds information as
+ * defined in HE capabilities IE
+ * @NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA: HE 6GHz band capabilities (__le16),
+ * given for all 6 GHz band channels
+ * @NL80211_BAND_IFTYPE_ATTR_VENDOR_ELEMS: vendor element capabilities that are
+ * advertised on this band/for this iftype (binary)
+ * @NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC: EHT MAC capabilities as in EHT
+ * capabilities element
+ * @NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY: EHT PHY capabilities as in EHT
+ * capabilities element
+ * @NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MCS_SET: EHT supported NSS/MCS as in EHT
+ * capabilities element
+ * @NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PPE: EHT PPE thresholds information as
+ * defined in EHT capabilities element
+ * @__NL80211_BAND_IFTYPE_ATTR_AFTER_LAST: internal use
+ * @NL80211_BAND_IFTYPE_ATTR_MAX: highest band attribute currently defined
+ */
+enum nl80211_band_iftype_attr {
+ __NL80211_BAND_IFTYPE_ATTR_INVALID,
+
+ NL80211_BAND_IFTYPE_ATTR_IFTYPES,
+ NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC,
+ NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY,
+ NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET,
+ NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE,
+ NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA,
+ NL80211_BAND_IFTYPE_ATTR_VENDOR_ELEMS,
+ NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC,
+ NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY,
+ NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MCS_SET,
+ NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PPE,
+
+ /* keep last */
+ __NL80211_BAND_IFTYPE_ATTR_AFTER_LAST,
+ NL80211_BAND_IFTYPE_ATTR_MAX = __NL80211_BAND_IFTYPE_ATTR_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_VHT_MCS_SET: 32-byte attribute containing the MCS set as
+ * defined in 802.11ac
+ * @NL80211_BAND_ATTR_VHT_CAPA: VHT capabilities, as in the HT information IE
+ * @NL80211_BAND_ATTR_IFTYPE_DATA: nested array attribute, with each entry using
+ * attributes from &enum nl80211_band_iftype_attr
+ * @NL80211_BAND_ATTR_EDMG_CHANNELS: bitmap that indicates the 2.16 GHz
+ * channel(s) that are allowed to be used for EDMG transmissions.
+ * Defined by IEEE P802.11ay/D4.0 section 9.4.2.251.
+ * @NL80211_BAND_ATTR_EDMG_BW_CONFIG: Channel BW Configuration subfield encodes
+ * the allowed channel bandwidth configurations.
+ * Defined by IEEE P802.11ay/D4.0 section 9.4.2.251, Table 13.
+ * @NL80211_BAND_ATTR_S1G_MCS_NSS_SET: S1G capabilities, supported S1G-MCS and NSS
+ * set subfield, as in the S1G information IE, 5 bytes
+ * @NL80211_BAND_ATTR_S1G_CAPA: S1G capabilities information subfield as in the
+ * S1G information IE, 10 bytes
+ * @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,
+
+ NL80211_BAND_ATTR_VHT_MCS_SET,
+ NL80211_BAND_ATTR_VHT_CAPA,
+ NL80211_BAND_ATTR_IFTYPE_DATA,
+
+ NL80211_BAND_ATTR_EDMG_CHANNELS,
+ NL80211_BAND_ATTR_EDMG_BW_CONFIG,
+
+ NL80211_BAND_ATTR_S1G_MCS_NSS_SET,
+ NL80211_BAND_ATTR_S1G_CAPA,
+
+ /* 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_wmm_rule - regulatory wmm rule
+ *
+ * @__NL80211_WMMR_INVALID: attribute number 0 is reserved
+ * @NL80211_WMMR_CW_MIN: Minimum contention window slot.
+ * @NL80211_WMMR_CW_MAX: Maximum contention window slot.
+ * @NL80211_WMMR_AIFSN: Arbitration Inter Frame Space.
+ * @NL80211_WMMR_TXOP: Maximum allowed tx operation time.
+ * @nl80211_WMMR_MAX: highest possible wmm rule.
+ * @__NL80211_WMMR_LAST: Internal use.
+ */
+enum nl80211_wmm_rule {
+ __NL80211_WMMR_INVALID,
+ NL80211_WMMR_CW_MIN,
+ NL80211_WMMR_CW_MAX,
+ NL80211_WMMR_AIFSN,
+ NL80211_WMMR_TXOP,
+
+ /* keep last */
+ __NL80211_WMMR_LAST,
+ NL80211_WMMR_MAX = __NL80211_WMMR_LAST - 1
+};
+
+/**
+ * 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_NO_IR: no mechanisms that initiate radiation
+ * are permitted on this channel, this includes sending probe
+ * requests, or modes of operation that require beaconing.
+ * @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_DFS_STATE: current state for DFS
+ * (enum nl80211_dfs_state)
+ * @NL80211_FREQUENCY_ATTR_DFS_TIME: time in miliseconds for how long
+ * this channel is in this DFS state.
+ * @NL80211_FREQUENCY_ATTR_NO_HT40_MINUS: HT40- isn't possible with this
+ * channel as the control channel
+ * @NL80211_FREQUENCY_ATTR_NO_HT40_PLUS: HT40+ isn't possible with this
+ * channel as the control channel
+ * @NL80211_FREQUENCY_ATTR_NO_80MHZ: any 80 MHz channel using this channel
+ * as the primary or any of the secondary channels isn't possible,
+ * this includes 80+80 channels
+ * @NL80211_FREQUENCY_ATTR_NO_160MHZ: any 160 MHz (but not 80+80) channel
+ * using this channel as the primary or any of the secondary channels
+ * isn't possible
+ * @NL80211_FREQUENCY_ATTR_DFS_CAC_TIME: DFS CAC time in milliseconds.
+ * @NL80211_FREQUENCY_ATTR_INDOOR_ONLY: Only indoor use is permitted on this
+ * channel. A channel that has the INDOOR_ONLY attribute can only be
+ * used when there is a clear assessment that the device is operating in
+ * an indoor surroundings, i.e., it is connected to AC power (and not
+ * through portable DC inverters) or is under the control of a master
+ * that is acting as an AP and is connected to AC power.
+ * @NL80211_FREQUENCY_ATTR_IR_CONCURRENT: IR operation is allowed on this
+ * channel if it's connected concurrently to a BSS on the same channel on
+ * the 2 GHz band or to a channel in the same UNII band (on the 5 GHz
+ * band), and IEEE80211_CHAN_RADAR is not set. Instantiating a GO or TDLS
+ * off-channel on a channel that has the IR_CONCURRENT attribute set can be
+ * done when there is a clear assessment that the device is operating under
+ * the guidance of an authorized master, i.e., setting up a GO or TDLS
+ * off-channel while the device is also connected to an AP with DFS and
+ * radar detection on the UNII band (it is up to user-space, i.e.,
+ * wpa_supplicant to perform the required verifications). Using this
+ * attribute for IR is disallowed for master interfaces (IBSS, AP).
+ * @NL80211_FREQUENCY_ATTR_NO_20MHZ: 20 MHz operation is not allowed
+ * on this channel in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_NO_10MHZ: 10 MHz operation is not allowed
+ * on this channel in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_WMM: this channel has wmm limitations.
+ * This is a nested attribute that contains the wmm limitation per AC.
+ * (see &enum nl80211_wmm_rule)
+ * @NL80211_FREQUENCY_ATTR_NO_HE: HE operation is not allowed on this channel
+ * in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_OFFSET: frequency offset in KHz
+ * @NL80211_FREQUENCY_ATTR_1MHZ: 1 MHz operation is allowed
+ * on this channel in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_2MHZ: 2 MHz operation is allowed
+ * on this channel in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_4MHZ: 4 MHz operation is allowed
+ * on this channel in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_8MHZ: 8 MHz operation is allowed
+ * on this channel in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_16MHZ: 16 MHz operation is allowed
+ * on this channel in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_NO_320MHZ: any 320 MHz channel using this channel
+ * as the primary or any of the secondary channels isn't possible
+ * @NL80211_FREQUENCY_ATTR_NO_EHT: EHT operation is not allowed on this channel
+ * in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number
+ * currently defined
+ * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use
+ *
+ * See https://apps.fcc.gov/eas/comments/GetPublishedDocument.html?id=327&tn=528122
+ * for more information on the FCC description of the relaxations allowed
+ * by NL80211_FREQUENCY_ATTR_INDOOR_ONLY and
+ * NL80211_FREQUENCY_ATTR_IR_CONCURRENT.
+ */
+enum nl80211_frequency_attr {
+ __NL80211_FREQUENCY_ATTR_INVALID,
+ NL80211_FREQUENCY_ATTR_FREQ,
+ NL80211_FREQUENCY_ATTR_DISABLED,
+ NL80211_FREQUENCY_ATTR_NO_IR,
+ __NL80211_FREQUENCY_ATTR_NO_IBSS,
+ NL80211_FREQUENCY_ATTR_RADAR,
+ NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
+ NL80211_FREQUENCY_ATTR_DFS_STATE,
+ NL80211_FREQUENCY_ATTR_DFS_TIME,
+ NL80211_FREQUENCY_ATTR_NO_HT40_MINUS,
+ NL80211_FREQUENCY_ATTR_NO_HT40_PLUS,
+ NL80211_FREQUENCY_ATTR_NO_80MHZ,
+ NL80211_FREQUENCY_ATTR_NO_160MHZ,
+ NL80211_FREQUENCY_ATTR_DFS_CAC_TIME,
+ NL80211_FREQUENCY_ATTR_INDOOR_ONLY,
+ NL80211_FREQUENCY_ATTR_IR_CONCURRENT,
+ NL80211_FREQUENCY_ATTR_NO_20MHZ,
+ NL80211_FREQUENCY_ATTR_NO_10MHZ,
+ NL80211_FREQUENCY_ATTR_WMM,
+ NL80211_FREQUENCY_ATTR_NO_HE,
+ NL80211_FREQUENCY_ATTR_OFFSET,
+ NL80211_FREQUENCY_ATTR_1MHZ,
+ NL80211_FREQUENCY_ATTR_2MHZ,
+ NL80211_FREQUENCY_ATTR_4MHZ,
+ NL80211_FREQUENCY_ATTR_8MHZ,
+ NL80211_FREQUENCY_ATTR_16MHZ,
+ NL80211_FREQUENCY_ATTR_NO_320MHZ,
+ NL80211_FREQUENCY_ATTR_NO_EHT,
+
+ /* 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
+#define NL80211_FREQUENCY_ATTR_PASSIVE_SCAN NL80211_FREQUENCY_ATTR_NO_IR
+#define NL80211_FREQUENCY_ATTR_NO_IBSS NL80211_FREQUENCY_ATTR_NO_IR
+#define NL80211_FREQUENCY_ATTR_NO_IR NL80211_FREQUENCY_ATTR_NO_IR
+#define NL80211_FREQUENCY_ATTR_GO_CONCURRENT \
+ NL80211_FREQUENCY_ATTR_IR_CONCURRENT
+
+/**
+ * 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 passed 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_ATTR_DFS_CAC_TIME: DFS CAC time in milliseconds.
+ * If not present or 0 default CAC time will be used.
+ * @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,
+
+ NL80211_ATTR_DFS_CAC_TIME,
+
+ /* keep last */
+ __NL80211_REG_RULE_ATTR_AFTER_LAST,
+ NL80211_REG_RULE_ATTR_MAX = __NL80211_REG_RULE_ATTR_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_sched_scan_match_attr - scheduled scan match attributes
+ * @__NL80211_SCHED_SCAN_MATCH_ATTR_INVALID: attribute number 0 is reserved
+ * @NL80211_SCHED_SCAN_MATCH_ATTR_SSID: SSID to be used for matching,
+ * only report BSS with matching SSID.
+ * (This cannot be used together with BSSID.)
+ * @NL80211_SCHED_SCAN_MATCH_ATTR_RSSI: RSSI threshold (in dBm) for reporting a
+ * BSS in scan results. Filtering is turned off if not specified. Note that
+ * if this attribute is in a match set of its own, then it is treated as
+ * the default value for all matchsets with an SSID, rather than being a
+ * matchset of its own without an RSSI filter. This is due to problems with
+ * how this API was implemented in the past. Also, due to the same problem,
+ * the only way to create a matchset with only an RSSI filter (with this
+ * attribute) is if there's only a single matchset with the RSSI attribute.
+ * @NL80211_SCHED_SCAN_MATCH_ATTR_RELATIVE_RSSI: Flag indicating whether
+ * %NL80211_SCHED_SCAN_MATCH_ATTR_RSSI to be used as absolute RSSI or
+ * relative to current bss's RSSI.
+ * @NL80211_SCHED_SCAN_MATCH_ATTR_RSSI_ADJUST: When present the RSSI level for
+ * BSS-es in the specified band is to be adjusted before doing
+ * RSSI-based BSS selection. The attribute value is a packed structure
+ * value as specified by &struct nl80211_bss_select_rssi_adjust.
+ * @NL80211_SCHED_SCAN_MATCH_ATTR_BSSID: BSSID to be used for matching
+ * (this cannot be used together with SSID).
+ * @NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI: Nested attribute that carries the
+ * band specific minimum rssi thresholds for the bands defined in
+ * enum nl80211_band. The minimum rssi threshold value(s32) specific to a
+ * band shall be encapsulated in attribute with type value equals to one
+ * of the NL80211_BAND_* defined in enum nl80211_band. For example, the
+ * minimum rssi threshold value for 2.4GHZ band shall be encapsulated
+ * within an attribute of type NL80211_BAND_2GHZ. And one or more of such
+ * attributes will be nested within this attribute.
+ * @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter
+ * attribute number currently defined
+ * @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use
+ */
+enum nl80211_sched_scan_match_attr {
+ __NL80211_SCHED_SCAN_MATCH_ATTR_INVALID,
+
+ NL80211_SCHED_SCAN_MATCH_ATTR_SSID,
+ NL80211_SCHED_SCAN_MATCH_ATTR_RSSI,
+ NL80211_SCHED_SCAN_MATCH_ATTR_RELATIVE_RSSI,
+ NL80211_SCHED_SCAN_MATCH_ATTR_RSSI_ADJUST,
+ NL80211_SCHED_SCAN_MATCH_ATTR_BSSID,
+ NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI,
+
+ /* keep last */
+ __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST,
+ NL80211_SCHED_SCAN_MATCH_ATTR_MAX =
+ __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST - 1
+};
+
+/* only for backward compatibility */
+#define NL80211_ATTR_SCHED_SCAN_MATCH_SSID NL80211_SCHED_SCAN_MATCH_ATTR_SSID
+
+/**
+ * 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_NO_IR: no mechanisms that initiate radiation are allowed,
+ * this includes probe requests or modes of operation that require
+ * beaconing.
+ * @NL80211_RRF_AUTO_BW: maximum available bandwidth should be calculated
+ * base on contiguous rules and wider channels will be allowed to cross
+ * multiple contiguous/overlapping frequency ranges.
+ * @NL80211_RRF_IR_CONCURRENT: See %NL80211_FREQUENCY_ATTR_IR_CONCURRENT
+ * @NL80211_RRF_NO_HT40MINUS: channels can't be used in HT40- operation
+ * @NL80211_RRF_NO_HT40PLUS: channels can't be used in HT40+ operation
+ * @NL80211_RRF_NO_80MHZ: 80MHz operation not allowed
+ * @NL80211_RRF_NO_160MHZ: 160MHz operation not allowed
+ * @NL80211_RRF_NO_HE: HE operation not allowed
+ * @NL80211_RRF_NO_320MHZ: 320MHz operation not 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_NO_IR = 1<<7,
+ __NL80211_RRF_NO_IBSS = 1<<8,
+ NL80211_RRF_AUTO_BW = 1<<11,
+ NL80211_RRF_IR_CONCURRENT = 1<<12,
+ NL80211_RRF_NO_HT40MINUS = 1<<13,
+ NL80211_RRF_NO_HT40PLUS = 1<<14,
+ NL80211_RRF_NO_80MHZ = 1<<15,
+ NL80211_RRF_NO_160MHZ = 1<<16,
+ NL80211_RRF_NO_HE = 1<<17,
+ NL80211_RRF_NO_320MHZ = 1<<18,
+};
+
+#define NL80211_RRF_PASSIVE_SCAN NL80211_RRF_NO_IR
+#define NL80211_RRF_NO_IBSS NL80211_RRF_NO_IR
+#define NL80211_RRF_NO_IR NL80211_RRF_NO_IR
+#define NL80211_RRF_NO_HT40 (NL80211_RRF_NO_HT40MINUS |\
+ NL80211_RRF_NO_HT40PLUS)
+#define NL80211_RRF_GO_CONCURRENT NL80211_RRF_IR_CONCURRENT
+
+/* For backport compatibility with older userspace */
+#define NL80211_RRF_NO_IR_ALL (NL80211_RRF_NO_IR | __NL80211_RRF_NO_IBSS)
+
+/**
+ * enum nl80211_dfs_regions - regulatory DFS regions
+ *
+ * @NL80211_DFS_UNSET: Country has no DFS master region specified
+ * @NL80211_DFS_FCC: Country follows DFS master rules from FCC
+ * @NL80211_DFS_ETSI: Country follows DFS master rules from ETSI
+ * @NL80211_DFS_JP: Country follows DFS master rules from JP/MKK/Telec
+ */
+enum nl80211_dfs_regions {
+ NL80211_DFS_UNSET = 0,
+ NL80211_DFS_FCC = 1,
+ NL80211_DFS_ETSI = 2,
+ NL80211_DFS_JP = 3,
+};
+
+/**
+ * enum nl80211_user_reg_hint_type - type of user regulatory hint
+ *
+ * @NL80211_USER_REG_HINT_USER: a user sent the hint. This is always
+ * assumed if the attribute is not set.
+ * @NL80211_USER_REG_HINT_CELL_BASE: the hint comes from a cellular
+ * base station. Device drivers that have been tested to work
+ * properly to support this type of hint can enable these hints
+ * by setting the NL80211_FEATURE_CELL_BASE_REG_HINTS feature
+ * capability on the struct wiphy. The wireless core will
+ * ignore all cell base station hints until at least one device
+ * present has been registered with the wireless core that
+ * has listed NL80211_FEATURE_CELL_BASE_REG_HINTS as a
+ * supported feature.
+ * @NL80211_USER_REG_HINT_INDOOR: a user sent an hint indicating that the
+ * platform is operating in an indoor environment.
+ */
+enum nl80211_user_reg_hint_type {
+ NL80211_USER_REG_HINT_USER = 0,
+ NL80211_USER_REG_HINT_CELL_BASE = 1,
+ NL80211_USER_REG_HINT_INDOOR = 2,
+};
+
+/**
+ * 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_TIME: amount of time (in ms) that the radio
+ * was turned on (on channel or globally)
+ * @NL80211_SURVEY_INFO_TIME_BUSY: amount of the time the primary
+ * channel was sensed busy (either due to activity or energy detect)
+ * @NL80211_SURVEY_INFO_TIME_EXT_BUSY: amount of time the extension
+ * channel was sensed busy
+ * @NL80211_SURVEY_INFO_TIME_RX: amount of time the radio spent
+ * receiving data (on channel or globally)
+ * @NL80211_SURVEY_INFO_TIME_TX: amount of time the radio spent
+ * transmitting data (on channel or globally)
+ * @NL80211_SURVEY_INFO_TIME_SCAN: time the radio spent for scan
+ * (on this channel or globally)
+ * @NL80211_SURVEY_INFO_PAD: attribute used for padding for 64-bit alignment
+ * @NL80211_SURVEY_INFO_TIME_BSS_RX: amount of time the radio spent
+ * receiving frames destined to the local BSS
+ * @NL80211_SURVEY_INFO_MAX: highest survey info attribute number
+ * currently defined
+ * @NL80211_SURVEY_INFO_FREQUENCY_OFFSET: center frequency offset in KHz
+ * @__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_TIME,
+ NL80211_SURVEY_INFO_TIME_BUSY,
+ NL80211_SURVEY_INFO_TIME_EXT_BUSY,
+ NL80211_SURVEY_INFO_TIME_RX,
+ NL80211_SURVEY_INFO_TIME_TX,
+ NL80211_SURVEY_INFO_TIME_SCAN,
+ NL80211_SURVEY_INFO_PAD,
+ NL80211_SURVEY_INFO_TIME_BSS_RX,
+ NL80211_SURVEY_INFO_FREQUENCY_OFFSET,
+
+ /* keep last */
+ __NL80211_SURVEY_INFO_AFTER_LAST,
+ NL80211_SURVEY_INFO_MAX = __NL80211_SURVEY_INFO_AFTER_LAST - 1
+};
+
+/* keep old names for compatibility */
+#define NL80211_SURVEY_INFO_CHANNEL_TIME NL80211_SURVEY_INFO_TIME
+#define NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY NL80211_SURVEY_INFO_TIME_BUSY
+#define NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY NL80211_SURVEY_INFO_TIME_EXT_BUSY
+#define NL80211_SURVEY_INFO_CHANNEL_TIME_RX NL80211_SURVEY_INFO_TIME_RX
+#define NL80211_SURVEY_INFO_CHANNEL_TIME_TX NL80211_SURVEY_INFO_TIME_TX
+
+/**
+ * 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_ACTIVE: use the configured MAC address
+ * and ACK incoming unicast packets.
+ *
+ * @__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,
+ NL80211_MNTR_FLAG_ACTIVE,
+
+ /* keep last */
+ __NL80211_MNTR_FLAG_AFTER_LAST,
+ NL80211_MNTR_FLAG_MAX = __NL80211_MNTR_FLAG_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_mesh_power_mode - mesh power save modes
+ *
+ * @NL80211_MESH_POWER_UNKNOWN: The mesh power mode of the mesh STA is
+ * not known or has not been set yet.
+ * @NL80211_MESH_POWER_ACTIVE: Active mesh power mode. The mesh STA is
+ * in Awake state all the time.
+ * @NL80211_MESH_POWER_LIGHT_SLEEP: Light sleep mode. The mesh STA will
+ * alternate between Active and Doze states, but will wake up for
+ * neighbor's beacons.
+ * @NL80211_MESH_POWER_DEEP_SLEEP: Deep sleep mode. The mesh STA will
+ * alternate between Active and Doze states, but may not wake up
+ * for neighbor's beacons.
+ *
+ * @__NL80211_MESH_POWER_AFTER_LAST - internal use
+ * @NL80211_MESH_POWER_MAX - highest possible power save level
+ */
+
+enum nl80211_mesh_power_mode {
+ NL80211_MESH_POWER_UNKNOWN,
+ NL80211_MESH_POWER_ACTIVE,
+ NL80211_MESH_POWER_LIGHT_SLEEP,
+ NL80211_MESH_POWER_DEEP_SLEEP,
+
+ __NL80211_MESH_POWER_AFTER_LAST,
+ NL80211_MESH_POWER_MAX = __NL80211_MESH_POWER_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_meshconf_params - mesh configuration parameters
+ *
+ * Mesh configuration parameters. These can be changed while the mesh is
+ * active.
+ *
+ * @__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 initial confirm timeout, in
+ * millisecond units, used by the peer link management to close a peer link
+ *
+ * @NL80211_MESHCONF_HOLDING_TIMEOUT: specifies the holding timeout, in
+ * millisecond units
+ *
+ * @NL80211_MESHCONF_MAX_PEER_LINKS: maximum number of peer links allowed
+ * on this mesh interface
+ *
+ * @NL80211_MESHCONF_MAX_RETRIES: specifies the maximum number of peer link
+ * open retries that can be sent to establish a new peer link instance in a
+ * mesh
+ *
+ * @NL80211_MESHCONF_TTL: specifies the value of TTL field set at a source mesh
+ * point.
+ *
+ * @NL80211_MESHCONF_AUTO_OPEN_PLINKS: whether we should automatically open
+ * peer links when we detect compatible mesh peers. Disabled if
+ * @NL80211_MESH_SETUP_USERSPACE_MPM or @NL80211_MESH_SETUP_USERSPACE_AMPE are
+ * set.
+ *
+ * @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_HWMP_ROOTMODE: whether root mode is enabled or not
+ *
+ * @NL80211_MESHCONF_ELEMENT_TTL: specifies the value of TTL field set at a
+ * source mesh point for path selection elements.
+ *
+ * @NL80211_MESHCONF_HWMP_RANN_INTERVAL: The interval of time (in TUs) between
+ * root announcements are transmitted.
+ *
+ * @NL80211_MESHCONF_GATE_ANNOUNCEMENTS: Advertise that this mesh station has
+ * access to a broader network beyond the MBSS. This is done via Root
+ * Announcement frames.
+ *
+ * @NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL: The minimum interval of time (in
+ * TUs) during which a mesh STA can send only one Action frame containing a
+ * PERR element.
+ *
+ * @NL80211_MESHCONF_FORWARDING: set Mesh STA as forwarding or non-forwarding
+ * or forwarding entity (default is TRUE - forwarding entity)
+ *
+ * @NL80211_MESHCONF_RSSI_THRESHOLD: RSSI threshold in dBm. This specifies the
+ * threshold for average signal strength of candidate station to establish
+ * a peer link.
+ *
+ * @NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR: maximum number of neighbors
+ * to synchronize to for 11s default synchronization method
+ * (see 11C.12.2.2)
+ *
+ * @NL80211_MESHCONF_HT_OPMODE: set mesh HT protection mode.
+ *
+ * @NL80211_MESHCONF_ATTR_MAX: highest possible mesh configuration attribute
+ *
+ * @NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT: The time (in TUs) for
+ * which mesh STAs receiving a proactive PREQ shall consider the forwarding
+ * information to the root mesh STA to be valid.
+ *
+ * @NL80211_MESHCONF_HWMP_ROOT_INTERVAL: The interval of time (in TUs) between
+ * proactive PREQs are transmitted.
+ *
+ * @NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL: The minimum interval of time
+ * (in TUs) during which a mesh STA can send only one Action frame
+ * containing a PREQ element for root path confirmation.
+ *
+ * @NL80211_MESHCONF_POWER_MODE: Default mesh power mode for new peer links.
+ * type &enum nl80211_mesh_power_mode (u32)
+ *
+ * @NL80211_MESHCONF_AWAKE_WINDOW: awake window duration (in TUs)
+ *
+ * @NL80211_MESHCONF_PLINK_TIMEOUT: If no tx activity is seen from a STA we've
+ * established peering with for longer than this time (in seconds), then
+ * remove it from the STA's list of peers. You may set this to 0 to disable
+ * the removal of the STA. Default is 30 minutes.
+ *
+ * @NL80211_MESHCONF_CONNECTED_TO_GATE: If set to true then this mesh STA
+ * will advertise that it is connected to a gate in the mesh formation
+ * field. If left unset then the mesh formation field will only
+ * advertise such if there is an active root mesh path.
+ *
+ * @NL80211_MESHCONF_NOLEARN: Try to avoid multi-hop path discovery (e.g.
+ * PREQ/PREP for HWMP) if the destination is a direct neighbor. Note that
+ * this might not be the optimal decision as a multi-hop route might be
+ * better. So if using this setting you will likely also want to disable
+ * dot11MeshForwarding and use another mesh routing protocol on top.
+ *
+ * @NL80211_MESHCONF_CONNECTED_TO_AS: If set to true then this mesh STA
+ * will advertise that it is connected to a authentication server
+ * in the mesh formation field.
+ *
+ * @__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,
+ NL80211_MESHCONF_HWMP_RANN_INTERVAL,
+ NL80211_MESHCONF_GATE_ANNOUNCEMENTS,
+ NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL,
+ NL80211_MESHCONF_FORWARDING,
+ NL80211_MESHCONF_RSSI_THRESHOLD,
+ NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR,
+ NL80211_MESHCONF_HT_OPMODE,
+ NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT,
+ NL80211_MESHCONF_HWMP_ROOT_INTERVAL,
+ NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL,
+ NL80211_MESHCONF_POWER_MODE,
+ NL80211_MESHCONF_AWAKE_WINDOW,
+ NL80211_MESHCONF_PLINK_TIMEOUT,
+ NL80211_MESHCONF_CONNECTED_TO_GATE,
+ NL80211_MESHCONF_NOLEARN,
+ NL80211_MESHCONF_CONNECTED_TO_AS,
+
+ /* keep last */
+ __NL80211_MESHCONF_ATTR_AFTER_LAST,
+ NL80211_MESHCONF_ATTR_MAX = __NL80211_MESHCONF_ATTR_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_mesh_setup_params - mesh setup parameters
+ *
+ * Mesh setup parameters. These are used to start/join a mesh and cannot be
+ * changed while the mesh is active.
+ *
+ * @__NL80211_MESH_SETUP_INVALID: Internal use
+ *
+ * @NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL: Enable this option to use a
+ * vendor specific path selection algorithm or disable it to use the
+ * default HWMP.
+ *
+ * @NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC: Enable this option to use a
+ * vendor specific path metric or disable it to use the default Airtime
+ * metric.
+ *
+ * @NL80211_MESH_SETUP_IE: Information elements for this mesh, for instance, a
+ * robust security network ie, or a vendor specific information element
+ * that vendors will use to identify the path selection methods and
+ * metrics in use.
+ *
+ * @NL80211_MESH_SETUP_USERSPACE_AUTH: Enable this option if an authentication
+ * daemon will be authenticating mesh candidates.
+ *
+ * @NL80211_MESH_SETUP_USERSPACE_AMPE: Enable this option if an authentication
+ * daemon will be securing peer link frames. AMPE is a secured version of
+ * Mesh Peering Management (MPM) and is implemented with the assistance of
+ * a userspace daemon. When this flag is set, the kernel will send peer
+ * management frames to a userspace daemon that will implement AMPE
+ * functionality (security capabilities selection, key confirmation, and
+ * key management). When the flag is unset (default), the kernel can
+ * autonomously complete (unsecured) mesh peering without the need of a
+ * userspace daemon.
+ *
+ * @NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC: Enable this option to use a
+ * vendor specific synchronization method or disable it to use the default
+ * neighbor offset synchronization
+ *
+ * @NL80211_MESH_SETUP_USERSPACE_MPM: Enable this option if userspace will
+ * implement an MPM which handles peer allocation and state.
+ *
+ * @NL80211_MESH_SETUP_AUTH_PROTOCOL: Inform the kernel of the authentication
+ * method (u8, as defined in IEEE 8.4.2.100.6, e.g. 0x1 for SAE).
+ * Default is no authentication method required.
+ *
+ * @NL80211_MESH_SETUP_ATTR_MAX: highest possible mesh setup attribute number
+ *
+ * @__NL80211_MESH_SETUP_ATTR_AFTER_LAST: Internal use
+ */
+enum nl80211_mesh_setup_params {
+ __NL80211_MESH_SETUP_INVALID,
+ NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL,
+ NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC,
+ NL80211_MESH_SETUP_IE,
+ NL80211_MESH_SETUP_USERSPACE_AUTH,
+ NL80211_MESH_SETUP_USERSPACE_AMPE,
+ NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC,
+ NL80211_MESH_SETUP_USERSPACE_MPM,
+ NL80211_MESH_SETUP_AUTH_PROTOCOL,
+
+ /* keep last */
+ __NL80211_MESH_SETUP_ATTR_AFTER_LAST,
+ NL80211_MESH_SETUP_ATTR_MAX = __NL80211_MESH_SETUP_ATTR_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_txq_attr - TX queue parameter attributes
+ * @__NL80211_TXQ_ATTR_INVALID: Attribute number 0 is reserved
+ * @NL80211_TXQ_ATTR_AC: AC identifier (NL80211_AC_*)
+ * @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_AC,
+ 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_ac {
+ NL80211_AC_VO,
+ NL80211_AC_VI,
+ NL80211_AC_BE,
+ NL80211_AC_BK,
+ NL80211_NUM_ACS
+};
+
+/* backward compat */
+#define NL80211_TXQ_ATTR_QUEUE NL80211_TXQ_ATTR_AC
+#define NL80211_TXQ_Q_VO NL80211_AC_VO
+#define NL80211_TXQ_Q_VI NL80211_AC_VI
+#define NL80211_TXQ_Q_BE NL80211_AC_BE
+#define NL80211_TXQ_Q_BK NL80211_AC_BK
+
+/**
+ * enum nl80211_channel_type - channel type
+ * @NL80211_CHAN_NO_HT: 20 MHz, non-HT channel
+ * @NL80211_CHAN_HT20: 20 MHz HT channel
+ * @NL80211_CHAN_HT40MINUS: HT40 channel, secondary channel
+ * below the control channel
+ * @NL80211_CHAN_HT40PLUS: HT40 channel, secondary channel
+ * above the control channel
+ */
+enum nl80211_channel_type {
+ NL80211_CHAN_NO_HT,
+ NL80211_CHAN_HT20,
+ NL80211_CHAN_HT40MINUS,
+ NL80211_CHAN_HT40PLUS
+};
+
+/**
+ * enum nl80211_key_mode - Key mode
+ *
+ * @NL80211_KEY_RX_TX: (Default)
+ * Key can be used for Rx and Tx immediately
+ *
+ * The following modes can only be selected for unicast keys and when the
+ * driver supports @NL80211_EXT_FEATURE_EXT_KEY_ID:
+ *
+ * @NL80211_KEY_NO_TX: Only allowed in combination with @NL80211_CMD_NEW_KEY:
+ * Unicast key can only be used for Rx, Tx not allowed, yet
+ * @NL80211_KEY_SET_TX: Only allowed in combination with @NL80211_CMD_SET_KEY:
+ * The unicast key identified by idx and mac is cleared for Tx and becomes
+ * the preferred Tx key for the station.
+ */
+enum nl80211_key_mode {
+ NL80211_KEY_RX_TX,
+ NL80211_KEY_NO_TX,
+ NL80211_KEY_SET_TX
+};
+
+/**
+ * enum nl80211_chan_width - channel width definitions
+ *
+ * These values are used with the %NL80211_ATTR_CHANNEL_WIDTH
+ * attribute.
+ *
+ * @NL80211_CHAN_WIDTH_20_NOHT: 20 MHz, non-HT channel
+ * @NL80211_CHAN_WIDTH_20: 20 MHz HT channel
+ * @NL80211_CHAN_WIDTH_40: 40 MHz channel, the %NL80211_ATTR_CENTER_FREQ1
+ * attribute must be provided as well
+ * @NL80211_CHAN_WIDTH_80: 80 MHz channel, the %NL80211_ATTR_CENTER_FREQ1
+ * attribute must be provided as well
+ * @NL80211_CHAN_WIDTH_80P80: 80+80 MHz channel, the %NL80211_ATTR_CENTER_FREQ1
+ * and %NL80211_ATTR_CENTER_FREQ2 attributes must be provided as well
+ * @NL80211_CHAN_WIDTH_160: 160 MHz channel, the %NL80211_ATTR_CENTER_FREQ1
+ * attribute must be provided as well
+ * @NL80211_CHAN_WIDTH_5: 5 MHz OFDM channel
+ * @NL80211_CHAN_WIDTH_10: 10 MHz OFDM channel
+ * @NL80211_CHAN_WIDTH_1: 1 MHz OFDM channel
+ * @NL80211_CHAN_WIDTH_2: 2 MHz OFDM channel
+ * @NL80211_CHAN_WIDTH_4: 4 MHz OFDM channel
+ * @NL80211_CHAN_WIDTH_8: 8 MHz OFDM channel
+ * @NL80211_CHAN_WIDTH_16: 16 MHz OFDM channel
+ * @NL80211_CHAN_WIDTH_320: 320 MHz channel, the %NL80211_ATTR_CENTER_FREQ1
+ * attribute must be provided as well
+ */
+enum nl80211_chan_width {
+ NL80211_CHAN_WIDTH_20_NOHT,
+ NL80211_CHAN_WIDTH_20,
+ NL80211_CHAN_WIDTH_40,
+ NL80211_CHAN_WIDTH_80,
+ NL80211_CHAN_WIDTH_80P80,
+ NL80211_CHAN_WIDTH_160,
+ NL80211_CHAN_WIDTH_5,
+ NL80211_CHAN_WIDTH_10,
+ NL80211_CHAN_WIDTH_1,
+ NL80211_CHAN_WIDTH_2,
+ NL80211_CHAN_WIDTH_4,
+ NL80211_CHAN_WIDTH_8,
+ NL80211_CHAN_WIDTH_16,
+ NL80211_CHAN_WIDTH_320,
+};
+
+/**
+ * enum nl80211_bss_scan_width - control channel width for a BSS
+ *
+ * These values are used with the %NL80211_BSS_CHAN_WIDTH attribute.
+ *
+ * @NL80211_BSS_CHAN_WIDTH_20: control channel is 20 MHz wide or compatible
+ * @NL80211_BSS_CHAN_WIDTH_10: control channel is 10 MHz wide
+ * @NL80211_BSS_CHAN_WIDTH_5: control channel is 5 MHz wide
+ * @NL80211_BSS_CHAN_WIDTH_1: control channel is 1 MHz wide
+ * @NL80211_BSS_CHAN_WIDTH_2: control channel is 2 MHz wide
+ */
+enum nl80211_bss_scan_width {
+ NL80211_BSS_CHAN_WIDTH_20,
+ NL80211_BSS_CHAN_WIDTH_10,
+ NL80211_BSS_CHAN_WIDTH_5,
+ NL80211_BSS_CHAN_WIDTH_1,
+ NL80211_BSS_CHAN_WIDTH_2,
+};
+
+/**
+ * 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)
+ * (if @NL80211_BSS_PRESP_DATA is present then this is known to be
+ * from a probe response, otherwise it may be from the same beacon
+ * that the NL80211_BSS_BEACON_TSF will be from)
+ * @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 and the data is
+ * different then 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.
+ * If present, the @NL80211_BSS_PRESP_DATA attribute indicates that the
+ * data here is known to be from a probe response, without any heuristics.
+ * @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_CHAN_WIDTH: channel width of the control channel
+ * (u32, enum nl80211_bss_scan_width)
+ * @NL80211_BSS_BEACON_TSF: TSF of the last received beacon (u64)
+ * (not present if no beacon frame has been received yet)
+ * @NL80211_BSS_PRESP_DATA: the data in @NL80211_BSS_INFORMATION_ELEMENTS and
+ * @NL80211_BSS_TSF is known to be from a probe response (flag attribute)
+ * @NL80211_BSS_LAST_SEEN_BOOTTIME: CLOCK_BOOTTIME timestamp when this entry
+ * was last updated by a received frame. The value is expected to be
+ * accurate to about 10ms. (u64, nanoseconds)
+ * @NL80211_BSS_PAD: attribute used for padding for 64-bit alignment
+ * @NL80211_BSS_PARENT_TSF: the time at the start of reception of the first
+ * octet of the timestamp field of the last beacon/probe received for
+ * this BSS. The time is the TSF of the BSS specified by
+ * @NL80211_BSS_PARENT_BSSID. (u64).
+ * @NL80211_BSS_PARENT_BSSID: the BSS according to which @NL80211_BSS_PARENT_TSF
+ * is set.
+ * @NL80211_BSS_CHAIN_SIGNAL: per-chain signal strength of last BSS update.
+ * Contains a nested array of signal strength attributes (u8, dBm),
+ * using the nesting index as the antenna number.
+ * @NL80211_BSS_FREQUENCY_OFFSET: frequency offset in KHz
+ * @NL80211_BSS_MLO_LINK_ID: MLO link ID of the BSS (u8).
+ * @NL80211_BSS_MLD_ADDR: MLD address of this BSS if connected to it.
+ * @__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,
+ NL80211_BSS_CHAN_WIDTH,
+ NL80211_BSS_BEACON_TSF,
+ NL80211_BSS_PRESP_DATA,
+ NL80211_BSS_LAST_SEEN_BOOTTIME,
+ NL80211_BSS_PAD,
+ NL80211_BSS_PARENT_TSF,
+ NL80211_BSS_PARENT_BSSID,
+ NL80211_BSS_CHAIN_SIGNAL,
+ NL80211_BSS_FREQUENCY_OFFSET,
+ NL80211_BSS_MLO_LINK_ID,
+ NL80211_BSS_MLD_ADDR,
+
+ /* 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.
+ * Note that this is no longer used since cfg80211 no longer
+ * keeps track of whether or not authentication was done with
+ * a given 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_SAE: Simultaneous authentication of equals
+ * @NL80211_AUTHTYPE_FILS_SK: Fast Initial Link Setup shared key
+ * @NL80211_AUTHTYPE_FILS_SK_PFS: Fast Initial Link Setup shared key with PFS
+ * @NL80211_AUTHTYPE_FILS_PK: Fast Initial Link Setup public key
+ * @__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,
+ NL80211_AUTHTYPE_SAE,
+ NL80211_AUTHTYPE_FILS_SK,
+ NL80211_AUTHTYPE_FILS_SK_PFS,
+ NL80211_AUTHTYPE_FILS_PK,
+
+ /* 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
+ * @NL80211_MFP_OPTIONAL: Management frame protection is optional
+ */
+enum nl80211_mfp {
+ NL80211_MFP_NO,
+ NL80211_MFP_REQUIRED,
+ NL80211_MFP_OPTIONAL,
+};
+
+enum nl80211_wpa_versions {
+ NL80211_WPA_VERSION_1 = 1 << 0,
+ NL80211_WPA_VERSION_2 = 1 << 1,
+ NL80211_WPA_VERSION_3 = 1 << 2,
+};
+
+/**
+ * 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_MODE: the mode from enum nl80211_key_mode.
+ * Defaults to @NL80211_KEY_RX_TX.
+ * @NL80211_KEY_DEFAULT_BEACON: flag indicating default Beacon frame key
+ *
+ * @__NL80211_KEY_AFTER_LAST: internal
+ * @NL80211_KEY_MAX: highest key attribute
+ */
+enum nl80211_key_attributes {
+ __NL80211_KEY_INVALID,
+ NL80211_KEY_DATA,
+ NL80211_KEY_IDX,
+ NL80211_KEY_CIPHER,
+ NL80211_KEY_SEQ,
+ NL80211_KEY_DEFAULT,
+ NL80211_KEY_DEFAULT_MGMT,
+ NL80211_KEY_TYPE,
+ NL80211_KEY_DEFAULT_TYPES,
+ NL80211_KEY_MODE,
+ NL80211_KEY_DEFAULT_BEACON,
+
+ /* 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_HT: HT (MCS) rates allowed for TX rate selection
+ * in an array of MCS numbers.
+ * @NL80211_TXRATE_VHT: VHT rates allowed for TX rate selection,
+ * see &struct nl80211_txrate_vht
+ * @NL80211_TXRATE_GI: configure GI, see &enum nl80211_txrate_gi
+ * @NL80211_TXRATE_HE: HE rates allowed for TX rate selection,
+ * see &struct nl80211_txrate_he
+ * @NL80211_TXRATE_HE_GI: configure HE GI, 0.8us, 1.6us and 3.2us.
+ * @NL80211_TXRATE_HE_LTF: configure HE LTF, 1XLTF, 2XLTF and 4XLTF.
+ * @__NL80211_TXRATE_AFTER_LAST: internal
+ * @NL80211_TXRATE_MAX: highest TX rate attribute
+ */
+enum nl80211_tx_rate_attributes {
+ __NL80211_TXRATE_INVALID,
+ NL80211_TXRATE_LEGACY,
+ NL80211_TXRATE_HT,
+ NL80211_TXRATE_VHT,
+ NL80211_TXRATE_GI,
+ NL80211_TXRATE_HE,
+ NL80211_TXRATE_HE_GI,
+ NL80211_TXRATE_HE_LTF,
+
+ /* keep last */
+ __NL80211_TXRATE_AFTER_LAST,
+ NL80211_TXRATE_MAX = __NL80211_TXRATE_AFTER_LAST - 1
+};
+
+#define NL80211_TXRATE_MCS NL80211_TXRATE_HT
+#define NL80211_VHT_NSS_MAX 8
+
+/**
+ * struct nl80211_txrate_vht - VHT MCS/NSS txrate bitmap
+ * @mcs: MCS bitmap table for each NSS (array index 0 for 1 stream, etc.)
+ */
+struct nl80211_txrate_vht {
+ __u16 mcs[NL80211_VHT_NSS_MAX];
+};
+
+#define NL80211_HE_NSS_MAX 8
+/**
+ * struct nl80211_txrate_he - HE MCS/NSS txrate bitmap
+ * @mcs: MCS bitmap table for each NSS (array index 0 for 1 stream, etc.)
+ */
+struct nl80211_txrate_he {
+ __u16 mcs[NL80211_HE_NSS_MAX];
+};
+
+enum nl80211_txrate_gi {
+ NL80211_TXRATE_DEFAULT_GI,
+ NL80211_TXRATE_FORCE_SGI,
+ NL80211_TXRATE_FORCE_LGI,
+};
+
+/**
+ * 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)
+ * @NL80211_BAND_60GHZ: around 60 GHz band (58.32 - 69.12 GHz)
+ * @NL80211_BAND_6GHZ: around 6 GHz band (5.9 - 7.2 GHz)
+ * @NL80211_BAND_S1GHZ: around 900MHz, supported by S1G PHYs
+ * @NL80211_BAND_LC: light communication band (placeholder)
+ * @NUM_NL80211_BANDS: number of bands, avoid using this in userspace
+ * since newer kernel versions may support more bands
+ */
+enum nl80211_band {
+ NL80211_BAND_2GHZ,
+ NL80211_BAND_5GHZ,
+ NL80211_BAND_60GHZ,
+ NL80211_BAND_6GHZ,
+ NL80211_BAND_S1GHZ,
+ NL80211_BAND_LC,
+
+ NUM_NL80211_BANDS,
+};
+
+/**
+ * enum nl80211_ps_state - powersave state
+ * @NL80211_PS_DISABLED: powersave is disabled
+ * @NL80211_PS_ENABLED: powersave is enabled
+ */
+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. Alternatively, if %NL80211_EXT_FEATURE_CQM_RSSI_LIST is
+ * set, multiple values can be supplied as a low-to-high sorted array of
+ * threshold values in dBm. Events will be sent when the RSSI value
+ * crosses any of the thresholds.
+ * @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_TXE_RATE: TX error rate in %. Minimum % of TX failures
+ * during the given %NL80211_ATTR_CQM_TXE_INTVL before an
+ * %NL80211_CMD_NOTIFY_CQM with reported %NL80211_ATTR_CQM_TXE_RATE and
+ * %NL80211_ATTR_CQM_TXE_PKTS is generated.
+ * @NL80211_ATTR_CQM_TXE_PKTS: number of attempted packets in a given
+ * %NL80211_ATTR_CQM_TXE_INTVL before %NL80211_ATTR_CQM_TXE_RATE is
+ * checked.
+ * @NL80211_ATTR_CQM_TXE_INTVL: interval in seconds. Specifies the periodic
+ * interval in which %NL80211_ATTR_CQM_TXE_PKTS and
+ * %NL80211_ATTR_CQM_TXE_RATE must be satisfied before generating an
+ * %NL80211_CMD_NOTIFY_CQM. Set to 0 to turn off TX error reporting.
+ * @NL80211_ATTR_CQM_BEACON_LOSS_EVENT: flag attribute that's set in a beacon
+ * loss event
+ * @NL80211_ATTR_CQM_RSSI_LEVEL: the RSSI value in dBm that triggered the
+ * RSSI threshold event.
+ * @__NL80211_ATTR_CQM_AFTER_LAST: internal
+ * @NL80211_ATTR_CQM_MAX: highest key attribute
+ */
+enum nl80211_attr_cqm {
+ __NL80211_ATTR_CQM_INVALID,
+ NL80211_ATTR_CQM_RSSI_THOLD,
+ NL80211_ATTR_CQM_RSSI_HYST,
+ NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
+ NL80211_ATTR_CQM_PKT_LOSS_EVENT,
+ NL80211_ATTR_CQM_TXE_RATE,
+ NL80211_ATTR_CQM_TXE_PKTS,
+ NL80211_ATTR_CQM_TXE_INTVL,
+ NL80211_ATTR_CQM_BEACON_LOSS_EVENT,
+ NL80211_ATTR_CQM_RSSI_LEVEL,
+
+ /* 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
+ * @NL80211_CQM_RSSI_BEACON_LOSS_EVENT: (reserved, never sent)
+ */
+enum nl80211_cqm_rssi_threshold_event {
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
+ NL80211_CQM_RSSI_BEACON_LOSS_EVENT,
+};
+
+
+/**
+ * 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,
+};
+
+/**
+ * enum nl80211_tid_config - TID config state
+ * @NL80211_TID_CONFIG_ENABLE: Enable config for the TID
+ * @NL80211_TID_CONFIG_DISABLE: Disable config for the TID
+ */
+enum nl80211_tid_config {
+ NL80211_TID_CONFIG_ENABLE,
+ NL80211_TID_CONFIG_DISABLE,
+};
+
+/* enum nl80211_tx_rate_setting - TX rate configuration type
+ * @NL80211_TX_RATE_AUTOMATIC: automatically determine TX rate
+ * @NL80211_TX_RATE_LIMITED: limit the TX rate by the TX rate parameter
+ * @NL80211_TX_RATE_FIXED: fix TX rate to the TX rate parameter
+ */
+enum nl80211_tx_rate_setting {
+ NL80211_TX_RATE_AUTOMATIC,
+ NL80211_TX_RATE_LIMITED,
+ NL80211_TX_RATE_FIXED,
+};
+
+/* enum nl80211_tid_config_attr - TID specific configuration.
+ * @NL80211_TID_CONFIG_ATTR_PAD: pad attribute for 64-bit values
+ * @NL80211_TID_CONFIG_ATTR_VIF_SUPP: a bitmap (u64) of attributes supported
+ * for per-vif configuration; doesn't list the ones that are generic
+ * (%NL80211_TID_CONFIG_ATTR_TIDS, %NL80211_TID_CONFIG_ATTR_OVERRIDE).
+ * @NL80211_TID_CONFIG_ATTR_PEER_SUPP: same as the previous per-vif one, but
+ * per peer instead.
+ * @NL80211_TID_CONFIG_ATTR_OVERRIDE: flag attribue, if set indicates
+ * that the new configuration overrides all previous peer
+ * configurations, otherwise previous peer specific configurations
+ * should be left untouched.
+ * @NL80211_TID_CONFIG_ATTR_TIDS: a bitmask value of TIDs (bit 0 to 7)
+ * Its type is u16.
+ * @NL80211_TID_CONFIG_ATTR_NOACK: Configure ack policy for the TID.
+ * specified in %NL80211_TID_CONFIG_ATTR_TID. see %enum nl80211_tid_config.
+ * Its type is u8.
+ * @NL80211_TID_CONFIG_ATTR_RETRY_SHORT: Number of retries used with data frame
+ * transmission, user-space sets this configuration in
+ * &NL80211_CMD_SET_TID_CONFIG. It is u8 type, min value is 1 and
+ * the max value is advertised by the driver in this attribute on
+ * output in wiphy capabilities.
+ * @NL80211_TID_CONFIG_ATTR_RETRY_LONG: Number of retries used with data frame
+ * transmission, user-space sets this configuration in
+ * &NL80211_CMD_SET_TID_CONFIG. Its type is u8, min value is 1 and
+ * the max value is advertised by the driver in this attribute on
+ * output in wiphy capabilities.
+ * @NL80211_TID_CONFIG_ATTR_AMPDU_CTRL: Enable/Disable MPDU aggregation
+ * for the TIDs specified in %NL80211_TID_CONFIG_ATTR_TIDS.
+ * Its type is u8, using the values from &nl80211_tid_config.
+ * @NL80211_TID_CONFIG_ATTR_RTSCTS_CTRL: Enable/Disable RTS_CTS for the TIDs
+ * specified in %NL80211_TID_CONFIG_ATTR_TIDS. It is u8 type, using
+ * the values from &nl80211_tid_config.
+ * @NL80211_TID_CONFIG_ATTR_AMSDU_CTRL: Enable/Disable MSDU aggregation
+ * for the TIDs specified in %NL80211_TID_CONFIG_ATTR_TIDS.
+ * Its type is u8, using the values from &nl80211_tid_config.
+ * @NL80211_TID_CONFIG_ATTR_TX_RATE_TYPE: This attribute will be useful
+ * to notfiy the driver that what type of txrate should be used
+ * for the TIDs specified in %NL80211_TID_CONFIG_ATTR_TIDS. using
+ * the values form &nl80211_tx_rate_setting.
+ * @NL80211_TID_CONFIG_ATTR_TX_RATE: Data frame TX rate mask should be applied
+ * with the parameters passed through %NL80211_ATTR_TX_RATES.
+ * configuration is applied to the data frame for the tid to that connected
+ * station.
+ */
+enum nl80211_tid_config_attr {
+ __NL80211_TID_CONFIG_ATTR_INVALID,
+ NL80211_TID_CONFIG_ATTR_PAD,
+ NL80211_TID_CONFIG_ATTR_VIF_SUPP,
+ NL80211_TID_CONFIG_ATTR_PEER_SUPP,
+ NL80211_TID_CONFIG_ATTR_OVERRIDE,
+ NL80211_TID_CONFIG_ATTR_TIDS,
+ NL80211_TID_CONFIG_ATTR_NOACK,
+ NL80211_TID_CONFIG_ATTR_RETRY_SHORT,
+ NL80211_TID_CONFIG_ATTR_RETRY_LONG,
+ NL80211_TID_CONFIG_ATTR_AMPDU_CTRL,
+ NL80211_TID_CONFIG_ATTR_RTSCTS_CTRL,
+ NL80211_TID_CONFIG_ATTR_AMSDU_CTRL,
+ NL80211_TID_CONFIG_ATTR_TX_RATE_TYPE,
+ NL80211_TID_CONFIG_ATTR_TX_RATE,
+
+ /* keep last */
+ __NL80211_TID_CONFIG_ATTR_AFTER_LAST,
+ NL80211_TID_CONFIG_ATTR_MAX = __NL80211_TID_CONFIG_ATTR_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_packet_pattern_attr - packet pattern attribute
+ * @__NL80211_PKTPAT_INVALID: invalid number for nested attribute
+ * @NL80211_PKTPAT_PATTERN: the pattern, values where the mask has
+ * a zero bit are ignored
+ * @NL80211_PKTPAT_MASK: pattern mask, must be long enough to have
+ * a bit for each byte in the pattern. The lowest-order bit corresponds
+ * to the first byte of the pattern, but the bytes of the pattern are
+ * in a little-endian-like format, i.e. the 9th byte of the pattern
+ * corresponds to the lowest-order bit in the second byte of the mask.
+ * For example: The match 00:xx:00:00:xx:00:00:00:00:xx:xx:xx (where
+ * xx indicates "don't care") would be represented by a pattern of
+ * twelve zero bytes, and a mask of "0xed,0x01".
+ * Note that the pattern matching is done as though frames were not
+ * 802.11 frames but 802.3 frames, i.e. the frame is fully unpacked
+ * first (including SNAP header unpacking) and then matched.
+ * @NL80211_PKTPAT_OFFSET: packet offset, pattern is matched after
+ * these fixed number of bytes of received packet
+ * @NUM_NL80211_PKTPAT: number of attributes
+ * @MAX_NL80211_PKTPAT: max attribute number
+ */
+enum nl80211_packet_pattern_attr {
+ __NL80211_PKTPAT_INVALID,
+ NL80211_PKTPAT_MASK,
+ NL80211_PKTPAT_PATTERN,
+ NL80211_PKTPAT_OFFSET,
+
+ NUM_NL80211_PKTPAT,
+ MAX_NL80211_PKTPAT = NUM_NL80211_PKTPAT - 1,
+};
+
+/**
+ * struct nl80211_pattern_support - packet pattern support information
+ * @max_patterns: maximum number of patterns supported
+ * @min_pattern_len: minimum length of each pattern
+ * @max_pattern_len: maximum length of each pattern
+ * @max_pkt_offset: maximum Rx packet offset
+ *
+ * This struct is carried in %NL80211_WOWLAN_TRIG_PKT_PATTERN when
+ * that is part of %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED or in
+ * %NL80211_ATTR_COALESCE_RULE_PKT_PATTERN when that is part of
+ * %NL80211_ATTR_COALESCE_RULE in the capability information given
+ * by the kernel to userspace.
+ */
+struct nl80211_pattern_support {
+ __u32 max_patterns;
+ __u32 min_pattern_len;
+ __u32 max_pattern_len;
+ __u32 max_pkt_offset;
+} __attribute__((packed));
+
+/* only for backward compatibility */
+#define __NL80211_WOWLAN_PKTPAT_INVALID __NL80211_PKTPAT_INVALID
+#define NL80211_WOWLAN_PKTPAT_MASK NL80211_PKTPAT_MASK
+#define NL80211_WOWLAN_PKTPAT_PATTERN NL80211_PKTPAT_PATTERN
+#define NL80211_WOWLAN_PKTPAT_OFFSET NL80211_PKTPAT_OFFSET
+#define NUM_NL80211_WOWLAN_PKTPAT NUM_NL80211_PKTPAT
+#define MAX_NL80211_WOWLAN_PKTPAT MAX_NL80211_PKTPAT
+#define nl80211_wowlan_pattern_support nl80211_pattern_support
+
+/**
+ * enum nl80211_wowlan_triggers - WoWLAN trigger definitions
+ * @__NL80211_WOWLAN_TRIG_INVALID: invalid number for nested attributes
+ * @NL80211_WOWLAN_TRIG_ANY: wake up on any activity, do not really put
+ * the chip into a special state -- works best with chips that have
+ * support for low-power operation already (flag)
+ * Note that this mode is incompatible with all of the others, if
+ * any others are even supported by the device.
+ * @NL80211_WOWLAN_TRIG_DISCONNECT: wake up on disconnect, the way disconnect
+ * is detected is implementation-specific (flag)
+ * @NL80211_WOWLAN_TRIG_MAGIC_PKT: wake up on magic packet (6x 0xff, followed
+ * by 16 repetitions of MAC addr, anywhere in payload) (flag)
+ * @NL80211_WOWLAN_TRIG_PKT_PATTERN: wake up on the specified packet patterns
+ * which are passed in an array of nested attributes, each nested attribute
+ * defining a with attributes from &struct nl80211_wowlan_trig_pkt_pattern.
+ * Each pattern defines a wakeup packet. Packet offset is associated with
+ * each pattern which is used while matching the pattern. The matching is
+ * done on the MSDU, i.e. as though the packet was an 802.3 packet, so the
+ * pattern matching is done after the packet is converted to the MSDU.
+ *
+ * In %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, it is a binary attribute
+ * carrying a &struct nl80211_pattern_support.
+ *
+ * When reporting wakeup. it is a u32 attribute containing the 0-based
+ * index of the pattern that caused the wakeup, in the patterns passed
+ * to the kernel when configuring.
+ * @NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED: Not a real trigger, and cannot be
+ * used when setting, used only to indicate that GTK rekeying is supported
+ * by the device (flag)
+ * @NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE: wake up on GTK rekey failure (if
+ * done by the device) (flag)
+ * @NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST: wake up on EAP Identity Request
+ * packet (flag)
+ * @NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE: wake up on 4-way handshake (flag)
+ * @NL80211_WOWLAN_TRIG_RFKILL_RELEASE: wake up when rfkill is released
+ * (on devices that have rfkill in the device) (flag)
+ * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211: For wakeup reporting only, contains
+ * the 802.11 packet that caused the wakeup, e.g. a deauth frame. The frame
+ * may be truncated, the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN
+ * attribute contains the original length.
+ * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN: Original length of the 802.11
+ * packet, may be bigger than the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211
+ * attribute if the packet was truncated somewhere.
+ * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023: For wakeup reporting only, contains the
+ * 802.11 packet that caused the wakeup, e.g. a magic packet. The frame may
+ * be truncated, the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN attribute
+ * contains the original length.
+ * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN: Original length of the 802.3
+ * packet, may be bigger than the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023
+ * attribute if the packet was truncated somewhere.
+ * @NL80211_WOWLAN_TRIG_TCP_CONNECTION: TCP connection wake, see DOC section
+ * "TCP connection wakeup" for more details. This is a nested attribute
+ * containing the exact information for establishing and keeping alive
+ * the TCP connection.
+ * @NL80211_WOWLAN_TRIG_TCP_WAKEUP_MATCH: For wakeup reporting only, the
+ * wakeup packet was received on the TCP connection
+ * @NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST: For wakeup reporting only, the
+ * TCP connection was lost or failed to be established
+ * @NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS: For wakeup reporting only,
+ * the TCP connection ran out of tokens to use for data to send to the
+ * service
+ * @NL80211_WOWLAN_TRIG_NET_DETECT: wake up when a configured network
+ * is detected. This is a nested attribute that contains the
+ * same attributes used with @NL80211_CMD_START_SCHED_SCAN. It
+ * specifies how the scan is performed (e.g. the interval, the
+ * channels to scan and the initial delay) as well as the scan
+ * results that will trigger a wake (i.e. the matchsets). This
+ * attribute is also sent in a response to
+ * @NL80211_CMD_GET_WIPHY, indicating the number of match sets
+ * supported by the driver (u32).
+ * @NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS: nested attribute
+ * containing an array with information about what triggered the
+ * wake up. If no elements are present in the array, it means
+ * that the information is not available. If more than one
+ * element is present, it means that more than one match
+ * occurred.
+ * Each element in the array is a nested attribute that contains
+ * one optional %NL80211_ATTR_SSID attribute and one optional
+ * %NL80211_ATTR_SCAN_FREQUENCIES attribute. At least one of
+ * these attributes must be present. If
+ * %NL80211_ATTR_SCAN_FREQUENCIES contains more than one
+ * frequency, it means that the match occurred in more than one
+ * channel.
+ * @NUM_NL80211_WOWLAN_TRIG: number of wake on wireless triggers
+ * @MAX_NL80211_WOWLAN_TRIG: highest wowlan trigger attribute number
+ *
+ * These nested attributes are used to configure the wakeup triggers and
+ * to report the wakeup reason(s).
+ */
+enum nl80211_wowlan_triggers {
+ __NL80211_WOWLAN_TRIG_INVALID,
+ NL80211_WOWLAN_TRIG_ANY,
+ NL80211_WOWLAN_TRIG_DISCONNECT,
+ NL80211_WOWLAN_TRIG_MAGIC_PKT,
+ NL80211_WOWLAN_TRIG_PKT_PATTERN,
+ NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED,
+ NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE,
+ NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST,
+ NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE,
+ NL80211_WOWLAN_TRIG_RFKILL_RELEASE,
+ NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211,
+ NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN,
+ NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023,
+ NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN,
+ NL80211_WOWLAN_TRIG_TCP_CONNECTION,
+ NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH,
+ NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST,
+ NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS,
+ NL80211_WOWLAN_TRIG_NET_DETECT,
+ NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS,
+
+ /* keep last */
+ NUM_NL80211_WOWLAN_TRIG,
+ MAX_NL80211_WOWLAN_TRIG = NUM_NL80211_WOWLAN_TRIG - 1
+};
+
+/**
+ * DOC: TCP connection wakeup
+ *
+ * Some devices can establish a TCP connection in order to be woken up by a
+ * packet coming in from outside their network segment, or behind NAT. If
+ * configured, the device will establish a TCP connection to the given
+ * service, and periodically send data to that service. The first data
+ * packet is usually transmitted after SYN/ACK, also ACKing the SYN/ACK.
+ * The data packets can optionally include a (little endian) sequence
+ * number (in the TCP payload!) that is generated by the device, and, also
+ * optionally, a token from a list of tokens. This serves as a keep-alive
+ * with the service, and for NATed connections, etc.
+ *
+ * During this keep-alive period, the server doesn't send any data to the
+ * client. When receiving data, it is compared against the wakeup pattern
+ * (and mask) and if it matches, the host is woken up. Similarly, if the
+ * connection breaks or cannot be established to start with, the host is
+ * also woken up.
+ *
+ * Developer's note: ARP offload is required for this, otherwise TCP
+ * response packets might not go through correctly.
+ */
+
+/**
+ * struct nl80211_wowlan_tcp_data_seq - WoWLAN TCP data sequence
+ * @start: starting value
+ * @offset: offset of sequence number in packet
+ * @len: length of the sequence value to write, 1 through 4
+ *
+ * Note: don't confuse with the TCP sequence number(s), this is for the
+ * keepalive packet payload. The actual value is written into the packet
+ * in little endian.
+ */
+struct nl80211_wowlan_tcp_data_seq {
+ __u32 start, offset, len;
+};
+
+/**
+ * struct nl80211_wowlan_tcp_data_token - WoWLAN TCP data token config
+ * @offset: offset of token in packet
+ * @len: length of each token
+ * @token_stream: stream of data to be used for the tokens, the length must
+ * be a multiple of @len for this to make sense
+ */
+struct nl80211_wowlan_tcp_data_token {
+ __u32 offset, len;
+ __u8 token_stream[];
+};
+
+/**
+ * struct nl80211_wowlan_tcp_data_token_feature - data token features
+ * @min_len: minimum token length
+ * @max_len: maximum token length
+ * @bufsize: total available token buffer size (max size of @token_stream)
+ */
+struct nl80211_wowlan_tcp_data_token_feature {
+ __u32 min_len, max_len, bufsize;
+};
+
+/**
+ * enum nl80211_wowlan_tcp_attrs - WoWLAN TCP connection parameters
+ * @__NL80211_WOWLAN_TCP_INVALID: invalid number for nested attributes
+ * @NL80211_WOWLAN_TCP_SRC_IPV4: source IPv4 address (in network byte order)
+ * @NL80211_WOWLAN_TCP_DST_IPV4: destination IPv4 address
+ * (in network byte order)
+ * @NL80211_WOWLAN_TCP_DST_MAC: destination MAC address, this is given because
+ * route lookup when configured might be invalid by the time we suspend,
+ * and doing a route lookup when suspending is no longer possible as it
+ * might require ARP querying.
+ * @NL80211_WOWLAN_TCP_SRC_PORT: source port (u16); optional, if not given a
+ * socket and port will be allocated
+ * @NL80211_WOWLAN_TCP_DST_PORT: destination port (u16)
+ * @NL80211_WOWLAN_TCP_DATA_PAYLOAD: data packet payload, at least one byte.
+ * For feature advertising, a u32 attribute holding the maximum length
+ * of the data payload.
+ * @NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ: data packet sequence configuration
+ * (if desired), a &struct nl80211_wowlan_tcp_data_seq. For feature
+ * advertising it is just a flag
+ * @NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN: data packet token configuration,
+ * see &struct nl80211_wowlan_tcp_data_token and for advertising see
+ * &struct nl80211_wowlan_tcp_data_token_feature.
+ * @NL80211_WOWLAN_TCP_DATA_INTERVAL: data interval in seconds, maximum
+ * interval in feature advertising (u32)
+ * @NL80211_WOWLAN_TCP_WAKE_PAYLOAD: wake packet payload, for advertising a
+ * u32 attribute holding the maximum length
+ * @NL80211_WOWLAN_TCP_WAKE_MASK: Wake packet payload mask, not used for
+ * feature advertising. The mask works like @NL80211_PKTPAT_MASK
+ * but on the TCP payload only.
+ * @NUM_NL80211_WOWLAN_TCP: number of TCP attributes
+ * @MAX_NL80211_WOWLAN_TCP: highest attribute number
+ */
+enum nl80211_wowlan_tcp_attrs {
+ __NL80211_WOWLAN_TCP_INVALID,
+ NL80211_WOWLAN_TCP_SRC_IPV4,
+ NL80211_WOWLAN_TCP_DST_IPV4,
+ NL80211_WOWLAN_TCP_DST_MAC,
+ NL80211_WOWLAN_TCP_SRC_PORT,
+ NL80211_WOWLAN_TCP_DST_PORT,
+ NL80211_WOWLAN_TCP_DATA_PAYLOAD,
+ NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ,
+ NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN,
+ NL80211_WOWLAN_TCP_DATA_INTERVAL,
+ NL80211_WOWLAN_TCP_WAKE_PAYLOAD,
+ NL80211_WOWLAN_TCP_WAKE_MASK,
+
+ /* keep last */
+ NUM_NL80211_WOWLAN_TCP,
+ MAX_NL80211_WOWLAN_TCP = NUM_NL80211_WOWLAN_TCP - 1
+};
+
+/**
+ * struct nl80211_coalesce_rule_support - coalesce rule support information
+ * @max_rules: maximum number of rules supported
+ * @pat: packet pattern support information
+ * @max_delay: maximum supported coalescing delay in msecs
+ *
+ * This struct is carried in %NL80211_ATTR_COALESCE_RULE in the
+ * capability information given by the kernel to userspace.
+ */
+struct nl80211_coalesce_rule_support {
+ __u32 max_rules;
+ struct nl80211_pattern_support pat;
+ __u32 max_delay;
+} __attribute__((packed));
+
+/**
+ * enum nl80211_attr_coalesce_rule - coalesce rule attribute
+ * @__NL80211_COALESCE_RULE_INVALID: invalid number for nested attribute
+ * @NL80211_ATTR_COALESCE_RULE_DELAY: delay in msecs used for packet coalescing
+ * @NL80211_ATTR_COALESCE_RULE_CONDITION: condition for packet coalescence,
+ * see &enum nl80211_coalesce_condition.
+ * @NL80211_ATTR_COALESCE_RULE_PKT_PATTERN: packet offset, pattern is matched
+ * after these fixed number of bytes of received packet
+ * @NUM_NL80211_ATTR_COALESCE_RULE: number of attributes
+ * @NL80211_ATTR_COALESCE_RULE_MAX: max attribute number
+ */
+enum nl80211_attr_coalesce_rule {
+ __NL80211_COALESCE_RULE_INVALID,
+ NL80211_ATTR_COALESCE_RULE_DELAY,
+ NL80211_ATTR_COALESCE_RULE_CONDITION,
+ NL80211_ATTR_COALESCE_RULE_PKT_PATTERN,
+
+ /* keep last */
+ NUM_NL80211_ATTR_COALESCE_RULE,
+ NL80211_ATTR_COALESCE_RULE_MAX = NUM_NL80211_ATTR_COALESCE_RULE - 1
+};
+
+/**
+ * enum nl80211_coalesce_condition - coalesce rule conditions
+ * @NL80211_COALESCE_CONDITION_MATCH: coalaesce Rx packets when patterns
+ * in a rule are matched.
+ * @NL80211_COALESCE_CONDITION_NO_MATCH: coalesce Rx packets when patterns
+ * in a rule are not matched.
+ */
+enum nl80211_coalesce_condition {
+ NL80211_COALESCE_CONDITION_MATCH,
+ NL80211_COALESCE_CONDITION_NO_MATCH
+};
+
+/**
+ * enum nl80211_iface_limit_attrs - limit attributes
+ * @NL80211_IFACE_LIMIT_UNSPEC: (reserved)
+ * @NL80211_IFACE_LIMIT_MAX: maximum number of interfaces that
+ * can be chosen from this set of interface types (u32)
+ * @NL80211_IFACE_LIMIT_TYPES: nested attribute containing a
+ * flag attribute for each interface type in this set
+ * @NUM_NL80211_IFACE_LIMIT: number of attributes
+ * @MAX_NL80211_IFACE_LIMIT: highest attribute number
+ */
+enum nl80211_iface_limit_attrs {
+ NL80211_IFACE_LIMIT_UNSPEC,
+ NL80211_IFACE_LIMIT_MAX,
+ NL80211_IFACE_LIMIT_TYPES,
+
+ /* keep last */
+ NUM_NL80211_IFACE_LIMIT,
+ MAX_NL80211_IFACE_LIMIT = NUM_NL80211_IFACE_LIMIT - 1
+};
+
+/**
+ * enum nl80211_if_combination_attrs -- interface combination attributes
+ *
+ * @NL80211_IFACE_COMB_UNSPEC: (reserved)
+ * @NL80211_IFACE_COMB_LIMITS: Nested attributes containing the limits
+ * for given interface types, see &enum nl80211_iface_limit_attrs.
+ * @NL80211_IFACE_COMB_MAXNUM: u32 attribute giving the total number of
+ * interfaces that can be created in this group. This number doesn't
+ * apply to interfaces purely managed in software, which are listed
+ * in a separate attribute %NL80211_ATTR_INTERFACES_SOFTWARE.
+ * @NL80211_IFACE_COMB_STA_AP_BI_MATCH: flag attribute specifying that
+ * beacon intervals within this group must be all the same even for
+ * infrastructure and AP/GO combinations, i.e. the GO(s) must adopt
+ * the infrastructure network's beacon interval.
+ * @NL80211_IFACE_COMB_NUM_CHANNELS: u32 attribute specifying how many
+ * different channels may be used within this group.
+ * @NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS: u32 attribute containing the bitmap
+ * of supported channel widths for radar detection.
+ * @NL80211_IFACE_COMB_RADAR_DETECT_REGIONS: u32 attribute containing the bitmap
+ * of supported regulatory regions for radar detection.
+ * @NL80211_IFACE_COMB_BI_MIN_GCD: u32 attribute specifying the minimum GCD of
+ * different beacon intervals supported by all the interface combinations
+ * in this group (if not present, all beacon intervals be identical).
+ * @NUM_NL80211_IFACE_COMB: number of attributes
+ * @MAX_NL80211_IFACE_COMB: highest attribute number
+ *
+ * Examples:
+ * limits = [ #{STA} <= 1, #{AP} <= 1 ], matching BI, channels = 1, max = 2
+ * => allows an AP and a STA that must match BIs
+ *
+ * numbers = [ #{AP, P2P-GO} <= 8 ], BI min gcd, channels = 1, max = 8,
+ * => allows 8 of AP/GO that can have BI gcd >= min gcd
+ *
+ * numbers = [ #{STA} <= 2 ], channels = 2, max = 2
+ * => allows two STAs on the same or on different channels
+ *
+ * numbers = [ #{STA} <= 1, #{P2P-client,P2P-GO} <= 3 ], max = 4
+ * => allows a STA plus three P2P interfaces
+ *
+ * The list of these four possibilities could completely be contained
+ * within the %NL80211_ATTR_INTERFACE_COMBINATIONS attribute to indicate
+ * that any of these groups must match.
+ *
+ * "Combinations" of just a single interface will not be listed here,
+ * a single interface of any valid interface type is assumed to always
+ * be possible by itself. This means that implicitly, for each valid
+ * interface type, the following group always exists:
+ * numbers = [ #{<type>} <= 1 ], channels = 1, max = 1
+ */
+enum nl80211_if_combination_attrs {
+ NL80211_IFACE_COMB_UNSPEC,
+ NL80211_IFACE_COMB_LIMITS,
+ NL80211_IFACE_COMB_MAXNUM,
+ NL80211_IFACE_COMB_STA_AP_BI_MATCH,
+ NL80211_IFACE_COMB_NUM_CHANNELS,
+ NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS,
+ NL80211_IFACE_COMB_RADAR_DETECT_REGIONS,
+ NL80211_IFACE_COMB_BI_MIN_GCD,
+
+ /* keep last */
+ NUM_NL80211_IFACE_COMB,
+ MAX_NL80211_IFACE_COMB = NUM_NL80211_IFACE_COMB - 1
+};
+
+
+/**
+ * enum nl80211_plink_state - state of a mesh peer link finite state machine
+ *
+ * @NL80211_PLINK_LISTEN: initial state, considered the implicit
+ * state of non existent mesh peer links
+ * @NL80211_PLINK_OPN_SNT: mesh plink open frame has been sent to
+ * this mesh peer
+ * @NL80211_PLINK_OPN_RCVD: mesh plink open frame has been received
+ * from this mesh peer
+ * @NL80211_PLINK_CNF_RCVD: mesh plink confirm frame has been
+ * received from this mesh peer
+ * @NL80211_PLINK_ESTAB: mesh peer link is established
+ * @NL80211_PLINK_HOLDING: mesh peer link is being closed or cancelled
+ * @NL80211_PLINK_BLOCKED: all frames transmitted from this mesh
+ * plink are discarded, except for authentication frames
+ * @NUM_NL80211_PLINK_STATES: number of peer link states
+ * @MAX_NL80211_PLINK_STATES: highest numerical value of plink states
+ */
+enum nl80211_plink_state {
+ NL80211_PLINK_LISTEN,
+ NL80211_PLINK_OPN_SNT,
+ NL80211_PLINK_OPN_RCVD,
+ NL80211_PLINK_CNF_RCVD,
+ NL80211_PLINK_ESTAB,
+ NL80211_PLINK_HOLDING,
+ NL80211_PLINK_BLOCKED,
+
+ /* keep last */
+ NUM_NL80211_PLINK_STATES,
+ MAX_NL80211_PLINK_STATES = NUM_NL80211_PLINK_STATES - 1
+};
+
+/**
+ * enum nl80211_plink_action - actions to perform in mesh peers
+ *
+ * @NL80211_PLINK_ACTION_NO_ACTION: perform no action
+ * @NL80211_PLINK_ACTION_OPEN: start mesh peer link establishment
+ * @NL80211_PLINK_ACTION_BLOCK: block traffic from this mesh peer
+ * @NUM_NL80211_PLINK_ACTIONS: number of possible actions
+ */
+enum plink_actions {
+ NL80211_PLINK_ACTION_NO_ACTION,
+ NL80211_PLINK_ACTION_OPEN,
+ NL80211_PLINK_ACTION_BLOCK,
+
+ NUM_NL80211_PLINK_ACTIONS,
+};
+
+
+#define NL80211_KCK_LEN 16
+#define NL80211_KEK_LEN 16
+#define NL80211_KCK_EXT_LEN 24
+#define NL80211_KEK_EXT_LEN 32
+#define NL80211_KCK_EXT_LEN_32 32
+#define NL80211_REPLAY_CTR_LEN 8
+
+/**
+ * enum nl80211_rekey_data - attributes for GTK rekey offload
+ * @__NL80211_REKEY_DATA_INVALID: invalid number for nested attributes
+ * @NL80211_REKEY_DATA_KEK: key encryption key (binary)
+ * @NL80211_REKEY_DATA_KCK: key confirmation key (binary)
+ * @NL80211_REKEY_DATA_REPLAY_CTR: replay counter (binary)
+ * @NL80211_REKEY_DATA_AKM: AKM data (OUI, suite type)
+ * @NUM_NL80211_REKEY_DATA: number of rekey attributes (internal)
+ * @MAX_NL80211_REKEY_DATA: highest rekey attribute (internal)
+ */
+enum nl80211_rekey_data {
+ __NL80211_REKEY_DATA_INVALID,
+ NL80211_REKEY_DATA_KEK,
+ NL80211_REKEY_DATA_KCK,
+ NL80211_REKEY_DATA_REPLAY_CTR,
+ NL80211_REKEY_DATA_AKM,
+
+ /* keep last */
+ NUM_NL80211_REKEY_DATA,
+ MAX_NL80211_REKEY_DATA = NUM_NL80211_REKEY_DATA - 1
+};
+
+/**
+ * enum nl80211_hidden_ssid - values for %NL80211_ATTR_HIDDEN_SSID
+ * @NL80211_HIDDEN_SSID_NOT_IN_USE: do not hide SSID (i.e., broadcast it in
+ * Beacon frames)
+ * @NL80211_HIDDEN_SSID_ZERO_LEN: hide SSID by using zero-length SSID element
+ * in Beacon frames
+ * @NL80211_HIDDEN_SSID_ZERO_CONTENTS: hide SSID by using correct length of SSID
+ * element in Beacon frames but zero out each byte in the SSID
+ */
+enum nl80211_hidden_ssid {
+ NL80211_HIDDEN_SSID_NOT_IN_USE,
+ NL80211_HIDDEN_SSID_ZERO_LEN,
+ NL80211_HIDDEN_SSID_ZERO_CONTENTS
+};
+
+/**
+ * enum nl80211_sta_wme_attr - station WME attributes
+ * @__NL80211_STA_WME_INVALID: invalid number for nested attribute
+ * @NL80211_STA_WME_UAPSD_QUEUES: bitmap of uapsd queues. the format
+ * is the same as the AC bitmap in the QoS info field.
+ * @NL80211_STA_WME_MAX_SP: max service period. the format is the same
+ * as the MAX_SP field in the QoS info field (but already shifted down).
+ * @__NL80211_STA_WME_AFTER_LAST: internal
+ * @NL80211_STA_WME_MAX: highest station WME attribute
+ */
+enum nl80211_sta_wme_attr {
+ __NL80211_STA_WME_INVALID,
+ NL80211_STA_WME_UAPSD_QUEUES,
+ NL80211_STA_WME_MAX_SP,
+
+ /* keep last */
+ __NL80211_STA_WME_AFTER_LAST,
+ NL80211_STA_WME_MAX = __NL80211_STA_WME_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_pmksa_candidate_attr - attributes for PMKSA caching candidates
+ * @__NL80211_PMKSA_CANDIDATE_INVALID: invalid number for nested attributes
+ * @NL80211_PMKSA_CANDIDATE_INDEX: candidate index (u32; the smaller, the higher
+ * priority)
+ * @NL80211_PMKSA_CANDIDATE_BSSID: candidate BSSID (6 octets)
+ * @NL80211_PMKSA_CANDIDATE_PREAUTH: RSN pre-authentication supported (flag)
+ * @NUM_NL80211_PMKSA_CANDIDATE: number of PMKSA caching candidate attributes
+ * (internal)
+ * @MAX_NL80211_PMKSA_CANDIDATE: highest PMKSA caching candidate attribute
+ * (internal)
+ */
+enum nl80211_pmksa_candidate_attr {
+ __NL80211_PMKSA_CANDIDATE_INVALID,
+ NL80211_PMKSA_CANDIDATE_INDEX,
+ NL80211_PMKSA_CANDIDATE_BSSID,
+ NL80211_PMKSA_CANDIDATE_PREAUTH,
+
+ /* keep last */
+ NUM_NL80211_PMKSA_CANDIDATE,
+ MAX_NL80211_PMKSA_CANDIDATE = NUM_NL80211_PMKSA_CANDIDATE - 1
+};
+
+/**
+ * enum nl80211_tdls_operation - values for %NL80211_ATTR_TDLS_OPERATION
+ * @NL80211_TDLS_DISCOVERY_REQ: Send a TDLS discovery request
+ * @NL80211_TDLS_SETUP: Setup TDLS link
+ * @NL80211_TDLS_TEARDOWN: Teardown a TDLS link which is already established
+ * @NL80211_TDLS_ENABLE_LINK: Enable TDLS link
+ * @NL80211_TDLS_DISABLE_LINK: Disable TDLS link
+ */
+enum nl80211_tdls_operation {
+ NL80211_TDLS_DISCOVERY_REQ,
+ NL80211_TDLS_SETUP,
+ NL80211_TDLS_TEARDOWN,
+ NL80211_TDLS_ENABLE_LINK,
+ NL80211_TDLS_DISABLE_LINK,
+};
+
+/**
+ * enum nl80211_ap_sme_features - device-integrated AP features
+ * @NL80211_AP_SME_SA_QUERY_OFFLOAD: SA Query procedures offloaded to driver
+ * when user space indicates support for SA Query procedures offload during
+ * "start ap" with %NL80211_AP_SETTINGS_SA_QUERY_OFFLOAD_SUPPORT.
+ */
+enum nl80211_ap_sme_features {
+ NL80211_AP_SME_SA_QUERY_OFFLOAD = 1 << 0,
+};
+
+/**
+ * enum nl80211_feature_flags - device/driver features
+ * @NL80211_FEATURE_SK_TX_STATUS: This driver supports reflecting back
+ * TX status to the socket error queue when requested with the
+ * socket option.
+ * @NL80211_FEATURE_HT_IBSS: This driver supports IBSS with HT datarates.
+ * @NL80211_FEATURE_INACTIVITY_TIMER: This driver takes care of freeing up
+ * the connected inactive stations in AP mode.
+ * @NL80211_FEATURE_CELL_BASE_REG_HINTS: This driver has been tested
+ * to work properly to support receiving regulatory hints from
+ * cellular base stations.
+ * @NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL: (no longer available, only
+ * here to reserve the value for API/ABI compatibility)
+ * @NL80211_FEATURE_SAE: This driver supports simultaneous authentication of
+ * equals (SAE) with user space SME (NL80211_CMD_AUTHENTICATE) in station
+ * mode
+ * @NL80211_FEATURE_LOW_PRIORITY_SCAN: This driver supports low priority scan
+ * @NL80211_FEATURE_SCAN_FLUSH: Scan flush is supported
+ * @NL80211_FEATURE_AP_SCAN: Support scanning using an AP vif
+ * @NL80211_FEATURE_VIF_TXPOWER: The driver supports per-vif TX power setting
+ * @NL80211_FEATURE_NEED_OBSS_SCAN: The driver expects userspace to perform
+ * OBSS scans and generate 20/40 BSS coex reports. This flag is used only
+ * for drivers implementing the CONNECT API, for AUTH/ASSOC it is implied.
+ * @NL80211_FEATURE_P2P_GO_CTWIN: P2P GO implementation supports CT Window
+ * setting
+ * @NL80211_FEATURE_P2P_GO_OPPPS: P2P GO implementation supports opportunistic
+ * powersave
+ * @NL80211_FEATURE_FULL_AP_CLIENT_STATE: The driver supports full state
+ * transitions for AP clients. Without this flag (and if the driver
+ * doesn't have the AP SME in the device) the driver supports adding
+ * stations only when they're associated and adds them in associated
+ * state (to later be transitioned into authorized), with this flag
+ * they should be added before even sending the authentication reply
+ * and then transitioned into authenticated, associated and authorized
+ * states using station flags.
+ * Note that even for drivers that support this, the default is to add
+ * stations in authenticated/associated state, so to add unauthenticated
+ * stations the authenticated/associated bits have to be set in the mask.
+ * @NL80211_FEATURE_ADVERTISE_CHAN_LIMITS: cfg80211 advertises channel limits
+ * (HT40, VHT 80/160 MHz) if this flag is set
+ * @NL80211_FEATURE_USERSPACE_MPM: This driver supports a userspace Mesh
+ * Peering Management entity which may be implemented by registering for
+ * beacons or NL80211_CMD_NEW_PEER_CANDIDATE events. The mesh beacon is
+ * still generated by the driver.
+ * @NL80211_FEATURE_ACTIVE_MONITOR: This driver supports an active monitor
+ * interface. An active monitor interface behaves like a normal monitor
+ * interface, but gets added to the driver. It ensures that incoming
+ * unicast packets directed at the configured interface address get ACKed.
+ * @NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE: This driver supports dynamic
+ * channel bandwidth change (e.g., HT 20 <-> 40 MHz channel) during the
+ * lifetime of a BSS.
+ * @NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES: This device adds a DS Parameter
+ * Set IE to probe requests.
+ * @NL80211_FEATURE_WFA_TPC_IE_IN_PROBES: This device adds a WFA TPC Report IE
+ * to probe requests.
+ * @NL80211_FEATURE_QUIET: This device, in client mode, supports Quiet Period
+ * requests sent to it by an AP.
+ * @NL80211_FEATURE_TX_POWER_INSERTION: This device is capable of inserting the
+ * current tx power value into the TPC Report IE in the spectrum
+ * management TPC Report action frame, and in the Radio Measurement Link
+ * Measurement Report action frame.
+ * @NL80211_FEATURE_ACKTO_ESTIMATION: This driver supports dynamic ACK timeout
+ * estimation (dynack). %NL80211_ATTR_WIPHY_DYN_ACK flag attribute is used
+ * to enable dynack.
+ * @NL80211_FEATURE_STATIC_SMPS: Device supports static spatial
+ * multiplexing powersave, ie. can turn off all but one chain
+ * even on HT connections that should be using more chains.
+ * @NL80211_FEATURE_DYNAMIC_SMPS: Device supports dynamic spatial
+ * multiplexing powersave, ie. can turn off all but one chain
+ * and then wake the rest up as required after, for example,
+ * rts/cts handshake.
+ * @NL80211_FEATURE_SUPPORTS_WMM_ADMISSION: the device supports setting up WMM
+ * TSPEC sessions (TID aka TSID 0-7) with the %NL80211_CMD_ADD_TX_TS
+ * command. Standard IEEE 802.11 TSPEC setup is not yet supported, it
+ * needs to be able to handle Block-Ack agreements and other things.
+ * @NL80211_FEATURE_MAC_ON_CREATE: Device supports configuring
+ * the vif's MAC address upon creation.
+ * See 'macaddr' field in the vif_params (cfg80211.h).
+ * @NL80211_FEATURE_TDLS_CHANNEL_SWITCH: Driver supports channel switching when
+ * operating as a TDLS peer.
+ * @NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR: This device/driver supports using a
+ * random MAC address during scan (if the device is unassociated); the
+ * %NL80211_SCAN_FLAG_RANDOM_ADDR flag may be set for scans and the MAC
+ * address mask/value will be used.
+ * @NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR: This device/driver supports
+ * using a random MAC address for every scan iteration during scheduled
+ * scan (while not associated), the %NL80211_SCAN_FLAG_RANDOM_ADDR may
+ * be set for scheduled scan and the MAC address mask/value will be used.
+ * @NL80211_FEATURE_ND_RANDOM_MAC_ADDR: This device/driver supports using a
+ * random MAC address for every scan iteration during "net detect", i.e.
+ * scan in unassociated WoWLAN, the %NL80211_SCAN_FLAG_RANDOM_ADDR may
+ * be set for scheduled scan and the MAC address mask/value will be used.
+ */
+enum nl80211_feature_flags {
+ NL80211_FEATURE_SK_TX_STATUS = 1 << 0,
+ NL80211_FEATURE_HT_IBSS = 1 << 1,
+ NL80211_FEATURE_INACTIVITY_TIMER = 1 << 2,
+ NL80211_FEATURE_CELL_BASE_REG_HINTS = 1 << 3,
+ NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL = 1 << 4,
+ NL80211_FEATURE_SAE = 1 << 5,
+ NL80211_FEATURE_LOW_PRIORITY_SCAN = 1 << 6,
+ NL80211_FEATURE_SCAN_FLUSH = 1 << 7,
+ NL80211_FEATURE_AP_SCAN = 1 << 8,
+ NL80211_FEATURE_VIF_TXPOWER = 1 << 9,
+ NL80211_FEATURE_NEED_OBSS_SCAN = 1 << 10,
+ NL80211_FEATURE_P2P_GO_CTWIN = 1 << 11,
+ NL80211_FEATURE_P2P_GO_OPPPS = 1 << 12,
+ /* bit 13 is reserved */
+ NL80211_FEATURE_ADVERTISE_CHAN_LIMITS = 1 << 14,
+ NL80211_FEATURE_FULL_AP_CLIENT_STATE = 1 << 15,
+ NL80211_FEATURE_USERSPACE_MPM = 1 << 16,
+ NL80211_FEATURE_ACTIVE_MONITOR = 1 << 17,
+ NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE = 1 << 18,
+ NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES = 1 << 19,
+ NL80211_FEATURE_WFA_TPC_IE_IN_PROBES = 1 << 20,
+ NL80211_FEATURE_QUIET = 1 << 21,
+ NL80211_FEATURE_TX_POWER_INSERTION = 1 << 22,
+ NL80211_FEATURE_ACKTO_ESTIMATION = 1 << 23,
+ NL80211_FEATURE_STATIC_SMPS = 1 << 24,
+ NL80211_FEATURE_DYNAMIC_SMPS = 1 << 25,
+ NL80211_FEATURE_SUPPORTS_WMM_ADMISSION = 1 << 26,
+ NL80211_FEATURE_MAC_ON_CREATE = 1 << 27,
+ NL80211_FEATURE_TDLS_CHANNEL_SWITCH = 1 << 28,
+ NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR = 1 << 29,
+ NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR = 1 << 30,
+ NL80211_FEATURE_ND_RANDOM_MAC_ADDR = 1U << 31,
+};
+
+/**
+ * enum nl80211_ext_feature_index - bit index of extended features.
+ * @NL80211_EXT_FEATURE_VHT_IBSS: This driver supports IBSS with VHT datarates.
+ * @NL80211_EXT_FEATURE_RRM: This driver supports RRM. When featured, user can
+ * request to use RRM (see %NL80211_ATTR_USE_RRM) with
+ * %NL80211_CMD_ASSOCIATE and %NL80211_CMD_CONNECT requests, which will set
+ * the ASSOC_REQ_USE_RRM flag in the association request even if
+ * NL80211_FEATURE_QUIET is not advertized.
+ * @NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER: This device supports MU-MIMO air
+ * sniffer which means that it can be configured to hear packets from
+ * certain groups which can be configured by the
+ * %NL80211_ATTR_MU_MIMO_GROUP_DATA attribute,
+ * or can be configured to follow a station by configuring the
+ * %NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR attribute.
+ * @NL80211_EXT_FEATURE_SCAN_START_TIME: This driver includes the actual
+ * time the scan started in scan results event. The time is the TSF of
+ * the BSS that the interface that requested the scan is connected to
+ * (if available).
+ * @NL80211_EXT_FEATURE_BSS_PARENT_TSF: Per BSS, this driver reports the
+ * time the last beacon/probe was received. The time is the TSF of the
+ * BSS that the interface that requested the scan is connected to
+ * (if available).
+ * @NL80211_EXT_FEATURE_SET_SCAN_DWELL: This driver supports configuration of
+ * channel dwell time.
+ * @NL80211_EXT_FEATURE_BEACON_RATE_LEGACY: Driver supports beacon rate
+ * configuration (AP/mesh), supporting a legacy (non HT/VHT) rate.
+ * @NL80211_EXT_FEATURE_BEACON_RATE_HT: Driver supports beacon rate
+ * configuration (AP/mesh) with HT rates.
+ * @NL80211_EXT_FEATURE_BEACON_RATE_VHT: Driver supports beacon rate
+ * configuration (AP/mesh) with VHT rates.
+ * @NL80211_EXT_FEATURE_FILS_STA: This driver supports Fast Initial Link Setup
+ * with user space SME (NL80211_CMD_AUTHENTICATE) in station mode.
+ * @NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA: This driver supports randomized TA
+ * in @NL80211_CMD_FRAME while not associated.
+ * @NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED: This driver supports
+ * randomized TA in @NL80211_CMD_FRAME while associated.
+ * @NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI: The driver supports sched_scan
+ * for reporting BSSs with better RSSI than the current connected BSS
+ * (%NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI).
+ * @NL80211_EXT_FEATURE_CQM_RSSI_LIST: With this driver the
+ * %NL80211_ATTR_CQM_RSSI_THOLD attribute accepts a list of zero or more
+ * RSSI threshold values to monitor rather than exactly one threshold.
+ * @NL80211_EXT_FEATURE_FILS_SK_OFFLOAD: Driver SME supports FILS shared key
+ * authentication with %NL80211_CMD_CONNECT.
+ * @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK: Device wants to do 4-way
+ * handshake with PSK in station mode (PSK is passed as part of the connect
+ * and associate commands), doing it in the host might not be supported.
+ * @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X: Device wants to do doing 4-way
+ * handshake with 802.1X in station mode (will pass EAP frames to the host
+ * and accept the set_pmk/del_pmk commands), doing it in the host might not
+ * be supported.
+ * @NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME: Driver is capable of overriding
+ * the max channel attribute in the FILS request params IE with the
+ * actual dwell time.
+ * @NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP: Driver accepts broadcast probe
+ * response
+ * @NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE: Driver supports sending
+ * the first probe request in each channel at rate of at least 5.5Mbps.
+ * @NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION: Driver supports
+ * probe request tx deferral and suppression
+ * @NL80211_EXT_FEATURE_MFP_OPTIONAL: Driver supports the %NL80211_MFP_OPTIONAL
+ * value in %NL80211_ATTR_USE_MFP.
+ * @NL80211_EXT_FEATURE_LOW_SPAN_SCAN: Driver supports low span scan.
+ * @NL80211_EXT_FEATURE_LOW_POWER_SCAN: Driver supports low power scan.
+ * @NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN: Driver supports high accuracy scan.
+ * @NL80211_EXT_FEATURE_DFS_OFFLOAD: HW/driver will offload DFS actions.
+ * Device or driver will do all DFS-related actions by itself,
+ * informing user-space about CAC progress, radar detection event,
+ * channel change triggered by radar detection event.
+ * No need to start CAC from user-space, no need to react to
+ * "radar detected" event.
+ * @NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211: Driver supports sending and
+ * receiving control port frames over nl80211 instead of the netdevice.
+ * @NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT: This driver/device supports
+ * (average) ACK signal strength reporting.
+ * @NL80211_EXT_FEATURE_TXQS: Driver supports FQ-CoDel-enabled intermediate
+ * TXQs.
+ * @NL80211_EXT_FEATURE_SCAN_RANDOM_SN: Driver/device supports randomizing the
+ * SN in probe request frames if requested by %NL80211_SCAN_FLAG_RANDOM_SN.
+ * @NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT: Driver/device can omit all data
+ * except for supported rates from the probe request content if requested
+ * by the %NL80211_SCAN_FLAG_MIN_PREQ_CONTENT flag.
+ * @NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER: Driver supports enabling fine
+ * timing measurement responder role.
+ *
+ * @NL80211_EXT_FEATURE_CAN_REPLACE_PTK0: Driver/device confirm that they are
+ * able to rekey an in-use key correctly. Userspace must not rekey PTK keys
+ * if this flag is not set. Ignoring this can leak clear text packets and/or
+ * freeze the connection.
+ * @NL80211_EXT_FEATURE_EXT_KEY_ID: Driver supports "Extended Key ID for
+ * Individually Addressed Frames" from IEEE802.11-2016.
+ *
+ * @NL80211_EXT_FEATURE_AIRTIME_FAIRNESS: Driver supports getting airtime
+ * fairness for transmitted packets and has enabled airtime fairness
+ * scheduling.
+ *
+ * @NL80211_EXT_FEATURE_AP_PMKSA_CACHING: Driver/device supports PMKSA caching
+ * (set/del PMKSA operations) in AP mode.
+ *
+ * @NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD: Driver supports
+ * filtering of sched scan results using band specific RSSI thresholds.
+ *
+ * @NL80211_EXT_FEATURE_STA_TX_PWR: This driver supports controlling tx power
+ * to a station.
+ *
+ * @NL80211_EXT_FEATURE_SAE_OFFLOAD: Device wants to do SAE authentication in
+ * station mode (SAE password is passed as part of the connect command).
+ *
+ * @NL80211_EXT_FEATURE_VLAN_OFFLOAD: The driver supports a single netdev
+ * with VLAN tagged frames and separate VLAN-specific netdevs added using
+ * vconfig similarly to the Ethernet case.
+ *
+ * @NL80211_EXT_FEATURE_AQL: The driver supports the Airtime Queue Limit (AQL)
+ * feature, which prevents bufferbloat by using the expected transmission
+ * time to limit the amount of data buffered in the hardware.
+ *
+ * @NL80211_EXT_FEATURE_BEACON_PROTECTION: The driver supports Beacon protection
+ * and can receive key configuration for BIGTK using key indexes 6 and 7.
+ * @NL80211_EXT_FEATURE_BEACON_PROTECTION_CLIENT: The driver supports Beacon
+ * protection as a client only and cannot transmit protected beacons.
+ *
+ * @NL80211_EXT_FEATURE_CONTROL_PORT_NO_PREAUTH: The driver can disable the
+ * forwarding of preauth frames over the control port. They are then
+ * handled as ordinary data frames.
+ *
+ * @NL80211_EXT_FEATURE_PROTECTED_TWT: Driver supports protected TWT frames
+ *
+ * @NL80211_EXT_FEATURE_DEL_IBSS_STA: The driver supports removing stations
+ * in IBSS mode, essentially by dropping their state.
+ *
+ * @NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS: management frame registrations
+ * are possible for multicast frames and those will be reported properly.
+ *
+ * @NL80211_EXT_FEATURE_SCAN_FREQ_KHZ: This driver supports receiving and
+ * reporting scan request with %NL80211_ATTR_SCAN_FREQ_KHZ. In order to
+ * report %NL80211_ATTR_SCAN_FREQ_KHZ, %NL80211_SCAN_FLAG_FREQ_KHZ must be
+ * included in the scan request.
+ *
+ * @NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211_TX_STATUS: The driver
+ * can report tx status for control port over nl80211 tx operations.
+ *
+ * @NL80211_EXT_FEATURE_OPERATING_CHANNEL_VALIDATION: Driver supports Operating
+ * Channel Validation (OCV) when using driver's SME for RSNA handshakes.
+ *
+ * @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_AP_PSK: Device wants to do 4-way
+ * handshake with PSK in AP mode (PSK is passed as part of the start AP
+ * command).
+ *
+ * @NL80211_EXT_FEATURE_SAE_OFFLOAD_AP: Device wants to do SAE authentication
+ * in AP mode (SAE password is passed as part of the start AP command).
+ *
+ * @NL80211_EXT_FEATURE_FILS_DISCOVERY: Driver/device supports FILS discovery
+ * frames transmission
+ *
+ * @NL80211_EXT_FEATURE_UNSOL_BCAST_PROBE_RESP: Driver/device supports
+ * unsolicited broadcast probe response transmission
+ *
+ * @NL80211_EXT_FEATURE_BEACON_RATE_HE: Driver supports beacon rate
+ * configuration (AP/mesh) with HE rates.
+ *
+ * @NL80211_EXT_FEATURE_SECURE_LTF: Device supports secure LTF measurement
+ * exchange protocol.
+ *
+ * @NL80211_EXT_FEATURE_SECURE_RTT: Device supports secure RTT measurement
+ * exchange protocol.
+ *
+ * @NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE: Device supports management
+ * frame protection for all management frames exchanged during the
+ * negotiation and range measurement procedure.
+ *
+ * @NL80211_EXT_FEATURE_BSS_COLOR: The driver supports BSS color collision
+ * detection and change announcemnts.
+ *
+ * @NL80211_EXT_FEATURE_FILS_CRYPTO_OFFLOAD: Driver running in AP mode supports
+ * FILS encryption and decryption for (Re)Association Request and Response
+ * frames. Userspace has to share FILS AAD details to the driver by using
+ * @NL80211_CMD_SET_FILS_AAD.
+ *
+ * @NL80211_EXT_FEATURE_RADAR_BACKGROUND: Device supports background radar/CAC
+ * detection.
+ *
+ * @NL80211_EXT_FEATURE_POWERED_ADDR_CHANGE: Device can perform a MAC address
+ * change without having to bring the underlying network device down
+ * first. For example, in station mode this can be used to vary the
+ * origin MAC address prior to a connection to a new AP for privacy
+ * or other reasons. Note that certain driver specific restrictions
+ * might apply, e.g. no scans in progress, no offchannel operations
+ * in progress, and no active connections.
+ *
+ * @NL80211_EXT_FEATURE_PUNCT: Driver supports preamble puncturing in AP mode.
+ *
+ * @NL80211_EXT_FEATURE_SECURE_NAN: Device supports NAN Pairing which enables
+ * authentication, data encryption and message integrity.
+ *
+ * @NL80211_EXT_FEATURE_AUTH_AND_DEAUTH_RANDOM_TA: Device supports randomized TA
+ * in authentication and deauthentication frames sent to unassociated peer
+ * using @NL80211_CMD_FRAME.
+ *
+ * @NUM_NL80211_EXT_FEATURES: number of extended features.
+ * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
+ */
+enum nl80211_ext_feature_index {
+ NL80211_EXT_FEATURE_VHT_IBSS,
+ NL80211_EXT_FEATURE_RRM,
+ NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER,
+ NL80211_EXT_FEATURE_SCAN_START_TIME,
+ NL80211_EXT_FEATURE_BSS_PARENT_TSF,
+ NL80211_EXT_FEATURE_SET_SCAN_DWELL,
+ NL80211_EXT_FEATURE_BEACON_RATE_LEGACY,
+ NL80211_EXT_FEATURE_BEACON_RATE_HT,
+ NL80211_EXT_FEATURE_BEACON_RATE_VHT,
+ NL80211_EXT_FEATURE_FILS_STA,
+ NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA,
+ NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED,
+ NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI,
+ NL80211_EXT_FEATURE_CQM_RSSI_LIST,
+ NL80211_EXT_FEATURE_FILS_SK_OFFLOAD,
+ NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK,
+ NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X,
+ NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME,
+ NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP,
+ NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE,
+ NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION,
+ NL80211_EXT_FEATURE_MFP_OPTIONAL,
+ NL80211_EXT_FEATURE_LOW_SPAN_SCAN,
+ NL80211_EXT_FEATURE_LOW_POWER_SCAN,
+ NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN,
+ NL80211_EXT_FEATURE_DFS_OFFLOAD,
+ NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211,
+ NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT,
+ /* we renamed this - stay compatible */
+ NL80211_EXT_FEATURE_DATA_ACK_SIGNAL_SUPPORT = NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT,
+ NL80211_EXT_FEATURE_TXQS,
+ NL80211_EXT_FEATURE_SCAN_RANDOM_SN,
+ NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT,
+ NL80211_EXT_FEATURE_CAN_REPLACE_PTK0,
+ NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER,
+ NL80211_EXT_FEATURE_AIRTIME_FAIRNESS,
+ NL80211_EXT_FEATURE_AP_PMKSA_CACHING,
+ NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD,
+ NL80211_EXT_FEATURE_EXT_KEY_ID,
+ NL80211_EXT_FEATURE_STA_TX_PWR,
+ NL80211_EXT_FEATURE_SAE_OFFLOAD,
+ NL80211_EXT_FEATURE_VLAN_OFFLOAD,
+ NL80211_EXT_FEATURE_AQL,
+ NL80211_EXT_FEATURE_BEACON_PROTECTION,
+ NL80211_EXT_FEATURE_CONTROL_PORT_NO_PREAUTH,
+ NL80211_EXT_FEATURE_PROTECTED_TWT,
+ NL80211_EXT_FEATURE_DEL_IBSS_STA,
+ NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS,
+ NL80211_EXT_FEATURE_BEACON_PROTECTION_CLIENT,
+ NL80211_EXT_FEATURE_SCAN_FREQ_KHZ,
+ NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211_TX_STATUS,
+ NL80211_EXT_FEATURE_OPERATING_CHANNEL_VALIDATION,
+ NL80211_EXT_FEATURE_4WAY_HANDSHAKE_AP_PSK,
+ NL80211_EXT_FEATURE_SAE_OFFLOAD_AP,
+ NL80211_EXT_FEATURE_FILS_DISCOVERY,
+ NL80211_EXT_FEATURE_UNSOL_BCAST_PROBE_RESP,
+ NL80211_EXT_FEATURE_BEACON_RATE_HE,
+ NL80211_EXT_FEATURE_SECURE_LTF,
+ NL80211_EXT_FEATURE_SECURE_RTT,
+ NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE,
+ NL80211_EXT_FEATURE_BSS_COLOR,
+ NL80211_EXT_FEATURE_FILS_CRYPTO_OFFLOAD,
+ NL80211_EXT_FEATURE_RADAR_BACKGROUND,
+ NL80211_EXT_FEATURE_POWERED_ADDR_CHANGE,
+ NL80211_EXT_FEATURE_PUNCT,
+ NL80211_EXT_FEATURE_SECURE_NAN,
+ NL80211_EXT_FEATURE_AUTH_AND_DEAUTH_RANDOM_TA,
+
+ /* add new features before the definition below */
+ NUM_NL80211_EXT_FEATURES,
+ MAX_NL80211_EXT_FEATURES = NUM_NL80211_EXT_FEATURES - 1
+};
+
+/**
+ * enum nl80211_probe_resp_offload_support_attr - optional supported
+ * protocols for probe-response offloading by the driver/FW.
+ * To be used with the %NL80211_ATTR_PROBE_RESP_OFFLOAD attribute.
+ * Each enum value represents a bit in the bitmap of supported
+ * protocols. Typically a subset of probe-requests belonging to a
+ * supported protocol will be excluded from offload and uploaded
+ * to the host.
+ *
+ * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS: Support for WPS ver. 1
+ * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2: Support for WPS ver. 2
+ * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P: Support for P2P
+ * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U: Support for 802.11u
+ */
+enum nl80211_probe_resp_offload_support_attr {
+ NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS = 1<<0,
+ NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 = 1<<1,
+ NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P = 1<<2,
+ NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U = 1<<3,
+};
+
+/**
+ * enum nl80211_connect_failed_reason - connection request failed reasons
+ * @NL80211_CONN_FAIL_MAX_CLIENTS: Maximum number of clients that can be
+ * handled by the AP is reached.
+ * @NL80211_CONN_FAIL_BLOCKED_CLIENT: Connection request is rejected due to ACL.
+ */
+enum nl80211_connect_failed_reason {
+ NL80211_CONN_FAIL_MAX_CLIENTS,
+ NL80211_CONN_FAIL_BLOCKED_CLIENT,
+};
+
+/**
+ * enum nl80211_timeout_reason - timeout reasons
+ *
+ * @NL80211_TIMEOUT_UNSPECIFIED: Timeout reason unspecified.
+ * @NL80211_TIMEOUT_SCAN: Scan (AP discovery) timed out.
+ * @NL80211_TIMEOUT_AUTH: Authentication timed out.
+ * @NL80211_TIMEOUT_ASSOC: Association timed out.
+ */
+enum nl80211_timeout_reason {
+ NL80211_TIMEOUT_UNSPECIFIED,
+ NL80211_TIMEOUT_SCAN,
+ NL80211_TIMEOUT_AUTH,
+ NL80211_TIMEOUT_ASSOC,
+};
+
+/**
+ * enum nl80211_scan_flags - scan request control flags
+ *
+ * Scan request control flags are used to control the handling
+ * of NL80211_CMD_TRIGGER_SCAN and NL80211_CMD_START_SCHED_SCAN
+ * requests.
+ *
+ * NL80211_SCAN_FLAG_LOW_SPAN, NL80211_SCAN_FLAG_LOW_POWER, and
+ * NL80211_SCAN_FLAG_HIGH_ACCURACY flags are exclusive of each other, i.e., only
+ * one of them can be used in the request.
+ *
+ * @NL80211_SCAN_FLAG_LOW_PRIORITY: scan request has low priority
+ * @NL80211_SCAN_FLAG_FLUSH: flush cache before scanning
+ * @NL80211_SCAN_FLAG_AP: force a scan even if the interface is configured
+ * as AP and the beaconing has already been configured. This attribute is
+ * dangerous because will destroy stations performance as a lot of frames
+ * will be lost while scanning off-channel, therefore it must be used only
+ * when really needed
+ * @NL80211_SCAN_FLAG_RANDOM_ADDR: use a random MAC address for this scan (or
+ * for scheduled scan: a different one for every scan iteration). When the
+ * flag is set, depending on device capabilities the @NL80211_ATTR_MAC and
+ * @NL80211_ATTR_MAC_MASK attributes may also be given in which case only
+ * the masked bits will be preserved from the MAC address and the remainder
+ * randomised. If the attributes are not given full randomisation (46 bits,
+ * locally administered 1, multicast 0) is assumed.
+ * This flag must not be requested when the feature isn't supported, check
+ * the nl80211 feature flags for the device.
+ * @NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME: fill the dwell time in the FILS
+ * request parameters IE in the probe request
+ * @NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP: accept broadcast probe responses
+ * @NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE: send probe request frames at
+ * rate of at least 5.5M. In case non OCE AP is discovered in the channel,
+ * only the first probe req in the channel will be sent in high rate.
+ * @NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION: allow probe request
+ * tx deferral (dot11FILSProbeDelay shall be set to 15ms)
+ * and suppression (if it has received a broadcast Probe Response frame,
+ * Beacon frame or FILS Discovery frame from an AP that the STA considers
+ * a suitable candidate for (re-)association - suitable in terms of
+ * SSID and/or RSSI.
+ * @NL80211_SCAN_FLAG_LOW_SPAN: Span corresponds to the total time taken to
+ * accomplish the scan. Thus, this flag intends the driver to perform the
+ * scan request with lesser span/duration. It is specific to the driver
+ * implementations on how this is accomplished. Scan accuracy may get
+ * impacted with this flag.
+ * @NL80211_SCAN_FLAG_LOW_POWER: This flag intends the scan attempts to consume
+ * optimal possible power. Drivers can resort to their specific means to
+ * optimize the power. Scan accuracy may get impacted with this flag.
+ * @NL80211_SCAN_FLAG_HIGH_ACCURACY: Accuracy here intends to the extent of scan
+ * results obtained. Thus HIGH_ACCURACY scan flag aims to get maximum
+ * possible scan results. This flag hints the driver to use the best
+ * possible scan configuration to improve the accuracy in scanning.
+ * Latency and power use may get impacted with this flag.
+ * @NL80211_SCAN_FLAG_RANDOM_SN: randomize the sequence number in probe
+ * request frames from this scan to avoid correlation/tracking being
+ * possible.
+ * @NL80211_SCAN_FLAG_MIN_PREQ_CONTENT: minimize probe request content to
+ * only have supported rates and no additional capabilities (unless
+ * added by userspace explicitly.)
+ * @NL80211_SCAN_FLAG_FREQ_KHZ: report scan results with
+ * %NL80211_ATTR_SCAN_FREQ_KHZ. This also means
+ * %NL80211_ATTR_SCAN_FREQUENCIES will not be included.
+ * @NL80211_SCAN_FLAG_COLOCATED_6GHZ: scan for collocated APs reported by
+ * 2.4/5 GHz APs. When the flag is set, the scan logic will use the
+ * information from the RNR element found in beacons/probe responses
+ * received on the 2.4/5 GHz channels to actively scan only the 6GHz
+ * channels on which APs are expected to be found. Note that when not set,
+ * the scan logic would scan all 6GHz channels, but since transmission of
+ * probe requests on non PSC channels is limited, it is highly likely that
+ * these channels would passively be scanned. Also note that when the flag
+ * is set, in addition to the colocated APs, PSC channels would also be
+ * scanned if the user space has asked for it.
+ */
+enum nl80211_scan_flags {
+ NL80211_SCAN_FLAG_LOW_PRIORITY = 1<<0,
+ NL80211_SCAN_FLAG_FLUSH = 1<<1,
+ NL80211_SCAN_FLAG_AP = 1<<2,
+ NL80211_SCAN_FLAG_RANDOM_ADDR = 1<<3,
+ NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME = 1<<4,
+ NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP = 1<<5,
+ NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE = 1<<6,
+ NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION = 1<<7,
+ NL80211_SCAN_FLAG_LOW_SPAN = 1<<8,
+ NL80211_SCAN_FLAG_LOW_POWER = 1<<9,
+ NL80211_SCAN_FLAG_HIGH_ACCURACY = 1<<10,
+ NL80211_SCAN_FLAG_RANDOM_SN = 1<<11,
+ NL80211_SCAN_FLAG_MIN_PREQ_CONTENT = 1<<12,
+ NL80211_SCAN_FLAG_FREQ_KHZ = 1<<13,
+ NL80211_SCAN_FLAG_COLOCATED_6GHZ = 1<<14,
+};
+
+/**
+ * enum nl80211_acl_policy - access control policy
+ *
+ * Access control policy is applied on a MAC list set by
+ * %NL80211_CMD_START_AP and %NL80211_CMD_SET_MAC_ACL, to
+ * be used with %NL80211_ATTR_ACL_POLICY.
+ *
+ * @NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED: Deny stations which are
+ * listed in ACL, i.e. allow all the stations which are not listed
+ * in ACL to authenticate.
+ * @NL80211_ACL_POLICY_DENY_UNLESS_LISTED: Allow the stations which are listed
+ * in ACL, i.e. deny all the stations which are not listed in ACL.
+ */
+enum nl80211_acl_policy {
+ NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED,
+ NL80211_ACL_POLICY_DENY_UNLESS_LISTED,
+};
+
+/**
+ * enum nl80211_smps_mode - SMPS mode
+ *
+ * Requested SMPS mode (for AP mode)
+ *
+ * @NL80211_SMPS_OFF: SMPS off (use all antennas).
+ * @NL80211_SMPS_STATIC: static SMPS (use a single antenna)
+ * @NL80211_SMPS_DYNAMIC: dynamic smps (start with a single antenna and
+ * turn on other antennas after CTS/RTS).
+ */
+enum nl80211_smps_mode {
+ NL80211_SMPS_OFF,
+ NL80211_SMPS_STATIC,
+ NL80211_SMPS_DYNAMIC,
+
+ __NL80211_SMPS_AFTER_LAST,
+ NL80211_SMPS_MAX = __NL80211_SMPS_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_radar_event - type of radar event for DFS operation
+ *
+ * Type of event to be used with NL80211_ATTR_RADAR_EVENT to inform userspace
+ * about detected radars or success of the channel available check (CAC)
+ *
+ * @NL80211_RADAR_DETECTED: A radar pattern has been detected. The channel is
+ * now unusable.
+ * @NL80211_RADAR_CAC_FINISHED: Channel Availability Check has been finished,
+ * the channel is now available.
+ * @NL80211_RADAR_CAC_ABORTED: Channel Availability Check has been aborted, no
+ * change to the channel status.
+ * @NL80211_RADAR_NOP_FINISHED: The Non-Occupancy Period for this channel is
+ * over, channel becomes usable.
+ * @NL80211_RADAR_PRE_CAC_EXPIRED: Channel Availability Check done on this
+ * non-operating channel is expired and no longer valid. New CAC must
+ * be done on this channel before starting the operation. This is not
+ * applicable for ETSI dfs domain where pre-CAC is valid for ever.
+ * @NL80211_RADAR_CAC_STARTED: Channel Availability Check has been started,
+ * should be generated by HW if NL80211_EXT_FEATURE_DFS_OFFLOAD is enabled.
+ */
+enum nl80211_radar_event {
+ NL80211_RADAR_DETECTED,
+ NL80211_RADAR_CAC_FINISHED,
+ NL80211_RADAR_CAC_ABORTED,
+ NL80211_RADAR_NOP_FINISHED,
+ NL80211_RADAR_PRE_CAC_EXPIRED,
+ NL80211_RADAR_CAC_STARTED,
+};
+
+/**
+ * enum nl80211_dfs_state - DFS states for channels
+ *
+ * Channel states used by the DFS code.
+ *
+ * @NL80211_DFS_USABLE: The channel can be used, but channel availability
+ * check (CAC) must be performed before using it for AP or IBSS.
+ * @NL80211_DFS_UNAVAILABLE: A radar has been detected on this channel, it
+ * is therefore marked as not available.
+ * @NL80211_DFS_AVAILABLE: The channel has been CAC checked and is available.
+ */
+enum nl80211_dfs_state {
+ NL80211_DFS_USABLE,
+ NL80211_DFS_UNAVAILABLE,
+ NL80211_DFS_AVAILABLE,
+};
+
+/**
+ * enum nl80211_protocol_features - nl80211 protocol features
+ * @NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP: nl80211 supports splitting
+ * wiphy dumps (if requested by the application with the attribute
+ * %NL80211_ATTR_SPLIT_WIPHY_DUMP. Also supported is filtering the
+ * wiphy dump by %NL80211_ATTR_WIPHY, %NL80211_ATTR_IFINDEX or
+ * %NL80211_ATTR_WDEV.
+ */
+enum nl80211_protocol_features {
+ NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP = 1 << 0,
+};
+
+/**
+ * enum nl80211_crit_proto_id - nl80211 critical protocol identifiers
+ *
+ * @NL80211_CRIT_PROTO_UNSPEC: protocol unspecified.
+ * @NL80211_CRIT_PROTO_DHCP: BOOTP or DHCPv6 protocol.
+ * @NL80211_CRIT_PROTO_EAPOL: EAPOL protocol.
+ * @NL80211_CRIT_PROTO_APIPA: APIPA protocol.
+ * @NUM_NL80211_CRIT_PROTO: must be kept last.
+ */
+enum nl80211_crit_proto_id {
+ NL80211_CRIT_PROTO_UNSPEC,
+ NL80211_CRIT_PROTO_DHCP,
+ NL80211_CRIT_PROTO_EAPOL,
+ NL80211_CRIT_PROTO_APIPA,
+ /* add other protocols before this one */
+ NUM_NL80211_CRIT_PROTO
+};
+
+/* maximum duration for critical protocol measures */
+#define NL80211_CRIT_PROTO_MAX_DURATION 5000 /* msec */
+
+/**
+ * enum nl80211_rxmgmt_flags - flags for received management frame.
+ *
+ * Used by cfg80211_rx_mgmt()
+ *
+ * @NL80211_RXMGMT_FLAG_ANSWERED: frame was answered by device/driver.
+ * @NL80211_RXMGMT_FLAG_EXTERNAL_AUTH: Host driver intends to offload
+ * the authentication. Exclusively defined for host drivers that
+ * advertises the SME functionality but would like the userspace
+ * to handle certain authentication algorithms (e.g. SAE).
+ */
+enum nl80211_rxmgmt_flags {
+ NL80211_RXMGMT_FLAG_ANSWERED = 1 << 0,
+ NL80211_RXMGMT_FLAG_EXTERNAL_AUTH = 1 << 1,
+};
+
+/*
+ * If this flag is unset, the lower 24 bits are an OUI, if set
+ * a Linux nl80211 vendor ID is used (no such IDs are allocated
+ * yet, so that's not valid so far)
+ */
+#define NL80211_VENDOR_ID_IS_LINUX 0x80000000
+
+/**
+ * struct nl80211_vendor_cmd_info - vendor command data
+ * @vendor_id: If the %NL80211_VENDOR_ID_IS_LINUX flag is clear, then the
+ * value is a 24-bit OUI; if it is set then a separately allocated ID
+ * may be used, but no such IDs are allocated yet. New IDs should be
+ * added to this file when needed.
+ * @subcmd: sub-command ID for the command
+ */
+struct nl80211_vendor_cmd_info {
+ __u32 vendor_id;
+ __u32 subcmd;
+};
+
+/**
+ * enum nl80211_tdls_peer_capability - TDLS peer flags.
+ *
+ * Used by tdls_mgmt() to determine which conditional elements need
+ * to be added to TDLS Setup frames.
+ *
+ * @NL80211_TDLS_PEER_HT: TDLS peer is HT capable.
+ * @NL80211_TDLS_PEER_VHT: TDLS peer is VHT capable.
+ * @NL80211_TDLS_PEER_WMM: TDLS peer is WMM capable.
+ * @NL80211_TDLS_PEER_HE: TDLS peer is HE capable.
+ */
+enum nl80211_tdls_peer_capability {
+ NL80211_TDLS_PEER_HT = 1<<0,
+ NL80211_TDLS_PEER_VHT = 1<<1,
+ NL80211_TDLS_PEER_WMM = 1<<2,
+ NL80211_TDLS_PEER_HE = 1<<3,
+};
+
+/**
+ * enum nl80211_sched_scan_plan - scanning plan for scheduled scan
+ * @__NL80211_SCHED_SCAN_PLAN_INVALID: attribute number 0 is reserved
+ * @NL80211_SCHED_SCAN_PLAN_INTERVAL: interval between scan iterations. In
+ * seconds (u32).
+ * @NL80211_SCHED_SCAN_PLAN_ITERATIONS: number of scan iterations in this
+ * scan plan (u32). The last scan plan must not specify this attribute
+ * because it will run infinitely. A value of zero is invalid as it will
+ * make the scan plan meaningless.
+ * @NL80211_SCHED_SCAN_PLAN_MAX: highest scheduled scan plan attribute number
+ * currently defined
+ * @__NL80211_SCHED_SCAN_PLAN_AFTER_LAST: internal use
+ */
+enum nl80211_sched_scan_plan {
+ __NL80211_SCHED_SCAN_PLAN_INVALID,
+ NL80211_SCHED_SCAN_PLAN_INTERVAL,
+ NL80211_SCHED_SCAN_PLAN_ITERATIONS,
+
+ /* keep last */
+ __NL80211_SCHED_SCAN_PLAN_AFTER_LAST,
+ NL80211_SCHED_SCAN_PLAN_MAX =
+ __NL80211_SCHED_SCAN_PLAN_AFTER_LAST - 1
+};
+
+/**
+ * struct nl80211_bss_select_rssi_adjust - RSSI adjustment parameters.
+ *
+ * @band: band of BSS that must match for RSSI value adjustment. The value
+ * of this field is according to &enum nl80211_band.
+ * @delta: value used to adjust the RSSI value of matching BSS in dB.
+ */
+struct nl80211_bss_select_rssi_adjust {
+ __u8 band;
+ __s8 delta;
+} __attribute__((packed));
+
+/**
+ * enum nl80211_bss_select_attr - attributes for bss selection.
+ *
+ * @__NL80211_BSS_SELECT_ATTR_INVALID: reserved.
+ * @NL80211_BSS_SELECT_ATTR_RSSI: Flag indicating only RSSI-based BSS selection
+ * is requested.
+ * @NL80211_BSS_SELECT_ATTR_BAND_PREF: attribute indicating BSS
+ * selection should be done such that the specified band is preferred.
+ * When there are multiple BSS-es in the preferred band, the driver
+ * shall use RSSI-based BSS selection as a second step. The value of
+ * this attribute is according to &enum nl80211_band (u32).
+ * @NL80211_BSS_SELECT_ATTR_RSSI_ADJUST: When present the RSSI level for
+ * BSS-es in the specified band is to be adjusted before doing
+ * RSSI-based BSS selection. The attribute value is a packed structure
+ * value as specified by &struct nl80211_bss_select_rssi_adjust.
+ * @NL80211_BSS_SELECT_ATTR_MAX: highest bss select attribute number.
+ * @__NL80211_BSS_SELECT_ATTR_AFTER_LAST: internal use.
+ *
+ * One and only one of these attributes are found within %NL80211_ATTR_BSS_SELECT
+ * for %NL80211_CMD_CONNECT. It specifies the required BSS selection behaviour
+ * which the driver shall use.
+ */
+enum nl80211_bss_select_attr {
+ __NL80211_BSS_SELECT_ATTR_INVALID,
+ NL80211_BSS_SELECT_ATTR_RSSI,
+ NL80211_BSS_SELECT_ATTR_BAND_PREF,
+ NL80211_BSS_SELECT_ATTR_RSSI_ADJUST,
+
+ /* keep last */
+ __NL80211_BSS_SELECT_ATTR_AFTER_LAST,
+ NL80211_BSS_SELECT_ATTR_MAX = __NL80211_BSS_SELECT_ATTR_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_nan_function_type - NAN function type
+ *
+ * Defines the function type of a NAN function
+ *
+ * @NL80211_NAN_FUNC_PUBLISH: function is publish
+ * @NL80211_NAN_FUNC_SUBSCRIBE: function is subscribe
+ * @NL80211_NAN_FUNC_FOLLOW_UP: function is follow-up
+ */
+enum nl80211_nan_function_type {
+ NL80211_NAN_FUNC_PUBLISH,
+ NL80211_NAN_FUNC_SUBSCRIBE,
+ NL80211_NAN_FUNC_FOLLOW_UP,
+
+ /* keep last */
+ __NL80211_NAN_FUNC_TYPE_AFTER_LAST,
+ NL80211_NAN_FUNC_MAX_TYPE = __NL80211_NAN_FUNC_TYPE_AFTER_LAST - 1,
+};
+
+/**
+ * enum nl80211_nan_publish_type - NAN publish tx type
+ *
+ * Defines how to send publish Service Discovery Frames
+ *
+ * @NL80211_NAN_SOLICITED_PUBLISH: publish function is solicited
+ * @NL80211_NAN_UNSOLICITED_PUBLISH: publish function is unsolicited
+ */
+enum nl80211_nan_publish_type {
+ NL80211_NAN_SOLICITED_PUBLISH = 1 << 0,
+ NL80211_NAN_UNSOLICITED_PUBLISH = 1 << 1,
+};
+
+/**
+ * enum nl80211_nan_func_term_reason - NAN functions termination reason
+ *
+ * Defines termination reasons of a NAN function
+ *
+ * @NL80211_NAN_FUNC_TERM_REASON_USER_REQUEST: requested by user
+ * @NL80211_NAN_FUNC_TERM_REASON_TTL_EXPIRED: timeout
+ * @NL80211_NAN_FUNC_TERM_REASON_ERROR: errored
+ */
+enum nl80211_nan_func_term_reason {
+ NL80211_NAN_FUNC_TERM_REASON_USER_REQUEST,
+ NL80211_NAN_FUNC_TERM_REASON_TTL_EXPIRED,
+ NL80211_NAN_FUNC_TERM_REASON_ERROR,
+};
+
+#define NL80211_NAN_FUNC_SERVICE_ID_LEN 6
+#define NL80211_NAN_FUNC_SERVICE_SPEC_INFO_MAX_LEN 0xff
+#define NL80211_NAN_FUNC_SRF_MAX_LEN 0xff
+
+/**
+ * enum nl80211_nan_func_attributes - NAN function attributes
+ * @__NL80211_NAN_FUNC_INVALID: invalid
+ * @NL80211_NAN_FUNC_TYPE: &enum nl80211_nan_function_type (u8).
+ * @NL80211_NAN_FUNC_SERVICE_ID: 6 bytes of the service ID hash as
+ * specified in NAN spec. This is a binary attribute.
+ * @NL80211_NAN_FUNC_PUBLISH_TYPE: relevant if the function's type is
+ * publish. Defines the transmission type for the publish Service Discovery
+ * Frame, see &enum nl80211_nan_publish_type. Its type is u8.
+ * @NL80211_NAN_FUNC_PUBLISH_BCAST: relevant if the function is a solicited
+ * publish. Should the solicited publish Service Discovery Frame be sent to
+ * the NAN Broadcast address. This is a flag.
+ * @NL80211_NAN_FUNC_SUBSCRIBE_ACTIVE: relevant if the function's type is
+ * subscribe. Is the subscribe active. This is a flag.
+ * @NL80211_NAN_FUNC_FOLLOW_UP_ID: relevant if the function's type is follow up.
+ * The instance ID for the follow up Service Discovery Frame. This is u8.
+ * @NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID: relevant if the function's type
+ * is follow up. This is a u8.
+ * The requestor instance ID for the follow up Service Discovery Frame.
+ * @NL80211_NAN_FUNC_FOLLOW_UP_DEST: the MAC address of the recipient of the
+ * follow up Service Discovery Frame. This is a binary attribute.
+ * @NL80211_NAN_FUNC_CLOSE_RANGE: is this function limited for devices in a
+ * close range. The range itself (RSSI) is defined by the device.
+ * This is a flag.
+ * @NL80211_NAN_FUNC_TTL: strictly positive number of DWs this function should
+ * stay active. If not present infinite TTL is assumed. This is a u32.
+ * @NL80211_NAN_FUNC_SERVICE_INFO: array of bytes describing the service
+ * specific info. This is a binary attribute.
+ * @NL80211_NAN_FUNC_SRF: Service Receive Filter. This is a nested attribute.
+ * See &enum nl80211_nan_srf_attributes.
+ * @NL80211_NAN_FUNC_RX_MATCH_FILTER: Receive Matching filter. This is a nested
+ * attribute. It is a list of binary values.
+ * @NL80211_NAN_FUNC_TX_MATCH_FILTER: Transmit Matching filter. This is a
+ * nested attribute. It is a list of binary values.
+ * @NL80211_NAN_FUNC_INSTANCE_ID: The instance ID of the function.
+ * Its type is u8 and it cannot be 0.
+ * @NL80211_NAN_FUNC_TERM_REASON: NAN function termination reason.
+ * See &enum nl80211_nan_func_term_reason.
+ *
+ * @NUM_NL80211_NAN_FUNC_ATTR: internal
+ * @NL80211_NAN_FUNC_ATTR_MAX: highest NAN function attribute
+ */
+enum nl80211_nan_func_attributes {
+ __NL80211_NAN_FUNC_INVALID,
+ NL80211_NAN_FUNC_TYPE,
+ NL80211_NAN_FUNC_SERVICE_ID,
+ NL80211_NAN_FUNC_PUBLISH_TYPE,
+ NL80211_NAN_FUNC_PUBLISH_BCAST,
+ NL80211_NAN_FUNC_SUBSCRIBE_ACTIVE,
+ NL80211_NAN_FUNC_FOLLOW_UP_ID,
+ NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID,
+ NL80211_NAN_FUNC_FOLLOW_UP_DEST,
+ NL80211_NAN_FUNC_CLOSE_RANGE,
+ NL80211_NAN_FUNC_TTL,
+ NL80211_NAN_FUNC_SERVICE_INFO,
+ NL80211_NAN_FUNC_SRF,
+ NL80211_NAN_FUNC_RX_MATCH_FILTER,
+ NL80211_NAN_FUNC_TX_MATCH_FILTER,
+ NL80211_NAN_FUNC_INSTANCE_ID,
+ NL80211_NAN_FUNC_TERM_REASON,
+
+ /* keep last */
+ NUM_NL80211_NAN_FUNC_ATTR,
+ NL80211_NAN_FUNC_ATTR_MAX = NUM_NL80211_NAN_FUNC_ATTR - 1
+};
+
+/**
+ * enum nl80211_nan_srf_attributes - NAN Service Response filter attributes
+ * @__NL80211_NAN_SRF_INVALID: invalid
+ * @NL80211_NAN_SRF_INCLUDE: present if the include bit of the SRF set.
+ * This is a flag.
+ * @NL80211_NAN_SRF_BF: Bloom Filter. Present if and only if
+ * %NL80211_NAN_SRF_MAC_ADDRS isn't present. This attribute is binary.
+ * @NL80211_NAN_SRF_BF_IDX: index of the Bloom Filter. Mandatory if
+ * %NL80211_NAN_SRF_BF is present. This is a u8.
+ * @NL80211_NAN_SRF_MAC_ADDRS: list of MAC addresses for the SRF. Present if
+ * and only if %NL80211_NAN_SRF_BF isn't present. This is a nested
+ * attribute. Each nested attribute is a MAC address.
+ * @NUM_NL80211_NAN_SRF_ATTR: internal
+ * @NL80211_NAN_SRF_ATTR_MAX: highest NAN SRF attribute
+ */
+enum nl80211_nan_srf_attributes {
+ __NL80211_NAN_SRF_INVALID,
+ NL80211_NAN_SRF_INCLUDE,
+ NL80211_NAN_SRF_BF,
+ NL80211_NAN_SRF_BF_IDX,
+ NL80211_NAN_SRF_MAC_ADDRS,
+
+ /* keep last */
+ NUM_NL80211_NAN_SRF_ATTR,
+ NL80211_NAN_SRF_ATTR_MAX = NUM_NL80211_NAN_SRF_ATTR - 1,
+};
+
+/**
+ * enum nl80211_nan_match_attributes - NAN match attributes
+ * @__NL80211_NAN_MATCH_INVALID: invalid
+ * @NL80211_NAN_MATCH_FUNC_LOCAL: the local function that had the
+ * match. This is a nested attribute.
+ * See &enum nl80211_nan_func_attributes.
+ * @NL80211_NAN_MATCH_FUNC_PEER: the peer function
+ * that caused the match. This is a nested attribute.
+ * See &enum nl80211_nan_func_attributes.
+ *
+ * @NUM_NL80211_NAN_MATCH_ATTR: internal
+ * @NL80211_NAN_MATCH_ATTR_MAX: highest NAN match attribute
+ */
+enum nl80211_nan_match_attributes {
+ __NL80211_NAN_MATCH_INVALID,
+ NL80211_NAN_MATCH_FUNC_LOCAL,
+ NL80211_NAN_MATCH_FUNC_PEER,
+
+ /* keep last */
+ NUM_NL80211_NAN_MATCH_ATTR,
+ NL80211_NAN_MATCH_ATTR_MAX = NUM_NL80211_NAN_MATCH_ATTR - 1
+};
+
+/**
+ * nl80211_external_auth_action - Action to perform with external
+ * authentication request. Used by NL80211_ATTR_EXTERNAL_AUTH_ACTION.
+ * @NL80211_EXTERNAL_AUTH_START: Start the authentication.
+ * @NL80211_EXTERNAL_AUTH_ABORT: Abort the ongoing authentication.
+ */
+enum nl80211_external_auth_action {
+ NL80211_EXTERNAL_AUTH_START,
+ NL80211_EXTERNAL_AUTH_ABORT,
+};
+
+/**
+ * enum nl80211_ftm_responder_attributes - fine timing measurement
+ * responder attributes
+ * @__NL80211_FTM_RESP_ATTR_INVALID: Invalid
+ * @NL80211_FTM_RESP_ATTR_ENABLED: FTM responder is enabled
+ * @NL80211_FTM_RESP_ATTR_LCI: The content of Measurement Report Element
+ * (9.4.2.22 in 802.11-2016) with type 8 - LCI (9.4.2.22.10),
+ * i.e. starting with the measurement token
+ * @NL80211_FTM_RESP_ATTR_CIVIC: The content of Measurement Report Element
+ * (9.4.2.22 in 802.11-2016) with type 11 - Civic (Section 9.4.2.22.13),
+ * i.e. starting with the measurement token
+ * @__NL80211_FTM_RESP_ATTR_LAST: Internal
+ * @NL80211_FTM_RESP_ATTR_MAX: highest FTM responder attribute.
+ */
+enum nl80211_ftm_responder_attributes {
+ __NL80211_FTM_RESP_ATTR_INVALID,
+
+ NL80211_FTM_RESP_ATTR_ENABLED,
+ NL80211_FTM_RESP_ATTR_LCI,
+ NL80211_FTM_RESP_ATTR_CIVICLOC,
+
+ /* keep last */
+ __NL80211_FTM_RESP_ATTR_LAST,
+ NL80211_FTM_RESP_ATTR_MAX = __NL80211_FTM_RESP_ATTR_LAST - 1,
+};
+
+/*
+ * enum nl80211_ftm_responder_stats - FTM responder statistics
+ *
+ * These attribute types are used with %NL80211_ATTR_FTM_RESPONDER_STATS
+ * when getting FTM responder statistics.
+ *
+ * @__NL80211_FTM_STATS_INVALID: attribute number 0 is reserved
+ * @NL80211_FTM_STATS_SUCCESS_NUM: number of FTM sessions in which all frames
+ * were ssfully answered (u32)
+ * @NL80211_FTM_STATS_PARTIAL_NUM: number of FTM sessions in which part of the
+ * frames were successfully answered (u32)
+ * @NL80211_FTM_STATS_FAILED_NUM: number of failed FTM sessions (u32)
+ * @NL80211_FTM_STATS_ASAP_NUM: number of ASAP sessions (u32)
+ * @NL80211_FTM_STATS_NON_ASAP_NUM: number of non-ASAP sessions (u32)
+ * @NL80211_FTM_STATS_TOTAL_DURATION_MSEC: total sessions durations - gives an
+ * indication of how much time the responder was busy (u64, msec)
+ * @NL80211_FTM_STATS_UNKNOWN_TRIGGERS_NUM: number of unknown FTM triggers -
+ * triggers from initiators that didn't finish successfully the negotiation
+ * phase with the responder (u32)
+ * @NL80211_FTM_STATS_RESCHEDULE_REQUESTS_NUM: number of FTM reschedule requests
+ * - initiator asks for a new scheduling although it already has scheduled
+ * FTM slot (u32)
+ * @NL80211_FTM_STATS_OUT_OF_WINDOW_TRIGGERS_NUM: number of FTM triggers out of
+ * scheduled window (u32)
+ * @NL80211_FTM_STATS_PAD: used for padding, ignore
+ * @__NL80211_TXQ_ATTR_AFTER_LAST: Internal
+ * @NL80211_FTM_STATS_MAX: highest possible FTM responder stats attribute
+ */
+enum nl80211_ftm_responder_stats {
+ __NL80211_FTM_STATS_INVALID,
+ NL80211_FTM_STATS_SUCCESS_NUM,
+ NL80211_FTM_STATS_PARTIAL_NUM,
+ NL80211_FTM_STATS_FAILED_NUM,
+ NL80211_FTM_STATS_ASAP_NUM,
+ NL80211_FTM_STATS_NON_ASAP_NUM,
+ NL80211_FTM_STATS_TOTAL_DURATION_MSEC,
+ NL80211_FTM_STATS_UNKNOWN_TRIGGERS_NUM,
+ NL80211_FTM_STATS_RESCHEDULE_REQUESTS_NUM,
+ NL80211_FTM_STATS_OUT_OF_WINDOW_TRIGGERS_NUM,
+ NL80211_FTM_STATS_PAD,
+
+ /* keep last */
+ __NL80211_FTM_STATS_AFTER_LAST,
+ NL80211_FTM_STATS_MAX = __NL80211_FTM_STATS_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_preamble - frame preamble types
+ * @NL80211_PREAMBLE_LEGACY: legacy (HR/DSSS, OFDM, ERP PHY) preamble
+ * @NL80211_PREAMBLE_HT: HT preamble
+ * @NL80211_PREAMBLE_VHT: VHT preamble
+ * @NL80211_PREAMBLE_DMG: DMG preamble
+ * @NL80211_PREAMBLE_HE: HE preamble
+ */
+enum nl80211_preamble {
+ NL80211_PREAMBLE_LEGACY,
+ NL80211_PREAMBLE_HT,
+ NL80211_PREAMBLE_VHT,
+ NL80211_PREAMBLE_DMG,
+ NL80211_PREAMBLE_HE,
+};
+
+/**
+ * enum nl80211_peer_measurement_type - peer measurement types
+ * @NL80211_PMSR_TYPE_INVALID: invalid/unused, needed as we use
+ * these numbers also for attributes
+ *
+ * @NL80211_PMSR_TYPE_FTM: flight time measurement
+ *
+ * @NUM_NL80211_PMSR_TYPES: internal
+ * @NL80211_PMSR_TYPE_MAX: highest type number
+ */
+enum nl80211_peer_measurement_type {
+ NL80211_PMSR_TYPE_INVALID,
+
+ NL80211_PMSR_TYPE_FTM,
+
+ NUM_NL80211_PMSR_TYPES,
+ NL80211_PMSR_TYPE_MAX = NUM_NL80211_PMSR_TYPES - 1
+};
+
+/**
+ * enum nl80211_peer_measurement_status - peer measurement status
+ * @NL80211_PMSR_STATUS_SUCCESS: measurement completed successfully
+ * @NL80211_PMSR_STATUS_REFUSED: measurement was locally refused
+ * @NL80211_PMSR_STATUS_TIMEOUT: measurement timed out
+ * @NL80211_PMSR_STATUS_FAILURE: measurement failed, a type-dependent
+ * reason may be available in the response data
+ */
+enum nl80211_peer_measurement_status {
+ NL80211_PMSR_STATUS_SUCCESS,
+ NL80211_PMSR_STATUS_REFUSED,
+ NL80211_PMSR_STATUS_TIMEOUT,
+ NL80211_PMSR_STATUS_FAILURE,
+};
+
+/**
+ * enum nl80211_peer_measurement_req - peer measurement request attributes
+ * @__NL80211_PMSR_REQ_ATTR_INVALID: invalid
+ *
+ * @NL80211_PMSR_REQ_ATTR_DATA: This is a nested attribute with measurement
+ * type-specific request data inside. The attributes used are from the
+ * enums named nl80211_peer_measurement_<type>_req.
+ * @NL80211_PMSR_REQ_ATTR_GET_AP_TSF: include AP TSF timestamp, if supported
+ * (flag attribute)
+ *
+ * @NUM_NL80211_PMSR_REQ_ATTRS: internal
+ * @NL80211_PMSR_REQ_ATTR_MAX: highest attribute number
+ */
+enum nl80211_peer_measurement_req {
+ __NL80211_PMSR_REQ_ATTR_INVALID,
+
+ NL80211_PMSR_REQ_ATTR_DATA,
+ NL80211_PMSR_REQ_ATTR_GET_AP_TSF,
+
+ /* keep last */
+ NUM_NL80211_PMSR_REQ_ATTRS,
+ NL80211_PMSR_REQ_ATTR_MAX = NUM_NL80211_PMSR_REQ_ATTRS - 1
+};
+
+/**
+ * enum nl80211_peer_measurement_resp - peer measurement response attributes
+ * @__NL80211_PMSR_RESP_ATTR_INVALID: invalid
+ *
+ * @NL80211_PMSR_RESP_ATTR_DATA: This is a nested attribute with measurement
+ * type-specific results inside. The attributes used are from the enums
+ * named nl80211_peer_measurement_<type>_resp.
+ * @NL80211_PMSR_RESP_ATTR_STATUS: u32 value with the measurement status
+ * (using values from &enum nl80211_peer_measurement_status.)
+ * @NL80211_PMSR_RESP_ATTR_HOST_TIME: host time (%CLOCK_BOOTTIME) when the
+ * result was measured; this value is not expected to be accurate to
+ * more than 20ms. (u64, nanoseconds)
+ * @NL80211_PMSR_RESP_ATTR_AP_TSF: TSF of the AP that the interface
+ * doing the measurement is connected to when the result was measured.
+ * This shall be accurately reported if supported and requested
+ * (u64, usec)
+ * @NL80211_PMSR_RESP_ATTR_FINAL: If results are sent to the host partially
+ * (*e.g. with FTM per-burst data) this flag will be cleared on all but
+ * the last result; if all results are combined it's set on the single
+ * result.
+ * @NL80211_PMSR_RESP_ATTR_PAD: padding for 64-bit attributes, ignore
+ *
+ * @NUM_NL80211_PMSR_RESP_ATTRS: internal
+ * @NL80211_PMSR_RESP_ATTR_MAX: highest attribute number
+ */
+enum nl80211_peer_measurement_resp {
+ __NL80211_PMSR_RESP_ATTR_INVALID,
+
+ NL80211_PMSR_RESP_ATTR_DATA,
+ NL80211_PMSR_RESP_ATTR_STATUS,
+ NL80211_PMSR_RESP_ATTR_HOST_TIME,
+ NL80211_PMSR_RESP_ATTR_AP_TSF,
+ NL80211_PMSR_RESP_ATTR_FINAL,
+ NL80211_PMSR_RESP_ATTR_PAD,
+
+ /* keep last */
+ NUM_NL80211_PMSR_RESP_ATTRS,
+ NL80211_PMSR_RESP_ATTR_MAX = NUM_NL80211_PMSR_RESP_ATTRS - 1
+};
+
+/**
+ * enum nl80211_peer_measurement_peer_attrs - peer attributes for measurement
+ * @__NL80211_PMSR_PEER_ATTR_INVALID: invalid
+ *
+ * @NL80211_PMSR_PEER_ATTR_ADDR: peer's MAC address
+ * @NL80211_PMSR_PEER_ATTR_CHAN: channel definition, nested, using top-level
+ * attributes like %NL80211_ATTR_WIPHY_FREQ etc.
+ * @NL80211_PMSR_PEER_ATTR_REQ: This is a nested attribute indexed by
+ * measurement type, with attributes from the
+ * &enum nl80211_peer_measurement_req inside.
+ * @NL80211_PMSR_PEER_ATTR_RESP: This is a nested attribute indexed by
+ * measurement type, with attributes from the
+ * &enum nl80211_peer_measurement_resp inside.
+ *
+ * @NUM_NL80211_PMSR_PEER_ATTRS: internal
+ * @NL80211_PMSR_PEER_ATTR_MAX: highest attribute number
+ */
+enum nl80211_peer_measurement_peer_attrs {
+ __NL80211_PMSR_PEER_ATTR_INVALID,
+
+ NL80211_PMSR_PEER_ATTR_ADDR,
+ NL80211_PMSR_PEER_ATTR_CHAN,
+ NL80211_PMSR_PEER_ATTR_REQ,
+ NL80211_PMSR_PEER_ATTR_RESP,
+
+ /* keep last */
+ NUM_NL80211_PMSR_PEER_ATTRS,
+ NL80211_PMSR_PEER_ATTR_MAX = NUM_NL80211_PMSR_PEER_ATTRS - 1,
+};
+
+/**
+ * enum nl80211_peer_measurement_attrs - peer measurement attributes
+ * @__NL80211_PMSR_ATTR_INVALID: invalid
+ *
+ * @NL80211_PMSR_ATTR_MAX_PEERS: u32 attribute used for capability
+ * advertisement only, indicates the maximum number of peers
+ * measurements can be done with in a single request
+ * @NL80211_PMSR_ATTR_REPORT_AP_TSF: flag attribute in capability
+ * indicating that the connected AP's TSF can be reported in
+ * measurement results
+ * @NL80211_PMSR_ATTR_RANDOMIZE_MAC_ADDR: flag attribute in capability
+ * indicating that MAC address randomization is supported.
+ * @NL80211_PMSR_ATTR_TYPE_CAPA: capabilities reported by the device,
+ * this contains a nesting indexed by measurement type, and
+ * type-specific capabilities inside, which are from the enums
+ * named nl80211_peer_measurement_<type>_capa.
+ * @NL80211_PMSR_ATTR_PEERS: nested attribute, the nesting index is
+ * meaningless, just a list of peers to measure with, with the
+ * sub-attributes taken from
+ * &enum nl80211_peer_measurement_peer_attrs.
+ *
+ * @NUM_NL80211_PMSR_ATTR: internal
+ * @NL80211_PMSR_ATTR_MAX: highest attribute number
+ */
+enum nl80211_peer_measurement_attrs {
+ __NL80211_PMSR_ATTR_INVALID,
+
+ NL80211_PMSR_ATTR_MAX_PEERS,
+ NL80211_PMSR_ATTR_REPORT_AP_TSF,
+ NL80211_PMSR_ATTR_RANDOMIZE_MAC_ADDR,
+ NL80211_PMSR_ATTR_TYPE_CAPA,
+ NL80211_PMSR_ATTR_PEERS,
+
+ /* keep last */
+ NUM_NL80211_PMSR_ATTR,
+ NL80211_PMSR_ATTR_MAX = NUM_NL80211_PMSR_ATTR - 1
+};
+
+/**
+ * enum nl80211_peer_measurement_ftm_capa - FTM capabilities
+ * @__NL80211_PMSR_FTM_CAPA_ATTR_INVALID: invalid
+ *
+ * @NL80211_PMSR_FTM_CAPA_ATTR_ASAP: flag attribute indicating ASAP mode
+ * is supported
+ * @NL80211_PMSR_FTM_CAPA_ATTR_NON_ASAP: flag attribute indicating non-ASAP
+ * mode is supported
+ * @NL80211_PMSR_FTM_CAPA_ATTR_REQ_LCI: flag attribute indicating if LCI
+ * data can be requested during the measurement
+ * @NL80211_PMSR_FTM_CAPA_ATTR_REQ_CIVICLOC: flag attribute indicating if civic
+ * location data can be requested during the measurement
+ * @NL80211_PMSR_FTM_CAPA_ATTR_PREAMBLES: u32 bitmap attribute of bits
+ * from &enum nl80211_preamble.
+ * @NL80211_PMSR_FTM_CAPA_ATTR_BANDWIDTHS: bitmap of values from
+ * &enum nl80211_chan_width indicating the supported channel
+ * bandwidths for FTM. Note that a higher channel bandwidth may be
+ * configured to allow for other measurements types with different
+ * bandwidth requirement in the same measurement.
+ * @NL80211_PMSR_FTM_CAPA_ATTR_MAX_BURSTS_EXPONENT: u32 attribute indicating
+ * the maximum bursts exponent that can be used (if not present anything
+ * is valid)
+ * @NL80211_PMSR_FTM_CAPA_ATTR_MAX_FTMS_PER_BURST: u32 attribute indicating
+ * the maximum FTMs per burst (if not present anything is valid)
+ * @NL80211_PMSR_FTM_CAPA_ATTR_TRIGGER_BASED: flag attribute indicating if
+ * trigger based ranging measurement is supported
+ * @NL80211_PMSR_FTM_CAPA_ATTR_NON_TRIGGER_BASED: flag attribute indicating
+ * if non trigger based ranging measurement is supported
+ *
+ * @NUM_NL80211_PMSR_FTM_CAPA_ATTR: internal
+ * @NL80211_PMSR_FTM_CAPA_ATTR_MAX: highest attribute number
+ */
+enum nl80211_peer_measurement_ftm_capa {
+ __NL80211_PMSR_FTM_CAPA_ATTR_INVALID,
+
+ NL80211_PMSR_FTM_CAPA_ATTR_ASAP,
+ NL80211_PMSR_FTM_CAPA_ATTR_NON_ASAP,
+ NL80211_PMSR_FTM_CAPA_ATTR_REQ_LCI,
+ NL80211_PMSR_FTM_CAPA_ATTR_REQ_CIVICLOC,
+ NL80211_PMSR_FTM_CAPA_ATTR_PREAMBLES,
+ NL80211_PMSR_FTM_CAPA_ATTR_BANDWIDTHS,
+ NL80211_PMSR_FTM_CAPA_ATTR_MAX_BURSTS_EXPONENT,
+ NL80211_PMSR_FTM_CAPA_ATTR_MAX_FTMS_PER_BURST,
+ NL80211_PMSR_FTM_CAPA_ATTR_TRIGGER_BASED,
+ NL80211_PMSR_FTM_CAPA_ATTR_NON_TRIGGER_BASED,
+
+ /* keep last */
+ NUM_NL80211_PMSR_FTM_CAPA_ATTR,
+ NL80211_PMSR_FTM_CAPA_ATTR_MAX = NUM_NL80211_PMSR_FTM_CAPA_ATTR - 1
+};
+
+/**
+ * enum nl80211_peer_measurement_ftm_req - FTM request attributes
+ * @__NL80211_PMSR_FTM_REQ_ATTR_INVALID: invalid
+ *
+ * @NL80211_PMSR_FTM_REQ_ATTR_ASAP: ASAP mode requested (flag)
+ * @NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE: preamble type (see
+ * &enum nl80211_preamble), optional for DMG (u32)
+ * @NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP: number of bursts exponent as in
+ * 802.11-2016 9.4.2.168 "Fine Timing Measurement Parameters element"
+ * (u8, 0-15, optional with default 15 i.e. "no preference")
+ * @NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD: interval between bursts in units
+ * of 100ms (u16, optional with default 0)
+ * @NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION: burst duration, as in 802.11-2016
+ * Table 9-257 "Burst Duration field encoding" (u8, 0-15, optional with
+ * default 15 i.e. "no preference")
+ * @NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST: number of successful FTM frames
+ * requested per burst
+ * (u8, 0-31, optional with default 0 i.e. "no preference")
+ * @NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES: number of FTMR frame retries
+ * (u8, default 3)
+ * @NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI: request LCI data (flag)
+ * @NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC: request civic location data
+ * (flag)
+ * @NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED: request trigger based ranging
+ * measurement (flag).
+ * This attribute and %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED are
+ * mutually exclusive.
+ * if neither %NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED nor
+ * %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED is set, EDCA based
+ * ranging will be used.
+ * @NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED: request non trigger based
+ * ranging measurement (flag)
+ * This attribute and %NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED are
+ * mutually exclusive.
+ * if neither %NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED nor
+ * %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED is set, EDCA based
+ * ranging will be used.
+ * @NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK: negotiate for LMR feedback. Only
+ * valid if either %NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED or
+ * %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED is set.
+ * @NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR: optional. The BSS color of the
+ * responder. Only valid if %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED
+ * or %NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED is set.
+ *
+ * @NUM_NL80211_PMSR_FTM_REQ_ATTR: internal
+ * @NL80211_PMSR_FTM_REQ_ATTR_MAX: highest attribute number
+ */
+enum nl80211_peer_measurement_ftm_req {
+ __NL80211_PMSR_FTM_REQ_ATTR_INVALID,
+
+ NL80211_PMSR_FTM_REQ_ATTR_ASAP,
+ NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE,
+ NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP,
+ NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD,
+ NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION,
+ NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST,
+ NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES,
+ NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI,
+ NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC,
+ NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED,
+ NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED,
+ NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK,
+ NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR,
+
+ /* keep last */
+ NUM_NL80211_PMSR_FTM_REQ_ATTR,
+ NL80211_PMSR_FTM_REQ_ATTR_MAX = NUM_NL80211_PMSR_FTM_REQ_ATTR - 1
+};
+
+/**
+ * enum nl80211_peer_measurement_ftm_failure_reasons - FTM failure reasons
+ * @NL80211_PMSR_FTM_FAILURE_UNSPECIFIED: unspecified failure, not used
+ * @NL80211_PMSR_FTM_FAILURE_NO_RESPONSE: no response from the FTM responder
+ * @NL80211_PMSR_FTM_FAILURE_REJECTED: FTM responder rejected measurement
+ * @NL80211_PMSR_FTM_FAILURE_WRONG_CHANNEL: we already know the peer is
+ * on a different channel, so can't measure (if we didn't know, we'd
+ * try and get no response)
+ * @NL80211_PMSR_FTM_FAILURE_PEER_NOT_CAPABLE: peer can't actually do FTM
+ * @NL80211_PMSR_FTM_FAILURE_INVALID_TIMESTAMP: invalid T1/T4 timestamps
+ * received
+ * @NL80211_PMSR_FTM_FAILURE_PEER_BUSY: peer reports busy, you may retry
+ * later (see %NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME)
+ * @NL80211_PMSR_FTM_FAILURE_BAD_CHANGED_PARAMS: parameters were changed
+ * by the peer and are no longer supported
+ */
+enum nl80211_peer_measurement_ftm_failure_reasons {
+ NL80211_PMSR_FTM_FAILURE_UNSPECIFIED,
+ NL80211_PMSR_FTM_FAILURE_NO_RESPONSE,
+ NL80211_PMSR_FTM_FAILURE_REJECTED,
+ NL80211_PMSR_FTM_FAILURE_WRONG_CHANNEL,
+ NL80211_PMSR_FTM_FAILURE_PEER_NOT_CAPABLE,
+ NL80211_PMSR_FTM_FAILURE_INVALID_TIMESTAMP,
+ NL80211_PMSR_FTM_FAILURE_PEER_BUSY,
+ NL80211_PMSR_FTM_FAILURE_BAD_CHANGED_PARAMS,
+};
+
+/**
+ * enum nl80211_peer_measurement_ftm_resp - FTM response attributes
+ * @__NL80211_PMSR_FTM_RESP_ATTR_INVALID: invalid
+ *
+ * @NL80211_PMSR_FTM_RESP_ATTR_FAIL_REASON: FTM-specific failure reason
+ * (u32, optional)
+ * @NL80211_PMSR_FTM_RESP_ATTR_BURST_INDEX: optional, if bursts are reported
+ * as separate results then it will be the burst index 0...(N-1) and
+ * the top level will indicate partial results (u32)
+ * @NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_ATTEMPTS: number of FTM Request frames
+ * transmitted (u32, optional)
+ * @NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_SUCCESSES: number of FTM Request frames
+ * that were acknowleged (u32, optional)
+ * @NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME: retry time received from the
+ * busy peer (u32, seconds)
+ * @NL80211_PMSR_FTM_RESP_ATTR_NUM_BURSTS_EXP: actual number of bursts exponent
+ * used by the responder (similar to request, u8)
+ * @NL80211_PMSR_FTM_RESP_ATTR_BURST_DURATION: actual burst duration used by
+ * the responder (similar to request, u8)
+ * @NL80211_PMSR_FTM_RESP_ATTR_FTMS_PER_BURST: actual FTMs per burst used
+ * by the responder (similar to request, u8)
+ * @NL80211_PMSR_FTM_RESP_ATTR_RSSI_AVG: average RSSI across all FTM action
+ * frames (optional, s32, 1/2 dBm)
+ * @NL80211_PMSR_FTM_RESP_ATTR_RSSI_SPREAD: RSSI spread across all FTM action
+ * frames (optional, s32, 1/2 dBm)
+ * @NL80211_PMSR_FTM_RESP_ATTR_TX_RATE: bitrate we used for the response to the
+ * FTM action frame (optional, nested, using &enum nl80211_rate_info
+ * attributes)
+ * @NL80211_PMSR_FTM_RESP_ATTR_RX_RATE: bitrate the responder used for the FTM
+ * action frame (optional, nested, using &enum nl80211_rate_info attrs)
+ * @NL80211_PMSR_FTM_RESP_ATTR_RTT_AVG: average RTT (s64, picoseconds, optional
+ * but one of RTT/DIST must be present)
+ * @NL80211_PMSR_FTM_RESP_ATTR_RTT_VARIANCE: RTT variance (u64, ps^2, note that
+ * standard deviation is the square root of variance, optional)
+ * @NL80211_PMSR_FTM_RESP_ATTR_RTT_SPREAD: RTT spread (u64, picoseconds,
+ * optional)
+ * @NL80211_PMSR_FTM_RESP_ATTR_DIST_AVG: average distance (s64, mm, optional
+ * but one of RTT/DIST must be present)
+ * @NL80211_PMSR_FTM_RESP_ATTR_DIST_VARIANCE: distance variance (u64, mm^2, note
+ * that standard deviation is the square root of variance, optional)
+ * @NL80211_PMSR_FTM_RESP_ATTR_DIST_SPREAD: distance spread (u64, mm, optional)
+ * @NL80211_PMSR_FTM_RESP_ATTR_LCI: LCI data from peer (binary, optional);
+ * this is the contents of the Measurement Report Element (802.11-2016
+ * 9.4.2.22.1) starting with the Measurement Token, with Measurement
+ * Type 8.
+ * @NL80211_PMSR_FTM_RESP_ATTR_CIVICLOC: civic location data from peer
+ * (binary, optional);
+ * this is the contents of the Measurement Report Element (802.11-2016
+ * 9.4.2.22.1) starting with the Measurement Token, with Measurement
+ * Type 11.
+ * @NL80211_PMSR_FTM_RESP_ATTR_PAD: ignore, for u64/s64 padding only
+ *
+ * @NUM_NL80211_PMSR_FTM_RESP_ATTR: internal
+ * @NL80211_PMSR_FTM_RESP_ATTR_MAX: highest attribute number
+ */
+enum nl80211_peer_measurement_ftm_resp {
+ __NL80211_PMSR_FTM_RESP_ATTR_INVALID,
+
+ NL80211_PMSR_FTM_RESP_ATTR_FAIL_REASON,
+ NL80211_PMSR_FTM_RESP_ATTR_BURST_INDEX,
+ NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_ATTEMPTS,
+ NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_SUCCESSES,
+ NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME,
+ NL80211_PMSR_FTM_RESP_ATTR_NUM_BURSTS_EXP,
+ NL80211_PMSR_FTM_RESP_ATTR_BURST_DURATION,
+ NL80211_PMSR_FTM_RESP_ATTR_FTMS_PER_BURST,
+ NL80211_PMSR_FTM_RESP_ATTR_RSSI_AVG,
+ NL80211_PMSR_FTM_RESP_ATTR_RSSI_SPREAD,
+ NL80211_PMSR_FTM_RESP_ATTR_TX_RATE,
+ NL80211_PMSR_FTM_RESP_ATTR_RX_RATE,
+ NL80211_PMSR_FTM_RESP_ATTR_RTT_AVG,
+ NL80211_PMSR_FTM_RESP_ATTR_RTT_VARIANCE,
+ NL80211_PMSR_FTM_RESP_ATTR_RTT_SPREAD,
+ NL80211_PMSR_FTM_RESP_ATTR_DIST_AVG,
+ NL80211_PMSR_FTM_RESP_ATTR_DIST_VARIANCE,
+ NL80211_PMSR_FTM_RESP_ATTR_DIST_SPREAD,
+ NL80211_PMSR_FTM_RESP_ATTR_LCI,
+ NL80211_PMSR_FTM_RESP_ATTR_CIVICLOC,
+ NL80211_PMSR_FTM_RESP_ATTR_PAD,
+
+ /* keep last */
+ NUM_NL80211_PMSR_FTM_RESP_ATTR,
+ NL80211_PMSR_FTM_RESP_ATTR_MAX = NUM_NL80211_PMSR_FTM_RESP_ATTR - 1
+};
+
+/**
+ * enum nl80211_obss_pd_attributes - OBSS packet detection attributes
+ * @__NL80211_HE_OBSS_PD_ATTR_INVALID: Invalid
+ *
+ * @NL80211_HE_OBSS_PD_ATTR_MIN_OFFSET: the OBSS PD minimum tx power offset.
+ * @NL80211_HE_OBSS_PD_ATTR_MAX_OFFSET: the OBSS PD maximum tx power offset.
+ * @NL80211_HE_OBSS_PD_ATTR_NON_SRG_MAX_OFFSET: the non-SRG OBSS PD maximum
+ * tx power offset.
+ * @NL80211_HE_OBSS_PD_ATTR_BSS_COLOR_BITMAP: bitmap that indicates the BSS color
+ * values used by members of the SRG.
+ * @NL80211_HE_OBSS_PD_ATTR_PARTIAL_BSSID_BITMAP: bitmap that indicates the partial
+ * BSSID values used by members of the SRG.
+ * @NL80211_HE_OBSS_PD_ATTR_SR_CTRL: The SR Control field of SRP element.
+ *
+ * @__NL80211_HE_OBSS_PD_ATTR_LAST: Internal
+ * @NL80211_HE_OBSS_PD_ATTR_MAX: highest OBSS PD attribute.
+ */
+enum nl80211_obss_pd_attributes {
+ __NL80211_HE_OBSS_PD_ATTR_INVALID,
+
+ NL80211_HE_OBSS_PD_ATTR_MIN_OFFSET,
+ NL80211_HE_OBSS_PD_ATTR_MAX_OFFSET,
+ NL80211_HE_OBSS_PD_ATTR_NON_SRG_MAX_OFFSET,
+ NL80211_HE_OBSS_PD_ATTR_BSS_COLOR_BITMAP,
+ NL80211_HE_OBSS_PD_ATTR_PARTIAL_BSSID_BITMAP,
+ NL80211_HE_OBSS_PD_ATTR_SR_CTRL,
+
+ /* keep last */
+ __NL80211_HE_OBSS_PD_ATTR_LAST,
+ NL80211_HE_OBSS_PD_ATTR_MAX = __NL80211_HE_OBSS_PD_ATTR_LAST - 1,
+};
+
+/**
+ * enum nl80211_bss_color_attributes - BSS Color attributes
+ * @__NL80211_HE_BSS_COLOR_ATTR_INVALID: Invalid
+ *
+ * @NL80211_HE_BSS_COLOR_ATTR_COLOR: the current BSS Color.
+ * @NL80211_HE_BSS_COLOR_ATTR_DISABLED: is BSS coloring disabled.
+ * @NL80211_HE_BSS_COLOR_ATTR_PARTIAL: the AID equation to be used..
+ *
+ * @__NL80211_HE_BSS_COLOR_ATTR_LAST: Internal
+ * @NL80211_HE_BSS_COLOR_ATTR_MAX: highest BSS Color attribute.
+ */
+enum nl80211_bss_color_attributes {
+ __NL80211_HE_BSS_COLOR_ATTR_INVALID,
+
+ NL80211_HE_BSS_COLOR_ATTR_COLOR,
+ NL80211_HE_BSS_COLOR_ATTR_DISABLED,
+ NL80211_HE_BSS_COLOR_ATTR_PARTIAL,
+
+ /* keep last */
+ __NL80211_HE_BSS_COLOR_ATTR_LAST,
+ NL80211_HE_BSS_COLOR_ATTR_MAX = __NL80211_HE_BSS_COLOR_ATTR_LAST - 1,
+};
+
+/**
+ * enum nl80211_iftype_akm_attributes - interface type AKM attributes
+ * @__NL80211_IFTYPE_AKM_ATTR_INVALID: Invalid
+ *
+ * @NL80211_IFTYPE_AKM_ATTR_IFTYPES: nested attribute containing a flag
+ * attribute for each interface type that supports AKM suites specified in
+ * %NL80211_IFTYPE_AKM_ATTR_SUITES
+ * @NL80211_IFTYPE_AKM_ATTR_SUITES: an array of u32. Used to indicate supported
+ * AKM suites for the specified interface types.
+ *
+ * @__NL80211_IFTYPE_AKM_ATTR_LAST: Internal
+ * @NL80211_IFTYPE_AKM_ATTR_MAX: highest interface type AKM attribute.
+ */
+enum nl80211_iftype_akm_attributes {
+ __NL80211_IFTYPE_AKM_ATTR_INVALID,
+
+ NL80211_IFTYPE_AKM_ATTR_IFTYPES,
+ NL80211_IFTYPE_AKM_ATTR_SUITES,
+
+ /* keep last */
+ __NL80211_IFTYPE_AKM_ATTR_LAST,
+ NL80211_IFTYPE_AKM_ATTR_MAX = __NL80211_IFTYPE_AKM_ATTR_LAST - 1,
+};
+
+/**
+ * enum nl80211_fils_discovery_attributes - FILS discovery configuration
+ * from IEEE Std 802.11ai-2016, Annex C.3 MIB detail.
+ *
+ * @__NL80211_FILS_DISCOVERY_ATTR_INVALID: Invalid
+ *
+ * @NL80211_FILS_DISCOVERY_ATTR_INT_MIN: Minimum packet interval (u32, TU).
+ * Allowed range: 0..10000 (TU = Time Unit)
+ * @NL80211_FILS_DISCOVERY_ATTR_INT_MAX: Maximum packet interval (u32, TU).
+ * Allowed range: 0..10000 (TU = Time Unit)
+ * @NL80211_FILS_DISCOVERY_ATTR_TMPL: Template data for FILS discovery action
+ * frame including the headers.
+ *
+ * @__NL80211_FILS_DISCOVERY_ATTR_LAST: Internal
+ * @NL80211_FILS_DISCOVERY_ATTR_MAX: highest attribute
+ */
+enum nl80211_fils_discovery_attributes {
+ __NL80211_FILS_DISCOVERY_ATTR_INVALID,
+
+ NL80211_FILS_DISCOVERY_ATTR_INT_MIN,
+ NL80211_FILS_DISCOVERY_ATTR_INT_MAX,
+ NL80211_FILS_DISCOVERY_ATTR_TMPL,
+
+ /* keep last */
+ __NL80211_FILS_DISCOVERY_ATTR_LAST,
+ NL80211_FILS_DISCOVERY_ATTR_MAX = __NL80211_FILS_DISCOVERY_ATTR_LAST - 1
+};
+
+/*
+ * FILS discovery template minimum length with action frame headers and
+ * mandatory fields.
+ */
+#define NL80211_FILS_DISCOVERY_TMPL_MIN_LEN 42
+
+/**
+ * enum nl80211_unsol_bcast_probe_resp_attributes - Unsolicited broadcast probe
+ * response configuration. Applicable only in 6GHz.
+ *
+ * @__NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INVALID: Invalid
+ *
+ * @NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INT: Maximum packet interval (u32, TU).
+ * Allowed range: 0..20 (TU = Time Unit). IEEE P802.11ax/D6.0
+ * 26.17.2.3.2 (AP behavior for fast passive scanning).
+ * @NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_TMPL: Unsolicited broadcast probe response
+ * frame template (binary).
+ *
+ * @__NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_LAST: Internal
+ * @NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_MAX: highest attribute
+ */
+enum nl80211_unsol_bcast_probe_resp_attributes {
+ __NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INVALID,
+
+ NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INT,
+ NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_TMPL,
+
+ /* keep last */
+ __NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_LAST,
+ NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_MAX =
+ __NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_LAST - 1
+};
+
+/**
+ * enum nl80211_sae_pwe_mechanism - The mechanism(s) allowed for SAE PWE
+ * derivation. Applicable only when WPA3-Personal SAE authentication is
+ * used.
+ *
+ * @NL80211_SAE_PWE_UNSPECIFIED: not specified, used internally to indicate that
+ * attribute is not present from userspace.
+ * @NL80211_SAE_PWE_HUNT_AND_PECK: hunting-and-pecking loop only
+ * @NL80211_SAE_PWE_HASH_TO_ELEMENT: hash-to-element only
+ * @NL80211_SAE_PWE_BOTH: both hunting-and-pecking loop and hash-to-element
+ * can be used.
+ */
+enum nl80211_sae_pwe_mechanism {
+ NL80211_SAE_PWE_UNSPECIFIED,
+ NL80211_SAE_PWE_HUNT_AND_PECK,
+ NL80211_SAE_PWE_HASH_TO_ELEMENT,
+ NL80211_SAE_PWE_BOTH,
+};
+
+/**
+ * enum nl80211_sar_type - type of SAR specs
+ *
+ * @NL80211_SAR_TYPE_POWER: power limitation specified in 0.25dBm unit
+ *
+ */
+enum nl80211_sar_type {
+ NL80211_SAR_TYPE_POWER,
+
+ /* add new type here */
+
+ /* Keep last */
+ NUM_NL80211_SAR_TYPE,
+};
+
+/**
+ * enum nl80211_sar_attrs - Attributes for SAR spec
+ *
+ * @NL80211_SAR_ATTR_TYPE: the SAR type as defined in &enum nl80211_sar_type.
+ *
+ * @NL80211_SAR_ATTR_SPECS: Nested array of SAR power
+ * limit specifications. Each specification contains a set
+ * of %nl80211_sar_specs_attrs.
+ *
+ * For SET operation, it contains array of %NL80211_SAR_ATTR_SPECS_POWER
+ * and %NL80211_SAR_ATTR_SPECS_RANGE_INDEX.
+ *
+ * For sar_capa dump, it contains array of
+ * %NL80211_SAR_ATTR_SPECS_START_FREQ
+ * and %NL80211_SAR_ATTR_SPECS_END_FREQ.
+ *
+ * @__NL80211_SAR_ATTR_LAST: Internal
+ * @NL80211_SAR_ATTR_MAX: highest sar attribute
+ *
+ * These attributes are used with %NL80211_CMD_SET_SAR_SPEC
+ */
+enum nl80211_sar_attrs {
+ __NL80211_SAR_ATTR_INVALID,
+
+ NL80211_SAR_ATTR_TYPE,
+ NL80211_SAR_ATTR_SPECS,
+
+ __NL80211_SAR_ATTR_LAST,
+ NL80211_SAR_ATTR_MAX = __NL80211_SAR_ATTR_LAST - 1,
+};
+
+/**
+ * enum nl80211_sar_specs_attrs - Attributes for SAR power limit specs
+ *
+ * @NL80211_SAR_ATTR_SPECS_POWER: Required (s32)value to specify the actual
+ * power limit value in units of 0.25 dBm if type is
+ * NL80211_SAR_TYPE_POWER. (i.e., a value of 44 represents 11 dBm).
+ * 0 means userspace doesn't have SAR limitation on this associated range.
+ *
+ * @NL80211_SAR_ATTR_SPECS_RANGE_INDEX: Required (u32) value to specify the
+ * index of exported freq range table and the associated power limitation
+ * is applied to this range.
+ *
+ * Userspace isn't required to set all the ranges advertised by WLAN driver,
+ * and userspace can skip some certain ranges. These skipped ranges don't
+ * have SAR limitations, and they are same as setting the
+ * %NL80211_SAR_ATTR_SPECS_POWER to any unreasonable high value because any
+ * value higher than regulatory allowed value just means SAR power
+ * limitation is removed, but it's required to set at least one range.
+ * It's not allowed to set duplicated range in one SET operation.
+ *
+ * Every SET operation overwrites previous SET operation.
+ *
+ * @NL80211_SAR_ATTR_SPECS_START_FREQ: Required (u32) value to specify the start
+ * frequency of this range edge when registering SAR capability to wiphy.
+ * It's not a channel center frequency. The unit is kHz.
+ *
+ * @NL80211_SAR_ATTR_SPECS_END_FREQ: Required (u32) value to specify the end
+ * frequency of this range edge when registering SAR capability to wiphy.
+ * It's not a channel center frequency. The unit is kHz.
+ *
+ * @__NL80211_SAR_ATTR_SPECS_LAST: Internal
+ * @NL80211_SAR_ATTR_SPECS_MAX: highest sar specs attribute
+ */
+enum nl80211_sar_specs_attrs {
+ __NL80211_SAR_ATTR_SPECS_INVALID,
+
+ NL80211_SAR_ATTR_SPECS_POWER,
+ NL80211_SAR_ATTR_SPECS_RANGE_INDEX,
+ NL80211_SAR_ATTR_SPECS_START_FREQ,
+ NL80211_SAR_ATTR_SPECS_END_FREQ,
+
+ __NL80211_SAR_ATTR_SPECS_LAST,
+ NL80211_SAR_ATTR_SPECS_MAX = __NL80211_SAR_ATTR_SPECS_LAST - 1,
+};
+
+/**
+ * enum nl80211_mbssid_config_attributes - multiple BSSID (MBSSID) and enhanced
+ * multi-BSSID advertisements (EMA) in AP mode.
+ * Kernel uses some of these attributes to advertise driver's support for
+ * MBSSID and EMA.
+ * Remaining attributes should be used by the userspace to configure the
+ * features.
+ *
+ * @__NL80211_MBSSID_CONFIG_ATTR_INVALID: Invalid
+ *
+ * @NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES: Used by the kernel to advertise
+ * the maximum number of MBSSID interfaces supported by the driver.
+ * Driver should indicate MBSSID support by setting
+ * wiphy->mbssid_max_interfaces to a value more than or equal to 2.
+ *
+ * @NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY: Used by the kernel
+ * to advertise the maximum profile periodicity supported by the driver
+ * if EMA is enabled. Driver should indicate EMA support to the userspace
+ * by setting wiphy->ema_max_profile_periodicity to
+ * a non-zero value.
+ *
+ * @NL80211_MBSSID_CONFIG_ATTR_INDEX: Mandatory parameter to pass the index of
+ * this BSS (u8) in the multiple BSSID set.
+ * Value must be set to 0 for the transmitting interface and non-zero for
+ * all non-transmitting interfaces. The userspace will be responsible
+ * for using unique indices for the interfaces.
+ * Range: 0 to wiphy->mbssid_max_interfaces-1.
+ *
+ * @NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX: Mandatory parameter for
+ * a non-transmitted profile which provides the interface index (u32) of
+ * the transmitted profile. The value must match one of the interface
+ * indices advertised by the kernel. Optional if the interface being set up
+ * is the transmitting one, however, if provided then the value must match
+ * the interface index of the same.
+ *
+ * @NL80211_MBSSID_CONFIG_ATTR_EMA: Flag used to enable EMA AP feature.
+ * Setting this flag is permitted only if the driver advertises EMA support
+ * by setting wiphy->ema_max_profile_periodicity to non-zero.
+ *
+ * @__NL80211_MBSSID_CONFIG_ATTR_LAST: Internal
+ * @NL80211_MBSSID_CONFIG_ATTR_MAX: highest attribute
+ */
+enum nl80211_mbssid_config_attributes {
+ __NL80211_MBSSID_CONFIG_ATTR_INVALID,
+
+ NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES,
+ NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY,
+ NL80211_MBSSID_CONFIG_ATTR_INDEX,
+ NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX,
+ NL80211_MBSSID_CONFIG_ATTR_EMA,
+
+ /* keep last */
+ __NL80211_MBSSID_CONFIG_ATTR_LAST,
+ NL80211_MBSSID_CONFIG_ATTR_MAX = __NL80211_MBSSID_CONFIG_ATTR_LAST - 1,
+};
+
+/**
+ * enum nl80211_ap_settings_flags - AP settings flags
+ *
+ * @NL80211_AP_SETTINGS_EXTERNAL_AUTH_SUPPORT: AP supports external
+ * authentication.
+ * @NL80211_AP_SETTINGS_SA_QUERY_OFFLOAD_SUPPORT: Userspace supports SA Query
+ * procedures offload to driver. If driver advertises
+ * %NL80211_AP_SME_SA_QUERY_OFFLOAD in AP SME features, userspace shall
+ * ignore SA Query procedures and validations when this flag is set by
+ * userspace.
+ */
+enum nl80211_ap_settings_flags {
+ NL80211_AP_SETTINGS_EXTERNAL_AUTH_SUPPORT = 1 << 0,
+ NL80211_AP_SETTINGS_SA_QUERY_OFFLOAD_SUPPORT = 1 << 1,
+};
+
+#endif /* __LINUX_NL80211_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/priv_netlink.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/priv_netlink.h
new file mode 100644
index 0000000..d3f091c
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/priv_netlink.h
@@ -0,0 +1,109 @@
+/*
+ * wpa_supplicant - Private copy of Linux netlink/rtnetlink definitions.
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README 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_SPACE(len) RTA_ALIGN(RTA_LENGTH(len))
+#define RTA_DATA(rta) ((void *) (((char *) (rta)) + RTA_LENGTH(0)))
+#define RTA_PAYLOAD(rta) ((int) ((rta)->rta_len) - 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/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/rfkill.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/rfkill.c
new file mode 100644
index 0000000..4d4d1b4
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/rfkill.c
@@ -0,0 +1,224 @@
+/*
+ * Linux rfkill helper functions for driver wrappers
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <fcntl.h>
+#include <limits.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;
+ uint32_t idx;
+};
+
+
+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;
+ }
+ if (event.op != RFKILL_OP_CHANGE || event.idx != rfkill->idx)
+ 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.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;
+ char *phy = NULL, *rfk_phy;
+ char buf[24 + IFNAMSIZ + 1];
+ char buf2[31 + 11 + 1];
+ int found = 0;
+
+ rfkill = os_zalloc(sizeof(*rfkill));
+ if (rfkill == NULL)
+ return NULL;
+
+ os_snprintf(buf, sizeof(buf), "/sys/class/net/%s/phy80211",
+ cfg->ifname);
+ phy = realpath(buf, NULL);
+ if (!phy) {
+ wpa_printf(MSG_INFO, "rfkill: Cannot get wiphy information");
+ goto fail;
+ }
+
+ 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;
+ }
+ if (event.op != RFKILL_OP_ADD ||
+ event.type != RFKILL_TYPE_WLAN)
+ continue;
+
+ os_snprintf(buf2, sizeof(buf2),
+ "/sys/class/rfkill/rfkill%d/device", event.idx);
+ rfk_phy = realpath(buf2, NULL);
+ if (!rfk_phy)
+ goto fail2;
+ found = os_strcmp(phy, rfk_phy) == 0;
+ free(rfk_phy);
+
+ if (!found)
+ 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);
+
+ rfkill->idx = event.idx;
+ 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;
+ }
+ break;
+ }
+
+ if (!found)
+ goto fail2;
+
+ free(phy);
+ eloop_register_read_sock(rfkill->fd, rfkill_receive, rfkill, NULL);
+
+ return rfkill;
+
+fail2:
+ close(rfkill->fd);
+fail:
+ os_free(rfkill);
+ /* use standard free function to match realpath() */
+ free(phy);
+ 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/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/rfkill.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/rfkill.h
new file mode 100644
index 0000000..e275653
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/rfkill.h
@@ -0,0 +1,41 @@
+/*
+ * Linux rfkill helper functions for driver wrappers
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README 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);
+};
+
+#ifdef CONFIG_RFKILL
+struct rfkill_data * rfkill_init(struct rfkill_config *cfg);
+void rfkill_deinit(struct rfkill_data *rfkill);
+int rfkill_is_blocked(struct rfkill_data *rfkill);
+#else
+static inline struct rfkill_data * rfkill_init(struct rfkill_config *cfg)
+{
+ return (void *) 1;
+}
+
+static inline void rfkill_deinit(struct rfkill_data *rfkill)
+{
+}
+
+static inline int rfkill_is_blocked(struct rfkill_data *rfkill)
+{
+ return 0;
+}
+#endif
+
+#endif /* RFKILL_H */