Cumulative patch from commit c41d0840a1ae4d755c525b091a4bf9d740efdb5f

c41d084 nl80211: Allow driver-based roam to change ESS
6ba7eba Add OpenSSL 0.9.8zf patch for EAP-FAST support
1de0710 atheros: Clear WPS appie during deinit
857d942 Extend offloaded ACS QCA vendor command to support VHT
0fd52a6 Remove duplicated wpa_s->conf->interworking check
ad44309 Add Extended Capabilities element to all Probe Request frames
9bd566a Delay AP selection if all networks are temporarily disabled
701f396 Don't optimize scan frequencies if selected network has changed
e9d2805 P2PS: Extend p2p_service_del asp to support 'all' parameter
6dd51ec P2PS: Add P2PS advertisements on ALL_SERVICES ANQP query
c40a891 P2PS: Delete ASP advertisements on wpas_p2p_service_flush
2dc422e P2PS: Update SD indicator value on ASP add/del/update
030a3e1 DFS: Fix range availability check
56ef992 DFS: Consider non-contiguous channels
6ceea4c Restart sched_scan on channel list change
e7a296b Remove unused shared_freq driver op
58e115b Fix hlr_auc_gw build with OpenSSL
5f9c92f nl80211: Fix vendor command handling
55e8f0e Fix CONFIG_EAP_UNAUTH_TLS without CONFIG_EAP_TLS build
9772af6 Interworking: Prevent scan during ANQP fetch and Interworking select
2c50246 Add a AP mode event message for possible PSK/passphrase mismatch
6784168 Remove SChannel support

Change-Id: I21078309f83821d4b685de77c517c0886b3366bd
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/src/ap/acs.c b/src/ap/acs.c
index ae7f6c3..652d020 100644
--- a/src/ap/acs.c
+++ b/src/ap/acs.c
@@ -479,16 +479,10 @@
 static int is_in_chanlist(struct hostapd_iface *iface,
 			  struct hostapd_channel_data *chan)
 {
-	int *entry;
-
-	if (!iface->conf->chanlist)
+	if (!iface->conf->acs_ch_list.num)
 		return 1;
 
-	for (entry = iface->conf->chanlist; *entry != -1; entry++) {
-		if (*entry == chan->chan)
-			return 1;
-	}
-	return 0;
+	return freq_range_list_includes(&iface->conf->acs_ch_list, chan->chan);
 }
 
 
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 76011dc..cccbfab 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -181,6 +181,8 @@
 	conf->corrupt_gtk_rekey_mic_probability = 0.0;
 #endif /* CONFIG_TESTING_OPTIONS */
 
+	conf->acs = 0;
+	conf->acs_ch_list.num = 0;
 #ifdef CONFIG_ACS
 	conf->acs_num_scans = 5;
 #endif /* CONFIG_ACS */
@@ -579,7 +581,7 @@
 	os_free(conf->bss);
 	os_free(conf->supported_rates);
 	os_free(conf->basic_rates);
-	os_free(conf->chanlist);
+	os_free(conf->acs_ch_list.range);
 	os_free(conf->driver_params);
 #ifdef CONFIG_ACS
 	os_free(conf->acs_chan_bias);
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 961d2dd..b9d6832 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -568,7 +568,8 @@
 	int fragm_threshold;
 	u8 send_probe_response;
 	u8 channel;
-	int *chanlist;
+	u8 acs;
+	struct wpa_freq_range_list acs_ch_list;
 	enum hostapd_hw_mode hw_mode; /* HOSTAPD_MODE_IEEE80211A, .. */
 	enum {
 		LONG_PREAMBLE = 0,
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index e16306c..9ee88b4 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -217,6 +217,15 @@
 }
 
 
