[wpa_supplicant] Cumulative patch from commit 257b119c2

Bug: 265294868
Test: Verify Passpoint ANQP functionality and Passpoint association
Test: Connect to Passpoint, Open, WPA2, WPA3 networks and run traffic
Test: Regression test b/218404053 (Bug: TBD)

BYPASS_INCLUSIVE_LANGUAGE_REASON=Merged from Open sourcie
257b119c2 QCA vendor attribute of update roaming cached statistics info
18436f393 Enhance QCA vendor interface for Concurrent AP Policy for XR
58fba11e1 Enhance QCA vendor interface with new hang reason codes
0f3f9cdca dpp-nfc: Try to request with alternative URL in additional cases
8b36248cd Add QCA vendor command to get the monitor mode status
0dd8bcef8 QCA vendor attributes for MLO and EHT capabilities
e5602989c QCA vendor attributes to configure EHT capabilities
d9d5e55c5 DPP: Respond to GAS on the same channel it was received on
651c9e957 Add new status code strings
3a2d27552 Make MFPR value from an associated STA available as hostapdMFPR
546debd5e Force MFPR=1 to be used on the 6 GHz band
f9c6ab834 P2P: Support preferred GO band based optimization for scanning
093bedc05 P2P: Allow persistent group join retry limit to be configured via D-Bus
8717110db Do not flush PMKSA cache on restoring dedicated per-ESS MAC address
1d4027fdb Make random MAC address style parameters use common enum values
681856c35 Check both sec and usec values to see if MAC address was changed
bdbb6e003 wpa_supplicant: Handle MAC address randomization changes for same ESS
4bd1efe07 dbus: Do not bring down primary interface when removing created AP interface
f4096e7cd EHT: Update EHT Operation element to P802.11be/D2.3 in AP settings
e869fdfee wpa_supplicant: Use MLD address in SAE authentication
8c0f83ae8 SME: Accept Authentication frame from an MLD AP
23039f5e4 SME: Add support for handling association with MLD
8f89661df SME: Add support for handling authentication with MLD
694a1c687 SAE: Make sme_sae_auth() return IE offset
870edfd67 WPA3: Update transition disable bitmap based on port authorized event
8fdf3c447 Sync with wireless-next.git include/uapi/linux/nl80211.h
f9804e306 nl80211: Enforce unique address for AP iftype
87bad8afa dbus: Pass in MAC address in CreateInterface method
b0722cf75 dbus: Fix a memory leak on error path in CreateInterface method
a7f6b8518 crypto: Check if crypto_bignum_to_bin() is successful
2749a2c6b nl80211: Actually get and store TX retries
998aeca3c crypto: Clear secrets from stack in hmac_sha256_vector()
909864ab1 HS 2.0: Restore ifdef for DEFINE_STACK_OF in est.c
af0ab435a PASN: Use the assigned status code from IEEE P802.11az/D7.0
3d798ff2a PASN: Align RSNXE with IEEE P802.11az/D7.0 definitions
ab2cb379d Define all assigned BSS membership selector values
ed0a7b480 wpa_supplicant: Implement HE membership selector check
054fcfab6 hostapd: Add require_he configuration
c46351d10 DFS: Clear cac_started when AP is disabled
3df42cf3c EHT: Use HE operating channel width in MCS length calculation
23e31eb68 SAE: Support cross AKM roaming between SAE AKMs in external auth case
a17026707 PASN: Avoid clearing secure context for the PASN deauthentication event
75a9c4bd4 Add new attributes in SCS rule config QCA vendor subcommand
7d8b96dcf wpa_supplicant: Apply same restrictions for MLD as for 6 GHz BSS
7216f79b9 nl80211: Support get_sta_mlo_info for SME-in-wpa_supplicant drivers
06eb608d5 nl80211: Handle scan results with MLD connection
033a57d26 nl80211: Get MLO support capability
32b745448 wpa_supplicant: Make valid_links u16
a2c4c0b1b nl80211: Support MLD association request
a134b4dc5 nl80211: Add support for MLD authentication
e3e68668c ctrl_iface: Report RNR and ML in BSS command
5f17763ad common: Combine definitions for Multi-Link and per STA profile control
7a7ce9574 dbus: Emit more information over D-Bus
ad4fa5dd3 Add more nl80211 info to struct wpa_signal_info
090f0f8c7 mbssid: Indicate MBSSID information in RNR
a1c4adda1 mbssid: Add nl80211 support
54b1352ef mbssid: Make the AID space shared
10749c3c4 mbssid: Process Known BSSID element
15690faad mbssid: Add MBSSID Configuration element
fc2e4bac5 mbssid: Set extended capabilities
a004bf2cd mbssid: Configure parameters and element data
c5a09b051 mbssid: Add Non-Inheritance element
920b56322 mbssid: Functions for building Multiple BSSID elements
931e5d4f9 mbssid: Configure all BSSes before beacon setup
78d0b9899 mbssid: Retrieve driver capabilities
7452e5447 mbssid: Add new configuration option
bb67d5b52 AP: Add testing option to delay EAPOL Tx
1897abad9 dbus: Add D-Bus property for current MAC address
1a800a940 EAP-TEAP server: Allow tunneled EAP method sequence to be optimized
f791b5bbc EAP-TEAP peer: Process Crypto-Binding TLV before EAP Payload TLV
5a9bd8a06 EAP-TEAP: Use EAP-FAST-MSCHAPv2 in the tunnel
364b6500b EAP-FAST: Move EAP-MSCHAPv2 special MSK handling into MSCHAPv2
81dedfbd7 nl80211: Increase the scan frequencies buffer
9a2781f24 wpa_supplicant: Support throughput estimation for EHT rates
755aaeb97 wpa_supplicant: Add missing memory allocation checks
69725c4cf OpenSSL: Fix BN_rshift() argument order
e9b4ad236 OpenSSL: Apply connection flags before reading certificates
bbd5a4689 SAE: Add an enum for defining sae_pwe parameter values
3a0edb2cd SAE: Enable H2E for 6 GHz BSS
20bfd4feb AP: Enable H2E on 6 GHz when SAE is used
b43e19f3f WPS: Cross band overlap detection with multiple interfaces
e2d88f86e DPP: Expose own and peer bootstrap info ids on authentication success
043dedee8 DPP: Expose enrollee pubkey hash for identification
2d8974e31 DPP: Move DPP_EVENT_AUTH_SUCCESS to a helper
d8d2b3a33 Implement read-only mode for SSIDs from the additional config (-I)
4cb23b66d ACS: Allow selecting a better channel when using 40/80/160 MHz
472101684 ACS: introduce acs_adjust_secondary
60e2934cb ACS: Introduce acs_get_bw_center_chan()
ed8e13dec ACS: Extract bw40/80/160 freqs out of acs_usable_bwXXX_chan()
9025def55 wpa_supplicant: Add support for pregenerated MAC
5da3e1ca4 mesh: Do not allow open mode key in 6 GHz
50a9b7d3d P2P: Include only 6 GHz PSCs in full scan
0d6cd88ee DPP: Use existing TCP connection to replay duplicate Presence Announcement
6af717f73 DPP: Don't close TCP connection for duplicate Presence Announcements
46e6b72b7 Add a callback to notify added PMKSA cache entry details
af1528a12 hostapd: Add RELOAD_BSS
bc2b88b25 hostapd: Add config_id to GET_CONFIG output
b37c3fbad hostapd: Add config_id parameter
46f6a3277 Split BSS-specific hostapd_clear_old_bss() from hostapd_clear_old()
2afb9b1a5 dbus: Add dbus notify when wpa_s->key_mgmt changes
98e9d553f nl80211: Check previous MAC address for locally-generated-deauth
87ffa1bec wpa_supplicant: Convert SSID into printable form before printing
416386060 Mark authorization completed on driver indication during 4-way HS offload
da2ec9459 D-Bus: Split set_cred_properties() into two functions
f5ce680ee D-Bus: Hotspot 2.0 credentials with multiple domains
2f739c71c ctrl: Fix compilation with UDP control interface
6d4548187 RSN: Split EAPOL-Key msg 3/4 processing for WPA(v1)
5b7957b7e RSN: Split EAPOL-Key msg 1/4 processing for WPA(v1)
e5dfce38f RSN: Split EAPOL-Key group msg 1/2 processing more completely for WPA(v1)
5ab43c738 RSN: Split WPA(v1) processing of EAPOL-Key frames into a separate function
f7fd891c7 Fix a typo in driver ops poll() documentation
3268ec0ac HS20: Use required_home_ois in hs20-osu-client
58eb905ad HS20: Support credentials with multiple home OIs
0143dc1cb OpenSSL: Load OpenSSL 3.0 legacy provider but let default be loaded
fef4c6cb0 OpenSSL: Don't provide implementation of DES/RC4 for FIPS builds
1d42dafce RSN: Do not include RC4 use in FIPS builds
df5ae2aad Add more detailed description of RADIUS attributes in EAP user file
0ba266d86 dbus: Add virtual interface create/remove logic to be inline with ctrl_iface
5102d7411 wpa_passphrase: Disable terminal echo when reading from stdin
86ab28217 PASN: Fix passing own address and peer address to pasn_deauthenticate()
a9062432e wpa_cli: Fix PASN control interface commands
b6d3fd05e FT: Use SHA256 to derive PMKID for AKM 00-0F-AC:3 (FT-EAP)
ef70f814a Add a new QCA vendor attribute to configure wifi calling (wfc) state
05ec48568 WPS: Pick WPS AP based on latest received WPS IE
ca4fa867d Enable PMF automatically if OCV is enabled
c823197bd SAE: Use Challenge Failure status code in confirm message failure cases
65c8633d9 Allow a lower priority BSS to be tried after network disabling
e91ac53d5 DFS: Do not allow channel checks to go beyond the channel list
f96dfdeef PASN: Fix missing libraries for libpasn.so on Android
91d148f50 PASN: Fix is_pasn_auth_frame() for mgmt tx status frames
b6c38cee9 Skip CAC if the driver switches channel to non-DFS
080afc03d Add hostapd control interface command to stop logging to file
0fd13c90e Add QCA vendor interface for AP doze mode configuration
4e1f55a11 Roam control configuration for 6 GHz in full scan only on prior discovery
34d93b0c9 HS 2.0: Deauthenticate STA on deauth-imminent more quickly if no URL
2e40f969b nl80211: Fix wrong requested links bitmap in sta_mlo_info.req_links
b6e226496 MLD STA: Fix IGTK and BIGTK MLO KDEs validation
2050130be Add a vendor attribute for roam control configuration for full scan
12f16c27b TLS: Fix unsigned int underflow in internal TLS 1.0/1.1 implementation
802b67bce Update tls_connection_set_verify() documentation to verify_peer=2
0202b9774 DPP: Fix memory leak of intro.peer_key in station handling
f723f7f8a P2P: Check dev pointer consistently when building PD Response
30403e965 WPS: Check NDEF record length fields separately
cd0e8653a TDLS: Use stored FTE length in MIC calculation
7e85e24f3 TDLS: Use stored peer RSNE length in MIC calculation
40a42613e FT: Simplify FTE parsing for FT-SAE-EXT-KEY using MIC Length subfield
5ea7a2f54 DPP: Drop PMKSA entry if AP reject association due to invalid PMKID
4840b45a2 Fix empty pmksa_cache_get()
3abd0c471 SAE: Print rejection of peer element clearly in debug log
9ff778fa4 Check for own address (SPA) match when finding PMKSA entries
9f04a9c8d Store own MAC address (SPA) in supplicant PMKSA cache entries
309765eb6 PASN: Use separate variables for BSSID and peer address
42f0c44d8 PASN: Use peer address instead of BSSID as the destination for initiator
15583802b nl80211: Allow up to 64-byte PMK in NL80211_CMD_SET_PMKSA
bbe5f0c1e FT: Do not try to use FT protocol between mobility domains
b92f61885 Don't use default RSNE/RSNXE when the driver indicates cross SSID roaming
d7febe33f MLO: Remove unnecessary debug prints about clearing AP RSNE/RSNXE
16d913bfd Define AFC vendor commands and events
46f5cf928 OpenSSL: Fix additional HPKE corner cases
bdc35acd5 SAE: Allow loading of the password from an external database
48dd8994a Fix external passwords with 4-way handshake offloading
e5a7c852c systemd: Use interface name in description of interface-specific units
a0628f8a5 OpenSSL: Remove unused assignment from HPKE expand
3e1a04afa nl80211: Check that attribute addition succeeds in offloaded PASN case
0658a22ef GAS: Try to make buffer length determination easier for static analyzers
271ce71c7 FT: Fix PMK-R0 derivation for FT-SAE-EXT-KEY with SHA512
2f61d703a MLD STA: Group key handshake processing for GTK/IGTK/BIGTK rekeying
f0760aa6d MLD STA: Use AP MLD address as destination for 4-way handshake EAPOL-Key frames
8f2e493be MLD STA: Validation of MLO KDEs for 4-way handshake EAPOL-Key frames
f15cc834c MLD STA: Processing of EAPOL-Key msg 3/4 frame when using MLO
08512e5f3 MLD STA: Extend key configuration functions to support Link ID
a4adb2f3e MLD STA: Configure TK to the driver using AP MLD address
fa5cad61a MLD STA: Use AP MLD address in PMKSA entry
052bf8a51 MLD STA: Use AP MLD address to derive pairwise keys
e78437256 MLD STA: Add MLO KDEs for EAPOL-Key msg 2/4 and 4/4
472a0b8d6 MLD STA: Set MLO connection info to wpa_sm
cc2236299 nl80211: Get all requested MLO links information from (re)association events
1ca5c2ec2 PASN: Fix spelling of RSNE in debug messages
a43536a72 PASN: Verify explicitly that elements are present before parsing
7e3852407 PASN: Fix MIC check not to modify const data
8481c7509 PASN: Fix Authentication frame checks
f899d7f37 dbus: Apply PMK properties immediately
c6f8af507 Add option to disable SAE key_mgmt without PMF
7ad757ec0 Document crypto_ec_key_get_subject_public_key() to use compressed format
6527a7656 DPP: Stop listen mode for chirp-initiated Authentication exchange
2e7339442 P2P: Discount current operating frequency when scanning new connection
00a762c26 Do not drop connection attempt when reconnecting to the same ESS
368de263b P2P: Skip Extended Listen timeout to allow scans during group formation
cff55f348 P2P: Clone sae_pwe config to new group interface config
ae517789f P2P: Allow PSC channel to be used for 6 GHz BW40
9c830d917 P2P: Track peer 6 GHz capability more robustly
1ca403a8b Add QCA vendor subcommand to notify about primary netdev
70d89f90e A vendor roam control configuration for delaying hand off for RX
fff81a468 PASN: Change pasn_use_384() to be a non-static function
ea241cbe9 PASN: Rename struct wpas_pasn to pasn_data
6be84343a PASN: Add pairing verification wrapper function for Wi-Fi Aware
325236948 PASN: Mark wpas_pasn_start() comeback argument const
b1ed44b6a PASN: Allow extra elements to be added into PASN Authentication frames
08abcdf4e PASN: Makefile and Android.mk changes for libpasn.so
78c5bb7f5 PASN: Move responder functionality into a separate file
c7edfce79 PASN: Move initiator changes into a separate file
975b7a02c Move SAE comeback token functionality into a separate file
1711fe912 PASN: Compute MIC from RSNE and RSNXE of the frame for Wi-Fi Aware
6f80014b1 PASN: Allow custom PMKID in Authentication frames for Wi-Fi Aware
e99047da2 PASN: Add a handler func to send mgmt frames to the driver from AP
4022ffc5d PASN: Store AKMP in the PTKSA cache
c55eadede PASN: Remove hapd dependency in processing PASN Authentication frames
6dc833bc5 PASN: Remove hapd dependency for PASN and SAE comeback
1861f5716 PASN: Remove hapd dependency for pasn_derive_keys()
1fa266e99 PASN: Remove hapd dependency for SAE and FILS wrapped data
bc9fbe1b2 PASN: Common wpas_pasn structure for initiator and responder
14b5ebce7 PASN: Add a common header file for initiator and responder
af5eec3b3 PASN: Function handler to transmit Authentication frames
629bbc91b PASN: Remove dependency of wpa_ssid for initiator
e7f45ca11 PASN: Remove wpa_s dependency for wpas_pasn_start()
0be131265 PASN: Remove wpa_s dependency for wpas_pasn_build_auth_1()
086ccdc59 PASN: Remove wpa_s dependency for the functions processing RX frames
e2e87b90b PASN: Remove wpa_s dependency for wpas_pasn_auth_tx_status()
de4b73a36 PASN: Remove wpa_s dependency for FILS wrapped data
90bb73c51 PASN: Remove wpa_sm dependency to add an entry to PMKSA cache
5313e5a79 PASN: Remove unused wpa_s parameter for wpas_pasn_sae_setup_pt()
f636cbd04 PASN: Remove wpa_s dependency for SAE wrapped data functions
5535fbcfa PASN: Add wpa_pasn_reset() to eliminate need for struct wpa_supplicant
10e455c44 Enable use of PMKSA caching independent of RSN supplicant state machine
1d0ee1908 Fix the vendor ID assignment for configuring periodic sounding
b17b86da4 QCA vendor attribute to configure periodic sounding
6f3efa21c MLD STA: Fix crash caused by NULL wpa_s->current_ssid
ef5a9a009 nl80211: Fix parsing PASN peer and src addresses from vendor nl attributes
2c55c9273 More debug prints for EAPOL-Key message generation (Authenticator)
90cef4f21 MLD STA: Fully clear MLO info to avoid use of uninitialized members
496a1ced1 MLD STA: Use MLD addresses for sending non-Public Action frames
17ae98873 MLD STA: Support processing of M1 received before association event
9dafad1ea EHT: Definitions for STA Control fields of Basic Multi-Link element
1fbea7d43 EHT: Multi-Link element defragmentation
ec03b71ee common: Refactor element defragmentation
347ea8f0a EHT: Parse Multi-Link elements
73f540b6a MLD STA: Fetch MLO association Link ID info to core wpa_supplicant
ee46b7d6d nl80211: Check MLO link status info in NL80211_CMD_CONNECT
6e015cd45 RADIUS: Add Filter-Id attribute
06800f612 Add QCA vendor attributes for EHT support in external ACS
042368663 Vendor attribute to configure QoS/AC upgrade for UDP frames
e5d15e225 EHT: Allow EHT to be disabled using disable_eht=1 in wpa_supplicant
041f6cea9 SAE: Accept FT and -EXT-KEY AKMs for external auth
b72922796 P2P: Get cached scan results on iface creation to avoid scan
8392ea9e7 SAE: Fix AKM suite selector check for external authentication
ebe6a7c94 FT: Cover variable length KCK in function documentation
eda4ba081 FT: Reassociation Response frame validation for FT-SAE-EXT-KEY
0f7253d35 FT: Response processing for FT-SAE-EXT-KEY
a1eb1bb0e FT: Supplicant side FTE generation for FT-SAE-EXT-KEY
883e33594 FT: Authentication request frame processing for FT-SAE-EXT-KEY
879363bbc FT: Reassociation Request frame parsing for FT-SAE-EXT-KEY
e8f23c948 FT: Association Response frame FTE generation for FT-SAE-EXT-KEY
a76a314c1 FT: Extend PMK-R0 derivation for FT-SAE-EXT-KEY
79cd846b2 FT: Extend PTK derivation for FT-SAE-EXT-KEY
39b60f334 FT: Extend PMK-R1 derivation for FT-SAE-EXT-KEY
fb4fc704c FT: Debug print FTE subelements during parsing
25b52e5f8 FT: Extend FTE parsing for FT-SAE-EXT-KEY
4f58afee9 FT: Extend MIC derivation for FT-SAE-EXT-KEY
dcd46edf5 FT: Extend PMKR1Name derivation for FT-SAE-EXT-KEY
9fd245564 FT: Support longer SAE PMK for FT in INITPSK AP
c41bd98be FT: AP mode FTE writing to support FT-SAE-KEY-EXT
efa0f51d3 FT: Accept 512-bit PMK-R1 from RRB
eb0821c90 Add service class id attribute in SCS rule config vendor subcommand
5607abe2e WNM: Print unsupported neighbor report subelements in debug log
fcdd76fa2 Interworking: Print unsupported inner EAP-TTLS method in debug log
f8a05de66 Move default action from after switch to within
7614fcebe ACS: Filter out 6 GHz channels if HE or EHT is not enabled
1864664ca Android: Delay QCA roam+auth event until NL80211_CMD_ROAM is received
5f3cdc064 Override ieee80211w from pmf for AP mode in wpa_supplicant
2b972a35b DPP: Require PMF when profile is for SAE without PSK
8219d2b7d PASN: Fix CONFIG_PASN=y build without CONFIG_IEEE80211R=y

Change-Id: Ifd6be0d096df54c13162fdda164cd8e804a51692
Merged-In: Ifd6be0d096df54c13162fdda164cd8e804a51692
(cherry picked from commit 38ad1edd94f7b4f3c3937dd21a9b7ef52140babf)
diff --git a/src/ap/acs.c b/src/ap/acs.c
index 8ee2e04..8cb5813 100644
--- a/src/ap/acs.c
+++ b/src/ap/acs.c
@@ -241,6 +241,57 @@
  * [1] http://en.wikipedia.org/wiki/Near_and_far_field
  */
 
+enum bw_type {
+	ACS_BW40,
+	ACS_BW80,
+	ACS_BW160,
+};
+
+struct bw_item {
+	int first;
+	int last;
+	int center_chan;
+};
+
+static const struct bw_item bw_40[] = {
+	{ 5180, 5200, 38 }, { 5220, 5240, 46 }, { 5260, 5280, 54 },
+	{ 5300, 5320, 62 }, { 5500, 5520, 102 }, { 5540, 5560, 110 },
+	{ 5580, 5600, 110 }, { 5620, 5640, 126}, { 5660, 5680, 134 },
+	{ 5700, 5720, 142 }, { 5745, 5765, 151 }, { 5785, 5805, 159 },
+	{ 5825, 5845, 167 }, { 5865, 5885, 175 },
+	{ 5955, 5975, 3 }, { 5995, 6015, 11 }, { 6035, 6055, 19 },
+	{ 6075, 6095, 27 }, { 6115, 6135, 35 }, { 6155, 6175, 43 },
+	{ 6195, 6215, 51 }, { 6235, 6255, 59 }, { 6275, 6295, 67 },
+	{ 6315, 6335, 75 }, { 6355, 6375, 83 }, { 6395, 6415, 91 },
+	{ 6435, 6455, 99 }, { 6475, 6495, 107 }, { 6515, 6535, 115 },
+	{ 6555, 6575, 123 }, { 6595, 6615, 131 }, { 6635, 6655, 139 },
+	{ 6675, 6695, 147 }, { 6715, 6735, 155 }, { 6755, 6775, 163 },
+	{ 6795, 6815, 171 }, { 6835, 6855, 179 }, { 6875, 6895, 187 },
+	{ 6915, 6935, 195 }, { 6955, 6975, 203 }, { 6995, 7015, 211 },
+	{ 7035, 7055, 219 }, { 7075, 7095, 227}, { -1, -1, -1 }
+};
+static const struct bw_item bw_80[] = {
+	{ 5180, 5240, 42 }, { 5260, 5320, 58 }, { 5500, 5560, 106 },
+	{ 5580, 5640, 122 }, { 5660, 5720, 138 }, { 5745, 5805, 155 },
+	{ 5825, 5885, 171},
+	{ 5955, 6015, 7 }, { 6035, 6095, 23 }, { 6115, 6175, 39 },
+	{ 6195, 6255, 55 }, { 6275, 6335, 71 }, { 6355, 6415, 87 },
+	{ 6435, 6495, 103 }, { 6515, 6575, 119 }, { 6595, 6655, 135 },
+	{ 6675, 6735, 151 }, { 6755, 6815, 167 }, { 6835, 6895, 183 },
+	{ 6915, 6975, 199 }, { 6995, 7055, 215 }, { -1, -1, -1 }
+};
+static const struct bw_item bw_160[] = {
+	{ 5180, 5320, 50 }, { 5500, 5640, 114 }, { 5745, 5885, 163 },
+	{ 5955, 6095, 15 }, { 6115, 6255, 47 }, { 6275, 6415, 79 },
+	{ 6435, 6575, 111 }, { 6595, 6735, 143 },
+	{ 6755, 6895, 175 }, { 6915, 7055, 207 }, { -1, -1, -1 }
+};
+static const struct bw_item *bw_desc[] = {
+	[ACS_BW40] = bw_40,
+	[ACS_BW80] = bw_80,
+	[ACS_BW160] = bw_160,
+};
+
 
 static int acs_request_scan(struct hostapd_iface *iface);
 static int acs_survey_is_sufficient(struct freq_survey *survey);
@@ -370,48 +421,31 @@
 }
 
 
-static int acs_usable_bw40_chan(const struct hostapd_channel_data *chan)
+static bool acs_usable_bw_chan(const struct hostapd_channel_data *chan,
+			       enum bw_type bw)
 {
-	const int allowed[] = { 5180, 5220, 5260, 5300, 5500, 5540, 5580, 5620,
-				5660, 5745, 5785, 4920, 4960, 5955, 5995, 6035,
-				6075, 6115, 6155, 6195, 6235, 6275, 6315, 6355,
-				6395, 6435, 6475, 6515, 6555, 6595, 6635, 6675,
-				6715, 6755, 6795, 6835, 6875, 6915, 6955, 6995,
-				7035, 7075 };
-	unsigned int i;
+	unsigned int i = 0;
 
-	for (i = 0; i < ARRAY_SIZE(allowed); i++)
-		if (chan->freq == allowed[i])
-			return 1;
+	while (bw_desc[bw][i].first != -1) {
+		if (chan->freq == bw_desc[bw][i].first)
+			return true;
+		i++;
+	}
 
-	return 0;
+	return false;
 }
 
 
