Accumulative patch from commit 17b8995cf5813d7c027cd7a6884700e791d72392

17b8995 Interworking: Try to use same BSS entry for storing GAS results
3db5439 Optimize Extended Capabilities element to be of minimal length
8cd6b7b hostapd/wpa_s: Use driver's extended capabilities
acb5464 Add ctrl_iface command FLUSH for clearing wpa_supplicant state
97236ce WPS: Skip rescanning after provisioning if AP was configured
4342326 Add ignore_old_scan_res configuration parameter
9599ccc WPS: Clear after_wps on new WPS connection
702621e WPS: Use latest updated BSS entry if multiple BSSID matches found
ab547b5 WPS: Add more helpful debug for invalid WPS_REG command parsing
a679c0f WPS: Allow hostapd process to control independent WPS interfaces
ccdff94 WPS AP: Add support for reconfiguration with in-memory config
8970bae nl80211: Use nla_nest_start/end instead of nla_put_nested
558d69e P2P: Omit P2P Group Info in case of no connected peers
65a32cd AP: Fix infinite loop in WPA state machine when out of random bytes
a5f61b2 Fix OLBC non-HT AP detection to check channel
69554d7 ap_list: Remove unused functions
08c99ca ap_list: Remove unused iteration list pointers
6b16917 ap_list: Remove unused fields
66f1f75 P2P: Fix provision discovery response handling in some cases
2f9b66d Extend ROAM command to handle multiple SSIDs per BSS

Change-Id: I46002b1d3bbf6e376c2ae09bcb2c824c54805bbd
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index 8a996f9..3ba4496 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -498,6 +498,9 @@
 	hapd_iface->owner = wpa_s;
 	hapd_iface->drv_flags = wpa_s->drv_flags;
 	hapd_iface->probe_resp_offloads = wpa_s->probe_resp_offloads;
+	hapd_iface->extended_capa = wpa_s->extended_capa;
+	hapd_iface->extended_capa_mask = wpa_s->extended_capa_mask;
+	hapd_iface->extended_capa_len = wpa_s->extended_capa_len;
 
 	wpa_s->ap_iface->conf = conf = hostapd_config_defaults();
 	if (conf == NULL) {
diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index c50de53..0e1576b 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -224,11 +224,27 @@
 }
 
 
-static void wpa_bss_copy_res(struct wpa_bss *dst, struct wpa_scan_res *src,
-			     struct os_time *fetch_time)
+static void calculate_update_time(const struct os_time *fetch_time,
+				  unsigned int age_ms,
+				  struct os_time *update_time)
 {
 	os_time_t usec;
 
+	update_time->sec = fetch_time->sec;
+	update_time->usec = fetch_time->usec;
+	update_time->sec -= age_ms / 1000;
+	usec = (age_ms % 1000) * 1000;
+	if (update_time->usec < usec) {
+		update_time->sec--;
+		update_time->usec += 1000000;
+	}
+	update_time->usec -= usec;
+}
+
+
+static void wpa_bss_copy_res(struct wpa_bss *dst, struct wpa_scan_res *src,
+			     struct os_time *fetch_time)
+{
 	dst->flags = src->flags;
 	os_memcpy(dst->bssid, src->bssid, ETH_ALEN);
 	dst->freq = src->freq;
@@ -239,15 +255,7 @@
 	dst->level = src->level;
 	dst->tsf = src->tsf;
 
-	dst->last_update.sec = fetch_time->sec;
-	dst->last_update.usec = fetch_time->usec;
-	dst->last_update.sec -= src->age / 1000;
-	usec = (src->age % 1000) * 1000;
-	if (dst->last_update.usec < usec) {
-		dst->last_update.sec--;
-		dst->last_update.usec += 1000000;
-	}
-	dst->last_update.usec -= usec;
+	calculate_update_time(fetch_time, src->age, &dst->last_update);
 }
 
 
@@ -567,6 +575,21 @@
 	const u8 *ssid, *p2p;
 	struct wpa_bss *bss;
 
+	if (wpa_s->conf->ignore_old_scan_res) {
+		struct os_time update;
+		calculate_update_time(fetch_time, res->age, &update);
+		if (os_time_before(&update, &wpa_s->scan_trigger_time)) {
+			struct os_time age;
+			os_time_sub(&wpa_s->scan_trigger_time, &update, &age);
+			wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Ignore driver BSS "
+				"table entry that is %u.%06u seconds older "
+				"than our scan trigger",
+				(unsigned int) age.sec,
+				(unsigned int) age.usec);
+			return;
+		}
+	}
+
 	ssid = wpa_scan_get_ie(res, WLAN_EID_SSID);
 	if (ssid == NULL) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "BSS: No SSID IE included for "