+int hostapd_reset_ap_wps_ie(struct hostapd_data *hapd)
+{
+	if (hapd->driver == NULL || hapd->driver->set_ap_wps_ie == NULL)
+		return 0;
+
+	return hapd->driver->set_ap_wps_ie(hapd->drv_priv, NULL, NULL, NULL);
+}
+
+
 int hostapd_set_ap_wps_ie(struct hostapd_data *hapd)
 {
 	struct wpabuf *beacon, *proberesp, *assocresp;
@@ -715,13 +724,66 @@
 int hostapd_drv_do_acs(struct hostapd_data *hapd)
 {
 	struct drv_acs_params params;
+	int ret, i, acs_ch_list_all = 0;
+	u8 *channels = NULL;
+	unsigned int num_channels = 0;
+	struct hostapd_hw_modes *mode;
 
 	if (hapd->driver == NULL || hapd->driver->do_acs == NULL)
 		return 0;
+
 	os_memset(&params, 0, sizeof(params));
 	params.hw_mode = hapd->iface->conf->hw_mode;
+
+	/*
+	 * If no chanlist config parameter is provided, include all enabled
+	 * channels of the selected hw_mode.
+	 */
+	if (!hapd->iface->conf->acs_ch_list.num)
+		acs_ch_list_all = 1;
+
+	mode = hapd->iface->current_mode;
+	if (mode == NULL)
+		return -1;
+	channels = os_malloc(mode->num_channels);
+	if (channels == NULL)
+		return -1;
+
+	for (i = 0; i < mode->num_channels; i++) {
+		struct hostapd_channel_data *chan = &mode->channels[i];
+		if (!acs_ch_list_all &&
+		    !freq_range_list_includes(&hapd->iface->conf->acs_ch_list,
+					      chan->chan))
+			continue;
+		if (!(chan->flag & HOSTAPD_CHAN_DISABLED))
+			channels[num_channels++] = chan->chan;
+	}
+
+	params.ch_list = channels;
+	params.ch_list_len = num_channels;
+
 	params.ht_enabled = !!(hapd->iface->conf->ieee80211n);
 	params.ht40_enabled = !!(hapd->iface->conf->ht_capab &
 				 HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET);
-	return hapd->driver->do_acs(hapd->drv_priv, &params);
+	params.vht_enabled = !!(hapd->iface->conf->ieee80211ac);
+	params.ch_width = 20;
+	if (hapd->iface->conf->ieee80211n && params.ht40_enabled)
+		params.ch_width = 40;
+
+	/* Note: VHT20 is defined by combination of ht_capab & vht_oper_chwidth
+	 */
+	if (hapd->iface->conf->ieee80211ac && params.ht40_enabled) {
+		if (hapd->iface->conf->vht_oper_chwidth == VHT_CHANWIDTH_80MHZ)
+			params.ch_width = 80;
+		else if (hapd->iface->conf->vht_oper_chwidth ==
+			 VHT_CHANWIDTH_160MHZ ||
+			 hapd->iface->conf->vht_oper_chwidth ==
+			 VHT_CHANWIDTH_80P80MHZ)
+			params.ch_width = 160;
+	}
+
+	ret = hapd->driver->do_acs(hapd->drv_priv, &params);
+	os_free(channels);
+
+	return ret;
 }
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index 5d07e71..82eaf3f 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -24,6 +24,7 @@
 void hostapd_free_ap_extra_ies(struct hostapd_data *hapd, struct wpabuf *beacon,
 			       struct wpabuf *proberesp,
 			       struct wpabuf *assocresp);
+int hostapd_reset_ap_wps_ie(struct hostapd_data *hapd);
 int hostapd_set_ap_wps_ie(struct hostapd_data *hapd);
 int hostapd_set_authorized(struct hostapd_data *hapd,
 			   struct sta_info *sta, int authorized);
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
index da6fd46..715f19b 100644
--- a/src/ap/dfs.c
+++ b/src/ap/dfs.c
@@ -122,6 +122,20 @@
 }
 
 
+static struct hostapd_channel_data *
+dfs_get_chan_data(struct hostapd_hw_modes *mode, int freq, int first_chan_idx)
+{
+	int i;
+
+	for (i = first_chan_idx; i < mode->num_channels; i++) {
+		if (mode->channels[i].freq == freq)
+			return &mode->channels[i];
+	}
+
+	return NULL;
+}
+
+
 static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
 				    int first_chan_idx, int num_chans,
 				    int skip_radar)
@@ -129,15 +143,15 @@
 	struct hostapd_channel_data *first_chan, *chan;
 	int i;
 
