[wpa_supplicant] cumilative patch from commit bb945b98f

Bug: 275651698
Test: Connect to open, WPA2, WPA3 and passpoint network
Test: Establish P2P connection
Test: Basic SoftAp tests
Test: Regression test (b/275948027)

BYPASS_INCLUSIVE_LANGUAGE_REASON=Merged from opne source
bb945b98f Add 40 and 80 MHz channels 165 and 173 for 5 GHz IBSS/mesh
0059fa5ba 6 GHz: Fix secondary channel setting
744295c8b Add 6 GHz channel validation during channel switching
5349a45d3 Set interface state as inactive if mesh bringup fails
a4af79624 Handle signal termination in hostapd_cli for all cases
cf8f13ac8 Add support to send 320 MHz bandwidth through vendor subcmd
a0403c023 EHT: Validate the puncturing bitmap for ACS
af0f60e7d EHT: Calculate puncturing bitmap for ACS
f3206fbe9 EHT: Configuration option for ACS puncturing threshold
e3621867c EHT: Process puncturing bitmap from channel switch event
e277e577c nl80211: Send EHT puncturing bitmap to the driver for switch command
29a882bed EHT: Configure puncturing bitmap during channel switch
4942b19ff EHT: Send puncturing bitmap to the driver for AP bring up
f9fc2eabb EHT: Add puncturing bitmap to EHT Operation element
46a5d989d EHT: Downgrade bandwidths for VHT and HE when using puncturing
7618269ec EHT: Validate puncturing bitmap
9102fda31 EHT: Add configuration option for puncturing in AP mode
9e79439fc nl80211: Retrieve driver support for EHT puncturing
507be376c Sync with wireless-next.git include/uapi/linux/nl80211.h
591256a8c FILS: 320 MHz support in FD frame
903e3a1e6 FILS: Fix maximum NSS calculation for FD frame
ecae45ff6 FILS: Make HE a requirement for FILS discovery
4e86692ff AP: Fix 6 GHz AP setup after disable-enable
a34b8477a ml80211: Put wiphy idx to obtain correct country code
1491fc64a Define QCA vendor per-enum 64-bit pad attributes
55e31699e qca-vendor: Add QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_NF_CAL_VAL
b1f85957c Add QCA vendor commands to set and get MLO links state information
44b32a752 mesh: Add EHT support
c4cb62ca8 WPA_AUTH: MLO: Add functions to get the AA and SPA
cab963e9f AP: Split check_assoc_ies()
7a7a2256c common: Support parsing link specific association request
b39e35693 common: Add support for clearing elements
0b2fc4268 common: Split ieee8021_parse_elems()
df6561ec0 nl80211: AP MLD support for adding multi link stations
b8b4ceb8d nl80211: Properly stop and deinit MLO AP
2f8fc46ed nl80211: Provide link_id in EAPOL_RX and RX_MGMT events
821374d43 nl80211: Introduce and implement a callback to add an MLO link for AP MLD
47269be36 nl80211: Refactor i802_bss to support multiple links
eb146ee80 AP: Add some bridge port attribute settings
f628e6b30 nl80211: Make sure scan frequency debug buffer is NUL terminated
41d23254b nl80211: Fix frequencies array boundary check for scanned frequencies
a9012070a Android: Add wowlan_disconnect_on_deinit to template configuration
e2ea0fd70 EST: Write the RSA private key using the standard PRIVATE KEY format
bfd236df2 webkit2: Avoid deprecated function call
2c3202682 P2P: Filter out 6 GHz frequencies if not allowed for P2P connection
b2bf7e39e Update PMK in wpa_sm when roam+auth event indicated with authorized flag
6b9c86466 nl80211: Replace the channel flags for VHT support
6f63aca7b DPP: Allow both STA and AP configObject to be set
7292e30b7 DPP: Fix @CONF-OBJ-SEP@ parsing for multiple configs
c31600ce1 P2P: Allow GO BSSID to be specified for P2P_GROUP_ADD commands
0430756e6 P2P: Optimize join scan frequency
b3921db42 nl80211: Add frequency info in start AP command
40c139664 macsec_linux: Add support for MACsec hardware offload
6d24673ab mka: Allow configuration of MACsec hardware offload
3081a9cb6 hostapd: Output country_code and country3 when using STATUS
91ad7a309 FT: Store PTKSA entry for the correct BSSID in the FT protocol case
3f3e356fa Mark addr argument to storing PTKSA const
242c3ad99 FT: Store PTKSA from FT protocol
ba6954874 Mark wpa_auth_remove_ptksa() static
3b1ad1334 FT: Include KDK in FT specific PTK derivation on the AP
870a5bdc0 nl80211: Report guard interval and dual carrier modulation
edcad193a dbus: Add inactive time to D-Bus signal info
a678a510f dbus: Add D-Bus signal for PSK mismatch heuristics
691f729d5 P2P: Make invitation flow less aggressive
f4a7e2a07 Rework IBSS/mesh 80 MHz channel selection
f91f971bd Fix creating 6 GHz IBSS/mesh on 5/6 GHz-capable PHYs
c623cee42 Make arrays static const in ibss_mesh_select_*()
64043e615 Split ibss_mesh_setup_freq() into multiple functions
8085a7e65 wpa_supplicant: Add option to explicitly set 4addr mode
1ffc7d1c6 Apply bias towards 6 GHz in roaming
faa410292 WNM: Event report handling for BSS color collision and in-use
97405be96 Small textual improvements to wpa_supplicant man page
ec02a0e93 hostapd: Output hw_mode when using STATUS
390e24c6c EAP-TTLS server: Add Ident field to MS-CHAP-Error
4ae798a22 P2P: Pick the best driver pref freq for invitation process
6c75f1dfa Send broadcast Probe Response frames on the 6 GHz band
edfcb2f1a MLD STA: Indicate MLO support in NL80211_CMD_CONNECT
c91852044 MLD STA: Add support for SAE external authentication offload to userspace
575712450 qca-vendor: Add QCA_WLAN_VENDOR_MCC_QUOTA_TYPE_LOW_LATENCY
ba150059d FT: Store PMK-R0/PMK-R1 after EAPOL-Key msg 2/4 MIC validation
56662f36d Refine vendor subcmd QCA_NL80211_VENDOR_SUBCMD_ROAM_STATS
72b8193f4 MACsec: Remove EAP Session-Id length constraint
3915e8834 hostapd: Report error on unknown ACCEPT_ACL/DENY_ACL commands
2cff340d1 utils: Move log2pcap to python3
12de8112b Fix BSS age underflow
d31c2b43a Fix segfault in case of an invalid configuration
a32b424a3 MLD STA: Use AP MLD address in PMKSA cache attempts for driver-SME case
8c4790cef MLD STA: Store PMKSA with AP MLD address for MLO connection event
bf124a03d SAE: Update PT value at later point for SME cases, if needed
1aadcca0a P2P: Enable SAE-H2E for client when joining a 6 GHz group
37f8257c4 SAE: Extend automatic enabling of H2E on 6 GHz to additional cases
89377c6b9 OCV: Fix build without CONFIG_OCV=y
2e47ea22c P2P: Fix handling Service Discovery Response received by GO device
dc7e330e0 Set OCV capability based on Association Request frame RSNE
831be6514 WPS: Do not indicate incorrect PBC overlap based on partner link
c9fc12425 P2P: Make wpas_p2p_notif_pbc_overlap() static

Change-Id: I1eb61fc82b98b937a2ff37a30e60e28129fe143d
diff --git a/src/ap/acs.c b/src/ap/acs.c
index 8cb5813..1181c7d 100644
--- a/src/ap/acs.c
+++ b/src/ap/acs.c
@@ -326,6 +326,7 @@
 		dl_list_init(&chan->survey_list);
 		chan->flag |= HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED;
 		chan->min_nf = 0;
+		chan->punct_bitmap = 0;
 	}
 }
 
@@ -711,6 +712,62 @@
 #define ACS_24GHZ_PREFER_1_6_11 0.8
 #endif /* ACS_24GHZ_PREFER_1_6_11 */
 
+
+#ifdef CONFIG_IEEE80211BE
+static void acs_update_puncturing_bitmap(struct hostapd_iface *iface,
+					 struct hostapd_hw_modes *mode, u32 bw,
+					 int n_chans,
+					 struct hostapd_channel_data *chan,
+					 long double factor,
+					 int index_primary)
+{
+	struct hostapd_config *conf = iface->conf;
+	struct hostapd_channel_data *adj_chan = NULL, *first_chan = chan;
+	int i;
+	long double threshold;
+
+	/*
+	 * If threshold is 0 or user configured puncturing pattern is
+	 * available then don't add additional puncturing.
+	 */
+	if (!conf->punct_acs_threshold || conf->punct_bitmap)
+		return;
+
+	if (is_24ghz_mode(mode->mode) || bw < 80)
+		return;
+
+	threshold = factor * conf->punct_acs_threshold / 100;
+	for (i = 0; i < n_chans; i++) {
+		int adj_freq;
+
+		if (i == index_primary)
+			continue; /* Cannot puncture primary channel */
+
+		if (i > index_primary)
+			adj_freq = chan->freq + (i - index_primary) * 20;
+		else
+			adj_freq = chan->freq - (index_primary - i) * 20;
+
+		adj_chan = acs_find_chan(iface, adj_freq);
+		if (!adj_chan) {
+			chan->punct_bitmap = 0;
+			return;
+		}
+
+		if (i == 0)
+			first_chan = adj_chan;
+
+		if (adj_chan->interference_factor > threshold)
+			chan->punct_bitmap |= BIT(i);
+	}
+
+	if (!is_punct_bitmap_valid(bw, (chan->freq - first_chan->freq) / 20,
+				   chan->punct_bitmap))
+		chan->punct_bitmap = 0;
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
 static void
 acs_find_ideal_chan_mode(struct hostapd_iface *iface,
 			 struct hostapd_hw_modes *mode,
@@ -734,7 +791,13 @@
 		/* Since in the current ACS implementation the first channel is
 		 * always a primary channel, skip channels not available as
 		 * primary until more sophisticated channel selection is
-		 * implemented. */
+		 * implemented.
+		 *
+		 * If this implementation is changed to allow any channel in
+		 * the bandwidth to be the primary one, the last parameter to
+		 * acs_update_puncturing_bitmap() should be changed to the index
+		 * of the primary channel
+		 */
 		if (!chan_pri_allowed(chan))
 			continue;
 
@@ -913,8 +976,20 @@
 
 		if (acs_usable_chan(chan) &&
 		    (!*ideal_chan || factor < *ideal_factor)) {
+			/* Reset puncturing bitmap for the previous ideal
+			 * channel */
+			if (*ideal_chan)
+				(*ideal_chan)->punct_bitmap = 0;
+
 			*ideal_factor = factor;
 			*ideal_chan = chan;
+
+#ifdef CONFIG_IEEE80211BE
+			if (iface->conf->ieee80211be)
+				acs_update_puncturing_bitmap(iface, mode, bw,
+							     n_chans, chan,
+							     factor, 0);
+#endif /* CONFIG_IEEE80211BE */
 		}
 
 		/* This channel would at least be usable */
@@ -991,9 +1066,22 @@
 	if (ideal_chan) {
 		wpa_printf(MSG_DEBUG, "ACS: Ideal channel is %d (%d MHz) with total interference factor of %Lg",
 			   ideal_chan->chan, ideal_chan->freq, ideal_factor);
+
+#ifdef CONFIG_IEEE80211BE
+		if (iface->conf->punct_acs_threshold)
+			wpa_printf(MSG_DEBUG, "ACS: RU puncturing bitmap 0x%x",
+				   ideal_chan->punct_bitmap);
+#endif /* CONFIG_IEEE80211BE */
+
 		return ideal_chan;
 	}
 
+#ifdef CONFIG_IEEE80211BE
+	if (iface->conf->punct_acs_threshold)
+		wpa_printf(MSG_DEBUG, "ACS: RU puncturing bitmap 0x%x",
+			   ideal_chan->punct_bitmap);
+#endif /* CONFIG_IEEE80211BE */
+
 	return rand_chan;
 }
 
@@ -1106,6 +1194,9 @@
 
 	iface->conf->channel = ideal_chan->chan;
 	iface->freq = ideal_chan->freq;
+#ifdef CONFIG_IEEE80211BE
+	iface->conf->punct_bitmap = ideal_chan->punct_bitmap;
+#endif /* CONFIG_IEEE80211BE */
 
 	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
 		acs_adjust_secondary(iface);
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 5dc8a8f..b5fcc38 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -1447,6 +1447,13 @@
 
 #ifdef CONFIG_FILS
 	if (full_config && bss->fils_discovery_max_int &&
+	    (!conf->ieee80211ax || bss->disable_11ax)) {
+		wpa_printf(MSG_ERROR,
+			   "Currently IEEE 802.11ax support is mandatory to enable FILS discovery transmission.");
+		return -1;
+	}
+
+	if (full_config && bss->fils_discovery_max_int &&
 	    bss->unsol_bcast_probe_resp_interval) {
 		wpa_printf(MSG_ERROR,
 			   "Cannot enable both FILS discovery and unsolicited broadcast Probe Response at the same time");
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 6dbb223..07ee31c 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -284,6 +284,7 @@
 	char bridge[IFNAMSIZ + 1];
 	char vlan_bridge[IFNAMSIZ + 1];
 	char wds_bridge[IFNAMSIZ + 1];
+	int bridge_hairpin; /* hairpin_mode on bridge members */
 
 	enum hostapd_logger_level logger_syslog_level, logger_stdout_level;
 
@@ -748,6 +749,7 @@
 #endif /* CONFIG_FILS */
 
 	int multicast_to_unicast;
+	int bridge_multicast_to_unicast;
 
 	int broadcast_deauth;
 
@@ -845,6 +847,19 @@
 	u32 macsec_replay_window;
 
 	/**
+	 * macsec_offload - Enable MACsec offload
+	 *
+	 * This setting applies only when MACsec is in use, i.e.,
+	 *  - macsec_policy is enabled
+	 *  - the key server has decided to enable MACsec
+	 *
+	 * 0 = MACSEC_OFFLOAD_OFF (default)
+	 * 1 = MACSEC_OFFLOAD_PHY
+	 * 2 = MACSEC_OFFLOAD_MAC
+	 */
+	int macsec_offload;
+
+	/**
 	 * macsec_port - MACsec port (in SCI)
 	 *
 	 * Port component of the SCI.
@@ -1143,6 +1158,8 @@
 	enum oper_chan_width eht_oper_chwidth;
 	u8 eht_oper_centr_freq_seg0_idx;
 	struct eht_phy_capabilities_info eht_phy_capab;
+	u16 punct_bitmap; /* a bitmap of disabled 20 MHz channels */
+	u8 punct_acs_threshold;
 #endif /* CONFIG_IEEE80211BE */
 
 	/* EHT enable/disable config from CHAN_SWITCH */
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index 1ffc37f..f77f738 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -459,6 +459,7 @@
 	params.qosinfo = qosinfo;
 	params.support_p2p_ps = supp_p2p_ps;
 	params.set = set;
+	params.mld_link_id = -1;
 	return hapd->driver->sta_add(hapd->drv_priv, &params);
 }
 
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index dbc6b06..c25a5bb 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -533,8 +533,7 @@
 static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
 				   const struct ieee80211_mgmt *req,
 				   int is_p2p, size_t *resp_len,
-				   bool bcast_probe_resp, const u8 *known_bss,
-				   u8 known_bss_len)
+				   const u8 *known_bss, u8 known_bss_len)
 {
 	struct ieee80211_mgmt *resp;
 	u8 *pos, *epos, *csa_pos;
@@ -585,6 +584,8 @@
 	if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
 		buflen += hostapd_eid_eht_capab_len(hapd, IEEE80211_MODE_AP);
 		buflen += 3 + sizeof(struct ieee80211_eht_operation);
+		if (hapd->iconf->punct_bitmap)
+			buflen += EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE;
 	}
 #endif /* CONFIG_IEEE80211BE */
 
