[PATCH 09/13] P2P: Add support for GO negotiation wrapped in PASN auth frame

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Add P2P2 support for GO negotiation wrapped in PASN authentication
frames as a action wrapper attribute.

Signed-off-by: Shivani Baranwal <quic_shivbara@xxxxxxxxxxx>

diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 9af586a7b..090bb2655 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -2418,7 +2418,8 @@ static void pasn_fils_auth_resp(struct hostapd_data *hapd,
 			      wpabuf_head(pasn->secret),
 			      wpabuf_len(pasn->secret),
 			      pasn_get_ptk(sta->pasn), pasn_get_akmp(sta->pasn),
-			      pasn_get_cipher(sta->pasn), sta->pasn->kdk_len);
+			      pasn_get_cipher(sta->pasn), sta->pasn->kdk_len,
+			      sta->pasn->kek_len);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to derive PTK");
 		goto fail;
diff --git a/src/common/common_module_tests.c b/src/common/common_module_tests.c
index a95ae36dc..5763c51f4 100644
--- a/src/common/common_module_tests.c
+++ b/src/common/common_module_tests.c
@@ -651,7 +651,7 @@ static int pasn_test_pasn_auth(void)
 			      spa_addr, bssid,
 			      dhss, sizeof(dhss),
 			      &ptk, WPA_KEY_MGMT_PASN, WPA_CIPHER_CCMP,
