Cumulative patch from commit 853b49a030c00fd6b2dde14e183ca2bf108eaa16

853b49a tests: Increase test_ap_wps_init connection timeout
28de68a P2P: Update peer operating channel from GO Negotiation Confirm
6701fdc P2P: Use the first pref_chan entry as operating channel preference
99d7c76 P2P: Add more debug info on operating channel selection
8d660e0 P2P: Add GO negotiation results into the P2P-GO-NEG-SUCCESS event
2c6f8cf Replace perror() with wpa_printf(strerror) in ctrl_iface calls
e743db4 IBSS RSN: Add IBSS-RSN-COMPLETED event message
4c55901 P2P: Add state info to global STATUS command
ae8c27f Add STATUS command to global control interface
42868f1 Add SAVE_CONFIG command to global control interface
1b9b31c Add SET command for global control interface
0185007 hostapd: Add survey dump support
245e026 hostapd: Split up channel checking into helpers
ba873bd wired: Wait for the link to become active before sending packets
d393de1 P2P: Validate the freq in p2p_group_add
973622c wpa_supplicant: Fix AP mode frequency initialization
d99ca89 P2P: Skip non-P2P interface in p2p_group_remove *
239abaf WPS: Set currently used RF band in RF Bands attribute
bf83eab nl80211: Start P2P Device when rfkill is unblocked
60b13c2 nl80211: Do not change type to station on P2P interfaces
e0591c3 wpa_supplicant: Reduce wait time for control interfaces
5046eb4 P2P: Allow separate interface GO to disconnect low-ack STAs
5bcd5c5 FT RRB: Clear pad field to avoid sending out uninitialized data
b378c41 nl80211: Fix deinit path to unregister nl_mgmt socket
a235aca Fix DETACH command debug prints to avoid use of freed memory
8d6e035 Make global UNIX socket non-blocking for ctrl_iface
86bd141 Change WEP network selection to reject WPA/WPA2 APs
2e145e9 WPS: Fix failure path to allow WSC_NACK and EAP-Failure to be exchanged
3351a38 WPS: Add control interface command for fetching latest status
e96872a WPS: Track peer MAC address from the last operations
ae23935 WPS: Track PBC status
61b6520 WPS: Track result of the latest WPS operation
50396e2 WPS: Add PBC mode activated/disabled events
961750c WPS: Share a common function for error strings
30158a0 nl80211: Update the assoc_freq during connect
83e7bb0 nl80211: Add more debug prints for DEL_STATION commands

Bug: 9056601

Change-Id: I8bc671eb13f4c2c388a4c15cf1ba968c24c9656a
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 2b364c1..a3ff189 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -197,6 +197,7 @@
 	u8 addr[ETH_ALEN];
 
 	int freq;
+	int if_dynamic;
 
 	void *ctx;
 	struct nl_handle *nl_preq, *nl_mgmt;
@@ -364,6 +365,8 @@
 static int wpa_driver_nl80211_authenticate_retry(
 	struct wpa_driver_nl80211_data *drv);
 
+static int i802_set_iface_flags(struct i802_bss *bss, int up);
+
 
 static const char * nl80211_command_to_string(enum nl80211_commands cmd)
 {
@@ -1294,7 +1297,9 @@
 		wpa_printf(MSG_DEBUG, "nl80211: Operating frequency for the "
 			   "associated BSS from scan results: %u MHz",
 			   arg.assoc_freq);
-		return arg.assoc_freq ? arg.assoc_freq : drv->assoc_freq;
+		if (arg.assoc_freq)
+			drv->assoc_freq = arg.assoc_freq;
+		return drv->assoc_freq;
 	}
 	wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d "
 		   "(%s)", ret, strerror(-ret));