-static int acs_usable_bw80_chan(const struct hostapd_channel_data *chan)
+static int acs_get_bw_center_chan(int freq, enum bw_type bw)
 {
-	const int allowed[] = { 5180, 5260, 5500, 5580, 5660, 5745, 5955, 6035,
-				6115, 6195, 6275, 6355, 6435, 6515, 6595, 6675,
-				6755, 6835, 6915, 6995 };
-	unsigned int i;
+	unsigned int i = 0;
 
-	for (i = 0; i < ARRAY_SIZE(allowed); i++)
-		if (chan->freq == allowed[i])
-			return 1;
-
-	return 0;
-}
-
-
-static int acs_usable_bw160_chan(const struct hostapd_channel_data *chan)
-{
-	const int allowed[] = { 5180, 5500, 5955, 6115, 6275, 6435, 6595, 6755,
-				6915 };
-	unsigned int i;
-
-	for (i = 0; i < ARRAY_SIZE(allowed); i++)
-		if (chan->freq == allowed[i])
-			return 1;
+	while (bw_desc[bw][i].first != -1) {
+		if (freq >= bw_desc[bw][i].first &&
+		    freq <= bw_desc[bw][i].last)
+			return bw_desc[bw][i].center_chan;
+		i++;
+	}
 
 	return 0;
 }
@@ -607,6 +641,26 @@
 }
 
 
+static enum hostapd_hw_mode
+acs_find_mode(struct hostapd_iface *iface, int freq)
+{
+	int i;
+	struct hostapd_hw_modes *mode;
+	struct hostapd_channel_data *chan;
+
+	for (i = 0; i < iface->num_hw_features; i++) {
+		mode = &iface->hw_features[i];
+		if (!hostapd_hw_skip_mode(iface, mode)) {
+			chan = acs_find_chan_mode(mode, freq);
+			if (chan)
+				return mode->mode;
+		}
+	}
+
+	return HOSTAPD_MODE_IEEE80211ANY;
+}
+
+
 static struct hostapd_channel_data *
 acs_find_chan(struct hostapd_iface *iface, int freq)
 {
@@ -665,7 +719,7 @@
 			 struct hostapd_channel_data **ideal_chan,
 			 long double *ideal_factor)
 {
-	struct hostapd_channel_data *chan, *adj_chan = NULL;
+	struct hostapd_channel_data *chan, *adj_chan = NULL, *best;
 	long double factor;
 	int i, j;
 	unsigned int k;
@@ -673,8 +727,9 @@
 	for (i = 0; i < mode->num_channels; i++) {
 		double total_weight;
 		struct acs_bias *bias, tmp_bias;
+		bool update_best = true;
 
-		chan = &mode->channels[i];
+		best = chan = &mode->channels[i];
 
 		/* Since in the current ACS implementation the first channel is
 		 * always a primary channel, skip channels not available as
@@ -713,7 +768,7 @@
 		    ((iface->conf->ieee80211n &&
 		      iface->conf->secondary_channel) ||
 		     is_6ghz_freq(chan->freq)) &&
-		    !acs_usable_bw40_chan(chan)) {
+		    !acs_usable_bw_chan(chan, ACS_BW40)) {
 			wpa_printf(MSG_DEBUG,
 				   "ACS: Channel %d: not allowed as primary channel for 40 MHz bandwidth",
 				   chan->chan);
@@ -724,7 +779,7 @@
 		    (iface->conf->ieee80211ac || iface->conf->ieee80211ax)) {
 			if (hostapd_get_oper_chwidth(iface->conf) ==
 			    CONF_OPER_CHWIDTH_80MHZ &&
-			    !acs_usable_bw80_chan(chan)) {
+			    !acs_usable_bw_chan(chan, ACS_BW80)) {
 				wpa_printf(MSG_DEBUG,
 					   "ACS: Channel %d: not allowed as primary channel for 80 MHz bandwidth",
 					   chan->chan);
@@ -733,7 +788,7 @@
 
 			if (hostapd_get_oper_chwidth(iface->conf) ==
 			    CONF_OPER_CHWIDTH_160MHZ &&
-			    !acs_usable_bw160_chan(chan)) {
+			    !acs_usable_bw_chan(chan, ACS_BW160)) {
 				wpa_printf(MSG_DEBUG,
 					   "ACS: Channel %d: not allowed as primary channel for 160 MHz bandwidth",
 					   chan->chan);
@@ -761,7 +816,15 @@
 			if (acs_usable_chan(adj_chan)) {
 				factor += adj_chan->interference_factor;
 				total_weight += 1;
+			} else {
+				update_best = false;
 			}
+
+			/* find the best channel in this segment */
+			if (update_best &&
+			    adj_chan->interference_factor <
+			    best->interference_factor)
+				best = adj_chan;
 		}
 
 		if (j != n_chans) {
@@ -770,6 +833,18 @@
 			continue;
 		}
 
+		/* If the AP is in the 5 GHz or 6 GHz band, lets prefer a less
+		 * crowded primary channel if one was found in the segment */
+		if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
+		    chan != best) {
+			wpa_printf(MSG_DEBUG,
+				   "ACS: promoting channel %d over %d (less interference %Lg/%Lg)",
+				   best->chan, chan->chan,
+				   chan->interference_factor,
+				   best->interference_factor);
+			chan = best;
+		}
+
 		/* 2.4 GHz has overlapping 20 MHz channels. Include adjacent
 		 * channel interference factor. */
 		if (is_24ghz_mode(mode->mode)) {
@@ -923,21 +998,50 @@
 }
 
 
+static void acs_adjust_secondary(struct hostapd_iface *iface)
+{
+	unsigned int i;
+
+	/* When working with bandwidth over 20 MHz on the 5 GHz or 6 GHz band,
+	 * ACS can return a secondary channel which is not the first channel of
+	 * the segment and we need to adjust. */
+	if (!iface->conf->secondary_channel ||
+	    acs_find_mode(iface, iface->freq) != HOSTAPD_MODE_IEEE80211A)
+		return;
+
+	wpa_printf(MSG_DEBUG, "ACS: Adjusting HT/VHT/HE secondary frequency");
+
+	for (i = 0; bw_desc[ACS_BW40][i].first != -1; i++) {
+		if (iface->freq == bw_desc[ACS_BW40][i].first)
+			iface->conf->secondary_channel = 1;
+		else if (iface->freq == bw_desc[ACS_BW40][i].last)
+			iface->conf->secondary_channel = -1;
+	}
+}
+
+
 static void acs_adjust_center_freq(struct hostapd_iface *iface)
 {
-	int offset;
+	int center;
 
 	wpa_printf(MSG_DEBUG, "ACS: Adjusting VHT center frequency");
 
 	switch (hostapd_get_oper_chwidth(iface->conf)) {
 	case CONF_OPER_CHWIDTH_USE_HT:
-		offset = 2 * iface->conf->secondary_channel;
+		if (iface->conf->secondary_channel &&
+		    iface->freq >= 2400 && iface->freq < 2500)
+			center = iface->conf->channel +
+				2 * iface->conf->secondary_channel;
+		else if (iface->conf->secondary_channel)
+			center = acs_get_bw_center_chan(iface->freq, ACS_BW40);
+		else
+			center = iface->conf->channel;
 		break;
 	case CONF_OPER_CHWIDTH_80MHZ:
-		offset = 6;
+		center = acs_get_bw_center_chan(iface->freq, ACS_BW80);
 		break;
 	case CONF_OPER_CHWIDTH_160MHZ:
-		offset = 14;
+		center = acs_get_bw_center_chan(iface->freq, ACS_BW160);
 		break;
 	default:
 		/* TODO: How can this be calculated? Adjust
@@ -947,8 +1051,7 @@
 		return;
 	}
 
-	hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
-					     iface->conf->channel + offset);
+	hostapd_set_oper_centr_freq_seg0_idx(iface->conf, center);
 }
 
 
@@ -1004,8 +1107,10 @@
 	iface->conf->channel = ideal_chan->chan;
 	iface->freq = ideal_chan->freq;
 
-	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax)
+	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
+		acs_adjust_secondary(iface);
 		acs_adjust_center_freq(iface);
+	}
 
 	err = hostapd_select_hw_mode(iface);
 	if (err) {
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index b73aae2..5dc8a8f 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -481,10 +481,11 @@
 	struct hostapd_ssid *ssid = &conf->ssid;
 	struct sae_password_entry *pw;
 
-	if ((conf->sae_pwe == 0 && !hostapd_sae_pw_id_in_use(conf) &&
+	if ((conf->sae_pwe == SAE_PWE_HUNT_AND_PECK &&
+	     !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 ||
+	    conf->sae_pwe == SAE_PWE_FORCE_HUNT_AND_PECK ||
 	    !wpa_key_mgmt_sae(conf->wpa_key_mgmt))
 		return 0; /* PT not needed */
 
@@ -798,6 +799,7 @@
 	os_free(conf->radius_req_attr_sqlite);
 	os_free(conf->rsn_preauth_interfaces);
 	os_free(conf->ctrl_interface);
+	os_free(conf->config_id);
 	os_free(conf->ca_cert);
 	os_free(conf->server_cert);
 	os_free(conf->server_cert2);
@@ -1211,6 +1213,14 @@
 		return false;
 	}
 
+#ifdef CONFIG_SAE
+	if (wpa_key_mgmt_sae(bss->wpa_key_mgmt) &&
+	    bss->sae_pwe == SAE_PWE_HUNT_AND_PECK) {
+		wpa_printf(MSG_INFO, "SAE: Enabling SAE H2E on 6 GHz");
+		bss->sae_pwe = SAE_PWE_BOTH;
+	}
+#endif /* CONFIG_SAE */
+
 	return true;
 }
 
@@ -1452,6 +1462,12 @@
 	}
 #endif /* CONFIG_IEEE80211BE */
 
+	if (full_config && bss->ignore_broadcast_ssid && conf->mbssid) {
+		wpa_printf(MSG_ERROR,
+			   "Hidden SSID is not suppored when MBSSID is enabled");
+		return -1;
+	}
+
 	return 0;
 }
 
@@ -1535,6 +1551,12 @@
 	}
 #endif /* CONFIG_IEEE80211BE */
 
+	if (full_config && conf->mbssid && !conf->ieee80211ax) {
+		wpa_printf(MSG_ERROR,
+			   "Cannot enable multiple BSSID support without ieee80211ax");
+		return -1;
+	}
+
 	for (i = 0; i < conf->num_bss; i++) {
 		if (hostapd_config_check_bss(conf->bss[i], conf, full_config))
 			return -1;
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 268829a..6dbb223 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -443,6 +443,7 @@
 	int eap_teap_pac_no_inner;
 	int eap_teap_separate_result;
 	int eap_teap_id;
+	int eap_teap_method_sequence;
 	int eap_sim_aka_result_ind;
 	int eap_sim_id;
 	char *imsi_privacy_key;
@@ -670,7 +671,7 @@
 	unsigned int sae_sync;
 	int sae_require_mfp;
 	int sae_confirm_immediate;
-	int sae_pwe;
+	enum sae_pwe sae_pwe;
 	int *sae_groups;
 	struct sae_password_entry *sae_passwords;
 
@@ -916,6 +917,8 @@
 	u8 ext_capa[EXT_CAPA_MAX_LEN];
 
 	u8 rnr;
+	char *config_id;
+	bool xrates_supported;
 };
 
 /**
@@ -1076,6 +1079,7 @@
 	int ecsa_ie_only;
 	unsigned int skip_send_eapol;
 	unsigned int enable_eapol_large_timeout;
+	bool delay_eapol_tx;
 #endif /* CONFIG_TESTING_OPTIONS */
 
 #ifdef CONFIG_ACS
@@ -1105,6 +1109,7 @@
 	u8 he_6ghz_rx_ant_pat;
 	u8 he_6ghz_tx_ant_pat;
 	u8 he_6ghz_reg_pwr_type;
+	bool require_he;
 #endif /* CONFIG_IEEE80211AX */
 
 	/* VHT enable/disable config from CHAN_SWITCH */
@@ -1144,6 +1149,12 @@
 #define CH_SWITCH_EHT_ENABLED BIT(0)
 #define CH_SWITCH_EHT_DISABLED BIT(1)
 	unsigned int ch_switch_eht_config;
+
+	enum mbssid {
+		MBSSID_DISABLED = 0,
+		MBSSID_ENABLED = 1,
+		ENHANCED_MBSSID_ENABLED = 2,
+	} mbssid;
 };
 
 
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index b9fd920..1ffc37f 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -92,7 +92,7 @@
 		goto fail;
 
 	pos = buf;
-	pos = hostapd_eid_ext_capab(hapd, pos);
+	pos = hostapd_eid_ext_capab(hapd, pos, false);
 	if (add_buf_data(&assocresp, buf, pos - buf) < 0)
 		goto fail;
 	pos = hostapd_eid_interworking(hapd, pos);
@@ -724,6 +724,7 @@
 	params.key_len = key_len;
 	params.vlan_id = vlan_id;
 	params.key_flag = key_flag;
+	params.link_id = -1;
 
 	return hapd->driver->set_key(hapd->drv_priv, &params);
 }
@@ -905,8 +906,10 @@
 			     chan->chan)))
 			continue;
 		if (is_6ghz_freq(chan->freq) &&
-		    hapd->iface->conf->acs_exclude_6ghz_non_psc &&
-		    !is_6ghz_psc_frequency(chan->freq))
+		    ((hapd->iface->conf->acs_exclude_6ghz_non_psc &&
+		      !is_6ghz_psc_frequency(chan->freq)) ||
+		     (!hapd->iface->conf->ieee80211ax &&
+		      !hapd->iface->conf->ieee80211be)))
 			continue;
 		if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
 		    !(hapd->iface->conf->acs_exclude_dfs &&
diff --git a/src/ap/ap_mlme.c b/src/ap/ap_mlme.c
index db8a267..309e69a 100644
--- a/src/ap/ap_mlme.c
+++ b/src/ap/ap_mlme.c
@@ -29,9 +29,9 @@
 		return "SHARED_KEY";
 	case WLAN_AUTH_FT:
 		return "FT";
+	default:
+		return "unknown";
 	}
-
-	return "unknown";
 }
 #endif /* CONFIG_NO_HOSTAPD_LOGGER */
 
diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c
index fd9c96f..4ab2a4a 100644
--- a/src/ap/authsrv.c
+++ b/src/ap/authsrv.c
@@ -211,6 +211,7 @@
 	cfg->eap_teap_pac_no_inner = hapd->conf->eap_teap_pac_no_inner;
 	cfg->eap_teap_separate_result = hapd->conf->eap_teap_separate_result;
 	cfg->eap_teap_id = hapd->conf->eap_teap_id;
+	cfg->eap_teap_method_sequence = hapd->conf->eap_teap_method_sequence;
 	cfg->eap_sim_aka_result_ind = hapd->conf->eap_sim_aka_result_ind;
 	cfg->eap_sim_id = hapd->conf->eap_sim_id;
 	cfg->imsi_privacy_key = hapd->imsi_privacy_key;
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 8676570..dbc6b06 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -462,15 +462,86 @@
 }
 
 
+static int
+ieee802_11_build_ap_params_mbssid(struct hostapd_data *hapd,
+				  struct wpa_driver_ap_params *params)
+{
+	struct hostapd_iface *iface = hapd->iface;
+	struct hostapd_data *tx_bss;
+	size_t len;
+	u8 elem_count = 0, *elem = NULL, **elem_offset = NULL, *end;
+
+	if (!iface->mbssid_max_interfaces ||
+	    iface->num_bss > iface->mbssid_max_interfaces ||
+	    (iface->conf->mbssid == ENHANCED_MBSSID_ENABLED &&
+	     !iface->ema_max_periodicity))
+		goto fail;
+
+	tx_bss = hostapd_mbssid_get_tx_bss(hapd);
+	len = hostapd_eid_mbssid_len(tx_bss, WLAN_FC_STYPE_BEACON, &elem_count,
+				     NULL, 0);
+	if (!len || (iface->conf->mbssid == ENHANCED_MBSSID_ENABLED &&
+		     elem_count > iface->ema_max_periodicity))
+		goto fail;
+
+	elem = os_zalloc(len);
+	if (!elem)
+		goto fail;
+
+	elem_offset = os_zalloc(elem_count * sizeof(u8 *));
+	if (!elem_offset)
+		goto fail;
+
+	end = hostapd_eid_mbssid(tx_bss, elem, elem + len, WLAN_FC_STYPE_BEACON,
+				 elem_count, elem_offset, NULL, 0);
+
+	params->mbssid_tx_iface = tx_bss->conf->iface;
+	params->mbssid_index = hostapd_mbssid_get_bss_index(hapd);
+	params->mbssid_elem = elem;
+	params->mbssid_elem_len = end - elem;
+	params->mbssid_elem_count = elem_count;
+	params->mbssid_elem_offset = elem_offset;
+	if (iface->conf->mbssid == ENHANCED_MBSSID_ENABLED)
+		params->ema = true;
+
+	return 0;
+
+fail:
+	os_free(elem);
+	wpa_printf(MSG_ERROR, "MBSSID: Configuration failed");
+	return -1;
+}
+
+
+static u8 * hostapd_eid_mbssid_config(struct hostapd_data *hapd, u8 *eid,
+				      u8 mbssid_elem_count)
+{
+	struct hostapd_iface *iface = hapd->iface;
+
+	if (iface->conf->mbssid == ENHANCED_MBSSID_ENABLED) {
+		*eid++ = WLAN_EID_EXTENSION;
+		*eid++ = 3;
+		*eid++ = WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION;
+		*eid++ = iface->num_bss;
+		*eid++ = mbssid_elem_count;
+	}
+
+	return eid;
+}
+
+
 static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
 				   const struct ieee80211_mgmt *req,
 				   int is_p2p, size_t *resp_len,
-				   bool bcast_probe_resp)
+				   bool bcast_probe_resp, const u8 *known_bss,
+				   u8 known_bss_len)
 {
 	struct ieee80211_mgmt *resp;
 	u8 *pos, *epos, *csa_pos;
 	size_t buflen;
 
+	hapd = hostapd_mbssid_get_tx_bss(hapd);
+
 #define MAX_PROBERESP_LEN 768
 	buflen = MAX_PROBERESP_LEN;
 #ifdef CONFIG_WPS
@@ -517,6 +588,8 @@
 	}
 #endif /* CONFIG_IEEE80211BE */
 
+	buflen += hostapd_eid_mbssid_len(hapd, WLAN_FC_STYPE_PROBE_RESP, NULL,
+					 known_bss, known_bss_len);
 	buflen += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_PROBE_RESP);
 	buflen += hostapd_mbo_ie_len(hapd);
 	buflen += hostapd_eid_owe_trans_len(hapd);
@@ -576,6 +649,8 @@
 
 	pos = hostapd_get_rsne(hapd, pos, epos - pos);
 	pos = hostapd_eid_bss_load(hapd, pos, epos - pos);
+	pos = hostapd_eid_mbssid(hapd, pos, epos, WLAN_FC_STYPE_PROBE_RESP, 0,
+				 NULL, known_bss, known_bss_len);
 	pos = hostapd_eid_rm_enabled_capab(hapd, pos, epos - pos);
 	pos = hostapd_get_mde(hapd, pos, epos - pos);
 
@@ -589,7 +664,11 @@
 	pos = hostapd_eid_ht_capabilities(hapd, pos);
 	pos = hostapd_eid_ht_operation(hapd, pos);
 
-	pos = hostapd_eid_ext_capab(hapd, pos);
+	/* Probe Response frames always include all non-TX profiles except
+	 * when a list of known BSSes is included in the Probe Request frame. */
+	pos = hostapd_eid_ext_capab(hapd, pos,
+				    hapd->iconf->mbssid >= MBSSID_ENABLED &&
+				    !known_bss_len);
 
 	pos = hostapd_eid_time_adv(hapd, pos);
 	pos = hostapd_eid_time_zone(hapd, pos);
@@ -1141,11 +1220,18 @@
 	}
 #endif /* CONFIG_TESTING_OPTIONS */
 
+	/* Do not send Probe Response frame from a non-transmitting multiple
+	 * BSSID profile unless the Probe Request frame is directed at that
+	 * particular BSS. */
+	if (hapd != hostapd_mbssid_get_tx_bss(hapd) && res != EXACT_SSID_MATCH)
+		return;
+
 	wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, RX_PROBE_REQUEST "sa=" MACSTR
 		     " signal=%d", MAC2STR(mgmt->sa), ssi_signal);
 
 	resp = hostapd_gen_probe_resp(hapd, mgmt, elems.p2p != NULL,
-				      &resp_len, false);
+				      &resp_len, false, elems.mbssid_known_bss,
+				      elems.mbssid_known_bss_len);
 	if (resp == NULL)
 		return;
 
@@ -1167,7 +1253,8 @@
 				hapd->cs_c_off_ecsa_proberesp;
 	}
 
-	ret = hostapd_drv_send_mlme(hapd, resp, resp_len, noack,
+	ret = hostapd_drv_send_mlme(hostapd_mbssid_get_tx_bss(hapd), resp,
+				    resp_len, noack,
 				    csa_offs_len ? csa_offs : NULL,
 				    csa_offs_len, 0);
 
@@ -1214,7 +1301,7 @@
 			   "this");
 
 	/* Generate a Probe Response template for the non-P2P case */
-	return hostapd_gen_probe_resp(hapd, NULL, 0, resp_len, false);
+	return hostapd_gen_probe_resp(hapd, NULL, 0, resp_len, false, NULL, 0);
 }
 
 #endif /* NEED_AP_MLME */
@@ -1233,7 +1320,7 @@
 
 	return hostapd_gen_probe_resp(hapd, NULL, 0,
 				      &params->unsol_bcast_probe_resp_tmpl_len,
-				      true);
+				      true, NULL, 0);
 }
 #endif /* CONFIG_IEEE80211AX */
 
@@ -1514,7 +1601,12 @@
 #ifdef NEED_AP_MLME
 	u16 capab_info;
 	u8 *pos, *tailpos, *tailend, *csa_pos;
+	bool complete = false;
+#endif /* NEED_AP_MLME */
 
+	os_memset(params, 0, sizeof(*params));
+
+#ifdef NEED_AP_MLME
 #define BEACON_HEAD_BUF_SIZE 256
 #define BEACON_TAIL_BUF_SIZE 512
 	head = os_zalloc(BEACON_HEAD_BUF_SIZE);
@@ -1566,6 +1658,9 @@
 	}
 #endif /* CONFIG_IEEE80211BE */
 
+	if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
+	    hapd == hostapd_mbssid_get_tx_bss(hapd))
+		tail_len += 5; /* Multiple BSSID Configuration element */
 	tail_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_BEACON);
 	tail_len += hostapd_mbo_ie_len(hapd);
 	tail_len += hostapd_eid_owe_trans_len(hapd);
@@ -1652,7 +1747,20 @@
 	tailpos = hostapd_eid_ht_capabilities(hapd, tailpos);
 	tailpos = hostapd_eid_ht_operation(hapd, tailpos);
 
-	tailpos = hostapd_eid_ext_capab(hapd, tailpos);
+	if (hapd->iconf->mbssid && hapd->iconf->num_bss > 1) {
+		if (ieee802_11_build_ap_params_mbssid(hapd, params)) {
+			os_free(head);
+			os_free(tail);
+			wpa_printf(MSG_ERROR,
+				   "MBSSID: Failed to set beacon data");
+			return -1;
+		}
+		complete = hapd->iconf->mbssid == MBSSID_ENABLED ||
+			(hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
+			 params->mbssid_elem_count == 1);
+	}
+
+	tailpos = hostapd_eid_ext_capab(hapd, tailpos, complete);
 
 	/*
 	 * TODO: Time Advertisement element should only be included in some
@@ -1692,6 +1800,8 @@
 	tailpos = hostapd_eid_rnr(hapd, tailpos, WLAN_FC_STYPE_BEACON);
 	tailpos = hostapd_eid_fils_indic(hapd, tailpos, 0);
 	tailpos = hostapd_get_rsnxe(hapd, tailpos, tailend - tailpos);
+	tailpos = hostapd_eid_mbssid_config(hapd, tailpos,
+					    params->mbssid_elem_count);
 
 #ifdef CONFIG_IEEE80211AX
 	if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
@@ -1774,7 +1884,6 @@
 	resp = hostapd_probe_resp_offloads(hapd, &resp_len);
 #endif /* NEED_AP_MLME */
 
-	os_memset(params, 0, sizeof(*params));
 	params->head = (u8 *) head;
 	params->head_len = head_len;
 	params->tail = tail;
@@ -1877,6 +1986,10 @@
 	params->head = NULL;
 	os_free(params->proberesp);
 	params->proberesp = NULL;
+	os_free(params->mbssid_elem);
+	params->mbssid_elem = NULL;
+	os_free(params->mbssid_elem_offset);
+	params->mbssid_elem_offset = NULL;
 #ifdef CONFIG_FILS
 	os_free(params->fd_frame_tmpl);
 	params->fd_frame_tmpl = NULL;
