Cumulative patch from commit 3b208346ec56342fda736e62601df485ed002493

3b20834 ctrl_iface: BSS command to skip info items if parsing fails
a9c52e8 HS 2.0R2: No longer use HTTP_RAW_POST_DATA
04c18fa curl: Don't free memory for subjectAltName before calling callback
5a8e48f mesh: Use MESH_CAP_* macros for mesh capability
d70a8ab mesh: Ignore crowded peer
a151b0e mesh: Select pairwise and group cipher based on network profile
3b6deac mesh: Avoid use of hardcoded cipher
f868d56 mesh: Clean up AMPE element encoding and parsing
4367eec mesh: Do not use RX MGTK as RX IGTK
a4eec3c mesh: Use variable length MGTK for RX
fccba2c mesh: Generate a separate TX IGTK if PMF is enabled
696f792 mesh: Support variable length TX MGTK
b02f4d0 mesh: Add variable length MTK support
846201d mesh: Coding style cleanup for MTK derivation
0f76d82 mesh: Fix MTK derivation to use AKM suite selector
f5ba692 mesh: Coding style cleanup for AEK derivation
a59c5e9 mesh: Fix AEK derivation to use AKM suite selector
18aca1a mesh: Use ieee80211w profile parameter
b8b499e mesh: Use WPA_NONCE_LEN macro
bb4e19e hostapd: Skip hostapd ACL check for drivers supporting ACL offload
00ec535 WPS: Fix memory leak with wps_ie in wpa_bss_is_wps_candidate()
d1296da Reserve QCA vendor specific nl80211 command 121
fae7b37 WPS: Do not expire probable BSSes for WPS connection
52a6c9c Add a QCA vendor command to configure AP parameters
31d3692 hostapd: Add comment about '-i' parameter in hostapd.conf
40f6282 hostapd: Accept interface names as a command line parameter
cc27c8e hostapd: Fix early init failure path
976dfb3 FST: Make fst_global_deinit() more robust
7a69fad mesh: Sync max peer links with kernel
f7cb6e9 Update PKCS#11 references in template wpa_supplicant.conf
c3d7fb7 OpenSSL: Initialise PKCS#11 engine even if found with ENGINE_by_id()
fdc1188 nl80211: Fix use-after-free in qca_nl80211_get_features()
8359472 hostapd Make GAS Address3 field selection behavior configurable
6996ff7 hostapd: Fix Public Action frame TX status processing for wildcard BSSID
78a3632 hostapd: Fix Public Action frame addressing (BSSID field)
c86bef2 wpa_supplicant: Make GAS Address3 field selection behavior configurable
a5a187b nl80211: Add TEST_FAIL() to command generation and set_mode
ee854ff mesh: Remove extra newline from the end of an error message
331f077 mesh: Allow 160 MHz channel to be configured
92a515b nl80211: Update drv->assoc_freq on mesh join
d2cc8bb mesh: Remove unreachable code
cc9a257 nl80211: Use extended capabilities per interface type
c6edea0 Sync with mac80211-next.git include/uapi/linux/nl80211.h
9a5160f Report connection timeouts in CTRL-EVENT-ASSOC-REJECT
dad0129 mesh: Support simple SAE group negotiation case
b4c738e mesh: Fix error path handling for RSN (MGTK init)
f4b4ddf D-Bus: Remove unused wpas_dbus_signal_p2p_group_started() parameter
4fe50bb D-Bus: Indicate whether created group is persistent or not
62fc8e6 mesh: Fix MESH_INTERFACE_ADD error path cleanup
9c10be3 mesh: Fix error path handling in init OOM cases
7012e25 Remove dead code from wpas_sched_scan_plans_set()
8e909fa Improve reattach scan OOM failure handling
f37d8a4 Indicate scan failure event on parameter cloning failure
9356823 wpaspy: Fix potentially referencing non existing attribute

Change-Id: I656be560523c206195a5bf8649e73d8aa70bd8f9
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index 1ba2ab3..1e00f35 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -660,6 +660,11 @@
 	if (ieee80211_is_dfs(params.freq.freq))
 		params.freq.freq = 0; /* set channel after CAC */
 
+	if (params.p2p)
+		wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_GO);
+	else
+		wpa_drv_get_ext_capa(wpa_s, WPA_IF_AP_BSS);
+
 	if (wpa_drv_associate(wpa_s, &params) < 0) {
 		wpa_msg(wpa_s, MSG_INFO, "Failed to start AP functionality");
 		return -1;
diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index a83ca10..3687a2e 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -12,6 +12,7 @@
 #include "utils/eloop.h"
 #include "common/ieee802_11_defs.h"
 #include "drivers/driver.h"
+#include "eap_peer/eap.h"
 #include "wpa_supplicant_i.h"
 #include "config.h"
 #include "notify.h"
@@ -303,6 +304,47 @@
 }
 
 
+static int wpa_bss_is_wps_candidate(struct wpa_supplicant *wpa_s,
+				    struct wpa_bss *bss)
+{
+#ifdef CONFIG_WPS
+	struct wpa_ssid *ssid;
+	struct wpabuf *wps_ie;
+	int pbc = 0, ret;
+
+	wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
+	if (!wps_ie)
+		return 0;
+
+	if (wps_is_selected_pbc_registrar(wps_ie)) {
+		pbc = 1;
+	} else if (!wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1)) {
+		wpabuf_free(wps_ie);
+		return 0;
+	}
+
+	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+		if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS))
+			continue;
+		if (ssid->ssid_len &&
+		    (ssid->ssid_len != bss->ssid_len ||
+		     os_memcmp(ssid->ssid, bss->ssid, ssid->ssid_len) != 0))
+			continue;
+
+		if (pbc)
+			ret = eap_is_wps_pbc_enrollee(&ssid->eap);
+		else
+			ret = eap_is_wps_pin_enrollee(&ssid->eap);
+		wpabuf_free(wps_ie);
+		return ret;
+	}
+	wpabuf_free(wps_ie);
+#endif /* CONFIG_WPS */
+
+	return 0;
+}
+
+
 static int wpa_bss_known(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
 {
 	struct wpa_ssid *ssid;
@@ -341,7 +383,8 @@
 	struct wpa_bss *bss;
 
 	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
-		if (!wpa_bss_known(wpa_s, bss)) {
+		if (!wpa_bss_known(wpa_s, bss) &&
+		    !wpa_bss_is_wps_candidate(wpa_s, bss)) {
 			wpa_bss_remove(wpa_s, bss, __func__);
 			return 0;
 		}
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index 674faab..0571c23 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -4368,6 +4368,7 @@
 	{ INT_RANGE(mbo_cell_capa, MBO_CELL_CAPA_AVAILABLE,
 		    MBO_CELL_CAPA_NOT_SUPPORTED), 0 },
 #endif /*CONFIG_MBO */
+	{ INT(gas_address3), 0 },
 };
 
 #undef FUNC
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index 786b85a..1535738 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -1292,6 +1292,16 @@
 	 */
 	enum mbo_cellular_capa mbo_cell_capa;
 #endif /* CONFIG_MBO */
+
+	/**
+	 * gas_address3 - GAS Address3 field behavior
+	 *
+	 * Values:
+	 * 0 - P2P specification (Address3 = AP BSSID)
+	 * 1 = IEEE 802.11 standard compliant (Address3 = Wildcard BSSID when
+	 *	sent to not-associated AP; if associated, AP BSSID)
+	 */
+	int gas_address3;
 };
 
 
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index 939a795..e72f844 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -1336,6 +1336,8 @@
 		fprintf(f, "mbo_cell_capa=%u\n", config->mbo_cell_capa);
 #endif /* CONFIG_MBO */
 
+	if (config->gas_address3)
+		fprintf(f, "gas_address3=%d\n", config->gas_address3);
 }
 
 #endif /* CONFIG_NO_CONFIG_WRITE */
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index e75f1ae..eb1e40a 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -4298,9 +4298,10 @@
 	if (mask & WPA_BSS_MASK_P2P_SCAN) {
 		ie = (const u8 *) (bss + 1);
 		ret = wpas_p2p_scan_result_text(ie, bss->ie_len, pos, end);
-		if (ret < 0 || ret >= end - pos)
+		if (ret >= end - pos)
 			return 0;
-		pos += ret;
+		if (ret > 0)
+			pos += ret;
 	}
 #endif /* CONFIG_P2P */
 