-			      WPA_KDK_MAX_LEN);
+			      WPA_KDK_MAX_LEN, 0);
 
 	if (ret)
 		return ret;
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index c753d49e7..71a812c7f 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -396,6 +396,10 @@ static int ieee802_11_parse_extension(const u8 *pos, size_t elen,
 		elems->mbssid_known_bss = pos;
 		elems->mbssid_known_bss_len = elen;
 		break;
+	case WLAN_EID_EXT_PASN_ENCRYPTED_ELEMENT:
+		elems->pasn_encrypted_ie = pos;
+		elems->pasn_encrypted_ie_len = elen;
+		break;
 	default:
 		if (show_errors) {
 			wpa_printf(MSG_MSGDUMP,
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index 32c6b08b5..66dc5f900 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -66,6 +66,7 @@ struct ieee802_11_elems {
 	const u8 *vendor_vht;
 	const u8 *p2p;
 	const u8 *p2p2_ie;
+	const u8 *pasn_encrypted_ie;
 	const u8 *wfd;
 	const u8 *link_id;
 	const u8 *interworking;
@@ -138,6 +139,7 @@ struct ieee802_11_elems {
 	u8 vendor_vht_len;
 	u8 p2p_len;
 	u8 p2p2_ie_len;
+	u8 pasn_encrypted_ie_len;
 	u8 wfd_len;
 	u8 interworking_len;
 	u8 qos_map_set_len;
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index ee45b124f..7e3233d95 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -524,6 +524,7 @@
 #define WLAN_EID_EXT_MULTI_LINK_TRAFFIC_INDICATION 110
 #define WLAN_EID_EXT_QOS_CHARACTERISTICS 113
 #define WLAN_EID_EXT_AKM_SUITE_SELECTOR 114
+#define WLAN_EID_EXT_PASN_ENCRYPTED_ELEMENT 140
 
 /* Extended Capabilities field */
 #define WLAN_EXT_CAPAB_20_40_COEX 0
@@ -616,6 +617,7 @@
 #define WLAN_RSNX_CAPAB_SECURE_RTT 9
 #define WLAN_RSNX_CAPAB_URNM_MFPR_X20 10
 #define WLAN_RSNX_CAPAB_URNM_MFPR 15
+#define WLAN_RSNX_CAPAB_KEK 18
 #define WLAN_RSNX_CAPAB_SSID_PROTECTION 21
 
 /* Multiple BSSID element subelements */
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index 94ce43dde..b7438cea9 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -1460,9 +1460,9 @@ int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len,
 		    const u8 *spa, const u8 *bssid,
 		    const u8 *dhss, size_t dhss_len,
 		    struct wpa_ptk *ptk, int akmp, int cipher,
-		    size_t kdk_len)
+		    size_t kdk_len, size_t kek_len)
 {
-	u8 tmp[WPA_KCK_MAX_LEN + WPA_TK_MAX_LEN + WPA_KDK_MAX_LEN];
+	u8 tmp[WPA_KCK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN + WPA_KDK_MAX_LEN];
 	u8 *data;
 	size_t data_len, ptk_len;
 	int ret = -1;
@@ -1497,7 +1497,7 @@ int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len,
 	ptk->kck_len = WPA_PASN_KCK_LEN;
 	ptk->tk_len = wpa_cipher_key_len(cipher);
 	ptk->kdk_len = kdk_len;
-	ptk->kek_len = 0;
+	ptk->kek_len = kek_len;
 	ptk->kek2_len = 0;
 	ptk->kck2_len = 0;
 
@@ -1508,7 +1508,7 @@ int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len,
 		goto err;
 	}
 
-	ptk_len = ptk->kck_len + ptk->tk_len + ptk->kdk_len;
+	ptk_len = ptk->kck_len + ptk->tk_len + ptk->kdk_len + ptk->kek_len;
 	if (ptk_len > sizeof(tmp))
 		goto err;
 
@@ -1537,12 +1537,18 @@ int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len,
 	os_memcpy(ptk->kck, tmp, WPA_PASN_KCK_LEN);
 	wpa_hexdump_key(MSG_DEBUG, "PASN: KCK:", ptk->kck, WPA_PASN_KCK_LEN);
 
-	os_memcpy(ptk->tk, tmp + WPA_PASN_KCK_LEN, ptk->tk_len);
+	if (kek_len) {
+		os_memcpy(ptk->kek, tmp + WPA_PASN_KCK_LEN, ptk->kek_len);
+		wpa_hexdump_key(MSG_DEBUG, "PASN: KEK:",
+				ptk->kek, ptk->kek_len);
+	}
+
+	os_memcpy(ptk->tk, tmp + WPA_PASN_KCK_LEN + ptk->kek_len, ptk->tk_len);
 	wpa_hexdump_key(MSG_DEBUG, "PASN: TK:", ptk->tk, ptk->tk_len);
 
 	if (kdk_len) {
-		os_memcpy(ptk->kdk, tmp + WPA_PASN_KCK_LEN + ptk->tk_len,
-			  ptk->kdk_len);
+		os_memcpy(ptk->kdk, tmp + WPA_PASN_KCK_LEN + ptk->kek_len +
+			  ptk->tk_len, ptk->kdk_len);
 		wpa_hexdump_key(MSG_DEBUG, "PASN: KDK:",
 				ptk->kdk, ptk->kdk_len);
 	}
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index 1e3136843..249f1d1c1 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -248,6 +248,7 @@ struct wpa_eapol_key {
 #define WPA_PASN_KCK_LEN 32
 #define WPA_PASN_MIC_MAX_LEN 24
 #define WPA_LTF_KEYSEED_MAX_LEN 48
+#define WPA_KEK_128 16
 
 /**
  * struct wpa_ptk - WPA Pairwise Transient Key
@@ -751,7 +752,7 @@ int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len,
 		    const u8 *spa, const u8 *bssid,
 		    const u8 *dhss, size_t dhss_len,
 		    struct wpa_ptk *ptk, int akmp, int cipher,
-		    size_t kdk_len);
+		    size_t kdk_len, size_t kek_len);
 
 u8 pasn_mic_len(int akmp, int cipher);
 
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index 5e12402e7..cd7817ecd 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -15,12 +15,15 @@
 #include "common/ieee802_11_common.h"
 #include "common/wpa_ctrl.h"
 #include "crypto/sha256.h"
+#include "crypto/sha384.h"
 #include "crypto/crypto.h"
 #include "crypto/random.h"
 #include "wps/wps_i.h"
 #include "p2p_i.h"
 #include "p2p.h"
-
+#include "common/sae.h"
+#include "pasn/pasn_common.h"
+#include "crypto/aes_wrap.h"
 
 static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx);
 static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev);
@@ -243,6 +246,9 @@ void p2p_go_neg_failed(struct p2p_data *p2p, int status)
 	peer->go_neg_conf = NULL;
 	p2p->go_neg_peer = NULL;
 
+	if (peer->p2p2 && peer->pasn)
+		wpa_pasn_reset(peer->pasn);
+
 	os_memset(&res, 0, sizeof(res));
 	res.status = status;
 	os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr, ETH_ALEN);
@@ -960,6 +966,12 @@ static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev)
 		dev->bootstrap_params = NULL;
 	}
 
+	if (dev->pasn) {
+		wpa_pasn_reset(dev->pasn);
+		pasn_data_deinit(dev->pasn);
+		dev->pasn = NULL;
+	}
+
 	wpabuf_free(dev->info.wfd_subelems);
 	wpabuf_free(dev->info.vendor_elems);
 	wpabuf_free(dev->go_neg_conf);
@@ -1912,6 +1924,9 @@ void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer)
 	wpabuf_free(peer->go_neg_conf);
 	peer->go_neg_conf = NULL;
 
+	if (peer->p2p2 && peer->pasn)
+		wpa_pasn_reset(peer->pasn);
+
 	p2p_set_state(p2p, P2P_PROVISIONING);
 	p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res);
 }
@@ -1928,17 +1943,16 @@ static void p2p_rx_p2p_action(struct p2p_data *p2p, const u8 *sa,
 
 	switch (data[0]) {
 	case P2P_GO_NEG_REQ:
-		p2p_process_go_neg_req(p2p, sa, data + 1, len - 1, rx_freq);
+		p2p_handle_go_neg_req(p2p, sa, data + 1, len - 1, rx_freq);
 		break;
 	case P2P_GO_NEG_RESP:
-		p2p_process_go_neg_resp(p2p, sa, data + 1, len - 1, rx_freq);
+		p2p_handle_go_neg_resp(p2p, sa, data + 1, len - 1, rx_freq);
 		break;
 	case P2P_GO_NEG_CONF:
-		p2p_process_go_neg_conf(p2p, sa, data + 1, len - 1);
+		p2p_handle_go_neg_conf(p2p, sa, data + 1, len - 1, false);
 		break;
 	case P2P_INVITATION_REQ:
-		p2p_process_invitation_req(p2p, sa, data + 1, len - 1,
-					   rx_freq);
+		p2p_handle_invitation_req(p2p, sa, data + 1, len - 1, rx_freq);
 		break;
 	case P2P_INVITATION_RESP:
 		p2p_process_invitation_resp(p2p, sa, data + 1, len - 1);
@@ -3016,6 +3030,9 @@ int p2p_pairing_info_init(struct p2p_data *p2p)
 			 pairing_info->dev_ik.dik_len);
 	p2p->pairing_info = pairing_info;
 
+	p2p->initiator_pmksa = pasn_initiator_pmksa_cache_init();
+	p2p->responder_pmksa = pasn_responder_pmksa_cache_init();
+
 	return 0;
 }
 
@@ -3085,6 +3102,8 @@ struct p2p_data * p2p_init(const struct p2p_config *cfg)
 
 void p2p_pairing_info_deinit(struct p2p_data *p2p)
 {
+	pasn_initiator_pmksa_cache_deinit(p2p->initiator_pmksa);
+	pasn_responder_pmksa_cache_deinit(p2p->responder_pmksa);
 	os_free(p2p->pairing_info);
 }
 
@@ -5860,3 +5879,745 @@ void p2p_process_usd_elems(struct p2p_data *p2p, const u8 *ies, u16 ies_len,
 
 	p2p_parse_free(&msg);
 }
+
+
+int p2p_prepare_pasn_extra_ie(struct p2p_data *p2p, struct wpabuf *extra_ies,
+			      struct wpabuf *frame, bool dira)
+{
+	struct wpabuf *buf, *buf2;
+
+	buf = wpabuf_alloc(1500);
+	if (!buf) {
+		p2p_dbg(p2p, "Mem alloc failed for buf");
+		return -1;
+	}
+
+	/* P2P Capability Extension attribute */
+	p2p_buf_add_pcea(buf, p2p);
+
+	/* P2P Device Identity Resolution attribute */
+	if (dira)
+		p2p_buf_add_dira(buf, p2p);
+
+	if (frame) {
+		p2p_dbg(p2p, "P2P: Added Action frame wrapper");
+		wpabuf_put_u8(buf, P2P_ATTR_ACTION_FRAME_WRAPPER);
+		wpabuf_put_le16(buf, wpabuf_len(frame));
+		wpabuf_put_buf(buf, frame);
+	}
+
+	buf2 = p2p_encaps_p2p_vendor_ie(p2p, buf, P2P2_IE_VENDOR_TYPE);
+	wpabuf_free(buf);
+
+	wpabuf_put_buf(extra_ies, buf2);
+	wpabuf_free(buf2);
+
+	return 0;
+}
+
+struct wpabuf *p2p_pairing_generate_rsnxe(int akmp)
+{
+	u32 capab;
+	size_t flen = 0;
+	struct wpabuf *buf;
+
+	capab = BIT(WLAN_RSNX_CAPAB_KEK);
+
+	if (akmp == WPA_KEY_MGMT_SAE)
+		capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
+
+	while (capab >> flen * 8)
+		flen++;
+
+	buf = wpabuf_alloc(2 + flen);
+	if (!buf) {
+		wpa_printf(MSG_ERROR, "Memory allocation failed");
+		return NULL;
+	}
+
+	if (wpabuf_tailroom(buf) < 2 + flen) {
+		wpa_printf(MSG_ERROR, "wpabuf tail room small");
+		wpabuf_free(buf);
+		return NULL;
+	}
+	capab |= flen - 1; /* bit 0-3 = Field length (n - 1) */
+
+	wpa_printf(MSG_DEBUG, "RSNXE capabilities: %04x", capab);
+	wpabuf_put_u8(buf, WLAN_EID_RSNX);
+	wpabuf_put_u8(buf, flen);
+	while (flen--) {
+		wpabuf_put_u8(buf, (capab & 0xff));
+		capab = capab >> 8;
+	}
+	return buf;
+}
+
+/* sae password id to derive pt */
+#define P2P_PAIRING_SSID "516F9A020000"
+
+void p2p_pairing_set_password(struct pasn_data *pasn, const char *passphrase,
+			      u32 len)
+{
+	const u8 *pairing_ssid;
+	size_t pairing_ssid_len;
+
+	if (!passphrase) {
+		wpa_printf(MSG_ERROR, "p2p pairing password NULL");
+		return;
+	}
+
+	pairing_ssid = (const u8 *)(P2P_PAIRING_SSID);
+	pairing_ssid_len = strlen(P2P_PAIRING_SSID);
+	pasn->pt = sae_derive_pt(NULL, pairing_ssid, pairing_ssid_len,
+				 (const u8 *)passphrase, len, NULL);
+	/* Set passpharse for Pairing Responder to validate PASN auth1 frame*/
+	pasn->password = passphrase;
+}
+
+void p2p_pasn_initialize(struct p2p_data *p2p, struct p2p_device *dev,
+			 const u8 *addr, int freq)
+{
+	struct pasn_data *pasn;
+	struct wpabuf *rsnxe;
+
+	if (!p2p || !dev)
+		return;
+
+	if (dev->pasn)
+		wpa_pasn_reset(dev->pasn);
+	else
+		dev->pasn = pasn_data_init();
+
+	pasn = dev->pasn;
+
+	os_memcpy(pasn->own_addr, p2p->cfg->dev_addr, ETH_ALEN);
+	os_memcpy(pasn->peer_addr, addr, ETH_ALEN);
+
+	if (dev->role == P2P_ROLE_PAIRING_INITIATOR)
+		memcpy(pasn->bssid, pasn->peer_addr, ETH_ALEN);
+	else
+		memcpy(pasn->bssid, pasn->own_addr, ETH_ALEN);
+
+	pasn->noauth = 1;
+	pasn->group = 19;
+	pasn->cipher = WPA_CIPHER_CCMP;
+	pasn->kek_len = WPA_KEK_128;
+
+	if (dev->password_len) {
+		pasn->akmp = WPA_KEY_MGMT_SAE;
+		p2p_pairing_set_password(pasn, dev->password,
+					 dev->password_len);
+		pasn->rsnxe_capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
+	} else {
+		pasn->akmp = WPA_KEY_MGMT_PASN;
+	}
+
+	pasn->rsn_pairwise = pasn->cipher;
+	pasn->wpa_key_mgmt = pasn->akmp;
+
+	rsnxe = p2p_pairing_generate_rsnxe(pasn->akmp);
+	if (rsnxe) {
+		pasn->rsnxe_ie = os_zalloc(wpabuf_len(rsnxe));
+		if (!pasn->rsnxe_ie) {
+			p2p_dbg(p2p, "Mem alloc failed for pasn rsnxe ie");
+			wpabuf_free(rsnxe);
+			return;
+		}
+		os_memcpy((u8 *)pasn->rsnxe_ie, wpabuf_head_u8(rsnxe),
+			  wpabuf_len(rsnxe));
+		pasn->rsnxe_ie_len = wpabuf_len(rsnxe);
+		wpabuf_free(rsnxe);
+	}
+
+	if (dev->role == P2P_ROLE_PAIRING_INITIATOR)
+		pasn->pmksa = p2p->initiator_pmksa;
+	else
+		pasn->pmksa = p2p->responder_pmksa;
+
+	pasn->cb_ctx = p2p->cfg->cb_ctx;
+	pasn->send_mgmt = p2p->cfg->pasn_send_mgmt;
+	pasn->update_extra_ies = p2p->cfg->pasn_update_extra_ies;
+	pasn->parse_encrypted_data = p2p->cfg->pasn_parse_encrypted_data;
+
+	pasn->freq = freq;
+}
+
+
+
+
+int p2p_initiate_pasn_auth(struct p2p_data *p2p, const u8 *addr, int freq)
+{
+	struct pasn_data *pasn;
+	struct p2p_device *dev;
+	struct wpabuf *extra_ies, *req;
+	int ret = 0;
+
+	if (!addr) {
+		p2p_dbg(p2p, "peer address NULL");
+		return -1;
+	}
+
+	dev = p2p_get_device(p2p, addr);
+	if (!dev) {
+		p2p_dbg(p2p, "Peer not known");
+		return -1;
+	}
+
+	dev->role = P2P_ROLE_PAIRING_INITIATOR;
+	p2p_pasn_initialize(p2p, dev, addr, freq);
+	pasn = dev->pasn;
+
+	/* FIXME:  Added to resolve listen freq issue resulting in GO Neg no
+	 * common channel failure
+	 */
+	p2p->cfg->reg_class = p2p->op_reg_class;
+	p2p->cfg->channel = p2p->op_channel;
+
+	req = p2p_build_go_neg_req(p2p, dev);
+	if (!req)
+		return -1;
+
+	p2p->go_neg_peer = dev;
+	dev->flags |= P2P_DEV_WAIT_GO_NEG_RESPONSE;
+
+	extra_ies = wpabuf_alloc(1500);
+	if (!extra_ies) {
+		wpabuf_free(req);
+		p2p_dbg(p2p, "Mem alloc failed for extra ies");
+		return -1;
+	}
+
+	if (p2p_prepare_pasn_extra_ie(p2p, extra_ies, req, false)) {
+		p2p_dbg(p2p, "prepare pasn extra ies failed");
+		ret = -1;
+		goto out;
+	}
+
+	pasn->extra_ies = os_zalloc(wpabuf_len(extra_ies));
+	if (!pasn->extra_ies) {
+		p2p_dbg(p2p, "Mem alloc failed for pasn extra ies");
+		ret = -1;
+		goto out;
+	}
+
+	os_memcpy((u8 *)pasn->extra_ies, wpabuf_head_u8(extra_ies),
+		  wpabuf_len(extra_ies));
+	pasn->extra_ies_len = wpabuf_len(extra_ies);
+
+	/* Start PASN Auth */
+	if (wpas_pasn_start(pasn, pasn->own_addr, pasn->peer_addr, pasn->bssid,
+			    pasn->akmp, pasn->cipher, pasn->group, pasn->freq,
+			    NULL, 0, NULL, 0, NULL)) {
+		p2p_dbg(p2p, "p2p pasn start failed");
+		ret = -1;
+	}
+out:
+	if (pasn->extra_ies) {
+		os_free((u8 *)pasn->extra_ies);
+		pasn->extra_ies = NULL;
+		pasn->extra_ies_len = 0;
+	}
+	wpabuf_free(req);
+	wpabuf_free(extra_ies);
+	return ret;
+}
+
+
+int p2p_pasn_handle_action_wrapper(struct p2p_data *p2p,
+				   struct p2p_device *dev,
+				   const struct ieee80211_mgmt *mgmt,
+				   size_t len, int freq, int trans_seq)
+{
+	const u8 *ies;
+	size_t ies_len;
+	size_t data_len = 0;
+	const u8 *data = NULL;
+	struct p2p_message msg;
+
+	ies = mgmt->u.auth.variable;
+	ies_len = len - offsetof(struct ieee80211_mgmt, u.auth.variable);
+
+	os_memset(&msg, 0, sizeof(msg));
+	if (p2p_parse_ies(ies, ies_len, &msg)) {
+		p2p_dbg(p2p, "Failed to parse P2P IE from auth frame");
+		p2p_parse_free(&msg);
+		return -1;
+	}
+
+	if (msg.dira && msg.dira_len) {
+	}
+
+	if (msg.action_frame_wrapper && msg.action_frame_wrapper_len) {
+		data = msg.action_frame_wrapper;
+		data_len = msg.action_frame_wrapper_len;
+		if (data[0] == WLAN_ACTION_PUBLIC &&
+		    data[1] == WLAN_PA_VENDOR_SPECIFIC) {
+			data += 2;
+			data_len -= 2;
+			if (data_len < 4 ||
+			    WPA_GET_BE32(data) != P2P_IE_VENDOR_TYPE) {
+				p2p_parse_free(&msg);
+				return -1;
+			}
+			data += 4;
+			data_len -= 4;
+		} else {
+			p2p_dbg(p2p, "Invalid category in action frame wrapper in Auth %d",
+				trans_seq);
+			p2p_parse_free(&msg);
+			return -1;
+		}
+	}
+
+	if (trans_seq == 1) {
+		if (msg.dira && data && data[0] == P2P_INVITATION_REQ) {
+			p2p_process_invitation_req(p2p, mgmt->sa, data + 1,
+						   data_len - 1, freq);
+			if (!p2p->invitation_resp)
+				p2p_dbg(p2p, "No Invitation Response found");
+			dev->pasn->action_frame_wrapper = p2p->invitation_resp;
+		} else if (data && data[0] == P2P_GO_NEG_REQ) {
+			p2p_process_go_neg_req(p2p, mgmt->sa, data + 1,
+					       data_len - 1, freq, true);
+			if (!p2p->go_neg_resp)
+				p2p_dbg(p2p, "No GO Neg Response found");
+			dev->pasn->action_frame_wrapper = p2p->go_neg_resp;
+		} else {
+			p2p_dbg(p2p, "Invalid action frame wrapper in Auth1");
+		}
+	} else if (trans_seq == 2) {
+		if (msg.dira && data && data[0] == P2P_INVITATION_RESP) {
+			p2p_process_invitation_resp(p2p, mgmt->sa, data + 1,
+						    data_len - 1);
+			dev->pasn->action_frame_wrapper = NULL;
+		} else if (data && data[0] == P2P_GO_NEG_RESP) {
+			p2p_process_go_neg_resp(p2p, mgmt->sa, data + 1,
+						data_len - 1, freq, true);
+			if (!p2p->go_neg_conf)
+				p2p_dbg(p2p, "No GO Neg confirm found");
+			dev->pasn->action_frame_wrapper = p2p->go_neg_conf;
+		} else {
+			p2p_dbg(p2p, "Invalid action frame wrapper in Auth2");
+		}
+	} else if (trans_seq == 3) {
+		if (data && data[0] == P2P_GO_NEG_CONF) {
+			p2p_handle_go_neg_conf(p2p, mgmt->sa, data + 1,
+					       data_len - 1, true);
+		} else {
+			p2p_invitation_resp_cb(p2p, P2P_SEND_ACTION_SUCCESS);
+		}
+	}
+	p2p_parse_free(&msg);
+	return 0;
+}
+
+
+static void p2p_pasn_add_encrypted_element(struct p2p_data *p2p,
+					   struct p2p_device *dev,
+					   struct wpabuf *buf)
+{
+	int ret;
+	struct pasn_data *pasn;
+	struct wpabuf *p2p2_ie;
+	u8 *len, *dika_len, *p2p2_ie_len;
+	u8 *pos, *key_data, *encrypted_data;
+	u16 key_data_len, pad_len = 0;
+
+	if (!p2p || !dev || !dev->pasn)
+		return;
+
+	pasn = dev->pasn;
+
+	if (dev->req_bootstrap_method != P2P_PBMA_OPPORTUNISTIC &&
+	    !p2p->pairing_info->enable_pairing_cache)
+		return;
+
+	p2p2_ie = wpabuf_alloc(100);
+	if (!p2p2_ie) {
+		p2p_dbg(p2p, "Mem alloc failed for p2p2 IE");
+		return;
+	}
+
+	p2p2_ie_len = p2p_buf_add_p2p2_ie_hdr(p2p2_ie);
+
+	if (p2p->pairing_info->enable_pairing_cache) {
+		wpabuf_put_u8(p2p2_ie, P2P_ATTR_DEVICE_IDENTITY_KEY);
+		dika_len = wpabuf_put(p2p2_ie, 2);
+
+		wpabuf_put_u8(p2p2_ie, DIRA_CIPHER_VERSION_128);
+		wpabuf_put_data(p2p2_ie, p2p->pairing_info->dev_ik.dik_data,
+				p2p->pairing_info->dev_ik.dik_len);
+		wpabuf_put_be32(p2p2_ie, p2p->pairing_info->dev_ik.expiration);
+
+		WPA_PUT_LE16(dika_len,
+			     (u8 *)wpabuf_put(p2p2_ie, 0) - dika_len - 2);
+	}
+
+	if (dev->req_bootstrap_method != P2P_PBMA_OPPORTUNISTIC &&
+	    dev->dev_password_len) {
+		wpabuf_put_u8(p2p2_ie, P2P_ATTR_PASSWORD);
+		wpabuf_put_le16(p2p2_ie, dev->dev_password_len);
+		wpabuf_put_data(p2p2_ie, dev->dev_password,
+				dev->dev_password_len);
+	}
+
+	p2p_buf_update_p2p2_ie_hdr(p2p2_ie, p2p2_ie_len);
+
+	key_data = (u8 *)wpabuf_head(p2p2_ie);
+	key_data_len = wpabuf_len(p2p2_ie);
+
+	pad_len = key_data_len % 8;
+
+	if (pad_len) {
+		pad_len = 8 - pad_len;
+		pos = key_data + key_data_len;
+		*pos++ = 0xdd;
+	}
+	key_data_len += pad_len + 8;
+
+	encrypted_data = os_malloc(key_data_len);
+	if (!encrypted_data) {
+		p2p_dbg(p2p, "P2P PASN: Mem alloc failed for encrypted data");
+		wpabuf_free(p2p2_ie);
+		return;
+	}
+	ret = aes_wrap(pasn->ptk.kek, pasn->ptk.kek_len,
+		       (key_data_len - 8) / 8, key_data, encrypted_data);
+	if (ret) {
+		p2p_dbg(p2p, "P2P PASN: AES upwrap failed, ret=%d", ret);
+		goto out;
+	}
+
+	wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+	len = wpabuf_put(buf, 1);
+
+	wpabuf_put_u8(buf, WLAN_EID_EXT_PASN_ENCRYPTED_ELEMENT);
+
+	wpabuf_put_data(buf, encrypted_data, key_data_len);
+	*len = (u8 *)wpabuf_put(buf, 0) - len - 1;
+
+out:
+	os_free(encrypted_data);
+	wpabuf_free(p2p2_ie);
+}
+
+
+int p2p_pasn_update_extra_ies(struct p2p_data *p2p, const u8 *peer_addr,
+			      bool dira)
+{
+	int ret = -1;
+	struct p2p_device *dev;
+	struct pasn_data *pasn;
+	struct wpabuf *extra_ies;
+
+	if (!p2p)
+		return -1;
+
+	dev = p2p_get_device(p2p, (u8 *)peer_addr);
+	if (!dev || !dev->pasn) {
+		p2p_dbg(p2p, "P2P PASN: Peer not found" MACSTR,
+			MAC2STR(peer_addr));
+		return -1;
+	}
+	pasn = dev->pasn;
+
+	extra_ies = wpabuf_alloc(1500);
+	if (!extra_ies) {
+		p2p_dbg(p2p, "Mem alloc failed for extra ies");
+		goto out;
+	}
+
+	if (p2p_prepare_pasn_extra_ie(p2p, extra_ies,
+				      pasn->action_frame_wrapper, dira)) {
+		p2p_dbg(p2p, "prepare pasn extra ies failed");
+		goto out;
+	}
+
+	p2p_pasn_add_encrypted_element(p2p, dev, extra_ies);
+
+	pasn->extra_ies = os_zalloc(wpabuf_len(extra_ies));
+	if (!pasn->extra_ies) {
+		p2p_dbg(p2p, "Mem alloc failed for pasn extra ies");
+		goto out;
+	}
+
+	os_memcpy((u8 *)pasn->extra_ies, wpabuf_head_u8(extra_ies),
+		  wpabuf_len(extra_ies));
+	pasn->extra_ies_len = wpabuf_len(extra_ies);
+	ret = 0;
+
+out:
+	wpabuf_free(extra_ies);
+	wpabuf_free(pasn->action_frame_wrapper);
+	pasn->action_frame_wrapper = NULL;
+
+	return ret;
+}
+
+
+int p2p_pasn_parse_encrypted_data(struct p2p_data *p2p, const u8 *data,
+				  size_t len)
+{
+	int ret = -1;
+	u8 attr_id;
+	u8 *buf, *pos;
+	u16 rem_len, attr_len;
+	struct p2p_device *dev;
+	struct pasn_data *pasn;
+	struct ieee802_11_elems elems;
+	const struct ieee80211_mgmt *mgmt =
+		(const struct ieee80211_mgmt *) data;
+
+	if (!p2p)
+		return -1;
+
+	dev = p2p_get_device(p2p, (u8 *)mgmt->sa);
+	if (!dev || !dev->pasn) {
+		p2p_dbg(p2p, "P2P PASN: Peer not found" MACSTR,
+			MAC2STR(mgmt->sa));
+		return -1;
+	}
+
+	if (ieee802_11_parse_elems(mgmt->u.auth.variable,
+				   len - offsetof(struct ieee80211_mgmt,
+						  u.auth.variable),
+				   &elems, 0) == ParseFailed) {
+		p2p_dbg(p2p, "P2P PASN: Failed parsing Authentication frame");
+		return -1;
+	}
+
+	if (!elems.pasn_encrypted_ie || !elems.pasn_encrypted_ie_len) {
+		p2p_dbg(p2p, "P2P PASN: No encrypted IEs");
+		return 0;
+	}
+
+	pasn = dev->pasn;
+	rem_len = elems.pasn_encrypted_ie_len;
+
+	buf = os_zalloc(rem_len);
+	if (!buf) {
+		p2p_dbg(p2p, "Mem alloc failed for buf");
+		return -1;
+	}
+
+	ret = aes_unwrap(pasn->ptk.kek, pasn->ptk.kek_len, (rem_len - 8) / 8,
+			 elems.pasn_encrypted_ie, buf);
+	if (ret) {
+		p2p_dbg(p2p, "P2P PASN: AES unwrap failed, ret=%d", ret);
+		goto done;
+	}
+
+	pos = buf;
+	if (pos[0] != WLAN_EID_VENDOR_SPECIFIC ||
+	    WPA_GET_BE32(&pos[2]) != P2P2_IE_VENDOR_TYPE) {
+		p2p_dbg(p2p, "P2P PASN: P2P2 IE not present");
+		goto done;
+	}
+
+	pos += 6;
+	rem_len -= 6;
+
+	while (rem_len > 2) {
+		attr_id = *pos++;
+		attr_len = WPA_GET_LE16(pos);
+
+		pos += 2;
+		rem_len -= 3;
+		switch (attr_id) {
+		case P2P_ATTR_DEVICE_IDENTITY_KEY:
+			if (rem_len < 13) {
+				p2p_dbg(p2p, "P2P PASN: Invalid rem len %d", rem_len);
+				goto done;
+			}
+			dev->info.dik_cipher_version = *pos++;
+			rem_len--;
+			if (dev->info.dik_cipher_version == 0) {
+				memcpy(dev->info.device_identity_key, pos, 16);
+				pos += 16;
+				rem_len -= 16;
+			} else {
+				p2p_dbg(p2p, "P2P PASN: Invalid cipher");
+				goto done;
+			}
+			dev->info.dik_lifetime = WPA_GET_BE32(pos);
+			pos += 4;
+			rem_len -= 4;
+			break;
+		case P2P_ATTR_PASSWORD:
+			if (rem_len < 1) {
+				p2p_dbg(p2p, "P2P PASN: Invalid rem len %d", rem_len);
+				goto done;
+			}
+			dev->info.password_len = attr_len;
+			memset(dev->info.password, 0,
+			       sizeof(dev->info.password));
+			memcpy(dev->info.password, pos, attr_len);
+			break;
+		default:
+			p2p_dbg(p2p, "Invalid Attr ID: %d", attr_id);
+			break;
+		}
+	}
+	ret = 0;
+done:
+	os_free(buf);
+	return ret;
+}
+
+int p2p_pasn_auth_tx_status(struct p2p_data *p2p, const u8 *data,
+			    size_t data_len, u8 acked)
+{
+	int ret = 0;
+	struct p2p_device *dev;
+	struct pasn_data *pasn;
+	const struct ieee80211_mgmt *mgmt =
+		(const struct ieee80211_mgmt *) data;
+
+	if (!p2p)
+		return -1;
+
+	dev = p2p_get_device(p2p, (u8 *)mgmt->da);
+	if (!dev || !dev->pasn) {
+		p2p_dbg(p2p, "P2P PASN: Peer not found" MACSTR,
+			MAC2STR(mgmt->da));
+		return -1;
+	}
+
+	pasn = dev->pasn;
+
+	ret = wpa_pasn_auth_tx_status(pasn, data, data_len, acked);
+	if (ret != 1 && acked == 0 && pasn->frame) {
+		return pasn->send_mgmt(pasn->cb_ctx, wpabuf_head(pasn->frame),
+				       wpabuf_len(pasn->frame), 0, pasn->freq,
+				       1000);
+	} else if (pasn->frame) {
+		wpabuf_free(pasn->frame);
+		pasn->frame = NULL;
+	}
+
+	if (ret != 1)
+		return ret;
+
+	if (dev == p2p->go_neg_peer)
+		p2p_go_complete(p2p, dev);
+
+	return 0;
+}
+
+int p2p_handle_pasn_auth(struct p2p_data *p2p, struct p2p_device *dev,
+			 const struct ieee80211_mgmt *mgmt, size_t len,
+			 int freq)
+{
+	struct pasn_data *pasn;
+	u16 auth_alg, auth_transaction, status_code;
+
+	if (!p2p || !dev || !dev->pasn)
+		return -1;
+
+	if (os_memcmp(mgmt->da, p2p->cfg->dev_addr, ETH_ALEN) != 0) {
+		p2p_dbg(p2p, "P2P PASN Responder: Not our frame");
+		return -1;
+	}
+
+	pasn = dev->pasn;
+	auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
+	status_code = le_to_host16(mgmt->u.auth.status_code);
+
+	auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
+
+	if (status_code != WLAN_STATUS_SUCCESS &&
+	    status_code != WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) {
+		p2p_dbg(p2p, "P2P PASN: Authentication rejected - status=%u",
+			status_code);
+		return -1;
+	}
+
+	if (auth_alg != WLAN_AUTH_PASN || auth_transaction == 2) {
+		p2p_dbg(p2p, "P2P PASN Responder: Not PASN frame "
+			" or Unexpected auth frame, auth_alg = %d",
+			auth_alg);
+		return -1;
+	}
+	if (auth_transaction == 1) {
+		if (p2p_pasn_handle_action_wrapper(p2p, dev, mgmt, len, freq,
+						   auth_transaction)) {
+			p2p_dbg(p2p, "P2P PASN Responder: Handle Auth1 action wrapper failed");
+			return -1;
+		}
+		if (handle_auth_pasn_1(pasn, p2p->cfg->dev_addr, mgmt->sa, mgmt,
+				       len) < 0) {
+			p2p_dbg(p2p, "P2P PASN Responder: Handle Auth1 failed");
+			return -1;
+		}
+	} else if (auth_transaction == 3) {
+		if (handle_auth_pasn_3(pasn, p2p->cfg->dev_addr, mgmt->sa, mgmt,
+				       len) < 0) {
+			p2p_dbg(p2p, "P2P PASN Responder: Handle PASN Auth3 failed");
+			return -1;
+		}
+		if (p2p_pasn_handle_action_wrapper(p2p, dev, mgmt, len, freq,
+						   auth_transaction)) {
+			p2p_dbg(p2p, "P2P PASN Responder: Handle Auth3 action wrapper failed");
+			memset(dev->info.device_identity_key, 0,
+			       sizeof(dev->info.device_identity_key));
+			memset(dev->info.password, 0,
+			       sizeof(dev->info.password));
+			dev->info.password_len = 0;
+			return -1;
+		}
+	}
+	return 0;
+}
+
+
+int p2p_pasn_auth_rx(struct p2p_data *p2p, const struct ieee80211_mgmt *mgmt,
+		     size_t len, int freq)
+{
+	int ret = 0;
+	u8 auth_transaction;
+	struct p2p_device *dev;
+	struct pasn_data *pasn;
+	struct wpa_pasn_params_data pasn_data;
+
+	dev = p2p_get_device(p2p, (u8 *)mgmt->sa);
+	if (!dev) {
+		p2p_dbg(p2p, "P2P PASN: Peer not found" MACSTR,
+			MAC2STR(mgmt->sa));
+		return -1;
+	}
+
+	if (!dev->pasn) {
+		p2p_dbg(p2p, "P2P PASN: uninitialized");
+		return -1;
+	}
+
+	pasn = dev->pasn;
+
+	if (pasn->frame) {
+		wpabuf_free(pasn->frame);
+		pasn->frame = NULL;
+	}
+
+	pasn_register_callbacks(pasn, p2p->cfg->cb_ctx,
+				p2p->cfg->pasn_send_mgmt, NULL);
+	auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
+
+	if (dev->role == P2P_ROLE_PAIRING_INITIATOR && auth_transaction == 2) {
+		if (p2p_pasn_handle_action_wrapper(p2p, dev, mgmt, len, freq,
+						   auth_transaction)) {
+			p2p_dbg(p2p, "P2P PASN Initiator: Handle Auth2 action wrapper failed");
+			return -1;
+		}
+		ret = wpa_pasn_auth_rx(pasn, (const u8 *)mgmt, len, &pasn_data);
+		forced_memzero(pasn_get_ptk(pasn), sizeof(pasn->ptk));
+
+		if (ret < 0) {
+			p2p_dbg(p2p, "P2P PASN: wpa_pasn_auth_rx failed");
+			dev->role = P2P_ROLE_IDLE;
+		}
+
+	} else {
+		ret = p2p_handle_pasn_auth(p2p, dev, mgmt, len, freq);
+	}
+	return ret;
+}
diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h
index bf73adb70..1403d0d20 100644
--- a/src/p2p/p2p.h
+++ b/src/p2p/p2p.h
@@ -460,6 +460,31 @@ struct p2p_peer_info {
 	 * p2p_pairing_config - P2P Pairing configuration
 	 */
 	struct p2p_pairing_config pairing_config;
+
+	/**
+	 * cipher version for Device Identity key generation
+	 */
+	u8 dik_cipher_version;
+
+	/**
+	 * Device Identity key which is unique for a device
+	 */
+	u8 device_identity_key[128];
+
+	/**
+	 * Device Identity key lifetime
+	 */
+	u32 dik_lifetime;
+
+	/**
+	 * password used during group formation post opportunistic pasn auth
+	 */
+	char password[100];
+
+	/**
+	 * password length. Non zero if valid
+	 */
+	u16 password_len;
 };
 
 enum p2p_prov_disc_status {
@@ -1255,6 +1280,23 @@ struct p2p_config {
 	 */
 	void (*bootstrap_completed)(void *ctx, const u8 *addr, int status,
 				    int freq);
+
+	/**
+	 * pasn_send_mgmt - Function handler to transmit a Management frame
+	 * @ctx: Callback context from cb_ctx
+	 * @data : Frame to transmit
+	 * @data_len: Length of frame to transmit
+	 * @noack : No ack flag
+	 * @freq: Frequency in MHz for the channel on which to transmit
+	 * @wait: How many milliseconds to wait for a response frame
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*pasn_send_mgmt)(void *ctx, const u8 *data, size_t data_len,
+			      int noack, unsigned int freq, unsigned int wait);
+
+	int (*pasn_update_extra_ies)(void *ctx, const u8 *peer_addr, bool dira);
+
+	int (*pasn_parse_encrypted_data)(void *ctx, const u8 *data, size_t len);
 };
 
 
@@ -2557,4 +2599,13 @@ int p2p_channel_to_freq(int op_class, int channel);
 struct wpabuf * p2p_usd_elems(struct p2p_data *p2p);
 void p2p_process_usd_elems(struct p2p_data *p2p, const u8 *ies, u16 ies_len,
 			   const u8 *peer_addr, unsigned int freq);
+int p2p_initiate_pasn_auth(struct p2p_data *p2p, const u8 *addr, int freq);
+int p2p_pasn_auth_rx(struct p2p_data *p2p, const struct ieee80211_mgmt *mgmt,
+		     size_t len, int freq);
+int p2p_pasn_update_extra_ies(struct p2p_data *p2p, const u8 *peer_addr,
+			      bool dira);
+int p2p_pasn_parse_encrypted_data(struct p2p_data *p2p, const u8 *data,
+				  size_t len);
+int p2p_pasn_auth_tx_status(struct p2p_data *p2p, const u8 *data,
+			    size_t data_len, u8 acked);
 #endif /* P2P_H */
diff --git a/src/p2p/p2p_build.c b/src/p2p/p2p_build.c
index fd48702fe..64e6b2aa3 100644
--- a/src/p2p/p2p_build.c
+++ b/src/p2p/p2p_build.c
@@ -1017,3 +1017,38 @@ int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id,
 
 	return 0;
 }
+
+
+struct wpabuf *p2p_encaps_p2p_vendor_ie(struct p2p_data *p2p,
+					struct wpabuf *subelems, u32 ie_type)
+{
+	struct wpabuf *ie;
+	const u8 *pos, *end;
+	size_t len;
+
+	if (!subelems)
+		return NULL;
+
+	len = wpabuf_len(subelems) + 1000;
+
+	ie = wpabuf_alloc(len);
+	if (!ie)
+		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, ie_type);
+		wpabuf_put_data(ie, pos, frag_len);
+		pos += frag_len;
+	}
+
+	return ie;
+}
diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c
index 04e5139d6..84f39d290 100644
--- a/src/p2p/p2p_go_neg.c
+++ b/src/p2p/p2p_go_neg.c
@@ -135,15 +135,14 @@ static const char * p2p_wps_method_str(enum p2p_wps_method wps_method)
 }
 
 
-static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p,
-					    struct p2p_device *peer)
+struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p,
+				     struct p2p_device *peer)
 {
-	struct wpabuf *buf;
-	u8 *len;
 	u8 group_capab;
 	size_t extra = 0;
 	u16 pw_id;
 	bool is_6ghz_capab;
+	struct wpabuf *buf, *buf2, *p2p_ie;
 
 #ifdef CONFIG_WIFI_DISPLAY
 	if (p2p->wfd_ie_go_neg)
@@ -153,13 +152,16 @@ static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p,
 	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_REQ])
 		extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_REQ]);
 