diff --git a/src/ap/comeback_token.c b/src/ap/comeback_token.c
new file mode 100644
index 0000000..8d9f21b
--- /dev/null
+++ b/src/ap/comeback_token.c
@@ -0,0 +1,139 @@
+/*
+ * hostapd / Comeback token mechanism for SAE
+ * Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "hostapd.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "common/ieee802_11_defs.h"
+#include "comeback_token.h"
+
+
+#if defined(CONFIG_SAE) || defined(CONFIG_PASN)
+
+static int comeback_token_hash(const u8 *comeback_key, const u8 *addr, u8 *idx)
+{
+	u8 hash[SHA256_MAC_LEN];
+
+	if (hmac_sha256(comeback_key, COMEBACK_KEY_SIZE,
+			addr, ETH_ALEN, hash) < 0)
+		return -1;
+	*idx = hash[0];
+	return 0;
+}
+
+
+int check_comeback_token(const u8 *comeback_key,
+			 u16 *comeback_pending_idx, const u8 *addr,
+			 const u8 *token, size_t token_len)
+{
+	u8 mac[SHA256_MAC_LEN];
+	const u8 *addrs[2];
+	size_t len[2];
+	u16 token_idx;
+	u8 idx;
+
+	if (token_len != SHA256_MAC_LEN ||
+	    comeback_token_hash(comeback_key, addr, &idx) < 0)
+		return -1;
+	token_idx = comeback_pending_idx[idx];
+	if (token_idx == 0 || token_idx != WPA_GET_BE16(token)) {
+		wpa_printf(MSG_DEBUG,
+			   "Comeback: Invalid anti-clogging token from "
+			   MACSTR " - token_idx 0x%04x, expected 0x%04x",
+			   MAC2STR(addr), WPA_GET_BE16(token), token_idx);
+		return -1;
+	}
+
+	addrs[0] = addr;
+	len[0] = ETH_ALEN;
+	addrs[1] = token;
+	len[1] = 2;
+	if (hmac_sha256_vector(comeback_key, COMEBACK_KEY_SIZE,
+			       2, addrs, len, mac) < 0 ||
+	    os_memcmp_const(token + 2, &mac[2], SHA256_MAC_LEN - 2) != 0)
+		return -1;
+
+	comeback_pending_idx[idx] = 0; /* invalidate used token */
+
+	return 0;
+}
+
+
+struct wpabuf *
+auth_build_token_req(struct os_reltime *last_comeback_key_update,
+		     u8 *comeback_key, u16 comeback_idx,
+		     u16 *comeback_pending_idx, size_t idx_len,
+		     int group, const u8 *addr, int h2e)
+{
+	struct wpabuf *buf;
+	u8 *token;
+	struct os_reltime now;
+	u8 idx[2];
+	const u8 *addrs[2];
+	size_t len[2];
+	u8 p_idx;
+	u16 token_idx;
+
+	os_get_reltime(&now);
+	if (!os_reltime_initialized(last_comeback_key_update) ||
+	    os_reltime_expired(&now, last_comeback_key_update, 60) ||
+	    comeback_idx == 0xffff) {
+		if (random_get_bytes(comeback_key, COMEBACK_KEY_SIZE) < 0)
+			return NULL;
+		wpa_hexdump(MSG_DEBUG, "Comeback: Updated token key",
+			    comeback_key, COMEBACK_KEY_SIZE);
+		*last_comeback_key_update = now;
+		comeback_idx = 0;
+		os_memset(comeback_pending_idx, 0, idx_len);
+	}
+
+	buf = wpabuf_alloc(sizeof(le16) + 3 + SHA256_MAC_LEN);
+	if (buf == NULL)
+		return NULL;
+
+	if (group)
+		wpabuf_put_le16(buf, group); /* Finite Cyclic Group */
+
+	if (h2e) {
+		/* Encapsulate Anti-clogging Token field in a container IE */
+		wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+		wpabuf_put_u8(buf, 1 + SHA256_MAC_LEN);
+		wpabuf_put_u8(buf, WLAN_EID_EXT_ANTI_CLOGGING_TOKEN);
+	}
+
+	if (comeback_token_hash(comeback_key, addr, &p_idx) < 0) {
+		wpabuf_free(buf);
+		return NULL;
+	}
+
+	token_idx = comeback_pending_idx[p_idx];
+	if (!token_idx) {
+		comeback_idx++;
+		token_idx = comeback_idx;
+		comeback_pending_idx[p_idx] = token_idx;
+	}
+	WPA_PUT_BE16(idx, token_idx);
+	token = wpabuf_put(buf, SHA256_MAC_LEN);
+	addrs[0] = addr;
+	len[0] = ETH_ALEN;
+	addrs[1] = idx;
+	len[1] = sizeof(idx);
+	if (hmac_sha256_vector(comeback_key, COMEBACK_KEY_SIZE,
+			       2, addrs, len, token) < 0) {
+		wpabuf_free(buf);
+		return NULL;
+	}
+	WPA_PUT_BE16(token, token_idx);
+
+	return buf;
+}
+
+#endif /* defined(CONFIG_SAE) || defined(CONFIG_PASN) */
diff --git a/src/ap/comeback_token.h b/src/ap/comeback_token.h
new file mode 100644
index 0000000..d5de9e6
--- /dev/null
+++ b/src/ap/comeback_token.h
@@ -0,0 +1,21 @@
+/*
+ * hostapd / Comeback token mechanism for SAE
+ * Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef COMEBACK_TOKEN_H
+#define COMEBACK_TOKEN_H
+
+int check_comeback_token(const u8 *comeback_key,
+			 u16 *comeback_pending_idx, const u8 *addr,
+			 const u8 *token, size_t token_len);
+struct wpabuf *
+auth_build_token_req(struct os_reltime *last_comeback_key_update,
+		     u8 *comeback_key, u16 comeback_idx,
+		     u16 *comeback_pending_idx, size_t idx_len,
+		     int group, const u8 *addr, int h2e);
+
+#endif /* COMEBACK_TOKEN_H */
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index ac8f6fa..168e5f5 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -99,7 +99,7 @@
 	len += ret;
 
 	ret = os_snprintf(buf + len, buflen - len, "rx_rate_info=%lu",
-			  data.current_rx_rate);
+			  data.current_rx_rate / 100);
 	if (os_snprintf_error(buflen - len, ret))
 		return len;
 	len += ret;
@@ -131,7 +131,7 @@
 		len += ret;
 
 	ret = os_snprintf(buf + len, buflen - len, "tx_rate_info=%lu",
-			  data.current_tx_rate);
+			  data.current_tx_rate / 100);
 	if (os_snprintf_error(buflen - len, ret))
 		return len;
 	len += ret;
@@ -206,9 +206,9 @@
 		return "REMOVE";
 	case STA_DISASSOC_FROM_CLI:
 		return "DISASSOC_FROM_CLI";
+	default:
+		return "?";
 	}
-
-	return "?";
 }
 
 
@@ -218,6 +218,7 @@
 {
 	int len, res, ret, i;
 	const char *keyid;
+	const u8 *dpp_pkhash;
 
 	if (!sta)
 		return 0;
@@ -386,6 +387,18 @@
 			len += ret;
 	}
 
+	dpp_pkhash = ap_sta_wpa_get_dpp_pkhash(hapd, sta);
+	if (dpp_pkhash) {
+		ret = os_snprintf(buf + len, buflen - len, "dpp_pkhash=");
+		if (!os_snprintf_error(buflen - len, ret))
+			len += ret;
+		len += wpa_snprintf_hex(buf + len, buflen - len, dpp_pkhash,
+					SHA256_MAC_LEN);
+		ret = os_snprintf(buf + len, buflen - len, "\n");
+		if (!os_snprintf_error(buflen - len, ret))
+			len += ret;
+	}
+
 	return len;
 }
 
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
index 7f31f28..e8c5ec9 100644
--- a/src/ap/dfs.c
+++ b/src/ap/dfs.c
@@ -445,6 +445,8 @@
 	mode = iface->current_mode;
 
 	for (i = 0; i < n_chans; i++) {
+		if (start_chan_idx + i >= mode->num_channels)
+			break;
 		channel = &mode->channels[start_chan_idx + i];
 		if (channel->flag & HOSTAPD_CHAN_RADAR)
 			res++;
@@ -797,6 +799,8 @@
 	mode = iface->current_mode;
 
 	for (i = 0; i < n_chans; i++) {
+		if (start_chan_idx + i >= mode->num_channels)
+			break;
 		channel = &mode->channels[start_chan_idx + i];
 		if (!(channel->flag & HOSTAPD_CHAN_RADAR))
 			continue;
diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c
index 32ddb3b..70dd18e 100644
--- a/src/ap/dpp_hostapd.c
+++ b/src/ap/dpp_hostapd.c
@@ -1378,8 +1378,7 @@
 static void hostapd_dpp_auth_success(struct hostapd_data *hapd, int initiator)
 {
 	wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded");
-	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_SUCCESS "init=%d",
-		initiator);
+	dpp_notify_auth_success(hapd->dpp_auth, initiator);
 #ifdef CONFIG_TESTING_OPTIONS
 	if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
 		wpa_printf(MSG_INFO,
@@ -2052,6 +2051,7 @@
 	os_time_t expire;
 	int expiration;
 	enum dpp_status_error res;
+	u8 pkhash[SHA256_MAC_LEN];
 
 	os_memset(&intro, 0, sizeof(intro));
 
@@ -2088,7 +2088,7 @@
 			     wpabuf_len(hapd->conf->dpp_netaccesskey),
 			     wpabuf_head(hapd->conf->dpp_csign),
 			     wpabuf_len(hapd->conf->dpp_csign),
-			     connector, connector_len, &expire);
+			     connector, connector_len, &expire, pkhash);
 	if (res == 255) {
 		wpa_printf(MSG_INFO,
 			   "DPP: Network Introduction protocol resulted in internal failure (peer "
@@ -2133,9 +2133,9 @@
 	else
 		expiration = 0;
 
-	if (wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
+	if (wpa_auth_pmksa_add3(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
 				intro.pmkid, expiration,
-				WPA_KEY_MGMT_DPP) < 0) {
+				WPA_KEY_MGMT_DPP, pkhash) < 0) {
 		wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry");
 		goto done;
 	}
@@ -2793,6 +2793,7 @@
 	os_time_t expire;
 	int expiration;
 	enum dpp_status_error res;
+	u8 pkhash[SHA256_MAC_LEN];
 
 	os_memset(&intro, 0, sizeof(intro));
 
@@ -2870,7 +2871,7 @@
 			     wpabuf_len(hapd->conf->dpp_netaccesskey),
 			     wpabuf_head(hapd->conf->dpp_csign),
 			     wpabuf_len(hapd->conf->dpp_csign),
-			     connector, connector_len, &expire);
+			     connector, connector_len, &expire, pkhash);
 	if (res == 255) {
 		wpa_printf(MSG_INFO,
 			   "DPP: Network Introduction protocol resulted in internal failure (peer "
@@ -2904,9 +2905,9 @@
 	else
 		expiration = 0;
 
-	if (wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
+	if (wpa_auth_pmksa_add3(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
 				intro.pmkid, expiration,
-				WPA_KEY_MGMT_DPP) < 0) {
+				WPA_KEY_MGMT_DPP, pkhash) < 0) {
 		wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry");
 		goto done;
 	}
@@ -3089,8 +3090,7 @@
 		 * from TX status handler, but since there was no such handler
 		 * call yet, simply send out the event message and proceed with
 		 * exchange. */
-		wpa_msg(hapd->msg_ctx, MSG_INFO,
-			DPP_EVENT_AUTH_SUCCESS "init=1");
+		dpp_notify_auth_success(hapd->dpp_auth, 1);
 		hapd->dpp_auth_ok_on_ack = 0;
 	}
 
@@ -3376,7 +3376,7 @@
 {
 	struct hostapd_data *hapd = ctx;
 
-	gas_serv_req_dpp_processing(hapd, addr, dialog_token, prot, buf);
+	gas_serv_req_dpp_processing(hapd, addr, dialog_token, prot, buf, 0);
 }
 
 #endif /* CONFIG_DPP2 */
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index b1cb31e..4c33e86 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -451,7 +451,7 @@
 		}
 #endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_SAE
-		if (hapd->conf->sae_pwe == 2 &&
+		if (hapd->conf->sae_pwe == SAE_PWE_BOTH &&
 		    sta->auth_alg == WLAN_AUTH_SAE &&
 		    sta->sae && !sta->sae->h2e &&
 		    ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len,
@@ -862,14 +862,16 @@
 			     int finished)
 {
 #ifdef NEED_AP_MLME
-	int channel, chwidth, is_dfs;
+	int channel, chwidth, is_dfs0, is_dfs;
 	u8 seg0_idx = 0, seg1_idx = 0;
 	size_t i;
 
 	hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
 		       HOSTAPD_LEVEL_INFO,
-		       "driver %s channel switch: freq=%d, ht=%d, vht_ch=0x%x, he_ch=0x%x, eht_ch=0x%x, offset=%d, width=%d (%s), cf1=%d, cf2=%d",
+		       "driver %s channel switch: iface->freq=%d, freq=%d, ht=%d, vht_ch=0x%x, "
+		       "he_ch=0x%x, eht_ch=0x%x, offset=%d, width=%d (%s), cf1=%d, cf2=%d",
 		       finished ? "had" : "starting",
+		       hapd->iface->freq,
 		       freq, ht, hapd->iconf->ch_switch_vht_config,
 		       hapd->iconf->ch_switch_he_config,
 		       hapd->iconf->ch_switch_eht_config, offset,
@@ -882,6 +884,8 @@
 		return;
 	}
 
+	/* Check if any of configured channels require DFS */
+	is_dfs0 = hostapd_is_dfs_required(hapd->iface);
 	hapd->iface->freq = freq;
 
 	channel = hostapd_hw_get_channel(hapd, freq);
@@ -997,11 +1001,11 @@
 				  hapd->iface->num_hw_features);
 
 	wpa_msg(hapd->msg_ctx, MSG_INFO,
-		"%sfreq=%d ht_enabled=%d ch_offset=%d ch_width=%s cf1=%d cf2=%d dfs=%d",
+		"%sfreq=%d ht_enabled=%d ch_offset=%d ch_width=%s cf1=%d cf2=%d is_dfs0=%d dfs=%d",
 		finished ? WPA_EVENT_CHANNEL_SWITCH :
 		WPA_EVENT_CHANNEL_SWITCH_STARTED,
 		freq, ht, offset, channel_width_to_string(width),
-		cf1, cf2, is_dfs);
+		cf1, cf2, is_dfs0, is_dfs);
 	if (!finished)
 		return;
 
@@ -1013,6 +1017,14 @@
 		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_CSA_FINISHED
 			"freq=%d dfs=%d", freq, is_dfs);
 	} else if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) {
+		/* Complete AP configuration for the first bring up. */
+		if (is_dfs0 > 0 &&
+		    hostapd_is_dfs_required(hapd->iface) <= 0 &&
+		    hapd->iface->state != HAPD_IFACE_ENABLED) {
+			/* Fake a CAC start bit to skip setting channel */
+			hapd->iface->cac_started = 1;
+			hostapd_setup_interface_complete(hapd->iface, 0);
+		}
 		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_CSA_FINISHED
 			"freq=%d dfs=%d", freq, is_dfs);
 	} else if (is_dfs &&
diff --git a/src/ap/fils_hlp.c b/src/ap/fils_hlp.c
index 0310aab..d64fb8c 100644
--- a/src/ap/fils_hlp.c
+++ b/src/ap/fils_hlp.c
@@ -530,9 +530,9 @@
 	switch (iph->ip_p) {
 	case 17:
 		return fils_process_hlp_udp(hapd, sta, dst, pos, len);
+	default:
+		return 0;
 	}
-
-	return 0;
 }
 
 
@@ -567,9 +567,9 @@
 	case ETH_P_IP:
 		return fils_process_hlp_ip(hapd, sta, pos, pkt + 2,
 					   end - pkt - 2);
+	default:
+		return 0;
 	}
-
-	return 0;
 }
 
 
diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c
index 90f1577..4642e49 100644
--- a/src/ap/gas_serv.c
+++ b/src/ap/gas_serv.c
@@ -1524,7 +1524,7 @@
 #ifdef CONFIG_DPP
 void gas_serv_req_dpp_processing(struct hostapd_data *hapd,
 				 const u8 *sa, u8 dialog_token,
-				 int prot, struct wpabuf *buf)
+				 int prot, struct wpabuf *buf, int freq)
 {
 	struct wpabuf *tx_buf;
 
@@ -1582,7 +1582,7 @@
 		return;
 	if (prot)
 		convert_to_protected_dual(tx_buf);
-	hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
+	hostapd_drv_send_action(hapd, freq ? freq : hapd->iface->freq, 0, sa,
 				wpabuf_head(tx_buf),
 				wpabuf_len(tx_buf));
 	wpabuf_free(tx_buf);
@@ -1593,7 +1593,7 @@
 static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
 					const u8 *sa,
 					const u8 *data, size_t len, int prot,
-					int std_addr3)
+					int std_addr3, int freq)
 {
 	const u8 *pos = data;
 	const u8 *end = data + len;
@@ -1688,7 +1688,8 @@
 						  data, len);
 		if (!msg)
 			return;
-		gas_serv_req_dpp_processing(hapd, sa, dialog_token, prot, msg);
+		gas_serv_req_dpp_processing(hapd, sa, dialog_token, prot, msg,
+					    freq);
 		return;
 	}
 #endif /* CONFIG_DPP */
@@ -1871,7 +1872,7 @@
 	switch (data[0]) {
 	case WLAN_PA_GAS_INITIAL_REQ:
 		gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot,
-					    std_addr3);
+					    std_addr3, freq);
 		break;
 	case WLAN_PA_GAS_COMEBACK_REQ:
 		gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot,
diff --git a/src/ap/gas_serv.h b/src/ap/gas_serv.h
index 1528af4..7646a98 100644
--- a/src/ap/gas_serv.h
+++ b/src/ap/gas_serv.h
@@ -90,6 +90,6 @@
 
 void gas_serv_req_dpp_processing(struct hostapd_data *hapd,
 				 const u8 *sa, u8 dialog_token,
-				 int prot, struct wpabuf *buf);
+				 int prot, struct wpabuf *buf, int freq);
 
 #endif /* GAS_SERV_H */
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 3681df6..58492e5 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -91,6 +91,29 @@
 }
 
 
+struct hostapd_data * hostapd_mbssid_get_tx_bss(struct hostapd_data *hapd)
+{
+	if (hapd->iconf->mbssid)
+		return hapd->iface->bss[0];
+
+	return hapd;
+}
+
+
+int hostapd_mbssid_get_bss_index(struct hostapd_data *hapd)
+{
+	if (hapd->iconf->mbssid) {
+		size_t i;
+
+		for (i = 1; i < hapd->iface->num_bss; i++)
+			if (hapd->iface->bss[i] == hapd)
+				return i;
+	}
+
+	return 0;
+}
+
+
 void hostapd_reconfig_encryption(struct hostapd_data *hapd)
 {
 	if (hapd->wpa_auth)
@@ -172,27 +195,34 @@
 }
 
 
-static void hostapd_clear_old(struct hostapd_iface *iface)
+static void hostapd_clear_old_bss(struct hostapd_data *bss)
 {
-	size_t j;
+	wpa_printf(MSG_DEBUG, "BSS %s changed - clear old state",
+		   bss->conf->iface);
 
 	/*
 	 * Deauthenticate all stations since the new configuration may not
 	 * allow them to use the BSS anymore.
 	 */
-	for (j = 0; j < iface->num_bss; j++) {
-		hostapd_flush_old_stations(iface->bss[j],
-					   WLAN_REASON_PREV_AUTH_NOT_VALID);
+	hostapd_flush_old_stations(bss, WLAN_REASON_PREV_AUTH_NOT_VALID);
 #ifdef CONFIG_WEP
-		hostapd_broadcast_wep_clear(iface->bss[j]);
+	hostapd_broadcast_wep_clear(bss);
 #endif /* CONFIG_WEP */
 
 #ifndef CONFIG_NO_RADIUS
-		/* TODO: update dynamic data based on changed configuration
-		 * items (e.g., open/close sockets, etc.) */
-		radius_client_flush(iface->bss[j]->radius, 0);
+	/* TODO: update dynamic data based on changed configuration
+	 * items (e.g., open/close sockets, etc.) */
+	radius_client_flush(bss->radius, 0);
 #endif /* CONFIG_NO_RADIUS */
-	}
+}
+
+
+static void hostapd_clear_old(struct hostapd_iface *iface)
+{
+	size_t j;
+
+	for (j = 0; j < iface->num_bss; j++)
+		hostapd_clear_old_bss(iface->bss[j]);
 }
 
 
@@ -236,13 +266,13 @@
 	if (newconf == NULL)
 		return -1;
 
-	hostapd_clear_old(iface);
-
 	oldconf = hapd->iconf;
 	if (hostapd_iface_conf_changed(newconf, oldconf)) {
 		char *fname;
 		int res;
 
+		hostapd_clear_old(iface);
+
 		wpa_printf(MSG_DEBUG,
 			   "Configuration changes include interface/BSS modification - force full disable+enable sequence");
 		fname = os_strdup(iface->config_fname);
@@ -272,6 +302,10 @@
 
 	for (j = 0; j < iface->num_bss; j++) {
 		hapd = iface->bss[j];
+		if (!hapd->conf->config_id || !newconf->bss[j]->config_id ||
+		    os_strcmp(hapd->conf->config_id,
+			      newconf->bss[j]->config_id) != 0)
+			hostapd_clear_old_bss(hapd);
 		hapd->iconf = newconf;
 		hapd->iconf->channel = oldconf->channel;
 		hapd->iconf->acs = oldconf->acs;
@@ -525,6 +559,7 @@
 	iface->current_rates = NULL;
 	os_free(iface->basic_rates);
 	iface->basic_rates = NULL;
+	iface->cac_started = 0;
 	ap_list_deinit(iface);
 	sta_track_deinit(iface);
 	airtime_policy_update_deinit(iface);
@@ -1104,18 +1139,55 @@
 #endif /* CONFIG_NO_RADIUS */
 
 
+static int hostapd_start_beacon(struct hostapd_data *hapd,
+				bool flush_old_stations)
+{
+	struct hostapd_bss_config *conf = hapd->conf;
+
+	if (!conf->start_disabled && ieee802_11_set_beacon(hapd) < 0)
+		return -1;
+
+	if (flush_old_stations && !conf->start_disabled &&
+	    conf->broadcast_deauth) {
+		u8 addr[ETH_ALEN];
+
+		/* Should any previously associated STA not have noticed that
+		 * the AP had stopped and restarted, send one more
+		 * deauthentication notification now that the AP is ready to
+		 * operate. */
+		wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
+			"Deauthenticate all stations at BSS start");
+		os_memset(addr, 0xff, ETH_ALEN);
+		hostapd_drv_sta_deauth(hapd, addr,
+				       WLAN_REASON_PREV_AUTH_NOT_VALID);
+	}
+
+	if (hapd->driver && hapd->driver->set_operstate)
+		hapd->driver->set_operstate(hapd->drv_priv, 1);
+
+	return 0;
+}
+
+
 /**
  * hostapd_setup_bss - Per-BSS setup (initialization)
  * @hapd: Pointer to BSS data
  * @first: Whether this BSS is the first BSS of an interface; -1 = not first,
  *	but interface may exist
+ * @start_beacon: Whether Beacon frame template should be configured and
+ *	transmission of Beaconf rames started at this time. This is used when
+ *	MBSSID element is enabled where the information regarding all BSSes
+ *	should be retrieved before configuring the Beacon frame template. The
+ *	calling functions are responsible for configuring the Beacon frame
+ *	explicitly if this is set to false.
  *
  * This function is used to initialize all per-BSS data structures and
  * resources. This gets called in a loop for each BSS when an interface is
  * initialized. Most of the modules that are initialized here will be
  * deinitialized in hostapd_cleanup().
  */
-static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
+static int hostapd_setup_bss(struct hostapd_data *hapd, int first,
+			     bool start_beacon)
 {
 	struct hostapd_bss_config *conf = hapd->conf;
 	u8 ssid[SSID_MAX_LEN + 1];
@@ -1388,29 +1460,11 @@
 		return -1;
 	}
 
-	if (!conf->start_disabled && ieee802_11_set_beacon(hapd) < 0)
-		return -1;
-
-	if (flush_old_stations && !conf->start_disabled &&
-	    conf->broadcast_deauth) {
-		u8 addr[ETH_ALEN];
-
-		/* Should any previously associated STA not have noticed that
-		 * the AP had stopped and restarted, send one more
-		 * deauthentication notification now that the AP is ready to
-		 * operate. */
-		wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
-			"Deauthenticate all stations at BSS start");
-		os_memset(addr, 0xff, ETH_ALEN);
-		hostapd_drv_sta_deauth(hapd, addr,
-				       WLAN_REASON_PREV_AUTH_NOT_VALID);
-	}
-
 	if (hapd->wpa_auth && wpa_init_keys(hapd->wpa_auth) < 0)
 		return -1;
 
-	if (hapd->driver && hapd->driver->set_operstate)
-		hapd->driver->set_operstate(hapd->drv_priv, 1);
+	if (start_beacon)
+		return hostapd_start_beacon(hapd, flush_old_stations);
 
 	return 0;
 }
@@ -2135,7 +2189,7 @@
 		hapd = iface->bss[j];
 		if (j)
 			os_memcpy(hapd->own_addr, prev_addr, ETH_ALEN);
