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/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index cfc30ce..23277b6 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -225,4 +225,14 @@
 	hapd->driver->poll_client(hapd->drv_priv, own_addr, addr, qos);
 }
 
+static inline int hostapd_drv_get_survey(struct hostapd_data *hapd,
+					 unsigned int freq)
+{
+	if (hapd->driver == NULL)
+		return -1;
+	if (!hapd->driver->get_survey)
+		return -1;
+	return hapd->driver->get_survey(hapd->drv_priv, freq);
+}
+
 #endif /* AP_DRV_OPS */
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index fa4b5e4..e3ba224 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -715,6 +715,72 @@
 }
 
 
+static struct hostapd_channel_data * hostapd_get_mode_channel(
+	struct hostapd_iface *iface, unsigned int freq)
+{
+	int i;
+	struct hostapd_channel_data *chan;
+
+	for (i = 0; i < iface->current_mode->num_channels; i++) {
+		chan = &iface->current_mode->channels[i];
+		if (!chan)
+			return NULL;
+		if ((unsigned int) chan->freq == freq)
+			return chan;
+	}
+
+	return NULL;
+}
+
+
+static void hostapd_update_nf(struct hostapd_iface *iface,
+			      struct hostapd_channel_data *chan,
+			      struct freq_survey *survey)
+{
+	if (!iface->chans_surveyed) {
+		chan->min_nf = survey->nf;
+		iface->lowest_nf = survey->nf;
+	} else {
+		if (dl_list_empty(&chan->survey_list))
+			chan->min_nf = survey->nf;
+		else if (survey->nf < chan->min_nf)
+			chan->min_nf = survey->nf;
+		if (survey->nf < iface->lowest_nf)
+			iface->lowest_nf = survey->nf;
+	}
+}
+
+
+static void hostapd_event_get_survey(struct hostapd_data *hapd,
+				     struct survey_results *survey_results)
+{
+	struct hostapd_iface *iface = hapd->iface;
+	struct freq_survey *survey, *tmp;
+	struct hostapd_channel_data *chan;
+
+	if (dl_list_empty(&survey_results->survey_list)) {
+		wpa_printf(MSG_DEBUG, "No survey data received");
+		return;
+	}
+
+	dl_list_for_each_safe(survey, tmp, &survey_results->survey_list,
+			      struct freq_survey, list) {
+		chan = hostapd_get_mode_channel(iface, survey->freq);
+		if (!chan)
+			continue;
+		if (chan->flag & HOSTAPD_CHAN_DISABLED)
+			continue;
+
+		dl_list_del(&survey->list);
+		dl_list_add_tail(&chan->survey_list, &survey->list);
+
+		hostapd_update_nf(iface, chan, survey);
+
+		iface->chans_surveyed++;
+	}
+}
+
+
 void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 			  union wpa_event_data *data)
 {
@@ -856,6 +922,9 @@
 			hapd, data->connect_failed_reason.addr,
 			data->connect_failed_reason.code);
 		break;
+	case EVENT_SURVEY:
+		hostapd_event_get_survey(hapd, &data->survey_results);
+		break;
 	default:
 		wpa_printf(MSG_DEBUG, "Unknown event %d", event);
 		break;
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 75d9c66..673dd34 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -47,6 +47,11 @@
 	struct hostapd_dynamic_iface **dynamic_iface;
 };
 
+enum hostapd_chan_status {
+	HOSTAPD_CHAN_VALID = 0, /* channel is ready */
+	HOSTAPD_CHAN_INVALID = 1, /* no usable channel found */
+	HOSTAPD_CHAN_ACS = 2, /* ACS work being performed */
+};
 
 struct hostapd_probereq_cb {
 	int (*cb)(void *ctx, const u8 *sa, const u8 *da, const u8 *bssid,
@@ -67,6 +72,25 @@
 	int ssi_signal; /* dBm */
 };
 
+enum wps_status {
+	WPS_STATUS_SUCCESS = 1,
+	WPS_STATUS_FAILURE
+};
+
+enum pbc_status {
+	WPS_PBC_STATUS_DISABLE,
+	WPS_PBC_STATUS_ACTIVE,
+	WPS_PBC_STATUS_TIMEOUT,
+	WPS_PBC_STATUS_OVERLAP
+};
+
+struct wps_stat {
+	enum wps_status status;
+	enum wps_error_indication failure_reason;
+	enum pbc_status pbc_status;
+	u8 peer_addr[ETH_ALEN];
+};
+
 
 /**
  * struct hostapd_data - hostapd per-BSS data structure
@@ -147,6 +171,8 @@
 	unsigned int ap_pin_failures_consecutive;
 	struct upnp_wps_device_sm *wps_upnp;
 	unsigned int ap_pin_lockout_time;
+
+	struct wps_stat wps_stats;
 #endif /* CONFIG_WPS */
 
 	struct hostapd_probereq_cb *probereq_cb;
@@ -275,6 +301,15 @@
 	int olbc_ht;
 
 	u16 ht_op_mode;
+
+	/* surveying helpers */
+
+	/* number of channels surveyed */
+	unsigned int chans_surveyed;
+
+	/* lowest observed noise floor in dBm */
+	s8 lowest_nf;
+
 	void (*scan_cb)(struct hostapd_iface *iface);
 };
 
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index 37112bd..bf0dd95 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -634,6 +634,81 @@
 }
 
 
