Accumulative patch from commit 6d28fb9627155929012cda944aacd5a52ce7051a

nl80211: Fetch assoc_freq from scan table for connect event
nl80211: Filter out duplicated BSS table entries from scan results
Update BSS table entry if roaming event indicates frequency change
WPS: Remove obsolete note about lack for WPS ER support
P2P: Include operating class 124 (channels 149,153,157,161)
Include nl80211 driver wrapper in default configuration for hostapd
Better messages when channel cannot be used in AP mode
WPS: Add a workaround for Windows 7 capability discovery for PBC
WPS UPnP: Fix UPnP initialization for non-bridge case with some drivers
Fix regression in RSN pre-authentication candidate list generation
  commit 6d28fb9627155929012cda944aacd5a52ce7051a

Change-Id: I3c68dad5fe323b1d86aa585c564a75e4fc1a2ea1
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 25720b8..0a3e76e 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -318,6 +318,7 @@
 	char *upc;
 	struct wpabuf *wps_vendor_ext[MAX_WPS_VENDOR_EXTENSIONS];
 #endif /* CONFIG_WPS */
+	int pbc_in_m1;
 
 #define P2P_ENABLED BIT(0)
 #define P2P_GROUP_OWNER BIT(1)
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 4e5eb01..32db668 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -784,6 +784,17 @@
 		return -1;
 	}
 
+	/*
+	 * WPS UPnP module can be initialized only when the "upnp_iface" is up.
+	 * If "interface" and "upnp_iface" are the same (e.g., non-bridge
+	 * mode), the interface is up only after driver_commit, so initialize
+	 * WPS after driver_commit.
+	 */
+	for (j = 0; j < iface->num_bss; j++) {
+		if (hostapd_init_wps_complete(iface->bss[j]))
+			return -1;
+	}
+
 	if (hapd->setup_complete_cb)
 		hapd->setup_complete_cb(hapd->setup_complete_cb_ctx);
 
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index f3fffd7..86f6811 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -603,7 +603,7 @@
 /**
  * hostapd_select_hw_mode - Select the hardware mode
  * @iface: Pointer to interface data.
- * Returns: 0 on success, -1 on failure
+ * Returns: 0 on success, < 0 on failure
  *
  * Sets up the hardware mode, channel, rates, and passive scanning
  * based on the configuration.
@@ -631,17 +631,23 @@
 			       HOSTAPD_LEVEL_WARNING,
 			       "Hardware does not support configured mode "
 			       "(%d)", (int) iface->conf->hw_mode);
-		return -1;
+		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->flag & HOSTAPD_CHAN_DISABLED) &&
-		    (chan->chan == iface->conf->channel)) {
-			ok = 1;
-			break;
+		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",
+					   j, chan->chan, chan->flag);
+			} else {
+				ok = 1;
+				break;
+			}
 		}
 	}
 	if (ok && iface->conf->secondary_channel) {
@@ -675,7 +681,7 @@
 		 * the channel automatically */
 		wpa_printf(MSG_ERROR, "Channel not configured "
 			   "(hw_mode/channel in hostapd.conf)");
-		return -1;
+		return -3;
 	}
 	if (ok == 0 && iface->conf->channel != 0) {
 		hostapd_logger(iface->bss[0], NULL,
@@ -693,7 +699,7 @@
 		hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_WARNING,
 			       "Hardware does not support configured channel");
-		return -1;
+		return -4;
 	}
 
 	return 0;
diff --git a/src/ap/hw_features.h b/src/ap/hw_features.h
index 88c2322..b84bca6 100644
--- a/src/ap/hw_features.h
+++ b/src/ap/hw_features.h
@@ -41,7 +41,7 @@
 
 static inline int hostapd_select_hw_mode(struct hostapd_iface *iface)
 {
-	return -1;
+	return -100;
 }
 
 static inline const char * hostapd_hw_mode_txt(int mode)
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index ac0c127..217f9f9 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -1701,6 +1701,7 @@
 	conf.wps = hapd->wps;
 	conf.fragment_size = hapd->conf->fragment_size;
 	conf.pwd_group = hapd->conf->pwd_group;
+	conf.pbc_in_m1 = hapd->conf->pbc_in_m1;
 
 	os_memset(&cb, 0, sizeof(cb));
 	cb.eapol_send = ieee802_1x_eapol_send;
diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
index fcbd89b..fc927f9 100644
--- a/src/ap/wps_hostapd.c
+++ b/src/ap/wps_hostapd.c
@@ -876,14 +876,6 @@
 	wps->model_description = hapd->conf->model_description;
 	wps->model_url = hapd->conf->model_url;
 	wps->upc = hapd->conf->upc;