-		if (hostapd_setup_bss(hapd, j == 0)) {
+		if (hostapd_setup_bss(hapd, j == 0, !iface->conf->mbssid)) {
 			for (;;) {
 				hapd = iface->bss[j];
 				hostapd_bss_deinit_no_free(hapd);
@@ -2149,6 +2203,24 @@
 		if (is_zero_ether_addr(hapd->conf->bssid))
 			prev_addr = hapd->own_addr;
 	}
+
+	if (hapd->iconf->mbssid) {
+		for (j = 0; hapd->iconf->mbssid && j < iface->num_bss; j++) {
+			hapd = iface->bss[j];
+			if (hostapd_start_beacon(hapd, true)) {
+				for (;;) {
+					hapd = iface->bss[j];
+					hostapd_bss_deinit_no_free(hapd);
+					hostapd_free_hapd_data(hapd);
+					if (j == 0)
+						break;
+					j--;
+				}
+				goto fail;
+			}
+		}
+	}
+
 	hapd = iface->bss[0];
 
 	hostapd_tx_queue_params(iface);
@@ -2802,6 +2874,21 @@
 }
 
 
+int hostapd_reload_bss_only(struct hostapd_data *bss)
+{
+
+	wpa_printf(MSG_DEBUG, "Reload BSS %s", bss->conf->iface);
+	hostapd_set_security_params(bss->conf, 1);
+	if (hostapd_config_check(bss->iconf, 1) < 0) {
+		wpa_printf(MSG_ERROR, "Updated BSS configuration is invalid");
+		return -1;
+	}
+	hostapd_clear_old_bss(bss);
+	hostapd_reload_bss(bss);
+	return 0;
+}
+
+
 int hostapd_disable_iface(struct hostapd_iface *hapd_iface)
 {
 	size_t j;
@@ -3030,7 +3117,7 @@
 
 			if (start_ctrl_iface_bss(hapd) < 0 ||
 			    (hapd_iface->state == HAPD_IFACE_ENABLED &&
-			     hostapd_setup_bss(hapd, -1))) {
+			     hostapd_setup_bss(hapd, -1, true))) {
 				hostapd_cleanup(hapd);
 				hapd_iface->bss[hapd_iface->num_bss - 1] = NULL;
 				hapd_iface->conf->num_bss--;
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index e121362..a88f9b6 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -352,11 +352,15 @@
 #endif /* CONFIG_SQLITE */
 
 #ifdef CONFIG_SAE
+
+#define COMEBACK_KEY_SIZE 8
+#define COMEBACK_PENDING_IDX_SIZE 256
+
 	/** Key used for generating SAE anti-clogging tokens */
-	u8 comeback_key[8];
+	u8 comeback_key[COMEBACK_KEY_SIZE];
 	struct os_reltime last_comeback_key_update;
 	u16 comeback_idx;
-	u16 comeback_pending_idx[256];
+	u16 comeback_pending_idx[COMEBACK_PENDING_IDX_SIZE];
 	int dot11RSNASAERetransPeriod; /* msec */
 	struct dl_list sae_commit_queue; /* struct hostapd_sae_commit_queue */
 #endif /* CONFIG_SAE */
@@ -640,6 +644,11 @@
 	/* Previous WMM element information */
 	struct hostapd_wmm_ac_params prev_wmm[WMM_AC_NUM];
 
+	/* Maximum number of interfaces supported for MBSSID advertisement */
+	unsigned int mbssid_max_interfaces;
+	/* Maximum profile periodicity for enhanced MBSSID advertisement */
+	unsigned int ema_max_periodicity;
+
 	int (*enable_iface_cb)(struct hostapd_iface *iface);
 	int (*disable_iface_cb)(struct hostapd_iface *iface);
 };
@@ -669,6 +678,7 @@
 void hostapd_interface_deinit_free(struct hostapd_iface *iface);
 int hostapd_enable_iface(struct hostapd_iface *hapd_iface);
 int hostapd_reload_iface(struct hostapd_iface *hapd_iface);
+int hostapd_reload_bss_only(struct hostapd_data *bss);
 int hostapd_disable_iface(struct hostapd_iface *hapd_iface);
 void hostapd_bss_deinit_no_free(struct hostapd_data *hapd);
 void hostapd_free_hapd_data(struct hostapd_data *hapd);
@@ -740,5 +750,7 @@
 #endif /* CONFIG_FST */
 
 int hostapd_set_acl(struct hostapd_data *hapd);
+struct hostapd_data * hostapd_mbssid_get_tx_bss(struct hostapd_data *hapd);
+int hostapd_mbssid_get_bss_index(struct hostapd_data *hapd);
 
 #endif /* HOSTAPD_H */
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index a65a296..e53f0dc 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -55,6 +55,8 @@
 #include "fils_hlp.h"
 #include "dpp_hostapd.h"
 #include "gas_query_ap.h"
+#include "comeback_token.h"
+#include "pasn/pasn_common.h"
 
 
 #ifdef CONFIG_FILS
@@ -68,11 +70,6 @@
 #endif /* CONFIG_FILS */
 
 #ifdef CONFIG_PASN
-
-static int handle_auth_pasn_resp(struct hostapd_data *hapd,
-				 struct sta_info *sta,
-				 struct rsn_pmksa_cache_entry *pmksa,
-				 u16 status);
 #ifdef CONFIG_FILS
 
 static void pasn_fils_auth_resp(struct hostapd_data *hapd,
@@ -118,9 +115,13 @@
 		num++;
 	if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht)
 		num++;
-	h2e_required = (hapd->conf->sae_pwe == 1 ||
+#ifdef CONFIG_IEEE80211AX
+	if (hapd->iconf->ieee80211ax && hapd->iconf->require_he)
+		num++;
+#endif /* CONFIG_IEEE80211AX */
+	h2e_required = (hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
 			hostapd_sae_pw_id_in_use(hapd->conf) == 2) &&
-		hapd->conf->sae_pwe != 3 &&
+		hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK &&
 		wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt);
 	if (h2e_required)
 		num++;
@@ -150,6 +151,13 @@
 		*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY;
 	}
 
+#ifdef CONFIG_IEEE80211AX
+	if (hapd->iconf->ieee80211ax && hapd->iconf->require_he && count < 8) {
+		count++;
+		*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HE_PHY;
+	}
+#endif /* CONFIG_IEEE80211AX */
+
 	if (h2e_required && count < 8) {
 		count++;
 		*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY;
@@ -165,6 +173,7 @@
 	int i, num, count;
 	int h2e_required;
 
+	hapd->conf->xrates_supported = false;
 	if (hapd->iface->current_rates == NULL)
 		return eid;
 
@@ -173,9 +182,13 @@
 		num++;
 	if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht)
 		num++;
-	h2e_required = (hapd->conf->sae_pwe == 1 ||
+#ifdef CONFIG_IEEE80211AX
+	if (hapd->iconf->ieee80211ax && hapd->iconf->require_he)
+		num++;
+#endif /* CONFIG_IEEE80211AX */
+	h2e_required = (hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
 			hostapd_sae_pw_id_in_use(hapd->conf) == 2) &&
-		hapd->conf->sae_pwe != 3 &&
+		hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK &&
 		wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt);
 	if (h2e_required)
 		num++;
@@ -208,12 +221,21 @@
 			*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY;
 	}
 
+#ifdef CONFIG_IEEE80211AX
+	if (hapd->iconf->ieee80211ax && hapd->iconf->require_he) {
+		count++;
+		if (count > 8)
+			*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HE_PHY;
+	}
+#endif /* CONFIG_IEEE80211AX */
+
 	if (h2e_required) {
 		count++;
 		if (count > 8)
 			*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY;
 	}
 
+	hapd->conf->xrates_supported = true;
 	return pos;
 }
 
@@ -561,7 +583,7 @@
 #endif /* CONFIG_SAE_PK */
 	}
 
-	if (rx_id && hapd->conf->sae_pwe != 3)
+	if (rx_id && hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
 		use_pt = 1;
 	else if (status_code == WLAN_STATUS_SUCCESS)
 		use_pt = 0;
@@ -737,124 +759,6 @@
 	return 0;
 }
 
-
-static int comeback_token_hash(struct hostapd_data *hapd, const u8 *addr,
-			       u8 *idx)
-{
-	u8 hash[SHA256_MAC_LEN];
-
-	if (hmac_sha256(hapd->comeback_key, sizeof(hapd->comeback_key),
-			addr, ETH_ALEN, hash) < 0)
-		return -1;
-	*idx = hash[0];
-	return 0;
-}
-
-
-static int check_comeback_token(struct hostapd_data *hapd, const u8 *addr,
-				const u8 *token, size_t token_len)
-{
-	u8 mac[SHA256_MAC_LEN];
-	const u8 *addrs[2];
-	size_t len[2];
-	u16 token_idx;
-	u8 idx;
-
-	if (token_len != SHA256_MAC_LEN ||
-	    comeback_token_hash(hapd, addr, &idx) < 0)
-		return -1;
-	token_idx = hapd->comeback_pending_idx[idx];
-	if (token_idx == 0 || token_idx != WPA_GET_BE16(token)) {
-		wpa_printf(MSG_DEBUG,
-			   "Comeback: Invalid anti-clogging token from "
-			   MACSTR " - token_idx 0x%04x, expected 0x%04x",
-			   MAC2STR(addr), WPA_GET_BE16(token), token_idx);
-		return -1;
-	}
-
-	addrs[0] = addr;
-	len[0] = ETH_ALEN;
-	addrs[1] = token;
-	len[1] = 2;
-	if (hmac_sha256_vector(hapd->comeback_key, sizeof(hapd->comeback_key),
-			       2, addrs, len, mac) < 0 ||
-	    os_memcmp_const(token + 2, &mac[2], SHA256_MAC_LEN - 2) != 0)
-		return -1;
-
-	hapd->comeback_pending_idx[idx] = 0; /* invalidate used token */
-
-	return 0;
-}
-
-
-static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd,
-					    int group, const u8 *addr, int h2e)
-{
-	struct wpabuf *buf;
-	u8 *token;
-	struct os_reltime now;
-	u8 idx[2];
-	const u8 *addrs[2];
-	size_t len[2];
-	u8 p_idx;
-	u16 token_idx;
-
-	os_get_reltime(&now);
-	if (!os_reltime_initialized(&hapd->last_comeback_key_update) ||
-	    os_reltime_expired(&now, &hapd->last_comeback_key_update, 60) ||
-	    hapd->comeback_idx == 0xffff) {
-		if (random_get_bytes(hapd->comeback_key,
-				     sizeof(hapd->comeback_key)) < 0)
-			return NULL;
-		wpa_hexdump(MSG_DEBUG, "Comeback: Updated token key",
-			    hapd->comeback_key, sizeof(hapd->comeback_key));
-		hapd->last_comeback_key_update = now;
-		hapd->comeback_idx = 0;
-		os_memset(hapd->comeback_pending_idx, 0,
-			  sizeof(hapd->comeback_pending_idx));
-	}
-
-	buf = wpabuf_alloc(sizeof(le16) + 3 + SHA256_MAC_LEN);
-	if (buf == NULL)
-		return NULL;
-
-	if (group)
-		wpabuf_put_le16(buf, group); /* Finite Cyclic Group */
-
-	if (h2e) {
-		/* Encapsulate Anti-clogging Token field in a container IE */
-		wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
-		wpabuf_put_u8(buf, 1 + SHA256_MAC_LEN);
-		wpabuf_put_u8(buf, WLAN_EID_EXT_ANTI_CLOGGING_TOKEN);
-	}
-
-	if (comeback_token_hash(hapd, addr, &p_idx) < 0) {
-		wpabuf_free(buf);
-		return NULL;
-	}
-
-	token_idx = hapd->comeback_pending_idx[p_idx];
-	if (!token_idx) {
-		hapd->comeback_idx++;
-		token_idx = hapd->comeback_idx;
-		hapd->comeback_pending_idx[p_idx] = token_idx;
-	}
-	WPA_PUT_BE16(idx, token_idx);
-	token = wpabuf_put(buf, SHA256_MAC_LEN);
-	addrs[0] = addr;
-	len[0] = ETH_ALEN;
-	addrs[1] = idx;
-	len[1] = sizeof(idx);
-	if (hmac_sha256_vector(hapd->comeback_key, sizeof(hapd->comeback_key),
-			       2, addrs, len, token) < 0) {
-		wpabuf_free(buf);
-		return NULL;
-	}
-	WPA_PUT_BE16(token, token_idx);
-
-	return buf;
-}
-
 #endif /* defined(CONFIG_SAE) || defined(CONFIG_PASN) */
 
 
@@ -1220,31 +1124,32 @@
 
 static int sae_status_success(struct hostapd_data *hapd, u16 status_code)
 {
-	int sae_pwe = hapd->conf->sae_pwe;
+	enum sae_pwe sae_pwe = hapd->conf->sae_pwe;
 	int id_in_use;
 	bool sae_pk = false;
 
 	id_in_use = hostapd_sae_pw_id_in_use(hapd->conf);
-	if (id_in_use == 2 && sae_pwe != 3)
-		sae_pwe = 1;
-	else if (id_in_use == 1 && sae_pwe == 0)
-		sae_pwe = 2;
+	if (id_in_use == 2 && sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
+		sae_pwe = SAE_PWE_HASH_TO_ELEMENT;
+	else if (id_in_use == 1 && sae_pwe == SAE_PWE_HUNT_AND_PECK)
+		sae_pwe = SAE_PWE_BOTH;
 #ifdef CONFIG_SAE_PK
 	sae_pk = hostapd_sae_pk_in_use(hapd->conf);
-	if (sae_pwe == 0 && sae_pk)
-		sae_pwe = 2;
+	if (sae_pwe == SAE_PWE_HUNT_AND_PECK && sae_pk)
+		sae_pwe = SAE_PWE_BOTH;
 #endif /* CONFIG_SAE_PK */
-	if (sae_pwe == 0 &&
+	if (sae_pwe == SAE_PWE_HUNT_AND_PECK &&
 	    (hapd->conf->wpa_key_mgmt &
 	     (WPA_KEY_MGMT_SAE_EXT_KEY | WPA_KEY_MGMT_FT_SAE_EXT_KEY)))
-		sae_pwe = 2;
+		sae_pwe = SAE_PWE_BOTH;
 
-	return ((sae_pwe == 0 || sae_pwe == 3) &&
+	return ((sae_pwe == SAE_PWE_HUNT_AND_PECK ||
+		 sae_pwe == SAE_PWE_FORCE_HUNT_AND_PECK) &&
 		status_code == WLAN_STATUS_SUCCESS) ||
-		(sae_pwe == 1 &&
+		(sae_pwe == SAE_PWE_HASH_TO_ELEMENT &&
 		 (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
 		  (sae_pk && status_code == WLAN_STATUS_SAE_PK))) ||
-		(sae_pwe == 2 &&
+		(sae_pwe == SAE_PWE_BOTH &&
 		 (status_code == WLAN_STATUS_SUCCESS ||
 		  status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
 		  (sae_pk && status_code == WLAN_STATUS_SAE_PK)));
@@ -1468,7 +1373,8 @@
 					mgmt->u.auth.variable, &token,
 					&token_len, groups, status_code ==
 					WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
-					status_code == WLAN_STATUS_SAE_PK);
+					status_code == WLAN_STATUS_SAE_PK,
+					NULL);
 		if (resp == SAE_SILENTLY_DISCARD) {
 			wpa_printf(MSG_DEBUG,
 				   "SAE: Drop commit message from " MACSTR " due to reflection attack",
@@ -1487,7 +1393,9 @@
 		}
 
 		if (token &&
-		    check_comeback_token(hapd, sta->addr, token, token_len)
+		    check_comeback_token(hapd->comeback_key,
+					 hapd->comeback_pending_idx, sta->addr,
+					 token, token_len)
 		    < 0) {
 			wpa_printf(MSG_DEBUG, "SAE: Drop commit message with "
 				   "incorrect token from " MACSTR,
@@ -1515,8 +1423,14 @@
 			if (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
 			    status_code == WLAN_STATUS_SAE_PK)
 				h2e = 1;
-			data = auth_build_token_req(hapd, sta->sae->group,
-						    sta->addr, h2e);
+			data = auth_build_token_req(
+				&hapd->last_comeback_key_update,
+				hapd->comeback_key,
+				hapd->comeback_idx,
+				hapd->comeback_pending_idx,
+				sizeof(hapd->comeback_pending_idx),
+				sta->sae->group,
+				sta->addr, h2e);
 			resp = WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ;
 			if (hapd->conf->mesh & MESH_ENABLED)
 				sae_set_state(sta, SAE_NOTHING,
@@ -1560,8 +1474,9 @@
 				return;
 			}
 
-			if (sae_check_confirm(sta->sae, var, var_len) < 0) {
-				resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			if (sae_check_confirm(sta->sae, var, var_len,
+					      NULL) < 0) {
+				resp = WLAN_STATUS_CHALLENGE_FAIL;
 				goto reply;
 			}
 			sta->sae->rc = peer_send_confirm;
@@ -2110,10 +2025,8 @@
 	if (wpa_key_mgmt_ft(wpa_auth_sta_key_mgmt(sta->wpa_sm))) {
 		/* FTE[R1KH-ID,R0KH-ID] when using FILS+FT */
 		int res;
-		int use_sha384 = wpa_key_mgmt_sha384(
-			wpa_auth_sta_key_mgmt(sta->wpa_sm));
 
-		res = wpa_auth_write_fte(hapd->wpa_auth, use_sha384,
+		res = wpa_auth_write_fte(hapd->wpa_auth, sta->wpa_sm,
 					 wpabuf_put(data, 0),
 					 wpabuf_tailroom(data));
 		if (res < 0) {
@@ -2390,243 +2303,15 @@
 
 
 #ifdef CONFIG_PASN
-#ifdef CONFIG_SAE
-
-static int pasn_wd_handle_sae_commit(struct hostapd_data *hapd,
-				     struct sta_info *sta,
-				     struct wpabuf *wd)
-{
-	struct pasn_data *pasn = sta->pasn;
-	const char *password;
-	const u8 *data;
-	size_t buf_len;
-	u16 res, alg, seq, status;
-	int groups[] = { pasn->group, 0 };
-	struct sae_pt *pt = NULL;
-	int ret;
-
-	if (!wd)
-		return -1;
-
-	data = wpabuf_head_u8(wd);
-	buf_len = wpabuf_len(wd);
-
-	if (buf_len < 6) {
-		wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short. len=%zu",
-			   buf_len);
-		return -1;
-	}
-
-	alg = WPA_GET_LE16(data);
-	seq = WPA_GET_LE16(data + 2);
-	status = WPA_GET_LE16(data + 4);
-
-	wpa_printf(MSG_DEBUG, "PASN: SAE commit: alg=%u, seq=%u, status=%u",
-		   alg, seq, status);
-
-	if (alg != WLAN_AUTH_SAE || seq != 1 ||
-	    status != WLAN_STATUS_SAE_HASH_TO_ELEMENT) {
-		wpa_printf(MSG_DEBUG, "PASN: Dropping peer SAE commit");
-		return -1;
-	}
-
-	sae_clear_data(&pasn->sae);
-	pasn->sae.state = SAE_NOTHING;
-
-	ret = sae_set_group(&pasn->sae, pasn->group);
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed to set SAE group");
-		return -1;
-	}
-
-	password = sae_get_password(hapd, sta, NULL, NULL, &pt, NULL);
-	if (!password || !pt) {
-		wpa_printf(MSG_DEBUG, "PASN: No SAE PT found");
-		return -1;
-	}
-
-	ret = sae_prepare_commit_pt(&pasn->sae, pt, hapd->own_addr, sta->addr,
-				    NULL, NULL);
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed to prepare SAE commit");
-		return -1;
-	}
-
-	res = sae_parse_commit(&pasn->sae, data + 6, buf_len - 6, NULL, 0,
-			       groups, 0);
-	if (res != WLAN_STATUS_SUCCESS) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed parsing SAE commit");
-		return -1;
-	}
-
-	/* Process the commit message and derive the PMK */
-	ret = sae_process_commit(&pasn->sae);
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "SAE: Failed to process peer commit");
-		return -1;
-	}
-
-	pasn->sae.state = SAE_COMMITTED;
-
-	return 0;
-}
-
-
-static int pasn_wd_handle_sae_confirm(struct hostapd_data *hapd,
-				      struct sta_info *sta,
-				      struct wpabuf *wd)
-{
-	struct pasn_data *pasn = sta->pasn;
-	const u8 *data;
-	size_t buf_len;
-	u16 res, alg, seq, status;
-
-	if (!wd)
-		return -1;
-
-	data = wpabuf_head_u8(wd);
-	buf_len = wpabuf_len(wd);
-
-	if (buf_len < 6) {
-		wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short. len=%zu",
-			   buf_len);
-		return -1;
-	}
-
-	alg = WPA_GET_LE16(data);
-	seq = WPA_GET_LE16(data + 2);
-	status = WPA_GET_LE16(data + 4);
-
-	wpa_printf(MSG_DEBUG, "PASN: SAE confirm: alg=%u, seq=%u, status=%u",
-		   alg, seq, status);
-
-	if (alg != WLAN_AUTH_SAE || seq != 2 || status != WLAN_STATUS_SUCCESS) {
-		wpa_printf(MSG_DEBUG, "PASN: Dropping peer SAE confirm");
-		return -1;
-	}
-
-	res = sae_check_confirm(&pasn->sae, data + 6, buf_len - 6);
-	if (res != WLAN_STATUS_SUCCESS) {
-		wpa_printf(MSG_DEBUG, "PASN: SAE failed checking confirm");
-		return -1;
-	}
-
-	pasn->sae.state = SAE_ACCEPTED;
-
-	/*
-	 * TODO: Based on on IEEE P802.11az/D2.6, the PMKSA derived with
-	 * PASN/SAE should only be allowed with future PASN only. For now do not
-	 * restrict this only for PASN.
-	 */
-	wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr,
-			       pasn->sae.pmk, pasn->sae.pmk_len,
-			       pasn->sae.pmkid, pasn->sae.akmp);
-	return 0;
-}
-
-
-static struct wpabuf * pasn_get_sae_wd(struct hostapd_data *hapd,
-				       struct sta_info *sta)
-{
-	struct pasn_data *pasn = sta->pasn;
-	struct wpabuf *buf = NULL;
-	u8 *len_ptr;
-	size_t len;
-
-	/* Need to add the entire Authentication frame body */
-	buf = wpabuf_alloc(8 + SAE_COMMIT_MAX_LEN + 8 + SAE_CONFIRM_MAX_LEN);
-	if (!buf) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed to allocate SAE buffer");
-		return NULL;
-	}
-
-	/* Need to add the entire authentication frame body for the commit */
-	len_ptr = wpabuf_put(buf, 2);
-	wpabuf_put_le16(buf, WLAN_AUTH_SAE);
-	wpabuf_put_le16(buf, 1);
-	wpabuf_put_le16(buf, WLAN_STATUS_SAE_HASH_TO_ELEMENT);
-
-	/* Write the actual commit and update the length accordingly */
-	sae_write_commit(&pasn->sae, buf, NULL, 0);
-	len = wpabuf_len(buf);
-	WPA_PUT_LE16(len_ptr, len - 2);
-
-	/* Need to add the entire Authentication frame body for the confirm */
-	len_ptr = wpabuf_put(buf, 2);
-	wpabuf_put_le16(buf, WLAN_AUTH_SAE);
-	wpabuf_put_le16(buf, 2);
-	wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
-
-	sae_write_confirm(&pasn->sae, buf);
-	WPA_PUT_LE16(len_ptr, wpabuf_len(buf) - len - 2);
-
-	pasn->sae.state = SAE_CONFIRMED;
-
-	return buf;
-}
-
-#endif /* CONFIG_SAE */
-
-
 #ifdef CONFIG_FILS
 
-static struct wpabuf * pasn_get_fils_wd(struct hostapd_data *hapd,
-					struct sta_info *sta)
-{
-	struct pasn_data *pasn = sta->pasn;
-	struct pasn_fils_data *fils = &pasn->fils;
-	struct wpabuf *buf = NULL;
-
-	if (!fils->erp_resp) {
-		wpa_printf(MSG_DEBUG, "PASN: FILS: Missing erp_resp");
-		return NULL;
-	}
-
-	buf = wpabuf_alloc(1500);
-	if (!buf)
-		return NULL;
-
-	/* Add the authentication algorithm */
-	wpabuf_put_le16(buf, WLAN_AUTH_FILS_SK);
-
-	/* Authentication Transaction seq# */
-	wpabuf_put_le16(buf, 2);
-
-	/* Status Code */
-	wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
-
-	/* Own RSNE */
-	wpa_pasn_add_rsne(buf, NULL, pasn->akmp, pasn->cipher);
-
-	/* FILS Nonce */
-	wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
-	wpabuf_put_u8(buf, 1 + FILS_NONCE_LEN);
-	wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_NONCE);
-	wpabuf_put_data(buf, fils->anonce, FILS_NONCE_LEN);
-
-	/* FILS Session */
-	wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
-	wpabuf_put_u8(buf, 1 + FILS_SESSION_LEN);
-	wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_SESSION);
-	wpabuf_put_data(buf, fils->session, FILS_SESSION_LEN);
-
-	/* Wrapped Data */
-	wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
-	wpabuf_put_u8(buf, 1 + wpabuf_len(fils->erp_resp));
-	wpabuf_put_u8(buf, WLAN_EID_EXT_WRAPPED_DATA);
-	wpabuf_put_buf(buf, fils->erp_resp);
-
-	return buf;
-}
-
-
 static void pasn_fils_auth_resp(struct hostapd_data *hapd,
 				struct sta_info *sta, u16 status,
 				struct wpabuf *erp_resp,
 				const u8 *msk, size_t msk_len)
 {
 	struct pasn_data *pasn = sta->pasn;
-	struct pasn_fils_data *fils = &pasn->fils;
+	struct pasn_fils *fils = &pasn->fils;
 	u8 pmk[PMK_LEN_MAX];
 	size_t pmk_len;
 	int ret;
@@ -2682,7 +2367,8 @@
 	pasn->secret = NULL;
 
 	fils->erp_resp = erp_resp;
-	ret = handle_auth_pasn_resp(hapd, sta, NULL, WLAN_STATUS_SUCCESS);
+	ret = handle_auth_pasn_resp(sta->pasn, hapd->own_addr, sta->addr, NULL,
+				    WLAN_STATUS_SUCCESS);
 	fils->erp_resp = NULL;
 
 	if (ret) {
@@ -2705,7 +2391,7 @@
 	return -1;
 #else /* CONFIG_NO_RADIUS */
 	struct pasn_data *pasn = sta->pasn;
-	struct pasn_fils_data *fils = &pasn->fils;
+	struct pasn_fils *fils = &pasn->fils;
 	struct ieee802_11_elems elems;
 	struct wpa_ie_data rsne_data;
 	struct wpabuf *fils_wd;
@@ -2756,7 +2442,7 @@
 	}
 
 	if (!elems.rsn_ie || !elems.fils_nonce || !elems.fils_nonce ||
-	    !elems.wrapped_data) {
+	    !elems.wrapped_data || !elems.fils_session) {
 		wpa_printf(MSG_DEBUG, "PASN: FILS: Missing IEs");
 		return -1;
 	}
@@ -2764,7 +2450,7 @@
 	ret = wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
 				   &rsne_data);
 	if (ret) {
-		wpa_printf(MSG_DEBUG, "PASN: FILS: Failed parsing RNSE");
+		wpa_printf(MSG_DEBUG, "PASN: FILS: Failed parsing RSNE");
 		return -1;
 	}
 
@@ -2824,36 +2510,45 @@
 #endif /* CONFIG_FILS */
 
 