@@ -603,9 +604,16 @@
 
 	resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
 					   WLAN_FC_STYPE_PROBE_RESP);
-	if (req)
+	/* Unicast the response to all requests on bands other than 6 GHz. For
+	 * the 6 GHz, unicast is used only if the actual SSID is not included in
+	 * the Beacon frames. Otherwise, broadcast response is used per IEEE
+	 * Std 802.11ax-2021, 26.17.2.3.2. Broadcast address is also used for
+	 * the Probe Response frame template for the unsolicited (i.e., not as
+	 * a response to a specific request) case. */
+	if (req && (!is_6ghz_op_class(hapd->iconf->op_class) ||
+		    hapd->conf->ignore_broadcast_ssid))
 		os_memcpy(resp->da, req->sa, ETH_ALEN);
-	else if (bcast_probe_resp)
+	else
 		os_memset(resp->da, 0xff, ETH_ALEN);
 
 	os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
@@ -1230,7 +1238,7 @@
 		     " signal=%d", MAC2STR(mgmt->sa), ssi_signal);
 
 	resp = hostapd_gen_probe_resp(hapd, mgmt, elems.p2p != NULL,
-				      &resp_len, false, elems.mbssid_known_bss,
+				      &resp_len, elems.mbssid_known_bss,
 				      elems.mbssid_known_bss_len);
 	if (resp == NULL)
 		return;
@@ -1301,7 +1309,7 @@
 			   "this");
 
 	/* Generate a Probe Response template for the non-P2P case */
-	return hostapd_gen_probe_resp(hapd, NULL, 0, resp_len, false, NULL, 0);
+	return hostapd_gen_probe_resp(hapd, NULL, 0, resp_len, NULL, 0);
 }
 
 #endif /* NEED_AP_MLME */
@@ -1320,7 +1328,7 @@
 
 	return hostapd_gen_probe_resp(hapd, NULL, 0,
 				      &params->unsol_bcast_probe_resp_tmpl_len,
-				      true, NULL, 0);
+				      NULL, 0);
 }
 #endif /* CONFIG_IEEE80211AX */
 
@@ -1351,6 +1359,9 @@
 		phy_index = FD_CAP_PHY_INDEX_HE;
 
 		switch (hapd->iconf->op_class) {
+		case 137:
+			chwidth = FD_CAP_BSS_CHWIDTH_320;
+			break;
 		case 135:
 			mcs_nss_size += 4;
 			/* fallthrough */
@@ -1404,14 +1415,37 @@
 	cap_info |= phy_index << FD_CAP_PHY_INDEX_SHIFT;
 	cap_info |= chwidth << FD_CAP_BSS_CHWIDTH_SHIFT;
 
-	if (mode) {
-		u16 *mcs = (u16 *) mode->he_capab[IEEE80211_MODE_AP].mcs;
+	if (mode && phy_index == FD_CAP_PHY_INDEX_HE) {
+		const u8 *he_mcs = mode->he_capab[IEEE80211_MODE_AP].mcs;
 		int i;
-		u16 nss = 0;
+		u16 nss = 0, mcs[6];
+
+		os_memset(mcs, 0xffff, 6 * sizeof(u16));
+
+		if (mcs_nss_size == 4) {
+			mcs[0] = WPA_GET_LE16(&he_mcs[0]);
+			mcs[1] = WPA_GET_LE16(&he_mcs[2]);
+		}
+
+		if (mcs_nss_size == 8) {
+			mcs[2] = WPA_GET_LE16(&he_mcs[4]);
+			mcs[3] = WPA_GET_LE16(&he_mcs[6]);
+		}
+
+		if (mcs_nss_size == 12) {
+			mcs[4] = WPA_GET_LE16(&he_mcs[8]);
+			mcs[5] = WPA_GET_LE16(&he_mcs[10]);
+		}
 
 		for (i = 0; i < HE_NSS_MAX_STREAMS; i++) {
 			u16 nss_mask = 0x3 << (i * 2);
 
+			/*
+			 * If NSS values supported by RX and TX are different
+			 * then choose the smaller of the two as the maximum
+			 * supported NSS as that is the value supported by
+			 * both RX and TX.
+			 */
 			if (mcs_nss_size == 4 &&
 			    (((mcs[0] & nss_mask) == nss_mask) ||
 			     ((mcs[1] & nss_mask) == nss_mask)))
@@ -1655,6 +1689,8 @@
 	if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
 		tail_len += hostapd_eid_eht_capab_len(hapd, IEEE80211_MODE_AP);
 		tail_len += 3 + sizeof(struct ieee80211_eht_operation);
+		if (hapd->iconf->punct_bitmap)
+			tail_len += EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE;
 	}
 #endif /* CONFIG_IEEE80211BE */
 
@@ -2065,6 +2101,10 @@
 	params.fd_frame_tmpl = hostapd_fils_discovery(hapd, &params);
 #endif /* CONFIG_FILS */
 
+#ifdef CONFIG_IEEE80211BE
+	params.punct_bitmap = iconf->punct_bitmap;
+#endif /* CONFIG_IEEE80211BE */
+
 	if (cmode &&
 	    hostapd_set_freq_params(&freq, iconf->hw_mode, iface->freq,
 				    iconf->channel, iconf->enable_edmg,
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index 168e5f5..6934a73 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -212,6 +212,26 @@
 }
 
 
+static const char * hw_mode_str(enum hostapd_hw_mode mode)
+{
+	switch (mode) {
+	case HOSTAPD_MODE_IEEE80211B:
+		return "b";
+	case HOSTAPD_MODE_IEEE80211G:
+		return "g";
+	case HOSTAPD_MODE_IEEE80211A:
+		return "a";
+	case HOSTAPD_MODE_IEEE80211AD:
+		return "ad";
+	case HOSTAPD_MODE_IEEE80211ANY:
+		return "any";
+	case NUM_HOSTAPD_MODES:
+		return "invalid";
+	}
+	return "unknown";
+}
+
+
 static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd,
 				      struct sta_info *sta,
 				      char *buf, size_t buflen)
@@ -696,6 +716,7 @@
 {
 	struct hostapd_iface *iface = hapd->iface;
 	struct hostapd_hw_modes *mode = iface->current_mode;
+	struct hostapd_config *iconf = hapd->iconf;
 	int len = 0, ret, j;
 	size_t i;
 
@@ -730,6 +751,24 @@
 		return len;
 	len += ret;
 
+	if (mode) {
+		ret = os_snprintf(buf + len, buflen - len, "hw_mode=%s\n",
+				  hw_mode_str(mode->mode));
+		if (os_snprintf_error(buflen - len, ret))
+			return len;
+		len += ret;
+	}
+
+	if (iconf->country[0] && iconf->country[1]) {
+		ret = os_snprintf(buf + len, buflen - len,
+				  "country_code=%c%c\ncountry3=0x%X\n",
+				  iconf->country[0], iconf->country[1],
+				  iconf->country[2]);
+		if (os_snprintf_error(buflen - len, ret))
+			return len;
+		len += ret;
+	}
+
 	if (!iface->cac_started || !iface->dfs_cac_ms) {
 		ret = os_snprintf(buf + len, buflen - len,
 				  "cac_time_seconds=%d\n"
@@ -941,16 +980,27 @@
 		} \
 	} while (0)
 
+#define SET_CSA_SETTING_EXT(str) \
+	do { \
+		const char *pos2 = os_strstr(pos, " " #str "="); \
+		if (pos2) { \
+			pos2 += sizeof(" " #str "=") - 1; \
+			settings->str = atoi(pos2); \
+		} \
+	} while (0)
+
 	SET_CSA_SETTING(center_freq1);
 	SET_CSA_SETTING(center_freq2);
 	SET_CSA_SETTING(bandwidth);
 	SET_CSA_SETTING(sec_channel_offset);
+	SET_CSA_SETTING_EXT(punct_bitmap);
 	settings->freq_params.ht_enabled = !!os_strstr(pos, " ht");
 	settings->freq_params.vht_enabled = !!os_strstr(pos, " vht");
 	settings->freq_params.he_enabled = !!os_strstr(pos, " he");
 	settings->freq_params.eht_enabled = !!os_strstr(pos, " eht");
 	settings->block_tx = !!os_strstr(pos, " blocktx");
 #undef SET_CSA_SETTING
+#undef SET_CSA_SETTING_EXT
 
 	return 0;
 }
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index 4c33e86..8fc128e 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -859,7 +859,7 @@
 
 void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
 			     int offset, int width, int cf1, int cf2,
-			     int finished)
+			     u16 punct_bitmap, int finished)
 {
 #ifdef NEED_AP_MLME
 	int channel, chwidth, is_dfs0, is_dfs;
@@ -868,14 +868,14 @@
 
 	hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
 		       HOSTAPD_LEVEL_INFO,
-		       "driver %s channel switch: iface->freq=%d, freq=%d, ht=%d, vht_ch=0x%x, "
-		       "he_ch=0x%x, eht_ch=0x%x, offset=%d, width=%d (%s), cf1=%d, cf2=%d",
+		       "driver %s channel switch: iface->freq=%d, freq=%d, ht=%d, vht_ch=0x%x, he_ch=0x%x, eht_ch=0x%x, offset=%d, width=%d (%s), cf1=%d, cf2=%d, puncturing_bitmap=0x%x",
 		       finished ? "had" : "starting",
 		       hapd->iface->freq,
 		       freq, ht, hapd->iconf->ch_switch_vht_config,
 		       hapd->iconf->ch_switch_he_config,
 		       hapd->iconf->ch_switch_eht_config, offset,
-		       width, channel_width_to_string(width), cf1, cf2);
+		       width, channel_width_to_string(width), cf1, cf2,
+		       punct_bitmap);
 
 	if (!hapd->iface->current_mode) {
 		hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
@@ -987,6 +987,9 @@
 	hostapd_set_oper_chwidth(hapd->iconf, chwidth);
 	hostapd_set_oper_centr_freq_seg0_idx(hapd->iconf, seg0_idx);
 	hostapd_set_oper_centr_freq_seg1_idx(hapd->iconf, seg1_idx);
+#ifdef CONFIG_IEEE80211BE
+	hapd->iconf->punct_bitmap = punct_bitmap;
+#endif /* CONFIG_IEEE80211BE */
 	if (hapd->iconf->ieee80211ac) {
 		hapd->iconf->vht_capab &= ~VHT_CAP_SUPP_CHAN_WIDTH_MASK;
 		if (chwidth == CONF_OPER_CHWIDTH_160MHZ)
@@ -1001,11 +1004,11 @@
 				  hapd->iface->num_hw_features);
 
 	wpa_msg(hapd->msg_ctx, MSG_INFO,
-		"%sfreq=%d ht_enabled=%d ch_offset=%d ch_width=%s cf1=%d cf2=%d is_dfs0=%d dfs=%d",
+		"%sfreq=%d ht_enabled=%d ch_offset=%d ch_width=%s cf1=%d cf2=%d is_dfs0=%d dfs=%d puncturing_bitmap=0x%04x",
 		finished ? WPA_EVENT_CHANNEL_SWITCH :
 		WPA_EVENT_CHANNEL_SWITCH_STARTED,
 		freq, ht, offset, channel_width_to_string(width),
-		cf1, cf2, is_dfs0, is_dfs);
+		cf1, cf2, is_dfs0, is_dfs, punct_bitmap);
 	if (!finished)
 		return;
 
@@ -2028,6 +2031,7 @@
 					data->ch_switch.ch_width,
 					data->ch_switch.cf1,
 					data->ch_switch.cf2,
+					data->ch_switch.punct_bitmap,
 					event == EVENT_CH_SWITCH);
 		break;
 	case EVENT_CONNECT_FAILED_REASON:
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 58492e5..8b3fb40 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -1435,6 +1435,22 @@
 		return -1;
 	}
 
