[wpa_supplicant] Cumulative patch from commit 61c6e7c62

Bug: 245752074
Test: connect/disconnect to WPA2, WPA3 networks
Test: SoftAp & p2p connection
Test: Regression test(b/247129565)

BYPASS_INCLUSIVE_LANGUAGE_REASON=Merged from Open source

61c6e7c62 nl80211: Parsing of MLO connection info from roam+auth event
3d842d910 MLD STA: Add support for parsing MLO KDEs
e1105bab8 Add QCA vendor interface support for Spatial Reuse (SR) feature
1f39f85b7 Fix compiler warning on shift overflow in QCA vendor definitions
c5b950b6a Fix compilation error due to use of bitwise '&' with boolean operands
7bdd3f2aa P2P: Allow P2P CSA from 5 GHz to 2.4 GHz band
a7684a21c Update hw mode after ACS selects the channel
768537921 P2P: Set operating class along with operating channel width
ee7eec518 P2P: Fix the issue in setting optimized listen channel
01944c095 Fix RNR BSSID setting for own interfaces
3a7fe1e21 dbus: Omit FT key mgmt capabilities without CONFIG_IEEE80211R
d2caf6ef5 dbus: Fix property DebugShowKeys and DebugTimestamp
a17f9a2d4 Add usage print for -q flag
f77c0f914 ACS: Include frequency in info messages
0c7b3814c Use a less generic name for IEEE802.11 CRC-32 routine
7ed17eee3 ACS: Don't select indoor channel on outdoor operation
1f795df7a wpa_supplicant man page missing -I flag
ef2d2e81a Add a new QCA vendor attribute to support flexible TWT
1b6f3b585 MLD STA: Indicate per link channel switch
b7f98d92d MLD STA: Add per-link MLO signal poll
28b2256da MLD STA: Add MLO_STATUS control interface command
8dd5b9a9e nl80211: Send bssid info as parameter to nl80211_get_link_signal()
6ca98040a MLD STA: Indicate AP MLD address in STATUS command
22ba81cfe MLD STA: Indicate AP MLD address in CTRL-EVENT-CONNECTED
db99e7341 Add AP MLD address into BSS command output
4bd316524 bss: Parse and store MLD address of the AP from Multi-Link element
cc29cadf2 Helper function to get MLD address from Basic Multi-Link element
9a8bd7be0 scan: Helper function to get ML IE of specified type from scan result IEs
e21128614 nl80211: Use AP MLD address to set supplicant port as authorized
7784964cb MLD STA: Fetch MLO connection info into core wpa_supplicant
e2147f917 nl80211: Indicate MLO connection info in driver status
c7741009f nl80211: Parse MLO connection info in NL80211_CMD_CONNECT event
bd499f0e6 Add a helper function to get ML IE of specified type from IEs buffer
36645f839 EHT: Add Multi-Link element field definitions
62612dfc1 P2P: Use only PSC for P2P group in the 6 GHz band
7974d8053 Configure RRM elements to the driver in the driver-AP-SME case
d1a7626ff SAE: Use correct SSID profile for SAE auth retries during external auth
096feac19 SAE: Include the new -EXT-KEY AKMs in external auth case
76793cbbc SAE: Make sure H2E PT is derived for external auth SSID profile
122cdd592 Enable TWT responder AP role only if IEEE 802.11ax/HE is enabled
ed442e8dc Add p2p_optimize_listen_chan=1 to default Android template
5b76c3654 Extend attributes of QCA_NL80211_VENDOR_SUBCMD_GET_STA_INFO
897e09085 QCA vendor attribute to configure periodic sounding
0cc6f985d wlantest: Recognize additional not-Robust Action categories
cd392151c Validate MAC Address KDE length in the parser
6f8af5974 Fix expiration logic for the first PTKSA cache entry
85e28a79b PASN: Set secure ranging context to driver after association
9b62b61c6 PASN: Configure secure ranging context to the driver in AP mode
de3b91a17 nl80211: Define vendor interface functions to offload PASN authentication
edd89d6db PASN: Set keys from PTKSA cache for a valid PTKSA entry
e2c3cdf2c nl80211: Register PASN Authentication frames for SME-in-driver
58a96187e nl80211: Allow PASN Authentication frames with random (foreign) MAC address
24929543b PASN: Deauthenticate on PTKSA cache entry expiration
74d894a2e PASN: Offload handshake for multiple peers from the driver
06317f5e3 PASN: Add driver operation to set secure ranging context and PASN response
2edebc6b6 PASN: Add interface to handle PASN request from the driver
9330bf318 PASN: Add function to compute LTF keyseed from KDK
9391f7100 Add own MAC address in PASN supplicant start and deauthentication
580bd04cf Add own MAC address used for key derivation to PTKSA cache
d0d585c48 Store secure ranging driver capabilities in WPA state machine
96a604128 Use separate PASN capabilities for AP and STA modes
909fa448e EAPOL: Update PMK length in EAPOL callback to support longer keys
c80dc6940 OpenSSL: Include rsa.h for all OpenSSL versions
723eb4f38 P2P: Fix a typo in a comment about removing 6 GHz channels
e9627f8c3 P2P: Skip 6 GHz band directly if 6 GHz P2P is disabled
03f7f633a Fix wrong AKM priority for FILS
1f9a988f1 DPP3: Do not initiate PKEX for PB if no configuration is available
aa75aa1dc Add QCA vendor interface to get SAR capabilities to userspace
5de45546d Add support to send multi AKM connect request when driver's SME in use
0ce1545dc nl80211: Determine maximum number of supported AKMs
48c620829 Update PSK after cross AKM roaming to WPA-PSK when driver's SME in use
7e97c619a Sync with wireless-next.git include/uapi/linux/nl80211.h
54706957e DPP: Fix DPP_RELAY_ADD_CONTROLLER command parsing
44b26d82b nl80211: Silence a compiler warning on printf in 32-bit builds
4ae14deee DPP3: Use chirping channel list in PB discovery
c58be1d8f DPP: Channel list generation for presence announcement to helper funcion
820211245 OpenSSL: Fix HPKE in some corner cases
57968faea DPP: Do not discard network introduction frames in test mode
d72302c6b DPP: Do not use 6 GHz channels for push button
89de431f2 DPP: Add config response status value to DPP-CONF-SENT
80d5e264c Enhance QCA vendor roam event to indicate MLO links after reassociation
662249306 Update copyright notices for the QCA vendor definitions
1d08b238c nl80211: Allow more time for the initial scan with 6 GHz
faf9c04cb Remove a host of unnecessary OPENSSL_IS_BORINGSSL ifdefs
b9cd5a82f Always process pending QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH data
ef4cd8e33 QoS: Use common classifier_mask for ipv4/ipv6
93be02592 Add fixed FDD mode to qca_btc_chain_mode QCA vendor attribute
5565fbee2 DPP: Check Enrollee supported curves when building Config Response
4cfb484e9 DPP: Allow dpp_controller_start without arguments in CLIs
c97000933 Fix ifdef condition for imsi_privacy_cert
f70db167a SAE: Derive a variable length PMK with the new AKM suites
91010e6f6 SAE: Indicate AKM suite selector in commit for new AKM suites
e81ec0962 SAE: Use H2E unconditionally with the new AKM suites
f8eed2e8b SAE: Store PMK length and AKM in SAE data
9dc4e9d13 SAE: EAPOL-Key and key/MIC length information for the new AKM suites
a32ef3cfb SAE: Driver capability flags for the new SAE AKM suites
91df8c9c6 SAE: Internal WPA_KEY_MGMT_* defines for extended key AKMs
5c8a714b1 SAE: Use wpa_key_mgmt_sae() helper
5456b0f26 Define new RSN AKM suite selector values
def33101c DPP: Clear push button announcement state on wpa_supplicant FLUSH
d22dfe918 DPP: Event message for indicating when Relay would need a Controller
bfe3cfc38 DPP: Allow Relay connections to Controllers to be added and removed
808834b18 Add a comparison function for hostapd_ip_addr
f7763880b DPP: Advertise Configurator connectivity on Relay automatically
ca682f80a DPP: Dynamic Controller initiated connection on Relay
d2388bcca DPP: Strict validation of PKEX peer bootstrapping key during auth
a7b8cef8b DPP3: Fix push button boostrapping key passing through PKEX
69d7c8e6b DPP: Add peer=id entry for PKEX-over-TCP case
1ff9251a8 DPP3: Push button Configurator in wpa_supplicant
e9137950f DPP: Recognize own PKEX Exchange Request if it ends up being received
692956446 DPP: Note PKEX code/identifier deletion in debug log
ae4a3a6f6 DPP: Add DPP-CONF-REQ-RX event for Controller
fb2937b85 DPP: Allow Controller to initiate PKEX through Relay
15af83cf1 DPP: Delete PKEX code and identifier on success completion of PKEX
479e412a6 DPP3: Default value for dpp_connector_privacy
148de3e0d DPP3: Private Peer Introduction protocol
786ea402b HPKE base mode with single-shot API
f0273bc81 OpenSSL: Remove a forgotten debug print
68209ddbe DPP: Allow 3rd party information to be added into config object
0e2217c95 DPP: Allow 3rd party information to be added into config request obj
3d82fbe05 Add QCA vendor subcommand and attributes for SCS rule configuration
16b62ddfa QCA vendor attribute for DBAM configuration
451ede2c3 DPP: Allow AP/Relay to be configured to listed for new TCP connections
7bbe85987 DPP3: Allow external configuration to be specified on AP for PB
8db786a43 DPP3: Testing functionality for push button announcements
37bccfcab DPP3: Push button bootstrap mechanism
a0054fe7c Add AP and STA specific P802.11az security capabilities (vendor command)
159e63613 QCA vendor command for CoAP offload processing
3b7bb17f6 Add QCA vendor attribute for TIM beacon statistics

Change-Id: Ic5faae10839f317cc70a4df7a3f2047812ffd34c
diff --git a/src/ap/acs.c b/src/ap/acs.c
index 57fc6da..8ee2e04 100644
--- a/src/ap/acs.c
+++ b/src/ap/acs.c
@@ -420,19 +420,24 @@
 static int acs_survey_is_sufficient(struct freq_survey *survey)
 {
 	if (!(survey->filled & SURVEY_HAS_NF)) {
-		wpa_printf(MSG_INFO, "ACS: Survey is missing noise floor");
+		wpa_printf(MSG_INFO,
+			   "ACS: Survey for freq %d is missing noise floor",
+			   survey->freq);
 		return 0;
 	}
 
 	if (!(survey->filled & SURVEY_HAS_CHAN_TIME)) {
-		wpa_printf(MSG_INFO, "ACS: Survey is missing channel time");
+		wpa_printf(MSG_INFO,
+			   "ACS: Survey for freq %d is missing channel time",
+			   survey->freq);
 		return 0;
 	}
 
 	if (!(survey->filled & SURVEY_HAS_CHAN_TIME_BUSY) &&
 	    !(survey->filled & SURVEY_HAS_CHAN_TIME_RX)) {
 		wpa_printf(MSG_INFO,
-			   "ACS: Survey is missing RX and busy time (at least one is required)");
+			   "ACS: Survey for freq %d is missing RX and busy time (at least one is required)",
+			   survey->freq);
 		return 0;
 	}
 
@@ -553,6 +558,10 @@
 		if (chan->max_tx_power < iface->conf->min_tx_power)
 			continue;
 
+		if ((chan->flag & HOSTAPD_CHAN_INDOOR_ONLY) &&
+		    iface->conf->country[2] == 0x4f)
+			continue;
+
 		wpa_printf(MSG_DEBUG, "ACS: Survey analysis for channel %d (%d MHz)",
 			   chan->chan, chan->freq);
 
@@ -687,6 +696,10 @@
 		if (chan->max_tx_power < iface->conf->min_tx_power)
 			continue;
 