-static struct wpabuf * pasn_get_wrapped_data(struct hostapd_data *hapd,
-					     struct sta_info *sta)
+static int hapd_pasn_send_mlme(void *ctx, const u8 *data, size_t data_len,
+			       int noack, unsigned int freq, unsigned int wait)
 {
-	switch (sta->pasn->akmp) {
-	case WPA_KEY_MGMT_PASN:
-		/* no wrapped data */
-		return NULL;
-	case WPA_KEY_MGMT_SAE:
-#ifdef CONFIG_SAE
-		return pasn_get_sae_wd(hapd, sta);
-#else /* CONFIG_SAE */
-		wpa_printf(MSG_ERROR,
-			   "PASN: SAE: Cannot derive wrapped data");
-		return NULL;
-#endif /* CONFIG_SAE */
-	case WPA_KEY_MGMT_FILS_SHA256:
-	case WPA_KEY_MGMT_FILS_SHA384:
-#ifdef CONFIG_FILS
-		return pasn_get_fils_wd(hapd, sta);
-#endif /* CONFIG_FILS */
-		/* fall through */
-	case WPA_KEY_MGMT_FT_PSK:
-	case WPA_KEY_MGMT_FT_IEEE8021X:
-	case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
-	default:
-		wpa_printf(MSG_ERROR,
-			   "PASN: TODO: Wrapped data for akmp=0x%x",
-			   sta->pasn->akmp);
-		return NULL;
-	}
+	struct hostapd_data *hapd = ctx;
+
+	return hostapd_drv_send_mlme(hapd, data, data_len, 0, NULL, 0, 0);
+}
+
+
+static void hapd_initialize_pasn(struct hostapd_data *hapd,
+				 struct sta_info *sta)
+{
+	struct pasn_data *pasn = sta->pasn;
+
+	pasn->cb_ctx = hapd;
+	pasn->send_mgmt = hapd_pasn_send_mlme;
+	pasn->pasn_groups = hapd->conf->pasn_groups;
+	pasn->wpa_key_mgmt = hapd->conf->wpa_key_mgmt;
+	pasn->rsn_pairwise = hapd->conf->rsn_pairwise;
+	pasn->derive_kdk = hapd->iface->drv_flags2 &
+		WPA_DRIVER_FLAGS2_SEC_LTF_AP;
+#ifdef CONFIG_TESTING_OPTIONS
+	pasn->corrupt_mic = hapd->conf->pasn_corrupt_mic;
+	if (hapd->conf->force_kdk_derivation)
+		pasn->derive_kdk = true;
+#endif /* CONFIG_TESTING_OPTIONS */
+	pasn->use_anti_clogging = use_anti_clogging(hapd);
+	pasn->password = sae_get_password(hapd, sta, NULL, NULL, &pasn->pt,
+					  NULL);
+	pasn->rsn_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &pasn->rsn_ie_len);
+	pasn->rsnxe_ie = hostapd_wpa_ie(hapd, WLAN_EID_RSNX);
+	pasn->disable_pmksa_caching = hapd->conf->disable_pmksa_caching;
+	pasn->pmksa = wpa_auth_get_pmksa_cache(hapd->wpa_auth);
+
+	pasn->comeback_after = hapd->conf->pasn_comeback_after;
+	pasn->comeback_idx = hapd->comeback_idx;
+	pasn->comeback_key =  hapd->comeback_key;
+	pasn->comeback_pending_idx = hapd->comeback_pending_idx;
+	os_memcpy(pasn->bssid, hapd->own_addr, ETH_ALEN);
 }
 
 
@@ -2889,582 +2584,16 @@
 }
 
 
-static int
-pasn_derive_keys(struct hostapd_data *hapd, struct sta_info *sta,
-		 const u8 *cached_pmk, size_t cached_pmk_len,
-		 struct wpa_pasn_params_data *pasn_data,
-		 struct wpabuf *wrapped_data,
-		 struct wpabuf *secret)
+static void hapd_pasn_update_params(struct hostapd_data *hapd,
+				    struct sta_info *sta,
+				    const struct ieee80211_mgmt *mgmt,
+				    size_t len)
 {
-	static const u8 pasn_default_pmk[] = {'P', 'M', 'K', 'z'};
-	u8 pmk[PMK_LEN_MAX];
-	u8 pmk_len;
-	int ret;
-
-	os_memset(pmk, 0, sizeof(pmk));
-	pmk_len = 0;
-
-	if (!cached_pmk || !cached_pmk_len)
-		wpa_printf(MSG_DEBUG, "PASN: No valid PMKSA entry");
-
-	if (sta->pasn->akmp == WPA_KEY_MGMT_PASN) {
-		wpa_printf(MSG_DEBUG, "PASN: Using default PMK");
-
-		pmk_len = WPA_PASN_PMK_LEN;
-		os_memcpy(pmk, pasn_default_pmk, sizeof(pasn_default_pmk));
-	} else if (cached_pmk && cached_pmk_len) {
-		wpa_printf(MSG_DEBUG, "PASN: Using PMKSA entry");
-
-		pmk_len = cached_pmk_len;
-		os_memcpy(pmk, cached_pmk, cached_pmk_len);
-	} else {
-		switch (sta->pasn->akmp) {
-#ifdef CONFIG_SAE
-		case WPA_KEY_MGMT_SAE:
-			if (sta->pasn->sae.state == SAE_COMMITTED) {
-				pmk_len = PMK_LEN;
-				os_memcpy(pmk, sta->pasn->sae.pmk, PMK_LEN);
-				break;
-			}
-#endif /* CONFIG_SAE */
-			/* fall through */
-		default:
-			/* TODO: Derive PMK based on wrapped data */
-			wpa_printf(MSG_DEBUG,
-				   "PASN: Missing PMK derivation");
-			return -1;
-		}
-	}
-
-	ret = pasn_pmk_to_ptk(pmk, pmk_len, sta->addr, hapd->own_addr,
-			      wpabuf_head(secret), wpabuf_len(secret),
-			      &sta->pasn->ptk, sta->pasn->akmp,
-			      sta->pasn->cipher, sta->pasn->kdk_len);
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed to derive PTK");
-		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;
-}
-
-
-static void handle_auth_pasn_comeback(struct hostapd_data *hapd,
-				      struct sta_info *sta, u16 group)
-{
-	struct wpabuf *buf, *comeback;
-	int ret;
-
-	wpa_printf(MSG_DEBUG,
-		   "PASN: Building comeback frame 2. Comeback after=%u",
-		   hapd->conf->pasn_comeback_after);
-
-	buf = wpabuf_alloc(1500);
-	if (!buf)
-		return;
-
-	wpa_pasn_build_auth_header(buf, hapd->own_addr, hapd->own_addr,
-				   sta->addr, 2,
-				   WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY);
-
-	/*
-	 * Do not include the group as a part of the token since it is not going
-	 * to be used.
-	 */
-	comeback = auth_build_token_req(hapd, 0, sta->addr, 0);
-	if (!comeback) {
-		wpa_printf(MSG_DEBUG,
-			   "PASN: Failed sending auth with comeback");
-		wpabuf_free(buf);
-		return;
-	}
-
-	wpa_pasn_add_parameter_ie(buf, group,
-				  WPA_PASN_WRAPPED_DATA_NO,
-				  NULL, 0, comeback,
-				  hapd->conf->pasn_comeback_after);
-	wpabuf_free(comeback);
-
-	wpa_printf(MSG_DEBUG,
-		   "PASN: comeback: STA=" MACSTR, MAC2STR(sta->addr));
-
-	ret = hostapd_drv_send_mlme(hapd, wpabuf_head(buf), wpabuf_len(buf), 0,
-				    NULL, 0, 0);
-	if (ret)
-		wpa_printf(MSG_INFO, "PASN: Failed to send comeback frame 2");
-
-	wpabuf_free(buf);
-}
-
-
-static int handle_auth_pasn_resp(struct hostapd_data *hapd,
-				 struct sta_info *sta,
-				 struct rsn_pmksa_cache_entry *pmksa,
-				 u16 status)
-{
-	struct wpabuf *buf, *pubkey = NULL, *wrapped_data_buf = NULL;
-	u8 mic[WPA_PASN_MAX_MIC_LEN];
-	u8 mic_len;
-	u8 *ptr;
-	const u8 *frame, *data, *rsn_ie, *rsnxe_ie;
-	u8 *data_buf = NULL;
-	size_t rsn_ie_len, frame_len, data_len;
-	int ret;
-	const u8 *pmkid = NULL;
-
-	wpa_printf(MSG_DEBUG, "PASN: Building frame 2: status=%u", status);
-
-	buf = wpabuf_alloc(1500);
-	if (!buf)
-		goto fail;
-
-	wpa_pasn_build_auth_header(buf, hapd->own_addr, hapd->own_addr,
-				   sta->addr, 2, status);
-
-	if (status != WLAN_STATUS_SUCCESS)
-		goto done;
-
-	if (pmksa) {
-		pmkid = pmksa->pmkid;
-#ifdef CONFIG_SAE
-	} else if (sta->pasn->akmp == WPA_KEY_MGMT_SAE) {
-		wpa_printf(MSG_DEBUG, "PASN: Use SAE PMKID");
-		pmkid = sta->pasn->sae.pmkid;
-#endif /* CONFIG_SAE */
-#ifdef CONFIG_FILS
-	} else if (sta->pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 ||
-		   sta->pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) {
-		wpa_printf(MSG_DEBUG, "PASN: Use FILS ERP PMKID");
-		pmkid = sta->pasn->fils.erp_pmkid;
-#endif /* CONFIG_FILS */
-	}
-
-	if (wpa_pasn_add_rsne(buf, pmkid,
-			      sta->pasn->akmp, sta->pasn->cipher) < 0)
-		goto fail;
-
-	/* No need to derive PMK if PMKSA is given */
-	if (!pmksa)
-		wrapped_data_buf = pasn_get_wrapped_data(hapd, sta);
-	else
-		sta->pasn->wrapped_data_format = WPA_PASN_WRAPPED_DATA_NO;
-
-	/* Get public key */
-	pubkey = crypto_ecdh_get_pubkey(sta->pasn->ecdh, 0);
-	pubkey = wpabuf_zeropad(pubkey,
-				crypto_ecdh_prime_len(sta->pasn->ecdh));
-	if (!pubkey) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed to get pubkey");
-		goto fail;
-	}
-
-	wpa_pasn_add_parameter_ie(buf, sta->pasn->group,
-				  sta->pasn->wrapped_data_format,
-				  pubkey, true, NULL, 0);
-
-	if (wpa_pasn_add_wrapped_data(buf, wrapped_data_buf) < 0)
-		goto fail;
-
-	wpabuf_free(wrapped_data_buf);
-	wrapped_data_buf = NULL;
-	wpabuf_free(pubkey);
-	pubkey = NULL;
-
-	/* Add RSNXE if needed */
-	rsnxe_ie = hostapd_wpa_ie(hapd, WLAN_EID_RSNX);
-	if (rsnxe_ie)
-		wpabuf_put_data(buf, rsnxe_ie, 2 + rsnxe_ie[1]);
-
-	/* Add the mic */
-	mic_len = pasn_mic_len(sta->pasn->akmp, sta->pasn->cipher);
-	wpabuf_put_u8(buf, WLAN_EID_MIC);
-	wpabuf_put_u8(buf, mic_len);
-	ptr = wpabuf_put(buf, mic_len);
-
-	os_memset(ptr, 0, mic_len);
-
-	frame = wpabuf_head_u8(buf) + IEEE80211_HDRLEN;
-	frame_len = wpabuf_len(buf) - IEEE80211_HDRLEN;
-
-	rsn_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &rsn_ie_len);
-	if (!rsn_ie || !rsn_ie_len)
-		goto fail;
-
-	/*
-	 * Note: wpa_auth_get_wpa_ie() might return not only the RSNE but also
-	 * MDE, etc. Thus, do not use the returned length but instead use the
-	 * length specified in the IE header.
-	 */
-	data_len = rsn_ie[1] + 2;
-	if (rsnxe_ie) {
-		data_buf = os_zalloc(rsn_ie[1] + 2 + rsnxe_ie[1] + 2);
-		if (!data_buf)
-			goto fail;
-
-		os_memcpy(data_buf, rsn_ie, rsn_ie[1] + 2);
-		os_memcpy(data_buf + rsn_ie[1] + 2, rsnxe_ie, rsnxe_ie[1] + 2);
-		data_len += rsnxe_ie[1] + 2;
-		data = data_buf;
-	} else {
-		data = rsn_ie;
-	}
-
-	ret = pasn_mic(sta->pasn->ptk.kck, sta->pasn->akmp, sta->pasn->cipher,
-		       hapd->own_addr, sta->addr, data, data_len,
-		       frame, frame_len, mic);
-	os_free(data_buf);
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "PASN: Frame 3: Failed MIC calculation");
-		goto fail;
-	}
-
-#ifdef CONFIG_TESTING_OPTIONS
-	if (hapd->conf->pasn_corrupt_mic) {
-		wpa_printf(MSG_DEBUG, "PASN: frame 2: Corrupt MIC");
-		mic[0] = ~mic[0];
-	}
-#endif /* CONFIG_TESTING_OPTIONS */
-
-	os_memcpy(ptr, mic, mic_len);
-
-done:
-	wpa_printf(MSG_DEBUG,
-		   "PASN: Building frame 2: success; resp STA=" MACSTR,
-		   MAC2STR(sta->addr));
-
-	ret = hostapd_drv_send_mlme(hapd, wpabuf_head(buf), wpabuf_len(buf), 0,
-				    NULL, 0, 0);
-	if (ret)
-		wpa_printf(MSG_INFO, "send_auth_reply: Send failed");
-
-	wpabuf_free(buf);
-	return ret;
-fail:
-	wpabuf_free(wrapped_data_buf);
-	wpabuf_free(pubkey);
-	wpabuf_free(buf);
-	return -1;
-}
-
-
-static void handle_auth_pasn_1(struct hostapd_data *hapd, struct sta_info *sta,
-			       const struct ieee80211_mgmt *mgmt, size_t len)
-{
+	struct pasn_data *pasn = sta->pasn;
 	struct ieee802_11_elems elems;
 	struct wpa_ie_data rsn_data;
 	struct wpa_pasn_params_data pasn_params;
-	struct rsn_pmksa_cache_entry *pmksa = NULL;
-	const u8 *cached_pmk = NULL;
-	size_t cached_pmk_len = 0;
-#ifdef CONFIG_IEEE80211R_AP
-	u8 pmk_r1[PMK_LEN_MAX];
-	size_t pmk_r1_len;
-#endif /* CONFIG_IEEE80211R_AP */
-	struct wpabuf *wrapped_data = NULL, *secret = NULL;
-	const int *groups = hapd->conf->pasn_groups;
-	static const int default_groups[] = { 19, 0 };
-	u16 status = WLAN_STATUS_SUCCESS;
-	int ret, inc_y;
-	bool derive_keys;
-	u32 i;
-	bool derive_kdk;
-
-	if (!groups)
-		groups = default_groups;
-
-	if (ieee802_11_parse_elems(mgmt->u.auth.variable,
-				   len - offsetof(struct ieee80211_mgmt,
-						  u.auth.variable),
-				   &elems, 0) == ParseFailed) {
-		wpa_printf(MSG_DEBUG,
-			   "PASN: Failed parsing Authentication frame");
-		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
-		goto send_resp;
-	}
-
-	ret = wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
-				   &rsn_data);
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed parsing RNSE");
-		status = WLAN_STATUS_INVALID_RSNIE;
-		goto send_resp;
-	}
-
-	ret = wpa_pasn_validate_rsne(&rsn_data);
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed validating RSNE");
-		status = WLAN_STATUS_INVALID_RSNIE;
-		goto send_resp;
-	}
-
-	if (!(rsn_data.key_mgmt & hapd->conf->wpa_key_mgmt) ||
-	    !(rsn_data.pairwise_cipher & hapd->conf->rsn_pairwise)) {
-		wpa_printf(MSG_DEBUG, "PASN: Mismatch in AKMP/cipher");
-		status = WLAN_STATUS_INVALID_RSNIE;
-		goto send_resp;
-	}
-
-	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_AP) &&
-		ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len,
-					  WLAN_RSNX_CAPAB_SECURE_LTF);
-#ifdef CONFIG_TESTING_OPTIONS
-	if (!derive_kdk)
-		derive_kdk = hapd->conf->force_kdk_derivation;
-#endif /* CONFIG_TESTING_OPTIONS */
-	if (derive_kdk)
-		sta->pasn->kdk_len = WPA_KDK_MAX_LEN;
-	else
-		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");
-		status = WLAN_STATUS_INVALID_PARAMETERS;
-		goto send_resp;
-	}
-
-	ret = wpa_pasn_parse_parameter_ie(elems.pasn_params - 3,
-					  elems.pasn_params_len + 3,
-					  false, &pasn_params);
-	if (ret) {
-		wpa_printf(MSG_DEBUG,
-			   "PASN: Failed validation of PASN Parameters IE");
-		status = WLAN_STATUS_INVALID_PARAMETERS;
-		goto send_resp;
-	}
-
-	for (i = 0; groups[i] > 0 && groups[i] != pasn_params.group; i++)
-		;
-
-	if (!pasn_params.group || groups[i] != pasn_params.group) {
-		wpa_printf(MSG_DEBUG, "PASN: Requested group=%hu not allowed",
-			   pasn_params.group);
-		status = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
-		goto send_resp;
-	}
-
-	if (!pasn_params.pubkey || !pasn_params.pubkey_len) {
-		wpa_printf(MSG_DEBUG, "PASN: Invalid public key");
-		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
-		goto send_resp;
-	}
-
-	if (pasn_params.comeback) {
-		wpa_printf(MSG_DEBUG, "PASN: Checking peer comeback token");
-
-		ret = check_comeback_token(hapd, sta->addr,
-					   pasn_params.comeback,
-					   pasn_params.comeback_len);
-
-		if (ret) {
-			wpa_printf(MSG_DEBUG, "PASN: Invalid comeback token");
-			status = WLAN_STATUS_UNSPECIFIED_FAILURE;
-			goto send_resp;
-		}
-	} else if (use_anti_clogging(hapd)) {
-		wpa_printf(MSG_DEBUG, "PASN: Respond with comeback");
-		handle_auth_pasn_comeback(hapd, sta, pasn_params.group);
-		ap_free_sta(hapd, sta);
-		return;
-	}
-
-	sta->pasn->ecdh = crypto_ecdh_init(pasn_params.group);
-	if (!sta->pasn->ecdh) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed to init ECDH");
-		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
-		goto send_resp;
-	}
-
-	sta->pasn->group = pasn_params.group;
-
-	if (pasn_params.pubkey[0] == WPA_PASN_PUBKEY_UNCOMPRESSED) {
-		inc_y = 1;
-	} else if (pasn_params.pubkey[0] == WPA_PASN_PUBKEY_COMPRESSED_0 ||
-		   pasn_params.pubkey[0] == WPA_PASN_PUBKEY_COMPRESSED_1) {
-		inc_y = 0;
-	} else {
-		wpa_printf(MSG_DEBUG,
-			   "PASN: Invalid first octet in pubkey=0x%x",
-			   pasn_params.pubkey[0]);
-		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
-		goto send_resp;
-	}
-
-	secret = crypto_ecdh_set_peerkey(sta->pasn->ecdh, inc_y,
-					 pasn_params.pubkey + 1,
-					 pasn_params.pubkey_len - 1);
-	if (!secret) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed to derive shared secret");
-		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
-		goto send_resp;
-	}
-
-	derive_keys = true;
-	if (pasn_params.wrapped_data_format != WPA_PASN_WRAPPED_DATA_NO) {
-		wrapped_data = ieee802_11_defrag(&elems,
-						 WLAN_EID_EXTENSION,
-						 WLAN_EID_EXT_WRAPPED_DATA);
-		if (!wrapped_data) {
-			wpa_printf(MSG_DEBUG, "PASN: Missing wrapped data");
-			status = WLAN_STATUS_UNSPECIFIED_FAILURE;
-			goto send_resp;
-		}
-
-#ifdef CONFIG_SAE
-		if (sta->pasn->akmp == WPA_KEY_MGMT_SAE) {
-			ret = pasn_wd_handle_sae_commit(hapd, sta,
-							wrapped_data);
-			if (ret) {
-				wpa_printf(MSG_DEBUG,
-					   "PASN: Failed processing SAE commit");
-				status = WLAN_STATUS_UNSPECIFIED_FAILURE;
-				goto send_resp;
-			}
-		}
-#endif /* CONFIG_SAE */
-#ifdef CONFIG_FILS
-		if (sta->pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 ||
-		    sta->pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) {
-			ret = pasn_wd_handle_fils(hapd, sta, wrapped_data);
-			if (ret) {
-				wpa_printf(MSG_DEBUG,
-					   "PASN: Failed processing FILS wrapped data");
-				status = WLAN_STATUS_UNSPECIFIED_FAILURE;
-				goto send_resp;
-			}
-
-			wpa_printf(MSG_DEBUG,
-				   "PASN: FILS: Pending AS response");
-
-			/*
-			 * With PASN/FILS, keys can be derived only after a
-			 * response from the AS is processed.
-			 */
-			derive_keys = false;
-		}
-#endif /* CONFIG_FILS */
-	}
-
-	sta->pasn->wrapped_data_format = pasn_params.wrapped_data_format;
-
-	ret = pasn_auth_frame_hash(sta->pasn->akmp, sta->pasn->cipher,
-				   ((const u8 *) mgmt) + IEEE80211_HDRLEN,
-				   len - IEEE80211_HDRLEN, sta->pasn->hash);
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed to compute hash");
-		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
-		goto send_resp;
-	}
-
-	if (!derive_keys) {
-		wpa_printf(MSG_DEBUG, "PASN: Storing secret");
-		sta->pasn->secret = secret;
-		wpabuf_free(wrapped_data);
-		return;
-	}
-
-	if (rsn_data.num_pmkid) {
-		if (wpa_key_mgmt_ft(sta->pasn->akmp)) {
-#ifdef CONFIG_IEEE80211R_AP
-			wpa_printf(MSG_DEBUG, "PASN: FT: Fetch PMK-R1");
-
-			ret = wpa_ft_fetch_pmk_r1(hapd->wpa_auth, sta->addr,
-						  rsn_data.pmkid,
-						  pmk_r1, &pmk_r1_len, NULL,
-						  NULL, NULL, NULL,
-						  NULL, NULL, NULL);
-			if (ret) {
-				wpa_printf(MSG_DEBUG,
-					   "PASN: FT: Failed getting PMK-R1");
-				status = WLAN_STATUS_UNSPECIFIED_FAILURE;
-				goto send_resp;
-			}
-			cached_pmk = pmk_r1;
-			cached_pmk_len = pmk_r1_len;
-#else /* CONFIG_IEEE80211R_AP */
-			wpa_printf(MSG_DEBUG, "PASN: FT: Not supported");
-			status = WLAN_STATUS_UNSPECIFIED_FAILURE;
-			goto send_resp;
-#endif /* CONFIG_IEEE80211R_AP */
-		} else {
-			wpa_printf(MSG_DEBUG, "PASN: Try to find PMKSA entry");
-
-			pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr,
-						   rsn_data.pmkid);
-			if (pmksa) {
-				cached_pmk = pmksa->pmk;
-				cached_pmk_len = pmksa->pmk_len;
-			}
-		}
-	} else {
-		wpa_printf(MSG_DEBUG, "PASN: No PMKID specified");
-	}
-
-	ret = pasn_derive_keys(hapd, sta, cached_pmk, cached_pmk_len,
-			       &pasn_params, wrapped_data, secret);
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed to derive keys");
-		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
-		goto send_resp;
-	}
-
-	ret = pasn_auth_frame_hash(sta->pasn->akmp, sta->pasn->cipher,
-				   ((const u8 *) mgmt) + IEEE80211_HDRLEN,
-				   len - IEEE80211_HDRLEN, sta->pasn->hash);
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed to compute hash");
-		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
-	}
-
-send_resp:
-	ret = handle_auth_pasn_resp(hapd, sta, pmksa, status);
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed to send response");
-		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
-	} else {
-		wpa_printf(MSG_DEBUG,
-			   "PASN: Success handling transaction == 1");
-	}
-
-	wpabuf_free(secret);
-	wpabuf_free(wrapped_data);
-
-	if (status != WLAN_STATUS_SUCCESS)
-		ap_free_sta(hapd, sta);
-}
-
-
-static void handle_auth_pasn_3(struct hostapd_data *hapd, struct sta_info *sta,
-			       const struct ieee80211_mgmt *mgmt, size_t len)
-{
-	struct ieee802_11_elems elems;
-	struct wpa_pasn_params_data pasn_params;
 	struct wpabuf *wrapped_data = NULL;
-	u8 mic[WPA_PASN_MAX_MIC_LEN], out_mic[WPA_PASN_MAX_MIC_LEN];
-	u8 mic_len;
-	int ret;
 
 	if (ieee802_11_parse_elems(mgmt->u.auth.variable,
 				   len - offsetof(struct ieee80211_mgmt,
@@ -3472,100 +2601,62 @@
 				   &elems, 0) == ParseFailed) {
 		wpa_printf(MSG_DEBUG,
 			   "PASN: Failed parsing Authentication frame");
-		goto fail;
+		return;
 	}
 
-	/* Check that the MIC IE exists. Save it and zero out the memory. */
-	mic_len = pasn_mic_len(sta->pasn->akmp, sta->pasn->cipher);
-	if (!elems.mic || elems.mic_len != mic_len) {
+	if (!elems.rsn_ie ||
+	    wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
+				 &rsn_data)) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed parsing RSNE");
+		return;
+	}
+
+	if (!(rsn_data.key_mgmt & pasn->wpa_key_mgmt) ||
+	    !(rsn_data.pairwise_cipher & pasn->rsn_pairwise)) {
+		wpa_printf(MSG_DEBUG, "PASN: Mismatch in AKMP/cipher");
+		return;
+	}
+
+	pasn->akmp = rsn_data.key_mgmt;
+	pasn->cipher = rsn_data.pairwise_cipher;
+
+	if (wpa_key_mgmt_ft(pasn->akmp) && rsn_data.num_pmkid) {
+#ifdef CONFIG_IEEE80211R_AP
+		pasn->pmk_r1_len = 0;
+		wpa_ft_fetch_pmk_r1(hapd->wpa_auth, sta->addr,
+				    rsn_data.pmkid,
+				    pasn->pmk_r1, &pasn->pmk_r1_len, NULL,
+				    NULL, NULL, NULL,
+				    NULL, NULL, NULL);
+#endif /* CONFIG_IEEE80211R_AP */
+	}
+#ifdef CONFIG_FILS
+	if (pasn->akmp != WPA_KEY_MGMT_FILS_SHA256 &&
+	    pasn->akmp != WPA_KEY_MGMT_FILS_SHA384)
+		return;
+	if (!elems.pasn_params ||
+	    wpa_pasn_parse_parameter_ie(elems.pasn_params - 3,
+					elems.pasn_params_len + 3,
+					false, &pasn_params)) {
 		wpa_printf(MSG_DEBUG,
-			   "PASN: Invalid MIC. Expecting len=%u", mic_len);
-		goto fail;
-	} else {
-		os_memcpy(mic, elems.mic, mic_len);
-		/* TODO: Clean this up.. Should not modify received frame
-		 * buffer. */
-		os_memset((u8 *) elems.mic, 0, mic_len);
+			   "PASN: Failed validation of PASN Parameters element");
+		return;
 	}