+static int hostapd_is_usable_chan(struct hostapd_iface *iface,
+				  int channel, int primary)
+{
+	int i;
+	struct hostapd_channel_data *chan;
+
+	for (i = 0; i < iface->current_mode->num_channels; i++) {
+		chan = &iface->current_mode->channels[i];
+		if (chan->chan != channel)
+			continue;
+
+		if (!(chan->flag & HOSTAPD_CHAN_DISABLED))
+			return 1;
+
+		wpa_printf(MSG_DEBUG,
+			   "%schannel [%i] (%i) is disabled for use in AP mode, flags: 0x%x%s%s%s",
+			   primary ? "" : "Configured HT40 secondary ",
+			   i, chan->chan, chan->flag,
+			   chan->flag & HOSTAPD_CHAN_NO_IBSS ? " NO-IBSS" : "",
+			   chan->flag & HOSTAPD_CHAN_PASSIVE_SCAN ?
+			   " PASSIVE-SCAN" : "",
+			   chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : "");
+	}
+
+	return 0;
+}
+
+
+static int hostapd_is_usable_chans(struct hostapd_iface *iface)
+{
+	if (!hostapd_is_usable_chan(iface, iface->conf->channel, 1))
+		return 0;
+
+	if (!iface->conf->secondary_channel)
+		return 1;
+
+	return hostapd_is_usable_chan(iface, iface->conf->channel +
+				      iface->conf->secondary_channel * 4, 0);
+}
+
+
+static enum hostapd_chan_status
+hostapd_check_chans(struct hostapd_iface *iface)
+{
+	if (iface->conf->channel) {
+		if (hostapd_is_usable_chans(iface))
+			return HOSTAPD_CHAN_VALID;
+		else
+			return HOSTAPD_CHAN_INVALID;
+	}
+
+	/*
+	 * The user set channel=0 which is used to trigger ACS,
+	 * which we do not yet support.
+	 */
+	return HOSTAPD_CHAN_INVALID;
+}
+
+
+static void hostapd_notify_bad_chans(struct hostapd_iface *iface)
+{
+	hostapd_logger(iface->bss[0], NULL,
+		       HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_WARNING,
+		       "Configured channel (%d) not found from the "
+		       "channel list of current mode (%d) %s",
+		       iface->conf->channel,
+		       iface->current_mode->mode,
+		       hostapd_hw_mode_txt(iface->current_mode->mode));
+	hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_WARNING,
+		       "Hardware does not support configured channel");
+}
+
+
 /**
  * hostapd_select_hw_mode - Select the hardware mode
  * @iface: Pointer to interface data.
@@ -644,7 +719,7 @@
  */
 int hostapd_select_hw_mode(struct hostapd_iface *iface)
 {
-	int i, j, ok;
+	int i;
 
 	if (iface->num_hw_features < 1)
 		return -1;
@@ -669,80 +744,15 @@
 		return -2;
 	}
 