-	buf = wpabuf_alloc(1000 + extra);
-	if (buf == NULL)
+	buf2 = wpabuf_alloc(1000 + extra);
+	if (!buf2)
 		return NULL;
 
-	p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_REQ, peer->dialog_token);
+	p2p_buf_add_public_action_hdr(buf2, P2P_GO_NEG_REQ, peer->dialog_token);
+
+	p2p_ie = wpabuf_alloc(500);
+	if (!p2p_ie)
+		return NULL;
 
-	len = p2p_buf_add_ie_hdr(buf);
 	group_capab = 0;
 	if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) {
 		group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
@@ -170,17 +172,17 @@ static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p,
 		group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
 	if (p2p->cfg->p2p_intra_bss)
 		group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
-	p2p_buf_add_capability(buf, p2p->dev_capab &
+	p2p_buf_add_capability(p2p_ie, p2p->dev_capab &
 			       ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY,
 			       group_capab);
-	p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | peer->tie_breaker);
-	p2p_buf_add_config_timeout(buf, p2p->go_timeout, p2p->client_timeout);
-	p2p_buf_add_listen_channel(buf, p2p->cfg->country, p2p->cfg->reg_class,
+	p2p_buf_add_go_intent(p2p_ie, (p2p->go_intent << 1) | peer->tie_breaker);
+	p2p_buf_add_config_timeout(p2p_ie, p2p->go_timeout, p2p->client_timeout);
+	p2p_buf_add_listen_channel(p2p_ie, p2p->cfg->country, p2p->cfg->reg_class,
 				   p2p->cfg->channel);
 	if (p2p->ext_listen_interval)
-		p2p_buf_add_ext_listen_timing(buf, p2p->ext_listen_period,
+		p2p_buf_add_ext_listen_timing(p2p_ie, p2p->ext_listen_period,
 					      p2p->ext_listen_interval);
-	p2p_buf_add_intended_addr(buf, p2p->intended_addr);
+	p2p_buf_add_intended_addr(p2p_ie, p2p->intended_addr);
 	is_6ghz_capab = is_p2p_6ghz_capable(p2p) &&
 		p2p_is_peer_6ghz_capab(p2p, peer->info.p2p_device_addr);
 	if (p2p->num_pref_freq) {
@@ -191,37 +193,41 @@ static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p,
 					p2p->num_pref_freq, &pref_chanlist, go);
 		p2p_channels_dump(p2p, "channel list after filtering",
 				  &pref_chanlist);
-		p2p_buf_add_channel_list(buf, p2p->cfg->country,
+		p2p_buf_add_channel_list(p2p_ie, p2p->cfg->country,
 					 &pref_chanlist, is_6ghz_capab);
 	} else {
-		p2p_buf_add_channel_list(buf, p2p->cfg->country,
+		p2p_buf_add_channel_list(p2p_ie, p2p->cfg->country,
 					 &p2p->channels, is_6ghz_capab);
 	}
-	p2p_buf_add_device_info(buf, p2p, peer);
-	p2p_buf_add_operating_channel(buf, p2p->cfg->country,
+	p2p_buf_add_device_info(p2p_ie, p2p, peer);
+	p2p_buf_add_operating_channel(p2p_ie, p2p->cfg->country,
 				      p2p->op_reg_class, p2p->op_channel);
-	p2p_buf_update_ie_hdr(buf, len);
 
-	p2p_buf_add_pref_channel_list(buf, p2p->pref_freq_list,
+	buf = p2p_encaps_p2p_vendor_ie(p2p, p2p_ie, P2P_IE_VENDOR_TYPE);
+	wpabuf_free(p2p_ie);
+
+	p2p_buf_add_pref_channel_list(buf2, p2p->pref_freq_list,
 				      p2p->num_pref_freq);
 
 	/* WPS IE with Device Password ID attribute */
 	pw_id = p2p_wps_method_pw_id(peer->wps_method);
 	if (peer->oob_pw_id)
 		pw_id = peer->oob_pw_id;
-	if (p2p_build_wps_ie(p2p, buf, pw_id, 0) < 0) {
+	if (peer && !peer->p2p2 && p2p_build_wps_ie(p2p, buf2, pw_id, 0) < 0) {
 		p2p_dbg(p2p, "Failed to build WPS IE for GO Negotiation Request");
+		wpabuf_free(buf2);
 		wpabuf_free(buf);
 		return NULL;
 	}
 
 #ifdef CONFIG_WIFI_DISPLAY
 	if (p2p->wfd_ie_go_neg)
-		wpabuf_put_buf(buf, p2p->wfd_ie_go_neg);
+		wpabuf_put_buf(buf2, p2p->wfd_ie_go_neg);
 #endif /* CONFIG_WIFI_DISPLAY */
 
 	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_REQ])
-		wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_REQ]);
+		wpabuf_put_buf(buf2, p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_REQ]);
+	buf = wpabuf_concat(buf2, buf);
 
 	return buf;
 }
@@ -292,13 +298,12 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p,
 					     u8 dialog_token, u8 status,
 					     u8 tie_breaker)
 {
-	struct wpabuf *buf;
-	u8 *len;
 	u8 group_capab;
 	size_t extra = 0;
 	u16 pw_id;
 	bool is_6ghz_capab;
 	struct p2p_channels pref_chanlist;
+	struct wpabuf *buf, *buf2, *p2p_ie;
 
 	p2p_dbg(p2p, "Building GO Negotiation Response");
 
@@ -310,14 +315,17 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p,
 	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_RESP])
 		extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_RESP]);
 