-
-	if (!elems.pasn_params || !elems.pasn_params_len) {
-		wpa_printf(MSG_DEBUG,
-			   "PASN: No PASN Parameters element found");
-		goto fail;
-	}
-
-	ret = wpa_pasn_parse_parameter_ie(elems.pasn_params - 3,
-					  elems.pasn_params_len + 3,
-					  false, &pasn_params);
-	if (ret) {
-		wpa_printf(MSG_DEBUG,
-			   "PASN: Failed validation of PASN Parameters IE");
-		goto fail;
-	}
-
-	if (pasn_params.pubkey || pasn_params.pubkey_len) {
-		wpa_printf(MSG_DEBUG,
-			   "PASN: Public key should not be included");
-		goto fail;
-	}
-
-	/* Verify the MIC */
-	ret = pasn_mic(sta->pasn->ptk.kck, sta->pasn->akmp, sta->pasn->cipher,
-		       sta->addr, hapd->own_addr,
-		       sta->pasn->hash, mic_len * 2,
-		       (u8 *) &mgmt->u.auth,
-		       len - offsetof(struct ieee80211_mgmt, u.auth),
-		       out_mic);
-
-	wpa_hexdump_key(MSG_DEBUG, "PASN: Frame MIC", mic, mic_len);
-	if (ret || os_memcmp(mic, out_mic, mic_len) != 0) {
-		wpa_printf(MSG_DEBUG, "PASN: Failed MIC verification");
-		goto fail;
-	}
-
 	if (pasn_params.wrapped_data_format != WPA_PASN_WRAPPED_DATA_NO) {
-		wrapped_data = ieee802_11_defrag(&elems,
-						 WLAN_EID_EXTENSION,
+		wrapped_data = ieee802_11_defrag(&elems, WLAN_EID_EXTENSION,
 						 WLAN_EID_EXT_WRAPPED_DATA);
-
 		if (!wrapped_data) {
 			wpa_printf(MSG_DEBUG, "PASN: Missing wrapped data");
-			goto fail;
+			return;
 		}
-
-#ifdef CONFIG_SAE
-		if (sta->pasn->akmp == WPA_KEY_MGMT_SAE) {
-			ret = pasn_wd_handle_sae_confirm(hapd, sta,
-							 wrapped_data);
-			if (ret) {
-				wpa_printf(MSG_DEBUG,
-					   "PASN: Failed processing SAE confirm");
-				wpabuf_free(wrapped_data);
-				goto fail;
-			}
-		}
-#endif /* CONFIG_SAE */
-#ifdef CONFIG_FILS
-		if (sta->pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 ||
-		    sta->pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) {
-			if (wrapped_data) {
-				wpa_printf(MSG_DEBUG,
-					   "PASN: FILS: Ignore wrapped data");
-			}
-		}
-#endif /* CONFIG_FILS */
-		wpabuf_free(wrapped_data);
+		if (pasn_wd_handle_fils(hapd, sta, wrapped_data))
+			wpa_printf(MSG_DEBUG,
+				   "PASN: Failed processing FILS wrapped data");
+		else
+			pasn->fils_wd_valid = true;
 	}
-
-	wpa_printf(MSG_INFO,
-		   "PASN: Success handling transaction == 3. Store 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);
+	wpabuf_free(wrapped_data);
+#endif /* CONFIG_FILS */
 }
 
 
@@ -3601,7 +2692,12 @@
 			return;
 		}
 
-		handle_auth_pasn_1(hapd, sta, mgmt, len);
+		hapd_initialize_pasn(hapd, sta);
+
+		hapd_pasn_update_params(hapd, sta, mgmt, len);
+		if (handle_auth_pasn_1(sta->pasn, hapd->own_addr,
+				       sta->addr, mgmt, len) < 0)
+			ap_free_sta(hapd, sta);
 	} else if (trans_seq == 3) {
 		if (!sta->pasn) {
 			wpa_printf(MSG_DEBUG,
@@ -3616,7 +2712,18 @@
 			return;
 		}
 
-		handle_auth_pasn_3(hapd, sta, mgmt, len);
+		if (handle_auth_pasn_3(sta->pasn, hapd->own_addr,
+				       sta->addr, mgmt, len) == 0) {
+			ptksa_cache_add(hapd->ptksa, hapd->own_addr, sta->addr,
+					sta->pasn->cipher, 43200,
+					&sta->pasn->ptk, NULL, NULL,
+					sta->pasn->akmp);
+
+			pasn_set_keys_from_cache(hapd, hapd->own_addr,
+						 sta->addr, sta->pasn->cipher,
+						 sta->pasn->akmp);
+		}
+		ap_free_sta(hapd, sta);
 	} else {
 		wpa_printf(MSG_DEBUG,
 			   "PASN: Invalid transaction %u - ignore", trans_seq);
@@ -4036,6 +3143,23 @@
 }
 
 
+static u8 hostapd_max_bssid_indicator(struct hostapd_data *hapd)
+{
+	size_t num_bss_nontx;
+	u8 max_bssid_ind = 0;
+
+	if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1)
+		return 0;
+
+	num_bss_nontx = hapd->iface->num_bss - 1;
+	while (num_bss_nontx > 0) {
+		max_bssid_ind++;
+		num_bss_nontx >>= 1;
+	}
+	return max_bssid_ind;
+}
+
+
 int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta)
 {
 	int i, j = 32, aid;
@@ -4061,7 +3185,7 @@
 	}
 	if (j == 32)
 		return -1;
-	aid = i * 32 + j + 1;
+	aid = i * 32 + j + (1 << hostapd_max_bssid_indicator(hapd));
 	if (aid > 2007)
 		return -1;
 
@@ -4605,6 +3729,15 @@
 					 elems.he_capabilities_len);
 		if (resp != WLAN_STATUS_SUCCESS)
 			return resp;
+
+		if (hapd->iconf->require_he && !(sta->flags & WLAN_STA_HE)) {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_INFO,
+				       "Station does not support mandatory HE PHY - reject association");
+			return WLAN_STATUS_DENIED_HE_NOT_SUPPORTED;
+		}
+
 		if (is_6ghz_op_class(hapd->iconf->op_class)) {
 			if (!(sta->flags & WLAN_STA_HE)) {
 				hostapd_logger(hapd, sta->addr,
@@ -4770,7 +3903,7 @@
 			return WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
 		}
 
-		if (hapd->conf->sae_pwe == 2 &&
+		if (hapd->conf->sae_pwe == SAE_PWE_BOTH &&
 		    sta->auth_alg == WLAN_AUTH_SAE &&
 		    sta->sae && !sta->sae->h2e &&
 		    ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len,
@@ -5251,7 +4384,7 @@
 	}
 #endif /* CONFIG_IEEE80211AX */
 
-	p = hostapd_eid_ext_capab(hapd, p);
+	p = hostapd_eid_ext_capab(hapd, p, false);
 	p = hostapd_eid_bss_max_idle_period(hapd, p);
 	if (sta && sta->qos_map_enabled)
 		p = hostapd_eid_qos_map_set(hapd, p);
@@ -6759,6 +5892,19 @@
 		return;
 	}
 
+#ifdef CONFIG_HS20
+	if (ok && len >= IEEE80211_HDRLEN + 2 &&
+	    mgmt->u.action.category == WLAN_ACTION_WNM &&
+	    mgmt->u.action.u.vs_public_action.action == WNM_NOTIFICATION_REQ &&
+	    sta->hs20_deauth_on_ack) {
+		wpa_printf(MSG_DEBUG, "HS 2.0: Deauthenticate STA " MACSTR
+			   " on acknowledging the WNM-Notification",
+			   MAC2STR(sta->addr));
+		ap_sta_session_timeout(hapd, sta, 0);
+		return;
+	}
+#endif /* CONFIG_HS20 */
+
 	if (len < 24 + 5 + sizeof(*report))
 		return;
 	report = (const struct rrm_measurement_report_element *)
@@ -7524,6 +6670,14 @@
 			    reporting_hapd->conf->ssid.short_ssid)
 				bss_param |= RNR_BSS_PARAM_SAME_SSID;
 
+			if (iface->conf->mbssid != MBSSID_DISABLED &&
+			    iface->num_bss > 1) {
+				bss_param |= RNR_BSS_PARAM_MULTIPLE_BSSID;
+				if (i == 0)
+					bss_param |=
+						RNR_BSS_PARAM_TRANSMITTED_BSSID;
+			}
+
 			if (is_6ghz_op_class(hapd->iconf->op_class) &&
 			    bss->conf->unsol_bcast_probe_resp_interval)
 				bss_param |=
@@ -7612,4 +6766,253 @@
 	return eid;
 }
 
+
+static bool mbssid_known_bss(unsigned int i, const u8 *known_bss,
+			     size_t known_bss_len)
+{
+	if (!known_bss || known_bss_len <= i / 8)
+		return false;
+	known_bss = &known_bss[i / 8];
+	return *known_bss & (u8) (BIT(i % 8));
+}
+
+
+static size_t hostapd_eid_mbssid_elem_len(struct hostapd_data *hapd,
+					  u32 frame_type, size_t *bss_index,
+					  const u8 *known_bss,
+					  size_t known_bss_len)
+{
+	struct hostapd_data *tx_bss = hostapd_mbssid_get_tx_bss(hapd);
+	size_t len = 3, i;
+
+	for (i = *bss_index; i < hapd->iface->num_bss; i++) {
+		struct hostapd_data *bss = hapd->iface->bss[i];
+		const u8 *auth, *rsn = NULL, *rsnx = NULL;
+		size_t nontx_profile_len, auth_len;
+		u8 ie_count = 0;
+
+		if (!bss || !bss->conf || !bss->started ||
+		    mbssid_known_bss(i, known_bss, known_bss_len))
+			continue;
+
+		/*
+		 * Sublement ID: 1 octet
+		 * Length: 1 octet
+		 * Nontransmitted capabilities: 4 octets
+		 * SSID element: 2 + variable
+		 * Multiple BSSID Index Element: 3 octets (+2 octets in beacons)
+		 * Fixed length = 1 + 1 + 4 + 2 + 3 = 11
+		 */
+		nontx_profile_len = 11 + bss->conf->ssid.ssid_len;
+
+		if (frame_type == WLAN_FC_STYPE_BEACON)
+			nontx_profile_len += 2;
+
+		auth = wpa_auth_get_wpa_ie(bss->wpa_auth, &auth_len);
+		if (auth) {
+			rsn = get_ie(auth, auth_len, WLAN_EID_RSN);
+			if (rsn)
+				nontx_profile_len += 2 + rsn[1];
+
+			rsnx = get_ie(auth, auth_len, WLAN_EID_RSNX);
+			if (rsnx)
+				nontx_profile_len += 2 + rsnx[1];
+		}
+		if (!rsn && hostapd_wpa_ie(tx_bss, WLAN_EID_RSN))
+			ie_count++;
+		if (!rsnx && hostapd_wpa_ie(tx_bss, WLAN_EID_RSNX))
+			ie_count++;
+		if (bss->conf->xrates_supported)
+			nontx_profile_len += 8;
+		else if (hapd->conf->xrates_supported)
+			ie_count++;
+		if (ie_count)
+			nontx_profile_len += 4 + ie_count;
+
+		if (len + nontx_profile_len > 255)
+			break;
+
+		len += nontx_profile_len;
+	}
+
+	*bss_index = i;
+	return len;
+}
+
+
+size_t hostapd_eid_mbssid_len(struct hostapd_data *hapd, u32 frame_type,
+			      u8 *elem_count, const u8 *known_bss,
+			      size_t known_bss_len)
+{
+	size_t len = 0, bss_index = 1;
+
+	if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1 ||
+	    (frame_type != WLAN_FC_STYPE_BEACON &&
+	     frame_type != WLAN_FC_STYPE_PROBE_RESP))
+		return 0;
+
+	if (frame_type == WLAN_FC_STYPE_BEACON) {
+		if (!elem_count) {
+			wpa_printf(MSG_INFO,
+				   "MBSSID: Insufficient data for Beacon frames");
+			return 0;
+		}
+		*elem_count = 0;
+	}
+
+	while (bss_index < hapd->iface->num_bss) {
+		len += hostapd_eid_mbssid_elem_len(hapd, frame_type,
+						   &bss_index, known_bss,
+						   known_bss_len);
+
+		if (frame_type == WLAN_FC_STYPE_BEACON)
+			*elem_count += 1;
+	}
+	return len;
+}
+
+
+static u8 * hostapd_eid_mbssid_elem(struct hostapd_data *hapd, u8 *eid, u8 *end,
+				    u32 frame_type, u8 max_bssid_indicator,
+				    size_t *bss_index, u8 elem_count,
+				    const u8 *known_bss, size_t known_bss_len)
+{
+	struct hostapd_data *tx_bss = hostapd_mbssid_get_tx_bss(hapd);
+	size_t i;
+	u8 *eid_len_offset, *max_bssid_indicator_offset;
+
+	*eid++ = WLAN_EID_MULTIPLE_BSSID;
+	eid_len_offset = eid++;
+	max_bssid_indicator_offset = eid++;
+
+	for (i = *bss_index; i < hapd->iface->num_bss; i++) {
+		struct hostapd_data *bss = hapd->iface->bss[i];
+		struct hostapd_bss_config *conf;
+		u8 *eid_len_pos, *nontx_bss_start = eid;
+		const u8 *auth, *rsn = NULL, *rsnx = NULL;
+		u8 ie_count = 0, non_inherit_ie[3];
+		size_t auth_len = 0;
+		u16 capab_info;
+
+		if (!bss || !bss->conf || !bss->started ||
+		    mbssid_known_bss(i, known_bss, known_bss_len))
+			continue;
+		conf = bss->conf;
+
+		*eid++ = WLAN_MBSSID_SUBELEMENT_NONTRANSMITTED_BSSID_PROFILE;
+		eid_len_pos = eid++;
+
+		capab_info = hostapd_own_capab_info(bss);
+		*eid++ = WLAN_EID_NONTRANSMITTED_BSSID_CAPA;
+		*eid++ = sizeof(capab_info);
+		WPA_PUT_LE16(eid, capab_info);
+		eid += sizeof(capab_info);
+
+		*eid++ = WLAN_EID_SSID;
+		*eid++ = conf->ssid.ssid_len;
+		os_memcpy(eid, conf->ssid.ssid, conf->ssid.ssid_len);
+		eid += conf->ssid.ssid_len;
+
+		*eid++ = WLAN_EID_MULTIPLE_BSSID_INDEX;
+		if (frame_type == WLAN_FC_STYPE_BEACON) {
+			*eid++ = 3;
+			*eid++ = i; /* BSSID Index */
+			if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
+			    (conf->dtim_period % elem_count))
+				conf->dtim_period = elem_count;
+			*eid++ = conf->dtim_period;
+			*eid++ = 0xFF; /* DTIM Count */
+		} else {
+			/* Probe Request frame does not include DTIM Period and
+			 * DTIM Count fields. */
+			*eid++ = 1;
+			*eid++ = i; /* BSSID Index */
+		}
+
+		auth = wpa_auth_get_wpa_ie(bss->wpa_auth, &auth_len);
+		if (auth) {
+			rsn = get_ie(auth, auth_len, WLAN_EID_RSN);
+			if (rsn) {
+				os_memcpy(eid, rsn, 2 + rsn[1]);
+				eid += 2 + rsn[1];
+			}
+
+			rsnx = get_ie(auth, auth_len, WLAN_EID_RSNX);
+			if (rsnx) {
+				os_memcpy(eid, rsnx, 2 + rsnx[1]);
+				eid += 2 + rsnx[1];
+			}
+		}
+		if (!rsn && hostapd_wpa_ie(tx_bss, WLAN_EID_RSN))
+			non_inherit_ie[ie_count++] = WLAN_EID_RSN;
+		if (!rsnx && hostapd_wpa_ie(tx_bss, WLAN_EID_RSNX))
+			non_inherit_ie[ie_count++] = WLAN_EID_RSNX;
+		if (hapd->conf->xrates_supported &&
+		    !bss->conf->xrates_supported)
+			non_inherit_ie[ie_count++] = WLAN_EID_EXT_SUPP_RATES;
+		if (ie_count) {
+			*eid++ = WLAN_EID_EXTENSION;
+			*eid++ = 2 + ie_count;
+			*eid++ = WLAN_EID_EXT_NON_INHERITANCE;
+			*eid++ = ie_count;
+			os_memcpy(eid, non_inherit_ie, ie_count);
+			eid += ie_count;
+		}
+
+		*eid_len_pos = (eid - eid_len_pos) - 1;
+
+		if (((eid - eid_len_offset) - 1) > 255) {
+			eid = nontx_bss_start;
+			break;
+		}
+	}
+
+	*bss_index = i;
+	*max_bssid_indicator_offset = max_bssid_indicator;
+	if (*max_bssid_indicator_offset < 1)
+		*max_bssid_indicator_offset = 1;
+	*eid_len_offset = (eid - eid_len_offset) - 1;
+	return eid;
+}
+
+
+u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end,
+			unsigned int frame_stype, u8 elem_count,
+			u8 **elem_offset,
+			const u8 *known_bss, size_t known_bss_len)
+{
+	size_t bss_index = 1;
+	u8 elem_index = 0;
+
+	if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1 ||
+	    (frame_stype != WLAN_FC_STYPE_BEACON &&
+	     frame_stype != WLAN_FC_STYPE_PROBE_RESP))
+		return eid;
+
+	if (frame_stype == WLAN_FC_STYPE_BEACON && !elem_offset) {
+		wpa_printf(MSG_INFO,
+			   "MBSSID: Insufficient data for Beacon frames");
+		return eid;
+	}
+
+	while (bss_index < hapd->iface->num_bss) {
+		if (frame_stype == WLAN_FC_STYPE_BEACON) {
+			if (elem_index == elem_count) {
+				wpa_printf(MSG_WARNING,
+					   "MBSSID: Larger number of elements than there is room in the provided array");
+				break;
+			}
+
+			elem_offset[elem_index] = eid;
+			elem_index = elem_index + 1;
+		}
+		eid = hostapd_eid_mbssid_elem(hapd, eid, end, frame_stype,
+					      hostapd_max_bssid_indicator(hapd),
+					      &bss_index, elem_count,
+					      known_bss, known_bss_len);
+	}
+
+	return eid;
+}
+
 #endif /* CONFIG_NATIVE_WINDOWS */
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index fa1f47b..5f443fc 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -45,7 +45,8 @@
 #endif /* NEED_AP_MLME */
 u16 hostapd_own_capab_info(struct hostapd_data *hapd);
 void ap_ht2040_timeout(void *eloop_data, void *user_data);
-u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid,
+			   bool mbssid_complete);
 u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid);
@@ -214,5 +215,12 @@
 		       enum ieee80211_op_mode opmode,
 		       const u8 *he_capab, size_t he_capab_len,
 		       const u8 *eht_capab, size_t eht_capab_len);
+size_t hostapd_eid_mbssid_len(struct hostapd_data *hapd, u32 frame_type,
+			      u8 *elem_count, const u8 *known_bss,
+			      size_t known_bss_len);
+u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end,
+			unsigned int frame_stype, u8 elem_count,
+			u8 **elem_offset,
+			const u8 *known_bss, size_t known_bss_len);
 
 #endif /* IEEE802_11_H */
diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
index ec36a9e..caaadce 100644
--- a/src/ap/ieee802_11_eht.c
+++ b/src/ap/ieee802_11_eht.c
@@ -42,11 +42,29 @@
 
 
 static u8 ieee80211_eht_mcs_set_size(enum hostapd_hw_mode mode, u8 opclass,
-				     const u8 *he_phy_cap,
+				     u8 he_oper_chwidth, const u8 *he_phy_cap,
 				     const u8 *eht_phy_cap)
 {
 	u8 sz = EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS;
 	bool band24, band5, band6;
+	u8 he_phy_cap_chwidth = ~HE_PHYCAP_CHANNEL_WIDTH_MASK;
+
+	switch (he_oper_chwidth) {
+	case CONF_OPER_CHWIDTH_80P80MHZ:
+		he_phy_cap_chwidth |=
+			HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G;
+		/* fall through */
+	case CONF_OPER_CHWIDTH_160MHZ:
+		he_phy_cap_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
+		/* fall through */
+	case CONF_OPER_CHWIDTH_80MHZ:
+	case CONF_OPER_CHWIDTH_USE_HT:
+		he_phy_cap_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
+			HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G;
+		break;
+	}
+
+	he_phy_cap_chwidth &= he_phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX];
 
 	band24 = mode == HOSTAPD_MODE_IEEE80211B ||
 		mode == HOSTAPD_MODE_IEEE80211G ||
@@ -56,19 +74,18 @@
 	band6 = is_6ghz_op_class(opclass);
 
 	if (band24 &&
-	    (he_phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
-	     HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G) == 0)
+	    (he_phy_cap_chwidth & HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G) == 0)
 		return EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY;
 
 	if (band5 &&
-	    (he_phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
+	    (he_phy_cap_chwidth &
 	     (HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G |
 	      HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
 	      HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G)) == 0)
 		return EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY;
 
 	if (band5 &&
-	    (he_phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
+	    (he_phy_cap_chwidth &
 	     (HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
 	      HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G)))
 	    sz += EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS;
@@ -98,6 +115,7 @@
 		return 0;
 
 	len += ieee80211_eht_mcs_set_size(mode->mode, hapd->iconf->op_class,
+					  hapd->iconf->he_oper_chwidth,
 					  mode->he_capab[opmode].phy_cap,
 					  eht_cap->phy_cap);
 	len += ieee80211_eht_ppet_size(WPA_GET_LE16(&eht_cap->ppet[0]),
@@ -152,6 +170,7 @@
 
 	mcs_nss_len = ieee80211_eht_mcs_set_size(mode->mode,
 						 hapd->iconf->op_class,
+						 hapd->iconf->he_oper_chwidth,
 						 mode->he_capab[opmode].phy_cap,
 						 eht_cap->phy_cap);
 	if (mcs_nss_len) {
@@ -178,17 +197,24 @@
 	struct ieee80211_eht_operation *oper;
 	u8 *pos = eid, seg0 = 0, seg1 = 0;
 	enum oper_chan_width chwidth;
+	size_t elen = 1 + 4 + 3;
 
 	if (!hapd->iface->current_mode)
 		return eid;
 
 	*pos++ = WLAN_EID_EXTENSION;
-	*pos++ = 5;
+	*pos++ = 1 + elen;
 	*pos++ = WLAN_EID_EXT_EHT_OPERATION;
 
 	oper = (struct ieee80211_eht_operation *) pos;
 	oper->oper_params = EHT_OPER_INFO_PRESENT;
 
+	/* TODO: Fill in appropriate EHT-MCS max Nss information */
+	oper->basic_eht_mcs_nss_set[0] = 0x11;
+	oper->basic_eht_mcs_nss_set[1] = 0x00;
+	oper->basic_eht_mcs_nss_set[2] = 0x00;
+	oper->basic_eht_mcs_nss_set[3] = 0x00;
+
 	if (is_6ghz_op_class(conf->op_class))
 		chwidth = op_class_to_ch_width(conf->op_class);
 	else
@@ -227,7 +253,7 @@
 	oper->oper_info.ccfs0 = seg0 ? seg0 : hapd->iconf->channel;
 	oper->oper_info.ccfs1 = seg1;
 
-	return pos + 4;
+	return pos + elen;
 }
 
 
@@ -275,6 +301,7 @@
 	sta_mcs = capab->optional;
 
 	if (ieee80211_eht_mcs_set_size(mode->mode, hapd->iconf->op_class,
+				       hapd->iconf->he_oper_chwidth,
 				       mode->he_capab[opmode].phy_cap,
 				       mode->eht_capab[opmode].phy_cap) ==
 	    EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY)
@@ -300,7 +327,7 @@
 
 
 static bool ieee80211_invalid_eht_cap_size(enum hostapd_hw_mode mode,
-					   u8 opclass,
+					   u8 opclass, u8 he_oper_chwidth,
 					   const u8 *he_cap, const u8 *eht_cap,
 					   size_t len)
 {
@@ -317,8 +344,8 @@
 	if (len < cap_len)
 		return true;
 
-	cap_len += ieee80211_eht_mcs_set_size(mode, opclass, he_phy_cap,
-					      cap->phy_cap);
+	cap_len += ieee80211_eht_mcs_set_size(mode, opclass, he_oper_chwidth,
+					      he_phy_cap, cap->phy_cap);
 	if (len < cap_len)
 		return true;
 
@@ -342,6 +369,7 @@
 	    !he_capab || he_capab_len < IEEE80211_HE_CAPAB_MIN_LEN ||
 	    !eht_capab ||
 	    ieee80211_invalid_eht_cap_size(mode, hapd->iconf->op_class,
+					   hapd->iconf->he_oper_chwidth,
 					   he_capab, eht_capab,
 					   eht_capab_len) ||
 	    !check_valid_eht_mcs(hapd, eht_capab, opmode)) {
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index eaeaec5..31dfb62 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -340,7 +340,8 @@
 }
 
 
-static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx)
+static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx,
+				   bool mbssid_complete)
 {
 	*pos = 0x00;
 
@@ -364,6 +365,8 @@
 			*pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */
 		if (hapd->conf->bss_transition)
 			*pos |= 0x08; /* Bit 19 - BSS Transition */
+		if (hapd->iconf->mbssid)
+			*pos |= 0x40; /* Bit 22 - Multiple BSSID */
 		break;
 	case 3: /* Bits 24-31 */
 #ifdef CONFIG_WNM_AP
@@ -435,6 +438,11 @@
 		    (hapd->iface->drv_flags &
 		     WPA_DRIVER_FLAGS_BEACON_PROTECTION))
 			*pos |= 0x10; /* Bit 84 - Beacon Protection Enabled */
+		if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED)
+			*pos |= 0x08; /* Bit 83 - Enhanced multiple BSSID */
+		if (mbssid_complete)
+			*pos |= 0x01; /* Bit 80 - Complete List of NonTxBSSID
+				       * Profiles */
 		break;
 	case 11: /* Bits 88-95 */
 #ifdef CONFIG_SAE_PK
@@ -448,7 +456,8 @@
 }
 
 
