Cumulative patch from commit f10ff62e4eda7917a8e28fe492fd98723a5e97c4
f10ff62 Describe preferred mechanism for submitting contributions
fcdb359 Use "STATUS-NO_EVENTS" instead of "STATUS" in get_wpa_status function
73ed03f wpa_supplicant: Add GTK RSC relaxation workaround
ea6030c Restore previous wpa_state in scan-only result handler
1e74ae4 WNM: Clear BSS TM data if already associated with preferred candidate
d129b02 EAP-pwd: Add support for Brainpool Elliptic Curves
a34eace dbus: Remove unused dict helper functions
cdcb2d0 wpa_cli: Add support for vendor_elem_* commands
17b7032 EAP peer: Clear ignore flag in INITIALIZE state
1f1e619 Add test programs for checking libwpa_client linking
736b7cb wpa_supplicant/Makefile: Fix libwpa_client build
2e38079 TLS: Fix memory leak with multiple TLS server instances
7b0f550 eap_sim_db: Implement eap_sim_db_expire_pending()
45c3e72 Add frequency to operating class determination for 5 GHz 100..140
e50c50d dbus: Expose interface globals via D-Bus properties
1aa0fb7 dbus: Pass property description to getters/setters
c93b7e1 RSN: Check result of EAPOL-Key frame send request
95be79f Allow -1 as value to disable frag_threshold
bc50bb0 Extend the range of values for the RTS threshold
053693d hostapd: Add feature to start all interfaces at the same time in sync
9578413 Reserve QCA vendor specific nl80211 commands 110..114
5d4c508 Assign QCA commands and attributes for Tx power scaling and OTA testing
5d1d69a P2P: Filter control chars in group client device name similarly to peer
f67d1a0 TDLS: Do not send error case of TPK M3 if TX fails
1248e58 wpa_supplicant: Reopen debug log file upon receipt of SIGHUP signal
d8fd633 Do not write ERROR level log entries if debug file is not used
67deaa5 l2_packet: Add build option to disable Linux packet socket workaround
fa46426 RSN: Do not try to connect if PMF disabled and AP requires it
8acbe7f WNM: Verify WNM Sleep Mode element length
dacd789 WNM: Mark set TFS buffer const
...
f24b979 OpenSSL: Merge error returns
84d6a17 TLS: Remove unused tls_capabilities()
7867227 ms_funcs: Merge similar return cases
3596361 hw_features: Merge similar return case in check_40mhz_2g4()
aac1efe Reject the initial 4-way handshake if initial GTK setup fails
2da5256 Add backtrace-based error path testing mechanism
55413ce P2P: Do not allow 40 MHz co-ex PRI/SEC switch to force MCC
Next patches were skipped due to explicit cherry-pick:
bddc51e RSN: Stop connection attempt on apparent PMK mismatch
3fdaaa8 Throttle control interface event message bursts
a530fe7 Add wpa_supplicant EVENT_TEST control interface command
ee1e3f5 hostapd: Global control interface notifications
2e95cfc Add debug prints for wpa_supplicant ctrl_iface socket send operations
ce7d0eb Update AP WPA/RSN IE on all associations if driver can select BSS
844dfeb QCA vendor command support to set band to driver
Change-Id: I909996c5afcd3b5d123ea1e23c0e1212021f7625
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 7fe736b..09c4fa1 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -1214,6 +1214,8 @@
#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
u64 flags;
#define WPA_DRIVER_SMPS_MODE_STATIC 0x00000001
@@ -1297,6 +1299,16 @@
*/
#define WPA_DRIVER_FLAGS_TX_POWER_INSERTION 0x00000008
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;
};
@@ -1397,6 +1409,16 @@
* 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,
};
struct wpa_init_params {
@@ -1537,8 +1559,8 @@
struct beacon_data beacon_csa;
struct beacon_data beacon_after;
- u16 counter_offset_beacon;
- u16 counter_offset_presp;
+ u16 counter_offset_beacon[2];
+ u16 counter_offset_presp[2];
};
/* TDLS peer capabilities for send_tdls_mgmt() */
@@ -1602,6 +1624,7 @@
/* ACS channel list info */
unsigned int ch_list_len;
const u8 *ch_list;
+ const int *freq_list;
};
@@ -1942,10 +1965,13 @@
* @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
* Returns: 0 on success, -1 on failure
*/
int (*send_mlme)(void *priv, const u8 *data, size_t data_len,
- int noack, unsigned int freq);
+ int noack, unsigned int freq, const u16 *csa_offs,
+ size_t csa_offs_len);
/**
* update_ft_ies - Update FT (IEEE 802.11r) IEs
@@ -2349,7 +2375,8 @@
* Returns: 0 on success, -1 on failure
*/
int (*sta_set_flags)(void *priv, const u8 *addr,
- int total_flags, int flags_or, int flags_and);
+ unsigned int total_flags, unsigned int flags_or,
+ unsigned int flags_and);
/**
* set_tx_queue_params - Set TX queue parameters
@@ -3394,6 +3421,32 @@
* Returns 0 on success, -1 on failure
*/
int (*set_band)(void *priv, enum set_band band);
+
+ /**
+ * 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: Preferred channel frequency list encoded in MHz values
+ * 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.
+ */
+ int (*get_pref_freq_list)(void *priv, enum wpa_driver_if_type if_type,
+ unsigned int *num, unsigned int *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);
};
@@ -4336,6 +4389,9 @@
* @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
*/
struct scan_info {
int aborted;
@@ -4343,6 +4399,8 @@
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;
} scan_info;
/**
diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c
index b721c32..1afeeb2 100644
--- a/src/drivers/driver_atheros.c
+++ b/src/drivers/driver_atheros.c
@@ -55,6 +55,10 @@
#include "netlink.h"
#include "linux_ioctl.h"
+#if defined(CONFIG_IEEE80211W) || defined(CONFIG_IEEE80211R) || defined(CONFIG_HS20) || defined(CONFIG_WNM) || defined(CONFIG_WPS)
+#define ATHEROS_USE_RAW_RECEIVE
+#endif
+
struct atheros_driver_data {
struct hostapd_data *hapd; /* back pointer */
@@ -430,7 +434,8 @@
static int
atheros_sta_set_flags(void *priv, const u8 *addr,
- int total_flags, int flags_or, int flags_and)
+ 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)
@@ -823,7 +828,7 @@
return 0;
}
-#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_WNM) || defined(CONFIG_HS20)
+#ifdef ATHEROS_USE_RAW_RECEIVE
static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf,
size_t len)
{
@@ -911,7 +916,7 @@
break;
}
}
-#endif
+#endif /* ATHEROS_USE_RAW_RECEIVE */
static int atheros_receive_pkt(struct atheros_driver_data *drv)
{
@@ -923,11 +928,11 @@
#ifdef CONFIG_WPS
filt.app_filterype |= IEEE80211_FILTER_TYPE_PROBE_REQ;
#endif /* CONFIG_WPS */
-#ifdef CONFIG_IEEE80211R
+#if defined(CONFIG_IEEE80211W) || defined(CONFIG_IEEE80211R)
filt.app_filterype |= (IEEE80211_FILTER_TYPE_ASSOC_REQ |
IEEE80211_FILTER_TYPE_AUTH |
IEEE80211_FILTER_TYPE_ACTION);
-#endif
+#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
#ifdef CONFIG_WNM
filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION;
#endif /* CONFIG_WNM */
@@ -1026,7 +1031,7 @@
#define atheros_set_ap_wps_ie NULL
#endif /* CONFIG_WPS */
-#ifdef CONFIG_IEEE80211R
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
static int
atheros_sta_auth(void *priv, const u8 *own_addr, const u8 *addr, u16 seq,
u16 status_code, const u8 *ie, size_t len)
@@ -1102,7 +1107,7 @@
}
return ret;
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
static void
atheros_new_sta(struct atheros_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN])
@@ -1177,6 +1182,7 @@
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 (strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) {
@@ -1232,9 +1238,6 @@
* so all are enabled for WPS... ugh.
*/
wpa_supplicant_event(drv->hapd, EVENT_WPS_BUTTON_PUSHED, NULL);
-#endif /* CONFIG_WPS */
-#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_HS20)
-#define MGMT_FRAM_TAG_SIZE 30 /* hardcoded in driver */
} else if (strncmp(custom, "Manage.prob_req ", 16) == 0) {
/*
* Atheros driver uses a hack to pass Probe Request frames as a
@@ -1243,47 +1246,53 @@
* Format: "Manage.prob_req <frame len>" | zero padding | frame
*/
int len = atoi(custom + 16);
- if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) {
+ 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 */
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
} else if (strncmp(custom, "Manage.assoc_req ", 17) == 0) {
/* Format: "Manage.assoc_req <frame len>" | zero padding |
* frame */
int len = atoi(custom + 17);
- if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) {
- wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req/"
- "assoc_req/auth event length %d", len);
+ 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 (strncmp(custom, "Manage.action ", 14) == 0) {
- /* Format: "Manage.assoc_req <frame len>" | zero padding |
- * frame */
- int len = atoi(custom + 14);
- if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) {
- wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req/"
- "assoc_req/auth event length %d", len);
- return;
- }
- atheros_raw_receive(drv, NULL,
- (u8 *) custom + MGMT_FRAM_TAG_SIZE, len);
- } else if (strncmp(custom, "Manage.auth ", 12) == 0) {
- /* Format: "Manage.auth <frame len>" | zero padding | frame
- */
+ } else if (strncmp(custom, "Manage.auth ", 12) == 0) {
+ /* Format: "Manage.auth <frame len>" | zero padding | frame */
int len = atoi(custom + 12);
- if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) {
- wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req/"
- "assoc_req/auth event length %d", len);
+ 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);
-#endif /* CONFIG_WPS or CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211W || CONFIG_IEEE80211R */
+#ifdef ATHEROS_USE_RAW_RECEIVE
+ } else if (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);
+#endif /* ATHEROS_USE_RAW_RECEIVE */
}
}
@@ -1375,7 +1384,7 @@
static void
atheros_wireless_event_wireless(struct atheros_driver_data *drv,
- char *data, int len)
+ char *data, unsigned int len)
{
struct iw_event iwe_buf, *iwe = &iwe_buf;
char *pos, *end, *custom, *buf;
@@ -1383,13 +1392,13 @@
pos = data;
end = data + len;
- while (pos + IW_EV_LCP_LEN <= end) {
+ 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_MSGDUMP, "Wireless event: cmd=0x%x len=%d",
iwe->cmd, iwe->len);
- if (iwe->len <= IW_EV_LCP_LEN)
+ if (iwe->len <= IW_EV_LCP_LEN || iwe->len > end - pos)
return;
custom = pos + IW_EV_POINT_LEN;
@@ -1421,7 +1430,7 @@
* just like IWEVCUSTOM.
*/
case IWEVCUSTOM:
- if (custom + iwe->u.data.length > end)
+ if (iwe->u.data.length > end - custom)
return;
buf = malloc(iwe->u.data.length + 1);
if (buf == NULL)
@@ -1695,10 +1704,10 @@
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);
- atheros_set_opt_ie(priv, NULL, 0);
}
netlink_deinit(drv->netlink);
(void) linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0);
@@ -1836,10 +1845,11 @@
}
-#ifdef CONFIG_IEEE80211R
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
static int atheros_send_mgmt(void *priv, const u8 *frm, size_t data_len,
- int noack, unsigned int freq)
+ int noack, unsigned int freq,
+ const u16 *csa_offs, size_t csa_offs_len)
{
struct atheros_driver_data *drv = priv;
u8 buf[1510];
@@ -1861,8 +1871,11 @@
return set80211priv(drv, IEEE80211_IOCTL_SEND_MGMT, mgmt_frm,
sizeof(struct ieee80211req_mgmtbuf) + data_len);
}
+#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
+#ifdef CONFIG_IEEE80211R
+
static int atheros_add_tspec(void *priv, const u8 *addr, u8 *tspec_ie,
size_t tspec_ielen)
{
@@ -2142,10 +2155,12 @@
.set_ap_wps_ie = atheros_set_ap_wps_ie,
.set_authmode = atheros_set_authmode,
.set_ap = atheros_set_ap,
-#ifdef CONFIG_IEEE80211R
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
.sta_assoc = atheros_sta_assoc,
.sta_auth = atheros_sta_auth,
.send_mlme = atheros_send_mgmt,
+#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
+#ifdef CONFIG_IEEE80211R
.add_tspec = atheros_add_tspec,
.add_sta_node = atheros_add_sta_node,
#endif /* CONFIG_IEEE80211R */
diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c
index 9ac87b4..bab1f03 100644
--- a/src/drivers/driver_bsd.c
+++ b/src/drivers/driver_bsd.c
@@ -894,7 +894,8 @@
static int
bsd_set_sta_authorized(void *priv, const u8 *addr,
- int total_flags, int flags_or, int flags_and)
+ unsigned int total_flags, unsigned int flags_or,
+ unsigned int flags_and)
{
int authorized = -1;
diff --git a/src/drivers/driver_hostap.c b/src/drivers/driver_hostap.c
index 8835005..517a3bb 100644
--- a/src/drivers/driver_hostap.c
+++ b/src/drivers/driver_hostap.c
@@ -140,7 +140,7 @@
static void handle_frame(struct hostap_driver_data *drv, u8 *buf, size_t len)
{
struct ieee80211_hdr *hdr;
- u16 fc, extra_len, type, stype;
+ u16 fc, type, stype;
size_t data_len = len;
int ver;
union wpa_event_data event;
@@ -165,19 +165,10 @@
ver = fc & WLAN_FC_PVER;
- /* protocol version 3 is reserved for indicating extra data after the
- * payload, version 2 for indicating ACKed frame (TX callbacks), and
- * version 1 for indicating failed frame (no ACK, TX callbacks) */
- if (ver == 3) {
- u8 *pos = buf + len - 2;
- extra_len = WPA_GET_LE16(pos);
- printf("extra data in frame (elen=%d)\n", extra_len);
- if ((size_t) extra_len + 2 > len) {
- printf(" extra data overflow\n");
- return;
- }
- len -= extra_len + 2;
- } else if (ver == 1 || ver == 2) {
+ /* 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) {
@@ -267,7 +258,8 @@
static int hostap_send_mlme(void *priv, const u8 *msg, size_t len, int noack,
- unsigned int freq)
+ unsigned int freq,
+ const u16 *csa_offs, size_t csa_offs_len)
{
struct hostap_driver_data *drv = priv;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) msg;
@@ -316,7 +308,7 @@
pos += 2;
memcpy(pos, data, data_len);
- res = hostap_send_mlme(drv, (u8 *) hdr, len, 0);
+ res = hostap_send_mlme(drv, (u8 *) hdr, len, 0, 0, NULL, 0);
if (res < 0) {
wpa_printf(MSG_ERROR, "hostap_send_eapol - packet len: %lu - "
"failed: %d (%s)",
@@ -329,7 +321,8 @@
static int hostap_sta_set_flags(void *priv, const u8 *addr,
- int total_flags, int flags_or, int flags_and)
+ unsigned int total_flags, unsigned int flags_or,
+ unsigned int flags_and)
{
struct hostap_driver_data *drv = priv;
struct prism2_hostapd_param param;
@@ -821,7 +814,7 @@
static void hostapd_wireless_event_wireless(struct hostap_driver_data *drv,
- char *data, int len)
+ char *data, unsigned int len)
{
struct iw_event iwe_buf, *iwe = &iwe_buf;
char *pos, *end, *custom, *buf;
@@ -829,13 +822,13 @@
pos = data;
end = data + len;
- while (pos + IW_EV_LCP_LEN <= end) {
+ 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)
+ if (iwe->len <= IW_EV_LCP_LEN || iwe->len > end - pos)
return;
custom = pos + IW_EV_POINT_LEN;
@@ -854,7 +847,7 @@
switch (iwe->cmd) {
case IWEVCUSTOM:
- if (custom + iwe->u.data.length > end)
+ if (iwe->u.data.length > end - custom)
return;
buf = malloc(iwe->u.data.length + 1);
if (buf == NULL)
@@ -1053,7 +1046,7 @@
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);
+ sizeof(mgmt.u.deauth), 0, 0, NULL, 0);
}
@@ -1091,7 +1084,7 @@
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);
+ sizeof(mgmt.u.disassoc), 0, 0, NULL, 0);
}
@@ -1169,7 +1162,7 @@
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);
+ hostap_send_mlme(priv, (u8 *)&hdr, sizeof(hdr), 0, 0, NULL, 0);
}
diff --git a/src/drivers/driver_ndis.c b/src/drivers/driver_ndis.c
index 669f1b8..fd32134 100644
--- a/src/drivers/driver_ndis.c
+++ b/src/drivers/driver_ndis.c
@@ -785,8 +785,8 @@
pos = (const u8 *) (res + 1);
end = pos + res->ie_len;
- while (pos + 1 < end) {
- if (pos + 2 + pos[1] > end)
+ while (end - pos > 1) {
+ if (2 + pos[1] > end - pos)
break;
if (pos[0] == ie)
return pos;
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 2a05a3b..e83a3df 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -176,7 +176,8 @@
static int nl80211_send_frame_cmd(struct i802_bss *bss,
unsigned int freq, unsigned int wait,
const u8 *buf, size_t buf_len, u64 *cookie,
- int no_cck, int no_ack, int offchanok);
+ 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);
@@ -1536,6 +1537,10 @@
"after rfkill unblock");
return;
}
+
+ if (is_p2p_net_interface(drv->nlmode))
+ nl80211_disable_11b_rates(drv, drv->ifindex, 1);
+
/* rtnetlink ifup handler will report interface as enabled */
}
@@ -1878,6 +1883,11 @@
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)
@@ -2221,6 +2231,11 @@
"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 {
@@ -2463,6 +2478,7 @@
}
+#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)
{
@@ -2490,6 +2506,7 @@
return ret;
}
+#endif /* CONFIG_DRIVER_NL80211_QCA */
static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss,
@@ -2520,6 +2537,7 @@
}
#endif /* CONFIG_TDLS */
+#ifdef CONFIG_DRIVER_NL80211_QCA
if (alg == WPA_ALG_PMK &&
(drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD)) {
wpa_printf(MSG_DEBUG, "%s: calling issue_key_mgmt_set_key",
@@ -2527,6 +2545,7 @@
ret = issue_key_mgmt_set_key(drv, key, key_len);
return ret;
}
+#endif /* CONFIG_DRIVER_NL80211_QCA */
if (alg == WPA_ALG_NONE) {
msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_DEL_KEY);
@@ -3084,7 +3103,9 @@
const void *data, size_t len,
int encrypt, int noack,
unsigned int freq, int no_cck,
- int offchanok, unsigned int wait_time)
+ int offchanok, unsigned int wait_time,
+ const u16 *csa_offs,
+ size_t csa_offs_len)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
u64 cookie;
@@ -3110,7 +3131,8 @@
wpa_printf(MSG_DEBUG, "nl80211: send_frame -> send_frame_cmd");
res = nl80211_send_frame_cmd(bss, freq, wait_time, data, len,
- &cookie, no_cck, noack, offchanok);
+ &cookie, no_cck, noack, offchanok,
+ csa_offs, csa_offs_len);
if (res == 0 && !noack) {
const struct ieee80211_mgmt *mgmt;
u16 fc;
@@ -3136,7 +3158,9 @@
size_t data_len, int noack,
unsigned int freq, int no_cck,
int offchanok,
- unsigned int wait_time)
+ unsigned int wait_time,
+ const u16 *csa_offs,
+ size_t csa_offs_len)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
struct ieee80211_mgmt *mgmt;
@@ -3166,7 +3190,7 @@
}
return nl80211_send_frame_cmd(bss, freq, 0,
data, data_len, NULL, 1, noack,
- 1);
+ 1, csa_offs, csa_offs_len);
}
if (drv->device_ap_sme && is_ap_interface(drv->nlmode)) {
@@ -3180,7 +3204,8 @@
wait_time,
data, data_len,
&drv->send_action_cookie,
- no_cck, noack, offchanok);
+ no_cck, noack, offchanok,
+ csa_offs, csa_offs_len);
}
if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
@@ -3200,7 +3225,8 @@
wpa_printf(MSG_DEBUG, "nl80211: send_mlme -> send_frame");
return wpa_driver_nl80211_send_frame(bss, data, data_len, encrypt,
noack, freq, no_cck, offchanok,
- wait_time);
+ wait_time, csa_offs,
+ csa_offs_len);
}
@@ -3253,7 +3279,7 @@
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
- struct nlattr *acl;
+ struct nl_msg *acl;
unsigned int i;
int ret;
@@ -3266,23 +3292,26 @@
wpa_printf(MSG_DEBUG, "nl80211: Set %s ACL (num_mac_acl=%u)",
params->acl_policy ? "Accept" : "Deny", params->num_mac_acl);
- if (!(msg = nl80211_drv_msg(drv, 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) ||
- (acl = nla_nest_start(msg, NL80211_ATTR_MAC_ADDRS)) == NULL) {
- nlmsg_free(msg);
+ acl = nlmsg_alloc();
+ if (!acl)
return -ENOMEM;
- }
-
for (i = 0; i < params->num_mac_acl; i++) {
- if (nla_put(msg, i + 1, ETH_ALEN, params->mac_acl[i].addr)) {
- nlmsg_free(msg);
+ if (nla_put(acl, i + 1, ETH_ALEN, params->mac_acl[i].addr)) {
+ nlmsg_free(acl);
return -ENOMEM;
}
}
- nla_nest_end(msg, acl);
+ if (!(msg = nl80211_drv_msg(drv, 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);
if (ret) {
@@ -4236,7 +4265,7 @@
memcpy(pos, data, data_len);
res = wpa_driver_nl80211_send_frame(bss, (u8 *) hdr, len, encrypt, 0,
- 0, 0, 0, 0);
+ 0, 0, 0, 0, NULL, 0);
if (res < 0) {
wpa_printf(MSG_ERROR, "i802_send_eapol - packet len: %lu - "
"failed: %d (%s)",
@@ -4249,8 +4278,9 @@
static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr,
- int total_flags,
- int flags_or, int flags_and)
+ unsigned int total_flags,
+ unsigned int flags_or,
+ unsigned int flags_and)
{
struct i802_bss *bss = priv;
struct nl_msg *msg;
@@ -4671,6 +4701,7 @@
int ret;
int algs;
+#ifdef CONFIG_DRIVER_NL80211_QCA
if (params->req_key_mgmt_offload && params->psk &&
(params->key_mgmt_suite == WPA_KEY_MGMT_PSK ||
params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 ||
@@ -4680,6 +4711,7 @@
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);
@@ -5424,7 +5456,7 @@
return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt,
IEEE80211_HDRLEN +
sizeof(mgmt.u.deauth), 0, 0, 0, 0,
- 0);
+ 0, NULL, 0);
}
@@ -5451,7 +5483,7 @@
return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt,
IEEE80211_HDRLEN +
sizeof(mgmt.u.disassoc), 0, 0, 0, 0,
- 0);
+ 0, NULL, 0);
}
@@ -5674,8 +5706,8 @@
struct wpa_driver_nl80211_data *drv;
struct i802_bss *bss;
size_t i;
- char brname[IFNAMSIZ];
- int ifindex, br_ifindex;
+ char master_ifname[IFNAMSIZ];
+ int ifindex, br_ifindex = 0;
int br_added = 0;
bss = wpa_driver_nl80211_drv_init(hapd, params->ifname,
@@ -5686,15 +5718,21 @@
drv = bss->drv;
- if (linux_br_get(brname, params->ifname) == 0) {
+ if (linux_br_get(master_ifname, params->ifname) == 0) {
wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in bridge %s",
- params->ifname, brname);
- br_ifindex = if_nametoindex(brname);
- os_strlcpy(bss->brname, brname, IFNAMSIZ);
+ 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));
} else {
- brname[0] = '\0';
- br_ifindex = 0;
+ master_ifname[0] = '\0';
}
+
bss->br_ifindex = br_ifindex;
for (i = 0; i < params->num_bridge; i++) {
@@ -5714,7 +5752,7 @@
if (i802_check_bridge(drv, bss, params->bridge[0],
params->ifname) < 0)
goto failed;
- if (os_strcmp(params->bridge[0], brname) != 0)
+ if (os_strcmp(params->bridge[0], master_ifname) != 0)
br_added = 1;
}
@@ -5792,8 +5830,9 @@
return NL80211_IFTYPE_P2P_DEVICE;
case WPA_IF_MESH:
return NL80211_IFTYPE_MESH_POINT;
+ default:
+ return -1;
}
- return -1;
}
@@ -6103,7 +6142,8 @@
unsigned int freq, unsigned int wait,
const u8 *buf, size_t buf_len,
u64 *cookie_out, int no_cck, int no_ack,
- int offchanok)
+ int offchanok, const u16 *csa_offs,
+ size_t csa_offs_len)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
@@ -6123,6 +6163,8 @@
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;
@@ -6182,12 +6224,12 @@
!drv->use_monitor))
ret = wpa_driver_nl80211_send_mlme(bss, buf, 24 + data_len,
0, freq, no_cck, 1,
- wait_time);
+ wait_time, NULL, 0);
else
ret = nl80211_send_frame_cmd(bss, freq, wait_time, buf,
24 + data_len,
&drv->send_action_cookie,
- no_cck, 0, 1);
+ no_cck, 0, 1, NULL, 0);
os_free(buf);
return ret;
@@ -6438,9 +6480,13 @@
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);
}
@@ -6529,7 +6575,7 @@
{
struct i802_bss *bss = priv;
return wpa_driver_nl80211_send_frame(bss, data, data_len, encrypt, 0,
- 0, 0, 0, 0);
+ 0, 0, 0, 0, NULL, 0);
}
@@ -6938,7 +6984,7 @@
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) < 0)
+ 0, 0, NULL, 0) < 0)
wpa_printf(MSG_DEBUG, "nl80211_send_null_frame: Failed to "
"send poll frame");
}
@@ -7208,6 +7254,19 @@
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);
}
@@ -7245,11 +7304,13 @@
static int driver_nl80211_send_mlme(void *priv, const u8 *data,
size_t data_len, int noack,
- unsigned int freq)
+ unsigned int freq,
+ const u16 *csa_offs, size_t csa_offs_len)
{
struct i802_bss *bss = priv;
return wpa_driver_nl80211_send_mlme(bss, data, data_len, noack,
- freq, 0, 0, 0);
+ freq, 0, 0, 0, csa_offs,
+ csa_offs_len);
}
@@ -7476,7 +7537,10 @@
"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.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",
drv->capa.key_mgmt,
drv->capa.enc,
drv->capa.auth,
@@ -7492,7 +7556,10 @@
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.mac_addr_rand_scan_supported,
+ drv->capa.conc_capab,
+ drv->capa.max_conc_chan_2_4,
+ drv->capa.max_conc_chan_5_0);
if (os_snprintf_error(end - pos, res))
return pos - buf;
pos += res;
@@ -7535,6 +7602,8 @@
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 width=%d cf1=%d cf2=%d)",
settings->cs_count, settings->block_tx,
@@ -7551,20 +7620,56 @@
(drv->nlmode != NL80211_IFTYPE_P2P_GO))
return -EOPNOTSUPP;
- /* check settings validity */
- if (!settings->beacon_csa.tail ||
- ((settings->beacon_csa.tail_len <=
- settings->counter_offset_beacon) ||
- (settings->beacon_csa.tail[settings->counter_offset_beacon] !=
- settings->cs_count)))
+ /*
+ * 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;
- if (settings->beacon_csa.probe_resp &&
- ((settings->beacon_csa.probe_resp_len <=
- settings->counter_offset_presp) ||
- (settings->beacon_csa.probe_resp[settings->counter_offset_presp] !=
- settings->cs_count)))
- 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,
@@ -7588,11 +7693,13 @@
if (ret)
goto error;
- if (nla_put_u16(msg, NL80211_ATTR_CSA_C_OFF_BEACON,
- settings->counter_offset_beacon) ||
+ 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_u16(msg, NL80211_ATTR_CSA_C_OFF_PRESP,
- settings->counter_offset_presp)))
+ 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);
@@ -7838,6 +7945,7 @@
}
+#ifdef CONFIG_DRIVER_NL80211_QCA
static int nl80211_roaming(void *priv, int allowed, const u8 *bssid)
{
struct i802_bss *bss = priv;
@@ -7870,6 +7978,7 @@
return send_and_recv_msgs(drv, msg, NULL, NULL);
}
+#endif /* CONFIG_DRIVER_NL80211_QCA */
static int nl80211_set_mac_addr(void *priv, const u8 *addr)
@@ -8354,6 +8463,8 @@
}
+#ifdef CONFIG_DRIVER_NL80211_QCA
+
static int hw_mode_to_qca_acs(enum hostapd_hw_mode hw_mode)
{
switch (hw_mode) {
@@ -8373,6 +8484,26 @@
}
+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 wpa_driver_do_acs(void *priv, struct drv_acs_params *params)
{
struct i802_bss *bss = priv;
@@ -8402,7 +8533,8 @@
params->ch_width) ||
(params->ch_list_len &&
nla_put(msg, QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST, params->ch_list_len,
- params->ch_list))) {
+ params->ch_list)) ||
+ add_acs_freq_list(msg, params->freq_list)) {
nlmsg_free(msg);
return -ENOBUFS;
}
@@ -8470,6 +8602,195 @@
}
+struct nl80211_pcl {
+ unsigned int num;
+ unsigned int *freq_list;
+};
+
+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];
+ 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];
+ if (!attr) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: preferred_freq_list couldn't be found");
+ param->num = 0;
+ return NL_SKIP;
+ }
+
+ /*
+ * 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] = freqs[num];
+ param->num = num;
+
+ return NL_SKIP;
+}
+
+
+static int nl80211_get_pref_freq_list(void *priv,
+ enum wpa_driver_if_type if_type,
+ unsigned int *num,
+ unsigned int *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);
+
+ os_memset(freq_list, 0, *num * sizeof(freq_list[0]));
+ ret = send_and_recv_msgs(drv, msg, preferred_freq_info_handler, ¶m);
+ 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",
+ i, freq_list[i]);
+ }
+
+ 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);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_ERROR, "%s: err in send_and_recv_msgs",
+ __func__);
+ return ret;
+ }
+ nlmsg_free(msg);
+ return 0;
+}
+
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+
const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.name = "nl80211",
.desc = "Linux nl80211/cfg80211",
@@ -8563,7 +8884,9 @@
.vendor_cmd = nl80211_vendor_cmd,
.set_qos_map = nl80211_set_qos_map,
.set_wowlan = nl80211_set_wowlan,
+#ifdef CONFIG_DRIVER_NL80211_QCA
.roaming = nl80211_roaming,
+#endif /* CONFIG_DRIVER_NL80211_QCA */
.set_mac_addr = nl80211_set_mac_addr,
#ifdef CONFIG_MESH
.init_mesh = wpa_driver_nl80211_init_mesh,
@@ -8576,6 +8899,10 @@
.br_set_net_param = wpa_driver_br_set_net_param,
.add_tx_ts = nl80211_add_ts,
.del_tx_ts = nl80211_del_ts,
+#ifdef CONFIG_DRIVER_NL80211_QCA
.do_acs = wpa_driver_do_acs,
.set_band = nl80211_set_band,
+ .get_pref_freq_list = nl80211_get_pref_freq_list,
+ .set_prob_oper_freq = nl80211_set_prob_oper_freq,
+#endif /* CONFIG_DRIVER_NL80211_QCA */
};
diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
index 1536d2f..ea5f058 100644
--- a/src/drivers/driver_nl80211.h
+++ b/src/drivers/driver_nl80211.h
@@ -146,7 +146,11 @@
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;
+ u64 vendor_scan_cookie;
u64 remain_on_chan_cookie;
u64 send_action_cookie;
@@ -180,6 +184,13 @@
int auth_wep_tx_keyidx;
int auth_local_state_change;
int auth_p2p;
+
+ /*
+ * 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;
};
struct nl_msg;
@@ -271,5 +282,7 @@
struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv);
void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv);
const u8 * nl80211_get_ie(const u8 *ies, size_t ies_len, u8 ie);
+int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss,
+ struct wpa_driver_scan_params *params);
#endif /* DRIVER_NL80211_H */
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index 4929cea..59a8efb 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -580,6 +580,7 @@
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;
@@ -589,6 +590,12 @@
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;
@@ -596,6 +603,10 @@
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;
+#endif /* CONFIG_DRIVER_NL80211_QCA */
}
}
@@ -627,6 +638,10 @@
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]);
+
return NL_SKIP;
}
@@ -683,8 +698,6 @@
if (!drv->capa.max_remain_on_chan)
drv->capa.max_remain_on_chan = 5000;
- if (info->channel_switch_supported)
- drv->capa.flags |= WPA_DRIVER_FLAGS_AP_CSA;
drv->capa.wmm_ac_supported = info->wmm_ac_supported;
drv->capa.mac_addr_rand_sched_scan_supported =
@@ -692,10 +705,18 @@
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;
+ }
+
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];
@@ -751,6 +772,7 @@
struct features_info {
u8 *flags;
size_t flags_len;
+ struct wpa_driver_capa *capa;
};
@@ -776,6 +798,19 @@
info->flags = nla_data(attr);
info->flags_len = nla_len(attr);
}
+ 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;
@@ -810,6 +845,7 @@
}
os_memset(&info, 0, sizeof(info));
+ info.capa = &drv->capa;
ret = send_and_recv_msgs(drv, msg, features_info_handler, &info);
if (ret || !info.flags)
return;
@@ -819,8 +855,14 @@
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;
}
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
{
@@ -901,9 +943,21 @@
if (!drv->use_monitor && !info.data_tx_status)
drv->capa.flags &= ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
+#ifdef CONFIG_DRIVER_NL80211_QCA
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 */
+
return 0;
}
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index 7b0f721..721b1b9 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -968,7 +968,7 @@
static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted,
- struct nlattr *tb[])
+ struct nlattr *tb[], int external_scan)
{
union wpa_event_data event;
struct nlattr *nl;
@@ -978,7 +978,7 @@
int freqs[MAX_REPORT_FREQS];
int num_freqs = 0;
- if (drv->scan_for_auth) {
+ if (!external_scan && drv->scan_for_auth) {
drv->scan_for_auth = 0;
wpa_printf(MSG_DEBUG, "nl80211: Scan results for missing "
"cfg80211 BSS entry");
@@ -989,6 +989,8 @@
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) {
@@ -1004,7 +1006,7 @@
}
}
if (tb[NL80211_ATTR_SCAN_FREQUENCIES]) {
- char msg[200], *pos, *end;
+ char msg[300], *pos, *end;
int res;
pos = msg;
@@ -1444,6 +1446,8 @@
}
+#ifdef CONFIG_DRIVER_NL80211_QCA
+
static void qca_nl80211_avoid_freq(struct wpa_driver_nl80211_data *drv,
const u8 *data, size_t len)
{
@@ -1686,6 +1690,140 @@
}
+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[300], *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);
+}
+
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+
static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv,
u32 subcmd, u8 *data, size_t len)
{
@@ -1693,6 +1831,7 @@
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;
@@ -1709,6 +1848,13 @@
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;
+#endif /* CONFIG_DRIVER_NL80211_QCA */
default:
wpa_printf(MSG_DEBUG,
"nl80211: Ignore unsupported QCA vendor event %u",
@@ -1831,6 +1977,7 @@
{
struct wpa_driver_nl80211_data *drv = bss->drv;
union wpa_event_data data;
+ int external_scan_event = 0;
wpa_printf(MSG_DEBUG, "nl80211: Drv Event %d (%s) received for %s",
cmd, nl80211_command_to_string(cmd), bss->ifname);
@@ -1883,28 +2030,38 @@
case NL80211_CMD_NEW_SCAN_RESULTS:
wpa_dbg(drv->ctx, MSG_DEBUG,
"nl80211: New scan results available");
- drv->scan_state = SCAN_COMPLETED;
drv->scan_complete_events = 1;
- eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv,
- drv->ctx);
- send_scan_event(drv, 0, tb);
+ if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) {
+ drv->scan_state = SCAN_COMPLETED;
+ 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);
+ send_scan_event(drv, 0, tb, 0);
break;
case NL80211_CMD_SCAN_ABORTED:
wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan aborted");
- drv->scan_state = SCAN_ABORTED;
- /*
- * Need to indicate that scan results are available in order
- * not to make wpa_supplicant stop its scanning.
- */
- eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv,
- drv->ctx);
- send_scan_event(drv, 1, tb);
+ if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) {
+ drv->scan_state = SCAN_ABORTED;
+ /*
+ * Need to indicate that scan results are available in
+ * order not to make wpa_supplicant stop its scanning.
+ */
+ eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout,
+ drv, drv->ctx);
+ 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:
diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c
index f3d45e5..966c3fb 100644
--- a/src/drivers/driver_nl80211_scan.c
+++ b/src/drivers/driver_nl80211_scan.c
@@ -14,6 +14,7 @@
#include "utils/common.h"
#include "utils/eloop.h"
#include "common/ieee802_11_defs.h"
+#include "common/qca-vendor.h"
#include "driver_nl80211.h"
@@ -221,6 +222,9 @@
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;
@@ -294,6 +298,7 @@
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);
@@ -443,8 +448,8 @@
pos = ies;
end = ies + ies_len;
- while (pos + 1 < end) {
- if (pos + 2 + pos[1] > end)
+ while (end - pos > 1) {
+ if (2 + pos[1] > end - pos)
break;
if (pos[0] == ie)
return pos;
@@ -778,3 +783,194 @@
wpa_scan_results_free(res);
}
+
+
+#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_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan SSID",
+ 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, NL80211_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;
+ }
+
+ nla_nest_end(msg, attr);
+
+ ret = send_and_recv_msgs(drv, msg, scan_cookie_handler, &cookie);
+ 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;
+
+ 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;
+}
+
+#endif /* CONFIG_DRIVER_NL80211_QCA */
diff --git a/src/drivers/driver_privsep.c b/src/drivers/driver_privsep.c
index 1cfc15d..762c12f 100644
--- a/src/drivers/driver_privsep.c
+++ b/src/drivers/driver_privsep.c
@@ -161,11 +161,11 @@
return NULL;
}
- while (results->num < (size_t) num && pos + sizeof(int) < end) {
+ while (results->num < (size_t) num && end - pos > sizeof(int)) {
int len;
os_memcpy(&len, pos, sizeof(int));
pos += sizeof(int);
- if (len < 0 || len > 10000 || pos + len > end)
+ if (len < 0 || len > 10000 || len > end - pos)
break;
r = os_malloc(len);
@@ -220,6 +220,56 @@
}
+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->sae_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->sae_data_len)
+ os_memcpy(pos, params->sae_data, params->sae_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)
{
@@ -309,6 +359,32 @@
}
+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)
@@ -468,6 +544,9 @@
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);
@@ -503,6 +582,9 @@
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);
@@ -703,6 +785,10 @@
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;
return 0;
}
@@ -735,6 +821,7 @@
.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,
diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c
index 01defdf..791cd5d 100644
--- a/src/drivers/driver_wext.c
+++ b/src/drivers/driver_wext.c
@@ -422,7 +422,7 @@
static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv,
- char *data, int len)
+ char *data, unsigned int len)
{
struct iw_event iwe_buf, *iwe = &iwe_buf;
char *pos, *end, *custom, *buf;
@@ -430,13 +430,13 @@
pos = data;
end = data + len;
- while (pos + IW_EV_LCP_LEN <= end) {
+ 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)
+ if (iwe->len <= IW_EV_LCP_LEN || iwe->len > end - pos)
return;
custom = pos + IW_EV_POINT_LEN;
@@ -480,7 +480,7 @@
}
break;
case IWEVMICHAELMICFAILURE:
- if (custom + iwe->u.data.length > end) {
+ if (iwe->u.data.length > end - custom) {
wpa_printf(MSG_DEBUG, "WEXT: Invalid "
"IWEVMICHAELMICFAILURE length");
return;
@@ -489,7 +489,7 @@
drv->ctx, custom, iwe->u.data.length);
break;
case IWEVCUSTOM:
- if (custom + iwe->u.data.length > end) {
+ if (iwe->u.data.length > end - custom) {
wpa_printf(MSG_DEBUG, "WEXT: Invalid "
"IWEVCUSTOM length");
return;
@@ -508,7 +508,7 @@
NULL);
break;
case IWEVASSOCREQIE:
- if (custom + iwe->u.data.length > end) {
+ if (iwe->u.data.length > end - custom) {
wpa_printf(MSG_DEBUG, "WEXT: Invalid "
"IWEVASSOCREQIE length");
return;
@@ -517,7 +517,7 @@
drv, custom, iwe->u.data.length);
break;
case IWEVASSOCRESPIE:
- if (custom + iwe->u.data.length > end) {
+ if (iwe->u.data.length > end - custom) {
wpa_printf(MSG_DEBUG, "WEXT: Invalid "
"IWEVASSOCRESPIE length");
return;
@@ -526,7 +526,7 @@
drv, custom, iwe->u.data.length);
break;
case IWEVPMKIDCAND:
- if (custom + iwe->u.data.length > end) {
+ if (iwe->u.data.length > end - custom) {
wpa_printf(MSG_DEBUG, "WEXT: Invalid "
"IWEVPMKIDCAND length");
return;
@@ -1220,7 +1220,7 @@
char *end)
{
int ssid_len = iwe->u.essid.length;
- if (custom + ssid_len > end)
+ if (ssid_len > end - custom)
return;
if (iwe->u.essid.flags &&
ssid_len > 0 &&
@@ -1316,7 +1316,7 @@
size_t clen;
clen = iwe->len;
- if (custom + clen > end)
+ if (clen > (size_t) (end - custom))
return;
maxrate = 0;
while (((ssize_t) clen) >= (ssize_t) sizeof(struct iw_param)) {
@@ -1369,7 +1369,7 @@
u8 *tmp;
clen = iwe->u.data.length;
- if (custom + clen > end)
+ if (clen > (size_t) (end - custom))
return;
if (clen > 7 && os_strncmp(custom, "wpa_ie=", 7) == 0) {
@@ -1441,8 +1441,8 @@
/* Figure out whether we need to fake any IEs */
pos = data->ie;
end = pos + data->ie_len;
- while (pos && pos + 1 < end) {
- if (pos + 2 + pos[1] > end)
+ while (pos && end - pos > 1) {
+ if (2 + pos[1] > end - pos)
break;
if (pos[0] == WLAN_EID_SSID)
ssid_ie = pos;
@@ -1530,11 +1530,11 @@
end = (char *) res_buf + len;
os_memset(&data, 0, sizeof(data));
- while (pos + IW_EV_LCP_LEN <= end) {
+ 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)
+ if (iwe->len <= IW_EV_LCP_LEN || iwe->len > end - pos)
break;
custom = pos + IW_EV_POINT_LEN;
diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak
index 9434078..c4f5f97 100644
--- a/src/drivers/drivers.mak
+++ b/src/drivers/drivers.mak
@@ -30,6 +30,9 @@
DRV_OBJS += ../src/drivers/driver_nl80211_monitor.o
DRV_OBJS += ../src/drivers/driver_nl80211_scan.o
DRV_OBJS += ../src/utils/radiotap.o
+ifdef CONFIG_DRIVER_NL80211_QCA
+DRV_CFLAGS += -DCONFIG_DRIVER_NL80211_QCA
+endif
NEED_SME=y
NEED_AP_MLME=y
NEED_NETLINK=y
@@ -54,7 +57,9 @@
ifdef CONFIG_LIBNL_TINY
DRV_LIBS += -lnl-tiny
else
- DRV_LIBS += -lnl
+ ifndef CONFIG_OSX
+ DRV_LIBS += -lnl
+ endif
endif
ifdef CONFIG_LIBNL20
diff --git a/src/drivers/drivers.mk b/src/drivers/drivers.mk
index 8da4c53..0a05a24 100644
--- a/src/drivers/drivers.mk
+++ b/src/drivers/drivers.mk
@@ -26,6 +26,9 @@
DRV_OBJS += src/drivers/driver_nl80211_monitor.c
DRV_OBJS += src/drivers/driver_nl80211_scan.c
DRV_OBJS += src/utils/radiotap.c
+ifdef CONFIG_DRIVER_NL80211_QCA
+DRV_CFLAGS += -DCONFIG_DRIVER_NL80211_QCA
+endif
NEED_SME=y
NEED_AP_MLME=y
NEED_NETLINK=y
diff --git a/src/drivers/linux_ioctl.c b/src/drivers/linux_ioctl.c
index 837971d..e21147a 100644
--- a/src/drivers/linux_ioctl.c
+++ b/src/drivers/linux_ioctl.c
@@ -219,3 +219,26 @@
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/src/drivers/linux_ioctl.h b/src/drivers/linux_ioctl.h
index c03fe6e..6de4d9b 100644
--- a/src/drivers/linux_ioctl.h
+++ b/src/drivers/linux_ioctl.h
@@ -18,5 +18,6 @@
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/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
index ae16ba9..c0ab6b0 100644
--- a/src/drivers/nl80211_copy.h
+++ b/src/drivers/nl80211_copy.h
@@ -1754,8 +1754,9 @@
* should be contained in the result as the sum of the respective counters
* over all channels.
*
- * @NL80211_ATTR_SCHED_SCAN_DELAY: delay before a scheduled scan (or a
- * WoWLAN net-detect scan) is started, u32 in seconds.
+ * @NL80211_ATTR_SCHED_SCAN_DELAY: delay before the first cycle of a
+ * scheduled scan (or a WoWLAN net-detect scan) is started, u32
+ * in seconds.
* @NL80211_ATTR_REG_INDOOR: flag attribute, if set indicates that the device
* is operating in an indoor environment.
@@ -2619,16 +2620,17 @@
* 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_GO_CONCURRENT: GO operation is allowed on this
+ * @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 on a
- * channel that has the GO_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 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)
+ * 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
@@ -2640,7 +2642,7 @@
* 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_GO_CONCURRENT.
+ * NL80211_FREQUENCY_ATTR_IR_CONCURRENT.
*/
enum nl80211_frequency_attr {
__NL80211_FREQUENCY_ATTR_INVALID,
@@ -2658,7 +2660,7 @@
NL80211_FREQUENCY_ATTR_NO_160MHZ,
NL80211_FREQUENCY_ATTR_DFS_CAC_TIME,
NL80211_FREQUENCY_ATTR_INDOOR_ONLY,
- NL80211_FREQUENCY_ATTR_GO_CONCURRENT,
+ NL80211_FREQUENCY_ATTR_IR_CONCURRENT,
NL80211_FREQUENCY_ATTR_NO_20MHZ,
NL80211_FREQUENCY_ATTR_NO_10MHZ,
@@ -2671,6 +2673,8 @@
#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
@@ -2829,7 +2833,7 @@
* @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_GO_CONCURRENT: See &NL80211_FREQUENCY_ATTR_GO_CONCURRENT
+ * @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
@@ -2846,7 +2850,7 @@
NL80211_RRF_NO_IR = 1<<7,
__NL80211_RRF_NO_IBSS = 1<<8,
NL80211_RRF_AUTO_BW = 1<<11,
- NL80211_RRF_GO_CONCURRENT = 1<<12,
+ NL80211_RRF_IR_CONCURRENT = 1<<12,
NL80211_RRF_NO_HT40MINUS = 1<<13,
NL80211_RRF_NO_HT40PLUS = 1<<14,
NL80211_RRF_NO_80MHZ = 1<<15,
@@ -2858,6 +2862,7 @@
#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)