xref: /freebsd/contrib/wpa/src/drivers/driver_nl80211_scan.c (revision 325151a32e114f02699a301c1e74080e7c1f1a26)
15b9c547cSRui Paulo /*
25b9c547cSRui Paulo  * Driver interaction with Linux nl80211/cfg80211 - Scanning
35b9c547cSRui Paulo  * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
45b9c547cSRui Paulo  * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
55b9c547cSRui Paulo  * Copyright (c) 2009-2010, Atheros Communications
65b9c547cSRui Paulo  *
75b9c547cSRui Paulo  * This software may be distributed under the terms of the BSD license.
85b9c547cSRui Paulo  * See README for more details.
95b9c547cSRui Paulo  */
105b9c547cSRui Paulo 
115b9c547cSRui Paulo #include "includes.h"
125b9c547cSRui Paulo #include <netlink/genl/genl.h>
135b9c547cSRui Paulo 
145b9c547cSRui Paulo #include "utils/common.h"
155b9c547cSRui Paulo #include "utils/eloop.h"
165b9c547cSRui Paulo #include "common/ieee802_11_defs.h"
175b9c547cSRui Paulo #include "driver_nl80211.h"
185b9c547cSRui Paulo 
195b9c547cSRui Paulo 
205b9c547cSRui Paulo static int get_noise_for_scan_results(struct nl_msg *msg, void *arg)
215b9c547cSRui Paulo {
225b9c547cSRui Paulo 	struct nlattr *tb[NL80211_ATTR_MAX + 1];
235b9c547cSRui Paulo 	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
245b9c547cSRui Paulo 	struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1];
255b9c547cSRui Paulo 	static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = {
265b9c547cSRui Paulo 		[NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 },
275b9c547cSRui Paulo 		[NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 },
285b9c547cSRui Paulo 	};
295b9c547cSRui Paulo 	struct wpa_scan_results *scan_results = arg;
305b9c547cSRui Paulo 	struct wpa_scan_res *scan_res;
315b9c547cSRui Paulo 	size_t i;
325b9c547cSRui Paulo 
335b9c547cSRui Paulo 	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
345b9c547cSRui Paulo 		  genlmsg_attrlen(gnlh, 0), NULL);
355b9c547cSRui Paulo 
365b9c547cSRui Paulo 	if (!tb[NL80211_ATTR_SURVEY_INFO]) {
375b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "nl80211: Survey data missing");
385b9c547cSRui Paulo 		return NL_SKIP;
395b9c547cSRui Paulo 	}
405b9c547cSRui Paulo 
415b9c547cSRui Paulo 	if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX,
425b9c547cSRui Paulo 			     tb[NL80211_ATTR_SURVEY_INFO],
435b9c547cSRui Paulo 			     survey_policy)) {
445b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "nl80211: Failed to parse nested "
455b9c547cSRui Paulo 			   "attributes");
465b9c547cSRui Paulo 		return NL_SKIP;
475b9c547cSRui Paulo 	}
485b9c547cSRui Paulo 
495b9c547cSRui Paulo 	if (!sinfo[NL80211_SURVEY_INFO_NOISE])
505b9c547cSRui Paulo 		return NL_SKIP;
515b9c547cSRui Paulo 
525b9c547cSRui Paulo 	if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY])
535b9c547cSRui Paulo 		return NL_SKIP;
545b9c547cSRui Paulo 
555b9c547cSRui Paulo 	for (i = 0; i < scan_results->num; ++i) {
565b9c547cSRui Paulo 		scan_res = scan_results->res[i];
575b9c547cSRui Paulo 		if (!scan_res)
585b9c547cSRui Paulo 			continue;
595b9c547cSRui Paulo 		if ((int) nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) !=
605b9c547cSRui Paulo 		    scan_res->freq)
615b9c547cSRui Paulo 			continue;
625b9c547cSRui Paulo 		if (!(scan_res->flags & WPA_SCAN_NOISE_INVALID))
635b9c547cSRui Paulo 			continue;
645b9c547cSRui Paulo 		scan_res->noise = (s8)
655b9c547cSRui Paulo 			nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]);
665b9c547cSRui Paulo 		scan_res->flags &= ~WPA_SCAN_NOISE_INVALID;
675b9c547cSRui Paulo 	}
685b9c547cSRui Paulo 
695b9c547cSRui Paulo 	return NL_SKIP;
705b9c547cSRui Paulo }
715b9c547cSRui Paulo 
725b9c547cSRui Paulo 
735b9c547cSRui Paulo static int nl80211_get_noise_for_scan_results(
745b9c547cSRui Paulo 	struct wpa_driver_nl80211_data *drv,
755b9c547cSRui Paulo 	struct wpa_scan_results *scan_res)
765b9c547cSRui Paulo {
775b9c547cSRui Paulo 	struct nl_msg *msg;
785b9c547cSRui Paulo 
795b9c547cSRui Paulo 	msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
805b9c547cSRui Paulo 	return send_and_recv_msgs(drv, msg, get_noise_for_scan_results,
815b9c547cSRui Paulo 				  scan_res);
825b9c547cSRui Paulo }
835b9c547cSRui Paulo 
845b9c547cSRui Paulo 
855b9c547cSRui Paulo /**
865b9c547cSRui Paulo  * wpa_driver_nl80211_scan_timeout - Scan timeout to report scan completion
875b9c547cSRui Paulo  * @eloop_ctx: Driver private data
885b9c547cSRui Paulo  * @timeout_ctx: ctx argument given to wpa_driver_nl80211_init()
895b9c547cSRui Paulo  *
905b9c547cSRui Paulo  * This function can be used as registered timeout when starting a scan to
915b9c547cSRui Paulo  * generate a scan completed event if the driver does not report this.
925b9c547cSRui Paulo  */
935b9c547cSRui Paulo void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx)
945b9c547cSRui Paulo {
955b9c547cSRui Paulo 	struct wpa_driver_nl80211_data *drv = eloop_ctx;
965b9c547cSRui Paulo 	if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED) {
975b9c547cSRui Paulo 		wpa_driver_nl80211_set_mode(drv->first_bss,
985b9c547cSRui Paulo 					    drv->ap_scan_as_station);
995b9c547cSRui Paulo 		drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
1005b9c547cSRui Paulo 	}
1015b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
1025b9c547cSRui Paulo 	wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
1035b9c547cSRui Paulo }
1045b9c547cSRui Paulo 
1055b9c547cSRui Paulo 
1065b9c547cSRui Paulo static struct nl_msg *
1075b9c547cSRui Paulo nl80211_scan_common(struct i802_bss *bss, u8 cmd,
1085b9c547cSRui Paulo 		    struct wpa_driver_scan_params *params)
1095b9c547cSRui Paulo {
1105b9c547cSRui Paulo 	struct wpa_driver_nl80211_data *drv = bss->drv;
1115b9c547cSRui Paulo 	struct nl_msg *msg;
1125b9c547cSRui Paulo 	size_t i;
1135b9c547cSRui Paulo 	u32 scan_flags = 0;
1145b9c547cSRui Paulo 
1155b9c547cSRui Paulo 	msg = nl80211_cmd_msg(bss, 0, cmd);
1165b9c547cSRui Paulo 	if (!msg)
1175b9c547cSRui Paulo 		return NULL;
1185b9c547cSRui Paulo 
1195b9c547cSRui Paulo 	if (params->num_ssids) {
1205b9c547cSRui Paulo 		struct nlattr *ssids;
1215b9c547cSRui Paulo 
1225b9c547cSRui Paulo 		ssids = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS);
1235b9c547cSRui Paulo 		if (ssids == NULL)
1245b9c547cSRui Paulo 			goto fail;
1255b9c547cSRui Paulo 		for (i = 0; i < params->num_ssids; i++) {
1265b9c547cSRui Paulo 			wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan SSID",
1275b9c547cSRui Paulo 					  params->ssids[i].ssid,
1285b9c547cSRui Paulo 					  params->ssids[i].ssid_len);
1295b9c547cSRui Paulo 			if (nla_put(msg, i + 1, params->ssids[i].ssid_len,
1305b9c547cSRui Paulo 				    params->ssids[i].ssid))
1315b9c547cSRui Paulo 				goto fail;
1325b9c547cSRui Paulo 		}
1335b9c547cSRui Paulo 		nla_nest_end(msg, ssids);
1345b9c547cSRui Paulo 	}
1355b9c547cSRui Paulo 
1365b9c547cSRui Paulo 	if (params->extra_ies) {
1375b9c547cSRui Paulo 		wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan extra IEs",
1385b9c547cSRui Paulo 			    params->extra_ies, params->extra_ies_len);
1395b9c547cSRui Paulo 		if (nla_put(msg, NL80211_ATTR_IE, params->extra_ies_len,
1405b9c547cSRui Paulo 			    params->extra_ies))
1415b9c547cSRui Paulo 			goto fail;
1425b9c547cSRui Paulo 	}
1435b9c547cSRui Paulo 
1445b9c547cSRui Paulo 	if (params->freqs) {
1455b9c547cSRui Paulo 		struct nlattr *freqs;
1465b9c547cSRui Paulo 		freqs = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES);
1475b9c547cSRui Paulo 		if (freqs == NULL)
1485b9c547cSRui Paulo 			goto fail;
1495b9c547cSRui Paulo 		for (i = 0; params->freqs[i]; i++) {
1505b9c547cSRui Paulo 			wpa_printf(MSG_MSGDUMP, "nl80211: Scan frequency %u "
1515b9c547cSRui Paulo 				   "MHz", params->freqs[i]);
1525b9c547cSRui Paulo 			if (nla_put_u32(msg, i + 1, params->freqs[i]))
1535b9c547cSRui Paulo 				goto fail;
1545b9c547cSRui Paulo 		}
1555b9c547cSRui Paulo 		nla_nest_end(msg, freqs);
1565b9c547cSRui Paulo 	}
1575b9c547cSRui Paulo 
1585b9c547cSRui Paulo 	os_free(drv->filter_ssids);
1595b9c547cSRui Paulo 	drv->filter_ssids = params->filter_ssids;
1605b9c547cSRui Paulo 	params->filter_ssids = NULL;
1615b9c547cSRui Paulo 	drv->num_filter_ssids = params->num_filter_ssids;
1625b9c547cSRui Paulo 
1635b9c547cSRui Paulo 	if (params->only_new_results) {
1645b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "nl80211: Add NL80211_SCAN_FLAG_FLUSH");
1655b9c547cSRui Paulo 		scan_flags |= NL80211_SCAN_FLAG_FLUSH;
1665b9c547cSRui Paulo 	}
1675b9c547cSRui Paulo 
1685b9c547cSRui Paulo 	if (params->low_priority && drv->have_low_prio_scan) {
1695b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
1705b9c547cSRui Paulo 			   "nl80211: Add NL80211_SCAN_FLAG_LOW_PRIORITY");
1715b9c547cSRui Paulo 		scan_flags |= NL80211_SCAN_FLAG_LOW_PRIORITY;
1725b9c547cSRui Paulo 	}
1735b9c547cSRui Paulo 
1745b9c547cSRui Paulo 	if (params->mac_addr_rand) {
1755b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
1765b9c547cSRui Paulo 			   "nl80211: Add NL80211_SCAN_FLAG_RANDOM_ADDR");
1775b9c547cSRui Paulo 		scan_flags |= NL80211_SCAN_FLAG_RANDOM_ADDR;
1785b9c547cSRui Paulo 
1795b9c547cSRui Paulo 		if (params->mac_addr) {
1805b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "nl80211: MAC address: " MACSTR,
1815b9c547cSRui Paulo 				   MAC2STR(params->mac_addr));
1825b9c547cSRui Paulo 			if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN,
1835b9c547cSRui Paulo 				    params->mac_addr))
1845b9c547cSRui Paulo 				goto fail;
1855b9c547cSRui Paulo 		}
1865b9c547cSRui Paulo 
1875b9c547cSRui Paulo 		if (params->mac_addr_mask) {
1885b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "nl80211: MAC address mask: "
1895b9c547cSRui Paulo 				   MACSTR, MAC2STR(params->mac_addr_mask));
1905b9c547cSRui Paulo 			if (nla_put(msg, NL80211_ATTR_MAC_MASK, ETH_ALEN,
1915b9c547cSRui Paulo 				    params->mac_addr_mask))
1925b9c547cSRui Paulo 				goto fail;
1935b9c547cSRui Paulo 		}
1945b9c547cSRui Paulo 	}
1955b9c547cSRui Paulo 
1965b9c547cSRui Paulo 	if (scan_flags &&
1975b9c547cSRui Paulo 	    nla_put_u32(msg, NL80211_ATTR_SCAN_FLAGS, scan_flags))
1985b9c547cSRui Paulo 		goto fail;
1995b9c547cSRui Paulo 
2005b9c547cSRui Paulo 	return msg;
2015b9c547cSRui Paulo 
2025b9c547cSRui Paulo fail:
2035b9c547cSRui Paulo 	nlmsg_free(msg);
2045b9c547cSRui Paulo 	return NULL;
2055b9c547cSRui Paulo }
2065b9c547cSRui Paulo 
2075b9c547cSRui Paulo 
2085b9c547cSRui Paulo /**
2095b9c547cSRui Paulo  * wpa_driver_nl80211_scan - Request the driver to initiate scan
2105b9c547cSRui Paulo  * @bss: Pointer to private driver data from wpa_driver_nl80211_init()
2115b9c547cSRui Paulo  * @params: Scan parameters
2125b9c547cSRui Paulo  * Returns: 0 on success, -1 on failure
2135b9c547cSRui Paulo  */
2145b9c547cSRui Paulo int wpa_driver_nl80211_scan(struct i802_bss *bss,
2155b9c547cSRui Paulo 			    struct wpa_driver_scan_params *params)
2165b9c547cSRui Paulo {
2175b9c547cSRui Paulo 	struct wpa_driver_nl80211_data *drv = bss->drv;
2185b9c547cSRui Paulo 	int ret = -1, timeout;
2195b9c547cSRui Paulo 	struct nl_msg *msg = NULL;
2205b9c547cSRui Paulo 
2215b9c547cSRui Paulo 	wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: scan request");
2225b9c547cSRui Paulo 	drv->scan_for_auth = 0;
2235b9c547cSRui Paulo 
224*325151a3SRui Paulo 	if (TEST_FAIL())
225*325151a3SRui Paulo 		return -1;
226*325151a3SRui Paulo 
2275b9c547cSRui Paulo 	msg = nl80211_scan_common(bss, NL80211_CMD_TRIGGER_SCAN, params);
2285b9c547cSRui Paulo 	if (!msg)
2295b9c547cSRui Paulo 		return -1;
2305b9c547cSRui Paulo 
2315b9c547cSRui Paulo 	if (params->p2p_probe) {
2325b9c547cSRui Paulo 		struct nlattr *rates;
2335b9c547cSRui Paulo 
2345b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "nl80211: P2P probe - mask SuppRates");
2355b9c547cSRui Paulo 
2365b9c547cSRui Paulo 		rates = nla_nest_start(msg, NL80211_ATTR_SCAN_SUPP_RATES);
2375b9c547cSRui Paulo 		if (rates == NULL)
2385b9c547cSRui Paulo 			goto fail;
2395b9c547cSRui Paulo 
2405b9c547cSRui Paulo 		/*
2415b9c547cSRui Paulo 		 * Remove 2.4 GHz rates 1, 2, 5.5, 11 Mbps from supported rates
2425b9c547cSRui Paulo 		 * by masking out everything else apart from the OFDM rates 6,
2435b9c547cSRui Paulo 		 * 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS rates. All 5 GHz
2445b9c547cSRui Paulo 		 * rates are left enabled.
2455b9c547cSRui Paulo 		 */
2465b9c547cSRui Paulo 		if (nla_put(msg, NL80211_BAND_2GHZ, 8,
2475b9c547cSRui Paulo 			    "\x0c\x12\x18\x24\x30\x48\x60\x6c"))
2485b9c547cSRui Paulo 			goto fail;
2495b9c547cSRui Paulo 		nla_nest_end(msg, rates);
2505b9c547cSRui Paulo 
2515b9c547cSRui Paulo 		if (nla_put_flag(msg, NL80211_ATTR_TX_NO_CCK_RATE))
2525b9c547cSRui Paulo 			goto fail;
2535b9c547cSRui Paulo 	}
2545b9c547cSRui Paulo 
2555b9c547cSRui Paulo 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
2565b9c547cSRui Paulo 	msg = NULL;
2575b9c547cSRui Paulo 	if (ret) {
2585b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "nl80211: Scan trigger failed: ret=%d "
2595b9c547cSRui Paulo 			   "(%s)", ret, strerror(-ret));
2605b9c547cSRui Paulo 		if (drv->hostapd && is_ap_interface(drv->nlmode)) {
2615b9c547cSRui Paulo 			enum nl80211_iftype old_mode = drv->nlmode;
2625b9c547cSRui Paulo 
2635b9c547cSRui Paulo 			/*
2645b9c547cSRui Paulo 			 * mac80211 does not allow scan requests in AP mode, so
2655b9c547cSRui Paulo 			 * try to do this in station mode.
2665b9c547cSRui Paulo 			 */
2675b9c547cSRui Paulo 			if (wpa_driver_nl80211_set_mode(
2685b9c547cSRui Paulo 				    bss, NL80211_IFTYPE_STATION))
2695b9c547cSRui Paulo 				goto fail;
2705b9c547cSRui Paulo 
2715b9c547cSRui Paulo 			if (wpa_driver_nl80211_scan(bss, params)) {
2725b9c547cSRui Paulo 				wpa_driver_nl80211_set_mode(bss, old_mode);
2735b9c547cSRui Paulo 				goto fail;
2745b9c547cSRui Paulo 			}
2755b9c547cSRui Paulo 
2765b9c547cSRui Paulo 			/* Restore AP mode when processing scan results */
2775b9c547cSRui Paulo 			drv->ap_scan_as_station = old_mode;
2785b9c547cSRui Paulo 			ret = 0;
2795b9c547cSRui Paulo 		} else
2805b9c547cSRui Paulo 			goto fail;
2815b9c547cSRui Paulo 	}
2825b9c547cSRui Paulo 
2835b9c547cSRui Paulo 	drv->scan_state = SCAN_REQUESTED;
2845b9c547cSRui Paulo 	/* Not all drivers generate "scan completed" wireless event, so try to
2855b9c547cSRui Paulo 	 * read results after a timeout. */
2865b9c547cSRui Paulo 	timeout = 10;
2875b9c547cSRui Paulo 	if (drv->scan_complete_events) {
2885b9c547cSRui Paulo 		/*
2895b9c547cSRui Paulo 		 * The driver seems to deliver events to notify when scan is
2905b9c547cSRui Paulo 		 * complete, so use longer timeout to avoid race conditions
2915b9c547cSRui Paulo 		 * with scanning and following association request.
2925b9c547cSRui Paulo 		 */
2935b9c547cSRui Paulo 		timeout = 30;
2945b9c547cSRui Paulo 	}
2955b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "Scan requested (ret=%d) - scan timeout %d "
2965b9c547cSRui Paulo 		   "seconds", ret, timeout);
2975b9c547cSRui Paulo 	eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
2985b9c547cSRui Paulo 	eloop_register_timeout(timeout, 0, wpa_driver_nl80211_scan_timeout,
2995b9c547cSRui Paulo 			       drv, drv->ctx);
3005b9c547cSRui Paulo 
3015b9c547cSRui Paulo fail:
3025b9c547cSRui Paulo 	nlmsg_free(msg);
3035b9c547cSRui Paulo 	return ret;
3045b9c547cSRui Paulo }
3055b9c547cSRui Paulo 
3065b9c547cSRui Paulo 
3075b9c547cSRui Paulo /**
3085b9c547cSRui Paulo  * wpa_driver_nl80211_sched_scan - Initiate a scheduled scan
3095b9c547cSRui Paulo  * @priv: Pointer to private driver data from wpa_driver_nl80211_init()
3105b9c547cSRui Paulo  * @params: Scan parameters
3115b9c547cSRui Paulo  * @interval: Interval between scan cycles in milliseconds
3125b9c547cSRui Paulo  * Returns: 0 on success, -1 on failure or if not supported
3135b9c547cSRui Paulo  */
3145b9c547cSRui Paulo int wpa_driver_nl80211_sched_scan(void *priv,
3155b9c547cSRui Paulo 				  struct wpa_driver_scan_params *params,
3165b9c547cSRui Paulo 				  u32 interval)
3175b9c547cSRui Paulo {
3185b9c547cSRui Paulo 	struct i802_bss *bss = priv;
3195b9c547cSRui Paulo 	struct wpa_driver_nl80211_data *drv = bss->drv;
3205b9c547cSRui Paulo 	int ret = -1;
3215b9c547cSRui Paulo 	struct nl_msg *msg;
3225b9c547cSRui Paulo 	size_t i;
3235b9c547cSRui Paulo 
3245b9c547cSRui Paulo 	wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: sched_scan request");
3255b9c547cSRui Paulo 
3265b9c547cSRui Paulo #ifdef ANDROID
3275b9c547cSRui Paulo 	if (!drv->capa.sched_scan_supported)
3285b9c547cSRui Paulo 		return android_pno_start(bss, params);
3295b9c547cSRui Paulo #endif /* ANDROID */
3305b9c547cSRui Paulo 
3315b9c547cSRui Paulo 	msg = nl80211_scan_common(bss, NL80211_CMD_START_SCHED_SCAN, params);
3325b9c547cSRui Paulo 	if (!msg ||
3335b9c547cSRui Paulo 	    nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, interval))
3345b9c547cSRui Paulo 		goto fail;
3355b9c547cSRui Paulo 
3365b9c547cSRui Paulo 	if ((drv->num_filter_ssids &&
3375b9c547cSRui Paulo 	    (int) drv->num_filter_ssids <= drv->capa.max_match_sets) ||
3385b9c547cSRui Paulo 	    params->filter_rssi) {
3395b9c547cSRui Paulo 		struct nlattr *match_sets;
3405b9c547cSRui Paulo 		match_sets = nla_nest_start(msg, NL80211_ATTR_SCHED_SCAN_MATCH);
3415b9c547cSRui Paulo 		if (match_sets == NULL)
3425b9c547cSRui Paulo 			goto fail;
3435b9c547cSRui Paulo 
3445b9c547cSRui Paulo 		for (i = 0; i < drv->num_filter_ssids; i++) {
3455b9c547cSRui Paulo 			struct nlattr *match_set_ssid;
3465b9c547cSRui Paulo 			wpa_hexdump_ascii(MSG_MSGDUMP,
3475b9c547cSRui Paulo 					  "nl80211: Sched scan filter SSID",
3485b9c547cSRui Paulo 					  drv->filter_ssids[i].ssid,
3495b9c547cSRui Paulo 					  drv->filter_ssids[i].ssid_len);
3505b9c547cSRui Paulo 
3515b9c547cSRui Paulo 			match_set_ssid = nla_nest_start(msg, i + 1);
3525b9c547cSRui Paulo 			if (match_set_ssid == NULL ||
3535b9c547cSRui Paulo 			    nla_put(msg, NL80211_ATTR_SCHED_SCAN_MATCH_SSID,
3545b9c547cSRui Paulo 				    drv->filter_ssids[i].ssid_len,
3555b9c547cSRui Paulo 				    drv->filter_ssids[i].ssid) ||
3565b9c547cSRui Paulo 			    (params->filter_rssi &&
3575b9c547cSRui Paulo 			     nla_put_u32(msg,
3585b9c547cSRui Paulo 					 NL80211_SCHED_SCAN_MATCH_ATTR_RSSI,
3595b9c547cSRui Paulo 					 params->filter_rssi)))
3605b9c547cSRui Paulo 				goto fail;
3615b9c547cSRui Paulo 
3625b9c547cSRui Paulo 			nla_nest_end(msg, match_set_ssid);
3635b9c547cSRui Paulo 		}
3645b9c547cSRui Paulo 
3655b9c547cSRui Paulo 		/*
3665b9c547cSRui Paulo 		 * Due to backward compatibility code, newer kernels treat this
3675b9c547cSRui Paulo 		 * matchset (with only an RSSI filter) as the default for all
3685b9c547cSRui Paulo 		 * other matchsets, unless it's the only one, in which case the
3695b9c547cSRui Paulo 		 * matchset will actually allow all SSIDs above the RSSI.
3705b9c547cSRui Paulo 		 */
3715b9c547cSRui Paulo 		if (params->filter_rssi) {
3725b9c547cSRui Paulo 			struct nlattr *match_set_rssi;
3735b9c547cSRui Paulo 			match_set_rssi = nla_nest_start(msg, 0);
3745b9c547cSRui Paulo 			if (match_set_rssi == NULL ||
3755b9c547cSRui Paulo 			    nla_put_u32(msg, NL80211_SCHED_SCAN_MATCH_ATTR_RSSI,
3765b9c547cSRui Paulo 					params->filter_rssi))
3775b9c547cSRui Paulo 				goto fail;
3785b9c547cSRui Paulo 			wpa_printf(MSG_MSGDUMP,
3795b9c547cSRui Paulo 				   "nl80211: Sched scan RSSI filter %d dBm",
3805b9c547cSRui Paulo 				   params->filter_rssi);
3815b9c547cSRui Paulo 			nla_nest_end(msg, match_set_rssi);
3825b9c547cSRui Paulo 		}
3835b9c547cSRui Paulo 
3845b9c547cSRui Paulo 		nla_nest_end(msg, match_sets);
3855b9c547cSRui Paulo 	}
3865b9c547cSRui Paulo 
3875b9c547cSRui Paulo 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
3885b9c547cSRui Paulo 
3895b9c547cSRui Paulo 	/* TODO: if we get an error here, we should fall back to normal scan */
3905b9c547cSRui Paulo 
3915b9c547cSRui Paulo 	msg = NULL;
3925b9c547cSRui Paulo 	if (ret) {
3935b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "nl80211: Sched scan start failed: "
3945b9c547cSRui Paulo 			   "ret=%d (%s)", ret, strerror(-ret));
3955b9c547cSRui Paulo 		goto fail;
3965b9c547cSRui Paulo 	}
3975b9c547cSRui Paulo 
3985b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "nl80211: Sched scan requested (ret=%d) - "
3995b9c547cSRui Paulo 		   "scan interval %d msec", ret, interval);
4005b9c547cSRui Paulo 
4015b9c547cSRui Paulo fail:
4025b9c547cSRui Paulo 	nlmsg_free(msg);
4035b9c547cSRui Paulo 	return ret;
4045b9c547cSRui Paulo }
4055b9c547cSRui Paulo 
4065b9c547cSRui Paulo 
4075b9c547cSRui Paulo /**
4085b9c547cSRui Paulo  * wpa_driver_nl80211_stop_sched_scan - Stop a scheduled scan
4095b9c547cSRui Paulo  * @priv: Pointer to private driver data from wpa_driver_nl80211_init()
4105b9c547cSRui Paulo  * Returns: 0 on success, -1 on failure or if not supported
4115b9c547cSRui Paulo  */
4125b9c547cSRui Paulo int wpa_driver_nl80211_stop_sched_scan(void *priv)
4135b9c547cSRui Paulo {
4145b9c547cSRui Paulo 	struct i802_bss *bss = priv;
4155b9c547cSRui Paulo 	struct wpa_driver_nl80211_data *drv = bss->drv;
4165b9c547cSRui Paulo 	int ret;
4175b9c547cSRui Paulo 	struct nl_msg *msg;
4185b9c547cSRui Paulo 
4195b9c547cSRui Paulo #ifdef ANDROID
4205b9c547cSRui Paulo 	if (!drv->capa.sched_scan_supported)
4215b9c547cSRui Paulo 		return android_pno_stop(bss);
4225b9c547cSRui Paulo #endif /* ANDROID */
4235b9c547cSRui Paulo 
4245b9c547cSRui Paulo 	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_STOP_SCHED_SCAN);
4255b9c547cSRui Paulo 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
4265b9c547cSRui Paulo 	if (ret) {
4275b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
4285b9c547cSRui Paulo 			   "nl80211: Sched scan stop failed: ret=%d (%s)",
4295b9c547cSRui Paulo 			   ret, strerror(-ret));
4305b9c547cSRui Paulo 	} else {
4315b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
4325b9c547cSRui Paulo 			   "nl80211: Sched scan stop sent");
4335b9c547cSRui Paulo 	}
4345b9c547cSRui Paulo 
4355b9c547cSRui Paulo 	return ret;
4365b9c547cSRui Paulo }
4375b9c547cSRui Paulo 
4385b9c547cSRui Paulo 
439*325151a3SRui Paulo const u8 * nl80211_get_ie(const u8 *ies, size_t ies_len, u8 ie)
4405b9c547cSRui Paulo {
4415b9c547cSRui Paulo 	const u8 *end, *pos;
4425b9c547cSRui Paulo 
4435b9c547cSRui Paulo 	if (ies == NULL)
4445b9c547cSRui Paulo 		return NULL;
4455b9c547cSRui Paulo 
4465b9c547cSRui Paulo 	pos = ies;
4475b9c547cSRui Paulo 	end = ies + ies_len;
4485b9c547cSRui Paulo 
4495b9c547cSRui Paulo 	while (pos + 1 < end) {
4505b9c547cSRui Paulo 		if (pos + 2 + pos[1] > end)
4515b9c547cSRui Paulo 			break;
4525b9c547cSRui Paulo 		if (pos[0] == ie)
4535b9c547cSRui Paulo 			return pos;
4545b9c547cSRui Paulo 		pos += 2 + pos[1];
4555b9c547cSRui Paulo 	}
4565b9c547cSRui Paulo 
4575b9c547cSRui Paulo 	return NULL;
4585b9c547cSRui Paulo }
4595b9c547cSRui Paulo 
4605b9c547cSRui Paulo 
4615b9c547cSRui Paulo static int nl80211_scan_filtered(struct wpa_driver_nl80211_data *drv,
4625b9c547cSRui Paulo 				 const u8 *ie, size_t ie_len)
4635b9c547cSRui Paulo {
4645b9c547cSRui Paulo 	const u8 *ssid;
4655b9c547cSRui Paulo 	size_t i;
4665b9c547cSRui Paulo 
4675b9c547cSRui Paulo 	if (drv->filter_ssids == NULL)
4685b9c547cSRui Paulo 		return 0;
4695b9c547cSRui Paulo 
4705b9c547cSRui Paulo 	ssid = nl80211_get_ie(ie, ie_len, WLAN_EID_SSID);
4715b9c547cSRui Paulo 	if (ssid == NULL)
4725b9c547cSRui Paulo 		return 1;
4735b9c547cSRui Paulo 
4745b9c547cSRui Paulo 	for (i = 0; i < drv->num_filter_ssids; i++) {
4755b9c547cSRui Paulo 		if (ssid[1] == drv->filter_ssids[i].ssid_len &&
4765b9c547cSRui Paulo 		    os_memcmp(ssid + 2, drv->filter_ssids[i].ssid, ssid[1]) ==
4775b9c547cSRui Paulo 		    0)
4785b9c547cSRui Paulo 			return 0;
4795b9c547cSRui Paulo 	}
4805b9c547cSRui Paulo 
4815b9c547cSRui Paulo 	return 1;
4825b9c547cSRui Paulo }
4835b9c547cSRui Paulo 
4845b9c547cSRui Paulo 
4855b9c547cSRui Paulo int bss_info_handler(struct nl_msg *msg, void *arg)
4865b9c547cSRui Paulo {
4875b9c547cSRui Paulo 	struct nlattr *tb[NL80211_ATTR_MAX + 1];
4885b9c547cSRui Paulo 	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
4895b9c547cSRui Paulo 	struct nlattr *bss[NL80211_BSS_MAX + 1];
4905b9c547cSRui Paulo 	static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
4915b9c547cSRui Paulo 		[NL80211_BSS_BSSID] = { .type = NLA_UNSPEC },
4925b9c547cSRui Paulo 		[NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
4935b9c547cSRui Paulo 		[NL80211_BSS_TSF] = { .type = NLA_U64 },
4945b9c547cSRui Paulo 		[NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 },
4955b9c547cSRui Paulo 		[NL80211_BSS_CAPABILITY] = { .type = NLA_U16 },
4965b9c547cSRui Paulo 		[NL80211_BSS_INFORMATION_ELEMENTS] = { .type = NLA_UNSPEC },
4975b9c547cSRui Paulo 		[NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 },
4985b9c547cSRui Paulo 		[NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 },
4995b9c547cSRui Paulo 		[NL80211_BSS_STATUS] = { .type = NLA_U32 },
5005b9c547cSRui Paulo 		[NL80211_BSS_SEEN_MS_AGO] = { .type = NLA_U32 },
5015b9c547cSRui Paulo 		[NL80211_BSS_BEACON_IES] = { .type = NLA_UNSPEC },
5025b9c547cSRui Paulo 	};
5035b9c547cSRui Paulo 	struct nl80211_bss_info_arg *_arg = arg;
5045b9c547cSRui Paulo 	struct wpa_scan_results *res = _arg->res;
5055b9c547cSRui Paulo 	struct wpa_scan_res **tmp;
5065b9c547cSRui Paulo 	struct wpa_scan_res *r;
5075b9c547cSRui Paulo 	const u8 *ie, *beacon_ie;
5085b9c547cSRui Paulo 	size_t ie_len, beacon_ie_len;
5095b9c547cSRui Paulo 	u8 *pos;
5105b9c547cSRui Paulo 	size_t i;
5115b9c547cSRui Paulo 
5125b9c547cSRui Paulo 	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
5135b9c547cSRui Paulo 		  genlmsg_attrlen(gnlh, 0), NULL);
5145b9c547cSRui Paulo 	if (!tb[NL80211_ATTR_BSS])
5155b9c547cSRui Paulo 		return NL_SKIP;
5165b9c547cSRui Paulo 	if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS],
5175b9c547cSRui Paulo 			     bss_policy))
5185b9c547cSRui Paulo 		return NL_SKIP;
5195b9c547cSRui Paulo 	if (bss[NL80211_BSS_STATUS]) {
5205b9c547cSRui Paulo 		enum nl80211_bss_status status;
5215b9c547cSRui Paulo 		status = nla_get_u32(bss[NL80211_BSS_STATUS]);
5225b9c547cSRui Paulo 		if (status == NL80211_BSS_STATUS_ASSOCIATED &&
5235b9c547cSRui Paulo 		    bss[NL80211_BSS_FREQUENCY]) {
5245b9c547cSRui Paulo 			_arg->assoc_freq =
5255b9c547cSRui Paulo 				nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
5265b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz",
5275b9c547cSRui Paulo 				   _arg->assoc_freq);
5285b9c547cSRui Paulo 		}
5295b9c547cSRui Paulo 		if (status == NL80211_BSS_STATUS_IBSS_JOINED &&
5305b9c547cSRui Paulo 		    bss[NL80211_BSS_FREQUENCY]) {
5315b9c547cSRui Paulo 			_arg->ibss_freq =
5325b9c547cSRui Paulo 				nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
5335b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "nl80211: IBSS-joined on %u MHz",
5345b9c547cSRui Paulo 				   _arg->ibss_freq);
5355b9c547cSRui Paulo 		}
5365b9c547cSRui Paulo 		if (status == NL80211_BSS_STATUS_ASSOCIATED &&
5375b9c547cSRui Paulo 		    bss[NL80211_BSS_BSSID]) {
5385b9c547cSRui Paulo 			os_memcpy(_arg->assoc_bssid,
5395b9c547cSRui Paulo 				  nla_data(bss[NL80211_BSS_BSSID]), ETH_ALEN);
5405b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "nl80211: Associated with "
5415b9c547cSRui Paulo 				   MACSTR, MAC2STR(_arg->assoc_bssid));
5425b9c547cSRui Paulo 		}
5435b9c547cSRui Paulo 	}
5445b9c547cSRui Paulo 	if (!res)
5455b9c547cSRui Paulo 		return NL_SKIP;
5465b9c547cSRui Paulo 	if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) {
5475b9c547cSRui Paulo 		ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
5485b9c547cSRui Paulo 		ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
5495b9c547cSRui Paulo 	} else {
5505b9c547cSRui Paulo 		ie = NULL;
5515b9c547cSRui Paulo 		ie_len = 0;
5525b9c547cSRui Paulo 	}
5535b9c547cSRui Paulo 	if (bss[NL80211_BSS_BEACON_IES]) {
5545b9c547cSRui Paulo 		beacon_ie = nla_data(bss[NL80211_BSS_BEACON_IES]);
5555b9c547cSRui Paulo 		beacon_ie_len = nla_len(bss[NL80211_BSS_BEACON_IES]);
5565b9c547cSRui Paulo 	} else {
5575b9c547cSRui Paulo 		beacon_ie = NULL;
5585b9c547cSRui Paulo 		beacon_ie_len = 0;
5595b9c547cSRui Paulo 	}
5605b9c547cSRui Paulo 
5615b9c547cSRui Paulo 	if (nl80211_scan_filtered(_arg->drv, ie ? ie : beacon_ie,
5625b9c547cSRui Paulo 				  ie ? ie_len : beacon_ie_len))
5635b9c547cSRui Paulo 		return NL_SKIP;
5645b9c547cSRui Paulo 
5655b9c547cSRui Paulo 	r = os_zalloc(sizeof(*r) + ie_len + beacon_ie_len);
5665b9c547cSRui Paulo 	if (r == NULL)
5675b9c547cSRui Paulo 		return NL_SKIP;
5685b9c547cSRui Paulo 	if (bss[NL80211_BSS_BSSID])
5695b9c547cSRui Paulo 		os_memcpy(r->bssid, nla_data(bss[NL80211_BSS_BSSID]),
5705b9c547cSRui Paulo 			  ETH_ALEN);
5715b9c547cSRui Paulo 	if (bss[NL80211_BSS_FREQUENCY])
5725b9c547cSRui Paulo 		r->freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
5735b9c547cSRui Paulo 	if (bss[NL80211_BSS_BEACON_INTERVAL])
5745b9c547cSRui Paulo 		r->beacon_int = nla_get_u16(bss[NL80211_BSS_BEACON_INTERVAL]);
5755b9c547cSRui Paulo 	if (bss[NL80211_BSS_CAPABILITY])
5765b9c547cSRui Paulo 		r->caps = nla_get_u16(bss[NL80211_BSS_CAPABILITY]);
5775b9c547cSRui Paulo 	r->flags |= WPA_SCAN_NOISE_INVALID;
5785b9c547cSRui Paulo 	if (bss[NL80211_BSS_SIGNAL_MBM]) {
5795b9c547cSRui Paulo 		r->level = nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]);
5805b9c547cSRui Paulo 		r->level /= 100; /* mBm to dBm */
5815b9c547cSRui Paulo 		r->flags |= WPA_SCAN_LEVEL_DBM | WPA_SCAN_QUAL_INVALID;
5825b9c547cSRui Paulo 	} else if (bss[NL80211_BSS_SIGNAL_UNSPEC]) {
5835b9c547cSRui Paulo 		r->level = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]);
5845b9c547cSRui Paulo 		r->flags |= WPA_SCAN_QUAL_INVALID;
5855b9c547cSRui Paulo 	} else
5865b9c547cSRui Paulo 		r->flags |= WPA_SCAN_LEVEL_INVALID | WPA_SCAN_QUAL_INVALID;
5875b9c547cSRui Paulo 	if (bss[NL80211_BSS_TSF])
5885b9c547cSRui Paulo 		r->tsf = nla_get_u64(bss[NL80211_BSS_TSF]);
589*325151a3SRui Paulo 	if (bss[NL80211_BSS_BEACON_TSF]) {
590*325151a3SRui Paulo 		u64 tsf = nla_get_u64(bss[NL80211_BSS_BEACON_TSF]);
591*325151a3SRui Paulo 		if (tsf > r->tsf)
592*325151a3SRui Paulo 			r->tsf = tsf;
593*325151a3SRui Paulo 	}
5945b9c547cSRui Paulo 	if (bss[NL80211_BSS_SEEN_MS_AGO])
5955b9c547cSRui Paulo 		r->age = nla_get_u32(bss[NL80211_BSS_SEEN_MS_AGO]);
5965b9c547cSRui Paulo 	r->ie_len = ie_len;
5975b9c547cSRui Paulo 	pos = (u8 *) (r + 1);
5985b9c547cSRui Paulo 	if (ie) {
5995b9c547cSRui Paulo 		os_memcpy(pos, ie, ie_len);
6005b9c547cSRui Paulo 		pos += ie_len;
6015b9c547cSRui Paulo 	}
6025b9c547cSRui Paulo 	r->beacon_ie_len = beacon_ie_len;
6035b9c547cSRui Paulo 	if (beacon_ie)
6045b9c547cSRui Paulo 		os_memcpy(pos, beacon_ie, beacon_ie_len);
6055b9c547cSRui Paulo 
6065b9c547cSRui Paulo 	if (bss[NL80211_BSS_STATUS]) {
6075b9c547cSRui Paulo 		enum nl80211_bss_status status;
6085b9c547cSRui Paulo 		status = nla_get_u32(bss[NL80211_BSS_STATUS]);
6095b9c547cSRui Paulo 		switch (status) {
6105b9c547cSRui Paulo 		case NL80211_BSS_STATUS_ASSOCIATED:
6115b9c547cSRui Paulo 			r->flags |= WPA_SCAN_ASSOCIATED;
6125b9c547cSRui Paulo 			break;
6135b9c547cSRui Paulo 		default:
6145b9c547cSRui Paulo 			break;
6155b9c547cSRui Paulo 		}
6165b9c547cSRui Paulo 	}
6175b9c547cSRui Paulo 
6185b9c547cSRui Paulo 	/*
6195b9c547cSRui Paulo 	 * cfg80211 maintains separate BSS table entries for APs if the same
6205b9c547cSRui Paulo 	 * BSSID,SSID pair is seen on multiple channels. wpa_supplicant does
6215b9c547cSRui Paulo 	 * not use frequency as a separate key in the BSS table, so filter out
6225b9c547cSRui Paulo 	 * duplicated entries. Prefer associated BSS entry in such a case in
6235b9c547cSRui Paulo 	 * order to get the correct frequency into the BSS table. Similarly,
6245b9c547cSRui Paulo 	 * prefer newer entries over older.
6255b9c547cSRui Paulo 	 */
6265b9c547cSRui Paulo 	for (i = 0; i < res->num; i++) {
6275b9c547cSRui Paulo 		const u8 *s1, *s2;
6285b9c547cSRui Paulo 		if (os_memcmp(res->res[i]->bssid, r->bssid, ETH_ALEN) != 0)
6295b9c547cSRui Paulo 			continue;
6305b9c547cSRui Paulo 
6315b9c547cSRui Paulo 		s1 = nl80211_get_ie((u8 *) (res->res[i] + 1),
6325b9c547cSRui Paulo 				    res->res[i]->ie_len, WLAN_EID_SSID);
6335b9c547cSRui Paulo 		s2 = nl80211_get_ie((u8 *) (r + 1), r->ie_len, WLAN_EID_SSID);
6345b9c547cSRui Paulo 		if (s1 == NULL || s2 == NULL || s1[1] != s2[1] ||
6355b9c547cSRui Paulo 		    os_memcmp(s1, s2, 2 + s1[1]) != 0)
6365b9c547cSRui Paulo 			continue;
6375b9c547cSRui Paulo 
6385b9c547cSRui Paulo 		/* Same BSSID,SSID was already included in scan results */
6395b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "nl80211: Remove duplicated scan result "
6405b9c547cSRui Paulo 			   "for " MACSTR, MAC2STR(r->bssid));
6415b9c547cSRui Paulo 
6425b9c547cSRui Paulo 		if (((r->flags & WPA_SCAN_ASSOCIATED) &&
6435b9c547cSRui Paulo 		     !(res->res[i]->flags & WPA_SCAN_ASSOCIATED)) ||
6445b9c547cSRui Paulo 		    r->age < res->res[i]->age) {
6455b9c547cSRui Paulo 			os_free(res->res[i]);
6465b9c547cSRui Paulo 			res->res[i] = r;
6475b9c547cSRui Paulo 		} else
6485b9c547cSRui Paulo 			os_free(r);
6495b9c547cSRui Paulo 		return NL_SKIP;
6505b9c547cSRui Paulo 	}
6515b9c547cSRui Paulo 
6525b9c547cSRui Paulo 	tmp = os_realloc_array(res->res, res->num + 1,
6535b9c547cSRui Paulo 			       sizeof(struct wpa_scan_res *));
6545b9c547cSRui Paulo 	if (tmp == NULL) {
6555b9c547cSRui Paulo 		os_free(r);
6565b9c547cSRui Paulo 		return NL_SKIP;
6575b9c547cSRui Paulo 	}
6585b9c547cSRui Paulo 	tmp[res->num++] = r;
6595b9c547cSRui Paulo 	res->res = tmp;
6605b9c547cSRui Paulo 
6615b9c547cSRui Paulo 	return NL_SKIP;
6625b9c547cSRui Paulo }
6635b9c547cSRui Paulo 
6645b9c547cSRui Paulo 
6655b9c547cSRui Paulo static void clear_state_mismatch(struct wpa_driver_nl80211_data *drv,
6665b9c547cSRui Paulo 				 const u8 *addr)
6675b9c547cSRui Paulo {
6685b9c547cSRui Paulo 	if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
6695b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "nl80211: Clear possible state "
6705b9c547cSRui Paulo 			   "mismatch (" MACSTR ")", MAC2STR(addr));
6715b9c547cSRui Paulo 		wpa_driver_nl80211_mlme(drv, addr,
6725b9c547cSRui Paulo 					NL80211_CMD_DEAUTHENTICATE,
6735b9c547cSRui Paulo 					WLAN_REASON_PREV_AUTH_NOT_VALID, 1);
6745b9c547cSRui Paulo 	}
6755b9c547cSRui Paulo }
6765b9c547cSRui Paulo 
6775b9c547cSRui Paulo 
6785b9c547cSRui Paulo static void wpa_driver_nl80211_check_bss_status(
6795b9c547cSRui Paulo 	struct wpa_driver_nl80211_data *drv, struct wpa_scan_results *res)
6805b9c547cSRui Paulo {
6815b9c547cSRui Paulo 	size_t i;
6825b9c547cSRui Paulo 
6835b9c547cSRui Paulo 	for (i = 0; i < res->num; i++) {
6845b9c547cSRui Paulo 		struct wpa_scan_res *r = res->res[i];
6855b9c547cSRui Paulo 
6865b9c547cSRui Paulo 		if (r->flags & WPA_SCAN_ASSOCIATED) {
6875b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "nl80211: Scan results "
6885b9c547cSRui Paulo 				   "indicate BSS status with " MACSTR
6895b9c547cSRui Paulo 				   " as associated",
6905b9c547cSRui Paulo 				   MAC2STR(r->bssid));
6915b9c547cSRui Paulo 			if (is_sta_interface(drv->nlmode) &&
6925b9c547cSRui Paulo 			    !drv->associated) {
6935b9c547cSRui Paulo 				wpa_printf(MSG_DEBUG, "nl80211: Local state "
6945b9c547cSRui Paulo 					   "(not associated) does not match "
6955b9c547cSRui Paulo 					   "with BSS state");
6965b9c547cSRui Paulo 				clear_state_mismatch(drv, r->bssid);
6975b9c547cSRui Paulo 			} else if (is_sta_interface(drv->nlmode) &&
6985b9c547cSRui Paulo 				   os_memcmp(drv->bssid, r->bssid, ETH_ALEN) !=
6995b9c547cSRui Paulo 				   0) {
7005b9c547cSRui Paulo 				wpa_printf(MSG_DEBUG, "nl80211: Local state "
7015b9c547cSRui Paulo 					   "(associated with " MACSTR ") does "
7025b9c547cSRui Paulo 					   "not match with BSS state",
7035b9c547cSRui Paulo 					   MAC2STR(drv->bssid));
7045b9c547cSRui Paulo 				clear_state_mismatch(drv, r->bssid);
7055b9c547cSRui Paulo 				clear_state_mismatch(drv, drv->bssid);
7065b9c547cSRui Paulo 			}
7075b9c547cSRui Paulo 		}
7085b9c547cSRui Paulo 	}
7095b9c547cSRui Paulo }
7105b9c547cSRui Paulo 
7115b9c547cSRui Paulo 
7125b9c547cSRui Paulo static struct wpa_scan_results *
7135b9c547cSRui Paulo nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv)
7145b9c547cSRui Paulo {
7155b9c547cSRui Paulo 	struct nl_msg *msg;
7165b9c547cSRui Paulo 	struct wpa_scan_results *res;
7175b9c547cSRui Paulo 	int ret;
7185b9c547cSRui Paulo 	struct nl80211_bss_info_arg arg;
7195b9c547cSRui Paulo 
7205b9c547cSRui Paulo 	res = os_zalloc(sizeof(*res));
7215b9c547cSRui Paulo 	if (res == NULL)
7225b9c547cSRui Paulo 		return NULL;
7235b9c547cSRui Paulo 	if (!(msg = nl80211_cmd_msg(drv->first_bss, NLM_F_DUMP,
7245b9c547cSRui Paulo 				    NL80211_CMD_GET_SCAN))) {
7255b9c547cSRui Paulo 		wpa_scan_results_free(res);
7265b9c547cSRui Paulo 		return NULL;
7275b9c547cSRui Paulo 	}
7285b9c547cSRui Paulo 
7295b9c547cSRui Paulo 	arg.drv = drv;
7305b9c547cSRui Paulo 	arg.res = res;
7315b9c547cSRui Paulo 	ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg);
7325b9c547cSRui Paulo 	if (ret == 0) {
7335b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "nl80211: Received scan results (%lu "
7345b9c547cSRui Paulo 			   "BSSes)", (unsigned long) res->num);
7355b9c547cSRui Paulo 		nl80211_get_noise_for_scan_results(drv, res);
7365b9c547cSRui Paulo 		return res;
7375b9c547cSRui Paulo 	}
7385b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d "
7395b9c547cSRui Paulo 		   "(%s)", ret, strerror(-ret));
7405b9c547cSRui Paulo 	wpa_scan_results_free(res);
7415b9c547cSRui Paulo 	return NULL;
7425b9c547cSRui Paulo }
7435b9c547cSRui Paulo 
7445b9c547cSRui Paulo 
7455b9c547cSRui Paulo /**
7465b9c547cSRui Paulo  * wpa_driver_nl80211_get_scan_results - Fetch the latest scan results
7475b9c547cSRui Paulo  * @priv: Pointer to private wext data from wpa_driver_nl80211_init()
7485b9c547cSRui Paulo  * Returns: Scan results on success, -1 on failure
7495b9c547cSRui Paulo  */
7505b9c547cSRui Paulo struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv)
7515b9c547cSRui Paulo {
7525b9c547cSRui Paulo 	struct i802_bss *bss = priv;
7535b9c547cSRui Paulo 	struct wpa_driver_nl80211_data *drv = bss->drv;
7545b9c547cSRui Paulo 	struct wpa_scan_results *res;
7555b9c547cSRui Paulo 
7565b9c547cSRui Paulo 	res = nl80211_get_scan_results(drv);
7575b9c547cSRui Paulo 	if (res)
7585b9c547cSRui Paulo 		wpa_driver_nl80211_check_bss_status(drv, res);
7595b9c547cSRui Paulo 	return res;
7605b9c547cSRui Paulo }
7615b9c547cSRui Paulo 
7625b9c547cSRui Paulo 
7635b9c547cSRui Paulo void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv)
7645b9c547cSRui Paulo {
7655b9c547cSRui Paulo 	struct wpa_scan_results *res;
7665b9c547cSRui Paulo 	size_t i;
7675b9c547cSRui Paulo 
7685b9c547cSRui Paulo 	res = nl80211_get_scan_results(drv);
7695b9c547cSRui Paulo 	if (res == NULL) {
7705b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "nl80211: Failed to get scan results");
7715b9c547cSRui Paulo 		return;
7725b9c547cSRui Paulo 	}
7735b9c547cSRui Paulo 
7745b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "nl80211: Scan result dump");
7755b9c547cSRui Paulo 	for (i = 0; i < res->num; i++) {
7765b9c547cSRui Paulo 		struct wpa_scan_res *r = res->res[i];
7775b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "nl80211: %d/%d " MACSTR "%s",
7785b9c547cSRui Paulo 			   (int) i, (int) res->num, MAC2STR(r->bssid),
7795b9c547cSRui Paulo 			   r->flags & WPA_SCAN_ASSOCIATED ? " [assoc]" : "");
7805b9c547cSRui Paulo 	}
7815b9c547cSRui Paulo 
7825b9c547cSRui Paulo 	wpa_scan_results_free(res);
7835b9c547cSRui Paulo }
784