-	buf = wpabuf_alloc(1000 + extra);
-	if (buf == NULL)
+	buf2 = wpabuf_alloc(1000 + extra);
+	if (!buf2)
 		return NULL;
 
-	p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_RESP, dialog_token);
+	p2p_buf_add_public_action_hdr(buf2, P2P_GO_NEG_RESP, dialog_token);
 
-	len = p2p_buf_add_ie_hdr(buf);
-	p2p_buf_add_status(buf, status);
+	p2p_ie = wpabuf_alloc(500);
+	if (!p2p_ie)
+		return NULL;
+
+	p2p_buf_add_status(p2p_ie, status);
 	group_capab = 0;
 	if (peer && peer->go_state == LOCAL_GO) {
 		if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) {
@@ -331,24 +339,25 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p,
 		if (p2p->cfg->p2p_intra_bss)
 			group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
 	}
-	p2p_buf_add_capability(buf, p2p->dev_capab &
+	p2p_buf_add_capability(p2p_ie, p2p->dev_capab &
 			       ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY,
 			       group_capab);
-	p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | tie_breaker);
-	p2p_buf_add_config_timeout(buf, p2p->go_timeout, p2p->client_timeout);
+	p2p_buf_add_go_intent(p2p_ie, (p2p->go_intent << 1) | tie_breaker);
+	p2p_buf_add_config_timeout(p2p_ie, p2p->go_timeout, p2p->client_timeout);
 	if (p2p->override_pref_op_class) {
 		p2p_dbg(p2p, "Override operating channel preference");
-		p2p_buf_add_operating_channel(buf, p2p->cfg->country,
+		p2p_buf_add_operating_channel(p2p_ie, p2p->cfg->country,
 					      p2p->override_pref_op_class,
 					      p2p->override_pref_channel);
 	} else if (peer && peer->go_state == REMOTE_GO && !p2p->num_pref_freq) {
 		p2p_dbg(p2p, "Omit Operating Channel attribute");
 	} else {
-		p2p_buf_add_operating_channel(buf, p2p->cfg->country,
+		p2p_buf_add_operating_channel(p2p_ie, p2p->cfg->country,
 					      p2p->op_reg_class,
 					      p2p->op_channel);
 	}
-	p2p_buf_add_intended_addr(buf, p2p->intended_addr);
+	p2p_buf_add_intended_addr(p2p_ie, p2p->intended_addr);
+
 	if (p2p->num_pref_freq) {
 		bool go = (peer && peer->go_state == LOCAL_GO) ||
 			p2p->go_intent == 15;
@@ -362,12 +371,12 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p,
 				  p2p->allow_6ghz);
 	}
 	if (status || peer == NULL) {
-		p2p_buf_add_channel_list(buf, p2p->cfg->country,
+		p2p_buf_add_channel_list(p2p_ie, p2p->cfg->country,
 					 &pref_chanlist, false);
 	} else if (peer->go_state == REMOTE_GO) {
 		is_6ghz_capab = is_p2p_6ghz_capable(p2p) &&
 			p2p_is_peer_6ghz_capab(p2p, peer->info.p2p_device_addr);
-		p2p_buf_add_channel_list(buf, p2p->cfg->country,
+		p2p_buf_add_channel_list(p2p_ie, p2p->cfg->country,
 					 &pref_chanlist, is_6ghz_capab);
 	} else {
 		struct p2p_channels res;
@@ -376,33 +385,37 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p,
 			p2p_is_peer_6ghz_capab(p2p, peer->info.p2p_device_addr);
 		p2p_channels_intersect(&pref_chanlist, &peer->channels,
 				       &res);
-		p2p_buf_add_channel_list(buf, p2p->cfg->country, &res,
-				       is_6ghz_capab);
+		p2p_buf_add_channel_list(p2p_ie, p2p->cfg->country, &res,
+					 is_6ghz_capab);
 	}
-	p2p_buf_add_device_info(buf, p2p, peer);
+	p2p_buf_add_device_info(p2p_ie, p2p, peer);
 	if (peer && peer->go_state == LOCAL_GO) {
-		p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, p2p->ssid,
+		p2p_buf_add_group_id(p2p_ie, p2p->cfg->dev_addr, p2p->ssid,
 				     p2p->ssid_len);
 	}
-	p2p_buf_update_ie_hdr(buf, len);
+
+	buf = p2p_encaps_p2p_vendor_ie(p2p, p2p_ie, P2P_IE_VENDOR_TYPE);
+	wpabuf_free(p2p_ie);
 
 	/* WPS IE with Device Password ID attribute */
 	pw_id = p2p_wps_method_pw_id(peer ? peer->wps_method : WPS_NOT_READY);
 	if (peer && peer->oob_pw_id)
 		pw_id = peer->oob_pw_id;
-	if (p2p_build_wps_ie(p2p, buf, pw_id, 0) < 0) {
+	if (peer && !peer->p2p2 && p2p_build_wps_ie(p2p, buf2, pw_id, 0) < 0) {
 		p2p_dbg(p2p, "Failed to build WPS IE for GO Negotiation Response");
+		wpabuf_free(buf2);
 		wpabuf_free(buf);
 		return NULL;
 	}
 
 #ifdef CONFIG_WIFI_DISPLAY
 	if (p2p->wfd_ie_go_neg)
-		wpabuf_put_buf(buf, p2p->wfd_ie_go_neg);
+		wpabuf_put_buf(buf2, p2p->wfd_ie_go_neg);
 #endif /* CONFIG_WIFI_DISPLAY */
 
 	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_RESP])
-		wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_RESP]);
+		wpabuf_put_buf(buf2, p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_RESP]);
+	buf = wpabuf_concat(buf2, buf);
 
 	return buf;
 }
@@ -801,21 +814,19 @@ void p2p_check_pref_chan(struct p2p_data *p2p, int go,
 }
 
 
-void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
-			    const u8 *data, size_t len, int rx_freq)
+int p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
+			   const u8 *data, size_t len, int rx_freq, bool p2p2)
 {
 	struct p2p_device *dev = NULL;
-	struct wpabuf *resp;
 	struct p2p_message msg;
 	u8 status = P2P_SC_FAIL_INVALID_PARAMS;
 	int tie_breaker = 0;
-	int freq;
 
 	p2p_dbg(p2p, "Received GO Negotiation Request from " MACSTR "(freq=%d)",
 		MAC2STR(sa), rx_freq);
 
 	if (p2p_parse(data, len, &msg))
-		return;
+		return -1;
 
 	if (!msg.capability) {
 		p2p_dbg(p2p, "Mandatory Capability attribute missing from GO Negotiation Request");
@@ -890,7 +901,7 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
 			p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
 			p2p_go_neg_failed(p2p, *msg.status);
 			p2p_parse_free(&msg);
-			return;
+			return -1;
 		}
 		goto fail;
 	}
