wpa_supplicant: Update to 07-Sep-2012 TOT

commit 44256451130c4766e4a019162de17d0734444ee9
Author: Arik Nemtsov <arik@wizery.com>
Date:   Fri Sep 7 00:22:40 2012 +0300

    AP: Configure basic rates from iface and not conf

Skipped patches:
20ed5e40ba95440a1946cf2dffad3047fb620582
cf8baca6a5719f4f3257631e03317affee015417
a297201df15656dbb0f37e90f3410d9e8102c6fd
620c783753bddd37988269314862dc7e4a62f700

Change-Id: I857aa80af6d1a21b61f7c03a085e7dfc6066d61a
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index eae00d8..4f58a92 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -1135,6 +1135,7 @@
 endif
 endif
 
+MD5OBJS =
 ifndef CONFIG_FIPS
 MD5OBJS += src/crypto/md5.c
 endif
diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index 580a82a..d326bef 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -35,6 +35,64 @@
 #define WPA_BSS_IES_CHANGED_FLAG	BIT(8)
 
 
+static void wpa_bss_set_hessid(struct wpa_bss *bss)
+{
+#ifdef CONFIG_INTERWORKING
+	const u8 *ie = wpa_bss_get_ie(bss, WLAN_EID_INTERWORKING);
+	if (ie == NULL || (ie[1] != 7 && ie[1] != 9)) {
+		os_memset(bss->hessid, 0, ETH_ALEN);
+		return;
+	}
+	if (ie[1] == 7)
+		os_memcpy(bss->hessid, ie + 3, ETH_ALEN);
+	else
+		os_memcpy(bss->hessid, ie + 5, ETH_ALEN);
+#endif /* CONFIG_INTERWORKING */
+}
+
+
+struct wpa_bss_anqp * wpa_bss_anqp_alloc(void)
+{
+	struct wpa_bss_anqp *anqp;
+	anqp = os_zalloc(sizeof(*anqp));
+	if (anqp == NULL)
+		return NULL;
+	anqp->users = 1;
+	return anqp;
+}
+
+
+static void wpa_bss_anqp_free(struct wpa_bss_anqp *anqp)
+{
+	if (anqp == NULL)
+		return;
+
+	anqp->users--;
+	if (anqp->users > 0) {
+		/* Another BSS entry holds a pointer to this ANQP info */
+		return;
+	}
+
+#ifdef CONFIG_INTERWORKING
+	wpabuf_free(anqp->venue_name);
+	wpabuf_free(anqp->network_auth_type);
+	wpabuf_free(anqp->roaming_consortium);
+	wpabuf_free(anqp->ip_addr_type_availability);
+	wpabuf_free(anqp->nai_realm);
+	wpabuf_free(anqp->anqp_3gpp);
+	wpabuf_free(anqp->domain_name);
+#endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_HS20
+	wpabuf_free(anqp->hs20_operator_friendly_name);
+	wpabuf_free(anqp->hs20_wan_metrics);
+	wpabuf_free(anqp->hs20_connection_capability);
+	wpabuf_free(anqp->hs20_operating_class);
+#endif /* CONFIG_HS20 */
+
+	os_free(anqp);
+}
+
+
 static void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
 			   const char *reason)
 {
@@ -45,21 +103,7 @@
 		" SSID '%s' due to %s", bss->id, MAC2STR(bss->bssid),
 		wpa_ssid_txt(bss->ssid, bss->ssid_len), reason);
 	wpas_notify_bss_removed(wpa_s, bss->bssid, bss->id);
-#ifdef CONFIG_INTERWORKING
-	wpabuf_free(bss->anqp_venue_name);
-	wpabuf_free(bss->anqp_network_auth_type);
-	wpabuf_free(bss->anqp_roaming_consortium);
-	wpabuf_free(bss->anqp_ip_addr_type_availability);
-	wpabuf_free(bss->anqp_nai_realm);
-	wpabuf_free(bss->anqp_3gpp);
-	wpabuf_free(bss->anqp_domain_name);
-#endif /* CONFIG_INTERWORKING */
-#ifdef CONFIG_HS20
-	wpabuf_free(bss->hs20_operator_friendly_name);
-	wpabuf_free(bss->hs20_wan_metrics);
-	wpabuf_free(bss->hs20_connection_capability);
-	wpabuf_free(bss->hs20_operating_class);
-#endif /* CONFIG_HS20 */
+	wpa_bss_anqp_free(bss->anqp);
 	os_free(bss);
 }
 
@@ -186,6 +230,7 @@
 	bss->ie_len = res->ie_len;
 	bss->beacon_ie_len = res->beacon_ie_len;
 	os_memcpy(bss + 1, res + 1, res->ie_len + res->beacon_ie_len);
+	wpa_bss_set_hessid(bss);
 
 	dl_list_add_tail(&wpa_s->bss, &bss->list);
 	dl_list_add_tail(&wpa_s->bss_id, &bss->list_id);