+		if ((chan->flag & HOSTAPD_CHAN_INDOOR_ONLY) &&
+		    iface->conf->country[2] == 0x4f)
+			continue;
+
 		if (!chan_bw_allowed(chan, bw, 1, 1)) {
 			wpa_printf(MSG_DEBUG,
 				   "ACS: Channel %d: BW %u is not supported",
@@ -996,7 +1009,8 @@
 
 	err = hostapd_select_hw_mode(iface);
 	if (err) {
-		wpa_printf(MSG_ERROR, "ACS: Could not (err: %d) select hw_mode for freq=%d channel=%d",
+		wpa_printf(MSG_ERROR,
+			   "ACS: Could not (err: %d) select hw_mode for freq=%d channel=%d",
 			err, iface->freq, iface->conf->channel);
 		err = -1;
 		goto fail;
@@ -1076,6 +1090,10 @@
 		if (chan->max_tx_power < iface->conf->min_tx_power)
 			continue;
 
+		if ((chan->flag & HOSTAPD_CHAN_INDOOR_ONLY) &&
+		    iface->conf->country[2] == 0x4f)
+			continue;
+
 		*freq++ = chan->freq;
 	}
 
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index db86a76..b73aae2 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -482,6 +482,7 @@
 	struct sae_password_entry *pw;
 
 	if ((conf->sae_pwe == 0 && !hostapd_sae_pw_id_in_use(conf) &&
+	     !wpa_key_mgmt_sae_ext_key(conf->wpa_key_mgmt) &&
 	     !hostapd_sae_pk_in_use(conf)) ||
 	    conf->sae_pwe == 3 ||
 	    !wpa_key_mgmt_sae(conf->wpa_key_mgmt))
@@ -949,6 +950,8 @@
 #ifdef CONFIG_DPP
 	os_free(conf->dpp_name);
 	os_free(conf->dpp_mud_url);
+	os_free(conf->dpp_extra_conf_req_name);
+	os_free(conf->dpp_extra_conf_req_value);
 	os_free(conf->dpp_connector);
 	wpabuf_free(conf->dpp_netaccesskey);
 	wpabuf_free(conf->dpp_csign);
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 05a1b05..268829a 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -755,12 +755,15 @@
 #ifdef CONFIG_DPP
 	char *dpp_name;
 	char *dpp_mud_url;
+	char *dpp_extra_conf_req_name;
+	char *dpp_extra_conf_req_value;
 	char *dpp_connector;
 	struct wpabuf *dpp_netaccesskey;
 	unsigned int dpp_netaccesskey_expiry;
 	struct wpabuf *dpp_csign;
 #ifdef CONFIG_DPP2
 	struct dpp_controller_conf *dpp_controller;
+	int dpp_relay_port;
 	int dpp_configurator_connectivity;
 	int dpp_pfs;
 #endif /* CONFIG_DPP2 */
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index 87c3b90..b9fd920 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -75,6 +75,14 @@
 
 	*beacon_ret = *proberesp_ret = *assocresp_ret = NULL;
 
+#ifdef NEED_AP_MLME
+	pos = buf;
+	pos = hostapd_eid_rm_enabled_capab(hapd, pos, sizeof(buf));
+	if (add_buf_data(&assocresp, buf, pos - buf) < 0 ||
+	    add_buf_data(&proberesp, buf, pos - buf) < 0)
+		goto fail;
+#endif /* NEED_AP_MLME */
+
 	pos = buf;
 	pos = hostapd_eid_time_adv(hapd, pos);
 	if (add_buf_data(&beacon, buf, pos - buf) < 0)
@@ -1016,3 +1024,30 @@
 		return 0;
 	return hapd->driver->dpp_listen(hapd->drv_priv, enable);
 }
+
+
+#ifdef CONFIG_PASN
+int hostapd_drv_set_secure_ranging_ctx(struct hostapd_data *hapd,
+				       const u8 *own_addr, const u8 *peer_addr,
+				       u32 cipher, u8 tk_len, const u8 *tk,
+				       u8 ltf_keyseed_len,
+				       const u8 *ltf_keyseed, u32 action)
+{
+	struct secure_ranging_params params;
+
+	if (!hapd->driver || !hapd->driver->set_secure_ranging_ctx)
+		return 0;
+
+	os_memset(&params, 0, sizeof(params));
+	params.own_addr = own_addr;
+	params.peer_addr = peer_addr;
+	params.cipher = cipher;
+	params.tk_len = tk_len;
+	params.tk = tk;
+	params.ltf_keyseed_len = ltf_keyseed_len;
+	params.ltf_keyseed = ltf_keyseed;
+	params.action = action;
+
+	return hapd->driver->set_secure_ranging_ctx(hapd->drv_priv, &params);
+}
+#endif /* CONFIG_PASN */
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index b4fb766..93b2244 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -138,6 +138,11 @@
 int hostapd_drv_update_dh_ie(struct hostapd_data *hapd, const u8 *peer,
 			     u16 reason_code, const u8 *ie, size_t ielen);
 int hostapd_drv_dpp_listen(struct hostapd_data *hapd, bool enable);
+int hostapd_drv_set_secure_ranging_ctx(struct hostapd_data *hapd,
+				       const u8 *own_addr, const u8 *addr,
+				       u32 cipher, u8 key_len, const u8 *key,
+				       u8 ltf_keyseed_len,
+				       const u8 *ltf_keyseed, u32 action);
 
 
 #include "drivers/driver.h"
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index 29b41f5..ac8f6fa 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -1060,7 +1060,8 @@
 	if (sscanf(pos, "%d", &expiration) != 1)
 		return NULL;
 
-	return wpa_auth_pmksa_create_entry(aa, spa, pmk, pmkid, expiration);
+	return wpa_auth_pmksa_create_entry(aa, spa, pmk, PMK_LEN,
+					   WPA_KEY_MGMT_SAE, pmkid, expiration);
 }
 
 #endif /* CONFIG_MESH */
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
index 146dd1a..7f31f28 100644
--- a/src/ap/dfs.c
+++ b/src/ap/dfs.c
@@ -281,6 +281,10 @@
 		if (chan->max_tx_power < iface->conf->min_tx_power)
 			continue;
 
+		if ((chan->flag & HOSTAPD_CHAN_INDOOR_ONLY) &&
+		    iface->conf->country[2] == 0x4f)
+			continue;
+
 		if (ret_chan && idx == channel_idx) {
 			wpa_printf(MSG_DEBUG, "Selected channel %d (%d)",
 				   chan->freq, chan->chan);
diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c
index 2613c0c..32ddb3b 100644
--- a/src/ap/dpp_hostapd.c
+++ b/src/ap/dpp_hostapd.c
@@ -15,11 +15,13 @@
 #include "common/dpp.h"
 #include "common/gas.h"
 #include "common/wpa_ctrl.h"
+#include "crypto/random.h"
 #include "hostapd.h"
 #include "ap_drv_ops.h"
 #include "gas_query_ap.h"
 #include "gas_serv.h"
 #include "wpa_auth.h"
+#include "beacon.h"
 #include "dpp_hostapd.h"
 
 
@@ -278,19 +280,37 @@
 }
 
 
+static void hostapd_dpp_pkex_clear_code(struct hostapd_data *hapd)
+{
+	if (!hapd->dpp_pkex_code && !hapd->dpp_pkex_identifier)
+		return;
+
+	/* Delete PKEX code and identifier on successful completion of
+	 * PKEX. We are not supposed to reuse these without being
+	 * explicitly requested to perform PKEX again. */
+	wpa_printf(MSG_DEBUG, "DPP: Delete PKEX code/identifier");
+	os_free(hapd->dpp_pkex_code);
+	hapd->dpp_pkex_code = NULL;
+	os_free(hapd->dpp_pkex_identifier);
+	hapd->dpp_pkex_identifier = NULL;
+}
+
+
 #ifdef CONFIG_DPP2
 static int hostapd_dpp_pkex_done(void *ctx, void *conn,
 				 struct dpp_bootstrap_info *peer_bi)
 {
 	struct hostapd_data *hapd = ctx;
-	const char *cmd = hapd->dpp_pkex_auth_cmd;
+	char cmd[500];
 	const char *pos;
 	u8 allowed_roles = DPP_CAPAB_CONFIGURATOR;
 	struct dpp_bootstrap_info *own_bi = NULL;
 	struct dpp_authentication *auth;
 
-	if (!cmd)
-		cmd = "";
+	hostapd_dpp_pkex_clear_code(hapd);
+
+	os_snprintf(cmd, sizeof(cmd), " peer=%u %s", peer_bi->id,
+		    hapd->dpp_pkex_auth_cmd ? hapd->dpp_pkex_auth_cmd : "");
 	wpa_printf(MSG_DEBUG, "DPP: Start authentication after PKEX (cmd: %s)",
 		   cmd);
 
@@ -342,6 +362,9 @@
 
 	return dpp_tcp_auth(hapd->iface->interfaces->dpp, conn, auth,
 			    hapd->conf->dpp_name, DPP_NETROLE_AP,
+			    hapd->conf->dpp_mud_url,
+			    hapd->conf->dpp_extra_conf_req_name,
+			    hapd->conf->dpp_extra_conf_req_value,
 			    hostapd_dpp_process_conf_obj, NULL);
 }
 #endif /* CONFIG_DPP2 */
@@ -362,7 +385,7 @@
 	hapd->dpp_pkex = NULL;
 	pkex = dpp_pkex_init(hapd->msg_ctx, hapd->dpp_pkex_bi, hapd->own_addr,
 			     hapd->dpp_pkex_identifier,
-			     hapd->dpp_pkex_code, v2);
+			     hapd->dpp_pkex_code, hapd->dpp_pkex_code_len, v2);
 	if (!pkex)
 		return -1;
 	pkex->forced_ver = ver != PKEX_VER_AUTO;
@@ -927,7 +950,10 @@
 	if (tcp)
 		return dpp_tcp_init(hapd->iface->interfaces->dpp, auth,
 				    &ipaddr, tcp_port, hapd->conf->dpp_name,
-				    DPP_NETROLE_AP, hapd->msg_ctx, hapd,
+				    DPP_NETROLE_AP, hapd->conf->dpp_mud_url,
+				    hapd->conf->dpp_extra_conf_req_name,
+				    hapd->conf->dpp_extra_conf_req_value,
+				    hapd->msg_ctx, hapd,
 				    hostapd_dpp_process_conf_obj, NULL);
 #endif /* CONFIG_DPP2 */
 
@@ -975,6 +1001,27 @@
 }
 
 
+#ifdef CONFIG_DPP2
+static void
+hostapd_dpp_relay_needs_controller(struct hostapd_data *hapd, const u8 *src,
+				   enum dpp_public_action_frame_type type)
+{
+	struct os_reltime now;
+
+	if (!hapd->conf->dpp_relay_port)
+		return;
+
+	os_get_reltime(&now);
+	if (hapd->dpp_relay_last_needs_ctrl.sec &&
+	    !os_reltime_expired(&now, &hapd->dpp_relay_last_needs_ctrl, 60))
+		return;
+	hapd->dpp_relay_last_needs_ctrl = now;
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_RELAY_NEEDS_CONTROLLER
+		MACSTR " %u", MAC2STR(src), type);
+}
+#endif /* CONFIG_DPP2 */
+
+
 static void hostapd_dpp_rx_auth_req(struct hostapd_data *hapd, const u8 *src,
 				    const u8 *hdr, const u8 *buf, size_t len,
 				    unsigned int freq)
@@ -1023,6 +1070,8 @@
 					src, hdr, buf, len, freq, i_bootstrap,
 					r_bootstrap, hapd) == 0)
 			return;
+		hostapd_dpp_relay_needs_controller(hapd, src,
+						   DPP_PA_AUTHENTICATION_REQ);
 	}
 #endif /* CONFIG_DPP2 */
 	if (!own_bi) {
@@ -1031,6 +1080,21 @@
 		return;
 	}
 
+	if (own_bi->type == DPP_BOOTSTRAP_PKEX) {
+		if (!peer_bi || peer_bi->type != DPP_BOOTSTRAP_PKEX) {
+			wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+				"No matching peer bootstrapping key found for PKEX - ignore message");
+			return;
+		}
+
+		if (os_memcmp(peer_bi->pubkey_hash, own_bi->peer_pubkey_hash,
+			      SHA256_MAC_LEN) != 0) {
+			wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+				"Mismatching peer PKEX bootstrapping key - ignore message");
+			return;
+		}
+	}
+
 	if (hapd->dpp_auth) {
 		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
 			"Already in DPP authentication exchange - ignore new one");
@@ -1286,7 +1350,9 @@
 
 	buf = dpp_build_conf_req_helper(auth, hapd->conf->dpp_name,
 					DPP_NETROLE_AP,
-					hapd->conf->dpp_mud_url, NULL);
+					hapd->conf->dpp_mud_url, NULL,
+					hapd->conf->dpp_extra_conf_req_name,
+					hapd->conf->dpp_extra_conf_req_value);
 	if (!buf) {
 		wpa_printf(MSG_DEBUG,
 			   "DPP: No configuration request data available");
@@ -1450,11 +1516,53 @@
 }
 
 