@@ -4381,9 +4382,10 @@
 	if (mask & WPA_BSS_MASK_MESH_SCAN) {
 		ie = (const u8 *) (bss + 1);
 		ret = wpas_mesh_scan_result_text(ie, bss->ie_len, pos, end);
-		if (ret < 0 || ret >= end - pos)
+		if (ret >= end - pos)
 			return 0;
-		pos += ret;
+		if (ret > 0)
+			pos += ret;
 	}
 #endif /* CONFIG_MESH */
 
diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c
index d894f6a..0263b2c 100644
--- a/wpa_supplicant/dbus/dbus_new.c
+++ b/wpa_supplicant/dbus/dbus_new.c
@@ -1254,14 +1254,11 @@
  * irrespective of the role (client/GO) of the current device
  *
  * @wpa_s: %wpa_supplicant network interface data
- * @ssid: SSID object
  * @client: this device is P2P client
- * @network_id: network id of the group started, use instead of ssid->id
- *	to account for persistent groups
+ * @persistent: 0 - non persistent group, 1 - persistent group
  */
 void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s,
-					const struct wpa_ssid *ssid,
-					int client, int network_id)
+					int client, int persistent)
 {
 	DBusMessage *msg;
 	DBusMessageIter iter, dict_iter;
@@ -1300,6 +1297,7 @@
 					      wpa_s->dbus_new_path) ||
 	    !wpa_dbus_dict_append_string(&dict_iter, "role",
 					 client ? "client" : "GO") ||
+	    !wpa_dbus_dict_append_bool(&dict_iter, "persistent", persistent) ||
 	    !wpa_dbus_dict_append_object_path(&dict_iter, "group_object",
 					      wpa_s->dbus_groupobj_path) ||
 	    !wpa_dbus_dict_close_write(&iter, &dict_iter)) {
diff --git a/wpa_supplicant/dbus/dbus_new.h b/wpa_supplicant/dbus/dbus_new.h
index 3ac66db..d64fcee 100644
--- a/wpa_supplicant/dbus/dbus_new.h
+++ b/wpa_supplicant/dbus/dbus_new.h
@@ -190,8 +190,7 @@
 				     const u8 *src, u16 dev_passwd_id,
 				     u8 go_intent);
 void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s,
-					const struct wpa_ssid *ssid,
-					int client, int network_id);
+					int client, int persistent);
 void wpas_dbus_signal_p2p_group_formation_failure(struct wpa_supplicant *wpa_s,
 						  const char *reason);
 void wpas_dbus_register_p2p_group(struct wpa_supplicant *wpa_s,
@@ -401,8 +400,7 @@
 
 static inline void
 wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s,
-				   const struct wpa_ssid *ssid,
-				   int client, int network_id)
+				   int client, int persistent)
 {
 }
 
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index 7a213b6..9f104f5 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -158,6 +158,15 @@
 	return -1;
 }
 
+static inline int wpa_drv_get_seqnum(struct wpa_supplicant *wpa_s,
+				     const u8 *addr, int idx, u8 *seq)
+{
+	if (wpa_s->driver->get_seqnum)
+		return wpa_s->driver->get_seqnum(wpa_s->ifname, wpa_s->drv_priv,
+						 addr, idx, seq);
+	return -1;
+}
+
 static inline int wpa_drv_sta_deauth(struct wpa_supplicant *wpa_s,
 				     const u8 *addr, int reason_code)
 {
@@ -926,4 +935,15 @@
 							   filters);
 }
 
+static inline int wpa_drv_get_ext_capa(struct wpa_supplicant *wpa_s,
+				       enum wpa_driver_if_type type)
+{
+	if (!wpa_s->driver->get_ext_capab)
+		return -1;
+	return wpa_s->driver->get_ext_capab(wpa_s->drv_priv, type,
+					    &wpa_s->extended_capa,
+					    &wpa_s->extended_capa_mask,
+					    &wpa_s->extended_capa_len);
+}
+
 #endif /* DRIVER_I_H */
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index c8d0553..057f630 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -3544,13 +3544,15 @@
 	case EVENT_ASSOC_REJECT:
 		if (data->assoc_reject.bssid)
 			wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT
-				"bssid=" MACSTR	" status_code=%u",
+				"bssid=" MACSTR	" status_code=%u%s",
 				MAC2STR(data->assoc_reject.bssid),
-				data->assoc_reject.status_code);
+				data->assoc_reject.status_code,
+				data->assoc_reject.timed_out ? " timeout" : "");
 		else
 			wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT
-				"status_code=%u",
-				data->assoc_reject.status_code);
+				"status_code=%u%s",
+				data->assoc_reject.status_code,
+				data->assoc_reject.timed_out ? " timeout" : "");
 		wpa_s->assoc_status_code = data->assoc_reject.status_code;
 		wpas_notify_assoc_status_code(wpa_s);
 		if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
diff --git a/wpa_supplicant/gas_query.c b/wpa_supplicant/gas_query.c
index 4f0d0e6..52f50cf 100644
--- a/wpa_supplicant/gas_query.c
+++ b/wpa_supplicant/gas_query.c
@@ -17,6 +17,7 @@
 #include "common/wpa_ctrl.h"
 #include "rsn_supp/wpa.h"
 #include "wpa_supplicant_i.h"
+#include "config.h"
 #include "driver_i.h"
 #include "offchannel.h"
 #include "gas_query.h"