@@ -829,6 +852,34 @@
 }
 
 
+/**
+ * wpa_bss_get_bssid_latest - Fetch the latest BSS table entry based on BSSID
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bssid: BSSID
+ * Returns: Pointer to the BSS entry or %NULL if not found
+ *
+ * This function is like wpa_bss_get_bssid(), but full BSS table is iterated to
+ * find the entry that has the most recent update. This can help in finding the
+ * correct entry in cases where the SSID of the AP may have changed recently
+ * (e.g., in WPS reconfiguration cases).
+ */
+struct wpa_bss * wpa_bss_get_bssid_latest(struct wpa_supplicant *wpa_s,
+					  const u8 *bssid)
+{
+	struct wpa_bss *bss, *found = NULL;
+	if (!wpa_supplicant_filter_bssid_match(wpa_s, bssid))
+		return NULL;
+	dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) {
+		if (os_memcmp(bss->bssid, bssid, ETH_ALEN) != 0)
+			continue;
+		if (found == NULL ||
+		    os_time_before(&found->last_update, &bss->last_update))
+			found = bss;
+	}
+	return found;
+}
+
+
 #ifdef CONFIG_P2P
 /**
  * wpa_bss_get_p2p_dev_addr - Fetch a BSS table entry based on P2P Device Addr
diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h
index 9f14d0e..2b41948 100644
--- a/wpa_supplicant/bss.h
+++ b/wpa_supplicant/bss.h
@@ -109,6 +109,8 @@
 			     const u8 *ssid, size_t ssid_len);
 struct wpa_bss * wpa_bss_get_bssid(struct wpa_supplicant *wpa_s,
 				   const u8 *bssid);
+struct wpa_bss * wpa_bss_get_bssid_latest(struct wpa_supplicant *wpa_s,
+					  const u8 *bssid);
 struct wpa_bss * wpa_bss_get_p2p_dev_addr(struct wpa_supplicant *wpa_s,
 					  const u8 *dev_addr);
 struct wpa_bss * wpa_bss_get_id(struct wpa_supplicant *wpa_s, unsigned int id);
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index 1b71ca7..0d98884 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -3089,6 +3089,7 @@
 	{ INT(dtim_period), 0 },
 	{ INT(beacon_int), 0 },
 	{ FUNC(ap_vendor_elements), 0 },
+	{ INT_RANGE(ignore_old_scan_res, 0, 1), 0 },
 };
 
 #undef FUNC
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index 00990cb..1315e7b 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -844,6 +844,16 @@
 	 * elements (id+len+payload for one or more elements).
 	 */
 	struct wpabuf *ap_vendor_elements;
+
+	/**
+	 * ignore_old_scan_res - Ignore scan results older than request
+	 *
+	 * The driver may have a cache of scan results that makes it return
+	 * information that is older than our scan trigger. This parameter can
+	 * be used to configure such old information to be ignored instead of
+	 * allowing it to update the internal BSS table.
+	 */
+	int ignore_old_scan_res;
 };
 
 
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index d5de030..5ca6bf2 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -1004,6 +1004,10 @@
 			fprintf(f, "\n");
 		}
 	}
+
+	if (config->ignore_old_scan_res)
+		fprintf(f, "ignore_old_scan_res=%d\n",
+			config->ignore_old_scan_res);
 }
 
 #endif /* CONFIG_NO_CONFIG_WRITE */
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index a8c57eb..6d2b3b1 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -3532,7 +3532,13 @@
 
 	wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM " MACSTR, MAC2STR(bssid));
 
-	bss = wpa_bss_get_bssid(wpa_s, bssid);
+	if (!ssid) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: No network "
+			   "configuration known for the target AP");
+		return -1;
+	}
+
+	bss = wpa_bss_get(wpa_s, bssid, ssid->ssid, ssid->ssid_len);
 	if (!bss) {
 		wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: Target AP not found "
 			   "from BSS table");
@@ -3544,12 +3550,6 @@
 	 * allow roaming to other networks
 	 */
 
-	if (!ssid) {
-		wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: No network "
-			   "configuration known for the target AP");
-		return -1;
-	}
-
 	wpa_s->reassociate = 1;
 	wpa_supplicant_connect(wpa_s, bss, ssid);
 