-	ok = 0;
-	for (j = 0; j < iface->current_mode->num_channels; j++) {
-		struct hostapd_channel_data *chan =
-			&iface->current_mode->channels[j];
-		if (chan->chan == iface->conf->channel) {
-			if (chan->flag & HOSTAPD_CHAN_DISABLED) {
-				wpa_printf(MSG_ERROR,
-					   "channel [%i] (%i) is disabled for "
-					   "use in AP mode, flags: 0x%x%s%s%s",
-					   j, chan->chan, chan->flag,
-					   chan->flag & HOSTAPD_CHAN_NO_IBSS ?
-					   " NO-IBSS" : "",
-					   chan->flag &
-					   HOSTAPD_CHAN_PASSIVE_SCAN ?
-					   " PASSIVE-SCAN" : "",
-					   chan->flag & HOSTAPD_CHAN_RADAR ?
-					   " RADAR" : "");
-			} else {
-				ok = 1;
-				break;
-			}
-		}
-	}
-	if (ok && iface->conf->secondary_channel) {
-		int sec_ok = 0;
-		int sec_chan = iface->conf->channel +
-			iface->conf->secondary_channel * 4;
-		for (j = 0; j < iface->current_mode->num_channels; j++) {
-			struct hostapd_channel_data *chan =
-				&iface->current_mode->channels[j];
-			if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
-			    (chan->chan == sec_chan)) {
-				sec_ok = 1;
-				break;
-			}
-		}
-		if (!sec_ok) {
-			hostapd_logger(iface->bss[0], NULL,
-				       HOSTAPD_MODULE_IEEE80211,
-				       HOSTAPD_LEVEL_WARNING,
-				       "Configured HT40 secondary channel "
-				       "(%d) not found from the channel list "
-				       "of current mode (%d) %s",
-				       sec_chan, iface->current_mode->mode,
-				       hostapd_hw_mode_txt(
-					       iface->current_mode->mode));
-			ok = 0;
-		}
-	}
-	if (iface->conf->channel == 0) {
-		/* TODO: could request a scan of neighboring BSSes and select
-		 * the channel automatically */
-		wpa_printf(MSG_ERROR, "Channel not configured "
-			   "(hw_mode/channel in hostapd.conf)");
+	switch (hostapd_check_chans(iface)) {
+	case HOSTAPD_CHAN_VALID:
+		return 0;
+	case HOSTAPD_CHAN_ACS: /* ACS not supported yet */
+	case HOSTAPD_CHAN_INVALID:
+	default:
+		hostapd_notify_bad_chans(iface);
 		return -3;
 	}
-	if (ok == 0 && iface->conf->channel != 0) {
-		hostapd_logger(iface->bss[0], NULL,
-			       HOSTAPD_MODULE_IEEE80211,
-			       HOSTAPD_LEVEL_WARNING,
-			       "Configured channel (%d) not found from the "
-			       "channel list of current mode (%d) %s",
-			       iface->conf->channel,
-			       iface->current_mode->mode,
-			       hostapd_hw_mode_txt(iface->current_mode->mode));
-		iface->current_mode = NULL;
-	}
-
-	if (iface->current_mode == NULL) {
-		hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
-			       HOSTAPD_LEVEL_WARNING,
-			       "Hardware does not support configured channel");
-		return -4;
-	}
 
 	return 0;
 }
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index 1bb5d97..29d9d29 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -329,6 +329,7 @@
 	os_memcpy(f.pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN);
 	os_memcpy(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN);
 	os_memcpy(f.s1kh_id, s1kh_id, ETH_ALEN);
+	os_memset(f.pad, 0, sizeof(f.pad));
 
 	if (aes_wrap(r0kh->key, (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8,
 		     f.nonce, frame.nonce) < 0)
@@ -1317,6 +1318,7 @@
 	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", r.pmk_r1_name,
 		    WPA_PMK_NAME_LEN);
 	r.pairwise = host_to_le16(pairwise);