@@ -273,6 +274,10 @@
 			struct wpabuf *req, unsigned int wait_time)
 {
 	int res, prot = pmf_in_use(gas->wpa_s, query->addr);
+	const u8 *bssid;
+	const u8 wildcard_bssid[ETH_ALEN] = {
+		0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+	};
 
 	wpa_printf(MSG_DEBUG, "GAS: Send action frame to " MACSTR " len=%u "
 		   "freq=%d prot=%d", MAC2STR(query->addr),
@@ -285,8 +290,15 @@
 	if (gas->wpa_s->max_remain_on_chan &&
 	    wait_time > gas->wpa_s->max_remain_on_chan)
 		wait_time = gas->wpa_s->max_remain_on_chan;
+	if (!gas->wpa_s->conf->gas_address3 ||
+	    (gas->wpa_s->current_ssid &&
+	     gas->wpa_s->wpa_state >= WPA_ASSOCIATED &&
+	     os_memcmp(query->addr, gas->wpa_s->bssid, ETH_ALEN) == 0))
+		bssid = query->addr;
+	else
+		bssid = wildcard_bssid;
 	res = offchannel_send_action(gas->wpa_s, query->freq, query->addr,
-				     gas->wpa_s->own_addr, query->addr,
+				     gas->wpa_s->own_addr, bssid,
 				     wpabuf_head(req), wpabuf_len(req),
 				     wait_time, gas_query_tx_status, 0);
 	if (res == 0)
diff --git a/wpa_supplicant/mesh.c b/wpa_supplicant/mesh.c
index 89b033b..bf9beb2 100644
--- a/wpa_supplicant/mesh.c
+++ b/wpa_supplicant/mesh.c
@@ -66,9 +66,11 @@
 }
 
 
-static struct mesh_conf * mesh_config_create(struct wpa_ssid *ssid)
+static struct mesh_conf * mesh_config_create(struct wpa_supplicant *wpa_s,
+					     struct wpa_ssid *ssid)
 {
 	struct mesh_conf *conf;
+	int cipher;
 
 	conf = os_zalloc(sizeof(struct mesh_conf));
 	if (!conf)
@@ -82,6 +84,33 @@
 			MESH_CONF_SEC_AMPE;
 	else
 		conf->security |= MESH_CONF_SEC_NONE;
+	conf->ieee80211w = ssid->ieee80211w;
+	if (conf->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT) {
+		if (wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_BIP)
+			conf->ieee80211w = wpa_s->conf->pmf;
+		else
+			conf->ieee80211w = NO_MGMT_FRAME_PROTECTION;
+	}
+
+	cipher = wpa_pick_pairwise_cipher(ssid->pairwise_cipher, 0);
+	if (cipher < 0 || cipher == WPA_CIPHER_TKIP) {
+		wpa_msg(wpa_s, MSG_INFO, "mesh: Invalid pairwise cipher");
+		os_free(conf);
+		return NULL;
+	}
+	conf->pairwise_cipher = cipher;
+
+	cipher = wpa_pick_group_cipher(ssid->group_cipher);
+	if (cipher < 0 || cipher == WPA_CIPHER_TKIP ||
+	    cipher == WPA_CIPHER_GTK_NOT_USED) {
+		wpa_msg(wpa_s, MSG_INFO, "mesh: Invalid group cipher");
+		os_free(conf);
+		return NULL;
+	}
+
+	conf->group_cipher = cipher;
+	if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION)
+		conf->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC;
 
 	/* defaults */
 	conf->mesh_pp_id = MESH_PATH_PROTOCOL_HWMP;
@@ -175,19 +204,13 @@
 		wpa_s->conf->dot11RSNASAERetransPeriod;
 	os_strlcpy(bss->conf->iface, wpa_s->ifname, sizeof(bss->conf->iface));
 
-	mconf = mesh_config_create(ssid);
+	mconf = mesh_config_create(wpa_s, ssid);
 	if (!mconf)
 		goto out_free;
 	ifmsh->mconf = mconf;
 
 	/* need conf->hw_mode for supported rates. */
-	if (ssid->frequency == 0) {
-		conf->hw_mode = HOSTAPD_MODE_IEEE80211G;
-		conf->channel = 1;
-	} else {
-		conf->hw_mode = ieee80211_freq_to_chan(ssid->frequency,
-						       &conf->channel);
-	}
+	conf->hw_mode = ieee80211_freq_to_chan(ssid->frequency, &conf->channel);
 	if (conf->hw_mode == NUM_HOSTAPD_MODES) {
 		wpa_printf(MSG_ERROR, "Unsupported mesh mode frequency: %d MHz",
 			   ssid->frequency);
@@ -341,15 +364,9 @@
 
 	wpa_supplicant_mesh_deinit(wpa_s);
 
-	if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) {
-		wpa_s->pairwise_cipher = WPA_CIPHER_CCMP;
-		wpa_s->group_cipher = WPA_CIPHER_CCMP;
-		wpa_s->mgmt_group_cipher = 0;
-	} else {
-		wpa_s->pairwise_cipher = WPA_CIPHER_NONE;
-		wpa_s->group_cipher = WPA_CIPHER_NONE;
-		wpa_s->mgmt_group_cipher = 0;
-	}
+	wpa_s->pairwise_cipher = WPA_CIPHER_NONE;
+	wpa_s->group_cipher = WPA_CIPHER_NONE;
+	wpa_s->mgmt_group_cipher = 0;
 
 	os_memset(&params, 0, sizeof(params));
 	params.meshid = ssid->ssid;
@@ -407,6 +424,12 @@
 		goto out;
 	}
 
+	if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) {
+		wpa_s->pairwise_cipher = wpa_s->mesh_rsn->pairwise_cipher;
+		wpa_s->group_cipher = wpa_s->mesh_rsn->group_cipher;
+		wpa_s->mgmt_group_cipher = wpa_s->mesh_rsn->mgmt_group_cipher;
+	}
+
 	if (wpa_s->ifmsh) {
 		params.ies = wpa_s->ifmsh->mconf->rsn_ie;
 		params.ie_len = wpa_s->ifmsh->mconf->rsn_ie_len;
@@ -417,7 +440,7 @@
 		wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
 	ret = wpa_drv_join_mesh(wpa_s, &params);
 	if (ret)
-		wpa_msg(wpa_s, MSG_ERROR, "mesh join error=%d\n", ret);
+		wpa_msg(wpa_s, MSG_ERROR, "mesh join error=%d", ret);
 
 	/* hostapd sets the interface down until we associate */
 	wpa_drv_set_operstate(wpa_s, 1);
@@ -591,7 +614,7 @@
 	if (!mesh_wpa_s) {
 		wpa_printf(MSG_ERROR,
 			   "mesh: Failed to create new wpa_supplicant interface");
-		wpa_supplicant_remove_iface(wpa_s->global, wpa_s, 0);
+		wpa_drv_if_remove(wpa_s, WPA_IF_MESH, ifname);
 		return -1;
 	}
 	mesh_wpa_s->mesh_if_created = 1;
diff --git a/wpa_supplicant/mesh_mpm.c b/wpa_supplicant/mesh_mpm.c
index a0b7174..8f327d8 100644
--- a/wpa_supplicant/mesh_mpm.c
+++ b/wpa_supplicant/mesh_mpm.c
@@ -290,7 +290,8 @@
 		/* TODO: Add Connected to Mesh Gate/AS subfields */
 		wpabuf_put_u8(buf, info);
 		/* always forwarding & accepting plinks for now */
-		wpabuf_put_u8(buf, 0x1 | 0x8);
+		wpabuf_put_u8(buf, MESH_CAP_ACCEPT_ADDITIONAL_PEER |
+			      MESH_CAP_FORWARDING);
 	} else {	/* Peer closing frame */
 		/* IE: Mesh ID */
 		wpabuf_put_u8(buf, WLAN_EID_MESH_ID);
@@ -649,6 +650,14 @@
 	struct sta_info *sta;
 	int ret;
 
+	if (elems->mesh_config_len >= 7 &&
+	    !(elems->mesh_config[6] & MESH_CAP_ACCEPT_ADDITIONAL_PEER)) {
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"mesh: Ignore a crowded peer " MACSTR,
+			MAC2STR(addr));
+		return NULL;
+	}
+
 	sta = ap_get_sta(data, addr);
 	if (!sta) {
 		sta = ap_sta_add(data, addr);
@@ -793,18 +802,32 @@
 		MAC2STR(sta->addr));
 
 	if (conf->security & MESH_CONF_SEC_AMPE) {
-		wpa_drv_set_key(wpa_s, WPA_ALG_CCMP, sta->addr, 0, 0,
-				seq, sizeof(seq), sta->mtk, sizeof(sta->mtk));
-		wpa_drv_set_key(wpa_s, WPA_ALG_CCMP, sta->addr, 1, 0,
-				seq, sizeof(seq),
-				sta->mgtk, sizeof(sta->mgtk));
-		wpa_drv_set_key(wpa_s, WPA_ALG_IGTK, sta->addr, 4, 0,
-				seq, sizeof(seq),
-				sta->mgtk, sizeof(sta->mgtk));
+		wpa_hexdump_key(MSG_DEBUG, "mesh: MTK", sta->mtk, sta->mtk_len);
+		wpa_drv_set_key(wpa_s, wpa_cipher_to_alg(conf->pairwise_cipher),
+				sta->addr, 0, 0, seq, sizeof(seq),
+				sta->mtk, sta->mtk_len);
 
-		wpa_hexdump_key(MSG_DEBUG, "mtk:", sta->mtk, sizeof(sta->mtk));
-		wpa_hexdump_key(MSG_DEBUG, "mgtk:",
-				sta->mgtk, sizeof(sta->mgtk));
+		wpa_hexdump_key(MSG_DEBUG, "mesh: RX MGTK Key RSC",
+				sta->mgtk_rsc, sizeof(sta->mgtk_rsc));
+		wpa_hexdump_key(MSG_DEBUG, "mesh: RX MGTK",
+				sta->mgtk, sta->mgtk_len);
+		wpa_drv_set_key(wpa_s, wpa_cipher_to_alg(conf->group_cipher),
+				sta->addr, sta->mgtk_key_id, 0,
+				sta->mgtk_rsc, sizeof(sta->mgtk_rsc),
+				sta->mgtk, sta->mgtk_len);
+
+		if (sta->igtk_len) {
+			wpa_hexdump_key(MSG_DEBUG, "mesh: RX IGTK Key RSC",
+					sta->igtk_rsc, sizeof(sta->igtk_rsc));
+			wpa_hexdump_key(MSG_DEBUG, "mesh: RX IGTK",
+					sta->igtk, sta->igtk_len);
+			wpa_drv_set_key(
+				wpa_s,
+				wpa_cipher_to_alg(conf->mgmt_group_cipher),
+				sta->addr, sta->igtk_key_id, 0,
+				sta->igtk_rsc, sizeof(sta->igtk_rsc),
+				sta->igtk, sta->igtk_len);
+		}
 	}
 
 	wpa_mesh_set_plink_state(wpa_s, sta, PLINK_ESTAB);