+#ifdef CONFIG_DPP3
+
+static bool hostapd_dpp_pb_active(struct hostapd_data *hapd)
+{
+	struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+
+	return ifaces && (ifaces->dpp_pb_time.sec ||
+			  ifaces->dpp_pb_time.usec);
+}
+
+
+static void hostapd_dpp_remove_pb_hash(struct hostapd_data *hapd)
+{
+	struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+	int i;
+
+	if (!ifaces->dpp_pb_bi)
+		return;
+	for (i = 0; i < DPP_PB_INFO_COUNT; i++) {
+		struct dpp_pb_info *info = &ifaces->dpp_pb[i];
+
+		if (info->rx_time.sec == 0 && info->rx_time.usec == 0)
+			continue;
+		if (os_memcmp(info->hash, ifaces->dpp_pb_resp_hash,
+			      SHA256_MAC_LEN) == 0) {
+			/* Allow a new push button session to be established
+			 * immediately without the successfully completed
+			 * session triggering session overlap. */
+			info->rx_time.sec = 0;
+			info->rx_time.usec = 0;
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Removed PB hash from session overlap detection due to successfully completed provisioning");
+		}
+	}
+}
+
+#endif /* CONFIG_DPP3 */
+
+
 static void hostapd_dpp_rx_conf_result(struct hostapd_data *hapd, const u8 *src,
 				       const u8 *hdr, const u8 *buf, size_t len)
 {
 	struct dpp_authentication *auth = hapd->dpp_auth;
 	enum dpp_status_error status;
+#ifdef CONFIG_DPP3
+	struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+#endif /* CONFIG_DPP3 */
 
 	wpa_printf(MSG_DEBUG, "DPP: Configuration Result from " MACSTR,
 		   MAC2STR(src));
@@ -1475,7 +1583,8 @@
 
 	if (status == DPP_STATUS_OK && auth->send_conn_status) {
 		wpa_msg(hapd->msg_ctx, MSG_INFO,
-			DPP_EVENT_CONF_SENT "wait_conn_status=1");
+			DPP_EVENT_CONF_SENT "wait_conn_status=1 conf_status=%d",
+			auth->conf_resp_status);
 		wpa_printf(MSG_DEBUG, "DPP: Wait for Connection Status Result");
 		eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout,
 				     hapd, NULL);
@@ -1491,13 +1600,28 @@
 	hostapd_drv_send_action_cancel_wait(hapd);
 	hostapd_dpp_listen_stop(hapd);
 	if (status == DPP_STATUS_OK)
-		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT
+			"conf_status=%d", auth->conf_resp_status);
 	else
 		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
 	dpp_auth_deinit(auth);
 	hapd->dpp_auth = NULL;
 	eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout, hapd,
 			     NULL);
+#ifdef CONFIG_DPP3
+	if (!ifaces->dpp_pb_result_indicated && hostapd_dpp_pb_active(hapd)) {
+		if (status == DPP_STATUS_OK)
+			wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_PB_RESULT
+				"success");
+		else
+			wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_PB_RESULT
+				"no-configuration-available");
+		ifaces->dpp_pb_result_indicated = true;
+		if (status == DPP_STATUS_OK)
+			hostapd_dpp_remove_pb_hash(hapd);
+		hostapd_dpp_push_button_stop(hapd);
+	}
+#endif /* CONFIG_DPP3 */
 }
 
 
@@ -1569,6 +1693,8 @@
 			return;
 		wpa_printf(MSG_DEBUG,
 			   "DPP: No matching bootstrapping information found");
+		hostapd_dpp_relay_needs_controller(
+			hapd, src, DPP_PA_PRESENCE_ANNOUNCEMENT);
 		return;
 	}
 
@@ -1659,6 +1785,8 @@
 			return;
 		wpa_printf(MSG_DEBUG,
 			   "DPP: No matching Configurator information found");
+		hostapd_dpp_relay_needs_controller(
+			hapd, src, DPP_PA_RECONFIG_ANNOUNCEMENT);
 		return;
 	}
 
@@ -1893,6 +2021,25 @@
 }
 
 
+static bool hapd_dpp_connector_available(struct hostapd_data *hapd)
+{
+	if (!hapd->wpa_auth ||
+	    !(hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) ||
+	    !(hapd->conf->wpa & WPA_PROTO_RSN)) {
+		wpa_printf(MSG_DEBUG, "DPP: DPP AKM not in use");
+		return false;
+	}
+
+	if (!hapd->conf->dpp_connector || !hapd->conf->dpp_netaccesskey ||
+	    !hapd->conf->dpp_csign) {
+		wpa_printf(MSG_DEBUG, "DPP: No own Connector/keys set");
+		return false;
+	}
+
+	return true;
+}
+
+
 static void hostapd_dpp_rx_peer_disc_req(struct hostapd_data *hapd,
 					 const u8 *src,
 					 const u8 *buf, size_t len,
@@ -1906,20 +2053,12 @@
 	int expiration;
 	enum dpp_status_error res;
 
+	os_memset(&intro, 0, sizeof(intro));
+
 	wpa_printf(MSG_DEBUG, "DPP: Peer Discovery Request from " MACSTR,
 		   MAC2STR(src));
-	if (!hapd->wpa_auth ||
-	    !(hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) ||
-	    !(hapd->conf->wpa & WPA_PROTO_RSN)) {
-		wpa_printf(MSG_DEBUG, "DPP: DPP AKM not in use");
+	if (!hapd_dpp_connector_available(hapd))
 		return;
-	}
-
-	if (!hapd->conf->dpp_connector || !hapd->conf->dpp_netaccesskey ||
-	    !hapd->conf->dpp_csign) {
-		wpa_printf(MSG_DEBUG, "DPP: No own Connector/keys set");
-		return;
-	}
 
 	os_get_time(&now);
 
@@ -1954,7 +2093,7 @@
 		wpa_printf(MSG_INFO,
 			   "DPP: Network Introduction protocol resulted in internal failure (peer "
 			   MACSTR ")", MAC2STR(src));
-		return;
+		goto done;
 	}
 	if (res != DPP_STATUS_OK) {
 		wpa_printf(MSG_INFO,
@@ -1962,7 +2101,7 @@
 			   MACSTR " status %d)", MAC2STR(src), res);
 		hostapd_dpp_send_peer_disc_resp(hapd, src, freq, trans_id[0],
 						res);
-		return;
+		goto done;
 	}
 
 #ifdef CONFIG_DPP3
@@ -1982,7 +2121,7 @@
 			hostapd_dpp_send_peer_disc_resp(hapd, src, freq,
 							trans_id[0],
 							DPP_STATUS_NO_MATCH);
-			return;
+			goto done;
 		}
 	}
 #endif /* CONFIG_DPP3 */
@@ -1998,11 +2137,13 @@
 				intro.pmkid, expiration,
 				WPA_KEY_MGMT_DPP) < 0) {
 		wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry");
-		return;
+		goto done;
 	}
 
 	hostapd_dpp_send_peer_disc_resp(hapd, src, freq, trans_id[0],
 					DPP_STATUS_OK);
+done:
+	dpp_peer_intro_deinit(&intro);
 }
 
 
@@ -2036,6 +2177,15 @@
 		goto try_relay;
 	}
 
+#ifdef CONFIG_DPP2
+	if (dpp_controller_is_own_pkex_req(hapd->iface->interfaces->dpp,
+					   buf, len)) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: PKEX Exchange Request is from local Controller - ignore request");
+		return;
+	}
+#endif /* CONFIG_DPP2 */
+
 	if (hapd->dpp_pkex) {
 		/* TODO: Support parallel operations */
 		wpa_printf(MSG_DEBUG,
@@ -2048,6 +2198,7 @@
 						  hapd->own_addr, src,
 						  hapd->dpp_pkex_identifier,
 						  hapd->dpp_pkex_code,
+						  hapd->dpp_pkex_code_len,
 						  buf, len, v2);
 	if (!hapd->dpp_pkex) {
 		wpa_printf(MSG_DEBUG,
@@ -2076,9 +2227,12 @@
 #ifdef CONFIG_DPP2
 	if (v2 && dpp_relay_rx_action(hapd->iface->interfaces->dpp,
 				      src, hdr, buf, len, freq, NULL, NULL,
-				      hapd) != 0)
+				      hapd) != 0) {
 		wpa_printf(MSG_DEBUG,
 			   "DPP: No Relay available for the message");
+		hostapd_dpp_relay_needs_controller(hapd, src,
+						   DPP_PA_PKEX_EXCHANGE_REQ);
+	}
 #else /* CONFIG_DPP2 */
 	wpa_printf(MSG_DEBUG, "DPP: No relay functionality included - skip");
 #endif /* CONFIG_DPP2 */
@@ -2165,6 +2319,7 @@
 				wpabuf_head(msg), wpabuf_len(msg));
 	wpabuf_free(msg);
 
+	hostapd_dpp_pkex_clear_code(hapd);
 	bi = dpp_pkex_finish(hapd->iface->interfaces->dpp, pkex, src, freq);
 	if (!bi)
 		return;
@@ -2177,6 +2332,7 @@
 				       const u8 *hdr, const u8 *buf, size_t len,
 				       unsigned int freq)
 {
+	struct hapd_interfaces *ifaces = hapd->iface->interfaces;
 	int res;
 	struct dpp_bootstrap_info *bi;
 	struct dpp_pkex *pkex = hapd->dpp_pkex;
@@ -2196,11 +2352,34 @@
 		return;
 	}
 
-	bi = dpp_pkex_finish(hapd->iface->interfaces->dpp, pkex, src, freq);
+	hostapd_dpp_pkex_clear_code(hapd);
+	bi = dpp_pkex_finish(ifaces->dpp, pkex, src, freq);
 	if (!bi)
 		return;
 	hapd->dpp_pkex = NULL;
 
+#ifdef CONFIG_DPP3
+	if (ifaces->dpp_pb_bi &&
+	    os_memcmp(bi->pubkey_hash_chirp, ifaces->dpp_pb_resp_hash,
+		      SHA256_MAC_LEN) != 0) {
+		char id[20];
+
+		wpa_printf(MSG_INFO,
+			   "DPP: Peer bootstrap key from PKEX does not match PB announcement hash");
+		wpa_hexdump(MSG_DEBUG,
+			    "DPP: Peer provided bootstrap key hash(chirp) from PB PKEX",
+			    bi->pubkey_hash_chirp, SHA256_MAC_LEN);
+		wpa_hexdump(MSG_DEBUG,
+			    "DPP: Peer provided bootstrap key hash(chirp) from PB announcement",
+			    ifaces->dpp_pb_resp_hash, SHA256_MAC_LEN);
+
+		os_snprintf(id, sizeof(id), "%u", bi->id);
+		dpp_bootstrap_remove(ifaces->dpp, id);
+		hostapd_dpp_push_button_stop(hapd);
+		return;
+	}
+#endif /* CONFIG_DPP3 */
+
 	os_snprintf(cmd, sizeof(cmd), " peer=%u %s",
 		    bi->id,
 		    hapd->dpp_pkex_auth_cmd ? hapd->dpp_pkex_auth_cmd : "");
@@ -2215,6 +2394,534 @@
 }
 
 
