Accumulative patch from commit dc013f1e37df3462085cf01a13f0c432f146ad7a

Author: Jouni Malinen <jouni@qca.qualcomm.com>
Date:   Tue Jan 15 12:03:29 2013 +0200
    eapol_test: Remove unnecessary header file inclusion

 - P2P: Send P2P-FIND-STOPPED event in the new continue-search states
 - P2P: Add some more details on Service Query TLV format
 - P2P: Use the same Dialog Token value for every GO Negotiation retry
 - P2P: Publish more connected clients info in Probe Response frames
 - P2P: Fix some memory leaks in p2p_add_device()
 - P2P: Use the same Dialog Token value for every PD retry
 - P2P: Document operating channel selection functions
 - P2P: Always re-select operating channel if not hard coded
 - P2P: Do not allow re-selection of GO channel if forced_freq in use
 - P2P: Set FORCE_FREQ flag as part of p2p_prepare_channel()
 - P2P: Share a single function for GO channel selection
 - P2P: Prefer operating channels where HT40 is possible
 - P2P: Be more careful with wpa_config_update_psk() call
 - P2P: Allow PSK to be used instead of passphrase for persistent GO
 - P2P: Consider age for the P2P scan results
 - Move some P2P offchannel operations to offchannel.c
 - P2P: Add more complete description of p2p_cancel
 - P2P: Allow p2p_cancel to be used to stop p2p_connect-join operation
 - Interworking changes
 - WNM changes
 - WPS changes
 - SAE changes

Change-Id: I38b847d3460066cc58aecbcf67266bfcff1d344e
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/hostapd/Android.mk b/hostapd/Android.mk
index 9b576aa..16417ea 100644
--- a/hostapd/Android.mk
+++ b/hostapd/Android.mk
@@ -217,10 +217,13 @@
 
 ifdef CONFIG_SAE
 L_CFLAGS += -DCONFIG_SAE
+OBJS += src/common/sae.c
+NEED_ECC=y
+NEED_DH_GROUPS=y
 endif
 
-ifdef CONFIG_IEEE80211V
-L_CFLAGS += -DCONFIG_IEEE80211V
+ifdef CONFIG_WNM
+L_CFLAGS += -DCONFIG_WNM
 OBJS += src/ap/wnm_ap.c
 endif
 
@@ -228,10 +231,6 @@
 L_CFLAGS += -DCONFIG_IEEE80211N
 endif
 
-ifdef CONFIG_WNM
-L_CFLAGS += -DCONFIG_WNM
-endif
-
 ifdef CONFIG_IEEE80211AC
 L_CFLAGS += -DCONFIG_IEEE80211AC
 endif
@@ -525,6 +524,10 @@
 ifdef TLS_FUNCS
 OBJS += src/crypto/tls_gnutls.c
 LIBS += -lgnutls -lgpg-error
+ifdef CONFIG_GNUTLS_EXTRA
+L_CFLAGS += -DCONFIG_GNUTLS_EXTRA
+LIBS += -lgnutls-extra
+endif
 endif
 OBJS += src/crypto/crypto_gnutls.c
 HOBJS += src/crypto/crypto_gnutls.c
@@ -779,6 +782,10 @@
 endif
 endif
 
+ifdef NEED_ECC
+L_CFLAGS += -DCONFIG_ECC
+endif
+
 ifdef CONFIG_NO_RANDOM_POOL
 L_CFLAGS += -DCONFIG_NO_RANDOM_POOL
 else
diff --git a/hostapd/ChangeLog b/hostapd/ChangeLog
index e739325..1a4e566 100644
--- a/hostapd/ChangeLog
+++ b/hostapd/ChangeLog
@@ -1,6 +1,10 @@
 ChangeLog for hostapd
 
-????-??-?? - v2.0
+????-??-?? - v2.1
+	* added support for simulataneous authentication of equals (SAE) for
+	  stronger password-based authentication with WPA2-Personal
+
+2013-01-12 - v2.0
 	* added AP-STA-DISCONNECTED ctrl_iface event
 	* improved debug logging (human readable event names, interface name
 	  included in more entries)
@@ -89,6 +93,11 @@
 	* added a workaround for WPS PBC session overlap detection to avoid
 	  interop issues with deployed station implementations that do not
 	  remove active PBC indication from Probe Request frames properly
+	* added support for using SQLite for the eap_user database
+	* added Acct-Session-Id attribute into Access-Request messages
+	* fixed EAPOL frame transmission to non-QoS STAs with nl80211
+	  (do not send QoS frames if the STA did not negotiate use of QoS for
+	  this association)
 
 2012-05-10 - v1.0
 	* Add channel selection support in hostapd. See hostapd.conf.
diff --git a/hostapd/Makefile b/hostapd/Makefile
index 4cc3805..8404e0c 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -174,10 +174,13 @@
 
 ifdef CONFIG_SAE
 CFLAGS += -DCONFIG_SAE
+OBJS += ../src/common/sae.o
+NEED_ECC=y
+NEED_DH_GROUPS=y
 endif
 
-ifdef CONFIG_IEEE80211V
-CFLAGS += -DCONFIG_IEEE80211V
+ifdef CONFIG_WNM
+CFLAGS += -DCONFIG_WNM
 OBJS += ../src/ap/wnm_ap.o
 endif
 
@@ -185,10 +188,6 @@
 CFLAGS += -DCONFIG_IEEE80211N
 endif
 
-ifdef CONFIG_WNM
-CFLAGS += -DCONFIG_WNM
-endif
-
 ifdef CONFIG_IEEE80211AC
 CFLAGS += -DCONFIG_IEEE80211AC
 endif
@@ -734,6 +733,10 @@
 endif
 endif
 
+ifdef NEED_ECC
+CFLAGS += -DCONFIG_ECC
+endif
+
 ifdef CONFIG_NO_RANDOM_POOL
 CFLAGS += -DCONFIG_NO_RANDOM_POOL
 else
diff --git a/hostapd/README b/hostapd/README
index 34dad30..39b70ca 100644
--- a/hostapd/README
+++ b/hostapd/README
@@ -2,7 +2,7 @@
 	  Authenticator and RADIUS authentication server
 ================================================================
 
-Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi> and contributors
 All Rights Reserved.
 
 This program is licensed under the BSD license (the one with
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 8af8157..7b22dfd 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / Configuration file parser
- * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -661,49 +661,12 @@
 
 static int hostapd_config_parse_cipher(int line, const char *value)
 {
-	int val = 0, last;
-	char *start, *end, *buf;
-
-	buf = os_strdup(value);
-	if (buf == NULL)
+	int val = wpa_parse_cipher(value);
+	if (val < 0) {
+		wpa_printf(MSG_ERROR, "Line %d: invalid cipher '%s'.",
+			   line, value);
 		return -1;
-	start = buf;
-
-	while (*start != '\0') {
-		while (*start == ' ' || *start == '\t')
-			start++;
-		if (*start == '\0')
-			break;
-		end = start;
-		while (*end != ' ' && *end != '\t' && *end != '\0')
-			end++;
-		last = *end == '\0';
-		*end = '\0';
-		if (os_strcmp(start, "CCMP") == 0)
-			val |= WPA_CIPHER_CCMP;
-		else if (os_strcmp(start, "GCMP") == 0)
-			val |= WPA_CIPHER_GCMP;
-		else if (os_strcmp(start, "TKIP") == 0)
-			val |= WPA_CIPHER_TKIP;
-		else if (os_strcmp(start, "WEP104") == 0)
-			val |= WPA_CIPHER_WEP104;
-		else if (os_strcmp(start, "WEP40") == 0)
-			val |= WPA_CIPHER_WEP40;
-		else if (os_strcmp(start, "NONE") == 0)
-			val |= WPA_CIPHER_NONE;
-		else {
-			wpa_printf(MSG_ERROR, "Line %d: invalid cipher '%s'.",
-				   line, start);
-			os_free(buf);
-			return -1;
-		}
-
-		if (last)
-			break;
-		start = end + 1;
 	}
-	os_free(buf);
-
 	if (val == 0) {
 		wpa_printf(MSG_ERROR, "Line %d: no cipher values configured.",
 			   line);
@@ -1780,6 +1743,8 @@
 				bss->ssid.ssid_set = 1;
 			}
 			os_free(str);
+		} else if (os_strcmp(buf, "utf8_ssid") == 0) {
+			bss->ssid.utf8_ssid = atoi(pos) > 0;
 		} else if (os_strcmp(buf, "macaddr_acl") == 0) {
 			bss->macaddr_acl = atoi(pos);
 			if (bss->macaddr_acl != ACCEPT_UNLESS_DENIED &&
@@ -2311,6 +2276,8 @@
 				conf->hw_mode = HOSTAPD_MODE_IEEE80211B;
 			else if (os_strcmp(pos, "g") == 0)
 				conf->hw_mode = HOSTAPD_MODE_IEEE80211G;
+			else if (os_strcmp(pos, "ad") == 0)
+				conf->hw_mode = HOSTAPD_MODE_IEEE80211AD;
 			else {
 				wpa_printf(MSG_ERROR, "Line %d: unknown "
 					   "hw_mode '%s'", line, pos);
@@ -2717,6 +2684,12 @@
 			bss->time_zone = os_strdup(pos);
 			if (bss->time_zone == NULL)
 				errors++;
+#ifdef CONFIG_WNM
+		} else if (os_strcmp(buf, "wnm_sleep_mode") == 0) {
+			bss->wnm_sleep_mode = atoi(pos);
+		} else if (os_strcmp(buf, "bss_transition") == 0) {
+			bss->bss_transition = atoi(pos);
+#endif /* CONFIG_WNM */
 #ifdef CONFIG_INTERWORKING
 		} else if (os_strcmp(buf, "interworking") == 0) {
 			bss->interworking = atoi(pos);
@@ -2925,6 +2898,14 @@
 
 			wpabuf_free(bss->vendor_elements);
 			bss->vendor_elements = elems;
+		} else if (os_strcmp(buf, "sae_anti_clogging_threshold") == 0) {
+			bss->sae_anti_clogging_threshold = atoi(pos);
+		} else if (os_strcmp(buf, "sae_groups") == 0) {
+			if (hostapd_parse_rates(&bss->sae_groups, pos)) {
+				wpa_printf(MSG_ERROR, "Line %d: Invalid "
+					   "sae_groups value '%s'", line, pos);
+				return 1;
+			}
 		} else {
 			wpa_printf(MSG_ERROR, "Line %d: unknown configuration "
 				   "item '%s'", line, buf);
@@ -2938,31 +2919,16 @@
 
 static void hostapd_set_security_params(struct hostapd_bss_config *bss)
 {
-	int pairwise;
-
 	if (bss->individual_wep_key_len == 0) {
 		/* individual keys are not use; can use key idx0 for
 		 * broadcast keys */
 		bss->broadcast_key_idx_min = 0;
 	}
 
-	/* Select group cipher based on the enabled pairwise cipher
-	 * suites */
-	pairwise = 0;
-	if (bss->wpa & 1)
-		pairwise |= bss->wpa_pairwise;
-	if (bss->wpa & 2) {
-		if (bss->rsn_pairwise == 0)
-			bss->rsn_pairwise = bss->wpa_pairwise;
-		pairwise |= bss->rsn_pairwise;
-	}
-	if (pairwise & WPA_CIPHER_TKIP)
-		bss->wpa_group = WPA_CIPHER_TKIP;
-	else if ((pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) ==
-		 WPA_CIPHER_GCMP)
-		bss->wpa_group = WPA_CIPHER_GCMP;
-	else
-		bss->wpa_group = WPA_CIPHER_CCMP;
+	if ((bss->wpa & 2) && bss->rsn_pairwise == 0)
+		bss->rsn_pairwise = bss->wpa_pairwise;
+	bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa, bss->wpa_pairwise,
+						    bss->rsn_pairwise);
 
 	bss->radius->auth_server = bss->radius->auth_servers;
 	bss->radius->acct_server = bss->radius->acct_servers;
diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index ccc018e..93b740e 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -436,6 +436,50 @@
 #endif /* CONFIG_WPS */
 
 
+#ifdef CONFIG_WNM
+
+static int hostapd_ctrl_iface_disassoc_imminent(struct hostapd_data *hapd,
+						const char *cmd)
+{
+	u8 addr[ETH_ALEN];
+	u8 buf[1000], *pos;
+	struct ieee80211_mgmt *mgmt;
+	int disassoc_timer;
+
+	if (hwaddr_aton(cmd, addr))
+		return -1;
+	if (cmd[17] != ' ')
+		return -1;
+	disassoc_timer = atoi(cmd + 17);
+
+	os_memset(buf, 0, sizeof(buf));
+	mgmt = (struct ieee80211_mgmt *) buf;
+	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					   WLAN_FC_STYPE_ACTION);
+	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.action.category = WLAN_ACTION_WNM;
+	mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
+	mgmt->u.action.u.bss_tm_req.dialog_token = 1;
+	mgmt->u.action.u.bss_tm_req.req_mode =
+		WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
+	mgmt->u.action.u.bss_tm_req.disassoc_timer =
+		host_to_le16(disassoc_timer);
+	mgmt->u.action.u.bss_tm_req.validity_interval = 0;
+
+	pos = mgmt->u.action.u.bss_tm_req.variable;
+
+	if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) {
+		wpa_printf(MSG_DEBUG, "Failed to send BSS Transition "
+			   "Management Request frame");
+		return -1;
+	}
+
+	return 0;
+}
+
+
 static int hostapd_ctrl_iface_ess_disassoc(struct hostapd_data *hapd,
 					   const char *cmd)
 {
@@ -486,6 +530,8 @@
 	return 0;
 }
 
+#endif /* CONFIG_WNM */
+
 
 static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd,
 					 char *buf, size_t buflen)
@@ -589,20 +635,9 @@
 		pos += ret;
 	}
 
-	if (hapd->conf->wpa && hapd->conf->wpa_group == WPA_CIPHER_CCMP) {
-		ret = os_snprintf(pos, end - pos, "group_cipher=CCMP\n");
-		if (ret < 0 || ret >= end - pos)
-			return pos - buf;
-		pos += ret;
-	} else if (hapd->conf->wpa &&
-		   hapd->conf->wpa_group == WPA_CIPHER_GCMP) {
-		ret = os_snprintf(pos, end - pos, "group_cipher=GCMP\n");
-		if (ret < 0 || ret >= end - pos)
-			return pos - buf;
-		pos += ret;
-	} else if (hapd->conf->wpa &&
-		   hapd->conf->wpa_group == WPA_CIPHER_TKIP) {
-		ret = os_snprintf(pos, end - pos, "group_cipher=TKIP\n");
+	if (hapd->conf->wpa) {
+		ret = os_snprintf(pos, end - pos, "group_cipher=%s\n",
+				  wpa_cipher_txt(hapd->conf->wpa_group));
 		if (ret < 0 || ret >= end - pos)
 			return pos - buf;
 		pos += ret;
@@ -614,24 +649,11 @@
 			return pos - buf;
 		pos += ret;
 
-		if (hapd->conf->rsn_pairwise & WPA_CIPHER_CCMP) {
-			ret = os_snprintf(pos, end - pos, "CCMP ");
-			if (ret < 0 || ret >= end - pos)
-				return pos - buf;
-			pos += ret;
-		}
-		if (hapd->conf->rsn_pairwise & WPA_CIPHER_GCMP) {
-			ret = os_snprintf(pos, end - pos, "GCMP ");
-			if (ret < 0 || ret >= end - pos)
-				return pos - buf;
-			pos += ret;
-		}
-		if (hapd->conf->rsn_pairwise & WPA_CIPHER_TKIP) {
-			ret = os_snprintf(pos, end - pos, "TKIP ");
-			if (ret < 0 || ret >= end - pos)
-				return pos - buf;
-			pos += ret;
-		}
+		ret = wpa_write_ciphers(pos, end, hapd->conf->rsn_pairwise,
+					" ");
+		if (ret < 0)
+			return pos - buf;
+		pos += ret;
 
 		ret = os_snprintf(pos, end - pos, "\n");
 		if (ret < 0 || ret >= end - pos)
@@ -645,24 +667,11 @@
 			return pos - buf;
 		pos += ret;
 
-		if (hapd->conf->wpa_pairwise & WPA_CIPHER_CCMP) {
-			ret = os_snprintf(pos, end - pos, "CCMP ");
-			if (ret < 0 || ret >= end - pos)
-				return pos - buf;
-			pos += ret;
-		}
-		if (hapd->conf->wpa_pairwise & WPA_CIPHER_GCMP) {
-			ret = os_snprintf(pos, end - pos, "GCMP ");
-			if (ret < 0 || ret >= end - pos)
-				return pos - buf;
-			pos += ret;
-		}
-		if (hapd->conf->wpa_pairwise & WPA_CIPHER_TKIP) {
-			ret = os_snprintf(pos, end - pos, "TKIP ");
-			if (ret < 0 || ret >= end - pos)
-				return pos - buf;
-			pos += ret;
-		}
+		ret = wpa_write_ciphers(pos, end, hapd->conf->rsn_pairwise,
+					" ");
+		if (ret < 0)
+			return pos - buf;
+		pos += ret;
 
 		ret = os_snprintf(pos, end - pos, "\n");
 		if (ret < 0 || ret >= end - pos)
@@ -906,9 +915,14 @@
 			hapd, buf + 14, reply, reply_size);
 #endif /* CONFIG_WPS_NFC */
 #endif /* CONFIG_WPS */
+#ifdef CONFIG_WNM
+	} else if (os_strncmp(buf, "DISASSOC_IMMINENT ", 18) == 0) {
+		if (hostapd_ctrl_iface_disassoc_imminent(hapd, buf + 18))
+			reply_len = -1;
 	} else if (os_strncmp(buf, "ESS_DISASSOC ", 13) == 0) {
 		if (hostapd_ctrl_iface_ess_disassoc(hapd, buf + 13))
 			reply_len = -1;
+#endif /* CONFIG_WNM */
 	} else if (os_strcmp(buf, "GET_CONFIG") == 0) {
 		reply_len = hostapd_ctrl_iface_get_config(hapd, reply,
 							  reply_size);
@@ -997,12 +1011,27 @@
 	}
 
 	if (hapd->conf->ctrl_interface_gid_set &&
-	    chown(hapd->conf->ctrl_interface, 0,
+	    chown(hapd->conf->ctrl_interface, -1,
 		  hapd->conf->ctrl_interface_gid) < 0) {
 		perror("chown[ctrl_interface]");
 		return -1;
 	}
 
+#ifdef ANDROID
+	/*
+	 * Android is using umask 0077 which would leave the control interface
+	 * directory without group access. This breaks things since Wi-Fi
+	 * framework assumes that this directory can be accessed by other
+	 * applications in the wifi group. Fix this by adding group access even
+	 * if umask value would prevent this.
+	 */
+	if (chmod(hapd->conf->ctrl_interface, S_IRWXU | S_IRWXG) < 0) {
+		wpa_printf(MSG_ERROR, "CTRL: Could not chmod directory: %s",
+			   strerror(errno));
+		/* Try to continue anyway */
+	}
+#endif /* ANDROID */
+
 	if (os_strlen(hapd->conf->ctrl_interface) + 1 +
 	    os_strlen(hapd->conf->iface) >= sizeof(addr.sun_path))
 		goto fail;
@@ -1055,7 +1084,7 @@
 	}
 
 	if (hapd->conf->ctrl_interface_gid_set &&
-	    chown(fname, 0, hapd->conf->ctrl_interface_gid) < 0) {
+	    chown(fname, -1, hapd->conf->ctrl_interface_gid) < 0) {
 		perror("chown[ctrl_interface/ifname]");
 		goto fail;
 	}
diff --git a/hostapd/dump_state.c b/hostapd/dump_state.c
index d33e05f..fcd9890 100644
--- a/hostapd/dump_state.c
+++ b/hostapd/dump_state.c
@@ -19,6 +19,7 @@
 #include "ap/ap_config.h"
 #include "ap/sta_info.h"
 #include "dump_state.h"
+#include "ap/ap_drv_ops.h"
 
 
 static void fprint_char(FILE *f, char c)
@@ -72,6 +73,7 @@
 #ifndef CONFIG_NO_RADIUS
 	char *buf;
 #endif /* CONFIG_NO_RADIUS */
+	struct hostap_sta_driver_data data;
 
 	if (!hapd->conf->dump_log_name) {
 		wpa_printf(MSG_DEBUG, "Dump file not defined - ignoring dump "
@@ -139,6 +141,13 @@
 			  "DEAUTH")));
 
 		ieee802_1x_dump_state(f, "  ", sta);
+
+		if (hostapd_drv_read_sta_data(hapd, &data, sta->addr) == 0) {
+			fprintf(f, "  rx_pkt=%lu tx_pkt=%lu\n"
+				"  rx_byte=%lu tx_byte=%lu\n",
+				data.rx_packets, data.tx_packets,
+				data.rx_bytes, data.tx_bytes);
+		}
 	}
 
 #ifndef CONFIG_NO_RADIUS
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index c839ad0..eca3996 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -90,6 +90,9 @@
 #ssid2=74657374
 #ssid2=P"hello\nthere"
 
+# UTF-8 SSID: Whether the SSID is to be interpreted using UTF-8 encoding
+#utf8_ssid=1
+
 # Country code (ISO/IEC 3166-1). Used to set regulatory domain.
 # Set as needed to indicate country in which device is operating.
 # This can limit available channels and transmit power.
@@ -103,6 +106,8 @@
 #ieee80211d=1
 
 # Operation mode (a = IEEE 802.11a, b = IEEE 802.11b, g = IEEE 802.11g,
+# ad = IEEE 802.11ad (60 GHz); a/g options are used with IEEE 802.11n, too, to
+# specify band)
 # Default: IEEE 802.11b
 hw_mode=g
 
@@ -1032,6 +1037,19 @@
 # 1 = enabled
 #okc=1
 
+# SAE threshold for anti-clogging mechanism (dot11RSNASAEAntiCloggingThreshold)
+# This parameter defines how many open SAE instances can be in progress at the
+# same time before the anti-clogging mechanism is taken into use.
+#sae_anti_clogging_threshold=5
+
+# Enabled SAE finite cyclic groups
+# SAE implementation are required to support group 19 (ECC group defined over a
+# 256-bit prime order field). All groups that are supported by the
+# implementation are enabled by default. This configuration parameter can be
+# used to specify a limited set of allowed groups. The group values are listed
+# in the IANA registry:
+# http://www.iana.org/assignments/ipsec-registry/ipsec-registry.xml#ipsec-registry-9
+#sae_groups=19 20 21 25 26
 
 ##### IEEE 802.11r configuration ##############################################
 
@@ -1300,6 +1318,16 @@
 # stdoffset[dst[offset][,start[/time],end[/time]]]
 #time_zone=EST5
 
+# WNM-Sleep Mode (extended sleep mode for stations)
+# 0 = disabled (default)
+# 1 = enabled (allow stations to use WNM-Sleep Mode)
+#wnm_sleep_mode=1
+
+# BSS Transition Management
+# 0 = disabled (default)
+# 1 = enabled
+#bss_transition=1
+
 ##### IEEE 802.11u-2011 #######################################################
 
 # Enable Interworking service
diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
index de1af3b..b693fa0 100644
--- a/hostapd/hostapd_cli.c
+++ b/hostapd/hostapd_cli.c
@@ -18,7 +18,7 @@
 
 static const char *hostapd_cli_version =
 "hostapd_cli v" VERSION_STR "\n"
-"Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi> and contributors";
+"Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi> and contributors";
 
 
 static const char *hostapd_cli_license =
@@ -544,6 +544,26 @@
 #endif /* CONFIG_WPS */
 
 
+static int hostapd_cli_cmd_disassoc_imminent(struct wpa_ctrl *ctrl, int argc,
+					     char *argv[])
+{
+	char buf[300];
+	int res;
+
+	if (argc < 2) {
+		printf("Invalid 'disassoc_imminent' command - two arguments "
+		       "(STA addr and Disassociation Timer) are needed\n");
+		return -1;
+	}
+
+	res = os_snprintf(buf, sizeof(buf), "DISASSOC_IMMINENT %s %s",
+			  argv[0], argv[1]);
+	if (res < 0 || res >= (int) sizeof(buf))
+		return -1;
+	return wpa_ctrl_command(ctrl, buf);
+}
+
+
 static int hostapd_cli_cmd_ess_disassoc(struct wpa_ctrl *ctrl, int argc,
 					char *argv[])
 {
@@ -780,6 +800,7 @@
 	{ "wps_ap_pin", hostapd_cli_cmd_wps_ap_pin },
 	{ "wps_config", hostapd_cli_cmd_wps_config },
 #endif /* CONFIG_WPS */
+	{ "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent },
 	{ "ess_disassoc", hostapd_cli_cmd_ess_disassoc },
 	{ "get_config", hostapd_cli_cmd_get_config },
 	{ "help", hostapd_cli_cmd_help },
diff --git a/hostapd/main.c b/hostapd/main.c
index 56f0002..d4256d0 100644
--- a/hostapd/main.c
+++ b/hostapd/main.c
@@ -468,7 +468,7 @@
 		"hostapd v" VERSION_STR "\n"
 		"User space daemon for IEEE 802.11 AP management,\n"
 		"IEEE 802.1X/WPA/WPA2/EAP/RADIUS Authenticator\n"
-		"Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi> "
+		"Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi> "
 		"and contributors\n");
 }
 
diff --git a/src/ap/accounting.c b/src/ap/accounting.c
index 7563b52..9540531 100644
--- a/src/ap/accounting.c
+++ b/src/ap/accounting.c
@@ -26,8 +26,6 @@
  * input/output octets and updates Acct-{Input,Output}-Gigawords. */
 #define ACCT_DEFAULT_UPDATE_INTERVAL 300
 
-static void accounting_sta_get_id(struct hostapd_data *hapd,
-				  struct sta_info *sta);
 static void accounting_sta_interim(struct hostapd_data *hapd,
 				   struct sta_info *sta);
 
@@ -210,7 +208,6 @@
 	if (sta->acct_session_started)
 		return;
 
-	accounting_sta_get_id(hapd, sta);
 	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
 		       HOSTAPD_LEVEL_INFO,
 		       "starting accounting session %08X-%08X",
@@ -377,7 +374,7 @@
 }
 
 
-static void accounting_sta_get_id(struct hostapd_data *hapd,
+void accounting_sta_get_id(struct hostapd_data *hapd,
 				  struct sta_info *sta)
 {
 	sta->acct_session_id_lo = hapd->acct_session_id_lo++;
diff --git a/src/ap/accounting.h b/src/ap/accounting.h
index 9d13d01..dcc54ee 100644
--- a/src/ap/accounting.h
+++ b/src/ap/accounting.h
@@ -10,6 +10,11 @@
 #define ACCOUNTING_H
 
 #ifdef CONFIG_NO_ACCOUNTING
+static inline void accounting_sta_get_id(struct hostapd_data *hapd,
+					 struct sta_info *sta)
+{
+}
+
 static inline void accounting_sta_start(struct hostapd_data *hapd,
 					struct sta_info *sta)
 {
@@ -29,6 +34,7 @@
 {
 }
 #else /* CONFIG_NO_ACCOUNTING */
+void accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta);
 void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta);
 void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta);
 int accounting_init(struct hostapd_data *hapd);
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 3c699f7..922f564 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -89,6 +89,8 @@
 #endif /* CONFIG_IEEE80211R */
 
 	bss->radius_das_time_window = 300;
+
+	bss->sae_anti_clogging_threshold = 5;
 }
 
 
@@ -158,6 +160,9 @@
 
 	conf->ht_capab = HT_CAP_INFO_SMPS_DISABLED;
 
+	conf->ap_table_max_size = 255;
+	conf->ap_table_expiration_time = 60;
+
 	return conf;
 }
 
@@ -516,6 +521,8 @@
 #endif /* CONFIG_HS20 */
 
 	wpabuf_free(conf->vendor_elements);
+
+	os_free(conf->sae_groups);
 }
 
 
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 71313c0..4742107 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -51,7 +51,8 @@
 struct hostapd_ssid {
 	u8 ssid[HOSTAPD_MAX_SSID_LEN];
 	size_t ssid_len;
-	int ssid_set;
+	unsigned int ssid_set:1;
+	unsigned int utf8_ssid:1;
 
 	char vlan[IFNAMSIZ + 1];
 	secpolicy security_policy;
@@ -390,6 +391,8 @@
 	/* IEEE 802.11v */
 	int time_advertisement;
 	char *time_zone;
+	int wnm_sleep_mode;
+	int bss_transition;
 
 	/* IEEE 802.11u - Interworking */
 	int interworking;
@@ -452,6 +455,9 @@
 #endif /* CONFIG_RADIUS_TEST */
 
 	struct wpabuf *vendor_elements;
+
+	unsigned int sae_anti_clogging_threshold;
+	int *sae_groups;
 };
 
 
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index 02da25b..b71d51d 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -338,6 +338,7 @@
 		    const u8 *supp_rates, size_t supp_rates_len,
 		    u16 listen_interval,
 		    const struct ieee80211_ht_capabilities *ht_capab,
+		    const struct ieee80211_vht_capabilities *vht_capab,
 		    u32 flags, u8 qosinfo)
 {
 	struct hostapd_sta_add_params params;
@@ -355,6 +356,7 @@
 	params.supp_rates_len = supp_rates_len;
 	params.listen_interval = listen_interval;
 	params.ht_capabilities = ht_capab;
+	params.vht_capabilities = vht_capab;
 	params.flags = hostapd_sta_flags_to_drv(flags);
 	params.qosinfo = qosinfo;
 	return hapd->driver->sta_add(hapd->drv_priv, &params);
@@ -454,19 +456,76 @@
 
 
 int hostapd_set_freq(struct hostapd_data *hapd, int mode, int freq,
-		     int channel, int ht_enabled, int sec_channel_offset)
+		     int channel, int ht_enabled, int vht_enabled,
+		     int sec_channel_offset, int vht_oper_chwidth,
+		     int center_segment0, int center_segment1)
 {
 	struct hostapd_freq_params data;
-	if (hapd->driver == NULL)
-		return 0;
-	if (hapd->driver->set_freq == NULL)
-		return 0;
+	int tmp;
+
 	os_memset(&data, 0, sizeof(data));
 	data.mode = mode;
 	data.freq = freq;
 	data.channel = channel;
 	data.ht_enabled = ht_enabled;
+	data.vht_enabled = vht_enabled;
 	data.sec_channel_offset = sec_channel_offset;
+	data.center_freq1 = freq + sec_channel_offset * 10;
+	data.center_freq2 = 0;
+	data.bandwidth = sec_channel_offset ? 40 : 20;
+
+	/*
+	 * This validation code is probably misplaced, maybe it should be
+	 * in src/ap/hw_features.c and check the hardware support as well.
+	 */
+	if (data.vht_enabled) switch (vht_oper_chwidth) {
+	case VHT_CHANWIDTH_USE_HT:
+		if (center_segment1)
+			return -1;
+		if (5000 + center_segment0 * 5 != data.center_freq1)
+			return -1;
+		break;
+	case VHT_CHANWIDTH_80P80MHZ:
+		if (center_segment1 == center_segment0 + 4 ||
+		    center_segment1 == center_segment0 - 4)
+			return -1;
+		data.center_freq2 = 5000 + center_segment1 * 5;
+		/* fall through */
+	case VHT_CHANWIDTH_80MHZ:
+		data.bandwidth = 80;
+		if (vht_oper_chwidth == 1 && center_segment1)
+			return -1;
+		if (vht_oper_chwidth == 3 && !center_segment1)
+			return -1;
+		if (!sec_channel_offset)
+			return -1;
+		/* primary 40 part must match the HT configuration */
+		tmp = (30 + freq - 5000 - center_segment0 * 5)/20;
+		tmp /= 2;
+		if (data.center_freq1 != 5000 +
+					 center_segment0 * 5 - 20 + 40 * tmp)
+			return -1;
+		data.center_freq1 = 5000 + center_segment0 * 5;
+		break;
+	case VHT_CHANWIDTH_160MHZ:
+		data.bandwidth = 160;
+		if (center_segment1)
+			return -1;
+		if (!sec_channel_offset)
+			return -1;
+		/* primary 40 part must match the HT configuration */
+		tmp = (70 + freq - 5000 - center_segment0 * 5)/20;
+		tmp /= 2;
+		if (data.center_freq1 != 5000 +
+					 center_segment0 * 5 - 60 + 40 * tmp)
+			return -1;
+		data.center_freq1 = 5000 + center_segment0 * 5;
+		break;
+	}
+	if (hapd->driver == NULL)
+		return 0;
+	if (hapd->driver->set_freq == NULL)
+		return 0;
 	return hapd->driver->set_freq(hapd->drv_priv, &data);
 }
 
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index 9c53b99..ceb7e68 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -13,6 +13,7 @@
 struct wpa_bss_params;
 struct wpa_driver_scan_params;
 struct ieee80211_ht_capabilities;
+struct ieee80211_vht_capabilities;
 
 u32 hostapd_sta_flags_to_drv(u32 flags);
 int hostapd_build_ap_extra_ies(struct hostapd_data *hapd,
@@ -37,6 +38,7 @@
 		    const u8 *supp_rates, size_t supp_rates_len,
 		    u16 listen_interval,
 		    const struct ieee80211_ht_capabilities *ht_capab,
+		    const struct ieee80211_vht_capabilities *vht_capab,
 		    u32 flags, u8 qosinfo);
 int hostapd_set_privacy(struct hostapd_data *hapd, int enabled);
 int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem,
@@ -55,7 +57,9 @@
 		       const u8 *addr, int idx, u8 *seq);
 int hostapd_flush(struct hostapd_data *hapd);
 int hostapd_set_freq(struct hostapd_data *hapd, int mode, int freq,
-		     int channel, int ht_enabled, int sec_channel_offset);
+		     int channel, int ht_enabled, int vht_enabled,
+		     int sec_channel_offset, int vht_oper_chwidth,
+		     int center_segment0, int center_segment1);
 int hostapd_set_rts(struct hostapd_data *hapd, int rts);
 int hostapd_set_frag(struct hostapd_data *hapd, int frag);
 int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr,
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index f761bf5..4c47c75 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -2,7 +2,7 @@
  * hostapd / IEEE 802.11 Management: Beacon and Probe Request/Response
  * Copyright (c) 2002-2004, Instant802 Networks, Inc.
  * Copyright (c) 2005-2006, Devicescape Software, Inc.
- * Copyright (c) 2008-2009, 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
@@ -310,6 +310,46 @@
 }
 
 
+enum ssid_match_result {
+	NO_SSID_MATCH,
+	EXACT_SSID_MATCH,
+	WILDCARD_SSID_MATCH
+};
+
+static enum ssid_match_result ssid_match(struct hostapd_data *hapd,
+					 const u8 *ssid, size_t ssid_len,
+					 const u8 *ssid_list,
+					 size_t ssid_list_len)
+{
+	const u8 *pos, *end;
+	int wildcard = 0;
+
+	if (ssid_len == 0)
+		wildcard = 1;
+	if (ssid_len == hapd->conf->ssid.ssid_len &&
+	    os_memcmp(ssid, hapd->conf->ssid.ssid, ssid_len) == 0)
+		return EXACT_SSID_MATCH;
+
+	if (ssid_list == NULL)
+		return wildcard ? WILDCARD_SSID_MATCH : NO_SSID_MATCH;
+
+	pos = ssid_list;
+	end = ssid_list + ssid_list_len;
+	while (pos + 1 <= end) {
+		if (pos + 2 + pos[1] > end)
+			break;
+		if (pos[1] == 0)
+			wildcard = 1;
+		if (pos[1] == hapd->conf->ssid.ssid_len &&
+		    os_memcmp(pos + 2, hapd->conf->ssid.ssid, pos[1]) == 0)
+			return EXACT_SSID_MATCH;
+		pos += 2 + pos[1];
+	}
+
+	return wildcard ? WILDCARD_SSID_MATCH : NO_SSID_MATCH;
+}
+
+
 void handle_probe_req(struct hostapd_data *hapd,
 		      const struct ieee80211_mgmt *mgmt, size_t len,
 		      int ssi_signal)
@@ -321,6 +361,7 @@
 	struct sta_info *sta = NULL;
 	size_t i, resp_len;
 	int noack;
+	enum ssid_match_result res;
 
 	ie = mgmt->u.probe_req.variable;
 	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req))
@@ -376,7 +417,8 @@
 	}
 #endif /* CONFIG_P2P */
 
-	if (hapd->conf->ignore_broadcast_ssid && elems.ssid_len == 0) {
+	if (hapd->conf->ignore_broadcast_ssid && elems.ssid_len == 0 &&
+	    elems.ssid_list_len == 0) {
 		wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR " for "
 			   "broadcast SSID ignored", MAC2STR(mgmt->sa));
 		return;
@@ -394,10 +436,9 @@
 	}
 #endif /* CONFIG_P2P */
 
-	if (elems.ssid_len == 0 ||
-	    (elems.ssid_len == hapd->conf->ssid.ssid_len &&
-	     os_memcmp(elems.ssid, hapd->conf->ssid.ssid, elems.ssid_len) ==
-	     0)) {
+	res = ssid_match(hapd, elems.ssid, elems.ssid_len,
+			 elems.ssid_list, elems.ssid_list_len);
+	if (res != NO_SSID_MATCH) {
 		if (sta)
 			sta->ssid_probe = &hapd->conf->ssid;
 	} else {
@@ -406,9 +447,10 @@
 			ieee802_11_print_ssid(ssid_txt, elems.ssid,
 					      elems.ssid_len);
 			wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR
-				   " for foreign SSID '%s' (DA " MACSTR ")",
+				   " for foreign SSID '%s' (DA " MACSTR ")%s",
 				   MAC2STR(mgmt->sa), ssid_txt,
-				   MAC2STR(mgmt->da));
+				   MAC2STR(mgmt->da),
+				   elems.ssid_list ? " (SSID list)" : "");
 		}
 		return;
 	}
@@ -455,7 +497,8 @@
 	 * If this is a broadcast probe request, apply no ack policy to avoid
 	 * excessive retries.
 	 */
-	noack = !!(elems.ssid_len == 0 && is_broadcast_ether_addr(mgmt->da));
+	noack = !!(res == WILDCARD_SSID_MATCH &&
+		   is_broadcast_ether_addr(mgmt->da));
 
 	if (hostapd_drv_send_mlme(hapd, resp, resp_len, noack) < 0)
 		perror("handle_probe_req: send");
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index c55d3fe..1cb7e73 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -189,6 +189,7 @@
 	u8 addr[ETH_ALEN];
 	struct sta_info *sta;
 	const char *pos;
+	u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
 
 	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DEAUTHENTICATE %s",
 		txtaddr);
@@ -228,11 +229,14 @@
 	}
 #endif /* CONFIG_P2P_MANAGER */
 
-	hostapd_drv_sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
+	pos = os_strstr(txtaddr, " reason=");
+	if (pos)
+		reason = atoi(pos + 8);
+
+	hostapd_drv_sta_deauth(hapd, addr, reason);
 	sta = ap_get_sta(hapd, addr);
 	if (sta)
-		ap_sta_deauthenticate(hapd, sta,
-				      WLAN_REASON_PREV_AUTH_NOT_VALID);
+		ap_sta_deauthenticate(hapd, sta, reason);
 	else if (addr[0] == 0xff)
 		hostapd_free_stas(hapd);
 
@@ -246,6 +250,7 @@
 	u8 addr[ETH_ALEN];
 	struct sta_info *sta;
 	const char *pos;
+	u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
 
 	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DISASSOCIATE %s",
 		txtaddr);
@@ -285,11 +290,14 @@
 	}
 #endif /* CONFIG_P2P_MANAGER */
 
-	hostapd_drv_sta_disassoc(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
+	pos = os_strstr(txtaddr, " reason=");
+	if (pos)
+		reason = atoi(pos + 8);
+
+	hostapd_drv_sta_disassoc(hapd, addr, reason);
 	sta = ap_get_sta(hapd, addr);
 	if (sta)
-		ap_sta_disassociate(hapd, sta,
-				    WLAN_REASON_PREV_AUTH_NOT_VALID);
+		ap_sta_disassociate(hapd, sta, reason);
 	else if (addr[0] == 0xff)
 		hostapd_free_stas(hapd);
 
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index 8613975..8980bec 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -512,13 +512,13 @@
 					   action->data + 2);
 	}
 #endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_IEEE80211V
+#ifdef CONFIG_WNM
 	if (action->category == WLAN_ACTION_WNM) {
 		wpa_printf(MSG_DEBUG, "%s: WNM_ACTION length %d",
 			   __func__, (int) action->len);
 		ieee802_11_rx_wnm_action_ap(hapd, action);
 	}
-#endif /* CONFIG_IEEE80211V */
+#endif /* CONFIG_WNM */
 }
 
 
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index cef9daf..92fda56 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -894,7 +894,11 @@
 		if (hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq,
 				     hapd->iconf->channel,
 				     hapd->iconf->ieee80211n,
-				     hapd->iconf->secondary_channel)) {
+				     hapd->iconf->ieee80211ac,
+				     hapd->iconf->secondary_channel,
+				     hapd->iconf->vht_oper_chwidth,
+				     hapd->iconf->vht_oper_centr_freq_seg0_idx,
+				     hapd->iconf->vht_oper_centr_freq_seg1_idx)) {
 			wpa_printf(MSG_ERROR, "Could not set channel for "
 				   "kernel driver");
 			return -1;
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index f1e7d9f..c9087b3 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -192,6 +192,12 @@
 #ifdef CONFIG_SQLITE
 	struct hostapd_eap_user tmp_eap_user;
 #endif /* CONFIG_SQLITE */
+
+#ifdef CONFIG_SAE
+	/** Key used for generating SAE anti-clogging tokens */
+	u8 sae_token_key[8];
+	os_time_t last_sae_token_key_update;
+#endif /* CONFIG_SAE */
 };
 
 
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index 97e1238..923b698 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -122,6 +122,8 @@
 	case HOSTAPD_MODE_IEEE80211G:
 		basic_rates = basic_rates_g;
 		break;
+	case HOSTAPD_MODE_IEEE80211AD:
+		return 0; /* No basic rates for 11ad */
 	default:
 		return -1;
 	}
@@ -756,6 +758,8 @@
 		return "IEEE 802.11b";
 	case HOSTAPD_MODE_IEEE80211G:
 		return "IEEE 802.11g";
+	case HOSTAPD_MODE_IEEE80211AD:
+		return "IEEE 802.11ad";
 	default:
 		return "UNKNOWN";
 	}
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index a13a135..79235df 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -13,10 +13,13 @@
 #include "utils/common.h"
 #include "utils/eloop.h"
 #include "crypto/crypto.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
 #include "drivers/driver.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
 #include "common/wpa_ctrl.h"
+#include "common/sae.h"
 #include "radius/radius.h"
 #include "radius/radius_client.h"
 #include "p2p/p2p.h"
@@ -34,6 +37,7 @@
 #include "ap_mlme.h"
 #include "p2p_hostapd.h"
 #include "ap_drv_ops.h"
+#include "wnm_ap.h"
 #include "ieee802_11.h"
 
 
@@ -316,19 +320,33 @@
 
 #ifdef CONFIG_SAE
 
-static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd,
-					     struct sta_info *sta)
+static struct wpabuf * auth_process_sae_commit(struct hostapd_data *hapd,
+					       struct sta_info *sta)
 {
 	struct wpabuf *buf;
 
-	buf = wpabuf_alloc(2);
+	if (hapd->conf->ssid.wpa_passphrase == NULL) {
+		wpa_printf(MSG_DEBUG, "SAE: No password available");
+		return NULL;
+	}
+
+	if (sae_prepare_commit(hapd->own_addr, sta->addr,
+			       (u8 *) hapd->conf->ssid.wpa_passphrase,
+			       os_strlen(hapd->conf->ssid.wpa_passphrase),
+			       sta->sae) < 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE");
+		return NULL;
+	}
+
+	if (sae_process_commit(sta->sae) < 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Failed to process peer commit");
+		return NULL;
+	}
+
+	buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN);
 	if (buf == NULL)
 		return NULL;
-
-	wpabuf_put_le16(buf, 19); /* Finite Cyclic Group */
-	/* TODO: Anti-Clogging Token (if requested) */
-	/* TODO: Scalar */
-	/* TODO: Element */
+	sae_write_commit(sta->sae, buf, NULL);
 
 	return buf;
 }
@@ -339,49 +357,82 @@
 {
 	struct wpabuf *buf;
 
-	buf = wpabuf_alloc(2);
+	buf = wpabuf_alloc(SAE_CONFIRM_MAX_LEN);
 	if (buf == NULL)
 		return NULL;
 
-	wpabuf_put_le16(buf, sta->sae_send_confirm);
-	sta->sae_send_confirm++;
-	/* TODO: Confirm */
+	sae_write_confirm(sta->sae, buf);
 
 	return buf;
 }
 
 
-static u16 handle_sae_commit(struct hostapd_data *hapd, struct sta_info *sta,
-			     const u8 *data, size_t len)
+static int use_sae_anti_clogging(struct hostapd_data *hapd)
 {
-	wpa_hexdump(MSG_DEBUG, "SAE commit fields", data, len);
+	struct sta_info *sta;
+	unsigned int open = 0;
 
-	/* Check Finite Cyclic Group */
-	if (len < 2)
-		return WLAN_STATUS_UNSPECIFIED_FAILURE;
-	if (WPA_GET_LE16(data) != 19) {
-		wpa_printf(MSG_DEBUG, "SAE: Unsupported Finite Cyclic Group %u",
-			   WPA_GET_LE16(data));
-		return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+	if (hapd->conf->sae_anti_clogging_threshold == 0)
+		return 1;
+
+	for (sta = hapd->sta_list; sta; sta = sta->next) {
+		if (!sta->sae)
+			continue;
+		if (sta->sae->state != SAE_COMMITTED &&
+		    sta->sae->state != SAE_CONFIRMED)
+			continue;
+		open++;
+		if (open >= hapd->conf->sae_anti_clogging_threshold)
+			return 1;
 	}
 
-	return WLAN_STATUS_SUCCESS;
+	return 0;
 }
 
 
-static u16 handle_sae_confirm(struct hostapd_data *hapd, struct sta_info *sta,
-			      const u8 *data, size_t len)
+static int check_sae_token(struct hostapd_data *hapd, const u8 *addr,
+			   const u8 *token, size_t token_len)
 {
-	u16 rc;
+	u8 mac[SHA256_MAC_LEN];
 
-	wpa_hexdump(MSG_DEBUG, "SAE confirm fields", data, len);
+	if (token_len != SHA256_MAC_LEN)
+		return -1;
+	if (hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key),
+			addr, ETH_ALEN, mac) < 0 ||
+	    os_memcmp(token, mac, SHA256_MAC_LEN) != 0)
+		return -1;
 
-	if (len < 2)
-		return WLAN_STATUS_UNSPECIFIED_FAILURE;
-	rc = WPA_GET_LE16(data);
-	wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", rc);
+	return 0;
+}
 
-	return WLAN_STATUS_SUCCESS;
+
+static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd,
+					    const u8 *addr)
+{
+	struct wpabuf *buf;
+	u8 *token;
+	struct os_time t;
+
+	os_get_time(&t);
+	if (hapd->last_sae_token_key_update == 0 ||
+	    t.sec > hapd->last_sae_token_key_update + 60) {
+		if (random_get_bytes(hapd->sae_token_key,
+				     sizeof(hapd->sae_token_key)) < 0)
+			return NULL;
+		wpa_hexdump(MSG_DEBUG, "SAE: Updated token key",
+			    hapd->sae_token_key, sizeof(hapd->sae_token_key));
+		hapd->last_sae_token_key_update = t.sec;
+	}
+
+	buf = wpabuf_alloc(SHA256_MAC_LEN);
+	if (buf == NULL)
+		return NULL;
+
+	token = wpabuf_put(buf, SHA256_MAC_LEN);
+	hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key),
+		    addr, ETH_ALEN, token);
+
+	return buf;
 }
 
 
@@ -390,19 +441,52 @@
 			    u8 auth_transaction)
 {
 	u16 resp = WLAN_STATUS_SUCCESS;
-	struct wpabuf *data;
+	struct wpabuf *data = NULL;
+
+	if (!sta->sae) {
+		if (auth_transaction != 1)
+			return;
+		sta->sae = os_zalloc(sizeof(*sta->sae));
+		if (sta->sae == NULL)
+			return;
+		sta->sae->state = SAE_NOTHING;
+	}
 
 	if (auth_transaction == 1) {
+		const u8 *token = NULL;
+		size_t token_len = 0;
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_DEBUG,
 			       "start SAE authentication (RX commit)");
-		resp = handle_sae_commit(hapd, sta, mgmt->u.auth.variable,
-					 ((u8 *) mgmt) + len -
-					 mgmt->u.auth.variable);
-		if (resp == WLAN_STATUS_SUCCESS)
-			sta->sae_state = SAE_COMMIT;
+		resp = sae_parse_commit(sta->sae, mgmt->u.auth.variable,
+					((const u8 *) mgmt) + len -
+					mgmt->u.auth.variable, &token,
+					&token_len, hapd->conf->sae_groups);
+		if (token && check_sae_token(hapd, sta->addr, token, token_len)
+		    < 0) {
+			wpa_printf(MSG_DEBUG, "SAE: Drop commit message with "
+				   "incorrect token from " MACSTR,
+				   MAC2STR(sta->addr));
+			return;
+		}
+
+		if (resp == WLAN_STATUS_SUCCESS) {
+			if (!token && use_sae_anti_clogging(hapd)) {
+				wpa_printf(MSG_DEBUG, "SAE: Request anti-"
+					   "clogging token from " MACSTR,
+					   MAC2STR(sta->addr));
+				data = auth_build_token_req(hapd, sta->addr);
+				resp = WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ;
+			} else {
+				data = auth_process_sae_commit(hapd, sta);
+				if (data == NULL)
+					resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+				else
+					sta->sae->state = SAE_COMMITTED;
+			}
+		}
 	} else if (auth_transaction == 2) {
-		if (sta->sae_state != SAE_COMMIT) {
+		if (sta->sae->state != SAE_COMMITTED) {
 			hostapd_logger(hapd, sta->addr,
 				       HOSTAPD_MODULE_IEEE80211,
 				       HOSTAPD_LEVEL_DEBUG,
@@ -412,14 +496,24 @@
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_DEBUG,
 			       "SAE authentication (RX confirm)");
-		resp = handle_sae_confirm(hapd, sta, mgmt->u.auth.variable,
-					  ((u8 *) mgmt) + len -
-					  mgmt->u.auth.variable);
-		if (resp == WLAN_STATUS_SUCCESS) {
+		if (sae_check_confirm(sta->sae, mgmt->u.auth.variable,
+				       ((u8 *) mgmt) + len -
+				       mgmt->u.auth.variable) < 0) {
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		} else {
+			resp = WLAN_STATUS_SUCCESS;
 			sta->flags |= WLAN_STA_AUTH;
 			wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
 			sta->auth_alg = WLAN_AUTH_SAE;
 			mlme_authenticate_indication(hapd, sta);
+
+			data = auth_build_sae_confirm(hapd, sta);
+			if (data == NULL)
+				resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			else {
+				sta->sae->state = SAE_ACCEPTED;
+				sae_clear_temp_data(sta->sae);
+			}
 		}
 	} else {
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
@@ -431,16 +525,6 @@
 
 	sta->auth_alg = WLAN_AUTH_SAE;
 
-	if (resp == WLAN_STATUS_SUCCESS) {
-		if (auth_transaction == 1)
-			data = auth_build_sae_commit(hapd, sta);
-		else
-			data = auth_build_sae_confirm(hapd, sta);
-		if (data == NULL)
-			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
-	} else
-		data = NULL;
-
 	send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
 			auth_transaction, resp,
 			data ? wpabuf_head(data) : (u8 *) "",
@@ -1434,13 +1518,32 @@
 #endif /* CONFIG_IEEE80211W */
 
 
+#ifdef CONFIG_WNM
+static void hostapd_wnm_action(struct hostapd_data *hapd, struct sta_info *sta,
+			       const struct ieee80211_mgmt *mgmt,
+			       size_t len)
+{
+	struct rx_action action;
+	if (len < IEEE80211_HDRLEN + 2)
+		return;
+	os_memset(&action, 0, sizeof(action));
+	action.da = mgmt->da;
+	action.sa = mgmt->sa;
+	action.bssid = mgmt->bssid;
+	action.category = mgmt->u.action.category;
+	action.data = (const u8 *) &mgmt->u.action.u.wnm_sleep_req.action;
+	action.len = len - IEEE80211_HDRLEN - 1;
+	action.freq = hapd->iface->freq;
+	ieee802_11_rx_wnm_action_ap(hapd, &action);
+}
+#endif /* CONFIG_WNM */
+
+
 static void handle_action(struct hostapd_data *hapd,
 			  const struct ieee80211_mgmt *mgmt, size_t len)
 {
-#if defined(CONFIG_IEEE80211W) || defined(CONFIG_IEEE80211R)
 	struct sta_info *sta;
 	sta = ap_get_sta(hapd, mgmt->sa);
-#endif /* CONFIG_IEEE80211W || CONFIG_IEEE80211R */
 
 	if (len < IEEE80211_HDRLEN + 1) {
 		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
@@ -1450,6 +1553,14 @@
 		return;
 	}
 
+	if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
+	    (sta == NULL || !(sta->flags & WLAN_STA_ASSOC))) {
+		wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignored Action "
+			   "frame (category=%u) from unassociated STA " MACSTR,
+			   MAC2STR(mgmt->sa), mgmt->u.action.category);
+		return;
+	}
+
 #ifdef CONFIG_IEEE80211W
 	if (sta && (sta->flags & WLAN_STA_MFP) &&
 	    !(mgmt->frame_control & host_to_le16(WLAN_FC_ISWEP) &&
@@ -1465,20 +1576,10 @@
 	switch (mgmt->u.action.category) {
 #ifdef CONFIG_IEEE80211R
 	case WLAN_ACTION_FT:
-	{
-		if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) {
-			wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignored FT Action "
-				   "frame from unassociated STA " MACSTR,
-				   MAC2STR(mgmt->sa));
-			return;
-		}
-
 		if (wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action,
 				     len - IEEE80211_HDRLEN))
 			break;
-
 		return;
-	}
 #endif /* CONFIG_IEEE80211R */
 	case WLAN_ACTION_WMM:
 		hostapd_wmm_action(hapd, mgmt, len);
@@ -1488,6 +1589,11 @@
 		hostapd_sa_query_action(hapd, mgmt, len);
 		return;
 #endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_WNM
+	case WLAN_ACTION_WNM:
+		hostapd_wnm_action(hapd, sta, mgmt, len);
+		return;
+#endif /* CONFIG_WNM */
 	case WLAN_ACTION_PUBLIC:
 		if (hapd->public_action_cb) {
 			hapd->public_action_cb(hapd->public_action_cb_ctx,
@@ -1685,6 +1791,7 @@
 	struct sta_info *sta;
 	int new_assoc = 1;
 	struct ieee80211_ht_capabilities ht_cap;
+	struct ieee80211_vht_capabilities vht_cap;
 
 	if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_resp) :
 				      sizeof(mgmt->u.assoc_resp))) {
@@ -1757,11 +1864,16 @@
 	if (sta->flags & WLAN_STA_HT)
 		hostapd_get_ht_capab(hapd, sta->ht_capabilities, &ht_cap);
 #endif /* CONFIG_IEEE80211N */
+#ifdef CONFIG_IEEE80211AC
+	if (sta->flags & WLAN_STA_VHT)
+		hostapd_get_vht_capab(hapd, sta->vht_capabilities, &vht_cap);
+#endif /* CONFIG_IEEE80211AC */
 
 	if (hostapd_sta_add(hapd, sta->addr, sta->aid, sta->capability,
 			    sta->supported_rates, sta->supported_rates_len,
 			    sta->listen_interval,
 			    sta->flags & WLAN_STA_HT ? &ht_cap : NULL,
+			    sta->flags & WLAN_STA_VHT ? &vht_cap : NULL,
 			    sta->flags, sta->qosinfo)) {
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_NOTICE,
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index 1e5800d..2aab56d 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -53,6 +53,9 @@
 void hostapd_get_ht_capab(struct hostapd_data *hapd,
 			  struct ieee80211_ht_capabilities *ht_cap,
 			  struct ieee80211_ht_capabilities *neg_ht_cap);
+void hostapd_get_vht_capab(struct hostapd_data *hapd,
+			   struct ieee80211_vht_capabilities *vht_cap,
+			   struct ieee80211_vht_capabilities *neg_vht_cap);
 u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
 		      const u8 *ht_capab, size_t ht_capab_len);
 void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta);
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index b3fdf3d..76f78a7 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -173,6 +173,14 @@
 		len = 5;
 	if (len < 4 && hapd->conf->interworking)
 		len = 4;
+	if (len < 3 && hapd->conf->wnm_sleep_mode)
+		len = 3;
+	if (len < 7 && hapd->conf->ssid.utf8_ssid)
+		len = 7;
+#ifdef CONFIG_WNM
+	if (len < 4)
+		len = 4;
+#endif /* CONFIG_WNM */
 	if (len == 0)
 		return eid;
 
@@ -180,9 +188,20 @@
 	*pos++ = len;
 	*pos++ = 0x00;
 	*pos++ = 0x00;
-	*pos++ = 0x00;
 
 	*pos = 0x00;
+	if (hapd->conf->wnm_sleep_mode)
+		*pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */
+	if (hapd->conf->bss_transition)
+		*pos |= 0x08; /* Bit 19 - BSS Transition */
+	pos++;
+
+	if (len < 4)
+		return pos;
+	*pos = 0x00;
+#ifdef CONFIG_WNM
+	*pos |= 0x02; /* Bit 25 - SSID List */
+#endif /* CONFIG_WNM */
 	if (hapd->conf->time_advertisement == 2)
 		*pos |= 0x08; /* Bit 27 - UTC TSF Offset */
 	if (hapd->conf->interworking)
@@ -198,6 +217,18 @@
 		*pos |= 0x80; /* Bit 39 - TDLS Channel Switching Prohibited */
 	pos++;
 
+	if (len < 6)
+		return pos;
+	*pos = 0x00;
+	pos++;
+
+	if (len < 7)
+		return pos;
+	*pos = 0x00;
+	if (hapd->conf->ssid.utf8_ssid)
+		*pos |= 0x01; /* Bit 48 - UTF-8 SSID */
+	pos++;
+
 	return pos;
 }
 
diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c
index b21c2b7..f6cc5d6 100644
--- a/src/ap/ieee802_11_vht.c
+++ b/src/ap/ieee802_11_vht.c
@@ -108,3 +108,14 @@
 
 	return WLAN_STATUS_SUCCESS;
 }
+
+void hostapd_get_vht_capab(struct hostapd_data *hapd,
+			   struct ieee80211_vht_capabilities *vht_cap,
+			   struct ieee80211_vht_capabilities *neg_vht_cap)
+{
+	if (vht_cap == NULL)
+		return;
+	os_memcpy(neg_vht_cap, vht_cap, sizeof(*neg_vht_cap));
+
+	/* TODO: mask own capabilities, like get_ht_capab() */
+}
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index e1b11ba..a832a73 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -66,8 +66,9 @@
 	if (sta->flags & WLAN_STA_PREAUTH) {
 		rsn_preauth_send(hapd, sta, buf, len);
 	} else {
-		hostapd_drv_hapd_send_eapol(hapd, sta->addr, buf, len,
-					    encrypt, sta->flags);
+		hostapd_drv_hapd_send_eapol(
+			hapd, sta->addr, buf, len,
+			encrypt, hostapd_sta_flags_to_drv(sta->flags));
 	}
 
 	os_free(buf);
@@ -354,6 +355,8 @@
 const char *radius_mode_txt(struct hostapd_data *hapd)
 {
 	switch (hapd->iface->conf->hw_mode) {
+	case HOSTAPD_MODE_IEEE80211AD:
+		return "802.11ad";
 	case HOSTAPD_MODE_IEEE80211A:
 		return "802.11a";
 	case HOSTAPD_MODE_IEEE80211G:
@@ -452,6 +455,16 @@
 		return -1;
 	}
 
+	if (sta->acct_session_id_hi || sta->acct_session_id_lo) {
+		os_snprintf(buf, sizeof(buf), "%08X-%08X",
+			    sta->acct_session_id_hi, sta->acct_session_id_lo);
+		if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
+					 (u8 *) buf, os_strlen(buf))) {
+			wpa_printf(MSG_ERROR, "Could not add Acct-Session-Id");
+			return -1;
+		}
+	}
+
 	return 0;
 }
 
diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c
index 3a9cc7b..d27fd30 100644
--- a/src/ap/pmksa_cache_auth.c
+++ b/src/ap/pmksa_cache_auth.c
@@ -209,6 +209,8 @@
 	pmksa->pmkid[PMKID_HASH(entry->pmkid)] = entry;
 
 	pmksa->pmksa_count++;
+	if (prev == NULL)
+		pmksa_cache_set_expiration(pmksa);
 	wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR,
 		   MAC2STR(entry->spa));
 	wpa_hexdump(MSG_DEBUG, "RSN: added PMKID", entry->pmkid, PMKID_LEN);
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index 6bc43d2..8ada121 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -12,6 +12,7 @@
 #include "utils/eloop.h"
 #include "common/ieee802_11_defs.h"
 #include "common/wpa_ctrl.h"
+#include "common/sae.h"
 #include "radius/radius.h"
 #include "radius/radius_client.h"
 #include "drivers/driver.h"
@@ -240,6 +241,11 @@
 	os_free(sta->identity);
 	os_free(sta->radius_cui);
 
+#ifdef CONFIG_SAE
+	sae_clear_data(sta->sae);
+	os_free(sta->sae);
+#endif /* CONFIG_SAE */
+
 	os_free(sta);
 }
 
@@ -493,6 +499,7 @@
 		return NULL;
 	}
 	sta->acct_interim_interval = hapd->conf->acct_interim_interval;
+	accounting_sta_get_id(hapd, sta);
 
 	/* initialize STA info data */
 	wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index d5e92fa..32ea46e 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -127,8 +127,7 @@
 	struct os_time connected_time;
 
 #ifdef CONFIG_SAE
-	enum { SAE_INIT, SAE_COMMIT, SAE_CONFIRM } sae_state;
-	u16 sae_send_confirm;
+	struct sae_data *sae;
 #endif /* CONFIG_SAE */
 };
 
diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
index 2594404..54a6b85 100644
--- a/src/ap/wnm_ap.c
+++ b/src/ap/wnm_ap.c
@@ -19,7 +19,6 @@
 
 #define MAX_TFS_IE_LEN  1024
 
-#ifdef CONFIG_IEEE80211V
 
 /* get the TFS IE from driver */
 static int ieee80211_11_get_tfs_ie(struct hostapd_data *hapd, const u8 *addr,
@@ -57,8 +56,8 @@
 	u16 wnmtfs_ie_len;
 	u8 *pos;
 	struct sta_info *sta;
-	enum wnm_oper tfs_oper = action_type == 0 ? WNM_SLEEP_TFS_RESP_IE_ADD :
-		WNM_SLEEP_TFS_RESP_IE_NONE;
+	enum wnm_oper tfs_oper = action_type == WNM_SLEEP_MODE_ENTER ?
+		WNM_SLEEP_TFS_RESP_IE_ADD : WNM_SLEEP_TFS_RESP_IE_NONE;
 
 	sta = ap_get_sta(hapd, addr);
 	if (sta == NULL) {
@@ -105,7 +104,8 @@
 	mgmt->u.action.u.wnm_sleep_resp.dialogtoken = dialog_token;
 	pos = (u8 *)mgmt->u.action.u.wnm_sleep_resp.variable;
 	/* add key data if MFP is enabled */
-	if (wpa_auth_uses_mfp(sta->wpa_sm) || action_type != 1){
+	if (!wpa_auth_uses_mfp(sta->wpa_sm) ||
+	    action_type != WNM_SLEEP_MODE_EXIT) {
 		mgmt->u.action.u.wnm_sleep_resp.keydata_len = 0;
 	} else {
 		gtk_elem_len = wpa_wnmsleep_gtk_subelem(sta->wpa_sm, pos);
@@ -132,7 +132,8 @@
 	os_memcpy(pos, &wnmsleep_ie, wnmsleep_ie_len);
 	/* copy TFS IE here */
 	pos += wnmsleep_ie_len;
-	os_memcpy(pos, wnmtfs_ie, wnmtfs_ie_len);
+	if (wnmtfs_ie)
+		os_memcpy(pos, wnmtfs_ie, wnmtfs_ie_len);
 
 	len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_resp) + gtk_elem_len +
 		igtk_elem_len + wnmsleep_ie_len + wnmtfs_ie_len;
@@ -152,7 +153,7 @@
 		 * WNM Sleep
 		 */
 		if (wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT &&
-		    wnmsleep_ie.action_type == 0) {
+		    wnmsleep_ie.action_type == WNM_SLEEP_MODE_ENTER) {
 			hostapd_drv_wnm_oper(hapd, WNM_SLEEP_ENTER_CONFIRM,
 					     addr, NULL, NULL);
 			wpa_set_wnmsleep(sta->wpa_sm, 1);
@@ -162,12 +163,14 @@
 		 * 2. start GTK/IGTK update if MFP is not used
 		 * 3. unpause the node in driver
 		 */
-		if (wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT &&
-		    wnmsleep_ie.action_type == 1) {
+		if ((wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT ||
+		     wnmsleep_ie.status ==
+		     WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) &&
+		    wnmsleep_ie.action_type == WNM_SLEEP_MODE_EXIT) {
 			wpa_set_wnmsleep(sta->wpa_sm, 0);
 			hostapd_drv_wnm_oper(hapd, WNM_SLEEP_EXIT_CONFIRM,
 					     addr, NULL, NULL);
-			if (wpa_auth_uses_mfp(sta->wpa_sm) && action_type == 1)
+			if (!wpa_auth_uses_mfp(sta->wpa_sm))
 				wpa_wnmsleep_rekey_gtk(sta->wpa_sm);
 		}
 	} else
@@ -184,29 +187,29 @@
 static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd,
 				       const u8 *addr, const u8 *frm, int len)
 {
-	/*
-	 * Action [1] | Dialog Token [1] | WNM-Sleep Mode IE |
-	 * TFS Response IE
-	 */
-	u8 *pos = (u8 *) frm; /* point to action field */
-	u8 dialog_token = pos[1];
+	/* Dialog Token [1] | WNM-Sleep Mode IE | TFS Response IE */
+	const u8 *pos = frm;
+	u8 dialog_token;
 	struct wnm_sleep_element *wnmsleep_ie = NULL;
 	/* multiple TFS Req IE (assuming consecutive) */
 	u8 *tfsreq_ie_start = NULL;
 	u8 *tfsreq_ie_end = NULL;
 	u16 tfsreq_ie_len = 0;
 
-	pos += 1 + 1;
-	while (pos - frm < len - 1) {
-		u8 ie_len = *(pos+1);
+	dialog_token = *pos++;
+	while (pos + 1 < frm + len) {
+		u8 ie_len = pos[1];
+		if (pos + 2 + ie_len > frm + len)
+			break;
 		if (*pos == WLAN_EID_WNMSLEEP)
-			wnmsleep_ie = (struct wnm_sleep_element *)pos;
+			wnmsleep_ie = (struct wnm_sleep_element *) pos;
 		else if (*pos == WLAN_EID_TFS_REQ) {
 			if (!tfsreq_ie_start)
-				tfsreq_ie_start = pos;
-			tfsreq_ie_end = pos;
+				tfsreq_ie_start = (u8 *) pos;
+			tfsreq_ie_end = (u8 *) pos;
 		} else
-			wpa_printf(MSG_DEBUG, "EID %d not recognized", *pos);
+			wpa_printf(MSG_DEBUG, "WNM: EID %d not recognized",
+				   *pos);
 		pos += ie_len + 2;
 	}
 
@@ -215,8 +218,9 @@
 		return;
 	}
 
-	if (wnmsleep_ie->action_type == 0 && tfsreq_ie_start &&
-	    tfsreq_ie_end && tfsreq_ie_end - tfsreq_ie_start >= 0) {
+	if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER &&
+	    tfsreq_ie_start && tfsreq_ie_end &&
+	    tfsreq_ie_end - tfsreq_ie_start >= 0) {
 		tfsreq_ie_len = (tfsreq_ie_end + tfsreq_ie_end[1] + 2) -
 			tfsreq_ie_start;
 		wpa_printf(MSG_DEBUG, "TFS Req IE(s) found");
@@ -231,7 +235,7 @@
 				      wnmsleep_ie->action_type,
 				      wnmsleep_ie->intval);
 
-	if (wnmsleep_ie->action_type == 1) {
+	if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) {
 		/* clear the tfs after sending the resp frame */
 		ieee80211_11_set_tfs_ie(hapd, addr, tfsreq_ie_start,
 					&tfsreq_ie_len, WNM_SLEEP_TFS_IE_DEL);
@@ -239,20 +243,29 @@
 }
 
 
-void ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
-				 struct rx_action *action)
+int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
+				struct rx_action *action)
 {
-	u8 *pos = (u8 *) action->data + 1; /* point to the action field */
-	u8 act = *pos;
+	if (action->len < 1 || action->data == NULL)
+		return -1;
 
-	switch (act) {
+	switch (action->data[0]) {
+	case WNM_BSS_TRANS_MGMT_QUERY:
+		wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Query");
+		/* TODO */
+		return -1;
+	case WNM_BSS_TRANS_MGMT_RESP:
+		wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management "
+			   "Response");
+		/* TODO */
+		return -1;
 	case WNM_SLEEP_MODE_REQ:
 		ieee802_11_rx_wnmsleep_req(hapd, action->sa, action->data + 1,
-					   action->len);
-		break;
-	default:
-		break;
+					   action->len - 1);
+		return 0;
 	}
-}
 
-#endif /* CONFIG_IEEE80211V */
+	wpa_printf(MSG_DEBUG, "WNM: Unsupported WNM Action %u from " MACSTR,
+		   action->data[0], MAC2STR(action->sa));
+	return -1;
+}
diff --git a/src/ap/wnm_ap.h b/src/ap/wnm_ap.h
index ab7c4f1..f05726e 100644
--- a/src/ap/wnm_ap.h
+++ b/src/ap/wnm_ap.h
@@ -11,7 +11,7 @@
 
 struct rx_action;
 
-void ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
-				 struct rx_action *action);
+int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
+				struct rx_action *action);
 
 #endif /* WNM_AP_H */
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 0816b25..fa4b1cb 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -282,8 +282,9 @@
 static int wpa_group_init_gmk_and_counter(struct wpa_authenticator *wpa_auth,
 					  struct wpa_group *group)
 {
-	u8 buf[ETH_ALEN + 8 + sizeof(group)];
+	u8 buf[ETH_ALEN + 8 + sizeof(unsigned long)];
 	u8 rkey[32];
+	unsigned long ptr;
 
 	if (random_get_bytes(group->GMK, WPA_GMK_LEN) < 0)
 		return -1;
@@ -295,7 +296,8 @@
 	 */
 	os_memcpy(buf, wpa_auth->addr, ETH_ALEN);
 	wpa_get_ntp_timestamp(buf + ETH_ALEN);
-	os_memcpy(buf + ETH_ALEN + 8, &group, sizeof(group));
+	ptr = (unsigned long) group;
+	os_memcpy(buf + ETH_ALEN + 8, &ptr, sizeof(ptr));
 	if (random_get_bytes(rkey, sizeof(rkey)) < 0)
 		return -1;
 
@@ -2413,11 +2415,9 @@
 				"marking station for GTK rekeying");
 	}
 
-#ifdef CONFIG_IEEE80211V
-	/* Do not rekey GTK/IGTK when STA is in wnmsleep */
+	/* Do not rekey GTK/IGTK when STA is in WNM-Sleep Mode */
 	if (sm->is_wnmsleep)
 		return 0;
-#endif /* CONFIG_IEEE80211V */
 
 	sm->group->GKeyDoneStations++;
 	sm->GUpdateStationKeys = TRUE;
@@ -2427,8 +2427,8 @@
 }
 
 
-#ifdef CONFIG_IEEE80211V
-/* update GTK when exiting wnmsleep mode */
+#ifdef CONFIG_WNM
+/* update GTK when exiting WNM-Sleep Mode */
 void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm)
 {
 	if (sm->is_wnmsleep)
@@ -2446,111 +2446,65 @@
 
 int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos)
 {
-	u8 *subelem;
 	struct wpa_group *gsm = sm->group;
-	size_t subelem_len, pad_len;
-	const u8 *key;
-	size_t key_len;
-	u8 keybuf[32];
-
-	/* GTK subslement */
-	key_len = gsm->GTK_len;
-	if (key_len > sizeof(keybuf))
-		return 0;
+	u8 *start = pos;
 
 	/*
-	 * Pad key for AES Key Wrap if it is not multiple of 8 bytes or is less
-	 * than 16 bytes.
-	 */
-	pad_len = key_len % 8;
-	if (pad_len)
-		pad_len = 8 - pad_len;
-	if (key_len + pad_len < 16)
-		pad_len += 8;
-	if (pad_len) {
-		os_memcpy(keybuf, gsm->GTK[gsm->GN - 1], key_len);
-		os_memset(keybuf + key_len, 0, pad_len);
-		keybuf[key_len] = 0xdd;
-		key_len += pad_len;
-		key = keybuf;
-	} else
-		key = gsm->GTK[gsm->GN - 1];
-
-	/*
+	 * GTK subelement:
 	 * Sub-elem ID[1] | Length[1] | Key Info[2] | Key Length[1] | RSC[8] |
-	 * Key[5..32] | 8 padding.
+	 * Key[5..32]
 	 */
-	subelem_len = 13 + key_len + 8;
-	subelem = os_zalloc(subelem_len);
-	if (subelem == NULL)
-		return 0;
-
-	subelem[0] = WNM_SLEEP_SUBELEM_GTK;
-	subelem[1] = 11 + key_len + 8;
+	*pos++ = WNM_SLEEP_SUBELEM_GTK;
+	*pos++ = 11 + gsm->GTK_len;
 	/* Key ID in B0-B1 of Key Info */
-	WPA_PUT_LE16(&subelem[2], gsm->GN & 0x03);
-	subelem[4] = gsm->GTK_len;
-	if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, subelem + 5) != 0)
-	{
-		os_free(subelem);
+	WPA_PUT_LE16(pos, gsm->GN & 0x03);
+	pos += 2;
+	*pos++ = gsm->GTK_len;
+	if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, pos) != 0)
 		return 0;
-	}
-	if (aes_wrap(sm->PTK.kek, key_len / 8, key, subelem + 13)) {
-		os_free(subelem);
-		return 0;
-	}
+	pos += 8;
+	os_memcpy(pos, gsm->GTK[gsm->GN - 1], gsm->GTK_len);
+	pos += gsm->GTK_len;
 
-	os_memcpy(pos, subelem, subelem_len);
-
-	wpa_hexdump_key(MSG_DEBUG, "Plaintext GTK",
+	wpa_printf(MSG_DEBUG, "WNM: GTK Key ID %u in WNM-Sleep Mode exit",
+		   gsm->GN);
+	wpa_hexdump_key(MSG_DEBUG, "WNM: GTK in WNM-Sleep Mode exit",
 			gsm->GTK[gsm->GN - 1], gsm->GTK_len);
-	os_free(subelem);
 
-	return subelem_len;
+	return pos - start;
 }
 
 
 #ifdef CONFIG_IEEE80211W
 int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos)
 {
-	u8 *subelem, *ptr;
 	struct wpa_group *gsm = sm->group;
-	size_t subelem_len;
+	u8 *start = pos;
 
-	/* IGTK subelement
-	 * Sub-elem ID[1] | Length[1] | KeyID[2] | PN[6] |
-	 * Key[16] | 8 padding */
-	subelem_len = 1 + 1 + 2 + 6 + WPA_IGTK_LEN + 8;
-	subelem = os_zalloc(subelem_len);
-	if (subelem == NULL)
+	/*
+	 * IGTK subelement:
+	 * Sub-elem ID[1] | Length[1] | KeyID[2] | PN[6] | Key[16]
+	 */
+	*pos++ = WNM_SLEEP_SUBELEM_IGTK;
+	*pos++ = 2 + 6 + WPA_IGTK_LEN;
+	WPA_PUT_LE16(pos, gsm->GN_igtk);
+	pos += 2;
+	if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos) != 0)
 		return 0;
+	pos += 6;
 
-	ptr = subelem;
-	*ptr++ = WNM_SLEEP_SUBELEM_IGTK;
-	*ptr++ = subelem_len - 2;
-	WPA_PUT_LE16(ptr, gsm->GN_igtk);
-	ptr += 2;
-	if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, ptr) != 0) {
-		os_free(subelem);
-		return 0;
-	}
-	ptr += 6;
-	if (aes_wrap(sm->PTK.kek, WPA_IGTK_LEN / 8,
-		     gsm->IGTK[gsm->GN_igtk - 4], ptr)) {
-		os_free(subelem);
-		return -1;
-	}
+	os_memcpy(pos, gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN);
+	pos += WPA_IGTK_LEN;
 
-	os_memcpy(pos, subelem, subelem_len);
-
-	wpa_hexdump_key(MSG_DEBUG, "Plaintext IGTK",
+	wpa_printf(MSG_DEBUG, "WNM: IGTK Key ID %u in WNM-Sleep Mode exit",
+		   gsm->GN_igtk);
+	wpa_hexdump_key(MSG_DEBUG, "WNM: IGTK in WNM-Sleep Mode exit",
 			gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN);
-	os_free(subelem);
 
-	return subelem_len;
+	return pos - start;
 }
 #endif /* CONFIG_IEEE80211W */
-#endif /* CONFIG_IEEE80211V */
+#endif /* CONFIG_WNM */
 
 
 static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 6ab170d..465eec6 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -282,14 +282,10 @@
 void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr);
 #endif /* CONFIG_IEEE80211R */
 
-#ifdef CONFIG_IEEE80211V
 void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm);
 void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag);
 int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos);
-#ifdef CONFIG_IEEE80211W
 int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos);
-#endif /* CONFIG_IEEE80211W */
-#endif /* CONFIG_IEEE80211V */
 
 int wpa_auth_uses_sae(struct wpa_state_machine *sm);
 
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index 48bf79b..ccb3f82 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -416,7 +416,7 @@
 		pad_len = 8 - pad_len;
 	if (key_len + pad_len < 16)
 		pad_len += 8;
-	if (pad_len) {
+	if (pad_len && key_len < sizeof(keybuf)) {
 		os_memcpy(keybuf, gsm->GTK[gsm->GN - 1], key_len);
 		os_memset(keybuf + key_len, 0, pad_len);
 		keybuf[key_len] = 0xdd;
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 76c61ea..fdaaaff 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / WPA authenticator glue code
- * 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.
@@ -10,6 +10,7 @@
 
 #include "utils/common.h"
 #include "common/ieee802_11_defs.h"
+#include "common/sae.h"
 #include "eapol_auth/eapol_auth_sm.h"
 #include "eapol_auth/eapol_auth_sm_i.h"
 #include "eap_server/eap.h"
@@ -184,7 +185,17 @@
 {
 	struct hostapd_data *hapd = ctx;
 	struct sta_info *sta = ap_get_sta(hapd, addr);
-	const u8 *psk = hostapd_get_psk(hapd->conf, addr, prev_psk);
+	const u8 *psk;
+
+#ifdef CONFIG_SAE
+	if (sta && sta->auth_alg == WLAN_AUTH_SAE) {
+		if (!sta->sae || prev_psk)
+			return NULL;
+		return sta->sae->pmk;
+	}
+#endif /* CONFIG_SAE */
+
+	psk = hostapd_get_psk(hapd->conf, addr, prev_psk);
 	/*
 	 * This is about to iterate over all psks, prev_psk gives the last
 	 * returned psk which should not be returned again.
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index d5cf2c5..97489d3 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -87,9 +87,7 @@
 	unsigned int ft_completed:1;
 	unsigned int pmk_r1_name_valid:1;
 #endif /* CONFIG_IEEE80211R */
-#ifdef CONFIG_IEEE80211V
 	unsigned int is_wnmsleep:1;
-#endif /* CONFIG_IEEE80211V */
 
 	u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN];
 	int req_replay_counter_used;
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index 4fd0135..cdfcca1 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -564,12 +564,9 @@
 	}
 #endif /* CONFIG_IEEE80211R */
 
-	if (ciphers & WPA_CIPHER_CCMP)
-		sm->pairwise = WPA_CIPHER_CCMP;
-	else if (ciphers & WPA_CIPHER_GCMP)
-		sm->pairwise = WPA_CIPHER_GCMP;
-	else
-		sm->pairwise = WPA_CIPHER_TKIP;
+	sm->pairwise = wpa_pick_pairwise_cipher(ciphers, 0);
+	if (sm->pairwise < 0)
+		return WPA_INVALID_PAIRWISE;
 
 	/* TODO: clear WPA/WPA2 state if STA changes from one to another */
 	if (wpa_ie[0] == WLAN_EID_RSN)
diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
index 85633ec..5ce4f1b 100644
--- a/src/ap/wps_hostapd.c
+++ b/src/ap/wps_hostapd.c
@@ -957,6 +957,9 @@
 	if (conf->ssid.security_policy == SECURITY_STATIC_WEP)
 		cfg.static_wep_only = 1;
 	cfg.dualband = interface_count(hapd->iface) > 1;
+	if ((wps->dev.rf_bands & (WPS_RF_50GHZ | WPS_RF_24GHZ)) ==
+	    (WPS_RF_50GHZ | WPS_RF_24GHZ))
+		cfg.dualband = 1;
 	if (cfg.dualband)
 		wpa_printf(MSG_DEBUG, "WPS: Dualband AP");
 
diff --git a/src/common/defs.h b/src/common/defs.h
index 85e9932..281dd8a 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -297,6 +297,7 @@
 	HOSTAPD_MODE_IEEE80211B,
 	HOSTAPD_MODE_IEEE80211G,
 	HOSTAPD_MODE_IEEE80211A,
+	HOSTAPD_MODE_IEEE80211AD,
 	NUM_HOSTAPD_MODES
 };
 
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index d9d3cd0..98fadda 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -284,6 +284,10 @@
 				break;
 			elems->bss_max_idle_period = pos;
 			break;
+		case WLAN_EID_SSID_LIST:
+			elems->ssid_list = pos;
+			elems->ssid_list_len = elen;
+			break;
 		default:
 			unknown++;
 			if (!show_errors)
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index bfc3eb2..55fa49d 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -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.
@@ -43,6 +43,7 @@
 	const u8 *hs20;
 	const u8 *ext_capab;
 	const u8 *bss_max_idle_period;
+	const u8 *ssid_list;
 
 	u8 ssid_len;
 	u8 supp_rates_len;
@@ -74,6 +75,7 @@
 	u8 interworking_len;
 	u8 hs20_len;
 	u8 ext_capab_len;
+	u8 ssid_list_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 07c8580..f72c0d4 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -232,6 +232,7 @@
 #define WLAN_EID_20_40_BSS_INTOLERANT 73
 #define WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS 74
 #define WLAN_EID_MMIE 76
+#define WLAN_EID_SSID_LIST 84
 #define WLAN_EID_BSS_MAX_IDLE_PERIOD 90
 #define WLAN_EID_TFS_REQ 91
 #define WLAN_EID_TFS_RESP 92
@@ -537,6 +538,16 @@
 					 * Entries */
 					u8 variable[0];
 				} STRUCT_PACKED bss_tm_req;
+				struct {
+					u8 action; /* 8 */
+					u8 dialog_token;
+					u8 status_code;
+					u8 bss_termination_delay;
+					/* Target BSSID (optional),
+					 * BSS Transition Candidate List
+					 * Entries (optional) */
+					u8 variable[0];
+				} STRUCT_PACKED bss_tm_resp;
 			} u;
 		} STRUCT_PACKED action;
 	} u;
@@ -701,6 +712,12 @@
 #define VHT_CAP_RX_ANTENNA_PATTERN                  ((u32) BIT(28))
 #define VHT_CAP_TX_ANTENNA_PATTERN                  ((u32) BIT(29))
 
+/* VHT channel widths */
+#define VHT_CHANWIDTH_USE_HT	0
+#define VHT_CHANWIDTH_80MHZ	1
+#define VHT_CHANWIDTH_160MHZ	2
+#define VHT_CHANWIDTH_80P80MHZ	3
+
 #define OUI_MICROSOFT 0x0050f2 /* Microsoft (also used in Wi-Fi specs)
 				* 00:50:F2 */
 #define WPA_IE_VENDOR_TYPE 0x0050f201
@@ -1048,11 +1065,14 @@
 struct wnm_sleep_element {
 	u8 eid;     /* WLAN_EID_WNMSLEEP */
 	u8 len;
-	u8 action_type; /* WLAN_WNM_SLEEP_ENTER/EXIT */
+	u8 action_type; /* WNM_SLEEP_ENTER/WNM_SLEEP_MODE_EXIT */
 	u8 status;
 	le16 intval;
 } STRUCT_PACKED;
 
+#define WNM_SLEEP_MODE_ENTER 0
+#define WNM_SLEEP_MODE_EXIT 1
+
 enum wnm_sleep_mode_response_status {
 	WNM_STATUS_SLEEP_ACCEPT = 0,
 	WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE = 1,
diff --git a/src/common/sae.c b/src/common/sae.c
new file mode 100644
index 0000000..44ffcd0
--- /dev/null
+++ b/src/common/sae.c
@@ -0,0 +1,1028 @@
+/*
+ * Simultaneous authentication of equals
+ * Copyright (c) 2012-2013, 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 "common.h"
+#include "crypto/crypto.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "crypto/dh_groups.h"
+#include "ieee802_11_defs.h"
+#include "sae.h"
+
+
+int sae_set_group(struct sae_data *sae, int group)
+{
+	struct sae_temporary_data *tmp;
+
+	sae_clear_data(sae);
+	tmp = sae->tmp = os_zalloc(sizeof(*tmp));
+	if (tmp == NULL)
+		return -1;
+
+	/* First, check if this is an ECC group */
+	tmp->ec = crypto_ec_init(group);
+	if (tmp->ec) {
+		sae->group = group;
+		tmp->prime_len = crypto_ec_prime_len(tmp->ec);
+		tmp->prime = crypto_ec_get_prime(tmp->ec);
+		tmp->order = crypto_ec_get_order(tmp->ec);
+		return 0;
+	}
+
+	/* Not an ECC group, check FFC */
+	tmp->dh = dh_groups_get(group);
+	if (tmp->dh) {
+		sae->group = group;
+		tmp->prime_len = tmp->dh->prime_len;
+		if (tmp->prime_len > SAE_MAX_PRIME_LEN) {
+			sae_clear_data(sae);
+			return -1;
+		}
+
+		tmp->prime_buf = crypto_bignum_init_set(tmp->dh->prime,
+							tmp->prime_len);
+		if (tmp->prime_buf == NULL) {
+			sae_clear_data(sae);
+			return -1;
+		}
+		tmp->prime = tmp->prime_buf;
+
+		tmp->order_buf = crypto_bignum_init_set(tmp->dh->order,
+							tmp->dh->order_len);
+		if (tmp->order_buf == NULL) {
+			sae_clear_data(sae);
+			return -1;
+		}
+		tmp->order = tmp->order_buf;
+
+		return 0;
+	}
+
+	/* Unsupported group */
+	return -1;
+}
+
+
+void sae_clear_temp_data(struct sae_data *sae)
+{
+	struct sae_temporary_data *tmp;
+	if (sae == NULL || sae->tmp == NULL)
+		return;
+	tmp = sae->tmp;
+	crypto_ec_deinit(tmp->ec);
+	crypto_bignum_deinit(tmp->prime_buf, 0);
+	crypto_bignum_deinit(tmp->order_buf, 0);
+	crypto_bignum_deinit(tmp->sae_rand, 1);
+	crypto_bignum_deinit(tmp->pwe_ffc, 1);
+	crypto_bignum_deinit(tmp->own_commit_scalar, 0);
+	crypto_bignum_deinit(tmp->own_commit_element_ffc, 0);
+	crypto_bignum_deinit(tmp->peer_commit_element_ffc, 0);
+	crypto_ec_point_deinit(tmp->pwe_ecc, 1);
+	crypto_ec_point_deinit(tmp->own_commit_element_ecc, 0);
+	crypto_ec_point_deinit(tmp->peer_commit_element_ecc, 0);
+	os_free(sae->tmp);
+	sae->tmp = NULL;
+}
+
+
+void sae_clear_data(struct sae_data *sae)
+{
+	if (sae == NULL)
+		return;
+	sae_clear_temp_data(sae);
+	crypto_bignum_deinit(sae->peer_commit_scalar, 0);
+	os_memset(sae, 0, sizeof(*sae));
+}
+
+
+static void buf_shift_right(u8 *buf, size_t len, size_t bits)
+{
+	size_t i;
+	for (i = len - 1; i > 0; i--)
+		buf[i] = (buf[i - 1] << (8 - bits)) | (buf[i] >> bits);
+	buf[0] >>= bits;
+}
+
+
+static struct crypto_bignum * sae_get_rand(struct sae_data *sae)
+{
+	u8 val[SAE_MAX_PRIME_LEN];
+	int iter = 0;
+	struct crypto_bignum *bn = NULL;
+	int order_len_bits = crypto_bignum_bits(sae->tmp->order);
+	size_t order_len = (order_len_bits + 7) / 8;
+
+	if (order_len > sizeof(val))
+		return NULL;
+
+	for (;;) {
+		if (iter++ > 100)
+			return NULL;
+		if (random_get_bytes(val, order_len) < 0)
+			return NULL;
+		if (order_len_bits % 8)
+			buf_shift_right(val, order_len, 8 - order_len_bits % 8);
+		bn = crypto_bignum_init_set(val, order_len);
+		if (bn == NULL)
+			return NULL;
+		if (crypto_bignum_is_zero(bn) ||
+		    crypto_bignum_is_one(bn) ||
+		    crypto_bignum_cmp(bn, sae->tmp->order) >= 0)
+			continue;
+		break;
+	}
+
+	os_memset(val, 0, order_len);
+	return bn;
+}
+
+
+static struct crypto_bignum * sae_get_rand_and_mask(struct sae_data *sae)
+{
+	crypto_bignum_deinit(sae->tmp->sae_rand, 1);
+	sae->tmp->sae_rand = sae_get_rand(sae);
+	if (sae->tmp->sae_rand == NULL)
+		return NULL;
+	return sae_get_rand(sae);
+}
+
+
+static void sae_pwd_seed_key(const u8 *addr1, const u8 *addr2, u8 *key)
+{
+	wpa_printf(MSG_DEBUG, "SAE: PWE derivation - addr1=" MACSTR
+		   " addr2=" MACSTR, MAC2STR(addr1), MAC2STR(addr2));
+	if (os_memcmp(addr1, addr2, ETH_ALEN) > 0) {
+		os_memcpy(key, addr1, ETH_ALEN);
+		os_memcpy(key + ETH_ALEN, addr2, ETH_ALEN);
+	} else {
+		os_memcpy(key, addr2, ETH_ALEN);
+		os_memcpy(key + ETH_ALEN, addr1, ETH_ALEN);
+	}
+}
+
+
+static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed,
+				 struct crypto_ec_point *pwe)
+{
+	u8 pwd_value[SAE_MAX_ECC_PRIME_LEN], prime[SAE_MAX_ECC_PRIME_LEN];
+	struct crypto_bignum *x;
+	int y_bit;
+	size_t bits;
+
+	if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime),
+				 sae->tmp->prime_len) < 0)
+		return -1;
+
+	wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN);
+
+	/* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */
+	bits = crypto_ec_prime_len_bits(sae->tmp->ec);
+	sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking",
+			prime, sae->tmp->prime_len, pwd_value, bits);
+	if (bits % 8)
+		buf_shift_right(pwd_value, sizeof(pwd_value), 8 - bits % 8);
+	wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value",
+			pwd_value, sae->tmp->prime_len);
+
+	if (os_memcmp(pwd_value, prime, sae->tmp->prime_len) >= 0)
+		return 0;
+
+	y_bit = pwd_seed[SHA256_MAC_LEN - 1] & 0x01;
+
+	x = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len);
+	if (x == NULL)
+		return -1;
+	if (crypto_ec_point_solve_y_coord(sae->tmp->ec, pwe, x, y_bit) < 0) {
+		crypto_bignum_deinit(x, 0);
+		wpa_printf(MSG_DEBUG, "SAE: No solution found");
+		return 0;
+	}
+	crypto_bignum_deinit(x, 0);
+
+	wpa_printf(MSG_DEBUG, "SAE: PWE found");
+
+	return 1;
+}
+
+
+static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed,
+				 struct crypto_bignum *pwe)
+{
+	u8 pwd_value[SAE_MAX_PRIME_LEN];
+	size_t bits = sae->tmp->prime_len * 8;
+	u8 exp[1];
+	struct crypto_bignum *a, *b;
+	int res;
+
+	wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN);
+
+	/* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */
+	sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking",
+			sae->tmp->dh->prime, sae->tmp->prime_len, pwd_value,
+			bits);
+	if (bits % 8)
+		buf_shift_right(pwd_value, sizeof(pwd_value), 8 - bits % 8);
+	wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", pwd_value,
+			sae->tmp->prime_len);
+
+	if (os_memcmp(pwd_value, sae->tmp->dh->prime, sae->tmp->prime_len) >= 0)
+	{
+		wpa_printf(MSG_DEBUG, "SAE: pwd-value >= p");
+		return 0;
+	}
+
+	/* PWE = pwd-value^((p-1)/r) modulo p */
+
+	a = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len);
+
+	if (sae->tmp->dh->safe_prime) {
+		/*
+		 * r = (p-1)/2 for the group used here, so this becomes:
+		 * PWE = pwd-value^2 modulo p
+		 */
+		exp[0] = 2;
+		b = crypto_bignum_init_set(exp, sizeof(exp));
+	} else {
+		/* Calculate exponent: (p-1)/r */
+		exp[0] = 1;
+		b = crypto_bignum_init_set(exp, sizeof(exp));
+		if (b == NULL ||
+		    crypto_bignum_sub(sae->tmp->prime, b, b) < 0 ||
+		    crypto_bignum_div(b, sae->tmp->order, b) < 0) {
+			crypto_bignum_deinit(b, 0);
+			b = NULL;
+		}
+	}
+
+	if (a == NULL || b == NULL)
+		res = -1;
+	else
+		res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe);
+
+	crypto_bignum_deinit(a, 0);
+	crypto_bignum_deinit(b, 0);
+
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Failed to calculate PWE");
+		return -1;
+	}
+
+	/* if (PWE > 1) --> found */
+	if (crypto_bignum_is_zero(pwe) || crypto_bignum_is_one(pwe)) {
+		wpa_printf(MSG_DEBUG, "SAE: PWE <= 1");
+		return 0;
+	}
+
+	wpa_printf(MSG_DEBUG, "SAE: PWE found");
+	return 1;
+}
+
+
+static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
+			      const u8 *addr2, const u8 *password,
+			      size_t password_len)
+{
+	u8 counter, k = 4;
+	u8 addrs[2 * ETH_ALEN];
+	const u8 *addr[2];
+	size_t len[2];
+	int found = 0;
+	struct crypto_ec_point *pwe_tmp;
+
+	if (sae->tmp->pwe_ecc == NULL) {
+		sae->tmp->pwe_ecc = crypto_ec_point_init(sae->tmp->ec);
+		if (sae->tmp->pwe_ecc == NULL)
+			return -1;
+	}
+	pwe_tmp = crypto_ec_point_init(sae->tmp->ec);
+	if (pwe_tmp == NULL)
+		return -1;
+
+	wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
+			      password, password_len);
+
+	/*
+	 * H(salt, ikm) = HMAC-SHA256(salt, ikm)
+	 * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC),
+	 *              password || counter)
+	 */
+	sae_pwd_seed_key(addr1, addr2, addrs);
+
+	addr[0] = password;
+	len[0] = password_len;
+	addr[1] = &counter;
+	len[1] = sizeof(counter);
+
+	/*
+	 * Continue for at least k iterations to protect against side-channel
+	 * attacks that attempt to determine the number of iterations required
+	 * in the loop.
+	 */
+	for (counter = 1; counter < k || !found; counter++) {
+		u8 pwd_seed[SHA256_MAC_LEN];
+		int res;
+
+		if (counter > 200) {
+			/* This should not happen in practice */
+			wpa_printf(MSG_DEBUG, "SAE: Failed to derive PWE");
+			break;
+		}
+
+		wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter);
+		if (hmac_sha256_vector(addrs, sizeof(addrs), 2, addr, len,
+				       pwd_seed) < 0)
+			break;
+		res = sae_test_pwd_seed_ecc(sae, pwd_seed,
+					    found ? pwe_tmp :
+					    sae->tmp->pwe_ecc);
+		if (res < 0)
+			break;
+		if (res == 0)
+			continue;
+		if (found) {
+			wpa_printf(MSG_DEBUG, "SAE: Ignore this PWE (one was "
+				   "already selected)");
+		} else {
+			wpa_printf(MSG_DEBUG, "SAE: Use this PWE");
+			found = 1;
+		}
+	}
+
+	crypto_ec_point_deinit(pwe_tmp, 1);
+
+	return found ? 0 : -1;
+}
+
+
+static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1,
+			      const u8 *addr2, const u8 *password,
+			      size_t password_len)
+{
+	u8 counter;
+	u8 addrs[2 * ETH_ALEN];
+	const u8 *addr[2];
+	size_t len[2];
+	int found = 0;
+
+	if (sae->tmp->pwe_ffc == NULL) {
+		sae->tmp->pwe_ffc = crypto_bignum_init();
+		if (sae->tmp->pwe_ffc == NULL)
+			return -1;
+	}
+
+	wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
+			      password, password_len);
+
+	/*
+	 * H(salt, ikm) = HMAC-SHA256(salt, ikm)
+	 * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC),
+	 *              password || counter)
+	 */
+	sae_pwd_seed_key(addr1, addr2, addrs);
+
+	addr[0] = password;
+	len[0] = password_len;
+	addr[1] = &counter;
+	len[1] = sizeof(counter);
+
+	for (counter = 1; !found; counter++) {
+		u8 pwd_seed[SHA256_MAC_LEN];
+		int res;
+
+		if (counter > 200) {
+			/* This should not happen in practice */
+			wpa_printf(MSG_DEBUG, "SAE: Failed to derive PWE");
+			break;
+		}
+
+		wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter);
+		if (hmac_sha256_vector(addrs, sizeof(addrs), 2, addr, len,
+				       pwd_seed) < 0)
+			break;
+		res = sae_test_pwd_seed_ffc(sae, pwd_seed, sae->tmp->pwe_ffc);
+		if (res < 0)
+			break;
+		if (res > 0) {
+			wpa_printf(MSG_DEBUG, "SAE: Use this PWE");
+			found = 1;
+		}
+	}
+
+	return found ? 0 : -1;
+}
+
+
+static int sae_derive_commit_element_ecc(struct sae_data *sae,
+					 struct crypto_bignum *mask)
+{
+	/* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */
+	if (!sae->tmp->own_commit_element_ecc) {
+		sae->tmp->own_commit_element_ecc =
+			crypto_ec_point_init(sae->tmp->ec);
+		if (!sae->tmp->own_commit_element_ecc)
+			return -1;
+	}
+
+	if (crypto_ec_point_mul(sae->tmp->ec, sae->tmp->pwe_ecc, mask,
+				sae->tmp->own_commit_element_ecc) < 0 ||
+	    crypto_ec_point_invert(sae->tmp->ec,
+				   sae->tmp->own_commit_element_ecc) < 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Could not compute commit-element");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int sae_derive_commit_element_ffc(struct sae_data *sae,
+					 struct crypto_bignum *mask)
+{
+	/* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */
+	if (!sae->tmp->own_commit_element_ffc) {
+		sae->tmp->own_commit_element_ffc = crypto_bignum_init();
+		if (!sae->tmp->own_commit_element_ffc)
+			return -1;
+	}
+
+	if (crypto_bignum_exptmod(sae->tmp->pwe_ffc, mask, sae->tmp->prime,
+				  sae->tmp->own_commit_element_ffc) < 0 ||
+	    crypto_bignum_inverse(sae->tmp->own_commit_element_ffc,
+				  sae->tmp->prime,
+				  sae->tmp->own_commit_element_ffc) < 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Could not compute commit-element");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int sae_derive_commit(struct sae_data *sae)
+{
+	struct crypto_bignum *mask;
+	int ret = -1;
+
+	mask = sae_get_rand_and_mask(sae);
+	if (mask == NULL) {
+		wpa_printf(MSG_DEBUG, "SAE: Could not get rand/mask");
+		return -1;
+	}
+
+	/* commit-scalar = (rand + mask) modulo r */
+	if (!sae->tmp->own_commit_scalar) {
+		sae->tmp->own_commit_scalar = crypto_bignum_init();
+		if (!sae->tmp->own_commit_scalar)
+			goto fail;
+	}
+	crypto_bignum_add(sae->tmp->sae_rand, mask,
+			  sae->tmp->own_commit_scalar);
+	crypto_bignum_mod(sae->tmp->own_commit_scalar, sae->tmp->order,
+			  sae->tmp->own_commit_scalar);
+
+	if (sae->tmp->ec && sae_derive_commit_element_ecc(sae, mask) < 0)
+		goto fail;
+	if (sae->tmp->dh && sae_derive_commit_element_ffc(sae, mask) < 0)
+		goto fail;
+
+	ret = 0;
+fail:
+	crypto_bignum_deinit(mask, 1);
+	return ret;
+}
+
+
+int sae_prepare_commit(const u8 *addr1, const u8 *addr2,
+		       const u8 *password, size_t password_len,
+		       struct sae_data *sae)
+{
+	if (sae->tmp->ec && sae_derive_pwe_ecc(sae, addr1, addr2, password,
+					  password_len) < 0)
+		return -1;
+	if (sae->tmp->dh && sae_derive_pwe_ffc(sae, addr1, addr2, password,
+					  password_len) < 0)
+		return -1;
+	if (sae_derive_commit(sae) < 0)
+		return -1;
+	return 0;
+}
+
+
+static int sae_derive_k_ecc(struct sae_data *sae, u8 *k)
+{
+	struct crypto_ec_point *K;
+	int ret = -1;
+
+	K = crypto_ec_point_init(sae->tmp->ec);
+	if (K == NULL)
+		goto fail;
+
+	if (!crypto_ec_point_is_on_curve(sae->tmp->ec,
+					 sae->tmp->peer_commit_element_ecc)) {
+		wpa_printf(MSG_DEBUG, "SAE: Peer element is not on curve");
+		goto fail;
+	}
+
+	/*
+	 * K = scalar-op(rand, (elem-op(scalar-op(peer-commit-scalar, PWE),
+	 *                                        PEER-COMMIT-ELEMENT)))
+	 * If K is identity element (point-at-infinity), reject
+	 * k = F(K) (= x coordinate)
+	 */
+
+	if (crypto_ec_point_mul(sae->tmp->ec, sae->tmp->pwe_ecc,
+				sae->peer_commit_scalar, K) < 0 ||
+	    crypto_ec_point_add(sae->tmp->ec, K,
+				sae->tmp->peer_commit_element_ecc, K) < 0 ||
+	    crypto_ec_point_mul(sae->tmp->ec, K, sae->tmp->sae_rand, K) < 0 ||
+	    crypto_ec_point_is_at_infinity(sae->tmp->ec, K) ||
+	    crypto_ec_point_to_bin(sae->tmp->ec, K, k, NULL) < 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Failed to calculate K and k");
+		goto fail;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "SAE: k", k, sae->tmp->prime_len);
+
+	ret = 0;
+fail:
+	crypto_ec_point_deinit(K, 1);
+	return ret;
+}
+
+
+static int sae_derive_k_ffc(struct sae_data *sae, u8 *k)
+{
+	struct crypto_bignum *K;
+	int ret = -1;
+
+	K = crypto_bignum_init();
+	if (K == NULL)
+		goto fail;
+
+	/*
+	 * K = scalar-op(rand, (elem-op(scalar-op(peer-commit-scalar, PWE),
+	 *                                        PEER-COMMIT-ELEMENT)))
+	 * If K is identity element (one), reject.
+	 * k = F(K) (= x coordinate)
+	 */
+
+	if (crypto_bignum_exptmod(sae->tmp->pwe_ffc, sae->peer_commit_scalar,
+				  sae->tmp->prime, K) < 0 ||
+	    crypto_bignum_mulmod(K, sae->tmp->peer_commit_element_ffc,
+				 sae->tmp->prime, K) < 0 ||
+	    crypto_bignum_exptmod(K, sae->tmp->sae_rand, sae->tmp->prime, K) < 0
+	    ||
+	    crypto_bignum_is_one(K) ||
+	    crypto_bignum_to_bin(K, k, SAE_MAX_PRIME_LEN, sae->tmp->prime_len) <
+	    0) {
+		wpa_printf(MSG_DEBUG, "SAE: Failed to calculate K and k");
+		goto fail;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "SAE: k", k, sae->tmp->prime_len);
+
+	ret = 0;
+fail:
+	crypto_bignum_deinit(K, 1);
+	return ret;
+}
+
+
+static int sae_derive_keys(struct sae_data *sae, const u8 *k)
+{
+	u8 null_key[SAE_KEYSEED_KEY_LEN], val[SAE_MAX_PRIME_LEN];
+	u8 keyseed[SHA256_MAC_LEN];
+	u8 keys[SAE_KCK_LEN + SAE_PMK_LEN];
+	struct crypto_bignum *tmp;
+	int ret = -1;
+
+	tmp = crypto_bignum_init();
+	if (tmp == NULL)
+		goto fail;
+
+	/* keyseed = H(<0>32, k)
+	 * KCK || PMK = KDF-512(keyseed, "SAE KCK and PMK",
+	 *                      (commit-scalar + peer-commit-scalar) modulo r)
+	 * PMKID = L((commit-scalar + peer-commit-scalar) modulo r, 0, 128)
+	 */
+
+	os_memset(null_key, 0, sizeof(null_key));
+	hmac_sha256(null_key, sizeof(null_key), k, sae->tmp->prime_len,
+		    keyseed);
+	wpa_hexdump_key(MSG_DEBUG, "SAE: keyseed", keyseed, sizeof(keyseed));
+
+	crypto_bignum_add(sae->tmp->own_commit_scalar, sae->peer_commit_scalar,
+			  tmp);
+	crypto_bignum_mod(tmp, sae->tmp->order, tmp);
+	crypto_bignum_to_bin(tmp, val, sizeof(val), sae->tmp->prime_len);
+	wpa_hexdump(MSG_DEBUG, "SAE: PMKID", val, SAE_PMKID_LEN);
+	sha256_prf(keyseed, sizeof(keyseed), "SAE KCK and PMK",
+		   val, sae->tmp->prime_len, keys, sizeof(keys));
+	os_memcpy(sae->tmp->kck, keys, SAE_KCK_LEN);
+	os_memcpy(sae->pmk, keys + SAE_KCK_LEN, SAE_PMK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "SAE: KCK", sae->tmp->kck, SAE_KCK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "SAE: PMK", sae->pmk, SAE_PMK_LEN);
+
+	ret = 0;
+fail:
+	crypto_bignum_deinit(tmp, 0);
+	return ret;
+}
+
+
+int sae_process_commit(struct sae_data *sae)
+{
+	u8 k[SAE_MAX_PRIME_LEN];
+	if ((sae->tmp->ec && sae_derive_k_ecc(sae, k) < 0) ||
+	    (sae->tmp->dh && sae_derive_k_ffc(sae, k) < 0) ||
+	    sae_derive_keys(sae, k) < 0)
+		return -1;
+	return 0;
+}
+
+
+void sae_write_commit(struct sae_data *sae, struct wpabuf *buf,
+		      const struct wpabuf *token)
+{
+	u8 *pos;
+	wpabuf_put_le16(buf, sae->group); /* Finite Cyclic Group */
+	if (token)
+		wpabuf_put_buf(buf, token);
+	pos = wpabuf_put(buf, sae->tmp->prime_len);
+	crypto_bignum_to_bin(sae->tmp->own_commit_scalar, pos,
+			     sae->tmp->prime_len, sae->tmp->prime_len);
+	wpa_hexdump(MSG_DEBUG, "SAE: own commit-scalar",
+		    pos, sae->tmp->prime_len);
+	if (sae->tmp->ec) {
+		pos = wpabuf_put(buf, 2 * sae->tmp->prime_len);
+		crypto_ec_point_to_bin(sae->tmp->ec,
+				       sae->tmp->own_commit_element_ecc,
+				       pos, pos + sae->tmp->prime_len);
+		wpa_hexdump(MSG_DEBUG, "SAE: own commit-element(x)",
+			    pos, sae->tmp->prime_len);
+		wpa_hexdump(MSG_DEBUG, "SAE: own commit-element(y)",
+			    pos + sae->tmp->prime_len, sae->tmp->prime_len);
+	} else {
+		pos = wpabuf_put(buf, sae->tmp->prime_len);
+		crypto_bignum_to_bin(sae->tmp->own_commit_element_ffc, pos,
+				     sae->tmp->prime_len, sae->tmp->prime_len);
+		wpa_hexdump(MSG_DEBUG, "SAE: own commit-element",
+			    pos, sae->tmp->prime_len);
+	}
+}
+
+
+static u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups,
+			     u16 group)
+{
+	if (allowed_groups) {
+		int i;
+		for (i = 0; allowed_groups[i] >= 0; i++) {
+			if (allowed_groups[i] == group)
+				break;
+		}
+		if (allowed_groups[i] != group) {
+			wpa_printf(MSG_DEBUG, "SAE: Proposed group %u not "
+				   "enabled in the current configuration",
+				   group);
+			return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+		}
+	}
+
+	if (sae->state == SAE_COMMITTED && group != sae->group) {
+		wpa_printf(MSG_DEBUG, "SAE: Do not allow group to be changed");
+		return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+	}
+
+	if (group != sae->group && sae_set_group(sae, group) < 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Unsupported Finite Cyclic Group %u",
+			   group);
+		return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+	}
+
+	if (sae->tmp->dh && !allowed_groups) {
+		wpa_printf(MSG_DEBUG, "SAE: Do not allow FFC group %u without "
+			   "explicit configuration enabling it", group);
+		return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+	}
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+static void sae_parse_commit_token(struct sae_data *sae, const u8 **pos,
+				   const u8 *end, const u8 **token,
+				   size_t *token_len)
+{
+	if (*pos + (sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len < end) {
+		size_t tlen = end - (*pos + (sae->tmp->ec ? 3 : 2) *
+				     sae->tmp->prime_len);
+		wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token", *pos, tlen);
+		if (token)
+			*token = *pos;
+		if (token_len)
+			*token_len = tlen;
+		*pos += tlen;
+	} else {
+		if (token)
+			*token = NULL;
+		if (token_len)
+			*token_len = 0;
+	}
+}
+
+
+static u16 sae_parse_commit_scalar(struct sae_data *sae, const u8 **pos,
+				   const u8 *end)
+{
+	struct crypto_bignum *peer_scalar;
+
+	if (*pos + sae->tmp->prime_len > end) {
+		wpa_printf(MSG_DEBUG, "SAE: Not enough data for scalar");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	peer_scalar = crypto_bignum_init_set(*pos, sae->tmp->prime_len);
+	if (peer_scalar == NULL)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+	/*
+	 * IEEE Std 802.11-2012, 11.3.8.6.1: If there is a protocol instance for
+	 * the peer and it is in Authenticated state, the new Commit Message
+	 * shall be dropped if the peer-scalar is identical to the one used in
+	 * the existing protocol instance.
+	 */
+	if (sae->state == SAE_ACCEPTED && sae->peer_commit_scalar &&
+	    crypto_bignum_cmp(sae->peer_commit_scalar, peer_scalar) == 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Do not accept re-use of previous "
+			   "peer-commit-scalar");
+		crypto_bignum_deinit(peer_scalar, 0);
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	/* 0 < scalar < r */
+	if (crypto_bignum_is_zero(peer_scalar) ||
+	    crypto_bignum_cmp(peer_scalar, sae->tmp->order) >= 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Invalid peer scalar");
+		crypto_bignum_deinit(peer_scalar, 0);
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+
+	crypto_bignum_deinit(sae->peer_commit_scalar, 0);
+	sae->peer_commit_scalar = peer_scalar;
+	wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-scalar",
+		    *pos, sae->tmp->prime_len);
+	*pos += sae->tmp->prime_len;
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 *pos,
+					const u8 *end)
+{
+	u8 prime[SAE_MAX_ECC_PRIME_LEN];
+
+	if (pos + 2 * sae->tmp->prime_len > end) {
+		wpa_printf(MSG_DEBUG, "SAE: Not enough data for "
+			   "commit-element");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime),
+				 sae->tmp->prime_len) < 0)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+	/* element x and y coordinates < p */
+	if (os_memcmp(pos, prime, sae->tmp->prime_len) >= 0 ||
+	    os_memcmp(pos + sae->tmp->prime_len + sae->tmp->prime_len, prime,
+		      sae->tmp->prime_len) >= 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Invalid coordinates in peer "
+			   "element");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(x)",
+		    pos, sae->tmp->prime_len);
+	wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(y)",
+		    pos + sae->tmp->prime_len, sae->tmp->prime_len);
+
+	crypto_ec_point_deinit(sae->tmp->peer_commit_element_ecc, 0);
+	sae->tmp->peer_commit_element_ecc =
+		crypto_ec_point_from_bin(sae->tmp->ec, pos);
+	if (sae->tmp->peer_commit_element_ecc == NULL)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 *pos,
+					const u8 *end)
+{
+	if (pos + sae->tmp->prime_len > end) {
+		wpa_printf(MSG_DEBUG, "SAE: Not enough data for "
+			   "commit-element");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+	wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element", pos,
+		    sae->tmp->prime_len);
+
+	crypto_bignum_deinit(sae->tmp->peer_commit_element_ffc, 0);
+	sae->tmp->peer_commit_element_ffc =
+		crypto_bignum_init_set(pos, sae->tmp->prime_len);
+	if (sae->tmp->peer_commit_element_ffc == NULL)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	if (crypto_bignum_is_zero(sae->tmp->peer_commit_element_ffc) ||
+	    crypto_bignum_is_one(sae->tmp->peer_commit_element_ffc) ||
+	    crypto_bignum_cmp(sae->tmp->peer_commit_element_ffc,
+			      sae->tmp->prime) >= 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Invalid peer element");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+static u16 sae_parse_commit_element(struct sae_data *sae, const u8 *pos,
+				    const u8 *end)
+{
+	if (sae->tmp->dh)
+		return sae_parse_commit_element_ffc(sae, pos, end);
+	return sae_parse_commit_element_ecc(sae, pos, end);
+}
+
+
+u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len,
+		     const u8 **token, size_t *token_len, int *allowed_groups)
+{
+	const u8 *pos = data, *end = data + len;
+	u16 res;
+
+	/* Check Finite Cyclic Group */
+	if (pos + 2 > end)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	res = sae_group_allowed(sae, allowed_groups, WPA_GET_LE16(pos));
+	if (res != WLAN_STATUS_SUCCESS)
+		return res;
+	pos += 2;
+
+	/* Optional Anti-Clogging Token */
+	sae_parse_commit_token(sae, &pos, end, token, token_len);
+
+	/* commit-scalar */
+	res = sae_parse_commit_scalar(sae, &pos, end);
+	if (res != WLAN_STATUS_SUCCESS)
+		return res;
+
+	/* commit-element */
+	return sae_parse_commit_element(sae, pos, end);
+}
+
+
+static void sae_cn_confirm(struct sae_data *sae, const u8 *sc,
+			   const struct crypto_bignum *scalar1,
+			   const u8 *element1, size_t element1_len,
+			   const struct crypto_bignum *scalar2,
+			   const u8 *element2, size_t element2_len,
+			   u8 *confirm)
+{
+	const u8 *addr[5];
+	size_t len[5];
+	u8 scalar_b1[SAE_MAX_PRIME_LEN], scalar_b2[SAE_MAX_PRIME_LEN];
+
+	/* Confirm
+	 * CN(key, X, Y, Z, ...) =
+	 *    HMAC-SHA256(key, D2OS(X) || D2OS(Y) || D2OS(Z) | ...)
+	 * confirm = CN(KCK, send-confirm, commit-scalar, COMMIT-ELEMENT,
+	 *              peer-commit-scalar, PEER-COMMIT-ELEMENT)
+	 * verifier = CN(KCK, peer-send-confirm, peer-commit-scalar,
+	 *               PEER-COMMIT-ELEMENT, commit-scalar, COMMIT-ELEMENT)
+	 */
+	addr[0] = sc;
+	len[0] = 2;
+	crypto_bignum_to_bin(scalar1, scalar_b1, sizeof(scalar_b1),
+			     sae->tmp->prime_len);
+	addr[1] = scalar_b1;
+	len[1] = sae->tmp->prime_len;
+	addr[2] = element1;
+	len[2] = element1_len;
+	crypto_bignum_to_bin(scalar2, scalar_b2, sizeof(scalar_b2),
+			     sae->tmp->prime_len);
+	addr[3] = scalar_b2;
+	len[3] = sae->tmp->prime_len;
+	addr[4] = element2;
+	len[4] = element2_len;
+	hmac_sha256_vector(sae->tmp->kck, sizeof(sae->tmp->kck), 5, addr, len,
+			   confirm);
+}
+
+
+static void sae_cn_confirm_ecc(struct sae_data *sae, const u8 *sc,
+			       const struct crypto_bignum *scalar1,
+			       const struct crypto_ec_point *element1,
+			       const struct crypto_bignum *scalar2,
+			       const struct crypto_ec_point *element2,
+			       u8 *confirm)
+{
+	u8 element_b1[2 * SAE_MAX_ECC_PRIME_LEN];
+	u8 element_b2[2 * SAE_MAX_ECC_PRIME_LEN];
+
+	crypto_ec_point_to_bin(sae->tmp->ec, element1, element_b1,
+			       element_b1 + sae->tmp->prime_len);
+	crypto_ec_point_to_bin(sae->tmp->ec, element2, element_b2,
+			       element_b2 + sae->tmp->prime_len);
+
+	sae_cn_confirm(sae, sc, scalar1, element_b1, 2 * sae->tmp->prime_len,
+		       scalar2, element_b2, 2 * sae->tmp->prime_len, confirm);
+}
+
+
+static void sae_cn_confirm_ffc(struct sae_data *sae, const u8 *sc,
+			       const struct crypto_bignum *scalar1,
+			       const struct crypto_bignum *element1,
+			       const struct crypto_bignum *scalar2,
+			       const struct crypto_bignum *element2,
+			       u8 *confirm)
+{
+	u8 element_b1[SAE_MAX_PRIME_LEN];
+	u8 element_b2[SAE_MAX_PRIME_LEN];
+
+	crypto_bignum_to_bin(element1, element_b1, sizeof(element_b1),
+			     sae->tmp->prime_len);
+	crypto_bignum_to_bin(element2, element_b2, sizeof(element_b2),
+			     sae->tmp->prime_len);
+
+	sae_cn_confirm(sae, sc, scalar1, element_b1, sae->tmp->prime_len,
+		       scalar2, element_b2, sae->tmp->prime_len, confirm);
+}
+
+
+void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf)
+{
+	const u8 *sc;
+
+	/* Send-Confirm */
+	sc = wpabuf_put(buf, 0);
+	wpabuf_put_le16(buf, sae->send_confirm);
+	sae->send_confirm++;
+
+	if (sae->tmp->ec)
+		sae_cn_confirm_ecc(sae, sc, sae->tmp->own_commit_scalar,
+				   sae->tmp->own_commit_element_ecc,
+				   sae->peer_commit_scalar,
+				   sae->tmp->peer_commit_element_ecc,
+				   wpabuf_put(buf, SHA256_MAC_LEN));
+	else
+		sae_cn_confirm_ffc(sae, sc, sae->tmp->own_commit_scalar,
+				   sae->tmp->own_commit_element_ffc,
+				   sae->peer_commit_scalar,
+				   sae->tmp->peer_commit_element_ffc,
+				   wpabuf_put(buf, SHA256_MAC_LEN));
+}
+
+
+int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len)
+{
+	u8 verifier[SHA256_MAC_LEN];
+
+	if (len < 2 + SHA256_MAC_LEN) {
+		wpa_printf(MSG_DEBUG, "SAE: Too short confirm message");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", WPA_GET_LE16(data));
+
+	if (sae->tmp->ec)
+		sae_cn_confirm_ecc(sae, data, sae->peer_commit_scalar,
+				   sae->tmp->peer_commit_element_ecc,
+				   sae->tmp->own_commit_scalar,
+				   sae->tmp->own_commit_element_ecc,
+				   verifier);
+	else
+		sae_cn_confirm_ffc(sae, data, sae->peer_commit_scalar,
+				   sae->tmp->peer_commit_element_ffc,
+				   sae->tmp->own_commit_scalar,
+				   sae->tmp->own_commit_element_ffc,
+				   verifier);
+
+	if (os_memcmp(verifier, data + 2, SHA256_MAC_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Confirm mismatch");
+		wpa_hexdump(MSG_DEBUG, "SAE: Received confirm",
+			    data + 2, SHA256_MAC_LEN);
+		wpa_hexdump(MSG_DEBUG, "SAE: Calculated verifier",
+			    verifier, SHA256_MAC_LEN);
+		return -1;
+	}
+
+	return 0;
+}
diff --git a/src/common/sae.h b/src/common/sae.h
new file mode 100644
index 0000000..d82a98e
--- /dev/null
+++ b/src/common/sae.h
@@ -0,0 +1,64 @@
+/*
+ * Simultaneous authentication of equals
+ * Copyright (c) 2012-2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SAE_H
+#define SAE_H
+
+#define SAE_KCK_LEN 32
+#define SAE_PMK_LEN 32
+#define SAE_PMKID_LEN 16
+#define SAE_KEYSEED_KEY_LEN 32
+#define SAE_MAX_PRIME_LEN 512
+#define SAE_MAX_ECC_PRIME_LEN 66
+#define SAE_COMMIT_MAX_LEN (2 + 3 * SAE_MAX_PRIME_LEN)
+#define SAE_CONFIRM_MAX_LEN (2 + SAE_MAX_PRIME_LEN)
+
+struct sae_temporary_data {
+	u8 kck[SAE_KCK_LEN];
+	struct crypto_bignum *own_commit_scalar;
+	struct crypto_bignum *own_commit_element_ffc;
+	struct crypto_ec_point *own_commit_element_ecc;
+	struct crypto_bignum *peer_commit_element_ffc;
+	struct crypto_ec_point *peer_commit_element_ecc;
+	struct crypto_ec_point *pwe_ecc;
+	struct crypto_bignum *pwe_ffc;
+	struct crypto_bignum *sae_rand;
+	struct crypto_ec *ec;
+	int prime_len;
+	const struct dh_group *dh;
+	const struct crypto_bignum *prime;
+	const struct crypto_bignum *order;
+	struct crypto_bignum *prime_buf;
+	struct crypto_bignum *order_buf;
+};
+
+struct sae_data {
+	enum { SAE_NOTHING, SAE_COMMITTED, SAE_CONFIRMED, SAE_ACCEPTED } state;
+	u16 send_confirm;
+	u8 pmk[SAE_PMK_LEN];
+	struct crypto_bignum *peer_commit_scalar;
+	int group;
+	struct sae_temporary_data *tmp;
+};
+
+int sae_set_group(struct sae_data *sae, int group);
+void sae_clear_temp_data(struct sae_data *sae);
+void sae_clear_data(struct sae_data *sae);
+
+int sae_prepare_commit(const u8 *addr1, const u8 *addr2,
+		       const u8 *password, size_t password_len,
+		       struct sae_data *sae);
+int sae_process_commit(struct sae_data *sae);
+void sae_write_commit(struct sae_data *sae, struct wpabuf *buf,
+		      const struct wpabuf *token);
+u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len,
+		     const u8 **token, size_t *token_len, int *allowed_groups);
+void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf);
+int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len);
+
+#endif /* SAE_H */
diff --git a/src/common/version.h b/src/common/version.h
index 7afba48..2faa8c7 100644
--- a/src/common/version.h
+++ b/src/common/version.h
@@ -5,6 +5,6 @@
 #define VERSION_STR_POSTFIX ""
 #endif /* VERSION_STR_POSTFIX */
 
-#define VERSION_STR "2.0-devel" VERSION_STR_POSTFIX
+#define VERSION_STR "2.1-devel" VERSION_STR_POSTFIX
 
 #endif /* VERSION_H */
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index 8d7a11c..a8cf6be 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -1,6 +1,6 @@
 /*
  * WPA/RSN - Shared functions for supplicant and authenticator
- * Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -1132,6 +1132,26 @@
 }
 
 
+enum wpa_cipher wpa_cipher_to_suite_driver(int cipher)
+{
+	switch (cipher) {
+	case WPA_CIPHER_NONE:
+		return CIPHER_NONE;
+	case WPA_CIPHER_WEP40:
+		return CIPHER_WEP40;
+	case WPA_CIPHER_WEP104:
+		return CIPHER_WEP104;
+	case WPA_CIPHER_CCMP:
+		return CIPHER_CCMP;
+	case WPA_CIPHER_GCMP:
+		return CIPHER_GCMP;
+	case WPA_CIPHER_TKIP:
+	default:
+		return CIPHER_TKIP;
+	}
+}
+
+
 int wpa_cipher_valid_pairwise(int cipher)
 {
 	return cipher == WPA_CIPHER_CCMP ||
@@ -1214,3 +1234,150 @@
 
 	return num_suites;
 }
+
+
+int wpa_pick_pairwise_cipher(int ciphers, int none_allowed)
+{
+	if (ciphers & WPA_CIPHER_CCMP)
+		return WPA_CIPHER_CCMP;
+	if (ciphers & WPA_CIPHER_GCMP)
+		return WPA_CIPHER_GCMP;
+	if (ciphers & WPA_CIPHER_TKIP)
+		return WPA_CIPHER_TKIP;
+	if (none_allowed && (ciphers & WPA_CIPHER_NONE))
+		return WPA_CIPHER_NONE;
+	return -1;
+}
+
+
+int wpa_pick_group_cipher(int ciphers)
+{
+	if (ciphers & WPA_CIPHER_CCMP)
+		return WPA_CIPHER_CCMP;
+	if (ciphers & WPA_CIPHER_GCMP)
+		return WPA_CIPHER_GCMP;
+	if (ciphers & WPA_CIPHER_TKIP)
+		return WPA_CIPHER_TKIP;
+	if (ciphers & WPA_CIPHER_WEP104)
+		return WPA_CIPHER_WEP104;
+	if (ciphers & WPA_CIPHER_WEP40)
+		return WPA_CIPHER_WEP40;
+	return -1;
+}
+
+
+int wpa_parse_cipher(const char *value)
+{
+	int val = 0, last;
+	char *start, *end, *buf;
+
+	buf = os_strdup(value);
+	if (buf == NULL)
+		return -1;
+	start = buf;
+
+	while (*start != '\0') {
+		while (*start == ' ' || *start == '\t')
+			start++;
+		if (*start == '\0')
+			break;
+		end = start;
+		while (*end != ' ' && *end != '\t' && *end != '\0')
+			end++;
+		last = *end == '\0';
+		*end = '\0';
+		if (os_strcmp(start, "CCMP") == 0)
+			val |= WPA_CIPHER_CCMP;
+		else if (os_strcmp(start, "GCMP") == 0)
+			val |= WPA_CIPHER_GCMP;
+		else if (os_strcmp(start, "TKIP") == 0)
+			val |= WPA_CIPHER_TKIP;
+		else if (os_strcmp(start, "WEP104") == 0)
+			val |= WPA_CIPHER_WEP104;
+		else if (os_strcmp(start, "WEP40") == 0)
+			val |= WPA_CIPHER_WEP40;
+		else if (os_strcmp(start, "NONE") == 0)
+			val |= WPA_CIPHER_NONE;
+		else {
+			os_free(buf);
+			return -1;
+		}
+
+		if (last)
+			break;
+		start = end + 1;
+	}
+	os_free(buf);
+
+	return val;
+}
+
+
+int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim)
+{
+	char *pos = start;
+	int ret;
+
+	if (ciphers & WPA_CIPHER_CCMP) {
+		ret = os_snprintf(pos, end - pos, "%sCCMP",
+				  pos == start ? "" : delim);
+		if (ret < 0 || ret >= end - pos)
+			return -1;
+		pos += ret;
+	}
+	if (ciphers & WPA_CIPHER_GCMP) {
+		ret = os_snprintf(pos, end - pos, "%sGCMP",
+				  pos == start ? "" : delim);
+		if (ret < 0 || ret >= end - pos)
+			return -1;
+		pos += ret;
+	}
+	if (ciphers & WPA_CIPHER_TKIP) {
+		ret = os_snprintf(pos, end - pos, "%sTKIP",
+				  pos == start ? "" : delim);
+		if (ret < 0 || ret >= end - pos)
+			return -1;
+		pos += ret;
+	}
+	if (ciphers & WPA_CIPHER_WEP104) {
+		ret = os_snprintf(pos, end - pos, "%sWEP104",
+				  pos == start ? "" : delim);
+		if (ret < 0 || ret >= end - pos)
+			return -1;
+		pos += ret;
+	}
+	if (ciphers & WPA_CIPHER_WEP40) {
+		ret = os_snprintf(pos, end - pos, "%sWEP40",
+				  pos == start ? "" : delim);
+		if (ret < 0 || ret >= end - pos)
+			return -1;
+		pos += ret;
+	}
+	if (ciphers & WPA_CIPHER_NONE) {
+		ret = os_snprintf(pos, end - pos, "%sNONE",
+				  pos == start ? "" : delim);
+		if (ret < 0 || ret >= end - pos)
+			return -1;
+		pos += ret;
+	}
+
+	return pos - start;
+}
+
+
+int wpa_select_ap_group_cipher(int wpa, int wpa_pairwise, int rsn_pairwise)
+{
+	int pairwise = 0;
+
+	/* Select group cipher based on the enabled pairwise cipher suites */
+	if (wpa & 1)
+		pairwise |= wpa_pairwise;
+	if (wpa & 2)
+		pairwise |= rsn_pairwise;
+
+	if (pairwise & WPA_CIPHER_TKIP)
+		return WPA_CIPHER_TKIP;
+	if ((pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) == WPA_CIPHER_GCMP)
+		return WPA_CIPHER_GCMP;
+	return WPA_CIPHER_CCMP;
+}
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index 20c79d8..2d63662 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -1,6 +1,6 @@
 /*
  * WPA definitions shared between hostapd and wpa_supplicant
- * Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2013, 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,12 @@
 #define WPA_GMK_LEN 32
 #define WPA_GTK_MAX_LEN 32
 
+#define WPA_ALLOWED_PAIRWISE_CIPHERS \
+(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | WPA_CIPHER_NONE)
+#define WPA_ALLOWED_GROUP_CIPHERS \
+(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | WPA_CIPHER_WEP104 | \
+WPA_CIPHER_WEP40)
+
 #define WPA_SELECTOR_LEN 4
 #define WPA_VERSION 1
 #define RSN_SELECTOR_LEN 4
@@ -386,9 +392,15 @@
 int wpa_cipher_key_len(int cipher);
 int wpa_cipher_rsc_len(int cipher);
 int wpa_cipher_to_alg(int cipher);
+enum wpa_cipher wpa_cipher_to_suite_driver(int cipher);
 int wpa_cipher_valid_pairwise(int cipher);
 u32 wpa_cipher_to_suite(int proto, int cipher);
 int rsn_cipher_put_suites(u8 *pos, int ciphers);
 int wpa_cipher_put_suites(u8 *pos, int ciphers);
+int wpa_pick_pairwise_cipher(int ciphers, int none_allowed);
+int wpa_pick_group_cipher(int ciphers);
+int wpa_parse_cipher(const char *value);
+int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim);
+int wpa_select_ap_group_cipher(int wpa, int wpa_pairwise, int rsn_pairwise);
 
 #endif /* WPA_COMMON_H */
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index 26b9acf..9bccaaa 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -1,6 +1,6 @@
 /*
- * WPA Supplicant / wrapper functions for crypto libraries
- * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ * Wrapper functions for crypto libraries
+ * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -457,4 +457,329 @@
  */
 int crypto_get_random(void *buf, size_t len);
 
+
+/**
+ * struct crypto_bignum - bignum
+ *
+ * Internal data structure for bignum implementation. The contents is specific
+ * to the used crypto library.
+ */
+struct crypto_bignum;
+
+/**
+ * crypto_bignum_init - Allocate memory for bignum
+ * Returns: Pointer to allocated bignum or %NULL on failure
+ */
+struct crypto_bignum * crypto_bignum_init(void);
+
+/**
+ * crypto_bignum_init_set - Allocate memory for bignum and set the value
+ * @buf: Buffer with unsigned binary value
+ * @len: Length of buf in octets
+ * Returns: Pointer to allocated bignum or %NULL on failure
+ */
+struct crypto_bignum * crypto_bignum_init_set(const u8 *buf, size_t len);
+
+/**
+ * crypto_bignum_deinit - Free bignum
+ * @n: Bignum from crypto_bignum_init() or crypto_bignum_init_set()
+ * @clear: Whether to clear the value from memory
+ */
+void crypto_bignum_deinit(struct crypto_bignum *n, int clear);
+
+/**
+ * crypto_bignum_to_bin - Set binary buffer to unsigned bignum
+ * @a: Bignum
+ * @buf: Buffer for the binary number
+ * @len: Length of @buf in octets
+ * @padlen: Length in octets to pad the result to or 0 to indicate no padding
+ * Returns: Number of octets written on success, -1 on failure
+ */
+int crypto_bignum_to_bin(const struct crypto_bignum *a,
+			 u8 *buf, size_t buflen, size_t padlen);
+
+/**
+ * crypto_bignum_add - c = a + b
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum; used to store the result of a + b
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_add(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b,
+		      struct crypto_bignum *c);
+
+/**
+ * crypto_bignum_mod - c = a % b
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum; used to store the result of a % b
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_mod(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b,
+		      struct crypto_bignum *c);
+
+/**
+ * crypto_bignum_exptmod - Modular exponentiation: d = a^b (mod c)
+ * @a: Bignum; base
+ * @b: Bignum; exponent
+ * @c: Bignum; modulus
+ * @d: Bignum; used to store the result of a^b (mod c)
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_exptmod(const struct crypto_bignum *a,
+			  const struct crypto_bignum *b,
+			  const struct crypto_bignum *c,
+			  struct crypto_bignum *d);
+
+/**
+ * crypto_bignum_rshift - b = a >> n
+ * @a: Bignum
+ * @n: Number of bits to shift
+ * @b: Bignum; used to store the result of a >> n
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_rshift(const struct crypto_bignum *a, int n,
+			 struct crypto_bignum *b);
+
+/**
+ * crypto_bignum_inverse - Inverse a bignum so that a * c = 1 (mod b)
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum; used to store the result
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_inverse(const struct crypto_bignum *a,
+			  const struct crypto_bignum *b,
+			  struct crypto_bignum *c);
+
+/**
+ * crypto_bignum_sub - c = a - b
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum; used to store the result of a - b
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_sub(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b,
+		      struct crypto_bignum *c);
+
+/**
+ * crypto_bignum_div - c = a / b
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum; used to store the result of a / b
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_div(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b,
+		      struct crypto_bignum *c);
+
+/**
+ * crypto_bignum_mulmod - d = a * b (mod c)
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum
+ * @d: Bignum; used to store the result of (a * b) % c
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_mulmod(const struct crypto_bignum *a,
+			 const struct crypto_bignum *b,
+			 const struct crypto_bignum *c,
+			 struct crypto_bignum *d);
+
+/**
+ * crypto_bignum_cmp - Compare two bignums
+ * @a: Bignum
+ * @b: Bignum
+ * Returns: -1 if a < b, 0 if a == b, or 1 if a > b
+ */
+int crypto_bignum_cmp(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b);
+
+/**
+ * crypto_bignum_bits - Get size of a bignum in bits
+ * @a: Bignum
+ * Returns: Number of bits in the bignum
+ */
+int crypto_bignum_bits(const struct crypto_bignum *a);
+
+/**
+ * crypto_bignum_is_zero - Is the given bignum zero
+ * @a: Bignum
+ * Returns: 1 if @a is zero or 0 if not
+ */
+int crypto_bignum_is_zero(const struct crypto_bignum *a);
+
+/**
+ * crypto_bignum_is_one - Is the given bignum one
+ * @a: Bignum
+ * Returns: 1 if @a is one or 0 if not
+ */
+int crypto_bignum_is_one(const struct crypto_bignum *a);
+
+/**
+ * struct crypto_ec - Elliptic curve context
+ *
+ * Internal data structure for EC implementation. The contents is specific
+ * to the used crypto library.
+ */
+struct crypto_ec;
+
+/**
+ * crypto_ec_init - Initialize elliptic curve context
+ * @group: Identifying number for the ECC group (IANA "Group Description"
+ *	attribute registrty for RFC 2409)
+ * Returns: Pointer to EC context or %NULL on failure
+ */
+struct crypto_ec * crypto_ec_init(int group);
+
+/**
+ * crypto_ec_deinit - Deinitialize elliptic curve context
+ * @e: EC context from crypto_ec_init()
+ */
+void crypto_ec_deinit(struct crypto_ec *e);
+
+/**
+ * crypto_ec_prime_len - Get length of the prime in octets
+ * @e: EC context from crypto_ec_init()
+ * Returns: Length of the prime defining the group
+ */
+size_t crypto_ec_prime_len(struct crypto_ec *e);
+
+/**
+ * crypto_ec_prime_len_bits - Get length of the prime in bits
+ * @e: EC context from crypto_ec_init()
+ * Returns: Length of the prime defining the group in bits
+ */
+size_t crypto_ec_prime_len_bits(struct crypto_ec *e);
+
+/**
+ * crypto_ec_get_prime - Get prime defining an EC group
+ * @e: EC context from crypto_ec_init()
+ * Returns: Prime (bignum) defining the group
+ */
+const struct crypto_bignum * crypto_ec_get_prime(struct crypto_ec *e);
+
+/**
+ * crypto_ec_get_order - Get order of an EC group
+ * @e: EC context from crypto_ec_init()
+ * Returns: Order (bignum) of the group
+ */
+const struct crypto_bignum * crypto_ec_get_order(struct crypto_ec *e);
+
+/**
+ * struct crypto_ec_point - Elliptic curve point
+ *
+ * Internal data structure for EC implementation to represent a point. The
+ * contents is specific to the used crypto library.
+ */
+struct crypto_ec_point;
+
+/**
+ * crypto_ec_point_init - Initialize data for an EC point
+ * @e: EC context from crypto_ec_init()
+ * Returns: Pointer to EC point data or %NULL on failure
+ */
+struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e);
+
+/**
+ * crypto_ec_point_deinit - Deinitialize EC point data
+ * @p: EC point data from crypto_ec_point_init()
+ * @clear: Whether to clear the EC point value from memory
+ */
+void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear);
+
+/**
+ * crypto_ec_point_to_bin - Write EC point value as binary data
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point data from crypto_ec_point_init()
+ * @x: Buffer for writing the binary data for x coordinate or %NULL if not used
+ * @y: Buffer for writing the binary data for y coordinate or %NULL if not used
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to write an EC point as binary data in a format
+ * that has the x and y coordinates in big endian byte order fields padded to
+ * the length of the prime defining the group.
+ */
+int crypto_ec_point_to_bin(struct crypto_ec *e,
+			   const struct crypto_ec_point *point, u8 *x, u8 *y);
+
+/**
+ * crypto_ec_point_from_bin - Create EC point from binary data
+ * @e: EC context from crypto_ec_init()
+ * @val: Binary data to read the EC point from
+ * Returns: Pointer to EC point data or %NULL on failure
+ *
+ * This function readers x and y coordinates of the EC point from the provided
+ * buffer assuming the values are in big endian byte order with fields padded to
+ * the length of the prime defining the group.
+ */
+struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e,
+						  const u8 *val);
+
+/**
+ * crypto_bignum_add - c = a + b
+ * @e: EC context from crypto_ec_init()
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum; used to store the result of a + b
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a,
+			const struct crypto_ec_point *b,
+			struct crypto_ec_point *c);
+
+/**
+ * crypto_bignum_mul - res = b * p
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point
+ * @b: Bignum
+ * @res: EC point; used to store the result of b * p
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p,
+			const struct crypto_bignum *b,
+			struct crypto_ec_point *res);
+
+/**
+ * crypto_ec_point_invert - Compute inverse of an EC point
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point to invert (and result of the operation)
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p);
+
+/**
+ * crypto_ec_point_solve_y_coord - Solve y coordinate for an x coordinate
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point to use for the returning the result
+ * @x: x coordinate
+ * @y_bit: y-bit (0 or 1) for selecting the y value to use
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_ec_point_solve_y_coord(struct crypto_ec *e,
+				  struct crypto_ec_point *p,
+				  const struct crypto_bignum *x, int y_bit);
+
+/**
+ * crypto_ec_point_is_at_infinity - Check whether EC point is neutral element
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point
+ * Returns: 1 if the specified EC point is the neutral element of the group or
+ *	0 if not
+ */
+int crypto_ec_point_is_at_infinity(struct crypto_ec *e,
+				   const struct crypto_ec_point *p);
+
+/**
+ * crypto_ec_point_is_on_curve - Check whether EC point is on curve
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point
+ * Returns: 1 if the specified EC point is on the curve or 0 if not
+ */
+int crypto_ec_point_is_on_curve(struct crypto_ec *e,
+				const struct crypto_ec_point *p);
+
 #endif /* CRYPTO_H */
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
index 711e312..5215c00 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-2012, Jouni Malinen <j@w1.fi>
+ * Wrapper functions for OpenSSL libcrypto
+ * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -19,6 +19,9 @@
 #ifdef CONFIG_OPENSSL_CMAC
 #include <openssl/cmac.h>
 #endif /* CONFIG_OPENSSL_CMAC */
+#ifdef CONFIG_ECC
+#include <openssl/ec.h>
+#endif /* CONFIG_ECC */
 
 #include "common.h"
 #include "wpabuf.h"
@@ -818,3 +821,413 @@
 	return omac1_aes_128_vector(key, 1, &data, &data_len, mac);
 }
 #endif /* CONFIG_OPENSSL_CMAC */
+
+
+struct crypto_bignum * crypto_bignum_init(void)
+{
+	return (struct crypto_bignum *) BN_new();
+}
+
+
+struct crypto_bignum * crypto_bignum_init_set(const u8 *buf, size_t len)
+{
+	BIGNUM *bn = BN_bin2bn(buf, len, NULL);
+	return (struct crypto_bignum *) bn;
+}
+
+
+void crypto_bignum_deinit(struct crypto_bignum *n, int clear)
+{
+	if (clear)
+		BN_clear_free((BIGNUM *) n);
+	else
+		BN_free((BIGNUM *) n);
+}
+
+
+int crypto_bignum_to_bin(const struct crypto_bignum *a,
+			 u8 *buf, size_t buflen, size_t padlen)
+{
+	int num_bytes, offset;
+
+	if (padlen > buflen)
+		return -1;
+
+	num_bytes = BN_num_bytes((const BIGNUM *) a);
+	if ((size_t) num_bytes > buflen)
+		return -1;
+	if (padlen > (size_t) num_bytes)
+		offset = padlen - num_bytes;
+	else
+		offset = 0;
+
+	os_memset(buf, 0, offset);
+	BN_bn2bin((const BIGNUM *) a, buf + offset);
+
+	return num_bytes + offset;
+}
+
+
+int crypto_bignum_add(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b,
+		      struct crypto_bignum *c)
+{
+	return BN_add((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b) ?
+		0 : -1;
+}
+
+
+int crypto_bignum_mod(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b,
+		      struct crypto_bignum *c)
+{
+	int res;
+	BN_CTX *bnctx;
+
+	bnctx = BN_CTX_new();
+	if (bnctx == NULL)
+		return -1;
+	res = BN_mod((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b,
+		     bnctx);
+	BN_CTX_free(bnctx);
+
+	return res ? 0 : -1;
+}
+
+
+int crypto_bignum_exptmod(const struct crypto_bignum *a,
+			  const struct crypto_bignum *b,
+			  const struct crypto_bignum *c,
+			  struct crypto_bignum *d)
+{
+	int res;
+	BN_CTX *bnctx;
+
+	bnctx = BN_CTX_new();
+	if (bnctx == NULL)
+		return -1;
+	res = BN_mod_exp((BIGNUM *) d, (const BIGNUM *) a, (const BIGNUM *) b,
+			 (const BIGNUM *) c, bnctx);
+	BN_CTX_free(bnctx);
+
+	return res ? 0 : -1;
+}
+
+
+int crypto_bignum_rshift(const struct crypto_bignum *a, int n,
+			 struct crypto_bignum *b)
+{
+	return BN_rshift((BIGNUM *) b, (const BIGNUM *) a, n) ? 0 : -1;
+}
+
+
+int crypto_bignum_inverse(const struct crypto_bignum *a,
+			  const struct crypto_bignum *b,
+			  struct crypto_bignum *c)
+{
+	BIGNUM *res;
+	BN_CTX *bnctx;
+
+	bnctx = BN_CTX_new();
+	if (bnctx == NULL)
+		return -1;
+	res = BN_mod_inverse((BIGNUM *) c, (const BIGNUM *) a,
+			     (const BIGNUM *) b, bnctx);
+	BN_CTX_free(bnctx);
+
+	return res ? 0 : -1;
+}
+
+
+int crypto_bignum_sub(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b,
+		      struct crypto_bignum *c)
+{
+	return BN_sub((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b) ?
+		0 : -1;
+}
+
+
+int crypto_bignum_div(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b,
+		      struct crypto_bignum *c)
+{
+	int res;
+
+	BN_CTX *bnctx;
+
+	bnctx = BN_CTX_new();
+	if (bnctx == NULL)
+		return -1;
+	res = BN_div((BIGNUM *) c, NULL, (const BIGNUM *) a,
+		     (const BIGNUM *) b, bnctx);
+	BN_CTX_free(bnctx);
+
+	return res ? 0 : -1;
+}
+
+
+int crypto_bignum_mulmod(const struct crypto_bignum *a,
+			 const struct crypto_bignum *b,
+			 const struct crypto_bignum *c,
+			 struct crypto_bignum *d)
+{
+	int res;
+
+	BN_CTX *bnctx;
+
+	bnctx = BN_CTX_new();
+	if (bnctx == NULL)
+		return -1;
+	res = BN_mod_mul((BIGNUM *) d, (const BIGNUM *) a, (const BIGNUM *) b,
+			 (const BIGNUM *) c, bnctx);
+	BN_CTX_free(bnctx);
+
+	return res ? 0 : -1;
+}
+
+
+int crypto_bignum_cmp(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b)
+{
+	return BN_cmp((const BIGNUM *) a, (const BIGNUM *) b);
+}
+
+
+int crypto_bignum_bits(const struct crypto_bignum *a)
+{
+	return BN_num_bits((const BIGNUM *) a);
+}
+
+
+int crypto_bignum_is_zero(const struct crypto_bignum *a)
+{
+	return BN_is_zero((const BIGNUM *) a);
+}
+
+
+int crypto_bignum_is_one(const struct crypto_bignum *a)
+{
+	return BN_is_one((const BIGNUM *) a);
+}
+
+
+#ifdef CONFIG_ECC
+
+struct crypto_ec {
+	EC_GROUP *group;
+	BN_CTX *bnctx;
+	BIGNUM *prime;
+	BIGNUM *order;
+};
+
+struct crypto_ec * crypto_ec_init(int group)
+{
+	struct crypto_ec *e;
+	int nid;
+
+	/* Map from IANA registry for IKE D-H groups to OpenSSL NID */
+	switch (group) {
+	case 19:
+		nid = NID_X9_62_prime256v1;
+		break;
+	case 20:
+		nid = NID_secp384r1;
+		break;
+	case 21:
+		nid = NID_secp521r1;
+		break;
+	case 25:
+		nid = NID_X9_62_prime192v1;
+		break;
+	case 26:
+		nid = NID_secp224r1;
+		break;
+	default:
+		return NULL;
+	}
+
+	e = os_zalloc(sizeof(*e));
+	if (e == NULL)
+		return NULL;
+
+	e->bnctx = BN_CTX_new();
+	e->group = EC_GROUP_new_by_curve_name(nid);
+	e->prime = BN_new();
+	e->order = BN_new();
+	if (e->group == NULL || e->bnctx == NULL || e->prime == NULL ||
+	    e->order == NULL ||
+	    !EC_GROUP_get_curve_GFp(e->group, e->prime, NULL, NULL, e->bnctx) ||
+	    !EC_GROUP_get_order(e->group, e->order, e->bnctx)) {
+		crypto_ec_deinit(e);
+		e = NULL;
+	}
+
+	return e;
+}
+
+
+void crypto_ec_deinit(struct crypto_ec *e)
+{
+	if (e == NULL)
+		return;
+	BN_free(e->order);
+	EC_GROUP_free(e->group);
+	BN_CTX_free(e->bnctx);
+	os_free(e);
+}
+
+
+struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e)
+{
+	if (e == NULL)
+		return NULL;
+	return (struct crypto_ec_point *) EC_POINT_new(e->group);
+}
+
+
+size_t crypto_ec_prime_len(struct crypto_ec *e)
+{
+	return BN_num_bytes(e->prime);
+}
+
+
+size_t crypto_ec_prime_len_bits(struct crypto_ec *e)
+{
+	return BN_num_bits(e->prime);
+}
+
+
+const struct crypto_bignum * crypto_ec_get_prime(struct crypto_ec *e)
+{
+	return (const struct crypto_bignum *) e->prime;
+}
+
+
+const struct crypto_bignum * crypto_ec_get_order(struct crypto_ec *e)
+{
+	return (const struct crypto_bignum *) e->order;
+}
+
+
+void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear)
+{
+	if (clear)
+		EC_POINT_clear_free((EC_POINT *) p);
+	else
+		EC_POINT_free((EC_POINT *) p);
+}
+
+
+int crypto_ec_point_to_bin(struct crypto_ec *e,
+			   const struct crypto_ec_point *point, u8 *x, u8 *y)
+{
+	BIGNUM *x_bn, *y_bn;
+	int ret = -1;
+	int len = BN_num_bytes(e->prime);
+
+	x_bn = BN_new();
+	y_bn = BN_new();
+
+	if (x_bn && y_bn &&
+	    EC_POINT_get_affine_coordinates_GFp(e->group, (EC_POINT *) point,
+						x_bn, y_bn, e->bnctx)) {
+		if (x) {
+			crypto_bignum_to_bin((struct crypto_bignum *) x_bn,
+					     x, len, len);
+		}
+		if (y) {
+			crypto_bignum_to_bin((struct crypto_bignum *) y_bn,
+					     y, len, len);
+		}
+		ret = 0;
+	}
+
+	BN_free(x_bn);
+	BN_free(y_bn);
+	return ret;
+}
+
+
+struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e,
+						  const u8 *val)
+{
+	BIGNUM *x, *y;
+	EC_POINT *elem;
+	int len = BN_num_bytes(e->prime);
+
+	x = BN_bin2bn(val, len, NULL);
+	y = BN_bin2bn(val + len, len, NULL);
+	elem = EC_POINT_new(e->group);
+	if (x == NULL || y == NULL || elem == NULL) {
+		BN_free(x);
+		BN_free(y);
+		EC_POINT_free(elem);
+		return NULL;
+	}
+
+	if (!EC_POINT_set_affine_coordinates_GFp(e->group, elem, x, y,
+						 e->bnctx)) {
+		EC_POINT_free(elem);
+		elem = NULL;
+	}
+
+	BN_free(x);
+	BN_free(y);
+
+	return (struct crypto_ec_point *) elem;
+}
+
+
+int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a,
+			const struct crypto_ec_point *b,
+			struct crypto_ec_point *c)
+{
+	return EC_POINT_add(e->group, (EC_POINT *) c, (const EC_POINT *) a,
+			    (const EC_POINT *) b, e->bnctx) ? 0 : -1;
+}
+
+
+int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p,
+			const struct crypto_bignum *b,
+			struct crypto_ec_point *res)
+{
+	return EC_POINT_mul(e->group, (EC_POINT *) res, NULL,
+			    (const EC_POINT *) p, (const BIGNUM *) b, e->bnctx)
+		? 0 : -1;
+}
+
+
+int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p)
+{
+	return EC_POINT_invert(e->group, (EC_POINT *) p, e->bnctx) ? 0 : -1;
+}
+
+
+int crypto_ec_point_solve_y_coord(struct crypto_ec *e,
+				  struct crypto_ec_point *p,
+				  const struct crypto_bignum *x, int y_bit)
+{
+	if (!EC_POINT_set_compressed_coordinates_GFp(e->group, (EC_POINT *) p,
+						     (const BIGNUM *) x, y_bit,
+						     e->bnctx) ||
+	    !EC_POINT_is_on_curve(e->group, (EC_POINT *) p, e->bnctx))
+		return -1;
+	return 0;
+}
+
+
+int crypto_ec_point_is_at_infinity(struct crypto_ec *e,
+				   const struct crypto_ec_point *p)
+{
+	return EC_POINT_is_at_infinity(e->group, (const EC_POINT *) p);
+}
+
+
+int crypto_ec_point_is_on_curve(struct crypto_ec *e,
+				const struct crypto_ec_point *p)
+{
+	return EC_POINT_is_on_curve(e->group, (const EC_POINT *) p, e->bnctx);
+}
+
+#endif /* CONFIG_ECC */
diff --git a/src/crypto/dh_groups.c b/src/crypto/dh_groups.c
index f757b6b..3a675df 100644
--- a/src/crypto/dh_groups.c
+++ b/src/crypto/dh_groups.c
@@ -35,6 +35,20 @@
 	0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x3A, 0x36, 0x20,
 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 };
+static const u8 dh_group1_order[96] = {
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
+	0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
+	0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
+	0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
+	0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
+	0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
+	0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
+	0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
+	0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
+	0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1D, 0x1B, 0x10,
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
 
 /* RFC 4306, B.2. Group 2 - 1024 Bit MODP
  * Generator: 2
@@ -59,6 +73,24 @@
 	0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81,
 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 };
+static const u8 dh_group2_order[128] = {
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
+	0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
+	0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
+	0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
+	0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
+	0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
+	0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
+	0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
+	0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
+	0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
+	0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
+	0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
+	0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
+	0x24, 0x94, 0x33, 0x28, 0xF6, 0x73, 0x29, 0xC0,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
 
 #endif /* ALL_DH_GROUPS */
 
@@ -93,6 +125,32 @@
 	0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x23, 0x73, 0x27,
 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 };
+static const u8 dh_group5_order[192] = {
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
+	0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
+	0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
+	0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
+	0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
+	0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
+	0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
+	0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
+	0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
+	0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
+	0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
+	0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
+	0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
+	0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E,
+	0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82,
+	0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD,
+	0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF,
+	0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB,
+	0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D,
+	0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36,
+	0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02,
+	0x78, 0xBA, 0x36, 0x04, 0x65, 0x11, 0xB9, 0x93,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
 
 #ifdef ALL_DH_GROUPS
 
@@ -135,6 +193,40 @@
 	0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68,
 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 };
+static const u8 dh_group14_order[256] = {
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
+	0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
+	0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
+	0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
+	0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
+	0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
+	0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
+	0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
+	0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
+	0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
+	0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
+	0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
+	0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
+	0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E,
+	0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82,
+	0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD,
+	0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF,
+	0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB,
+	0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D,
+	0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36,
+	0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02,
+	0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE,
+	0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D,
+	0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01,
+	0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47,
+	0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64,
+	0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C,
+	0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72,
+	0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88,
+	0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x56, 0x55, 0x34,
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
 
 /* RFC 3526, 4. Group 15 - 3072 Bit MODP
  * Generator: 2
@@ -191,6 +283,56 @@
 	0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x3A, 0xD2, 0xCA,
 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 };
+static const u8 dh_group15_order[384] = {
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
+	0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
+	0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
+	0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
+	0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
+	0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
+	0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
+	0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
+	0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
+	0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
+	0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
+	0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
+	0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
+	0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E,
+	0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82,
+	0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD,
+	0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF,
+	0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB,
+	0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D,
+	0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36,
+	0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02,
+	0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE,
+	0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D,
+	0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01,
+	0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47,
+	0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64,
+	0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C,
+	0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72,
+	0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88,
+	0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x55, 0x62, 0x16,
+	0xD6, 0x99, 0x8B, 0x86, 0x82, 0x28, 0x3D, 0x19,
+	0xD4, 0x2A, 0x90, 0xD5, 0xEF, 0x8E, 0x5D, 0x32,
+	0x76, 0x7D, 0xC2, 0x82, 0x2C, 0x6D, 0xF7, 0x85,
+	0x45, 0x75, 0x38, 0xAB, 0xAE, 0x83, 0x06, 0x3E,
+	0xD9, 0xCB, 0x87, 0xC2, 0xD3, 0x70, 0xF2, 0x63,
+	0xD5, 0xFA, 0xD7, 0x46, 0x6D, 0x84, 0x99, 0xEB,
+	0x8F, 0x46, 0x4A, 0x70, 0x25, 0x12, 0xB0, 0xCE,
+	0xE7, 0x71, 0xE9, 0x13, 0x0D, 0x69, 0x77, 0x35,
+	0xF8, 0x97, 0xFD, 0x03, 0x6C, 0xC5, 0x04, 0x32,
+	0x6C, 0x3B, 0x01, 0x39, 0x9F, 0x64, 0x35, 0x32,
+	0x29, 0x0F, 0x95, 0x8C, 0x0B, 0xBD, 0x90, 0x06,
+	0x5D, 0xF0, 0x8B, 0xAB, 0xBD, 0x30, 0xAE, 0xB6,
+	0x3B, 0x84, 0xC4, 0x60, 0x5D, 0x6C, 0xA3, 0x71,
+	0x04, 0x71, 0x27, 0xD0, 0x3A, 0x72, 0xD5, 0x98,
+	0xA1, 0xED, 0xAD, 0xFE, 0x70, 0x7E, 0x88, 0x47,
+	0x25, 0xC1, 0x68, 0x90, 0x54, 0x9D, 0x69, 0x65,
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
 
 /* RFC 3526, 5. Group 16 - 4096 Bit MODP
  * Generator: 2
@@ -263,6 +405,72 @@
 	0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x06, 0x31, 0x99,
 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 };
+static const u8 dh_group16_order[512] = {
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
+	0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
+	0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
+	0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
+	0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
+	0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
+	0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
+	0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
+	0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
+	0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
+	0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
+	0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
+	0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
+	0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E,
+	0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82,
+	0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD,
+	0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF,
+	0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB,
+	0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D,
+	0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36,
+	0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02,
+	0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE,
+	0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D,
+	0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01,
+	0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47,
+	0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64,
+	0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C,
+	0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72,
+	0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88,
+	0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x55, 0x62, 0x16,
+	0xD6, 0x99, 0x8B, 0x86, 0x82, 0x28, 0x3D, 0x19,
+	0xD4, 0x2A, 0x90, 0xD5, 0xEF, 0x8E, 0x5D, 0x32,
+	0x76, 0x7D, 0xC2, 0x82, 0x2C, 0x6D, 0xF7, 0x85,
+	0x45, 0x75, 0x38, 0xAB, 0xAE, 0x83, 0x06, 0x3E,
+	0xD9, 0xCB, 0x87, 0xC2, 0xD3, 0x70, 0xF2, 0x63,
+	0xD5, 0xFA, 0xD7, 0x46, 0x6D, 0x84, 0x99, 0xEB,
+	0x8F, 0x46, 0x4A, 0x70, 0x25, 0x12, 0xB0, 0xCE,
+	0xE7, 0x71, 0xE9, 0x13, 0x0D, 0x69, 0x77, 0x35,
+	0xF8, 0x97, 0xFD, 0x03, 0x6C, 0xC5, 0x04, 0x32,
+	0x6C, 0x3B, 0x01, 0x39, 0x9F, 0x64, 0x35, 0x32,
+	0x29, 0x0F, 0x95, 0x8C, 0x0B, 0xBD, 0x90, 0x06,
+	0x5D, 0xF0, 0x8B, 0xAB, 0xBD, 0x30, 0xAE, 0xB6,
+	0x3B, 0x84, 0xC4, 0x60, 0x5D, 0x6C, 0xA3, 0x71,
+	0x04, 0x71, 0x27, 0xD0, 0x3A, 0x72, 0xD5, 0x98,
+	0xA1, 0xED, 0xAD, 0xFE, 0x70, 0x7E, 0x88, 0x47,
+	0x25, 0xC1, 0x68, 0x90, 0x54, 0x90, 0x84, 0x00,
+	0x8D, 0x39, 0x1E, 0x09, 0x53, 0xC3, 0xF3, 0x6B,
+	0xC4, 0x38, 0xCD, 0x08, 0x5E, 0xDD, 0x2D, 0x93,
+	0x4C, 0xE1, 0x93, 0x8C, 0x35, 0x7A, 0x71, 0x1E,
+	0x0D, 0x4A, 0x34, 0x1A, 0x5B, 0x0A, 0x85, 0xED,
+	0x12, 0xC1, 0xF4, 0xE5, 0x15, 0x6A, 0x26, 0x74,
+	0x6D, 0xDD, 0xE1, 0x6D, 0x82, 0x6F, 0x47, 0x7C,
+	0x97, 0x47, 0x7E, 0x0A, 0x0F, 0xDF, 0x65, 0x53,
+	0x14, 0x3E, 0x2C, 0xA3, 0xA7, 0x35, 0xE0, 0x2E,
+	0xCC, 0xD9, 0x4B, 0x27, 0xD0, 0x48, 0x61, 0xD1,
+	0x11, 0x9D, 0xD0, 0xC3, 0x28, 0xAD, 0xF3, 0xF6,
+	0x8F, 0xB0, 0x94, 0xB8, 0x67, 0x71, 0x6B, 0xD7,
+	0xDC, 0x0D, 0xEE, 0xBB, 0x10, 0xB8, 0x24, 0x0E,
+	0x68, 0x03, 0x48, 0x93, 0xEA, 0xD8, 0x2D, 0x54,
+	0xC9, 0xDA, 0x75, 0x4C, 0x46, 0xC7, 0xEE, 0xE0,
+	0xC3, 0x7F, 0xDB, 0xEE, 0x48, 0x53, 0x60, 0x47,
+	0xA6, 0xFA, 0x1A, 0xE4, 0x9A, 0x03, 0x18, 0xCC,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
 
 /* RFC 3526, 6. Group 17 - 6144 Bit MODP
  * Generator: 2
@@ -367,6 +575,104 @@
 	0xE6, 0x94, 0xF9, 0x1E, 0x6D, 0xCC, 0x40, 0x24,
 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 };
+static const u8 dh_group17_order[768] = {
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
+	0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
+	0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
+	0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
+	0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
+	0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
+	0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
+	0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
+	0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
+	0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
+	0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
+	0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
+	0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
+	0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E,
+	0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82,
+	0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD,
+	0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF,
+	0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB,
+	0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D,
+	0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36,
+	0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02,
+	0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE,
+	0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D,
+	0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01,
+	0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47,
+	0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64,
+	0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C,
+	0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72,
+	0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88,
+	0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x55, 0x62, 0x16,
+	0xD6, 0x99, 0x8B, 0x86, 0x82, 0x28, 0x3D, 0x19,
+	0xD4, 0x2A, 0x90, 0xD5, 0xEF, 0x8E, 0x5D, 0x32,
+	0x76, 0x7D, 0xC2, 0x82, 0x2C, 0x6D, 0xF7, 0x85,
+	0x45, 0x75, 0x38, 0xAB, 0xAE, 0x83, 0x06, 0x3E,
+	0xD9, 0xCB, 0x87, 0xC2, 0xD3, 0x70, 0xF2, 0x63,
+	0xD5, 0xFA, 0xD7, 0x46, 0x6D, 0x84, 0x99, 0xEB,
+	0x8F, 0x46, 0x4A, 0x70, 0x25, 0x12, 0xB0, 0xCE,
+	0xE7, 0x71, 0xE9, 0x13, 0x0D, 0x69, 0x77, 0x35,
+	0xF8, 0x97, 0xFD, 0x03, 0x6C, 0xC5, 0x04, 0x32,
+	0x6C, 0x3B, 0x01, 0x39, 0x9F, 0x64, 0x35, 0x32,
+	0x29, 0x0F, 0x95, 0x8C, 0x0B, 0xBD, 0x90, 0x06,
+	0x5D, 0xF0, 0x8B, 0xAB, 0xBD, 0x30, 0xAE, 0xB6,
+	0x3B, 0x84, 0xC4, 0x60, 0x5D, 0x6C, 0xA3, 0x71,
+	0x04, 0x71, 0x27, 0xD0, 0x3A, 0x72, 0xD5, 0x98,
+	0xA1, 0xED, 0xAD, 0xFE, 0x70, 0x7E, 0x88, 0x47,
+	0x25, 0xC1, 0x68, 0x90, 0x54, 0x90, 0x84, 0x00,
+	0x8D, 0x39, 0x1E, 0x09, 0x53, 0xC3, 0xF3, 0x6B,
+	0xC4, 0x38, 0xCD, 0x08, 0x5E, 0xDD, 0x2D, 0x93,
+	0x4C, 0xE1, 0x93, 0x8C, 0x35, 0x7A, 0x71, 0x1E,
+	0x0D, 0x4A, 0x34, 0x1A, 0x5B, 0x0A, 0x85, 0xED,
+	0x12, 0xC1, 0xF4, 0xE5, 0x15, 0x6A, 0x26, 0x74,
+	0x6D, 0xDD, 0xE1, 0x6D, 0x82, 0x6F, 0x47, 0x7C,
+	0x97, 0x47, 0x7E, 0x0A, 0x0F, 0xDF, 0x65, 0x53,
+	0x14, 0x3E, 0x2C, 0xA3, 0xA7, 0x35, 0xE0, 0x2E,
+	0xCC, 0xD9, 0x4B, 0x27, 0xD0, 0x48, 0x61, 0xD1,
+	0x11, 0x9D, 0xD0, 0xC3, 0x28, 0xAD, 0xF3, 0xF6,
+	0x8F, 0xB0, 0x94, 0xB8, 0x67, 0x71, 0x6B, 0xD7,
+	0xDC, 0x0D, 0xEE, 0xBB, 0x10, 0xB8, 0x24, 0x0E,
+	0x68, 0x03, 0x48, 0x93, 0xEA, 0xD8, 0x2D, 0x54,
+	0xC9, 0xDA, 0x75, 0x4C, 0x46, 0xC7, 0xEE, 0xE0,
+	0xC3, 0x7F, 0xDB, 0xEE, 0x48, 0x53, 0x60, 0x47,
+	0xA6, 0xFA, 0x1A, 0xE4, 0x9A, 0x01, 0x42, 0x49,
+	0x1B, 0x61, 0xFD, 0x5A, 0x69, 0x3E, 0x38, 0x13,
+	0x60, 0xEA, 0x6E, 0x59, 0x30, 0x13, 0x23, 0x6F,
+	0x64, 0xBA, 0x8F, 0x3B, 0x1E, 0xDD, 0x1B, 0xDE,
+	0xFC, 0x7F, 0xCA, 0x03, 0x56, 0xCF, 0x29, 0x87,
+	0x72, 0xED, 0x9C, 0x17, 0xA0, 0x98, 0x00, 0xD7,
+	0x58, 0x35, 0x29, 0xF6, 0xC8, 0x13, 0xEC, 0x18,
+	0x8B, 0xCB, 0x93, 0xD8, 0x43, 0x2D, 0x44, 0x8C,
+	0x6D, 0x1F, 0x6D, 0xF5, 0xE7, 0xCD, 0x8A, 0x76,
+	0xA2, 0x67, 0x36, 0x5D, 0x67, 0x6A, 0x5D, 0x8D,
+	0xED, 0xBF, 0x8A, 0x23, 0xF3, 0x66, 0x12, 0xA5,
+	0x99, 0x90, 0x28, 0xA8, 0x95, 0xEB, 0xD7, 0xA1,
+	0x37, 0xDC, 0x7A, 0x00, 0x9B, 0xC6, 0x69, 0x5F,
+	0xAC, 0xC1, 0xE5, 0x00, 0xE3, 0x25, 0xC9, 0x76,
+	0x78, 0x19, 0x75, 0x0A, 0xE8, 0xB9, 0x0E, 0x81,
+	0xFA, 0x41, 0x6B, 0xE7, 0x37, 0x3A, 0x7F, 0x7B,
+	0x6A, 0xAF, 0x38, 0x17, 0xA3, 0x4C, 0x06, 0x41,
+	0x5A, 0xD4, 0x20, 0x18, 0xC8, 0x05, 0x8E, 0x4F,
+	0x2C, 0xF3, 0xE4, 0xBF, 0xDF, 0x63, 0xF4, 0x79,
+	0x91, 0xD4, 0xBD, 0x3F, 0x1B, 0x66, 0x44, 0x5F,
+	0x07, 0x8E, 0xA2, 0xDB, 0xFF, 0xAC, 0x2D, 0x62,
+	0xA5, 0xEA, 0x03, 0xD9, 0x15, 0xA0, 0xAA, 0x55,
+	0x66, 0x47, 0xB6, 0xBF, 0x5F, 0xA4, 0x70, 0xEC,
+	0x0A, 0x66, 0x2F, 0x69, 0x07, 0xC0, 0x1B, 0xF0,
+	0x53, 0xCB, 0x8A, 0xF7, 0x79, 0x4D, 0xF1, 0x94,
+	0x03, 0x50, 0xEA, 0xC5, 0xDB, 0xE2, 0xED, 0x3B,
+	0x7A, 0xA8, 0x55, 0x1E, 0xC5, 0x0F, 0xDF, 0xF8,
+	0x75, 0x8C, 0xE6, 0x58, 0xD1, 0x89, 0xEA, 0xAE,
+	0x6D, 0x2B, 0x64, 0xF6, 0x17, 0x79, 0x4B, 0x19,
+	0x1C, 0x3F, 0xF4, 0x6B, 0xB7, 0x1E, 0x02, 0x34,
+	0x02, 0x1F, 0x47, 0xB3, 0x1F, 0xA4, 0x30, 0x77,
+	0x09, 0x5F, 0x96, 0xAD, 0x85, 0xBA, 0x3A, 0x6B,
+	0x73, 0x4A, 0x7C, 0x8F, 0x36, 0xE6, 0x20, 0x12,
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
 
 /* RFC 3526, 7. Group 18 - 8192 Bit MODP
  * Generator: 2
@@ -503,25 +809,363 @@
 	0x60, 0xC9, 0x80, 0xDD, 0x98, 0xED, 0xD3, 0xDF,
 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 };
+static const u8 dh_group18_order[1024] = {
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
+	0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
+	0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
+	0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
+	0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
+	0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
+	0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
+	0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
+	0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
+	0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
+	0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
+	0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
+	0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
+	0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E,
+	0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82,
+	0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD,
+	0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF,
+	0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB,
+	0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D,
+	0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36,
+	0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02,
+	0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE,
+	0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D,
+	0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01,
+	0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47,
+	0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64,
+	0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C,
+	0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72,
+	0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88,
+	0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x55, 0x62, 0x16,
+	0xD6, 0x99, 0x8B, 0x86, 0x82, 0x28, 0x3D, 0x19,
+	0xD4, 0x2A, 0x90, 0xD5, 0xEF, 0x8E, 0x5D, 0x32,
+	0x76, 0x7D, 0xC2, 0x82, 0x2C, 0x6D, 0xF7, 0x85,
+	0x45, 0x75, 0x38, 0xAB, 0xAE, 0x83, 0x06, 0x3E,
+	0xD9, 0xCB, 0x87, 0xC2, 0xD3, 0x70, 0xF2, 0x63,
+	0xD5, 0xFA, 0xD7, 0x46, 0x6D, 0x84, 0x99, 0xEB,
+	0x8F, 0x46, 0x4A, 0x70, 0x25, 0x12, 0xB0, 0xCE,
+	0xE7, 0x71, 0xE9, 0x13, 0x0D, 0x69, 0x77, 0x35,
+	0xF8, 0x97, 0xFD, 0x03, 0x6C, 0xC5, 0x04, 0x32,
+	0x6C, 0x3B, 0x01, 0x39, 0x9F, 0x64, 0x35, 0x32,
+	0x29, 0x0F, 0x95, 0x8C, 0x0B, 0xBD, 0x90, 0x06,
+	0x5D, 0xF0, 0x8B, 0xAB, 0xBD, 0x30, 0xAE, 0xB6,
+	0x3B, 0x84, 0xC4, 0x60, 0x5D, 0x6C, 0xA3, 0x71,
+	0x04, 0x71, 0x27, 0xD0, 0x3A, 0x72, 0xD5, 0x98,
+	0xA1, 0xED, 0xAD, 0xFE, 0x70, 0x7E, 0x88, 0x47,
+	0x25, 0xC1, 0x68, 0x90, 0x54, 0x90, 0x84, 0x00,
+	0x8D, 0x39, 0x1E, 0x09, 0x53, 0xC3, 0xF3, 0x6B,
+	0xC4, 0x38, 0xCD, 0x08, 0x5E, 0xDD, 0x2D, 0x93,
+	0x4C, 0xE1, 0x93, 0x8C, 0x35, 0x7A, 0x71, 0x1E,
+	0x0D, 0x4A, 0x34, 0x1A, 0x5B, 0x0A, 0x85, 0xED,
+	0x12, 0xC1, 0xF4, 0xE5, 0x15, 0x6A, 0x26, 0x74,
+	0x6D, 0xDD, 0xE1, 0x6D, 0x82, 0x6F, 0x47, 0x7C,
+	0x97, 0x47, 0x7E, 0x0A, 0x0F, 0xDF, 0x65, 0x53,
+	0x14, 0x3E, 0x2C, 0xA3, 0xA7, 0x35, 0xE0, 0x2E,
+	0xCC, 0xD9, 0x4B, 0x27, 0xD0, 0x48, 0x61, 0xD1,
+	0x11, 0x9D, 0xD0, 0xC3, 0x28, 0xAD, 0xF3, 0xF6,
+	0x8F, 0xB0, 0x94, 0xB8, 0x67, 0x71, 0x6B, 0xD7,
+	0xDC, 0x0D, 0xEE, 0xBB, 0x10, 0xB8, 0x24, 0x0E,
+	0x68, 0x03, 0x48, 0x93, 0xEA, 0xD8, 0x2D, 0x54,
+	0xC9, 0xDA, 0x75, 0x4C, 0x46, 0xC7, 0xEE, 0xE0,
+	0xC3, 0x7F, 0xDB, 0xEE, 0x48, 0x53, 0x60, 0x47,
+	0xA6, 0xFA, 0x1A, 0xE4, 0x9A, 0x01, 0x42, 0x49,
+	0x1B, 0x61, 0xFD, 0x5A, 0x69, 0x3E, 0x38, 0x13,
+	0x60, 0xEA, 0x6E, 0x59, 0x30, 0x13, 0x23, 0x6F,
+	0x64, 0xBA, 0x8F, 0x3B, 0x1E, 0xDD, 0x1B, 0xDE,
+	0xFC, 0x7F, 0xCA, 0x03, 0x56, 0xCF, 0x29, 0x87,
+	0x72, 0xED, 0x9C, 0x17, 0xA0, 0x98, 0x00, 0xD7,
+	0x58, 0x35, 0x29, 0xF6, 0xC8, 0x13, 0xEC, 0x18,
+	0x8B, 0xCB, 0x93, 0xD8, 0x43, 0x2D, 0x44, 0x8C,
+	0x6D, 0x1F, 0x6D, 0xF5, 0xE7, 0xCD, 0x8A, 0x76,
+	0xA2, 0x67, 0x36, 0x5D, 0x67, 0x6A, 0x5D, 0x8D,
+	0xED, 0xBF, 0x8A, 0x23, 0xF3, 0x66, 0x12, 0xA5,
+	0x99, 0x90, 0x28, 0xA8, 0x95, 0xEB, 0xD7, 0xA1,
+	0x37, 0xDC, 0x7A, 0x00, 0x9B, 0xC6, 0x69, 0x5F,
+	0xAC, 0xC1, 0xE5, 0x00, 0xE3, 0x25, 0xC9, 0x76,
+	0x78, 0x19, 0x75, 0x0A, 0xE8, 0xB9, 0x0E, 0x81,
+	0xFA, 0x41, 0x6B, 0xE7, 0x37, 0x3A, 0x7F, 0x7B,
+	0x6A, 0xAF, 0x38, 0x17, 0xA3, 0x4C, 0x06, 0x41,
+	0x5A, 0xD4, 0x20, 0x18, 0xC8, 0x05, 0x8E, 0x4F,
+	0x2C, 0xF3, 0xE4, 0xBF, 0xDF, 0x63, 0xF4, 0x79,
+	0x91, 0xD4, 0xBD, 0x3F, 0x1B, 0x66, 0x44, 0x5F,
+	0x07, 0x8E, 0xA2, 0xDB, 0xFF, 0xAC, 0x2D, 0x62,
+	0xA5, 0xEA, 0x03, 0xD9, 0x15, 0xA0, 0xAA, 0x55,
+	0x66, 0x47, 0xB6, 0xBF, 0x5F, 0xA4, 0x70, 0xEC,
+	0x0A, 0x66, 0x2F, 0x69, 0x07, 0xC0, 0x1B, 0xF0,
+	0x53, 0xCB, 0x8A, 0xF7, 0x79, 0x4D, 0xF1, 0x94,
+	0x03, 0x50, 0xEA, 0xC5, 0xDB, 0xE2, 0xED, 0x3B,
+	0x7A, 0xA8, 0x55, 0x1E, 0xC5, 0x0F, 0xDF, 0xF8,
+	0x75, 0x8C, 0xE6, 0x58, 0xD1, 0x89, 0xEA, 0xAE,
+	0x6D, 0x2B, 0x64, 0xF6, 0x17, 0x79, 0x4B, 0x19,
+	0x1C, 0x3F, 0xF4, 0x6B, 0xB7, 0x1E, 0x02, 0x34,
+	0x02, 0x1F, 0x47, 0xB3, 0x1F, 0xA4, 0x30, 0x77,
+	0x09, 0x5F, 0x96, 0xAD, 0x85, 0xBA, 0x3A, 0x6B,
+	0x73, 0x4A, 0x7C, 0x8F, 0x36, 0xDF, 0x08, 0xAC,
+	0xBA, 0x51, 0xC9, 0x37, 0x89, 0x7F, 0x72, 0xF2,
+	0x1C, 0x3B, 0xBE, 0x5B, 0x54, 0x99, 0x6F, 0xC6,
+	0x6C, 0x5F, 0x62, 0x68, 0x39, 0xDC, 0x98, 0xDD,
+	0x1D, 0xE4, 0x19, 0x5B, 0x46, 0xCE, 0xE9, 0x80,
+	0x3A, 0x0F, 0xD3, 0xDF, 0xC5, 0x7E, 0x23, 0xF6,
+	0x92, 0xBB, 0x7B, 0x49, 0xB5, 0xD2, 0x12, 0x33,
+	0x1D, 0x55, 0xB1, 0xCE, 0x2D, 0x72, 0x7A, 0xB4,
+	0x1A, 0x11, 0xDA, 0x3A, 0x15, 0xF8, 0xE4, 0xBC,
+	0x11, 0xC7, 0x8B, 0x65, 0xF1, 0xCE, 0xB2, 0x96,
+	0xF1, 0xFE, 0xDC, 0x5F, 0x7E, 0x42, 0x45, 0x6C,
+	0x91, 0x11, 0x17, 0x02, 0x52, 0x01, 0xBE, 0x03,
+	0x89, 0xF5, 0xAB, 0xD4, 0x0D, 0x11, 0xF8, 0x63,
+	0x9A, 0x39, 0xFE, 0x32, 0x36, 0x75, 0x18, 0x35,
+	0xA5, 0xE5, 0xE4, 0x43, 0x17, 0xC1, 0xC2, 0xEE,
+	0xFD, 0x4E, 0xA5, 0xBF, 0xD1, 0x60, 0x43, 0xF4,
+	0x3C, 0xB4, 0x19, 0x81, 0xF6, 0xAD, 0xEE, 0x9D,
+	0x03, 0x15, 0x9E, 0x7A, 0xD9, 0xD1, 0x3C, 0x53,
+	0x36, 0x95, 0x09, 0xFC, 0x1F, 0xA2, 0x7C, 0x16,
+	0xEF, 0x98, 0x87, 0x70, 0x3A, 0x55, 0xB5, 0x1B,
+	0x22, 0xCB, 0xF4, 0x4C, 0xD0, 0x12, 0xAE, 0xE0,
+	0xB2, 0x79, 0x8E, 0x62, 0x84, 0x23, 0x42, 0x8E,
+	0xFC, 0xD5, 0xA4, 0x0C, 0xAE, 0xF6, 0xBF, 0x50,
+	0xD8, 0xEA, 0x88, 0x5E, 0xBF, 0x73, 0xA6, 0xB9,
+	0xFD, 0x79, 0xB5, 0xE1, 0x8F, 0x67, 0xD1, 0x34,
+	0x1A, 0xC8, 0x23, 0x7A, 0x75, 0xC3, 0xCF, 0xC9,
+	0x20, 0x04, 0xA1, 0xC5, 0xA4, 0x0E, 0x36, 0x6B,
+	0xC4, 0x4D, 0x00, 0x17, 0x6A, 0xF7, 0x1C, 0x15,
+	0xE4, 0x8C, 0x86, 0xD3, 0x7E, 0x01, 0x37, 0x23,
+	0xCA, 0xAC, 0x72, 0x23, 0xAB, 0x3B, 0xF4, 0xD5,
+	0x4F, 0x18, 0x28, 0x71, 0x3B, 0x2B, 0x4A, 0x6F,
+	0xE4, 0x0F, 0xAB, 0x74, 0x40, 0x5C, 0xB7, 0x38,
+	0xB0, 0x64, 0xC0, 0x6E, 0xCC, 0x76, 0xE9, 0xEF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+/*
+ * RFC 5114, 2.1.
+ * Group 22 - 1024-bit MODP Group with 160-bit Prime Order Subgroup
+ */
+static const u8 dh_group22_generator[] = {
+	0xA4, 0xD1, 0xCB, 0xD5, 0xC3, 0xFD, 0x34, 0x12,
+	0x67, 0x65, 0xA4, 0x42, 0xEF, 0xB9, 0x99, 0x05,
+	0xF8, 0x10, 0x4D, 0xD2, 0x58, 0xAC, 0x50, 0x7F,
+	0xD6, 0x40, 0x6C, 0xFF, 0x14, 0x26, 0x6D, 0x31,
+	0x26, 0x6F, 0xEA, 0x1E, 0x5C, 0x41, 0x56, 0x4B,
+	0x77, 0x7E, 0x69, 0x0F, 0x55, 0x04, 0xF2, 0x13,
+	0x16, 0x02, 0x17, 0xB4, 0xB0, 0x1B, 0x88, 0x6A,
+	0x5E, 0x91, 0x54, 0x7F, 0x9E, 0x27, 0x49, 0xF4,
+	0xD7, 0xFB, 0xD7, 0xD3, 0xB9, 0xA9, 0x2E, 0xE1,
+	0x90, 0x9D, 0x0D, 0x22, 0x63, 0xF8, 0x0A, 0x76,
+	0xA6, 0xA2, 0x4C, 0x08, 0x7A, 0x09, 0x1F, 0x53,
+	0x1D, 0xBF, 0x0A, 0x01, 0x69, 0xB6, 0xA2, 0x8A,
+	0xD6, 0x62, 0xA4, 0xD1, 0x8E, 0x73, 0xAF, 0xA3,
+	0x2D, 0x77, 0x9D, 0x59, 0x18, 0xD0, 0x8B, 0xC8,
+	0x85, 0x8F, 0x4D, 0xCE, 0xF9, 0x7C, 0x2A, 0x24,
+	0x85, 0x5E, 0x6E, 0xEB, 0x22, 0xB3, 0xB2, 0xE5
+};
+static const u8 dh_group22_prime[] = {
+	0xB1, 0x0B, 0x8F, 0x96, 0xA0, 0x80, 0xE0, 0x1D,
+	0xDE, 0x92, 0xDE, 0x5E, 0xAE, 0x5D, 0x54, 0xEC,
+	0x52, 0xC9, 0x9F, 0xBC, 0xFB, 0x06, 0xA3, 0xC6,
+	0x9A, 0x6A, 0x9D, 0xCA, 0x52, 0xD2, 0x3B, 0x61,
+	0x60, 0x73, 0xE2, 0x86, 0x75, 0xA2, 0x3D, 0x18,
+	0x98, 0x38, 0xEF, 0x1E, 0x2E, 0xE6, 0x52, 0xC0,
+	0x13, 0xEC, 0xB4, 0xAE, 0xA9, 0x06, 0x11, 0x23,
+	0x24, 0x97, 0x5C, 0x3C, 0xD4, 0x9B, 0x83, 0xBF,
+	0xAC, 0xCB, 0xDD, 0x7D, 0x90, 0xC4, 0xBD, 0x70,
+	0x98, 0x48, 0x8E, 0x9C, 0x21, 0x9A, 0x73, 0x72,
+	0x4E, 0xFF, 0xD6, 0xFA, 0xE5, 0x64, 0x47, 0x38,
+	0xFA, 0xA3, 0x1A, 0x4F, 0xF5, 0x5B, 0xCC, 0xC0,
+	0xA1, 0x51, 0xAF, 0x5F, 0x0D, 0xC8, 0xB4, 0xBD,
+	0x45, 0xBF, 0x37, 0xDF, 0x36, 0x5C, 0x1A, 0x65,
+	0xE6, 0x8C, 0xFD, 0xA7, 0x6D, 0x4D, 0xA7, 0x08,
+	0xDF, 0x1F, 0xB2, 0xBC, 0x2E, 0x4A, 0x43, 0x71
+};
+static const u8 dh_group22_order[] = {
+	0xF5, 0x18, 0xAA, 0x87, 0x81, 0xA8, 0xDF, 0x27,
+	0x8A, 0xBA, 0x4E, 0x7D, 0x64, 0xB7, 0xCB, 0x9D,
+	0x49, 0x46, 0x23, 0x53
+};
+
+/*
+ * RFC 5114, 2.2.
+ * Group 23 - 2048-bit MODP Group with 224-bit Prime Order Subgroup
+ */
+static const u8 dh_group23_generator[] = {
+	0xAC, 0x40, 0x32, 0xEF, 0x4F, 0x2D, 0x9A, 0xE3,
+	0x9D, 0xF3, 0x0B, 0x5C, 0x8F, 0xFD, 0xAC, 0x50,
+	0x6C, 0xDE, 0xBE, 0x7B, 0x89, 0x99, 0x8C, 0xAF,
+	0x74, 0x86, 0x6A, 0x08, 0xCF, 0xE4, 0xFF, 0xE3,
+	0xA6, 0x82, 0x4A, 0x4E, 0x10, 0xB9, 0xA6, 0xF0,
+	0xDD, 0x92, 0x1F, 0x01, 0xA7, 0x0C, 0x4A, 0xFA,
+	0xAB, 0x73, 0x9D, 0x77, 0x00, 0xC2, 0x9F, 0x52,
+	0xC5, 0x7D, 0xB1, 0x7C, 0x62, 0x0A, 0x86, 0x52,
+	0xBE, 0x5E, 0x90, 0x01, 0xA8, 0xD6, 0x6A, 0xD7,
+	0xC1, 0x76, 0x69, 0x10, 0x19, 0x99, 0x02, 0x4A,
+	0xF4, 0xD0, 0x27, 0x27, 0x5A, 0xC1, 0x34, 0x8B,
+	0xB8, 0xA7, 0x62, 0xD0, 0x52, 0x1B, 0xC9, 0x8A,
+	0xE2, 0x47, 0x15, 0x04, 0x22, 0xEA, 0x1E, 0xD4,
+	0x09, 0x93, 0x9D, 0x54, 0xDA, 0x74, 0x60, 0xCD,
+	0xB5, 0xF6, 0xC6, 0xB2, 0x50, 0x71, 0x7C, 0xBE,
+	0xF1, 0x80, 0xEB, 0x34, 0x11, 0x8E, 0x98, 0xD1,
+	0x19, 0x52, 0x9A, 0x45, 0xD6, 0xF8, 0x34, 0x56,
+	0x6E, 0x30, 0x25, 0xE3, 0x16, 0xA3, 0x30, 0xEF,
+	0xBB, 0x77, 0xA8, 0x6F, 0x0C, 0x1A, 0xB1, 0x5B,
+	0x05, 0x1A, 0xE3, 0xD4, 0x28, 0xC8, 0xF8, 0xAC,
+	0xB7, 0x0A, 0x81, 0x37, 0x15, 0x0B, 0x8E, 0xEB,
+	0x10, 0xE1, 0x83, 0xED, 0xD1, 0x99, 0x63, 0xDD,
+	0xD9, 0xE2, 0x63, 0xE4, 0x77, 0x05, 0x89, 0xEF,
+	0x6A, 0xA2, 0x1E, 0x7F, 0x5F, 0x2F, 0xF3, 0x81,
+	0xB5, 0x39, 0xCC, 0xE3, 0x40, 0x9D, 0x13, 0xCD,
+	0x56, 0x6A, 0xFB, 0xB4, 0x8D, 0x6C, 0x01, 0x91,
+	0x81, 0xE1, 0xBC, 0xFE, 0x94, 0xB3, 0x02, 0x69,
+	0xED, 0xFE, 0x72, 0xFE, 0x9B, 0x6A, 0xA4, 0xBD,
+	0x7B, 0x5A, 0x0F, 0x1C, 0x71, 0xCF, 0xFF, 0x4C,
+	0x19, 0xC4, 0x18, 0xE1, 0xF6, 0xEC, 0x01, 0x79,
+	0x81, 0xBC, 0x08, 0x7F, 0x2A, 0x70, 0x65, 0xB3,
+	0x84, 0xB8, 0x90, 0xD3, 0x19, 0x1F, 0x2B, 0xFA
+};
+static const u8 dh_group23_prime[] = {
+	0xAD, 0x10, 0x7E, 0x1E, 0x91, 0x23, 0xA9, 0xD0,
+	0xD6, 0x60, 0xFA, 0xA7, 0x95, 0x59, 0xC5, 0x1F,
+	0xA2, 0x0D, 0x64, 0xE5, 0x68, 0x3B, 0x9F, 0xD1,
+	0xB5, 0x4B, 0x15, 0x97, 0xB6, 0x1D, 0x0A, 0x75,
+	0xE6, 0xFA, 0x14, 0x1D, 0xF9, 0x5A, 0x56, 0xDB,
+	0xAF, 0x9A, 0x3C, 0x40, 0x7B, 0xA1, 0xDF, 0x15,
+	0xEB, 0x3D, 0x68, 0x8A, 0x30, 0x9C, 0x18, 0x0E,
+	0x1D, 0xE6, 0xB8, 0x5A, 0x12, 0x74, 0xA0, 0xA6,
+	0x6D, 0x3F, 0x81, 0x52, 0xAD, 0x6A, 0xC2, 0x12,
+	0x90, 0x37, 0xC9, 0xED, 0xEF, 0xDA, 0x4D, 0xF8,
+	0xD9, 0x1E, 0x8F, 0xEF, 0x55, 0xB7, 0x39, 0x4B,
+	0x7A, 0xD5, 0xB7, 0xD0, 0xB6, 0xC1, 0x22, 0x07,
+	0xC9, 0xF9, 0x8D, 0x11, 0xED, 0x34, 0xDB, 0xF6,
+	0xC6, 0xBA, 0x0B, 0x2C, 0x8B, 0xBC, 0x27, 0xBE,
+	0x6A, 0x00, 0xE0, 0xA0, 0xB9, 0xC4, 0x97, 0x08,
+	0xB3, 0xBF, 0x8A, 0x31, 0x70, 0x91, 0x88, 0x36,
+	0x81, 0x28, 0x61, 0x30, 0xBC, 0x89, 0x85, 0xDB,
+	0x16, 0x02, 0xE7, 0x14, 0x41, 0x5D, 0x93, 0x30,
+	0x27, 0x82, 0x73, 0xC7, 0xDE, 0x31, 0xEF, 0xDC,
+	0x73, 0x10, 0xF7, 0x12, 0x1F, 0xD5, 0xA0, 0x74,
+	0x15, 0x98, 0x7D, 0x9A, 0xDC, 0x0A, 0x48, 0x6D,
+	0xCD, 0xF9, 0x3A, 0xCC, 0x44, 0x32, 0x83, 0x87,
+	0x31, 0x5D, 0x75, 0xE1, 0x98, 0xC6, 0x41, 0xA4,
+	0x80, 0xCD, 0x86, 0xA1, 0xB9, 0xE5, 0x87, 0xE8,
+	0xBE, 0x60, 0xE6, 0x9C, 0xC9, 0x28, 0xB2, 0xB9,
+	0xC5, 0x21, 0x72, 0xE4, 0x13, 0x04, 0x2E, 0x9B,
+	0x23, 0xF1, 0x0B, 0x0E, 0x16, 0xE7, 0x97, 0x63,
+	0xC9, 0xB5, 0x3D, 0xCF, 0x4B, 0xA8, 0x0A, 0x29,
+	0xE3, 0xFB, 0x73, 0xC1, 0x6B, 0x8E, 0x75, 0xB9,
+	0x7E, 0xF3, 0x63, 0xE2, 0xFF, 0xA3, 0x1F, 0x71,
+	0xCF, 0x9D, 0xE5, 0x38, 0x4E, 0x71, 0xB8, 0x1C,
+	0x0A, 0xC4, 0xDF, 0xFE, 0x0C, 0x10, 0xE6, 0x4F
+};
+static const u8 dh_group23_order[] = {
+	0x80, 0x1C, 0x0D, 0x34, 0xC5, 0x8D, 0x93, 0xFE,
+	0x99, 0x71, 0x77, 0x10, 0x1F, 0x80, 0x53, 0x5A,
+	0x47, 0x38, 0xCE, 0xBC, 0xBF, 0x38, 0x9A, 0x99,
+	0xB3, 0x63, 0x71, 0xEB
+};
+
+/*
+ * RFC 5114, 2.3.
+ * Group 24 - 2048-bit MODP Group with 256-bit Prime Order Subgroup
+ */
+static const u8 dh_group24_generator[] = {
+	0x3F, 0xB3, 0x2C, 0x9B, 0x73, 0x13, 0x4D, 0x0B,
+	0x2E, 0x77, 0x50, 0x66, 0x60, 0xED, 0xBD, 0x48,
+	0x4C, 0xA7, 0xB1, 0x8F, 0x21, 0xEF, 0x20, 0x54,
+	0x07, 0xF4, 0x79, 0x3A, 0x1A, 0x0B, 0xA1, 0x25,
+	0x10, 0xDB, 0xC1, 0x50, 0x77, 0xBE, 0x46, 0x3F,
+	0xFF, 0x4F, 0xED, 0x4A, 0xAC, 0x0B, 0xB5, 0x55,
+	0xBE, 0x3A, 0x6C, 0x1B, 0x0C, 0x6B, 0x47, 0xB1,
+	0xBC, 0x37, 0x73, 0xBF, 0x7E, 0x8C, 0x6F, 0x62,
+	0x90, 0x12, 0x28, 0xF8, 0xC2, 0x8C, 0xBB, 0x18,
+	0xA5, 0x5A, 0xE3, 0x13, 0x41, 0x00, 0x0A, 0x65,
+	0x01, 0x96, 0xF9, 0x31, 0xC7, 0x7A, 0x57, 0xF2,
+	0xDD, 0xF4, 0x63, 0xE5, 0xE9, 0xEC, 0x14, 0x4B,
+	0x77, 0x7D, 0xE6, 0x2A, 0xAA, 0xB8, 0xA8, 0x62,
+	0x8A, 0xC3, 0x76, 0xD2, 0x82, 0xD6, 0xED, 0x38,
+	0x64, 0xE6, 0x79, 0x82, 0x42, 0x8E, 0xBC, 0x83,
+	0x1D, 0x14, 0x34, 0x8F, 0x6F, 0x2F, 0x91, 0x93,
+	0xB5, 0x04, 0x5A, 0xF2, 0x76, 0x71, 0x64, 0xE1,
+	0xDF, 0xC9, 0x67, 0xC1, 0xFB, 0x3F, 0x2E, 0x55,
+	0xA4, 0xBD, 0x1B, 0xFF, 0xE8, 0x3B, 0x9C, 0x80,
+	0xD0, 0x52, 0xB9, 0x85, 0xD1, 0x82, 0xEA, 0x0A,
+	0xDB, 0x2A, 0x3B, 0x73, 0x13, 0xD3, 0xFE, 0x14,
+	0xC8, 0x48, 0x4B, 0x1E, 0x05, 0x25, 0x88, 0xB9,
+	0xB7, 0xD2, 0xBB, 0xD2, 0xDF, 0x01, 0x61, 0x99,
+	0xEC, 0xD0, 0x6E, 0x15, 0x57, 0xCD, 0x09, 0x15,
+	0xB3, 0x35, 0x3B, 0xBB, 0x64, 0xE0, 0xEC, 0x37,
+	0x7F, 0xD0, 0x28, 0x37, 0x0D, 0xF9, 0x2B, 0x52,
+	0xC7, 0x89, 0x14, 0x28, 0xCD, 0xC6, 0x7E, 0xB6,
+	0x18, 0x4B, 0x52, 0x3D, 0x1D, 0xB2, 0x46, 0xC3,
+	0x2F, 0x63, 0x07, 0x84, 0x90, 0xF0, 0x0E, 0xF8,
+	0xD6, 0x47, 0xD1, 0x48, 0xD4, 0x79, 0x54, 0x51,
+	0x5E, 0x23, 0x27, 0xCF, 0xEF, 0x98, 0xC5, 0x82,
+	0x66, 0x4B, 0x4C, 0x0F, 0x6C, 0xC4, 0x16, 0x59
+};
+static const u8 dh_group24_prime[] = {
+	0x87, 0xA8, 0xE6, 0x1D, 0xB4, 0xB6, 0x66, 0x3C,
+	0xFF, 0xBB, 0xD1, 0x9C, 0x65, 0x19, 0x59, 0x99,
+	0x8C, 0xEE, 0xF6, 0x08, 0x66, 0x0D, 0xD0, 0xF2,
+	0x5D, 0x2C, 0xEE, 0xD4, 0x43, 0x5E, 0x3B, 0x00,
+	0xE0, 0x0D, 0xF8, 0xF1, 0xD6, 0x19, 0x57, 0xD4,
+	0xFA, 0xF7, 0xDF, 0x45, 0x61, 0xB2, 0xAA, 0x30,
+	0x16, 0xC3, 0xD9, 0x11, 0x34, 0x09, 0x6F, 0xAA,
+	0x3B, 0xF4, 0x29, 0x6D, 0x83, 0x0E, 0x9A, 0x7C,
+	0x20, 0x9E, 0x0C, 0x64, 0x97, 0x51, 0x7A, 0xBD,
+	0x5A, 0x8A, 0x9D, 0x30, 0x6B, 0xCF, 0x67, 0xED,
+	0x91, 0xF9, 0xE6, 0x72, 0x5B, 0x47, 0x58, 0xC0,
+	0x22, 0xE0, 0xB1, 0xEF, 0x42, 0x75, 0xBF, 0x7B,
+	0x6C, 0x5B, 0xFC, 0x11, 0xD4, 0x5F, 0x90, 0x88,
+	0xB9, 0x41, 0xF5, 0x4E, 0xB1, 0xE5, 0x9B, 0xB8,
+	0xBC, 0x39, 0xA0, 0xBF, 0x12, 0x30, 0x7F, 0x5C,
+	0x4F, 0xDB, 0x70, 0xC5, 0x81, 0xB2, 0x3F, 0x76,
+	0xB6, 0x3A, 0xCA, 0xE1, 0xCA, 0xA6, 0xB7, 0x90,
+	0x2D, 0x52, 0x52, 0x67, 0x35, 0x48, 0x8A, 0x0E,
+	0xF1, 0x3C, 0x6D, 0x9A, 0x51, 0xBF, 0xA4, 0xAB,
+	0x3A, 0xD8, 0x34, 0x77, 0x96, 0x52, 0x4D, 0x8E,
+	0xF6, 0xA1, 0x67, 0xB5, 0xA4, 0x18, 0x25, 0xD9,
+	0x67, 0xE1, 0x44, 0xE5, 0x14, 0x05, 0x64, 0x25,
+	0x1C, 0xCA, 0xCB, 0x83, 0xE6, 0xB4, 0x86, 0xF6,
+	0xB3, 0xCA, 0x3F, 0x79, 0x71, 0x50, 0x60, 0x26,
+	0xC0, 0xB8, 0x57, 0xF6, 0x89, 0x96, 0x28, 0x56,
+	0xDE, 0xD4, 0x01, 0x0A, 0xBD, 0x0B, 0xE6, 0x21,
+	0xC3, 0xA3, 0x96, 0x0A, 0x54, 0xE7, 0x10, 0xC3,
+	0x75, 0xF2, 0x63, 0x75, 0xD7, 0x01, 0x41, 0x03,
+	0xA4, 0xB5, 0x43, 0x30, 0xC1, 0x98, 0xAF, 0x12,
+	0x61, 0x16, 0xD2, 0x27, 0x6E, 0x11, 0x71, 0x5F,
+	0x69, 0x38, 0x77, 0xFA, 0xD7, 0xEF, 0x09, 0xCA,
+	0xDB, 0x09, 0x4A, 0xE9, 0x1E, 0x1A, 0x15, 0x97
+};
+static const u8 dh_group24_order[] = {
+	0x8C, 0xF8, 0x36, 0x42, 0xA7, 0x09, 0xA0, 0x97,
+	0xB4, 0x47, 0x99, 0x76, 0x40, 0x12, 0x9D, 0xA2,
+	0x99, 0xB1, 0xA4, 0x7D, 0x1E, 0xB3, 0x75, 0x0B,
+	0xA3, 0x08, 0xB0, 0xFE, 0x64, 0xF5, 0xFB, 0xD3
+};
 
 #endif /* ALL_DH_GROUPS */
 
 
-#define DH_GROUP(id) \
+#define DH_GROUP(id,safe) \
 { id, dh_group ## id ## _generator, sizeof(dh_group ## id ## _generator), \
-dh_group ## id ## _prime, sizeof(dh_group ## id ## _prime) }
+dh_group ## id ## _prime, sizeof(dh_group ## id ## _prime), \
+dh_group ## id ## _order, sizeof(dh_group ## id ## _order), safe }
 		
 
 static struct dh_group dh_groups[] = {
-	DH_GROUP(5),
+	DH_GROUP(5, 1),
 #ifdef ALL_DH_GROUPS
-	DH_GROUP(1),
-	DH_GROUP(2),
-	DH_GROUP(14),
-	DH_GROUP(15),
-	DH_GROUP(16),
-	DH_GROUP(17),
-	DH_GROUP(18)
+	DH_GROUP(1, 1),
+	DH_GROUP(2, 1),
+	DH_GROUP(14, 1),
+	DH_GROUP(15, 1),
+	DH_GROUP(16, 1),
+	DH_GROUP(17, 1),
+	DH_GROUP(18, 1),
+	DH_GROUP(22, 0),
+	DH_GROUP(23, 0),
+	DH_GROUP(24, 0)
 #endif /* ALL_DH_GROUPS */
 };
 
diff --git a/src/crypto/dh_groups.h b/src/crypto/dh_groups.h
index 225f006..d0e74b9 100644
--- a/src/crypto/dh_groups.h
+++ b/src/crypto/dh_groups.h
@@ -15,6 +15,9 @@
 	size_t generator_len;
 	const u8 *prime;
 	size_t prime_len;
+	const u8 *order;
+	size_t order_len;
+	unsigned int safe_prime:1;
 };
 
 const struct dh_group * dh_groups_get(int id);
diff --git a/src/crypto/sha256-prf.c b/src/crypto/sha256-prf.c
index 0da6d13..9a11208 100644
--- a/src/crypto/sha256-prf.c
+++ b/src/crypto/sha256-prf.c
@@ -1,6 +1,6 @@
 /*
  * SHA256-based PRF (IEEE 802.11r)
- * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -29,12 +29,36 @@
 void sha256_prf(const u8 *key, size_t key_len, const char *label,
 		const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
 {
+	sha256_prf_bits(key, key_len, label, data, data_len, buf, buf_len * 8);
+}
+
+
+/**
+ * sha256_prf_bits - IEEE Std 802.11-2012, 11.6.1.7.2 Key derivation function
+ * @key: Key for KDF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @data: Extra data to bind into the key
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bits of key to generate
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key. If the requested buf_len is not divisible by eight, the least
+ * significant 1-7 bits of the last octet in the output are not part of the
+ * requested output.
+ */
+void sha256_prf_bits(const u8 *key, size_t key_len, const char *label,
+		     const u8 *data, size_t data_len, u8 *buf,
+		     size_t buf_len_bits)
+{
 	u16 counter = 1;
 	size_t pos, plen;
 	u8 hash[SHA256_MAC_LEN];
 	const u8 *addr[4];
 	size_t len[4];
 	u8 counter_le[2], length_le[2];
+	size_t buf_len = (buf_len_bits + 7) / 8;
 
 	addr[0] = counter_le;
 	len[0] = 2;
@@ -45,7 +69,7 @@
 	addr[3] = length_le;
 	len[3] = sizeof(length_le);
 
-	WPA_PUT_LE16(length_le, buf_len * 8);
+	WPA_PUT_LE16(length_le, buf_len_bits);
 	pos = 0;
 	while (pos < buf_len) {
 		plen = buf_len - pos;
@@ -57,8 +81,18 @@
 		} else {
 			hmac_sha256_vector(key, key_len, 4, addr, len, hash);
 			os_memcpy(&buf[pos], hash, plen);
+			pos += plen;
 			break;
 		}
 		counter++;
 	}
+
+	/*
+	 * Mask out unused bits in the last octet if it does not use all the
+	 * bits.
+	 */
+	if (buf_len_bits % 8) {
+		u8 mask = 0xff << (8 - buf_len_bits % 8);
+		buf[pos - 1] &= mask;
+	}
 }
diff --git a/src/crypto/sha256.h b/src/crypto/sha256.h
index fcac800..7596a52 100644
--- a/src/crypto/sha256.h
+++ b/src/crypto/sha256.h
@@ -1,6 +1,6 @@
 /*
  * SHA256 hash implementation and interface functions
- * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -17,6 +17,9 @@
 		size_t data_len, u8 *mac);
 void sha256_prf(const u8 *key, size_t key_len, const char *label,
 	      const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
+void sha256_prf_bits(const u8 *key, size_t key_len, const char *label,
+		     const u8 *data, size_t data_len, u8 *buf,
+		     size_t buf_len_bits);
 void tls_prf_sha256(const u8 *secret, size_t secret_len,
 		    const char *label, const u8 *seed, size_t seed_len,
 		    u8 *out, size_t outlen);
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index f7bdc2c..5e8fd65 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -898,6 +898,7 @@
 	size_t supp_rates_len;
 	u16 listen_interval;
 	const struct ieee80211_ht_capabilities *ht_capabilities;
+	const struct ieee80211_vht_capabilities *vht_capabilities;
 	u32 flags; /* bitmask of WPA_STA_* flags */
 	int set; /* Set STA parameters instead of add */
 	u8 qosinfo;
@@ -907,10 +908,19 @@
 	int mode;
 	int freq;
 	int channel;
+	/* for HT */
 	int ht_enabled;
 	int sec_channel_offset; /* 0 = HT40 disabled, -1 = HT40 enabled,
 				 * secondary channel below primary, 1 = HT40
 				 * enabled, secondary channel above primary */
+
+	/* for VHT */
+	int vht_enabled;
+
+	/* valid for both HT and VHT, center_freq2 is non-zero
+	 * only for bandwidth 80 and an 80+80 channel */
+	int center_freq1, center_freq2;
+	int bandwidth;
 };
 
 enum wpa_driver_if_type {
diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c
index a0a5210..c2f5934 100644
--- a/src/drivers/driver_atheros.c
+++ b/src/drivers/driver_atheros.c
@@ -874,7 +874,7 @@
 }
 #endif /* CONFIG_HS20 */
 
-#if defined(CONFIG_IEEE80211V) && !defined(CONFIG_IEEE80211R)
+#if defined(CONFIG_WNM) && !defined(CONFIG_IEEE80211R)
 static void atheros_raw_recv_11v(void *ctx, const u8 *src_addr, const u8 *buf,
 				 size_t len)
 {
@@ -921,9 +921,9 @@
 		break;
 	}
 }
-#endif /* CONFIG_IEEE80211V */
+#endif /* CONFIG_WNM */
 
-#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211V)
+#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_WNM)
 static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf,
 				size_t len)
 {
@@ -933,9 +933,9 @@
 #ifdef CONFIG_IEEE80211R
 	atheros_raw_recv_11r(ctx, src_addr, buf, len);
 #endif /* CONFIG_IEEE80211R */
-#if defined(CONFIG_IEEE80211V) && !defined(CONFIG_IEEE80211R)
+#if defined(CONFIG_WNM) && !defined(CONFIG_IEEE80211R)
 	atheros_raw_recv_11v(ctx, src_addr, buf, len);
-#endif /* CONFIG_IEEE80211V */
+#endif /* CONFIG_WNM */
 #ifdef CONFIG_HS20
 	atheros_raw_recv_hs20(ctx, src_addr, buf, len);
 #endif /* CONFIG_HS20 */
@@ -957,9 +957,9 @@
 			       IEEE80211_FILTER_TYPE_AUTH |
 			       IEEE80211_FILTER_TYPE_ACTION);
 #endif
-#ifdef CONFIG_IEEE80211V
+#ifdef CONFIG_WNM
 	filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION;
-#endif /* CONFIG_IEEE80211V */
+#endif /* CONFIG_WNM */
 #ifdef CONFIG_HS20
 	filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION;
 #endif /* CONFIG_HS20 */
@@ -1688,13 +1688,17 @@
 	linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0);
 	atheros_set_privacy(drv, 0); /* default to no privacy */
 
-	atheros_receive_pkt(drv);
+	if (atheros_receive_pkt(drv))
+		goto bad;
 
 	if (atheros_wireless_event_init(drv))
 		goto bad;
 
 	return drv;
 bad:
+	atheros_reset_appfilter(drv);
+	if (drv->sock_raw)
+		l2_packet_deinit(drv->sock_raw);
 	if (drv->sock_recv != NULL && drv->sock_recv != drv->sock_xmit)
 		l2_packet_deinit(drv->sock_recv);
 	if (drv->sock_xmit != NULL)
@@ -1959,7 +1963,7 @@
 }
 
 
-#ifdef CONFIG_IEEE80211V
+#ifdef CONFIG_WNM
 static int athr_wnm_tfs(struct atheros_driver_data *drv, const u8* peer,
 			u8 *ie, u16 *len, enum wnm_oper oper)
 {
@@ -2112,7 +2116,7 @@
 		return -1;
 	}
 }
-#endif /* CONFIG_IEEE80211V */
+#endif /* CONFIG_WNM */
 
 
 const struct wpa_driver_ops wpa_driver_atheros_ops = {
@@ -2146,7 +2150,7 @@
 	.add_sta_node    	= atheros_add_sta_node,
 #endif /* CONFIG_IEEE80211R */
 	.send_action		= atheros_send_action,
-#ifdef CONFIG_IEEE80211V
+#ifdef CONFIG_WNM
 	.wnm_oper		= atheros_wnm_oper,
-#endif /* CONFIG_IEEE80211V */
+#endif /* CONFIG_WNM */
 };
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index a0644b4..e530911 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -195,6 +195,7 @@
 
 	int freq;
 
+	void *ctx;
 	struct nl_handle *nl_preq, *nl_mgmt;
 	struct nl_cb *nl_cb;
 
@@ -1538,7 +1539,7 @@
 }
 
 
-static void mlme_event_michael_mic_failure(struct wpa_driver_nl80211_data *drv,
+static void mlme_event_michael_mic_failure(struct i802_bss *bss,
 					   struct nlattr *tb[])
 {
 	union wpa_event_data data;
@@ -1570,7 +1571,7 @@
 		wpa_printf(MSG_DEBUG, "nl80211: Key Id %d", key_id);
 	}
 
-	wpa_supplicant_event(drv->ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
+	wpa_supplicant_event(bss->ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
 }
 
 
@@ -2177,9 +2178,11 @@
 }
 
 
-static void do_process_drv_event(struct wpa_driver_nl80211_data *drv,
-				 int cmd, struct nlattr **tb)
+static void do_process_drv_event(struct i802_bss *bss, int cmd,
+				 struct nlattr **tb)
 {
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+
 	if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED &&
 	    (cmd == NL80211_CMD_NEW_SCAN_RESULTS ||
 	     cmd == NL80211_CMD_SCAN_ABORTED)) {
@@ -2252,7 +2255,7 @@
 				      tb[NL80211_ATTR_DISCONNECTED_BY_AP]);
 		break;
 	case NL80211_CMD_MICHAEL_MIC_FAILURE:
-		mlme_event_michael_mic_failure(drv, tb);
+		mlme_event_michael_mic_failure(bss, tb);
 		break;
 	case NL80211_CMD_JOIN_IBSS:
 		mlme_event_join_ibss(drv, tb);
@@ -2307,21 +2310,25 @@
 	struct wpa_driver_nl80211_data *drv = arg;
 	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
 	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct i802_bss *bss;
+	int ifidx = -1;
 
 	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
 		  genlmsg_attrlen(gnlh, 0), NULL);
 
-	if (tb[NL80211_ATTR_IFINDEX]) {
-		int ifindex = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
-		if (ifindex != drv->ifindex && !have_ifidx(drv, ifindex)) {
-			wpa_printf(MSG_DEBUG, "nl80211: Ignored event (cmd=%d)"
-				   " for foreign interface (ifindex %d)",
-				   gnlh->cmd, ifindex);
+	if (tb[NL80211_ATTR_IFINDEX])
+		ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
+
+	for (bss = &drv->first_bss; bss; bss = bss->next) {
+		if (ifidx == -1 || ifidx == bss->ifindex) {
+			do_process_drv_event(bss, gnlh->cmd, tb);
 			return NL_SKIP;
 		}
 	}
 
-	do_process_drv_event(drv, gnlh->cmd, tb);
+	wpa_printf(MSG_DEBUG, "nl80211: Ignored event (cmd=%d) for foreign "
+		   "interface (ifindex %d)", gnlh->cmd, ifidx);
+
 	return NL_SKIP;
 }
 
@@ -2333,6 +2340,7 @@
 	struct nlattr *tb[NL80211_ATTR_MAX + 1];
 	struct wpa_driver_nl80211_data *drv, *tmp;
 	int ifidx = -1;
+	struct i802_bss *bss;
 
 	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
 		  genlmsg_attrlen(gnlh, 0), NULL);
@@ -2342,9 +2350,12 @@
 
 	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))
-			do_process_drv_event(drv, gnlh->cmd, tb);
+		for (bss = &drv->first_bss; bss; bss = bss->next) {
+			if (ifidx == -1 || ifidx == bss->ifindex) {
+				do_process_drv_event(bss, gnlh->cmd, tb);
+				return NL_SKIP;
+			}
+		}
 	}
 
 	return NL_SKIP;
@@ -3087,6 +3098,8 @@
 	drv->ctx = ctx;
 	bss = &drv->first_bss;
 	bss->drv = drv;
+	bss->ctx = ctx;
+
 	os_strlcpy(bss->ifname, ifname, sizeof(bss->ifname));
 	drv->monitor_ifidx = -1;
 	drv->monitor_sock = -1;
@@ -3284,6 +3297,9 @@
 	/* WNM - BSS Transition Management Request */
 	if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x07", 2) < 0)
 		return -1;
+	/* WNM-Sleep Mode Response */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x11", 2) < 0)
+		return -1;
 
 	return 0;
 }
@@ -4989,19 +5005,35 @@
 				/* crude heuristic */
 				if (mode->channels[idx].freq < 4000)
 					mode->mode = HOSTAPD_MODE_IEEE80211B;
+				else if (mode->channels[idx].freq > 50000)
+					mode->mode = HOSTAPD_MODE_IEEE80211AD;
 				else
 					mode->mode = HOSTAPD_MODE_IEEE80211A;
 				mode_is_set = 1;
 			}
 
-			/* crude heuristic */
-			if (mode->channels[idx].freq < 4000)
+			switch (mode->mode) {
+			case HOSTAPD_MODE_IEEE80211AD:
+				mode->channels[idx].chan =
+					(mode->channels[idx].freq - 56160) /
+					2160;
+				break;
+			case HOSTAPD_MODE_IEEE80211A:
+				mode->channels[idx].chan =
+					mode->channels[idx].freq / 5 - 1000;
+				break;
+			case HOSTAPD_MODE_IEEE80211B:
+			case HOSTAPD_MODE_IEEE80211G:
 				if (mode->channels[idx].freq == 2484)
 					mode->channels[idx].chan = 14;
 				else
-					mode->channels[idx].chan = (mode->channels[idx].freq - 2407) / 5;
-			else
-				mode->channels[idx].chan = mode->channels[idx].freq/5 - 1000;
+					mode->channels[idx].chan =
+						(mode->channels[idx].freq -
+						 2407) / 5;
+				break;
+			default:
+				break;
+			}
 
 			if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
 				mode->channels[idx].flag |=
@@ -5665,16 +5697,16 @@
 
 
 static int wpa_driver_nl80211_set_freq(struct i802_bss *bss,
-				       int freq, int ht_enabled,
-				       int sec_channel_offset)
+				       struct hostapd_freq_params *freq)
 {
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct nl_msg *msg;
 	int ret;
 
-	wpa_printf(MSG_DEBUG, "nl80211: Set freq %d (ht_enabled=%d "
-		   "sec_channel_offset=%d)",
-		   freq, ht_enabled, sec_channel_offset);
+	wpa_printf(MSG_DEBUG, "nl80211: Set freq %d (ht_enabled=%d, vht_enabled=%d,"
+		   " bandwidth=%d MHz, cf1=%d MHz, cf2=%d MHz)",
+		   freq->freq, freq->ht_enabled, freq->vht_enabled,
+		   freq->bandwidth, freq->center_freq1, freq->center_freq2);
 	msg = nlmsg_alloc();
 	if (!msg)
 		return -1;
@@ -5682,9 +5714,38 @@
 	nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY);
 
 	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
-	if (ht_enabled) {
-		switch (sec_channel_offset) {
+	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq->freq);
+	if (freq->vht_enabled) {
+		switch (freq->bandwidth) {
+		case 20:
+			NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH,
+				    NL80211_CHAN_WIDTH_20);
+			break;
+		case 40:
+			NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH,
+				    NL80211_CHAN_WIDTH_40);
+			break;
+		case 80:
+			if (freq->center_freq2)
+				NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH,
+					    NL80211_CHAN_WIDTH_80P80);
+			else
+				NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH,
+					    NL80211_CHAN_WIDTH_80);
+			break;
+		case 160:
+			NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH,
+				    NL80211_CHAN_WIDTH_160);
+			break;
+		default:
+			return -1;
+		}
+		NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ1, freq->center_freq1);
+		if (freq->center_freq2)
+			NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ2,
+				    freq->center_freq2);
+	} else if (freq->ht_enabled) {
+		switch (freq->sec_channel_offset) {
 		case -1:
 			NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
 				    NL80211_CHAN_HT40MINUS);
@@ -5703,11 +5764,11 @@
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
 	msg = NULL;
 	if (ret == 0) {
-		bss->freq = freq;
+		bss->freq = freq->freq;
 		return 0;
 	}
 	wpa_printf(MSG_DEBUG, "nl80211: Failed to set channel (freq=%d): "
-		   "%d (%s)", freq, ret, strerror(-ret));
+		   "%d (%s)", freq->freq, ret, strerror(-ret));
 nla_put_failure:
 	nlmsg_free(msg);
 	return -1;
@@ -5768,6 +5829,12 @@
 			params->ht_capabilities);
 	}
 
+	if (params->vht_capabilities) {
+		NLA_PUT(msg, NL80211_ATTR_VHT_CAPABILITY,
+			sizeof(*params->vht_capabilities),
+			params->vht_capabilities);
+	}
+
 	os_memset(&upd, 0, sizeof(upd));
 	upd.mask = sta_flags_nl80211(params->flags);
 	upd.set = upd.mask;
@@ -6603,7 +6670,10 @@
 static int wpa_driver_nl80211_ap(struct wpa_driver_nl80211_data *drv,
 				 struct wpa_driver_associate_params *params)
 {
-	enum nl80211_iftype nlmode;
+	enum nl80211_iftype nlmode, old_mode;
+	struct hostapd_freq_params freq = {
+		.freq = params->freq,
+	};
 
 	if (params->p2p) {
 		wpa_printf(MSG_DEBUG, "nl80211: Setup AP operations for P2P "
@@ -6612,8 +6682,15 @@
 	} else
 		nlmode = NL80211_IFTYPE_AP;
 
-	if (wpa_driver_nl80211_set_mode(&drv->first_bss, nlmode) ||
-	    wpa_driver_nl80211_set_freq(&drv->first_bss, params->freq, 0, 0)) {
+	old_mode = drv->nlmode;
+	if (wpa_driver_nl80211_set_mode(&drv->first_bss, nlmode)) {
+		nl80211_remove_monitor_interface(drv);
+		return -1;
+	}
+
+	if (wpa_driver_nl80211_set_freq(&drv->first_bss, &freq)) {
+		if (old_mode != nlmode)
+			wpa_driver_nl80211_set_mode(&drv->first_bss, old_mode);
 		nl80211_remove_monitor_interface(drv);
 		return -1;
 	}
@@ -6899,6 +6976,11 @@
 		NLA_PUT_U32(msg, NL80211_ATTR_AKM_SUITES, mgmt);
 	}
 
+#ifdef CONFIG_IEEE80211W
+	if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED)
+		NLA_PUT_U32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED);
+#endif /* CONFIG_IEEE80211W */
+
 	if (params->disable_ht)
 		NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_HT);
 
@@ -7300,8 +7382,7 @@
 static int i802_set_freq(void *priv, struct hostapd_freq_params *freq)
 {
 	struct i802_bss *bss = priv;
-	return wpa_driver_nl80211_set_freq(bss, freq->freq, freq->ht_enabled,
-					   freq->sec_channel_offset);
+	return wpa_driver_nl80211_set_freq(bss, freq);
 }
 
 
@@ -8137,6 +8218,7 @@
 		new_bss->drv = drv;
 		new_bss->next = drv->first_bss.next;
 		new_bss->freq = drv->first_bss.freq;
+		new_bss->ctx = bss_ctx;
 		drv->first_bss.next = new_bss;
 		if (drv_priv)
 			*drv_priv = new_bss;
diff --git a/src/drivers/driver_test.c b/src/drivers/driver_test.c
index 89aee7d..bd65dd8 100644
--- a/src/drivers/driver_test.c
+++ b/src/drivers/driver_test.c
@@ -1321,7 +1321,8 @@
 		for (i = 0; i < drv->num_scanres; i++) {
 			struct wpa_scan_res *bss = drv->scanres[i];
 			if (p2p_scan_res_handler(drv->p2p, bss->bssid,
-						 bss->freq, bss->level,
+						 bss->freq, bss->age,
+						 bss->level,
 						 (const u8 *) (bss + 1),
 						 bss->ie_len) > 0)
 				return;
diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
index 1a9a819..e3e19f8 100644
--- a/src/drivers/nl80211_copy.h
+++ b/src/drivers/nl80211_copy.h
@@ -118,8 +118,9 @@
  *	to get a list of all present wiphys.
  * @NL80211_CMD_SET_WIPHY: set wiphy parameters, needs %NL80211_ATTR_WIPHY or
  *	%NL80211_ATTR_IFINDEX; can be used to set %NL80211_ATTR_WIPHY_NAME,
- *	%NL80211_ATTR_WIPHY_TXQ_PARAMS, %NL80211_ATTR_WIPHY_FREQ,
- *	%NL80211_ATTR_WIPHY_CHANNEL_TYPE, %NL80211_ATTR_WIPHY_RETRY_SHORT,
+ *	%NL80211_ATTR_WIPHY_TXQ_PARAMS, %NL80211_ATTR_WIPHY_FREQ (and the
+ *	attributes determining the channel width; this is used for setting
+ *	monitor mode channel),  %NL80211_ATTR_WIPHY_RETRY_SHORT,
  *	%NL80211_ATTR_WIPHY_RETRY_LONG, %NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
  *	and/or %NL80211_ATTR_WIPHY_RTS_THRESHOLD.
  *	However, for setting the channel, see %NL80211_CMD_SET_CHANNEL
@@ -171,7 +172,7 @@
  *	%NL80211_ATTR_AKM_SUITES, %NL80211_ATTR_PRIVACY,
  *	%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_ATTR_WIPHY_FREQ and the attributes determining channel width.
  * @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
@@ -401,8 +402,7 @@
  *	a response while being associated to an AP on another channel.
  *	%NL80211_ATTR_IFINDEX is used to specify which interface (and thus
  *	radio) is used. %NL80211_ATTR_WIPHY_FREQ is used to specify the
- *	frequency for the operation and %NL80211_ATTR_WIPHY_CHANNEL_TYPE may be
- *	optionally used to specify additional channel parameters.
+ *	frequency for the operation.
  *	%NL80211_ATTR_DURATION is used to specify the duration in milliseconds
  *	to remain on the channel. This command is also used as an event to
  *	notify when the requested duration starts (it may take a while for the
@@ -440,12 +440,11 @@
  *	as an event indicating reception of a frame that was not processed in
  *	kernel code, but is for us (i.e., which may need to be processed in a
  *	user space application). %NL80211_ATTR_FRAME is used to specify the
- *	frame contents (including header). %NL80211_ATTR_WIPHY_FREQ (and
- *	optionally %NL80211_ATTR_WIPHY_CHANNEL_TYPE) is used to indicate on
- *	which channel the frame is to be transmitted or was received. If this
- *	channel is not the current channel (remain-on-channel or the
- *	operational channel) the device will switch to the given channel and
- *	transmit the frame, optionally waiting for a response for the time
+ *	frame contents (including header). %NL80211_ATTR_WIPHY_FREQ is used
+ *	to indicate on which channel the frame is to be transmitted or was
+ *	received. If this channel is not the current channel (remain-on-channel
+ *	or the operational channel) the device will switch to the given channel
+ *	and transmit the frame, optionally waiting for a response for the time
  *	specified using %NL80211_ATTR_DURATION. When called, this operation
  *	returns a cookie (%NL80211_ATTR_COOKIE) that will be included with the
  *	TX status event pertaining to the TX request.
@@ -473,8 +472,8 @@
  *	command is used as an event to indicate the that a trigger level was
  *	reached.
  * @NL80211_CMD_SET_CHANNEL: Set the channel (using %NL80211_ATTR_WIPHY_FREQ
- *	and %NL80211_ATTR_WIPHY_CHANNEL_TYPE) the given interface (identifed
- *	by %NL80211_ATTR_IFINDEX) shall operate on.
+ *	and the attributes determining channel width) the given interface
+ *	(identifed by %NL80211_ATTR_IFINDEX) shall operate on.
  *	In case multiple channels are supported by the device, the mechanism
  *	with which it switches channels is implementation-defined.
  *	When a monitor interface is given, it can only switch channel while
@@ -568,8 +567,8 @@
  *
  * @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_ATTR_IFINDEX is now on %NL80211_ATTR_WIPHY_FREQ and the
+ *	attributes determining channel width.
  *
  * @NL80211_CMD_START_P2P_DEVICE: Start the given P2P Device, identified by
  *	its %NL80211_ATTR_WDEV identifier. It must have been created with
@@ -773,14 +772,26 @@
  *	/sys/class/ieee80211/<phyname>/index
  * @NL80211_ATTR_WIPHY_NAME: wiphy name (used for renaming)
  * @NL80211_ATTR_WIPHY_TXQ_PARAMS: a nested array of TX queue parameters
- * @NL80211_ATTR_WIPHY_FREQ: frequency of the selected channel in MHz
+ * @NL80211_ATTR_WIPHY_FREQ: frequency of the selected channel in MHz,
+ *	defines the channel together with the (deprecated)
+ *	%NL80211_ATTR_WIPHY_CHANNEL_TYPE attribute or the attributes
+ *	%NL80211_ATTR_CHANNEL_WIDTH and if needed %NL80211_ATTR_CENTER_FREQ1
+ *	and %NL80211_ATTR_CENTER_FREQ2
+ * @NL80211_ATTR_CHANNEL_WIDTH: u32 attribute containing one of the values
+ *	of &enum nl80211_chan_width, describing the channel width. See the
+ *	documentation of the enum for more information.
+ * @NL80211_ATTR_CENTER_FREQ1: Center frequency of the first part of the
+ *	channel, used for anything but 20 MHz bandwidth
+ * @NL80211_ATTR_CENTER_FREQ2: Center frequency of the second part of the
+ *	channel, used only for 80+80 MHz bandwidth
  * @NL80211_ATTR_WIPHY_CHANNEL_TYPE: included with NL80211_ATTR_WIPHY_FREQ
- *	if HT20 or HT40 are allowed (i.e., 802.11n disabled if not included):
+ *	if HT20 or HT40 are to be used (i.e., HT disabled if not included):
  *	NL80211_CHAN_NO_HT = HT not allowed (i.e., same as not including
  *		this attribute)
  *	NL80211_CHAN_HT20 = HT20 only
  *	NL80211_CHAN_HT40MINUS = secondary channel is below the primary channel
  *	NL80211_CHAN_HT40PLUS = secondary channel is above the primary channel
+ *	This attribute is now deprecated.
  * @NL80211_ATTR_WIPHY_RETRY_SHORT: TX retry limit for frames whose length is
  *	less than or equal to the RTS threshold; allowed range: 1..255;
  *	dot11ShortRetryLimit; u8
@@ -1292,6 +1303,13 @@
  *
  * @NL80211_ATTR_SCAN_FLAGS: scan request control flags (u32)
  *
+ * @NL80211_ATTR_P2P_CTWINDOW: P2P GO Client Traffic Window (u8), used with
+ *	the START_AP and SET_BSS commands
+ * @NL80211_ATTR_P2P_OPPPS: P2P GO opportunistic PS (u8), used with the
+ *	START_AP and SET_BSS commands. This can have the values 0 or 1;
+ *	if not given in START_AP 0 is assumed, if not given in SET_BSS
+ *	no change is made.
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -1555,6 +1573,13 @@
 
 	NL80211_ATTR_SCAN_FLAGS,
 
+	NL80211_ATTR_CHANNEL_WIDTH,
+	NL80211_ATTR_CENTER_FREQ1,
+	NL80211_ATTR_CENTER_FREQ2,
+
+	NL80211_ATTR_P2P_CTWINDOW,
+	NL80211_ATTR_P2P_OPPPS,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
@@ -1719,10 +1744,15 @@
  * @__NL80211_RATE_INFO_INVALID: attribute number 0 is reserved
  * @NL80211_RATE_INFO_BITRATE: total bitrate (u16, 100kbit/s)
  * @NL80211_RATE_INFO_MCS: mcs index for 802.11n (u8)
- * @NL80211_RATE_INFO_40_MHZ_WIDTH: 40 Mhz dualchannel bitrate
+ * @NL80211_RATE_INFO_40_MHZ_WIDTH: 40 MHz dualchannel bitrate
  * @NL80211_RATE_INFO_SHORT_GI: 400ns guard interval
  * @NL80211_RATE_INFO_BITRATE32: total bitrate (u32, 100kbit/s)
  * @NL80211_RATE_INFO_MAX: highest rate_info number currently defined
+ * @NL80211_RATE_INFO_VHT_MCS: MCS index for VHT (u8)
+ * @NL80211_RATE_INFO_VHT_NSS: number of streams in VHT (u8)
+ * @NL80211_RATE_INFO_80_MHZ_WIDTH: 80 MHz VHT rate
+ * @NL80211_RATE_INFO_80P80_MHZ_WIDTH: 80+80 MHz VHT rate
+ * @NL80211_RATE_INFO_160_MHZ_WIDTH: 160 MHz VHT rate
  * @__NL80211_RATE_INFO_AFTER_LAST: internal use
  */
 enum nl80211_rate_info {
@@ -1732,6 +1762,11 @@
 	NL80211_RATE_INFO_40_MHZ_WIDTH,
 	NL80211_RATE_INFO_SHORT_GI,
 	NL80211_RATE_INFO_BITRATE32,
+	NL80211_RATE_INFO_VHT_MCS,
+	NL80211_RATE_INFO_VHT_NSS,
+	NL80211_RATE_INFO_80_MHZ_WIDTH,
+	NL80211_RATE_INFO_80P80_MHZ_WIDTH,
+	NL80211_RATE_INFO_160_MHZ_WIDTH,
 
 	/* keep last */
 	__NL80211_RATE_INFO_AFTER_LAST,
@@ -2440,6 +2475,15 @@
 #define NL80211_TXQ_Q_BE	NL80211_AC_BE
 #define NL80211_TXQ_Q_BK	NL80211_AC_BK
 
+/**
+ * enum nl80211_channel_type - channel type
+ * @NL80211_CHAN_NO_HT: 20 MHz, non-HT channel
+ * @NL80211_CHAN_HT20: 20 MHz HT channel
+ * @NL80211_CHAN_HT40MINUS: HT40 channel, secondary channel
+ *	below the control channel
+ * @NL80211_CHAN_HT40PLUS: HT40 channel, secondary channel
+ *	above the control channel
+ */
 enum nl80211_channel_type {
 	NL80211_CHAN_NO_HT,
 	NL80211_CHAN_HT20,
@@ -2448,6 +2492,32 @@
 };
 
 /**
+ * enum nl80211_chan_width - channel width definitions
+ *
+ * These values are used with the %NL80211_ATTR_CHANNEL_WIDTH
+ * attribute.
+ *
+ * @NL80211_CHAN_WIDTH_20_NOHT: 20 MHz, non-HT channel
+ * @NL80211_CHAN_WIDTH_20: 20 MHz HT channel
+ * @NL80211_CHAN_WIDTH_40: 40 MHz channel, the %NL80211_ATTR_CENTER_FREQ1
+ *	attribute must be provided as well
+ * @NL80211_CHAN_WIDTH_80: 80 MHz channel, the %NL80211_ATTR_CENTER_FREQ1
+ *	attribute must be provided as well
+ * @NL80211_CHAN_WIDTH_80P80: 80+80 MHz channel, the %NL80211_ATTR_CENTER_FREQ1
+ *	and %NL80211_ATTR_CENTER_FREQ2 attributes must be provided as well
+ * @NL80211_CHAN_WIDTH_160: 160 MHz channel, the %NL80211_ATTR_CENTER_FREQ1
+ *	attribute must be provided as well
+ */
+enum nl80211_chan_width {
+	NL80211_CHAN_WIDTH_20_NOHT,
+	NL80211_CHAN_WIDTH_20,
+	NL80211_CHAN_WIDTH_40,
+	NL80211_CHAN_WIDTH_80,
+	NL80211_CHAN_WIDTH_80P80,
+	NL80211_CHAN_WIDTH_160,
+};
+
+/**
  * enum nl80211_bss - netlink attributes for a BSS
  *
  * @__NL80211_BSS_INVALID: invalid
@@ -3066,6 +3136,10 @@
  * @NL80211_FEATURE_NEED_OBSS_SCAN: The driver expects userspace to perform
  *	OBSS scans and generate 20/40 BSS coex reports. This flag is used only
  *	for drivers implementing the CONNECT API, for AUTH/ASSOC it is implied.
+ * @NL80211_FEATURE_P2P_GO_CTWIN: P2P GO implementation supports CT Window
+ *	setting
+ * @NL80211_FEATURE_P2P_GO_OPPPS: P2P GO implementation supports opportunistic
+ *	powersave
  */
 enum nl80211_feature_flags {
 	NL80211_FEATURE_SK_TX_STATUS			= 1 << 0,
@@ -3079,6 +3153,8 @@
 	NL80211_FEATURE_AP_SCAN				= 1 << 8,
 	NL80211_FEATURE_VIF_TXPOWER			= 1 << 9,
 	NL80211_FEATURE_NEED_OBSS_SCAN			= 1 << 10,
+	NL80211_FEATURE_P2P_GO_CTWIN			= 1 << 11,
+	NL80211_FEATURE_P2P_GO_OPPPS			= 1 << 12,
 };
 
 /**
diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c
index a4c9b25..85c242a 100644
--- a/src/eap_peer/eap.c
+++ b/src/eap_peer/eap.c
@@ -891,6 +891,7 @@
 
 	wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_STARTED
 		"EAP authentication started");
+	eap_notify_status(sm, "started", "");
 
 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, req,
 			       &msg_len);
diff --git a/src/eap_peer/eap_fast_pac.c b/src/eap_peer/eap_fast_pac.c
index fc987da..8c480b9 100644
--- a/src/eap_peer/eap_fast_pac.c
+++ b/src/eap_peer/eap_fast_pac.c
@@ -422,8 +422,12 @@
 	if (eap_fast_init_pac_data(sm, pac_file, &rc) < 0)
 		return 0;
 
-	if (eap_fast_read_line(&rc, &pos) < 0 ||
-	    os_strcmp(pac_file_hdr, rc.buf) != 0)
+	if (eap_fast_read_line(&rc, &pos) < 0) {
+		/* empty file - assume it is fine to overwrite */
+		eap_fast_deinit_pac_data(&rc);
+		return 0;
+	}
+	if (os_strcmp(pac_file_hdr, rc.buf) != 0)
 		err = "Unrecognized header line";
 
 	while (!err && eap_fast_read_line(&rc, &pos) == 0) {
diff --git a/src/eap_server/eap_server_aka.c b/src/eap_server/eap_server_aka.c
index 177b58d..469b9a0 100644
--- a/src/eap_server/eap_server_aka.c
+++ b/src/eap_server/eap_server_aka.c
@@ -731,6 +731,17 @@
 		return;
 	}
 
+	if (((data->eap_method == EAP_TYPE_AKA_PRIME &&
+	      username[0] == EAP_AKA_PRIME_REAUTH_ID_PREFIX) ||
+	     (data->eap_method == EAP_TYPE_AKA &&
+	      username[0] == EAP_AKA_REAUTH_ID_PREFIX)) &&
+	    data->identity_round == 1) {
+		/* Remain in IDENTITY state for another round to request full
+		 * auth identity since we did not recognize reauth id */
+		os_free(username);
+		return;
+	}
+
 	if ((data->eap_method == EAP_TYPE_AKA_PRIME &&
 	     username[0] == EAP_AKA_PRIME_PSEUDONYM_PREFIX) ||
 	    (data->eap_method == EAP_TYPE_AKA &&
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index b122f7c..aaacc9a 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -630,6 +630,7 @@
  *	P2P Device Address or P2P Interface Address)
  * @level: Signal level (signal strength of the received frame from the peer)
  * @freq: Frequency on which the Beacon or Probe Response frame was received
+ * @age_ms: Age of the information in milliseconds
  * @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
@@ -640,13 +641,15 @@
  * like Provision Discovery Request that contains P2P Capability and P2P Device
  * Info attributes.
  */
-int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, int level,
-		   const u8 *ies, size_t ies_len, int scan_res)
+int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
+		   unsigned int age_ms, int level, const u8 *ies,
+		   size_t ies_len, int scan_res)
 {
 	struct p2p_device *dev;
 	struct p2p_message msg;
 	const u8 *p2p_dev_addr;
 	int i;
+	struct os_time time_now, time_tmp_age, entry_ts;
 
 	os_memset(&msg, 0, sizeof(msg));
 	if (p2p_parse_ies(ies, ies_len, &msg)) {
@@ -673,6 +676,7 @@
 		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Do not add peer "
 			"filter for " MACSTR " due to peer filter",
 			MAC2STR(p2p_dev_addr));
+		p2p_parse_free(&msg);
 		return 0;
 	}
 
@@ -681,7 +685,24 @@
 		p2p_parse_free(&msg);
 		return -1;
 	}
-	os_get_time(&dev->last_seen);
+
+	os_get_time(&time_now);
+	time_tmp_age.sec = age_ms / 1000;
+	time_tmp_age.usec = (age_ms % 1000) * 1000;
+	os_time_sub(&time_now, &time_tmp_age, &entry_ts);
+
+	/*
+	 * Update the device entry only if the new peer
+	 * entry is newer than the one previously stored.
+	 */
+	if (dev->last_seen.usec > 0 &&
+	    os_time_before(&entry_ts, &dev->last_seen)) {
+		p2p_parse_free(&msg);
+		return -1;
+	}
+
+	os_memcpy(&dev->last_seen, &entry_ts, sizeof(struct os_time));
+
 	dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY);
 
 	if (os_memcmp(addr, p2p_dev_addr, ETH_ALEN) != 0)
@@ -1123,7 +1144,9 @@
 	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Stopping find");
 	eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
 	p2p_clear_timeout(p2p);
-	if (p2p->state == P2P_SEARCH)
+	if (p2p->state == P2P_SEARCH ||
+	    p2p->state == P2P_CONTINUE_SEARCH_WHEN_READY ||
+	    p2p->state == P2P_SEARCH_WHEN_READY)
 		wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, P2P_EVENT_FIND_STOPPED);
 	p2p_set_state(p2p, P2P_IDLE);
 	p2p_free_req_dev_types(p2p);
@@ -1166,89 +1189,115 @@
 }
 
 
-static int p2p_prepare_channel(struct p2p_data *p2p, unsigned int force_freq,
-			       unsigned int pref_freq)
+static int p2p_prepare_channel_pref(struct p2p_data *p2p,
+				    unsigned int force_freq,
+				    unsigned int pref_freq)
 {
-	if (force_freq || pref_freq) {
-		u8 op_reg_class, op_channel;
-		unsigned int freq = force_freq ? force_freq : pref_freq;
-		if (p2p_freq_to_channel(p2p->cfg->country, freq,
-					&op_reg_class, &op_channel) < 0) {
-			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
-				"P2P: Unsupported frequency %u MHz",
-				freq);
-			return -1;
-		}
-		if (!p2p_channels_includes(&p2p->cfg->channels, op_reg_class,
-					   op_channel)) {
-			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
-				"P2P: Frequency %u MHz (oper_class %u "
-				"channel %u) not allowed for P2P",
-				freq, op_reg_class, op_channel);
-			return -1;
-		}
-		p2p->op_reg_class = op_reg_class;
-		p2p->op_channel = op_channel;
-		if (force_freq) {
-			p2p->channels.reg_classes = 1;
-			p2p->channels.reg_class[0].channels = 1;
-			p2p->channels.reg_class[0].reg_class =
-				p2p->op_reg_class;
-			p2p->channels.reg_class[0].channel[0] = p2p->op_channel;
-		} else {
-			os_memcpy(&p2p->channels, &p2p->cfg->channels,
-				  sizeof(struct p2p_channels));
-		}
+	u8 op_class, op_channel;
+	unsigned int freq = force_freq ? force_freq : pref_freq;
+
+	if (p2p_freq_to_channel(p2p->cfg->country, freq,
+				&op_class, &op_channel) < 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Unsupported frequency %u MHz", freq);
+		return -1;
+	}
+
+	if (!p2p_channels_includes(&p2p->cfg->channels, op_class, op_channel)) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Frequency %u MHz (oper_class %u channel %u) not "
+			"allowed for P2P", freq, op_class, op_channel);
+		return -1;
+	}
+
+	p2p->op_reg_class = op_class;
+	p2p->op_channel = op_channel;
+
+	if (force_freq) {
+		p2p->channels.reg_classes = 1;
+		p2p->channels.reg_class[0].channels = 1;
+		p2p->channels.reg_class[0].reg_class = p2p->op_reg_class;
+		p2p->channels.reg_class[0].channel[0] = p2p->op_channel;
 	} else {
-		u8 op_reg_class, op_channel;
-
-		if (!p2p->cfg->cfg_op_channel && p2p->best_freq_overall > 0 &&
-		    p2p_supported_freq(p2p, p2p->best_freq_overall) &&
-		    p2p_freq_to_channel(p2p->cfg->country,
-					p2p->best_freq_overall,
-					&op_reg_class, &op_channel) == 0) {
-			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
-				"P2P: Select best overall channel as "
-				"operating channel preference");
-			p2p->op_reg_class = op_reg_class;
-			p2p->op_channel = op_channel;
-		} else if (!p2p->cfg->cfg_op_channel && p2p->best_freq_5 > 0 &&
-			   p2p_supported_freq(p2p, p2p->best_freq_5) &&
-			   p2p_freq_to_channel(p2p->cfg->country,
-					       p2p->best_freq_5,
-					       &op_reg_class, &op_channel) ==
-			   0) {
-			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
-				"P2P: Select best 5 GHz channel as "
-				"operating channel preference");
-			p2p->op_reg_class = op_reg_class;
-			p2p->op_channel = op_channel;
-		} else if (!p2p->cfg->cfg_op_channel &&
-			   p2p->best_freq_24 > 0 &&
-			   p2p_supported_freq(p2p, p2p->best_freq_24) &&
-			   p2p_freq_to_channel(p2p->cfg->country,
-					       p2p->best_freq_24,
-					       &op_reg_class, &op_channel) ==
-			   0) {
-			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
-				"P2P: Select best 2.4 GHz channel as "
-				"operating channel preference");
-			p2p->op_reg_class = op_reg_class;
-			p2p->op_channel = op_channel;
-		} else {
-			p2p->op_reg_class = p2p->cfg->op_reg_class;
-			p2p->op_channel = p2p->cfg->op_channel;
-		}
-
 		os_memcpy(&p2p->channels, &p2p->cfg->channels,
 			  sizeof(struct p2p_channels));
 	}
+
+	return 0;
+}
+
+
+static void p2p_prepare_channel_best(struct p2p_data *p2p)
+{
+	u8 op_class, op_channel;
+
+	if (!p2p->cfg->cfg_op_channel && p2p->best_freq_overall > 0 &&
+	    p2p_supported_freq(p2p, p2p->best_freq_overall) &&
+	    p2p_freq_to_channel(p2p->cfg->country, p2p->best_freq_overall,
+				&op_class, &op_channel) == 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Select best "
+			"overall channel as operating channel preference");
+		p2p->op_reg_class = op_class;
+		p2p->op_channel = op_channel;
+	} else if (!p2p->cfg->cfg_op_channel && p2p->best_freq_5 > 0 &&
+		   p2p_supported_freq(p2p, p2p->best_freq_5) &&
+		   p2p_freq_to_channel(p2p->cfg->country, p2p->best_freq_5,
+				       &op_class, &op_channel) == 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Select best 5 GHz "
+			"channel as operating channel preference");
+		p2p->op_reg_class = op_class;
+		p2p->op_channel = op_channel;
+	} else if (!p2p->cfg->cfg_op_channel && p2p->best_freq_24 > 0 &&
+		   p2p_supported_freq(p2p, p2p->best_freq_24) &&
+		   p2p_freq_to_channel(p2p->cfg->country, p2p->best_freq_24,
+				       &op_class, &op_channel) == 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Select best 2.4 "
+			"GHz channel as operating channel preference");
+		p2p->op_reg_class = op_class;
+		p2p->op_channel = op_channel;
+	} else {
+		p2p->op_reg_class = p2p->cfg->op_reg_class;
+		p2p->op_channel = p2p->cfg->op_channel;
+	}
+
+	os_memcpy(&p2p->channels, &p2p->cfg->channels,
+		  sizeof(struct p2p_channels));
+}
+
+
+/**
+ * p2p_prepare_channel - Select operating channel for GO Negotiation
+ * @p2p: P2P module context from p2p_init()
+ * @dev: Selected peer device
+ * @force_freq: Forced frequency in MHz or 0 if not forced
+ * @pref_freq: Preferred frequency in MHz or 0 if no preference
+ * Returns: 0 on success, -1 on failure (channel not supported for P2P)
+ *
+ * This function is used to do initial operating channel selection for GO
+ * Negotiation prior to having received peer information. The selected channel
+ * may be further optimized in p2p_reselect_channel() once the peer information
+ * is available.
+ */
+static int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev,
+			       unsigned int force_freq, unsigned int pref_freq)
+{
+	if (force_freq || pref_freq) {
+		if (p2p_prepare_channel_pref(p2p, force_freq, pref_freq) < 0)
+			return -1;
+	} else {
+		p2p_prepare_channel_best(p2p);
+	}
 	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
 		"P2P: Own preference for operation channel: "
 		"Operating Class %u Channel %u%s",
 		p2p->op_reg_class, p2p->op_channel,
 		force_freq ? " (forced)" : "");
 
+	if (force_freq)
+		dev->flags |= P2P_DEV_FORCE_FREQ;
+	else
+		dev->flags &= ~P2P_DEV_FORCE_FREQ;
+
 	return 0;
 }
 
@@ -1289,9 +1338,6 @@
 		MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr),
 		wps_method, persistent_group, pd_before_go_neg);
 
-	if (p2p_prepare_channel(p2p, force_freq, pref_freq) < 0)
-		return -1;
-
 	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,
@@ -1300,6 +1346,9 @@
 		return -1;
 	}
 
+	if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq) < 0)
+		return -1;
+
 	if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) {
 		if (!(dev->info.dev_capab &
 		      P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
@@ -1339,8 +1388,16 @@
 	dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM;
 	if (pd_before_go_neg)
 		dev->flags |= P2P_DEV_PD_BEFORE_GO_NEG;
-	else
+	else {
 		dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG;
+		/*
+		 * Assign dialog token here to use the same value in each
+		 * retry within the same GO Negotiation exchange.
+		 */
+		dev->dialog_token++;
+		if (dev->dialog_token == 0)
+			dev->dialog_token = 1;
+	}
 	dev->connect_reqs = 0;
 	dev->go_neg_req_sent = 0;
 	dev->go_state = UNKNOWN_GO;
@@ -1367,11 +1424,6 @@
 	dev->wps_method = wps_method;
 	dev->status = P2P_SC_SUCCESS;
 
-	if (force_freq)
-		dev->flags |= P2P_DEV_FORCE_FREQ;
-	else
-		dev->flags &= ~P2P_DEV_FORCE_FREQ;
-
 	if (p2p->p2p_scan_running) {
 		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
 			"P2P: p2p_scan running - delay connect send");
@@ -1401,9 +1453,6 @@
 		MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr),
 		wps_method, persistent_group);
 
-	if (p2p_prepare_channel(p2p, force_freq, pref_freq) < 0)
-		return -1;
-
 	dev = p2p_get_device(p2p, peer_addr);
 	if (dev == NULL) {
 		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
@@ -1412,6 +1461,9 @@
 		return -1;
 	}
 
+	if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq) < 0)
+		return -1;
+
 	p2p->ssid_set = 0;
 	if (force_ssid) {
 		wpa_hexdump_ascii(MSG_DEBUG, "P2P: Forced SSID",
@@ -1432,11 +1484,6 @@
 	dev->wps_method = wps_method;
 	dev->status = P2P_SC_SUCCESS;
 
-	if (force_freq)
-		dev->flags |= P2P_DEV_FORCE_FREQ;
-	else
-		dev->flags &= ~P2P_DEV_FORCE_FREQ;
-
 	return 0;
 }
 
@@ -2840,9 +2887,10 @@
 
 
 int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq,
-			 int level, const u8 *ies, size_t ies_len)
+			 unsigned int age, int level, const u8 *ies,
+			 size_t ies_len)
 {
-	p2p_add_device(p2p, bssid, freq, level, ies, ies_len, 1);
+	p2p_add_device(p2p, bssid, freq, age, level, ies, ies_len, 1);
 
 	return 0;
 }
diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h
index 045e6f7..18e733b 100644
--- a/src/p2p/p2p.h
+++ b/src/p2p/p2p.h
@@ -1200,6 +1200,7 @@
  * @p2p: P2P module context from p2p_init()
  * @bssid: BSSID of the scan result
  * @freq: Frequency of the channel on which the device was found in MHz
+ * @age: Age of the scan result in milliseconds
  * @level: Signal level (signal strength of the received Beacon/Probe Response
  *	frame)
  * @ies: Pointer to IEs from the scan result
@@ -1221,7 +1222,8 @@
  * start of a pending operation, e.g., to start a pending GO negotiation.
  */
 int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq,
-			 int level, const u8 *ies, size_t ies_len);
+			 unsigned int age, int level, const u8 *ies,
+			 size_t ies_len);
 
 /**
  * p2p_scan_res_handled - Indicate end of scan results
diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c
index fe4b560..37d43bb 100644
--- a/src/p2p/p2p_go_neg.c
+++ b/src/p2p/p2p_go_neg.c
@@ -145,9 +145,6 @@
 	if (buf == NULL)
 		return NULL;
 
-	peer->dialog_token++;
-	if (peer->dialog_token == 0)
-		peer->dialog_token = 1;
 	p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_REQ, peer->dialog_token);
 
 	len = p2p_buf_add_ie_hdr(buf);
@@ -336,6 +333,17 @@
 }
 
 
+/**
+ * p2p_reselect_channel - Re-select operating channel based on peer information
+ * @p2p: P2P module context from p2p_init()
+ * @intersection: Support channel list intersection from local and peer
+ *
+ * This function is used to re-select the best channel after having received
+ * information from the peer to allow supported channel lists to be intersected.
+ * This can be used to improve initial channel selection done in
+ * p2p_prepare_channel() prior to the start of GO Negotiation. In addition, this
+ * can be used for Invitation case.
+ */
 void p2p_reselect_channel(struct p2p_data *p2p,
 			  struct p2p_channels *intersection)
 {
@@ -390,6 +398,35 @@
 		}
 	}
 
+	/* Try a channel where we might be able to use HT40 */
+	for (i = 0; i < intersection->reg_classes; i++) {
+		struct p2p_reg_class *c = &intersection->reg_class[i];
+		if (c->reg_class == 116 || c->reg_class == 117 ||
+		    c->reg_class == 126 || c->reg_class == 127) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: Pick possible HT40 channel (reg_class "
+				"%u channel %u) from intersection",
+				c->reg_class, c->channel[0]);
+			p2p->op_reg_class = c->reg_class;
+			p2p->op_channel = c->channel[0];
+			return;
+		}
+	}
+
+	/*
+	 * Try to see if the original channel is in the intersection. If
+	 * so, no need to change anything, as it already contains some
+	 * randomness.
+	 */
+	if (p2p_channels_includes(intersection, p2p->op_reg_class,
+				  p2p->op_channel)) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Using original operating class and channel "
+			"(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.
@@ -403,6 +440,60 @@
 }
 
 
+static int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev,
+				 u8 *status)
+{
+	struct p2p_channels intersection;
+	size_t i;
+
+	p2p_channels_intersect(&p2p->channels, &dev->channels, &intersection);
+	if (intersection.reg_classes == 0 ||
+	    intersection.reg_class[0].channels == 0) {
+		*status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: No common channels found");
+		return -1;
+	}
+
+	for (i = 0; i < intersection.reg_classes; i++) {
+		struct p2p_reg_class *c;
+		c = &intersection.reg_class[i];
+		wpa_printf(MSG_DEBUG, "P2P: reg_class %u", c->reg_class);
+		wpa_hexdump(MSG_DEBUG, "P2P: channels",
+			    c->channel, c->channels);
+	}
+
+	if (!p2p_channels_includes(&intersection, p2p->op_reg_class,
+				   p2p->op_channel)) {
+		if (dev->flags & P2P_DEV_FORCE_FREQ) {
+			*status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer does "
+				"not support the forced channel");
+			return -1;
+		}
+
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Selected operating "
+			"channel (op_class %u channel %u) not acceptable to "
+			"the peer", p2p->op_reg_class, p2p->op_channel);
+		p2p_reselect_channel(p2p, &intersection);
+	} else if (!(dev->flags & P2P_DEV_FORCE_FREQ) &&
+		   !p2p->cfg->cfg_op_channel) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Try to optimize "
+			"channel selection with peer information received; "
+			"previously selected op_class %u channel %u",
+			p2p->op_reg_class, p2p->op_channel);
+		p2p_reselect_channel(p2p, &intersection);
+	}
+
+	if (!p2p->ssid_set) {
+		p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len);
+		p2p->ssid_set = 1;
+	}
+
+	return 0;
+}
+
+
 void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
 			    const u8 *data, size_t len, int rx_freq)
 {
@@ -619,36 +710,8 @@
 			goto fail;
 		}
 
-		if (go) {
-			struct p2p_channels intersection;
-			size_t i;
-			p2p_channels_intersect(&p2p->channels, &dev->channels,
-					       &intersection);
-			if (intersection.reg_classes == 0 ||
-			    intersection.reg_class[0].channels == 0) {
-				status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
-				wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
-					"P2P: No common channels found");
-				goto fail;
-			}
-			for (i = 0; i < intersection.reg_classes; i++) {
-				struct p2p_reg_class *c;
-				c = &intersection.reg_class[i];
-				wpa_printf(MSG_DEBUG, "P2P: reg_class %u",
-					   c->reg_class);
-				wpa_hexdump(MSG_DEBUG, "P2P: channels",
-					    c->channel, c->channels);
-			}
-			if (!p2p_channels_includes(&intersection,
-						   p2p->op_reg_class,
-						   p2p->op_channel))
-				p2p_reselect_channel(p2p, &intersection);
-
-			if (!p2p->ssid_set) {
-				p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len);
-				p2p->ssid_set = 1;
-			}
-		}
+		if (go && p2p_go_select_channel(p2p, dev, &status) < 0)
+			goto fail;
 
 		dev->go_state = go ? LOCAL_GO : REMOTE_GO;
 		dev->oper_freq = p2p_channel_to_freq((const char *)
@@ -1021,35 +1084,8 @@
 		goto fail;
 	}
 
-	if (go) {
-		struct p2p_channels intersection;
-		size_t i;
-		p2p_channels_intersect(&p2p->channels, &dev->channels,
-				       &intersection);
-		if (intersection.reg_classes == 0 ||
-		    intersection.reg_class[0].channels == 0) {
-			status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
-			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
-				"P2P: No common channels found");
-			goto fail;
-		}
-		for (i = 0; i < intersection.reg_classes; i++) {
-			struct p2p_reg_class *c;
-			c = &intersection.reg_class[i];
-			wpa_printf(MSG_DEBUG, "P2P: reg_class %u",
-				   c->reg_class);
-			wpa_hexdump(MSG_DEBUG, "P2P: channels",
-				    c->channel, c->channels);
-		}
-		if (!p2p_channels_includes(&intersection, p2p->op_reg_class,
-					   p2p->op_channel))
-			p2p_reselect_channel(p2p, &intersection);
-
-		if (!p2p->ssid_set) {
-			p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len);
-			p2p->ssid_set = 1;
-		}
-	}
+	if (go && p2p_go_select_channel(p2p, dev, &status) < 0)
+		goto fail;
 
 	p2p_set_state(p2p, P2P_GO_NEG);
 	p2p_clear_timeout(p2p);
diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c
index 8687320..633dd5c 100644
--- a/src/p2p/p2p_group.c
+++ b/src/p2p/p2p_group.c
@@ -169,6 +169,39 @@
 }
 
 
+static struct wpabuf * p2p_group_encaps_probe_resp(struct wpabuf *subelems)
+{
+	struct wpabuf *ie;
+	const u8 *pos, *end;
+	size_t len;
+
+	if (subelems == NULL)
+		return NULL;
+
+	len = wpabuf_len(subelems) + 100;
+
+	ie = wpabuf_alloc(len);
+	if (ie == NULL)
+		return NULL;
+
+	pos = wpabuf_head(subelems);
+	end = pos + wpabuf_len(subelems);
+
+	while (end > pos) {
+		size_t frag_len = end - pos;
+		if (frag_len > 251)
+			frag_len = 251;
+		wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
+		wpabuf_put_u8(ie, 4 + frag_len);
+		wpabuf_put_be32(ie, P2P_IE_VENDOR_TYPE);
+		wpabuf_put_data(ie, pos, frag_len);
+		pos += frag_len;
+	}
+
+	return ie;
+}
+
+
 static struct wpabuf * p2p_group_build_beacon_ie(struct p2p_group *group)
 {
 	struct wpabuf *ie;
@@ -367,9 +400,8 @@
 static struct wpabuf * p2p_group_build_probe_resp_ie(struct p2p_group *group)
 {
 	u8 *group_info;
-	struct wpabuf *ie;
+	struct wpabuf *p2p_subelems, *ie;
 	struct p2p_group_member *m;
-	u8 *len;
 	size_t extra = 0;
 
 #ifdef CONFIG_WIFI_DISPLAY
@@ -377,33 +409,32 @@
 		extra += wpabuf_len(group->wfd_ie);
 #endif /* CONFIG_WIFI_DISPLAY */
 
-	ie = wpabuf_alloc(257 + extra);
-	if (ie == NULL)
+	p2p_subelems = wpabuf_alloc(500 + extra);
+	if (p2p_subelems == NULL)
 		return NULL;
 
 #ifdef CONFIG_WIFI_DISPLAY
 	if (group->wfd_ie)
-		wpabuf_put_buf(ie, group->wfd_ie);
+		wpabuf_put_buf(p2p_subelems, group->wfd_ie);
 #endif /* CONFIG_WIFI_DISPLAY */
 
-	len = p2p_buf_add_ie_hdr(ie);
-
-	p2p_group_add_common_ies(group, ie);
-	p2p_group_add_noa(ie, group->noa);
+	p2p_group_add_common_ies(group, p2p_subelems);
+	p2p_group_add_noa(p2p_subelems, group->noa);
 
 	/* P2P Device Info */
-	p2p_buf_add_device_info(ie, group->p2p, NULL);
+	p2p_buf_add_device_info(p2p_subelems, group->p2p, NULL);
 
 	/* P2P Group Info */
-	group_info = wpabuf_put(ie, 0);
-	wpabuf_put_u8(ie, P2P_ATTR_GROUP_INFO);
-	wpabuf_put_le16(ie, 0); /* Length to be filled */
+	group_info = wpabuf_put(p2p_subelems, 0);
+	wpabuf_put_u8(p2p_subelems, P2P_ATTR_GROUP_INFO);
+	wpabuf_put_le16(p2p_subelems, 0); /* Length to be filled */
 	for (m = group->members; m; m = m->next)
-		p2p_client_info(ie, m);
+		p2p_client_info(p2p_subelems, m);
 	WPA_PUT_LE16(group_info + 1,
-		     (u8 *) wpabuf_put(ie, 0) - group_info - 3);
+		     (u8 *) wpabuf_put(p2p_subelems, 0) - group_info - 3);
 
-	p2p_buf_update_ie_hdr(ie, len);
+	ie = p2p_group_encaps_probe_resp(p2p_subelems);
+	wpabuf_free(p2p_subelems);
 
 	return ie;
 }
diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h
index d2868fb..712544b 100644
--- a/src/p2p/p2p_i.h
+++ b/src/p2p/p2p_i.h
@@ -705,8 +705,9 @@
 						struct p2p_message *msg);
 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, int scan_res);
+int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
+		   unsigned int age_ms, int level, 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);
diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c
index 7bf6600..ac67932 100644
--- a/src/p2p/p2p_invitation.c
+++ b/src/p2p/p2p_invitation.c
@@ -176,8 +176,8 @@
 			"P2P: Invitation Request from unknown peer "
 			MACSTR, MAC2STR(sa));
 
-		if (p2p_add_device(p2p, sa, rx_freq, 0, data + 1, len - 1, 0))
-		{
+		if (p2p_add_device(p2p, sa, rx_freq, 0, 0, data + 1, len - 1,
+				   0)) {
 			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
 				"P2P: Invitation Request add device failed "
 				MACSTR, MAC2STR(sa));
diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c
index e40f2b7..ca33f17 100644
--- a/src/p2p/p2p_pd.c
+++ b/src/p2p/p2p_pd.c
@@ -151,8 +151,9 @@
 		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, 0))
-		{
+
+		if (p2p_add_device(p2p, sa, rx_freq, 0, 0, data + 1, len - 1,
+				   0)) {
 			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
 			        "P2P: Provision Discovery Request add device "
 				"failed " MACSTR, MAC2STR(sa));
@@ -379,9 +380,6 @@
 		/* TODO: use device discoverability request through GO */
 	}
 
-	dev->dialog_token++;
-	if (dev->dialog_token == 0)
-		dev->dialog_token = 1;
 	req = p2p_build_prov_disc_req(p2p, dev->dialog_token,
 				      dev->req_config_methods,
 				      join ? dev : NULL);
@@ -452,6 +450,14 @@
 	if (p2p->user_initiated_pd)
 		p2p->pd_retries = MAX_PROV_DISC_REQ_RETRIES;
 
+	/*
+	 * Assign dialog token here to use the same value in each retry within
+	 * the same PD exchange.
+	 */
+	dev->dialog_token++;
+	if (dev->dialog_token == 0)
+		dev->dialog_token = 1;
+
 	return p2p_send_prov_disc_req(p2p, dev, join, force_freq);
 }
 
diff --git a/src/rsn_supp/peerkey.c b/src/rsn_supp/peerkey.c
index f2bac34..789ac25 100644
--- a/src/rsn_supp/peerkey.c
+++ b/src/rsn_supp/peerkey.c
@@ -217,23 +217,17 @@
 		return -1;
 	}
 
-	cipher = ie.pairwise_cipher & sm->allowed_pairwise_cipher;
-	if (cipher & WPA_CIPHER_CCMP) {
-		wpa_printf(MSG_DEBUG, "RSN: Using CCMP for PeerKey");
-		cipher = WPA_CIPHER_CCMP;
-	} else if (cipher & WPA_CIPHER_GCMP) {
-		wpa_printf(MSG_DEBUG, "RSN: Using GCMP for PeerKey");
-		cipher = WPA_CIPHER_GCMP;
-	} else if (cipher & WPA_CIPHER_TKIP) {
-		wpa_printf(MSG_DEBUG, "RSN: Using TKIP for PeerKey");
-		cipher = WPA_CIPHER_TKIP;
-	} else {
+	cipher = wpa_pick_pairwise_cipher(ie.pairwise_cipher &
+					  sm->allowed_pairwise_cipher, 0);
+	if (cipher < 0) {
 		wpa_printf(MSG_INFO, "RSN: No acceptable cipher in SMK M2");
 		wpa_supplicant_send_smk_error(sm, src_addr, kde.mac_addr,
 					      STK_MUI_SMK, STK_ERR_CPHR_NS,
 					      ver);
 		return -1;
 	}
+	wpa_printf(MSG_DEBUG, "RSN: Using %s for PeerKey",
+		   wpa_cipher_txt(cipher));
 
 	/* TODO: find existing entry and if found, use that instead of adding
 	 * a new one; how to handle the case where both ends initiate at the
@@ -496,17 +490,9 @@
 	peerkey->rsnie_p_len = kde->rsn_ie_len;
 	os_memcpy(peerkey->pnonce, kde->nonce, WPA_NONCE_LEN);
 
-	cipher = ie.pairwise_cipher & sm->allowed_pairwise_cipher;
-	if (cipher & WPA_CIPHER_CCMP) {
-		wpa_printf(MSG_DEBUG, "RSN: Using CCMP for PeerKey");
-		peerkey->cipher = WPA_CIPHER_CCMP;
-	} else if (cipher & WPA_CIPHER_GCMP) {
-		wpa_printf(MSG_DEBUG, "RSN: Using GCMP for PeerKey");
-		peerkey->cipher = WPA_CIPHER_GCMP;
-	} else if (cipher & WPA_CIPHER_TKIP) {
-		wpa_printf(MSG_DEBUG, "RSN: Using TKIP for PeerKey");
-		peerkey->cipher = WPA_CIPHER_TKIP;
-	} else {
+	cipher = wpa_pick_pairwise_cipher(ie.pairwise_cipher &
+					  sm->allowed_pairwise_cipher, 0);
+	if (cipher < 0) {
 		wpa_printf(MSG_INFO, "RSN: SMK Peer STA " MACSTR " selected "
 			   "unacceptable cipher", MAC2STR(kde->mac_addr));
 		wpa_supplicant_send_smk_error(sm, src_addr, kde->mac_addr,
@@ -515,6 +501,9 @@
 		/* TODO: abort negotiation */
 		return -1;
 	}
+	wpa_printf(MSG_DEBUG, "RSN: Using %s for PeerKey",
+		   wpa_cipher_txt(cipher));
+	peerkey->cipher = cipher;
 
 	return 0;
 }
diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c
index 7646ca8..c38fada 100644
--- a/src/rsn_supp/tdls.c
+++ b/src/rsn_supp/tdls.c
@@ -1309,21 +1309,10 @@
 		wpa_printf(MSG_DEBUG, "TDLS: No supported rates received");
 		return -1;
 	}
-
-	peer->supp_rates_len = kde->supp_rates_len - 2;
-	if (peer->supp_rates_len > IEEE80211_MAX_SUPP_RATES)
-		peer->supp_rates_len = IEEE80211_MAX_SUPP_RATES;
-	os_memcpy(peer->supp_rates, kde->supp_rates + 2, peer->supp_rates_len);
-
-	if (kde->ext_supp_rates) {
-		int clen = kde->ext_supp_rates_len - 2;
-		if (peer->supp_rates_len + clen > IEEE80211_MAX_SUPP_RATES)
-			clen = IEEE80211_MAX_SUPP_RATES - peer->supp_rates_len;
-		os_memcpy(peer->supp_rates + peer->supp_rates_len,
-			  kde->ext_supp_rates + 2, clen);
-		peer->supp_rates_len += clen;
-	}
-
+	peer->supp_rates_len = merge_byte_arrays(
+		peer->supp_rates, sizeof(peer->supp_rates),
+		kde->supp_rates + 2, kde->supp_rates_len - 2,
+		kde->ext_supp_rates + 2, kde->ext_supp_rates_len - 2);
 	return 0;
 }
 
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 3c45f3a..e50404c 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -2627,7 +2627,7 @@
 }
 
 
-#ifdef CONFIG_IEEE80211V
+#ifdef CONFIG_WNM
 int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf)
 {
 	struct wpa_gtk_data gd;
@@ -2637,7 +2637,6 @@
 #endif /* CONFIG_IEEE80211W */
 	u16 keyinfo;
 	u8 keylen;  /* plaintext key len */
-	u8 keydatalen;
 	u8 *key_rsc;
 
 	os_memset(&gd, 0, sizeof(gd));
@@ -2655,8 +2654,7 @@
 
 	if (subelem_id == WNM_SLEEP_SUBELEM_GTK) {
 		key_rsc = buf + 5;
-		keyinfo = WPA_GET_LE16(buf+2);
-		keydatalen = buf[1] - 11 - 8;
+		keyinfo = WPA_GET_LE16(buf + 2);
 		gd.gtk_len = keylen;
 		if (gd.gtk_len != buf[4]) {
 			wpa_printf(MSG_DEBUG, "GTK len mismatch len %d vs %d",
@@ -2667,18 +2665,7 @@
 		gd.tx = wpa_supplicant_gtk_tx_bit_workaround(
 		         sm, !!(keyinfo & WPA_KEY_INFO_TXRX));
 
-		if (keydatalen % 8) {
-			wpa_printf(MSG_DEBUG, "WPA: Unsupported AES-WRAP len "
-				   "%d", keydatalen);
-			return -1;
-		}
-
-		if (aes_unwrap(sm->ptk.kek, keydatalen / 8, buf + 13, gd.gtk))
-		{
-			wpa_printf(MSG_WARNING, "WNM: AES unwrap failed - "
-				   "could not decrypt GTK");
-			return -1;
-		}
+		os_memcpy(gd.gtk, buf + 13, gd.gtk_len);
 
 		wpa_hexdump_key(MSG_DEBUG, "Install GTK (WNM SLEEP)",
 				gd.gtk, gd.gtk_len);
@@ -2689,22 +2676,11 @@
 		}
 #ifdef CONFIG_IEEE80211W
 	} else if (subelem_id == WNM_SLEEP_SUBELEM_IGTK) {
-		if (buf[1] != 2 + 6 + WPA_IGTK_LEN + 8) {
-			wpa_printf(MSG_DEBUG, "WPA: Unsupported AES-WRAP len "
-				   "%d", buf[1] - 2 - 6 - 8);
-			return -1;
-		}
 		os_memcpy(igd.keyid, buf + 2, 2);
 		os_memcpy(igd.pn, buf + 4, 6);
 
 		keyidx = WPA_GET_LE16(igd.keyid);
-
-		if (aes_unwrap(sm->ptk.kek, WPA_IGTK_LEN / 8, buf + 10,
-			       igd.igtk)) {
-			wpa_printf(MSG_WARNING, "WNM: AES unwrap failed - "
-				   "could not decrypr IGTK");
-			return -1;
-		}
+		os_memcpy(igd.igtk, buf + 10, WPA_IGTK_LEN);
 
 		wpa_hexdump_key(MSG_DEBUG, "Install IGTK (WNM SLEEP)",
 				igd.igtk, WPA_IGTK_LEN);
@@ -2723,4 +2699,4 @@
 
 	return 0;
 }
-#endif /* CONFIG_IEEE80211V */
+#endif /* CONFIG_WNM */
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index 8afdf39..791974c 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -365,8 +365,6 @@
 void wpa_tdls_disable_link(struct wpa_sm *sm, const u8 *addr);
 int wpa_tdls_is_external_setup(struct wpa_sm *sm);
 
-#ifdef CONFIG_IEEE80211V
 int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf);
-#endif /* CONFIG_IEEE80211V */
 
 #endif /* WPA_H */
diff --git a/src/utils/common.h b/src/utils/common.h
index 5fc916c..a859042 100644
--- a/src/utils/common.h
+++ b/src/utils/common.h
@@ -224,69 +224,105 @@
 
 /* Macros for handling unaligned memory accesses */
 
-#define WPA_GET_BE16(a) ((u16) (((a)[0] << 8) | (a)[1]))
-#define WPA_PUT_BE16(a, val)			\
-	do {					\
-		(a)[0] = ((u16) (val)) >> 8;	\
-		(a)[1] = ((u16) (val)) & 0xff;	\
-	} while (0)
+static inline u16 WPA_GET_BE16(const u8 *a)
+{
+	return (a[0] << 8) | a[1];
+}
 
-#define WPA_GET_LE16(a) ((u16) (((a)[1] << 8) | (a)[0]))
-#define WPA_PUT_LE16(a, val)			\
-	do {					\
-		(a)[1] = ((u16) (val)) >> 8;	\
-		(a)[0] = ((u16) (val)) & 0xff;	\
-	} while (0)
+static inline void WPA_PUT_BE16(u8 *a, u16 val)
+{
+	a[0] = val >> 8;
+	a[1] = val & 0xff;
+}
 
-#define WPA_GET_BE24(a) ((((u32) (a)[0]) << 16) | (((u32) (a)[1]) << 8) | \
-			 ((u32) (a)[2]))
-#define WPA_PUT_BE24(a, val)					\
-	do {							\
-		(a)[0] = (u8) ((((u32) (val)) >> 16) & 0xff);	\
-		(a)[1] = (u8) ((((u32) (val)) >> 8) & 0xff);	\
-		(a)[2] = (u8) (((u32) (val)) & 0xff);		\
-	} while (0)
+static inline u16 WPA_GET_LE16(const u8 *a)
+{
+	return (a[1] << 8) | a[0];
+}
 
-#define WPA_GET_BE32(a) ((((u32) (a)[0]) << 24) | (((u32) (a)[1]) << 16) | \
-			 (((u32) (a)[2]) << 8) | ((u32) (a)[3]))
-#define WPA_PUT_BE32(a, val)					\
-	do {							\
-		(a)[0] = (u8) ((((u32) (val)) >> 24) & 0xff);	\
-		(a)[1] = (u8) ((((u32) (val)) >> 16) & 0xff);	\
-		(a)[2] = (u8) ((((u32) (val)) >> 8) & 0xff);	\
-		(a)[3] = (u8) (((u32) (val)) & 0xff);		\
-	} while (0)
+static inline void WPA_PUT_LE16(u8 *a, u16 val)
+{
+	a[1] = val >> 8;
+	a[0] = val & 0xff;
+}
 
-#define WPA_GET_LE32(a) ((((u32) (a)[3]) << 24) | (((u32) (a)[2]) << 16) | \
-			 (((u32) (a)[1]) << 8) | ((u32) (a)[0]))
-#define WPA_PUT_LE32(a, val)					\
-	do {							\
-		(a)[3] = (u8) ((((u32) (val)) >> 24) & 0xff);	\
-		(a)[2] = (u8) ((((u32) (val)) >> 16) & 0xff);	\
-		(a)[1] = (u8) ((((u32) (val)) >> 8) & 0xff);	\
-		(a)[0] = (u8) (((u32) (val)) & 0xff);		\
-	} while (0)
+static inline u32 WPA_GET_BE24(const u8 *a)
+{
+	return (a[0] << 16) | (a[1] << 8) | a[2];
+}
 
-#define WPA_GET_BE64(a) ((((u64) (a)[0]) << 56) | (((u64) (a)[1]) << 48) | \
-			 (((u64) (a)[2]) << 40) | (((u64) (a)[3]) << 32) | \
-			 (((u64) (a)[4]) << 24) | (((u64) (a)[5]) << 16) | \
-			 (((u64) (a)[6]) << 8) | ((u64) (a)[7]))
-#define WPA_PUT_BE64(a, val)				\
-	do {						\
-		(a)[0] = (u8) (((u64) (val)) >> 56);	\
-		(a)[1] = (u8) (((u64) (val)) >> 48);	\
-		(a)[2] = (u8) (((u64) (val)) >> 40);	\
-		(a)[3] = (u8) (((u64) (val)) >> 32);	\
-		(a)[4] = (u8) (((u64) (val)) >> 24);	\
-		(a)[5] = (u8) (((u64) (val)) >> 16);	\
-		(a)[6] = (u8) (((u64) (val)) >> 8);	\
-		(a)[7] = (u8) (((u64) (val)) & 0xff);	\
-	} while (0)
+static inline void WPA_PUT_BE24(u8 *a, u32 val)
+{
+	a[0] = (val >> 16) & 0xff;
+	a[1] = (val >> 8) & 0xff;
+	a[2] = val & 0xff;
+}
 
-#define WPA_GET_LE64(a) ((((u64) (a)[7]) << 56) | (((u64) (a)[6]) << 48) | \
-			 (((u64) (a)[5]) << 40) | (((u64) (a)[4]) << 32) | \
-			 (((u64) (a)[3]) << 24) | (((u64) (a)[2]) << 16) | \
-			 (((u64) (a)[1]) << 8) | ((u64) (a)[0]))
+static inline u32 WPA_GET_BE32(const u8 *a)
+{
+	return (a[0] << 24) | (a[1] << 16) | (a[2] << 8) | a[3];
+}
+
+static inline void WPA_PUT_BE32(u8 *a, u32 val)
+{
+	a[0] = (val >> 24) & 0xff;
+	a[1] = (val >> 16) & 0xff;
+	a[2] = (val >> 8) & 0xff;
+	a[3] = val & 0xff;
+}
+
+static inline u32 WPA_GET_LE32(const u8 *a)
+{
+	return (a[3] << 24) | (a[2] << 16) | (a[1] << 8) | a[0];
+}
+
+static inline void WPA_PUT_LE32(u8 *a, u32 val)
+{
+	a[3] = (val >> 24) & 0xff;
+	a[2] = (val >> 16) & 0xff;
+	a[1] = (val >> 8) & 0xff;
+	a[0] = val & 0xff;
+}
+
+static inline u64 WPA_GET_BE64(const u8 *a)
+{
+	return (((u64) a[0]) << 56) | (((u64) a[1]) << 48) |
+		(((u64) a[2]) << 40) | (((u64) a[3]) << 32) |
+		(((u64) a[4]) << 24) | (((u64) a[5]) << 16) |
+		(((u64) a[6]) << 8) | ((u64) a[7]);
+}
+
+static inline void WPA_PUT_BE64(u8 *a, u64 val)
+{
+	a[0] = val >> 56;
+	a[1] = val >> 48;
+	a[2] = val >> 40;
+	a[3] = val >> 32;
+	a[4] = val >> 24;
+	a[5] = val >> 16;
+	a[6] = val >> 8;
+	a[7] = val & 0xff;
+}
+
+static inline u64 WPA_GET_LE64(const u8 *a)
+{
+	return (((u64) a[7]) << 56) | (((u64) a[6]) << 48) |
+		(((u64) a[5]) << 40) | (((u64) a[4]) << 32) |
+		(((u64) a[3]) << 24) | (((u64) a[2]) << 16) |
+		(((u64) a[1]) << 8) | ((u64) a[0]);
+}
+
+static inline void WPA_PUT_LE64(u8 *a, u64 val)
+{
+	a[7] = val >> 56;
+	a[6] = val >> 48;
+	a[5] = val >> 40;
+	a[4] = val >> 32;
+	a[3] = val >> 24;
+	a[2] = val >> 16;
+	a[1] = val >> 8;
+	a[0] = val & 0xff;
+}
 
 
 #ifndef ETH_ALEN
diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c
index b24836e..11e7e84 100644
--- a/src/wps/wps_registrar.c
+++ b/src/wps/wps_registrar.c
@@ -1298,7 +1298,7 @@
 	    wps_build_uuid_e(probe, reg->wps->uuid) ||
 	    wps_build_device_attrs(&reg->wps->dev, probe) ||
 	    wps_build_probe_config_methods(reg, probe) ||
-	    wps_build_rf_bands(&reg->wps->dev, probe) ||
+	    (reg->dualband && wps_build_rf_bands(&reg->wps->dev, probe)) ||
 	    wps_build_wfa_ext(probe, 0, auth_macs, count) ||
 	    wps_build_vendor_ext(&reg->wps->dev, probe)) {
 		wpabuf_free(beacon);
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index 0ebe607..7545ab2 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -195,10 +195,13 @@
 
 ifdef CONFIG_SAE
 L_CFLAGS += -DCONFIG_SAE
+OBJS += src/common/sae.c
+NEED_ECC=y
+NEED_DH_GROUPS=y
 endif
 
-ifdef CONFIG_IEEE80211V
-L_CFLAGS += -DCONFIG_IEEE80211V
+ifdef CONFIG_WNM
+L_CFLAGS += -DCONFIG_WNM
 OBJS += wnm_sta.c
 endif
 
@@ -582,7 +585,7 @@
 ifdef CONFIG_EAP_PWD
 L_CFLAGS += -DEAP_PWD
 OBJS += src/eap_peer/eap_pwd.c src/eap_common/eap_pwd_common.c
-OBJS_h += src/eap_server/eap_pwd.c
+OBJS_h += src/eap_server/eap_server_pwd.c
 CONFIG_IEEE8021X_EAPOL=y
 NEED_SHA256=y
 endif
@@ -747,6 +750,9 @@
 ifdef CONFIG_IEEE80211N
 OBJS += src/ap/ieee802_11_ht.c
 endif
+ifdef CONFIG_WNM
+OBJS += src/ap/wnm_ap.c
+endif
 ifdef CONFIG_CTRL_IFACE
 OBJS += src/ap/ctrl_iface_ap.c
 endif
@@ -760,10 +766,6 @@
 L_CFLAGS += -DCONFIG_IEEE80211N
 endif
 
-ifdef CONFIG_WNM
-L_CFLAGS += -DCONFIG_WNM
-endif
-
 ifdef NEED_AP_MLME
 OBJS += src/ap/wmm.c
 OBJS += src/ap/ap_list.c
@@ -882,6 +884,8 @@
 OBJS_h += src/eap_server/eap_server_tls_common.c
 ifndef CONFIG_FIPS
 NEED_TLS_PRF=y
+NEED_SHA1=y
+NEED_MD5=y
 endif
 endif
 
@@ -1144,10 +1148,7 @@
 endif
 endif
 
-MD5OBJS =
-ifndef CONFIG_FIPS
-MD5OBJS += src/crypto/md5.c
-endif
+MD5OBJS = src/crypto/md5.c
 ifdef NEED_MD5
 ifdef CONFIG_INTERNAL_MD5
 MD5OBJS += src/crypto/md5-internal.c
@@ -1203,6 +1204,10 @@
 endif
 endif
 
+ifdef NEED_ECC
+L_CFLAGS += -DCONFIG_ECC
+endif
+
 ifdef CONFIG_NO_RANDOM_POOL
 L_CFLAGS += -DCONFIG_NO_RANDOM_POOL
 else
diff --git a/wpa_supplicant/ChangeLog b/wpa_supplicant/ChangeLog
index 6a5ab17..3f10e11 100644
--- a/wpa_supplicant/ChangeLog
+++ b/wpa_supplicant/ChangeLog
@@ -1,6 +1,10 @@
 ChangeLog for wpa_supplicant
 
-????-??-?? - v2.0
+????-??-?? - v2.1
+	* added support for simulataneous authentication of equals (SAE) for
+	  stronger password-based authentication with WPA2-Personal
+
+2013-01-12 - v2.0
 	* removed Qt3-based wpa_gui (obsoleted by wpa_qui-qt4)
 	* removed unmaintained driver wrappers broadcom, iphone, osx, ralink,
 	  hostap, madwifi (hostap and madwifi remain available for hostapd;
@@ -104,6 +108,7 @@
 	    credential match with ANQP information
 	* limited PMKSA cache entries to be used only with the network context
 	  that was used to create them
+	* improved PMKSA cache expiration to avoid unnecessary disconnections
 	* adjusted bgscan_simple fast-scan backoff to avoid too frequent
 	  background scans
 	* removed ctrl_iface event on P2P PD Response in join-group case
@@ -149,6 +154,7 @@
 	* added initial support for WNM operations
 	  - Keep-alive based on BSS max idle period
 	  - WNM-Sleep Mode
+	  - minimal BSS Transition Management processing
 	* added autoscan module to control scanning behavior while not connected
 	  - autoscan_periodic and autoscan_exponential modules
 	* added new WPS NFC ctrl_iface mechanism
@@ -223,6 +229,10 @@
 	* added a workaround for WPS PBC session overlap detection to avoid
 	  interop issues with deployed station implementations that do not
 	  remove active PBC indication from Probe Request frames properly
+	* added basic support for 60 GHz band
+	* extend EAPOL frames processing workaround for roaming cases
+	  (postpone processing of unexpected EAPOL frame until association
+	  event to handle reordered events)
 
 2012-05-10 - v1.0
 	* bsd: Add support for setting HT values in IFM_MMASK.
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index c6c76ec..f39a3d7 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -174,10 +174,13 @@
 
 ifdef CONFIG_SAE
 CFLAGS += -DCONFIG_SAE
+OBJS += ../src/common/sae.o
+NEED_ECC=y
+NEED_DH_GROUPS=y
 endif
 
-ifdef CONFIG_IEEE80211V
-CFLAGS += -DCONFIG_IEEE80211V
+ifdef CONFIG_WNM
+CFLAGS += -DCONFIG_WNM
 OBJS += wnm_sta.o
 endif
 
@@ -560,7 +563,7 @@
 ifdef CONFIG_EAP_PWD
 CFLAGS += -DEAP_PWD
 OBJS += ../src/eap_peer/eap_pwd.o ../src/eap_common/eap_pwd_common.o
-OBJS_h += ../src/eap_server/eap_pwd.o
+OBJS_h += ../src/eap_server/eap_server_pwd.o
 CONFIG_IEEE8021X_EAPOL=y
 NEED_SHA256=y
 endif
@@ -725,6 +728,9 @@
 ifdef CONFIG_IEEE80211N
 OBJS += ../src/ap/ieee802_11_ht.o
 endif
+ifdef CONFIG_WNM
+OBJS += ../src/ap/wnm_ap.o
+endif
 ifdef CONFIG_CTRL_IFACE
 OBJS += ../src/ap/ctrl_iface_ap.o
 endif
@@ -738,10 +744,6 @@
 CFLAGS += -DCONFIG_IEEE80211N
 endif
 
-ifdef CONFIG_WNM
-CFLAGS += -DCONFIG_WNM
-endif
-
 ifdef NEED_AP_MLME
 OBJS += ../src/ap/wmm.o
 OBJS += ../src/ap/ap_list.o
@@ -860,6 +862,8 @@
 OBJS_h += ../src/eap_server/eap_server_tls_common.o
 ifndef CONFIG_FIPS
 NEED_TLS_PRF=y
+NEED_SHA1=y
+NEED_MD5=y
 endif
 endif
 
@@ -1179,6 +1183,10 @@
 endif
 endif
 
+ifdef NEED_ECC
+CFLAGS += -DCONFIG_ECC
+endif
+
 ifdef CONFIG_NO_RANDOM_POOL
 CFLAGS += -DCONFIG_NO_RANDOM_POOL
 else
@@ -1406,6 +1414,7 @@
 endif
 
 OBJS += ../src/drivers/driver_common.o
+OBJS_priv += ../src/drivers/driver_common.o
 
 OBJS_wpa_rm := ctrl_iface.o ctrl_iface_unix.o
 OBJS_wpa := $(filter-out $(OBJS_wpa_rm),$(OBJS)) $(OBJS_h) tests/test_wpa.o
diff --git a/wpa_supplicant/README b/wpa_supplicant/README
index a06e5c1..d84e61e 100644
--- a/wpa_supplicant/README
+++ b/wpa_supplicant/README
@@ -1,7 +1,7 @@
 WPA Supplicant
 ==============
 
-Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi> and contributors
 All Rights Reserved.
 
 This program is licensed under the BSD license (the one with
diff --git a/wpa_supplicant/README-HS20 b/wpa_supplicant/README-HS20
index feb9049..5669c55 100644
--- a/wpa_supplicant/README-HS20
+++ b/wpa_supplicant/README-HS20
@@ -190,6 +190,11 @@
 # phase2: Pre-configure Phase 2 (inner authentication) parameters
 #	This optional field is used with like the 'eap' parameter.
 #
+# excluded_ssid: Excluded SSID
+#	This optional field can be used to excluded specific SSID(s) from
+#	matching with the network. Multiple entries can be used to specify more
+#	than one SSID.
+#
 # for example:
 #
 #cred={
diff --git a/wpa_supplicant/README-P2P b/wpa_supplicant/README-P2P
index 4abc2f2..fb99c7b 100644
--- a/wpa_supplicant/README-P2P
+++ b/wpa_supplicant/README-P2P
@@ -191,7 +191,13 @@
 
 p2p_cancel
 
-Cancel an ongoing P2P group formation related operation.
+Cancel an ongoing P2P group formation and joining-a-group related
+operation. This operations unauthorizes the specific peer device (if any
+had been authorized to start group formation), stops P2P find (if in
+progress), stops pending operations for join-a-group, and removes the
+P2P group interface (if one was used) that is in the WPS provisioning
+step. If the WPS provisioning step has been completed, the group is not
+terminated.
 
 Service Discovery
 
@@ -219,6 +225,19 @@
 will be automatically removed when the specified peer has replied to
 it.
 
+Service Query TLV has following format:
+Length (2 octets, little endian) - length of following data
+Service Protocol Type (1 octet) - see the table below
+Service Transaction ID (1 octet) - nonzero identifier for the TLV
+Query Data (Length - 2 octets of data) - service protocol specific data
+
+Service Protocol Types:
+0 = All service protocols
+1 = Bonjour
+2 = UPnP
+3 = WS-Discovery
+4 = Wi-Fi Display
+
 For UPnP, an alternative command format can be used to specify a
 single query TLV (i.e., a service discovery for a specific UPnP
 service):
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index e261ef9..85ee6cb 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -46,7 +46,6 @@
 				  struct hostapd_config *conf)
 {
 	struct hostapd_bss_config *bss = &conf->bss[0];
-	int pairwise;
 
 	conf->driver = wpa_s->driver;
 
@@ -63,6 +62,10 @@
 		   (ssid->frequency >= 5745 && ssid->frequency <= 5825)) {
 		conf->hw_mode = HOSTAPD_MODE_IEEE80211A;
 		conf->channel = (ssid->frequency - 5000) / 5;
+	} else if (ssid->frequency >= 56160 + 2160 * 1 &&
+		   ssid->frequency <= 56160 + 2160 * 4) {
+		conf->hw_mode = HOSTAPD_MODE_IEEE80211AD;
+		conf->channel = (ssid->frequency - 56160) / 2160;
 	} else {
 		wpa_printf(MSG_ERROR, "Unsupported AP mode frequency: %d MHz",
 			   ssid->frequency);
@@ -207,22 +210,10 @@
 	if (ssid->dtim_period)
 		bss->dtim_period = ssid->dtim_period;
 
-	/* Select group cipher based on the enabled pairwise cipher suites */
-	pairwise = 0;
-	if (bss->wpa & 1)
-		pairwise |= bss->wpa_pairwise;
-	if (bss->wpa & 2) {
-		if (bss->rsn_pairwise == 0)
-			bss->rsn_pairwise = bss->wpa_pairwise;
-		pairwise |= bss->rsn_pairwise;
-	}
-	if (pairwise & WPA_CIPHER_TKIP)
-		bss->wpa_group = WPA_CIPHER_TKIP;
-	else if ((pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) ==
-		 WPA_CIPHER_GCMP)
-		bss->wpa_group = WPA_CIPHER_GCMP;
-	else
-		bss->wpa_group = WPA_CIPHER_CCMP;
+	if ((bss->wpa & 2) && bss->rsn_pairwise == 0)
+		bss->rsn_pairwise = bss->wpa_pairwise;
+	bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa, bss->wpa_pairwise,
+						    bss->rsn_pairwise);
 
 	if (bss->wpa && bss->ieee802_1x)
 		bss->ssid.security_policy = SECURITY_WPA;
@@ -264,7 +255,7 @@
 		goto no_wps;
 #ifdef CONFIG_WPS2
 	if (bss->ssid.security_policy == SECURITY_WPA_PSK &&
-	    (!(pairwise & WPA_CIPHER_CCMP) || !(bss->wpa & 2)))
+	    (!(bss->rsn_pairwise & WPA_CIPHER_CCMP) || !(bss->wpa & 2)))
 		goto no_wps; /* WPS2 does not allow WPA/TKIP-only
 			      * configuration */
 #endif /* CONFIG_WPS2 */
@@ -462,20 +453,15 @@
 		wpa_s->key_mgmt = WPA_KEY_MGMT_NONE;
 	params.key_mgmt_suite = key_mgmt2driver(wpa_s->key_mgmt);
 
-	if (ssid->pairwise_cipher & WPA_CIPHER_CCMP)
-		wpa_s->pairwise_cipher = WPA_CIPHER_CCMP;
-	else if (ssid->pairwise_cipher & WPA_CIPHER_GCMP)
-		wpa_s->pairwise_cipher = WPA_CIPHER_GCMP;
-	else if (ssid->pairwise_cipher & WPA_CIPHER_TKIP)
-		wpa_s->pairwise_cipher = WPA_CIPHER_TKIP;
-	else if (ssid->pairwise_cipher & WPA_CIPHER_NONE)
-		wpa_s->pairwise_cipher = WPA_CIPHER_NONE;
-	else {
+	wpa_s->pairwise_cipher = wpa_pick_pairwise_cipher(ssid->pairwise_cipher,
+							  1);
+	if (wpa_s->pairwise_cipher < 0) {
 		wpa_printf(MSG_WARNING, "WPA: Failed to select pairwise "
 			   "cipher.");
 		return -1;
 	}
-	params.pairwise_suite = cipher_suite2driver(wpa_s->pairwise_cipher);
+	params.pairwise_suite =
+		wpa_cipher_to_suite_driver(wpa_s->pairwise_cipher);
 	params.group_suite = params.pairwise_suite;
 
 #ifdef CONFIG_P2P
@@ -599,7 +585,6 @@
 
 	wpa_s->current_ssid = NULL;
 	wpa_s->assoc_freq = 0;
-	wpa_s->reassociated_connection = 0;
 #ifdef CONFIG_P2P
 	if (wpa_s->ap_iface->bss)
 		wpa_s->ap_iface->bss[0]->p2p_group = NULL;
diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index 0babbd5..87b7db8 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -1,6 +1,6 @@
 /*
  * BSS table
- * Copyright (c) 2009-2010, 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.
@@ -51,6 +51,14 @@
 }
 
 
+/**
+ * wpa_bss_anqp_alloc - Allocate ANQP data structure for a BSS entry
+ * Returns: Allocated ANQP data structure or %NULL on failure
+ *
+ * The allocated ANQP data structure has its users count set to 1. It may be
+ * shared by multiple BSS entries and each shared entry is freed with
+ * wpa_bss_anqp_free().
+ */
 struct wpa_bss_anqp * wpa_bss_anqp_alloc(void)
 {
 	struct wpa_bss_anqp *anqp;
@@ -62,6 +70,11 @@
 }
 
 
+/**
+ * wpa_bss_anqp_clone - Clone an ANQP data structure
+ * @anqp: ANQP data structure from wpa_bss_anqp_alloc()
+ * Returns: Cloned ANQP data structure or %NULL on failure
+ */
 static struct wpa_bss_anqp * wpa_bss_anqp_clone(struct wpa_bss_anqp *anqp)
 {
 	struct wpa_bss_anqp *n;
@@ -92,6 +105,14 @@
 }
 
 
+/**
+ * wpa_bss_anqp_unshare_alloc - Unshare ANQP data (if shared) in a BSS entry
+ * @bss: BSS entry
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function ensures the specific BSS entry has an ANQP data structure that
+ * is not shared with any other BSS entry.
+ */
 int wpa_bss_anqp_unshare_alloc(struct wpa_bss *bss)
 {
 	struct wpa_bss_anqp *anqp;
@@ -116,6 +137,10 @@
 }
 
 
+/**
+ * wpa_bss_anqp_free - Free an ANQP data structure
+ * @anqp: ANQP data structure from wpa_bss_anqp_alloc() or wpa_bss_anqp_clone()
+ */
 static void wpa_bss_anqp_free(struct wpa_bss_anqp *anqp)
 {
 	if (anqp == NULL)
@@ -175,6 +200,14 @@
 }
 
 
+/**
+ * wpa_bss_get - Fetch a BSS table entry based on BSSID and SSID
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bssid: BSSID
+ * @ssid: SSID
+ * @ssid_len: Length of @ssid
+ * Returns: Pointer to the BSS entry or %NULL if not found
+ */
 struct wpa_bss * wpa_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid,
 			     const u8 *ssid, size_t ssid_len)
 {
@@ -496,6 +529,15 @@
 }
 
 
+/**
+ * wpa_bss_update_start - Start a BSS table update from scan results
+ * @wpa_s: Pointer to wpa_supplicant data
+ *
+ * This function is called at the start of each BSS table update round for new
+ * scan results. The actual scan result entries are indicated with calls to
+ * wpa_bss_update_scan_res() and the update round is finished with a call to
+ * wpa_bss_update_end().
+ */
 void wpa_bss_update_start(struct wpa_supplicant *wpa_s)
 {
 	wpa_s->bss_update_idx++;
@@ -505,6 +547,15 @@
 }
 
 
+/**
+ * wpa_bss_update_scan_res - Update a BSS table entry based on a scan result
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @res: Scan result
+ *
+ * This function updates a BSS table entry (or adds one) based on a scan result.
+ * This is called separately for each scan result between the calls to
+ * wpa_bss_update_start() and wpa_bss_update_end().
+ */
 void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s,
 			     struct wpa_scan_res *res)
 {
@@ -610,6 +661,16 @@
 }
 
 
+/**
+ * wpa_bss_update_end - End a BSS table update from scan results
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @info: Information about scan parameters
+ * @new_scan: Whether this update round was based on a new scan
+ *
+ * This function is called at the end of each BSS table update round for new
+ * scan results. The start of the update was indicated with a call to
+ * wpa_bss_update_start().
+ */
 void wpa_bss_update_end(struct wpa_supplicant *wpa_s, struct scan_info *info,
 			int new_scan)
 {
@@ -655,6 +716,13 @@
 }
 
 
+/**
+ * wpa_bss_flush_by_age - Flush old BSS entries
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @age: Maximum entry age in seconds
+ *
+ * Remove BSS entries that have not been updated during the last @age seconds.
+ */
 void wpa_bss_flush_by_age(struct wpa_supplicant *wpa_s, int age)
 {
 	struct wpa_bss *bss, *n;
@@ -688,6 +756,14 @@
 }
 
 
+/**
+ * wpa_bss_init - Initialize BSS table
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This prepares BSS table lists and timer for periodic updates. The BSS table
+ * is deinitialized with wpa_bss_deinit() once not needed anymore.
+ */
 int wpa_bss_init(struct wpa_supplicant *wpa_s)
 {
 	dl_list_init(&wpa_s->bss);
@@ -698,6 +774,10 @@
 }
 
 
+/**
+ * wpa_bss_flush - Flush all unused BSS entries
+ * @wpa_s: Pointer to wpa_supplicant data
+ */
 void wpa_bss_flush(struct wpa_supplicant *wpa_s)
 {
 	struct wpa_bss *bss, *n;
@@ -713,6 +793,10 @@
 }
 
 
+/**
+ * wpa_bss_deinit - Deinitialize BSS table
+ * @wpa_s: Pointer to wpa_supplicant data
+ */
 void wpa_bss_deinit(struct wpa_supplicant *wpa_s)
 {
 	eloop_cancel_timeout(wpa_bss_timeout, wpa_s, NULL);
@@ -720,6 +804,12 @@
 }
 
 
+/**
+ * wpa_bss_get_bssid - Fetch a BSS table entry based on BSSID
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bssid: BSSID
+ * Returns: Pointer to the BSS entry or %NULL if not found
+ */
 struct wpa_bss * wpa_bss_get_bssid(struct wpa_supplicant *wpa_s,
 				   const u8 *bssid)
 {
@@ -735,6 +825,12 @@
 
 
 #ifdef CONFIG_P2P
+/**
+ * wpa_bss_get_p2p_dev_addr - Fetch a BSS table entry based on P2P Device Addr
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @dev_addr: P2P Device Address of the GO
+ * Returns: Pointer to the BSS entry or %NULL if not found
+ */
 struct wpa_bss * wpa_bss_get_p2p_dev_addr(struct wpa_supplicant *wpa_s,
 					  const u8 *dev_addr)
 {
@@ -751,6 +847,12 @@
 #endif /* CONFIG_P2P */
 
 
+/**
+ * wpa_bss_get_id - Fetch a BSS table entry based on identifier
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @id: Unique identifier (struct wpa_bss::id) assigned for the entry
+ * Returns: Pointer to the BSS entry or %NULL if not found
+ */
 struct wpa_bss * wpa_bss_get_id(struct wpa_supplicant *wpa_s, unsigned int id)
 {
 	struct wpa_bss *bss;
@@ -762,6 +864,15 @@
 }
 
 
+/**
+ * wpa_bss_get_ie - Fetch a specified information element from a BSS entry
+ * @bss: BSS table entry
+ * @ie: Information element identitifier (WLAN_EID_*)
+ * Returns: Pointer to the information element (id field) or %NULL if not found
+ *
+ * This function returns the first matching information element in the BSS
+ * entry.
+ */
 const u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie)
 {
 	const u8 *end, *pos;
@@ -781,6 +892,15 @@
 }
 
 
+/**
+ * wpa_bss_get_vendor_ie - Fetch a vendor information element from a BSS entry
+ * @bss: BSS table entry
+ * @vendor_type: Vendor type (four octets starting the IE payload)
+ * Returns: Pointer to the information element (id field) or %NULL if not found
+ *
+ * This function returns the first matching information element in the BSS
+ * entry.
+ */
 const u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type)
 {
 	const u8 *end, *pos;
@@ -801,6 +921,16 @@
 }
 
 
+/**
+ * wpa_bss_get_vendor_ie_multi - Fetch vendor IE data from a BSS entry
+ * @bss: BSS table entry
+ * @vendor_type: Vendor type (four octets starting the IE payload)
+ * Returns: Pointer to the information element payload or %NULL if not found
+ *
+ * This function returns concatenated payload of possibly fragmented vendor
+ * specific information elements in the BSS entry. The caller is responsible for
+ * freeing the returned buffer.
+ */
 struct wpabuf * wpa_bss_get_vendor_ie_multi(const struct wpa_bss *bss,
 					    u32 vendor_type)
 {
@@ -832,6 +962,19 @@
 }
 
 
+/**
+ * wpa_bss_get_vendor_ie_multi_beacon - Fetch vendor IE data from a BSS entry
+ * @bss: BSS table entry
+ * @vendor_type: Vendor type (four octets starting the IE payload)
+ * Returns: Pointer to the information element payload or %NULL if not found
+ *
+ * This function returns concatenated payload of possibly fragmented vendor
+ * specific information elements in the BSS entry. The caller is responsible for
+ * freeing the returned buffer.
+ *
+ * This function is like wpa_bss_get_vendor_ie_multi(), but uses IE buffer only
+ * from Beacon frames instead of either Beacon or Probe Response frames.
+ */
 struct wpabuf * wpa_bss_get_vendor_ie_multi_beacon(const struct wpa_bss *bss,
 						   u32 vendor_type)
 {
@@ -864,6 +1007,11 @@
 }
 
 
+/**
+ * wpa_bss_get_max_rate - Get maximum legacy TX rate supported in a BSS
+ * @bss: BSS table entry
+ * Returns: Maximum legacy rate in units of 500 kbps
+ */
 int wpa_bss_get_max_rate(const struct wpa_bss *bss)
 {
 	int rate = 0;
@@ -886,6 +1034,15 @@
 }
 
 
+/**
+ * wpa_bss_get_bit_rates - Get legacy TX rates supported in a BSS
+ * @bss: BSS table entry
+ * @rates: Buffer for returning a pointer to the rates list (units of 500 kbps)
+ * Returns: number of legacy TX rates or -1 on failure
+ *
+ * The caller is responsible for freeing the returned buffer with os_free() in
+ * case of success.
+ */
 int wpa_bss_get_bit_rates(const struct wpa_bss *bss, u8 **rates)
 {
 	const u8 *ie, *ie2;
diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h
index eb01f2d..01f6c59 100644
--- a/wpa_supplicant/bss.h
+++ b/wpa_supplicant/bss.h
@@ -19,7 +19,11 @@
 #define WPA_BSS_ASSOCIATED		BIT(5)
 #define WPA_BSS_ANQP_FETCH_TRIED	BIT(6)
 
+/**
+ * struct wpa_bss_anqp - ANQP data for a BSS entry (struct wpa_bss)
+ */
 struct wpa_bss_anqp {
+	/** Number of BSS entries referring to this ANQP data instance */
 	unsigned int users;
 #ifdef CONFIG_INTERWORKING
 	struct wpabuf *venue_name;
@@ -40,49 +44,52 @@
 
 /**
  * struct wpa_bss - BSS table
- * @list: List entry for struct wpa_supplicant::bss
- * @list_id: List entry for struct wpa_supplicant::bss_id
- * @id: Unique identifier for this BSS entry
- * @scan_miss_count: Number of counts without seeing this BSS
- * @flags: information flags about the BSS/IBSS (WPA_BSS_*)
- * @last_update_idx: Index of the last scan update
- * @bssid: BSSID
- * @hessid: HESSID
- * @freq: frequency of the channel in MHz (e.g., 2412 = channel 1)
- * @beacon_int: beacon interval in TUs (host byte order)
- * @caps: capability information field in host byte order
- * @qual: signal quality
- * @noise: noise level
- * @level: signal level
- * @tsf: Timestamp of last Beacon/Probe Response frame
- * @last_update: Time of the last update (i.e., Beacon or Probe Response RX)
- * @ie_len: length of the following IE field in octets (from Probe Response)
- * @beacon_ie_len: length of the following Beacon IE field in octets
  *
  * This structure is used to store information about neighboring BSSes in
  * generic format. It is mainly updated based on scan results from the driver.
  */
 struct wpa_bss {
+	/** List entry for struct wpa_supplicant::bss */
 	struct dl_list list;
+	/** List entry for struct wpa_supplicant::bss_id */
 	struct dl_list list_id;
+	/** Unique identifier for this BSS entry */
 	unsigned int id;
+	/** Number of counts without seeing this BSS */
 	unsigned int scan_miss_count;
+	/** Index of the last scan update */
 	unsigned int last_update_idx;
+	/** Information flags about the BSS/IBSS (WPA_BSS_*) */
 	unsigned int flags;
+	/** BSSID */
 	u8 bssid[ETH_ALEN];
+	/** HESSID */
 	u8 hessid[ETH_ALEN];
+	/** SSID */
 	u8 ssid[32];
+	/** Length of SSID */
 	size_t ssid_len;
+	/** Frequency of the channel in MHz (e.g., 2412 = channel 1) */
 	int freq;
+	/** Beacon interval in TUs (host byte order) */
 	u16 beacon_int;
+	/** Capability information field in host byte order */
 	u16 caps;
+	/** Signal quality */
 	int qual;
+	/** Noise level */
 	int noise;
+	/** Signal level */
 	int level;
+	/** Timestamp of last Beacon/Probe Response frame */
 	u64 tsf;
+	/** Time of the last update (i.e., Beacon or Probe Response RX) */
 	struct os_time last_update;
+	/** ANQP data */
 	struct wpa_bss_anqp *anqp;
+	/** Length of the following IE field in octets (from Probe Response) */
 	size_t ie_len;
+	/** Length of the following Beacon IE field in octets */
 	size_t beacon_ie_len;
 	/* followed by ie_len octets of IEs */
 	/* followed by beacon_ie_len octets of IEs */
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index e157845..2c52c68 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -629,49 +629,12 @@
 
 static int wpa_config_parse_cipher(int line, const char *value)
 {
-	int val = 0, last;
-	char *start, *end, *buf;
-
-	buf = os_strdup(value);
-	if (buf == NULL)
+	int val = wpa_parse_cipher(value);
+	if (val < 0) {
+		wpa_printf(MSG_ERROR, "Line %d: invalid cipher '%s'.",
+			   line, value);
 		return -1;
-	start = buf;
-
-	while (*start != '\0') {
-		while (*start == ' ' || *start == '\t')
-			start++;
-		if (*start == '\0')
-			break;
-		end = start;
-		while (*end != ' ' && *end != '\t' && *end != '\0')
-			end++;
-		last = *end == '\0';
-		*end = '\0';
-		if (os_strcmp(start, "CCMP") == 0)
-			val |= WPA_CIPHER_CCMP;
-		else if (os_strcmp(start, "GCMP") == 0)
-			val |= WPA_CIPHER_GCMP;
-		else if (os_strcmp(start, "TKIP") == 0)
-			val |= WPA_CIPHER_TKIP;
-		else if (os_strcmp(start, "WEP104") == 0)
-			val |= WPA_CIPHER_WEP104;
-		else if (os_strcmp(start, "WEP40") == 0)
-			val |= WPA_CIPHER_WEP40;
-		else if (os_strcmp(start, "NONE") == 0)
-			val |= WPA_CIPHER_NONE;
-		else {
-			wpa_printf(MSG_ERROR, "Line %d: invalid cipher '%s'.",
-				   line, start);
-			os_free(buf);
-			return -1;
-		}
-
-		if (last)
-			break;
-		start = end + 1;
 	}
-	os_free(buf);
-
 	if (val == 0) {
 		wpa_printf(MSG_ERROR, "Line %d: no cipher values configured.",
 			   line);
@@ -684,72 +647,13 @@
 #ifndef NO_CONFIG_WRITE
 static char * wpa_config_write_cipher(int cipher)
 {
-	char *buf, *pos, *end;
-	int ret;
-
-	pos = buf = os_zalloc(50);
+	char *buf = os_zalloc(50);
 	if (buf == NULL)
 		return NULL;
-	end = buf + 50;
 
-	if (cipher & WPA_CIPHER_CCMP) {
-		ret = os_snprintf(pos, end - pos, "%sCCMP",
-				  pos == buf ? "" : " ");
-		if (ret < 0 || ret >= end - pos) {
-			end[-1] = '\0';
-			return buf;
-		}
-		pos += ret;
-	}
-
-	if (cipher & WPA_CIPHER_GCMP) {
-		ret = os_snprintf(pos, end - pos, "%sGCMP",
-				  pos == buf ? "" : " ");
-		if (ret < 0 || ret >= end - pos) {
-			end[-1] = '\0';
-			return buf;
-		}
-		pos += ret;
-	}
-
-	if (cipher & WPA_CIPHER_TKIP) {
-		ret = os_snprintf(pos, end - pos, "%sTKIP",
-				  pos == buf ? "" : " ");
-		if (ret < 0 || ret >= end - pos) {
-			end[-1] = '\0';
-			return buf;
-		}
-		pos += ret;
-	}
-
-	if (cipher & WPA_CIPHER_WEP104) {
-		ret = os_snprintf(pos, end - pos, "%sWEP104",
-				  pos == buf ? "" : " ");
-		if (ret < 0 || ret >= end - pos) {
-			end[-1] = '\0';
-			return buf;
-		}
-		pos += ret;
-	}
-
-	if (cipher & WPA_CIPHER_WEP40) {
-		ret = os_snprintf(pos, end - pos, "%sWEP40",
-				  pos == buf ? "" : " ");
-		if (ret < 0 || ret >= end - pos) {
-			end[-1] = '\0';
-			return buf;
-		}
-		pos += ret;
-	}
-
-	if (cipher & WPA_CIPHER_NONE) {
-		ret = os_snprintf(pos, end - pos, "%sNONE",
-				  pos == buf ? "" : " ");
-		if (ret < 0 || ret >= end - pos) {
-			end[-1] = '\0';
-			return buf;
-		}
-		pos += ret;
+	if (wpa_write_ciphers(buf, buf + 50, cipher, " ") < 0) {
+		os_free(buf);
+		return NULL;
 	}
 
 	return buf;
@@ -765,8 +669,7 @@
 	val = wpa_config_parse_cipher(line, value);
 	if (val == -1)
 		return -1;
-	if (val & ~(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP |
-		    WPA_CIPHER_NONE)) {
+	if (val & ~WPA_ALLOWED_PAIRWISE_CIPHERS) {
 		wpa_printf(MSG_ERROR, "Line %d: not allowed pairwise cipher "
 			   "(0x%x).", line, val);
 		return -1;
@@ -795,8 +698,7 @@
 	val = wpa_config_parse_cipher(line, value);
 	if (val == -1)
 		return -1;
-	if (val & ~(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP |
-		    WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40)) {
+	if (val & ~WPA_ALLOWED_GROUP_CIPHERS) {
 		wpa_printf(MSG_ERROR, "Line %d: not allowed group cipher "
 			   "(0x%x).", line, val);
 		return -1;
@@ -916,9 +818,7 @@
 #endif /* NO_CONFIG_WRITE */
 
 
-static int * wpa_config_parse_freqs(const struct parse_data *data,
-				    struct wpa_ssid *ssid, int line,
-				    const char *value)
+static int * wpa_config_parse_int_array(const char *value)
 {
 	int *freqs;
 	size_t used, len;
@@ -965,7 +865,7 @@
 {
 	int *freqs;
 
-	freqs = wpa_config_parse_freqs(data, ssid, line, value);
+	freqs = wpa_config_parse_int_array(value);
 	if (freqs == NULL)
 		return -1;
 	os_free(ssid->scan_freq);
@@ -981,7 +881,7 @@
 {
 	int *freqs;
 
-	freqs = wpa_config_parse_freqs(data, ssid, line, value);
+	freqs = wpa_config_parse_int_array(value);
 	if (freqs == NULL)
 		return -1;
 	os_free(ssid->freq_list);
@@ -1627,7 +1527,7 @@
 #endif /* CONFIG_IEEE80211W */
 	{ INT_RANGE(peerkey, 0, 1) },
 	{ INT_RANGE(mixed_cell, 0, 1) },
-	{ INT_RANGE(frequency, 0, 10000) },
+	{ INT_RANGE(frequency, 0, 65000) },
 	{ INT(wpa_ptk_rekey) },
 	{ STR(bgscan) },
 	{ INT_RANGE(ignore_broadcast_ssid, 0, 2) },
@@ -1637,6 +1537,7 @@
 #ifdef CONFIG_HT_OVERRIDES
 	{ INT_RANGE(disable_ht, 0, 1) },
 	{ INT_RANGE(disable_ht40, -1, 1) },
+	{ INT_RANGE(disable_sgi, 0, 1) },
 	{ INT_RANGE(disable_max_amsdu, -1, 1) },
 	{ INT_RANGE(ampdu_factor, -1, 3) },
 	{ INT_RANGE(ampdu_density, -1, 7) },
@@ -1835,6 +1736,7 @@
 	os_free(cred->eap_method);
 	os_free(cred->phase1);
 	os_free(cred->phase2);
+	os_free(cred->excluded_ssid);
 	os_free(cred);
 }
 
@@ -1901,6 +1803,7 @@
 	wpabuf_free(config->wps_nfc_dh_privkey);
 	wpabuf_free(config->wps_nfc_dev_pw);
 	os_free(config->ext_password_backend);
+	os_free(config->sae_groups);
 	os_free(config);
 }
 
@@ -2037,6 +1940,7 @@
 #ifdef CONFIG_HT_OVERRIDES
 	ssid->disable_ht = DEFAULT_DISABLE_HT;
 	ssid->disable_ht40 = DEFAULT_DISABLE_HT40;
+	ssid->disable_sgi = DEFAULT_DISABLE_SGI;
 	ssid->disable_max_amsdu = DEFAULT_DISABLE_MAX_AMSDU;
 	ssid->ampdu_factor = DEFAULT_AMPDU_FACTOR;
 	ssid->ampdu_density = DEFAULT_AMPDU_DENSITY;
@@ -2411,6 +2315,34 @@
 		return 0;
 	}
 
+	if (os_strcmp(var, "excluded_ssid") == 0) {
+		struct excluded_ssid *e;
+
+		if (len > MAX_SSID_LEN) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid "
+				   "excluded_ssid length %d", line, (int) len);
+			os_free(val);
+			return -1;
+		}
+
+		e = os_realloc_array(cred->excluded_ssid,
+				     cred->num_excluded_ssid + 1,
+				     sizeof(struct excluded_ssid));
+		if (e == NULL) {
+			os_free(val);
+			return -1;
+		}
+		cred->excluded_ssid = e;
+
+		e = &cred->excluded_ssid[cred->num_excluded_ssid++];
+		os_memcpy(e->ssid, val, len);
+		e->ssid_len = len;
+
+		os_free(val);
+
+		return 0;
+	}
+
 	if (line) {
 		wpa_printf(MSG_ERROR, "Line %d: unknown cred field '%s'.",
 			   line, var);
@@ -2947,6 +2879,24 @@
 }
 
 
+static int wpa_config_process_sae_groups(
+	const struct global_parse_data *data,
+	struct wpa_config *config, int line, const char *pos)
+{
+	int *groups = wpa_config_parse_int_array(pos);
+	if (groups == NULL) {
+		wpa_printf(MSG_ERROR, "Line %d: Invalid sae_groups '%s'",
+			   line, pos);
+		return -1;
+	}
+
+	os_free(config->sae_groups);
+	config->sae_groups = groups;
+
+	return 0;
+}
+
+
 #ifdef OFFSET
 #undef OFFSET
 #endif /* OFFSET */
@@ -3039,6 +2989,7 @@
 	{ INT_RANGE(auto_interworking, 0, 1), 0 },
 	{ INT(okc), 0 },
 	{ INT(pmf), 0 },
+	{ FUNC(sae_groups), 0 },
 };
 
 #undef FUNC
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index c0aea0b..0c3cb9a 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -196,6 +196,12 @@
 	 * Pre-configured EAP parameters or %NULL.
 	 */
 	char *phase2;
+
+	struct excluded_ssid {
+		u8 ssid[MAX_SSID_LEN];
+		size_t ssid_len;
+	} *excluded_ssid;
+	size_t num_excluded_ssid;
 };
 
 
@@ -710,12 +716,12 @@
 	struct wpabuf *wps_nfc_dh_pubkey;
 
 	/**
-	 * wps_nfc_dh_pubkey - NFC DH Private Key for password token
+	 * wps_nfc_dh_privkey - NFC DH Private Key for password token
 	 */
 	struct wpabuf *wps_nfc_dh_privkey;
 
 	/**
-	 * wps_nfc_dh_pubkey - NFC Device Password for password token
+	 * wps_nfc_dev_pw - NFC Device Password for password token
 	 */
 	struct wpabuf *wps_nfc_dev_pw;
 
@@ -791,6 +797,16 @@
 	 * this default behavior.
 	 */
 	enum mfp_options pmf;
+
+	/**
+	 * sae_groups - Preference list of enabled groups for SAE
+	 *
+	 * By default (if this parameter is not set), the mandatory group 19
+	 * (ECC group defined over a 256-bit prime order field) is preferred,
+	 * but other groups are also enabled. If this parameter is set, the
+	 * groups will be tried in the indicated order.
+	 */
+	int *sae_groups;
 };
 
 
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index d66eac5..50c3533 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -17,6 +17,8 @@
 #include "base64.h"
 #include "uuid.h"
 #include "p2p/p2p.h"
+#include "eap_peer/eap_methods.h"
+#include "eap_peer/eap.h"
 
 
 static int newline_terminated(const char *buf, size_t buflen)
@@ -676,6 +678,7 @@
 	INT_DEFe(fragment_size, DEFAULT_FRAGMENT_SIZE);
 #endif /* IEEE8021X_EAPOL */
 	INT(mode);
+	INT(frequency);
 	write_int(f, "proactive_key_caching", ssid->proactive_key_caching, -1);
 	INT(disabled);
 	INT(peerkey);
@@ -704,16 +707,52 @@
 		fprintf(f, "\trealm=\"%s\"\n", cred->realm);
 	if (cred->username)
 		fprintf(f, "\tusername=\"%s\"\n", cred->username);
-	if (cred->password)
+	if (cred->password && cred->ext_password)
+		fprintf(f, "\tpassword=ext:%s\n", cred->password);
+	else if (cred->password)
 		fprintf(f, "\tpassword=\"%s\"\n", cred->password);
 	if (cred->ca_cert)
 		fprintf(f, "\tca_cert=\"%s\"\n", cred->ca_cert);
+	if (cred->client_cert)
+		fprintf(f, "\tclient_cert=\"%s\"\n", cred->client_cert);
+	if (cred->private_key)
+		fprintf(f, "\tprivate_key=\"%s\"\n", cred->private_key);
+	if (cred->private_key_passwd)
+		fprintf(f, "\tprivate_key_passwd=\"%s\"\n",
+			cred->private_key_passwd);
 	if (cred->imsi)
 		fprintf(f, "\timsi=\"%s\"\n", cred->imsi);
 	if (cred->milenage)
 		fprintf(f, "\tmilenage=\"%s\"\n", cred->milenage);
 	if (cred->domain)
 		fprintf(f, "\tdomain=\"%s\"\n", cred->domain);
+	if (cred->roaming_consortium_len) {
+		size_t i;
+		fprintf(f, "\troaming_consortium=");
+		for (i = 0; i < cred->roaming_consortium_len; i++)
+			fprintf(f, "%02x", cred->roaming_consortium[i]);
+		fprintf(f, "\n");
+	}
+	if (cred->eap_method) {
+		const char *name;
+		name = eap_get_name(cred->eap_method[0].vendor,
+				    cred->eap_method[0].method);
+		fprintf(f, "\teap=%s\n", name);
+	}
+	if (cred->phase1)
+		fprintf(f, "\tphase1=\"%s\"\n", cred->phase1);
+	if (cred->phase2)
+		fprintf(f, "\tphase2=\"%s\"\n", cred->phase2);
+	if (cred->excluded_ssid) {
+		size_t i, j;
+		for (i = 0; i < cred->num_excluded_ssid; i++) {
+			struct excluded_ssid *e = &cred->excluded_ssid[i];
+			fprintf(f, "\texcluded_ssid=");
+			for (j = 0; j < e->ssid_len; j++)
+				fprintf(f, "%02x", e->ssid[j]);
+			fprintf(f, "\n");
+		}
+	}
 }
 
 
@@ -931,6 +970,16 @@
 		fprintf(f, "okc=%d\n", config->okc);
 	if (config->pmf)
 		fprintf(f, "pmf=%d\n", config->pmf);
+
+	if (config->sae_groups) {
+		int i;
+		fprintf(f, "sae_groups=");
+		for (i = 0; config->sae_groups[i] >= 0; i++) {
+			fprintf(f, "%s%d", i > 0 ? " " : "",
+				config->sae_groups[i]);
+		}
+		fprintf(f, "\n");
+	}
 }
 
 #endif /* CONFIG_NO_CONFIG_WRITE */
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index c1184f8..d079863 100644
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -28,6 +28,7 @@
 #define DEFAULT_BG_SCAN_PERIOD -1
 #define DEFAULT_DISABLE_HT 0
 #define DEFAULT_DISABLE_HT40 0
+#define DEFAULT_DISABLE_SGI 0
 #define DEFAULT_DISABLE_MAX_AMSDU -1 /* no change */
 #define DEFAULT_AMPDU_FACTOR -1 /* no change */
 #define DEFAULT_AMPDU_DENSITY -1 /* no change */
@@ -502,6 +503,14 @@
 	int disable_ht40;
 
 	/**
+	 * disable_sgi - Disable SGI (Short Guard Interval) for this network
+	 *
+	 * By default, use it if it is available, but this can be configured
+	 * to 1 to have it disabled.
+	 */
+	int disable_sgi;
+
+	/**
 	 * disable_max_amsdu - Disable MAX A-MSDU
 	 *
 	 * A-MDSU will be 3839 bytes when disabled, or 7935
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index 564c91e..3408a85 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -38,6 +38,7 @@
 #include "interworking.h"
 #include "blacklist.h"
 #include "autoscan.h"
+#include "wnm_sta.h"
 
 extern struct wpa_driver_ops *wpa_drivers[];
 
@@ -57,6 +58,11 @@
 	if (wpa_s->pno)
 		return 0;
 
+	if (wpa_s->wpa_state == WPA_SCANNING) {
+		wpa_supplicant_cancel_sched_scan(wpa_s);
+		wpa_supplicant_cancel_scan(wpa_s);
+	}
+
 	os_memset(&params, 0, sizeof(params));
 
 	num_ssid = 0;
@@ -112,11 +118,17 @@
 
 static int pno_stop(struct wpa_supplicant *wpa_s)
 {
+	int ret = 0;
+
 	if (wpa_s->pno) {
 		wpa_s->pno = 0;
-		return wpa_drv_stop_sched_scan(wpa_s);
+		ret = wpa_drv_stop_sched_scan(wpa_s);
 	}
-	return 0;
+
+	if (wpa_s->wpa_state == WPA_SCANNING)
+		wpa_supplicant_req_scan(wpa_s, 0, 0);
+
+	return ret;
 }
 
 
@@ -414,6 +426,8 @@
 		ret = set_bssid_filter(wpa_s, value);
 	} else if (os_strcasecmp(cmd, "disallow_aps") == 0) {
 		ret = set_disallow_aps(wpa_s, value);
+	} else if (os_strcasecmp(cmd, "no_keep_alive") == 0) {
+		wpa_s->no_keep_alive = !!atoi(value);
 	} else {
 		value[-1] = '=';
 		ret = wpa_config_process_global(wpa_s->conf, cmd, -1);
@@ -1359,6 +1373,16 @@
 #endif /* CONFIG_AP */
 		pos += wpa_sm_get_status(wpa_s->wpa, pos, end - pos, verbose);
 	}
+#ifdef CONFIG_SAE
+	if (wpa_s->wpa_state >= WPA_ASSOCIATED &&
+	    wpa_s->sme.sae.state == SAE_ACCEPTED && !wpa_s->ap_iface) {
+		ret = os_snprintf(pos, end - pos, "sae_group=%d\n",
+				  wpa_s->sme.sae.group);
+		if (ret < 0 || ret >= end - pos)
+			return pos - buf;
+		pos += ret;
+	}
+#endif /* CONFIG_SAE */
 	ret = os_snprintf(pos, end - pos, "wpa_state=%s\n",
 			  wpa_supplicant_state_txt(wpa_s->wpa_state));
 	if (ret < 0 || ret >= end - pos)
@@ -1465,8 +1489,7 @@
 		struct wpa_ssid *ssid = wpa_s->current_ssid;
 		wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_CONNECTED "- connection to "
 			MACSTR " completed %s [id=%d id_str=%s]",
-			MAC2STR(wpa_s->bssid), wpa_s->reassociated_connection ?
-			"(reauth)" : "(auth)",
+			MAC2STR(wpa_s->bssid), "(auth)",
 			ssid ? ssid->id : -1,
 			ssid && ssid->id_str ? ssid->id_str : "");
 	}
@@ -1713,54 +1736,15 @@
 
 static char * wpa_supplicant_cipher_txt(char *pos, char *end, int cipher)
 {
-	int first = 1, ret;
+	int ret;
 	ret = os_snprintf(pos, end - pos, "-");
 	if (ret < 0 || ret >= end - pos)
 		return pos;
 	pos += ret;
-	if (cipher & WPA_CIPHER_NONE) {
-		ret = os_snprintf(pos, end - pos, "%sNONE", first ? "" : "+");
-		if (ret < 0 || ret >= end - pos)
-			return pos;
-		pos += ret;
-		first = 0;
-	}
-	if (cipher & WPA_CIPHER_WEP40) {
-		ret = os_snprintf(pos, end - pos, "%sWEP40", first ? "" : "+");
-		if (ret < 0 || ret >= end - pos)
-			return pos;
-		pos += ret;
-		first = 0;
-	}
-	if (cipher & WPA_CIPHER_WEP104) {
-		ret = os_snprintf(pos, end - pos, "%sWEP104",
-				  first ? "" : "+");
-		if (ret < 0 || ret >= end - pos)
-			return pos;
-		pos += ret;
-		first = 0;
-	}
-	if (cipher & WPA_CIPHER_TKIP) {
-		ret = os_snprintf(pos, end - pos, "%sTKIP", first ? "" : "+");
-		if (ret < 0 || ret >= end - pos)
-			return pos;
-		pos += ret;
-		first = 0;
-	}
-	if (cipher & WPA_CIPHER_CCMP) {
-		ret = os_snprintf(pos, end - pos, "%sCCMP", first ? "" : "+");
-		if (ret < 0 || ret >= end - pos)
-			return pos;
-		pos += ret;
-		first = 0;
-	}
-	if (cipher & WPA_CIPHER_GCMP) {
-		ret = os_snprintf(pos, end - pos, "%sGCMP", first ? "" : "+");
-		if (ret < 0 || ret >= end - pos)
-			return pos;
-		pos += ret;
-		first = 0;
-	}
+	ret = wpa_write_ciphers(pos, end, cipher, "+");
+	if (ret < 0)
+		return pos;
+	pos += ret;
 	return pos;
 }
 
@@ -2784,6 +2768,9 @@
 		case HOSTAPD_MODE_IEEE80211A:
 			hmode = "A";
 			break;
+		case HOSTAPD_MODE_IEEE80211AD:
+			hmode = "AD";
+			break;
 		default:
 			continue;
 		}
@@ -4698,6 +4685,60 @@
 #endif /* CONFIG_AUTOSCAN */
 
 
+#ifdef CONFIG_WNM
+
+static int wpas_ctrl_iface_wnm_sleep(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	int enter;
+	int intval = 0;
+	char *pos;
+	int ret;
+	struct wpabuf *tfs_req = NULL;
+
+	if (os_strncmp(cmd, "enter", 5) == 0)
+		enter = 1;
+	else if (os_strncmp(cmd, "exit", 4) == 0)
+		enter = 0;
+	else
+		return -1;
+
+	pos = os_strstr(cmd, " interval=");
+	if (pos)
+		intval = atoi(pos + 10);
+
+	pos = os_strstr(cmd, " tfs_req=");
+	if (pos) {
+		char *end;
+		size_t len;
+		pos += 9;
+		end = os_strchr(pos, ' ');
+		if (end)
+			len = end - pos;
+		else
+			len = os_strlen(pos);
+		if (len & 1)
+			return -1;
+		len /= 2;
+		tfs_req = wpabuf_alloc(len);
+		if (tfs_req == NULL)
+			return -1;
+		if (hexstr2bin(pos, wpabuf_put(tfs_req, len), len) < 0) {
+			wpabuf_free(tfs_req);
+			return -1;
+		}
+	}
+
+	ret = ieee802_11_send_wnmsleep_req(wpa_s, enter ? WNM_SLEEP_MODE_ENTER :
+					   WNM_SLEEP_MODE_EXIT, intval,
+					   tfs_req);
+	wpabuf_free(tfs_req);
+
+	return ret;
+}
+
+#endif /* CONFIG_WNM */
+
+
 static int wpa_supplicant_signal_poll(struct wpa_supplicant *wpa_s, char *buf,
 				      size_t buflen)
 {
@@ -5124,7 +5165,7 @@
 		if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
 			reply_len = -1;
 		else {
-			if (!wpa_s->scanning &&
+			if (!wpa_s->sched_scanning && !wpa_s->scanning &&
 			    ((wpa_s->wpa_state <= WPA_SCANNING) ||
 			     (wpa_s->wpa_state == WPA_COMPLETED))) {
 				wpa_s->normal_scans = 0;
@@ -5271,6 +5312,11 @@
 	} else if (os_strcmp(buf, "REAUTHENTICATE") == 0) {
 		pmksa_cache_clear_current(wpa_s->wpa);
 		eapol_sm_request_reauth(wpa_s->eapol);
+#ifdef CONFIG_WNM
+	} else if (os_strncmp(buf, "WNM_SLEEP ", 10) == 0) {
+		if (wpas_ctrl_iface_wnm_sleep(wpa_s, buf + 10))
+			reply_len = -1;
+#endif /* CONFIG_WNM */
 	} else {
 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
 		reply_len = 16;
diff --git a/wpa_supplicant/eapol_test.c b/wpa_supplicant/eapol_test.c
index 03b8c7e..87e399c 100644
--- a/wpa_supplicant/eapol_test.c
+++ b/wpa_supplicant/eapol_test.c
@@ -21,7 +21,6 @@
 #include "eloop.h"
 #include "utils/base64.h"
 #include "rsn_supp/wpa.h"
-#include "eap_peer/eap_i.h"
 #include "wpa_supplicant_i.h"
 #include "radius/radius.h"
 #include "radius/radius_client.h"
@@ -98,7 +97,7 @@
 	size_t len;
 	char *pos;
 	u32 val;
-	char buf[128];
+	char buf[RADIUS_MAX_ATTR_LEN + 1];
 
 	switch (attr->syntax) {
 	case 's':
@@ -114,7 +113,7 @@
 		if (pos[0] == '0' && pos[1] == 'x')
 			pos += 2;
 		len = os_strlen(pos);
-		if ((len & 1) || (len / 2) > sizeof(buf)) {
+		if ((len & 1) || (len / 2) > RADIUS_MAX_ATTR_LEN) {
 			printf("Invalid extra attribute hexstring\n");
 			return -1;
 		}
@@ -171,7 +170,7 @@
 					  const u8 *eap, size_t len)
 {
 	struct radius_msg *msg;
-	char buf[128];
+	char buf[RADIUS_MAX_ATTR_LEN + 1];
 	const struct eap_hdr *hdr;
 	const u8 *pos;
 
@@ -1173,7 +1172,7 @@
 			wait_for_monitor++;
 			break;
 		case 'N':
-			p1 = os_zalloc(sizeof(p1));
+			p1 = os_zalloc(sizeof(*p1));
 			if (p1 == NULL)
 				break;
 			if (!p)
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index 91c644f..3fefb48 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -102,8 +102,9 @@
 	if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) {
 		u8 wpa_ie[80];
 		size_t wpa_ie_len = sizeof(wpa_ie);
-		wpa_supplicant_set_suites(wpa_s, NULL, ssid,
-					  wpa_ie, &wpa_ie_len);
+		if (wpa_supplicant_set_suites(wpa_s, NULL, ssid,
+					      wpa_ie, &wpa_ie_len) < 0)
+			wpa_dbg(wpa_s, MSG_DEBUG, "Could not set WPA suites");
 	} else {
 		wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
 	}
@@ -1355,12 +1356,16 @@
 	if (wpa_s->wpa_state < WPA_ASSOCIATED)
 		return;
 
-	wpa_printf(MSG_DEBUG, "WNM: Send keep-alive to AP " MACSTR,
-		   MAC2STR(wpa_s->bssid));
-	/* TODO: could skip this if normal data traffic has been sent */
-	/* TODO: Consider using some more appropriate data frame for this */
-	if (wpa_s->l2)
-		l2_packet_send(wpa_s->l2, wpa_s->bssid, 0x0800, (u8 *) "", 0);
+	if (!wpa_s->no_keep_alive) {
+		wpa_printf(MSG_DEBUG, "WNM: Send keep-alive to AP " MACSTR,
+			   MAC2STR(wpa_s->bssid));
+		/* TODO: could skip this if normal data traffic has been sent */
+		/* TODO: Consider using some more appropriate data frame for
+		 * this */
+		if (wpa_s->l2)
+			l2_packet_send(wpa_s->l2, wpa_s->bssid, 0x0800,
+				       (u8 *) "", 0);
+	}
 
 #ifdef CONFIG_SME
 	if (wpa_s->sme.bss_max_idle_period) {
@@ -2183,7 +2188,7 @@
 #endif /* CONFIG_TDLS */
 
 
-#ifdef CONFIG_IEEE80211V
+#ifdef CONFIG_WNM
 static void wpa_supplicant_event_wnm(struct wpa_supplicant *wpa_s,
 				     union wpa_event_data *data)
 {
@@ -2195,11 +2200,11 @@
 			   "(action=%d, intval=%d)",
 			   data->wnm.sleep_action, data->wnm.sleep_intval);
 		ieee802_11_send_wnmsleep_req(wpa_s, data->wnm.sleep_action,
-					     data->wnm.sleep_intval);
+					     data->wnm.sleep_intval, NULL);
 		break;
 	}
 }
-#endif /* CONFIG_IEEE80211V */
+#endif /* CONFIG_WNM */
 
 
 #ifdef CONFIG_IEEE80211R
@@ -2322,50 +2327,6 @@
 }
 
 
-static void wnm_action_rx(struct wpa_supplicant *wpa_s, struct rx_action *rx)
-{
-	u8 action, mode;
-	const u8 *pos, *end;
-
-	if (rx->data == NULL || rx->len == 0)
-		return;
-
-	pos = rx->data;
-	end = pos + rx->len;
-	action = *pos++;
-
-	wpa_printf(MSG_DEBUG, "WNM: RX action %u from " MACSTR,
-		   action, MAC2STR(rx->sa));
-	switch (action) {
-	case WNM_BSS_TRANS_MGMT_REQ:
-		if (pos + 5 > end)
-			break;
-		wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management "
-			   "Request: dialog_token=%u request_mode=0x%x "
-			   "disassoc_timer=%u validity_interval=%u",
-			   pos[0], pos[1], WPA_GET_LE16(pos + 2), pos[4]);
-		mode = pos[1];
-		pos += 5;
-		if (mode & 0x08)
-			pos += 12; /* BSS Termination Duration */
-		if (mode & 0x10) {
-			char url[256];
-			if (pos + 1 > end || pos + 1 + pos[0] > end) {
-				wpa_printf(MSG_DEBUG, "WNM: Invalid BSS "
-					   "Transition Management Request "
-					   "(URL)");
-				break;
-			}
-			os_memcpy(url, pos + 1, pos[0]);
-			url[pos[0]] = '\0';
-			wpa_msg(wpa_s, MSG_INFO, "WNM: ESS Disassociation "
-				"Imminent - session_info_url=%s", url);
-		}
-		break;
-	}
-}
-
-
 void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 			  union wpa_event_data *data)
 {
@@ -2552,11 +2513,11 @@
 		wpa_supplicant_event_tdls(wpa_s, data);
 		break;
 #endif /* CONFIG_TDLS */
-#ifdef CONFIG_IEEE80211V
+#ifdef CONFIG_WNM
 	case EVENT_WNM:
 		wpa_supplicant_event_wnm(wpa_s, data);
 		break;
-#endif /* CONFIG_IEEE80211V */
+#endif /* CONFIG_WNM */
 #ifdef CONFIG_IEEE80211R
 	case EVENT_FT_RESPONSE:
 		wpa_supplicant_event_ft_response(wpa_s, data);
@@ -2795,12 +2756,12 @@
 		}
 #endif /* CONFIG_SME */
 #endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_IEEE80211V
+#ifdef CONFIG_WNM
 		if (data->rx_action.category == WLAN_ACTION_WNM) {
 			ieee802_11_rx_wnm_action(wpa_s, &data->rx_action);
 			break;
 		}
-#endif /* CONFIG_IEEE80211V */
+#endif /* CONFIG_WNM */
 #ifdef CONFIG_GAS
 		if (data->rx_action.category == WLAN_ACTION_PUBLIC &&
 		    gas_query_rx(wpa_s->gas, data->rx_action.da,
@@ -2809,10 +2770,6 @@
 				 data->rx_action.freq) == 0)
 			break;
 #endif /* CONFIG_GAS */
-		if (data->rx_action.category == WLAN_ACTION_WNM) {
-			wnm_action_rx(wpa_s, &data->rx_action);
-			break;
-		}
 #ifdef CONFIG_TDLS
 		if (data->rx_action.category == WLAN_ACTION_PUBLIC &&
 		    data->rx_action.len >= 4 &&
diff --git a/wpa_supplicant/gas_query.c b/wpa_supplicant/gas_query.c
index 0d4ca8e..27bcc7a 100644
--- a/wpa_supplicant/gas_query.c
+++ b/wpa_supplicant/gas_query.c
@@ -19,9 +19,13 @@
 #include "gas_query.h"
 
 
+/** GAS query timeout in seconds */
 #define GAS_QUERY_TIMEOUT_PERIOD 5
 
 
+/**
+ * struct gas_query_pending - Pending GAS query
+ */
 struct gas_query_pending {
 	struct dl_list list;
 	u8 addr[ETH_ALEN];
@@ -40,6 +44,9 @@
 	void *ctx;
 };
 
+/**
+ * struct gas_query - Internal GAS query data
+ */
 struct gas_query {
 	struct wpa_supplicant *wpa_s;
 	struct dl_list pending; /* struct gas_query_pending */
@@ -50,6 +57,11 @@
 static void gas_query_timeout(void *eloop_data, void *user_ctx);
 
 
+/**
+ * gas_query_init - Initialize GAS query component
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: Pointer to GAS query data or %NULL on failure
+ */
 struct gas_query * gas_query_init(struct wpa_supplicant *wpa_s)
 {
 	struct gas_query *gas;
@@ -82,6 +94,10 @@
 }
 
 
+/**
+ * gas_query_deinit - Deinitialize GAS query component
+ * @gas: GAS query data from gas_query_init()
+ */
 void gas_query_deinit(struct gas_query *gas)
 {
 	struct gas_query_pending *query, *next;
@@ -274,6 +290,17 @@
 }
 
 
+/**
+ * gas_query_rx - Indicate reception of a Public Action frame
+ * @gas: GAS query data from gas_query_init()
+ * @da: Destination MAC address of the Action frame
+ * @sa: Source MAC address of the Action frame
+ * @bssid: BSSID of the Action frame
+ * @data: Payload of the Action frame
+ * @len: Length of @data
+ * @freq: Frequency (in MHz) on which the frame was received
+ * Returns: 0 if the Public Action frame was a GAS frame or -1 if not
+ */
 int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa,
 		 const u8 *bssid, const u8 *data, size_t len, int freq)
 {
@@ -414,6 +441,16 @@
 }
 
 
+/**
+ * gas_query_req - Request a GAS query
+ * @gas: GAS query data from gas_query_init()
+ * @dst: Destination MAC address for the query
+ * @freq: Frequency (in MHz) for the channel on which to send the query
+ * @req: GAS query payload
+ * @cb: Callback function for reporting GAS query result and response
+ * @ctx: Context pointer to use with the @cb call
+ * Returns: dialog token (>= 0) on success or -1 on failure
+ */
 int gas_query_req(struct gas_query *gas, const u8 *dst, int freq,
 		  struct wpabuf *req,
 		  void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
@@ -465,6 +502,12 @@
 }
 
 
+/**
+ * gas_query_cancel - Cancel a pending GAS query
+ * @gas: GAS query data from gas_query_init()
+ * @dst: Destination MAC address for the query
+ * @dialog_token: Dialog token from gas_query_req()
+ */
 void gas_query_cancel(struct gas_query *gas, const u8 *dst, u8 dialog_token)
 {
 	struct gas_query_pending *query;
diff --git a/wpa_supplicant/gas_query.h b/wpa_supplicant/gas_query.h
index 01aba6e..5c3d161 100644
--- a/wpa_supplicant/gas_query.h
+++ b/wpa_supplicant/gas_query.h
@@ -19,6 +19,9 @@
 int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa,
 		 const u8 *bssid, const u8 *data, size_t len, int freq);
 
+/**
+ * enum gas_query_result - GAS query result
+ */
 enum gas_query_result {
 	GAS_QUERY_SUCCESS,
 	GAS_QUERY_FAILURE,
diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c
index cfe56ea..b8a8bb2 100644
--- a/wpa_supplicant/interworking.c
+++ b/wpa_supplicant/interworking.c
@@ -13,6 +13,7 @@
 #include "common/gas.h"
 #include "common/wpa_ctrl.h"
 #include "utils/pcsc_funcs.h"
+#include "utils/eloop.h"
 #include "drivers/driver.h"
 #include "eap_common/eap_defs.h"
 #include "eap_peer/eap.h"
@@ -170,6 +171,13 @@
 }
 
 
+static void interworking_continue_anqp(void *eloop_ctx, void *sock_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	interworking_next_anqp_fetch(wpa_s);
+}
+
+
 static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s,
 				      struct wpa_bss *bss)
 {
@@ -238,6 +246,8 @@
 	if (res < 0) {
 		wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
 		ret = -1;
+		eloop_register_timeout(0, 0, interworking_continue_anqp, wpa_s,
+				       NULL);
 	} else
 		wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
 			   "%u", res);
@@ -516,13 +526,18 @@
 		return 0;
 	}
 
-	if (eap->method == EAP_TYPE_PEAP &&
-	    eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL)
-		return 0;
+	if (eap->method == EAP_TYPE_PEAP) {
+		if (eap->inner_method &&
+		    eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL)
+			return 0;
+		if (!eap->inner_method &&
+		    eap_get_name(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2) == NULL)
+			return 0;
+	}
 
 	if (eap->method == EAP_TYPE_TTLS) {
 		if (eap->inner_method == 0 && eap->inner_non_eap == 0)
-			return 0;
+			return 1; /* Assume TTLS/MSCHAPv2 is used */
 		if (eap->inner_method &&
 		    eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL)
 			return 0;
@@ -948,6 +963,24 @@
 }
 
 
+static int cred_excluded_ssid(struct wpa_cred *cred, struct wpa_bss *bss)
+{
+	size_t i;
+
+	if (!cred->excluded_ssid)
+		return 0;
+
+	for (i = 0; i < cred->num_excluded_ssid; i++) {
+		struct excluded_ssid *e = &cred->excluded_ssid[i];
+		if (bss->ssid_len == e->ssid_len &&
+		    os_memcmp(bss->ssid, e->ssid, e->ssid_len) == 0)
+			return 1;
+	}
+
+	return 0;
+}
+
+
 static struct wpa_cred * interworking_credentials_available_roaming_consortium(
 	struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
 {
@@ -975,6 +1008,9 @@
 					      cred->roaming_consortium_len))
 			continue;
 
+		if (cred_excluded_ssid(cred, bss))
+			continue;
+
 		if (selected == NULL ||
 		    selected->priority < cred->priority)
 			selected = cred;
@@ -1263,11 +1299,20 @@
 					   0) < 0)
 				goto fail;
 			break;
+		default:
+			/* EAP params were not set - assume TTLS/MSCHAPv2 */
+			if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAPV2\"",
+					   0) < 0)
+				goto fail;
+			break;
 		}
 		break;
 	case EAP_TYPE_PEAP:
 		os_snprintf(buf, sizeof(buf), "\"auth=%s\"",
-			    eap_get_name(EAP_VENDOR_IETF, eap->inner_method));
+			    eap_get_name(EAP_VENDOR_IETF,
+					 eap->inner_method ?
+					 eap->inner_method :
+					 EAP_TYPE_MSCHAPV2));
 		if (wpa_config_set(ssid, "phase2", buf, 0) < 0)
 			goto fail;
 		break;
@@ -1337,6 +1382,8 @@
 		ret = plmn_id_match(bss->anqp->anqp_3gpp, imsi, mnc_len);
 		wpa_printf(MSG_DEBUG, "PLMN match %sfound", ret ? "" : "not ");
 		if (ret) {
+			if (cred_excluded_ssid(cred, bss))
+				continue;
 			if (selected == NULL ||
 			    selected->priority < cred->priority)
 				selected = cred;
@@ -1377,6 +1424,8 @@
 			if (!nai_realm_match(&realm[i], cred->realm))
 				continue;
 			if (nai_realm_find_eap(cred, &realm[i])) {
+				if (cred_excluded_ssid(cred, bss))
+					continue;
 				if (selected == NULL ||
 				    selected->priority < cred->priority)
 					selected = cred;
@@ -1459,7 +1508,8 @@
 		mnc_len = wpa_s->mnc_len;
 	}
 #endif /* CONFIG_PCSC */
-	if (imsi && build_root_nai(nai, sizeof(nai), imsi, mnc_len, 0) == 0) {
+	if (domain_names &&
+	    imsi && build_root_nai(nai, sizeof(nai), imsi, mnc_len, 0) == 0) {
 		realm = os_strchr(nai, '@');
 		if (realm)
 			realm++;
@@ -1471,7 +1521,7 @@
 	}
 #endif /* INTERWORKING_3GPP */
 
-	if (cred->domain == NULL)
+	if (domain_names == NULL || cred->domain == NULL)
 		return 0;
 
 	wpa_printf(MSG_DEBUG, "Interworking: Search for match with "
@@ -1630,6 +1680,11 @@
 			continue;
 		if (other->anqp == NULL)
 			continue;
+		if (other->anqp->roaming_consortium == NULL &&
+		    other->anqp->nai_realm == NULL &&
+		    other->anqp->anqp_3gpp == NULL &&
+		    other->anqp->domain_name == NULL)
+			continue;
 		if (!(other->flags & WPA_BSS_ANQP_FETCH_TRIED))
 			continue;
 		if (os_memcmp(bss->hessid, other->hessid, ETH_ALEN) != 0)
@@ -1655,7 +1710,7 @@
 	int found = 0;
 	const u8 *ie;
 
-	if (!wpa_s->fetch_anqp_in_progress)
+	if (eloop_terminated() || !wpa_s->fetch_anqp_in_progress)
 		return;
 
 	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
diff --git a/wpa_supplicant/offchannel.c b/wpa_supplicant/offchannel.c
index b88eab1..856eca7 100644
--- a/wpa_supplicant/offchannel.c
+++ b/wpa_supplicant/offchannel.c
@@ -132,6 +132,17 @@
 }
 
 
+/**
+ * offchannel_send_action_tx_status - TX status callback
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @dst: Destination MAC address of the transmitted Action frame
+ * @data: Transmitted frame payload
+ * @data_len: Length of @data in bytes
+ * @result: TX status
+ *
+ * This function is called whenever the driver indicates a TX status event for
+ * a frame sent by offchannel_send_action() using wpa_drv_send_action().
+ */
 void offchannel_send_action_tx_status(
 	struct wpa_supplicant *wpa_s, const u8 *dst, const u8 *data,
 	size_t data_len, enum offchannel_send_action_result result)
@@ -164,6 +175,27 @@
 }
 
 
+/**
+ * offchannel_send_action - Request off-channel Action frame TX
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @freq: The frequency in MHz indicating the channel on which the frame is to
+ *	transmitted or 0 for the current channel (only if associated)
+ * @dst: Action frame destination MAC address
+ * @src: Action frame source MAC address
+ * @bssid: Action frame BSSID
+ * @buf: Frame to transmit starting from the Category field
+ * @len: Length of @buf in bytes
+ * @wait_time: Wait time for response in milliseconds
+ * @tx_cb: Callback function for indicating TX status or %NULL for now callback
+ * @no_cck: Whether CCK rates are to be disallowed for TX rate selection
+ * Returns: 0 on success or -1 on failure
+ *
+ * This function is used to request an Action frame to be transmitted on the
+ * current operating channel or on another channel (off-channel). The actual
+ * frame transmission will be delayed until the driver is ready on the specified
+ * channel. The @wait_time parameter can be used to request the driver to remain
+ * awake on the channel to wait for a response.
+ */
 int offchannel_send_action(struct wpa_supplicant *wpa_s, unsigned int freq,
 			   const u8 *dst, const u8 *src, const u8 *bssid,
 			   const u8 *buf, size_t len, unsigned int wait_time,
@@ -266,6 +298,13 @@
 }
 
 
+/**
+ * offchannel_send_send_action_done - Notify completion of Action frame sequence
+ * @wpa_s: Pointer to wpa_supplicant data
+ *
+ * This function can be used to cancel a wait for additional response frames on
+ * the channel that was used with offchannel_send_action().
+ */
 void offchannel_send_action_done(struct wpa_supplicant *wpa_s)
 {
 	wpa_printf(MSG_DEBUG, "Off-channel: Action frame sequence done "
@@ -284,6 +323,15 @@
 }
 
 
+/**
+ * offchannel_remain_on_channel_cb - Remain-on-channel callback function
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @freq: Frequency (in MHz) of the selected channel
+ * @duration: Duration of the remain-on-channel operation in milliseconds
+ *
+ * This function is called whenever the driver notifies beginning of a
+ * remain-on-channel operation.
+ */
 void offchannel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
 				     unsigned int freq, unsigned int duration)
 {
@@ -293,6 +341,14 @@
 }
 
 
+/**
+ * offchannel_cancel_remain_on_channel_cb - Remain-on-channel stopped callback
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @freq: Frequency (in MHz) of the selected channel
+ *
+ * This function is called whenever the driver notifies termination of a
+ * remain-on-channel operation.
+ */
 void offchannel_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
 					    unsigned int freq)
 {
@@ -300,9 +356,42 @@
 }
 
 
-void offchannel_deinit(struct wpa_supplicant *wpa_s)
+/**
+ * offchannel_pending_action_tx - Check whether there is a pending Action TX
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: Pointer to pending frame or %NULL if no pending operation
+ *
+ * This function can be used to check whether there is a pending Action frame TX
+ * operation. The returned pointer should be used only for checking whether it
+ * is %NULL (no pending frame) or to print the pointer value in debug
+ * information (i.e., the pointer should not be dereferenced).
+ */
+const void * offchannel_pending_action_tx(struct wpa_supplicant *wpa_s)
+{
+	return wpa_s->pending_action_tx;
+}
+
+
+/**
+ * offchannel_clear_pending_action_tx - Clear pending Action frame TX
+ * @wpa_s: Pointer to wpa_supplicant data
+ */
+void offchannel_clear_pending_action_tx(struct wpa_supplicant *wpa_s)
 {
 	wpabuf_free(wpa_s->pending_action_tx);
 	wpa_s->pending_action_tx = NULL;
+}
+
+
+/**
+ * offchannel_deinit - Deinit off-channel operations
+ * @wpa_s: Pointer to wpa_supplicant data
+ *
+ * This function is used to free up any allocated resources for off-channel
+ * operations.
+ */
+void offchannel_deinit(struct wpa_supplicant *wpa_s)
+{
+	offchannel_clear_pending_action_tx(wpa_s);
 	eloop_cancel_timeout(wpas_send_action_cb, wpa_s, NULL);
 }
diff --git a/wpa_supplicant/offchannel.h b/wpa_supplicant/offchannel.h
index 1d3948c..0ad7e18 100644
--- a/wpa_supplicant/offchannel.h
+++ b/wpa_supplicant/offchannel.h
@@ -29,5 +29,7 @@
 void offchannel_send_action_tx_status(
 	struct wpa_supplicant *wpa_s, const u8 *dst, const u8 *data,
 	size_t data_len, enum offchannel_send_action_result result);
+const void * offchannel_pending_action_tx(struct wpa_supplicant *wpa_s);
+void offchannel_clear_pending_action_tx(struct wpa_supplicant *wpa_s);
 
 #endif /* OFFCHANNEL_H */
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index c2d702e..523178f 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -112,7 +112,7 @@
 	for (i = 0; i < scan_res->num; i++) {
 		struct wpa_scan_res *bss = scan_res->res[i];
 		if (p2p_scan_res_handler(wpa_s->global->p2p, bss->bssid,
-					 bss->freq, bss->level,
+					 bss->freq, bss->age, bss->level,
 					 (const u8 *) (bss + 1),
 					 bss->ie_len) > 0)
 			break;
@@ -820,15 +820,28 @@
 		wpa_printf(MSG_DEBUG, "P2P: Group setup without provisioning");
 		if (wpa_s->global->p2p_group_formation == wpa_s)
 			wpa_s->global->p2p_group_formation = NULL;
-		wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
-			"%s GO ssid=\"%s\" freq=%d passphrase=\"%s\" "
-			"go_dev_addr=" MACSTR "%s",
-			wpa_s->ifname,
-			wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
-			ssid->frequency,
-			params->passphrase ? params->passphrase : "",
-			MAC2STR(wpa_s->global->p2p_dev_addr),
-			params->persistent_group ? " [PERSISTENT]" : "");
+		if (os_strlen(params->passphrase) > 0) {
+			wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
+				"%s GO ssid=\"%s\" freq=%d passphrase=\"%s\" "
+				"go_dev_addr=" MACSTR "%s", wpa_s->ifname,
+				wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
+				ssid->frequency, params->passphrase,
+				MAC2STR(wpa_s->global->p2p_dev_addr),
+				params->persistent_group ? " [PERSISTENT]" :
+				"");
+		} else {
+			char psk[65];
+			wpa_snprintf_hex(psk, sizeof(psk), params->psk,
+					 sizeof(params->psk));
+			wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
+				"%s GO ssid=\"%s\" freq=%d psk=%s "
+				"go_dev_addr=" MACSTR "%s", wpa_s->ifname,
+				wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
+				ssid->frequency, psk,
+				MAC2STR(wpa_s->global->p2p_dev_addr),
+				params->persistent_group ? " [PERSISTENT]" :
+				"");
+		}
 
 		if (params->persistent_group)
 			network_id = wpas_p2p_store_persistent_group(
@@ -898,17 +911,20 @@
 	ssid->key_mgmt = WPA_KEY_MGMT_PSK;
 	ssid->proto = WPA_PROTO_RSN;
 	ssid->pairwise_cipher = WPA_CIPHER_CCMP;
-	ssid->passphrase = os_strdup(params->passphrase);
-	if (ssid->passphrase == NULL) {
-		wpa_msg(wpa_s, MSG_ERROR, "P2P: Failed to copy passphrase for "
-			"GO");
-		wpa_config_remove_network(wpa_s->conf, ssid->id);
-		return;
-	}
+	if (os_strlen(params->passphrase) > 0) {
+		ssid->passphrase = os_strdup(params->passphrase);
+		if (ssid->passphrase == NULL) {
+			wpa_msg(wpa_s, MSG_ERROR, "P2P: Failed to copy "
+				"passphrase for GO");
+			wpa_config_remove_network(wpa_s->conf, ssid->id);
+			return;
+		}
+	} else
+		ssid->passphrase = NULL;
 	ssid->psk_set = params->psk_set;
 	if (ssid->psk_set)
 		os_memcpy(ssid->psk, params->psk, sizeof(ssid->psk));
-	else
+	else if (ssid->passphrase)
 		wpa_config_update_psk(ssid);
 	ssid->ap_max_inactivity = wpa_s->parent->conf->p2p_go_max_inactivity;
 
@@ -3567,6 +3583,7 @@
 	}
 
 	group->p2p_in_provisioning = 1;
+	wpa_s->global->p2p_group_formation = wpa_s;
 	group->p2p_fallback_to_go_neg = wpa_s->p2p_fallback_to_go_neg;
 
 	os_memset(&res, 0, sizeof(res));
@@ -3845,12 +3862,12 @@
 {
 	wpa_printf(MSG_DEBUG, "P2P: Cancel remain-on-channel callback "
 		   "(p2p_long_listen=%d ms pending_action_tx=%p)",
-		   wpa_s->p2p_long_listen, wpa_s->pending_action_tx);
+		   wpa_s->p2p_long_listen, offchannel_pending_action_tx(wpa_s));
 	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
 		return;
 	if (p2p_listen_end(wpa_s->global->p2p, freq) > 0)
 		return; /* P2P module started a new operation */
-	if (wpa_s->pending_action_tx)
+	if (offchannel_pending_action_tx(wpa_s))
 		return;
 	if (wpa_s->p2p_long_listen > 0)
 		wpa_s->p2p_long_listen -= wpa_s->max_remain_on_chan;
@@ -4186,14 +4203,15 @@
 	params.psk_set = ssid->psk_set;
 	if (params.psk_set)
 		os_memcpy(params.psk, ssid->psk, sizeof(params.psk));
-	if (ssid->passphrase == NULL ||
-	    os_strlen(ssid->passphrase) >= sizeof(params.passphrase)) {
-		wpa_printf(MSG_DEBUG, "P2P: Invalid passphrase in persistent "
-			   "group");
-		return -1;
+	if (ssid->passphrase) {
+		if (os_strlen(ssid->passphrase) >= sizeof(params.passphrase)) {
+			wpa_printf(MSG_ERROR, "P2P: Invalid passphrase in "
+				   "persistent group");
+			return -1;
+		}
+		os_strlcpy(params.passphrase, ssid->passphrase,
+			   sizeof(params.passphrase));
 	}
-	os_strlcpy(params.passphrase, ssid->passphrase,
-		   sizeof(params.passphrase));
 	os_memcpy(params.ssid, ssid->ssid, ssid->ssid_len);
 	params.ssid_len = ssid->ssid_len;
 	params.persistent_group = 1;
@@ -4408,13 +4426,12 @@
 
 static void wpas_p2p_clear_pending_action_tx(struct wpa_supplicant *wpa_s)
 {
-	if (!wpa_s->pending_action_tx)
+	if (!offchannel_pending_action_tx(wpa_s))
 		return;
 
 	wpa_printf(MSG_DEBUG, "P2P: Drop pending Action TX due to new "
 		   "operation request");
-	wpabuf_free(wpa_s->pending_action_tx);
-	wpa_s->pending_action_tx = NULL;
+	offchannel_clear_pending_action_tx(wpa_s);
 }
 
 
@@ -5336,6 +5353,13 @@
 }
 
 
+static void wpas_p2p_scan_res_ignore(struct wpa_supplicant *wpa_s,
+				     struct wpa_scan_results *scan_res)
+{
+	wpa_printf(MSG_DEBUG, "P2P: Ignore scan results");
+}
+
+
 int wpas_p2p_cancel(struct wpa_supplicant *wpa_s)
 {
 	struct wpa_global *global = wpa_s->global;
@@ -5359,6 +5383,18 @@
 		found = 1;
 	}
 
+	if (wpa_s->scan_res_handler == wpas_p2p_scan_res_join) {
+		wpa_printf(MSG_DEBUG, "P2P: Stop pending scan for join");
+		wpa_s->scan_res_handler = wpas_p2p_scan_res_ignore;
+		found = 1;
+	}
+
+	if (wpa_s->pending_pd_before_join) {
+		wpa_printf(MSG_DEBUG, "P2P: Stop pending PD before join");
+		wpa_s->pending_pd_before_join = 0;
+		found = 1;
+	}
+
 	wpas_p2p_stop_find(wpa_s);
 
 	for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index 048e6d0..c59b8ba 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - Scanning
- * Copyright (c) 2003-2010, 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.
@@ -81,6 +81,15 @@
 #endif /* CONFIG_WPS */
 
 
+/**
+ * wpa_supplicant_enabled_networks - Check whether there are enabled networks
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: 0 if no networks are enabled, >0 if networks are enabled
+ *
+ * This function is used to figure out whether any networks (or Interworking
+ * with enabled credentials and auto_interworking) are present in the current
+ * configuration.
+ */
 int wpa_supplicant_enabled_networks(struct wpa_supplicant *wpa_s)
 {
 	struct wpa_ssid *ssid = wpa_s->conf->ssid;
@@ -199,6 +208,12 @@
 }
 
 
+/**
+ * wpa_supplicant_trigger_scan - Request driver to start a scan
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @params: Scan parameters
+ * Returns: 0 on success, -1 on failure
+ */
 int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s,
 				struct wpa_driver_scan_params *params)
 {
@@ -861,6 +876,7 @@
  * @wpa_s: Pointer to wpa_supplicant data
  * @sec: Number of seconds after which to scan
  * @usec: Number of microseconds after which to scan
+ * Returns: 0 on success or -1 otherwise
  *
  * This function is used to schedule periodic scans for neighboring
  * access points after the specified time.
@@ -882,6 +898,7 @@
 /**
  * wpa_supplicant_req_sched_scan - Start a periodic scheduled scan
  * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: 0 is sched_scan was started or -1 otherwise
  *
  * This function is used to schedule periodic scans for neighboring
  * access points repeating the scan continuously.
@@ -1135,6 +1152,16 @@
 }
 
 
+/**
+ * wpa_supplicant_notify_scanning - Indicate possible scan state change
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @scanning: Whether scanning is currently in progress
+ *
+ * This function is to generate scanning notifycations. It is called whenever
+ * there may have been a change in scanning (scan started, completed, stopped).
+ * wpas_notify_scanning() is called whenever the scanning state changed from the
+ * previously notified state.
+ */
 void wpa_supplicant_notify_scanning(struct wpa_supplicant *wpa_s,
 				    int scanning)
 {
@@ -1172,6 +1199,15 @@
 }
 
 
+/**
+ * wpa_scan_get_ie - Fetch a specified information element from a scan result
+ * @res: Scan result entry
+ * @ie: Information element identitifier (WLAN_EID_*)
+ * Returns: Pointer to the information element (id field) or %NULL if not found
+ *
+ * This function returns the first matching information element in the scan
+ * result.
+ */
 const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie)
 {
 	const u8 *end, *pos;
@@ -1191,6 +1227,15 @@
 }
 
 
+/**
+ * wpa_scan_get_vendor_ie - Fetch vendor information element from a scan result
+ * @res: Scan result entry
+ * @vendor_type: Vendor type (four octets starting the IE payload)
+ * Returns: Pointer to the information element (id field) or %NULL if not found
+ *
+ * This function returns the first matching information element in the scan
+ * result.
+ */
 const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res,
 				  u32 vendor_type)
 {
@@ -1212,6 +1257,16 @@
 }
 
 
+/**
+ * wpa_scan_get_vendor_ie_multi - Fetch vendor IE data from a scan result
+ * @res: Scan result entry
+ * @vendor_type: Vendor type (four octets starting the IE payload)
+ * Returns: Pointer to the information element payload or %NULL if not found
+ *
+ * This function returns concatenated payload of possibly fragmented vendor
+ * specific information elements in the scan result. The caller is responsible
+ * for freeing the returned buffer.
+ */
 struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res,
 					     u32 vendor_type)
 {
@@ -1243,40 +1298,6 @@
 }
 
 
-struct wpabuf * wpa_scan_get_vendor_ie_multi_beacon(
-	const struct wpa_scan_res *res, u32 vendor_type)
-{
-	struct wpabuf *buf;
-	const u8 *end, *pos;
-
-	if (res->beacon_ie_len == 0)
-		return NULL;
-	buf = wpabuf_alloc(res->beacon_ie_len);
-	if (buf == NULL)
-		return NULL;
-
-	pos = (const u8 *) (res + 1);
-	pos += res->ie_len;
-	end = pos + res->beacon_ie_len;
-
-	while (pos + 1 < end) {
-		if (pos + 2 + pos[1] > end)
-			break;
-		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
-		    vendor_type == WPA_GET_BE32(&pos[2]))
-			wpabuf_put_data(buf, pos + 2 + 4, pos[1] - 4);
-		pos += 2 + pos[1];
-	}
-
-	if (wpabuf_len(buf) == 0) {
-		wpabuf_free(buf);
-		buf = NULL;
-	}
-
-	return buf;
-}
-
-
 /*
  * Channels with a great SNR can operate at full rate. What is a great SNR?
  * This doc https://supportforums.cisco.com/docs/DOC-12954 says, "the general
@@ -1440,6 +1461,15 @@
 }
 
 
+/**
+ * wpa_supplicant_filter_bssid_match - Is the specified BSSID allowed
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bssid: BSSID to check
+ * Returns: 0 if the BSSID is filtered or 1 if not
+ *
+ * This function is used to filter out specific BSSIDs from scan reslts mainly
+ * for testing purposes (SET bssid_filter ctrl_iface command).
+ */
 int wpa_supplicant_filter_bssid_match(struct wpa_supplicant *wpa_s,
 				      const u8 *bssid)
 {
@@ -1531,6 +1561,18 @@
 }
 
 
+/**
+ * wpa_supplicant_update_scan_results - Update scan results from the driver
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function updates the BSS table within wpa_supplicant based on the
+ * currently available scan results from the driver without requesting a new
+ * scan. This is used in cases where the driver indicates an association
+ * (including roaming within ESS) and wpa_supplicant does not yet have the
+ * needed information to complete the connection (e.g., to perform validation
+ * steps in 4-way handshake).
+ */
 int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s)
 {
 	struct wpa_scan_results *scan_res;
diff --git a/wpa_supplicant/scan.h b/wpa_supplicant/scan.h
index b0ddf97..5096287 100644
--- a/wpa_supplicant/scan.h
+++ b/wpa_supplicant/scan.h
@@ -30,8 +30,6 @@
 				  u32 vendor_type);
 struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res,
 					     u32 vendor_type);
-struct wpabuf * wpa_scan_get_vendor_ie_multi_beacon(
-	const struct wpa_scan_res *res, u32 vendor_type);
 int wpa_supplicant_filter_bssid_match(struct wpa_supplicant *wpa_s,
 				      const u8 *bssid);
 
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index e273cb3..30f9779 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -1,6 +1,6 @@
 /*
  * wpa_supplicant - SME
- * Copyright (c) 2009-2010, 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.
@@ -14,6 +14,7 @@
 #include "common/ieee802_11_common.h"
 #include "eapol_supp/eapol_supp_sm.h"
 #include "common/wpa_common.h"
+#include "common/sae.h"
 #include "rsn_supp/wpa.h"
 #include "rsn_supp/pmksa_cache.h"
 #include "config.h"
@@ -41,20 +42,78 @@
 
 #ifdef CONFIG_SAE
 
-static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s)
+static int index_within_array(const int *array, int idx)
+{
+	int i;
+	for (i = 0; i < idx; i++) {
+		if (array[i] == -1)
+			return 0;
+	}
+	return 1;
+}
+
+
+static int sme_set_sae_group(struct wpa_supplicant *wpa_s)
+{
+	int *groups = wpa_s->conf->sae_groups;
+	int default_groups[] = { 19, 20, 21, 25, 26 };
+
+	if (!groups)
+		groups = default_groups;
+
+	/* Configuration may have changed, so validate current index */
+	if (!index_within_array(groups, wpa_s->sme.sae_group_index))
+		return -1;
+
+	for (;;) {
+		int group = groups[wpa_s->sme.sae_group_index];
+		if (group < 0)
+			break;
+		if (sae_set_group(&wpa_s->sme.sae, group) == 0) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "SME: Selected SAE group %d",
+				wpa_s->sme.sae.group);
+		       return 0;
+		}
+		wpa_s->sme.sae_group_index++;
+	}
+
+	return -1;
+}
+
+
+static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s,
+						 struct wpa_ssid *ssid,
+						 const u8 *bssid)
 {
 	struct wpabuf *buf;
+	size_t len;
 
-	buf = wpabuf_alloc(4 + 2);
+	if (ssid->passphrase == NULL) {
+		wpa_printf(MSG_DEBUG, "SAE: No password available");
+		return NULL;
+	}
+
+	if (sme_set_sae_group(wpa_s) < 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Failed to select group");
+		return NULL;
+	}
+
+	if (sae_prepare_commit(wpa_s->own_addr, bssid,
+			       (u8 *) ssid->passphrase,
+			       os_strlen(ssid->passphrase),
+			       &wpa_s->sme.sae) < 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE");
+		return NULL;
+	}
+
+	len = wpa_s->sme.sae_token ? wpabuf_len(wpa_s->sme.sae_token) : 0;
+	buf = wpabuf_alloc(4 + SAE_COMMIT_MAX_LEN + len);
 	if (buf == NULL)
 		return NULL;
 
 	wpabuf_put_le16(buf, 1); /* Transaction seq# */
 	wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
-	wpabuf_put_le16(buf, 19); /* Finite Cyclic Group */
-	/* TODO: Anti-Clogging Token (if requested) */
-	/* TODO: Scalar */
-	/* TODO: Element */
+	sae_write_commit(&wpa_s->sme.sae, buf, wpa_s->sme.sae_token);
 
 	return buf;
 }
@@ -64,15 +123,13 @@
 {
 	struct wpabuf *buf;
 
-	buf = wpabuf_alloc(4 + 2);
+	buf = wpabuf_alloc(4 + SAE_CONFIRM_MAX_LEN);
 	if (buf == NULL)
 		return NULL;
 
 	wpabuf_put_le16(buf, 2); /* Transaction seq# */
 	wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
-	wpabuf_put_le16(buf, wpa_s->sme.sae_send_confirm);
-	wpa_s->sme.sae_send_confirm++;
-	/* TODO: Confirm */
+	sae_write_confirm(&wpa_s->sme.sae, buf);
 
 	return buf;
 }
@@ -94,6 +151,8 @@
 #endif /* CONFIG_IEEE80211R */
 	int i, bssid_changed;
 	struct wpabuf *resp = NULL;
+	u8 ext_capab[10];
+	int ext_capab_len;
 
 	if (bss == NULL) {
 		wpa_msg(wpa_s, MSG_ERROR, "SME: No scan result available for "
@@ -308,35 +367,30 @@
 	}
 #endif /* CONFIG_HS20 */
 
-#ifdef CONFIG_INTERWORKING
-	if (wpa_s->conf->interworking) {
+	ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab);
+	if (ext_capab_len > 0) {
 		u8 *pos = wpa_s->sme.assoc_req_ie;
 		if (wpa_s->sme.assoc_req_ie_len > 0 && pos[0] == WLAN_EID_RSN)
 			pos += 2 + pos[1];
-		os_memmove(pos + 6, pos,
+		os_memmove(pos + ext_capab_len, pos,
 			   wpa_s->sme.assoc_req_ie_len -
 			   (pos - wpa_s->sme.assoc_req_ie));
-		wpa_s->sme.assoc_req_ie_len += 6;
-		*pos++ = WLAN_EID_EXT_CAPAB;
-		*pos++ = 4;
-		*pos++ = 0x00;
-		*pos++ = 0x00;
-		*pos++ = 0x00;
-		*pos++ = 0x80; /* Bit 31 - Interworking */
+		wpa_s->sme.assoc_req_ie_len += ext_capab_len;
+		os_memcpy(pos, ext_capab, ext_capab_len);
 	}
-#endif /* CONFIG_INTERWORKING */
 
 #ifdef CONFIG_SAE
 	if (params.auth_alg == WPA_AUTH_ALG_SAE) {
 		if (start)
-			resp = sme_auth_build_sae_commit(wpa_s);
+			resp = sme_auth_build_sae_commit(wpa_s, ssid,
+							 bss->bssid);
 		else
 			resp = sme_auth_build_sae_confirm(wpa_s);
 		if (resp == NULL)
 			return;
 		params.sae_data = wpabuf_head(resp);
 		params.sae_data_len = wpabuf_len(resp);
-		wpa_s->sme.sae_state = start ? SME_SAE_COMMIT : SME_SAE_CONFIRM;
+		wpa_s->sme.sae.state = start ? SAE_COMMITTED : SAE_CONFIRMED;
 	}
 #endif /* CONFIG_SAE */
 
@@ -381,53 +435,48 @@
 void sme_authenticate(struct wpa_supplicant *wpa_s,
 		      struct wpa_bss *bss, struct wpa_ssid *ssid)
 {
-	wpa_s->sme.sae_state = SME_SAE_INIT;
-	wpa_s->sme.sae_send_confirm = 0;
+#ifdef CONFIG_SAE
+	wpa_s->sme.sae.state = SAE_NOTHING;
+	wpa_s->sme.sae.send_confirm = 0;
+#endif /* CONFIG_SAE */
 	sme_send_authentication(wpa_s, bss, ssid, 1);
 }
 
 
 #ifdef CONFIG_SAE
 
-static int sme_sae_process_commit(struct wpa_supplicant *wpa_s, const u8 *data,
-				  size_t len)
-{
-	/* Check Finite Cyclic Group */
-	if (len < 2)
-		return -1;
-	if (WPA_GET_LE16(data) != 19) {
-		wpa_printf(MSG_DEBUG, "SAE: Unsupported Finite Cyclic Group %u",
-			   WPA_GET_LE16(data));
-		return -1;
-	}
-
-	/* TODO */
-
-	return 0;
-}
-
-
-static int sme_sae_process_confirm(struct wpa_supplicant *wpa_s, const u8 *data,
-				   size_t len)
-{
-	u16 rc;
-
-	if (len < 2)
-		return -1;
-	rc = WPA_GET_LE16(data);
-	wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", rc);
-
-	/* TODO */
-	return 0;
-}
-
-
 static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
 			u16 status_code, const u8 *data, size_t len)
 {
 	wpa_dbg(wpa_s, MSG_DEBUG, "SME: SAE authentication transaction %u "
 		"status code %u", auth_transaction, status_code);
-	wpa_hexdump(MSG_DEBUG, "SME: SAE fields", data, len);
+
+	if (auth_transaction == 1 &&
+	    status_code == WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ &&
+	    wpa_s->sme.sae.state == SAE_COMMITTED &&
+	    wpa_s->current_bss && wpa_s->current_ssid) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "SME: SAE anti-clogging token "
+			"requested");
+		wpabuf_free(wpa_s->sme.sae_token);
+		wpa_s->sme.sae_token = wpabuf_alloc_copy(data, len);
+		sme_send_authentication(wpa_s, wpa_s->current_bss,
+					wpa_s->current_ssid, 1);
+		return 0;
+	}
+
+	if (auth_transaction == 1 &&
+	    status_code == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED &&
+	    wpa_s->sme.sae.state == SAE_COMMITTED &&
+	    wpa_s->current_bss && wpa_s->current_ssid) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "SME: SAE group not supported");
+		wpa_s->sme.sae_group_index++;
+		if (sme_set_sae_group(wpa_s) < 0)
+			return -1; /* no other groups enabled */
+		wpa_dbg(wpa_s, MSG_DEBUG, "SME: Try next enabled SAE group");
+		sme_send_authentication(wpa_s, wpa_s->current_bss,
+					wpa_s->current_ssid, 1);
+		return 0;
+	}
 
 	if (status_code != WLAN_STATUS_SUCCESS)
 		return -1;
@@ -437,19 +486,32 @@
 		if (wpa_s->current_bss == NULL ||
 		    wpa_s->current_ssid == NULL)
 			return -1;
-		if (wpa_s->sme.sae_state != SME_SAE_COMMIT)
+		if (wpa_s->sme.sae.state != SAE_COMMITTED)
 			return -1;
-		if (sme_sae_process_commit(wpa_s, data, len) < 0)
+		if (sae_parse_commit(&wpa_s->sme.sae, data, len, NULL, NULL,
+				     wpa_s->conf->sae_groups) !=
+		    WLAN_STATUS_SUCCESS)
 			return -1;
+
+		if (sae_process_commit(&wpa_s->sme.sae) < 0) {
+			wpa_printf(MSG_DEBUG, "SAE: Failed to process peer "
+				   "commit");
+			return -1;
+		}
+
+		wpabuf_free(wpa_s->sme.sae_token);
+		wpa_s->sme.sae_token = NULL;
 		sme_send_authentication(wpa_s, wpa_s->current_bss,
 					wpa_s->current_ssid, 0);
 		return 0;
 	} else if (auth_transaction == 2) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE confirm");
-		if (wpa_s->sme.sae_state != SME_SAE_CONFIRM)
+		if (wpa_s->sme.sae.state != SAE_CONFIRMED)
 			return -1;
-		if (sme_sae_process_confirm(wpa_s, data, len) < 0)
+		if (sae_check_confirm(&wpa_s->sme.sae, data, len) < 0)
 			return -1;
+		wpa_s->sme.sae.state = SAE_ACCEPTED;
+		sae_clear_temp_data(&wpa_s->sme.sae);
 		return 1;
 	}
 
@@ -503,6 +565,10 @@
 		}
 		if (res != 1)
 			return;
+
+		wpa_printf(MSG_DEBUG, "SME: SAE completed - setting PMK for "
+			   "4-way handshake");
+		wpa_sm_set_pmk(wpa_s->wpa, wpa_s->sme.sae.pmk, PMK_LEN);
 	}
 #endif /* CONFIG_SAE */
 
@@ -577,8 +643,9 @@
 	params.wpa_ie = wpa_s->sme.assoc_req_ie_len ?
 		wpa_s->sme.assoc_req_ie : NULL;
 	params.wpa_ie_len = wpa_s->sme.assoc_req_ie_len;
-	params.pairwise_suite = cipher_suite2driver(wpa_s->pairwise_cipher);
-	params.group_suite = cipher_suite2driver(wpa_s->group_cipher);
+	params.pairwise_suite =
+		wpa_cipher_to_suite_driver(wpa_s->pairwise_cipher);
+	params.group_suite = wpa_cipher_to_suite_driver(wpa_s->group_cipher);
 #ifdef CONFIG_HT_OVERRIDES
 	os_memset(&htcaps, 0, sizeof(htcaps));
 	os_memset(&htcaps_mask, 0, sizeof(htcaps_mask));
@@ -805,6 +872,11 @@
 #ifdef CONFIG_IEEE80211W
 	sme_stop_sa_query(wpa_s);
 #endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_SAE
+	wpabuf_free(wpa_s->sme.sae_token);
+	wpa_s->sme.sae_token = NULL;
+	sae_clear_data(&wpa_s->sme.sae);
+#endif /* CONFIG_SAE */
 
 	eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL);
 	eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL);
diff --git a/wpa_supplicant/tests/test_wpa.c b/wpa_supplicant/tests/test_wpa.c
index 0d659ad..ba2be6f 100644
--- a/wpa_supplicant/tests/test_wpa.c
+++ b/wpa_supplicant/tests/test_wpa.c
@@ -14,7 +14,7 @@
 #include "../config.h"
 #include "rsn_supp/wpa.h"
 #include "rsn_supp/wpa_ie.h"
-#include "../hostapd/wpa.h"
+#include "ap/wpa_auth.h"
 
 
 extern int wpa_debug_level;
diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c
index 98ce966..4d9e453 100644
--- a/wpa_supplicant/wnm_sta.c
+++ b/wpa_supplicant/wnm_sta.c
@@ -11,12 +11,12 @@
 #include "utils/common.h"
 #include "common/ieee802_11_defs.h"
 #include "rsn_supp/wpa.h"
-#include "../wpa_supplicant/wpa_supplicant_i.h"
-#include "../wpa_supplicant/driver_i.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "scan.h"
 
 #define MAX_TFS_IE_LEN  1024
 
-#ifdef CONFIG_IEEE80211V
 
 /* get the TFS IE from driver */
 static int ieee80211_11_get_tfs_ie(struct wpa_supplicant *wpa_s, u8 *buf,
@@ -41,7 +41,7 @@
 
 /* MLME-SLEEPMODE.request */
 int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s,
-				 u8 action, u8 intval)
+				 u8 action, u16 intval, struct wpabuf *tfs_req)
 {
 	struct ieee80211_mgmt *mgmt;
 	int res;
@@ -53,6 +53,11 @@
 	enum wnm_oper tfs_oper = action == 0 ? WNM_SLEEP_TFS_REQ_IE_ADD :
 		WNM_SLEEP_TFS_REQ_IE_NONE;
 
+	wpa_printf(MSG_DEBUG, "WNM: Request to send WNM-Sleep Mode Request "
+		   "action=%s to " MACSTR,
+		   action == 0 ? "enter" : "exit",
+		   MAC2STR(wpa_s->bssid));
+
 	/* WNM-Sleep Mode IE */
 	wnmsleep_ie_len = sizeof(struct wnm_sleep_element);
 	wnmsleep_ie = os_zalloc(sizeof(struct wnm_sleep_element));
@@ -62,25 +67,41 @@
 	wnmsleep_ie->len = wnmsleep_ie_len - 2;
 	wnmsleep_ie->action_type = action;
 	wnmsleep_ie->status = WNM_STATUS_SLEEP_ACCEPT;
-	wnmsleep_ie->intval = intval;
+	wnmsleep_ie->intval = host_to_le16(intval);
+	wpa_hexdump(MSG_DEBUG, "WNM: WNM-Sleep Mode element",
+		    (u8 *) wnmsleep_ie, wnmsleep_ie_len);
 
 	/* TFS IE(s) */
-	wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN);
-	if (wnmtfs_ie == NULL) {
-		os_free(wnmsleep_ie);
-		return -1;
+	if (tfs_req) {
+		wnmtfs_ie_len = wpabuf_len(tfs_req);
+		wnmtfs_ie = os_malloc(wnmtfs_ie_len);
+		if (wnmtfs_ie == NULL) {
+			os_free(wnmsleep_ie);
+			return -1;
+		}
+		os_memcpy(wnmtfs_ie, wpabuf_head(tfs_req), wnmtfs_ie_len);
+	} else {
+		wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN);
+		if (wnmtfs_ie == NULL) {
+			os_free(wnmsleep_ie);
+			return -1;
+		}
+		if (ieee80211_11_get_tfs_ie(wpa_s, wnmtfs_ie, &wnmtfs_ie_len,
+					    tfs_oper)) {
+			wnmtfs_ie_len = 0;
+			os_free(wnmtfs_ie);
+			wnmtfs_ie = NULL;
+		}
 	}
-	if (ieee80211_11_get_tfs_ie(wpa_s, wnmtfs_ie, &wnmtfs_ie_len,
-				    tfs_oper)) {
-		wnmtfs_ie_len = 0;
-		os_free(wnmtfs_ie);
-		wnmtfs_ie = NULL;
-	}
+	wpa_hexdump(MSG_DEBUG, "WNM: TFS Request element",
+		    (u8 *) wnmtfs_ie, wnmtfs_ie_len);
 
 	mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + wnmtfs_ie_len);
 	if (mgmt == NULL) {
 		wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
 			   "WNM-Sleep Request action frame");
+		os_free(wnmsleep_ie);
+		os_free(wnmtfs_ie);
 		return -1;
 	}
 
@@ -91,6 +112,7 @@
 					   WLAN_FC_STYPE_ACTION);
 	mgmt->u.action.category = WLAN_ACTION_WNM;
 	mgmt->u.action.u.wnm_sleep_req.action = WNM_SLEEP_MODE_REQ;
+	mgmt->u.action.u.wnm_sleep_req.dialogtoken = 1;
 	os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable, wnmsleep_ie,
 		  wnmsleep_ie_len);
 	/* copy TFS IE here */
@@ -117,6 +139,92 @@
 }
 
 
+static void wnm_sleep_mode_enter_success(struct wpa_supplicant *wpa_s,
+					 u8 *tfsresp_ie_start,
+					 u8 *tfsresp_ie_end)
+{
+	wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_CONFIRM,
+			 wpa_s->bssid, NULL, NULL);
+	/* remove GTK/IGTK ?? */
+
+	/* set the TFS Resp IE(s) */
+	if (tfsresp_ie_start && tfsresp_ie_end &&
+	    tfsresp_ie_end - tfsresp_ie_start >= 0) {
+		u16 tfsresp_ie_len;
+		tfsresp_ie_len = (tfsresp_ie_end + tfsresp_ie_end[1] + 2) -
+			tfsresp_ie_start;
+		wpa_printf(MSG_DEBUG, "TFS Resp IE(s) found");
+		/* pass the TFS Resp IE(s) to driver for processing */
+		if (ieee80211_11_set_tfs_ie(wpa_s, wpa_s->bssid,
+					    tfsresp_ie_start,
+					    &tfsresp_ie_len,
+					    WNM_SLEEP_TFS_RESP_IE_SET))
+			wpa_printf(MSG_DEBUG, "WNM: Fail to set TFS Resp IE");
+	}
+}
+
+
+static void wnm_sleep_mode_exit_success(struct wpa_supplicant *wpa_s,
+					const u8 *frm, u16 key_len_total)
+{
+	u8 *ptr, *end;
+	u8 gtk_len;
+
+	wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_CONFIRM,  wpa_s->bssid,
+			 NULL, NULL);
+
+	/* Install GTK/IGTK */
+
+	/* point to key data field */
+	ptr = (u8 *) frm + 1 + 1 + 2;
+	end = ptr + key_len_total;
+	wpa_hexdump_key(MSG_DEBUG, "WNM: Key Data", ptr, key_len_total);
+
+	while (ptr + 1 < end) {
+		if (ptr + 2 + ptr[1] > end) {
+			wpa_printf(MSG_DEBUG, "WNM: Invalid Key Data element "
+				   "length");
+			if (end > ptr) {
+				wpa_hexdump(MSG_DEBUG, "WNM: Remaining data",
+					    ptr, end - ptr);
+			}
+			break;
+		}
+		if (*ptr == WNM_SLEEP_SUBELEM_GTK) {
+			if (ptr[1] < 11 + 5) {
+				wpa_printf(MSG_DEBUG, "WNM: Too short GTK "
+					   "subelem");
+				break;
+			}
+			gtk_len = *(ptr + 4);
+			if (ptr[1] < 11 + gtk_len ||
+			    gtk_len < 5 || gtk_len > 32) {
+				wpa_printf(MSG_DEBUG, "WNM: Invalid GTK "
+					   "subelem");
+				break;
+			}
+			wpa_wnmsleep_install_key(
+				wpa_s->wpa,
+				WNM_SLEEP_SUBELEM_GTK,
+				ptr);
+			ptr += 13 + gtk_len;
+#ifdef CONFIG_IEEE80211W
+		} else if (*ptr == WNM_SLEEP_SUBELEM_IGTK) {
+			if (ptr[1] < 2 + 6 + WPA_IGTK_LEN) {
+				wpa_printf(MSG_DEBUG, "WNM: Too short IGTK "
+					   "subelem");
+				break;
+			}
+			wpa_wnmsleep_install_key(wpa_s->wpa,
+						 WNM_SLEEP_SUBELEM_IGTK, ptr);
+			ptr += 10 + WPA_IGTK_LEN;
+#endif /* CONFIG_IEEE80211W */
+		} else
+			break; /* skip the loop */
+	}
+}
+
+
 static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s,
 					const u8 *frm, int len)
 {
@@ -126,21 +234,25 @@
 	 */
 	u8 *pos = (u8 *) frm; /* point to action field */
 	u16 key_len_total = le_to_host16(*((u16 *)(frm+2)));
-	u8 gtk_len;
-#ifdef CONFIG_IEEE80211W
-	u8 igtk_len;
-#endif /* CONFIG_IEEE80211W */
 	struct wnm_sleep_element *wnmsleep_ie = NULL;
 	/* multiple TFS Resp IE (assuming consecutive) */
 	u8 *tfsresp_ie_start = NULL;
 	u8 *tfsresp_ie_end = NULL;
-	u16 tfsresp_ie_len = 0;
 
 	wpa_printf(MSG_DEBUG, "action=%d token = %d key_len_total = %d",
 		   frm[0], frm[1], key_len_total);
 	pos += 4 + key_len_total;
+	if (pos > frm + len) {
+		wpa_printf(MSG_INFO, "WNM: Too short frame for Key Data field");
+		return;
+	}
 	while (pos - frm < len) {
 		u8 ie_len = *(pos + 1);
+		if (pos + 2 + ie_len > frm + len) {
+			wpa_printf(MSG_INFO, "WNM: Invalid IE len %u", ie_len);
+			break;
+		}
+		wpa_hexdump(MSG_DEBUG, "WNM: Element", pos, 2 + ie_len);
 		if (*pos == WLAN_EID_WNMSLEEP)
 			wnmsleep_ie = (struct wnm_sleep_element *) pos;
 		else if (*pos == WLAN_EID_TFS_RESP) {
@@ -157,86 +269,151 @@
 		return;
 	}
 
-	if (wnmsleep_ie->status == WNM_STATUS_SLEEP_ACCEPT) {
+	if (wnmsleep_ie->status == WNM_STATUS_SLEEP_ACCEPT ||
+	    wnmsleep_ie->status == WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) {
 		wpa_printf(MSG_DEBUG, "Successfully recv WNM-Sleep Response "
 			   "frame (action=%d, intval=%d)",
 			   wnmsleep_ie->action_type, wnmsleep_ie->intval);
-		if (wnmsleep_ie->action_type == 0) {
-			wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_CONFIRM,
-					 wpa_s->bssid, NULL, NULL);
-			/* remove GTK/IGTK ?? */
-
-			/* set the TFS Resp IE(s) */
-			if (tfsresp_ie_start && tfsresp_ie_end &&
-			    tfsresp_ie_end - tfsresp_ie_start >= 0) {
-				tfsresp_ie_len = (tfsresp_ie_end +
-						  tfsresp_ie_end[1] + 2) -
-					tfsresp_ie_start;
-				wpa_printf(MSG_DEBUG, "TFS Resp IE(s) found");
-				/*
-				 * pass the TFS Resp IE(s) to driver for
-				 * processing
-				 */
-				if (ieee80211_11_set_tfs_ie(
-					    wpa_s, wpa_s->bssid,
-					    tfsresp_ie_start,
-					    &tfsresp_ie_len,
-					    WNM_SLEEP_TFS_RESP_IE_SET))
-					wpa_printf(MSG_DEBUG, "Fail to set "
-						   "TFS Resp IE");
-			}
-		} else if (wnmsleep_ie->action_type == 1) {
-			wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_CONFIRM,
-					 wpa_s->bssid, NULL, NULL);
-			/* Install GTK/IGTK */
-			do {
-				/* point to key data field */
-				u8 *ptr = (u8 *) frm + 1 + 1 + 2;
-				while (ptr < (u8 *) frm + 4 + key_len_total) {
-					if (*ptr == WNM_SLEEP_SUBELEM_GTK) {
-						gtk_len = *(ptr + 4);
-						wpa_wnmsleep_install_key(
-							wpa_s->wpa,
-							WNM_SLEEP_SUBELEM_GTK,
-							ptr);
-						ptr += 13 + gtk_len;
-#ifdef CONFIG_IEEE80211W
-					} else if (*ptr ==
-						   WNM_SLEEP_SUBELEM_IGTK) {
-						igtk_len = WPA_IGTK_LEN;
-						wpa_wnmsleep_install_key(
-							wpa_s->wpa,
-							WNM_SLEEP_SUBELEM_IGTK,
-							ptr);
-						ptr += 10 + WPA_IGTK_LEN;
-#endif /* CONFIG_IEEE80211W */
-					} else
-						break; /* skip the loop */
-				}
-			} while(0);
+		if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER) {
+			wnm_sleep_mode_enter_success(wpa_s, tfsresp_ie_start,
+						     tfsresp_ie_end);
+		} else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) {
+			wnm_sleep_mode_exit_success(wpa_s, frm, key_len_total);
 		}
 	} else {
 		wpa_printf(MSG_DEBUG, "Reject recv WNM-Sleep Response frame "
 			   "(action=%d, intval=%d)",
 			   wnmsleep_ie->action_type, wnmsleep_ie->intval);
-		if (wnmsleep_ie->action_type == 0)
+		if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER)
 			wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_FAIL,
 					 wpa_s->bssid, NULL, NULL);
-		else if (wnmsleep_ie->action_type == 1)
+		else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT)
 			wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_FAIL,
 					 wpa_s->bssid, NULL, NULL);
 	}
 }
 
 
+static void wnm_send_bss_transition_mgmt_resp(struct wpa_supplicant *wpa_s,
+					      u8 dialog_token, u8 status,
+					      u8 delay, const u8 *target_bssid)
+{
+	u8 buf[1000], *pos;
+	struct ieee80211_mgmt *mgmt;
+	size_t len;
+
+	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Response "
+		   "to " MACSTR " dialog_token=%u status=%u delay=%d",
+		   MAC2STR(wpa_s->bssid), dialog_token, status, delay);
+
+	mgmt = (struct ieee80211_mgmt *) buf;
+	os_memset(&buf, 0, sizeof(buf));
+	os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
+	os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
+	os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
+	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					   WLAN_FC_STYPE_ACTION);
+	mgmt->u.action.category = WLAN_ACTION_WNM;
+	mgmt->u.action.u.bss_tm_resp.action = WNM_BSS_TRANS_MGMT_RESP;
+	mgmt->u.action.u.bss_tm_resp.dialog_token = dialog_token;
+	mgmt->u.action.u.bss_tm_resp.status_code = status;
+	mgmt->u.action.u.bss_tm_resp.bss_termination_delay = delay;
+	pos = mgmt->u.action.u.bss_tm_resp.variable;
+	if (target_bssid) {
+		os_memcpy(pos, target_bssid, ETH_ALEN);
+		pos += ETH_ALEN;
+	}
+
+	len = pos - (u8 *) &mgmt->u.action.category;
+
+	wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+			    wpa_s->own_addr, wpa_s->bssid,
+			    &mgmt->u.action.category, len, 0);
+}
+
+
+static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
+					     const u8 *pos, const u8 *end,
+					     int reply)
+{
+	u8 dialog_token;
+	u8 mode;
+	u16 disassoc_timer;
+
+	if (pos + 5 > end)
+		return;
+
+	dialog_token = pos[0];
+	mode = pos[1];
+	disassoc_timer = WPA_GET_LE16(pos + 2);
+
+	wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Request: "
+		   "dialog_token=%u request_mode=0x%x "
+		   "disassoc_timer=%u validity_interval=%u",
+		   dialog_token, mode, disassoc_timer, pos[4]);
+	pos += 5;
+	if (mode & 0x08)
+		pos += 12; /* BSS Termination Duration */
+	if (mode & 0x10) {
+		char url[256];
+		if (pos + 1 > end || pos + 1 + pos[0] > end) {
+			wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition "
+				   "Management Request (URL)");
+			return;
+		}
+		os_memcpy(url, pos + 1, pos[0]);
+		url[pos[0]] = '\0';
+		wpa_msg(wpa_s, MSG_INFO, "WNM: ESS Disassociation Imminent - "
+			"session_info_url=%s", url);
+	}
+
+	if (mode & 0x04) {
+		wpa_msg(wpa_s, MSG_INFO, "WNM: Disassociation Imminent - "
+			"Disassociation Timer %u", disassoc_timer);
+		if (disassoc_timer && !wpa_s->scanning) {
+			/* TODO: mark current BSS less preferred for
+			 * selection */
+			wpa_printf(MSG_DEBUG, "Trying to find another BSS");
+			wpa_supplicant_req_scan(wpa_s, 0, 0);
+		}
+	}
+
+	if (reply) {
+		/* TODO: add support for reporting Accept */
+		wnm_send_bss_transition_mgmt_resp(wpa_s, dialog_token,
+						  1 /* Reject - unspecified */,
+						  0, NULL);
+	}
+}
+
+
 void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
 			      struct rx_action *action)
 {
-	u8 *pos = (u8 *) action->data; /* point to action field */
-	u8 act = *pos++;
-	/* u8 dialog_token = *pos++; */
+	const u8 *pos, *end;
+	u8 act;
+
+	if (action->data == NULL || action->len == 0)
+		return;
+
+	pos = action->data;
+	end = pos + action->len;
+	act = *pos++;
+
+	wpa_printf(MSG_DEBUG, "WNM: RX action %u from " MACSTR,
+		   act, MAC2STR(action->sa));
+	if (wpa_s->wpa_state < WPA_ASSOCIATED ||
+	    os_memcmp(action->sa, wpa_s->bssid, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "WNM: Ignore unexpected WNM Action "
+			   "frame");
+		return;
+	}
 
 	switch (act) {
+	case WNM_BSS_TRANS_MGMT_REQ:
+		ieee802_11_rx_bss_trans_mgmt_req(wpa_s, pos, end,
+						 !(action->da[0] & 0x01));
+		break;
 	case WNM_SLEEP_MODE_RESP:
 		ieee802_11_rx_wnmsleep_resp(wpa_s, action->data, action->len);
 		break;
@@ -244,5 +421,3 @@
 		break;
 	}
 }
-
-#endif /* CONFIG_IEEE80211V */
diff --git a/wpa_supplicant/wnm_sta.h b/wpa_supplicant/wnm_sta.h
index ba2535b..3f9d88b 100644
--- a/wpa_supplicant/wnm_sta.h
+++ b/wpa_supplicant/wnm_sta.h
@@ -13,7 +13,7 @@
 struct wpa_supplicant;
 
 int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s,
-				 u8 action, u8 intval);
+				 u8 action, u16 intval, struct wpabuf *tfs_req);
 
 void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
 			      struct rx_action *action);
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index 4e7c81c..cc08009 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -28,7 +28,7 @@
 
 static const char *wpa_cli_version =
 "wpa_cli v" VERSION_STR "\n"
-"Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi> and contributors";
+"Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi> and contributors";
 
 
 static const char *wpa_cli_license =
@@ -2273,6 +2273,16 @@
 #endif /* CONFIG_AUTOSCAN */
 
 
+#ifdef CONFIG_WNM
+
+static int wpa_cli_cmd_wnm_sleep(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "WNM_SLEEP", 0, argc, argv);
+}
+
+#endif /* CONFIG_WNM */
+
+
 static int wpa_cli_cmd_raw(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
 	if (argc == 0)
@@ -2737,6 +2747,10 @@
 	{ "autoscan", wpa_cli_cmd_autoscan, NULL, cli_cmd_flag_none,
 	  "[params] = Set or unset (if none) autoscan parameters" },
 #endif /* CONFIG_AUTOSCAN */
+#ifdef CONFIG_WNM
+	{ "wnm_sleep", wpa_cli_cmd_wnm_sleep, NULL, cli_cmd_flag_none,
+	  "<enter/exit> [interval=#] = enter/exit WNM-Sleep mode" },
+#endif /* CONFIG_WNM */
 	{ "raw", wpa_cli_cmd_raw, NULL, cli_cmd_flag_sensitive,
 	  "<params..> = Sent unprocessed command" },
 #ifdef ANDROID
diff --git a/wpa_supplicant/wpa_gui-qt4/wpagui.cpp b/wpa_supplicant/wpa_gui-qt4/wpagui.cpp
index 42e14f0..6bba8d2 100644
--- a/wpa_supplicant/wpa_gui-qt4/wpagui.cpp
+++ b/wpa_supplicant/wpa_gui-qt4/wpagui.cpp
@@ -709,7 +709,7 @@
 void WpaGui::helpAbout()
 {
 	QMessageBox::about(this, "wpa_gui for wpa_supplicant",
-			   "Copyright (c) 2003-2012,\n"
+			   "Copyright (c) 2003-2013,\n"
 			   "Jouni Malinen <j@w1.fi>\n"
 			   "and contributors.\n"
 			   "\n"
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index 2f7d9ea..ee1a06c 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -52,7 +52,7 @@
 
 const char *wpa_supplicant_version =
 "wpa_supplicant v" VERSION_STR "\n"
-"Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi> and contributors";
+"Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi> and contributors";
 
 const char *wpa_supplicant_license =
 "This software may be distributed under the terms of the BSD license.\n"
@@ -665,15 +665,13 @@
 		struct wpa_ssid *ssid = wpa_s->current_ssid;
 		wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_CONNECTED "- Connection to "
 			MACSTR " completed %s [id=%d id_str=%s]",
-			MAC2STR(wpa_s->bssid), wpa_s->reassociated_connection ?
-			"(reauth)" : "(auth)",
+			MAC2STR(wpa_s->bssid), "(auth)",
 			ssid ? ssid->id : -1,
 			ssid && ssid->id_str ? ssid->id_str : "");
 #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
 		wpas_clear_temp_disabled(wpa_s, ssid, 1);
 		wpa_s->extra_blacklist_count = 0;
 		wpa_s->new_connection = 0;
-		wpa_s->reassociated_connection = 1;
 		wpa_drv_set_operstate(wpa_s, 1);
 #ifndef IEEE8021X_EAPOL
 		wpa_drv_set_supp_port(wpa_s, 1);
@@ -853,26 +851,6 @@
 }
 
 
-enum wpa_cipher cipher_suite2driver(int cipher)
-{
-	switch (cipher) {
-	case WPA_CIPHER_NONE:
-		return CIPHER_NONE;
-	case WPA_CIPHER_WEP40:
-		return CIPHER_WEP40;
-	case WPA_CIPHER_WEP104:
-		return CIPHER_WEP104;
-	case WPA_CIPHER_CCMP:
-		return CIPHER_CCMP;
-	case WPA_CIPHER_GCMP:
-		return CIPHER_GCMP;
-	case WPA_CIPHER_TKIP:
-	default:
-		return CIPHER_TKIP;
-	}
-}
-
-
 enum wpa_key_mgmt key_mgmt2driver(int key_mgmt)
 {
 	switch (key_mgmt) {
@@ -1041,45 +1019,24 @@
 	}
 
 	sel = ie.group_cipher & ssid->group_cipher;
-	if (sel & WPA_CIPHER_CCMP) {
-		wpa_s->group_cipher = WPA_CIPHER_CCMP;
-		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK CCMP");
-	} else if (sel & WPA_CIPHER_GCMP) {
-		wpa_s->group_cipher = WPA_CIPHER_GCMP;
-		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK GCMP");
-	} else if (sel & WPA_CIPHER_TKIP) {
-		wpa_s->group_cipher = WPA_CIPHER_TKIP;
-		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK TKIP");
-	} else if (sel & WPA_CIPHER_WEP104) {
-		wpa_s->group_cipher = WPA_CIPHER_WEP104;
-		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK WEP104");
-	} else if (sel & WPA_CIPHER_WEP40) {
-		wpa_s->group_cipher = WPA_CIPHER_WEP40;
-		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK WEP40");
-	} else {
+	wpa_s->group_cipher = wpa_pick_group_cipher(sel);
+	if (wpa_s->group_cipher < 0) {
 		wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select group "
 			"cipher");
 		return -1;
 	}
+	wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK %s",
+		wpa_cipher_txt(wpa_s->group_cipher));
 
 	sel = ie.pairwise_cipher & ssid->pairwise_cipher;
-	if (sel & WPA_CIPHER_CCMP) {
-		wpa_s->pairwise_cipher = WPA_CIPHER_CCMP;
-		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK CCMP");
-	} else if (sel & WPA_CIPHER_GCMP) {
-		wpa_s->pairwise_cipher = WPA_CIPHER_GCMP;
-		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK GCMP");
-	} else if (sel & WPA_CIPHER_TKIP) {
-		wpa_s->pairwise_cipher = WPA_CIPHER_TKIP;
-		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK TKIP");
-	} else if (sel & WPA_CIPHER_NONE) {
-		wpa_s->pairwise_cipher = WPA_CIPHER_NONE;
-		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK NONE");
-	} else {
+	wpa_s->pairwise_cipher = wpa_pick_pairwise_cipher(sel, 1);
+	if (wpa_s->pairwise_cipher < 0) {
 		wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select pairwise "
 			"cipher");
 		return -1;
 	}
+	wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK %s",
+		wpa_cipher_txt(wpa_s->pairwise_cipher));
 
 	sel = ie.key_mgmt & ssid->key_mgmt;
 #ifdef CONFIG_SAE
@@ -1236,6 +1193,33 @@
 }
 
 
+int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf)
+{
+	u32 ext_capab = 0;
+	u8 *pos = buf;
+
+#ifdef CONFIG_INTERWORKING
+	if (wpa_s->conf->interworking)
+		ext_capab |= BIT(31); /* Interworking */
+#endif /* CONFIG_INTERWORKING */
+
+#ifdef CONFIG_WNM
+	ext_capab |= BIT(17); /* WNM-Sleep Mode */
+	ext_capab |= BIT(19); /* BSS Transition */
+#endif /* CONFIG_WNM */
+
+	if (!ext_capab)
+		return 0;
+
+	*pos++ = WLAN_EID_EXT_CAPAB;
+	*pos++ = 4;
+	WPA_PUT_LE32(pos, ext_capab);
+	pos += 4;
+
+	return pos - buf;
+}
+
+
 /**
  * wpa_supplicant_associate - Request association
  * @wpa_s: Pointer to wpa_supplicant data
@@ -1257,6 +1241,8 @@
 	struct wpa_driver_capa capa;
 	int assoc_failed = 0;
 	struct wpa_ssid *old_ssid;
+	u8 ext_capab[10];
+	int ext_capab_len;
 #ifdef CONFIG_HT_OVERRIDES
 	struct ieee80211_ht_capabilities htcaps;
 	struct ieee80211_ht_capabilities htcaps_mask;
@@ -1470,26 +1456,21 @@
 	}
 #endif /* CONFIG_HS20 */
 
-#ifdef CONFIG_INTERWORKING
-	if (wpa_s->conf->interworking) {
+	ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab);
+	if (ext_capab_len > 0) {
 		u8 *pos = wpa_ie;
 		if (wpa_ie_len > 0 && pos[0] == WLAN_EID_RSN)
 			pos += 2 + pos[1];
-		os_memmove(pos + 6, pos, wpa_ie_len - (pos - wpa_ie));
-		wpa_ie_len += 6;
-		*pos++ = WLAN_EID_EXT_CAPAB;
-		*pos++ = 4;
-		*pos++ = 0x00;
-		*pos++ = 0x00;
-		*pos++ = 0x00;
-		*pos++ = 0x80; /* Bit 31 - Interworking */
+		os_memmove(pos + ext_capab_len, pos,
+			   wpa_ie_len - (pos - wpa_ie));
+		wpa_ie_len += ext_capab_len;
+		os_memcpy(pos, ext_capab, ext_capab_len);
 	}
-#endif /* CONFIG_INTERWORKING */
 
 	wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL);
 	use_crypt = 1;
-	cipher_pairwise = cipher_suite2driver(wpa_s->pairwise_cipher);
-	cipher_group = cipher_suite2driver(wpa_s->group_cipher);
+	cipher_pairwise = wpa_cipher_to_suite_driver(wpa_s->pairwise_cipher);
+	cipher_group = wpa_cipher_to_suite_driver(wpa_s->group_cipher);
 	if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
 	    wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
 		if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE)
@@ -2609,6 +2590,28 @@
 }
 
 
+static int wpa_set_disable_sgi(struct wpa_supplicant *wpa_s,
+			       struct ieee80211_ht_capabilities *htcaps,
+			       struct ieee80211_ht_capabilities *htcaps_mask,
+			       int disabled)
+{
+	/* Masking these out disables SGI */
+	u16 msk = host_to_le16(HT_CAP_INFO_SHORT_GI20MHZ |
+			       HT_CAP_INFO_SHORT_GI40MHZ);
+
+	wpa_msg(wpa_s, MSG_DEBUG, "set_disable_sgi: %d", disabled);
+
+	if (disabled)
+		htcaps->ht_capabilities_info &= ~msk;
+	else
+		htcaps->ht_capabilities_info |= msk;
+
+	htcaps_mask->ht_capabilities_info |= msk;
+
+	return 0;
+}
+
+
 void wpa_supplicant_apply_ht_overrides(
 	struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
 	struct wpa_driver_associate_params *params)
@@ -2631,6 +2634,7 @@
 	wpa_set_ampdu_factor(wpa_s, htcaps, htcaps_mask, ssid->ampdu_factor);
 	wpa_set_ampdu_density(wpa_s, htcaps, htcaps_mask, ssid->ampdu_density);
 	wpa_set_disable_ht40(wpa_s, htcaps, htcaps_mask, ssid->disable_ht40);
+	wpa_set_disable_sgi(wpa_s, htcaps, htcaps_mask, ssid->disable_sgi);
 }
 
 #endif /* CONFIG_HT_OVERRIDES */
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index 5f0dec6..18460b8 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -281,6 +281,14 @@
 # ieee80211w parameter.
 #pmf=0
 
+# Enabled SAE finite cyclic groups in preference order
+# By default (if this parameter is not set), the mandatory group 19 (ECC group
+# defined over a 256-bit prime order field) is preferred, but other groups are
+# also enabled. If this parameter is set, the groups will be tried in the
+# indicated order. The group values are listed in the IANA registry:
+# http://www.iana.org/assignments/ipsec-registry/ipsec-registry.xml#ipsec-registry-9
+#sae_groups=21 20 19 26 25
+
 # Interworking (IEEE 802.11u)
 
 # Enable Interworking
@@ -389,6 +397,11 @@
 # phase2: Pre-configure Phase 2 (inner authentication) parameters
 #	This optional field is used with like the 'eap' parameter.
 #
+# excluded_ssid: Excluded SSID
+#	This optional field can be used to excluded specific SSID(s) from
+#	matching with the network. Multiple entries can be used to specify more
+#	than one SSID.
+#
 # for example:
 #
 #cred={
@@ -489,6 +502,23 @@
 # set, scan results that do not match any of the specified frequencies are not
 # considered when selecting a BSS.
 #
+# bgscan: Background scanning
+# wpa_supplicant behavior for background scanning can be specified by
+# configuring a bgscan module. These modules are responsible for requesting
+# background scans for the purpose of roaming within an ESS (i.e., within a
+# single network block with all the APs using the same SSID). The bgscan
+# parameter uses following format: "<bgscan module name>:<module parameters>"
+# Following bgscan modules are available:
+# simple - Periodic background scans based on signal strength
+# bgscan="simple:<short bgscan interval in seconds>:<signal strength threshold>:
+# <long interval>"
+# bgscan="simple:30:-45:300"
+# learn - Learn channels used by the network and try to avoid bgscans on other
+# channels (experimental)
+# bgscan="learn:<short bgscan interval in seconds>:<signal strength threshold>:
+# <long interval>[:<database file name>]"
+# bgscan="learn:30:-45:300:/etc/wpa_supplicant/network1.bgscan"
+#
 # proto: list of accepted protocols
 # WPA = WPA/IEEE 802.11i/D3.0
 # RSN = WPA2/IEEE 802.11i (also WPA2 can be used as an alias for RSN)
@@ -802,6 +832,34 @@
 # DTIM period in Beacon intervals for AP mode (default: 2)
 #dtim_period=2
 
+# disable_ht: Whether HT (802.11n) should be disabled.
+# 0 = HT enabled (if AP supports it)
+# 1 = HT disabled
+#
+# disable_ht40: Whether HT-40 (802.11n) should be disabled.
+# 0 = HT-40 enabled (if AP supports it)
+# 1 = HT-40 disabled
+#
+# disable_sgi: Whether SGI (short guard interval) should be disabled.
+# 0 = SGI enabled (if AP supports it)
+# 1 = SGI disabled
+#
+# ht_mcs:  Configure allowed MCS rates.
+#  Parsed as an array of bytes, in base-16 (ascii-hex)
+# ht_mcs=""                                   // Use all available (default)
+# ht_mcs="0xff 00 00 00 00 00 00 00 00 00 "   // Use MCS 0-7 only
+# ht_mcs="0xff ff 00 00 00 00 00 00 00 00 "   // Use MCS 0-15 only
+#
+# disable_max_amsdu:  Whether MAX_AMSDU should be disabled.
+# -1 = Do not make any changes.
+# 0  = Enable MAX-AMSDU if hardware supports it.
+# 1  = Disable AMSDU
+#
+# ampdu_density:  Allow overriding AMPDU density configuration.
+#  Treated as hint by the kernel.
+# -1 = Do not make any changes.
+# 0-3 = Set AMPDU density (aka factor) to specified value.
+
 # Example blocks:
 
 # Simple case: WPA-PSK, PSK as an ASCII passphrase, allow all valid ciphers
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 87d2098..ecbdedf 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -11,6 +11,7 @@
 
 #include "utils/list.h"
 #include "common/defs.h"
+#include "common/sae.h"
 #include "config_ssid.h"
 
 extern const char *wpa_supplicant_version;
@@ -249,11 +250,15 @@
 };
 
 
+/**
+ * offchannel_send_action_result - Result of offchannel send Action frame
+ */
 enum offchannel_send_action_result {
-	OFFCHANNEL_SEND_ACTION_SUCCESS /* Frame was send and acknowledged */,
-	OFFCHANNEL_SEND_ACTION_NO_ACK /* Frame was sent, but not acknowledged
+	OFFCHANNEL_SEND_ACTION_SUCCESS /**< Frame was send and acknowledged */,
+	OFFCHANNEL_SEND_ACTION_NO_ACK /**< Frame was sent, but not acknowledged
 				       */,
-	OFFCHANNEL_SEND_ACTION_FAILED /* Frame was not sent due to a failure */
+	OFFCHANNEL_SEND_ACTION_FAILED /**< Frame was not sent due to a failure
+				       */
 };
 
 struct wps_ap_info {
@@ -380,7 +385,6 @@
 	int scanning;
 	int sched_scanning;
 	int new_connection;
-	int reassociated_connection;
 
 	int eapol_received; /* number of EAPOL packets received after the
 			     * previous association event */
@@ -510,12 +514,11 @@
 		u8 sched_obss_scan;
 		u16 obss_scan_int;
 		u16 bss_max_idle_period;
-		enum {
-			SME_SAE_INIT,
-			SME_SAE_COMMIT,
-			SME_SAE_CONFIRM
-		} sae_state;
-		u16 sae_send_confirm;
+#ifdef CONFIG_SAE
+		struct sae_data sae;
+		struct wpabuf *sae_token;
+		int sae_group_index;
+#endif /* CONFIG_SAE */
 	} sme;
 #endif /* CONFIG_SME */
 
@@ -669,6 +672,8 @@
 	struct wpabuf *last_gas_resp;
 	u8 last_gas_addr[ETH_ALEN];
 	u8 last_gas_dialog_token;
+
+	unsigned int no_keep_alive:1;
 };
 
 
@@ -743,7 +748,6 @@
 void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
 			     const u8 *buf, size_t len);
 enum wpa_key_mgmt key_mgmt2driver(int key_mgmt);
-enum wpa_cipher cipher_suite2driver(int cipher);
 void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s);
 void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s);
 void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid);
@@ -756,6 +760,7 @@
 int disallowed_ssid(struct wpa_supplicant *wpa_s, const u8 *ssid,
 		    size_t ssid_len);
 void wpas_request_connection(struct wpa_supplicant *wpa_s);
+int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf);
 
 /**
  * wpa_supplicant_ctrl_iface_ctrl_rsp_handle - Handle a control response
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index 1ba4c92..4859774 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -662,6 +662,8 @@
 		return;
 	}
 
+	wpas_notify_eap_status(wpa_s, "eap parameter needed", field_name);
+
 	buflen = 100 + os_strlen(txt) + ssid->ssid_len;
 	buf = os_malloc(buflen);
 	if (buf == NULL)
@@ -806,6 +808,7 @@
 }
 
 
+#ifndef CONFIG_NO_WPA
 static void wpa_supplicant_set_rekey_offload(void *ctx, const u8 *kek,
 					     const u8 *kck,
 					     const u8 *replay_ctr)
@@ -814,6 +817,7 @@
 
 	wpa_drv_set_rekey_info(wpa_s, kek, kck, replay_ctr);
 }
+#endif /* CONFIG_NO_WPA */
 
 
 int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s)
diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
index 0239c55..711c3c0 100644
--- a/wpa_supplicant/wps_supplicant.c
+++ b/wpa_supplicant/wps_supplicant.c
@@ -423,6 +423,13 @@
 	}
 #endif /* CONFIG_NO_CONFIG_WRITE */
 
+	/*
+	 * Optimize the post-WPS scan based on the channel used during
+	 * the provisioning in case EAP-Failure is not received.
+	 */
+	wpa_s->after_wps = 5;
+	wpa_s->wps_freq = wpa_s->assoc_freq;
+
 	return 0;
 }
 
@@ -504,6 +511,7 @@
 static void wpas_wps_reenable_networks(struct wpa_supplicant *wpa_s)
 {
 	struct wpa_ssid *ssid;
+	int changed = 0;
 
 	eloop_cancel_timeout(wpas_wps_reenable_networks_cb, wpa_s, NULL);
 
@@ -512,8 +520,19 @@
 			ssid->disabled_for_connect = 0;
 			ssid->disabled = 0;
 			wpas_notify_network_enabled_changed(wpa_s, ssid);
+			changed++;
 		}
 	}
+
+	if (changed) {
+#ifndef CONFIG_NO_CONFIG_WRITE
+		if (wpa_s->conf->update_config &&
+		    wpa_config_write(wpa_s->confname, wpa_s->conf)) {
+			wpa_printf(MSG_DEBUG, "WPS: Failed to update "
+				   "configuration");
+		}
+#endif /* CONFIG_NO_CONFIG_WRITE */
+	}
 }
 
 
@@ -919,7 +938,8 @@
 		}
 	}
 #endif /* CONFIG_P2P */
-	wpa_config_set(ssid, "phase1", "\"pbc=1\"", 0);
+	if (wpa_config_set(ssid, "phase1", "\"pbc=1\"", 0) < 0)
+		return -1;
 	if (wpa_s->wps_fragment_size)
 		ssid->eap.fragment_size = wpa_s->wps_fragment_size;
 	eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
@@ -962,7 +982,8 @@
 		os_snprintf(val, sizeof(val), "\"pin=%08d dev_pw_id=%u\"",
 			    rpin, dev_pw_id);
 	}
-	wpa_config_set(ssid, "phase1", val, 0);
+	if (wpa_config_set(ssid, "phase1", val, 0) < 0)
+		return -1;
 	if (wpa_s->wps_fragment_size)
 		ssid->eap.fragment_size = wpa_s->wps_fragment_size;
 	eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
@@ -1036,7 +1057,8 @@
 	res = os_snprintf(pos, end - pos, "\"");
 	if (res < 0 || res >= end - pos)
 		return -1;
-	wpa_config_set(ssid, "phase1", val, 0);
+	if (wpa_config_set(ssid, "phase1", val, 0) < 0)
+		return -1;
 	if (wpa_s->wps_fragment_size)
 		ssid->eap.fragment_size = wpa_s->wps_fragment_size;
 	eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,