diff --git a/wpa_supplicant/mesh_rsn.c b/wpa_supplicant/mesh_rsn.c
index 1994f3f..c5f5d69 100644
--- a/wpa_supplicant/mesh_rsn.c
+++ b/wpa_supplicant/mesh_rsn.c
@@ -136,7 +136,8 @@
 }
 
 
-static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr)
+static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr,
+				enum mfp_options ieee80211w)
 {
 	struct wpa_auth_config conf;
 	struct wpa_auth_callbacks cb;
@@ -145,13 +146,18 @@
 	wpa_printf(MSG_DEBUG, "AUTH: Initializing group state machine");
 
 	os_memset(&conf, 0, sizeof(conf));
-	conf.wpa = 2;
+	conf.wpa = WPA_PROTO_RSN;
 	conf.wpa_key_mgmt = WPA_KEY_MGMT_SAE;
-	conf.wpa_pairwise = WPA_CIPHER_CCMP;
-	conf.rsn_pairwise = WPA_CIPHER_CCMP;
-	conf.wpa_group = WPA_CIPHER_CCMP;
+	conf.wpa_pairwise = rsn->pairwise_cipher;
+	conf.rsn_pairwise = rsn->pairwise_cipher;
+	conf.wpa_group = rsn->group_cipher;
 	conf.eapol_version = 0;
 	conf.wpa_group_rekey = -1;
+#ifdef CONFIG_IEEE80211W
+	conf.ieee80211w = ieee80211w;
+	if (ieee80211w != NO_MGMT_FRAME_PROTECTION)
+		conf.group_mgmt_cipher = rsn->mgmt_group_cipher;
+#endif /* CONFIG_IEEE80211W */
 
 	os_memset(&cb, 0, sizeof(cb));
 	cb.ctx = rsn;
@@ -167,18 +173,34 @@
 	}
 
 	/* TODO: support rekeying */
-	if (random_get_bytes(rsn->mgtk, 16) < 0) {
-		wpa_deinit(rsn->auth);
+	rsn->mgtk_len = wpa_cipher_key_len(conf.wpa_group);
+	if (random_get_bytes(rsn->mgtk, rsn->mgtk_len) < 0)
 		return -1;
-	}
+	rsn->mgtk_key_id = 1;
 
-	/* group mgmt */
-	wpa_drv_set_key(rsn->wpa_s, WPA_ALG_IGTK, NULL, 4, 1,
-			seq, sizeof(seq), rsn->mgtk, sizeof(rsn->mgtk));
+#ifdef CONFIG_IEEE80211W
+	if (ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+		rsn->igtk_len = wpa_cipher_key_len(conf.group_mgmt_cipher);
+		if (random_get_bytes(rsn->igtk, rsn->igtk_len) < 0)
+			return -1;
+		rsn->igtk_key_id = 4;
+
+		/* group mgmt */
+		wpa_hexdump_key(MSG_DEBUG, "mesh: Own TX IGTK",
+				rsn->igtk, rsn->igtk_len);
+		wpa_drv_set_key(rsn->wpa_s,
+				wpa_cipher_to_alg(rsn->mgmt_group_cipher), NULL,
+				rsn->igtk_key_id, 1,
+				seq, sizeof(seq), rsn->igtk, rsn->igtk_len);
+	}
+#endif /* CONFIG_IEEE80211W */
 
 	/* group privacy / data frames */
-	wpa_drv_set_key(rsn->wpa_s, WPA_ALG_CCMP, NULL, 1, 1,
-			seq, sizeof(seq), rsn->mgtk, sizeof(rsn->mgtk));
+	wpa_hexdump_key(MSG_DEBUG, "mesh: Own TX MGTK",
+			rsn->mgtk, rsn->mgtk_len);
+	wpa_drv_set_key(rsn->wpa_s, wpa_cipher_to_alg(rsn->group_cipher), NULL,
+			rsn->mgtk_key_id, 1, seq, sizeof(seq),
+			rsn->mgtk, rsn->mgtk_len);
 
 	return 0;
 }
@@ -187,6 +209,9 @@
 static void mesh_rsn_deinit(struct mesh_rsn *rsn)
 {
 	os_memset(rsn->mgtk, 0, sizeof(rsn->mgtk));
+	rsn->mgtk_len = 0;
+	os_memset(rsn->igtk, 0, sizeof(rsn->igtk));
+	rsn->igtk_len = 0;
 	if (rsn->auth)
 		wpa_deinit(rsn->auth);
 }
@@ -204,8 +229,12 @@
 	if (mesh_rsn == NULL)
 		return NULL;
 	mesh_rsn->wpa_s = wpa_s;
+	mesh_rsn->pairwise_cipher = conf->pairwise_cipher;
+	mesh_rsn->group_cipher = conf->group_cipher;
+	mesh_rsn->mgmt_group_cipher = conf->mgmt_group_cipher;
 