+#ifdef CONFIG_DPP3
+
+static void hostapd_dpp_pb_pkex_init(struct hostapd_data *hapd,
+				     unsigned int freq, const u8 *src,
+				     const u8 *r_hash)
+{
+	struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+	struct dpp_pkex *pkex;
+	struct wpabuf *msg;
+	char ssid_hex[2 * SSID_MAX_LEN + 1], *pass_hex = NULL;
+	char cmd[300];
+	const char *password = NULL;
+	struct sae_password_entry *e;
+	int conf_id = -1;
+	bool sae = false, psk = false;
+	size_t len;
+
+	if (hapd->dpp_pkex) {
+		wpa_printf(MSG_DEBUG,
+			   "PDP: Sending previously generated PKEX Exchange Request to "
+			   MACSTR, MAC2STR(src));
+		msg = hapd->dpp_pkex->exchange_req;
+		hostapd_drv_send_action(hapd, freq, 0, src,
+					wpabuf_head(msg), wpabuf_len(msg));
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "DPP: Initiate PKEX for push button with "
+		   MACSTR, MAC2STR(src));
+
+	hapd->dpp_pkex_bi = ifaces->dpp_pb_bi;
+	os_memcpy(ifaces->dpp_pb_resp_hash, r_hash, SHA256_MAC_LEN);
+
+	pkex = dpp_pkex_init(hapd->msg_ctx, hapd->dpp_pkex_bi, hapd->own_addr,
+			     "PBPKEX", (const char *) ifaces->dpp_pb_c_nonce,
+			     ifaces->dpp_pb_bi->curve->nonce_len,
+			     true);
+	if (!pkex) {
+		hostapd_dpp_push_button_stop(hapd);
+		return;
+	}
+	pkex->freq = freq;
+
+	hapd->dpp_pkex = pkex;
+	msg = hapd->dpp_pkex->exchange_req;
+
+	if (ifaces->dpp_pb_cmd) {
+		/* Use the externally provided configuration */
+		os_free(hapd->dpp_pkex_auth_cmd);
+		len = 30 + os_strlen(ifaces->dpp_pb_cmd);
+		hapd->dpp_pkex_auth_cmd = os_malloc(len);
+		if (!hapd->dpp_pkex_auth_cmd) {
+			hostapd_dpp_push_button_stop(hapd);
+			return;
+		}
+		os_snprintf(hapd->dpp_pkex_auth_cmd, len, " own=%d %s",
+			    hapd->dpp_pkex_bi->id, ifaces->dpp_pb_cmd);
+		goto send_frame;
+	}
+
+	/* Build config based on the current AP configuration */
+	wpa_snprintf_hex(ssid_hex, sizeof(ssid_hex),
+			 (const u8 *) hapd->conf->ssid.ssid,
+			 hapd->conf->ssid.ssid_len);
+
+	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) {
+		/* TODO: If a local Configurator has been enabled, allow a
+		 * DPP AKM credential to be provisioned by setting conf_id. */
+	}
+
+	if (hapd->conf->wpa & WPA_PROTO_RSN) {
+		psk = hapd->conf->wpa_key_mgmt & (WPA_KEY_MGMT_PSK |
+						  WPA_KEY_MGMT_PSK_SHA256);
+#ifdef CONFIG_SAE
+		sae = hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE;
+#endif /* CONFIG_SAE */
+	}
+
+#ifdef CONFIG_SAE
+	for (e = hapd->conf->sae_passwords; sae && e && !password;
+	     e = e->next) {
+		if (e->identifier || !is_broadcast_ether_addr(e->peer_addr))
+			continue;
+		password = e->password;
+	}
+#endif /* CONFIG_SAE */
+	if (!password && hapd->conf->ssid.wpa_passphrase_set &&
+	    hapd->conf->ssid.wpa_passphrase)
+		password = hapd->conf->ssid.wpa_passphrase;
+	if (password) {
+		len = 2 * os_strlen(password) + 1;
+		pass_hex = os_malloc(len);
+		if (!pass_hex) {
+			hostapd_dpp_push_button_stop(hapd);
+			return;
+		}
+		wpa_snprintf_hex(pass_hex, len, (const u8 *) password,
+				 os_strlen(password));
+	}
+
+	if (conf_id > 0 && sae && psk && pass_hex) {
+		os_snprintf(cmd, sizeof(cmd),
+			    "conf=sta-dpp+psk+sae configurator=%d ssid=%s pass=%s",
+			    conf_id, ssid_hex, pass_hex);
+	} else if (conf_id > 0 && sae && pass_hex) {
+		os_snprintf(cmd, sizeof(cmd),
+			    "conf=sta-dpp+sae configurator=%d ssid=%s pass=%s",
+			    conf_id, ssid_hex, pass_hex);
+	} else if (conf_id > 0) {
+		os_snprintf(cmd, sizeof(cmd),
+			    "conf=sta-dpp configurator=%d ssid=%s",
+			    conf_id, ssid_hex);
+	} if (sae && psk && pass_hex) {
+		os_snprintf(cmd, sizeof(cmd),
+			    "conf=sta-psk+sae ssid=%s pass=%s",
+			    ssid_hex, pass_hex);
+	} else if (sae && pass_hex) {
+		os_snprintf(cmd, sizeof(cmd),
+			    "conf=sta-sae ssid=%s pass=%s",
+			    ssid_hex, pass_hex);
+	} else if (psk && pass_hex) {
+		os_snprintf(cmd, sizeof(cmd),
+			    "conf=sta-psk ssid=%s pass=%s",
+			    ssid_hex, pass_hex);
+	} else {
+		wpa_printf(MSG_INFO,
+			   "DPP: Unsupported AP configuration for push button");
+		str_clear_free(pass_hex);
+		hostapd_dpp_push_button_stop(hapd);
+		return;
+	}
+	str_clear_free(pass_hex);
+
+	os_free(hapd->dpp_pkex_auth_cmd);
+	len = 30 + os_strlen(cmd);
+	hapd->dpp_pkex_auth_cmd = os_malloc(len);
+	if (hapd->dpp_pkex_auth_cmd)
+		os_snprintf(hapd->dpp_pkex_auth_cmd, len, " own=%d %s",
+			    hapd->dpp_pkex_bi->id, cmd);
+	forced_memzero(cmd, sizeof(cmd));
+	if (!hapd->dpp_pkex_auth_cmd) {
+		hostapd_dpp_push_button_stop(hapd);
+		return;
+	}
+
+send_frame:
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+		" freq=%u type=%d", MAC2STR(src), freq,
+		DPP_PA_PKEX_EXCHANGE_REQ);
+	hostapd_drv_send_action(hapd, pkex->freq, 0, src,
+				wpabuf_head(msg), wpabuf_len(msg));
+	pkex->exch_req_wait_time = 2000;
+	pkex->exch_req_tries = 1;
+}
+
+
+static void
+hostapd_dpp_rx_pb_presence_announcement(struct hostapd_data *hapd,
+					const u8 *src, const u8 *hdr,
+					const u8 *buf, size_t len,
+					unsigned int freq)
+{
+	struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+	const u8 *r_hash;
+	u16 r_hash_len;
+	unsigned int i;
+	bool found = false;
+	struct dpp_pb_info *info, *tmp;
+	struct os_reltime now, age;
+	struct wpabuf *msg;
+
+	if (!ifaces)
+		return;
+
+	os_get_reltime(&now);
+	wpa_printf(MSG_DEBUG, "DPP: Push Button Presence Announcement from "
+		   MACSTR, MAC2STR(src));
+
+	r_hash = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+			      &r_hash_len);
+	if (!r_hash || r_hash_len != SHA256_MAC_LEN) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid required Responder Bootstrapping Key Hash attribute");
+		return;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
+		    r_hash, r_hash_len);
+
+	for (i = 0; i < DPP_PB_INFO_COUNT; i++) {
+		info = &ifaces->dpp_pb[i];
+		if ((info->rx_time.sec == 0 && info->rx_time.usec == 0) ||
+		    os_memcmp(r_hash, info->hash, SHA256_MAC_LEN) != 0)
+			continue;
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Active push button Enrollee already known");
+		found = true;
+		info->rx_time = now;
+	}
+
+	if (!found) {
+		for (i = 0; i < DPP_PB_INFO_COUNT; i++) {
+			tmp = &ifaces->dpp_pb[i];
+			if (tmp->rx_time.sec == 0 && tmp->rx_time.usec == 0)
+				continue;
+
+			if (os_reltime_expired(&now, &tmp->rx_time, 120)) {
+				wpa_hexdump(MSG_DEBUG,
+					    "DPP: Push button Enrollee hash expired",
+					    tmp->hash, SHA256_MAC_LEN);
+				tmp->rx_time.sec = 0;
+				tmp->rx_time.usec = 0;
+				continue;
+			}
+
+			wpa_hexdump(MSG_DEBUG,
+				    "DPP: Push button session overlap with hash",
+				    tmp->hash, SHA256_MAC_LEN);
+			if (!ifaces->dpp_pb_result_indicated &&
+			    hostapd_dpp_pb_active(hapd)) {
+				wpa_msg(hapd->msg_ctx, MSG_INFO,
+					DPP_EVENT_PB_RESULT "session-overlap");
+				ifaces->dpp_pb_result_indicated = true;
+			}
+			hostapd_dpp_push_button_stop(hapd);
+			return;
+		}
+
+		/* Replace the oldest entry */
+		info = &ifaces->dpp_pb[0];
+		for (i = 1; i < DPP_PB_INFO_COUNT; i++) {
+			tmp = &ifaces->dpp_pb[i];
+			if (os_reltime_before(&tmp->rx_time, &info->rx_time))
+				info = tmp;
+		}
+		wpa_printf(MSG_DEBUG, "DPP: New active push button Enrollee");
+		os_memcpy(info->hash, r_hash, SHA256_MAC_LEN);
+		info->rx_time = now;
+	}
+
+	if (!hostapd_dpp_pb_active(hapd)) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Discard message since own push button has not been pressed");
+		return;
+	}
+
+	if (ifaces->dpp_pb_announce_time.sec == 0 &&
+	    ifaces->dpp_pb_announce_time.usec == 0) {
+		/* Start a wait before allowing PKEX to be initiated */
+		ifaces->dpp_pb_announce_time = now;
+	}
+
+	if (!ifaces->dpp_pb_bi) {
+		int res;
+
+		res = dpp_bootstrap_gen(ifaces->dpp, "type=pkex");
+		if (res < 0)
+			return;
+		ifaces->dpp_pb_bi = dpp_bootstrap_get_id(ifaces->dpp, res);
+		if (!ifaces->dpp_pb_bi)
+			return;
+
+		if (random_get_bytes(ifaces->dpp_pb_c_nonce,
+				     ifaces->dpp_pb_bi->curve->nonce_len)) {
+			wpa_printf(MSG_ERROR,
+				   "DPP: Failed to generate C-nonce");
+			hostapd_dpp_push_button_stop(hapd);
+			return;
+		}
+	}
+
+	/* Skip the response if one was sent within last 50 ms since the
+	 * Enrollee is going to send out at least three announcement messages.
+	 */
+	os_reltime_sub(&now, &ifaces->dpp_pb_last_resp, &age);
+	if (age.sec == 0 && age.usec < 50000) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Skip Push Button Presence Announcement Response frame immediately after having sent one");
+		return;
+	}
+
+	msg = dpp_build_pb_announcement_resp(
+		ifaces->dpp_pb_bi, r_hash, ifaces->dpp_pb_c_nonce,
+		ifaces->dpp_pb_bi->curve->nonce_len);
+	if (!msg) {
+		hostapd_dpp_push_button_stop(hapd);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Send Push Button Presence Announcement Response to "
+		   MACSTR, MAC2STR(src));
+	ifaces->dpp_pb_last_resp = now;
+
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+		" freq=%u type=%d", MAC2STR(src), freq,
+		DPP_PA_PB_PRESENCE_ANNOUNCEMENT_RESP);
+	hostapd_drv_send_action(hapd, freq, 0, src,
+				wpabuf_head(msg), wpabuf_len(msg));
+	wpabuf_free(msg);
+
+	if (os_reltime_expired(&now, &ifaces->dpp_pb_announce_time, 15))
+		hostapd_dpp_pb_pkex_init(hapd, freq, src, r_hash);
+}
+
+
+static void
+hostapd_dpp_rx_priv_peer_intro_query(struct hostapd_data *hapd, const u8 *src,
+				     const u8 *hdr, const u8 *buf, size_t len,
+				     unsigned int freq)
+{
+	const u8 *trans_id, *version;
+	u16 trans_id_len, version_len;
+	struct wpabuf *msg;
+	u8 ver = DPP_VERSION;
+	int conn_ver;
+
+	wpa_printf(MSG_DEBUG, "DPP: Private Peer Introduction Query from "
+		   MACSTR, MAC2STR(src));
+
+	if (!hapd_dpp_connector_available(hapd))
+		return;
+
+	trans_id = dpp_get_attr(buf, len, DPP_ATTR_TRANSACTION_ID,
+			       &trans_id_len);
+	if (!trans_id || trans_id_len != 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Peer did not include Transaction ID");
+		return;
+	}
+
+	version = dpp_get_attr(buf, len, DPP_ATTR_PROTOCOL_VERSION,
+			       &version_len);
+	if (!version || version_len != 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Peer did not include Protocol Version");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "DPP: Transaction ID %u, Version %u",
+		   trans_id[0], version[0]);
+
+	len = 5 + 5 + 4 + os_strlen(hapd->conf->dpp_connector);
+	msg = dpp_alloc_msg(DPP_PA_PRIV_PEER_INTRO_NOTIFY, len);
+	if (!msg)
+		return;
+
+	/* Transaction ID */
+	wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID);
+	wpabuf_put_le16(msg, 1);
+	wpabuf_put_u8(msg, trans_id[0]);
+
+	/* Protocol Version */
+	conn_ver = dpp_get_connector_version(hapd->conf->dpp_connector);
+	if (conn_ver > 0 && ver != conn_ver) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Use Connector version %d instead of current protocol version %d",
+			   conn_ver, ver);
+		ver = conn_ver;
+	}
+	wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
+	wpabuf_put_le16(msg, 1);
+	wpabuf_put_u8(msg, ver);
+
+	/* DPP Connector */
+	wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR);
+	wpabuf_put_le16(msg, os_strlen(hapd->conf->dpp_connector));
+	wpabuf_put_str(msg, hapd->conf->dpp_connector);
+
+	wpa_printf(MSG_DEBUG, "DPP: Send Private Peer Introduction Notify to "
+		   MACSTR, MAC2STR(src));
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+		" freq=%u type=%d", MAC2STR(src), freq,
+		DPP_PA_PRIV_PEER_INTRO_NOTIFY);
+	hostapd_drv_send_action(hapd, freq, 0, src,
+				wpabuf_head(msg), wpabuf_len(msg));
+	wpabuf_free(msg);
+}
+
+
+static void
+hostapd_dpp_rx_priv_peer_intro_update(struct hostapd_data *hapd, const u8 *src,
+				      const u8 *hdr, const u8 *buf, size_t len,
+				      unsigned int freq)
+{
+	struct crypto_ec_key *own_key;
+	const struct dpp_curve_params *curve;
+	enum hpke_kem_id kem_id;
+	enum hpke_kdf_id kdf_id;
+	enum hpke_aead_id aead_id;
+	const u8 *aad = hdr;
+	size_t aad_len = DPP_HDR_LEN;
+	struct wpabuf *pt;
+	const u8 *trans_id, *wrapped, *version, *connector;
+	u16 trans_id_len, wrapped_len, version_len, connector_len;
+	struct os_time now;
+	struct dpp_introduction intro;
+	os_time_t expire;
+	int expiration;
+	enum dpp_status_error res;
+
+	os_memset(&intro, 0, sizeof(intro));
+
+	wpa_printf(MSG_DEBUG, "DPP: Private Peer Introduction Update from "
+		   MACSTR, MAC2STR(src));
+
+	if (!hapd_dpp_connector_available(hapd))
+		return;
+
+	os_get_time(&now);
+
+	if (hapd->conf->dpp_netaccesskey_expiry &&
+	    (os_time_t) hapd->conf->dpp_netaccesskey_expiry < now.sec) {
+		wpa_printf(MSG_INFO, "DPP: Own netAccessKey expired");
+		return;
+	}
+
+	trans_id = dpp_get_attr(buf, len, DPP_ATTR_TRANSACTION_ID,
+			       &trans_id_len);
+	if (!trans_id || trans_id_len != 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Peer did not include Transaction ID");
+		return;
+	}
+
+	wrapped = dpp_get_attr(buf, len, DPP_ATTR_WRAPPED_DATA,
+			       &wrapped_len);
+	if (!wrapped) {
+		wpa_printf(MSG_DEBUG, "DPP: Peer did not include Wrapped Data");
+		return;
+	}
+
+	own_key = dpp_set_keypair(&curve,
+				  wpabuf_head(hapd->conf->dpp_netaccesskey),
+				  wpabuf_len(hapd->conf->dpp_netaccesskey));
+	if (!own_key) {
+		wpa_printf(MSG_ERROR, "DPP: Failed to parse own netAccessKey");
+		return;
+	}
+
+	if (dpp_hpke_suite(curve->ike_group, &kem_id, &kdf_id, &aead_id) < 0) {
+		wpa_printf(MSG_ERROR, "DPP: Unsupported curve %d",
+			   curve->ike_group);
+		crypto_ec_key_deinit(own_key);
+		return;
+	}
+
+	pt = hpke_base_open(kem_id, kdf_id, aead_id, own_key, NULL, 0,
+			    aad, aad_len, wrapped, wrapped_len);
+	crypto_ec_key_deinit(own_key);
+	if (!pt) {
+		wpa_printf(MSG_INFO, "DPP: Failed to decrypt Connector");
+		return;
+	}
+	wpa_hexdump_buf(MSG_MSGDUMP, "DPP: HPKE-Decrypted Wrapped Data", pt);
+
+	connector = dpp_get_attr(wpabuf_head(pt), wpabuf_len(pt),
+				 DPP_ATTR_CONNECTOR, &connector_len);
+	if (!connector) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Peer did not include its Connector");
+		goto done;
+	}
+
+	version = dpp_get_attr(wpabuf_head(pt), wpabuf_len(pt),
+			       DPP_ATTR_PROTOCOL_VERSION, &version_len);
+	if (!version || version_len < 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Peer did not include Protocol Version");
+		goto done;
+	}
+
+	res = dpp_peer_intro(&intro, hapd->conf->dpp_connector,
+			     wpabuf_head(hapd->conf->dpp_netaccesskey),
+			     wpabuf_len(hapd->conf->dpp_netaccesskey),
+			     wpabuf_head(hapd->conf->dpp_csign),
+			     wpabuf_len(hapd->conf->dpp_csign),
+			     connector, connector_len, &expire);
+	if (res == 255) {
+		wpa_printf(MSG_INFO,
+			   "DPP: Network Introduction protocol resulted in internal failure (peer "
+			   MACSTR ")", MAC2STR(src));
+		goto done;
+	}
+	if (res != DPP_STATUS_OK) {
+		wpa_printf(MSG_INFO,
+			   "DPP: Network Introduction protocol resulted in failure (peer "
+			   MACSTR " status %d)", MAC2STR(src), res);
+		goto done;
+	}
+
+	if (intro.peer_version && intro.peer_version >= 2) {
+		u8 attr_version = 1;
+
+		if (version && version_len >= 1)
+			attr_version = version[0];
+		if (attr_version != intro.peer_version) {
+			wpa_printf(MSG_INFO,
+				   "DPP: Protocol version mismatch (Connector: %d Attribute: %d",
+				   intro.peer_version, attr_version);
+			goto done;
+		}
+	}
+
+	if (!expire || (os_time_t) hapd->conf->dpp_netaccesskey_expiry < expire)
+		expire = hapd->conf->dpp_netaccesskey_expiry;
+	if (expire)
+		expiration = expire - now.sec;
+	else
+		expiration = 0;
+
+	if (wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
+				intro.pmkid, expiration,
+				WPA_KEY_MGMT_DPP) < 0) {
+		wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry");
+		goto done;
+	}
+
+	wpa_printf(MSG_DEBUG, "DPP: Private Peer Introduction completed with "
+		   MACSTR, MAC2STR(src));
+
+done:
+	dpp_peer_intro_deinit(&intro);
+	wpabuf_free(pt);
+}
+
+#endif /* CONFIG_DPP3 */
+
+
 void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src,
 			   const u8 *buf, size_t len, unsigned int freq)
 {
@@ -2320,6 +3027,20 @@
 						  freq);
 		break;
 #endif /* CONFIG_DPP2 */