-
-	if (hostapd_wps_upnp_init(hapd, wps) < 0) {
-		wpa_printf(MSG_ERROR, "Failed to initialize WPS UPnP");
-		wps_registrar_deinit(wps->registrar);
-		os_free(wps->network_key);
-		os_free(wps);
-		return -1;
-	}
 #endif /* CONFIG_WPS_UPNP */
 
 	hostapd_register_probereq_cb(hapd, hostapd_wps_probe_req_rx, hapd);
@@ -894,6 +886,28 @@
 }
 
 
+int hostapd_init_wps_complete(struct hostapd_data *hapd)
+{
+	struct wps_context *wps = hapd->wps;
+
+	if (hapd->wps == NULL)
+		return 0;
+
+#ifdef CONFIG_WPS_UPNP
+	if (hostapd_wps_upnp_init(hapd, wps) < 0) {
+		wpa_printf(MSG_ERROR, "Failed to initialize WPS UPnP");
+		wps_registrar_deinit(wps->registrar);
+		os_free(wps->network_key);
+		os_free(wps);
+		hapd->wps = NULL;
+		return -1;
+	}
+#endif /* CONFIG_WPS_UPNP */
+
+	return 0;
+}
+
+
 void hostapd_deinit_wps(struct hostapd_data *hapd)
 {
 	eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
diff --git a/src/ap/wps_hostapd.h b/src/ap/wps_hostapd.h
index 338a220..6b28c13 100644
--- a/src/ap/wps_hostapd.h
+++ b/src/ap/wps_hostapd.h
@@ -19,6 +19,7 @@
 
 int hostapd_init_wps(struct hostapd_data *hapd,
 		     struct hostapd_bss_config *conf);
+int hostapd_init_wps_complete(struct hostapd_data *hapd);
 void hostapd_deinit_wps(struct hostapd_data *hapd);
 void hostapd_update_wps(struct hostapd_data *hapd);
 int hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *addr,
@@ -50,6 +51,11 @@
 {
 }
 
+static inline int hostapd_init_wps_complete(struct hostapd_data *hapd)
+{
+    return 0;
+}
+
 static inline void hostapd_update_wps(struct hostapd_data *hapd)
 {
 }
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 90b3f20..b4f01fa 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -220,6 +220,15 @@
 static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv);
 
 
+struct nl80211_bss_info_arg {
+	struct wpa_driver_nl80211_data *drv;
+	struct wpa_scan_results *res;
+	unsigned int assoc_freq;
+};
+
+static int bss_info_handler(struct nl_msg *msg, void *arg);
+
+
 /* nl80211 code */
 static int ack_handler(struct nl_msg *msg, void *arg)
 {
@@ -589,6 +598,38 @@
 }
 
 
+static unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv)
+{
+	struct nl_msg *msg;
+	int ret;
+	struct nl80211_bss_info_arg arg;
+
+	os_memset(&arg, 0, sizeof(arg));
+	msg = nlmsg_alloc();
+	if (!msg)
+		goto nla_put_failure;
+
+	genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, NLM_F_DUMP,
+		    NL80211_CMD_GET_SCAN, 0);
+	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+	arg.drv = drv;
+	ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg);
+	msg = NULL;
+	if (ret == 0) {
+		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;
+	}
+	wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d "
+		   "(%s)", ret, strerror(-ret));
+nla_put_failure:
+	nlmsg_free(msg);
+	return drv->assoc_freq;
+}
+
+
 static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
 			    const u8 *frame, size_t len)
 {
@@ -679,6 +720,8 @@
 		event.assoc_info.resp_ies_len = nla_len(resp_ie);
 	}
 
+	event.assoc_info.freq = nl80211_get_assoc_freq(drv);
+
 	wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
 }
 
@@ -2347,11 +2390,6 @@
 }
 
 
-struct nl80211_bss_info_arg {
-	struct wpa_driver_nl80211_data *drv;
-	struct wpa_scan_results *res;
-};
-
 static int bss_info_handler(struct nl_msg *msg, void *arg)
 {
 	struct nlattr *tb[NL80211_ATTR_MAX + 1];
@@ -2377,6 +2415,7 @@
 	const u8 *ie, *beacon_ie;
 	size_t ie_len, beacon_ie_len;
 	u8 *pos;
+	size_t i;
 
 	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
 		  genlmsg_attrlen(gnlh, 0), NULL);