-	if (__mesh_rsn_auth_init(mesh_rsn, wpa_s->own_addr) < 0) {
+	if (__mesh_rsn_auth_init(mesh_rsn, wpa_s->own_addr,
+				 conf->ieee80211w) < 0) {
 		mesh_rsn_deinit(mesh_rsn);
 		os_free(mesh_rsn);
 		return NULL;
@@ -358,18 +387,27 @@
 {
 	u8 *myaddr = rsn->wpa_s->own_addr;
 	u8 *peer = sta->addr;
-	u8 *addr1 = peer, *addr2 = myaddr;
-	u8 context[AES_BLOCK_SIZE];
+	u8 *addr1, *addr2;
+	u8 context[RSN_SELECTOR_LEN + 2 * ETH_ALEN], *ptr = context;
 
-	/* SAE */
-	RSN_SELECTOR_PUT(context, wpa_cipher_to_suite(0, WPA_CIPHER_GCMP));
+	/*
+	 * AEK = KDF-Hash-256(PMK, "AEK Derivation", Selected AKM Suite ||
+	 *       min(localMAC, peerMAC) || max(localMAC, peerMAC))
+	 */
+	/* Selected AKM Suite: SAE */
+	RSN_SELECTOR_PUT(ptr, RSN_AUTH_KEY_MGMT_SAE);
+	ptr += RSN_SELECTOR_LEN;
 
 	if (os_memcmp(myaddr, peer, ETH_ALEN) < 0) {
 		addr1 = myaddr;
 		addr2 = peer;
+	} else {
+		addr1 = peer;
+		addr2 = myaddr;
 	}
-	os_memcpy(context + 4, addr1, ETH_ALEN);
-	os_memcpy(context + 10, addr2, ETH_ALEN);
+	os_memcpy(ptr, addr1, ETH_ALEN);
+	ptr += ETH_ALEN;
+	os_memcpy(ptr, addr2, ETH_ALEN);
 
 	sha256_prf(sta->sae->pmk, sizeof(sta->sae->pmk), "AEK Derivation",
 		   context, sizeof(context), sta->aek, sizeof(sta->aek));
@@ -381,40 +419,44 @@
 {
 	u8 *ptr;
 	u8 *min, *max;
-	u16 min_lid, max_lid;
-	size_t nonce_len = sizeof(sta->my_nonce);
-	size_t lid_len = sizeof(sta->my_lid);
 	u8 *myaddr = wpa_s->own_addr;
 	u8 *peer = sta->addr;
-	/* 2 nonces, 2 linkids, akm suite, 2 mac addrs */
-	u8 context[64 + 4 + 4 + 12];
+	u8 context[2 * WPA_NONCE_LEN + 2 * 2 + RSN_SELECTOR_LEN + 2 * ETH_ALEN];
 
+	/*
+	 * MTK = KDF-Hash-Length(PMK, "Temporal Key Derivation", min(localNonce,
+	 *  peerNonce) || max(localNonce, peerNonce) || min(localLinkID,
+	 *  peerLinkID) || max(localLinkID, peerLinkID) || Selected AKM Suite ||
+	 *  min(localMAC, peerMAC) || max(localMAC, peerMAC))
+	 */
 	ptr = context;
-	if (os_memcmp(sta->my_nonce, sta->peer_nonce, nonce_len) < 0) {
+	if (os_memcmp(sta->my_nonce, sta->peer_nonce, WPA_NONCE_LEN) < 0) {
 		min = sta->my_nonce;
 		max = sta->peer_nonce;
 	} else {
 		min = sta->peer_nonce;
 		max = sta->my_nonce;
 	}
-	os_memcpy(ptr, min, nonce_len);
-	os_memcpy(ptr + nonce_len, max, nonce_len);
-	ptr += 2 * nonce_len;
+	os_memcpy(ptr, min, WPA_NONCE_LEN);
+	ptr += WPA_NONCE_LEN;
+	os_memcpy(ptr, max, WPA_NONCE_LEN);
+	ptr += WPA_NONCE_LEN;
 
 	if (sta->my_lid < sta->peer_lid) {
-		min_lid = host_to_le16(sta->my_lid);
-		max_lid = host_to_le16(sta->peer_lid);
+		WPA_PUT_LE16(ptr, sta->my_lid);
+		ptr += 2;
+		WPA_PUT_LE16(ptr, sta->peer_lid);
+		ptr += 2;
 	} else {
-		min_lid = host_to_le16(sta->peer_lid);
-		max_lid = host_to_le16(sta->my_lid);
+		WPA_PUT_LE16(ptr, sta->peer_lid);
+		ptr += 2;
+		WPA_PUT_LE16(ptr, sta->my_lid);
+		ptr += 2;
 	}
-	os_memcpy(ptr, &min_lid, lid_len);
-	os_memcpy(ptr + lid_len, &max_lid, lid_len);
-	ptr += 2 * lid_len;
 
-	/* SAE */
-	RSN_SELECTOR_PUT(ptr, wpa_cipher_to_suite(0, WPA_CIPHER_GCMP));
-	ptr += 4;
+	/* Selected AKM Suite: SAE */
+	RSN_SELECTOR_PUT(ptr, RSN_AUTH_KEY_MGMT_SAE);
+	ptr += RSN_SELECTOR_LEN;
 
 	if (os_memcmp(myaddr, peer, ETH_ALEN) < 0) {
 		min = myaddr;
@@ -424,22 +466,24 @@
 		max = myaddr;
 	}
 	os_memcpy(ptr, min, ETH_ALEN);
-	os_memcpy(ptr + ETH_ALEN, max, ETH_ALEN);
+	ptr += ETH_ALEN;
+	os_memcpy(ptr, max, ETH_ALEN);
 
-	sha256_prf(sta->sae->pmk, sizeof(sta->sae->pmk),
+	sta->mtk_len = wpa_cipher_key_len(wpa_s->mesh_rsn->pairwise_cipher);
+	sha256_prf(sta->sae->pmk, SAE_PMK_LEN,
 		   "Temporal Key Derivation", context, sizeof(context),
-		   sta->mtk, sizeof(sta->mtk));
+		   sta->mtk, sta->mtk_len);
 	return 0;
 }
 
 
 void mesh_rsn_init_ampe_sta(struct wpa_supplicant *wpa_s, struct sta_info *sta)
 {
-	if (random_get_bytes(sta->my_nonce, 32) < 0) {
+	if (random_get_bytes(sta->my_nonce, WPA_NONCE_LEN) < 0) {
 		wpa_printf(MSG_INFO, "mesh: Failed to derive random nonce");
 		/* TODO: How to handle this more cleanly? */
 	}
-	os_memset(sta->peer_nonce, 0, 32);
+	os_memset(sta->peer_nonce, 0, WPA_NONCE_LEN);
 	mesh_rsn_derive_aek(wpa_s->mesh_rsn, sta);
 }
 
@@ -455,65 +499,89 @@
 {
 	struct ieee80211_ampe_ie *ampe;
 	u8 const *ie = wpabuf_head_u8(buf) + wpabuf_len(buf);
-	u8 *ampe_ie = NULL, *mic_ie = NULL, *mic_payload;
+	u8 *ampe_ie, *pos, *mic_payload;
 	const u8 *aad[] = { rsn->wpa_s->own_addr, sta->addr, cat };
 	const size_t aad_len[] = { ETH_ALEN, ETH_ALEN, ie - cat };
 	int ret = 0;
+	size_t len;
 
-	if (AES_BLOCK_SIZE + 2 + sizeof(*ampe) + 2 > wpabuf_tailroom(buf)) {
+	len = sizeof(*ampe) + rsn->mgtk_len + WPA_KEY_RSC_LEN + 4;
+#ifdef CONFIG_IEEE80211W
+	if (rsn->igtk_len)
+		len += 2 + 6 + rsn->igtk_len;
+#endif /* CONFIG_IEEE80211W */
+
+	if (2 + AES_BLOCK_SIZE + 2 + len > wpabuf_tailroom(buf)) {
 		wpa_printf(MSG_ERROR, "protect frame: buffer too small");
 		return -EINVAL;
 	}
 
-	ampe_ie = os_zalloc(2 + sizeof(*ampe));
+	ampe_ie = os_zalloc(2 + len);
 	if (!ampe_ie) {
 		wpa_printf(MSG_ERROR, "protect frame: out of memory");
 		return -ENOMEM;
 	}
 
-	mic_ie = os_zalloc(2 + AES_BLOCK_SIZE);
-	if (!mic_ie) {
-		wpa_printf(MSG_ERROR, "protect frame: out of memory");
-		ret = -ENOMEM;
-		goto free;
-	}
-
 	/*  IE: AMPE */
 	ampe_ie[0] = WLAN_EID_AMPE;
-	ampe_ie[1] = sizeof(*ampe);
+	ampe_ie[1] = len;
 	ampe = (struct ieee80211_ampe_ie *) (ampe_ie + 2);
 
 	RSN_SELECTOR_PUT(ampe->selected_pairwise_suite,
-		     wpa_cipher_to_suite(WPA_PROTO_RSN, WPA_CIPHER_CCMP));
-	os_memcpy(ampe->local_nonce, sta->my_nonce, 32);
-	os_memcpy(ampe->peer_nonce, sta->peer_nonce, 32);
-	/* incomplete: see 13.5.4 */
+			 RSN_CIPHER_SUITE_CCMP);
+	os_memcpy(ampe->local_nonce, sta->my_nonce, WPA_NONCE_LEN);
+	os_memcpy(ampe->peer_nonce, sta->peer_nonce, WPA_NONCE_LEN);
+
+	pos = (u8 *) (ampe + 1);
+
+	/* TODO: Key Replay Counter[8] optionally for
+	 * Mesh Group Key Inform/Acknowledge frames */
+
 	/* TODO: static mgtk for now since we don't support rekeying! */
-	os_memcpy(ampe->mgtk, rsn->mgtk, 16);
-	/*  TODO: Populate Key RSC */
-	/*  expire in 13 decades or so */
-	os_memset(ampe->key_expiration, 0xff, 4);
+	/*
+	 * GTKdata[variable]:
+	 * MGTK[variable] || Key RSC[8] || GTKExpirationTime[4]
+	 */
+	os_memcpy(pos, rsn->mgtk, rsn->mgtk_len);
+	pos += rsn->mgtk_len;
+	wpa_drv_get_seqnum(rsn->wpa_s, NULL, rsn->mgtk_key_id, pos);
+	pos += WPA_KEY_RSC_LEN;
+	/* Use fixed GTKExpirationTime for now */
+	WPA_PUT_LE32(pos, 0xffffffff);
+	pos += 4;
+
+#ifdef CONFIG_IEEE80211W
+	/*
+	 * IGTKdata[variable]:
+	 * Key ID[2], IPN[6], IGTK[variable]
+	 */
+	if (rsn->igtk_len) {
+		WPA_PUT_LE16(pos, rsn->igtk_key_id);
+		pos += 2;
+		wpa_drv_get_seqnum(rsn->wpa_s, NULL, rsn->igtk_key_id, pos);
+		pos += 6;
+		os_memcpy(pos, rsn->igtk, rsn->igtk_len);
+	}
+#endif /* CONFIG_IEEE80211W */
+
+	wpa_hexdump_key(MSG_DEBUG, "mesh: Plaintext AMPE element",
+			ampe_ie, 2 + len);
 
 	/* IE: MIC */
-	mic_ie[0] = WLAN_EID_MIC;
-	mic_ie[1] = AES_BLOCK_SIZE;
-	wpabuf_put_data(buf, mic_ie, 2);
+	wpabuf_put_u8(buf, WLAN_EID_MIC);
+	wpabuf_put_u8(buf, AES_BLOCK_SIZE);
 	/* MIC field is output ciphertext */
 
 	/* encrypt after MIC */
-	mic_payload = (u8 *) wpabuf_put(buf, 2 + sizeof(*ampe) +
-					AES_BLOCK_SIZE);
+	mic_payload = wpabuf_put(buf, 2 + len + AES_BLOCK_SIZE);
 
-	if (aes_siv_encrypt(sta->aek, ampe_ie, 2 + sizeof(*ampe), 3,
+	if (aes_siv_encrypt(sta->aek, ampe_ie, 2 + len, 3,
 			    aad, aad_len, mic_payload)) {
 		wpa_printf(MSG_ERROR, "protect frame: failed to encrypt");
 		ret = -ENOMEM;
-		goto free;
 	}
 
-free:
 	os_free(ampe_ie);
-	os_free(mic_ie);
 
 	return ret;
 }
@@ -526,14 +594,15 @@
 {
 	int ret = 0;
 	struct ieee80211_ampe_ie *ampe;
-	u8 null_nonce[32] = {};
+	u8 null_nonce[WPA_NONCE_LEN] = {};
 	u8 ampe_eid;
 	u8 ampe_ie_len;
-	u8 *ampe_buf, *crypt = NULL;
+	u8 *ampe_buf, *crypt = NULL, *pos, *end;
 	size_t crypt_len;
 	const u8 *aad[] = { sta->addr, wpa_s->own_addr, cat };
 	const size_t aad_len[] = { ETH_ALEN, ETH_ALEN,
 				   (elems->mic - 2) - cat };
+	size_t key_len;
 
 	if (!sta->sae) {
 		struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
@@ -562,7 +631,7 @@
 		return -1;
 
 	crypt_len = elems_len - (elems->mic - start);
-	if (crypt_len < 2) {
+	if (crypt_len < 2 + AES_BLOCK_SIZE) {
 		wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: missing ampe ie");
 		return -1;
 	}
@@ -584,10 +653,15 @@
 		goto free;
 	}
 
+	crypt_len -= AES_BLOCK_SIZE;
+	wpa_hexdump_key(MSG_DEBUG, "mesh: Decrypted AMPE element",
+			ampe_buf, crypt_len);
+
 	ampe_eid = *ampe_buf++;
 	ampe_ie_len = *ampe_buf++;
 
 	if (ampe_eid != WLAN_EID_AMPE ||
+	    (size_t) 2 + ampe_ie_len > crypt_len ||
 	    ampe_ie_len < sizeof(struct ieee80211_ampe_ie)) {
 		wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: invalid ampe ie");
 		ret = -1;
@@ -595,17 +669,67 @@
 	}
 
 	ampe = (struct ieee80211_ampe_ie *) ampe_buf;
-	if (os_memcmp(ampe->peer_nonce, null_nonce, 32) != 0 &&
-	    os_memcmp(ampe->peer_nonce, sta->my_nonce, 32) != 0) {
+	pos = (u8 *) (ampe + 1);
+	end = ampe_buf + ampe_ie_len;
+	if (os_memcmp(ampe->peer_nonce, null_nonce, WPA_NONCE_LEN) != 0 &&
+	    os_memcmp(ampe->peer_nonce, sta->my_nonce, WPA_NONCE_LEN) != 0) {
 		wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: invalid peer nonce");
 		ret = -1;
 		goto free;
 	}
 	os_memcpy(sta->peer_nonce, ampe->local_nonce,
 		  sizeof(ampe->local_nonce));
-	os_memcpy(sta->mgtk, ampe->mgtk, sizeof(ampe->mgtk));
 
-	/* todo parse mgtk expiration */
+	/* TODO: Key Replay Counter[8] in Mesh Group Key Inform/Acknowledge
+	 * frames */
+
+	/*
+	 * GTKdata[variable]:
+	 * MGTK[variable] || Key RSC[8] || GTKExpirationTime[4]
+	 */
+	sta->mgtk_key_id = 1; /* FIX: Where to get Key ID? */
+	key_len = wpa_cipher_key_len(wpa_s->mesh_rsn->group_cipher);
+	if ((int) key_len + WPA_KEY_RSC_LEN + 4 > end - pos) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "mesh: Truncated AMPE element");
+		ret = -1;
+		goto free;
+	}
+	sta->mgtk_len = key_len;
+	os_memcpy(sta->mgtk, pos, sta->mgtk_len);
+	wpa_hexdump_key(MSG_DEBUG, "mesh: GTKdata - MGTK",
+			sta->mgtk, sta->mgtk_len);
+	pos += sta->mgtk_len;
+	wpa_hexdump(MSG_DEBUG, "mesh: GTKdata - MGTK - Key RSC",
+		    pos, WPA_KEY_RSC_LEN);
+	os_memcpy(sta->mgtk_rsc, pos, sizeof(sta->mgtk_rsc));
+	pos += WPA_KEY_RSC_LEN;
+	wpa_printf(MSG_DEBUG,
+		   "mesh: GTKdata - MGTK - GTKExpirationTime: %u seconds",
+		   WPA_GET_LE32(pos));
+	pos += 4;
+
+#ifdef CONFIG_IEEE80211W
+	/*
+	 * IGTKdata[variable]:
+	 * Key ID[2], IPN[6], IGTK[variable]
+	 */
+	key_len = wpa_cipher_key_len(wpa_s->mesh_rsn->mgmt_group_cipher);
+	if (end - pos >= (int) (2 + 6 + key_len)) {
+		sta->igtk_key_id = WPA_GET_LE16(pos);
+		wpa_printf(MSG_DEBUG, "mesh: IGTKdata - Key ID %u",
+			   sta->igtk_key_id);
+		pos += 2;
+		os_memcpy(sta->igtk_rsc, pos, sizeof(sta->igtk_rsc));
+		wpa_hexdump(MSG_DEBUG, "mesh: IGTKdata - IPN",
+			    sta->igtk_rsc, sizeof(sta->igtk_rsc));
+		pos += 6;
+		os_memcpy(sta->igtk, pos, key_len);
+		sta->igtk_len = key_len;
+		wpa_hexdump_key(MSG_DEBUG, "mesh: IGTKdata - IGTK",
+				sta->igtk, sta->igtk_len);
+	}
+#endif /* CONFIG_IEEE80211W */
+
 free:
 	os_free(crypt);
 	return ret;
diff --git a/wpa_supplicant/mesh_rsn.h b/wpa_supplicant/mesh_rsn.h
index 89601d4..8775ced 100644
--- a/wpa_supplicant/mesh_rsn.h
+++ b/wpa_supplicant/mesh_rsn.h
@@ -12,7 +12,15 @@
 struct mesh_rsn {
 	struct wpa_supplicant *wpa_s;
 	struct wpa_authenticator *auth;
-	u8 mgtk[16];
+	unsigned int pairwise_cipher;
+	unsigned int group_cipher;
+	u8 mgtk[WPA_TK_MAX_LEN];
+	size_t mgtk_len;
+	u8 mgtk_key_id;
+	unsigned int mgmt_group_cipher;
+	u8 igtk_key_id;
+	u8 igtk[WPA_TK_MAX_LEN];
+	size_t igtk_len;
 #ifdef CONFIG_SAE
 	struct wpabuf *sae_token;
 	int sae_group_index;
diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c
index e739363..67e36ae 100644
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -668,13 +668,13 @@
 
 
 void wpas_notify_p2p_group_started(struct wpa_supplicant *wpa_s,
-				   struct wpa_ssid *ssid, int network_id,
+				   struct wpa_ssid *ssid, int persistent,
 				   int client)
 {
 	/* Notify a group has been started */
 	wpas_dbus_register_p2p_group(wpa_s, ssid);
 
-	wpas_dbus_signal_p2p_group_started(wpa_s, ssid, client, network_id);
+	wpas_dbus_signal_p2p_group_started(wpa_s, client, persistent);
 }
 
 
diff --git a/wpa_supplicant/notify.h b/wpa_supplicant/notify.h
index 1b7f04d..8cce0f3 100644
--- a/wpa_supplicant/notify.h
+++ b/wpa_supplicant/notify.h
@@ -113,7 +113,7 @@
 					 u16 config_methods,
 					 unsigned int generated_pin);
 void wpas_notify_p2p_group_started(struct wpa_supplicant *wpa_s,
-				   struct wpa_ssid *ssid, int network_id,
+				   struct wpa_ssid *ssid, int persistent,
 				   int client);
 void wpas_notify_p2p_group_formation_failure(struct wpa_supplicant *wpa_s,
 					     const char *reason);
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index 8f6acd6..8c5af5e 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -1301,7 +1301,6 @@
 	int client;
 	int persistent;
 	u8 go_dev_addr[ETH_ALEN];
-	int network_id = -1;
 
 	/*
 	 * This callback is likely called for the main interface. Update wpa_s
@@ -1376,16 +1375,15 @@
 	}
 
 	if (persistent)
-		network_id = wpas_p2p_store_persistent_group(wpa_s->p2pdev,
-							     ssid, go_dev_addr);
+		wpas_p2p_store_persistent_group(wpa_s->p2pdev,
+						ssid, go_dev_addr);
 	else {
 		os_free(wpa_s->global->add_psk);
 		wpa_s->global->add_psk = NULL;
 	}
-	if (network_id < 0 && ssid)
-		network_id = ssid->id;
+
 	if (!client) {
-		wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 0);
+		wpas_notify_p2p_group_started(wpa_s, ssid, persistent, 0);
 		os_get_reltime(&wpa_s->global->p2p_go_wait_client);
 	}
 }
@@ -1750,7 +1748,6 @@
 	struct wpa_supplicant *wpa_s = ctx;
 	struct p2p_go_neg_results *params = data;
 	struct wpa_ssid *ssid;
-	int network_id = -1;
 
 	wpa_s->ap_configured_cb = NULL;
 	wpa_s->ap_configured_cb_ctx = NULL;
@@ -1797,14 +1794,14 @@
 
 		os_get_reltime(&wpa_s->global->p2p_go_wait_client);
 		if (params->persistent_group) {
-			network_id = wpas_p2p_store_persistent_group(
+			wpas_p2p_store_persistent_group(
 				wpa_s->p2pdev, ssid,
 				wpa_s->global->p2p_dev_addr);
 			wpas_p2p_add_psk_list(wpa_s, ssid);
 		}
-		if (network_id < 0)
-			network_id = ssid->id;
-		wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 0);
+
+		wpas_notify_p2p_group_started(wpa_s, ssid,
+					      params->persistent_group, 0);
 		wpas_p2p_cross_connect_setup(wpa_s);
 		wpas_p2p_set_group_idle_timeout(wpa_s);
 
@@ -6941,7 +6938,6 @@
 {
 	struct wpa_ssid *ssid = wpa_s->current_ssid;
 	u8 go_dev_addr[ETH_ALEN];
-	int network_id = -1;
 	int persistent;
 	int freq;
 	u8 ip[3 * 4];
@@ -7000,11 +6996,10 @@
 			       ip_addr);
 
 	if (persistent)
-		network_id = wpas_p2p_store_persistent_group(wpa_s->p2pdev,
-							     ssid, go_dev_addr);
-	if (network_id < 0)
-		network_id = ssid->id;
-	wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 1);
+		wpas_p2p_store_persistent_group(wpa_s->p2pdev,
+						ssid, go_dev_addr);
+
+	wpas_notify_p2p_group_started(wpa_s, ssid, persistent, 1);
 }
 
 
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index 330679c..6ade9af 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -227,10 +227,8 @@
 	}
 
 	ctx = wpa_scan_clone_params(params);
-	if (ctx == NULL)
-		return -1;
-
-	if (radio_add_work(wpa_s, 0, "scan", 0, wpas_trigger_scan_cb, ctx) < 0)
+	if (!ctx ||
+	    radio_add_work(wpa_s, 0, "scan", 0, wpas_trigger_scan_cb, ctx) < 0)
 	{
 		wpa_scan_free_params(ctx);
 		wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_FAILED "ret=-1");
@@ -438,6 +436,13 @@
 	enum wps_request_type req_type = WPS_REQ_ENROLLEE_INFO;
 #endif /* CONFIG_WPS */
 
+#ifdef CONFIG_P2P
+	if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT)
+		wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_CLIENT);
+	else
+#endif /* CONFIG_P2P */
+		wpa_drv_get_ext_capa(wpa_s, WPA_IF_STATION);
+
 	ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
 					     sizeof(ext_capab));
 	if (ext_capab_len > 0 &&
@@ -837,12 +842,10 @@
 		 * slot for the zero-terminator.
 		 */
 		params.freqs = os_malloc(sizeof(int) * 2);
-		if (params.freqs == NULL) {
-			wpa_dbg(wpa_s, MSG_ERROR, "Memory allocation failed");
-			return;
+		if (params.freqs) {
+			params.freqs[0] = wpa_s->assoc_freq;
+			params.freqs[1] = 0;
 		}
-		params.freqs[0] = wpa_s->assoc_freq;
-		params.freqs[1] = 0;
 
 		/*
 		 * Reset the reattach flag so that we fall back to full scan if
@@ -2611,13 +2614,6 @@
 			goto fail;
 		}
 
-		if (!scan_plan->interval) {
-			wpa_printf(MSG_ERROR,
-				   "scan plan %u: Interval cannot be zero",
-				   num);
-			goto fail;
-		}
-
 		if (scan_plan->interval > wpa_s->max_sched_scan_plan_interval) {
 			wpa_printf(MSG_WARNING,
 				   "scan plan %u: Scan interval too long(%u), use the maximum allowed(%u)",
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index 2fbb2c6..3a8f5b1 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -455,6 +455,11 @@
 	}
 #endif /* CONFIG_MBO */
 
+	if (params.p2p)
+		wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_CLIENT);
+	else
+		wpa_drv_get_ext_capa(wpa_s, WPA_IF_STATION);
+
 	ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
 					     sizeof(ext_capab));
 	if (ext_capab_len > 0) {
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index aa785bd..d3848cb 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -2039,6 +2039,16 @@
 			if (chwidth == VHT_CHANWIDTH_80P80MHZ)
 				break;
 		}
+	} else if (ssid->max_oper_chwidth == VHT_CHANWIDTH_160MHZ) {
+		if (freq->freq == 5180) {
+			chwidth = VHT_CHANWIDTH_160MHZ;
+			vht_caps |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+			seg0 = 50;
+		} else if (freq->freq == 5520) {
+			chwidth = VHT_CHANWIDTH_160MHZ;
+			vht_caps |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+			seg0 = 114;
+		}
 	}
 
 	if (hostapd_set_freq_params(&vht_freq, mode->mode, freq->freq,
@@ -2291,6 +2301,11 @@
 	 * element in all cases, it is justifiable to skip it to avoid
 	 * interoperability issues.
 	 */
+	if (ssid->p2p_group)
+		wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_CLIENT);
+	else
+		wpa_drv_get_ext_capa(wpa_s, WPA_IF_STATION);
+
 	if (!bss || wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB)) {
 		u8 ext_capab[18];
 		int ext_capab_len;
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index 2d487c5..1d86a71 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -168,10 +168,13 @@
 fast_reauth=1
 
 # OpenSSL Engine support
-# These options can be used to load OpenSSL engines.
+# These options can be used to load OpenSSL engines in special or legacy
+# modes.
 # The two engines that are supported currently are shown below:
 # They are both from the opensc project (http://www.opensc.org/)
-# By default no engines are loaded.
+# By default the PKCS#11 engine is loaded if the client_cert or
+# private_key option appear to be a PKCS#11 URI, and these options
+# should not need to be used explicitly.
 # make the opensc engine available
 #opensc_engine_path=/usr/lib/opensc/engine_opensc.so
 # make the pkcs11 engine available
@@ -440,6 +443,12 @@
 #     matching network block
 #auto_interworking=0
 
+# GAS Address3 field behavior
+# 0 = P2P specification (Address3 = AP BSSID); default
+# 1 = IEEE 802.11 standard compliant (Address3 = Wildcard BSSID when
+#     sent to not-associated AP; if associated, AP BSSID)
+#gas_address3=0
+
 # credential block
 #
 # Each credential used for automatic network selection is configured as a set
@@ -474,6 +483,10 @@
 #	(EAP-TLS). Full path to the file should be used since working
 #	directory may change when wpa_supplicant is run in the background.
 #
+#	Certificates from PKCS#11 tokens can be referenced by a PKCS#11 URI.
+#
+#	For example: private_key="pkcs11:manufacturer=piv_II;id=%01"
+#
 #	Alternatively, a named configuration blob can be used by setting
 #	this to blob://blob_name.
 #
@@ -484,6 +497,9 @@
 #	used since working directory may change when wpa_supplicant is run
 #	in the background.
 #
+#	Keys in PKCS#11 tokens can be referenced by a PKCS#11 URI.
+#	For example: private_key="pkcs11:manufacturer=piv_II;id=%01"
+#
 #	Windows certificate store can be used by leaving client_cert out and
 #	configuring private_key in one of the following formats:
 #
@@ -1581,22 +1597,10 @@
 	group=CCMP TKIP
 	identity="user@example.com"
 	ca_cert="/etc/cert/ca.pem"
-	client_cert="/etc/cert/user.pem"
 
-	engine=1
-
-	# The engine configured here must be available. Look at
-	# OpenSSL engine support in the global section.
-	# The key available through the engine must be the private key
-	# matching the client certificate configured above.
-
-	# use the opensc engine
-	#engine_id="opensc"
-	#key_id="45"
-
-	# use the pkcs11 engine
-	engine_id="pkcs11"
-	key_id="id_45"
+	# Certificate and/or key identified by PKCS#11 URI (RFC7512)
+	client_cert="pkcs11:manufacturer=piv_II;id=%01"
+	private_key="pkcs11:manufacturer=piv_II;id=%01"
 
 	# Optional PIN configuration; this can be left out and PIN will be
 	# asked through the control interface