-u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid)
+u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid,
+			   bool mbssid_complete)
 {
 	u8 *pos = eid;
 	u8 len = EXT_CAPA_MAX_LEN, i;
@@ -459,7 +468,7 @@
 	*pos++ = WLAN_EID_EXT_CAPAB;
 	*pos++ = len;
 	for (i = 0; i < len; i++, pos++) {
-		hostapd_ext_capab_byte(hapd, pos, i);
+		hostapd_ext_capab_byte(hapd, pos, i, mbssid_complete);
 
 		if (i < hapd->iface->extended_capa_len) {
 			*pos &= ~hapd->iface->extended_capa_mask[i];
@@ -470,6 +479,13 @@
 			*pos &= ~hapd->conf->ext_capa_mask[i];
 			*pos |= hapd->conf->ext_capa[i];
 		}
+
+		/* Clear bits 83 and 22 if EMA and MBSSID are not enabled
+		 * otherwise association fails with some clients */
+		if (i == 10 && hapd->iconf->mbssid < ENHANCED_MBSSID_ENABLED)
+			*pos &= ~0x08;
+		if (i == 2 && !hapd->iconf->mbssid)
+			*pos &= ~0x40;
 	}
 
 	while (len > 0 && eid[1 + len] == 0) {
@@ -1062,10 +1078,11 @@
 #endif /* CONFIG_SAE_PK */
 
 	if (wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
-	    (hapd->conf->sae_pwe == 1 || hapd->conf->sae_pwe == 2 ||
+	    (hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+	     hapd->conf->sae_pwe == SAE_PWE_BOTH ||
 	     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) {
+	    hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK) {
 		capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
 #ifdef CONFIG_SAE_PK
 		if (sae_pk)
@@ -1078,7 +1095,7 @@
 	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_AP)
-		capab |= BIT(WLAN_RSNX_CAPAB_PROT_RANGE_NEG);
+		capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR);
 
 	flen = (capab & 0xff00) ? 2 : 1;
 	if (len < 2 + flen || !capab)
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index d90792c..46a47d0 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -1709,23 +1709,35 @@
 
 
 static void ieee802_1x_hs20_deauth_req(struct hostapd_data *hapd,
-				       struct sta_info *sta, u8 *pos,
+				       struct sta_info *sta, const u8 *pos,
 				       size_t len)
 {
+	size_t url_len;
+	unsigned int timeout;
+
 	if (len < 3)
 		return; /* Malformed information */
+	url_len = len - 3;
 	sta->hs20_deauth_requested = 1;
+	sta->hs20_deauth_on_ack = url_len == 0;
 	wpa_printf(MSG_DEBUG,
-		   "HS 2.0: Deauthentication request - Code %u  Re-auth Delay %u",
-		   *pos, WPA_GET_LE16(pos + 1));
+		   "HS 2.0: Deauthentication request - Code %u  Re-auth Delay %u  URL length %zu",
+		   *pos, WPA_GET_LE16(pos + 1), url_len);
 	wpabuf_free(sta->hs20_deauth_req);
 	sta->hs20_deauth_req = wpabuf_alloc(len + 1);
 	if (sta->hs20_deauth_req) {
 		wpabuf_put_data(sta->hs20_deauth_req, pos, 3);
-		wpabuf_put_u8(sta->hs20_deauth_req, len - 3);
-		wpabuf_put_data(sta->hs20_deauth_req, pos + 3, len - 3);
+		wpabuf_put_u8(sta->hs20_deauth_req, url_len);
+		wpabuf_put_data(sta->hs20_deauth_req, pos + 3, url_len);
 	}
-	ap_sta_session_timeout(hapd, sta, hapd->conf->hs20_deauth_req_timeout);
+	timeout = hapd->conf->hs20_deauth_req_timeout;
+	/* If there is no URL, no need to provide time to fetch it. Use a short
+	 * timeout here to allow maximum time for completing 4-way handshake and
+	 * WNM-Notification delivery. Acknowledgement of the frame will result
+	 * in cutting this wait further. */
+	if (!url_len && timeout > 2)
+		timeout = 2;
+	ap_sta_session_timeout(hapd, sta, timeout);
 }
 
 
@@ -1813,6 +1825,7 @@
 	buf = NULL;
 	sta->remediation = 0;
 	sta->hs20_deauth_requested = 0;
+	sta->hs20_deauth_on_ack = 0;
 
 	for (;;) {
 		if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c
index b67b852..32d291d 100644
--- a/src/ap/pmksa_cache_auth.c
+++ b/src/ap/pmksa_cache_auth.c
@@ -40,6 +40,7 @@
 {
 	os_free(entry->vlan_desc);
 	os_free(entry->identity);
+	os_free(entry->dpp_pkhash);
 	wpabuf_free(entry->cui);
 #ifndef CONFIG_NO_RADIUS
 	radius_free_class(&entry->radius_class);
diff --git a/src/ap/pmksa_cache_auth.h b/src/ap/pmksa_cache_auth.h
index 2ef2174..e3cee4a 100644
--- a/src/ap/pmksa_cache_auth.h
+++ b/src/ap/pmksa_cache_auth.h
@@ -23,6 +23,8 @@
 	int akmp; /* WPA_KEY_MGMT_* */
 	u8 spa[ETH_ALEN];
 
+	u8 *dpp_pkhash; /* SHA256_MAC_LEN octet hash value of DPP Connector
+			 * public key */
 	u8 *identity;
 	size_t identity_len;
 	struct wpabuf *cui;
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index c541926..63f514c 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -1260,6 +1260,13 @@
 }
 
 
+const u8 * ap_sta_wpa_get_dpp_pkhash(struct hostapd_data *hapd,
+				     struct sta_info *sta)
+{
+	return wpa_auth_get_dpp_pkhash(sta->wpa_sm);
+}
+
+
 void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta,
 			   int authorized)
 {
@@ -1298,10 +1305,13 @@
 					sta->addr, authorized, dev_addr);
 
 	if (authorized) {
+		const u8 *dpp_pkhash;
 		const char *keyid;
+		char dpp_pkhash_buf[100];
 		char keyid_buf[100];
 		char ip_addr[100];
 
+		dpp_pkhash_buf[0] = '\0';
 		keyid_buf[0] = '\0';
 		ip_addr[0] = '\0';
 #ifdef CONFIG_P2P
@@ -1319,14 +1329,27 @@
 				    " keyid=%s", keyid);
 		}
 
-		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s%s",
-			buf, ip_addr, keyid_buf);
+		dpp_pkhash = ap_sta_wpa_get_dpp_pkhash(hapd, sta);
+		if (dpp_pkhash) {
+			const char *prefix = " dpp_pkhash=";
+			size_t plen = os_strlen(prefix);
+
+			os_strlcpy(dpp_pkhash_buf, prefix,
+				   sizeof(dpp_pkhash_buf));
+			wpa_snprintf_hex(&dpp_pkhash_buf[plen],
+					 sizeof(dpp_pkhash_buf) - plen,
+					 dpp_pkhash, SHA256_MAC_LEN);
+		}
+
+		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s%s%s",
+			buf, ip_addr, keyid_buf, dpp_pkhash_buf);
 
 		if (hapd->msg_ctx_parent &&
 		    hapd->msg_ctx_parent != hapd->msg_ctx)
 			wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO,
-					  AP_STA_CONNECTED "%s%s%s",
-					  buf, ip_addr, keyid_buf);
+					  AP_STA_CONNECTED "%s%s%s%s",
+					  buf, ip_addr, keyid_buf,
+					  dpp_pkhash_buf);
 	} else {
 		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED "%s", buf);
 
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index d2a8344..b59b758 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -16,6 +16,7 @@
 #include "common/ieee802_11_defs.h"
 #include "common/sae.h"
 #include "crypto/sha384.h"
+#include "pasn/pasn_common.h"
 
 /* STA flags */
 #define WLAN_STA_AUTH BIT(0)
@@ -68,44 +69,6 @@
 	enum frame_encryption encrypted;
 };
 
-enum pasn_fils_state {
-	PASN_FILS_STATE_NONE = 0,
-	PASN_FILS_STATE_PENDING_AS,
-	PASN_FILS_STATE_COMPLETE
-};
-
-struct pasn_fils_data {
-	u8 state;
-	u8 nonce[FILS_NONCE_LEN];
-	u8 anonce[FILS_NONCE_LEN];
-	u8 session[FILS_SESSION_LEN];
-	u8 erp_pmkid[PMKID_LEN];
-
-	struct wpabuf *erp_resp;
-};
-
-struct pasn_data {
-	int akmp;
-	int cipher;
-	u16 group;
-	bool secure_ltf;
-	u8 trans_seq;
-	u8 wrapped_data_format;
-	size_t kdk_len;
-
-	u8 hash[SHA384_MAC_LEN];
-	struct wpa_ptk ptk;
-	struct crypto_ecdh *ecdh;
-
-	struct wpabuf *secret;
-#ifdef CONFIG_SAE
-	struct sae_data sae;
-#endif /* CONFIG_SAE */
-#ifdef CONFIG_FILS
-	struct pasn_fils_data fils;
-#endif /* CONFIG_FILS */
-};
-
 struct sta_info {
 	struct sta_info *next; /* next entry in sta list */
 	struct sta_info *hnext; /* next entry in hash table list */
@@ -155,6 +118,7 @@
 	unsigned int qos_map_enabled:1;
 	unsigned int remediation:1;
 	unsigned int hs20_deauth_requested:1;
+	unsigned int hs20_deauth_on_ack:1;
 	unsigned int session_timeout_set:1;
 	unsigned int radius_das_match:1;
 	unsigned int ecsa_supported:1;
@@ -390,6 +354,8 @@
 int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta);
 const char * ap_sta_wpa_get_keyid(struct hostapd_data *hapd,
 				  struct sta_info *sta);
+const u8 * ap_sta_wpa_get_dpp_pkhash(struct hostapd_data *hapd,
+				     struct sta_info *sta);
 void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
 		       const u8 *addr, u16 reason);
 
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 64a1800..7aff64f 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -23,6 +23,7 @@
 #include "crypto/sha1.h"
 #include "crypto/sha256.h"
 #include "crypto/sha384.h"
+#include "crypto/sha512.h"
 #include "crypto/random.h"
 #include "eapol_auth/eapol_auth_sm.h"
 #include "drivers/driver.h"
@@ -1652,22 +1653,25 @@
 		if (pad_len)
 			*pos++ = 0xdd;
 
-		wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data",
+		wpa_hexdump_key(MSG_DEBUG,
+				"Plaintext EAPOL-Key Key Data (+ padding)",
 				buf, key_data_len);
 		if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
 		    wpa_use_aes_key_wrap(sm->wpa_key_mgmt) ||
 		    version == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
-			wpa_printf(MSG_DEBUG,
-				   "WPA: Encrypt Key Data using AES-WRAP (KEK length %zu)",
-				   sm->PTK.kek_len);
+			wpa_hexdump_key(MSG_DEBUG, "RSN: AES-WRAP using KEK",
+					sm->PTK.kek, sm->PTK.kek_len);
 			if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len,
 				     (key_data_len - 8) / 8, buf, key_data)) {
 				os_free(hdr);
 				bin_clear_free(buf, key_data_len);
 				return;
 			}
+			wpa_hexdump(MSG_DEBUG,
+				    "RSN: Encrypted Key Data from AES-WRAP",
+				    key_data, key_data_len);
 			WPA_PUT_BE16(key_mic + mic_len, key_data_len);
-#ifndef CONFIG_NO_RC4
+#if !defined(CONFIG_NO_RC4) && !defined(CONFIG_FIPS)
 		} else if (sm->PTK.kek_len == 16) {
 			u8 ek[32];
 
@@ -1681,7 +1685,7 @@
 			os_memcpy(key_data, buf, key_data_len);
 			rc4_skip(ek, 32, 256, key_data, key_data_len);
 			WPA_PUT_BE16(key_mic + mic_len, key_data_len);
-#endif /* CONFIG_NO_RC4 */
+#endif /* !(CONFIG_NO_RC4 || CONFIG_FIPS) */
 		} else {
 			os_free(hdr);
 			bin_clear_free(buf, key_data_len);
@@ -1716,6 +1720,7 @@
 	}
 
 	wpa_auth_set_eapol(wpa_auth, sm->addr, WPA_EAPOL_inc_EapolFramesTx, 1);
+	wpa_hexdump(MSG_DEBUG, "Send EAPOL-Key msg", hdr, len);
 	wpa_auth_send_eapol(wpa_auth, sm->addr, (u8 *) hdr, len,
 			sm->pairwise_set);
 	os_free(hdr);
@@ -1735,10 +1740,25 @@
 	if (!sm)
 		return;
 
+	ctr = pairwise ? sm->TimeoutCtr : sm->GTimeoutCtr;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	/* When delay_eapol_tx is true, delay the EAPOL-Key transmission by
+	 * sending it only on the last attempt after all timeouts for the prior
+	 * skipped attemps. */
+	if (wpa_auth->conf.delay_eapol_tx &&
+	    ctr != wpa_auth->conf.wpa_pairwise_update_count) {
+		wpa_msg(sm->wpa_auth->conf.msg_ctx, MSG_INFO,
+			"DELAY-EAPOL-TX-%d", ctr);
+		goto skip_tx;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
 	__wpa_send_eapol(wpa_auth, sm, key_info, key_rsc, nonce, kde, kde_len,
 			 keyidx, encr, 0);
+#ifdef CONFIG_TESTING_OPTIONS
+skip_tx:
+#endif /* CONFIG_TESTING_OPTIONS */
 
-	ctr = pairwise ? sm->TimeoutCtr : sm->GTimeoutCtr;
 	if (ctr == 1 && wpa_auth->conf.tx_status)
 		timeout_ms = pairwise ? eapol_key_timeout_first :
 			eapol_key_timeout_first_group;
@@ -2192,13 +2212,20 @@
 		os_memcpy(sm->PMK, psk, psk_len);
 		sm->pmk_len = psk_len;
 #ifdef CONFIG_IEEE80211R_AP
-		os_memcpy(sm->xxkey, psk, PMK_LEN);
 		sm->xxkey_len = PMK_LEN;
+#ifdef CONFIG_SAE
+		if (sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+		    (psk_len == SHA512_MAC_LEN || psk_len == SHA384_MAC_LEN ||
+		     psk_len == SHA256_MAC_LEN))
+			sm->xxkey_len = psk_len;
+#endif /* CONFIG_SAE */
+		os_memcpy(sm->xxkey, psk, sm->xxkey_len);
 #endif /* CONFIG_IEEE80211R_AP */
 	}
 #ifdef CONFIG_SAE
 	if (wpa_auth_uses_sae(sm) && sm->pmksa) {
-		wpa_printf(MSG_DEBUG, "SAE: PMK from PMKSA cache");
+		wpa_printf(MSG_DEBUG, "SAE: PMK from PMKSA cache (len=%zu)",
+			   sm->pmksa->pmk_len);
 		os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len);
 		sm->pmk_len = sm->pmksa->pmk_len;
 #ifdef CONFIG_IEEE80211R_AP
@@ -2460,7 +2487,6 @@
 		struct wpa_authenticator *wpa_auth = sm->wpa_auth;
 		struct wpa_auth_config *conf = &wpa_auth->conf;
 		u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN];
-		int use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
 
 		if (wpa_derive_pmk_r0(fils_ft, fils_ft_len,
 				      conf->ssid, conf->ssid_len,
@@ -2468,7 +2494,7 @@
 				      conf->r0_key_holder,
 				      conf->r0_key_holder_len,
 				      sm->addr, pmk_r0, pmk_r0_name,
-				      use_sha384) < 0)
+				      sm->wpa_key_mgmt) < 0)
 			return -1;
 
 		wpa_ft_store_pmk_fils(sm, pmk_r0, pmk_r0_name);
@@ -2476,7 +2502,7 @@
 
 		res = wpa_derive_pmk_r1_name(pmk_r0_name, conf->r1_key_holder,
 					     sm->addr, sm->pmk_r1_name,
-					     use_sha384);
+					     fils_ft_len);
 		forced_memzero(pmk_r0, PMK_LEN_MAX);
 		if (res < 0)
 			return -1;
@@ -3694,9 +3720,8 @@
 				  2 + sm->assoc_resp_ftie[1]);
 			res = 2 + sm->assoc_resp_ftie[1];
 		} else {
-			int use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
-
-			res = wpa_write_ftie(conf, use_sha384,
+			res = wpa_write_ftie(conf, sm->wpa_key_mgmt,
+					     sm->xxkey_len,
 					     conf->r0_key_holder,
 					     conf->r0_key_holder_len,
 					     NULL, NULL, pos,
@@ -4814,11 +4839,13 @@
 			  "wpa=%d\n"
 			  "AKMSuiteSelector=" RSN_SUITE "\n"
 			  "hostapdWPAPTKState=%d\n"
-			  "hostapdWPAPTKGroupState=%d\n",
+			  "hostapdWPAPTKGroupState=%d\n"
+			  "hostapdMFPR=%d\n",
 			  sm->wpa,
 			  RSN_SUITE_ARG(wpa_akm_to_suite(sm->wpa_key_mgmt)),
 			  sm->wpa_ptk_state,
-			  sm->wpa_ptk_group_state);
+			  sm->wpa_ptk_group_state,
+			  sm->mfpr);
 	if (os_snprintf_error(buflen - len, ret))
 		return len;
 	len += ret;
@@ -4855,6 +4882,14 @@
 }
 
 
+const u8 * wpa_auth_get_dpp_pkhash(struct wpa_state_machine *sm)
+{
+	if (!sm || !sm->pmksa)
+		return NULL;
+	return sm->pmksa->dpp_pkhash;
+}
+
+
 int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm)
 {
 	if (!sm)
@@ -5017,6 +5052,29 @@
 }
 
 
+int wpa_auth_pmksa_add3(struct wpa_authenticator *wpa_auth, const u8 *addr,
+			const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+			int session_timeout, int akmp, const u8 *dpp_pkhash)
+{
+	struct rsn_pmksa_cache_entry *entry;
+
+	if (wpa_auth->conf.disable_pmksa_caching)
+		return -1;
+
+	wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK (3)", pmk, PMK_LEN);
+	entry = pmksa_cache_auth_add(wpa_auth->pmksa, pmk, pmk_len, pmkid,
+				 NULL, 0, wpa_auth->addr, addr, session_timeout,
+				 NULL, akmp);
+	if (!entry)
+		return -1;
+
+	if (dpp_pkhash)
+		entry->dpp_pkhash = os_memdup(dpp_pkhash, SHA256_MAC_LEN);
+
+	return 0;
+}
+
+
 void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
 			   const u8 *sta_addr)
 {
@@ -5102,6 +5160,15 @@
 #endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
 
 
+struct rsn_pmksa_cache *
+wpa_auth_get_pmksa_cache(struct wpa_authenticator *wpa_auth)
+{
+	if (!wpa_auth || !wpa_auth->pmksa)
+		return NULL;
+	return wpa_auth->pmksa;
+}
+
+
 struct rsn_pmksa_cache_entry *
 wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
 		   const u8 *pmkid)
@@ -5462,13 +5529,14 @@
 
 
 #ifdef CONFIG_IEEE80211R_AP
-int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth, int use_sha384,
+int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth,
+		       struct wpa_state_machine *sm,
 		       u8 *buf, size_t len)
 {
 	struct wpa_auth_config *conf = &wpa_auth->conf;
 
-	return wpa_write_ftie(conf, use_sha384, conf->r0_key_holder,
-			      conf->r0_key_holder_len,
+	return wpa_write_ftie(conf, sm->wpa_key_mgmt, sm->xxkey_len,
+			      conf->r0_key_holder, conf->r0_key_holder_len,
 			      NULL, NULL, buf, len, NULL, 0, 0);
 }
 #endif /* CONFIG_IEEE80211R_AP */
@@ -5682,9 +5750,8 @@
 				  2 + sm->assoc_resp_ftie[1]);
 			res = 2 + sm->assoc_resp_ftie[1];
 		} else {
-			int use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
-
-			res = wpa_write_ftie(conf, use_sha384,
+			res = wpa_write_ftie(conf, sm->wpa_key_mgmt,
+					     sm->xxkey_len,
 					     conf->r0_key_holder,
 					     conf->r0_key_holder_len,
 					     NULL, NULL, pos,
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index a18f7cb..3b32fe3 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -242,6 +242,7 @@
 	int ft_rsnxe_used;
 	unsigned int skip_send_eapol:1;
 	unsigned int enable_eapol_large_timeout:1;
+	bool delay_eapol_tx;
 #endif /* CONFIG_TESTING_OPTIONS */
 	unsigned int oci_freq_override_eapol_m3;
 	unsigned int oci_freq_override_eapol_g1;
@@ -257,7 +258,7 @@
 	unsigned int fils_cache_id_set:1;
 	u8 fils_cache_id[FILS_CACHE_ID_LEN];
 #endif /* CONFIG_FILS */
-	int sae_pwe;
+	enum sae_pwe sae_pwe;
 	bool sae_pk;
 
 	unsigned int secure_ltf:1;
@@ -412,6 +413,7 @@
 int wpa_auth_pairwise_set(struct wpa_state_machine *sm);
 int wpa_auth_get_pairwise(struct wpa_state_machine *sm);
 const u8 * wpa_auth_get_pmk(struct wpa_state_machine *sm, int *len);
+const u8 * wpa_auth_get_dpp_pkhash(struct wpa_state_machine *sm);
 int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm);
 int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm);
 int wpa_auth_sta_ft_tk_already_set(struct wpa_state_machine *sm);
@@ -437,6 +439,9 @@
 int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr,
 			const u8 *pmk, size_t pmk_len, const u8 *pmkid,
 			int session_timeout, int akmp);
+int wpa_auth_pmksa_add3(struct wpa_authenticator *wpa_auth, const u8 *addr,
+			const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+			int session_timeout, int akmp, const u8 *dpp_pkhash);
 void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
 			   const u8 *sta_addr);
 int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf,
@@ -450,6 +455,8 @@
 			    const u8 *pmkid, int expiration);
 int wpa_auth_pmksa_add_entry(struct wpa_authenticator *wpa_auth,
 			     struct rsn_pmksa_cache_entry *entry);
+struct rsn_pmksa_cache *
+wpa_auth_get_pmksa_cache(struct wpa_authenticator *wpa_auth);
 struct rsn_pmksa_cache_entry *
 wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
 		   const u8 *pmkid);
@@ -537,7 +544,8 @@
 int get_sta_tx_parameters(struct wpa_state_machine *sm, int ap_max_chanwidth,
 			  int ap_seg1_idx, int *bandwidth, int *seg1_idx);
 
-int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth, int use_sha384,
+int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth,
+		       struct wpa_state_machine *sm,
 		       u8 *buf, size_t len);
 void wpa_auth_get_fils_aead_params(struct wpa_state_machine *sm,
 				   u8 *fils_anonce, u8 *fils_snonce,
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index 1b1324b..88d63bb 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -20,6 +20,7 @@
 #include "crypto/aes_siv.h"
 #include "crypto/aes_wrap.h"
 #include "crypto/sha384.h"
+#include "crypto/sha512.h"
 #include "crypto/random.h"
 #include "ap_config.h"
 #include "ieee802_11.h"
@@ -805,15 +806,29 @@
 }
 
 
-int wpa_write_ftie(struct wpa_auth_config *conf, int use_sha384,
+int wpa_write_ftie(struct wpa_auth_config *conf, int key_mgmt, size_t key_len,
 		   const u8 *r0kh_id, size_t r0kh_id_len,
 		   const u8 *anonce, const u8 *snonce,
 		   u8 *buf, size_t len, const u8 *subelem,
 		   size_t subelem_len, int rsnxe_used)
 {
 	u8 *pos = buf, *ielen;
-	size_t hdrlen = use_sha384 ? sizeof(struct rsn_ftie_sha384) :
-		sizeof(struct rsn_ftie);
+	size_t hdrlen;
+	u16 mic_control = rsnxe_used ? FTE_MIC_CTRL_RSNXE_USED : 0;
+
+	if (key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+	    key_len == SHA256_MAC_LEN)
+		hdrlen = sizeof(struct rsn_ftie);
+	else if (key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+		 key_len == SHA384_MAC_LEN)
+		hdrlen = sizeof(struct rsn_ftie_sha384);
+	else if (key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+		 key_len == SHA512_MAC_LEN)
+		hdrlen = sizeof(struct rsn_ftie_sha512);
+	else if (wpa_key_mgmt_sha384(key_mgmt))
+		hdrlen = sizeof(struct rsn_ftie_sha384);
+	else
+		hdrlen = sizeof(struct rsn_ftie);
 
 	if (len < 2 + hdrlen + 2 + FT_R1KH_ID_LEN + 2 + r0kh_id_len +
 	    subelem_len)
@@ -822,12 +837,27 @@
 	*pos++ = WLAN_EID_FAST_BSS_TRANSITION;
 	ielen = pos++;
 
-	if (use_sha384) {
+	if (key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+	    key_len == SHA512_MAC_LEN) {
+		struct rsn_ftie_sha512 *hdr = (struct rsn_ftie_sha512 *) pos;
+
+		os_memset(hdr, 0, sizeof(*hdr));
+		pos += sizeof(*hdr);
+		mic_control |= FTE_MIC_LEN_32 << FTE_MIC_CTRL_MIC_LEN_SHIFT;
+		WPA_PUT_LE16(hdr->mic_control, mic_control);
+		if (anonce)
+			os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN);
+		if (snonce)
+			os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN);
+	} else if ((key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+		    key_len == SHA384_MAC_LEN) ||
+		   wpa_key_mgmt_sha384(key_mgmt)) {
 		struct rsn_ftie_sha384 *hdr = (struct rsn_ftie_sha384 *) pos;
 
 		os_memset(hdr, 0, sizeof(*hdr));
 		pos += sizeof(*hdr);
-		WPA_PUT_LE16(hdr->mic_control, !!rsnxe_used);
+		mic_control |= FTE_MIC_LEN_24 << FTE_MIC_CTRL_MIC_LEN_SHIFT;
+		WPA_PUT_LE16(hdr->mic_control, mic_control);
 		if (anonce)
 			os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN);
 		if (snonce)
@@ -837,7 +867,8 @@
 
 		os_memset(hdr, 0, sizeof(*hdr));
 		pos += sizeof(*hdr);
-		WPA_PUT_LE16(hdr->mic_control, !!rsnxe_used);
+		mic_control |= FTE_MIC_LEN_16 << FTE_MIC_CTRL_MIC_LEN_SHIFT;
+		WPA_PUT_LE16(hdr->mic_control, mic_control);
 		if (anonce)
 			os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN);
 		if (snonce)
@@ -2081,9 +2112,7 @@
 int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk)
 {
 	u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN];