@@ -922,7 +933,7 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
 		p2p_dbg(p2p, "User has rejected this peer");
 		status = P2P_SC_FAIL_REJECTED_BY_USER;
 	} else if (dev == NULL ||
-		   (dev->wps_method == WPS_NOT_READY &&
+		   (dev->wps_method == WPS_NOT_READY && !p2p2 &&
 		    (p2p->authorized_oob_dev_pw_id == 0 ||
 		     p2p->authorized_oob_dev_pw_id !=
 		     msg.dev_password_id))) {
@@ -968,7 +979,7 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
 		    os_memcmp(sa, p2p->cfg->dev_addr, ETH_ALEN) > 0) {
 			p2p_dbg(p2p, "Do not reply since peer has higher address and GO Neg Request already sent");
 			p2p_parse_free(&msg);
-			return;
+			return -1;
 		}
 
 		if (dev->go_neg_req_sent &&
@@ -976,7 +987,7 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
 			p2p_dbg(p2p,
 				"Do not reply since peer is waiting for us to start a new GO Negotiation and GO Neg Request already sent");
 			p2p_parse_free(&msg);
-			return;
+			return -1;
 		}
 
 		go = p2p_go_det(p2p->go_intent, *msg.go_intent);
@@ -993,6 +1004,9 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
 			goto fail;
 		}
 
+		if (p2p2)
+			goto skip;
+
 		switch (msg.dev_password_id) {
 		case DEV_PW_REGISTRAR_SPECIFIED:
 			p2p_dbg(p2p, "PIN from peer Display");
@@ -1059,7 +1073,7 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
 			status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
 			goto fail;
 		}
-
+skip:
 		if (go && p2p_go_select_channel(p2p, dev, &status) < 0)
 			goto fail;
 
@@ -1086,7 +1100,10 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
 		p2p_set_state(p2p, P2P_GO_NEG);
 		p2p_clear_timeout(p2p);
 		dev->dialog_token = msg.dialog_token;
-		os_memcpy(dev->intended_addr, msg.intended_addr, ETH_ALEN);
+		if (!is_zero_ether_addr(msg.intended_addr)) {
+			p2p_dbg(p2p, "msg.intended_addr" MACSTR, MAC2STR(msg.intended_addr));
+			os_memcpy(dev->intended_addr, msg.intended_addr, ETH_ALEN);
+		}
 		p2p->go_neg_peer = dev;
 		eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL);
 		status = P2P_SC_SUCCESS;
@@ -1095,22 +1112,13 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
 fail:
 	if (dev)
 		dev->status = status;
-	resp = p2p_build_go_neg_resp(p2p, dev, msg.dialog_token, status,
-				     !tie_breaker);
+	p2p->go_neg_resp = p2p_build_go_neg_resp(p2p, dev, msg.dialog_token,
+						 status, !tie_breaker);
+
 	p2p_parse_free(&msg);
-	if (resp == NULL)
-		return;
-	p2p_dbg(p2p, "Sending GO Negotiation Response");
-	if (rx_freq > 0)
-		freq = rx_freq;
-	else
-		freq = p2p_channel_to_freq(p2p->cfg->reg_class,
-					   p2p->cfg->channel);
-	if (freq < 0) {
-		p2p_dbg(p2p, "Unknown regulatory class/channel");
-		wpabuf_free(resp);
-		return;
-	}
+	if (!p2p->go_neg_resp)
+		return -1;
+
 	if (status == P2P_SC_SUCCESS) {
 		p2p->pending_action_state = P2P_PENDING_GO_NEG_RESPONSE;
 		dev->flags |= P2P_DEV_WAIT_GO_NEG_CONFIRM;
@@ -1128,13 +1136,38 @@ fail:
 	} else
 		p2p->pending_action_state =
 			P2P_PENDING_GO_NEG_RESPONSE_FAILURE;
-	if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr,
-			    p2p->cfg->dev_addr,
-			    wpabuf_head(resp), wpabuf_len(resp), 100) < 0) {
-		p2p_dbg(p2p, "Failed to send Action frame");
+	return 0;
+}
+
+void p2p_handle_go_neg_req(struct p2p_data *p2p, const u8 *sa, const u8 *data,
+			   size_t len, int rx_freq)
+{
+	int freq;
+
+	if (p2p_process_go_neg_req(p2p, sa, data, len, rx_freq, false))
+		return;
+
+	p2p_dbg(p2p, "Sending GO Negotiation Response");
+
+	if (rx_freq > 0)
+		freq = rx_freq;
+	else
+		freq = p2p_channel_to_freq(p2p->cfg->reg_class,
+					   p2p->cfg->channel);
+	if (freq < 0) {
+		p2p_dbg(p2p, "Unknown regulatory class/channel");
+		return;
 	}
 
-	wpabuf_free(resp);
+	if (p2p->go_neg_resp &&
+	    p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr,
+			    p2p->cfg->dev_addr, wpabuf_head(p2p->go_neg_resp),
+			    wpabuf_len(p2p->go_neg_resp), 100) < 0) {
+		p2p_dbg(p2p, "Failed to send Action frame");
+	}
+	wpabuf_free(p2p->go_neg_resp);
+	p2p->go_neg_resp = NULL;
+	return;
 }
 
 
@@ -1143,12 +1176,11 @@ static struct wpabuf * p2p_build_go_neg_conf(struct p2p_data *p2p,
 					     u8 dialog_token, u8 status,
 					     const u8 *resp_chan, int go)
 {
-	struct wpabuf *buf;
-	u8 *len;
 	struct p2p_channels res;
 	u8 group_capab;
 	size_t extra = 0;
 	bool is_6ghz_capab;
+	struct wpabuf *buf, *buf2, *p2p_ie;
 
 	p2p_dbg(p2p, "Building GO Negotiation Confirm");
 
@@ -1160,14 +1192,17 @@ static struct wpabuf * p2p_build_go_neg_conf(struct p2p_data *p2p,
 	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_CONF])
 		extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_CONF]);
 
-	buf = wpabuf_alloc(1000 + extra);
-	if (buf == NULL)
+	buf2 = wpabuf_alloc(1000 + extra);
+	if (!buf2)
 		return NULL;
 
-	p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_CONF, dialog_token);
+	p2p_buf_add_public_action_hdr(buf2, P2P_GO_NEG_CONF, dialog_token);
 
-	len = p2p_buf_add_ie_hdr(buf);
-	p2p_buf_add_status(buf, status);
+	p2p_ie = wpabuf_alloc(500);
+	if (!p2p_ie)
+		return NULL;
+
+	p2p_buf_add_status(p2p_ie, status);
 	group_capab = 0;
 	if (peer->go_state == LOCAL_GO) {
 		if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) {
@@ -1181,40 +1216,44 @@ static struct wpabuf * p2p_build_go_neg_conf(struct p2p_data *p2p,
 		if (p2p->cfg->p2p_intra_bss)
 			group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
 	}
-	p2p_buf_add_capability(buf, p2p->dev_capab &
+	p2p_buf_add_capability(p2p_ie, p2p->dev_capab &
 			       ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY,
 			       group_capab);
 	if (go || resp_chan == NULL)
-		p2p_buf_add_operating_channel(buf, p2p->cfg->country,
+		p2p_buf_add_operating_channel(p2p_ie, p2p->cfg->country,
 					      p2p->op_reg_class,
 					      p2p->op_channel);
 	else
-		p2p_buf_add_operating_channel(buf, (const char *) resp_chan,
+		p2p_buf_add_operating_channel(p2p_ie, (const char *) resp_chan,
 					      resp_chan[3], resp_chan[4]);
 	p2p_channels_intersect(&p2p->channels, &peer->channels, &res);
 	is_6ghz_capab = is_p2p_6ghz_capable(p2p) &&
 		p2p_is_peer_6ghz_capab(p2p, peer->info.p2p_device_addr);
-	p2p_buf_add_channel_list(buf, p2p->cfg->country, &res, is_6ghz_capab);
+	p2p_buf_add_channel_list(p2p_ie, p2p->cfg->country, &res, is_6ghz_capab);
 	if (go) {
-		p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, p2p->ssid,
+		p2p_buf_add_group_id(p2p_ie, p2p->cfg->dev_addr, p2p->ssid,
 				     p2p->ssid_len);
 	}
-	p2p_buf_update_ie_hdr(buf, len);
+
+	buf = p2p_encaps_p2p_vendor_ie(p2p, p2p_ie, P2P_IE_VENDOR_TYPE);
+	wpabuf_free(p2p_ie);
 
 #ifdef CONFIG_WIFI_DISPLAY
 	if (p2p->wfd_ie_go_neg)
-		wpabuf_put_buf(buf, p2p->wfd_ie_go_neg);
+		wpabuf_put_buf(buf2, p2p->wfd_ie_go_neg);
 #endif /* CONFIG_WIFI_DISPLAY */
 
 	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_CONF])
-		wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_CONF]);
+		wpabuf_put_buf(buf2, p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_CONF]);
+
+	buf = wpabuf_concat(buf2, buf);
 
 	return buf;
 }
 
 
-void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa,
-			     const u8 *data, size_t len, int rx_freq)
+int p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data,
+			    size_t len, int rx_freq, bool p2p2)
 {
 	struct p2p_device *dev;
 	int go = -1;
@@ -1225,20 +1264,20 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa,
 	p2p_dbg(p2p, "Received GO Negotiation Response from " MACSTR
 		" (freq=%d)", MAC2STR(sa), rx_freq);
 	dev = p2p_get_device(p2p, sa);
-	if (dev == NULL || dev->wps_method == WPS_NOT_READY ||
+	if (dev == NULL || (!p2p2 && dev->wps_method == WPS_NOT_READY) ||
 	    dev != p2p->go_neg_peer) {
 		p2p_dbg(p2p, "Not ready for GO negotiation with " MACSTR,
 			MAC2STR(sa));
-		return;
+		return -1;
 	}
 
 	if (p2p_parse(data, len, &msg))
-		return;
+		return -1;
 
 	if (!(dev->flags & P2P_DEV_WAIT_GO_NEG_RESPONSE)) {
 		p2p_dbg(p2p, "Was not expecting GO Negotiation Response - ignore");
 		p2p_parse_free(&msg);
-		return;
+		return -1;
 	}
 	dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE;
 	p2p_update_peer_6ghz_capab(dev, &msg);
@@ -1247,7 +1286,7 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa,
 		p2p_dbg(p2p, "Unexpected Dialog Token %u (expected %u)",
 			msg.dialog_token, dev->dialog_token);
 		p2p_parse_free(&msg);
-		return;
+		return -1;
 	}
 
 	if (!msg.status) {
@@ -1276,7 +1315,7 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa,
 		}
 		p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
 		p2p_parse_free(&msg);
-		return;
+		return -1;
 	}
 
 	if (!msg.capability) {
@@ -1377,6 +1416,9 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa,
 	} else
 		dev->oper_freq = 0;
 
+	if (p2p2)
+		goto skip;
+
 	switch (msg.dev_password_id) {
 	case DEV_PW_REGISTRAR_SPECIFIED:
 		p2p_dbg(p2p, "PIN from peer Display");
@@ -1432,6 +1474,7 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa,
 		goto fail;
 	}
 
+skip:
 	if (go && p2p_go_select_channel(p2p, dev, &status) < 0)
 		goto fail;
 
@@ -1446,7 +1489,10 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa,
 	p2p_clear_timeout(p2p);
 
 	p2p_dbg(p2p, "GO Negotiation with " MACSTR, MAC2STR(sa));
-	os_memcpy(dev->intended_addr, msg.intended_addr, ETH_ALEN);
+	if (!is_zero_ether_addr(msg.intended_addr)) {
+		p2p_dbg(p2p, "msg.intended_addr" MACSTR, MAC2STR(msg.intended_addr));
+		os_memcpy(dev->intended_addr, msg.intended_addr, ETH_ALEN);
+	}
 
 fail:
 	/* Store GO Negotiation Confirmation to allow retransmission */
@@ -1454,15 +1500,15 @@ fail:
 	dev->go_neg_conf = p2p_build_go_neg_conf(p2p, dev, msg.dialog_token,
 						 status, msg.operating_channel,
 						 go);
+	p2p->go_neg_conf = wpabuf_dup(dev->go_neg_conf);
 	p2p_parse_free(&msg);
-	if (dev->go_neg_conf == NULL)
-		return;
-	p2p_dbg(p2p, "Sending GO Negotiation Confirm");
+
 	if (status == P2P_SC_SUCCESS) {
 		p2p->pending_action_state = P2P_PENDING_GO_NEG_CONFIRM;
 		dev->go_state = go ? LOCAL_GO : REMOTE_GO;
 	} else
 		p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+
 	if (rx_freq > 0)
 		freq = rx_freq;
 	else
@@ -1471,6 +1517,36 @@ fail:
 	dev->go_neg_conf_freq = freq;
 	dev->go_neg_conf_sent = 0;
 
+	if (status != P2P_SC_SUCCESS) {
+		p2p_dbg(p2p, "GO Negotiation failed");
+		p2p_go_neg_failed(p2p, status);
+	}
+	return 0;
+}
+
+void p2p_handle_go_neg_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data,
+			    size_t len, int rx_freq)
+{
+	int freq;
+	struct p2p_device *dev;
+
+	dev = p2p_get_device(p2p, sa);
+	if (dev == NULL || dev->wps_method == WPS_NOT_READY ||
+	    dev != p2p->go_neg_peer) {
+		p2p_dbg(p2p, "Not ready for GO negotiation with " MACSTR,
+			MAC2STR(sa));
+		return;
+	}
+
+	if (p2p_process_go_neg_resp(p2p, sa, data, len, rx_freq, false))
+		return;
+
+	p2p_dbg(p2p, "Sending GO Negotiation Confirm");
+	if (rx_freq > 0)
+		freq = rx_freq;
+	else
+		freq = dev->listen_freq;
+
 	if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, sa,
 			    wpabuf_head(dev->go_neg_conf),
 			    wpabuf_len(dev->go_neg_conf), 50) < 0) {
@@ -1479,15 +1555,16 @@ fail:
 		p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
 	} else
 		dev->go_neg_conf_sent++;