+#ifdef CONFIG_DPP3
+	case DPP_PA_PB_PRESENCE_ANNOUNCEMENT:
+		hostapd_dpp_rx_pb_presence_announcement(hapd, src, hdr,
+							buf, len, freq);
+		break;
+	case DPP_PA_PRIV_PEER_INTRO_QUERY:
+		hostapd_dpp_rx_priv_peer_intro_query(hapd, src, hdr,
+						     buf, len, freq);
+		break;
+	case DPP_PA_PRIV_PEER_INTRO_UPDATE:
+		hostapd_dpp_rx_priv_peer_intro_update(hapd, src, hdr,
+						      buf, len, freq);
+		break;
+#endif /* CONFIG_DPP3 */
 	default:
 		wpa_printf(MSG_DEBUG,
 			   "DPP: Ignored unsupported frame subtype %d", type);
@@ -2388,6 +3109,9 @@
 void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok)
 {
 	struct dpp_authentication *auth = hapd->dpp_auth;
+#ifdef CONFIG_DPP3
+	struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+#endif /* CONFIG_DPP3 */
 
 	if (!auth)
 		return;
@@ -2422,11 +3146,26 @@
 	hostapd_drv_send_action_cancel_wait(hapd);
 
 	if (ok)
-		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT
+			"conf_status=%d", auth->conf_resp_status);
 	else
 		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
 	dpp_auth_deinit(hapd->dpp_auth);
 	hapd->dpp_auth = NULL;
+#ifdef CONFIG_DPP3
+	if (!ifaces->dpp_pb_result_indicated && hostapd_dpp_pb_active(hapd)) {
+		if (ok)
+			wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_PB_RESULT
+				"success");
+		else
+			wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_PB_RESULT
+				"could-not-connect");
+		ifaces->dpp_pb_result_indicated = true;
+		if (ok)
+			hostapd_dpp_remove_pb_hash(hapd);
+		hostapd_dpp_push_button_stop(hapd);
+	}
+#endif /* CONFIG_DPP3 */
 }
 
 
@@ -2528,6 +3267,7 @@
 	hapd->dpp_pkex_code = os_strdup(pos + 6);
 	if (!hapd->dpp_pkex_code)
 		return -1;
+	hapd->dpp_pkex_code_len = os_strlen(hapd->dpp_pkex_code);
 
 	pos = os_strstr(cmd, " ver=");
 	if (pos) {
@@ -2576,7 +3316,7 @@
 			return -1;
 	}
 
-	if ((id_val != 0 && id_val != 1) || !hapd->dpp_pkex_code)
+	if ((id_val != 0 && id_val != 1))
 		return -1;
 
 	/* TODO: Support multiple PKEX entries */
@@ -2600,6 +3340,9 @@
 	hapd->dpp_auth = NULL;
 	dpp_pkex_free(hapd->dpp_pkex);
 	hapd->dpp_pkex = NULL;
+#ifdef CONFIG_DPP3
+	hostapd_dpp_push_button_stop(hapd);
+#endif /* CONFIG_DPP3 */
 }
 
 
@@ -2611,6 +3354,9 @@
 	struct hostapd_data *hapd = ctx;
 	u8 *buf;
 
+	if (freq == 0)
+		freq = hapd->iface->freq;
+
 	wpa_printf(MSG_DEBUG, "DPP: Send action frame dst=" MACSTR " freq=%u",
 		   MAC2STR(addr), freq);
 	buf = os_malloc(2 + len);
@@ -2643,6 +3389,7 @@
 	struct dpp_relay_config config;
 
 	os_memset(&config, 0, sizeof(config));
+	config.msg_ctx = hapd->msg_ctx;
 	config.cb_ctx = hapd;
 	config.tx = hostapd_dpp_relay_tx;
 	config.gas_resp_tx = hostapd_dpp_relay_gas_resp_tx;
@@ -2653,12 +3400,76 @@
 					     &config) < 0)
 			return -1;
 	}
+
+	if (hapd->conf->dpp_relay_port)
+		dpp_relay_listen(hapd->iface->interfaces->dpp,
+				 hapd->conf->dpp_relay_port,
+				 &config);
 #endif /* CONFIG_DPP2 */
 
 	return 0;
 }
 
 