@@ -2385,6 +2424,19 @@
 	if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS],
 			     bss_policy))
 		return NL_SKIP;
+	if (bss[NL80211_BSS_STATUS]) {
+		enum nl80211_bss_status status;
+		status = nla_get_u32(bss[NL80211_BSS_STATUS]);
+		if (status == NL80211_BSS_STATUS_ASSOCIATED &&
+		    bss[NL80211_BSS_FREQUENCY]) {
+			_arg->assoc_freq =
+				nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
+			wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz",
+				   _arg->assoc_freq);
+		}
+	}
+	if (!res)
+		return NL_SKIP;
 	if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) {
 		ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
 		ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
@@ -2455,6 +2507,38 @@
 		}
 	}
 
+	/*
+	 * cfg80211 maintains separate BSS table entries for APs if the same
+	 * BSSID,SSID pair is seen on multiple channels. wpa_supplicant does
+	 * not use frequency as a separate key in the BSS table, so filter out
+	 * duplicated entries. Prefer associated BSS entry in such a case in
+	 * order to get the correct frequency into the BSS table.
+	 */
+	for (i = 0; i < res->num; i++) {
+		const u8 *s1, *s2;
+		if (os_memcmp(res->res[i]->bssid, r->bssid, ETH_ALEN) != 0)
+			continue;
+
+		s1 = nl80211_get_ie((u8 *) (res->res[i] + 1),
+				    res->res[i]->ie_len, WLAN_EID_SSID);
+		s2 = nl80211_get_ie((u8 *) (r + 1), r->ie_len, WLAN_EID_SSID);
+		if (s1 == NULL || s2 == NULL || s1[1] != s2[1] ||
+		    os_memcmp(s1, s2, 2 + s1[1]) != 0)
+			continue;
+
+		/* Same BSSID,SSID was already included in scan results */
+		wpa_printf(MSG_DEBUG, "nl80211: Remove duplicated scan result "
+			   "for " MACSTR, MAC2STR(r->bssid));
+
+		if ((r->flags & WPA_SCAN_ASSOCIATED) &&
+		    !(res->res[i]->flags & WPA_SCAN_ASSOCIATED)) {
+			os_free(res->res[i]);
+			res->res[i] = r;
+		} else
+			os_free(r);
+		return NL_SKIP;
+	}
+
 	tmp = os_realloc(res->res,
 			 (res->num + 1) * sizeof(struct wpa_scan_res *));
 	if (tmp == NULL) {
diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h
index 6b29075..e1f500a 100644
--- a/src/eap_server/eap.h
+++ b/src/eap_server/eap.h
@@ -110,6 +110,8 @@
 	const struct wpabuf *assoc_p2p_ie;
 	const u8 *peer_addr;
 	int fragment_size;
+
+	int pbc_in_m1;
 };
 
 
diff --git a/src/eap_server/eap_i.h b/src/eap_server/eap_i.h
index daac746..3cba5aa 100644
--- a/src/eap_server/eap_i.h
+++ b/src/eap_server/eap_i.h
@@ -192,6 +192,8 @@
 
 	/* Fragmentation size for EAP method init() handler */
 	int fragment_size;
+
+	int pbc_in_m1;
 };
 
 int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len,
diff --git a/src/eap_server/eap_server.c b/src/eap_server/eap_server.c
index 41416b1..0f0da29 100644
--- a/src/eap_server/eap_server.c
+++ b/src/eap_server/eap_server.c
@@ -1261,6 +1261,7 @@
 		os_memcpy(sm->peer_addr, conf->peer_addr, ETH_ALEN);
 	sm->fragment_size = conf->fragment_size;
 	sm->pwd_group = conf->pwd_group;
+	sm->pbc_in_m1 = conf->pbc_in_m1;
 
 	wpa_printf(MSG_DEBUG, "EAP: Server state machine created");
 
diff --git a/src/eap_server/eap_server_wsc.c b/src/eap_server/eap_server_wsc.c
index e944a4d..556882d 100644
--- a/src/eap_server/eap_server_wsc.c
+++ b/src/eap_server/eap_server_wsc.c
@@ -144,6 +144,7 @@
 		cfg.p2p_dev_addr = p2p_get_go_dev_addr(sm->assoc_p2p_ie);
 	}
 #endif /* CONFIG_P2P */