-	if (status != P2P_SC_SUCCESS) {
-		p2p_dbg(p2p, "GO Negotiation failed");
-		p2p_go_neg_failed(p2p, status);
-	}
+
+
+	wpabuf_free(p2p->go_neg_conf);
+	p2p->go_neg_conf = NULL;
+	return;
 }
 
 
-void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa,
-			     const u8 *data, size_t len)
+void p2p_handle_go_neg_conf(struct p2p_data *p2p, const u8 *sa,
+			    const u8 *data, size_t len, bool p2p2)
 {
 	struct p2p_device *dev;
 	struct p2p_message msg;
@@ -1495,7 +1572,7 @@ void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa,
 	p2p_dbg(p2p, "Received GO Negotiation Confirm from " MACSTR,
 		MAC2STR(sa));
 	dev = p2p_get_device(p2p, sa);
-	if (dev == NULL || dev->wps_method == WPS_NOT_READY ||
+	if (dev == NULL || (!p2p2 && dev->wps_method == WPS_NOT_READY) ||
 	    dev != p2p->go_neg_peer) {
 		p2p_dbg(p2p, "Not ready for GO negotiation with " MACSTR,
 			MAC2STR(sa));
diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h
index 5463f4ba2..ed893d438 100644
--- a/src/p2p/p2p_i.h
+++ b/src/p2p/p2p_i.h
@@ -42,6 +42,13 @@ enum p2p_go_state {
 };
 
 
+/* Enumeration for P2P device current role */
+enum p2p_role {
+	P2P_ROLE_IDLE = 0,
+	P2P_ROLE_PAIRING_INITIATOR,
+	P2P_ROLE_PAIRING_RESPONDER,
+};
+
 /**
  * struct bootstrap_params - P2P Device bootstrap request params
  */
@@ -193,6 +200,22 @@ struct p2p_device {
 	 * password length. Non zero if valid
 	 */
 	u16 password_len;
+
+	/* pasn data structure */
+	struct pasn_data *pasn;
+
+	/* device role */
+	enum p2p_role role;
+
+	/**
+	 * password used during group formation post opportunistic pasn auth
+	 */
+	char dev_password[100];
+
+	/**
+	 * password length. Non zero if valid
+	 */
+	u16 dev_password_len;
 };
 
 struct p2p_sd_query {
@@ -637,6 +660,29 @@ struct p2p_data {
 	bool allow_6ghz;
 
 	struct p2p_pairing_info *pairing_info;
+	/*p2p pairing initiator pmksa cache list */
+	struct rsn_pmksa_cache *initiator_pmksa;
+	/* p2p pairing responder pmksa cache list */
+	struct rsn_pmksa_cache *responder_pmksa;
+	/**
+	 * go_neg_resp - GO Negotiation Response frame
+	 */
+	struct wpabuf *go_neg_resp;
+
+	/**
+	 * go_neg_conf - GO Negotiation Confirmation frame
+	 */
+	struct wpabuf *go_neg_conf;
+
+	/**
+	 * invitation_req - Invitation request frame
+	 */
+	struct wpabuf *invitation_req;
+
+	/**
+	 * invitation_resp - Invitation Response frame
+	 */
+	struct wpabuf *invitation_resp;
 };
 
 /**
@@ -749,6 +795,12 @@ struct p2p_message {
 
 	const u8 *pbma_info;
 	size_t pbma_info_len;
+
+	const u8 *action_frame_wrapper;
+	size_t action_frame_wrapper_len;
+
+	const u8 *dira;
+	size_t dira_len;
 };
 
 
@@ -889,6 +941,8 @@ int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id,
 void p2p_buf_add_pref_channel_list(struct wpabuf *buf,
 				   const struct weighted_pcl *pref_freq_list,
 				   unsigned int size);
+struct wpabuf *p2p_encaps_p2p_vendor_ie(struct p2p_data *p2p,
+					struct wpabuf *subelems, u32 ie_type);
 
 /* p2p_sd.c */
 struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p,
@@ -905,15 +959,21 @@ void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa,
 int p2p_start_sd(struct p2p_data *p2p, struct p2p_device *dev);
 
 /* p2p_go_neg.c */
+struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p,
+				     struct p2p_device *peer);
 int p2p_peer_channels_check(struct p2p_data *p2p, struct p2p_channels *own,
 			    struct p2p_device *dev,
 			    const u8 *channel_list, size_t channel_list_len);
-void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
-			    const u8 *data, size_t len, int rx_freq);
-void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa,
-			     const u8 *data, size_t len, int rx_freq);
-void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa,
-			     const u8 *data, size_t len);
+void p2p_handle_go_neg_req(struct p2p_data *p2p, const u8 *sa, const u8 *data,
+			   size_t len, int rx_freq);
+void p2p_handle_go_neg_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data,
+			    size_t len, int rx_freq);
+void p2p_handle_go_neg_conf(struct p2p_data *p2p, const u8 *sa, const u8 *data,
+			    size_t len, bool p2p2);
+int p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, const u8 *data,
+			   size_t len, int rx_freq, bool p2p2);
+int p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data,
+			    size_t len, int rx_freq, bool p2p2);
 int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev);
 u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method);
 void p2p_reselect_channel(struct p2p_data *p2p,
@@ -934,10 +994,14 @@ void p2p_process_pcea(struct p2p_data *p2p, struct p2p_message *msg,
 		      struct p2p_device *dev);
 
 /* p2p_invitation.c */
-void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa,
-				const u8 *data, size_t len, int rx_freq);
+void p2p_handle_invitation_req(struct p2p_data *p2p, const u8 *sa,
+			       const u8 *data, size_t len, int rx_freq);
+void p2p_handle_invitation_resp(struct p2p_data *p2p, const u8 *sa,
+				const u8 *data, size_t len);
+int p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa,
+			       const u8 *data, size_t len, int rx_freq);
 void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa,
-				 const u8 *data, size_t len);
+				const u8 *data, size_t len);
 int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev,
 		    const u8 *go_dev_addr, int dev_pw_id);
 void p2p_invitation_req_cb(struct p2p_data *p2p, int success);
@@ -999,6 +1063,8 @@ void p2p_pref_channel_filter(const struct p2p_channels *a,
 			     struct p2p_channels *res, bool go);
 
 void p2p_sd_query_cb(struct p2p_data *p2p, int success);
+void p2p_pasn_initialize(struct p2p_data *p2p, struct p2p_device *dev,
+			 const u8 *addr, int freq);
 
 void p2p_dbg(struct p2p_data *p2p, const char *fmt, ...)
 PRINTF_FORMAT(2, 3);
diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c
index 70a7f6fa6..8ade838a8 100644
--- a/src/p2p/p2p_invitation.c
+++ b/src/p2p/p2p_invitation.c
@@ -181,14 +181,12 @@ static struct wpabuf * p2p_build_invitation_resp(struct p2p_data *p2p,
 }
 
 
-void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa,
-				const u8 *data, size_t len, int rx_freq)
+int p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa,
+			       const u8 *data, size_t len, int rx_freq)
 {
 	struct p2p_device *dev;
 	struct p2p_message msg;
-	struct wpabuf *resp = NULL;
 	u8 status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
-	int freq;
 	int go = 0;
 	u8 group_bssid[ETH_ALEN], *bssid;
 	int op_freq = 0;
@@ -202,7 +200,7 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa,
 		MAC2STR(sa), rx_freq);
 
 	if (p2p_parse(data, len, &msg))
-		return;
+		return -1;
 
 	dev = p2p_get_device(p2p, sa);
 	if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
@@ -385,21 +383,11 @@ fail:
 		bssid = group_bssid;
 	else
 		bssid = NULL;
-	resp = p2p_build_invitation_resp(p2p, dev, msg.dialog_token, status,
-					 bssid, reg_class, channel, channels);
-
-	if (resp == NULL)
-		goto out;
-
-	if (rx_freq > 0)
-		freq = rx_freq;
-	else
-		freq = p2p_channel_to_freq(p2p->cfg->reg_class,
-					   p2p->cfg->channel);
-	if (freq < 0) {
-		p2p_dbg(p2p, "Unknown regulatory class/channel");
-		goto out;
-	}
+	p2p->invitation_resp = p2p_build_invitation_resp(p2p, dev,
+							 msg.dialog_token,
+							 status, bssid,
+							 reg_class, channel,
+							 channels);
 
 	/*
 	 * Store copy of invitation data to be used when processing TX status
@@ -424,17 +412,38 @@ fail:
 	}
 	p2p->inv_status = status;
 	p2p->inv_op_freq = op_freq;
+	p2p_parse_free(&msg);
+	return 0;
+}
+
+
+void p2p_handle_invitation_req(struct p2p_data *p2p, const u8 *sa,
+			       const u8 *data, size_t len, int rx_freq)
+{
+	int freq;
+
+	if (p2p_process_invitation_req(p2p, sa, data, len, rx_freq))
+		return;
+
+	if (rx_freq > 0)
+		freq = rx_freq;
+	else
+		freq = p2p_channel_to_freq(p2p->cfg->reg_class,
+					   p2p->cfg->channel);
+	if (freq < 0)
+		p2p_dbg(p2p, "Unknown regulatory class/channel");
 
 	p2p->pending_action_state = P2P_PENDING_INVITATION_RESPONSE;
-	if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr,
+	if (p2p->invitation_resp &&
+	    p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr,
 			    p2p->cfg->dev_addr,
-			    wpabuf_head(resp), wpabuf_len(resp), 50) < 0) {
+			    wpabuf_head(p2p->invitation_resp),
+			    wpabuf_len(p2p->invitation_resp), 50) < 0)
 		p2p_dbg(p2p, "Failed to send Action frame");
-	}
 
-out:
-	wpabuf_free(resp);
-	p2p_parse_free(&msg);
+	wpabuf_free(p2p->invitation_resp);
+	p2p->invitation_resp = NULL;
+	return;
 }
 
 
diff --git a/src/p2p/p2p_parse.c b/src/p2p/p2p_parse.c
index a70e18079..de2a43fef 100644
--- a/src/p2p/p2p_parse.c
+++ b/src/p2p/p2p_parse.c
@@ -437,6 +437,26 @@ static int p2p_parse_attribute(u8 id, const u8 *data, u16 len,
 		msg->pbma_info_len = len;
 		wpa_printf(MSG_DEBUG, "P2P: * PBMA (length=%u)", len);
 		break;
+	case P2P_ATTR_ACTION_FRAME_WRAPPER:
+		if (len < 2) {
+			wpa_printf(MSG_DEBUG, "P2P: Too short Action frame (length %d)",
+				   len);
+			return -1;
+		}
+		msg->action_frame_wrapper = data;
+		msg->action_frame_wrapper_len = len;
+		wpa_printf(MSG_DEBUG, "P2P: * Action frame wrapper (length=%u)", len);
+		break;
+	case P2P_ATTR_DEVICE_IDENTITY_RESOLUTION:
+		if (len < 2) {
+			wpa_printf(MSG_DEBUG, "P2P: Too short DIRA (length %d)",
+				   len);
+			return -1;
+		}
+		msg->dira = data;
+		msg->dira_len = len;
+		wpa_printf(MSG_DEBUG, "P2P: * DIRA (length=%u)", len);
+		break;
 	default:
 		wpa_printf(MSG_DEBUG, "P2P: Skipped unknown attribute %d "
 			   "(length %d)", id, len);
diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c
index 0d2903712..dc54be0a2 100644
--- a/src/p2p/p2p_pd.c
+++ b/src/p2p/p2p_pd.c
@@ -815,6 +815,10 @@ static void p2p_process_prov_disc_bootstrap_req(struct p2p_data *p2p,
 
 	wpa_printf(MSG_ERROR, "Bootstrap received %d", bootstrap);
 
+	if (status == P2P_SC_SUCCESS) {
+		dev->role = P2P_ROLE_PAIRING_RESPONDER;
+		p2p_pasn_initialize(p2p, dev, sa, rx_freq);
+	}
 out:
 	/*
 	 * Send PD Bootstrapping Response for the PD Request
diff --git a/src/pasn/pasn_common.h b/src/pasn/pasn_common.h
index 36710c2b7..469a4d438 100644
--- a/src/pasn/pasn_common.h
+++ b/src/pasn/pasn_common.h
@@ -55,6 +55,7 @@ struct pasn_data {
 	int rsn_pairwise;
 	u16 rsnxe_capab;
 	const u8 *rsnxe_ie;
+	size_t rsnxe_ie_len;
 	bool custom_pmkid_valid;
 	u8 custom_pmkid[PMKID_LEN];
 
@@ -66,6 +67,7 @@ struct pasn_data {
 	size_t extra_ies_len;
 
 	/* External modules do not access below variables */
+	size_t kek_len;
 	u16 group;
 	bool secure_ltf;
 	int freq;
@@ -129,6 +131,8 @@ struct pasn_data {
 	struct os_reltime last_comeback_key_update;
 	u16 comeback_idx;
 	u16 *comeback_pending_idx;
+	struct wpabuf *action_frame_wrapper;
+	struct wpabuf *frame;
 
 	/**
 	 * send_mgmt - Function handler to transmit a Management frame
@@ -150,6 +154,10 @@ struct pasn_data {
 	 */
 	int (*validate_custom_pmkid)(void *ctx, const u8 *addr,
 				     const u8 *pmkid);
+
+	int (*update_extra_ies)(void *ctx, const u8 *peer_addr, bool dira);
+
+	int (*parse_encrypted_data)(void *ctx, const u8 *data, size_t len);
 };
 
 /* Initiator */
@@ -202,9 +210,14 @@ void pasn_set_peer_addr(struct pasn_data *pasn, const u8 *addr);
 void pasn_set_bssid(struct pasn_data *pasn, const u8 *addr);
 void pasn_set_initiator_pmksa(struct pasn_data *pasn,
 			      struct rsn_pmksa_cache *pmksa);
-void pasn_set_responder_pmksa(struct pasn_data *pasn,
-			      struct rsn_pmksa_cache *pmksa);
 int pasn_set_pt(struct pasn_data *pasn, struct sae_pt *pt);