+#ifdef CONFIG_DPP2
+
+int hostapd_dpp_add_controller(struct hostapd_data *hapd, const char *cmd)
+{
+	struct dpp_relay_config config;
+	struct hostapd_ip_addr addr;
+	u8 pkhash[SHA256_MAC_LEN];
+	char *pos, *tmp;
+	int ret = -1;
+	bool prev_state, new_state;
+	struct dpp_global *dpp = hapd->iface->interfaces->dpp;
+
+	tmp = os_strdup(cmd);
+	if (!tmp)
+		goto fail;
+	pos = os_strchr(tmp, ' ');
+	if (!pos)
+		goto fail;
+	*pos++ = '\0';
+	if (hostapd_parse_ip_addr(tmp, &addr) < 0 ||
+	    hexstr2bin(pos, pkhash, SHA256_MAC_LEN) < 0)
+		goto fail;
+
+	os_memset(&config, 0, sizeof(config));
+	config.msg_ctx = hapd->msg_ctx;
+	config.cb_ctx = hapd;
+	config.tx = hostapd_dpp_relay_tx;
+	config.gas_resp_tx = hostapd_dpp_relay_gas_resp_tx;
+	config.ipaddr = &addr;
+	config.pkhash = pkhash;
+	prev_state = dpp_relay_controller_available(dpp);
+	ret = dpp_relay_add_controller(dpp, &config);
+	new_state = dpp_relay_controller_available(dpp);
+	if (new_state != prev_state)
+		ieee802_11_update_beacons(hapd->iface);
+fail:
+	os_free(tmp);
+	return ret;
+}
+
+
+void hostapd_dpp_remove_controller(struct hostapd_data *hapd, const char *cmd)
+{
+	struct hostapd_ip_addr addr;
+	bool prev_state, new_state;
+	struct dpp_global *dpp = hapd->iface->interfaces->dpp;
+
+	if (hostapd_parse_ip_addr(cmd, &addr) < 0)
+		return;
+	prev_state = dpp_relay_controller_available(dpp);
+	dpp_relay_remove_controller(dpp, &addr);
+	new_state = dpp_relay_controller_available(dpp);
+	if (new_state != prev_state)
+		ieee802_11_update_beacons(hapd->iface);
+}
+
+#endif /* CONFIG_DPP2 */
+
+
 int hostapd_dpp_init(struct hostapd_data *hapd)
 {
 	hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE;
@@ -2693,11 +3504,14 @@
 	eloop_cancel_timeout(hostapd_dpp_conn_status_result_wait_timeout, hapd,
 			     NULL);
 	hostapd_dpp_chirp_stop(hapd);
-	if (hapd->iface->interfaces)
+	if (hapd->iface->interfaces) {
+		dpp_relay_stop_listen(hapd->iface->interfaces->dpp);
 		dpp_controller_stop_for_ctx(hapd->iface->interfaces->dpp, hapd);
+	}
 #endif /* CONFIG_DPP2 */
 #ifdef CONFIG_DPP3
 	eloop_cancel_timeout(hostapd_dpp_build_new_key, hapd, NULL);
+	hostapd_dpp_push_button_stop(hapd);
 #endif /* CONFIG_DPP3 */
 	dpp_auth_deinit(hapd->dpp_auth);
 	hapd->dpp_auth = NULL;
@@ -2705,6 +3519,8 @@
 	hapd->dpp_pkex = NULL;
 	os_free(hapd->dpp_configurator_params);
 	hapd->dpp_configurator_params = NULL;
+	os_free(hapd->dpp_pkex_auth_cmd);
+	hapd->dpp_pkex_auth_cmd = NULL;
 }
 
 
@@ -3077,3 +3893,84 @@
 }
 
 #endif /* CONFIG_DPP2 */
+
+
+#ifdef CONFIG_DPP3
+
+static void hostapd_dpp_push_button_expire(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+
+	wpa_printf(MSG_DEBUG, "DPP: Active push button mode expired");
+	hostapd_dpp_push_button_stop(hapd);
+}
+
+
+int hostapd_dpp_push_button(struct hostapd_data *hapd, const char *cmd)
+{
+	struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+
+	if (!ifaces || !ifaces->dpp)
+		return -1;
+	os_get_reltime(&ifaces->dpp_pb_time);
+	ifaces->dpp_pb_announce_time.sec = 0;
+	ifaces->dpp_pb_announce_time.usec = 0;
+	str_clear_free(ifaces->dpp_pb_cmd);
+	ifaces->dpp_pb_cmd = NULL;
+	if (cmd) {
+		ifaces->dpp_pb_cmd = os_strdup(cmd);
+		if (!ifaces->dpp_pb_cmd)
+			return -1;
+	}
+	eloop_register_timeout(100, 0, hostapd_dpp_push_button_expire,
+			       hapd, NULL);
+
+	return 0;
+}
+
+
+void hostapd_dpp_push_button_stop(struct hostapd_data *hapd)
+{
+	struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+
+	if (!ifaces || !ifaces->dpp)
+		return;
+	eloop_cancel_timeout(hostapd_dpp_push_button_expire, hapd, NULL);
+	if (hostapd_dpp_pb_active(hapd)) {
+		wpa_printf(MSG_DEBUG, "DPP: Stop active push button mode");
+		if (!ifaces->dpp_pb_result_indicated)
+			wpa_msg(hapd->msg_ctx, MSG_INFO,
+				DPP_EVENT_PB_RESULT "failed");
+	}
+	ifaces->dpp_pb_time.sec = 0;
+	ifaces->dpp_pb_time.usec = 0;
+	dpp_pkex_free(hapd->dpp_pkex);
+	hapd->dpp_pkex = NULL;
+	os_free(hapd->dpp_pkex_auth_cmd);
+	hapd->dpp_pkex_auth_cmd = NULL;
+
+	if (ifaces->dpp_pb_bi) {
+		char id[20];
+
+		os_snprintf(id, sizeof(id), "%u", ifaces->dpp_pb_bi->id);
+		dpp_bootstrap_remove(ifaces->dpp, id);
+		ifaces->dpp_pb_bi = NULL;
+	}
+
+	ifaces->dpp_pb_result_indicated = false;
+
+	str_clear_free(ifaces->dpp_pb_cmd);
+	ifaces->dpp_pb_cmd = NULL;
+}
+
+#endif /* CONFIG_DPP3 */
+
+
+#ifdef CONFIG_DPP2
+bool hostapd_dpp_configurator_connectivity(struct hostapd_data *hapd)
+{
+	return hapd->conf->dpp_configurator_connectivity ||
+		(hapd->iface->interfaces &&
+		 dpp_relay_controller_available(hapd->iface->interfaces->dpp));
+}
+#endif /* CONFIG_DPP2 */
diff --git a/src/ap/dpp_hostapd.h b/src/ap/dpp_hostapd.h
index 264d3e4..55f1fce 100644
--- a/src/ap/dpp_hostapd.h
+++ b/src/ap/dpp_hostapd.h
@@ -45,5 +45,10 @@
 int hostapd_dpp_chirp(struct hostapd_data *hapd, const char *cmd);
 void hostapd_dpp_chirp_stop(struct hostapd_data *hapd);
 void hostapd_dpp_remove_bi(void *ctx, struct dpp_bootstrap_info *bi);
+int hostapd_dpp_push_button(struct hostapd_data *hapd, const char *cmd);
+void hostapd_dpp_push_button_stop(struct hostapd_data *hapd);
+bool hostapd_dpp_configurator_connectivity(struct hostapd_data *hapd);
+int hostapd_dpp_add_controller(struct hostapd_data *hapd, const char *cmd);
+void hostapd_dpp_remove_controller(struct hostapd_data *hapd, const char *cmd);
 
 #endif /* DPP_HOSTAPD_H */
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 05faa0f..3681df6 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -1238,7 +1238,8 @@
 	 * Short SSID calculation is identical to FCS and it is defined in
 	 * IEEE P802.11-REVmd/D3.0, 9.4.2.170.3 (Calculating the Short-SSID).
 	 */
-	conf->ssid.short_ssid = crc32(conf->ssid.ssid, conf->ssid.ssid_len);
+	conf->ssid.short_ssid = ieee80211_crc32(conf->ssid.ssid,
+						conf->ssid.ssid_len);
 
 	if (!hostapd_drv_none(hapd)) {
 		wpa_printf(MSG_DEBUG, "Using interface %s with hwaddr " MACSTR
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 15d59dc..e121362 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -76,6 +76,17 @@
 
 #ifdef CONFIG_DPP
 	struct dpp_global *dpp;
+#ifdef CONFIG_DPP3
+	struct os_reltime dpp_pb_time;
+	struct os_reltime dpp_pb_announce_time;
+	struct dpp_pb_info dpp_pb[DPP_PB_INFO_COUNT];
+	struct dpp_bootstrap_info *dpp_pb_bi;
+	u8 dpp_pb_c_nonce[DPP_MAX_NONCE_LEN];
+	u8 dpp_pb_resp_hash[SHA256_MAC_LEN];
+	struct os_reltime dpp_pb_last_resp;
+	bool dpp_pb_result_indicated;
+	char *dpp_pb_cmd;
+#endif /* CONFIG_DPP3 */
 #endif /* CONFIG_DPP */
 
 #ifdef CONFIG_CTRL_IFACE_UDP
@@ -401,6 +412,7 @@
 	struct dpp_pkex *dpp_pkex;
 	struct dpp_bootstrap_info *dpp_pkex_bi;
 	char *dpp_pkex_code;
+	size_t dpp_pkex_code_len;
 	char *dpp_pkex_identifier;
 	enum dpp_pkex_ver dpp_pkex_ver;
 	char *dpp_pkex_auth_cmd;
@@ -421,6 +433,7 @@
 	int dpp_chirp_round;
 	int dpp_chirp_scan_done;
 	int dpp_chirp_listen;
+	struct os_reltime dpp_relay_last_needs_ctrl;
 #endif /* CONFIG_DPP2 */
 #ifdef CONFIG_TESTING_OPTIONS
 	char *dpp_config_obj_override;
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 8806a58..a65a296 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -982,7 +982,8 @@
 	sta->sae->peer_commit_scalar_accepted = sta->sae->peer_commit_scalar;
 	sta->sae->peer_commit_scalar = NULL;
 	wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr,
-			       sta->sae->pmk, sta->sae->pmkid);
+			       sta->sae->pmk, sta->sae->pmk_len,
+			       sta->sae->pmkid, sta->sae->akmp);
 	sae_sme_send_external_auth_status(hapd, sta, WLAN_STATUS_SUCCESS);
 }
 
@@ -1233,6 +1234,10 @@
 	if (sae_pwe == 0 && sae_pk)
 		sae_pwe = 2;
 #endif /* CONFIG_SAE_PK */
+	if (sae_pwe == 0 &&
+	    (hapd->conf->wpa_key_mgmt &
+	     (WPA_KEY_MGMT_SAE_EXT_KEY | WPA_KEY_MGMT_FT_SAE_EXT_KEY)))
+		sae_pwe = 2;
 
 	return ((sae_pwe == 0 || sae_pwe == 3) &&
 		status_code == WLAN_STATUS_SUCCESS) ||
@@ -2514,7 +2519,8 @@
 	 * restrict this only for PASN.
 	 */
 	wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr,
-			       pasn->sae.pmk, pasn->sae.pmkid);
+			       pasn->sae.pmk, pasn->sae.pmk_len,
+			       pasn->sae.pmkid, pasn->sae.akmp);
 	return 0;
 }
 
@@ -2661,6 +2667,15 @@
 		goto fail;
 	}
 
+	if (pasn->secure_ltf) {
+		ret = wpa_ltf_keyseed(&pasn->ptk, pasn->akmp, pasn->cipher);
+		if (ret) {
+			wpa_printf(MSG_DEBUG,
+				   "PASN: FILS: Failed to derive LTF keyseed");
+			goto fail;
+		}
+	}
+
 	wpa_printf(MSG_DEBUG, "PASN: PTK successfully derived");
 
 	wpabuf_free(pasn->secret);
@@ -2842,6 +2857,38 @@
 }
 
 
+static int pasn_set_keys_from_cache(struct hostapd_data *hapd,
+				    const u8 *own_addr, const u8 *sta_addr,
+				    int cipher, int akmp)
+{
+	struct ptksa_cache_entry *entry;
+
+	entry = ptksa_cache_get(hapd->ptksa, sta_addr, cipher);
+	if (!entry) {
+		wpa_printf(MSG_DEBUG, "PASN: peer " MACSTR
+			   " not present in PTKSA cache", MAC2STR(sta_addr));
+		return -1;
+	}
+
+	if (os_memcmp(entry->own_addr, own_addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: own addr " MACSTR " and PTKSA entry own addr "
+			   MACSTR " differ",
+			   MAC2STR(own_addr), MAC2STR(entry->own_addr));
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "PASN: " MACSTR " present in PTKSA cache",
+		   MAC2STR(sta_addr));
+	hostapd_drv_set_secure_ranging_ctx(hapd, own_addr, sta_addr, cipher,
+					   entry->ptk.tk_len, entry->ptk.tk,
+					   entry->ptk.ltf_keyseed_len,
+					   entry->ptk.ltf_keyseed, 0);
+
+	return 0;
+}
+
+
 static int
 pasn_derive_keys(struct hostapd_data *hapd, struct sta_info *sta,
 		 const u8 *cached_pmk, size_t cached_pmk_len,
@@ -2898,6 +2945,16 @@
 		return -1;
 	}
 