+	cfg.pbc_in_m1 = sm->pbc_in_m1;
 	data->wps = wps_init(&cfg);
 	if (data->wps == NULL) {
 		os_free(data);
diff --git a/src/eapol_auth/eapol_auth_sm.c b/src/eapol_auth/eapol_auth_sm.c
index 841a1c5..4aa71ad 100644
--- a/src/eapol_auth/eapol_auth_sm.c
+++ b/src/eapol_auth/eapol_auth_sm.c
@@ -834,6 +834,7 @@
 	eap_conf.peer_addr = addr;
 	eap_conf.fragment_size = eapol->conf.fragment_size;
 	eap_conf.pwd_group = eapol->conf.pwd_group;
+	eap_conf.pbc_in_m1 = eapol->conf.pbc_in_m1;
 	sm->eap = eap_server_sm_init(sm, &eapol_cb, &eap_conf);
 	if (sm->eap == NULL) {
 		eapol_auth_free(sm);
@@ -1039,6 +1040,7 @@
 	dst->eap_sim_db_priv = src->eap_sim_db_priv;
 	os_free(dst->eap_req_id_text);
 	dst->pwd_group = src->pwd_group;
+	dst->pbc_in_m1 = src->pbc_in_m1;
 	if (src->eap_req_id_text) {
 		dst->eap_req_id_text = os_malloc(src->eap_req_id_text_len);
 		if (dst->eap_req_id_text == NULL)
diff --git a/src/eapol_auth/eapol_auth_sm.h b/src/eapol_auth/eapol_auth_sm.h
index 59a10b4..724bf8b 100644
--- a/src/eapol_auth/eapol_auth_sm.h
+++ b/src/eapol_auth/eapol_auth_sm.h
@@ -42,6 +42,7 @@
 	struct wps_context *wps;
 	int fragment_size;
 	u16 pwd_group;
+	int pbc_in_m1;
 
 	/* Opaque context pointer to owner data for callback functions */
 	void *ctx;
diff --git a/src/wps/wps.c b/src/wps/wps.c
index 7564d60..5c8c25f 100644
--- a/src/wps/wps.c
+++ b/src/wps/wps.c
@@ -113,6 +113,7 @@
 		os_memcpy(data->p2p_dev_addr, cfg->p2p_dev_addr, ETH_ALEN);
 
 	data->use_psk_key = cfg->use_psk_key;
+	data->pbc_in_m1 = cfg->pbc_in_m1;
 
 	return data;
 }
diff --git a/src/wps/wps.h b/src/wps/wps.h
index 918273d..3e4c218 100644
--- a/src/wps/wps.h
+++ b/src/wps/wps.h
@@ -187,6 +187,14 @@
 	 * to %NULL to indicate the station does not have a P2P Device Address.
 	 */
 	const u8 *p2p_dev_addr;
+
+	/**
+	 * pbc_in_m1 - Do not remove PushButton config method in M1 (AP)
+	 *
+	 * This can be used to enable a workaround to allow Windows 7 to use
+	 * PBC with the AP.
+	 */
+	int pbc_in_m1;
 };
 
 struct wps_data * wps_init(const struct wps_config *cfg);
diff --git a/src/wps/wps_enrollee.c b/src/wps/wps_enrollee.c
index 390254e..5b3c045 100644
--- a/src/wps/wps_enrollee.c
+++ b/src/wps/wps_enrollee.c
@@ -133,10 +133,17 @@
 		return NULL;
 
 	config_methods = wps->wps->config_methods;
-	if (wps->wps->ap) {
+	if (wps->wps->ap && !wps->pbc_in_m1 &&
+	    (wps->dev_password_len != 0 ||
+	     (config_methods & WPS_CONFIG_DISPLAY))) {
 		/*
 		 * These are the methods that the AP supports as an Enrollee
 		 * for adding external Registrars, so remove PushButton.
+		 *
+		 * As a workaround for Windows 7 mechanism for probing WPS
+		 * capabilities from M1, leave PushButton option if no PIN
+		 * method is available or if WPS configuration enables PBC
+		 * workaround.
 		 */
 		config_methods &= ~WPS_CONFIG_PUSHBUTTON;
 #ifdef CONFIG_WPS2
diff --git a/src/wps/wps_i.h b/src/wps/wps_i.h
index 437999b..bdb6da2 100644
--- a/src/wps/wps_i.h
+++ b/src/wps/wps_i.h
@@ -119,6 +119,7 @@
 	int use_psk_key;
 	u8 p2p_dev_addr[ETH_ALEN]; /* P2P Device Address of the client or
 				    * 00:00:00:00:00:00 if not a P2p client */
+	int pbc_in_m1;
 };