@@ -4509,6 +4509,15 @@
 }
 
 
+static void p2p_ctrl_flush(struct wpa_supplicant *wpa_s)
+{
+	os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN);
+	wpa_s->force_long_sd = 0;
+	if (wpa_s->global->p2p)
+		p2p_flush(wpa_s->global->p2p);
+}
+
+
 static int p2p_ctrl_presence_req(struct wpa_supplicant *wpa_s, char *cmd)
 {
 	char *pos, *pos2;
@@ -4979,6 +4988,54 @@
 #endif
 
 
+static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s)
+{
+	wpa_dbg(wpa_s, MSG_DEBUG, "Flush all wpa_supplicant state");
+
+#ifdef CONFIG_P2P
+	wpas_p2p_stop_find(wpa_s);
+	p2p_ctrl_flush(wpa_s);
+	wpas_p2p_group_remove(wpa_s, "*");
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_WPS_TESTING
+	wps_version_number = 0x20;
+	wps_testing_dummy_cred = 0;
+#endif /* CONFIG_WPS_TESTING */
+#ifdef CONFIG_WPS
+	wpas_wps_cancel(wpa_s);
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_TDLS_TESTING
+	extern unsigned int tdls_testing;
+	tdls_testing = 0;
+#endif /* CONFIG_TDLS_TESTING */
+#ifdef CONFIG_TDLS
+	wpa_drv_tdls_oper(wpa_s, TDLS_ENABLE, NULL);
+	wpa_tdls_enable(wpa_s->wpa, 1);
+#endif /* CONFIG_TDLS */
+
+	wpa_s->no_keep_alive = 0;
+
+	os_free(wpa_s->disallow_aps_bssid);
+	wpa_s->disallow_aps_bssid = NULL;
+	wpa_s->disallow_aps_bssid_count = 0;
+	os_free(wpa_s->disallow_aps_ssid);
+	wpa_s->disallow_aps_ssid = NULL;
+	wpa_s->disallow_aps_ssid_count = 0;
+
+	wpa_s->set_sta_uapsd = 0;
+	wpa_s->sta_uapsd = 0;
+
+	wpa_drv_radio_disable(wpa_s, 0);
+
+	wpa_bss_flush(wpa_s);
+	wpa_blacklist_clear(wpa_s);
+	wpa_supplicant_ctrl_iface_remove_network(wpa_s, "all");
+	wpa_supplicant_ctrl_iface_remove_cred(wpa_s, "all");
+}
+
+
 char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
 					 char *buf, size_t *resp_len)
 {
@@ -5260,10 +5317,7 @@
 		if (p2p_ctrl_set(wpa_s, buf + 8) < 0)
 			reply_len = -1;
 	} else if (os_strcmp(buf, "P2P_FLUSH") == 0) {
-		os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN);
-		wpa_s->force_long_sd = 0;
-		if (wpa_s->global->p2p)
-			p2p_flush(wpa_s->global->p2p);
+		p2p_ctrl_flush(wpa_s);
 	} else if (os_strncmp(buf, "P2P_UNAUTHORIZE ", 16) == 0) {
 		if (wpas_p2p_unauthorize(wpa_s, buf + 16) < 0)
 			reply_len = -1;
@@ -5516,6 +5570,8 @@
 		if (wpas_ctrl_iface_wnm_sleep(wpa_s, buf + 10))
 			reply_len = -1;
 #endif /* CONFIG_WNM */
+	} else if (os_strcmp(buf, "FLUSH") == 0) {
+		wpa_supplicant_ctrl_iface_flush(wpa_s);
 	} else {
 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
 		reply_len = 16;
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index b117b62..6c0a246 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -854,9 +854,8 @@
 }
 
 
-static struct wpa_bss *
-wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s,
-			    struct wpa_ssid **selected_ssid)
+struct wpa_bss * wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s,
+					     struct wpa_ssid **selected_ssid)
 {
 	struct wpa_bss *selected = NULL;
 	int prio;
diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c
index 6fe762f..b59dd6a 100644
--- a/wpa_supplicant/interworking.c
+++ b/wpa_supplicant/interworking.c
@@ -187,6 +187,7 @@
 
 	wpa_printf(MSG_DEBUG, "Interworking: ANQP Query Request to " MACSTR,
 		   MAC2STR(bss->bssid));
+	wpa_s->interworking_gas_bss = bss;
 
 	info_ids[num_info_ids++] = ANQP_CAPABILITY_LIST;
 	if (all) {
@@ -1809,11 +1810,11 @@
 
 
 static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s,
-					    const u8 *sa, u16 info_id,
+					    struct wpa_bss *bss, const u8 *sa,
+					    u16 info_id,
 					    const u8 *data, size_t slen)
 {
 	const u8 *pos = data;
-	struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, sa);
 	struct wpa_bss_anqp *anqp = NULL;
 #ifdef CONFIG_HS20
 	u8 type;
@@ -1949,6 +1950,7 @@
 	const u8 *end;
 	u16 info_id;
 	u16 slen;
+	struct wpa_bss *bss = NULL, *tmp;
 
 	if (result != GAS_QUERY_SUCCESS)
 		return;
@@ -1961,6 +1963,21 @@
 		return;
 	}
 