-	if (first_chan_idx + num_chans >= mode->num_channels)
+	if (first_chan_idx + num_chans > mode->num_channels)
 		return 0;
 
 	first_chan = &mode->channels[first_chan_idx];
 
 	for (i = 0; i < num_chans; i++) {
-		chan = &mode->channels[first_chan_idx + i];
-
-		if (first_chan->freq + i * 20 != chan->freq)
+		chan = dfs_get_chan_data(mode, first_chan->freq + i * 20,
+					 first_chan_idx);
+		if (!chan)
 			return 0;
 
 		if (!dfs_channel_available(chan, skip_radar))
@@ -151,16 +165,10 @@
 static int is_in_chanlist(struct hostapd_iface *iface,
 			  struct hostapd_channel_data *chan)
 {
-	int *entry;
-
-	if (!iface->conf->chanlist)
+	if (!iface->conf->acs_ch_list.num)
 		return 1;
 
-	for (entry = iface->conf->chanlist; *entry != -1; entry++) {
-		if (*entry == chan->chan)
-			return 1;
-	}
-	return 0;
+	return freq_range_list_includes(&iface->conf->acs_ch_list, chan->chan);
 }
 
 
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index a0adc67..507053e 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -532,9 +532,8 @@
 
 #ifdef CONFIG_ACS
 static void hostapd_acs_channel_selected(struct hostapd_data *hapd,
-					 u8 pri_channel, u8 sec_channel)
+					 struct acs_selected_channels *acs_res)
 {
-	int channel;
 	int ret;
 
 	if (hapd->iconf->channel) {
@@ -543,29 +542,55 @@
 		return;
 	}
 
-	hapd->iface->freq = hostapd_hw_get_freq(hapd, pri_channel);
+	hapd->iface->freq = hostapd_hw_get_freq(hapd, acs_res->pri_channel);
 
-	channel = pri_channel;
-	if (!channel) {
+	if (!acs_res->pri_channel) {
 		hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_WARNING,
 			       "driver switched to bad channel");
 		return;
 	}
 
-	hapd->iconf->channel = channel;
+	hapd->iconf->channel = acs_res->pri_channel;
+	hapd->iconf->acs = 1;
 
-	if (sec_channel == 0)
+	if (acs_res->sec_channel == 0)
 		hapd->iconf->secondary_channel = 0;
-	else if (sec_channel < pri_channel)
+	else if (acs_res->sec_channel < acs_res->pri_channel)
 		hapd->iconf->secondary_channel = -1;
-	else if (sec_channel > pri_channel)
+	else if (acs_res->sec_channel > acs_res->pri_channel)
 		hapd->iconf->secondary_channel = 1;
 	else {
 		wpa_printf(MSG_ERROR, "Invalid secondary channel!");
 		return;
 	}
 
+	if (hapd->iface->conf->ieee80211ac) {
+		/* set defaults for backwards compatibility */
+		hapd->iconf->vht_oper_centr_freq_seg1_idx = 0;
+		hapd->iconf->vht_oper_centr_freq_seg0_idx = 0;
+		hapd->iconf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT;
+		if (acs_res->ch_width == 80) {
+			hapd->iconf->vht_oper_centr_freq_seg0_idx =
+				acs_res->vht_seg0_center_ch;
+			hapd->iconf->vht_oper_chwidth = VHT_CHANWIDTH_80MHZ;
+		} else if (acs_res->ch_width == 160) {
+			if (acs_res->vht_seg1_center_ch == 0) {
+				hapd->iconf->vht_oper_centr_freq_seg0_idx =
+					acs_res->vht_seg0_center_ch;
+				hapd->iconf->vht_oper_chwidth =
+					VHT_CHANWIDTH_160MHZ;
+			} else {
+				hapd->iconf->vht_oper_centr_freq_seg0_idx =
+					acs_res->vht_seg0_center_ch;
+				hapd->iconf->vht_oper_centr_freq_seg1_idx =
+					acs_res->vht_seg1_center_ch;
+				hapd->iconf->vht_oper_chwidth =
+					VHT_CHANWIDTH_80P80MHZ;
+			}
+		}
+	}
+
 	ret = hostapd_acs_completed(hapd->iface, 0);
 	if (ret) {
 		wpa_printf(MSG_ERROR,
@@ -1248,9 +1273,8 @@
 		break;
 #ifdef CONFIG_ACS
 	case EVENT_ACS_CHANNEL_SELECTED:
-		hostapd_acs_channel_selected(
-			hapd, data->acs_selected_channels.pri_channel,
-			data->acs_selected_channels.sec_channel);
+		hostapd_acs_channel_selected(hapd,
+					     &data->acs_selected_channels);
 		break;
 #endif /* CONFIG_ACS */
 	default:
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 3e4e16b..6cdb6d3 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -179,6 +179,7 @@
 		hapd = iface->bss[j];
 		hapd->iconf = newconf;
 		hapd->iconf->channel = oldconf->channel;
+		hapd->iconf->acs = oldconf->acs;
 		hapd->iconf->secondary_channel = oldconf->secondary_channel;
 		hapd->iconf->ieee80211n = oldconf->ieee80211n;
 		hapd->iconf->ieee80211ac = oldconf->ieee80211ac;
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index 05431d3..96744c4 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -510,7 +510,11 @@
 		return 0;
 	}
 
-	if ((conf & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
+	/*
+	 * Driver ACS chosen channel may not be HT40 due to internal driver
+	 * restrictions.
+	 */
+	if (!iface->conf->acs && (conf & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
 	    !(hw & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) {
 		wpa_printf(MSG_ERROR, "Driver does not support configured "
 			   "HT capability [HT40*]");
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 9c5f609..b83b460 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -67,6 +67,14 @@
 }
 
 
+static inline void wpa_auth_psk_failure_report(
+	struct wpa_authenticator *wpa_auth, const u8 *addr)
+{
+	if (wpa_auth->cb.psk_failure_report)
+		wpa_auth->cb.psk_failure_report(wpa_auth->cb.ctx, addr);
+}
+
+
 static inline void wpa_auth_set_eapol(struct wpa_authenticator *wpa_auth,
 				      const u8 *addr, wpa_eapol_variable var,
 				      int value)
@@ -1985,7 +1993,7 @@
 SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
 {
 	struct wpa_ptk PTK;
-	int ok = 0;
+	int ok = 0, psk_found = 0;
 	const u8 *pmk = NULL;
 
 	SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk);
@@ -2001,6 +2009,7 @@
 					       sm->p2p_dev_addr, pmk);
 			if (pmk == NULL)
 				break;
+			psk_found = 1;
 		} else
 			pmk = sm->PMK;
 
@@ -2020,6 +2029,8 @@
 	if (!ok) {
 		wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
 				"invalid MIC in msg 2/4 of 4-Way Handshake");
+		if (psk_found)
+			wpa_auth_psk_failure_report(sm->wpa_auth, sm->addr);
 		return;
 	}
 
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 2788e65..11e745e 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -189,6 +189,7 @@
 		       const char *txt);
 	void (*disconnect)(void *ctx, const u8 *addr, u16 reason);
 	int (*mic_failure_report)(void *ctx, const u8 *addr);
+	void (*psk_failure_report)(void *ctx, const u8 *addr);
 	void (*set_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var,
 			  int value);
 	int (*get_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var);
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 7f83207..d417a72 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -11,6 +11,7 @@
 #include "utils/common.h"
 #include "common/ieee802_11_defs.h"
 #include "common/sae.h"
+#include "common/wpa_ctrl.h"
 #include "eapol_auth/eapol_auth_sm.h"
 #include "eapol_auth/eapol_auth_sm_i.h"
 #include "eap_server/eap.h"
@@ -144,6 +145,14 @@
 }
 
 
+static void hostapd_wpa_auth_psk_failure_report(void *ctx, const u8 *addr)
+{
+	struct hostapd_data *hapd = ctx;
+	wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_POSSIBLE_PSK_MISMATCH MACSTR,
+		MAC2STR(addr));
+}
+
+
 static void hostapd_wpa_auth_set_eapol(void *ctx, const u8 *addr,
 				       wpa_eapol_variable var, int value)
 {
@@ -579,6 +588,7 @@
 	cb.logger = hostapd_wpa_auth_logger;
 	cb.disconnect = hostapd_wpa_auth_disconnect;
 	cb.mic_failure_report = hostapd_wpa_auth_mic_failure_report;
+	cb.psk_failure_report = hostapd_wpa_auth_psk_failure_report;
 	cb.set_eapol = hostapd_wpa_auth_set_eapol;
 	cb.get_eapol = hostapd_wpa_auth_get_eapol;
 	cb.get_psk = hostapd_wpa_auth_get_psk;
diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
index b0e8b0b..7e74829 100644
--- a/src/ap/wps_hostapd.c
+++ b/src/ap/wps_hostapd.c
@@ -856,8 +856,10 @@
 	wpabuf_free(hapd->wps_probe_resp_ie);
 	hapd->wps_probe_resp_ie = NULL;
 
-	if (deinit_only)
+	if (deinit_only) {
+		hostapd_reset_ap_wps_ie(hapd);
 		return;
+	}
 
 	hostapd_set_ap_wps_ie(hapd);
 }