+	if (conf->bridge[0]) {
+		/* Set explicitly configured bridge parameters that might have
+		 * been lost if the interface has been removed out of the
+		 * bridge. */
+
+		/* multicast to unicast on bridge ports */
+		if (conf->bridge_multicast_to_unicast)
+			hostapd_drv_br_port_set_attr(
+				hapd, DRV_BR_PORT_ATTR_MCAST2UCAST, 1);
+
+		/* hairpin mode */
+		if (conf->bridge_hairpin)
+			hostapd_drv_br_port_set_attr(
+				hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE, 1);
+	}
+
 	if (conf->proxy_arp) {
 		if (x_snoop_init(hapd)) {
 			wpa_printf(MSG_ERROR,
@@ -1749,7 +1765,7 @@
 	bw = center_idx_to_bw_6ghz(seg0);
 	/* Assign the secondary channel if absent in config for
 	 * bandwidths > 20 MHz */
-	if (bw > 20 && !iface->conf->secondary_channel) {
+	if (bw > 0 && !iface->conf->secondary_channel) {
 		if (((iface->conf->channel - 1) / 4) % 2)
 			iface->conf->secondary_channel = -1;
 		else
@@ -1768,6 +1784,11 @@
 	} else {
 		int ret;
 
+		if (iface->conf->acs) {
+			iface->freq = 0;
+			iface->conf->channel = 0;
+		}
+
 		ret = configured_fixed_chan_to_freq(iface);
 		if (ret < 0)
 			goto fail;
@@ -3634,6 +3655,9 @@
 	struct hostapd_iface *iface = hapd->iface;
 	struct hostapd_freq_params old_freq;
 	int ret;
+#ifdef CONFIG_IEEE80211BE
+	u16 old_punct_bitmap;
+#endif /* CONFIG_IEEE80211BE */
 	u8 chan, bandwidth;
 
 	os_memset(&old_freq, 0, sizeof(old_freq));
@@ -3679,9 +3703,16 @@
 	if (ret)
 		return ret;
 
+#ifdef CONFIG_IEEE80211BE
+	old_punct_bitmap = iface->conf->punct_bitmap;
+	iface->conf->punct_bitmap = settings->punct_bitmap;
+#endif /* CONFIG_IEEE80211BE */
 	ret = hostapd_build_beacon_data(hapd, &settings->beacon_after);
 
 	/* change back the configuration */
+#ifdef CONFIG_IEEE80211BE
+	iface->conf->punct_bitmap = old_punct_bitmap;
+#endif /* CONFIG_IEEE80211BE */
 	hostapd_change_config_freq(iface->bss[0], iface->conf,
 				   &old_freq, NULL);
 
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index a88f9b6..b81da30 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -727,7 +727,7 @@
 			 int ssi_signal);
 void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
 			     int offset, int width, int cf1, int cf2,
-			     int finished);
+			     u16 punct_bitmap, int finished);
 struct survey_results;
 void hostapd_event_get_survey(struct hostapd_iface *iface,
 			      struct survey_results *survey_results);
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index ed5ff41..842d9f5 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -893,6 +893,55 @@
 }
 
 
+static bool hostapd_is_usable_punct_bitmap(struct hostapd_iface *iface)
+{
+#ifdef CONFIG_IEEE80211BE
+	struct hostapd_config *conf = iface->conf;
+	u8 bw, start_chan;
+
+	if (!conf->punct_bitmap)
+		return true;
+
+	if (!conf->ieee80211be) {
+		wpa_printf(MSG_ERROR,
+			   "Currently RU puncturing is supported only if ieee80211be is enabled");
+		return false;
+	}
+
+	if (iface->freq >= 2412 && iface->freq <= 2484) {
+		wpa_printf(MSG_ERROR,
+			   "RU puncturing not supported in 2.4 GHz");
+		return false;
+	}
+
+	switch (conf->eht_oper_chwidth) {
+	case 0:
+		wpa_printf(MSG_ERROR,
+			   "RU puncturing is supported only in 80 MHz and 160 MHz");
+		return false;
+	case 1:
+		bw = 80;
+		start_chan = conf->eht_oper_centr_freq_seg0_idx - 6;
+		break;
+	case 2:
+		bw = 160;
+		start_chan = conf->eht_oper_centr_freq_seg0_idx - 14;
+		break;
+	default:
+		return false;
+	}
+
+	if (!is_punct_bitmap_valid(bw, (conf->channel - start_chan) / 4,
+				   conf->punct_bitmap)) {
+		wpa_printf(MSG_ERROR, "Invalid puncturing bitmap");
+		return false;
+	}
+#endif /* CONFIG_IEEE80211BE */
+
+	return true;
+}
+
+
 static int hostapd_is_usable_chans(struct hostapd_iface *iface)
 {
 	int secondary_freq;
@@ -915,6 +964,9 @@
 	if (!hostapd_is_usable_edmg(iface))
 		return 0;
 
+	if (!hostapd_is_usable_punct_bitmap(iface))
+		return 0;
+
 	if (!iface->conf->secondary_channel)
 		return 1;
 
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index e53f0dc..0142ee4 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -3652,40 +3652,34 @@
 }
 
 
-static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
-			   const u8 *ies, size_t ies_len, int reassoc)
+static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
+			     const u8 *ies, size_t ies_len,
+			     struct ieee802_11_elems *elems, int reassoc)
 {
-	struct ieee802_11_elems elems;
 	int resp;
 	const u8 *wpa_ie;
 	size_t wpa_ie_len;
 	const u8 *p2p_dev_addr = NULL;
 
-	if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) {
-		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
-			       HOSTAPD_LEVEL_INFO, "Station sent an invalid "
-			       "association request");
-		return WLAN_STATUS_UNSPECIFIED_FAILURE;
-	}
-
-	resp = check_ssid(hapd, sta, elems.ssid, elems.ssid_len);
+	resp = check_ssid(hapd, sta, elems->ssid, elems->ssid_len);
 	if (resp != WLAN_STATUS_SUCCESS)
 		return resp;
-	resp = check_wmm(hapd, sta, elems.wmm, elems.wmm_len);
+	resp = check_wmm(hapd, sta, elems->wmm, elems->wmm_len);
 	if (resp != WLAN_STATUS_SUCCESS)
 		return resp;
-	resp = check_ext_capab(hapd, sta, elems.ext_capab, elems.ext_capab_len);
+	resp = check_ext_capab(hapd, sta, elems->ext_capab,
+			       elems->ext_capab_len);
 	if (resp != WLAN_STATUS_SUCCESS)
 		return resp;
-	resp = copy_supp_rates(hapd, sta, &elems);
+	resp = copy_supp_rates(hapd, sta, elems);
 	if (resp != WLAN_STATUS_SUCCESS)
 		return resp;
 
-	resp = check_multi_ap(hapd, sta, elems.multi_ap, elems.multi_ap_len);
+	resp = check_multi_ap(hapd, sta, elems->multi_ap, elems->multi_ap_len);
 	if (resp != WLAN_STATUS_SUCCESS)
 		return resp;
 
-	resp = copy_sta_ht_capab(hapd, sta, elems.ht_capabilities);
+	resp = copy_sta_ht_capab(hapd, sta, elems->ht_capabilities);
 	if (resp != WLAN_STATUS_SUCCESS)
 		return resp;
 	if (hapd->iconf->ieee80211n && hapd->iconf->require_ht &&
@@ -3698,11 +3692,11 @@
 
 #ifdef CONFIG_IEEE80211AC
 	if (hapd->iconf->ieee80211ac) {
-		resp = copy_sta_vht_capab(hapd, sta, elems.vht_capabilities);
+		resp = copy_sta_vht_capab(hapd, sta, elems->vht_capabilities);
 		if (resp != WLAN_STATUS_SUCCESS)
 			return resp;
 
-		resp = set_sta_vht_opmode(hapd, sta, elems.vht_opmode_notif);
+		resp = set_sta_vht_opmode(hapd, sta, elems->vht_opmode_notif);
 		if (resp != WLAN_STATUS_SUCCESS)
 			return resp;
 	}
@@ -3715,9 +3709,9 @@
 		return WLAN_STATUS_ASSOC_DENIED_NO_VHT;
 	}
 
-	if (hapd->conf->vendor_vht && !elems.vht_capabilities) {
-		resp = copy_sta_vendor_vht(hapd, sta, elems.vendor_vht,
-					   elems.vendor_vht_len);
+	if (hapd->conf->vendor_vht && !elems->vht_capabilities) {
+		resp = copy_sta_vendor_vht(hapd, sta, elems->vendor_vht,
+					   elems->vendor_vht_len);
 		if (resp != WLAN_STATUS_SUCCESS)
 			return resp;
 	}
@@ -3725,8 +3719,8 @@
 #ifdef CONFIG_IEEE80211AX
 	if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
 		resp = copy_sta_he_capab(hapd, sta, IEEE80211_MODE_AP,
-					 elems.he_capabilities,
-					 elems.he_capabilities_len);
+					 elems->he_capabilities,
+					 elems->he_capabilities_len);
 		if (resp != WLAN_STATUS_SUCCESS)
 			return resp;
 
@@ -3747,7 +3741,7 @@
 				return WLAN_STATUS_DENIED_HE_NOT_SUPPORTED;
 			}
 			resp = copy_sta_he_6ghz_capab(hapd, sta,
-						      elems.he_6ghz_band_cap);
+						      elems->he_6ghz_band_cap);
 			if (resp != WLAN_STATUS_SUCCESS)
 				return resp;
 		}
@@ -3756,17 +3750,17 @@
 #ifdef CONFIG_IEEE80211BE
 	if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
 		resp = copy_sta_eht_capab(hapd, sta, IEEE80211_MODE_AP,
-					  elems.he_capabilities,
-					  elems.he_capabilities_len,
-					  elems.eht_capabilities,
-					  elems.eht_capabilities_len);
+					  elems->he_capabilities,
+					  elems->he_capabilities_len,
+					  elems->eht_capabilities,
+					  elems->eht_capabilities_len);
 		if (resp != WLAN_STATUS_SUCCESS)
 			return resp;
 	}
 #endif /* CONFIG_IEEE80211BE */
 
 #ifdef CONFIG_P2P
-	if (elems.p2p) {
+	if (elems->p2p && ies && ies_len) {
 		wpabuf_free(sta->p2p_ie);
 		sta->p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len,
 							  P2P_IE_VENDOR_TYPE);
@@ -3778,13 +3772,13 @@
 	}
 #endif /* CONFIG_P2P */
 