@@ -365,6 +410,8 @@
 		}
 		dl_list_add(prev, &bss->list_id);
 	}
+	if (changes & WPA_BSS_IES_CHANGED_FLAG)
+		wpa_bss_set_hessid(bss);
 	dl_list_add_tail(&wpa_s->bss, &bss->list);
 
 	notify_bss_changes(wpa_s, changes, bss);
diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h
index 65e962b..8a307cc 100644
--- a/wpa_supplicant/bss.h
+++ b/wpa_supplicant/bss.h
@@ -19,6 +19,25 @@
 #define WPA_BSS_ASSOCIATED		BIT(5)
 #define WPA_BSS_ANQP_FETCH_TRIED	BIT(6)
 
+struct wpa_bss_anqp {
+	unsigned int users;
+#ifdef CONFIG_INTERWORKING
+	struct wpabuf *venue_name;
+	struct wpabuf *network_auth_type;
+	struct wpabuf *roaming_consortium;
+	struct wpabuf *ip_addr_type_availability;
+	struct wpabuf *nai_realm;
+	struct wpabuf *anqp_3gpp;
+	struct wpabuf *domain_name;
+#endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_HS20
+	struct wpabuf *hs20_operator_friendly_name;
+	struct wpabuf *hs20_wan_metrics;
+	struct wpabuf *hs20_connection_capability;
+	struct wpabuf *hs20_operating_class;
+#endif /* CONFIG_HS20 */
+};
+
 /**
  * struct wpa_bss - BSS table
  * @list: List entry for struct wpa_supplicant::bss
@@ -28,6 +47,7 @@
  * @flags: information flags about the BSS/IBSS (WPA_BSS_*)
  * @last_update_idx: Index of the last scan update
  * @bssid: BSSID
+ * @hessid: HESSID
  * @freq: frequency of the channel in MHz (e.g., 2412 = channel 1)
  * @beacon_int: beacon interval in TUs (host byte order)
  * @caps: capability information field in host byte order
@@ -50,6 +70,7 @@
 	unsigned int last_update_idx;
 	unsigned int flags;
 	u8 bssid[ETH_ALEN];
+	u8 hessid[ETH_ALEN];
 	u8 ssid[32];
 	size_t ssid_len;
 	int freq;
@@ -60,21 +81,7 @@
 	int level;
 	u64 tsf;
 	struct os_time last_update;
-#ifdef CONFIG_INTERWORKING
-	struct wpabuf *anqp_venue_name;
-	struct wpabuf *anqp_network_auth_type;
-	struct wpabuf *anqp_roaming_consortium;
-	struct wpabuf *anqp_ip_addr_type_availability;
-	struct wpabuf *anqp_nai_realm;
-	struct wpabuf *anqp_3gpp;
-	struct wpabuf *anqp_domain_name;
-#endif /* CONFIG_INTERWORKING */
-#ifdef CONFIG_HS20
-	struct wpabuf *hs20_operator_friendly_name;
-	struct wpabuf *hs20_wan_metrics;
-	struct wpabuf *hs20_connection_capability;
-	struct wpabuf *hs20_operating_class;
-#endif /* CONFIG_HS20 */
+	struct wpa_bss_anqp *anqp;
 	size_t ie_len;
 	size_t beacon_ie_len;
 	/* followed by ie_len octets of IEs */
@@ -103,5 +110,6 @@
 					    u32 vendor_type);
 int wpa_bss_get_max_rate(const struct wpa_bss *bss);
 int wpa_bss_get_bit_rates(const struct wpa_bss *bss, u8 **rates);
+struct wpa_bss_anqp * wpa_bss_anqp_alloc(void);
 
 #endif /* BSS_H */
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index 4f0c43d..480c077 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -2792,27 +2792,28 @@
 #endif /* CONFIG_WIFI_DISPLAY */
 
 #ifdef CONFIG_INTERWORKING