+	if (sta->pasn->secure_ltf) {
+		ret = wpa_ltf_keyseed(&sta->pasn->ptk, sta->pasn->akmp,
+				      sta->pasn->cipher);
+		if (ret) {
+			wpa_printf(MSG_DEBUG,
+				   "PASN: Failed to derive LTF keyseed");
+			return -1;
+		}
+	}
+
 	wpa_printf(MSG_DEBUG, "PASN: PTK successfully derived");
 	return 0;
 }
@@ -3161,7 +3218,7 @@
 	sta->pasn->akmp = rsn_data.key_mgmt;
 	sta->pasn->cipher = rsn_data.pairwise_cipher;
 
-	derive_kdk = (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF) &&
+	derive_kdk = (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_AP) &&
 		ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len,
 					  WLAN_RSNX_CAPAB_SECURE_LTF);
 #ifdef CONFIG_TESTING_OPTIONS
@@ -3174,6 +3231,13 @@
 		sta->pasn->kdk_len = 0;
 	wpa_printf(MSG_DEBUG, "PASN: kdk_len=%zu", sta->pasn->kdk_len);
 
+	if ((hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_AP) &&
+	    ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len,
+				      WLAN_RSNX_CAPAB_SECURE_LTF))
+		sta->pasn->secure_ltf = true;
+	else
+		sta->pasn->secure_ltf = false;
+
 	if (!elems.pasn_params || !elems.pasn_params_len) {
 		wpa_printf(MSG_DEBUG,
 			   "PASN: No PASN Parameters element found");
@@ -3496,8 +3560,10 @@
 	wpa_printf(MSG_INFO,
 		   "PASN: Success handling transaction == 3. Store PTK");
 
-	ptksa_cache_add(hapd->ptksa, sta->addr, sta->pasn->cipher, 43200,
-			&sta->pasn->ptk);
+	ptksa_cache_add(hapd->ptksa, hapd->own_addr, sta->addr,
+			sta->pasn->cipher, 43200, &sta->pasn->ptk, NULL, NULL);
+	pasn_set_keys_from_cache(hapd, hapd->own_addr, sta->addr,
+				 sta->pasn->cipher, sta->pasn->akmp);
 fail:
 	ap_free_sta(hapd, sta);
 }
@@ -4686,7 +4752,7 @@
 		    sta->auth_alg == WLAN_AUTH_OPEN) {
 			struct rsn_pmksa_cache_entry *sa;
 			sa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
-			if (!sa || sa->akmp != WPA_KEY_MGMT_SAE) {
+			if (!sa || !wpa_key_mgmt_sae(sa->akmp)) {
 				wpa_printf(MSG_DEBUG,
 					   "SAE: No PMKSA cache entry found for "
 					   MACSTR, MAC2STR(sta->addr));
@@ -7450,7 +7516,7 @@
 				break;
 
 			*eid++ = RNR_NEIGHBOR_AP_OFFSET_UNKNOWN;
-			os_memcpy(eid, bss->conf->bssid, ETH_ALEN);
+			os_memcpy(eid, bss->own_addr, ETH_ALEN);
 			eid += ETH_ALEN;
 			os_memcpy(eid, &bss->conf->ssid.short_ssid, 4);
 			eid += 4;
diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
index b5b7e5d..12273c3 100644
--- a/src/ap/ieee802_11_he.c
+++ b/src/ap/ieee802_11_he.c
@@ -533,7 +533,8 @@
 	u8 *mac_cap;
 
 	if (!hapd->iface->current_mode ||
-	    !hapd->iface->current_mode->he_capab[mode].he_supported)
+	    !hapd->iface->current_mode->he_capab[mode].he_supported ||
+	    !hapd->iconf->ieee80211ax || hapd->conf->disable_11ax)
 		return 0;
 
 	mac_cap = hapd->iface->current_mode->he_capab[mode].mac_cap;
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index 4e7c33e..eaeaec5 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -17,6 +17,7 @@
 #include "ap_config.h"
 #include "ap_drv_ops.h"
 #include "wpa_auth.h"
+#include "dpp_hostapd.h"
 #include "ieee802_11.h"
 
 
@@ -412,8 +413,7 @@
 			*pos |= 0x01;
 #endif /* CONFIG_FILS */
 #ifdef CONFIG_IEEE80211AX
-		if (hapd->iconf->ieee80211ax &&
-		    hostapd_get_he_twt_responder(hapd, IEEE80211_MODE_AP))
+		if (hostapd_get_he_twt_responder(hapd, IEEE80211_MODE_AP))
 			*pos |= 0x40; /* Bit 78 - TWT responder */
 #endif /* CONFIG_IEEE80211AX */
 		break;
@@ -873,7 +873,7 @@
 size_t hostapd_eid_dpp_cc_len(struct hostapd_data *hapd)
 {
 #ifdef CONFIG_DPP2
-	if (hapd->conf->dpp_configurator_connectivity)
+	if (hostapd_dpp_configurator_connectivity(hapd))
 		return 6;
 #endif /* CONFIG_DPP2 */
 	return 0;
@@ -885,7 +885,7 @@
 	u8 *pos = eid;
 
 #ifdef CONFIG_DPP2
-	if (!hapd->conf->dpp_configurator_connectivity || len < 6)
+	if (!hostapd_dpp_configurator_connectivity(hapd) || len < 6)
 		return pos;
 
 	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
@@ -1063,7 +1063,8 @@
 
 	if (wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
 	    (hapd->conf->sae_pwe == 1 || hapd->conf->sae_pwe == 2 ||
-	     hostapd_sae_pw_id_in_use(hapd->conf) || sae_pk) &&
+	     hostapd_sae_pw_id_in_use(hapd->conf) || sae_pk ||
+	     wpa_key_mgmt_sae_ext_key(hapd->conf->wpa_key_mgmt)) &&
 	    hapd->conf->sae_pwe != 3) {
 		capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
 #ifdef CONFIG_SAE_PK
@@ -1072,11 +1073,11 @@
 #endif /* CONFIG_SAE_PK */
 	}
 
-	if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF)
+	if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_AP)
 		capab |= BIT(WLAN_RSNX_CAPAB_SECURE_LTF);
-	if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_RTT)
+	if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_RTT_AP)
 		capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT);
-	if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG)
+	if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP)
 		capab |= BIT(WLAN_RSNX_CAPAB_PROT_RANGE_NEG);
 
 	flen = (capab & 0xff00) ? 2 : 1;
diff --git a/src/ap/neighbor_db.c b/src/ap/neighbor_db.c
index 52f25eb..5b276e8 100644
--- a/src/ap/neighbor_db.c
+++ b/src/ap/neighbor_db.c
@@ -136,7 +136,7 @@
 
 	os_memcpy(entry->bssid, bssid, ETH_ALEN);
 	os_memcpy(&entry->ssid, ssid, sizeof(entry->ssid));
-	entry->short_ssid = crc32(ssid->ssid, ssid->ssid_len);
+	entry->short_ssid = ieee80211_crc32(ssid->ssid, ssid->ssid_len);
 
 	entry->nr = wpabuf_dup(nr);
 	if (!entry->nr)
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 5c92e01..d2a8344 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -88,6 +88,7 @@
 	int akmp;
 	int cipher;
 	u16 group;
+	bool secure_ltf;
 	u8 trans_seq;
 	u8 wrapped_data_format;
 	size_t kdk_len;
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 2954af6..64a1800 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -149,6 +149,20 @@
 }
 
 
+#ifdef CONFIG_PASN
+static inline int wpa_auth_set_ltf_keyseed(struct wpa_authenticator *wpa_auth,
+					   const u8 *peer_addr,
+					   const u8 *ltf_keyseed,
+					   size_t ltf_keyseed_len)
+{
+	if (!wpa_auth->cb->set_ltf_keyseed)
+		return -1;
+	return wpa_auth->cb->set_ltf_keyseed(wpa_auth->cb_ctx, peer_addr,
+					     ltf_keyseed, ltf_keyseed_len);
+}
+#endif /* CONFIG_PASN */
+
+
 static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth,
 				      const u8 *addr, int idx, u8 *seq)
 {
@@ -2320,6 +2334,7 @@
 	const u8 *z = NULL;
 	size_t z_len = 0, kdk_len;
 	int akmp;
+	int ret;
 
 	if (sm->wpa_auth->conf.force_kdk_derivation ||
 	    (sm->wpa_auth->conf.secure_ltf &&
@@ -2333,16 +2348,33 @@
 		if (sm->ft_completed) {
 			u8 ptk_name[WPA_PMK_NAME_LEN];
 
-			return wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len,
-						 sm->SNonce, sm->ANonce,
-						 sm->addr, sm->wpa_auth->addr,
-						 sm->pmk_r1_name,
-						 ptk, ptk_name,
-						 sm->wpa_key_mgmt,
-						 sm->pairwise,
-						 kdk_len);
+			ret = wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len,
+						sm->SNonce, sm->ANonce,
+						sm->addr, sm->wpa_auth->addr,
+						sm->pmk_r1_name, ptk,
+						ptk_name, sm->wpa_key_mgmt,
+						sm->pairwise, kdk_len);
+		} else {
+			ret = wpa_auth_derive_ptk_ft(sm, ptk);
 		}
-		return wpa_auth_derive_ptk_ft(sm, ptk);
+		if (ret) {
+			wpa_printf(MSG_ERROR, "FT: PTK derivation failed");
+			return ret;
+		}
+
+#ifdef CONFIG_PASN
+		if (sm->wpa_auth->conf.secure_ltf &&
+		    ieee802_11_rsnx_capab(sm->rsnxe,
+					  WLAN_RSNX_CAPAB_SECURE_LTF)) {
+			ret = wpa_ltf_keyseed(ptk, sm->wpa_key_mgmt,
+					      sm->pairwise);
+			if (ret) {
+				wpa_printf(MSG_ERROR,
+					   "FT: LTF keyseed derivation failed");
+			}
+		}
+#endif /* CONFIG_PASN */
+		return ret;
 	}
 #endif /* CONFIG_IEEE80211R_AP */
 
@@ -2356,9 +2388,27 @@
 	akmp = sm->wpa_key_mgmt;
 	if (force_sha256)
 		akmp |= WPA_KEY_MGMT_PSK_SHA256;
-	return wpa_pmk_to_ptk(pmk, pmk_len, "Pairwise key expansion",
-			      sm->wpa_auth->addr, sm->addr, sm->ANonce, snonce,
-			      ptk, akmp, sm->pairwise, z, z_len, kdk_len);
+	ret = wpa_pmk_to_ptk(pmk, pmk_len, "Pairwise key expansion",
+			     sm->wpa_auth->addr, sm->addr, sm->ANonce,
+			     snonce, ptk, akmp, sm->pairwise, z, z_len,
+			     kdk_len);
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "WPA: PTK derivation failed");
+		return ret;
+	}
+
+#ifdef CONFIG_PASN
+	if (sm->wpa_auth->conf.secure_ltf &&
+	    ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF)) {
+		ret = wpa_ltf_keyseed(ptk, sm->wpa_key_mgmt, sm->pairwise);
+		if (ret) {
+			wpa_printf(MSG_DEBUG,
+				   "WPA: LTF keyseed derivation failed");
+		}
+	}
+#endif /* CONFIG_PASN */
+	return ret;
 }
 
 
@@ -2389,6 +2439,19 @@
 			      fils_ft, &fils_ft_len, kdk_len);
 	if (res < 0)
 		return res;
+
+#ifdef CONFIG_PASN
+	if (sm->wpa_auth->conf.secure_ltf &&
+	    ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF)) {
+		res = wpa_ltf_keyseed(&sm->PTK, sm->wpa_key_mgmt, sm->pairwise);
+		if (res) {
+			wpa_printf(MSG_ERROR,
+				   "FILS: LTF keyseed derivation failed");
+			return res;
+		}
+	}
+#endif /* CONFIG_PASN */
+
 	sm->PTK_valid = true;
 	sm->tk_already_set = false;
 
@@ -2892,6 +2955,19 @@
 		wpa_printf(MSG_DEBUG, "FILS: Failed to set TK to the driver");
 		return -1;
 	}