-	if ((hapd->conf->wpa & WPA_PROTO_RSN) && elems.rsn_ie) {
-		wpa_ie = elems.rsn_ie;
-		wpa_ie_len = elems.rsn_ie_len;
+	if ((hapd->conf->wpa & WPA_PROTO_RSN) && elems->rsn_ie) {
+		wpa_ie = elems->rsn_ie;
+		wpa_ie_len = elems->rsn_ie_len;
 	} else if ((hapd->conf->wpa & WPA_PROTO_WPA) &&
-		   elems.wpa_ie) {
-		wpa_ie = elems.wpa_ie;
-		wpa_ie_len = elems.wpa_ie_len;
+		   elems->wpa_ie) {
+		wpa_ie = elems->wpa_ie;
+		wpa_ie_len = elems->wpa_ie_len;
 	} else {
 		wpa_ie = NULL;
 		wpa_ie_len = 0;
@@ -3792,7 +3786,7 @@
 
 #ifdef CONFIG_WPS
 	sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2);
-	if (hapd->conf->wps_state && elems.wps_ie) {
+	if (hapd->conf->wps_state && elems->wps_ie && ies && ies_len) {
 		wpa_printf(MSG_DEBUG, "STA included WPS IE in (Re)Association "
 			   "Request - assume WPS is used");
 		if (check_sa_query(hapd, sta, reassoc))
@@ -3846,10 +3840,12 @@
 		res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
 					  hapd->iface->freq,
 					  wpa_ie, wpa_ie_len,
-					  elems.rsnxe ? elems.rsnxe - 2 : NULL,
-					  elems.rsnxe ? elems.rsnxe_len + 2 : 0,
-					  elems.mdie, elems.mdie_len,
-					  elems.owe_dh, elems.owe_dh_len);
+					  elems->rsnxe ? elems->rsnxe - 2 :
+					  NULL,
+					  elems->rsnxe ? elems->rsnxe_len + 2 :
+					  0,
+					  elems->mdie, elems->mdie_len,
+					  elems->owe_dh, elems->owe_dh_len);
 		resp = wpa_res_to_status_code(res);
 		if (resp != WLAN_STATUS_SUCCESS)
 			return resp;
@@ -3906,7 +3902,7 @@
 		if (hapd->conf->sae_pwe == SAE_PWE_BOTH &&
 		    sta->auth_alg == WLAN_AUTH_SAE &&
 		    sta->sae && !sta->sae->h2e &&
-		    ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len,
+		    ieee802_11_rsnx_capab_len(elems->rsnxe, elems->rsnxe_len,
 					      WLAN_RSNX_CAPAB_SAE_H2E)) {
 			wpa_printf(MSG_INFO, "SAE: " MACSTR
 				   " indicates support for SAE H2E, but did not use it",
@@ -3918,9 +3914,9 @@
 #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) {
-			resp = owe_process_assoc_req(hapd, sta, elems.owe_dh,
-						     elems.owe_dh_len);
+		    elems->owe_dh) {
+			resp = owe_process_assoc_req(hapd, sta, elems->owe_dh,
+						     elems->owe_dh_len);
 			if (resp != WLAN_STATUS_SUCCESS)
 				return resp;
 		}
@@ -3934,7 +3930,7 @@
 		    (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) &&
 		    hapd->conf->dpp_netaccesskey && sta->wpa_sm &&
 		    wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_DPP &&
-		    elems.owe_dh) {
+		    elems->owe_dh) {
 			sta->dpp_pfs = dpp_pfs_init(
 				wpabuf_head(hapd->conf->dpp_netaccesskey),
 				wpabuf_len(hapd->conf->dpp_netaccesskey));
@@ -3945,8 +3941,8 @@
 				goto pfs_fail;
 			}
 
-			if (dpp_pfs_process(sta->dpp_pfs, elems.owe_dh,
-					    elems.owe_dh_len) < 0) {
+			if (dpp_pfs_process(sta->dpp_pfs, elems->owe_dh,
+					    elems->owe_dh_len) < 0) {
 				dpp_pfs_free(sta->dpp_pfs);
 				sta->dpp_pfs = NULL;
 				return WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -3969,7 +3965,7 @@
 		}
 #ifdef CONFIG_HS20
 	} else if (hapd->conf->osen) {
-		if (elems.osen == NULL) {
+		if (!elems->osen) {
 			hostapd_logger(
 				hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 				HOSTAPD_LEVEL_INFO,
@@ -3987,7 +3983,7 @@
 			return WLAN_STATUS_UNSPECIFIED_FAILURE;
 		}
 		if (wpa_validate_osen(hapd->wpa_auth, sta->wpa_sm,
-				      elems.osen - 2, elems.osen_len + 2) < 0)
+				      elems->osen - 2, elems->osen_len + 2) < 0)
 			return WLAN_STATUS_INVALID_IE;
 #endif /* CONFIG_HS20 */
 	} else
@@ -3999,12 +3995,12 @@
 
 #ifdef CONFIG_HS20
 	wpabuf_free(sta->hs20_ie);
-	if (elems.hs20 && elems.hs20_len > 4) {
+	if (elems->hs20 && elems->hs20_len > 4) {
 		int release;
 
-		sta->hs20_ie = wpabuf_alloc_copy(elems.hs20 + 4,
-						 elems.hs20_len - 4);
-		release = ((elems.hs20[4] >> 4) & 0x0f) + 1;
+		sta->hs20_ie = wpabuf_alloc_copy(elems->hs20 + 4,
+						 elems->hs20_len - 4);
+		release = ((elems->hs20[4] >> 4) & 0x0f) + 1;
 		if (release >= 2 && !wpa_auth_uses_mfp(sta->wpa_sm) &&
 		    hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
 			wpa_printf(MSG_DEBUG,
@@ -4017,10 +4013,10 @@
 	}
 
 	wpabuf_free(sta->roaming_consortium);
-	if (elems.roaming_cons_sel)
+	if (elems->roaming_cons_sel)
 		sta->roaming_consortium = wpabuf_alloc_copy(
-			elems.roaming_cons_sel + 4,
-			elems.roaming_cons_sel_len - 4);
+			elems->roaming_cons_sel + 4,
+			elems->roaming_cons_sel_len - 4);
 	else
 		sta->roaming_consortium = NULL;
 #endif /* CONFIG_HS20 */
@@ -4028,16 +4024,16 @@
 #ifdef CONFIG_FST
 	wpabuf_free(sta->mb_ies);
 	if (hapd->iface->fst)
-		sta->mb_ies = mb_ies_by_info(&elems.mb_ies);
+		sta->mb_ies = mb_ies_by_info(&elems->mb_ies);
 	else
 		sta->mb_ies = NULL;
 #endif /* CONFIG_FST */
 
 #ifdef CONFIG_MBO
-	mbo_ap_check_sta_assoc(hapd, sta, &elems);
+	mbo_ap_check_sta_assoc(hapd, sta, elems);
 
 	if (hapd->conf->mbo_enabled && (hapd->conf->wpa & 2) &&
-	    elems.mbo && sta->cell_capa && !(sta->flags & WLAN_STA_MFP) &&
+	    elems->mbo && sta->cell_capa && !(sta->flags & WLAN_STA_MFP) &&
 	    hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
 		wpa_printf(MSG_INFO,
 			   "MBO: Reject WPA2 association without PMF");
@@ -4067,7 +4063,7 @@
 					  &tx_seg1_idx) < 0)
 			return WLAN_STATUS_UNSPECIFIED_FAILURE;
 
-		res = ocv_verify_tx_params(elems.oci, elems.oci_len, &ci,
+		res = ocv_verify_tx_params(elems->oci, elems->oci_len, &ci,
 					   tx_chanwidth, tx_seg1_idx);
 		if (wpa_auth_uses_ocv(sta->wpa_sm) == 2 &&
 		    res == OCI_NOT_FOUND) {
@@ -4086,18 +4082,18 @@
 	}
 #endif /* CONFIG_FILS && CONFIG_OCV */
 
-	ap_copy_sta_supp_op_classes(sta, elems.supp_op_classes,
-				    elems.supp_op_classes_len);
+	ap_copy_sta_supp_op_classes(sta, elems->supp_op_classes,
+				    elems->supp_op_classes_len);
 
 	if ((sta->capability & WLAN_CAPABILITY_RADIO_MEASUREMENT) &&
-	    elems.rrm_enabled &&
-	    elems.rrm_enabled_len >= sizeof(sta->rrm_enabled_capa))
-		os_memcpy(sta->rrm_enabled_capa, elems.rrm_enabled,
+	    elems->rrm_enabled &&
+	    elems->rrm_enabled_len >= sizeof(sta->rrm_enabled_capa))
+		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];
+	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;
@@ -4107,6 +4103,22 @@
 }
 
 
+static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
+			   const u8 *ies, size_t ies_len, int reassoc)
+{
+	struct ieee802_11_elems elems;
+
+	if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO,
+			       "Station sent an invalid association request");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	return __check_assoc_ies(hapd, sta, ies, ies_len, &elems, reassoc);
+}
+
+
 static void send_deauth(struct hostapd_data *hapd, const u8 *addr,
 			u16 reason_code)
 {
@@ -4266,6 +4278,8 @@
 	if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
 		buflen += hostapd_eid_eht_capab_len(hapd, IEEE80211_MODE_AP);
 		buflen += 3 + sizeof(struct ieee80211_eht_operation);
+		if (hapd->iconf->punct_bitmap)
+			buflen += EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE;
 	}
 #endif /* CONFIG_IEEE80211BE */
 
@@ -5330,10 +5344,10 @@
 
 			pos = &mgmt->u.action.u.public_action.action;
 			end = ((const u8 *) mgmt) + len;
-			gas_query_ap_rx(hapd->gas, mgmt->sa,
-					mgmt->u.action.category,
-					pos, end - pos, freq);
-			return 1;
+			if (gas_query_ap_rx(hapd->gas, mgmt->sa,
+					    mgmt->u.action.category,
+					    pos, end - pos, freq) == 0)
+				return 1;
 		}
 #endif /* CONFIG_DPP */
 		if (hapd->public_action_cb) {
@@ -7015,4 +7029,73 @@
 	return eid;
 }
 
+
+static void punct_update_legacy_bw_80(u8 bitmap, u8 pri_chan, u8 *seg0)
+{
+	u8 first_chan = *seg0 - 6, sec_chan;
+
+	switch (bitmap) {
+	case 0x6:
+		*seg0 = 0;
+		return;
+	case 0x8:
+	case 0x4:
+	case 0x2:
+	case 0x1:
+	case 0xC:
+	case 0x3:
+		if (pri_chan < *seg0)
+			*seg0 -= 4;
+		else
+			*seg0 += 4;
+		break;
+	}
+
+	if (pri_chan < *seg0)
+		sec_chan = pri_chan + 4;
+	else
+		sec_chan = pri_chan - 4;
+
+	if (bitmap & BIT((sec_chan - first_chan) / 4))
+		*seg0 = 0;
+}
+
+
+static void punct_update_legacy_bw_160(u8 bitmap, u8 pri,
+				       enum oper_chan_width *width, u8 *seg0)
+{
+	if (pri < *seg0) {
+		*seg0 -= 8;
+		if (bitmap & 0x0F) {
+			*width = 0;
+			punct_update_legacy_bw_80(bitmap & 0xF, pri, seg0);
+		}
+	} else {
+		*seg0 += 8;
+		if (bitmap & 0xF0) {
+			*width = 0;
+			punct_update_legacy_bw_80((bitmap & 0xF0) >> 4, pri,
+						  seg0);
+		}
+	}
+}
+
+
+void punct_update_legacy_bw(u16 bitmap, u8 pri, enum oper_chan_width *width,
+			    u8 *seg0, u8 *seg1)
+{
+	if (*width == CONF_OPER_CHWIDTH_80MHZ && (bitmap & 0xF)) {
+		*width = CONF_OPER_CHWIDTH_USE_HT;
+		punct_update_legacy_bw_80(bitmap & 0xF, pri, seg0);
+	}
+
+	if (*width == CONF_OPER_CHWIDTH_160MHZ && (bitmap & 0xFF)) {
+		*width = CONF_OPER_CHWIDTH_80MHZ;
+		*seg1 = 0;
+		punct_update_legacy_bw_160(bitmap & 0xFF, pri, width, seg0);
+	}
+
+	/* TODO: 320 MHz */
+}
+
 #endif /* CONFIG_NATIVE_WINDOWS */
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index 5f443fc..1e4c843 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -18,6 +18,7 @@
 struct ieee80211_mgmt;
 struct radius_sta;
 enum ieee80211_op_mode;
+enum oper_chan_width;
 
 int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
 		    struct hostapd_frame_info *fi);
@@ -222,5 +223,7 @@
 			unsigned int frame_stype, u8 elem_count,
 			u8 **elem_offset,
 			const u8 *known_bss, size_t known_bss_len);
+void punct_update_legacy_bw(u16 bitmap, u8 pri_chan,
+			    enum oper_chan_width *width, u8 *seg0, u8 *seg1);
 
 #endif /* IEEE802_11_H */
diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
index caaadce..6ebe0f9 100644
--- a/src/ap/ieee802_11_eht.c
+++ b/src/ap/ieee802_11_eht.c
@@ -202,6 +202,9 @@
 	if (!hapd->iface->current_mode)
 		return eid;
 
+	if (hapd->iconf->punct_bitmap)
+		elen += EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE;
+
 	*pos++ = WLAN_EID_EXTENSION;
 	*pos++ = 1 + elen;
 	*pos++ = WLAN_EID_EXT_EHT_OPERATION;
@@ -253,6 +256,12 @@
 	oper->oper_info.ccfs0 = seg0 ? seg0 : hapd->iconf->channel;
 	oper->oper_info.ccfs1 = seg1;
 
+	if (hapd->iconf->punct_bitmap) {
+		oper->oper_params |= EHT_OPER_DISABLED_SUBCHAN_BITMAP_PRESENT;
+		oper->oper_info.disabled_chan_bitmap =
+			host_to_le16(hapd->iconf->punct_bitmap);
+	}
+
 	return pos + elen;
 }
 
diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
index 12273c3..548a448 100644
--- a/src/ap/ieee802_11_he.c
+++ b/src/ap/ieee802_11_he.c
@@ -219,10 +219,20 @@
 	pos += 6; /* skip the fixed part */
 
 	if (is_6ghz_op_class(hapd->iconf->op_class)) {
+		enum oper_chan_width oper_chwidth =
+			hostapd_get_oper_chwidth(hapd->iconf);
 		u8 seg0 = hapd->iconf->he_oper_centr_freq_seg0_idx;
 		u8 seg1 = hostapd_get_oper_centr_freq_seg1_idx(hapd->iconf);
 		u8 control;
 
+#ifdef CONFIG_IEEE80211BE
+		if (hapd->iconf->punct_bitmap) {
+			punct_update_legacy_bw(hapd->iconf->punct_bitmap,
+					       hapd->iconf->channel,
+					       &oper_chwidth, &seg0, &seg1);
+		}
+#endif /* CONFIG_IEEE80211BE */
+
 		if (!seg0)
 			seg0 = hapd->iconf->channel;
 
@@ -253,7 +263,7 @@
 		*pos++ = control;
 
 		/* Channel Center Freq Seg0/Seg1 */
-		if (hapd->iconf->he_oper_chwidth == 2) {
+		if (oper_chwidth == 2) {
 			/*
 			 * Seg 0 indicates the channel center frequency index of
 			 * the 160 MHz channel.
diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c
index 681b6d7..db615a3 100644
--- a/src/ap/ieee802_11_vht.c
+++ b/src/ap/ieee802_11_vht.c
@@ -75,6 +75,10 @@
 {
 	struct ieee80211_vht_operation *oper;
 	u8 *pos = eid;
+	enum oper_chan_width oper_chwidth =
+		hostapd_get_oper_chwidth(hapd->iconf);
+	u8 seg0 = hapd->iconf->vht_oper_centr_freq_seg0_idx;
+	u8 seg1 = hapd->iconf->vht_oper_centr_freq_seg1_idx;
 
 	if (is_6ghz_op_class(hapd->iconf->op_class))
 		return eid;
@@ -85,18 +89,24 @@
 	oper = (struct ieee80211_vht_operation *) pos;
 	os_memset(oper, 0, sizeof(*oper));
 
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->iconf->punct_bitmap) {
+		punct_update_legacy_bw(hapd->iconf->punct_bitmap,
+				       hapd->iconf->channel,
+				       &oper_chwidth, &seg0, &seg1);
+	}
+#endif /* CONFIG_IEEE80211BE */
+
 	/*
 	 * center freq = 5 GHz + (5 * index)
 	 * So index 42 gives center freq 5.210 GHz
 	 * which is channel 42 in 5G band
 	 */
-	oper->vht_op_info_chan_center_freq_seg0_idx =
-		hapd->iconf->vht_oper_centr_freq_seg0_idx;
-	oper->vht_op_info_chan_center_freq_seg1_idx =
-		hapd->iconf->vht_oper_centr_freq_seg1_idx;
+	oper->vht_op_info_chan_center_freq_seg0_idx = seg0;
+	oper->vht_op_info_chan_center_freq_seg1_idx = seg1;
 
-	oper->vht_op_info_chwidth = hapd->iconf->vht_oper_chwidth;
-	if (hapd->iconf->vht_oper_chwidth == CONF_OPER_CHWIDTH_160MHZ) {
+	oper->vht_op_info_chwidth = oper_chwidth;
+	if (oper_chwidth == CONF_OPER_CHWIDTH_160MHZ) {
 		/*
 		 * Convert 160 MHz channel width to new style as interop
 		 * workaround.
@@ -109,8 +119,7 @@
 			oper->vht_op_info_chan_center_freq_seg0_idx -= 8;
 		else
 			oper->vht_op_info_chan_center_freq_seg0_idx += 8;
-	} else if (hapd->iconf->vht_oper_chwidth ==
-		   CONF_OPER_CHWIDTH_80P80MHZ) {
+	} else if (oper_chwidth == CONF_OPER_CHWIDTH_80P80MHZ) {
 		/*
 		 * Convert 80+80 MHz channel width to new style as interop
 		 * workaround.
diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
index 23a352c..153ee40 100644
--- a/src/ap/wnm_ap.c
+++ b/src/ap/wnm_ap.c
@@ -643,6 +643,133 @@
 }
 
 
+
+static const char * wnm_event_type2str(enum wnm_event_report_type wtype)
+{
+#define W2S(wtype) case WNM_EVENT_TYPE_ ## wtype: return #wtype;
+	switch (wtype) {
+	W2S(TRANSITION)
+	W2S(RSNA)
+	W2S(P2P_LINK)
+	W2S(WNM_LOG)
+	W2S(BSS_COLOR_COLLISION)
+	W2S(BSS_COLOR_IN_USE)
+	}
+	return "UNKNOWN";
+#undef W2S
+}
+
+
+static void ieee802_11_rx_wnm_event_report(struct hostapd_data *hapd,
+					   const u8 *addr, const u8 *buf,
+					   size_t len)
+{
+	struct sta_info *sta;
+	u8 dialog_token;
+	struct wnm_event_report_element *report_ie;
+	const u8 *pos = buf, *end = buf + len;
+	const size_t fixed_field_len = 3; /* Event Token/Type/Report Status */
+#ifdef CONFIG_IEEE80211AX
+	const size_t tsf_len = 8;
+	u8 color;
+	u64 bitmap;
+#endif /* CONFIG_IEEE80211AX */
+
+	if (end - pos < 1 + 2) {
+		wpa_printf(MSG_DEBUG,
+			   "WNM: Ignore too short WNM Event Report frame from "
+			   MACSTR, MAC2STR(addr));
+		return;
+	}
+
+	dialog_token = *pos++;
+	report_ie = (struct wnm_event_report_element *) pos;
+
+	if (end - pos < 2 + report_ie->len ||
+	    report_ie->len < fixed_field_len) {
+		wpa_printf(MSG_DEBUG,
+			   "WNM: Ignore truncated WNM Event Report frame from "
+			   MACSTR, MAC2STR(addr));
+		return;
+	}
+
+	if (report_ie->eid != WLAN_EID_EVENT_REPORT ||
+	    report_ie->status != WNM_STATUS_SUCCESSFUL)
+		return;
+
+	wpa_printf(MSG_DEBUG, "WNM: Received WNM Event Report frame from "
+		   MACSTR " dialog_token=%u event_token=%u type=%d (%s)",
+		   MAC2STR(addr), dialog_token, report_ie->token,
+		   report_ie->type, wnm_event_type2str(report_ie->type));
+
+	pos += 2 + fixed_field_len;
+	wpa_hexdump(MSG_MSGDUMP, "WNM: Event Report", pos, end - pos);
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta || !(sta->flags & WLAN_STA_ASSOC)) {
+		wpa_printf(MSG_DEBUG, "Station " MACSTR
+			   " not found for received WNM Event Report",
+			   MAC2STR(addr));
+		return;
+	}
+
+	switch (report_ie->type) {
+#ifdef CONFIG_IEEE80211AX
+	case WNM_EVENT_TYPE_BSS_COLOR_COLLISION:
+		if (!hapd->iconf->ieee80211ax || hapd->conf->disable_11ax)
+			return;
+		if (report_ie->len <
+		    fixed_field_len + tsf_len + 8) {
+			wpa_printf(MSG_DEBUG,
+				   "WNM: Too short BSS color collision event report from "
+				   MACSTR, MAC2STR(addr));
+			return;
+		}
+		bitmap = WPA_GET_LE64(report_ie->u.bss_color_collision.color_bitmap);
+		wpa_printf(MSG_DEBUG,
+			   "WNM: BSS color collision bitmap 0x%llx reported by "
+			   MACSTR, (unsigned long long) bitmap, MAC2STR(addr));
+		hostapd_switch_color(hapd->iface->bss[0], bitmap);
+		break;
+	case WNM_EVENT_TYPE_BSS_COLOR_IN_USE:
+		if (!hapd->iconf->ieee80211ax || hapd->conf->disable_11ax)
+			return;
+		if (report_ie->len < fixed_field_len + tsf_len + 1) {
+			wpa_printf(MSG_DEBUG,
+				   "WNM: Too short BSS color in use event report from "
+				   MACSTR, MAC2STR(addr));
+			return;
+		}
+		color = report_ie->u.bss_color_in_use.color;
+		if (color > 63) {
+			wpa_printf(MSG_DEBUG,
+				   "WNM: Invalid BSS color %u report from "
+				   MACSTR, color, MAC2STR(addr));
+			return;
+		}
+		if (color == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "WNM: BSS color use report canceled by "
+				   MACSTR, MAC2STR(addr));
+			/* TODO: Clear stored color from the collision bitmap
+			 * if there are no other users for it. */
+			return;
+		}
+		wpa_printf(MSG_DEBUG, "WNM: BSS color %u use report by "
+			   MACSTR, color, MAC2STR(addr));
+		hapd->color_collision_bitmap |= 1ULL << color;
+		break;
+#endif /* CONFIG_IEEE80211AX */
+	default:
+		wpa_printf(MSG_DEBUG,
+			   "WNM Event Report type=%d (%s) not supported",
+			   report_ie->type,
+			   wnm_event_type2str(report_ie->type));
+		break;
+	}
+}
+
+
 int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
 				const struct ieee80211_mgmt *mgmt, size_t len)
 {
@@ -658,6 +785,10 @@
 	plen = len - IEEE80211_HDRLEN - 2;
 
 	switch (action) {
+	case WNM_EVENT_REPORT:
+		ieee802_11_rx_wnm_event_report(hapd, mgmt->sa, payload,
+					       plen);
+		return 0;
 	case WNM_BSS_TRANS_MGMT_QUERY:
 		ieee802_11_rx_bss_trans_mgmt_query(hapd, mgmt->sa, payload,
 						   plen);
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 7aff64f..635a74a 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -36,7 +36,7 @@
 
 #define STATE_MACHINE_DATA struct wpa_state_machine
 #define STATE_MACHINE_DEBUG_PREFIX "WPA"
-#define STATE_MACHINE_ADDR sm->addr
+#define STATE_MACHINE_ADDR wpa_auth_get_spa(sm)
 
 
 static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx);
@@ -59,7 +59,9 @@
 				       struct wpa_group *group);
 static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
 			  const u8 *pmk, unsigned int pmk_len,
-			  struct wpa_ptk *ptk, int force_sha256);
+			  struct wpa_ptk *ptk, int force_sha256,
+			  u8 *pmk_r0, u8 *pmk_r1, u8 *pmk_r0_name,
+			  size_t *key_len);
 static void wpa_group_free(struct wpa_authenticator *wpa_auth,
 			   struct wpa_group *group);
 static void wpa_group_get(struct wpa_authenticator *wpa_auth,
@@ -80,6 +82,18 @@
 static const int dot11RSNAConfigSATimeout = 60;
 
 
+static const u8 * wpa_auth_get_aa(const struct wpa_state_machine *sm)
+{
+	return sm->wpa_auth->addr;
+}
+
+
+static const u8 * wpa_auth_get_spa(const struct wpa_state_machine *sm)
+{
+	return sm->addr;
+}
+
+
 static inline int wpa_auth_mic_failure_report(
 	struct wpa_authenticator *wpa_auth, const u8 *addr)
 {
@@ -253,13 +267,14 @@
 }
 
 
-void wpa_auth_remove_ptksa(struct wpa_authenticator *wpa_auth,
-			   const u8 *addr, int cipher)
+static void wpa_auth_remove_ptksa(struct wpa_authenticator *wpa_auth,
+				  const u8 *addr, int cipher)
 {
 	if (wpa_auth->cb->clear_ptksa)
 		wpa_auth->cb->clear_ptksa(wpa_auth->cb_ctx, addr, cipher);
 }
 
+
 void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr,
 		     logger_level level, const char *txt)
 {
@@ -378,7 +393,8 @@
 	struct wpa_authenticator *wpa_auth = eloop_ctx;
 	struct wpa_state_machine *sm = timeout_ctx;
 
-	wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "rekeying PTK");
+	wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
+			"rekeying PTK");
 	wpa_request_new_ptk(sm);
 	wpa_sm_step(sm);
 }
@@ -388,7 +404,8 @@
 {
 	if (sm && sm->wpa_auth->conf.wpa_ptk_rekey) {
 		wpa_printf(MSG_DEBUG, "WPA: Start PTK rekeying timer for "
-			   MACSTR " (%d seconds)", MAC2STR(sm->addr),
+			   MACSTR " (%d seconds)",
+			   MAC2STR(wpa_auth_get_spa(sm)),
 			   sm->wpa_auth->conf.wpa_ptk_rekey);
 		eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
 		eloop_register_timeout(sm->wpa_auth->conf.wpa_ptk_rekey, 0,
@@ -695,7 +712,7 @@
 
 #ifdef CONFIG_IEEE80211R_AP
 	if (sm->ft_completed) {
-		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
+		wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
 				"FT authentication already completed - do not start 4-way handshake");
 		/* Go to PTKINITDONE state to allow GTK rekeying */
 		sm->wpa_ptk_state = WPA_PTK_PTKINITDONE;
@@ -706,7 +723,7 @@
 
 #ifdef CONFIG_FILS
 	if (sm->fils_completed) {
-		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
+		wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
 				"FILS authentication already completed - do not start 4-way handshake");
 		/* Go to PTKINITDONE state to allow GTK rekeying */
 		sm->wpa_ptk_state = WPA_PTK_PTKINITDONE;
@@ -721,7 +738,7 @@
 		return wpa_sm_step(sm);
 	}
 
-	wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
+	wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
 			"start authentication");
 	sm->started = 1;
 
@@ -755,7 +772,8 @@
 			   MACSTR " (bit %u)",
 			   sm->ip_addr[0], sm->ip_addr[1],
 			   sm->ip_addr[2], sm->ip_addr[3],
-			   MAC2STR(sm->addr), sm->ip_addr_bit);
+			   MAC2STR(wpa_auth_get_spa(sm)),
+			   sm->ip_addr_bit);
 		bitfield_clear(sm->wpa_auth->ip_pool, sm->ip_addr_bit);
 	}
 #endif /* CONFIG_P2P */
@@ -787,7 +805,7 @@
 
 	wpa_auth = sm->wpa_auth;
 	if (wpa_auth->conf.wpa_strict_rekey && sm->has_GTK) {
-		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
+		wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
 				"strict rekeying - force GTK rekey since STA is leaving");
 		if (eloop_deplete_timeout(0, 500000, wpa_rekey_gtk,
 					  wpa_auth, NULL) == -1)
@@ -807,7 +825,7 @@
 		 * Freeing will be completed in the end of wpa_sm_step(). */
 		wpa_printf(MSG_DEBUG,
 			   "WPA: Registering pending STA state machine deinit for "
-			   MACSTR, MAC2STR(sm->addr));
+			   MACSTR, MAC2STR(wpa_auth_get_spa(sm)));
 		sm->pending_deinit = 1;
 	} else
 		wpa_free_sta_sm(sm);
@@ -822,7 +840,7 @@
 	if (!sm->use_ext_key_id && sm->wpa_auth->conf.wpa_deny_ptk0_rekey) {
 		wpa_printf(MSG_INFO,
 			   "WPA: PTK0 rekey not allowed, disconnect " MACSTR,
-			   MAC2STR(sm->addr));
+			   MAC2STR(wpa_auth_get_spa(sm)));
 		sm->Disconnect = true;
 		/* Try to encourage the STA to reconnect */
 		sm->disconnect_reason =
@@ -920,18 +938,19 @@
 				    struct wpa_state_machine *sm, int group)
 {
 	/* Supplicant reported a Michael MIC error */
-	wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+	wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
 			 "received EAPOL-Key Error Request (STA detected Michael MIC failure (group=%d))",
 			 group);
 
 	if (group && wpa_auth->conf.wpa_group != WPA_CIPHER_TKIP) {
-		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+		wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
 				"ignore Michael MIC failure report since group cipher is not TKIP");
 	} else if (!group && sm->pairwise != WPA_CIPHER_TKIP) {
-		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+		wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
 				"ignore Michael MIC failure report since pairwise cipher is not TKIP");
 	} else {
-		if (wpa_auth_mic_failure_report(wpa_auth, sm->addr) > 0)
+		if (wpa_auth_mic_failure_report(wpa_auth,
+						wpa_auth_get_spa(sm)) > 0)
 			return 1; /* STA entry was removed */
 		sm->dot11RSNAStatsTKIPRemoteMICFailures++;
 		wpa_auth->dot11RSNAStatsTKIPRemoteMICFailures++;
@@ -954,6 +973,10 @@
 	const u8 *pmk = NULL;
 	size_t pmk_len;
 	int vlan_id = 0;
+	u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN];
+	u8 pmk_r1[PMK_LEN_MAX];
+	size_t key_len;
+	int ret = -1;
 
 	os_memset(&PTK, 0, sizeof(PTK));
 	for (;;) {
@@ -975,8 +998,8 @@
 			pmk_len = sm->pmk_len;
 		}
 
-		if (wpa_derive_ptk(sm, sm->alt_SNonce, pmk, pmk_len, &PTK, 0) <
-		    0)
+		if (wpa_derive_ptk(sm, sm->alt_SNonce, pmk, pmk_len, &PTK, 0,
+				   pmk_r0, pmk_r1, pmk_r0_name, &key_len) < 0)
 			break;
 
 		if (wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK,
@@ -997,7 +1020,7 @@
 	if (!ok) {
 		wpa_printf(MSG_DEBUG,
 			   "WPA: Earlier SNonce did not result in matching MIC");
-		return -1;
+		goto fail;
 	}
 
 	wpa_printf(MSG_DEBUG,
@@ -1006,14 +1029,26 @@
 
 	if (vlan_id && wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) &&
 	    wpa_auth_update_vlan(sm->wpa_auth, sm->addr, vlan_id) < 0)
-		return -1;
+		goto fail;
+
+#ifdef CONFIG_IEEE80211R_AP
+	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt) && !sm->ft_completed) {
+		wpa_printf(MSG_DEBUG, "FT: Store PMK-R0/PMK-R1");
+		wpa_auth_ft_store_keys(sm, pmk_r0, pmk_r1, pmk_r0_name,
+				       key_len);
+	}
+#endif /* CONFIG_IEEE80211R_AP */
 
 	os_memcpy(sm->SNonce, sm->alt_SNonce, WPA_NONCE_LEN);
 	os_memcpy(&sm->PTK, &PTK, sizeof(PTK));
 	forced_memzero(&PTK, sizeof(PTK));
 	sm->PTK_valid = true;
 