+	/*
+	 * If possible, select the BSS entry based on which BSS entry was used
+	 * for the request. This can help in cases where multiple BSS entries
+	 * may exist for the same AP.
+	 */
+	dl_list_for_each_reverse(tmp, &wpa_s->bss, struct wpa_bss, list) {
+		if (tmp == wpa_s->interworking_gas_bss &&
+		    os_memcmp(tmp->bssid, dst, ETH_ALEN) == 0) {
+			bss = tmp;
+			break;
+		}
+	}
+	if (bss == NULL)
+		bss = wpa_bss_get_bssid(wpa_s, dst);
+
 	pos = wpabuf_head(resp);
 	end = pos + wpabuf_len(resp);
 
@@ -1978,7 +1995,7 @@
 				   "for Info ID %u", info_id);
 			break;
 		}
-		interworking_parse_rx_anqp_resp(wpa_s, dst, info_id, pos,
+		interworking_parse_rx_anqp_resp(wpa_s, bss, dst, info_id, pos,
 						slen);
 		pos += slen;
 	}
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index c6856e2..fc311e9 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -228,8 +228,10 @@
 				break;
 			}
 		}
-	} else
+	} else {
+		os_get_time(&wpa_s->scan_trigger_time);
 		wpa_s->scan_res_handler = wpas_p2p_scan_res_handler;
+	}
 
 	return ret;
 }
@@ -994,6 +996,7 @@
 	d->persistent_reconnect = s->persistent_reconnect;
 	d->max_num_sta = s->max_num_sta;
 	d->pbc_in_m1 = s->pbc_in_m1;
+	d->ignore_old_scan_res = s->ignore_old_scan_res;
 }
 
 
