15b9c547cSRui Paulo /*
25b9c547cSRui Paulo * Driver interaction with Linux nl80211/cfg80211 - Scanning
3780fb4a2SCy Schubert * Copyright(c) 2015 Intel Deutschland GmbH
45b9c547cSRui Paulo * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
55b9c547cSRui Paulo * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
65b9c547cSRui Paulo * Copyright (c) 2009-2010, Atheros Communications
75b9c547cSRui Paulo *
85b9c547cSRui Paulo * This software may be distributed under the terms of the BSD license.
95b9c547cSRui Paulo * See README for more details.
105b9c547cSRui Paulo */
115b9c547cSRui Paulo
125b9c547cSRui Paulo #include "includes.h"
1385732ac8SCy Schubert #include <time.h>
145b9c547cSRui Paulo #include <netlink/genl/genl.h>
155b9c547cSRui Paulo
165b9c547cSRui Paulo #include "utils/common.h"
175b9c547cSRui Paulo #include "utils/eloop.h"
185b9c547cSRui Paulo #include "common/ieee802_11_defs.h"
19780fb4a2SCy Schubert #include "common/ieee802_11_common.h"
20780fb4a2SCy Schubert #include "common/qca-vendor.h"
215b9c547cSRui Paulo #include "driver_nl80211.h"
225b9c547cSRui Paulo
235b9c547cSRui Paulo
24*a90b9d01SCy Schubert #define MAX_NL80211_NOISE_FREQS 100
2585732ac8SCy Schubert
2685732ac8SCy Schubert struct nl80211_noise_info {
2785732ac8SCy Schubert u32 freq[MAX_NL80211_NOISE_FREQS];
2885732ac8SCy Schubert s8 noise[MAX_NL80211_NOISE_FREQS];
2985732ac8SCy Schubert unsigned int count;
3085732ac8SCy Schubert };
3185732ac8SCy Schubert
get_noise_for_scan_results(struct nl_msg * msg,void * arg)325b9c547cSRui Paulo static int get_noise_for_scan_results(struct nl_msg *msg, void *arg)
335b9c547cSRui Paulo {
345b9c547cSRui Paulo struct nlattr *tb[NL80211_ATTR_MAX + 1];
355b9c547cSRui Paulo struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
365b9c547cSRui Paulo struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1];
375b9c547cSRui Paulo static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = {
385b9c547cSRui Paulo [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 },
395b9c547cSRui Paulo [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 },
405b9c547cSRui Paulo };
4185732ac8SCy Schubert struct nl80211_noise_info *info = arg;
4285732ac8SCy Schubert
4385732ac8SCy Schubert if (info->count >= MAX_NL80211_NOISE_FREQS)
44*a90b9d01SCy Schubert return NL_SKIP;
455b9c547cSRui Paulo
465b9c547cSRui Paulo nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
475b9c547cSRui Paulo genlmsg_attrlen(gnlh, 0), NULL);
485b9c547cSRui Paulo
495b9c547cSRui Paulo if (!tb[NL80211_ATTR_SURVEY_INFO]) {
505b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "nl80211: Survey data missing");
515b9c547cSRui Paulo return NL_SKIP;
525b9c547cSRui Paulo }
535b9c547cSRui Paulo
545b9c547cSRui Paulo if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX,
555b9c547cSRui Paulo tb[NL80211_ATTR_SURVEY_INFO],
565b9c547cSRui Paulo survey_policy)) {
575b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "nl80211: Failed to parse nested "
585b9c547cSRui Paulo "attributes");
595b9c547cSRui Paulo return NL_SKIP;
605b9c547cSRui Paulo }
615b9c547cSRui Paulo
625b9c547cSRui Paulo if (!sinfo[NL80211_SURVEY_INFO_NOISE])
635b9c547cSRui Paulo return NL_SKIP;
645b9c547cSRui Paulo
655b9c547cSRui Paulo if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY])
665b9c547cSRui Paulo return NL_SKIP;
675b9c547cSRui Paulo
6885732ac8SCy Schubert info->freq[info->count] =
6985732ac8SCy Schubert nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]);
7085732ac8SCy Schubert info->noise[info->count] =
7185732ac8SCy Schubert (s8) nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]);
7285732ac8SCy Schubert info->count++;
735b9c547cSRui Paulo
745b9c547cSRui Paulo return NL_SKIP;
755b9c547cSRui Paulo }
765b9c547cSRui Paulo
775b9c547cSRui Paulo
nl80211_get_noise_for_scan_results(struct wpa_driver_nl80211_data * drv,struct nl80211_noise_info * info)785b9c547cSRui Paulo static int nl80211_get_noise_for_scan_results(
7985732ac8SCy Schubert struct wpa_driver_nl80211_data *drv, struct nl80211_noise_info *info)
805b9c547cSRui Paulo {
815b9c547cSRui Paulo struct nl_msg *msg;
825b9c547cSRui Paulo
8385732ac8SCy Schubert os_memset(info, 0, sizeof(*info));
845b9c547cSRui Paulo msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
85*a90b9d01SCy Schubert return send_and_recv_resp(drv, msg, get_noise_for_scan_results, info);
865b9c547cSRui Paulo }
875b9c547cSRui Paulo
885b9c547cSRui Paulo
nl80211_abort_scan(struct i802_bss * bss)8985732ac8SCy Schubert static int nl80211_abort_scan(struct i802_bss *bss)
9085732ac8SCy Schubert {
9185732ac8SCy Schubert int ret;
9285732ac8SCy Schubert struct nl_msg *msg;
9385732ac8SCy Schubert struct wpa_driver_nl80211_data *drv = bss->drv;
9485732ac8SCy Schubert
9585732ac8SCy Schubert wpa_printf(MSG_DEBUG, "nl80211: Abort scan");
9685732ac8SCy Schubert msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_ABORT_SCAN);
97*a90b9d01SCy Schubert ret = send_and_recv_cmd(drv, msg);
9885732ac8SCy Schubert if (ret) {
9985732ac8SCy Schubert wpa_printf(MSG_DEBUG, "nl80211: Abort scan failed: ret=%d (%s)",
10085732ac8SCy Schubert ret, strerror(-ret));
10185732ac8SCy Schubert }
10285732ac8SCy Schubert return ret;
10385732ac8SCy Schubert }
10485732ac8SCy Schubert
10585732ac8SCy Schubert
10685732ac8SCy Schubert #ifdef CONFIG_DRIVER_NL80211_QCA
nl80211_abort_vendor_scan(struct wpa_driver_nl80211_data * drv,u64 scan_cookie)10785732ac8SCy Schubert static int nl80211_abort_vendor_scan(struct wpa_driver_nl80211_data *drv,
10885732ac8SCy Schubert u64 scan_cookie)
10985732ac8SCy Schubert {
11085732ac8SCy Schubert struct nl_msg *msg;
11185732ac8SCy Schubert struct nlattr *params;
11285732ac8SCy Schubert int ret;
11385732ac8SCy Schubert
11485732ac8SCy Schubert wpa_printf(MSG_DEBUG, "nl80211: Abort vendor scan with cookie 0x%llx",
11585732ac8SCy Schubert (long long unsigned int) scan_cookie);
11685732ac8SCy Schubert
11785732ac8SCy Schubert msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
11885732ac8SCy Schubert if (!msg ||
11985732ac8SCy Schubert nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
12085732ac8SCy Schubert nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
12185732ac8SCy Schubert QCA_NL80211_VENDOR_SUBCMD_ABORT_SCAN) ||
12285732ac8SCy Schubert !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
12385732ac8SCy Schubert nla_put_u64(msg, QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE, scan_cookie))
12485732ac8SCy Schubert goto fail;
12585732ac8SCy Schubert
12685732ac8SCy Schubert nla_nest_end(msg, params);
12785732ac8SCy Schubert
128*a90b9d01SCy Schubert ret = send_and_recv_cmd(drv, msg);
12985732ac8SCy Schubert msg = NULL;
13085732ac8SCy Schubert if (ret) {
13185732ac8SCy Schubert wpa_printf(MSG_INFO,
13285732ac8SCy Schubert "nl80211: Aborting vendor scan with cookie 0x%llx failed: ret=%d (%s)",
13385732ac8SCy Schubert (long long unsigned int) scan_cookie, ret,
13485732ac8SCy Schubert strerror(-ret));
13585732ac8SCy Schubert goto fail;
13685732ac8SCy Schubert }
13785732ac8SCy Schubert return 0;
13885732ac8SCy Schubert fail:
13985732ac8SCy Schubert nlmsg_free(msg);
14085732ac8SCy Schubert return -1;
14185732ac8SCy Schubert }
14285732ac8SCy Schubert #endif /* CONFIG_DRIVER_NL80211_QCA */
14385732ac8SCy Schubert
14485732ac8SCy Schubert
1455b9c547cSRui Paulo /**
1465b9c547cSRui Paulo * wpa_driver_nl80211_scan_timeout - Scan timeout to report scan completion
1475b9c547cSRui Paulo * @eloop_ctx: Driver private data
1485b9c547cSRui Paulo * @timeout_ctx: ctx argument given to wpa_driver_nl80211_init()
1495b9c547cSRui Paulo *
1505b9c547cSRui Paulo * This function can be used as registered timeout when starting a scan to
1515b9c547cSRui Paulo * generate a scan completed event if the driver does not report this.
1525b9c547cSRui Paulo */
wpa_driver_nl80211_scan_timeout(void * eloop_ctx,void * timeout_ctx)1535b9c547cSRui Paulo void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx)
1545b9c547cSRui Paulo {
1555b9c547cSRui Paulo struct wpa_driver_nl80211_data *drv = eloop_ctx;
156780fb4a2SCy Schubert
157780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, "nl80211: Scan timeout - try to abort it");
15885732ac8SCy Schubert #ifdef CONFIG_DRIVER_NL80211_QCA
15985732ac8SCy Schubert if (drv->vendor_scan_cookie &&
16085732ac8SCy Schubert nl80211_abort_vendor_scan(drv, drv->vendor_scan_cookie) == 0)
16185732ac8SCy Schubert return;
16285732ac8SCy Schubert #endif /* CONFIG_DRIVER_NL80211_QCA */
16385732ac8SCy Schubert if (!drv->vendor_scan_cookie &&
16485732ac8SCy Schubert nl80211_abort_scan(drv->first_bss) == 0)
165780fb4a2SCy Schubert return;
166780fb4a2SCy Schubert
167780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, "nl80211: Failed to abort scan");
168780fb4a2SCy Schubert
169c1d255d3SCy Schubert if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED)
170c1d255d3SCy Schubert nl80211_restore_ap_mode(drv->first_bss);
171780fb4a2SCy Schubert
172780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, "nl80211: Try to get scan results");
1735b9c547cSRui Paulo wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
1745b9c547cSRui Paulo }
1755b9c547cSRui Paulo
1765b9c547cSRui Paulo
1775b9c547cSRui Paulo static struct nl_msg *
nl80211_scan_common(struct i802_bss * bss,u8 cmd,struct wpa_driver_scan_params * params)1785b9c547cSRui Paulo nl80211_scan_common(struct i802_bss *bss, u8 cmd,
1795b9c547cSRui Paulo struct wpa_driver_scan_params *params)
1805b9c547cSRui Paulo {
1815b9c547cSRui Paulo struct wpa_driver_nl80211_data *drv = bss->drv;
1825b9c547cSRui Paulo struct nl_msg *msg;
1835b9c547cSRui Paulo size_t i;
1845b9c547cSRui Paulo u32 scan_flags = 0;
1855b9c547cSRui Paulo
1865b9c547cSRui Paulo msg = nl80211_cmd_msg(bss, 0, cmd);
1875b9c547cSRui Paulo if (!msg)
1885b9c547cSRui Paulo return NULL;
1895b9c547cSRui Paulo
1905b9c547cSRui Paulo if (params->num_ssids) {
1915b9c547cSRui Paulo struct nlattr *ssids;
1925b9c547cSRui Paulo
1935b9c547cSRui Paulo ssids = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS);
1945b9c547cSRui Paulo if (ssids == NULL)
1955b9c547cSRui Paulo goto fail;
1965b9c547cSRui Paulo for (i = 0; i < params->num_ssids; i++) {
1974bc52338SCy Schubert wpa_printf(MSG_MSGDUMP, "nl80211: Scan SSID %s",
1984bc52338SCy Schubert wpa_ssid_txt(params->ssids[i].ssid,
1994bc52338SCy Schubert params->ssids[i].ssid_len));
2005b9c547cSRui Paulo if (nla_put(msg, i + 1, params->ssids[i].ssid_len,
2015b9c547cSRui Paulo params->ssids[i].ssid))
2025b9c547cSRui Paulo goto fail;
2035b9c547cSRui Paulo }
2045b9c547cSRui Paulo nla_nest_end(msg, ssids);
205*a90b9d01SCy Schubert
206*a90b9d01SCy Schubert /*
207*a90b9d01SCy Schubert * If allowed, scan for 6 GHz APs that are reported by other
208*a90b9d01SCy Schubert * APs. Note that if the flag is not set and 6 GHz channels are
209*a90b9d01SCy Schubert * to be scanned, it is highly likely that non-PSC channels
210*a90b9d01SCy Schubert * would be scanned passively (due to the Probe Request frame
211*a90b9d01SCy Schubert * transmission restrictions mandated in IEEE Std 802.11ax-2021,
212*a90b9d01SCy Schubert * 26.17.2.3 (Scanning in the 6 GHz band). Passive scanning of
213*a90b9d01SCy Schubert * all non-PSC channels would take a significant amount of time.
214*a90b9d01SCy Schubert */
215*a90b9d01SCy Schubert if (!params->non_coloc_6ghz) {
216*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG,
217*a90b9d01SCy Schubert "nl80211: Scan co-located APs on 6 GHz");
218*a90b9d01SCy Schubert scan_flags |= NL80211_SCAN_FLAG_COLOCATED_6GHZ;
219*a90b9d01SCy Schubert }
220780fb4a2SCy Schubert } else {
221780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, "nl80211: Passive scan requested");
2225b9c547cSRui Paulo }
2235b9c547cSRui Paulo
2245b9c547cSRui Paulo if (params->extra_ies) {
2255b9c547cSRui Paulo wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan extra IEs",
2265b9c547cSRui Paulo params->extra_ies, params->extra_ies_len);
2275b9c547cSRui Paulo if (nla_put(msg, NL80211_ATTR_IE, params->extra_ies_len,
2285b9c547cSRui Paulo params->extra_ies))
2295b9c547cSRui Paulo goto fail;
2305b9c547cSRui Paulo }
2315b9c547cSRui Paulo
2325b9c547cSRui Paulo if (params->freqs) {
2335b9c547cSRui Paulo struct nlattr *freqs;
2345b9c547cSRui Paulo freqs = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES);
2355b9c547cSRui Paulo if (freqs == NULL)
2365b9c547cSRui Paulo goto fail;
2375b9c547cSRui Paulo for (i = 0; params->freqs[i]; i++) {
2385b9c547cSRui Paulo wpa_printf(MSG_MSGDUMP, "nl80211: Scan frequency %u "
2395b9c547cSRui Paulo "MHz", params->freqs[i]);
2405b9c547cSRui Paulo if (nla_put_u32(msg, i + 1, params->freqs[i]))
2415b9c547cSRui Paulo goto fail;
2425b9c547cSRui Paulo }
2435b9c547cSRui Paulo nla_nest_end(msg, freqs);
2445b9c547cSRui Paulo }
2455b9c547cSRui Paulo
2465b9c547cSRui Paulo os_free(drv->filter_ssids);
2475b9c547cSRui Paulo drv->filter_ssids = params->filter_ssids;
2485b9c547cSRui Paulo params->filter_ssids = NULL;
2495b9c547cSRui Paulo drv->num_filter_ssids = params->num_filter_ssids;
2505b9c547cSRui Paulo
251c1d255d3SCy Schubert if (!drv->hostapd && is_ap_interface(drv->nlmode)) {
252c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "nl80211: Add NL80211_SCAN_FLAG_AP");
253c1d255d3SCy Schubert scan_flags |= NL80211_SCAN_FLAG_AP;
254c1d255d3SCy Schubert }
255c1d255d3SCy Schubert
2565b9c547cSRui Paulo if (params->only_new_results) {
2575b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "nl80211: Add NL80211_SCAN_FLAG_FLUSH");
2585b9c547cSRui Paulo scan_flags |= NL80211_SCAN_FLAG_FLUSH;
2595b9c547cSRui Paulo }
2605b9c547cSRui Paulo
2615b9c547cSRui Paulo if (params->low_priority && drv->have_low_prio_scan) {
2625b9c547cSRui Paulo wpa_printf(MSG_DEBUG,
2635b9c547cSRui Paulo "nl80211: Add NL80211_SCAN_FLAG_LOW_PRIORITY");
2645b9c547cSRui Paulo scan_flags |= NL80211_SCAN_FLAG_LOW_PRIORITY;
2655b9c547cSRui Paulo }
2665b9c547cSRui Paulo
2675b9c547cSRui Paulo if (params->mac_addr_rand) {
2685b9c547cSRui Paulo wpa_printf(MSG_DEBUG,
2695b9c547cSRui Paulo "nl80211: Add NL80211_SCAN_FLAG_RANDOM_ADDR");
2705b9c547cSRui Paulo scan_flags |= NL80211_SCAN_FLAG_RANDOM_ADDR;
2715b9c547cSRui Paulo
2725b9c547cSRui Paulo if (params->mac_addr) {
2735b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "nl80211: MAC address: " MACSTR,
2745b9c547cSRui Paulo MAC2STR(params->mac_addr));
2755b9c547cSRui Paulo if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN,
2765b9c547cSRui Paulo params->mac_addr))
2775b9c547cSRui Paulo goto fail;
2785b9c547cSRui Paulo }
2795b9c547cSRui Paulo
2805b9c547cSRui Paulo if (params->mac_addr_mask) {
2815b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "nl80211: MAC address mask: "
2825b9c547cSRui Paulo MACSTR, MAC2STR(params->mac_addr_mask));
2835b9c547cSRui Paulo if (nla_put(msg, NL80211_ATTR_MAC_MASK, ETH_ALEN,
2845b9c547cSRui Paulo params->mac_addr_mask))
2855b9c547cSRui Paulo goto fail;
2865b9c547cSRui Paulo }
2875b9c547cSRui Paulo }
2885b9c547cSRui Paulo
28985732ac8SCy Schubert if (params->duration) {
29085732ac8SCy Schubert if (!(drv->capa.rrm_flags &
29185732ac8SCy Schubert WPA_DRIVER_FLAGS_SUPPORT_SET_SCAN_DWELL) ||
29285732ac8SCy Schubert nla_put_u16(msg, NL80211_ATTR_MEASUREMENT_DURATION,
29385732ac8SCy Schubert params->duration))
29485732ac8SCy Schubert goto fail;
29585732ac8SCy Schubert
29685732ac8SCy Schubert if (params->duration_mandatory &&
29785732ac8SCy Schubert nla_put_flag(msg,
29885732ac8SCy Schubert NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY))
29985732ac8SCy Schubert goto fail;
30085732ac8SCy Schubert }
30185732ac8SCy Schubert
30285732ac8SCy Schubert if (params->oce_scan) {
30385732ac8SCy Schubert wpa_printf(MSG_DEBUG,
30485732ac8SCy Schubert "nl80211: Add NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME");
30585732ac8SCy Schubert wpa_printf(MSG_DEBUG,
30685732ac8SCy Schubert "nl80211: Add NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP");
30785732ac8SCy Schubert wpa_printf(MSG_DEBUG,
30885732ac8SCy Schubert "nl80211: Add NL80211_SCAN_FLAG_OCE_PROBE_REQ_MIN_TX_RATE");
30985732ac8SCy Schubert wpa_printf(MSG_DEBUG,
31085732ac8SCy Schubert "nl80211: Add NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION");
31185732ac8SCy Schubert scan_flags |= NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME |
31285732ac8SCy Schubert NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP |
31385732ac8SCy Schubert NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE |
31485732ac8SCy Schubert NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION;
31585732ac8SCy Schubert }
31685732ac8SCy Schubert
317*a90b9d01SCy Schubert if (params->min_probe_req_content) {
318*a90b9d01SCy Schubert if (drv->capa.flags2 & WPA_DRIVER_FLAGS2_SCAN_MIN_PREQ)
319*a90b9d01SCy Schubert scan_flags |= NL80211_SCAN_FLAG_MIN_PREQ_CONTENT;
320*a90b9d01SCy Schubert else
321*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG,
322*a90b9d01SCy Schubert "nl80211: NL80211_SCAN_FLAG_MIN_PREQ_CONTENT not supported");
323*a90b9d01SCy Schubert }
324*a90b9d01SCy Schubert
3255b9c547cSRui Paulo if (scan_flags &&
3265b9c547cSRui Paulo nla_put_u32(msg, NL80211_ATTR_SCAN_FLAGS, scan_flags))
3275b9c547cSRui Paulo goto fail;
3285b9c547cSRui Paulo
3295b9c547cSRui Paulo return msg;
3305b9c547cSRui Paulo
3315b9c547cSRui Paulo fail:
3325b9c547cSRui Paulo nlmsg_free(msg);
3335b9c547cSRui Paulo return NULL;
3345b9c547cSRui Paulo }
3355b9c547cSRui Paulo
3365b9c547cSRui Paulo
3375b9c547cSRui Paulo /**
3385b9c547cSRui Paulo * wpa_driver_nl80211_scan - Request the driver to initiate scan
3395b9c547cSRui Paulo * @bss: Pointer to private driver data from wpa_driver_nl80211_init()
3405b9c547cSRui Paulo * @params: Scan parameters
3415b9c547cSRui Paulo * Returns: 0 on success, -1 on failure
3425b9c547cSRui Paulo */
wpa_driver_nl80211_scan(struct i802_bss * bss,struct wpa_driver_scan_params * params)3435b9c547cSRui Paulo int wpa_driver_nl80211_scan(struct i802_bss *bss,
3445b9c547cSRui Paulo struct wpa_driver_scan_params *params)
3455b9c547cSRui Paulo {
3465b9c547cSRui Paulo struct wpa_driver_nl80211_data *drv = bss->drv;
3475b9c547cSRui Paulo int ret = -1, timeout;
3485b9c547cSRui Paulo struct nl_msg *msg = NULL;
3495b9c547cSRui Paulo
3505b9c547cSRui Paulo wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: scan request");
3515b9c547cSRui Paulo drv->scan_for_auth = 0;
3525b9c547cSRui Paulo
353325151a3SRui Paulo if (TEST_FAIL())
354325151a3SRui Paulo return -1;
355325151a3SRui Paulo
3565b9c547cSRui Paulo msg = nl80211_scan_common(bss, NL80211_CMD_TRIGGER_SCAN, params);
3575b9c547cSRui Paulo if (!msg)
3585b9c547cSRui Paulo return -1;
3595b9c547cSRui Paulo
3605b9c547cSRui Paulo if (params->p2p_probe) {
3615b9c547cSRui Paulo struct nlattr *rates;
3625b9c547cSRui Paulo
3635b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "nl80211: P2P probe - mask SuppRates");
3645b9c547cSRui Paulo
3655b9c547cSRui Paulo rates = nla_nest_start(msg, NL80211_ATTR_SCAN_SUPP_RATES);
3665b9c547cSRui Paulo if (rates == NULL)
3675b9c547cSRui Paulo goto fail;
3685b9c547cSRui Paulo
3695b9c547cSRui Paulo /*
3705b9c547cSRui Paulo * Remove 2.4 GHz rates 1, 2, 5.5, 11 Mbps from supported rates
3715b9c547cSRui Paulo * by masking out everything else apart from the OFDM rates 6,
3725b9c547cSRui Paulo * 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS rates. All 5 GHz
3735b9c547cSRui Paulo * rates are left enabled.
3745b9c547cSRui Paulo */
3755b9c547cSRui Paulo if (nla_put(msg, NL80211_BAND_2GHZ, 8,
3765b9c547cSRui Paulo "\x0c\x12\x18\x24\x30\x48\x60\x6c"))
3775b9c547cSRui Paulo goto fail;
3785b9c547cSRui Paulo nla_nest_end(msg, rates);
3795b9c547cSRui Paulo
3805b9c547cSRui Paulo if (nla_put_flag(msg, NL80211_ATTR_TX_NO_CCK_RATE))
3815b9c547cSRui Paulo goto fail;
3825b9c547cSRui Paulo }
3835b9c547cSRui Paulo
384780fb4a2SCy Schubert if (params->bssid) {
385780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, "nl80211: Scan for a specific BSSID: "
386780fb4a2SCy Schubert MACSTR, MAC2STR(params->bssid));
387*a90b9d01SCy Schubert if (nla_put(msg, NL80211_ATTR_BSSID, ETH_ALEN, params->bssid))
388*a90b9d01SCy Schubert goto fail;
389*a90b9d01SCy Schubert /* NL80211_ATTR_MAC was used for this purpose initially and the
390*a90b9d01SCy Schubert * NL80211_ATTR_BSSID was added in 2016 when MAC address
391*a90b9d01SCy Schubert * randomization was added. For compatibility with older kernel
392*a90b9d01SCy Schubert * versions, add the NL80211_ATTR_MAC attribute as well when
393*a90b9d01SCy Schubert * the conflicting functionality is not in use. */
394*a90b9d01SCy Schubert if (!params->mac_addr_rand &&
395*a90b9d01SCy Schubert nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid))
396780fb4a2SCy Schubert goto fail;
397780fb4a2SCy Schubert }
398780fb4a2SCy Schubert
399*a90b9d01SCy Schubert ret = send_and_recv_cmd(drv, msg);
4005b9c547cSRui Paulo msg = NULL;
4015b9c547cSRui Paulo if (ret) {
4025b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "nl80211: Scan trigger failed: ret=%d "
4035b9c547cSRui Paulo "(%s)", ret, strerror(-ret));
4045b9c547cSRui Paulo if (drv->hostapd && is_ap_interface(drv->nlmode)) {
4055b9c547cSRui Paulo /*
4065b9c547cSRui Paulo * mac80211 does not allow scan requests in AP mode, so
4075b9c547cSRui Paulo * try to do this in station mode.
4085b9c547cSRui Paulo */
409*a90b9d01SCy Schubert drv->ap_scan_as_station = drv->nlmode;
4105b9c547cSRui Paulo if (wpa_driver_nl80211_set_mode(
411*a90b9d01SCy Schubert bss, NL80211_IFTYPE_STATION) ||
412*a90b9d01SCy Schubert wpa_driver_nl80211_scan(bss, params)) {
413*a90b9d01SCy Schubert nl80211_restore_ap_mode(bss);
4145b9c547cSRui Paulo goto fail;
4155b9c547cSRui Paulo }
4165b9c547cSRui Paulo
4175b9c547cSRui Paulo ret = 0;
4185b9c547cSRui Paulo } else
4195b9c547cSRui Paulo goto fail;
4205b9c547cSRui Paulo }
4215b9c547cSRui Paulo
4225b9c547cSRui Paulo drv->scan_state = SCAN_REQUESTED;
4235b9c547cSRui Paulo /* Not all drivers generate "scan completed" wireless event, so try to
4245b9c547cSRui Paulo * read results after a timeout. */
425*a90b9d01SCy Schubert timeout = drv->uses_6ghz ? 20 : 10;
426*a90b9d01SCy Schubert if (drv->uses_s1g)
427*a90b9d01SCy Schubert timeout += 5;
4285b9c547cSRui Paulo if (drv->scan_complete_events) {
4295b9c547cSRui Paulo /*
4305b9c547cSRui Paulo * The driver seems to deliver events to notify when scan is
4315b9c547cSRui Paulo * complete, so use longer timeout to avoid race conditions
4325b9c547cSRui Paulo * with scanning and following association request.
4335b9c547cSRui Paulo */
4345b9c547cSRui Paulo timeout = 30;
4355b9c547cSRui Paulo }
4365b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "Scan requested (ret=%d) - scan timeout %d "
4375b9c547cSRui Paulo "seconds", ret, timeout);
4385b9c547cSRui Paulo eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
4395b9c547cSRui Paulo eloop_register_timeout(timeout, 0, wpa_driver_nl80211_scan_timeout,
4405b9c547cSRui Paulo drv, drv->ctx);
441780fb4a2SCy Schubert drv->last_scan_cmd = NL80211_CMD_TRIGGER_SCAN;
4425b9c547cSRui Paulo
4435b9c547cSRui Paulo fail:
4445b9c547cSRui Paulo nlmsg_free(msg);
4455b9c547cSRui Paulo return ret;
4465b9c547cSRui Paulo }
4475b9c547cSRui Paulo
4485b9c547cSRui Paulo
449780fb4a2SCy Schubert static int
nl80211_sched_scan_add_scan_plans(struct wpa_driver_nl80211_data * drv,struct nl_msg * msg,struct wpa_driver_scan_params * params)450780fb4a2SCy Schubert nl80211_sched_scan_add_scan_plans(struct wpa_driver_nl80211_data *drv,
451780fb4a2SCy Schubert struct nl_msg *msg,
452780fb4a2SCy Schubert struct wpa_driver_scan_params *params)
453780fb4a2SCy Schubert {
454780fb4a2SCy Schubert struct nlattr *plans;
455780fb4a2SCy Schubert struct sched_scan_plan *scan_plans = params->sched_scan_plans;
456780fb4a2SCy Schubert unsigned int i;
457780fb4a2SCy Schubert
458780fb4a2SCy Schubert plans = nla_nest_start(msg, NL80211_ATTR_SCHED_SCAN_PLANS);
459780fb4a2SCy Schubert if (!plans)
460780fb4a2SCy Schubert return -1;
461780fb4a2SCy Schubert
462780fb4a2SCy Schubert for (i = 0; i < params->sched_scan_plans_num; i++) {
463780fb4a2SCy Schubert struct nlattr *plan = nla_nest_start(msg, i + 1);
464780fb4a2SCy Schubert
465780fb4a2SCy Schubert if (!plan)
466780fb4a2SCy Schubert return -1;
467780fb4a2SCy Schubert
468780fb4a2SCy Schubert if (!scan_plans[i].interval ||
469780fb4a2SCy Schubert scan_plans[i].interval >
470780fb4a2SCy Schubert drv->capa.max_sched_scan_plan_interval) {
471780fb4a2SCy Schubert wpa_printf(MSG_DEBUG,
472780fb4a2SCy Schubert "nl80211: sched scan plan no. %u: Invalid interval: %u",
473780fb4a2SCy Schubert i, scan_plans[i].interval);
474780fb4a2SCy Schubert return -1;
475780fb4a2SCy Schubert }
476780fb4a2SCy Schubert
477780fb4a2SCy Schubert if (nla_put_u32(msg, NL80211_SCHED_SCAN_PLAN_INTERVAL,
478780fb4a2SCy Schubert scan_plans[i].interval))
479780fb4a2SCy Schubert return -1;
480780fb4a2SCy Schubert
481780fb4a2SCy Schubert if (scan_plans[i].iterations >
482780fb4a2SCy Schubert drv->capa.max_sched_scan_plan_iterations) {
483780fb4a2SCy Schubert wpa_printf(MSG_DEBUG,
484780fb4a2SCy Schubert "nl80211: sched scan plan no. %u: Invalid number of iterations: %u",
485780fb4a2SCy Schubert i, scan_plans[i].iterations);
486780fb4a2SCy Schubert return -1;
487780fb4a2SCy Schubert }
488780fb4a2SCy Schubert
489780fb4a2SCy Schubert if (scan_plans[i].iterations &&
490780fb4a2SCy Schubert nla_put_u32(msg, NL80211_SCHED_SCAN_PLAN_ITERATIONS,
491780fb4a2SCy Schubert scan_plans[i].iterations))
492780fb4a2SCy Schubert return -1;
493780fb4a2SCy Schubert
494780fb4a2SCy Schubert nla_nest_end(msg, plan);
495780fb4a2SCy Schubert
496780fb4a2SCy Schubert /*
497780fb4a2SCy Schubert * All the scan plans must specify the number of iterations
498780fb4a2SCy Schubert * except the last plan, which will run infinitely. So if the
499780fb4a2SCy Schubert * number of iterations is not specified, this ought to be the
500780fb4a2SCy Schubert * last scan plan.
501780fb4a2SCy Schubert */
502780fb4a2SCy Schubert if (!scan_plans[i].iterations)
503780fb4a2SCy Schubert break;
504780fb4a2SCy Schubert }
505780fb4a2SCy Schubert
506780fb4a2SCy Schubert if (i != params->sched_scan_plans_num - 1) {
507780fb4a2SCy Schubert wpa_printf(MSG_DEBUG,
508780fb4a2SCy Schubert "nl80211: All sched scan plans but the last must specify number of iterations");
509780fb4a2SCy Schubert return -1;
510780fb4a2SCy Schubert }
511780fb4a2SCy Schubert
512780fb4a2SCy Schubert nla_nest_end(msg, plans);
513780fb4a2SCy Schubert return 0;
514780fb4a2SCy Schubert }
515780fb4a2SCy Schubert
516780fb4a2SCy Schubert
5175b9c547cSRui Paulo /**
5185b9c547cSRui Paulo * wpa_driver_nl80211_sched_scan - Initiate a scheduled scan
5195b9c547cSRui Paulo * @priv: Pointer to private driver data from wpa_driver_nl80211_init()
5205b9c547cSRui Paulo * @params: Scan parameters
5215b9c547cSRui Paulo * Returns: 0 on success, -1 on failure or if not supported
5225b9c547cSRui Paulo */
wpa_driver_nl80211_sched_scan(void * priv,struct wpa_driver_scan_params * params)5235b9c547cSRui Paulo int wpa_driver_nl80211_sched_scan(void *priv,
524780fb4a2SCy Schubert struct wpa_driver_scan_params *params)
5255b9c547cSRui Paulo {
5265b9c547cSRui Paulo struct i802_bss *bss = priv;
5275b9c547cSRui Paulo struct wpa_driver_nl80211_data *drv = bss->drv;
5285b9c547cSRui Paulo int ret = -1;
5295b9c547cSRui Paulo struct nl_msg *msg;
5305b9c547cSRui Paulo size_t i;
5315b9c547cSRui Paulo
5325b9c547cSRui Paulo wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: sched_scan request");
5335b9c547cSRui Paulo
5345b9c547cSRui Paulo #ifdef ANDROID
5355b9c547cSRui Paulo if (!drv->capa.sched_scan_supported)
5365b9c547cSRui Paulo return android_pno_start(bss, params);
5375b9c547cSRui Paulo #endif /* ANDROID */
5385b9c547cSRui Paulo
539780fb4a2SCy Schubert if (!params->sched_scan_plans_num ||
540780fb4a2SCy Schubert params->sched_scan_plans_num > drv->capa.max_sched_scan_plans) {
541780fb4a2SCy Schubert wpa_printf(MSG_ERROR,
542780fb4a2SCy Schubert "nl80211: Invalid number of sched scan plans: %u",
543780fb4a2SCy Schubert params->sched_scan_plans_num);
544780fb4a2SCy Schubert return -1;
545780fb4a2SCy Schubert }
546780fb4a2SCy Schubert
5475b9c547cSRui Paulo msg = nl80211_scan_common(bss, NL80211_CMD_START_SCHED_SCAN, params);
548780fb4a2SCy Schubert if (!msg)
5495b9c547cSRui Paulo goto fail;
5505b9c547cSRui Paulo
551780fb4a2SCy Schubert if (drv->capa.max_sched_scan_plan_iterations) {
552780fb4a2SCy Schubert if (nl80211_sched_scan_add_scan_plans(drv, msg, params))
553780fb4a2SCy Schubert goto fail;
554780fb4a2SCy Schubert } else {
555780fb4a2SCy Schubert if (nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL,
556780fb4a2SCy Schubert params->sched_scan_plans[0].interval * 1000))
557780fb4a2SCy Schubert goto fail;
558780fb4a2SCy Schubert }
559780fb4a2SCy Schubert
5605b9c547cSRui Paulo if ((drv->num_filter_ssids &&
5615b9c547cSRui Paulo (int) drv->num_filter_ssids <= drv->capa.max_match_sets) ||
5625b9c547cSRui Paulo params->filter_rssi) {
5635b9c547cSRui Paulo struct nlattr *match_sets;
5645b9c547cSRui Paulo match_sets = nla_nest_start(msg, NL80211_ATTR_SCHED_SCAN_MATCH);
5655b9c547cSRui Paulo if (match_sets == NULL)
5665b9c547cSRui Paulo goto fail;
5675b9c547cSRui Paulo
5685b9c547cSRui Paulo for (i = 0; i < drv->num_filter_ssids; i++) {
5695b9c547cSRui Paulo struct nlattr *match_set_ssid;
5704bc52338SCy Schubert wpa_printf(MSG_MSGDUMP,
5714bc52338SCy Schubert "nl80211: Sched scan filter SSID %s",
5724bc52338SCy Schubert wpa_ssid_txt(drv->filter_ssids[i].ssid,
5734bc52338SCy Schubert drv->filter_ssids[i].ssid_len));
5745b9c547cSRui Paulo
5755b9c547cSRui Paulo match_set_ssid = nla_nest_start(msg, i + 1);
5765b9c547cSRui Paulo if (match_set_ssid == NULL ||
5775b9c547cSRui Paulo nla_put(msg, NL80211_ATTR_SCHED_SCAN_MATCH_SSID,
5785b9c547cSRui Paulo drv->filter_ssids[i].ssid_len,
5795b9c547cSRui Paulo drv->filter_ssids[i].ssid) ||
5805b9c547cSRui Paulo (params->filter_rssi &&
5815b9c547cSRui Paulo nla_put_u32(msg,
5825b9c547cSRui Paulo NL80211_SCHED_SCAN_MATCH_ATTR_RSSI,
5835b9c547cSRui Paulo params->filter_rssi)))
5845b9c547cSRui Paulo goto fail;
5855b9c547cSRui Paulo
5865b9c547cSRui Paulo nla_nest_end(msg, match_set_ssid);
5875b9c547cSRui Paulo }
5885b9c547cSRui Paulo
5895b9c547cSRui Paulo /*
5905b9c547cSRui Paulo * Due to backward compatibility code, newer kernels treat this
5915b9c547cSRui Paulo * matchset (with only an RSSI filter) as the default for all
5925b9c547cSRui Paulo * other matchsets, unless it's the only one, in which case the
5935b9c547cSRui Paulo * matchset will actually allow all SSIDs above the RSSI.
5945b9c547cSRui Paulo */
5955b9c547cSRui Paulo if (params->filter_rssi) {
5965b9c547cSRui Paulo struct nlattr *match_set_rssi;
5975b9c547cSRui Paulo match_set_rssi = nla_nest_start(msg, 0);
5985b9c547cSRui Paulo if (match_set_rssi == NULL ||
5995b9c547cSRui Paulo nla_put_u32(msg, NL80211_SCHED_SCAN_MATCH_ATTR_RSSI,
6005b9c547cSRui Paulo params->filter_rssi))
6015b9c547cSRui Paulo goto fail;
6025b9c547cSRui Paulo wpa_printf(MSG_MSGDUMP,
6035b9c547cSRui Paulo "nl80211: Sched scan RSSI filter %d dBm",
6045b9c547cSRui Paulo params->filter_rssi);
6055b9c547cSRui Paulo nla_nest_end(msg, match_set_rssi);
6065b9c547cSRui Paulo }
6075b9c547cSRui Paulo
6085b9c547cSRui Paulo nla_nest_end(msg, match_sets);
6095b9c547cSRui Paulo }
6105b9c547cSRui Paulo
61185732ac8SCy Schubert if (params->relative_rssi_set) {
61285732ac8SCy Schubert struct nl80211_bss_select_rssi_adjust rssi_adjust;
61385732ac8SCy Schubert
61485732ac8SCy Schubert os_memset(&rssi_adjust, 0, sizeof(rssi_adjust));
61585732ac8SCy Schubert wpa_printf(MSG_DEBUG, "nl80211: Relative RSSI: %d",
61685732ac8SCy Schubert params->relative_rssi);
61785732ac8SCy Schubert if (nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI,
61885732ac8SCy Schubert params->relative_rssi))
61985732ac8SCy Schubert goto fail;
62085732ac8SCy Schubert
62185732ac8SCy Schubert if (params->relative_adjust_rssi) {
62285732ac8SCy Schubert int pref_band_set = 1;
62385732ac8SCy Schubert
62485732ac8SCy Schubert switch (params->relative_adjust_band) {
62585732ac8SCy Schubert case WPA_SETBAND_5G:
62685732ac8SCy Schubert rssi_adjust.band = NL80211_BAND_5GHZ;
62785732ac8SCy Schubert break;
62885732ac8SCy Schubert case WPA_SETBAND_2G:
62985732ac8SCy Schubert rssi_adjust.band = NL80211_BAND_2GHZ;
63085732ac8SCy Schubert break;
63185732ac8SCy Schubert default:
63285732ac8SCy Schubert pref_band_set = 0;
63385732ac8SCy Schubert break;
63485732ac8SCy Schubert }
63585732ac8SCy Schubert rssi_adjust.delta = params->relative_adjust_rssi;
63685732ac8SCy Schubert
63785732ac8SCy Schubert if (pref_band_set &&
63885732ac8SCy Schubert nla_put(msg, NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST,
63985732ac8SCy Schubert sizeof(rssi_adjust), &rssi_adjust))
64085732ac8SCy Schubert goto fail;
64185732ac8SCy Schubert }
64285732ac8SCy Schubert }
64385732ac8SCy Schubert
64485732ac8SCy Schubert if (params->sched_scan_start_delay &&
64585732ac8SCy Schubert nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_DELAY,
64685732ac8SCy Schubert params->sched_scan_start_delay))
64785732ac8SCy Schubert goto fail;
64885732ac8SCy Schubert
649*a90b9d01SCy Schubert ret = send_and_recv_cmd(drv, msg);
6505b9c547cSRui Paulo
6515b9c547cSRui Paulo /* TODO: if we get an error here, we should fall back to normal scan */
6525b9c547cSRui Paulo
6535b9c547cSRui Paulo msg = NULL;
6545b9c547cSRui Paulo if (ret) {
6555b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "nl80211: Sched scan start failed: "
6565b9c547cSRui Paulo "ret=%d (%s)", ret, strerror(-ret));
6575b9c547cSRui Paulo goto fail;
6585b9c547cSRui Paulo }
6595b9c547cSRui Paulo
660780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, "nl80211: Sched scan requested (ret=%d)", ret);
6615b9c547cSRui Paulo
6625b9c547cSRui Paulo fail:
6635b9c547cSRui Paulo nlmsg_free(msg);
6645b9c547cSRui Paulo return ret;
6655b9c547cSRui Paulo }
6665b9c547cSRui Paulo
6675b9c547cSRui Paulo
6685b9c547cSRui Paulo /**
6695b9c547cSRui Paulo * wpa_driver_nl80211_stop_sched_scan - Stop a scheduled scan
6705b9c547cSRui Paulo * @priv: Pointer to private driver data from wpa_driver_nl80211_init()
6715b9c547cSRui Paulo * Returns: 0 on success, -1 on failure or if not supported
6725b9c547cSRui Paulo */
wpa_driver_nl80211_stop_sched_scan(void * priv)6735b9c547cSRui Paulo int wpa_driver_nl80211_stop_sched_scan(void *priv)
6745b9c547cSRui Paulo {
6755b9c547cSRui Paulo struct i802_bss *bss = priv;
6765b9c547cSRui Paulo struct wpa_driver_nl80211_data *drv = bss->drv;
6775b9c547cSRui Paulo int ret;
6785b9c547cSRui Paulo struct nl_msg *msg;
6795b9c547cSRui Paulo
6805b9c547cSRui Paulo #ifdef ANDROID
6815b9c547cSRui Paulo if (!drv->capa.sched_scan_supported)
6825b9c547cSRui Paulo return android_pno_stop(bss);
6835b9c547cSRui Paulo #endif /* ANDROID */
6845b9c547cSRui Paulo
6855b9c547cSRui Paulo msg = nl80211_drv_msg(drv, 0, NL80211_CMD_STOP_SCHED_SCAN);
686*a90b9d01SCy Schubert ret = send_and_recv_cmd(drv, msg);
6875b9c547cSRui Paulo if (ret) {
6885b9c547cSRui Paulo wpa_printf(MSG_DEBUG,
6895b9c547cSRui Paulo "nl80211: Sched scan stop failed: ret=%d (%s)",
6905b9c547cSRui Paulo ret, strerror(-ret));
6915b9c547cSRui Paulo } else {
6925b9c547cSRui Paulo wpa_printf(MSG_DEBUG,
6935b9c547cSRui Paulo "nl80211: Sched scan stop sent");
6945b9c547cSRui Paulo }
6955b9c547cSRui Paulo
6965b9c547cSRui Paulo return ret;
6975b9c547cSRui Paulo }
6985b9c547cSRui Paulo
6995b9c547cSRui Paulo
nl80211_scan_filtered(struct wpa_driver_nl80211_data * drv,const u8 * ie,size_t ie_len)7005b9c547cSRui Paulo static int nl80211_scan_filtered(struct wpa_driver_nl80211_data *drv,
7015b9c547cSRui Paulo const u8 *ie, size_t ie_len)
7025b9c547cSRui Paulo {
7035b9c547cSRui Paulo const u8 *ssid;
7045b9c547cSRui Paulo size_t i;
7055b9c547cSRui Paulo
7065b9c547cSRui Paulo if (drv->filter_ssids == NULL)
7075b9c547cSRui Paulo return 0;
7085b9c547cSRui Paulo
709780fb4a2SCy Schubert ssid = get_ie(ie, ie_len, WLAN_EID_SSID);
7105b9c547cSRui Paulo if (ssid == NULL)
7115b9c547cSRui Paulo return 1;
7125b9c547cSRui Paulo
7135b9c547cSRui Paulo for (i = 0; i < drv->num_filter_ssids; i++) {
7145b9c547cSRui Paulo if (ssid[1] == drv->filter_ssids[i].ssid_len &&
7155b9c547cSRui Paulo os_memcmp(ssid + 2, drv->filter_ssids[i].ssid, ssid[1]) ==
7165b9c547cSRui Paulo 0)
7175b9c547cSRui Paulo return 0;
7185b9c547cSRui Paulo }
7195b9c547cSRui Paulo
7205b9c547cSRui Paulo return 1;
7215b9c547cSRui Paulo }
7225b9c547cSRui Paulo
7235b9c547cSRui Paulo
72485732ac8SCy Schubert static struct wpa_scan_res *
nl80211_parse_bss_info(struct wpa_driver_nl80211_data * drv,struct nl_msg * msg,const u8 * bssid)72585732ac8SCy Schubert nl80211_parse_bss_info(struct wpa_driver_nl80211_data *drv,
726*a90b9d01SCy Schubert struct nl_msg *msg, const u8 *bssid)
7275b9c547cSRui Paulo {
7285b9c547cSRui Paulo struct nlattr *tb[NL80211_ATTR_MAX + 1];
7295b9c547cSRui Paulo struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
7305b9c547cSRui Paulo struct nlattr *bss[NL80211_BSS_MAX + 1];
7315b9c547cSRui Paulo static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
7325b9c547cSRui Paulo [NL80211_BSS_BSSID] = { .type = NLA_UNSPEC },
7335b9c547cSRui Paulo [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
7345b9c547cSRui Paulo [NL80211_BSS_TSF] = { .type = NLA_U64 },
7355b9c547cSRui Paulo [NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 },
7365b9c547cSRui Paulo [NL80211_BSS_CAPABILITY] = { .type = NLA_U16 },
7375b9c547cSRui Paulo [NL80211_BSS_INFORMATION_ELEMENTS] = { .type = NLA_UNSPEC },
7385b9c547cSRui Paulo [NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 },
7395b9c547cSRui Paulo [NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 },
7405b9c547cSRui Paulo [NL80211_BSS_STATUS] = { .type = NLA_U32 },
7415b9c547cSRui Paulo [NL80211_BSS_SEEN_MS_AGO] = { .type = NLA_U32 },
7425b9c547cSRui Paulo [NL80211_BSS_BEACON_IES] = { .type = NLA_UNSPEC },
743*a90b9d01SCy Schubert [NL80211_BSS_BEACON_TSF] = { .type = NLA_U64 },
74485732ac8SCy Schubert [NL80211_BSS_PARENT_TSF] = { .type = NLA_U64 },
74585732ac8SCy Schubert [NL80211_BSS_PARENT_BSSID] = { .type = NLA_UNSPEC },
74685732ac8SCy Schubert [NL80211_BSS_LAST_SEEN_BOOTTIME] = { .type = NLA_U64 },
7475b9c547cSRui Paulo };
7485b9c547cSRui Paulo struct wpa_scan_res *r;
7495b9c547cSRui Paulo const u8 *ie, *beacon_ie;
7505b9c547cSRui Paulo size_t ie_len, beacon_ie_len;
7515b9c547cSRui Paulo u8 *pos;
7525b9c547cSRui Paulo
7535b9c547cSRui Paulo nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
7545b9c547cSRui Paulo genlmsg_attrlen(gnlh, 0), NULL);
7555b9c547cSRui Paulo if (!tb[NL80211_ATTR_BSS])
75685732ac8SCy Schubert return NULL;
7575b9c547cSRui Paulo if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS],
7585b9c547cSRui Paulo bss_policy))
75985732ac8SCy Schubert return NULL;
760*a90b9d01SCy Schubert if (bssid && bss[NL80211_BSS_BSSID] &&
761*a90b9d01SCy Schubert !ether_addr_equal(bssid, nla_data(bss[NL80211_BSS_BSSID])))
762*a90b9d01SCy Schubert return NULL;
7635b9c547cSRui Paulo if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) {
7645b9c547cSRui Paulo ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
7655b9c547cSRui Paulo ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
7665b9c547cSRui Paulo } else {
7675b9c547cSRui Paulo ie = NULL;
7685b9c547cSRui Paulo ie_len = 0;
7695b9c547cSRui Paulo }
7705b9c547cSRui Paulo if (bss[NL80211_BSS_BEACON_IES]) {
7715b9c547cSRui Paulo beacon_ie = nla_data(bss[NL80211_BSS_BEACON_IES]);
7725b9c547cSRui Paulo beacon_ie_len = nla_len(bss[NL80211_BSS_BEACON_IES]);
7735b9c547cSRui Paulo } else {
7745b9c547cSRui Paulo beacon_ie = NULL;
7755b9c547cSRui Paulo beacon_ie_len = 0;
7765b9c547cSRui Paulo }
7775b9c547cSRui Paulo
77885732ac8SCy Schubert if (nl80211_scan_filtered(drv, ie ? ie : beacon_ie,
7795b9c547cSRui Paulo ie ? ie_len : beacon_ie_len))
78085732ac8SCy Schubert return NULL;
7815b9c547cSRui Paulo
7825b9c547cSRui Paulo r = os_zalloc(sizeof(*r) + ie_len + beacon_ie_len);
7835b9c547cSRui Paulo if (r == NULL)
78485732ac8SCy Schubert return NULL;
7855b9c547cSRui Paulo if (bss[NL80211_BSS_BSSID])
7865b9c547cSRui Paulo os_memcpy(r->bssid, nla_data(bss[NL80211_BSS_BSSID]),
7875b9c547cSRui Paulo ETH_ALEN);
7885b9c547cSRui Paulo if (bss[NL80211_BSS_FREQUENCY])
7895b9c547cSRui Paulo r->freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
7905b9c547cSRui Paulo if (bss[NL80211_BSS_BEACON_INTERVAL])
7915b9c547cSRui Paulo r->beacon_int = nla_get_u16(bss[NL80211_BSS_BEACON_INTERVAL]);
7925b9c547cSRui Paulo if (bss[NL80211_BSS_CAPABILITY])
7935b9c547cSRui Paulo r->caps = nla_get_u16(bss[NL80211_BSS_CAPABILITY]);
7945b9c547cSRui Paulo r->flags |= WPA_SCAN_NOISE_INVALID;
7955b9c547cSRui Paulo if (bss[NL80211_BSS_SIGNAL_MBM]) {
7965b9c547cSRui Paulo r->level = nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]);
7975b9c547cSRui Paulo r->level /= 100; /* mBm to dBm */
7985b9c547cSRui Paulo r->flags |= WPA_SCAN_LEVEL_DBM | WPA_SCAN_QUAL_INVALID;
7995b9c547cSRui Paulo } else if (bss[NL80211_BSS_SIGNAL_UNSPEC]) {
8005b9c547cSRui Paulo r->level = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]);
8015b9c547cSRui Paulo r->flags |= WPA_SCAN_QUAL_INVALID;
8025b9c547cSRui Paulo } else
8035b9c547cSRui Paulo r->flags |= WPA_SCAN_LEVEL_INVALID | WPA_SCAN_QUAL_INVALID;
8045b9c547cSRui Paulo if (bss[NL80211_BSS_TSF])
8055b9c547cSRui Paulo r->tsf = nla_get_u64(bss[NL80211_BSS_TSF]);
806325151a3SRui Paulo if (bss[NL80211_BSS_BEACON_TSF]) {
807325151a3SRui Paulo u64 tsf = nla_get_u64(bss[NL80211_BSS_BEACON_TSF]);
808*a90b9d01SCy Schubert if (tsf > r->tsf) {
809325151a3SRui Paulo r->tsf = tsf;
810*a90b9d01SCy Schubert r->beacon_newer = true;
811*a90b9d01SCy Schubert }
812325151a3SRui Paulo }
8135b9c547cSRui Paulo if (bss[NL80211_BSS_SEEN_MS_AGO])
8145b9c547cSRui Paulo r->age = nla_get_u32(bss[NL80211_BSS_SEEN_MS_AGO]);
81585732ac8SCy Schubert if (bss[NL80211_BSS_LAST_SEEN_BOOTTIME]) {
81685732ac8SCy Schubert u64 boottime;
81785732ac8SCy Schubert struct timespec ts;
81885732ac8SCy Schubert
81985732ac8SCy Schubert #ifndef CLOCK_BOOTTIME
82085732ac8SCy Schubert #define CLOCK_BOOTTIME 7
82185732ac8SCy Schubert #endif
82285732ac8SCy Schubert if (clock_gettime(CLOCK_BOOTTIME, &ts) == 0) {
82385732ac8SCy Schubert /* Use more accurate boottime information to update the
82485732ac8SCy Schubert * scan result age since the driver reports this and
82585732ac8SCy Schubert * CLOCK_BOOTTIME is available. */
82685732ac8SCy Schubert boottime = nla_get_u64(
82785732ac8SCy Schubert bss[NL80211_BSS_LAST_SEEN_BOOTTIME]);
82885732ac8SCy Schubert r->age = ((u64) ts.tv_sec * 1000000000 +
82985732ac8SCy Schubert ts.tv_nsec - boottime) / 1000000;
83085732ac8SCy Schubert }
83185732ac8SCy Schubert }
8325b9c547cSRui Paulo r->ie_len = ie_len;
8335b9c547cSRui Paulo pos = (u8 *) (r + 1);
8345b9c547cSRui Paulo if (ie) {
8355b9c547cSRui Paulo os_memcpy(pos, ie, ie_len);
8365b9c547cSRui Paulo pos += ie_len;
8375b9c547cSRui Paulo }
8385b9c547cSRui Paulo r->beacon_ie_len = beacon_ie_len;
8395b9c547cSRui Paulo if (beacon_ie)
8405b9c547cSRui Paulo os_memcpy(pos, beacon_ie, beacon_ie_len);
8415b9c547cSRui Paulo
8425b9c547cSRui Paulo if (bss[NL80211_BSS_STATUS]) {
8435b9c547cSRui Paulo enum nl80211_bss_status status;
8445b9c547cSRui Paulo status = nla_get_u32(bss[NL80211_BSS_STATUS]);
8455b9c547cSRui Paulo switch (status) {
8465b9c547cSRui Paulo case NL80211_BSS_STATUS_ASSOCIATED:
8475b9c547cSRui Paulo r->flags |= WPA_SCAN_ASSOCIATED;
8485b9c547cSRui Paulo break;
8495b9c547cSRui Paulo default:
8505b9c547cSRui Paulo break;
8515b9c547cSRui Paulo }
8525b9c547cSRui Paulo }
8535b9c547cSRui Paulo
85485732ac8SCy Schubert if (bss[NL80211_BSS_PARENT_TSF] && bss[NL80211_BSS_PARENT_BSSID]) {
85585732ac8SCy Schubert r->parent_tsf = nla_get_u64(bss[NL80211_BSS_PARENT_TSF]);
85685732ac8SCy Schubert os_memcpy(r->tsf_bssid, nla_data(bss[NL80211_BSS_PARENT_BSSID]),
85785732ac8SCy Schubert ETH_ALEN);
85885732ac8SCy Schubert }
8595b9c547cSRui Paulo
86085732ac8SCy Schubert return r;
86185732ac8SCy Schubert }
8625b9c547cSRui Paulo
8635b9c547cSRui Paulo
86485732ac8SCy Schubert struct nl80211_bss_info_arg {
86585732ac8SCy Schubert struct wpa_driver_nl80211_data *drv;
86685732ac8SCy Schubert struct wpa_scan_results *res;
867*a90b9d01SCy Schubert const u8 *bssid;
86885732ac8SCy Schubert };
86985732ac8SCy Schubert
bss_info_handler(struct nl_msg * msg,void * arg)87085732ac8SCy Schubert static int bss_info_handler(struct nl_msg *msg, void *arg)
87185732ac8SCy Schubert {
87285732ac8SCy Schubert struct nl80211_bss_info_arg *_arg = arg;
87385732ac8SCy Schubert struct wpa_scan_results *res = _arg->res;
87485732ac8SCy Schubert struct wpa_scan_res **tmp;
87585732ac8SCy Schubert struct wpa_scan_res *r;
87685732ac8SCy Schubert
877*a90b9d01SCy Schubert r = nl80211_parse_bss_info(_arg->drv, msg, _arg->bssid);
87885732ac8SCy Schubert if (!r)
87985732ac8SCy Schubert return NL_SKIP;
88085732ac8SCy Schubert
88185732ac8SCy Schubert if (!res) {
8825b9c547cSRui Paulo os_free(r);
8835b9c547cSRui Paulo return NL_SKIP;
8845b9c547cSRui Paulo }
8855b9c547cSRui Paulo tmp = os_realloc_array(res->res, res->num + 1,
8865b9c547cSRui Paulo sizeof(struct wpa_scan_res *));
8875b9c547cSRui Paulo if (tmp == NULL) {
8885b9c547cSRui Paulo os_free(r);
8895b9c547cSRui Paulo return NL_SKIP;
8905b9c547cSRui Paulo }
8915b9c547cSRui Paulo tmp[res->num++] = r;
8925b9c547cSRui Paulo res->res = tmp;
8935b9c547cSRui Paulo
8945b9c547cSRui Paulo return NL_SKIP;
8955b9c547cSRui Paulo }
8965b9c547cSRui Paulo
8975b9c547cSRui Paulo
clear_state_mismatch(struct wpa_driver_nl80211_data * drv,const u8 * addr)8985b9c547cSRui Paulo static void clear_state_mismatch(struct wpa_driver_nl80211_data *drv,
8995b9c547cSRui Paulo const u8 *addr)
9005b9c547cSRui Paulo {
9015b9c547cSRui Paulo if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
9025b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "nl80211: Clear possible state "
9035b9c547cSRui Paulo "mismatch (" MACSTR ")", MAC2STR(addr));
9045b9c547cSRui Paulo wpa_driver_nl80211_mlme(drv, addr,
9055b9c547cSRui Paulo NL80211_CMD_DEAUTHENTICATE,
90685732ac8SCy Schubert WLAN_REASON_PREV_AUTH_NOT_VALID, 1,
907c1d255d3SCy Schubert drv->first_bss);
90885732ac8SCy Schubert }
90985732ac8SCy Schubert }
91085732ac8SCy Schubert
91185732ac8SCy Schubert
nl80211_check_bss_status(struct wpa_driver_nl80211_data * drv,struct wpa_scan_res * r)91285732ac8SCy Schubert static void nl80211_check_bss_status(struct wpa_driver_nl80211_data *drv,
91385732ac8SCy Schubert struct wpa_scan_res *r)
91485732ac8SCy Schubert {
91585732ac8SCy Schubert if (!(r->flags & WPA_SCAN_ASSOCIATED))
91685732ac8SCy Schubert return;
91785732ac8SCy Schubert
91885732ac8SCy Schubert wpa_printf(MSG_DEBUG, "nl80211: Scan results indicate BSS status with "
91985732ac8SCy Schubert MACSTR " as associated", MAC2STR(r->bssid));
92085732ac8SCy Schubert if (is_sta_interface(drv->nlmode) && !drv->associated) {
92185732ac8SCy Schubert wpa_printf(MSG_DEBUG,
92285732ac8SCy Schubert "nl80211: Local state (not associated) does not match with BSS state");
92385732ac8SCy Schubert clear_state_mismatch(drv, r->bssid);
92485732ac8SCy Schubert } else if (is_sta_interface(drv->nlmode) &&
925*a90b9d01SCy Schubert !ether_addr_equal(drv->bssid, r->bssid)) {
92685732ac8SCy Schubert wpa_printf(MSG_DEBUG,
92785732ac8SCy Schubert "nl80211: Local state (associated with " MACSTR
92885732ac8SCy Schubert ") does not match with BSS state",
92985732ac8SCy Schubert MAC2STR(drv->bssid));
930*a90b9d01SCy Schubert
931*a90b9d01SCy Schubert if (!ether_addr_equal(drv->sta_mlo_info.ap_mld_addr,
932*a90b9d01SCy Schubert drv->bssid)) {
93385732ac8SCy Schubert clear_state_mismatch(drv, r->bssid);
934*a90b9d01SCy Schubert
935*a90b9d01SCy Schubert if (!is_zero_ether_addr(drv->sta_mlo_info.ap_mld_addr))
936*a90b9d01SCy Schubert clear_state_mismatch(
937*a90b9d01SCy Schubert drv, drv->sta_mlo_info.ap_mld_addr);
938*a90b9d01SCy Schubert else
93985732ac8SCy Schubert clear_state_mismatch(drv, drv->bssid);
940*a90b9d01SCy Schubert
941*a90b9d01SCy Schubert } else {
942*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG,
943*a90b9d01SCy Schubert "nl80211: BSSID is the MLD address");
944*a90b9d01SCy Schubert }
9455b9c547cSRui Paulo }
9465b9c547cSRui Paulo }
9475b9c547cSRui Paulo
9485b9c547cSRui Paulo
wpa_driver_nl80211_check_bss_status(struct wpa_driver_nl80211_data * drv,struct wpa_scan_results * res)9495b9c547cSRui Paulo static void wpa_driver_nl80211_check_bss_status(
9505b9c547cSRui Paulo struct wpa_driver_nl80211_data *drv, struct wpa_scan_results *res)
9515b9c547cSRui Paulo {
9525b9c547cSRui Paulo size_t i;
9535b9c547cSRui Paulo
95485732ac8SCy Schubert for (i = 0; i < res->num; i++)
95585732ac8SCy Schubert nl80211_check_bss_status(drv, res->res[i]);
95685732ac8SCy Schubert }
9575b9c547cSRui Paulo
95885732ac8SCy Schubert
nl80211_update_scan_res_noise(struct wpa_scan_res * res,struct nl80211_noise_info * info)95985732ac8SCy Schubert static void nl80211_update_scan_res_noise(struct wpa_scan_res *res,
96085732ac8SCy Schubert struct nl80211_noise_info *info)
96185732ac8SCy Schubert {
96285732ac8SCy Schubert unsigned int i;
96385732ac8SCy Schubert
96485732ac8SCy Schubert for (i = 0; res && i < info->count; i++) {
96585732ac8SCy Schubert if ((int) info->freq[i] != res->freq ||
96685732ac8SCy Schubert !(res->flags & WPA_SCAN_NOISE_INVALID))
96785732ac8SCy Schubert continue;
96885732ac8SCy Schubert res->noise = info->noise[i];
96985732ac8SCy Schubert res->flags &= ~WPA_SCAN_NOISE_INVALID;
9705b9c547cSRui Paulo }
9715b9c547cSRui Paulo }
9725b9c547cSRui Paulo
9735b9c547cSRui Paulo
9745b9c547cSRui Paulo static struct wpa_scan_results *
nl80211_get_scan_results(struct wpa_driver_nl80211_data * drv,const u8 * bssid)975*a90b9d01SCy Schubert nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv, const u8 *bssid)
9765b9c547cSRui Paulo {
9775b9c547cSRui Paulo struct nl_msg *msg;
9785b9c547cSRui Paulo struct wpa_scan_results *res;
9795b9c547cSRui Paulo int ret;
9805b9c547cSRui Paulo struct nl80211_bss_info_arg arg;
981c1d255d3SCy Schubert int count = 0;
9825b9c547cSRui Paulo
983c1d255d3SCy Schubert try_again:
9845b9c547cSRui Paulo res = os_zalloc(sizeof(*res));
9855b9c547cSRui Paulo if (res == NULL)
9865b9c547cSRui Paulo return NULL;
9875b9c547cSRui Paulo if (!(msg = nl80211_cmd_msg(drv->first_bss, NLM_F_DUMP,
9885b9c547cSRui Paulo NL80211_CMD_GET_SCAN))) {
9895b9c547cSRui Paulo wpa_scan_results_free(res);
9905b9c547cSRui Paulo return NULL;
9915b9c547cSRui Paulo }
9925b9c547cSRui Paulo
9935b9c547cSRui Paulo arg.drv = drv;
9945b9c547cSRui Paulo arg.res = res;
995*a90b9d01SCy Schubert arg.bssid = bssid;
996*a90b9d01SCy Schubert ret = send_and_recv_resp(drv, msg, bss_info_handler, &arg);
997c1d255d3SCy Schubert if (ret == -EAGAIN) {
998c1d255d3SCy Schubert count++;
999c1d255d3SCy Schubert if (count >= 10) {
1000c1d255d3SCy Schubert wpa_printf(MSG_INFO,
1001c1d255d3SCy Schubert "nl80211: Failed to receive consistent scan result dump");
1002c1d255d3SCy Schubert } else {
1003c1d255d3SCy Schubert wpa_printf(MSG_DEBUG,
1004c1d255d3SCy Schubert "nl80211: Failed to receive consistent scan result dump - try again");
1005c1d255d3SCy Schubert wpa_scan_results_free(res);
1006c1d255d3SCy Schubert goto try_again;
1007c1d255d3SCy Schubert }
1008c1d255d3SCy Schubert }
10095b9c547cSRui Paulo if (ret == 0) {
101085732ac8SCy Schubert struct nl80211_noise_info info;
101185732ac8SCy Schubert
10125b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "nl80211: Received scan results (%lu "
10135b9c547cSRui Paulo "BSSes)", (unsigned long) res->num);
101485732ac8SCy Schubert if (nl80211_get_noise_for_scan_results(drv, &info) == 0) {
101585732ac8SCy Schubert size_t i;
101685732ac8SCy Schubert
101785732ac8SCy Schubert for (i = 0; i < res->num; ++i)
101885732ac8SCy Schubert nl80211_update_scan_res_noise(res->res[i],
101985732ac8SCy Schubert &info);
102085732ac8SCy Schubert }
10215b9c547cSRui Paulo return res;
10225b9c547cSRui Paulo }
10235b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d "
10245b9c547cSRui Paulo "(%s)", ret, strerror(-ret));
10255b9c547cSRui Paulo wpa_scan_results_free(res);
10265b9c547cSRui Paulo return NULL;
10275b9c547cSRui Paulo }
10285b9c547cSRui Paulo
10295b9c547cSRui Paulo
10305b9c547cSRui Paulo /**
10315b9c547cSRui Paulo * wpa_driver_nl80211_get_scan_results - Fetch the latest scan results
1032*a90b9d01SCy Schubert * @priv: Pointer to private nl80211 data from wpa_driver_nl80211_init()
1033*a90b9d01SCy Schubert * @bssid: Return results only for the specified BSSID, %NULL for all
10345b9c547cSRui Paulo * Returns: Scan results on success, -1 on failure
10355b9c547cSRui Paulo */
wpa_driver_nl80211_get_scan_results(void * priv,const u8 * bssid)1036*a90b9d01SCy Schubert struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv,
1037*a90b9d01SCy Schubert const u8 *bssid)
10385b9c547cSRui Paulo {
10395b9c547cSRui Paulo struct i802_bss *bss = priv;
10405b9c547cSRui Paulo struct wpa_driver_nl80211_data *drv = bss->drv;
10415b9c547cSRui Paulo struct wpa_scan_results *res;
10425b9c547cSRui Paulo
1043*a90b9d01SCy Schubert res = nl80211_get_scan_results(drv, bssid);
10445b9c547cSRui Paulo if (res)
10455b9c547cSRui Paulo wpa_driver_nl80211_check_bss_status(drv, res);
10465b9c547cSRui Paulo return res;
10475b9c547cSRui Paulo }
10485b9c547cSRui Paulo
10495b9c547cSRui Paulo
105085732ac8SCy Schubert struct nl80211_dump_scan_ctx {
105185732ac8SCy Schubert struct wpa_driver_nl80211_data *drv;
105285732ac8SCy Schubert int idx;
105385732ac8SCy Schubert };
105485732ac8SCy Schubert
nl80211_dump_scan_handler(struct nl_msg * msg,void * arg)105585732ac8SCy Schubert static int nl80211_dump_scan_handler(struct nl_msg *msg, void *arg)
105685732ac8SCy Schubert {
105785732ac8SCy Schubert struct nl80211_dump_scan_ctx *ctx = arg;
105885732ac8SCy Schubert struct wpa_scan_res *r;
105985732ac8SCy Schubert
1060*a90b9d01SCy Schubert r = nl80211_parse_bss_info(ctx->drv, msg, NULL);
106185732ac8SCy Schubert if (!r)
106285732ac8SCy Schubert return NL_SKIP;
106385732ac8SCy Schubert wpa_printf(MSG_DEBUG, "nl80211: %d " MACSTR " %d%s",
106485732ac8SCy Schubert ctx->idx, MAC2STR(r->bssid), r->freq,
106585732ac8SCy Schubert r->flags & WPA_SCAN_ASSOCIATED ? " [assoc]" : "");
106685732ac8SCy Schubert ctx->idx++;
106785732ac8SCy Schubert os_free(r);
106885732ac8SCy Schubert return NL_SKIP;
106985732ac8SCy Schubert }
107085732ac8SCy Schubert
107185732ac8SCy Schubert
nl80211_dump_scan(struct wpa_driver_nl80211_data * drv)10725b9c547cSRui Paulo void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv)
10735b9c547cSRui Paulo {
107485732ac8SCy Schubert struct nl_msg *msg;
107585732ac8SCy Schubert struct nl80211_dump_scan_ctx ctx;
10765b9c547cSRui Paulo
10775b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "nl80211: Scan result dump");
107885732ac8SCy Schubert ctx.drv = drv;
107985732ac8SCy Schubert ctx.idx = 0;
108085732ac8SCy Schubert msg = nl80211_cmd_msg(drv->first_bss, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
108185732ac8SCy Schubert if (msg)
1082*a90b9d01SCy Schubert send_and_recv_resp(drv, msg, nl80211_dump_scan_handler, &ctx);
10835b9c547cSRui Paulo }
1084780fb4a2SCy Schubert
1085780fb4a2SCy Schubert
wpa_driver_nl80211_abort_scan(void * priv,u64 scan_cookie)108685732ac8SCy Schubert int wpa_driver_nl80211_abort_scan(void *priv, u64 scan_cookie)
1087780fb4a2SCy Schubert {
1088780fb4a2SCy Schubert struct i802_bss *bss = priv;
108985732ac8SCy Schubert #ifdef CONFIG_DRIVER_NL80211_QCA
1090780fb4a2SCy Schubert struct wpa_driver_nl80211_data *drv = bss->drv;
1091780fb4a2SCy Schubert
109285732ac8SCy Schubert /*
109385732ac8SCy Schubert * If scan_cookie is zero, a normal scan through kernel (cfg80211)
109485732ac8SCy Schubert * was triggered, hence abort the cfg80211 scan instead of the vendor
109585732ac8SCy Schubert * scan.
109685732ac8SCy Schubert */
109785732ac8SCy Schubert if (drv->scan_vendor_cmd_avail && scan_cookie)
109885732ac8SCy Schubert return nl80211_abort_vendor_scan(drv, scan_cookie);
109985732ac8SCy Schubert #endif /* CONFIG_DRIVER_NL80211_QCA */
110085732ac8SCy Schubert return nl80211_abort_scan(bss);
1101780fb4a2SCy Schubert }
1102780fb4a2SCy Schubert
1103780fb4a2SCy Schubert
1104780fb4a2SCy Schubert #ifdef CONFIG_DRIVER_NL80211_QCA
1105780fb4a2SCy Schubert
scan_cookie_handler(struct nl_msg * msg,void * arg)1106780fb4a2SCy Schubert static int scan_cookie_handler(struct nl_msg *msg, void *arg)
1107780fb4a2SCy Schubert {
1108780fb4a2SCy Schubert struct nlattr *tb[NL80211_ATTR_MAX + 1];
1109780fb4a2SCy Schubert struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
1110780fb4a2SCy Schubert u64 *cookie = arg;
1111780fb4a2SCy Schubert
1112780fb4a2SCy Schubert nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
1113780fb4a2SCy Schubert genlmsg_attrlen(gnlh, 0), NULL);
1114780fb4a2SCy Schubert
1115780fb4a2SCy Schubert if (tb[NL80211_ATTR_VENDOR_DATA]) {
1116780fb4a2SCy Schubert struct nlattr *nl_vendor = tb[NL80211_ATTR_VENDOR_DATA];
1117780fb4a2SCy Schubert struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1];
1118780fb4a2SCy Schubert
1119780fb4a2SCy Schubert nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_SCAN_MAX,
1120780fb4a2SCy Schubert nla_data(nl_vendor), nla_len(nl_vendor), NULL);
1121780fb4a2SCy Schubert
1122780fb4a2SCy Schubert if (tb_vendor[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE])
1123780fb4a2SCy Schubert *cookie = nla_get_u64(
1124780fb4a2SCy Schubert tb_vendor[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]);
1125780fb4a2SCy Schubert }
1126780fb4a2SCy Schubert
1127780fb4a2SCy Schubert return NL_SKIP;
1128780fb4a2SCy Schubert }
1129780fb4a2SCy Schubert
1130780fb4a2SCy Schubert
1131780fb4a2SCy Schubert /**
1132780fb4a2SCy Schubert * wpa_driver_nl80211_vendor_scan - Request the driver to initiate a vendor scan
1133780fb4a2SCy Schubert * @bss: Pointer to private driver data from wpa_driver_nl80211_init()
1134780fb4a2SCy Schubert * @params: Scan parameters
1135780fb4a2SCy Schubert * Returns: 0 on success, -1 on failure
1136780fb4a2SCy Schubert */
wpa_driver_nl80211_vendor_scan(struct i802_bss * bss,struct wpa_driver_scan_params * params)1137780fb4a2SCy Schubert int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss,
1138780fb4a2SCy Schubert struct wpa_driver_scan_params *params)
1139780fb4a2SCy Schubert {
1140780fb4a2SCy Schubert struct wpa_driver_nl80211_data *drv = bss->drv;
1141780fb4a2SCy Schubert struct nl_msg *msg = NULL;
1142780fb4a2SCy Schubert struct nlattr *attr;
1143780fb4a2SCy Schubert size_t i;
1144780fb4a2SCy Schubert u32 scan_flags = 0;
1145780fb4a2SCy Schubert int ret = -1;
1146780fb4a2SCy Schubert u64 cookie = 0;
1147780fb4a2SCy Schubert
1148780fb4a2SCy Schubert wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: vendor scan request");
1149780fb4a2SCy Schubert drv->scan_for_auth = 0;
1150780fb4a2SCy Schubert
1151780fb4a2SCy Schubert if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
1152780fb4a2SCy Schubert nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
1153780fb4a2SCy Schubert nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
1154780fb4a2SCy Schubert QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN) )
1155780fb4a2SCy Schubert goto fail;
1156780fb4a2SCy Schubert
1157780fb4a2SCy Schubert attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
1158780fb4a2SCy Schubert if (attr == NULL)
1159780fb4a2SCy Schubert goto fail;
1160780fb4a2SCy Schubert
1161780fb4a2SCy Schubert if (params->num_ssids) {
1162780fb4a2SCy Schubert struct nlattr *ssids;
1163780fb4a2SCy Schubert
1164780fb4a2SCy Schubert ssids = nla_nest_start(msg, QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS);
1165780fb4a2SCy Schubert if (ssids == NULL)
1166780fb4a2SCy Schubert goto fail;
1167780fb4a2SCy Schubert for (i = 0; i < params->num_ssids; i++) {
11684bc52338SCy Schubert wpa_printf(MSG_MSGDUMP, "nl80211: Scan SSID %s",
11694bc52338SCy Schubert wpa_ssid_txt(params->ssids[i].ssid,
11704bc52338SCy Schubert params->ssids[i].ssid_len));
1171780fb4a2SCy Schubert if (nla_put(msg, i + 1, params->ssids[i].ssid_len,
1172780fb4a2SCy Schubert params->ssids[i].ssid))
1173780fb4a2SCy Schubert goto fail;
1174780fb4a2SCy Schubert }
1175780fb4a2SCy Schubert nla_nest_end(msg, ssids);
1176780fb4a2SCy Schubert }
1177780fb4a2SCy Schubert
1178780fb4a2SCy Schubert if (params->extra_ies) {
1179780fb4a2SCy Schubert wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan extra IEs",
1180780fb4a2SCy Schubert params->extra_ies, params->extra_ies_len);
1181780fb4a2SCy Schubert if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_SCAN_IE,
1182780fb4a2SCy Schubert params->extra_ies_len, params->extra_ies))
1183780fb4a2SCy Schubert goto fail;
1184780fb4a2SCy Schubert }
1185780fb4a2SCy Schubert
1186780fb4a2SCy Schubert if (params->freqs) {
1187780fb4a2SCy Schubert struct nlattr *freqs;
1188780fb4a2SCy Schubert
1189780fb4a2SCy Schubert freqs = nla_nest_start(msg,
1190780fb4a2SCy Schubert QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES);
1191780fb4a2SCy Schubert if (freqs == NULL)
1192780fb4a2SCy Schubert goto fail;
1193780fb4a2SCy Schubert for (i = 0; params->freqs[i]; i++) {
1194780fb4a2SCy Schubert wpa_printf(MSG_MSGDUMP,
1195780fb4a2SCy Schubert "nl80211: Scan frequency %u MHz",
1196780fb4a2SCy Schubert params->freqs[i]);
1197780fb4a2SCy Schubert if (nla_put_u32(msg, i + 1, params->freqs[i]))
1198780fb4a2SCy Schubert goto fail;
1199780fb4a2SCy Schubert }
1200780fb4a2SCy Schubert nla_nest_end(msg, freqs);
1201780fb4a2SCy Schubert }
1202780fb4a2SCy Schubert
1203780fb4a2SCy Schubert os_free(drv->filter_ssids);
1204780fb4a2SCy Schubert drv->filter_ssids = params->filter_ssids;
1205780fb4a2SCy Schubert params->filter_ssids = NULL;
1206780fb4a2SCy Schubert drv->num_filter_ssids = params->num_filter_ssids;
1207780fb4a2SCy Schubert
1208780fb4a2SCy Schubert if (params->low_priority && drv->have_low_prio_scan) {
1209780fb4a2SCy Schubert wpa_printf(MSG_DEBUG,
1210780fb4a2SCy Schubert "nl80211: Add NL80211_SCAN_FLAG_LOW_PRIORITY");
1211780fb4a2SCy Schubert scan_flags |= NL80211_SCAN_FLAG_LOW_PRIORITY;
1212780fb4a2SCy Schubert }
1213780fb4a2SCy Schubert
1214780fb4a2SCy Schubert if (params->mac_addr_rand) {
1215780fb4a2SCy Schubert wpa_printf(MSG_DEBUG,
1216780fb4a2SCy Schubert "nl80211: Add NL80211_SCAN_FLAG_RANDOM_ADDR");
1217780fb4a2SCy Schubert scan_flags |= NL80211_SCAN_FLAG_RANDOM_ADDR;
1218780fb4a2SCy Schubert
1219780fb4a2SCy Schubert if (params->mac_addr) {
1220780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, "nl80211: MAC address: " MACSTR,
1221780fb4a2SCy Schubert MAC2STR(params->mac_addr));
1222780fb4a2SCy Schubert if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_SCAN_MAC,
1223780fb4a2SCy Schubert ETH_ALEN, params->mac_addr))
1224780fb4a2SCy Schubert goto fail;
1225780fb4a2SCy Schubert }
1226780fb4a2SCy Schubert
1227780fb4a2SCy Schubert if (params->mac_addr_mask) {
1228780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, "nl80211: MAC address mask: "
1229780fb4a2SCy Schubert MACSTR, MAC2STR(params->mac_addr_mask));
1230780fb4a2SCy Schubert if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK,
1231780fb4a2SCy Schubert ETH_ALEN, params->mac_addr_mask))
1232780fb4a2SCy Schubert goto fail;
1233780fb4a2SCy Schubert }
1234780fb4a2SCy Schubert }
1235780fb4a2SCy Schubert
1236780fb4a2SCy Schubert if (scan_flags &&
123785732ac8SCy Schubert nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS, scan_flags))
1238780fb4a2SCy Schubert goto fail;
1239780fb4a2SCy Schubert
1240780fb4a2SCy Schubert if (params->p2p_probe) {
1241780fb4a2SCy Schubert struct nlattr *rates;
1242780fb4a2SCy Schubert
1243780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, "nl80211: P2P probe - mask SuppRates");
1244780fb4a2SCy Schubert
1245780fb4a2SCy Schubert rates = nla_nest_start(msg,
1246780fb4a2SCy Schubert QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES);
1247780fb4a2SCy Schubert if (rates == NULL)
1248780fb4a2SCy Schubert goto fail;
1249780fb4a2SCy Schubert
1250780fb4a2SCy Schubert /*
1251780fb4a2SCy Schubert * Remove 2.4 GHz rates 1, 2, 5.5, 11 Mbps from supported rates
1252780fb4a2SCy Schubert * by masking out everything else apart from the OFDM rates 6,
1253780fb4a2SCy Schubert * 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS rates. All 5 GHz
1254780fb4a2SCy Schubert * rates are left enabled.
1255780fb4a2SCy Schubert */
1256780fb4a2SCy Schubert if (nla_put(msg, NL80211_BAND_2GHZ, 8,
1257780fb4a2SCy Schubert "\x0c\x12\x18\x24\x30\x48\x60\x6c"))
1258780fb4a2SCy Schubert goto fail;
1259780fb4a2SCy Schubert nla_nest_end(msg, rates);
1260780fb4a2SCy Schubert
1261780fb4a2SCy Schubert if (nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE))
1262780fb4a2SCy Schubert goto fail;
1263780fb4a2SCy Schubert }
1264780fb4a2SCy Schubert
126585732ac8SCy Schubert if (params->bssid) {
126685732ac8SCy Schubert wpa_printf(MSG_DEBUG, "nl80211: Scan for a specific BSSID: "
126785732ac8SCy Schubert MACSTR, MAC2STR(params->bssid));
126885732ac8SCy Schubert if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_SCAN_BSSID, ETH_ALEN,
126985732ac8SCy Schubert params->bssid))
127085732ac8SCy Schubert goto fail;
127185732ac8SCy Schubert }
127285732ac8SCy Schubert
1273*a90b9d01SCy Schubert if (is_ap_interface(drv->nlmode) &&
1274*a90b9d01SCy Schubert params->link_id != NL80211_DRV_LINK_ID_NA &&
1275*a90b9d01SCy Schubert nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_SCAN_LINK_ID, params->link_id))
1276*a90b9d01SCy Schubert goto fail;
1277*a90b9d01SCy Schubert
1278780fb4a2SCy Schubert nla_nest_end(msg, attr);
1279780fb4a2SCy Schubert
1280*a90b9d01SCy Schubert ret = send_and_recv_resp(drv, msg, scan_cookie_handler, &cookie);
1281780fb4a2SCy Schubert msg = NULL;
1282780fb4a2SCy Schubert if (ret) {
1283780fb4a2SCy Schubert wpa_printf(MSG_DEBUG,
1284780fb4a2SCy Schubert "nl80211: Vendor scan trigger failed: ret=%d (%s)",
1285780fb4a2SCy Schubert ret, strerror(-ret));
1286780fb4a2SCy Schubert goto fail;
1287780fb4a2SCy Schubert }
1288780fb4a2SCy Schubert
1289780fb4a2SCy Schubert drv->vendor_scan_cookie = cookie;
1290780fb4a2SCy Schubert drv->scan_state = SCAN_REQUESTED;
129185732ac8SCy Schubert /* Pass the cookie to the caller to help distinguish the scans. */
129285732ac8SCy Schubert params->scan_cookie = cookie;
1293780fb4a2SCy Schubert
1294780fb4a2SCy Schubert wpa_printf(MSG_DEBUG,
1295780fb4a2SCy Schubert "nl80211: Vendor scan requested (ret=%d) - scan timeout 30 seconds, scan cookie:0x%llx",
1296780fb4a2SCy Schubert ret, (long long unsigned int) cookie);
1297780fb4a2SCy Schubert eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
1298780fb4a2SCy Schubert eloop_register_timeout(30, 0, wpa_driver_nl80211_scan_timeout,
1299780fb4a2SCy Schubert drv, drv->ctx);
1300780fb4a2SCy Schubert drv->last_scan_cmd = NL80211_CMD_VENDOR;
1301780fb4a2SCy Schubert
1302780fb4a2SCy Schubert fail:
1303780fb4a2SCy Schubert nlmsg_free(msg);
1304780fb4a2SCy Schubert return ret;
1305780fb4a2SCy Schubert }
1306780fb4a2SCy Schubert
1307780fb4a2SCy Schubert
1308780fb4a2SCy Schubert /**
1309780fb4a2SCy Schubert * nl80211_set_default_scan_ies - Set the scan default IEs to the driver
1310780fb4a2SCy Schubert * @priv: Pointer to private driver data from wpa_driver_nl80211_init()
1311780fb4a2SCy Schubert * @ies: Pointer to IEs buffer
1312780fb4a2SCy Schubert * @ies_len: Length of IEs in bytes
1313780fb4a2SCy Schubert * Returns: 0 on success, -1 on failure
1314780fb4a2SCy Schubert */
nl80211_set_default_scan_ies(void * priv,const u8 * ies,size_t ies_len)1315780fb4a2SCy Schubert int nl80211_set_default_scan_ies(void *priv, const u8 *ies, size_t ies_len)
1316780fb4a2SCy Schubert {
1317780fb4a2SCy Schubert struct i802_bss *bss = priv;
1318780fb4a2SCy Schubert struct wpa_driver_nl80211_data *drv = bss->drv;
1319780fb4a2SCy Schubert struct nl_msg *msg = NULL;
1320780fb4a2SCy Schubert struct nlattr *attr;
1321780fb4a2SCy Schubert int ret = -1;
1322780fb4a2SCy Schubert
1323780fb4a2SCy Schubert if (!drv->set_wifi_conf_vendor_cmd_avail)
1324780fb4a2SCy Schubert return -1;
1325780fb4a2SCy Schubert
1326780fb4a2SCy Schubert if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
1327780fb4a2SCy Schubert nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
1328780fb4a2SCy Schubert nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
1329780fb4a2SCy Schubert QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION))
1330780fb4a2SCy Schubert goto fail;
1331780fb4a2SCy Schubert
1332780fb4a2SCy Schubert attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
1333780fb4a2SCy Schubert if (attr == NULL)
1334780fb4a2SCy Schubert goto fail;
1335780fb4a2SCy Schubert
1336780fb4a2SCy Schubert wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan default IEs", ies, ies_len);
1337780fb4a2SCy Schubert if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_CONFIG_SCAN_DEFAULT_IES,
1338780fb4a2SCy Schubert ies_len, ies))
1339780fb4a2SCy Schubert goto fail;
1340780fb4a2SCy Schubert
1341780fb4a2SCy Schubert nla_nest_end(msg, attr);
1342780fb4a2SCy Schubert
1343*a90b9d01SCy Schubert ret = send_and_recv_cmd(drv, msg);
1344780fb4a2SCy Schubert msg = NULL;
1345780fb4a2SCy Schubert if (ret) {
1346780fb4a2SCy Schubert wpa_printf(MSG_ERROR,
1347780fb4a2SCy Schubert "nl80211: Set scan default IEs failed: ret=%d (%s)",
1348780fb4a2SCy Schubert ret, strerror(-ret));
1349780fb4a2SCy Schubert goto fail;
1350780fb4a2SCy Schubert }
1351780fb4a2SCy Schubert
1352780fb4a2SCy Schubert fail:
1353780fb4a2SCy Schubert nlmsg_free(msg);
1354780fb4a2SCy Schubert return ret;
1355780fb4a2SCy Schubert }
1356780fb4a2SCy Schubert
1357780fb4a2SCy Schubert #endif /* CONFIG_DRIVER_NL80211_QCA */
1358