-	return 0;
+	ret = 0;
+fail:
+	forced_memzero(pmk_r0, sizeof(pmk_r0));
+	forced_memzero(pmk_r1, sizeof(pmk_r1));
+	return ret;
 }
 
 
@@ -1063,7 +1098,7 @@
 	key_data_length = WPA_GET_BE16(mic + mic_len);
 	wpa_printf(MSG_DEBUG, "WPA: Received EAPOL-Key from " MACSTR
 		   " key_info=0x%x type=%u mic_len=%zu key_data_length=%u",
-		   MAC2STR(sm->addr), key_info, key->type,
+		   MAC2STR(wpa_auth_get_spa(sm)), key_info, key->type,
 		   mic_len, key_data_length);
 	wpa_hexdump(MSG_MSGDUMP,
 		    "WPA: EAPOL-Key header (ending before Key MIC)",
@@ -1138,7 +1173,7 @@
 			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,
+				wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
 						LOGGER_WARNING,
 						"advertised support for AES-128-CMAC, but did not use it");
 				return;
@@ -1147,7 +1182,7 @@
 			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,
+				wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
 						LOGGER_WARNING,
 						"did not use HMAC-SHA1-AES with CCMP/GCMP");
 				return;
@@ -1156,7 +1191,8 @@
 
 		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,
+			wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+					LOGGER_WARNING,
 					"did not use EAPOL-Key descriptor version 0 as required for AKM-defined cases");
 			return;
 		}
@@ -1166,7 +1202,8 @@
 		if (sm->req_replay_counter_used &&
 		    os_memcmp(key->replay_counter, sm->req_replay_counter,
 			      WPA_REPLAY_COUNTER_LEN) <= 0) {
-			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING,
+			wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+					LOGGER_WARNING,
 					"received EAPOL-Key request with replayed counter");
 			return;
 		}
@@ -1189,7 +1226,8 @@
 			 * pending requests, so allow the SNonce to be updated
 			 * even if we have already sent out EAPOL-Key 3/4.
 			 */
-			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+			wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+					 LOGGER_DEBUG,
 					 "Process SNonce update from STA based on retransmitted EAPOL-Key 1/4");
 			sm->update_snonce = 1;
 			os_memcpy(sm->alt_SNonce, sm->SNonce, WPA_NONCE_LEN);
@@ -1209,7 +1247,8 @@
 			 * there was two EAPOL-Key 2/4 messages and they had
 			 * different SNonce values.
 			 */
-			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+			wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+					 LOGGER_DEBUG,
 					 "Try to process received EAPOL-Key 4/4 based on old Replay Counter and SNonce from an earlier EAPOL-Key 1/4");
 			goto continue_processing;
 		}
@@ -1218,11 +1257,13 @@
 		    wpa_replay_counter_valid(sm->prev_key_replay,
 					     key->replay_counter) &&
 		    sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING) {
-			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+			wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+					 LOGGER_DEBUG,
 					 "ignore retransmitted EAPOL-Key %s - SNonce did not change",
 					 msgtxt);
 		} else {
-			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+			wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+					 LOGGER_DEBUG,
 					 "received EAPOL-Key %s with unexpected replay counter",
 					 msgtxt);
 		}
@@ -1242,7 +1283,7 @@
 #ifdef CONFIG_FILS
 	if (sm->wpa == WPA_VERSION_WPA2 && mic_len == 0 &&
 	    !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
-		wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+		wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
 				 "WPA: Encr Key Data bit not set even though AEAD cipher is supposed to be used - drop frame");
 		return;
 	}
@@ -1254,7 +1295,8 @@
 		    sm->wpa_ptk_state != WPA_PTK_PTKCALCNEGOTIATING &&
 		    (!sm->update_snonce ||
 		     sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING)) {
-			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+			wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+					 LOGGER_INFO,
 					 "received EAPOL-Key msg 2/4 in invalid state (%d) - dropped",
 					 sm->wpa_ptk_state);
 			return;
@@ -1281,7 +1323,8 @@
 	case PAIRWISE_4:
 		if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING ||
 		    !sm->PTK_valid) {
-			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+			wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+					 LOGGER_INFO,
 					 "received EAPOL-Key msg 4/4 in invalid state (%d) - dropped",
 					 sm->wpa_ptk_state);
 			return;
@@ -1290,7 +1333,8 @@
 	case GROUP_2:
 		if (sm->wpa_ptk_group_state != WPA_PTK_GROUP_REKEYNEGOTIATING
 		    || !sm->PTK_valid) {
-			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+			wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+					 LOGGER_INFO,
 					 "received EAPOL-Key msg 2/2 in invalid state (%d) - dropped",
 					 sm->wpa_ptk_group_state);
 			return;
@@ -1300,18 +1344,18 @@
 		break;
 	}
 
-	wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+	wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
 			 "received EAPOL-Key frame (%s)", msgtxt);
 
 	if (key_info & WPA_KEY_INFO_ACK) {
-		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+		wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
 				"received invalid EAPOL-Key: Key Ack set");
 		return;
 	}
 
 	if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
 	    !(key_info & WPA_KEY_INFO_MIC)) {
-		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+		wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
 				"received invalid EAPOL-Key: Key MIC not set");
 		return;
 	}
@@ -1319,7 +1363,7 @@
 #ifdef CONFIG_FILS
 	if (wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
 	    (key_info & WPA_KEY_INFO_MIC)) {
-		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+		wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
 				"received invalid EAPOL-Key: Key MIC set");
 		return;
 	}
@@ -1332,7 +1376,8 @@
 				       data, data_len) &&
 		    (msg != PAIRWISE_4 || !sm->alt_snonce_valid ||
 		     wpa_try_alt_snonce(sm, data, data_len))) {
-			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+			wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+					LOGGER_INFO,
 					"received EAPOL-Key with invalid MIC");
 #ifdef TEST_FUZZ
 			wpa_printf(MSG_INFO,
@@ -1345,7 +1390,8 @@
 		if (!mic_len &&
 		    wpa_aead_decrypt(sm, &sm->PTK, data, data_len,
 				     &key_data_length) < 0) {
-			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+			wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+					LOGGER_INFO,
 					"received EAPOL-Key with invalid MIC");
 #ifdef TEST_FUZZ
 			wpa_printf(MSG_INFO,
@@ -1369,7 +1415,8 @@
 			os_memcpy(sm->req_replay_counter, key->replay_counter,
 				  WPA_REPLAY_COUNTER_LEN);
 		} else {
-			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+			wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+					LOGGER_INFO,
 					"received EAPOL-Key request with invalid MIC");
 			return;
 		}
@@ -1385,7 +1432,8 @@
 				    !(key_info & WPA_KEY_INFO_KEY_TYPE)) > 0)
 				return; /* STA entry was removed */
 		} else if (key_info & WPA_KEY_INFO_KEY_TYPE) {
-			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+			wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+					LOGGER_INFO,
 					"received EAPOL-Key Request for new 4-Way Handshake");
 			wpa_request_new_ptk(sm);
 		} else if (key_data_length > 0 &&
@@ -1393,7 +1441,8 @@
 					     &kde) == 0 &&
 			   kde.mac_addr) {
 		} else {
-			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+			wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+					LOGGER_INFO,
 					"received EAPOL-Key Request for GTK rekeying");
 			eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
 			if (wpa_auth_gtk_rekey_in_process(wpa_auth))
@@ -1504,7 +1553,8 @@
 	}
 
 	sm->pending_1_of_4_timeout = 0;
-	wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "EAPOL-Key timeout");
+	wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
+			"EAPOL-Key timeout");
 	sm->TimeoutEvt = true;
 	wpa_sm_step(sm);
 }
@@ -1696,7 +1746,8 @@
 
 	if (key_info & WPA_KEY_INFO_MIC) {
 		if (!sm->PTK_valid || !mic_len) {
-			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
+			wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+					LOGGER_DEBUG,
 					"PTK not valid when sending EAPOL-Key frame");
 			os_free(hdr);
 			return;
@@ -1712,7 +1763,8 @@
 		if (!pairwise &&
 		    conf->corrupt_gtk_rekey_mic_probability > 0.0 &&
 		    drand48() < conf->corrupt_gtk_rekey_mic_probability) {
-			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+			wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+					LOGGER_INFO,
 					"Corrupting group EAPOL-Key Key MIC");
 			key_mic[0]++;
 		}
@@ -1842,7 +1894,7 @@
 	if (!sm)
 		return -1;
 
-	wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+	wpa_auth_vlogger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
 			 "event %d notification", event);
 
 	switch (event) {
@@ -1902,7 +1954,7 @@
 		    sm->wpa_auth->conf.wpa_deny_ptk0_rekey) {
 			wpa_printf(MSG_INFO,
 				   "WPA: PTK0 rekey not allowed, disconnect "
-				   MACSTR, MAC2STR(sm->addr));
+				   MACSTR, MAC2STR(wpa_auth_get_spa(sm)));
 			sm->Disconnect = true;
 			/* Try to encourage the STA to reconnect */
 			sm->disconnect_reason =
@@ -2149,7 +2201,8 @@
 		sm->disconnect_reason = WLAN_REASON_INVALID_PMKID;
 		return;
 #endif /* CONFIG_DPP */
-	} else if (wpa_auth_get_msk(sm->wpa_auth, sm->addr, msk, &len) == 0) {
+	} else if (wpa_auth_get_msk(sm->wpa_auth, wpa_auth_get_spa(sm),
+				    msk, &len) == 0) {
 		unsigned int pmk_len;
 
 		if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt))
@@ -2257,7 +2310,7 @@
 		return;
 	}
 
-	wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+	wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
 			"sending 1/4 msg of 4-Way Handshake");
 	/*
 	 * For infrastructure BSS cases, it is better for the AP not to include
@@ -2336,8 +2389,10 @@
 			 * Calculate PMKID since no PMKSA cache entry was
 			 * available with pre-calculated PMKID.
 			 */
-			rsn_pmkid(sm->PMK, sm->pmk_len, sm->wpa_auth->addr,
-				  sm->addr, &pmkid[2 + RSN_SELECTOR_LEN],
+			rsn_pmkid(sm->PMK, sm->pmk_len,
+				  wpa_auth_get_aa(sm),
+				  wpa_auth_get_spa(sm),
+				  &pmkid[2 + RSN_SELECTOR_LEN],
 				  sm->wpa_key_mgmt);
 			wpa_hexdump(MSG_DEBUG,
 				    "RSN: Message 1/4 PMKID derived from PMK",
@@ -2356,7 +2411,9 @@
 
 static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
 			  const u8 *pmk, unsigned int pmk_len,
-			  struct wpa_ptk *ptk, int force_sha256)
+			  struct wpa_ptk *ptk, int force_sha256,
+			  u8 *pmk_r0, u8 *pmk_r1, u8 *pmk_r0_name,
+			  size_t *key_len)
 {
 	const u8 *z = NULL;
 	size_t z_len = 0, kdk_len;
@@ -2377,12 +2434,15 @@
 
 			ret = wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len,
 						sm->SNonce, sm->ANonce,
-						sm->addr, sm->wpa_auth->addr,
+						wpa_auth_get_spa(sm),
+						wpa_auth_get_aa(sm),
 						sm->pmk_r1_name, ptk,
 						ptk_name, sm->wpa_key_mgmt,
 						sm->pairwise, kdk_len);
 		} else {
-			ret = wpa_auth_derive_ptk_ft(sm, ptk);
+			ret = wpa_auth_derive_ptk_ft(sm, ptk, pmk_r0, pmk_r1,
+						     pmk_r0_name, key_len,
+						     kdk_len);
 		}
 		if (ret) {
 			wpa_printf(MSG_ERROR, "FT: PTK derivation failed");
@@ -2416,9 +2476,9 @@
 	if (force_sha256)
 		akmp |= WPA_KEY_MGMT_PSK_SHA256;
 	ret = wpa_pmk_to_ptk(pmk, pmk_len, "Pairwise key expansion",
-			     sm->wpa_auth->addr, sm->addr, sm->ANonce,
-			     snonce, ptk, akmp, sm->pairwise, z, z_len,
-			     kdk_len);
+			     wpa_auth_get_aa(sm), wpa_auth_get_spa(sm),
+			     sm->ANonce, snonce, ptk, akmp,
+			     sm->pairwise, z, z_len, kdk_len);
 	if (ret) {
 		wpa_printf(MSG_DEBUG,
 			   "WPA: PTK derivation failed");
@@ -2459,7 +2519,8 @@
 	else
 		kdk_len = 0;
 
-	res = fils_pmk_to_ptk(pmk, pmk_len, sm->addr, sm->wpa_auth->addr,
+	res = fils_pmk_to_ptk(pmk, pmk_len, wpa_auth_get_spa(sm),
+			      wpa_auth_get_aa(sm),
 			      snonce, anonce, dhss, dhss_len,
 			      &sm->PTK, ick, &ick_len,
 			      sm->wpa_key_mgmt, sm->pairwise,
@@ -2493,7 +2554,7 @@
 				      conf->mobility_domain,
 				      conf->r0_key_holder,
 				      conf->r0_key_holder_len,
-				      sm->addr, pmk_r0, pmk_r0_name,
+				      wpa_auth_get_spa(sm), pmk_r0, pmk_r0_name,
 				      sm->wpa_key_mgmt) < 0)
 			return -1;
 
@@ -2501,7 +2562,8 @@
 		forced_memzero(fils_ft, sizeof(fils_ft));
 
 		res = wpa_derive_pmk_r1_name(pmk_r0_name, conf->r1_key_holder,
-					     sm->addr, sm->pmk_r1_name,
+					     wpa_auth_get_spa(sm),
+					     sm->pmk_r1_name,
 					     fils_ft_len);
 		forced_memzero(pmk_r0, PMK_LEN_MAX);
 		if (res < 0)
@@ -2513,7 +2575,8 @@
 #endif /* CONFIG_IEEE80211R_AP */
 
 	res = fils_key_auth_sk(ick, ick_len, snonce, anonce,
-			       sm->addr, sm->wpa_auth->addr,
+			       wpa_auth_get_spa(sm),
+			       wpa_auth_get_aa(sm),
 			       g_sta ? wpabuf_head(g_sta) : NULL,
 			       g_sta ? wpabuf_len(g_sta) : 0,
 			       g_ap ? wpabuf_head(g_ap) : NULL,
@@ -2548,7 +2611,7 @@
 	key_data_len = WPA_GET_BE16(pos);
 	if (key_data_len < AES_BLOCK_SIZE ||
 	    key_data_len > buf_len - sizeof(*hdr) - sizeof(*key) - 2) {
-		wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO,
+		wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
 				"No room for AES-SIV data in the frame");
 		return -1;
 	}
@@ -2564,7 +2627,7 @@
 	aad_len[0] = pos - buf;
 	if (aes_siv_decrypt(ptk->kek, ptk->kek_len, pos, key_data_len,
 			    1, aad, aad_len, tmp) < 0) {
-		wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO,
+		wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
 				"Invalid AES-SIV data in the frame");
 		bin_clear_free(tmp, key_data_len);
 		return -1;
@@ -3067,6 +3130,9 @@
 	struct wpa_eapol_ie_parse kde;
 	int vlan_id = 0;
 	int owe_ptk_workaround = !!wpa_auth->conf.owe_ptk_workaround;
+	u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN];
+	u8 pmk_r1[PMK_LEN_MAX];
+	size_t key_len;
 
 	SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk);
 	sm->EAPOLKeyReceived = false;
