wpa_supplicant: Update to 07-Jul-2012 TOT

commit a5ed45586c63ffd8f9d2b44e27c251d7bacbeaf4
Author: Jouni Malinen <j@w1.fi>
Date:   Sat Jul 7 13:01:45 2012 +0300

    WPS SSDP: Fix socket leaks on error paths

Change-Id: I0864aac7fc88fa2a60f5cca7d524b94363410c85
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/src/ap/accounting.c b/src/ap/accounting.c
index 2a04560..2c3a6d9 100644
--- a/src/ap/accounting.c
+++ b/src/ap/accounting.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / RADIUS Accounting
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2009, 2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -39,6 +39,8 @@
 	u8 *val;
 	size_t len;
 	int i;
+	struct wpabuf *b;
+	struct hostapd_radius_attr *attr;
 
 	msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST,
 			     radius_client_get_id(hapd->radius));
@@ -67,7 +69,9 @@
 		goto fail;
 	}
 
-	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC,
+	if (!hostapd_config_get_radius_attr(hapd->conf->radius_acct_req_attr,
+					    RADIUS_ATTR_ACCT_AUTHENTIC) &&
+	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC,
 				       hapd->conf->ieee802_1x ?
 				       RADIUS_ACCT_AUTHENTIC_RADIUS :
 				       RADIUS_ACCT_AUTHENTIC_LOCAL)) {
@@ -91,7 +95,9 @@
 		}
 	}
 
-	if (hapd->conf->own_ip_addr.af == AF_INET &&
+	if (!hostapd_config_get_radius_attr(hapd->conf->radius_acct_req_attr,
+					    RADIUS_ATTR_NAS_IP_ADDRESS) &&
+	    hapd->conf->own_ip_addr.af == AF_INET &&
 	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
 				 (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) {
 		printf("Could not add NAS-IP-Address\n");
@@ -99,7 +105,9 @@
 	}
 
 #ifdef CONFIG_IPV6
-	if (hapd->conf->own_ip_addr.af == AF_INET6 &&
+	if (!hostapd_config_get_radius_attr(hapd->conf->radius_acct_req_attr,
+					    RADIUS_ATTR_NAS_IPV6_ADDRESS) &&
+	    hapd->conf->own_ip_addr.af == AF_INET6 &&
 	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS,
 				 (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) {
 		printf("Could not add NAS-IPv6-Address\n");
@@ -107,7 +115,9 @@
 	}
 #endif /* CONFIG_IPV6 */
 
-	if (hapd->conf->nas_identifier &&
+	if (!hostapd_config_get_radius_attr(hapd->conf->radius_acct_req_attr,
+					    RADIUS_ATTR_NAS_IDENTIFIER) &&
+	    hapd->conf->nas_identifier &&
 	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
 				 (u8 *) hapd->conf->nas_identifier,
 				 os_strlen(hapd->conf->nas_identifier))) {
@@ -115,7 +125,9 @@
 		goto fail;
 	}
 
-	if (sta &&
+	if (!hostapd_config_get_radius_attr(hapd->conf->radius_acct_req_attr,
+					    RADIUS_ATTR_NAS_PORT) &&
+	    sta &&
 	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) {
 		printf("Could not add NAS-Port\n");
 		goto fail;
@@ -123,7 +135,9 @@
 
 	os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s",
 		    MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid);
-	if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID,
+	if (!hostapd_config_get_radius_attr(hapd->conf->radius_acct_req_attr,
+					    RADIUS_ATTR_CALLED_STATION_ID) &&
+	    !radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID,
 				 (u8 *) buf, os_strlen(buf))) {
 		printf("Could not add Called-Station-Id\n");
 		goto fail;
@@ -138,7 +152,10 @@
 			goto fail;
 		}
 
-		if (!radius_msg_add_attr_int32(
+		if (!hostapd_config_get_radius_attr(
+			    hapd->conf->radius_acct_req_attr,
+			    RADIUS_ATTR_NAS_PORT_TYPE) &&
+		    !radius_msg_add_attr_int32(
 			    msg, RADIUS_ATTR_NAS_PORT_TYPE,
 			    RADIUS_NAS_PORT_TYPE_IEEE_802_11)) {
 			printf("Could not add NAS-Port-Type\n");
@@ -149,7 +166,10 @@
 			    radius_sta_rate(hapd, sta) / 2,
 			    (radius_sta_rate(hapd, sta) & 1) ? ".5" : "",
 			    radius_mode_txt(hapd));
-		if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
+		if (!hostapd_config_get_radius_attr(
+			    hapd->conf->radius_acct_req_attr,
+			    RADIUS_ATTR_CONNECT_INFO) &&
+		    !radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
 					 (u8 *) buf, os_strlen(buf))) {
 			printf("Could not add Connect-Info\n");
 			goto fail;
@@ -167,6 +187,26 @@
 				goto fail;
 			}
 		}
+
+		b = ieee802_1x_get_radius_cui(sta->eapol_sm);
+		if (b &&
+		    !radius_msg_add_attr(msg,
+					 RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+					 wpabuf_head(b), wpabuf_len(b))) {
+			wpa_printf(MSG_ERROR, "Could not add CUI");
+			goto fail;
+		}
+	}
+
+	for (attr = hapd->conf->radius_acct_req_attr; attr; attr = attr->next)
+	{
+		if (!radius_msg_add_attr(msg, attr->type,
+					 wpabuf_head(attr->val),
+					 wpabuf_len(attr->val))) {
+			wpa_printf(MSG_ERROR, "Could not add RADIUS "
+				   "attribute");
+			goto fail;
+		}
 	}
 
 	return msg;
@@ -259,8 +299,9 @@
 			       hapd, sta);
 
 	msg = accounting_msg(hapd, sta, RADIUS_ACCT_STATUS_TYPE_START);
-	if (msg)
-		radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr);
+	if (msg &&
+	    radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr) < 0)
+		radius_msg_free(msg);
 
 	sta->acct_session_started = 1;
 }
@@ -358,9 +399,10 @@
 		goto fail;
 	}
 
-	radius_client_send(hapd->radius, msg,
-			   stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM,
-			   sta->addr);
+	if (radius_client_send(hapd->radius, msg,
+			       stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM,
+			       sta->addr) < 0)
+		goto fail;
 	return;
 
  fail:
@@ -463,7 +505,8 @@
 		return;
 	}
 
-	radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL);
+	if (radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL) < 0)
+		radius_msg_free(msg);
 }
 
 
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index b9f5994..811ffc1 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / Configuration helper functions
- * Copyright (c) 2003-2009, 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.
@@ -87,6 +87,8 @@
 #ifdef CONFIG_IEEE80211R
 	bss->ft_over_ds = 1;
 #endif /* CONFIG_IEEE80211R */
+
+	bss->radius_das_time_window = 300;
 }
 
 
@@ -336,6 +338,30 @@
 }
 
 
+struct hostapd_radius_attr *
+hostapd_config_get_radius_attr(struct hostapd_radius_attr *attr, u8 type)
+{
+	for (; attr; attr = attr->next) {
+		if (attr->type == type)
+			return attr;
+	}
+	return NULL;
+}
+
+
+static void hostapd_config_free_radius_attr(struct hostapd_radius_attr *attr)
+{
+	struct hostapd_radius_attr *prev;
+
+	while (attr) {
+		prev = attr;
+		attr = attr->next;
+		wpabuf_free(prev->val);
+		os_free(prev);
+	}
+}
+
+
 static void hostapd_config_free_eap_user(struct hostapd_eap_user *user)
 {
 	os_free(user->identity);
@@ -392,6 +418,8 @@
 				   conf->radius->num_auth_servers);
 	hostapd_config_free_radius(conf->radius->acct_servers,
 				   conf->radius->num_acct_servers);
+	hostapd_config_free_radius_attr(conf->radius_auth_req_attr);
+	hostapd_config_free_radius_attr(conf->radius_acct_req_attr);
 	os_free(conf->rsn_preauth_interfaces);
 	os_free(conf->ctrl_interface);
 	os_free(conf->ca_cert);
@@ -406,6 +434,7 @@
 	os_free(conf->radius_server_clients);
 	os_free(conf->test_socket);
 	os_free(conf->radius);
+	os_free(conf->radius_das_shared_secret);
 	hostapd_config_free_vlan(conf);
 	if (conf->ssid.dyn_vlan_keys) {
 		struct hostapd_ssid *ssid = &conf->ssid;
@@ -465,9 +494,13 @@
 	os_free(conf->model_description);
 	os_free(conf->model_url);
 	os_free(conf->upc);
+	wpabuf_free(conf->wps_nfc_dh_pubkey);
+	wpabuf_free(conf->wps_nfc_dh_privkey);
+	wpabuf_free(conf->wps_nfc_dev_pw);
 #endif /* CONFIG_WPS */
 
 	os_free(conf->roaming_consortium);
+	os_free(conf->venue_name);
 
 #ifdef CONFIG_RADIUS_TEST
 	os_free(conf->dump_msk_file);
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 2ec25ad..13d5549 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -1,6 +1,6 @@
 /*
  * hostapd / Configuration definitions and helpers functions
- * Copyright (c) 2003-2009, 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.
@@ -116,6 +116,12 @@
 	int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */
 };
 
+struct hostapd_radius_attr {
+	u8 type;
+	struct wpabuf *val;
+	struct hostapd_radius_attr *next;
+};
+
 
 #define NUM_TX_QUEUES 4
 
@@ -142,6 +148,12 @@
 	u8 oi[MAX_ROAMING_CONSORTIUM_LEN];
 };
 
+struct hostapd_venue_name {
+	u8 lang[3];
+	u8 name_len;
+	u8 name[252];
+};
+
 /**
  * struct hostapd_bss_config - Per-BSS configuration
  */
@@ -171,6 +183,15 @@
 	char *nas_identifier;
 	struct hostapd_radius_servers *radius;
 	int acct_interim_interval;
+	int radius_request_cui;
+	struct hostapd_radius_attr *radius_auth_req_attr;
+	struct hostapd_radius_attr *radius_acct_req_attr;
+	int radius_das_port;
+	unsigned int radius_das_time_window;
+	int radius_das_require_event_timestamp;
+	struct hostapd_ip_addr radius_das_client_addr;
+	u8 *radius_das_shared_secret;
+	size_t radius_das_shared_secret_len;
 
 	struct hostapd_ssid ssid;
 
@@ -326,6 +347,10 @@
 	char *model_url;
 	char *upc;
 	struct wpabuf *wps_vendor_ext[MAX_WPS_VENDOR_EXTENSIONS];
+	int wps_nfc_dev_pw_id;
+	struct wpabuf *wps_nfc_dh_pubkey;
+	struct wpabuf *wps_nfc_dh_privkey;
+	struct wpabuf *wps_nfc_dev_pw;
 #endif /* CONFIG_WPS */
 	int pbc_in_m1;
 
@@ -343,6 +368,7 @@
 #define TDLS_PROHIBIT_CHAN_SWITCH BIT(1)
 	int tdls;
 	int disable_11n;
+	int disable_11ac;
 
 	/* IEEE 802.11v */
 	int time_advertisement;
@@ -364,6 +390,13 @@
 	unsigned int roaming_consortium_count;
 	struct hostapd_roaming_consortium *roaming_consortium;
 
+	/* IEEE 802.11u - Venue Name duples */
+	unsigned int venue_name_count;
+	struct hostapd_venue_name *venue_name;
+
+	u16 gas_comeback_delay;
+	int gas_frag_limit;
+
 	u8 wps_rf_bands; /* RF bands for WPS (WPS_RF_*) */
 
 #ifdef CONFIG_RADIUS_TEST
@@ -423,6 +456,9 @@
 	int ieee80211n;
 	int secondary_channel;
 	int require_ht;
+	u32 vht_capab;
+	int ieee80211ac;
+	u8 vht_oper_chwidth;
 };
 
 
@@ -444,5 +480,7 @@
 const struct hostapd_eap_user *
 hostapd_get_eap_user(const struct hostapd_bss_config *conf, const u8 *identity,
 		     size_t identity_len, int phase2);
+struct hostapd_radius_attr *
+hostapd_config_get_radius_attr(struct hostapd_radius_attr *attr, u8 type);
 
 #endif /* HOSTAPD_CONFIG_H */
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index d07cc6b..859b529 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -584,3 +584,15 @@
 	return hapd->driver->sta_disassoc(hapd->drv_priv, hapd->own_addr, addr,
 					  reason);
 }
+
+
+int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
+			    unsigned int wait, const u8 *dst, const u8 *data,
+			    size_t len)
+{
+	if (hapd->driver == NULL || hapd->driver->send_action == NULL)
+		return 0;
+	return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst,
+					 hapd->own_addr, hapd->own_addr, data,
+					 len, 0);
+}
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index 8038fa2..169c91b 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -86,6 +86,9 @@
 			   const u8 *addr, int reason);
 int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
 			     const u8 *addr, int reason);
+int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
+			    unsigned int wait, const u8 *dst, const u8 *data,
+			    size_t len);
 int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr,
 			 u16 auth_alg);
 int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr,
diff --git a/src/ap/ap_list.c b/src/ap/ap_list.c
index b4252cf..933b158 100644
--- a/src/ap/ap_list.c
+++ b/src/ap/ap_list.c
@@ -289,10 +289,8 @@
 	ap->num_beacons++;
 	os_get_time(&now);
 	ap->last_beacon = now.sec;
-	if (fi) {
-		ap->ssi_signal = fi->ssi_signal;
+	if (fi)
 		ap->datarate = fi->datarate;
-	}
 
 	if (!new_ap && ap != iface->ap_list) {
 		/* move AP entry into the beginning of the list so that the
@@ -320,7 +318,7 @@
 #endif /* CONFIG_IEEE80211N */
 
 	if (set_beacon)
-		ieee802_11_set_beacons(iface);
+		ieee802_11_update_beacons(iface);
 }
 
 
@@ -375,7 +373,7 @@
 	}
 
 	if (set_beacon)
-		ieee802_11_set_beacons(iface);
+		ieee802_11_update_beacons(iface);
 }
 
 
diff --git a/src/ap/ap_list.h b/src/ap/ap_list.h
index 201f6ec..f0b4125 100644
--- a/src/ap/ap_list.h
+++ b/src/ap/ap_list.h
@@ -34,7 +34,6 @@
 
 	int channel;
 	int datarate; /* in 100 kbps */
-	int ssi_signal;
 
 	int ht_support;
 
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 4ea8684..0f29ccd 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -262,6 +262,11 @@
 	pos = hostapd_eid_adv_proto(hapd, pos);
 	pos = hostapd_eid_roaming_consortium(hapd, pos);
 
+#ifdef CONFIG_IEEE80211AC
+	pos = hostapd_eid_vht_capabilities(hapd, pos);
+	pos = hostapd_eid_vht_operation(hapd, pos);
+#endif /* CONFIG_IEEE80211AC */
+
 	/* Wi-Fi Alliance WMM */
 	pos = hostapd_eid_wmm(hapd, pos);
 
@@ -293,7 +298,8 @@
 
 
 void handle_probe_req(struct hostapd_data *hapd,
-		      const struct ieee80211_mgmt *mgmt, size_t len)
+		      const struct ieee80211_mgmt *mgmt, size_t len,
+		      int ssi_signal)
 {
 	u8 *resp;
 	struct ieee802_11_elems elems;
@@ -311,7 +317,7 @@
 	for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++)
 		if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx,
 					    mgmt->sa, mgmt->da, mgmt->bssid,
-					    ie, ie_len) > 0)
+					    ie, ie_len, ssi_signal) > 0)
 			return;
 
 	if (!hapd->iconf->send_probe_response)
@@ -594,6 +600,11 @@
 	tailpos = hostapd_eid_adv_proto(hapd, tailpos);
 	tailpos = hostapd_eid_roaming_consortium(hapd, tailpos);
 
+#ifdef CONFIG_IEEE80211AC
+	tailpos = hostapd_eid_vht_capabilities(hapd, tailpos);
+	tailpos = hostapd_eid_vht_operation(hapd, tailpos);
+#endif /* CONFIG_IEEE80211AC */
+
 	/* Wi-Fi Alliance WMM */
 	tailpos = hostapd_eid_wmm(hapd, tailpos);
 
@@ -682,6 +693,7 @@
 	    !is_zero_ether_addr(hapd->conf->hessid))
 		params.hessid = hapd->conf->hessid;
 	params.access_network_type = hapd->conf->access_network_type;
+	params.ap_max_inactivity = hapd->conf->ap_max_inactivity;
 	if (hostapd_drv_set_ap(hapd, &params))
 		wpa_printf(MSG_ERROR, "Failed to set beacon parameters");
 	hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp);
@@ -699,4 +711,14 @@
 		ieee802_11_set_beacon(iface->bss[i]);
 }
 
+
+/* only update beacons if started */
+void ieee802_11_update_beacons(struct hostapd_iface *iface)
+{
+	size_t i;
+	for (i = 0; i < iface->num_bss; i++)
+		if (iface->bss[i]->beacon_set_done)
+			ieee802_11_set_beacon(iface->bss[i]);
+}
+
 #endif /* CONFIG_NATIVE_WINDOWS */
diff --git a/src/ap/beacon.h b/src/ap/beacon.h
index a944f5f..37f10d2 100644
--- a/src/ap/beacon.h
+++ b/src/ap/beacon.h
@@ -19,8 +19,10 @@
 struct ieee80211_mgmt;
 
 void handle_probe_req(struct hostapd_data *hapd,
-		      const struct ieee80211_mgmt *mgmt, size_t len);
+		      const struct ieee80211_mgmt *mgmt, size_t len,
+		      int ssi_signal);
 void ieee802_11_set_beacon(struct hostapd_data *hapd);
 void ieee802_11_set_beacons(struct hostapd_iface *iface);
+void ieee802_11_update_beacons(struct hostapd_iface *iface);
 
 #endif /* BEACON_H */
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index d76b381..ab9c83e 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -9,6 +9,7 @@
 #include "utils/includes.h"
 
 #include "utils/common.h"
+#include "common/ieee802_11_defs.h"
 #include "hostapd.h"
 #include "ieee802_1x.h"
 #include "wpa_auth.h"
@@ -17,6 +18,7 @@
 #include "wps_hostapd.h"
 #include "p2p_hostapd.h"
 #include "ctrl_iface_ap.h"
+#include "ap_drv_ops.h"
 
 
 static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd,
@@ -100,3 +102,170 @@
 	}		
 	return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen);
 }
+
+
+#ifdef CONFIG_P2P_MANAGER
+static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype,
+				  u8 minor_reason_code, const u8 *addr)
+{
+	struct ieee80211_mgmt *mgmt;
+	int ret;
+	u8 *pos;
+
+	if (hapd->driver->send_frame == NULL)
+		return -1;
+
+	mgmt = os_zalloc(sizeof(*mgmt) + 100);
+	if (mgmt == NULL)
+		return -1;
+
+	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "P2P: Disconnect STA " MACSTR
+		" with minor reason code %u (stype=%u)",
+		MAC2STR(addr), minor_reason_code, stype);
+
+	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, stype);
+	os_memcpy(mgmt->da, addr, ETH_ALEN);
+	os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+	os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+	if (stype == WLAN_FC_STYPE_DEAUTH) {
+		mgmt->u.deauth.reason_code =
+			host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
+		pos = (u8 *) (&mgmt->u.deauth.reason_code + 1);
+	} else {
+		mgmt->u.disassoc.reason_code =
+			host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
+		pos = (u8 *) (&mgmt->u.disassoc.reason_code + 1);
+	}
+
+	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
+	*pos++ = 4 + 3 + 1;
+	WPA_PUT_BE24(pos, OUI_WFA);
+	pos += 3;
+	*pos++ = P2P_OUI_TYPE;
+
+	*pos++ = P2P_ATTR_MINOR_REASON_CODE;
+	WPA_PUT_LE16(pos, 1);
+	pos += 2;
+	*pos++ = minor_reason_code;
+
+	ret = hapd->driver->send_frame(hapd->drv_priv, (u8 *) mgmt,
+				       pos - (u8 *) mgmt, 1);
+	os_free(mgmt);
+
+	return ret < 0 ? -1 : 0;
+}
+#endif /* CONFIG_P2P_MANAGER */
+
+
+int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd,
+				      const char *txtaddr)
+{
+	u8 addr[ETH_ALEN];
+	struct sta_info *sta;
+	const char *pos;
+
+	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DEAUTHENTICATE %s",
+		txtaddr);
+
+	if (hwaddr_aton(txtaddr, addr))
+		return -1;
+
+	pos = os_strstr(txtaddr, " test=");
+	if (pos) {
+		struct ieee80211_mgmt mgmt;
+		int encrypt;
+		if (hapd->driver->send_frame == NULL)
+			return -1;
+		pos += 6;
+		encrypt = atoi(pos);
+		os_memset(&mgmt, 0, sizeof(mgmt));
+		mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+						  WLAN_FC_STYPE_DEAUTH);
+		os_memcpy(mgmt.da, addr, ETH_ALEN);
+		os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
+		os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
+		mgmt.u.deauth.reason_code =
+			host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
+		if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt,
+					     IEEE80211_HDRLEN +
+					     sizeof(mgmt.u.deauth),
+					     encrypt) < 0)
+			return -1;
+		return 0;
+	}
+
+#ifdef CONFIG_P2P_MANAGER
+	pos = os_strstr(txtaddr, " p2p=");
+	if (pos) {
+		return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DEAUTH,
+					      atoi(pos + 5), addr);
+	}
+#endif /* CONFIG_P2P_MANAGER */
+
+	hostapd_drv_sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
+	sta = ap_get_sta(hapd, addr);
+	if (sta)
+		ap_sta_deauthenticate(hapd, sta,
+				      WLAN_REASON_PREV_AUTH_NOT_VALID);
+	else if (addr[0] == 0xff)
+		hostapd_free_stas(hapd);
+
+	return 0;
+}
+
+
+int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd,
+				    const char *txtaddr)
+{
+	u8 addr[ETH_ALEN];
+	struct sta_info *sta;
+	const char *pos;
+
+	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DISASSOCIATE %s",
+		txtaddr);
+
+	if (hwaddr_aton(txtaddr, addr))
+		return -1;
+
+	pos = os_strstr(txtaddr, " test=");
+	if (pos) {
+		struct ieee80211_mgmt mgmt;
+		int encrypt;
+		if (hapd->driver->send_frame == NULL)
+			return -1;
+		pos += 6;
+		encrypt = atoi(pos);
+		os_memset(&mgmt, 0, sizeof(mgmt));
+		mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+						  WLAN_FC_STYPE_DISASSOC);
+		os_memcpy(mgmt.da, addr, ETH_ALEN);
+		os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
+		os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
+		mgmt.u.disassoc.reason_code =
+			host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
+		if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt,
+					     IEEE80211_HDRLEN +
+					     sizeof(mgmt.u.deauth),
+					     encrypt) < 0)
+			return -1;
+		return 0;
+	}
+
+#ifdef CONFIG_P2P_MANAGER
+	pos = os_strstr(txtaddr, " p2p=");
+	if (pos) {
+		return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DISASSOC,
+					      atoi(pos + 5), addr);
+	}
+#endif /* CONFIG_P2P_MANAGER */
+
+	hostapd_drv_sta_disassoc(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
+	sta = ap_get_sta(hapd, addr);
+	if (sta)
+		ap_sta_disassociate(hapd, sta,
+				    WLAN_REASON_PREV_AUTH_NOT_VALID);
+	else if (addr[0] == 0xff)
+		hostapd_free_stas(hapd);
+
+	return 0;
+}
diff --git a/src/ap/ctrl_iface_ap.h b/src/ap/ctrl_iface_ap.h
index 0e4286d..e83f894 100644
--- a/src/ap/ctrl_iface_ap.h
+++ b/src/ap/ctrl_iface_ap.h
@@ -15,5 +15,9 @@
 			   char *buf, size_t buflen);
 int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr,
 				char *buf, size_t buflen);
+int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd,
+				      const char *txtaddr);
+int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd,
+				    const char *txtaddr);
 
 #endif /* CTRL_IFACE_AP_H */
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index 820a903..cf06a4f 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -26,6 +26,7 @@
 #include "wps_hostapd.h"
 #include "ap_drv_ops.h"
 #include "ap_config.h"
+#include "hw_features.h"
 
 
 int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
@@ -78,10 +79,19 @@
 	sta = ap_get_sta(hapd, addr);
 	if (sta) {
 		accounting_sta_stop(hapd, sta);
+
+		/*
+		 * Make sure that the previously registered inactivity timer
+		 * will not remove the STA immediately.
+		 */
+		sta->timeout_next = STA_NULLFUNC;
 	} else {
 		sta = ap_sta_add(hapd, addr);
-		if (sta == NULL)
+		if (sta == NULL) {
+			hostapd_drv_sta_disassoc(hapd, addr,
+						 WLAN_REASON_DISASSOC_AP_BUSY);
 			return -1;
+		}
 	}
 	sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2);
 
@@ -264,8 +274,36 @@
 }
 
 
+void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
+			     int offset)
+{
+#ifdef NEED_AP_MLME
+	int channel;
+
+	hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_INFO, "driver had channel switch: "
+		       "freq=%d, ht=%d, offset=%d", freq, ht, offset);
+
+	hapd->iface->freq = freq;
+
+	channel = hostapd_hw_get_channel(hapd, freq);
+	if (!channel) {
+		hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_WARNING, "driver switched to "
+			       "bad channel!");
+		return;
+	}
+
+	hapd->iconf->channel = channel;
+	hapd->iconf->ieee80211n = ht;
+	hapd->iconf->secondary_channel = offset;
+#endif /* NEED_AP_MLME */
+}
+
+
 int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da,
-			 const u8 *bssid, const u8 *ie, size_t ie_len)
+			 const u8 *bssid, const u8 *ie, size_t ie_len,
+			 int ssi_signal)
 {
 	size_t i;
 	int ret = 0;
@@ -276,7 +314,8 @@
 	random_add_randomness(sa, ETH_ALEN);
 	for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++) {
 		if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx,
-					    sa, da, bssid, ie, ie_len) > 0) {
+					    sa, da, bssid, ie, ie_len,
+					    ssi_signal) > 0) {
 			ret = 1;
 			break;
 		}
@@ -541,7 +580,8 @@
 				     data->rx_probe_req.da,
 				     data->rx_probe_req.bssid,
 				     data->rx_probe_req.ie,
-				     data->rx_probe_req.ie_len);
+				     data->rx_probe_req.ie_len,
+				     data->rx_probe_req.ssi_signal);
 		break;
 	case EVENT_NEW_STA:
 		hostapd_event_new_sta(hapd, data->new_sta.addr);
@@ -578,6 +618,13 @@
 		hostapd_rx_action(hapd, &data->rx_action);
 		break;
 #endif /* NEED_AP_MLME */
+	case EVENT_CH_SWITCH:
+		if (!data)
+			break;
+		hostapd_event_ch_switch(hapd, data->ch_switch.freq,
+					data->ch_switch.ht_enabled,
+					data->ch_switch.ch_offset);
+		break;
 	default:
 		wpa_printf(MSG_DEBUG, "Unknown event %d", event);
 		break;
diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c
new file mode 100644
index 0000000..2177b02
--- /dev/null
+++ b/src/ap/gas_serv.c
@@ -0,0 +1,673 @@
+/*
+ * Generic advertisement service (GAS) server
+ * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/gas.h"
+#include "utils/eloop.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "sta_info.h"
+#include "gas_serv.h"
+
+
+static struct gas_dialog_info *
+gas_dialog_create(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token)
+{
+	struct sta_info *sta;
+	struct gas_dialog_info *dia = NULL;
+	int i, j;
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta) {
+		/*
+		 * We need a STA entry to be able to maintain state for
+		 * the GAS query.
+		 */
+		wpa_printf(MSG_DEBUG, "ANQP: Add a temporary STA entry for "
+			   "GAS query");
+		sta = ap_sta_add(hapd, addr);
+		if (!sta) {
+			wpa_printf(MSG_DEBUG, "Failed to add STA " MACSTR
+				   " for GAS query", MAC2STR(addr));
+			return NULL;
+		}
+		sta->flags |= WLAN_STA_GAS;
+		/*
+		 * The default inactivity is 300 seconds. We don't need
+		 * it to be that long.
+		 */
+		ap_sta_session_timeout(hapd, sta, 5);
+	}
+
+	if (sta->gas_dialog == NULL) {
+		sta->gas_dialog = os_zalloc(GAS_DIALOG_MAX *
+					    sizeof(struct gas_dialog_info));
+		if (sta->gas_dialog == NULL)
+			return NULL;
+	}
+
+	for (i = sta->gas_dialog_next, j = 0; j < GAS_DIALOG_MAX; i++, j++) {
+		if (i == GAS_DIALOG_MAX)
+			i = 0;
+		if (sta->gas_dialog[i].valid)
+			continue;
+		dia = &sta->gas_dialog[i];
+		dia->valid = 1;
+		dia->index = i;
+		dia->dialog_token = dialog_token;
+		sta->gas_dialog_next = (++i == GAS_DIALOG_MAX) ? 0 : i;
+		return dia;
+	}
+
+	wpa_msg(hapd->msg_ctx, MSG_ERROR, "ANQP: Could not create dialog for "
+		MACSTR " dialog_token %u. Consider increasing "
+		"GAS_DIALOG_MAX.", MAC2STR(addr), dialog_token);
+
+	return NULL;
+}
+
+
+struct gas_dialog_info *
+gas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr,
+		     u8 dialog_token)
+{
+	struct sta_info *sta;
+	int i;
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta) {
+		wpa_printf(MSG_DEBUG, "ANQP: could not find STA " MACSTR,
+			   MAC2STR(addr));
+		return NULL;
+	}
+	for (i = 0; sta->gas_dialog && i < GAS_DIALOG_MAX; i++) {
+		if (sta->gas_dialog[i].dialog_token != dialog_token ||
+		    !sta->gas_dialog[i].valid)
+			continue;
+		return &sta->gas_dialog[i];
+	}
+	wpa_printf(MSG_DEBUG, "ANQP: Could not find dialog for "
+		   MACSTR " dialog_token %u", MAC2STR(addr), dialog_token);
+	return NULL;
+}
+
+
+void gas_serv_dialog_clear(struct gas_dialog_info *dia)
+{
+	wpabuf_free(dia->sd_resp);
+	os_memset(dia, 0, sizeof(*dia));
+}
+
+
+static void gas_serv_free_dialogs(struct hostapd_data *hapd,
+				  const u8 *sta_addr)
+{
+	struct sta_info *sta;
+	int i;
+
+	sta = ap_get_sta(hapd, sta_addr);
+	if (sta == NULL || sta->gas_dialog == NULL)
+		return;
+
+	for (i = 0; i < GAS_DIALOG_MAX; i++) {
+		if (sta->gas_dialog[i].valid)
+			return;
+	}
+
+	os_free(sta->gas_dialog);
+	sta->gas_dialog = NULL;
+}
+
+
+static void anqp_add_capab_list(struct hostapd_data *hapd,
+				struct wpabuf *buf)
+{
+	u8 *len;
+
+	len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST);
+	wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST);
+	if (hapd->conf->venue_name)
+		wpabuf_put_le16(buf, ANQP_VENUE_NAME);
+	if (hapd->conf->roaming_consortium)
+		wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM);
+	gas_anqp_set_element_len(buf, len);
+}
+
+
+static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf)
+{
+	if (hapd->conf->venue_name) {
+		u8 *len;
+		unsigned int i;
+		len = gas_anqp_add_element(buf, ANQP_VENUE_NAME);
+		wpabuf_put_u8(buf, hapd->conf->venue_group);
+		wpabuf_put_u8(buf, hapd->conf->venue_type);
+		for (i = 0; i < hapd->conf->venue_name_count; i++) {
+			struct hostapd_venue_name *vn;
+			vn = &hapd->conf->venue_name[i];
+			wpabuf_put_u8(buf, 3 + vn->name_len);
+			wpabuf_put_data(buf, vn->lang, 3);
+			wpabuf_put_data(buf, vn->name, vn->name_len);
+		}
+		gas_anqp_set_element_len(buf, len);
+	}
+}
+
+
+static void anqp_add_roaming_consortium(struct hostapd_data *hapd,
+					struct wpabuf *buf)
+{
+	unsigned int i;
+	u8 *len;
+
+	len = gas_anqp_add_element(buf, ANQP_ROAMING_CONSORTIUM);
+	for (i = 0; i < hapd->conf->roaming_consortium_count; i++) {
+		struct hostapd_roaming_consortium *rc;
+		rc = &hapd->conf->roaming_consortium[i];
+		wpabuf_put_u8(buf, rc->len);
+		wpabuf_put_data(buf, rc->oi, rc->len);
+	}
+	gas_anqp_set_element_len(buf, len);
+}
+
+
+static struct wpabuf *
+gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
+				unsigned int request,
+				struct gas_dialog_info *di)
+{
+	struct wpabuf *buf;
+
+	buf = wpabuf_alloc(1400);
+	if (buf == NULL)
+		return NULL;
+
+	if (request & ANQP_REQ_CAPABILITY_LIST)
+		anqp_add_capab_list(hapd, buf);
+	if (request & ANQP_REQ_VENUE_NAME)
+		anqp_add_venue_name(hapd, buf);
+	if (request & ANQP_REQ_ROAMING_CONSORTIUM)
+		anqp_add_roaming_consortium(hapd, buf);
+
+	return buf;
+}
+
+
+static void gas_serv_clear_cached_ies(void *eloop_data, void *user_ctx)
+{
+	struct gas_dialog_info *dia = eloop_data;
+
+	wpa_printf(MSG_DEBUG, "GAS: Timeout triggered, clearing dialog for "
+		   "dialog token %d", dia->dialog_token);
+
+	gas_serv_dialog_clear(dia);
+}
+
+
+struct anqp_query_info {
+	unsigned int request;
+	unsigned int remote_request;
+	const void *param;
+	u32 param_arg;
+	u16 remote_delay;
+};
+
+
+static void set_anqp_req(unsigned int bit, const char *name, int local,
+			 unsigned int remote, u16 remote_delay,
+			 struct anqp_query_info *qi)
+{
+	qi->request |= bit;
+	if (local) {
+		wpa_printf(MSG_DEBUG, "ANQP: %s (local)", name);
+	} else if (bit & remote) {
+		wpa_printf(MSG_DEBUG, "ANQP: %s (remote)", name);
+		qi->remote_request |= bit;
+		if (remote_delay > qi->remote_delay)
+			qi->remote_delay = remote_delay;
+	} else {
+		wpa_printf(MSG_DEBUG, "ANQP: %s not available", name);
+	}
+}
+
+
+static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id,
+				  struct anqp_query_info *qi)
+{
+	switch (info_id) {
+	case ANQP_CAPABILITY_LIST:
+		set_anqp_req(ANQP_REQ_CAPABILITY_LIST, "Capability List", 1, 0,
+			     0, qi);
+		break;
+	case ANQP_VENUE_NAME:
+		set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name",
+			     hapd->conf->venue_name != NULL, 0, 0, qi);
+		break;
+	case ANQP_ROAMING_CONSORTIUM:
+		set_anqp_req(ANQP_REQ_ROAMING_CONSORTIUM, "Roaming Consortium",
+			     hapd->conf->roaming_consortium != NULL, 0, 0, qi);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
+			   info_id);
+		break;
+	}
+}
+
+
+static void rx_anqp_query_list(struct hostapd_data *hapd,
+			       const u8 *pos, const u8 *end,
+			       struct anqp_query_info *qi)
+{
+	wpa_printf(MSG_DEBUG, "ANQP: %u Info IDs requested in Query list",
+		   (unsigned int) (end - pos) / 2);
+
+	while (pos + 2 <= end) {
+		rx_anqp_query_list_id(hapd, WPA_GET_LE16(pos), qi);
+		pos += 2;
+	}
+}
+
+
+static void gas_serv_req_local_processing(struct hostapd_data *hapd,
+					  const u8 *sa, u8 dialog_token,
+					  struct anqp_query_info *qi)
+{
+	struct wpabuf *buf, *tx_buf;
+
+	buf = gas_serv_build_gas_resp_payload(hapd, qi->request, NULL);
+	wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses",
+			buf);
+	if (!buf)
+		return;
+
+	if (wpabuf_len(buf) > hapd->gas_frag_limit ||
+	    hapd->conf->gas_comeback_delay) {
+		struct gas_dialog_info *di;
+		u16 comeback_delay = 1;
+
+		if (hapd->conf->gas_comeback_delay) {
+			/* Testing - allow overriding of the delay value */
+			comeback_delay = hapd->conf->gas_comeback_delay;
+		}
+
+		wpa_printf(MSG_DEBUG, "ANQP: Too long response to fit in "
+			   "initial response - use GAS comeback");
+		di = gas_dialog_create(hapd, sa, dialog_token);
+		if (!di) {
+			wpa_printf(MSG_INFO, "ANQP: Could not create dialog "
+				   "for " MACSTR " (dialog token %u)",
+				   MAC2STR(sa), dialog_token);
+			wpabuf_free(buf);
+			return;
+		}
+		di->sd_resp = buf;
+		di->sd_resp_pos = 0;
+		tx_buf = gas_anqp_build_initial_resp_buf(
+			dialog_token, WLAN_STATUS_SUCCESS, comeback_delay,
+			NULL);
+	} else {
+		wpa_printf(MSG_DEBUG, "ANQP: Initial response (no comeback)");
+		tx_buf = gas_anqp_build_initial_resp_buf(
+			dialog_token, WLAN_STATUS_SUCCESS, 0, buf);
+		wpabuf_free(buf);
+	}
+	if (!tx_buf)
+		return;
+
+	hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
+				wpabuf_head(tx_buf), wpabuf_len(tx_buf));
+	wpabuf_free(tx_buf);
+}
+
+
+static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
+					const u8 *sa,
+					const u8 *data, size_t len)
+{
+	const u8 *pos = data;
+	const u8 *end = data + len;
+	const u8 *next;
+	u8 dialog_token;
+	u16 slen;
+	struct anqp_query_info qi;
+	const u8 *adv_proto;
+
+	if (len < 1 + 2)
+		return;
+
+	os_memset(&qi, 0, sizeof(qi));
+
+	dialog_token = *pos++;
+	wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+		"GAS: GAS Initial Request from " MACSTR " (dialog token %u) ",
+		MAC2STR(sa), dialog_token);
+
+	if (*pos != WLAN_EID_ADV_PROTO) {
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+			"GAS: Unexpected IE in GAS Initial Request: %u", *pos);
+		return;
+	}
+	adv_proto = pos++;
+
+	slen = *pos++;
+	next = pos + slen;
+	if (next > end || slen < 2) {
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+			"GAS: Invalid IE in GAS Initial Request");
+		return;
+	}
+	pos++; /* skip QueryRespLenLimit and PAME-BI */
+
+	if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
+		struct wpabuf *buf;
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+			"GAS: Unsupported GAS advertisement protocol id %u",
+			*pos);
+		if (sa[0] & 0x01)
+			return; /* Invalid source address - drop silently */
+		buf = gas_build_initial_resp(
+			dialog_token, WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED,
+			0, 2 + slen + 2);
+		if (buf == NULL)
+			return;
+		wpabuf_put_data(buf, adv_proto, 2 + slen);
+		wpabuf_put_le16(buf, 0); /* Query Response Length */
+		hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
+					wpabuf_head(buf), wpabuf_len(buf));
+		wpabuf_free(buf);
+		return;
+	}
+
+	pos = next;
+	/* Query Request */
+	if (pos + 2 > end)
+		return;
+	slen = WPA_GET_LE16(pos);
+	pos += 2;
+	if (pos + slen > end)
+		return;
+	end = pos + slen;
+
+	/* ANQP Query Request */
+	while (pos < end) {
+		u16 info_id, elen;
+
+		if (pos + 4 > end)
+			return;
+
+		info_id = WPA_GET_LE16(pos);
+		pos += 2;
+		elen = WPA_GET_LE16(pos);
+		pos += 2;
+
+		if (pos + elen > end) {
+			wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request");
+			return;
+		}
+
+		switch (info_id) {
+		case ANQP_QUERY_LIST:
+			rx_anqp_query_list(hapd, pos, pos + elen, &qi);
+			break;
+		default:
+			wpa_printf(MSG_DEBUG, "ANQP: Unsupported Query "
+				   "Request element %u", info_id);
+			break;
+		}
+
+		pos += elen;
+	}
+
+	gas_serv_req_local_processing(hapd, sa, dialog_token, &qi);
+}
+
+
+void gas_serv_tx_gas_response(struct hostapd_data *hapd, const u8 *dst,
+			      struct gas_dialog_info *dialog)
+{
+	struct wpabuf *buf, *tx_buf;
+	u8 dialog_token = dialog->dialog_token;
+	size_t frag_len;
+
+	if (dialog->sd_resp == NULL) {
+		buf = gas_serv_build_gas_resp_payload(hapd,
+						      dialog->all_requested,
+						      dialog);
+		wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses",
+			buf);
+		if (!buf)
+			goto tx_gas_response_done;
+		dialog->sd_resp = buf;
+		dialog->sd_resp_pos = 0;
+	}
+	frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
+	if (frag_len > hapd->gas_frag_limit || dialog->comeback_delay ||
+	    hapd->conf->gas_comeback_delay) {
+		u16 comeback_delay_tus = dialog->comeback_delay +
+			GAS_SERV_COMEBACK_DELAY_FUDGE;
+		u32 comeback_delay_secs, comeback_delay_usecs;
+
+		if (hapd->conf->gas_comeback_delay) {
+			/* Testing - allow overriding of the delay value */
+			comeback_delay_tus = hapd->conf->gas_comeback_delay;
+		}
+
+		wpa_printf(MSG_DEBUG, "GAS: Response frag_len %u (frag limit "
+			   "%u) and comeback delay %u, "
+			   "requesting comebacks", (unsigned int) frag_len,
+			   (unsigned int) hapd->gas_frag_limit,
+			   dialog->comeback_delay);
+		tx_buf = gas_anqp_build_initial_resp_buf(dialog_token,
+							 WLAN_STATUS_SUCCESS,
+							 comeback_delay_tus,
+							 NULL);
+		if (tx_buf) {
+			wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+				"GAS: Tx GAS Initial Resp (comeback = 10TU)");
+			hostapd_drv_send_action(hapd, hapd->iface->freq, 0,
+						dst,
+						wpabuf_head(tx_buf),
+						wpabuf_len(tx_buf));
+		}
+		wpabuf_free(tx_buf);
+
+		/* start a timer of 1.5 * comeback-delay */
+		comeback_delay_tus = comeback_delay_tus +
+			(comeback_delay_tus / 2);
+		comeback_delay_secs = (comeback_delay_tus * 1024) / 1000000;
+		comeback_delay_usecs = (comeback_delay_tus * 1024) -
+			(comeback_delay_secs * 1000000);
+		eloop_register_timeout(comeback_delay_secs,
+				       comeback_delay_usecs,
+				       gas_serv_clear_cached_ies, dialog,
+				       NULL);
+		goto tx_gas_response_done;
+	}
+
+	buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) +
+				dialog->sd_resp_pos, frag_len);
+	if (buf == NULL) {
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Buffer allocation "
+			"failed");
+		goto tx_gas_response_done;
+	}
+	tx_buf = gas_anqp_build_initial_resp_buf(dialog_token,
+						 WLAN_STATUS_SUCCESS, 0, buf);
+	wpabuf_free(buf);
+	if (tx_buf == NULL)
+		goto tx_gas_response_done;
+	wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Initial "
+		"Response (frag_id %d frag_len %d)",
+		dialog->sd_frag_id, (int) frag_len);
+	dialog->sd_frag_id++;
+
+	hostapd_drv_send_action(hapd, hapd->iface->freq, 0, dst,
+				wpabuf_head(tx_buf), wpabuf_len(tx_buf));
+	wpabuf_free(tx_buf);
+tx_gas_response_done:
+	gas_serv_clear_cached_ies(dialog, NULL);
+}
+
+
+static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
+					 const u8 *sa,
+					 const u8 *data, size_t len)
+{
+	struct gas_dialog_info *dialog;
+	struct wpabuf *buf, *tx_buf;
+	u8 dialog_token;
+	size_t frag_len;
+	int more = 0;
+
+	wpa_hexdump(MSG_DEBUG, "GAS: RX GAS Comeback Request", data, len);
+	if (len < 1)
+		return;
+	dialog_token = *data;
+	wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Dialog Token: %u",
+		dialog_token);
+
+	dialog = gas_serv_dialog_find(hapd, sa, dialog_token);
+	if (!dialog) {
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: No pending SD "
+			"response fragment for " MACSTR " dialog token %u",
+			MAC2STR(sa), dialog_token);
+
+		if (sa[0] & 0x01)
+			return; /* Invalid source address - drop silently */
+		tx_buf = gas_anqp_build_comeback_resp_buf(
+			dialog_token, WLAN_STATUS_NO_OUTSTANDING_GAS_REQ, 0, 0,
+			0, NULL);
+		if (tx_buf == NULL)
+			return;
+		goto send_resp;
+	}
+
+	if (dialog->sd_resp == NULL) {
+		wpa_printf(MSG_DEBUG, "GAS: Remote request 0x%x received 0x%x",
+			   dialog->requested, dialog->received);
+		if ((dialog->requested & dialog->received) !=
+		    dialog->requested) {
+			wpa_printf(MSG_DEBUG, "GAS: Did not receive response "
+				   "from remote processing");
+			gas_serv_dialog_clear(dialog);
+			tx_buf = gas_anqp_build_comeback_resp_buf(
+				dialog_token,
+				WLAN_STATUS_GAS_RESP_NOT_RECEIVED, 0, 0, 0,
+				NULL);
+			if (tx_buf == NULL)
+				return;
+			goto send_resp;
+		}
+
+		buf = gas_serv_build_gas_resp_payload(hapd,
+						      dialog->all_requested,
+						      dialog);
+		wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses",
+			buf);
+		if (!buf)
+			goto rx_gas_comeback_req_done;
+		dialog->sd_resp = buf;
+		dialog->sd_resp_pos = 0;
+	}
+	frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
+	if (frag_len > hapd->gas_frag_limit) {
+		frag_len = hapd->gas_frag_limit;
+		more = 1;
+	}
+	wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: resp frag_len %u",
+		(unsigned int) frag_len);
+	buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) +
+				dialog->sd_resp_pos, frag_len);
+	if (buf == NULL) {
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Failed to allocate "
+			"buffer");
+		goto rx_gas_comeback_req_done;
+	}
+	tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token,
+						  WLAN_STATUS_SUCCESS,
+						  dialog->sd_frag_id,
+						  more, 0, buf);
+	wpabuf_free(buf);
+	if (tx_buf == NULL)
+		goto rx_gas_comeback_req_done;
+	wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Comeback Response "
+		"(frag_id %d more=%d frag_len=%d)",
+		dialog->sd_frag_id, more, (int) frag_len);
+	dialog->sd_frag_id++;
+	dialog->sd_resp_pos += frag_len;
+
+	if (more) {
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: %d more bytes remain "
+			"to be sent",
+			(int) (wpabuf_len(dialog->sd_resp) -
+			       dialog->sd_resp_pos));
+	} else {
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of "
+			"SD response sent");
+		gas_serv_dialog_clear(dialog);
+		gas_serv_free_dialogs(hapd, sa);
+	}
+
+send_resp:
+	hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
+				wpabuf_head(tx_buf), wpabuf_len(tx_buf));
+	wpabuf_free(tx_buf);
+	return;
+
+rx_gas_comeback_req_done:
+	gas_serv_clear_cached_ies(dialog, NULL);
+}
+
+
+static void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len,
+				      int freq)
+{
+	struct hostapd_data *hapd = ctx;
+	const struct ieee80211_mgmt *mgmt;
+	size_t hdr_len;
+	const u8 *sa, *data;
+
+	mgmt = (const struct ieee80211_mgmt *) buf;
+	hdr_len = (const u8 *) &mgmt->u.action.u.vs_public_action.action - buf;
+	if (hdr_len > len)
+		return;
+	if (mgmt->u.action.category != WLAN_ACTION_PUBLIC)
+		return;
+	sa = mgmt->sa;
+	len -= hdr_len;
+	data = &mgmt->u.action.u.public_action.action;
+	switch (data[0]) {
+	case WLAN_PA_GAS_INITIAL_REQ:
+		gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1);
+		break;
+	case WLAN_PA_GAS_COMEBACK_REQ:
+		gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1);
+		break;
+	}
+}
+
+
+int gas_serv_init(struct hostapd_data *hapd)
+{
+	hapd->public_action_cb = gas_serv_rx_public_action;
+	hapd->public_action_cb_ctx = hapd;
+	hapd->gas_frag_limit = 1400;
+	if (hapd->conf->gas_frag_limit > 0)
+		hapd->gas_frag_limit = hapd->conf->gas_frag_limit;
+	return 0;
+}
+
+
+void gas_serv_deinit(struct hostapd_data *hapd)
+{
+}
diff --git a/src/ap/gas_serv.h b/src/ap/gas_serv.h
new file mode 100644
index 0000000..0e2eaf6
--- /dev/null
+++ b/src/ap/gas_serv.h
@@ -0,0 +1,49 @@
+/*
+ * Generic advertisement service (GAS) server
+ * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef GAS_SERV_H
+#define GAS_SERV_H
+
+#define ANQP_REQ_CAPABILITY_LIST \
+	(1 << (ANQP_CAPABILITY_LIST - ANQP_QUERY_LIST))
+#define ANQP_REQ_VENUE_NAME \
+	(1 << (ANQP_VENUE_NAME - ANQP_QUERY_LIST))
+#define ANQP_REQ_ROAMING_CONSORTIUM \
+	(1 << (ANQP_ROAMING_CONSORTIUM - ANQP_QUERY_LIST))
+
+/* To account for latencies between hostapd and external ANQP processor */
+#define GAS_SERV_COMEBACK_DELAY_FUDGE 10
+#define GAS_SERV_MIN_COMEBACK_DELAY 100 /* in TU */
+
+struct gas_dialog_info {
+	u8 valid;
+	u8 index;
+	struct wpabuf *sd_resp; /* Fragmented response */
+	u8 dialog_token;
+	size_t sd_resp_pos; /* Offset in sd_resp */
+	u8 sd_frag_id;
+	u16 comeback_delay;
+
+	unsigned int requested;
+	unsigned int received;
+	unsigned int all_requested;
+};
+
+struct hostapd_data;
+
+void gas_serv_tx_gas_response(struct hostapd_data *hapd, const u8 *dst,
+			      struct gas_dialog_info *dialog);
+struct gas_dialog_info *
+gas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr,
+		     u8 dialog_token);
+void gas_serv_dialog_clear(struct gas_dialog_info *dialog);
+
+int gas_serv_init(struct hostapd_data *hapd);
+void gas_serv_deinit(struct hostapd_data *hapd);
+
+#endif /* GAS_SERV_H */
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index c5cbdf7..22c5e65 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / Initialization and configuration
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -12,6 +12,7 @@
 #include "utils/eloop.h"
 #include "common/ieee802_11_defs.h"
 #include "radius/radius_client.h"
+#include "radius/radius_das.h"
 #include "drivers/driver.h"
 #include "hostapd.h"
 #include "authsrv.h"
@@ -30,15 +31,33 @@
 #include "ap_drv_ops.h"
 #include "ap_config.h"
 #include "p2p_hostapd.h"
+#include "gas_serv.h"
 
 
-static int hostapd_flush_old_stations(struct hostapd_data *hapd);
+static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason);
 static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd);
 static int hostapd_broadcast_wep_clear(struct hostapd_data *hapd);
 
 extern int wpa_debug_level;
 
 
+int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
+			       int (*cb)(struct hostapd_iface *iface,
+					 void *ctx), void *ctx)
+{
+	size_t i;
+	int ret;
+
+	for (i = 0; i < interfaces->count; i++) {
+		ret = cb(interfaces->iface[i], ctx);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+
 static void hostapd_reload_bss(struct hostapd_data *hapd)
 {
 #ifndef CONFIG_NO_RADIUS
@@ -105,7 +124,8 @@
 	 * allow them to use the BSS anymore.
 	 */
 	for (j = 0; j < iface->num_bss; j++) {
-		hostapd_flush_old_stations(iface->bss[j]);
+		hostapd_flush_old_stations(iface->bss[j],
+					   WLAN_REASON_PREV_AUTH_NOT_VALID);
 		hostapd_broadcast_wep_clear(iface->bss[j]);
 
 #ifndef CONFIG_NO_RADIUS
@@ -210,21 +230,9 @@
 	return errors;
 }
 
-/**
- * hostapd_cleanup - Per-BSS cleanup (deinitialization)
- * @hapd: Pointer to BSS data
- *
- * This function is used to free all per-BSS data structures and resources.
- * This gets called in a loop for each BSS between calls to
- * hostapd_cleanup_iface_pre() and hostapd_cleanup_iface() when an interface
- * is deinitialized. Most of the modules that are initialized in
- * hostapd_setup_bss() are deinitialized here.
- */
-static void hostapd_cleanup(struct hostapd_data *hapd)
-{
-	if (hapd->iface->ctrl_iface_deinit)
-		hapd->iface->ctrl_iface_deinit(hapd);
 
+static void hostapd_free_hapd_data(struct hostapd_data *hapd)
+{
 	iapp_deinit(hapd->iapp);
 	hapd->iapp = NULL;
 	accounting_deinit(hapd);
@@ -234,6 +242,8 @@
 #ifndef CONFIG_NO_RADIUS
 	radius_client_deinit(hapd->radius);
 	hapd->radius = NULL;
+	radius_das_deinit(hapd->radius_das);
+	hapd->radius_das = NULL;
 #endif /* CONFIG_NO_RADIUS */
 
 	hostapd_deinit_wps(hapd);
@@ -257,6 +267,28 @@
 #endif /* CONFIG_P2P */
 
 	wpabuf_free(hapd->time_adv);
+
+#ifdef CONFIG_INTERWORKING
+	gas_serv_deinit(hapd);
+#endif /* CONFIG_INTERWORKING */
+}
+
+
+/**
+ * hostapd_cleanup - Per-BSS cleanup (deinitialization)
+ * @hapd: Pointer to BSS data
+ *
+ * This function is used to free all per-BSS data structures and resources.
+ * This gets called in a loop for each BSS between calls to
+ * hostapd_cleanup_iface_pre() and hostapd_cleanup_iface() when an interface
+ * is deinitialized. Most of the modules that are initialized in
+ * hostapd_setup_bss() are deinitialized here.
+ */
+static void hostapd_cleanup(struct hostapd_data *hapd)
+{
+	if (hapd->iface->ctrl_iface_deinit)
+		hapd->iface->ctrl_iface_deinit(hapd);
+	hostapd_free_hapd_data(hapd);
 }
 
 
@@ -272,6 +304,18 @@
 }
 
 
+static void hostapd_cleanup_iface_partial(struct hostapd_iface *iface)
+{
+	hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
+	iface->hw_features = NULL;
+	os_free(iface->current_rates);
+	iface->current_rates = NULL;
+	os_free(iface->basic_rates);
+	iface->basic_rates = NULL;
+	ap_list_deinit(iface);
+}
+
+
 /**
  * hostapd_cleanup_iface - Complete per-interface cleanup
  * @iface: Pointer to interface data
@@ -281,13 +325,7 @@
  */
 static void hostapd_cleanup_iface(struct hostapd_iface *iface)
 {
-	hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
-	iface->hw_features = NULL;
-	os_free(iface->current_rates);
-	iface->current_rates = NULL;
-	os_free(iface->basic_rates);
-	iface->basic_rates = NULL;
-	ap_list_deinit(iface);
+	hostapd_cleanup_iface_partial(iface);
 	hostapd_config_free(iface->conf);
 	iface->conf = NULL;
 
@@ -297,6 +335,15 @@
 }
 
 
+static void hostapd_clear_wep(struct hostapd_data *hapd)
+{
+	if (hapd->drv_priv) {
+		hostapd_set_privacy(hapd, 0);
+		hostapd_broadcast_wep_clear(hapd);
+	}
+}
+
+
 static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd)
 {
 	int i;
@@ -333,7 +380,7 @@
 }
 
 
-static int hostapd_flush_old_stations(struct hostapd_data *hapd)
+static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason)
 {
 	int ret = 0;
 	u8 addr[ETH_ALEN];
@@ -349,7 +396,7 @@
 	}
 	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "Deauthenticate all stations");
 	os_memset(addr, 0xff, ETH_ALEN);
-	hostapd_drv_sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
+	hostapd_drv_sta_deauth(hapd, addr, reason);
 	hostapd_free_stas(hapd);
 
 	return ret;
@@ -464,6 +511,86 @@
 }
 
 
+#ifndef CONFIG_NO_RADIUS
+
+static int hostapd_das_nas_mismatch(struct hostapd_data *hapd,
+				    struct radius_das_attrs *attr)
+{
+	/* TODO */
+	return 0;
+}
+
+
+static struct sta_info * hostapd_das_find_sta(struct hostapd_data *hapd,
+					      struct radius_das_attrs *attr)
+{
+	struct sta_info *sta = NULL;
+	char buf[128];
+
+	if (attr->sta_addr)
+		sta = ap_get_sta(hapd, attr->sta_addr);
+
+	if (sta == NULL && attr->acct_session_id &&
+	    attr->acct_session_id_len == 17) {
+		for (sta = hapd->sta_list; sta; sta = sta->next) {
+			os_snprintf(buf, sizeof(buf), "%08X-%08X",
+				    sta->acct_session_id_hi,
+				    sta->acct_session_id_lo);
+			if (os_memcmp(attr->acct_session_id, buf, 17) == 0)
+				break;
+		}
+	}
+
+	if (sta == NULL && attr->cui) {
+		for (sta = hapd->sta_list; sta; sta = sta->next) {
+			struct wpabuf *cui;
+			cui = ieee802_1x_get_radius_cui(sta->eapol_sm);
+			if (cui && wpabuf_len(cui) == attr->cui_len &&
+			    os_memcmp(wpabuf_head(cui), attr->cui,
+				      attr->cui_len) == 0)
+				break;
+		}
+	}
+
+	if (sta == NULL && attr->user_name) {
+		for (sta = hapd->sta_list; sta; sta = sta->next) {
+			u8 *identity;
+			size_t identity_len;
+			identity = ieee802_1x_get_identity(sta->eapol_sm,
+							   &identity_len);
+			if (identity &&
+			    identity_len == attr->user_name_len &&
+			    os_memcmp(identity, attr->user_name, identity_len)
+			    == 0)
+				break;
+		}
+	}
+
+	return sta;
+}
+
+
+static enum radius_das_res
+hostapd_das_disconnect(void *ctx, struct radius_das_attrs *attr)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+
+	if (hostapd_das_nas_mismatch(hapd, attr))
+		return RADIUS_DAS_NAS_MISMATCH;
+
+	sta = hostapd_das_find_sta(hapd, attr);
+	if (sta == NULL)
+		return RADIUS_DAS_SESSION_NOT_FOUND;
+
+	hostapd_drv_sta_deauth(hapd, sta->addr,
+			       WLAN_REASON_PREV_AUTH_NOT_VALID);
+	ap_sta_deauthenticate(hapd, sta, WLAN_REASON_PREV_AUTH_NOT_VALID);
+
+	return RADIUS_DAS_SUCCESS;
+}
+
+#endif /* CONFIG_NO_RADIUS */
 
 
 /**
@@ -519,7 +646,7 @@
 	if (conf->wmm_enabled < 0)
 		conf->wmm_enabled = hapd->iconf->ieee80211n;
 
-	hostapd_flush_old_stations(hapd);
+	hostapd_flush_old_stations(hapd, WLAN_REASON_PREV_AUTH_NOT_VALID);
 	hostapd_set_privacy(hapd, 0);
 
 	hostapd_broadcast_wep_clear(hapd);
@@ -583,6 +710,27 @@
 		wpa_printf(MSG_ERROR, "RADIUS client initialization failed.");
 		return -1;
 	}
+
+	if (hapd->conf->radius_das_port) {
+		struct radius_das_conf das_conf;
+		os_memset(&das_conf, 0, sizeof(das_conf));
+		das_conf.port = hapd->conf->radius_das_port;
+		das_conf.shared_secret = hapd->conf->radius_das_shared_secret;
+		das_conf.shared_secret_len =
+			hapd->conf->radius_das_shared_secret_len;
+		das_conf.client_addr = &hapd->conf->radius_das_client_addr;
+		das_conf.time_window = hapd->conf->radius_das_time_window;
+		das_conf.require_event_timestamp =
+			hapd->conf->radius_das_require_event_timestamp;
+		das_conf.ctx = hapd;
+		das_conf.disconnect = hostapd_das_disconnect;
+		hapd->radius_das = radius_das_init(&das_conf);
+		if (hapd->radius_das == NULL) {
+			wpa_printf(MSG_ERROR, "RADIUS DAS initialization "
+				   "failed.");
+			return -1;
+		}
+	}
 #endif /* CONFIG_NO_RADIUS */
 
 	if (hostapd_acl_init(hapd)) {
@@ -615,6 +763,13 @@
 		return -1;
 	}
 
+#ifdef CONFIG_INTERWORKING
+	if (gas_serv_init(hapd)) {
+		wpa_printf(MSG_ERROR, "GAS server initialization failed");
+		return -1;
+	}
+#endif /* CONFIG_INTERWORKING */
+
 	if (hapd->iface->ctrl_iface_init &&
 	    hapd->iface->ctrl_iface_init(hapd)) {
 		wpa_printf(MSG_ERROR, "Failed to setup control interface");
@@ -857,6 +1012,7 @@
 	hapd->conf = bss;
 	hapd->iface = hapd_iface;
 	hapd->driver = hapd->iconf->driver;
+	hapd->ctrl_sock = -1;
 
 	return hapd;
 }
@@ -873,7 +1029,8 @@
 	for (j = 0; j < iface->num_bss; j++) {
 		struct hostapd_data *hapd = iface->bss[j];
 		hostapd_free_stas(hapd);
-		hostapd_flush_old_stations(hapd);
+		hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING);
+		hostapd_clear_wep(hapd);
 		hostapd_cleanup(hapd);
 	}
 }
@@ -937,4 +1094,12 @@
 			wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH);
 	} else
 		wpa_auth_sta_associated(hapd->wpa_auth, sta->wpa_sm);
+
+	wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
+		   "for " MACSTR " (%d seconds - ap_max_inactivity)",
+		   __func__, MAC2STR(sta->addr),
+		   hapd->conf->ap_max_inactivity);
+	eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+	eloop_register_timeout(hapd->conf->ap_max_inactivity, 0,
+			       ap_handle_timer, hapd, sta);
 }
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index bc28805..f7ed311 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -15,7 +15,6 @@
 struct wpa_ctrl_dst;
 struct radius_server_data;
 struct upnp_wps_device_sm;
-struct hapd_interfaces;
 struct hostapd_data;
 struct sta_info;
 struct hostap_sta_driver_data;
@@ -24,9 +23,15 @@
 enum wps_event;
 union wps_event_data;
 
+struct hapd_interfaces {
+	size_t count;
+	struct hostapd_iface **iface;
+};
+
+
 struct hostapd_probereq_cb {
 	int (*cb)(void *ctx, const u8 *sa, const u8 *da, const u8 *bssid,
-		  const u8 *ie, size_t ie_len);
+		  const u8 *ie, size_t ie_len, int ssi_signal);
 	void *ctx;
 };
 
@@ -40,7 +45,7 @@
 struct hostapd_frame_info {
 	u32 channel;
 	u32 datarate;
-	u32 ssi_signal;
+	int ssi_signal; /* dBm */
 };
 
 
@@ -80,6 +85,7 @@
 
 	struct radius_client_data *radius;
 	u32 acct_session_id_hi, acct_session_id_lo;
+	struct radius_das_data *radius_das;
 
 	struct iapp_data *iapp;
 
@@ -164,6 +170,9 @@
 	int noa_start;
 	int noa_duration;
 #endif /* CONFIG_P2P */
+#ifdef CONFIG_INTERWORKING
+	size_t gas_frag_limit;
+#endif /* CONFIG_INTERWORKING */
 };
 
 
@@ -242,6 +251,9 @@
 };
 
 /* hostapd.c */
+int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
+			       int (*cb)(struct hostapd_iface *iface,
+					 void *ctx), void *ctx);
 int hostapd_reload_config(struct hostapd_iface *iface);
 struct hostapd_data *
 hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
@@ -258,7 +270,8 @@
 int hostapd_register_probereq_cb(struct hostapd_data *hapd,
 				 int (*cb)(void *ctx, const u8 *sa,
 					   const u8 *da, const u8 *bssid,
-					   const u8 *ie, size_t ie_len),
+					   const u8 *ie, size_t ie_len,
+					   int ssi_signal),
 				 void *ctx);
 void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr);
 
@@ -268,6 +281,9 @@
 void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr);
 void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr);
 int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da,
-			 const u8 *bssid, const u8 *ie, size_t ie_len);
+			 const u8 *bssid, const u8 *ie, size_t ie_len,
+			 int ssi_signal);
+void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
+			     int offset);
 
 #endif /* HOSTAPD_H */
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index 8c6fef2..76c4211 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -2,7 +2,7 @@
  * hostapd / Hardware feature query and different modes
  * Copyright 2002-2003, Instant802 Networks, Inc.
  * Copyright 2005-2006, Devicescape Software, Inc.
- * Copyright (c) 2008-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -416,7 +416,7 @@
 	int res;
 
 	/* Check list of neighboring BSSes (from scan) to see whether 40 MHz is
-	 * allowed per IEEE 802.11n/D7.0, 11.14.3.2 */
+	 * allowed per IEEE Std 802.11-2012, 10.15.3.2 */
 
 	iface->scan_cb = NULL;
 
@@ -447,6 +447,46 @@
 }
 
 
+static void ieee80211n_scan_channels_2g4(struct hostapd_iface *iface,
+					 struct wpa_driver_scan_params *params)
+{
+	/* Scan only the affected frequency range */
+	int pri_freq, sec_freq;
+	int affected_start, affected_end;
+	int i, pos;
+	struct hostapd_hw_modes *mode;
+
+	if (iface->current_mode == NULL)
+		return;
+
+	pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel);
+	if (iface->conf->secondary_channel > 0)
+		sec_freq = pri_freq + 20;
+	else
+		sec_freq = pri_freq - 20;
+	affected_start = (pri_freq + sec_freq) / 2 - 25;
+	affected_end = (pri_freq + sec_freq) / 2 + 25;
+	wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
+		   affected_start, affected_end);
+
+	mode = iface->current_mode;
+	params->freqs = os_zalloc((mode->num_channels + 1) * sizeof(int));
+	if (params->freqs == NULL)
+		return;
+	pos = 0;
+
+	for (i = 0; i < mode->num_channels; i++) {
+		struct hostapd_channel_data *chan = &mode->channels[i];
+		if (chan->flag & HOSTAPD_CHAN_DISABLED)
+			continue;
+		if (chan->freq < affected_start ||
+		    chan->freq > affected_end)
+			continue;
+		params->freqs[pos++] = chan->freq;
+	}
+}
+
+
 static int ieee80211n_check_40mhz(struct hostapd_iface *iface)
 {
 	struct wpa_driver_scan_params params;
@@ -457,12 +497,15 @@
 	wpa_printf(MSG_DEBUG, "Scan for neighboring BSSes prior to enabling "
 		   "40 MHz channel");
 	os_memset(&params, 0, sizeof(params));
-	/* TODO: scan only the needed frequency */
+	if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
+		ieee80211n_scan_channels_2g4(iface, &params);
 	if (hostapd_driver_scan(iface->bss[0], &params) < 0) {
 		wpa_printf(MSG_ERROR, "Failed to request a scan of "
 			   "neighboring BSSes");
+		os_free(params.freqs);
 		return -1;
 	}
+	os_free(params.freqs);
 
 	iface->scan_cb = ieee80211n_check_scan;
 	return 1;
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 9c931ca..3996c90 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -876,6 +876,7 @@
 #endif /* CONFIG_IEEE80211N */
 
 	p = hostapd_eid_ext_capab(hapd, p);
+	p = hostapd_eid_bss_max_idle_period(hapd, p);
 
 	if (sta->flags & WLAN_STA_WMM)
 		p = hostapd_eid_wmm(hapd, p);
@@ -1392,7 +1393,7 @@
 
 
 	if (stype == WLAN_FC_STYPE_PROBE_REQ) {
-		handle_probe_req(hapd, mgmt, len);
+		handle_probe_req(hapd, mgmt, len, fi->ssi_signal);
 		return;
 	}
 
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index d30e90f..9993bee 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -45,6 +45,8 @@
 u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid);
 int hostapd_ht_operation_update(struct hostapd_iface *iface);
 void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
 				  const u8 *addr, const u8 *trans_id);
@@ -72,5 +74,6 @@
 u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid);
 int hostapd_update_time_adv(struct hostapd_data *hapd);
 void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr);
+u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid);
 
 #endif /* IEEE802_11_H */
diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c
index 109c4bc..0c4c5f3 100644
--- a/src/ap/ieee802_11_auth.c
+++ b/src/ap/ieee802_11_auth.c
@@ -193,7 +193,8 @@
 		goto fail;
 	}
 
-	radius_client_send(hapd->radius, msg, RADIUS_AUTH, addr);
+	if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, addr) < 0)
+		goto fail;
 	return 0;
 
  fail:
@@ -492,7 +493,7 @@
 		}
 
 		if (hapd->conf->wpa_psk_radius == PSK_RADIUS_REQUIRED &&
-		    cache->psk == NULL)
+		    !cache->has_psk)
 			cache->accepted = HOSTAPD_ACL_REJECT;
 	} else
 		cache->accepted = HOSTAPD_ACL_REJECT;
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index 0935cd5..b3fdf3d 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / IEEE 802.11 Management
- * Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -397,3 +397,31 @@
 
 	return 0;
 }
+
+
+u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 *pos = eid;
+
+#ifdef CONFIG_WNM
+	if (hapd->conf->ap_max_inactivity > 0) {
+		unsigned int val;
+		*pos++ = WLAN_EID_BSS_MAX_IDLE_PERIOD;
+		*pos++ = 3;
+		val = hapd->conf->ap_max_inactivity;
+		if (val > 68000)
+			val = 68000;
+		val *= 1000;
+		val /= 1024;
+		if (val == 0)
+			val = 1;
+		if (val > 65535)
+			val = 65535;
+		WPA_PUT_LE16(pos, val);
+		pos += 2;
+		*pos++ = 0x00; /* TODO: Protected Keep-Alive Required */
+	}
+#endif /* CONFIG_WNM */
+
+	return pos;
+}
diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c
new file mode 100644
index 0000000..3ad33c8
--- /dev/null
+++ b/src/ap/ieee802_11_vht.c
@@ -0,0 +1,72 @@
+/*
+ * hostapd / IEEE 802.11ac VHT
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of BSD license
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "drivers/driver.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "sta_info.h"
+#include "beacon.h"
+#include "ieee802_11.h"
+
+
+u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid)
+{
+	struct ieee80211_vht_capabilities *cap;
+	u8 *pos = eid;
+
+	if (!hapd->iconf->ieee80211ac || !hapd->iface->current_mode ||
+	    hapd->conf->disable_11ac)
+		return eid;
+
+	*pos++ = WLAN_EID_VHT_CAP;
+	*pos++ = sizeof(*cap);
+
+	cap = (struct ieee80211_vht_capabilities *) pos;
+	os_memset(cap, 0, sizeof(*cap));
+	cap->vht_capabilities_info = host_to_le32(
+		hapd->iface->current_mode->vht_capab);
+
+	/* Supported MCS set comes from hw */
+	os_memcpy(cap->vht_supported_mcs_set,
+	          hapd->iface->current_mode->vht_mcs_set, 8);
+
+	pos += sizeof(*cap);
+
+	return pos;
+}
+
+
+u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid)
+{
+	struct ieee80211_vht_operation *oper;
+	u8 *pos = eid;
+
+	if (!hapd->iconf->ieee80211ac || hapd->conf->disable_11ac)
+		return eid;
+
+	*pos++ = WLAN_EID_VHT_OPERATION;
+	*pos++ = sizeof(*oper);
+
+	oper = (struct ieee80211_vht_operation *) pos;
+	os_memset(oper, 0, sizeof(*oper));
+
+	oper->vht_op_info_chwidth = hapd->iconf->vht_oper_chwidth;
+
+	/* VHT Basic MCS set comes from hw */
+	/* Hard code 1 stream, MCS0-7 is a min Basic VHT MCS rates */
+	oper->vht_basic_mcs_set = 0xfffc;
+	pos += sizeof(*oper);
+
+	return pos;
+}
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index a329777..dd0df1d 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -416,6 +416,7 @@
 	struct radius_msg *msg;
 	char buf[128];
 	struct eapol_state_machine *sm = sta->eapol_sm;
+	struct hostapd_radius_attr *attr;
 
 	if (sm == NULL)
 		return;
@@ -442,7 +443,9 @@
 		goto fail;
 	}
 
-	if (hapd->conf->own_ip_addr.af == AF_INET &&
+	if (!hostapd_config_get_radius_attr(hapd->conf->radius_auth_req_attr,
+					    RADIUS_ATTR_NAS_IP_ADDRESS) &&
+	    hapd->conf->own_ip_addr.af == AF_INET &&
 	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
 				 (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) {
 		printf("Could not add NAS-IP-Address\n");
@@ -450,7 +453,9 @@
 	}
 
 #ifdef CONFIG_IPV6
-	if (hapd->conf->own_ip_addr.af == AF_INET6 &&
+	if (!hostapd_config_get_radius_attr(hapd->conf->radius_auth_req_attr,
+					    RADIUS_ATTR_NAS_IPV6_ADDRESS) &&
+	    hapd->conf->own_ip_addr.af == AF_INET6 &&
 	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS,
 				 (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) {
 		printf("Could not add NAS-IPv6-Address\n");
@@ -458,7 +463,9 @@
 	}
 #endif /* CONFIG_IPV6 */
 
-	if (hapd->conf->nas_identifier &&
+	if (!hostapd_config_get_radius_attr(hapd->conf->radius_auth_req_attr,
+					    RADIUS_ATTR_NAS_IDENTIFIER) &&
+	    hapd->conf->nas_identifier &&
 	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
 				 (u8 *) hapd->conf->nas_identifier,
 				 os_strlen(hapd->conf->nas_identifier))) {
@@ -466,7 +473,9 @@
 		goto fail;
 	}
 
-	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) {
+	if (!hostapd_config_get_radius_attr(hapd->conf->radius_auth_req_attr,
+					    RADIUS_ATTR_NAS_PORT) &&
+	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) {
 		printf("Could not add NAS-Port\n");
 		goto fail;
 	}
@@ -474,7 +483,9 @@
 	os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s",
 		    MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid);
 	buf[sizeof(buf) - 1] = '\0';
-	if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID,
+	if (!hostapd_config_get_radius_attr(hapd->conf->radius_auth_req_attr,
+					    RADIUS_ATTR_CALLED_STATION_ID) &&
+	    !radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID,
 				 (u8 *) buf, os_strlen(buf))) {
 		printf("Could not add Called-Station-Id\n");
 		goto fail;
@@ -492,12 +503,16 @@
 	/* TODO: should probably check MTU from driver config; 2304 is max for
 	 * IEEE 802.11, but use 1400 to avoid problems with too large packets
 	 */
-	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_FRAMED_MTU, 1400)) {
+	if (!hostapd_config_get_radius_attr(hapd->conf->radius_auth_req_attr,
+					    RADIUS_ATTR_FRAMED_MTU) &&
+	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_FRAMED_MTU, 1400)) {
 		printf("Could not add Framed-MTU\n");
 		goto fail;
 	}
 
-	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE,
+	if (!hostapd_config_get_radius_attr(hapd->conf->radius_auth_req_attr,
+					    RADIUS_ATTR_NAS_PORT_TYPE) &&
+	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE,
 				       RADIUS_NAS_PORT_TYPE_IEEE_802_11)) {
 		printf("Could not add NAS-Port-Type\n");
 		goto fail;
@@ -513,7 +528,9 @@
 			    radius_mode_txt(hapd));
 		buf[sizeof(buf) - 1] = '\0';
 	}
-	if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
+	if (!hostapd_config_get_radius_attr(hapd->conf->radius_auth_req_attr,
+					    RADIUS_ATTR_CONNECT_INFO) &&
+	    !radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
 				 (u8 *) buf, os_strlen(buf))) {
 		printf("Could not add Connect-Info\n");
 		goto fail;
@@ -541,6 +558,36 @@
 		}
 	}
 
+	if (hapd->conf->radius_request_cui) {
+		const u8 *cui;
+		size_t cui_len;
+		/* Add previously learned CUI or nul CUI to request CUI */
+		if (sm->radius_cui) {
+			cui = wpabuf_head(sm->radius_cui);
+			cui_len = wpabuf_len(sm->radius_cui);
+		} else {
+			cui = (const u8 *) "\0";
+			cui_len = 1;
+		}
+		if (!radius_msg_add_attr(msg,
+					 RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+					 cui, cui_len)) {
+			wpa_printf(MSG_ERROR, "Could not add CUI");
+			goto fail;
+		}
+	}
+
+	for (attr = hapd->conf->radius_auth_req_attr; attr; attr = attr->next)
+	{
+		if (!radius_msg_add_attr(msg, attr->type,
+					 wpabuf_head(attr->val),
+					 wpabuf_len(attr->val))) {
+			wpa_printf(MSG_ERROR, "Could not add RADIUS "
+				   "attribute");
+			goto fail;
+		}
+	}
+
 	if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, sta->addr) < 0)
 		goto fail;
 
@@ -861,12 +908,22 @@
 	if (!force_1x && !hapd->conf->ieee802_1x) {
 		wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore STA - "
 			   "802.1X not enabled or forced for WPS");
+		/*
+		 * Clear any possible EAPOL authenticator state to support
+		 * reassociation change from WPS to PSK.
+		 */
+		ieee802_1x_free_station(sta);
 		return;
 	}
 
 	key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm);
 	if (key_mgmt != -1 && wpa_key_mgmt_wpa_psk(key_mgmt)) {
 		wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore STA - using PSK");
+		/*
+		 * Clear any possible EAPOL authenticator state to support
+		 * reassociation change from WPA-EAP to PSK.
+		 */
+		ieee802_1x_free_station(sta);
 		return;
 	}
 
@@ -968,6 +1025,7 @@
 #ifndef CONFIG_NO_RADIUS
 	radius_msg_free(sm->last_recv_radius);
 	radius_free_class(&sm->radius_class);
+	wpabuf_free(sm->radius_cui);
 #endif /* CONFIG_NO_RADIUS */
 
 	os_free(sm->identity);
@@ -1189,6 +1247,32 @@
 }
 
 
+/* Update CUI based on Chargeable-User-Identity attribute in Access-Accept */
+static void ieee802_1x_update_sta_cui(struct hostapd_data *hapd,
+				      struct sta_info *sta,
+				      struct radius_msg *msg)
+{
+	struct eapol_state_machine *sm = sta->eapol_sm;
+	struct wpabuf *cui;
+	u8 *buf;
+	size_t len;
+
+	if (sm == NULL)
+		return;
+
+	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+				    &buf, &len, NULL) < 0)
+		return;
+
+	cui = wpabuf_alloc_copy(buf, len);
+	if (cui == NULL)
+		return;
+
+	wpabuf_free(sm->radius_cui);
+	sm->radius_cui = cui;
+}
+
+
 struct sta_id_search {
 	u8 identifier;
 	struct eapol_state_machine *sm;
@@ -1348,6 +1432,7 @@
 				    shared_secret_len);
 		ieee802_1x_store_radius_class(hapd, sta, msg);
 		ieee802_1x_update_sta_identity(hapd, sta, msg);
+		ieee802_1x_update_sta_cui(hapd, sta, msg);
 		if (sm->eap_if->eapKeyAvailable &&
 		    wpa_auth_pmksa_add(sta->wpa_sm, sm->eapol_key_crypt,
 				       session_timeout_set ?
@@ -1865,6 +1950,14 @@
 }
 
 
+struct wpabuf * ieee802_1x_get_radius_cui(struct eapol_state_machine *sm)
+{
+	if (sm == NULL)
+		return NULL;
+	return sm->radius_cui;
+}
+
+
 const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len)
 {
 	*len = 0;
diff --git a/src/ap/ieee802_1x.h b/src/ap/ieee802_1x.h
index 58f6084..f9b05ca 100644
--- a/src/ap/ieee802_1x.h
+++ b/src/ap/ieee802_1x.h
@@ -67,6 +67,7 @@
 u8 * ieee802_1x_get_identity(struct eapol_state_machine *sm, size_t *len);
 u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len,
 				 int idx);
+struct wpabuf * ieee802_1x_get_radius_cui(struct eapol_state_machine *sm);
 const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len);
 void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm,
 				    int enabled);
diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c
index 9eb4840..ba2c033 100644
--- a/src/ap/pmksa_cache_auth.c
+++ b/src/ap/pmksa_cache_auth.c
@@ -1,6 +1,6 @@
 /*
  * hostapd - PMKSA cache for IEEE 802.11i RSN
- * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2008, 2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -40,6 +40,7 @@
 	if (entry == NULL)
 		return;
 	os_free(entry->identity);
+	wpabuf_free(entry->cui);
 #ifndef CONFIG_NO_RADIUS
 	radius_free_class(&entry->radius_class);
 #endif /* CONFIG_NO_RADIUS */
@@ -136,6 +137,9 @@
 		}
 	}
 
+	if (eapol->radius_cui)
+		entry->cui = wpabuf_dup(eapol->radius_cui);
+
 #ifndef CONFIG_NO_RADIUS
 	radius_copy_class(&entry->radius_class, &eapol->radius_class);
 #endif /* CONFIG_NO_RADIUS */
@@ -163,6 +167,11 @@
 				  eapol->identity, eapol->identity_len);
 	}
 
+	if (entry->cui) {
+		wpabuf_free(eapol->radius_cui);
+		eapol->radius_cui = wpabuf_dup(entry->cui);
+	}
+
 #ifndef CONFIG_NO_RADIUS
 	radius_free_class(&eapol->radius_class);
 	radius_copy_class(&eapol->radius_class, &entry->radius_class);
@@ -299,6 +308,8 @@
 				  old_entry->identity_len);
 		}
 	}
+	if (old_entry->cui)
+		entry->cui = wpabuf_dup(old_entry->cui);
 #ifndef CONFIG_NO_RADIUS
 	radius_copy_class(&entry->radius_class, &old_entry->radius_class);
 #endif /* CONFIG_NO_RADIUS */
diff --git a/src/ap/pmksa_cache_auth.h b/src/ap/pmksa_cache_auth.h
index 74b73c4..d473f3f 100644
--- a/src/ap/pmksa_cache_auth.h
+++ b/src/ap/pmksa_cache_auth.h
@@ -1,6 +1,6 @@
 /*
  * hostapd - PMKSA cache for IEEE 802.11i RSN
- * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2008, 2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -25,6 +25,7 @@
 
 	u8 *identity;
 	size_t identity_len;
+	struct wpabuf *cui;
 	struct radius_class_data radius_class;
 	u8 eap_type_authsrv;
 	int vlan_id;
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index afabdaa..95b701c 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -28,6 +28,7 @@
 #include "vlan_init.h"
 #include "p2p_hostapd.h"
 #include "ap_drv_ops.h"
+#include "gas_serv.h"
 #include "sta_info.h"
 
 static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd,
@@ -194,6 +195,8 @@
 	if (set_beacon)
 		ieee802_11_set_beacons(hapd->iface);
 
+	wpa_printf(MSG_DEBUG, "%s: cancel ap_handle_timer for " MACSTR,
+		   __func__, MAC2STR(sta->addr));
 	eloop_cancel_timeout(ap_handle_timer, hapd, sta);
 	eloop_cancel_timeout(ap_handle_session_timer, hapd, sta);
 	eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
@@ -218,6 +221,15 @@
 	p2p_group_notif_disassoc(hapd->p2p_group, sta->addr);
 #endif /* CONFIG_P2P */
 
+#ifdef CONFIG_INTERWORKING
+	if (sta->gas_dialog) {
+		int i;
+		for (i = 0; i < GAS_DIALOG_MAX; i++)
+			gas_serv_dialog_clear(&sta->gas_dialog[i]);
+		os_free(sta->gas_dialog);
+	}
+#endif /* CONFIG_INTERWORKING */
+
 	wpabuf_free(sta->wps_ie);
 	wpabuf_free(sta->p2p_ie);
 
@@ -262,6 +274,9 @@
 	struct sta_info *sta = timeout_ctx;
 	unsigned long next_time = 0;
 
+	wpa_printf(MSG_DEBUG, "%s: " MACSTR " flags=0x%x timeout_next=%d",
+		   __func__, MAC2STR(sta->addr), sta->flags,
+		   sta->timeout_next);
 	if (sta->timeout_next == STA_REMOVE) {
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_INFO, "deauthenticated due to "
@@ -274,6 +289,12 @@
 	    (sta->timeout_next == STA_NULLFUNC ||
 	     sta->timeout_next == STA_DISASSOC)) {
 		int inactive_sec;
+		/*
+		 * Add random value to timeout so that we don't end up bouncing
+		 * all stations at the same time if we have lots of associated
+		 * stations that are idle (but keep re-associating).
+		 */
+		int fuzz = os_random() % 20;
 		inactive_sec = hostapd_drv_get_inact_sec(hapd, sta->addr);
 		if (inactive_sec == -1) {
 			wpa_msg(hapd->msg_ctx, MSG_DEBUG,
@@ -285,7 +306,7 @@
 			 * Anyway, try again after the next inactivity timeout,
 			 * but do not disconnect the station now.
 			 */
-			next_time = hapd->conf->ap_max_inactivity;
+			next_time = hapd->conf->ap_max_inactivity + fuzz;
 		} else if (inactive_sec < hapd->conf->ap_max_inactivity &&
 			   sta->flags & WLAN_STA_ASSOC) {
 			/* station activity detected; reset timeout state */
@@ -293,7 +314,7 @@
 				"Station " MACSTR " has been active %is ago",
 				MAC2STR(sta->addr), inactive_sec);
 			sta->timeout_next = STA_NULLFUNC;
-			next_time = hapd->conf->ap_max_inactivity -
+			next_time = hapd->conf->ap_max_inactivity + fuzz -
 				inactive_sec;
 		} else {
 			wpa_msg(hapd->msg_ctx, MSG_DEBUG,
@@ -320,6 +341,9 @@
 	}
 
 	if (next_time) {
+		wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
+			   "for " MACSTR " (%lu seconds)",
+			   __func__, MAC2STR(sta->addr), next_time);
 		eloop_register_timeout(next_time, 0, ap_handle_timer, hapd,
 				       sta);
 		return;
@@ -353,6 +377,9 @@
 	switch (sta->timeout_next) {
 	case STA_NULLFUNC:
 		sta->timeout_next = STA_DISASSOC;
+		wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
+			   "for " MACSTR " (%d seconds - AP_DISASSOC_DELAY)",
+			   __func__, MAC2STR(sta->addr), AP_DISASSOC_DELAY);
 		eloop_register_timeout(AP_DISASSOC_DELAY, 0, ap_handle_timer,
 				       hapd, sta);
 		break;
@@ -369,6 +396,9 @@
 			       HOSTAPD_LEVEL_INFO, "disassociated due to "
 			       "inactivity");
 		sta->timeout_next = STA_DEAUTH;
+		wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
+			   "for " MACSTR " (%d seconds - AP_DEAUTH_DELAY)",
+			   __func__, MAC2STR(sta->addr), AP_DEAUTH_DELAY);
 		eloop_register_timeout(AP_DEAUTH_DELAY, 0, ap_handle_timer,
 				       hapd, sta);
 		mlme_disassociate_indication(
@@ -397,8 +427,14 @@
 	struct sta_info *sta = timeout_ctx;
 	u8 addr[ETH_ALEN];
 
-	if (!(sta->flags & WLAN_STA_AUTH))
+	if (!(sta->flags & WLAN_STA_AUTH)) {
+		if (sta->flags & WLAN_STA_GAS) {
+			wpa_printf(MSG_DEBUG, "GAS: Remove temporary STA "
+				   "entry " MACSTR, MAC2STR(sta->addr));
+			ap_free_sta(hapd, sta);
+		}
 		return;
+	}
 
 	mlme_deauthenticate_indication(hapd, sta,
 				       WLAN_REASON_PREV_AUTH_NOT_VALID);
@@ -455,6 +491,10 @@
 	sta->acct_interim_interval = hapd->conf->acct_interim_interval;
 
 	/* initialize STA info data */
+	wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
+		   "for " MACSTR " (%d seconds - ap_max_inactivity)",
+		   __func__, MAC2STR(addr),
+		   hapd->conf->ap_max_inactivity);
 	eloop_register_timeout(hapd->conf->ap_max_inactivity, 0,
 			       ap_handle_timer, hapd, sta);
 	os_memcpy(sta->addr, addr, ETH_ALEN);
@@ -528,6 +568,11 @@
 	sta->flags &= ~WLAN_STA_ASSOC;
 	ap_sta_set_authorized(hapd, sta, 0);
 	sta->timeout_next = STA_DEAUTH;
+	wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
+		   "for " MACSTR " (%d seconds - "
+		   "AP_MAX_INACTIVITY_AFTER_DISASSOC)",
+		   __func__, MAC2STR(sta->addr),
+		   AP_MAX_INACTIVITY_AFTER_DISASSOC);
 	eloop_cancel_timeout(ap_handle_timer, hapd, sta);
 	eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DISASSOC, 0,
 			       ap_handle_timer, hapd, sta);
@@ -561,6 +606,11 @@
 	sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
 	ap_sta_set_authorized(hapd, sta, 0);
 	sta->timeout_next = STA_REMOVE;
+	wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
+		   "for " MACSTR " (%d seconds - "
+		   "AP_MAX_INACTIVITY_AFTER_DEAUTH)",
+		   __func__, MAC2STR(sta->addr),
+		   AP_MAX_INACTIVITY_AFTER_DEAUTH);
 	eloop_cancel_timeout(ap_handle_timer, hapd, sta);
 	eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0,
 			       ap_handle_timer, hapd, sta);
@@ -576,6 +626,23 @@
 }
 
 
+#ifdef CONFIG_WPS
+int ap_sta_wps_cancel(struct hostapd_data *hapd,
+		      struct sta_info *sta, void *ctx)
+{
+	if (sta && (sta->flags & WLAN_STA_WPS)) {
+		ap_sta_deauthenticate(hapd, sta,
+				      WLAN_REASON_PREV_AUTH_NOT_VALID);
+		wpa_printf(MSG_DEBUG, "WPS: %s: Deauth sta=" MACSTR,
+			   __func__, MAC2STR(sta->addr));
+		return 1;
+	}
+
+	return 0;
+}
+#endif /* CONFIG_WPS */
+
+
 int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta,
 		     int old_vlanid)
 {
@@ -778,11 +845,20 @@
 			   int authorized)
 {
 	const u8 *dev_addr = NULL;
+#ifdef CONFIG_P2P
+	u8 addr[ETH_ALEN];
+#endif /* CONFIG_P2P */
+
 	if (!!authorized == !!(sta->flags & WLAN_STA_AUTHORIZED))
 		return;
 
 #ifdef CONFIG_P2P
-	dev_addr = p2p_group_get_dev_addr(hapd->p2p_group, sta->addr);
+	if (hapd->p2p_group == NULL) {
+		if (sta->p2p_ie != NULL &&
+		    p2p_parse_dev_addr_in_p2p_ie(sta->p2p_ie, addr) == 0)
+			dev_addr = addr;
+	} else
+		dev_addr = p2p_group_get_dev_addr(hapd->p2p_group, sta->addr);
 #endif /* CONFIG_P2P */
 
 	if (authorized) {
@@ -848,6 +924,11 @@
 	wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH);
 	ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
 	sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
+	wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
+		   "for " MACSTR " (%d seconds - "
+		   "AP_MAX_INACTIVITY_AFTER_DEAUTH)",
+		   __func__, MAC2STR(sta->addr),
+		   AP_MAX_INACTIVITY_AFTER_DEAUTH);
 	eloop_cancel_timeout(ap_handle_timer, hapd, sta);
 	eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0,
 			       ap_handle_timer, hapd, sta);
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 60b3a7b..cef428d 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -27,6 +27,7 @@
 #define WLAN_STA_WDS BIT(14)
 #define WLAN_STA_ASSOC_REQ_OK BIT(15)
 #define WLAN_STA_WPS2 BIT(16)
+#define WLAN_STA_GAS BIT(17)
 #define WLAN_STA_PENDING_DISASSOC_CB BIT(29)
 #define WLAN_STA_PENDING_DEAUTH_CB BIT(30)
 #define WLAN_STA_NONERP BIT(31)
@@ -107,6 +108,12 @@
 	struct os_time sa_query_start;
 #endif /* CONFIG_IEEE80211W */
 
+#ifdef CONFIG_INTERWORKING
+#define GAS_DIALOG_MAX 8 /* Max concurrent dialog number */
+	struct gas_dialog_info *gas_dialog;
+	u8 gas_dialog_next;
+#endif /* CONFIG_INTERWORKING */
+
 	struct wpabuf *wps_ie; /* WPS IE from (Re)Association Request */
 	struct wpabuf *p2p_ie; /* P2P IE from (Re)Association Request */
 };
@@ -137,7 +144,6 @@
 struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta);
 void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta);
 void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta);
-void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta);
 void hostapd_free_stas(struct hostapd_data *hapd);
 void ap_handle_timer(void *eloop_ctx, void *timeout_ctx);
 void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta,
@@ -149,6 +155,10 @@
 			 u16 reason);
 void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta,
 			   u16 reason);
+#ifdef CONFIG_WPS
+int ap_sta_wps_cancel(struct hostapd_data *hapd,
+		      struct sta_info *sta, void *ctx);
+#endif /* CONFIG_WPS */
 int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta,
 		     int old_vlanid);
 void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta);
diff --git a/src/ap/tkip_countermeasures.c b/src/ap/tkip_countermeasures.c
index 60088ee..dd5aa68 100644
--- a/src/ap/tkip_countermeasures.c
+++ b/src/ap/tkip_countermeasures.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / TKIP countermeasures
- * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -11,6 +11,7 @@
 #include "utils/common.h"
 #include "utils/eloop.h"
 #include "common/ieee802_11_defs.h"
+#include "radius/radius.h"
 #include "hostapd.h"
 #include "sta_info.h"
 #include "ap_mlme.h"
@@ -44,12 +45,17 @@
 	eloop_cancel_timeout(ieee80211_tkip_countermeasures_stop, hapd, NULL);
 	eloop_register_timeout(60, 0, ieee80211_tkip_countermeasures_stop,
 			       hapd, NULL);
-	for (sta = hapd->sta_list; sta != NULL; sta = sta->next) {
+	while ((sta = hapd->sta_list)) {
+		sta->acct_terminate_cause =
+			RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_RESET;
+		if (sta->flags & WLAN_STA_AUTH) {
+			mlme_deauthenticate_indication(
+				hapd, sta,
+				WLAN_REASON_MICHAEL_MIC_FAILURE);
+		}
 		hostapd_drv_sta_deauth(hapd, sta->addr,
 				       WLAN_REASON_MICHAEL_MIC_FAILURE);
-		ap_sta_set_authorized(hapd, sta, 0);
-		sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
-		hostapd_drv_sta_remove(hapd, sta->addr);
+		ap_free_sta(hapd, sta);
 	}
 }
 
diff --git a/src/ap/utils.c b/src/ap/utils.c
index 36c1182..3e9fc08 100644
--- a/src/ap/utils.c
+++ b/src/ap/utils.c
@@ -17,7 +17,8 @@
 int hostapd_register_probereq_cb(struct hostapd_data *hapd,
 				 int (*cb)(void *ctx, const u8 *sa,
 					   const u8 *da, const u8 *bssid,
-					   const u8 *ie, size_t ie_len),
+					   const u8 *ie, size_t ie_len,
+					   int ssi_signal),
 				 void *ctx)
 {
 	struct hostapd_probereq_cb *n;
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index d1b9b4d..1d942a4 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -1641,10 +1641,23 @@
 
 	wpa_group_ensure_init(sm->wpa_auth, sm->group);
 
-	os_memcpy(sm->ANonce, sm->group->Counter, WPA_NONCE_LEN);
+	/*
+	 * Definition of ANonce selection in IEEE Std 802.11i-2004 is somewhat
+	 * ambiguous. The Authenticator state machine uses a counter that is
+	 * incremented by one for each 4-way handshake. However, the security
+	 * analysis of 4-way handshake points out that unpredictable nonces
+	 * help in preventing precomputation attacks. Instead of the state
+	 * machine definition, use an unpredictable nonce value here to provide
+	 * stronger protection against potential precomputation attacks.
+	 */
+	if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) {
+		wpa_printf(MSG_ERROR, "WPA: Failed to get random data for "
+			   "ANonce.");
+		wpa_sta_disconnect(sm->wpa_auth, sm->addr);
+		return;
+	}
 	wpa_hexdump(MSG_DEBUG, "WPA: Assign ANonce", sm->ANonce,
 		    WPA_NONCE_LEN);
-	inc_byte_array(sm->group->Counter, WPA_NONCE_LEN);
 	sm->ReAuthenticationRequest = FALSE;
 	/* IEEE 802.11i does not clear TimeoutCtr here, but this is more
 	 * logical place than INITIALIZE since AUTHENTICATION2 can be
diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
index 8999217..07ce06c 100644
--- a/src/ap/wps_hostapd.c
+++ b/src/ap/wps_hostapd.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / WPS integration
- * Copyright (c) 2008-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -12,6 +12,8 @@
 #include "utils/eloop.h"
 #include "utils/uuid.h"
 #include "crypto/dh_groups.h"
+#include "crypto/dh_group5.h"
+#include "crypto/random.h"
 #include "common/wpa_ctrl.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
@@ -20,6 +22,7 @@
 #include "wps/wps.h"
 #include "wps/wps_defs.h"
 #include "wps/wps_dev_attr.h"
+#include "wps/wps_attr_parse.h"
 #include "hostapd.h"
 #include "ap_config.h"
 #include "ap_drv_ops.h"
@@ -37,7 +40,8 @@
 
 static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, const u8 *da,
 				    const u8 *bssid,
-				    const u8 *ie, size_t ie_len);
+				    const u8 *ie, size_t ie_len,
+				    int ssi_signal);
 static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx);
 
 
@@ -183,19 +187,23 @@
 struct wps_stop_reg_data {
 	struct hostapd_data *current_hapd;
 	const u8 *uuid_e;
+	const u8 *dev_pw;
+	size_t dev_pw_len;
 };
 
 static int wps_stop_registrar(struct hostapd_data *hapd, void *ctx)
 {
 	struct wps_stop_reg_data *data = ctx;
 	if (hapd != data->current_hapd && hapd->wps != NULL)
-		wps_registrar_complete(hapd->wps->registrar, data->uuid_e);
+		wps_registrar_complete(hapd->wps->registrar, data->uuid_e,
+				       data->dev_pw, data->dev_pw_len);
 	return 0;
 }
 
 
 static void hostapd_wps_reg_success_cb(void *ctx, const u8 *mac_addr,
-				       const u8 *uuid_e)
+				       const u8 *uuid_e, const u8 *dev_pw,
+				       size_t dev_pw_len)
 {
 	struct hostapd_data *hapd = ctx;
 	char uuid[40];
@@ -209,6 +217,8 @@
 					 mac_addr, uuid_e);
 	data.current_hapd = hapd;
 	data.uuid_e = uuid_e;
+	data.dev_pw = dev_pw;
+	data.dev_pw_len = dev_pw_len;
 	hostapd_wps_for_each(hapd, wps_stop_registrar, &data);
 }
 
@@ -984,6 +994,20 @@
 }
 
 
+static void hostapd_wps_nfc_clear(struct wps_context *wps)
+{
+#ifdef CONFIG_WPS_NFC
+	wps->ap_nfc_dev_pw_id = 0;
+	wpabuf_free(wps->ap_nfc_dh_pubkey);
+	wps->ap_nfc_dh_pubkey = NULL;
+	wpabuf_free(wps->ap_nfc_dh_privkey);
+	wps->ap_nfc_dh_privkey = NULL;
+	wpabuf_free(wps->ap_nfc_dev_pw);
+	wps->ap_nfc_dev_pw = NULL;
+#endif /* CONFIG_WPS_NFC */
+}
+
+
 void hostapd_deinit_wps(struct hostapd_data *hapd)
 {
 	eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
@@ -1001,6 +1025,7 @@
 	wpabuf_free(hapd->wps->oob_conf.pubkey_hash);
 	wpabuf_free(hapd->wps->oob_conf.dev_password);
 	wps_free_pending_msgs(hapd->wps->upnp_msgs);
+	hostapd_wps_nfc_clear(hapd->wps);
 	os_free(hapd->wps);
 	hapd->wps = NULL;
 	hostapd_wps_clear_ies(hapd);
@@ -1098,6 +1123,24 @@
 }
 
 
+static int wps_cancel(struct hostapd_data *hapd, void *ctx)
+{
+	if (hapd->wps == NULL)
+		return 0;
+
+	wps_registrar_wps_cancel(hapd->wps->registrar);
+	ap_for_each_sta(hapd, ap_sta_wps_cancel, NULL);
+
+	return 0;
+}
+
+
+int hostapd_wps_cancel(struct hostapd_data *hapd)
+{
+	return hostapd_wps_for_each(hapd, wps_cancel, NULL);
+}
+
+
 #ifdef CONFIG_WPS_OOB
 int hostapd_wps_start_oob(struct hostapd_data *hapd, char *device_type,
 			  char *path, char *method, char *name)
@@ -1154,7 +1197,8 @@
 
 static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, const u8 *da,
 				    const u8 *bssid,
-				    const u8 *ie, size_t ie_len)
+				    const u8 *ie, size_t ie_len,
+				    int ssi_signal)
 {
 	struct hostapd_data *hapd = ctx;
 	struct wpabuf *wps_ie;
@@ -1471,3 +1515,156 @@
 
 	return wps_registrar_config_ap(hapd->wps->registrar, &cred);
 }
+
+
+#ifdef CONFIG_WPS_NFC
+
+struct wps_nfc_password_token_data {
+	const u8 *oob_dev_pw;
+	size_t oob_dev_pw_len;
+	int added;
+};
+
+
+static int wps_add_nfc_password_token(struct hostapd_data *hapd, void *ctx)
+{
+	struct wps_nfc_password_token_data *data = ctx;
+	int ret;
+
+	if (hapd->wps == NULL)
+		return 0;
+	ret = wps_registrar_add_nfc_password_token(hapd->wps->registrar,
+						   data->oob_dev_pw,
+						   data->oob_dev_pw_len);
+	if (ret == 0)
+		data->added++;
+	return ret;
+}
+
+
+static int hostapd_wps_add_nfc_password_token(struct hostapd_data *hapd,
+					      struct wps_parse_attr *attr)
+{
+	struct wps_nfc_password_token_data data;
+
+	data.oob_dev_pw = attr->oob_dev_password;
+	data.oob_dev_pw_len = attr->oob_dev_password_len;
+	data.added = 0;
+	if (hostapd_wps_for_each(hapd, wps_add_nfc_password_token, &data) < 0)
+		return -1;
+	return data.added ? 0 : -1;
+}
+
+
+static int hostapd_wps_nfc_tag_process(struct hostapd_data *hapd,
+				       const struct wpabuf *wps)
+{
+	struct wps_parse_attr attr;
+
+	wpa_hexdump_buf(MSG_DEBUG, "WPS: Received NFC tag payload", wps);
+
+	if (wps_parse_msg(wps, &attr)) {
+		wpa_printf(MSG_DEBUG, "WPS: Ignore invalid data from NFC tag");
+		return -1;
+	}
+
+	if (attr.oob_dev_password)
+		return hostapd_wps_add_nfc_password_token(hapd, &attr);
+
+	wpa_printf(MSG_DEBUG, "WPS: Ignore unrecognized NFC tag");
+	return -1;
+}
+
+
+int hostapd_wps_nfc_tag_read(struct hostapd_data *hapd,
+			     const struct wpabuf *data)
+{
+	const struct wpabuf *wps = data;
+	struct wpabuf *tmp = NULL;
+	int ret;
+
+	if (wpabuf_len(data) < 4)
+		return -1;
+
+	if (*wpabuf_head_u8(data) != 0x10) {
+		/* Assume this contains full NDEF record */
+		tmp = ndef_parse_wifi(data);
+		if (tmp == NULL) {
+			wpa_printf(MSG_DEBUG, "WPS: Could not parse NDEF");
+			return -1;
+		}
+		wps = tmp;
+	}
+
+	ret = hostapd_wps_nfc_tag_process(hapd, wps);
+	wpabuf_free(tmp);
+	return ret;
+}
+
+
+struct wpabuf * hostapd_wps_nfc_config_token(struct hostapd_data *hapd,
+					     int ndef)
+{
+	struct wpabuf *ret;
+
+	if (hapd->wps == NULL)
+		return NULL;
+
+	ret = wps_get_oob_cred(hapd->wps);
+	if (ndef && ret) {
+		struct wpabuf *tmp;
+		tmp = ndef_build_wifi(ret);
+		wpabuf_free(ret);
+		if (tmp == NULL)
+			return NULL;
+		ret = tmp;
+	}
+
+	return ret;
+}
+
+
+struct wpabuf * hostapd_wps_nfc_token_gen(struct hostapd_data *hapd, int ndef)
+{
+	return wps_nfc_token_gen(ndef, &hapd->conf->wps_nfc_dev_pw_id,
+				 &hapd->conf->wps_nfc_dh_pubkey,
+				 &hapd->conf->wps_nfc_dh_privkey,
+				 &hapd->conf->wps_nfc_dev_pw);
+}
+
+
+int hostapd_wps_nfc_token_enable(struct hostapd_data *hapd)
+{
+	struct wps_context *wps = hapd->wps;
+
+	if (wps == NULL)
+		return -1;
+
+	if (!hapd->conf->wps_nfc_dh_pubkey ||
+	    !hapd->conf->wps_nfc_dh_privkey ||
+	    !hapd->conf->wps_nfc_dev_pw ||
+	    !hapd->conf->wps_nfc_dev_pw_id)
+		return -1;
+
+	hostapd_wps_nfc_clear(wps);
+	wps->ap_nfc_dev_pw_id = hapd->conf->wps_nfc_dev_pw_id;
+	wps->ap_nfc_dh_pubkey = wpabuf_dup(hapd->conf->wps_nfc_dh_pubkey);
+	wps->ap_nfc_dh_privkey = wpabuf_dup(hapd->conf->wps_nfc_dh_privkey);
+	wps->ap_nfc_dev_pw = wpabuf_dup(hapd->conf->wps_nfc_dev_pw);
+
+	if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey ||
+	    !wps->ap_nfc_dev_pw) {
+		hostapd_wps_nfc_clear(wps);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void hostapd_wps_nfc_token_disable(struct hostapd_data *hapd)
+{
+	hostapd_wps_nfc_clear(hapd->wps);
+}
+
+#endif /* CONFIG_WPS_NFC */
diff --git a/src/ap/wps_hostapd.h b/src/ap/wps_hostapd.h
index 9194225..f968e15 100644
--- a/src/ap/wps_hostapd.h
+++ b/src/ap/wps_hostapd.h
@@ -1,6 +1,6 @@
 /*
  * hostapd / WPS integration
- * Copyright (c) 2008-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -20,6 +20,7 @@
 			const char *uuid, const char *pin, int timeout);
 int hostapd_wps_button_pushed(struct hostapd_data *hapd,
 			      const u8 *p2p_dev_addr);
+int hostapd_wps_cancel(struct hostapd_data *hapd);
 int hostapd_wps_start_oob(struct hostapd_data *hapd, char *device_type,
 			  char *path, char *method, char *name);
 int hostapd_wps_get_mib_sta(struct hostapd_data *hapd, const u8 *addr,
@@ -32,6 +33,13 @@
 void hostapd_wps_update_ie(struct hostapd_data *hapd);
 int hostapd_wps_config_ap(struct hostapd_data *hapd, const char *ssid,
 			  const char *auth, const char *encr, const char *key);
+int hostapd_wps_nfc_tag_read(struct hostapd_data *hapd,
+			     const struct wpabuf *data);
+struct wpabuf * hostapd_wps_nfc_config_token(struct hostapd_data *hapd,
+					     int ndef);
+struct wpabuf * hostapd_wps_nfc_token_gen(struct hostapd_data *hapd, int ndef);
+int hostapd_wps_nfc_token_enable(struct hostapd_data *hapd);
+void hostapd_wps_nfc_token_disable(struct hostapd_data *hapd);
 
 #else /* CONFIG_WPS */
 
@@ -67,6 +75,11 @@
 	return 0;
 }
 
+static inline int hostapd_wps_cancel(struct hostapd_data *hapd)
+{
+	return 0;
+}
+
 #endif /* CONFIG_WPS */
 
 #endif /* WPS_HOSTAPD_H */
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index b0d310d..d65675c 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -1,6 +1,6 @@
 /*
  * IEEE 802.11 Common routines
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -97,6 +97,11 @@
 			elems->p2p = pos;
 			elems->p2p_len = elen;
 			break;
+		case HS20_INDICATION_OUI_TYPE:
+			/* Hotspot 2.0 */
+			elems->hs20 = pos;
+			elems->hs20_len = elen;
+			break;
 		default:
 			wpa_printf(MSG_MSGDUMP, "Unknown WFA "
 				   "information element ignored "
@@ -257,6 +262,15 @@
 			elems->interworking = pos;
 			elems->interworking_len = elen;
 			break;
+		case WLAN_EID_EXT_CAPAB:
+			elems->ext_capab = pos;
+			elems->ext_capab_len = elen;
+			break;
+		case WLAN_EID_BSS_MAX_IDLE_PERIOD:
+			if (elen < 3)
+				break;
+			elems->bss_max_idle_period = pos;
+			break;
 		default:
 			unknown++;
 			if (!show_errors)
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index bfac88c..d9b2b6c 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -37,6 +37,9 @@
 	const u8 *p2p;
 	const u8 *link_id;
 	const u8 *interworking;
+	const u8 *hs20;
+	const u8 *ext_capab;
+	const u8 *bss_max_idle_period;
 
 	u8 ssid_len;
 	u8 supp_rates_len;
@@ -63,6 +66,8 @@
 	u8 vendor_ht_cap_len;
 	u8 p2p_len;
 	u8 interworking_len;
+	u8 hs20_len;
+	u8 ext_capab_len;
 };
 
 typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes;
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 3bef006..b4f9275 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -228,12 +228,25 @@
 #define WLAN_EID_20_40_BSS_INTOLERANT 73
 #define WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS 74
 #define WLAN_EID_MMIE 76
+#define WLAN_EID_BSS_MAX_IDLE_PERIOD 90
+#define WLAN_EID_TFS_REQ 91
+#define WLAN_EID_TFS_RESP 92
+#define WLAN_EID_WNMSLEEP 93
 #define WLAN_EID_TIME_ZONE 98
 #define WLAN_EID_LINK_ID 101
 #define WLAN_EID_INTERWORKING 107
 #define WLAN_EID_ADV_PROTO 108
 #define WLAN_EID_ROAMING_CONSORTIUM 111
 #define WLAN_EID_EXT_CAPAB 127
+#define WLAN_EID_VHT_CAP 191
+#define WLAN_EID_VHT_OPERATION 192
+#define WLAN_EID_VHT_EXTENDED_BSS_LOAD 193
+#define WLAN_EID_VHT_WIDE_BW_CHSWITCH  194
+#define WLAN_EID_VHT_TRANSMIT_POWER_ENVELOPE 195
+#define WLAN_EID_VHT_CHANNEL_SWITCH_WRAPPER 196
+#define WLAN_EID_VHT_AID 197
+#define WLAN_EID_VHT_QUIET_CHANNEL 198
+#define WLAN_EID_VHT_OPERATING_MODE_NOTIFICATION 199
 #define WLAN_EID_VENDOR_SPECIFIC 221
 
 
@@ -254,6 +267,7 @@
 #define WLAN_ACTION_VENDOR_SPECIFIC 127
 
 /* Public action codes */
+#define WLAN_PA_20_40_BSS_COEX 0
 #define WLAN_PA_VENDOR_SPECIFIC 9
 #define WLAN_PA_GAS_INITIAL_REQ 10
 #define WLAN_PA_GAS_INITIAL_RESP 11
@@ -487,6 +501,17 @@
 				} STRUCT_PACKED sa_query_resp;
 				struct {
 					u8 action;
+					u8 dialogtoken;
+					u8 variable[0];
+				} STRUCT_PACKED wnm_sleep_req;
+				struct {
+					u8 action;
+					u8 dialogtoken;
+					le16 keydata_len;
+					u8 variable[0];
+				} STRUCT_PACKED wnm_sleep_resp;
+				struct {
+					u8 action;
 					u8 variable[0];
 				} STRUCT_PACKED public_action;
 				struct {
@@ -534,6 +559,19 @@
 	u8 basic_set[16];
 } STRUCT_PACKED;
 
+
+struct ieee80211_vht_capabilities {
+	le32 vht_capabilities_info;
+	u8 vht_supported_mcs_set[8];
+} STRUCT_PACKED;
+
+struct ieee80211_vht_operation {
+	u8 vht_op_info_chwidth;
+	u8 vht_op_info_chan_center_freq_seg0_idx;
+	u8 vht_op_info_chan_center_freq_seg1_idx;
+	le16 vht_basic_mcs_set;
+} STRUCT_PACKED;
+
 #ifdef _MSC_VER
 #pragma pack(pop)
 #endif /* _MSC_VER */
@@ -630,12 +668,40 @@
 
 #define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127
 
+/* VHT Defines */
+#define VHT_CAP_MAX_MPDU_LENGTH_7991                ((u32) BIT(0))
+#define VHT_CAP_MAX_MPDU_LENGTH_11454               ((u32) BIT(1))
+#define VHT_CAP_SUPP_CHAN_WIDTH_160MHZ              ((u32) BIT(2))
+#define VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ     ((u32) BIT(3))
+#define VHT_CAP_RXLDPC                              ((u32) BIT(4))
+#define VHT_CAP_SHORT_GI_80                         ((u32) BIT(5))
+#define VHT_CAP_SHORT_GI_160                        ((u32) BIT(6))
+#define VHT_CAP_TXSTBC                              ((u32) BIT(7))
+#define VHT_CAP_RXSTBC_1                            ((u32) BIT(8))
+#define VHT_CAP_RXSTBC_2                            ((u32) BIT(9))
+#define VHT_CAP_RXSTBC_3                            ((u32) BIT(8) | BIT(9))
+#define VHT_CAP_RXSTBC_4                            ((u32) BIT(10))
+#define VHT_CAP_SU_BEAMFORMER_CAPABLE               ((u32) BIT(11))
+#define VHT_CAP_SU_BEAMFORMEE_CAPABLE               ((u32) BIT(12))
+#define VHT_CAP_BEAMFORMER_ANTENNAS_MAX             ((u32) BIT(13) | BIT(14))
+#define VHT_CAP_SOUNDING_DIMENTION_MAX              ((u32) BIT(16) | BIT(17))
+#define VHT_CAP_MU_BEAMFORMER_CAPABLE               ((u32) BIT(19))
+#define VHT_CAP_MU_BEAMFORMEE_CAPABLE               ((u32) BIT(20))
+#define VHT_CAP_VHT_TXOP_PS                         ((u32) BIT(21))
+#define VHT_CAP_HTC_VHT                             ((u32) BIT(22))
+#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT          ((u32) BIT(23))
+#define VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB   ((u32) BIT(27))
+#define VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB     ((u32) BIT(26) | BIT(27))
+#define VHT_CAP_RX_ANTENNA_PATTERN                  ((u32) BIT(28))
+#define VHT_CAP_TX_ANTENNA_PATTERN                  ((u32) BIT(29))
+
 #define OUI_MICROSOFT 0x0050f2 /* Microsoft (also used in Wi-Fi specs)
 				* 00:50:F2 */
 #define WPA_IE_VENDOR_TYPE 0x0050f201
 #define WPS_IE_VENDOR_TYPE 0x0050f204
 #define OUI_WFA 0x506f9a
 #define P2P_IE_VENDOR_TYPE 0x506f9a09
+#define HS20_IE_VENDOR_TYPE 0x506f9a10
 
 #define WMM_OUI_TYPE 2
 #define WMM_OUI_SUBTYPE_INFORMATION_ELEMENT 0
@@ -749,6 +815,16 @@
 };
 
 
+#define HS20_INDICATION_OUI_TYPE 16
+#define HS20_ANQP_OUI_TYPE 17
+#define HS20_STYPE_QUERY_LIST 1
+#define HS20_STYPE_CAPABILITY_LIST 2
+#define HS20_STYPE_OPERATOR_FRIENDLY_NAME 3
+#define HS20_STYPE_WAN_METRICS 4
+#define HS20_STYPE_CONNECTION_CAPABILITY 5
+#define HS20_STYPE_NAI_HOME_REALM_QUERY 6
+#define HS20_STYPE_OPERATING_CLASS 7
+
 /* Wi-Fi Direct (P2P) */
 
 #define P2P_OUI_TYPE 9
@@ -915,4 +991,48 @@
 #define WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED BIT(3)
 #define WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT BIT(4)
 
+/* IEEE Std 802.11-2012, 8.4.2.62 20/40 BSS Coexistence element */
+#define WLAN_20_40_BSS_COEX_INFO_REQ            BIT(0)
+#define WLAN_20_40_BSS_COEX_40MHZ_INTOL         BIT(1)
+#define WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ     BIT(2)
+#define WLAN_20_40_BSS_COEX_OBSS_EXEMPT_REQ     BIT(3)
+#define WLAN_20_40_BSS_COEX_OBSS_EXEMPT_GRNT    BIT(4)
+
+struct ieee80211_2040_bss_coex_ie {
+	u8 element_id;
+	u8 length;
+	u8 coex_param;
+} STRUCT_PACKED;
+
+struct ieee80211_2040_intol_chan_report {
+	u8 element_id;
+	u8 length;
+	u8 op_class;
+	u8 variable[0];	/* Channel List */
+} STRUCT_PACKED;
+
+/* IEEE 802.11v - WNM-Sleep Mode element */
+struct wnm_sleep_element {
+	u8 eid;     /* WLAN_EID_WNMSLEEP */
+	u8 len;
+	u8 action_type; /* WLAN_WNM_SLEEP_ENTER/EXIT */
+	u8 status;
+	le16 intval;
+} STRUCT_PACKED;
+
+enum wnm_sleep_mode_response_status {
+	WNM_STATUS_SLEEP_ACCEPT = 0,
+	WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE = 1,
+	WNM_STATUS_DENIED_ACTION = 2,
+	WNM_STATUS_DENIED_TMP = 3,
+	WNM_STATUS_DENIED_KEY = 4,
+	WNM_STATUS_DENIED_OTHER_WNM_SERVICE = 5
+};
+
+/* WNM-Sleep Mode subelement IDs */
+enum wnm_sleep_mode_subelement_id {
+	WNM_SLEEP_SUBELEM_GTK = 0,
+	WNM_SLEEP_SUBELEM_IGTK = 1
+};
+
 #endif /* IEEE802_11_DEFS_H */
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index 5768fdd..a440b69 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -120,6 +120,8 @@
 #define P2P_EVENT_PROV_DISC_PBC_REQ "P2P-PROV-DISC-PBC-REQ "
 /* parameters: <peer address> */
 #define P2P_EVENT_PROV_DISC_PBC_RESP "P2P-PROV-DISC-PBC-RESP "
+/* parameters: <peer address> <status> */
+#define P2P_EVENT_PROV_DISC_FAILURE "P2P-PROV-DISC-FAILURE"
 /* parameters: <freq> <src addr> <dialog token> <update indicator> <TLVs> */
 #define P2P_EVENT_SERV_DISC_REQ "P2P-SERV-DISC-REQ "
 /* parameters: <src addr> <update indicator> <TLVs> */
@@ -143,6 +145,27 @@
 #define AP_STA_DISCONNECTED "AP-STA-DISCONNECTED "
 
 
+/* BSS command information masks */
+
+#define WPA_BSS_MASK_ALL		0xFFFFFFFF
+#define WPA_BSS_MASK_ID			BIT(0)
+#define WPA_BSS_MASK_BSSID		BIT(1)
+#define WPA_BSS_MASK_FREQ		BIT(2)
+#define WPA_BSS_MASK_BEACON_INT		BIT(3)
+#define WPA_BSS_MASK_CAPABILITIES	BIT(4)
+#define WPA_BSS_MASK_QUAL		BIT(5)
+#define WPA_BSS_MASK_NOISE		BIT(6)
+#define WPA_BSS_MASK_LEVEL		BIT(7)
+#define WPA_BSS_MASK_TSF		BIT(8)
+#define WPA_BSS_MASK_AGE		BIT(9)
+#define WPA_BSS_MASK_IE			BIT(10)
+#define WPA_BSS_MASK_FLAGS		BIT(11)
+#define WPA_BSS_MASK_SSID		BIT(12)
+#define WPA_BSS_MASK_WPS_SCAN		BIT(13)
+#define WPA_BSS_MASK_P2P_SCAN		BIT(14)
+#define WPA_BSS_MASK_INTERNETW		BIT(15)
+
+
 /* wpa_supplicant/hostapd control interface access */
 
 /**
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
index 1171f29..8506fff 100644
--- a/src/crypto/crypto_openssl.c
+++ b/src/crypto/crypto_openssl.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant / wrapper functions for libcrypto
- * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -14,6 +14,7 @@
 #include <openssl/bn.h>
 #include <openssl/evp.h>
 #include <openssl/dh.h>
+#include <openssl/hmac.h>
 
 #include "common.h"
 #include "wpabuf.h"
@@ -452,6 +453,41 @@
 }
 
 
+void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ)
+{
+	DH *dh;
+
+	dh = DH_new();
+	if (dh == NULL)
+		return NULL;
+
+	dh->g = BN_new();
+	if (dh->g == NULL || BN_set_word(dh->g, 2) != 1)
+		goto err;
+
+	dh->p = get_group5_prime();
+	if (dh->p == NULL)
+		goto err;
+
+	dh->priv_key = BN_bin2bn(wpabuf_head(priv), wpabuf_len(priv), NULL);
+	if (dh->priv_key == NULL)
+		goto err;
+
+	dh->pub_key = BN_bin2bn(wpabuf_head(publ), wpabuf_len(publ), NULL);
+	if (dh->pub_key == NULL)
+		goto err;
+
+	if (DH_generate_key(dh) != 1)
+		goto err;
+
+	return dh;
+
+err:
+	DH_free(dh);
+	return NULL;
+}
+
+
 struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public,
 				  const struct wpabuf *own_private)
 {
@@ -497,3 +533,93 @@
 	dh = ctx;
 	DH_free(dh);
 }
+
+
+struct crypto_hash {
+	HMAC_CTX ctx;
+};
+
+
+struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
+				      size_t key_len)
+{
+	struct crypto_hash *ctx;
+	const EVP_MD *md;
+
+	switch (alg) {
+#ifndef OPENSSL_NO_MD5
+	case CRYPTO_HASH_ALG_HMAC_MD5:
+		md = EVP_md5();
+		break;
+#endif /* OPENSSL_NO_MD5 */
+#ifndef OPENSSL_NO_SHA
+	case CRYPTO_HASH_ALG_HMAC_SHA1:
+		md = EVP_sha1();
+		break;
+#endif /* OPENSSL_NO_SHA */
+#ifndef OPENSSL_NO_SHA256
+#ifdef CONFIG_SHA256
+	case CRYPTO_HASH_ALG_HMAC_SHA256:
+		md = EVP_sha256();
+		break;
+#endif /* CONFIG_SHA256 */
+#endif /* OPENSSL_NO_SHA256 */
+	default:
+		return NULL;
+	}
+
+	ctx = os_zalloc(sizeof(*ctx));
+	if (ctx == NULL)
+		return NULL;
+
+#if OPENSSL_VERSION_NUMBER < 0x00909000
+	HMAC_Init_ex(&ctx->ctx, key, key_len, md, NULL);
+#else /* openssl < 0.9.9 */
+	if (HMAC_Init_ex(&ctx->ctx, key, key_len, md, NULL) != 1) {
+		os_free(ctx);
+		return NULL;
+	}
+#endif /* openssl < 0.9.9 */
+
+	return ctx;
+}
+
+
+void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len)
+{
+	if (ctx == NULL)
+		return;
+	HMAC_Update(&ctx->ctx, data, len);
+}
+
+
+int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
+{
+	unsigned int mdlen;
+	int res;
+
+	if (ctx == NULL)
+		return -2;
+
+	if (mac == NULL || len == NULL) {
+		os_free(ctx);
+		return 0;
+	}
+
+	mdlen = *len;
+#if OPENSSL_VERSION_NUMBER < 0x00909000
+	HMAC_Final(&ctx->ctx, mac, &mdlen);
+	res = 1;
+#else /* openssl < 0.9.9 */
+	res = HMAC_Final(&ctx->ctx, mac, &mdlen);
+#endif /* openssl < 0.9.9 */
+	HMAC_CTX_cleanup(&ctx->ctx);
+	os_free(ctx);
+
+	if (res == 1) {
+		*len = mdlen;
+		return 0;
+	}
+
+	return -1;
+}
diff --git a/src/crypto/dh_group5.c b/src/crypto/dh_group5.c
index 9a94ca5..ccdbfc8 100644
--- a/src/crypto/dh_group5.c
+++ b/src/crypto/dh_group5.c
@@ -1,6 +1,6 @@
 /*
  * Diffie-Hellman group 5 operations
- * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009, 2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -22,6 +22,12 @@
 }
 
 
+void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ)
+{
+	return (void *) 1;
+}
+
+
 struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public,
 				  const struct wpabuf *own_private)
 {
diff --git a/src/crypto/dh_group5.h b/src/crypto/dh_group5.h
index 8813427..abee8ea 100644
--- a/src/crypto/dh_group5.h
+++ b/src/crypto/dh_group5.h
@@ -1,6 +1,6 @@
 /*
  * Diffie-Hellman group 5 operations
- * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009, 2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -10,6 +10,7 @@
 #define DH_GROUP5_H
 
 void * dh5_init(struct wpabuf **priv, struct wpabuf **publ);
+void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ);
 struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public,
 				  const struct wpabuf *own_private);
 void dh5_free(void *ctx);
diff --git a/src/crypto/random.c b/src/crypto/random.c
index 55e9391..d85c3e6 100644
--- a/src/crypto/random.c
+++ b/src/crypto/random.c
@@ -128,8 +128,6 @@
 	static unsigned int count = 0;
 
 	count++;
-	wpa_printf(MSG_MSGDUMP, "Add randomness: count=%u entropy=%u",
-		   count, entropy);
 	if (entropy > MIN_COLLECT_ENTROPY && (count & 0x3ff) != 0) {
 		/*
 		 * No need to add more entropy at this point, so save CPU and
@@ -137,6 +135,8 @@
 		 */
 		return;
 	}
+	wpa_printf(MSG_EXCESSIVE, "Add randomness: count=%u entropy=%u",
+		   count, entropy);
 
 	os_get_time(&t);
 	wpa_hexdump_key(MSG_EXCESSIVE, "random pool",
diff --git a/src/crypto/tls.h b/src/crypto/tls.h
index 2bd3bbb..990f6e6 100644
--- a/src/crypto/tls.h
+++ b/src/crypto/tls.h
@@ -21,8 +21,10 @@
 };
 
 enum tls_event {
+	TLS_CERT_CHAIN_SUCCESS,
 	TLS_CERT_CHAIN_FAILURE,
-	TLS_PEER_CERTIFICATE
+	TLS_PEER_CERTIFICATE,
+	TLS_ALERT
 };
 
 /*
@@ -57,6 +59,12 @@
 		const u8 *hash;
 		size_t hash_len;
 	} peer_cert;
+
+	struct {
+		int is_local;
+		const char *type;
+		const char *description;
+	} alert;
 };
 
 struct tls_config {
diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
index aaa920b..09b02e4 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -527,6 +527,15 @@
 			else
 				conn->write_alerts++;
 		}
+		if (tls_global->event_cb != NULL) {
+			union tls_event_data ev;
+			os_memset(&ev, 0, sizeof(ev));
+			ev.alert.is_local = !(where & SSL_CB_READ);
+			ev.alert.type = SSL_alert_type_string_long(ret);
+			ev.alert.description = SSL_alert_desc_string_long(ret);
+			tls_global->event_cb(tls_global->cb_ctx, TLS_ALERT,
+					     &ev);
+		}
 	} else if (where & SSL_CB_EXIT && ret <= 0) {
 		wpa_printf(MSG_DEBUG, "SSL: %s:%s in %s",
 			   str, ret == 0 ? "failed" : "error",
@@ -1274,6 +1283,10 @@
 				       TLS_FAIL_SERVER_CHAIN_PROBE);
 	}
 
+	if (preverify_ok && tls_global->event_cb != NULL)
+		tls_global->event_cb(tls_global->cb_ctx,
+				     TLS_CERT_CHAIN_SUCCESS, NULL);
+
 	return preverify_ok;
 }
 
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index f1d4a14..0aab61e 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -100,6 +100,16 @@
 	 */
 	u8 a_mpdu_params;
 
+	/**
+	 * vht_capab - VHT (IEEE 802.11ac) capabilities
+	 */
+	u32 vht_capab;
+
+	/**
+	 * vht_mcs_set - VHT MCS (IEEE 802.11ac) rate parameters
+	 */
+	u8 vht_mcs_set[8];
+
 	unsigned int flags; /* HOSTAPD_MODE_FLAG_* */
 };
 
@@ -333,6 +343,13 @@
 	int freq;
 
 	/**
+	 * bg_scan_period - Background scan period in seconds, 0 to disable
+	 * background scan, or -1 to indicate no change to default driver
+	 * configuration
+	 */
+	int bg_scan_period;
+
+	/**
 	 * wpa_ie - WPA information element for (Re)Association Request
 	 * WPA information element to be included in (Re)Association
 	 * Request (including information element id and length). Use
@@ -707,6 +724,13 @@
 	 * enabled.
 	 */
 	u8 access_network_type;
+
+	/**
+	 * ap_max_inactivity - Timeout in seconds to detect STA's inactivity
+	 *
+	 * This is used by driver which advertises this capability.
+	 */
+	int ap_max_inactivity;
 };
 
 /**
@@ -726,6 +750,7 @@
 #define WPA_DRIVER_CAPA_ENC_WEP104	0x00000002
 #define WPA_DRIVER_CAPA_ENC_TKIP	0x00000004
 #define WPA_DRIVER_CAPA_ENC_CCMP	0x00000008
+#define WPA_DRIVER_CAPA_ENC_WEP128	0x00000010
 	unsigned int enc;
 
 #define WPA_DRIVER_AUTH_OPEN		0x00000001
@@ -790,6 +815,8 @@
 #define WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD		0x00200000
 /* Driver supports U-APSD in AP mode */
 #define WPA_DRIVER_FLAGS_AP_UAPSD			0x00400000
+/* Driver supports inactivity timer in AP mode */
+#define WPA_DRIVER_FLAGS_INACTIVITY_TIMER		0x00800000
 	unsigned int flags;
 
 	int max_scan_ssids;
@@ -2003,6 +2030,16 @@
 	int (*deinit_ap)(void *priv);
 
 	/**
+	 * deinit_p2p_cli - Deinitialize P2P client mode
+	 * @priv: Private driver interface data
+	 * Returns: 0 on success, -1 on failure (or if not supported)
+	 *
+	 * This optional function can be used to disable P2P client mode. It
+	 * can be used to change the interface type back to station mode.
+	 */
+	int (*deinit_p2p_cli)(void *priv);
+
+	/**
 	 * suspend - Notification on system suspend/hibernate event
 	 * @priv: Private driver interface data
 	 */
@@ -2508,19 +2545,30 @@
 	 */
 	void (*poll_client)(void *priv, const u8 *own_addr,
 			    const u8 *addr, int qos);
-#ifdef ANDROID_P2P
+
 	/**
-	 * switch_channel - Announce channel switch and migrate the GO to a
-	 * given frequency.
+	 * radio_disable - Disable/enable radio
 	 * @priv: Private driver interface data
-	 * @freq: frequency in MHz
+	 * @disabled: 1=disable 0=enable radio
 	 * Returns: 0 on success, -1 on failure
 	 *
-	 * This function is used to move the GO to the legacy STA channel to avoid
-	 * frequency conflict in single channel concurrency.
+	 * This optional command is for testing purposes. It can be used to
+	 * disable the radio on a testbed device to simulate out-of-radio-range
+	 * conditions.
+	 */
+	int (*radio_disable)(void *priv, int disabled);
+
+	/**
+	 * switch_channel - Announce channel switch and migrate the GO to the
+	 * given frequency
+	 * @priv: Private driver interface data
+	 * @freq: Frequency in MHz
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function is used to move the GO to the legacy STA channel to
+	 * avoid frequency conflict in single channel concurrency.
 	 */
 	int (*switch_channel)(void *priv, unsigned int freq);
-#endif
 };
 
 
@@ -2951,7 +2999,14 @@
 	/**
 	 * EVENT_EAPOL_TX_STATUS - notify of EAPOL TX status
 	 */
-	EVENT_EAPOL_TX_STATUS
+	EVENT_EAPOL_TX_STATUS,
+
+	/**
+	 * EVENT_CH_SWITCH - AP or GO decided to switch channels
+	 *
+	 * Described in wpa_event_data.ch_switch
+	 * */
+	EVENT_CH_SWITCH
 };
 
 
@@ -3267,7 +3322,7 @@
 		const u8 *frame;
 		size_t frame_len;
 		u32 datarate;
-		u32 ssi_signal;
+		int ssi_signal; /* dBm */
 	} rx_mgmt;
 
 	/**
@@ -3385,6 +3440,11 @@
 		 * ie_len - Length of ie buffer in octets
 		 */
 		size_t ie_len;
+
+		/**
+		 * signal - signal strength in dBm (or 0 if not available)
+		 */
+		int ssi_signal;
 	} rx_probe_req;
 
 	/**
@@ -3541,6 +3601,18 @@
 		int data_len;
 		int ack;
 	} eapol_tx_status;
+
+	/**
+	 * struct ch_switch
+	 * @freq: Frequency of new channel in MHz
+	 * @ht_enabled: Whether this is an HT channel
+	 * @ch_offset: Secondary channel offset
+	 */
+	struct ch_switch {
+		int freq;
+		int ht_enabled;
+		int ch_offset;
+	} ch_switch;
 };
 
 /**
diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c
index b17d1a6..73898d3 100644
--- a/src/drivers/driver_atheros.c
+++ b/src/drivers/driver_atheros.c
@@ -5,14 +5,8 @@
  * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
  * Copyright (c) 2009, Atheros Communications
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "includes.h"
@@ -39,11 +33,11 @@
 
 #ifdef CONFIG_WPS
 #include <netpacket/packet.h>
+#endif /* CONFIG_WPS */
 
 #ifndef ETH_P_80211_RAW
 #define ETH_P_80211_RAW 0x0019
 #endif
-#endif /* CONFIG_WPS */
 
 #include "linux_wext.h"
 
@@ -73,6 +67,7 @@
 	struct wpabuf *wpa_ie;
 	struct wpabuf *wps_beacon_ie;
 	struct wpabuf *wps_probe_resp_ie;
+	u8	own_addr[ETH_ALEN];
 };
 
 static int atheros_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
@@ -732,8 +727,8 @@
 }
 
 #ifdef CONFIG_WPS
-static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf,
-				size_t len)
+static void atheros_raw_recv_wps(void *ctx, const u8 *src_addr, const u8 *buf,
+				 size_t len)
 {
 	struct atheros_driver_data *drv = ctx;
 	const struct ieee80211_mgmt *mgmt;
@@ -762,28 +757,139 @@
 }
 #endif /* CONFIG_WPS */
 
-static int atheros_receive_probe_req(struct atheros_driver_data *drv)
+#ifdef CONFIG_IEEE80211R
+static void atheros_raw_recv_11r(void *ctx, const u8 *src_addr, const u8 *buf,
+				 size_t len)
+{
+	struct atheros_driver_data *drv = ctx;
+	union wpa_event_data event;
+	const struct ieee80211_mgmt *mgmt;
+	u16 fc;
+	u16 stype;
+	int ielen;
+	const u8 *iebuf;
+
+	/* Do 11R processing for ASSOC/AUTH/FT ACTION frames */
+	if (len < IEEE80211_HDRLEN)
+		return;
+	mgmt = (const struct ieee80211_mgmt *) buf;
+
+	fc = le_to_host16(mgmt->frame_control);
+
+	if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT)
+		return;
+	stype = WLAN_FC_GET_STYPE(fc);
+
+	wpa_printf(MSG_DEBUG, "%s: subtype 0x%x len %d", __func__, stype,
+		   (int) len);
+
+	if (os_memcmp(drv->own_addr, mgmt->bssid, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "%s: BSSID does not match - ignore",
+			   __func__);
+		return;
+	}
+	switch (stype) {
+	case WLAN_FC_STYPE_ASSOC_REQ:
+		if (len - IEEE80211_HDRLEN < sizeof(mgmt->u.assoc_req))
+			break;
+		ielen = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req));
+		iebuf = mgmt->u.assoc_req.variable;
+		drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, 0);
+		break;
+	case WLAN_FC_STYPE_REASSOC_REQ:
+		if (len - IEEE80211_HDRLEN < sizeof(mgmt->u.reassoc_req))
+			break;
+		ielen = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req));
+		iebuf = mgmt->u.reassoc_req.variable;
+		drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, 1);
+		break;
+	case WLAN_FC_STYPE_ACTION:
+		if (&mgmt->u.action.category > buf + len)
+			break;
+		os_memset(&event, 0, sizeof(event));
+		event.rx_action.da = mgmt->da;
+		event.rx_action.sa = mgmt->sa;
+		event.rx_action.bssid = mgmt->bssid;
+		event.rx_action.category = mgmt->u.action.category;
+		event.rx_action.data = &mgmt->u.action.category;
+		event.rx_action.len = buf + len - event.rx_action.data;
+		wpa_supplicant_event(drv->hapd, EVENT_RX_ACTION, &event);
+		break;
+	case WLAN_FC_STYPE_AUTH:
+		if (len - IEEE80211_HDRLEN < sizeof(mgmt->u.auth))
+			break;
+		os_memset(&event, 0, sizeof(event));
+		os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN);
+		os_memcpy(event.auth.bssid, mgmt->bssid, ETH_ALEN);
+		event.auth.auth_type = le_to_host16(mgmt->u.auth.auth_alg);
+		event.auth.status_code =
+			le_to_host16(mgmt->u.auth.status_code);
+		event.auth.auth_transaction =
+			le_to_host16(mgmt->u.auth.auth_transaction);
+		event.auth.ies = mgmt->u.auth.variable;
+		event.auth.ies_len = len - IEEE80211_HDRLEN -
+			sizeof(mgmt->u.auth);
+		wpa_supplicant_event(drv->hapd, EVENT_AUTH, &event);
+		break;
+	default:
+		break;
+	}
+}
+#endif /* CONFIG_IEEE80211R */
+
+#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R)
+static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf,
+				size_t len)
+{
+#ifdef CONFIG_WPS
+	atheros_raw_recv_wps(ctx, src_addr, buf, len);
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_IEEE80211R
+	atheros_raw_recv_11r(ctx, src_addr, buf, len);
+#endif /* CONFIG_IEEE80211R */
+}
+#endif /* CONFIG_WPS || CONFIG_IEEE80211R */
+
+static int atheros_receive_pkt(struct atheros_driver_data *drv)
 {
 	int ret = 0;
-#ifdef CONFIG_WPS
 	struct ieee80211req_set_filter filt;
 
 	wpa_printf(MSG_DEBUG, "%s Enter", __func__);
-	filt.app_filterype = IEEE80211_FILTER_TYPE_PROBE_REQ;
+	filt.app_filterype = 0;
+#ifdef CONFIG_WPS
+	filt.app_filterype |= IEEE80211_FILTER_TYPE_PROBE_REQ;
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_IEEE80211R
+	filt.app_filterype |= (IEEE80211_FILTER_TYPE_ASSOC_REQ |
+			       IEEE80211_FILTER_TYPE_AUTH);
+#endif
+	if (filt.app_filterype) {
+		ret = set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt,
+				   sizeof(struct ieee80211req_set_filter));
+		if (ret)
+			return ret;
+	}
 
-	ret = set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt,
-			   sizeof(struct ieee80211req_set_filter));
-	if (ret)
-		return ret;
-
+#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R)
 	drv->sock_raw = l2_packet_init(drv->iface, NULL, ETH_P_80211_RAW,
 				       atheros_raw_receive, drv, 1);
 	if (drv->sock_raw == NULL)
 		return -1;
-#endif /* CONFIG_WPS */
+#endif /* CONFIG_WPS || CONFIG_IEEE80211R */
+	if (l2_packet_get_own_addr(drv->sock_xmit, drv->own_addr))
+		return -1;
 	return ret;
 }
 
+static int atheros_reset_appfilter(struct atheros_driver_data *drv)
+{
+	struct ieee80211req_set_filter filt;
+	filt.app_filterype = 0;
+	return set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt,
+			    sizeof(struct ieee80211req_set_filter));
+}
+
 #ifdef CONFIG_WPS
 static int
 atheros_set_wps_ie(void *priv, const u8 *ie, size_t len, u32 frametype)
@@ -852,6 +958,84 @@
 #define atheros_set_ap_wps_ie NULL
 #endif /* CONFIG_WPS */
 
+#ifdef CONFIG_IEEE80211R
+static int
+atheros_sta_auth(void *priv, const u8 *own_addr, const u8 *addr, u16 seq,
+		 u16 status_code, const u8 *ie, size_t len)
+{
+	struct atheros_driver_data *drv = priv;
+	struct ieee80211req_mlme mlme;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "%s: addr=%s status_code=%d",
+		   __func__, ether_sprintf(addr), status_code);
+
+	mlme.im_op = IEEE80211_MLME_AUTH;
+	mlme.im_reason = status_code;
+	mlme.im_seq = seq;
+	os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+	mlme.im_optie_len = len;
+	if (len) {
+		if (len < IEEE80211_MAX_OPT_IE) {
+			os_memcpy(mlme.im_optie, ie, len);
+		} else {
+			wpa_printf(MSG_DEBUG, "%s: Not enough space to copy "
+				   "opt_ie STA (addr " MACSTR " reason %d, "
+				   "ie_len %d)",
+				   __func__, MAC2STR(addr), status_code,
+				   (int) len);
+			return -1;
+		}
+	}
+	ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme));
+	if (ret < 0) {
+		wpa_printf(MSG_DEBUG, "%s: Failed to auth STA (addr " MACSTR
+			   " reason %d)",
+			   __func__, MAC2STR(addr), status_code);
+	}
+	return ret;
+}
+
+static int
+atheros_sta_assoc(void *priv, const u8 *own_addr, const u8 *addr,
+		  int reassoc, u16 status_code, const u8 *ie, size_t len)
+{
+	struct atheros_driver_data *drv = priv;
+	struct ieee80211req_mlme mlme;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "%s: addr=%s status_code=%d reassoc %d",
+		   __func__, ether_sprintf(addr), status_code, reassoc);
+
+	if (reassoc)
+		mlme.im_op = IEEE80211_MLME_REASSOC;
+	else
+		mlme.im_op = IEEE80211_MLME_ASSOC;
+	mlme.im_reason = status_code;
+	os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+	mlme.im_optie_len = len;
+	if (len) {
+		if (len < IEEE80211_MAX_OPT_IE) {
+			os_memcpy(mlme.im_optie, ie, len);
+		} else {
+			wpa_printf(MSG_DEBUG, "%s: Not enough space to copy "
+				   "opt_ie STA (addr " MACSTR " reason %d, "
+				   "ie_len %d)",
+				   __func__, MAC2STR(addr), status_code,
+				   (int) len);
+			return -1;
+		}
+	}
+	ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme));
+	if (ret < 0) {
+		wpa_printf(MSG_DEBUG, "%s: Failed to assoc STA (addr " MACSTR
+			   " reason %d)",
+			   __func__, MAC2STR(addr), status_code);
+	}
+	return ret;
+}
+#endif /* CONFIG_IEEE80211R */
+
 static void
 atheros_new_sta(struct atheros_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN])
 {
@@ -980,6 +1164,9 @@
 		 * so all are enabled for WPS... ugh.
 		 */
 		wpa_supplicant_event(drv->hapd, EVENT_WPS_BUTTON_PUSHED, NULL);
+#endif /* CONFIG_WPS */
+#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R)
+#define WPS_FRAM_TAG_SIZE 30 /* hardcoded in driver */
 	} else if (strncmp(custom, "Manage.prob_req ", 16) == 0) {
 		/*
 		 * Atheros driver uses a hack to pass Probe Request frames as a
@@ -987,7 +1174,6 @@
 		 * packet sniffing) didn't work when bridging.
 		 * Format: "Manage.prob_req <frame len>" | zero padding | frame
 		 */
-#define WPS_FRAM_TAG_SIZE 30 /* hardcoded in driver */
 		int len = atoi(custom + 16);
 		if (len < 0 || custom + WPS_FRAM_TAG_SIZE + len > end) {
 			wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req event "
@@ -996,7 +1182,40 @@
 		}
 		atheros_raw_receive(drv, NULL,
 				    (u8 *) custom + WPS_FRAM_TAG_SIZE, len);
-#endif /* CONFIG_WPS */
+	} else if (strncmp(custom, "Manage.assoc_req ", 17) == 0) {
+		/* Format: "Manage.assoc_req <frame len>" | zero padding |
+		 * frame */
+		int len = atoi(custom + 17);
+		if (len < 0 || custom + WPS_FRAM_TAG_SIZE + len > end) {
+			wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req/"
+				   "assoc_req/auth event length %d", len);
+			return;
+		}
+		atheros_raw_receive(drv, NULL,
+				    (u8 *) custom + WPS_FRAM_TAG_SIZE, len);
+	} else if (strncmp(custom, "Manage.action ", 14) == 0) {
+		/* Format: "Manage.assoc_req <frame len>" | zero padding |
+		 * frame */
+		int len = atoi(custom + 14);
+		if (len < 0 || custom + WPS_FRAM_TAG_SIZE + len > end) {
+			wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req/"
+				   "assoc_req/auth event length %d", len);
+			return;
+		}
+		atheros_raw_receive(drv, NULL,
+				    (u8 *) custom + WPS_FRAM_TAG_SIZE, len);
+	} else if (strncmp(custom, "Manage.auth ", 12) == 0) {
+		/* Format: "Manage.auth <frame len>" | zero padding | frame
+		 */
+		int len = atoi(custom + 12);
+		if (len < 0 || custom + WPS_FRAM_TAG_SIZE + len > end) {
+			wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req/"
+				   "assoc_req/auth event length %d", len);
+			return;
+		}
+		atheros_raw_receive(drv, NULL,
+				    (u8 *) custom + WPS_FRAM_TAG_SIZE, len);
+#endif /* CONFIG_WPS or CONFIG_IEEE80211R */
 	}
 }
 
@@ -1278,7 +1497,7 @@
 	linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0);
 	atheros_set_privacy(drv, 0); /* default to no privacy */
 
-	atheros_receive_probe_req(drv);
+	atheros_receive_pkt(drv);
 
 	if (atheros_wireless_event_init(drv))
 		goto bad;
@@ -1302,6 +1521,7 @@
 {
 	struct atheros_driver_data *drv = priv;
 
+	atheros_reset_appfilter(drv);
 	netlink_deinit(drv->netlink);
 	(void) linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0);
 	if (drv->ioctl_sock >= 0)
@@ -1348,7 +1568,6 @@
 	memset(&iwr, 0, sizeof(iwr));
 	os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
 	iwr.u.essid.pointer = (caddr_t) buf;
-	iwr.u.essid.length = len;
 	iwr.u.essid.length = (len > IW_ESSID_MAX_SIZE) ?
 		IW_ESSID_MAX_SIZE : len;
 
@@ -1421,6 +1640,75 @@
 	return 0;
 }
 
+
+#ifdef CONFIG_IEEE80211R
+
+static int atheros_send_mgmt(void *priv, const u8 *frm, size_t data_len,
+			     int noack)
+{
+	struct atheros_driver_data *drv = priv;
+	u8 buf[1510];
+	const struct ieee80211_mgmt *mgmt;
+	struct ieee80211req_mgmtbuf *mgmt_frm;
+
+	mgmt = (const struct ieee80211_mgmt *) frm;
+	wpa_printf(MSG_DEBUG, "%s frmlen = %lu " MACSTR, __func__,
+		   (unsigned long) data_len, MAC2STR(mgmt->da));
+	mgmt_frm = (struct ieee80211req_mgmtbuf *) buf;
+	memcpy(mgmt_frm->macaddr, (u8 *)mgmt->da, IEEE80211_ADDR_LEN);
+	mgmt_frm->buflen = data_len;
+	if (&mgmt_frm->buf[0] + data_len > buf + sizeof(buf)) {
+		wpa_printf(MSG_INFO, "atheros: Too long frame for "
+			   "atheros_send_mgmt (%u)", (unsigned int) data_len);
+		return -1;
+	}
+	os_memcpy(&mgmt_frm->buf[0], frm, data_len);
+	return set80211priv(drv, IEEE80211_IOCTL_SEND_MGMT, mgmt_frm,
+			    sizeof(struct ieee80211req_mgmtbuf) + data_len);
+}
+
+
+static int atheros_add_tspec(void *priv, const u8 *addr, u8 *tspec_ie,
+			     size_t tspec_ielen)
+{
+	struct atheros_driver_data *drv = priv;
+	int retv;
+	struct ieee80211req_res req;
+	struct ieee80211req_res_addts *addts = &req.u.addts;
+
+	wpa_printf(MSG_DEBUG, "%s", __func__);
+	req.type = IEEE80211_RESREQ_ADDTS;
+	os_memcpy(&req.macaddr[0], addr, IEEE80211_ADDR_LEN);
+	os_memcpy(addts->tspecie, tspec_ie, tspec_ielen);
+	retv = set80211priv(drv, IEEE80211_IOCTL_RES_REQ, &req,
+			    sizeof(struct ieee80211req_res));
+	if (retv < 0) {
+		wpa_printf(MSG_DEBUG, "%s IEEE80211_IOCTL_RES_REQ FAILED "
+			   "retv = %d", __func__, retv);
+		return -1;
+	}
+	os_memcpy(tspec_ie, addts->tspecie, tspec_ielen);
+	return addts->status;
+}
+
+
+static int atheros_add_sta_node(void *priv, const u8 *addr, u16 auth_alg)
+{
+	struct atheros_driver_data *drv = priv;
+	struct ieee80211req_res req;
+	struct ieee80211req_res_addnode *addnode = &req.u.addnode;
+
+	wpa_printf(MSG_DEBUG, "%s", __func__);
+	req.type = IEEE80211_RESREQ_ADDNODE;
+	os_memcpy(&req.macaddr[0], addr, IEEE80211_ADDR_LEN);
+	addnode->auth_alg = auth_alg;
+	return set80211priv(drv, IEEE80211_IOCTL_RES_REQ, &req,
+			    sizeof(struct ieee80211req_res));
+}
+
+#endif /* CONFIG_IEEE80211R */
+
+
 const struct wpa_driver_ops wpa_driver_atheros_ops = {
 	.name			= "atheros",
 	.hapd_init		= atheros_init,
@@ -1444,4 +1732,11 @@
 	.set_ap_wps_ie		= atheros_set_ap_wps_ie,
 	.set_authmode		= atheros_set_authmode,
 	.set_ap			= atheros_set_ap,
+#ifdef CONFIG_IEEE80211R
+	.sta_assoc              = atheros_sta_assoc,
+	.sta_auth               = atheros_sta_auth,
+	.send_mlme       	= atheros_send_mgmt,
+	.add_tspec      	= atheros_add_tspec,
+	.add_sta_node    	= atheros_add_sta_node,
+#endif /* CONFIG_IEEE80211R */
 };
diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c
index 4596a51..67abb67 100644
--- a/src/drivers/driver_bsd.c
+++ b/src/drivers/driver_bsd.c
@@ -3,14 +3,8 @@
  * Copyright (c) 2004, Sam Leffler <sam@errno.com>
  * Copyright (c) 2004, 2Wire, Inc
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "includes.h"
diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c
index 345e851..81856aa 100644
--- a/src/drivers/driver_common.c
+++ b/src/drivers/driver_common.c
@@ -77,6 +77,7 @@
 	E2S(SCHED_SCAN_STOPPED);
 	E2S(DRIVER_CLIENT_POLL_OK);
 	E2S(EAPOL_TX_STATUS);
+	E2S(CH_SWITCH);
 	}
 
 	return "UNKNOWN";
diff --git a/src/drivers/driver_madwifi.c b/src/drivers/driver_madwifi.c
index edb086f..bb48011 100644
--- a/src/drivers/driver_madwifi.c
+++ b/src/drivers/driver_madwifi.c
@@ -4,14 +4,8 @@
  * Copyright (c) 2004, Video54 Technologies
  * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  *
  * While this driver wrapper supports both AP (hostapd) and station
  * (wpa_supplicant) operations, the station side is deprecated and
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 8795104..d54d0f5 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -189,6 +189,7 @@
 	unsigned int beacon_set:1;
 	unsigned int added_if_into_bridge:1;
 	unsigned int added_bridge:1;
+	unsigned int in_deinit:1;
 
 	u8 addr[ETH_ALEN];
 
@@ -456,6 +457,7 @@
 			     valid_data);
 }
 
+
 int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv,
 			      struct nl_msg *msg,
 			      int (*valid_handler)(struct nl_msg *, void *),
@@ -797,10 +799,28 @@
 		   del ? "removed" : "added");
 
 	if (os_strcmp(drv->first_bss.ifname, event.interface_status.ifname) == 0) {
-		if (del)
+		if (del) {
+			if (drv->if_removed) {
+				wpa_printf(MSG_DEBUG, "nl80211: if_removed "
+					   "already set - ignore event");
+				return;
+			}
 			drv->if_removed = 1;
-		else
+		} else {
+			if (if_nametoindex(drv->first_bss.ifname) == 0) {
+				wpa_printf(MSG_DEBUG, "nl80211: Interface %s "
+					   "does not exist - ignore "
+					   "RTM_NEWLINK",
+					   drv->first_bss.ifname);
+				return;
+			}
+			if (!drv->if_removed) {
+				wpa_printf(MSG_DEBUG, "nl80211: if_removed "
+					   "already cleared - ignore event");
+				return;
+			}
 			drv->if_removed = 0;
+		}
 	}
 
 	wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
@@ -917,6 +937,14 @@
 			wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up "
 				   "event since interface %s is down",
 				   namebuf);
+		} else if (if_nametoindex(drv->first_bss.ifname) == 0) {
+			wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up "
+				   "event since interface %s does not exist",
+				   drv->first_bss.ifname);
+		} else if (drv->if_removed) {
+			wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up "
+				   "event since interface %s is marked "
+				   "removed", drv->first_bss.ifname);
 		} else {
 			wpa_printf(MSG_DEBUG, "nl80211: Interface up");
 			drv->if_disabled = 0;
@@ -1103,6 +1131,7 @@
 	}
 
 	event.assoc_info.freq = drv->assoc_freq;
+
 	wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
 }
 
@@ -1176,9 +1205,43 @@
 	drv->associated = 0;
 	os_memset(&data, 0, sizeof(data));
 	if (reason)
-		data.disassoc_info.reason_code = nla_get_u16(reason);
-	data.disassoc_info.locally_generated = by_ap == NULL;
-	wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, &data);
+		data.deauth_info.reason_code = nla_get_u16(reason);
+	data.deauth_info.locally_generated = by_ap == NULL;
+	wpa_supplicant_event(drv->ctx, EVENT_DEAUTH, &data);
+}
+
+
+static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv,
+				 struct nlattr *freq, struct nlattr *type)
+{
+	union wpa_event_data data;
+	int ht_enabled = 1;
+	int chan_offset = 0;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Channel switch event");
+
+	if (!freq || !type)
+		return;
+
+	switch (nla_get_u32(type)) {
+	case NL80211_CHAN_NO_HT:
+		ht_enabled = 0;
+		break;
+	case NL80211_CHAN_HT20:
+		break;
+	case NL80211_CHAN_HT40PLUS:
+		chan_offset = 1;
+		break;
+	case NL80211_CHAN_HT40MINUS:
+		chan_offset = -1;
+		break;
+	}
+
+	data.ch_switch.freq = nla_get_u32(freq);
+	data.ch_switch.ht_enabled = ht_enabled;
+	data.ch_switch.ch_offset = chan_offset;
+
+	wpa_supplicant_event(drv->ctx, EVENT_CH_SWITCH, &data);
 }
 
 
@@ -1208,11 +1271,13 @@
 
 
 static void mlme_event_mgmt(struct wpa_driver_nl80211_data *drv,
-			    struct nlattr *freq, const u8 *frame, size_t len)
+			    struct nlattr *freq, struct nlattr *sig,
+			    const u8 *frame, size_t len)
 {
 	const struct ieee80211_mgmt *mgmt;
 	union wpa_event_data event;
 	u16 fc, stype;
+	int ssi_signal = 0;
 
 	mgmt = (const struct ieee80211_mgmt *) frame;
 	if (len < 24) {
@@ -1223,6 +1288,9 @@
 	fc = le_to_host16(mgmt->frame_control);
 	stype = WLAN_FC_GET_STYPE(fc);
 
+	if (sig)
+		ssi_signal = (s32) nla_get_u32(sig);
+
 	os_memset(&event, 0, sizeof(event));
 	if (freq) {
 		event.rx_action.freq = nla_get_u32(freq);
@@ -1239,6 +1307,7 @@
 	} else {
 		event.rx_mgmt.frame = frame;
 		event.rx_mgmt.frame_len = len;
+		event.rx_mgmt.ssi_signal = ssi_signal;
 		wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
 	}
 }
@@ -1379,7 +1448,7 @@
 		       enum nl80211_commands cmd, struct nlattr *frame,
 		       struct nlattr *addr, struct nlattr *timed_out,
 		       struct nlattr *freq, struct nlattr *ack,
-		       struct nlattr *cookie)
+		       struct nlattr *cookie, struct nlattr *sig)
 {
 	if (timed_out && addr) {
 		mlme_timeout_event(drv, cmd, addr);
@@ -1412,7 +1481,8 @@
 					   nla_data(frame), nla_len(frame));
 		break;
 	case NL80211_CMD_FRAME:
-		mlme_event_mgmt(drv, freq, nla_data(frame), nla_len(frame));
+		mlme_event_mgmt(drv, freq, sig, nla_data(frame),
+				nla_len(frame));
 		break;
 	case NL80211_CMD_FRAME_TX_STATUS:
 		mlme_event_mgmt_tx_status(drv, cookie, nla_data(frame),
@@ -2084,7 +2154,8 @@
 		mlme_event(drv, cmd, tb[NL80211_ATTR_FRAME],
 			   tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT],
 			   tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK],
-			   tb[NL80211_ATTR_COOKIE]);
+			   tb[NL80211_ATTR_COOKIE],
+			   tb[NL80211_ATTR_RX_SIGNAL_DBM]);
 		break;
 	case NL80211_CMD_CONNECT:
 	case NL80211_CMD_ROAM:
@@ -2094,6 +2165,10 @@
 				   tb[NL80211_ATTR_REQ_IE],
 				   tb[NL80211_ATTR_RESP_IE]);
 		break;
+	case NL80211_CMD_CH_SWITCH_NOTIFY:
+		mlme_event_ch_switch(drv, tb[NL80211_ATTR_WIPHY_FREQ],
+				     tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
+		break;
 	case NL80211_CMD_DISCONNECT:
 		mlme_event_disconnect(drv, tb[NL80211_ATTR_REASON_CODE],
 				      tb[NL80211_ATTR_MAC],
@@ -2176,7 +2251,7 @@
 	struct nl80211_global *global = arg;
 	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
 	struct nlattr *tb[NL80211_ATTR_MAX + 1];
-	struct wpa_driver_nl80211_data *drv;
+	struct wpa_driver_nl80211_data *drv, *tmp;
 	int ifidx = -1;
 
 	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
@@ -2185,15 +2260,11 @@
 	if (tb[NL80211_ATTR_IFINDEX])
 		ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
 
-	dl_list_for_each(drv, &global->interfaces,
-			 struct wpa_driver_nl80211_data, list) {
+	dl_list_for_each_safe(drv, tmp, &global->interfaces,
+			      struct wpa_driver_nl80211_data, list) {
 		if (ifidx == -1 || ifidx == drv->ifindex ||
-		    have_ifidx(drv, ifidx)) {
+		    have_ifidx(drv, ifidx))
 			do_process_drv_event(drv, gnlh->cmd, tb);
-#ifdef ANDROID_P2P
-			break;
-#endif
-		}
 	}
 
 	return NL_SKIP;
@@ -2215,7 +2286,8 @@
 		mlme_event(bss->drv, gnlh->cmd, tb[NL80211_ATTR_FRAME],
 			   tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT],
 			   tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK],
-			   tb[NL80211_ATTR_COOKIE]);
+			   tb[NL80211_ATTR_COOKIE],
+			   tb[NL80211_ATTR_RX_SIGNAL_DBM]);
 		break;
 	case NL80211_CMD_UNEXPECTED_FRAME:
 		nl80211_spurious_frame(bss, tb, 0);
@@ -2504,6 +2576,9 @@
 
 		if (flags & NL80211_FEATURE_SK_TX_STATUS)
 			info->data_tx_status = 1;
+
+		if (flags & NL80211_FEATURE_INACTIVITY_TIMER)
+			capa->flags |= WPA_DRIVER_FLAGS_INACTIVITY_TIMER;
 	}
 
 	if (tb[NL80211_ATTR_PROBE_RESP_OFFLOAD]) {
@@ -2571,17 +2646,29 @@
 	drv->capa.flags |= WPA_DRIVER_FLAGS_SANE_ERROR_CODES;
 	drv->capa.flags |= WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE;
 	drv->capa.flags |= WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
-	drv->capa.flags |= WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS;
+#ifndef ANDROID_P2P
+	if (!info.device_ap_sme)
+#endif
+		drv->capa.flags |= WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS;
 
 	drv->device_ap_sme = info.device_ap_sme;
 	drv->poll_command_supported = info.poll_command_supported;
 	drv->data_tx_status = info.data_tx_status;
 
+#ifdef ANDROID_P2P
+	if(drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) {
+		/* Driver is new enough to support monitorless mode*/
+		wpa_printf(MSG_DEBUG, "nl80211: Driver is new "
+			  "enough to support monitor-less mode");
+		drv->use_monitor = 0;
+	}
+#else
 	/*
 	 * If poll command is supported mac80211 is new enough to
 	 * have everything we need to not need monitor interfaces.
 	 */
 	drv->use_monitor = !info.poll_command_supported;
+#endif
 
 	if (drv->device_ap_sme && drv->use_monitor) {
 		/*
@@ -2803,10 +2890,7 @@
 	u8 data[2048];
 	struct msghdr msg;
 	struct iovec entry;
-	struct {
-		struct cmsghdr cm;
-		char control[512];
-	} control;
+	u8 control[512];
 	struct cmsghdr *cmsg;
 	int res, found_ee = 0, found_wifi = 0, acked = 0;
 	union wpa_event_data event;
@@ -3309,6 +3393,7 @@
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 
+	bss->in_deinit = 1;
 	if (drv->data_tx_status)
 		eloop_unregister_read_sock(drv->eapol_tx_sock);
 	if (drv->eapol_tx_sock >= 0)
@@ -3467,6 +3552,8 @@
 	}
 
 	if (params->p2p_probe) {
+		wpa_printf(MSG_DEBUG, "nl80211: P2P probe - mask SuppRates");
+
 		/*
 		 * Remove 2.4 GHz rates 1, 2, 5.5, 11 Mbps from supported rates
 		 * by masking out everything else apart from the OFDM rates 6,
@@ -4731,6 +4818,18 @@
 			os_memcpy(mode->mcs_set, mcs, 16);
 		}
 
+		if (tb_band[NL80211_BAND_ATTR_VHT_CAPA]) {
+			mode->vht_capab = nla_get_u32(
+				tb_band[NL80211_BAND_ATTR_VHT_CAPA]);
+		}
+
+		if (tb_band[NL80211_BAND_ATTR_VHT_MCS_SET] &&
+		    nla_len(tb_band[NL80211_BAND_ATTR_VHT_MCS_SET])) {
+			u8 *mcs;
+			mcs = nla_data(tb_band[NL80211_BAND_ATTR_VHT_MCS_SET]);
+			os_memcpy(mode->vht_mcs_set, mcs, 8);
+		}
+
 		nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) {
 			nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq),
 				  nla_len(nl_freq), freq_policy);
@@ -5191,7 +5290,9 @@
 	if (drv->device_ap_sme && is_ap_interface(drv->nlmode)) {
 		if (freq == 0)
 			freq = bss->freq;
-		return nl80211_send_frame_cmd(bss, freq, 0,
+		return nl80211_send_frame_cmd(bss, freq,
+					      (int) freq == bss->freq ? 0 :
+					      wait_time,
 					      data, data_len,
 					      &drv->send_action_cookie,
 					      no_cck, noack, offchanok);
@@ -5403,6 +5504,11 @@
 			wpabuf_head(params->assocresp_ies));
 	}
 
+	if (drv->capa.flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)  {
+		NLA_PUT_U16(msg, NL80211_ATTR_INACTIVITY_TIMEOUT,
+			    params->ap_max_inactivity);
+	}
+
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)",
@@ -5413,7 +5519,6 @@
 				params->short_slot_time, params->ht_opmode,
 				params->isolate, params->basic_rates);
 	}
-
 	return ret;
  nla_put_failure:
 	nlmsg_free(msg);
@@ -5851,8 +5956,8 @@
 		case IEEE80211_RADIOTAP_RATE:
 			datarate = *iter.this_arg * 5;
 			break;
-		case IEEE80211_RADIOTAP_DB_ANTSIGNAL:
-			ssi_signal = *iter.this_arg;
+		case IEEE80211_RADIOTAP_DBM_ANTSIGNAL:
+			ssi_signal = (s8) *iter.this_arg;
 			break;
 		}
 	}
@@ -6149,7 +6254,6 @@
 		if (nl80211_mgmt_subscribe_ap(bss))
 			return -1;
 
-#ifndef ANDROID_P2P
 	if (drv->device_ap_sme && !drv->use_monitor)
 		if (nl80211_mgmt_subscribe_ap_dev_sme(bss))
 			return -1;
@@ -6157,15 +6261,17 @@
 	if (!drv->device_ap_sme && drv->use_monitor &&
 	    nl80211_create_monitor_interface(drv) &&
 	    !drv->device_ap_sme)
-#else
-	if (drv->device_ap_sme)
+		return -1;
+
+#ifdef ANDROID_P2P
+	if (drv->device_ap_sme && drv->use_monitor)
 		if (nl80211_mgmt_subscribe_ap_dev_sme(bss))
 			return -1;
 
 	if (drv->use_monitor &&
 	    nl80211_create_monitor_interface(drv))
-#endif
 		return -1;
+#endif
 
 	if (drv->device_ap_sme &&
 	    wpa_driver_nl80211_probe_req_report(bss, 1) < 0) {
@@ -6568,6 +6674,12 @@
 		wpa_printf(MSG_DEBUG, "  * freq=%d", params->freq);
 		NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq);
 	}
+	if (params->bg_scan_period >= 0) {
+		wpa_printf(MSG_DEBUG, "  * bg scan period=%d",
+			   params->bg_scan_period);
+		NLA_PUT_U16(msg, NL80211_ATTR_BG_SCAN_PERIOD,
+			    params->bg_scan_period);
+	}
 	if (params->ssid) {
 		wpa_hexdump_ascii(MSG_DEBUG, "  * SSID",
 				  params->ssid, params->ssid_len);
@@ -6765,6 +6877,12 @@
 		drv->assoc_freq = params->freq;
 	} else
 		drv->assoc_freq = 0;
+	if (params->bg_scan_period >= 0) {
+		wpa_printf(MSG_DEBUG, "  * bg scan period=%d",
+			   params->bg_scan_period);
+		NLA_PUT_U16(msg, NL80211_ATTR_BG_SCAN_PERIOD,
+			    params->bg_scan_period);
+	}
 	if (params->ssid) {
 		wpa_hexdump_ascii(MSG_DEBUG, "  * SSID",
 				  params->ssid, params->ssid_len);
@@ -6980,7 +7098,7 @@
 		nl80211_mgmt_unsubscribe(bss, "mode change");
 	}
 
-	if (!is_ap_interface(nlmode) &&
+	if (!bss->in_deinit && !is_ap_interface(nlmode) &&
 	    nl80211_mgmt_subscribe_non_ap(bss) < 0)
 		wpa_printf(MSG_DEBUG, "nl80211: Failed to register Action "
 			   "frame processing - ignore for now");
@@ -7402,8 +7520,12 @@
 			   int reason)
 {
 	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct ieee80211_mgmt mgmt;
 
+	if (drv->device_ap_sme)
+		return wpa_driver_nl80211_sta_remove(bss, addr);
+
 	memset(&mgmt, 0, sizeof(mgmt));
 	mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
 					  WLAN_FC_STYPE_DEAUTH);
@@ -7421,8 +7543,12 @@
 			     int reason)
 {
 	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct ieee80211_mgmt mgmt;
 
+	if (drv->device_ap_sme)
+		return wpa_driver_nl80211_sta_remove(bss, addr);
+
 	memset(&mgmt, 0, sizeof(mgmt));
 	mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
 					  WLAN_FC_STYPE_DISASSOC);
@@ -7967,6 +8093,9 @@
 	if (!msg)
 		return -1;
 
+	wpa_printf(MSG_DEBUG, "nl80211: CMD_FRAME freq=%u wait=%u no_cck=%d "
+		   "no_ack=%d offchanok=%d",
+		   freq, wait, no_cck, no_ack, offchanok);
 	nl80211_cmd(drv, msg, 0, NL80211_CMD_FRAME);
 
 	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
@@ -8269,6 +8398,16 @@
 }
 
 
+static int wpa_driver_nl80211_deinit_p2p_cli(void *priv)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	if (drv->nlmode != NL80211_IFTYPE_P2P_CLIENT)
+		return -1;
+	return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION);
+}
+
+
 static void wpa_driver_nl80211_resume(void *priv)
 {
 	struct i802_bss *bss = priv;
@@ -8407,11 +8546,9 @@
 			   MACSTR,
 			   driver->phyname, driver->first_bss.ifname,
 			   MAC2STR(driver->first_bss.addr));
-#ifdef ANDROID_P2P
-		if(is_ap_interface(driver->nlmode))
+		if (is_ap_interface(driver->nlmode))
 			freq = driver->first_bss.freq;
 		else
-#endif
 			freq = nl80211_get_assoc_freq(driver);
 		wpa_printf(MSG_DEBUG, "nl80211: Shared freq for PHY %s: %d",
 			   drv->phyname, freq);
@@ -9016,6 +9153,7 @@
 	wpa_driver_nl80211_cancel_remain_on_channel,
 	.probe_req_report = wpa_driver_nl80211_probe_req_report,
 	.deinit_ap = wpa_driver_nl80211_deinit_ap,
+	.deinit_p2p_cli = wpa_driver_nl80211_deinit_p2p_cli,
 	.resume = wpa_driver_nl80211_resume,
 	.send_ft_action = nl80211_send_ft_action,
 	.signal_monitor = nl80211_signal_monitor,
diff --git a/src/drivers/driver_privsep.c b/src/drivers/driver_privsep.c
index 81a328a..c8916f0 100644
--- a/src/drivers/driver_privsep.c
+++ b/src/drivers/driver_privsep.c
@@ -651,7 +651,7 @@
 	os_strlcpy(addr.sun_path, drv->own_socket_path, sizeof(addr.sun_path));
 	if (bind(drv->priv_socket, (struct sockaddr *) &addr, sizeof(addr)) <
 	    0) {
-		perror("bind(PF_UNIX)");
+		perror("privsep-set-params priv-sock: bind(PF_UNIX)");
 		close(drv->priv_socket);
 		drv->priv_socket = -1;
 		unlink(drv->own_socket_path);
@@ -676,7 +676,7 @@
 	os_strlcpy(addr.sun_path, drv->own_cmd_path, sizeof(addr.sun_path));
 	if (bind(drv->cmd_socket, (struct sockaddr *) &addr, sizeof(addr)) < 0)
 	{
-		perror("bind(PF_UNIX)");
+		perror("privsep-set-params cmd-sock: bind(PF_UNIX)");
 		close(drv->cmd_socket);
 		drv->cmd_socket = -1;
 		unlink(drv->own_cmd_path);
diff --git a/src/drivers/driver_roboswitch.c b/src/drivers/driver_roboswitch.c
index 61b75b1..0a9078a 100644
--- a/src/drivers/driver_roboswitch.c
+++ b/src/drivers/driver_roboswitch.c
@@ -2,14 +2,8 @@
  * WPA Supplicant - roboswitch driver interface
  * Copyright (c) 2008-2009 Jouke Witteveen
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "includes.h"
diff --git a/src/drivers/driver_test.c b/src/drivers/driver_test.c
index 43b30e9..f011651 100644
--- a/src/drivers/driver_test.c
+++ b/src/drivers/driver_test.c
@@ -1275,7 +1275,7 @@
 			alen = sizeof(addr_un);
 		}
 		if (bind(drv->test_socket, addr, alen) < 0) {
-			perror("bind(PF_UNIX)");
+			perror("test-driver-init: bind(PF_UNIX)");
 			close(drv->test_socket);
 			if (drv->own_socket_path)
 				unlink(drv->own_socket_path);
@@ -2252,7 +2252,7 @@
 	os_strlcpy(addr.sun_path, drv->own_socket_path, sizeof(addr.sun_path));
 	if (bind(drv->test_socket, (struct sockaddr *) &addr,
 		 sizeof(addr)) < 0) {
-		perror("bind(PF_UNIX)");
+		perror("test-driver-attach: bind(PF_UNIX)");
 		close(drv->test_socket);
 		unlink(drv->own_socket_path);
 		os_free(drv->own_socket_path);
@@ -2867,7 +2867,8 @@
 	if (!drv->p2p)
 		return -1;
 	return p2p_connect(drv->p2p, peer_addr, wps_method, go_intent,
-			   own_interface_addr, force_freq, persistent_group);
+			   own_interface_addr, force_freq, persistent_group,
+			   NULL, 0, 0);
 }
 
 
@@ -2912,7 +2913,7 @@
 
 static int test_p2p_scan(void *ctx, enum p2p_scan_type type, int freq,
 			 unsigned int num_req_dev_types,
-			 const u8 *req_dev_types, const u8 *dev_id)
+			 const u8 *req_dev_types, const u8 *dev_id, u16 pw_id)
 {
 	struct wpa_driver_test_data *drv = ctx;
 	struct wpa_driver_scan_params params;
@@ -2933,8 +2934,8 @@
 
 #if 0 /* TODO: WPS IE */
 	wpa_s->wps->dev.p2p = 1;
-	wps_ie = wps_build_probe_req_ie(0, &wpa_s->wps->dev, wpa_s->wps->uuid,
-					WPS_REQ_ENROLLEE);
+	wps_ie = wps_build_probe_req_ie(pw_id, &wpa_s->wps->dev,
+					wpa_s->wps->uuid, WPS_REQ_ENROLLEE);
 #else
 	wps_ie = wpabuf_alloc(1);
 #endif
@@ -2961,11 +2962,6 @@
 		break;
 	case P2P_SCAN_FULL:
 		break;
-	case P2P_SCAN_SPECIFIC:
-		social_channels[0] = freq;
-		social_channels[1] = 0;
-		params.freqs = social_channels;
-		break;
 	case P2P_SCAN_SOCIAL_PLUS_ONE:
 		social_channels[3] = freq;
 		params.freqs = social_channels;
diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c
index 204de34..fd90438 100644
--- a/src/drivers/driver_wext.c
+++ b/src/drivers/driver_wext.c
@@ -584,10 +584,28 @@
 		   del ? "removed" : "added");
 
 	if (os_strcmp(drv->ifname, event.interface_status.ifname) == 0) {
-		if (del)
+		if (del) {
+			if (drv->if_removed) {
+				wpa_printf(MSG_DEBUG, "WEXT: if_removed "
+					   "already set - ignore event");
+				return;
+			}
 			drv->if_removed = 1;
-		else
+		} else {
+			if (if_nametoindex(drv->ifname) == 0) {
+				wpa_printf(MSG_DEBUG, "WEXT: Interface %s "
+					   "does not exist - ignore "
+					   "RTM_NEWLINK",
+					   drv->ifname);
+				return;
+			}
+			if (!drv->if_removed) {
+				wpa_printf(MSG_DEBUG, "WEXT: if_removed "
+					   "already cleared - ignore event");
+				return;
+			}
 			drv->if_removed = 0;
+		}
 	}
 
 	wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
@@ -643,6 +661,7 @@
 	struct wpa_driver_wext_data *drv = ctx;
 	int attrlen, rta_len;
 	struct rtattr *attr;
+	char namebuf[IFNAMSIZ];
 
 	if (!wpa_driver_wext_own_ifindex(drv, ifi->ifi_index, buf, len)) {
 		wpa_printf(MSG_DEBUG, "Ignore event for foreign ifindex %d",
@@ -665,9 +684,25 @@
 	}
 
 	if (drv->if_disabled && (ifi->ifi_flags & IFF_UP)) {
-		wpa_printf(MSG_DEBUG, "WEXT: Interface up");
-		drv->if_disabled = 0;
-		wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED, NULL);
+		if (if_indextoname(ifi->ifi_index, namebuf) &&
+		    linux_iface_up(drv->ioctl_sock, drv->ifname) == 0) {
+			wpa_printf(MSG_DEBUG, "WEXT: Ignore interface up "
+				   "event since interface %s is down",
+				   namebuf);
+		} else if (if_nametoindex(drv->ifname) == 0) {
+			wpa_printf(MSG_DEBUG, "WEXT: Ignore interface up "
+				   "event since interface %s does not exist",
+				   drv->ifname);
+		} else if (drv->if_removed) {
+			wpa_printf(MSG_DEBUG, "WEXT: Ignore interface up "
+				   "event since interface %s is marked "
+				   "removed", drv->ifname);
+		} else {
+			wpa_printf(MSG_DEBUG, "WEXT: Interface up");
+			drv->if_disabled = 0;
+			wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED,
+					     NULL);
+		}
 	}
 
 	/*
@@ -1566,6 +1601,7 @@
 		}
 		drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 |
 			WPA_DRIVER_CAPA_ENC_WEP104;
+		drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP128;
 		if (range->enc_capa & IW_ENC_CAPA_CIPHER_TKIP)
 			drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP;
 		if (range->enc_capa & IW_ENC_CAPA_CIPHER_CCMP)
diff --git a/src/drivers/driver_wired.c b/src/drivers/driver_wired.c
index 618db26..e0f0f22 100644
--- a/src/drivers/driver_wired.c
+++ b/src/drivers/driver_wired.c
@@ -3,14 +3,8 @@
  * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
  * Copyright (c) 2004, Gunter Burchardt <tira@isx.de>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "includes.h"
diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
index 4f98fae..74cc55c 100644
--- a/src/drivers/nl80211_copy.h
+++ b/src/drivers/nl80211_copy.h
@@ -156,21 +156,25 @@
  * @NL80211_CMD_DEL_KEY: delete a key identified by %NL80211_ATTR_KEY_IDX
  *	or %NL80211_ATTR_MAC.
  *
- * @NL80211_CMD_GET_BEACON: retrieve beacon information (returned in a
- *	%NL80222_CMD_NEW_BEACON message)
- * @NL80211_CMD_SET_BEACON: set the beacon on an access point interface
- *	using the %NL80211_ATTR_BEACON_INTERVAL, %NL80211_ATTR_DTIM_PERIOD,
- *	%NL80211_ATTR_BEACON_HEAD and %NL80211_ATTR_BEACON_TAIL attributes.
- *	Following attributes are provided for drivers that generate full Beacon
- *	and Probe Response frames internally: %NL80211_ATTR_SSID,
+ * @NL80211_CMD_GET_BEACON: (not used)
+ * @NL80211_CMD_SET_BEACON: change the beacon on an access point interface
+ *	using the %NL80211_ATTR_BEACON_HEAD and %NL80211_ATTR_BEACON_TAIL
+ *	attributes. For drivers that generate the beacon and probe responses
+ *	internally, the following attributes must be provided: %NL80211_ATTR_IE,
+ *	%NL80211_ATTR_IE_PROBE_RESP and %NL80211_ATTR_IE_ASSOC_RESP.
+ * @NL80211_CMD_START_AP: Start AP operation on an AP interface, parameters
+ *	are like for %NL80211_CMD_SET_BEACON, and additionally parameters that
+ *	do not change are used, these include %NL80211_ATTR_BEACON_INTERVAL,
+ *	%NL80211_ATTR_DTIM_PERIOD, %NL80211_ATTR_SSID,
  *	%NL80211_ATTR_HIDDEN_SSID, %NL80211_ATTR_CIPHERS_PAIRWISE,
  *	%NL80211_ATTR_CIPHER_GROUP, %NL80211_ATTR_WPA_VERSIONS,
  *	%NL80211_ATTR_AKM_SUITES, %NL80211_ATTR_PRIVACY,
- *	%NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_IE, %NL80211_ATTR_IE_PROBE_RESP,
- *	%NL80211_ATTR_IE_ASSOC_RESP.
- * @NL80211_CMD_NEW_BEACON: add a new beacon to an access point interface,
- *	parameters are like for %NL80211_CMD_SET_BEACON.
- * @NL80211_CMD_DEL_BEACON: remove the beacon, stop sending it
+ *	%NL80211_ATTR_AUTH_TYPE and %NL80211_ATTR_INACTIVITY_TIMEOUT.
+ *	The channel to use can be set on the interface or be given using the
+ *	%NL80211_ATTR_WIPHY_FREQ and %NL80211_ATTR_WIPHY_CHANNEL_TYPE attrs.
+ * @NL80211_CMD_NEW_BEACON: old alias for %NL80211_CMD_START_AP
+ * @NL80211_CMD_STOP_AP: Stop AP operation on the given interface
+ * @NL80211_CMD_DEL_BEACON: old alias for %NL80211_CMD_STOP_AP
  *
  * @NL80211_CMD_GET_STATION: Get station attributes for station identified by
  *	%NL80211_ATTR_MAC on the interface identified by %NL80211_ATTR_IFINDEX.
@@ -273,6 +277,12 @@
  * @NL80211_CMD_NEW_SURVEY_RESULTS: survey data notification (as a reply to
  *	NL80211_CMD_GET_SURVEY and on the "scan" multicast group)
  *
+ * @NL80211_CMD_SET_PMKSA: Add a PMKSA cache entry, using %NL80211_ATTR_MAC
+ *	(for the BSSID) and %NL80211_ATTR_PMKID.
+ * @NL80211_CMD_DEL_PMKSA: Delete a PMKSA cache entry, using %NL80211_ATTR_MAC
+ *	(for the BSSID) and %NL80211_ATTR_PMKID.
+ * @NL80211_CMD_FLUSH_PMKSA: Flush all PMKSA cache entries.
+ *
  * @NL80211_CMD_REG_CHANGE: indicates to userspace the regulatory domain
  * 	has been changed and provides details of the request information
  * 	that caused the change such as who initiated the regulatory request
@@ -367,6 +377,11 @@
  *	%NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT,
  *	%NL80211_ATTR_CONTROL_PORT_ETHERTYPE and
  *	%NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT.
+ *	Background scan period can optionally be
+ *	specified in %NL80211_ATTR_BG_SCAN_PERIOD,
+ *	if not specified default background scan configuration
+ *	in driver is used and if period value is 0, bg scan will be disabled.
+ *	This attribute is ignored if driver does not support roam scan.
  *	It is also sent as an event, with the BSSID and response IEs when the
  *	connection is established or failed to be established. This can be
  *	determined by the STATUS_CODE attribute.
@@ -447,6 +462,10 @@
  *	the frame.
  * @NL80211_CMD_ACTION_TX_STATUS: Alias for @NL80211_CMD_FRAME_TX_STATUS for
  *	backward compatibility.
+ *
+ * @NL80211_CMD_SET_POWER_SAVE: Set powersave, using %NL80211_ATTR_PS_STATE
+ * @NL80211_CMD_GET_POWER_SAVE: Get powersave status in %NL80211_ATTR_PS_STATE
+ *
  * @NL80211_CMD_SET_CQM: Connection quality monitor configuration. This command
  *	is used to configure connection quality monitoring notification trigger
  *	levels.
@@ -541,6 +560,11 @@
  * @NL80211_CMD_SET_NOACK_MAP: sets a bitmap for the individual TIDs whether
  *      No Acknowledgement Policy should be applied.
  *
+ * @NL80211_CMD_CH_SWITCH_NOTIFY: An AP or GO may decide to switch channels
+ *	independently of the userspace SME, send this event indicating
+ *	%NL80211_ATTR_IFINDEX is now on %NL80211_ATTR_WIPHY_FREQ with
+ *	%NL80211_ATTR_WIPHY_CHANNEL_TYPE.
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -565,8 +589,10 @@
 
 	NL80211_CMD_GET_BEACON,
 	NL80211_CMD_SET_BEACON,
-	NL80211_CMD_NEW_BEACON,
-	NL80211_CMD_DEL_BEACON,
+	NL80211_CMD_START_AP,
+	NL80211_CMD_NEW_BEACON = NL80211_CMD_START_AP,
+	NL80211_CMD_STOP_AP,
+	NL80211_CMD_DEL_BEACON = NL80211_CMD_STOP_AP,
 
 	NL80211_CMD_GET_STATION,
 	NL80211_CMD_SET_STATION,
@@ -680,6 +706,8 @@
 
 	NL80211_CMD_SET_NOACK_MAP,
 
+	NL80211_CMD_CH_SWITCH_NOTIFY,
+
 	/* add new commands above here */
 
 	/* used to define NL80211_CMD_MAX below */
@@ -753,6 +781,13 @@
  *	section 7.3.2.25.1, e.g. 0x000FAC04)
  * @NL80211_ATTR_KEY_SEQ: transmit key sequence number (IV/PN) for TKIP and
  *	CCMP keys, each six bytes in little endian
+ * @NL80211_ATTR_KEY_DEFAULT: Flag attribute indicating the key is default key
+ * @NL80211_ATTR_KEY_DEFAULT_MGMT: Flag attribute indicating the key is the
+ *	default management key
+ * @NL80211_ATTR_CIPHER_SUITES_PAIRWISE: For crypto settings for connect or
+ *	other commands, indicates which pairwise cipher suites are used
+ * @NL80211_ATTR_CIPHER_SUITE_GROUP: For crypto settings for connect or
+ *	other commands, indicates which group cipher suite is used
  *
  * @NL80211_ATTR_BEACON_INTERVAL: beacon interval in TU
  * @NL80211_ATTR_DTIM_PERIOD: DTIM period for beaconing
@@ -988,6 +1023,8 @@
  * @NL80211_ATTR_ACK: Flag attribute indicating that the frame was
  *	acknowledged by the recipient.
  *
+ * @NL80211_ATTR_PS_STATE: powersave state, using &enum nl80211_ps_state values.
+ *
  * @NL80211_ATTR_CQM: connection quality monitor configuration in a
  *	nested attribute with %NL80211_ATTR_CQM_* sub-attributes.
  *
@@ -1045,7 +1082,7 @@
  *	flag isn't set, the frame will be rejected. This is also used as an
  *	nl80211 capability flag.
  *
- * @NL80211_ATTR_BSS_HTOPMODE: HT operation mode (u16)
+ * @NL80211_ATTR_BSS_HT_OPMODE: HT operation mode (u16)
  *
  * @NL80211_ATTR_KEY_DEFAULT_TYPES: A nested attribute containing flags
  *	attributes, specifying what a key should be set as default as.
@@ -1069,10 +1106,10 @@
  *	indicate which WoW triggers should be enabled. This is also
  *	used by %NL80211_CMD_GET_WOWLAN to get the currently enabled WoWLAN
  *	triggers.
-
+ *
  * @NL80211_ATTR_SCHED_SCAN_INTERVAL: Interval between scheduled scan
  *	cycles, in msecs.
-
+ *
  * @NL80211_ATTR_SCHED_SCAN_MATCH: Nested attribute with one or more
  *	sets of attributes to match during scheduled scans.  Only BSSs
  *	that match any of the sets will be reported.  These are
@@ -1099,7 +1136,7 @@
  *	are managed in software: interfaces of these types aren't subject to
  *	any restrictions in their number or combinations.
  *
- * @%NL80211_ATTR_REKEY_DATA: nested attribute containing the information
+ * @NL80211_ATTR_REKEY_DATA: nested attribute containing the information
  *	necessary for GTK rekeying in the device, see &enum nl80211_rekey_data.
  *
  * @NL80211_ATTR_SCAN_SUPP_RATES: rates per to be advertised as supported in scan,
@@ -1166,7 +1203,6 @@
  * @NL80211_ATTR_FEATURE_FLAGS: This u32 attribute contains flags from
  *	&enum nl80211_feature_flags and is advertised in wiphy information.
  * @NL80211_ATTR_PROBE_RESP_OFFLOAD: Indicates that the HW responds to probe
- *
  *	requests while operating in AP-mode.
  *	This attribute holds a bitmap of the supported protocols for
  *	offloading (see &enum nl80211_probe_resp_offload_support_attr).
@@ -1193,6 +1229,19 @@
  * @NL80211_ATTR_NOACK_MAP: This u16 bitmap contains the No Ack Policy of
  *      up to 16 TIDs.
  *
+ * @NL80211_ATTR_INACTIVITY_TIMEOUT: timeout value in seconds, this can be
+ *	used by the drivers which has MLME in firmware and does not have support
+ *	to report per station tx/rx activity to free up the staion entry from
+ *	the list. This needs to be used when the driver advertises the
+ *	capability to timeout the stations.
+ *
+ * @NL80211_ATTR_RX_SIGNAL_DBM: signal strength in dBm (as a 32-bit int);
+ *	this attribute is (depending on the driver capabilities) added to
+ *	received frames indicated with %NL80211_CMD_FRAME.
+ *
+ * @NL80211_ATTR_BG_SCAN_PERIOD: Background scan period in seconds
+ *      or 0 to disable background scan.
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -1438,6 +1487,12 @@
 
 	NL80211_ATTR_NOACK_MAP,
 
+	NL80211_ATTR_INACTIVITY_TIMEOUT,
+
+	NL80211_ATTR_RX_SIGNAL_DBM,
+
+	NL80211_ATTR_BG_SCAN_PERIOD,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
@@ -1475,6 +1530,7 @@
 #define NL80211_ATTR_FEATURE_FLAGS NL80211_ATTR_FEATURE_FLAGS
 
 #define NL80211_MAX_SUPP_RATES			32
+#define NL80211_MAX_SUPP_HT_RATES		77
 #define NL80211_MAX_SUPP_REG_RULES		32
 #define NL80211_TKIP_DATA_OFFSET_ENCR_KEY	0
 #define NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY	16
@@ -1484,6 +1540,11 @@
 #define NL80211_MAX_NR_CIPHER_SUITES		5
 #define NL80211_MAX_NR_AKM_SUITES		2
 
+#define NL80211_MIN_REMAIN_ON_CHANNEL_TIME	10
+
+/* default RSSI threshold for scan results if none specified. */
+#define NL80211_SCAN_RSSI_THOLD_OFF		-300
+
 /**
  * enum nl80211_iftype - (virtual) interface types
  *
@@ -1558,6 +1619,8 @@
 	NL80211_STA_FLAG_MAX = __NL80211_STA_FLAG_AFTER_LAST - 1
 };
 
+#define NL80211_STA_FLAG_MAX_OLD_API	NL80211_STA_FLAG_TDLS_PEER
+
 /**
  * struct nl80211_sta_flag_update - station flags mask/set
  * @mask: mask of station flags to set
@@ -1656,6 +1719,7 @@
  * @NL80211_STA_INFO_CONNECTED_TIME: time since the station is last connected
  * @NL80211_STA_INFO_STA_FLAGS: Contains a struct nl80211_sta_flag_update.
  * @NL80211_STA_INFO_BEACON_LOSS: count of times beacon loss was detected (u32)
+ * @NL80211_STA_INFO_T_OFFSET: timing offset with respect to this STA (s64)
  * @__NL80211_STA_INFO_AFTER_LAST: internal
  * @NL80211_STA_INFO_MAX: highest possible station info attribute
  */
@@ -1679,6 +1743,7 @@
 	NL80211_STA_INFO_CONNECTED_TIME,
 	NL80211_STA_INFO_STA_FLAGS,
 	NL80211_STA_INFO_BEACON_LOSS,
+	NL80211_STA_INFO_T_OFFSET,
 
 	/* keep last */
 	__NL80211_STA_INFO_AFTER_LAST,
@@ -1748,6 +1813,9 @@
  * @NL80211_BAND_ATTR_HT_CAPA: HT capabilities, as in the HT information IE
  * @NL80211_BAND_ATTR_HT_AMPDU_FACTOR: A-MPDU factor, as in 11n
  * @NL80211_BAND_ATTR_HT_AMPDU_DENSITY: A-MPDU density, as in 11n
+ * @NL80211_BAND_ATTR_VHT_MCS_SET: 32-byte attribute containing the MCS set as
+ *	defined in 802.11ac
+ * @NL80211_BAND_ATTR_VHT_CAPA: VHT capabilities, as in the HT information IE
  * @NL80211_BAND_ATTR_MAX: highest band attribute currently defined
  * @__NL80211_BAND_ATTR_AFTER_LAST: internal use
  */
@@ -1761,6 +1829,9 @@
 	NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
 	NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
 
+	NL80211_BAND_ATTR_VHT_MCS_SET,
+	NL80211_BAND_ATTR_VHT_CAPA,
+
 	/* keep last */
 	__NL80211_BAND_ATTR_AFTER_LAST,
 	NL80211_BAND_ATTR_MAX = __NL80211_BAND_ATTR_AFTER_LAST - 1
@@ -1912,6 +1983,8 @@
  * @__NL80211_SCHED_SCAN_MATCH_ATTR_INVALID: attribute number 0 is reserved
  * @NL80211_SCHED_SCAN_MATCH_ATTR_SSID: SSID to be used for matching,
  * only report BSS with matching SSID.
+ * @NL80211_SCHED_SCAN_MATCH_ATTR_RSSI: RSSI threshold (in dBm) for reporting a
+ *	BSS in scan results. Filtering is turned off if not specified.
  * @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter
  *	attribute number currently defined
  * @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use
@@ -1919,7 +1992,8 @@
 enum nl80211_sched_scan_match_attr {
 	__NL80211_SCHED_SCAN_MATCH_ATTR_INVALID,
 
-	NL80211_ATTR_SCHED_SCAN_MATCH_SSID,
+	NL80211_SCHED_SCAN_MATCH_ATTR_SSID,
+	NL80211_SCHED_SCAN_MATCH_ATTR_RSSI,
 
 	/* keep last */
 	__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST,
@@ -1927,6 +2001,9 @@
 		__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST - 1
 };
 
+/* only for backward compatibility */
+#define NL80211_ATTR_SCHED_SCAN_MATCH_SSID NL80211_SCHED_SCAN_MATCH_ATTR_SSID
+
 /**
  * enum nl80211_reg_rule_flags - regulatory rule flags
  *
@@ -1956,9 +2033,9 @@
  * enum nl80211_dfs_regions - regulatory DFS regions
  *
  * @NL80211_DFS_UNSET: Country has no DFS master region specified
- * @NL80211_DFS_FCC_: Country follows DFS master rules from FCC
- * @NL80211_DFS_FCC_: Country follows DFS master rules from ETSI
- * @NL80211_DFS_JP_: Country follows DFS master rules from JP/MKK/Telec
+ * @NL80211_DFS_FCC: Country follows DFS master rules from FCC
+ * @NL80211_DFS_ETSI: Country follows DFS master rules from ETSI
+ * @NL80211_DFS_JP: Country follows DFS master rules from JP/MKK/Telec
  */
 enum nl80211_dfs_regions {
 	NL80211_DFS_UNSET	= 0,
@@ -2046,69 +2123,91 @@
  * @__NL80211_MESHCONF_INVALID: internal use
  *
  * @NL80211_MESHCONF_RETRY_TIMEOUT: specifies the initial retry timeout in
- * millisecond units, used by the Peer Link Open message
+ *	millisecond units, used by the Peer Link Open message
  *
  * @NL80211_MESHCONF_CONFIRM_TIMEOUT: specifies the initial confirm timeout, in
- * millisecond units, used by the peer link management to close a peer link
+ *	millisecond units, used by the peer link management to close a peer link
  *
  * @NL80211_MESHCONF_HOLDING_TIMEOUT: specifies the holding timeout, in
- * millisecond units
+ *	millisecond units
  *
  * @NL80211_MESHCONF_MAX_PEER_LINKS: maximum number of peer links allowed
- * on this mesh interface
+ *	on this mesh interface
  *
  * @NL80211_MESHCONF_MAX_RETRIES: specifies the maximum number of peer link
- * open retries that can be sent to establish a new peer link instance in a
- * mesh
+ *	open retries that can be sent to establish a new peer link instance in a
+ *	mesh
  *
  * @NL80211_MESHCONF_TTL: specifies the value of TTL field set at a source mesh
- * point.
+ *	point.
  *
  * @NL80211_MESHCONF_AUTO_OPEN_PLINKS: whether we should automatically
- * open peer links when we detect compatible mesh peers.
+ *	open peer links when we detect compatible mesh peers.
  *
  * @NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES: the number of action frames
- * containing a PREQ that an MP can send to a particular destination (path
- * target)
+ *	containing a PREQ that an MP can send to a particular destination (path
+ *	target)
  *
  * @NL80211_MESHCONF_PATH_REFRESH_TIME: how frequently to refresh mesh paths
- * (in milliseconds)
+ *	(in milliseconds)
  *
  * @NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT: minimum length of time to wait
- * until giving up on a path discovery (in milliseconds)
+ *	until giving up on a path discovery (in milliseconds)
  *
  * @NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT: The time (in TUs) for which mesh
- * points receiving a PREQ shall consider the forwarding information from the
- * root to be valid. (TU = time unit)
+ *	points receiving a PREQ shall consider the forwarding information from
+ *	the root to be valid. (TU = time unit)
  *
  * @NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL: The minimum interval of time (in
- * TUs) during which an MP can send only one action frame containing a PREQ
- * reference element
+ *	TUs) during which an MP can send only one action frame containing a PREQ
+ *	reference element
  *
  * @NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME: The interval of time (in TUs)
- * that it takes for an HWMP information element to propagate across the mesh
+ *	that it takes for an HWMP information element to propagate across the
+ *	mesh
  *
  * @NL80211_MESHCONF_HWMP_ROOTMODE: whether root mode is enabled or not
  *
  * @NL80211_MESHCONF_ELEMENT_TTL: specifies the value of TTL field set at a
- * source mesh point for path selection elements.
+ *	source mesh point for path selection elements.
  *
  * @NL80211_MESHCONF_HWMP_RANN_INTERVAL:  The interval of time (in TUs) between
- * root announcements are transmitted.
+ *	root announcements are transmitted.
  *
  * @NL80211_MESHCONF_GATE_ANNOUNCEMENTS: Advertise that this mesh station has
- * access to a broader network beyond the MBSS.  This is done via Root
- * Announcement frames.
+ *	access to a broader network beyond the MBSS.  This is done via Root
+ *	Announcement frames.
  *
  * @NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL: The minimum interval of time (in
- * TUs) during which a mesh STA can send only one Action frame containing a
- * PERR element.
+ *	TUs) during which a mesh STA can send only one Action frame containing a
+ *	PERR element.
  *
  * @NL80211_MESHCONF_FORWARDING: set Mesh STA as forwarding or non-forwarding
- * or forwarding entity (default is TRUE - forwarding entity)
+ *	or forwarding entity (default is TRUE - forwarding entity)
+ *
+ * @NL80211_MESHCONF_RSSI_THRESHOLD: RSSI threshold in dBm. This specifies the
+ *	threshold for average signal strength of candidate station to establish
+ *	a peer link.
+ *
+ * @NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR: maximum number of neighbors
+ *	to synchronize to for 11s default synchronization method
+ *	(see 11C.12.2.2)
+ *
+ * @NL80211_MESHCONF_HT_OPMODE: set mesh HT protection mode.
  *
  * @NL80211_MESHCONF_ATTR_MAX: highest possible mesh configuration attribute
  *
+ * @NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT: The time (in TUs) for
+ *	which mesh STAs receiving a proactive PREQ shall consider the forwarding
+ *	information to the root mesh STA to be valid.
+ *
+ * @NL80211_MESHCONF_HWMP_ROOT_INTERVAL: The interval of time (in TUs) between
+ *	proactive PREQs are transmitted.
+ *
+ * @NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL: The minimum interval of time
+ *	(in TUs) during which a mesh STA can send only one Action frame
+ *	containing a PREQ element for root path confirmation.
+ *
  * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use
  */
 enum nl80211_meshconf_params {
@@ -2132,6 +2231,12 @@
 	NL80211_MESHCONF_GATE_ANNOUNCEMENTS,
 	NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL,
 	NL80211_MESHCONF_FORWARDING,
+	NL80211_MESHCONF_RSSI_THRESHOLD,
+	NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR,
+	NL80211_MESHCONF_HT_OPMODE,
+	NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT,
+	NL80211_MESHCONF_HWMP_ROOT_INTERVAL,
+	NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL,
 
 	/* keep last */
 	__NL80211_MESHCONF_ATTR_AFTER_LAST,
@@ -2147,30 +2252,37 @@
  * @__NL80211_MESH_SETUP_INVALID: Internal use
  *
  * @NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL: Enable this option to use a
- * vendor specific path selection algorithm or disable it to use the default
- * HWMP.
+ *	vendor specific path selection algorithm or disable it to use the
+ *	default HWMP.
  *
  * @NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC: Enable this option to use a
- * vendor specific path metric or disable it to use the default Airtime
- * metric.
+ *	vendor specific path metric or disable it to use the default Airtime
+ *	metric.
  *
  * @NL80211_MESH_SETUP_IE: Information elements for this mesh, for instance, a
- * robust security network ie, or a vendor specific information element that
- * vendors will use to identify the path selection methods and metrics in use.
+ *	robust security network ie, or a vendor specific information element
+ *	that vendors will use to identify the path selection methods and
+ *	metrics in use.
  *
  * @NL80211_MESH_SETUP_USERSPACE_AUTH: Enable this option if an authentication
- * daemon will be authenticating mesh candidates.
+ *	daemon will be authenticating mesh candidates.
  *
  * @NL80211_MESH_SETUP_USERSPACE_AMPE: Enable this option if an authentication
- * daemon will be securing peer link frames.  AMPE is a secured version of Mesh
- * Peering Management (MPM) and is implemented with the assistance of a
- * userspace daemon.  When this flag is set, the kernel will send peer
- * management frames to a userspace daemon that will implement AMPE
- * functionality (security capabilities selection, key confirmation, and key
- * management).  When the flag is unset (default), the kernel can autonomously
- * complete (unsecured) mesh peering without the need of a userspace daemon.
+ *	daemon will be securing peer link frames.  AMPE is a secured version of
+ *	Mesh Peering Management (MPM) and is implemented with the assistance of
+ *	a userspace daemon.  When this flag is set, the kernel will send peer
+ *	management frames to a userspace daemon that will implement AMPE
+ *	functionality (security capabilities selection, key confirmation, and
+ *	key management).  When the flag is unset (default), the kernel can
+ *	autonomously complete (unsecured) mesh peering without the need of a
+ *	userspace daemon.
+ *
+ * @NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC: Enable this option to use a
+ *	vendor specific synchronization method or disable it to use the default
+ *	neighbor offset synchronization
  *
  * @NL80211_MESH_SETUP_ATTR_MAX: highest possible mesh setup attribute number
+ *
  * @__NL80211_MESH_SETUP_ATTR_AFTER_LAST: Internal use
  */
 enum nl80211_mesh_setup_params {
@@ -2180,6 +2292,7 @@
 	NL80211_MESH_SETUP_IE,
 	NL80211_MESH_SETUP_USERSPACE_AUTH,
 	NL80211_MESH_SETUP_USERSPACE_AMPE,
+	NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC,
 
 	/* keep last */
 	__NL80211_MESH_SETUP_ATTR_AFTER_LAST,
@@ -2189,7 +2302,7 @@
 /**
  * enum nl80211_txq_attr - TX queue parameter attributes
  * @__NL80211_TXQ_ATTR_INVALID: Attribute number 0 is reserved
- * @NL80211_TXQ_ATTR_QUEUE: TX queue identifier (NL80211_TXQ_Q_*)
+ * @NL80211_TXQ_ATTR_AC: AC identifier (NL80211_AC_*)
  * @NL80211_TXQ_ATTR_TXOP: Maximum burst time in units of 32 usecs, 0 meaning
  *	disabled
  * @NL80211_TXQ_ATTR_CWMIN: Minimum contention window [a value of the form
@@ -2202,7 +2315,7 @@
  */
 enum nl80211_txq_attr {
 	__NL80211_TXQ_ATTR_INVALID,
-	NL80211_TXQ_ATTR_QUEUE,
+	NL80211_TXQ_ATTR_AC,
 	NL80211_TXQ_ATTR_TXOP,
 	NL80211_TXQ_ATTR_CWMIN,
 	NL80211_TXQ_ATTR_CWMAX,
@@ -2213,13 +2326,21 @@
 	NL80211_TXQ_ATTR_MAX = __NL80211_TXQ_ATTR_AFTER_LAST - 1
 };
 
-enum nl80211_txq_q {
-	NL80211_TXQ_Q_VO,
-	NL80211_TXQ_Q_VI,
-	NL80211_TXQ_Q_BE,
-	NL80211_TXQ_Q_BK
+enum nl80211_ac {
+	NL80211_AC_VO,
+	NL80211_AC_VI,
+	NL80211_AC_BE,
+	NL80211_AC_BK,
+	NL80211_NUM_ACS
 };
 
+/* backward compat */
+#define NL80211_TXQ_ATTR_QUEUE	NL80211_TXQ_ATTR_AC
+#define NL80211_TXQ_Q_VO	NL80211_AC_VO
+#define NL80211_TXQ_Q_VI	NL80211_AC_VI
+#define NL80211_TXQ_Q_BE	NL80211_AC_BE
+#define NL80211_TXQ_Q_BK	NL80211_AC_BK
+
 enum nl80211_channel_type {
 	NL80211_CHAN_NO_HT,
 	NL80211_CHAN_HT20,
@@ -2405,12 +2526,15 @@
  *	in an array of rates as defined in IEEE 802.11 7.3.2.2 (u8 values with
  *	1 = 500 kbps) but without the IE length restriction (at most
  *	%NL80211_MAX_SUPP_RATES in a single array).
+ * @NL80211_TXRATE_MCS: HT (MCS) rates allowed for TX rate selection
+ *	in an array of MCS numbers.
  * @__NL80211_TXRATE_AFTER_LAST: internal
  * @NL80211_TXRATE_MAX: highest TX rate attribute
  */
 enum nl80211_tx_rate_attributes {
 	__NL80211_TXRATE_INVALID,
 	NL80211_TXRATE_LEGACY,
+	NL80211_TXRATE_MCS,
 
 	/* keep last */
 	__NL80211_TXRATE_AFTER_LAST,
@@ -2421,12 +2545,19 @@
  * enum nl80211_band - Frequency band
  * @NL80211_BAND_2GHZ: 2.4 GHz ISM band
  * @NL80211_BAND_5GHZ: around 5 GHz band (4.9 - 5.7 GHz)
+ * @NL80211_BAND_60GHZ: around 60 GHz band (58.32 - 64.80 GHz)
  */
 enum nl80211_band {
 	NL80211_BAND_2GHZ,
 	NL80211_BAND_5GHZ,
+	NL80211_BAND_60GHZ,
 };
 
+/**
+ * enum nl80211_ps_state - powersave state
+ * @NL80211_PS_DISABLED: powersave is disabled
+ * @NL80211_PS_ENABLED: powersave is enabled
+ */
 enum nl80211_ps_state {
 	NL80211_PS_DISABLED,
 	NL80211_PS_ENABLED,
@@ -2465,10 +2596,14 @@
  *      configured threshold
  * @NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH: The RSSI is higher than the
  *      configured threshold
+ * @NL80211_CQM_RSSI_BEACON_LOSS_EVENT: The device experienced beacon loss.
+ *	(Note that deauth/disassoc will still follow if the AP is not
+ *	available. This event might get used as roaming event, etc.)
  */
 enum nl80211_cqm_rssi_threshold_event {
 	NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
 	NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
+	NL80211_CQM_RSSI_BEACON_LOSS_EVENT,
 };
 
 
@@ -2796,10 +2931,13 @@
  *	TX status to the socket error queue when requested with the
  *	socket option.
  * @NL80211_FEATURE_HT_IBSS: This driver supports IBSS with HT datarates.
+ * @NL80211_FEATURE_INACTIVITY_TIMER: This driver takes care of freeing up
+ *	the connected inactive stations in AP mode.
  */
 enum nl80211_feature_flags {
 	NL80211_FEATURE_SK_TX_STATUS	= 1 << 0,
 	NL80211_FEATURE_HT_IBSS		= 1 << 1,
+	NL80211_FEATURE_INACTIVITY_TIMER = 1 << 2,
 };
 
 /**
diff --git a/src/eap_common/eap_defs.h b/src/eap_common/eap_defs.h
index af0a29a..360193a 100644
--- a/src/eap_common/eap_defs.h
+++ b/src/eap_common/eap_defs.h
@@ -60,7 +60,7 @@
 	EAP_TYPE_PSK = 47 /* RFC 4764 */,
 	EAP_TYPE_SAKE = 48 /* RFC 4763 */,
 	EAP_TYPE_IKEV2 = 49 /* RFC 5106 */,
-	EAP_TYPE_AKA_PRIME = 50 /* draft-arkko-eap-aka-kdf-10.txt */,
+	EAP_TYPE_AKA_PRIME = 50 /* RFC 5448 */,
 	EAP_TYPE_GPSK = 51 /* RFC 5433 */,
 	EAP_TYPE_PWD = 52 /* RFC 5931 */,
 	EAP_TYPE_EXPANDED = 254 /* RFC 3748 */
diff --git a/src/eap_common/eap_pwd_common.c b/src/eap_common/eap_pwd_common.c
index 5b6bf3c..7d6e6b8 100644
--- a/src/eap_common/eap_pwd_common.c
+++ b/src/eap_common/eap_pwd_common.c
@@ -8,70 +8,76 @@
 
 #include "includes.h"
 #include "common.h"
+#include "crypto/sha256.h"
+#include "crypto/crypto.h"
 #include "eap_defs.h"
 #include "eap_pwd_common.h"
 
 /* The random function H(x) = HMAC-SHA256(0^32, x) */
-void H_Init(HMAC_CTX *ctx)
+struct crypto_hash * eap_pwd_h_init(void)
 {
-	u8 allzero[SHA256_DIGEST_LENGTH];
-
-	os_memset(allzero, 0, SHA256_DIGEST_LENGTH);
-	HMAC_Init(ctx, allzero, SHA256_DIGEST_LENGTH, EVP_sha256());
+	u8 allzero[SHA256_MAC_LEN];
+	os_memset(allzero, 0, SHA256_MAC_LEN);
+	return crypto_hash_init(CRYPTO_HASH_ALG_HMAC_SHA256, allzero,
+				SHA256_MAC_LEN);
 }
 
 
-void H_Update(HMAC_CTX *ctx, const u8 *data, int len)
+void eap_pwd_h_update(struct crypto_hash *hash, const u8 *data, size_t len)
 {
-	HMAC_Update(ctx, data, len);
+	crypto_hash_update(hash, data, len);
 }
 
 
-void H_Final(HMAC_CTX *ctx, u8 *digest)
+void eap_pwd_h_final(struct crypto_hash *hash, u8 *digest)
 {
-	unsigned int mdlen = SHA256_DIGEST_LENGTH;
-
-	HMAC_Final(ctx, digest, &mdlen);
-	HMAC_CTX_cleanup(ctx);
+	size_t len = SHA256_MAC_LEN;
+	crypto_hash_finish(hash, digest, &len);
 }
 
 
 /* a counter-based KDF based on NIST SP800-108 */
-void eap_pwd_kdf(u8 *key, int keylen, u8 *label, int labellen,
-		 u8 *result, int resultbitlen)
+static int eap_pwd_kdf(const u8 *key, size_t keylen, const u8 *label,
+		       size_t labellen, u8 *result, size_t resultbitlen)
 {
-	HMAC_CTX hctx;
-	unsigned char digest[SHA256_DIGEST_LENGTH];
+	struct crypto_hash *hash;
+	u8 digest[SHA256_MAC_LEN];
 	u16 i, ctr, L;
-	int resultbytelen, len = 0;
-	unsigned int mdlen = SHA256_DIGEST_LENGTH;
-	unsigned char mask = 0xff;
+	size_t resultbytelen, len = 0, mdlen;
 
-	resultbytelen = (resultbitlen + 7)/8;
+	resultbytelen = (resultbitlen + 7) / 8;
 	ctr = 0;
 	L = htons(resultbitlen);
 	while (len < resultbytelen) {
-		ctr++; i = htons(ctr);
-		HMAC_Init(&hctx, key, keylen, EVP_sha256());
+		ctr++;
+		i = htons(ctr);
+		hash = crypto_hash_init(CRYPTO_HASH_ALG_HMAC_SHA256,
+					key, keylen);
+		if (hash == NULL)
+			return -1;
 		if (ctr > 1)
-			HMAC_Update(&hctx, digest, mdlen);
-		HMAC_Update(&hctx, (u8 *) &i, sizeof(u16));
-		HMAC_Update(&hctx, label, labellen);
-		HMAC_Update(&hctx, (u8 *) &L, sizeof(u16));
-		HMAC_Final(&hctx, digest, &mdlen);
-		if ((len + (int) mdlen) > resultbytelen)
+			crypto_hash_update(hash, digest, SHA256_MAC_LEN);
+		crypto_hash_update(hash, (u8 *) &i, sizeof(u16));
+		crypto_hash_update(hash, label, labellen);
+		crypto_hash_update(hash, (u8 *) &L, sizeof(u16));
+		mdlen = SHA256_MAC_LEN;
+		if (crypto_hash_finish(hash, digest, &mdlen) < 0)
+			return -1;
+		if ((len + mdlen) > resultbytelen)
 			os_memcpy(result + len, digest, resultbytelen - len);
 		else
 			os_memcpy(result + len, digest, mdlen);
 		len += mdlen;
-		HMAC_CTX_cleanup(&hctx);
 	}
 
 	/* since we're expanding to a bit length, mask off the excess */
 	if (resultbitlen % 8) {
+		u8 mask = 0xff;
 		mask <<= (8 - (resultbitlen % 8));
 		result[resultbytelen - 1] &= mask;
 	}
+
+	return 0;
 }
 
 
@@ -85,9 +91,10 @@
 			     u8 *id_peer, int id_peer_len, u8 *token)
 {
 	BIGNUM *x_candidate = NULL, *rnd = NULL, *cofactor = NULL;
-	HMAC_CTX ctx;
-	unsigned char pwe_digest[SHA256_DIGEST_LENGTH], *prfbuf = NULL, ctr;
-	int nid, is_odd, primebitlen, primebytelen, ret = 0;
+	struct crypto_hash *hash;
+	unsigned char pwe_digest[SHA256_MAC_LEN], *prfbuf = NULL, ctr;
+	int nid, is_odd, ret = 0;
+	size_t primebytelen, primebitlen;
 
 	switch (num) { /* from IANA registry for IKE D-H groups */
         case 19:
@@ -154,7 +161,7 @@
 	os_memset(prfbuf, 0, primebytelen);
 	ctr = 0;
 	while (1) {
-		if (ctr > 10) {
+		if (ctr > 30) {
 			wpa_printf(MSG_INFO, "EAP-pwd: unable to find random "
 				   "point on curve for group %d, something's "
 				   "fishy", num);
@@ -167,20 +174,23 @@
 		 *    pwd-seed = H(token | peer-id | server-id | password |
 		 *		   counter)
 		 */
-		H_Init(&ctx);
-		H_Update(&ctx, token, sizeof(u32));
-		H_Update(&ctx, id_peer, id_peer_len);
-		H_Update(&ctx, id_server, id_server_len);
-		H_Update(&ctx, password, password_len);
-		H_Update(&ctx, &ctr, sizeof(ctr));
-		H_Final(&ctx, pwe_digest);
+		hash = eap_pwd_h_init();
+		if (hash == NULL)
+			goto fail;
+		eap_pwd_h_update(hash, token, sizeof(u32));
+		eap_pwd_h_update(hash, id_peer, id_peer_len);
+		eap_pwd_h_update(hash, id_server, id_server_len);
+		eap_pwd_h_update(hash, password, password_len);
+		eap_pwd_h_update(hash, &ctr, sizeof(ctr));
+		eap_pwd_h_final(hash, pwe_digest);
 
-		BN_bin2bn(pwe_digest, SHA256_DIGEST_LENGTH, rnd);
+		BN_bin2bn(pwe_digest, SHA256_MAC_LEN, rnd);
 
-		eap_pwd_kdf(pwe_digest, SHA256_DIGEST_LENGTH,
-			    (unsigned char *) "EAP-pwd Hunting And Pecking",
-			    os_strlen("EAP-pwd Hunting And Pecking"),
-			    prfbuf, primebitlen);
+		if (eap_pwd_kdf(pwe_digest, SHA256_MAC_LEN,
+				(u8 *) "EAP-pwd Hunting And Pecking",
+				os_strlen("EAP-pwd Hunting And Pecking"),
+				prfbuf, primebitlen) < 0)
+			goto fail;
 
 		BN_bin2bn(prfbuf, primebytelen, x_candidate);
 
@@ -252,11 +262,13 @@
 	if (0) {
  fail:
 		EC_GROUP_free(grp->group);
+		grp->group = NULL;
 		EC_POINT_free(grp->pwe);
+		grp->pwe = NULL;
 		BN_free(grp->order);
+		grp->order = NULL;
 		BN_free(grp->prime);
-		os_free(grp);
-		grp = NULL;
+		grp->prime = NULL;
 		ret = 1;
 	}
 	/* cleanliness and order.... */
@@ -274,9 +286,9 @@
 		 u8 *confirm_peer, u8 *confirm_server,
 		 u32 *ciphersuite, u8 *msk, u8 *emsk)
 {
-	HMAC_CTX ctx;
-	u8 mk[SHA256_DIGEST_LENGTH], *cruft;
-	u8 session_id[SHA256_DIGEST_LENGTH + 1];
+	struct crypto_hash *hash;
+	u8 mk[SHA256_MAC_LEN], *cruft;
+	u8 session_id[SHA256_MAC_LEN + 1];
 	u8 msk_emsk[EAP_MSK_LEN + EAP_EMSK_LEN];
 	int offset;
 
@@ -288,37 +300,46 @@
 	 *	scal_s)
 	 */
 	session_id[0] = EAP_TYPE_PWD;
-	H_Init(&ctx);
-	H_Update(&ctx, (u8 *)ciphersuite, sizeof(u32));
+	hash = eap_pwd_h_init();
+	if (hash == NULL) {
+		os_free(cruft);
+		return -1;
+	}
+	eap_pwd_h_update(hash, (u8 *) ciphersuite, sizeof(u32));
 	offset = BN_num_bytes(grp->order) - BN_num_bytes(peer_scalar);
 	os_memset(cruft, 0, BN_num_bytes(grp->prime));
 	BN_bn2bin(peer_scalar, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(grp->order));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->order));
 	offset = BN_num_bytes(grp->order) - BN_num_bytes(server_scalar);
 	os_memset(cruft, 0, BN_num_bytes(grp->prime));
 	BN_bn2bin(server_scalar, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(grp->order));
-	H_Final(&ctx, &session_id[1]);
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->order));
+	eap_pwd_h_final(hash, &session_id[1]);
 
 	/* then compute MK = H(k | confirm-peer | confirm-server) */
-	H_Init(&ctx);
+	hash = eap_pwd_h_init();
+	if (hash == NULL) {
+		os_free(cruft);
+		return -1;
+	}
 	offset = BN_num_bytes(grp->prime) - BN_num_bytes(k);
 	os_memset(cruft, 0, BN_num_bytes(grp->prime));
 	BN_bn2bin(k, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(grp->prime));
-	H_Update(&ctx, confirm_peer, SHA256_DIGEST_LENGTH);
-	H_Update(&ctx, confirm_server, SHA256_DIGEST_LENGTH);
-	H_Final(&ctx, mk);
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->prime));
+	os_free(cruft);
+	eap_pwd_h_update(hash, confirm_peer, SHA256_MAC_LEN);
+	eap_pwd_h_update(hash, confirm_server, SHA256_MAC_LEN);
+	eap_pwd_h_final(hash, mk);
 
 	/* stretch the mk with the session-id to get MSK | EMSK */
-	eap_pwd_kdf(mk, SHA256_DIGEST_LENGTH,
-		    session_id, SHA256_DIGEST_LENGTH+1,
-		    msk_emsk, (EAP_MSK_LEN + EAP_EMSK_LEN) * 8);
+	if (eap_pwd_kdf(mk, SHA256_MAC_LEN,
+			session_id, SHA256_MAC_LEN + 1,
+			msk_emsk, (EAP_MSK_LEN + EAP_EMSK_LEN) * 8) < 0) {
+		return -1;
+	}
 
 	os_memcpy(msk, msk_emsk, EAP_MSK_LEN);
 	os_memcpy(emsk, msk_emsk + EAP_MSK_LEN, EAP_EMSK_LEN);
 
-	os_free(cruft);
-
 	return 1;
 }
diff --git a/src/eap_common/eap_pwd_common.h b/src/eap_common/eap_pwd_common.h
index 358d008..816e58c 100644
--- a/src/eap_common/eap_pwd_common.h
+++ b/src/eap_common/eap_pwd_common.h
@@ -10,10 +10,8 @@
 #define EAP_PWD_COMMON_H
 
 #include <openssl/bn.h>
-#include <openssl/sha.h>
 #include <openssl/ec.h>
 #include <openssl/evp.h>
-#include <openssl/hmac.h>
 
 /*
  * definition of a finite cyclic group
@@ -62,8 +60,8 @@
 			     int, u8 *);
 int compute_keys(EAP_PWD_group *, BN_CTX *, BIGNUM *, BIGNUM *, BIGNUM *,
 		 u8 *, u8 *, u32 *, u8 *, u8 *);
-void H_Init(HMAC_CTX *);
-void H_Update(HMAC_CTX *, const u8 *, int);
-void H_Final(HMAC_CTX *, u8 *);
+struct crypto_hash * eap_pwd_h_init(void);
+void eap_pwd_h_update(struct crypto_hash *hash, const u8 *data, size_t len);
+void eap_pwd_h_final(struct crypto_hash *hash, u8 *digest);
 
 #endif  /* EAP_PWD_COMMON_H */
diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c
index 6a88589..8b43be4 100644
--- a/src/eap_peer/eap.c
+++ b/src/eap_peer/eap.c
@@ -1,6 +1,6 @@
 /*
  * EAP peer state machines (RFC 4137)
- * Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -81,6 +81,16 @@
 }
 
 
+static void eap_notify_status(struct eap_sm *sm, const char *status,
+				      const char *parameter)
+{
+	wpa_printf(MSG_DEBUG, "EAP: Status notification: %s (param=%s)",
+		   status, parameter);
+	if (sm->eapol_cb->notify_status)
+		sm->eapol_cb->notify_status(sm->eapol_ctx, status, parameter);
+}
+
+
 static void eap_deinit_prev_method(struct eap_sm *sm, const char *txt)
 {
 	if (sm->m == NULL || sm->eap_method_priv == NULL)
@@ -213,6 +223,7 @@
 {
 	int reinit;
 	EapType method;
+	const struct eap_method *eap_method;
 
 	SM_ENTRY(EAP, GET_METHOD);
 
@@ -221,18 +232,24 @@
 	else
 		method = sm->reqMethod;
 
+	eap_method = eap_peer_get_eap_method(sm->reqVendor, method);
+
 	if (!eap_sm_allowMethod(sm, sm->reqVendor, method)) {
 		wpa_printf(MSG_DEBUG, "EAP: vendor %u method %u not allowed",
 			   sm->reqVendor, method);
 		wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD
 			"vendor=%u method=%u -> NAK",
 			sm->reqVendor, method);
+		eap_notify_status(sm, "refuse proposed method",
+				  eap_method ?  eap_method->name : "unknown");
 		goto nak;
 	}
 
 	wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD
 		"vendor=%u method=%u", sm->reqVendor, method);
 
+	eap_notify_status(sm, "accept proposed method",
+			  eap_method ?  eap_method->name : "unknown");
 	/*
 	 * RFC 4137 does not define specific operation for fast
 	 * re-authentication (session resumption). The design here is to allow
@@ -256,7 +273,7 @@
 
 	sm->selectedMethod = sm->reqMethod;
 	if (sm->m == NULL)
-		sm->m = eap_peer_get_eap_method(sm->reqVendor, method);
+		sm->m = eap_method;
 	if (!sm->m) {
 		wpa_printf(MSG_DEBUG, "EAP: Could not find selected method: "
 			   "vendor %d method %d",
@@ -938,7 +955,7 @@
 static int eap_sm_imsi_identity(struct eap_sm *sm,
 				struct eap_peer_config *conf)
 {
-	int aka = 0;
+	enum { EAP_SM_SIM, EAP_SM_AKA, EAP_SM_AKA_PRIME } method = EAP_SM_SIM;
 	char imsi[100];
 	size_t imsi_len;
 	struct eap_method_type *m = conf->eap_methods;
@@ -966,8 +983,14 @@
 	for (i = 0; m && (m[i].vendor != EAP_VENDOR_IETF ||
 			  m[i].method != EAP_TYPE_NONE); i++) {
 		if (m[i].vendor == EAP_VENDOR_IETF &&
+		    m[i].method == EAP_TYPE_AKA_PRIME) {
+			method = EAP_SM_AKA_PRIME;
+			break;
+		}
+
+		if (m[i].vendor == EAP_VENDOR_IETF &&
 		    m[i].method == EAP_TYPE_AKA) {
-			aka = 1;
+			method = EAP_SM_AKA;
 			break;
 		}
 	}
@@ -980,7 +1003,17 @@
 		return -1;
 	}
 
-	conf->identity[0] = aka ? '0' : '1';
+	switch (method) {
+	case EAP_SM_SIM:
+		conf->identity[0] = '1';
+		break;
+	case EAP_SM_AKA:
+		conf->identity[0] = '0';
+		break;
+	case EAP_SM_AKA_PRIME:
+		conf->identity[0] = '6';
+		break;
+	}
 	os_memcpy(conf->identity + 1, imsi, imsi_len);
 	conf->identity_len = 1 + imsi_len;
 
@@ -1219,10 +1252,12 @@
 		break;
 	case EAP_CODE_SUCCESS:
 		wpa_printf(MSG_DEBUG, "EAP: Received EAP-Success");
+		eap_notify_status(sm, "completion", "success");
 		sm->rxSuccess = TRUE;
 		break;
 	case EAP_CODE_FAILURE:
 		wpa_printf(MSG_DEBUG, "EAP: Received EAP-Failure");
+		eap_notify_status(sm, "completion", "failure");
 		sm->rxFailure = TRUE;
 		break;
 	default:
@@ -1240,6 +1275,10 @@
 	char *hash_hex = NULL;
 
 	switch (ev) {
+	case TLS_CERT_CHAIN_SUCCESS:
+		eap_notify_status(sm, "remote certificate verification",
+				  "success");
+		break;
 	case TLS_CERT_CHAIN_FAILURE:
 		wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_TLS_CERT_ERROR
 			"reason=%d depth=%d subject='%s' err='%s'",
@@ -1247,6 +1286,8 @@
 			data->cert_fail.depth,
 			data->cert_fail.subject,
 			data->cert_fail.reason_txt);
+		eap_notify_status(sm, "remote certificate verification",
+				  data->cert_fail.reason_txt);
 		break;
 	case TLS_PEER_CERTIFICATE:
 		if (!sm->eapol_cb->notify_cert)
@@ -1267,6 +1308,14 @@
 					  data->peer_cert.subject,
 					  hash_hex, data->peer_cert.cert);
 		break;
+	case TLS_ALERT:
+		if (data->alert.is_local)
+			eap_notify_status(sm, "local TLS alert",
+					  data->alert.description);
+		else
+			eap_notify_status(sm, "remote TLS alert",
+					  data->alert.description);
+		break;
 	}
 
 	os_free(hash_hex);
@@ -1321,6 +1370,13 @@
 		return NULL;
 	}
 
+	sm->ssl_ctx2 = tls_init(&tlsconf);
+	if (sm->ssl_ctx2 == NULL) {
+		wpa_printf(MSG_INFO, "SSL: Failed to initialize TLS "
+			   "context (2).");
+		/* Run without separate TLS context within TLS tunnel */
+	}
+
 	return sm;
 }
 
@@ -1338,6 +1394,8 @@
 		return;
 	eap_deinit_prev_method(sm, "EAP deinit");
 	eap_sm_abort(sm);
+	if (sm->ssl_ctx2)
+		tls_deinit(sm->ssl_ctx2);
 	tls_deinit(sm->ssl_ctx);
 	os_free(sm);
 }
diff --git a/src/eap_peer/eap.h b/src/eap_peer/eap.h
index b95a285..6e87475 100644
--- a/src/eap_peer/eap.h
+++ b/src/eap_peer/eap.h
@@ -226,6 +226,15 @@
 	 */
 	void (*notify_cert)(void *ctx, int depth, const char *subject,
 			    const char *cert_hash, const struct wpabuf *cert);
+
+	/**
+	 * notify_status - Notification of the current EAP state
+	 * @ctx: eapol_ctx from eap_peer_sm_init() call
+	 * @status: Step in the process of EAP authentication
+	 * @parameter: Step-specific parameter, e.g., EAP method name
+	 */
+	void (*notify_status)(void *ctx, const char *status,
+			      const char *parameter);
 };
 
 /**
diff --git a/src/eap_peer/eap_aka.c b/src/eap_peer/eap_aka.c
index 7e37e44..1cec4d8 100644
--- a/src/eap_peer/eap_aka.c
+++ b/src/eap_peer/eap_aka.c
@@ -1,6 +1,6 @@
 /*
- * EAP peer method: EAP-AKA (RFC 4187) and EAP-AKA' (draft-arkko-eap-aka-kdf)
- * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ * EAP peer method: EAP-AKA (RFC 4187) and EAP-AKA' (RFC 5448)
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -229,19 +229,19 @@
 
 static void eap_aka_clear_identities(struct eap_aka_data *data, int id)
 {
-	if (id & CLEAR_PSEUDONYM) {
+	if ((id & CLEAR_PSEUDONYM) && data->pseudonym) {
 		wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old pseudonym");
 		os_free(data->pseudonym);
 		data->pseudonym = NULL;
 		data->pseudonym_len = 0;
 	}
-	if (id & CLEAR_REAUTH_ID) {
+	if ((id & CLEAR_REAUTH_ID) && data->reauth_id) {
 		wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old reauth_id");
 		os_free(data->reauth_id);
 		data->reauth_id = NULL;
 		data->reauth_id_len = 0;
 	}
-	if (id & CLEAR_EAP_ID) {
+	if ((id & CLEAR_EAP_ID) && data->last_eap_identity) {
 		wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old eap_id");
 		os_free(data->last_eap_identity);
 		data->last_eap_identity = NULL;
diff --git a/src/eap_peer/eap_i.h b/src/eap_peer/eap_i.h
index 06d6db6..3318b81 100644
--- a/src/eap_peer/eap_i.h
+++ b/src/eap_peer/eap_i.h
@@ -317,6 +317,7 @@
 	void *msg_ctx;
 	void *scard_ctx;
 	void *ssl_ctx;
+	void *ssl_ctx2;
 
 	unsigned int workaround;
 
diff --git a/src/eap_peer/eap_pwd.c b/src/eap_peer/eap_pwd.c
index 37e9234..267d0a5 100644
--- a/src/eap_peer/eap_pwd.c
+++ b/src/eap_peer/eap_pwd.c
@@ -9,6 +9,7 @@
 #include "includes.h"
 
 #include "common.h"
+#include "crypto/sha256.h"
 #include "eap_peer/eap_i.h"
 #include "eap_common/eap_pwd_common.h"
 
@@ -459,10 +460,10 @@
 				 const u8 *payload, size_t payload_len)
 {
 	BIGNUM *x = NULL, *y = NULL;
-	HMAC_CTX ctx;
+	struct crypto_hash *hash;
 	u32 cs;
 	u16 grp;
-	u8 conf[SHA256_DIGEST_LENGTH], *cruft = NULL, *ptr;
+	u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr;
 	int offset;
 
 	/*
@@ -489,7 +490,9 @@
 	 * server's commit is H(k | server_element | server_scalar |
 	 *			peer_element | peer_scalar | ciphersuite)
 	 */
-	H_Init(&ctx);
+	hash = eap_pwd_h_init();
+	if (hash == NULL)
+		goto fin;
 
 	/*
 	 * zero the memory each time because this is mod prime math and some
@@ -498,7 +501,7 @@
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k);
 	BN_bn2bin(data->k, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 
 	/* server element: x, y */
 	if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
@@ -511,18 +514,18 @@
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
 	BN_bn2bin(x, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
 	BN_bn2bin(y, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 
 	/* server scalar */
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->order) -
 		BN_num_bytes(data->server_scalar);
 	BN_bn2bin(data->server_scalar, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->order));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
 
 	/* my element: x, y */
 	if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
@@ -536,27 +539,27 @@
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
 	BN_bn2bin(x, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
 	BN_bn2bin(y, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 
 	/* my scalar */
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->order) -
 		BN_num_bytes(data->my_scalar);
 	BN_bn2bin(data->my_scalar, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->order));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
 
 	/* the ciphersuite */
-	H_Update(&ctx, (u8 *) &cs, sizeof(u32));
+	eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32));
 
 	/* random function fin */
-	H_Final(&ctx, conf);
+	eap_pwd_h_final(hash, conf);
 
 	ptr = (u8 *) payload;
-	if (os_memcmp(conf, ptr, SHA256_DIGEST_LENGTH)) {
+	if (os_memcmp(conf, ptr, SHA256_MAC_LEN)) {
 		wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm did not verify");
 		goto fin;
 	}
@@ -568,13 +571,15 @@
 	 *  H(k | peer_element | peer_scalar | server_element | server_scalar |
 	 *    ciphersuite)
 	 */
-	H_Init(&ctx);
+	hash = eap_pwd_h_init();
+	if (hash == NULL)
+		goto fin;
 
 	/* k */
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k);
 	BN_bn2bin(data->k, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 
 	/* my element */
 	if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
@@ -587,18 +592,18 @@
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
 	BN_bn2bin(x, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
 	BN_bn2bin(y, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 
 	/* my scalar */
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->order) -
 		BN_num_bytes(data->my_scalar);
 	BN_bn2bin(data->my_scalar, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->order));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
 
 	/* server element: x, y */
 	if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
@@ -611,24 +616,24 @@
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
 	BN_bn2bin(x, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
 	BN_bn2bin(y, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 
 	/* server scalar */
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->order) -
 		BN_num_bytes(data->server_scalar);
 	BN_bn2bin(data->server_scalar, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->order));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
 
 	/* the ciphersuite */
-	H_Update(&ctx, (u8 *) &cs, sizeof(u32));
+	eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32));
 
 	/* all done */
-	H_Final(&ctx, conf);
+	eap_pwd_h_final(hash, conf);
 
 	if (compute_keys(data->grp, data->bnctx, data->k,
 			 data->my_scalar, data->server_scalar, conf, ptr,
@@ -638,11 +643,11 @@
 		goto fin;
 	}
 
-	data->outbuf = wpabuf_alloc(SHA256_DIGEST_LENGTH);
+	data->outbuf = wpabuf_alloc(SHA256_MAC_LEN);
 	if (data->outbuf == NULL)
 		goto fin;
 
-	wpabuf_put_data(data->outbuf, conf, SHA256_DIGEST_LENGTH);
+	wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN);
 
 fin:
 	os_free(cruft);
@@ -725,6 +730,7 @@
 		 */
 		if (data->out_frag_pos >= wpabuf_len(data->outbuf)) {
 			wpabuf_free(data->outbuf);
+			data->outbuf = NULL;
 			data->out_frag_pos = 0;
 		}
 		wpa_printf(MSG_DEBUG, "EAP-pwd: Send %s fragment of %d bytes",
@@ -856,8 +862,11 @@
 	/*
 	 * if we're not fragmenting then there's no need to carry this around
 	 */
-	if (data->out_frag_pos == 0)
+	if (data->out_frag_pos == 0) {
 		wpabuf_free(data->outbuf);
+		data->outbuf = NULL;
+		data->out_frag_pos = 0;
+	}
 
 	return resp;
 }
diff --git a/src/eap_peer/eap_tls.c b/src/eap_peer/eap_tls.c
index a3067fa..ed52fb6 100644
--- a/src/eap_peer/eap_tls.c
+++ b/src/eap_peer/eap_tls.c
@@ -1,6 +1,6 @@
 /*
  * EAP peer method: EAP-TLS (RFC 2716)
- * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2008, 2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -21,6 +21,7 @@
 struct eap_tls_data {
 	struct eap_ssl_data ssl;
 	u8 *key_data;
+	void *ssl_ctx;
 };
 
 
@@ -40,6 +41,9 @@
 	if (data == NULL)
 		return NULL;
 
+	data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 :
+		sm->ssl_ctx;
+
 	if (eap_peer_tls_ssl_init(sm, &data->ssl, config)) {
 		wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
 		eap_tls_deinit(sm, data);
@@ -165,7 +169,7 @@
 		return eap_tls_failure(sm, data, ret, res, resp, id);
 	}
 
-	if (tls_connection_established(sm->ssl_ctx, data->ssl.conn))
+	if (tls_connection_established(data->ssl_ctx, data->ssl.conn))
 		eap_tls_success(sm, data, ret);
 
 	if (res == 1) {
@@ -180,7 +184,7 @@
 static Boolean eap_tls_has_reauth_data(struct eap_sm *sm, void *priv)
 {
 	struct eap_tls_data *data = priv;
-	return tls_connection_established(sm->ssl_ctx, data->ssl.conn);
+	return tls_connection_established(data->ssl_ctx, data->ssl.conn);
 }
 
 
diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c
index 52549f4..69e83d9 100644
--- a/src/eap_peer/eap_tls_common.c
+++ b/src/eap_peer/eap_tls_common.c
@@ -1,6 +1,6 @@
 /*
  * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions
- * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -136,14 +136,14 @@
 {
 	int res;
 
-	data->conn = tls_connection_init(sm->ssl_ctx);
+	data->conn = tls_connection_init(data->ssl_ctx);
 	if (data->conn == NULL) {
 		wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS "
 			   "connection");
 		return -1;
 	}
 
-	res = tls_connection_set_params(sm->ssl_ctx, data->conn, params);
+	res = tls_connection_set_params(data->ssl_ctx, data->conn, params);
 	if (res == TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED) {
 		/*
 		 * At this point with the pkcs11 engine the PIN might be wrong.
@@ -162,13 +162,13 @@
 		config->pin = NULL;
 		eap_sm_request_pin(sm);
 		sm->ignore = TRUE;
-		tls_connection_deinit(sm->ssl_ctx, data->conn);
+		tls_connection_deinit(data->ssl_ctx, data->conn);
 		data->conn = NULL;
 		return -1;
 	} else if (res) {
 		wpa_printf(MSG_INFO, "TLS: Failed to set TLS connection "
 			   "parameters");
-		tls_connection_deinit(sm->ssl_ctx, data->conn);
+		tls_connection_deinit(data->ssl_ctx, data->conn);
 		data->conn = NULL;
 		return -1;
 	}
@@ -197,6 +197,8 @@
 
 	data->eap = sm;
 	data->phase2 = sm->init_phase2;
+	data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 :
+		sm->ssl_ctx;
 	if (eap_tls_params_from_conf(sm, data, &params, config, data->phase2) <
 	    0)
 		return -1;
@@ -234,7 +236,7 @@
  */
 void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data)
 {
-	tls_connection_deinit(sm->ssl_ctx, data->conn);
+	tls_connection_deinit(data->ssl_ctx, data->conn);
 	eap_peer_tls_reset_input(data);
 	eap_peer_tls_reset_output(data);
 }
@@ -265,8 +267,8 @@
 		return NULL;
 
 	/* First, try to use TLS library function for PRF, if available. */
-	if (tls_connection_prf(sm->ssl_ctx, data->conn, label, 0, out, len) ==
-	    0)
+	if (tls_connection_prf(data->ssl_ctx, data->conn, label, 0, out, len)
+	    == 0)
 		return out;
 
 	/*
@@ -274,7 +276,7 @@
 	 * session parameters and use an internal implementation of TLS PRF to
 	 * derive the key.
 	 */
-	if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys))
+	if (tls_connection_get_keys(data->ssl_ctx, data->conn, &keys))
 		goto fail;
 
 	if (keys.client_random == NULL || keys.server_random == NULL ||
@@ -441,14 +443,14 @@
 		WPA_ASSERT(data->tls_out == NULL);
 	}
 	appl_data = NULL;
-	data->tls_out = tls_connection_handshake(sm->ssl_ctx, data->conn,
+	data->tls_out = tls_connection_handshake(data->ssl_ctx, data->conn,
 						 msg, &appl_data);
 
 	eap_peer_tls_reset_input(data);
 
 	if (appl_data &&
-	    tls_connection_established(sm->ssl_ctx, data->conn) &&
-	    !tls_connection_get_failed(sm->ssl_ctx, data->conn)) {
+	    tls_connection_established(data->ssl_ctx, data->conn) &&
+	    !tls_connection_get_failed(data->ssl_ctx, data->conn)) {
 		wpa_hexdump_buf_key(MSG_MSGDUMP, "SSL: Application data",
 				    appl_data);
 		*out_data = appl_data;
@@ -616,7 +618,7 @@
 		return -1;
 	}
 
-	if (tls_connection_get_failed(sm->ssl_ctx, data->conn)) {
+	if (tls_connection_get_failed(data->ssl_ctx, data->conn)) {
 		/* TLS processing has failed - return error */
 		wpa_printf(MSG_DEBUG, "SSL: Failed - tls_out available to "
 			   "report error");
@@ -675,7 +677,7 @@
 {
 	eap_peer_tls_reset_input(data);
 	eap_peer_tls_reset_output(data);
-	return tls_connection_shutdown(sm->ssl_ctx, data->conn);
+	return tls_connection_shutdown(data->ssl_ctx, data->conn);
 }
 
 
@@ -694,7 +696,8 @@
 	char name[128];
 	int len = 0, ret;
 
-	if (tls_get_cipher(sm->ssl_ctx, data->conn, name, sizeof(name)) == 0) {
+	if (tls_get_cipher(data->ssl_ctx, data->conn, name, sizeof(name)) == 0)
+	{
 		ret = os_snprintf(buf + len, buflen - len,
 				  "EAP TLS cipher=%s\n", name);
 		if (ret < 0 || (size_t) ret >= buflen - len)
@@ -741,7 +744,7 @@
 	size_t left;
 	unsigned int tls_msg_len;
 
-	if (tls_get_errors(sm->ssl_ctx)) {
+	if (tls_get_errors(data->ssl_ctx)) {
 		wpa_printf(MSG_INFO, "SSL: TLS errors detected");
 		ret->ignore = TRUE;
 		return NULL;
@@ -849,7 +852,7 @@
 	if (msg == NULL)
 		return need_more_input ? 1 : -1;
 
-	*in_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->conn, msg);
+	*in_decrypted = tls_connection_decrypt(data->ssl_ctx, data->conn, msg);
 	eap_peer_tls_reset_input(data);
 	if (*in_decrypted == NULL) {
 		wpa_printf(MSG_INFO, "SSL: Failed to decrypt Phase 2 data");
@@ -877,8 +880,8 @@
 {
 	if (in_data) {
 		eap_peer_tls_reset_output(data);
-		data->tls_out = tls_connection_encrypt(sm->ssl_ctx, data->conn,
-						       in_data);
+		data->tls_out = tls_connection_encrypt(data->ssl_ctx,
+						       data->conn, in_data);
 		if (data->tls_out == NULL) {
 			wpa_printf(MSG_INFO, "SSL: Failed to encrypt Phase 2 "
 				   "data (in_len=%lu)",
diff --git a/src/eap_peer/eap_tls_common.h b/src/eap_peer/eap_tls_common.h
index 7426467..771385b 100644
--- a/src/eap_peer/eap_tls_common.h
+++ b/src/eap_peer/eap_tls_common.h
@@ -1,6 +1,6 @@
 /*
  * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions
- * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2009, 2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -63,6 +63,11 @@
 	 * eap - EAP state machine allocated with eap_peer_sm_init()
 	 */
 	struct eap_sm *eap;
+
+	/**
+	 * ssl_ctx - TLS library context to use for the connection
+	 */
+	void *ssl_ctx;
 };
 
 
diff --git a/src/eap_peer/eap_ttls.c b/src/eap_peer/eap_ttls.c
index 0204ba2..e09f5e5 100644
--- a/src/eap_peer/eap_ttls.c
+++ b/src/eap_peer/eap_ttls.c
@@ -435,7 +435,6 @@
 			   "implicit challenge");
 		return -1;
 	}
-	peer_challenge = challenge + 1 + EAP_TTLS_MSCHAPV2_CHALLENGE_LEN;
 
 	pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE,
 			       RADIUS_VENDOR_ID_MICROSOFT, 1,
@@ -448,7 +447,14 @@
 	data->ident = challenge[EAP_TTLS_MSCHAPV2_CHALLENGE_LEN];
 	*pos++ = data->ident;
 	*pos++ = 0; /* Flags */
-	os_memcpy(pos, peer_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN);
+	if (os_get_random(pos, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN) < 0) {
+		os_free(challenge);
+		wpabuf_free(msg);
+		wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to get "
+			   "random data for peer challenge");
+		return -1;
+	}
+	peer_challenge = pos;
 	pos += EAP_TTLS_MSCHAPV2_CHALLENGE_LEN;
 	os_memset(pos, 0, 8); /* Reserved, must be zero */
 	pos += 8;
@@ -456,6 +462,7 @@
 				     password_len, pwhash, challenge,
 				     peer_challenge, pos, data->auth_response,
 				     data->master_key)) {
+		os_free(challenge);
 		wpabuf_free(msg);
 		wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive "
 			   "response");
diff --git a/src/eap_peer/eap_wsc.c b/src/eap_peer/eap_wsc.c
index 4473b90..d007a57 100644
--- a/src/eap_peer/eap_wsc.c
+++ b/src/eap_peer/eap_wsc.c
@@ -1,6 +1,6 @@
 /*
  * EAP-WSC peer for Wi-Fi Protected Setup
- * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007-2009, 2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -137,6 +137,8 @@
 	struct wps_context *wps;
 	struct wps_credential new_ap_settings;
 	int res;
+	u8 dev_pw[WPS_OOB_DEVICE_PASSWORD_LEN];
+	int nfc = 0;
 
 	wps = sm->wps;
 	if (wps == NULL) {
@@ -184,13 +186,26 @@
 		while (*pos != '\0' && *pos != ' ')
 			pos++;
 		cfg.pin_len = pos - (const char *) cfg.pin;
+		if (cfg.pin_len >= WPS_OOB_DEVICE_PASSWORD_MIN_LEN * 2 &&
+		    cfg.pin_len <= WPS_OOB_DEVICE_PASSWORD_LEN * 2 &&
+		    hexstr2bin((const char *) cfg.pin, dev_pw,
+			       cfg.pin_len / 2) == 0) {
+			/* Convert OOB Device Password to binary */
+			cfg.pin = dev_pw;
+			cfg.pin_len /= 2;
+		}
+		if (cfg.pin_len == 6 && os_strncmp(pos, "nfc-pw", 6) == 0) {
+			cfg.pin = NULL;
+			cfg.pin_len = 0;
+			nfc = 1;
+		}
 	} else {
 		pos = os_strstr(phase1, "pbc=1");
 		if (pos)
 			cfg.pbc = 1;
 	}
 
-	if (cfg.pin == NULL && !cfg.pbc) {
+	if (cfg.pin == NULL && !cfg.pbc && !nfc) {
 		wpa_printf(MSG_INFO, "EAP-WSC: PIN or PBC not set in phase1 "
 			   "configuration data");
 		os_free(data);
diff --git a/src/eap_server/eap_server_aka.c b/src/eap_server/eap_server_aka.c
index 135c02b..9cd5509 100644
--- a/src/eap_server/eap_server_aka.c
+++ b/src/eap_server/eap_server_aka.c
@@ -1,6 +1,6 @@
 /*
- * hostapd / EAP-AKA (RFC 4187) and EAP-AKA' (draft-arkko-eap-aka-kdf)
- * Copyright (c) 2005-2008, Jouni Malinen <j@w1.fi>
+ * hostapd / EAP-AKA (RFC 4187) and EAP-AKA' (RFC 5448)
+ * Copyright (c) 2005-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -266,8 +266,18 @@
 			       EAP_AKA_SUBTYPE_IDENTITY);
 	if (eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity,
 				      sm->identity_len)) {
-		wpa_printf(MSG_DEBUG, "   AT_PERMANENT_ID_REQ");
-		eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0);
+		if (sm->identity_len > 0 &&
+		    (sm->identity[0] == EAP_AKA_REAUTH_ID_PREFIX ||
+		     sm->identity[0] == EAP_AKA_PRIME_REAUTH_ID_PREFIX)) {
+			/* Reauth id may have expired - try fullauth */
+			wpa_printf(MSG_DEBUG, "   AT_FULLAUTH_ID_REQ");
+			eap_sim_msg_add(msg, EAP_SIM_AT_FULLAUTH_ID_REQ, 0,
+					NULL, 0);
+		} else {
+			wpa_printf(MSG_DEBUG, "   AT_PERMANENT_ID_REQ");
+			eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0,
+					NULL, 0);
+		}
 	} else {
 		/*
 		 * RFC 4187, Chap. 4.1.4 recommends that identity from EAP is
@@ -292,12 +302,23 @@
 			      const u8 *nonce_s)
 {
 	os_free(data->next_pseudonym);
-	data->next_pseudonym =
-		eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, 1);
+	if (nonce_s == NULL) {
+		data->next_pseudonym =
+			eap_sim_db_get_next_pseudonym(
+				sm->eap_sim_db_priv,
+				data->eap_method == EAP_TYPE_AKA_PRIME ?
+				EAP_SIM_DB_AKA_PRIME : EAP_SIM_DB_AKA);
+	} else {
+		/* Do not update pseudonym during re-authentication */
+		data->next_pseudonym = NULL;
+	}
 	os_free(data->next_reauth_id);
 	if (data->counter <= EAP_AKA_MAX_FAST_REAUTHS) {
 		data->next_reauth_id =
-			eap_sim_db_get_next_reauth_id(sm->eap_sim_db_priv, 1);
+			eap_sim_db_get_next_reauth_id(
+				sm->eap_sim_db_priv,
+				data->eap_method == EAP_TYPE_AKA_PRIME ?
+				EAP_SIM_DB_AKA_PRIME : EAP_SIM_DB_AKA);
 	} else {
 		wpa_printf(MSG_DEBUG, "EAP-AKA: Max fast re-authentication "
 			   "count exceeded - force full authentication");
@@ -615,7 +636,8 @@
 		identity = data->reauth->identity;
 		identity_len = data->reauth->identity_len;
 	} else if (sm->identity && sm->identity_len > 0 &&
-		   sm->identity[0] == EAP_AKA_PERMANENT_PREFIX) {
+		   (sm->identity[0] == EAP_AKA_PERMANENT_PREFIX ||
+		    sm->identity[0] == EAP_AKA_PRIME_PERMANENT_PREFIX)) {
 		identity = sm->identity;
 		identity_len = sm->identity_len;
 	} else {
@@ -731,7 +753,7 @@
 			  sm->identity, identity_len);
 
 	if (data->eap_method == EAP_TYPE_AKA_PRIME) {
-		eap_aka_prime_derive_keys(identity, identity_len, data->ik,
+		eap_aka_prime_derive_keys(sm->identity, identity_len, data->ik,
 					  data->ck, data->k_encr, data->k_aut,
 					  data->k_re, data->msk, data->emsk);
 	} else {
@@ -1017,11 +1039,6 @@
 		identity_len = id2_len;
 	}
 
-	if (data->next_pseudonym) {
-		eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity,
-					 identity_len, data->next_pseudonym);
-		data->next_pseudonym = NULL;
-	}
 	if (data->next_reauth_id) {
 		if (data->eap_method == EAP_TYPE_AKA_PRIME) {
 #ifdef EAP_SERVER_AKA_PRIME
diff --git a/src/eap_server/eap_server_pwd.c b/src/eap_server/eap_server_pwd.c
index 6c47dee..b61061b 100644
--- a/src/eap_server/eap_server_pwd.c
+++ b/src/eap_server/eap_server_pwd.c
@@ -9,6 +9,7 @@
 #include "includes.h"
 
 #include "common.h"
+#include "crypto/sha256.h"
 #include "eap_server/eap_i.h"
 #include "eap_common/eap_pwd_common.h"
 
@@ -40,7 +41,7 @@
 	EC_POINT *my_element;
 	EC_POINT *peer_element;
 
-	u8 my_confirm[SHA256_DIGEST_LENGTH];
+	u8 my_confirm[SHA256_MAC_LEN];
 
 	u8 msk[EAP_MSK_LEN];
 	u8 emsk[EAP_EMSK_LEN];
@@ -288,8 +289,8 @@
 				      struct eap_pwd_data *data, u8 id)
 {
 	BIGNUM *x = NULL, *y = NULL;
-	HMAC_CTX ctx;
-	u8 conf[SHA256_DIGEST_LENGTH], *cruft = NULL, *ptr;
+	struct crypto_hash *hash;
+	u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr;
 	u16 grp;
 	int offset;
 
@@ -313,7 +314,9 @@
 	 * commit is H(k | server_element | server_scalar | peer_element |
 	 *	       peer_scalar | ciphersuite)
 	 */
-	H_Init(&ctx);
+	hash = eap_pwd_h_init();
+	if (hash == NULL)
+		goto fin;
 
 	/*
 	 * Zero the memory each time because this is mod prime math and some
@@ -324,7 +327,7 @@
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k);
 	BN_bn2bin(data->k, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 
 	/* server element: x, y */
 	if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
@@ -338,18 +341,18 @@
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
 	BN_bn2bin(x, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
 	BN_bn2bin(y, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 
 	/* server scalar */
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->order) -
 		BN_num_bytes(data->my_scalar);
 	BN_bn2bin(data->my_scalar, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->order));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
 
 	/* peer element: x, y */
 	if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
@@ -363,18 +366,18 @@
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
 	BN_bn2bin(x, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
 	BN_bn2bin(y, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 
 	/* peer scalar */
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->order) -
 		BN_num_bytes(data->peer_scalar);
 	BN_bn2bin(data->peer_scalar, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->order));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
 
 	/* ciphersuite */
 	grp = htons(data->group_num);
@@ -386,17 +389,17 @@
 	ptr += sizeof(u8);
 	*ptr = EAP_PWD_DEFAULT_PRF;
 	ptr += sizeof(u8);
-	H_Update(&ctx, cruft, ptr-cruft);
+	eap_pwd_h_update(hash, cruft, ptr - cruft);
 
 	/* all done with the random function */
-	H_Final(&ctx, conf);
-	os_memcpy(data->my_confirm, conf, SHA256_DIGEST_LENGTH);
+	eap_pwd_h_final(hash, conf);
+	os_memcpy(data->my_confirm, conf, SHA256_MAC_LEN);
 
-	data->outbuf = wpabuf_alloc(SHA256_DIGEST_LENGTH);
+	data->outbuf = wpabuf_alloc(SHA256_MAC_LEN);
 	if (data->outbuf == NULL)
 		goto fin;
 
-	wpabuf_put_data(data->outbuf, conf, SHA256_DIGEST_LENGTH);
+	wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN);
 
 fin:
 	os_free(cruft);
@@ -404,8 +407,6 @@
 	BN_free(y);
 	if (data->outbuf == NULL)
 		eap_pwd_state(data, FAILURE);
-
-	return;
 }
 
 
@@ -735,10 +736,10 @@
 			     const u8 *payload, size_t payload_len)
 {
 	BIGNUM *x = NULL, *y = NULL;
-	HMAC_CTX ctx;
+	struct crypto_hash *hash;
 	u32 cs;
 	u16 grp;
-	u8 conf[SHA256_DIGEST_LENGTH], *cruft = NULL, *ptr;
+	u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr;
 	int offset;
 
 	/* build up the ciphersuite: group | random_function | prf */
@@ -761,13 +762,15 @@
 	 * commit is H(k | peer_element | peer_scalar | server_element |
 	 *	       server_scalar | ciphersuite)
 	 */
-	H_Init(&ctx);
+	hash = eap_pwd_h_init();
+	if (hash == NULL)
+		goto fin;
 
 	/* k */
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k);
 	BN_bn2bin(data->k, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 
 	/* peer element: x, y */
 	if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
@@ -780,18 +783,18 @@
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
 	BN_bn2bin(x, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
 	BN_bn2bin(y, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 
 	/* peer scalar */
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->order) -
 		BN_num_bytes(data->peer_scalar);
 	BN_bn2bin(data->peer_scalar, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->order));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
 
 	/* server element: x, y */
 	if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
@@ -805,28 +808,28 @@
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
 	BN_bn2bin(x, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
 	BN_bn2bin(y, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 
 	/* server scalar */
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 	offset = BN_num_bytes(data->grp->order) -
 		BN_num_bytes(data->my_scalar);
 	BN_bn2bin(data->my_scalar, cruft + offset);
-	H_Update(&ctx, cruft, BN_num_bytes(data->grp->order));
+	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
 
 	/* ciphersuite */
 	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
-	H_Update(&ctx, (u8 *)&cs, sizeof(u32));
+	eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32));
 
 	/* all done */
-	H_Final(&ctx, conf);
+	eap_pwd_h_final(hash, conf);
 
 	ptr = (u8 *) payload;
-	if (os_memcmp(conf, ptr, SHA256_DIGEST_LENGTH)) {
+	if (os_memcmp(conf, ptr, SHA256_MAC_LEN)) {
 		wpa_printf(MSG_INFO, "EAP-PWD (server): confirm did not "
 			   "verify");
 		goto fin;
diff --git a/src/eap_server/eap_server_sim.c b/src/eap_server/eap_server_sim.c
index f49f70e..6658d9c 100644
--- a/src/eap_server/eap_server_sim.c
+++ b/src/eap_server/eap_server_sim.c
@@ -107,8 +107,17 @@
 			       EAP_SIM_SUBTYPE_START);
 	if (eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity,
 				      sm->identity_len)) {
-		wpa_printf(MSG_DEBUG, "   AT_PERMANENT_ID_REQ");
-		eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0);
+		if (sm->identity_len > 0 &&
+		    sm->identity[0] == EAP_SIM_REAUTH_ID_PREFIX) {
+			/* Reauth id may have expired - try fullauth */
+			wpa_printf(MSG_DEBUG, "   AT_FULLAUTH_ID_REQ");
+			eap_sim_msg_add(msg, EAP_SIM_AT_FULLAUTH_ID_REQ, 0,
+					NULL, 0);
+		} else {
+			wpa_printf(MSG_DEBUG, "   AT_PERMANENT_ID_REQ");
+			eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0,
+					NULL, 0);
+		}
 	} else {
 		/*
 		 * RFC 4186, Chap. 4.2.4 recommends that identity from EAP is
@@ -131,12 +140,19 @@
 			      const u8 *nonce_s)
 {
 	os_free(data->next_pseudonym);
-	data->next_pseudonym =
-		eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, 0);
+	if (nonce_s == NULL) {
+		data->next_pseudonym =
+			eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv,
+						      EAP_SIM_DB_SIM);
+	} else {
+		/* Do not update pseudonym during re-authentication */
+		data->next_pseudonym = NULL;
+	}
 	os_free(data->next_reauth_id);
 	if (data->counter <= EAP_SIM_MAX_FAST_REAUTHS) {
 		data->next_reauth_id =
-			eap_sim_db_get_next_reauth_id(sm->eap_sim_db_priv, 0);
+			eap_sim_db_get_next_reauth_id(sm->eap_sim_db_priv,
+						      EAP_SIM_DB_SIM);
 	} else {
 		wpa_printf(MSG_DEBUG, "EAP-SIM: Max fast re-authentication "
 			   "count exceeded - force full authentication");
@@ -616,11 +632,6 @@
 		identity_len = id2_len;
 	}
 
-	if (data->next_pseudonym) {
-		eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity,
-					 identity_len, data->next_pseudonym);
-		data->next_pseudonym = NULL;
-	}
 	if (data->next_reauth_id) {
 		eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity,
 				      identity_len, data->next_reauth_id,
diff --git a/src/eap_server/eap_sim_db.c b/src/eap_server/eap_sim_db.c
index 9db26dc..ce3238c 100644
--- a/src/eap_server/eap_sim_db.c
+++ b/src/eap_server/eap_sim_db.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / EAP-SIM database/authenticator gateway
- * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005-2010, 2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -647,7 +647,8 @@
 
 	if (identity_len == 0 ||
 	    (identity[0] != EAP_SIM_PSEUDONYM_PREFIX &&
-	     identity[0] != EAP_AKA_PSEUDONYM_PREFIX))
+	     identity[0] != EAP_AKA_PSEUDONYM_PREFIX &&
+	     identity[0] != EAP_AKA_PRIME_PSEUDONYM_PREFIX))
 		return NULL;
 
 	/* Remove possible realm from identity */
@@ -685,7 +686,8 @@
 
 	if (identity_len == 0 ||
 	    (identity[0] != EAP_SIM_PERMANENT_PREFIX &&
-	     identity[0] != EAP_AKA_PERMANENT_PREFIX))
+	     identity[0] != EAP_AKA_PERMANENT_PREFIX &&
+	     identity[0] != EAP_AKA_PRIME_PERMANENT_PREFIX))
 		return NULL;
 
 	p = data->pseudonyms;
@@ -710,7 +712,8 @@
 
 	if (identity_len == 0 ||
 	    (identity[0] != EAP_SIM_REAUTH_ID_PREFIX &&
-	     identity[0] != EAP_AKA_REAUTH_ID_PREFIX))
+	     identity[0] != EAP_AKA_REAUTH_ID_PREFIX &&
+	     identity[0] != EAP_AKA_PRIME_REAUTH_ID_PREFIX))
 		return NULL;
 
 	/* Remove possible realm from identity */
@@ -777,8 +780,9 @@
  * @identity_len: Length of identity in bytes 
  * Returns: 0 if the user is found or -1 on failure
  *
- * In most cases, the user name is ['0','1'] | IMSI, i.e., 1 followed by the
- * IMSI in ASCII format, ['2','3'] | pseudonym, or ['4','5'] | reauth_id.
+ * In most cases, the user name is ['0','1','6'] | IMSI, i.e., 1 followed by
+ * the IMSI in ASCII format for EAP-SIM, ['2','3','7'] | pseudonym, or
+ * ['4','5','7'] | reauth_id.
  */
 int eap_sim_db_identity_known(void *priv, const u8 *identity,
 			      size_t identity_len)
@@ -789,21 +793,24 @@
 		return -1;
 
 	if (identity[0] == EAP_SIM_PSEUDONYM_PREFIX ||
-	    identity[0] == EAP_AKA_PSEUDONYM_PREFIX) {
+	    identity[0] == EAP_AKA_PSEUDONYM_PREFIX ||
+	    identity[0] == EAP_AKA_PRIME_PSEUDONYM_PREFIX) {
 		struct eap_sim_pseudonym *p =
 			eap_sim_db_get_pseudonym(data, identity, identity_len);
 		return p ? 0 : -1;
 	}
 
 	if (identity[0] == EAP_SIM_REAUTH_ID_PREFIX ||
-	    identity[0] == EAP_AKA_REAUTH_ID_PREFIX) {
+	    identity[0] == EAP_AKA_REAUTH_ID_PREFIX ||
+	    identity[0] == EAP_AKA_PRIME_REAUTH_ID_PREFIX) {
 		struct eap_sim_reauth *r =
 			eap_sim_db_get_reauth(data, identity, identity_len);
 		return r ? 0 : -1;
 	}
 
 	if (identity[0] != EAP_SIM_PERMANENT_PREFIX &&
-	    identity[0] != EAP_AKA_PERMANENT_PREFIX) {
+	    identity[0] != EAP_AKA_PERMANENT_PREFIX &&
+	    identity[0] != EAP_AKA_PRIME_PERMANENT_PREFIX) {
 		/* Unknown identity prefix */
 		return -1;
 	}
@@ -843,7 +850,7 @@
 /**
  * eap_sim_db_get_next_pseudonym - EAP-SIM DB: Get next pseudonym
  * @priv: Private data pointer from eap_sim_db_init()
- * @aka: Using EAP-AKA instead of EAP-SIM
+ * @method: EAP method (SIM/AKA/AKA')
  * Returns: Next pseudonym (allocated string) or %NULL on failure
  *
  * This function is used to generate a pseudonym for EAP-SIM. The returned
@@ -851,18 +858,31 @@
  * with eap_sim_db_add_pseudonym() once the authentication has been completed
  * successfully. Caller is responsible for freeing the returned buffer.
  */
-char * eap_sim_db_get_next_pseudonym(void *priv, int aka)
+char * eap_sim_db_get_next_pseudonym(void *priv, enum eap_sim_db_method method)
 {
 	struct eap_sim_db_data *data = priv;
-	return eap_sim_db_get_next(data, aka ? EAP_AKA_PSEUDONYM_PREFIX :
-				   EAP_SIM_PSEUDONYM_PREFIX);
+	char prefix = EAP_SIM_REAUTH_ID_PREFIX;
+
+	switch (method) {
+	case EAP_SIM_DB_SIM:
+		prefix = EAP_SIM_PSEUDONYM_PREFIX;
+		break;
+	case EAP_SIM_DB_AKA:
+		prefix = EAP_AKA_PSEUDONYM_PREFIX;
+		break;
+	case EAP_SIM_DB_AKA_PRIME:
+		prefix = EAP_AKA_PRIME_PSEUDONYM_PREFIX;
+		break;
+	}
+
+	return eap_sim_db_get_next(data, prefix);
 }
 
 
 /**
  * eap_sim_db_get_next_reauth_id - EAP-SIM DB: Get next reauth_id
  * @priv: Private data pointer from eap_sim_db_init()
- * @aka: Using EAP-AKA instead of EAP-SIM
+ * @method: EAP method (SIM/AKA/AKA')
  * Returns: Next reauth_id (allocated string) or %NULL on failure
  *
  * This function is used to generate a fast re-authentication identity for
@@ -871,11 +891,24 @@
  * has been completed successfully. Caller is responsible for freeing the
  * returned buffer.
  */
-char * eap_sim_db_get_next_reauth_id(void *priv, int aka)
+char * eap_sim_db_get_next_reauth_id(void *priv, enum eap_sim_db_method method)
 {
 	struct eap_sim_db_data *data = priv;
-	return eap_sim_db_get_next(data, aka ? EAP_AKA_REAUTH_ID_PREFIX :
-				   EAP_SIM_REAUTH_ID_PREFIX);
+	char prefix = EAP_SIM_REAUTH_ID_PREFIX;
+
+	switch (method) {
+	case EAP_SIM_DB_SIM:
+		prefix = EAP_SIM_REAUTH_ID_PREFIX;
+		break;
+	case EAP_SIM_DB_AKA:
+		prefix = EAP_AKA_REAUTH_ID_PREFIX;
+		break;
+	case EAP_SIM_DB_AKA_PRIME:
+		prefix = EAP_AKA_PRIME_REAUTH_ID_PREFIX;
+		break;
+	}
+
+	return eap_sim_db_get_next(data, prefix);
 }
 
 
@@ -1156,7 +1189,7 @@
  * called once the results become available.
  *
  * In most cases, the user name is '0' | IMSI, i.e., 0 followed by the IMSI in
- * ASCII format.
+ * ASCII format for EAP-AKA and '6' | IMSI for EAP-AKA'.
  *
  * When using an external server for AKA authentication, this function can
  * always start a request and return EAP_SIM_DB_PENDING immediately if
@@ -1178,7 +1211,8 @@
 	char msg[40];
 
 	if (identity_len < 2 || identity == NULL ||
-	    identity[0] != EAP_AKA_PERMANENT_PREFIX) {
+	    (identity[0] != EAP_AKA_PERMANENT_PREFIX &&
+	     identity[0] != EAP_AKA_PRIME_PERMANENT_PREFIX)) {
 		wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
 				  identity, identity_len);
 		return EAP_SIM_DB_FAILURE;
@@ -1281,7 +1315,8 @@
 	size_t i;
 
 	if (identity_len < 2 || identity == NULL ||
-	    identity[0] != EAP_AKA_PERMANENT_PREFIX) {
+	    (identity[0] != EAP_AKA_PERMANENT_PREFIX &&
+	     identity[0] != EAP_AKA_PRIME_PERMANENT_PREFIX)) {
 		wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
 				  identity, identity_len);
 		return -1;
diff --git a/src/eap_server/eap_sim_db.h b/src/eap_server/eap_sim_db.h
index abe185e..1f6375a 100644
--- a/src/eap_server/eap_sim_db.h
+++ b/src/eap_server/eap_sim_db.h
@@ -1,6 +1,6 @@
 /*
  * hostapd / EAP-SIM database/authenticator gateway
- * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005-2008, 2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -18,6 +18,15 @@
 #define EAP_AKA_PERMANENT_PREFIX '0'
 #define EAP_AKA_PSEUDONYM_PREFIX '2'
 #define EAP_AKA_REAUTH_ID_PREFIX '4'
+#define EAP_AKA_PRIME_PERMANENT_PREFIX '6'
+#define EAP_AKA_PRIME_PSEUDONYM_PREFIX '7'
+#define EAP_AKA_PRIME_REAUTH_ID_PREFIX '8'
+
+enum eap_sim_db_method {
+	EAP_SIM_DB_SIM,
+	EAP_SIM_DB_AKA,
+	EAP_SIM_DB_AKA_PRIME
+};
 
 void * eap_sim_db_init(const char *config,
 		       void (*get_complete_cb)(void *ctx, void *session_ctx),
@@ -36,9 +45,11 @@
 int eap_sim_db_identity_known(void *priv, const u8 *identity,
 			      size_t identity_len);
 
-char * eap_sim_db_get_next_pseudonym(void *priv, int aka);
+char * eap_sim_db_get_next_pseudonym(void *priv,
+				     enum eap_sim_db_method method);
 
-char * eap_sim_db_get_next_reauth_id(void *priv, int aka);
+char * eap_sim_db_get_next_reauth_id(void *priv,
+				     enum eap_sim_db_method method);
 
 int eap_sim_db_add_pseudonym(void *priv, const u8 *identity,
 			     size_t identity_len, char *pseudonym);
diff --git a/src/eapol_auth/eapol_auth_sm_i.h b/src/eapol_auth/eapol_auth_sm_i.h
index d0b7641..d7f893a 100644
--- a/src/eapol_auth/eapol_auth_sm_i.h
+++ b/src/eapol_auth/eapol_auth_sm_i.h
@@ -157,6 +157,7 @@
 			      * Authentication server */
 	u8 eap_type_supp; /* EAP type of the last EAP packet from Supplicant */
 	struct radius_class_data radius_class;
+	struct wpabuf *radius_cui; /* Chargeable-User-Identity */
 
 	/* Keys for encrypting and signing EAPOL-Key frames */
 	u8 *eapol_key_sign;
diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c
index c83709f..f0cae70 100644
--- a/src/eapol_supp/eapol_supp_sm.c
+++ b/src/eapol_supp/eapol_supp_sm.c
@@ -1847,6 +1847,17 @@
 				 cert_hash, cert);
 }
 
+
+static void eapol_sm_notify_status(void *ctx, const char *status,
+				   const char *parameter)
+{
+	struct eapol_sm *sm = ctx;
+
+	if (sm->ctx->status_cb)
+		sm->ctx->status_cb(sm->ctx->ctx, status, parameter);
+}
+
+
 static struct eapol_callbacks eapol_cb =
 {
 	eapol_sm_get_config,
@@ -1859,7 +1870,8 @@
 	eapol_sm_get_config_blob,
 	eapol_sm_notify_pending,
 	eapol_sm_eap_param_needed,
-	eapol_sm_notify_cert
+	eapol_sm_notify_cert,
+	eapol_sm_notify_status
 };
 
 
diff --git a/src/eapol_supp/eapol_supp_sm.h b/src/eapol_supp/eapol_supp_sm.h
index 3260c61..1a20e4b 100644
--- a/src/eapol_supp/eapol_supp_sm.h
+++ b/src/eapol_supp/eapol_supp_sm.h
@@ -230,6 +230,15 @@
 	 * cert_in_cb - Include server certificates in callback
 	 */
 	int cert_in_cb;
+
+	/**
+	 * status_cb - Notification of a change in EAP status
+	 * @ctx: Callback context (ctx)
+	 * @status: Step in the process of EAP authentication
+	 * @parameter: Step-specific parameter, e.g., EAP method name
+	 */
+	void (*status_cb)(void *ctx, const char *status,
+			  const char *parameter);
 };
 
 
diff --git a/src/l2_packet/l2_packet_freebsd.c b/src/l2_packet/l2_packet_freebsd.c
index e24277c..2e9a04c 100644
--- a/src/l2_packet/l2_packet_freebsd.c
+++ b/src/l2_packet/l2_packet_freebsd.c
@@ -3,14 +3,8 @@
  * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
  * Copyright (c) 2005, Sam Leffler <sam@errno.com>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "includes.h"
diff --git a/src/l2_packet/l2_packet_privsep.c b/src/l2_packet/l2_packet_privsep.c
index aacd76b..6b117ca 100644
--- a/src/l2_packet/l2_packet_privsep.c
+++ b/src/l2_packet/l2_packet_privsep.c
@@ -173,7 +173,7 @@
 	addr.sun_family = AF_UNIX;
 	os_strlcpy(addr.sun_path, l2->own_socket_path, sizeof(addr.sun_path));
 	if (bind(l2->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-		perror("bind(PF_UNIX)");
+		perror("l2-pkt-privsep: bind(PF_UNIX)");
 		goto fail;
 	}
 
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index aeeaccb..58ada3b 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -119,7 +119,7 @@
 			continue;
 #endif
 
-		wpa_msg(p2p->cfg->msg_ctx, MSG_ERROR, "P2P: Expiring old peer "
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Expiring old peer "
 			"entry " MACSTR, MAC2STR(dev->info.p2p_device_addr));
 #ifdef ANDROID_P2P
 		/* SD_FAIR_POLICY: Update the current sd_dev_list pointer to next device */
@@ -193,14 +193,14 @@
 }
 
 
-void p2p_clear_provisioning_info(struct p2p_data *p2p, const u8 *iface_addr)
+void p2p_clear_provisioning_info(struct p2p_data *p2p, const u8 *addr)
 {
 	struct p2p_device *dev = NULL;
 
-	if (!iface_addr || !p2p)
+	if (!addr || !p2p)
 		return;
 
-	dev = p2p_get_device_interface(p2p, iface_addr);
+	dev = p2p_get_device(p2p, addr);
 	if (dev)
 		dev->wps_prov_info = 0;
 }
@@ -315,7 +315,7 @@
 	p2p->pending_listen_usec = (timeout % 1000) * 1000;
 
 	if (p2p->p2p_scan_running) {
-		if (p2p->start_after_scan == P2P_AFTER_SCAN_NOTHING) {
+		if (p2p->start_after_scan == P2P_AFTER_SCAN_CONNECT) {
 			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
 				"P2P: p2p_scan running - connect is already "
 				"pending - skip listen");
@@ -484,13 +484,25 @@
 			continue; /* ignore our own entry */
 		dev = p2p_get_device(p2p, cli->p2p_device_addr);
 		if (dev) {
-			/*
-			 * Update information only if we have not received this
-			 * directly from the client.
-			 */
 			if (dev->flags & (P2P_DEV_GROUP_CLIENT_ONLY |
-					  P2P_DEV_PROBE_REQ_ONLY))
+					  P2P_DEV_PROBE_REQ_ONLY)) {
+				/*
+				 * Update information since we have not
+				 * received this directly from the client.
+				 */
 				p2p_copy_client_info(dev, cli);
+			} else {
+				/*
+				 * Need to update P2P Client Discoverability
+				 * flag since it is valid only in P2P Group
+				 * Info attribute.
+				 */
+				dev->info.dev_capab &=
+					~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
+				dev->info.dev_capab |=
+					cli->dev_capab &
+					P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
+			}
 			if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) {
 				dev->flags &= ~P2P_DEV_PROBE_REQ_ONLY;
 			}
@@ -573,7 +585,13 @@
 	}
 
 	if (msg->capability) {
-		dev->info.dev_capab = msg->capability[0];
+		/*
+		 * P2P Client Discoverability bit is reserved in all frames
+		 * that use this function, so do not change its value here.
+		 */
+		dev->info.dev_capab &= P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
+		dev->info.dev_capab |= msg->capability[0] &
+			~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
 		dev->info.group_capab = msg->capability[1];
 	}
 
@@ -582,6 +600,7 @@
 		dev->ext_listen_interval =
 			WPA_GET_LE16(msg->ext_listen_timing + 2);
 	}
+
 	if (!probe_req) {
 		dev->info.config_methods = msg->config_methods ?
 			msg->config_methods : msg->wps_config_methods;
@@ -590,7 +609,7 @@
 
 
 /**
- * p2p_add_device - Add peer entries based on scan results
+ * p2p_add_device - Add peer entries based on scan results or P2P frames
  * @p2p: P2P module context from p2p_init()
  * @addr: Source address of Beacon or Probe Response frame (may be either
  *	P2P Device Address or P2P Interface Address)
@@ -598,6 +617,7 @@
  * @freq: Frequency on which the Beacon or Probe Response frame was received
  * @ies: IEs from the Beacon or Probe Response frame
  * @ies_len: Length of ies buffer in octets
+ * @scan_res: Whether this was based on scan results
  * Returns: 0 on success, -1 on failure
  *
  * If the scan result is for a GO, the clients in the group will also be added
@@ -606,7 +626,7 @@
  * Info attributes.
  */
 int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, int level,
-		   const u8 *ies, size_t ies_len)
+		   const u8 *ies, size_t ies_len, int scan_res)
 {
 	struct p2p_device *dev;
 	struct p2p_message msg;
@@ -675,16 +695,18 @@
 		}
 	}
 
-	if (dev->listen_freq && dev->listen_freq != freq) {
+	if (dev->listen_freq && dev->listen_freq != freq && scan_res) {
 		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
 			"P2P: Update Listen frequency based on scan "
 			"results (" MACSTR " %d -> %d MHz (DS param %d)",
 			MAC2STR(dev->info.p2p_device_addr), dev->listen_freq,
 			freq, msg.ds_params ? *msg.ds_params : -1);
 	}
-	dev->listen_freq = freq;
-	if (msg.group_info)
-		dev->oper_freq = freq;
+	if (scan_res) {
+		dev->listen_freq = freq;
+		if (msg.group_info)
+			dev->oper_freq = freq;
+	}
 	dev->info.level = level;
 
 	p2p_copy_wps_info(dev, 0, &msg);
@@ -703,8 +725,10 @@
 			break;
 	}
 
-	p2p_add_group_clients(p2p, p2p_dev_addr, addr, freq, msg.group_info,
-			      msg.group_info_len);
+	if (scan_res) {
+		p2p_add_group_clients(p2p, p2p_dev_addr, addr, freq,
+				      msg.group_info, msg.group_info_len);
+	}
 
 	p2p_parse_free(&msg);
 
@@ -821,6 +845,7 @@
 {
 	int freq = 0;
 	enum p2p_scan_type type;
+	u16 pw_id = DEV_PW_DEFAULT;
 
 	if (p2p->drv_in_listen) {
 		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Driver is still "
@@ -830,30 +855,8 @@
 	}
 	p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
 
-	if (p2p->go_neg_peer) {
-		/*
-		 * Only scan the known listen frequency of the peer
-		 * during GO Negotiation start.
-		 */
-		freq = p2p->go_neg_peer->listen_freq;
-		if (freq <= 0)
-			freq = p2p->go_neg_peer->oper_freq;
-		type = P2P_SCAN_SPECIFIC;
-		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search "
-			"for freq %u (GO Neg)", freq);
-	} else if (p2p->invite_peer) {
-		/*
-		 * Only scan the known listen frequency of the peer
-		 * during Invite start.
-		 */
-		freq = p2p->invite_peer->listen_freq;
-		if (freq <= 0)
-			freq = p2p->invite_peer->oper_freq;
-		type = P2P_SCAN_SPECIFIC;
-		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search "
-			"for freq %u (Invite)", freq);
-	} else if (p2p->find_type == P2P_FIND_PROGRESSIVE &&
-		   (freq = p2p_get_next_prog_freq(p2p)) > 0) {
+	if (p2p->find_type == P2P_FIND_PROGRESSIVE &&
+	    (freq = p2p_get_next_prog_freq(p2p)) > 0) {
 		type = P2P_SCAN_SOCIAL_PLUS_ONE;
 		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search "
 			"(+ freq %u)", freq);
@@ -864,7 +867,7 @@
 
 	if (p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, type, freq,
 			       p2p->num_req_dev_types, p2p->req_dev_types,
-			       p2p->find_dev_id)) {
+			       p2p->find_dev_id, pw_id)) {
 		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
 			"P2P: Scan request failed");
 		p2p_continue_find(p2p);
@@ -1016,12 +1019,14 @@
 	case P2P_FIND_PROGRESSIVE:
 		res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_FULL, 0,
 					 p2p->num_req_dev_types,
-					 p2p->req_dev_types, dev_id);
+					 p2p->req_dev_types, dev_id,
+					 DEV_PW_DEFAULT);
 		break;
 	case P2P_FIND_ONLY_SOCIAL:
 		res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_SOCIAL, 0,
 					 p2p->num_req_dev_types,
-					 p2p->req_dev_types, dev_id);
+					 p2p->req_dev_types, dev_id,
+					 DEV_PW_DEFAULT);
 		break;
 	default:
 		return -1;
@@ -1090,11 +1095,21 @@
 	p2p->go_neg_peer = NULL;
 	p2p->sd_peer = NULL;
 	p2p->invite_peer = NULL;
+	p2p_stop_listen_for_freq(p2p, freq);
+}
+
+
+void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq)
+{
 	if (freq > 0 && p2p->drv_in_listen == freq && p2p->in_listen) {
 		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Skip stop_listen "
 			"since we are on correct channel for response");
 		return;
 	}
+	if (p2p->in_listen) {
+		p2p->in_listen = 0;
+		p2p_clear_timeout(p2p);
+	}
 	if (p2p->drv_in_listen) {
 		/*
 		 * The driver may not deliver callback to p2p_listen_end()
@@ -1217,21 +1232,22 @@
 int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr,
 		enum p2p_wps_method wps_method,
 		int go_intent, const u8 *own_interface_addr,
-		unsigned int force_freq, int persistent_group)
+		unsigned int force_freq, int persistent_group,
+		const u8 *force_ssid, size_t force_ssid_len,
+		int pd_before_go_neg)
 {
 	struct p2p_device *dev;
 
 	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
 		"P2P: Request to start group negotiation - peer=" MACSTR
 		"  GO Intent=%d  Intended Interface Address=" MACSTR
-		" wps_method=%d persistent_group=%d",
+		" wps_method=%d persistent_group=%d pd_before_go_neg=%d",
 		MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr),
-		wps_method, persistent_group);
+		wps_method, persistent_group, pd_before_go_neg);
 
 	if (p2p_prepare_channel(p2p, force_freq) < 0)
 		return -1;
 
-	p2p->ssid_set = 0;
 	dev = p2p_get_device(p2p, peer_addr);
 	if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
 		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
@@ -1264,10 +1280,23 @@
 		 */
 	}
 
+	p2p->ssid_set = 0;
+	if (force_ssid) {
+		wpa_hexdump_ascii(MSG_DEBUG, "P2P: Forced SSID",
+				  force_ssid, force_ssid_len);
+		os_memcpy(p2p->ssid, force_ssid, force_ssid_len);
+		p2p->ssid_len = force_ssid_len;
+		p2p->ssid_set = 1;
+	}
+
 	dev->flags &= ~P2P_DEV_NOT_YET_READY;
 	dev->flags &= ~P2P_DEV_USER_REJECTED;
 	dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE;
 	dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM;
+	if (pd_before_go_neg)
+		dev->flags |= P2P_DEV_PD_BEFORE_GO_NEG;
+	else
+		dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG;
 	dev->connect_reqs = 0;
 	dev->go_neg_req_sent = 0;
 	dev->go_state = UNKNOWN_GO;
@@ -1315,7 +1344,8 @@
 int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr,
 		  enum p2p_wps_method wps_method,
 		  int go_intent, const u8 *own_interface_addr,
-		  unsigned int force_freq, int persistent_group)
+		  unsigned int force_freq, int persistent_group,
+		  const u8 *force_ssid, size_t force_ssid_len)
 {
 	struct p2p_device *dev;
 
@@ -1337,6 +1367,15 @@
 		return -1;
 	}
 
+	p2p->ssid_set = 0;
+	if (force_ssid) {
+		wpa_hexdump_ascii(MSG_DEBUG, "P2P: Forced SSID",
+				  force_ssid, force_ssid_len);
+		os_memcpy(p2p->ssid, force_ssid, force_ssid_len);
+		p2p->ssid_len = force_ssid_len;
+		p2p->ssid_set = 1;
+	}
+
 	dev->flags &= ~P2P_DEV_NOT_YET_READY;
 	dev->flags &= ~P2P_DEV_USER_REJECTED;
 	dev->go_neg_req_sent = 0;
@@ -1821,16 +1860,23 @@
 {
 	struct wpabuf *buf;
 	u8 *len;
+	int pw_id = -1;
 
 	buf = wpabuf_alloc(1000);
 	if (buf == NULL)
 		return NULL;
 
-	p2p_build_wps_ie(p2p, buf, DEV_PW_DEFAULT, 1);
+	if (p2p->go_neg_peer) {
+		/* Advertise immediate availability of WPS credential */
+		pw_id = p2p_wps_method_pw_id(p2p->go_neg_peer->wps_method);
+	}
+
+	p2p_build_wps_ie(p2p, buf, pw_id, 1);
 
 	/* P2P IE */
 	len = p2p_buf_add_ie_hdr(buf);
-	p2p_buf_add_capability(buf, p2p->dev_capab, 0);
+	p2p_buf_add_capability(buf, p2p->dev_capab &
+			       ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0);
 	if (p2p->ext_listen_interval)
 		p2p_buf_add_ext_listen_timing(buf, p2p->ext_listen_period,
 					      p2p->ext_listen_interval);
@@ -1874,9 +1920,9 @@
 }
 
 
-static void p2p_reply_probe(struct p2p_data *p2p, const u8 *addr,
-			    const u8 *dst, const u8 *bssid, const u8 *ie,
-			    size_t ie_len)
+static enum p2p_probe_req_status
+p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
+		const u8 *bssid, const u8 *ie, size_t ie_len)
 {
 	struct ieee802_11_elems elems;
 	struct wpabuf *buf;
@@ -1886,55 +1932,55 @@
 
 	if (!p2p->in_listen || !p2p->drv_in_listen) {
 		/* not in Listen state - ignore Probe Request */
-		return;
+		return P2P_PREQ_NOT_LISTEN;
 	}
 
 	if (ieee802_11_parse_elems((u8 *) ie, ie_len, &elems, 0) ==
 	    ParseFailed) {
 		/* Ignore invalid Probe Request frames */
-		return;
+		return P2P_PREQ_MALFORMED;
 	}
 
 	if (elems.p2p == NULL) {
 		/* not a P2P probe - ignore it */
-		return;
+		return P2P_PREQ_NOT_P2P;
 	}
 
 	if (dst && !is_broadcast_ether_addr(dst) &&
 	    os_memcmp(dst, p2p->cfg->dev_addr, ETH_ALEN) != 0) {
 		/* Not sent to the broadcast address or our P2P Device Address
 		 */
-		return;
+		return P2P_PREQ_NOT_PROCESSED;
 	}
 
 	if (bssid && !is_broadcast_ether_addr(bssid)) {
 		/* Not sent to the Wildcard BSSID */
-		return;
+		return P2P_PREQ_NOT_PROCESSED;
 	}
 
 	if (elems.ssid == NULL || elems.ssid_len != P2P_WILDCARD_SSID_LEN ||
 	    os_memcmp(elems.ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) !=
 	    0) {
 		/* not using P2P Wildcard SSID - ignore */
-		return;
+		return P2P_PREQ_NOT_PROCESSED;
 	}
 
 	if (supp_rates_11b_only(&elems)) {
 		/* Indicates support for 11b rates only */
-		return;
+		return P2P_PREQ_NOT_P2P;
 	}
 
 	os_memset(&msg, 0, sizeof(msg));
 	if (p2p_parse_ies(ie, ie_len, &msg) < 0) {
 		/* Could not parse P2P attributes */
-		return;
+		return P2P_PREQ_NOT_P2P;
 	}
 
 	if (msg.device_id &&
-	    os_memcmp(msg.device_id, p2p->cfg->dev_addr, ETH_ALEN != 0)) {
+	    os_memcmp(msg.device_id, p2p->cfg->dev_addr, ETH_ALEN) != 0) {
 		/* Device ID did not match */
 		p2p_parse_free(&msg);
-		return;
+		return P2P_PREQ_NOT_PROCESSED;
 	}
 
 	/* Check Requested Device Type match */
@@ -1942,12 +1988,14 @@
 	    !p2p_match_dev_type(p2p, msg.wps_attributes)) {
 		/* No match with Requested Device Type */
 		p2p_parse_free(&msg);
-		return;
+		return P2P_PREQ_NOT_PROCESSED;
 	}
 	p2p_parse_free(&msg);
 
-	if (!p2p->cfg->send_probe_resp)
-		return; /* Response generated elsewhere */
+	if (!p2p->cfg->send_probe_resp) {
+		/* Response generated elsewhere */
+		return P2P_PREQ_NOT_PROCESSED;
+	}
 
 	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
 		"P2P: Reply to P2P Probe Request in Listen state");
@@ -1960,12 +2008,12 @@
 	 */
 	ies = p2p_build_probe_resp_ies(p2p);
 	if (ies == NULL)
-		return;
+		return P2P_PREQ_NOT_PROCESSED;
 
 	buf = wpabuf_alloc(200 + wpabuf_len(ies));
 	if (buf == NULL) {
 		wpabuf_free(ies);
-		return;
+		return P2P_PREQ_NOT_PROCESSED;
 	}
 
 	resp = NULL;
@@ -2008,15 +2056,20 @@
 	p2p->cfg->send_probe_resp(p2p->cfg->cb_ctx, buf);
 
 	wpabuf_free(buf);
+
+	return P2P_PREQ_NOT_PROCESSED;
 }
 
 
-int p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
-		     const u8 *bssid, const u8 *ie, size_t ie_len)
+enum p2p_probe_req_status
+p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
+		 const u8 *bssid, const u8 *ie, size_t ie_len)
 {
+	enum p2p_probe_req_status res;
+
 	p2p_add_dev_from_probe_req(p2p, addr, ie, ie_len);
 
-	p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len);
+	res = p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len);
 
 	if ((p2p->state == P2P_CONNECT || p2p->state == P2P_CONNECT_LISTEN) &&
 	    p2p->go_neg_peer &&
@@ -2027,7 +2080,7 @@
 			"P2P: Found GO Negotiation peer - try to start GO "
 			"negotiation from timeout");
 		eloop_register_timeout(0, 0, p2p_go_neg_start, p2p, NULL);
-		return 1;
+		return P2P_PREQ_PROCESSED;
 	}
 
 	if ((p2p->state == P2P_INVITE || p2p->state == P2P_INVITE_LISTEN) &&
@@ -2039,10 +2092,10 @@
 			"P2P: Found Invite peer - try to start Invite from "
 			"timeout");
 		eloop_register_timeout(0, 0, p2p_invite_start, p2p, NULL);
-		return 1;
+		return P2P_PREQ_PROCESSED;
 	}
 
-	return 0;
+	return res;
 }
 
 
@@ -2156,29 +2209,37 @@
 }
 
 
+int p2p_parse_dev_addr_in_p2p_ie(struct wpabuf *p2p_ie, u8 *dev_addr)
+{
+	struct p2p_message msg;
+
+	os_memset(&msg, 0, sizeof(msg));
+	if (p2p_parse_p2p_ie(p2p_ie, &msg))
+		return -1;
+
+	if (msg.p2p_device_addr) {
+		os_memcpy(dev_addr, msg.p2p_device_addr, ETH_ALEN);
+		return 0;
+	} else if (msg.device_id) {
+		os_memcpy(dev_addr, msg.device_id, ETH_ALEN);
+		return 0;
+	}
+	return -1;
+}
+
+
 int p2p_parse_dev_addr(const u8 *ies, size_t ies_len, u8 *dev_addr)
 {
 	struct wpabuf *p2p_ie;
-	struct p2p_message msg;
+	int ret;
 
 	p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len,
 					     P2P_IE_VENDOR_TYPE);
 	if (p2p_ie == NULL)
 		return -1;
-	os_memset(&msg, 0, sizeof(msg));
-	if (p2p_parse_p2p_ie(p2p_ie, &msg)) {
-		wpabuf_free(p2p_ie);
-		return -1;
-	}
-
-	if (msg.p2p_device_addr == NULL) {
-		wpabuf_free(p2p_ie);
-		return -1;
-	}
-
-	os_memcpy(dev_addr, msg.p2p_device_addr, ETH_ALEN);
+	ret = p2p_parse_dev_addr_in_p2p_ie(p2p_ie, dev_addr);
 	wpabuf_free(p2p_ie);
-	return 0;
+	return ret;
 }
 
 
@@ -2256,6 +2317,17 @@
 		p2p->cfg->model_number = os_strdup(cfg->model_number);
 	if (cfg->serial_number)
 		p2p->cfg->serial_number = os_strdup(cfg->serial_number);
+	if (cfg->pref_chan) {
+		p2p->cfg->pref_chan = os_malloc(cfg->num_pref_chan *
+						sizeof(struct p2p_channel));
+		if (p2p->cfg->pref_chan) {
+			os_memcpy(p2p->cfg->pref_chan, cfg->pref_chan,
+				  cfg->num_pref_chan *
+				  sizeof(struct p2p_channel));
+		} else
+			p2p->cfg->num_pref_chan = 0;
+	}
+
 #ifdef ANDROID_P2P
 	/* 100ms listen time is too less to receive the response frames in some scenarios
 	 * increasing min listen time to 200ms.
@@ -2298,6 +2370,7 @@
 	os_free(p2p->cfg->model_name);
 	os_free(p2p->cfg->model_number);
 	os_free(p2p->cfg->serial_number);
+	os_free(p2p->cfg->pref_chan);
 	os_free(p2p->groups);
 	wpabuf_free(p2p->sd_resp);
 	os_free(p2p->after_scan_tx);
@@ -2309,16 +2382,7 @@
 void p2p_flush(struct p2p_data *p2p)
 {
 	struct p2p_device *dev, *prev;
-	p2p_clear_timeout(p2p);
-#ifdef ANDROID_P2P
-	if (p2p->state == P2P_SEARCH)
-		wpa_msg(p2p->cfg->msg_ctx, MSG_INFO,
-						P2P_EVENT_FIND_STOPPED);
-#endif
-	p2p_set_state(p2p, P2P_IDLE);
-	p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
-	p2p->go_neg_peer = NULL;
-	eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
+	p2p_stop_find(p2p);
 	dl_list_for_each_safe(dev, prev, &p2p->devices, struct p2p_device,
 			      list) {
 		dl_list_del(&dev->list);
@@ -2671,17 +2735,7 @@
 int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq,
 			 int level, const u8 *ies, size_t ies_len)
 {
-	p2p_add_device(p2p, bssid, freq, level, ies, ies_len);
-
-	if (p2p->go_neg_peer && p2p->state == P2P_SEARCH &&
-	    os_memcmp(p2p->go_neg_peer->info.p2p_device_addr, bssid, ETH_ALEN)
-	    == 0) {
-		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
-			"P2P: Found GO Negotiation peer - try to start GO "
-			"negotiation");
-		p2p_connect_send(p2p, p2p->go_neg_peer);
-		return 1;
-	}
+	p2p_add_device(p2p, bssid, freq, level, ies, ies_len, 1);
 
 	return 0;
 }
@@ -2706,7 +2760,8 @@
 void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id)
 {
 	u8 *len = p2p_buf_add_ie_hdr(ies);
-	p2p_buf_add_capability(ies, p2p->dev_capab, 0);
+	p2p_buf_add_capability(ies, p2p->dev_capab &
+			       ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0);
 	if (dev_id)
 		p2p_buf_add_device_id(ies, dev_id);
 	if (p2p->cfg->reg_class && p2p->cfg->channel)
@@ -2748,19 +2803,14 @@
 	}
 
 	if (success) {
-#ifndef ANDROID_P2P
-		dev->go_neg_req_sent++;
-#endif
 		if (dev->flags & P2P_DEV_USER_REJECTED) {
 			p2p_set_state(p2p, P2P_IDLE);
 			return;
 		}
-	}
-#ifdef ANDROID_P2P
-	else {
+	} else if (dev->go_neg_req_sent) {
+		/* Cancel the increment from p2p_connect_send() on failure */
 		dev->go_neg_req_sent--;
 	}
-#endif
 
 	if (!success &&
 	    (dev->info.dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY) &&
@@ -2978,6 +3028,18 @@
 				"new one");
 			return 1;
 		}
+		if (p2p->pending_listen_freq) {
+			/*
+			 * Better wait a bit if the driver is unable to start
+			 * offchannel operation for some reason. p2p_search()
+			 * will be started from internal timeout.
+			 */
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Listen "
+				"operation did not seem to start - delay "
+				"search phase to avoid busy loop");
+			p2p_set_timeout(p2p, 0, 100000);
+			return 1;
+		}
 		p2p_search(p2p);
 		return 1;
 	}
@@ -2989,6 +3051,14 @@
 static void p2p_timeout_connect(struct p2p_data *p2p)
 {
 	p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+	if (p2p->go_neg_peer &&
+	    (p2p->go_neg_peer->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Wait for GO "
+			"Negotiation Confirm timed out - assume GO "
+			"Negotiation failed");
+		p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1);
+		return;
+	}
 	p2p_set_state(p2p, P2P_CONNECT_LISTEN);
 	p2p_listen_in_find(p2p);
 }
@@ -3802,6 +3872,28 @@
 }
 
 
+int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan,
+		      const struct p2p_channel *pref_chan)
+{
+	struct p2p_channel *n;
+
+	if (pref_chan) {
+		n = os_malloc(num_pref_chan * sizeof(struct p2p_channel));
+		if (n == NULL)
+			return -1;
+		os_memcpy(n, pref_chan,
+			  num_pref_chan * sizeof(struct p2p_channel));
+	} else
+		n = NULL;
+
+	os_free(p2p->cfg->pref_chan);
+	p2p->cfg->pref_chan = n;
+	p2p->cfg->num_pref_chan = num_pref_chan;
+
+	return 0;
+}
+
+
 int p2p_get_interface_addr(struct p2p_data *p2p, const u8 *dev_addr,
 			   u8 *iface_addr)
 {
diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h
index fe98f5e..7006707 100644
--- a/src/p2p/p2p.h
+++ b/src/p2p/p2p.h
@@ -131,7 +131,6 @@
 enum p2p_scan_type {
 	P2P_SCAN_SOCIAL,
 	P2P_SCAN_FULL,
-	P2P_SCAN_SPECIFIC,
 	P2P_SCAN_SOCIAL_PLUS_ONE
 };
 
@@ -218,6 +217,11 @@
 	P2P_PROV_DISC_REJECTED,
 };
 
+struct p2p_channel {
+	u8 op_class;
+	u8 chan;
+};
+
 /**
  * struct p2p_config - P2P configuration
  *
@@ -265,6 +269,16 @@
 	struct p2p_channels channels;
 
 	/**
+	 * num_pref_chan - Number of pref_chan entries
+	 */
+	unsigned int num_pref_chan;
+
+	/**
+	 * pref_chan - Preferred channels for GO Negotiation
+	 */
+	struct p2p_channel *pref_chan;
+
+	/**
 	 * pri_dev_type - Primary Device Type (see WPS)
 	 */
 	u8 pri_dev_type[8];
@@ -354,14 +368,14 @@
 	 * @num_req_dev_types: Number of requested device types
 	 * @req_dev_types: Array containing requested device types
 	 * @dev_id: Device ID to search for or %NULL to find all devices
+	 * @pw_id: Device Password ID
 	 * Returns: 0 on success, -1 on failure
 	 *
 	 * This callback function is used to request a P2P scan or search
 	 * operation to be completed. Type type argument specifies which type
 	 * of scan is to be done. @P2P_SCAN_SOCIAL indicates that only the
 	 * social channels (1, 6, 11) should be scanned. @P2P_SCAN_FULL
-	 * indicates that all channels are to be scanned. @P2P_SCAN_SPECIFIC
-	 * request a scan of a single channel specified by freq.
+	 * indicates that all channels are to be scanned.
 	 * @P2P_SCAN_SOCIAL_PLUS_ONE request scan of all the social channels
 	 * plus one extra channel specified by freq.
 	 *
@@ -377,7 +391,7 @@
 	 */
 	int (*p2p_scan)(void *ctx, enum p2p_scan_type type, int freq,
 			unsigned int num_req_dev_types,
-			const u8 *req_dev_types, const u8 *dev_id);
+			const u8 *req_dev_types, const u8 *dev_id, u16 pw_id);
 
 	/**
 	 * send_probe_resp - Transmit a Probe Response frame
@@ -861,12 +875,20 @@
  * @persistent_group: Whether to create a persistent group (0 = no, 1 =
  * persistent group without persistent reconnect, 2 = persistent group with
  * persistent reconnect)
+ * @force_ssid: Forced SSID for the group if we become GO or %NULL to generate
+ *	a new SSID
+ * @force_ssid_len: Length of $force_ssid buffer
+ * @pd_before_go_neg: Whether to send Provision Discovery prior to GO
+ *	Negotiation as an interoperability workaround when initiating group
+ *	formation
  * Returns: 0 on success, -1 on failure
  */
 int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr,
 		enum p2p_wps_method wps_method,
 		int go_intent, const u8 *own_interface_addr,
-		unsigned int force_freq, int persistent_group);
+		unsigned int force_freq, int persistent_group,
+		const u8 *force_ssid, size_t force_ssid_len,
+		int pd_before_go_neg);
 
 /**
  * p2p_authorize - Authorize P2P group formation (GO negotiation)
@@ -879,6 +901,9 @@
  * @persistent_group: Whether to create a persistent group (0 = no, 1 =
  * persistent group without persistent reconnect, 2 = persistent group with
  * persistent reconnect)
+ * @force_ssid: Forced SSID for the group if we become GO or %NULL to generate
+ *	a new SSID
+ * @force_ssid_len: Length of $force_ssid buffer
  * Returns: 0 on success, -1 on failure
  *
  * This is like p2p_connect(), but the actual group negotiation is not
@@ -887,7 +912,8 @@
 int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr,
 		  enum p2p_wps_method wps_method,
 		  int go_intent, const u8 *own_interface_addr,
-		  unsigned int force_freq, int persistent_group);
+		  unsigned int force_freq, int persistent_group,
+		  const u8 *force_ssid, size_t force_ssid_len);
 
 /**
  * p2p_reject - Reject peer device (explicitly block connection attempts)
@@ -1060,17 +1086,34 @@
 /**
  * p2p_clear_provisioning_info - Clear any stored provisioning info
  * @p2p: P2P module context from p2p_init()
- * @iface_addr: Peer P2P Interface Address
+ * @iface_addr: Peer P2P Device Address
  *
  * This function is used to clear stored WPS provisioning info for the given
  * peer.
  */
-void p2p_clear_provisioning_info(struct p2p_data *p2p, const u8 *iface_addr);
+void p2p_clear_provisioning_info(struct p2p_data *p2p, const u8 *addr);
 
 
 /* Event notifications from lower layer driver operations */
 
 /**
+ * enum p2p_probe_req_status
+ *
+ * @P2P_PREQ_MALFORMED: frame was not well-formed
+ * @P2P_PREQ_NOT_LISTEN: device isn't in listen state, frame ignored
+ * @P2P_PREQ_NOT_P2P: frame was not a P2P probe request
+ * @P2P_PREQ_P2P_NOT_PROCESSED: frame was P2P but wasn't processed
+ * @P2P_PREQ_P2P_PROCESSED: frame has been processed by P2P
+ */
+enum p2p_probe_req_status {
+	P2P_PREQ_MALFORMED,
+	P2P_PREQ_NOT_LISTEN,
+	P2P_PREQ_NOT_P2P,
+	P2P_PREQ_NOT_PROCESSED,
+	P2P_PREQ_PROCESSED
+};
+
+/**
  * p2p_probe_req_rx - Report reception of a Probe Request frame
  * @p2p: P2P module context from p2p_init()
  * @addr: Source MAC address
@@ -1078,10 +1121,11 @@
  * @bssid: BSSID if available or %NULL
  * @ie: Information elements from the Probe Request frame body
  * @ie_len: Length of ie buffer in octets
- * Returns: 0 to indicate the frame was not processed or 1 if it was
+ * Returns: value indicating the type and status of the probe request
  */
-int p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
-		     const u8 *bssid, const u8 *ie, size_t ie_len);
+enum p2p_probe_req_status
+p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
+		 const u8 *bssid, const u8 *ie, size_t ie_len);
 
 /**
  * p2p_rx_action - Report received Action frame
@@ -1220,6 +1264,16 @@
 	unsigned int max_clients;
 
 	/**
+	 * ssid - Group SSID
+	 */
+	u8 ssid[32];
+
+	/**
+	 * ssid_len - Length of SSID
+	 */
+	size_t ssid_len;
+
+	/**
 	 * cb_ctx - Context to use with callback functions
 	 */
 	void *cb_ctx;
@@ -1371,6 +1425,15 @@
 int p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, char *end);
 
 /**
+ * p2p_parse_dev_addr_in_p2p_ie - Parse P2P Device Address from a concatenated
+ * P2P IE
+ * @p2p_ie: P2P IE
+ * @dev_addr: Buffer for returning P2P Device Address
+ * Returns: 0 on success or -1 if P2P Device Address could not be parsed
+ */
+int p2p_parse_dev_addr_in_p2p_ie(struct wpabuf *p2p_ie, u8 *dev_addr);
+
+/**
  * p2p_parse_dev_addr - Parse P2P Device Address from P2P IE(s)
  * @ies: Information elements from scan results
  * @ies_len: ies buffer length in octets
@@ -1608,6 +1671,16 @@
 			 int cfg_op_channel);
 
 /**
+ * p2p_set_pref_chan - Set P2P preferred channel list
+ * @p2p: P2P module context from p2p_init()
+ * @num_pref_chan: Number of entries in pref_chan list
+ * @pref_chan: Preferred channels or %NULL to remove preferences
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan,
+		      const struct p2p_channel *pref_chan);
+
+/**
  * p2p_in_progress - Check whether a P2P operation is progress
  * @p2p: P2P module context from p2p_init()
  * Returns: 0 if P2P module is idle or 1 if an operation is in progress
diff --git a/src/p2p/p2p_build.c b/src/p2p/p2p_build.c
index 2106964..def422d 100644
--- a/src/p2p/p2p_build.c
+++ b/src/p2p/p2p_build.c
@@ -351,7 +351,7 @@
 }
 
 
-void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, u16 pw_id,
+void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id,
 		      int all_attr)
 {
 	u8 *len;
@@ -369,11 +369,14 @@
 		wpabuf_put_u8(buf, WPS_STATE_NOT_CONFIGURED);
 	}
 
-	/* Device Password ID */
-	wpabuf_put_be16(buf, ATTR_DEV_PASSWORD_ID);
-	wpabuf_put_be16(buf, 2);
-	wpa_printf(MSG_DEBUG, "P2P: WPS IE Device Password ID: %d", pw_id);
-	wpabuf_put_be16(buf, pw_id);
+	if (pw_id >= 0) {
+		/* Device Password ID */
+		wpabuf_put_be16(buf, ATTR_DEV_PASSWORD_ID);
+		wpabuf_put_be16(buf, 2);
+		wpa_printf(MSG_DEBUG, "P2P: WPS IE Device Password ID: %d",
+			   pw_id);
+		wpabuf_put_be16(buf, pw_id);
+	}
 
 	if (all_attr) {
 		wpabuf_put_be16(buf, ATTR_RESPONSE_TYPE);
diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c
index 6e818c2..248b2a0 100644
--- a/src/p2p/p2p_go_neg.c
+++ b/src/p2p/p2p_go_neg.c
@@ -98,7 +98,7 @@
 }
 
 
-static u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method)
+u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method)
 {
 	switch (wps_method) {
 	case WPS_PIN_DISPLAY:
@@ -155,7 +155,9 @@
 		group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
 	if (p2p->cfg->p2p_intra_bss)
 		group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
-	p2p_buf_add_capability(buf, p2p->dev_capab, group_capab);
+	p2p_buf_add_capability(buf, p2p->dev_capab &
+			       ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY,
+			       group_capab);
 	p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) |
 			      p2p->next_tie_breaker);
 	p2p->next_tie_breaker = !p2p->next_tie_breaker;
@@ -184,6 +186,23 @@
 	struct wpabuf *req;
 	int freq;
 
+	if (dev->flags & P2P_DEV_PD_BEFORE_GO_NEG) {
+		u16 config_method;
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Use PD-before-GO-Neg workaround for " MACSTR,
+			MAC2STR(dev->info.p2p_device_addr));
+		if (dev->wps_method == WPS_PIN_DISPLAY)
+			config_method = WPS_CONFIG_KEYPAD;
+		else if (dev->wps_method == WPS_PIN_KEYPAD)
+			config_method = WPS_CONFIG_DISPLAY;
+		else if (dev->wps_method == WPS_PBC)
+			config_method = WPS_CONFIG_PUSHBUTTON;
+		else
+			return -1;
+		return p2p_prov_disc_req(p2p, dev->info.p2p_device_addr,
+					 config_method, 0, 0);
+	}
+
 	freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
 	if (freq <= 0) {
 		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
@@ -203,9 +222,6 @@
 	p2p->go_neg_peer = dev;
 	dev->flags |= P2P_DEV_WAIT_GO_NEG_RESPONSE;
 	dev->connect_reqs++;
-#ifdef ANDROID_P2P
-	dev->go_neg_req_sent++;
-#endif
 	if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr,
 			    p2p->cfg->dev_addr, dev->info.p2p_device_addr,
 			    wpabuf_head(req), wpabuf_len(req), 200) < 0) {
@@ -213,7 +229,8 @@
 			"P2P: Failed to send Action frame");
 		/* Use P2P find to recover and retry */
 		p2p_set_timeout(p2p, 0, 0);
-	}
+	} else
+		dev->go_neg_req_sent++;
 
 	wpabuf_free(req);
 
@@ -253,7 +270,9 @@
 		if (p2p->cfg->p2p_intra_bss)
 			group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
 	}
-	p2p_buf_add_capability(buf, p2p->dev_capab, group_capab);
+	p2p_buf_add_capability(buf, p2p->dev_capab &
+			       ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY,
+			       group_capab);
 	p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | tie_breaker);
 	p2p_buf_add_config_timeout(buf, 100, 20);
 	if (peer && peer->go_state == REMOTE_GO) {
@@ -299,6 +318,7 @@
 	struct p2p_reg_class *cl;
 	int freq;
 	u8 op_reg_class, op_channel;
+	unsigned int i;
 
 	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Selected operating "
 		"channel (reg_class %u channel %u) not acceptable to the "
@@ -331,6 +351,21 @@
 		return;
 	}
 
+	/* Select channel with highest preference if the peer supports it */
+	for (i = 0; p2p->cfg->pref_chan && i < p2p->cfg->num_pref_chan; i++) {
+		if (p2p_channels_includes(intersection,
+					  p2p->cfg->pref_chan[i].op_class,
+					  p2p->cfg->pref_chan[i].chan)) {
+			p2p->op_reg_class = p2p->cfg->pref_chan[i].op_class;
+			p2p->op_channel = p2p->cfg->pref_chan[i].chan;
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Pick "
+				"highest preferred chnnel (op_class %u "
+				"channel %u) from intersection",
+				p2p->op_reg_class, p2p->op_channel);
+			return;
+		}
+	}
+
 	/*
 	 * Fall back to whatever is included in the channel intersection since
 	 * no better options seems to be available.
@@ -641,6 +676,17 @@
 	if (status == P2P_SC_SUCCESS) {
 		p2p->pending_action_state = P2P_PENDING_GO_NEG_RESPONSE;
 		dev->flags |= P2P_DEV_WAIT_GO_NEG_CONFIRM;
+		if (os_memcmp(sa, p2p->cfg->dev_addr, ETH_ALEN) < 0) {
+			/*
+			 * Peer has smaller address, so the GO Negotiation
+			 * Response from us is expected to complete
+			 * negotiation. Ignore a GO Negotiation Response from
+			 * the peer if it happens to be received after this
+			 * point due to a race condition in GO Negotiation
+			 * Request transmission and processing.
+			 */
+			dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE;
+		}
 	} else
 		p2p->pending_action_state =
 			P2P_PENDING_GO_NEG_RESPONSE_FAILURE;
@@ -688,7 +734,9 @@
 		if (p2p->cfg->p2p_intra_bss)
 			group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
 	}
-	p2p_buf_add_capability(buf, p2p->dev_capab, group_capab);
+	p2p_buf_add_capability(buf, p2p->dev_capab &
+			       ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY,
+			       group_capab);
 	if (go || resp_chan == NULL)
 		p2p_buf_add_operating_channel(buf, p2p->cfg->country,
 					      p2p->op_reg_class,
@@ -992,7 +1040,7 @@
 	else
 		freq = dev->listen_freq;
 	if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, sa,
-			    wpabuf_head(conf), wpabuf_len(conf), 200) < 0) {
+			    wpabuf_head(conf), wpabuf_len(conf), 0) < 0) {
 		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
 			"P2P: Failed to send Action frame");
 		p2p_go_neg_failed(p2p, dev, -1);
diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c
index fafd135..8d4a3cb 100644
--- a/src/p2p/p2p_group.c
+++ b/src/p2p/p2p_group.c
@@ -135,11 +135,10 @@
 static void p2p_group_add_common_ies(struct p2p_group *group,
 				     struct wpabuf *ie)
 {
-	u8 dev_capab = 0, group_capab = 0;
+	u8 dev_capab = group->p2p->dev_capab, group_capab = 0;
 
 	/* P2P Capability */
-	dev_capab |= P2P_DEV_CAPAB_SERVICE_DISCOVERY;
-	dev_capab |= P2P_DEV_CAPAB_INVITATION_PROCEDURE;
+	dev_capab &= ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
 	group_capab |= P2P_GROUP_CAPAB_GROUP_OWNER;
 	if (group->cfg->persistent_group) {
 		group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
@@ -731,3 +730,15 @@
 
 	return 0;
 }
+
+
+int p2p_group_is_group_id_match(struct p2p_group *group, const u8 *group_id,
+				size_t group_id_len)
+{
+	if (group_id_len != ETH_ALEN + group->cfg->ssid_len)
+		return 0;
+	if (os_memcmp(group_id, group->p2p->cfg->dev_addr, ETH_ALEN) != 0)
+		return 0;
+	return os_memcmp(group_id + ETH_ALEN, group->cfg->ssid,
+			 group->cfg->ssid_len) == 0;
+}
diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h
index 4ccd38d..5e21249 100644
--- a/src/p2p/p2p_i.h
+++ b/src/p2p/p2p_i.h
@@ -90,6 +90,7 @@
 #define P2P_DEV_PD_FOR_JOIN BIT(14)
 #define P2P_DEV_REPORTED_ONCE BIT(15)
 #define P2P_DEV_PREFER_PERSISTENT_RECONN BIT(16)
+#define P2P_DEV_PD_BEFORE_GO_NEG BIT(17)
 	unsigned int flags;
 
 	int status; /* enum p2p_status_code */
@@ -557,6 +558,8 @@
 u8 p2p_group_presence_req(struct p2p_group *group,
 			  const u8 *client_interface_addr,
 			  const u8 *noa, size_t noa_len);
+int p2p_group_is_group_id_match(struct p2p_group *group, const u8 *group_id,
+				size_t group_id_len);
 
 
 void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token);
@@ -588,7 +591,7 @@
 void p2p_buf_add_ext_listen_timing(struct wpabuf *buf, u16 period,
 				   u16 interval);
 void p2p_buf_add_p2p_interface(struct wpabuf *buf, struct p2p_data *p2p);
-void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, u16 pw_id,
+void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id,
 		      int all_attr);
 
 /* p2p_sd.c */
@@ -616,6 +619,7 @@
 void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa,
 			     const u8 *data, size_t len);
 int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev);
+u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method);
 
 /* p2p_pd.c */
 void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
@@ -660,7 +664,7 @@
 void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr,
 		      struct p2p_device *dev, struct p2p_message *msg);
 int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, int level,
-		   const u8 *ies, size_t ies_len);
+		   const u8 *ies, size_t ies_len, int scan_res);
 struct p2p_device * p2p_get_device(struct p2p_data *p2p, const u8 *addr);
 struct p2p_device * p2p_get_device_interface(struct p2p_data *p2p,
 					     const u8 *addr);
@@ -675,5 +679,6 @@
 int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
 		    const u8 *src, const u8 *bssid, const u8 *buf,
 		    size_t len, unsigned int wait_time);
+void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq);
 
 #endif /* P2P_I_H */
diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c
index 417f1e7..5925549 100644
--- a/src/p2p/p2p_invitation.c
+++ b/src/p2p/p2p_invitation.c
@@ -121,7 +121,8 @@
 			"P2P: Invitation Request from unknown peer "
 			MACSTR, MAC2STR(sa));
 
-		if (p2p_add_device(p2p, sa, rx_freq, 0, data + 1, len - 1)) {
+		if (p2p_add_device(p2p, sa, rx_freq, 0, data + 1, len - 1, 0))
+		{
 			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
 				"P2P: Invitation Request add device failed "
 				MACSTR, MAC2STR(sa));
@@ -344,6 +345,8 @@
 	req = p2p_build_invitation_req(p2p, dev, go_dev_addr);
 	if (req == NULL)
 		return -1;
+	if (p2p->state != P2P_IDLE)
+		p2p_stop_listen_for_freq(p2p, freq);
 	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
 		"P2P: Sending Invitation Request");
 	p2p_set_state(p2p, P2P_INVITE);
diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c
index 55a3b90..38a9dd8 100644
--- a/src/p2p/p2p_pd.c
+++ b/src/p2p/p2p_pd.c
@@ -54,7 +54,8 @@
 	p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_REQ, dialog_token);
 
 	len = p2p_buf_add_ie_hdr(buf);
-	p2p_buf_add_capability(buf, p2p->dev_capab, 0);
+	p2p_buf_add_capability(buf, p2p->dev_capab &
+			       ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0);
 	p2p_buf_add_device_info(buf, p2p, NULL);
 	if (go) {
 		p2p_buf_add_group_id(buf, go->info.p2p_device_addr,
@@ -110,7 +111,8 @@
 		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
 			"P2P: Provision Discovery Request from "
 			"unknown peer " MACSTR, MAC2STR(sa));
-		if (p2p_add_device(p2p, sa, rx_freq, 0, data + 1, len - 1)) {
+		if (p2p_add_device(p2p, sa, rx_freq, 0, data + 1, len - 1, 0))
+		{
 			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
 			        "P2P: Provision Discovery Request add device "
 				"failed " MACSTR, MAC2STR(sa));
@@ -125,6 +127,21 @@
 		goto out;
 	}
 
+	if (msg.group_id) {
+		size_t i;
+		for (i = 0; i < p2p->num_groups; i++) {
+			if (p2p_group_is_group_id_match(p2p->groups[i],
+							msg.group_id,
+							msg.group_id_len))
+				break;
+		}
+		if (i == p2p->num_groups) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: PD "
+				"request for unknown P2P Group ID - reject");
+			goto out;
+		}
+	}
+
 	if (dev)
 		dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
 				P2P_DEV_PD_PEER_KEYPAD);
@@ -198,6 +215,7 @@
 	struct p2p_message msg;
 	struct p2p_device *dev;
 	u16 report_config_methods = 0;
+	int success = 0;
 
 	if (p2p_parse(data, len, &msg))
 		return;
@@ -266,11 +284,21 @@
 	dev->wps_prov_info = msg.wps_config_methods;
 
 	p2p_parse_free(&msg);
+	success = 1;
 
 out:
 	dev->req_config_methods = 0;
 	p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
-	if (p2p->cfg->prov_disc_resp)
+	if (dev->flags & P2P_DEV_PD_BEFORE_GO_NEG) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Start GO Neg after the PD-before-GO-Neg "
+			"workaround with " MACSTR,
+			MAC2STR(dev->info.p2p_device_addr));
+		dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG;
+		p2p_connect_send(p2p, dev);
+		return;
+	}
+	if (success && p2p->cfg->prov_disc_resp)
 		p2p->cfg->prov_disc_resp(p2p->cfg->cb_ctx, sa,
 					 report_config_methods);
 }
@@ -316,6 +344,8 @@
 	if (req == NULL)
 		return -1;
 
+	if (p2p->state != P2P_IDLE)
+		p2p_stop_listen_for_freq(p2p, freq);
 	p2p->pending_action_state = P2P_PENDING_PD;
 	if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr,
 			    p2p->cfg->dev_addr, dev->info.p2p_device_addr,
diff --git a/src/p2p/p2p_sd.c b/src/p2p/p2p_sd.c
index 37b3f7b..0509767 100644
--- a/src/p2p/p2p_sd.c
+++ b/src/p2p/p2p_sd.c
@@ -364,9 +364,14 @@
 				"previous SD response");
 			wpabuf_free(p2p->sd_resp);
 		}
+		p2p->sd_resp = wpabuf_dup(resp_tlvs);
+		if (p2p->sd_resp == NULL) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_ERROR, "P2P: Failed to "
+				"allocate SD response fragmentation area");
+			return;
+		}
 		os_memcpy(p2p->sd_resp_addr, dst, ETH_ALEN);
 		p2p->sd_resp_dialog_token = dialog_token;
-		p2p->sd_resp = wpabuf_dup(resp_tlvs);
 		p2p->sd_resp_pos = 0;
 		p2p->sd_frag_id = 0;
 		resp = p2p_build_sd_response(dialog_token, WLAN_STATUS_SUCCESS,
@@ -404,9 +409,18 @@
 	u16 slen;
 	u16 update_indic;
 
+#ifdef ANDROID_P2P
+	if (p2p->state != P2P_SD_DURING_FIND) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: #### Not ignoring unexpected GAS Initial Response from "
+			MACSTR " state %d", MAC2STR(sa), p2p->state);
+	}
+	if (p2p->sd_peer == NULL ||
+#else
 	if (p2p->state != P2P_SD_DURING_FIND || p2p->sd_peer == NULL ||
+#endif
 	    os_memcmp(sa, p2p->sd_peer->info.p2p_device_addr, ETH_ALEN) != 0) {
-		wpa_msg(p2p->cfg->msg_ctx, MSG_ERROR,
+		wpa_msg(p2p->cfg->msg_ctx, MSG_INFO,
 			"P2P: Ignore unexpected GAS Initial Response from "
 			MACSTR, MAC2STR(sa));
 		return;
@@ -645,9 +659,18 @@
 
 	wpa_hexdump(MSG_DEBUG, "P2P: RX GAS Comeback Response", data, len);
 
-	if (p2p->state != P2P_SD_DURING_FIND || p2p->sd_peer == NULL ||
-	    os_memcmp(sa, p2p->sd_peer->info.p2p_device_addr, ETH_ALEN) != 0) {
+#ifdef ANDROID_P2P
+	if (p2p->state != P2P_SD_DURING_FIND) {
 		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: #### Not ignoring unexpected GAS Comeback Response from "
+			MACSTR " state %d", MAC2STR(sa), p2p->state);
+	}
+	if (p2p->sd_peer == NULL ||
+#else
+	if (p2p->state != P2P_SD_DURING_FIND || p2p->sd_peer == NULL ||
+#endif
+	    os_memcmp(sa, p2p->sd_peer->info.p2p_device_addr, ETH_ALEN) != 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_INFO,
 			"P2P: Ignore unexpected GAS Comeback Response from "
 			MACSTR, MAC2STR(sa));
 		return;
@@ -842,7 +865,7 @@
 {
 	struct p2p_sd_query *q;
 #ifdef ANDROID_P2P
-	/* Currently, supplicant doesn't support more than one pending broadcast SD request. 
+	/* Currently, supplicant doesn't support more than one pending broadcast SD request.
 	 * So reject if application is registering another one before cancelling the existing one.
 	 */
 	for (q = p2p->sd_queries; q; q = q->next) {
@@ -872,7 +895,7 @@
 
 	q->next = p2p->sd_queries;
 	p2p->sd_queries = q;
-	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Added SD Query %p for_all_peers %d", q, q->for_all_peers);
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Added SD Query %p", q);
 
 	if (dst == NULL) {
 		struct p2p_device *dev;
@@ -893,24 +916,10 @@
 int p2p_sd_cancel_request(struct p2p_data *p2p, void *req)
 {
 	if (p2p_unlink_sd_query(p2p, req)) {
-#ifdef ANDROID_P2P
-	struct p2p_device *dev;
-	struct p2p_sd_query *q = (struct p2p_sd_query *)req;
-#endif
 		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
 			"P2P: Cancel pending SD query %p", req);
 #ifdef ANDROID_P2P
-		/* If the request is a bcast query, then clear the
-		 * P2P_DEV_SD_INFO flag so that when new sd query is registered,
-		 * we will send the SD request frames to peer devices.
-		 */
-		if(q->for_all_peers) {
-			p2p->sd_dev_list = NULL;
-			dl_list_for_each(dev, &p2p->devices,
-							struct p2p_device, list) {
-				dev->flags &= ~P2P_DEV_SD_INFO;
-			}
-		}
+		p2p->sd_dev_list = NULL;
 #endif
 		p2p_free_sd_query(req);
 		return 0;
diff --git a/src/radius/radius.c b/src/radius/radius.c
index 0dd6b12..2b9cbca 100644
--- a/src/radius/radius.c
+++ b/src/radius/radius.c
@@ -1,6 +1,6 @@
 /*
  * RADIUS message processing
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2009, 2011-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -147,6 +147,12 @@
 	case RADIUS_CODE_STATUS_SERVER: return "Status-Server";
 	case RADIUS_CODE_STATUS_CLIENT: return "Status-Client";
 	case RADIUS_CODE_RESERVED: return "Reserved";
+	case RADIUS_CODE_DISCONNECT_REQUEST: return "Disconnect-Request";
+	case RADIUS_CODE_DISCONNECT_ACK: return "Disconnect-ACK";
+	case RADIUS_CODE_DISCONNECT_NAK: return "Disconnect-NAK";
+	case RADIUS_CODE_COA_REQUEST: return "CoA-Request";
+	case RADIUS_CODE_COA_ACK: return "CoA-ACK";
+	case RADIUS_CODE_COA_NAK: return "CoA-NAK";
 	default: return "?Unknown?";
 	}
 }
@@ -222,9 +228,10 @@
 	  RADIUS_ATTR_HEXDUMP },
 	{ RADIUS_ATTR_ACCT_INTERIM_INTERVAL, "Acct-Interim-Interval",
 	  RADIUS_ATTR_INT32 },
-	{ RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, "Chargable-User-Identity",
+	{ RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, "Chargeable-User-Identity",
 	  RADIUS_ATTR_TEXT },
 	{ RADIUS_ATTR_NAS_IPV6_ADDRESS, "NAS-IPv6-Address", RADIUS_ATTR_IPV6 },
+	{ RADIUS_ATTR_ERROR_CAUSE, "Error-Cause", RADIUS_ATTR_INT32 }
 };
 #define RADIUS_ATTRS (sizeof(radius_attrs) / sizeof(radius_attrs[0]))
 
@@ -406,6 +413,45 @@
 }
 
 
+int radius_msg_finish_das_resp(struct radius_msg *msg, const u8 *secret,
+			       size_t secret_len,
+			       const struct radius_hdr *req_hdr)
+{
+	const u8 *addr[2];
+	size_t len[2];
+	u8 auth[MD5_MAC_LEN];
+	struct radius_attr_hdr *attr;
+
+	os_memset(auth, 0, MD5_MAC_LEN);
+	attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
+				   auth, MD5_MAC_LEN);
+	if (attr == NULL) {
+		wpa_printf(MSG_WARNING, "Could not add Message-Authenticator");
+		return -1;
+	}
+
+	msg->hdr->length = htons(wpabuf_len(msg->buf));
+	os_memcpy(msg->hdr->authenticator, req_hdr->authenticator, 16);
+	hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
+		 wpabuf_len(msg->buf), (u8 *) (attr + 1));
+
+	/* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
+	addr[0] = wpabuf_head_u8(msg->buf);
+	len[0] = wpabuf_len(msg->buf);
+	addr[1] = secret;
+	len[1] = secret_len;
+	if (md5_vector(2, addr, len, msg->hdr->authenticator) < 0)
+		return -1;
+
+	if (wpabuf_len(msg->buf) > 0xffff) {
+		wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)",
+			   (unsigned long) wpabuf_len(msg->buf));
+		return -1;
+	}
+	return 0;
+}
+
+
 void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret,
 			    size_t secret_len)
 {
@@ -427,6 +473,88 @@
 }
 
 
+int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret,
+			       size_t secret_len)
+{
+	const u8 *addr[4];
+	size_t len[4];
+	u8 zero[MD5_MAC_LEN];
+	u8 hash[MD5_MAC_LEN];
+
+	os_memset(zero, 0, sizeof(zero));
+	addr[0] = (u8 *) msg->hdr;
+	len[0] = sizeof(struct radius_hdr) - MD5_MAC_LEN;
+	addr[1] = zero;
+	len[1] = MD5_MAC_LEN;
+	addr[2] = (u8 *) (msg->hdr + 1);
+	len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr);
+	addr[3] = secret;
+	len[3] = secret_len;
+	md5_vector(4, addr, len, hash);
+	return os_memcmp(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0;
+}
+
+
+int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret,
+			      size_t secret_len)
+{
+	const u8 *addr[4];
+	size_t len[4];
+	u8 zero[MD5_MAC_LEN];
+	u8 hash[MD5_MAC_LEN];
+	u8 auth[MD5_MAC_LEN], orig[MD5_MAC_LEN];
+	u8 orig_authenticator[16];
+
+	struct radius_attr_hdr *attr = NULL, *tmp;
+	size_t i;
+
+	os_memset(zero, 0, sizeof(zero));
+	addr[0] = (u8 *) msg->hdr;
+	len[0] = sizeof(struct radius_hdr) - MD5_MAC_LEN;
+	addr[1] = zero;
+	len[1] = MD5_MAC_LEN;
+	addr[2] = (u8 *) (msg->hdr + 1);
+	len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr);
+	addr[3] = secret;
+	len[3] = secret_len;
+	md5_vector(4, addr, len, hash);
+	if (os_memcmp(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0)
+		return 1;
+
+	for (i = 0; i < msg->attr_used; i++) {
+		tmp = radius_get_attr_hdr(msg, i);
+		if (tmp->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) {
+			if (attr != NULL) {
+				wpa_printf(MSG_WARNING, "Multiple "
+					   "Message-Authenticator attributes "
+					   "in RADIUS message");
+				return 1;
+			}
+			attr = tmp;
+		}
+	}
+
+	if (attr == NULL) {
+		/* Message-Authenticator is MAY; not required */
+		return 0;
+	}
+
+	os_memcpy(orig, attr + 1, MD5_MAC_LEN);
+	os_memset(attr + 1, 0, MD5_MAC_LEN);
+	os_memcpy(orig_authenticator, msg->hdr->authenticator,
+		  sizeof(orig_authenticator));
+	os_memset(msg->hdr->authenticator, 0,
+		  sizeof(msg->hdr->authenticator));
+	hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
+		 wpabuf_len(msg->buf), auth);
+	os_memcpy(attr + 1, orig, MD5_MAC_LEN);
+	os_memcpy(msg->hdr->authenticator, orig_authenticator,
+		  sizeof(orig_authenticator));
+
+	return os_memcmp(orig, auth, MD5_MAC_LEN) != 0;
+}
+
+
 static int radius_msg_add_attr_to_array(struct radius_msg *msg,
 					struct radius_attr_hdr *attr)
 {
@@ -1424,3 +1552,24 @@
 
 	return 0;
 }
+
+
+u8 radius_msg_find_unlisted_attr(struct radius_msg *msg, u8 *attrs)
+{
+	size_t i, j;
+	struct radius_attr_hdr *attr;
+
+	for (i = 0; i < msg->attr_used; i++) {
+		attr = radius_get_attr_hdr(msg, i);
+
+		for (j = 0; attrs[j]; j++) {
+			if (attr->type == attrs[j])
+				break;
+		}
+
+		if (attrs[j] == 0)
+			return attr->type; /* unlisted attr */
+	}
+
+	return 0;
+}
diff --git a/src/radius/radius.h b/src/radius/radius.h
index 44123bd..2d059df 100644
--- a/src/radius/radius.h
+++ b/src/radius/radius.h
@@ -1,6 +1,6 @@
 /*
  * RADIUS message processing
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2009, 2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -31,6 +31,12 @@
        RADIUS_CODE_ACCESS_CHALLENGE = 11,
        RADIUS_CODE_STATUS_SERVER = 12,
        RADIUS_CODE_STATUS_CLIENT = 13,
+       RADIUS_CODE_DISCONNECT_REQUEST = 40,
+       RADIUS_CODE_DISCONNECT_ACK = 41,
+       RADIUS_CODE_DISCONNECT_NAK = 42,
+       RADIUS_CODE_COA_REQUEST = 43,
+       RADIUS_CODE_COA_ACK = 44,
+       RADIUS_CODE_COA_NAK = 45,
        RADIUS_CODE_RESERVED = 255
 };
 
@@ -83,7 +89,8 @@
        RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID = 81,
        RADIUS_ATTR_ACCT_INTERIM_INTERVAL = 85,
        RADIUS_ATTR_CHARGEABLE_USER_IDENTITY = 89,
-       RADIUS_ATTR_NAS_IPV6_ADDRESS = 95
+       RADIUS_ATTR_NAS_IPV6_ADDRESS = 95,
+       RADIUS_ATTR_ERROR_CAUSE = 101
 };
 
 
@@ -192,8 +199,15 @@
 		      size_t secret_len);
 int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret,
 			  size_t secret_len, const u8 *req_authenticator);
+int radius_msg_finish_das_resp(struct radius_msg *msg, const u8 *secret,
+			       size_t secret_len,
+			       const struct radius_hdr *req_hdr);
 void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret,
 			    size_t secret_len);
+int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret,
+			       size_t secret_len);
+int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret,
+			       size_t secret_len);
 struct radius_attr_hdr * radius_msg_add_attr(struct radius_msg *msg, u8 type,
 					     const u8 *data, size_t data_len);
 struct radius_msg * radius_msg_parse(const u8 *data, size_t len);
@@ -268,4 +282,6 @@
 int radius_copy_class(struct radius_class_data *dst,
 		      const struct radius_class_data *src);
 
+u8 radius_msg_find_unlisted_attr(struct radius_msg *msg, u8 *attrs);
+
 #endif /* RADIUS_H */
diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c
index bcd471b..1ee7131 100644
--- a/src/radius/radius_client.c
+++ b/src/radius/radius_client.c
@@ -505,7 +505,7 @@
 			       NULL);
 	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
 		       HOSTAPD_LEVEL_DEBUG, "Next RADIUS client retransmit in"
-		       " %ld seconds\n", (long int) (first - now.sec));
+		       " %ld seconds", (long int) (first - now.sec));
 }
 
 
@@ -678,7 +678,7 @@
 	radius_client_list_add(radius, msg, msg_type, shared_secret,
 			       shared_secret_len, addr);
 
-	return res;
+	return 0;
 }
 
 
diff --git a/src/radius/radius_das.c b/src/radius/radius_das.c
new file mode 100644
index 0000000..8ecfffc
--- /dev/null
+++ b/src/radius/radius_das.c
@@ -0,0 +1,356 @@
+/*
+ * RADIUS Dynamic Authorization Server (DAS) (RFC 5176)
+ * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <net/if.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/ip_addr.h"
+#include "radius.h"
+#include "radius_das.h"
+
+
+extern int wpa_debug_level;
+
+
+struct radius_das_data {
+	int sock;
+	u8 *shared_secret;
+	size_t shared_secret_len;
+	struct hostapd_ip_addr client_addr;
+	unsigned int time_window;
+	int require_event_timestamp;
+	void *ctx;
+	enum radius_das_res (*disconnect)(void *ctx,
+					  struct radius_das_attrs *attr);
+};
+
+
+static struct radius_msg * radius_das_disconnect(struct radius_das_data *das,
+						 struct radius_msg *msg,
+						 const char *abuf,
+						 int from_port)
+{
+	struct radius_hdr *hdr;
+	struct radius_msg *reply;
+	u8 allowed[] = {
+		RADIUS_ATTR_USER_NAME,
+		RADIUS_ATTR_CALLING_STATION_ID,
+		RADIUS_ATTR_ACCT_SESSION_ID,
+		RADIUS_ATTR_EVENT_TIMESTAMP,
+		RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
+		RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+		0
+	};
+	int error = 405;
+	u8 attr;
+	enum radius_das_res res;
+	struct radius_das_attrs attrs;
+	u8 *buf;
+	size_t len;
+	char tmp[100];
+	u8 sta_addr[ETH_ALEN];
+
+	hdr = radius_msg_get_hdr(msg);
+
+	attr = radius_msg_find_unlisted_attr(msg, allowed);
+	if (attr) {
+		wpa_printf(MSG_INFO, "DAS: Unsupported attribute %u in "
+			   "Disconnect-Request from %s:%d", attr,
+			   abuf, from_port);
+		error = 401;
+		goto fail;
+	}
+
+	os_memset(&attrs, 0, sizeof(attrs));
+
+	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CALLING_STATION_ID,
+				    &buf, &len, NULL) == 0) {
+		if (len >= sizeof(tmp))
+			len = sizeof(tmp) - 1;
+		os_memcpy(tmp, buf, len);
+		tmp[len] = '\0';
+		if (hwaddr_aton2(tmp, sta_addr) < 0) {
+			wpa_printf(MSG_INFO, "DAS: Invalid Calling-Station-Id "
+				   "'%s' from %s:%d", tmp, abuf, from_port);
+			error = 407;
+			goto fail;
+		}
+		attrs.sta_addr = sta_addr;
+	}
+
+	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME,
+				    &buf, &len, NULL) == 0) {
+		attrs.user_name = buf;
+		attrs.user_name_len = len;
+	}
+
+	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
+				    &buf, &len, NULL) == 0) {
+		attrs.acct_session_id = buf;
+		attrs.acct_session_id_len = len;
+	}
+
+	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+				    &buf, &len, NULL) == 0) {
+		attrs.cui = buf;
+		attrs.cui_len = len;
+	}
+
+	res = das->disconnect(das->ctx, &attrs);
+	switch (res) {
+	case RADIUS_DAS_NAS_MISMATCH:
+		wpa_printf(MSG_INFO, "DAS: NAS mismatch from %s:%d",
+			   abuf, from_port);
+		error = 403;
+		break;
+	case RADIUS_DAS_SESSION_NOT_FOUND:
+		wpa_printf(MSG_INFO, "DAS: Session not found for request from "
+			   "%s:%d", abuf, from_port);
+		error = 503;
+		break;
+	case RADIUS_DAS_SUCCESS:
+		error = 0;
+		break;
+	}
+
+fail:
+	reply = radius_msg_new(error ? RADIUS_CODE_DISCONNECT_NAK :
+			       RADIUS_CODE_DISCONNECT_ACK, hdr->identifier);
+	if (reply == NULL)
+		return NULL;
+
+	if (error) {
+		radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE,
+					  error);
+	}
+
+	return reply;
+}
+
+
+static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct radius_das_data *das = eloop_ctx;
+	u8 buf[1500];
+	union {
+		struct sockaddr_storage ss;
+		struct sockaddr_in sin;
+#ifdef CONFIG_IPV6
+		struct sockaddr_in6 sin6;
+#endif /* CONFIG_IPV6 */
+	} from;
+	char abuf[50];
+	int from_port = 0;
+	socklen_t fromlen;
+	int len;
+	struct radius_msg *msg, *reply = NULL;
+	struct radius_hdr *hdr;
+	struct wpabuf *rbuf;
+	u32 val;
+	int res;
+	struct os_time now;
+
+	fromlen = sizeof(from);
+	len = recvfrom(sock, buf, sizeof(buf), 0,
+		       (struct sockaddr *) &from.ss, &fromlen);
+	if (len < 0) {
+		wpa_printf(MSG_ERROR, "DAS: recvfrom: %s", strerror(errno));
+		return;
+	}
+
+	os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
+	from_port = ntohs(from.sin.sin_port);
+
+	wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d",
+		   len, abuf, from_port);
+	if (das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr) {
+		wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client");
+		return;
+	}
+
+	msg = radius_msg_parse(buf, len);
+	if (msg == NULL) {
+		wpa_printf(MSG_DEBUG, "DAS: Parsing incoming RADIUS packet "
+			   "from %s:%d failed", abuf, from_port);
+		return;
+	}
+
+	if (wpa_debug_level <= MSG_MSGDUMP)
+		radius_msg_dump(msg);
+
+	if (radius_msg_verify_das_req(msg, das->shared_secret,
+				       das->shared_secret_len)) {
+		wpa_printf(MSG_DEBUG, "DAS: Invalid authenticator in packet "
+			   "from %s:%d - drop", abuf, from_port);
+		goto fail;
+	}
+
+	os_get_time(&now);
+	res = radius_msg_get_attr(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
+				  (u8 *) &val, 4);
+	if (res == 4) {
+		u32 timestamp = ntohl(val);
+		if (abs(now.sec - timestamp) > das->time_window) {
+			wpa_printf(MSG_DEBUG, "DAS: Unacceptable "
+				   "Event-Timestamp (%u; local time %u) in "
+				   "packet from %s:%d - drop",
+				   timestamp, (unsigned int) now.sec,
+				   abuf, from_port);
+			goto fail;
+		}
+	} else if (das->require_event_timestamp) {
+		wpa_printf(MSG_DEBUG, "DAS: Missing Event-Timestamp in packet "
+			   "from %s:%d - drop", abuf, from_port);
+		goto fail;
+	}
+
+	hdr = radius_msg_get_hdr(msg);
+
+	switch (hdr->code) {
+	case RADIUS_CODE_DISCONNECT_REQUEST:
+		reply = radius_das_disconnect(das, msg, abuf, from_port);
+		break;
+	case RADIUS_CODE_COA_REQUEST:
+		/* TODO */
+		reply = radius_msg_new(RADIUS_CODE_COA_NAK,
+				       hdr->identifier);
+		if (reply == NULL)
+			break;
+
+		/* Unsupported Service */
+		radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE, 405);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "DAS: Unexpected RADIUS code %u in "
+			   "packet from %s:%d",
+			   hdr->code, abuf, from_port);
+	}
+
+	if (reply) {
+		wpa_printf(MSG_DEBUG, "DAS: Reply to %s:%d", abuf, from_port);
+
+		if (!radius_msg_add_attr_int32(reply,
+					       RADIUS_ATTR_EVENT_TIMESTAMP,
+					       now.sec)) {
+			wpa_printf(MSG_DEBUG, "DAS: Failed to add "
+				   "Event-Timestamp attribute");
+		}
+
+		if (radius_msg_finish_das_resp(reply, das->shared_secret,
+					       das->shared_secret_len, hdr) <
+		    0) {
+			wpa_printf(MSG_DEBUG, "DAS: Failed to add "
+				   "Message-Authenticator attribute");
+		}
+
+		if (wpa_debug_level <= MSG_MSGDUMP)
+			radius_msg_dump(reply);
+
+		rbuf = radius_msg_get_buf(reply);
+		res = sendto(das->sock, wpabuf_head(rbuf),
+			     wpabuf_len(rbuf), 0,
+			     (struct sockaddr *) &from.ss, fromlen);
+		if (res < 0) {
+			wpa_printf(MSG_ERROR, "DAS: sendto(to %s:%d): %s",
+				   abuf, from_port, strerror(errno));
+		}
+	}
+
+fail:
+	radius_msg_free(msg);
+	radius_msg_free(reply);
+}
+
+
+static int radius_das_open_socket(int port)
+{
+	int s;
+	struct sockaddr_in addr;
+
+	s = socket(PF_INET, SOCK_DGRAM, 0);
+	if (s < 0) {
+		perror("socket");
+		return -1;
+	}
+
+	os_memset(&addr, 0, sizeof(addr));
+	addr.sin_family = AF_INET;
+	addr.sin_port = htons(port);
+	if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		perror("bind");
+		close(s);
+		return -1;
+	}
+
+	return s;
+}
+
+
+struct radius_das_data *
+radius_das_init(struct radius_das_conf *conf)
+{
+	struct radius_das_data *das;
+
+	if (conf->port == 0 || conf->shared_secret == NULL ||
+	    conf->client_addr == NULL)
+		return NULL;
+
+	das = os_zalloc(sizeof(*das));
+	if (das == NULL)
+		return NULL;
+
+	das->time_window = conf->time_window;
+	das->require_event_timestamp = conf->require_event_timestamp;
+	das->ctx = conf->ctx;
+	das->disconnect = conf->disconnect;
+
+	os_memcpy(&das->client_addr, conf->client_addr,
+		  sizeof(das->client_addr));
+
+	das->shared_secret = os_malloc(conf->shared_secret_len);
+	if (das->shared_secret == NULL) {
+		radius_das_deinit(das);
+		return NULL;
+	}
+	os_memcpy(das->shared_secret, conf->shared_secret,
+		  conf->shared_secret_len);
+	das->shared_secret_len = conf->shared_secret_len;
+
+	das->sock = radius_das_open_socket(conf->port);
+	if (das->sock < 0) {
+		wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS "
+			   "DAS");
+		radius_das_deinit(das);
+		return NULL;
+	}
+
+	if (eloop_register_read_sock(das->sock, radius_das_receive, das, NULL))
+	{
+		radius_das_deinit(das);
+		return NULL;
+	}
+
+	return das;
+}
+
+
+void radius_das_deinit(struct radius_das_data *das)
+{
+	if (das == NULL)
+		return;
+
+	if (das->sock >= 0) {
+		eloop_unregister_read_sock(das->sock);
+		close(das->sock);
+	}
+
+	os_free(das->shared_secret);
+	os_free(das);
+}
diff --git a/src/radius/radius_das.h b/src/radius/radius_das.h
new file mode 100644
index 0000000..738b18b
--- /dev/null
+++ b/src/radius/radius_das.h
@@ -0,0 +1,47 @@
+/*
+ * RADIUS Dynamic Authorization Server (DAS)
+ * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef RADIUS_DAS_H
+#define RADIUS_DAS_H
+
+struct radius_das_data;
+
+enum radius_das_res {
+	RADIUS_DAS_SUCCESS,
+	RADIUS_DAS_NAS_MISMATCH,
+	RADIUS_DAS_SESSION_NOT_FOUND
+};
+
+struct radius_das_attrs {
+	const u8 *sta_addr;
+	const u8 *user_name;
+	size_t user_name_len;
+	const u8 *acct_session_id;
+	size_t acct_session_id_len;
+	const u8 *cui;
+	size_t cui_len;
+};
+
+struct radius_das_conf {
+	int port;
+	const u8 *shared_secret;
+	size_t shared_secret_len;
+	const struct hostapd_ip_addr *client_addr;
+	unsigned int time_window;
+	int require_event_timestamp;
+	void *ctx;
+	enum radius_das_res (*disconnect)(void *ctx,
+					  struct radius_das_attrs *attr);
+};
+
+struct radius_das_data *
+radius_das_init(struct radius_das_conf *conf);
+
+void radius_das_deinit(struct radius_das_data *data);
+
+#endif /* RADIUS_DAS_H */
diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c
index 6ebc37a..7646ca8 100644
--- a/src/rsn_supp/tdls.c
+++ b/src/rsn_supp/tdls.c
@@ -2202,7 +2202,9 @@
 	if (sm == NULL)
 		return -1;
 
-	sm->l2_tdls = l2_packet_init(sm->ifname, sm->own_addr,
+	sm->l2_tdls = l2_packet_init(sm->bridge_ifname ? sm->bridge_ifname :
+				     sm->ifname,
+				     sm->own_addr,
 				     ETH_P_80211_ENCAP, wpa_supplicant_rx_tdls,
 				     sm, 0);
 	if (sm->l2_tdls == NULL) {
diff --git a/src/tls/libtommath.c b/src/tls/libtommath.c
index 7c9857f..741b442 100644
--- a/src/tls/libtommath.c
+++ b/src/tls/libtommath.c
@@ -66,11 +66,19 @@
 
 #define  OPT_CAST(x)
 
+#ifdef __x86_64__
+typedef unsigned long mp_digit;
+typedef unsigned long mp_word __attribute__((mode(TI)));
+
+#define DIGIT_BIT 60
+#define MP_64BIT
+#else
 typedef unsigned long mp_digit;
 typedef u64 mp_word;
 
 #define DIGIT_BIT          28
 #define MP_28BIT
+#endif
 
 
 #define XMALLOC  os_malloc
diff --git a/src/utils/build_config.h b/src/utils/build_config.h
index 04b8d98..f947388 100644
--- a/src/utils/build_config.h
+++ b/src/utils/build_config.h
@@ -47,28 +47,6 @@
 #endif /* USE_INTERNAL_CRYPTO */
 #endif /* CONFIG_WIN32_DEFAULTS */
 
-#ifdef __SYMBIAN32__
-#define OS_NO_C_LIB_DEFINES
-#define CONFIG_ANSI_C_EXTRA
-#define CONFIG_NO_WPA_MSG
-#define CONFIG_NO_HOSTAPD_LOGGER
-#define CONFIG_NO_STDOUT_DEBUG
-#define CONFIG_BACKEND_FILE
-#define CONFIG_INTERNAL_LIBTOMMATH
-#define CONFIG_CRYPTO_INTERNAL
-#define IEEE8021X_EAPOL
-#define PKCS12_FUNCS
-#define EAP_MD5
-#define EAP_TLS
-#define EAP_MSCHAPv2
-#define EAP_PEAP
-#define EAP_TTLS
-#define EAP_GTC
-#define EAP_OTP
-#define EAP_LEAP
-#define EAP_FAST
-#endif /* __SYMBIAN32__ */
-
 #ifdef CONFIG_XCODE_DEFAULTS
 #define CONFIG_DRIVER_OSX
 #define CONFIG_BACKEND_FILE
diff --git a/src/utils/common.h b/src/utils/common.h
index 43ceb54..7f115ef 100644
--- a/src/utils/common.h
+++ b/src/utils/common.h
@@ -63,12 +63,6 @@
 #endif
 #endif /* CONFIG_TI_COMPILER */
 
-#ifdef __SYMBIAN32__
-#define __BIG_ENDIAN 4321
-#define __LITTLE_ENDIAN 1234
-#define __BYTE_ORDER __LITTLE_ENDIAN
-#endif /* __SYMBIAN32__ */
-
 #ifdef CONFIG_NATIVE_WINDOWS
 #include <winsock.h>
 
@@ -132,16 +126,6 @@
 #define WPA_TYPES_DEFINED
 #endif /* CONFIG_TI_COMPILER */
 
-#ifdef __SYMBIAN32__
-#define __REMOVE_PLATSEC_DIAGNOSTICS__
-#include <e32def.h>
-typedef TUint64 u64;
-typedef TUint32 u32;
-typedef TUint16 u16;
-typedef TUint8 u8;
-#define WPA_TYPES_DEFINED
-#endif /* __SYMBIAN32__ */
-
 #ifndef WPA_TYPES_DEFINED
 #ifdef CONFIG_USE_INTTYPES_H
 #include <inttypes.h>
diff --git a/src/utils/eloop.c b/src/utils/eloop.c
index 5691f15..bb32401 100644
--- a/src/utils/eloop.c
+++ b/src/utils/eloop.c
@@ -278,7 +278,7 @@
 				pollfds_map[fd] = pfd;
 				nxt++;
 			}
-			pfd->events |= POLLIN;
+			pfd->events |= POLLOUT;
 		}
 	}
 
@@ -350,7 +350,8 @@
 				      int max_pollfd_map)
 {
 	if (eloop_sock_table_dispatch_table(readers, pollfds_map,
-					    max_pollfd_map, POLLIN))
+					    max_pollfd_map, POLLIN | POLLERR |
+					    POLLHUP))
 		return; /* pollfds may be invalid at this point */
 
 	if (eloop_sock_table_dispatch_table(writers, pollfds_map,
diff --git a/src/utils/includes.h b/src/utils/includes.h
index 787be18..6c6ec87 100644
--- a/src/utils/includes.h
+++ b/src/utils/includes.h
@@ -41,9 +41,7 @@
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #ifndef __vxworks
-#ifndef __SYMBIAN32__
 #include <sys/uio.h>
-#endif /* __SYMBIAN32__ */
 #include <sys/time.h>
 #endif /* __vxworks */
 #endif /* CONFIG_TI_COMPILER */
diff --git a/src/utils/pcsc_funcs.c b/src/utils/pcsc_funcs.c
index a97f9fa..08510d0 100644
--- a/src/utils/pcsc_funcs.c
+++ b/src/utils/pcsc_funcs.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant / PC/SC smartcard interface for USIM, GSM SIM
- * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2007, 2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -70,6 +70,9 @@
 #define USIM_TLV_TOTAL_FILE_SIZE	0x81
 #define USIM_TLV_PIN_STATUS_TEMPLATE	0xC6
 #define USIM_TLV_SHORT_FILE_ID		0x88
+#define USIM_TLV_SECURITY_ATTR_8B	0x8B
+#define USIM_TLV_SECURITY_ATTR_8C	0x8C
+#define USIM_TLV_SECURITY_ATTR_AB	0xAB
 
 #define USIM_PS_DO_TAG			0x90
 
@@ -81,6 +84,27 @@
 #define CK_LEN 16
 
 
+/* GSM files
+ * File type in first octet:
+ * 3F = Master File
+ * 7F = Dedicated File
+ * 2F = Elementary File under the Master File
+ * 6F = Elementary File under a Dedicated File
+ */
+#define SCARD_FILE_MF		0x3F00
+#define SCARD_FILE_GSM_DF	0x7F20
+#define SCARD_FILE_UMTS_DF	0x7F50
+#define SCARD_FILE_GSM_EF_IMSI	0x6F07
+#define SCARD_FILE_GSM_EF_AD	0x6FAD
+#define SCARD_FILE_EF_DIR	0x2F00
+#define SCARD_FILE_EF_ICCID	0x2FE2
+#define SCARD_FILE_EF_CK	0x6FE1
+#define SCARD_FILE_EF_IK	0x6FE2
+
+#define SCARD_CHV1_OFFSET	13
+#define SCARD_CHV1_FLAG		0x80
+
+
 typedef enum { SCARD_GSM_SIM, SCARD_USIM } sim_types;
 
 struct scard_data {
@@ -234,37 +258,60 @@
 static int scard_parse_fsp_templ(unsigned char *buf, size_t buf_len,
 				 int *ps_do, int *file_len)
 {
-		unsigned char *pos, *end;
+	unsigned char *pos, *end;
 
-		if (ps_do)
-			*ps_do = -1;
-		if (file_len)
-			*file_len = -1;
+	if (ps_do)
+		*ps_do = -1;
+	if (file_len)
+		*file_len = -1;
 
-		pos = buf;
-		end = pos + buf_len;
-		if (*pos != USIM_FSP_TEMPL_TAG) {
-			wpa_printf(MSG_DEBUG, "SCARD: file header did not "
-				   "start with FSP template tag");
-			return -1;
-		}
-		pos++;
-		if (pos >= end)
-			return -1;
-		if ((pos + pos[0]) < end)
-			end = pos + 1 + pos[0];
-		pos++;
-		wpa_hexdump(MSG_DEBUG, "SCARD: file header FSP template",
-			    pos, end - pos);
+	pos = buf;
+	end = pos + buf_len;
+	if (*pos != USIM_FSP_TEMPL_TAG) {
+		wpa_printf(MSG_DEBUG, "SCARD: file header did not "
+			   "start with FSP template tag");
+		return -1;
+	}
+	pos++;
+	if (pos >= end)
+		return -1;
+	if ((pos + pos[0]) < end)
+		end = pos + 1 + pos[0];
+	pos++;
+	wpa_hexdump(MSG_DEBUG, "SCARD: file header FSP template",
+		    pos, end - pos);
 
-		while (pos + 1 < end) {
-			wpa_printf(MSG_MSGDUMP, "SCARD: file header TLV "
-				   "0x%02x len=%d", pos[0], pos[1]);
-			if (pos + 2 + pos[1] > end)
-				break;
+	while (pos + 1 < end) {
+		wpa_printf(MSG_MSGDUMP, "SCARD: file header TLV 0x%02x len=%d",
+			   pos[0], pos[1]);
+		if (pos + 2 + pos[1] > end)
+			break;
 
-			if (pos[0] == USIM_TLV_FILE_SIZE &&
-			    (pos[1] == 1 || pos[1] == 2) && file_len) {
+		switch (pos[0]) {
+		case USIM_TLV_FILE_DESC:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: File Descriptor TLV",
+				    pos + 2, pos[1]);
+			break;
+		case USIM_TLV_FILE_ID:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: File Identifier TLV",
+				    pos + 2, pos[1]);
+			break;
+		case USIM_TLV_DF_NAME:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: DF name (AID) TLV",
+				    pos + 2, pos[1]);
+			break;
+		case USIM_TLV_PROPR_INFO:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: Proprietary "
+				    "information TLV", pos + 2, pos[1]);
+			break;
+		case USIM_TLV_LIFE_CYCLE_STATUS:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: Life Cycle Status "
+				    "Integer TLV", pos + 2, pos[1]);
+			break;
+		case USIM_TLV_FILE_SIZE:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: File size TLV",
+				    pos + 2, pos[1]);
+			if ((pos[1] == 1 || pos[1] == 2) && file_len) {
 				if (pos[1] == 1)
 					*file_len = (int) pos[2];
 				else
@@ -273,21 +320,43 @@
 				wpa_printf(MSG_DEBUG, "SCARD: file_size=%d",
 					   *file_len);
 			}
-
-			if (pos[0] == USIM_TLV_PIN_STATUS_TEMPLATE &&
-			    pos[1] >= 2 && pos[2] == USIM_PS_DO_TAG &&
+			break;
+		case USIM_TLV_TOTAL_FILE_SIZE:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: Total file size TLV",
+				    pos + 2, pos[1]);
+			break;
+		case USIM_TLV_PIN_STATUS_TEMPLATE:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: PIN Status Template "
+				    "DO TLV", pos + 2, pos[1]);
+			if (pos[1] >= 2 && pos[2] == USIM_PS_DO_TAG &&
 			    pos[3] >= 1 && ps_do) {
 				wpa_printf(MSG_DEBUG, "SCARD: PS_DO=0x%02x",
 					   pos[4]);
 				*ps_do = (int) pos[4];
 			}
-
-			pos += 2 + pos[1];
-
-			if (pos == end)
-				return 0;
+			break;
+		case USIM_TLV_SHORT_FILE_ID:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: Short File "
+				    "Identifier (SFI) TLV", pos + 2, pos[1]);
+			break;
+		case USIM_TLV_SECURITY_ATTR_8B:
+		case USIM_TLV_SECURITY_ATTR_8C:
+		case USIM_TLV_SECURITY_ATTR_AB:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: Security attribute "
+				    "TLV", pos + 2, pos[1]);
+			break;
+		default:
+			wpa_hexdump(MSG_MSGDUMP, "SCARD: Unrecognized TLV",
+				    pos, 2 + pos[1]);
+			break;
 		}
-		return -1;
+
+		pos += 2 + pos[1];
+
+		if (pos == end)
+			return 0;
+	}
+	return -1;
 }
 
 
@@ -328,7 +397,7 @@
 		unsigned char rid[5];
 		unsigned char appl_code[2]; /* 0x1002 for 3G USIM */
 	} *efdir;
-	unsigned char buf[100];
+	unsigned char buf[127];
 	size_t blen;
 
 	efdir = (struct efdir *) buf;
@@ -417,6 +486,7 @@
 /**
  * scard_init - Initialize SIM/USIM connection using PC/SC
  * @sim_type: Allowed SIM types (SIM, USIM, or both)
+ * @reader: Reader name prefix to search for
  * Returns: Pointer to private data structure, or %NULL on failure
  *
  * This function is used to initialize SIM/USIM connection. PC/SC is used to
@@ -425,10 +495,10 @@
  * access some of the card functions. Once the connection is not needed
  * anymore, scard_deinit() can be used to close it.
  */
-struct scard_data * scard_init(scard_sim_type sim_type)
+struct scard_data * scard_init(scard_sim_type sim_type, const char *reader)
 {
 	long ret;
-	unsigned long len;
+	unsigned long len, pos;
 	struct scard_data *scard;
 #ifdef CONFIG_NATIVE_WINDOWS
 	TCHAR *readers = NULL;
@@ -482,18 +552,41 @@
 			   "available.");
 		goto failed;
 	}
-	/* readers is a list of available reader. Last entry is terminated with
-	 * double NUL.
-	 * TODO: add support for selecting the reader; now just use the first
-	 * one.. */
+	wpa_hexdump_ascii(MSG_DEBUG, "SCARD: Readers", (u8 *) readers, len);
+	/*
+	 * readers is a list of available readers. The last entry is terminated
+	 * with double null.
+	 */
+	pos = 0;
 #ifdef UNICODE
-	wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%S'", readers);
+	/* TODO */
 #else /* UNICODE */
-	wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%s'", readers);
+	while (pos < len) {
+		if (reader == NULL ||
+		    os_strncmp(&readers[pos], reader, os_strlen(reader)) == 0)
+			break;
+		while (pos < len && readers[pos])
+			pos++;
+		pos++; /* skip separating null */
+		if (pos < len && readers[pos] == '\0')
+			pos = len; /* double null terminates list */
+	}
+#endif /* UNICODE */
+	if (pos >= len) {
+		wpa_printf(MSG_WARNING, "SCARD: No reader with prefix '%s' "
+			   "found", reader);
+		goto failed;
+	}
+
+#ifdef UNICODE
+	wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%S'", &readers[pos]);
+#else /* UNICODE */
+	wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%s'", &readers[pos]);
 #endif /* UNICODE */
 
-	ret = SCardConnect(scard->ctx, readers, SCARD_SHARE_SHARED,
-			   SCARD_PROTOCOL_T0, &scard->card, &scard->protocol);
+	ret = SCardConnect(scard->ctx, &readers[pos], SCARD_SHARE_SHARED,
+			   SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1,
+			   &scard->card, &scard->protocol);
 	if (ret != SCARD_S_SUCCESS) {
 		if (ret == (long) SCARD_E_NO_SMARTCARD)
 			wpa_printf(MSG_INFO, "No smart card inserted.");
@@ -582,7 +675,8 @@
 	}
 	if (pin_needed) {
 		scard->pin1_required = 1;
-		wpa_printf(MSG_DEBUG, "PIN1 needed for SIM access");
+		wpa_printf(MSG_DEBUG, "PIN1 needed for SIM access (retry "
+			   "counter=%d)", scard_get_pin_retry_counter(scard));
 	}
 
 	ret = SCardEndTransaction(scard->card, SCARD_LEAVE_CARD);
@@ -939,6 +1033,46 @@
 }
 
 
+int scard_get_pin_retry_counter(struct scard_data *scard)
+{
+	long ret;
+	unsigned char resp[3];
+	unsigned char cmd[5] = { SIM_CMD_VERIFY_CHV1 };
+	size_t len;
+	u16 val;
+
+	wpa_printf(MSG_DEBUG, "SCARD: fetching PIN retry counter");
+
+	if (scard->sim_type == SCARD_USIM)
+		cmd[0] = USIM_CLA;
+	cmd[4] = 0; /* Empty data */
+
+	len = sizeof(resp);
+	ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len);
+	if (ret != SCARD_S_SUCCESS)
+		return -2;
+
+	if (len != 2) {
+		wpa_printf(MSG_WARNING, "SCARD: failed to fetch PIN retry "
+			   "counter");
+		return -1;
+	}
+
+	val = WPA_GET_BE16(resp);
+	if (val == 0x63c0 || val == 0x6983) {
+		wpa_printf(MSG_DEBUG, "SCARD: PIN has been blocked");
+		return 0;
+	}
+
+	if (val >= 0x63c0 && val <= 0x63cf)
+		return val & 0x000f;
+
+	wpa_printf(MSG_DEBUG, "SCARD: Unexpected PIN retry counter response "
+		   "value 0x%x", val);
+	return 0;
+}
+
+
 /**
  * scard_get_imsi - Read IMSI from SIM/USIM card
  * @scard: Pointer to private data from scard_init()
@@ -1285,3 +1419,9 @@
 	wpa_printf(MSG_DEBUG, "SCARD: Unrecognized response");
 	return -1;
 }
+
+
+int scard_supports_umts(struct scard_data *scard)
+{
+	return scard->sim_type == SCARD_USIM;
+}
diff --git a/src/utils/pcsc_funcs.h b/src/utils/pcsc_funcs.h
index 2fd3610..b4ebc99 100644
--- a/src/utils/pcsc_funcs.h
+++ b/src/utils/pcsc_funcs.h
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant / PC/SC smartcard interface for USIM, GSM SIM
- * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2006, 2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -9,26 +9,6 @@
 #ifndef PCSC_FUNCS_H
 #define PCSC_FUNCS_H
 
-/* GSM files
- * File type in first octet:
- * 3F = Master File
- * 7F = Dedicated File
- * 2F = Elementary File under the Master File
- * 6F = Elementary File under a Dedicated File
- */
-#define SCARD_FILE_MF		0x3F00
-#define SCARD_FILE_GSM_DF	0x7F20
-#define SCARD_FILE_UMTS_DF	0x7F50
-#define SCARD_FILE_GSM_EF_IMSI	0x6F07
-#define SCARD_FILE_GSM_EF_AD	0x6FAD
-#define SCARD_FILE_EF_DIR	0x2F00
-#define SCARD_FILE_EF_ICCID	0x2FE2
-#define SCARD_FILE_EF_CK	0x6FE1
-#define SCARD_FILE_EF_IK	0x6FE2
-
-#define SCARD_CHV1_OFFSET	13
-#define SCARD_CHV1_FLAG		0x80
-
 typedef enum {
 	SCARD_GSM_SIM_ONLY,
 	SCARD_USIM_ONLY,
@@ -37,7 +17,7 @@
 
 
 #ifdef PCSC_FUNCS
-struct scard_data * scard_init(scard_sim_type sim_type);
+struct scard_data * scard_init(scard_sim_type sim_type, const char *reader);
 void scard_deinit(struct scard_data *scard);
 
 int scard_set_pin(struct scard_data *scard, const char *pin);
@@ -49,15 +29,20 @@
 		    const unsigned char *autn,
 		    unsigned char *res, size_t *res_len,
 		    unsigned char *ik, unsigned char *ck, unsigned char *auts);
+int scard_get_pin_retry_counter(struct scard_data *scard);
+int scard_supports_umts(struct scard_data *scard);
 
 #else /* PCSC_FUNCS */
 
-#define scard_init(s) NULL
+#define scard_init(s, r) NULL
 #define scard_deinit(s) do { } while (0)
 #define scard_set_pin(s, p) -1
 #define scard_get_imsi(s, i, l) -1
+#define scard_get_mnc_len(s) -1
 #define scard_gsm_auth(s, r, s2, k) -1
 #define scard_umts_auth(s, r, a, r2, rl, i, c, a2) -1
+#define scard_get_pin_retry_counter(s) -1
+#define scard_supports_umts(s) 0
 
 #endif /* PCSC_FUNCS */
 
diff --git a/src/utils/wpa_debug.c b/src/utils/wpa_debug.c
index a8b9106..5511ef1 100644
--- a/src/utils/wpa_debug.c
+++ b/src/utils/wpa_debug.c
@@ -16,6 +16,18 @@
 static int wpa_debug_syslog = 0;
 #endif /* CONFIG_DEBUG_SYSLOG */
 
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdio.h>
+
+static FILE *wpa_debug_tracing_file = NULL;
+
+#define WPAS_TRACE_PFX "wpas <%d>: "
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
+
 
 int wpa_debug_level = MSG_INFO;
 int wpa_debug_show_keys = 0;
@@ -30,25 +42,18 @@
 #define ANDROID_LOG_NAME	"wpa_supplicant"
 #endif /* ANDROID_LOG_NAME */
 
-void android_printf(int level, char *format, ...)
+static int wpa_to_android_level(int level)
 {
-	if (level >= wpa_debug_level) {
-		va_list ap;
-		if (level == MSG_ERROR)
-			level = ANDROID_LOG_ERROR;
-		else if (level == MSG_WARNING)
-			level = ANDROID_LOG_WARN;
-		else if (level == MSG_INFO)
-			level = ANDROID_LOG_INFO;
-		else
-			level = ANDROID_LOG_DEBUG;
-		va_start(ap, format);
-		__android_log_vprint(level, ANDROID_LOG_NAME, format, ap);
-		va_end(ap);
-	}
+	if (level == MSG_ERROR)
+		return ANDROID_LOG_ERROR;
+	if (level == MSG_WARNING)
+		return ANDROID_LOG_WARN;
+	if (level == MSG_INFO)
+		return ANDROID_LOG_INFO;
+	return ANDROID_LOG_DEBUG;
 }
 
-#else /* CONFIG_ANDROID_LOG */
+#endif /* CONFIG_ANDROID_LOG */
 
 #ifndef CONFIG_NO_STDOUT_DEBUG
 
@@ -59,6 +64,7 @@
 
 void wpa_debug_print_timestamp(void)
 {
+#ifndef CONFIG_ANDROID_LOG
 	struct os_time tv;
 
 	if (!wpa_debug_timestamp)
@@ -72,6 +78,7 @@
 	} else
 #endif /* CONFIG_DEBUG_FILE */
 	printf("%ld.%06u: ", (long) tv.sec, (unsigned int) tv.usec);
+#endif /* CONFIG_ANDROID_LOG */
 }
 
 
@@ -112,6 +119,77 @@
 #endif /* CONFIG_DEBUG_SYSLOG */
 
 
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+
+int wpa_debug_open_linux_tracing(void)
+{
+	int mounts, trace_fd;
+	char buf[4096] = {};
+	ssize_t buflen;
+	char *line, *tmp1, *path = NULL;
+
+	mounts = open("/proc/mounts", O_RDONLY);
+	if (mounts < 0) {
+		printf("no /proc/mounts\n");
+		return -1;
+	}
+
+	buflen = read(mounts, buf, sizeof(buf) - 1);
+	close(mounts);
+	if (buflen < 0) {
+		printf("failed to read /proc/mounts\n");
+		return -1;
+	}
+
+	line = strtok_r(buf, "\n", &tmp1);
+	while (line) {
+		char *tmp2, *tmp_path, *fstype;
+		/* "<dev> <mountpoint> <fs type> ..." */
+		strtok_r(line, " ", &tmp2);
+		tmp_path = strtok_r(NULL, " ", &tmp2);
+		fstype = strtok_r(NULL, " ", &tmp2);
+		if (strcmp(fstype, "debugfs") == 0) {
+			path = tmp_path;
+			break;
+		}
+
+		line = strtok_r(NULL, "\n", &tmp1);
+	}
+
+	if (path == NULL) {
+		printf("debugfs mountpoint not found\n");
+		return -1;
+	}
+
+	snprintf(buf, sizeof(buf) - 1, "%s/tracing/trace_marker", path);
+
+	trace_fd = open(buf, O_WRONLY);
+	if (trace_fd < 0) {
+		printf("failed to open trace_marker file\n");
+		return -1;
+	}
+	wpa_debug_tracing_file = fdopen(trace_fd, "w");
+	if (wpa_debug_tracing_file == NULL) {
+		close(trace_fd);
+		printf("failed to fdopen()\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void wpa_debug_close_linux_tracing(void)
+{
+	if (wpa_debug_tracing_file == NULL)
+		return;
+	fclose(wpa_debug_tracing_file);
+	wpa_debug_tracing_file = NULL;
+}
+
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
+
+
 /**
  * wpa_printf - conditional printf
  * @level: priority level (MSG_*) of the message
@@ -129,6 +207,10 @@
 
 	va_start(ap, fmt);
 	if (level >= wpa_debug_level) {
+#ifdef CONFIG_ANDROID_LOG
+		__android_log_vprint(wpa_to_android_level(level),
+				     ANDROID_LOG_NAME, fmt, ap);
+#else /* CONFIG_ANDROID_LOG */
 #ifdef CONFIG_DEBUG_SYSLOG
 		if (wpa_debug_syslog) {
 			vsyslog(syslog_priority(level), fmt, ap);
@@ -149,8 +231,20 @@
 #ifdef CONFIG_DEBUG_SYSLOG
 		}
 #endif /* CONFIG_DEBUG_SYSLOG */
+#endif /* CONFIG_ANDROID_LOG */
 	}
 	va_end(ap);
+
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+	if (wpa_debug_tracing_file != NULL) {
+		va_start(ap, fmt);
+		fprintf(wpa_debug_tracing_file, WPAS_TRACE_PFX, level);
+		vfprintf(wpa_debug_tracing_file, fmt, ap);
+		fprintf(wpa_debug_tracing_file, "\n");
+		fflush(wpa_debug_tracing_file);
+		va_end(ap);
+	}
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
 }
 
 
@@ -158,8 +252,65 @@
 			 size_t len, int show)
 {
 	size_t i;
+
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+	if (wpa_debug_tracing_file != NULL) {
+		fprintf(wpa_debug_tracing_file,
+			WPAS_TRACE_PFX "%s - hexdump(len=%lu):",
+			level, title, (unsigned long) len);
+		if (buf == NULL) {
+			fprintf(wpa_debug_tracing_file, " [NULL]\n");
+		} else if (!show) {
+			fprintf(wpa_debug_tracing_file, " [REMOVED]\n");
+		} else {
+			for (i = 0; i < len; i++)
+				fprintf(wpa_debug_tracing_file,
+					" %02x", buf[i]);
+		}
+		fflush(wpa_debug_tracing_file);
+	}
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
+
 	if (level < wpa_debug_level)
 		return;
+#ifdef CONFIG_ANDROID_LOG
+	{
+		const char *display;
+		char *strbuf = NULL;
+		size_t slen = len;
+		if (buf == NULL) {
+			display = " [NULL]";
+		} else if (len == 0) {
+			display = "";
+		} else if (show && len) {
+			/* Limit debug message length for Android log */
+			if (slen > 32)
+				slen = 32;
+			strbuf = os_malloc(1 + 3 * slen);
+			if (strbuf == NULL) {
+				wpa_printf(MSG_ERROR, "wpa_hexdump: Failed to "
+					   "allocate message buffer");
+				return;
+			}
+
+			for (i = 0; i < slen; i++)
+				os_snprintf(&strbuf[i * 3], 4, " %02x",
+					    buf[i]);
+
+			display = strbuf;
+		} else {
+			display = " [REMOVED]";
+		}
+
+		__android_log_print(wpa_to_android_level(level),
+				    ANDROID_LOG_NAME,
+				    "%s - hexdump(len=%lu):%s%s",
+				    title, (long unsigned int) len, display,
+				    len > slen ? " ..." : "");
+		os_free(strbuf);
+		return;
+	}
+#else /* CONFIG_ANDROID_LOG */
 #ifdef CONFIG_DEBUG_SYSLOG
 	if (wpa_debug_syslog) {
 		const char *display;
@@ -187,7 +338,7 @@
 		}
 
 		syslog(syslog_priority(level), "%s - hexdump(len=%lu):%s",
-		       title, len, display);
+		       title, (unsigned long) len, display);
 		os_free(strbuf);
 		return;
 	}
@@ -221,6 +372,7 @@
 #ifdef CONFIG_DEBUG_FILE
 	}
 #endif /* CONFIG_DEBUG_FILE */
+#endif /* CONFIG_ANDROID_LOG */
 }
 
 void wpa_hexdump(int level, const char *title, const u8 *buf, size_t len)
@@ -242,8 +394,30 @@
 	const u8 *pos = buf;
 	const size_t line_len = 16;
 
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+	if (wpa_debug_tracing_file != NULL) {
+		fprintf(wpa_debug_tracing_file,
+			WPAS_TRACE_PFX "%s - hexdump_ascii(len=%lu):",
+			level, title, (unsigned long) len);
+		if (buf == NULL) {
+			fprintf(wpa_debug_tracing_file, " [NULL]\n");
+		} else if (!show) {
+			fprintf(wpa_debug_tracing_file, " [REMOVED]\n");
+		} else {
+			/* can do ascii processing in userspace */
+			for (i = 0; i < len; i++)
+				fprintf(wpa_debug_tracing_file,
+					" %02x", buf[i]);
+		}
+		fflush(wpa_debug_tracing_file);
+	}
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
+
 	if (level < wpa_debug_level)
 		return;
+#ifdef CONFIG_ANDROID_LOG
+	_wpa_hexdump(level, title, buf, len, show);
+#else /* CONFIG_ANDROID_LOG */
 	wpa_debug_print_timestamp();
 #ifdef CONFIG_DEBUG_FILE
 	if (out_file) {
@@ -317,6 +491,7 @@
 #ifdef CONFIG_DEBUG_FILE
 	}
 #endif /* CONFIG_DEBUG_FILE */
+#endif /* CONFIG_ANDROID_LOG */
 }
 
 
@@ -398,7 +573,6 @@
 
 #endif /* CONFIG_NO_STDOUT_DEBUG */
 
-#endif /* CONFIG_ANDROID_LOG */
 
 #ifndef CONFIG_NO_WPA_MSG
 static wpa_msg_cb_func wpa_msg_cb = NULL;
diff --git a/src/utils/wpa_debug.h b/src/utils/wpa_debug.h
index bbef49b..339c749 100644
--- a/src/utils/wpa_debug.h
+++ b/src/utils/wpa_debug.h
@@ -18,32 +18,6 @@
 	MSG_EXCESSIVE, MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR
 };
 
-#ifdef CONFIG_ANDROID_LOG
-
-#define wpa_debug_print_timestamp() do {} while (0)
-#define wpa_hexdump(...)            do {} while (0)
-#define wpa_hexdump_key(...)        do {} while (0)
-#define wpa_hexdump_buf(l,t,b)      do {} while (0)
-#define wpa_hexdump_buf_key(l,t,b)  do {} while (0)
-#define wpa_hexdump_ascii(...)      do {} while (0)
-#define wpa_hexdump_ascii_key(...)  do {} while (0)
-#define wpa_debug_open_file(...)    do {} while (0)
-#define wpa_debug_close_file()      do {} while (0)
-#define wpa_dbg(...)                do {} while (0)
-
-static inline int wpa_debug_reopen_file(void)
-{
-	return 0;
-}
-
-
-void android_printf(int level, char *format, ...)
-PRINTF_FORMAT(2, 3);
-
-#define wpa_printf android_printf
-
-#else /* CONFIG_ANDROID_LOG */
-
 #ifdef CONFIG_NO_STDOUT_DEBUG
 
 #define wpa_debug_print_timestamp() do { } while (0)
@@ -177,8 +151,6 @@
 
 #endif /* CONFIG_NO_STDOUT_DEBUG */
 
-#endif /* CONFIG_ANDROID_LOG */
-
 
 #ifdef CONFIG_NO_WPA_MSG
 #define wpa_msg(args...) do { } while (0)
@@ -283,6 +255,24 @@
 
 #endif /* CONFIG_DEBUG_SYSLOG */
 
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+
+int wpa_debug_open_linux_tracing(void);
+void wpa_debug_close_linux_tracing(void);
+
+#else /* CONFIG_DEBUG_LINUX_TRACING */
+
+static inline int wpa_debug_open_linux_tracing(void)
+{
+	return 0;
+}
+
+static inline void wpa_debug_close_linux_tracing(void)
+{
+}
+
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
+
 
 #ifdef EAPOL_TEST
 #define WPA_ASSERT(a)						       \
diff --git a/src/wps/ndef.c b/src/wps/ndef.c
index 9baec7f..7630ecb 100644
--- a/src/wps/ndef.c
+++ b/src/wps/ndef.c
@@ -1,16 +1,10 @@
 /*
  * NDEF(NFC Data Exchange Format) routines for Wi-Fi Protected Setup
  *   Reference is "NFCForum-TS-NDEF_1.0 2006-07-24".
- * Copyright (c) 2009, Masashi Honma <honma@ictec.co.jp>
+ * Copyright (c) 2009-2012, Masashi Honma <masashi.honma@gmail.com>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "includes.h"
@@ -26,9 +20,9 @@
 #define FLAG_TNF_RFC2046 (0x02)
 
 struct ndef_record {
-	u8 *type;
-	u8 *id;
-	u8 *payload;
+	const u8 *type;
+	const u8 *id;
+	const u8 *payload;
 	u8 type_length;
 	u8 id_length;
 	u32 payload_length;
@@ -37,9 +31,10 @@
 
 static char wifi_handover_type[] = "application/vnd.wfa.wsc";
 
-static int ndef_parse_record(u8 *data, u32 size, struct ndef_record *record)
+static int ndef_parse_record(const u8 *data, u32 size,
+			     struct ndef_record *record)
 {
-	u8 *pos = data + 1;
+	const u8 *pos = data + 1;
 
 	if (size < 2)
 		return -1;
@@ -78,12 +73,12 @@
 }
 
 
-static struct wpabuf * ndef_parse_records(struct wpabuf *buf,
+static struct wpabuf * ndef_parse_records(const struct wpabuf *buf,
 					  int (*filter)(struct ndef_record *))
 {
 	struct ndef_record record;
 	int len = wpabuf_len(buf);
-	u8 *data = wpabuf_mhead(buf);
+	const u8 *data = wpabuf_head(buf);
 
 	while (len > 0) {
 		if (ndef_parse_record(data, len, &record) < 0) {
@@ -103,13 +98,14 @@
 
 static struct wpabuf * ndef_build_record(u8 flags, void *type,
 					 u8 type_length, void *id,
-					 u8 id_length, void *payload,
-					 u32 payload_length)
+					 u8 id_length,
+					 const struct wpabuf *payload)
 {
 	struct wpabuf *record;
 	size_t total_len;
 	int short_record;
 	u8 local_flag;
+	size_t payload_length = wpabuf_len(payload);
 
 	short_record = payload_length < 256 ? 1 : 0;
 
@@ -144,7 +140,7 @@
 		wpabuf_put_u8(record, id_length);
 	wpabuf_put_data(record, type, type_length);
 	wpabuf_put_data(record, id, id_length);
-	wpabuf_put_data(record, payload, payload_length);
+	wpabuf_put_buf(record, payload);
 	return record;
 }
 
@@ -160,16 +156,15 @@
 }
 
 
-struct wpabuf * ndef_parse_wifi(struct wpabuf *buf)
+struct wpabuf * ndef_parse_wifi(const struct wpabuf *buf)
 {
 	return ndef_parse_records(buf, wifi_filter);
 }
 
 
-struct wpabuf * ndef_build_wifi(struct wpabuf *buf)
+struct wpabuf * ndef_build_wifi(const struct wpabuf *buf)
 {
 	return ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_MESSAGE_END |
 				 FLAG_TNF_RFC2046, wifi_handover_type,
-				 os_strlen(wifi_handover_type), NULL, 0,
-				 wpabuf_mhead(buf), wpabuf_len(buf));
+				 os_strlen(wifi_handover_type), NULL, 0, buf);
 }
diff --git a/src/wps/wps.c b/src/wps/wps.c
index 9422c71..5453962 100644
--- a/src/wps/wps.c
+++ b/src/wps/wps.c
@@ -56,6 +56,23 @@
 		data->dev_password_len = cfg->pin_len;
 	}
 
+#ifdef CONFIG_WPS_NFC
+	if (cfg->wps->ap && !cfg->registrar && cfg->wps->ap_nfc_dev_pw_id) {
+		data->dev_pw_id = cfg->wps->ap_nfc_dev_pw_id;
+		os_free(data->dev_password);
+		data->dev_password =
+			os_malloc(wpabuf_len(cfg->wps->ap_nfc_dev_pw));
+		if (data->dev_password == NULL) {
+			os_free(data);
+			return NULL;
+		}
+		os_memcpy(data->dev_password,
+			  wpabuf_head(cfg->wps->ap_nfc_dev_pw),
+			  wpabuf_len(cfg->wps->ap_nfc_dev_pw));
+		data->dev_password_len = wpabuf_len(cfg->wps->ap_nfc_dev_pw);
+	}
+#endif /* CONFIG_WPS_NFC */
+
 	data->pbc = cfg->pbc;
 	if (cfg->pbc) {
 		/* Use special PIN '00000000' for PBC */
@@ -118,6 +135,12 @@
  */
 void wps_deinit(struct wps_data *data)
 {
+#ifdef CONFIG_WPS_NFC
+	if (data->registrar && data->nfc_pw_token)
+		wps_registrar_remove_nfc_pw_token(data->wps->registrar,
+						  data->nfc_pw_token);
+#endif /* CONFIG_WPS_NFC */
+
 	if (data->wps_pin_revealed) {
 		wpa_printf(MSG_DEBUG, "WPS: Full PIN information revealed and "
 			   "negotiation failed");
@@ -136,6 +159,7 @@
 	wps_device_data_free(&data->peer_dev);
 	os_free(data->new_ap_settings);
 	dh5_free(data->dh_ctx);
+	os_free(data->nfc_pw_token);
 	os_free(data);
 }
 
@@ -431,7 +455,8 @@
 
 /**
  * wps_build_probe_req_ie - Build WPS IE for Probe Request
- * @pbc: Whether searching for PBC mode APs
+ * @pw_id: Password ID (DEV_PW_PUSHBUTTON for active PBC and DEV_PW_DEFAULT for
+ * most other use cases)
  * @dev: Device attributes
  * @uuid: Own UUID
  * @req_type: Value for Request Type attribute
@@ -442,7 +467,7 @@
  *
  * The caller is responsible for freeing the buffer.
  */
-struct wpabuf * wps_build_probe_req_ie(int pbc, struct wps_device_data *dev,
+struct wpabuf * wps_build_probe_req_ie(u16 pw_id, struct wps_device_data *dev,
 				       const u8 *uuid,
 				       enum wps_request_type req_type,
 				       unsigned int num_req_dev_types,
@@ -464,8 +489,7 @@
 	    wps_build_rf_bands(dev, ie) ||
 	    wps_build_assoc_state(NULL, ie) ||
 	    wps_build_config_error(ie, WPS_CFG_NO_ERROR) ||
-	    wps_build_dev_password_id(ie, pbc ? DEV_PW_PUSHBUTTON :
-				      DEV_PW_DEFAULT) ||
+	    wps_build_dev_password_id(ie, pw_id) ||
 #ifdef CONFIG_WPS2
 	    wps_build_manufacturer(dev, ie) ||
 	    wps_build_model_name(dev, ie) ||
diff --git a/src/wps/wps.h b/src/wps/wps.h
index 22e029f..c45b68c 100644
--- a/src/wps/wps.h
+++ b/src/wps/wps.h
@@ -1,6 +1,6 @@
 /*
  * Wi-Fi Protected Setup
- * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -27,6 +27,7 @@
 struct wps_registrar;
 struct upnp_wps_device_sm;
 struct wps_er;
+struct wps_parse_attr;
 
 /**
  * struct wps_credential - WPS Credential
@@ -94,6 +95,7 @@
 	u32 os_version;
 	u8 rf_bands;
 	u16 config_methods;
+	struct wpabuf *vendor_ext_m1;
 	struct wpabuf *vendor_ext[MAX_WPS_VENDOR_EXTENSIONS];
 
 	int p2p;
@@ -238,7 +240,7 @@
 
 struct wpabuf * wps_build_assoc_req_ie(enum wps_request_type req_type);
 struct wpabuf * wps_build_assoc_resp_ie(void);
-struct wpabuf * wps_build_probe_req_ie(int pbc, struct wps_device_data *dev,
+struct wpabuf * wps_build_probe_req_ie(u16 pw_id, struct wps_device_data *dev,
 				       const u8 *uuid,
 				       enum wps_request_type req_type,
 				       unsigned int num_req_dev_types,
@@ -294,12 +296,15 @@
 	 * @ctx: Higher layer context data (cb_ctx)
 	 * @mac_addr: MAC address of the Enrollee
 	 * @uuid_e: UUID-E of the Enrollee
+	 * @dev_pw: Device Password (PIN) used during registration
+	 * @dev_pw_len: Length of dev_pw in octets
 	 *
 	 * This callback is called whenever an Enrollee completes registration
 	 * successfully.
 	 */
 	void (*reg_success_cb)(void *ctx, const u8 *mac_addr,
-			       const u8 *uuid_e);
+			       const u8 *uuid_e, const u8 *dev_pw,
+			       size_t dev_pw_len);
 
 	/**
 	 * set_sel_reg_cb - Callback for reporting selected registrar changes
@@ -752,6 +757,11 @@
 
 	/* Pending messages from UPnP PutWLANResponse */
 	struct upnp_pending_message *upnp_msgs;
+
+	u16 ap_nfc_dev_pw_id;
+	struct wpabuf *ap_nfc_dh_pubkey;
+	struct wpabuf *ap_nfc_dh_privkey;
+	struct wpabuf *ap_nfc_dev_pw;
 };
 
 struct oob_device_data {
@@ -783,7 +793,8 @@
 int wps_registrar_unlock_pin(struct wps_registrar *reg, const u8 *uuid);
 int wps_registrar_button_pushed(struct wps_registrar *reg,
 				const u8 *p2p_dev_addr);
-void wps_registrar_complete(struct wps_registrar *registrar, const u8 *uuid_e);
+void wps_registrar_complete(struct wps_registrar *registrar, const u8 *uuid_e,
+			    const u8 *dev_pw, size_t dev_pw_len);
 void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr,
 				const struct wpabuf *wps_data,
 				int p2p_wildcard);
@@ -792,6 +803,12 @@
 			   char *buf, size_t buflen);
 int wps_registrar_config_ap(struct wps_registrar *reg,
 			    struct wps_credential *cred);
+int wps_registrar_add_nfc_pw_token(struct wps_registrar *reg,
+				   const u8 *pubkey_hash, u16 pw_id,
+				   const u8 *dev_pw, size_t dev_pw_len);
+int wps_registrar_add_nfc_password_token(struct wps_registrar *reg,
+					 const u8 *oob_dev_pw,
+					 size_t oob_dev_pw_len);
 
 int wps_build_credential_wrap(struct wpabuf *msg,
 			      const struct wps_credential *cred);
@@ -799,6 +816,7 @@
 unsigned int wps_pin_checksum(unsigned int pin);
 unsigned int wps_pin_valid(unsigned int pin);
 unsigned int wps_generate_pin(void);
+int wps_pin_str_valid(const char *pin);
 void wps_free_pending_msgs(struct upnp_pending_message *msgs);
 
 struct oob_device_data * wps_get_oob_device(char *device_type);
@@ -806,6 +824,8 @@
 int wps_get_oob_method(char *method);
 int wps_process_oob(struct wps_context *wps, struct oob_device_data *oob_dev,
 		    int registrar);
+struct wpabuf * wps_get_oob_cred(struct wps_context *wps);
+int wps_oob_use_cred(struct wps_context *wps, struct wps_parse_attr *attr);
 int wps_attr_text(struct wpabuf *data, char *buf, char *end);
 
 struct wps_er * wps_er_init(struct wps_context *wps, const char *ifname,
@@ -821,12 +841,23 @@
 		      const struct wps_credential *cred);
 int wps_er_config(struct wps_er *er, const u8 *uuid, const u8 *pin,
 		  size_t pin_len, const struct wps_credential *cred);
+struct wpabuf * wps_er_nfc_config_token(struct wps_er *er, const u8 *uuid);
 
 int wps_dev_type_str2bin(const char *str, u8 dev_type[WPS_DEV_TYPE_LEN]);
 char * wps_dev_type_bin2str(const u8 dev_type[WPS_DEV_TYPE_LEN], char *buf,
 			    size_t buf_len);
 void uuid_gen_mac_addr(const u8 *mac_addr, u8 *uuid);
 u16 wps_config_methods_str2bin(const char *str);
+struct wpabuf * wps_build_nfc_pw_token(u16 dev_pw_id,
+				       const struct wpabuf *pubkey,
+				       const struct wpabuf *dev_pw);
+struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey,
+				  struct wpabuf **privkey,
+				  struct wpabuf **dev_pw);
+
+/* ndef.c */
+struct wpabuf * ndef_parse_wifi(const struct wpabuf *buf);
+struct wpabuf * ndef_build_wifi(const struct wpabuf *buf);
 
 #ifdef CONFIG_WPS_STRICT
 int wps_validate_beacon(const struct wpabuf *wps_ie);
diff --git a/src/wps/wps_attr_build.c b/src/wps/wps_attr_build.c
index 753083d..9be30b9 100644
--- a/src/wps/wps_attr_build.c
+++ b/src/wps/wps_attr_build.c
@@ -30,6 +30,14 @@
 		wps->dh_ctx = wps->wps->dh_ctx;
 		wps->wps->dh_ctx = NULL;
 		pubkey = wpabuf_dup(wps->wps->dh_pubkey);
+#ifdef CONFIG_WPS_NFC
+	} else if (wps->dev_pw_id >= 0x10 && wps->wps->ap &&
+		   wps->dev_pw_id == wps->wps->ap_nfc_dev_pw_id) {
+		wpa_printf(MSG_DEBUG, "WPS: Using NFC password token DH keys");
+		wps->dh_privkey = wpabuf_dup(wps->wps->ap_nfc_dh_privkey);
+		pubkey = wpabuf_dup(wps->wps->ap_nfc_dh_pubkey);
+		wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, pubkey);
+#endif /* CONFIG_WPS_NFC */
 	} else {
 		wpa_printf(MSG_DEBUG, "WPS: Generate new DH keys");
 		wps->dh_privkey = NULL;
@@ -340,19 +348,34 @@
 
 
 #ifdef CONFIG_WPS_OOB
-int wps_build_oob_dev_password(struct wpabuf *msg, struct wps_context *wps)
+int wps_build_oob_dev_pw(struct wpabuf *msg, u16 dev_pw_id,
+			 const struct wpabuf *pubkey, const u8 *dev_pw,
+			 size_t dev_pw_len)
 {
 	size_t hash_len;
 	const u8 *addr[1];
 	u8 pubkey_hash[WPS_HASH_LEN];
+
+	addr[0] = wpabuf_head(pubkey);
+	hash_len = wpabuf_len(pubkey);
+	sha256_vector(1, addr, &hash_len, pubkey_hash);
+
+	wpabuf_put_be16(msg, ATTR_OOB_DEVICE_PASSWORD);
+	wpabuf_put_be16(msg, WPS_OOB_PUBKEY_HASH_LEN + 2 + dev_pw_len);
+	wpabuf_put_data(msg, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
+	wpabuf_put_be16(msg, dev_pw_id);
+	wpabuf_put_data(msg, dev_pw, dev_pw_len);
+
+	return 0;
+}
+
+
+int wps_build_oob_dev_password(struct wpabuf *msg, struct wps_context *wps)
+{
 	u8 dev_password_bin[WPS_OOB_DEVICE_PASSWORD_LEN];
 
 	wpa_printf(MSG_DEBUG, "WPS:  * OOB Device Password");
 
-	addr[0] = wpabuf_head(wps->dh_pubkey);
-	hash_len = wpabuf_len(wps->dh_pubkey);
-	sha256_vector(1, addr, &hash_len, pubkey_hash);
-
 	if (os_get_random((u8 *) &wps->oob_dev_pw_id, sizeof(u16)) < 0) {
 		wpa_printf(MSG_ERROR, "WPS: device password id "
 			   "generation error");
@@ -367,19 +390,15 @@
 		return -1;
 	}
 
-	wpabuf_put_be16(msg, ATTR_OOB_DEVICE_PASSWORD);
-	wpabuf_put_be16(msg, WPS_OOB_DEVICE_PASSWORD_ATTR_LEN);
-	wpabuf_put_data(msg, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
-	wpabuf_put_be16(msg, wps->oob_dev_pw_id);
-	wpabuf_put_data(msg, dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN);
-
 	wpa_snprintf_hex_uppercase(
 		wpabuf_put(wps->oob_conf.dev_password,
 			   wpabuf_size(wps->oob_conf.dev_password)),
 		wpabuf_size(wps->oob_conf.dev_password),
 		dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN);
 
-	return 0;
+	return wps_build_oob_dev_pw(msg, wps->oob_dev_pw_id, wps->dh_pubkey,
+				    dev_password_bin,
+				    WPS_OOB_DEVICE_PASSWORD_LEN);
 }
 #endif /* CONFIG_WPS_OOB */
 
diff --git a/src/wps/wps_attr_parse.c b/src/wps/wps_attr_parse.c
index f5ad403..5aa9b00 100644
--- a/src/wps/wps_attr_parse.c
+++ b/src/wps/wps_attr_parse.c
@@ -9,7 +9,8 @@
 #include "includes.h"
 
 #include "common.h"
-#include "wps_i.h"
+#include "wps_defs.h"
+#include "wps_attr_parse.h"
 
 #ifndef CONFIG_WPS_STRICT
 #define WPS_WORKAROUNDS
@@ -262,12 +263,16 @@
 		attr->dev_password_id = pos;
 		break;
 	case ATTR_OOB_DEVICE_PASSWORD:
-		if (len != WPS_OOB_DEVICE_PASSWORD_ATTR_LEN) {
+		if (len < WPS_OOB_PUBKEY_HASH_LEN + 2 +
+		    WPS_OOB_DEVICE_PASSWORD_MIN_LEN ||
+		    len > WPS_OOB_PUBKEY_HASH_LEN + 2 +
+		    WPS_OOB_DEVICE_PASSWORD_LEN) {
 			wpa_printf(MSG_DEBUG, "WPS: Invalid OOB Device "
 				   "Password length %u", len);
 			return -1;
 		}
 		attr->oob_dev_password = pos;
+		attr->oob_dev_password_len = len;
 		break;
 	case ATTR_OS_VERSION:
 		if (len != 4) {
diff --git a/src/wps/wps_attr_parse.h b/src/wps/wps_attr_parse.h
new file mode 100644
index 0000000..332e966
--- /dev/null
+++ b/src/wps/wps_attr_parse.h
@@ -0,0 +1,107 @@
+/*
+ * Wi-Fi Protected Setup - attribute parsing
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPS_ATTR_PARSE_H
+#define WPS_ATTR_PARSE_H
+
+#include "wps.h"
+
+struct wps_parse_attr {
+	/* fixed length fields */
+	const u8 *version; /* 1 octet */
+	const u8 *version2; /* 1 octet */
+	const u8 *msg_type; /* 1 octet */
+	const u8 *enrollee_nonce; /* WPS_NONCE_LEN (16) octets */
+	const u8 *registrar_nonce; /* WPS_NONCE_LEN (16) octets */
+	const u8 *uuid_r; /* WPS_UUID_LEN (16) octets */
+	const u8 *uuid_e; /* WPS_UUID_LEN (16) octets */
+	const u8 *auth_type_flags; /* 2 octets */
+	const u8 *encr_type_flags; /* 2 octets */
+	const u8 *conn_type_flags; /* 1 octet */
+	const u8 *config_methods; /* 2 octets */
+	const u8 *sel_reg_config_methods; /* 2 octets */
+	const u8 *primary_dev_type; /* 8 octets */
+	const u8 *rf_bands; /* 1 octet */
+	const u8 *assoc_state; /* 2 octets */
+	const u8 *config_error; /* 2 octets */
+	const u8 *dev_password_id; /* 2 octets */
+	const u8 *os_version; /* 4 octets */
+	const u8 *wps_state; /* 1 octet */
+	const u8 *authenticator; /* WPS_AUTHENTICATOR_LEN (8) octets */
+	const u8 *r_hash1; /* WPS_HASH_LEN (32) octets */
+	const u8 *r_hash2; /* WPS_HASH_LEN (32) octets */
+	const u8 *e_hash1; /* WPS_HASH_LEN (32) octets */
+	const u8 *e_hash2; /* WPS_HASH_LEN (32) octets */
+	const u8 *r_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */
+	const u8 *r_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */
+	const u8 *e_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */
+	const u8 *e_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */
+	const u8 *key_wrap_auth; /* WPS_KWA_LEN (8) octets */
+	const u8 *auth_type; /* 2 octets */
+	const u8 *encr_type; /* 2 octets */
+	const u8 *network_idx; /* 1 octet */
+	const u8 *network_key_idx; /* 1 octet */
+	const u8 *mac_addr; /* ETH_ALEN (6) octets */
+	const u8 *key_prov_auto; /* 1 octet (Bool) */
+	const u8 *dot1x_enabled; /* 1 octet (Bool) */
+	const u8 *selected_registrar; /* 1 octet (Bool) */
+	const u8 *request_type; /* 1 octet */
+	const u8 *response_type; /* 1 octet */
+	const u8 *ap_setup_locked; /* 1 octet */
+	const u8 *settings_delay_time; /* 1 octet */
+	const u8 *network_key_shareable; /* 1 octet (Bool) */
+	const u8 *request_to_enroll; /* 1 octet (Bool) */
+
+	/* variable length fields */
+	const u8 *manufacturer;
+	size_t manufacturer_len;
+	const u8 *model_name;
+	size_t model_name_len;
+	const u8 *model_number;
+	size_t model_number_len;
+	const u8 *serial_number;
+	size_t serial_number_len;
+	const u8 *dev_name;
+	size_t dev_name_len;
+	const u8 *public_key;
+	size_t public_key_len;
+	const u8 *encr_settings;
+	size_t encr_settings_len;
+	const u8 *ssid; /* <= 32 octets */
+	size_t ssid_len;
+	const u8 *network_key; /* <= 64 octets */
+	size_t network_key_len;
+	const u8 *eap_type; /* <= 8 octets */
+	size_t eap_type_len;
+	const u8 *eap_identity; /* <= 64 octets */
+	size_t eap_identity_len;
+	const u8 *authorized_macs; /* <= 30 octets */
+	size_t authorized_macs_len;
+	const u8 *sec_dev_type_list; /* <= 128 octets */
+	size_t sec_dev_type_list_len;
+	const u8 *oob_dev_password; /* 38..54 octets */
+	size_t oob_dev_password_len;
+
+	/* attributes that can occur multiple times */
+#define MAX_CRED_COUNT 10
+	const u8 *cred[MAX_CRED_COUNT];
+	size_t cred_len[MAX_CRED_COUNT];
+	size_t num_cred;
+
+#define MAX_REQ_DEV_TYPE_COUNT 10
+	const u8 *req_dev_type[MAX_REQ_DEV_TYPE_COUNT];
+	size_t num_req_dev_type;
+
+	const u8 *vendor_ext[MAX_WPS_PARSE_VENDOR_EXT];
+	size_t vendor_ext_len[MAX_WPS_PARSE_VENDOR_EXT];
+	size_t num_vendor_ext;
+};
+
+int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr);
+
+#endif /* WPS_ATTR_PARSE_H */
diff --git a/src/wps/wps_common.c b/src/wps/wps_common.c
index 2dff4b2..5a8817f 100644
--- a/src/wps/wps_common.c
+++ b/src/wps/wps_common.c
@@ -1,6 +1,6 @@
 /*
  * Wi-Fi Protected Setup - common functionality
- * Copyright (c) 2008-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -249,6 +249,22 @@
 }
 
 
+int wps_pin_str_valid(const char *pin)
+{
+	const char *p;
+	size_t len;
+
+	p = pin;
+	while (*p >= '0' && *p <= '9')
+		p++;
+	if (*p != '\0')
+		return 0;
+
+	len = p - pin;
+	return len == 4 || len == 8;
+}
+
+
 void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg,
 		    u16 config_error, u16 error_indication)
 {
@@ -308,7 +324,7 @@
 
 #ifdef CONFIG_WPS_OOB
 
-static struct wpabuf * wps_get_oob_cred(struct wps_context *wps)
+struct wpabuf * wps_get_oob_cred(struct wps_context *wps)
 {
 	struct wps_data data;
 	struct wpabuf *plain;
@@ -335,11 +351,35 @@
 }
 
 
+struct wpabuf * wps_build_nfc_pw_token(u16 dev_pw_id,
+				       const struct wpabuf *pubkey,
+				       const struct wpabuf *dev_pw)
+{
+	struct wpabuf *data;
+
+	data = wpabuf_alloc(200);
+	if (data == NULL)
+		return NULL;
+
+	if (wps_build_version(data) ||
+	    wps_build_oob_dev_pw(data, dev_pw_id, pubkey,
+				 wpabuf_head(dev_pw), wpabuf_len(dev_pw)) ||
+	    wps_build_wfa_ext(data, 0, NULL, 0)) {
+		wpa_printf(MSG_ERROR, "WPS: Failed to build NFC password "
+			   "token");
+		wpabuf_free(data);
+		return NULL;
+	}
+
+	return data;
+}
+
+
 static struct wpabuf * wps_get_oob_dev_pwd(struct wps_context *wps)
 {
 	struct wpabuf *data;
 
-	data = wpabuf_alloc(9 + WPS_OOB_DEVICE_PASSWORD_ATTR_LEN);
+	data = wpabuf_alloc(200);
 	if (data == NULL) {
 		wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
 			   "device password attribute");
@@ -375,6 +415,7 @@
 	struct oob_conf_data *oob_conf = &wps->oob_conf;
 	struct wps_parse_attr attr;
 	const u8 *pos;
+	size_t pw_len;
 
 	if (wps_parse_msg(data, &attr) < 0 ||
 	    attr.oob_dev_password == NULL) {
@@ -384,6 +425,7 @@
 
 	pos = attr.oob_dev_password;
 
+	wpabuf_free(oob_conf->pubkey_hash);
 	oob_conf->pubkey_hash =
 		wpabuf_alloc_copy(pos, WPS_OOB_PUBKEY_HASH_LEN);
 	if (oob_conf->pubkey_hash == NULL) {
@@ -396,39 +438,32 @@
 	wps->oob_dev_pw_id = WPA_GET_BE16(pos);
 	pos += sizeof(wps->oob_dev_pw_id);
 
-	oob_conf->dev_password =
-		wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN * 2 + 1);
+	pw_len = attr.oob_dev_password_len - WPS_OOB_PUBKEY_HASH_LEN - 2;
+	oob_conf->dev_password = wpabuf_alloc(pw_len * 2 + 1);
 	if (oob_conf->dev_password == NULL) {
 		wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
 			   "device password");
 		return -1;
 	}
 	wpa_snprintf_hex_uppercase(wpabuf_put(oob_conf->dev_password,
-				   wpabuf_size(oob_conf->dev_password)),
-				   wpabuf_size(oob_conf->dev_password), pos,
-				   WPS_OOB_DEVICE_PASSWORD_LEN);
+					      pw_len * 2 + 1),
+				   pw_len * 2 + 1, pos, pw_len);
 
 	return 0;
 }
 
 
-static int wps_parse_oob_cred(struct wps_context *wps, struct wpabuf *data)
+int wps_oob_use_cred(struct wps_context *wps, struct wps_parse_attr *attr)
 {
 	struct wpabuf msg;
-	struct wps_parse_attr attr;
 	size_t i;
 
-	if (wps_parse_msg(data, &attr) < 0 || attr.num_cred <= 0) {
-		wpa_printf(MSG_ERROR, "WPS: OOB credential not found");
-		return -1;
-	}
-
-	for (i = 0; i < attr.num_cred; i++) {
+	for (i = 0; i < attr->num_cred; i++) {
 		struct wps_credential local_cred;
 		struct wps_parse_attr cattr;
 
 		os_memset(&local_cred, 0, sizeof(local_cred));
-		wpabuf_set(&msg, attr.cred[i], attr.cred_len[i]);
+		wpabuf_set(&msg, attr->cred[i], attr->cred_len[i]);
 		if (wps_parse_msg(&msg, &cattr) < 0 ||
 		    wps_process_cred(&cattr, &local_cred)) {
 			wpa_printf(MSG_ERROR, "WPS: Failed to parse OOB "
@@ -442,6 +477,19 @@
 }
 
 
+static int wps_parse_oob_cred(struct wps_context *wps, struct wpabuf *data)
+{
+	struct wps_parse_attr attr;
+
+	if (wps_parse_msg(data, &attr) < 0 || attr.num_cred <= 0) {
+		wpa_printf(MSG_ERROR, "WPS: OOB credential not found");
+		return -1;
+	}
+
+	return wps_oob_use_cred(wps, &attr);
+}
+
+
 int wps_process_oob(struct wps_context *wps, struct oob_device_data *oob_dev,
 		    int registrar)
 {
@@ -695,3 +743,53 @@
 
 	return msg;
 }
+
+
+#ifdef CONFIG_WPS_NFC
+struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey,
+				  struct wpabuf **privkey,
+				  struct wpabuf **dev_pw)
+{
+	struct wpabuf *priv = NULL, *pub = NULL, *pw, *ret;
+	void *dh_ctx;
+	u16 val;
+
+	pw = wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN);
+	if (pw == NULL)
+		return NULL;
+
+	if (random_get_bytes(wpabuf_put(pw, WPS_OOB_DEVICE_PASSWORD_LEN),
+			     WPS_OOB_DEVICE_PASSWORD_LEN) ||
+	    random_get_bytes((u8 *) &val, sizeof(val))) {
+		wpabuf_free(pw);
+		return NULL;
+	}
+
+	dh_ctx = dh5_init(&priv, &pub);
+	if (dh_ctx == NULL) {
+		wpabuf_free(pw);
+		return NULL;
+	}
+	dh5_free(dh_ctx);
+
+	*id = 0x10 + val % 0xfff0;
+	wpabuf_free(*pubkey);
+	*pubkey = pub;
+	wpabuf_free(*privkey);
+	*privkey = priv;
+	wpabuf_free(*dev_pw);
+	*dev_pw = pw;
+
+	ret = wps_build_nfc_pw_token(*id, *pubkey, *dev_pw);
+	if (ndef && ret) {
+		struct wpabuf *tmp;
+		tmp = ndef_build_wifi(ret);
+		wpabuf_free(ret);
+		if (tmp == NULL)
+			return NULL;
+		ret = tmp;
+	}
+
+	return ret;
+}
+#endif /* CONFIG_WPS_NFC */
diff --git a/src/wps/wps_defs.h b/src/wps/wps_defs.h
index e128a19..2f42603 100644
--- a/src/wps/wps_defs.h
+++ b/src/wps/wps_defs.h
@@ -41,7 +41,7 @@
 #define WPS_MGMTAUTHKEY_LEN 32
 #define WPS_MGMTENCKEY_LEN 16
 #define WPS_MGMT_KEY_ID_LEN 16
-#define WPS_OOB_DEVICE_PASSWORD_ATTR_LEN 54
+#define WPS_OOB_DEVICE_PASSWORD_MIN_LEN 16
 #define WPS_OOB_DEVICE_PASSWORD_LEN 32
 #define WPS_OOB_PUBKEY_HASH_LEN 20
 
diff --git a/src/wps/wps_dev_attr.c b/src/wps/wps_dev_attr.c
index 559582d..3c94a43 100644
--- a/src/wps/wps_dev_attr.c
+++ b/src/wps/wps_dev_attr.c
@@ -203,6 +203,20 @@
 }
 
 
+int wps_build_vendor_ext_m1(struct wps_device_data *dev, struct wpabuf *msg)
+{
+	if (dev->vendor_ext_m1 != NULL) {
+		wpa_hexdump(MSG_DEBUG, "WPS:  * Vendor Extension M1",
+			    wpabuf_head_u8(dev->vendor_ext_m1),
+			    wpabuf_len(dev->vendor_ext_m1));
+		wpabuf_put_be16(msg, ATTR_VENDOR_EXT);
+		wpabuf_put_be16(msg, wpabuf_len(dev->vendor_ext_m1));
+		wpabuf_put_buf(msg, dev->vendor_ext_m1);
+	}
+	return 0;
+}
+
+
 int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg)
 {
 	wpa_printf(MSG_DEBUG, "WPS:  * RF Bands (%x)", dev->rf_bands);
diff --git a/src/wps/wps_dev_attr.h b/src/wps/wps_dev_attr.h
index 7ca81ad..200c9c4 100644
--- a/src/wps/wps_dev_attr.h
+++ b/src/wps/wps_dev_attr.h
@@ -17,6 +17,7 @@
 int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg);
 int wps_build_device_attrs(struct wps_device_data *dev, struct wpabuf *msg);
 int wps_build_os_version(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_vendor_ext_m1(struct wps_device_data *dev, struct wpabuf *msg);
 int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg);
 int wps_build_primary_dev_type(struct wps_device_data *dev,
 			       struct wpabuf *msg);
diff --git a/src/wps/wps_enrollee.c b/src/wps/wps_enrollee.c
index 85d2e95..da0c101 100644
--- a/src/wps/wps_enrollee.c
+++ b/src/wps/wps_enrollee.c
@@ -163,7 +163,8 @@
 	    wps_build_dev_password_id(msg, wps->dev_pw_id) ||
 	    wps_build_config_error(msg, WPS_CFG_NO_ERROR) ||
 	    wps_build_os_version(&wps->wps->dev, msg) ||
-	    wps_build_wfa_ext(msg, 0, NULL, 0)) {
+	    wps_build_wfa_ext(msg, 0, NULL, 0) ||
+	    wps_build_vendor_ext_m1(&wps->wps->dev, msg)) {
 		wpabuf_free(msg);
 		return NULL;
 	}
diff --git a/src/wps/wps_er.c b/src/wps/wps_er.c
index 0655a3a..95a0dec 100644
--- a/src/wps/wps_er.c
+++ b/src/wps/wps_er.c
@@ -1,6 +1,6 @@
 /*
  * Wi-Fi Protected Setup - External Registrar
- * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -1996,3 +1996,41 @@
 
 	return 0;
 }
+
+
+#ifdef CONFIG_WPS_NFC
+struct wpabuf * wps_er_nfc_config_token(struct wps_er *er, const u8 *uuid)
+{
+	struct wps_er_ap *ap;
+	struct wpabuf *ret;
+	struct wps_data data;
+
+	if (er == NULL)
+		return NULL;
+
+	ap = wps_er_ap_get(er, NULL, uuid);
+	if (ap == NULL)
+		return NULL;
+	if (ap->ap_settings == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS ER: No settings known for the "
+			   "selected AP");
+		return NULL;
+	}
+
+	ret = wpabuf_alloc(500);
+	if (ret == NULL)
+		return NULL;
+
+	os_memset(&data, 0, sizeof(data));
+	data.wps = er->wps;
+	data.use_cred = ap->ap_settings;
+	if (wps_build_version(ret) ||
+	    wps_build_cred(&data, ret) ||
+	    wps_build_wfa_ext(ret, 0, NULL, 0)) {
+		wpabuf_free(ret);
+		return NULL;
+	}
+
+	return ret;
+}
+#endif /* CONFIG_WPS_NFC */
diff --git a/src/wps/wps_i.h b/src/wps/wps_i.h
index 1297f65..86ad248 100644
--- a/src/wps/wps_i.h
+++ b/src/wps/wps_i.h
@@ -1,6 +1,6 @@
 /*
  * Wi-Fi Protected Setup - internal definitions
- * Copyright (c) 2008-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -10,6 +10,9 @@
 #define WPS_I_H
 
 #include "wps.h"
+#include "wps_attr_parse.h"
+
+struct wps_nfc_pw_token;
 
 /**
  * struct wps_data - WPS registration protocol data
@@ -114,100 +117,11 @@
 	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;
+
+	struct wps_nfc_pw_token *nfc_pw_token;
 };
 
 
-struct wps_parse_attr {
-	/* fixed length fields */
-	const u8 *version; /* 1 octet */
-	const u8 *version2; /* 1 octet */
-	const u8 *msg_type; /* 1 octet */
-	const u8 *enrollee_nonce; /* WPS_NONCE_LEN (16) octets */
-	const u8 *registrar_nonce; /* WPS_NONCE_LEN (16) octets */
-	const u8 *uuid_r; /* WPS_UUID_LEN (16) octets */
-	const u8 *uuid_e; /* WPS_UUID_LEN (16) octets */
-	const u8 *auth_type_flags; /* 2 octets */
-	const u8 *encr_type_flags; /* 2 octets */
-	const u8 *conn_type_flags; /* 1 octet */
-	const u8 *config_methods; /* 2 octets */
-	const u8 *sel_reg_config_methods; /* 2 octets */
-	const u8 *primary_dev_type; /* 8 octets */
-	const u8 *rf_bands; /* 1 octet */
-	const u8 *assoc_state; /* 2 octets */
-	const u8 *config_error; /* 2 octets */
-	const u8 *dev_password_id; /* 2 octets */
-	const u8 *oob_dev_password; /* WPS_OOB_DEVICE_PASSWORD_ATTR_LEN (54)
-				     * octets */
-	const u8 *os_version; /* 4 octets */
-	const u8 *wps_state; /* 1 octet */
-	const u8 *authenticator; /* WPS_AUTHENTICATOR_LEN (8) octets */
-	const u8 *r_hash1; /* WPS_HASH_LEN (32) octets */
-	const u8 *r_hash2; /* WPS_HASH_LEN (32) octets */
-	const u8 *e_hash1; /* WPS_HASH_LEN (32) octets */
-	const u8 *e_hash2; /* WPS_HASH_LEN (32) octets */
-	const u8 *r_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */
-	const u8 *r_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */
-	const u8 *e_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */
-	const u8 *e_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */
-	const u8 *key_wrap_auth; /* WPS_KWA_LEN (8) octets */
-	const u8 *auth_type; /* 2 octets */
-	const u8 *encr_type; /* 2 octets */
-	const u8 *network_idx; /* 1 octet */
-	const u8 *network_key_idx; /* 1 octet */
-	const u8 *mac_addr; /* ETH_ALEN (6) octets */
-	const u8 *key_prov_auto; /* 1 octet (Bool) */
-	const u8 *dot1x_enabled; /* 1 octet (Bool) */
-	const u8 *selected_registrar; /* 1 octet (Bool) */
-	const u8 *request_type; /* 1 octet */
-	const u8 *response_type; /* 1 octet */
-	const u8 *ap_setup_locked; /* 1 octet */
-	const u8 *settings_delay_time; /* 1 octet */
-	const u8 *network_key_shareable; /* 1 octet (Bool) */
-	const u8 *request_to_enroll; /* 1 octet (Bool) */
-
-	/* variable length fields */
-	const u8 *manufacturer;
-	size_t manufacturer_len;
-	const u8 *model_name;
-	size_t model_name_len;
-	const u8 *model_number;
-	size_t model_number_len;
-	const u8 *serial_number;
-	size_t serial_number_len;
-	const u8 *dev_name;
-	size_t dev_name_len;
-	const u8 *public_key;
-	size_t public_key_len;
-	const u8 *encr_settings;
-	size_t encr_settings_len;
-	const u8 *ssid; /* <= 32 octets */
-	size_t ssid_len;
-	const u8 *network_key; /* <= 64 octets */
-	size_t network_key_len;
-	const u8 *eap_type; /* <= 8 octets */
-	size_t eap_type_len;
-	const u8 *eap_identity; /* <= 64 octets */
-	size_t eap_identity_len;
-	const u8 *authorized_macs; /* <= 30 octets */
-	size_t authorized_macs_len;
-	const u8 *sec_dev_type_list; /* <= 128 octets */
-	size_t sec_dev_type_list_len;
-
-	/* attributes that can occur multiple times */
-#define MAX_CRED_COUNT 10
-	const u8 *cred[MAX_CRED_COUNT];
-	size_t cred_len[MAX_CRED_COUNT];
-	size_t num_cred;
-
-#define MAX_REQ_DEV_TYPE_COUNT 10
-	const u8 *req_dev_type[MAX_REQ_DEV_TYPE_COUNT];
-	size_t num_req_dev_type;
-
-	const u8 *vendor_ext[MAX_WPS_PARSE_VENDOR_EXT];
-	size_t vendor_ext_len[MAX_WPS_PARSE_VENDOR_EXT];
-	size_t num_vendor_ext;
-};
-
 /* wps_common.c */
 void wps_kdf(const u8 *key, const u8 *label_prefix, size_t label_prefix_len,
 	     const char *label, u8 *res, size_t res_len);
@@ -230,9 +144,6 @@
 struct wpabuf * wps_build_wsc_ack(struct wps_data *wps);
 struct wpabuf * wps_build_wsc_nack(struct wps_data *wps);
 
-/* wps_attr_parse.c */
-int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr);
-
 /* wps_attr_build.c */
 int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg);
 int wps_build_req_type(struct wpabuf *msg, enum wps_request_type type);
@@ -255,6 +166,9 @@
 int wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg);
 int wps_build_conn_type_flags(struct wps_data *wps, struct wpabuf *msg);
 int wps_build_assoc_state(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_oob_dev_pw(struct wpabuf *msg, u16 dev_pw_id,
+			 const struct wpabuf *pubkey, const u8 *dev_pw,
+			 size_t dev_pw_len);
 int wps_build_oob_dev_password(struct wpabuf *msg, struct wps_context *wps);
 struct wpabuf * wps_ie_encapsulate(struct wpabuf *data);
 
@@ -288,9 +202,7 @@
 const u8 * wps_authorized_macs(struct wps_registrar *reg, size_t *count);
 int wps_registrar_pbc_overlap(struct wps_registrar *reg,
 			      const u8 *addr, const u8 *uuid_e);
-
-/* ndef.c */
-struct wpabuf * ndef_parse_wifi(struct wpabuf *buf);
-struct wpabuf * ndef_build_wifi(struct wpabuf *buf);
+void wps_registrar_remove_nfc_pw_token(struct wps_registrar *reg,
+				       struct wps_nfc_pw_token *token);
 
 #endif /* WPS_I_H */
diff --git a/src/wps/wps_nfc.c b/src/wps/wps_nfc.c
index ff12000..6804350 100644
--- a/src/wps/wps_nfc.c
+++ b/src/wps/wps_nfc.c
@@ -1,15 +1,9 @@
 /*
  * NFC routines for Wi-Fi Protected Setup
- * Copyright (c) 2009, Masashi Honma <honma@ictec.co.jp>
+ * Copyright (c) 2009-2012, Masashi Honma <masashi.honma@gmail.com>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "includes.h"
diff --git a/src/wps/wps_nfc_pn531.c b/src/wps/wps_nfc_pn531.c
index 7e05e4d..c2bf457 100644
--- a/src/wps/wps_nfc_pn531.c
+++ b/src/wps/wps_nfc_pn531.c
@@ -1,15 +1,9 @@
 /*
  * NFC PN531 routines for Wi-Fi Protected Setup
- * Copyright (c) 2009, Masashi Honma <honma@ictec.co.jp>
+ * Copyright (c) 2009-2012, Masashi Honma <masashi.honma@gmail.com>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "includes.h"
diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c
index 5ed7ea4..44bb006 100644
--- a/src/wps/wps_registrar.c
+++ b/src/wps/wps_registrar.c
@@ -1,6 +1,6 @@
 /*
  * Wi-Fi Protected Setup - Registrar
- * Copyright (c) 2008-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -26,6 +26,53 @@
 #define WPS_WORKAROUNDS
 #endif /* CONFIG_WPS_STRICT */
 
+#ifdef CONFIG_WPS_NFC
+
+struct wps_nfc_pw_token {
+	struct dl_list list;
+	u8 pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN];
+	u16 pw_id;
+	u8 dev_pw[WPS_OOB_DEVICE_PASSWORD_LEN];
+	size_t dev_pw_len;
+};
+
+
+static void wps_remove_nfc_pw_token(struct wps_nfc_pw_token *token)
+{
+	dl_list_del(&token->list);
+	os_free(token);
+}
+
+
+static void wps_free_nfc_pw_tokens(struct dl_list *tokens, u16 pw_id)
+{
+	struct wps_nfc_pw_token *token, *prev;
+	dl_list_for_each_safe(token, prev, tokens, struct wps_nfc_pw_token,
+			      list) {
+		if (pw_id == 0 || pw_id == token->pw_id)
+			wps_remove_nfc_pw_token(token);
+	}
+}
+
+
+static struct wps_nfc_pw_token * wps_get_nfc_pw_token(struct dl_list *tokens,
+						      u16 pw_id)
+{
+	struct wps_nfc_pw_token *token;
+	dl_list_for_each(token, tokens, struct wps_nfc_pw_token, list) {
+		if (pw_id == token->pw_id)
+			return token;
+	}
+	return NULL;
+}
+
+#else /* CONFIG_WPS_NFC */
+
+#define wps_free_nfc_pw_tokens(t, p) do { } while (0)
+
+#endif /* CONFIG_WPS_NFC */
+
+
 struct wps_uuid_pin {
 	struct dl_list list;
 	u8 uuid[WPS_UUID_LEN];
@@ -102,7 +149,8 @@
 	void (*pin_needed_cb)(void *ctx, const u8 *uuid_e,
 			      const struct wps_device_data *dev);
 	void (*reg_success_cb)(void *ctx, const u8 *mac_addr,
-			       const u8 *uuid_e);
+			       const u8 *uuid_e, const u8 *dev_pw,
+			       size_t dev_pw_len);
 	void (*set_sel_reg_cb)(void *ctx, int sel_reg, u16 dev_passwd_id,
 			       u16 sel_reg_config_methods);
 	void (*enrollee_seen_cb)(void *ctx, const u8 *addr, const u8 *uuid_e,
@@ -112,6 +160,7 @@
 	void *cb_ctx;
 
 	struct dl_list pins;
+	struct dl_list nfc_pw_tokens;
 	struct wps_pbc_session *pbc_sessions;
 
 	int skip_cred_build;
@@ -484,12 +533,16 @@
 {
 	*methods |= WPS_CONFIG_PUSHBUTTON;
 #ifdef CONFIG_WPS2
-	if (conf_methods & WPS_CONFIG_VIRT_PUSHBUTTON)
+	if ((conf_methods & WPS_CONFIG_VIRT_PUSHBUTTON) ==
+	    WPS_CONFIG_VIRT_PUSHBUTTON)
 		*methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
-	if (conf_methods & WPS_CONFIG_PHY_PUSHBUTTON)
+	if ((conf_methods & WPS_CONFIG_PHY_PUSHBUTTON) ==
+	    WPS_CONFIG_PHY_PUSHBUTTON)
 		*methods |= WPS_CONFIG_PHY_PUSHBUTTON;
-	if (!(*methods & (WPS_CONFIG_VIRT_PUSHBUTTON |
-			  WPS_CONFIG_PHY_PUSHBUTTON))) {
+	if ((*methods & WPS_CONFIG_VIRT_PUSHBUTTON) !=
+	    WPS_CONFIG_VIRT_PUSHBUTTON &&
+	    (*methods & WPS_CONFIG_PHY_PUSHBUTTON) !=
+	    WPS_CONFIG_PHY_PUSHBUTTON) {
 		/*
 		 * Required to include virtual/physical flag, but we were not
 		 * configured with push button type, so have to default to one
@@ -591,6 +644,7 @@
 		return NULL;
 
 	dl_list_init(&reg->pins);
+	dl_list_init(&reg->nfc_pw_tokens);
 	reg->wps = wps;
 	reg->new_psk_cb = cfg->new_psk_cb;
 	reg->set_ie_cb = cfg->set_ie_cb;
@@ -634,6 +688,7 @@
 	eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
 	eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
 	wps_free_pins(&reg->pins);
+	wps_free_nfc_pw_tokens(&reg->nfc_pw_tokens, 0);
 	wps_free_pbc_sessions(reg->pbc_sessions);
 	wpabuf_free(reg->extra_cred);
 	wps_free_devices(reg->devices);
@@ -740,14 +795,22 @@
 /**
  * wps_registrar_invalidate_wildcard_pin - Invalidate a wildcard PIN
  * @reg: Registrar data from wps_registrar_init()
+ * @dev_pw: PIN to search for or %NULL to match any
+ * @dev_pw_len: Length of dev_pw in octets
  * Returns: 0 on success, -1 if not wildcard PIN is enabled
  */
-static int wps_registrar_invalidate_wildcard_pin(struct wps_registrar *reg)
+static int wps_registrar_invalidate_wildcard_pin(struct wps_registrar *reg,
+						 const u8 *dev_pw,
+						 size_t dev_pw_len)
 {
 	struct wps_uuid_pin *pin, *prev;
 
 	dl_list_for_each_safe(pin, prev, &reg->pins, struct wps_uuid_pin, list)
 	{
+		if (dev_pw && pin->pin &&
+		    (dev_pw_len != pin->pin_len ||
+		     os_memcmp(dev_pw, pin->pin, dev_pw_len) != 0))
+			continue; /* different PIN */
 		if (pin->wildcard_uuid) {
 			wpa_hexdump(MSG_DEBUG, "WPS: Invalidated PIN for UUID",
 				    pin->uuid, WPS_UUID_LEN);
@@ -945,7 +1008,8 @@
 }
 
 
-void wps_registrar_complete(struct wps_registrar *registrar, const u8 *uuid_e)
+void wps_registrar_complete(struct wps_registrar *registrar, const u8 *uuid_e,
+			    const u8 *dev_pw, size_t dev_pw_len)
 {
 	if (registrar->pbc) {
 		wps_registrar_remove_pbc_session(registrar,
@@ -954,6 +1018,13 @@
 	} else {
 		wps_registrar_pin_completed(registrar);
 	}
+
+	if (dev_pw &&
+	    wps_registrar_invalidate_wildcard_pin(registrar, dev_pw,
+						  dev_pw_len) == 0) {
+		wpa_hexdump_key(MSG_DEBUG, "WPS: Invalidated wildcard PIN",
+				dev_pw, dev_pw_len);
+	}
 }
 
 
@@ -968,7 +1039,7 @@
 		/* PIN Method */
 		wpa_printf(MSG_DEBUG, "WPS: PIN is set - cancelling it");
 		wps_registrar_pin_completed(reg);
-		wps_registrar_invalidate_wildcard_pin(reg);
+		wps_registrar_invalidate_wildcard_pin(reg, NULL, 0);
 		return 1;
 	}
 	return 0;
@@ -1071,12 +1142,13 @@
 
 
 static void wps_cb_reg_success(struct wps_registrar *reg, const u8 *mac_addr,
-			       const u8 *uuid_e)
+			       const u8 *uuid_e, const u8 *dev_pw,
+			       size_t dev_pw_len)
 {
 	if (reg->reg_success_cb == NULL)
 		return;
 
-	reg->reg_success_cb(reg->cb_ctx, mac_addr, uuid_e);
+	reg->reg_success_cb(reg->cb_ctx, mac_addr, uuid_e, dev_pw, dev_pw_len);
 }
 
 
@@ -1238,6 +1310,13 @@
 		wpa_printf(MSG_DEBUG, "WPS: Use default PIN for PBC");
 		pin = (const u8 *) "00000000";
 		pin_len = 8;
+#ifdef CONFIG_WPS_NFC
+	} else if (wps->nfc_pw_token) {
+		wpa_printf(MSG_DEBUG, "WPS: Use OOB Device Password from NFC "
+			   "Password Token");
+		pin = wps->nfc_pw_token->dev_pw;
+		pin_len = wps->nfc_pw_token->dev_pw_len;
+#endif /* CONFIG_WPS_NFC */
 	} else {
 		pin = wps_registrar_get_pin(wps->wps->registrar, wps->uuid_e,
 					    &pin_len);
@@ -2424,8 +2503,34 @@
 		return WPS_CONTINUE;
 	}
 
+#ifdef CONFIG_WPS_NFC
+	if (wps->dev_pw_id >= 0x10) {
+		struct wps_nfc_pw_token *token;
+		const u8 *addr[1];
+		u8 hash[WPS_HASH_LEN];
+
+		token = wps_get_nfc_pw_token(
+			&wps->wps->registrar->nfc_pw_tokens, wps->dev_pw_id);
+		if (token) {
+			wpa_printf(MSG_DEBUG, "WPS: Found matching NFC "
+				   "Password Token");
+			dl_list_del(&token->list);
+			wps->nfc_pw_token = token;
+
+			addr[0] = attr->public_key;
+			sha256_vector(1, addr, &attr->public_key_len, hash);
+			if (os_memcmp(hash, wps->nfc_pw_token->pubkey_hash,
+				      WPS_OOB_PUBKEY_HASH_LEN) != 0) {
+				wpa_printf(MSG_ERROR, "WPS: Public Key hash "
+					   "mismatch");
+				return WPS_FAILURE;
+			}
+		}
+	}
+#endif /* CONFIG_WPS_NFC */
+
 #ifdef CONFIG_WPS_OOB
-	if (wps->dev_pw_id >= 0x10 &&
+	if (wps->dev_pw_id >= 0x10 && wps->nfc_pw_token == NULL &&
 	    wps->dev_pw_id != wps->wps->oob_dev_pw_id) {
 		wpa_printf(MSG_DEBUG, "WPS: OOB Device Password ID "
 			   "%d mismatch", wps->dev_pw_id);
@@ -3055,7 +3160,8 @@
 		wps->new_psk = NULL;
 	}
 
-	wps_cb_reg_success(wps->wps->registrar, wps->mac_addr_e, wps->uuid_e);
+	wps_cb_reg_success(wps->wps->registrar, wps->mac_addr_e, wps->uuid_e,
+			   wps->dev_password, wps->dev_password_len);
 
 	if (wps->pbc) {
 		wps_registrar_remove_pbc_session(wps->wps->registrar,
@@ -3348,3 +3454,84 @@
 
 	return -1;
 }
+
+
+#ifdef CONFIG_WPS_NFC
+
+int wps_registrar_add_nfc_pw_token(struct wps_registrar *reg,
+				   const u8 *pubkey_hash, u16 pw_id,
+				   const u8 *dev_pw, size_t dev_pw_len)
+{
+	struct wps_nfc_pw_token *token;
+
+	if (dev_pw_len > WPS_OOB_DEVICE_PASSWORD_LEN)
+		return -1;
+
+	wps_free_nfc_pw_tokens(&reg->nfc_pw_tokens, pw_id);
+
+	token = os_zalloc(sizeof(*token));
+	if (token == NULL)
+		return -1;
+
+	os_memcpy(token->pubkey_hash, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
+	token->pw_id = pw_id;
+	os_memcpy(token->dev_pw, dev_pw, dev_pw_len);
+	token->dev_pw_len = dev_pw_len;
+
+	dl_list_add(&reg->nfc_pw_tokens, &token->list);
+
+	reg->selected_registrar = 1;
+	reg->pbc = 0;
+	wps_registrar_add_authorized_mac(reg,
+					 (u8 *) "\xff\xff\xff\xff\xff\xff");
+	wps_registrar_selected_registrar_changed(reg);
+	eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
+	eloop_register_timeout(WPS_PBC_WALK_TIME, 0,
+			       wps_registrar_set_selected_timeout,
+			       reg, NULL);
+
+	return 0;
+}
+
+
+int wps_registrar_add_nfc_password_token(struct wps_registrar *reg,
+					 const u8 *oob_dev_pw,
+					 size_t oob_dev_pw_len)
+{
+	const u8 *pos, *hash, *dev_pw;
+	u16 id;
+	size_t dev_pw_len;
+
+	if (oob_dev_pw_len < WPS_OOB_PUBKEY_HASH_LEN + 2 +
+	    WPS_OOB_DEVICE_PASSWORD_MIN_LEN ||
+	    oob_dev_pw_len > WPS_OOB_PUBKEY_HASH_LEN + 2 +
+	    WPS_OOB_DEVICE_PASSWORD_LEN)
+		return -1;
+
+	hash = oob_dev_pw;
+	pos = oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN;
+	id = WPA_GET_BE16(pos);
+	dev_pw = pos + 2;
+	dev_pw_len = oob_dev_pw + oob_dev_pw_len - dev_pw;
+
+	wpa_printf(MSG_DEBUG, "WPS: Add NFC Password Token for Password ID %u",
+		   id);
+
+	wpa_hexdump(MSG_DEBUG, "WPS: Public Key Hash",
+		    hash, WPS_OOB_PUBKEY_HASH_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "WPS: Device Password", dev_pw, dev_pw_len);
+
+	return wps_registrar_add_nfc_pw_token(reg, hash, id, dev_pw,
+					      dev_pw_len);
+}
+
+
+void wps_registrar_remove_nfc_pw_token(struct wps_registrar *reg,
+				       struct wps_nfc_pw_token *token)
+{
+	wps_registrar_remove_authorized_mac(reg,
+					    (u8 *) "\xff\xff\xff\xff\xff\xff");
+	wps_registrar_selected_registrar_changed(reg);
+}
+
+#endif /* CONFIG_WPS_NFC */
diff --git a/src/wps/wps_ufd.c b/src/wps/wps_ufd.c
index 61f6553..f83bdf4 100644
--- a/src/wps/wps_ufd.c
+++ b/src/wps/wps_ufd.c
@@ -1,15 +1,9 @@
 /*
  * UFD routines for Wi-Fi Protected Setup
- * Copyright (c) 2009, Masashi Honma <honma@ictec.co.jp>
+ * Copyright (c) 2009-2012, Masashi Honma <masashi.honma@gmail.com>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "includes.h"
@@ -165,8 +159,10 @@
 	}
 
 	data = os_zalloc(sizeof(*data));
-	if (data == NULL)
+	if (data == NULL) {
+		close(ufd_fd);
 		return NULL;
+	}
 	data->ufd_fd = ufd_fd;
 	return data;
 }
diff --git a/src/wps/wps_upnp.c b/src/wps/wps_upnp.c
index 766cac4..09a46a2 100644
--- a/src/wps/wps_upnp.c
+++ b/src/wps/wps_upnp.c
@@ -305,15 +305,15 @@
 	int alloc_len;
 	char *scratch_mem = NULL;
 	char *mem;
-	char *domain_and_port;
+	char *host;
 	char *delim;
 	char *path;
-	char *domain;
 	int port = 80;  /* port to send to (default is port 80) */
 	struct addrinfo hints;
 	struct addrinfo *result = NULL;
 	struct addrinfo *rp;
 	int rerr;
+	size_t host_len, path_len;
 
 	/* url MUST begin with http: */
 	if (url_len < 7 || os_strncasecmp(url, "http://", 7))
@@ -321,30 +321,24 @@
 	url += 7;
 	url_len -= 7;
 
-	/* allocate memory for the extra stuff we need */
-	alloc_len = 2 * (url_len + 1);
-	scratch_mem = os_zalloc(alloc_len);
+	/* Make a copy of the string to allow modification during parsing */
+	scratch_mem = os_malloc(url_len + 1);
 	if (scratch_mem == NULL)
 		goto fail;
-	mem = scratch_mem;
-	os_strncpy(mem, url, url_len);
-	wpa_printf(MSG_DEBUG, "WPS UPnP: Adding URL '%s'", mem);
-	domain_and_port = mem;
-	mem += 1 + os_strlen(mem);
-	delim = os_strchr(domain_and_port, '/');
+	os_memcpy(scratch_mem, url, url_len);
+	scratch_mem[url_len] = '\0';
+	wpa_printf(MSG_DEBUG, "WPS UPnP: Adding URL '%s'", scratch_mem);
+	host = scratch_mem;
+	path = os_strchr(host, '/');
+	if (path)
+		*path++ = '\0'; /* null terminate host */
+
+	/* Process and remove optional port component */
+	delim = os_strchr(host, ':');
 	if (delim) {
-		*delim++ = 0;   /* null terminate domain and port */
-		path = delim;
-	} else {
-		path = domain_and_port + os_strlen(domain_and_port);
-	}
-	domain = mem;
-	strcpy(domain, domain_and_port);
-	delim = os_strchr(domain, ':');
-	if (delim) {
-		*delim++ = 0;   /* null terminate domain */
-		if (isdigit(*delim))
-			port = atol(delim);
+		*delim = '\0'; /* null terminate host name for now */
+		if (isdigit(delim[1]))
+			port = atol(delim + 1);
 	}
 
 	/*
@@ -367,13 +361,21 @@
 	hints.ai_flags = 0;
 #endif
 	hints.ai_protocol = 0;          /* Any protocol? */
-	rerr = getaddrinfo(domain, NULL /* fill in port ourselves */,
+	rerr = getaddrinfo(host, NULL /* fill in port ourselves */,
 			   &hints, &result);
 	if (rerr) {
 		wpa_printf(MSG_INFO, "WPS UPnP: Resolve error %d (%s) on: %s",
-			   rerr, gai_strerror(rerr), domain);
+			   rerr, gai_strerror(rerr), host);
 		goto fail;
 	}
+
+	if (delim)
+		*delim = ':'; /* Restore port */
+
+	host_len = os_strlen(host);
+	path_len = path ? os_strlen(path) : 0;
+	alloc_len = host_len + 1 + 1 + path_len + 1;
+
 	for (rp = result; rp; rp = rp->ai_next) {
 		struct subscr_addr *a;
 
@@ -386,16 +388,16 @@
 
 		a = os_zalloc(sizeof(*a) + alloc_len);
 		if (a == NULL)
-			continue;
-		mem = (void *) (a + 1);
+			break;
+		mem = (char *) (a + 1);
 		a->domain_and_port = mem;
-		strcpy(mem, domain_and_port);
-		mem += 1 + strlen(mem);
+		os_memcpy(mem, host, host_len);
+		mem += host_len + 1;
 		a->path = mem;
-		if (path[0] != '/')
+		if (path == NULL || path[0] != '/')
 			*mem++ = '/';
-		strcpy(mem, path);
-		mem += 1 + os_strlen(mem);
+		if (path)
+			os_memcpy(mem, path, path_len);
 		os_memcpy(&a->saddr, rp->ai_addr, sizeof(a->saddr));
 		a->saddr.sin_port = htons(port);
 
diff --git a/src/wps/wps_upnp_ssdp.c b/src/wps/wps_upnp_ssdp.c
index 4c4aebf..17a8207 100644
--- a/src/wps/wps_upnp_ssdp.c
+++ b/src/wps/wps_upnp_ssdp.c
@@ -866,20 +866,24 @@
 		return -1;
 
 #if 0   /* maybe ok if we sometimes block on writes */
-	if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0)
+	if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0) {
+		close(sd);
 		return -1;
+	}
 #endif
 
 	if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF,
 		       &ip_addr, sizeof(ip_addr))) {
 		wpa_printf(MSG_DEBUG, "WPS: setsockopt(IP_MULTICAST_IF) %x: "
 			   "%d (%s)", ip_addr, errno, strerror(errno));
+		close(sd);
 		return -1;
 	}
 	if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL,
 		       &ttl, sizeof(ttl))) {
 		wpa_printf(MSG_DEBUG, "WPS: setsockopt(IP_MULTICAST_TTL): "
 			   "%d (%s)", errno, strerror(errno));
+		close(sd);
 		return -1;
 	}
 
@@ -898,6 +902,7 @@
 				   "WPS UPnP: setsockopt "
 				   "IP_ADD_MEMBERSHIP errno %d (%s)",
 				   errno, strerror(errno));
+			close(sd);
 			return -1;
 		}
 	}