-	if (mask & WPA_BSS_MASK_INTERNETW) {
+	if ((mask & WPA_BSS_MASK_INTERNETW) && bss->anqp) {
+		struct wpa_bss_anqp *anqp = bss->anqp;
 		pos = anqp_add_hex(pos, end, "anqp_venue_name",
-				   bss->anqp_venue_name);
+				   anqp->venue_name);
 		pos = anqp_add_hex(pos, end, "anqp_network_auth_type",
-				   bss->anqp_network_auth_type);
+				   anqp->network_auth_type);
 		pos = anqp_add_hex(pos, end, "anqp_roaming_consortium",
-				   bss->anqp_roaming_consortium);
+				   anqp->roaming_consortium);
 		pos = anqp_add_hex(pos, end, "anqp_ip_addr_type_availability",
-				   bss->anqp_ip_addr_type_availability);
+				   anqp->ip_addr_type_availability);
 		pos = anqp_add_hex(pos, end, "anqp_nai_realm",
-				   bss->anqp_nai_realm);
-		pos = anqp_add_hex(pos, end, "anqp_3gpp", bss->anqp_3gpp);
+				   anqp->nai_realm);
+		pos = anqp_add_hex(pos, end, "anqp_3gpp", anqp->anqp_3gpp);
 		pos = anqp_add_hex(pos, end, "anqp_domain_name",
-				   bss->anqp_domain_name);
+				   anqp->domain_name);
 #ifdef CONFIG_HS20
 		pos = anqp_add_hex(pos, end, "hs20_operator_friendly_name",
-				   bss->hs20_operator_friendly_name);
+				   anqp->hs20_operator_friendly_name);
 		pos = anqp_add_hex(pos, end, "hs20_wan_metrics",
-				   bss->hs20_wan_metrics);
+				   anqp->hs20_wan_metrics);
 		pos = anqp_add_hex(pos, end, "hs20_connection_capability",
-				   bss->hs20_connection_capability);
+				   anqp->hs20_connection_capability);
 #endif /* CONFIG_HS20 */
 	}
 #endif /* CONFIG_INTERWORKING */
@@ -4381,6 +4382,7 @@
 }
 #endif
 
+
 char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
 					 char *buf, size_t *resp_len)
 {
diff --git a/wpa_supplicant/eapol_test.c b/wpa_supplicant/eapol_test.c
index 7d63c1b..03b8c7e 100644
--- a/wpa_supplicant/eapol_test.c
+++ b/wpa_supplicant/eapol_test.c
@@ -429,6 +429,37 @@
 }
 
 
+static void eapol_test_set_anon_id(void *ctx, const u8 *id, size_t len)
+{
+	struct eapol_test_data *e = ctx;
+	struct wpa_supplicant *wpa_s = e->wpa_s;
+	char *str;
+	int res;
+
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP method updated anonymous_identity",
+			  id, len);
+
+	if (wpa_s->current_ssid == NULL)
+		return;
+
+	if (id == NULL) {
+		if (wpa_config_set(wpa_s->current_ssid, "anonymous_identity",
+				   "NULL", 0) < 0)
+			return;
+	} else {
+		str = os_malloc(len * 2 + 1);
+		if (str == NULL)
+			return;
+		wpa_snprintf_hex(str, len * 2 + 1, id, len);
+		res = wpa_config_set(wpa_s->current_ssid, "anonymous_identity",
+				     str, 0);
+		os_free(str);
+		if (res < 0)
+			return;
+	}
+}
+
+
 static int test_eapol(struct eapol_test_data *e, struct wpa_supplicant *wpa_s,
 		      struct wpa_ssid *ssid)
 {
@@ -456,6 +487,7 @@
 	ctx->pkcs11_module_path = wpa_s->conf->pkcs11_module_path;
 	ctx->cert_cb = eapol_test_cert_cb;
 	ctx->cert_in_cb = 1;
+	ctx->set_anon_id = eapol_test_set_anon_id;
 
 	wpa_s->eapol = eapol_sm_init(ctx);
 	if (wpa_s->eapol == NULL) {
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index c3acad4..bb870c0 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -1062,6 +1062,9 @@
 	struct wpa_ssid *ssid = NULL;
 	struct wpa_scan_results *scan_res;
 	int ap = 0;
+#ifndef CONFIG_NO_RANDOM_POOL
+	size_t i, num;
+#endif /* CONFIG_NO_RANDOM_POOL */
 
 #ifdef CONFIG_AP
 	if (wpa_s->ap_iface)
@@ -1100,7 +1103,6 @@
 	}
 
 #ifndef CONFIG_NO_RANDOM_POOL
-	size_t i, num;
 	num = scan_res->num;
 	if (num > 10)
 		num = 10;
diff --git a/wpa_supplicant/hs20_supplicant.c b/wpa_supplicant/hs20_supplicant.c
index 2afa16c..0eb6119 100644
--- a/wpa_supplicant/hs20_supplicant.c
+++ b/wpa_supplicant/hs20_supplicant.c
@@ -110,10 +110,14 @@
 	const u8 *pos = data;
 	u8 subtype;
 	struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, sa);
+	struct wpa_bss_anqp *anqp = NULL;
 
 	if (slen < 2)
 		return;
 
+	if (bss)
+		anqp = bss->anqp;
+
 	subtype = *pos++;
 	slen--;
 
@@ -130,9 +134,9 @@
 		wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
 			" Operator Friendly Name", MAC2STR(sa));
 		wpa_hexdump_ascii(MSG_DEBUG, "oper friendly name", pos, slen);