@@ -3515,8 +3520,7 @@
 {
 	struct wpa_driver_nl80211_data *drv = ctx;
 	wpa_printf(MSG_DEBUG, "nl80211: RFKILL unblocked");
-	if (linux_set_iface_flags(drv->global->ioctl_sock,
-				  drv->first_bss.ifname, 1)) {
+	if (i802_set_iface_flags(&drv->first_bss, 1)) {
 		wpa_printf(MSG_DEBUG, "nl80211: Could not set interface UP "
 			   "after rfkill unblock");
 		return;
@@ -4034,15 +4038,14 @@
 #endif /* HOSTAPD */
 	struct i802_bss *bss = &drv->first_bss;
 	int send_rfkill_event = 0;
-	int dynamic_if;
 
 	drv->ifindex = if_nametoindex(bss->ifname);
 	bss->ifindex = drv->ifindex;
 	bss->wdev_id = drv->global->if_add_wdevid;
 	bss->wdev_id_set = drv->global->if_add_wdevid_set;
 
-	dynamic_if = drv->ifindex == drv->global->if_add_ifindex;
-	dynamic_if = dynamic_if || drv->global->if_add_wdevid_set;
+	bss->if_dynamic = drv->ifindex == drv->global->if_add_ifindex;
+	bss->if_dynamic = bss->if_dynamic || drv->global->if_add_wdevid_set;
 	drv->global->if_add_wdevid_set = 0;
 
 	if (wpa_driver_nl80211_capa(drv))
@@ -4052,7 +4055,7 @@
 		   bss->ifname, drv->phyname);
 
 #ifndef HOSTAPD
-	if (dynamic_if)
+	if (bss->if_dynamic)
 		nlmode = nl80211_get_ifmode(bss);
 
 	/*
@@ -4191,6 +4194,7 @@
 	(void) i802_set_iface_flags(bss, 0);
 	if (drv->nlmode != NL80211_IFTYPE_P2P_DEVICE) {
 		wpa_driver_nl80211_set_mode(bss, NL80211_IFTYPE_STATION);
+		nl80211_mgmt_unsubscribe(bss, "deinit");
 	} else {
 		nl80211_mgmt_unsubscribe(bss, "deinit");
 		nl80211_del_p2pdev(bss);
@@ -6710,6 +6714,9 @@
 	NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
 
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	wpa_printf(MSG_DEBUG, "nl80211: sta_remove -> DEL_STATION %s " MACSTR
+		   " --> %d (%s)",
+		   bss->ifname, MAC2STR(addr), ret, strerror(-ret));
 	if (ret == -ENOENT)
 		return 0;
 	return ret;
@@ -7669,7 +7676,9 @@
 	if (params->freq) {
 		wpa_printf(MSG_DEBUG, "  * freq=%d", params->freq);
 		NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq);
-	}
+		drv->assoc_freq = params->freq;
+	} else
+		drv->assoc_freq = 0;
 	if (params->bg_scan_period >= 0) {
 		wpa_printf(MSG_DEBUG, "  * bg scan period=%d",
 			   params->bg_scan_period);
@@ -8401,6 +8410,8 @@
 	if (!msg)
 		return -1;
 
+	wpa_printf(MSG_DEBUG, "nl80211: flush -> DEL_STATION %s (all)",
+		   bss->ifname);
 	nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_STATION);
 
 	/*
@@ -9567,6 +9578,14 @@
 	if (!is_ap_interface(drv->nlmode))
 		return -1;
 	wpa_driver_nl80211_del_beacon(drv);
+
+	/*
+	 * If the P2P GO interface was dynamically added, then it is
+	 * possible that the interface change to station is not possible.
+	 */
+	if (drv->nlmode == NL80211_IFTYPE_P2P_GO && bss->if_dynamic)
+		return 0;
+
 	return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION);
 }
 
@@ -9589,6 +9608,14 @@
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	if (drv->nlmode != NL80211_IFTYPE_P2P_CLIENT)
 		return -1;
+
+	/*
+	 * If the P2P Client interface was dynamically added, then it is
+	 * possible that the interface change to station is not possible.
+	 */
+	if (bss->if_dynamic)
+		return 0;
+
 	return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION);
 }
 
@@ -9995,6 +10022,185 @@
 }
 
 