@@ -3422,8 +3425,8 @@
 		if (join == 0 &&
 		    wpa_s->auto_pd_scan_retry < P2P_AUTO_PD_SCAN_ATTEMPTS) {
 			wpa_s->auto_pd_scan_retry++;
-			bss = wpa_bss_get_bssid(wpa_s,
-						wpa_s->pending_join_dev_addr);
+			bss = wpa_bss_get_bssid_latest(
+				wpa_s, wpa_s->pending_join_dev_addr);
 			if (bss) {
 				freq = bss->freq;
 				wpa_printf(MSG_DEBUG, "P2P: Scan retry %d for "
@@ -3503,7 +3506,7 @@
 		wpa_printf(MSG_DEBUG, "P2P: Target GO operating frequency "
 			   "from P2P peer table: %d MHz", freq);
 	}
-	bss = wpa_bss_get_bssid(wpa_s, wpa_s->pending_join_iface_addr);
+	bss = wpa_bss_get_bssid_latest(wpa_s, wpa_s->pending_join_iface_addr);
 	if (bss) {
 		freq = bss->freq;
 		wpa_printf(MSG_DEBUG, "P2P: Target GO operating frequency "
@@ -3638,8 +3641,10 @@
 	 * the new scan results become available.
 	 */
 	ret = wpa_drv_scan(wpa_s, &params);
-	if (!ret)
+	if (!ret) {
+		os_get_time(&wpa_s->scan_trigger_time);
 		wpa_s->scan_res_handler = wpas_p2p_scan_res_join;
+	}
 
 	wpabuf_free(ies);
 
@@ -3715,7 +3720,7 @@
 	os_memcpy(res.peer_interface_addr, wpa_s->pending_join_iface_addr,
 		  ETH_ALEN);
 	res.wps_method = wpa_s->pending_join_wps_method;
-	bss = wpa_bss_get_bssid(wpa_s, wpa_s->pending_join_iface_addr);
+	bss = wpa_bss_get_bssid_latest(wpa_s, wpa_s->pending_join_iface_addr);
 	if (bss) {
 		res.freq = bss->freq;
 		res.ssid_len = bss->ssid_len;
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index 825e90b..db73a18 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -227,6 +227,7 @@
 		wpa_supplicant_notify_scanning(wpa_s, 0);
 		wpas_notify_scan_done(wpa_s, 0);
 	} else {
+		os_get_time(&wpa_s->scan_trigger_time);
 		wpa_s->scan_runs++;
 		wpa_s->normal_scans++;
 	}
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index 90e4169..cdbe011 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -2323,6 +2323,12 @@
 #endif
 
 
+static int wpa_cli_cmd_flush(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "FLUSH");
+}
+
+
 enum wpa_cli_cmd_flags {
 	cli_cmd_flag_none		= 0x00,
 	cli_cmd_flag_sensitive		= 0x01
@@ -2770,6 +2776,8 @@
 #endif /* CONFIG_WNM */
 	{ "raw", wpa_cli_cmd_raw, NULL, cli_cmd_flag_sensitive,
 	  "<params..> = Sent unprocessed command" },
+	{ "flush", wpa_cli_cmd_flush, NULL, cli_cmd_flag_none,
+	  "= flush wpa_supplicant state" },
 #ifdef ANDROID
 	{ "driver", wpa_cli_cmd_driver, NULL,
 	  cli_cmd_flag_none,
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index 6fb1cae..a6478b6 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -1184,30 +1184,67 @@
 }
 
 
+static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx)
+{
+	*pos = 0x00;
+
+	switch (idx) {
+	case 0: /* Bits 0-7 */
+		break;
+	case 1: /* Bits 8-15 */
+		break;
+	case 2: /* Bits 16-23 */
+#ifdef CONFIG_WNM
+		*pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */
+		*pos |= 0x08; /* Bit 19 - BSS Transition */
+#endif /* CONFIG_WNM */
+		break;
+	case 3: /* Bits 24-31 */
+#ifdef CONFIG_WNM
+		*pos |= 0x02; /* Bit 25 - SSID List */
+#endif /* CONFIG_WNM */
+#ifdef CONFIG_INTERWORKING
+		if (wpa_s->conf->interworking)
+			*pos |= 0x80; /* Bit 31 - Interworking */
+#endif /* CONFIG_INTERWORKING */
+		break;
+	case 4: /* Bits 32-39 */
+		break;
+	case 5: /* Bits 40-47 */
+		break;
+	case 6: /* Bits 48-55 */
+		break;
+	}
+}
+
+
 int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf)
 {
-	u32 ext_capab = 0;
 	u8 *pos = buf;
+	u8 len = 4, i;
 
-#ifdef CONFIG_INTERWORKING
-	if (wpa_s->conf->interworking)
-		ext_capab |= BIT(31); /* Interworking */
-#endif /* CONFIG_INTERWORKING */
-
-#ifdef CONFIG_WNM
-	ext_capab |= BIT(17); /* WNM-Sleep Mode */
-	ext_capab |= BIT(19); /* BSS Transition */
-#endif /* CONFIG_WNM */
-
-	if (!ext_capab)
-		return 0;
+	if (len < wpa_s->extended_capa_len)
+		len = wpa_s->extended_capa_len;
 
 	*pos++ = WLAN_EID_EXT_CAPAB;
-	*pos++ = 4;
-	WPA_PUT_LE32(pos, ext_capab);
-	pos += 4;
+	*pos++ = len;
+	for (i = 0; i < len; i++, pos++) {
+		wpas_ext_capab_byte(wpa_s, pos, i);
 
-	return pos - buf;
+		if (i < wpa_s->extended_capa_len) {
+			*pos &= ~wpa_s->extended_capa_mask[i];
+			*pos |= wpa_s->extended_capa[i];
+		}
+	}
+
+	while (len > 0 && buf[1 + len] == 0) {
+		len--;
+		buf[1] = len;
+	}
+	if (len == 0)
+		return 0;
+
+	return 2 + len;
 }
 
 
@@ -2925,6 +2962,9 @@
 		wpa_s->max_match_sets = capa.max_match_sets;
 		wpa_s->max_remain_on_chan = capa.max_remain_on_chan;
 		wpa_s->max_stations = capa.max_stations;
+		wpa_s->extended_capa = capa.extended_capa;
+		wpa_s->extended_capa_mask = capa.extended_capa_mask;
+		wpa_s->extended_capa_len = capa.extended_capa_len;
 	}
 	if (wpa_s->max_remain_on_chan == 0)
 		wpa_s->max_remain_on_chan = 1000;
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index b407060..0935a06 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -302,6 +302,15 @@
 # one or more elements). This is used in AP and P2P GO modes.
 #ap_vendor_elements=dd0411223301
 
+# Ignore scan results older than request
+#
+# The driver may have a cache of scan results that makes it return
+# information that is older than our scan trigger. This parameter can
+# be used to configure such old information to be ignored instead of
+# allowing it to update the internal BSS table.
+#ignore_old_scan_res=0
+
+
 # Interworking (IEEE 802.11u)
 
 # Enable Interworking
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 5465c4f..0308806 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -444,6 +444,7 @@
 		 */
 		MANUAL_SCAN_REQ
 	} scan_req;