+struct rsn_pmksa_cache * pasn_initiator_pmksa_cache_init(void);
+void pasn_initiator_pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa);
+int pasn_initiator_pmksa_cache_add(struct rsn_pmksa_cache *pmksa, u8 *own_addr,
+				   u8 *bssid, u8 *pmk, size_t pmk_len);
+int pasn_initiator_pmksa_cache_get(struct rsn_pmksa_cache *pmksa, u8 *bssid,
+				   u8 *pmkid, u8 *pmk, size_t *pmk_len);
+void pasn_initiator_pmksa_cache_flush(struct rsn_pmksa_cache *pmksa);
 
 /* Responder */
 void pasn_set_password(struct pasn_data *pasn, const char *password);
@@ -215,6 +228,15 @@ void pasn_set_rsnxe_ie(struct pasn_data *pasn, const u8 *rsnxe_ie);
 void pasn_set_custom_pmkid(struct pasn_data *pasn, const u8 *pmkid);
 int pasn_set_extra_ies(struct pasn_data *pasn, const u8 *extra_ies,
 		       size_t extra_ies_len);
+void pasn_set_responder_pmksa(struct pasn_data *pasn,
+			      struct rsn_pmksa_cache *pmksa);
+struct rsn_pmksa_cache * pasn_responder_pmksa_cache_init(void);
+void pasn_responder_pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa);
+int pasn_responder_pmksa_cache_add(struct rsn_pmksa_cache *pmksa, u8 *own_addr,
+				   u8 *bssid, u8 *pmk, size_t pmk_len);
+int pasn_responder_pmksa_cache_get(struct rsn_pmksa_cache *pmksa, u8 *bssid,
+				   u8 *pmkid, u8 *pmk, size_t *pmk_len);
+void pasn_responder_pmksa_cache_flush(struct rsn_pmksa_cache *pmksa);
 
 int pasn_get_akmp(struct pasn_data *pasn);
 int pasn_get_cipher(struct pasn_data *pasn);
diff --git a/src/pasn/pasn_initiator.c b/src/pasn/pasn_initiator.c
index d273067b7..e56ef3897 100644
--- a/src/pasn/pasn_initiator.c
+++ b/src/pasn/pasn_initiator.c
@@ -26,6 +26,50 @@
 #include "pasn_common.h"
 
 
+struct rsn_pmksa_cache * pasn_initiator_pmksa_cache_init(void)
+{
+	return pmksa_cache_init(NULL, NULL, NULL, NULL, NULL);
+}
+
+
+void pasn_initiator_pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa)
+{
+	return pmksa_cache_deinit(pmksa);
+}
+
+
+int pasn_initiator_pmksa_cache_add(struct rsn_pmksa_cache *pmksa, u8 *own_addr,
+				   u8 *bssid, u8 *pmk, size_t pmk_len)
+{
+	if (pmksa_cache_add(pmksa, pmk, pmk_len, NULL, NULL, 0, bssid, own_addr,
+			    NULL, WPA_KEY_MGMT_SAE, 0))
+		return 0;
+	return -1;
+}
+
+
+int pasn_initiator_pmksa_cache_get(struct rsn_pmksa_cache *pmksa, u8 *bssid,
+				   u8 *pmkid, u8 *pmk, size_t *pmk_len)
+{
+	struct rsn_pmksa_cache_entry *entry;
+
+	entry = pmksa_cache_get(pmksa, bssid, NULL, NULL, NULL, 0);
+	if (entry) {
+		os_memcpy(pmkid, entry->pmkid, PMKID_LEN);
+		os_memcpy(pmk, entry->pmk, entry->pmk_len);
+		*pmk_len = entry->pmk_len;
+		return 0;
+	}
+	return -1;
+}
+
+
+void pasn_initiator_pmksa_cache_flush(struct rsn_pmksa_cache *pmksa)
+{
+	return pmksa_cache_flush(pmksa, NULL, NULL, 0, false);
+}
+
+
 void pasn_set_initiator_pmksa(struct pasn_data *pasn,
 			      struct rsn_pmksa_cache *pmksa)
 {
@@ -587,7 +631,10 @@ static struct wpabuf * wpas_pasn_build_auth_1(struct pasn_data *pasn,
 	if (wpa_pasn_add_wrapped_data(buf, wrapped_data_buf) < 0)
 		goto fail;
 
-	wpa_pasn_add_rsnxe(buf, pasn->rsnxe_capab);
+	if (pasn->rsnxe_ie)
+		wpabuf_put_data(buf, pasn->rsnxe_ie, pasn->rsnxe_ie_len);
+	else
+		wpa_pasn_add_rsnxe(buf, pasn->rsnxe_capab);
 
 	wpa_pasn_add_extra_ies(buf, pasn->extra_ies, pasn->extra_ies_len);
 
@@ -616,11 +663,13 @@ fail:
 }
 
 
-static struct wpabuf * wpas_pasn_build_auth_3(struct pasn_data *pasn)
+static struct wpabuf * wpas_pasn_build_auth_3(struct pasn_data *pasn,
+					      const u8 *mgmt, size_t len)
 {
 	struct wpabuf *buf, *wrapped_data_buf = NULL;
 	u8 mic[WPA_PASN_MAX_MIC_LEN];
-	u8 mic_len, data_len;
+	u8 mic_len;
+	size_t data_len;
 	const u8 *data;
 	u8 *ptr;
 	u8 wrapped_data;
@@ -654,6 +703,11 @@ static struct wpabuf * wpas_pasn_build_auth_3(struct pasn_data *pasn)
 	wpabuf_free(wrapped_data_buf);
 	wrapped_data_buf = NULL;
 
+	if (pasn->update_extra_ies && pasn->cb_ctx)
+		pasn->update_extra_ies(pasn->cb_ctx, pasn->peer_addr, false);
+
+	wpa_pasn_add_extra_ies(buf, pasn->extra_ies, pasn->extra_ies_len);
+
 	/* Add the MIC */
 	mic_len = pasn_mic_len(pasn->akmp, pasn->cipher);
 	wpabuf_put_u8(buf, WLAN_EID_MIC);
@@ -747,13 +801,25 @@ void wpa_pasn_reset(struct pasn_data *pasn)
 	pasn->derive_kdk = false;
 	pasn->rsn_ie = NULL;
 	pasn->rsn_ie_len = 0;
-	pasn->rsnxe_ie = NULL;
 	pasn->custom_pmkid_valid = false;
 
+	if (pasn->rsnxe_ie) {
+		os_free((u8 *)pasn->rsnxe_ie);
+		pasn->rsnxe_ie = NULL;
+		pasn->rsnxe_ie_len = 0;
+	}
 	if (pasn->extra_ies) {
 		os_free((u8 *) pasn->extra_ies);
 		pasn->extra_ies = NULL;
 	}
+	if (pasn->action_frame_wrapper) {
+		wpabuf_free(pasn->action_frame_wrapper);
+		pasn->action_frame_wrapper = NULL;
+	}
+	if (pasn->frame) {
+		wpabuf_free(pasn->frame);
+		pasn->frame = NULL;
+	}
 }
 
 
@@ -928,12 +994,12 @@ static int wpas_pasn_send_auth_1(struct pasn_data *pasn, const u8 *own_addr,
 			      wpabuf_head(frame), wpabuf_len(frame), 0,
 			      pasn->freq, 1000);
 
-	wpabuf_free(frame);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "PASN: Failed sending 1st auth frame");
+		wpabuf_free(frame);
 		goto fail;
 	}
-
+	pasn->frame = frame;
 	return 0;
 
 fail:
@@ -1233,7 +1299,7 @@ int wpa_pasn_auth_rx(struct pasn_data *pasn, const u8 *data, size_t len,
 			      pasn->own_addr, pasn->peer_addr,
 			      wpabuf_head(secret), wpabuf_len(secret),
 			      &pasn->ptk, pasn->akmp, pasn->cipher,
-			      pasn->kdk_len);
+			      pasn->kdk_len, pasn->kek_len);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "PASN: Failed to derive PTK");
 		goto fail;
@@ -1323,7 +1389,10 @@ int wpa_pasn_auth_rx(struct pasn_data *pasn, const u8 *data, size_t len,
 
 	wpa_printf(MSG_DEBUG, "PASN: Success verifying Authentication frame");
 
-	frame = wpas_pasn_build_auth_3(pasn);
+	if (pasn->parse_encrypted_data && pasn->cb_ctx)
+		pasn->parse_encrypted_data(pasn->cb_ctx, data, len);
+
+	frame = wpas_pasn_build_auth_3(pasn, data, len);
 	if (!frame) {
 		wpa_printf(MSG_DEBUG, "PASN: Failed building 3rd auth frame");
 		goto fail;
@@ -1332,12 +1401,13 @@ int wpa_pasn_auth_rx(struct pasn_data *pasn, const u8 *data, size_t len,
 	ret = pasn->send_mgmt(pasn->cb_ctx,
 			      wpabuf_head(frame), wpabuf_len(frame), 0,
 			      pasn->freq, 100);
-	wpabuf_free(frame);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "PASN: Failed sending 3st auth frame");
+		wpabuf_free(frame);
 		goto fail;
 	}
 
+	pasn->frame = frame;
 	wpa_printf(MSG_DEBUG, "PASN: Success sending last frame. Store PTK");
 
 	pasn->status = WLAN_STATUS_SUCCESS;
diff --git a/src/pasn/pasn_responder.c b/src/pasn/pasn_responder.c
index b99136492..89f5fff56 100644
--- a/src/pasn/pasn_responder.c
+++ b/src/pasn/pasn_responder.c
@@ -26,6 +26,50 @@
 #include "pasn_common.h"
 
 
+struct rsn_pmksa_cache * pasn_responder_pmksa_cache_init(void)
+{
+	return pmksa_cache_auth_init(NULL, NULL);
+}
+
+
+void pasn_responder_pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa)
+{
+	return pmksa_cache_auth_deinit(pmksa);
+}
+
+
+int pasn_responder_pmksa_cache_add(struct rsn_pmksa_cache *pmksa, u8 *own_addr,
+				   u8 *bssid, u8 *pmk, size_t pmk_len)
+{
+	if (pmksa_cache_auth_add(pmksa, pmk, pmk_len, NULL, NULL, 0, own_addr,
+				 bssid, 0, NULL, WPA_KEY_MGMT_SAE))
+		return 0;
+	return -1;
+}
+
+
+int pasn_responder_pmksa_cache_get(struct rsn_pmksa_cache *pmksa, u8 *bssid,
+				   u8 *pmkid, u8 *pmk, size_t *pmk_len)
+{
+	struct rsn_pmksa_cache_entry *entry;
+
+	entry = pmksa_cache_auth_get(pmksa, bssid, NULL);
+	if (entry) {
+		os_memcpy(pmkid, entry->pmkid, PMKID_LEN);
+		os_memcpy(pmk, entry->pmk, entry->pmk_len);
+		*pmk_len = entry->pmk_len;
+		return 0;
+	}
+	return -1;
+}
+
+
+void pasn_responder_pmksa_cache_flush(struct rsn_pmksa_cache *pmksa)
+{
+	return pmksa_cache_auth_flush(pmksa);
+}
+
+
 void pasn_set_responder_pmksa(struct pasn_data *pasn,
 			      struct rsn_pmksa_cache *pmksa)
 {
@@ -349,7 +393,7 @@ pasn_derive_keys(struct pasn_data *pasn,
 	ret = pasn_pmk_to_ptk(pmk, pmk_len, peer_addr, own_addr,
 			      wpabuf_head(secret), wpabuf_len(secret),
 			      &pasn->ptk, pasn->akmp,
-			      pasn->cipher, pasn->kdk_len);
+			      pasn->cipher, pasn->kdk_len, pasn->kek_len);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "PASN: Failed to derive PTK");
 		return -1;
@@ -414,7 +458,7 @@ static void handle_auth_pasn_comeback(struct pasn_data *pasn,
 		   "PASN: comeback: STA=" MACSTR, MAC2STR(peer_addr));
 
 	ret = pasn->send_mgmt(pasn->cb_ctx, wpabuf_head_u8(buf),
-			      wpabuf_len(buf), 0, 0, 0);
+			      wpabuf_len(buf), 0, pasn->freq, 0);
 	if (ret)
 		wpa_printf(MSG_INFO, "PASN: Failed to send comeback frame 2");
 
@@ -502,6 +546,10 @@ int handle_auth_pasn_resp(struct pasn_data *pasn, const u8 *own_addr,
 	if (rsnxe_ie)
 		wpabuf_put_data(buf, rsnxe_ie, 2 + rsnxe_ie[1]);
 
+	if (pasn->update_extra_ies && pasn->cb_ctx)
+		pasn->update_extra_ies(pasn->cb_ctx, peer_addr,
+				       pmkid ? true : false);
+
 	wpa_pasn_add_extra_ies(buf, pasn->extra_ies, pasn->extra_ies_len);
 
 	/* Add the mic */
@@ -579,12 +627,14 @@ done:
 		   MAC2STR(peer_addr));
 
 	ret = pasn->send_mgmt(pasn->cb_ctx, wpabuf_head_u8(buf),
-			      wpabuf_len(buf), 0, 0, 0);
-	if (ret)
+			      wpabuf_len(buf), 0, pasn->freq, 0);
+	if (ret) {
 		wpa_printf(MSG_INFO, "send_auth_reply: Send failed");
+		goto fail;
+	}
 
 	wpabuf_free(rsn_buf);
-	wpabuf_free(buf);
+	pasn->frame = buf;
 	return ret;
 fail:
 	wpabuf_free(wrapped_data_buf);
@@ -1020,6 +1070,9 @@ int handle_auth_pasn_3(struct pasn_data *pasn, const u8 *own_addr,
 		wpabuf_free(wrapped_data);
 	}
 
+	if (pasn->parse_encrypted_data && pasn->cb_ctx)
+		pasn->parse_encrypted_data(pasn->cb_ctx, (const u8 *) mgmt, len);
+
 	wpa_printf(MSG_INFO,
 		   "PASN: Success handling transaction == 3. Store PTK");
 	return 0;
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index 4ab9f0b88..c3465a1d0 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -5952,6 +5952,35 @@ static void wpas_link_reconfig(struct wpa_supplicant *wpa_s)
 }
 
 