@@ -3105,7 +3171,8 @@
 		}
 
 		if (wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK,
-				   owe_ptk_workaround == 2) < 0)
+				   owe_ptk_workaround == 2, pmk_r0, pmk_r1,
+				   pmk_r0_name, &key_len) < 0)
 			break;
 
 		if (mic_len &&
@@ -3154,15 +3221,16 @@
 						 sm->last_rx_eapol_key,
 						 sm->last_rx_eapol_key_len);
 		sm->waiting_radius_psk = 1;
-		return;
+		goto out;
 	}
 
 	if (!ok) {
-		wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+		wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm),
+				LOGGER_DEBUG,
 				"invalid MIC in msg 2/4 of 4-Way Handshake");
 		if (psk_found)
 			wpa_auth_psk_failure_report(sm->wpa_auth, sm->addr);
-		return;
+		goto out;
 	}
 
 	/*
@@ -3176,12 +3244,12 @@
 	key_data_length = WPA_GET_BE16(mic + mic_len);
 	if (key_data_length > sm->last_rx_eapol_key_len - sizeof(*hdr) -
 	    sizeof(*key) - mic_len - 2)
-		return;
+		goto out;
 
 	if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) {
-		wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+		wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
 				 "received EAPOL-Key msg 2/4 with invalid Key Data contents");
-		return;
+		goto out;
 	}
 	if (kde.rsn_ie) {
 		eapol_key_ie = kde.rsn_ie;
@@ -3197,7 +3265,7 @@
 	if (!sm->wpa_ie ||
 	    wpa_compare_rsn_ie(ft, sm->wpa_ie, sm->wpa_ie_len,
 			       eapol_key_ie, eapol_key_ie_len)) {
-		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+		wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
 				"WPA IE from (Re)AssocReq did not match with msg 2/4");
 		if (sm->wpa_ie) {
 			wpa_hexdump(MSG_DEBUG, "WPA IE in AssocReq",
@@ -3208,14 +3276,14 @@
 		/* MLME-DEAUTHENTICATE.request */
 		wpa_sta_disconnect(wpa_auth, sm->addr,
 				   WLAN_REASON_PREV_AUTH_NOT_VALID);
-		return;
+		goto out;
 	}
 	if ((!sm->rsnxe && kde.rsnxe) ||
 	    (sm->rsnxe && !kde.rsnxe) ||
 	    (sm->rsnxe && kde.rsnxe &&
 	     (sm->rsnxe_len != kde.rsnxe_len ||
 	      os_memcmp(sm->rsnxe, kde.rsnxe, sm->rsnxe_len) != 0))) {
-		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+		wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
 				"RSNXE from (Re)AssocReq did not match the one in EAPOL-Key msg 2/4");
 		wpa_hexdump(MSG_DEBUG, "RSNXE in AssocReq",
 			    sm->rsnxe, sm->rsnxe_len);
@@ -3224,7 +3292,7 @@
 		/* MLME-DEAUTHENTICATE.request */
 		wpa_sta_disconnect(wpa_auth, sm->addr,
 				   WLAN_REASON_PREV_AUTH_NOT_VALID);
-		return;
+		goto out;
 	}
 #ifdef CONFIG_OCV
 	if (wpa_auth_uses_ocv(sm)) {
@@ -3234,33 +3302,37 @@
 		enum oci_verify_result res;
 
 		if (wpa_channel_info(wpa_auth, &ci) != 0) {
-			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+			wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+					LOGGER_INFO,
 					"Failed to get channel info to validate received OCI in EAPOL-Key 2/4");
-			return;
+			goto out;
 		}
 
 		if (get_sta_tx_parameters(sm,
 					  channel_width_to_int(ci.chanwidth),
 					  ci.seg1_idx, &tx_chanwidth,
 					  &tx_seg1_idx) < 0)
-			return;
+			goto out;
 
 		res = ocv_verify_tx_params(kde.oci, kde.oci_len, &ci,
 					   tx_chanwidth, tx_seg1_idx);
 		if (wpa_auth_uses_ocv(sm) == 2 && res == OCI_NOT_FOUND) {
 			/* Work around misbehaving STAs */
-			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+			wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+					 LOGGER_INFO,
 					 "Disable OCV with a STA that does not send OCI");
 			wpa_auth_set_ocv(sm, 0);
 		} else if (res != OCI_SUCCESS) {
-			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+			wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+					 LOGGER_INFO,
 					 "OCV failed: %s", ocv_errorstr);
 			if (wpa_auth->conf.msg_ctx)
 				wpa_msg(wpa_auth->conf.msg_ctx, MSG_INFO,
 					OCV_FAILURE "addr=" MACSTR
 					" frame=eapol-key-m2 error=%s",
-					MAC2STR(sm->addr), ocv_errorstr);
-			return;
+					MAC2STR(wpa_auth_get_spa(sm)),
+					ocv_errorstr);
+			goto out;
 		}
 	}
 #endif /* CONFIG_OCV */
@@ -3268,7 +3340,7 @@
 	if (ft && ft_check_msg_2_of_4(wpa_auth, sm, &kde) < 0) {
 		wpa_sta_disconnect(wpa_auth, sm->addr,
 				   WLAN_REASON_PREV_AUTH_NOT_VALID);
-		return;
+		goto out;
 	}
 #endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_P2P
@@ -3288,7 +3360,8 @@
 				   MACSTR " (bit %u)",
 				   sm->ip_addr[0], sm->ip_addr[1],
 				   sm->ip_addr[2], sm->ip_addr[3],
-				   MAC2STR(sm->addr), sm->ip_addr_bit);
+				   MAC2STR(wpa_auth_get_spa(sm)),
+				   sm->ip_addr_bit);
 		}
 	}
 #endif /* CONFIG_P2P */
@@ -3306,7 +3379,7 @@
 				   "DPP: Peer indicated it supports PFS and local configuration allows this, but PFS was not negotiated for the association");
 			wpa_sta_disconnect(wpa_auth, sm->addr,
 					   WLAN_REASON_PREV_AUTH_NOT_VALID);
-			return;
+			goto out;
 		}
 	}
 #endif /* CONFIG_DPP2 */
@@ -3319,14 +3392,15 @@
 		 */
 		if (os_memcmp_const(sm->sup_pmk_r1_name, sm->pmk_r1_name,
 				    WPA_PMK_NAME_LEN) != 0) {
-			wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+			wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm),
+					LOGGER_DEBUG,
 					"PMKR1Name mismatch in FT 4-way handshake");
 			wpa_hexdump(MSG_DEBUG,
 				    "FT: PMKR1Name from Supplicant",
 				    sm->sup_pmk_r1_name, WPA_PMK_NAME_LEN);
 			wpa_hexdump(MSG_DEBUG, "FT: Derived PMKR1Name",
 				    sm->pmk_r1_name, WPA_PMK_NAME_LEN);
-			return;
+			goto out;
 		}
 	}
 #endif /* CONFIG_IEEE80211R_AP */
@@ -3335,7 +3409,7 @@
 	    wpa_auth_update_vlan(wpa_auth, sm->addr, vlan_id) < 0) {
 		wpa_sta_disconnect(wpa_auth, sm->addr,
 				   WLAN_REASON_PREV_AUTH_NOT_VALID);
-		return;
+		goto out;
 	}
 
 	sm->pending_1_of_4_timeout = 0;
@@ -3351,9 +3425,20 @@
 
 	sm->MICVerified = true;
 
+#ifdef CONFIG_IEEE80211R_AP
+	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt) && !sm->ft_completed) {
+		wpa_printf(MSG_DEBUG, "FT: Store PMK-R0/PMK-R1");
+		wpa_auth_ft_store_keys(sm, pmk_r0, pmk_r1, pmk_r0_name,
+				       key_len);
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+
 	os_memcpy(&sm->PTK, &PTK, sizeof(PTK));
 	forced_memzero(&PTK, sizeof(PTK));
 	sm->PTK_valid = true;
+out:
+	forced_memzero(pmk_r0, sizeof(pmk_r0));
+	forced_memzero(pmk_r1, sizeof(pmk_r1));
 }
 
 
@@ -3577,7 +3662,7 @@
 		wpa_ie = wpa_ie_buf;
 	}
 #endif /* CONFIG_TESTING_OPTIONS */
-	wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+	wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
 			"sending 3/4 msg of 4-Way Handshake");
 	if (sm->wpa == WPA_VERSION_WPA2) {
 		if (sm->use_ext_key_id && sm->TimeoutCtr == 1 &&
@@ -3639,7 +3724,8 @@
 			 * by setting the Secure bit here even in the case of
 			 * WPA if the supplicant used it first.
 			 */
-			wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+			wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm),
+					LOGGER_DEBUG,
 					"STA used Secure bit in WPA msg 2/4 - set Secure for 3/4 as workaround");
 			secure = 1;
 		}
@@ -3865,14 +3951,14 @@
 		sm->PInitAKeys = true;
 	else
 		sm->has_GTK = true;
-	wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO,
+	wpa_auth_vlogger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
 			 "pairwise key handshake completed (%s)",
 			 sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN");
 	wpa_msg(sm->wpa_auth->conf.msg_ctx, MSG_INFO, "EAPOL-4WAY-HS-COMPLETED "
 		MACSTR, MAC2STR(sm->addr));
 
 #ifdef CONFIG_IEEE80211R_AP
-	wpa_ft_push_pmk_r1(sm->wpa_auth, sm->addr);
+	wpa_ft_push_pmk_r1(sm->wpa_auth, wpa_auth_get_spa(sm));
 #endif /* CONFIG_IEEE80211R_AP */
 
 	sm->ptkstart_without_success = 0;
@@ -3888,7 +3974,7 @@
 		SM_ENTER(WPA_PTK, INITIALIZE);
 	else if (sm->Disconnect
 		 /* || FIX: dot11RSNAConfigSALifetime timeout */) {
-		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
+		wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
 				"WPA_PTK: sm->Disconnect");
 		SM_ENTER(WPA_PTK, DISCONNECT);
 	}
@@ -3937,7 +4023,8 @@
 #endif /* CONFIG_DPP */
 		} else {
 			wpa_auth->dot11RSNA4WayHandshakeFailures++;
-			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+			wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+					LOGGER_INFO,
 					"INITPMK - keyAvailable = false");
 			SM_ENTER(WPA_PTK, DISCONNECT);
 		}
@@ -3956,7 +4043,8 @@
 				   "INITPSK: No PSK yet available for STA - use RADIUS later");
 			SM_ENTER(WPA_PTK, PTKSTART);
 		} else {
-			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+			wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+					LOGGER_INFO,
 					"no PSK configured for the STA");
 			wpa_auth->dot11RSNA4WayHandshakeFailures++;
 			SM_ENTER(WPA_PTK, DISCONNECT);
@@ -3968,7 +4056,8 @@
 			SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING);
 		else if (sm->TimeoutCtr > conf->wpa_pairwise_update_count) {
 			wpa_auth->dot11RSNA4WayHandshakeFailures++;
-			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+			wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+					 LOGGER_DEBUG,
 					 "PTKSTART: Retry limit %u reached",
 					 conf->wpa_pairwise_update_count);
 			sm->disconnect_reason =
@@ -4000,7 +4089,8 @@
 			 (conf->wpa_disable_eapol_key_retries &&
 			  sm->TimeoutCtr > 1)) {
 			wpa_auth->dot11RSNA4WayHandshakeFailures++;
-			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+			wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+					 LOGGER_DEBUG,
 					 "PTKINITNEGOTIATING: Retry limit %u reached",
 					 conf->wpa_pairwise_update_count);
 			sm->disconnect_reason =
@@ -4057,7 +4147,7 @@
 	os_memset(rsc, 0, WPA_KEY_RSC_LEN);
 	if (gsm->wpa_group_state == WPA_GROUP_SETKEYSDONE)
 		wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc);
-	wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+	wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
 			"sending 1/2 msg of Group Key Handshake");
 
 	gtk = gsm->GTK[gsm->GN - 1];
@@ -4138,7 +4228,7 @@
 		return;
 
 	if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) {
-		wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+		wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
 				 "received EAPOL-Key group msg 2/2 with invalid Key Data contents");
 		return;
 	}
@@ -4149,7 +4239,8 @@
 		int tx_seg1_idx;
 
 		if (wpa_channel_info(wpa_auth, &ci) != 0) {
-			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+			wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+					LOGGER_INFO,
 					"Failed to get channel info to validate received OCI in EAPOL-Key group 2/2");
 			return;
 		}