-		if (bss) {
-			wpabuf_free(bss->hs20_operator_friendly_name);
-			bss->hs20_operator_friendly_name =
+		if (anqp) {
+			wpabuf_free(anqp->hs20_operator_friendly_name);
+			anqp->hs20_operator_friendly_name =
 				wpabuf_alloc_copy(pos, slen);
 		}
 		break;
@@ -140,18 +144,18 @@
 		wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
 			" WAN Metrics", MAC2STR(sa));
 		wpa_hexdump_ascii(MSG_DEBUG, "WAN Metrics", pos, slen);
-		if (bss) {
-			wpabuf_free(bss->hs20_wan_metrics);
-			bss->hs20_wan_metrics = wpabuf_alloc_copy(pos, slen);
+		if (anqp) {
+			wpabuf_free(anqp->hs20_wan_metrics);
+			anqp->hs20_wan_metrics = wpabuf_alloc_copy(pos, slen);
 		}
 		break;
 	case HS20_STYPE_CONNECTION_CAPABILITY:
 		wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
 			" Connection Capability", MAC2STR(sa));
 		wpa_hexdump_ascii(MSG_DEBUG, "conn capability", pos, slen);
-		if (bss) {
-			wpabuf_free(bss->hs20_connection_capability);
-			bss->hs20_connection_capability =
+		if (anqp) {
+			wpabuf_free(anqp->hs20_connection_capability);
+			anqp->hs20_connection_capability =
 				wpabuf_alloc_copy(pos, slen);
 		}
 		break;
@@ -159,9 +163,9 @@
 		wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
 			" Operating Class", MAC2STR(sa));
 		wpa_hexdump_ascii(MSG_DEBUG, "Operating Class", pos, slen);
-		if (bss) {
-			wpabuf_free(bss->hs20_operating_class);
-			bss->hs20_operating_class =
+		if (anqp) {
+			wpabuf_free(anqp->hs20_operating_class);
+			anqp->hs20_operating_class =
 				wpabuf_alloc_copy(pos, slen);
 		}
 		break;
diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c
index 7b5b20e..b362bcb 100644
--- a/wpa_supplicant/interworking.c
+++ b/wpa_supplicant/interworking.c
@@ -52,6 +52,18 @@
 	}
 	wpa_s->disconnected = 0;
 	wpa_s->reassociate = 1;
+
+	if (wpa_s->last_scan_res_used > 0) {
+		struct os_time now;
+		os_get_time(&now);
+		if (now.sec - wpa_s->last_scan.sec <= 5) {
+			wpa_printf(MSG_DEBUG, "Interworking: Old scan results "
+				   "are fresh - connect without new scan");
+			if (wpas_select_network_from_last_scan(wpa_s) == 0)
+				return;
+		}
+	}
+
 	wpa_supplicant_req_scan(wpa_s, 0, 0);
 }
 
@@ -96,27 +108,101 @@
 }
 
 