+int wpas_pasn_auth(struct wpa_supplicant *wpa_s,
+		   const struct ieee80211_mgmt *mgmt, size_t len, int freq)
+{
+	int ret = 0;
+	struct ieee802_11_elems elems;
+
+	if (len < 24) {
+		wpa_printf(MSG_DEBUG, "nl80211: Too short management frame");
+		return -2;
+	}
+
+	if (ieee802_11_parse_elems(mgmt->u.auth.variable,
+				   len - offsetof(struct ieee80211_mgmt,
+						  u.auth.variable),
+				   &elems, 1) == ParseFailed) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: Failed parsing Authentication frame");
+		return -2;
+	}
+
+	if (!elems.p2p2_ie || !elems.p2p2_ie_len)
+		ret = wpas_pasn_auth_rx(wpa_s, mgmt, len);
+	else
+		ret = wpas_p2p_pasn_auth_rx(wpa_s, mgmt, len, freq);
+
+	return ret;
+}
+
+
 void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 			  union wpa_event_data *data)
 {
@@ -6182,11 +6211,22 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 #endif /* CONFIG_WNM */
 #ifdef CONFIG_PASN
 		if (data->tx_status.type == WLAN_FC_TYPE_MGMT &&
-		    data->tx_status.stype == WLAN_FC_STYPE_AUTH &&
-		    wpas_pasn_auth_tx_status(wpa_s, data->tx_status.data,
-					     data->tx_status.data_len,
-					     data->tx_status.ack) == 0)
-			break;
+		    data->tx_status.stype == WLAN_FC_STYPE_AUTH) {
+			if (!wpa_s->pasn_auth_work &&
+			    wpa_s->p2p_pasn_auth_work) {
+				if (wpas_p2p_pasn_auth_tx_status(wpa_s,
+						data->tx_status.data,
+						data->tx_status.data_len,
+						data->tx_status.ack) == 0)
+					break;
+			} else {
+				if (wpas_pasn_auth_tx_status(wpa_s,
+						data->tx_status.data,
+						data->tx_status.data_len,
+						data->tx_status.ack) == 0)
+					break;
+			}
+		}
 #endif /* CONFIG_PASN */
 #ifdef CONFIG_AP
 		if (wpa_s->ap_iface == NULL) {
@@ -6458,8 +6498,8 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 			}
 #ifdef CONFIG_PASN
 			if (stype == WLAN_FC_STYPE_AUTH &&
-			    wpas_pasn_auth_rx(wpa_s, mgmt,
-					      data->rx_mgmt.frame_len) != -2)
+			    wpas_pasn_auth(wpa_s, mgmt, data->rx_mgmt.frame_len,
+					   data->rx_mgmt.freq) != -2)
 				break;
 #endif /* CONFIG_PASN */
 
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index d2e661318..5e04add1c 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -38,7 +38,6 @@
 #include "p2p_supplicant.h"
 #include "wifi_display.h"
 
-
 /*
  * How many times to try to scan to find the GO before giving up on join
  * request.
@@ -1716,6 +1715,28 @@ static void wpas_send_action_done(void *ctx)
 	offchannel_send_action_done(wpa_s);
 }
 
+struct wpa_p2p_pasn_auth_work {
+	u8 peer_addr[ETH_ALEN];
+	bool verify;
+	int freq;
+};
+
+
+static void wpas_p2p_pasn_free_auth_work(struct wpa_p2p_pasn_auth_work *awork)
+{
+	os_free(awork);
+}
+
+
+static void wpas_p2p_pasn_cancel_auth_work(struct wpa_supplicant *wpa_s)
+{
+	wpa_printf(MSG_DEBUG, "P2P PASN: Cancel p2p-pasn-start-auth work");
+
+	/* Remove pending/started work */
+	radio_remove_works(wpa_s, "p2p-pasn-start-auth", 0);
+}
+
+
 
 static int wpas_copy_go_neg_results(struct wpa_supplicant *wpa_s,
 				    struct p2p_go_neg_results *params)
@@ -2391,6 +2412,12 @@ static void wpas_p2p_group_formation_timeout(void *eloop_ctx,
 					     void *timeout_ctx)
 {
 	struct wpa_supplicant *wpa_s = eloop_ctx;
+
+	if (wpa_s->p2p_pasn_auth_work) {
+		wpas_p2p_pasn_cancel_auth_work(wpa_s);
+		wpa_s->p2p_pasn_auth_work = NULL;
+	}
+
 	wpa_printf(MSG_DEBUG, "P2P: Group Formation timed out");
 	wpas_p2p_group_formation_failed(wpa_s, 0);
 }
@@ -2456,6 +2483,11 @@ static void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res)
 		wpa_s->roc_waiting_drv_freq = 0;
 	}
 
+	if (wpa_s->p2p_pasn_auth_work) {
+		wpas_p2p_pasn_cancel_auth_work(wpa_s);
+		wpa_s->p2p_pasn_auth_work = NULL;
+	}
+
 	if (res->status) {
 		wpa_msg_global(wpa_s, MSG_INFO,
 			       P2P_EVENT_GO_NEG_FAILURE "status=%d",
@@ -4822,6 +4854,68 @@ static int wpas_p2p_get_pref_freq_list(void *ctx, int go,
 					  WPA_IF_P2P_CLIENT, len, freq_list);
 }
 
+
+static void wpas_p2p_pasn_auth_start_cb(struct wpa_radio_work *work, int deinit)
+{
+	struct wpa_supplicant *wpa_s = work->wpa_s;
+	struct wpa_p2p_pasn_auth_work *awork = work->ctx;
+	struct p2p_data *p2p = wpa_s->global->p2p;
+	const u8 *peer_addr = NULL;
+
+	if (deinit) {
+		if (!work->started) {
+			eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
+					     wpa_s->p2pdev, NULL);
+		}
+		os_free(awork);
+		return;
+	}
+
+	if (!is_zero_ether_addr(awork->peer_addr))
+		peer_addr = awork->peer_addr;
+	if (p2p_initiate_pasn_auth(p2p, peer_addr, awork->freq)) {
+		wpa_printf(MSG_DEBUG,
+			   "P2P PASN: Failed to start PASN authentication");
+		goto fail;
+	}
+	eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
+			     wpa_s->p2pdev, NULL);
+	eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0,
+			       wpas_p2p_group_formation_timeout,
+			       wpa_s->p2pdev, NULL);
+	wpa_s->p2p_pasn_auth_work = work;
+	return;
+fail:
+	wpas_p2p_pasn_free_auth_work(awork);
+	work->ctx = NULL;
+	radio_work_done(work);
+}
+
+static int wpas_p2p_initiate_pasn_auth(struct wpa_supplicant *wpa_s,
+				       const u8 *peer_addr, int freq)
+{
+	struct wpa_p2p_pasn_auth_work *awork;
+
+	wpas_p2p_pasn_cancel_auth_work(wpa_s);
+	wpa_s->p2p_pasn_auth_work = NULL;
+
+	awork = os_zalloc(sizeof(*awork));
+	if (!awork)
+		return -1;
+
+	awork->freq = freq;
+	os_memcpy(awork->peer_addr, peer_addr, ETH_ALEN);
+
+	if (radio_add_work(wpa_s, freq, "p2p-pasn-start-auth", 1,
+			   wpas_p2p_pasn_auth_start_cb, awork) < 0) {
+		wpas_p2p_pasn_free_auth_work(awork);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "P2P PASN: Auth work successfully added");
+	return 0;
+}
+
 static void wpas_p2p_send_bootstrap_comeback(void *eloop_ctx, void *timeout_ctx)
 {
 	struct wpa_supplicant *wpa_s = eloop_ctx;
@@ -4886,8 +4980,46 @@ static void wpas_bootstrap_completed(void *ctx, const u8 *addr, int status,
 
 	if (status)
 		return;
+
+	wpas_p2p_initiate_pasn_auth(wpa_s, addr, freq);
 }
 
+static int wpas_p2p_pasn_send_mlme(void *ctx, const u8 *data, size_t data_len,
+				   int noack, unsigned int freq,
+				   unsigned int wait)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	return wpa_drv_send_mlme(wpa_s, data, data_len, noack, freq, wait);
+}
+
+
+static int wpas_p2p_pasn_update_extra_ies(void *ctx, const u8 *peer_addr,
+					  bool dira)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	struct p2p_data *p2p = wpa_s->global->p2p;
+
+	return p2p_pasn_update_extra_ies(p2p, peer_addr, dira);
+}
+
+
+static int wpas_p2p_pasn_parse_encrypted_data(void *ctx, const u8 *data,
+					      size_t len)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	struct p2p_data *p2p = wpa_s->global->p2p;
+
+	return p2p_pasn_parse_encrypted_data(p2p, data, len);
+}
+
+int wpas_p2p_pasn_auth_tx_status(struct wpa_supplicant *wpa_s, const u8 *data,
+				 size_t data_len, u8 acked)
+{
+	struct p2p_data *p2p = wpa_s->global->p2p;
+
+	return p2p_pasn_auth_tx_status(p2p, data, data_len, acked);
+}
 
 int wpas_p2p_mac_setup(struct wpa_supplicant *wpa_s)
 {
@@ -5011,6 +5143,9 @@ int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s)
 	p2p.register_bootstrap_comeback = wpas_p2p_register_bootstrap_comeback;
 	p2p.bootstrap_req_rx = wpas_bootstrap_req_rx;
 	p2p.bootstrap_completed = wpas_bootstrap_completed;
+	p2p.pasn_send_mgmt = wpas_p2p_pasn_send_mlme;
+	p2p.pasn_update_extra_ies = wpas_p2p_pasn_update_extra_ies;
+	p2p.pasn_parse_encrypted_data = wpas_p2p_pasn_parse_encrypted_data;
 
 	os_memcpy(wpa_s->global->p2p_dev_addr, wpa_s->own_addr, ETH_ALEN);
 	os_memcpy(p2p.dev_addr, wpa_s->global->p2p_dev_addr, ETH_ALEN);
@@ -10349,3 +10484,14 @@ void wpas_p2p_process_usd_elems(struct wpa_supplicant *wpa_s, const u8 *buf,
 		return;
 	p2p_process_usd_elems(p2p, buf, buf_len, peer_addr, freq);
 }
+
+int wpas_p2p_pasn_auth_rx(struct wpa_supplicant *wpa_s,
+			  const struct ieee80211_mgmt *mgmt, size_t len,
+			  int freq)
+{
+	struct p2p_data *p2p = wpa_s->global->p2p;
+
+	if (wpa_s->global->p2p_disabled || !p2p)
+		return -2;
+	return p2p_pasn_auth_rx(p2p, mgmt, len, freq);
+}
diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h
index a2cb78d7c..4f798980d 100644
--- a/wpa_supplicant/p2p_supplicant.h
+++ b/wpa_supplicant/p2p_supplicant.h
@@ -227,7 +227,9 @@ int wpas_p2p_lo_start(struct wpa_supplicant *wpa_s, unsigned int freq,
 int wpas_p2p_lo_stop(struct wpa_supplicant *wpa_s);
 int wpas_p2p_mac_setup(struct wpa_supplicant *wpa_s);
 struct wpabuf * wpas_p2p_usd_elems(struct wpa_supplicant *wpa_s);
-
+int wpas_p2p_pasn_auth_rx(struct wpa_supplicant *wpa_s,
+			  const struct ieee80211_mgmt *mgmt, size_t len,
+			  int freq);
 #else /* CONFIG_P2P */
 
 static inline int
@@ -357,6 +359,12 @@ static inline struct wpabuf * wpas_p2p_usd_elems(struct wpa_supplicant *wpa_s)
 {
 	return NULL;
 }
+static int wpas_p2p_pasn_auth_rx(struct wpa_supplicant *wpa_s,
+				 const struct ieee80211_mgmt *mgmt, size_t len,
+				 int freq)
+{
+	return 0;
+}
 
 #endif /* CONFIG_P2P */
 
diff --git a/wpa_supplicant/pasn_supplicant.c b/wpa_supplicant/pasn_supplicant.c
index 1bb38f73d..5959fca2d 100644
--- a/wpa_supplicant/pasn_supplicant.c
+++ b/wpa_supplicant/pasn_supplicant.c
@@ -806,6 +806,11 @@ int wpas_pasn_auth_rx(struct wpa_supplicant *wpa_s,
 	if (!wpa_s->pasn_auth_work)
 		return -2;
 
+	if (pasn->frame) {
+		wpabuf_free(pasn->frame);
+		pasn->frame = NULL;
+	}
+
 	pasn_register_callbacks(pasn, wpa_s, wpas_pasn_send_mlme, NULL);
 	ret = wpa_pasn_auth_rx(pasn, (const u8 *) mgmt, len, &pasn_data);
 	if (ret == 0) {
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 2724183da..0740bdab4 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -1588,6 +1588,9 @@ struct wpa_supplicant {
 	struct wpa_radio_work *pasn_auth_work;
 	unsigned int pasn_count;
 	struct pasn_auth *pasn_params;
+#ifdef CONFIG_P2P
+	struct wpa_radio_work *p2p_pasn_auth_work;
+#endif /* CONFIG_P2P */
 #endif /* CONFIG_PASN */
 
 	bool is_6ghz_enabled;
@@ -2010,5 +2013,7 @@ bool wpas_ap_link_address(struct wpa_supplicant *wpa_s, const u8 *addr);
 void wpas_p2p_process_usd_elems(struct wpa_supplicant *wpa_s, const u8 *buf,
 				u16 buf_len, const u8 *peer_addr,
 				unsigned int freq);
+int wpas_p2p_pasn_auth_tx_status(struct wpa_supplicant *wpa_s, const u8 *data,
+				 size_t data_len, u8 acked);
 
 #endif /* WPA_SUPPLICANT_I_H */
-- 
2.34.1


_______________________________________________
Hostap mailing list
Hostap@xxxxxxxxxxxxxxxxxxx
http://lists.infradead.org/mailman/listinfo/hostap



[Index of Archives]     [Linux Wireless]     [Linux Kernel]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]

  Powered by Linux