+static void clean_survey_results(struct survey_results *survey_results)
+{
+	struct freq_survey *survey, *tmp;
+
+	if (dl_list_empty(&survey_results->survey_list))
+		return;
+
+	dl_list_for_each_safe(survey, tmp, &survey_results->survey_list,
+			      struct freq_survey, list) {
+		dl_list_del(&survey->list);
+		os_free(survey);
+	}
+}
+
+
+static void add_survey(struct nlattr **sinfo, u32 ifidx,
+		       struct dl_list *survey_list)
+{
+	struct freq_survey *survey;
+
+	survey = os_zalloc(sizeof(struct freq_survey));
+	if  (!survey)
+		return;
+
+	survey->ifidx = ifidx;
+	survey->freq = nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]);
+	survey->filled = 0;
+
+	if (sinfo[NL80211_SURVEY_INFO_NOISE]) {
+		survey->nf = (int8_t)
+			nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]);
+		survey->filled |= SURVEY_HAS_NF;
+	}
+
+	if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME]) {
+		survey->channel_time =
+			nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME]);
+		survey->filled |= SURVEY_HAS_CHAN_TIME;
+	}
+
+	if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY]) {
+		survey->channel_time_busy =
+			nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY]);
+		survey->filled |= SURVEY_HAS_CHAN_TIME_BUSY;
+	}
+
+	if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_RX]) {
+		survey->channel_time_rx =
+			nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_RX]);
+		survey->filled |= SURVEY_HAS_CHAN_TIME_RX;
+	}
+
+	if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_TX]) {
+		survey->channel_time_tx =
+			nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_TX]);
+		survey->filled |= SURVEY_HAS_CHAN_TIME_TX;
+	}
+
+	wpa_printf(MSG_DEBUG, "nl80211: Freq survey dump event (freq=%d MHz noise=%d channel_time=%ld busy_time=%ld tx_time=%ld rx_time=%ld filled=%04x)",
+		   survey->freq,
+		   survey->nf,
+		   (unsigned long int) survey->channel_time,
+		   (unsigned long int) survey->channel_time_busy,
+		   (unsigned long int) survey->channel_time_tx,
+		   (unsigned long int) survey->channel_time_rx,
+		   survey->filled);
+
+	dl_list_add_tail(survey_list, &survey->list);
+}
+
+
+static int check_survey_ok(struct nlattr **sinfo, u32 surveyed_freq,
+			   unsigned int freq_filter)
+{
+	if (!freq_filter)
+		return 1;
+
+	return freq_filter == surveyed_freq;
+}
+
+
+static int survey_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1];
+	struct survey_results *survey_results;
+	u32 surveyed_freq = 0;
+	u32 ifidx;
+
+	static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = {
+		[NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 },
+		[NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 },
+	};
+
+	survey_results = (struct survey_results *) arg;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
+
+	if (!tb[NL80211_ATTR_SURVEY_INFO])
+		return NL_SKIP;
+
+	if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX,
+			     tb[NL80211_ATTR_SURVEY_INFO],
+			     survey_policy))
+		return NL_SKIP;
+
+	if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY]) {
+		wpa_printf(MSG_ERROR, "nl80211: Invalid survey data");
+		return NL_SKIP;
+	}
+
+	surveyed_freq = nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]);
+
+	if (!check_survey_ok(sinfo, surveyed_freq,
+			     survey_results->freq_filter))
+		return NL_SKIP;
+
+	if (survey_results->freq_filter &&
+	    survey_results->freq_filter != surveyed_freq) {
+		wpa_printf(MSG_EXCESSIVE, "nl80211: Ignoring survey data for freq %d MHz",
+			   surveyed_freq);
+		return NL_SKIP;
+	}
+
+	add_survey(sinfo, ifidx, &survey_results->survey_list);
+
+	return NL_SKIP;
+}
+
+
+static int wpa_driver_nl80211_get_survey(void *priv, unsigned int freq)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int err = -ENOBUFS;
+	union wpa_event_data data;
+	struct survey_results *survey_results;
+
+	os_memset(&data, 0, sizeof(data));
+	survey_results = &data.survey_results;
+
+	dl_list_init(&survey_results->survey_list);
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		goto nla_put_failure;
+
+	nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
+
+	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+	if (freq)
+		data.survey_results.freq_filter = freq;
+
+	do {
+		wpa_printf(MSG_DEBUG, "nl80211: Fetch survey data");
+		err = send_and_recv_msgs(drv, msg, survey_handler,
+					 survey_results);
+	} while (err > 0);
+
+	if (err) {
+		wpa_printf(MSG_ERROR, "nl80211: Failed to process survey data");
+		goto out_clean;
+	}
+
+	wpa_supplicant_event(drv->ctx, EVENT_SURVEY, &data);
+
+out_clean:
+	clean_survey_results(survey_results);
+nla_put_failure:
+	return err;
+}
+
+
 static void nl80211_set_rekey_info(void *priv, const u8 *kek, const u8 *kck,
 				   const u8 *replay_ctr)
 {
@@ -10625,6 +10831,7 @@
 #endif /* CONFIG_TDLS */
 	.update_ft_ies = wpa_driver_nl80211_update_ft_ies,
 	.get_mac_addr = wpa_driver_nl80211_get_macaddr,
+	.get_survey = wpa_driver_nl80211_get_survey,
 #ifdef ANDROID_P2P
 	.set_noa = wpa_driver_set_p2p_noa,
 	.get_noa = wpa_driver_get_p2p_noa,