+static int cred_with_roaming_consortium(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_cred *cred;
+
+	for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+		if (cred->roaming_consortium_len)
+			return 1;
+	}
+	return 0;
+}
+
+
+static int cred_with_3gpp(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_cred *cred;
+
+	for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+		if (cred->pcsc || cred->imsi)
+			return 1;
+	}
+	return 0;
+}
+
+
+static int cred_with_nai_realm(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_cred *cred;
+
+	for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+		if (cred->pcsc || cred->imsi)
+			continue;
+		if (!cred->eap_method)
+			return 1;
+		if (cred->realm && cred->roaming_consortium_len == 0)
+			return 1;
+	}
+	return 0;
+}
+
+
+static int cred_with_domain(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_cred *cred;
+
+	for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+		if (cred->domain || cred->pcsc || cred->imsi)
+			return 1;
+	}
+	return 0;
+}
+
+
+static int additional_roaming_consortiums(struct wpa_bss *bss)
+{
+	const u8 *ie;
+	ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM);
+	if (ie == NULL || ie[1] == 0)
+		return 0;
+	return ie[2]; /* Number of ANQP OIs */
+}
+
+
 static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s,
 				      struct wpa_bss *bss)
 {
 	struct wpabuf *buf;
 	int ret = 0;
 	int res;
-	u16 info_ids[] = {
-		ANQP_CAPABILITY_LIST,
-		ANQP_VENUE_NAME,
-		ANQP_NETWORK_AUTH_TYPE,
-		ANQP_ROAMING_CONSORTIUM,
-		ANQP_IP_ADDR_TYPE_AVAILABILITY,
-		ANQP_NAI_REALM,
-		ANQP_3GPP_CELLULAR_NETWORK,
-		ANQP_DOMAIN_NAME
-	};
+	u16 info_ids[8];
+	size_t num_info_ids = 0;
 	struct wpabuf *extra = NULL;
+	int all = wpa_s->fetch_all_anqp;
 
 	wpa_printf(MSG_DEBUG, "Interworking: ANQP Query Request to " MACSTR,
 		   MAC2STR(bss->bssid));
 
+	info_ids[num_info_ids++] = ANQP_CAPABILITY_LIST;
+	if (all) {
+		info_ids[num_info_ids++] = ANQP_VENUE_NAME;
+		info_ids[num_info_ids++] = ANQP_NETWORK_AUTH_TYPE;
+	}
+	if (all || (cred_with_roaming_consortium(wpa_s) &&
+		    additional_roaming_consortiums(bss)))
+		info_ids[num_info_ids++] = ANQP_ROAMING_CONSORTIUM;
+	if (all)
+		info_ids[num_info_ids++] = ANQP_IP_ADDR_TYPE_AVAILABILITY;
+	if (all || cred_with_nai_realm(wpa_s))
+		info_ids[num_info_ids++] = ANQP_NAI_REALM;
+	if (all || cred_with_3gpp(wpa_s))
+		info_ids[num_info_ids++] = ANQP_3GPP_CELLULAR_NETWORK;
+	if (all || cred_with_domain(wpa_s))
+		info_ids[num_info_ids++] = ANQP_DOMAIN_NAME;
+	wpa_hexdump(MSG_DEBUG, "Interworking: ANQP Query info",
+		    (u8 *) info_ids, num_info_ids * 2);
+
 #ifdef CONFIG_HS20
 	if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE)) {
 		u8 *len_pos;
@@ -131,16 +217,18 @@
 		wpabuf_put_u8(extra, HS20_STYPE_QUERY_LIST);
 		wpabuf_put_u8(extra, 0); /* Reserved */
 		wpabuf_put_u8(extra, HS20_STYPE_CAPABILITY_LIST);
-		wpabuf_put_u8(extra, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
-		wpabuf_put_u8(extra, HS20_STYPE_WAN_METRICS);
-		wpabuf_put_u8(extra, HS20_STYPE_CONNECTION_CAPABILITY);
-		wpabuf_put_u8(extra, HS20_STYPE_OPERATING_CLASS);
+		if (all) {
+			wpabuf_put_u8(extra,
+				      HS20_STYPE_OPERATOR_FRIENDLY_NAME);
+			wpabuf_put_u8(extra, HS20_STYPE_WAN_METRICS);
+			wpabuf_put_u8(extra, HS20_STYPE_CONNECTION_CAPABILITY);
+			wpabuf_put_u8(extra, HS20_STYPE_OPERATING_CLASS);
+		}
 		gas_anqp_set_element_len(extra, len_pos);
 	}
 #endif /* CONFIG_HS20 */
 
-	buf = anqp_build_req(info_ids, sizeof(info_ids) / sizeof(info_ids[0]),
-			     extra);
+	buf = anqp_build_req(info_ids, num_info_ids, extra);
 	wpabuf_free(extra);
 	if (buf == NULL)
 		return -1;
@@ -648,8 +736,11 @@
 	struct wpa_cred *cred;
 	struct wpa_ssid *ssid;
 	const u8 *ie;
+	int eap_type;
+	int res;
+	char prefix;
 
-	if (bss->anqp_3gpp == NULL)
+	if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL)
 		return -1;
 
 	for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
@@ -680,7 +771,7 @@
 #ifdef PCSC_FUNCS
 	compare:
 #endif /* PCSC_FUNCS */
-		if (plmn_id_match(bss->anqp_3gpp, imsi, mnc_len))
+		if (plmn_id_match(bss->anqp->anqp_3gpp, imsi, mnc_len))
 			break;
 	}
 	if (cred == NULL)
@@ -709,14 +800,40 @@
 	if (interworking_set_hs20_params(ssid) < 0)
 		goto fail;
 
-	/* TODO: figure out whether to use EAP-SIM, EAP-AKA, or EAP-AKA' */
-	if (wpa_config_set(ssid, "eap", "SIM", 0) < 0) {
-		wpa_printf(MSG_DEBUG, "EAP-SIM not supported");
+	eap_type = EAP_TYPE_SIM;
+	if (cred->pcsc && wpa_s->scard && scard_supports_umts(wpa_s->scard))
+		eap_type = EAP_TYPE_AKA;
+	if (cred->eap_method && cred->eap_method[0].vendor == EAP_VENDOR_IETF) {
+		if (cred->eap_method[0].method == EAP_TYPE_SIM ||
+		    cred->eap_method[0].method == EAP_TYPE_AKA ||
+		    cred->eap_method[0].method == EAP_TYPE_AKA_PRIME)
+			eap_type = cred->eap_method[0].method;
+	}
+
+	switch (eap_type) {
+	case EAP_TYPE_SIM:
+		prefix = '1';
+		res = wpa_config_set(ssid, "eap", "SIM", 0);
+		break;
+	case EAP_TYPE_AKA:
+		prefix = '0';
+		res = wpa_config_set(ssid, "eap", "AKA", 0);
+		break;
+	case EAP_TYPE_AKA_PRIME:
+		prefix = '6';
+		res = wpa_config_set(ssid, "eap", "AKA'", 0);
+		break;
+	default:
+		res = -1;
+		break;
+	}
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG, "Selected EAP method (%d) not supported",
+			   eap_type);
 		goto fail;
 	}