+	struct os_time scan_trigger_time;
 	int scan_runs; /* number of scan runs since WPS was started */
 	int *next_scan_freqs;
 	int scan_interval; /* time in sec between scans to find suitable AP */
@@ -460,6 +461,10 @@
 	 */
 	unsigned int probe_resp_offloads;
 
+	/* extended capabilities supported by the driver */
+	const u8 *extended_capa, *extended_capa_mask;
+	unsigned int extended_capa_len;
+
 	int max_scan_ssids;
 	int max_sched_scan_ssids;
 	int sched_scan_supported;
@@ -654,6 +659,7 @@
 	unsigned int auto_select:1;
 	unsigned int auto_network_select:1;
 	unsigned int fetch_all_anqp:1;
+	struct wpa_bss *interworking_gas_bss;
 #endif /* CONFIG_INTERWORKING */
 	unsigned int drv_capa_known;
 
@@ -790,6 +796,8 @@
 void wpa_supplicant_delayed_mic_error_report(void *eloop_ctx, void *sock_ctx);
 void wnm_bss_keep_alive_deinit(struct wpa_supplicant *wpa_s);
 int wpa_supplicant_fast_associate(struct wpa_supplicant *wpa_s);
+struct wpa_bss * wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s,
+					     struct wpa_ssid **selected_ssid);
 
 /* eap_register.c */
 int eap_register_methods(void);
diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
index 1b1b5f7..603ce91 100644
--- a/wpa_supplicant/wps_supplicant.c
+++ b/wpa_supplicant/wps_supplicant.c
@@ -84,6 +84,10 @@
 	    !(wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
 		int disabled = wpa_s->current_ssid->disabled;
 		unsigned int freq = wpa_s->assoc_freq;
+		struct wpa_bss *bss;
+		struct wpa_ssid *ssid = NULL;
+		int use_fast_assoc = 0;
+
 		wpa_printf(MSG_DEBUG, "WPS: Network configuration replaced - "
 			   "try to associate with the received credential "
 			   "(freq=%u)", freq);
@@ -98,7 +102,26 @@
 		wpa_s->wps_freq = freq;
 		wpa_s->normal_scans = 0;
 		wpa_s->reassociate = 1;
-		wpa_supplicant_req_scan(wpa_s, 0, 0);
+
+		wpa_printf(MSG_DEBUG, "WPS: Checking whether fast association "
+			   "without a new scan can be used");
+		bss = wpa_supplicant_pick_network(wpa_s, &ssid);
+		if (bss) {
+			struct wpabuf *wps;
+			struct wps_parse_attr attr;
+
+			wps = wpa_bss_get_vendor_ie_multi(bss,
+							  WPS_IE_VENDOR_TYPE);
+			if (wps && wps_parse_msg(wps, &attr) == 0 &&
+			    attr.wps_state &&
+			    *attr.wps_state == WPS_STATE_CONFIGURED)
+				use_fast_assoc = 1;
+			wpabuf_free(wps);
+		}
+
+		if (!use_fast_assoc ||
+		    wpa_supplicant_fast_associate(wpa_s) != 1)
+			wpa_supplicant_req_scan(wpa_s, 0, 0);
 		return 1;
 	}
 
@@ -871,9 +894,10 @@
 	struct wpa_ssid *ssid;
 	struct wpa_bss *bss;
 
+	wpa_s->after_wps = 0;
 	wpa_s->known_wps_freq = 0;
 	if (bssid) {
-		bss = wpa_bss_get_bssid(wpa_s, bssid);
+		bss = wpa_bss_get_bssid_latest(wpa_s, bssid);
 		if (bss && bss->freq > 0) {
 			wpa_s->known_wps_freq = 1;
 			wpa_s->wps_freq = bss->freq;