+	os_memset(r.pad, 0, sizeof(r.pad));
 
 	if (aes_wrap(r1kh->key, (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8,
 		     r.nonce, resp.nonce) < 0) {
@@ -1620,6 +1622,7 @@
 	os_get_time(&now);
 	WPA_PUT_LE32(f.timestamp, now.sec);
 	f.pairwise = host_to_le16(pairwise);
+	os_memset(f.pad, 0, sizeof(f.pad));
 	if (aes_wrap(r1kh->key, (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8,
 		     f.timestamp, frame.timestamp) < 0)
 		return;
diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
index 69b34fe..3304c06 100644
--- a/src/ap/wps_hostapd.c
+++ b/src/ap/wps_hostapd.c
@@ -707,6 +707,12 @@
 static void hostapd_pwd_auth_fail(struct hostapd_data *hapd,
 				  struct wps_event_pwd_auth_fail *data)
 {
+	/* Update WPS Status - Authentication Failure */
+	wpa_printf(MSG_DEBUG, "WPS: Authentication failure update");
+	hapd->wps_stats.status = WPS_STATUS_FAILURE;
+	hapd->wps_stats.failure_reason = WPS_EI_AUTH_FAILURE;
+	os_memcpy(hapd->wps_stats.peer_addr, data->peer_macaddr, ETH_ALEN);
+
 	hostapd_wps_for_each(hapd, wps_pwd_auth_fail, data);
 }
 
@@ -734,21 +740,59 @@
 }
 
 
-static const char * wps_event_fail_reason[NUM_WPS_EI_VALUES] = {
-	"No Error", /* WPS_EI_NO_ERROR */
-	"TKIP Only Prohibited", /* WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED */
-	"WEP Prohibited" /* WPS_EI_SECURITY_WEP_PROHIBITED */
-};
+static void hostapd_wps_event_pbc_overlap(struct hostapd_data *hapd)
+{
+	/* Update WPS Status - PBC Overlap */
+	hapd->wps_stats.pbc_status = WPS_PBC_STATUS_OVERLAP;
+}
+
+
+static void hostapd_wps_event_pbc_timeout(struct hostapd_data *hapd)
+{
+	/* Update WPS PBC Status:PBC Timeout */
+	hapd->wps_stats.pbc_status = WPS_PBC_STATUS_TIMEOUT;
+}
+
+
+static void hostapd_wps_event_pbc_active(struct hostapd_data *hapd)
+{
+	/* Update WPS PBC status - Active */
+	hapd->wps_stats.pbc_status = WPS_PBC_STATUS_ACTIVE;
+}
+
+
+static void hostapd_wps_event_pbc_disable(struct hostapd_data *hapd)
+{
+	/* Update WPS PBC status - Active */
+	hapd->wps_stats.pbc_status = WPS_PBC_STATUS_DISABLE;
+}
+
+
+static void hostapd_wps_event_success(struct hostapd_data *hapd,
+				      struct wps_event_success *success)
+{
+	/* Update WPS status - Success */
+	hapd->wps_stats.pbc_status = WPS_PBC_STATUS_DISABLE;
+	hapd->wps_stats.status = WPS_STATUS_SUCCESS;
+	os_memcpy(hapd->wps_stats.peer_addr, success->peer_macaddr, ETH_ALEN);
+}
+
 
 static void hostapd_wps_event_fail(struct hostapd_data *hapd,
 				   struct wps_event_fail *fail)
 {
+	/* Update WPS status - Failure */
+	hapd->wps_stats.status = WPS_STATUS_FAILURE;
+	os_memcpy(hapd->wps_stats.peer_addr, fail->peer_macaddr, ETH_ALEN);
+
+	hapd->wps_stats.failure_reason = fail->error_indication;
+
 	if (fail->error_indication > 0 &&
 	    fail->error_indication < NUM_WPS_EI_VALUES) {
 		wpa_msg(hapd->msg_ctx, MSG_INFO,
 			WPS_EVENT_FAIL "msg=%d config_error=%d reason=%d (%s)",
 			fail->msg, fail->config_error, fail->error_indication,
-			wps_event_fail_reason[fail->error_indication]);
+			wps_ei_str(fail->error_indication));
 	} else {
 		wpa_msg(hapd->msg_ctx, MSG_INFO,
 			WPS_EVENT_FAIL "msg=%d config_error=%d",
@@ -770,17 +814,28 @@
 		hostapd_wps_event_fail(hapd, &data->fail);
 		break;
 	case WPS_EV_SUCCESS:
+		hostapd_wps_event_success(hapd, &data->success);
 		wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_SUCCESS);
 		break;
 	case WPS_EV_PWD_AUTH_FAIL:
 		hostapd_pwd_auth_fail(hapd, &data->pwd_auth_fail);
 		break;
 	case WPS_EV_PBC_OVERLAP:
+		hostapd_wps_event_pbc_overlap(hapd);
 		wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_OVERLAP);
 		break;
 	case WPS_EV_PBC_TIMEOUT:
+		hostapd_wps_event_pbc_timeout(hapd);
 		wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_TIMEOUT);
 		break;
+	case WPS_EV_PBC_ACTIVE:
+		hostapd_wps_event_pbc_active(hapd);
+		wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_ACTIVE);
+		break;
+	case WPS_EV_PBC_DISABLE:
+		hostapd_wps_event_pbc_disable(hapd);
+		wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_DISABLE);
+		break;
 	case WPS_EV_ER_AP_ADD:
 		break;
 	case WPS_EV_ER_AP_REMOVE:
@@ -802,6 +857,15 @@
 }
 
 
+static int hostapd_wps_rf_band_cb(void *ctx)
+{
+	struct hostapd_data *hapd = ctx;
+
+	return hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ?
+		WPS_RF_50GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */
+}
+
+
 static void hostapd_wps_clear_ies(struct hostapd_data *hapd)
 {
 	wpabuf_free(hapd->wps_beacon_ie);
@@ -909,6 +973,7 @@
 
 	wps->cred_cb = hostapd_wps_cred_cb;
 	wps->event_cb = hostapd_wps_event_cb;
+	wps->rf_band_cb = hostapd_wps_rf_band_cb;
 	wps->cb_ctx = hapd;
 
 	os_memset(&cfg, 0, sizeof(cfg));