-	if (cred->pcsc && wpa_s->scard && scard_supports_umts(wpa_s->scard))
-		wpa_config_set(ssid, "eap", "AKA", 0);
-	if (!cred->pcsc && set_root_nai(ssid, cred->imsi, '1') < 0) {
+
+	if (!cred->pcsc && set_root_nai(ssid, cred->imsi, prefix) < 0) {
 		wpa_printf(MSG_DEBUG, "Failed to set Root NAI");
 		goto fail;
 	}
@@ -835,7 +952,8 @@
 
 	ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM);
 
-	if (ie == NULL && bss->anqp_roaming_consortium == NULL)
+	if (ie == NULL &&
+	    (bss->anqp == NULL || bss->anqp->roaming_consortium == NULL))
 		return NULL;
 
 	if (wpa_s->conf->cred == NULL)
@@ -845,7 +963,10 @@
 		if (cred->roaming_consortium_len == 0)
 			continue;
 
-		if (!roaming_consortium_match(ie, bss->anqp_roaming_consortium,
+		if (!roaming_consortium_match(ie,
+					      bss->anqp ?
+					      bss->anqp->roaming_consortium :
+					      NULL,
 					      cred->roaming_consortium,
 					      cred->roaming_consortium_len))
 			continue;
@@ -1035,7 +1156,8 @@
 		return interworking_connect_roaming_consortium(wpa_s, cred,
 							       bss, ie);
 
-	realm = nai_realm_parse(bss->anqp_nai_realm, &count);
+	realm = nai_realm_parse(bss->anqp ? bss->anqp->nai_realm : NULL,
+				&count);
 	if (realm == NULL) {
 		wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI "
 			   "Realm list from " MACSTR, MAC2STR(bss->bssid));
@@ -1162,7 +1284,7 @@
 	int ret;
 
 #ifdef INTERWORKING_3GPP
-	if (bss->anqp_3gpp == NULL)
+	if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL)
 		return NULL;
 
 	for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
@@ -1195,7 +1317,7 @@
 #endif /* PCSC_FUNCS */
 		wpa_printf(MSG_DEBUG, "Interworking: Parsing 3GPP info from "
 			   MACSTR, MAC2STR(bss->bssid));
-		ret = plmn_id_match(bss->anqp_3gpp, imsi, mnc_len);
+		ret = plmn_id_match(bss->anqp->anqp_3gpp, imsi, mnc_len);
 		wpa_printf(MSG_DEBUG, "PLMN match %sfound", ret ? "" : "not ");
 		if (ret) {
 			if (selected == NULL ||
@@ -1215,7 +1337,7 @@
 	struct nai_realm *realm;
 	u16 count, i;
 
-	if (bss->anqp_nai_realm == NULL)
+	if (bss->anqp == NULL || bss->anqp->nai_realm == NULL)
 		return NULL;
 
 	if (wpa_s->conf->cred == NULL)
@@ -1223,7 +1345,7 @@
 
 	wpa_printf(MSG_DEBUG, "Interworking: Parsing NAI Realm list from "
 		   MACSTR, MAC2STR(bss->bssid));
-	realm = nai_realm_parse(bss->anqp_nai_realm, &count);
+	realm = nai_realm_parse(bss->anqp->nai_realm, &count);
 	if (realm == NULL) {
 		wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI "
 			   "Realm list from " MACSTR, MAC2STR(bss->bssid));
@@ -1319,11 +1441,13 @@
 		int mnc_len = 0;
 		if (cred->imsi)
 			imsi = cred->imsi;
+#ifdef CONFIG_PCSC
 		else if (cred->pcsc && wpa_s->conf->pcsc_reader &&
 			 wpa_s->scard && wpa_s->imsi[0]) {
 			imsi = wpa_s->imsi;
 			mnc_len = wpa_s->mnc_len;
 		}
+#endif /* CONFIG_PCSC */
 		if (imsi && build_root_nai(nai, sizeof(nai), imsi, mnc_len, 0)
 		    == 0) {
 			realm = os_strchr(nai, '@');
@@ -1402,7 +1526,8 @@
 			continue;
 		}
 		count++;
-		res = interworking_home_sp(wpa_s, bss->anqp_domain_name);
+		res = interworking_home_sp(wpa_s, bss->anqp ?
+					   bss->anqp->domain_name : NULL);
 		if (res > 0)
 			type = "home";
 		else if (res == 0)
@@ -1465,6 +1590,38 @@
 }
 
 
+static struct wpa_bss_anqp *
+interworking_match_anqp_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+	struct wpa_bss *other;
+
+	if (is_zero_ether_addr(bss->hessid))
+		return NULL; /* Cannot be in the same homegenous ESS */
+
+	dl_list_for_each(other, &wpa_s->bss, struct wpa_bss, list) {
+		if (other == bss)
+			continue;
+		if (other->anqp == NULL)
+			continue;
+		if (!(other->flags & WPA_BSS_ANQP_FETCH_TRIED))
+			continue;
+		if (os_memcmp(bss->hessid, other->hessid, ETH_ALEN) != 0)
+			continue;
+		if (bss->ssid_len != other->ssid_len ||
+		    os_memcmp(bss->ssid, other->ssid, bss->ssid_len) != 0)
+			continue;
+
+		wpa_printf(MSG_DEBUG, "Interworking: Share ANQP data with "
+			   "already fetched BSSID " MACSTR " and " MACSTR,
+			   MAC2STR(other->bssid), MAC2STR(bss->bssid));
+		other->anqp->users++;
+		return other->anqp;
+	}
+
+	return NULL;
+}
+
+
 static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s)
 {
 	struct wpa_bss *bss;
@@ -1482,6 +1639,17 @@
 			continue; /* AP does not support Interworking */
 
 		if (!(bss->flags & WPA_BSS_ANQP_FETCH_TRIED)) {
+			if (bss->anqp == NULL) {
+				bss->anqp = interworking_match_anqp_info(wpa_s,
+									 bss);
+				if (bss->anqp) {
+					/* Shared data already fetched */
+					continue;
+				}
+				bss->anqp = wpa_bss_anqp_alloc();
+				if (bss->anqp == NULL)
+					break;
+			}
 			found++;
 			bss->flags |= WPA_BSS_ANQP_FETCH_TRIED;
 			wpa_msg(wpa_s, MSG_INFO, "Starting ANQP fetch for "
@@ -1518,6 +1686,7 @@
 		return 0;
 
 	wpa_s->network_select = 0;
+	wpa_s->fetch_all_anqp = 1;
 
 	interworking_start_fetch_anqp(wpa_s);
 
@@ -1576,10 +1745,14 @@
 {
 	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;
 #endif /* CONFIG_HS20 */
 
+	if (bss)
+		anqp = bss->anqp;
+
 	switch (info_id) {
 	case ANQP_CAPABILITY_LIST:
 		wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
@@ -1589,9 +1762,9 @@
 		wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
 			" Venue Name", MAC2STR(sa));
 		wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Venue Name", pos, slen);
-		if (bss) {
-			wpabuf_free(bss->anqp_venue_name);
-			bss->anqp_venue_name = wpabuf_alloc_copy(pos, slen);
+		if (anqp) {
+			wpabuf_free(anqp->venue_name);
+			anqp->venue_name = wpabuf_alloc_copy(pos, slen);
 		}
 		break;
 	case ANQP_NETWORK_AUTH_TYPE:
@@ -1600,10 +1773,9 @@
 			MAC2STR(sa));
 		wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Network Authentication "
 				  "Type", pos, slen);
-		if (bss) {
-			wpabuf_free(bss->anqp_network_auth_type);
-			bss->anqp_network_auth_type =
-				wpabuf_alloc_copy(pos, slen);
+		if (anqp) {
+			wpabuf_free(anqp->network_auth_type);
+			anqp->network_auth_type = wpabuf_alloc_copy(pos, slen);
 		}
 		break;
 	case ANQP_ROAMING_CONSORTIUM:
@@ -1611,10 +1783,9 @@
 			" Roaming Consortium list", MAC2STR(sa));
 		wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Roaming Consortium",
 				  pos, slen);
-		if (bss) {
-			wpabuf_free(bss->anqp_roaming_consortium);
-			bss->anqp_roaming_consortium =
-				wpabuf_alloc_copy(pos, slen);
+		if (anqp) {
+			wpabuf_free(anqp->roaming_consortium);
+			anqp->roaming_consortium = wpabuf_alloc_copy(pos, slen);
 		}
 		break;
 	case ANQP_IP_ADDR_TYPE_AVAILABILITY:
@@ -1623,9 +1794,9 @@
 			MAC2STR(sa));
 		wpa_hexdump(MSG_MSGDUMP, "ANQP: IP Address Availability",
 			    pos, slen);
-		if (bss) {
-			wpabuf_free(bss->anqp_ip_addr_type_availability);
-			bss->anqp_ip_addr_type_availability =
+		if (anqp) {
+			wpabuf_free(anqp->ip_addr_type_availability);
+			anqp->ip_addr_type_availability =
 				wpabuf_alloc_copy(pos, slen);
 		}
 		break;
@@ -1633,9 +1804,9 @@
 		wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
 			" NAI Realm list", MAC2STR(sa));
 		wpa_hexdump_ascii(MSG_DEBUG, "ANQP: NAI Realm", pos, slen);
-		if (bss) {
-			wpabuf_free(bss->anqp_nai_realm);
-			bss->anqp_nai_realm = wpabuf_alloc_copy(pos, slen);
+		if (anqp) {
+			wpabuf_free(anqp->nai_realm);
+			anqp->nai_realm = wpabuf_alloc_copy(pos, slen);
 		}
 		break;
 	case ANQP_3GPP_CELLULAR_NETWORK:
@@ -1643,18 +1814,18 @@
 			" 3GPP Cellular Network information", MAC2STR(sa));
 		wpa_hexdump_ascii(MSG_DEBUG, "ANQP: 3GPP Cellular Network",
 				  pos, slen);
-		if (bss) {
-			wpabuf_free(bss->anqp_3gpp);
-			bss->anqp_3gpp = wpabuf_alloc_copy(pos, slen);
+		if (anqp) {
+			wpabuf_free(anqp->anqp_3gpp);
+			anqp->anqp_3gpp = wpabuf_alloc_copy(pos, slen);
 		}
 		break;
 	case ANQP_DOMAIN_NAME:
 		wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
 			" Domain Name list", MAC2STR(sa));
 		wpa_hexdump_ascii(MSG_MSGDUMP, "ANQP: Domain Name", pos, slen);
-		if (bss) {
-			wpabuf_free(bss->anqp_domain_name);
-			bss->anqp_domain_name = wpabuf_alloc_copy(pos, slen);
+		if (anqp) {
+			wpabuf_free(anqp->domain_name);
+			anqp->domain_name = wpabuf_alloc_copy(pos, slen);
 		}
 		break;
 	case ANQP_VENDOR_SPECIFIC:
@@ -1760,6 +1931,7 @@
 	wpa_s->network_select = 1;
 	wpa_s->auto_network_select = 0;
 	wpa_s->auto_select = !!auto_select;
+	wpa_s->fetch_all_anqp = 0;
 	wpa_printf(MSG_DEBUG, "Interworking: Start scan for network "
 		   "selection");
 	wpa_s->scan_res_handler = interworking_scan_res_handler;
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index 2c07fd0..0b0ea88 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -586,7 +586,8 @@
 #	EAP-PSK/PAX/SAKE/GPSK.
 # anonymous_identity: Anonymous identity string for EAP (to be used as the
 #	unencrypted identity with EAP types that support different tunnelled
-#	identity, e.g., EAP-TTLS)
+#	identity, e.g., EAP-TTLS). This field can also be used with
+#	EAP-SIM/AKA/AKA' to store the pseudonym identity.
 # password: Password string for EAP. This field can include either the
 #	plaintext password (using ASCII or hex string) or a NtPasswordHash
 #	(16-byte MD4 hash of password) in hash:<32 hex digits> format.
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index fb8dba8..6011439 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -578,6 +578,7 @@
 	unsigned int network_select:1;
 	unsigned int auto_select:1;
 	unsigned int auto_network_select:1;
+	unsigned int fetch_all_anqp:1;
 #endif /* CONFIG_INTERWORKING */
 	unsigned int drv_capa_known;
 
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index fb4fa22..6aa5205 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - Glue code to setup EAPOL and RSN modules
- * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -729,6 +729,44 @@
 
 	wpas_notify_eap_status(wpa_s, status, parameter);
 }
+
+
+static void wpa_supplicant_set_anon_id(void *ctx, const u8 *id, size_t len)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	char *str;
+	int res;
+
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP method updated anonymous_identity",
+			  id, len);
+
+	if (wpa_s->current_ssid == NULL)
+		return;
+
+	if (id == NULL) {
+		if (wpa_config_set(wpa_s->current_ssid, "anonymous_identity",
+				   "NULL", 0) < 0)
+			return;
+	} else {
+		str = os_malloc(len * 2 + 1);
+		if (str == NULL)
+			return;
+		wpa_snprintf_hex(str, len * 2 + 1, id, len);
+		res = wpa_config_set(wpa_s->current_ssid, "anonymous_identity",
+				     str, 0);
+		os_free(str);
+		if (res < 0)
+			return;
+	}
+
+	if (wpa_s->conf->update_config) {
+		res = wpa_config_write(wpa_s->confname, wpa_s->conf);
+		if (res) {
+			wpa_printf(MSG_DEBUG, "Failed to update config after "
+				   "anonymous_id update");
+		}
+	}
+}
 #endif /* IEEE8021X_EAPOL */
 
 
@@ -761,6 +799,7 @@
 	ctx->cb = wpa_supplicant_eapol_cb;
 	ctx->cert_cb = wpa_supplicant_cert_cb;
 	ctx->status_cb = wpa_supplicant_status_cb;
+	ctx->set_anon_id = wpa_supplicant_set_anon_id;
 	ctx->cb_ctx = wpa_s;
 	wpa_s->eapol = eapol_sm_init(ctx);
 	if (wpa_s->eapol == NULL) {