+
+#ifdef CONFIG_PASN
+	if (sm->wpa_auth->conf.secure_ltf &&
+	    ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF) &&
+	    wpa_auth_set_ltf_keyseed(sm->wpa_auth, sm->addr,
+				     sm->PTK.ltf_keyseed,
+				     sm->PTK.ltf_keyseed_len)) {
+		wpa_printf(MSG_ERROR,
+			   "FILS: Failed to set LTF keyseed to driver");
+		return -1;
+	}
+#endif /* CONFIG_PASN */
+
 	sm->pairwise_set = true;
 	sm->tk_already_set = true;
 
@@ -3490,6 +3566,21 @@
 			return;
 		}
 
+#ifdef CONFIG_PASN
+		if (sm->wpa_auth->conf.secure_ltf &&
+		    ieee802_11_rsnx_capab(sm->rsnxe,
+					  WLAN_RSNX_CAPAB_SECURE_LTF) &&
+		    wpa_auth_set_ltf_keyseed(sm->wpa_auth, sm->addr,
+					     sm->PTK.ltf_keyseed,
+					     sm->PTK.ltf_keyseed_len)) {
+			wpa_printf(MSG_ERROR,
+				   "WPA: Failed to set LTF keyseed to driver");
+			wpa_sta_disconnect(sm->wpa_auth, sm->addr,
+					   WLAN_REASON_PREV_AUTH_NOT_VALID);
+			return;
+		}
+#endif /* CONFIG_PASN */
+
 		/* WPA2 send GTK in the 4-way handshake */
 		secure = 1;
 		gtk = gsm->GTK[gsm->GN - 1];
@@ -3701,6 +3792,22 @@
 					   WLAN_REASON_PREV_AUTH_NOT_VALID);
 			return;
 		}
+
+#ifdef CONFIG_PASN
+		if (sm->wpa_auth->conf.secure_ltf &&
+		    ieee802_11_rsnx_capab(sm->rsnxe,
+					  WLAN_RSNX_CAPAB_SECURE_LTF) &&
+		    wpa_auth_set_ltf_keyseed(sm->wpa_auth, sm->addr,
+					     sm->PTK.ltf_keyseed,
+					     sm->PTK.ltf_keyseed_len)) {
+			wpa_printf(MSG_ERROR,
+				   "WPA: Failed to set LTF keyseed to driver");
+			wpa_sta_disconnect(sm->wpa_auth, sm->addr,
+					   WLAN_REASON_PREV_AUTH_NOT_VALID);
+			return;
+		}
+#endif /* CONFIG_PASN */
+
 		/* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */
 		sm->pairwise_set = true;
 
@@ -4869,16 +4976,17 @@
 
 
 int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
-			   const u8 *pmk, const u8 *pmkid)
+			   const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+			   int akmp)
 {
 	if (wpa_auth->conf.disable_pmksa_caching)
 		return -1;
 
-	wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK from SAE", pmk, PMK_LEN);
-	if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, PMK_LEN, pmkid,
-				 NULL, 0,
-				 wpa_auth->addr, addr, 0, NULL,
-				 WPA_KEY_MGMT_SAE))
+	wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK from SAE", pmk, pmk_len);
+	if (!akmp)
+		akmp = WPA_KEY_MGMT_SAE;
+	if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, pmk_len, pmkid,
+				 NULL, 0, wpa_auth->addr, addr, 0, NULL, akmp))
 		return 0;
 
 	return -1;
@@ -4956,13 +5064,14 @@
 
 struct rsn_pmksa_cache_entry *
 wpa_auth_pmksa_create_entry(const u8 *aa, const u8 *spa, const u8 *pmk,
+			    size_t pmk_len, int akmp,
 			    const u8 *pmkid, int expiration)
 {
 	struct rsn_pmksa_cache_entry *entry;
 	struct os_reltime now;
 
-	entry = pmksa_cache_auth_create_entry(pmk, PMK_LEN, pmkid, NULL, 0, aa,
-					      spa, 0, NULL, WPA_KEY_MGMT_SAE);
+	entry = pmksa_cache_auth_create_entry(pmk, pmk_len, pmkid, NULL, 0, aa,
+					      spa, 0, NULL, akmp);
 	if (!entry)
 		return NULL;
 
@@ -5276,7 +5385,8 @@
 {
 	if (!sm)
 		return 0;
-	return sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE;
+	return sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE ||
+		sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY;
 }
 
 
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 348a1de..a18f7cb 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -352,6 +352,10 @@
 #ifdef CONFIG_MESH
 	int (*start_ampe)(void *ctx, const u8 *sta_addr);
 #endif /* CONFIG_MESH */
+#ifdef CONFIG_PASN
+	int (*set_ltf_keyseed)(void *ctx, const u8 *addr, const u8 *ltf_keyseed,
+			       size_t ltf_keyseed_len);
+#endif /* CONFIG_PASN */
 };
 
 struct wpa_authenticator * wpa_init(const u8 *addr,
@@ -427,7 +431,8 @@
 			       int session_timeout,
 			       struct eapol_state_machine *eapol);
 int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
-			   const u8 *pmk, const u8 *pmkid);
+			   const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+			   int akmp);
 void wpa_auth_add_sae_pmkid(struct wpa_state_machine *sm, const u8 *pmkid);
 int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr,
 			const u8 *pmk, size_t pmk_len, const u8 *pmkid,
@@ -441,6 +446,7 @@
 			     char *buf, size_t len);
 struct rsn_pmksa_cache_entry *
 wpa_auth_pmksa_create_entry(const u8 *aa, const u8 *spa, const u8 *pmk,
+			    size_t pmk_len, int akmp,
 			    const u8 *pmkid, int expiration);
 int wpa_auth_pmksa_add_entry(struct wpa_authenticator *wpa_auth,
 			     struct rsn_pmksa_cache_entry *entry);
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index 7a97613..1b1324b 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -2805,6 +2805,20 @@
 }
 
 
+#ifdef CONFIG_PASN
+static inline int wpa_auth_set_ltf_keyseed(struct wpa_authenticator *wpa_auth,
+					   const u8 *peer_addr,
+					   const u8 *ltf_keyseed,
+					   size_t ltf_keyseed_len)
+{
+	if (!wpa_auth->cb->set_ltf_keyseed)
+		return -1;
+	return wpa_auth->cb->set_ltf_keyseed(wpa_auth->cb_ctx, peer_addr,
+					     ltf_keyseed, ltf_keyseed_len);
+}
+#endif /* CONFIG_PASN */
+
+
 static inline int wpa_auth_add_sta_ft(struct wpa_authenticator *wpa_auth,
 				      const u8 *addr)
 {
@@ -2849,6 +2863,18 @@
 			     sm->PTK.tk, klen, KEY_FLAG_PAIRWISE_RX_TX))
 		return;
 
+#ifdef CONFIG_PASN
+	if (sm->wpa_auth->conf.secure_ltf &&
+	    ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF) &&
+	    wpa_auth_set_ltf_keyseed(sm->wpa_auth, sm->addr,
+				     sm->PTK.ltf_keyseed,
+				     sm->PTK.ltf_keyseed_len)) {
+		wpa_printf(MSG_ERROR,
+			   "FT: Failed to set LTF keyseed to driver");
+		return;
+	}
+#endif /* CONFIG_PASN */
+
 	/* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */
 	sm->pairwise_set = true;
 	sm->tk_already_set = true;
@@ -3210,6 +3236,15 @@
 			      pairwise, kdk_len) < 0)
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 
+#ifdef CONFIG_PASN
+	if (sm->wpa_auth->conf.secure_ltf &&
+	    ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF) &&
+	    wpa_ltf_keyseed(&sm->PTK, sm->wpa_key_mgmt, pairwise)) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to derive LTF keyseed");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+#endif /* CONFIG_PASN */
+
 	sm->pairwise = pairwise;
 	sm->PTK_valid = true;
 	sm->tk_already_set = false;
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 9e8dae1..a510952 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -350,6 +350,8 @@
 	if (sta && sta->auth_alg == WLAN_AUTH_SAE) {
 		if (!sta->sae || prev_psk)
 			return NULL;
+		if (psk_len)
+			*psk_len = sta->sae->pmk_len;
 		return sta->sae->pmk;
 	}
 	if (sta && wpa_auth_uses_sae(sta->wpa_sm)) {
@@ -934,7 +936,8 @@
 {
 	struct hostapd_data *hapd = ctx;
 
-	ptksa_cache_add(hapd->ptksa, addr, cipher, life_time, ptk);
+	ptksa_cache_add(hapd->ptksa, hapd->own_addr, addr, cipher, life_time,
+			ptk, NULL, NULL);
 }
 
 
@@ -1469,6 +1472,21 @@
 #endif /* CONFIG_NO_RADIUS */
 
 
+#ifdef CONFIG_PASN
+static int hostapd_set_ltf_keyseed(void *ctx, const u8 *peer_addr,
+				   const u8 *ltf_keyseed,
+				   size_t ltf_keyseed_len)
+{
+	struct hostapd_data *hapd = ctx;
+
+	return hostapd_drv_set_secure_ranging_ctx(hapd, hapd->own_addr,
+						  peer_addr, 0, 0, NULL,
+						  ltf_keyseed_len,
+						  ltf_keyseed, 0);
+}
+#endif /* CONFIG_PASN */
+
+
 int hostapd_setup_wpa(struct hostapd_data *hapd)
 {
 	struct wpa_auth_config _conf;
@@ -1515,6 +1533,9 @@
 #ifndef CONFIG_NO_RADIUS
 		.request_radius_psk = hostapd_request_radius_psk,
 #endif /* CONFIG_NO_RADIUS */
+#ifdef CONFIG_PASN
+		.set_ltf_keyseed = hostapd_set_ltf_keyseed,
+#endif /* CONFIG_PASN */
 	};
 	const u8 *wpa_ie;
 	size_t wpa_ie_len;
@@ -1551,11 +1572,12 @@
 #endif /* CONFIG_OCV */
 
 	_conf.secure_ltf =
-		!!(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF);
+		!!(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_AP);
 	_conf.secure_rtt =
-		!!(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_RTT);
+		!!(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_RTT_AP);
 	_conf.prot_range_neg =
-		!!(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG);
+		!!(hapd->iface->drv_flags2 &
+		   WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP);
 
 	hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb, hapd);
 	if (hapd->wpa_auth == NULL) {
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index 524922e..1c8affa 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -228,11 +228,21 @@
 		pos += RSN_SELECTOR_LEN;
 		num_suites++;
 	}
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE_EXT_KEY);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
 	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE) {
 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE);
 		pos += RSN_SELECTOR_LEN;
 		num_suites++;
 	}
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE_EXT_KEY) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
 #endif /* CONFIG_SAE */
 	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B);
@@ -395,7 +405,8 @@
 	size_t flen;
 
 	if (wpa_key_mgmt_sae(conf->wpa_key_mgmt) &&
-	    (conf->sae_pwe == 1 || conf->sae_pwe == 2 || conf->sae_pk)) {
+	    (conf->sae_pwe == 1 || conf->sae_pwe == 2 || conf->sae_pk ||
+	     wpa_key_mgmt_sae_ext_key(conf->wpa_key_mgmt))) {
 		capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
 #ifdef CONFIG_SAE_PK
 		if (conf->sae_pk)
@@ -670,8 +681,12 @@
 #ifdef CONFIG_SAE
 		else if (data.key_mgmt & WPA_KEY_MGMT_SAE)
 			selector = RSN_AUTH_KEY_MGMT_SAE;
+		else if (data.key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY)
+			selector = RSN_AUTH_KEY_MGMT_SAE_EXT_KEY;
 		else if (data.key_mgmt & WPA_KEY_MGMT_FT_SAE)
 			selector = RSN_AUTH_KEY_MGMT_FT_SAE;
+		else if (data.key_mgmt & WPA_KEY_MGMT_FT_SAE_EXT_KEY)
+			selector = RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY;
 #endif /* CONFIG_SAE */
 		else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X)
 			selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
@@ -778,8 +793,12 @@
 #ifdef CONFIG_SAE
 	else if (key_mgmt & WPA_KEY_MGMT_SAE)
 		sm->wpa_key_mgmt = WPA_KEY_MGMT_SAE;
+	else if (key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_SAE_EXT_KEY;
 	else if (key_mgmt & WPA_KEY_MGMT_FT_SAE)
 		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_SAE;
+	else if (key_mgmt & WPA_KEY_MGMT_FT_SAE_EXT_KEY)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_SAE_EXT_KEY;
 #endif /* CONFIG_SAE */
 	else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X)
 		sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X;