-	size_t pmk_r0_len = wpa_key_mgmt_sha384(sm->wpa_key_mgmt) ?
-		SHA384_MAC_LEN : PMK_LEN;
-	size_t pmk_r1_len = pmk_r0_len;
+	size_t pmk_r0_len, pmk_r1_len;
 	u8 pmk_r1[PMK_LEN_MAX];
 	u8 ptk_name[WPA_PMK_NAME_LEN];
 	const u8 *mdid = sm->wpa_auth->conf.mobility_domain;
@@ -2101,6 +2130,17 @@
 	const u8 *mpmk;
 	size_t mpmk_len;
 
+	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+	    (sm->xxkey_len == SHA256_MAC_LEN ||
+	     sm->xxkey_len == SHA384_MAC_LEN ||
+	     sm->xxkey_len == SHA512_MAC_LEN))
+		pmk_r0_len = sm->xxkey_len;
+	else if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt))
+		pmk_r0_len = SHA384_MAC_LEN;
+	else
+		pmk_r0_len = PMK_LEN;
+	pmk_r1_len = pmk_r0_len;
+
 	if (sm->xxkey_len > 0) {
 		mpmk = sm->xxkey;
 		mpmk_len = sm->xxkey_len;
@@ -2127,7 +2167,7 @@
 	if (wpa_derive_pmk_r0(mpmk, mpmk_len, ssid, ssid_len, mdid,
 			      r0kh, r0kh_len, sm->addr,
 			      pmk_r0, pmk_r0_name,
-			      wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) < 0)
+			      sm->wpa_key_mgmt) < 0)
 		return -1;
 	if (!psk_local || !wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt))
 		wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_len,
@@ -2521,12 +2561,11 @@
 	u8 *anonce, *snonce;
 	const u8 *kck;
 	size_t kck_len;
-	int use_sha384;
+	size_t key_len;
 
 	if (sm == NULL)
 		return pos;
 
-	use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
 	conf = &sm->wpa_auth->conf;
 
 	if (!wpa_key_mgmt_ft(sm->wpa_key_mgmt))
@@ -2697,7 +2736,8 @@
 		snonce = NULL;
 	}
 	rsnxe_used = (auth_alg == WLAN_AUTH_FT) &&
-		(conf->sae_pwe == 1 || conf->sae_pwe == 2);
+		(conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+		 conf->sae_pwe == SAE_PWE_BOTH);
 #ifdef CONFIG_TESTING_OPTIONS
 	if (sm->wpa_auth->conf.ft_rsnxe_used) {
 		rsnxe_used = sm->wpa_auth->conf.ft_rsnxe_used == 1;
@@ -2705,7 +2745,20 @@
 			   rsnxe_used);
 	}
 #endif /* CONFIG_TESTING_OPTIONS */
-	res = wpa_write_ftie(conf, use_sha384, r0kh_id, r0kh_id_len,
+	key_len = sm->xxkey_len;
+	if (!key_len)
+		key_len = sm->pmk_r1_len;
+	if (!key_len && sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+	    sm->wpa_auth->cb->get_psk) {
+		size_t psk_len;
+
+		if (sm->wpa_auth->cb->get_psk(sm->wpa_auth->cb_ctx,
+					      sm->addr, sm->p2p_dev_addr,
+					      NULL, &psk_len, NULL))
+			key_len = psk_len;
+	}
+	res = wpa_write_ftie(conf, sm->wpa_key_mgmt, key_len,
+			     r0kh_id, r0kh_id_len,
 			     anonce, snonce, pos, end - pos,
 			     subelem, subelem_len, rsnxe_used);
 	os_free(subelem);
@@ -2715,7 +2768,16 @@
 	ftie_len = res;
 	pos += res;
 
-	if (use_sha384) {
+	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+	    key_len == SHA512_MAC_LEN) {
+		struct rsn_ftie_sha512 *_ftie =
+			(struct rsn_ftie_sha512 *) (ftie + 2);
+
+		fte_mic = _ftie->mic;
+		elem_count = &_ftie->mic_control[1];
+	} else if ((sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+		    key_len == SHA384_MAC_LEN) ||
+		   wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) {
 		struct rsn_ftie_sha384 *_ftie =
 			(struct rsn_ftie_sha384 *) (ftie + 2);
 
@@ -2731,8 +2793,8 @@
 		*elem_count = 3; /* Information element count */
 
 	ric_start = pos;
-	if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse, use_sha384) == 0
-	    && parse.ric) {
+	if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse,
+			     sm->wpa_key_mgmt) == 0 && parse.ric) {
 		pos = wpa_ft_process_ric(sm, pos, end, parse.ric,
 					 parse.ric_len);
 		if (auth_alg == WLAN_AUTH_FT)
@@ -2772,7 +2834,8 @@
 		kck_len = sm->PTK.kck_len;
 	}
 	if (auth_alg == WLAN_AUTH_FT &&
-	    wpa_ft_mic(kck, kck_len, sm->addr, sm->wpa_auth->addr, 6,
+	    wpa_ft_mic(sm->wpa_key_mgmt, kck, kck_len,
+		       sm->addr, sm->wpa_auth->addr, 6,
 		       mdie, mdie_len, ftie, ftie_len,
 		       rsnie, rsnie_len,
 		       ric_start, ric_start ? pos - ric_start : 0,
@@ -2913,7 +2976,8 @@
 
 		if (wpa_derive_pmk_r0(pmk, PMK_LEN, ssid, ssid_len, mdid, r0kh,
 				      r0kh_len, sm->addr,
-				      pmk_r0, pmk_r0_name, 0) < 0 ||
+				      pmk_r0, pmk_r0_name,
+				      WPA_KEY_MGMT_FT_PSK) < 0 ||
 		    wpa_derive_pmk_r1(pmk_r0, PMK_LEN, pmk_r0_name, r1kh,
 				      sm->addr, pmk_r1, pmk_r1_name) < 0 ||
 		    os_memcmp_const(pmk_r1_name, req_pmk_r1_name,
@@ -3013,7 +3077,8 @@
 				      const u8 **identity, size_t *identity_len,
 				      const u8 **radius_cui,
 				      size_t *radius_cui_len,
-				      int *out_session_timeout)
+				      int *out_session_timeout,
+				      size_t *pmk_r1_len)
 {
 	struct wpa_auth_config *conf = &wpa_auth->conf;
 	const struct wpa_ft_pmk_r0_sa *r0;
@@ -3072,6 +3137,8 @@
 
 	*out_session_timeout = session_timeout;
 
+	*pmk_r1_len = r0->pmk_r0_len;
+
 	return 0;
 }
 
@@ -3092,8 +3159,7 @@
 	struct vlan_description vlan;
 	const u8 *identity, *radius_cui;
 	size_t identity_len = 0, radius_cui_len = 0;
-	int use_sha384;
-	size_t pmk_r1_len, kdk_len;
+	size_t pmk_r1_len, kdk_len, len;
 
 	*resp_ies = NULL;
 	*resp_ies_len = 0;
@@ -3104,12 +3170,10 @@
 	wpa_hexdump(MSG_DEBUG, "FT: Received authentication frame IEs",
 		    ies, ies_len);
 
-	if (wpa_ft_parse_ies(ies, ies_len, &parse, -1)) {
+	if (wpa_ft_parse_ies(ies, ies_len, &parse, 0)) {
 		wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs");
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 	}
-	use_sha384 = wpa_key_mgmt_sha384(parse.key_mgmt);
-	pmk_r1_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN;
 
 	mdie = (struct rsn_mdie *) parse.mdie;
 	if (mdie == NULL || parse.mdie_len < sizeof(*mdie) ||
@@ -3120,26 +3184,9 @@
 		return WLAN_STATUS_INVALID_MDIE;
 	}
 
-	if (use_sha384) {
-		struct rsn_ftie_sha384 *ftie;
-
-		ftie = (struct rsn_ftie_sha384 *) parse.ftie;
-		if (!ftie || parse.ftie_len < sizeof(*ftie)) {
-			wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
-			return WLAN_STATUS_INVALID_FTIE;
-		}
-
-		os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
-	} else {
-		struct rsn_ftie *ftie;
-
-		ftie = (struct rsn_ftie *) parse.ftie;
-		if (!ftie || parse.ftie_len < sizeof(*ftie)) {
-			wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
-			return WLAN_STATUS_INVALID_FTIE;
-		}
-
-		os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
+	if (!parse.ftie || parse.ftie_len < sizeof(struct rsn_ftie)) {
+		wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+		return WLAN_STATUS_INVALID_FTIE;
 	}
 
 	if (parse.r0kh_id == NULL) {
@@ -3162,49 +3209,73 @@
 
 	wpa_hexdump(MSG_DEBUG, "FT: Requested PMKR0Name",
 		    parse.rsn_pmkid, WPA_PMK_NAME_LEN);
-	if (wpa_derive_pmk_r1_name(parse.rsn_pmkid,
-				   sm->wpa_auth->conf.r1_key_holder, sm->addr,
-				   pmk_r1_name, use_sha384) < 0)
-		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 
 	if (conf->ft_psk_generate_local &&
 	    wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) {
+		if (wpa_derive_pmk_r1_name(parse.rsn_pmkid,
+					   sm->wpa_auth->conf.r1_key_holder,
+					   sm->addr, pmk_r1_name, PMK_LEN) < 0)
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
 		if (wpa_ft_psk_pmk_r1(sm, pmk_r1_name, pmk_r1, &pairwise,
 				      &vlan, &identity, &identity_len,
 				      &radius_cui, &radius_cui_len,
 				      &session_timeout) < 0)
 			return WLAN_STATUS_INVALID_PMKID;
+		pmk_r1_len = PMK_LEN;
 		wpa_printf(MSG_DEBUG,
 			   "FT: Generated PMK-R1 for FT-PSK locally");
-	} else if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name,
-				       pmk_r1, &pmk_r1_len, &pairwise, &vlan,
-				       &identity, &identity_len, &radius_cui,
-				       &radius_cui_len, &session_timeout) < 0) {
-		wpa_printf(MSG_DEBUG,
-			   "FT: No PMK-R1 available in local cache for the requested PMKR1Name");
-		if (wpa_ft_local_derive_pmk_r1(sm->wpa_auth, sm,
-					       parse.r0kh_id, parse.r0kh_id_len,
-					       parse.rsn_pmkid,
-					       pmk_r1_name, pmk_r1, &pairwise,
-					       &vlan, &identity, &identity_len,
-					       &radius_cui, &radius_cui_len,
-					       &session_timeout) == 0) {
+		goto pmk_r1_derived;
+	}
+
+	/* Need to test all possible hash algorithms for FT-SAE-EXT-KEY since
+	 * the key length is not yet known. For other AKMs, only the length
+	 * identified by the AKM is used. */
+	for (len = SHA256_MAC_LEN; len <= SHA512_MAC_LEN; len += 16) {
+		if (parse.key_mgmt != WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+		    ((wpa_key_mgmt_sha384(parse.key_mgmt) &&
+		      len != SHA384_MAC_LEN) ||
+		     (!wpa_key_mgmt_sha384(parse.key_mgmt) &&
+		      len != SHA256_MAC_LEN)))
+			continue;
+		if (wpa_derive_pmk_r1_name(parse.rsn_pmkid,
+					   sm->wpa_auth->conf.r1_key_holder,
+					   sm->addr, pmk_r1_name, len) < 0)
+			continue;
+
+		if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name,
+					pmk_r1, &pmk_r1_len, &pairwise, &vlan,
+					&identity, &identity_len, &radius_cui,
+					&radius_cui_len,
+					&session_timeout) == 0) {
 			wpa_printf(MSG_DEBUG,
-				   "FT: Generated PMK-R1 based on local PMK-R0");
+				   "FT: Found PMKR1Name (using SHA%zu) from local cache",
+				   pmk_r1_len * 8);
 			goto pmk_r1_derived;
 		}
-
-		if (wpa_ft_pull_pmk_r1(sm, ies, ies_len, parse.rsn_pmkid) < 0) {
-			wpa_printf(MSG_DEBUG,
-				   "FT: Did not have matching PMK-R1 and either unknown or blocked R0KH-ID or NAK from R0KH");
-			return WLAN_STATUS_INVALID_PMKID;
-		}
-
-		return -1; /* Status pending */
-	} else {
-		wpa_printf(MSG_DEBUG, "FT: Found PMKR1Name from local cache");
 	}
 
+	wpa_printf(MSG_DEBUG,
+		   "FT: No PMK-R1 available in local cache for the requested PMKR1Name");
+	if (wpa_ft_local_derive_pmk_r1(sm->wpa_auth, sm,
+				       parse.r0kh_id, parse.r0kh_id_len,
+				       parse.rsn_pmkid,
+				       pmk_r1_name, pmk_r1, &pairwise,
+				       &vlan, &identity, &identity_len,
+				       &radius_cui, &radius_cui_len,
+				       &session_timeout, &pmk_r1_len) == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "FT: Generated PMK-R1 based on local PMK-R0");
+		goto pmk_r1_derived;
+	}
+
+	if (wpa_ft_pull_pmk_r1(sm, ies, ies_len, parse.rsn_pmkid) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "FT: Did not have matching PMK-R1 and either unknown or blocked R0KH-ID or NAK from R0KH");
+		return WLAN_STATUS_INVALID_PMKID;
+	}
+
+	return -1; /* Status pending */
+
 pmk_r1_derived:
 	wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, pmk_r1_len);
 	sm->pmk_r1_name_valid = 1;
@@ -3218,6 +3289,40 @@
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 	}
 
+	/* Now that we know the correct PMK-R1 length and as such, the length
+	 * of the MIC field, fetch the SNonce. */
+	if (pmk_r1_len == SHA512_MAC_LEN) {
+		const struct rsn_ftie_sha512 *ftie;
+
+		ftie = (const struct rsn_ftie_sha512 *) parse.ftie;
+		if (!ftie || parse.ftie_len < sizeof(*ftie)) {
+			wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+			return WLAN_STATUS_INVALID_FTIE;
+		}
+
+		os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
+	} else if (pmk_r1_len == SHA384_MAC_LEN) {
+		const struct rsn_ftie_sha384 *ftie;
+
+		ftie = (const struct rsn_ftie_sha384 *) parse.ftie;
+		if (!ftie || parse.ftie_len < sizeof(*ftie)) {
+			wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+			return WLAN_STATUS_INVALID_FTIE;
+		}
+
+		os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
+	} else {
+		const struct rsn_ftie *ftie;
+
+		ftie = (const struct rsn_ftie *) parse.ftie;
+		if (!ftie || parse.ftie_len < sizeof(*ftie)) {
+			wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+			return WLAN_STATUS_INVALID_FTIE;
+		}
+
+		os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
+	}
+
 	wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
 		    sm->SNonce, WPA_NONCE_LEN);
 	wpa_hexdump(MSG_DEBUG, "FT: Generated ANonce",
@@ -3232,14 +3337,14 @@
 
 	if (wpa_pmk_r1_to_ptk(pmk_r1, pmk_r1_len, sm->SNonce, sm->ANonce,
 			      sm->addr, sm->wpa_auth->addr, pmk_r1_name,
-			      &sm->PTK, ptk_name, sm->wpa_key_mgmt,
+			      &sm->PTK, ptk_name, parse.key_mgmt,
 			      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_ltf_keyseed(&sm->PTK, parse.key_mgmt, pairwise)) {
 		wpa_printf(MSG_DEBUG, "FT: Failed to derive LTF keyseed");
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 	}
@@ -3282,7 +3387,8 @@
 		goto fail;
 	pos += ret;
 
-	ret = wpa_write_ftie(conf, use_sha384, parse.r0kh_id, parse.r0kh_id_len,
+	ret = wpa_write_ftie(conf, parse.key_mgmt, pmk_r1_len,
+			     parse.r0kh_id, parse.r0kh_id_len,
 			     sm->ANonce, sm->SNonce, pos, end - pos, NULL, 0,
 			     0);
 	if (ret < 0)
@@ -3349,25 +3455,20 @@
 	struct wpa_ft_ies parse;
 	struct rsn_mdie *mdie;
 	u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
-	size_t mic_len = 16;
+	size_t mic_len;
 	unsigned int count;
 	const u8 *kck;
 	size_t kck_len;
-	int use_sha384;
-	const u8 *anonce, *snonce, *fte_mic;
-	u8 fte_elem_count;
-	int rsnxe_used;
 	struct wpa_auth_config *conf;
 
 	if (sm == NULL)
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 
 	conf = &sm->wpa_auth->conf;
-	use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
 
 	wpa_hexdump(MSG_DEBUG, "FT: Reassoc Req IEs", ies, ies_len);
 
-	if (wpa_ft_parse_ies(ies, ies_len, &parse, use_sha384) < 0) {
+	if (wpa_ft_parse_ies(ies, ies_len, &parse, sm->wpa_key_mgmt) < 0) {
 		wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs");
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 	}
@@ -3397,55 +3498,42 @@
 		return WLAN_STATUS_INVALID_MDIE;
 	}
 
-	if (use_sha384) {
-		struct rsn_ftie_sha384 *ftie;
+	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+	    sm->pmk_r1_len == SHA512_MAC_LEN)
+		mic_len = 32;
+	else if ((sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+		  sm->pmk_r1_len == SHA384_MAC_LEN) ||
+		 wpa_key_mgmt_sha384(sm->wpa_key_mgmt))
+		mic_len = 24;
+	else
+		mic_len = 16;
 
-		ftie = (struct rsn_ftie_sha384 *) parse.ftie;
-		if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
-			wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
-			return WLAN_STATUS_INVALID_FTIE;
-		}
-
-		anonce = ftie->anonce;
-		snonce = ftie->snonce;
-		rsnxe_used = ftie->mic_control[0] & 0x01;
-		fte_elem_count = ftie->mic_control[1];
-		fte_mic = ftie->mic;
-	} else {
-		struct rsn_ftie *ftie;
-
-		ftie = (struct rsn_ftie *) parse.ftie;
-		if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
-			wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
-			return WLAN_STATUS_INVALID_FTIE;
-		}
-
-		anonce = ftie->anonce;
-		snonce = ftie->snonce;
-		rsnxe_used = ftie->mic_control[0] & 0x01;
-		fte_elem_count = ftie->mic_control[1];
-		fte_mic = ftie->mic;
+	if (!parse.ftie || !parse.fte_anonce || !parse.fte_snonce ||
+	    parse.fte_mic_len != mic_len) {
+		wpa_printf(MSG_DEBUG,
+			   "FT: Invalid FTE (fte_mic_len=%zu mic_len=%zu)",
+			   parse.fte_mic_len, mic_len);
+		return WLAN_STATUS_INVALID_FTIE;
 	}
 
-	if (os_memcmp(snonce, sm->SNonce, WPA_NONCE_LEN) != 0) {
+	if (os_memcmp(parse.fte_snonce, sm->SNonce, WPA_NONCE_LEN) != 0) {
 		wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE");
 		wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
-			    snonce, WPA_NONCE_LEN);
+			    parse.fte_snonce, WPA_NONCE_LEN);
 		wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
 			    sm->SNonce, WPA_NONCE_LEN);
 		return WLAN_STATUS_INVALID_FTIE;
 	}
 
-	if (os_memcmp(anonce, sm->ANonce, WPA_NONCE_LEN) != 0) {
+	if (os_memcmp(parse.fte_anonce, sm->ANonce, WPA_NONCE_LEN) != 0) {
 		wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE");
 		wpa_hexdump(MSG_DEBUG, "FT: Received ANonce",
-			    anonce, WPA_NONCE_LEN);
+			    parse.fte_anonce, WPA_NONCE_LEN);
 		wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce",
 			    sm->ANonce, WPA_NONCE_LEN);
 		return WLAN_STATUS_INVALID_FTIE;
 	}
 
-
 	if (parse.r0kh_id == NULL) {
 		wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE");
 		return WLAN_STATUS_INVALID_FTIE;
@@ -3492,10 +3580,10 @@
 		count += ieee802_11_ie_count(parse.ric, parse.ric_len);
 	if (parse.rsnxe)
 		count++;
-	if (fte_elem_count != count) {
+	if (parse.fte_elem_count != count) {
 		wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC "
 			   "Control: received %u expected %u",
-			   fte_elem_count, count);
+			   parse.fte_elem_count, count);
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 	}
 
@@ -3506,7 +3594,8 @@
 		kck = sm->PTK.kck;
 		kck_len = sm->PTK.kck_len;
 	}
-	if (wpa_ft_mic(kck, kck_len, sm->addr, sm->wpa_auth->addr, 5,
+	if (wpa_ft_mic(sm->wpa_key_mgmt, kck, kck_len,
+		       sm->addr, sm->wpa_auth->addr, 5,
 		       parse.mdie - 2, parse.mdie_len + 2,
 		       parse.ftie - 2, parse.ftie_len + 2,
 		       parse.rsn - 2, parse.rsn_len + 2,
@@ -3518,12 +3607,12 @@
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 	}
 
-	if (os_memcmp_const(mic, fte_mic, mic_len) != 0) {
+	if (os_memcmp_const(mic, parse.fte_mic, mic_len) != 0) {
 		wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE");
 		wpa_printf(MSG_DEBUG, "FT: addr=" MACSTR " auth_addr=" MACSTR,
 			   MAC2STR(sm->addr), MAC2STR(sm->wpa_auth->addr));
 		wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC",
-			    fte_mic, mic_len);
+			    parse.fte_mic, mic_len);
 		wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, mic_len);
 		wpa_hexdump(MSG_MSGDUMP, "FT: MDIE",
 			    parse.mdie - 2, parse.mdie_len + 2);
@@ -3537,7 +3626,9 @@
 		return WLAN_STATUS_INVALID_FTIE;
 	}
 
-	if (rsnxe_used && (conf->sae_pwe == 1 || conf->sae_pwe == 2) &&
+	if (parse.fte_rsnxe_used &&
+	    (conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+	     conf->sae_pwe == SAE_PWE_BOTH) &&
 	    !parse.rsnxe) {
 		wpa_printf(MSG_INFO,
 			   "FT: FTE indicated that STA uses RSNXE, but RSNXE was not included");
@@ -4105,7 +4196,8 @@
 	pmk_r1_len = PMK_LEN;
 	if (wpa_ft_rrb_get_tlv(plain, plain_len, FT_RRB_PMK_R1, &f_pmk_r1_len,
 			       &f_pmk_r1) == 0 &&
-	    (f_pmk_r1_len == PMK_LEN || f_pmk_r1_len == SHA384_MAC_LEN))
+	    (f_pmk_r1_len == PMK_LEN || f_pmk_r1_len == SHA384_MAC_LEN ||
+	     f_pmk_r1_len == SHA512_MAC_LEN))
 		pmk_r1_len = f_pmk_r1_len;
 	RRB_GET(FT_RRB_PMK_R1, pmk_r1, msgtype, pmk_r1_len);
 	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f_pmk_r1, pmk_r1_len);
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index a510952..9f6699b 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -117,6 +117,7 @@
 #ifdef CONFIG_TESTING_OPTIONS
 	wconf->corrupt_gtk_rekey_mic_probability =
 		iconf->corrupt_gtk_rekey_mic_probability;
+	wconf->delay_eapol_tx = iconf->delay_eapol_tx;
 	if (conf->own_ie_override &&
 	    wpabuf_len(conf->own_ie_override) <= MAX_OWN_IE_OVERRIDE) {
 		wconf->own_ie_override_len = wpabuf_len(conf->own_ie_override);
@@ -198,10 +199,10 @@
 #endif /* CONFIG_FILS */
 	wconf->sae_pwe = conf->sae_pwe;
 	sae_pw_id = hostapd_sae_pw_id_in_use(conf);
-	if (sae_pw_id == 2 && wconf->sae_pwe != 3)
-		wconf->sae_pwe = 1;
-	else if (sae_pw_id == 1 && wconf->sae_pwe == 0)
-		wconf->sae_pwe = 2;
+	if (sae_pw_id == 2 && wconf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
+		wconf->sae_pwe = SAE_PWE_HASH_TO_ELEMENT;
+	else if (sae_pw_id == 1 && wconf->sae_pwe == SAE_PWE_HUNT_AND_PECK)
+		wconf->sae_pwe = SAE_PWE_BOTH;
 #ifdef CONFIG_SAE_PK
 	wconf->sae_pk = hostapd_sae_pk_in_use(conf);
 #endif /* CONFIG_SAE_PK */
@@ -937,7 +938,7 @@
 	struct hostapd_data *hapd = ctx;
 
 	ptksa_cache_add(hapd->ptksa, hapd->own_addr, addr, cipher, life_time,
-			ptk, NULL, NULL);
+			ptk, NULL, NULL, 0);
 }
 
 
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index 5bd699c..7ed3f2b 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -86,6 +86,7 @@
 	unsigned int pending_deinit:1;
 	unsigned int started:1;
 	unsigned int mgmt_frame_prot:1;
+	unsigned int mfpr:1;
 	unsigned int rx_eapol_key_secure:1;
 	unsigned int update_snonce:1;
 	unsigned int alt_snonce_valid:1;
@@ -298,7 +299,7 @@
 
 #ifdef CONFIG_IEEE80211R_AP
 int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len);
-int wpa_write_ftie(struct wpa_auth_config *conf, int use_sha384,
+int wpa_write_ftie(struct wpa_auth_config *conf, int key_mgmt, size_t key_len,
 		   const u8 *r0kh_id, size_t r0kh_id_len,
 		   const u8 *anonce, const u8 *snonce,
 		   u8 *buf, size_t len, const u8 *subelem,
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index 1c8affa..43ccec9 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -405,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 == SAE_PWE_HASH_TO_ELEMENT ||
+	     conf->sae_pwe == SAE_PWE_BOTH || conf->sae_pk ||
 	     wpa_key_mgmt_sae_ext_key(conf->wpa_key_mgmt))) {
 		capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
 #ifdef CONFIG_SAE_PK
@@ -419,7 +420,7 @@
 	if (conf->secure_rtt)
 		capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT);
 	if (conf->prot_range_neg)
-		capab |= BIT(WLAN_RSNX_CAPAB_PROT_RANGE_NEG);
+		capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR);
 
 	flen = (capab & 0xff00) ? 2 : 1;
 	if (!capab)
@@ -883,6 +884,7 @@
 		sm->mgmt_frame_prot = 0;
 	else
 		sm->mgmt_frame_prot = 1;
+	sm->mfpr = !!(data.capabilities & WPA_CAPABILITY_MFPR);
 
 	if (sm->mgmt_frame_prot && (ciphers & WPA_CIPHER_TKIP)) {
 		    wpa_printf(MSG_DEBUG,