@@ -4163,13 +4254,15 @@
 		if (ocv_verify_tx_params(kde.oci, kde.oci_len, &ci,
 					 tx_chanwidth, tx_seg1_idx) !=
 		    OCI_SUCCESS) {
-			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+			wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+					 LOGGER_INFO,
 					 "OCV failed: %s", ocv_errorstr);
 			if (wpa_auth->conf.msg_ctx)
 				wpa_msg(wpa_auth->conf.msg_ctx, MSG_INFO,
 					OCV_FAILURE "addr=" MACSTR
 					" frame=eapol-key-g2 error=%s",
-					MAC2STR(sm->addr), ocv_errorstr);
+					MAC2STR(wpa_auth_get_spa(sm)),
+					ocv_errorstr);
 			return;
 		}
 	}
@@ -4180,7 +4273,7 @@
 	sm->GUpdateStationKeys = false;
 	sm->GTimeoutCtr = 0;
 	/* FIX: MLME.SetProtection.Request(TA, Tx_Rx) */
-	wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+	wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
 			 "group key handshake completed (%s)",
 			 sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN");
 	sm->has_GTK = true;
@@ -4195,7 +4288,7 @@
 	sm->GUpdateStationKeys = false;
 	sm->Disconnect = true;
 	sm->disconnect_reason = WLAN_REASON_GROUP_KEY_UPDATE_TIMEOUT;
-	wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO,
+	wpa_auth_vlogger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
 			 "group key handshake failed (%s) after %u tries",
 			 sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN",
 			 sm->wpa_auth->conf.wpa_group_update_count);
@@ -4308,7 +4401,8 @@
 		return 0;
 
 	if (sm->wpa_ptk_state != WPA_PTK_PTKINITDONE) {
-		wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+		wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm),
+				LOGGER_DEBUG,
 				"Not in PTKINITDONE; skip Group Key update");
 		sm->GUpdateStationKeys = false;
 		return 0;
@@ -4319,7 +4413,8 @@
 		 * Since we clear the GKeyDoneStations before the loop, the
 		 * station needs to be counted here anyway.
 		 */
-		wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+		wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm),
+				LOGGER_DEBUG,
 				"GUpdateStationKeys was already set when marking station for GTK rekeying");
 	}
 
@@ -4553,7 +4648,7 @@
 	if (sm->group == ctx) {
 		wpa_printf(MSG_DEBUG, "WPA: Mark STA " MACSTR
 			   " for disconnection due to fatal failure",
-			   MAC2STR(sm->addr));
+			   MAC2STR(wpa_auth_get_spa(sm)));
 		sm->Disconnect = true;
 	}
 
@@ -4646,7 +4741,7 @@
 	if (sm->pending_deinit) {
 		wpa_printf(MSG_DEBUG,
 			   "WPA: Completing pending STA state machine deinit for "
-			   MACSTR, MAC2STR(sm->addr));
+			   MACSTR, MAC2STR(wpa_auth_get_spa(sm)));
 		wpa_free_sta_sm(sm);
 		return 1;
 	}
@@ -4982,7 +5077,8 @@
 	wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK", pmk, pmk_len);
 	if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, pmk_len, NULL,
 				 sm->PTK.kck, sm->PTK.kck_len,
-				 sm->wpa_auth->addr, sm->addr, session_timeout,
+				 wpa_auth_get_aa(sm),
+				 wpa_auth_get_spa(sm), session_timeout,
 				 eapol, sm->wpa_key_mgmt))
 		return 0;
 
@@ -5392,7 +5488,7 @@
 
 	wpa_printf(MSG_DEBUG, "WPA: Moving STA " MACSTR
 		   " to use group state machine for VLAN ID %d",
-		   MAC2STR(sm->addr), vlan_id);
+		   MAC2STR(wpa_auth_get_spa(sm)), vlan_id);
 
 	wpa_group_get(sm->wpa_auth, group);
 	wpa_group_put(sm->wpa_auth, sm->group);
@@ -5408,7 +5504,7 @@
 	if (!wpa_auth || !sm)
 		return;
 	wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key TX status for STA " MACSTR
-		   " ack=%d", MAC2STR(sm->addr), ack);
+		   " ack=%d", MAC2STR(wpa_auth_get_spa(sm)), ack);
 	if (sm->pending_1_of_4_timeout && ack) {
 		/*
 		 * Some deployed supplicant implementations update their SNonce
@@ -5606,7 +5702,7 @@
 		anonce = anonce_buf;
 	}
 
-	wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+	wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
 			"sending 1/4 msg of 4-Way Handshake (TESTING)");
 	wpa_send_eapol(sm->wpa_auth, sm,
 		       WPA_KEY_INFO_ACK | WPA_KEY_INFO_KEY_TYPE, NULL,
@@ -5648,7 +5744,7 @@
 			wpa_ie = wpa_ie + wpa_ie[1] + 2;
 		wpa_ie_len = wpa_ie[1] + 2;
 	}
-	wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+	wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
 			"sending 3/4 msg of 4-Way Handshake (TESTING)");
 	if (sm->wpa == WPA_VERSION_WPA2) {
 		/* WPA2 send GTK in the 4-way handshake */
@@ -5673,7 +5769,8 @@
 			 * by setting the Secure bit here even in the case of
 			 * WPA if the supplicant used it first.
 			 */
-			wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+			wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm),
+					LOGGER_DEBUG,
 					"STA used Secure bit in WPA msg 2/4 - set Secure for 3/4 as workaround");
 			secure = 1;
 		}
@@ -5810,7 +5907,7 @@
 	/* Send EAPOL(1, 1, 1, !Pair, G, RSC, GNonce, MIC(PTK), GTK[GN]) */
 	os_memset(rsc, 0, WPA_KEY_RSC_LEN);
 	/* Use 0 RSC */
-	wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+	wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
 			"sending 1/2 msg of Group Key Handshake (TESTING)");
 
 	gtk = gsm->GTK[gsm->GN - 1];
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index 88d63bb..35585cd 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -35,6 +35,9 @@
 const unsigned int ftRRBseqTimeout = 10;
 const unsigned int ftRRBmaxQueueLen = 100;
 
+/* TODO: make these configurable */
+static const int dot11RSNAConfigPMKLifetime = 43200;
+
 
 static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm,
 				     const u8 *current_ap, const u8 *sta_addr,
@@ -2109,11 +2112,11 @@
 }
 
 
-int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk)
+int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk,
+			   u8 *pmk_r0, u8 *pmk_r1, u8 *pmk_r0_name,
+			   size_t *key_len, size_t kdk_len)
 {
-	u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN];
 	size_t pmk_r0_len, pmk_r1_len;
-	u8 pmk_r1[PMK_LEN_MAX];
 	u8 ptk_name[WPA_PMK_NAME_LEN];
 	const u8 *mdid = sm->wpa_auth->conf.mobility_domain;
 	const u8 *r0kh = sm->wpa_auth->conf.r0_key_holder;
@@ -2121,12 +2124,6 @@
 	const u8 *r1kh = sm->wpa_auth->conf.r1_key_holder;
 	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;
 	const u8 *mpmk;
 	size_t mpmk_len;
 
@@ -2139,7 +2136,7 @@
 		pmk_r0_len = SHA384_MAC_LEN;
 	else
 		pmk_r0_len = PMK_LEN;
-	pmk_r1_len = pmk_r0_len;
+	*key_len = pmk_r1_len = pmk_r0_len;
 
 	if (sm->xxkey_len > 0) {
 		mpmk = sm->xxkey;
@@ -2153,10 +2150,39 @@
 		return -1;
 	}
 
+	if (wpa_derive_pmk_r0(mpmk, mpmk_len, ssid, ssid_len, mdid,
+			      r0kh, r0kh_len, sm->addr,
+			      pmk_r0, pmk_r0_name,
+			      sm->wpa_key_mgmt) < 0 ||
+	    wpa_derive_pmk_r1(pmk_r0, pmk_r0_len, pmk_r0_name, r1kh, sm->addr,
+			      pmk_r1, sm->pmk_r1_name) < 0)
+		return -1;
+
+	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,
+				 kdk_len);
+}
+
+
+void wpa_auth_ft_store_keys(struct wpa_state_machine *sm, const u8 *pmk_r0,
+			    const u8 *pmk_r1, const u8 *pmk_r0_name,
+			    size_t key_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 (psk_local && wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt))
+		return;
+
 	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;
+		return;
 	}
 
 	identity_len = wpa_ft_get_identity(sm->wpa_auth, sm->addr, &identity);
@@ -2164,31 +2190,16 @@
 					       &radius_cui);
 	session_timeout = wpa_ft_get_session_timeout(sm->wpa_auth, sm->addr);
 
-	if (wpa_derive_pmk_r0(mpmk, mpmk_len, ssid, ssid_len, mdid,
-			      r0kh, r0kh_len, sm->addr,
-			      pmk_r0, pmk_r0_name,
-			      sm->wpa_key_mgmt) < 0)
-		return -1;
-	if (!psk_local || !wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt))
-		wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_len,
-				    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_len, pmk_r0_name, r1kh, sm->addr,
-			      pmk_r1, sm->pmk_r1_name) < 0)
-		return -1;
-	if (!psk_local || !wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt))
-		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, 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,
-				 0);
+	wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, key_len,
+			    pmk_r0_name,
+			    sm->pairwise, &vlan, expires_in,
+			    session_timeout, identity, identity_len,
+			    radius_cui, radius_cui_len);
+	wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, key_len,
+			    sm->pmk_r1_name, sm->pairwise, &vlan,
+			    expires_in, session_timeout, identity,
+			    identity_len, radius_cui, radius_cui_len);
 }
 
 
@@ -2941,6 +2952,9 @@
 	/* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */
 	sm->pairwise_set = true;
 	sm->tk_already_set = true;
+
+	wpa_auth_store_ptksa(sm->wpa_auth, sm->addr, sm->pairwise,
+			     dot11RSNAConfigPMKLifetime, &sm->PTK);
 }
 
 
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index 7ed3f2b..d401550 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -296,6 +296,9 @@
 int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth,
 			   int (*cb)(struct wpa_authenticator *a, void *ctx),
 			   void *cb_ctx);
+void wpa_auth_store_ptksa(struct wpa_authenticator *wpa_auth,
+			  const u8 *addr, int cipher,
+			  u32 life_time, const struct wpa_ptk *ptk);
 
 #ifdef CONFIG_IEEE80211R_AP
 int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len);
@@ -304,7 +307,12 @@
 		   const u8 *anonce, const u8 *snonce,
 		   u8 *buf, size_t len, const u8 *subelem,
 		   size_t subelem_len, int rsnxe_used);
-int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk);
+int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk,
+			   u8 *pmk_r0, u8 *pmk_r1, u8 *pmk_r0_name,
+			   size_t *key_len, size_t kdk_len);
+void wpa_auth_ft_store_keys(struct wpa_state_machine *sm, const u8 *pmk_r0,
+			    const u8 *pmk_r1, const u8 *pmk_r0_name,
+			    size_t key_len);
 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 retry);
diff --git a/src/ap/wpa_auth_kay.c b/src/ap/wpa_auth_kay.c
index e2c4e10..625f405 100644
--- a/src/ap/wpa_auth_kay.c
+++ b/src/ap/wpa_auth_kay.c
@@ -327,6 +327,7 @@
 	res = ieee802_1x_kay_init(kay_ctx, policy,
 				  hapd->conf->macsec_replay_protect,
 				  hapd->conf->macsec_replay_window,
+				  hapd->conf->macsec_offload,
 				  hapd->conf->macsec_port,
 				  hapd->conf->mka_priority,
 				  hapd->conf->macsec_csindex,
@@ -352,33 +353,6 @@
 }
 
 
-static int ieee802_1x_auth_get_session_id(struct hostapd_data *hapd,
-					  struct sta_info *sta, u8 *sid,
-					  size_t *len)
-{
-	const u8 *session_id;
-	size_t id_len, need_len;
-
-	session_id = ieee802_1x_get_session_id(sta->eapol_sm, &id_len);
-	if (!session_id) {
-		wpa_printf(MSG_DEBUG,
-			   "MACsec: Failed to get SessionID from EAPOL state machines");
-		return -1;
-	}
-
-	need_len = 1 + 2 * 32 /* random size */;
-	if (need_len > id_len) {
-		wpa_printf(MSG_DEBUG, "EAP Session-Id not long enough");
-		return -1;
-	}
-
-	os_memcpy(sid, session_id, need_len);
-	*len = need_len;
-
-	return 0;
-}
-
-
 static int ieee802_1x_auth_get_msk(struct hostapd_data *hapd,
 				   struct sta_info *sta, u8 *msk, size_t *len)
 {
@@ -410,8 +384,8 @@
 void * ieee802_1x_notify_create_actor_hapd(struct hostapd_data *hapd,
 					   struct sta_info *sta)
 {
-	u8 *sid;
-	size_t sid_len = 128;
+	const u8 *sid;
+	size_t sid_len;
 	struct mka_key_name *ckn;
 	struct mka_key *cak;
 	struct mka_key *msk;
@@ -425,10 +399,9 @@
 		   MACSTR, MAC2STR(sta->addr));
 
 	msk = os_zalloc(sizeof(*msk));
-	sid = os_zalloc(sid_len);
 	ckn = os_zalloc(sizeof(*ckn));
 	cak = os_zalloc(sizeof(*cak));
-	if (!msk || !sid || !ckn || !cak)
+	if (!msk || !ckn || !cak)
 		goto fail;
 
 	msk->len = DEFAULT_KEY_LEN;
@@ -437,8 +410,8 @@
 		goto fail;
 	}
 
-	if (ieee802_1x_auth_get_session_id(hapd, sta, sid, &sid_len))
-	{
+	sid = ieee802_1x_get_session_id(sta->eapol_sm, &sid_len);
+	if (!sid) {
 		wpa_printf(MSG_ERROR,
 			   "IEEE 802.1X: Could not get EAP Session Id");
 		goto fail;
@@ -470,7 +443,6 @@
 
 fail:
 	bin_clear_free(msk, sizeof(*msk));
-	os_free(sid);
 	os_free(ckn);
 	bin_clear_free(cak, sizeof(*cak));