Cumulative patch from commit 7bf86e44161fe1bcadaf4ca2cc3b6358fb7f3bf9

7bf86e441 Define test config QCA vendor attribute for HE MU EDCA params
6cc77193c Define test config QCA vendor attribute for Tx beamformee NSTS
73d3f8841 HS 2.0: CoA-Request from Terms and Conditions server
abed6136a RADIUS: Add DAC implementation in hostapd(AS)
72aad113c RADIUS: Allow 0.0.0.0 to be used as wildard radius_das_client
2122fc83a RADIUS server: Add current_sessions SQLite table
d4e39c51f HS 2.0: Move Terms and Conditions Server URL generation from AP to AS
de3885fcc HS 2.0: Process Credential/UsernamePassword/EAPMethod nodes in PPS MO
3a8dd390c SAE: Allow authentication restart on AP
a10f5714c HS 2.0: Allow Hotspot 2.0 version number to be overridden for build
e8cf9fc33 P2P: Use more compact debug print of common group frequencies
26eac0a99 P2P: Improve common group channel selection if GO needs to be moved
616178a2a P2P/AP: More detailed debug prints on HT/VHT parameter selection
c6f01fbf2 nl80211: Add more command/event names to debug prints
4a0e01156 AP: Fix HT 20/40 co-ex transition timer cancellation on iface removal
b915f2cdf nl80211: Handle NL80211_CMD_WIPHY_REG_CHANGE event
c3e4f40cd FT: Derive PMKR0Name/PMKR1Name using SHA-384 with AKM 00-0F-AC:13
e8d08cf37 SAE: Do not drop STA entry on reauthentication in infrastructure BSS
ef13b33af FT: Support BIP-CMAC-256, BIP-GMAC-128, BIP-GMAC-256 in STA case
ff168f8c2 FT: Support 256-bit IGTK in AP case
2cf36d608 FT: Handle AssocResp generation failures as fatal errors
657b4c45a FILS: Fix KEK2 use in FT-FILS use cases
88bf44be4 FT: Fix potential NULL pointer dereference in MDE addition
661afb2ed FT: Fix RRB error path handling
df3bf6870 FT: Debug print of IE parsing element details
d8e8c992c FT: Add key management value FT-EAP-SHA384 for wpa_supplicant
c6f2eceb8 FT: Add key management value FT-EAP-SHA384 for hostapd
3d9cd0841 FT: Add support for variable length PMK-R1 receiving in RRB
a7968ea56 FT: FTE generation for SHA384-based AKM on AP
06f128660 FT: FTE generation for SHA384-based AKM on STA
9a33737a0 FT: FTE parsing for SHA384-based AKM
8c2715b35 FT: Connection settings for SHA384-based AKM
994eac7e6 FT: PMK-R0 derivation using SHA384-based AKM
1655e81c9 FT: PMKID derivation using SHA384-based AKM
40a2eb116 FT: PTK derivation using SHA384-based AKM
7880a6a2b FT: PMK-R1 derivation using SHA384-based AKM
b327026a7 FT: FTE MIC calculation using SHA384-based AKM
c49a9d6b9 FT: EAPOL-Key MIC calculation using SHA384-based AKM
9f12271b2 FT: XXKey derivation for SHA384-based AKM
a3e18dbb6 FT: Support variable length keys
c22bb5bba FT: SHA384-based AKM in RSNE processing
3a11c69e0 EAP-TLS: Derive Session-Id using TLS-Exporter when TLS v1.3 is used
2d26434ac EAP-TLS server: Fix EMSK derivation with TLS v1.3
f8aed720e EAP-TLS: Update key derivation label per draft-ietf-emu-eap-tls13-00
20c2ea412 wpa_supplicant: Fix parsing of max_oper_chwidth
8fd29a043 mesh: Fix crash with CONFIG_TAXONOMY enabled
8518326b2 Add some missing driver flags strings
b375b04b6 WNM: Fix time_zone advertisement without time_zone configuration
0928b6294 nl80211: Allow mesh interface to send channel switch request
f5a602168 HS 2.0: Allow OSEN connection to be used in an RSN BSS
8d660a4ba HS 2.0: Allow OSEN connection to be enabled in an RSN BSS
0fe3ede0a macsec_qca: Fix byte order of TX SCI port
22ac3dfeb EAP-pwd: Mask timing of PWE derivation
b8acd5011 EAP-pwd peer: Add SHA512 hash based salt mechanism
fb3675848 EAP-pwd peer: Add SHA256 hash based salt mechanism
7ef8adc7d EAP-pwd peer: Add SHA-1 hash based salt mechanism
7280723fb EAP-pwd peer: Check for unexpected state for ID exchange
44136f6ca EAP-pwd peer: Add support for salted password databases
0744e303c EAP-pwd peer: Move PWE derivation to commit exchange
d52ead3db EAP-pwd server: Add support for salted password databases
a8712ce5b EAP-pwd: Pre-processing method definitions from RFC 8146
2a5c29188 EAP-pwd: Move EC group initialization to earlier step
b829e4b69 Add a QCA vendor command attribute to enable/disable GTX
833bb2ab1 FT: Disable PMKSA caching with FT
91db940ff fst: Fix compile error in fst_ctrl_aux.h with C++ compilers
9be19d0b9 SAE: Add support for using the optional Password Identifier
d6a65a83f mesh: Register msg_ctx for hostapd/AP code
4e47eec5a Add QCA NAN vendor attributes to provide IPv6 information
997312964 wolfSSL: Fix crypto_bignum_rshift() wrapper
4b2e03c42 wolfSSL: DH initialization to call TEST_FAIL() for error tests
2b01270c8 wolfSSL: Fix ECDH set peer to use the index when importing point
ffff7f709 OWE: Mark connection failed in the unlikely no-bss-entry case
0cc41ee63 Move wpa_supplicant_event() EVENT_ASSOC_REJECT handling into a function
bda9c0859 hostapd: Fix CHAN_SWITCH command for VHT20 and VHT40
16d5c9637 OWE: Get the bss from bssid of assoc_reject to try for next group
c4dd3afa3 ACS: Avoid invalid interference factor when survey channel time is zero
6965705bf Fix style issues in qca-vendor.h
7d66e7c40 Define new QCA feature flag for self managed regulatory support
38f60142d Define test config vendor attribute for Tx beamformee configuration
d109aa6ca SAE: Flush PMKSA if an assoc reject without timeout is received
46053a4c9 wext: Avoid gcc warnings on restricted pointer aliasing
ce2a9644b Silence a gcc warning on switch statement fallthrough
d267bdf9b Silence new gcc warnings on switch statement fallthroughs
60a5737e8 FT: Clear SME FT data on disassoc
98b806b03 Add QCA vendor attribute for spectral hardware generation
d57e06170 wpa_supplicant: Make channel switch event available for non-AP builds
d7f038ded wpa_supplicant: Add ieee80211ac information in STATUS
ab35793ec wolfSSL: Fix EAP-FAST key derivation
71faf06cb wolfSSL: Do not free cert store after setting it
b7f5b0ec6 wolfSSL: Fix OCSP ifdefs
ca620a364 wolfSSL: Fix altSubjectName handling
7be462084 wolfSSL: Use defines from wolfssl/options.h
312212174 wolfSSL: Remove aes-omac1.o from hostapd build
385dd7189 wolfSSL: Use wolfSSL memory allocation in dh5_init()
6590d8466 wolfSSL: Load certificates using 'chain' APIs
fc5e88e3e wolfSSL: Changes for memory allocation failure testing
06657d316 wolfSSL: Fix crypto_hash_init() memory clearing
d39605710 wolfSSL: Fix crypto_ec_point_y_sqr()
e3501ac18 wolfSSL: Fix crypto_ec_point_solve_y_coord()
187ad3a30 wolfSSL: Add crypto_ecdh_*()
3d2f638d6 wolfSSL: Use new digest namespace
847665ebe wolfSSL: Fix conditional EAP-FAST compilation issue
d501c27cf EAP-TLS server: Disable TLS v1.3 by default
e8a7af9a3 EAP-TLS peer: Disable TLS v1.3 by default
bbbc7e801 EAP-TLS: Extend TLS version config to allow TLS v1.3 to be disabled
53b34578f OpenSSL: Move server vs. client information into connection data
0de820b33 EAP-TLS peer: MSK/EMSK derivation with TLS v1.3
1854981c7 EAP-TLS peer: Allow NewSessionTicket after Client Finished with TLS v1.3
6dd98483e EAP-TLS peer: Support fragmentation of last message
a80423b52 EAP-TLS server: MSK/EMSK derivation with TLS v1.3
bac1bdba3 EAP-TLS peer: Determine whether TLS v1.3 or newer is used
fe7b06c5e EAP-TLS server: Determine whether TLS v1.3 or newer is used
c26ac1895 Mark eap_server_tls_derive_key() label argument const
34c6949fb EAP-TLS server: Remove trailing whitespace
4a576c473 OpenSSL: Terminate TLS handshake if ClientHello cannot be generated
2439714f9 DPP: Fix testing code for invalid keys with OpenSSL 1.1.1
c456e6e3f HS 2.0: Terms and Conditions server and management
42f416916 HS 2.0: Update server SQL DB initialization to cover new fields
5bd5eb54d HS 2.0: Update server instructions for Ubuntu 16.04
04ee197f8 HS 2.0: Maintain a database of pending T&C acceptance sessions
625a8c86b wpa_cli: Indicate HS20-T-C-ACCEPTANCE to action scripts
6b21df0bb Add QCA vendor command/attr to filter frames from other BSSs
452603809 HS 2.0: Terms and Conditions testing feature in authentication server
f456940ef HS 2.0: CoA-Request processing for Terms and Conditions filtering
d239ab396 DFS: Mark channels required DFS based on reg-domain info from the driver
7fc6a024f HS 2.0: Process received Terms and Conditions Acceptance notification
8760b9848 HS 2.0: Send Terms and Conditions Acceptance notification
6cb8f4f38 HS 2.0: Terms and Conditions attributes in Access-Request messages
1952b626b hostapd: Add ctrl iface indications for WDS STA interface
2598e6930 FILS: Enable SHA256 KDF even without PMF/SAE in the build
1baa130bd nl80211: Print NL80211_CMD_SET_POWER_SAVE errors in debug log
72b6e5d1e Do not remove CCMP group cipher if any CCMP/GCMP cipher is enabled
c4315e662 AP: Handle AP initalization failure in async flow
86c998d37 FT: Add FT auth algorithm to connect params when roaming
3dc3afe29 FT: Add MDE to assoc request IEs in connect params
b55c623e4 Make CENTER_FRQ1 available independently in SIGNAL_POLL
4204669c6 HS 2.0: Add Roaming Consortium Selection element into AssocReq
6311547e7 HS 2.0: Add Roaming Consortium Selection network profile parameter
5b7695275 HS 2.0: Use roaming_consortiums list to match OIs for access
2e88032f1 HS 2.0: OSU client to send HomeSP/RoamingConsortiumOI to wpa_supplicant
909a948b0 HS 2.0: Add a new cred block parameter roaming_consortiums
9b6f93e4d HS 2.0: Document credential parameter required_roaming_consortium
67cca3464 HS 2.0: Copy Roaming Consortium OI from (Re)AssocReq to Access-Request
0e450db28 HS 2.0: Allow configuration of operator icons
6a8a04d74 HS 2.0: Add fetching of Operator Icon Metadata ANQP-element
37547ad63 wpa_supplicant: Increase authentication timeout if CAC is started
2dd5fbbff wpa_supplicant: Rename wpas_event_*() to wpas_ap_event_*()
f875da044 nl80211: Add MLME auth work-around for -EEXIST errno
4449efeb1 Fix building nt_password_hash with gnutls
5ecdf06c8 DPP: Fix build with LibreSSL v2.5
4b603f01d DPP: Fix X509_ALGOR_get0() use with LibreSSL
dee566d98 OpenSSL: Skip SSL_OP_NO_TLSv1_3 if not defined to fix LibreSSL build
ba3658cff Fix build with LibreSSL 2.7
a2ab37308 Fix sae_password documentation in wpa_supplicant to refer correct field
d5906fbb7 mesh: Properly handle sae_password
d6d7debb5 Fix wpa_supplicant build with CONFIG_NO_WPA
852b2f273 SAE: Only allow SAE AKMP for PMKSA caching attempts
06b1a1043 SAE: Fix default PMK configuration for PMKSA caching case
8e402d165 WPA: Fix a typo in a debug message
1bd131105 Clear pmk_len more consistently for extra protection
66dbc8d9c Add more debug prints for wpa_sm_set_pmk() calls
26e0ada47 Define new test config attribute for HE LTF configuration
4d6eb9f2e Fix hostapd wmm_enabled setup on config reload path
a6509e850 FT: Add session_timeout to push/resp
3cb286ca4 FT: Add set/get session_timeout callback functions
13f118dc5 Convert STA session_timeout to os_reltime
069b4e304 FT: Include identity and radius_cui in pull/resp frames
9fbe292ff FT: Move wpa_ft_rrb_build_r0() special case to caller
8c345234f FT: Add set/get identity/radius_cui callback functions
17a8a9893 Fix potential memory leak with identity/radius_cui
17010c38d FT: Add IEEE VLAN support (including tagged VLANs)
47a039b01 FT: Add set_vlan()/get_vlan() callback functions
3a3e28320 FT: Add expiration to PMK-R0 and PMK-R1 cache
09211c989 FT: Use dl_list for PMK-R0/R1 cache
89dea17e1 FT: Add helper function for FILS key storing
83fe4bd3b FT: Convert r0_key_lifetime to seconds
ee2c6bb5e Remove CONFIG_IEEE80211R_AP=y build option from wpa_supplicant
c6b5b9a33 hostapd: Add more authentication error case debugging
e2fc13d0a hostapd: Add logging around Michael MIC related failures
e7525a295 hostapd: Add send_auth_reply() caller info in debug logging
d89edb611 wpa_supplicant: Don't reply to EAPOL if pkt_type is PACKET_OTHERHOST
8fb2b3573 Clean up setting of iface->p2p_mgmt flag
d53401c54 dbus: Redirect signal processing to the management device if present
e48021276 dbus: Add FILS to global capabilities
208263c01 Add config information related to MACsec
e360010c3 dbus: Add mesh to global capabilities
9f917339c tests: Fix clearing of IGTK PN in handshake retransmit test functions
45f7574d3 Propagate the EAP method error code
77a020a11 wpa_supplicant: Fix auth failure when the MAC is updated externally
2ff9696d3 Add definitions for RADIUS attributes standardised in RFC 7055
c17915462 nl80211: Add DFS offload support using upstream nl80211 definitions
1841086ad Sync with mac80211-next.git include/uapi/linux/nl80211.h
7cfe2f001 Add SAR V2 power selection capability (QCA vendor attributes)
7e1d3ee96 Add hostapd.conf venue_url to set Venue URL ANQP-element
64624f31c OWE: Fix CONFIG_OWE=y build without CONFIG_IEEE80211R=y
d555c39c4 FILS: Fix CONFIG_FILS=y build without CONFIG_IEEE80211R=y
f6de72534 Add NOTE control interface command for hostapd
2f3738781 FILS: Add more complete support for FT-FILS use cases
4ddca8142 FT: Derive PMK-R1 locally if requested PMKR0Name is found
1dc0945cc FT: Do not send PMK-R1 pull request to own R0KH address
1778f1e9a SAE: Fix PTK derivation to use KDF-SHA256
9d94e4bb6 SAE: Fix PMKID in EAPOL-Key msg 1/4
a03f9d17e SAE: Fix FT-SAE key derivation for a case where PMKID in msg 1/4 matches
0fa669bca Fix a resource leak on hostapd maclist parsing error path
10949e248 Do not disable 40 MHz based on co-ex report with matching primary channel
8cbd9c3ed Support multiple 20/40 BSS Intolerant Channel Report elements
8127a0ac0 Ignore intra-BSS 20/40 BSS Coexistence Management from not-associated STA
8aa599d45 Add more debug prints for 20/40 BSS Coexistence Management frame Rx
d58c3bd8b hostapd: Ignore LOW_ACK event for co-operative steering clients
ec2b5173c Make STA opmode change event available to upper layers
e8ada1600 nl80211: Add support for STA opmode change events
72123a84c hostapd: Add last_ack_rssi into ctrl iface cmd STA
2df73f52e Add hostapd_cli poll_sta command
f5701cc66 OWE: Clean up pointer check in a testing code path
ebe61e564 Sync with mac80211-next.git include/uapi/linux/nl80211.h
8179ae3a2 DPP: Support retrieving of configurator's private key
4bc801ab4 SAE: Fix EAPOL-Key integrity and key-wrap algorithm selection
d74963d41 DPP: Extend dpp_test 89 functionality to transmit side
f8bfc9cbd Use correct WPA_ALG_* values to compare for enum wpa_alg
cce16e43f mka: Mark ieee802_1x_kay_create_mka() ckn and cak arguments const
22151b111 wpa_supplicant: Fix memory leaks in ieee802_1x_create_preshared_mka()
3a52f6b38 mka: Do not print contents of SAK to debug log
77977b3d5 mka: Detect duplicate MAC addresses during key server election
5762855ab mka: Loss of live peers to result in connect PENDING not AUTHENTICATED
8fb546d8e mka: Ignore MACsec SAK Use Old Key parameter if we don't have our old key
b54b53e64 mka: When matching CKNs ensure that lengths are identical
fec03f983 Add support for wolfSSL cryptographic library
92eb00aec Extend ACL check for Probe Request frames
01542e651 Add new WiFi test config attributes to configure BA params
ba6080441 Add new WiFi test config attribute to allow WEP/TKIP in HE
c05f96a38 FILS: Check kde more consistently to avoid static analyzer warnings
087474512 SAE: Debug print group support in the crypto library
f5e0a3324 SAE: Fix potential infinite loop in mismatching PMK case on AP
427729ee6 Reject eap_server_erp hostapd.conf parameter without CONFIG_ERP=y
c54cc8bb1 BoringSSL: Set appropriate sigalgs for Suite B RSA 3K cases
aa6de8e6b BoringSSL: Map OpenSSL SUITEB192 cipher into appropriate sigalgs
7a47f34b1 BoringSSL: Map OpenSSL SUITEB192 cipher into appropriate parameters
355250234 OpenSSL: Replace SSL_set1_curves_list() with SSL_set1_curves()
4ab0f11b8 Allow HT40 on 5 GHz channels 165 and 169
299d21e8e nl80211: Use the new NL80211_MFP_OPTIONAL option
b8e88d357 wpa_supplicant: Handle port authorized event
a8c45d47d nl80211: Handle port authorized event
0a20bd7d9 driver: Add port authorized event
05fc7c68f nl80211: Add API to set the PMK to the driver
0ff08f963 nl80211: Check 4-way handshake offload support
730c5a1d0 nl80211: Support passing PSK on connect
14dcb22a5 wpa_passphrase: Include $(LIBS) for linking
4b07484c3 DPP: Do not include common/dpp.h without CONFIG_DPP=y
9ec0dfa31 Define host_to_le64() for Windows builds
6e3726c09 Fix a typo in disassoc_low_ack documentation
19e20c14f Add ap_isolate configuration option for wpa_supplicant AP mode
a4016163e Extend APF interface for read/write and enable/disable ops
b2e4074ca OpenSSL: Fix EAP-FAST with OpenSSL 1.1.1-pre1
a22e235fd OWE: Add testing RSNE for OWE assoc response with driver SME/MLME
aca4d84e3 DPP: Use wildcard BSSID in GAS query frames
0887215d9 nl80211: Do not try to add too large NL80211_ATTR_PMK for set/del PMKSA
e7f6e6ee1 nl80211: Print NL80211_CMD_{SET,DEL}_PMKSA failures in debug log
3988046de hostapd: Dynamic MAC ACL management over control interface
6a252ece2 DPP: Fix GAS query removal race condition on DPP_STOP_LISTEN
3b50f8a46 DPP: PKEX initiation on other bands
659ac96d7 ieee802_11_mgmt: Handle frame info more consistently
458d8984d SAE: Reject request with mismatching PMKID (no PMKSA cache entry)
5ac434642 Define new QCA vendor sub command for wifi test configuration
70e190137 DPP: Require use of PMF for DPP AKM
79ce2d519 OWE: Rename function to match use (driver-SME/MLME)
759da93a8 OWE: Check for ECDH availability before use (driver-SME/MLME)
af65ef28f OWE: Add RSNE when not using PMKSA caching (driver-SME/MLME)
5850cba38 OWE: Support DH groups 20 and 21 with driver-SME/MLME
f9854c183 hostapd: Fix wpa_psk_file support for FT-PSK
3bd35b681 wpa_supplicant: Fix parsing errors on additional config file
9c5fe742a wpa_supplicant: Free config only if it was allocated in same call
9f8d459d4 OWE: Fix association IEs for transition mode open AP connection
c6096c6ee wpa_cli: Enable add/remove/get vendor elements without P2P
d1e3d40d0 common: Fix the description of wpa_ctrl_request() function
82424732a Fix compiler issue with CONFIG_TESTING_OPTIONS
ebf404373 D-Bus: Report error on starting P2P find
66b9f3ca8 wpa_cli: Fix cred_fields[] declaration
5ff39c138 SAE: Support external authentication offload for driver-SME cases
40a68f338 nl80211: Create a netlink socket handle for the Connect interface
ba71cb821 nl80211: Introduce the interface for external authentication
8678b14fa Add new QCA vendor commands for thermal shutdown
99576f6f3 Sync with mac80211-next.git include/uapi/linux/nl80211.h
cd483be25 OWE: Use PMKSA caching if available with driver AP MLME
a4668c681 OWE: Handle unsupported finite cyclic group with driver MLME
f811cc83b atheros: Send correct SSID length to the driver
e005725a6 nl80211: Add DPP and OWE AKM selectors to CONNECT/ASSOC request
2cc2a0a7e Extend QCA vendor NDP params to support schedule update indication
a7769f6da Update QCA vendor commands to match ASOP
18e3e9c6e OWE: Transition mode with non-AP-MLME
92b6e0c58 hostapd: Send broadcast Public Action frame with wildcard BSSID address
fe3f81e6a DPP: Update PKEX role-specific points
b4cd8b7e1 Add QCA vendor command and attributes for MSDU queue depth threshold
703470bfa FILS: Fix extended capability bit setting for FILS in AP mode
9e834fc64 EAP-SIM/AKA: Separate identity for MK derivation
b6b5e3554 DPP: Get rid of compiler warnings on signed/unsigned comparison
630ea1334 DPP: Track M.x/N.x/L.x availability for ke derivation
c1790a5ff OWE: Allow station in transition mode to connect to an open BSS
c103c0b51 Extend NUD Stats to collect the data packet statistics
5d5ee699a Copy WLAN-Reason-Code value from Access-Reject to Deauthentication
f75ed556c RADIUS: Add WLAN-Reason-Code attribute to Access-Reject
727e9aacb HS 2.0: Set appropriate permission(s) for cert file/folders on Android
feba5848b Replace RSNE group key management mismatch status/reason codes
9cc8303d3 Add QCA vendor command to get the WLAN MAC information
fd9209549 DPP: Report reception of Config Request to upper layers
80f71b71d DPP: Authentication exchange retries and channel iteration in hostapd
c5cc7a59a Report offchannel RX frame frequency to hostapd
b7ed94834 GnuTLS: Add option to build with libnettle instead of libgcrypt
c36d82241 GnuTLS: Suite B validation
dd227e67a GnuTLS: Add support for disabling TLS versions
5d292fcfb GnuTLS: Implement tls_get_cipher()
5791d2117 GnuTLS: Make debug prints clearer for cert/key parsing
622d4d49e GnuTLS: Add TEST_FAIL() to crypto routines for testing purposes
f8697e810 GnuTLS: Implement tls_get_version()
133f8d09f GnuTLS: Implement HMAC functions using libgcrypt
85c12a62e GnuTLS: Implement sha{256,384,512}_vector() using libgcrypt
cc3e7bfc3 GnuTLS: Use a helper function for hash functions
2cb40e9f4 OWE: Try all supported DH groups automatically on STA
02b38d0ad Fix MFP-enabled test for disallowed TKIP
ba3d435fe SAE: Add option to require MFP for SAE associations
c4fc7e31c SAE: Set special Sc value when moving to Accepted state
abcbd0604 SAE: Add Rc variable and peer send-confirm validation
9249afc8e SAE: Print state changes in debug log
d8b841eba SAE: Make dot11RSNASAESync configurable
9596a7565 PAE: Remove OpenSSL header dependency
04b1bcc5f EAP-pwd: Use abstract crypto API
0c3d49afd EAP-EKE: Use abstract crypto API
c335507c8 Enhance crypto abstract API for DH
0e02f2a9f crypto: Implement new crypto API functions for DH
eac084cb3 OpenSSL: Implement new crypto API functions
23ff5d73d Enhance crypto abstract API
441e8bfbb Fix copy-paste errors in function comments
47e966aba DH: Remove trailing whitespace
3546ef510 EAP-pwd: Remove trailing empty line
61536b5f4 Add new QCA vendor attribute for getting preferred channel
1a2cb20d5 Fix couple of QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS comments
2fca2d214 Vendor parameter for forcing RSNE override
12bce14b1 wpa_cli: Add completion for get/set cred commands
6240424a7 dbus: Add FILS key mgmt values into BSS security properties
2d942ec4a Define a QCA vendor command to retrieve SAR Power limits
d98038bb0 FILS: Driver configuration to disable/enable FILS features
af832aa99 hostapd: Add average channel utilization in STATUS
802c0fd0c hostapd: Update BSS load update period dynamically
778d87054 Fix error handling in bss_load_update_period parser
dff5ab97e hostapd_cli: Add dpp_listen and dpp_stop_listen
5f30b69cd OWE: Allow DH Parameters element overriding with driver SME
04ded82ef OWE: Fix error case handling with drivers that implement AP SME
c23e87d0d OpenSSL: Replace EVP_PKEY_paramgen() with EC_KEY_new_by_curve_name()
7641d485d BoringSSL: Use EC_KEY_new_by_curve_name() to simplify implementation
a5da39607 Revert "BoringSSL: Add DPP special cases regardless of claimed version number"
0f3084472 Revert "BoringSSL: Define RSA_bits() helper"
a2c442be2 OpenSSL: Allow cipher list to be overridden for tls_suiteb=1 case
e5c7c2f28 Fix ap-mgmt-fuzzer build
63942cf0f OpenSSL: Avoid SSL*_use_default_passwd_cb()
149143e31 OpenSSL: Remove unnecessary os_strdup() from password callback
b65353a76 Android: Set CONFIG_NO_RANDOM_POOL=y
ff28df739 Android: Move hostapd to vendor partition
9b0de99fa Add new QCA vendor attribute for WLAN Latency Module (WLM)
055cd3978 tests: DPP P-256 test vectors
f55269753 DPP: Allow protocol key to be overridden for testing purposes
afd3cf14a WPS: Add GCMP-256 and CCMP-256 cipher options on Enrollee
5f481b6fd WPS: Check BSS table against current BSSID if credential does not match
402c8e00b WPS: Map GCMP-256 and CCMP-256 to AES encryption type
a2660890a WPS: Allow WPS to be enabled in CCMP-256 and GCMP-256 only cases
163fc3d56 DPP: Indicate to upper layers whether mutual authentication was used
34603767b DPP: Extend protocol testing to allow exchange termination
ed2c493e9 DPP: Change Authentication Response retry time to 1 second
963d3149a nl80211: Fix NL80211_MESHCONF_AUTO_OPEN_PLINKS encoding
a2426829c nl80211: Fix NL80211_ATTR_SMPS_MODE encoding
f0a383a98 DPP: Extend dpp_test with invalid E-Nonce in Config Req
55c6c8585 DPP: Extend dpp_test with invalid Transaction ID in Peer Disc Req
364039d22 Add QCA vendor command and attributes for RROP
0e19300d5 nl80211: Use consistent "0x" prefix for the cookie values
fa5c90749 DPP: Call wpas_dpp_stop() from wpas_dpp_deinit()
1866dfb52 DPP: Stop pending GAS client operation on DPP_STOP_LISTEN
f981ce729 DPP: Do not continue if public key hash derivation fails
8a37d54ea DPP: Fix memory leak on dpp_auth_build_conf() error paths
281f480c5 JSON: Fix a memory leak on an error path
622934128 DPP: Fix a memory leak on an error path
ed62d4011 DPP: Deinit PKEX instance on DPP_STOP_LISTEN
634a130a5 DPP: Clear authentication instance on configuration completion in AP
d3cb7ebe1 DPP: Do not process dpp_auth_ok_on_ack multiple times
dc2f24f1b DPP: Fix compilation without CONFIG_TESTING_OPTIONS=y
82feacced DPP: Ignore GAS server status callback for unknown response
a2588be82 DPP: Add DPP_CONFIGURATOR_SIGN support to hostapd
7eb6bfb45 DPP: Move hostapd Configurator/bootstrap data into global context
73f21929a DPP: Auto-generate Initiator bootstrapping info if needed
725a953ae nl80211: Filter global events based on wiphy
0bd7f104c hostapd: Add supported rate information into STATUS and STA
1489fcf87 FILS: Do not leave error value in left counter
beae255a7 WPA: Check wpa_eapol_key_mic() result on TX
acc555f9e DPP: Allow PKEX x/X and y/Y keypairs to be overridden
d722c50d0 DPP: Print more interim EC_POINT results into debug log
2bdc47a94 DPP: Allow PKEX own/peer MAC addresses to be overridden
af4103e5e DPP: Provide peer_mac to PKEX Initiator through function argument
918a2ac40 DPP: Work around missing EVP_PKEY_CTX_set_ec_param_enc()
3ca4be1ea DPP: Remove compiler warnings about signed/unsigned comparisons
116454f46 DPP: Fix error return value in dpp_auth_conf_rx()
63dc0f9c4 hostapd: Disassoc STA without WPA/RSN IE if AP proto is WPA/RSN
cc79e06f0 hostapd: Add wpa_msg_ctrl() to report Probe Request frames from STA
0db637ca8 DPP: Fix number of Authentication Request retry cases
921f5acd1 DPP: Take response wait time into account for init retries
248264c62 DPP: Stop Authentication Request attempts if no response after ACK
e3a5882b3 DPP: Add SAE credential support to Configurator
5dd745b73 DPP: Add akm=sae and akm=psk+sae support in Enrollee role
a44467395 DPP: Protocol testing capability to send invalid I-Nonce in Auth Req
00d2d13db DPP: Retry PKEX Exchange Request frame up to five times
ab0375508 TDLS: Add testing capability to send TPK M2 twice
4b8de0c92 DPP: Protocol testing for invalid Peer Discovery Req/Resp values
f9cf7d03f DPP: Protocol testing for invalid Config Attrib Object value
3f35ec2dc DPP: Protocol testing for invalid DPP Status value
9efa53149 DPP: Use helper functions to build Bootstrap Key Hash attributes
acdf703d5 DPP: Replace custom undefined attr with DPP Status in after-wrapped data
56f24d1da DPP: Use a helper function to build DPP Status attribute
65ecce87f DPP: Protocol testing for writing invalid I/R Bootstrap Key Hash
b6b4226bd DPP: Protocol testing capability to generate invalid Protocol Key
c6eb3e34b Stronger GTK derivation routine
94619905c DPP: Fix dpp_test_gen_invalid_key() with BoringSSL
746c1792a DPP: Build bootstrapping key DER encoding using custom routine
f2d27ef94 DPP: Use a helper function to DER encode bootstrapping key
c1564149a BoringSSL: Add AES support with 192-bit keys
edd72f55f OpenSSL: Debug message if requested AES key length is not supported
5548453a2 BoringSSL: Add DPP special cases regardless of claimed version number
f29761297 BoringSSL: Implement crypto_ecdh_init()
7721fe082 BoringSSL: Comment out SSL_set_default_passwd_cb*() calls
b9dc63c26 BoringSSL: Comment out SSL_set1_sigalgs_list() call
3cfbd3b0f BoringSSL: Define RSA_bits() helper
27781c0ab Allow group cipher selection to be overridden
af6614ca1 Fix block comment style in QCA vendor attribute definition
2115603a4 Add QCA_NL80211_VENDOR_SUBCMD_PEER_FLUSH_PENDING
27987b67f Add new QCA vendor attribute for LL stats
41db74cf7 atheros: Process SAE authentication frames using EVENT_RX_MGMT
3d9dd4b77 atheros: Generate EVENT_TX_STATUS events for management frames
f5b74b966 common: Avoid conflict with __bitwise macro from linux/types.h
4109555ef DPP: Fix compiler warning of testing code
1d624a070 Reject PMK-to-PTK derivation with unsupported cipher
762fb4f06 DPP: Testing capability to send unexpected Authentication Response
03abb6b54 DPP: Reject unexpected Req/Resp message based on Auth/PKEX role
95b0104a3 DPP: Retransmit DPP Authentication Response frame if it is not ACKed
c1d377396 DPP: Stop authentication exchange of DPP_STOP_LISTEN
d1f082644 DPP: Allowed initiator to indicate either role
f97ace34c DPP: Support multiple channels for initiating DPP Authentication
de0298618 DPP: Share a helper function for PKEX final steps
a306ed5a5 DPP: Protocol testing to allow missing attributes in peer discovery
1fafdf112 DPP: Add DPP_LISTEN and DPP_STOP_LISTEN to hostapd
fbfceef3a Add QCA vendor commands for spectral scan
bb9808fa9 P2P: Continue P2P_WAIT_PEER_(IDLE/CONNECT) sequence on a listen cancel
37ed3254d P2P: ACS offload for the autonomous GO
962b8fcf4 Add new QCA vendor attributes for MAC counters
af7f10fcd DPP: Protocol testing for invalid Config Resp attribute values
8c99e6264 DPP: Report Config Request/Response failure reasons on control interface
f411ad1b8 DPP: Protocol testing to remove attributes from Config Req/Resp
7e0ebe21b DPP: Protocol testing - invalid I/R-Auth value in PKEX Commit-Reveal
89d0bf678 DPP: Protocol testing - invalid Bootstrap Key value in PKEX Commit-Reveal
f31ef96dc DPP: Protocol testing - invalid Status value in PKEX Exchange Response
d05c82c4d DPP: Move PKEX z derivation on Responder to earlier phase
578c9ea1a DPP: Fix a typo in a debug print
5f5fff436 DPP: Explicitly check that PKEX Qr is not the point-at-infinity
29ab69e4b DPP: PKEX counter t
039b8e736 DPP: Terminate PKEX exchange on detection of a mismatching code
fc0efa2a1 DPP: Use dpp_bn2bin_pad() helper to simplify code
e0247e798 DPP: PKEX and STATUS_BAD_GROUP
2265353a4 DPP: Remove obsolete TODO comment on discovery object
fe12ae777 Fix Status Code in TKIP countermeasures case
1cfcbd32a DPP: Testing capability to generate invalid PKEX encrypted key (M and N)
d7e7b7122 DPP: Report PKEX failure reasons over control interface
61f9f27f8 DPP: Extend protocol testing to cover missing attributes in PKEX
b3e4cc5cb DPP: Move PKEX Commit-Reveal Response building to a helper function
b0626c2a6 DPP: Move PKEX Commit-Reveal Request building to a helper function
a5c3b41b2 DPP: Move PKEX Exchange Response building to a helper function
60b9dd86f DPP: Fix couple of typos in debug messages
06f2df069 DPP: Fix hostapd control interface events for initiator case
219d4c9fc DPP: Report possible PKEX code mismatch in control interface
69d8d029f DPP: Enable PMF when adding wpa_supplicant network profile
0c3bc1be0 Fix test build breakage when not compiling with ieee80211w support
ea4ace9c7 hostapd: Add max_txpower into STATUS command
bf6c65afc hostapd: Add Beacon interval and DTIM period into STATUS command
c7ae2b310 hostapd: Add HT/VHT capability info into STATUS command
1f91a8bde hostapd: Add HT/VHT capability info into STA command
65f9db6bc hostapd: Add extended capabilities into STA command
d1f3a8144 hostapd: Add [HT] flag into STA command
ba72b4b12 hostapd: Add Min/Max Transmit Power Capability into STA command
33c8bbd8c OWE: Add AP mode handling of OWE with drivers that implement SME
28d126413 Check hostapd current_mode before dereferencing it in additional places
348c93847 AP-side workaround for WNM-Sleep Mode GTK/IGTK reinstallation issues
3f5a1860a wpa_auth: Deplete group rekey eloop handler for strict rekeying
92662fb28 Allow forcing group rekeying for testing purposes
d27092069 DPP: Negotiation channel change request from Initiator
b7dddab7b DPP: Allow testing override values to be cleared
e85b66012 DPP: Add DPP Status attribute into Peer Discovery Response
19ef4289c DPP: Process Authentication Confirm failure cases
7d917ab04 DPP: Send Authentication Confirm failure reports
978bc3f2a DPP: Auth Resp/Conf incorrect attribute values for protocol testing
9b5111203 DPP: Allow Responder to decide not to use mutual authentication
dcdaeab79 DPP: Report Auth Conf failures in control interface
f9c7d7702 DPP: Omission of Auth Conf attributes for protocol testing
26806abe8 DPP: Report invalid messages and failure conditions in control interface
ce9acce00 DPP: Omission of Auth Resp attributes for protocol testing
a03406dbe DPP: Move Authentication Response building into a separate function
0e7cb8c6c DPP: Omission of Auth Req attributes for protocol testing
606a8e8d5 DPP: Move Authentication Request building into a separate function
3749ad0e1 DPP: Explicitly check and reject 0x00 and 0x03 I/R-capab role
af48810ba DPP: Report transmitted messages as control interface events
a70739349 DPP: Report received messages as control interface events
27fefbbb7 DPP: Remove unnecessary Wrapped Data checks from callers
0c881807b DPP: Verify that Wrapped Data attribute is the last one in the message
60239f60a DPP: Protocol testing framework
7ed5337d8 Fix a typo in a debug message
5f7c9e50f EAP server: Add event messages for more EAP states
a68e0d869 Fix a typo in a comment (the variable is ptk, not pkt)
de41b960b wpa_supplicant: Increase UDP control interface RX buffer
74e55b658 Fix test build breakage when not compiling with mesh support
3d0fb9558 WNM: Ignore BSS Transition Management frames in bss_transition=0 case
114f2830d WNM: Ignore WNM-Sleep Mode Request in wnm_sleep_mode=0 case

Bug: 111131936
Test: Device boots up and connects to wifi networks.
Test: Able to turn on/off softap.
Test: Sent for regression tests (b/111209415).
Change-Id: I85cbeeee6a4ff52a5e23f8917b15633c896c0cf3
Signed-off-by: Roshan Pius <rpius@google.com>
diff --git a/src/ap/Makefile b/src/ap/Makefile
index b8c167c..9b07ee1 100644
--- a/src/ap/Makefile
+++ b/src/ap/Makefile
@@ -10,6 +10,7 @@
 
 CFLAGS += -DHOSTAPD
 CFLAGS += -DNEED_AP_MLME
+CFLAGS += -DCONFIG_ETH_P_OUI
 CFLAGS += -DCONFIG_HS20
 CFLAGS += -DCONFIG_INTERWORKING
 CFLAGS += -DCONFIG_IEEE80211R
@@ -34,6 +35,7 @@
 	dhcp_snoop.o \
 	drv_callbacks.o \
 	eap_user_db.o \
+	eth_p_oui.o \
 	gas_serv.o \
 	hostapd.o \
 	hs20.o \
diff --git a/src/ap/acs.c b/src/ap/acs.c
index aa59058..6d4c041 100644
--- a/src/ap/acs.c
+++ b/src/ap/acs.c
@@ -314,7 +314,7 @@
 
 	/* TODO: figure out the best multiplier for noise floor base */
 	factor = pow(10, survey->nf / 5.0L) +
-		(busy / total) *
+		(total ? (busy / total) : 0) *
 		pow(2, pow(10, (long double) survey->nf / 10.0L) -
 		    pow(10, (long double) min_nf / 10.0L));
 
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 07310f9..820cba9 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -10,6 +10,7 @@
 
 #include "utils/common.h"
 #include "crypto/sha1.h"
+#include "crypto/tls.h"
 #include "radius/radius_client.h"
 #include "common/ieee802_11_defs.h"
 #include "common/eapol_common.h"
@@ -103,11 +104,13 @@
 	bss->rkh_neg_timeout = 60;
 	bss->rkh_pull_timeout = 1000;
 	bss->rkh_pull_retries = 4;
+	bss->r0_key_lifetime = 1209600;
 #endif /* CONFIG_IEEE80211R_AP */
 
 	bss->radius_das_time_window = 300;
 
 	bss->sae_anti_clogging_threshold = 5;
+	bss->sae_sync = 5;
 
 	bss->gas_frag_limit = 1400;
 
@@ -123,6 +126,11 @@
 #ifdef CONFIG_MBO
 	bss->mbo_cell_data_conn_pref = -1;
 #endif /* CONFIG_MBO */
+
+	/* Disable TLS v1.3 by default for now to avoid interoperability issue.
+	 * This can be enabled by default once the implementation has been fully
+	 * completed and tested with other implementations. */
+	bss->tls_flags = TLS_CONN_DISABLE_TLSv1_3;
 }
 
 
@@ -407,6 +415,7 @@
 	hostapd_config_free_radius_attr(user->accept_attr);
 	os_free(user->identity);
 	bin_clear_free(user->password, user->password_len);
+	bin_clear_free(user->salt, user->salt_len);
 	os_free(user);
 }
 
@@ -473,6 +482,22 @@
 }
 
 
+static void hostapd_config_free_sae_passwords(struct hostapd_bss_config *conf)
+{
+	struct sae_password_entry *pw, *tmp;
+
+	pw = conf->sae_passwords;
+	conf->sae_passwords = NULL;
+	while (pw) {
+		tmp = pw;
+		pw = pw->next;
+		str_clear_free(tmp->password);
+		os_free(tmp->identifier);
+		os_free(tmp);
+	}
+}
+
+
 void hostapd_config_free_bss(struct hostapd_bss_config *conf)
 {
 	if (conf == NULL)
@@ -576,6 +601,7 @@
 
 	os_free(conf->roaming_consortium);
 	os_free(conf->venue_name);
+	os_free(conf->venue_url);
 	os_free(conf->nai_realm_data);
 	os_free(conf->network_auth_type);
 	os_free(conf->anqp_3gpp_cell_net);
@@ -609,7 +635,16 @@
 		}
 		os_free(conf->hs20_osu_providers);
 	}
+	if (conf->hs20_operator_icon) {
+		size_t i;
+
+		for (i = 0; i < conf->hs20_operator_icon_count; i++)
+			os_free(conf->hs20_operator_icon[i]);
+		os_free(conf->hs20_operator_icon);
+	}
 	os_free(conf->subscr_remediation_url);
+	os_free(conf->t_c_filename);
+	os_free(conf->t_c_server_url);
 #endif /* CONFIG_HS20 */
 
 	wpabuf_free(conf->vendor_elements);
@@ -640,7 +675,7 @@
 	wpabuf_free(conf->dpp_csign);
 #endif /* CONFIG_DPP */
 
-	os_free(conf->sae_password);
+	hostapd_config_free_sae_passwords(conf);
 
 	os_free(conf);
 }
@@ -936,7 +971,9 @@
 
 	if (full_config && bss->wps_state && bss->wpa &&
 	    (!(bss->wpa & 2) ||
-	     !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)))) {
+	     !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP |
+				    WPA_CIPHER_CCMP_256 |
+				    WPA_CIPHER_GCMP_256)))) {
 		wpa_printf(MSG_INFO, "WPS: WPA/TKIP configuration without "
 			   "WPA2/CCMP/GCMP forced WPS to be disabled");
 		bss->wps_state = 0;
@@ -1046,8 +1083,12 @@
 
 	if ((bss->wpa & 2) && bss->rsn_pairwise == 0)
 		bss->rsn_pairwise = bss->wpa_pairwise;
-	bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa, bss->wpa_pairwise,
-						    bss->rsn_pairwise);
+	if (bss->group_cipher)
+		bss->wpa_group = bss->group_cipher;
+	else
+		bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa,
+							    bss->wpa_pairwise,
+							    bss->rsn_pairwise);
 	if (!bss->wpa_group_rekey_set)
 		bss->wpa_group_rekey = bss->wpa_group == WPA_CIPHER_TKIP ?
 			600 : 86400;
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 89bf289..5b71126 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -160,6 +160,8 @@
 	} methods[EAP_MAX_METHODS];
 	u8 *password;
 	size_t password_len;
+	u8 *salt;
+	size_t salt_len; /* non-zero when password is salted */
 	int phase2;
 	int force_version;
 	unsigned int wildcard_prefix:1;
@@ -169,6 +171,7 @@
 	unsigned int macacl:1;
 	int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */
 	struct hostapd_radius_attr *accept_attr;
+	u32 t_c_timestamp;
 };
 
 struct hostapd_radius_attr {
@@ -201,6 +204,12 @@
 	u8 name[252];
 };
 
+struct hostapd_venue_url {
+	u8 venue_number;
+	u8 url_len;
+	u8 url[254];
+};
+
 #define MAX_NAI_REALMS 10
 #define MAX_NAI_REALMLEN 255
 #define MAX_NAI_EAP_METHODS 5
@@ -230,6 +239,12 @@
 	char realm[];
 };
 
+struct sae_password_entry {
+	struct sae_password_entry *next;
+	char *password;
+	char *identifier;
+	u8 peer_addr[ETH_ALEN];
+};
 
 /**
  * struct hostapd_bss_config - Per-BSS configuration
@@ -248,7 +263,8 @@
 	int max_num_sta; /* maximum number of STAs in station table */
 
 	int dtim_period;
-	int bss_load_update_period;
+	unsigned int bss_load_update_period;
+	unsigned int chan_util_avg_period;
 
 	int ieee802_1x; /* use IEEE 802.1X */
 	int eapol_version;
@@ -325,6 +341,7 @@
 		PSK_RADIUS_REQUIRED = 2
 	} wpa_psk_radius;
 	int wpa_pairwise;
+	int group_cipher; /* wpa_group value override from configuation */
 	int wpa_group;
 	int wpa_group_rekey;
 	int wpa_group_rekey_set;
@@ -342,7 +359,7 @@
 	/* IEEE 802.11r - Fast BSS Transition */
 	u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
 	u8 r1_key_holder[FT_R1KH_ID_LEN];
-	u32 r0_key_lifetime;
+	u32 r0_key_lifetime; /* PMK-R0 lifetime seconds */
 	int rkh_pos_timeout;
 	int rkh_neg_timeout;
 	int rkh_pull_timeout; /* ms */
@@ -353,6 +370,7 @@
 	int pmk_r1_push;
 	int ft_over_ds;
 	int ft_psk_generate_local;
+	int r1_max_key_lifetime;
 #endif /* CONFIG_IEEE80211R_AP */
 
 	char *ctrl_interface; /* directory for UNIX domain sockets */
@@ -479,6 +497,7 @@
 	int time_advertisement;
 	char *time_zone;
 	int wnm_sleep_mode;
+	int wnm_sleep_mode_no_keys;
 	int bss_transition;
 
 	/* IEEE 802.11u - Interworking */
@@ -501,6 +520,10 @@
 	unsigned int venue_name_count;
 	struct hostapd_lang_string *venue_name;
 
+	/* Venue URL duples */
+	unsigned int venue_url_count;
+	struct hostapd_venue_url *venue_url;
+
 	/* IEEE 802.11u - Network Authentication Type */
 	u8 *network_auth_type;
 	size_t network_auth_type_len;
@@ -566,9 +589,14 @@
 		struct hostapd_lang_string *service_desc;
 	} *hs20_osu_providers, *last_osu;
 	size_t hs20_osu_providers_count;
+	char **hs20_operator_icon;
+	size_t hs20_operator_icon_count;
 	unsigned int hs20_deauth_req_timeout;
 	char *subscr_remediation_url;
 	u8 subscr_remediation_method;
+	char *t_c_filename;
+	u32 t_c_timestamp;
+	char *t_c_server_url;
 #endif /* CONFIG_HS20 */
 
 	u8 wps_rf_bands; /* RF bands for WPS (WPS_RF_*) */
@@ -581,8 +609,10 @@
 	struct wpabuf *assocresp_elements;
 
 	unsigned int sae_anti_clogging_threshold;
+	unsigned int sae_sync;
+	int sae_require_mfp;
 	int *sae_groups;
-	char *sae_password;
+	struct sae_password_entry *sae_passwords;
 
 	char *wowlan_triggers; /* Wake-on-WLAN triggers */
 
@@ -796,6 +826,11 @@
 	struct he_phy_capabilities_info he_phy_capab;
 	struct he_operation he_op;
 #endif /* CONFIG_IEEE80211AX */
+
+	/* VHT enable/disable config from CHAN_SWITCH */
+#define CH_SWITCH_VHT_ENABLED BIT(0)
+#define CH_SWITCH_VHT_DISABLED BIT(1)
+	unsigned int ch_switch_vht_config;
 };
 
 
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index 8f4d839..728d7f0 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -185,6 +185,13 @@
 	}
 #endif /* CONFIG_MBO */
 
+#ifdef CONFIG_OWE
+	pos = hostapd_eid_owe_trans(hapd, buf, sizeof(buf));
+	if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
+	    add_buf_data(&proberesp, buf, pos - buf) < 0)
+		goto fail;
+#endif /* CONFIG_OWE */
+
 	add_buf(&beacon, hapd->conf->vendor_elements);
 	add_buf(&proberesp, hapd->conf->vendor_elements);
 	add_buf(&assocresp, hapd->conf->assocresp_elements);
@@ -736,6 +743,15 @@
 		sta = ap_get_sta(hapd, dst);
 		if (!sta || !(sta->flags & WLAN_STA_ASSOC))
 			bssid = wildcard_bssid;
+	} else if (is_broadcast_ether_addr(dst) &&
+		   len > 0 && data[0] == WLAN_ACTION_PUBLIC) {
+		/*
+		 * The only current use case of Public Action frames with
+		 * broadcast destination address is DPP PKEX. That case is
+		 * directing all devices and not just the STAs within the BSS,
+		 * so have to use the wildcard BSSID value.
+		 */
+		bssid = wildcard_bssid;
 	}
 	return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst,
 					 hapd->own_addr, bssid, data, len, 0);
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index bf8169d..db93fde 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -103,6 +103,14 @@
 				     unsigned int freq,
 				     unsigned int wait, const u8 *dst,
 				     const u8 *data, size_t len);
+static inline void
+hostapd_drv_send_action_cancel_wait(struct hostapd_data *hapd)
+{
+	if (!hapd->driver || !hapd->driver->send_action_cancel_wait ||
+	    !hapd->drv_priv)
+		return;
+	hapd->driver->send_action_cancel_wait(hapd->drv_priv);
+}
 int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr,
 			 u16 auth_alg);
 int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr,
diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c
index a20f49f..66d255c 100644
--- a/src/ap/authsrv.c
+++ b/src/ap/authsrv.c
@@ -77,12 +77,20 @@
 			goto out;
 		user->password_len = eap_user->password_len;
 		user->password_hash = eap_user->password_hash;
+		if (eap_user->salt && eap_user->salt_len) {
+			user->salt = os_memdup(eap_user->salt,
+					       eap_user->salt_len);
+			if (!user->salt)
+				goto out;
+			user->salt_len = eap_user->salt_len;
+		}
 	}
 	user->force_version = eap_user->force_version;
 	user->macacl = eap_user->macacl;
 	user->ttls_auth = eap_user->ttls_auth;
 	user->remediation = eap_user->remediation;
 	user->accept_attr = eap_user->accept_attr;
+	user->t_c_timestamp = eap_user->t_c_timestamp;
 	rv = 0;
 
 out:
@@ -128,6 +136,7 @@
 #ifdef CONFIG_HS20
 	srv.subscr_remediation_url = conf->subscr_remediation_url;
 	srv.subscr_remediation_method = conf->subscr_remediation_method;
+	srv.t_c_server_url = conf->t_c_server_url;
 #endif /* CONFIG_HS20 */
 	srv.erp = conf->eap_server_erp;
 	srv.erp_domain = conf->erp_domain;
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 3ea28a7..7d079d2 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -16,6 +16,7 @@
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
 #include "common/hw_features_common.h"
+#include "common/wpa_ctrl.h"
 #include "wps/wps_defs.h"
 #include "p2p/p2p.h"
 #include "hostapd.h"
@@ -30,6 +31,7 @@
 #include "hs20.h"
 #include "dfs.h"
 #include "taxonomy.h"
+#include "ieee802_11_auth.h"
 
 
 #ifdef NEED_AP_MLME
@@ -363,67 +365,6 @@
 }
 
 
-#ifdef CONFIG_OWE
-static int hostapd_eid_owe_trans_enabled(struct hostapd_data *hapd)
-{
-	return hapd->conf->owe_transition_ssid_len > 0 &&
-		!is_zero_ether_addr(hapd->conf->owe_transition_bssid);
-}
-#endif /* CONFIG_OWE */
-
-
-static size_t hostapd_eid_owe_trans_len(struct hostapd_data *hapd)
-{
-#ifdef CONFIG_OWE
-	if (!hostapd_eid_owe_trans_enabled(hapd))
-		return 0;
-	return 6 + ETH_ALEN + 1 + hapd->conf->owe_transition_ssid_len;
-#else /* CONFIG_OWE */
-	return 0;
-#endif /* CONFIG_OWE */
-}
-
-
-static u8 * hostapd_eid_owe_trans(struct hostapd_data *hapd, u8 *eid,
-				  size_t len)
-{
-#ifdef CONFIG_OWE
-	u8 *pos = eid;
-	size_t elen;
-
-	if (hapd->conf->owe_transition_ifname[0] &&
-	    !hostapd_eid_owe_trans_enabled(hapd))
-		hostapd_owe_trans_get_info(hapd);
-
-	if (!hostapd_eid_owe_trans_enabled(hapd))
-		return pos;
-
-	elen = hostapd_eid_owe_trans_len(hapd);
-	if (len < elen) {
-		wpa_printf(MSG_DEBUG,
-			   "OWE: Not enough room in the buffer for OWE IE");
-		return pos;
-	}
-
-	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
-	*pos++ = elen - 2;
-	WPA_PUT_BE24(pos, OUI_WFA);
-	pos += 3;
-	*pos++ = OWE_OUI_TYPE;
-	os_memcpy(pos, hapd->conf->owe_transition_bssid, ETH_ALEN);
-	pos += ETH_ALEN;
-	*pos++ = hapd->conf->owe_transition_ssid_len;
-	os_memcpy(pos, hapd->conf->owe_transition_ssid,
-		  hapd->conf->owe_transition_ssid_len);
-	pos += hapd->conf->owe_transition_ssid_len;
-
-	return pos;
-#else /* CONFIG_OWE */
-	return eid;
-#endif /* CONFIG_OWE */
-}
-
-
 static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
 				   const struct ieee80211_mgmt *req,
 				   int is_p2p, size_t *resp_len)
@@ -791,6 +732,11 @@
 	int ret;
 	u16 csa_offs[2];
 	size_t csa_offs_len;
+	u32 session_timeout, acct_interim_interval;
+	struct vlan_description vlan_id;
+	struct hostapd_sta_wpa_psk_short *psk = NULL;
+	char *identity = NULL;
+	char *radius_cui = NULL;
 
 	if (len < IEEE80211_HDRLEN)
 		return;
@@ -799,6 +745,17 @@
 		sta_track_add(hapd->iface, mgmt->sa, ssi_signal);
 	ie_len = len - IEEE80211_HDRLEN;
 
+	ret = ieee802_11_allowed_address(hapd, mgmt->sa, (const u8 *) mgmt, len,
+					 &session_timeout,
+					 &acct_interim_interval, &vlan_id,
+					 &psk, &identity, &radius_cui, 1);
+	if (ret == HOSTAPD_ACL_REJECT) {
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+			"Ignore Probe Request frame from " MACSTR
+			" due to ACL reject ", MAC2STR(mgmt->sa));
+		return;
+	}
+
 	for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++)
 		if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx,
 					    mgmt->sa, mgmt->da, mgmt->bssid,
@@ -993,6 +950,9 @@
 	}
 #endif /* CONFIG_TESTING_OPTIONS */
 
+	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);
 	if (resp == NULL)
diff --git a/src/ap/bss_load.c b/src/ap/bss_load.c
index fb63942..725d3cd 100644
--- a/src/ap/bss_load.c
+++ b/src/ap/bss_load.c
@@ -16,11 +16,35 @@
 #include "beacon.h"
 
 
+static int get_bss_load_update_timeout(struct hostapd_data *hapd,
+				       unsigned int *sec, unsigned int *usec)
+{
+	unsigned int update_period = hapd->conf->bss_load_update_period;
+	unsigned int beacon_int = hapd->iconf->beacon_int;
+	unsigned int update_timeout;
+
+	if (!update_period || !beacon_int) {
+		wpa_printf(MSG_ERROR,
+			   "BSS Load: Invalid BSS load update configuration (period=%u beacon_int=%u)",
+			   update_period, beacon_int);
+		return -1;
+	}
+
+	update_timeout = update_period * beacon_int;
+
+	*sec = ((update_timeout / 1000) * 1024) / 1000;
+	*usec = (update_timeout % 1000) * 1024;
+
+	return 0;
+}
+
+
 static void update_channel_utilization(void *eloop_data, void *user_data)
 {
 	struct hostapd_data *hapd = eloop_data;
 	unsigned int sec, usec;
 	int err;
+	struct hostapd_iface *iface = hapd->iface;
 
 	if (!(hapd->beacon_set_done && hapd->started))
 		return;
@@ -33,8 +57,24 @@
 
 	ieee802_11_set_beacon(hapd);
 
-	sec = ((hapd->bss_load_update_timeout / 1000) * 1024) / 1000;
-	usec = (hapd->bss_load_update_timeout % 1000) * 1024;
+	if (get_bss_load_update_timeout(hapd, &sec, &usec) < 0)
+		return;
+
+	if (hapd->conf->chan_util_avg_period) {
+		iface->chan_util_samples_sum += iface->channel_utilization;
+		iface->chan_util_num_sample_periods +=
+			hapd->conf->bss_load_update_period;
+		if (iface->chan_util_num_sample_periods >=
+		    hapd->conf->chan_util_avg_period) {
+			iface->chan_util_average =
+				iface->chan_util_samples_sum /
+				(iface->chan_util_num_sample_periods /
+				 hapd->conf->bss_load_update_period);
+			iface->chan_util_samples_sum = 0;
+			iface->chan_util_num_sample_periods = 0;
+		}
+	}
+
 	eloop_register_timeout(sec, usec, update_channel_utilization, hapd,
 			       NULL);
 }
@@ -42,17 +82,11 @@
 
 int bss_load_update_init(struct hostapd_data *hapd)
 {
-	struct hostapd_bss_config *conf = hapd->conf;
-	struct hostapd_config *iconf = hapd->iconf;
 	unsigned int sec, usec;
 
-	if (!conf->bss_load_update_period || !iconf->beacon_int)
+	if (get_bss_load_update_timeout(hapd, &sec, &usec) < 0)
 		return -1;
 
-	hapd->bss_load_update_timeout = conf->bss_load_update_period *
-					iconf->beacon_int;
-	sec = ((hapd->bss_load_update_timeout / 1000) * 1024) / 1000;
-	usec = (hapd->bss_load_update_timeout % 1000) * 1024;
 	eloop_register_timeout(sec, usec, update_channel_utilization, hapd,
 			       NULL);
 	return 0;
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index 1a2b4e5..21b813e 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -26,6 +26,30 @@
 #include "taxonomy.h"
 
 
+static size_t hostapd_write_ht_mcs_bitmask(char *buf, size_t buflen,
+					   size_t curr_len, const u8 *mcs_set)
+{
+	int ret;
+	size_t len = curr_len;
+
+	ret = os_snprintf(buf + len, buflen - len,
+			  "ht_mcs_bitmask=");
+	if (os_snprintf_error(buflen - len, ret))
+		return len;
+	len += ret;
+
+	/* 77 first bits (+ 3 reserved bits) */
+	len += wpa_snprintf_hex(buf + len, buflen - len, mcs_set, 10);
+
+	ret = os_snprintf(buf + len, buflen - len, "\n");
+	if (os_snprintf_error(buflen - len, ret))
+		return curr_len;
+	len += ret;
+
+	return len;
+}
+
+
 static int hostapd_get_sta_tx_rx(struct hostapd_data *hapd,
 				 struct sta_info *sta,
 				 char *buf, size_t buflen)
@@ -111,6 +135,31 @@
 	if (!os_snprintf_error(buflen - len, ret))
 		len += ret;
 
+	if ((sta->flags & WLAN_STA_VHT) && sta->vht_capabilities) {
+		ret = os_snprintf(buf + len, buflen - len,
+				  "rx_vht_mcs_map=%04x\n"
+				  "tx_vht_mcs_map=%04x\n",
+				  le_to_host16(sta->vht_capabilities->
+					       vht_supported_mcs_set.rx_map),
+				  le_to_host16(sta->vht_capabilities->
+					       vht_supported_mcs_set.tx_map));
+		if (!os_snprintf_error(buflen - len, ret))
+			len += ret;
+	}
+
+	if ((sta->flags & WLAN_STA_HT) && sta->ht_capabilities) {
+		len = hostapd_write_ht_mcs_bitmask(buf, buflen, len,
+						   sta->ht_capabilities->
+						   supported_mcs_set);
+	}
+
+	if (data.flags & STA_DRV_DATA_LAST_ACK_RSSI) {
+		ret = os_snprintf(buf + len, buflen - len,
+				  "last_ack_signal=%d\n", data.last_ack_rssi);
+		if (!os_snprintf_error(buflen - len, ret))
+			len += ret;
+	}
+
 	return len;
 }
 
@@ -245,6 +294,53 @@
 		len += os_snprintf(buf + len, buflen - len, "\n");
 	}
 
+	if (sta->power_capab) {
+		ret = os_snprintf(buf + len, buflen - len,
+				  "min_txpower=%d\n"
+				  "max_txpower=%d\n",
+				  sta->min_tx_power, sta->max_tx_power);
+		if (!os_snprintf_error(buflen - len, ret))
+			len += ret;
+	}
+
+#ifdef CONFIG_IEEE80211AC
+	if ((sta->flags & WLAN_STA_VHT) && sta->vht_capabilities) {
+		res = os_snprintf(buf + len, buflen - len,
+				  "vht_caps_info=0x%08x\n",
+				  le_to_host32(sta->vht_capabilities->
+					       vht_capabilities_info));
+		if (!os_snprintf_error(buflen - len, res))
+			len += res;
+	}
+#endif /* CONFIG_IEEE80211AC */
+
+#ifdef CONFIG_IEEE80211N
+	if ((sta->flags & WLAN_STA_HT) && sta->ht_capabilities) {
+		res = os_snprintf(buf + len, buflen - len,
+				  "ht_caps_info=0x%04x\n",
+				  le_to_host16(sta->ht_capabilities->
+					       ht_capabilities_info));
+		if (!os_snprintf_error(buflen - len, res))
+			len += res;
+	}
+#endif /* CONFIG_IEEE80211N */
+
+	if (sta->ext_capability &&
+	    buflen - len > (unsigned) (11 + 2 * sta->ext_capability[0])) {
+		len += os_snprintf(buf + len, buflen - len, "ext_capab=");
+		len += wpa_snprintf_hex(buf + len, buflen - len,
+					sta->ext_capability + 1,
+					sta->ext_capability[0]);
+		len += os_snprintf(buf + len, buflen - len, "\n");
+	}
+
+	if (sta->flags & WLAN_STA_WDS && sta->ifname_wds) {
+		ret = os_snprintf(buf + len, buflen - len,
+				  "wds_sta_ifname=%s\n", sta->ifname_wds);
+		if (!os_snprintf_error(buflen - len, ret))
+			len += ret;
+	}
+
 	return len;
 }
 
@@ -546,7 +642,8 @@
 			      size_t buflen)
 {
 	struct hostapd_iface *iface = hapd->iface;
-	int len = 0, ret;
+	struct hostapd_hw_modes *mode = iface->current_mode;
+	int len = 0, ret, j;
 	size_t i;
 
 	ret = os_snprintf(buf + len, buflen - len,
@@ -606,13 +703,17 @@
 			  "channel=%u\n"
 			  "secondary_channel=%d\n"
 			  "ieee80211n=%d\n"
-			  "ieee80211ac=%d\n",
+			  "ieee80211ac=%d\n"
+			  "beacon_int=%u\n"
+			  "dtim_period=%d\n",
 			  iface->conf->channel,
 			  iface->conf->ieee80211n && !hapd->conf->disable_11n ?
 			  iface->conf->secondary_channel : 0,
 			  iface->conf->ieee80211n && !hapd->conf->disable_11n,
 			  iface->conf->ieee80211ac &&
-			  !hapd->conf->disable_11ac);
+			  !hapd->conf->disable_11ac,
+			  iface->conf->beacon_int,
+			  hapd->conf->dtim_period);
 	if (os_snprintf_error(buflen - len, ret))
 		return len;
 	len += ret;
@@ -620,15 +721,76 @@
 		ret = os_snprintf(buf + len, buflen - len,
 				  "vht_oper_chwidth=%d\n"
 				  "vht_oper_centr_freq_seg0_idx=%d\n"
-				  "vht_oper_centr_freq_seg1_idx=%d\n",
+				  "vht_oper_centr_freq_seg1_idx=%d\n"
+				  "vht_caps_info=%08x\n",
 				  iface->conf->vht_oper_chwidth,
 				  iface->conf->vht_oper_centr_freq_seg0_idx,
-				  iface->conf->vht_oper_centr_freq_seg1_idx);
+				  iface->conf->vht_oper_centr_freq_seg1_idx,
+				  iface->conf->vht_capab);
 		if (os_snprintf_error(buflen - len, ret))
 			return len;
 		len += ret;
 	}
 
+	if (iface->conf->ieee80211ac && !hapd->conf->disable_11ac && mode) {
+		u16 rxmap = WPA_GET_LE16(&mode->vht_mcs_set[0]);
+		u16 txmap = WPA_GET_LE16(&mode->vht_mcs_set[4]);
+
+		ret = os_snprintf(buf + len, buflen - len,
+				  "rx_vht_mcs_map=%04x\n"
+				  "tx_vht_mcs_map=%04x\n",
+				  rxmap, txmap);
+		if (os_snprintf_error(buflen - len, ret))
+			return len;
+		len += ret;
+	}
+
+	if (iface->conf->ieee80211n && !hapd->conf->disable_11n) {
+		ret = os_snprintf(buf + len, buflen - len,
+				  "ht_caps_info=%04x\n",
+				  hapd->iconf->ht_capab);
+		if (os_snprintf_error(buflen - len, ret))
+			return len;
+		len += ret;
+	}
+
+	if (iface->conf->ieee80211n && !hapd->conf->disable_11n && mode) {
+		len = hostapd_write_ht_mcs_bitmask(buf, buflen, len,
+						   mode->mcs_set);
+	}
+
+	if (iface->current_rates && iface->num_rates) {
+		ret = os_snprintf(buf + len, buflen - len, "supported_rates=");
+		if (os_snprintf_error(buflen - len, ret))
+			return len;
+		len += ret;
+
+		for (j = 0; j < iface->num_rates; j++) {
+			ret = os_snprintf(buf + len, buflen - len, "%s%02x",
+					  j > 0 ? " " : "",
+					  iface->current_rates[j].rate / 5);
+			if (os_snprintf_error(buflen - len, ret))
+				return len;
+			len += ret;
+		}
+		ret = os_snprintf(buf + len, buflen - len, "\n");
+		if (os_snprintf_error(buflen - len, ret))
+			return len;
+		len += ret;
+	}
+
+	for (j = 0; mode && j < mode->num_channels; j++) {
+		if (mode->channels[j].freq == iface->freq) {
+			ret = os_snprintf(buf + len, buflen - len,
+					  "max_txpower=%u\n",
+					  mode->channels[j].max_tx_power);
+			if (os_snprintf_error(buflen - len, ret))
+				return len;
+			len += ret;
+			break;
+		}
+	}
+
 	for (i = 0; i < iface->num_bss; i++) {
 		struct hostapd_data *bss = iface->bss[i];
 		ret = os_snprintf(buf + len, buflen - len,
@@ -647,6 +809,15 @@
 		len += ret;
 	}
 
+	if (hapd->conf->chan_util_avg_period) {
+		ret = os_snprintf(buf + len, buflen - len,
+				  "chan_util_avg=%u\n",
+				  iface->chan_util_average);
+		if (os_snprintf_error(buflen - len, ret))
+			return len;
+		len += ret;
+	}
+
 	return len;
 }
 
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
index 5a0d781..993dd19 100644
--- a/src/ap/dfs.c
+++ b/src/ap/dfs.c
@@ -1111,7 +1111,8 @@
 		return 1;
 	}
 
-	if (ieee80211_is_dfs(iface->freq)) {
+	if (ieee80211_is_dfs(iface->freq, iface->hw_features,
+			     iface->num_hw_features)) {
 		wpa_printf(MSG_DEBUG, "%s: freq %d MHz requires DFS",
 			   __func__, iface->freq);
 		return 0;
diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c
index aae2910..4d2b71a 100644
--- a/src/ap/dpp_hostapd.c
+++ b/src/ap/dpp_hostapd.c
@@ -20,7 +20,10 @@
 #include "dpp_hostapd.h"
 
 
+static void hostapd_dpp_reply_wait_timeout(void *eloop_ctx, void *timeout_ctx);
 static void hostapd_dpp_auth_success(struct hostapd_data *hapd, int initiator);
+static void hostapd_dpp_init_timeout(void *eloop_ctx, void *timeout_ctx);
+static int hostapd_dpp_auth_init_next(struct hostapd_data *hapd);
 
 static const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
 
@@ -30,7 +33,7 @@
 {
 	struct dpp_configurator *conf;
 
-	dl_list_for_each(conf, &hapd->dpp_configurator,
+	dl_list_for_each(conf, &hapd->iface->interfaces->dpp_configurator,
 			 struct dpp_configurator, list) {
 		if (conf->id == id)
 			return conf;
@@ -44,8 +47,8 @@
 	struct dpp_bootstrap_info *bi;
 	unsigned int max_id = 0;
 
-	dl_list_for_each(bi, &hapd->dpp_bootstrap, struct dpp_bootstrap_info,
-			 list) {
+	dl_list_for_each(bi, &hapd->iface->interfaces->dpp_bootstrap,
+			 struct dpp_bootstrap_info, list) {
 		if (bi->id > max_id)
 			max_id = bi->id;
 	}
@@ -69,12 +72,16 @@
 		return -1;
 
 	bi->id = hapd_dpp_next_id(hapd);
-	dl_list_add(&hapd->dpp_bootstrap, &bi->list);
+	dl_list_add(&hapd->iface->interfaces->dpp_bootstrap, &bi->list);
 
 	if (auth && auth->response_pending &&
 	    dpp_notify_new_qr_code(auth, bi) == 1) {
 		wpa_printf(MSG_DEBUG,
 			   "DPP: Sending out pending authentication response");
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+			" freq=%u type=%d",
+			MAC2STR(auth->peer_mac_addr), auth->curr_freq,
+			DPP_PA_AUTHENTICATION_RESP);
 		hostapd_drv_send_action(hapd, auth->curr_freq, 0,
 					auth->peer_mac_addr,
 					wpabuf_head(hapd->dpp_auth->resp_msg),
@@ -175,7 +182,7 @@
 		    info ? "I:" : "", info ? info : "", info ? ";" : "",
 		    pk);
 	bi->id = hapd_dpp_next_id(hapd);
-	dl_list_add(&hapd->dpp_bootstrap, &bi->list);
+	dl_list_add(&hapd->iface->interfaces->dpp_bootstrap, &bi->list);
 	ret = bi->id;
 	bi = NULL;
 fail:
@@ -196,8 +203,8 @@
 {
 	struct dpp_bootstrap_info *bi;
 
-	dl_list_for_each(bi, &hapd->dpp_bootstrap, struct dpp_bootstrap_info,
-			 list) {
+	dl_list_for_each(bi, &hapd->iface->interfaces->dpp_bootstrap,
+			 struct dpp_bootstrap_info, list) {
 		if (bi->id == id)
 			return bi;
 	}
@@ -205,12 +212,12 @@
 }
 
 
-static int dpp_bootstrap_del(struct hostapd_data *hapd, unsigned int id)
+static int dpp_bootstrap_del(struct hapd_interfaces *ifaces, unsigned int id)
 {
 	struct dpp_bootstrap_info *bi, *tmp;
 	int found = 0;
 
-	dl_list_for_each_safe(bi, tmp, &hapd->dpp_bootstrap,
+	dl_list_for_each_safe(bi, tmp, &ifaces->dpp_bootstrap,
 			      struct dpp_bootstrap_info, list) {
 		if (id && bi->id != id)
 			continue;
@@ -237,7 +244,7 @@
 			return -1;
 	}
 
-	return dpp_bootstrap_del(hapd, id_val);
+	return dpp_bootstrap_del(hapd->iface->interfaces, id_val);
 }
 
 
@@ -274,11 +281,72 @@
 }
 
 
+static void hostapd_dpp_auth_resp_retry_timeout(void *eloop_ctx,
+						void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct dpp_authentication *auth = hapd->dpp_auth;
+
+	if (!auth || !auth->resp_msg)
+		return;
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Retry Authentication Response after timeout");
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+		" freq=%u type=%d",
+		MAC2STR(auth->peer_mac_addr), auth->curr_freq,
+		DPP_PA_AUTHENTICATION_RESP);
+	hostapd_drv_send_action(hapd, auth->curr_freq, 500, auth->peer_mac_addr,
+				wpabuf_head(auth->resp_msg),
+				wpabuf_len(auth->resp_msg));
+}
+
+
+static void hostapd_dpp_auth_resp_retry(struct hostapd_data *hapd)
+{
+	struct dpp_authentication *auth = hapd->dpp_auth;
+	unsigned int wait_time, max_tries;
+
+	if (!auth || !auth->resp_msg)
+		return;
+
+	if (hapd->dpp_resp_max_tries)
+		max_tries = hapd->dpp_resp_max_tries;
+	else
+		max_tries = 5;
+	auth->auth_resp_tries++;
+	if (auth->auth_resp_tries >= max_tries) {
+		wpa_printf(MSG_INFO,
+			   "DPP: No confirm received from initiator - stopping exchange");
+		hostapd_drv_send_action_cancel_wait(hapd);
+		dpp_auth_deinit(hapd->dpp_auth);
+		hapd->dpp_auth = NULL;
+		return;
+	}
+
+	if (hapd->dpp_resp_retry_time)
+		wait_time = hapd->dpp_resp_retry_time;
+	else
+		wait_time = 1000;
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Schedule retransmission of Authentication Response frame in %u ms",
+		wait_time);
+	eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
+	eloop_register_timeout(wait_time / 1000,
+			       (wait_time % 1000) * 1000,
+			       hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
+}
+
+
 void hostapd_dpp_tx_status(struct hostapd_data *hapd, const u8 *dst,
 			   const u8 *data, size_t data_len, int ok)
 {
+	struct dpp_authentication *auth = hapd->dpp_auth;
+
 	wpa_printf(MSG_DEBUG, "DPP: TX status: dst=" MACSTR " ok=%d",
 		   MAC2STR(dst), ok);
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX_STATUS "dst=" MACSTR
+		" result=%s", MAC2STR(dst), ok ? "SUCCESS" : "FAILED");
 
 	if (!hapd->dpp_auth) {
 		wpa_printf(MSG_DEBUG,
@@ -289,6 +357,12 @@
 	if (hapd->dpp_auth->remove_on_tx_status) {
 		wpa_printf(MSG_DEBUG,
 			   "DPP: Terminate authentication exchange due to an earlier error");
+		eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
+		eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout,
+				     hapd, NULL);
+		eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd,
+				     NULL);
+		hostapd_drv_send_action_cancel_wait(hapd);
 		dpp_auth_deinit(hapd->dpp_auth);
 		hapd->dpp_auth = NULL;
 		return;
@@ -296,6 +370,120 @@
 
 	if (hapd->dpp_auth_ok_on_ack)
 		hostapd_dpp_auth_success(hapd, 1);
+
+	if (!is_broadcast_ether_addr(dst) && !ok) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unicast DPP Action frame was not ACKed");
+		if (auth->waiting_auth_resp) {
+			/* In case of DPP Authentication Request frame, move to
+			 * the next channel immediately. */
+			hostapd_drv_send_action_cancel_wait(hapd);
+			hostapd_dpp_auth_init_next(hapd);
+			return;
+		}
+		if (auth->waiting_auth_conf) {
+			hostapd_dpp_auth_resp_retry(hapd);
+			return;
+		}
+	}
+
+	if (!is_broadcast_ether_addr(dst) && auth->waiting_auth_resp && ok) {
+		/* Allow timeout handling to stop iteration if no response is
+		 * received from a peer that has ACKed a request. */
+		auth->auth_req_ack = 1;
+	}
+
+	if (!hapd->dpp_auth_ok_on_ack && hapd->dpp_auth->neg_freq > 0 &&
+	    hapd->dpp_auth->curr_freq != hapd->dpp_auth->neg_freq) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Move from curr_freq %u MHz to neg_freq %u MHz for response",
+			   hapd->dpp_auth->curr_freq,
+			   hapd->dpp_auth->neg_freq);
+		hostapd_drv_send_action_cancel_wait(hapd);
+
+		if (hapd->dpp_auth->neg_freq !=
+		    (unsigned int) hapd->iface->freq && hapd->iface->freq > 0) {
+			/* TODO: Listen operation on non-operating channel */
+			wpa_printf(MSG_INFO,
+				   "DPP: Listen operation on non-operating channel (%d MHz) is not yet supported (operating channel: %d MHz)",
+				   hapd->dpp_auth->neg_freq, hapd->iface->freq);
+		}
+	}
+
+	if (hapd->dpp_auth_ok_on_ack)
+		hapd->dpp_auth_ok_on_ack = 0;
+}
+
+
+static void hostapd_dpp_reply_wait_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct dpp_authentication *auth = hapd->dpp_auth;
+	unsigned int freq;
+	struct os_reltime now, diff;
+	unsigned int wait_time, diff_ms;
+
+	if (!auth || !auth->waiting_auth_resp)
+		return;
+
+	wait_time = hapd->dpp_resp_wait_time ?
+		hapd->dpp_resp_wait_time : 2000;
+	os_get_reltime(&now);
+	os_reltime_sub(&now, &hapd->dpp_last_init, &diff);
+	diff_ms = diff.sec * 1000 + diff.usec / 1000;
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Reply wait timeout - wait_time=%u diff_ms=%u",
+		   wait_time, diff_ms);
+
+	if (auth->auth_req_ack && diff_ms >= wait_time) {
+		/* Peer ACK'ed Authentication Request frame, but did not reply
+		 * with Authentication Response frame within two seconds. */
+		wpa_printf(MSG_INFO,
+			   "DPP: No response received from responder - stopping initiation attempt");
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_INIT_FAILED);
+		hostapd_drv_send_action_cancel_wait(hapd);
+		hostapd_dpp_listen_stop(hapd);
+		dpp_auth_deinit(auth);
+		hapd->dpp_auth = NULL;
+		return;
+	}
+
+	if (diff_ms >= wait_time) {
+		/* Authentication Request frame was not ACK'ed and no reply
+		 * was receiving within two seconds. */
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Continue Initiator channel iteration");
+		hostapd_drv_send_action_cancel_wait(hapd);
+		hostapd_dpp_listen_stop(hapd);
+		hostapd_dpp_auth_init_next(hapd);
+		return;
+	}
+
+	/* Driver did not support 2000 ms long wait_time with TX command, so
+	 * schedule listen operation to continue waiting for the response.
+	 *
+	 * DPP listen operations continue until stopped, so simply schedule a
+	 * new call to this function at the point when the two second reply
+	 * wait has expired. */
+	wait_time -= diff_ms;
+
+	freq = auth->curr_freq;
+	if (auth->neg_freq > 0)
+		freq = auth->neg_freq;
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Continue reply wait on channel %u MHz for %u ms",
+		   freq, wait_time);
+	hapd->dpp_in_response_listen = 1;
+
+	if (freq != (unsigned int) hapd->iface->freq && hapd->iface->freq > 0) {
+		/* TODO: Listen operation on non-operating channel */
+		wpa_printf(MSG_INFO,
+			   "DPP: Listen operation on non-operating channel (%d MHz) is not yet supported (operating channel: %d MHz)",
+			   freq, hapd->iface->freq);
+	}
+
+	eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000,
+			       hostapd_dpp_reply_wait_timeout, hapd, NULL);
 }
 
 
@@ -371,8 +559,15 @@
 			goto fail;
 		os_memcpy(conf_sta->ssid, ssid, ssid_len);
 		conf_sta->ssid_len = ssid_len;
-		if (os_strstr(cmd, " conf=sta-psk")) {
-			conf_sta->dpp = 0;
+		if (os_strstr(cmd, " conf=sta-psk") ||
+		    os_strstr(cmd, " conf=sta-sae") ||
+		    os_strstr(cmd, " conf=sta-psk-sae")) {
+			if (os_strstr(cmd, " conf=sta-psk-sae"))
+				conf_sta->akm = DPP_AKM_PSK_SAE;
+			else if (os_strstr(cmd, " conf=sta-sae"))
+				conf_sta->akm = DPP_AKM_SAE;
+			else
+				conf_sta->akm = DPP_AKM_PSK;
 			if (psk_set) {
 				os_memcpy(conf_sta->psk, psk, PMK_LEN);
 			} else {
@@ -381,7 +576,7 @@
 					goto fail;
 			}
 		} else if (os_strstr(cmd, " conf=sta-dpp")) {
-			conf_sta->dpp = 1;
+			conf_sta->akm = DPP_AKM_DPP;
 		} else {
 			goto fail;
 		}
@@ -393,8 +588,15 @@
 			goto fail;
 		os_memcpy(conf_ap->ssid, ssid, ssid_len);
 		conf_ap->ssid_len = ssid_len;
-		if (os_strstr(cmd, " conf=ap-psk")) {
-			conf_ap->dpp = 0;
+		if (os_strstr(cmd, " conf=ap-psk") ||
+		    os_strstr(cmd, " conf=ap-sae") ||
+		    os_strstr(cmd, " conf=ap-psk-sae")) {
+			if (os_strstr(cmd, " conf=ap-psk-sae"))
+				conf_ap->akm = DPP_AKM_PSK_SAE;
+			else if (os_strstr(cmd, " conf=ap-sae"))
+				conf_ap->akm = DPP_AKM_SAE;
+			else
+				conf_ap->akm = DPP_AKM_PSK;
 			if (psk_set) {
 				os_memcpy(conf_ap->psk, psk, PMK_LEN);
 			} else {
@@ -403,7 +605,7 @@
 					goto fail;
 			}
 		} else if (os_strstr(cmd, " conf=ap-dpp")) {
-			conf_ap->dpp = 1;
+			conf_ap->akm = DPP_AKM_DPP;
 		} else {
 			goto fail;
 		}
@@ -446,14 +648,110 @@
 }
 
 
+static void hostapd_dpp_init_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+
+	if (!hapd->dpp_auth)
+		return;
+	wpa_printf(MSG_DEBUG, "DPP: Retry initiation after timeout");
+	hostapd_dpp_auth_init_next(hapd);
+}
+
+
+static int hostapd_dpp_auth_init_next(struct hostapd_data *hapd)
+{
+	struct dpp_authentication *auth = hapd->dpp_auth;
+	const u8 *dst;
+	unsigned int wait_time, max_wait_time, freq, max_tries, used;
+	struct os_reltime now, diff;
+
+	if (!auth)
+		return -1;
+
+	if (auth->freq_idx == 0)
+		os_get_reltime(&hapd->dpp_init_iter_start);
+
+	if (auth->freq_idx >= auth->num_freq) {
+		auth->num_freq_iters++;
+		if (hapd->dpp_init_max_tries)
+			max_tries = hapd->dpp_init_max_tries;
+		else
+			max_tries = 5;
+		if (auth->num_freq_iters >= max_tries || auth->auth_req_ack) {
+			wpa_printf(MSG_INFO,
+				   "DPP: No response received from responder - stopping initiation attempt");
+			wpa_msg(hapd->msg_ctx, MSG_INFO,
+				DPP_EVENT_AUTH_INIT_FAILED);
+			eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout,
+					     hapd, NULL);
+			hostapd_drv_send_action_cancel_wait(hapd);
+			dpp_auth_deinit(hapd->dpp_auth);
+			hapd->dpp_auth = NULL;
+			return -1;
+		}
+		auth->freq_idx = 0;
+		eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
+		if (hapd->dpp_init_retry_time)
+			wait_time = hapd->dpp_init_retry_time;
+		else
+			wait_time = 10000;
+		os_get_reltime(&now);
+		os_reltime_sub(&now, &hapd->dpp_init_iter_start, &diff);
+		used = diff.sec * 1000 + diff.usec / 1000;
+		if (used > wait_time)
+			wait_time = 0;
+		else
+			wait_time -= used;
+		wpa_printf(MSG_DEBUG, "DPP: Next init attempt in %u ms",
+			   wait_time);
+		eloop_register_timeout(wait_time / 1000,
+				       (wait_time % 1000) * 1000,
+				       hostapd_dpp_init_timeout, hapd,
+				       NULL);
+		return 0;
+	}
+	freq = auth->freq[auth->freq_idx++];
+	auth->curr_freq = freq;
+
+	if (is_zero_ether_addr(auth->peer_bi->mac_addr))
+		dst = broadcast;
+	else
+		dst = auth->peer_bi->mac_addr;
+	hapd->dpp_auth_ok_on_ack = 0;
+	eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
+	wait_time = 2000; /* TODO: hapd->max_remain_on_chan; */
+	max_wait_time = hapd->dpp_resp_wait_time ?
+		hapd->dpp_resp_wait_time : 2000;
+	if (wait_time > max_wait_time)
+		wait_time = max_wait_time;
+	wait_time += 10; /* give the driver some extra time to complete */
+	eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000,
+			       hostapd_dpp_reply_wait_timeout, hapd, NULL);
+	wait_time -= 10;
+	if (auth->neg_freq > 0 && freq != auth->neg_freq) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Initiate on %u MHz and move to neg_freq %u MHz for response",
+			   freq, auth->neg_freq);
+	}
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+		" freq=%u type=%d",
+		MAC2STR(dst), freq, DPP_PA_AUTHENTICATION_REQ);
+	auth->auth_req_ack = 0;
+	os_get_reltime(&hapd->dpp_last_init);
+	return hostapd_drv_send_action(hapd, freq, wait_time,
+				       dst,
+				       wpabuf_head(hapd->dpp_auth->req_msg),
+				       wpabuf_len(hapd->dpp_auth->req_msg));
+}
+
+
 int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd)
 {
 	const char *pos;
 	struct dpp_bootstrap_info *peer_bi, *own_bi = NULL;
-	const u8 *dst;
-	int res;
-	int configurator = 1;
-	struct dpp_configuration *conf_sta = NULL, *conf_ap = NULL;
+	u8 allowed_roles = DPP_CAPAB_CONFIGURATOR;
+	unsigned int neg_freq = 0;
 
 	pos = os_strstr(cmd, " peer=");
 	if (!pos)
@@ -488,77 +786,102 @@
 	if (pos) {
 		pos += 6;
 		if (os_strncmp(pos, "configurator", 12) == 0)
-			configurator = 1;
+			allowed_roles = DPP_CAPAB_CONFIGURATOR;
 		else if (os_strncmp(pos, "enrollee", 8) == 0)
-			configurator = 0;
+			allowed_roles = DPP_CAPAB_ENROLLEE;
+		else if (os_strncmp(pos, "either", 6) == 0)
+			allowed_roles = DPP_CAPAB_CONFIGURATOR |
+				DPP_CAPAB_ENROLLEE;
 		else
 			goto fail;
 	}
 
-	if (hapd->dpp_auth)
+	pos = os_strstr(cmd, " neg_freq=");
+	if (pos)
+		neg_freq = atoi(pos + 10);
+
+	if (hapd->dpp_auth) {
+		eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
+		eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout,
+				     hapd, NULL);
+		eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd,
+				     NULL);
+		hostapd_drv_send_action_cancel_wait(hapd);
 		dpp_auth_deinit(hapd->dpp_auth);
-	hapd->dpp_auth = dpp_auth_init(hapd, peer_bi, own_bi, configurator);
+	}
+
+	hapd->dpp_auth = dpp_auth_init(hapd->msg_ctx, peer_bi, own_bi,
+				       allowed_roles, neg_freq,
+				       hapd->iface->hw_features,
+				       hapd->iface->num_hw_features);
 	if (!hapd->dpp_auth)
 		goto fail;
 	hostapd_dpp_set_testing_options(hapd, hapd->dpp_auth);
 	hostapd_dpp_set_configurator(hapd, hapd->dpp_auth, cmd);
 
-	/* TODO: Support iteration over all frequencies and filtering of
-	 * frequencies based on locally enabled channels that allow initiation
-	 * of transmission. */
-	if (peer_bi->num_freq > 0)
-		hapd->dpp_auth->curr_freq = peer_bi->freq[0];
-	else
-		hapd->dpp_auth->curr_freq = 2412;
+	hapd->dpp_auth->neg_freq = neg_freq;
 
-	if (is_zero_ether_addr(peer_bi->mac_addr)) {
-		dst = broadcast;
-	} else {
-		dst = peer_bi->mac_addr;
+	if (!is_zero_ether_addr(peer_bi->mac_addr))
 		os_memcpy(hapd->dpp_auth->peer_mac_addr, peer_bi->mac_addr,
 			  ETH_ALEN);
-	}
-	hapd->dpp_auth_ok_on_ack = 0;
 
-	res = hostapd_drv_send_action(hapd, hapd->dpp_auth->curr_freq, 0,
-				      dst, wpabuf_head(hapd->dpp_auth->req_msg),
-				      wpabuf_len(hapd->dpp_auth->req_msg));
-
-	return res;
+	return hostapd_dpp_auth_init_next(hapd);
 fail:
-	dpp_configuration_free(conf_sta);
-	dpp_configuration_free(conf_ap);
 	return -1;
 }
 
 
+int hostapd_dpp_listen(struct hostapd_data *hapd, const char *cmd)
+{
+	int freq;
+
+	freq = atoi(cmd);
+	if (freq <= 0)
+		return -1;
+
+	if (os_strstr(cmd, " role=configurator"))
+		hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR;
+	else if (os_strstr(cmd, " role=enrollee"))
+		hapd->dpp_allowed_roles = DPP_CAPAB_ENROLLEE;
+	else
+		hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR |
+			DPP_CAPAB_ENROLLEE;
+	hapd->dpp_qr_mutual = os_strstr(cmd, " qr=mutual") != NULL;
+
+	if (freq != hapd->iface->freq && hapd->iface->freq > 0) {
+		/* TODO: Listen operation on non-operating channel */
+		wpa_printf(MSG_INFO,
+			   "DPP: Listen operation on non-operating channel (%d MHz) is not yet supported (operating channel: %d MHz)",
+			   freq, hapd->iface->freq);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void hostapd_dpp_listen_stop(struct hostapd_data *hapd)
+{
+	/* TODO: Stop listen operation on non-operating channel */
+}
+
+
 static void hostapd_dpp_rx_auth_req(struct hostapd_data *hapd, const u8 *src,
 				    const u8 *hdr, const u8 *buf, size_t len,
 				    unsigned int freq)
 {
-	const u8 *r_bootstrap, *i_bootstrap, *wrapped_data;
-	u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len;
+	const u8 *r_bootstrap, *i_bootstrap;
+	u16 r_bootstrap_len, i_bootstrap_len;
 	struct dpp_bootstrap_info *bi, *own_bi = NULL, *peer_bi = NULL;
 
 	wpa_printf(MSG_DEBUG, "DPP: Authentication Request from " MACSTR,
 		   MAC2STR(src));
 
-	wrapped_data = dpp_get_attr(buf, len, DPP_ATTR_WRAPPED_DATA,
-				    &wrapped_data_len);
-	if (!wrapped_data) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Missing required Wrapped data attribute");
-		return;
-	}
-	wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped data",
-		    wrapped_data, wrapped_data_len);
-
 	r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
 				   &r_bootstrap_len);
-	if (!r_bootstrap || r_bootstrap > wrapped_data ||
-	    r_bootstrap_len != SHA256_MAC_LEN) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Missing or invalid required Responder Bootstrapping Key Hash attribute");
+	if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+			"Missing or invalid required Responder Bootstrapping Key Hash attribute");
 		return;
 	}
 	wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
@@ -566,10 +889,9 @@
 
 	i_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
 				   &i_bootstrap_len);
-	if (!i_bootstrap || i_bootstrap > wrapped_data ||
-	    i_bootstrap_len != SHA256_MAC_LEN) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Missing or invalid required Initiator Bootstrapping Key Hash attribute");
+	if (!i_bootstrap || i_bootstrap_len != SHA256_MAC_LEN) {
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+			"Missing or invalid required Initiator Bootstrapping Key Hash attribute");
 		return;
 	}
 	wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Bootstrapping Key Hash",
@@ -577,8 +899,8 @@
 
 	/* Try to find own and peer bootstrapping key matches based on the
 	 * received hash values */
-	dl_list_for_each(bi, &hapd->dpp_bootstrap, struct dpp_bootstrap_info,
-			 list) {
+	dl_list_for_each(bi, &hapd->iface->interfaces->dpp_bootstrap,
+			 struct dpp_bootstrap_info, list) {
 		if (!own_bi && bi->own &&
 		    os_memcmp(bi->pubkey_hash, r_bootstrap,
 			      SHA256_MAC_LEN) == 0) {
@@ -600,22 +922,21 @@
 	}
 
 	if (!own_bi) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: No matching own bootstrapping key found - ignore message");
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+			"No matching own bootstrapping key found - ignore message");
 		return;
 	}
 
 	if (hapd->dpp_auth) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Already in DPP authentication exchange - ignore new one");
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+			"Already in DPP authentication exchange - ignore new one");
 		return;
 	}
 
 	hapd->dpp_auth_ok_on_ack = 0;
 	hapd->dpp_auth = dpp_auth_req_rx(hapd->msg_ctx, hapd->dpp_allowed_roles,
 					 hapd->dpp_qr_mutual,
-					 peer_bi, own_bi, freq, hdr, buf,
-					 wrapped_data, wrapped_data_len);
+					 peer_bi, own_bi, freq, hdr, buf, len);
 	if (!hapd->dpp_auth) {
 		wpa_printf(MSG_DEBUG, "DPP: No response generated");
 		return;
@@ -625,55 +946,22 @@
 				     hapd->dpp_configurator_params);
 	os_memcpy(hapd->dpp_auth->peer_mac_addr, src, ETH_ALEN);
 
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+		" freq=%u type=%d",
+		MAC2STR(src), hapd->dpp_auth->curr_freq,
+		DPP_PA_AUTHENTICATION_RESP);
 	hostapd_drv_send_action(hapd, hapd->dpp_auth->curr_freq, 0,
 				src, wpabuf_head(hapd->dpp_auth->resp_msg),
 				wpabuf_len(hapd->dpp_auth->resp_msg));
 }
 
 
-static void hostapd_dpp_gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token,
-				    enum gas_query_ap_result result,
-				    const struct wpabuf *adv_proto,
-				    const struct wpabuf *resp, u16 status_code)
+static void hostapd_dpp_handle_config_obj(struct hostapd_data *hapd,
+					  struct dpp_authentication *auth)
 {
-	struct hostapd_data *hapd = ctx;
-	const u8 *pos;
-	struct dpp_authentication *auth = hapd->dpp_auth;
-
-	if (!auth || !auth->auth_success) {
-		wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
-		return;
-	}
-	if (!resp || status_code != WLAN_STATUS_SUCCESS) {
-		wpa_printf(MSG_DEBUG, "DPP: GAS query did not succeed");
-		goto fail;
-	}
-
-	wpa_hexdump_buf(MSG_DEBUG, "DPP: Configuration Response adv_proto",
-			adv_proto);
-	wpa_hexdump_buf(MSG_DEBUG, "DPP: Configuration Response (GAS response)",
-			resp);
-
-	if (wpabuf_len(adv_proto) != 10 ||
-	    !(pos = wpabuf_head(adv_proto)) ||
-	    pos[0] != WLAN_EID_ADV_PROTO ||
-	    pos[1] != 8 ||
-	    pos[3] != WLAN_EID_VENDOR_SPECIFIC ||
-	    pos[4] != 5 ||
-	    WPA_GET_BE24(&pos[5]) != OUI_WFA ||
-	    pos[8] != 0x1a ||
-	    pos[9] != 1) {
-		wpa_printf(MSG_DEBUG,
-			   "DPP: Not a DPP Advertisement Protocol ID");
-		goto fail;
-	}
-
-	if (dpp_conf_resp_rx(auth, resp) < 0) {
-		wpa_printf(MSG_DEBUG, "DPP: Configuration attempt failed");
-		goto fail;
-	}
-
 	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_RECEIVED);
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_AKM "%s",
+		dpp_akm_str(auth->akm));
 	if (auth->ssid_len)
 		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_SSID "%s",
 			wpa_ssid_txt(auth->ssid, auth->ssid_len));
@@ -736,6 +1024,52 @@
 			os_free(hex);
 		}
 	}
+}
+
+
+static void hostapd_dpp_gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token,
+				    enum gas_query_ap_result result,
+				    const struct wpabuf *adv_proto,
+				    const struct wpabuf *resp, u16 status_code)
+{
+	struct hostapd_data *hapd = ctx;
+	const u8 *pos;
+	struct dpp_authentication *auth = hapd->dpp_auth;
+
+	if (!auth || !auth->auth_success) {
+		wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
+		return;
+	}
+	if (!resp || status_code != WLAN_STATUS_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "DPP: GAS query did not succeed");
+		goto fail;
+	}
+
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: Configuration Response adv_proto",
+			adv_proto);
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: Configuration Response (GAS response)",
+			resp);
+
+	if (wpabuf_len(adv_proto) != 10 ||
+	    !(pos = wpabuf_head(adv_proto)) ||
+	    pos[0] != WLAN_EID_ADV_PROTO ||
+	    pos[1] != 8 ||
+	    pos[3] != WLAN_EID_VENDOR_SPECIFIC ||
+	    pos[4] != 5 ||
+	    WPA_GET_BE24(&pos[5]) != OUI_WFA ||
+	    pos[8] != 0x1a ||
+	    pos[9] != 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Not a DPP Advertisement Protocol ID");
+		goto fail;
+	}
+
+	if (dpp_conf_resp_rx(auth, resp) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Configuration attempt failed");
+		goto fail;
+	}
+
+	hostapd_dpp_handle_config_obj(hapd, auth);
 	dpp_auth_deinit(hapd->dpp_auth);
 	hapd->dpp_auth = NULL;
 	return;
@@ -811,6 +1145,17 @@
 	wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded");
 	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_SUCCESS "init=%d",
 		initiator);
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
+		wpa_printf(MSG_INFO,
+			   "DPP: TESTING - stop at Authentication Confirm");
+		if (hapd->dpp_auth->configurator) {
+			/* Prevent GAS response */
+			hapd->dpp_auth->auth_success = 0;
+		}
+		return;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
 
 	if (!hapd->dpp_auth->configurator)
 		hostapd_dpp_start_gas_client(hapd);
@@ -818,7 +1163,8 @@
 
 
 static void hostapd_dpp_rx_auth_resp(struct hostapd_data *hapd, const u8 *src,
-				     const u8 *hdr, const u8 *buf, size_t len)
+				     const u8 *hdr, const u8 *buf, size_t len,
+				     unsigned int freq)
 {
 	struct dpp_authentication *auth = hapd->dpp_auth;
 	struct wpabuf *msg;
@@ -839,6 +1185,15 @@
 		return;
 	}
 
+	eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
+
+	if (auth->curr_freq != freq && auth->neg_freq == freq) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Responder accepted request for different negotiation channel");
+		auth->curr_freq = freq;
+	}
+
+	eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
 	msg = dpp_auth_resp_rx(auth, hdr, buf, len);
 	if (!msg) {
 		if (auth->auth_resp_status == DPP_STATUS_RESPONSE_PENDING) {
@@ -850,6 +1205,9 @@
 	}
 	os_memcpy(auth->peer_mac_addr, src, ETH_ALEN);
 
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+		" freq=%u type=%d", MAC2STR(src), auth->curr_freq,
+		DPP_PA_AUTHENTICATION_CONF);
 	hostapd_drv_send_action(hapd, auth->curr_freq, 0, src,
 				wpabuf_head(msg), wpabuf_len(msg));
 	wpabuf_free(msg);
@@ -886,6 +1244,98 @@
 }
 
 
+static void hostapd_dpp_send_peer_disc_resp(struct hostapd_data *hapd,
+					    const u8 *src, unsigned int freq,
+					    u8 trans_id,
+					    enum dpp_status_error status)
+{
+	struct wpabuf *msg;
+
+	msg = dpp_alloc_msg(DPP_PA_PEER_DISCOVERY_RESP,
+			    5 + 5 + 4 + os_strlen(hapd->conf->dpp_connector));
+	if (!msg)
+		return;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_NO_TRANSACTION_ID_PEER_DISC_RESP) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - no Transaction ID");
+		goto skip_trans_id;
+	}
+	if (dpp_test == DPP_TEST_INVALID_TRANSACTION_ID_PEER_DISC_RESP) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - invalid Transaction ID");
+		trans_id ^= 0x01;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	/* Transaction ID */
+	wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID);
+	wpabuf_put_le16(msg, 1);
+	wpabuf_put_u8(msg, trans_id);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_trans_id:
+	if (dpp_test == DPP_TEST_NO_STATUS_PEER_DISC_RESP) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
+		goto skip_status;
+	}
+	if (dpp_test == DPP_TEST_INVALID_STATUS_PEER_DISC_RESP) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
+		status = 254;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	/* DPP Status */
+	wpabuf_put_le16(msg, DPP_ATTR_STATUS);
+	wpabuf_put_le16(msg, 1);
+	wpabuf_put_u8(msg, status);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_status:
+	if (dpp_test == DPP_TEST_NO_CONNECTOR_PEER_DISC_RESP) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - no Connector");
+		goto skip_connector;
+	}
+	if (status == DPP_STATUS_OK &&
+	    dpp_test == DPP_TEST_INVALID_CONNECTOR_PEER_DISC_RESP) {
+		char *connector;
+
+		wpa_printf(MSG_INFO, "DPP: TESTING - invalid Connector");
+		connector = dpp_corrupt_connector_signature(
+			hapd->conf->dpp_connector);
+		if (!connector) {
+			wpabuf_free(msg);
+			return;
+		}
+		wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR);
+		wpabuf_put_le16(msg, os_strlen(connector));
+		wpabuf_put_str(msg, connector);
+		os_free(connector);
+		goto skip_connector;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	/* DPP Connector */
+	if (status == DPP_STATUS_OK) {
+		wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR);
+		wpabuf_put_le16(msg, os_strlen(hapd->conf->dpp_connector));
+		wpabuf_put_str(msg, hapd->conf->dpp_connector);
+	}
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_connector:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	wpa_printf(MSG_DEBUG, "DPP: Send Peer Discovery Response to " MACSTR
+		   " status=%d", MAC2STR(src), status);
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+		" freq=%u type=%d status=%d", MAC2STR(src), freq,
+		DPP_PA_PEER_DISCOVERY_RESP, status);
+	hostapd_drv_send_action(hapd, freq, 0, src,
+				wpabuf_head(msg), wpabuf_len(msg));
+	wpabuf_free(msg);
+}
+
+
 static void hostapd_dpp_rx_peer_disc_req(struct hostapd_data *hapd,
 					 const u8 *src,
 					 const u8 *buf, size_t len,
@@ -897,7 +1347,7 @@
 	struct dpp_introduction intro;
 	os_time_t expire;
 	int expiration;
-	struct wpabuf *msg;
+	enum dpp_status_error res;
 
 	wpa_printf(MSG_DEBUG, "DPP: Peer Discovery Request from " MACSTR,
 		   MAC2STR(src));
@@ -917,7 +1367,7 @@
 	os_get_time(&now);
 
 	if (hapd->conf->dpp_netaccesskey_expiry &&
-	    hapd->conf->dpp_netaccesskey_expiry < now.sec) {
+	    (os_time_t) hapd->conf->dpp_netaccesskey_expiry < now.sec) {
 		wpa_printf(MSG_INFO, "DPP: Own netAccessKey expired");
 		return;
 	}
@@ -937,18 +1387,28 @@
 		return;
 	}
 
-	if (dpp_peer_intro(&intro, hapd->conf->dpp_connector,
-			   wpabuf_head(hapd->conf->dpp_netaccesskey),
-			   wpabuf_len(hapd->conf->dpp_netaccesskey),
-			   wpabuf_head(hapd->conf->dpp_csign),
-			   wpabuf_len(hapd->conf->dpp_csign),
-			   connector, connector_len, &expire) < 0) {
+	res = dpp_peer_intro(&intro, hapd->conf->dpp_connector,
+			     wpabuf_head(hapd->conf->dpp_netaccesskey),
+			     wpabuf_len(hapd->conf->dpp_netaccesskey),
+			     wpabuf_head(hapd->conf->dpp_csign),
+			     wpabuf_len(hapd->conf->dpp_csign),
+			     connector, connector_len, &expire);
+	if (res == 255) {
 		wpa_printf(MSG_INFO,
-			   "DPP: Network Introduction protocol resulted in failure");
+			   "DPP: Network Introduction protocol resulted in internal failure (peer "
+			   MACSTR ")", MAC2STR(src));
+		return;
+	}
+	if (res != DPP_STATUS_OK) {
+		wpa_printf(MSG_INFO,
+			   "DPP: Network Introduction protocol resulted in failure (peer "
+			   MACSTR " status %d)", MAC2STR(src), res);
+		hostapd_dpp_send_peer_disc_resp(hapd, src, freq, trans_id[0],
+						res);
 		return;
 	}
 
-	if (!expire || hapd->conf->dpp_netaccesskey_expiry < expire)
+	if (!expire || (os_time_t) hapd->conf->dpp_netaccesskey_expiry < expire)
 		expire = hapd->conf->dpp_netaccesskey_expiry;
 	if (expire)
 		expiration = expire - now.sec;
@@ -962,26 +1422,8 @@
 		return;
 	}
 
-	msg = dpp_alloc_msg(DPP_PA_PEER_DISCOVERY_RESP,
-			    5 + 4 + os_strlen(hapd->conf->dpp_connector));
-	if (!msg)
-		return;
-
-	/* Transaction ID */
-	wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID);
-	wpabuf_put_le16(msg, 1);
-	wpabuf_put_u8(msg, trans_id[0]);
-
-	/* DPP Connector */
-	wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR);
-	wpabuf_put_le16(msg, os_strlen(hapd->conf->dpp_connector));
-	wpabuf_put_str(msg, hapd->conf->dpp_connector);
-
-	wpa_printf(MSG_DEBUG, "DPP: Send Peer Discovery Response to " MACSTR,
-		   MAC2STR(src));
-	hostapd_drv_send_action(hapd, freq, 0, src,
-				wpabuf_head(msg), wpabuf_len(msg));
-	wpabuf_free(msg);
+	hostapd_dpp_send_peer_disc_resp(hapd, src, freq, trans_id[0],
+					DPP_STATUS_OK);
 }
 
 
@@ -1011,7 +1453,8 @@
 		return;
 	}
 
-	hapd->dpp_pkex = dpp_pkex_rx_exchange_req(hapd->dpp_pkex_bi,
+	hapd->dpp_pkex = dpp_pkex_rx_exchange_req(hapd->msg_ctx,
+						  hapd->dpp_pkex_bi,
 						  hapd->own_addr, src,
 						  hapd->dpp_pkex_identifier,
 						  hapd->dpp_pkex_code,
@@ -1023,8 +1466,19 @@
 	}
 
 	msg = hapd->dpp_pkex->exchange_resp;
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+		" freq=%u type=%d", MAC2STR(src), freq,
+		DPP_PA_PKEX_EXCHANGE_RESP);
 	hostapd_drv_send_action(hapd, freq, 0, src,
 				wpabuf_head(msg), wpabuf_len(msg));
+	if (hapd->dpp_pkex->failed) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Terminate PKEX exchange due to an earlier error");
+		if (hapd->dpp_pkex->t > hapd->dpp_pkex->own_bi->pkex_t)
+			hapd->dpp_pkex->own_bi->pkex_t = hapd->dpp_pkex->t;
+		dpp_pkex_free(hapd->dpp_pkex);
+		hapd->dpp_pkex = NULL;
+	}
 }
 
 
@@ -1046,8 +1500,7 @@
 		return;
 	}
 
-	os_memcpy(hapd->dpp_pkex->peer_mac, src, ETH_ALEN);
-	msg = dpp_pkex_rx_exchange_resp(hapd->dpp_pkex, buf, len);
+	msg = dpp_pkex_rx_exchange_resp(hapd->dpp_pkex, src, buf, len);
 	if (!msg) {
 		wpa_printf(MSG_DEBUG, "DPP: Failed to process the response");
 		return;
@@ -1056,6 +1509,9 @@
 	wpa_printf(MSG_DEBUG, "DPP: Send PKEX Commit-Reveal Request to " MACSTR,
 		   MAC2STR(src));
 
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+		" freq=%u type=%d", MAC2STR(src), freq,
+		DPP_PA_PKEX_COMMIT_REVEAL_REQ);
 	hostapd_drv_send_action(hapd, freq, 0, src,
 				wpabuf_head(msg), wpabuf_len(msg));
 	wpabuf_free(msg);
@@ -1082,12 +1538,23 @@
 	msg = dpp_pkex_rx_commit_reveal_req(pkex, hdr, buf, len);
 	if (!msg) {
 		wpa_printf(MSG_DEBUG, "DPP: Failed to process the request");
+		if (hapd->dpp_pkex->failed) {
+			wpa_printf(MSG_DEBUG, "DPP: Terminate PKEX exchange");
+			if (hapd->dpp_pkex->t > hapd->dpp_pkex->own_bi->pkex_t)
+				hapd->dpp_pkex->own_bi->pkex_t =
+					hapd->dpp_pkex->t;
+			dpp_pkex_free(hapd->dpp_pkex);
+			hapd->dpp_pkex = NULL;
+		}
 		return;
 	}
 
 	wpa_printf(MSG_DEBUG, "DPP: Send PKEX Commit-Reveal Response to "
 		   MACSTR, MAC2STR(src));
 
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+		" freq=%u type=%d", MAC2STR(src), freq,
+		DPP_PA_PKEX_COMMIT_REVEAL_RESP);
 	hostapd_drv_send_action(hapd, freq, 0, src,
 				wpabuf_head(msg), wpabuf_len(msg));
 	wpabuf_free(msg);
@@ -1109,7 +1576,7 @@
 		dpp_bootstrap_info_free(bi);
 		return;
 	}
-	dl_list_add(&hapd->dpp_bootstrap, &bi->list);
+	dl_list_add(&hapd->iface->interfaces->dpp_bootstrap, &bi->list);
 }
 
 
@@ -1156,7 +1623,7 @@
 		dpp_bootstrap_info_free(bi);
 		return;
 	}
-	dl_list_add(&hapd->dpp_bootstrap, &bi->list);
+	dl_list_add(&hapd->iface->interfaces->dpp_bootstrap, &bi->list);
 
 	os_snprintf(cmd, sizeof(cmd), " peer=%u %s",
 		    bi->id,
@@ -1178,6 +1645,7 @@
 	u8 crypto_suite;
 	enum dpp_public_action_frame_type type;
 	const u8 *hdr;
+	unsigned int pkex_t;
 
 	if (len < DPP_HDR_LEN)
 		return;
@@ -1197,18 +1665,27 @@
 	if (crypto_suite != 1) {
 		wpa_printf(MSG_DEBUG, "DPP: Unsupported crypto suite %u",
 			   crypto_suite);
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_RX "src=" MACSTR
+			" freq=%u type=%d ignore=unsupported-crypto-suite",
+			MAC2STR(src), freq, type);
 		return;
 	}
 	wpa_hexdump(MSG_MSGDUMP, "DPP: Received message attributes", buf, len);
-	if (dpp_check_attrs(buf, len) < 0)
+	if (dpp_check_attrs(buf, len) < 0) {
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_RX "src=" MACSTR
+			" freq=%u type=%d ignore=invalid-attributes",
+			MAC2STR(src), freq, type);
 		return;
+	}
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_RX "src=" MACSTR
+		" freq=%u type=%d", MAC2STR(src), freq, type);
 
 	switch (type) {
 	case DPP_PA_AUTHENTICATION_REQ:
 		hostapd_dpp_rx_auth_req(hapd, src, hdr, buf, len, freq);
 		break;
 	case DPP_PA_AUTHENTICATION_RESP:
-		hostapd_dpp_rx_auth_resp(hapd, src, hdr, buf, len);
+		hostapd_dpp_rx_auth_resp(hapd, src, hdr, buf, len, freq);
 		break;
 	case DPP_PA_AUTHENTICATION_CONF:
 		hostapd_dpp_rx_auth_conf(hapd, src, hdr, buf, len);
@@ -1235,6 +1712,17 @@
 			   "DPP: Ignored unsupported frame subtype %d", type);
 		break;
 	}
+
+	if (hapd->dpp_pkex)
+		pkex_t = hapd->dpp_pkex->t;
+	else if (hapd->dpp_pkex_bi)
+		pkex_t = hapd->dpp_pkex_bi->pkex_t;
+	else
+		pkex_t = 0;
+	if (pkex_t >= PKEX_COUNTER_T_LIMIT) {
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_PKEX_T_LIMIT "id=0");
+		hostapd_dpp_pkex_remove(hapd, "*");
+	}
 }
 
 
@@ -1254,6 +1742,8 @@
 	wpa_hexdump(MSG_DEBUG,
 		    "DPP: Received Configuration Request (GAS Query Request)",
 		    query, query_len);
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_REQ_RX "src=" MACSTR,
+		MAC2STR(sa));
 	resp = dpp_conf_req_rx(auth, query, query_len);
 	if (!resp)
 		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
@@ -1261,12 +1751,30 @@
 }
 
 
+void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok)
+{
+	if (!hapd->dpp_auth)
+		return;
+
+	eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
+	eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
+	hostapd_drv_send_action_cancel_wait(hapd);
+
+	if (ok)
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
+	else
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
+	dpp_auth_deinit(hapd->dpp_auth);
+	hapd->dpp_auth = NULL;
+}
+
+
 static unsigned int hostapd_dpp_next_configurator_id(struct hostapd_data *hapd)
 {
 	struct dpp_configurator *conf;
 	unsigned int max_id = 0;
 
-	dl_list_for_each(conf, &hapd->dpp_configurator,
+	dl_list_for_each(conf, &hapd->iface->interfaces->dpp_configurator,
 			 struct dpp_configurator, list) {
 		if (conf->id > max_id)
 			max_id = conf->id;
@@ -1300,7 +1808,7 @@
 		goto fail;
 
 	conf->id = hostapd_dpp_next_configurator_id(hapd);
-	dl_list_add(&hapd->dpp_configurator, &conf->list);
+	dl_list_add(&hapd->iface->interfaces->dpp_configurator, &conf->list);
 	ret = conf->id;
 	conf = NULL;
 fail:
@@ -1312,12 +1820,12 @@
 }
 
 
-static int dpp_configurator_del(struct hostapd_data *hapd, unsigned int id)
+static int dpp_configurator_del(struct hapd_interfaces *ifaces, unsigned int id)
 {
 	struct dpp_configurator *conf, *tmp;
 	int found = 0;
 
-	dl_list_for_each_safe(conf, tmp, &hapd->dpp_configurator,
+	dl_list_for_each_safe(conf, tmp, &ifaces->dpp_configurator,
 			      struct dpp_configurator, list) {
 		if (id && conf->id != id)
 			continue;
@@ -1344,7 +1852,45 @@
 			return -1;
 	}
 
-	return dpp_configurator_del(hapd, id_val);
+	return dpp_configurator_del(hapd->iface->interfaces, id_val);
+}
+
+
+int hostapd_dpp_configurator_sign(struct hostapd_data *hapd, const char *cmd)
+{
+	struct dpp_authentication *auth;
+	int ret = -1;
+	char *curve = NULL;
+
+	auth = os_zalloc(sizeof(*auth));
+	if (!auth)
+		return -1;
+
+	curve = get_param(cmd, " curve=");
+	hostapd_dpp_set_configurator(hapd, auth, cmd);
+
+	if (dpp_configurator_own_config(auth, curve, 1) == 0) {
+		hostapd_dpp_handle_config_obj(hapd, auth);
+		ret = 0;
+	}
+
+	dpp_auth_deinit(auth);
+	os_free(curve);
+
+	return ret;
+}
+
+
+int hostapd_dpp_configurator_get_key(struct hostapd_data *hapd, unsigned int id,
+				     char *buf, size_t buflen)
+{
+	struct dpp_configurator *conf;
+
+	conf = hostapd_dpp_configurator_get_id(hapd, id);
+	if (!conf)
+		return -1;
+
+	return dpp_configurator_get_key(conf, buf, buflen);
 }
 
 
@@ -1369,6 +1915,7 @@
 		return -1;
 	}
 	hapd->dpp_pkex_bi = own_bi;
+	own_bi->pkex_t = 0; /* clear pending errors on new code */
 
 	os_free(hapd->dpp_pkex_identifier);
 	hapd->dpp_pkex_identifier = NULL;
@@ -1398,7 +1945,8 @@
 
 		wpa_printf(MSG_DEBUG, "DPP: Initiating PKEX");
 		dpp_pkex_free(hapd->dpp_pkex);
-		hapd->dpp_pkex = dpp_pkex_init(own_bi, hapd->own_addr,
+		hapd->dpp_pkex = dpp_pkex_init(hapd->msg_ctx, own_bi,
+					       hapd->own_addr,
 					       hapd->dpp_pkex_identifier,
 					       hapd->dpp_pkex_code);
 		if (!hapd->dpp_pkex)
@@ -1406,6 +1954,9 @@
 
 		msg = hapd->dpp_pkex->exchange_req;
 		/* TODO: Which channel to use? */
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+			" freq=%u type=%d", MAC2STR(broadcast), 2437,
+			DPP_PA_PKEX_EXCHANGE_REQ);
 		hostapd_drv_send_action(hapd, 2437, 0, broadcast,
 					wpabuf_head(msg), wpabuf_len(msg));
 	}
@@ -1449,10 +2000,17 @@
 }
 
 
+void hostapd_dpp_stop(struct hostapd_data *hapd)
+{
+	dpp_auth_deinit(hapd->dpp_auth);
+	hapd->dpp_auth = NULL;
+	dpp_pkex_free(hapd->dpp_pkex);
+	hapd->dpp_pkex = NULL;
+}
+
+
 int hostapd_dpp_init(struct hostapd_data *hapd)
 {
-	dl_list_init(&hapd->dpp_bootstrap);
-	dl_list_init(&hapd->dpp_configurator);
 	hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE;
 	hapd->dpp_init_done = 1;
 	return 0;
@@ -1472,8 +2030,9 @@
 #endif /* CONFIG_TESTING_OPTIONS */
 	if (!hapd->dpp_init_done)
 		return;
-	dpp_bootstrap_del(hapd, 0);
-	dpp_configurator_del(hapd, 0);
+	eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
+	eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
+	eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
 	dpp_auth_deinit(hapd->dpp_auth);
 	hapd->dpp_auth = NULL;
 	hostapd_dpp_pkex_remove(hapd, "*");
@@ -1481,3 +2040,20 @@
 	os_free(hapd->dpp_configurator_params);
 	hapd->dpp_configurator_params = NULL;
 }
+
+
+void hostapd_dpp_init_global(struct hapd_interfaces *ifaces)
+{
+	dl_list_init(&ifaces->dpp_bootstrap);
+	dl_list_init(&ifaces->dpp_configurator);
+	ifaces->dpp_init_done = 1;
+}
+
+
+void hostapd_dpp_deinit_global(struct hapd_interfaces *ifaces)
+{
+	if (!ifaces->dpp_init_done)
+		return;
+	dpp_bootstrap_del(ifaces, 0);
+	dpp_configurator_del(ifaces, 0);
+}
diff --git a/src/ap/dpp_hostapd.h b/src/ap/dpp_hostapd.h
index d870b20..3ef7c14 100644
--- a/src/ap/dpp_hostapd.h
+++ b/src/ap/dpp_hostapd.h
@@ -17,6 +17,8 @@
 int hostapd_dpp_bootstrap_info(struct hostapd_data *hapd, int id,
 			       char *reply, int reply_size);
 int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd);
+int hostapd_dpp_listen(struct hostapd_data *hapd, const char *cmd);
+void hostapd_dpp_listen_stop(struct hostapd_data *hapd);
 void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src,
 			   const u8 *buf, size_t len, unsigned int freq);
 void hostapd_dpp_tx_status(struct hostapd_data *hapd, const u8 *dst,
@@ -24,11 +26,18 @@
 struct wpabuf *
 hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa,
 			    const u8 *query, size_t query_len);
+void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok);
 int hostapd_dpp_configurator_add(struct hostapd_data *hapd, const char *cmd);
 int hostapd_dpp_configurator_remove(struct hostapd_data *hapd, const char *id);
+int hostapd_dpp_configurator_sign(struct hostapd_data *hapd, const char *cmd);
+int hostapd_dpp_configurator_get_key(struct hostapd_data *hapd, unsigned int id,
+				     char *buf, size_t buflen);
 int hostapd_dpp_pkex_add(struct hostapd_data *hapd, const char *cmd);
 int hostapd_dpp_pkex_remove(struct hostapd_data *hapd, const char *id);
+void hostapd_dpp_stop(struct hostapd_data *hapd);
 int hostapd_dpp_init(struct hostapd_data *hapd);
 void hostapd_dpp_deinit(struct hostapd_data *hapd);
+void hostapd_dpp_init_global(struct hapd_interfaces *ifaces);
+void hostapd_dpp_deinit_global(struct hapd_interfaces *ifaces);
 
 #endif /* DPP_HOSTAPD_H */
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index 648f20e..3994278 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -235,6 +235,14 @@
 						 elems.hs20_len - 4);
 	} else
 		sta->hs20_ie = NULL;
+
+	wpabuf_free(sta->roaming_consortium);
+	if (elems.roaming_cons_sel)
+		sta->roaming_consortium = wpabuf_alloc_copy(
+			elems.roaming_cons_sel + 4,
+			elems.roaming_cons_sel_len - 4);
+	else
+		sta->roaming_consortium = NULL;
 #endif /* CONFIG_HS20 */
 
 #ifdef CONFIG_FST
@@ -262,7 +270,9 @@
 #endif /* CONFIG_WPS */
 
 			wpa_printf(MSG_DEBUG, "No WPA/RSN IE from STA");
-			return -1;
+			reason = WLAN_REASON_INVALID_IE;
+			status = WLAN_STATUS_INVALID_IE;
+			goto fail;
 		}
 #ifdef CONFIG_WPS
 		if (hapd->conf->wps_state && ie[0] == 0xdd && ie[1] >= 4 &&
@@ -317,8 +327,8 @@
 				reason = WLAN_REASON_INVALID_IE;
 				status = WLAN_STATUS_INVALID_IE;
 			} else if (res == WPA_INVALID_MGMT_GROUP_CIPHER) {
-				reason = WLAN_REASON_GROUP_CIPHER_NOT_VALID;
-				status = WLAN_STATUS_GROUP_CIPHER_NOT_VALID;
+				reason = WLAN_REASON_CIPHER_SUITE_REJECTED;
+				status = WLAN_STATUS_CIPHER_REJECTED_PER_POLICY;
 			}
 #endif /* CONFIG_IEEE80211W */
 			else {
@@ -443,6 +453,10 @@
 #ifdef CONFIG_IEEE80211R_AP
 	p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, buf, sizeof(buf),
 					sta->auth_alg, req_ies, req_ies_len);
+	if (!p) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to write AssocResp IEs");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
 #endif /* CONFIG_IEEE80211R_AP */
 
 #ifdef CONFIG_FILS
@@ -520,7 +534,32 @@
 	}
 #endif /* CONFIG_FILS */
 
-#if defined(CONFIG_IEEE80211R_AP) || defined(CONFIG_FILS)
+#ifdef CONFIG_OWE
+	if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
+	    wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE &&
+	    elems.owe_dh) {
+		u8 *npos;
+
+		npos = owe_assoc_req_process(hapd, sta,
+					     elems.owe_dh, elems.owe_dh_len,
+					     p, sizeof(buf) - (p - buf),
+					     &reason);
+		if (npos)
+			p = npos;
+		if (!npos &&
+		    reason == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) {
+			status = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+			hostapd_sta_assoc(hapd, addr, reassoc, status, buf,
+					  p - buf);
+			return 0;
+		}
+
+		if (!npos || reason != WLAN_STATUS_SUCCESS)
+			goto fail;
+	}
+#endif /* CONFIG_OWE */
+
+#if defined(CONFIG_IEEE80211R_AP) || defined(CONFIG_FILS) || defined(CONFIG_OWE)
 	hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf);
 
 	if (sta->auth_alg == WLAN_AUTH_FT ||
@@ -615,7 +654,7 @@
 {
 	struct sta_info *sta = ap_get_sta(hapd, addr);
 
-	if (!sta || !hapd->conf->disassoc_low_ack)
+	if (!sta || !hapd->conf->disassoc_low_ack || sta->agreed_to_steer)
 		return;
 
 	hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
@@ -626,6 +665,73 @@
 }
 
 
+void hostapd_event_sta_opmode_changed(struct hostapd_data *hapd, const u8 *addr,
+				      enum smps_mode smps_mode,
+				      enum chan_width chan_width, u8 rx_nss)
+{
+	struct sta_info *sta = ap_get_sta(hapd, addr);
+	const char *txt;
+
+	if (!sta)
+		return;
+
+	switch (smps_mode) {
+	case SMPS_AUTOMATIC:
+		txt = "automatic";
+		break;
+	case SMPS_OFF:
+		txt = "off";
+		break;
+	case SMPS_DYNAMIC:
+		txt = "dynamic";
+		break;
+	case SMPS_STATIC:
+		txt = "static";
+		break;
+	default:
+		txt = NULL;
+		break;
+	}
+	if (txt) {
+		wpa_msg(hapd->msg_ctx, MSG_INFO, STA_OPMODE_SMPS_MODE_CHANGED
+			MACSTR " %s", MAC2STR(addr), txt);
+	}
+
+	switch (chan_width) {
+	case CHAN_WIDTH_20_NOHT:
+		txt = "20(no-HT)";
+		break;
+	case CHAN_WIDTH_20:
+		txt = "20";
+		break;
+	case CHAN_WIDTH_40:
+		txt = "40";
+		break;
+	case CHAN_WIDTH_80:
+		txt = "80";
+		break;
+	case CHAN_WIDTH_80P80:
+		txt = "80+80";
+		break;
+	case CHAN_WIDTH_160:
+		txt = "160";
+		break;
+	default:
+		txt = NULL;
+		break;
+	}
+	if (txt) {
+		wpa_msg(hapd->msg_ctx, MSG_INFO, STA_OPMODE_MAX_BW_CHANGED
+			MACSTR " %s", MAC2STR(addr), txt);
+	}
+
+	if (rx_nss != 0xff) {
+		wpa_msg(hapd->msg_ctx, MSG_INFO, STA_OPMODE_N_SS_CHANGED
+			MACSTR " %d", MAC2STR(addr), rx_nss);
+	}
+}
+
+
 void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
 			     int offset, int width, int cf1, int cf2)
 {
@@ -635,9 +741,9 @@
 
 	hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
 		       HOSTAPD_LEVEL_INFO,
-		       "driver had channel switch: freq=%d, ht=%d, offset=%d, width=%d (%s), cf1=%d, cf2=%d",
-		       freq, ht, offset, width, channel_width_to_string(width),
-		       cf1, cf2);
+		       "driver had channel switch: freq=%d, ht=%d, vht_ch=0x%x, offset=%d, width=%d (%s), cf1=%d, cf2=%d",
+		       freq, ht, hapd->iconf->ch_switch_vht_config, offset,
+		       width, channel_width_to_string(width), cf1, cf2);
 
 	hapd->iface->freq = freq;
 
@@ -682,14 +788,26 @@
 
 	hapd->iconf->channel = channel;
 	hapd->iconf->ieee80211n = ht;
-	if (!ht)
+	if (!ht) {
 		hapd->iconf->ieee80211ac = 0;
+	} else if (hapd->iconf->ch_switch_vht_config) {
+		/* CHAN_SWITCH VHT config */
+		if (hapd->iconf->ch_switch_vht_config &
+		    CH_SWITCH_VHT_ENABLED)
+			hapd->iconf->ieee80211ac = 1;
+		else if (hapd->iconf->ch_switch_vht_config &
+			 CH_SWITCH_VHT_DISABLED)
+			hapd->iconf->ieee80211ac = 0;
+	}
+	hapd->iconf->ch_switch_vht_config = 0;
+
 	hapd->iconf->secondary_channel = offset;
 	hapd->iconf->vht_oper_chwidth = chwidth;
 	hapd->iconf->vht_oper_centr_freq_seg0_idx = seg0_idx;
 	hapd->iconf->vht_oper_centr_freq_seg1_idx = seg1_idx;
 
-	is_dfs = ieee80211_is_dfs(freq);
+	is_dfs = ieee80211_is_dfs(freq, hapd->iface->hw_features,
+				  hapd->iface->num_hw_features);
 
 	if (hapd->csa_in_progress &&
 	    freq == hapd->cs_freq_params.freq) {
@@ -1095,6 +1213,7 @@
 	}
 
 	os_memset(&fi, 0, sizeof(fi));
+	fi.freq = rx_mgmt->freq;
 	fi.datarate = rx_mgmt->datarate;
 	fi.ssi_signal = rx_mgmt->ssi_signal;
 
@@ -1378,6 +1497,28 @@
 #endif /* NEED_AP_MLME */
 
 
+static void hostapd_event_wds_sta_interface_status(struct hostapd_data *hapd,
+						   int istatus,
+						   const char *ifname,
+						   const u8 *addr)
+{
+	struct sta_info *sta = ap_get_sta(hapd, addr);
+
+	if (sta) {
+		os_free(sta->ifname_wds);
+		if (istatus == INTERFACE_ADDED)
+			sta->ifname_wds = os_strdup(ifname);
+		else
+			sta->ifname_wds = NULL;
+	}
+
+	wpa_msg(hapd->msg_ctx, MSG_INFO, "%sifname=%s sta_addr=" MACSTR,
+		istatus == INTERFACE_ADDED ?
+		WDS_STA_INTERFACE_ADDED : WDS_STA_INTERFACE_REMOVED,
+		ifname, MAC2STR(addr));
+}
+
+
 void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 			  union wpa_event_data *data)
 {
@@ -1586,6 +1727,18 @@
 					     &data->acs_selected_channels);
 		break;
 #endif /* CONFIG_ACS */
+	case EVENT_STATION_OPMODE_CHANGED:
+		hostapd_event_sta_opmode_changed(hapd, data->sta_opmode.addr,
+						 data->sta_opmode.smps_mode,
+						 data->sta_opmode.chan_width,
+						 data->sta_opmode.rx_nss);
+		break;
+	case EVENT_WDS_STA_INTERFACE_STATUS:
+		hostapd_event_wds_sta_interface_status(
+			hapd, data->wds_sta_interface.istatus,
+			data->wds_sta_interface.ifname,
+			data->wds_sta_interface.sta_addr);
+		break;
 	default:
 		wpa_printf(MSG_DEBUG, "Unknown event %d", event);
 		break;
diff --git a/src/ap/eap_user_db.c b/src/ap/eap_user_db.c
index 082d0f5..fab307f 100644
--- a/src/ap/eap_user_db.c
+++ b/src/ap/eap_user_db.c
@@ -91,6 +91,8 @@
 			set_user_methods(user, argv[i]);
 		} else if (os_strcmp(col[i], "remediation") == 0 && argv[i]) {
 			user->remediation = strlen(argv[i]) > 0;
+		} else if (os_strcmp(col[i], "t_c_timestamp") == 0 && argv[i]) {
+			user->t_c_timestamp = strtol(argv[i], 0, 10);
 		}
 	}
 
diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c
index fadb740..04fb3e1 100644
--- a/src/ap/gas_serv.c
+++ b/src/ap/gas_serv.c
@@ -183,6 +183,8 @@
 		wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
 	if (hapd->conf->hs20_icons_count)
 		wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST);
+	if (hapd->conf->hs20_operator_icon_count)
+		wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_ICON_METADATA);
 	gas_anqp_set_element_len(buf, len);
 }
 #endif /* CONFIG_HS20 */
@@ -288,7 +290,7 @@
 #endif /* CONFIG_FILS */
 	if (get_anqp_elem(hapd, ANQP_CAG))
 		wpabuf_put_le16(buf, ANQP_CAG);
-	if (get_anqp_elem(hapd, ANQP_VENUE_URL))
+	if (hapd->conf->venue_url || get_anqp_elem(hapd, ANQP_VENUE_URL))
 		wpabuf_put_le16(buf, ANQP_VENUE_URL);
 	if (get_anqp_elem(hapd, ANQP_ADVICE_OF_CHARGE))
 		wpabuf_put_le16(buf, ANQP_ADVICE_OF_CHARGE);
@@ -328,6 +330,29 @@
 }
 
 
+static void anqp_add_venue_url(struct hostapd_data *hapd, struct wpabuf *buf)
+{
+	if (anqp_add_override(hapd, buf, ANQP_VENUE_URL))
+		return;
+
+	if (hapd->conf->venue_url) {
+		u8 *len;
+		unsigned int i;
+
+		len = gas_anqp_add_element(buf, ANQP_VENUE_URL);
+		for (i = 0; i < hapd->conf->venue_url_count; i++) {
+			struct hostapd_venue_url *url;
+
+			url = &hapd->conf->venue_url[i];
+			wpabuf_put_u8(buf, 1 + url->url_len);
+			wpabuf_put_u8(buf, url->venue_number);
+			wpabuf_put_data(buf, url->url, url->url_len);
+		}
+		gas_anqp_set_element_len(buf, len);
+	}
+}
+
+
 static void anqp_add_network_auth_type(struct hostapd_data *hapd,
 				       struct wpabuf *buf)
 {
@@ -680,6 +705,29 @@
 }
 
 
+static void anqp_add_icon(struct wpabuf *buf, struct hostapd_bss_config *bss,
+			  const char *name)
+{
+	size_t j;
+	struct hs20_icon *icon = NULL;
+
+	for (j = 0; j < bss->hs20_icons_count && !icon; j++) {
+		if (os_strcmp(name, bss->hs20_icons[j].name) == 0)
+			icon = &bss->hs20_icons[j];
+	}
+	if (!icon)
+		return; /* icon info not found */
+
+	wpabuf_put_le16(buf, icon->width);
+	wpabuf_put_le16(buf, icon->height);
+	wpabuf_put_data(buf, icon->language, 3);
+	wpabuf_put_u8(buf, os_strlen(icon->type));
+	wpabuf_put_str(buf, icon->type);
+	wpabuf_put_u8(buf, os_strlen(icon->name));
+	wpabuf_put_str(buf, icon->name);
+}
+
+
 static void anqp_add_osu_provider(struct wpabuf *buf,
 				  struct hostapd_bss_config *bss,
 				  struct hs20_osu_provider *p)
@@ -714,26 +762,8 @@
 
 	/* Icons Available */
 	len2 = wpabuf_put(buf, 2);
-	for (i = 0; i < p->icons_count; i++) {
-		size_t j;
-		struct hs20_icon *icon = NULL;
-
-		for (j = 0; j < bss->hs20_icons_count && !icon; j++) {
-			if (os_strcmp(p->icons[i], bss->hs20_icons[j].name) ==
-			    0)
-				icon = &bss->hs20_icons[j];
-		}
-		if (!icon)
-			continue; /* icon info not found */
-
-		wpabuf_put_le16(buf, icon->width);
-		wpabuf_put_le16(buf, icon->height);
-		wpabuf_put_data(buf, icon->language, 3);
-		wpabuf_put_u8(buf, os_strlen(icon->type));
-		wpabuf_put_str(buf, icon->type);
-		wpabuf_put_u8(buf, os_strlen(icon->name));
-		wpabuf_put_str(buf, icon->name);
-	}
+	for (i = 0; i < p->icons_count; i++)
+		anqp_add_icon(buf, bss, p->icons[i]);
 	WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
 
 	/* OSU_NAI */
@@ -842,6 +872,30 @@
 	gas_anqp_set_element_len(buf, len);
 }
 
+
+static void anqp_add_operator_icon_metadata(struct hostapd_data *hapd,
+					    struct wpabuf *buf)
+{
+	struct hostapd_bss_config *bss = hapd->conf;
+	size_t i;
+	u8 *len;
+
+	if (!bss->hs20_operator_icon_count)
+		return;
+
+	len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+
+	wpabuf_put_be24(buf, OUI_WFA);
+	wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+	wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_ICON_METADATA);
+	wpabuf_put_u8(buf, 0); /* Reserved */
+
+	for (i = 0; i < bss->hs20_operator_icon_count; i++)
+		anqp_add_icon(buf, bss, bss->hs20_operator_icon[i]);
+
+	gas_anqp_set_element_len(buf, len);
+}
+
 #endif /* CONFIG_HS20 */
 
 
@@ -946,6 +1000,10 @@
 			continue;
 		}
 #endif /* CONFIG_FILS */
+		if (extra_req[i] == ANQP_VENUE_URL) {
+			anqp_add_venue_url(hapd, buf);
+			continue;
+		}
 		anqp_add_elem(hapd, buf, extra_req[i]);
 	}
 
@@ -964,6 +1022,8 @@
 		anqp_add_osu_providers_list(hapd, buf);
 	if (request & ANQP_REQ_ICON_REQUEST)
 		anqp_add_icon_binary_file(hapd, buf, icon_name, icon_name_len);
+	if (request & ANQP_REQ_OPERATOR_ICON_METADATA)
+		anqp_add_operator_icon_metadata(hapd, buf);
 #endif /* CONFIG_HS20 */
 
 #ifdef CONFIG_MBO
@@ -1082,7 +1142,10 @@
 				   "ANQP: FILS Realm Information (local)");
 		} else
 #endif /* CONFIG_FILS */
-		if (!get_anqp_elem(hapd, info_id)) {
+		if (info_id == ANQP_VENUE_URL && hapd->conf->venue_url) {
+			wpa_printf(MSG_DEBUG,
+				   "ANQP: Venue URL (local)");
+		} else if (!get_anqp_elem(hapd, info_id)) {
 			wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
 				   info_id);
 			break;
@@ -1148,6 +1211,11 @@
 		set_anqp_req(ANQP_REQ_OSU_PROVIDERS_LIST, "OSU Providers list",
 			     hapd->conf->hs20_osu_providers_count, qi);
 		break;
+	case HS20_STYPE_OPERATOR_ICON_METADATA:
+		set_anqp_req(ANQP_REQ_OPERATOR_ICON_METADATA,
+			     "Operator Icon Metadata",
+			     hapd->conf->hs20_operator_icon_count, qi);
+		break;
 	default:
 		wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u",
 			   subtype);
@@ -1460,7 +1528,7 @@
 			gas_serv_write_dpp_adv_proto(tx_buf);
 			wpabuf_put_le16(tx_buf, wpabuf_len(buf));
 			wpabuf_put_buf(tx_buf, buf);
-			wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
+			hostapd_dpp_gas_status_handler(hapd, 1);
 		}
 		wpabuf_free(buf);
 	}
@@ -1702,7 +1770,7 @@
 			"SD response sent");
 #ifdef CONFIG_DPP
 		if (dialog->dpp)
-			wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
+			hostapd_dpp_gas_status_handler(hapd, 1);
 #endif /* CONFIG_DPP */
 		gas_serv_dialog_clear(dialog);
 		gas_serv_free_dialogs(hapd, sa);
diff --git a/src/ap/gas_serv.h b/src/ap/gas_serv.h
index 3a30298..0afdcb1 100644
--- a/src/ap/gas_serv.h
+++ b/src/ap/gas_serv.h
@@ -60,6 +60,8 @@
 	(0x10000 << HS20_STYPE_OSU_PROVIDERS_LIST)
 #define ANQP_REQ_ICON_REQUEST \
 	(0x10000 << HS20_STYPE_ICON_REQUEST)
+#define ANQP_REQ_OPERATOR_ICON_METADATA \
+	(0x10000 << HS20_STYPE_OPERATOR_ICON_METADATA)
 /* The first MBO ANQP-element can be included in the optimized bitmap. */
 #define ANQP_REQ_MBO_CELL_DATA_CONN_PREF \
 	(BIT(29) << MBO_ANQP_SUBTYPE_CELL_CONN_PREF)
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 12911df..f095586 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -49,6 +49,7 @@
 #include "rrm.h"
 #include "fils_hlp.h"
 #include "acs.h"
+#include "hs20.h"
 
 
 static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason);
@@ -56,6 +57,8 @@
 static int hostapd_broadcast_wep_clear(struct hostapd_data *hapd);
 static int setup_interface2(struct hostapd_iface *iface);
 static void channel_list_update_timeout(void *eloop_ctx, void *timeout_ctx);
+static void hostapd_interface_setup_failure_handler(void *eloop_ctx,
+						    void *timeout_ctx);
 
 
 int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
@@ -82,6 +85,9 @@
 	if (!hapd->started)
 		return;
 
+	if (hapd->conf->wmm_enabled < 0)
+		hapd->conf->wmm_enabled = hapd->iconf->ieee80211n;
+
 #ifndef CONFIG_NO_RADIUS
 	radius_client_reconfig(hapd->radius, hapd->conf->radius);
 #endif /* CONFIG_NO_RADIUS */
@@ -426,6 +432,8 @@
 {
 	wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
 	eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
+	eloop_cancel_timeout(hostapd_interface_setup_failure_handler, iface,
+			     NULL);
 
 	hostapd_cleanup_iface_partial(iface);
 	hostapd_config_free(iface->conf);
@@ -893,6 +901,48 @@
 	return RADIUS_DAS_SUCCESS;
 }
 
+
+#ifdef CONFIG_HS20
+static enum radius_das_res
+hostapd_das_coa(void *ctx, struct radius_das_attrs *attr)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+	int multi;
+
+	if (hostapd_das_nas_mismatch(hapd, attr))
+		return RADIUS_DAS_NAS_MISMATCH;
+
+	sta = hostapd_das_find_sta(hapd, attr, &multi);
+	if (!sta) {
+		if (multi) {
+			wpa_printf(MSG_DEBUG,
+				   "RADIUS DAS: Multiple sessions match - not supported");
+			return RADIUS_DAS_MULTI_SESSION_MATCH;
+		}
+		wpa_printf(MSG_DEBUG, "RADIUS DAS: No matching session found");
+		return RADIUS_DAS_SESSION_NOT_FOUND;
+	}
+
+	wpa_printf(MSG_DEBUG, "RADIUS DAS: Found a matching session " MACSTR
+		   " - CoA", MAC2STR(sta->addr));
+
+	if (attr->hs20_t_c_filtering) {
+		if (attr->hs20_t_c_filtering[0] & BIT(0)) {
+			wpa_printf(MSG_DEBUG,
+				   "HS 2.0: Unexpected Terms and Conditions filtering required in CoA-Request");
+			return RADIUS_DAS_COA_FAILED;
+		}
+
+		hs20_t_c_filtering(hapd, sta, 0);
+	}
+
+	return RADIUS_DAS_SUCCESS;
+}
+#else /* CONFIG_HS20 */
+#define hostapd_das_coa NULL
+#endif /* CONFIG_HS20 */
+
 #endif /* CONFIG_NO_RADIUS */
 
 
@@ -1067,6 +1117,7 @@
 			conf->radius_das_require_message_authenticator;
 		das_conf.ctx = hapd;
 		das_conf.disconnect = hostapd_das_disconnect;
+		das_conf.coa = hostapd_das_coa;
 		hapd->radius_das = radius_das_init(&das_conf);
 		if (hapd->radius_das == NULL) {
 			wpa_printf(MSG_ERROR, "RADIUS DAS initialization "
@@ -1776,6 +1827,20 @@
 }
 
 
+static void hostapd_interface_setup_failure_handler(void *eloop_ctx,
+						    void *timeout_ctx)
+{
+	struct hostapd_iface *iface = eloop_ctx;
+	struct hostapd_data *hapd;
+
+	if (iface->num_bss < 1 || !iface->bss || !iface->bss[0])
+		return;
+	hapd = iface->bss[0];
+	if (hapd->setup_complete_cb)
+		hapd->setup_complete_cb(hapd->setup_complete_cb_ctx);
+}
+
+
 static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
 						 int err)
 {
@@ -1976,8 +2041,19 @@
 		iface->fst = NULL;
 	}
 #endif /* CONFIG_FST */
-	if (iface->interfaces && iface->interfaces->terminate_on_error)
+
+	if (iface->interfaces && iface->interfaces->terminate_on_error) {
 		eloop_terminate();
+	} else if (hapd->setup_complete_cb) {
+		/*
+		 * Calling hapd->setup_complete_cb directly may cause iface
+		 * deinitialization which may be accessed later by the caller.
+		 */
+		eloop_register_timeout(0, 0,
+				       hostapd_interface_setup_failure_handler,
+				       iface, NULL);
+	}
+
 	return -1;
 }
 
@@ -2159,12 +2235,6 @@
 
 	hostapd_set_state(iface, HAPD_IFACE_DISABLED);
 
-#ifdef CONFIG_IEEE80211N
-#ifdef NEED_AP_MLME
-	hostapd_stop_setup_timers(iface);
-	eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL);
-#endif /* NEED_AP_MLME */
-#endif /* CONFIG_IEEE80211N */
 	eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
 	iface->wait_channel_update = 0;
 
@@ -2180,6 +2250,13 @@
 			break;
 		hostapd_bss_deinit(iface->bss[j]);
 	}
+
+#ifdef CONFIG_IEEE80211N
+#ifdef NEED_AP_MLME
+	hostapd_stop_setup_timers(iface);
+	eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL);
+#endif /* NEED_AP_MLME */
+#endif /* CONFIG_IEEE80211N */
 }
 
 
@@ -3294,6 +3371,19 @@
 }
 
 
+void hostapd_chan_switch_vht_config(struct hostapd_data *hapd, int vht_enabled)
+{
+	if (vht_enabled)
+		hapd->iconf->ch_switch_vht_config |= CH_SWITCH_VHT_ENABLED;
+	else
+		hapd->iconf->ch_switch_vht_config |= CH_SWITCH_VHT_DISABLED;
+
+	hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_INFO, "CHAN_SWITCH VHT CONFIG 0x%x",
+		       hapd->iconf->ch_switch_vht_config);
+}
+
+
 int hostapd_switch_channel(struct hostapd_data *hapd,
 			   struct csa_settings *settings)
 {
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index d7c6720..6c41726 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -57,6 +57,12 @@
 	struct dl_list eth_p_oui; /* OUI Extended EtherType handlers */
 #endif /* CONFIG_ETH_P_OUI */
 	int eloop_initialized;
+
+#ifdef CONFIG_DPP
+	int dpp_init_done;
+	struct dl_list dpp_bootstrap; /* struct dpp_bootstrap_info */
+	struct dl_list dpp_configurator; /* struct dpp_configurator */
+#endif /* CONFIG_DPP */
 };
 
 enum hostapd_chan_status {
@@ -79,6 +85,7 @@
 };
 
 struct hostapd_frame_info {
+	unsigned int freq;
 	u32 channel;
 	u32 datarate;
 	int ssi_signal; /* dBm */
@@ -257,9 +264,6 @@
 	unsigned int cs_c_off_ecsa_beacon;
 	unsigned int cs_c_off_ecsa_proberesp;
 
-	/* BSS Load */
-	unsigned int bss_load_update_timeout;
-
 #ifdef CONFIG_P2P
 	struct p2p_data *p2p;
 	struct p2p_group *p2p_group;
@@ -338,13 +342,12 @@
 	int dhcp_sock; /* UDP socket used with the DHCP server */
 
 #ifdef CONFIG_DPP
-	struct dl_list dpp_bootstrap; /* struct dpp_bootstrap_info */
-	struct dl_list dpp_configurator; /* struct dpp_configurator */
 	int dpp_init_done;
 	struct dpp_authentication *dpp_auth;
 	u8 dpp_allowed_roles;
 	int dpp_qr_mutual;
 	int dpp_auth_ok_on_ack;
+	int dpp_in_response_listen;
 	struct gas_query_ap *gas;
 	struct dpp_pkex *dpp_pkex;
 	struct dpp_bootstrap_info *dpp_pkex_bi;
@@ -352,6 +355,13 @@
 	char *dpp_pkex_identifier;
 	char *dpp_pkex_auth_cmd;
 	char *dpp_configurator_params;
+	struct os_reltime dpp_last_init;
+	struct os_reltime dpp_init_iter_start;
+	unsigned int dpp_init_max_tries;
+	unsigned int dpp_init_retry_time;
+	unsigned int dpp_resp_wait_time;
+	unsigned int dpp_resp_max_tries;
+	unsigned int dpp_resp_retry_time;
 #ifdef CONFIG_TESTING_OPTIONS
 	char *dpp_config_obj_override;
 	char *dpp_discovery_override;
@@ -497,6 +507,10 @@
 	u64 last_channel_time_busy;
 	u8 channel_utilization;
 
+	unsigned int chan_util_samples_sum;
+	unsigned int chan_util_num_sample_periods;
+	unsigned int chan_util_average;
+
 	/* eCSA IE will be added only if operating class is specified */
 	u8 cs_oper_class;
 
@@ -551,6 +565,7 @@
 void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s);
 const char * hostapd_state_text(enum hostapd_iface_state s);
 int hostapd_csa_in_progress(struct hostapd_iface *iface);
+void hostapd_chan_switch_vht_config(struct hostapd_data *hapd, int vht_enabled);
 int hostapd_switch_channel(struct hostapd_data *hapd,
 			   struct csa_settings *settings);
 void
@@ -595,6 +610,9 @@
 
 struct hostapd_data * hostapd_get_iface(struct hapd_interfaces *interfaces,
 					const char *ifname);
+void hostapd_event_sta_opmode_changed(struct hostapd_data *hapd, const u8 *addr,
+				      enum smps_mode smps_mode,
+				      enum chan_width chan_width, u8 rx_nss);
 
 #ifdef CONFIG_FST
 void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd,
diff --git a/src/ap/hs20.c b/src/ap/hs20.c
index d7909fa..98d016d 100644
--- a/src/ap/hs20.c
+++ b/src/ap/hs20.c
@@ -11,9 +11,11 @@
 
 #include "common.h"
 #include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
 #include "hostapd.h"
 #include "ap_config.h"
 #include "ap_drv_ops.h"
+#include "sta_info.h"
 #include "hs20.h"
 
 
@@ -175,3 +177,71 @@
 
 	return ret;
 }
+
+
+int hs20_send_wnm_notification_t_c(struct hostapd_data *hapd,
+				   const u8 *addr, const char *url)
+{
+	struct wpabuf *buf;
+	int ret;
+	size_t url_len = os_strlen(url);
+
+	if (!url) {
+		wpa_printf(MSG_INFO, "HS 2.0: No T&C Server URL available");
+		return -1;
+	}
+
+	if (5 + url_len > 255) {
+		wpa_printf(MSG_INFO,
+			   "HS 2.0: Too long T&C Server URL for WNM-Notification: '%s'",
+			   url);
+		return -1;
+	}
+
+	buf = wpabuf_alloc(4 + 7 + url_len);
+	if (!buf)
+		return -1;
+
+	wpabuf_put_u8(buf, WLAN_ACTION_WNM);
+	wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ);
+	wpabuf_put_u8(buf, 1); /* Dialog token */
+	wpabuf_put_u8(buf, 1); /* Type - 1 reserved for WFA */
+
+	/* Terms and Conditions Acceptance subelement */
+	wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+	wpabuf_put_u8(buf, 4 + 1 + url_len);
+	wpabuf_put_be24(buf, OUI_WFA);
+	wpabuf_put_u8(buf, HS20_WNM_T_C_ACCEPTANCE);
+	wpabuf_put_u8(buf, url_len);
+	wpabuf_put_str(buf, url);
+
+	ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+				      wpabuf_head(buf), wpabuf_len(buf));
+
+	wpabuf_free(buf);
+
+	return ret;
+}
+
+
+void hs20_t_c_filtering(struct hostapd_data *hapd, struct sta_info *sta,
+			int enabled)
+{
+	if (enabled) {
+		wpa_printf(MSG_DEBUG,
+			   "HS 2.0: Terms and Conditions filtering required for "
+			   MACSTR, MAC2STR(sta->addr));
+		sta->hs20_t_c_filtering = 1;
+		/* TODO: Enable firewall filtering for the STA */
+		wpa_msg(hapd->msg_ctx, MSG_INFO, HS20_T_C_FILTERING_ADD MACSTR,
+			MAC2STR(sta->addr));
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "HS 2.0: Terms and Conditions filtering not required for "
+			   MACSTR, MAC2STR(sta->addr));
+		sta->hs20_t_c_filtering = 0;
+		/* TODO: Disable firewall filtering for the STA */
+		wpa_msg(hapd->msg_ctx, MSG_INFO,
+			HS20_T_C_FILTERING_REMOVE MACSTR, MAC2STR(sta->addr));
+	}
+}
diff --git a/src/ap/hs20.h b/src/ap/hs20.h
index 152439f..e99e26e 100644
--- a/src/ap/hs20.h
+++ b/src/ap/hs20.h
@@ -18,5 +18,9 @@
 int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd,
 					  const u8 *addr,
 					  const struct wpabuf *payload);
+int hs20_send_wnm_notification_t_c(struct hostapd_data *hapd,
+				   const u8 *addr, const char *url);
+void hs20_t_c_filtering(struct hostapd_data *hapd, struct sta_info *sta,
+			int enabled);
 
 #endif /* HS20_H */
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 65c4d88..d7e7210 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -277,7 +277,7 @@
 static int send_auth_reply(struct hostapd_data *hapd,
 			   const u8 *dst, const u8 *bssid,
 			   u16 auth_alg, u16 auth_transaction, u16 resp,
-			   const u8 *ies, size_t ies_len)
+			   const u8 *ies, size_t ies_len, const char *dbg)
 {
 	struct ieee80211_mgmt *reply;
 	u8 *buf;
@@ -304,9 +304,9 @@
 		os_memcpy(reply->u.auth.variable, ies, ies_len);
 
 	wpa_printf(MSG_DEBUG, "authentication reply: STA=" MACSTR
-		   " auth_alg=%d auth_transaction=%d resp=%d (IE len=%lu)",
+		   " auth_alg=%d auth_transaction=%d resp=%d (IE len=%lu) (dbg=%s)",
 		   MAC2STR(dst), auth_alg, auth_transaction,
-		   resp, (unsigned long) ies_len);
+		   resp, (unsigned long) ies_len, dbg);
 	if (hostapd_drv_send_mlme(hapd, reply, rlen, 0) < 0)
 		wpa_printf(MSG_INFO, "send_auth_reply: send failed");
 	else
@@ -328,7 +328,8 @@
 	int reply_res;
 
 	reply_res = send_auth_reply(hapd, dst, bssid, WLAN_AUTH_FT,
-				    auth_transaction, status, ies, ies_len);
+				    auth_transaction, status, ies, ies_len,
+				    "auth-ft-finish");
 
 	sta = ap_get_sta(hapd, dst);
 	if (sta == NULL)
@@ -354,16 +355,39 @@
 
 #ifdef CONFIG_SAE
 
-#define dot11RSNASAESync 5		/* attempts */
+static void sae_set_state(struct sta_info *sta, enum sae_state state,
+			  const char *reason)
+{
+	wpa_printf(MSG_DEBUG, "SAE: State %s -> %s for peer " MACSTR " (%s)",
+		   sae_state_txt(sta->sae->state), sae_state_txt(state),
+		   MAC2STR(sta->addr), reason);
+	sta->sae->state = state;
+}
 
 
 static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd,
 					     struct sta_info *sta, int update)
 {
 	struct wpabuf *buf;
-	const char *password;
+	const char *password = NULL;
+	struct sae_password_entry *pw;
+	const char *rx_id = NULL;
 
-	password = hapd->conf->sae_password;
+	if (sta->sae->tmp)
+		rx_id = sta->sae->tmp->pw_id;
+
+	for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
+		if (!is_broadcast_ether_addr(pw->peer_addr) &&
+		    os_memcmp(pw->peer_addr, sta->addr, ETH_ALEN) != 0)
+			continue;
+		if ((rx_id && !pw->identifier) || (!rx_id && pw->identifier))
+			continue;
+		if (rx_id && pw->identifier &&
+		    os_strcmp(rx_id, pw->identifier) != 0)
+			continue;
+		password = pw->password;
+		break;
+	}
 	if (!password)
 		password = hapd->conf->ssid.wpa_passphrase;
 	if (!password) {
@@ -373,17 +397,18 @@
 
 	if (update &&
 	    sae_prepare_commit(hapd->own_addr, sta->addr,
-			       (u8 *) password, os_strlen(password),
+			       (u8 *) password, os_strlen(password), rx_id,
 			       sta->sae) < 0) {
 		wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE");
 		return NULL;
 	}
 
-	buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN);
+	buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN +
+			   (rx_id ? 3 + os_strlen(rx_id) : 0));
 	if (buf == NULL)
 		return NULL;
 	sae_write_commit(sta->sae, buf, sta->sae->tmp ?
-			 sta->sae->tmp->anti_clogging_token : NULL);
+			 sta->sae->tmp->anti_clogging_token : NULL, rx_id);
 
 	return buf;
 }
@@ -412,12 +437,14 @@
 	int reply_res;
 
 	data = auth_build_sae_commit(hapd, sta, update);
+	if (!data && sta->sae->tmp && sta->sae->tmp->pw_id)
+		return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
 	if (data == NULL)
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 
 	reply_res = send_auth_reply(hapd, sta->addr, bssid, WLAN_AUTH_SAE, 1,
 				    WLAN_STATUS_SUCCESS, wpabuf_head(data),
-				    wpabuf_len(data));
+				    wpabuf_len(data), "sae-send-commit");
 
 	wpabuf_free(data);
 
@@ -438,7 +465,7 @@
 
 	reply_res = send_auth_reply(hapd, sta->addr, bssid, WLAN_AUTH_SAE, 2,
 				    WLAN_STATUS_SUCCESS, wpabuf_head(data),
-				    wpabuf_len(data));
+				    wpabuf_len(data), "sae-send-confirm");
 
 	wpabuf_free(data);
 
@@ -517,10 +544,10 @@
 }
 
 
-static int sae_check_big_sync(struct sta_info *sta)
+static int sae_check_big_sync(struct hostapd_data *hapd, struct sta_info *sta)
 {
-	if (sta->sae->sync > dot11RSNASAESync) {
-		sta->sae->state = SAE_NOTHING;
+	if (sta->sae->sync > hapd->conf->sae_sync) {
+		sae_set_state(sta, SAE_NOTHING, "Sync > dot11RSNASAESync");
 		sta->sae->sync = 0;
 		return -1;
 	}
@@ -534,12 +561,13 @@
 	struct sta_info *sta = eloop_data;
 	int ret;
 
-	if (sae_check_big_sync(sta))
+	if (sae_check_big_sync(hapd, sta))
 		return;
 	sta->sae->sync++;
 	wpa_printf(MSG_DEBUG, "SAE: Auth SAE retransmit timer for " MACSTR
-		   " (sync=%d state=%d)",
-		   MAC2STR(sta->addr), sta->sae->sync, sta->sae->state);
+		   " (sync=%d state=%s)",
+		   MAC2STR(sta->addr), sta->sae->sync,
+		   sae_state_txt(sta->sae->state));
 
 	switch (sta->sae->state) {
 	case SAE_COMMITTED:
@@ -588,7 +616,7 @@
 	sta->auth_alg = WLAN_AUTH_SAE;
 	mlme_authenticate_indication(hapd, sta);
 	wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
-	sta->sae->state = SAE_ACCEPTED;
+	sae_set_state(sta, SAE_ACCEPTED, "Accept Confirm");
 	wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr,
 			       sta->sae->pmk, sta->sae->pmkid);
 }
@@ -602,13 +630,16 @@
 	if (auth_transaction != 1 && auth_transaction != 2)
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 
+	wpa_printf(MSG_DEBUG, "SAE: Peer " MACSTR " state=%s auth_trans=%u",
+		   MAC2STR(sta->addr), sae_state_txt(sta->sae->state),
+		   auth_transaction);
 	switch (sta->sae->state) {
 	case SAE_NOTHING:
 		if (auth_transaction == 1) {
 			ret = auth_sae_send_commit(hapd, sta, bssid, 1);
 			if (ret)
 				return ret;
-			sta->sae->state = SAE_COMMITTED;
+			sae_set_state(sta, SAE_COMMITTED, "Sent Commit");
 
 			if (sae_process_commit(sta->sae) < 0)
 				return WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -630,7 +661,8 @@
 				ret = auth_sae_send_confirm(hapd, sta, bssid);
 				if (ret)
 					return ret;
-				sta->sae->state = SAE_CONFIRMED;
+				sae_set_state(sta, SAE_CONFIRMED,
+					      "Sent Confirm (mesh)");
 			} else {
 				/*
 				 * For infrastructure BSS, send only the Commit
@@ -659,7 +691,7 @@
 			ret = auth_sae_send_confirm(hapd, sta, bssid);
 			if (ret)
 				return ret;
-			sta->sae->state = SAE_CONFIRMED;
+			sae_set_state(sta, SAE_CONFIRMED, "Sent Confirm");
 			sta->sae->sync = 0;
 			sae_set_retransmit_timer(hapd, sta);
 		} else if (hapd->conf->mesh & MESH_ENABLED) {
@@ -667,7 +699,7 @@
 			 * In mesh case, follow SAE finite state machine and
 			 * send Commit now, if sync count allows.
 			 */
-			if (sae_check_big_sync(sta))
+			if (sae_check_big_sync(hapd, sta))
 				return WLAN_STATUS_SUCCESS;
 			sta->sae->sync++;
 
@@ -686,7 +718,7 @@
 			if (ret)
 				return ret;
 
-			sta->sae->state = SAE_CONFIRMED;
+			sae_set_state(sta, SAE_CONFIRMED, "Sent Confirm");
 
 			/*
 			 * Since this was triggered on Confirm RX, run another
@@ -699,7 +731,7 @@
 	case SAE_CONFIRMED:
 		sae_clear_retransmit_timer(hapd, sta);
 		if (auth_transaction == 1) {
-			if (sae_check_big_sync(sta))
+			if (sae_check_big_sync(hapd, sta))
 				return WLAN_STATUS_SUCCESS;
 			sta->sae->sync++;
 
@@ -716,18 +748,31 @@
 
 			sae_set_retransmit_timer(hapd, sta);
 		} else {
+			sta->sae->send_confirm = 0xffff;
 			sae_accept_sta(hapd, sta);
 		}
 		break;
 	case SAE_ACCEPTED:
-		if (auth_transaction == 1) {
+		if (auth_transaction == 1 &&
+		    (hapd->conf->mesh & MESH_ENABLED)) {
 			wpa_printf(MSG_DEBUG, "SAE: remove the STA (" MACSTR
 				   ") doing reauthentication",
 				   MAC2STR(sta->addr));
 			ap_free_sta(hapd, sta);
 			wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
+		} else if (auth_transaction == 1) {
+			wpa_printf(MSG_DEBUG, "SAE: Start reauthentication");
+			ret = auth_sae_send_commit(hapd, sta, bssid, 1);
+			if (ret)
+				return ret;
+			sae_set_state(sta, SAE_COMMITTED, "Sent Commit");
+
+			if (sae_process_commit(sta->sae) < 0)
+				return WLAN_STATUS_UNSPECIFIED_FAILURE;
+			sta->sae->sync = 0;
+			sae_set_retransmit_timer(hapd, sta);
 		} else {
-			if (sae_check_big_sync(sta))
+			if (sae_check_big_sync(hapd, sta))
 				return WLAN_STATUS_SUCCESS;
 			sta->sae->sync++;
 
@@ -799,7 +844,8 @@
 		pos = mgmt->u.auth.variable;
 		end = ((const u8 *) mgmt) + len;
 		send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
-				auth_transaction, resp, pos, end - pos);
+				auth_transaction, resp, pos, end - pos,
+				"auth-sae-reflection-attack");
 		goto remove_sta;
 	}
 
@@ -808,7 +854,8 @@
 		send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
 				auth_transaction, resp,
 				wpabuf_head(hapd->conf->sae_commit_override),
-				wpabuf_len(hapd->conf->sae_commit_override));
+				wpabuf_len(hapd->conf->sae_commit_override),
+				"sae-commit-override");
 		goto remove_sta;
 	}
 #endif /* CONFIG_TESTING_OPTIONS */
@@ -823,7 +870,7 @@
 			resp = -1;
 			goto remove_sta;
 		}
-		sta->sae->state = SAE_NOTHING;
+		sae_set_state(sta, SAE_NOTHING, "Init");
 		sta->sae->sync = 0;
 	}
 
@@ -886,7 +933,8 @@
 					   "SAE: Failed to send commit message");
 				goto remove_sta;
 			}
-			sta->sae->state = SAE_COMMITTED;
+			sae_set_state(sta, SAE_COMMITTED,
+				      "Sent Commit (anti-clogging token case in mesh)");
 			sta->sae->sync = 0;
 			sae_set_retransmit_timer(hapd, sta);
 			return;
@@ -905,6 +953,20 @@
 		if (status_code != WLAN_STATUS_SUCCESS)
 			goto remove_sta;
 
+		if (!(hapd->conf->mesh & MESH_ENABLED) &&
+		    sta->sae->state == SAE_COMMITTED) {
+			/* This is needed in the infrastructure BSS case to
+			 * address a sequence where a STA entry may remain in
+			 * hostapd across two attempts to do SAE authentication
+			 * by the same STA. The second attempt may end up trying
+			 * to use a different group and that would not be
+			 * allowed if we remain in Committed state with the
+			 * previously set parameters. */
+			sae_set_state(sta, SAE_NOTHING,
+				      "Clear existing state to allow restart");
+			sae_clear_data(sta->sae);
+		}
+
 		resp = sae_parse_commit(sta->sae, mgmt->u.auth.variable,
 					((const u8 *) mgmt) + len -
 					mgmt->u.auth.variable, &token,
@@ -915,6 +977,17 @@
 				   MAC2STR(sta->addr));
 			goto remove_sta;
 		}
+
+		if (resp == WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER) {
+			wpa_msg(hapd->msg_ctx, MSG_INFO,
+				WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER
+				MACSTR, MAC2STR(sta->addr));
+			sae_clear_retransmit_timer(hapd, sta);
+			sae_set_state(sta, SAE_NOTHING,
+				      "Unknown Password Identifier");
+			goto remove_sta;
+		}
+
 		if (token && check_sae_token(hapd, sta->addr, token, token_len)
 		    < 0) {
 			wpa_printf(MSG_DEBUG, "SAE: Drop commit message with "
@@ -935,7 +1008,8 @@
 						    sta->addr);
 			resp = WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ;
 			if (hapd->conf->mesh & MESH_ENABLED)
-				sta->sae->state = SAE_NOTHING;
+				sae_set_state(sta, SAE_NOTHING,
+					      "Request anti-clogging token case in mesh");
 			goto reply;
 		}
 
@@ -949,12 +1023,36 @@
 			goto remove_sta;
 		if (sta->sae->state >= SAE_CONFIRMED ||
 		    !(hapd->conf->mesh & MESH_ENABLED)) {
-			if (sae_check_confirm(sta->sae, mgmt->u.auth.variable,
-					      ((u8 *) mgmt) + len -
-					      mgmt->u.auth.variable) < 0) {
+			const u8 *var;
+			size_t var_len;
+			u16 peer_send_confirm;
+
+			var = mgmt->u.auth.variable;
+			var_len = ((u8 *) mgmt) + len - mgmt->u.auth.variable;
+			if (var_len < 2) {
 				resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
 				goto reply;
 			}
+
+			peer_send_confirm = WPA_GET_LE16(var);
+
+			if (sta->sae->state == SAE_ACCEPTED &&
+			    (peer_send_confirm <= sta->sae->rc ||
+			     peer_send_confirm == 0xffff)) {
+				wpa_printf(MSG_DEBUG,
+					   "SAE: Silently ignore unexpected Confirm from peer "
+					   MACSTR
+					   " (peer-send-confirm=%u Rc=%u)",
+					   MAC2STR(sta->addr),
+					   peer_send_confirm, sta->sae->rc);
+				return;
+			}
+
+			if (sae_check_confirm(sta->sae, var, var_len) < 0) {
+				resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+				goto reply;
+			}
+			sta->sae->rc = peer_send_confirm;
 		}
 		resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction);
 	} else {
@@ -972,7 +1070,7 @@
 		send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
 				auth_transaction, resp,
 				data ? wpabuf_head(data) : (u8 *) "",
-				data ? wpabuf_len(data) : 0);
+				data ? wpabuf_len(data) : 0, "auth-sae");
 	}
 
 remove_sta:
@@ -1009,7 +1107,7 @@
 	if (ret)
 		return -1;
 
-	sta->sae->state = SAE_COMMITTED;
+	sae_set_state(sta, SAE_COMMITTED, "Init and sent commit");
 	sta->sae->sync = 0;
 	sae_set_retransmit_timer(hapd, sta);
 
@@ -1033,7 +1131,7 @@
 	if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION)
 		return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
 	if (res == WPA_INVALID_MGMT_GROUP_CIPHER)
-		return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
+		return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY;
 #endif /* CONFIG_IEEE80211W */
 	if (res == WPA_INVALID_MDIE)
 		return WLAN_STATUS_INVALID_MDIE;
@@ -1374,8 +1472,11 @@
 	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, wpabuf_put(data, 0),
+		res = wpa_auth_write_fte(hapd->wpa_auth, use_sha384,
+					 wpabuf_put(data, 0),
 					 wpabuf_tailroom(data));
 		if (res < 0) {
 			*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -1430,15 +1531,24 @@
 		if (sta->fils_erp_pmkid_set) {
 			/* TODO: get PMKLifetime from WPA parameters */
 			unsigned int dot11RSNAConfigPMKLifetime = 43200;
+			int session_timeout;
+
+			session_timeout = dot11RSNAConfigPMKLifetime;
+			if (sta->session_timeout_set) {
+				struct os_reltime now, diff;
+
+				os_get_reltime(&now);
+				os_reltime_sub(&sta->session_timeout, &now,
+					       &diff);
+				session_timeout = diff.sec;
+			}
 
 			sta->fils_erp_pmkid_set = 0;
 			if (wpa_auth_pmksa_add2(
 				    hapd->wpa_auth, sta->addr,
 				    pmk, pmk_len,
 				    sta->fils_erp_pmkid,
-				    sta->session_timeout_set ?
-				    sta->session_timeout :
-				    dot11RSNAConfigPMKLifetime,
+				    session_timeout,
 				    wpa_auth_sta_key_mgmt(sta->wpa_sm)) < 0) {
 				wpa_printf(MSG_ERROR,
 					   "FILS: Failed to add PMKSA cache entry based on ERP");
@@ -1496,7 +1606,7 @@
 		WLAN_AUTH_FILS_SK_PFS : WLAN_AUTH_FILS_SK;
 	send_auth_reply(hapd, sta->addr, hapd->own_addr, auth_alg, 2, resp,
 			data ? wpabuf_head(data) : (u8 *) "",
-			data ? wpabuf_len(data) : 0);
+			data ? wpabuf_len(data) : 0, "auth-fils-finish");
 	wpabuf_free(data);
 
 	if (resp == WLAN_STATUS_SUCCESS) {
@@ -1538,20 +1648,21 @@
 #endif /* CONFIG_FILS */
 
 
-static int
+int
 ieee802_11_allowed_address(struct hostapd_data *hapd, const u8 *addr,
 			   const u8 *msg, size_t len, u32 *session_timeout,
 			   u32 *acct_interim_interval,
 			   struct vlan_description *vlan_id,
 			   struct hostapd_sta_wpa_psk_short **psk,
-			   char **identity, char **radius_cui)
+			   char **identity, char **radius_cui, int is_probe_req)
 {
 	int res;
 
 	os_memset(vlan_id, 0, sizeof(*vlan_id));
 	res = hostapd_allowed_address(hapd, addr, msg, len,
 				      session_timeout, acct_interim_interval,
-				      vlan_id, psk, identity, radius_cui);
+				      vlan_id, psk, identity, radius_cui,
+				      is_probe_req);
 
 	if (res == HOSTAPD_ACL_REJECT) {
 		wpa_printf(MSG_INFO,
@@ -1605,17 +1716,25 @@
 		sta->psk = NULL;
 	}
 
+	os_free(sta->identity);
 	sta->identity = *identity;
 	*identity = NULL;
+
+	os_free(sta->radius_cui);
 	sta->radius_cui = *radius_cui;
 	*radius_cui = NULL;
 
 	if (hapd->conf->acct_interim_interval == 0 && acct_interim_interval)
 		sta->acct_interim_interval = acct_interim_interval;
-	if (res == HOSTAPD_ACL_ACCEPT_TIMEOUT)
+	if (res == HOSTAPD_ACL_ACCEPT_TIMEOUT) {
+		sta->session_timeout_set = 1;
+		os_get_reltime(&sta->session_timeout);
+		sta->session_timeout.sec += session_timeout;
 		ap_sta_session_timeout(hapd, sta, session_timeout);
-	else
+	} else {
+		sta->session_timeout_set = 0;
 		ap_sta_no_session_timeout(hapd, sta);
+	}
 
 	return 0;
 }
@@ -1686,7 +1805,9 @@
 #endif /* CONFIG_NO_RC4 */
 
 	if (hapd->tkip_countermeasures) {
-		resp = WLAN_REASON_MICHAEL_MIC_FAILURE;
+		wpa_printf(MSG_DEBUG,
+			   "Ongoing TKIP countermeasures (Michael MIC failure) - reject authentication");
+		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
 		goto fail;
 	}
 
@@ -1787,8 +1908,12 @@
 
 	res = ieee802_11_allowed_address(
 		hapd, mgmt->sa, (const u8 *) mgmt, len, &session_timeout,
-		&acct_interim_interval, &vlan_id, &psk, &identity, &radius_cui);
+		&acct_interim_interval, &vlan_id, &psk, &identity, &radius_cui,
+		0);
 	if (res == HOSTAPD_ACL_REJECT) {
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+			"Ignore Authentication frame from " MACSTR
+			" due to ACL reject", MAC2STR(mgmt->sa));
 		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
 		goto fail;
 	}
@@ -1839,6 +1964,7 @@
 
 		sta = ap_sta_add(hapd, mgmt->sa);
 		if (!sta) {
+			wpa_printf(MSG_DEBUG, "ap_sta_add() failed");
 			resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
 			goto fail;
 		}
@@ -1850,6 +1976,7 @@
 		hapd, sta, res, session_timeout, acct_interim_interval,
 		&vlan_id, &psk, &identity, &radius_cui);
 	if (res) {
+		wpa_printf(MSG_DEBUG, "ieee802_11_set_radius_info() failed");
 		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
 		goto fail;
 	}
@@ -1918,6 +2045,9 @@
 	case WLAN_AUTH_SHARED_KEY:
 		resp = auth_shared_key(hapd, sta, auth_transaction, challenge,
 				       fc & WLAN_FC_ISWEP);
+		if (resp != 0)
+			wpa_printf(MSG_DEBUG,
+				   "auth_shared_key() failed: status=%d", resp);
 		sta->auth_alg = WLAN_AUTH_SHARED_KEY;
 		mlme_authenticate_indication(hapd, sta);
 		if (sta->challenge && auth_transaction == 1) {
@@ -1988,7 +2118,7 @@
 
 	reply_res = send_auth_reply(hapd, mgmt->sa, mgmt->bssid, auth_alg,
 				    auth_transaction + 1, resp, resp_ies,
-				    resp_ies_len);
+				    resp_ies_len, "handle-auth");
 
 	if (sta && sta->added_unassoc && (resp != WLAN_STATUS_SUCCESS ||
 					  reply_res != WLAN_STATUS_SUCCESS)) {
@@ -2123,8 +2253,16 @@
 	}
 #endif /* CONFIG_INTERWORKING */
 
-	if (ext_capab_ie_len > 0)
+	if (ext_capab_ie_len > 0) {
 		sta->ecsa_supported = !!(ext_capab_ie[0] & BIT(2));
+		os_free(sta->ext_capability);
+		sta->ext_capability = os_malloc(1 + ext_capab_ie_len);
+		if (sta->ext_capability) {
+			sta->ext_capability[0] = ext_capab_ie_len;
+			os_memcpy(sta->ext_capability + 1, ext_capab_ie,
+				  ext_capab_ie_len);
+		}
+	}
 
 	return WLAN_STATUS_SUCCESS;
 }
@@ -2486,6 +2624,10 @@
 #endif /* CONFIG_IEEE80211R_AP */
 
 #ifdef CONFIG_SAE
+		if (wpa_auth_uses_sae(sta->wpa_sm) && sta->sae &&
+		    sta->sae->state == SAE_ACCEPTED)
+			wpa_auth_add_sae_pmkid(sta->wpa_sm, sta->sae->pmkid);
+
 		if (wpa_auth_uses_sae(sta->wpa_sm) &&
 		    sta->auth_alg == WLAN_AUTH_OPEN) {
 			struct rsn_pmksa_cache_entry *sa;
@@ -2568,6 +2710,14 @@
 						 elems.hs20_len - 4);
 	} else
 		sta->hs20_ie = NULL;
+
+	wpabuf_free(sta->roaming_consortium);
+	if (elems.roaming_cons_sel)
+		sta->roaming_consortium = wpabuf_alloc_copy(
+			elems.roaming_cons_sel + 4,
+			elems.roaming_cons_sel_len - 4);
+	else
+		sta->roaming_consortium = NULL;
 #endif /* CONFIG_HS20 */
 
 #ifdef CONFIG_FST
@@ -2599,6 +2749,14 @@
 		os_memcpy(sta->rrm_enabled_capa, elems.rrm_enabled,
 			  sizeof(sta->rrm_enabled_capa));
 
+	if (elems.power_capab) {
+		sta->min_tx_power = elems.power_capab[0];
+		sta->max_tx_power = elems.power_capab[1];
+		sta->power_capab = 1;
+	} else {
+		sta->power_capab = 0;
+	}
+
 	return WLAN_STATUS_SUCCESS;
 }
 
@@ -2747,6 +2905,12 @@
 		p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, p,
 						buf + buflen - p,
 						sta->auth_alg, ies, ies_len);
+		if (!p) {
+			wpa_printf(MSG_DEBUG,
+				   "FT: Failed to write AssocResp IEs");
+			res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto done;
+		}
 	}
 #endif /* CONFIG_IEEE80211R_AP */
 
@@ -2932,6 +3096,61 @@
 }
 
 
+#ifdef CONFIG_OWE
+u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta,
+			   const u8 *owe_dh, u8 owe_dh_len,
+			   u8 *owe_buf, size_t owe_buf_len, u16 *reason)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+	if (hapd->conf->own_ie_override) {
+		wpa_printf(MSG_DEBUG, "OWE: Using IE override");
+		*reason = WLAN_STATUS_SUCCESS;
+		return wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf,
+						     owe_buf_len, NULL, 0);
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	if (wpa_auth_sta_get_pmksa(sta->wpa_sm)) {
+		wpa_printf(MSG_DEBUG, "OWE: Using PMKSA caching");
+		owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf,
+							owe_buf_len, NULL, 0);
+		*reason = WLAN_STATUS_SUCCESS;
+		return owe_buf;
+	}
+
+	*reason = owe_process_assoc_req(hapd, sta, owe_dh, owe_dh_len);
+	if (*reason != WLAN_STATUS_SUCCESS)
+		return NULL;
+
+	owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf,
+						owe_buf_len, NULL, 0);
+
+	if (sta->owe_ecdh && owe_buf) {
+		struct wpabuf *pub;
+
+		pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
+		if (!pub) {
+			*reason = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			return owe_buf;
+		}
+
+		/* OWE Diffie-Hellman Parameter element */
+		*owe_buf++ = WLAN_EID_EXTENSION; /* Element ID */
+		*owe_buf++ = 1 + 2 + wpabuf_len(pub); /* Length */
+		*owe_buf++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension
+							 */
+		WPA_PUT_LE16(owe_buf, sta->owe_group);
+		owe_buf += 2;
+		os_memcpy(owe_buf, wpabuf_head(pub), wpabuf_len(pub));
+		owe_buf += wpabuf_len(pub);
+		wpabuf_free(pub);
+	}
+
+	return owe_buf;
+}
+#endif /* CONFIG_OWE */
+
+
 #ifdef CONFIG_FILS
 
 void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta)
@@ -3079,8 +3298,12 @@
 			acl_res = ieee802_11_allowed_address(
 				hapd, mgmt->sa, (const u8 *) mgmt, len,
 				&session_timeout, &acct_interim_interval,
-				&vlan_id, &psk, &identity, &radius_cui);
+				&vlan_id, &psk, &identity, &radius_cui, 0);
 			if (acl_res == HOSTAPD_ACL_REJECT) {
+				wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+					"Ignore Association Request frame from "
+					MACSTR " due to ACL reject",
+					MAC2STR(mgmt->sa));
 				resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
 				goto fail;
 			}
@@ -3144,7 +3367,7 @@
 		WLAN_FC_STYPE_ASSOC_REQ;
 
 	if (hapd->tkip_countermeasures) {
-		resp = WLAN_REASON_MICHAEL_MIC_FAILURE;
+		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
 		goto fail;
 	}
 
@@ -3174,6 +3397,8 @@
 	if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
 	    sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
 	    sta->auth_alg == WLAN_AUTH_FILS_PK) {
+		int res;
+
 		/* The end of the payload is encrypted. Need to decrypt it
 		 * before parsing. */
 
@@ -3183,13 +3408,14 @@
 			goto fail;
 		}
 
-		left = fils_decrypt_assoc(sta->wpa_sm, sta->fils_session, mgmt,
-					  len, tmp, left);
-		if (left < 0) {
+		res = fils_decrypt_assoc(sta->wpa_sm, sta->fils_session, mgmt,
+					 len, tmp, left);
+		if (res < 0) {
 			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
 			goto fail;
 		}
 		pos = tmp;
+		left = res;
 	}
 #endif /* CONFIG_FILS */
 
@@ -3208,7 +3434,8 @@
 
 	sta->listen_interval = listen_interval;
 
-	if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
+	if (hapd->iface->current_mode &&
+	    hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
 		sta->flags |= WLAN_STA_NONERP;
 	for (i = 0; i < sta->supported_rates_len; i++) {
 		if ((sta->supported_rates[i] & 0x7f) > 22) {
@@ -3227,7 +3454,8 @@
 	    !sta->no_short_slot_time_set) {
 		sta->no_short_slot_time_set = 1;
 		hapd->iface->num_sta_no_short_slot_time++;
-		if (hapd->iface->current_mode->mode ==
+		if (hapd->iface->current_mode &&
+		    hapd->iface->current_mode->mode ==
 		    HOSTAPD_MODE_IEEE80211G &&
 		    hapd->iface->num_sta_no_short_slot_time == 1)
 			ieee802_11_set_beacons(hapd->iface);
@@ -3242,7 +3470,8 @@
 	    !sta->no_short_preamble_set) {
 		sta->no_short_preamble_set = 1;
 		hapd->iface->num_sta_no_short_preamble++;
-		if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
+		if (hapd->iface->current_mode &&
+		    hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
 		    && hapd->iface->num_sta_no_short_preamble == 1)
 			ieee802_11_set_beacons(hapd->iface);
 	}
@@ -3512,7 +3741,8 @@
 
 
 static int handle_action(struct hostapd_data *hapd,
-			 const struct ieee80211_mgmt *mgmt, size_t len)
+			 const struct ieee80211_mgmt *mgmt, size_t len,
+			 unsigned int freq)
 {
 	struct sta_info *sta;
 	sta = ap_get_sta(hapd, mgmt->sa);
@@ -3601,9 +3831,6 @@
 		if (len >= IEEE80211_HDRLEN + 2 &&
 		    mgmt->u.action.u.public_action.action ==
 		    WLAN_PA_20_40_BSS_COEX) {
-			wpa_printf(MSG_DEBUG,
-				   "HT20/40 coex mgmt frame received from STA "
-				   MACSTR, MAC2STR(mgmt->sa));
 			hostapd_2040_coex_action(hapd, mgmt, len);
 			return 1;
 		}
@@ -3621,7 +3848,7 @@
 			pos = mgmt->u.action.u.vs_public_action.oui;
 			end = ((const u8 *) mgmt) + len;
 			hostapd_dpp_rx_action(hapd, mgmt->sa, pos, end - pos,
-					      hapd->iface->freq);
+					      freq);
 			return 1;
 		}
 		if (len >= IEEE80211_HDRLEN + 2 &&
@@ -3720,10 +3947,17 @@
 	struct ieee80211_mgmt *mgmt;
 	u16 fc, stype;
 	int ret = 0;
+	unsigned int freq;
+	int ssi_signal = fi ? fi->ssi_signal : 0;
 
 	if (len < 24)
 		return 0;
 
+	if (fi && fi->freq)
+		freq = fi->freq;
+	else
+		freq = hapd->iface->freq;
+
 	mgmt = (struct ieee80211_mgmt *) buf;
 	fc = le_to_host16(mgmt->frame_control);
 	stype = WLAN_FC_GET_STYPE(fc);
@@ -3750,7 +3984,7 @@
 
 
 	if (stype == WLAN_FC_STYPE_PROBE_REQ) {
-		handle_probe_req(hapd, mgmt, len, fi->ssi_signal);
+		handle_probe_req(hapd, mgmt, len, ssi_signal);
 		return 1;
 	}
 
@@ -3765,7 +3999,7 @@
 	}
 
 	if (hapd->iconf->track_sta_max_num)
-		sta_track_add(hapd->iface, mgmt->sa, fi->ssi_signal);
+		sta_track_add(hapd->iface, mgmt->sa, ssi_signal);
 
 	switch (stype) {
 	case WLAN_FC_STYPE_AUTH:
@@ -3795,7 +4029,7 @@
 		break;
 	case WLAN_FC_STYPE_ACTION:
 		wpa_printf(MSG_DEBUG, "mgmt::action");
-		ret = handle_action(hapd, mgmt, len);
+		ret = handle_action(hapd, mgmt, len, freq);
 		break;
 	default:
 		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index 3b381b4..2f3b4da 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -16,6 +16,8 @@
 struct ieee80211_ht_capabilities;
 struct ieee80211_vht_capabilities;
 struct ieee80211_mgmt;
+struct vlan_description;
+struct hostapd_sta_wpa_psk_short;
 
 int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
 		    struct hostapd_frame_info *fi);
@@ -142,6 +144,9 @@
 				 struct sta_info *sta, int success,
 				 struct wpabuf *erp_resp,
 				 const u8 *msk, size_t msk_len);
+u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta,
+			   const u8 *owe_dh, u8 owe_dh_len,
+			   u8 *owe_buf, size_t owe_buf_len, u16 *reason);
 void fils_hlp_timeout(void *eloop_ctx, void *eloop_data);
 void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta);
 void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
@@ -151,4 +156,14 @@
 				 struct sta_info *sta,
 				 u16 resp, struct wpabuf *data, int pub));
 
+size_t hostapd_eid_owe_trans_len(struct hostapd_data *hapd);
+u8 * hostapd_eid_owe_trans(struct hostapd_data *hapd, u8 *eid, size_t len);
+int ieee802_11_allowed_address(struct hostapd_data *hapd, const u8 *addr,
+			       const u8 *msg, size_t len, u32 *session_timeout,
+			       u32 *acct_interim_interval,
+			       struct vlan_description *vlan_id,
+			       struct hostapd_sta_wpa_psk_short **psk,
+			       char **identity, char **radius_cui,
+			       int is_probe_req);
+
 #endif /* IEEE802_11_H */
diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c
index 3308398..5cb7fb1 100644
--- a/src/ap/ieee802_11_auth.c
+++ b/src/ap/ieee802_11_auth.c
@@ -244,6 +244,7 @@
  * @psk: Linked list buffer for returning WPA PSK
  * @identity: Buffer for returning identity (from RADIUS)
  * @radius_cui: Buffer for returning CUI (from RADIUS)
+ * @is_probe_req: Whether this query for a Probe Request frame
  * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING
  *
  * The caller is responsible for freeing the returned *identity and *radius_cui
@@ -254,7 +255,8 @@
 			    u32 *acct_interim_interval,
 			    struct vlan_description *vlan_id,
 			    struct hostapd_sta_wpa_psk_short **psk,
-			    char **identity, char **radius_cui)
+			    char **identity, char **radius_cui,
+			    int is_probe_req)
 {
 	int res;
 
@@ -281,6 +283,12 @@
 #else /* CONFIG_NO_RADIUS */
 		struct hostapd_acl_query_data *query;
 
+		if (is_probe_req) {
+			/* Skip RADIUS queries for Probe Request frames to avoid
+			 * excessive load on the authentication server. */
+			return HOSTAPD_ACL_ACCEPT;
+		};
+
 		/* Check whether ACL cache has an entry for this station */
 		res = hostapd_acl_cache_get(hapd, addr, session_timeout,
 					    acct_interim_interval, vlan_id, psk,
diff --git a/src/ap/ieee802_11_auth.h b/src/ap/ieee802_11_auth.h
index 71f53b9..5aece51 100644
--- a/src/ap/ieee802_11_auth.h
+++ b/src/ap/ieee802_11_auth.h
@@ -23,7 +23,8 @@
 			    u32 *acct_interim_interval,
 			    struct vlan_description *vlan_id,
 			    struct hostapd_sta_wpa_psk_short **psk,
-			    char **identity, char **radius_cui);
+			    char **identity, char **radius_cui,
+			    int is_probe_req);
 int hostapd_acl_init(struct hostapd_data *hapd);
 void hostapd_acl_deinit(struct hostapd_data *hapd);
 void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk);
diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c
index 146e447..214855d 100644
--- a/src/ap/ieee802_11_ht.c
+++ b/src/ap/ieee802_11_ht.c
@@ -236,17 +236,29 @@
 	int i;
 	const u8 *start = (const u8 *) mgmt;
 	const u8 *data = start + IEEE80211_HDRLEN + 2;
+	struct sta_info *sta;
+
+	wpa_printf(MSG_DEBUG,
+		   "HT: Received 20/40 BSS Coexistence Management frame from "
+		   MACSTR, MAC2STR(mgmt->sa));
 
 	hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
 		       HOSTAPD_LEVEL_DEBUG, "hostapd_public_action - action=%d",
 		       mgmt->u.action.u.public_action.action);
 
-	if (!(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
+	if (!(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) {
+		wpa_printf(MSG_DEBUG,
+			   "Ignore 20/40 BSS Coexistence Management frame since 40 MHz capability is not enabled");
 		return;
+	}
 
-	if (len < IEEE80211_HDRLEN + 2 + sizeof(*bc_ie))
+	if (len < IEEE80211_HDRLEN + 2 + sizeof(*bc_ie)) {
+		wpa_printf(MSG_DEBUG,
+			   "Ignore too short 20/40 BSS Coexistence Management frame");
 		return;
+	}
 
+	/* 20/40 BSS Coexistence element */
 	bc_ie = (struct ieee80211_2040_bss_coex_ie *) data;
 	if (bc_ie->element_id != WLAN_EID_20_40_BSS_COEXISTENCE ||
 	    bc_ie->length < 1) {
@@ -254,13 +266,35 @@
 			   bc_ie->element_id, bc_ie->length);
 		return;
 	}
-	if (len < IEEE80211_HDRLEN + 2 + 2 + bc_ie->length)
+	if (len < IEEE80211_HDRLEN + 2 + 2 + bc_ie->length) {
+		wpa_printf(MSG_DEBUG,
+			   "Truncated 20/40 BSS Coexistence element");
 		return;
+	}
 	data += 2 + bc_ie->length;
 
-	wpa_printf(MSG_DEBUG, "20/40 BSS Coexistence Information field: 0x%x",
-		   bc_ie->coex_param);
+	wpa_printf(MSG_DEBUG,
+		   "20/40 BSS Coexistence Information field: 0x%x (%s%s%s%s%s%s)",
+		   bc_ie->coex_param,
+		   (bc_ie->coex_param & BIT(0)) ? "[InfoReq]" : "",
+		   (bc_ie->coex_param & BIT(1)) ? "[40MHzIntolerant]" : "",
+		   (bc_ie->coex_param & BIT(2)) ? "[20MHzBSSWidthReq]" : "",
+		   (bc_ie->coex_param & BIT(3)) ? "[OBSSScanExemptionReq]" : "",
+		   (bc_ie->coex_param & BIT(4)) ?
+		   "[OBSSScanExemptionGrant]" : "",
+		   (bc_ie->coex_param & (BIT(5) | BIT(6) | BIT(7))) ?
+		   "[Reserved]" : "");
+
 	if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ) {
+		/* Intra-BSS communication prohibiting 20/40 MHz BSS operation
+		 */
+		sta = ap_get_sta(hapd, mgmt->sa);
+		if (!sta || !(sta->flags & WLAN_STA_ASSOC)) {
+			wpa_printf(MSG_DEBUG,
+				   "Ignore intra-BSS 20/40 BSS Coexistence Management frame from not-associated STA");
+			return;
+		}
+
 		hostapd_logger(hapd, mgmt->sa,
 			       HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_DEBUG,
@@ -269,6 +303,8 @@
 	}
 
 	if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_40MHZ_INTOL) {
+		/* Inter-BSS communication prohibiting 20/40 MHz BSS operation
+		 */
 		hostapd_logger(hapd, mgmt->sa,
 			       HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_DEBUG,
@@ -276,12 +312,16 @@
 		is_ht40_allowed = 0;
 	}
 
-	if (start + len - data >= 3 &&
-	    data[0] == WLAN_EID_20_40_BSS_INTOLERANT && data[1] >= 1) {
+	/* 20/40 BSS Intolerant Channel Report element (zero or more times) */
+	while (start + len - data >= 3 &&
+	       data[0] == WLAN_EID_20_40_BSS_INTOLERANT && data[1] >= 1) {
 		u8 ielen = data[1];
 
-		if (ielen > start + len - data - 2)
+		if (ielen > start + len - data - 2) {
+			wpa_printf(MSG_DEBUG,
+				   "Truncated 20/40 BSS Intolerant Channel Report element");
 			return;
+		}
 		ic_report = (struct ieee80211_2040_intol_chan_report *) data;
 		wpa_printf(MSG_DEBUG,
 			   "20/40 BSS Intolerant Channel Report: Operating Class %u",
@@ -292,8 +332,10 @@
 		for (i = 0; i < ielen - 1; i++) {
 			u8 chan = ic_report->variable[i];
 
+			if (chan == iface->conf->channel)
+				continue; /* matching own primary channel */
 			if (is_40_allowed(iface, chan))
-				continue;
+				continue; /* not within affected channels */
 			hostapd_logger(hapd, mgmt->sa,
 				       HOSTAPD_MODULE_IEEE80211,
 				       HOSTAPD_LEVEL_DEBUG,
@@ -301,6 +343,8 @@
 				       chan);
 			is_ht40_allowed = 0;
 		}
+
+		data += 2 + ielen;
 	}
 	wpa_printf(MSG_DEBUG, "is_ht40_allowed=%d num_sta_ht40_intolerant=%d",
 		   is_ht40_allowed, iface->num_sta_ht40_intolerant);
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index 902f64f..a3f8609 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -225,6 +225,7 @@
 			*pos |= 0x40; /* Bit 70 - FTM responder */
 		if (hapd->conf->ftm_initiator)
 			*pos |= 0x80; /* Bit 71 - FTM initiator */
+		break;
 	case 9: /* Bits 72-79 */
 #ifdef CONFIG_FILS
 		if ((hapd->conf->wpa & WPA_PROTO_RSN) &&
@@ -445,7 +446,7 @@
 {
 	size_t len;
 
-	if (hapd->conf->time_advertisement != 2)
+	if (hapd->conf->time_advertisement != 2 || !hapd->conf->time_zone)
 		return eid;
 
 	len = os_strlen(hapd->conf->time_zone);
@@ -607,6 +608,67 @@
 #endif /* CONFIG_MBO */
 
 
+#ifdef CONFIG_OWE
+static int hostapd_eid_owe_trans_enabled(struct hostapd_data *hapd)
+{
+	return hapd->conf->owe_transition_ssid_len > 0 &&
+		!is_zero_ether_addr(hapd->conf->owe_transition_bssid);
+}
+#endif /* CONFIG_OWE */
+
+
+size_t hostapd_eid_owe_trans_len(struct hostapd_data *hapd)
+{
+#ifdef CONFIG_OWE
+	if (!hostapd_eid_owe_trans_enabled(hapd))
+		return 0;
+	return 6 + ETH_ALEN + 1 + hapd->conf->owe_transition_ssid_len;
+#else /* CONFIG_OWE */
+	return 0;
+#endif /* CONFIG_OWE */
+}
+
+
+u8 * hostapd_eid_owe_trans(struct hostapd_data *hapd, u8 *eid,
+				  size_t len)
+{
+#ifdef CONFIG_OWE
+	u8 *pos = eid;
+	size_t elen;
+
+	if (hapd->conf->owe_transition_ifname[0] &&
+	    !hostapd_eid_owe_trans_enabled(hapd))
+		hostapd_owe_trans_get_info(hapd);
+
+	if (!hostapd_eid_owe_trans_enabled(hapd))
+		return pos;
+
+	elen = hostapd_eid_owe_trans_len(hapd);
+	if (len < elen) {
+		wpa_printf(MSG_DEBUG,
+			   "OWE: Not enough room in the buffer for OWE IE");
+		return pos;
+	}
+
+	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
+	*pos++ = elen - 2;
+	WPA_PUT_BE24(pos, OUI_WFA);
+	pos += 3;
+	*pos++ = OWE_OUI_TYPE;
+	os_memcpy(pos, hapd->conf->owe_transition_bssid, ETH_ALEN);
+	pos += ETH_ALEN;
+	*pos++ = hapd->conf->owe_transition_ssid_len;
+	os_memcpy(pos, hapd->conf->owe_transition_ssid,
+		  hapd->conf->owe_transition_ssid_len);
+	pos += hapd->conf->owe_transition_ssid_len;
+
+	return pos;
+#else /* CONFIG_OWE */
+	return eid;
+#endif /* CONFIG_OWE */
+}
+
+
 void ap_copy_sta_supp_op_classes(struct sta_info *sta,
 				 const u8 *supp_op_classes,
 				 size_t supp_op_classes_len)
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index 793d381..985f8b7 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -712,6 +712,41 @@
 				goto fail;
 			}
 		}
+
+		if (sta->roaming_consortium &&
+		    !radius_msg_add_wfa(
+			    msg, RADIUS_VENDOR_ATTR_WFA_HS20_ROAMING_CONSORTIUM,
+			    wpabuf_head(sta->roaming_consortium),
+			    wpabuf_len(sta->roaming_consortium))) {
+			wpa_printf(MSG_ERROR,
+				   "Could not add HS 2.0 Roaming Consortium");
+			goto fail;
+		}
+
+		if (hapd->conf->t_c_filename) {
+			be32 timestamp;
+
+			if (!radius_msg_add_wfa(
+				    msg,
+				    RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILENAME,
+				    (const u8 *) hapd->conf->t_c_filename,
+				    os_strlen(hapd->conf->t_c_filename))) {
+				wpa_printf(MSG_ERROR,
+					   "Could not add HS 2.0 T&C Filename");
+				goto fail;
+			}
+
+			timestamp = host_to_be32(hapd->conf->t_c_timestamp);
+			if (!radius_msg_add_wfa(
+				    msg,
+				    RADIUS_VENDOR_ATTR_WFA_HS20_TIMESTAMP,
+				    (const u8 *) &timestamp,
+				    sizeof(timestamp))) {
+				wpa_printf(MSG_ERROR,
+					   "Could not add HS 2.0 Timestamp");
+				goto fail;
+			}
+		}
 	}
 #endif /* CONFIG_HS20 */
 
@@ -1177,7 +1212,7 @@
 		sta->eapol_sm->portValid = TRUE;
 		if (sta->eapol_sm->eap)
 			eap_sm_notify_cached(sta->eapol_sm->eap);
-		/* TODO: get vlan_id from R0KH using RRB message */
+		ap_sta_bind_vlan(hapd, sta);
 		return;
 	}
 #endif /* CONFIG_IEEE80211R_AP */
@@ -1587,6 +1622,33 @@
 	ap_sta_session_warning_timeout(hapd, sta, warning_time);
 }
 
+
+static void ieee802_1x_hs20_t_c_filtering(struct hostapd_data *hapd,
+					  struct sta_info *sta, u8 *pos,
+					  size_t len)
+{
+	if (len < 4)
+		return; /* Malformed information */
+	wpa_printf(MSG_DEBUG,
+		   "HS 2.0: Terms and Conditions filtering %02x %02x %02x %02x",
+		   pos[0], pos[1], pos[2], pos[3]);
+	hs20_t_c_filtering(hapd, sta, pos[0] & BIT(0));
+}
+
+
+static void ieee802_1x_hs20_t_c_url(struct hostapd_data *hapd,
+				    struct sta_info *sta, u8 *pos, size_t len)
+{
+	os_free(sta->t_c_url);
+	sta->t_c_url = os_malloc(len + 1);
+	if (!sta->t_c_url)
+		return;
+	os_memcpy(sta->t_c_url, pos, len);
+	sta->t_c_url[len] = '\0';
+	wpa_printf(MSG_DEBUG,
+		   "HS 2.0: Terms and Conditions URL %s", sta->t_c_url);
+}
+
 #endif /* CONFIG_HS20 */
 
 
@@ -1634,6 +1696,12 @@
 			ieee802_1x_hs20_session_info(hapd, sta, pos, sublen,
 						     session_timeout);
 			break;
+		case RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING:
+			ieee802_1x_hs20_t_c_filtering(hapd, sta, pos, sublen);
+			break;
+		case RADIUS_VENDOR_ATTR_WFA_HS20_T_C_URL:
+			ieee802_1x_hs20_t_c_url(hapd, sta, pos, sublen);
+			break;
 		}
 	}
 #endif /* CONFIG_HS20 */
@@ -1691,6 +1759,7 @@
 	struct sta_info *sta;
 	u32 session_timeout = 0, termination_action, acct_interim_interval;
 	int session_timeout_set;
+	u32 reason_code;
 	struct eapol_state_machine *sm;
 	int override_eapReq = 0;
 	struct radius_hdr *hdr = radius_msg_get_hdr(msg);
@@ -1816,14 +1885,17 @@
 			break;
 
 		sta->session_timeout_set = !!session_timeout_set;
-		sta->session_timeout = session_timeout;
+		os_get_reltime(&sta->session_timeout);
+		sta->session_timeout.sec += session_timeout;
 
 		/* RFC 3580, Ch. 3.17 */
 		if (session_timeout_set && termination_action ==
-		    RADIUS_TERMINATION_ACTION_RADIUS_REQUEST) {
+		    RADIUS_TERMINATION_ACTION_RADIUS_REQUEST)
 			sm->reAuthPeriod = session_timeout;
-		} else if (session_timeout_set)
+		else if (session_timeout_set)
 			ap_sta_session_timeout(hapd, sta, session_timeout);
+		else
+			ap_sta_no_session_timeout(hapd, sta);
 
 		sm->eap_if->aaaSuccess = TRUE;
 		override_eapReq = 1;
@@ -1839,6 +1911,13 @@
 	case RADIUS_CODE_ACCESS_REJECT:
 		sm->eap_if->aaaFail = TRUE;
 		override_eapReq = 1;
+		if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_WLAN_REASON_CODE,
+					      &reason_code) == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "RADIUS server indicated WLAN-Reason-Code %u in Access-Reject for "
+				   MACSTR, reason_code, MAC2STR(sta->addr));
+			sta->disconnect_reason_code = reason_code;
+		}
 		break;
 	case RADIUS_CODE_ACCESS_CHALLENGE:
 		sm->eap_if->aaaEapReq = TRUE;
@@ -2081,6 +2160,13 @@
 			goto out;
 		user->password_len = eap_user->password_len;
 		user->password_hash = eap_user->password_hash;
+		if (eap_user->salt && eap_user->salt_len) {
+			user->salt = os_memdup(eap_user->salt,
+					       eap_user->salt_len);
+			if (!user->salt)
+				goto out;
+			user->salt_len = eap_user->salt_len;
+		}
 	}
 	user->force_version = eap_user->force_version;
 	user->macacl = eap_user->macacl;
@@ -2693,6 +2779,15 @@
 		hs20_send_wnm_notification_deauth_req(hapd, sta->addr,
 						      sta->hs20_deauth_req);
 	}
+
+	if (sta->hs20_t_c_filtering) {
+		wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to "
+			   MACSTR " to indicate Terms and Conditions filtering",
+			   MAC2STR(sta->addr));
+		hs20_send_wnm_notification_t_c(hapd, sta->addr, sta->t_c_url);
+		os_free(sta->t_c_url);
+		sta->t_c_url = NULL;
+	}
 }
 #endif /* CONFIG_HS20 */
 
@@ -2706,6 +2801,7 @@
 	/* TODO: get PMKLifetime from WPA parameters */
 	static const int dot11RSNAConfigPMKLifetime = 43200;
 	unsigned int session_timeout;
+	struct os_reltime now, remaining;
 
 #ifdef CONFIG_HS20
 	if (remediation && !sta->remediation) {
@@ -2716,7 +2812,8 @@
 		sta->remediation_method = 1; /* SOAP-XML SPP */
 	}
 
-	if (success && (sta->remediation || sta->hs20_deauth_req)) {
+	if (success && (sta->remediation || sta->hs20_deauth_req ||
+			sta->hs20_t_c_filtering)) {
 		wpa_printf(MSG_DEBUG, "HS 2.0: Schedule WNM-Notification to "
 			   MACSTR " in 100 ms", MAC2STR(sta->addr));
 		eloop_cancel_timeout(ieee802_1x_wnm_notif_send, hapd, sta);
@@ -2726,10 +2823,13 @@
 #endif /* CONFIG_HS20 */
 
 	key = ieee802_1x_get_key(sta->eapol_sm, &len);
-	if (sta->session_timeout_set)
-		session_timeout = sta->session_timeout;
-	else
+	if (sta->session_timeout_set) {
+		os_get_reltime(&now);
+		os_reltime_sub(&sta->session_timeout, &now, &remaining);
+		session_timeout = (remaining.sec > 0) ? remaining.sec : 1;
+	} else {
 		session_timeout = dot11RSNAConfigPMKLifetime;
+	}
 	if (success && key && len >= PMK_LEN && !sta->remediation &&
 	    !sta->hs20_deauth_requested &&
 	    wpa_auth_pmksa_add(sta->wpa_sm, key, len, session_timeout,
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index b1fde3c..179cf43 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -197,7 +197,8 @@
 	if (sta->no_short_slot_time_set) {
 		sta->no_short_slot_time_set = 0;
 		hapd->iface->num_sta_no_short_slot_time--;
-		if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
+		if (hapd->iface->current_mode &&
+		    hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
 		    && hapd->iface->num_sta_no_short_slot_time == 0)
 			set_beacon++;
 	}
@@ -205,7 +206,8 @@
 	if (sta->no_short_preamble_set) {
 		sta->no_short_preamble_set = 0;
 		hapd->iface->num_sta_no_short_preamble--;
-		if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
+		if (hapd->iface->current_mode &&
+		    hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
 		    && hapd->iface->num_sta_no_short_preamble == 0)
 			set_beacon++;
 	}
@@ -319,6 +321,7 @@
 	wpabuf_free(sta->wps_ie);
 	wpabuf_free(sta->p2p_ie);
 	wpabuf_free(sta->hs20_ie);
+	wpabuf_free(sta->roaming_consortium);
 #ifdef CONFIG_FST
 	wpabuf_free(sta->mb_ies);
 #endif /* CONFIG_FST */
@@ -329,6 +332,7 @@
 	os_free(sta->identity);
 	os_free(sta->radius_cui);
 	os_free(sta->remediation_url);
+	os_free(sta->t_c_url);
 	wpabuf_free(sta->hs20_deauth_req);
 	os_free(sta->hs20_session_info_url);
 
@@ -357,6 +361,14 @@
 	crypto_ecdh_deinit(sta->owe_ecdh);
 #endif /* CONFIG_OWE */
 
+	os_free(sta->ext_capability);
+
+#ifdef CONFIG_WNM_AP
+	eloop_cancel_timeout(ap_sta_reset_steer_flag_timer, hapd, sta);
+#endif /* CONFIG_WNM_AP */
+
+	os_free(sta->ifname_wds);
+
 	os_free(sta);
 }
 
@@ -1342,7 +1354,7 @@
 	int res;
 
 	buf[0] = '\0';
-	res = os_snprintf(buf, buflen, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+	res = os_snprintf(buf, buflen, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
 			  (flags & WLAN_STA_AUTH ? "[AUTH]" : ""),
 			  (flags & WLAN_STA_ASSOC ? "[ASSOC]" : ""),
 			  (flags & WLAN_STA_AUTHORIZED ? "[AUTHORIZED]" : ""),
@@ -1359,6 +1371,7 @@
 			  (flags & WLAN_STA_NONERP ? "[NonERP]" : ""),
 			  (flags & WLAN_STA_WPS2 ? "[WPS2]" : ""),
 			  (flags & WLAN_STA_GAS ? "[GAS]" : ""),
+			  (flags & WLAN_STA_HT ? "[HT]" : ""),
 			  (flags & WLAN_STA_VHT ? "[VHT]" : ""),
 			  (flags & WLAN_STA_VENDOR_VHT ? "[VENDOR_VHT]" : ""),
 			  (flags & WLAN_STA_WNM_SLEEP_MODE ?
@@ -1374,13 +1387,16 @@
 {
 	struct hostapd_data *hapd = eloop_ctx;
 	struct sta_info *sta = timeout_ctx;
+	u16 reason;
 
 	wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
 		"IEEE 802.1X: Scheduled disconnection of " MACSTR
 		" after EAP-Failure", MAC2STR(sta->addr));
 
-	ap_sta_disconnect(hapd, sta, sta->addr,
-			  WLAN_REASON_IEEE_802_1X_AUTH_FAILED);
+	reason = sta->disconnect_reason_code;
+	if (!reason)
+		reason = WLAN_REASON_IEEE_802_1X_AUTH_FAILED;
+	ap_sta_disconnect(hapd, sta, sta->addr, reason);
 	if (sta->flags & WLAN_STA_WPS)
 		hostapd_wps_eap_completed(hapd);
 }
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 3fb60f6..9cac6f1 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -9,11 +9,7 @@
 #ifndef STA_INFO_H
 #define STA_INFO_H
 
-#ifdef CONFIG_MESH
-/* needed for mesh_plink_state enum */
 #include "common/defs.h"
-#endif /* CONFIG_MESH */
-
 #include "list.h"
 #include "vlan.h"
 #include "common/wpa_common.h"
@@ -71,6 +67,7 @@
 	be32 ipaddr;
 	struct dl_list ip6addr; /* list head for struct ip6addr */
 	u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */
+	u16 disconnect_reason_code; /* RADIUS server override */
 	u32 flags; /* Bitfield of WLAN_STA_* */
 	u16 capability;
 	u16 listen_interval; /* or beacon_int for APs */
@@ -117,6 +114,9 @@
 	unsigned int ecsa_supported:1;
 	unsigned int added_unassoc:1;
 	unsigned int pending_wds_enable:1;
+	unsigned int power_capab:1;
+	unsigned int agreed_to_steer:1;
+	unsigned int hs20_t_c_filtering:1;
 
 	u16 auth_alg;
 
@@ -183,8 +183,11 @@
 	struct wpabuf *wps_ie; /* WPS IE from (Re)Association Request */
 	struct wpabuf *p2p_ie; /* P2P IE from (Re)Association Request */
 	struct wpabuf *hs20_ie; /* HS 2.0 IE from (Re)Association Request */
+	/* Hotspot 2.0 Roaming Consortium from (Re)Association Request */
+	struct wpabuf *roaming_consortium;
 	u8 remediation_method;
 	char *remediation_url; /* HS 2.0 Subscription Remediation Server URL */
+	char *t_c_url; /* HS 2.0 Terms and Conditions Server URL */
 	struct wpabuf *hs20_deauth_req;
 	char *hs20_session_info_url;
 	int hs20_disassoc_timer;
@@ -199,7 +202,8 @@
 	unsigned int mesh_sae_pmksa_caching:1;
 #endif /* CONFIG_SAE */
 
-	u32 session_timeout; /* valid only if session_timeout_set == 1 */
+	/* valid only if session_timeout_set == 1 */
+	struct os_reltime session_timeout;
 
 	/* Last Authentication/(Re)Association Request/Action frame sequence
 	 * control */
@@ -218,6 +222,9 @@
 
 	u8 rrm_enabled_capa[5];
 
+	s8 min_tx_power;
+	s8 max_tx_power;
+
 #ifdef CONFIG_TAXONOMY
 	struct wpabuf *probe_ie_taxonomy;
 	struct wpabuf *assoc_ie_taxonomy;
@@ -251,6 +258,9 @@
 	u16 owe_group;
 #endif /* CONFIG_OWE */
 
+	u8 *ext_capability;
+	char *ifname_wds; /* WDS ifname, if in use */
+
 #ifdef CONFIG_TESTING_OPTIONS
 	enum wpa_alg last_tk_alg;
 	int last_tk_key_idx;
diff --git a/src/ap/tkip_countermeasures.c b/src/ap/tkip_countermeasures.c
index 4725e2b..557570c 100644
--- a/src/ap/tkip_countermeasures.c
+++ b/src/ap/tkip_countermeasures.c
@@ -71,6 +71,11 @@
 	struct os_reltime now;
 	int ret = 0;
 
+	hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_INFO,
+		       "Michael MIC failure detected in received frame%s",
+		       local ? " (local)" : "");
+
 	if (addr && local) {
 		struct sta_info *sta = ap_get_sta(hapd, addr);
 		if (sta != NULL) {
diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
index 7c4fde0..710fe50 100644
--- a/src/ap/wnm_ap.c
+++ b/src/ap/wnm_ap.c
@@ -109,6 +109,7 @@
 	pos = (u8 *)mgmt->u.action.u.wnm_sleep_resp.variable;
 	/* add key data if MFP is enabled */
 	if (!wpa_auth_uses_mfp(sta->wpa_sm) ||
+	    hapd->conf->wnm_sleep_mode_no_keys ||
 	    action_type != WNM_SLEEP_MODE_EXIT) {
 		mgmt->u.action.u.wnm_sleep_resp.keydata_len = 0;
 	} else {
@@ -173,7 +174,8 @@
 			wpa_set_wnmsleep(sta->wpa_sm, 0);
 			hostapd_drv_wnm_oper(hapd, WNM_SLEEP_EXIT_CONFIRM,
 					     addr, NULL, NULL);
-			if (!wpa_auth_uses_mfp(sta->wpa_sm))
+			if (!wpa_auth_uses_mfp(sta->wpa_sm) ||
+			    hapd->conf->wnm_sleep_mode_no_keys)
 				wpa_wnmsleep_rekey_gtk(sta->wpa_sm);
 		}
 	} else
@@ -200,6 +202,13 @@
 	u8 *tfsreq_ie_end = NULL;
 	u16 tfsreq_ie_len = 0;
 
+	if (!hapd->conf->wnm_sleep_mode) {
+		wpa_printf(MSG_DEBUG, "Ignore WNM-Sleep Mode Request from "
+			   MACSTR " since WNM-Sleep Mode is disabled",
+			   MAC2STR(addr));
+		return;
+	}
+
 	dialog_token = *pos++;
 	while (pos + 1 < frm + len) {
 		u8 ie_len = pos[1];
@@ -295,6 +304,20 @@
 {
 	u8 dialog_token, reason;
 	const u8 *pos, *end;
+	int enabled = hapd->conf->bss_transition;
+
+#ifdef CONFIG_MBO
+	if (hapd->conf->mbo_enabled)
+		enabled = 1;
+#endif /* CONFIG_MBO */
+	if (!enabled) {
+		wpa_printf(MSG_DEBUG,
+			   "Ignore BSS Transition Management Query from "
+			   MACSTR
+			   " since BSS Transition Management is disabled",
+			   MAC2STR(addr));
+		return;
+	}
 
 	if (len < 2) {
 		wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Query from "
@@ -318,12 +341,40 @@
 }
 
 
+void ap_sta_reset_steer_flag_timer(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct sta_info *sta = timeout_ctx;
+
+	if (sta->agreed_to_steer) {
+		wpa_printf(MSG_DEBUG, "%s: Reset steering flag for STA " MACSTR,
+			   hapd->conf->iface, MAC2STR(sta->addr));
+		sta->agreed_to_steer = 0;
+	}
+}
+
+
 static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd,
 					      const u8 *addr, const u8 *frm,
 					      size_t len)
 {
 	u8 dialog_token, status_code, bss_termination_delay;
 	const u8 *pos, *end;
+	int enabled = hapd->conf->bss_transition;
+	struct sta_info *sta;
+
+#ifdef CONFIG_MBO
+	if (hapd->conf->mbo_enabled)
+		enabled = 1;
+#endif /* CONFIG_MBO */
+	if (!enabled) {
+		wpa_printf(MSG_DEBUG,
+			   "Ignore BSS Transition Management Response from "
+			   MACSTR
+			   " since BSS Transition Management is disabled",
+			   MAC2STR(addr));
+		return;
+	}
 
 	if (len < 3) {
 		wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Response from "
@@ -342,11 +393,23 @@
 		   "bss_termination_delay=%u", MAC2STR(addr), dialog_token,
 		   status_code, bss_termination_delay);
 
+	sta = ap_get_sta(hapd, addr);
+	if (!sta) {
+		wpa_printf(MSG_DEBUG, "Station " MACSTR
+			   " not found for received BSS TM Response",
+			   MAC2STR(addr));
+		return;
+	}
+
 	if (status_code == WNM_BSS_TM_ACCEPT) {
 		if (end - pos < ETH_ALEN) {
 			wpa_printf(MSG_DEBUG, "WNM: not enough room for Target BSSID field");
 			return;
 		}
+		sta->agreed_to_steer = 1;
+		eloop_cancel_timeout(ap_sta_reset_steer_flag_timer, hapd, sta);
+		eloop_register_timeout(2, 0, ap_sta_reset_steer_flag_timer,
+				       hapd, sta);
 		wpa_printf(MSG_DEBUG, "WNM: Target BSSID: " MACSTR,
 			   MAC2STR(pos));
 		wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR
@@ -356,6 +419,7 @@
 			MAC2STR(pos));
 		pos += ETH_ALEN;
 	} else {
+		sta->agreed_to_steer = 0;
 		wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR
 			" status_code=%u bss_termination_delay=%u",
 			MAC2STR(addr), status_code, bss_termination_delay);
diff --git a/src/ap/wnm_ap.h b/src/ap/wnm_ap.h
index a44eadb..56d0f88 100644
--- a/src/ap/wnm_ap.h
+++ b/src/ap/wnm_ap.h
@@ -23,5 +23,6 @@
 			const u8 *bss_term_dur, const char *url,
 			const u8 *nei_rep, size_t nei_rep_len,
 			const u8 *mbo_attrs, size_t mbo_len);
+void ap_sta_reset_steer_flag_timer(void *eloop_ctx, void *timeout_ctx);
 
 #endif /* WNM_AP_H */
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 8265fa1..126d98c 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -1,6 +1,6 @@
 /*
  * IEEE 802.11 RSN / WPA Authenticator
- * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2018, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -19,6 +19,7 @@
 #include "crypto/crypto.h"
 #include "crypto/sha1.h"
 #include "crypto/sha256.h"
+#include "crypto/sha384.h"
 #include "crypto/random.h"
 #include "eapol_auth/eapol_auth_sm.h"
 #include "ap_config.h"
@@ -237,23 +238,6 @@
 }
 
 
-static int wpa_use_aes_cmac(struct wpa_state_machine *sm)
-{
-	int ret = 0;
-#ifdef CONFIG_IEEE80211R_AP
-	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
-		ret = 1;
-#endif /* CONFIG_IEEE80211R_AP */
-#ifdef CONFIG_IEEE80211W
-	if (wpa_key_mgmt_sha256(sm->wpa_key_mgmt))
-		ret = 1;
-#endif /* CONFIG_IEEE80211W */
-	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN)
-		ret = 1;
-	return ret;
-}
-
-
 static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx)
 {
 	struct wpa_authenticator *wpa_auth = eloop_ctx;
@@ -698,9 +682,10 @@
 		wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
 				"strict rekeying - force GTK rekey since STA "
 				"is leaving");
-		eloop_cancel_timeout(wpa_rekey_gtk, sm->wpa_auth, NULL);
-		eloop_register_timeout(0, 500000, wpa_rekey_gtk, sm->wpa_auth,
-				       NULL);
+		if (eloop_deplete_timeout(0, 500000, wpa_rekey_gtk,
+					  sm->wpa_auth, NULL) == -1)
+			eloop_register_timeout(0, 500000, wpa_rekey_gtk, sm->wpa_auth,
+					       NULL);
 	}
 
 	eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm);
@@ -859,6 +844,12 @@
 					       sm->p2p_dev_addr, pmk, &pmk_len);
 			if (pmk == NULL)
 				break;
+#ifdef CONFIG_IEEE80211R_AP
+			if (wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) {
+				os_memcpy(sm->xxkey, pmk, pmk_len);
+				sm->xxkey_len = pmk_len;
+			}
+#endif /* CONFIG_IEEE80211R_AP */
 		} else {
 			pmk = sm->PMK;
 			pmk_len = sm->pmk_len;
@@ -873,7 +864,8 @@
 			break;
 		}
 
-		if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt))
+		if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
+		    wpa_key_mgmt_sae(sm->wpa_key_mgmt))
 			break;
 	}
 
@@ -1001,10 +993,8 @@
 		u16 ver = key_info & WPA_KEY_INFO_TYPE_MASK;
 		if (sm->pairwise == WPA_CIPHER_CCMP ||
 		    sm->pairwise == WPA_CIPHER_GCMP) {
-			if (wpa_use_aes_cmac(sm) &&
-			    sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN &&
-			    !wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) &&
-			    !wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
+			if (wpa_use_cmac(sm->wpa_key_mgmt) &&
+			    !wpa_use_akm_defined(sm->wpa_key_mgmt) &&
 			    ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
 				wpa_auth_logger(wpa_auth, sm->addr,
 						LOGGER_WARNING,
@@ -1014,11 +1004,8 @@
 				return;
 			}
 
-			if (!wpa_use_aes_cmac(sm) &&
-			    !wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) &&
-			    !wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
-			    sm->wpa_key_mgmt != WPA_KEY_MGMT_OWE &&
-			    sm->wpa_key_mgmt != WPA_KEY_MGMT_DPP &&
+			if (!wpa_use_cmac(sm->wpa_key_mgmt) &&
+			    !wpa_use_akm_defined(sm->wpa_key_mgmt) &&
 			    ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
 				wpa_auth_logger(wpa_auth, sm->addr,
 						LOGGER_WARNING,
@@ -1028,10 +1015,7 @@
 			}
 		}
 
-		if ((wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) ||
-		     wpa_key_mgmt_fils(sm->wpa_key_mgmt) ||
-		     sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP ||
-		     sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE) &&
+		if (wpa_use_akm_defined(sm->wpa_key_mgmt) &&
 		    ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
 			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING,
 					"did not use EAPOL-Key descriptor version 0 as required for AKM-defined cases");
@@ -1317,7 +1301,7 @@
 static int wpa_gmk_to_gtk(const u8 *gmk, const char *label, const u8 *addr,
 			  const u8 *gnonce, u8 *gtk, size_t gtk_len)
 {
-	u8 data[ETH_ALEN + WPA_NONCE_LEN + 8 + 16];
+	u8 data[ETH_ALEN + WPA_NONCE_LEN + 8 + WPA_GTK_MAX_LEN];
 	u8 *pos;
 	int ret = 0;
 
@@ -1328,21 +1312,30 @@
 	 * is done only at the Authenticator and as such, does not need to be
 	 * exactly same.
 	 */
+	os_memset(data, 0, sizeof(data));
 	os_memcpy(data, addr, ETH_ALEN);
 	os_memcpy(data + ETH_ALEN, gnonce, WPA_NONCE_LEN);
 	pos = data + ETH_ALEN + WPA_NONCE_LEN;
 	wpa_get_ntp_timestamp(pos);
 	pos += 8;
-	if (random_get_bytes(pos, 16) < 0)
+	if (random_get_bytes(pos, gtk_len) < 0)
 		ret = -1;
 
-#ifdef CONFIG_IEEE80211W
-	sha256_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len);
-#else /* CONFIG_IEEE80211W */
-	if (sha1_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len)
-	    < 0)
+#ifdef CONFIG_SHA384
+	if (sha384_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data),
+		       gtk, gtk_len) < 0)
 		ret = -1;
-#endif /* CONFIG_IEEE80211W */
+#else /* CONFIG_SHA384 */
+#ifdef CONFIG_SHA256
+	if (sha256_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data),
+		       gtk, gtk_len) < 0)
+		ret = -1;
+#else /* CONFIG_SHA256 */
+	if (sha1_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data),
+		     gtk, gtk_len) < 0)
+		ret = -1;
+#endif /* CONFIG_SHA256 */
+#endif /* CONFIG_SHA384 */
 
 	return ret;
 }
@@ -1383,13 +1376,9 @@
 
 	if (force_version)
 		version = force_version;
-	else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
-		 sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE ||
-		 sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP ||
-		 wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) ||
-		 wpa_key_mgmt_fils(sm->wpa_key_mgmt))
+	else if (wpa_use_akm_defined(sm->wpa_key_mgmt))
 		version = WPA_KEY_INFO_TYPE_AKM_DEFINED;
-	else if (wpa_use_aes_cmac(sm))
+	else if (wpa_use_cmac(sm->wpa_key_mgmt))
 		version = WPA_KEY_INFO_TYPE_AES_128_CMAC;
 	else if (sm->pairwise != WPA_CIPHER_TKIP)
 		version = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
@@ -1411,10 +1400,7 @@
 	key_data_len = kde_len;
 
 	if ((version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
-	     sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE ||
-	     sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP ||
-	     sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
-	     wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) ||
+	     wpa_use_aes_key_wrap(sm->wpa_key_mgmt) ||
 	     version == WPA_KEY_INFO_TYPE_AES_128_CMAC) && encr) {
 		pad_len = key_data_len % 8;
 		if (pad_len)
@@ -1474,7 +1460,7 @@
 		os_memcpy(key_data, kde, kde_len);
 		WPA_PUT_BE16(key_mic + mic_len, kde_len);
 #ifdef CONFIG_FILS
-	} else if (!mic_len) {
+	} else if (!mic_len && kde) {
 		const u8 *aad[1];
 		size_t aad_len[1];
 
@@ -1513,10 +1499,7 @@
 		wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data",
 				buf, key_data_len);
 		if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
-		    sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE ||
-		    sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP ||
-		    sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
-		    wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) ||
+		    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 %u)",
@@ -1560,9 +1543,12 @@
 			return;
 		}
 
-		wpa_eapol_key_mic(sm->PTK.kck, sm->PTK.kck_len,
-				  sm->wpa_key_mgmt, version,
-				  (u8 *) hdr, len, key_mic);
+		if (wpa_eapol_key_mic(sm->PTK.kck, sm->PTK.kck_len,
+				      sm->wpa_key_mgmt, version,
+				      (u8 *) hdr, len, key_mic) < 0) {
+			os_free(hdr);
+			return;
+		}
 #ifdef CONFIG_TESTING_OPTIONS
 		if (!pairwise &&
 		    wpa_auth->conf.corrupt_gtk_rekey_mic_probability > 0.0 &&
@@ -1963,8 +1949,13 @@
 		sm->pmk_len = pmk_len;
 #ifdef CONFIG_IEEE80211R_AP
 		if (len >= 2 * PMK_LEN) {
-			os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN);
-			sm->xxkey_len = PMK_LEN;
+			if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) {
+				os_memcpy(sm->xxkey, msk, SHA384_MAC_LEN);
+				sm->xxkey_len = SHA384_MAC_LEN;
+			} else {
+				os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN);
+				sm->xxkey_len = PMK_LEN;
+			}
 		}
 #endif /* CONFIG_IEEE80211R_AP */
 	} else {
@@ -2049,11 +2040,31 @@
 		pmkid[1] = RSN_SELECTOR_LEN + PMKID_LEN;
 		RSN_SELECTOR_PUT(&pmkid[2], RSN_KEY_DATA_PMKID);
 		if (sm->pmksa) {
+			wpa_hexdump(MSG_DEBUG,
+				    "RSN: Message 1/4 PMKID from PMKSA entry",
+				    sm->pmksa->pmkid, PMKID_LEN);
 			os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN],
 				  sm->pmksa->pmkid, PMKID_LEN);
 		} else if (wpa_key_mgmt_suite_b(sm->wpa_key_mgmt)) {
 			/* No KCK available to derive PMKID */
+			wpa_printf(MSG_DEBUG,
+				   "RSN: No KCK available to derive PMKID for message 1/4");
 			pmkid = NULL;
+#ifdef CONFIG_SAE
+		} else if (wpa_key_mgmt_sae(sm->wpa_key_mgmt)) {
+			if (sm->pmkid_set) {
+				wpa_hexdump(MSG_DEBUG,
+					    "RSN: Message 1/4 PMKID from SAE",
+					    sm->pmkid, PMKID_LEN);
+				os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN],
+					  sm->pmkid, PMKID_LEN);
+			} else {
+				/* No PMKID available */
+				wpa_printf(MSG_DEBUG,
+					   "RSN: No SAE PMKID available for message 1/4");
+				pmkid = NULL;
+			}
+#endif /* CONFIG_SAE */
 		} else {
 			/*
 			 * Calculate PMKID since no PMKSA cache entry was
@@ -2062,6 +2073,9 @@
 			rsn_pmkid(sm->PMK, sm->pmk_len, sm->wpa_auth->addr,
 				  sm->addr, &pmkid[2 + RSN_SELECTOR_LEN],
 				  sm->wpa_key_mgmt);
+			wpa_hexdump(MSG_DEBUG,
+				    "RSN: Message 1/4 PMKID derived from PMK",
+				    &pmkid[2 + RSN_SELECTOR_LEN], PMKID_LEN);
 		}
 	}
 	wpa_send_eapol(sm->wpa_auth, sm,
@@ -2112,21 +2126,24 @@
 	if (fils_ft_len) {
 		struct wpa_authenticator *wpa_auth = sm->wpa_auth;
 		struct wpa_auth_config *conf = &wpa_auth->conf;
-		u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN];
+		u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN];
+		int use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
+		size_t pmk_r0_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN;
 
 		if (wpa_derive_pmk_r0(fils_ft, fils_ft_len,
 				      conf->ssid, conf->ssid_len,
 				      conf->mobility_domain,
 				      conf->r0_key_holder,
 				      conf->r0_key_holder_len,
-				      sm->addr, pmk_r0, pmk_r0_name) < 0)
+				      sm->addr, pmk_r0, pmk_r0_name,
+				      use_sha384) < 0)
 			return -1;
 
-		wpa_hexdump_key(MSG_DEBUG, "FILS+FT: PMK-R0", pmk_r0, PMK_LEN);
+		wpa_hexdump_key(MSG_DEBUG, "FILS+FT: PMK-R0",
+				pmk_r0, pmk_r0_len);
 		wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR0Name",
 			    pmk_r0_name, WPA_PMK_NAME_LEN);
-		wpa_ft_store_pmk_r0(wpa_auth, sm->addr, pmk_r0, pmk_r0_name,
-				    sm->pairwise);
+		wpa_ft_store_pmk_fils(sm, pmk_r0, pmk_r0_name);
 		os_memset(fils_ft, 0, sizeof(fils_ft));
 	}
 #endif /* CONFIG_IEEE80211R_AP */
@@ -2507,7 +2524,8 @@
 	/* GTK KDE */
 	gtk = gsm->GTK[gsm->GN - 1];
 	gtk_len = gsm->GTK_len;
-	if (sm->wpa_auth->conf.disable_gtk) {
+	if (sm->wpa_auth->conf.disable_gtk ||
+	    sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
 		/*
 		 * Provide unique random GTK to each STA to prevent use
 		 * of GTK in the BSS.
@@ -2629,6 +2647,12 @@
 			if (pmk == NULL)
 				break;
 			psk_found = 1;
+#ifdef CONFIG_IEEE80211R_AP
+			if (wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) {
+				os_memcpy(sm->xxkey, pmk, pmk_len);
+				sm->xxkey_len = pmk_len;
+			}
+#endif /* CONFIG_IEEE80211R_AP */
 		} else {
 			pmk = sm->PMK;
 			pmk_len = sm->pmk_len;
@@ -2654,7 +2678,8 @@
 		}
 #endif /* CONFIG_FILS */
 
-		if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt))
+		if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
+		    wpa_key_mgmt_sae(sm->wpa_key_mgmt))
 			break;
 	}
 
@@ -2816,7 +2841,8 @@
 	else
 		os_memcpy(igtk.pn, rsc, sizeof(igtk.pn));
 	os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], len);
-	if (sm->wpa_auth->conf.disable_gtk) {
+	if (sm->wpa_auth->conf.disable_gtk ||
+	    sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
 		/*
 		 * Provide unique random IGTK to each STA to prevent use of
 		 * IGTK in the BSS.
@@ -2894,7 +2920,8 @@
 		secure = 1;
 		gtk = gsm->GTK[gsm->GN - 1];
 		gtk_len = gsm->GTK_len;
-		if (sm->wpa_auth->conf.disable_gtk) {
+		if (sm->wpa_auth->conf.disable_gtk ||
+		    sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
 			/*
 			 * Provide unique random GTK to each STA to prevent use
 			 * of GTK in the BSS.
@@ -2987,7 +3014,10 @@
 				  2 + sm->assoc_resp_ftie[1]);
 			res = 2 + sm->assoc_resp_ftie[1];
 		} else {
-			res = wpa_write_ftie(conf, conf->r0_key_holder,
+			int use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
+
+			res = wpa_write_ftie(conf, use_sha384,
+					     conf->r0_key_holder,
 					     conf->r0_key_holder_len,
 					     NULL, NULL, pos,
 					     kde + kde_len - pos,
@@ -3012,7 +3042,7 @@
 		*pos++ = WLAN_EID_TIMEOUT_INTERVAL;
 		*pos++ = 5;
 		*pos++ = WLAN_TIMEOUT_KEY_LIFETIME;
-		WPA_PUT_LE32(pos, conf->r0_key_lifetime * 60);
+		WPA_PUT_LE32(pos, conf->r0_key_lifetime);
 		pos += 4;
 	}
 #endif /* CONFIG_IEEE80211R_AP */
@@ -3270,7 +3300,8 @@
 			"sending 1/2 msg of Group Key Handshake");
 
 	gtk = gsm->GTK[gsm->GN - 1];
-	if (sm->wpa_auth->conf.disable_gtk) {
+	if (sm->wpa_auth->conf.disable_gtk ||
+	    sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
 		/*
 		 * Provide unique random GTK to each STA to prevent use
 		 * of GTK in the BSS.
@@ -4047,6 +4078,13 @@
 }
 
 
+void wpa_auth_add_sae_pmkid(struct wpa_state_machine *sm, const u8 *pmkid)
+{
+	os_memcpy(sm->pmkid, pmkid, PMKID_LEN);
+	sm->pmkid_set = 1;
+}
+
+
 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)
@@ -4505,11 +4543,12 @@
 
 
 #ifdef CONFIG_IEEE80211R_AP
-int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth, u8 *buf, size_t len)
+int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth, int use_sha384,
+		       u8 *buf, size_t len)
 {
 	struct wpa_auth_config *conf = &wpa_auth->conf;
 
-	return wpa_write_ftie(conf, conf->r0_key_holder,
+	return wpa_write_ftie(conf, use_sha384, conf->r0_key_holder,
 			      conf->r0_key_holder_len,
 			      NULL, NULL, buf, len, NULL, 0);
 }
@@ -4529,7 +4568,7 @@
 #endif /* CONFIG_FILS */
 
 
-#if CONFIG_TESTING_OPTIONS
+#ifdef CONFIG_TESTING_OPTIONS
 
 int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce,
 		       void (*cb)(void *ctx1, void *ctx2),
@@ -4557,7 +4596,10 @@
 		       void (*cb)(void *ctx1, void *ctx2),
 		       void *ctx1, void *ctx2)
 {
-	u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos, *opos;
+	u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos;
+#ifdef CONFIG_IEEE80211W
+	u8 *opos;
+#endif /* CONFIG_IEEE80211W */
 	size_t gtk_len, kde_len;
 	struct wpa_group *gsm = sm->group;
 	u8 *wpa_ie;
@@ -4654,12 +4696,15 @@
 		pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
 				  gtk, gtk_len);
 	}
+#ifdef CONFIG_IEEE80211W
 	opos = pos;
 	pos = ieee80211w_kde_add(sm, pos);
-	if (pos - opos >= WPA_IGTK_KDE_PREFIX_LEN) {
-		opos += 2; /* skip keyid */
+	if (pos - opos >= 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN) {
+		/* skip KDE header and keyid */
+		opos += 2 + RSN_SELECTOR_LEN + 2;
 		os_memset(opos, 0, 6); /* clear PN */
 	}
+#endif /* CONFIG_IEEE80211W */
 
 #ifdef CONFIG_IEEE80211R_AP
 	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
@@ -4673,7 +4718,10 @@
 				  2 + sm->assoc_resp_ftie[1]);
 			res = 2 + sm->assoc_resp_ftie[1];
 		} else {
-			res = wpa_write_ftie(conf, conf->r0_key_holder,
+			int use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
+
+			res = wpa_write_ftie(conf, use_sha384,
+					     conf->r0_key_holder,
 					     conf->r0_key_holder_len,
 					     NULL, NULL, pos,
 					     kde + kde_len - pos,
@@ -4698,7 +4746,7 @@
 		*pos++ = WLAN_EID_TIMEOUT_INTERVAL;
 		*pos++ = 5;
 		*pos++ = WLAN_TIMEOUT_KEY_LIFETIME;
-		WPA_PUT_LE32(pos, conf->r0_key_lifetime * 60);
+		WPA_PUT_LE32(pos, conf->r0_key_lifetime);
 		pos += 4;
 	}
 #endif /* CONFIG_IEEE80211R_AP */
@@ -4722,7 +4770,10 @@
 	u8 rsc[WPA_KEY_RSC_LEN];
 	struct wpa_group *gsm = sm->group;
 	const u8 *kde;
-	u8 *kde_buf = NULL, *pos, *opos, hdr[2];
+	u8 *kde_buf = NULL, *pos, hdr[2];
+#ifdef CONFIG_IEEE80211W
+	u8 *opos;
+#endif /* CONFIG_IEEE80211W */
 	size_t kde_len;
 	u8 *gtk;
 
@@ -4745,12 +4796,16 @@
 		hdr[1] = 0;
 		pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
 				  gtk, gsm->GTK_len);
+#ifdef CONFIG_IEEE80211W
 		opos = pos;
 		pos = ieee80211w_kde_add(sm, pos);
-		if (pos - opos >= WPA_IGTK_KDE_PREFIX_LEN) {
-			opos += 2; /* skip keyid */
+		if (pos - opos >=
+		    2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN) {
+			/* skip KDE header and keyid */
+			opos += 2 + RSN_SELECTOR_LEN + 2;
 			os_memset(opos, 0, 6); /* clear PN */
 		}
+#endif /* CONFIG_IEEE80211W */
 		kde_len = pos - kde;
 	} else {
 		kde = gtk;
@@ -4773,4 +4828,13 @@
 	return 0;
 }
 
+
+int wpa_auth_rekey_gtk(struct wpa_authenticator *wpa_auth)
+{
+	if (!wpa_auth)
+		return -1;
+	eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
+	return eloop_register_timeout(0, 0, wpa_rekey_gtk, wpa_auth, NULL);
+}
+
 #endif /* CONFIG_TESTING_OPTIONS */
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 22f33dd..fad5536 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -14,6 +14,8 @@
 #include "common/wpa_common.h"
 #include "common/ieee802_11_defs.h"
 
+struct vlan_description;
+
 #define MAX_OWN_IE_OVERRIDE 256
 
 #ifdef _MSC_VER
@@ -77,6 +79,14 @@
 #define FT_RRB_PMK_R1        10 /* PMK_LEN */
 
 #define FT_RRB_PAIRWISE      11 /* le16 */
+#define FT_RRB_EXPIRES_IN    12 /* le16 seconds */
+
+#define FT_RRB_VLAN_UNTAGGED 13 /* le16 */
+#define FT_RRB_VLAN_TAGGED   14 /* n times le16 */
+
+#define FT_RRB_IDENTITY      15
+#define FT_RRB_RADIUS_CUI    16
+#define FT_RRB_SESSION_TIMEOUT  17 /* le32 seconds */
 
 struct ft_rrb_tlv {
 	le16 type;
@@ -92,6 +102,8 @@
 
 /* session TLVs:
  *   required: PMK_R1, PMK_R1_NAME, PAIRWISE
+ *   optional: VLAN_UNTAGGED, VLAN_TAGGED, EXPIRES_IN, IDENTITY, RADIUS_CUI,
+ *		 SESSION_TIMEOUT
  *
  * pull frame TLVs:
  *   auth:
@@ -177,6 +189,7 @@
 #ifdef CONFIG_IEEE80211W
 	enum mfp_options ieee80211w;
 	int group_mgmt_cipher;
+	int sae_require_mfp;
 #endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_IEEE80211R_AP
 	u8 ssid[SSID_MAX_LEN];
@@ -185,11 +198,12 @@
 	u8 r0_key_holder[FT_R0KH_ID_MAX_LEN];
 	size_t r0_key_holder_len;
 	u8 r1_key_holder[FT_R1KH_ID_LEN];
-	u32 r0_key_lifetime;
+	u32 r0_key_lifetime; /* PMK-R0 lifetime seconds */
 	int rkh_pos_timeout;
 	int rkh_neg_timeout;
 	int rkh_pull_timeout; /* ms */
 	int rkh_pull_retries;
+	int r1_max_key_lifetime;
 	u32 reassociation_deadline;
 	struct ft_remote_r0kh **r0kh_list;
 	struct ft_remote_r1kh **r1kh_list;
@@ -253,6 +267,20 @@
 			size_t data_len);
 #ifdef CONFIG_IEEE80211R_AP
 	struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr);
+	int (*set_vlan)(void *ctx, const u8 *sta_addr,
+			struct vlan_description *vlan);
+	int (*get_vlan)(void *ctx, const u8 *sta_addr,
+			struct vlan_description *vlan);
+	int (*set_identity)(void *ctx, const u8 *sta_addr,
+			    const u8 *identity, size_t identity_len);
+	size_t (*get_identity)(void *ctx, const u8 *sta_addr, const u8 **buf);
+	int (*set_radius_cui)(void *ctx, const u8 *sta_addr,
+			      const u8 *radius_cui, size_t radius_cui_len);
+	size_t (*get_radius_cui)(void *ctx, const u8 *sta_addr, const u8 **buf);
+	void (*set_session_timeout)(void *ctx, const u8 *sta_addr,
+				    int session_timeout);
+	int (*get_session_timeout)(void *ctx, const u8 *sta_addr);
+
 	int (*send_ft_action)(void *ctx, const u8 *dst,
 			      const u8 *data, size_t data_len);
 	int (*add_tspec)(void *ctx, const u8 *sta_addr, u8 *tspec_ie,
@@ -331,6 +359,7 @@
 			       struct eapol_state_machine *eapol);
 int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
 			   const u8 *pmk, const u8 *pmkid);
+void wpa_auth_add_sae_pmkid(struct wpa_state_machine *sm, const u8 *pmkid);
 int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr,
 			const u8 *pmk, size_t pmk_len, const u8 *pmkid,
 			int session_timeout, int akmp);
@@ -420,7 +449,8 @@
 int wpa_fils_validate_key_confirm(struct wpa_state_machine *sm, const u8 *ies,
 				  size_t ies_len);
 
-int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth, u8 *buf, size_t len);
+int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth, int use_sha384,
+		       u8 *buf, size_t len);
 void wpa_auth_get_fils_aead_params(struct wpa_state_machine *sm,
 				   u8 *fils_anonce, u8 *fils_snonce,
 				   u8 *fils_kek, size_t *fils_kek_len);
@@ -437,5 +467,6 @@
 int wpa_auth_resend_group_m1(struct wpa_state_machine *sm,
 			     void (*cb)(void *ctx1, void *ctx2),
 			     void *ctx1, void *ctx2);
+int wpa_auth_rekey_gtk(struct wpa_authenticator *wpa_auth);
 
 #endif /* WPA_AUTH_H */
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index 153752d..e8d46ab 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -1,6 +1,6 @@
 /*
  * hostapd - IEEE 802.11r - Fast BSS Transition
- * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2018, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -16,6 +16,7 @@
 #include "crypto/aes.h"
 #include "crypto/aes_siv.h"
 #include "crypto/aes_wrap.h"
+#include "crypto/sha384.h"
 #include "crypto/random.h"
 #include "ap_config.h"
 #include "ieee802_11.h"
@@ -192,6 +193,98 @@
 }
 
 
+static int cmp_int(const void *a, const void *b)
+{
+	int x, y;
+
+	x = *((int *) a);
+	y = *((int *) b);
+	return x - y;
+}
+
+
+static int wpa_ft_rrb_get_tlv_vlan(const u8 *plain, const size_t plain_len,
+				   struct vlan_description *vlan)
+{
+	struct ft_rrb_tlv *f;
+	size_t left;
+	size_t len;
+	int taggedidx;
+	int vlan_id;
+	int type;
+
+	left = plain_len;
+	taggedidx = 0;
+	os_memset(vlan, 0, sizeof(*vlan));
+
+	while (left >= sizeof(*f)) {
+		f = (struct ft_rrb_tlv *) plain;
+
+		left -= sizeof(*f);
+		plain += sizeof(*f);
+
+		len = le_to_host16(f->len);
+		type = le_to_host16(f->type);
+
+		if (left < len) {
+			wpa_printf(MSG_DEBUG, "FT: RRB message truncated");
+			return -1;
+		}
+
+		if (type != FT_RRB_VLAN_UNTAGGED && type != FT_RRB_VLAN_TAGGED)
+			goto skip;
+
+		if (type == FT_RRB_VLAN_UNTAGGED && len != sizeof(le16)) {
+			wpa_printf(MSG_DEBUG,
+				   "FT: RRB VLAN_UNTAGGED invalid length");
+			return -1;
+		}
+
+		if (type == FT_RRB_VLAN_TAGGED && len % sizeof(le16) != 0) {
+			wpa_printf(MSG_DEBUG,
+				   "FT: RRB VLAN_TAGGED invalid length");
+			return -1;
+		}
+
+		while (len >= sizeof(le16)) {
+			vlan_id = WPA_GET_LE16(plain);
+			plain += sizeof(le16);
+			left -= sizeof(le16);
+			len -= sizeof(le16);
+
+			if (vlan_id <= 0 || vlan_id > MAX_VLAN_ID) {
+				wpa_printf(MSG_DEBUG,
+					   "FT: RRB VLAN ID invalid %d",
+					   vlan_id);
+				continue;
+			}
+
+			if (type == FT_RRB_VLAN_UNTAGGED)
+				vlan->untagged = vlan_id;
+
+			if (type == FT_RRB_VLAN_TAGGED &&
+			    taggedidx < MAX_NUM_TAGGED_VLAN) {
+				vlan->tagged[taggedidx] = vlan_id;
+				taggedidx++;
+			} else if (type == FT_RRB_VLAN_TAGGED) {
+				wpa_printf(MSG_DEBUG, "FT: RRB too many VLANs");
+			}
+		}
+
+	skip:
+		left -= len;
+		plain += len;
+	}
+
+	if (taggedidx)
+		qsort(vlan->tagged, taggedidx, sizeof(int), cmp_int);
+
+	vlan->notempty = vlan->untagged || vlan->tagged[0];
+
+	return 0;
+}
+
+
 static size_t wpa_ft_tlv_len(const struct tlv_list *tlvs)
 {
 	size_t tlv_len = 0;
@@ -233,6 +326,8 @@
 
 		if (tlv_len + tlvs[i].len > (size_t) (endpos - start))
 			return tlv_len;
+		if (tlvs[i].len == 0)
+			continue;
 		tlv_len += tlvs[i].len;
 		os_memcpy(pos, tlvs[i].data, tlvs[i].len);
 		pos = start + tlv_len;
@@ -242,8 +337,84 @@
 }
 
 
+static size_t wpa_ft_vlan_len(const struct vlan_description *vlan)
+{
+	size_t tlv_len = 0;
+	int i;
+
+	if (!vlan || !vlan->notempty)
+		return 0;
+
+	if (vlan->untagged) {
+		tlv_len += sizeof(struct ft_rrb_tlv);
+		tlv_len += sizeof(le16);
+	}
+	if (vlan->tagged[0])
+		tlv_len += sizeof(struct ft_rrb_tlv);
+	for (i = 0; i < MAX_NUM_TAGGED_VLAN && vlan->tagged[i]; i++)
+		tlv_len += sizeof(le16);
+
+	return tlv_len;
+}
+
+
+static size_t wpa_ft_vlan_lin(const struct vlan_description *vlan,
+			      u8 *start, u8 *endpos)
+{
+	size_t tlv_len;
+	int i, len;
+	struct ft_rrb_tlv *hdr;
+	u8 *pos = start;
+
+	if (!vlan || !vlan->notempty)
+		return 0;
+
+	tlv_len = 0;
+	if (vlan->untagged) {
+		tlv_len += sizeof(*hdr);
+		if (start + tlv_len > endpos)
+			return tlv_len;
+		hdr = (struct ft_rrb_tlv *) pos;
+		hdr->type = host_to_le16(FT_RRB_VLAN_UNTAGGED);
+		hdr->len = host_to_le16(sizeof(le16));
+		pos = start + tlv_len;
+
+		tlv_len += sizeof(le16);
+		if (start + tlv_len > endpos)
+			return tlv_len;
+		WPA_PUT_LE16(pos, vlan->untagged);
+		pos = start + tlv_len;
+	}
+
+	if (!vlan->tagged[0])
+		return tlv_len;
+
+	tlv_len += sizeof(*hdr);
+	if (start + tlv_len > endpos)
+		return tlv_len;
+	hdr = (struct ft_rrb_tlv *) pos;
+	hdr->type = host_to_le16(FT_RRB_VLAN_TAGGED);
+	len = 0; /* len is computed below */
+	pos = start + tlv_len;
+
+	for (i = 0; i < MAX_NUM_TAGGED_VLAN && vlan->tagged[i]; i++) {
+		tlv_len += sizeof(le16);
+		if (start + tlv_len > endpos)
+			break;
+		len += sizeof(le16);
+		WPA_PUT_LE16(pos, vlan->tagged[i]);
+		pos = start + tlv_len;
+	}
+
+	hdr->len = host_to_le16(len);
+
+	return tlv_len;
+}
+
+
 static int wpa_ft_rrb_lin(const struct tlv_list *tlvs1,
 			  const struct tlv_list *tlvs2,
+			  const struct vlan_description *vlan,
 			  u8 **plain, size_t *plain_len)
 {
 	u8 *pos, *endpos;
@@ -251,6 +422,7 @@
 
 	tlv_len = wpa_ft_tlv_len(tlvs1);
 	tlv_len += wpa_ft_tlv_len(tlvs2);
+	tlv_len += wpa_ft_vlan_len(vlan);
 
 	*plain_len = tlv_len;
 	*plain = os_zalloc(tlv_len);
@@ -263,6 +435,7 @@
 	endpos = *plain + tlv_len;
 	pos += wpa_ft_tlv_lin(tlvs1, pos, endpos);
 	pos += wpa_ft_tlv_lin(tlvs2, pos, endpos);
+	pos += wpa_ft_vlan_lin(vlan, pos, endpos);
 
 	/* sanity check */
 	if (pos != endpos) {
@@ -324,6 +497,7 @@
 			    const struct tlv_list *tlvs_enc0,
 			    const struct tlv_list *tlvs_enc1,
 			    const struct tlv_list *tlvs_auth,
+			    const struct vlan_description *vlan,
 			    const u8 *src_addr, u8 type,
 			    u8 **packet, size_t *packet_len)
 {
@@ -331,10 +505,11 @@
 	size_t plain_len = 0, auth_len = 0;
 	int ret = -1;
 
-	if (wpa_ft_rrb_lin(tlvs_enc0, tlvs_enc1, &plain, &plain_len) < 0)
+	*packet = NULL;
+	if (wpa_ft_rrb_lin(tlvs_enc0, tlvs_enc1, vlan, &plain, &plain_len) < 0)
 		goto out;
 
-	if (wpa_ft_rrb_lin(tlvs_auth, NULL, &auth, &auth_len) < 0)
+	if (wpa_ft_rrb_lin(tlvs_auth, NULL, NULL, &auth, &auth_len) < 0)
 		goto out;
 
 	*packet_len = sizeof(u16) + auth_len + plain_len;
@@ -456,6 +631,89 @@
 }
 
 
+static int wpa_ft_set_vlan(struct wpa_authenticator *wpa_auth,
+			   const u8 *sta_addr, struct vlan_description *vlan)
+{
+	if (!wpa_auth->cb->set_vlan)
+		return -1;
+	return wpa_auth->cb->set_vlan(wpa_auth->cb_ctx, sta_addr, vlan);
+}
+
+
+static int wpa_ft_get_vlan(struct wpa_authenticator *wpa_auth,
+			   const u8 *sta_addr, struct vlan_description *vlan)
+{
+	if (!wpa_auth->cb->get_vlan)
+		return -1;
+	return wpa_auth->cb->get_vlan(wpa_auth->cb_ctx, sta_addr, vlan);
+}
+
+
+static int
+wpa_ft_set_identity(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
+		    const u8 *identity, size_t identity_len)
+{
+	if (!wpa_auth->cb->set_identity)
+		return -1;
+	return wpa_auth->cb->set_identity(wpa_auth->cb_ctx, sta_addr, identity,
+					  identity_len);
+}
+
+
+static size_t
+wpa_ft_get_identity(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
+		    const u8 **buf)
+{
+	*buf = NULL;
+	if (!wpa_auth->cb->get_identity)
+		return 0;
+	return wpa_auth->cb->get_identity(wpa_auth->cb_ctx, sta_addr, buf);
+}
+
+
+static int
+wpa_ft_set_radius_cui(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
+		      const u8 *radius_cui, size_t radius_cui_len)
+{
+	if (!wpa_auth->cb->set_radius_cui)
+		return -1;
+	return wpa_auth->cb->set_radius_cui(wpa_auth->cb_ctx, sta_addr,
+					    radius_cui, radius_cui_len);
+}
+
+
+static size_t
+wpa_ft_get_radius_cui(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
+		      const u8 **buf)
+{
+	*buf = NULL;
+	if (!wpa_auth->cb->get_radius_cui)
+		return 0;
+	return wpa_auth->cb->get_radius_cui(wpa_auth->cb_ctx, sta_addr, buf);
+}
+
+
+static void
+wpa_ft_set_session_timeout(struct wpa_authenticator *wpa_auth,
+			    const u8 *sta_addr, int session_timeout)
+{
+	if (!wpa_auth->cb->set_session_timeout)
+		return;
+	wpa_auth->cb->set_session_timeout(wpa_auth->cb_ctx, sta_addr,
+					  session_timeout);
+}
+
+
+static int
+wpa_ft_get_session_timeout(struct wpa_authenticator *wpa_auth,
+			    const u8 *sta_addr)
+{
+	if (!wpa_auth->cb->get_session_timeout)
+		return 0;
+	return wpa_auth->cb->get_session_timeout(wpa_auth->cb_ctx, sta_addr);
+}
+
+
 static int wpa_ft_add_tspec(struct wpa_authenticator *wpa_auth,
 			    const u8 *sta_addr,
 			    u8 *tspec_ie, size_t tspec_ielen)
@@ -489,30 +747,44 @@
 }
 
 
-int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id,
-		   size_t r0kh_id_len,
+int wpa_write_ftie(struct wpa_auth_config *conf, int use_sha384,
+		   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)
 {
 	u8 *pos = buf, *ielen;
-	struct rsn_ftie *hdr;
+	size_t hdrlen = use_sha384 ? sizeof(struct rsn_ftie_sha384) :
+		sizeof(struct rsn_ftie);
 
-	if (len < 2 + sizeof(*hdr) + 2 + FT_R1KH_ID_LEN + 2 + r0kh_id_len +
+	if (len < 2 + hdrlen + 2 + FT_R1KH_ID_LEN + 2 + r0kh_id_len +
 	    subelem_len)
 		return -1;
 
 	*pos++ = WLAN_EID_FAST_BSS_TRANSITION;
 	ielen = pos++;
 
-	hdr = (struct rsn_ftie *) pos;
-	os_memset(hdr, 0, sizeof(*hdr));
-	pos += sizeof(*hdr);
-	WPA_PUT_LE16(hdr->mic_control, 0);
-	if (anonce)
-		os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN);
-	if (snonce)
-		os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN);
+	if (use_sha384) {
+		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, 0);
+		if (anonce)
+			os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN);
+		if (snonce)
+			os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN);
+	} else {
+		struct rsn_ftie *hdr = (struct rsn_ftie *) pos;
+
+		os_memset(hdr, 0, sizeof(*hdr));
+		pos += sizeof(*hdr);
+		WPA_PUT_LE16(hdr->mic_control, 0);
+		if (anonce)
+			os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN);
+		if (snonce)
+			os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN);
+	}
 
 	/* Optional Parameters */
 	*pos++ = FTIE_SUBELEM_R1KH_ID;
@@ -656,7 +928,7 @@
 
 	seq_req_auth[0].data = item->nonce;
 
-	if (wpa_ft_rrb_build(key, key_len, NULL, NULL, seq_req_auth,
+	if (wpa_ft_rrb_build(key, key_len, NULL, NULL, seq_req_auth, NULL,
 			     wpa_auth->addr, FT_PACKET_R0KH_R1KH_SEQ_REQ,
 			     &packet, &packet_len) < 0) {
 		item = NULL; /* some other seq resp might still accept this */
@@ -834,34 +1106,136 @@
 
 
 struct wpa_ft_pmk_r0_sa {
-	struct wpa_ft_pmk_r0_sa *next;
-	u8 pmk_r0[PMK_LEN];
+	struct dl_list list;
+	u8 pmk_r0[PMK_LEN_MAX];
+	size_t pmk_r0_len;
 	u8 pmk_r0_name[WPA_PMK_NAME_LEN];
 	u8 spa[ETH_ALEN];
 	int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */
-	/* TODO: expiration, identity, radius_class, EAP type, VLAN ID */
+	struct vlan_description *vlan;
+	os_time_t expiration; /* 0 for no expiration */
+	u8 *identity;
+	size_t identity_len;
+	u8 *radius_cui;
+	size_t radius_cui_len;
+	os_time_t session_timeout; /* 0 for no expiration */
+	/* TODO: radius_class, EAP type */
 	int pmk_r1_pushed;
 };
 
 struct wpa_ft_pmk_r1_sa {
-	struct wpa_ft_pmk_r1_sa *next;
-	u8 pmk_r1[PMK_LEN];
+	struct dl_list list;
+	u8 pmk_r1[PMK_LEN_MAX];
+	size_t pmk_r1_len;
 	u8 pmk_r1_name[WPA_PMK_NAME_LEN];
 	u8 spa[ETH_ALEN];
 	int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */
-	/* TODO: expiration, identity, radius_class, EAP type, VLAN ID */
+	struct vlan_description *vlan;
+	u8 *identity;
+	size_t identity_len;
+	u8 *radius_cui;
+	size_t radius_cui_len;
+	os_time_t session_timeout; /* 0 for no expiration */
+	/* TODO: radius_class, EAP type */
 };
 
 struct wpa_ft_pmk_cache {
-	struct wpa_ft_pmk_r0_sa *pmk_r0;
-	struct wpa_ft_pmk_r1_sa *pmk_r1;
+	struct dl_list pmk_r0; /* struct wpa_ft_pmk_r0_sa */
+	struct dl_list pmk_r1; /* struct wpa_ft_pmk_r1_sa */
 };
 
+
+static void wpa_ft_expire_pmk_r0(void *eloop_ctx, void *timeout_ctx);
+static void wpa_ft_expire_pmk_r1(void *eloop_ctx, void *timeout_ctx);
+
+
+static void wpa_ft_free_pmk_r0(struct wpa_ft_pmk_r0_sa *r0)
+{
+	if (!r0)
+		return;
+
+	dl_list_del(&r0->list);
+	eloop_cancel_timeout(wpa_ft_expire_pmk_r0, r0, NULL);
+
+	os_memset(r0->pmk_r0, 0, PMK_LEN_MAX);
+	os_free(r0->vlan);
+	os_free(r0->identity);
+	os_free(r0->radius_cui);
+	os_free(r0);
+}
+
+
+static void wpa_ft_expire_pmk_r0(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_ft_pmk_r0_sa *r0 = eloop_ctx;
+	struct os_reltime now;
+	int expires_in;
+	int session_timeout;
+
+	os_get_reltime(&now);
+
+	if (!r0)
+		return;
+
+	expires_in = r0->expiration - now.sec;
+	session_timeout = r0->session_timeout - now.sec;
+	/* conditions to remove from cache:
+	 * a) r0->expiration is set and hit
+	 * -or-
+	 * b) r0->session_timeout is set and hit
+	 */
+	if ((!r0->expiration || expires_in > 0) &&
+	    (!r0->session_timeout || session_timeout > 0)) {
+		wpa_printf(MSG_ERROR,
+			   "FT: %s() called for non-expired entry %p",
+			   __func__, r0);
+		eloop_cancel_timeout(wpa_ft_expire_pmk_r0, r0, NULL);
+		if (r0->expiration && expires_in > 0)
+			eloop_register_timeout(expires_in + 1, 0,
+					       wpa_ft_expire_pmk_r0, r0, NULL);
+		if (r0->session_timeout && session_timeout > 0)
+			eloop_register_timeout(session_timeout + 1, 0,
+					       wpa_ft_expire_pmk_r0, r0, NULL);
+		return;
+	}
+
+	wpa_ft_free_pmk_r0(r0);
+}
+
+
+static void wpa_ft_free_pmk_r1(struct wpa_ft_pmk_r1_sa *r1)
+{
+	if (!r1)
+		return;
+
+	dl_list_del(&r1->list);
+	eloop_cancel_timeout(wpa_ft_expire_pmk_r1, r1, NULL);
+
+	os_memset(r1->pmk_r1, 0, PMK_LEN_MAX);
+	os_free(r1->vlan);
+	os_free(r1->identity);
+	os_free(r1->radius_cui);
+	os_free(r1);
+}
+
+
+static void wpa_ft_expire_pmk_r1(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_ft_pmk_r1_sa *r1 = eloop_ctx;
+
+	wpa_ft_free_pmk_r1(r1);
+}
+
+
 struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void)
 {
 	struct wpa_ft_pmk_cache *cache;
 
 	cache = os_zalloc(sizeof(*cache));
+	if (cache) {
+		dl_list_init(&cache->pmk_r0);
+		dl_list_init(&cache->pmk_r1);
+	}
 
 	return cache;
 }
@@ -872,46 +1246,77 @@
 	struct wpa_ft_pmk_r0_sa *r0, *r0prev;
 	struct wpa_ft_pmk_r1_sa *r1, *r1prev;
 
-	r0 = cache->pmk_r0;
-	while (r0) {
-		r0prev = r0;
-		r0 = r0->next;
-		os_memset(r0prev->pmk_r0, 0, PMK_LEN);
-		os_free(r0prev);
-	}
+	dl_list_for_each_safe(r0, r0prev, &cache->pmk_r0,
+			      struct wpa_ft_pmk_r0_sa, list)
+		wpa_ft_free_pmk_r0(r0);
 
-	r1 = cache->pmk_r1;
-	while (r1) {
-		r1prev = r1;
-		r1 = r1->next;
-		os_memset(r1prev->pmk_r1, 0, PMK_LEN);
-		os_free(r1prev);
-	}
+	dl_list_for_each_safe(r1, r1prev, &cache->pmk_r1,
+			      struct wpa_ft_pmk_r1_sa, list)
+		wpa_ft_free_pmk_r1(r1);
 
 	os_free(cache);
 }
 
 
-int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth,
-			const u8 *spa, const u8 *pmk_r0,
-			const u8 *pmk_r0_name, int pairwise)
+static int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth,
+			       const u8 *spa, const u8 *pmk_r0,
+			       size_t pmk_r0_len,
+			       const u8 *pmk_r0_name, int pairwise,
+			       const struct vlan_description *vlan,
+			       int expires_in, int session_timeout,
+			       const u8 *identity, size_t identity_len,
+			       const u8 *radius_cui, size_t radius_cui_len)
 {
 	struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
 	struct wpa_ft_pmk_r0_sa *r0;
+	struct os_reltime now;
 
-	/* TODO: add expiration and limit on number of entries in cache */
+	/* TODO: add limit on number of entries in cache */
+	os_get_reltime(&now);
 
 	r0 = os_zalloc(sizeof(*r0));
 	if (r0 == NULL)
 		return -1;
 
-	os_memcpy(r0->pmk_r0, pmk_r0, PMK_LEN);
+	os_memcpy(r0->pmk_r0, pmk_r0, pmk_r0_len);
+	r0->pmk_r0_len = pmk_r0_len;
 	os_memcpy(r0->pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN);
 	os_memcpy(r0->spa, spa, ETH_ALEN);
 	r0->pairwise = pairwise;
+	if (expires_in > 0)
+		r0->expiration = now.sec + expires_in;
+	if (vlan && vlan->notempty) {
+		r0->vlan = os_zalloc(sizeof(*vlan));
+		if (!r0->vlan) {
+			bin_clear_free(r0, sizeof(*r0));
+			return -1;
+		}
+		*r0->vlan = *vlan;
+	}
+	if (identity) {
+		r0->identity = os_malloc(identity_len);
+		if (r0->identity) {
+			os_memcpy(r0->identity, identity, identity_len);
+			r0->identity_len = identity_len;
+		}
+	}
+	if (radius_cui) {
+		r0->radius_cui = os_malloc(radius_cui_len);
+		if (r0->radius_cui) {
+			os_memcpy(r0->radius_cui, radius_cui, radius_cui_len);
+			r0->radius_cui_len = radius_cui_len;
+		}
+	}
+	if (session_timeout > 0)
+		r0->session_timeout = now.sec + session_timeout;
 
-	r0->next = cache->pmk_r0;
-	cache->pmk_r0 = r0;
+	dl_list_add(&cache->pmk_r0, &r0->list);
+	if (expires_in > 0)
+		eloop_register_timeout(expires_in + 1, 0, wpa_ft_expire_pmk_r0,
+				       r0, NULL);
+	if (session_timeout > 0)
+		eloop_register_timeout(session_timeout + 1, 0,
+				       wpa_ft_expire_pmk_r0, r0, NULL);
 
 	return 0;
 }
@@ -923,17 +1328,16 @@
 {
 	struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
 	struct wpa_ft_pmk_r0_sa *r0;
+	struct os_reltime now;
 
-	r0 = cache->pmk_r0;
-	while (r0) {
+	os_get_reltime(&now);
+	dl_list_for_each(r0, &cache->pmk_r0, struct wpa_ft_pmk_r0_sa, list) {
 		if (os_memcmp(r0->spa, spa, ETH_ALEN) == 0 &&
 		    os_memcmp_const(r0->pmk_r0_name, pmk_r0_name,
 				    WPA_PMK_NAME_LEN) == 0) {
 			*r0_out = r0;
 			return 0;
 		}
-
-		r0 = r0->next;
 	}
 
 	*r0_out = NULL;
@@ -943,24 +1347,66 @@
 
 static int wpa_ft_store_pmk_r1(struct wpa_authenticator *wpa_auth,
 			       const u8 *spa, const u8 *pmk_r1,
-			       const u8 *pmk_r1_name, int pairwise)
+			       size_t pmk_r1_len,
+			       const u8 *pmk_r1_name, int pairwise,
+			       const struct vlan_description *vlan,
+			       int expires_in, int session_timeout,
+			       const u8 *identity, size_t identity_len,
+			       const u8 *radius_cui, size_t radius_cui_len)
 {
 	struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
+	int max_expires_in = wpa_auth->conf.r1_max_key_lifetime;
 	struct wpa_ft_pmk_r1_sa *r1;
+	struct os_reltime now;
 
-	/* TODO: add expiration and limit on number of entries in cache */
+	/* TODO: limit on number of entries in cache */
+	os_get_reltime(&now);
+
+	if (max_expires_in && (max_expires_in < expires_in || expires_in == 0))
+		expires_in = max_expires_in;
 
 	r1 = os_zalloc(sizeof(*r1));
 	if (r1 == NULL)
 		return -1;
 
-	os_memcpy(r1->pmk_r1, pmk_r1, PMK_LEN);
+	os_memcpy(r1->pmk_r1, pmk_r1, pmk_r1_len);
+	r1->pmk_r1_len = pmk_r1_len;
 	os_memcpy(r1->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN);
 	os_memcpy(r1->spa, spa, ETH_ALEN);
 	r1->pairwise = pairwise;
+	if (vlan && vlan->notempty) {
+		r1->vlan = os_zalloc(sizeof(*vlan));
+		if (!r1->vlan) {
+			bin_clear_free(r1, sizeof(*r1));
+			return -1;
+		}
+		*r1->vlan = *vlan;
+	}
+	if (identity) {
+		r1->identity = os_malloc(identity_len);
+		if (r1->identity) {
+			os_memcpy(r1->identity, identity, identity_len);
+			r1->identity_len = identity_len;
+		}
+	}
+	if (radius_cui) {
+		r1->radius_cui = os_malloc(radius_cui_len);
+		if (r1->radius_cui) {
+			os_memcpy(r1->radius_cui, radius_cui, radius_cui_len);
+			r1->radius_cui_len = radius_cui_len;
+		}
+	}
+	if (session_timeout > 0)
+		r1->session_timeout = now.sec + session_timeout;
 
-	r1->next = cache->pmk_r1;
-	cache->pmk_r1 = r1;
+	dl_list_add(&cache->pmk_r1, &r1->list);
+
+	if (expires_in > 0)
+		eloop_register_timeout(expires_in + 1, 0, wpa_ft_expire_pmk_r1,
+				       r1, NULL);
+	if (session_timeout > 0)
+		eloop_register_timeout(session_timeout + 1, 0,
+				       wpa_ft_expire_pmk_r1, r1, NULL);
 
 	return 0;
 }
@@ -968,23 +1414,47 @@
 
 static int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth,
 			       const u8 *spa, const u8 *pmk_r1_name,
-			       u8 *pmk_r1, int *pairwise)
+			       u8 *pmk_r1, size_t *pmk_r1_len, int *pairwise,
+			       struct vlan_description *vlan,
+			       const u8 **identity, size_t *identity_len,
+			       const u8 **radius_cui, size_t *radius_cui_len,
+			       int *session_timeout)
 {
 	struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
 	struct wpa_ft_pmk_r1_sa *r1;
+	struct os_reltime now;
 
-	r1 = cache->pmk_r1;
-	while (r1) {
+	os_get_reltime(&now);
+
+	dl_list_for_each(r1, &cache->pmk_r1, struct wpa_ft_pmk_r1_sa, list) {
 		if (os_memcmp(r1->spa, spa, ETH_ALEN) == 0 &&
 		    os_memcmp_const(r1->pmk_r1_name, pmk_r1_name,
 				    WPA_PMK_NAME_LEN) == 0) {
-			os_memcpy(pmk_r1, r1->pmk_r1, PMK_LEN);
+			os_memcpy(pmk_r1, r1->pmk_r1, r1->pmk_r1_len);
+			*pmk_r1_len = r1->pmk_r1_len;
 			if (pairwise)
 				*pairwise = r1->pairwise;
+			if (vlan && r1->vlan)
+				*vlan = *r1->vlan;
+			if (vlan && !r1->vlan)
+				os_memset(vlan, 0, sizeof(*vlan));
+			if (identity && identity_len) {
+				*identity = r1->identity;
+				*identity_len = r1->identity_len;
+			}
+			if (radius_cui && radius_cui_len) {
+				*radius_cui = r1->radius_cui;
+				*radius_cui_len = r1->radius_cui_len;
+			}
+			if (session_timeout && r1->session_timeout > now.sec)
+				*session_timeout = r1->session_timeout -
+					now.sec;
+			else if (session_timeout && r1->session_timeout)
+				*session_timeout = 1;
+			else
+				*session_timeout = 0;
 			return 0;
 		}
-
-		r1 = r1->next;
 	}
 
 	return -1;
@@ -1459,6 +1929,11 @@
 			    sm->r0kh_id, sm->r0kh_id_len);
 		return -1;
 	}
+	if (os_memcmp(r0kh->addr, sm->wpa_auth->addr, ETH_ALEN) == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "FT: R0KH-ID points to self - no matching key available");
+		return -1;
+	}
 
 	key = r0kh->key;
 	key_len = sizeof(r0kh->key);
@@ -1486,7 +1961,7 @@
 		return -1;
 	}
 
-	if (wpa_ft_rrb_build(key, key_len, req_enc, NULL, req_auth,
+	if (wpa_ft_rrb_build(key, key_len, req_enc, NULL, req_auth, NULL,
 			     sm->wpa_auth->addr, FT_PACKET_R0KH_R1KH_PULL,
 			     &packet, &packet_len) < 0)
 		return -1;
@@ -1512,11 +1987,43 @@
 }
 
 
+int wpa_ft_store_pmk_fils(struct wpa_state_machine *sm,
+			  const u8 *pmk_r0, const u8 *pmk_r0_name)
+{
+	int expires_in = sm->wpa_auth->conf.r0_key_lifetime;
+	struct vlan_description vlan;
+	const u8 *identity, *radius_cui;
+	size_t identity_len, radius_cui_len;
+	int session_timeout;
+	size_t pmk_r0_len = wpa_key_mgmt_sha384(sm->wpa_key_mgmt) ?
+		SHA384_MAC_LEN : PMK_LEN;
+
+	if (wpa_ft_get_vlan(sm->wpa_auth, sm->addr, &vlan) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: vlan not available for STA " MACSTR,
+			   MAC2STR(sm->addr));
+		return -1;
+	}
+
+	identity_len = wpa_ft_get_identity(sm->wpa_auth, sm->addr, &identity);
+	radius_cui_len = wpa_ft_get_radius_cui(sm->wpa_auth, sm->addr,
+					       &radius_cui);
+	session_timeout = wpa_ft_get_session_timeout(sm->wpa_auth, sm->addr);
+
+	return wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_len,
+				   pmk_r0_name, sm->pairwise, &vlan, expires_in,
+				   session_timeout, identity, identity_len,
+				   radius_cui, radius_cui_len);
+}
+
+
 int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
 			   struct wpa_ptk *ptk)
 {
-	u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN];
-	u8 pmk_r1[PMK_LEN];
+	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;
+	u8 pmk_r1[PMK_LEN_MAX];
 	u8 ptk_name[WPA_PMK_NAME_LEN];
 	const u8 *mdid = sm->wpa_auth->conf.mobility_domain;
 	const u8 *r0kh = sm->wpa_auth->conf.r0_key_holder;
@@ -1525,6 +2032,11 @@
 	const u8 *ssid = sm->wpa_auth->conf.ssid;
 	size_t ssid_len = sm->wpa_auth->conf.ssid_len;
 	int psk_local = sm->wpa_auth->conf.ft_psk_generate_local;
+	int expires_in = sm->wpa_auth->conf.r0_key_lifetime;
+	struct vlan_description vlan;
+	const u8 *identity, *radius_cui;
+	size_t identity_len, radius_cui_len;
+	int session_timeout;
 
 	if (sm->xxkey_len == 0) {
 		wpa_printf(MSG_DEBUG, "FT: XXKey not available for key "
@@ -1532,28 +2044,45 @@
 		return -1;
 	}
 
+	if (wpa_ft_get_vlan(sm->wpa_auth, sm->addr, &vlan) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: vlan not available for STA " MACSTR,
+			   MAC2STR(sm->addr));
+		return -1;
+	}
+
+	identity_len = wpa_ft_get_identity(sm->wpa_auth, sm->addr, &identity);
+	radius_cui_len = wpa_ft_get_radius_cui(sm->wpa_auth, sm->addr,
+					       &radius_cui);
+	session_timeout = wpa_ft_get_session_timeout(sm->wpa_auth, sm->addr);
+
 	if (wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, ssid, ssid_len, mdid,
 			      r0kh, r0kh_len, sm->addr,
-			      pmk_r0, pmk_r0_name) < 0)
+			      pmk_r0, pmk_r0_name,
+			      wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) < 0)
 		return -1;
-	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, PMK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, pmk_r0_len);
 	wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", pmk_r0_name, WPA_PMK_NAME_LEN);
 	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_name,
-				    sm->pairwise);
+		wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_len,
+				    pmk_r0_name,
+				    sm->pairwise, &vlan, expires_in,
+				    session_timeout, identity, identity_len,
+				    radius_cui, radius_cui_len);
 
-	if (wpa_derive_pmk_r1(pmk_r0, pmk_r0_name, r1kh, sm->addr,
+	if (wpa_derive_pmk_r1(pmk_r0, pmk_r0_len, pmk_r0_name, r1kh, sm->addr,
 			      pmk_r1, sm->pmk_r1_name) < 0)
 		return -1;
-	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, PMK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, pmk_r1_len);
 	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name,
 		    WPA_PMK_NAME_LEN);
 	if (!psk_local || !wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt))
-		wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1,
-				    sm->pmk_r1_name, sm->pairwise);
+		wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, pmk_r1_len,
+				    sm->pmk_r1_name, sm->pairwise, &vlan,
+				    expires_in, session_timeout, identity,
+				    identity_len, radius_cui, radius_cui_len);
 
-	return wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr,
-				 sm->wpa_auth->addr, sm->pmk_r1_name,
+	return wpa_pmk_r1_to_ptk(pmk_r1, pmk_r1_len, sm->SNonce, sm->ANonce,
+				 sm->addr, sm->wpa_auth->addr, sm->pmk_r1_name,
 				 ptk, ptk_name, sm->wpa_key_mgmt, sm->pairwise);
 }
 
@@ -1575,6 +2104,16 @@
 	const u8 *key;
 	size_t key_len;
 	u8 keybuf[32];
+	const u8 *kek;
+	size_t kek_len;
+
+	if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+		kek = sm->PTK.kek2;
+		kek_len = sm->PTK.kek2_len;
+	} else {
+		kek = sm->PTK.kek;
+		kek_len = sm->PTK.kek_len;
+	}
 
 	key_len = gsm->GTK_len;
 	if (key_len > sizeof(keybuf))
@@ -1613,8 +2152,10 @@
 	WPA_PUT_LE16(&subelem[2], gsm->GN & 0x03);
 	subelem[4] = gsm->GTK_len;
 	wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, subelem + 5);
-	if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len, key_len / 8, key,
-		     subelem + 13)) {
+	if (aes_wrap(kek, kek_len, key_len / 8, key, subelem + 13)) {
+		wpa_printf(MSG_DEBUG,
+			   "FT: GTK subelem encryption failed: kek_len=%d",
+			   (int) kek_len);
 		os_free(subelem);
 		return NULL;
 	}
@@ -1630,10 +2171,23 @@
 	u8 *subelem, *pos;
 	struct wpa_group *gsm = sm->group;
 	size_t subelem_len;
+	const u8 *kek;
+	size_t kek_len;
+	size_t igtk_len;
+
+	if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+		kek = sm->PTK.kek2;
+		kek_len = sm->PTK.kek2_len;
+	} else {
+		kek = sm->PTK.kek;
+		kek_len = sm->PTK.kek_len;
+	}
+
+	igtk_len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
 
 	/* Sub-elem ID[1] | Length[1] | KeyID[2] | IPN[6] | Key Length[1] |
 	 * Key[16+8] */
-	subelem_len = 1 + 1 + 2 + 6 + 1 + WPA_IGTK_LEN + 8;
+	subelem_len = 1 + 1 + 2 + 6 + 1 + igtk_len + 8;
 	subelem = os_zalloc(subelem_len);
 	if (subelem == NULL)
 		return NULL;
@@ -1645,9 +2199,12 @@
 	pos += 2;
 	wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos);
 	pos += 6;
-	*pos++ = WPA_IGTK_LEN;
-	if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len, WPA_IGTK_LEN / 8,
+	*pos++ = igtk_len;
+	if (aes_wrap(kek, kek_len, igtk_len / 8,
 		     gsm->IGTK[gsm->GN_igtk - 4], pos)) {
+		wpa_printf(MSG_DEBUG,
+			   "FT: IGTK subelem encryption failed: kek_len=%d",
+			   (int) kek_len);
 		os_free(subelem);
 		return NULL;
 	}
@@ -1794,17 +2351,21 @@
 				 const u8 *req_ies, size_t req_ies_len)
 {
 	u8 *end, *mdie, *ftie, *rsnie = NULL, *r0kh_id, *subelem = NULL;
+	u8 *fte_mic, *elem_count;
 	size_t mdie_len, ftie_len, rsnie_len = 0, r0kh_id_len, subelem_len = 0;
 	int res;
 	struct wpa_auth_config *conf;
-	struct rsn_ftie *_ftie;
 	struct wpa_ft_ies parse;
 	u8 *ric_start;
 	u8 *anonce, *snonce;
+	const u8 *kck;
+	size_t kck_len;
+	int use_sha384;
 
 	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))
@@ -1819,7 +2380,7 @@
 		 */
 		res = wpa_write_rsn_ie(conf, pos, end - pos, sm->pmk_r1_name);
 		if (res < 0)
-			return pos;
+			return NULL;
 		rsnie = pos;
 		rsnie_len = res;
 		pos += res;
@@ -1828,7 +2389,7 @@
 	/* Mobility Domain Information */
 	res = wpa_write_mdie(conf, pos, end - pos);
 	if (res < 0)
-		return pos;
+		return NULL;
 	mdie = pos;
 	mdie_len = res;
 	pos += res;
@@ -1836,6 +2397,11 @@
 	/* Fast BSS Transition Information */
 	if (auth_alg == WLAN_AUTH_FT) {
 		subelem = wpa_ft_gtk_subelem(sm, &subelem_len);
+		if (!subelem) {
+			wpa_printf(MSG_DEBUG,
+				   "FT: Failed to add GTK subelement");
+			return NULL;
+		}
 		r0kh_id = sm->r0kh_id;
 		r0kh_id_len = sm->r0kh_id_len;
 		anonce = sm->ANonce;
@@ -1847,14 +2413,16 @@
 			u8 *nbuf;
 			igtk = wpa_ft_igtk_subelem(sm, &igtk_len);
 			if (igtk == NULL) {
+				wpa_printf(MSG_DEBUG,
+					   "FT: Failed to add IGTK subelement");
 				os_free(subelem);
-				return pos;
+				return NULL;
 			}
 			nbuf = os_realloc(subelem, subelem_len + igtk_len);
 			if (nbuf == NULL) {
 				os_free(subelem);
 				os_free(igtk);
-				return pos;
+				return NULL;
 			}
 			subelem = nbuf;
 			os_memcpy(subelem + subelem_len, igtk, igtk_len);
@@ -1868,44 +2436,66 @@
 		anonce = NULL;
 		snonce = NULL;
 	}
-	res = wpa_write_ftie(conf, r0kh_id, r0kh_id_len, anonce, snonce, pos,
-			     end - pos, subelem, subelem_len);
+	res = wpa_write_ftie(conf, use_sha384, r0kh_id, r0kh_id_len,
+			     anonce, snonce, pos, end - pos,
+			     subelem, subelem_len);
 	os_free(subelem);
 	if (res < 0)
-		return pos;
+		return NULL;
 	ftie = pos;
 	ftie_len = res;
 	pos += res;
 
-	_ftie = (struct rsn_ftie *) (ftie + 2);
+	if (use_sha384) {
+		struct rsn_ftie_sha384 *_ftie =
+			(struct rsn_ftie_sha384 *) (ftie + 2);
+
+		fte_mic = _ftie->mic;
+		elem_count = &_ftie->mic_control[1];
+	} else {
+		struct rsn_ftie *_ftie = (struct rsn_ftie *) (ftie + 2);
+
+		fte_mic = _ftie->mic;
+		elem_count = &_ftie->mic_control[1];
+	}
 	if (auth_alg == WLAN_AUTH_FT)
-		_ftie->mic_control[1] = 3; /* Information element count */
+		*elem_count = 3; /* Information element count */
 
 	ric_start = pos;
-	if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse) == 0 && parse.ric) {
+	if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse, use_sha384) == 0
+	    && parse.ric) {
 		pos = wpa_ft_process_ric(sm, pos, end, parse.ric,
 					 parse.ric_len);
 		if (auth_alg == WLAN_AUTH_FT)
-			_ftie->mic_control[1] +=
+			*elem_count +=
 				ieee802_11_ie_count(ric_start,
 						    pos - ric_start);
 	}
 	if (ric_start == pos)
 		ric_start = NULL;
 
+	if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+		kck = sm->PTK.kck2;
+		kck_len = sm->PTK.kck2_len;
+	} else {
+		kck = sm->PTK.kck;
+		kck_len = sm->PTK.kck_len;
+	}
 	if (auth_alg == WLAN_AUTH_FT &&
-	    wpa_ft_mic(sm->PTK.kck, sm->PTK.kck_len, sm->addr,
-		       sm->wpa_auth->addr, 6,
+	    wpa_ft_mic(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,
-		       _ftie->mic) < 0)
+		       fte_mic) < 0) {
 		wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC");
+		return NULL;
+	}
 
 	os_free(sm->assoc_resp_ftie);
 	sm->assoc_resp_ftie = os_malloc(ftie_len);
-	if (sm->assoc_resp_ftie)
-		os_memcpy(sm->assoc_resp_ftie, ftie, ftie_len);
+	if (!sm->assoc_resp_ftie)
+		return NULL;
+	os_memcpy(sm->assoc_resp_ftie, ftie, ftie_len);
 
 	return pos;
 }
@@ -1964,7 +2554,12 @@
 /* Derive PMK-R1 from PSK, check all available PSK */
 static int wpa_ft_psk_pmk_r1(struct wpa_state_machine *sm,
 			     const u8 *req_pmk_r1_name,
-			     u8 *out_pmk_r1, int *out_pairwise)
+			     u8 *out_pmk_r1, int *out_pairwise,
+			     struct vlan_description *out_vlan,
+			     const u8 **out_identity, size_t *out_identity_len,
+			     const u8 **out_radius_cui,
+			     size_t *out_radius_cui_len,
+			     int *out_session_timeout)
 {
 	const u8 *pmk = NULL;
 	u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN];
@@ -1988,9 +2583,9 @@
 
 		if (wpa_derive_pmk_r0(pmk, PMK_LEN, ssid, ssid_len, mdid, r0kh,
 				      r0kh_len, sm->addr,
-				      pmk_r0, pmk_r0_name) < 0 ||
-		    wpa_derive_pmk_r1(pmk_r0, pmk_r0_name, r1kh, sm->addr,
-				      pmk_r1, pmk_r1_name) < 0 ||
+				      pmk_r0, pmk_r0_name, 0) < 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,
 				    WPA_PMK_NAME_LEN) != 0)
 			continue;
@@ -2001,6 +2596,28 @@
 		os_memcpy(out_pmk_r1, pmk_r1, PMK_LEN);
 		if (out_pairwise)
 			*out_pairwise = pairwise;
+		if (out_vlan &&
+		    wpa_ft_get_vlan(sm->wpa_auth, sm->addr, out_vlan) < 0) {
+			wpa_printf(MSG_DEBUG, "FT: vlan not available for STA "
+				   MACSTR, MAC2STR(sm->addr));
+			return -1;
+		}
+
+		if (out_identity && out_identity_len) {
+			*out_identity_len = wpa_ft_get_identity(
+				sm->wpa_auth, sm->addr, out_identity);
+		}
+
+		if (out_radius_cui && out_radius_cui_len) {
+			*out_radius_cui_len = wpa_ft_get_radius_cui(
+				sm->wpa_auth, sm->addr, out_radius_cui);
+		}
+
+		if (out_session_timeout) {
+			*out_session_timeout = wpa_ft_get_session_timeout(
+				sm->wpa_auth, sm->addr);
+		}
+
 		return 0;
 	}
 
@@ -2029,6 +2646,10 @@
 	}
 	if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
 		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
+#ifdef CONFIG_SHA384
+	else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X_SHA384;
+#endif /* CONFIG_SHA384 */
 	else if (key_mgmt & WPA_KEY_MGMT_FT_PSK)
 		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_PSK;
 #ifdef CONFIG_FILS
@@ -2050,20 +2671,99 @@
 }
 
 
+static int wpa_ft_local_derive_pmk_r1(struct wpa_authenticator *wpa_auth,
+				      struct wpa_state_machine *sm,
+				      const u8 *r0kh_id, size_t r0kh_id_len,
+				      const u8 *req_pmk_r0_name,
+				      const u8 *req_pmk_r1_name,
+				      u8 *out_pmk_r1, int *out_pairwise,
+				      struct vlan_description *vlan,
+				      const u8 **identity, size_t *identity_len,
+				      const u8 **radius_cui,
+				      size_t *radius_cui_len,
+				      int *out_session_timeout)
+{
+	struct wpa_auth_config *conf = &wpa_auth->conf;
+	const struct wpa_ft_pmk_r0_sa *r0;
+	u8 pmk_r1_name[WPA_PMK_NAME_LEN];
+	int expires_in = 0;
+	int session_timeout = 0;
+	struct os_reltime now;
+
+	if (conf->r0_key_holder_len != r0kh_id_len ||
+	    os_memcmp(conf->r0_key_holder, r0kh_id, conf->r0_key_holder_len) !=
+	    0)
+		return -1; /* not our R0KH-ID */
+
+	wpa_printf(MSG_DEBUG, "FT: STA R0KH-ID matching local configuration");
+	if (wpa_ft_fetch_pmk_r0(sm->wpa_auth, sm->addr, req_pmk_r0_name, &r0) <
+	    0)
+		return -1; /* no matching PMKR0Name in local cache */
+
+	wpa_printf(MSG_DEBUG, "FT: Requested PMKR0Name found in local cache");
+
+	if (wpa_derive_pmk_r1(r0->pmk_r0, r0->pmk_r0_len, r0->pmk_r0_name,
+			      conf->r1_key_holder,
+			      sm->addr, out_pmk_r1, pmk_r1_name) < 0)
+		return -1;
+	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", out_pmk_r1, r0->pmk_r0_len);
+	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN);
+
+	os_get_reltime(&now);
+	if (r0->expiration)
+		expires_in = r0->expiration - now.sec;
+
+	if (r0->session_timeout)
+		session_timeout = r0->session_timeout - now.sec;
+
+	wpa_ft_store_pmk_r1(wpa_auth, sm->addr, out_pmk_r1, r0->pmk_r0_len,
+			    pmk_r1_name,
+			    sm->pairwise, r0->vlan, expires_in, session_timeout,
+			    r0->identity, r0->identity_len,
+			    r0->radius_cui, r0->radius_cui_len);
+
+	*out_pairwise = sm->pairwise;
+	if (vlan) {
+		if (r0->vlan)
+			*vlan = *r0->vlan;
+		else
+			os_memset(vlan, 0, sizeof(*vlan));
+	}
+
+	if (identity && identity_len) {
+		*identity = r0->identity;
+		*identity_len = r0->identity_len;
+	}
+
+	if (radius_cui && radius_cui_len) {
+		*radius_cui = r0->radius_cui;
+		*radius_cui_len = r0->radius_cui_len;
+	}
+
+	*out_session_timeout = session_timeout;
+
+	return 0;
+}
+
+
 static int wpa_ft_process_auth_req(struct wpa_state_machine *sm,
 				   const u8 *ies, size_t ies_len,
 				   u8 **resp_ies, size_t *resp_ies_len)
 {
 	struct rsn_mdie *mdie;
-	struct rsn_ftie *ftie;
-	u8 pmk_r1[PMK_LEN], pmk_r1_name[WPA_PMK_NAME_LEN];
+	u8 pmk_r1[PMK_LEN_MAX], pmk_r1_name[WPA_PMK_NAME_LEN];
 	u8 ptk_name[WPA_PMK_NAME_LEN];
 	struct wpa_auth_config *conf;
 	struct wpa_ft_ies parse;
 	size_t buflen;
 	int ret;
 	u8 *pos, *end;
-	int pairwise;
+	int pairwise, session_timeout = 0;
+	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;
 
 	*resp_ies = NULL;
 	*resp_ies_len = 0;
@@ -2074,10 +2774,12 @@
 	wpa_hexdump(MSG_DEBUG, "FT: Received authentication frame IEs",
 		    ies, ies_len);
 
-	if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) {
+	if (wpa_ft_parse_ies(ies, ies_len, &parse, -1)) {
 		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) ||
@@ -2088,13 +2790,27 @@
 		return WLAN_STATUS_INVALID_MDIE;
 	}
 
-	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;
-	}
+	if (use_sha384) {
+		struct rsn_ftie_sha384 *ftie;
 
-	os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
+		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.r0kh_id == NULL) {
 		wpa_printf(MSG_DEBUG, "FT: Invalid FTIE - no R0KH-ID");
@@ -2118,17 +2834,38 @@
 		    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) < 0)
+				   pmk_r1_name, use_sha384) < 0)
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 	wpa_hexdump(MSG_DEBUG, "FT: Derived requested PMKR1Name",
 		    pmk_r1_name, WPA_PMK_NAME_LEN);
 
 	if (conf->ft_psk_generate_local &&
 	    wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) {
-		if (wpa_ft_psk_pmk_r1(sm, pmk_r1_name, pmk_r1, &pairwise) < 0)
+		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;
+		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, &pairwise) < 0) {
+				       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) {
+			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");
@@ -2136,9 +2873,12 @@
 		}
 
 		return -1; /* Status pending */
+	} else {
+		wpa_printf(MSG_DEBUG, "FT: Found PMKR1Name from local cache");
 	}
 
-	wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, PMK_LEN);
+pmk_r1_derived:
+	wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, pmk_r1_len);
 	sm->pmk_r1_name_valid = 1;
 	os_memcpy(sm->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN);
 
@@ -2153,8 +2893,8 @@
 	wpa_hexdump(MSG_DEBUG, "FT: Generated ANonce",
 		    sm->ANonce, WPA_NONCE_LEN);
 
-	if (wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr,
-			      sm->wpa_auth->addr, pmk_r1_name,
+	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,
 			      pairwise) < 0)
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -2164,6 +2904,19 @@
 	sm->tk_already_set = FALSE;
 	wpa_ft_install_ptk(sm);
 
+	if (wpa_ft_set_vlan(sm->wpa_auth, sm->addr, &vlan) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to configure VLAN");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+	if (wpa_ft_set_identity(sm->wpa_auth, sm->addr,
+				identity, identity_len) < 0 ||
+	    wpa_ft_set_radius_cui(sm->wpa_auth, sm->addr,
+				  radius_cui, radius_cui_len) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to configure identity/CUI");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+	wpa_ft_set_session_timeout(sm->wpa_auth, sm->addr, session_timeout);
+
 	buflen = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) +
 		2 + FT_R1KH_ID_LEN + 200;
 	*resp_ies = os_zalloc(buflen);
@@ -2183,7 +2936,7 @@
 		goto fail;
 	pos += ret;
 
-	ret = wpa_write_ftie(conf, parse.r0kh_id, parse.r0kh_id_len,
+	ret = wpa_write_ftie(conf, use_sha384, parse.r0kh_id, parse.r0kh_id_len,
 			     sm->ANonce, sm->SNonce, pos, end - pos, NULL, 0);
 	if (ret < 0)
 		goto fail;
@@ -2247,17 +3000,23 @@
 {
 	struct wpa_ft_ies parse;
 	struct rsn_mdie *mdie;
-	struct rsn_ftie *ftie;
 	u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
 	size_t mic_len = 16;
 	unsigned int count;
+	const u8 *kck;
+	size_t kck_len;
+	int use_sha384;
+	const u8 *anonce, *snonce, *fte_mic;
+	u8 fte_elem_count;
 
 	if (sm == NULL)
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 
+	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) < 0) {
+	if (wpa_ft_parse_ies(ies, ies_len, &parse, use_sha384) < 0) {
 		wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs");
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 	}
@@ -2288,25 +3047,47 @@
 		return WLAN_STATUS_INVALID_MDIE;
 	}
 
-	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;
+	if (use_sha384) {
+		struct rsn_ftie_sha384 *ftie;
+
+		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;
+		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;
+		fte_elem_count = ftie->mic_control[1];
+		fte_mic = ftie->mic;
 	}
 
-	if (os_memcmp(ftie->snonce, sm->SNonce, WPA_NONCE_LEN) != 0) {
+	if (os_memcmp(snonce, sm->SNonce, WPA_NONCE_LEN) != 0) {
 		wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE");
 		wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
-			    ftie->snonce, WPA_NONCE_LEN);
+			    snonce, WPA_NONCE_LEN);
 		wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
 			    sm->SNonce, WPA_NONCE_LEN);
 		return WLAN_STATUS_INVALID_FTIE;
 	}
 
-	if (os_memcmp(ftie->anonce, sm->ANonce, WPA_NONCE_LEN) != 0) {
+	if (os_memcmp(anonce, sm->ANonce, WPA_NONCE_LEN) != 0) {
 		wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE");
 		wpa_hexdump(MSG_DEBUG, "FT: Received ANonce",
-			    ftie->anonce, WPA_NONCE_LEN);
+			    anonce, WPA_NONCE_LEN);
 		wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce",
 			    sm->ANonce, WPA_NONCE_LEN);
 		return WLAN_STATUS_INVALID_FTIE;
@@ -2357,15 +3138,21 @@
 	count = 3;
 	if (parse.ric)
 		count += ieee802_11_ie_count(parse.ric, parse.ric_len);
-	if (ftie->mic_control[1] != count) {
+	if (fte_elem_count != count) {
 		wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC "
 			   "Control: received %u expected %u",
-			   ftie->mic_control[1], count);
+			   fte_elem_count, count);
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 	}
 
-	if (wpa_ft_mic(sm->PTK.kck, sm->PTK.kck_len, sm->addr,
-		       sm->wpa_auth->addr, 5,
+	if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+		kck = sm->PTK.kck2;
+		kck_len = sm->PTK.kck2_len;
+	} else {
+		kck = sm->PTK.kck;
+		kck_len = sm->PTK.kck_len;
+	}
+	if (wpa_ft_mic(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,
@@ -2375,12 +3162,12 @@
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 	}
 
-	if (os_memcmp_const(mic, ftie->mic, mic_len) != 0) {
+	if (os_memcmp_const(mic, 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",
-			    ftie->mic, mic_len);
+			    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);
@@ -2579,33 +3366,65 @@
 			       const u8 *src_addr, u8 type,
 			       u8 **packet, size_t *packet_len)
 {
-	u8 pmk_r1[PMK_LEN];
+	u8 pmk_r1[PMK_LEN_MAX];
+	size_t pmk_r1_len = pmk_r0->pmk_r0_len;
 	u8 pmk_r1_name[WPA_PMK_NAME_LEN];
 	u8 f_pairwise[sizeof(le16)];
+	u8 f_expires_in[sizeof(le16)];
+	u8 f_session_timeout[sizeof(le32)];
+	int expires_in;
+	int session_timeout;
+	struct os_reltime now;
 	int ret;
 	struct tlv_list sess_tlv[] = {
-		{ .type = FT_RRB_PMK_R1, .len = sizeof(pmk_r1),
+		{ .type = FT_RRB_PMK_R1, .len = pmk_r1_len,
 		  .data = pmk_r1 },
 		{ .type = FT_RRB_PMK_R1_NAME, .len = sizeof(pmk_r1_name),
 		  .data = pmk_r1_name },
 		{ .type = FT_RRB_PAIRWISE, .len = sizeof(f_pairwise),
 		  .data = f_pairwise },
+		{ .type = FT_RRB_EXPIRES_IN, .len = sizeof(f_expires_in),
+		  .data = f_expires_in },
+		{ .type = FT_RRB_IDENTITY, .len = pmk_r0->identity_len,
+		  .data = pmk_r0->identity },
+		{ .type = FT_RRB_RADIUS_CUI, .len = pmk_r0->radius_cui_len,
+		  .data = pmk_r0->radius_cui },
+		{ .type = FT_RRB_SESSION_TIMEOUT,
+		  .len = sizeof(f_session_timeout),
+		  .data = f_session_timeout },
 		{ .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
 	};
 
-	if (!pmk_r0)
-		return wpa_ft_rrb_build(key, key_len, tlvs, NULL, tlv_auth,
-					src_addr, type, packet, packet_len);
-
-	if (wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_name, r1kh_id,
+	if (wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_len,
+			      pmk_r0->pmk_r0_name, r1kh_id,
 			      s1kh_id, pmk_r1, pmk_r1_name) < 0)
 		return -1;
-	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, PMK_LEN);
-	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 (for peer AP)",
+			pmk_r1, pmk_r1_len);
+	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name (for peer AP)",
+		    pmk_r1_name, WPA_PMK_NAME_LEN);
 	WPA_PUT_LE16(f_pairwise, pmk_r0->pairwise);
 
+	os_get_reltime(&now);
+	if (pmk_r0->expiration > now.sec)
+		expires_in = pmk_r0->expiration - now.sec;
+	else if (pmk_r0->expiration)
+		expires_in = 1;
+	else
+		expires_in = 0;
+	WPA_PUT_LE16(f_expires_in, expires_in);
+
+	if (pmk_r0->session_timeout > now.sec)
+		session_timeout = pmk_r0->session_timeout - now.sec;
+	else if (pmk_r0->session_timeout)
+		session_timeout = 1;
+	else
+		session_timeout = 0;
+	WPA_PUT_LE32(f_session_timeout, session_timeout);
+
 	ret = wpa_ft_rrb_build(key, key_len, tlvs, sess_tlv, tlv_auth,
-			       src_addr, type, packet, packet_len);
+			       pmk_r0->vlan, src_addr, type,
+			       packet, packet_len);
 
 	os_memset(pmk_r1, 0, sizeof(pmk_r1));
 
@@ -2736,13 +3555,18 @@
 	resp_auth[4].len = 0;
 	resp_auth[4].data = NULL;
 
-	if (wpa_ft_fetch_pmk_r0(wpa_auth, f_s1kh_id, f_pmk_r0_name, &r0) < 0)
+	if (wpa_ft_fetch_pmk_r0(wpa_auth, f_s1kh_id, f_pmk_r0_name, &r0) < 0) {
 		wpa_printf(MSG_DEBUG, "FT: No matching PMK-R0-Name found");
-
-	ret = wpa_ft_rrb_build_r0(key, key_len, resp, r0, f_r1kh_id, f_s1kh_id,
-				  resp_auth, wpa_auth->addr,
-				  FT_PACKET_R0KH_R1KH_RESP,
-				  &packet, &packet_len);
+		ret = wpa_ft_rrb_build(key, key_len, resp, NULL, resp_auth,
+				       NULL, wpa_auth->addr,
+				       FT_PACKET_R0KH_R1KH_RESP,
+				       &packet, &packet_len);
+	} else {
+		ret = wpa_ft_rrb_build_r0(key, key_len, resp, r0, f_r1kh_id,
+					  f_s1kh_id, resp_auth, wpa_auth->addr,
+					  FT_PACKET_R0KH_R1KH_RESP,
+					  &packet, &packet_len);
+	}
 
 	if (!ret)
 		wpa_ft_rrb_oui_send(wpa_auth, src_addr,
@@ -2780,10 +3604,20 @@
 	int seq_ret;
 	const u8 *f_r1kh_id, *f_s1kh_id, *f_r0kh_id;
 	const u8 *f_pmk_r1_name, *f_pairwise, *f_pmk_r1;
+	const u8 *f_expires_in;
 	size_t f_r1kh_id_len, f_s1kh_id_len, f_r0kh_id_len;
+	const u8 *f_identity, *f_radius_cui;
+	const u8 *f_session_timeout;
 	size_t f_pmk_r1_name_len, f_pairwise_len, f_pmk_r1_len;
+	size_t f_expires_in_len;
+	size_t f_identity_len, f_radius_cui_len;
+	size_t f_session_timeout_len;
 	int pairwise;
 	int ret = -1;
+	int expires_in;
+	int session_timeout;
+	struct vlan_description vlan;
+	size_t pmk_r1_len;
 
 	RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, msgtype, -1);
 	wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID", f_r0kh_id, f_r0kh_id_len);
@@ -2862,13 +3696,58 @@
 	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name",
 		    f_pmk_r1_name, WPA_PMK_NAME_LEN);
 
-	RRB_GET(FT_RRB_PMK_R1, pmk_r1, msgtype, PMK_LEN);
-	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f_pmk_r1, PMK_LEN);
+	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))
+		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);
 
 	pairwise = WPA_GET_LE16(f_pairwise);
 
-	if (wpa_ft_store_pmk_r1(wpa_auth, f_s1kh_id, f_pmk_r1, f_pmk_r1_name,
-				pairwise) < 0)
+	RRB_GET_OPTIONAL(FT_RRB_EXPIRES_IN, expires_in, msgtype,
+			 sizeof(le16));
+	if (f_expires_in)
+		expires_in = WPA_GET_LE16(f_expires_in);
+	else
+		expires_in = 0;
+
+	wpa_printf(MSG_DEBUG, "FT: PMK-R1 %s - expires_in=%d", msgtype,
+		   expires_in);
+
+	if (wpa_ft_rrb_get_tlv_vlan(plain, plain_len, &vlan) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Cannot parse vlan");
+		wpa_ft_rrb_dump(plain, plain_len);
+		goto out;
+	}
+
+	wpa_printf(MSG_DEBUG, "FT: vlan %d%s",
+		   le_to_host16(vlan.untagged), vlan.tagged[0] ? "+" : "");
+
+	RRB_GET_OPTIONAL(FT_RRB_IDENTITY, identity, msgtype, -1);
+	if (f_identity)
+		wpa_hexdump_ascii(MSG_DEBUG, "FT: Identity", f_identity,
+				  f_identity_len);
+
+	RRB_GET_OPTIONAL(FT_RRB_RADIUS_CUI, radius_cui, msgtype, -1);
+	if (f_radius_cui)
+		wpa_hexdump_ascii(MSG_DEBUG, "FT: CUI", f_radius_cui,
+				  f_radius_cui_len);
+
+	RRB_GET_OPTIONAL(FT_RRB_SESSION_TIMEOUT, session_timeout, msgtype,
+			 sizeof(le32));
+	if (f_session_timeout)
+		session_timeout = WPA_GET_LE32(f_session_timeout);
+	else
+		session_timeout = 0;
+	wpa_printf(MSG_DEBUG, "FT: session_timeout %d", session_timeout);
+
+	if (wpa_ft_store_pmk_r1(wpa_auth, f_s1kh_id, f_pmk_r1, pmk_r1_len,
+				f_pmk_r1_name,
+				pairwise, &vlan, expires_in, session_timeout,
+				f_identity, f_identity_len, f_radius_cui,
+				f_radius_cui_len) < 0)
 		goto out;
 
 	ret = 0;
@@ -3178,7 +4057,7 @@
 	seq_resp_auth[4].len = 0;
 	seq_resp_auth[4].data = NULL;
 
-	if (wpa_ft_rrb_build(key, key_len, NULL, NULL, seq_resp_auth,
+	if (wpa_ft_rrb_build(key, key_len, NULL, NULL, seq_resp_auth, NULL,
 			     wpa_auth->addr, FT_PACKET_R0KH_R1KH_SEQ_RESP,
 			     &packet, &packet_len) < 0)
 		goto out;
@@ -3527,7 +4406,8 @@
 
 void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr)
 {
-	struct wpa_ft_pmk_r0_sa *r0;
+	struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
+	struct wpa_ft_pmk_r0_sa *r0, *r0found = NULL;
 	struct ft_remote_r1kh *r1kh;
 
 	if (!wpa_auth->conf.pmk_r1_push)
@@ -3535,13 +4415,14 @@
 	if (!wpa_auth->conf.r1kh_list)
 		return;
 
-	r0 = wpa_auth->ft_pmk_cache->pmk_r0;
-	while (r0) {
-		if (os_memcmp(r0->spa, addr, ETH_ALEN) == 0)
+	dl_list_for_each(r0, &cache->pmk_r0, struct wpa_ft_pmk_r0_sa, list) {
+		if (os_memcmp(r0->spa, addr, ETH_ALEN) == 0) {
+			r0found = r0;
 			break;
-		r0 = r0->next;
+		}
 	}
 
+	r0 = r0found;
 	if (r0 == NULL || r0->pmk_r1_pushed)
 		return;
 	r0->pmk_r1_pushed = 1;
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 98133a0..8127403 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -59,6 +59,7 @@
 #ifdef CONFIG_IEEE80211W
 	wconf->ieee80211w = conf->ieee80211w;
 	wconf->group_mgmt_cipher = conf->group_mgmt_cipher;
+	wconf->sae_require_mfp = conf->sae_require_mfp;
 #endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_IEEE80211R_AP
 	wconf->ssid_len = conf->ssid.ssid_len;
@@ -75,6 +76,7 @@
 	}
 	os_memcpy(wconf->r1_key_holder, conf->r1_key_holder, FT_R1KH_ID_LEN);
 	wconf->r0_key_lifetime = conf->r0_key_lifetime;
+	wconf->r1_max_key_lifetime = conf->r1_max_key_lifetime;
 	wconf->reassociation_deadline = conf->reassociation_deadline;
 	wconf->rkh_pos_timeout = conf->rkh_pos_timeout;
 	wconf->rkh_neg_timeout = conf->rkh_neg_timeout;
@@ -364,10 +366,10 @@
 			sta->last_tk_len = key_len;
 		}
 #ifdef CONFIG_IEEE80211W
-	} else if (alg == WPA_CIPHER_AES_128_CMAC ||
-		   alg == WPA_CIPHER_BIP_GMAC_128 ||
-		   alg == WPA_CIPHER_BIP_GMAC_256 ||
-		   alg == WPA_CIPHER_BIP_CMAC_256) {
+	} else if (alg == WPA_ALG_IGTK ||
+		   alg == WPA_ALG_BIP_GMAC_128 ||
+		   alg == WPA_ALG_BIP_GMAC_256 ||
+		   alg == WPA_ALG_BIP_CMAC_256) {
 		hapd->last_igtk_alg = alg;
 		hapd->last_igtk_key_idx = idx;
 		if (key)
@@ -834,6 +836,244 @@
 }
 
 
+static int hostapd_wpa_auth_set_vlan(void *ctx, const u8 *sta_addr,
+				     struct vlan_description *vlan)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+
+	sta = ap_get_sta(hapd, sta_addr);
+	if (!sta || !sta->wpa_sm)
+		return -1;
+
+	if (vlan->notempty &&
+	    !hostapd_vlan_valid(hapd->conf->vlan, vlan)) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO,
+			       "Invalid VLAN %d%s received from FT",
+			       vlan->untagged, vlan->tagged[0] ? "+" : "");
+		return -1;
+	}
+
+	if (ap_sta_set_vlan(hapd, sta, vlan) < 0)
+		return -1;
+	/* Configure wpa_group for GTK but ignore error due to driver not
+	 * knowing this STA. */
+	ap_sta_bind_vlan(hapd, sta);
+
+	if (sta->vlan_id)
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id);
+
+	return 0;
+}
+
+
+static int hostapd_wpa_auth_get_vlan(void *ctx, const u8 *sta_addr,
+				     struct vlan_description *vlan)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+
+	sta = ap_get_sta(hapd, sta_addr);
+	if (!sta)
+		return -1;
+
+	if (sta->vlan_desc)
+		*vlan = *sta->vlan_desc;
+	else
+		os_memset(vlan, 0, sizeof(*vlan));
+
+	return 0;
+}
+
+
+static int
+hostapd_wpa_auth_set_identity(void *ctx, const u8 *sta_addr,
+			      const u8 *identity, size_t identity_len)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+
+	sta = ap_get_sta(hapd, sta_addr);
+	if (!sta)
+		return -1;
+
+	os_free(sta->identity);
+	sta->identity = NULL;
+
+	if (sta->eapol_sm) {
+		os_free(sta->eapol_sm->identity);
+		sta->eapol_sm->identity = NULL;
+		sta->eapol_sm->identity_len = 0;
+	}
+
+	if (!identity_len)
+		return 0;
+
+	/* sta->identity is NULL terminated */
+	sta->identity = os_zalloc(identity_len + 1);
+	if (!sta->identity)
+		return -1;
+	os_memcpy(sta->identity, identity, identity_len);
+
+	if (sta->eapol_sm) {
+		sta->eapol_sm->identity = os_zalloc(identity_len);
+		if (!sta->eapol_sm->identity)
+			return -1;
+		os_memcpy(sta->eapol_sm->identity, identity, identity_len);
+		sta->eapol_sm->identity_len = identity_len;
+	}
+
+	return 0;
+}
+
+
+static size_t
+hostapd_wpa_auth_get_identity(void *ctx, const u8 *sta_addr, const u8 **buf)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+	size_t len;
+	char *identity;
+
+	sta = ap_get_sta(hapd, sta_addr);
+	if (!sta)
+		return 0;
+
+	*buf = ieee802_1x_get_identity(sta->eapol_sm, &len);
+	if (*buf && len)
+		return len;
+
+	if (!sta->identity) {
+		*buf = NULL;
+		return 0;
+	}
+
+	identity = sta->identity;
+	len = os_strlen(identity);
+	*buf = (u8 *) identity;
+
+	return len;
+}
+
+
+static int
+hostapd_wpa_auth_set_radius_cui(void *ctx, const u8 *sta_addr,
+				const u8 *radius_cui, size_t radius_cui_len)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+
+	sta = ap_get_sta(hapd, sta_addr);
+	if (!sta)
+		return -1;
+
+	os_free(sta->radius_cui);
+	sta->radius_cui = NULL;
+
+	if (sta->eapol_sm) {
+		wpabuf_free(sta->eapol_sm->radius_cui);
+		sta->eapol_sm->radius_cui = NULL;
+	}
+
+	if (!radius_cui)
+		return 0;
+
+	/* sta->radius_cui is NULL terminated */
+	sta->radius_cui = os_zalloc(radius_cui_len + 1);
+	if (!sta->radius_cui)
+		return -1;
+	os_memcpy(sta->radius_cui, radius_cui, radius_cui_len);
+
+	if (sta->eapol_sm) {
+		sta->eapol_sm->radius_cui = wpabuf_alloc_copy(radius_cui,
+							      radius_cui_len);
+		if (!sta->eapol_sm->radius_cui)
+			return -1;
+	}
+
+	return 0;
+}
+
+
+static size_t
+hostapd_wpa_auth_get_radius_cui(void *ctx, const u8 *sta_addr, const u8 **buf)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+	struct wpabuf *b;
+	size_t len;
+	char *radius_cui;
+
+	sta = ap_get_sta(hapd, sta_addr);
+	if (!sta)
+		return 0;
+
+	b = ieee802_1x_get_radius_cui(sta->eapol_sm);
+	if (b) {
+		len = wpabuf_len(b);
+		*buf = wpabuf_head(b);
+		return len;
+	}
+
+	if (!sta->radius_cui) {
+		*buf = NULL;
+		return 0;
+	}
+
+	radius_cui = sta->radius_cui;
+	len = os_strlen(radius_cui);
+	*buf = (u8 *) radius_cui;
+
+	return len;
+}
+
+
+static void hostapd_wpa_auth_set_session_timeout(void *ctx, const u8 *sta_addr,
+						 int session_timeout)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+
+	sta = ap_get_sta(hapd, sta_addr);
+	if (!sta)
+		return;
+
+	if (session_timeout) {
+		os_get_reltime(&sta->session_timeout);
+		sta->session_timeout.sec += session_timeout;
+		sta->session_timeout_set = 1;
+		ap_sta_session_timeout(hapd, sta, session_timeout);
+	} else {
+		sta->session_timeout_set = 0;
+		ap_sta_no_session_timeout(hapd, sta);
+	}
+}
+
+
+static int hostapd_wpa_auth_get_session_timeout(void *ctx, const u8 *sta_addr)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+	struct os_reltime now, remaining;
+
+	sta = ap_get_sta(hapd, sta_addr);
+	if (!sta || !sta->session_timeout_set)
+		return 0;
+
+	os_get_reltime(&now);
+	if (os_reltime_before(&sta->session_timeout, &now)) {
+		/* already expired, return >0 as timeout was set */
+		return 1;
+	}
+
+	os_reltime_sub(&sta->session_timeout, &now, &remaining);
+
+	return (remaining.sec > 0) ? remaining.sec : 1;
+}
+
+
 static void hostapd_rrb_receive(void *ctx, const u8 *src_addr, const u8 *buf,
 				size_t len)
 {
@@ -953,6 +1193,14 @@
 		.send_ft_action = hostapd_wpa_auth_send_ft_action,
 		.add_sta = hostapd_wpa_auth_add_sta,
 		.add_tspec = hostapd_wpa_auth_add_tspec,
+		.set_vlan = hostapd_wpa_auth_set_vlan,
+		.get_vlan = hostapd_wpa_auth_get_vlan,
+		.set_identity = hostapd_wpa_auth_set_identity,
+		.get_identity = hostapd_wpa_auth_get_identity,
+		.set_radius_cui = hostapd_wpa_auth_set_radius_cui,
+		.get_radius_cui = hostapd_wpa_auth_get_radius_cui,
+		.set_session_timeout = hostapd_wpa_auth_set_session_timeout,
+		.get_session_timeout = hostapd_wpa_auth_get_session_timeout,
 #endif /* CONFIG_IEEE80211R_AP */
 	};
 	const u8 *wpa_ie;
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index befa800..b1cea1b 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -58,6 +58,7 @@
 	u8 alt_replay_counter[WPA_REPLAY_COUNTER_LEN];
 	u8 PMK[PMK_LEN_MAX];
 	unsigned int pmk_len;
+	u8 pmkid[PMKID_LEN]; /* valid if pmkid_set == 1 */
 	struct wpa_ptk PTK;
 	Boolean PTK_valid;
 	Boolean pairwise_set;
@@ -90,6 +91,7 @@
 	unsigned int pmk_r1_name_valid:1;
 #endif /* CONFIG_IEEE80211R_AP */
 	unsigned int is_wnmsleep:1;
+	unsigned int pmkid_set:1;
 
 	u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN];
 	int req_replay_counter_used;
@@ -110,7 +112,8 @@
 	u32 dot11RSNAStatsTKIPRemoteMICFailures;
 
 #ifdef CONFIG_IEEE80211R_AP
-	u8 xxkey[PMK_LEN]; /* PSK or the second 256 bits of MSK */
+	u8 xxkey[PMK_LEN_MAX]; /* PSK or the second 256 bits of MSK, or the
+				* first 384 bits of MSK */
 	size_t xxkey_len;
 	u8 pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name derived from FT Auth
 					   * Request */
@@ -274,8 +277,8 @@
 
 #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, const u8 *r0kh_id,
-		   size_t r0kh_id_len,
+int wpa_write_ftie(struct wpa_auth_config *conf, int use_sha384,
+		   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);
@@ -284,9 +287,8 @@
 struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void);
 void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache);
 void wpa_ft_install_ptk(struct wpa_state_machine *sm);
-int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth,
-			const u8 *spa, const u8 *pmk_r0,
-			const u8 *pmk_r0_name, int pairwise);
+int wpa_ft_store_pmk_fils(struct wpa_state_machine *sm, const u8 *pmk_r0,
+			  const u8 *pmk_r0_name);
 #endif /* CONFIG_IEEE80211R_AP */
 
 #endif /* WPA_AUTH_I_H */
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index 0196d00..421dd5a 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -1,6 +1,6 @@
 /*
  * hostapd - WPA/RSN IE and KDE definitions
- * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2018, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -170,6 +170,13 @@
 		pos += RSN_SELECTOR_LEN;
 		num_suites++;
 	}
+#ifdef CONFIG_SHA384
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+#endif /* CONFIG_SHA384 */
 	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) {
 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK);
 		pos += RSN_SELECTOR_LEN;
@@ -248,6 +255,13 @@
 		num_suites++;
 	}
 #endif /* CONFIG_DPP */
+#ifdef CONFIG_HS20
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_OSEN) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OSEN);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+#endif /* CONFIG_HS20 */
 
 #ifdef CONFIG_RSN_TESTING
 	if (rsn_testing) {
@@ -559,6 +573,10 @@
 			selector = RSN_AUTH_KEY_MGMT_FILS_SHA256;
 #endif /* CONFIG_FILS */
 #ifdef CONFIG_IEEE80211R_AP
+#ifdef CONFIG_SHA384
+		else if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384)
+			selector = RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384;
+#endif /* CONFIG_SHA384 */
 		else if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
 			selector = RSN_AUTH_KEY_MGMT_FT_802_1X;
 		else if (data.key_mgmt & WPA_KEY_MGMT_FT_PSK)
@@ -588,6 +606,10 @@
 		else if (data.key_mgmt & WPA_KEY_MGMT_DPP)
 			selector = RSN_AUTH_KEY_MGMT_DPP;
 #endif /* CONFIG_DPP */
+#ifdef CONFIG_HS20
+		else if (data.key_mgmt & WPA_KEY_MGMT_OSEN)
+			selector = RSN_AUTH_KEY_MGMT_OSEN;
+#endif /* CONFIG_HS20 */
 		wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector;
 
 		selector = wpa_cipher_to_suite(WPA_PROTO_RSN,
@@ -661,6 +683,10 @@
 		sm->wpa_key_mgmt = WPA_KEY_MGMT_FILS_SHA256;
 #endif /* CONFIG_FILS */
 #ifdef CONFIG_IEEE80211R_AP
+#ifdef CONFIG_SHA384
+	else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X_SHA384;
+#endif /* CONFIG_SHA384 */
 	else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
 		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
 	else if (key_mgmt & WPA_KEY_MGMT_FT_PSK)
@@ -688,6 +714,10 @@
 	else if (key_mgmt & WPA_KEY_MGMT_DPP)
 		sm->wpa_key_mgmt = WPA_KEY_MGMT_DPP;
 #endif /* CONFIG_DPP */
+#ifdef CONFIG_HS20
+	else if (key_mgmt & WPA_KEY_MGMT_OSEN)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_OSEN;
+#endif /* CONFIG_HS20 */
 	else
 		sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
 
@@ -711,12 +741,6 @@
 			return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
 		}
 
-		if (ciphers & WPA_CIPHER_TKIP) {
-			wpa_printf(MSG_DEBUG, "Management frame protection "
-				   "cannot use TKIP");
-			return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
-		}
-
 		if (data.mgmt_group_cipher != wpa_auth->conf.group_mgmt_cipher)
 		{
 			wpa_printf(MSG_DEBUG, "Unsupported management group "
@@ -725,11 +749,27 @@
 		}
 	}
 
+#ifdef CONFIG_SAE
+	if (wpa_auth->conf.ieee80211w == MGMT_FRAME_PROTECTION_OPTIONAL &&
+	    wpa_key_mgmt_sae(sm->wpa_key_mgmt) &&
+	    !(data.capabilities & WPA_CAPABILITY_MFPC)) {
+		wpa_printf(MSG_DEBUG,
+			   "Management frame protection required with SAE, but client did not enable it");
+		return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
+	}
+#endif /* CONFIG_SAE */
+
 	if (wpa_auth->conf.ieee80211w == NO_MGMT_FRAME_PROTECTION ||
 	    !(data.capabilities & WPA_CAPABILITY_MFPC))
 		sm->mgmt_frame_prot = 0;
 	else
 		sm->mgmt_frame_prot = 1;
+
+	if (sm->mgmt_frame_prot && (ciphers & WPA_CIPHER_TKIP)) {
+		    wpa_printf(MSG_DEBUG,
+			       "Management frame protection cannot use TKIP");
+		    return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
+	}
 #endif /* CONFIG_IEEE80211W */
 
 #ifdef CONFIG_IEEE80211R_AP
@@ -817,6 +857,15 @@
 		os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmkid, PMKID_LEN);
 	}
 
+#ifdef CONFIG_SAE
+	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_SAE && data.num_pmkid &&
+	    !sm->pmksa) {
+		wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+				 "No PMKSA cache entry found for SAE");
+		return WPA_INVALID_PMKID;
+	}
+#endif /* CONFIG_SAE */
+
 #ifdef CONFIG_DPP
 	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && !sm->pmksa) {
 		wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
@@ -1018,6 +1067,19 @@
 				   const u8 *req_ies, size_t req_ies_len)
 {
 	int res;
+	struct wpa_auth_config *conf = &sm->wpa_auth->conf;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (conf->own_ie_override_len) {
+		if (max_len < conf->own_ie_override_len)
+			return NULL;
+		wpa_hexdump(MSG_DEBUG, "WPA: Forced own IE(s) for testing",
+			    conf->own_ie_override, conf->own_ie_override_len);
+		os_memcpy(pos, conf->own_ie_override,
+			  conf->own_ie_override_len);
+		return pos + conf->own_ie_override_len;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
 
 	res = wpa_write_rsn_ie(&sm->wpa_auth->conf, pos, max_len,
 			       sm->pmksa ? sm->pmksa->pmkid : NULL);
diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
index 95b40da..5ec0199 100644
--- a/src/ap/wps_hostapd.c
+++ b/src/ap/wps_hostapd.c
@@ -1064,7 +1064,9 @@
 		if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X)
 			wps->auth_types |= WPS_AUTH_WPA2;
 
-		if (conf->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) {
+		if (conf->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP |
+					  WPA_CIPHER_CCMP_256 |
+					  WPA_CIPHER_GCMP_256)) {
 			wps->encr_types |= WPS_ENCR_AES;
 			wps->encr_types_rsn |= WPS_ENCR_AES;
 		}