ASR_BASE

Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/Makefile b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/Makefile
new file mode 100644
index 0000000..a1e9b7c
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/Makefile
@@ -0,0 +1,60 @@
+CFLAGS += -DHOSTAPD
+CFLAGS += -DNEED_AP_MLME
+CFLAGS += -DCONFIG_ETH_P_OUI
+CFLAGS += -DCONFIG_HS20
+CFLAGS += -DCONFIG_INTERWORKING
+CFLAGS += -DCONFIG_IEEE80211R
+CFLAGS += -DCONFIG_IEEE80211R_AP
+CFLAGS += -DCONFIG_WPS
+CFLAGS += -DCONFIG_PROXYARP
+CFLAGS += -DCONFIG_IPV6
+CFLAGS += -DCONFIG_AIRTIME_POLICY
+
+LIB_OBJS= \
+	accounting.o \
+	ap_config.o \
+	ap_drv_ops.o \
+	ap_list.o \
+	ap_mlme.o \
+	airtime_policy.o \
+	authsrv.o \
+	beacon.o \
+	bss_load.o \
+	ctrl_iface_ap.o \
+	dfs.o \
+	dhcp_snoop.o \
+	drv_callbacks.o \
+	eap_user_db.o \
+	eth_p_oui.o \
+	gas_serv.o \
+	hostapd.o \
+	hs20.o \
+	hw_features.o \
+	ieee802_11_auth.o \
+	ieee802_11.o \
+	ieee802_11_ht.o \
+	ieee802_11_shared.o \
+	ieee802_11_vht.o \
+	ieee802_1x.o \
+	neighbor_db.o \
+	ndisc_snoop.o \
+	p2p_hostapd.o \
+	pmksa_cache_auth.o \
+	preauth_auth.o \
+	rrm.o \
+	sta_info.o \
+	tkip_countermeasures.o \
+	utils.o \
+	vlan.o \
+	vlan_ifconfig.o \
+	vlan_init.o \
+	wmm.o \
+	wnm_ap.o \
+	wpa_auth.o \
+	wpa_auth_ft.o \
+	wpa_auth_glue.o \
+	wpa_auth_ie.o \
+	wps_hostapd.o \
+	x_snoop.o
+
+include ../lib.rules
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/accounting.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/accounting.c
new file mode 100644
index 0000000..9fc1886
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/accounting.c
@@ -0,0 +1,547 @@
+/*
+ * hostapd / RADIUS Accounting
+ * Copyright (c) 2002-2009, 2012-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
+#include "radius/radius.h"
+#include "radius/radius_client.h"
+#include "hostapd.h"
+#include "ieee802_1x.h"
+#include "ap_config.h"
+#include "sta_info.h"
+#include "ap_drv_ops.h"
+#include "accounting.h"
+
+
+/* Default interval in seconds for polling TX/RX octets from the driver if
+ * STA is not using interim accounting. This detects wrap arounds for
+ * input/output octets and updates Acct-{Input,Output}-Gigawords. */
+#define ACCT_DEFAULT_UPDATE_INTERVAL 300
+
+static void accounting_sta_interim(struct hostapd_data *hapd,
+				   struct sta_info *sta);
+
+
+static struct radius_msg * accounting_msg(struct hostapd_data *hapd,
+					  struct sta_info *sta,
+					  int status_type)
+{
+	struct radius_msg *msg;
+	char buf[128];
+	u8 *val;
+	size_t len;
+	int i;
+	struct wpabuf *b;
+	struct os_time now;
+
+	msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST,
+			     radius_client_get_id(hapd->radius));
+	if (msg == NULL) {
+		wpa_printf(MSG_INFO, "Could not create new RADIUS packet");
+		return NULL;
+	}
+
+	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_STATUS_TYPE,
+				       status_type)) {
+		wpa_printf(MSG_INFO, "Could not add Acct-Status-Type");
+		goto fail;
+	}
+
+	if (sta) {
+		if (!hostapd_config_get_radius_attr(
+			    hapd->conf->radius_acct_req_attr,
+			    RADIUS_ATTR_ACCT_AUTHENTIC) &&
+		    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC,
+					       hapd->conf->ieee802_1x ?
+					       RADIUS_ACCT_AUTHENTIC_RADIUS :
+					       RADIUS_ACCT_AUTHENTIC_LOCAL)) {
+			wpa_printf(MSG_INFO, "Could not add Acct-Authentic");
+			goto fail;
+		}
+
+		/* Use 802.1X identity if available */
+		val = ieee802_1x_get_identity(sta->eapol_sm, &len);
+
+		/* Use RADIUS ACL identity if 802.1X provides no identity */
+		if (!val && sta->identity) {
+			val = (u8 *) sta->identity;
+			len = os_strlen(sta->identity);
+		}
+
+		/* Use STA MAC if neither 802.1X nor RADIUS ACL provided
+		 * identity */
+		if (!val) {
+			os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT,
+				    MAC2STR(sta->addr));
+			val = (u8 *) buf;
+			len = os_strlen(buf);
+		}
+
+		if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, val,
+					 len)) {
+			wpa_printf(MSG_INFO, "Could not add User-Name");
+			goto fail;
+		}
+	}
+
+	if (add_common_radius_attr(hapd, hapd->conf->radius_acct_req_attr, sta,
+				   msg) < 0)
+		goto fail;
+
+	if (sta && add_sqlite_radius_attr(hapd, sta, msg, 1) < 0)
+		goto fail;
+
+	if (sta) {
+		for (i = 0; ; i++) {
+			val = ieee802_1x_get_radius_class(sta->eapol_sm, &len,
+							  i);
+			if (val == NULL)
+				break;
+
+			if (!radius_msg_add_attr(msg, RADIUS_ATTR_CLASS,
+						 val, len)) {
+				wpa_printf(MSG_INFO, "Could not add Class");
+				goto fail;
+			}
+		}
+
+		b = ieee802_1x_get_radius_cui(sta->eapol_sm);
+		if (b &&
+		    !radius_msg_add_attr(msg,
+					 RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+					 wpabuf_head(b), wpabuf_len(b))) {
+			wpa_printf(MSG_ERROR, "Could not add CUI");
+			goto fail;
+		}
+
+		if (!b && sta->radius_cui &&
+		    !radius_msg_add_attr(msg,
+					 RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+					 (u8 *) sta->radius_cui,
+					 os_strlen(sta->radius_cui))) {
+			wpa_printf(MSG_ERROR, "Could not add CUI from ACL");
+			goto fail;
+		}
+
+		if (sta->ipaddr &&
+		    !radius_msg_add_attr_int32(msg,
+					       RADIUS_ATTR_FRAMED_IP_ADDRESS,
+					       be_to_host32(sta->ipaddr))) {
+			wpa_printf(MSG_ERROR,
+				   "Could not add Framed-IP-Address");
+			goto fail;
+		}
+	}
+
+	os_get_time(&now);
+	if (now.sec > 1000000000 &&
+	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
+				       now.sec)) {
+		wpa_printf(MSG_INFO, "Could not add Event-Timestamp");
+		goto fail;
+	}
+
+	/*
+	 * Add Acct-Delay-Time with zero value for the first transmission. This
+	 * will be updated within radius_client.c when retransmitting the frame.
+	 */
+	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_DELAY_TIME, 0)) {
+		wpa_printf(MSG_INFO, "Could not add Acct-Delay-Time");
+		goto fail;
+	}
+
+	return msg;
+
+ fail:
+	radius_msg_free(msg);
+	return NULL;
+}
+
+
+static int accounting_sta_update_stats(struct hostapd_data *hapd,
+				       struct sta_info *sta,
+				       struct hostap_sta_driver_data *data)
+{
+	if (hostapd_drv_read_sta_data(hapd, data, sta->addr))
+		return -1;
+
+	if (!data->bytes_64bit) {
+		/* Extend 32-bit counters from the driver to 64-bit counters */
+		if (sta->last_rx_bytes_lo > data->rx_bytes)
+			sta->last_rx_bytes_hi++;
+		sta->last_rx_bytes_lo = data->rx_bytes;
+
+		if (sta->last_tx_bytes_lo > data->tx_bytes)
+			sta->last_tx_bytes_hi++;
+		sta->last_tx_bytes_lo = data->tx_bytes;
+	}
+
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "updated TX/RX stats: rx_bytes=%llu [%u:%u] tx_bytes=%llu [%u:%u] bytes_64bit=%d",
+		       data->rx_bytes, sta->last_rx_bytes_hi,
+		       sta->last_rx_bytes_lo,
+		       data->tx_bytes, sta->last_tx_bytes_hi,
+		       sta->last_tx_bytes_lo,
+		       data->bytes_64bit);
+
+	return 0;
+}
+
+
+static void accounting_interim_update(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct sta_info *sta = timeout_ctx;
+	int interval;
+
+	if (sta->acct_interim_interval) {
+		accounting_sta_interim(hapd, sta);
+		interval = sta->acct_interim_interval;
+	} else {
+		struct hostap_sta_driver_data data;
+		accounting_sta_update_stats(hapd, sta, &data);
+		interval = ACCT_DEFAULT_UPDATE_INTERVAL;
+	}
+
+	eloop_register_timeout(interval, 0, accounting_interim_update,
+			       hapd, sta);
+}
+
+
+/**
+ * accounting_sta_start - Start STA accounting
+ * @hapd: hostapd BSS data
+ * @sta: The station
+ */
+void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	struct radius_msg *msg;
+	int interval;
+
+	if (sta->acct_session_started)
+		return;
+
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+		       HOSTAPD_LEVEL_INFO,
+		       "starting accounting session %016llX",
+		       (unsigned long long) sta->acct_session_id);
+
+	os_get_reltime(&sta->acct_session_start);
+	sta->last_rx_bytes_hi = 0;
+	sta->last_rx_bytes_lo = 0;
+	sta->last_tx_bytes_hi = 0;
+	sta->last_tx_bytes_lo = 0;
+	hostapd_drv_sta_clear_stats(hapd, sta->addr);
+
+	if (!hapd->conf->radius->acct_server)
+		return;
+
+	if (sta->acct_interim_interval)
+		interval = sta->acct_interim_interval;
+	else
+		interval = ACCT_DEFAULT_UPDATE_INTERVAL;
+	eloop_register_timeout(interval, 0, accounting_interim_update,
+			       hapd, sta);
+
+	msg = accounting_msg(hapd, sta, RADIUS_ACCT_STATUS_TYPE_START);
+	if (msg &&
+	    radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr) < 0)
+		radius_msg_free(msg);
+
+	sta->acct_session_started = 1;
+}
+
+
+static void accounting_sta_report(struct hostapd_data *hapd,
+				  struct sta_info *sta, int stop)
+{
+	struct radius_msg *msg;
+	int cause = sta->acct_terminate_cause;
+	struct hostap_sta_driver_data data;
+	struct os_reltime now_r, diff;
+	u64 bytes;
+
+	if (!hapd->conf->radius->acct_server)
+		return;
+
+	msg = accounting_msg(hapd, sta,
+			     stop ? RADIUS_ACCT_STATUS_TYPE_STOP :
+			     RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE);
+	if (!msg) {
+		wpa_printf(MSG_INFO, "Could not create RADIUS Accounting message");
+		return;
+	}
+
+	os_get_reltime(&now_r);
+	os_reltime_sub(&now_r, &sta->acct_session_start, &diff);
+	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME,
+				       diff.sec)) {
+		wpa_printf(MSG_INFO, "Could not add Acct-Session-Time");
+		goto fail;
+	}
+
+	if (accounting_sta_update_stats(hapd, sta, &data) == 0) {
+		if (!radius_msg_add_attr_int32(msg,
+					       RADIUS_ATTR_ACCT_INPUT_PACKETS,
+					       data.rx_packets)) {
+			wpa_printf(MSG_INFO, "Could not add Acct-Input-Packets");
+			goto fail;
+		}
+		if (!radius_msg_add_attr_int32(msg,
+					       RADIUS_ATTR_ACCT_OUTPUT_PACKETS,
+					       data.tx_packets)) {
+			wpa_printf(MSG_INFO, "Could not add Acct-Output-Packets");
+			goto fail;
+		}
+		if (data.bytes_64bit)
+			bytes = data.rx_bytes;
+		else
+			bytes = ((u64) sta->last_rx_bytes_hi << 32) |
+				sta->last_rx_bytes_lo;
+		if (!radius_msg_add_attr_int32(msg,
+					       RADIUS_ATTR_ACCT_INPUT_OCTETS,
+					       (u32) bytes)) {
+			wpa_printf(MSG_INFO, "Could not add Acct-Input-Octets");
+			goto fail;
+		}
+		if (!radius_msg_add_attr_int32(msg,
+					       RADIUS_ATTR_ACCT_INPUT_GIGAWORDS,
+					       (u32) (bytes >> 32))) {
+			wpa_printf(MSG_INFO, "Could not add Acct-Input-Gigawords");
+			goto fail;
+		}
+		if (data.bytes_64bit)
+			bytes = data.tx_bytes;
+		else
+			bytes = ((u64) sta->last_tx_bytes_hi << 32) |
+				sta->last_tx_bytes_lo;
+		if (!radius_msg_add_attr_int32(msg,
+					       RADIUS_ATTR_ACCT_OUTPUT_OCTETS,
+					       (u32) bytes)) {
+			wpa_printf(MSG_INFO, "Could not add Acct-Output-Octets");
+			goto fail;
+		}
+		if (!radius_msg_add_attr_int32(msg,
+					       RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS,
+					       (u32) (bytes >> 32))) {
+			wpa_printf(MSG_INFO, "Could not add Acct-Output-Gigawords");
+			goto fail;
+		}
+	}
+
+	if (eloop_terminated())
+		cause = RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT;
+
+	if (stop && cause &&
+	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE,
+				       cause)) {
+		wpa_printf(MSG_INFO, "Could not add Acct-Terminate-Cause");
+		goto fail;
+	}
+
+	if (radius_client_send(hapd->radius, msg,
+			       stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM,
+			       sta->addr) < 0)
+		goto fail;
+	return;
+
+ fail:
+	radius_msg_free(msg);
+}
+
+
+/**
+ * accounting_sta_interim - Send a interim STA accounting report
+ * @hapd: hostapd BSS data
+ * @sta: The station
+ */
+static void accounting_sta_interim(struct hostapd_data *hapd,
+				   struct sta_info *sta)
+{
+	if (sta->acct_session_started)
+		accounting_sta_report(hapd, sta, 0);
+}
+
+
+/**
+ * accounting_sta_stop - Stop STA accounting
+ * @hapd: hostapd BSS data
+ * @sta: The station
+ */
+void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	if (sta->acct_session_started) {
+		accounting_sta_report(hapd, sta, 1);
+		eloop_cancel_timeout(accounting_interim_update, hapd, sta);
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+			       HOSTAPD_LEVEL_INFO,
+			       "stopped accounting session %016llX",
+			       (unsigned long long) sta->acct_session_id);
+		sta->acct_session_started = 0;
+	}
+}
+
+
+int accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	return radius_gen_session_id((u8 *) &sta->acct_session_id,
+				     sizeof(sta->acct_session_id));
+}
+
+
+/**
+ * accounting_receive - Process the RADIUS frames from Accounting Server
+ * @msg: RADIUS response message
+ * @req: RADIUS request message
+ * @shared_secret: RADIUS shared secret
+ * @shared_secret_len: Length of shared_secret in octets
+ * @data: Context data (struct hostapd_data *)
+ * Returns: Processing status
+ */
+static RadiusRxResult
+accounting_receive(struct radius_msg *msg, struct radius_msg *req,
+		   const u8 *shared_secret, size_t shared_secret_len,
+		   void *data)
+{
+	if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCOUNTING_RESPONSE) {
+		wpa_printf(MSG_INFO, "Unknown RADIUS message code");
+		return RADIUS_RX_UNKNOWN;
+	}
+
+	if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) {
+		wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have correct Authenticator - dropped");
+		return RADIUS_RX_INVALID_AUTHENTICATOR;
+	}
+
+	return RADIUS_RX_PROCESSED;
+}
+
+
+static void accounting_report_state(struct hostapd_data *hapd, int on)
+{
+	struct radius_msg *msg;
+
+	if (!hapd->conf->radius->acct_server || hapd->radius == NULL)
+		return;
+
+	/* Inform RADIUS server that accounting will start/stop so that the
+	 * server can close old accounting sessions. */
+	msg = accounting_msg(hapd, NULL,
+			     on ? RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON :
+			     RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF);
+	if (!msg)
+		return;
+
+	if (hapd->acct_session_id) {
+		char buf[20];
+
+		os_snprintf(buf, sizeof(buf), "%016llX",
+			    (unsigned long long) hapd->acct_session_id);
+		if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
+					 (u8 *) buf, os_strlen(buf)))
+			wpa_printf(MSG_ERROR, "Could not add Acct-Session-Id");
+	}
+
+	if (radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL) < 0)
+		radius_msg_free(msg);
+}
+
+
+static void accounting_interim_error_cb(const u8 *addr, void *ctx)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+	unsigned int i, wait_time;
+	int res;
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta)
+		return;
+	sta->acct_interim_errors++;
+	if (sta->acct_interim_errors > 10 /* RADIUS_CLIENT_MAX_RETRIES */) {
+		wpa_printf(MSG_DEBUG,
+			   "Interim RADIUS accounting update failed for " MACSTR
+			   " - too many errors, abandon this interim accounting update",
+			   MAC2STR(addr));
+		sta->acct_interim_errors = 0;
+		/* Next update will be tried after normal update interval */
+		return;
+	}
+
+	/*
+	 * Use a shorter update interval as an improved retransmission mechanism
+	 * for failed interim accounting updates. This allows the statistics to
+	 * be updated for each retransmission.
+	 *
+	 * RADIUS client code has already waited RADIUS_CLIENT_FIRST_WAIT.
+	 * Schedule the first retry attempt immediately and every following one
+	 * with exponential backoff.
+	 */
+	if (sta->acct_interim_errors == 1) {
+		wait_time = 0;
+	} else {
+		wait_time = 3; /* RADIUS_CLIENT_FIRST_WAIT */
+		for (i = 1; i < sta->acct_interim_errors; i++)
+			wait_time *= 2;
+	}
+	res = eloop_deplete_timeout(wait_time, 0, accounting_interim_update,
+				    hapd, sta);
+	if (res == 1)
+		wpa_printf(MSG_DEBUG,
+			   "Interim RADIUS accounting update failed for " MACSTR
+			   " (error count: %u) - schedule next update in %u seconds",
+			   MAC2STR(addr), sta->acct_interim_errors, wait_time);
+	else if (res == 0)
+		wpa_printf(MSG_DEBUG,
+			   "Interim RADIUS accounting update failed for " MACSTR
+			   " (error count: %u)", MAC2STR(addr),
+			   sta->acct_interim_errors);
+	else
+		wpa_printf(MSG_DEBUG,
+			   "Interim RADIUS accounting update failed for " MACSTR
+			   " (error count: %u) - no timer found", MAC2STR(addr),
+			   sta->acct_interim_errors);
+}
+
+
+/**
+ * accounting_init: Initialize accounting
+ * @hapd: hostapd BSS data
+ * Returns: 0 on success, -1 on failure
+ */
+int accounting_init(struct hostapd_data *hapd)
+{
+	if (radius_gen_session_id((u8 *) &hapd->acct_session_id,
+				  sizeof(hapd->acct_session_id)) < 0)
+		return -1;
+
+	if (radius_client_register(hapd->radius, RADIUS_ACCT,
+				   accounting_receive, hapd))
+		return -1;
+	radius_client_set_interim_error_cb(hapd->radius,
+					   accounting_interim_error_cb, hapd);
+
+	accounting_report_state(hapd, 1);
+
+	return 0;
+}
+
+
+/**
+ * accounting_deinit: Deinitialize accounting
+ * @hapd: hostapd BSS data
+ */
+void accounting_deinit(struct hostapd_data *hapd)
+{
+	accounting_report_state(hapd, 0);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/accounting.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/accounting.h
new file mode 100644
index 0000000..de5a33f
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/accounting.h
@@ -0,0 +1,45 @@
+/*
+ * hostapd / RADIUS Accounting
+ * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef ACCOUNTING_H
+#define ACCOUNTING_H
+
+#ifdef CONFIG_NO_ACCOUNTING
+static inline int accounting_sta_get_id(struct hostapd_data *hapd,
+					struct sta_info *sta)
+{
+	return 0;
+}
+
+static inline void accounting_sta_start(struct hostapd_data *hapd,
+					struct sta_info *sta)
+{
+}
+
+static inline void accounting_sta_stop(struct hostapd_data *hapd,
+				       struct sta_info *sta)
+{
+}
+
+static inline int accounting_init(struct hostapd_data *hapd)
+{
+	return 0;
+}
+
+static inline void accounting_deinit(struct hostapd_data *hapd)
+{
+}
+#else /* CONFIG_NO_ACCOUNTING */
+int accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta);
+void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta);
+void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta);
+int accounting_init(struct hostapd_data *hapd);
+void accounting_deinit(struct hostapd_data *hapd);
+#endif /* CONFIG_NO_ACCOUNTING */
+
+#endif /* ACCOUNTING_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/acs.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/acs.c
new file mode 100644
index 0000000..da13d48
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/acs.c
@@ -0,0 +1,1385 @@
+/*
+ * ACS - Automatic Channel Selection module
+ * Copyright (c) 2011, Atheros Communications
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <math.h>
+
+#include "utils/common.h"
+#include "utils/list.h"
+#include "common/ieee802_11_defs.h"
+#include "common/hw_features_common.h"
+#include "common/wpa_ctrl.h"
+#include "drivers/driver.h"
+#include "hostapd.h"
+#include "ap_drv_ops.h"
+#include "ap_config.h"
+#include "hw_features.h"
+#include "acs.h"
+
+/*
+ * Automatic Channel Selection
+ * ===========================
+ *
+ * More info at
+ * ------------
+ * http://wireless.kernel.org/en/users/Documentation/acs
+ *
+ * How to use
+ * ----------
+ * - make sure you have CONFIG_ACS=y in hostapd's .config
+ * - use channel=0 or channel=acs to enable ACS
+ *
+ * How does it work
+ * ----------------
+ * 1. passive scans are used to collect survey data
+ *    (it is assumed that scan trigger collection of survey data in driver)
+ * 2. interference factor is calculated for each channel
+ * 3. ideal channel is picked depending on channel width by using adjacent
+ *    channel interference factors
+ *
+ * Known limitations
+ * -----------------
+ * - Current implementation depends heavily on the amount of time willing to
+ *   spend gathering survey data during hostapd startup. Short traffic bursts
+ *   may be missed and a suboptimal channel may be picked.
+ * - Ideal channel may end up overlapping a channel with 40 MHz intolerant BSS
+ *
+ * Todo / Ideas
+ * ------------
+ * - implement other interference computation methods
+ *   - BSS/RSSI based
+ *   - spectral scan based
+ *   (should be possibly to hook this up with current ACS scans)
+ * - add wpa_supplicant support (for P2P)
+ * - collect a histogram of interference over time allowing more educated
+ *   guess about an ideal channel (perhaps CSA could be used to migrate AP to a
+ *   new "better" channel while running)
+ * - include neighboring BSS scan to avoid conflicts with 40 MHz intolerant BSSs
+ *   when choosing the ideal channel
+ *
+ * Survey interference factor implementation details
+ * -------------------------------------------------
+ * Generic interference_factor in struct hostapd_channel_data is used.
+ *
+ * The survey interference factor is defined as the ratio of the
+ * observed busy time over the time we spent on the channel,
+ * this value is then amplified by the observed noise floor on
+ * the channel in comparison to the lowest noise floor observed
+ * on the entire band.
+ *
+ * This corresponds to:
+ * ---
+ * (busy time - tx time) / (active time - tx time) * 2^(chan_nf + band_min_nf)
+ * ---
+ *
+ * The coefficient of 2 reflects the way power in "far-field"
+ * radiation decreases as the square of distance from the antenna [1].
+ * What this does is it decreases the observed busy time ratio if the
+ * noise observed was low but increases it if the noise was high,
+ * proportionally to the way "far field" radiation changes over
+ * distance.
+ *
+ * If channel busy time is not available the fallback is to use channel RX time.
+ *
+ * Since noise floor is in dBm it is necessary to convert it into Watts so that
+ * combined channel interference (e.g., HT40, which uses two channels) can be
+ * calculated easily.
+ * ---
+ * (busy time - tx time) / (active time - tx time) *
+ *    2^(10^(chan_nf/10) + 10^(band_min_nf/10))
+ * ---
+ *
+ * However to account for cases where busy/rx time is 0 (channel load is then
+ * 0%) channel noise floor signal power is combined into the equation so a
+ * channel with lower noise floor is preferred. The equation becomes:
+ * ---
+ * 10^(chan_nf/5) + (busy time - tx time) / (active time - tx time) *
+ *    2^(10^(chan_nf/10) + 10^(band_min_nf/10))
+ * ---
+ *
+ * All this "interference factor" is purely subjective and only time
+ * will tell how usable this is. By using the minimum noise floor we
+ * remove any possible issues due to card calibration. The computation
+ * of the interference factor then is dependent on what the card itself
+ * picks up as the minimum noise, not an actual real possible card
+ * noise value.
+ *
+ * Total interference computation details
+ * --------------------------------------
+ * The above channel interference factor is calculated with no respect to
+ * target operational bandwidth.
+ *
+ * To find an ideal channel the above data is combined by taking into account
+ * the target operational bandwidth and selected band. E.g., on 2.4 GHz channels
+ * overlap with 20 MHz bandwidth, but there is no overlap for 20 MHz bandwidth
+ * on 5 GHz.
+ *
+ * Each valid and possible channel spec (i.e., channel + width) is taken and its
+ * interference factor is computed by summing up interferences of each channel
+ * it overlaps. The one with least total interference is picked up.
+ *
+ * Note: This implies base channel interference factor must be non-negative
+ * allowing easy summing up.
+ *
+ * Example ACS analysis printout
+ * -----------------------------
+ *
+ * ACS: Trying survey-based ACS
+ * ACS: Survey analysis for channel 1 (2412 MHz)
+ * ACS:  1: min_nf=-113 interference_factor=0.0802469 nf=-113 time=162 busy=0 rx=13
+ * ACS:  2: min_nf=-113 interference_factor=0.0745342 nf=-113 time=161 busy=0 rx=12
+ * ACS:  3: min_nf=-113 interference_factor=0.0679012 nf=-113 time=162 busy=0 rx=11
+ * ACS:  4: min_nf=-113 interference_factor=0.0310559 nf=-113 time=161 busy=0 rx=5
+ * ACS:  5: min_nf=-113 interference_factor=0.0248447 nf=-113 time=161 busy=0 rx=4
+ * ACS:  * interference factor average: 0.0557166
+ * ACS: Survey analysis for channel 2 (2417 MHz)
+ * ACS:  1: min_nf=-113 interference_factor=0.0185185 nf=-113 time=162 busy=0 rx=3
+ * ACS:  2: min_nf=-113 interference_factor=0.0246914 nf=-113 time=162 busy=0 rx=4
+ * ACS:  3: min_nf=-113 interference_factor=0.037037 nf=-113 time=162 busy=0 rx=6
+ * ACS:  4: min_nf=-113 interference_factor=0.149068 nf=-113 time=161 busy=0 rx=24
+ * ACS:  5: min_nf=-113 interference_factor=0.0248447 nf=-113 time=161 busy=0 rx=4
+ * ACS:  * interference factor average: 0.050832
+ * ACS: Survey analysis for channel 3 (2422 MHz)
+ * ACS:  1: min_nf=-113 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0
+ * ACS:  2: min_nf=-113 interference_factor=0.0185185 nf=-113 time=162 busy=0 rx=3
+ * ACS:  3: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3
+ * ACS:  4: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3
+ * ACS:  5: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3
+ * ACS:  * interference factor average: 0.0148838
+ * ACS: Survey analysis for channel 4 (2427 MHz)
+ * ACS:  1: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
+ * ACS:  2: min_nf=-114 interference_factor=0.0555556 nf=-114 time=162 busy=0 rx=9
+ * ACS:  3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0
+ * ACS:  4: min_nf=-114 interference_factor=0.0186335 nf=-114 time=161 busy=0 rx=3
+ * ACS:  5: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
+ * ACS:  * interference factor average: 0.0160801
+ * ACS: Survey analysis for channel 5 (2432 MHz)
+ * ACS:  1: min_nf=-114 interference_factor=0.409938 nf=-113 time=161 busy=0 rx=66
+ * ACS:  2: min_nf=-114 interference_factor=0.0432099 nf=-113 time=162 busy=0 rx=7
+ * ACS:  3: min_nf=-114 interference_factor=0.0124224 nf=-113 time=161 busy=0 rx=2
+ * ACS:  4: min_nf=-114 interference_factor=0.677019 nf=-113 time=161 busy=0 rx=109
+ * ACS:  5: min_nf=-114 interference_factor=0.0186335 nf=-114 time=161 busy=0 rx=3
+ * ACS:  * interference factor average: 0.232244
+ * ACS: Survey analysis for channel 6 (2437 MHz)
+ * ACS:  1: min_nf=-113 interference_factor=0.552795 nf=-113 time=161 busy=0 rx=89
+ * ACS:  2: min_nf=-113 interference_factor=0.0807453 nf=-112 time=161 busy=0 rx=13
+ * ACS:  3: min_nf=-113 interference_factor=0.0310559 nf=-113 time=161 busy=0 rx=5
+ * ACS:  4: min_nf=-113 interference_factor=0.434783 nf=-112 time=161 busy=0 rx=70
+ * ACS:  5: min_nf=-113 interference_factor=0.0621118 nf=-113 time=161 busy=0 rx=10
+ * ACS:  * interference factor average: 0.232298
+ * ACS: Survey analysis for channel 7 (2442 MHz)
+ * ACS:  1: min_nf=-113 interference_factor=0.440994 nf=-112 time=161 busy=0 rx=71
+ * ACS:  2: min_nf=-113 interference_factor=0.385093 nf=-113 time=161 busy=0 rx=62
+ * ACS:  3: min_nf=-113 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6
+ * ACS:  4: min_nf=-113 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6
+ * ACS:  5: min_nf=-113 interference_factor=0.0745342 nf=-113 time=161 busy=0 rx=12
+ * ACS:  * interference factor average: 0.195031
+ * ACS: Survey analysis for channel 8 (2447 MHz)
+ * ACS:  1: min_nf=-114 interference_factor=0.0496894 nf=-112 time=161 busy=0 rx=8
+ * ACS:  2: min_nf=-114 interference_factor=0.0496894 nf=-114 time=161 busy=0 rx=8
+ * ACS:  3: min_nf=-114 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6
+ * ACS:  4: min_nf=-114 interference_factor=0.12963 nf=-113 time=162 busy=0 rx=21
+ * ACS:  5: min_nf=-114 interference_factor=0.166667 nf=-114 time=162 busy=0 rx=27
+ * ACS:  * interference factor average: 0.0865885
+ * ACS: Survey analysis for channel 9 (2452 MHz)
+ * ACS:  1: min_nf=-114 interference_factor=0.0124224 nf=-114 time=161 busy=0 rx=2
+ * ACS:  2: min_nf=-114 interference_factor=0.0310559 nf=-114 time=161 busy=0 rx=5
+ * ACS:  3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0
+ * ACS:  4: min_nf=-114 interference_factor=0.00617284 nf=-114 time=162 busy=0 rx=1
+ * ACS:  5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
+ * ACS:  * interference factor average: 0.00993022
+ * ACS: Survey analysis for channel 10 (2457 MHz)
+ * ACS:  1: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
+ * ACS:  2: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
+ * ACS:  3: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
+ * ACS:  4: min_nf=-114 interference_factor=0.0493827 nf=-114 time=162 busy=0 rx=8
+ * ACS:  5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
+ * ACS:  * interference factor average: 0.0136033
+ * ACS: Survey analysis for channel 11 (2462 MHz)
+ * ACS:  1: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0
+ * ACS:  2: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=161 busy=0 rx=0
+ * ACS:  3: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=161 busy=0 rx=0
+ * ACS:  4: min_nf=-114 interference_factor=0.0432099 nf=-114 time=162 busy=0 rx=7
+ * ACS:  5: min_nf=-114 interference_factor=0.0925926 nf=-114 time=162 busy=0 rx=15
+ * ACS:  * interference factor average: 0.0271605
+ * ACS: Survey analysis for channel 12 (2467 MHz)
+ * ACS:  1: min_nf=-114 interference_factor=0.0621118 nf=-113 time=161 busy=0 rx=10
+ * ACS:  2: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
+ * ACS:  3: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0
+ * ACS:  4: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0
+ * ACS:  5: min_nf=-114 interference_factor=0.00617284 nf=-113 time=162 busy=0 rx=1
+ * ACS:  * interference factor average: 0.0148992
+ * ACS: Survey analysis for channel 13 (2472 MHz)
+ * ACS:  1: min_nf=-114 interference_factor=0.0745342 nf=-114 time=161 busy=0 rx=12
+ * ACS:  2: min_nf=-114 interference_factor=0.0555556 nf=-114 time=162 busy=0 rx=9
+ * ACS:  3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
+ * ACS:  4: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
+ * ACS:  5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
+ * ACS:  * interference factor average: 0.0260179
+ * ACS: Survey analysis for selected bandwidth 20MHz
+ * ACS:  * channel 1: total interference = 0.121432
+ * ACS:  * channel 2: total interference = 0.137512
+ * ACS:  * channel 3: total interference = 0.369757
+ * ACS:  * channel 4: total interference = 0.546338
+ * ACS:  * channel 5: total interference = 0.690538
+ * ACS:  * channel 6: total interference = 0.762242
+ * ACS:  * channel 7: total interference = 0.756092
+ * ACS:  * channel 8: total interference = 0.537451
+ * ACS:  * channel 9: total interference = 0.332313
+ * ACS:  * channel 10: total interference = 0.152182
+ * ACS:  * channel 11: total interference = 0.0916111
+ * ACS:  * channel 12: total interference = 0.0816809
+ * ACS:  * channel 13: total interference = 0.0680776
+ * ACS: Ideal channel is 13 (2472 MHz) with total interference factor of 0.0680776
+ *
+ * [1] http://en.wikipedia.org/wiki/Near_and_far_field
+ */
+
+enum bw_type {
+	ACS_BW40,
+	ACS_BW80,
+	ACS_BW160,
+};
+
+struct bw_item {
+	int first;
+	int last;
+	int center_chan;
+};
+
+static const struct bw_item bw_40[] = {
+	{ 5180, 5200, 38 }, { 5220, 5240, 46 }, { 5260, 5280, 54 },
+	{ 5300, 5320, 62 }, { 5500, 5520, 102 }, { 5540, 5560, 110 },
+	{ 5580, 5600, 110 }, { 5620, 5640, 126}, { 5660, 5680, 134 },
+	{ 5700, 5720, 142 }, { 5745, 5765, 151 }, { 5785, 5805, 159 },
+	{ 5825, 5845, 167 }, { 5865, 5885, 175 },
+	{ 5955, 5975, 3 }, { 5995, 6015, 11 }, { 6035, 6055, 19 },
+	{ 6075, 6095, 27 }, { 6115, 6135, 35 }, { 6155, 6175, 43 },
+	{ 6195, 6215, 51 }, { 6235, 6255, 59 }, { 6275, 6295, 67 },
+	{ 6315, 6335, 75 }, { 6355, 6375, 83 }, { 6395, 6415, 91 },
+	{ 6435, 6455, 99 }, { 6475, 6495, 107 }, { 6515, 6535, 115 },
+	{ 6555, 6575, 123 }, { 6595, 6615, 131 }, { 6635, 6655, 139 },
+	{ 6675, 6695, 147 }, { 6715, 6735, 155 }, { 6755, 6775, 163 },
+	{ 6795, 6815, 171 }, { 6835, 6855, 179 }, { 6875, 6895, 187 },
+	{ 6915, 6935, 195 }, { 6955, 6975, 203 }, { 6995, 7015, 211 },
+	{ 7035, 7055, 219 }, { 7075, 7095, 227}, { -1, -1, -1 }
+};
+static const struct bw_item bw_80[] = {
+	{ 5180, 5240, 42 }, { 5260, 5320, 58 }, { 5500, 5560, 106 },
+	{ 5580, 5640, 122 }, { 5660, 5720, 138 }, { 5745, 5805, 155 },
+	{ 5825, 5885, 171},
+	{ 5955, 6015, 7 }, { 6035, 6095, 23 }, { 6115, 6175, 39 },
+	{ 6195, 6255, 55 }, { 6275, 6335, 71 }, { 6355, 6415, 87 },
+	{ 6435, 6495, 103 }, { 6515, 6575, 119 }, { 6595, 6655, 135 },
+	{ 6675, 6735, 151 }, { 6755, 6815, 167 }, { 6835, 6895, 183 },
+	{ 6915, 6975, 199 }, { 6995, 7055, 215 }, { -1, -1, -1 }
+};
+static const struct bw_item bw_160[] = {
+	{ 5180, 5320, 50 }, { 5500, 5640, 114 }, { 5745, 5885, 163 },
+	{ 5955, 6095, 15 }, { 6115, 6255, 47 }, { 6275, 6415, 79 },
+	{ 6435, 6575, 111 }, { 6595, 6735, 143 },
+	{ 6755, 6895, 175 }, { 6915, 7055, 207 }, { -1, -1, -1 }
+};
+static const struct bw_item *bw_desc[] = {
+	[ACS_BW40] = bw_40,
+	[ACS_BW80] = bw_80,
+	[ACS_BW160] = bw_160,
+};
+
+
+static int acs_request_scan(struct hostapd_iface *iface);
+static int acs_survey_is_sufficient(struct freq_survey *survey);
+
+
+static void acs_clean_chan_surveys(struct hostapd_channel_data *chan)
+{
+	struct freq_survey *survey, *tmp;
+
+	if (dl_list_empty(&chan->survey_list))
+		return;
+
+	dl_list_for_each_safe(survey, tmp, &chan->survey_list,
+			      struct freq_survey, list) {
+		dl_list_del(&survey->list);
+		os_free(survey);
+	}
+}
+
+
+static void acs_cleanup_mode(struct hostapd_hw_modes *mode)
+{
+	int i;
+	struct hostapd_channel_data *chan;
+
+	for (i = 0; i < mode->num_channels; i++) {
+		chan = &mode->channels[i];
+
+		if (chan->flag & HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED)
+			acs_clean_chan_surveys(chan);
+
+		dl_list_init(&chan->survey_list);
+		chan->flag |= HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED;
+		chan->min_nf = 0;
+		chan->punct_bitmap = 0;
+	}
+}
+
+
+void acs_cleanup(struct hostapd_iface *iface)
+{
+	int i;
+
+	for (i = 0; i < iface->num_hw_features; i++)
+		acs_cleanup_mode(&iface->hw_features[i]);
+
+	iface->chans_surveyed = 0;
+	iface->acs_num_completed_scans = 0;
+}
+
+
+static void acs_fail(struct hostapd_iface *iface)
+{
+	wpa_printf(MSG_ERROR, "ACS: Failed to start");
+	acs_cleanup(iface);
+	hostapd_disable_iface(iface);
+}
+
+
+static long double
+acs_survey_interference_factor(struct freq_survey *survey, s8 min_nf)
+{
+	long double factor, busy, total;
+
+	if (survey->filled & SURVEY_HAS_CHAN_TIME_BUSY)
+		busy = survey->channel_time_busy;
+	else if (survey->filled & SURVEY_HAS_CHAN_TIME_RX)
+		busy = survey->channel_time_rx;
+	else {
+		wpa_printf(MSG_ERROR, "ACS: Survey data missing");
+		return 0;
+	}
+
+	total = survey->channel_time;
+
+	if (survey->filled & SURVEY_HAS_CHAN_TIME_TX) {
+		busy -= survey->channel_time_tx;
+		total -= survey->channel_time_tx;
+	}
+
+	/* TODO: figure out the best multiplier for noise floor base */
+	factor = pow(10, survey->nf / 5.0L) +
+		(total ? (busy / total) : 0) *
+		pow(2, pow(10, (long double) survey->nf / 10.0L) -
+		    pow(10, (long double) min_nf / 10.0L));
+
+	return factor;
+}
+
+
+static void
+acs_survey_chan_interference_factor(struct hostapd_iface *iface,
+				    struct hostapd_channel_data *chan)
+{
+	struct freq_survey *survey;
+	unsigned int i = 0;
+	long double int_factor = 0;
+	unsigned count = 0;
+
+	if (dl_list_empty(&chan->survey_list) ||
+	    (chan->flag & HOSTAPD_CHAN_DISABLED))
+		return;
+
+	chan->interference_factor = 0;
+
+	dl_list_for_each(survey, &chan->survey_list, struct freq_survey, list)
+	{
+		i++;
+
+		if (!acs_survey_is_sufficient(survey)) {
+			wpa_printf(MSG_DEBUG, "ACS: %d: insufficient data", i);
+			continue;
+		}
+
+		count++;
+		int_factor = acs_survey_interference_factor(survey,
+							    iface->lowest_nf);
+		chan->interference_factor += int_factor;
+		wpa_printf(MSG_DEBUG, "ACS: %d: min_nf=%d interference_factor=%Lg nf=%d time=%lu busy=%lu rx=%lu",
+			   i, chan->min_nf, int_factor,
+			   survey->nf, (unsigned long) survey->channel_time,
+			   (unsigned long) survey->channel_time_busy,
+			   (unsigned long) survey->channel_time_rx);
+	}
+
+	if (count)
+		chan->interference_factor /= count;
+}
+
+
+static bool acs_usable_bw_chan(const struct hostapd_channel_data *chan,
+			       enum bw_type bw)
+{
+	unsigned int i = 0;
+
+	while (bw_desc[bw][i].first != -1) {
+		if (chan->freq == bw_desc[bw][i].first)
+			return true;
+		i++;
+	}
+
+	return false;
+}
+
+
+static int acs_get_bw_center_chan(int freq, enum bw_type bw)
+{
+	unsigned int i = 0;
+
+	while (bw_desc[bw][i].first != -1) {
+		if (freq >= bw_desc[bw][i].first &&
+		    freq <= bw_desc[bw][i].last)
+			return bw_desc[bw][i].center_chan;
+		i++;
+	}
+
+	return 0;
+}
+
+
+static int acs_survey_is_sufficient(struct freq_survey *survey)
+{
+	if (!(survey->filled & SURVEY_HAS_NF)) {
+		survey->nf = -95;
+		wpa_printf(MSG_INFO,
+			   "ACS: Survey for freq %d is missing noise floor",
+			   survey->freq);
+	}
+
+	if (!(survey->filled & SURVEY_HAS_CHAN_TIME)) {
+		survey->channel_time = 0;
+		wpa_printf(MSG_INFO,
+			   "ACS: Survey for freq %d is missing channel time",
+			   survey->freq);
+	}
+
+	if (!(survey->filled & SURVEY_HAS_CHAN_TIME_BUSY) &&
+	    !(survey->filled & SURVEY_HAS_CHAN_TIME_RX)) {
+		wpa_printf(MSG_INFO,
+			   "ACS: Survey for freq %d is missing RX and busy time (at least one is required)",
+			   survey->freq);
+	}
+
+	return 1;
+}
+
+
+static int acs_survey_list_is_sufficient(struct hostapd_channel_data *chan)
+{
+	struct freq_survey *survey;
+	int ret = -1;
+
+	dl_list_for_each(survey, &chan->survey_list, struct freq_survey, list)
+	{
+		if (acs_survey_is_sufficient(survey)) {
+			ret = 1;
+			break;
+		}
+		ret = 0;
+	}
+
+	if (ret == -1)
+		ret = 1; /* no survey list entries */
+
+	if (!ret) {
+		wpa_printf(MSG_INFO,
+			   "ACS: Channel %d has insufficient survey data",
+			   chan->chan);
+	}
+
+	return ret;
+}
+
+
+static int acs_surveys_are_sufficient_mode(struct hostapd_hw_modes *mode)
+{
+	int i;
+	struct hostapd_channel_data *chan;
+
+	for (i = 0; i < mode->num_channels; i++) {
+		chan = &mode->channels[i];
+		if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
+		    acs_survey_list_is_sufficient(chan))
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static int acs_surveys_are_sufficient(struct hostapd_iface *iface)
+{
+	int i;
+	struct hostapd_hw_modes *mode;
+
+	for (i = 0; i < iface->num_hw_features; i++) {
+		mode = &iface->hw_features[i];
+		if (!hostapd_hw_skip_mode(iface, mode) &&
+		    acs_surveys_are_sufficient_mode(mode))
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static int acs_usable_chan(struct hostapd_channel_data *chan)
+{
+	return !dl_list_empty(&chan->survey_list) &&
+		!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
+		acs_survey_list_is_sufficient(chan);
+}
+
+
+static int is_in_chanlist(struct hostapd_iface *iface,
+			  struct hostapd_channel_data *chan)
+{
+	if (!iface->conf->acs_ch_list.num)
+		return 1;
+
+	return freq_range_list_includes(&iface->conf->acs_ch_list, chan->chan);
+}
+
+
+static int is_in_freqlist(struct hostapd_iface *iface,
+			  struct hostapd_channel_data *chan)
+{
+	if (!iface->conf->acs_freq_list.num)
+		return 1;
+
+	return freq_range_list_includes(&iface->conf->acs_freq_list,
+					chan->freq);
+}
+
+
+static void acs_survey_mode_interference_factor(
+	struct hostapd_iface *iface, struct hostapd_hw_modes *mode)
+{
+	int i;
+	struct hostapd_channel_data *chan;
+
+	for (i = 0; i < mode->num_channels; i++) {
+		chan = &mode->channels[i];
+
+		if (!acs_usable_chan(chan))
+			continue;
+
+		if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
+		    iface->conf->acs_exclude_dfs)
+			continue;
+
+		if (!is_in_chanlist(iface, chan))
+			continue;
+
+		if (!is_in_freqlist(iface, chan))
+			continue;
+
+		if (chan->max_tx_power < iface->conf->min_tx_power)
+			continue;
+
+		if ((chan->flag & HOSTAPD_CHAN_INDOOR_ONLY) &&
+		    iface->conf->country[2] == 0x4f)
+			continue;
+
+		wpa_printf(MSG_DEBUG, "ACS: Survey analysis for channel %d (%d MHz)",
+			   chan->chan, chan->freq);
+
+		acs_survey_chan_interference_factor(iface, chan);
+
+		wpa_printf(MSG_DEBUG, "ACS:  * interference factor average: %Lg",
+			   chan->interference_factor);
+	}
+}
+
+
+static void acs_survey_all_chans_interference_factor(
+	struct hostapd_iface *iface)
+{
+	int i;
+	struct hostapd_hw_modes *mode;
+
+	for (i = 0; i < iface->num_hw_features; i++) {
+		mode = &iface->hw_features[i];
+		if (!hostapd_hw_skip_mode(iface, mode))
+			acs_survey_mode_interference_factor(iface, mode);
+	}
+}
+
+
+static struct hostapd_channel_data *
+acs_find_chan_mode(struct hostapd_hw_modes *mode, int freq)
+{
+	struct hostapd_channel_data *chan;
+	int i;
+
+	for (i = 0; i < mode->num_channels; i++) {
+		chan = &mode->channels[i];
+
+		if (chan->flag & HOSTAPD_CHAN_DISABLED)
+			continue;
+
+		if (chan->freq == freq)
+			return chan;
+	}
+
+	return NULL;
+}
+
+
+static enum hostapd_hw_mode
+acs_find_mode(struct hostapd_iface *iface, int freq)
+{
+	int i;
+	struct hostapd_hw_modes *mode;
+	struct hostapd_channel_data *chan;
+
+	for (i = 0; i < iface->num_hw_features; i++) {
+		mode = &iface->hw_features[i];
+		if (!hostapd_hw_skip_mode(iface, mode)) {
+			chan = acs_find_chan_mode(mode, freq);
+			if (chan)
+				return mode->mode;
+		}
+	}
+
+	return HOSTAPD_MODE_IEEE80211ANY;
+}
+
+
+static struct hostapd_channel_data *
+acs_find_chan(struct hostapd_iface *iface, int freq)
+{
+	int i;
+	struct hostapd_hw_modes *mode;
+	struct hostapd_channel_data *chan;
+
+	for (i = 0; i < iface->num_hw_features; i++) {
+		mode = &iface->hw_features[i];
+		if (!hostapd_hw_skip_mode(iface, mode)) {
+			chan = acs_find_chan_mode(mode, freq);
+			if (chan)
+				return chan;
+		}
+	}
+
+	return NULL;
+}
+
+
+static int is_24ghz_mode(enum hostapd_hw_mode mode)
+{
+	return mode == HOSTAPD_MODE_IEEE80211B ||
+		mode == HOSTAPD_MODE_IEEE80211G;
+}
+
+
+static int is_common_24ghz_chan(int chan)
+{
+	return chan == 1 || chan == 6 || chan == 11;
+}
+
+
+#ifndef ACS_ADJ_WEIGHT
+#define ACS_ADJ_WEIGHT 0.85
+#endif /* ACS_ADJ_WEIGHT */
+
+#ifndef ACS_NEXT_ADJ_WEIGHT
+#define ACS_NEXT_ADJ_WEIGHT 0.55
+#endif /* ACS_NEXT_ADJ_WEIGHT */
+
+#ifndef ACS_24GHZ_PREFER_1_6_11
+/*
+ * Select commonly used channels 1, 6, 11 by default even if a neighboring
+ * channel has a smaller interference factor as long as it is not better by more
+ * than this multiplier.
+ */
+#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,
+			 int n_chans, u32 bw,
+			 struct hostapd_channel_data **rand_chan,
+			 struct hostapd_channel_data **ideal_chan,
+			 long double *ideal_factor)
+{
+	struct hostapd_channel_data *chan, *adj_chan = NULL, *best;
+	long double factor;
+	int i, j;
+	unsigned int k;
+
+	for (i = 0; i < mode->num_channels; i++) {
+		double total_weight;
+		struct acs_bias *bias, tmp_bias;
+		bool update_best = true;
+
+		best = chan = &mode->channels[i];
+
+		/* 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.
+		 *
+		 * 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;
+
+		if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
+		    iface->conf->acs_exclude_dfs)
+			continue;
+
+		if (!is_in_chanlist(iface, chan))
+			continue;
+
+		if (!is_in_freqlist(iface, chan))
+			continue;
+
+		if (chan->max_tx_power < iface->conf->min_tx_power)
+			continue;
+
+		if ((chan->flag & HOSTAPD_CHAN_INDOOR_ONLY) &&
+		    iface->conf->country[2] == 0x4f)
+			continue;
+
+		if (!chan_bw_allowed(chan, bw, 1, 1)) {
+			wpa_printf(MSG_DEBUG,
+				   "ACS: Channel %d: BW %u is not supported",
+				   chan->chan, bw);
+			continue;
+		}
+
+		/* HT40 on 5 GHz has a limited set of primary channels as per
+		 * 11n Annex J */
+		if (mode->mode == HOSTAPD_MODE_IEEE80211A &&
+		    ((iface->conf->ieee80211n &&
+		      iface->conf->secondary_channel) ||
+		     is_6ghz_freq(chan->freq)) &&
+		    !acs_usable_bw_chan(chan, ACS_BW40)) {
+			wpa_printf(MSG_DEBUG,
+				   "ACS: Channel %d: not allowed as primary channel for 40 MHz bandwidth",
+				   chan->chan);
+			continue;
+		}
+
+		if (mode->mode == HOSTAPD_MODE_IEEE80211A &&
+		    (iface->conf->ieee80211ac || iface->conf->ieee80211ax)) {
+			if (hostapd_get_oper_chwidth(iface->conf) ==
+			    CONF_OPER_CHWIDTH_80MHZ &&
+			    !acs_usable_bw_chan(chan, ACS_BW80)) {
+				wpa_printf(MSG_DEBUG,
+					   "ACS: Channel %d: not allowed as primary channel for 80 MHz bandwidth",
+					   chan->chan);
+				continue;
+			}
+
+			if (hostapd_get_oper_chwidth(iface->conf) ==
+			    CONF_OPER_CHWIDTH_160MHZ &&
+			    !acs_usable_bw_chan(chan, ACS_BW160)) {
+				wpa_printf(MSG_DEBUG,
+					   "ACS: Channel %d: not allowed as primary channel for 160 MHz bandwidth",
+					   chan->chan);
+				continue;
+			}
+		}
+
+		factor = 0;
+		if (acs_usable_chan(chan))
+			factor = chan->interference_factor;
+		total_weight = 1;
+
+		for (j = 1; j < n_chans; j++) {
+			adj_chan = acs_find_chan(iface, chan->freq + (j * 20));
+			if (!adj_chan)
+				break;
+
+			if (!chan_bw_allowed(adj_chan, bw, 1, 0)) {
+				wpa_printf(MSG_DEBUG,
+					   "ACS: PRI Channel %d: secondary channel %d BW %u is not supported",
+					   chan->chan, adj_chan->chan, bw);
+				break;
+			}
+
+			if (acs_usable_chan(adj_chan)) {
+				factor += adj_chan->interference_factor;
+				total_weight += 1;
+			} else {
+				update_best = false;
+			}
+
+			/* find the best channel in this segment */
+			if (update_best &&
+			    adj_chan->interference_factor <
+			    best->interference_factor)
+				best = adj_chan;
+		}
+
+		if (j != n_chans) {
+			wpa_printf(MSG_DEBUG, "ACS: Channel %d: not enough bandwidth",
+				   chan->chan);
+			continue;
+		}
+
+		/* If the AP is in the 5 GHz or 6 GHz band, lets prefer a less
+		 * crowded primary channel if one was found in the segment */
+		if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
+		    chan != best) {
+			wpa_printf(MSG_DEBUG,
+				   "ACS: promoting channel %d over %d (less interference %Lg/%Lg)",
+				   best->chan, chan->chan,
+				   chan->interference_factor,
+				   best->interference_factor);
+			chan = best;
+		}
+
+		/* 2.4 GHz has overlapping 20 MHz channels. Include adjacent
+		 * channel interference factor. */
+		if (is_24ghz_mode(mode->mode)) {
+			for (j = 0; j < n_chans; j++) {
+				adj_chan = acs_find_chan(iface, chan->freq +
+							 (j * 20) - 5);
+				if (adj_chan && acs_usable_chan(adj_chan)) {
+					factor += ACS_ADJ_WEIGHT *
+						adj_chan->interference_factor;
+					total_weight += ACS_ADJ_WEIGHT;
+				}
+
+				adj_chan = acs_find_chan(iface, chan->freq +
+							 (j * 20) - 10);
+				if (adj_chan && acs_usable_chan(adj_chan)) {
+					factor += ACS_NEXT_ADJ_WEIGHT *
+						adj_chan->interference_factor;
+					total_weight += ACS_NEXT_ADJ_WEIGHT;
+				}
+
+				adj_chan = acs_find_chan(iface, chan->freq +
+							 (j * 20) + 5);
+				if (adj_chan && acs_usable_chan(adj_chan)) {
+					factor += ACS_ADJ_WEIGHT *
+						adj_chan->interference_factor;
+					total_weight += ACS_ADJ_WEIGHT;
+				}
+
+				adj_chan = acs_find_chan(iface, chan->freq +
+							 (j * 20) + 10);
+				if (adj_chan && acs_usable_chan(adj_chan)) {
+					factor += ACS_NEXT_ADJ_WEIGHT *
+						adj_chan->interference_factor;
+					total_weight += ACS_NEXT_ADJ_WEIGHT;
+				}
+			}
+		}
+
+		factor /= total_weight;
+
+		bias = NULL;
+		if (iface->conf->acs_chan_bias) {
+			for (k = 0; k < iface->conf->num_acs_chan_bias; k++) {
+				bias = &iface->conf->acs_chan_bias[k];
+				if (bias->channel == chan->chan)
+					break;
+				bias = NULL;
+			}
+		} else if (is_24ghz_mode(mode->mode) &&
+			   is_common_24ghz_chan(chan->chan)) {
+			tmp_bias.channel = chan->chan;
+			tmp_bias.bias = ACS_24GHZ_PREFER_1_6_11;
+			bias = &tmp_bias;
+		}
+
+		if (bias) {
+			factor *= bias->bias;
+			wpa_printf(MSG_DEBUG,
+				   "ACS:  * channel %d: total interference = %Lg (%f bias)",
+				   chan->chan, factor, bias->bias);
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "ACS:  * channel %d: total interference = %Lg",
+				   chan->chan, factor);
+		}
+
+		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 */
+		if (!(*rand_chan))
+			*rand_chan = chan;
+	}
+}
+
+
+/*
+ * At this point it's assumed chan->interference_factor has been computed.
+ * This function should be reusable regardless of interference computation
+ * option (survey, BSS, spectral, ...). chan->interference factor must be
+ * summable (i.e., must be always greater than zero).
+ */
+static struct hostapd_channel_data *
+acs_find_ideal_chan(struct hostapd_iface *iface)
+{
+	struct hostapd_channel_data *ideal_chan = NULL,
+		*rand_chan = NULL;
+	long double ideal_factor = 0;
+	int i;
+	int n_chans = 1;
+	u32 bw;
+	struct hostapd_hw_modes *mode;
+
+	if (is_6ghz_op_class(iface->conf->op_class)) {
+		bw = op_class_to_bandwidth(iface->conf->op_class);
+		n_chans = bw / 20;
+		goto bw_selected;
+	}
+
+	/* TODO: HT40- support */
+
+	if (iface->conf->ieee80211n &&
+	    iface->conf->secondary_channel == -1) {
+		wpa_printf(MSG_ERROR, "ACS: HT40- is not supported yet. Please try HT40+");
+		return NULL;
+	}
+
+	if (iface->conf->ieee80211n &&
+	    iface->conf->secondary_channel)
+		n_chans = 2;
+
+	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
+		switch (hostapd_get_oper_chwidth(iface->conf)) {
+		case CONF_OPER_CHWIDTH_80MHZ:
+			n_chans = 4;
+			break;
+		case CONF_OPER_CHWIDTH_160MHZ:
+			n_chans = 8;
+			break;
+		default:
+			break;
+		}
+	}
+
+	bw = num_chan_to_bw(n_chans);
+
+bw_selected:
+	/* TODO: VHT/HE80+80. Update acs_adjust_center_freq() too. */
+
+	wpa_printf(MSG_DEBUG,
+		   "ACS: Survey analysis for selected bandwidth %d MHz", bw);
+
+	for (i = 0; i < iface->num_hw_features; i++) {
+		mode = &iface->hw_features[i];
+		if (!hostapd_hw_skip_mode(iface, mode))
+			acs_find_ideal_chan_mode(iface, mode, n_chans, bw,
+						 &rand_chan, &ideal_chan,
+						 &ideal_factor);
+	}
+
+	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;
+}
+
+
+static void acs_adjust_secondary(struct hostapd_iface *iface)
+{
+	unsigned int i;
+
+	/* When working with bandwidth over 20 MHz on the 5 GHz or 6 GHz band,
+	 * ACS can return a secondary channel which is not the first channel of
+	 * the segment and we need to adjust. */
+	if (!iface->conf->secondary_channel ||
+	    acs_find_mode(iface, iface->freq) != HOSTAPD_MODE_IEEE80211A)
+		return;
+
+	wpa_printf(MSG_DEBUG, "ACS: Adjusting HT/VHT/HE secondary frequency");
+
+	for (i = 0; bw_desc[ACS_BW40][i].first != -1; i++) {
+		if (iface->freq == bw_desc[ACS_BW40][i].first)
+			iface->conf->secondary_channel = 1;
+		else if (iface->freq == bw_desc[ACS_BW40][i].last)
+			iface->conf->secondary_channel = -1;
+	}
+}
+
+
+static void acs_adjust_center_freq(struct hostapd_iface *iface)
+{
+	int center;
+
+	wpa_printf(MSG_DEBUG, "ACS: Adjusting VHT center frequency");
+
+	switch (hostapd_get_oper_chwidth(iface->conf)) {
+	case CONF_OPER_CHWIDTH_USE_HT:
+		if (iface->conf->secondary_channel &&
+		    iface->freq >= 2400 && iface->freq < 2500)
+			center = iface->conf->channel +
+				2 * iface->conf->secondary_channel;
+		else if (iface->conf->secondary_channel)
+			center = acs_get_bw_center_chan(iface->freq, ACS_BW40);
+		else
+			center = iface->conf->channel;
+		break;
+	case CONF_OPER_CHWIDTH_80MHZ:
+		center = acs_get_bw_center_chan(iface->freq, ACS_BW80);
+		break;
+	case CONF_OPER_CHWIDTH_160MHZ:
+		center = acs_get_bw_center_chan(iface->freq, ACS_BW160);
+		break;
+	default:
+		/* TODO: How can this be calculated? Adjust
+		 * acs_find_ideal_chan() */
+		wpa_printf(MSG_INFO,
+			   "ACS: Only VHT20/40/80/160 is supported now");
+		return;
+	}
+
+	hostapd_set_oper_centr_freq_seg0_idx(iface->conf, center);
+}
+
+
+static int acs_study_survey_based(struct hostapd_iface *iface)
+{
+	wpa_printf(MSG_DEBUG, "ACS: Trying survey-based ACS");
+
+	if (!iface->chans_surveyed) {
+		wpa_printf(MSG_ERROR, "ACS: Unable to collect survey data");
+		return -1;
+	}
+
+	if (!acs_surveys_are_sufficient(iface)) {
+		wpa_printf(MSG_ERROR, "ACS: Surveys have insufficient data");
+		return -1;
+	}
+
+	acs_survey_all_chans_interference_factor(iface);
+	return 0;
+}
+
+
+static int acs_study_options(struct hostapd_iface *iface)
+{
+	if (acs_study_survey_based(iface) == 0)
+		return 0;
+
+	/* TODO: If no surveys are available/sufficient this is a good
+	 * place to fallback to BSS-based ACS */
+
+	return -1;
+}
+
+
+static void acs_study(struct hostapd_iface *iface)
+{
+	struct hostapd_channel_data *ideal_chan;
+	int err;
+
+	err = acs_study_options(iface);
+	if (err < 0) {
+		wpa_printf(MSG_ERROR, "ACS: All study options have failed");
+		goto fail;
+	}
+
+	ideal_chan = acs_find_ideal_chan(iface);
+	if (!ideal_chan) {
+		wpa_printf(MSG_ERROR, "ACS: Failed to compute ideal channel");
+		err = -1;
+		goto fail;
+	}
+
+	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);
+		acs_adjust_center_freq(iface);
+	}
+
+	err = hostapd_select_hw_mode(iface);
+	if (err) {
+		wpa_printf(MSG_ERROR,
+			   "ACS: Could not (err: %d) select hw_mode for freq=%d channel=%d",
+			err, iface->freq, iface->conf->channel);
+		err = -1;
+		goto fail;
+	}
+
+	err = 0;
+fail:
+	/*
+	 * hostapd_setup_interface_complete() will return -1 on failure,
+	 * 0 on success and 0 is HOSTAPD_CHAN_VALID :)
+	 */
+	if (hostapd_acs_completed(iface, err) == HOSTAPD_CHAN_VALID) {
+		acs_cleanup(iface);
+		return;
+	}
+
+	/* This can possibly happen if channel parameters (secondary
+	 * channel, center frequencies) are misconfigured */
+	wpa_printf(MSG_ERROR, "ACS: Possibly channel configuration is invalid, please report this along with your config file.");
+	acs_fail(iface);
+}
+
+
+static void acs_scan_complete(struct hostapd_iface *iface)
+{
+	int err;
+
+	iface->scan_cb = NULL;
+
+	wpa_printf(MSG_DEBUG, "ACS: Using survey based algorithm (acs_num_scans=%d)",
+		   iface->conf->acs_num_scans);
+
+	err = hostapd_drv_get_survey(iface->bss[0], 0);
+	if (err) {
+		wpa_printf(MSG_ERROR, "ACS: Failed to get survey data");
+		goto fail;
+	}
+
+	if (++iface->acs_num_completed_scans < iface->conf->acs_num_scans) {
+		err = acs_request_scan(iface);
+		if (err) {
+			wpa_printf(MSG_ERROR, "ACS: Failed to request scan");
+			goto fail;
+		}
+
+		return;
+	}
+
+	acs_study(iface);
+	return;
+fail:
+	hostapd_acs_completed(iface, 1);
+	acs_fail(iface);
+}
+
+
+static int * acs_request_scan_add_freqs(struct hostapd_iface *iface,
+					struct hostapd_hw_modes *mode,
+					int *freq)
+{
+	struct hostapd_channel_data *chan;
+	int i;
+
+	for (i = 0; i < mode->num_channels; i++) {
+		chan = &mode->channels[i];
+		if ((chan->flag & HOSTAPD_CHAN_DISABLED) ||
+		    ((chan->flag & HOSTAPD_CHAN_RADAR) &&
+		     iface->conf->acs_exclude_dfs))
+			continue;
+
+		if (!is_in_chanlist(iface, chan))
+			continue;
+
+		if (!is_in_freqlist(iface, chan))
+			continue;
+
+		if (chan->max_tx_power < iface->conf->min_tx_power)
+			continue;
+
+		if ((chan->flag & HOSTAPD_CHAN_INDOOR_ONLY) &&
+		    iface->conf->country[2] == 0x4f)
+			continue;
+
+		*freq++ = chan->freq;
+	}
+
+	return freq;
+}
+
+
+static int acs_request_scan(struct hostapd_iface *iface)
+{
+	struct wpa_driver_scan_params params;
+	int i, *freq;
+	int num_channels;
+	struct hostapd_hw_modes *mode;
+
+	os_memset(&params, 0, sizeof(params));
+
+	num_channels = 0;
+	for (i = 0; i < iface->num_hw_features; i++) {
+		mode = &iface->hw_features[i];
+		if (!hostapd_hw_skip_mode(iface, mode))
+			num_channels += mode->num_channels;
+	}
+
+	params.freqs = os_calloc(num_channels + 1, sizeof(params.freqs[0]));
+	if (params.freqs == NULL)
+		return -1;
+
+	freq = params.freqs;
+
+	for (i = 0; i < iface->num_hw_features; i++) {
+		mode = &iface->hw_features[i];
+		if (!hostapd_hw_skip_mode(iface, mode))
+			freq = acs_request_scan_add_freqs(iface, mode, freq);
+	}
+
+	*freq = 0;
+
+	if (params.freqs == freq) {
+		wpa_printf(MSG_ERROR, "ACS: No available channels found");
+		os_free(params.freqs);
+		return -1;
+	}
+
+	iface->scan_cb = acs_scan_complete;
+
+	wpa_printf(MSG_DEBUG, "ACS: Scanning %d / %d",
+		   iface->acs_num_completed_scans + 1,
+		   iface->conf->acs_num_scans);
+
+	if (hostapd_driver_scan(iface->bss[0], &params) < 0) {
+		wpa_printf(MSG_ERROR, "ACS: Failed to request initial scan");
+		acs_cleanup(iface);
+		os_free(params.freqs);
+		return -1;
+	}
+
+	os_free(params.freqs);
+	return 0;
+}
+
+
+enum hostapd_chan_status acs_init(struct hostapd_iface *iface)
+{
+	int err;
+
+	wpa_printf(MSG_INFO, "ACS: Automatic channel selection started, this may take a bit");
+
+	if (iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) {
+		wpa_printf(MSG_INFO, "ACS: Offloading to driver");
+
+		err = hostapd_drv_do_acs(iface->bss[0]);
+		if (err) {
+			if (err == 1)
+				return HOSTAPD_CHAN_INVALID_NO_IR;
+			return HOSTAPD_CHAN_INVALID;
+		}
+
+		return HOSTAPD_CHAN_ACS;
+	}
+
+	if (!iface->current_mode &&
+	    iface->conf->hw_mode != HOSTAPD_MODE_IEEE80211ANY)
+		return HOSTAPD_CHAN_INVALID;
+
+	acs_cleanup(iface);
+
+	if (acs_request_scan(iface) < 0)
+		return HOSTAPD_CHAN_INVALID;
+
+	hostapd_set_state(iface, HAPD_IFACE_ACS);
+	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_STARTED);
+
+	return HOSTAPD_CHAN_ACS;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/acs.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/acs.h
new file mode 100644
index 0000000..ec84f0e
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/acs.h
@@ -0,0 +1,32 @@
+/*
+ * ACS - Automatic Channel Selection module
+ * Copyright (c) 2011, Atheros Communications
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef ACS_H
+#define ACS_H
+
+#ifdef CONFIG_ACS
+
+enum hostapd_chan_status acs_init(struct hostapd_iface *iface);
+void acs_cleanup(struct hostapd_iface *iface);
+
+#else /* CONFIG_ACS */
+
+static inline enum hostapd_chan_status acs_init(struct hostapd_iface *iface)
+{
+	wpa_printf(MSG_ERROR, "ACS was disabled on your build, rebuild hostapd with CONFIG_ACS=y or set channel");
+	return HOSTAPD_CHAN_INVALID;
+}
+
+static inline void acs_cleanup(struct hostapd_iface *iface)
+{
+}
+
+#endif /* CONFIG_ACS */
+
+#endif /* ACS_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/airtime_policy.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/airtime_policy.c
new file mode 100644
index 0000000..3ec8272
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/airtime_policy.c
@@ -0,0 +1,282 @@
+/*
+ * Airtime policy configuration
+ * Copyright (c) 2018-2019, Toke Høiland-Jørgensen <toke@toke.dk>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "hostapd.h"
+#include "ap_drv_ops.h"
+#include "sta_info.h"
+#include "airtime_policy.h"
+
+/* Idea:
+ * Two modes of airtime enforcement:
+ * 1. Static weights: specify weights per MAC address with a per-BSS default
+ * 2. Per-BSS limits: Dynamically calculate weights of backlogged stations to
+ *    enforce relative total shares between BSSes.
+ *
+ * - Periodic per-station callback to update queue status.
+ *
+ * Copy accounting_sta_update_stats() to get TXQ info and airtime weights and
+ * keep them updated in sta_info.
+ *
+ * - Separate periodic per-bss (or per-iface?) callback to update weights.
+ *
+ * Just need to loop through all interfaces, count sum the active stations (or
+ * should the per-STA callback just adjust that for the BSS?) and calculate new
+ * weights.
+ */
+
+static int get_airtime_policy_update_timeout(struct hostapd_iface *iface,
+					     unsigned int *sec,
+					     unsigned int *usec)
+{
+	unsigned int update_int = iface->conf->airtime_update_interval;
+
+	if (!update_int) {
+		wpa_printf(MSG_ERROR,
+			   "Airtime policy: Invalid airtime policy update interval %u",
+			   update_int);
+		return -1;
+	}
+
+	*sec = update_int / 1000;
+	*usec = (update_int % 1000) * 1000;
+
+	return 0;
+}
+
+
+static void set_new_backlog_time(struct hostapd_data *hapd,
+				 struct sta_info *sta,
+				 struct os_reltime *now)
+{
+	sta->backlogged_until = *now;
+	sta->backlogged_until.usec += hapd->iconf->airtime_update_interval *
+		AIRTIME_BACKLOG_EXPIRY_FACTOR;
+	while (sta->backlogged_until.usec >= 1000000) {
+		sta->backlogged_until.sec++;
+		sta->backlogged_until.usec -= 1000000;
+	}
+}
+
+
+static void count_backlogged_sta(struct hostapd_data *hapd)
+{
+	struct sta_info *sta;
+	struct hostap_sta_driver_data data = {};
+	unsigned int num_backlogged = 0;
+	struct os_reltime now;
+
+	os_get_reltime(&now);
+
+	for (sta = hapd->sta_list; sta; sta = sta->next) {
+		if (hostapd_drv_read_sta_data(hapd, &data, sta->addr))
+			continue;
+#ifdef CONFIG_TESTING_OPTIONS
+		if (hapd->force_backlog_bytes)
+			data.backlog_bytes = 1;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+		if (data.backlog_bytes > 0)
+			set_new_backlog_time(hapd, sta, &now);
+		if (os_reltime_before(&now, &sta->backlogged_until))
+			num_backlogged++;
+	}
+	hapd->num_backlogged_sta = num_backlogged;
+}
+
+
+static int sta_set_airtime_weight(struct hostapd_data *hapd,
+				  struct sta_info *sta,
+				  unsigned int weight)
+{
+	int ret = 0;
+
+	if (weight != sta->airtime_weight &&
+	    (ret = hostapd_sta_set_airtime_weight(hapd, sta->addr, weight)))
+		return ret;
+
+	sta->airtime_weight = weight;
+	return ret;
+}
+
+
+static void set_sta_weights(struct hostapd_data *hapd, unsigned int weight)
+{
+	struct sta_info *sta;
+
+	for (sta = hapd->sta_list; sta; sta = sta->next) {
+		unsigned int sta_weight = weight;
+
+		if (sta->dyn_airtime_weight)
+			sta_weight = (weight * sta->dyn_airtime_weight) / 256;
+
+		sta_set_airtime_weight(hapd, sta, sta_weight);
+	}
+}
+
+
+static unsigned int get_airtime_quantum(unsigned int max_wt)
+{
+	unsigned int quantum = AIRTIME_QUANTUM_TARGET / max_wt;
+
+	if (quantum < AIRTIME_QUANTUM_MIN)
+		quantum = AIRTIME_QUANTUM_MIN;
+	else if (quantum > AIRTIME_QUANTUM_MAX)
+		quantum = AIRTIME_QUANTUM_MAX;
+
+	return quantum;
+}
+
+
+static void update_airtime_weights(void *eloop_data, void *user_data)
+{
+	struct hostapd_iface *iface = eloop_data;
+	struct hostapd_data *bss;
+	unsigned int sec, usec;
+	unsigned int num_sta_min = 0, num_sta_prod = 1, num_sta_sum = 0,
+		wt_sum = 0;
+	unsigned int quantum;
+	bool all_div_min = true;
+	bool apply_limit = iface->conf->airtime_mode == AIRTIME_MODE_DYNAMIC;
+	int wt, num_bss = 0, max_wt = 0;
+	size_t i;
+
+	for (i = 0; i < iface->num_bss; i++) {
+		bss = iface->bss[i];
+		if (!bss->started || !bss->conf->airtime_weight)
+			continue;
+
+		count_backlogged_sta(bss);
+		if (!bss->num_backlogged_sta)
+			continue;
+
+		if (!num_sta_min || bss->num_backlogged_sta < num_sta_min)
+			num_sta_min = bss->num_backlogged_sta;
+
+		num_sta_prod *= bss->num_backlogged_sta;
+		num_sta_sum += bss->num_backlogged_sta;
+		wt_sum += bss->conf->airtime_weight;
+		num_bss++;
+	}
+
+	if (num_sta_min) {
+		for (i = 0; i < iface->num_bss; i++) {
+			bss = iface->bss[i];
+			if (!bss->started || !bss->conf->airtime_weight)
+				continue;
+
+			/* Check if we can divide all sta numbers by the
+			 * smallest number to keep weights as small as possible.
+			 * This is a lazy way to avoid having to factor
+			 * integers. */
+			if (bss->num_backlogged_sta &&
+			    bss->num_backlogged_sta % num_sta_min > 0)
+				all_div_min = false;
+
+			/* If we're in LIMIT mode, we only apply the weight
+			 * scaling when the BSS(es) marked as limited would a
+			 * larger share than the relative BSS weights indicates
+			 * it should. */
+			if (!apply_limit && bss->conf->airtime_limit) {
+				if (bss->num_backlogged_sta * wt_sum >
+				    bss->conf->airtime_weight * num_sta_sum)
+					apply_limit = true;
+			}
+		}
+		if (all_div_min)
+			num_sta_prod /= num_sta_min;
+	}
+
+	for (i = 0; i < iface->num_bss; i++) {
+		bss = iface->bss[i];
+		if (!bss->started || !bss->conf->airtime_weight)
+			continue;
+
+		/* We only set the calculated weight if the BSS has active
+		 * stations and there are other active interfaces as well -
+		 * otherwise we just set a unit weight. This ensures that
+		 * the weights are set reasonably when stations transition from
+		 * inactive to active. */
+		if (apply_limit && bss->num_backlogged_sta && num_bss > 1)
+			wt = bss->conf->airtime_weight * num_sta_prod /
+				bss->num_backlogged_sta;
+		else
+			wt = 1;
+
+		bss->airtime_weight = wt;
+		if (wt > max_wt)
+			max_wt = wt;
+	}
+
+	quantum = get_airtime_quantum(max_wt);
+
+	for (i = 0; i < iface->num_bss; i++) {
+		bss = iface->bss[i];
+		if (!bss->started || !bss->conf->airtime_weight)
+			continue;
+		set_sta_weights(bss, bss->airtime_weight * quantum);
+	}
+
+	if (get_airtime_policy_update_timeout(iface, &sec, &usec) < 0)
+		return;
+
+	eloop_register_timeout(sec, usec, update_airtime_weights, iface,
+			       NULL);
+}
+
+
+static int get_weight_for_sta(struct hostapd_data *hapd, const u8 *sta)
+{
+	struct airtime_sta_weight *wt;
+
+	wt = hapd->conf->airtime_weight_list;
+	while (wt && os_memcmp(wt->addr, sta, ETH_ALEN) != 0)
+		wt = wt->next;
+
+	return wt ? wt->weight : hapd->conf->airtime_weight;
+}
+
+
+int airtime_policy_new_sta(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	unsigned int weight;
+
+	if (hapd->iconf->airtime_mode == AIRTIME_MODE_STATIC) {
+		if (sta->dyn_airtime_weight)
+			weight = sta->dyn_airtime_weight;
+		else
+			weight = get_weight_for_sta(hapd, sta->addr);
+		if (weight)
+			return sta_set_airtime_weight(hapd, sta, weight);
+	}
+	return 0;
+}
+
+
+int airtime_policy_update_init(struct hostapd_iface *iface)
+{
+	unsigned int sec, usec;
+
+	if (iface->conf->airtime_mode < AIRTIME_MODE_DYNAMIC)
+		return 0;
+
+	if (get_airtime_policy_update_timeout(iface, &sec, &usec) < 0)
+		return -1;
+
+	eloop_register_timeout(sec, usec, update_airtime_weights, iface, NULL);
+	return 0;
+}
+
+
+void airtime_policy_update_deinit(struct hostapd_iface *iface)
+{
+	eloop_cancel_timeout(update_airtime_weights, iface, NULL);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/airtime_policy.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/airtime_policy.h
new file mode 100644
index 0000000..c2a9b00
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/airtime_policy.h
@@ -0,0 +1,48 @@
+/*
+ * Airtime policy configuration
+ * Copyright (c) 2018-2019, Toke Høiland-Jørgensen <toke@toke.dk>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef AIRTIME_POLICY_H
+#define AIRTIME_POLICY_H
+
+struct hostapd_iface;
+
+#ifdef CONFIG_AIRTIME_POLICY
+
+#define AIRTIME_DEFAULT_UPDATE_INTERVAL 200 /* ms */
+#define AIRTIME_BACKLOG_EXPIRY_FACTOR 2500 /* 2.5 intervals + convert to usec */
+
+/* scale quantum so this becomes the effective quantum after applying the max
+ * weight, but never go below min or above max */
+#define AIRTIME_QUANTUM_MIN 8 /* usec */
+#define AIRTIME_QUANTUM_MAX 256 /* usec */
+#define AIRTIME_QUANTUM_TARGET 1024 /* usec */
+
+int airtime_policy_new_sta(struct hostapd_data *hapd, struct sta_info *sta);
+int airtime_policy_update_init(struct hostapd_iface *iface);
+void airtime_policy_update_deinit(struct hostapd_iface *iface);
+
+#else /* CONFIG_AIRTIME_POLICY */
+
+static inline int airtime_policy_new_sta(struct hostapd_data *hapd,
+					 struct sta_info *sta)
+{
+	return -1;
+}
+
+static inline int airtime_policy_update_init(struct hostapd_iface *iface)
+{
+	return -1;
+}
+
+static inline void airtime_policy_update_deinit(struct hostapd_iface *iface)
+{
+}
+
+#endif /* CONFIG_AIRTIME_POLICY */
+
+#endif /* AIRTIME_POLICY_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_config.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_config.c
new file mode 100644
index 0000000..61cfcca
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_config.c
@@ -0,0 +1,1769 @@
+/*
+ * hostapd / Configuration helper functions
+ * Copyright (c) 2003-2022, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "radius/radius_client.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_1x_defs.h"
+#include "common/eapol_common.h"
+#include "common/dhcp.h"
+#include "common/sae.h"
+#include "eap_common/eap_wsc_common.h"
+#include "eap_server/eap.h"
+#include "wpa_auth.h"
+#include "sta_info.h"
+#include "airtime_policy.h"
+#include "ap_config.h"
+
+
+static void hostapd_config_free_vlan(struct hostapd_bss_config *bss)
+{
+	struct hostapd_vlan *vlan, *prev;
+
+	vlan = bss->vlan;
+	prev = NULL;
+	while (vlan) {
+		prev = vlan;
+		vlan = vlan->next;
+		os_free(prev);
+	}
+
+	bss->vlan = NULL;
+}
+
+
+#ifndef DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES
+#define DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES 0
+#endif /* DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES */
+
+void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
+{
+	dl_list_init(&bss->anqp_elem);
+
+	bss->logger_syslog_level = HOSTAPD_LEVEL_INFO;
+	bss->logger_stdout_level = HOSTAPD_LEVEL_INFO;
+	bss->logger_syslog = (unsigned int) -1;
+	bss->logger_stdout = (unsigned int) -1;
+
+#ifdef CONFIG_WEP
+	bss->auth_algs = WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED;
+
+	bss->wep_rekeying_period = 300;
+	/* use key0 in individual key and key1 in broadcast key */
+	bss->broadcast_key_idx_min = 1;
+	bss->broadcast_key_idx_max = 2;
+#else /* CONFIG_WEP */
+	bss->auth_algs = WPA_AUTH_ALG_OPEN;
+#endif /* CONFIG_WEP */
+	bss->eap_reauth_period = 3600;
+
+	bss->wpa_group_rekey = 600;
+	bss->wpa_gmk_rekey = 86400;
+	bss->wpa_deny_ptk0_rekey = PTK0_REKEY_ALLOW_ALWAYS;
+	bss->wpa_group_update_count = 4;
+	bss->wpa_pairwise_update_count = 4;
+	bss->wpa_disable_eapol_key_retries =
+		DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES;
+	bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
+#ifdef CONFIG_NO_TKIP
+	bss->wpa_pairwise = WPA_CIPHER_CCMP;
+	bss->wpa_group = WPA_CIPHER_CCMP;
+#else /* CONFIG_NO_TKIP */
+	bss->wpa_pairwise = WPA_CIPHER_TKIP;
+	bss->wpa_group = WPA_CIPHER_TKIP;
+#endif /* CONFIG_NO_TKIP */
+	bss->rsn_pairwise = 0;
+
+	bss->max_num_sta = MAX_STA_COUNT;
+
+	bss->dtim_period = 2;
+
+	bss->radius_server_auth_port = 1812;
+	bss->eap_sim_db_timeout = 1;
+	bss->eap_sim_id = 3;
+	bss->ap_max_inactivity = AP_MAX_INACTIVITY;
+	bss->eapol_version = EAPOL_VERSION;
+
+	bss->max_listen_interval = 65535;
+
+	bss->pwd_group = 19; /* ECC: GF(p=256) */
+
+	bss->assoc_sa_query_max_timeout = 1000;
+	bss->assoc_sa_query_retry_timeout = 201;
+	bss->group_mgmt_cipher = WPA_CIPHER_AES_128_CMAC;
+#ifdef EAP_SERVER_FAST
+	 /* both anonymous and authenticated provisioning */
+	bss->eap_fast_prov = 3;
+	bss->pac_key_lifetime = 7 * 24 * 60 * 60;
+	bss->pac_key_refresh_time = 1 * 24 * 60 * 60;
+#endif /* EAP_SERVER_FAST */
+
+	/* Set to -1 as defaults depends on HT in setup */
+	bss->wmm_enabled = -1;
+
+#ifdef CONFIG_IEEE80211R_AP
+	bss->ft_over_ds = 1;
+	bss->rkh_pos_timeout = 86400;
+	bss->rkh_neg_timeout = 60;
+	bss->rkh_pull_timeout = 1000;
+	bss->rkh_pull_retries = 4;
+	bss->r0_key_lifetime = 1209600;
+#endif /* CONFIG_IEEE80211R_AP */
+
+	bss->radius_das_time_window = 300;
+
+	bss->anti_clogging_threshold = 5;
+	bss->sae_sync = 5;
+
+	bss->gas_frag_limit = 1400;
+
+#ifdef CONFIG_FILS
+	dl_list_init(&bss->fils_realms);
+	bss->fils_hlp_wait_time = 30;
+	bss->dhcp_server_port = DHCP_SERVER_PORT;
+	bss->dhcp_relay_port = DHCP_SERVER_PORT;
+	bss->fils_discovery_min_int = 20;
+#endif /* CONFIG_FILS */
+
+	bss->broadcast_deauth = 1;
+
+#ifdef CONFIG_MBO
+	bss->mbo_cell_data_conn_pref = -1;
+#endif /* CONFIG_MBO */
+
+	/* Disable TLS v1.3 by default for now to avoid interoperability issue.
+	 * This can be enabled by default once the implementation has been fully
+	 * completed and tested with other implementations. */
+	bss->tls_flags = TLS_CONN_DISABLE_TLSv1_3;
+
+	bss->max_auth_rounds = 100;
+	bss->max_auth_rounds_short = 50;
+
+	bss->send_probe_response = 1;
+
+#ifdef CONFIG_HS20
+	bss->hs20_release = (HS20_VERSION >> 4) + 1;
+#endif /* CONFIG_HS20 */
+
+#ifdef CONFIG_MACSEC
+	bss->mka_priority = DEFAULT_PRIO_NOT_KEY_SERVER;
+	bss->macsec_port = 1;
+#endif /* CONFIG_MACSEC */
+
+	/* Default to strict CRL checking. */
+	bss->check_crl_strict = 1;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	bss->sae_commit_status = -1;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#ifdef CONFIG_PASN
+	/* comeback after 10 TUs */
+	bss->pasn_comeback_after = 10;
+	bss->pasn_noauth = 1;
+#endif /* CONFIG_PASN */
+}
+
+
+struct hostapd_config * hostapd_config_defaults(void)
+{
+#define ecw2cw(ecw) ((1 << (ecw)) - 1)
+
+	struct hostapd_config *conf;
+	struct hostapd_bss_config *bss;
+	const int aCWmin = 4, aCWmax = 10;
+	const struct hostapd_wmm_ac_params ac_bk =
+		{ aCWmin, aCWmax, 7, 0, 0 }; /* background traffic */
+	const struct hostapd_wmm_ac_params ac_be =
+		{ aCWmin, aCWmax, 3, 0, 0 }; /* best effort traffic */
+	const struct hostapd_wmm_ac_params ac_vi = /* video traffic */
+		{ aCWmin - 1, aCWmin, 2, 3008 / 32, 0 };
+	const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */
+		{ aCWmin - 2, aCWmin - 1, 2, 1504 / 32, 0 };
+	const struct hostapd_tx_queue_params txq_bk =
+		{ 7, ecw2cw(aCWmin), ecw2cw(aCWmax), 0 };
+	const struct hostapd_tx_queue_params txq_be =
+		{ 3, ecw2cw(aCWmin), 4 * (ecw2cw(aCWmin) + 1) - 1, 0};
+	const struct hostapd_tx_queue_params txq_vi =
+		{ 1, (ecw2cw(aCWmin) + 1) / 2 - 1, ecw2cw(aCWmin), 30};
+	const struct hostapd_tx_queue_params txq_vo =
+		{ 1, (ecw2cw(aCWmin) + 1) / 4 - 1,
+		  (ecw2cw(aCWmin) + 1) / 2 - 1, 15};
+
+#undef ecw2cw
+
+	conf = os_zalloc(sizeof(*conf));
+	bss = os_zalloc(sizeof(*bss));
+	if (conf == NULL || bss == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to allocate memory for "
+			   "configuration data.");
+		os_free(conf);
+		os_free(bss);
+		return NULL;
+	}
+	conf->bss = os_calloc(1, sizeof(struct hostapd_bss_config *));
+	if (conf->bss == NULL) {
+		os_free(conf);
+		os_free(bss);
+		return NULL;
+	}
+	conf->bss[0] = bss;
+
+	bss->radius = os_zalloc(sizeof(*bss->radius));
+	if (bss->radius == NULL) {
+		os_free(conf->bss);
+		os_free(conf);
+		os_free(bss);
+		return NULL;
+	}
+
+	hostapd_config_defaults_bss(bss);
+
+	conf->num_bss = 1;
+#ifdef CONFIG_24G_BW_SWITCH
+	/* yewei, add for enable 20M/40M switch, 20240118 */
+	conf->auto_20m_40m = 0;
+#endif /* CONFIG_24G_BW_SWITCH */
+#ifdef CONFIG_5G_BW_SWITCH
+	// To enable 20M/40M/80M bandwidth switch. Added by Liangyu Chu
+	conf->auto_80m = 0;
+#endif /* CONFIG_5G_BW_SWITCH */
+
+	conf->beacon_int = 100;
+	conf->rts_threshold = -2; /* use driver default: 2347 */
+	conf->fragm_threshold = -2; /* user driver default: 2346 */
+	/* Set to invalid value means do not add Power Constraint IE */
+	conf->local_pwr_constraint = -1;
+#ifdef CONFIG_DFS_CAC_TIMER
+	/* Yewei, add dfs cac timer in seconds, 20240118 */
+	conf->dfs_cac_s = 0;
+#endif /* CONFIG_DFS_CAC_TIMER */
+	conf->wmm_ac_params[0] = ac_be;
+	conf->wmm_ac_params[1] = ac_bk;
+	conf->wmm_ac_params[2] = ac_vi;
+	conf->wmm_ac_params[3] = ac_vo;
+
+	conf->tx_queue[0] = txq_vo;
+	conf->tx_queue[1] = txq_vi;
+	conf->tx_queue[2] = txq_be;
+	conf->tx_queue[3] = txq_bk;
+
+	conf->ht_capab = HT_CAP_INFO_SMPS_DISABLED;
+
+	conf->ap_table_max_size = 255;
+	conf->ap_table_expiration_time = 60;
+	conf->track_sta_max_age = 180;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	conf->ignore_probe_probability = 0.0;
+	conf->ignore_auth_probability = 0.0;
+	conf->ignore_assoc_probability = 0.0;
+	conf->ignore_reassoc_probability = 0.0;
+	conf->corrupt_gtk_rekey_mic_probability = 0.0;
+	conf->ecsa_ie_only = 0;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	conf->acs = 0;
+	conf->acs_ch_list.num = 0;
+#ifdef CONFIG_ACS
+	conf->acs_num_scans = 5;
+#endif /* CONFIG_ACS */
+
+#ifdef CONFIG_IEEE80211AX
+	conf->he_op.he_rts_threshold = HE_OPERATION_RTS_THRESHOLD_MASK >>
+		HE_OPERATION_RTS_THRESHOLD_OFFSET;
+	/* Set default basic MCS/NSS set to single stream MCS 0-7 */
+	conf->he_op.he_basic_mcs_nss_set = 0xfffc;
+	conf->he_op.he_bss_color_disabled = 1;
+	conf->he_op.he_bss_color_partial = 0;
+	conf->he_op.he_bss_color = os_random() % 63 + 1;
+	conf->he_op.he_twt_responder = 1;
+	conf->he_6ghz_max_mpdu = 2;
+	conf->he_6ghz_max_ampdu_len_exp = 7;
+	conf->he_6ghz_rx_ant_pat = 1;
+	conf->he_6ghz_tx_ant_pat = 1;
+#endif /* CONFIG_IEEE80211AX */
+
+	/* The third octet of the country string uses an ASCII space character
+	 * by default to indicate that the regulations encompass all
+	 * environments for the current frequency band in the country. */
+	conf->country[2] = ' ';
+
+	conf->rssi_reject_assoc_rssi = 0;
+	conf->rssi_reject_assoc_timeout = 30;
+
+#ifdef CONFIG_AIRTIME_POLICY
+	conf->airtime_update_interval = AIRTIME_DEFAULT_UPDATE_INTERVAL;
+#endif /* CONFIG_AIRTIME_POLICY */
+
+	return conf;
+}
+
+
+int hostapd_mac_comp(const void *a, const void *b)
+{
+	return os_memcmp(a, b, sizeof(macaddr));
+}
+
+
+static int hostapd_config_read_wpa_psk(const char *fname,
+				       struct hostapd_ssid *ssid)
+{
+	FILE *f;
+	char buf[128], *pos;
+	const char *keyid;
+	char *context;
+	char *context2;
+	char *token;
+	char *name;
+	char *value;
+	int line = 0, ret = 0, len, ok;
+	u8 addr[ETH_ALEN];
+	struct hostapd_wpa_psk *psk;
+
+	if (!fname)
+		return 0;
+
+	f = fopen(fname, "r");
+	if (!f) {
+		wpa_printf(MSG_ERROR, "WPA PSK file '%s' not found.", fname);
+		return -1;
+	}
+
+	while (fgets(buf, sizeof(buf), f)) {
+		int vlan_id = 0;
+		int wps = 0;
+
+		line++;
+
+		if (buf[0] == '#')
+			continue;
+		pos = buf;
+		while (*pos != '\0') {
+			if (*pos == '\n') {
+				*pos = '\0';
+				break;
+			}
+			pos++;
+		}
+		if (buf[0] == '\0')
+			continue;
+
+		context = NULL;
+		keyid = NULL;
+		while ((token = str_token(buf, " ", &context))) {
+			if (!os_strchr(token, '='))
+				break;
+			context2 = NULL;
+			name = str_token(token, "=", &context2);
+			if (!name)
+				break;
+			value = str_token(token, "", &context2);
+			if (!value)
+				value = "";
+			if (!os_strcmp(name, "keyid")) {
+				keyid = value;
+			} else if (!os_strcmp(name, "wps")) {
+				wps = atoi(value);
+			} else if (!os_strcmp(name, "vlanid")) {
+				vlan_id = atoi(value);
+			} else {
+				wpa_printf(MSG_ERROR,
+					   "Unrecognized '%s=%s' on line %d in '%s'",
+					   name, value, line, fname);
+				ret = -1;
+				break;
+			}
+		}
+
+		if (ret == -1)
+			break;
+
+		if (!token)
+			token = "";
+		if (hwaddr_aton(token, addr)) {
+			wpa_printf(MSG_ERROR,
+				   "Invalid MAC address '%s' on line %d in '%s'",
+				   token, line, fname);
+			ret = -1;
+			break;
+		}
+
+		psk = os_zalloc(sizeof(*psk));
+		if (psk == NULL) {
+			wpa_printf(MSG_ERROR, "WPA PSK allocation failed");
+			ret = -1;
+			break;
+		}
+		psk->vlan_id = vlan_id;
+		if (is_zero_ether_addr(addr))
+			psk->group = 1;
+		else
+			os_memcpy(psk->addr, addr, ETH_ALEN);
+
+		pos = str_token(buf, "", &context);
+		if (!pos) {
+			wpa_printf(MSG_ERROR, "No PSK on line %d in '%s'",
+				   line, fname);
+			os_free(psk);
+			ret = -1;
+			break;
+		}
+
+		ok = 0;
+		len = os_strlen(pos);
+		if (len == 2 * PMK_LEN &&
+		    hexstr2bin(pos, psk->psk, PMK_LEN) == 0)
+			ok = 1;
+		else if (len >= 8 && len < 64 &&
+			 pbkdf2_sha1(pos, ssid->ssid, ssid->ssid_len,
+				     4096, psk->psk, PMK_LEN) == 0)
+			ok = 1;
+		if (!ok) {
+			wpa_printf(MSG_ERROR,
+				   "Invalid PSK '%s' on line %d in '%s'",
+				   pos, line, fname);
+			os_free(psk);
+			ret = -1;
+			break;
+		}
+
+		if (keyid) {
+			len = os_strlcpy(psk->keyid, keyid, sizeof(psk->keyid));
+			if ((size_t) len >= sizeof(psk->keyid)) {
+				wpa_printf(MSG_ERROR,
+					   "PSK keyid too long on line %d in '%s'",
+					   line, fname);
+				os_free(psk);
+				ret = -1;
+				break;
+			}
+		}
+
+		psk->wps = wps;
+
+		psk->next = ssid->wpa_psk;
+		ssid->wpa_psk = psk;
+	}
+
+	fclose(f);
+
+	return ret;
+}
+
+
+static int hostapd_derive_psk(struct hostapd_ssid *ssid)
+{
+	ssid->wpa_psk = os_zalloc(sizeof(struct hostapd_wpa_psk));
+	if (ssid->wpa_psk == NULL) {
+		wpa_printf(MSG_ERROR, "Unable to alloc space for PSK");
+		return -1;
+	}
+	wpa_hexdump_ascii(MSG_DEBUG, "SSID",
+			  (u8 *) ssid->ssid, ssid->ssid_len);
+	wpa_hexdump_ascii_key(MSG_DEBUG, "PSK (ASCII passphrase)",
+			      (u8 *) ssid->wpa_passphrase,
+			      os_strlen(ssid->wpa_passphrase));
+	if (pbkdf2_sha1(ssid->wpa_passphrase,
+			ssid->ssid, ssid->ssid_len,
+			4096, ssid->wpa_psk->psk, PMK_LEN) != 0) {
+		wpa_printf(MSG_ERROR, "Error in pbkdf2_sha1()");
+		return -1;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "PSK (from passphrase)",
+			ssid->wpa_psk->psk, PMK_LEN);
+	return 0;
+}
+
+
+int hostapd_setup_sae_pt(struct hostapd_bss_config *conf)
+{
+#ifdef CONFIG_SAE
+	struct hostapd_ssid *ssid = &conf->ssid;
+	struct sae_password_entry *pw;
+
+	if ((conf->sae_pwe == SAE_PWE_HUNT_AND_PECK &&
+	     !hostapd_sae_pw_id_in_use(conf) &&
+	     !wpa_key_mgmt_sae_ext_key(conf->wpa_key_mgmt) &&
+	     !hostapd_sae_pk_in_use(conf)) ||
+	    conf->sae_pwe == SAE_PWE_FORCE_HUNT_AND_PECK ||
+	    !wpa_key_mgmt_sae(conf->wpa_key_mgmt))
+		return 0; /* PT not needed */
+
+	sae_deinit_pt(ssid->pt);
+	ssid->pt = NULL;
+	if (ssid->wpa_passphrase) {
+		ssid->pt = sae_derive_pt(conf->sae_groups, ssid->ssid,
+					 ssid->ssid_len,
+					 (const u8 *) ssid->wpa_passphrase,
+					 os_strlen(ssid->wpa_passphrase),
+					 NULL);
+		if (!ssid->pt)
+			return -1;
+	}
+
+	for (pw = conf->sae_passwords; pw; pw = pw->next) {
+		sae_deinit_pt(pw->pt);
+		pw->pt = sae_derive_pt(conf->sae_groups, ssid->ssid,
+				       ssid->ssid_len,
+				       (const u8 *) pw->password,
+				       os_strlen(pw->password),
+				       pw->identifier);
+		if (!pw->pt)
+			return -1;
+	}
+#endif /* CONFIG_SAE */
+
+	return 0;
+}
+
+
+int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf)
+{
+	struct hostapd_ssid *ssid = &conf->ssid;
+
+	if (hostapd_setup_sae_pt(conf) < 0)
+		return -1;
+
+	if (ssid->wpa_passphrase != NULL) {
+		if (ssid->wpa_psk != NULL) {
+			wpa_printf(MSG_DEBUG, "Using pre-configured WPA PSK "
+				   "instead of passphrase");
+		} else {
+			wpa_printf(MSG_DEBUG, "Deriving WPA PSK based on "
+				   "passphrase");
+			if (hostapd_derive_psk(ssid) < 0)
+				return -1;
+		}
+		ssid->wpa_psk->group = 1;
+	}
+
+	return hostapd_config_read_wpa_psk(ssid->wpa_psk_file, &conf->ssid);
+}
+
+
+static void hostapd_config_free_radius(struct hostapd_radius_server *servers,
+				       int num_servers)
+{
+	int i;
+
+	for (i = 0; i < num_servers; i++) {
+		os_free(servers[i].shared_secret);
+	}
+	os_free(servers);
+}
+
+
+struct hostapd_radius_attr *
+hostapd_config_get_radius_attr(struct hostapd_radius_attr *attr, u8 type)
+{
+	for (; attr; attr = attr->next) {
+		if (attr->type == type)
+			return attr;
+	}
+	return NULL;
+}
+
+
+struct hostapd_radius_attr * hostapd_parse_radius_attr(const char *value)
+{
+	const char *pos;
+	char syntax;
+	struct hostapd_radius_attr *attr;
+	size_t len;
+
+	attr = os_zalloc(sizeof(*attr));
+	if (!attr)
+		return NULL;
+
+	attr->type = atoi(value);
+
+	pos = os_strchr(value, ':');
+	if (!pos) {
+		attr->val = wpabuf_alloc(1);
+		if (!attr->val) {
+			os_free(attr);
+			return NULL;
+		}
+		wpabuf_put_u8(attr->val, 0);
+		return attr;
+	}
+
+	pos++;
+	if (pos[0] == '\0' || pos[1] != ':') {
+		os_free(attr);
+		return NULL;
+	}
+	syntax = *pos++;
+	pos++;
+
+	switch (syntax) {
+	case 's':
+		attr->val = wpabuf_alloc_copy(pos, os_strlen(pos));
+		break;
+	case 'x':
+		len = os_strlen(pos);
+		if (len & 1)
+			break;
+		len /= 2;
+		attr->val = wpabuf_alloc(len);
+		if (!attr->val)
+			break;
+		if (hexstr2bin(pos, wpabuf_put(attr->val, len), len) < 0) {
+			wpabuf_free(attr->val);
+			os_free(attr);
+			return NULL;
+		}
+		break;
+	case 'd':
+		attr->val = wpabuf_alloc(4);
+		if (attr->val)
+			wpabuf_put_be32(attr->val, atoi(pos));
+		break;
+	default:
+		os_free(attr);
+		return NULL;
+	}
+
+	if (!attr->val) {
+		os_free(attr);
+		return NULL;
+	}
+
+	return attr;
+}
+
+
+void hostapd_config_free_radius_attr(struct hostapd_radius_attr *attr)
+{
+	struct hostapd_radius_attr *prev;
+
+	while (attr) {
+		prev = attr;
+		attr = attr->next;
+		wpabuf_free(prev->val);
+		os_free(prev);
+	}
+}
+
+
+void hostapd_config_free_eap_user(struct hostapd_eap_user *user)
+{
+	hostapd_config_free_radius_attr(user->accept_attr);
+	os_free(user->identity);
+	bin_clear_free(user->password, user->password_len);
+	bin_clear_free(user->salt, user->salt_len);
+	os_free(user);
+}
+
+
+void hostapd_config_free_eap_users(struct hostapd_eap_user *user)
+{
+	struct hostapd_eap_user *prev_user;
+
+	while (user) {
+		prev_user = user;
+		user = user->next;
+		hostapd_config_free_eap_user(prev_user);
+	}
+}
+
+
+#ifdef CONFIG_WEP
+static void hostapd_config_free_wep(struct hostapd_wep_keys *keys)
+{
+	int i;
+	for (i = 0; i < NUM_WEP_KEYS; i++) {
+		bin_clear_free(keys->key[i], keys->len[i]);
+		keys->key[i] = NULL;
+	}
+}
+#endif /* CONFIG_WEP */
+
+
+void hostapd_config_clear_wpa_psk(struct hostapd_wpa_psk **l)
+{
+	struct hostapd_wpa_psk *psk, *tmp;
+
+	for (psk = *l; psk;) {
+		tmp = psk;
+		psk = psk->next;
+		bin_clear_free(tmp, sizeof(*tmp));
+	}
+	*l = NULL;
+}
+
+
+static void hostapd_config_free_anqp_elem(struct hostapd_bss_config *conf)
+{
+	struct anqp_element *elem;
+
+	while ((elem = dl_list_first(&conf->anqp_elem, struct anqp_element,
+				     list))) {
+		dl_list_del(&elem->list);
+		wpabuf_free(elem->payload);
+		os_free(elem);
+	}
+}
+
+
+static void hostapd_config_free_fils_realms(struct hostapd_bss_config *conf)
+{
+#ifdef CONFIG_FILS
+	struct fils_realm *realm;
+
+	while ((realm = dl_list_first(&conf->fils_realms, struct fils_realm,
+				      list))) {
+		dl_list_del(&realm->list);
+		os_free(realm);
+	}
+#endif /* CONFIG_FILS */
+}
+
+
+static void hostapd_config_free_sae_passwords(struct hostapd_bss_config *conf)
+{
+	struct sae_password_entry *pw, *tmp;
+
+	pw = conf->sae_passwords;
+	conf->sae_passwords = NULL;
+	while (pw) {
+		tmp = pw;
+		pw = pw->next;
+		str_clear_free(tmp->password);
+		os_free(tmp->identifier);
+#ifdef CONFIG_SAE
+		sae_deinit_pt(tmp->pt);
+#endif /* CONFIG_SAE */
+#ifdef CONFIG_SAE_PK
+		sae_deinit_pk(tmp->pk);
+#endif /* CONFIG_SAE_PK */
+		os_free(tmp);
+	}
+}
+
+
+#ifdef CONFIG_DPP2
+static void hostapd_dpp_controller_conf_free(struct dpp_controller_conf *conf)
+{
+	struct dpp_controller_conf *prev;
+
+	while (conf) {
+		prev = conf;
+		conf = conf->next;
+		os_free(prev);
+	}
+}
+#endif /* CONFIG_DPP2 */
+
+
+void hostapd_config_free_bss(struct hostapd_bss_config *conf)
+{
+#if defined(CONFIG_WPS) || defined(CONFIG_HS20)
+	size_t i;
+#endif
+
+	if (conf == NULL)
+		return;
+
+	hostapd_config_clear_wpa_psk(&conf->ssid.wpa_psk);
+
+	str_clear_free(conf->ssid.wpa_passphrase);
+	os_free(conf->ssid.wpa_psk_file);
+#ifdef CONFIG_WEP
+	hostapd_config_free_wep(&conf->ssid.wep);
+#endif /* CONFIG_WEP */
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+	os_free(conf->ssid.vlan_tagged_interface);
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+#ifdef CONFIG_SAE
+	sae_deinit_pt(conf->ssid.pt);
+#endif /* CONFIG_SAE */
+
+	hostapd_config_free_eap_users(conf->eap_user);
+	os_free(conf->eap_user_sqlite);
+
+	os_free(conf->eap_req_id_text);
+	os_free(conf->erp_domain);
+	os_free(conf->accept_mac);
+	os_free(conf->deny_mac);
+	os_free(conf->nas_identifier);
+	if (conf->radius) {
+		hostapd_config_free_radius(conf->radius->auth_servers,
+					   conf->radius->num_auth_servers);
+		hostapd_config_free_radius(conf->radius->acct_servers,
+					   conf->radius->num_acct_servers);
+		os_free(conf->radius->force_client_dev);
+	}
+	hostapd_config_free_radius_attr(conf->radius_auth_req_attr);
+	hostapd_config_free_radius_attr(conf->radius_acct_req_attr);
+	os_free(conf->radius_req_attr_sqlite);
+	os_free(conf->rsn_preauth_interfaces);
+	os_free(conf->ctrl_interface);
+	os_free(conf->config_id);
+	os_free(conf->ca_cert);
+	os_free(conf->server_cert);
+	os_free(conf->server_cert2);
+	os_free(conf->private_key);
+	os_free(conf->private_key2);
+	os_free(conf->private_key_passwd);
+	os_free(conf->private_key_passwd2);
+	os_free(conf->check_cert_subject);
+	os_free(conf->ocsp_stapling_response);
+	os_free(conf->ocsp_stapling_response_multi);
+	os_free(conf->dh_file);
+	os_free(conf->openssl_ciphers);
+	os_free(conf->openssl_ecdh_curves);
+	os_free(conf->pac_opaque_encr_key);
+	os_free(conf->eap_fast_a_id);
+	os_free(conf->eap_fast_a_id_info);
+	os_free(conf->eap_sim_db);
+	os_free(conf->imsi_privacy_key);
+	os_free(conf->radius_server_clients);
+	os_free(conf->radius);
+	os_free(conf->radius_das_shared_secret);
+	hostapd_config_free_vlan(conf);
+	os_free(conf->time_zone);
+
+#ifdef CONFIG_IEEE80211R_AP
+	{
+		struct ft_remote_r0kh *r0kh, *r0kh_prev;
+		struct ft_remote_r1kh *r1kh, *r1kh_prev;
+
+		r0kh = conf->r0kh_list;
+		conf->r0kh_list = NULL;
+		while (r0kh) {
+			r0kh_prev = r0kh;
+			r0kh = r0kh->next;
+			os_free(r0kh_prev);
+		}
+
+		r1kh = conf->r1kh_list;
+		conf->r1kh_list = NULL;
+		while (r1kh) {
+			r1kh_prev = r1kh;
+			r1kh = r1kh->next;
+			os_free(r1kh_prev);
+		}
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+
+#ifdef CONFIG_WPS
+	os_free(conf->wps_pin_requests);
+	os_free(conf->device_name);
+	os_free(conf->manufacturer);
+	os_free(conf->model_name);
+	os_free(conf->model_number);
+	os_free(conf->serial_number);
+	os_free(conf->config_methods);
+	os_free(conf->ap_pin);
+	os_free(conf->extra_cred);
+	os_free(conf->ap_settings);
+	hostapd_config_clear_wpa_psk(&conf->multi_ap_backhaul_ssid.wpa_psk);
+	str_clear_free(conf->multi_ap_backhaul_ssid.wpa_passphrase);
+	os_free(conf->upnp_iface);
+	os_free(conf->friendly_name);
+	os_free(conf->manufacturer_url);
+	os_free(conf->model_description);
+	os_free(conf->model_url);
+	os_free(conf->upc);
+	for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++)
+		wpabuf_free(conf->wps_vendor_ext[i]);
+	wpabuf_free(conf->wps_application_ext);
+	wpabuf_free(conf->wps_nfc_dh_pubkey);
+	wpabuf_free(conf->wps_nfc_dh_privkey);
+	wpabuf_free(conf->wps_nfc_dev_pw);
+#endif /* CONFIG_WPS */
+
+	os_free(conf->roaming_consortium);
+	os_free(conf->venue_name);
+	os_free(conf->venue_url);
+	os_free(conf->nai_realm_data);
+	os_free(conf->network_auth_type);
+	os_free(conf->anqp_3gpp_cell_net);
+	os_free(conf->domain_name);
+	hostapd_config_free_anqp_elem(conf);
+
+#ifdef CONFIG_RADIUS_TEST
+	os_free(conf->dump_msk_file);
+#endif /* CONFIG_RADIUS_TEST */
+
+#ifdef CONFIG_HS20
+	os_free(conf->hs20_oper_friendly_name);
+	os_free(conf->hs20_wan_metrics);
+	os_free(conf->hs20_connection_capability);
+	os_free(conf->hs20_operating_class);
+	os_free(conf->hs20_icons);
+	if (conf->hs20_osu_providers) {
+		for (i = 0; i < conf->hs20_osu_providers_count; i++) {
+			struct hs20_osu_provider *p;
+			size_t j;
+			p = &conf->hs20_osu_providers[i];
+			os_free(p->friendly_name);
+			os_free(p->server_uri);
+			os_free(p->method_list);
+			for (j = 0; j < p->icons_count; j++)
+				os_free(p->icons[j]);
+			os_free(p->icons);
+			os_free(p->osu_nai);
+			os_free(p->osu_nai2);
+			os_free(p->service_desc);
+		}
+		os_free(conf->hs20_osu_providers);
+	}
+	if (conf->hs20_operator_icon) {
+		for (i = 0; i < conf->hs20_operator_icon_count; i++)
+			os_free(conf->hs20_operator_icon[i]);
+		os_free(conf->hs20_operator_icon);
+	}
+	os_free(conf->subscr_remediation_url);
+	os_free(conf->hs20_sim_provisioning_url);
+	os_free(conf->t_c_filename);
+	os_free(conf->t_c_server_url);
+#endif /* CONFIG_HS20 */
+
+	wpabuf_free(conf->vendor_elements);
+	wpabuf_free(conf->assocresp_elements);
+
+	os_free(conf->sae_groups);
+#ifdef CONFIG_OWE
+	os_free(conf->owe_groups);
+#endif /* CONFIG_OWE */
+
+	os_free(conf->wowlan_triggers);
+
+	os_free(conf->server_id);
+
+#ifdef CONFIG_TESTING_OPTIONS
+	wpabuf_free(conf->own_ie_override);
+	wpabuf_free(conf->sae_commit_override);
+	wpabuf_free(conf->rsne_override_eapol);
+	wpabuf_free(conf->rsnxe_override_eapol);
+	wpabuf_free(conf->rsne_override_ft);
+	wpabuf_free(conf->rsnxe_override_ft);
+	wpabuf_free(conf->gtk_rsc_override);
+	wpabuf_free(conf->igtk_rsc_override);
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	os_free(conf->no_probe_resp_if_seen_on);
+	os_free(conf->no_auth_if_seen_on);
+
+	hostapd_config_free_fils_realms(conf);
+
+#ifdef CONFIG_DPP
+	os_free(conf->dpp_name);
+	os_free(conf->dpp_mud_url);
+	os_free(conf->dpp_extra_conf_req_name);
+	os_free(conf->dpp_extra_conf_req_value);
+	os_free(conf->dpp_connector);
+	wpabuf_free(conf->dpp_netaccesskey);
+	wpabuf_free(conf->dpp_csign);
+#ifdef CONFIG_DPP2
+	hostapd_dpp_controller_conf_free(conf->dpp_controller);
+#endif /* CONFIG_DPP2 */
+#endif /* CONFIG_DPP */
+
+	hostapd_config_free_sae_passwords(conf);
+
+#ifdef CONFIG_AIRTIME_POLICY
+	{
+		struct airtime_sta_weight *wt, *wt_prev;
+
+		wt = conf->airtime_weight_list;
+		conf->airtime_weight_list = NULL;
+		while (wt) {
+			wt_prev = wt;
+			wt = wt->next;
+			os_free(wt_prev);
+		}
+	}
+#endif /* CONFIG_AIRTIME_POLICY */
+
+#ifdef CONFIG_PASN
+	os_free(conf->pasn_groups);
+#endif /* CONFIG_PASN */
+
+	os_free(conf);
+}
+
+
+/**
+ * hostapd_config_free - Free hostapd configuration
+ * @conf: Configuration data from hostapd_config_read().
+ */
+void hostapd_config_free(struct hostapd_config *conf)
+{
+	size_t i;
+
+	if (conf == NULL)
+		return;
+
+	for (i = 0; i < conf->num_bss; i++)
+		hostapd_config_free_bss(conf->bss[i]);
+	os_free(conf->config_id);
+	os_free(conf->bss);
+	os_free(conf->supported_rates);
+	os_free(conf->basic_rates);
+	os_free(conf->acs_ch_list.range);
+	os_free(conf->acs_freq_list.range);
+	os_free(conf->driver_params);
+#ifdef CONFIG_ACS
+	os_free(conf->acs_chan_bias);
+#endif /* CONFIG_ACS */
+	wpabuf_free(conf->lci);
+	wpabuf_free(conf->civic);
+
+	os_free(conf);
+}
+
+
+/**
+ * hostapd_maclist_found - Find a MAC address from a list
+ * @list: MAC address list
+ * @num_entries: Number of addresses in the list
+ * @addr: Address to search for
+ * @vlan_id: Buffer for returning VLAN ID or %NULL if not needed
+ * Returns: 1 if address is in the list or 0 if not.
+ *
+ * Perform a binary search for given MAC address from a pre-sorted list.
+ */
+int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries,
+			  const u8 *addr, struct vlan_description *vlan_id)
+{
+	int start, end, middle, res;
+
+	start = 0;
+	end = num_entries - 1;
+
+	while (start <= end) {
+		middle = (start + end) / 2;
+		res = os_memcmp(list[middle].addr, addr, ETH_ALEN);
+		if (res == 0) {
+			if (vlan_id)
+				*vlan_id = list[middle].vlan_id;
+			return 1;
+		}
+		if (res < 0)
+			start = middle + 1;
+		else
+			end = middle - 1;
+	}
+
+	return 0;
+}
+
+
+int hostapd_rate_found(int *list, int rate)
+{
+	int i;
+
+	if (list == NULL)
+		return 0;
+
+	for (i = 0; list[i] >= 0; i++)
+		if (list[i] == rate)
+			return 1;
+
+	return 0;
+}
+
+
+int hostapd_vlan_valid(struct hostapd_vlan *vlan,
+		       struct vlan_description *vlan_desc)
+{
+	struct hostapd_vlan *v = vlan;
+	int i;
+
+	if (!vlan_desc->notempty || vlan_desc->untagged < 0 ||
+	    vlan_desc->untagged > MAX_VLAN_ID)
+		return 0;
+	for (i = 0; i < MAX_NUM_TAGGED_VLAN; i++) {
+		if (vlan_desc->tagged[i] < 0 ||
+		    vlan_desc->tagged[i] > MAX_VLAN_ID)
+			return 0;
+	}
+	if (!vlan_desc->untagged && !vlan_desc->tagged[0])
+		return 0;
+
+	while (v) {
+		if (!vlan_compare(&v->vlan_desc, vlan_desc) ||
+		    v->vlan_id == VLAN_ID_WILDCARD)
+			return 1;
+		v = v->next;
+	}
+	return 0;
+}
+
+
+const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id)
+{
+	struct hostapd_vlan *v = vlan;
+	while (v) {
+		if (v->vlan_id == vlan_id)
+			return v->ifname;
+		v = v->next;
+	}
+	return NULL;
+}
+
+
+const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
+			   const u8 *addr, const u8 *p2p_dev_addr,
+			   const u8 *prev_psk, int *vlan_id)
+{
+	struct hostapd_wpa_psk *psk;
+	int next_ok = prev_psk == NULL;
+
+	if (vlan_id)
+		*vlan_id = 0;
+
+	if (p2p_dev_addr && !is_zero_ether_addr(p2p_dev_addr)) {
+		wpa_printf(MSG_DEBUG, "Searching a PSK for " MACSTR
+			   " p2p_dev_addr=" MACSTR " prev_psk=%p",
+			   MAC2STR(addr), MAC2STR(p2p_dev_addr), prev_psk);
+		addr = NULL; /* Use P2P Device Address for matching */
+	} else {
+		wpa_printf(MSG_DEBUG, "Searching a PSK for " MACSTR
+			   " prev_psk=%p",
+			   MAC2STR(addr), prev_psk);
+	}
+
+	for (psk = conf->ssid.wpa_psk; psk != NULL; psk = psk->next) {
+		if (next_ok &&
+		    (psk->group ||
+		     (addr && os_memcmp(psk->addr, addr, ETH_ALEN) == 0) ||
+		     (!addr && p2p_dev_addr &&
+		      os_memcmp(psk->p2p_dev_addr, p2p_dev_addr, ETH_ALEN) ==
+		      0))) {
+			if (vlan_id)
+				*vlan_id = psk->vlan_id;
+			return psk->psk;
+		}
+
+		if (psk->psk == prev_psk)
+			next_ok = 1;
+	}
+
+	return NULL;
+}
+
+
+#ifdef CONFIG_SAE_PK
+static bool hostapd_sae_pk_password_without_pk(struct hostapd_bss_config *bss)
+{
+	struct sae_password_entry *pw;
+	bool res = false;
+
+	if (bss->ssid.wpa_passphrase &&
+#ifdef CONFIG_TESTING_OPTIONS
+	    !bss->sae_pk_password_check_skip &&
+#endif /* CONFIG_TESTING_OPTIONS */
+	    sae_pk_valid_password(bss->ssid.wpa_passphrase))
+		res = true;
+
+	for (pw = bss->sae_passwords; pw; pw = pw->next) {
+		if (!pw->pk &&
+#ifdef CONFIG_TESTING_OPTIONS
+		    !bss->sae_pk_password_check_skip &&
+#endif /* CONFIG_TESTING_OPTIONS */
+		    sae_pk_valid_password(pw->password))
+			return true;
+
+		if (bss->ssid.wpa_passphrase && res && pw->pk &&
+		    os_strcmp(bss->ssid.wpa_passphrase, pw->password) == 0)
+			res = false;
+	}
+
+	return res;
+}
+#endif /* CONFIG_SAE_PK */
+
+
+static bool hostapd_config_check_bss_6g(struct hostapd_bss_config *bss)
+{
+	if (bss->wpa != WPA_PROTO_RSN) {
+		wpa_printf(MSG_ERROR,
+			   "Pre-RSNA security methods are not allowed in 6 GHz");
+		return false;
+	}
+
+	if (bss->ieee80211w != MGMT_FRAME_PROTECTION_REQUIRED) {
+		wpa_printf(MSG_ERROR,
+			   "Management frame protection is required in 6 GHz");
+		return false;
+	}
+
+	if (bss->wpa_key_mgmt & (WPA_KEY_MGMT_PSK |
+				 WPA_KEY_MGMT_FT_PSK |
+				 WPA_KEY_MGMT_PSK_SHA256)) {
+		wpa_printf(MSG_ERROR, "Invalid AKM suite for 6 GHz");
+		return false;
+	}
+
+	if (bss->rsn_pairwise & (WPA_CIPHER_WEP40 |
+				 WPA_CIPHER_WEP104 |
+				 WPA_CIPHER_TKIP)) {
+		wpa_printf(MSG_ERROR,
+			   "Invalid pairwise cipher suite for 6 GHz");
+		return false;
+	}
+
+	if (bss->wpa_group & (WPA_CIPHER_WEP40 |
+			      WPA_CIPHER_WEP104 |
+			      WPA_CIPHER_TKIP)) {
+		wpa_printf(MSG_ERROR, "Invalid group cipher suite for 6 GHz");
+		return false;
+	}
+
+#ifdef CONFIG_SAE
+	if (wpa_key_mgmt_sae(bss->wpa_key_mgmt) &&
+	    bss->sae_pwe == SAE_PWE_HUNT_AND_PECK) {
+		wpa_printf(MSG_INFO, "SAE: Enabling SAE H2E on 6 GHz");
+		bss->sae_pwe = SAE_PWE_BOTH;
+	}
+#endif /* CONFIG_SAE */
+
+	return true;
+}
+
+
+static int hostapd_config_check_bss(struct hostapd_bss_config *bss,
+				    struct hostapd_config *conf,
+				    int full_config)
+{
+	if (full_config && is_6ghz_op_class(conf->op_class) &&
+	    !hostapd_config_check_bss_6g(bss))
+		return -1;
+
+	if (full_config && bss->ieee802_1x && !bss->eap_server &&
+	    !bss->radius->auth_servers) {
+		wpa_printf(MSG_ERROR, "Invalid IEEE 802.1X configuration (no "
+			   "EAP authenticator configured).");
+		return -1;
+	}
+
+#ifdef CONFIG_WEP
+	if (bss->wpa) {
+		int wep, i;
+
+		wep = bss->default_wep_key_len > 0 ||
+		       bss->individual_wep_key_len > 0;
+		for (i = 0; i < NUM_WEP_KEYS; i++) {
+			if (bss->ssid.wep.keys_set) {
+				wep = 1;
+				break;
+			}
+		}
+
+		if (wep) {
+			wpa_printf(MSG_ERROR, "WEP configuration in a WPA network is not supported");
+			return -1;
+		}
+	}
+#endif /* CONFIG_WEP */
+
+	if (full_config && bss->wpa &&
+	    bss->wpa_psk_radius != PSK_RADIUS_IGNORED &&
+	    bss->wpa_psk_radius != PSK_RADIUS_DURING_4WAY_HS &&
+	    bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH) {
+		wpa_printf(MSG_ERROR, "WPA-PSK using RADIUS enabled, but no "
+			   "RADIUS checking (macaddr_acl=2) enabled.");
+		return -1;
+	}
+
+	if (full_config && bss->wpa &&
+	    wpa_key_mgmt_wpa_psk_no_sae(bss->wpa_key_mgmt) &&
+	    bss->ssid.wpa_psk == NULL && bss->ssid.wpa_passphrase == NULL &&
+	    bss->ssid.wpa_psk_file == NULL &&
+	    bss->wpa_psk_radius != PSK_RADIUS_DURING_4WAY_HS &&
+	    (bss->wpa_psk_radius != PSK_RADIUS_REQUIRED ||
+	     bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH)) {
+		wpa_printf(MSG_ERROR, "WPA-PSK enabled, but PSK or passphrase "
+			   "is not configured.");
+		return -1;
+	}
+
+	if (full_config && !is_zero_ether_addr(bss->bssid)) {
+		size_t i;
+
+		for (i = 0; i < conf->num_bss; i++) {
+			if (conf->bss[i] != bss &&
+			    (hostapd_mac_comp(conf->bss[i]->bssid,
+					      bss->bssid) == 0)) {
+				wpa_printf(MSG_ERROR, "Duplicate BSSID " MACSTR
+					   " on interface '%s' and '%s'.",
+					   MAC2STR(bss->bssid),
+					   conf->bss[i]->iface, bss->iface);
+				return -1;
+			}
+		}
+	}
+
+#ifdef CONFIG_IEEE80211R_AP
+	if (full_config && wpa_key_mgmt_ft(bss->wpa_key_mgmt) &&
+	    (bss->nas_identifier == NULL ||
+	     os_strlen(bss->nas_identifier) < 1 ||
+	     os_strlen(bss->nas_identifier) > FT_R0KH_ID_MAX_LEN)) {
+		wpa_printf(MSG_ERROR, "FT (IEEE 802.11r) requires "
+			   "nas_identifier to be configured as a 1..48 octet "
+			   "string");
+		return -1;
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+
+	if (full_config && conf->ieee80211n &&
+	    conf->hw_mode == HOSTAPD_MODE_IEEE80211B) {
+		bss->disable_11n = true;
+		wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) in 11b mode is not "
+			   "allowed, disabling HT capabilities");
+	}
+
+#ifdef CONFIG_WEP
+	if (full_config && conf->ieee80211n &&
+	    bss->ssid.security_policy == SECURITY_STATIC_WEP) {
+		bss->disable_11n = true;
+		wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) with WEP is not "
+			   "allowed, disabling HT capabilities");
+	}
+#endif /* CONFIG_WEP */
+
+	if (full_config && conf->ieee80211n && bss->wpa &&
+	    !(bss->wpa_pairwise & WPA_CIPHER_CCMP) &&
+	    !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP |
+				   WPA_CIPHER_CCMP_256 | WPA_CIPHER_GCMP_256)))
+	{
+		bss->disable_11n = true;
+		wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) with WPA/WPA2 "
+			   "requires CCMP/GCMP to be enabled, disabling HT "
+			   "capabilities");
+	}
+
+#ifdef CONFIG_IEEE80211AC
+#ifdef CONFIG_WEP
+	if (full_config && conf->ieee80211ac &&
+	    bss->ssid.security_policy == SECURITY_STATIC_WEP) {
+		bss->disable_11ac = true;
+		wpa_printf(MSG_ERROR,
+			   "VHT (IEEE 802.11ac) with WEP is not allowed, disabling VHT capabilities");
+	}
+#endif /* CONFIG_WEP */
+
+	if (full_config && conf->ieee80211ac && bss->wpa &&
+	    !(bss->wpa_pairwise & WPA_CIPHER_CCMP) &&
+	    !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP |
+				   WPA_CIPHER_CCMP_256 | WPA_CIPHER_GCMP_256)))
+	{
+		bss->disable_11ac = true;
+		wpa_printf(MSG_ERROR,
+			   "VHT (IEEE 802.11ac) with WPA/WPA2 requires CCMP/GCMP to be enabled, disabling VHT capabilities");
+	}
+#endif /* CONFIG_IEEE80211AC */
+
+#ifdef CONFIG_IEEE80211AX
+#ifdef CONFIG_WEP
+	if (full_config && conf->ieee80211ax &&
+	    bss->ssid.security_policy == SECURITY_STATIC_WEP) {
+		bss->disable_11ax = true;
+		wpa_printf(MSG_ERROR,
+			   "HE (IEEE 802.11ax) with WEP is not allowed, disabling HE capabilities");
+	}
+#endif /* CONFIG_WEP */
+
+	if (full_config && conf->ieee80211ax && bss->wpa &&
+	    !(bss->wpa_pairwise & WPA_CIPHER_CCMP) &&
+	    !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP |
+				   WPA_CIPHER_CCMP_256 | WPA_CIPHER_GCMP_256)))
+	{
+		bss->disable_11ax = true;
+		wpa_printf(MSG_ERROR,
+			   "HE (IEEE 802.11ax) with WPA/WPA2 requires CCMP/GCMP to be enabled, disabling HE capabilities");
+	}
+#endif /* CONFIG_IEEE80211AX */
+
+#ifdef CONFIG_WPS
+	if (full_config && bss->wps_state && bss->ignore_broadcast_ssid) {
+		wpa_printf(MSG_INFO, "WPS: ignore_broadcast_ssid "
+			   "configuration forced WPS to be disabled");
+		bss->wps_state = 0;
+	}
+
+#ifdef CONFIG_WEP
+	if (full_config && bss->wps_state &&
+	    bss->ssid.wep.keys_set && bss->wpa == 0) {
+		wpa_printf(MSG_INFO, "WPS: WEP configuration forced WPS to be "
+			   "disabled");
+		bss->wps_state = 0;
+	}
+#endif /* CONFIG_WEP */
+
+	if (full_config && bss->wps_state && bss->wpa &&
+	    (!(bss->wpa & 2) ||
+	     !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP |
+				    WPA_CIPHER_CCMP_256 |
+				    WPA_CIPHER_GCMP_256)))) {
+		wpa_printf(MSG_INFO, "WPS: WPA/TKIP configuration without "
+			   "WPA2/CCMP/GCMP forced WPS to be disabled");
+		bss->wps_state = 0;
+	}
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_HS20
+	if (full_config && bss->hs20 &&
+	    (!(bss->wpa & 2) ||
+	     !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP |
+				    WPA_CIPHER_CCMP_256 |
+				    WPA_CIPHER_GCMP_256)))) {
+		wpa_printf(MSG_ERROR, "HS 2.0: WPA2-Enterprise/CCMP "
+			   "configuration is required for Hotspot 2.0 "
+			   "functionality");
+		return -1;
+	}
+#endif /* CONFIG_HS20 */
+
+#ifdef CONFIG_MBO
+	if (full_config && bss->mbo_enabled && (bss->wpa & 2) &&
+	    bss->ieee80211w == NO_MGMT_FRAME_PROTECTION) {
+		wpa_printf(MSG_ERROR,
+			   "MBO: PMF needs to be enabled whenever using WPA2 with MBO");
+		return -1;
+	}
+#endif /* CONFIG_MBO */
+
+#ifdef CONFIG_OCV
+	if (full_config && bss->ieee80211w == NO_MGMT_FRAME_PROTECTION &&
+	    bss->ocv) {
+		wpa_printf(MSG_ERROR,
+			   "OCV: PMF needs to be enabled whenever using OCV");
+		return -1;
+	}
+#endif /* CONFIG_OCV */
+
+#ifdef CONFIG_SAE_PK
+	if (full_config && hostapd_sae_pk_in_use(bss) &&
+	    hostapd_sae_pk_password_without_pk(bss)) {
+		wpa_printf(MSG_ERROR,
+			   "SAE-PK: SAE password uses SAE-PK style, but does not have PK configured");
+		return -1;
+	}
+#endif /* CONFIG_SAE_PK */
+
+#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");
+		return -1;
+	}
+#endif /* CONFIG_FILS */
+
+#ifdef CONFIG_IEEE80211BE
+	if (full_config && !bss->disable_11be && bss->disable_11ax) {
+		bss->disable_11be = true;
+		wpa_printf(MSG_INFO,
+			   "Disabling IEEE 802.11be as IEEE 802.11ax is disabled for this BSS");
+	}
+#endif /* CONFIG_IEEE80211BE */
+
+	if (full_config && bss->ignore_broadcast_ssid && conf->mbssid) {
+		wpa_printf(MSG_ERROR,
+			   "Hidden SSID is not suppored when MBSSID is enabled");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int hostapd_config_check_cw(struct hostapd_config *conf, int queue)
+{
+	int tx_cwmin = conf->tx_queue[queue].cwmin;
+	int tx_cwmax = conf->tx_queue[queue].cwmax;
+	int ac_cwmin = conf->wmm_ac_params[queue].cwmin;
+	int ac_cwmax = conf->wmm_ac_params[queue].cwmax;
+
+	if (tx_cwmin > tx_cwmax) {
+		wpa_printf(MSG_ERROR,
+			   "Invalid TX queue cwMin/cwMax values. cwMin(%d) greater than cwMax(%d)",
+			   tx_cwmin, tx_cwmax);
+		return -1;
+	}
+	if (ac_cwmin > ac_cwmax) {
+		wpa_printf(MSG_ERROR,
+			   "Invalid WMM AC cwMin/cwMax values. cwMin(%d) greater than cwMax(%d)",
+			   ac_cwmin, ac_cwmax);
+		return -1;
+	}
+	return 0;
+}
+
+
+int hostapd_config_check(struct hostapd_config *conf, int full_config)
+{
+	size_t i;
+
+	if (full_config && is_6ghz_op_class(conf->op_class) &&
+	    !conf->hw_mode_set) {
+		/* Use the appropriate hw_mode value automatically when the
+		 * op_class parameter has been set, but hw_mode was not. */
+		conf->hw_mode = HOSTAPD_MODE_IEEE80211A;
+	}
+
+	if (full_config && conf->ieee80211d &&
+	    (!conf->country[0] || !conf->country[1])) {
+		wpa_printf(MSG_ERROR, "Cannot enable IEEE 802.11d without "
+			   "setting the country_code");
+		return -1;
+	}
+
+	if (full_config && conf->ieee80211h && !conf->ieee80211d) {
+		wpa_printf(MSG_ERROR, "Cannot enable IEEE 802.11h without "
+			   "IEEE 802.11d enabled");
+		return -1;
+	}
+
+	if (full_config && conf->local_pwr_constraint != -1 &&
+	    !conf->ieee80211d) {
+		wpa_printf(MSG_ERROR, "Cannot add Power Constraint element without Country element");
+		return -1;
+	}
+
+	if (full_config && conf->spectrum_mgmt_required &&
+	    conf->local_pwr_constraint == -1) {
+		wpa_printf(MSG_ERROR, "Cannot set Spectrum Management bit without Country and Power Constraint elements");
+		return -1;
+	}
+
+#ifdef CONFIG_AIRTIME_POLICY
+	if (full_config && conf->airtime_mode > AIRTIME_MODE_STATIC &&
+	    !conf->airtime_update_interval) {
+		wpa_printf(MSG_ERROR, "Airtime update interval cannot be zero");
+		return -1;
+	}
+#endif /* CONFIG_AIRTIME_POLICY */
+	for (i = 0; i < NUM_TX_QUEUES; i++) {
+		if (hostapd_config_check_cw(conf, i))
+			return -1;
+	}
+
+#ifdef CONFIG_IEEE80211BE
+	if (full_config && conf->ieee80211be && !conf->ieee80211ax) {
+		wpa_printf(MSG_ERROR,
+			   "Cannot set ieee80211be without ieee80211ax");
+		return -1;
+	}
+#endif /* CONFIG_IEEE80211BE */
+
+	if (full_config && conf->mbssid && !conf->ieee80211ax) {
+		wpa_printf(MSG_ERROR,
+			   "Cannot enable multiple BSSID support without ieee80211ax");
+		return -1;
+	}
+
+	for (i = 0; i < conf->num_bss; i++) {
+		if (hostapd_config_check_bss(conf->bss[i], conf, full_config))
+			return -1;
+	}
+
+	return 0;
+}
+
+
+void hostapd_set_security_params(struct hostapd_bss_config *bss,
+				 int full_config)
+{
+#ifdef CONFIG_WEP
+	if (bss->individual_wep_key_len == 0) {
+		/* individual keys are not use; can use key idx0 for
+		 * broadcast keys */
+		bss->broadcast_key_idx_min = 0;
+	}
+#endif /* CONFIG_WEP */
+
+	if ((bss->wpa & 2) && bss->rsn_pairwise == 0)
+		bss->rsn_pairwise = bss->wpa_pairwise;
+	if (bss->group_cipher)
+		bss->wpa_group = bss->group_cipher;
+	else
+		bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa,
+							    bss->wpa_pairwise,
+							    bss->rsn_pairwise);
+	if (!bss->wpa_group_rekey_set)
+		bss->wpa_group_rekey = bss->wpa_group == WPA_CIPHER_TKIP ?
+			600 : 86400;
+
+	if (full_config) {
+		bss->radius->auth_server = bss->radius->auth_servers;
+		bss->radius->acct_server = bss->radius->acct_servers;
+	}
+
+	if (bss->wpa && bss->ieee802_1x) {
+		bss->ssid.security_policy = SECURITY_WPA;
+	} else if (bss->wpa) {
+		bss->ssid.security_policy = SECURITY_WPA_PSK;
+	} else if (bss->ieee802_1x) {
+		int cipher = WPA_CIPHER_NONE;
+		bss->ssid.security_policy = SECURITY_IEEE_802_1X;
+#ifdef CONFIG_WEP
+		bss->ssid.wep.default_len = bss->default_wep_key_len;
+		if (full_config && bss->default_wep_key_len) {
+			cipher = bss->default_wep_key_len >= 13 ?
+				WPA_CIPHER_WEP104 : WPA_CIPHER_WEP40;
+		} else if (full_config && bss->ssid.wep.keys_set) {
+			if (bss->ssid.wep.len[0] >= 13)
+				cipher = WPA_CIPHER_WEP104;
+			else
+				cipher = WPA_CIPHER_WEP40;
+		}
+#endif /* CONFIG_WEP */
+		bss->wpa_group = cipher;
+		bss->wpa_pairwise = cipher;
+		bss->rsn_pairwise = cipher;
+		if (full_config)
+			bss->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_NO_WPA;
+#ifdef CONFIG_WEP
+	} else if (bss->ssid.wep.keys_set) {
+		int cipher = WPA_CIPHER_WEP40;
+		if (bss->ssid.wep.len[0] >= 13)
+			cipher = WPA_CIPHER_WEP104;
+		bss->ssid.security_policy = SECURITY_STATIC_WEP;
+		bss->wpa_group = cipher;
+		bss->wpa_pairwise = cipher;
+		bss->rsn_pairwise = cipher;
+		if (full_config)
+			bss->wpa_key_mgmt = WPA_KEY_MGMT_NONE;
+#endif /* CONFIG_WEP */
+	} else if (bss->osen) {
+		bss->ssid.security_policy = SECURITY_OSEN;
+		bss->wpa_group = WPA_CIPHER_CCMP;
+		bss->wpa_pairwise = 0;
+		bss->rsn_pairwise = WPA_CIPHER_CCMP;
+	} else {
+		bss->ssid.security_policy = SECURITY_PLAINTEXT;
+		if (full_config) {
+			bss->wpa_group = WPA_CIPHER_NONE;
+			bss->wpa_pairwise = WPA_CIPHER_NONE;
+			bss->rsn_pairwise = WPA_CIPHER_NONE;
+			bss->wpa_key_mgmt = WPA_KEY_MGMT_NONE;
+		}
+	}
+}
+
+
+int hostapd_sae_pw_id_in_use(struct hostapd_bss_config *conf)
+{
+	int with_id = 0, without_id = 0;
+	struct sae_password_entry *pw;
+
+	if (conf->ssid.wpa_passphrase)
+		without_id = 1;
+
+	for (pw = conf->sae_passwords; pw; pw = pw->next) {
+		if (pw->identifier)
+			with_id = 1;
+		else
+			without_id = 1;
+		if (with_id && without_id)
+			break;
+	}
+
+	if (with_id && !without_id)
+		return 2;
+	return with_id;
+}
+
+
+bool hostapd_sae_pk_in_use(struct hostapd_bss_config *conf)
+{
+#ifdef CONFIG_SAE_PK
+	struct sae_password_entry *pw;
+
+	for (pw = conf->sae_passwords; pw; pw = pw->next) {
+		if (pw->pk)
+			return true;
+	}
+#endif /* CONFIG_SAE_PK */
+
+	return false;
+}
+
+
+#ifdef CONFIG_SAE_PK
+bool hostapd_sae_pk_exclusively(struct hostapd_bss_config *conf)
+{
+	bool with_pk = false;
+	struct sae_password_entry *pw;
+
+	if (conf->ssid.wpa_passphrase)
+		return false;
+
+	for (pw = conf->sae_passwords; pw; pw = pw->next) {
+		if (!pw->pk)
+			return false;
+		with_pk = true;
+	}
+
+	return with_pk;
+}
+#endif /* CONFIG_SAE_PK */
+
+
+int hostapd_acl_comp(const void *a, const void *b)
+{
+	const struct mac_acl_entry *aa = a;
+	const struct mac_acl_entry *bb = b;
+	return os_memcmp(aa->addr, bb->addr, sizeof(macaddr));
+}
+
+
+int hostapd_add_acl_maclist(struct mac_acl_entry **acl, int *num,
+			    int vlan_id, const u8 *addr)
+{
+	struct mac_acl_entry *newacl;
+
+	newacl = os_realloc_array(*acl, *num + 1, sizeof(**acl));
+	if (!newacl) {
+		wpa_printf(MSG_ERROR, "MAC list reallocation failed");
+		return -1;
+	}
+
+	*acl = newacl;
+	os_memcpy((*acl)[*num].addr, addr, ETH_ALEN);
+	os_memset(&(*acl)[*num].vlan_id, 0, sizeof((*acl)[*num].vlan_id));
+	(*acl)[*num].vlan_id.untagged = vlan_id;
+	(*acl)[*num].vlan_id.notempty = !!vlan_id;
+	(*num)++;
+
+	return 0;
+}
+
+
+void hostapd_remove_acl_mac(struct mac_acl_entry **acl, int *num,
+			    const u8 *addr)
+{
+	int i = 0;
+
+	while (i < *num) {
+		if (os_memcmp((*acl)[i].addr, addr, ETH_ALEN) == 0) {
+			os_remove_in_array(*acl, *num, sizeof(**acl), i);
+			(*num)--;
+		} else {
+			i++;
+		}
+	}
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_config.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_config.h
new file mode 100644
index 0000000..dc95316
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_config.h
@@ -0,0 +1,1329 @@
+/*
+ * hostapd / Configuration definitions and helpers functions
+ * Copyright (c) 2003-2022, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef HOSTAPD_CONFIG_H
+#define HOSTAPD_CONFIG_H
+
+#include "common/defs.h"
+#include "utils/list.h"
+#include "ip_addr.h"
+#include "common/wpa_common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "crypto/sha256.h"
+#include "wps/wps.h"
+#include "fst/fst.h"
+#include "vlan.h"
+
+enum macaddr_acl {
+	ACCEPT_UNLESS_DENIED = 0,
+	DENY_UNLESS_ACCEPTED = 1,
+	USE_EXTERNAL_RADIUS_AUTH = 2
+};
+
+/**
+ * mesh_conf - local MBSS state and settings
+ */
+struct mesh_conf {
+	u8 meshid[32];
+	u8 meshid_len;
+	/* Active Path Selection Protocol Identifier */
+	u8 mesh_pp_id;
+	/* Active Path Selection Metric Identifier */
+	u8 mesh_pm_id;
+	/* Congestion Control Mode Identifier */
+	u8 mesh_cc_id;
+	/* Synchronization Protocol Identifier */
+	u8 mesh_sp_id;
+	/* Authentication Protocol Identifier */
+	u8 mesh_auth_id;
+	u8 *rsn_ie;
+	int rsn_ie_len;
+#define MESH_CONF_SEC_NONE BIT(0)
+#define MESH_CONF_SEC_AUTH BIT(1)
+#define MESH_CONF_SEC_AMPE BIT(2)
+	unsigned int security;
+	enum mfp_options ieee80211w;
+	int ocv;
+	unsigned int pairwise_cipher;
+	unsigned int group_cipher;
+	unsigned int mgmt_group_cipher;
+	int dot11MeshMaxRetries;
+	int dot11MeshRetryTimeout; /* msec */
+	int dot11MeshConfirmTimeout; /* msec */
+	int dot11MeshHoldingTimeout; /* msec */
+	int mesh_fwding;
+};
+
+#define MAX_STA_COUNT 2007
+#define MAX_VLAN_ID 4094
+
+typedef u8 macaddr[ETH_ALEN];
+
+struct mac_acl_entry {
+	macaddr addr;
+	struct vlan_description vlan_id;
+};
+
+struct hostapd_radius_servers;
+struct ft_remote_r0kh;
+struct ft_remote_r1kh;
+
+#ifdef CONFIG_WEP
+#define NUM_WEP_KEYS 4
+struct hostapd_wep_keys {
+	u8 idx;
+	u8 *key[NUM_WEP_KEYS];
+	size_t len[NUM_WEP_KEYS];
+	int keys_set;
+	size_t default_len; /* key length used for dynamic key generation */
+};
+#endif /* CONFIG_WEP */
+
+typedef enum hostap_security_policy {
+	SECURITY_PLAINTEXT = 0,
+#ifdef CONFIG_WEP
+	SECURITY_STATIC_WEP = 1,
+#endif /* CONFIG_WEP */
+	SECURITY_IEEE_802_1X = 2,
+	SECURITY_WPA_PSK = 3,
+	SECURITY_WPA = 4,
+	SECURITY_OSEN = 5
+} secpolicy;
+
+struct hostapd_ssid {
+	u8 ssid[SSID_MAX_LEN];
+	size_t ssid_len;
+	u32 short_ssid;
+	unsigned int ssid_set:1;
+	unsigned int utf8_ssid:1;
+	unsigned int wpa_passphrase_set:1;
+	unsigned int wpa_psk_set:1;
+
+	char vlan[IFNAMSIZ + 1];
+	secpolicy security_policy;
+
+	struct hostapd_wpa_psk *wpa_psk;
+	char *wpa_passphrase;
+	char *wpa_psk_file;
+	struct sae_pt *pt;
+
+#ifdef CONFIG_WEP
+	struct hostapd_wep_keys wep;
+#endif /* CONFIG_WEP */
+
+#define DYNAMIC_VLAN_DISABLED 0
+#define DYNAMIC_VLAN_OPTIONAL 1
+#define DYNAMIC_VLAN_REQUIRED 2
+	int dynamic_vlan;
+	int vlan_no_bridge;
+#define DYNAMIC_VLAN_NAMING_WITHOUT_DEVICE 0
+#define DYNAMIC_VLAN_NAMING_WITH_DEVICE 1
+#define DYNAMIC_VLAN_NAMING_END 2
+	int vlan_naming;
+	int per_sta_vif;
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+	char *vlan_tagged_interface;
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+};
+
+
+#define VLAN_ID_WILDCARD -1
+
+struct hostapd_vlan {
+	struct hostapd_vlan *next;
+	int vlan_id; /* VLAN ID or -1 (VLAN_ID_WILDCARD) for wildcard entry */
+	struct vlan_description vlan_desc;
+	char ifname[IFNAMSIZ + 1];
+	char bridge[IFNAMSIZ + 1];
+	int configured;
+	int dynamic_vlan;
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+
+#define DVLAN_CLEAN_WLAN_PORT	0x8
+	int clean;
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+};
+
+#define PMK_LEN 32
+#define KEYID_LEN 32
+#define MIN_PASSPHRASE_LEN 8
+#define MAX_PASSPHRASE_LEN 63
+struct hostapd_sta_wpa_psk_short {
+	struct hostapd_sta_wpa_psk_short *next;
+	unsigned int is_passphrase:1;
+	u8 psk[PMK_LEN];
+	char passphrase[MAX_PASSPHRASE_LEN + 1];
+	int ref; /* (number of references held) - 1 */
+};
+
+struct hostapd_wpa_psk {
+	struct hostapd_wpa_psk *next;
+	int group;
+	char keyid[KEYID_LEN];
+	int wps;
+	u8 psk[PMK_LEN];
+	u8 addr[ETH_ALEN];
+	u8 p2p_dev_addr[ETH_ALEN];
+	int vlan_id;
+};
+
+struct hostapd_eap_user {
+	struct hostapd_eap_user *next;
+	u8 *identity;
+	size_t identity_len;
+	struct {
+		int vendor;
+		u32 method;
+	} methods[EAP_MAX_METHODS];
+	u8 *password;
+	size_t password_len;
+	u8 *salt;
+	size_t salt_len; /* non-zero when password is salted */
+	int phase2;
+	int force_version;
+	unsigned int wildcard_prefix:1;
+	unsigned int password_hash:1; /* whether password is hashed with
+				       * nt_password_hash() */
+	unsigned int remediation:1;
+	unsigned int macacl:1;
+	int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */
+	struct hostapd_radius_attr *accept_attr;
+	u32 t_c_timestamp;
+};
+
+struct hostapd_radius_attr {
+	u8 type;
+	struct wpabuf *val;
+	struct hostapd_radius_attr *next;
+};
+
+
+#define NUM_TX_QUEUES 4
+#define MAX_ROAMING_CONSORTIUM_LEN 15
+
+struct hostapd_roaming_consortium {
+	u8 len;
+	u8 oi[MAX_ROAMING_CONSORTIUM_LEN];
+};
+
+struct hostapd_lang_string {
+	u8 lang[3];
+	u8 name_len;
+	u8 name[252];
+};
+
+struct hostapd_venue_url {
+	u8 venue_number;
+	u8 url_len;
+	u8 url[254];
+};
+
+#define MAX_NAI_REALMS 10
+#define MAX_NAI_REALMLEN 255
+#define MAX_NAI_EAP_METHODS 5
+#define MAX_NAI_AUTH_TYPES 4
+struct hostapd_nai_realm_data {
+	u8 encoding;
+	char realm_buf[MAX_NAI_REALMLEN + 1];
+	char *realm[MAX_NAI_REALMS];
+	u8 eap_method_count;
+	struct hostapd_nai_realm_eap {
+		u8 eap_method;
+		u8 num_auths;
+		u8 auth_id[MAX_NAI_AUTH_TYPES];
+		u8 auth_val[MAX_NAI_AUTH_TYPES];
+	} eap_method[MAX_NAI_EAP_METHODS];
+};
+
+struct anqp_element {
+	struct dl_list list;
+	u16 infoid;
+	struct wpabuf *payload;
+};
+
+struct fils_realm {
+	struct dl_list list;
+	u8 hash[2];
+	char realm[];
+};
+
+struct sae_password_entry {
+	struct sae_password_entry *next;
+	char *password;
+	char *identifier;
+	u8 peer_addr[ETH_ALEN];
+	int vlan_id;
+	struct sae_pt *pt;
+	struct sae_pk *pk;
+};
+
+struct dpp_controller_conf {
+	struct dpp_controller_conf *next;
+	u8 pkhash[SHA256_MAC_LEN];
+	struct hostapd_ip_addr ipaddr;
+};
+
+struct airtime_sta_weight {
+	struct airtime_sta_weight *next;
+	unsigned int weight;
+	u8 addr[ETH_ALEN];
+};
+
+#define EXT_CAPA_MAX_LEN 15
+
+/**
+ * struct hostapd_bss_config - Per-BSS configuration
+ */
+struct hostapd_bss_config {
+	char iface[IFNAMSIZ + 1];
+	char bridge[IFNAMSIZ + 1];
+	char ft_iface[IFNAMSIZ + 1];
+	char snoop_iface[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;
+
+	unsigned int logger_syslog; /* module bitfield */
+	unsigned int logger_stdout; /* module bitfield */
+
+	int max_num_sta; /* maximum number of STAs in station table */
+
+	int dtim_period;
+	unsigned int bss_load_update_period;
+	unsigned int chan_util_avg_period;
+
+	int ieee802_1x; /* use IEEE 802.1X */
+	int eapol_version;
+	int eap_server; /* Use internal EAP server instead of external
+			 * RADIUS server */
+	struct hostapd_eap_user *eap_user;
+	char *eap_user_sqlite;
+	char *eap_sim_db;
+	unsigned int eap_sim_db_timeout;
+	int eap_server_erp; /* Whether ERP is enabled on internal EAP server */
+	struct hostapd_ip_addr own_ip_addr;
+	int dynamic_own_ip_addr;
+	char *nas_identifier;
+	struct hostapd_radius_servers *radius;
+	int acct_interim_interval;
+	int radius_request_cui;
+	struct hostapd_radius_attr *radius_auth_req_attr;
+	struct hostapd_radius_attr *radius_acct_req_attr;
+	char *radius_req_attr_sqlite;
+	int radius_das_port;
+	unsigned int radius_das_time_window;
+	int radius_das_require_event_timestamp;
+	int radius_das_require_message_authenticator;
+	struct hostapd_ip_addr radius_das_client_addr;
+	u8 *radius_das_shared_secret;
+	size_t radius_das_shared_secret_len;
+
+	struct hostapd_ssid ssid;
+
+	char *eap_req_id_text; /* optional displayable message sent with
+				* EAP Request-Identity */
+	size_t eap_req_id_text_len;
+	int eapol_key_index_workaround;
+
+#ifdef CONFIG_WEP
+	size_t default_wep_key_len;
+	int individual_wep_key_len;
+	int wep_rekeying_period;
+	int broadcast_key_idx_min, broadcast_key_idx_max;
+#endif /* CONFIG_WEP */
+	int eap_reauth_period;
+	int erp_send_reauth_start;
+	char *erp_domain;
+#ifdef CONFIG_TESTING_OPTIONS
+	bool eap_skip_prot_success;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	enum macaddr_acl macaddr_acl;
+	struct mac_acl_entry *accept_mac;
+	int num_accept_mac;
+	struct mac_acl_entry *deny_mac;
+	int num_deny_mac;
+	int wds_sta;
+	int isolate;
+	int start_disabled;
+
+	int auth_algs; /* bitfield of allowed IEEE 802.11 authentication
+			* algorithms, WPA_AUTH_ALG_{OPEN,SHARED,LEAP} */
+
+	int wpa; /* bitfield of WPA_PROTO_WPA, WPA_PROTO_RSN */
+	int extended_key_id;
+	int wpa_key_mgmt;
+	enum mfp_options ieee80211w;
+	int group_mgmt_cipher;
+	int beacon_prot;
+	/* dot11AssociationSAQueryMaximumTimeout (in TUs) */
+	unsigned int assoc_sa_query_max_timeout;
+	/* dot11AssociationSAQueryRetryTimeout (in TUs) */
+	int assoc_sa_query_retry_timeout;
+#ifdef CONFIG_OCV
+	int ocv; /* Operating Channel Validation */
+#endif /* CONFIG_OCV */
+	enum {
+		PSK_RADIUS_IGNORED = 0,
+		PSK_RADIUS_ACCEPTED = 1,
+		PSK_RADIUS_REQUIRED = 2,
+		PSK_RADIUS_DURING_4WAY_HS = 3,
+	} wpa_psk_radius;
+	int wpa_pairwise;
+	int group_cipher; /* wpa_group value override from configuation */
+	int wpa_group;
+	int wpa_group_rekey;
+	int wpa_group_rekey_set;
+	int wpa_strict_rekey;
+	int wpa_gmk_rekey;
+	int wpa_ptk_rekey;
+	enum ptk0_rekey_handling wpa_deny_ptk0_rekey;
+	u32 wpa_group_update_count;
+	u32 wpa_pairwise_update_count;
+	int wpa_disable_eapol_key_retries;
+	int rsn_pairwise;
+	int rsn_preauth;
+	char *rsn_preauth_interfaces;
+
+#ifdef CONFIG_IEEE80211R_AP
+	/* IEEE 802.11r - Fast BSS Transition */
+	u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
+	u8 r1_key_holder[FT_R1KH_ID_LEN];
+	u32 r0_key_lifetime; /* PMK-R0 lifetime seconds */
+	int rkh_pos_timeout;
+	int rkh_neg_timeout;
+	int rkh_pull_timeout; /* ms */
+	int rkh_pull_retries;
+	u32 reassociation_deadline;
+	struct ft_remote_r0kh *r0kh_list;
+	struct ft_remote_r1kh *r1kh_list;
+	int pmk_r1_push;
+	int ft_over_ds;
+	int ft_psk_generate_local;
+	int r1_max_key_lifetime;
+#endif /* CONFIG_IEEE80211R_AP */
+
+	char *ctrl_interface; /* directory for UNIX domain sockets */
+#ifndef CONFIG_NATIVE_WINDOWS
+	gid_t ctrl_interface_gid;
+#endif /* CONFIG_NATIVE_WINDOWS */
+	int ctrl_interface_gid_set;
+
+	char *ca_cert;
+	char *server_cert;
+	char *server_cert2;
+	char *private_key;
+	char *private_key2;
+	char *private_key_passwd;
+	char *private_key_passwd2;
+	char *check_cert_subject;
+	int check_crl;
+	int check_crl_strict;
+	unsigned int crl_reload_interval;
+	unsigned int tls_session_lifetime;
+	unsigned int tls_flags;
+	unsigned int max_auth_rounds;
+	unsigned int max_auth_rounds_short;
+	char *ocsp_stapling_response;
+	char *ocsp_stapling_response_multi;
+	char *dh_file;
+	char *openssl_ciphers;
+	char *openssl_ecdh_curves;
+	u8 *pac_opaque_encr_key;
+	u8 *eap_fast_a_id;
+	size_t eap_fast_a_id_len;
+	char *eap_fast_a_id_info;
+	int eap_fast_prov;
+	int pac_key_lifetime;
+	int pac_key_refresh_time;
+	int eap_teap_auth;
+	int eap_teap_pac_no_inner;
+	int eap_teap_separate_result;
+	int eap_teap_id;
+	int eap_teap_method_sequence;
+	int eap_sim_aka_result_ind;
+	int eap_sim_id;
+	char *imsi_privacy_key;
+	int tnc;
+	int fragment_size;
+	u16 pwd_group;
+
+	char *radius_server_clients;
+	int radius_server_auth_port;
+	int radius_server_acct_port;
+	int radius_server_ipv6;
+
+	int use_pae_group_addr; /* Whether to send EAPOL frames to PAE group
+				 * address instead of individual address
+				 * (for driver_wired.c).
+				 */
+
+	int ap_max_inactivity;
+	int ignore_broadcast_ssid;
+	int no_probe_resp_if_max_sta;
+
+	int wmm_enabled;
+	int wmm_uapsd;
+
+	struct hostapd_vlan *vlan;
+
+	macaddr bssid;
+
+	/*
+	 * Maximum listen interval that STAs can use when associating with this
+	 * BSS. If a STA tries to use larger value, the association will be
+	 * denied with status code 51.
+	 */
+	u16 max_listen_interval;
+
+	int disable_pmksa_caching;
+	int okc; /* Opportunistic Key Caching */
+
+	int wps_state;
+#ifdef CONFIG_WPS
+	int wps_independent;
+	int ap_setup_locked;
+	u8 uuid[16];
+	char *wps_pin_requests;
+	char *device_name;
+	char *manufacturer;
+	char *model_name;
+	char *model_number;
+	char *serial_number;
+	u8 device_type[WPS_DEV_TYPE_LEN];
+	char *config_methods;
+	u8 os_version[4];
+	char *ap_pin;
+	int skip_cred_build;
+	u8 *extra_cred;
+	size_t extra_cred_len;
+	int wps_cred_processing;
+	int wps_cred_add_sae;
+	int force_per_enrollee_psk;
+	u8 *ap_settings;
+	size_t ap_settings_len;
+	struct hostapd_ssid multi_ap_backhaul_ssid;
+	char *upnp_iface;
+	char *friendly_name;
+	char *manufacturer_url;
+	char *model_description;
+	char *model_url;
+	char *upc;
+	struct wpabuf *wps_vendor_ext[MAX_WPS_VENDOR_EXTENSIONS];
+	struct wpabuf *wps_application_ext;
+	int wps_nfc_pw_from_config;
+	int wps_nfc_dev_pw_id;
+	struct wpabuf *wps_nfc_dh_pubkey;
+	struct wpabuf *wps_nfc_dh_privkey;
+	struct wpabuf *wps_nfc_dev_pw;
+#endif /* CONFIG_WPS */
+	int pbc_in_m1;
+	char *server_id;
+
+#define P2P_ENABLED BIT(0)
+#define P2P_GROUP_OWNER BIT(1)
+#define P2P_GROUP_FORMATION BIT(2)
+#define P2P_MANAGE BIT(3)
+#define P2P_ALLOW_CROSS_CONNECTION BIT(4)
+	int p2p;
+#ifdef CONFIG_P2P
+	u8 ip_addr_go[4];
+	u8 ip_addr_mask[4];
+	u8 ip_addr_start[4];
+	u8 ip_addr_end[4];
+#endif /* CONFIG_P2P */
+
+	int disassoc_low_ack;
+	int skip_inactivity_poll;
+
+#define TDLS_PROHIBIT BIT(0)
+#define TDLS_PROHIBIT_CHAN_SWITCH BIT(1)
+	int tdls;
+	bool disable_11n;
+	bool disable_11ac;
+	bool disable_11ax;
+	bool disable_11be;
+
+	/* IEEE 802.11v */
+	int time_advertisement;
+	char *time_zone;
+	int wnm_sleep_mode;
+	int wnm_sleep_mode_no_keys;
+	int bss_transition;
+
+	/* IEEE 802.11u - Interworking */
+	int interworking;
+	int access_network_type;
+	int internet;
+	int asra;
+	int esr;
+	int uesa;
+	int venue_info_set;
+	u8 venue_group;
+	u8 venue_type;
+	u8 hessid[ETH_ALEN];
+
+	/* IEEE 802.11u - Roaming Consortium list */
+	unsigned int roaming_consortium_count;
+	struct hostapd_roaming_consortium *roaming_consortium;
+
+	/* IEEE 802.11u - Venue Name duples */
+	unsigned int venue_name_count;
+	struct hostapd_lang_string *venue_name;
+
+	/* Venue URL duples */
+	unsigned int venue_url_count;
+	struct hostapd_venue_url *venue_url;
+
+	/* IEEE 802.11u - Network Authentication Type */
+	u8 *network_auth_type;
+	size_t network_auth_type_len;
+
+	/* IEEE 802.11u - IP Address Type Availability */
+	u8 ipaddr_type_availability;
+	u8 ipaddr_type_configured;
+
+	/* IEEE 802.11u - 3GPP Cellular Network */
+	u8 *anqp_3gpp_cell_net;
+	size_t anqp_3gpp_cell_net_len;
+
+	/* IEEE 802.11u - Domain Name */
+	u8 *domain_name;
+	size_t domain_name_len;
+
+	unsigned int nai_realm_count;
+	struct hostapd_nai_realm_data *nai_realm_data;
+
+	struct dl_list anqp_elem; /* list of struct anqp_element */
+
+	u16 gas_comeback_delay;
+	size_t gas_frag_limit;
+	int gas_address3;
+
+	u8 qos_map_set[16 + 2 * 21];
+	unsigned int qos_map_set_len;
+
+	int osen;
+	int proxy_arp;
+	int na_mcast_to_ucast;
+
+#ifdef CONFIG_HS20
+	int hs20;
+	int hs20_release;
+	int disable_dgaf;
+	u16 anqp_domain_id;
+	unsigned int hs20_oper_friendly_name_count;
+	struct hostapd_lang_string *hs20_oper_friendly_name;
+	u8 *hs20_wan_metrics;
+	u8 *hs20_connection_capability;
+	size_t hs20_connection_capability_len;
+	u8 *hs20_operating_class;
+	u8 hs20_operating_class_len;
+	struct hs20_icon {
+		u16 width;
+		u16 height;
+		char language[3];
+		char type[256];
+		char name[256];
+		char file[256];
+	} *hs20_icons;
+	size_t hs20_icons_count;
+	u8 osu_ssid[SSID_MAX_LEN];
+	size_t osu_ssid_len;
+	struct hs20_osu_provider {
+		unsigned int friendly_name_count;
+		struct hostapd_lang_string *friendly_name;
+		char *server_uri;
+		int *method_list;
+		char **icons;
+		size_t icons_count;
+		char *osu_nai;
+		char *osu_nai2;
+		unsigned int service_desc_count;
+		struct hostapd_lang_string *service_desc;
+	} *hs20_osu_providers, *last_osu;
+	size_t hs20_osu_providers_count;
+	size_t hs20_osu_providers_nai_count;
+	char **hs20_operator_icon;
+	size_t hs20_operator_icon_count;
+	unsigned int hs20_deauth_req_timeout;
+	char *subscr_remediation_url;
+	u8 subscr_remediation_method;
+	char *hs20_sim_provisioning_url;
+	char *t_c_filename;
+	u32 t_c_timestamp;
+	char *t_c_server_url;
+#endif /* CONFIG_HS20 */
+
+	u8 wps_rf_bands; /* RF bands for WPS (WPS_RF_*) */
+
+#ifdef CONFIG_RADIUS_TEST
+	char *dump_msk_file;
+#endif /* CONFIG_RADIUS_TEST */
+
+	struct wpabuf *vendor_elements;
+	struct wpabuf *assocresp_elements;
+
+	unsigned int anti_clogging_threshold;
+	unsigned int sae_sync;
+	int sae_require_mfp;
+	int sae_confirm_immediate;
+	enum sae_pwe sae_pwe;
+	int *sae_groups;
+	struct sae_password_entry *sae_passwords;
+
+	char *wowlan_triggers; /* Wake-on-WLAN triggers */
+
+#ifdef CONFIG_TESTING_OPTIONS
+	u8 bss_load_test[5];
+	u8 bss_load_test_set;
+	struct wpabuf *own_ie_override;
+	int sae_reflection_attack;
+	int sae_commit_status;
+	int sae_pk_omit;
+	int sae_pk_password_check_skip;
+	struct wpabuf *sae_commit_override;
+	struct wpabuf *rsne_override_eapol;
+	struct wpabuf *rsnxe_override_eapol;
+	struct wpabuf *rsne_override_ft;
+	struct wpabuf *rsnxe_override_ft;
+	struct wpabuf *gtk_rsc_override;
+	struct wpabuf *igtk_rsc_override;
+	int no_beacon_rsnxe;
+	int skip_prune_assoc;
+	int ft_rsnxe_used;
+	unsigned int oci_freq_override_eapol_m3;
+	unsigned int oci_freq_override_eapol_g1;
+	unsigned int oci_freq_override_saquery_req;
+	unsigned int oci_freq_override_saquery_resp;
+	unsigned int oci_freq_override_ft_assoc;
+	unsigned int oci_freq_override_fils_assoc;
+	unsigned int oci_freq_override_wnm_sleep;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#define MESH_ENABLED BIT(0)
+	int mesh;
+	int mesh_fwding;
+
+	u8 radio_measurements[RRM_CAPABILITIES_IE_LEN];
+
+	int vendor_vht;
+	int use_sta_nsts;
+
+	char *no_probe_resp_if_seen_on;
+	char *no_auth_if_seen_on;
+
+	int pbss;
+
+#ifdef CONFIG_MBO
+	int mbo_enabled;
+	/**
+	 * oce - Enable OCE in AP and/or STA-CFON mode
+	 *  - BIT(0) is Reserved
+	 *  - Set BIT(1) to enable OCE in STA-CFON mode
+	 *  - Set BIT(2) to enable OCE in AP mode
+	 */
+	unsigned int oce;
+	int mbo_cell_data_conn_pref;
+#endif /* CONFIG_MBO */
+
+	int ftm_responder;
+	int ftm_initiator;
+
+#ifdef CONFIG_FILS
+	u8 fils_cache_id[FILS_CACHE_ID_LEN];
+	int fils_cache_id_set;
+	struct dl_list fils_realms; /* list of struct fils_realm */
+	int fils_dh_group;
+	struct hostapd_ip_addr dhcp_server;
+	int dhcp_rapid_commit_proxy;
+	unsigned int fils_hlp_wait_time;
+	u16 dhcp_server_port;
+	u16 dhcp_relay_port;
+	u32 fils_discovery_min_int;
+	u32 fils_discovery_max_int;
+#endif /* CONFIG_FILS */
+
+	int multicast_to_unicast;
+	int bridge_multicast_to_unicast;
+
+	int broadcast_deauth;
+
+	int notify_mgmt_frames;
+
+#ifdef CONFIG_DPP
+	char *dpp_name;
+	char *dpp_mud_url;
+	char *dpp_extra_conf_req_name;
+	char *dpp_extra_conf_req_value;
+	char *dpp_connector;
+	struct wpabuf *dpp_netaccesskey;
+	unsigned int dpp_netaccesskey_expiry;
+	struct wpabuf *dpp_csign;
+#ifdef CONFIG_DPP2
+	struct dpp_controller_conf *dpp_controller;
+	int dpp_relay_port;
+	int dpp_configurator_connectivity;
+	int dpp_pfs;
+#endif /* CONFIG_DPP2 */
+#endif /* CONFIG_DPP */
+
+#ifdef CONFIG_OWE
+	macaddr owe_transition_bssid;
+	u8 owe_transition_ssid[SSID_MAX_LEN];
+	size_t owe_transition_ssid_len;
+	char owe_transition_ifname[IFNAMSIZ + 1];
+	int *owe_groups;
+	int owe_ptk_workaround;
+#endif /* CONFIG_OWE */
+
+	int coloc_intf_reporting;
+
+	u8 send_probe_response;
+
+	u8 transition_disable;
+
+#define BACKHAUL_BSS 1
+#define FRONTHAUL_BSS 2
+	int multi_ap; /* bitmap of BACKHAUL_BSS, FRONTHAUL_BSS */
+
+#ifdef CONFIG_AIRTIME_POLICY
+	unsigned int airtime_weight;
+	int airtime_limit;
+	struct airtime_sta_weight *airtime_weight_list;
+#endif /* CONFIG_AIRTIME_POLICY */
+
+#ifdef CONFIG_MACSEC
+	/**
+	 * macsec_policy - Determines the policy for MACsec secure session
+	 *
+	 * 0: MACsec not in use (default)
+	 * 1: MACsec enabled - Should secure, accept key server's advice to
+	 *    determine whether to use a secure session or not.
+	 */
+	int macsec_policy;
+
+	/**
+	 * macsec_integ_only - Determines how MACsec are transmitted
+	 *
+	 * This setting applies only when MACsec is in use, i.e.,
+	 *  - macsec_policy is enabled
+	 *  - the key server has decided to enable MACsec
+	 *
+	 * 0: Encrypt traffic (default)
+	 * 1: Integrity only
+	 */
+	int macsec_integ_only;
+
+	/**
+	 * macsec_replay_protect - Enable MACsec replay protection
+	 *
+	 * This setting applies only when MACsec is in use, i.e.,
+	 *  - macsec_policy is enabled
+	 *  - the key server has decided to enable MACsec
+	 *
+	 * 0: Replay protection disabled (default)
+	 * 1: Replay protection enabled
+	 */
+	int macsec_replay_protect;
+
+	/**
+	 * macsec_replay_window - MACsec replay protection window
+	 *
+	 * A window in which replay is tolerated, to allow receipt of frames
+	 * that have been misordered by the network.
+	 *
+	 * This setting applies only when MACsec replay protection active, i.e.,
+	 *  - macsec_replay_protect is enabled
+	 *  - the key server has decided to enable MACsec
+	 *
+	 * 0: No replay window, strict check (default)
+	 * 1..2^32-1: number of packets that could be misordered
+	 */
+	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.
+	 *
+	 * Range: 1-65534 (default: 1)
+	 */
+	int macsec_port;
+
+	/**
+	 * mka_priority - Priority of MKA Actor
+	 *
+	 * Range: 0-255 (default: 255)
+	 */
+	int mka_priority;
+
+	/**
+	 * macsec_csindex - Cipher suite index for MACsec
+	 *
+	 * Range: 0-1 (default: 0)
+	 */
+	int macsec_csindex;
+
+	/**
+	 * mka_ckn - MKA pre-shared CKN
+	 */
+#define MACSEC_CKN_MAX_LEN 32
+	size_t mka_ckn_len;
+	u8 mka_ckn[MACSEC_CKN_MAX_LEN];
+
+	/**
+	 * mka_cak - MKA pre-shared CAK
+	 */
+#define MACSEC_CAK_MAX_LEN 32
+	size_t mka_cak_len;
+	u8 mka_cak[MACSEC_CAK_MAX_LEN];
+
+#define MKA_PSK_SET_CKN BIT(0)
+#define MKA_PSK_SET_CAK BIT(1)
+#define MKA_PSK_SET (MKA_PSK_SET_CKN | MKA_PSK_SET_CAK)
+	/**
+	 * mka_psk_set - Whether mka_ckn and mka_cak are set
+	 */
+	u8 mka_psk_set;
+#endif /* CONFIG_MACSEC */
+
+#ifdef CONFIG_PASN
+	/* Whether to allow PASN-UNAUTH */
+	int pasn_noauth;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	/*
+	 * Normally, KDK should be derived if and only if both sides support
+	 * secure LTF. Allow forcing KDK derivation for testing purposes.
+	 */
+	int force_kdk_derivation;
+
+	/* If set, corrupt the MIC in the 2nd Authentication frame of PASN */
+	int pasn_corrupt_mic;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	int *pasn_groups;
+
+	/*
+	 * The time in TUs after which the non-AP STA is requested to retry the
+	 * PASN authentication in case there are too many parallel operations.
+	 */
+	u16 pasn_comeback_after;
+#endif /* CONFIG_PASN */
+
+	unsigned int unsol_bcast_probe_resp_interval;
+
+	u8 ext_capa_mask[EXT_CAPA_MAX_LEN];
+	u8 ext_capa[EXT_CAPA_MAX_LEN];
+
+	u8 rnr;
+	char *config_id;
+	bool xrates_supported;
+
+#ifdef CONFIG_IEEE80211BE
+	/* The AP is part of an AP MLD */
+	u8 mld_ap;
+
+	/* The MLD ID to which the AP MLD is affiliated with */
+	u8 mld_id;
+#endif /* CONFIG_IEEE80211BE */
+};
+
+/**
+ * struct he_phy_capabilities_info - HE PHY capabilities
+ */
+struct he_phy_capabilities_info {
+	bool he_su_beamformer;
+	bool he_su_beamformee;
+	bool he_mu_beamformer;
+};
+
+/**
+ * struct he_operation - HE operation
+ */
+struct he_operation {
+	u8 he_bss_color;
+	u8 he_bss_color_disabled;
+	u8 he_bss_color_partial;
+	u8 he_default_pe_duration;
+	u8 he_twt_required;
+	u8 he_twt_responder;
+	u16 he_rts_threshold;
+	u8 he_er_su_disable;
+	u16 he_basic_mcs_nss_set;
+};
+
+/**
+ * struct spatial_reuse - Spatial reuse
+ */
+struct spatial_reuse {
+	u8 sr_control;
+	u8 non_srg_obss_pd_max_offset;
+	u8 srg_obss_pd_min_offset;
+	u8 srg_obss_pd_max_offset;
+	u8 srg_bss_color_bitmap[8];
+	u8 srg_partial_bssid_bitmap[8];
+};
+
+/**
+ * struct eht_phy_capabilities_info - EHT PHY capabilities
+ */
+struct eht_phy_capabilities_info {
+	bool su_beamformer;
+	bool su_beamformee;
+	bool mu_beamformer;
+};
+
+/**
+ * struct hostapd_config - Per-radio interface configuration
+ */
+struct hostapd_config {
+	struct hostapd_bss_config **bss, *last_bss;
+	size_t num_bss;
+	char *config_id;
+
+	u16 beacon_int;
+	int rts_threshold;
+	int fragm_threshold;
+	u8 op_class;
+	u8 channel;
+	int enable_edmg;
+	u8 edmg_channel;
+	u8 acs;
+	struct wpa_freq_range_list acs_ch_list;
+	struct wpa_freq_range_list acs_freq_list;
+	u8 acs_freq_list_present;
+	int acs_exclude_dfs;
+	u8 min_tx_power;
+	enum hostapd_hw_mode hw_mode; /* HOSTAPD_MODE_IEEE80211A, .. */
+	bool hw_mode_set;
+	int acs_exclude_6ghz_non_psc;
+	int enable_background_radar;
+	enum {
+		LONG_PREAMBLE = 0,
+		SHORT_PREAMBLE = 1
+	} preamble;
+
+	int *supported_rates;
+	int *basic_rates;
+	unsigned int beacon_rate;
+	enum beacon_rate_type rate_type;
+
+	const struct wpa_driver_ops *driver;
+	char *driver_params;
+
+	int ap_table_max_size;
+	int ap_table_expiration_time;
+
+	unsigned int track_sta_max_num;
+	unsigned int track_sta_max_age;
+
+	int max_num_sta;
+
+	char country[3]; /* first two octets: country code as described in
+			  * ISO/IEC 3166-1. Third octet:
+			  * ' ' (ascii 32): all environments
+			  * 'O': Outdoor environemnt only
+			  * 'I': Indoor environment only
+			  * 'X': Used with noncountry entity ("XXX")
+			  * 0x00..0x31: identifying IEEE 802.11 standard
+			  *	Annex E table (0x04 = global table)
+			  */
+
+	int ieee80211d;
+
+	int ieee80211h; /* DFS */
+
+	/*
+	 * Local power constraint is an octet encoded as an unsigned integer in
+	 * units of decibels. Invalid value -1 indicates that Power Constraint
+	 * element will not be added.
+	 */
+	int local_pwr_constraint;
+
+	/* Control Spectrum Management bit */
+	int spectrum_mgmt_required;
+
+	struct hostapd_tx_queue_params tx_queue[NUM_TX_QUEUES];
+
+	/*
+	 * WMM AC parameters, in same order as 802.1D, i.e.
+	 * 0 = BE (best effort)
+	 * 1 = BK (background)
+	 * 2 = VI (video)
+	 * 3 = VO (voice)
+	 */
+	struct hostapd_wmm_ac_params wmm_ac_params[4];
+#ifdef CONFIG_DFS_CAC_TIMER
+	/* Yewei, add dfs cac timer in seconds, 20240118 */
+	int dfs_cac_s;
+#endif /* CONFIG_DFS_CAC_TIMER */
+	int ht_op_mode_fixed;
+	u16 ht_capab;
+	int noscan;
+	int no_ht_coex;
+	int ieee80211n;
+	int secondary_channel;
+	int no_pri_sec_switch;
+	int require_ht;
+	int obss_interval;
+	u32 vht_capab;
+	int ieee80211ac;
+	int require_vht;
+	enum oper_chan_width vht_oper_chwidth;
+	u8 vht_oper_centr_freq_seg0_idx;
+	u8 vht_oper_centr_freq_seg1_idx;
+	u8 ht40_plus_minus_allowed;
+
+#ifdef CONFIG_24G_BW_SWITCH
+	/* yewei, add for enable 20M/40M switch, 20240118 */
+	int auto_20m_40m;
+#endif /* CONFIG_24G_BW_SWITCH */
+#ifdef CONFIG_5G_BW_SWITCH
+	// Add for auto 20M/40M/80M bandwidth switch. Added by Liangyu Chu
+	int auto_80m;
+#endif /* CONFIG_5G_BW_SWITCH */
+
+	/* Use driver-generated interface addresses when adding multiple BSSs */
+	u8 use_driver_iface_addr;
+
+#ifdef CONFIG_FST
+	struct fst_iface_cfg fst_cfg;
+#endif /* CONFIG_FST */
+
+#ifdef CONFIG_P2P
+	u8 p2p_go_ctwindow;
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_TESTING_OPTIONS
+	double ignore_probe_probability;
+	double ignore_auth_probability;
+	double ignore_assoc_probability;
+	double ignore_reassoc_probability;
+	double corrupt_gtk_rekey_mic_probability;
+	int ecsa_ie_only;
+	bool delay_eapol_tx;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#ifdef CONFIG_ACS
+	unsigned int acs_num_scans;
+	struct acs_bias {
+		int channel;
+		double bias;
+	} *acs_chan_bias;
+	unsigned int num_acs_chan_bias;
+#endif /* CONFIG_ACS */
+
+	struct wpabuf *lci;
+	struct wpabuf *civic;
+	int stationary_ap;
+
+	int ieee80211ax;
+#ifdef CONFIG_IEEE80211AX
+	struct he_phy_capabilities_info he_phy_capab;
+	struct he_operation he_op;
+	struct ieee80211_he_mu_edca_parameter_set he_mu_edca;
+	struct spatial_reuse spr;
+	enum oper_chan_width he_oper_chwidth;
+	u8 he_oper_centr_freq_seg0_idx;
+	u8 he_oper_centr_freq_seg1_idx;
+	u8 he_6ghz_max_mpdu;
+	u8 he_6ghz_max_ampdu_len_exp;
+	u8 he_6ghz_rx_ant_pat;
+	u8 he_6ghz_tx_ant_pat;
+	u8 he_6ghz_reg_pwr_type;
+	bool require_he;
+#endif /* CONFIG_IEEE80211AX */
+
+	/* VHT enable/disable config from CHAN_SWITCH */
+#define CH_SWITCH_VHT_ENABLED BIT(0)
+#define CH_SWITCH_VHT_DISABLED BIT(1)
+	unsigned int ch_switch_vht_config;
+
+	/* HE enable/disable config from CHAN_SWITCH */
+#define CH_SWITCH_HE_ENABLED BIT(0)
+#define CH_SWITCH_HE_DISABLED BIT(1)
+	unsigned int ch_switch_he_config;
+
+	int rssi_reject_assoc_rssi;
+	int rssi_reject_assoc_timeout;
+	int rssi_ignore_probe_request;
+
+#ifdef CONFIG_AIRTIME_POLICY
+	enum {
+		AIRTIME_MODE_OFF = 0,
+		AIRTIME_MODE_STATIC = 1,
+		AIRTIME_MODE_DYNAMIC = 2,
+		AIRTIME_MODE_LIMIT = 3,
+		__AIRTIME_MODE_MAX,
+	} airtime_mode;
+	unsigned int airtime_update_interval;
+#define AIRTIME_MODE_MAX (__AIRTIME_MODE_MAX - 1)
+#endif /* CONFIG_AIRTIME_POLICY */
+
+	int ieee80211be;
+#ifdef CONFIG_IEEE80211BE
+	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 */
+#define CH_SWITCH_EHT_ENABLED BIT(0)
+#define CH_SWITCH_EHT_DISABLED BIT(1)
+	unsigned int ch_switch_eht_config;
+
+	enum mbssid {
+		MBSSID_DISABLED = 0,
+		MBSSID_ENABLED = 1,
+		ENHANCED_MBSSID_ENABLED = 2,
+	} mbssid;
+};
+
+
+static inline enum oper_chan_width
+hostapd_get_oper_chwidth(struct hostapd_config *conf)
+{
+#ifdef CONFIG_IEEE80211BE
+	if (conf->ieee80211be)
+		return conf->eht_oper_chwidth;
+#endif /* CONFIG_IEEE80211BE */
+#ifdef CONFIG_IEEE80211AX
+	if (conf->ieee80211ax)
+		return conf->he_oper_chwidth;
+#endif /* CONFIG_IEEE80211AX */
+	return conf->vht_oper_chwidth;
+}
+
+static inline void
+hostapd_set_oper_chwidth(struct hostapd_config *conf,
+			 enum oper_chan_width oper_chwidth)
+{
+#ifdef CONFIG_IEEE80211BE
+	if (conf->ieee80211be)
+		conf->eht_oper_chwidth = oper_chwidth;
+	if (oper_chwidth == CONF_OPER_CHWIDTH_320MHZ)
+		oper_chwidth = CONF_OPER_CHWIDTH_160MHZ;
+#endif /* CONFIG_IEEE80211BE */
+#ifdef CONFIG_IEEE80211AX
+	if (conf->ieee80211ax)
+		conf->he_oper_chwidth = oper_chwidth;
+#endif /* CONFIG_IEEE80211AX */
+	conf->vht_oper_chwidth = oper_chwidth;
+}
+
+static inline u8
+hostapd_get_oper_centr_freq_seg0_idx(struct hostapd_config *conf)
+{
+#ifdef CONFIG_IEEE80211BE
+	if (conf->ieee80211be)
+		return conf->eht_oper_centr_freq_seg0_idx;
+#endif /* CONFIG_IEEE80211BE */
+#ifdef CONFIG_IEEE80211AX
+	if (conf->ieee80211ax)
+		return conf->he_oper_centr_freq_seg0_idx;
+#endif /* CONFIG_IEEE80211AX */
+	return conf->vht_oper_centr_freq_seg0_idx;
+}
+
+static inline void
+hostapd_set_oper_centr_freq_seg0_idx(struct hostapd_config *conf,
+				     u8 oper_centr_freq_seg0_idx)
+{
+#ifdef CONFIG_IEEE80211BE
+	if (conf->ieee80211be)
+		conf->eht_oper_centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
+	if (center_idx_to_bw_6ghz(oper_centr_freq_seg0_idx) == 4)
+		oper_centr_freq_seg0_idx +=
+			conf->channel > oper_centr_freq_seg0_idx ? 16 : -16;
+#endif /* CONFIG_IEEE80211BE */
+#ifdef CONFIG_IEEE80211AX
+	if (conf->ieee80211ax)
+		conf->he_oper_centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
+#endif /* CONFIG_IEEE80211AX */
+	conf->vht_oper_centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
+}
+
+static inline u8
+hostapd_get_oper_centr_freq_seg1_idx(struct hostapd_config *conf)
+{
+#ifdef CONFIG_IEEE80211AX
+	if (conf->ieee80211ax)
+		return conf->he_oper_centr_freq_seg1_idx;
+#endif /* CONFIG_IEEE80211AX */
+	return conf->vht_oper_centr_freq_seg1_idx;
+}
+
+static inline void
+hostapd_set_oper_centr_freq_seg1_idx(struct hostapd_config *conf,
+				     u8 oper_centr_freq_seg1_idx)
+{
+#ifdef CONFIG_IEEE80211AX
+	if (conf->ieee80211ax)
+		conf->he_oper_centr_freq_seg1_idx = oper_centr_freq_seg1_idx;
+#endif /* CONFIG_IEEE80211AX */
+	conf->vht_oper_centr_freq_seg1_idx = oper_centr_freq_seg1_idx;
+}
+
+
+int hostapd_mac_comp(const void *a, const void *b);
+struct hostapd_config * hostapd_config_defaults(void);
+void hostapd_config_defaults_bss(struct hostapd_bss_config *bss);
+void hostapd_config_free_radius_attr(struct hostapd_radius_attr *attr);
+void hostapd_config_free_eap_user(struct hostapd_eap_user *user);
+void hostapd_config_free_eap_users(struct hostapd_eap_user *user);
+void hostapd_config_clear_wpa_psk(struct hostapd_wpa_psk **p);
+void hostapd_config_free_bss(struct hostapd_bss_config *conf);
+void hostapd_config_free(struct hostapd_config *conf);
+int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries,
+			  const u8 *addr, struct vlan_description *vlan_id);
+int hostapd_rate_found(int *list, int rate);
+const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
+			   const u8 *addr, const u8 *p2p_dev_addr,
+			   const u8 *prev_psk, int *vlan_id);
+int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf);
+int hostapd_vlan_valid(struct hostapd_vlan *vlan,
+		       struct vlan_description *vlan_desc);
+const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan,
+					int vlan_id);
+struct hostapd_radius_attr *
+hostapd_config_get_radius_attr(struct hostapd_radius_attr *attr, u8 type);
+struct hostapd_radius_attr * hostapd_parse_radius_attr(const char *value);
+int hostapd_config_check(struct hostapd_config *conf, int full_config);
+void hostapd_set_security_params(struct hostapd_bss_config *bss,
+				 int full_config);
+int hostapd_sae_pw_id_in_use(struct hostapd_bss_config *conf);
+bool hostapd_sae_pk_in_use(struct hostapd_bss_config *conf);
+bool hostapd_sae_pk_exclusively(struct hostapd_bss_config *conf);
+int hostapd_setup_sae_pt(struct hostapd_bss_config *conf);
+int hostapd_acl_comp(const void *a, const void *b);
+int hostapd_add_acl_maclist(struct mac_acl_entry **acl, int *num,
+			    int vlan_id, const u8 *addr);
+void hostapd_remove_acl_mac(struct mac_acl_entry **acl, int *num,
+			    const u8 *addr);
+
+#endif /* HOSTAPD_CONFIG_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_drv_ops.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_drv_ops.c
new file mode 100644
index 0000000..75ddfa1
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_drv_ops.c
@@ -0,0 +1,1139 @@
+/*
+ * hostapd - Driver operations
+ * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/hw_features_common.h"
+#include "wps/wps.h"
+#include "p2p/p2p.h"
+#include "hostapd.h"
+#include "ieee802_11.h"
+#include "sta_info.h"
+#include "ap_config.h"
+#include "p2p_hostapd.h"
+#include "hs20.h"
+#include "wpa_auth.h"
+#include "ap_drv_ops.h"
+
+
+u32 hostapd_sta_flags_to_drv(u32 flags)
+{
+	int res = 0;
+	if (flags & WLAN_STA_AUTHORIZED)
+		res |= WPA_STA_AUTHORIZED;
+	if (flags & WLAN_STA_WMM)
+		res |= WPA_STA_WMM;
+	if (flags & WLAN_STA_SHORT_PREAMBLE)
+		res |= WPA_STA_SHORT_PREAMBLE;
+	if (flags & WLAN_STA_MFP)
+		res |= WPA_STA_MFP;
+	if (flags & WLAN_STA_AUTH)
+		res |= WPA_STA_AUTHENTICATED;
+	if (flags & WLAN_STA_ASSOC)
+		res |= WPA_STA_ASSOCIATED;
+	return res;
+}
+
+
+static int add_buf(struct wpabuf **dst, const struct wpabuf *src)
+{
+	if (!src)
+		return 0;
+	if (wpabuf_resize(dst, wpabuf_len(src)) != 0)
+		return -1;
+	wpabuf_put_buf(*dst, src);
+	return 0;
+}
+
+
+static int add_buf_data(struct wpabuf **dst, const u8 *data, size_t len)
+{
+	if (!data || !len)
+		return 0;
+	if (wpabuf_resize(dst, len) != 0)
+		return -1;
+	wpabuf_put_data(*dst, data, len);
+	return 0;
+}
+
+
+int hostapd_build_ap_extra_ies(struct hostapd_data *hapd,
+			       struct wpabuf **beacon_ret,
+			       struct wpabuf **proberesp_ret,
+			       struct wpabuf **assocresp_ret)
+{
+	struct wpabuf *beacon = NULL, *proberesp = NULL, *assocresp = NULL;
+	u8 buf[200], *pos;
+
+	*beacon_ret = *proberesp_ret = *assocresp_ret = NULL;
+
+#ifdef NEED_AP_MLME
+	pos = buf;
+	pos = hostapd_eid_rm_enabled_capab(hapd, pos, sizeof(buf));
+	if (add_buf_data(&assocresp, buf, pos - buf) < 0 ||
+	    add_buf_data(&proberesp, buf, pos - buf) < 0)
+		goto fail;
+#endif /* NEED_AP_MLME */
+
+	pos = buf;
+	pos = hostapd_eid_time_adv(hapd, pos);
+	if (add_buf_data(&beacon, buf, pos - buf) < 0)
+		goto fail;
+	pos = hostapd_eid_time_zone(hapd, pos);
+	if (add_buf_data(&proberesp, buf, pos - buf) < 0)
+		goto fail;
+
+	pos = buf;
+	pos = hostapd_eid_ext_capab(hapd, pos, false);
+	if (add_buf_data(&assocresp, buf, pos - buf) < 0)
+		goto fail;
+	pos = hostapd_eid_interworking(hapd, pos);
+	pos = hostapd_eid_adv_proto(hapd, pos);
+	pos = hostapd_eid_roaming_consortium(hapd, pos);
+	if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
+	    add_buf_data(&proberesp, buf, pos - buf) < 0)
+		goto fail;
+
+#ifdef CONFIG_FST
+	if (add_buf(&beacon, hapd->iface->fst_ies) < 0 ||
+	    add_buf(&proberesp, hapd->iface->fst_ies) < 0 ||
+	    add_buf(&assocresp, hapd->iface->fst_ies) < 0)
+		goto fail;
+#endif /* CONFIG_FST */
+
+#ifdef CONFIG_FILS
+	pos = hostapd_eid_fils_indic(hapd, buf, 0);
+	if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
+	    add_buf_data(&proberesp, buf, pos - buf) < 0)
+		goto fail;
+#endif /* CONFIG_FILS */
+
+	pos = hostapd_eid_rsnxe(hapd, buf, sizeof(buf));
+	if (add_buf_data(&assocresp, buf, pos - buf) < 0)
+		goto fail;
+
+	if (add_buf(&beacon, hapd->wps_beacon_ie) < 0 ||
+	    add_buf(&proberesp, hapd->wps_probe_resp_ie) < 0)
+		goto fail;
+
+#ifdef CONFIG_P2P
+	if (add_buf(&beacon, hapd->p2p_beacon_ie) < 0 ||
+	    add_buf(&proberesp, hapd->p2p_probe_resp_ie) < 0)
+		goto fail;
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_P2P_MANAGER
+	if (hapd->conf->p2p & P2P_MANAGE) {
+		if (wpabuf_resize(&beacon, 100) == 0) {
+			u8 *start, *p;
+			start = wpabuf_put(beacon, 0);
+			p = hostapd_eid_p2p_manage(hapd, start);
+			wpabuf_put(beacon, p - start);
+		}
+
+		if (wpabuf_resize(&proberesp, 100) == 0) {
+			u8 *start, *p;
+			start = wpabuf_put(proberesp, 0);
+			p = hostapd_eid_p2p_manage(hapd, start);
+			wpabuf_put(proberesp, p - start);
+		}
+	}
+#endif /* CONFIG_P2P_MANAGER */
+
+#ifdef CONFIG_WPS
+	if (hapd->conf->wps_state) {
+		struct wpabuf *a = wps_build_assoc_resp_ie();
+		add_buf(&assocresp, a);
+		wpabuf_free(a);
+	}
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_P2P_MANAGER
+	if (hapd->conf->p2p & P2P_MANAGE) {
+		if (wpabuf_resize(&assocresp, 100) == 0) {
+			u8 *start, *p;
+			start = wpabuf_put(assocresp, 0);
+			p = hostapd_eid_p2p_manage(hapd, start);
+			wpabuf_put(assocresp, p - start);
+		}
+	}
+#endif /* CONFIG_P2P_MANAGER */
+
+#ifdef CONFIG_WIFI_DISPLAY
+	if (hapd->p2p_group) {
+		struct wpabuf *a;
+		a = p2p_group_assoc_resp_ie(hapd->p2p_group, P2P_SC_SUCCESS);
+		add_buf(&assocresp, a);
+		wpabuf_free(a);
+	}
+#endif /* CONFIG_WIFI_DISPLAY */
+
+#ifdef CONFIG_HS20
+	pos = hostapd_eid_hs20_indication(hapd, buf);
+	if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
+	    add_buf_data(&proberesp, buf, pos - buf) < 0)
+		goto fail;
+
+	pos = hostapd_eid_osen(hapd, buf);
+	if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
+	    add_buf_data(&proberesp, buf, pos - buf) < 0)
+		goto fail;
+#endif /* CONFIG_HS20 */
+
+#ifdef CONFIG_MBO
+	if (hapd->conf->mbo_enabled ||
+	    OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd)) {
+		pos = hostapd_eid_mbo(hapd, buf, sizeof(buf));
+		if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
+		    add_buf_data(&proberesp, buf, pos - buf) < 0 ||
+		    add_buf_data(&assocresp, buf, pos - buf) < 0)
+			goto fail;
+	}
+#endif /* CONFIG_MBO */
+
+#ifdef CONFIG_OWE
+	pos = hostapd_eid_owe_trans(hapd, buf, sizeof(buf));
+	if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
+	    add_buf_data(&proberesp, buf, pos - buf) < 0)
+		goto fail;
+#endif /* CONFIG_OWE */
+
+	add_buf(&beacon, hapd->conf->vendor_elements);
+	add_buf(&proberesp, hapd->conf->vendor_elements);
+	add_buf(&assocresp, hapd->conf->assocresp_elements);
+
+	*beacon_ret = beacon;
+	*proberesp_ret = proberesp;
+	*assocresp_ret = assocresp;
+
+	return 0;
+
+fail:
+	wpabuf_free(beacon);
+	wpabuf_free(proberesp);
+	wpabuf_free(assocresp);
+	return -1;
+}
+
+
+void hostapd_free_ap_extra_ies(struct hostapd_data *hapd,
+			       struct wpabuf *beacon,
+			       struct wpabuf *proberesp,
+			       struct wpabuf *assocresp)
+{
+	wpabuf_free(beacon);
+	wpabuf_free(proberesp);
+	wpabuf_free(assocresp);
+}
+
+
+int hostapd_reset_ap_wps_ie(struct hostapd_data *hapd)
+{
+	if (hapd->driver == NULL || hapd->driver->set_ap_wps_ie == NULL)
+		return 0;
+
+	return hapd->driver->set_ap_wps_ie(hapd->drv_priv, NULL, NULL, NULL);
+}
+
+
+int hostapd_set_ap_wps_ie(struct hostapd_data *hapd)
+{
+	struct wpabuf *beacon, *proberesp, *assocresp;
+	int ret;
+
+	if (hapd->driver == NULL || hapd->driver->set_ap_wps_ie == NULL)
+		return 0;
+
+	if (hostapd_build_ap_extra_ies(hapd, &beacon, &proberesp, &assocresp) <
+	    0)
+		return -1;
+
+	ret = hapd->driver->set_ap_wps_ie(hapd->drv_priv, beacon, proberesp,
+					  assocresp);
+
+	hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp);
+
+	return ret;
+}
+
+
+int hostapd_set_authorized(struct hostapd_data *hapd,
+			   struct sta_info *sta, int authorized)
+{
+	if (authorized) {
+		return hostapd_sta_set_flags(hapd, sta->addr,
+					     hostapd_sta_flags_to_drv(
+						     sta->flags),
+					     WPA_STA_AUTHORIZED, ~0);
+	}
+
+	return hostapd_sta_set_flags(hapd, sta->addr,
+				     hostapd_sta_flags_to_drv(sta->flags),
+				     0, ~WPA_STA_AUTHORIZED);
+}
+
+
+int hostapd_set_sta_flags(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	int set_flags, total_flags, flags_and, flags_or;
+	total_flags = hostapd_sta_flags_to_drv(sta->flags);
+	set_flags = WPA_STA_SHORT_PREAMBLE | WPA_STA_WMM | WPA_STA_MFP;
+	if (((!hapd->conf->ieee802_1x && !hapd->conf->wpa) ||
+	     sta->auth_alg == WLAN_AUTH_FT) &&
+	    sta->flags & WLAN_STA_AUTHORIZED)
+		set_flags |= WPA_STA_AUTHORIZED;
+	flags_or = total_flags & set_flags;
+	flags_and = total_flags | ~set_flags;
+	return hostapd_sta_set_flags(hapd, sta->addr, total_flags,
+				     flags_or, flags_and);
+}
+
+
+int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname,
+			      int enabled)
+{
+	struct wpa_bss_params params;
+	os_memset(&params, 0, sizeof(params));
+	params.ifname = ifname;
+	params.enabled = enabled;
+	if (enabled) {
+		params.wpa = hapd->conf->wpa;
+		params.ieee802_1x = hapd->conf->ieee802_1x;
+		params.wpa_group = hapd->conf->wpa_group;
+		if ((hapd->conf->wpa & (WPA_PROTO_WPA | WPA_PROTO_RSN)) ==
+		    (WPA_PROTO_WPA | WPA_PROTO_RSN))
+			params.wpa_pairwise = hapd->conf->wpa_pairwise |
+				hapd->conf->rsn_pairwise;
+		else if (hapd->conf->wpa & WPA_PROTO_RSN)
+			params.wpa_pairwise = hapd->conf->rsn_pairwise;
+		else if (hapd->conf->wpa & WPA_PROTO_WPA)
+			params.wpa_pairwise = hapd->conf->wpa_pairwise;
+		params.wpa_key_mgmt = hapd->conf->wpa_key_mgmt;
+		params.rsn_preauth = hapd->conf->rsn_preauth;
+		params.ieee80211w = hapd->conf->ieee80211w;
+	}
+	return hostapd_set_ieee8021x(hapd, &params);
+}
+
+
+int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname)
+{
+	char force_ifname[IFNAMSIZ];
+	u8 if_addr[ETH_ALEN];
+	return hostapd_if_add(hapd, WPA_IF_AP_VLAN, ifname, hapd->own_addr,
+			      NULL, NULL, force_ifname, if_addr, NULL, 0);
+}
+
+
+int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname)
+{
+	return hostapd_if_remove(hapd, WPA_IF_AP_VLAN, ifname);
+}
+
+
+int hostapd_set_wds_sta(struct hostapd_data *hapd, char *ifname_wds,
+			const u8 *addr, int aid, int val)
+{
+	const char *bridge = NULL;
+
+	if (hapd->driver == NULL || hapd->driver->set_wds_sta == NULL)
+		return -1;
+	if (hapd->conf->wds_bridge[0])
+		bridge = hapd->conf->wds_bridge;
+	return hapd->driver->set_wds_sta(hapd->drv_priv, addr, aid, val,
+					 bridge, ifname_wds);
+}
+
+
+int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr,
+			 u16 auth_alg)
+{
+	if (hapd->driver == NULL || hapd->driver->add_sta_node == NULL)
+		return -EOPNOTSUPP;
+	return hapd->driver->add_sta_node(hapd->drv_priv, addr, auth_alg);
+}
+
+
+int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr,
+		     u16 seq, u16 status, const u8 *ie, size_t len)
+{
+	struct wpa_driver_sta_auth_params params;
+#ifdef CONFIG_FILS
+	struct sta_info *sta;
+#endif /* CONFIG_FILS */
+
+	if (hapd->driver == NULL || hapd->driver->sta_auth == NULL)
+		return 0;
+
+	os_memset(&params, 0, sizeof(params));
+
+#ifdef CONFIG_FILS
+	sta = ap_get_sta(hapd, addr);
+	if (!sta) {
+		wpa_printf(MSG_DEBUG, "Station " MACSTR
+			   " not found for sta_auth processing",
+			   MAC2STR(addr));
+		return 0;
+	}
+
+	if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+	    sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+	    sta->auth_alg == WLAN_AUTH_FILS_PK) {
+		params.fils_auth = 1;
+		wpa_auth_get_fils_aead_params(sta->wpa_sm, params.fils_anonce,
+					      params.fils_snonce,
+					      params.fils_kek,
+					      &params.fils_kek_len);
+	}
+#endif /* CONFIG_FILS */
+
+	params.own_addr = hapd->own_addr;
+	params.addr = addr;
+	params.seq = seq;
+	params.status = status;
+	params.ie = ie;
+	params.len = len;
+
+	return hapd->driver->sta_auth(hapd->drv_priv, &params);
+}
+
+
+int hostapd_sta_assoc(struct hostapd_data *hapd, const u8 *addr,
+		      int reassoc, u16 status, const u8 *ie, size_t len)
+{
+	if (hapd->driver == NULL || hapd->driver->sta_assoc == NULL)
+		return 0;
+	return hapd->driver->sta_assoc(hapd->drv_priv, hapd->own_addr, addr,
+				       reassoc, status, ie, len);
+}
+
+
+int hostapd_sta_add(struct hostapd_data *hapd,
+		    const u8 *addr, u16 aid, u16 capability,
+		    const u8 *supp_rates, size_t supp_rates_len,
+		    u16 listen_interval,
+		    const struct ieee80211_ht_capabilities *ht_capab,
+		    const struct ieee80211_vht_capabilities *vht_capab,
+		    const struct ieee80211_he_capabilities *he_capab,
+		    size_t he_capab_len,
+		    const struct ieee80211_eht_capabilities *eht_capab,
+		    size_t eht_capab_len,
+		    const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab,
+		    u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
+		    int set, const u8 *link_addr, bool mld_link_sta)
+{
+	struct hostapd_sta_add_params params;
+
+	if (hapd->driver == NULL)
+		return 0;
+	if (hapd->driver->sta_add == NULL)
+		return 0;
+
+	os_memset(&params, 0, sizeof(params));
+	params.addr = addr;
+	params.aid = aid;
+	params.capability = capability;
+	params.supp_rates = supp_rates;
+	params.supp_rates_len = supp_rates_len;
+	params.listen_interval = listen_interval;
+	params.ht_capabilities = ht_capab;
+	params.vht_capabilities = vht_capab;
+	params.he_capab = he_capab;
+	params.he_capab_len = he_capab_len;
+	params.eht_capab = eht_capab;
+	params.eht_capab_len = eht_capab_len;
+	params.he_6ghz_capab = he_6ghz_capab;
+	params.vht_opmode_enabled = !!(flags & WLAN_STA_VHT_OPMODE_ENABLED);
+	params.vht_opmode = vht_opmode;
+	params.flags = hostapd_sta_flags_to_drv(flags);
+	params.qosinfo = qosinfo;
+	params.support_p2p_ps = supp_p2p_ps;
+	params.set = set;
+	params.mld_link_id = -1;
+
+#ifdef CONFIG_IEEE80211BE
+	/*
+	 * An AP MLD needs to always specify to what link the station needs
+	 * to be added.
+	 */
+	if (hapd->conf->mld_ap) {
+		params.mld_link_id = hapd->mld_link_id;
+		params.mld_link_addr = link_addr;
+		params.mld_link_sta = mld_link_sta;
+	}
+#endif /* CONFIG_IEEE80211BE */
+
+	return hapd->driver->sta_add(hapd->drv_priv, &params);
+}
+
+
+int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr,
+		      u8 *tspec_ie, size_t tspec_ielen)
+{
+	if (hapd->driver == NULL || hapd->driver->add_tspec == NULL)
+		return 0;
+	return hapd->driver->add_tspec(hapd->drv_priv, addr, tspec_ie,
+				       tspec_ielen);
+}
+
+
+int hostapd_set_privacy(struct hostapd_data *hapd, int enabled)
+{
+	if (hapd->driver == NULL || hapd->driver->set_privacy == NULL)
+		return 0;
+	return hapd->driver->set_privacy(hapd->drv_priv, enabled);
+}
+
+
+int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem,
+			     size_t elem_len)
+{
+	if (hapd->driver == NULL || hapd->driver->set_generic_elem == NULL)
+		return 0;
+	return hapd->driver->set_generic_elem(hapd->drv_priv, elem, elem_len);
+}
+
+
+int hostapd_get_ssid(struct hostapd_data *hapd, u8 *buf, size_t len)
+{
+	if (hapd->driver == NULL || hapd->driver->hapd_get_ssid == NULL)
+		return 0;
+	return hapd->driver->hapd_get_ssid(hapd->drv_priv, buf, len);
+}
+
+
+int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len)
+{
+	if (hapd->driver == NULL || hapd->driver->hapd_set_ssid == NULL)
+		return 0;
+	return hapd->driver->hapd_set_ssid(hapd->drv_priv, buf, len);
+}
+
+
+int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type,
+		   const char *ifname, const u8 *addr, void *bss_ctx,
+		   void **drv_priv, char *force_ifname, u8 *if_addr,
+		   const char *bridge, int use_existing)
+{
+	if (hapd->driver == NULL || hapd->driver->if_add == NULL)
+		return -1;
+	return hapd->driver->if_add(hapd->drv_priv, type, ifname, addr,
+				    bss_ctx, drv_priv, force_ifname, if_addr,
+				    bridge, use_existing, 1);
+}
+
+
+int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
+		      const char *ifname)
+{
+	if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+	    hapd->driver->if_remove == NULL)
+		return -1;
+	return hapd->driver->if_remove(hapd->drv_priv, type, ifname);
+}
+
+
+int hostapd_set_ieee8021x(struct hostapd_data *hapd,
+			  struct wpa_bss_params *params)
+{
+	if (hapd->driver == NULL || hapd->driver->set_ieee8021x == NULL)
+		return 0;
+	return hapd->driver->set_ieee8021x(hapd->drv_priv, params);
+}
+
+
+int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd,
+		       const u8 *addr, int idx, int link_id, u8 *seq)
+{
+	if (hapd->driver == NULL || hapd->driver->get_seqnum == NULL)
+		return 0;
+	return hapd->driver->get_seqnum(ifname, hapd->drv_priv, addr, idx,
+					link_id, seq);
+}
+
+
+int hostapd_flush(struct hostapd_data *hapd)
+{
+	if (hapd->driver == NULL || hapd->driver->flush == NULL)
+		return 0;
+	return hapd->driver->flush(hapd->drv_priv);
+}
+
+
+int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode,
+		     int freq, int channel, int edmg, u8 edmg_channel,
+		     int ht_enabled, int vht_enabled,
+		     int he_enabled, bool eht_enabled,
+		     int sec_channel_offset, int oper_chwidth,
+		     int center_segment0, int center_segment1)
+{
+	struct hostapd_freq_params data;
+	struct hostapd_hw_modes *cmode = hapd->iface->current_mode;
+
+	if (hostapd_set_freq_params(&data, mode, freq, channel, edmg,
+				    edmg_channel, ht_enabled,
+				    vht_enabled, he_enabled, eht_enabled,
+				    sec_channel_offset, oper_chwidth,
+				    center_segment0, center_segment1,
+				    cmode ? cmode->vht_capab : 0,
+				    cmode ?
+				    &cmode->he_capab[IEEE80211_MODE_AP] : NULL,
+				    cmode ?
+				    &cmode->eht_capab[IEEE80211_MODE_AP] :
+				    NULL))
+		return -1;
+
+	if (hapd->driver == NULL)
+		return 0;
+	if (hapd->driver->set_freq == NULL)
+		return 0;
+
+	data.link_id = -1;
+
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->conf->mld_ap) {
+		data.link_id = hapd->mld_link_id;
+		wpa_printf(MSG_DEBUG,
+			   "hostapd_set_freq: link_id=%d", data.link_id);
+	}
+#endif /* CONFIG_IEEE80211BE */
+
+	return hapd->driver->set_freq(hapd->drv_priv, &data);
+}
+
+int hostapd_set_rts(struct hostapd_data *hapd, int rts)
+{
+	if (hapd->driver == NULL || hapd->driver->set_rts == NULL)
+		return 0;
+	return hapd->driver->set_rts(hapd->drv_priv, rts);
+}
+
+
+int hostapd_set_frag(struct hostapd_data *hapd, int frag)
+{
+	if (hapd->driver == NULL || hapd->driver->set_frag == NULL)
+		return 0;
+	return hapd->driver->set_frag(hapd->drv_priv, frag);
+}
+
+
+int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr,
+			  int total_flags, int flags_or, int flags_and)
+{
+	if (!hapd->driver || !hapd->drv_priv || !hapd->driver->sta_set_flags)
+		return 0;
+	return hapd->driver->sta_set_flags(hapd->drv_priv, addr, total_flags,
+					   flags_or, flags_and);
+}
+
+
+int hostapd_sta_set_airtime_weight(struct hostapd_data *hapd, const u8 *addr,
+				   unsigned int weight)
+{
+	if (!hapd->driver || !hapd->driver->sta_set_airtime_weight)
+		return 0;
+	return hapd->driver->sta_set_airtime_weight(hapd->drv_priv, addr,
+						    weight);
+}
+
+
+int hostapd_set_country(struct hostapd_data *hapd, const char *country)
+{
+	if (hapd->driver == NULL ||
+	    hapd->driver->set_country == NULL)
+		return 0;
+	return hapd->driver->set_country(hapd->drv_priv, country);
+}
+
+
+int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs,
+				int cw_min, int cw_max, int burst_time)
+{
+	int link_id = -1;
+
+	if (hapd->driver == NULL || hapd->driver->set_tx_queue_params == NULL)
+		return 0;
+
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->conf->mld_ap)
+		link_id = hapd->mld_link_id;
+#endif /* CONFIG_IEEE80211BE */
+
+	return hapd->driver->set_tx_queue_params(hapd->drv_priv, queue, aifs,
+						 cw_min, cw_max, burst_time,
+						 link_id);
+}
+
+
+struct hostapd_hw_modes *
+hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes,
+			    u16 *flags, u8 *dfs_domain)
+{
+	if (!hapd->driver || !hapd->driver->get_hw_feature_data ||
+	    !hapd->drv_priv)
+		return NULL;
+	return hapd->driver->get_hw_feature_data(hapd->drv_priv, num_modes,
+						 flags, dfs_domain);
+}
+
+
+int hostapd_driver_commit(struct hostapd_data *hapd)
+{
+	if (hapd->driver == NULL || hapd->driver->commit == NULL)
+		return 0;
+	return hapd->driver->commit(hapd->drv_priv);
+}
+
+
+int hostapd_drv_none(struct hostapd_data *hapd)
+{
+	return hapd->driver && os_strcmp(hapd->driver->name, "none") == 0;
+}
+
+
+bool hostapd_drv_nl80211(struct hostapd_data *hapd)
+{
+	return hapd->driver && os_strcmp(hapd->driver->name, "nl80211") == 0;
+}
+
+
+int hostapd_driver_scan(struct hostapd_data *hapd,
+			struct wpa_driver_scan_params *params)
+{
+	if (hapd->driver && hapd->driver->scan2)
+		return hapd->driver->scan2(hapd->drv_priv, params);
+	return -1;
+}
+
+
+struct wpa_scan_results * hostapd_driver_get_scan_results(
+	struct hostapd_data *hapd)
+{
+	if (hapd->driver && hapd->driver->get_scan_results2)
+		return hapd->driver->get_scan_results2(hapd->drv_priv);
+	return NULL;
+}
+
+
+int hostapd_driver_set_noa(struct hostapd_data *hapd, u8 count, int start,
+			   int duration)
+{
+	if (hapd->driver && hapd->driver->set_noa)
+		return hapd->driver->set_noa(hapd->drv_priv, count, start,
+					     duration);
+	return -1;
+}
+
+
+int hostapd_drv_set_key(const char *ifname, struct hostapd_data *hapd,
+			enum wpa_alg alg, const u8 *addr,
+			int key_idx, int vlan_id, int set_tx,
+			const u8 *seq, size_t seq_len,
+			const u8 *key, size_t key_len, enum key_flag key_flag)
+{
+	struct wpa_driver_set_key_params params;
+
+	if (hapd->driver == NULL || hapd->driver->set_key == NULL)
+		return 0;
+
+	os_memset(&params, 0, sizeof(params));
+	params.ifname = ifname;
+	params.alg = alg;
+	params.addr = addr;
+	params.key_idx = key_idx;
+	params.set_tx = set_tx;
+	params.seq = seq;
+	params.seq_len = seq_len;
+	params.key = key;
+	params.key_len = key_len;
+	params.vlan_id = vlan_id;
+	params.key_flag = key_flag;
+	params.link_id = -1;
+
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->conf->mld_ap && !(key_flag & KEY_FLAG_PAIRWISE))
+		params.link_id = hapd->mld_link_id;
+#endif /* CONFIG_IEEE80211BE */
+
+	return hapd->driver->set_key(hapd->drv_priv, &params);
+}
+
+
+int hostapd_drv_send_mlme(struct hostapd_data *hapd,
+			  const void *msg, size_t len, int noack,
+			  const u16 *csa_offs, size_t csa_offs_len,
+			  int no_encrypt)
+{
+	int link_id = -1;
+
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->conf->mld_ap)
+		link_id = hapd->mld_link_id;
+#endif /* CONFIG_IEEE80211BE */
+
+	if (!hapd->driver || !hapd->driver->send_mlme || !hapd->drv_priv)
+		return 0;
+	return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0,
+				       csa_offs, csa_offs_len, no_encrypt, 0,
+				       link_id);
+}
+
+
+int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
+			   const u8 *addr, int reason)
+{
+	int link_id = -1;
+
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->conf->mld_ap)
+		link_id = hapd->mld_link_id;
+#endif /* CONFIG_IEEE80211BE */
+
+	if (!hapd->driver || !hapd->driver->sta_deauth || !hapd->drv_priv)
+		return 0;
+	return hapd->driver->sta_deauth(hapd->drv_priv, hapd->own_addr, addr,
+					reason, link_id);
+}
+
+
+int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
+			     const u8 *addr, int reason)
+{
+	if (!hapd->driver || !hapd->driver->sta_disassoc || !hapd->drv_priv)
+		return 0;
+	return hapd->driver->sta_disassoc(hapd->drv_priv, hapd->own_addr, addr,
+					  reason);
+}
+
+
+int hostapd_drv_wnm_oper(struct hostapd_data *hapd, enum wnm_oper oper,
+			 const u8 *peer, u8 *buf, u16 *buf_len)
+{
+	if (hapd->driver == NULL || hapd->driver->wnm_oper == NULL)
+		return -1;
+	return hapd->driver->wnm_oper(hapd->drv_priv, oper, peer, buf,
+				      buf_len);
+}
+
+
+int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
+			    unsigned int wait, const u8 *dst, const u8 *data,
+			    size_t len)
+{
+	const u8 *bssid;
+	const u8 wildcard_bssid[ETH_ALEN] = {
+		0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+	};
+
+	if (!hapd->driver || !hapd->driver->send_action || !hapd->drv_priv)
+		return 0;
+	bssid = hapd->own_addr;
+	if (!is_multicast_ether_addr(dst) &&
+	    len > 0 && data[0] == WLAN_ACTION_PUBLIC) {
+		struct sta_info *sta;
+
+		/*
+		 * Public Action frames to a STA that is not a member of the BSS
+		 * shall use wildcard BSSID value.
+		 */
+		sta = ap_get_sta(hapd, dst);
+		if (!sta || !(sta->flags & WLAN_STA_ASSOC))
+			bssid = wildcard_bssid;
+	} else if (is_broadcast_ether_addr(dst) &&
+		   len > 0 && data[0] == WLAN_ACTION_PUBLIC) {
+		/*
+		 * The only current use case of Public Action frames with
+		 * broadcast destination address is DPP PKEX. That case is
+		 * directing all devices and not just the STAs within the BSS,
+		 * so have to use the wildcard BSSID value.
+		 */
+		bssid = wildcard_bssid;
+	}
+	return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst,
+					 hapd->own_addr, bssid, data, len, 0);
+}
+
+
+int hostapd_drv_send_action_addr3_ap(struct hostapd_data *hapd,
+				     unsigned int freq,
+				     unsigned int wait, const u8 *dst,
+				     const u8 *data, size_t len)
+{
+	if (hapd->driver == NULL || hapd->driver->send_action == NULL)
+		return 0;
+	return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst,
+					 hapd->own_addr, hapd->own_addr, data,
+					 len, 0);
+}
+
+
+int hostapd_start_dfs_cac(struct hostapd_iface *iface,
+			  enum hostapd_hw_mode mode, int freq,
+			  int channel, int ht_enabled, int vht_enabled,
+			  int he_enabled, bool eht_enabled,
+			  int sec_channel_offset, int oper_chwidth,
+			  int center_segment0, int center_segment1,
+			  bool radar_background)
+{
+	struct hostapd_data *hapd = iface->bss[0];
+	struct hostapd_freq_params data;
+	int res;
+	struct hostapd_hw_modes *cmode = iface->current_mode;
+
+	if (!hapd->driver || !hapd->driver->start_dfs_cac || !cmode)
+		return 0;
+
+	if (!iface->conf->ieee80211h) {
+		wpa_printf(MSG_ERROR, "Can't start DFS CAC, DFS functionality "
+			   "is not enabled");
+		return -1;
+	}
+
+	if (hostapd_set_freq_params(&data, mode, freq, channel, 0, 0,
+				    ht_enabled,
+				    vht_enabled, he_enabled, eht_enabled,
+				    sec_channel_offset,
+				    oper_chwidth, center_segment0,
+				    center_segment1,
+				    cmode->vht_capab,
+				    &cmode->he_capab[IEEE80211_MODE_AP],
+				    &cmode->eht_capab[IEEE80211_MODE_AP])) {
+		wpa_printf(MSG_ERROR, "Can't set freq params");
+		return -1;
+	}
+	data.radar_background = radar_background;
+
+	res = hapd->driver->start_dfs_cac(hapd->drv_priv, &data);
+	if (!res) {
+		if (radar_background)
+			iface->radar_background.cac_started = 1;
+		else
+			iface->cac_started = 1;
+		os_get_reltime(&iface->dfs_cac_start);
+	}
+
+	return res;
+}
+
+
+int hostapd_drv_set_qos_map(struct hostapd_data *hapd,
+			    const u8 *qos_map_set, u8 qos_map_set_len)
+{
+	if (!hapd->driver || !hapd->driver->set_qos_map || !hapd->drv_priv ||
+	    !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_QOS_MAPPING))
+		return 0;
+	return hapd->driver->set_qos_map(hapd->drv_priv, qos_map_set,
+					 qos_map_set_len);
+}
+
+
+void hostapd_get_hw_mode_any_channels(struct hostapd_data *hapd,
+				      struct hostapd_hw_modes *mode,
+				      int acs_ch_list_all, bool allow_disabled,
+				      int **freq_list)
+{
+	int i;
+	bool is_no_ir = false;
+
+	for (i = 0; i < mode->num_channels; i++) {
+		struct hostapd_channel_data *chan = &mode->channels[i];
+
+		if (!acs_ch_list_all &&
+		    (hapd->iface->conf->acs_freq_list.num &&
+		     !freq_range_list_includes(
+			     &hapd->iface->conf->acs_freq_list,
+			     chan->freq)))
+			continue;
+		if (!acs_ch_list_all &&
+		    (!hapd->iface->conf->acs_freq_list_present &&
+		     hapd->iface->conf->acs_ch_list.num &&
+		     !freq_range_list_includes(
+			     &hapd->iface->conf->acs_ch_list,
+			     chan->chan)))
+			continue;
+		if (is_6ghz_freq(chan->freq) &&
+		    ((hapd->iface->conf->acs_exclude_6ghz_non_psc &&
+		      !is_6ghz_psc_frequency(chan->freq)) ||
+		     (!hapd->iface->conf->ieee80211ax &&
+		      !hapd->iface->conf->ieee80211be)))
+			continue;
+		if ((!(chan->flag & HOSTAPD_CHAN_DISABLED) || allow_disabled) &&
+		    !(hapd->iface->conf->acs_exclude_dfs &&
+		      (chan->flag & HOSTAPD_CHAN_RADAR)) &&
+		    !(chan->max_tx_power < hapd->iface->conf->min_tx_power))
+			int_array_add_unique(freq_list, chan->freq);
+		else if ((chan->flag & HOSTAPD_CHAN_NO_IR) &&
+			 is_6ghz_freq(chan->freq))
+			is_no_ir = true;
+	}
+
+	hapd->iface->is_no_ir = is_no_ir;
+}
+
+
+void hostapd_get_ext_capa(struct hostapd_iface *iface)
+{
+	struct hostapd_data *hapd = iface->bss[0];
+
+	if (!hapd->driver || !hapd->driver->get_ext_capab)
+		return;
+
+	hapd->driver->get_ext_capab(hapd->drv_priv, WPA_IF_AP_BSS,
+				    &iface->extended_capa,
+				    &iface->extended_capa_mask,
+				    &iface->extended_capa_len);
+}
+
+
+void hostapd_get_mld_capa(struct hostapd_iface *iface)
+{
+	struct hostapd_data *hapd = iface->bss[0];
+
+	if (!hapd->driver || !hapd->driver->get_mld_capab)
+		return;
+
+	hapd->driver->get_mld_capab(hapd->drv_priv, WPA_IF_AP_BSS,
+				    &iface->mld_eml_capa,
+				    &iface->mld_mld_capa);
+}
+
+
+/**
+ * hostapd_drv_do_acs - Start automatic channel selection
+ * @hapd: BSS data for the device initiating ACS
+ * Returns: 0 on success, -1 on failure, 1 on failure due to NO_IR (AFC)
+ */
+int hostapd_drv_do_acs(struct hostapd_data *hapd)
+{
+	struct drv_acs_params params;
+	int ret, i, acs_ch_list_all = 0;
+	struct hostapd_hw_modes *mode;
+	int *freq_list = NULL;
+	enum hostapd_hw_mode selected_mode;
+
+	if (hapd->driver == NULL || hapd->driver->do_acs == NULL)
+		return 0;
+
+	os_memset(&params, 0, sizeof(params));
+	params.hw_mode = hapd->iface->conf->hw_mode;
+
+	/*
+	 * If no chanlist config parameter is provided, include all enabled
+	 * channels of the selected hw_mode.
+	 */
+	if (hapd->iface->conf->acs_freq_list_present)
+		acs_ch_list_all = !hapd->iface->conf->acs_freq_list.num;
+	else
+		acs_ch_list_all = !hapd->iface->conf->acs_ch_list.num;
+
+	if (hapd->iface->current_mode)
+		selected_mode = hapd->iface->current_mode->mode;
+	else
+		selected_mode = HOSTAPD_MODE_IEEE80211ANY;
+
+	for (i = 0; i < hapd->iface->num_hw_features; i++) {
+		mode = &hapd->iface->hw_features[i];
+		if (selected_mode != HOSTAPD_MODE_IEEE80211ANY &&
+		    selected_mode != mode->mode)
+			continue;
+		hostapd_get_hw_mode_any_channels(hapd, mode, acs_ch_list_all,
+						 false, &freq_list);
+	}
+
+	if (!freq_list && hapd->iface->is_no_ir) {
+		wpa_printf(MSG_ERROR,
+			   "NO_IR: Interface freq_list is empty. Failing do_acs.");
+		return 1;
+	}
+
+	params.freq_list = freq_list;
+	params.edmg_enabled = hapd->iface->conf->enable_edmg;
+
+	params.ht_enabled = !!(hapd->iface->conf->ieee80211n);
+	params.ht40_enabled = !!(hapd->iface->conf->ht_capab &
+				 HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET);
+	params.vht_enabled = !!(hapd->iface->conf->ieee80211ac);
+	params.eht_enabled = !!(hapd->iface->conf->ieee80211be);
+	params.ch_width = 20;
+	if (hapd->iface->conf->ieee80211n && params.ht40_enabled)
+		params.ch_width = 40;
+
+	/* Note: VHT20 is defined by combination of ht_capab & oper_chwidth
+	 */
+	if ((hapd->iface->conf->ieee80211be ||
+	     hapd->iface->conf->ieee80211ax ||
+	     hapd->iface->conf->ieee80211ac) &&
+	    params.ht40_enabled) {
+		enum oper_chan_width oper_chwidth;
+
+		oper_chwidth = hostapd_get_oper_chwidth(hapd->iface->conf);
+		if (oper_chwidth == CONF_OPER_CHWIDTH_80MHZ)
+			params.ch_width = 80;
+		else if (oper_chwidth == CONF_OPER_CHWIDTH_160MHZ ||
+			 oper_chwidth == CONF_OPER_CHWIDTH_80P80MHZ)
+			params.ch_width = 160;
+		else if (oper_chwidth == CONF_OPER_CHWIDTH_320MHZ)
+			params.ch_width = 320;
+	}
+
+	if (hapd->iface->conf->op_class)
+		params.ch_width = op_class_to_bandwidth(
+			hapd->iface->conf->op_class);
+	ret = hapd->driver->do_acs(hapd->drv_priv, &params);
+	os_free(freq_list);
+
+	return ret;
+}
+
+
+int hostapd_drv_update_dh_ie(struct hostapd_data *hapd, const u8 *peer,
+			     u16 reason_code, const u8 *ie, size_t ielen)
+{
+	if (!hapd->driver || !hapd->driver->update_dh_ie || !hapd->drv_priv)
+		return 0;
+	return hapd->driver->update_dh_ie(hapd->drv_priv, peer, reason_code,
+					  ie, ielen);
+}
+
+
+int hostapd_drv_dpp_listen(struct hostapd_data *hapd, bool enable)
+{
+	if (!hapd->driver || !hapd->driver->dpp_listen || !hapd->drv_priv)
+		return 0;
+	return hapd->driver->dpp_listen(hapd->drv_priv, enable);
+}
+
+
+#ifdef CONFIG_PASN
+int hostapd_drv_set_secure_ranging_ctx(struct hostapd_data *hapd,
+				       const u8 *own_addr, const u8 *peer_addr,
+				       u32 cipher, u8 tk_len, const u8 *tk,
+				       u8 ltf_keyseed_len,
+				       const u8 *ltf_keyseed, u32 action)
+{
+	struct secure_ranging_params params;
+
+	if (!hapd->driver || !hapd->driver->set_secure_ranging_ctx)
+		return 0;
+
+	os_memset(&params, 0, sizeof(params));
+	params.own_addr = own_addr;
+	params.peer_addr = peer_addr;
+	params.cipher = cipher;
+	params.tk_len = tk_len;
+	params.tk = tk;
+	params.ltf_keyseed_len = ltf_keyseed_len;
+	params.ltf_keyseed = ltf_keyseed;
+	params.action = action;
+
+	return hapd->driver->set_secure_ranging_ctx(hapd->drv_priv, &params);
+}
+#endif /* CONFIG_PASN */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_drv_ops.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_drv_ops.h
new file mode 100644
index 0000000..331b0ea
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_drv_ops.h
@@ -0,0 +1,457 @@
+/*
+ * hostapd - Driver operations
+ * Copyright (c) 2009-2014, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef AP_DRV_OPS
+#define AP_DRV_OPS
+
+enum wpa_driver_if_type;
+struct wpa_bss_params;
+struct wpa_driver_scan_params;
+struct ieee80211_ht_capabilities;
+struct ieee80211_vht_capabilities;
+struct hostapd_freq_params;
+
+u32 hostapd_sta_flags_to_drv(u32 flags);
+int hostapd_build_ap_extra_ies(struct hostapd_data *hapd,
+			       struct wpabuf **beacon,
+			       struct wpabuf **proberesp,
+			       struct wpabuf **assocresp);
+void hostapd_free_ap_extra_ies(struct hostapd_data *hapd, struct wpabuf *beacon,
+			       struct wpabuf *proberesp,
+			       struct wpabuf *assocresp);
+int hostapd_reset_ap_wps_ie(struct hostapd_data *hapd);
+int hostapd_set_ap_wps_ie(struct hostapd_data *hapd);
+int hostapd_set_authorized(struct hostapd_data *hapd,
+			   struct sta_info *sta, int authorized);
+int hostapd_set_sta_flags(struct hostapd_data *hapd, struct sta_info *sta);
+int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname,
+			      int enabled);
+int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname);
+int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname);
+int hostapd_set_wds_sta(struct hostapd_data *hapd, char *ifname_wds,
+			const u8 *addr, int aid, int val);
+int hostapd_sta_add(struct hostapd_data *hapd,
+		    const u8 *addr, u16 aid, u16 capability,
+		    const u8 *supp_rates, size_t supp_rates_len,
+		    u16 listen_interval,
+		    const struct ieee80211_ht_capabilities *ht_capab,
+		    const struct ieee80211_vht_capabilities *vht_capab,
+		    const struct ieee80211_he_capabilities *he_capab,
+		    size_t he_capab_len,
+		    const struct ieee80211_eht_capabilities *eht_capab,
+		    size_t eht_capab_len,
+		    const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab,
+		    u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
+		    int set, const u8 *link_addr, bool mld_link_sta);
+int hostapd_set_privacy(struct hostapd_data *hapd, int enabled);
+int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem,
+			     size_t elem_len);
+int hostapd_get_ssid(struct hostapd_data *hapd, u8 *buf, size_t len);
+int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len);
+int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type,
+		   const char *ifname, const u8 *addr, void *bss_ctx,
+		   void **drv_priv, char *force_ifname, u8 *if_addr,
+		   const char *bridge, int use_existing);
+int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
+		      const char *ifname);
+int hostapd_set_ieee8021x(struct hostapd_data *hapd,
+			  struct wpa_bss_params *params);
+int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd,
+		       const u8 *addr, int idx, int link_id, u8 *seq);
+int hostapd_flush(struct hostapd_data *hapd);
+int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode,
+		     int freq, int channel, int edmg, u8 edmg_channel,
+		     int ht_enabled, int vht_enabled, int he_enabled,
+		     bool eht_enabled, int sec_channel_offset, int oper_chwidth,
+		     int center_segment0, int center_segment1);
+int hostapd_set_rts(struct hostapd_data *hapd, int rts);
+int hostapd_set_frag(struct hostapd_data *hapd, int frag);
+int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr,
+			  int total_flags, int flags_or, int flags_and);
+int hostapd_sta_set_airtime_weight(struct hostapd_data *hapd, const u8 *addr,
+				   unsigned int weight);
+int hostapd_set_country(struct hostapd_data *hapd, const char *country);
+int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs,
+				int cw_min, int cw_max, int burst_time);
+struct hostapd_hw_modes *
+hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes,
+			    u16 *flags, u8 *dfs_domain);
+int hostapd_driver_commit(struct hostapd_data *hapd);
+int hostapd_drv_none(struct hostapd_data *hapd);
+bool hostapd_drv_nl80211(struct hostapd_data *hapd);
+int hostapd_driver_scan(struct hostapd_data *hapd,
+			struct wpa_driver_scan_params *params);
+struct wpa_scan_results * hostapd_driver_get_scan_results(
+	struct hostapd_data *hapd);
+int hostapd_driver_set_noa(struct hostapd_data *hapd, u8 count, int start,
+			   int duration);
+int hostapd_drv_set_key(const char *ifname,
+			struct hostapd_data *hapd,
+			enum wpa_alg alg, const u8 *addr,
+			int key_idx, int vlan_id, int set_tx,
+			const u8 *seq, size_t seq_len,
+			const u8 *key, size_t key_len, enum key_flag key_flag);
+int hostapd_drv_send_mlme(struct hostapd_data *hapd,
+			  const void *msg, size_t len, int noack,
+			  const u16 *csa_offs, size_t csa_offs_len,
+			  int no_encrypt);
+int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
+			   const u8 *addr, int reason);
+int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
+			     const u8 *addr, int reason);
+int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
+			    unsigned int wait, const u8 *dst, const u8 *data,
+			    size_t len);
+int hostapd_drv_send_action_addr3_ap(struct hostapd_data *hapd,
+				     unsigned int freq,
+				     unsigned int wait, const u8 *dst,
+				     const u8 *data, size_t len);
+static inline void
+hostapd_drv_send_action_cancel_wait(struct hostapd_data *hapd)
+{
+	if (!hapd->driver || !hapd->driver->send_action_cancel_wait ||
+	    !hapd->drv_priv)
+		return;
+	hapd->driver->send_action_cancel_wait(hapd->drv_priv);
+}
+int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr,
+			 u16 auth_alg);
+int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr,
+		     u16 seq, u16 status, const u8 *ie, size_t len);
+int hostapd_sta_assoc(struct hostapd_data *hapd, const u8 *addr,
+		      int reassoc, u16 status, const u8 *ie, size_t len);
+int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr,
+		      u8 *tspec_ie, size_t tspec_ielen);
+int hostapd_start_dfs_cac(struct hostapd_iface *iface,
+			  enum hostapd_hw_mode mode, int freq,
+			  int channel, int ht_enabled, int vht_enabled,
+			  int he_enabled, bool eht_enabled,
+			  int sec_channel_offset, int oper_chwidth,
+			  int center_segment0, int center_segment1,
+			  bool radar_background);
+int hostapd_drv_do_acs(struct hostapd_data *hapd);
+int hostapd_drv_update_dh_ie(struct hostapd_data *hapd, const u8 *peer,
+			     u16 reason_code, const u8 *ie, size_t ielen);
+int hostapd_drv_dpp_listen(struct hostapd_data *hapd, bool enable);
+int hostapd_drv_set_secure_ranging_ctx(struct hostapd_data *hapd,
+				       const u8 *own_addr, const u8 *addr,
+				       u32 cipher, u8 key_len, const u8 *key,
+				       u8 ltf_keyseed_len,
+				       const u8 *ltf_keyseed, u32 action);
+
+
+#include "drivers/driver.h"
+
+int hostapd_drv_wnm_oper(struct hostapd_data *hapd,
+			 enum wnm_oper oper, const u8 *peer,
+			 u8 *buf, u16 *buf_len);
+
+int hostapd_drv_set_qos_map(struct hostapd_data *hapd, const u8 *qos_map_set,
+			    u8 qos_map_set_len);
+
+void hostapd_get_ext_capa(struct hostapd_iface *iface);
+void hostapd_get_mld_capa(struct hostapd_iface *iface);
+
+void hostapd_get_hw_mode_any_channels(struct hostapd_data *hapd,
+				      struct hostapd_hw_modes *mode,
+				      int acs_ch_list_all, bool allow_disabled,
+				      int **freq_list);
+
+static inline int hostapd_drv_set_countermeasures(struct hostapd_data *hapd,
+						  int enabled)
+{
+	if (hapd->driver == NULL ||
+	    hapd->driver->hapd_set_countermeasures == NULL)
+		return 0;
+	return hapd->driver->hapd_set_countermeasures(hapd->drv_priv, enabled);
+}
+
+static inline int hostapd_drv_set_sta_vlan(const char *ifname,
+					   struct hostapd_data *hapd,
+					   const u8 *addr, int vlan_id,
+					   int link_id)
+{
+	if (hapd->driver == NULL || hapd->driver->set_sta_vlan == NULL)
+		return 0;
+	return hapd->driver->set_sta_vlan(hapd->drv_priv, addr, ifname,
+					  vlan_id, link_id);
+}
+
+static inline int hostapd_drv_get_inact_sec(struct hostapd_data *hapd,
+					    const u8 *addr)
+{
+	if (hapd->driver == NULL || hapd->driver->get_inact_sec == NULL)
+		return 0;
+	return hapd->driver->get_inact_sec(hapd->drv_priv, addr);
+}
+
+static inline int hostapd_drv_sta_remove(struct hostapd_data *hapd,
+					 const u8 *addr)
+{
+	if (!hapd->driver || !hapd->driver->sta_remove || !hapd->drv_priv)
+		return 0;
+	return hapd->driver->sta_remove(hapd->drv_priv, addr);
+}
+
+static inline int hostapd_drv_hapd_send_eapol(struct hostapd_data *hapd,
+					      const u8 *addr, const u8 *data,
+					      size_t data_len, int encrypt,
+					      u32 flags, int link_id)
+{
+	if (hapd->driver == NULL || hapd->driver->hapd_send_eapol == NULL)
+		return 0;
+	return hapd->driver->hapd_send_eapol(hapd->drv_priv, addr, data,
+					     data_len, encrypt,
+					     hapd->own_addr, flags, link_id);
+}
+
+static inline int hostapd_drv_read_sta_data(
+	struct hostapd_data *hapd, struct hostap_sta_driver_data *data,
+	const u8 *addr)
+{
+	if (hapd->driver == NULL || hapd->driver->read_sta_data == NULL)
+		return -1;
+	return hapd->driver->read_sta_data(hapd->drv_priv, data, addr);
+}
+
+static inline int hostapd_drv_sta_clear_stats(struct hostapd_data *hapd,
+					      const u8 *addr)
+{
+	if (hapd->driver == NULL || hapd->driver->sta_clear_stats == NULL)
+		return 0;
+	return hapd->driver->sta_clear_stats(hapd->drv_priv, addr);
+}
+
+static inline int hostapd_drv_set_acl(struct hostapd_data *hapd,
+				      struct hostapd_acl_params *params)
+{
+	if (hapd->driver == NULL || hapd->driver->set_acl == NULL)
+		return 0;
+	return hapd->driver->set_acl(hapd->drv_priv, params);
+}
+
+static inline int hostapd_drv_set_ap(struct hostapd_data *hapd,
+				     struct wpa_driver_ap_params *params)
+{
+	if (hapd->driver == NULL || hapd->driver->set_ap == NULL)
+		return 0;
+	return hapd->driver->set_ap(hapd->drv_priv, params);
+}
+
+static inline int hostapd_drv_set_radius_acl_auth(struct hostapd_data *hapd,
+						  const u8 *mac, int accepted,
+						  u32 session_timeout)
+{
+	if (hapd->driver == NULL || hapd->driver->set_radius_acl_auth == NULL)
+		return 0;
+	return hapd->driver->set_radius_acl_auth(hapd->drv_priv, mac, accepted,
+						 session_timeout);
+}
+
+static inline int hostapd_drv_set_radius_acl_expire(struct hostapd_data *hapd,
+						    const u8 *mac)
+{
+	if (hapd->driver == NULL ||
+	    hapd->driver->set_radius_acl_expire == NULL)
+		return 0;
+	return hapd->driver->set_radius_acl_expire(hapd->drv_priv, mac);
+}
+
+static inline int hostapd_drv_set_authmode(struct hostapd_data *hapd,
+					   int auth_algs)
+{
+	if (hapd->driver == NULL || hapd->driver->set_authmode == NULL)
+		return 0;
+	return hapd->driver->set_authmode(hapd->drv_priv, auth_algs);
+}
+
+static inline void hostapd_drv_poll_client(struct hostapd_data *hapd,
+					   const u8 *own_addr, const u8 *addr,
+					   int qos)
+{
+	if (hapd->driver == NULL || hapd->driver->poll_client == NULL)
+		return;
+	hapd->driver->poll_client(hapd->drv_priv, own_addr, addr, qos);
+}
+
+static inline int hostapd_drv_get_survey(struct hostapd_data *hapd,
+					 unsigned int freq)
+{
+	if (hapd->driver == NULL)
+		return -1;
+	if (!hapd->driver->get_survey)
+		return -1;
+	return hapd->driver->get_survey(hapd->drv_priv, freq);
+}
+
+static inline int hostapd_get_country(struct hostapd_data *hapd, char *alpha2)
+{
+	if (hapd->driver == NULL || hapd->driver->get_country == NULL)
+		return -1;
+	return hapd->driver->get_country(hapd->drv_priv, alpha2);
+}
+
+static inline const char * hostapd_drv_get_radio_name(struct hostapd_data *hapd)
+{
+	if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+	    hapd->driver->get_radio_name == NULL)
+		return NULL;
+	return hapd->driver->get_radio_name(hapd->drv_priv);
+}
+
+static inline int hostapd_drv_switch_channel(struct hostapd_data *hapd,
+					     struct csa_settings *settings)
+{
+	if (hapd->driver == NULL || hapd->driver->switch_channel == NULL ||
+	    hapd->drv_priv == NULL)
+		return -1;
+
+	return hapd->driver->switch_channel(hapd->drv_priv, settings);
+}
+
+#ifdef CONFIG_IEEE80211AX
+static inline int hostapd_drv_switch_color(struct hostapd_data *hapd,
+					   struct cca_settings *settings)
+{
+	if (!hapd->driver || !hapd->driver->switch_color || !hapd->drv_priv)
+		return -1;
+
+	return hapd->driver->switch_color(hapd->drv_priv, settings);
+}
+#endif /* CONFIG_IEEE80211AX */
+
+static inline int hostapd_drv_status(struct hostapd_data *hapd, char *buf,
+				     size_t buflen)
+{
+	if (!hapd->driver || !hapd->driver->status || !hapd->drv_priv)
+		return -1;
+	return hapd->driver->status(hapd->drv_priv, buf, buflen);
+}
+
+static inline int hostapd_drv_br_add_ip_neigh(struct hostapd_data *hapd,
+					      int version, const u8 *ipaddr,
+					      int prefixlen, const u8 *addr)
+{
+	if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+	    hapd->driver->br_add_ip_neigh == NULL)
+		return -1;
+	return hapd->driver->br_add_ip_neigh(hapd->drv_priv, version, ipaddr,
+					     prefixlen, addr);
+}
+
+static inline int hostapd_drv_br_delete_ip_neigh(struct hostapd_data *hapd,
+						 u8 version, const u8 *ipaddr)
+{
+	if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+	    hapd->driver->br_delete_ip_neigh == NULL)
+		return -1;
+	return hapd->driver->br_delete_ip_neigh(hapd->drv_priv, version,
+						ipaddr);
+}
+
+static inline int hostapd_drv_br_port_set_attr(struct hostapd_data *hapd,
+					       enum drv_br_port_attr attr,
+					       unsigned int val)
+{
+	if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+	    hapd->driver->br_port_set_attr == NULL)
+		return -1;
+	return hapd->driver->br_port_set_attr(hapd->drv_priv, attr, val);
+}
+
+static inline int hostapd_drv_br_set_net_param(struct hostapd_data *hapd,
+					       enum drv_br_net_param param,
+					       unsigned int val)
+{
+	if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+	    hapd->driver->br_set_net_param == NULL)
+		return -1;
+	return hapd->driver->br_set_net_param(hapd->drv_priv, param, val);
+}
+
+static inline int hostapd_drv_vendor_cmd(struct hostapd_data *hapd,
+					 int vendor_id, int subcmd,
+					 const u8 *data, size_t data_len,
+					 enum nested_attr nested_attr_flag,
+					 struct wpabuf *buf)
+{
+	if (hapd->driver == NULL || hapd->driver->vendor_cmd == NULL)
+		return -1;
+	return hapd->driver->vendor_cmd(hapd->drv_priv, vendor_id, subcmd, data,
+					data_len, nested_attr_flag, buf);
+}
+
+static inline int hostapd_drv_stop_ap(struct hostapd_data *hapd)
+{
+	if (!hapd->driver || !hapd->driver->stop_ap || !hapd->drv_priv)
+		return 0;
+	return hapd->driver->stop_ap(hapd->drv_priv);
+}
+
+static inline int hostapd_drv_channel_info(struct hostapd_data *hapd,
+					   struct wpa_channel_info *ci)
+{
+	if (!hapd->driver || !hapd->driver->channel_info)
+		return -1;
+	return hapd->driver->channel_info(hapd->drv_priv, ci);
+}
+
+static inline int
+hostapd_drv_send_external_auth_status(struct hostapd_data *hapd,
+				      struct external_auth *params)
+{
+	if (!hapd->driver || !hapd->drv_priv ||
+	    !hapd->driver->send_external_auth_status)
+		return -1;
+	return hapd->driver->send_external_auth_status(hapd->drv_priv, params);
+}
+
+static inline int
+hostapd_drv_set_band(struct hostapd_data *hapd, u32 band_mask)
+{
+	if (!hapd->driver || !hapd->drv_priv || !hapd->driver->set_band)
+		return -1;
+	return hapd->driver->set_band(hapd->drv_priv, band_mask);
+}
+
+#ifdef ANDROID
+static inline int hostapd_drv_driver_cmd(struct hostapd_data *hapd,
+					 char *cmd, char *buf, size_t buf_len)
+{
+	if (!hapd->driver->driver_cmd)
+		return -1;
+	return hapd->driver->driver_cmd(hapd->drv_priv, cmd, buf, buf_len);
+}
+#endif /* ANDROID */
+
+#ifdef CONFIG_TESTING_OPTIONS
+static inline int
+hostapd_drv_register_frame(struct hostapd_data *hapd, u16 type,
+			   const u8 *match, size_t match_len,
+			   bool multicast)
+{
+	if (!hapd->driver || !hapd->drv_priv || !hapd->driver->register_frame)
+		return -1;
+	return hapd->driver->register_frame(hapd->drv_priv, type, match,
+					    match_len, multicast);
+}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#ifdef CONFIG_IEEE80211BE
+static inline int hostapd_drv_link_add(struct hostapd_data *hapd,
+				       u8 link_id, const u8 *addr)
+{
+	if (!hapd->driver || !hapd->drv_priv || !hapd->driver->link_add)
+		return -1;
+
+	return hapd->driver->link_add(hapd->drv_priv, link_id, addr);
+
+}
+#endif /* CONFIG_IEEE80211BE */
+
+#endif /* AP_DRV_OPS */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_list.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_list.c
new file mode 100644
index 0000000..aa590d1
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_list.c
@@ -0,0 +1,607 @@
+/*
+ * hostapd / AP table
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2004, Instant802 Networks, Inc.
+ * Copyright (c) 2006, Devicescape Software, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ieee802_11.h"
+#include "sta_info.h"
+#include "beacon.h"
+#include "ap_list.h"
+
+
+/* AP list is a double linked list with head->prev pointing to the end of the
+ * list and tail->next = NULL. Entries are moved to the head of the list
+ * whenever a beacon has been received from the AP in question. The tail entry
+ * in this link will thus be the least recently used entry. */
+
+
+static int ap_list_beacon_olbc(struct hostapd_iface *iface, struct ap_info *ap)
+{
+	int i;
+
+	if (iface->current_mode == NULL ||
+	    iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G ||
+	    iface->conf->channel != ap->channel)
+		return 0;
+
+	if (ap->erp != -1 && (ap->erp & ERP_INFO_NON_ERP_PRESENT))
+		return 1;
+
+	for (i = 0; i < WLAN_SUPP_RATES_MAX; i++) {
+		int rate = (ap->supported_rates[i] & 0x7f) * 5;
+		if (rate == 60 || rate == 90 || rate > 110)
+			return 0;
+	}
+
+	return 1;
+}
+
+
+static struct ap_info * ap_get_ap(struct hostapd_iface *iface, const u8 *ap)
+{
+	struct ap_info *s;
+
+	s = iface->ap_hash[STA_HASH(ap)];
+	while (s != NULL && os_memcmp(s->addr, ap, ETH_ALEN) != 0)
+		s = s->hnext;
+	return s;
+}
+
+
+static void ap_ap_list_add(struct hostapd_iface *iface, struct ap_info *ap)
+{
+	if (iface->ap_list) {
+		ap->prev = iface->ap_list->prev;
+		iface->ap_list->prev = ap;
+	} else
+		ap->prev = ap;
+	ap->next = iface->ap_list;
+	iface->ap_list = ap;
+}
+
+
+static void ap_ap_list_del(struct hostapd_iface *iface, struct ap_info *ap)
+{
+	if (iface->ap_list == ap)
+		iface->ap_list = ap->next;
+	else
+		ap->prev->next = ap->next;
+
+	if (ap->next)
+		ap->next->prev = ap->prev;
+	else if (iface->ap_list)
+		iface->ap_list->prev = ap->prev;
+}
+
+
+static void ap_ap_hash_add(struct hostapd_iface *iface, struct ap_info *ap)
+{
+	ap->hnext = iface->ap_hash[STA_HASH(ap->addr)];
+	iface->ap_hash[STA_HASH(ap->addr)] = ap;
+}
+
+
+static void ap_ap_hash_del(struct hostapd_iface *iface, struct ap_info *ap)
+{
+	struct ap_info *s;
+
+	s = iface->ap_hash[STA_HASH(ap->addr)];
+	if (s == NULL) return;
+	if (os_memcmp(s->addr, ap->addr, ETH_ALEN) == 0) {
+		iface->ap_hash[STA_HASH(ap->addr)] = s->hnext;
+		return;
+	}
+
+	while (s->hnext != NULL &&
+	       os_memcmp(s->hnext->addr, ap->addr, ETH_ALEN) != 0)
+		s = s->hnext;
+	if (s->hnext != NULL)
+		s->hnext = s->hnext->hnext;
+	else
+		wpa_printf(MSG_INFO, "AP: could not remove AP " MACSTR
+			   " from hash table",  MAC2STR(ap->addr));
+}
+
+
+static void ap_free_ap(struct hostapd_iface *iface, struct ap_info *ap)
+{
+	ap_ap_hash_del(iface, ap);
+	ap_ap_list_del(iface, ap);
+
+	iface->num_ap--;
+	os_free(ap);
+}
+
+
+static void hostapd_free_aps(struct hostapd_iface *iface)
+{
+	struct ap_info *ap, *prev;
+
+	ap = iface->ap_list;
+
+	while (ap) {
+		prev = ap;
+		ap = ap->next;
+		ap_free_ap(iface, prev);
+	}
+
+	iface->ap_list = NULL;
+}
+
+
+static struct ap_info * ap_ap_add(struct hostapd_iface *iface, const u8 *addr)
+{
+	struct ap_info *ap;
+
+	ap = os_zalloc(sizeof(struct ap_info));
+	if (ap == NULL)
+		return NULL;
+
+	/* initialize AP info data */
+	os_memcpy(ap->addr, addr, ETH_ALEN);
+	ap_ap_list_add(iface, ap);
+	iface->num_ap++;
+	ap_ap_hash_add(iface, ap);
+
+	if (iface->num_ap > iface->conf->ap_table_max_size && ap != ap->prev) {
+		wpa_printf(MSG_DEBUG, "Removing the least recently used AP "
+			   MACSTR " from AP table", MAC2STR(ap->prev->addr));
+		ap_free_ap(iface, ap->prev);
+	}
+
+	return ap;
+}
+
+
+#ifdef CONFIG_24G_BW_SWITCH
+// To check 2.4G band 20M/40M bandwidth switch. Added by Yewei
+static int ap_list_handle_obss(struct hostapd_data *hapd, struct ieee802_11_elems *elems)
+{
+	int switch_to_20 = 0;
+	struct hostapd_iface *iface = hapd->iface;
+
+	// OBSS operation only applicable to 2.4G band.
+	if (hapd->iface->current_mode && (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A))
+	    return 0;
+	
+	// Only applicable when supporting at least HT 40
+	if ((!(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) ||
+		(!(iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)))
+		return 0;
+	
+	// Only applicable when auto_20m_40m is enabled
+	if (!iface->conf->auto_20m_40m)
+	{
+		return 0;
+	}
+
+	// If the Beacon is on the same channel, no need to handle
+	if (elems->ds_params && hapd->iface->current_mode &&
+	    (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G ||
+	     hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211B) &&
+	    (hapd->iconf->channel == elems->ds_params[0])) {
+		return 0;
+	}
+
+	// Check if need to switch to 20M bandwidth
+	if (elems->ht_capabilities) {
+		struct ieee80211_ht_capabilities *ht_cap = (struct ieee80211_ht_capabilities *)elems->ht_capabilities;
+		if (0 != iface->conf->secondary_channel) {
+			iface->secondary_ch = iface->conf->secondary_channel;
+			iface->conf->secondary_channel = 0;
+			//ieee802_11_update_beacons(iface);
+			switch_to_20 = 1;
+			wpa_printf(MSG_INFO, "ap_list_handle_obss, %s, switch_to_20:%d", iface->phy, switch_to_20);
+		}
+	}
+	else if (!elems->ht_capabilities && !elems->vht_capabilities && !elems->he_capabilities) {
+		if (iface->conf->secondary_channel) {
+			iface->secondary_ch = iface->conf->secondary_channel;
+			iface->conf->secondary_channel = 0;
+			//ieee802_11_update_beacons(iface);
+			switch_to_20 = 1;
+			wpa_printf(MSG_INFO, "ap_list_handle_obss, %s, switch_to_20:%d", iface->phy, switch_to_20);			
+		}
+	} 	
+
+	return switch_to_20;
+}
+#endif /* CONFIG_24G_BW_SWITCH */
+
+
+#ifdef CONFIG_5G_BW_SWITCH
+// To check 5G band 20M/40M/80M bandwidth switch in AP list timer. Added by Liangyu Chu
+static int ap_list_5g_BW_switch(struct hostapd_iface *iface)
+{
+	// Only applicable to 5G band
+	if (iface->current_mode && 
+		(iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G ||
+	     iface->current_mode->mode == HOSTAPD_MODE_IEEE80211B))
+	{
+		/*wpa_printf(MSG_INFO, "ap_list_5g_BW_switch: %s, Not at 5G band", iface->phy);*/	
+	     return 0;	
+	}
+	
+	// Only applicable when supporting at least HT 40
+    if (!iface->conf->ht_capab || !(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) 
+	{
+		/*wpa_printf(MSG_INFO, "ap_list_5g_BW_switch: %s, Not support HT 40M bandwidth", iface->phy);*/
+		return 0;
+	}
+	
+	// Only applicable to VHT or HE mode
+	// if (!iface->conf->vht_capab || !iface->conf->he_phy_capab)
+	if (!iface->conf->ieee80211ac && !iface->conf->ieee80211ax)
+	{
+		/*wpa_printf(MSG_INFO, "ap_list_5g_BW_switch: %s, Not support VHT or HE capabilities", iface->phy);*/
+		return 0;
+	}
+
+	// Only applicable when auto_80M is enabled
+	if (!iface->conf->auto_80m)
+	{
+		/*wpa_printf(MSG_INFO, "ap_list_5g_BW_switch: %s, auto BW switch is not enabled", iface->phy);*/
+		return 0;
+	}
+
+	int bw_switch = 0;
+	int current_bw = 20;
+	int target_bw = 80;
+
+	// Compute current bandwith
+	if (iface->conf->vht_oper_chwidth 
+#ifdef CONFIG_IEEE80211AX
+        || iface->conf->he_oper_chwidth
+#endif
+       )
+		current_bw = 80;
+	else if (iface->conf->secondary_channel)
+		current_bw = 40;
+
+	// Compute target bandwidth from AP list statistics
+	if ((iface->num_ap > AP_LIST_MAX_NUM_STA_TO_20M) || 
+		(iface->max_rssi && (iface->max_rssi > AP_LIST_MAX_RSSI_TO_20M)))
+		target_bw = 20;
+	else if ((iface->num_ap > AP_LIST_MAX_NUM_STA_TO_40M) || 
+		(iface->max_rssi && (iface->max_rssi > AP_LIST_MAX_RSSI_TO_40M)))
+		target_bw = 40;
+
+	// Only applicable when BW changes 
+	if (current_bw == target_bw)
+	{
+		wpa_printf(MSG_INFO, "ap_list_5g_BW_switch: %s, num_ap=%d, max_rssi=%d, target_bw=%d, current_bw=%d, no need to switch BW",
+			iface->phy, iface->num_ap, iface->max_rssi, target_bw, current_bw);
+		return 0;
+	}	
+	else		
+	{	
+		wpa_printf(MSG_INFO, "ap_list_5g_BW_switch: %s, num_ap=%d, max_rssi=%d, target_bw=%d, current_bw=%d, need to switch BW",
+			iface->phy, iface->num_ap, iface->max_rssi, target_bw, current_bw);
+	}
+
+	// 80M -> 20M switch
+	if (current_bw == 80 && target_bw == 20)
+	{
+		// Record previous settings
+		iface->secondary_ch = iface->conf->secondary_channel;
+		iface->vht_oper_ch_bw = iface->conf->vht_oper_chwidth;
+		iface->vht_center_freq_seg0_80m = iface->conf->vht_oper_centr_freq_seg0_idx;	
+
+		// 20M bandwidth configuration
+		iface->conf->secondary_channel = 0;
+		iface->conf->vht_oper_chwidth = 0;
+		iface->conf->vht_oper_centr_freq_seg0_idx = 0;
+
+		bw_switch = 1;
+	}
+	// 20M -> 80M switch
+	else if (current_bw == 20 && target_bw == 80)
+	{
+		// 80M bandwidth configuration
+		iface->conf->secondary_channel = iface->secondary_ch;
+		iface->conf->vht_oper_chwidth = iface->vht_oper_ch_bw;
+		iface->conf->vht_oper_centr_freq_seg0_idx = iface->vht_center_freq_seg0_80m;
+
+		bw_switch = 1;
+	}
+
+	// 80M -> 40M switch
+	else if (current_bw == 80 && target_bw == 40)
+	{
+		// Record previous settings
+		iface->vht_oper_ch_bw = iface->conf->vht_oper_chwidth;
+		iface->vht_center_freq_seg0_80m = iface->conf->vht_oper_centr_freq_seg0_idx;	
+
+		// 40M bandwidth configuration
+		iface->conf->vht_oper_chwidth = 0;
+		iface->conf->vht_oper_centr_freq_seg0_idx = iface->vht_center_freq_seg0_40m;
+		
+		bw_switch = 1;
+	}
+	// 40M -> 80M switch
+	else if (current_bw == 40 && target_bw == 80)
+	{
+		// Record previous settings
+		iface->vht_center_freq_seg0_40m = iface->conf->vht_oper_centr_freq_seg0_idx;	
+	
+		// 80M bandwidth configuration
+		iface->conf->vht_oper_chwidth = iface->vht_oper_ch_bw;
+		iface->conf->vht_oper_centr_freq_seg0_idx = iface->vht_center_freq_seg0_80m;
+
+		bw_switch = 1;
+	}
+	
+	// 40M -> 20M switch
+	else if (current_bw == 40 && target_bw == 20)
+	{
+		// Record previous settings
+		iface->secondary_ch = iface->conf->secondary_channel;
+		iface->vht_center_freq_seg0_40m = iface->conf->vht_oper_centr_freq_seg0_idx;			
+
+		// 20M bandwidth configuration
+		iface->conf->secondary_channel = 0;
+		iface->conf->vht_oper_centr_freq_seg0_idx = 0;
+		
+		bw_switch = 1;
+	}
+	// 20M -> 40M switch
+	else if (current_bw == 20 && target_bw == 40)
+	{
+		// 40M bandwidth configuration		
+		iface->conf->secondary_channel = iface->secondary_ch;
+		iface->conf->vht_oper_centr_freq_seg0_idx = iface->vht_center_freq_seg0_40m;
+
+		bw_switch = 1;
+	}
+
+	// Always set HE operation IEs the same as VHT operation IEs if applicable
+#ifdef CONFIG_IEEE80211AX	
+	if (bw_switch && iface->conf->ieee80211ac && iface->conf->ieee80211ax)
+	{
+		iface->conf->he_oper_chwidth = iface->conf->vht_oper_chwidth;
+		iface->conf->he_oper_centr_freq_seg0_idx = iface->conf->vht_oper_centr_freq_seg0_idx;
+	}
+#endif
+	
+	return bw_switch;
+}
+#endif /* CONFIG_5G_BW_SWITCH */
+
+
+#if defined CONFIG_24G_BW_SWITCH || defined CONFIG_5G_BW_SWITCH
+void ap_list_process_beacon(struct hostapd_data *hapd,
+#else
+void ap_list_process_beacon(struct hostapd_iface *iface,
+#endif /* defined CONFIG_24G_BW_SWITCH || defined CONFIG_5G_BW_SWITCH */
+			    const struct ieee80211_mgmt *mgmt,
+			    struct ieee802_11_elems *elems,
+			    struct hostapd_frame_info *fi)
+{
+	struct ap_info *ap;
+	int new_ap = 0;
+	int set_beacon = 0;
+#if defined CONFIG_24G_BW_SWITCH || defined CONFIG_5G_BW_SWITCH
+	struct hostapd_iface *iface = hapd->iface;
+#endif /* defined CONFIG_24G_BW_SWITCH || defined CONFIG_5G_BW_SWITCH */
+#ifdef CONFIG_5G_BW_SWITCH
+	int ssi_signal = fi ? fi->ssi_signal : 0;
+	int ch_width = 20; // Default set each AP as 20M bandwidth
+#endif /* CONFIG_5G_BW_SWITCH */
+
+	if (iface->conf->ap_table_max_size < 1)
+		return;
+
+	ap = ap_get_ap(iface, mgmt->bssid);
+	if (!ap) {
+		ap = ap_ap_add(iface, mgmt->bssid);
+		if (!ap) {
+			wpa_printf(MSG_INFO,
+				   "Failed to allocate AP information entry");
+			return;
+		}
+		new_ap = 1;
+	}
+
+	merge_byte_arrays(ap->supported_rates, WLAN_SUPP_RATES_MAX,
+			  elems->supp_rates, elems->supp_rates_len,
+			  elems->ext_supp_rates, elems->ext_supp_rates_len);
+
+	// Check ERP related info
+	if (elems->erp_info)
+		ap->erp = elems->erp_info[0];
+	else
+		ap->erp = -1;
+
+	// Check channel info
+	if (elems->ds_params)
+		ap->channel = elems->ds_params[0];
+	else if (elems->ht_operation)
+		ap->channel = elems->ht_operation[0];
+	else if (fi)
+		ap->channel = fi->channel;
+
+	// Check HT capablities
+	if (elems->ht_capabilities)
+		ap->ht_support = 1;
+	else
+		ap->ht_support = 0;
+
+#ifdef CONFIG_5G_BW_SWITCH
+	// Check VHT capabilities
+	if (elems->vht_capabilities)
+		ap->vht_support = 1;
+	else
+		ap->vht_support = 0;	
+
+	// Check HE capabilities
+	if (elems->he_capabilities)
+		ap->he_support = 1;
+	else
+		ap->he_support = 0;
+
+	// Check operation bandwidth as reported in Beacon frame. Only consider VHT and HE.
+	if ((ap->vht_support || ap->he_support) && (elems->vht_operation))
+	{
+		struct ieee80211_vht_operation *vht_op = (struct ieee80211_vht_operation *)elems->vht_operation;
+		int op_bw = vht_op->vht_op_info_chwidth;
+		switch (op_bw)
+		{
+			case 0: ch_width = 20; break;
+			case 1: ch_width = 80; break;
+			case 2: ch_width = 160; break;
+			default: break;
+		}
+	}
+	ap->ch_width = ch_width;
+
+	// Check RSSI of the received Beacon frame
+	ap->ssi_signal = ssi_signal;
+	if ((iface->max_rssi == 0) || (ssi_signal > iface->max_rssi))
+		iface->max_rssi = ssi_signal;
+
+	/*wpa_printf(MSG_INFO, "ap_list_process_beacon: BSSID=" MACSTR ", channel=%d, ch_width=%d, ssi_signal=%d, ht_support=%d, vht_support=%d, he_support=%d",
+		MAC2STR(mgmt->bssid), ap->channel, ap->ch_width, ap->ssi_signal, ap->ht_support, ap->vht_support, ap->he_support);*/		
+#endif /* CONFIG_5G_BW_SWITCH */
+
+	os_get_reltime(&ap->last_beacon);
+
+	if (!new_ap && ap != iface->ap_list) {
+		/* move AP entry into the beginning of the list so that the
+		 * oldest entry is always in the end of the list */
+		ap_ap_list_del(iface, ap);
+		ap_ap_list_add(iface, ap);
+	}
+
+	if (!iface->olbc &&
+	    ap_list_beacon_olbc(iface, ap)) {
+		iface->olbc = 1;
+		wpa_printf(MSG_DEBUG, "OLBC AP detected: " MACSTR
+			   " (channel %d) - enable protection",
+			   MAC2STR(ap->addr), ap->channel);
+		set_beacon++;
+	}
+
+	if (!iface->olbc_ht && !ap->ht_support &&
+	    (ap->channel == 0 ||
+	     ap->channel == iface->conf->channel ||
+	     ap->channel == iface->conf->channel +
+	     iface->conf->secondary_channel * 4)) {
+		iface->olbc_ht = 1;
+		hostapd_ht_operation_update(iface);
+		wpa_printf(MSG_DEBUG, "OLBC HT AP detected: " MACSTR
+			   " (channel %d) - enable protection",
+			   MAC2STR(ap->addr), ap->channel);
+		set_beacon++;
+	}
+
+#ifdef CONFIG_24G_BW_SWITCH
+	// 2.4G band 20M/40M bandwidth adaptation. Added by Yewei
+	if (ap_list_handle_obss(hapd, elems))
+		set_beacon++;
+#endif /* CONFIG_24G_BW_SWITCH */
+#if 0
+	// 5G band 20M/40M/80M bandwidth adaptation. Added by Liangyu Chu
+	if (ap_list_5g_BW_switch(iface))
+		set_beacon++;
+#endif
+
+	if (set_beacon)
+		ieee802_11_update_beacons(iface);
+}
+
+
+void ap_list_timer(struct hostapd_iface *iface)
+{
+	struct os_reltime now;
+	struct ap_info *ap;
+	int set_beacon = 0;
+
+	if (!iface->ap_list)
+		return;
+
+	os_get_reltime(&now);
+
+	while (iface->ap_list) {
+		ap = iface->ap_list->prev;
+		if (!os_reltime_expired(&now, &ap->last_beacon,
+					iface->conf->ap_table_expiration_time))
+			break;
+
+		ap_free_ap(iface, ap);
+	}
+
+#ifdef CONFIG_24G_BW_SWITCH
+	/* yewei, if the number of neighbor aps is zero, need swtich to 40Mhz, 2024-01-04 */
+	ap = iface->ap_list;
+	if ((!ap) && (iface->secondary_ch) && (0 == iface->conf->secondary_channel) && 
+	    (iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) && 
+	    (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX) &&
+	    (iface->conf->auto_20m_40m)) {    
+        	iface->conf->secondary_channel = iface->secondary_ch;
+        	set_beacon++;
+			wpa_printf(MSG_INFO, "ap_list_timer, %s, switch_to_40", iface->phy);			
+    	}
+#endif /* CONFIG_24G_BW_SWITCH */
+
+#ifdef CONFIG_5G_BW_SWITCH
+	// 5G band 20M/40M/80M bandwidth adaptation. Added by Liangyu Chu
+	if (ap_list_5g_BW_switch(iface))
+		set_beacon++;
+
+	// Update max RSSI level
+	iface->max_rssi = 0;
+#endif /* CONFIG_5G_BW_SWITCH */
+
+	if (iface->olbc || iface->olbc_ht) {
+		int olbc = 0;
+		int olbc_ht = 0;
+
+		ap = iface->ap_list;
+		while (ap && (olbc == 0 || olbc_ht == 0)) {
+			if (ap_list_beacon_olbc(iface, ap))
+				olbc = 1;
+			if (!ap->ht_support)
+				olbc_ht = 1;
+			ap = ap->next;
+		}
+		if (!olbc && iface->olbc) {
+			wpa_printf(MSG_DEBUG, "OLBC not detected anymore");
+			iface->olbc = 0;
+			set_beacon++;
+		}
+		if (!olbc_ht && iface->olbc_ht) {
+			wpa_printf(MSG_DEBUG, "OLBC HT not detected anymore");
+			iface->olbc_ht = 0;
+			hostapd_ht_operation_update(iface);
+			set_beacon++;
+		}
+	}
+
+	if (set_beacon)
+		ieee802_11_update_beacons(iface);
+}
+
+
+int ap_list_init(struct hostapd_iface *iface)
+{
+	return 0;
+}
+
+
+void ap_list_deinit(struct hostapd_iface *iface)
+{
+	hostapd_free_aps(iface);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_list.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_list.h
new file mode 100644
index 0000000..02630b7
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_list.h
@@ -0,0 +1,77 @@
+/*
+ * hostapd / AP table
+ * Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2004, Instant802 Networks, Inc.
+ * Copyright (c) 2006, Devicescape Software, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef AP_LIST_H
+#define AP_LIST_H
+
+#ifdef CONFIG_5G_BW_SWITCH
+#define AP_LIST_MAX_NUM_STA_TO_20M 		12
+#define AP_LIST_MAX_NUM_STA_TO_40M 		6
+#define AP_LIST_MAX_RSSI_TO_20M			-30
+#define AP_LIST_MAX_RSSI_TO_40M			-50
+#endif /* CONFIG_5G_BW_SWITCH */
+
+struct ap_info {
+	/* Note: next/prev pointers are updated whenever a new beacon is
+	 * received because these are used to find the least recently used
+	 * entries. */
+	struct ap_info *next; /* next entry in AP list */
+	struct ap_info *prev; /* previous entry in AP list */
+	struct ap_info *hnext; /* next entry in hash table list */
+	u8 addr[6];
+	u8 supported_rates[WLAN_SUPP_RATES_MAX];
+	int erp; /* ERP Info or -1 if ERP info element not present */
+
+	// Add more AP Info for bandwidth adaptation. Added by Liangyu Chu.
+	int channel;
+#ifdef CONFIG_5G_BW_SWITCH
+	int ssi_signal;
+	int ch_width;
+#endif /* CONFIG_5G_BW_SWITCH */
+	int ht_support;
+#ifdef CONFIG_5G_BW_SWITCH
+	int vht_support;
+	int he_support;
+#endif /* CONFIG_5G_BW_SWITCH */
+
+	struct os_reltime last_beacon;
+};
+
+struct ieee802_11_elems;
+struct hostapd_frame_info;
+
+#if defined CONFIG_24G_BW_SWITCH || defined CONFIG_5G_BW_SWITCH
+void ap_list_process_beacon(struct hostapd_data *hapd,
+#else
+void ap_list_process_beacon(struct hostapd_iface *iface,
+#endif /* defined CONFIG_24G_BW_SWITCH || defined CONFIG_5G_BW_SWITCH */
+			    const struct ieee80211_mgmt *mgmt,
+			    struct ieee802_11_elems *elems,
+			    struct hostapd_frame_info *fi);
+#ifdef NEED_AP_MLME
+int ap_list_init(struct hostapd_iface *iface);
+void ap_list_deinit(struct hostapd_iface *iface);
+void ap_list_timer(struct hostapd_iface *iface);
+#else /* NEED_AP_MLME */
+static inline int ap_list_init(struct hostapd_iface *iface)
+{
+	return 0;
+}
+
+static inline void ap_list_deinit(struct hostapd_iface *iface)
+{
+}
+
+static inline void ap_list_timer(struct hostapd_iface *iface)
+{
+}
+#endif /* NEED_AP_MLME */
+
+#endif /* AP_LIST_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_mlme.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_mlme.c
new file mode 100644
index 0000000..309e69a
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_mlme.c
@@ -0,0 +1,191 @@
+/*
+ * hostapd / IEEE 802.11 MLME
+ * Copyright 2003-2006, Jouni Malinen <j@w1.fi>
+ * Copyright 2003-2004, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "ieee802_11.h"
+#include "wpa_auth.h"
+#include "sta_info.h"
+#include "ap_mlme.h"
+#include "hostapd.h"
+
+
+#ifndef CONFIG_NO_HOSTAPD_LOGGER
+static const char * mlme_auth_alg_str(int alg)
+{
+	switch (alg) {
+	case WLAN_AUTH_OPEN:
+		return "OPEN_SYSTEM";
+	case WLAN_AUTH_SHARED_KEY:
+		return "SHARED_KEY";
+	case WLAN_AUTH_FT:
+		return "FT";
+	default:
+		return "unknown";
+	}
+}
+#endif /* CONFIG_NO_HOSTAPD_LOGGER */
+
+
+/**
+ * mlme_authenticate_indication - Report the establishment of an authentication
+ * relationship with a specific peer MAC entity
+ * @hapd: BSS data
+ * @sta: peer STA data
+ *
+ * MLME calls this function as a result of the establishment of an
+ * authentication relationship with a specific peer MAC entity that
+ * resulted from an authentication procedure that was initiated by
+ * that specific peer MAC entity.
+ *
+ * PeerSTAAddress = sta->addr
+ * AuthenticationType = sta->auth_alg (WLAN_AUTH_OPEN / WLAN_AUTH_SHARED_KEY)
+ */
+void mlme_authenticate_indication(struct hostapd_data *hapd,
+				  struct sta_info *sta)
+{
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "MLME-AUTHENTICATE.indication(" MACSTR ", %s)",
+		       MAC2STR(sta->addr), mlme_auth_alg_str(sta->auth_alg));
+	if (sta->auth_alg != WLAN_AUTH_FT &&
+	    sta->auth_alg != WLAN_AUTH_FILS_SK &&
+	    sta->auth_alg != WLAN_AUTH_FILS_SK_PFS &&
+	    sta->auth_alg != WLAN_AUTH_FILS_PK &&
+	    !(sta->flags & WLAN_STA_MFP))
+		mlme_deletekeys_request(hapd, sta);
+	ap_sta_clear_disconnect_timeouts(hapd, sta);
+}
+
+
+/**
+ * mlme_deauthenticate_indication - Report the invalidation of an
+ * authentication relationship with a specific peer MAC entity
+ * @hapd: BSS data
+ * @sta: Peer STA data
+ * @reason_code: ReasonCode from Deauthentication frame
+ *
+ * MLME calls this function as a result of the invalidation of an
+ * authentication relationship with a specific peer MAC entity.
+ *
+ * PeerSTAAddress = sta->addr
+ */
+void mlme_deauthenticate_indication(struct hostapd_data *hapd,
+				    struct sta_info *sta, u16 reason_code)
+{
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "MLME-DEAUTHENTICATE.indication(" MACSTR ", %d)",
+		       MAC2STR(sta->addr), reason_code);
+	if (!hapd->iface->driver_ap_teardown)
+		mlme_deletekeys_request(hapd, sta);
+}
+
+
+/**
+ * mlme_associate_indication - Report the establishment of an association with
+ * a specific peer MAC entity
+ * @hapd: BSS data
+ * @sta: peer STA data
+ *
+ * MLME calls this function as a result of the establishment of an
+ * association with a specific peer MAC entity that resulted from an
+ * association procedure that was initiated by that specific peer MAC entity.
+ *
+ * PeerSTAAddress = sta->addr
+ */
+void mlme_associate_indication(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "MLME-ASSOCIATE.indication(" MACSTR ")",
+		       MAC2STR(sta->addr));
+	if (sta->auth_alg != WLAN_AUTH_FT &&
+	    sta->auth_alg != WLAN_AUTH_FILS_SK &&
+	    sta->auth_alg != WLAN_AUTH_FILS_SK_PFS &&
+	    sta->auth_alg != WLAN_AUTH_FILS_PK)
+		mlme_deletekeys_request(hapd, sta);
+	ap_sta_clear_disconnect_timeouts(hapd, sta);
+}
+
+
+/**
+ * mlme_reassociate_indication - Report the establishment of an reassociation
+ * with a specific peer MAC entity
+ * @hapd: BSS data
+ * @sta: peer STA data
+ *
+ * MLME calls this function as a result of the establishment of an
+ * reassociation with a specific peer MAC entity that resulted from a
+ * reassociation procedure that was initiated by that specific peer MAC entity.
+ *
+ * PeerSTAAddress = sta->addr
+ */
+void mlme_reassociate_indication(struct hostapd_data *hapd,
+				 struct sta_info *sta)
+{
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "MLME-REASSOCIATE.indication(" MACSTR ")",
+		       MAC2STR(sta->addr));
+	if (sta->auth_alg != WLAN_AUTH_FT &&
+	    sta->auth_alg != WLAN_AUTH_FILS_SK &&
+	    sta->auth_alg != WLAN_AUTH_FILS_SK_PFS &&
+	    sta->auth_alg != WLAN_AUTH_FILS_PK)
+		mlme_deletekeys_request(hapd, sta);
+	ap_sta_clear_disconnect_timeouts(hapd, sta);
+}
+
+
+/**
+ * mlme_disassociate_indication - Report disassociation with a specific peer
+ * MAC entity
+ * @hapd: BSS data
+ * @sta: Peer STA data
+ * @reason_code: ReasonCode from Disassociation frame
+ *
+ * MLME calls this function as a result of the invalidation of an association
+ * relationship with a specific peer MAC entity.
+ *
+ * PeerSTAAddress = sta->addr
+ */
+void mlme_disassociate_indication(struct hostapd_data *hapd,
+				  struct sta_info *sta, u16 reason_code)
+{
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "MLME-DISASSOCIATE.indication(" MACSTR ", %d)",
+		       MAC2STR(sta->addr), reason_code);
+	mlme_deletekeys_request(hapd, sta);
+}
+
+
+void mlme_michaelmicfailure_indication(struct hostapd_data *hapd,
+				       const u8 *addr)
+{
+	hostapd_logger(hapd, addr, HOSTAPD_MODULE_MLME,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "MLME-MichaelMICFailure.indication(" MACSTR ")",
+		       MAC2STR(addr));
+}
+
+
+void mlme_deletekeys_request(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "MLME-DELETEKEYS.request(" MACSTR ")",
+		       MAC2STR(sta->addr));
+
+	if (sta->wpa_sm)
+		wpa_remove_ptk(sta->wpa_sm);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_mlme.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_mlme.h
new file mode 100644
index 0000000..e7fd69d
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ap_mlme.h
@@ -0,0 +1,34 @@
+/*
+ * hostapd / IEEE 802.11 MLME
+ * Copyright 2003, Jouni Malinen <j@w1.fi>
+ * Copyright 2003-2004, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef MLME_H
+#define MLME_H
+
+void mlme_authenticate_indication(struct hostapd_data *hapd,
+				  struct sta_info *sta);
+
+void mlme_deauthenticate_indication(struct hostapd_data *hapd,
+				    struct sta_info *sta, u16 reason_code);
+
+void mlme_associate_indication(struct hostapd_data *hapd,
+			       struct sta_info *sta);
+
+void mlme_reassociate_indication(struct hostapd_data *hapd,
+				 struct sta_info *sta);
+
+void mlme_disassociate_indication(struct hostapd_data *hapd,
+				  struct sta_info *sta, u16 reason_code);
+
+void mlme_michaelmicfailure_indication(struct hostapd_data *hapd,
+				       const u8 *addr);
+
+void mlme_deletekeys_request(struct hostapd_data *hapd, struct sta_info *sta);
+
+#endif /* MLME_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/authsrv.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/authsrv.c
new file mode 100644
index 0000000..cc1d722
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/authsrv.c
@@ -0,0 +1,418 @@
+/*
+ * Authentication server setup
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "crypto/crypto.h"
+#include "crypto/tls.h"
+#include "eap_server/eap.h"
+#include "eap_server/eap_sim_db.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "radius/radius_server.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "sta_info.h"
+#include "authsrv.h"
+
+
+#if defined(EAP_SERVER_SIM) || defined(EAP_SERVER_AKA)
+#define EAP_SIM_DB
+#endif /* EAP_SERVER_SIM || EAP_SERVER_AKA */
+
+
+#ifdef EAP_SIM_DB
+static int hostapd_sim_db_cb_sta(struct hostapd_data *hapd,
+				 struct sta_info *sta, void *ctx)
+{
+	if (eapol_auth_eap_pending_cb(sta->eapol_sm, ctx) == 0)
+		return 1;
+	return 0;
+}
+
+
+static void hostapd_sim_db_cb(void *ctx, void *session_ctx)
+{
+	struct hostapd_data *hapd = ctx;
+	if (ap_for_each_sta(hapd, hostapd_sim_db_cb_sta, session_ctx) == 0) {
+#ifdef RADIUS_SERVER
+		radius_server_eap_pending_cb(hapd->radius_srv, session_ctx);
+#endif /* RADIUS_SERVER */
+	}
+}
+#endif /* EAP_SIM_DB */
+
+
+#ifdef RADIUS_SERVER
+
+static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity,
+				       size_t identity_len, int phase2,
+				       struct eap_user *user)
+{
+	const struct hostapd_eap_user *eap_user;
+	int i;
+	int rv = -1;
+
+	eap_user = hostapd_get_eap_user(ctx, identity, identity_len, phase2);
+	if (eap_user == NULL)
+		goto out;
+
+	if (user == NULL)
+		return 0;
+
+	os_memset(user, 0, sizeof(*user));
+	for (i = 0; i < EAP_MAX_METHODS; i++) {
+		user->methods[i].vendor = eap_user->methods[i].vendor;
+		user->methods[i].method = eap_user->methods[i].method;
+	}
+
+	if (eap_user->password) {
+		user->password = os_memdup(eap_user->password,
+					   eap_user->password_len);
+		if (user->password == NULL)
+			goto out;
+		user->password_len = eap_user->password_len;
+		user->password_hash = eap_user->password_hash;
+		if (eap_user->salt && eap_user->salt_len) {
+			user->salt = os_memdup(eap_user->salt,
+					       eap_user->salt_len);
+			if (!user->salt)
+				goto out;
+			user->salt_len = eap_user->salt_len;
+		}
+	}
+	user->force_version = eap_user->force_version;
+	user->macacl = eap_user->macacl;
+	user->ttls_auth = eap_user->ttls_auth;
+	user->remediation = eap_user->remediation;
+	user->accept_attr = eap_user->accept_attr;
+	user->t_c_timestamp = eap_user->t_c_timestamp;
+	rv = 0;
+
+out:
+	if (rv)
+		wpa_printf(MSG_DEBUG, "%s: Failed to find user", __func__);
+
+	return rv;
+}
+
+
+static int hostapd_setup_radius_srv(struct hostapd_data *hapd)
+{
+	struct radius_server_conf srv;
+	struct hostapd_bss_config *conf = hapd->conf;
+
+	if (hapd->mld_first_bss) {
+		wpa_printf(MSG_DEBUG,
+			   "MLD: Using RADIUS server of the first BSS");
+
+		hapd->radius_srv = hapd->mld_first_bss->radius_srv;
+		return 0;
+	}
+
+	os_memset(&srv, 0, sizeof(srv));
+	srv.client_file = conf->radius_server_clients;
+	srv.auth_port = conf->radius_server_auth_port;
+	srv.acct_port = conf->radius_server_acct_port;
+	srv.conf_ctx = hapd;
+	srv.ipv6 = conf->radius_server_ipv6;
+	srv.get_eap_user = hostapd_radius_get_eap_user;
+	srv.eap_req_id_text = conf->eap_req_id_text;
+	srv.eap_req_id_text_len = conf->eap_req_id_text_len;
+	srv.sqlite_file = conf->eap_user_sqlite;
+#ifdef CONFIG_RADIUS_TEST
+	srv.dump_msk_file = conf->dump_msk_file;
+#endif /* CONFIG_RADIUS_TEST */
+#ifdef CONFIG_HS20
+	srv.subscr_remediation_url = conf->subscr_remediation_url;
+	srv.subscr_remediation_method = conf->subscr_remediation_method;
+	srv.hs20_sim_provisioning_url = conf->hs20_sim_provisioning_url;
+	srv.t_c_server_url = conf->t_c_server_url;
+#endif /* CONFIG_HS20 */
+	srv.erp_domain = conf->erp_domain;
+	srv.eap_cfg = hapd->eap_cfg;
+
+	hapd->radius_srv = radius_server_init(&srv);
+	if (hapd->radius_srv == NULL) {
+		wpa_printf(MSG_ERROR, "RADIUS server initialization failed.");
+		return -1;
+	}
+
+	return 0;
+}
+
+#endif /* RADIUS_SERVER */
+
+
+#ifdef EAP_TLS_FUNCS
+static void authsrv_tls_event(void *ctx, enum tls_event ev,
+			      union tls_event_data *data)
+{
+	switch (ev) {
+	case TLS_CERT_CHAIN_SUCCESS:
+		wpa_printf(MSG_DEBUG, "authsrv: remote certificate verification success");
+		break;
+	case TLS_CERT_CHAIN_FAILURE:
+		wpa_printf(MSG_INFO, "authsrv: certificate chain failure: reason=%d depth=%d subject='%s' err='%s'",
+			   data->cert_fail.reason,
+			   data->cert_fail.depth,
+			   data->cert_fail.subject,
+			   data->cert_fail.reason_txt);
+		break;
+	case TLS_PEER_CERTIFICATE:
+		wpa_printf(MSG_DEBUG, "authsrv: peer certificate: depth=%d serial_num=%s subject=%s",
+			   data->peer_cert.depth,
+			   data->peer_cert.serial_num ? data->peer_cert.serial_num : "N/A",
+			   data->peer_cert.subject);
+		break;
+	case TLS_ALERT:
+		if (data->alert.is_local)
+			wpa_printf(MSG_DEBUG, "authsrv: local TLS alert: %s",
+				   data->alert.description);
+		else
+			wpa_printf(MSG_DEBUG, "authsrv: remote TLS alert: %s",
+				   data->alert.description);
+		break;
+	case TLS_UNSAFE_RENEGOTIATION_DISABLED:
+		/* Not applicable to TLS server */
+		break;
+	}
+}
+#endif /* EAP_TLS_FUNCS */
+
+
+static struct eap_config * authsrv_eap_config(struct hostapd_data *hapd)
+{
+	struct eap_config *cfg;
+
+	cfg = os_zalloc(sizeof(*cfg));
+	if (!cfg)
+		return NULL;
+
+	cfg->eap_server = hapd->conf->eap_server;
+	cfg->ssl_ctx = hapd->ssl_ctx;
+	cfg->msg_ctx = hapd->msg_ctx;
+	cfg->eap_sim_db_priv = hapd->eap_sim_db_priv;
+	cfg->tls_session_lifetime = hapd->conf->tls_session_lifetime;
+	cfg->tls_flags = hapd->conf->tls_flags;
+	cfg->max_auth_rounds = hapd->conf->max_auth_rounds;
+	cfg->max_auth_rounds_short = hapd->conf->max_auth_rounds_short;
+	if (hapd->conf->pac_opaque_encr_key)
+		cfg->pac_opaque_encr_key =
+			os_memdup(hapd->conf->pac_opaque_encr_key, 16);
+	if (hapd->conf->eap_fast_a_id) {
+		cfg->eap_fast_a_id = os_memdup(hapd->conf->eap_fast_a_id,
+					       hapd->conf->eap_fast_a_id_len);
+		cfg->eap_fast_a_id_len = hapd->conf->eap_fast_a_id_len;
+	}
+	if (hapd->conf->eap_fast_a_id_info)
+		cfg->eap_fast_a_id_info =
+			os_strdup(hapd->conf->eap_fast_a_id_info);
+	cfg->eap_fast_prov = hapd->conf->eap_fast_prov;
+	cfg->pac_key_lifetime = hapd->conf->pac_key_lifetime;
+	cfg->pac_key_refresh_time = hapd->conf->pac_key_refresh_time;
+	cfg->eap_teap_auth = hapd->conf->eap_teap_auth;
+	cfg->eap_teap_pac_no_inner = hapd->conf->eap_teap_pac_no_inner;
+	cfg->eap_teap_separate_result = hapd->conf->eap_teap_separate_result;
+	cfg->eap_teap_id = hapd->conf->eap_teap_id;
+	cfg->eap_teap_method_sequence = hapd->conf->eap_teap_method_sequence;
+	cfg->eap_sim_aka_result_ind = hapd->conf->eap_sim_aka_result_ind;
+	cfg->eap_sim_id = hapd->conf->eap_sim_id;
+	cfg->imsi_privacy_key = hapd->imsi_privacy_key;
+	cfg->tnc = hapd->conf->tnc;
+	cfg->wps = hapd->wps;
+	cfg->fragment_size = hapd->conf->fragment_size;
+	cfg->pwd_group = hapd->conf->pwd_group;
+	cfg->pbc_in_m1 = hapd->conf->pbc_in_m1;
+	if (hapd->conf->server_id) {
+		cfg->server_id = (u8 *) os_strdup(hapd->conf->server_id);
+		cfg->server_id_len = os_strlen(hapd->conf->server_id);
+	} else {
+		cfg->server_id = (u8 *) os_strdup("hostapd");
+		cfg->server_id_len = 7;
+	}
+	cfg->erp = hapd->conf->eap_server_erp;
+#ifdef CONFIG_TESTING_OPTIONS
+	cfg->skip_prot_success = hapd->conf->eap_skip_prot_success;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	return cfg;
+}
+
+
+int authsrv_init(struct hostapd_data *hapd)
+{
+	if (hapd->mld_first_bss) {
+		wpa_printf(MSG_DEBUG, "MLD: Using auth_serv of the first BSS");
+
+#ifdef EAP_TLS_FUNCS
+		hapd->ssl_ctx = hapd->mld_first_bss->ssl_ctx;
+#endif /* EAP_TLS_FUNCS */
+		hapd->eap_cfg = hapd->mld_first_bss->eap_cfg;
+#ifdef EAP_SIM_DB
+		hapd->eap_sim_db_priv = hapd->mld_first_bss->eap_sim_db_priv;
+#endif /* EAP_SIM_DB */
+		return 0;
+	}
+
+#ifdef EAP_TLS_FUNCS
+	if (hapd->conf->eap_server &&
+	    (hapd->conf->ca_cert || hapd->conf->server_cert ||
+	     hapd->conf->private_key || hapd->conf->dh_file ||
+	     hapd->conf->server_cert2 || hapd->conf->private_key2)) {
+		struct tls_config conf;
+		struct tls_connection_params params;
+
+		os_memset(&conf, 0, sizeof(conf));
+		conf.tls_session_lifetime = hapd->conf->tls_session_lifetime;
+		if (hapd->conf->crl_reload_interval > 0 &&
+		    hapd->conf->check_crl <= 0) {
+			wpa_printf(MSG_INFO,
+				   "Cannot enable CRL reload functionality - it depends on check_crl being set");
+		} else if (hapd->conf->crl_reload_interval > 0) {
+			conf.crl_reload_interval =
+				hapd->conf->crl_reload_interval;
+			wpa_printf(MSG_INFO,
+				   "Enabled CRL reload functionality");
+		}
+		conf.tls_flags = hapd->conf->tls_flags;
+		conf.event_cb = authsrv_tls_event;
+		conf.cb_ctx = hapd;
+		hapd->ssl_ctx = tls_init(&conf);
+		if (hapd->ssl_ctx == NULL) {
+			wpa_printf(MSG_ERROR, "Failed to initialize TLS");
+			authsrv_deinit(hapd);
+			return -1;
+		}
+
+		os_memset(&params, 0, sizeof(params));
+		params.ca_cert = hapd->conf->ca_cert;
+		params.client_cert = hapd->conf->server_cert;
+		params.client_cert2 = hapd->conf->server_cert2;
+		params.private_key = hapd->conf->private_key;
+		params.private_key2 = hapd->conf->private_key2;
+		params.private_key_passwd = hapd->conf->private_key_passwd;
+		params.private_key_passwd2 = hapd->conf->private_key_passwd2;
+		params.dh_file = hapd->conf->dh_file;
+		params.openssl_ciphers = hapd->conf->openssl_ciphers;
+		params.openssl_ecdh_curves = hapd->conf->openssl_ecdh_curves;
+		params.ocsp_stapling_response =
+			hapd->conf->ocsp_stapling_response;
+		params.ocsp_stapling_response_multi =
+			hapd->conf->ocsp_stapling_response_multi;
+		params.check_cert_subject = hapd->conf->check_cert_subject;
+
+		if (tls_global_set_params(hapd->ssl_ctx, &params)) {
+			wpa_printf(MSG_ERROR, "Failed to set TLS parameters");
+			authsrv_deinit(hapd);
+			return -1;
+		}
+
+		if (tls_global_set_verify(hapd->ssl_ctx,
+					  hapd->conf->check_crl,
+					  hapd->conf->check_crl_strict)) {
+			wpa_printf(MSG_ERROR, "Failed to enable check_crl");
+			authsrv_deinit(hapd);
+			return -1;
+		}
+	}
+#endif /* EAP_TLS_FUNCS */
+
+#ifdef CRYPTO_RSA_OAEP_SHA256
+	crypto_rsa_key_free(hapd->imsi_privacy_key);
+	hapd->imsi_privacy_key = NULL;
+	if (hapd->conf->imsi_privacy_key) {
+		hapd->imsi_privacy_key = crypto_rsa_key_read(
+			hapd->conf->imsi_privacy_key, true);
+		if (!hapd->imsi_privacy_key) {
+			wpa_printf(MSG_ERROR,
+				   "Failed to read/parse IMSI privacy key %s",
+				   hapd->conf->imsi_privacy_key);
+			authsrv_deinit(hapd);
+			return -1;
+		}
+	}
+#endif /* CRYPTO_RSA_OAEP_SHA256 */
+
+#ifdef EAP_SIM_DB
+	if (hapd->conf->eap_sim_db) {
+		hapd->eap_sim_db_priv =
+			eap_sim_db_init(hapd->conf->eap_sim_db,
+					hapd->conf->eap_sim_db_timeout,
+					hostapd_sim_db_cb, hapd);
+		if (hapd->eap_sim_db_priv == NULL) {
+			wpa_printf(MSG_ERROR, "Failed to initialize EAP-SIM "
+				   "database interface");
+			authsrv_deinit(hapd);
+			return -1;
+		}
+	}
+#endif /* EAP_SIM_DB */
+
+	hapd->eap_cfg = authsrv_eap_config(hapd);
+	if (!hapd->eap_cfg) {
+		wpa_printf(MSG_ERROR,
+			   "Failed to build EAP server configuration");
+		authsrv_deinit(hapd);
+		return -1;
+	}
+
+#ifdef RADIUS_SERVER
+	if (hapd->conf->radius_server_clients &&
+	    hostapd_setup_radius_srv(hapd))
+		return -1;
+#endif /* RADIUS_SERVER */
+
+	return 0;
+}
+
+
+void authsrv_deinit(struct hostapd_data *hapd)
+{
+	if (hapd->mld_first_bss) {
+		wpa_printf(MSG_DEBUG,
+			   "MLD: Deinit auth_serv of a non-first BSS");
+
+		hapd->radius_srv = NULL;
+		hapd->eap_cfg = NULL;
+#ifdef EAP_SIM_DB
+		hapd->eap_sim_db_priv = NULL;
+#endif /* EAP_SIM_DB */
+#ifdef EAP_TLS_FUNCS
+		hapd->ssl_ctx = NULL;
+#endif /* EAP_TLS_FUNCS */
+		return;
+	}
+
+#ifdef RADIUS_SERVER
+	radius_server_deinit(hapd->radius_srv);
+	hapd->radius_srv = NULL;
+#endif /* RADIUS_SERVER */
+
+#ifdef CRYPTO_RSA_OAEP_SHA256
+	crypto_rsa_key_free(hapd->imsi_privacy_key);
+	hapd->imsi_privacy_key = NULL;
+#endif /* CRYPTO_RSA_OAEP_SHA256 */
+
+#ifdef EAP_TLS_FUNCS
+	if (hapd->ssl_ctx) {
+		tls_deinit(hapd->ssl_ctx);
+		hapd->ssl_ctx = NULL;
+	}
+#endif /* EAP_TLS_FUNCS */
+
+#ifdef EAP_SIM_DB
+	if (hapd->eap_sim_db_priv) {
+		eap_sim_db_deinit(hapd->eap_sim_db_priv);
+		hapd->eap_sim_db_priv = NULL;
+	}
+#endif /* EAP_SIM_DB */
+
+	eap_server_config_free(hapd->eap_cfg);
+	hapd->eap_cfg = NULL;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/authsrv.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/authsrv.h
new file mode 100644
index 0000000..2f4ed34
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/authsrv.h
@@ -0,0 +1,15 @@
+/*
+ * Authentication server setup
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef AUTHSRV_H
+#define AUTHSRV_H
+
+int authsrv_init(struct hostapd_data *hapd);
+void authsrv_deinit(struct hostapd_data *hapd);
+
+#endif /* AUTHSRV_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/beacon.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/beacon.c
new file mode 100644
index 0000000..78b3476
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/beacon.c
@@ -0,0 +1,2285 @@
+/*
+ * hostapd / IEEE 802.11 Management: Beacon and Probe Request/Response
+ * Copyright (c) 2002-2004, Instant802 Networks, Inc.
+ * Copyright (c) 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#ifndef CONFIG_NATIVE_WINDOWS
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/hw_features_common.h"
+#include "common/wpa_ctrl.h"
+#include "wps/wps_defs.h"
+#include "p2p/p2p.h"
+#include "hostapd.h"
+#include "ieee802_11.h"
+#include "wpa_auth.h"
+#include "wmm.h"
+#include "ap_config.h"
+#include "sta_info.h"
+#include "p2p_hostapd.h"
+#include "ap_drv_ops.h"
+#include "beacon.h"
+#include "hs20.h"
+#include "dfs.h"
+#include "taxonomy.h"
+#include "ieee802_11_auth.h"
+
+
+#ifdef NEED_AP_MLME
+
+static u8 * hostapd_eid_bss_load(struct hostapd_data *hapd, u8 *eid, size_t len)
+{
+	if (len < 2 + 5)
+		return eid;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (hapd->conf->bss_load_test_set) {
+		*eid++ = WLAN_EID_BSS_LOAD;
+		*eid++ = 5;
+		os_memcpy(eid, hapd->conf->bss_load_test, 5);
+		eid += 5;
+		return eid;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+	if (hapd->conf->bss_load_update_period) {
+		*eid++ = WLAN_EID_BSS_LOAD;
+		*eid++ = 5;
+		WPA_PUT_LE16(eid, hapd->num_sta);
+		eid += 2;
+		*eid++ = hapd->iface->channel_utilization;
+		WPA_PUT_LE16(eid, 0); /* no available admission capabity */
+		eid += 2;
+	}
+	return eid;
+}
+
+
+static u8 ieee802_11_erp_info(struct hostapd_data *hapd)
+{
+	u8 erp = 0;
+
+	if (hapd->iface->current_mode == NULL ||
+	    hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
+		return 0;
+
+	if (hapd->iface->olbc)
+		erp |= ERP_INFO_USE_PROTECTION;
+	if (hapd->iface->num_sta_non_erp > 0) {
+		erp |= ERP_INFO_NON_ERP_PRESENT |
+			ERP_INFO_USE_PROTECTION;
+	}
+	if (hapd->iface->num_sta_no_short_preamble > 0 ||
+	    hapd->iconf->preamble == LONG_PREAMBLE)
+		erp |= ERP_INFO_BARKER_PREAMBLE_MODE;
+
+	return erp;
+}
+
+
+static u8 * hostapd_eid_ds_params(struct hostapd_data *hapd, u8 *eid)
+{
+	*eid++ = WLAN_EID_DS_PARAMS;
+	*eid++ = 1;
+	*eid++ = hapd->iconf->channel;
+	return eid;
+}
+
+
+static u8 * hostapd_eid_erp_info(struct hostapd_data *hapd, u8 *eid)
+{
+	if (hapd->iface->current_mode == NULL ||
+	    hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
+		return eid;
+
+	/* Set NonERP_present and use_protection bits if there
+	 * are any associated NonERP stations. */
+	/* TODO: use_protection bit can be set to zero even if
+	 * there are NonERP stations present. This optimization
+	 * might be useful if NonERP stations are "quiet".
+	 * See 802.11g/D6 E-1 for recommended practice.
+	 * In addition, Non ERP present might be set, if AP detects Non ERP
+	 * operation on other APs. */
+
+	/* Add ERP Information element */
+	*eid++ = WLAN_EID_ERP_INFO;
+	*eid++ = 1;
+	*eid++ = ieee802_11_erp_info(hapd);
+
+	return eid;
+}
+
+
+static u8 * hostapd_eid_pwr_constraint(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 *pos = eid;
+	u8 local_pwr_constraint = 0;
+	int dfs;
+
+	if (hapd->iface->current_mode == NULL ||
+	    hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
+		return eid;
+
+	/* Let host drivers add this IE if DFS support is offloaded */
+	if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
+		return eid;
+
+	/*
+	 * There is no DFS support and power constraint was not directly
+	 * requested by config option.
+	 */
+	if (!hapd->iconf->ieee80211h &&
+	    hapd->iconf->local_pwr_constraint == -1)
+		return eid;
+
+	/* Check if DFS is required by regulatory. */
+	dfs = hostapd_is_dfs_required(hapd->iface);
+	if (dfs < 0) {
+		wpa_printf(MSG_WARNING, "Failed to check if DFS is required; ret=%d",
+			   dfs);
+		dfs = 0;
+	}
+
+	if (dfs == 0 && hapd->iconf->local_pwr_constraint == -1)
+		return eid;
+
+	/*
+	 * ieee80211h (DFS) is enabled so Power Constraint element shall
+	 * be added when running on DFS channel whenever local_pwr_constraint
+	 * is configured or not. In order to meet regulations when TPC is not
+	 * implemented using a transmit power that is below the legal maximum
+	 * (including any mitigation factor) should help. In this case,
+	 * indicate 3 dB below maximum allowed transmit power.
+	 */
+	if (hapd->iconf->local_pwr_constraint == -1)
+		local_pwr_constraint = 3;
+
+	/*
+	 * A STA that is not an AP shall use a transmit power less than or
+	 * equal to the local maximum transmit power level for the channel.
+	 * The local maximum transmit power can be calculated from the formula:
+	 * local max TX pwr = max TX pwr - local pwr constraint
+	 * Where max TX pwr is maximum transmit power level specified for
+	 * channel in Country element and local pwr constraint is specified
+	 * for channel in this Power Constraint element.
+	 */
+
+	/* Element ID */
+	*pos++ = WLAN_EID_PWR_CONSTRAINT;
+	/* Length */
+	*pos++ = 1;
+	/* Local Power Constraint */
+	if (local_pwr_constraint)
+		*pos++ = local_pwr_constraint;
+	else
+		*pos++ = hapd->iconf->local_pwr_constraint;
+
+	return pos;
+}
+
+
+static u8 * hostapd_eid_country_add(struct hostapd_data *hapd, u8 *pos,
+				    u8 *end, int chan_spacing,
+				    struct hostapd_channel_data *start,
+				    struct hostapd_channel_data *prev)
+{
+	if (end - pos < 3)
+		return pos;
+
+	/* first channel number */
+	*pos++ = start->chan;
+	/* number of channels */
+	*pos++ = (prev->chan - start->chan) / chan_spacing + 1;
+	/* maximum transmit power level */
+	if (!is_6ghz_op_class(hapd->iconf->op_class))
+		*pos++ = start->max_tx_power;
+	else
+		*pos++ = 0; /* Reserved when operating on the 6 GHz band */
+
+	return pos;
+}
+
+
+static u8 * hostapd_fill_subband_triplets(struct hostapd_data *hapd, u8 *pos,
+					    u8 *end)
+{
+	int i;
+	struct hostapd_hw_modes *mode;
+	struct hostapd_channel_data *start, *prev;
+	int chan_spacing = 1;
+
+	mode = hapd->iface->current_mode;
+	if (mode->mode == HOSTAPD_MODE_IEEE80211A)
+		chan_spacing = 4;
+
+	start = prev = NULL;
+	for (i = 0; i < mode->num_channels; i++) {
+		struct hostapd_channel_data *chan = &mode->channels[i];
+		if (chan->flag & HOSTAPD_CHAN_DISABLED)
+			continue;
+		if (start && prev &&
+		    prev->chan + chan_spacing == chan->chan &&
+		    start->max_tx_power == chan->max_tx_power) {
+			prev = chan;
+			continue; /* can use same entry */
+		}
+
+		if (start && prev) {
+			pos = hostapd_eid_country_add(hapd, pos, end,
+						      chan_spacing,
+						      start, prev);
+			start = NULL;
+		}
+
+		/* Start new group */
+		start = prev = chan;
+	}
+
+	if (start) {
+		pos = hostapd_eid_country_add(hapd, pos, end, chan_spacing,
+					      start, prev);
+	}
+
+	return pos;
+}
+
+
+static u8 * hostapd_eid_country(struct hostapd_data *hapd, u8 *eid,
+				int max_len)
+{
+	u8 *pos = eid;
+	u8 *end = eid + max_len;
+
+	if (!hapd->iconf->ieee80211d || max_len < 6 ||
+	    hapd->iface->current_mode == NULL)
+		return eid;
+
+	*pos++ = WLAN_EID_COUNTRY;
+	pos++; /* length will be set later */
+	os_memcpy(pos, hapd->iconf->country, 3); /* e.g., 'US ' */
+	pos += 3;
+
+	if (is_6ghz_op_class(hapd->iconf->op_class)) {
+		/* Force the third octet of the country string to indicate
+		 * Global Operating Class (Table E-4) */
+		eid[4] = 0x04;
+
+		/* Operating Triplet field */
+		/* Operating Extension Identifier (>= 201 to indicate this is
+		 * not a Subband Triplet field) */
+		*pos++ = 201;
+		/* Operating Class */
+		*pos++ = hapd->iconf->op_class;
+		/* Coverage Class */
+		*pos++ = 0;
+		/* Subband Triplets are required only for the 20 MHz case */
+		if (hapd->iconf->op_class == 131 ||
+		    hapd->iconf->op_class == 136)
+			pos = hostapd_fill_subband_triplets(hapd, pos, end);
+	} else {
+		pos = hostapd_fill_subband_triplets(hapd, pos, end);
+	}
+
+	if ((pos - eid) & 1) {
+		if (end - pos < 1)
+			return eid;
+		*pos++ = 0; /* pad for 16-bit alignment */
+	}
+
+	eid[1] = (pos - eid) - 2;
+
+	return pos;
+}
+
+
+const u8 * hostapd_wpa_ie(struct hostapd_data *hapd, u8 eid)
+{
+	const u8 *ies;
+	size_t ies_len;
+
+	ies = wpa_auth_get_wpa_ie(hapd->wpa_auth, &ies_len);
+	if (!ies)
+		return NULL;
+
+	return get_ie(ies, ies_len, eid);
+}
+
+
+static const u8 * hostapd_vendor_wpa_ie(struct hostapd_data *hapd,
+					u32 vendor_type)
+{
+	const u8 *ies;
+	size_t ies_len;
+
+	ies = wpa_auth_get_wpa_ie(hapd->wpa_auth, &ies_len);
+	if (!ies)
+		return NULL;
+
+	return get_vendor_ie(ies, ies_len, vendor_type);
+}
+
+
+static u8 * hostapd_get_rsne(struct hostapd_data *hapd, u8 *pos, size_t len)
+{
+	const u8 *ie;
+
+	ie = hostapd_wpa_ie(hapd, WLAN_EID_RSN);
+	if (!ie || 2U + ie[1] > len)
+		return pos;
+
+	os_memcpy(pos, ie, 2 + ie[1]);
+	return pos + 2 + ie[1];
+}
+
+
+static u8 * hostapd_get_mde(struct hostapd_data *hapd, u8 *pos, size_t len)
+{
+	const u8 *ie;
+
+	ie = hostapd_wpa_ie(hapd, WLAN_EID_MOBILITY_DOMAIN);
+	if (!ie || 2U + ie[1] > len)
+		return pos;
+
+	os_memcpy(pos, ie, 2 + ie[1]);
+	return pos + 2 + ie[1];
+}
+
+
+static u8 * hostapd_get_rsnxe(struct hostapd_data *hapd, u8 *pos, size_t len)
+{
+	const u8 *ie;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (hapd->conf->no_beacon_rsnxe) {
+		wpa_printf(MSG_INFO, "TESTING: Do not add RSNXE into Beacon");
+		return pos;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+	ie = hostapd_wpa_ie(hapd, WLAN_EID_RSNX);
+	if (!ie || 2U + ie[1] > len)
+		return pos;
+
+	os_memcpy(pos, ie, 2 + ie[1]);
+	return pos + 2 + ie[1];
+}
+
+
+static u8 * hostapd_get_wpa_ie(struct hostapd_data *hapd, u8 *pos, size_t len)
+{
+	const u8 *ie;
+
+	ie = hostapd_vendor_wpa_ie(hapd, WPA_IE_VENDOR_TYPE);
+	if (!ie || 2U + ie[1] > len)
+		return pos;
+
+	os_memcpy(pos, ie, 2 + ie[1]);
+	return pos + 2 + ie[1];
+}
+
+
+static u8 * hostapd_get_osen_ie(struct hostapd_data *hapd, u8 *pos, size_t len)
+{
+	const u8 *ie;
+
+	ie = hostapd_vendor_wpa_ie(hapd, OSEN_IE_VENDOR_TYPE);
+	if (!ie || 2U + ie[1] > len)
+		return pos;
+
+	os_memcpy(pos, ie, 2 + ie[1]);
+	return pos + 2 + ie[1];
+}
+
+
+static u8 * hostapd_eid_csa(struct hostapd_data *hapd, u8 *eid)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+	if (hapd->iface->cs_oper_class && hapd->iconf->ecsa_ie_only)
+		return eid;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	if (!hapd->cs_freq_params.channel)
+		return eid;
+
+	*eid++ = WLAN_EID_CHANNEL_SWITCH;
+	*eid++ = 3;
+	*eid++ = hapd->cs_block_tx;
+	*eid++ = hapd->cs_freq_params.channel;
+	*eid++ = hapd->cs_count;
+
+	return eid;
+}
+
+
+static u8 * hostapd_eid_ecsa(struct hostapd_data *hapd, u8 *eid)
+{
+	if (!hapd->cs_freq_params.channel || !hapd->iface->cs_oper_class)
+		return eid;
+
+	*eid++ = WLAN_EID_EXT_CHANSWITCH_ANN;
+	*eid++ = 4;
+	*eid++ = hapd->cs_block_tx;
+	*eid++ = hapd->iface->cs_oper_class;
+	*eid++ = hapd->cs_freq_params.channel;
+	*eid++ = hapd->cs_count;
+
+	return eid;
+}
+
+
+static u8 * hostapd_eid_supported_op_classes(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 op_class, channel;
+
+	if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA) ||
+	    !hapd->iface->freq)
+		return eid;
+
+	if (ieee80211_freq_to_channel_ext(hapd->iface->freq,
+					  hapd->iconf->secondary_channel,
+					  hostapd_get_oper_chwidth(hapd->iconf),
+					  &op_class, &channel) ==
+	    NUM_HOSTAPD_MODES)
+		return eid;
+
+	*eid++ = WLAN_EID_SUPPORTED_OPERATING_CLASSES;
+	*eid++ = 2;
+
+	/* Current Operating Class */
+	*eid++ = op_class;
+
+	/* TODO: Advertise all the supported operating classes */
+	*eid++ = 0;
+
+	return eid;
+}
+
+
+static int
+ieee802_11_build_ap_params_mbssid(struct hostapd_data *hapd,
+				  struct wpa_driver_ap_params *params)
+{
+	struct hostapd_iface *iface = hapd->iface;
+	struct hostapd_data *tx_bss;
+	size_t len, rnr_len = 0;
+	u8 elem_count = 0, *elem = NULL, **elem_offset = NULL, *end;
+	u8 rnr_elem_count = 0, *rnr_elem = NULL, **rnr_elem_offset = NULL;
+
+	if (!iface->mbssid_max_interfaces ||
+	    iface->num_bss > iface->mbssid_max_interfaces ||
+	    (iface->conf->mbssid == ENHANCED_MBSSID_ENABLED &&
+	     !iface->ema_max_periodicity))
+		goto fail;
+
+	tx_bss = hostapd_mbssid_get_tx_bss(hapd);
+	len = hostapd_eid_mbssid_len(tx_bss, WLAN_FC_STYPE_BEACON, &elem_count,
+				     NULL, 0, &rnr_len);
+	if (!len || (iface->conf->mbssid == ENHANCED_MBSSID_ENABLED &&
+		     elem_count > iface->ema_max_periodicity))
+		goto fail;
+
+	elem = os_zalloc(len);
+	if (!elem)
+		goto fail;
+
+	elem_offset = os_zalloc(elem_count * sizeof(u8 *));
+	if (!elem_offset)
+		goto fail;
+
+	if (rnr_len) {
+		rnr_elem = os_zalloc(rnr_len);
+		if (!rnr_elem)
+			goto fail;
+
+		rnr_elem_offset = os_calloc(elem_count + 1, sizeof(u8 *));
+		if (!rnr_elem_offset)
+			goto fail;
+	}
+
+	end = hostapd_eid_mbssid(tx_bss, elem, elem + len, WLAN_FC_STYPE_BEACON,
+				 elem_count, elem_offset, NULL, 0, rnr_elem,
+				 &rnr_elem_count, rnr_elem_offset, rnr_len);
+
+	params->mbssid_tx_iface = tx_bss->conf->iface;
+	params->mbssid_index = hostapd_mbssid_get_bss_index(hapd);
+	params->mbssid_elem = elem;
+	params->mbssid_elem_len = end - elem;
+	params->mbssid_elem_count = elem_count;
+	params->mbssid_elem_offset = elem_offset;
+	params->rnr_elem = rnr_elem;
+	params->rnr_elem_len = rnr_len;
+	params->rnr_elem_count = rnr_elem_count;
+	params->rnr_elem_offset = rnr_elem_offset;
+	if (iface->conf->mbssid == ENHANCED_MBSSID_ENABLED)
+		params->ema = true;
+
+	return 0;
+
+fail:
+	os_free(rnr_elem);
+	os_free(rnr_elem_offset);
+	os_free(elem_offset);
+	os_free(elem);
+	wpa_printf(MSG_ERROR, "MBSSID: Configuration failed");
+	return -1;
+}
+
+
+static u8 * hostapd_eid_mbssid_config(struct hostapd_data *hapd, u8 *eid,
+				      u8 mbssid_elem_count)
+{
+	struct hostapd_iface *iface = hapd->iface;
+
+	if (iface->conf->mbssid == ENHANCED_MBSSID_ENABLED) {
+		*eid++ = WLAN_EID_EXTENSION;
+		*eid++ = 3;
+		*eid++ = WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION;
+		*eid++ = iface->num_bss;
+		*eid++ = mbssid_elem_count;
+	}
+
+	return eid;
+}
+
+
+static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
+				   const struct ieee80211_mgmt *req,
+				   int is_p2p, size_t *resp_len,
+				   const u8 *known_bss, u8 known_bss_len)
+{
+	struct ieee80211_mgmt *resp;
+	u8 *pos, *epos, *csa_pos;
+	size_t buflen;
+
+	hapd = hostapd_mbssid_get_tx_bss(hapd);
+
+#define MAX_PROBERESP_LEN 768
+	buflen = MAX_PROBERESP_LEN;
+#ifdef CONFIG_WPS
+	if (hapd->wps_probe_resp_ie)
+		buflen += wpabuf_len(hapd->wps_probe_resp_ie);
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+	if (hapd->p2p_probe_resp_ie)
+		buflen += wpabuf_len(hapd->p2p_probe_resp_ie);
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_FST
+	if (hapd->iface->fst_ies)
+		buflen += wpabuf_len(hapd->iface->fst_ies);
+#endif /* CONFIG_FST */
+	if (hapd->conf->vendor_elements)
+		buflen += wpabuf_len(hapd->conf->vendor_elements);
+	if (hapd->conf->vendor_vht) {
+		buflen += 5 + 2 + sizeof(struct ieee80211_vht_capabilities) +
+			2 + sizeof(struct ieee80211_vht_operation);
+	}
+
+#ifdef CONFIG_IEEE80211AX
+	if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
+		buflen += 3 + sizeof(struct ieee80211_he_capabilities) +
+			3 + sizeof(struct ieee80211_he_operation) +
+			3 + sizeof(struct ieee80211_he_mu_edca_parameter_set) +
+			3 + sizeof(struct ieee80211_spatial_reuse);
+		if (is_6ghz_op_class(hapd->iconf->op_class)) {
+			buflen += sizeof(struct ieee80211_he_6ghz_oper_info) +
+				3 + sizeof(struct ieee80211_he_6ghz_band_cap);
+			 /* An additional Transmit Power Envelope element for
+			  * subordinate client */
+			if (hapd->iconf->he_6ghz_reg_pwr_type ==
+			    HE_6GHZ_INDOOR_AP)
+				buflen += 4;
+		}
+	}
+#endif /* CONFIG_IEEE80211AX */
+
+#ifdef CONFIG_IEEE80211BE
+	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;
+
+		/*
+		 * TODO: Multi-Link element has variable length and can be
+		 * long based on the common info and number of per
+		 * station profiles. For now use 256.
+		 */
+		if (hapd->conf->mld_ap)
+			buflen += 256;
+	}
+#endif /* CONFIG_IEEE80211BE */
+
+	buflen += hostapd_eid_mbssid_len(hapd, WLAN_FC_STYPE_PROBE_RESP, NULL,
+					 known_bss, known_bss_len, NULL);
+	buflen += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_PROBE_RESP);
+	buflen += hostapd_mbo_ie_len(hapd);
+	buflen += hostapd_eid_owe_trans_len(hapd);
+	buflen += hostapd_eid_dpp_cc_len(hapd);
+
+	resp = os_zalloc(buflen);
+	if (resp == NULL)
+		return NULL;
+
+	epos = ((u8 *) resp) + MAX_PROBERESP_LEN;
+
+	resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					   WLAN_FC_STYPE_PROBE_RESP);
+	/* 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
+		os_memset(resp->da, 0xff, ETH_ALEN);
+
+	os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
+
+	os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
+	resp->u.probe_resp.beacon_int =
+		host_to_le16(hapd->iconf->beacon_int);
+
+	/* hardware or low-level driver will setup seq_ctrl and timestamp */
+	resp->u.probe_resp.capab_info =
+		host_to_le16(hostapd_own_capab_info(hapd));
+
+	pos = resp->u.probe_resp.variable;
+	*pos++ = WLAN_EID_SSID;
+	*pos++ = hapd->conf->ssid.ssid_len;
+	os_memcpy(pos, hapd->conf->ssid.ssid, hapd->conf->ssid.ssid_len);
+	pos += hapd->conf->ssid.ssid_len;
+
+	/* Supported rates */
+	pos = hostapd_eid_supp_rates(hapd, pos);
+
+	/* DS Params */
+	pos = hostapd_eid_ds_params(hapd, pos);
+
+	pos = hostapd_eid_country(hapd, pos, epos - pos);
+
+	/* Power Constraint element */
+	pos = hostapd_eid_pwr_constraint(hapd, pos);
+
+	/* CSA IE */
+	csa_pos = hostapd_eid_csa(hapd, pos);
+	if (csa_pos != pos)
+		hapd->cs_c_off_proberesp = csa_pos - (u8 *) resp - 1;
+	pos = csa_pos;
+
+	/* ERP Information element */
+	pos = hostapd_eid_erp_info(hapd, pos);
+
+	/* Extended supported rates */
+	pos = hostapd_eid_ext_supp_rates(hapd, pos);
+
+	pos = hostapd_get_rsne(hapd, pos, epos - pos);
+	pos = hostapd_eid_bss_load(hapd, pos, epos - pos);
+	pos = hostapd_eid_mbssid(hapd, pos, epos, WLAN_FC_STYPE_PROBE_RESP, 0,
+				 NULL, known_bss, known_bss_len, NULL, NULL,
+				 NULL, 0);
+	pos = hostapd_eid_rm_enabled_capab(hapd, pos, epos - pos);
+	pos = hostapd_get_mde(hapd, pos, epos - pos);
+
+	/* eCSA IE */
+	csa_pos = hostapd_eid_ecsa(hapd, pos);
+	if (csa_pos != pos)
+		hapd->cs_c_off_ecsa_proberesp = csa_pos - (u8 *) resp - 1;
+	pos = csa_pos;
+
+	pos = hostapd_eid_supported_op_classes(hapd, pos);
+	pos = hostapd_eid_ht_capabilities(hapd, pos);
+	pos = hostapd_eid_ht_operation(hapd, pos);
+
+	/* Probe Response frames always include all non-TX profiles except
+	 * when a list of known BSSes is included in the Probe Request frame. */
+	pos = hostapd_eid_ext_capab(hapd, pos,
+				    hapd->iconf->mbssid >= MBSSID_ENABLED &&
+				    !known_bss_len);
+
+	pos = hostapd_eid_time_adv(hapd, pos);
+	pos = hostapd_eid_time_zone(hapd, pos);
+
+	pos = hostapd_eid_interworking(hapd, pos);
+	pos = hostapd_eid_adv_proto(hapd, pos);
+	pos = hostapd_eid_roaming_consortium(hapd, pos);
+
+#ifdef CONFIG_FST
+	if (hapd->iface->fst_ies) {
+		os_memcpy(pos, wpabuf_head(hapd->iface->fst_ies),
+			  wpabuf_len(hapd->iface->fst_ies));
+		pos += wpabuf_len(hapd->iface->fst_ies);
+	}
+#endif /* CONFIG_FST */
+
+#ifdef CONFIG_IEEE80211AC
+	if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac &&
+	    !is_6ghz_op_class(hapd->iconf->op_class)) {
+		pos = hostapd_eid_vht_capabilities(hapd, pos, 0);
+		pos = hostapd_eid_vht_operation(hapd, pos);
+		pos = hostapd_eid_txpower_envelope(hapd, pos);
+	}
+#endif /* CONFIG_IEEE80211AC */
+
+#ifdef CONFIG_IEEE80211AX
+	if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax &&
+	    is_6ghz_op_class(hapd->iconf->op_class))
+		pos = hostapd_eid_txpower_envelope(hapd, pos);
+#endif /* CONFIG_IEEE80211AX */
+
+	pos = hostapd_eid_wb_chsw_wrapper(hapd, pos);
+
+	pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_PROBE_RESP);
+	pos = hostapd_eid_fils_indic(hapd, pos, 0);
+	pos = hostapd_get_rsnxe(hapd, pos, epos - pos);
+
+#ifdef CONFIG_IEEE80211AX
+	if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
+		u8 *cca_pos;
+
+		pos = hostapd_eid_he_capab(hapd, pos, IEEE80211_MODE_AP);
+		pos = hostapd_eid_he_operation(hapd, pos);
+
+		/* BSS Color Change Announcement element */
+		cca_pos = hostapd_eid_cca(hapd, pos);
+		if (cca_pos != pos)
+			hapd->cca_c_off_proberesp = cca_pos - (u8 *) resp - 2;
+		pos = cca_pos;
+
+		pos = hostapd_eid_spatial_reuse(hapd, pos);
+		pos = hostapd_eid_he_mu_edca_parameter_set(hapd, pos);
+		pos = hostapd_eid_he_6ghz_band_cap(hapd, pos);
+	}
+#endif /* CONFIG_IEEE80211AX */
+
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
+		if (hapd->conf->mld_ap)
+			pos = hostapd_eid_eht_basic_ml(hapd, pos, NULL, true);
+		pos = hostapd_eid_eht_capab(hapd, pos, IEEE80211_MODE_AP);
+		pos = hostapd_eid_eht_operation(hapd, pos);
+	}
+#endif /* CONFIG_IEEE80211BE */
+
+#ifdef CONFIG_IEEE80211AC
+	if (hapd->conf->vendor_vht)
+		pos = hostapd_eid_vendor_vht(hapd, pos);
+#endif /* CONFIG_IEEE80211AC */
+
+	/* WPA / OSEN */
+	pos = hostapd_get_wpa_ie(hapd, pos, epos - pos);
+	pos = hostapd_get_osen_ie(hapd, pos, epos - pos);
+
+	/* Wi-Fi Alliance WMM */
+	pos = hostapd_eid_wmm(hapd, pos);
+
+#ifdef CONFIG_WPS
+	if (hapd->conf->wps_state && hapd->wps_probe_resp_ie) {
+		os_memcpy(pos, wpabuf_head(hapd->wps_probe_resp_ie),
+			  wpabuf_len(hapd->wps_probe_resp_ie));
+		pos += wpabuf_len(hapd->wps_probe_resp_ie);
+	}
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_P2P
+	if ((hapd->conf->p2p & P2P_ENABLED) && is_p2p &&
+	    hapd->p2p_probe_resp_ie) {
+		os_memcpy(pos, wpabuf_head(hapd->p2p_probe_resp_ie),
+			  wpabuf_len(hapd->p2p_probe_resp_ie));
+		pos += wpabuf_len(hapd->p2p_probe_resp_ie);
+	}
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_P2P_MANAGER
+	if ((hapd->conf->p2p & (P2P_MANAGE | P2P_ENABLED | P2P_GROUP_OWNER)) ==
+	    P2P_MANAGE)
+		pos = hostapd_eid_p2p_manage(hapd, pos);
+#endif /* CONFIG_P2P_MANAGER */
+
+#ifdef CONFIG_HS20
+	pos = hostapd_eid_hs20_indication(hapd, pos);
+#endif /* CONFIG_HS20 */
+
+	pos = hostapd_eid_mbo(hapd, pos, (u8 *) resp + buflen - pos);
+	pos = hostapd_eid_owe_trans(hapd, pos, (u8 *) resp + buflen - pos);
+	pos = hostapd_eid_dpp_cc(hapd, pos, (u8 *) resp + buflen - pos);
+
+	if (hapd->conf->vendor_elements) {
+		os_memcpy(pos, wpabuf_head(hapd->conf->vendor_elements),
+			  wpabuf_len(hapd->conf->vendor_elements));
+		pos += wpabuf_len(hapd->conf->vendor_elements);
+	}
+
+	*resp_len = pos - (u8 *) resp;
+	return (u8 *) resp;
+}
+
+
+enum ssid_match_result {
+	NO_SSID_MATCH,
+	EXACT_SSID_MATCH,
+	WILDCARD_SSID_MATCH,
+	CO_LOCATED_SSID_MATCH,
+};
+
+static enum ssid_match_result ssid_match(struct hostapd_data *hapd,
+					 const u8 *ssid, size_t ssid_len,
+					 const u8 *ssid_list,
+					 size_t ssid_list_len,
+					 const u8 *short_ssid_list,
+					 size_t short_ssid_list_len)
+{
+	const u8 *pos, *end;
+	struct hostapd_iface *iface = hapd->iface;
+	int wildcard = 0;
+	size_t i, j;
+
+	if (ssid_len == 0)
+		wildcard = 1;
+	if (ssid_len == hapd->conf->ssid.ssid_len &&
+	    os_memcmp(ssid, hapd->conf->ssid.ssid, ssid_len) == 0)
+		return EXACT_SSID_MATCH;
+
+	if (ssid_list) {
+		pos = ssid_list;
+		end = ssid_list + ssid_list_len;
+		while (end - pos >= 2) {
+			if (2 + pos[1] > end - pos)
+				break;
+			if (pos[1] == 0)
+				wildcard = 1;
+			if (pos[1] == hapd->conf->ssid.ssid_len &&
+			    os_memcmp(pos + 2, hapd->conf->ssid.ssid,
+				      pos[1]) == 0)
+				return EXACT_SSID_MATCH;
+			pos += 2 + pos[1];
+		}
+	}
+
+	if (short_ssid_list) {
+		pos = short_ssid_list;
+		end = short_ssid_list + short_ssid_list_len;
+		while (end - pos >= 4) {
+			if (hapd->conf->ssid.short_ssid == WPA_GET_LE32(pos))
+				return EXACT_SSID_MATCH;
+			pos += 4;
+		}
+	}
+
+	if (wildcard)
+		return WILDCARD_SSID_MATCH;
+
+	if (!iface->interfaces || iface->interfaces->count <= 1 ||
+	    is_6ghz_op_class(hapd->iconf->op_class))
+		return NO_SSID_MATCH;
+
+	for (i = 0; i < iface->interfaces->count; i++) {
+		struct hostapd_iface *colocated;
+
+		colocated = iface->interfaces->iface[i];
+
+		if (colocated == iface ||
+		    !is_6ghz_op_class(colocated->conf->op_class))
+			continue;
+
+		for (j = 0; j < colocated->num_bss; j++) {
+			struct hostapd_bss_config *conf;
+
+			conf = colocated->bss[j]->conf;
+			if (ssid_len == conf->ssid.ssid_len &&
+			    os_memcmp(ssid, conf->ssid.ssid, ssid_len) == 0)
+				return CO_LOCATED_SSID_MATCH;
+		}
+	}
+
+	return NO_SSID_MATCH;
+}
+
+
+void sta_track_expire(struct hostapd_iface *iface, int force)
+{
+	struct os_reltime now;
+	struct hostapd_sta_info *info;
+
+	if (!iface->num_sta_seen)
+		return;
+
+	os_get_reltime(&now);
+	while ((info = dl_list_first(&iface->sta_seen, struct hostapd_sta_info,
+				     list))) {
+		if (!force &&
+		    !os_reltime_expired(&now, &info->last_seen,
+					iface->conf->track_sta_max_age))
+			break;
+		force = 0;
+
+		wpa_printf(MSG_MSGDUMP, "%s: Expire STA tracking entry for "
+			   MACSTR, iface->bss[0]->conf->iface,
+			   MAC2STR(info->addr));
+		dl_list_del(&info->list);
+		iface->num_sta_seen--;
+		sta_track_del(info);
+	}
+}
+
+
+static struct hostapd_sta_info * sta_track_get(struct hostapd_iface *iface,
+					       const u8 *addr)
+{
+	struct hostapd_sta_info *info;
+
+	dl_list_for_each(info, &iface->sta_seen, struct hostapd_sta_info, list)
+		if (os_memcmp(addr, info->addr, ETH_ALEN) == 0)
+			return info;
+
+	return NULL;
+}
+
+
+void sta_track_add(struct hostapd_iface *iface, const u8 *addr, int ssi_signal)
+{
+	struct hostapd_sta_info *info;
+
+	info = sta_track_get(iface, addr);
+	if (info) {
+		/* Move the most recent entry to the end of the list */
+		dl_list_del(&info->list);
+		dl_list_add_tail(&iface->sta_seen, &info->list);
+		os_get_reltime(&info->last_seen);
+		info->ssi_signal = ssi_signal;
+		return;
+	}
+
+	/* Add a new entry */
+	info = os_zalloc(sizeof(*info));
+	if (info == NULL)
+		return;
+	os_memcpy(info->addr, addr, ETH_ALEN);
+	os_get_reltime(&info->last_seen);
+	info->ssi_signal = ssi_signal;
+
+	if (iface->num_sta_seen >= iface->conf->track_sta_max_num) {
+		/* Expire oldest entry to make room for a new one */
+		sta_track_expire(iface, 1);
+	}
+
+	wpa_printf(MSG_MSGDUMP, "%s: Add STA tracking entry for "
+		   MACSTR, iface->bss[0]->conf->iface, MAC2STR(addr));
+	dl_list_add_tail(&iface->sta_seen, &info->list);
+	iface->num_sta_seen++;
+}
+
+
+struct hostapd_data *
+sta_track_seen_on(struct hostapd_iface *iface, const u8 *addr,
+		  const char *ifname)
+{
+	struct hapd_interfaces *interfaces = iface->interfaces;
+	size_t i, j;
+
+	for (i = 0; i < interfaces->count; i++) {
+		struct hostapd_data *hapd = NULL;
+
+		iface = interfaces->iface[i];
+		for (j = 0; j < iface->num_bss; j++) {
+			hapd = iface->bss[j];
+			if (os_strcmp(ifname, hapd->conf->iface) == 0)
+				break;
+			hapd = NULL;
+		}
+
+		if (hapd && sta_track_get(iface, addr))
+			return hapd;
+	}
+
+	return NULL;
+}
+
+
+#ifdef CONFIG_TAXONOMY
+void sta_track_claim_taxonomy_info(struct hostapd_iface *iface, const u8 *addr,
+				   struct wpabuf **probe_ie_taxonomy)
+{
+	struct hostapd_sta_info *info;
+
+	info = sta_track_get(iface, addr);
+	if (!info)
+		return;
+
+	wpabuf_free(*probe_ie_taxonomy);
+	*probe_ie_taxonomy = info->probe_ie_taxonomy;
+	info->probe_ie_taxonomy = NULL;
+}
+#endif /* CONFIG_TAXONOMY */
+
+
+void handle_probe_req(struct hostapd_data *hapd,
+		      const struct ieee80211_mgmt *mgmt, size_t len,
+		      int ssi_signal)
+{
+	u8 *resp;
+	struct ieee802_11_elems elems;
+	const u8 *ie;
+	size_t ie_len;
+	size_t i, resp_len;
+	int noack;
+	enum ssid_match_result res;
+	int ret;
+	u16 csa_offs[2];
+	size_t csa_offs_len;
+	struct radius_sta rad_info;
+	struct hostapd_ubus_request req = {
+		.type = HOSTAPD_UBUS_PROBE_REQ,
+		.mgmt_frame = mgmt,
+		.ssi_signal = ssi_signal,
+		.elems = &elems,
+	};
+
+	if (hapd->iconf->rssi_ignore_probe_request && ssi_signal &&
+	    ssi_signal < hapd->iconf->rssi_ignore_probe_request)
+		return;
+
+	if (len < IEEE80211_HDRLEN)
+		return;
+	ie = ((const u8 *) mgmt) + IEEE80211_HDRLEN;
+	if (hapd->iconf->track_sta_max_num)
+		sta_track_add(hapd->iface, mgmt->sa, ssi_signal);
+	ie_len = len - IEEE80211_HDRLEN;
+
+	ret = hostapd_allowed_address(hapd, mgmt->sa, (const u8 *) mgmt, len,
+				      &rad_info, 1);
+	if (ret == HOSTAPD_ACL_REJECT) {
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+			"Ignore Probe Request frame from " MACSTR
+			" due to ACL reject ", MAC2STR(mgmt->sa));
+		return;
+	}
+
+	for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++)
+		if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx,
+					    mgmt->sa, mgmt->da, mgmt->bssid,
+					    ie, ie_len, ssi_signal) > 0)
+			return;
+
+	if (!hapd->conf->send_probe_response)
+		return;
+
+	if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) {
+		wpa_printf(MSG_DEBUG, "Could not parse ProbeReq from " MACSTR,
+			   MAC2STR(mgmt->sa));
+		return;
+	}
+
+	if ((!elems.ssid || !elems.supp_rates)) {
+		wpa_printf(MSG_DEBUG, "STA " MACSTR " sent probe request "
+			   "without SSID or supported rates element",
+			   MAC2STR(mgmt->sa));
+		return;
+	}
+
+	/*
+	 * No need to reply if the Probe Request frame was sent on an adjacent
+	 * channel. IEEE Std 802.11-2012 describes this as a requirement for an
+	 * AP with dot11RadioMeasurementActivated set to true, but strictly
+	 * speaking does not allow such ignoring of Probe Request frames if
+	 * dot11RadioMeasurementActivated is false. Anyway, this can help reduce
+	 * number of unnecessary Probe Response frames for cases where the STA
+	 * is less likely to see them (Probe Request frame sent on a
+	 * neighboring, but partially overlapping, channel).
+	 */
+	if (elems.ds_params &&
+	    hapd->iface->current_mode &&
+	    (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G ||
+	     hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211B) &&
+	    hapd->iconf->channel != elems.ds_params[0]) {
+		wpa_printf(MSG_DEBUG,
+			   "Ignore Probe Request due to DS Params mismatch: chan=%u != ds.chan=%u",
+			   hapd->iconf->channel, elems.ds_params[0]);
+		return;
+	}
+
+#ifdef CONFIG_P2P
+	if (hapd->p2p && hapd->p2p_group && elems.wps_ie) {
+		struct wpabuf *wps;
+		wps = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA);
+		if (wps && !p2p_group_match_dev_type(hapd->p2p_group, wps)) {
+			wpa_printf(MSG_MSGDUMP, "P2P: Ignore Probe Request "
+				   "due to mismatch with Requested Device "
+				   "Type");
+			wpabuf_free(wps);
+			return;
+		}
+		wpabuf_free(wps);
+	}
+
+	if (hapd->p2p && hapd->p2p_group && elems.p2p) {
+		struct wpabuf *p2p;
+		p2p = ieee802_11_vendor_ie_concat(ie, ie_len, P2P_IE_VENDOR_TYPE);
+		if (p2p && !p2p_group_match_dev_id(hapd->p2p_group, p2p)) {
+			wpa_printf(MSG_MSGDUMP, "P2P: Ignore Probe Request "
+				   "due to mismatch with Device ID");
+			wpabuf_free(p2p);
+			return;
+		}
+		wpabuf_free(p2p);
+	}
+#endif /* CONFIG_P2P */
+
+	if (hapd->conf->ignore_broadcast_ssid && elems.ssid_len == 0 &&
+	    elems.ssid_list_len == 0 && elems.short_ssid_list_len == 0) {
+		wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR " for "
+			   "broadcast SSID ignored", MAC2STR(mgmt->sa));
+		return;
+	}
+
+#ifdef CONFIG_P2P
+	if ((hapd->conf->p2p & P2P_GROUP_OWNER) &&
+	    elems.ssid_len == P2P_WILDCARD_SSID_LEN &&
+	    os_memcmp(elems.ssid, P2P_WILDCARD_SSID,
+		      P2P_WILDCARD_SSID_LEN) == 0) {
+		/* Process P2P Wildcard SSID like Wildcard SSID */
+		elems.ssid_len = 0;
+	}
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_TAXONOMY
+	{
+		struct sta_info *sta;
+		struct hostapd_sta_info *info;
+
+		if ((sta = ap_get_sta(hapd, mgmt->sa)) != NULL) {
+			taxonomy_sta_info_probe_req(hapd, sta, ie, ie_len);
+		} else if ((info = sta_track_get(hapd->iface,
+						 mgmt->sa)) != NULL) {
+			taxonomy_hostapd_sta_info_probe_req(hapd, info,
+							    ie, ie_len);
+		}
+	}
+#endif /* CONFIG_TAXONOMY */
+
+	res = ssid_match(hapd, elems.ssid, elems.ssid_len,
+			 elems.ssid_list, elems.ssid_list_len,
+			 elems.short_ssid_list, elems.short_ssid_list_len);
+	if (res == NO_SSID_MATCH) {
+		if (!(mgmt->da[0] & 0x01)) {
+			wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR
+				   " for foreign SSID '%s' (DA " MACSTR ")%s",
+				   MAC2STR(mgmt->sa),
+				   wpa_ssid_txt(elems.ssid, elems.ssid_len),
+				   MAC2STR(mgmt->da),
+				   elems.ssid_list ? " (SSID list)" : "");
+		}
+		return;
+	}
+
+	if (hapd->conf->ignore_broadcast_ssid && res == WILDCARD_SSID_MATCH) {
+		wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR " for "
+			   "broadcast SSID ignored", MAC2STR(mgmt->sa));
+		return;
+	}
+
+#ifdef CONFIG_INTERWORKING
+	if (hapd->conf->interworking &&
+	    elems.interworking && elems.interworking_len >= 1) {
+		u8 ant = elems.interworking[0] & 0x0f;
+		if (ant != INTERWORKING_ANT_WILDCARD &&
+		    ant != hapd->conf->access_network_type) {
+			wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR
+				   " for mismatching ANT %u ignored",
+				   MAC2STR(mgmt->sa), ant);
+			return;
+		}
+	}
+
+	if (hapd->conf->interworking && elems.interworking &&
+	    (elems.interworking_len == 7 || elems.interworking_len == 9)) {
+		const u8 *hessid;
+		if (elems.interworking_len == 7)
+			hessid = elems.interworking + 1;
+		else
+			hessid = elems.interworking + 1 + 2;
+		if (!is_broadcast_ether_addr(hessid) &&
+		    os_memcmp(hessid, hapd->conf->hessid, ETH_ALEN) != 0) {
+			wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR
+				   " for mismatching HESSID " MACSTR
+				   " ignored",
+				   MAC2STR(mgmt->sa), MAC2STR(hessid));
+			return;
+		}
+	}
+#endif /* CONFIG_INTERWORKING */
+
+#ifdef CONFIG_P2P
+	if ((hapd->conf->p2p & P2P_GROUP_OWNER) &&
+	    supp_rates_11b_only(&elems)) {
+		/* Indicates support for 11b rates only */
+		wpa_printf(MSG_EXCESSIVE, "P2P: Ignore Probe Request from "
+			   MACSTR " with only 802.11b rates",
+			   MAC2STR(mgmt->sa));
+		return;
+	}
+#endif /* CONFIG_P2P */
+
+	if (hostapd_ubus_handle_event(hapd, &req)) {
+		wpa_printf(MSG_DEBUG, "Probe request for " MACSTR " rejected by ubus handler.\n",
+		       MAC2STR(mgmt->sa));
+		return;
+	}
+
+	/* TODO: verify that supp_rates contains at least one matching rate
+	 * with AP configuration */
+
+	if (hapd->conf->no_probe_resp_if_seen_on &&
+	    is_multicast_ether_addr(mgmt->da) &&
+	    is_multicast_ether_addr(mgmt->bssid) &&
+	    sta_track_seen_on(hapd->iface, mgmt->sa,
+			      hapd->conf->no_probe_resp_if_seen_on)) {
+		wpa_printf(MSG_MSGDUMP, "%s: Ignore Probe Request from " MACSTR
+			   " since STA has been seen on %s",
+			   hapd->conf->iface, MAC2STR(mgmt->sa),
+			   hapd->conf->no_probe_resp_if_seen_on);
+		return;
+	}
+
+	if (hapd->conf->no_probe_resp_if_max_sta &&
+	    is_multicast_ether_addr(mgmt->da) &&
+	    is_multicast_ether_addr(mgmt->bssid) &&
+	    hostapd_check_max_sta(hapd) &&
+	    !ap_get_sta(hapd, mgmt->sa)) {
+		wpa_printf(MSG_MSGDUMP, "%s: Ignore Probe Request from " MACSTR
+			   " since no room for additional STA",
+			   hapd->conf->iface, MAC2STR(mgmt->sa));
+		return;
+	}
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (hapd->iconf->ignore_probe_probability > 0.0 &&
+	    drand48() < hapd->iconf->ignore_probe_probability) {
+		wpa_printf(MSG_INFO,
+			   "TESTING: ignoring probe request from " MACSTR,
+			   MAC2STR(mgmt->sa));
+		return;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	/* Do not send Probe Response frame from a non-transmitting multiple
+	 * BSSID profile unless the Probe Request frame is directed at that
+	 * particular BSS. */
+	if (hapd != hostapd_mbssid_get_tx_bss(hapd) && res != EXACT_SSID_MATCH)
+		return;
+
+	wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, RX_PROBE_REQUEST "sa=" MACSTR
+		     " signal=%d", MAC2STR(mgmt->sa), ssi_signal);
+
+	resp = hostapd_gen_probe_resp(hapd, mgmt, elems.p2p != NULL,
+				      &resp_len, elems.mbssid_known_bss,
+				      elems.mbssid_known_bss_len);
+	if (resp == NULL)
+		return;
+
+	/*
+	 * If this is a broadcast probe request, apply no ack policy to avoid
+	 * excessive retries.
+	 */
+	noack = !!(res == WILDCARD_SSID_MATCH &&
+		   is_broadcast_ether_addr(mgmt->da));
+
+	csa_offs_len = 0;
+	if (hapd->csa_in_progress) {
+		if (hapd->cs_c_off_proberesp)
+			csa_offs[csa_offs_len++] =
+				hapd->cs_c_off_proberesp;
+
+		if (hapd->cs_c_off_ecsa_proberesp)
+			csa_offs[csa_offs_len++] =
+				hapd->cs_c_off_ecsa_proberesp;
+	}
+
+	ret = hostapd_drv_send_mlme(hostapd_mbssid_get_tx_bss(hapd), resp,
+				    resp_len, noack,
+				    csa_offs_len ? csa_offs : NULL,
+				    csa_offs_len, 0);
+
+	if (ret < 0)
+		wpa_printf(MSG_INFO, "handle_probe_req: send failed");
+
+	os_free(resp);
+
+	wpa_printf(MSG_EXCESSIVE, "STA " MACSTR " sent probe request for %s "
+		   "SSID", MAC2STR(mgmt->sa),
+		   elems.ssid_len == 0 ? "broadcast" : "our");
+}
+
+
+static u8 * hostapd_probe_resp_offloads(struct hostapd_data *hapd,
+					size_t *resp_len)
+{
+	/* check probe response offloading caps and print warnings */
+	if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD))
+		return NULL;
+
+#ifdef CONFIG_WPS
+	if (hapd->conf->wps_state && hapd->wps_probe_resp_ie &&
+	    (!(hapd->iface->probe_resp_offloads &
+	       (WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS |
+		WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2))))
+		wpa_printf(MSG_WARNING, "Device is trying to offload WPS "
+			   "Probe Response while not supporting this");
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_P2P
+	if ((hapd->conf->p2p & P2P_ENABLED) && hapd->p2p_probe_resp_ie &&
+	    !(hapd->iface->probe_resp_offloads &
+	      WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P))
+		wpa_printf(MSG_WARNING, "Device is trying to offload P2P "
+			   "Probe Response while not supporting this");
+#endif  /* CONFIG_P2P */
+
+	if (hapd->conf->interworking &&
+	    !(hapd->iface->probe_resp_offloads &
+	      WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING))
+		wpa_printf(MSG_WARNING, "Device is trying to offload "
+			   "Interworking Probe Response while not supporting "
+			   "this");
+
+	/* Generate a Probe Response template for the non-P2P case */
+	return hostapd_gen_probe_resp(hapd, NULL, 0, resp_len, NULL, 0);
+}
+
+#endif /* NEED_AP_MLME */
+
+
+#ifdef CONFIG_IEEE80211AX
+/* Unsolicited broadcast Probe Response transmission, 6 GHz only */
+static u8 * hostapd_unsol_bcast_probe_resp(struct hostapd_data *hapd,
+					   struct wpa_driver_ap_params *params)
+{
+	if (!is_6ghz_op_class(hapd->iconf->op_class))
+		return NULL;
+
+	params->unsol_bcast_probe_resp_interval =
+		hapd->conf->unsol_bcast_probe_resp_interval;
+
+	return hostapd_gen_probe_resp(hapd, NULL, 0,
+				      &params->unsol_bcast_probe_resp_tmpl_len,
+				      NULL, 0);
+}
+#endif /* CONFIG_IEEE80211AX */
+
+
+void sta_track_del(struct hostapd_sta_info *info)
+{
+#ifdef CONFIG_TAXONOMY
+	wpabuf_free(info->probe_ie_taxonomy);
+	info->probe_ie_taxonomy = NULL;
+#endif /* CONFIG_TAXONOMY */
+	os_free(info);
+}
+
+
+#ifdef CONFIG_FILS
+
+static u16 hostapd_fils_discovery_cap(struct hostapd_data *hapd)
+{
+	u16 cap_info, phy_index = 0;
+	u8 chwidth = FD_CAP_BSS_CHWIDTH_20, mcs_nss_size = 4;
+	struct hostapd_hw_modes *mode = hapd->iface->current_mode;
+
+	cap_info = FD_CAP_ESS;
+	if (hapd->conf->wpa)
+		cap_info |= FD_CAP_PRIVACY;
+
+	if (is_6ghz_op_class(hapd->iconf->op_class)) {
+		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 */
+		case 134:
+			mcs_nss_size += 4;
+			chwidth = FD_CAP_BSS_CHWIDTH_160_80_80;
+			break;
+		case 133:
+			chwidth = FD_CAP_BSS_CHWIDTH_80;
+			break;
+		case 132:
+			chwidth = FD_CAP_BSS_CHWIDTH_40;
+			break;
+		}
+	} else {
+		switch (hostapd_get_oper_chwidth(hapd->iconf)) {
+		case CONF_OPER_CHWIDTH_80P80MHZ:
+			mcs_nss_size += 4;
+			/* fallthrough */
+		case CONF_OPER_CHWIDTH_160MHZ:
+			mcs_nss_size += 4;
+			chwidth = FD_CAP_BSS_CHWIDTH_160_80_80;
+			break;
+		case CONF_OPER_CHWIDTH_80MHZ:
+			chwidth = FD_CAP_BSS_CHWIDTH_80;
+			break;
+		case CONF_OPER_CHWIDTH_USE_HT:
+			if (hapd->iconf->secondary_channel)
+				chwidth = FD_CAP_BSS_CHWIDTH_40;
+			else
+				chwidth = FD_CAP_BSS_CHWIDTH_20;
+			break;
+		default:
+			break;
+		}
+
+#ifdef CONFIG_IEEE80211AX
+		if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax)
+			phy_index = FD_CAP_PHY_INDEX_HE;
+#endif /* CONFIG_IEEE80211AX */
+#ifdef CONFIG_IEEE80211AC
+		if (!phy_index &&
+		    hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac)
+			phy_index = FD_CAP_PHY_INDEX_VHT;
+#endif /* CONFIG_IEEE80211AC */
+		if (!phy_index &&
+		    hapd->iconf->ieee80211n && !hapd->conf->disable_11n)
+			phy_index = FD_CAP_PHY_INDEX_HT;
+	}
+
+	cap_info |= phy_index << FD_CAP_PHY_INDEX_SHIFT;
+	cap_info |= chwidth << FD_CAP_BSS_CHWIDTH_SHIFT;
+
+	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, 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)))
+				continue;
+
+			if (mcs_nss_size == 8 &&
+			    (((mcs[2] & nss_mask) == nss_mask) ||
+			     ((mcs[3] & nss_mask) == nss_mask)))
+				continue;
+
+			if (mcs_nss_size == 12 &&
+			    (((mcs[4] & nss_mask) == nss_mask) ||
+			     ((mcs[5] & nss_mask) == nss_mask)))
+				continue;
+
+			nss++;
+		}
+
+		if (nss > 4)
+			cap_info |= FD_CAP_NSS_5_8 << FD_CAP_NSS_SHIFT;
+		else if (nss)
+			cap_info |= (nss - 1) << FD_CAP_NSS_SHIFT;
+	}
+
+	return cap_info;
+}
+
+
+static u8 * hostapd_gen_fils_discovery(struct hostapd_data *hapd, size_t *len)
+{
+	struct ieee80211_mgmt *head;
+	const u8 *mobility_domain;
+	u8 *pos, *length_pos, buf[200];
+	u16 ctl = 0;
+	u8 fd_rsn_info[5];
+	size_t total_len, buf_len;
+
+	total_len = 24 + 2 + 12;
+
+	/* FILS Discovery Frame Control */
+	ctl = (sizeof(hapd->conf->ssid.short_ssid) - 1) |
+		FD_FRAME_CTL_SHORT_SSID_PRESENT |
+		FD_FRAME_CTL_LENGTH_PRESENT |
+		FD_FRAME_CTL_CAP_PRESENT;
+	total_len += 4 + 1 + 2;
+
+	/* Check for optional subfields and calculate length */
+	if (wpa_auth_write_fd_rsn_info(hapd->wpa_auth, fd_rsn_info)) {
+		ctl |= FD_FRAME_CTL_RSN_INFO_PRESENT;
+		total_len += sizeof(fd_rsn_info);
+	}
+
+	mobility_domain = hostapd_wpa_ie(hapd, WLAN_EID_MOBILITY_DOMAIN);
+	if (mobility_domain) {
+		ctl |= FD_FRAME_CTL_MD_PRESENT;
+		total_len += 3;
+	}
+
+	total_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_ACTION);
+
+	pos = hostapd_eid_fils_indic(hapd, buf, 0);
+	buf_len = pos - buf;
+	total_len += buf_len;
+
+#ifdef CONFIG_IEEE80211AX
+	/* Transmit Power Envelope element(s) */
+	if (is_6ghz_op_class(hapd->iconf->op_class)) {
+		total_len += 4;
+		if (hapd->iconf->he_6ghz_reg_pwr_type == HE_6GHZ_INDOOR_AP)
+			total_len += 4;
+	}
+#endif /* CONFIG_IEEE80211AX */
+
+	head = os_zalloc(total_len);
+	if (!head)
+		return NULL;
+
+	head->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					   WLAN_FC_STYPE_ACTION);
+	os_memset(head->da, 0xff, ETH_ALEN);
+	os_memcpy(head->sa, hapd->own_addr, ETH_ALEN);
+	os_memcpy(head->bssid, hapd->own_addr, ETH_ALEN);
+
+	head->u.action.category = WLAN_ACTION_PUBLIC;
+	head->u.action.u.public_action.action = WLAN_PA_FILS_DISCOVERY;
+
+	pos = &head->u.action.u.public_action.variable[0];
+
+	/* FILS Discovery Information field */
+
+	/* FILS Discovery Frame Control */
+	WPA_PUT_LE16(pos, ctl);
+	pos += 2;
+
+	/* Hardware or low-level driver will fill in the Timestamp value */
+	pos += 8;
+
+	/* Beacon Interval */
+	WPA_PUT_LE16(pos, hapd->iconf->beacon_int);
+	pos += 2;
+
+	/* Short SSID */
+	WPA_PUT_LE32(pos, hapd->conf->ssid.short_ssid);
+	pos += sizeof(hapd->conf->ssid.short_ssid);
+
+	/* Store position of FILS discovery information element Length field */
+	length_pos = pos++;
+
+	/* FD Capability */
+	WPA_PUT_LE16(pos, hostapd_fils_discovery_cap(hapd));
+	pos += 2;
+
+	/* Operating Class - not present */
+
+	/* Primary Channel - not present */
+
+	/* AP Configuration Sequence Number - not present */
+
+	/* Access Network Options - not present */
+
+	/* FD RSN Information */
+	if (ctl & FD_FRAME_CTL_RSN_INFO_PRESENT) {
+		os_memcpy(pos, fd_rsn_info, sizeof(fd_rsn_info));
+		pos += sizeof(fd_rsn_info);
+	}
+
+	/* Channel Center Frequency Segment 1 - not present */
+
+	/* Mobility Domain */
+	if (ctl & FD_FRAME_CTL_MD_PRESENT) {
+		os_memcpy(pos, &mobility_domain[2], 3);
+		pos += 3;
+	}
+
+	/* Fill in the Length field value */
+	*length_pos = pos - (length_pos + 1);
+
+	pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_ACTION);
+
+	/* FILS Indication element */
+	if (buf_len) {
+		os_memcpy(pos, buf, buf_len);
+		pos += buf_len;
+	}
+
+	if (is_6ghz_op_class(hapd->iconf->op_class))
+		pos = hostapd_eid_txpower_envelope(hapd, pos);
+
+	*len = pos - (u8 *) head;
+	wpa_hexdump(MSG_DEBUG, "FILS Discovery frame template",
+		    head, pos - (u8 *) head);
+	return (u8 *) head;
+}
+
+
+/* Configure FILS Discovery frame transmission parameters */
+static u8 * hostapd_fils_discovery(struct hostapd_data *hapd,
+				   struct wpa_driver_ap_params *params)
+{
+	params->fd_max_int = hapd->conf->fils_discovery_max_int;
+	if (is_6ghz_op_class(hapd->iconf->op_class) &&
+	    params->fd_max_int > FD_MAX_INTERVAL_6GHZ)
+		params->fd_max_int = FD_MAX_INTERVAL_6GHZ;
+
+	params->fd_min_int = hapd->conf->fils_discovery_min_int;
+	if (params->fd_min_int > params->fd_max_int)
+		params->fd_min_int = params->fd_max_int;
+
+	if (params->fd_max_int)
+		return hostapd_gen_fils_discovery(hapd,
+						  &params->fd_frame_tmpl_len);
+
+	return NULL;
+}
+
+#endif /* CONFIG_FILS */
+
+
+int ieee802_11_build_ap_params(struct hostapd_data *hapd,
+			       struct wpa_driver_ap_params *params)
+{
+	struct ieee80211_mgmt *head = NULL;
+	u8 *tail = NULL;
+	size_t head_len = 0, tail_len = 0;
+	u8 *resp = NULL;
+	size_t resp_len = 0;
+#ifdef NEED_AP_MLME
+	u16 capab_info;
+	u8 *pos, *tailpos, *tailend, *csa_pos;
+	bool complete = false;
+#endif /* NEED_AP_MLME */
+
+	os_memset(params, 0, sizeof(*params));
+
+#ifdef NEED_AP_MLME
+#define BEACON_HEAD_BUF_SIZE 256
+#define BEACON_TAIL_BUF_SIZE 512
+	head = os_zalloc(BEACON_HEAD_BUF_SIZE);
+	tail_len = BEACON_TAIL_BUF_SIZE;
+#ifdef CONFIG_WPS
+	if (hapd->conf->wps_state && hapd->wps_beacon_ie)
+		tail_len += wpabuf_len(hapd->wps_beacon_ie);
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+	if (hapd->p2p_beacon_ie)
+		tail_len += wpabuf_len(hapd->p2p_beacon_ie);
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_FST
+	if (hapd->iface->fst_ies)
+		tail_len += wpabuf_len(hapd->iface->fst_ies);
+#endif /* CONFIG_FST */
+	if (hapd->conf->vendor_elements)
+		tail_len += wpabuf_len(hapd->conf->vendor_elements);
+
+#ifdef CONFIG_IEEE80211AC
+	if (hapd->conf->vendor_vht) {
+		tail_len += 5 + 2 + sizeof(struct ieee80211_vht_capabilities) +
+			2 + sizeof(struct ieee80211_vht_operation);
+	}
+#endif /* CONFIG_IEEE80211AC */
+
+#ifdef CONFIG_IEEE80211AX
+	if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
+		tail_len += 3 + sizeof(struct ieee80211_he_capabilities) +
+			3 + sizeof(struct ieee80211_he_operation) +
+			3 + sizeof(struct ieee80211_he_mu_edca_parameter_set) +
+			3 + sizeof(struct ieee80211_spatial_reuse);
+		if (is_6ghz_op_class(hapd->iconf->op_class)) {
+			tail_len += sizeof(struct ieee80211_he_6ghz_oper_info) +
+				3 + sizeof(struct ieee80211_he_6ghz_band_cap);
+			 /* An additional Transmit Power Envelope element for
+			  * subordinate client */
+			if (hapd->iconf->he_6ghz_reg_pwr_type ==
+			    HE_6GHZ_INDOOR_AP)
+				tail_len += 4;
+		}
+	}
+#endif /* CONFIG_IEEE80211AX */
+
+#ifdef CONFIG_IEEE80211BE
+	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;
+
+		/*
+		 * TODO: Multi-Link element has variable length and can be
+		 * long based on the common info and number of per
+		 * station profiles. For now use 256.
+		 */
+		if (hapd->conf->mld_ap)
+			tail_len += 256;
+	}
+#endif /* CONFIG_IEEE80211BE */
+
+	if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
+	    hapd == hostapd_mbssid_get_tx_bss(hapd))
+		tail_len += 5; /* Multiple BSSID Configuration element */
+	tail_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_BEACON);
+	tail_len += hostapd_mbo_ie_len(hapd);
+	tail_len += hostapd_eid_owe_trans_len(hapd);
+	tail_len += hostapd_eid_dpp_cc_len(hapd);
+
+	tailpos = tail = os_malloc(tail_len);
+	if (head == NULL || tail == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to set beacon data");
+		os_free(head);
+		os_free(tail);
+		return -1;
+	}
+	tailend = tail + tail_len;
+
+	head->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					   WLAN_FC_STYPE_BEACON);
+	head->duration = host_to_le16(0);
+	os_memset(head->da, 0xff, ETH_ALEN);
+
+	os_memcpy(head->sa, hapd->own_addr, ETH_ALEN);
+	os_memcpy(head->bssid, hapd->own_addr, ETH_ALEN);
+	head->u.beacon.beacon_int =
+		host_to_le16(hapd->iconf->beacon_int);
+
+	/* hardware or low-level driver will setup seq_ctrl and timestamp */
+	capab_info = hostapd_own_capab_info(hapd);
+	head->u.beacon.capab_info = host_to_le16(capab_info);
+	pos = &head->u.beacon.variable[0];
+
+	/* SSID */
+	*pos++ = WLAN_EID_SSID;
+	if (hapd->conf->ignore_broadcast_ssid == 2) {
+		/* clear the data, but keep the correct length of the SSID */
+		*pos++ = hapd->conf->ssid.ssid_len;
+		os_memset(pos, 0, hapd->conf->ssid.ssid_len);
+		pos += hapd->conf->ssid.ssid_len;
+	} else if (hapd->conf->ignore_broadcast_ssid) {
+		*pos++ = 0; /* empty SSID */
+	} else {
+		*pos++ = hapd->conf->ssid.ssid_len;
+		os_memcpy(pos, hapd->conf->ssid.ssid,
+			  hapd->conf->ssid.ssid_len);
+		pos += hapd->conf->ssid.ssid_len;
+	}
+
+	/* Supported rates */
+	pos = hostapd_eid_supp_rates(hapd, pos);
+
+	/* DS Params */
+	pos = hostapd_eid_ds_params(hapd, pos);
+
+	head_len = pos - (u8 *) head;
+
+	tailpos = hostapd_eid_country(hapd, tailpos, tailend - tailpos);
+
+	/* Power Constraint element */
+	tailpos = hostapd_eid_pwr_constraint(hapd, tailpos);
+
+	/* CSA IE */
+	csa_pos = hostapd_eid_csa(hapd, tailpos);
+	if (csa_pos != tailpos)
+		hapd->cs_c_off_beacon = csa_pos - tail - 1;
+	tailpos = csa_pos;
+
+	/* ERP Information element */
+	tailpos = hostapd_eid_erp_info(hapd, tailpos);
+
+	/* Extended supported rates */
+	tailpos = hostapd_eid_ext_supp_rates(hapd, tailpos);
+
+	tailpos = hostapd_get_rsne(hapd, tailpos, tailend - tailpos);
+	tailpos = hostapd_eid_bss_load(hapd, tailpos, tailend - tailpos);
+	tailpos = hostapd_eid_rm_enabled_capab(hapd, tailpos,
+					       tailend - tailpos);
+	tailpos = hostapd_get_mde(hapd, tailpos, tailend - tailpos);
+
+	/* eCSA IE */
+	csa_pos = hostapd_eid_ecsa(hapd, tailpos);
+	if (csa_pos != tailpos)
+		hapd->cs_c_off_ecsa_beacon = csa_pos - tail - 1;
+	tailpos = csa_pos;
+
+	tailpos = hostapd_eid_supported_op_classes(hapd, tailpos);
+	tailpos = hostapd_eid_ht_capabilities(hapd, tailpos);
+	tailpos = hostapd_eid_ht_operation(hapd, tailpos);
+
+	if (hapd->iconf->mbssid && hapd->iconf->num_bss > 1) {
+		if (ieee802_11_build_ap_params_mbssid(hapd, params)) {
+			os_free(head);
+			os_free(tail);
+			wpa_printf(MSG_ERROR,
+				   "MBSSID: Failed to set beacon data");
+			return -1;
+		}
+		complete = hapd->iconf->mbssid == MBSSID_ENABLED ||
+			(hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
+			 params->mbssid_elem_count == 1);
+	}
+
+	tailpos = hostapd_eid_ext_capab(hapd, tailpos, complete);
+
+	/*
+	 * TODO: Time Advertisement element should only be included in some
+	 * DTIM Beacon frames.
+	 */
+	tailpos = hostapd_eid_time_adv(hapd, tailpos);
+
+	tailpos = hostapd_eid_interworking(hapd, tailpos);
+	tailpos = hostapd_eid_adv_proto(hapd, tailpos);
+	tailpos = hostapd_eid_roaming_consortium(hapd, tailpos);
+
+#ifdef CONFIG_FST
+	if (hapd->iface->fst_ies) {
+		os_memcpy(tailpos, wpabuf_head(hapd->iface->fst_ies),
+			  wpabuf_len(hapd->iface->fst_ies));
+		tailpos += wpabuf_len(hapd->iface->fst_ies);
+	}
+#endif /* CONFIG_FST */
+
+#ifdef CONFIG_IEEE80211AC
+	if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac &&
+	    !is_6ghz_op_class(hapd->iconf->op_class)) {
+		tailpos = hostapd_eid_vht_capabilities(hapd, tailpos, 0);
+		tailpos = hostapd_eid_vht_operation(hapd, tailpos);
+		tailpos = hostapd_eid_txpower_envelope(hapd, tailpos);
+	}
+#endif /* CONFIG_IEEE80211AC */
+
+#ifdef CONFIG_IEEE80211AX
+	if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax &&
+	    is_6ghz_op_class(hapd->iconf->op_class))
+		tailpos = hostapd_eid_txpower_envelope(hapd, tailpos);
+#endif /* CONFIG_IEEE80211AX */
+
+	tailpos = hostapd_eid_wb_chsw_wrapper(hapd, tailpos);
+
+	tailpos = hostapd_eid_rnr(hapd, tailpos, WLAN_FC_STYPE_BEACON);
+	tailpos = hostapd_eid_fils_indic(hapd, tailpos, 0);
+	tailpos = hostapd_get_rsnxe(hapd, tailpos, tailend - tailpos);
+	tailpos = hostapd_eid_mbssid_config(hapd, tailpos,
+					    params->mbssid_elem_count);
+
+#ifdef CONFIG_IEEE80211AX
+	if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
+		u8 *cca_pos;
+
+		tailpos = hostapd_eid_he_capab(hapd, tailpos,
+					       IEEE80211_MODE_AP);
+		tailpos = hostapd_eid_he_operation(hapd, tailpos);
+
+		/* BSS Color Change Announcement element */
+		cca_pos = hostapd_eid_cca(hapd, tailpos);
+		if (cca_pos != tailpos)
+			hapd->cca_c_off_beacon = cca_pos - tail - 2;
+		tailpos = cca_pos;
+
+		tailpos = hostapd_eid_spatial_reuse(hapd, tailpos);
+		tailpos = hostapd_eid_he_mu_edca_parameter_set(hapd, tailpos);
+		tailpos = hostapd_eid_he_6ghz_band_cap(hapd, tailpos);
+	}
+#endif /* CONFIG_IEEE80211AX */
+
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
+		if (hapd->conf->mld_ap)
+			tailpos = hostapd_eid_eht_basic_ml(hapd, tailpos, NULL,
+							   true);
+		tailpos = hostapd_eid_eht_capab(hapd, tailpos,
+						IEEE80211_MODE_AP);
+		tailpos = hostapd_eid_eht_operation(hapd, tailpos);
+	}
+#endif /* CONFIG_IEEE80211BE */
+
+#ifdef CONFIG_IEEE80211AC
+	if (hapd->conf->vendor_vht)
+		tailpos = hostapd_eid_vendor_vht(hapd, tailpos);
+#endif /* CONFIG_IEEE80211AC */
+
+	/* WPA / OSEN */
+	tailpos = hostapd_get_wpa_ie(hapd, tailpos, tailend - tailpos);
+	tailpos = hostapd_get_osen_ie(hapd, tailpos, tailend - tailpos);
+
+	/* Wi-Fi Alliance WMM */
+	tailpos = hostapd_eid_wmm(hapd, tailpos);
+
+#ifdef CONFIG_WPS
+	if (hapd->conf->wps_state && hapd->wps_beacon_ie) {
+		os_memcpy(tailpos, wpabuf_head(hapd->wps_beacon_ie),
+			  wpabuf_len(hapd->wps_beacon_ie));
+		tailpos += wpabuf_len(hapd->wps_beacon_ie);
+	}
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_P2P
+	if ((hapd->conf->p2p & P2P_ENABLED) && hapd->p2p_beacon_ie) {
+		os_memcpy(tailpos, wpabuf_head(hapd->p2p_beacon_ie),
+			  wpabuf_len(hapd->p2p_beacon_ie));
+		tailpos += wpabuf_len(hapd->p2p_beacon_ie);
+	}
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_P2P_MANAGER
+	if ((hapd->conf->p2p & (P2P_MANAGE | P2P_ENABLED | P2P_GROUP_OWNER)) ==
+	    P2P_MANAGE)
+		tailpos = hostapd_eid_p2p_manage(hapd, tailpos);
+#endif /* CONFIG_P2P_MANAGER */
+
+#ifdef CONFIG_HS20
+	tailpos = hostapd_eid_hs20_indication(hapd, tailpos);
+#endif /* CONFIG_HS20 */
+
+	tailpos = hostapd_eid_mbo(hapd, tailpos, tail + tail_len - tailpos);
+	tailpos = hostapd_eid_owe_trans(hapd, tailpos,
+					tail + tail_len - tailpos);
+	tailpos = hostapd_eid_dpp_cc(hapd, tailpos, tail + tail_len - tailpos);
+
+	if (hapd->conf->vendor_elements) {
+		os_memcpy(tailpos, wpabuf_head(hapd->conf->vendor_elements),
+			  wpabuf_len(hapd->conf->vendor_elements));
+		tailpos += wpabuf_len(hapd->conf->vendor_elements);
+	}
+
+	tail_len = tailpos > tail ? tailpos - tail : 0;
+
+	resp = hostapd_probe_resp_offloads(hapd, &resp_len);
+#endif /* NEED_AP_MLME */
+
+	params->head = (u8 *) head;
+	params->head_len = head_len;
+	params->tail = tail;
+	params->tail_len = tail_len;
+	params->proberesp = resp;
+	params->proberesp_len = resp_len;
+	params->dtim_period = hapd->conf->dtim_period;
+	params->beacon_int = hapd->iconf->beacon_int;
+	params->basic_rates = hapd->iface->basic_rates;
+	params->beacon_rate = hapd->iconf->beacon_rate;
+	params->rate_type = hapd->iconf->rate_type;
+	params->ssid = hapd->conf->ssid.ssid;
+	params->ssid_len = hapd->conf->ssid.ssid_len;
+	if ((hapd->conf->wpa & (WPA_PROTO_WPA | WPA_PROTO_RSN)) ==
+	    (WPA_PROTO_WPA | WPA_PROTO_RSN))
+		params->pairwise_ciphers = hapd->conf->wpa_pairwise |
+			hapd->conf->rsn_pairwise;
+	else if (hapd->conf->wpa & WPA_PROTO_RSN)
+		params->pairwise_ciphers = hapd->conf->rsn_pairwise;
+	else if (hapd->conf->wpa & WPA_PROTO_WPA)
+		params->pairwise_ciphers = hapd->conf->wpa_pairwise;
+	params->group_cipher = hapd->conf->wpa_group;
+	params->key_mgmt_suites = hapd->conf->wpa_key_mgmt;
+	params->auth_algs = hapd->conf->auth_algs;
+	params->wpa_version = hapd->conf->wpa;
+	params->privacy = hapd->conf->wpa;
+#ifdef CONFIG_WEP
+	params->privacy |= hapd->conf->ssid.wep.keys_set ||
+		(hapd->conf->ieee802_1x &&
+		 (hapd->conf->default_wep_key_len ||
+		  hapd->conf->individual_wep_key_len));
+#endif /* CONFIG_WEP */
+	switch (hapd->conf->ignore_broadcast_ssid) {
+	case 0:
+		params->hide_ssid = NO_SSID_HIDING;
+		break;
+	case 1:
+		params->hide_ssid = HIDDEN_SSID_ZERO_LEN;
+		break;
+	case 2:
+		params->hide_ssid = HIDDEN_SSID_ZERO_CONTENTS;
+		break;
+	}
+	params->isolate = hapd->conf->isolate;
+#ifdef NEED_AP_MLME
+	params->cts_protect = !!(ieee802_11_erp_info(hapd) &
+				ERP_INFO_USE_PROTECTION);
+	params->preamble = hapd->iface->num_sta_no_short_preamble == 0 &&
+		hapd->iconf->preamble == SHORT_PREAMBLE;
+	if (hapd->iface->current_mode &&
+	    hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
+		params->short_slot_time =
+			hapd->iface->num_sta_no_short_slot_time > 0 ? 0 : 1;
+	else
+		params->short_slot_time = -1;
+	if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n)
+		params->ht_opmode = -1;
+	else
+		params->ht_opmode = hapd->iface->ht_op_mode;
+#endif /* NEED_AP_MLME */
+	params->interworking = hapd->conf->interworking;
+	if (hapd->conf->interworking &&
+	    !is_zero_ether_addr(hapd->conf->hessid))
+		params->hessid = hapd->conf->hessid;
+	params->access_network_type = hapd->conf->access_network_type;
+	params->ap_max_inactivity = hapd->conf->ap_max_inactivity;
+#ifdef CONFIG_P2P
+	params->p2p_go_ctwindow = hapd->iconf->p2p_go_ctwindow;
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_HS20
+	params->disable_dgaf = hapd->conf->disable_dgaf;
+	if (hapd->conf->osen) {
+		params->privacy = 1;
+		params->osen = 1;
+	}
+#endif /* CONFIG_HS20 */
+	params->multicast_to_unicast = hapd->conf->multicast_to_unicast;
+	params->pbss = hapd->conf->pbss;
+
+	if (hapd->conf->ftm_responder) {
+		if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_FTM_RESPONDER) {
+			params->ftm_responder = 1;
+			params->lci = hapd->iface->conf->lci;
+			params->civic = hapd->iface->conf->civic;
+		} else {
+			wpa_printf(MSG_WARNING,
+				   "Not configuring FTM responder as the driver doesn't advertise support for it");
+		}
+	}
+
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->conf->mld_ap && hapd->iconf->ieee80211be &&
+	    !hapd->conf->disable_11be) {
+		params->mld_ap = true;
+		params->mld_link_id = hapd->mld_link_id;
+	}
+#endif /* CONFIG_IEEE80211BE */
+
+	return 0;
+}
+
+
+void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params)
+{
+	os_free(params->tail);
+	params->tail = NULL;
+	os_free(params->head);
+	params->head = NULL;
+	os_free(params->proberesp);
+	params->proberesp = NULL;
+	os_free(params->mbssid_elem);
+	params->mbssid_elem = NULL;
+	os_free(params->mbssid_elem_offset);
+	params->mbssid_elem_offset = NULL;
+	os_free(params->rnr_elem);
+	params->rnr_elem = NULL;
+	os_free(params->rnr_elem_offset);
+	params->rnr_elem_offset = NULL;
+#ifdef CONFIG_FILS
+	os_free(params->fd_frame_tmpl);
+	params->fd_frame_tmpl = NULL;
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_IEEE80211AX
+	os_free(params->unsol_bcast_probe_resp_tmpl);
+	params->unsol_bcast_probe_resp_tmpl = NULL;
+#endif /* CONFIG_IEEE80211AX */
+	os_free(params->allowed_freqs);
+	params->allowed_freqs = NULL;
+}
+
+
+static int __ieee802_11_set_beacon(struct hostapd_data *hapd)
+{
+	struct wpa_driver_ap_params params;
+	struct hostapd_freq_params freq;
+	struct hostapd_iface *iface = hapd->iface;
+	struct hostapd_config *iconf = iface->conf;
+	struct hostapd_hw_modes *cmode = iface->current_mode;
+	struct wpabuf *beacon, *proberesp, *assocresp;
+	int res, ret = -1, i;
+	struct hostapd_hw_modes *mode;
+
+	if (!hapd->drv_priv) {
+		wpa_printf(MSG_ERROR, "Interface is disabled");
+		return -1;
+	}
+
+	hapd->beacon_set_done = 1;
+
+	if (ieee802_11_build_ap_params(hapd, &params) < 0)
+		return -1;
+
+	if (hostapd_build_ap_extra_ies(hapd, &beacon, &proberesp, &assocresp) <
+	    0)
+		goto fail;
+
+	params.beacon_ies = beacon;
+	params.proberesp_ies = proberesp;
+	params.assocresp_ies = assocresp;
+	params.reenable = hapd->reenable_beacon;
+#ifdef CONFIG_IEEE80211AX
+	params.he_spr_ctrl = hapd->iface->conf->spr.sr_control;
+	params.he_spr_non_srg_obss_pd_max_offset =
+		hapd->iface->conf->spr.non_srg_obss_pd_max_offset;
+	params.he_spr_srg_obss_pd_min_offset =
+		hapd->iface->conf->spr.srg_obss_pd_min_offset;
+	params.he_spr_srg_obss_pd_max_offset =
+		hapd->iface->conf->spr.srg_obss_pd_max_offset;
+	os_memcpy(params.he_spr_bss_color_bitmap,
+		  hapd->iface->conf->spr.srg_bss_color_bitmap, 8);
+	os_memcpy(params.he_spr_partial_bssid_bitmap,
+		  hapd->iface->conf->spr.srg_partial_bssid_bitmap, 8);
+	params.he_bss_color_disabled =
+		hapd->iface->conf->he_op.he_bss_color_disabled;
+	params.he_bss_color_partial =
+		hapd->iface->conf->he_op.he_bss_color_partial;
+	params.he_bss_color = hapd->iface->conf->he_op.he_bss_color;
+	params.twt_responder = hostapd_get_he_twt_responder(hapd,
+							    IEEE80211_MODE_AP);
+	params.unsol_bcast_probe_resp_tmpl =
+		hostapd_unsol_bcast_probe_resp(hapd, &params);
+#endif /* CONFIG_IEEE80211AX */
+	hapd->reenable_beacon = 0;
+#ifdef CONFIG_SAE
+	params.sae_pwe = hapd->conf->sae_pwe;
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_FILS
+	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,
+				    iconf->edmg_channel, iconf->ieee80211n,
+				    iconf->ieee80211ac, iconf->ieee80211ax,
+				    iconf->ieee80211be,
+				    iconf->secondary_channel,
+				    hostapd_get_oper_chwidth(iconf),
+				    hostapd_get_oper_centr_freq_seg0_idx(iconf),
+				    hostapd_get_oper_centr_freq_seg1_idx(iconf),
+				    cmode->vht_capab,
+				    &cmode->he_capab[IEEE80211_MODE_AP],
+				    &cmode->eht_capab[IEEE80211_MODE_AP]) == 0)
+		params.freq = &freq;
+
+	for (i = 0; i < hapd->iface->num_hw_features; i++) {
+		mode = &hapd->iface->hw_features[i];
+
+		if (iconf->hw_mode != HOSTAPD_MODE_IEEE80211ANY &&
+		    iconf->hw_mode != mode->mode)
+			continue;
+
+		hostapd_get_hw_mode_any_channels(hapd, mode,
+						 !(iconf->acs_freq_list.num ||
+						   iconf->acs_ch_list.num),
+						 true, &params.allowed_freqs);
+	}
+
+	res = hostapd_drv_set_ap(hapd, &params);
+	hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp);
+	if (res)
+		wpa_printf(MSG_ERROR, "Failed to set beacon parameters");
+	else
+		ret = 0;
+fail:
+	ieee802_11_free_ap_params(&params);
+	return ret;
+}
+
+
+int ieee802_11_set_beacon(struct hostapd_data *hapd)
+{
+	struct hostapd_iface *iface = hapd->iface;
+	int ret;
+	size_t i, j;
+	bool is_6g;
+
+	ret = __ieee802_11_set_beacon(hapd);
+	if (ret != 0)
+		return ret;
+
+	if (!iface->interfaces || iface->interfaces->count <= 1)
+		return 0;
+
+	/* Update Beacon frames in case of 6 GHz colocation or AP MLD */
+	is_6g = is_6ghz_op_class(iface->conf->op_class);
+	for (j = 0; j < iface->interfaces->count; j++) {
+		struct hostapd_iface *other;
+		bool mld_ap = false;
+
+		other = iface->interfaces->iface[j];
+		if (other == iface || !other || !other->conf)
+			continue;
+
+#ifdef CONFIG_IEEE80211BE
+		if (hapd->conf->mld_ap && other->bss[0]->conf->mld_ap &&
+		    hapd->conf->mld_id == other->bss[0]->conf->mld_id)
+			mld_ap = true;
+#endif /* CONFIG_IEEE80211BE */
+
+		if (is_6g == is_6ghz_op_class(other->conf->op_class) &&
+		    !mld_ap)
+			continue;
+
+		for (i = 0; i < other->num_bss; i++) {
+			if (other->bss[i] && other->bss[i]->started)
+				__ieee802_11_set_beacon(other->bss[i]);
+		}
+	}
+
+	return 0;
+}
+
+
+int ieee802_11_set_beacons(struct hostapd_iface *iface)
+{
+	size_t i;
+	int ret = 0;
+
+	for (i = 0; i < iface->num_bss; i++) {
+		if (iface->bss[i]->started &&
+		    ieee802_11_set_beacon(iface->bss[i]) < 0)
+			ret = -1;
+	}
+
+	return ret;
+}
+
+
+/* only update beacons if started */
+int ieee802_11_update_beacons(struct hostapd_iface *iface)
+{
+	size_t i;
+	int ret = 0;
+
+	for (i = 0; i < iface->num_bss; i++) {
+		if (iface->bss[i]->beacon_set_done && iface->bss[i]->started &&
+		    ieee802_11_set_beacon(iface->bss[i]) < 0)
+			ret = -1;
+	}
+
+	return ret;
+}
+
+#endif /* CONFIG_NATIVE_WINDOWS */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/beacon.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/beacon.h
new file mode 100644
index 0000000..c320825
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/beacon.h
@@ -0,0 +1,35 @@
+/*
+ * hostapd / IEEE 802.11 Management: Beacon and Probe Request/Response
+ * Copyright (c) 2002-2004, Instant802 Networks, Inc.
+ * Copyright (c) 2005-2006, Devicescape Software, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef BEACON_H
+#define BEACON_H
+
+struct ieee80211_mgmt;
+
+void handle_probe_req(struct hostapd_data *hapd,
+		      const struct ieee80211_mgmt *mgmt, size_t len,
+		      int ssi_signal);
+int ieee802_11_set_beacon(struct hostapd_data *hapd);
+int ieee802_11_set_beacons(struct hostapd_iface *iface);
+int ieee802_11_update_beacons(struct hostapd_iface *iface);
+int ieee802_11_build_ap_params(struct hostapd_data *hapd,
+			       struct wpa_driver_ap_params *params);
+void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params);
+void sta_track_add(struct hostapd_iface *iface, const u8 *addr, int ssi_signal);
+void sta_track_del(struct hostapd_sta_info *info);
+void sta_track_expire(struct hostapd_iface *iface, int force);
+struct hostapd_data *
+sta_track_seen_on(struct hostapd_iface *iface, const u8 *addr,
+		  const char *ifname);
+void sta_track_claim_taxonomy_info(struct hostapd_iface *iface, const u8 *addr,
+				   struct wpabuf **probe_ie_taxonomy);
+
+const u8 * hostapd_wpa_ie(struct hostapd_data *hapd, u8 eid);
+
+#endif /* BEACON_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/bss_load.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/bss_load.c
new file mode 100644
index 0000000..725d3cd
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/bss_load.c
@@ -0,0 +1,99 @@
+/*
+ * BSS Load Element / Channel Utilization
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "hostapd.h"
+#include "bss_load.h"
+#include "ap_drv_ops.h"
+#include "beacon.h"
+
+
+static int get_bss_load_update_timeout(struct hostapd_data *hapd,
+				       unsigned int *sec, unsigned int *usec)
+{
+	unsigned int update_period = hapd->conf->bss_load_update_period;
+	unsigned int beacon_int = hapd->iconf->beacon_int;
+	unsigned int update_timeout;
+
+	if (!update_period || !beacon_int) {
+		wpa_printf(MSG_ERROR,
+			   "BSS Load: Invalid BSS load update configuration (period=%u beacon_int=%u)",
+			   update_period, beacon_int);
+		return -1;
+	}
+
+	update_timeout = update_period * beacon_int;
+
+	*sec = ((update_timeout / 1000) * 1024) / 1000;
+	*usec = (update_timeout % 1000) * 1024;
+
+	return 0;
+}
+
+
+static void update_channel_utilization(void *eloop_data, void *user_data)
+{
+	struct hostapd_data *hapd = eloop_data;
+	unsigned int sec, usec;
+	int err;
+	struct hostapd_iface *iface = hapd->iface;
+
+	if (!(hapd->beacon_set_done && hapd->started))
+		return;
+
+	err = hostapd_drv_get_survey(hapd, hapd->iface->freq);
+	if (err) {
+		wpa_printf(MSG_ERROR, "BSS Load: Failed to get survey data");
+		return;
+	}
+
+	ieee802_11_set_beacon(hapd);
+
+	if (get_bss_load_update_timeout(hapd, &sec, &usec) < 0)
+		return;
+
+	if (hapd->conf->chan_util_avg_period) {
+		iface->chan_util_samples_sum += iface->channel_utilization;
+		iface->chan_util_num_sample_periods +=
+			hapd->conf->bss_load_update_period;
+		if (iface->chan_util_num_sample_periods >=
+		    hapd->conf->chan_util_avg_period) {
+			iface->chan_util_average =
+				iface->chan_util_samples_sum /
+				(iface->chan_util_num_sample_periods /
+				 hapd->conf->bss_load_update_period);
+			iface->chan_util_samples_sum = 0;
+			iface->chan_util_num_sample_periods = 0;
+		}
+	}
+
+	eloop_register_timeout(sec, usec, update_channel_utilization, hapd,
+			       NULL);
+}
+
+
+int bss_load_update_init(struct hostapd_data *hapd)
+{
+	unsigned int sec, usec;
+
+	if (get_bss_load_update_timeout(hapd, &sec, &usec) < 0)
+		return -1;
+
+	eloop_register_timeout(sec, usec, update_channel_utilization, hapd,
+			       NULL);
+	return 0;
+}
+
+
+void bss_load_update_deinit(struct hostapd_data *hapd)
+{
+	eloop_cancel_timeout(update_channel_utilization, hapd, NULL);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/bss_load.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/bss_load.h
new file mode 100644
index 0000000..ac3c793
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/bss_load.h
@@ -0,0 +1,17 @@
+/*
+ * BSS load update
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef BSS_LOAD_UPDATE_H
+#define BSS_LOAD_UPDATE_H
+
+
+int bss_load_update_init(struct hostapd_data *hapd);
+void bss_load_update_deinit(struct hostapd_data *hapd);
+
+
+#endif /* BSS_LOAD_UPDATE_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/comeback_token.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/comeback_token.c
new file mode 100644
index 0000000..8d9f21b
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/comeback_token.c
@@ -0,0 +1,139 @@
+/*
+ * hostapd / Comeback token mechanism for SAE
+ * Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "hostapd.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "common/ieee802_11_defs.h"
+#include "comeback_token.h"
+
+
+#if defined(CONFIG_SAE) || defined(CONFIG_PASN)
+
+static int comeback_token_hash(const u8 *comeback_key, const u8 *addr, u8 *idx)
+{
+	u8 hash[SHA256_MAC_LEN];
+
+	if (hmac_sha256(comeback_key, COMEBACK_KEY_SIZE,
+			addr, ETH_ALEN, hash) < 0)
+		return -1;
+	*idx = hash[0];
+	return 0;
+}
+
+
+int check_comeback_token(const u8 *comeback_key,
+			 u16 *comeback_pending_idx, const u8 *addr,
+			 const u8 *token, size_t token_len)
+{
+	u8 mac[SHA256_MAC_LEN];
+	const u8 *addrs[2];
+	size_t len[2];
+	u16 token_idx;
+	u8 idx;
+
+	if (token_len != SHA256_MAC_LEN ||
+	    comeback_token_hash(comeback_key, addr, &idx) < 0)
+		return -1;
+	token_idx = comeback_pending_idx[idx];
+	if (token_idx == 0 || token_idx != WPA_GET_BE16(token)) {
+		wpa_printf(MSG_DEBUG,
+			   "Comeback: Invalid anti-clogging token from "
+			   MACSTR " - token_idx 0x%04x, expected 0x%04x",
+			   MAC2STR(addr), WPA_GET_BE16(token), token_idx);
+		return -1;
+	}
+
+	addrs[0] = addr;
+	len[0] = ETH_ALEN;
+	addrs[1] = token;
+	len[1] = 2;
+	if (hmac_sha256_vector(comeback_key, COMEBACK_KEY_SIZE,
+			       2, addrs, len, mac) < 0 ||
+	    os_memcmp_const(token + 2, &mac[2], SHA256_MAC_LEN - 2) != 0)
+		return -1;
+
+	comeback_pending_idx[idx] = 0; /* invalidate used token */
+
+	return 0;
+}
+
+
+struct wpabuf *
+auth_build_token_req(struct os_reltime *last_comeback_key_update,
+		     u8 *comeback_key, u16 comeback_idx,
+		     u16 *comeback_pending_idx, size_t idx_len,
+		     int group, const u8 *addr, int h2e)
+{
+	struct wpabuf *buf;
+	u8 *token;
+	struct os_reltime now;
+	u8 idx[2];
+	const u8 *addrs[2];
+	size_t len[2];
+	u8 p_idx;
+	u16 token_idx;
+
+	os_get_reltime(&now);
+	if (!os_reltime_initialized(last_comeback_key_update) ||
+	    os_reltime_expired(&now, last_comeback_key_update, 60) ||
+	    comeback_idx == 0xffff) {
+		if (random_get_bytes(comeback_key, COMEBACK_KEY_SIZE) < 0)
+			return NULL;
+		wpa_hexdump(MSG_DEBUG, "Comeback: Updated token key",
+			    comeback_key, COMEBACK_KEY_SIZE);
+		*last_comeback_key_update = now;
+		comeback_idx = 0;
+		os_memset(comeback_pending_idx, 0, idx_len);
+	}
+
+	buf = wpabuf_alloc(sizeof(le16) + 3 + SHA256_MAC_LEN);
+	if (buf == NULL)
+		return NULL;
+
+	if (group)
+		wpabuf_put_le16(buf, group); /* Finite Cyclic Group */
+
+	if (h2e) {
+		/* Encapsulate Anti-clogging Token field in a container IE */
+		wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+		wpabuf_put_u8(buf, 1 + SHA256_MAC_LEN);
+		wpabuf_put_u8(buf, WLAN_EID_EXT_ANTI_CLOGGING_TOKEN);
+	}
+
+	if (comeback_token_hash(comeback_key, addr, &p_idx) < 0) {
+		wpabuf_free(buf);
+		return NULL;
+	}
+
+	token_idx = comeback_pending_idx[p_idx];
+	if (!token_idx) {
+		comeback_idx++;
+		token_idx = comeback_idx;
+		comeback_pending_idx[p_idx] = token_idx;
+	}
+	WPA_PUT_BE16(idx, token_idx);
+	token = wpabuf_put(buf, SHA256_MAC_LEN);
+	addrs[0] = addr;
+	len[0] = ETH_ALEN;
+	addrs[1] = idx;
+	len[1] = sizeof(idx);
+	if (hmac_sha256_vector(comeback_key, COMEBACK_KEY_SIZE,
+			       2, addrs, len, token) < 0) {
+		wpabuf_free(buf);
+		return NULL;
+	}
+	WPA_PUT_BE16(token, token_idx);
+
+	return buf;
+}
+
+#endif /* defined(CONFIG_SAE) || defined(CONFIG_PASN) */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/comeback_token.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/comeback_token.h
new file mode 100644
index 0000000..d5de9e6
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/comeback_token.h
@@ -0,0 +1,21 @@
+/*
+ * hostapd / Comeback token mechanism for SAE
+ * Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef COMEBACK_TOKEN_H
+#define COMEBACK_TOKEN_H
+
+int check_comeback_token(const u8 *comeback_key,
+			 u16 *comeback_pending_idx, const u8 *addr,
+			 const u8 *token, size_t token_len);
+struct wpabuf *
+auth_build_token_req(struct os_reltime *last_comeback_key_update,
+		     u8 *comeback_key, u16 comeback_idx,
+		     u16 *comeback_pending_idx, size_t idx_len,
+		     int group, const u8 *addr, int h2e);
+
+#endif /* COMEBACK_TOKEN_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ctrl_iface_ap.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ctrl_iface_ap.c
new file mode 100644
index 0000000..50a4dc4
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ctrl_iface_ap.c
@@ -0,0 +1,1501 @@
+/*
+ * Control interface for shared AP commands
+ * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/sae.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "fst/fst_ctrl_iface.h"
+#include "hostapd.h"
+#include "ieee802_1x.h"
+#include "wpa_auth.h"
+#include "ieee802_11.h"
+#include "sta_info.h"
+#include "wps_hostapd.h"
+#include "p2p_hostapd.h"
+#include "ctrl_iface_ap.h"
+#include "ap_drv_ops.h"
+#include "mbo_ap.h"
+#include "taxonomy.h"
+#include "wnm_ap.h"
+
+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";
+}
+
+#ifdef CONFIG_CTRL_IFACE_MIB
+
+static size_t hostapd_write_ht_mcs_bitmask(char *buf, size_t buflen,
+					   size_t curr_len, const u8 *mcs_set)
+{
+	int ret;
+	size_t len = curr_len;
+
+	ret = os_snprintf(buf + len, buflen - len,
+			  "ht_mcs_bitmask=");
+	if (os_snprintf_error(buflen - len, ret))
+		return len;
+	len += ret;
+
+	/* 77 first bits (+ 3 reserved bits) */
+	len += wpa_snprintf_hex(buf + len, buflen - len, mcs_set, 10);
+
+	ret = os_snprintf(buf + len, buflen - len, "\n");
+	if (os_snprintf_error(buflen - len, ret))
+		return curr_len;
+	len += ret;
+
+	return len;
+}
+
+
+static int hostapd_get_sta_conn_time(struct sta_info *sta,
+				     struct hostap_sta_driver_data *data,
+				     char *buf, size_t buflen)
+{
+	struct os_reltime age;
+	unsigned long secs;
+	int ret;
+
+	if (sta->connected_time.sec) {
+		/* Locally maintained time in AP mode */
+		os_reltime_age(&sta->connected_time, &age);
+		secs = (unsigned long) age.sec;
+	} else if (data->flags & STA_DRV_DATA_CONN_TIME) {
+		/* Time from the driver in mesh mode */
+		secs = data->connected_sec;
+	} else {
+		return 0;
+	}
+
+	ret = os_snprintf(buf, buflen, "connected_time=%lu\n", secs);
+	if (os_snprintf_error(buflen, ret))
+		return 0;
+	return ret;
+}
+
+
+static int hostapd_get_sta_info(struct hostapd_data *hapd,
+				struct sta_info *sta,
+				char *buf, size_t buflen)
+{
+	struct hostap_sta_driver_data data;
+	int ret;
+	int len = 0;
+
+	if (hostapd_drv_read_sta_data(hapd, &data, sta->addr) < 0)
+		return 0;
+
+	ret = os_snprintf(buf, buflen, "rx_packets=%lu\ntx_packets=%lu\n"
+			  "rx_bytes=%llu\ntx_bytes=%llu\ninactive_msec=%lu\n"
+			  "signal=%d\n",
+			  data.rx_packets, data.tx_packets,
+			  data.rx_bytes, data.tx_bytes, data.inactive_msec,
+			  data.signal);
+	if (os_snprintf_error(buflen, ret))
+		return 0;
+	len += ret;
+
+	ret = os_snprintf(buf + len, buflen - len, "rx_rate_info=%lu",
+			  data.current_rx_rate / 100);
+	if (os_snprintf_error(buflen - len, ret))
+		return len;
+	len += ret;
+	if (data.flags & STA_DRV_DATA_RX_MCS) {
+		ret = os_snprintf(buf + len, buflen - len, " mcs %u",
+				  data.rx_mcs);
+		if (!os_snprintf_error(buflen - len, ret))
+			len += ret;
+	}
+	if (data.flags & STA_DRV_DATA_RX_VHT_MCS) {
+		ret = os_snprintf(buf + len, buflen - len, " vhtmcs %u",
+				  data.rx_vhtmcs);
+		if (!os_snprintf_error(buflen - len, ret))
+			len += ret;
+	}
+	if (data.flags & STA_DRV_DATA_RX_VHT_NSS) {
+		ret = os_snprintf(buf + len, buflen - len, " vhtnss %u",
+				  data.rx_vht_nss);
+		if (!os_snprintf_error(buflen - len, ret))
+			len += ret;
+	}
+	if (data.flags & STA_DRV_DATA_RX_SHORT_GI) {
+		ret = os_snprintf(buf + len, buflen - len, " shortGI");
+		if (!os_snprintf_error(buflen - len, ret))
+			len += ret;
+	}
+	ret = os_snprintf(buf + len, buflen - len, "\n");
+	if (!os_snprintf_error(buflen - len, ret))
+		len += ret;
+
+	ret = os_snprintf(buf + len, buflen - len, "tx_rate_info=%lu",
+			  data.current_tx_rate / 100);
+	if (os_snprintf_error(buflen - len, ret))
+		return len;
+	len += ret;
+	if (data.flags & STA_DRV_DATA_TX_MCS) {
+		ret = os_snprintf(buf + len, buflen - len, " mcs %u",
+				  data.tx_mcs);
+		if (!os_snprintf_error(buflen - len, ret))
+			len += ret;
+	}
+	if (data.flags & STA_DRV_DATA_TX_VHT_MCS) {
+		ret = os_snprintf(buf + len, buflen - len, " vhtmcs %u",
+				  data.tx_vhtmcs);
+		if (!os_snprintf_error(buflen - len, ret))
+			len += ret;
+	}
+	if (data.flags & STA_DRV_DATA_TX_VHT_NSS) {
+		ret = os_snprintf(buf + len, buflen - len, " vhtnss %u",
+				  data.tx_vht_nss);
+		if (!os_snprintf_error(buflen - len, ret))
+			len += ret;
+	}
+	if (data.flags & STA_DRV_DATA_TX_SHORT_GI) {
+		ret = os_snprintf(buf + len, buflen - len, " shortGI");
+		if (!os_snprintf_error(buflen - len, ret))
+			len += ret;
+	}
+	ret = os_snprintf(buf + len, buflen - len, "\n");
+	if (!os_snprintf_error(buflen - len, ret))
+		len += ret;
+
+	if ((sta->flags & WLAN_STA_VHT) && sta->vht_capabilities) {
+		ret = os_snprintf(buf + len, buflen - len,
+				  "rx_vht_mcs_map=%04x\n"
+				  "tx_vht_mcs_map=%04x\n",
+				  le_to_host16(sta->vht_capabilities->
+					       vht_supported_mcs_set.rx_map),
+				  le_to_host16(sta->vht_capabilities->
+					       vht_supported_mcs_set.tx_map));
+		if (!os_snprintf_error(buflen - len, ret))
+			len += ret;
+	}
+
+	if ((sta->flags & WLAN_STA_HT) && sta->ht_capabilities) {
+		len = hostapd_write_ht_mcs_bitmask(buf, buflen, len,
+						   sta->ht_capabilities->
+						   supported_mcs_set);
+	}
+
+	if (data.flags & STA_DRV_DATA_LAST_ACK_RSSI) {
+		ret = os_snprintf(buf + len, buflen - len,
+				  "last_ack_signal=%d\n", data.last_ack_rssi);
+		if (!os_snprintf_error(buflen - len, ret))
+			len += ret;
+	}
+
+	len += hostapd_get_sta_conn_time(sta, &data, buf + len, buflen - len);
+
+	return len;
+}
+
+
+static const char * timeout_next_str(int val)
+{
+	switch (val) {
+	case STA_NULLFUNC:
+		return "NULLFUNC POLL";
+	case STA_DISASSOC:
+		return "DISASSOC";
+	case STA_DEAUTH:
+		return "DEAUTH";
+	case STA_REMOVE:
+		return "REMOVE";
+	case STA_DISASSOC_FROM_CLI:
+		return "DISASSOC_FROM_CLI";
+	default:
+		return "?";
+	}
+}
+
+
+static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd,
+				      struct sta_info *sta,
+				      char *buf, size_t buflen)
+{
+	int len, res, ret, i;
+	const char *keyid;
+	const u8 *dpp_pkhash;
+
+	if (!sta)
+		return 0;
+
+	len = 0;
+	ret = os_snprintf(buf + len, buflen - len, MACSTR "\nflags=",
+			  MAC2STR(sta->addr));
+	if (os_snprintf_error(buflen - len, ret))
+		return len;
+	len += ret;
+
+	ret = ap_sta_flags_txt(sta->flags, buf + len, buflen - len);
+	if (ret < 0)
+		return len;
+	len += ret;
+
+	ret = os_snprintf(buf + len, buflen - len, "\naid=%d\ncapability=0x%x\n"
+			  "listen_interval=%d\nsupported_rates=",
+			  sta->aid, sta->capability, sta->listen_interval);
+	if (os_snprintf_error(buflen - len, ret))
+		return len;
+	len += ret;
+
+	for (i = 0; i < sta->supported_rates_len; i++) {
+		ret = os_snprintf(buf + len, buflen - len, "%02x%s",
+				  sta->supported_rates[i],
+				  i + 1 < sta->supported_rates_len ? " " : "");
+		if (os_snprintf_error(buflen - len, ret))
+			return len;
+		len += ret;
+	}
+
+	ret = os_snprintf(buf + len, buflen - len, "\ntimeout_next=%s\n",
+			  timeout_next_str(sta->timeout_next));
+	if (os_snprintf_error(buflen - len, ret))
+		return len;
+	len += ret;
+
+	res = ieee802_11_get_mib_sta(hapd, sta, buf + len, buflen - len);
+	if (res >= 0)
+		len += res;
+	res = wpa_get_mib_sta(sta->wpa_sm, buf + len, buflen - len);
+	if (res >= 0)
+		len += res;
+	res = ieee802_1x_get_mib_sta(hapd, sta, buf + len, buflen - len);
+	if (res >= 0)
+		len += res;
+	res = hostapd_wps_get_mib_sta(hapd, sta->addr, buf + len,
+				      buflen - len);
+	if (res >= 0)
+		len += res;
+	res = hostapd_p2p_get_mib_sta(hapd, sta, buf + len, buflen - len);
+	if (res >= 0)
+		len += res;
+
+	len += hostapd_get_sta_info(hapd, sta, buf + len, buflen - len);
+
+#ifdef CONFIG_SAE
+	if (sta->sae && sta->sae->state == SAE_ACCEPTED) {
+		res = os_snprintf(buf + len, buflen - len, "sae_group=%d\n",
+				  sta->sae->group);
+		if (!os_snprintf_error(buflen - len, res))
+			len += res;
+	}
+
+	if (sta->sae && sta->sae->tmp) {
+		const u8 *pos;
+		unsigned int j, count;
+		struct wpabuf *groups = sta->sae->tmp->peer_rejected_groups;
+
+		res = os_snprintf(buf + len, buflen - len,
+				  "sae_rejected_groups=");
+		if (!os_snprintf_error(buflen - len, res))
+			len += res;
+
+		if (groups) {
+			pos = wpabuf_head(groups);
+			count = wpabuf_len(groups) / 2;
+		} else {
+			pos = NULL;
+			count = 0;
+		}
+		for (j = 0; pos && j < count; j++) {
+			res = os_snprintf(buf + len, buflen - len, "%s%d",
+					  j == 0 ? "" : " ", WPA_GET_LE16(pos));
+			if (!os_snprintf_error(buflen - len, res))
+				len += res;
+			pos += 2;
+		}
+
+		res = os_snprintf(buf + len, buflen - len, "\n");
+		if (!os_snprintf_error(buflen - len, res))
+			len += res;
+	}
+#endif /* CONFIG_SAE */
+
+	if (sta->vlan_id > 0) {
+		res = os_snprintf(buf + len, buflen - len, "vlan_id=%d\n",
+				  sta->vlan_id);
+		if (!os_snprintf_error(buflen - len, res))
+			len += res;
+	}
+
+	res = mbo_ap_get_info(sta, buf + len, buflen - len);
+	if (res >= 0)
+		len += res;
+
+	if (sta->supp_op_classes &&
+	    buflen - len > (unsigned) (17 + 2 * sta->supp_op_classes[0])) {
+		len += os_snprintf(buf + len, buflen - len, "supp_op_classes=");
+		len += wpa_snprintf_hex(buf + len, buflen - len,
+					sta->supp_op_classes + 1,
+					sta->supp_op_classes[0]);
+		len += os_snprintf(buf + len, buflen - len, "\n");
+	}
+
+	if (sta->power_capab) {
+		ret = os_snprintf(buf + len, buflen - len,
+				  "min_txpower=%d\n"
+				  "max_txpower=%d\n",
+				  sta->min_tx_power, sta->max_tx_power);
+		if (!os_snprintf_error(buflen - len, ret))
+			len += ret;
+	}
+
+#ifdef CONFIG_IEEE80211AC
+	if ((sta->flags & WLAN_STA_VHT) && sta->vht_capabilities) {
+		res = os_snprintf(buf + len, buflen - len,
+				  "vht_caps_info=0x%08x\n",
+				  le_to_host32(sta->vht_capabilities->
+					       vht_capabilities_info));
+		if (!os_snprintf_error(buflen - len, res))
+			len += res;
+	}
+#endif /* CONFIG_IEEE80211AC */
+
+	if ((sta->flags & WLAN_STA_HT) && sta->ht_capabilities) {
+		res = os_snprintf(buf + len, buflen - len,
+				  "ht_caps_info=0x%04x\n",
+				  le_to_host16(sta->ht_capabilities->
+					       ht_capabilities_info));
+		if (!os_snprintf_error(buflen - len, res))
+			len += res;
+	}
+
+	if (sta->ext_capability &&
+	    buflen - len > (unsigned) (11 + 2 * sta->ext_capability[0])) {
+		len += os_snprintf(buf + len, buflen - len, "ext_capab=");
+		len += wpa_snprintf_hex(buf + len, buflen - len,
+					sta->ext_capability + 1,
+					sta->ext_capability[0]);
+		len += os_snprintf(buf + len, buflen - len, "\n");
+	}
+
+	if (sta->flags & WLAN_STA_WDS && sta->ifname_wds) {
+		ret = os_snprintf(buf + len, buflen - len,
+				  "wds_sta_ifname=%s\n", sta->ifname_wds);
+		if (!os_snprintf_error(buflen - len, ret))
+			len += ret;
+	}
+
+	keyid = ap_sta_wpa_get_keyid(hapd, sta);
+	if (keyid) {
+		ret = os_snprintf(buf + len, buflen - len, "keyid=%s\n", keyid);
+		if (!os_snprintf_error(buflen - len, ret))
+			len += ret;
+	}
+
+	dpp_pkhash = ap_sta_wpa_get_dpp_pkhash(hapd, sta);
+	if (dpp_pkhash) {
+		ret = os_snprintf(buf + len, buflen - len, "dpp_pkhash=");
+		if (!os_snprintf_error(buflen - len, ret))
+			len += ret;
+		len += wpa_snprintf_hex(buf + len, buflen - len, dpp_pkhash,
+					SHA256_MAC_LEN);
+		ret = os_snprintf(buf + len, buflen - len, "\n");
+		if (!os_snprintf_error(buflen - len, ret))
+			len += ret;
+	}
+
+	return len;
+}
+
+
+int hostapd_ctrl_iface_sta_first(struct hostapd_data *hapd,
+				 char *buf, size_t buflen)
+{
+	return hostapd_ctrl_iface_sta_mib(hapd, hapd->sta_list, buf, buflen);
+}
+
+
+int hostapd_ctrl_iface_sta(struct hostapd_data *hapd, const char *txtaddr,
+			   char *buf, size_t buflen)
+{
+	u8 addr[ETH_ALEN];
+	int ret;
+	const char *pos;
+	struct sta_info *sta;
+
+	if (hwaddr_aton(txtaddr, addr)) {
+		ret = os_snprintf(buf, buflen, "FAIL\n");
+		if (os_snprintf_error(buflen, ret))
+			return 0;
+		return ret;
+	}
+
+	sta = ap_get_sta(hapd, addr);
+	if (sta == NULL)
+		return -1;
+
+	pos = os_strchr(txtaddr, ' ');
+	if (pos) {
+		pos++;
+
+#ifdef HOSTAPD_DUMP_STATE
+		if (os_strcmp(pos, "eapol") == 0) {
+			if (sta->eapol_sm == NULL)
+				return -1;
+			return eapol_auth_dump_state(sta->eapol_sm, buf,
+						     buflen);
+		}
+#endif /* HOSTAPD_DUMP_STATE */
+
+		return -1;
+	}
+
+	ret = hostapd_ctrl_iface_sta_mib(hapd, sta, buf, buflen);
+	ret += fst_ctrl_iface_mb_info(addr, buf + ret, buflen - ret);
+
+	return ret;
+}
+
+
+int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr,
+				char *buf, size_t buflen)
+{
+	u8 addr[ETH_ALEN];
+	struct sta_info *sta;
+	int ret;
+
+	if (hwaddr_aton(txtaddr, addr) ||
+	    (sta = ap_get_sta(hapd, addr)) == NULL) {
+		ret = os_snprintf(buf, buflen, "FAIL\n");
+		if (os_snprintf_error(buflen, ret))
+			return 0;
+		return ret;
+	}
+
+	if (!sta->next)
+		return 0;
+
+	return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen);
+}
+
+#endif
+
+#ifdef CONFIG_P2P_MANAGER
+static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype,
+				  u8 minor_reason_code, const u8 *addr)
+{
+	struct ieee80211_mgmt *mgmt;
+	int ret;
+	u8 *pos;
+
+	mgmt = os_zalloc(sizeof(*mgmt) + 100);
+	if (mgmt == NULL)
+		return -1;
+
+	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, stype);
+	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "P2P: Disconnect STA " MACSTR
+		" with minor reason code %u (stype=%u (%s))",
+		MAC2STR(addr), minor_reason_code, stype,
+		fc2str(le_to_host16(mgmt->frame_control)));
+
+	os_memcpy(mgmt->da, addr, ETH_ALEN);
+	os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+	os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+	if (stype == WLAN_FC_STYPE_DEAUTH) {
+		mgmt->u.deauth.reason_code =
+			host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
+		pos = mgmt->u.deauth.variable;
+	} else {
+		mgmt->u.disassoc.reason_code =
+			host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
+		pos = mgmt->u.disassoc.variable;
+	}
+
+	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
+	*pos++ = 4 + 3 + 1;
+	WPA_PUT_BE32(pos, P2P_IE_VENDOR_TYPE);
+	pos += 4;
+
+	*pos++ = P2P_ATTR_MINOR_REASON_CODE;
+	WPA_PUT_LE16(pos, 1);
+	pos += 2;
+	*pos++ = minor_reason_code;
+
+	ret = hostapd_drv_send_mlme(hapd, mgmt, pos - (u8 *) mgmt, 0, NULL, 0,
+				    0);
+	os_free(mgmt);
+
+	return ret < 0 ? -1 : 0;
+}
+#endif /* CONFIG_P2P_MANAGER */
+
+
+int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd,
+				      const char *txtaddr)
+{
+	u8 addr[ETH_ALEN];
+	struct sta_info *sta;
+	const char *pos;
+	u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
+
+	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DEAUTHENTICATE %s",
+		txtaddr);
+
+	if (hwaddr_aton(txtaddr, addr))
+		return -1;
+
+	pos = os_strstr(txtaddr, " reason=");
+	if (pos)
+		reason = atoi(pos + 8);
+
+	pos = os_strstr(txtaddr, " test=");
+	if (pos) {
+		struct ieee80211_mgmt mgmt;
+		int encrypt;
+
+		pos += 6;
+		encrypt = atoi(pos);
+		os_memset(&mgmt, 0, sizeof(mgmt));
+		mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+						  WLAN_FC_STYPE_DEAUTH);
+		os_memcpy(mgmt.da, addr, ETH_ALEN);
+		os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
+		os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
+		mgmt.u.deauth.reason_code = host_to_le16(reason);
+		if (hostapd_drv_send_mlme(hapd, (u8 *) &mgmt,
+					  IEEE80211_HDRLEN +
+					  sizeof(mgmt.u.deauth),
+					  0, NULL, 0, !encrypt) < 0)
+			return -1;
+		return 0;
+	}
+
+#ifdef CONFIG_P2P_MANAGER
+	pos = os_strstr(txtaddr, " p2p=");
+	if (pos) {
+		return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DEAUTH,
+					      atoi(pos + 5), addr);
+	}
+#endif /* CONFIG_P2P_MANAGER */
+
+	if (os_strstr(txtaddr, " tx=0"))
+		hostapd_drv_sta_remove(hapd, addr);
+	else
+		hostapd_drv_sta_deauth(hapd, addr, reason);
+	sta = ap_get_sta(hapd, addr);
+	if (sta)
+		ap_sta_deauthenticate(hapd, sta, reason);
+	else if (addr[0] == 0xff)
+		hostapd_free_stas(hapd);
+
+	return 0;
+}
+
+
+int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd,
+				    const char *txtaddr)
+{
+	u8 addr[ETH_ALEN];
+	struct sta_info *sta;
+	const char *pos;
+	u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
+
+	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DISASSOCIATE %s",
+		txtaddr);
+
+	if (hwaddr_aton(txtaddr, addr))
+		return -1;
+
+	pos = os_strstr(txtaddr, " reason=");
+	if (pos)
+		reason = atoi(pos + 8);
+
+	pos = os_strstr(txtaddr, " test=");
+	if (pos) {
+		struct ieee80211_mgmt mgmt;
+		int encrypt;
+
+		pos += 6;
+		encrypt = atoi(pos);
+		os_memset(&mgmt, 0, sizeof(mgmt));
+		mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+						  WLAN_FC_STYPE_DISASSOC);
+		os_memcpy(mgmt.da, addr, ETH_ALEN);
+		os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
+		os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
+		mgmt.u.disassoc.reason_code = host_to_le16(reason);
+		if (hostapd_drv_send_mlme(hapd, (u8 *) &mgmt,
+					  IEEE80211_HDRLEN +
+					  sizeof(mgmt.u.deauth),
+					  0, NULL, 0, !encrypt) < 0)
+			return -1;
+		return 0;
+	}
+
+#ifdef CONFIG_P2P_MANAGER
+	pos = os_strstr(txtaddr, " p2p=");
+	if (pos) {
+		return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DISASSOC,
+					      atoi(pos + 5), addr);
+	}
+#endif /* CONFIG_P2P_MANAGER */
+
+	if (os_strstr(txtaddr, " tx=0"))
+		hostapd_drv_sta_remove(hapd, addr);
+	else
+		hostapd_drv_sta_disassoc(hapd, addr, reason);
+	sta = ap_get_sta(hapd, addr);
+	if (sta)
+		ap_sta_disassociate(hapd, sta, reason);
+	else if (addr[0] == 0xff)
+		hostapd_free_stas(hapd);
+
+	return 0;
+}
+
+
+#ifdef CONFIG_TAXONOMY
+int hostapd_ctrl_iface_signature(struct hostapd_data *hapd,
+				 const char *txtaddr,
+				 char *buf, size_t buflen)
+{
+	u8 addr[ETH_ALEN];
+	struct sta_info *sta;
+
+	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE SIGNATURE %s", txtaddr);
+
+	if (hwaddr_aton(txtaddr, addr))
+		return -1;
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta)
+		return -1;
+
+	return retrieve_sta_taxonomy(hapd, sta, buf, buflen);
+}
+#endif /* CONFIG_TAXONOMY */
+
+
+int hostapd_ctrl_iface_poll_sta(struct hostapd_data *hapd,
+				const char *txtaddr)
+{
+	u8 addr[ETH_ALEN];
+	struct sta_info *sta;
+
+	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE POLL_STA %s", txtaddr);
+
+	if (hwaddr_aton(txtaddr, addr))
+		return -1;
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta)
+		return -1;
+
+	hostapd_drv_poll_client(hapd, hapd->own_addr, addr,
+				sta->flags & WLAN_STA_WMM);
+	return 0;
+}
+
+
+int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
+			      size_t buflen)
+{
+	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;
+
+	ret = os_snprintf(buf + len, buflen - len,
+			  "state=%s\n"
+			  "phy=%s\n"
+			  "freq=%d\n"
+			  "num_sta_non_erp=%d\n"
+			  "num_sta_no_short_slot_time=%d\n"
+			  "num_sta_no_short_preamble=%d\n"
+			  "olbc=%d\n"
+			  "num_sta_ht_no_gf=%d\n"
+			  "num_sta_no_ht=%d\n"
+			  "num_sta_ht_20_mhz=%d\n"
+			  "num_sta_ht40_intolerant=%d\n"
+			  "olbc_ht=%d\n"
+			  "ht_op_mode=0x%x\n",
+			  hostapd_state_text(iface->state),
+			  iface->phy,
+			  iface->freq,
+			  iface->num_sta_non_erp,
+			  iface->num_sta_no_short_slot_time,
+			  iface->num_sta_no_short_preamble,
+			  iface->olbc,
+			  iface->num_sta_ht_no_gf,
+			  iface->num_sta_no_ht,
+			  iface->num_sta_ht_20mhz,
+			  iface->num_sta_ht40_intolerant,
+			  iface->olbc_ht,
+			  iface->ht_op_mode);
+	if (os_snprintf_error(buflen - len, ret))
+		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"
+				  "cac_time_left_seconds=N/A\n",
+				  iface->dfs_cac_ms / 1000);
+	} else {
+		/* CAC started and CAC time set - calculate remaining time */
+		struct os_reltime now;
+		long left_time;
+
+		os_reltime_age(&iface->dfs_cac_start, &now);
+		left_time = (long) iface->dfs_cac_ms / 1000 - now.sec;
+		ret = os_snprintf(buf + len, buflen - len,
+				  "cac_time_seconds=%u\n"
+				  "cac_time_left_seconds=%lu\n",
+				  iface->dfs_cac_ms / 1000,
+				  left_time > 0 ? left_time : 0);
+	}
+	if (os_snprintf_error(buflen - len, ret))
+		return len;
+	len += ret;
+
+	ret = os_snprintf(buf + len, buflen - len,
+			  "channel=%u\n"
+			  "edmg_enable=%d\n"
+			  "edmg_channel=%d\n"
+			  "secondary_channel=%d\n"
+			  "ieee80211n=%d\n"
+			  "ieee80211ac=%d\n"
+			  "ieee80211ax=%d\n"
+			  "ieee80211be=%d\n"
+			  "beacon_int=%u\n"
+			  "dtim_period=%d\n",
+			  iface->conf->channel,
+			  iface->conf->enable_edmg,
+			  iface->conf->edmg_channel,
+			  iface->conf->ieee80211n && !hapd->conf->disable_11n ?
+			  iface->conf->secondary_channel : 0,
+			  iface->conf->ieee80211n && !hapd->conf->disable_11n,
+			  iface->conf->ieee80211ac &&
+			  !hapd->conf->disable_11ac,
+			  iface->conf->ieee80211ax &&
+			  !hapd->conf->disable_11ax,
+			  iface->conf->ieee80211be &&
+			  !hapd->conf->disable_11be,
+			  iface->conf->beacon_int,
+			  hapd->conf->dtim_period);
+	if (os_snprintf_error(buflen - len, ret))
+		return len;
+	len += ret;
+
+#ifdef CONFIG_IEEE80211BE
+	if (iface->conf->ieee80211be && !hapd->conf->disable_11be) {
+		ret = os_snprintf(buf + len, buflen - len,
+				  "eht_oper_chwidth=%d\n"
+				  "eht_oper_centr_freq_seg0_idx=%d\n",
+				  iface->conf->eht_oper_chwidth,
+				  iface->conf->eht_oper_centr_freq_seg0_idx);
+		if (os_snprintf_error(buflen - len, ret))
+			return len;
+		len += ret;
+	}
+#endif /* CONFIG_IEEE80211BE */
+
+#ifdef CONFIG_IEEE80211AX
+	if (iface->conf->ieee80211ax && !hapd->conf->disable_11ax) {
+		ret = os_snprintf(buf + len, buflen - len,
+				  "he_oper_chwidth=%d\n"
+				  "he_oper_centr_freq_seg0_idx=%d\n"
+				  "he_oper_centr_freq_seg1_idx=%d\n",
+				  iface->conf->he_oper_chwidth,
+				  iface->conf->he_oper_centr_freq_seg0_idx,
+				  iface->conf->he_oper_centr_freq_seg1_idx);
+		if (os_snprintf_error(buflen - len, ret))
+			return len;
+		len += ret;
+	}
+#endif /* CONFIG_IEEE80211AX */
+
+	if (iface->conf->ieee80211ac && !hapd->conf->disable_11ac) {
+		ret = os_snprintf(buf + len, buflen - len,
+				  "vht_oper_chwidth=%d\n"
+				  "vht_oper_centr_freq_seg0_idx=%d\n"
+				  "vht_oper_centr_freq_seg1_idx=%d\n"
+				  "vht_caps_info=%08x\n",
+				  iface->conf->vht_oper_chwidth,
+				  iface->conf->vht_oper_centr_freq_seg0_idx,
+				  iface->conf->vht_oper_centr_freq_seg1_idx,
+				  iface->conf->vht_capab);
+		if (os_snprintf_error(buflen - len, ret))
+			return len;
+		len += ret;
+	}
+
+	if (iface->conf->ieee80211ac && !hapd->conf->disable_11ac && mode) {
+		u16 rxmap = WPA_GET_LE16(&mode->vht_mcs_set[0]);
+		u16 txmap = WPA_GET_LE16(&mode->vht_mcs_set[4]);
+
+		ret = os_snprintf(buf + len, buflen - len,
+				  "rx_vht_mcs_map=%04x\n"
+				  "tx_vht_mcs_map=%04x\n",
+				  rxmap, txmap);
+		if (os_snprintf_error(buflen - len, ret))
+			return len;
+		len += ret;
+	}
+
+	if (iface->conf->ieee80211n && !hapd->conf->disable_11n) {
+		ret = os_snprintf(buf + len, buflen - len,
+				  "ht_caps_info=%04x\n",
+				  hapd->iconf->ht_capab);
+		if (os_snprintf_error(buflen - len, ret))
+			return len;
+		len += ret;
+	}
+#ifdef CONFIG_CTRL_IFACE_MIB
+	if (iface->conf->ieee80211n && !hapd->conf->disable_11n && mode) {
+		len = hostapd_write_ht_mcs_bitmask(buf, buflen, len,
+						   mode->mcs_set);
+	}
+#endif /* CONFIG_CTRL_IFACE_MIB */
+	if (iface->current_rates && iface->num_rates) {
+		ret = os_snprintf(buf + len, buflen - len, "supported_rates=");
+		if (os_snprintf_error(buflen - len, ret))
+			return len;
+		len += ret;
+
+		for (j = 0; j < iface->num_rates; j++) {
+			ret = os_snprintf(buf + len, buflen - len, "%s%02x",
+					  j > 0 ? " " : "",
+					  iface->current_rates[j].rate / 5);
+			if (os_snprintf_error(buflen - len, ret))
+				return len;
+			len += ret;
+		}
+		ret = os_snprintf(buf + len, buflen - len, "\n");
+		if (os_snprintf_error(buflen - len, ret))
+			return len;
+		len += ret;
+	}
+
+	for (j = 0; mode && j < mode->num_channels; j++) {
+		if (mode->channels[j].freq == iface->freq) {
+			ret = os_snprintf(buf + len, buflen - len,
+					  "max_txpower=%u\n",
+					  mode->channels[j].max_tx_power);
+			if (os_snprintf_error(buflen - len, ret))
+				return len;
+			len += ret;
+			break;
+		}
+	}
+
+	for (i = 0; i < iface->num_bss; i++) {
+		struct hostapd_data *bss = iface->bss[i];
+		ret = os_snprintf(buf + len, buflen - len,
+				  "bss[%d]=%s\n"
+				  "bssid[%d]=" MACSTR "\n"
+				  "ssid[%d]=%s\n"
+				  "num_sta[%d]=%d\n",
+				  (int) i, bss->conf->iface,
+				  (int) i, MAC2STR(bss->own_addr),
+				  (int) i,
+				  wpa_ssid_txt(bss->conf->ssid.ssid,
+					       bss->conf->ssid.ssid_len),
+				  (int) i, bss->num_sta);
+		if (os_snprintf_error(buflen - len, ret))
+			return len;
+		len += ret;
+
+#ifdef CONFIG_IEEE80211BE
+		if (bss->conf->mld_ap) {
+			ret = os_snprintf(buf + len, buflen - len,
+					  "mld_addr[%d]=" MACSTR "\n"
+					  "mld_id[%d]=%d\n"
+					  "mld_link_id[%d]=%d\n",
+					  (int) i, MAC2STR(bss->mld_addr),
+					  (int) i, bss->conf->mld_id,
+					  (int) i, bss->mld_link_id);
+			if (os_snprintf_error(buflen - len, ret))
+				return len;
+			len += ret;
+		}
+#endif /* CONFIG_IEEE80211BE */
+	}
+
+	if (hapd->conf->chan_util_avg_period) {
+		ret = os_snprintf(buf + len, buflen - len,
+				  "chan_util_avg=%u\n",
+				  iface->chan_util_average);
+		if (os_snprintf_error(buflen - len, ret))
+			return len;
+		len += ret;
+	}
+
+	return len;
+}
+
+
+int hostapd_parse_csa_settings(const char *pos,
+			       struct csa_settings *settings)
+{
+	char *end;
+
+	os_memset(settings, 0, sizeof(*settings));
+	settings->cs_count = strtol(pos, &end, 10);
+	if (pos == end) {
+		wpa_printf(MSG_ERROR, "chanswitch: invalid cs_count provided");
+		return -1;
+	}
+
+	settings->freq_params.freq = atoi(end);
+	if (settings->freq_params.freq == 0) {
+		wpa_printf(MSG_ERROR, "chanswitch: invalid freq provided");
+		return -1;
+	}
+
+#define SET_CSA_SETTING(str) \
+	do { \
+		const char *pos2 = os_strstr(pos, " " #str "="); \
+		if (pos2) { \
+			pos2 += sizeof(" " #str "=") - 1; \
+			settings->freq_params.str = atoi(pos2); \
+		} \
+	} 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;
+}
+
+
+int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd)
+{
+	struct hostapd_iface *iface = hapd->iface;
+	int i;
+
+	for (i = 0; i < iface->num_bss; i++)
+		hostapd_drv_stop_ap(iface->bss[i]);
+
+	return 0;
+}
+
+
+int hostapd_ctrl_iface_pmksa_list(struct hostapd_data *hapd, char *buf,
+				  size_t len)
+{
+	return wpa_auth_pmksa_list(hapd->wpa_auth, buf, len);
+}
+
+
+void hostapd_ctrl_iface_pmksa_flush(struct hostapd_data *hapd)
+{
+	wpa_auth_pmksa_flush(hapd->wpa_auth);
+}
+
+
+int hostapd_ctrl_iface_pmksa_add(struct hostapd_data *hapd, char *cmd)
+{
+	u8 spa[ETH_ALEN];
+	u8 pmkid[PMKID_LEN];
+	u8 pmk[PMK_LEN_MAX];
+	size_t pmk_len;
+	char *pos, *pos2;
+	int akmp = 0, expiration = 0;
+
+	/*
+	 * Entry format:
+	 * <STA addr> <PMKID> <PMK> <expiration in seconds> <akmp>
+	 */
+
+	if (hwaddr_aton(cmd, spa))
+		return -1;
+
+	pos = os_strchr(cmd, ' ');
+	if (!pos)
+		return -1;
+	pos++;
+
+	if (hexstr2bin(pos, pmkid, PMKID_LEN) < 0)
+		return -1;
+
+	pos = os_strchr(pos, ' ');
+	if (!pos)
+		return -1;
+	pos++;
+
+	pos2 = os_strchr(pos, ' ');
+	if (!pos2)
+		return -1;
+	pmk_len = (pos2 - pos) / 2;
+	if (pmk_len < PMK_LEN || pmk_len > PMK_LEN_MAX ||
+	    hexstr2bin(pos, pmk, pmk_len) < 0)
+		return -1;
+
+	pos = pos2 + 1;
+
+	if (sscanf(pos, "%d %d", &expiration, &akmp) != 2)
+		return -1;
+
+	return wpa_auth_pmksa_add2(hapd->wpa_auth, spa, pmk, pmk_len,
+				   pmkid, expiration, akmp);
+}
+
+
+#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
+#ifdef CONFIG_MESH
+
+int hostapd_ctrl_iface_pmksa_list_mesh(struct hostapd_data *hapd,
+				       const u8 *addr, char *buf, size_t len)
+{
+	return wpa_auth_pmksa_list_mesh(hapd->wpa_auth, addr, buf, len);
+}
+
+
+void * hostapd_ctrl_iface_pmksa_create_entry(const u8 *aa, char *cmd)
+{
+	u8 spa[ETH_ALEN];
+	u8 pmkid[PMKID_LEN];
+	u8 pmk[PMK_LEN_MAX];
+	char *pos;
+	int expiration;
+
+	/*
+	 * Entry format:
+	 * <BSSID> <PMKID> <PMK> <expiration in seconds>
+	 */
+
+	if (hwaddr_aton(cmd, spa))
+		return NULL;
+
+	pos = os_strchr(cmd, ' ');
+	if (!pos)
+		return NULL;
+	pos++;
+
+	if (hexstr2bin(pos, pmkid, PMKID_LEN) < 0)
+		return NULL;
+
+	pos = os_strchr(pos, ' ');
+	if (!pos)
+		return NULL;
+	pos++;
+
+	if (hexstr2bin(pos, pmk, PMK_LEN) < 0)
+		return NULL;
+
+	pos = os_strchr(pos, ' ');
+	if (!pos)
+		return NULL;
+	pos++;
+
+	if (sscanf(pos, "%d", &expiration) != 1)
+		return NULL;
+
+	return wpa_auth_pmksa_create_entry(aa, spa, pmk, PMK_LEN,
+					   WPA_KEY_MGMT_SAE, pmkid, expiration);
+}
+
+#endif /* CONFIG_MESH */
+#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
+
+
+#ifdef CONFIG_WNM_AP
+
+int hostapd_ctrl_iface_disassoc_imminent(struct hostapd_data *hapd,
+					 const char *cmd)
+{
+	u8 addr[ETH_ALEN];
+	int disassoc_timer;
+	struct sta_info *sta;
+
+	if (hwaddr_aton(cmd, addr))
+		return -1;
+	if (cmd[17] != ' ')
+		return -1;
+	disassoc_timer = atoi(cmd + 17);
+
+	sta = ap_get_sta(hapd, addr);
+	if (sta == NULL) {
+		wpa_printf(MSG_DEBUG, "Station " MACSTR
+			   " not found for disassociation imminent message",
+			   MAC2STR(addr));
+		return -1;
+	}
+
+	return wnm_send_disassoc_imminent(hapd, sta, disassoc_timer);
+}
+
+
+int hostapd_ctrl_iface_ess_disassoc(struct hostapd_data *hapd,
+				    const char *cmd)
+{
+	u8 addr[ETH_ALEN];
+	const char *url, *timerstr;
+	int disassoc_timer;
+	struct sta_info *sta;
+
+	if (hwaddr_aton(cmd, addr))
+		return -1;
+
+	sta = ap_get_sta(hapd, addr);
+	if (sta == NULL) {
+		wpa_printf(MSG_DEBUG, "Station " MACSTR
+			   " not found for ESS disassociation imminent message",
+			   MAC2STR(addr));
+		return -1;
+	}
+
+	timerstr = cmd + 17;
+	if (*timerstr != ' ')
+		return -1;
+	timerstr++;
+	disassoc_timer = atoi(timerstr);
+	if (disassoc_timer < 0 || disassoc_timer > 65535)
+		return -1;
+
+	url = os_strchr(timerstr, ' ');
+	if (url == NULL)
+		return -1;
+	url++;
+
+	return wnm_send_ess_disassoc_imminent(hapd, sta, url, disassoc_timer);
+}
+
+
+int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd,
+				  const char *cmd)
+{
+	u8 addr[ETH_ALEN];
+	const char *pos, *end;
+	int disassoc_timer = 0;
+	struct sta_info *sta;
+	u8 req_mode = 0, valid_int = 0x01, dialog_token = 0x01;
+	u8 bss_term_dur[12];
+	char *url = NULL;
+	int ret;
+	u8 nei_rep[1000];
+	int nei_len;
+	u8 mbo[10];
+	size_t mbo_len = 0;
+
+	if (hwaddr_aton(cmd, addr)) {
+		wpa_printf(MSG_DEBUG, "Invalid STA MAC address");
+		return -1;
+	}
+
+	sta = ap_get_sta(hapd, addr);
+	if (sta == NULL) {
+		wpa_printf(MSG_DEBUG, "Station " MACSTR
+			   " not found for BSS TM Request message",
+			   MAC2STR(addr));
+		return -1;
+	}
+
+	pos = os_strstr(cmd, " disassoc_timer=");
+	if (pos) {
+		pos += 16;
+		disassoc_timer = atoi(pos);
+		if (disassoc_timer < 0 || disassoc_timer > 65535) {
+			wpa_printf(MSG_DEBUG, "Invalid disassoc_timer");
+			return -1;
+		}
+	}
+
+	pos = os_strstr(cmd, " valid_int=");
+	if (pos) {
+		pos += 11;
+		valid_int = atoi(pos);
+	}
+
+	pos = os_strstr(cmd, " dialog_token=");
+	if (pos) {
+		pos += 14;
+		dialog_token = atoi(pos);
+	}
+
+	pos = os_strstr(cmd, " bss_term=");
+	if (pos) {
+		pos += 10;
+		req_mode |= WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED;
+		/* TODO: TSF configurable/learnable */
+		bss_term_dur[0] = 4; /* Subelement ID */
+		bss_term_dur[1] = 10; /* Length */
+		os_memset(&bss_term_dur[2], 0, 8);
+		end = os_strchr(pos, ',');
+		if (end == NULL) {
+			wpa_printf(MSG_DEBUG, "Invalid bss_term data");
+			return -1;
+		}
+		end++;
+		WPA_PUT_LE16(&bss_term_dur[10], atoi(end));
+	}
+
+	nei_len = ieee802_11_parse_candidate_list(cmd, nei_rep,
+						  sizeof(nei_rep));
+	if (nei_len < 0)
+		return -1;
+
+	pos = os_strstr(cmd, " url=");
+	if (pos) {
+		size_t len;
+		pos += 5;
+		end = os_strchr(pos, ' ');
+		if (end)
+			len = end - pos;
+		else
+			len = os_strlen(pos);
+		url = os_malloc(len + 1);
+		if (url == NULL)
+			return -1;
+		os_memcpy(url, pos, len);
+		url[len] = '\0';
+		req_mode |= WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT;
+	}
+
+	if (os_strstr(cmd, " pref=1"))
+		req_mode |= WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED;
+	if (os_strstr(cmd, " abridged=1"))
+		req_mode |= WNM_BSS_TM_REQ_ABRIDGED;
+	if (os_strstr(cmd, " disassoc_imminent=1"))
+		req_mode |= WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
+
+#ifdef CONFIG_MBO
+	pos = os_strstr(cmd, "mbo=");
+	if (pos) {
+		unsigned int mbo_reason, cell_pref, reassoc_delay;
+		u8 *mbo_pos = mbo;
+
+		ret = sscanf(pos, "mbo=%u:%u:%u", &mbo_reason,
+			     &reassoc_delay, &cell_pref);
+		if (ret != 3) {
+			wpa_printf(MSG_DEBUG,
+				   "MBO requires three arguments: mbo=<reason>:<reassoc_delay>:<cell_pref>");
+			ret = -1;
+			goto fail;
+		}
+
+		if (mbo_reason > MBO_TRANSITION_REASON_PREMIUM_AP) {
+			wpa_printf(MSG_DEBUG,
+				   "Invalid MBO transition reason code %u",
+				   mbo_reason);
+			ret = -1;
+			goto fail;
+		}
+
+		/* Valid values for Cellular preference are: 0, 1, 255 */
+		if (cell_pref != 0 && cell_pref != 1 && cell_pref != 255) {
+			wpa_printf(MSG_DEBUG,
+				   "Invalid MBO cellular capability %u",
+				   cell_pref);
+			ret = -1;
+			goto fail;
+		}
+
+		if (reassoc_delay > 65535 ||
+		    (reassoc_delay &&
+		     !(req_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT))) {
+			wpa_printf(MSG_DEBUG,
+				   "MBO: Assoc retry delay is only valid in disassoc imminent mode");
+			ret = -1;
+			goto fail;
+		}
+
+		*mbo_pos++ = MBO_ATTR_ID_TRANSITION_REASON;
+		*mbo_pos++ = 1;
+		*mbo_pos++ = mbo_reason;
+		*mbo_pos++ = MBO_ATTR_ID_CELL_DATA_PREF;
+		*mbo_pos++ = 1;
+		*mbo_pos++ = cell_pref;
+
+		if (reassoc_delay) {
+			*mbo_pos++ = MBO_ATTR_ID_ASSOC_RETRY_DELAY;
+			*mbo_pos++ = 2;
+			WPA_PUT_LE16(mbo_pos, reassoc_delay);
+			mbo_pos += 2;
+		}
+
+		mbo_len = mbo_pos - mbo;
+	}
+#endif /* CONFIG_MBO */
+
+	ret = wnm_send_bss_tm_req(hapd, sta, req_mode, disassoc_timer,
+				  valid_int, bss_term_dur, dialog_token, url,
+				  nei_len ? nei_rep : NULL, nei_len,
+				  mbo_len ? mbo : NULL, mbo_len);
+#ifdef CONFIG_MBO
+fail:
+#endif /* CONFIG_MBO */
+	os_free(url);
+	return ret;
+}
+
+#endif /* CONFIG_WNM_AP */
+
+
+int hostapd_ctrl_iface_acl_del_mac(struct mac_acl_entry **acl, int *num,
+				   const char *txtaddr)
+{
+	u8 addr[ETH_ALEN];
+	struct vlan_description vlan_id;
+
+	if (!(*num))
+		return 0;
+
+	if (hwaddr_aton(txtaddr, addr))
+		return -1;
+
+	if (hostapd_maclist_found(*acl, *num, addr, &vlan_id))
+		hostapd_remove_acl_mac(acl, num, addr);
+
+	return 0;
+}
+
+
+void hostapd_ctrl_iface_acl_clear_list(struct mac_acl_entry **acl,
+				       int *num)
+{
+	while (*num)
+		hostapd_remove_acl_mac(acl, num, (*acl)[0].addr);
+}
+
+
+int hostapd_ctrl_iface_acl_show_mac(struct mac_acl_entry *acl, int num,
+				    char *buf, size_t buflen)
+{
+	int i = 0, len = 0, ret = 0;
+
+	if (!acl)
+		return 0;
+
+	while (i < num) {
+		ret = os_snprintf(buf + len, buflen - len,
+				  MACSTR " VLAN_ID=%d\n",
+				  MAC2STR(acl[i].addr),
+				  acl[i].vlan_id.untagged);
+		if (ret < 0 || (size_t) ret >= buflen - len)
+			return len;
+		i++;
+		len += ret;
+	}
+	return len;
+}
+
+
+int hostapd_ctrl_iface_acl_add_mac(struct mac_acl_entry **acl, int *num,
+				   const char *cmd)
+{
+	u8 addr[ETH_ALEN];
+	struct vlan_description vlan_id;
+	int ret = 0, vlanid = 0;
+	const char *pos;
+
+	if (hwaddr_aton(cmd, addr))
+		return -1;
+
+	pos = os_strstr(cmd, "VLAN_ID=");
+	if (pos)
+		vlanid = atoi(pos + 8);
+
+	if (!hostapd_maclist_found(*acl, *num, addr, &vlan_id)) {
+		ret = hostapd_add_acl_maclist(acl, num, vlanid, addr);
+		if (ret != -1 && *acl)
+			qsort(*acl, *num, sizeof(**acl), hostapd_acl_comp);
+	}
+
+	return ret < 0 ? -1 : 0;
+}
+
+
+int hostapd_disassoc_accept_mac(struct hostapd_data *hapd)
+{
+	struct sta_info *sta;
+	struct vlan_description vlan_id;
+
+	if (hapd->conf->macaddr_acl != DENY_UNLESS_ACCEPTED)
+		return 0;
+
+	for (sta = hapd->sta_list; sta; sta = sta->next) {
+		if (!hostapd_maclist_found(hapd->conf->accept_mac,
+					   hapd->conf->num_accept_mac,
+					   sta->addr, &vlan_id) ||
+		    (vlan_id.notempty &&
+		     vlan_compare(&vlan_id, sta->vlan_desc)))
+			ap_sta_disconnect(hapd, sta, sta->addr,
+					  WLAN_REASON_UNSPECIFIED);
+	}
+
+	return 0;
+}
+
+
+int hostapd_disassoc_deny_mac(struct hostapd_data *hapd)
+{
+	struct sta_info *sta;
+	struct vlan_description vlan_id;
+
+	for (sta = hapd->sta_list; sta; sta = sta->next) {
+		if (hostapd_maclist_found(hapd->conf->deny_mac,
+					  hapd->conf->num_deny_mac, sta->addr,
+					  &vlan_id) &&
+		    (!vlan_id.notempty ||
+		     !vlan_compare(&vlan_id, sta->vlan_desc)))
+			ap_sta_disconnect(hapd, sta, sta->addr,
+					  WLAN_REASON_UNSPECIFIED);
+	}
+
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ctrl_iface_ap.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ctrl_iface_ap.h
new file mode 100644
index 0000000..614f042
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ctrl_iface_ap.h
@@ -0,0 +1,57 @@
+/*
+ * Control interface for shared AP commands
+ * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef CTRL_IFACE_AP_H
+#define CTRL_IFACE_AP_H
+
+int hostapd_ctrl_iface_sta_first(struct hostapd_data *hapd,
+				 char *buf, size_t buflen);
+int hostapd_ctrl_iface_sta(struct hostapd_data *hapd, const char *txtaddr,
+			   char *buf, size_t buflen);
+int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr,
+				char *buf, size_t buflen);
+int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd,
+				      const char *txtaddr);
+int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd,
+				    const char *txtaddr);
+int hostapd_ctrl_iface_signature(struct hostapd_data *hapd,
+				 const char *txtaddr,
+				 char *buf, size_t buflen);
+int hostapd_ctrl_iface_poll_sta(struct hostapd_data *hapd,
+				const char *txtaddr);
+int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
+			      size_t buflen);
+int hostapd_parse_csa_settings(const char *pos,
+			       struct csa_settings *settings);
+int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd);
+int hostapd_ctrl_iface_pmksa_list(struct hostapd_data *hapd, char *buf,
+				  size_t len);
+void hostapd_ctrl_iface_pmksa_flush(struct hostapd_data *hapd);
+int hostapd_ctrl_iface_pmksa_add(struct hostapd_data *hapd, char *cmd);
+int hostapd_ctrl_iface_pmksa_list_mesh(struct hostapd_data *hapd,
+				       const u8 *addr, char *buf, size_t len);
+void * hostapd_ctrl_iface_pmksa_create_entry(const u8 *aa, char *cmd);
+
+int hostapd_ctrl_iface_disassoc_imminent(struct hostapd_data *hapd,
+					 const char *cmd);
+int hostapd_ctrl_iface_ess_disassoc(struct hostapd_data *hapd,
+				    const char *cmd);
+int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd,
+				  const char *cmd);
+int hostapd_ctrl_iface_acl_add_mac(struct mac_acl_entry **acl, int *num,
+				   const char *cmd);
+int hostapd_ctrl_iface_acl_del_mac(struct mac_acl_entry **acl, int *num,
+				   const char *txtaddr);
+void hostapd_ctrl_iface_acl_clear_list(struct mac_acl_entry **acl,
+				       int *num);
+int hostapd_ctrl_iface_acl_show_mac(struct mac_acl_entry *acl, int num,
+				    char *buf, size_t buflen);
+int hostapd_disassoc_accept_mac(struct hostapd_data *hapd);
+int hostapd_disassoc_deny_mac(struct hostapd_data *hapd);
+
+#endif /* CTRL_IFACE_AP_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/dfs.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/dfs.c
new file mode 100644
index 0000000..d1786ee
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/dfs.c
@@ -0,0 +1,1669 @@
+/*
+ * DFS - Dynamic Frequency Selection
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2013-2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/hw_features_common.h"
+#include "common/wpa_ctrl.h"
+#include "hostapd.h"
+#include "ap_drv_ops.h"
+#include "drivers/driver.h"
+#include "dfs.h"
+#include "crypto/crypto.h"
+
+
+enum dfs_channel_type {
+	DFS_ANY_CHANNEL,
+	DFS_AVAILABLE, /* non-radar or radar-available */
+	DFS_NO_CAC_YET, /* radar-not-yet-available */
+};
+
+static struct hostapd_channel_data *
+dfs_downgrade_bandwidth(struct hostapd_iface *iface, int *secondary_channel,
+			u8 *oper_centr_freq_seg0_idx,
+			u8 *oper_centr_freq_seg1_idx,
+			enum dfs_channel_type *channel_type);
+
+
+static bool dfs_use_radar_background(struct hostapd_iface *iface)
+{
+	return (iface->drv_flags2 & WPA_DRIVER_RADAR_BACKGROUND) &&
+		iface->conf->enable_background_radar;
+}
+
+
+static int dfs_get_used_n_chans(struct hostapd_iface *iface, int *seg1)
+{
+	int n_chans = 1;
+
+	*seg1 = 0;
+
+	if (iface->conf->ieee80211n && iface->conf->secondary_channel)
+		n_chans = 2;
+
+	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
+		switch (hostapd_get_oper_chwidth(iface->conf)) {
+		case CONF_OPER_CHWIDTH_USE_HT:
+			break;
+		case CONF_OPER_CHWIDTH_80MHZ:
+			n_chans = 4;
+			break;
+		case CONF_OPER_CHWIDTH_160MHZ:
+			n_chans = 8;
+			break;
+		case CONF_OPER_CHWIDTH_80P80MHZ:
+			n_chans = 4;
+			*seg1 = 4;
+			break;
+		default:
+			break;
+		}
+	}
+
+	return n_chans;
+}
+
+
+/* dfs_channel_available: select new channel according to type parameter */
+static int dfs_channel_available(struct hostapd_channel_data *chan,
+				 enum dfs_channel_type type)
+{
+	if (type == DFS_NO_CAC_YET) {
+		/* Select only radar channel where CAC has not been
+		 * performed yet
+		 */
+		if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
+		    (chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
+		     HOSTAPD_CHAN_DFS_USABLE)
+			return 1;
+		return 0;
+	}
+
+	/*
+	 * When radar detection happens, CSA is performed. However, there's no
+	 * time for CAC, so radar channels must be skipped when finding a new
+	 * channel for CSA, unless they are available for immediate use.
+	 */
+	if (type == DFS_AVAILABLE && (chan->flag & HOSTAPD_CHAN_RADAR) &&
+	    ((chan->flag & HOSTAPD_CHAN_DFS_MASK) !=
+	     HOSTAPD_CHAN_DFS_AVAILABLE))
+		return 0;
+
+	if (chan->flag & HOSTAPD_CHAN_DISABLED)
+		return 0;
+	if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
+	    ((chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
+	     HOSTAPD_CHAN_DFS_UNAVAILABLE))
+		return 0;
+	return 1;
+}
+
+
+static int dfs_is_chan_allowed(struct hostapd_channel_data *chan, int n_chans)
+{
+	/*
+	 * The tables contain first valid channel number based on channel width.
+	 * We will also choose this first channel as the control one.
+	 */
+	int allowed_40[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
+			     165, 173, 184, 192 };
+	/*
+	 * VHT80, valid channels based on center frequency:
+	 * 42, 58, 106, 122, 138, 155, 171
+	 */
+	int allowed_80[] = { 36, 52, 100, 116, 132, 149, 165 };
+	/*
+	 * VHT160 valid channels based on center frequency:
+	 * 50, 114, 163
+	 */
+	int allowed_160[] = { 36, 100, 149 };
+	int *allowed = allowed_40;
+	unsigned int i, allowed_no = 0;
+
+	switch (n_chans) {
+	case 2:
+		allowed = allowed_40;
+		allowed_no = ARRAY_SIZE(allowed_40);
+		break;
+	case 4:
+		allowed = allowed_80;
+		allowed_no = ARRAY_SIZE(allowed_80);
+		break;
+	case 8:
+		allowed = allowed_160;
+		allowed_no = ARRAY_SIZE(allowed_160);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "Unknown width for %d channels", n_chans);
+		break;
+	}
+
+	for (i = 0; i < allowed_no; i++) {
+		if (chan->chan == allowed[i])
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static struct hostapd_channel_data *
+dfs_get_chan_data(struct hostapd_hw_modes *mode, int freq, int first_chan_idx)
+{
+	int i;
+
+	for (i = first_chan_idx; i < mode->num_channels; i++) {
+		if (mode->channels[i].freq == freq)
+			return &mode->channels[i];
+	}
+
+	return NULL;
+}
+
+
+static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
+				    int first_chan_idx, int num_chans,
+				    enum dfs_channel_type type)
+{
+	struct hostapd_channel_data *first_chan, *chan;
+	int i;
+	u32 bw = num_chan_to_bw(num_chans);
+
+	if (first_chan_idx + num_chans > mode->num_channels) {
+		wpa_printf(MSG_DEBUG,
+			   "DFS: some channels in range not defined");
+		return 0;
+	}
+
+	first_chan = &mode->channels[first_chan_idx];
+
+	/* hostapd DFS implementation assumes the first channel as primary.
+	 * If it's not allowed to use the first channel as primary, decline the
+	 * whole channel range. */
+	if (!chan_pri_allowed(first_chan)) {
+		wpa_printf(MSG_DEBUG, "DFS: primary chanenl not allowed");
+		return 0;
+	}
+
+	for (i = 0; i < num_chans; i++) {
+		chan = dfs_get_chan_data(mode, first_chan->freq + i * 20,
+					 first_chan_idx);
+		if (!chan) {
+			wpa_printf(MSG_DEBUG, "DFS: no channel data for %d",
+				   first_chan->freq + i * 20);
+			return 0;
+		}
+
+		/* HT 40 MHz secondary channel availability checked only for
+		 * primary channel */
+		if (!chan_bw_allowed(chan, bw, 1, !i)) {
+			wpa_printf(MSG_DEBUG, "DFS: bw now allowed for %d",
+				   first_chan->freq + i * 20);
+			return 0;
+		}
+
+		if (!dfs_channel_available(chan, type)) {
+			wpa_printf(MSG_DEBUG, "DFS: channel not available %d",
+				   first_chan->freq + i * 20);
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
+
+static int is_in_chanlist(struct hostapd_iface *iface,
+			  struct hostapd_channel_data *chan)
+{
+	if (!iface->conf->acs_ch_list.num)
+		return 1;
+
+	return freq_range_list_includes(&iface->conf->acs_ch_list, chan->chan);
+}
+
+
+/*
+ * The function assumes HT40+ operation.
+ * Make sure to adjust the following variables after calling this:
+ *  - hapd->secondary_channel
+ *  - hapd->vht/he_oper_centr_freq_seg0_idx
+ *  - hapd->vht/he_oper_centr_freq_seg1_idx
+ */
+static int dfs_find_channel(struct hostapd_iface *iface,
+			    struct hostapd_channel_data **ret_chan,
+			    int idx, enum dfs_channel_type type)
+{
+	struct hostapd_hw_modes *mode;
+	struct hostapd_channel_data *chan;
+	int i, channel_idx = 0, n_chans, n_chans1;
+
+	mode = iface->current_mode;
+	n_chans = dfs_get_used_n_chans(iface, &n_chans1);
+
+	wpa_printf(MSG_DEBUG, "DFS new chan checking %d channels", n_chans);
+	for (i = 0; i < mode->num_channels; i++) {
+		chan = &mode->channels[i];
+
+		/* Skip HT40/VHT incompatible channels */
+		if (iface->conf->ieee80211n &&
+		    iface->conf->secondary_channel &&
+		    (!dfs_is_chan_allowed(chan, n_chans) ||
+		     !(chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P))) {
+			wpa_printf(MSG_DEBUG,
+				   "DFS: channel %d (%d) is incompatible",
+				   chan->freq, chan->chan);
+			continue;
+		}
+
+		/* Skip incompatible chandefs */
+		if (!dfs_chan_range_available(mode, i, n_chans, type)) {
+			wpa_printf(MSG_DEBUG,
+				   "DFS: range not available for %d (%d)",
+				   chan->freq, chan->chan);
+			continue;
+		}
+
+		if (!is_in_chanlist(iface, chan)) {
+			wpa_printf(MSG_DEBUG,
+				   "DFS: channel %d (%d) not in chanlist",
+				   chan->freq, chan->chan);
+			continue;
+		}
+
+		if (chan->max_tx_power < iface->conf->min_tx_power)
+			continue;
+
+		if ((chan->flag & HOSTAPD_CHAN_INDOOR_ONLY) &&
+		    iface->conf->country[2] == 0x4f)
+			continue;
+
+		if (ret_chan && idx == channel_idx) {
+			wpa_printf(MSG_DEBUG, "Selected channel %d (%d)",
+				   chan->freq, chan->chan);
+			*ret_chan = chan;
+			return idx;
+		}
+		wpa_printf(MSG_DEBUG, "Adding channel %d (%d)",
+			   chan->freq, chan->chan);
+		channel_idx++;
+	}
+	return channel_idx;
+}
+
+
+static void dfs_adjust_center_freq(struct hostapd_iface *iface,
+				   struct hostapd_channel_data *chan,
+				   int secondary_channel,
+				   int sec_chan_idx_80p80,
+				   u8 *oper_centr_freq_seg0_idx,
+				   u8 *oper_centr_freq_seg1_idx)
+{
+	if (!iface->conf->ieee80211ac && !iface->conf->ieee80211ax)
+		return;
+
+	if (!chan)
+		return;
+
+	*oper_centr_freq_seg1_idx = 0;
+
+	switch (hostapd_get_oper_chwidth(iface->conf)) {
+	case CONF_OPER_CHWIDTH_USE_HT:
+		if (secondary_channel == 1)
+			*oper_centr_freq_seg0_idx = chan->chan + 2;
+		else if (secondary_channel == -1)
+			*oper_centr_freq_seg0_idx = chan->chan - 2;
+		else
+			*oper_centr_freq_seg0_idx = chan->chan;
+		break;
+	case CONF_OPER_CHWIDTH_80MHZ:
+		*oper_centr_freq_seg0_idx = chan->chan + 6;
+		break;
+	case CONF_OPER_CHWIDTH_160MHZ:
+		*oper_centr_freq_seg0_idx = chan->chan + 14;
+		break;
+	case CONF_OPER_CHWIDTH_80P80MHZ:
+		*oper_centr_freq_seg0_idx = chan->chan + 6;
+		*oper_centr_freq_seg1_idx = sec_chan_idx_80p80 + 6;
+		break;
+
+	default:
+		wpa_printf(MSG_INFO,
+			   "DFS: Unsupported channel width configuration");
+		*oper_centr_freq_seg0_idx = 0;
+		break;
+	}
+
+	wpa_printf(MSG_DEBUG, "DFS adjusting VHT center frequency: %d, %d",
+		   *oper_centr_freq_seg0_idx,
+		   *oper_centr_freq_seg1_idx);
+}
+
+
+/* Return start channel idx we will use for mode->channels[idx] */
+static int dfs_get_start_chan_idx(struct hostapd_iface *iface, int *seg1_start)
+{
+	struct hostapd_hw_modes *mode;
+	struct hostapd_channel_data *chan;
+	int channel_no = iface->conf->channel;
+	int res = -1, i;
+	int chan_seg1 = -1;
+
+	*seg1_start = -1;
+
+	/* HT40- */
+	if (iface->conf->ieee80211n && iface->conf->secondary_channel == -1)
+		channel_no -= 4;
+
+	/* VHT/HE */
+	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
+		switch (hostapd_get_oper_chwidth(iface->conf)) {
+		case CONF_OPER_CHWIDTH_USE_HT:
+			break;
+		case CONF_OPER_CHWIDTH_80MHZ:
+			channel_no = hostapd_get_oper_centr_freq_seg0_idx(
+				iface->conf) - 6;
+			break;
+		case CONF_OPER_CHWIDTH_160MHZ:
+			channel_no = hostapd_get_oper_centr_freq_seg0_idx(
+				iface->conf) - 14;
+			break;
+		case CONF_OPER_CHWIDTH_80P80MHZ:
+			channel_no = hostapd_get_oper_centr_freq_seg0_idx(
+				iface->conf) - 6;
+			chan_seg1 = hostapd_get_oper_centr_freq_seg1_idx(
+				iface->conf) - 6;
+			break;
+		default:
+			wpa_printf(MSG_INFO,
+				   "DFS only VHT20/40/80/160/80+80 is supported now");
+			channel_no = -1;
+			break;
+		}
+	}
+
+	/* Get idx */
+	mode = iface->current_mode;
+	for (i = 0; i < mode->num_channels; i++) {
+		chan = &mode->channels[i];
+		if (chan->chan == channel_no) {
+			res = i;
+			break;
+		}
+	}
+
+	if (res != -1 && chan_seg1 > -1) {
+		int found = 0;
+
+		/* Get idx for seg1 */
+		mode = iface->current_mode;
+		for (i = 0; i < mode->num_channels; i++) {
+			chan = &mode->channels[i];
+			if (chan->chan == chan_seg1) {
+				*seg1_start = i;
+				found = 1;
+				break;
+			}
+		}
+		if (!found)
+			res = -1;
+	}
+
+	if (res == -1) {
+		wpa_printf(MSG_DEBUG,
+			   "DFS chan_idx seems wrong; num-ch: %d ch-no: %d conf-ch-no: %d 11n: %d sec-ch: %d vht-oper-width: %d",
+			   mode->num_channels, channel_no, iface->conf->channel,
+			   iface->conf->ieee80211n,
+			   iface->conf->secondary_channel,
+			   hostapd_get_oper_chwidth(iface->conf));
+
+		for (i = 0; i < mode->num_channels; i++) {
+			wpa_printf(MSG_DEBUG, "Available channel: %d",
+				   mode->channels[i].chan);
+		}
+	}
+
+	return res;
+}
+
+
+/* At least one channel have radar flag */
+static int dfs_check_chans_radar(struct hostapd_iface *iface,
+				 int start_chan_idx, int n_chans)
+{
+	struct hostapd_channel_data *channel;
+	struct hostapd_hw_modes *mode;
+	int i, res = 0;
+
+	mode = iface->current_mode;
+
+	for (i = 0; i < n_chans; i++) {
+		if (start_chan_idx + i >= mode->num_channels)
+			break;
+		channel = &mode->channels[start_chan_idx + i];
+		if (channel->flag & HOSTAPD_CHAN_RADAR)
+			res++;
+	}
+
+	return res;
+}
+
+
+/* All channels available */
+static int dfs_check_chans_available(struct hostapd_iface *iface,
+				     int start_chan_idx, int n_chans)
+{
+	struct hostapd_channel_data *channel;
+	struct hostapd_hw_modes *mode;
+	int i;
+
+	mode = iface->current_mode;
+
+	for (i = 0; i < n_chans; i++) {
+		channel = &mode->channels[start_chan_idx + i];
+
+		if (channel->flag & HOSTAPD_CHAN_DISABLED)
+			break;
+
+		if (!(channel->flag & HOSTAPD_CHAN_RADAR))
+			continue;
+
+		if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) !=
+		    HOSTAPD_CHAN_DFS_AVAILABLE)
+			break;
+	}
+
+	return i == n_chans;
+}
+
+
+/* At least one channel unavailable */
+static int dfs_check_chans_unavailable(struct hostapd_iface *iface,
+				       int start_chan_idx,
+				       int n_chans)
+{
+	struct hostapd_channel_data *channel;
+	struct hostapd_hw_modes *mode;
+	int i, res = 0;
+
+	mode = iface->current_mode;
+
+	for (i = 0; i < n_chans; i++) {
+		channel = &mode->channels[start_chan_idx + i];
+		if (channel->flag & HOSTAPD_CHAN_DISABLED)
+			res++;
+		if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) ==
+		    HOSTAPD_CHAN_DFS_UNAVAILABLE)
+			res++;
+	}
+
+	return res;
+}
+
+
+static struct hostapd_channel_data *
+dfs_get_valid_channel(struct hostapd_iface *iface,
+		      int *secondary_channel,
+		      u8 *oper_centr_freq_seg0_idx,
+		      u8 *oper_centr_freq_seg1_idx,
+		      enum dfs_channel_type type)
+{
+	struct hostapd_hw_modes *mode;
+	struct hostapd_channel_data *chan = NULL;
+	struct hostapd_channel_data *chan2 = NULL;
+	int num_available_chandefs;
+	int chan_idx, chan_idx2;
+	int sec_chan_idx_80p80 = -1;
+	bool is_mesh = false;
+	int i;
+	u32 _rand;
+
+#ifdef CONFIG_MESH
+	is_mesh = iface->mconf;
+#endif
+
+	wpa_printf(MSG_DEBUG, "DFS: Selecting random channel");
+	*secondary_channel = 0;
+	*oper_centr_freq_seg0_idx = 0;
+	*oper_centr_freq_seg1_idx = 0;
+
+	if (iface->current_mode == NULL)
+		return NULL;
+
+	mode = iface->current_mode;
+	if (mode->mode != HOSTAPD_MODE_IEEE80211A)
+		return NULL;
+
+	/* Get the count first */
+	num_available_chandefs = dfs_find_channel(iface, NULL, 0, type);
+	wpa_printf(MSG_DEBUG, "DFS: num_available_chandefs=%d",
+		   num_available_chandefs);
+	if (num_available_chandefs == 0)
+		return NULL;
+
+	/* try to use deterministic channel in mesh, so that both sides
+	 * have a chance to switch to the same channel */
+	if (is_mesh) {
+#ifdef CONFIG_MESH
+		u64 hash[4];
+		const u8 *meshid[1] = { &iface->mconf->meshid[0] };
+		const size_t meshid_len = iface->mconf->meshid_len;
+
+		sha256_vector(1, meshid, &meshid_len, (u8 *)&hash[0]);
+		_rand = hash[0] + hash[1] + hash[2] + hash[3];
+#endif
+	} else if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0)
+		return NULL;
+
+	chan_idx = _rand % num_available_chandefs;
+	dfs_find_channel(iface, &chan, chan_idx, type);
+	if (!chan) {
+		wpa_printf(MSG_DEBUG, "DFS: no random channel found");
+		return NULL;
+	}
+	wpa_printf(MSG_DEBUG, "DFS: got random channel %d (%d)",
+		   chan->freq, chan->chan);
+
+	/* dfs_find_channel() calculations assume HT40+ */
+	if (iface->conf->secondary_channel)
+		*secondary_channel = 1;
+	else
+		*secondary_channel = 0;
+
+	/* Get secondary channel for HT80P80 */
+	if (hostapd_get_oper_chwidth(iface->conf) ==
+	    CONF_OPER_CHWIDTH_80P80MHZ) {
+		if (num_available_chandefs <= 1) {
+			wpa_printf(MSG_ERROR,
+				   "only 1 valid chan, can't support 80+80");
+			return NULL;
+		}
+
+		/*
+		 * Loop all channels except channel1 to find a valid channel2
+		 * that is not adjacent to channel1.
+		 */
+		for (i = 0; i < num_available_chandefs - 1; i++) {
+			/* start from chan_idx + 1, end when chan_idx - 1 */
+			chan_idx2 = (chan_idx + 1 + i) % num_available_chandefs;
+			dfs_find_channel(iface, &chan2, chan_idx2, type);
+			if (chan2 && abs(chan2->chan - chan->chan) > 12) {
+				/* two channels are not adjacent */
+				sec_chan_idx_80p80 = chan2->chan;
+				wpa_printf(MSG_DEBUG,
+					   "DFS: got second chan: %d (%d)",
+					   chan2->freq, chan2->chan);
+				break;
+			}
+		}
+
+		/* Check if we got a valid secondary channel which is not
+		 * adjacent to the first channel.
+		 */
+		if (sec_chan_idx_80p80 == -1) {
+			wpa_printf(MSG_INFO,
+				   "DFS: failed to get chan2 for 80+80");
+			return NULL;
+		}
+	}
+
+	dfs_adjust_center_freq(iface, chan,
+			       *secondary_channel,
+			       sec_chan_idx_80p80,
+			       oper_centr_freq_seg0_idx,
+			       oper_centr_freq_seg1_idx);
+
+	return chan;
+}
+
+
+static int dfs_set_valid_channel(struct hostapd_iface *iface, int skip_radar)
+{
+	struct hostapd_channel_data *channel;
+	u8 cf1 = 0, cf2 = 0;
+	int sec = 0;
+
+	channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2,
+					skip_radar ? DFS_AVAILABLE :
+					DFS_ANY_CHANNEL);
+	if (!channel) {
+		wpa_printf(MSG_ERROR, "could not get valid channel");
+		return -1;
+	}
+
+	iface->freq = channel->freq;
+	iface->conf->channel = channel->chan;
+	iface->conf->secondary_channel = sec;
+	hostapd_set_oper_centr_freq_seg0_idx(iface->conf, cf1);
+	hostapd_set_oper_centr_freq_seg1_idx(iface->conf, cf2);
+
+	return 0;
+}
+
+
+static int set_dfs_state_freq(struct hostapd_iface *iface, int freq, u32 state)
+{
+	struct hostapd_hw_modes *mode;
+	struct hostapd_channel_data *chan = NULL;
+	int i;
+
+	mode = iface->current_mode;
+	if (mode == NULL)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "set_dfs_state 0x%X for %d MHz", state, freq);
+	for (i = 0; i < iface->current_mode->num_channels; i++) {
+		chan = &iface->current_mode->channels[i];
+		if (chan->freq == freq) {
+			if (chan->flag & HOSTAPD_CHAN_RADAR) {
+				chan->flag &= ~HOSTAPD_CHAN_DFS_MASK;
+				chan->flag |= state;
+				return 1; /* Channel found */
+			}
+		}
+	}
+	wpa_printf(MSG_WARNING, "Can't set DFS state for freq %d MHz", freq);
+	return 0;
+}
+
+
+static int set_dfs_state(struct hostapd_iface *iface, int freq, int ht_enabled,
+			 int chan_offset, int chan_width, int cf1,
+			 int cf2, u32 state)
+{
+	int n_chans = 1, i;
+	struct hostapd_hw_modes *mode;
+	int frequency = freq;
+	int frequency2 = 0;
+	int ret = 0;
+
+	mode = iface->current_mode;
+	if (mode == NULL)
+		return 0;
+
+	if (mode->mode != HOSTAPD_MODE_IEEE80211A) {
+		wpa_printf(MSG_WARNING, "current_mode != IEEE80211A");
+		return 0;
+	}
+
+	/* Seems cf1 and chan_width is enough here */
+	switch (chan_width) {
+	case CHAN_WIDTH_20_NOHT:
+	case CHAN_WIDTH_20:
+		n_chans = 1;
+		if (frequency == 0)
+			frequency = cf1;
+		break;
+	case CHAN_WIDTH_40:
+		n_chans = 2;
+		frequency = cf1 - 10;
+		break;
+	case CHAN_WIDTH_80:
+		n_chans = 4;
+		frequency = cf1 - 30;
+		break;
+	case CHAN_WIDTH_80P80:
+		n_chans = 4;
+		frequency = cf1 - 30;
+		frequency2 = cf2 - 30;
+		break;
+	case CHAN_WIDTH_160:
+		n_chans = 8;
+		frequency = cf1 - 70;
+		break;
+	default:
+		wpa_printf(MSG_INFO, "DFS chan_width %d not supported",
+			   chan_width);
+		break;
+	}
+
+	wpa_printf(MSG_DEBUG, "DFS freq: %dMHz, n_chans: %d", frequency,
+		   n_chans);
+	for (i = 0; i < n_chans; i++) {
+		ret += set_dfs_state_freq(iface, frequency, state);
+		frequency = frequency + 20;
+
+		if (chan_width == CHAN_WIDTH_80P80) {
+			ret += set_dfs_state_freq(iface, frequency2, state);
+			frequency2 = frequency2 + 20;
+		}
+	}
+
+	return ret;
+}
+
+
+static int dfs_are_channels_overlapped(struct hostapd_iface *iface, int freq,
+				       int chan_width, int cf1, int cf2)
+{
+	int start_chan_idx, start_chan_idx1;
+	struct hostapd_hw_modes *mode;
+	struct hostapd_channel_data *chan;
+	int n_chans, n_chans1, i, j, frequency = freq, radar_n_chans = 1;
+	u8 radar_chan;
+	int res = 0;
+
+	/* Our configuration */
+	mode = iface->current_mode;
+	start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1);
+	n_chans = dfs_get_used_n_chans(iface, &n_chans1);
+
+	/* Check we are on DFS channel(s) */
+	if (!dfs_check_chans_radar(iface, start_chan_idx, n_chans))
+		return 0;
+
+	/* Reported via radar event */
+	switch (chan_width) {
+	case CHAN_WIDTH_20_NOHT:
+	case CHAN_WIDTH_20:
+		radar_n_chans = 1;
+		if (frequency == 0)
+			frequency = cf1;
+		break;
+	case CHAN_WIDTH_40:
+		radar_n_chans = 2;
+		frequency = cf1 - 10;
+		break;
+	case CHAN_WIDTH_80:
+		radar_n_chans = 4;
+		frequency = cf1 - 30;
+		break;
+	case CHAN_WIDTH_160:
+		radar_n_chans = 8;
+		frequency = cf1 - 70;
+		break;
+	default:
+		wpa_printf(MSG_INFO, "DFS chan_width %d not supported",
+			   chan_width);
+		break;
+	}
+
+	ieee80211_freq_to_chan(frequency, &radar_chan);
+
+	for (i = 0; i < n_chans; i++) {
+		chan = &mode->channels[start_chan_idx + i];
+		if (!(chan->flag & HOSTAPD_CHAN_RADAR))
+			continue;
+		for (j = 0; j < radar_n_chans; j++) {
+			wpa_printf(MSG_DEBUG, "checking our: %d, radar: %d",
+				   chan->chan, radar_chan + j * 4);
+			if (chan->chan == radar_chan + j * 4)
+				res++;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "overlapped: %d", res);
+
+	return res;
+}
+
+
+static unsigned int dfs_get_cac_time(struct hostapd_iface *iface,
+				     int start_chan_idx, int n_chans)
+{
+	struct hostapd_channel_data *channel;
+	struct hostapd_hw_modes *mode;
+	int i;
+	unsigned int cac_time_ms = 0;
+#ifdef CONFIG_DFS_CAC_TIMER
+	/* Yewei, add dfs cac timer in seconds,, 20240118 */
+	if (iface->conf->dfs_cac_s) {
+		cac_time_ms = iface->conf->dfs_cac_s * 1000;
+		return cac_time_ms;
+	}
+#endif /* CONFIG_DFS_CAC_TIMER */
+	mode = iface->current_mode;
+
+	for (i = 0; i < n_chans; i++) {
+		if (start_chan_idx + i >= mode->num_channels)
+			break;
+		channel = &mode->channels[start_chan_idx + i];
+		if (!(channel->flag & HOSTAPD_CHAN_RADAR))
+			continue;
+		if (channel->dfs_cac_ms > cac_time_ms)
+			cac_time_ms = channel->dfs_cac_ms;
+	}
+
+	return cac_time_ms;
+}
+
+
+/*
+ * Main DFS handler
+ * 1 - continue channel/ap setup
+ * 0 - channel/ap setup will be continued after CAC
+ * -1 - hit critical error
+ */
+int hostapd_handle_dfs(struct hostapd_iface *iface)
+{
+	int res, n_chans, n_chans1, start_chan_idx, start_chan_idx1;
+	int skip_radar = 0;
+
+	if (is_6ghz_freq(iface->freq))
+		return 1;
+
+	if (!iface->current_mode) {
+		/*
+		 * This can happen with drivers that do not provide mode
+		 * information and as such, cannot really use hostapd for DFS.
+		 */
+		wpa_printf(MSG_DEBUG,
+			   "DFS: No current_mode information - assume no need to perform DFS operations by hostapd");
+		return 1;
+	}
+
+	iface->cac_started = 0;
+
+	do {
+		/* Get start (first) channel for current configuration */
+		start_chan_idx = dfs_get_start_chan_idx(iface,
+							&start_chan_idx1);
+		if (start_chan_idx == -1)
+			return -1;
+
+		/* Get number of used channels, depend on width */
+		n_chans = dfs_get_used_n_chans(iface, &n_chans1);
+
+		/* Setup CAC time */
+		iface->dfs_cac_ms = dfs_get_cac_time(iface, start_chan_idx,
+						     n_chans);
+
+		/* Check if any of configured channels require DFS */
+		res = dfs_check_chans_radar(iface, start_chan_idx, n_chans);
+		wpa_printf(MSG_DEBUG,
+			   "DFS %d channels required radar detection",
+			   res);
+		if (!res)
+			return 1;
+
+		/* Check if all channels are DFS available */
+		res = dfs_check_chans_available(iface, start_chan_idx, n_chans);
+		wpa_printf(MSG_DEBUG,
+			   "DFS all channels available, (SKIP CAC): %s",
+			   res ? "yes" : "no");
+		if (res)
+			return 1;
+
+		/* Check if any of configured channels is unavailable */
+		res = dfs_check_chans_unavailable(iface, start_chan_idx,
+						  n_chans);
+		wpa_printf(MSG_DEBUG, "DFS %d chans unavailable - choose other channel: %s",
+			   res, res ? "yes": "no");
+		if (res) {
+			if (dfs_set_valid_channel(iface, skip_radar) < 0) {
+				hostapd_set_state(iface, HAPD_IFACE_DFS);
+				return 0;
+			}
+		}
+	} while (res);
+
+	/* Finally start CAC */
+	hostapd_set_state(iface, HAPD_IFACE_DFS);
+	wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz%s", iface->freq,
+		   dfs_use_radar_background(iface) ? " (background)" : "");
+	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
+		"freq=%d chan=%d sec_chan=%d, width=%d, seg0=%d, seg1=%d, cac_time=%ds",
+		iface->freq,
+		iface->conf->channel, iface->conf->secondary_channel,
+		hostapd_get_oper_chwidth(iface->conf),
+		hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
+		hostapd_get_oper_centr_freq_seg1_idx(iface->conf),
+		iface->dfs_cac_ms / 1000);
+
+	res = hostapd_start_dfs_cac(
+		iface, iface->conf->hw_mode, iface->freq, iface->conf->channel,
+		iface->conf->ieee80211n, iface->conf->ieee80211ac,
+		iface->conf->ieee80211ax, iface->conf->ieee80211be,
+		iface->conf->secondary_channel,
+		hostapd_get_oper_chwidth(iface->conf),
+		hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
+		hostapd_get_oper_centr_freq_seg1_idx(iface->conf),
+		dfs_use_radar_background(iface));
+
+	if (res) {
+		wpa_printf(MSG_ERROR, "DFS start_dfs_cac() failed, %d", res);
+		return -1;
+	}
+
+	if (dfs_use_radar_background(iface)) {
+		/* Cache background radar parameters. */
+		iface->radar_background.channel = iface->conf->channel;
+		iface->radar_background.secondary_channel =
+			iface->conf->secondary_channel;
+		iface->radar_background.freq = iface->freq;
+		iface->radar_background.centr_freq_seg0_idx =
+			hostapd_get_oper_centr_freq_seg0_idx(iface->conf);
+		iface->radar_background.centr_freq_seg1_idx =
+			hostapd_get_oper_centr_freq_seg1_idx(iface->conf);
+
+		/*
+		 * Let's select a random channel according to the
+		 * regulations and perform CAC on dedicated radar chain.
+		 */
+		res = dfs_set_valid_channel(iface, 1);
+		if (res < 0)
+			return res;
+
+		iface->radar_background.temp_ch = 1;
+		return 1;
+	}
+
+	return 0;
+}
+
+
+int hostapd_is_dfs_chan_available(struct hostapd_iface *iface)
+{
+	int n_chans, n_chans1, start_chan_idx, start_chan_idx1;
+
+	/* Get the start (first) channel for current configuration */
+	start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1);
+	if (start_chan_idx < 0)
+		return 0;
+
+	/* Get the number of used channels, depending on width */
+	n_chans = dfs_get_used_n_chans(iface, &n_chans1);
+
+	/* Check if all channels are DFS available */
+	return dfs_check_chans_available(iface, start_chan_idx, n_chans);
+}
+
+
+static int hostapd_dfs_request_channel_switch(struct hostapd_iface *iface,
+					      int channel, int freq,
+					      int secondary_channel,
+					      u8 current_vht_oper_chwidth,
+					      u8 oper_centr_freq_seg0_idx,
+					      u8 oper_centr_freq_seg1_idx)
+{
+	struct hostapd_hw_modes *cmode = iface->current_mode;
+	int ieee80211_mode = IEEE80211_MODE_AP, err;
+	struct csa_settings csa_settings;
+	u8 new_vht_oper_chwidth;
+	unsigned int i;
+
+	wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d", channel);
+	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
+		"freq=%d chan=%d sec_chan=%d", freq, channel,
+		secondary_channel);
+
+	new_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
+	hostapd_set_oper_chwidth(iface->conf, current_vht_oper_chwidth);
+
+	/* Setup CSA request */
+	os_memset(&csa_settings, 0, sizeof(csa_settings));
+	csa_settings.cs_count = 5;
+	csa_settings.block_tx = 1;
+#ifdef CONFIG_MESH
+	if (iface->mconf)
+		ieee80211_mode = IEEE80211_MODE_MESH;
+#endif /* CONFIG_MESH */
+	err = hostapd_set_freq_params(&csa_settings.freq_params,
+				      iface->conf->hw_mode,
+				      freq, channel,
+				      iface->conf->enable_edmg,
+				      iface->conf->edmg_channel,
+				      iface->conf->ieee80211n,
+				      iface->conf->ieee80211ac,
+				      iface->conf->ieee80211ax,
+				      iface->conf->ieee80211be,
+				      secondary_channel,
+				      new_vht_oper_chwidth,
+				      oper_centr_freq_seg0_idx,
+				      oper_centr_freq_seg1_idx,
+				      cmode->vht_capab,
+				      &cmode->he_capab[ieee80211_mode],
+				      &cmode->eht_capab[ieee80211_mode]);
+
+	if (err) {
+		wpa_printf(MSG_ERROR,
+			   "DFS failed to calculate CSA freq params");
+		hostapd_disable_iface(iface);
+		return err;
+	}
+
+	for (i = 0; i < iface->num_bss; i++) {
+		err = hostapd_switch_channel(iface->bss[i], &csa_settings);
+		if (err)
+			break;
+	}
+
+	if (err) {
+		wpa_printf(MSG_WARNING,
+			   "DFS failed to schedule CSA (%d) - trying fallback",
+			   err);
+		iface->freq = freq;
+		iface->conf->channel = channel;
+		iface->conf->secondary_channel = secondary_channel;
+		hostapd_set_oper_chwidth(iface->conf, new_vht_oper_chwidth);
+		hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
+						     oper_centr_freq_seg0_idx);
+		hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
+						     oper_centr_freq_seg1_idx);
+
+		hostapd_disable_iface(iface);
+		hostapd_enable_iface(iface);
+
+		return 0;
+	}
+
+	/* Channel configuration will be updated once CSA completes and
+	 * ch_switch_notify event is received */
+	wpa_printf(MSG_DEBUG, "DFS waiting channel switch event");
+
+	return 0;
+}
+
+
+static void hostpad_dfs_update_background_chain(struct hostapd_iface *iface)
+{
+	int sec = 0;
+	enum dfs_channel_type channel_type = DFS_NO_CAC_YET;
+	struct hostapd_channel_data *channel;
+	u8 oper_centr_freq_seg0_idx = 0;
+	u8 oper_centr_freq_seg1_idx = 0;
+
+	/*
+	 * Allow selection of DFS channel in ETSI to comply with
+	 * uniform spreading.
+	 */
+	if (iface->dfs_domain == HOSTAPD_DFS_REGION_ETSI)
+		channel_type = DFS_ANY_CHANNEL;
+
+	channel = dfs_get_valid_channel(iface, &sec, &oper_centr_freq_seg0_idx,
+					&oper_centr_freq_seg1_idx,
+					channel_type);
+	if (!channel ||
+	    channel->chan == iface->conf->channel ||
+	    channel->chan == iface->radar_background.channel)
+		channel = dfs_downgrade_bandwidth(iface, &sec,
+						  &oper_centr_freq_seg0_idx,
+						  &oper_centr_freq_seg1_idx,
+						  &channel_type);
+	if (!channel ||
+	    hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
+				  channel->freq, channel->chan,
+				  iface->conf->ieee80211n,
+				  iface->conf->ieee80211ac,
+				  iface->conf->ieee80211ax,
+				  iface->conf->ieee80211be,
+				  sec, hostapd_get_oper_chwidth(iface->conf),
+				  oper_centr_freq_seg0_idx,
+				  oper_centr_freq_seg1_idx, true)) {
+		wpa_printf(MSG_ERROR, "DFS failed to start CAC offchannel");
+		iface->radar_background.channel = -1;
+		return;
+	}
+
+	iface->radar_background.channel = channel->chan;
+	iface->radar_background.freq = channel->freq;
+	iface->radar_background.secondary_channel = sec;
+	iface->radar_background.centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
+	iface->radar_background.centr_freq_seg1_idx = oper_centr_freq_seg1_idx;
+
+	wpa_printf(MSG_DEBUG,
+		   "%s: setting background chain to chan %d (%d MHz)",
+		   __func__, channel->chan, channel->freq);
+}
+
+
+static bool
+hostapd_dfs_is_background_event(struct hostapd_iface *iface, int freq)
+{
+	return dfs_use_radar_background(iface) &&
+		iface->radar_background.channel != -1 &&
+		iface->radar_background.freq == freq;
+}
+
+
+static int
+hostapd_dfs_start_channel_switch_background(struct hostapd_iface *iface)
+{
+	u8 current_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
+
+	iface->conf->channel = iface->radar_background.channel;
+	iface->freq = iface->radar_background.freq;
+	iface->conf->secondary_channel =
+		iface->radar_background.secondary_channel;
+	hostapd_set_oper_centr_freq_seg0_idx(
+		iface->conf, iface->radar_background.centr_freq_seg0_idx);
+	hostapd_set_oper_centr_freq_seg1_idx(
+		iface->conf, iface->radar_background.centr_freq_seg1_idx);
+
+	hostpad_dfs_update_background_chain(iface);
+
+	return hostapd_dfs_request_channel_switch(
+		iface, iface->conf->channel, iface->freq,
+		iface->conf->secondary_channel, current_vht_oper_chwidth,
+		hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
+		hostapd_get_oper_centr_freq_seg1_idx(iface->conf));
+}
+
+
+int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
+			     int ht_enabled, int chan_offset, int chan_width,
+			     int cf1, int cf2)
+{
+	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_COMPLETED
+		"success=%d freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
+		success, freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
+
+	if (success) {
+		/* Complete iface/ap configuration */
+		if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) {
+			/* Complete AP configuration for the first bring up. */
+			if (iface->state != HAPD_IFACE_ENABLED)
+				hostapd_setup_interface_complete(iface, 0);
+			else
+				iface->cac_started = 0;
+		} else {
+			set_dfs_state(iface, freq, ht_enabled, chan_offset,
+				      chan_width, cf1, cf2,
+				      HOSTAPD_CHAN_DFS_AVAILABLE);
+
+			/*
+			 * Radar event from background chain for the selected
+			 * channel. Perform CSA, move the main chain to the
+			 * selected channel and configure the background chain
+			 * to a new DFS channel.
+			 */
+			if (hostapd_dfs_is_background_event(iface, freq)) {
+				iface->radar_background.cac_started = 0;
+				if (!iface->radar_background.temp_ch)
+					return 0;
+
+				iface->radar_background.temp_ch = 0;
+				return hostapd_dfs_start_channel_switch_background(iface);
+			}
+
+			/*
+			 * Just mark the channel available when CAC completion
+			 * event is received in enabled state. CAC result could
+			 * have been propagated from another radio having the
+			 * same regulatory configuration. When CAC completion is
+			 * received during non-HAPD_IFACE_ENABLED state, make
+			 * sure the configured channel is available because this
+			 * CAC completion event could have been propagated from
+			 * another radio.
+			 */
+			if (iface->state != HAPD_IFACE_ENABLED &&
+			    hostapd_is_dfs_chan_available(iface)) {
+				hostapd_setup_interface_complete(iface, 0);
+				iface->cac_started = 0;
+			}
+		}
+	} else if (hostapd_dfs_is_background_event(iface, freq)) {
+		iface->radar_background.cac_started = 0;
+		hostpad_dfs_update_background_chain(iface);
+	}
+
+	return 0;
+}
+
+
+int hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq,
+				int ht_enabled, int chan_offset, int chan_width,
+				int cf1, int cf2)
+{
+	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_PRE_CAC_EXPIRED
+		"freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
+		freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
+
+	hostapd_ubus_notify_radar_detected(iface, freq, chan_width, cf1, cf2);
+
+	/* Proceed only if DFS is not offloaded to the driver */
+	if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
+		return 0;
+
+	set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
+		      cf1, cf2, HOSTAPD_CHAN_DFS_USABLE);
+
+	return 0;
+}
+
+
+static struct hostapd_channel_data *
+dfs_downgrade_bandwidth(struct hostapd_iface *iface, int *secondary_channel,
+			u8 *oper_centr_freq_seg0_idx,
+			u8 *oper_centr_freq_seg1_idx,
+			enum dfs_channel_type *channel_type)
+{
+	struct hostapd_channel_data *channel;
+
+	for (;;) {
+		channel = dfs_get_valid_channel(iface, secondary_channel,
+						oper_centr_freq_seg0_idx,
+						oper_centr_freq_seg1_idx,
+						*channel_type);
+		if (channel) {
+			wpa_printf(MSG_DEBUG, "DFS: Selected channel: %d",
+				   channel->chan);
+			return channel;
+		}
+
+		if (*channel_type != DFS_ANY_CHANNEL) {
+			*channel_type = DFS_ANY_CHANNEL;
+		} else {
+			int oper_chwidth;
+
+			oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
+			if (oper_chwidth == CONF_OPER_CHWIDTH_USE_HT)
+				break;
+			*channel_type = DFS_AVAILABLE;
+			hostapd_set_oper_chwidth(iface->conf, oper_chwidth - 1);
+		}
+	}
+
+	wpa_printf(MSG_INFO,
+		   "%s: no DFS channels left, waiting for NOP to finish",
+		   __func__);
+	return NULL;
+}
+
+
+static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface)
+{
+	struct hostapd_channel_data *channel;
+	int secondary_channel;
+	u8 oper_centr_freq_seg0_idx = 0;
+	u8 oper_centr_freq_seg1_idx = 0;
+	enum dfs_channel_type channel_type = DFS_ANY_CHANNEL;
+	int err = 1;
+
+	/* Radar detected during active CAC */
+	iface->cac_started = 0;
+	channel = dfs_get_valid_channel(iface, &secondary_channel,
+					&oper_centr_freq_seg0_idx,
+					&oper_centr_freq_seg1_idx,
+					channel_type);
+
+	if (!channel) {
+		channel = dfs_downgrade_bandwidth(iface, &secondary_channel,
+						  &oper_centr_freq_seg0_idx,
+						  &oper_centr_freq_seg1_idx,
+						  &channel_type);
+		if (!channel) {
+			wpa_printf(MSG_ERROR, "No valid channel available");
+			return err;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d",
+		   channel->chan);
+	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
+		"freq=%d chan=%d sec_chan=%d", channel->freq,
+		channel->chan, secondary_channel);
+
+	iface->freq = channel->freq;
+	iface->conf->channel = channel->chan;
+	iface->conf->secondary_channel = secondary_channel;
+	hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
+					     oper_centr_freq_seg0_idx);
+	hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
+					     oper_centr_freq_seg1_idx);
+	err = 0;
+
+	hostapd_setup_interface_complete(iface, err);
+	return err;
+}
+
+
+static int
+hostapd_dfs_background_start_channel_switch(struct hostapd_iface *iface,
+					    int freq)
+{
+	if (!dfs_use_radar_background(iface))
+		return -1; /* Background radar chain not supported. */
+
+	wpa_printf(MSG_DEBUG,
+		   "%s called (background CAC active: %s, CSA active: %s)",
+		   __func__, iface->radar_background.cac_started ? "yes" : "no",
+		   hostapd_csa_in_progress(iface) ? "yes" : "no");
+
+	/* Check if CSA in progress */
+	if (hostapd_csa_in_progress(iface))
+		return 0;
+
+	if (hostapd_dfs_is_background_event(iface, freq)) {
+		/*
+		 * Radar pattern is reported on the background chain.
+		 * Just select a new random channel according to the
+		 * regulations for monitoring.
+		 */
+		hostpad_dfs_update_background_chain(iface);
+		return 0;
+	}
+
+	/*
+	 * If background radar detection is supported and the radar channel
+	 * monitored by the background chain is available switch to it without
+	 * waiting for the CAC.
+	 */
+	if (iface->radar_background.channel == -1)
+		return -1; /* Background radar chain not available. */
+
+	if (iface->radar_background.cac_started) {
+		/*
+		 * Background channel not available yet. Perform CAC on the
+		 * main chain.
+		 */
+		iface->radar_background.temp_ch = 1;
+		return -1;
+	}
+
+	return hostapd_dfs_start_channel_switch_background(iface);
+}
+
+
+static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
+{
+	struct hostapd_channel_data *channel;
+	int secondary_channel;
+	u8 oper_centr_freq_seg0_idx;
+	u8 oper_centr_freq_seg1_idx;
+	enum dfs_channel_type channel_type = DFS_AVAILABLE;
+	u8 current_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
+
+	wpa_printf(MSG_DEBUG, "%s called (CAC active: %s, CSA active: %s)",
+		   __func__, iface->cac_started ? "yes" : "no",
+		   hostapd_csa_in_progress(iface) ? "yes" : "no");
+
+	/* Check if CSA in progress */
+	if (hostapd_csa_in_progress(iface))
+		return 0;
+
+	/* Check if active CAC */
+	if (iface->cac_started)
+		return hostapd_dfs_start_channel_switch_cac(iface);
+
+	/*
+	 * Allow selection of DFS channel in ETSI to comply with
+	 * uniform spreading.
+	 */
+	if (iface->dfs_domain == HOSTAPD_DFS_REGION_ETSI)
+		channel_type = DFS_ANY_CHANNEL;
+
+	/* Perform channel switch/CSA */
+	channel = dfs_get_valid_channel(iface, &secondary_channel,
+					&oper_centr_freq_seg0_idx,
+					&oper_centr_freq_seg1_idx,
+					channel_type);
+
+	if (!channel) {
+		/*
+		 * If there is no channel to switch immediately to, check if
+		 * there is another channel where we can switch even if it
+		 * requires to perform a CAC first.
+		 */
+		channel_type = DFS_ANY_CHANNEL;
+		channel = dfs_downgrade_bandwidth(iface, &secondary_channel,
+						  &oper_centr_freq_seg0_idx,
+						  &oper_centr_freq_seg1_idx,
+						  &channel_type);
+		if (!channel) {
+			/*
+			 * Toggle interface state to enter DFS state
+			 * until NOP is finished.
+			 */
+			hostapd_disable_iface(iface);
+			hostapd_enable_iface(iface);
+			return 0;
+		}
+
+		if (channel_type == DFS_ANY_CHANNEL) {
+			iface->freq = channel->freq;
+			iface->conf->channel = channel->chan;
+			iface->conf->secondary_channel = secondary_channel;
+			hostapd_set_oper_centr_freq_seg0_idx(
+				iface->conf, oper_centr_freq_seg0_idx);
+			hostapd_set_oper_centr_freq_seg1_idx(
+				iface->conf, oper_centr_freq_seg1_idx);
+
+			hostapd_disable_iface(iface);
+			hostapd_enable_iface(iface);
+			return 0;
+		}
+	}
+
+	return hostapd_dfs_request_channel_switch(iface, channel->chan,
+						  channel->freq,
+						  secondary_channel,
+						  current_vht_oper_chwidth,
+						  oper_centr_freq_seg0_idx,
+						  oper_centr_freq_seg1_idx);
+}
+
+
+int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
+			       int ht_enabled, int chan_offset, int chan_width,
+			       int cf1, int cf2)
+{
+	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_RADAR_DETECTED
+		"freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
+		freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
+
+	/* Proceed only if DFS is not offloaded to the driver */
+	if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
+		return 0;
+
+	if (!iface->conf->ieee80211h)
+		return 0;
+
+	/* mark radar frequency as invalid */
+	if (!set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
+			   cf1, cf2, HOSTAPD_CHAN_DFS_UNAVAILABLE))
+		return 0;
+
+	if (!hostapd_dfs_is_background_event(iface, freq)) {
+		/* Skip if reported radar event not overlapped our channels */
+		if (!dfs_are_channels_overlapped(iface, freq, chan_width,
+						 cf1, cf2))
+			return 0;
+	}
+
+	if (hostapd_dfs_background_start_channel_switch(iface, freq)) {
+		/* Radar detected while operating, switch the channel. */
+		return hostapd_dfs_start_channel_switch(iface);
+	}
+
+	return 0;
+}
+
+
+int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
+			     int ht_enabled, int chan_offset, int chan_width,
+			     int cf1, int cf2)
+{
+	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NOP_FINISHED
+		"freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
+		freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
+
+	/* Proceed only if DFS is not offloaded to the driver */
+	if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
+		return 0;
+
+	/* TODO add correct implementation here */
+	set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
+		      cf1, cf2, HOSTAPD_CHAN_DFS_USABLE);
+
+	if (iface->state == HAPD_IFACE_DFS && !iface->cac_started) {
+		/* Handle cases where all channels were initially unavailable */
+		hostapd_handle_dfs(iface);
+	} else if (dfs_use_radar_background(iface) &&
+		   iface->radar_background.channel == -1) {
+		/* Reset radar background chain if disabled */
+		hostpad_dfs_update_background_chain(iface);
+	}
+
+	return 0;
+}
+
+
+int hostapd_is_dfs_required(struct hostapd_iface *iface)
+{
+	int n_chans, n_chans1, start_chan_idx, start_chan_idx1, res;
+
+	if ((!(iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
+	     !iface->conf->ieee80211h) ||
+	    !iface->current_mode ||
+	    iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
+		return 0;
+
+	/* Get start (first) channel for current configuration */
+	start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1);
+	if (start_chan_idx == -1)
+		return -1;
+
+	/* Get number of used channels, depend on width */
+	n_chans = dfs_get_used_n_chans(iface, &n_chans1);
+
+	/* Check if any of configured channels require DFS */
+	res = dfs_check_chans_radar(iface, start_chan_idx, n_chans);
+	if (res)
+		return res;
+	if (start_chan_idx1 >= 0 && n_chans1 > 0)
+		res = dfs_check_chans_radar(iface, start_chan_idx1, n_chans1);
+	return res;
+}
+
+
+int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
+			  int ht_enabled, int chan_offset, int chan_width,
+			  int cf1, int cf2)
+{
+	if (hostapd_dfs_is_background_event(iface, freq)) {
+		iface->radar_background.cac_started = 1;
+	} else {
+		/* This is called when the driver indicates that an offloaded
+		 * DFS has started CAC. */
+		hostapd_set_state(iface, HAPD_IFACE_DFS);
+		iface->cac_started = 1;
+	}
+	/* TODO: How to check CAC time for ETSI weather channels? */
+#ifdef CONFIG_DFS_CAC_TIMER
+	/* Yewei, add dfs cac timer in seconds, 20240118 */
+	if (iface->conf->dfs_cac_s) {
+		iface->dfs_cac_ms = iface->conf->dfs_cac_s * 1000;
+	}
+	else
+#endif /* CONFIG_DFS_CAC_TIMER */
+	{
+		iface->dfs_cac_ms = 60000;
+	}
+	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
+		"freq=%d chan=%d chan_offset=%d width=%d seg0=%d "
+		"seg1=%d cac_time=%ds%s",
+		freq, (freq - 5000) / 5, chan_offset, chan_width, cf1, cf2,
+		iface->dfs_cac_ms / 1000,
+		hostapd_dfs_is_background_event(iface, freq) ?
+		" (background)" : "");
+
+	os_get_reltime(&iface->dfs_cac_start);
+	return 0;
+}
+
+
+/*
+ * Main DFS handler for offloaded case.
+ * 2 - continue channel/AP setup for non-DFS channel
+ * 1 - continue channel/AP setup for DFS channel
+ * 0 - channel/AP setup will be continued after CAC
+ * -1 - hit critical error
+ */
+int hostapd_handle_dfs_offload(struct hostapd_iface *iface)
+{
+	int dfs_res;
+
+	wpa_printf(MSG_DEBUG, "%s: iface->cac_started: %d",
+		   __func__, iface->cac_started);
+
+	/*
+	 * If DFS has already been started, then we are being called from a
+	 * callback to continue AP/channel setup. Reset the CAC start flag and
+	 * return.
+	 */
+	if (iface->cac_started) {
+		wpa_printf(MSG_DEBUG, "%s: iface->cac_started: %d",
+			   __func__, iface->cac_started);
+		iface->cac_started = 0;
+		return 1;
+	}
+
+	dfs_res = hostapd_is_dfs_required(iface);
+	if (dfs_res > 0) {
+		wpa_printf(MSG_DEBUG,
+			   "%s: freq %d MHz requires DFS for %d chans",
+			   __func__, iface->freq, dfs_res);
+		return 0;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "%s: freq %d MHz does not require DFS. Continue channel/AP setup",
+		   __func__, iface->freq);
+	return 2;
+}
+
+
+int hostapd_is_dfs_overlap(struct hostapd_iface *iface, enum chan_width width,
+			   int center_freq)
+{
+	struct hostapd_channel_data *chan;
+	struct hostapd_hw_modes *mode = iface->current_mode;
+	int half_width;
+	int res = 0;
+	int i;
+
+	if (!iface->conf->ieee80211h || !mode ||
+	    mode->mode != HOSTAPD_MODE_IEEE80211A)
+		return 0;
+
+	switch (width) {
+	case CHAN_WIDTH_20_NOHT:
+	case CHAN_WIDTH_20:
+		half_width = 10;
+		break;
+	case CHAN_WIDTH_40:
+		half_width = 20;
+		break;
+	case CHAN_WIDTH_80:
+	case CHAN_WIDTH_80P80:
+		half_width = 40;
+		break;
+	case CHAN_WIDTH_160:
+		half_width = 80;
+		break;
+	default:
+		wpa_printf(MSG_WARNING, "DFS chanwidth %d not supported",
+			   width);
+		return 0;
+	}
+
+	for (i = 0; i < mode->num_channels; i++) {
+		chan = &mode->channels[i];
+
+		if (!(chan->flag & HOSTAPD_CHAN_RADAR))
+			continue;
+
+		if ((chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
+		    HOSTAPD_CHAN_DFS_AVAILABLE)
+			continue;
+
+		if (center_freq - chan->freq < half_width &&
+		    chan->freq - center_freq < half_width)
+			res++;
+	}
+
+	wpa_printf(MSG_DEBUG, "DFS CAC required: (%d, %d): in range: %s",
+		   center_freq - half_width, center_freq + half_width,
+		   res ? "yes" : "no");
+
+	return res;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/dfs.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/dfs.h
new file mode 100644
index 0000000..606c1b3
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/dfs.h
@@ -0,0 +1,36 @@
+/*
+ * DFS - Dynamic Frequency Selection
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2013-2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+#ifndef DFS_H
+#define DFS_H
+
+int hostapd_handle_dfs(struct hostapd_iface *iface);
+
+int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
+			     int ht_enabled, int chan_offset, int chan_width,
+			     int cf1, int cf2);
+int hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq,
+				int ht_enabled, int chan_offset, int chan_width,
+				int cf1, int cf2);
+int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
+			       int ht_enabled,
+			       int chan_offset, int chan_width,
+			       int cf1, int cf2);
+int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
+			     int ht_enabled,
+			     int chan_offset, int chan_width, int cf1, int cf2);
+int hostapd_is_dfs_required(struct hostapd_iface *iface);
+int hostapd_is_dfs_chan_available(struct hostapd_iface *iface);
+int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
+			  int ht_enabled, int chan_offset, int chan_width,
+			  int cf1, int cf2);
+int hostapd_handle_dfs_offload(struct hostapd_iface *iface);
+int hostapd_is_dfs_overlap(struct hostapd_iface *iface, enum chan_width width,
+			   int center_freq);
+
+#endif /* DFS_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/dhcp_snoop.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/dhcp_snoop.c
new file mode 100644
index 0000000..551936b
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/dhcp_snoop.c
@@ -0,0 +1,160 @@
+/*
+ * DHCP snooping for Proxy ARP
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/dhcp.h"
+#include "l2_packet/l2_packet.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ap_drv_ops.h"
+#include "x_snoop.h"
+#include "dhcp_snoop.h"
+
+
+static const char * ipaddr_str(u32 addr)
+{
+	static char buf[17];
+
+	os_snprintf(buf, sizeof(buf), "%u.%u.%u.%u",
+		    (addr >> 24) & 0xff, (addr >> 16) & 0xff,
+		    (addr >> 8) & 0xff, addr & 0xff);
+	return buf;
+}
+
+
+static void handle_dhcp(void *ctx, const u8 *src_addr, const u8 *buf,
+			size_t len)
+{
+	struct hostapd_data *hapd = ctx;
+	const struct bootp_pkt *b;
+	struct sta_info *sta;
+	int exten_len;
+	const u8 *end, *pos;
+	int res, msgtype = 0, prefixlen = 32;
+	u32 subnet_mask = 0;
+	u16 ip_len;
+
+	exten_len = len - ETH_HLEN - (sizeof(*b) - sizeof(b->exten));
+	if (exten_len < 4)
+		return;
+
+	b = (const struct bootp_pkt *) &buf[ETH_HLEN];
+	ip_len = ntohs(b->iph.ip_len);
+	if (ip_len > (unsigned int) (len - ETH_HLEN))
+		return;
+
+	if (WPA_GET_BE32(b->exten) != DHCP_MAGIC)
+		return;
+
+	/* Parse DHCP options */
+	end = (const u8 *) b + ip_len;
+	pos = &b->exten[4];
+	while (pos < end && *pos != DHCP_OPT_END) {
+		const u8 *opt = pos++;
+
+		if (*opt == DHCP_OPT_PAD)
+			continue;
+
+		if (pos >= end || 1 + *pos > end - pos)
+			break;
+		pos += *pos + 1;
+		if (pos >= end)
+			break;
+
+		switch (*opt) {
+		case DHCP_OPT_SUBNET_MASK:
+			if (opt[1] == 4)
+				subnet_mask = WPA_GET_BE32(&opt[2]);
+			if (subnet_mask == 0)
+				return;
+			while (!(subnet_mask & 0x1)) {
+				subnet_mask >>= 1;
+				prefixlen--;
+			}
+			break;
+		case DHCP_OPT_MSG_TYPE:
+			if (opt[1])
+				msgtype = opt[2];
+			break;
+		default:
+			break;
+		}
+	}
+
+#ifdef CONFIG_HS20
+	if (hapd->conf->disable_dgaf && is_broadcast_ether_addr(buf)) {
+		for (sta = hapd->sta_list; sta; sta = sta->next) {
+			if (!(sta->flags & WLAN_STA_AUTHORIZED))
+				continue;
+			x_snoop_mcast_to_ucast_convert_send(hapd, sta,
+							    (u8 *) buf, len);
+		}
+	}
+#endif /* CONFIG_HS20 */
+
+	if (msgtype == DHCPACK) {
+		if (b->your_ip == 0)
+			return;
+
+		/* DHCPACK for DHCPREQUEST */
+		sta = ap_get_sta(hapd, b->hw_addr);
+		if (!sta)
+			return;
+
+		wpa_printf(MSG_DEBUG, "dhcp_snoop: Found DHCPACK for " MACSTR
+			   " @ IPv4 address %s/%d",
+			   MAC2STR(sta->addr),
+			   ipaddr_str(be_to_host32(b->your_ip)),
+			   prefixlen);
+
+		if (sta->ipaddr == b->your_ip)
+			return;
+
+		if (sta->ipaddr != 0) {
+			wpa_printf(MSG_DEBUG,
+				   "dhcp_snoop: Removing IPv4 address %s from the ip neigh table",
+				   ipaddr_str(be_to_host32(sta->ipaddr)));
+			hostapd_drv_br_delete_ip_neigh(hapd, 4,
+						       (u8 *) &sta->ipaddr);
+		}
+
+		res = hostapd_drv_br_add_ip_neigh(hapd, 4, (u8 *) &b->your_ip,
+						  prefixlen, sta->addr);
+		if (res) {
+			wpa_printf(MSG_DEBUG,
+				   "dhcp_snoop: Adding ip neigh table failed: %d",
+				   res);
+			return;
+		}
+		sta->ipaddr = b->your_ip;
+	}
+}
+
+
+int dhcp_snoop_init(struct hostapd_data *hapd)
+{
+	hapd->sock_dhcp = x_snoop_get_l2_packet(hapd, handle_dhcp,
+						L2_PACKET_FILTER_DHCP);
+	if (hapd->sock_dhcp == NULL) {
+		wpa_printf(MSG_DEBUG,
+			   "dhcp_snoop: Failed to initialize L2 packet processing for DHCP packet: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void dhcp_snoop_deinit(struct hostapd_data *hapd)
+{
+	l2_packet_deinit(hapd->sock_dhcp);
+	hapd->sock_dhcp = NULL;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/dhcp_snoop.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/dhcp_snoop.h
new file mode 100644
index 0000000..93d0050
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/dhcp_snoop.h
@@ -0,0 +1,30 @@
+/*
+ * DHCP snooping for Proxy ARP
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DHCP_SNOOP_H
+#define DHCP_SNOOP_H
+
+#ifdef CONFIG_PROXYARP
+
+int dhcp_snoop_init(struct hostapd_data *hapd);
+void dhcp_snoop_deinit(struct hostapd_data *hapd);
+
+#else /* CONFIG_PROXYARP */
+
+static inline int dhcp_snoop_init(struct hostapd_data *hapd)
+{
+	return 0;
+}
+
+static inline void dhcp_snoop_deinit(struct hostapd_data *hapd)
+{
+}
+
+#endif /* CONFIG_PROXYARP */
+
+#endif /* DHCP_SNOOP_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/dpp_hostapd.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/dpp_hostapd.c
new file mode 100644
index 0000000..70dd18e
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/dpp_hostapd.c
@@ -0,0 +1,3976 @@
+/*
+ * hostapd / DPP integration
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2018-2020, The Linux Foundation
+ * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/dpp.h"
+#include "common/gas.h"
+#include "common/wpa_ctrl.h"
+#include "crypto/random.h"
+#include "hostapd.h"
+#include "ap_drv_ops.h"
+#include "gas_query_ap.h"
+#include "gas_serv.h"
+#include "wpa_auth.h"
+#include "beacon.h"
+#include "dpp_hostapd.h"
+
+
+static void hostapd_dpp_reply_wait_timeout(void *eloop_ctx, void *timeout_ctx);
+static void hostapd_dpp_auth_conf_wait_timeout(void *eloop_ctx,
+					       void *timeout_ctx);
+static void hostapd_dpp_auth_success(struct hostapd_data *hapd, int initiator);
+static void hostapd_dpp_init_timeout(void *eloop_ctx, void *timeout_ctx);
+static int hostapd_dpp_auth_init_next(struct hostapd_data *hapd);
+static void hostapd_dpp_set_testing_options(struct hostapd_data *hapd,
+					    struct dpp_authentication *auth);
+static void hostapd_dpp_start_gas_client(struct hostapd_data *hapd);
+#ifdef CONFIG_DPP2
+static void hostapd_dpp_reconfig_reply_wait_timeout(void *eloop_ctx,
+						    void *timeout_ctx);
+static void hostapd_dpp_handle_config_obj(struct hostapd_data *hapd,
+					  struct dpp_authentication *auth,
+					  struct dpp_config_obj *conf);
+static int hostapd_dpp_process_conf_obj(void *ctx,
+					struct dpp_authentication *auth);
+#endif /* CONFIG_DPP2 */
+
+static const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+
+/**
+ * hostapd_dpp_qr_code - Parse and add DPP bootstrapping info from a QR Code
+ * @hapd: Pointer to hostapd_data
+ * @cmd: DPP URI read from a QR Code
+ * Returns: Identifier of the stored info or -1 on failure
+ */
+int hostapd_dpp_qr_code(struct hostapd_data *hapd, const char *cmd)
+{
+	struct dpp_bootstrap_info *bi;
+	struct dpp_authentication *auth = hapd->dpp_auth;
+
+	bi = dpp_add_qr_code(hapd->iface->interfaces->dpp, cmd);
+	if (!bi)
+		return -1;
+
+	if (auth && auth->response_pending &&
+	    dpp_notify_new_qr_code(auth, bi) == 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Sending out pending authentication response");
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+			" freq=%u type=%d",
+			MAC2STR(auth->peer_mac_addr), auth->curr_freq,
+			DPP_PA_AUTHENTICATION_RESP);
+		hostapd_drv_send_action(hapd, auth->curr_freq, 0,
+					auth->peer_mac_addr,
+					wpabuf_head(hapd->dpp_auth->resp_msg),
+					wpabuf_len(hapd->dpp_auth->resp_msg));
+	}
+
+#ifdef CONFIG_DPP2
+	dpp_controller_new_qr_code(hapd->iface->interfaces->dpp, bi);
+#endif /* CONFIG_DPP2 */
+
+	return bi->id;
+}
+
+
+/**
+ * hostapd_dpp_nfc_uri - Parse and add DPP bootstrapping info from NFC Tag (URI)
+ * @hapd: Pointer to hostapd_data
+ * @cmd: DPP URI read from a NFC Tag (URI NDEF message)
+ * Returns: Identifier of the stored info or -1 on failure
+ */
+int hostapd_dpp_nfc_uri(struct hostapd_data *hapd, const char *cmd)
+{
+	struct dpp_bootstrap_info *bi;
+
+	bi = dpp_add_nfc_uri(hapd->iface->interfaces->dpp, cmd);
+	if (!bi)
+		return -1;
+
+	return bi->id;
+}
+
+
+int hostapd_dpp_nfc_handover_req(struct hostapd_data *hapd, const char *cmd)
+{
+	const char *pos;
+	struct dpp_bootstrap_info *peer_bi, *own_bi;
+
+	pos = os_strstr(cmd, " own=");
+	if (!pos)
+		return -1;
+	pos += 5;
+	own_bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp, atoi(pos));
+	if (!own_bi)
+		return -1;
+
+	pos = os_strstr(cmd, " uri=");
+	if (!pos)
+		return -1;
+	pos += 5;
+	peer_bi = dpp_add_nfc_uri(hapd->iface->interfaces->dpp, pos);
+	if (!peer_bi) {
+		wpa_printf(MSG_INFO,
+			   "DPP: Failed to parse URI from NFC Handover Request");
+		return -1;
+	}
+
+	if (dpp_nfc_update_bi(own_bi, peer_bi) < 0)
+		return -1;
+
+	return peer_bi->id;
+}
+
+
+int hostapd_dpp_nfc_handover_sel(struct hostapd_data *hapd, const char *cmd)
+{
+	const char *pos;
+	struct dpp_bootstrap_info *peer_bi, *own_bi;
+
+	pos = os_strstr(cmd, " own=");
+	if (!pos)
+		return -1;
+	pos += 5;
+	own_bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp, atoi(pos));
+	if (!own_bi)
+		return -1;
+
+	pos = os_strstr(cmd, " uri=");
+	if (!pos)
+		return -1;
+	pos += 5;
+	peer_bi = dpp_add_nfc_uri(hapd->iface->interfaces->dpp, pos);
+	if (!peer_bi) {
+		wpa_printf(MSG_INFO,
+			   "DPP: Failed to parse URI from NFC Handover Select");
+		return -1;
+	}
+
+	if (peer_bi->curve != own_bi->curve) {
+		wpa_printf(MSG_INFO,
+			   "DPP: Peer (NFC Handover Selector) used different curve");
+		return -1;
+	}
+
+	return peer_bi->id;
+}
+
+
+static void hostapd_dpp_auth_resp_retry_timeout(void *eloop_ctx,
+						void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct dpp_authentication *auth = hapd->dpp_auth;
+
+	if (!auth || !auth->resp_msg)
+		return;
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Retry Authentication Response after timeout");
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+		" freq=%u type=%d",
+		MAC2STR(auth->peer_mac_addr), auth->curr_freq,
+		DPP_PA_AUTHENTICATION_RESP);
+	hostapd_drv_send_action(hapd, auth->curr_freq, 500, auth->peer_mac_addr,
+				wpabuf_head(auth->resp_msg),
+				wpabuf_len(auth->resp_msg));
+}
+
+
+static void hostapd_dpp_auth_resp_retry(struct hostapd_data *hapd)
+{
+	struct dpp_authentication *auth = hapd->dpp_auth;
+	unsigned int wait_time, max_tries;
+
+	if (!auth || !auth->resp_msg)
+		return;
+
+	if (hapd->dpp_resp_max_tries)
+		max_tries = hapd->dpp_resp_max_tries;
+	else
+		max_tries = 5;
+	auth->auth_resp_tries++;
+	if (auth->auth_resp_tries >= max_tries) {
+		wpa_printf(MSG_INFO,
+			   "DPP: No confirm received from initiator - stopping exchange");
+		hostapd_drv_send_action_cancel_wait(hapd);
+		dpp_auth_deinit(hapd->dpp_auth);
+		hapd->dpp_auth = NULL;
+		return;
+	}
+
+	if (hapd->dpp_resp_retry_time)
+		wait_time = hapd->dpp_resp_retry_time;
+	else
+		wait_time = 1000;
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Schedule retransmission of Authentication Response frame in %u ms",
+		wait_time);
+	eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
+	eloop_register_timeout(wait_time / 1000,
+			       (wait_time % 1000) * 1000,
+			       hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
+}
+
+
+static int hostapd_dpp_allow_ir(struct hostapd_data *hapd, unsigned int freq)
+{
+	int i, j;
+
+	if (!hapd->iface->hw_features)
+		return -1;
+
+	for (i = 0; i < hapd->iface->num_hw_features; i++) {
+		struct hostapd_hw_modes *mode = &hapd->iface->hw_features[i];
+
+		for (j = 0; j < mode->num_channels; j++) {
+			struct hostapd_channel_data *chan = &mode->channels[j];
+
+			if (chan->freq != (int) freq)
+				continue;
+
+			if (chan->flag & (HOSTAPD_CHAN_DISABLED |
+					  HOSTAPD_CHAN_NO_IR |
+					  HOSTAPD_CHAN_RADAR))
+				continue;
+
+			return 1;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Frequency %u MHz not supported or does not allow PKEX initiation in the current channel list",
+		   freq);
+
+	return 0;
+}
+
+
+static int hostapd_dpp_pkex_next_channel(struct hostapd_data *hapd,
+					 struct dpp_pkex *pkex)
+{
+	if (pkex->freq == 2437)
+		pkex->freq = 5745;
+	else if (pkex->freq == 5745)
+		pkex->freq = 5220;
+	else if (pkex->freq == 5220)
+		pkex->freq = 60480;
+	else
+		return -1; /* no more channels to try */
+
+	if (hostapd_dpp_allow_ir(hapd, pkex->freq) == 1) {
+		wpa_printf(MSG_DEBUG, "DPP: Try to initiate on %u MHz",
+			   pkex->freq);
+		return 0;
+	}
+
+	/* Could not use this channel - try the next one */
+	return hostapd_dpp_pkex_next_channel(hapd, pkex);
+}
+
+
+static void hostapd_dpp_pkex_clear_code(struct hostapd_data *hapd)
+{
+	if (!hapd->dpp_pkex_code && !hapd->dpp_pkex_identifier)
+		return;
+
+	/* Delete PKEX code and identifier on successful completion of
+	 * PKEX. We are not supposed to reuse these without being
+	 * explicitly requested to perform PKEX again. */
+	wpa_printf(MSG_DEBUG, "DPP: Delete PKEX code/identifier");
+	os_free(hapd->dpp_pkex_code);
+	hapd->dpp_pkex_code = NULL;
+	os_free(hapd->dpp_pkex_identifier);
+	hapd->dpp_pkex_identifier = NULL;
+}
+
+
+#ifdef CONFIG_DPP2
+static int hostapd_dpp_pkex_done(void *ctx, void *conn,
+				 struct dpp_bootstrap_info *peer_bi)
+{
+	struct hostapd_data *hapd = ctx;
+	char cmd[500];
+	const char *pos;
+	u8 allowed_roles = DPP_CAPAB_CONFIGURATOR;
+	struct dpp_bootstrap_info *own_bi = NULL;
+	struct dpp_authentication *auth;
+
+	hostapd_dpp_pkex_clear_code(hapd);
+
+	os_snprintf(cmd, sizeof(cmd), " peer=%u %s", peer_bi->id,
+		    hapd->dpp_pkex_auth_cmd ? hapd->dpp_pkex_auth_cmd : "");
+	wpa_printf(MSG_DEBUG, "DPP: Start authentication after PKEX (cmd: %s)",
+		   cmd);
+
+	pos = os_strstr(cmd, " own=");
+	if (pos) {
+		pos += 5;
+		own_bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp,
+					      atoi(pos));
+		if (!own_bi) {
+			wpa_printf(MSG_INFO,
+				   "DPP: Could not find bootstrapping info for the identified local entry");
+			return -1;
+		}
+
+		if (peer_bi->curve != own_bi->curve) {
+			wpa_printf(MSG_INFO,
+				   "DPP: Mismatching curves in bootstrapping info (peer=%s own=%s)",
+				   peer_bi->curve->name, own_bi->curve->name);
+			return -1;
+		}
+	}
+
+	pos = os_strstr(cmd, " role=");
+	if (pos) {
+		pos += 6;
+		if (os_strncmp(pos, "configurator", 12) == 0)
+			allowed_roles = DPP_CAPAB_CONFIGURATOR;
+		else if (os_strncmp(pos, "enrollee", 8) == 0)
+			allowed_roles = DPP_CAPAB_ENROLLEE;
+		else if (os_strncmp(pos, "either", 6) == 0)
+			allowed_roles = DPP_CAPAB_CONFIGURATOR |
+				DPP_CAPAB_ENROLLEE;
+		else
+			return -1;
+	}
+
+	auth = dpp_auth_init(hapd->iface->interfaces->dpp, hapd->msg_ctx,
+			     peer_bi, own_bi, allowed_roles, 0,
+			     hapd->iface->hw_features,
+			     hapd->iface->num_hw_features);
+	if (!auth)
+		return -1;
+
+	hostapd_dpp_set_testing_options(hapd, auth);
+	if (dpp_set_configurator(auth, cmd) < 0) {
+		dpp_auth_deinit(auth);
+		return -1;
+	}
+
+	return dpp_tcp_auth(hapd->iface->interfaces->dpp, conn, auth,
+			    hapd->conf->dpp_name, DPP_NETROLE_AP,
+			    hapd->conf->dpp_mud_url,
+			    hapd->conf->dpp_extra_conf_req_name,
+			    hapd->conf->dpp_extra_conf_req_value,
+			    hostapd_dpp_process_conf_obj, NULL);
+}
+#endif /* CONFIG_DPP2 */
+
+
+static int hostapd_dpp_pkex_init(struct hostapd_data *hapd,
+				 enum dpp_pkex_ver ver,
+				 const struct hostapd_ip_addr *ipaddr,
+				 int tcp_port)
+{
+	struct dpp_pkex *pkex;
+	struct wpabuf *msg;
+	unsigned int wait_time;
+	bool v2 = ver != PKEX_VER_ONLY_1;
+
+	wpa_printf(MSG_DEBUG, "DPP: Initiating PKEXv%d", v2 ? 2 : 1);
+	dpp_pkex_free(hapd->dpp_pkex);
+	hapd->dpp_pkex = NULL;
+	pkex = dpp_pkex_init(hapd->msg_ctx, hapd->dpp_pkex_bi, hapd->own_addr,
+			     hapd->dpp_pkex_identifier,
+			     hapd->dpp_pkex_code, hapd->dpp_pkex_code_len, v2);
+	if (!pkex)
+		return -1;
+	pkex->forced_ver = ver != PKEX_VER_AUTO;
+
+	if (ipaddr) {
+#ifdef CONFIG_DPP2
+		return dpp_tcp_pkex_init(hapd->iface->interfaces->dpp, pkex,
+					 ipaddr, tcp_port,
+					 hapd->msg_ctx, hapd,
+					 hostapd_dpp_pkex_done);
+#else /* CONFIG_DPP2 */
+		return -1;
+#endif /* CONFIG_DPP2 */
+	}
+
+	hapd->dpp_pkex = pkex;
+	msg = hapd->dpp_pkex->exchange_req;
+	wait_time = 2000; /* TODO: hapd->max_remain_on_chan; */
+	pkex->freq = 2437;
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+		" freq=%u type=%d", MAC2STR(broadcast), pkex->freq,
+		v2 ? DPP_PA_PKEX_EXCHANGE_REQ :
+		DPP_PA_PKEX_V1_EXCHANGE_REQ);
+	hostapd_drv_send_action(hapd, pkex->freq, 0, broadcast,
+				wpabuf_head(msg), wpabuf_len(msg));
+	pkex->exch_req_wait_time = wait_time;
+	pkex->exch_req_tries = 1;
+
+	return 0;
+}
+
+
+static void hostapd_dpp_pkex_retry_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct dpp_pkex *pkex = hapd->dpp_pkex;
+
+	if (!pkex || !pkex->exchange_req)
+		return;
+	if (pkex->exch_req_tries >= 5) {
+		if (hostapd_dpp_pkex_next_channel(hapd, pkex) < 0) {
+#ifdef CONFIG_DPP3
+			if (pkex->v2 && !pkex->forced_ver) {
+				wpa_printf(MSG_DEBUG,
+					   "DPP: Fall back to PKEXv1");
+				hostapd_dpp_pkex_init(hapd, PKEX_VER_ONLY_1,
+						      NULL, 0);
+				return;
+			}
+#endif /* CONFIG_DPP3 */
+			wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+				"No response from PKEX peer");
+			dpp_pkex_free(pkex);
+			hapd->dpp_pkex = NULL;
+			return;
+		}
+		pkex->exch_req_tries = 0;
+	}
+
+	pkex->exch_req_tries++;
+	wpa_printf(MSG_DEBUG, "DPP: Retransmit PKEX Exchange Request (try %u)",
+		   pkex->exch_req_tries);
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+		" freq=%u type=%d",
+		MAC2STR(broadcast), pkex->freq,
+		pkex->v2 ? DPP_PA_PKEX_EXCHANGE_REQ :
+		DPP_PA_PKEX_V1_EXCHANGE_REQ);
+	hostapd_drv_send_action(hapd, pkex->freq, pkex->exch_req_wait_time,
+				broadcast,
+				wpabuf_head(pkex->exchange_req),
+				wpabuf_len(pkex->exchange_req));
+}
+
+
+static void hostapd_dpp_pkex_tx_status(struct hostapd_data *hapd, const u8 *dst,
+				       const u8 *data, size_t data_len, int ok)
+{
+	struct dpp_pkex *pkex = hapd->dpp_pkex;
+
+	if (pkex->failed) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Terminate PKEX exchange due to an earlier error");
+		if (pkex->t > pkex->own_bi->pkex_t)
+			pkex->own_bi->pkex_t = pkex->t;
+		dpp_pkex_free(pkex);
+		hapd->dpp_pkex = NULL;
+		return;
+	}
+
+	if (pkex->exch_req_wait_time && pkex->exchange_req) {
+		/* Wait for PKEX Exchange Response frame and retry request if
+		 * no response is seen. */
+		eloop_cancel_timeout(hostapd_dpp_pkex_retry_timeout, hapd,
+				     NULL);
+		eloop_register_timeout(pkex->exch_req_wait_time / 1000,
+				       (pkex->exch_req_wait_time % 1000) * 1000,
+				       hostapd_dpp_pkex_retry_timeout, hapd,
+				       NULL);
+	}
+}
+
+
+void hostapd_dpp_tx_status(struct hostapd_data *hapd, const u8 *dst,
+			   const u8 *data, size_t data_len, int ok)
+{
+	struct dpp_authentication *auth = hapd->dpp_auth;
+
+	wpa_printf(MSG_DEBUG, "DPP: TX status: dst=" MACSTR " ok=%d",
+		   MAC2STR(dst), ok);
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX_STATUS "dst=" MACSTR
+		" result=%s", MAC2STR(dst), ok ? "SUCCESS" : "FAILED");
+
+	if (!hapd->dpp_auth) {
+		if (hapd->dpp_pkex) {
+			hostapd_dpp_pkex_tx_status(hapd, dst, data, data_len,
+						   ok);
+			return;
+		}
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Ignore TX status since there is no ongoing authentication exchange");
+		return;
+	}
+
+#ifdef CONFIG_DPP2
+	if (auth->connect_on_tx_status) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Complete exchange on configuration result");
+		dpp_auth_deinit(hapd->dpp_auth);
+		hapd->dpp_auth = NULL;
+		return;
+	}
+#endif /* CONFIG_DPP2 */
+
+	if (hapd->dpp_auth->remove_on_tx_status) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Terminate authentication exchange due to an earlier error");
+		eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
+		eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout,
+				     hapd, NULL);
+		eloop_cancel_timeout(hostapd_dpp_auth_conf_wait_timeout,
+				     hapd, NULL);
+		eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd,
+				     NULL);
+#ifdef CONFIG_DPP2
+		eloop_cancel_timeout(hostapd_dpp_reconfig_reply_wait_timeout,
+				     hapd, NULL);
+#endif /* CONFIG_DPP2 */
+		hostapd_drv_send_action_cancel_wait(hapd);
+		dpp_auth_deinit(hapd->dpp_auth);
+		hapd->dpp_auth = NULL;
+		return;
+	}
+
+	if (hapd->dpp_auth_ok_on_ack)
+		hostapd_dpp_auth_success(hapd, 1);
+
+	if (!is_broadcast_ether_addr(dst) && !ok) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unicast DPP Action frame was not ACKed");
+		if (auth->waiting_auth_resp) {
+			/* In case of DPP Authentication Request frame, move to
+			 * the next channel immediately. */
+			hostapd_drv_send_action_cancel_wait(hapd);
+			hostapd_dpp_auth_init_next(hapd);
+			return;
+		}
+		if (auth->waiting_auth_conf) {
+			hostapd_dpp_auth_resp_retry(hapd);
+			return;
+		}
+	}
+
+	if (auth->waiting_auth_conf &&
+	    auth->auth_resp_status == DPP_STATUS_OK) {
+		/* Make sure we do not get stuck waiting for Auth Confirm
+		 * indefinitely after successfully transmitted Auth Response to
+		 * allow new authentication exchanges to be started. */
+		eloop_cancel_timeout(hostapd_dpp_auth_conf_wait_timeout, hapd,
+				     NULL);
+		eloop_register_timeout(1, 0, hostapd_dpp_auth_conf_wait_timeout,
+				       hapd, NULL);
+	}
+
+	if (!is_broadcast_ether_addr(dst) && auth->waiting_auth_resp && ok) {
+		/* Allow timeout handling to stop iteration if no response is
+		 * received from a peer that has ACKed a request. */
+		auth->auth_req_ack = 1;
+	}
+
+	if (!hapd->dpp_auth_ok_on_ack && hapd->dpp_auth->neg_freq > 0 &&
+	    hapd->dpp_auth->curr_freq != hapd->dpp_auth->neg_freq) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Move from curr_freq %u MHz to neg_freq %u MHz for response",
+			   hapd->dpp_auth->curr_freq,
+			   hapd->dpp_auth->neg_freq);
+		hostapd_drv_send_action_cancel_wait(hapd);
+
+		if (hapd->dpp_auth->neg_freq !=
+		    (unsigned int) hapd->iface->freq && hapd->iface->freq > 0) {
+			/* TODO: Listen operation on non-operating channel */
+			wpa_printf(MSG_INFO,
+				   "DPP: Listen operation on non-operating channel (%d MHz) is not yet supported (operating channel: %d MHz)",
+				   hapd->dpp_auth->neg_freq, hapd->iface->freq);
+		}
+	}
+
+	if (hapd->dpp_auth_ok_on_ack)
+		hapd->dpp_auth_ok_on_ack = 0;
+}
+
+
+static void hostapd_dpp_reply_wait_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct dpp_authentication *auth = hapd->dpp_auth;
+	unsigned int freq;
+	struct os_reltime now, diff;
+	unsigned int wait_time, diff_ms;
+
+	if (!auth || !auth->waiting_auth_resp)
+		return;
+
+	wait_time = hapd->dpp_resp_wait_time ?
+		hapd->dpp_resp_wait_time : 2000;
+	os_get_reltime(&now);
+	os_reltime_sub(&now, &hapd->dpp_last_init, &diff);
+	diff_ms = diff.sec * 1000 + diff.usec / 1000;
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Reply wait timeout - wait_time=%u diff_ms=%u",
+		   wait_time, diff_ms);
+
+	if (auth->auth_req_ack && diff_ms >= wait_time) {
+		/* Peer ACK'ed Authentication Request frame, but did not reply
+		 * with Authentication Response frame within two seconds. */
+		wpa_printf(MSG_INFO,
+			   "DPP: No response received from responder - stopping initiation attempt");
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_INIT_FAILED);
+		hostapd_drv_send_action_cancel_wait(hapd);
+		hostapd_dpp_listen_stop(hapd);
+		dpp_auth_deinit(auth);
+		hapd->dpp_auth = NULL;
+		return;
+	}
+
+	if (diff_ms >= wait_time) {
+		/* Authentication Request frame was not ACK'ed and no reply
+		 * was receiving within two seconds. */
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Continue Initiator channel iteration");
+		hostapd_drv_send_action_cancel_wait(hapd);
+		hostapd_dpp_listen_stop(hapd);
+		hostapd_dpp_auth_init_next(hapd);
+		return;
+	}
+
+	/* Driver did not support 2000 ms long wait_time with TX command, so
+	 * schedule listen operation to continue waiting for the response.
+	 *
+	 * DPP listen operations continue until stopped, so simply schedule a
+	 * new call to this function at the point when the two second reply
+	 * wait has expired. */
+	wait_time -= diff_ms;
+
+	freq = auth->curr_freq;
+	if (auth->neg_freq > 0)
+		freq = auth->neg_freq;
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Continue reply wait on channel %u MHz for %u ms",
+		   freq, wait_time);
+	hapd->dpp_in_response_listen = 1;
+
+	if (freq != (unsigned int) hapd->iface->freq && hapd->iface->freq > 0) {
+		/* TODO: Listen operation on non-operating channel */
+		wpa_printf(MSG_INFO,
+			   "DPP: Listen operation on non-operating channel (%d MHz) is not yet supported (operating channel: %d MHz)",
+			   freq, hapd->iface->freq);
+	}
+
+	eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000,
+			       hostapd_dpp_reply_wait_timeout, hapd, NULL);
+}
+
+
+static void hostapd_dpp_auth_conf_wait_timeout(void *eloop_ctx,
+					       void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct dpp_authentication *auth = hapd->dpp_auth;
+
+	if (!auth || !auth->waiting_auth_conf)
+		return;
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Terminate authentication exchange due to Auth Confirm timeout");
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+		"No Auth Confirm received");
+	hostapd_drv_send_action_cancel_wait(hapd);
+	dpp_auth_deinit(auth);
+	hapd->dpp_auth = NULL;
+}
+
+
+static void hostapd_dpp_set_testing_options(struct hostapd_data *hapd,
+					    struct dpp_authentication *auth)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+	if (hapd->dpp_config_obj_override)
+		auth->config_obj_override =
+			os_strdup(hapd->dpp_config_obj_override);
+	if (hapd->dpp_discovery_override)
+		auth->discovery_override =
+			os_strdup(hapd->dpp_discovery_override);
+	if (hapd->dpp_groups_override)
+		auth->groups_override = os_strdup(hapd->dpp_groups_override);
+	auth->ignore_netaccesskey_mismatch =
+		hapd->dpp_ignore_netaccesskey_mismatch;
+#endif /* CONFIG_TESTING_OPTIONS */
+}
+
+
+static void hostapd_dpp_init_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+
+	if (!hapd->dpp_auth)
+		return;
+	wpa_printf(MSG_DEBUG, "DPP: Retry initiation after timeout");
+	hostapd_dpp_auth_init_next(hapd);
+}
+
+
+static int hostapd_dpp_auth_init_next(struct hostapd_data *hapd)
+{
+	struct dpp_authentication *auth = hapd->dpp_auth;
+	const u8 *dst;
+	unsigned int wait_time, max_wait_time, freq, max_tries, used;
+	struct os_reltime now, diff;
+
+	if (!auth)
+		return -1;
+
+	if (auth->freq_idx == 0)
+		os_get_reltime(&hapd->dpp_init_iter_start);
+
+	if (auth->freq_idx >= auth->num_freq) {
+		auth->num_freq_iters++;
+		if (hapd->dpp_init_max_tries)
+			max_tries = hapd->dpp_init_max_tries;
+		else
+			max_tries = 5;
+		if (auth->num_freq_iters >= max_tries || auth->auth_req_ack) {
+			wpa_printf(MSG_INFO,
+				   "DPP: No response received from responder - stopping initiation attempt");
+			wpa_msg(hapd->msg_ctx, MSG_INFO,
+				DPP_EVENT_AUTH_INIT_FAILED);
+			eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout,
+					     hapd, NULL);
+			hostapd_drv_send_action_cancel_wait(hapd);
+			dpp_auth_deinit(hapd->dpp_auth);
+			hapd->dpp_auth = NULL;
+			return -1;
+		}
+		auth->freq_idx = 0;
+		eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
+		if (hapd->dpp_init_retry_time)
+			wait_time = hapd->dpp_init_retry_time;
+		else
+			wait_time = 10000;
+		os_get_reltime(&now);
+		os_reltime_sub(&now, &hapd->dpp_init_iter_start, &diff);
+		used = diff.sec * 1000 + diff.usec / 1000;
+		if (used > wait_time)
+			wait_time = 0;
+		else
+			wait_time -= used;
+		wpa_printf(MSG_DEBUG, "DPP: Next init attempt in %u ms",
+			   wait_time);
+		eloop_register_timeout(wait_time / 1000,
+				       (wait_time % 1000) * 1000,
+				       hostapd_dpp_init_timeout, hapd,
+				       NULL);
+		return 0;
+	}
+	freq = auth->freq[auth->freq_idx++];
+	auth->curr_freq = freq;
+
+	if (!is_zero_ether_addr(auth->peer_mac_addr))
+		dst = auth->peer_mac_addr;
+	else if (is_zero_ether_addr(auth->peer_bi->mac_addr))
+		dst = broadcast;
+	else
+		dst = auth->peer_bi->mac_addr;
+	hapd->dpp_auth_ok_on_ack = 0;
+	eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
+	wait_time = 2000; /* TODO: hapd->max_remain_on_chan; */
+	max_wait_time = hapd->dpp_resp_wait_time ?
+		hapd->dpp_resp_wait_time : 2000;
+	if (wait_time > max_wait_time)
+		wait_time = max_wait_time;
+	wait_time += 10; /* give the driver some extra time to complete */
+	eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000,
+			       hostapd_dpp_reply_wait_timeout, hapd, NULL);
+	wait_time -= 10;
+	if (auth->neg_freq > 0 && freq != auth->neg_freq) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Initiate on %u MHz and move to neg_freq %u MHz for response",
+			   freq, auth->neg_freq);
+	}
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+		" freq=%u type=%d",
+		MAC2STR(dst), freq, DPP_PA_AUTHENTICATION_REQ);
+	auth->auth_req_ack = 0;
+	os_get_reltime(&hapd->dpp_last_init);
+	return hostapd_drv_send_action(hapd, freq, wait_time,
+				       dst,
+				       wpabuf_head(hapd->dpp_auth->req_msg),
+				       wpabuf_len(hapd->dpp_auth->req_msg));
+}
+
+
+#ifdef CONFIG_DPP2
+static int hostapd_dpp_process_conf_obj(void *ctx,
+				     struct dpp_authentication *auth)
+{
+	struct hostapd_data *hapd = ctx;
+	unsigned int i;
+
+	for (i = 0; i < auth->num_conf_obj; i++)
+		hostapd_dpp_handle_config_obj(hapd, auth,
+					      &auth->conf_obj[i]);
+
+	return 0;
+}
+#endif /* CONFIG_DPP2 */
+
+
+int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd)
+{
+	const char *pos;
+	struct dpp_bootstrap_info *peer_bi, *own_bi = NULL;
+	struct dpp_authentication *auth;
+	u8 allowed_roles = DPP_CAPAB_CONFIGURATOR;
+	unsigned int neg_freq = 0;
+	int tcp = 0;
+#ifdef CONFIG_DPP2
+	int tcp_port = DPP_TCP_PORT;
+	struct hostapd_ip_addr ipaddr;
+	char *addr;
+#endif /* CONFIG_DPP2 */
+
+	pos = os_strstr(cmd, " peer=");
+	if (!pos)
+		return -1;
+	pos += 6;
+	peer_bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp, atoi(pos));
+	if (!peer_bi) {
+		wpa_printf(MSG_INFO,
+			   "DPP: Could not find bootstrapping info for the identified peer");
+		return -1;
+	}
+
+#ifdef CONFIG_DPP2
+	pos = os_strstr(cmd, " tcp_port=");
+	if (pos) {
+		pos += 10;
+		tcp_port = atoi(pos);
+	}
+
+	addr = get_param(cmd, " tcp_addr=");
+	if (addr && os_strcmp(addr, "from-uri") == 0) {
+		os_free(addr);
+		if (!peer_bi->host) {
+			wpa_printf(MSG_INFO,
+				   "DPP: TCP address not available in peer URI");
+			return -1;
+		}
+		tcp = 1;
+		os_memcpy(&ipaddr, peer_bi->host, sizeof(ipaddr));
+		tcp_port = peer_bi->port;
+	} else if (addr) {
+		int res;
+
+		res = hostapd_parse_ip_addr(addr, &ipaddr);
+		os_free(addr);
+		if (res)
+			return -1;
+		tcp = 1;
+	}
+#endif /* CONFIG_DPP2 */
+
+	pos = os_strstr(cmd, " own=");
+	if (pos) {
+		pos += 5;
+		own_bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp,
+					      atoi(pos));
+		if (!own_bi) {
+			wpa_printf(MSG_INFO,
+				   "DPP: Could not find bootstrapping info for the identified local entry");
+			return -1;
+		}
+
+		if (peer_bi->curve != own_bi->curve) {
+			wpa_printf(MSG_INFO,
+				   "DPP: Mismatching curves in bootstrapping info (peer=%s own=%s)",
+				   peer_bi->curve->name, own_bi->curve->name);
+			return -1;
+		}
+	}
+
+	pos = os_strstr(cmd, " role=");
+	if (pos) {
+		pos += 6;
+		if (os_strncmp(pos, "configurator", 12) == 0)
+			allowed_roles = DPP_CAPAB_CONFIGURATOR;
+		else if (os_strncmp(pos, "enrollee", 8) == 0)
+			allowed_roles = DPP_CAPAB_ENROLLEE;
+		else if (os_strncmp(pos, "either", 6) == 0)
+			allowed_roles = DPP_CAPAB_CONFIGURATOR |
+				DPP_CAPAB_ENROLLEE;
+		else
+			goto fail;
+	}
+
+	pos = os_strstr(cmd, " neg_freq=");
+	if (pos)
+		neg_freq = atoi(pos + 10);
+
+	if (!tcp && hapd->dpp_auth) {
+		eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
+		eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout,
+				     hapd, NULL);
+		eloop_cancel_timeout(hostapd_dpp_auth_conf_wait_timeout,
+				     hapd, NULL);
+		eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd,
+				     NULL);
+#ifdef CONFIG_DPP2
+		eloop_cancel_timeout(hostapd_dpp_reconfig_reply_wait_timeout,
+				     hapd, NULL);
+#endif /* CONFIG_DPP2 */
+		hostapd_drv_send_action_cancel_wait(hapd);
+		dpp_auth_deinit(hapd->dpp_auth);
+	}
+
+	auth = dpp_auth_init(hapd->iface->interfaces->dpp, hapd->msg_ctx,
+			     peer_bi, own_bi, allowed_roles, neg_freq,
+			     hapd->iface->hw_features,
+			     hapd->iface->num_hw_features);
+	if (!auth)
+		goto fail;
+	hostapd_dpp_set_testing_options(hapd, auth);
+	if (dpp_set_configurator(auth, cmd) < 0) {
+		dpp_auth_deinit(auth);
+		goto fail;
+	}
+
+	auth->neg_freq = neg_freq;
+
+	if (!is_zero_ether_addr(peer_bi->mac_addr))
+		os_memcpy(auth->peer_mac_addr, peer_bi->mac_addr, ETH_ALEN);
+
+#ifdef CONFIG_DPP2
+	if (tcp)
+		return dpp_tcp_init(hapd->iface->interfaces->dpp, auth,
+				    &ipaddr, tcp_port, hapd->conf->dpp_name,
+				    DPP_NETROLE_AP, hapd->conf->dpp_mud_url,
+				    hapd->conf->dpp_extra_conf_req_name,
+				    hapd->conf->dpp_extra_conf_req_value,
+				    hapd->msg_ctx, hapd,
+				    hostapd_dpp_process_conf_obj, NULL);
+#endif /* CONFIG_DPP2 */
+
+	hapd->dpp_auth = auth;
+	return hostapd_dpp_auth_init_next(hapd);
+fail:
+	return -1;
+}
+
+
+int hostapd_dpp_listen(struct hostapd_data *hapd, const char *cmd)
+{
+	int freq;
+
+	freq = atoi(cmd);
+	if (freq <= 0)
+		return -1;
+
+	if (os_strstr(cmd, " role=configurator"))
+		hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR;
+	else if (os_strstr(cmd, " role=enrollee"))
+		hapd->dpp_allowed_roles = DPP_CAPAB_ENROLLEE;
+	else
+		hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR |
+			DPP_CAPAB_ENROLLEE;
+	hapd->dpp_qr_mutual = os_strstr(cmd, " qr=mutual") != NULL;
+
+	if (freq != hapd->iface->freq && hapd->iface->freq > 0) {
+		/* TODO: Listen operation on non-operating channel */
+		wpa_printf(MSG_INFO,
+			   "DPP: Listen operation on non-operating channel (%d MHz) is not yet supported (operating channel: %d MHz)",
+			   freq, hapd->iface->freq);
+		return -1;
+	}
+
+	hostapd_drv_dpp_listen(hapd, true);
+	return 0;
+}
+
+
+void hostapd_dpp_listen_stop(struct hostapd_data *hapd)
+{
+	hostapd_drv_dpp_listen(hapd, false);
+	/* TODO: Stop listen operation on non-operating channel */
+}
+
+
+#ifdef CONFIG_DPP2
+static void
+hostapd_dpp_relay_needs_controller(struct hostapd_data *hapd, const u8 *src,
+				   enum dpp_public_action_frame_type type)
+{
+	struct os_reltime now;
+
+	if (!hapd->conf->dpp_relay_port)
+		return;
+
+	os_get_reltime(&now);
+	if (hapd->dpp_relay_last_needs_ctrl.sec &&
+	    !os_reltime_expired(&now, &hapd->dpp_relay_last_needs_ctrl, 60))
+		return;
+	hapd->dpp_relay_last_needs_ctrl = now;
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_RELAY_NEEDS_CONTROLLER
+		MACSTR " %u", MAC2STR(src), type);
+}
+#endif /* CONFIG_DPP2 */
+
+
+static void hostapd_dpp_rx_auth_req(struct hostapd_data *hapd, const u8 *src,
+				    const u8 *hdr, const u8 *buf, size_t len,
+				    unsigned int freq)
+{
+	const u8 *r_bootstrap, *i_bootstrap;
+	u16 r_bootstrap_len, i_bootstrap_len;
+	struct dpp_bootstrap_info *own_bi = NULL, *peer_bi = NULL;
+
+	if (!hapd->iface->interfaces->dpp)
+		return;
+
+	wpa_printf(MSG_DEBUG, "DPP: Authentication Request from " MACSTR,
+		   MAC2STR(src));
+
+#ifdef CONFIG_DPP2
+	hostapd_dpp_chirp_stop(hapd);
+#endif /* CONFIG_DPP2 */
+
+	r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+				   &r_bootstrap_len);
+	if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+			"Missing or invalid required Responder Bootstrapping Key Hash attribute");
+		return;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
+		    r_bootstrap, r_bootstrap_len);
+
+	i_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
+				   &i_bootstrap_len);
+	if (!i_bootstrap || i_bootstrap_len != SHA256_MAC_LEN) {
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+			"Missing or invalid required Initiator Bootstrapping Key Hash attribute");
+		return;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Bootstrapping Key Hash",
+		    i_bootstrap, i_bootstrap_len);
+
+	/* Try to find own and peer bootstrapping key matches based on the
+	 * received hash values */
+	dpp_bootstrap_find_pair(hapd->iface->interfaces->dpp, i_bootstrap,
+				r_bootstrap, &own_bi, &peer_bi);
+#ifdef CONFIG_DPP2
+	if (!own_bi) {
+		if (dpp_relay_rx_action(hapd->iface->interfaces->dpp,
+					src, hdr, buf, len, freq, i_bootstrap,
+					r_bootstrap, hapd) == 0)
+			return;
+		hostapd_dpp_relay_needs_controller(hapd, src,
+						   DPP_PA_AUTHENTICATION_REQ);
+	}
+#endif /* CONFIG_DPP2 */
+	if (!own_bi) {
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+			"No matching own bootstrapping key found - ignore message");
+		return;
+	}
+
+	if (own_bi->type == DPP_BOOTSTRAP_PKEX) {
+		if (!peer_bi || peer_bi->type != DPP_BOOTSTRAP_PKEX) {
+			wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+				"No matching peer bootstrapping key found for PKEX - ignore message");
+			return;
+		}
+
+		if (os_memcmp(peer_bi->pubkey_hash, own_bi->peer_pubkey_hash,
+			      SHA256_MAC_LEN) != 0) {
+			wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+				"Mismatching peer PKEX bootstrapping key - ignore message");
+			return;
+		}
+	}
+
+	if (hapd->dpp_auth) {
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+			"Already in DPP authentication exchange - ignore new one");
+		return;
+	}
+
+	hapd->dpp_auth_ok_on_ack = 0;
+	hapd->dpp_auth = dpp_auth_req_rx(hapd->iface->interfaces->dpp,
+					 hapd->msg_ctx, hapd->dpp_allowed_roles,
+					 hapd->dpp_qr_mutual,
+					 peer_bi, own_bi, freq, hdr, buf, len);
+	if (!hapd->dpp_auth) {
+		wpa_printf(MSG_DEBUG, "DPP: No response generated");
+		return;
+	}
+	hostapd_dpp_set_testing_options(hapd, hapd->dpp_auth);
+	if (dpp_set_configurator(hapd->dpp_auth,
+				 hapd->dpp_configurator_params) < 0) {
+		dpp_auth_deinit(hapd->dpp_auth);
+		hapd->dpp_auth = NULL;
+		return;
+	}
+	os_memcpy(hapd->dpp_auth->peer_mac_addr, src, ETH_ALEN);
+
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+		" freq=%u type=%d",
+		MAC2STR(src), hapd->dpp_auth->curr_freq,
+		DPP_PA_AUTHENTICATION_RESP);
+	hostapd_drv_send_action(hapd, hapd->dpp_auth->curr_freq, 0,
+				src, wpabuf_head(hapd->dpp_auth->resp_msg),
+				wpabuf_len(hapd->dpp_auth->resp_msg));
+}
+
+
+static void hostapd_dpp_handle_config_obj(struct hostapd_data *hapd,
+					  struct dpp_authentication *auth,
+					  struct dpp_config_obj *conf)
+{
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_RECEIVED);
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_AKM "%s",
+		dpp_akm_str(conf->akm));
+	if (conf->ssid_len)
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_SSID "%s",
+			wpa_ssid_txt(conf->ssid, conf->ssid_len));
+	if (conf->connector) {
+		/* TODO: Save the Connector and consider using a command
+		 * to fetch the value instead of sending an event with
+		 * it. The Connector could end up being larger than what
+		 * most clients are ready to receive as an event
+		 * message. */
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONNECTOR "%s",
+			conf->connector);
+	}
+	if (conf->passphrase[0]) {
+		char hex[64 * 2 + 1];
+
+		wpa_snprintf_hex(hex, sizeof(hex),
+				 (const u8 *) conf->passphrase,
+				 os_strlen(conf->passphrase));
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_PASS "%s",
+			hex);
+	} else if (conf->psk_set) {
+		char hex[PMK_LEN * 2 + 1];
+
+		wpa_snprintf_hex(hex, sizeof(hex), conf->psk, PMK_LEN);
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_PSK "%s",
+			hex);
+	}
+	if (conf->c_sign_key) {
+		char *hex;
+		size_t hexlen;
+
+		hexlen = 2 * wpabuf_len(conf->c_sign_key) + 1;
+		hex = os_malloc(hexlen);
+		if (hex) {
+			wpa_snprintf_hex(hex, hexlen,
+					 wpabuf_head(conf->c_sign_key),
+					 wpabuf_len(conf->c_sign_key));
+			wpa_msg(hapd->msg_ctx, MSG_INFO,
+				DPP_EVENT_C_SIGN_KEY "%s", hex);
+			os_free(hex);
+		}
+	}
+	if (auth->net_access_key) {
+		char *hex;
+		size_t hexlen;
+
+		hexlen = 2 * wpabuf_len(auth->net_access_key) + 1;
+		hex = os_malloc(hexlen);
+		if (hex) {
+			wpa_snprintf_hex(hex, hexlen,
+					 wpabuf_head(auth->net_access_key),
+					 wpabuf_len(auth->net_access_key));
+			if (auth->net_access_key_expiry)
+				wpa_msg(hapd->msg_ctx, MSG_INFO,
+					DPP_EVENT_NET_ACCESS_KEY "%s %lu", hex,
+					(unsigned long)
+					auth->net_access_key_expiry);
+			else
+				wpa_msg(hapd->msg_ctx, MSG_INFO,
+					DPP_EVENT_NET_ACCESS_KEY "%s", hex);
+			os_free(hex);
+		}
+	}
+}
+
+
+static int hostapd_dpp_handle_key_pkg(struct hostapd_data *hapd,
+				      struct dpp_asymmetric_key *key)
+{
+#ifdef CONFIG_DPP2
+	int res;
+
+	if (!key)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "DPP: Received Configurator backup");
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_RECEIVED);
+
+	while (key) {
+		res = dpp_configurator_from_backup(
+			hapd->iface->interfaces->dpp, key);
+		if (res < 0)
+			return -1;
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFIGURATOR_ID "%d",
+			res);
+		key = key->next;
+	}
+#endif /* CONFIG_DPP2 */
+
+	return 0;
+}
+
+
+#ifdef CONFIG_DPP3
+static void hostapd_dpp_build_new_key(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct dpp_authentication *auth = hapd->dpp_auth;
+
+	if (!auth || !auth->waiting_new_key)
+		return;
+
+	wpa_printf(MSG_DEBUG, "DPP: Build config request with a new key");
+	hostapd_dpp_start_gas_client(hapd);
+}
+#endif /* CONFIG_DPP3 */
+
+
+static void hostapd_dpp_gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token,
+				    enum gas_query_ap_result result,
+				    const struct wpabuf *adv_proto,
+				    const struct wpabuf *resp, u16 status_code)
+{
+	struct hostapd_data *hapd = ctx;
+	const u8 *pos;
+	struct dpp_authentication *auth = hapd->dpp_auth;
+	enum dpp_status_error status = DPP_STATUS_CONFIG_REJECTED;
+	int res;
+
+	if (!auth || !auth->auth_success) {
+		wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
+		return;
+	}
+	if (result != GAS_QUERY_AP_SUCCESS ||
+	    !resp || status_code != WLAN_STATUS_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "DPP: GAS query did not succeed");
+		goto fail;
+	}
+
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: Configuration Response adv_proto",
+			adv_proto);
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: Configuration Response (GAS response)",
+			resp);
+
+	if (wpabuf_len(adv_proto) != 10 ||
+	    !(pos = wpabuf_head(adv_proto)) ||
+	    pos[0] != WLAN_EID_ADV_PROTO ||
+	    pos[1] != 8 ||
+	    pos[3] != WLAN_EID_VENDOR_SPECIFIC ||
+	    pos[4] != 5 ||
+	    WPA_GET_BE24(&pos[5]) != OUI_WFA ||
+	    pos[8] != 0x1a ||
+	    pos[9] != 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Not a DPP Advertisement Protocol ID");
+		goto fail;
+	}
+
+	res = dpp_conf_resp_rx(auth, resp);
+#ifdef CONFIG_DPP3
+	if (res == -3) {
+		wpa_printf(MSG_DEBUG, "DPP: New protocol key needed");
+		eloop_register_timeout(0, 0, hostapd_dpp_build_new_key, hapd,
+				       NULL);
+		return;
+	}
+#endif /* CONFIG_DPP3 */
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Configuration attempt failed");
+		goto fail;
+	}
+
+	hostapd_dpp_handle_config_obj(hapd, auth, &auth->conf_obj[0]);
+	if (hostapd_dpp_handle_key_pkg(hapd, auth->conf_key_pkg) < 0)
+		goto fail;
+
+	status = DPP_STATUS_OK;
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_REJECT_CONFIG) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - Reject Config Object");
+		status = DPP_STATUS_CONFIG_REJECTED;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+fail:
+	if (status != DPP_STATUS_OK)
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
+#ifdef CONFIG_DPP2
+	if (auth->peer_version >= 2 &&
+	    auth->conf_resp_status == DPP_STATUS_OK) {
+		struct wpabuf *msg;
+
+		wpa_printf(MSG_DEBUG, "DPP: Send DPP Configuration Result");
+		msg = dpp_build_conf_result(auth, status);
+		if (!msg)
+			goto fail2;
+
+		wpa_msg(hapd->msg_ctx, MSG_INFO,
+			DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
+			MAC2STR(addr), auth->curr_freq,
+			DPP_PA_CONFIGURATION_RESULT);
+		hostapd_drv_send_action(hapd, auth->curr_freq, 0,
+					addr, wpabuf_head(msg),
+					wpabuf_len(msg));
+		wpabuf_free(msg);
+
+		/* This exchange will be terminated in the TX status handler */
+		auth->connect_on_tx_status = 1;
+		return;
+	}
+fail2:
+#endif /* CONFIG_DPP2 */
+	dpp_auth_deinit(hapd->dpp_auth);
+	hapd->dpp_auth = NULL;
+}
+
+
+static void hostapd_dpp_start_gas_client(struct hostapd_data *hapd)
+{
+	struct dpp_authentication *auth = hapd->dpp_auth;
+	struct wpabuf *buf;
+	int res;
+
+	buf = dpp_build_conf_req_helper(auth, hapd->conf->dpp_name,
+					DPP_NETROLE_AP,
+					hapd->conf->dpp_mud_url, NULL,
+					hapd->conf->dpp_extra_conf_req_name,
+					hapd->conf->dpp_extra_conf_req_value);
+	if (!buf) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No configuration request data available");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "DPP: GAS request to " MACSTR " (freq %u MHz)",
+		   MAC2STR(auth->peer_mac_addr), auth->curr_freq);
+
+	res = gas_query_ap_req(hapd->gas, auth->peer_mac_addr, auth->curr_freq,
+			       buf, hostapd_dpp_gas_resp_cb, hapd);
+	if (res < 0) {
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+			"GAS: Failed to send Query Request");
+		wpabuf_free(buf);
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: GAS query started with dialog token %u", res);
+	}
+}
+
+
+static void hostapd_dpp_auth_success(struct hostapd_data *hapd, int initiator)
+{
+	wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded");
+	dpp_notify_auth_success(hapd->dpp_auth, initiator);
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
+		wpa_printf(MSG_INFO,
+			   "DPP: TESTING - stop at Authentication Confirm");
+		if (hapd->dpp_auth->configurator) {
+			/* Prevent GAS response */
+			hapd->dpp_auth->auth_success = 0;
+		}
+		return;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	if (!hapd->dpp_auth->configurator)
+		hostapd_dpp_start_gas_client(hapd);
+}
+
+
+static void hostapd_dpp_rx_auth_resp(struct hostapd_data *hapd, const u8 *src,
+				     const u8 *hdr, const u8 *buf, size_t len,
+				     unsigned int freq)
+{
+	struct dpp_authentication *auth = hapd->dpp_auth;
+	struct wpabuf *msg;
+
+	wpa_printf(MSG_DEBUG, "DPP: Authentication Response from " MACSTR,
+		   MAC2STR(src));
+
+	if (!auth) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No DPP Authentication in progress - drop");
+		return;
+	}
+
+	if (!is_zero_ether_addr(auth->peer_mac_addr) &&
+	    os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
+			   MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
+		return;
+	}
+
+	eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
+
+	if (auth->curr_freq != freq && auth->neg_freq == freq) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Responder accepted request for different negotiation channel");
+		auth->curr_freq = freq;
+	}
+
+	eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
+	msg = dpp_auth_resp_rx(auth, hdr, buf, len);
+	if (!msg) {
+		if (auth->auth_resp_status == DPP_STATUS_RESPONSE_PENDING) {
+			wpa_printf(MSG_DEBUG, "DPP: Wait for full response");
+			return;
+		}
+		wpa_printf(MSG_DEBUG, "DPP: No confirm generated");
+		return;
+	}
+	os_memcpy(auth->peer_mac_addr, src, ETH_ALEN);
+
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+		" freq=%u type=%d", MAC2STR(src), auth->curr_freq,
+		DPP_PA_AUTHENTICATION_CONF);
+	hostapd_drv_send_action(hapd, auth->curr_freq, 0, src,
+				wpabuf_head(msg), wpabuf_len(msg));
+	wpabuf_free(msg);
+	hapd->dpp_auth_ok_on_ack = 1;
+}
+
+
+static void hostapd_dpp_rx_auth_conf(struct hostapd_data *hapd, const u8 *src,
+				     const u8 *hdr, const u8 *buf, size_t len)
+{
+	struct dpp_authentication *auth = hapd->dpp_auth;
+
+	wpa_printf(MSG_DEBUG, "DPP: Authentication Confirmation from " MACSTR,
+		   MAC2STR(src));
+
+	if (!auth) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No DPP Authentication in progress - drop");
+		return;
+	}
+
+	if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
+			   MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
+		return;
+	}
+
+	if (dpp_auth_conf_rx(auth, hdr, buf, len) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Authentication failed");
+		return;
+	}
+
+	hostapd_dpp_auth_success(hapd, 0);
+}
+
+
+#ifdef CONFIG_DPP2
+
+static void hostapd_dpp_config_result_wait_timeout(void *eloop_ctx,
+						   void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct dpp_authentication *auth = hapd->dpp_auth;
+
+	if (!auth || !auth->waiting_conf_result)
+		return;
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Timeout while waiting for Configuration Result");
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
+	dpp_auth_deinit(auth);
+	hapd->dpp_auth = NULL;
+}
+
+
+static void hostapd_dpp_conn_status_result_wait_timeout(void *eloop_ctx,
+							void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct dpp_authentication *auth = hapd->dpp_auth;
+
+	if (!auth || !auth->waiting_conf_result)
+		return;
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Timeout while waiting for Connection Status Result");
+	wpa_msg(hapd->msg_ctx, MSG_INFO,
+		DPP_EVENT_CONN_STATUS_RESULT "timeout");
+	dpp_auth_deinit(auth);
+	hapd->dpp_auth = NULL;
+}
+
+
+#ifdef CONFIG_DPP3
+
+static bool hostapd_dpp_pb_active(struct hostapd_data *hapd)
+{
+	struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+
+	return ifaces && (ifaces->dpp_pb_time.sec ||
+			  ifaces->dpp_pb_time.usec);
+}
+
+
+static void hostapd_dpp_remove_pb_hash(struct hostapd_data *hapd)
+{
+	struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+	int i;
+
+	if (!ifaces->dpp_pb_bi)
+		return;
+	for (i = 0; i < DPP_PB_INFO_COUNT; i++) {
+		struct dpp_pb_info *info = &ifaces->dpp_pb[i];
+
+		if (info->rx_time.sec == 0 && info->rx_time.usec == 0)
+			continue;
+		if (os_memcmp(info->hash, ifaces->dpp_pb_resp_hash,
+			      SHA256_MAC_LEN) == 0) {
+			/* Allow a new push button session to be established
+			 * immediately without the successfully completed
+			 * session triggering session overlap. */
+			info->rx_time.sec = 0;
+			info->rx_time.usec = 0;
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Removed PB hash from session overlap detection due to successfully completed provisioning");
+		}
+	}
+}
+
+#endif /* CONFIG_DPP3 */
+
+
+static void hostapd_dpp_rx_conf_result(struct hostapd_data *hapd, const u8 *src,
+				       const u8 *hdr, const u8 *buf, size_t len)
+{
+	struct dpp_authentication *auth = hapd->dpp_auth;
+	enum dpp_status_error status;
+#ifdef CONFIG_DPP3
+	struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+#endif /* CONFIG_DPP3 */
+
+	wpa_printf(MSG_DEBUG, "DPP: Configuration Result from " MACSTR,
+		   MAC2STR(src));
+
+	if (!auth || !auth->waiting_conf_result) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No DPP Configuration waiting for result - drop");
+		return;
+	}
+
+	if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
+			   MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
+		return;
+	}
+
+	status = dpp_conf_result_rx(auth, hdr, buf, len);
+
+	if (status == DPP_STATUS_OK && auth->send_conn_status) {
+		wpa_msg(hapd->msg_ctx, MSG_INFO,
+			DPP_EVENT_CONF_SENT "wait_conn_status=1 conf_status=%d",
+			auth->conf_resp_status);
+		wpa_printf(MSG_DEBUG, "DPP: Wait for Connection Status Result");
+		eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout,
+				     hapd, NULL);
+		auth->waiting_conn_status_result = 1;
+		eloop_cancel_timeout(
+			hostapd_dpp_conn_status_result_wait_timeout,
+			hapd, NULL);
+		eloop_register_timeout(
+			16, 0, hostapd_dpp_conn_status_result_wait_timeout,
+			hapd, NULL);
+		return;
+	}
+	hostapd_drv_send_action_cancel_wait(hapd);
+	hostapd_dpp_listen_stop(hapd);
+	if (status == DPP_STATUS_OK)
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT
+			"conf_status=%d", auth->conf_resp_status);
+	else
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
+	dpp_auth_deinit(auth);
+	hapd->dpp_auth = NULL;
+	eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout, hapd,
+			     NULL);
+#ifdef CONFIG_DPP3
+	if (!ifaces->dpp_pb_result_indicated && hostapd_dpp_pb_active(hapd)) {
+		if (status == DPP_STATUS_OK)
+			wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_PB_RESULT
+				"success");
+		else
+			wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_PB_RESULT
+				"no-configuration-available");
+		ifaces->dpp_pb_result_indicated = true;
+		if (status == DPP_STATUS_OK)
+			hostapd_dpp_remove_pb_hash(hapd);
+		hostapd_dpp_push_button_stop(hapd);
+	}
+#endif /* CONFIG_DPP3 */
+}
+
+
+static void hostapd_dpp_rx_conn_status_result(struct hostapd_data *hapd,
+					      const u8 *src, const u8 *hdr,
+					      const u8 *buf, size_t len)
+{
+	struct dpp_authentication *auth = hapd->dpp_auth;
+	enum dpp_status_error status;
+	u8 ssid[SSID_MAX_LEN];
+	size_t ssid_len = 0;
+	char *channel_list = NULL;
+
+	wpa_printf(MSG_DEBUG, "DPP: Connection Status Result");
+
+	if (!auth || !auth->waiting_conn_status_result) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No DPP Configuration waiting for connection status result - drop");
+		return;
+	}
+
+	status = dpp_conn_status_result_rx(auth, hdr, buf, len,
+					   ssid, &ssid_len, &channel_list);
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONN_STATUS_RESULT
+		"result=%d ssid=%s channel_list=%s",
+		status, wpa_ssid_txt(ssid, ssid_len),
+		channel_list ? channel_list : "N/A");
+	os_free(channel_list);
+	hostapd_drv_send_action_cancel_wait(hapd);
+	hostapd_dpp_listen_stop(hapd);
+	dpp_auth_deinit(auth);
+	hapd->dpp_auth = NULL;
+	eloop_cancel_timeout(hostapd_dpp_conn_status_result_wait_timeout,
+			     hapd, NULL);
+}
+
+
+static void
+hostapd_dpp_rx_presence_announcement(struct hostapd_data *hapd, const u8 *src,
+				     const u8 *hdr, const u8 *buf, size_t len,
+				     unsigned int freq)
+{
+	const u8 *r_bootstrap;
+	u16 r_bootstrap_len;
+	struct dpp_bootstrap_info *peer_bi;
+	struct dpp_authentication *auth;
+
+	wpa_printf(MSG_DEBUG, "DPP: Presence Announcement from " MACSTR,
+		   MAC2STR(src));
+
+	r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+				   &r_bootstrap_len);
+	if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+			"Missing or invalid required Responder Bootstrapping Key Hash attribute");
+		return;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
+		    r_bootstrap, r_bootstrap_len);
+	peer_bi = dpp_bootstrap_find_chirp(hapd->iface->interfaces->dpp,
+					   r_bootstrap);
+	dpp_notify_chirp_received(hapd->msg_ctx,
+				  peer_bi ? (int) peer_bi->id : -1,
+				  src, freq, r_bootstrap);
+	if (!peer_bi) {
+		if (dpp_relay_rx_action(hapd->iface->interfaces->dpp,
+					src, hdr, buf, len, freq, NULL,
+					r_bootstrap, hapd) == 0)
+			return;
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No matching bootstrapping information found");
+		hostapd_dpp_relay_needs_controller(
+			hapd, src, DPP_PA_PRESENCE_ANNOUNCEMENT);
+		return;
+	}
+
+	if (hapd->dpp_auth) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Ignore Presence Announcement during ongoing Authentication");
+		return;
+	}
+
+	auth = dpp_auth_init(hapd->iface->interfaces->dpp, hapd->msg_ctx,
+			     peer_bi, NULL, DPP_CAPAB_CONFIGURATOR, freq, NULL,
+			     0);
+	if (!auth)
+		return;
+	hostapd_dpp_set_testing_options(hapd, auth);
+	if (dpp_set_configurator(auth,
+				 hapd->dpp_configurator_params) < 0) {
+		dpp_auth_deinit(auth);
+		return;
+	}
+
+	auth->neg_freq = freq;
+
+	/* The source address of the Presence Announcement frame overrides any
+	 * MAC address information from the bootstrapping information. */
+	os_memcpy(auth->peer_mac_addr, src, ETH_ALEN);
+
+	hapd->dpp_auth = auth;
+	if (hostapd_dpp_auth_init_next(hapd) < 0) {
+		dpp_auth_deinit(hapd->dpp_auth);
+		hapd->dpp_auth = NULL;
+	}
+}
+
+
+static void hostapd_dpp_reconfig_reply_wait_timeout(void *eloop_ctx,
+						    void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct dpp_authentication *auth = hapd->dpp_auth;
+
+	if (!auth)
+		return;
+
+	wpa_printf(MSG_DEBUG, "DPP: Reconfig Reply wait timeout");
+	hostapd_dpp_listen_stop(hapd);
+	dpp_auth_deinit(auth);
+	hapd->dpp_auth = NULL;
+}
+
+
+static void
+hostapd_dpp_rx_reconfig_announcement(struct hostapd_data *hapd, const u8 *src,
+				     const u8 *hdr, const u8 *buf, size_t len,
+				     unsigned int freq)
+{
+	const u8 *csign_hash, *fcgroup, *a_nonce, *e_id;
+	u16 csign_hash_len, fcgroup_len, a_nonce_len, e_id_len;
+	struct dpp_configurator *conf;
+	struct dpp_authentication *auth;
+	unsigned int wait_time, max_wait_time;
+	u16 group;
+
+	if (hapd->dpp_auth) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Ignore Reconfig Announcement during ongoing Authentication");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "DPP: Reconfig Announcement from " MACSTR,
+		   MAC2STR(src));
+
+	csign_hash = dpp_get_attr(buf, len, DPP_ATTR_C_SIGN_KEY_HASH,
+				  &csign_hash_len);
+	if (!csign_hash || csign_hash_len != SHA256_MAC_LEN) {
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+			"Missing or invalid required Configurator C-sign key Hash attribute");
+		return;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Configurator C-sign key Hash (kid)",
+		    csign_hash, csign_hash_len);
+	conf = dpp_configurator_find_kid(hapd->iface->interfaces->dpp,
+					 csign_hash);
+	if (!conf) {
+		if (dpp_relay_rx_action(hapd->iface->interfaces->dpp,
+					src, hdr, buf, len, freq, NULL,
+					NULL, hapd) == 0)
+			return;
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No matching Configurator information found");
+		hostapd_dpp_relay_needs_controller(
+			hapd, src, DPP_PA_RECONFIG_ANNOUNCEMENT);
+		return;
+	}
+
+	fcgroup = dpp_get_attr(buf, len, DPP_ATTR_FINITE_CYCLIC_GROUP,
+			       &fcgroup_len);
+	if (!fcgroup || fcgroup_len != 2) {
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+			"Missing or invalid required Finite Cyclic Group attribute");
+		return;
+	}
+	group = WPA_GET_LE16(fcgroup);
+	wpa_printf(MSG_DEBUG, "DPP: Enrollee finite cyclic group: %u", group);
+
+	a_nonce = dpp_get_attr(buf, len, DPP_ATTR_A_NONCE, &a_nonce_len);
+	e_id = dpp_get_attr(buf, len, DPP_ATTR_E_PRIME_ID, &e_id_len);
+
+	auth = dpp_reconfig_init(hapd->iface->interfaces->dpp, hapd->msg_ctx,
+				 conf, freq, group, a_nonce, a_nonce_len,
+				 e_id, e_id_len);
+	if (!auth)
+		return;
+	hostapd_dpp_set_testing_options(hapd, auth);
+	if (dpp_set_configurator(auth, hapd->dpp_configurator_params) < 0) {
+		dpp_auth_deinit(auth);
+		return;
+	}
+
+	os_memcpy(auth->peer_mac_addr, src, ETH_ALEN);
+	hapd->dpp_auth = auth;
+
+	hapd->dpp_in_response_listen = 0;
+	hapd->dpp_auth_ok_on_ack = 0;
+	wait_time = 2000; /* TODO: hapd->max_remain_on_chan; */
+	max_wait_time = hapd->dpp_resp_wait_time ?
+		hapd->dpp_resp_wait_time : 2000;
+	if (wait_time > max_wait_time)
+		wait_time = max_wait_time;
+	wait_time += 10; /* give the driver some extra time to complete */
+	eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000,
+			       hostapd_dpp_reconfig_reply_wait_timeout,
+			       hapd, NULL);
+	wait_time -= 10;
+
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+		" freq=%u type=%d",
+		MAC2STR(src), freq, DPP_PA_RECONFIG_AUTH_REQ);
+	if (hostapd_drv_send_action(hapd, freq, wait_time, src,
+				    wpabuf_head(auth->reconfig_req_msg),
+				    wpabuf_len(auth->reconfig_req_msg)) < 0) {
+		dpp_auth_deinit(hapd->dpp_auth);
+		hapd->dpp_auth = NULL;
+	}
+}
+
+
+static void
+hostapd_dpp_rx_reconfig_auth_resp(struct hostapd_data *hapd, const u8 *src,
+				  const u8 *hdr, const u8 *buf, size_t len,
+				  unsigned int freq)
+{
+	struct dpp_authentication *auth = hapd->dpp_auth;
+	struct wpabuf *conf;
+
+	wpa_printf(MSG_DEBUG, "DPP: Reconfig Authentication Response from "
+		   MACSTR, MAC2STR(src));
+
+	if (!auth || !auth->reconfig || !auth->configurator) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No DPP Reconfig Authentication in progress - drop");
+		return;
+	}
+
+	if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
+			   MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
+		return;
+	}
+
+	conf = dpp_reconfig_auth_resp_rx(auth, hdr, buf, len);
+	if (!conf)
+		return;
+
+	eloop_cancel_timeout(hostapd_dpp_reconfig_reply_wait_timeout,
+			     hapd, NULL);
+
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+		" freq=%u type=%d",
+		MAC2STR(src), freq, DPP_PA_RECONFIG_AUTH_CONF);
+	if (hostapd_drv_send_action(hapd, freq, 500, src,
+				    wpabuf_head(conf), wpabuf_len(conf)) < 0) {
+		wpabuf_free(conf);
+		dpp_auth_deinit(hapd->dpp_auth);
+		hapd->dpp_auth = NULL;
+		return;
+	}
+	wpabuf_free(conf);
+}
+
+#endif /* CONFIG_DPP2 */
+
+
+static void hostapd_dpp_send_peer_disc_resp(struct hostapd_data *hapd,
+					    const u8 *src, unsigned int freq,
+					    u8 trans_id,
+					    enum dpp_status_error status)
+{
+	struct wpabuf *msg;
+	size_t len;
+
+	len = 5 + 5 + 4 + os_strlen(hapd->conf->dpp_connector);
+#ifdef CONFIG_DPP2
+	len += 5;
+#endif /* CONFIG_DPP2 */
+	msg = dpp_alloc_msg(DPP_PA_PEER_DISCOVERY_RESP, len);
+	if (!msg)
+		return;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_NO_TRANSACTION_ID_PEER_DISC_RESP) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - no Transaction ID");
+		goto skip_trans_id;
+	}
+	if (dpp_test == DPP_TEST_INVALID_TRANSACTION_ID_PEER_DISC_RESP) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - invalid Transaction ID");
+		trans_id ^= 0x01;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	/* Transaction ID */
+	wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID);
+	wpabuf_put_le16(msg, 1);
+	wpabuf_put_u8(msg, trans_id);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_trans_id:
+	if (dpp_test == DPP_TEST_NO_STATUS_PEER_DISC_RESP) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
+		goto skip_status;
+	}
+	if (dpp_test == DPP_TEST_INVALID_STATUS_PEER_DISC_RESP) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
+		status = 254;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	/* DPP Status */
+	wpabuf_put_le16(msg, DPP_ATTR_STATUS);
+	wpabuf_put_le16(msg, 1);
+	wpabuf_put_u8(msg, status);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_status:
+	if (dpp_test == DPP_TEST_NO_CONNECTOR_PEER_DISC_RESP) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - no Connector");
+		goto skip_connector;
+	}
+	if (status == DPP_STATUS_OK &&
+	    dpp_test == DPP_TEST_INVALID_CONNECTOR_PEER_DISC_RESP) {
+		char *connector;
+
+		wpa_printf(MSG_INFO, "DPP: TESTING - invalid Connector");
+		connector = dpp_corrupt_connector_signature(
+			hapd->conf->dpp_connector);
+		if (!connector) {
+			wpabuf_free(msg);
+			return;
+		}
+		wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR);
+		wpabuf_put_le16(msg, os_strlen(connector));
+		wpabuf_put_str(msg, connector);
+		os_free(connector);
+		goto skip_connector;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	/* DPP Connector */
+	if (status == DPP_STATUS_OK) {
+		wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR);
+		wpabuf_put_le16(msg, os_strlen(hapd->conf->dpp_connector));
+		wpabuf_put_str(msg, hapd->conf->dpp_connector);
+	}
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_connector:
+	if (dpp_test == DPP_TEST_NO_PROTOCOL_VERSION_PEER_DISC_RESP) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - no Protocol Version");
+		goto skip_proto_ver;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#ifdef CONFIG_DPP2
+	if (DPP_VERSION > 1) {
+		u8 ver = DPP_VERSION;
+#ifdef CONFIG_DPP3
+		int conn_ver;
+
+		conn_ver = dpp_get_connector_version(hapd->conf->dpp_connector);
+		if (conn_ver > 0 && ver != conn_ver) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Use Connector version %d instead of current protocol version %d",
+				   conn_ver, ver);
+			ver = conn_ver;
+		}
+#endif /* CONFIG_DPP3 */
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (dpp_test == DPP_TEST_INVALID_PROTOCOL_VERSION_PEER_DISC_RESP) {
+		wpa_printf(MSG_INFO, "DPP: TESTING - invalid Protocol Version");
+		ver = 1;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+		/* Protocol Version */
+		wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
+		wpabuf_put_le16(msg, 1);
+		wpabuf_put_u8(msg, ver);
+	}
+#endif /* CONFIG_DPP2 */
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_proto_ver:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	wpa_printf(MSG_DEBUG, "DPP: Send Peer Discovery Response to " MACSTR
+		   " status=%d", MAC2STR(src), status);
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+		" freq=%u type=%d status=%d", MAC2STR(src), freq,
+		DPP_PA_PEER_DISCOVERY_RESP, status);
+	hostapd_drv_send_action(hapd, freq, 0, src,
+				wpabuf_head(msg), wpabuf_len(msg));
+	wpabuf_free(msg);
+}
+
+
+static bool hapd_dpp_connector_available(struct hostapd_data *hapd)
+{
+	if (!hapd->wpa_auth ||
+	    !(hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) ||
+	    !(hapd->conf->wpa & WPA_PROTO_RSN)) {
+		wpa_printf(MSG_DEBUG, "DPP: DPP AKM not in use");
+		return false;
+	}
+
+	if (!hapd->conf->dpp_connector || !hapd->conf->dpp_netaccesskey ||
+	    !hapd->conf->dpp_csign) {
+		wpa_printf(MSG_DEBUG, "DPP: No own Connector/keys set");
+		return false;
+	}
+
+	return true;
+}
+
+
+static void hostapd_dpp_rx_peer_disc_req(struct hostapd_data *hapd,
+					 const u8 *src,
+					 const u8 *buf, size_t len,
+					 unsigned int freq)
+{
+	const u8 *connector, *trans_id;
+	u16 connector_len, trans_id_len;
+	struct os_time now;
+	struct dpp_introduction intro;
+	os_time_t expire;
+	int expiration;
+	enum dpp_status_error res;
+	u8 pkhash[SHA256_MAC_LEN];
+
+	os_memset(&intro, 0, sizeof(intro));
+
+	wpa_printf(MSG_DEBUG, "DPP: Peer Discovery Request from " MACSTR,
+		   MAC2STR(src));
+	if (!hapd_dpp_connector_available(hapd))
+		return;
+
+	os_get_time(&now);
+
+	if (hapd->conf->dpp_netaccesskey_expiry &&
+	    (os_time_t) hapd->conf->dpp_netaccesskey_expiry < now.sec) {
+		wpa_printf(MSG_INFO, "DPP: Own netAccessKey expired");
+		return;
+	}
+
+	trans_id = dpp_get_attr(buf, len, DPP_ATTR_TRANSACTION_ID,
+			       &trans_id_len);
+	if (!trans_id || trans_id_len != 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Peer did not include Transaction ID");
+		return;
+	}
+
+	connector = dpp_get_attr(buf, len, DPP_ATTR_CONNECTOR, &connector_len);
+	if (!connector) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Peer did not include its Connector");
+		return;
+	}
+
+	res = dpp_peer_intro(&intro, hapd->conf->dpp_connector,
+			     wpabuf_head(hapd->conf->dpp_netaccesskey),
+			     wpabuf_len(hapd->conf->dpp_netaccesskey),
+			     wpabuf_head(hapd->conf->dpp_csign),
+			     wpabuf_len(hapd->conf->dpp_csign),
+			     connector, connector_len, &expire, pkhash);
+	if (res == 255) {
+		wpa_printf(MSG_INFO,
+			   "DPP: Network Introduction protocol resulted in internal failure (peer "
+			   MACSTR ")", MAC2STR(src));
+		goto done;
+	}
+	if (res != DPP_STATUS_OK) {
+		wpa_printf(MSG_INFO,
+			   "DPP: Network Introduction protocol resulted in failure (peer "
+			   MACSTR " status %d)", MAC2STR(src), res);
+		hostapd_dpp_send_peer_disc_resp(hapd, src, freq, trans_id[0],
+						res);
+		goto done;
+	}
+
+#ifdef CONFIG_DPP3
+	if (intro.peer_version && intro.peer_version >= 2) {
+		const u8 *version;
+		u16 version_len;
+		u8 attr_version = 1;
+
+		version = dpp_get_attr(buf, len, DPP_ATTR_PROTOCOL_VERSION,
+				       &version_len);
+		if (version && version_len >= 1)
+			attr_version = version[0];
+		if (attr_version != intro.peer_version) {
+			wpa_printf(MSG_INFO,
+				   "DPP: Protocol version mismatch (Connector: %d Attribute: %d",
+				   intro.peer_version, attr_version);
+			hostapd_dpp_send_peer_disc_resp(hapd, src, freq,
+							trans_id[0],
+							DPP_STATUS_NO_MATCH);
+			goto done;
+		}
+	}
+#endif /* CONFIG_DPP3 */
+
+	if (!expire || (os_time_t) hapd->conf->dpp_netaccesskey_expiry < expire)
+		expire = hapd->conf->dpp_netaccesskey_expiry;
+	if (expire)
+		expiration = expire - now.sec;
+	else
+		expiration = 0;
+
+	if (wpa_auth_pmksa_add3(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
+				intro.pmkid, expiration,
+				WPA_KEY_MGMT_DPP, pkhash) < 0) {
+		wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry");
+		goto done;
+	}
+
+	hostapd_dpp_send_peer_disc_resp(hapd, src, freq, trans_id[0],
+					DPP_STATUS_OK);
+done:
+	dpp_peer_intro_deinit(&intro);
+}
+
+
+static void
+hostapd_dpp_rx_pkex_exchange_req(struct hostapd_data *hapd, const u8 *src,
+				 const u8 *hdr, const u8 *buf, size_t len,
+				 unsigned int freq, bool v2)
+{
+	struct wpabuf *msg;
+
+	wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Request from " MACSTR,
+		   MAC2STR(src));
+
+	if (hapd->dpp_pkex_ver == PKEX_VER_ONLY_1 && v2) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Ignore PKEXv2 Exchange Request when configured to be PKEX v1 only");
+		return;
+	}
+	if (hapd->dpp_pkex_ver == PKEX_VER_ONLY_2 && !v2) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Ignore PKEXv1 Exchange Request when configured to be PKEX v2 only");
+		return;
+	}
+
+	/* TODO: Support multiple PKEX codes by iterating over all the enabled
+	 * values here */
+
+	if (!hapd->dpp_pkex_code || !hapd->dpp_pkex_bi) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No PKEX code configured - ignore request");
+		goto try_relay;
+	}
+
+#ifdef CONFIG_DPP2
+	if (dpp_controller_is_own_pkex_req(hapd->iface->interfaces->dpp,
+					   buf, len)) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: PKEX Exchange Request is from local Controller - ignore request");
+		return;
+	}
+#endif /* CONFIG_DPP2 */
+
+	if (hapd->dpp_pkex) {
+		/* TODO: Support parallel operations */
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Already in PKEX session - ignore new request");
+		goto try_relay;
+	}
+
+	hapd->dpp_pkex = dpp_pkex_rx_exchange_req(hapd->msg_ctx,
+						  hapd->dpp_pkex_bi,
+						  hapd->own_addr, src,
+						  hapd->dpp_pkex_identifier,
+						  hapd->dpp_pkex_code,
+						  hapd->dpp_pkex_code_len,
+						  buf, len, v2);
+	if (!hapd->dpp_pkex) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Failed to process the request - ignore it");
+		goto try_relay;
+	}
+
+	msg = hapd->dpp_pkex->exchange_resp;
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+		" freq=%u type=%d", MAC2STR(src), freq,
+		DPP_PA_PKEX_EXCHANGE_RESP);
+	hostapd_drv_send_action(hapd, freq, 0, src,
+				wpabuf_head(msg), wpabuf_len(msg));
+	if (hapd->dpp_pkex->failed) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Terminate PKEX exchange due to an earlier error");
+		if (hapd->dpp_pkex->t > hapd->dpp_pkex->own_bi->pkex_t)
+			hapd->dpp_pkex->own_bi->pkex_t = hapd->dpp_pkex->t;
+		dpp_pkex_free(hapd->dpp_pkex);
+		hapd->dpp_pkex = NULL;
+	}
+
+	return;
+
+try_relay:
+#ifdef CONFIG_DPP2
+	if (v2 && dpp_relay_rx_action(hapd->iface->interfaces->dpp,
+				      src, hdr, buf, len, freq, NULL, NULL,
+				      hapd) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No Relay available for the message");
+		hostapd_dpp_relay_needs_controller(hapd, src,
+						   DPP_PA_PKEX_EXCHANGE_REQ);
+	}
+#else /* CONFIG_DPP2 */
+	wpa_printf(MSG_DEBUG, "DPP: No relay functionality included - skip");
+#endif /* CONFIG_DPP2 */
+}
+
+
+static void
+hostapd_dpp_rx_pkex_exchange_resp(struct hostapd_data *hapd, const u8 *src,
+				  const u8 *buf, size_t len, unsigned int freq)
+{
+	struct wpabuf *msg;
+
+	wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Response from " MACSTR,
+		   MAC2STR(src));
+
+	/* TODO: Support multiple PKEX codes by iterating over all the enabled
+	 * values here */
+
+	if (!hapd->dpp_pkex || !hapd->dpp_pkex->initiator ||
+	    hapd->dpp_pkex->exchange_done) {
+		wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session");
+		return;
+	}
+
+	eloop_cancel_timeout(hostapd_dpp_pkex_retry_timeout, hapd, NULL);
+	hapd->dpp_pkex->exch_req_wait_time = 0;
+
+	msg = dpp_pkex_rx_exchange_resp(hapd->dpp_pkex, src, buf, len);
+	if (!msg) {
+		wpa_printf(MSG_DEBUG, "DPP: Failed to process the response");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "DPP: Send PKEX Commit-Reveal Request to " MACSTR,
+		   MAC2STR(src));
+
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+		" freq=%u type=%d", MAC2STR(src), freq,
+		DPP_PA_PKEX_COMMIT_REVEAL_REQ);
+	hostapd_drv_send_action(hapd, freq, 0, src,
+				wpabuf_head(msg), wpabuf_len(msg));
+	wpabuf_free(msg);
+}
+
+
+static void
+hostapd_dpp_rx_pkex_commit_reveal_req(struct hostapd_data *hapd, const u8 *src,
+				      const u8 *hdr, const u8 *buf, size_t len,
+				      unsigned int freq)
+{
+	struct wpabuf *msg;
+	struct dpp_pkex *pkex = hapd->dpp_pkex;
+	struct dpp_bootstrap_info *bi;
+
+	wpa_printf(MSG_DEBUG, "DPP: PKEX Commit-Reveal Request from " MACSTR,
+		   MAC2STR(src));
+
+	if (!pkex || pkex->initiator || !pkex->exchange_done) {
+		wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session");
+		return;
+	}
+
+	msg = dpp_pkex_rx_commit_reveal_req(pkex, hdr, buf, len);
+	if (!msg) {
+		wpa_printf(MSG_DEBUG, "DPP: Failed to process the request");
+		if (hapd->dpp_pkex->failed) {
+			wpa_printf(MSG_DEBUG, "DPP: Terminate PKEX exchange");
+			if (hapd->dpp_pkex->t > hapd->dpp_pkex->own_bi->pkex_t)
+				hapd->dpp_pkex->own_bi->pkex_t =
+					hapd->dpp_pkex->t;
+			dpp_pkex_free(hapd->dpp_pkex);
+			hapd->dpp_pkex = NULL;
+		}
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "DPP: Send PKEX Commit-Reveal Response to "
+		   MACSTR, MAC2STR(src));
+
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+		" freq=%u type=%d", MAC2STR(src), freq,
+		DPP_PA_PKEX_COMMIT_REVEAL_RESP);
+	hostapd_drv_send_action(hapd, freq, 0, src,
+				wpabuf_head(msg), wpabuf_len(msg));
+	wpabuf_free(msg);
+
+	hostapd_dpp_pkex_clear_code(hapd);
+	bi = dpp_pkex_finish(hapd->iface->interfaces->dpp, pkex, src, freq);
+	if (!bi)
+		return;
+	hapd->dpp_pkex = NULL;
+}
+
+
+static void
+hostapd_dpp_rx_pkex_commit_reveal_resp(struct hostapd_data *hapd, const u8 *src,
+				       const u8 *hdr, const u8 *buf, size_t len,
+				       unsigned int freq)
+{
+	struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+	int res;
+	struct dpp_bootstrap_info *bi;
+	struct dpp_pkex *pkex = hapd->dpp_pkex;
+	char cmd[500];
+
+	wpa_printf(MSG_DEBUG, "DPP: PKEX Commit-Reveal Response from " MACSTR,
+		   MAC2STR(src));
+
+	if (!pkex || !pkex->initiator || !pkex->exchange_done) {
+		wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session");
+		return;
+	}
+
+	res = dpp_pkex_rx_commit_reveal_resp(pkex, hdr, buf, len);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Failed to process the response");
+		return;
+	}
+
+	hostapd_dpp_pkex_clear_code(hapd);
+	bi = dpp_pkex_finish(ifaces->dpp, pkex, src, freq);
+	if (!bi)
+		return;
+	hapd->dpp_pkex = NULL;
+
+#ifdef CONFIG_DPP3
+	if (ifaces->dpp_pb_bi &&
+	    os_memcmp(bi->pubkey_hash_chirp, ifaces->dpp_pb_resp_hash,
+		      SHA256_MAC_LEN) != 0) {
+		char id[20];
+
+		wpa_printf(MSG_INFO,
+			   "DPP: Peer bootstrap key from PKEX does not match PB announcement hash");
+		wpa_hexdump(MSG_DEBUG,
+			    "DPP: Peer provided bootstrap key hash(chirp) from PB PKEX",
+			    bi->pubkey_hash_chirp, SHA256_MAC_LEN);
+		wpa_hexdump(MSG_DEBUG,
+			    "DPP: Peer provided bootstrap key hash(chirp) from PB announcement",
+			    ifaces->dpp_pb_resp_hash, SHA256_MAC_LEN);
+
+		os_snprintf(id, sizeof(id), "%u", bi->id);
+		dpp_bootstrap_remove(ifaces->dpp, id);
+		hostapd_dpp_push_button_stop(hapd);
+		return;
+	}
+#endif /* CONFIG_DPP3 */
+
+	os_snprintf(cmd, sizeof(cmd), " peer=%u %s",
+		    bi->id,
+		    hapd->dpp_pkex_auth_cmd ? hapd->dpp_pkex_auth_cmd : "");
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Start authentication after PKEX with parameters: %s",
+		   cmd);
+	if (hostapd_dpp_auth_init(hapd, cmd) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Authentication initialization failed");
+		return;
+	}
+}
+
+
+#ifdef CONFIG_DPP3
+
+static void hostapd_dpp_pb_pkex_init(struct hostapd_data *hapd,
+				     unsigned int freq, const u8 *src,
+				     const u8 *r_hash)
+{
+	struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+	struct dpp_pkex *pkex;
+	struct wpabuf *msg;
+	char ssid_hex[2 * SSID_MAX_LEN + 1], *pass_hex = NULL;
+	char cmd[300];
+	const char *password = NULL;
+	struct sae_password_entry *e;
+	int conf_id = -1;
+	bool sae = false, psk = false;
+	size_t len;
+
+	if (hapd->dpp_pkex) {
+		wpa_printf(MSG_DEBUG,
+			   "PDP: Sending previously generated PKEX Exchange Request to "
+			   MACSTR, MAC2STR(src));
+		msg = hapd->dpp_pkex->exchange_req;
+		hostapd_drv_send_action(hapd, freq, 0, src,
+					wpabuf_head(msg), wpabuf_len(msg));
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "DPP: Initiate PKEX for push button with "
+		   MACSTR, MAC2STR(src));
+
+	hapd->dpp_pkex_bi = ifaces->dpp_pb_bi;
+	os_memcpy(ifaces->dpp_pb_resp_hash, r_hash, SHA256_MAC_LEN);
+
+	pkex = dpp_pkex_init(hapd->msg_ctx, hapd->dpp_pkex_bi, hapd->own_addr,
+			     "PBPKEX", (const char *) ifaces->dpp_pb_c_nonce,
+			     ifaces->dpp_pb_bi->curve->nonce_len,
+			     true);
+	if (!pkex) {
+		hostapd_dpp_push_button_stop(hapd);
+		return;
+	}
+	pkex->freq = freq;
+
+	hapd->dpp_pkex = pkex;
+	msg = hapd->dpp_pkex->exchange_req;
+
+	if (ifaces->dpp_pb_cmd) {
+		/* Use the externally provided configuration */
+		os_free(hapd->dpp_pkex_auth_cmd);
+		len = 30 + os_strlen(ifaces->dpp_pb_cmd);
+		hapd->dpp_pkex_auth_cmd = os_malloc(len);
+		if (!hapd->dpp_pkex_auth_cmd) {
+			hostapd_dpp_push_button_stop(hapd);
+			return;
+		}
+		os_snprintf(hapd->dpp_pkex_auth_cmd, len, " own=%d %s",
+			    hapd->dpp_pkex_bi->id, ifaces->dpp_pb_cmd);
+		goto send_frame;
+	}
+
+	/* Build config based on the current AP configuration */
+	wpa_snprintf_hex(ssid_hex, sizeof(ssid_hex),
+			 (const u8 *) hapd->conf->ssid.ssid,
+			 hapd->conf->ssid.ssid_len);
+
+	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) {
+		/* TODO: If a local Configurator has been enabled, allow a
+		 * DPP AKM credential to be provisioned by setting conf_id. */
+	}
+
+	if (hapd->conf->wpa & WPA_PROTO_RSN) {
+		psk = hapd->conf->wpa_key_mgmt & (WPA_KEY_MGMT_PSK |
+						  WPA_KEY_MGMT_PSK_SHA256);
+#ifdef CONFIG_SAE
+		sae = hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE;
+#endif /* CONFIG_SAE */
+	}
+
+#ifdef CONFIG_SAE
+	for (e = hapd->conf->sae_passwords; sae && e && !password;
+	     e = e->next) {
+		if (e->identifier || !is_broadcast_ether_addr(e->peer_addr))
+			continue;
+		password = e->password;
+	}
+#endif /* CONFIG_SAE */
+	if (!password && hapd->conf->ssid.wpa_passphrase_set &&
+	    hapd->conf->ssid.wpa_passphrase)
+		password = hapd->conf->ssid.wpa_passphrase;
+	if (password) {
+		len = 2 * os_strlen(password) + 1;
+		pass_hex = os_malloc(len);
+		if (!pass_hex) {
+			hostapd_dpp_push_button_stop(hapd);
+			return;
+		}
+		wpa_snprintf_hex(pass_hex, len, (const u8 *) password,
+				 os_strlen(password));
+	}
+
+	if (conf_id > 0 && sae && psk && pass_hex) {
+		os_snprintf(cmd, sizeof(cmd),
+			    "conf=sta-dpp+psk+sae configurator=%d ssid=%s pass=%s",
+			    conf_id, ssid_hex, pass_hex);
+	} else if (conf_id > 0 && sae && pass_hex) {
+		os_snprintf(cmd, sizeof(cmd),
+			    "conf=sta-dpp+sae configurator=%d ssid=%s pass=%s",
+			    conf_id, ssid_hex, pass_hex);
+	} else if (conf_id > 0) {
+		os_snprintf(cmd, sizeof(cmd),
+			    "conf=sta-dpp configurator=%d ssid=%s",
+			    conf_id, ssid_hex);
+	} if (sae && psk && pass_hex) {
+		os_snprintf(cmd, sizeof(cmd),
+			    "conf=sta-psk+sae ssid=%s pass=%s",
+			    ssid_hex, pass_hex);
+	} else if (sae && pass_hex) {
+		os_snprintf(cmd, sizeof(cmd),
+			    "conf=sta-sae ssid=%s pass=%s",
+			    ssid_hex, pass_hex);
+	} else if (psk && pass_hex) {
+		os_snprintf(cmd, sizeof(cmd),
+			    "conf=sta-psk ssid=%s pass=%s",
+			    ssid_hex, pass_hex);
+	} else {
+		wpa_printf(MSG_INFO,
+			   "DPP: Unsupported AP configuration for push button");
+		str_clear_free(pass_hex);
+		hostapd_dpp_push_button_stop(hapd);
+		return;
+	}
+	str_clear_free(pass_hex);
+
+	os_free(hapd->dpp_pkex_auth_cmd);
+	len = 30 + os_strlen(cmd);
+	hapd->dpp_pkex_auth_cmd = os_malloc(len);
+	if (hapd->dpp_pkex_auth_cmd)
+		os_snprintf(hapd->dpp_pkex_auth_cmd, len, " own=%d %s",
+			    hapd->dpp_pkex_bi->id, cmd);
+	forced_memzero(cmd, sizeof(cmd));
+	if (!hapd->dpp_pkex_auth_cmd) {
+		hostapd_dpp_push_button_stop(hapd);
+		return;
+	}
+
+send_frame:
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+		" freq=%u type=%d", MAC2STR(src), freq,
+		DPP_PA_PKEX_EXCHANGE_REQ);
+	hostapd_drv_send_action(hapd, pkex->freq, 0, src,
+				wpabuf_head(msg), wpabuf_len(msg));
+	pkex->exch_req_wait_time = 2000;
+	pkex->exch_req_tries = 1;
+}
+
+
+static void
+hostapd_dpp_rx_pb_presence_announcement(struct hostapd_data *hapd,
+					const u8 *src, const u8 *hdr,
+					const u8 *buf, size_t len,
+					unsigned int freq)
+{
+	struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+	const u8 *r_hash;
+	u16 r_hash_len;
+	unsigned int i;
+	bool found = false;
+	struct dpp_pb_info *info, *tmp;
+	struct os_reltime now, age;
+	struct wpabuf *msg;
+
+	if (!ifaces)
+		return;
+
+	os_get_reltime(&now);
+	wpa_printf(MSG_DEBUG, "DPP: Push Button Presence Announcement from "
+		   MACSTR, MAC2STR(src));
+
+	r_hash = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+			      &r_hash_len);
+	if (!r_hash || r_hash_len != SHA256_MAC_LEN) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Missing or invalid required Responder Bootstrapping Key Hash attribute");
+		return;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
+		    r_hash, r_hash_len);
+
+	for (i = 0; i < DPP_PB_INFO_COUNT; i++) {
+		info = &ifaces->dpp_pb[i];
+		if ((info->rx_time.sec == 0 && info->rx_time.usec == 0) ||
+		    os_memcmp(r_hash, info->hash, SHA256_MAC_LEN) != 0)
+			continue;
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Active push button Enrollee already known");
+		found = true;
+		info->rx_time = now;
+	}
+
+	if (!found) {
+		for (i = 0; i < DPP_PB_INFO_COUNT; i++) {
+			tmp = &ifaces->dpp_pb[i];
+			if (tmp->rx_time.sec == 0 && tmp->rx_time.usec == 0)
+				continue;
+
+			if (os_reltime_expired(&now, &tmp->rx_time, 120)) {
+				wpa_hexdump(MSG_DEBUG,
+					    "DPP: Push button Enrollee hash expired",
+					    tmp->hash, SHA256_MAC_LEN);
+				tmp->rx_time.sec = 0;
+				tmp->rx_time.usec = 0;
+				continue;
+			}
+
+			wpa_hexdump(MSG_DEBUG,
+				    "DPP: Push button session overlap with hash",
+				    tmp->hash, SHA256_MAC_LEN);
+			if (!ifaces->dpp_pb_result_indicated &&
+			    hostapd_dpp_pb_active(hapd)) {
+				wpa_msg(hapd->msg_ctx, MSG_INFO,
+					DPP_EVENT_PB_RESULT "session-overlap");
+				ifaces->dpp_pb_result_indicated = true;
+			}
+			hostapd_dpp_push_button_stop(hapd);
+			return;
+		}
+
+		/* Replace the oldest entry */
+		info = &ifaces->dpp_pb[0];
+		for (i = 1; i < DPP_PB_INFO_COUNT; i++) {
+			tmp = &ifaces->dpp_pb[i];
+			if (os_reltime_before(&tmp->rx_time, &info->rx_time))
+				info = tmp;
+		}
+		wpa_printf(MSG_DEBUG, "DPP: New active push button Enrollee");
+		os_memcpy(info->hash, r_hash, SHA256_MAC_LEN);
+		info->rx_time = now;
+	}
+
+	if (!hostapd_dpp_pb_active(hapd)) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Discard message since own push button has not been pressed");
+		return;
+	}
+
+	if (ifaces->dpp_pb_announce_time.sec == 0 &&
+	    ifaces->dpp_pb_announce_time.usec == 0) {
+		/* Start a wait before allowing PKEX to be initiated */
+		ifaces->dpp_pb_announce_time = now;
+	}
+
+	if (!ifaces->dpp_pb_bi) {
+		int res;
+
+		res = dpp_bootstrap_gen(ifaces->dpp, "type=pkex");
+		if (res < 0)
+			return;
+		ifaces->dpp_pb_bi = dpp_bootstrap_get_id(ifaces->dpp, res);
+		if (!ifaces->dpp_pb_bi)
+			return;
+
+		if (random_get_bytes(ifaces->dpp_pb_c_nonce,
+				     ifaces->dpp_pb_bi->curve->nonce_len)) {
+			wpa_printf(MSG_ERROR,
+				   "DPP: Failed to generate C-nonce");
+			hostapd_dpp_push_button_stop(hapd);
+			return;
+		}
+	}
+
+	/* Skip the response if one was sent within last 50 ms since the
+	 * Enrollee is going to send out at least three announcement messages.
+	 */
+	os_reltime_sub(&now, &ifaces->dpp_pb_last_resp, &age);
+	if (age.sec == 0 && age.usec < 50000) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Skip Push Button Presence Announcement Response frame immediately after having sent one");
+		return;
+	}
+
+	msg = dpp_build_pb_announcement_resp(
+		ifaces->dpp_pb_bi, r_hash, ifaces->dpp_pb_c_nonce,
+		ifaces->dpp_pb_bi->curve->nonce_len);
+	if (!msg) {
+		hostapd_dpp_push_button_stop(hapd);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Send Push Button Presence Announcement Response to "
+		   MACSTR, MAC2STR(src));
+	ifaces->dpp_pb_last_resp = now;
+
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+		" freq=%u type=%d", MAC2STR(src), freq,
+		DPP_PA_PB_PRESENCE_ANNOUNCEMENT_RESP);
+	hostapd_drv_send_action(hapd, freq, 0, src,
+				wpabuf_head(msg), wpabuf_len(msg));
+	wpabuf_free(msg);
+
+	if (os_reltime_expired(&now, &ifaces->dpp_pb_announce_time, 15))
+		hostapd_dpp_pb_pkex_init(hapd, freq, src, r_hash);
+}
+
+
+static void
+hostapd_dpp_rx_priv_peer_intro_query(struct hostapd_data *hapd, const u8 *src,
+				     const u8 *hdr, const u8 *buf, size_t len,
+				     unsigned int freq)
+{
+	const u8 *trans_id, *version;
+	u16 trans_id_len, version_len;
+	struct wpabuf *msg;
+	u8 ver = DPP_VERSION;
+	int conn_ver;
+
+	wpa_printf(MSG_DEBUG, "DPP: Private Peer Introduction Query from "
+		   MACSTR, MAC2STR(src));
+
+	if (!hapd_dpp_connector_available(hapd))
+		return;
+
+	trans_id = dpp_get_attr(buf, len, DPP_ATTR_TRANSACTION_ID,
+			       &trans_id_len);
+	if (!trans_id || trans_id_len != 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Peer did not include Transaction ID");
+		return;
+	}
+
+	version = dpp_get_attr(buf, len, DPP_ATTR_PROTOCOL_VERSION,
+			       &version_len);
+	if (!version || version_len != 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Peer did not include Protocol Version");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "DPP: Transaction ID %u, Version %u",
+		   trans_id[0], version[0]);
+
+	len = 5 + 5 + 4 + os_strlen(hapd->conf->dpp_connector);
+	msg = dpp_alloc_msg(DPP_PA_PRIV_PEER_INTRO_NOTIFY, len);
+	if (!msg)
+		return;
+
+	/* Transaction ID */
+	wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID);
+	wpabuf_put_le16(msg, 1);
+	wpabuf_put_u8(msg, trans_id[0]);
+
+	/* Protocol Version */
+	conn_ver = dpp_get_connector_version(hapd->conf->dpp_connector);
+	if (conn_ver > 0 && ver != conn_ver) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Use Connector version %d instead of current protocol version %d",
+			   conn_ver, ver);
+		ver = conn_ver;
+	}
+	wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
+	wpabuf_put_le16(msg, 1);
+	wpabuf_put_u8(msg, ver);
+
+	/* DPP Connector */
+	wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR);
+	wpabuf_put_le16(msg, os_strlen(hapd->conf->dpp_connector));
+	wpabuf_put_str(msg, hapd->conf->dpp_connector);
+
+	wpa_printf(MSG_DEBUG, "DPP: Send Private Peer Introduction Notify to "
+		   MACSTR, MAC2STR(src));
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+		" freq=%u type=%d", MAC2STR(src), freq,
+		DPP_PA_PRIV_PEER_INTRO_NOTIFY);
+	hostapd_drv_send_action(hapd, freq, 0, src,
+				wpabuf_head(msg), wpabuf_len(msg));
+	wpabuf_free(msg);
+}
+
+
+static void
+hostapd_dpp_rx_priv_peer_intro_update(struct hostapd_data *hapd, const u8 *src,
+				      const u8 *hdr, const u8 *buf, size_t len,
+				      unsigned int freq)
+{
+	struct crypto_ec_key *own_key;
+	const struct dpp_curve_params *curve;
+	enum hpke_kem_id kem_id;
+	enum hpke_kdf_id kdf_id;
+	enum hpke_aead_id aead_id;
+	const u8 *aad = hdr;
+	size_t aad_len = DPP_HDR_LEN;
+	struct wpabuf *pt;
+	const u8 *trans_id, *wrapped, *version, *connector;
+	u16 trans_id_len, wrapped_len, version_len, connector_len;
+	struct os_time now;
+	struct dpp_introduction intro;
+	os_time_t expire;
+	int expiration;
+	enum dpp_status_error res;
+	u8 pkhash[SHA256_MAC_LEN];
+
+	os_memset(&intro, 0, sizeof(intro));
+
+	wpa_printf(MSG_DEBUG, "DPP: Private Peer Introduction Update from "
+		   MACSTR, MAC2STR(src));
+
+	if (!hapd_dpp_connector_available(hapd))
+		return;
+
+	os_get_time(&now);
+
+	if (hapd->conf->dpp_netaccesskey_expiry &&
+	    (os_time_t) hapd->conf->dpp_netaccesskey_expiry < now.sec) {
+		wpa_printf(MSG_INFO, "DPP: Own netAccessKey expired");
+		return;
+	}
+
+	trans_id = dpp_get_attr(buf, len, DPP_ATTR_TRANSACTION_ID,
+			       &trans_id_len);
+	if (!trans_id || trans_id_len != 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Peer did not include Transaction ID");
+		return;
+	}
+
+	wrapped = dpp_get_attr(buf, len, DPP_ATTR_WRAPPED_DATA,
+			       &wrapped_len);
+	if (!wrapped) {
+		wpa_printf(MSG_DEBUG, "DPP: Peer did not include Wrapped Data");
+		return;
+	}
+
+	own_key = dpp_set_keypair(&curve,
+				  wpabuf_head(hapd->conf->dpp_netaccesskey),
+				  wpabuf_len(hapd->conf->dpp_netaccesskey));
+	if (!own_key) {
+		wpa_printf(MSG_ERROR, "DPP: Failed to parse own netAccessKey");
+		return;
+	}
+
+	if (dpp_hpke_suite(curve->ike_group, &kem_id, &kdf_id, &aead_id) < 0) {
+		wpa_printf(MSG_ERROR, "DPP: Unsupported curve %d",
+			   curve->ike_group);
+		crypto_ec_key_deinit(own_key);
+		return;
+	}
+
+	pt = hpke_base_open(kem_id, kdf_id, aead_id, own_key, NULL, 0,
+			    aad, aad_len, wrapped, wrapped_len);
+	crypto_ec_key_deinit(own_key);
+	if (!pt) {
+		wpa_printf(MSG_INFO, "DPP: Failed to decrypt Connector");
+		return;
+	}
+	wpa_hexdump_buf(MSG_MSGDUMP, "DPP: HPKE-Decrypted Wrapped Data", pt);
+
+	connector = dpp_get_attr(wpabuf_head(pt), wpabuf_len(pt),
+				 DPP_ATTR_CONNECTOR, &connector_len);
+	if (!connector) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Peer did not include its Connector");
+		goto done;
+	}
+
+	version = dpp_get_attr(wpabuf_head(pt), wpabuf_len(pt),
+			       DPP_ATTR_PROTOCOL_VERSION, &version_len);
+	if (!version || version_len < 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Peer did not include Protocol Version");
+		goto done;
+	}
+
+	res = dpp_peer_intro(&intro, hapd->conf->dpp_connector,
+			     wpabuf_head(hapd->conf->dpp_netaccesskey),
+			     wpabuf_len(hapd->conf->dpp_netaccesskey),
+			     wpabuf_head(hapd->conf->dpp_csign),
+			     wpabuf_len(hapd->conf->dpp_csign),
+			     connector, connector_len, &expire, pkhash);
+	if (res == 255) {
+		wpa_printf(MSG_INFO,
+			   "DPP: Network Introduction protocol resulted in internal failure (peer "
+			   MACSTR ")", MAC2STR(src));
+		goto done;
+	}
+	if (res != DPP_STATUS_OK) {
+		wpa_printf(MSG_INFO,
+			   "DPP: Network Introduction protocol resulted in failure (peer "
+			   MACSTR " status %d)", MAC2STR(src), res);
+		goto done;
+	}
+
+	if (intro.peer_version && intro.peer_version >= 2) {
+		u8 attr_version = 1;
+
+		if (version && version_len >= 1)
+			attr_version = version[0];
+		if (attr_version != intro.peer_version) {
+			wpa_printf(MSG_INFO,
+				   "DPP: Protocol version mismatch (Connector: %d Attribute: %d",
+				   intro.peer_version, attr_version);
+			goto done;
+		}
+	}
+
+	if (!expire || (os_time_t) hapd->conf->dpp_netaccesskey_expiry < expire)
+		expire = hapd->conf->dpp_netaccesskey_expiry;
+	if (expire)
+		expiration = expire - now.sec;
+	else
+		expiration = 0;
+
+	if (wpa_auth_pmksa_add3(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
+				intro.pmkid, expiration,
+				WPA_KEY_MGMT_DPP, pkhash) < 0) {
+		wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry");
+		goto done;
+	}
+
+	wpa_printf(MSG_DEBUG, "DPP: Private Peer Introduction completed with "
+		   MACSTR, MAC2STR(src));
+
+done:
+	dpp_peer_intro_deinit(&intro);
+	wpabuf_free(pt);
+}
+
+#endif /* CONFIG_DPP3 */
+
+
+void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src,
+			   const u8 *buf, size_t len, unsigned int freq)
+{
+	u8 crypto_suite;
+	enum dpp_public_action_frame_type type;
+	const u8 *hdr;
+	unsigned int pkex_t;
+
+	if (len < DPP_HDR_LEN)
+		return;
+	if (WPA_GET_BE24(buf) != OUI_WFA || buf[3] != DPP_OUI_TYPE)
+		return;
+	hdr = buf;
+	buf += 4;
+	len -= 4;
+	crypto_suite = *buf++;
+	type = *buf++;
+	len -= 2;
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Received DPP Public Action frame crypto suite %u type %d from "
+		   MACSTR " freq=%u",
+		   crypto_suite, type, MAC2STR(src), freq);
+	if (crypto_suite != 1) {
+		wpa_printf(MSG_DEBUG, "DPP: Unsupported crypto suite %u",
+			   crypto_suite);
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_RX "src=" MACSTR
+			" freq=%u type=%d ignore=unsupported-crypto-suite",
+			MAC2STR(src), freq, type);
+		return;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Received message attributes", buf, len);
+	if (dpp_check_attrs(buf, len) < 0) {
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_RX "src=" MACSTR
+			" freq=%u type=%d ignore=invalid-attributes",
+			MAC2STR(src), freq, type);
+		return;
+	}
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_RX "src=" MACSTR
+		" freq=%u type=%d", MAC2STR(src), freq, type);
+
+#ifdef CONFIG_DPP2
+	if (dpp_relay_rx_action(hapd->iface->interfaces->dpp,
+				src, hdr, buf, len, freq, NULL, NULL,
+				hapd) == 0)
+		return;
+#endif /* CONFIG_DPP2 */
+
+	switch (type) {
+	case DPP_PA_AUTHENTICATION_REQ:
+		hostapd_dpp_rx_auth_req(hapd, src, hdr, buf, len, freq);
+		break;
+	case DPP_PA_AUTHENTICATION_RESP:
+		hostapd_dpp_rx_auth_resp(hapd, src, hdr, buf, len, freq);
+		break;
+	case DPP_PA_AUTHENTICATION_CONF:
+		hostapd_dpp_rx_auth_conf(hapd, src, hdr, buf, len);
+		break;
+	case DPP_PA_PEER_DISCOVERY_REQ:
+		hostapd_dpp_rx_peer_disc_req(hapd, src, buf, len, freq);
+		break;
+#ifdef CONFIG_DPP3
+	case DPP_PA_PKEX_EXCHANGE_REQ:
+		/* This is for PKEXv2, but for now, process only with
+		 * CONFIG_DPP3 to avoid issues with a capability that has not
+		 * been tested with other implementations. */
+		hostapd_dpp_rx_pkex_exchange_req(hapd, src, hdr, buf, len, freq,
+						 true);
+		break;
+#endif /* CONFIG_DPP3 */
+	case DPP_PA_PKEX_V1_EXCHANGE_REQ:
+		hostapd_dpp_rx_pkex_exchange_req(hapd, src, hdr, buf, len, freq,
+						 false);
+		break;
+	case DPP_PA_PKEX_EXCHANGE_RESP:
+		hostapd_dpp_rx_pkex_exchange_resp(hapd, src, buf, len, freq);
+		break;
+	case DPP_PA_PKEX_COMMIT_REVEAL_REQ:
+		hostapd_dpp_rx_pkex_commit_reveal_req(hapd, src, hdr, buf, len,
+						      freq);
+		break;
+	case DPP_PA_PKEX_COMMIT_REVEAL_RESP:
+		hostapd_dpp_rx_pkex_commit_reveal_resp(hapd, src, hdr, buf, len,
+						       freq);
+		break;
+#ifdef CONFIG_DPP2
+	case DPP_PA_CONFIGURATION_RESULT:
+		hostapd_dpp_rx_conf_result(hapd, src, hdr, buf, len);
+		break;
+	case DPP_PA_CONNECTION_STATUS_RESULT:
+		hostapd_dpp_rx_conn_status_result(hapd, src, hdr, buf, len);
+		break;
+	case DPP_PA_PRESENCE_ANNOUNCEMENT:
+		hostapd_dpp_rx_presence_announcement(hapd, src, hdr, buf, len,
+						     freq);
+		break;
+	case DPP_PA_RECONFIG_ANNOUNCEMENT:
+		hostapd_dpp_rx_reconfig_announcement(hapd, src, hdr, buf, len,
+						     freq);
+		break;
+	case DPP_PA_RECONFIG_AUTH_RESP:
+		hostapd_dpp_rx_reconfig_auth_resp(hapd, src, hdr, buf, len,
+						  freq);
+		break;
+#endif /* CONFIG_DPP2 */
+#ifdef CONFIG_DPP3
+	case DPP_PA_PB_PRESENCE_ANNOUNCEMENT:
+		hostapd_dpp_rx_pb_presence_announcement(hapd, src, hdr,
+							buf, len, freq);
+		break;
+	case DPP_PA_PRIV_PEER_INTRO_QUERY:
+		hostapd_dpp_rx_priv_peer_intro_query(hapd, src, hdr,
+						     buf, len, freq);
+		break;
+	case DPP_PA_PRIV_PEER_INTRO_UPDATE:
+		hostapd_dpp_rx_priv_peer_intro_update(hapd, src, hdr,
+						      buf, len, freq);
+		break;
+#endif /* CONFIG_DPP3 */
+	default:
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Ignored unsupported frame subtype %d", type);
+		break;
+	}
+
+	if (hapd->dpp_pkex)
+		pkex_t = hapd->dpp_pkex->t;
+	else if (hapd->dpp_pkex_bi)
+		pkex_t = hapd->dpp_pkex_bi->pkex_t;
+	else
+		pkex_t = 0;
+	if (pkex_t >= PKEX_COUNTER_T_LIMIT) {
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_PKEX_T_LIMIT "id=0");
+		hostapd_dpp_pkex_remove(hapd, "*");
+	}
+}
+
+
+struct wpabuf *
+hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa,
+			    const u8 *query, size_t query_len,
+			    const u8 *data, size_t data_len)
+{
+	struct dpp_authentication *auth = hapd->dpp_auth;
+	struct wpabuf *resp;
+
+	wpa_printf(MSG_DEBUG, "DPP: GAS request from " MACSTR, MAC2STR(sa));
+	if (!auth || (!auth->auth_success && !auth->reconfig_success) ||
+	    os_memcmp(sa, auth->peer_mac_addr, ETH_ALEN) != 0) {
+#ifdef CONFIG_DPP2
+		if (dpp_relay_rx_gas_req(hapd->iface->interfaces->dpp, sa, data,
+				     data_len) == 0) {
+			/* Response will be forwarded once received over TCP */
+			return NULL;
+		}
+#endif /* CONFIG_DPP2 */
+		wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
+		return NULL;
+	}
+
+	if (hapd->dpp_auth_ok_on_ack && auth->configurator) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Have not received ACK for Auth Confirm yet - assume it was received based on this GAS request");
+		/* hostapd_dpp_auth_success() would normally have been called
+		 * from TX status handler, but since there was no such handler
+		 * call yet, simply send out the event message and proceed with
+		 * exchange. */
+		dpp_notify_auth_success(hapd->dpp_auth, 1);
+		hapd->dpp_auth_ok_on_ack = 0;
+	}
+
+	wpa_hexdump(MSG_DEBUG,
+		    "DPP: Received Configuration Request (GAS Query Request)",
+		    query, query_len);
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_REQ_RX "src=" MACSTR,
+		MAC2STR(sa));
+	resp = dpp_conf_req_rx(auth, query, query_len);
+	if (!resp)
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
+	return resp;
+}
+
+
+void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok)
+{
+	struct dpp_authentication *auth = hapd->dpp_auth;
+#ifdef CONFIG_DPP3
+	struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+#endif /* CONFIG_DPP3 */
+
+	if (!auth)
+		return;
+
+#ifdef CONFIG_DPP3
+	if (auth->waiting_new_key && ok) {
+		wpa_printf(MSG_DEBUG, "DPP: Waiting for a new key");
+		return;
+	}
+#endif /* CONFIG_DPP3 */
+
+	wpa_printf(MSG_DEBUG, "DPP: Configuration exchange completed (ok=%d)",
+		   ok);
+	eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
+	eloop_cancel_timeout(hostapd_dpp_auth_conf_wait_timeout, hapd, NULL);
+	eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
+#ifdef CONFIG_DPP2
+		eloop_cancel_timeout(hostapd_dpp_reconfig_reply_wait_timeout,
+				     hapd, NULL);
+	if (ok && auth->peer_version >= 2 &&
+	    auth->conf_resp_status == DPP_STATUS_OK) {
+		wpa_printf(MSG_DEBUG, "DPP: Wait for Configuration Result");
+		auth->waiting_conf_result = 1;
+		eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout,
+				     hapd, NULL);
+		eloop_register_timeout(2, 0,
+				       hostapd_dpp_config_result_wait_timeout,
+				       hapd, NULL);
+		return;
+	}
+#endif /* CONFIG_DPP2 */
+	hostapd_drv_send_action_cancel_wait(hapd);
+
+	if (ok)
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT
+			"conf_status=%d", auth->conf_resp_status);
+	else
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
+	dpp_auth_deinit(hapd->dpp_auth);
+	hapd->dpp_auth = NULL;
+#ifdef CONFIG_DPP3
+	if (!ifaces->dpp_pb_result_indicated && hostapd_dpp_pb_active(hapd)) {
+		if (ok)
+			wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_PB_RESULT
+				"success");
+		else
+			wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_PB_RESULT
+				"could-not-connect");
+		ifaces->dpp_pb_result_indicated = true;
+		if (ok)
+			hostapd_dpp_remove_pb_hash(hapd);
+		hostapd_dpp_push_button_stop(hapd);
+	}
+#endif /* CONFIG_DPP3 */
+}
+
+
+int hostapd_dpp_configurator_sign(struct hostapd_data *hapd, const char *cmd)
+{
+	struct dpp_authentication *auth;
+	int ret = -1;
+	char *curve = NULL;
+
+	auth = dpp_alloc_auth(hapd->iface->interfaces->dpp, hapd->msg_ctx);
+	if (!auth)
+		return -1;
+
+	curve = get_param(cmd, " curve=");
+	hostapd_dpp_set_testing_options(hapd, auth);
+	if (dpp_set_configurator(auth, cmd) == 0 &&
+	    dpp_configurator_own_config(auth, curve, 1) == 0) {
+		hostapd_dpp_handle_config_obj(hapd, auth, &auth->conf_obj[0]);
+		ret = 0;
+	}
+
+	dpp_auth_deinit(auth);
+	os_free(curve);
+
+	return ret;
+}
+
+
+int hostapd_dpp_pkex_add(struct hostapd_data *hapd, const char *cmd)
+{
+	struct dpp_bootstrap_info *own_bi;
+	const char *pos, *end;
+#ifdef CONFIG_DPP3
+		enum dpp_pkex_ver ver = PKEX_VER_AUTO;
+#else /* CONFIG_DPP3 */
+		enum dpp_pkex_ver ver = PKEX_VER_ONLY_1;
+#endif /* CONFIG_DPP3 */
+	int tcp_port = DPP_TCP_PORT;
+	struct hostapd_ip_addr *ipaddr = NULL;
+#ifdef CONFIG_DPP2
+	struct hostapd_ip_addr ipaddr_buf;
+	char *addr;
+
+	pos = os_strstr(cmd, " tcp_port=");
+	if (pos) {
+		pos += 10;
+		tcp_port = atoi(pos);
+	}
+
+	addr = get_param(cmd, " tcp_addr=");
+	if (addr) {
+		int res;
+
+		res = hostapd_parse_ip_addr(addr, &ipaddr_buf);
+		os_free(addr);
+		if (res)
+			return -1;
+		ipaddr = &ipaddr_buf;
+	}
+#endif /* CONFIG_DPP2 */
+
+	pos = os_strstr(cmd, " own=");
+	if (!pos)
+		return -1;
+	pos += 5;
+	own_bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp, atoi(pos));
+	if (!own_bi) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Identified bootstrap info not found");
+		return -1;
+	}
+	if (own_bi->type != DPP_BOOTSTRAP_PKEX) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Identified bootstrap info not for PKEX");
+		return -1;
+	}
+	hapd->dpp_pkex_bi = own_bi;
+	own_bi->pkex_t = 0; /* clear pending errors on new code */
+
+	os_free(hapd->dpp_pkex_identifier);
+	hapd->dpp_pkex_identifier = NULL;
+	pos = os_strstr(cmd, " identifier=");
+	if (pos) {
+		pos += 12;
+		end = os_strchr(pos, ' ');
+		if (!end)
+			return -1;
+		hapd->dpp_pkex_identifier = os_malloc(end - pos + 1);
+		if (!hapd->dpp_pkex_identifier)
+			return -1;
+		os_memcpy(hapd->dpp_pkex_identifier, pos, end - pos);
+		hapd->dpp_pkex_identifier[end - pos] = '\0';
+	}
+
+	pos = os_strstr(cmd, " code=");
+	if (!pos)
+		return -1;
+	os_free(hapd->dpp_pkex_code);
+	hapd->dpp_pkex_code = os_strdup(pos + 6);
+	if (!hapd->dpp_pkex_code)
+		return -1;
+	hapd->dpp_pkex_code_len = os_strlen(hapd->dpp_pkex_code);
+
+	pos = os_strstr(cmd, " ver=");
+	if (pos) {
+		int v;
+
+		pos += 5;
+		v = atoi(pos);
+		if (v == 1)
+			ver = PKEX_VER_ONLY_1;
+		else if (v == 2)
+			ver = PKEX_VER_ONLY_2;
+		else
+			return -1;
+	}
+	hapd->dpp_pkex_ver = ver;
+
+	if (os_strstr(cmd, " init=1")) {
+		if (hostapd_dpp_pkex_init(hapd, ver, ipaddr, tcp_port) < 0)
+			return -1;
+	} else {
+#ifdef CONFIG_DPP2
+		dpp_controller_pkex_add(hapd->iface->interfaces->dpp, own_bi,
+					hapd->dpp_pkex_code,
+					hapd->dpp_pkex_identifier);
+#endif /* CONFIG_DPP2 */
+	}
+
+	/* TODO: Support multiple PKEX info entries */
+
+	os_free(hapd->dpp_pkex_auth_cmd);
+	hapd->dpp_pkex_auth_cmd = os_strdup(cmd);
+
+	return 1;
+}
+
+
+int hostapd_dpp_pkex_remove(struct hostapd_data *hapd, const char *id)
+{
+	unsigned int id_val;
+
+	if (os_strcmp(id, "*") == 0) {
+		id_val = 0;
+	} else {
+		id_val = atoi(id);
+		if (id_val == 0)
+			return -1;
+	}
+
+	if ((id_val != 0 && id_val != 1))
+		return -1;
+
+	/* TODO: Support multiple PKEX entries */
+	os_free(hapd->dpp_pkex_code);
+	hapd->dpp_pkex_code = NULL;
+	os_free(hapd->dpp_pkex_identifier);
+	hapd->dpp_pkex_identifier = NULL;
+	os_free(hapd->dpp_pkex_auth_cmd);
+	hapd->dpp_pkex_auth_cmd = NULL;
+	hapd->dpp_pkex_bi = NULL;
+	/* TODO: Remove dpp_pkex only if it is for the identified PKEX code */
+	dpp_pkex_free(hapd->dpp_pkex);
+	hapd->dpp_pkex = NULL;
+	return 0;
+}
+
+
+void hostapd_dpp_stop(struct hostapd_data *hapd)
+{
+	dpp_auth_deinit(hapd->dpp_auth);
+	hapd->dpp_auth = NULL;
+	dpp_pkex_free(hapd->dpp_pkex);
+	hapd->dpp_pkex = NULL;
+#ifdef CONFIG_DPP3
+	hostapd_dpp_push_button_stop(hapd);
+#endif /* CONFIG_DPP3 */
+}
+
+
+#ifdef CONFIG_DPP2
+
+static void hostapd_dpp_relay_tx(void *ctx, const u8 *addr, unsigned int freq,
+				 const u8 *msg, size_t len)
+{
+	struct hostapd_data *hapd = ctx;
+	u8 *buf;
+
+	if (freq == 0)
+		freq = hapd->iface->freq;
+
+	wpa_printf(MSG_DEBUG, "DPP: Send action frame dst=" MACSTR " freq=%u",
+		   MAC2STR(addr), freq);
+	buf = os_malloc(2 + len);
+	if (!buf)
+		return;
+	buf[0] = WLAN_ACTION_PUBLIC;
+	buf[1] = WLAN_PA_VENDOR_SPECIFIC;
+	os_memcpy(buf + 2, msg, len);
+	hostapd_drv_send_action(hapd, freq, 0, addr, buf, 2 + len);
+	os_free(buf);
+}
+
+
+static void hostapd_dpp_relay_gas_resp_tx(void *ctx, const u8 *addr,
+					  u8 dialog_token, int prot,
+					  struct wpabuf *buf)
+{
+	struct hostapd_data *hapd = ctx;
+
+	gas_serv_req_dpp_processing(hapd, addr, dialog_token, prot, buf, 0);
+}
+
+#endif /* CONFIG_DPP2 */
+
+
+static int hostapd_dpp_add_controllers(struct hostapd_data *hapd)
+{
+#ifdef CONFIG_DPP2
+	struct dpp_controller_conf *ctrl;
+	struct dpp_relay_config config;
+
+	os_memset(&config, 0, sizeof(config));
+	config.msg_ctx = hapd->msg_ctx;
+	config.cb_ctx = hapd;
+	config.tx = hostapd_dpp_relay_tx;
+	config.gas_resp_tx = hostapd_dpp_relay_gas_resp_tx;
+	for (ctrl = hapd->conf->dpp_controller; ctrl; ctrl = ctrl->next) {
+		config.ipaddr = &ctrl->ipaddr;
+		config.pkhash = ctrl->pkhash;
+		if (dpp_relay_add_controller(hapd->iface->interfaces->dpp,
+					     &config) < 0)
+			return -1;
+	}
+
+	if (hapd->conf->dpp_relay_port)
+		dpp_relay_listen(hapd->iface->interfaces->dpp,
+				 hapd->conf->dpp_relay_port,
+				 &config);
+#endif /* CONFIG_DPP2 */
+
+	return 0;
+}
+
+
+#ifdef CONFIG_DPP2
+
+int hostapd_dpp_add_controller(struct hostapd_data *hapd, const char *cmd)
+{
+	struct dpp_relay_config config;
+	struct hostapd_ip_addr addr;
+	u8 pkhash[SHA256_MAC_LEN];
+	char *pos, *tmp;
+	int ret = -1;
+	bool prev_state, new_state;
+	struct dpp_global *dpp = hapd->iface->interfaces->dpp;
+
+	tmp = os_strdup(cmd);
+	if (!tmp)
+		goto fail;
+	pos = os_strchr(tmp, ' ');
+	if (!pos)
+		goto fail;
+	*pos++ = '\0';
+	if (hostapd_parse_ip_addr(tmp, &addr) < 0 ||
+	    hexstr2bin(pos, pkhash, SHA256_MAC_LEN) < 0)
+		goto fail;
+
+	os_memset(&config, 0, sizeof(config));
+	config.msg_ctx = hapd->msg_ctx;
+	config.cb_ctx = hapd;
+	config.tx = hostapd_dpp_relay_tx;
+	config.gas_resp_tx = hostapd_dpp_relay_gas_resp_tx;
+	config.ipaddr = &addr;
+	config.pkhash = pkhash;
+	prev_state = dpp_relay_controller_available(dpp);
+	ret = dpp_relay_add_controller(dpp, &config);
+	new_state = dpp_relay_controller_available(dpp);
+	if (new_state != prev_state)
+		ieee802_11_update_beacons(hapd->iface);
+fail:
+	os_free(tmp);
+	return ret;
+}
+
+
+void hostapd_dpp_remove_controller(struct hostapd_data *hapd, const char *cmd)
+{
+	struct hostapd_ip_addr addr;
+	bool prev_state, new_state;
+	struct dpp_global *dpp = hapd->iface->interfaces->dpp;
+
+	if (hostapd_parse_ip_addr(cmd, &addr) < 0)
+		return;
+	prev_state = dpp_relay_controller_available(dpp);
+	dpp_relay_remove_controller(dpp, &addr);
+	new_state = dpp_relay_controller_available(dpp);
+	if (new_state != prev_state)
+		ieee802_11_update_beacons(hapd->iface);
+}
+
+#endif /* CONFIG_DPP2 */
+
+
+int hostapd_dpp_init(struct hostapd_data *hapd)
+{
+	hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE;
+	hapd->dpp_init_done = 1;
+	return hostapd_dpp_add_controllers(hapd);
+}
+
+
+void hostapd_dpp_deinit(struct hostapd_data *hapd)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+	os_free(hapd->dpp_config_obj_override);
+	hapd->dpp_config_obj_override = NULL;
+	os_free(hapd->dpp_discovery_override);
+	hapd->dpp_discovery_override = NULL;
+	os_free(hapd->dpp_groups_override);
+	hapd->dpp_groups_override = NULL;
+	hapd->dpp_ignore_netaccesskey_mismatch = 0;
+#endif /* CONFIG_TESTING_OPTIONS */
+	if (!hapd->dpp_init_done)
+		return;
+	eloop_cancel_timeout(hostapd_dpp_pkex_retry_timeout, hapd, NULL);
+	eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
+	eloop_cancel_timeout(hostapd_dpp_auth_conf_wait_timeout, hapd, NULL);
+	eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
+	eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
+#ifdef CONFIG_DPP2
+	eloop_cancel_timeout(hostapd_dpp_reconfig_reply_wait_timeout,
+			     hapd, NULL);
+	eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout, hapd,
+			     NULL);
+	eloop_cancel_timeout(hostapd_dpp_conn_status_result_wait_timeout, hapd,
+			     NULL);
+	hostapd_dpp_chirp_stop(hapd);
+	if (hapd->iface->interfaces) {
+		dpp_relay_stop_listen(hapd->iface->interfaces->dpp);
+		dpp_controller_stop_for_ctx(hapd->iface->interfaces->dpp, hapd);
+	}
+#endif /* CONFIG_DPP2 */
+#ifdef CONFIG_DPP3
+	eloop_cancel_timeout(hostapd_dpp_build_new_key, hapd, NULL);
+	hostapd_dpp_push_button_stop(hapd);
+#endif /* CONFIG_DPP3 */
+	dpp_auth_deinit(hapd->dpp_auth);
+	hapd->dpp_auth = NULL;
+	hostapd_dpp_pkex_remove(hapd, "*");
+	hapd->dpp_pkex = NULL;
+	os_free(hapd->dpp_configurator_params);
+	hapd->dpp_configurator_params = NULL;
+	os_free(hapd->dpp_pkex_auth_cmd);
+	hapd->dpp_pkex_auth_cmd = NULL;
+}
+
+
+#ifdef CONFIG_DPP2
+
+int hostapd_dpp_controller_start(struct hostapd_data *hapd, const char *cmd)
+{
+	struct dpp_controller_config config;
+	const char *pos;
+
+	os_memset(&config, 0, sizeof(config));
+	config.allowed_roles = DPP_CAPAB_ENROLLEE | DPP_CAPAB_CONFIGURATOR;
+	config.netrole = DPP_NETROLE_AP;
+	config.msg_ctx = hapd->msg_ctx;
+	config.cb_ctx = hapd;
+	config.process_conf_obj = hostapd_dpp_process_conf_obj;
+	if (cmd) {
+		pos = os_strstr(cmd, " tcp_port=");
+		if (pos) {
+			pos += 10;
+			config.tcp_port = atoi(pos);
+		}
+
+		pos = os_strstr(cmd, " role=");
+		if (pos) {
+			pos += 6;
+			if (os_strncmp(pos, "configurator", 12) == 0)
+				config.allowed_roles = DPP_CAPAB_CONFIGURATOR;
+			else if (os_strncmp(pos, "enrollee", 8) == 0)
+				config.allowed_roles = DPP_CAPAB_ENROLLEE;
+			else if (os_strncmp(pos, "either", 6) == 0)
+				config.allowed_roles = DPP_CAPAB_CONFIGURATOR |
+					DPP_CAPAB_ENROLLEE;
+			else
+				return -1;
+		}
+
+		config.qr_mutual = os_strstr(cmd, " qr=mutual") != NULL;
+	}
+	config.configurator_params = hapd->dpp_configurator_params;
+	return dpp_controller_start(hapd->iface->interfaces->dpp, &config);
+}
+
+
+static void hostapd_dpp_chirp_next(void *eloop_ctx, void *timeout_ctx);
+
+static void hostapd_dpp_chirp_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+
+	wpa_printf(MSG_DEBUG, "DPP: No chirp response received");
+	hostapd_drv_send_action_cancel_wait(hapd);
+	hostapd_dpp_chirp_next(hapd, NULL);
+}
+
+
+static void hostapd_dpp_chirp_start(struct hostapd_data *hapd)
+{
+	struct wpabuf *msg;
+	int type;
+
+	msg = hapd->dpp_presence_announcement;
+	type = DPP_PA_PRESENCE_ANNOUNCEMENT;
+	wpa_printf(MSG_DEBUG, "DPP: Chirp on %d MHz", hapd->dpp_chirp_freq);
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+		" freq=%u type=%d",
+		MAC2STR(broadcast), hapd->dpp_chirp_freq, type);
+	if (hostapd_drv_send_action(
+		    hapd, hapd->dpp_chirp_freq, 2000, broadcast,
+		    wpabuf_head(msg), wpabuf_len(msg)) < 0 ||
+	    eloop_register_timeout(2, 0, hostapd_dpp_chirp_timeout,
+				   hapd, NULL) < 0)
+		hostapd_dpp_chirp_stop(hapd);
+}
+
+
+static struct hostapd_hw_modes *
+dpp_get_mode(struct hostapd_data *hapd,
+	     enum hostapd_hw_mode mode)
+{
+	struct hostapd_hw_modes *modes = hapd->iface->hw_features;
+	u16 num_modes = hapd->iface->num_hw_features;
+	u16 i;
+
+	for (i = 0; i < num_modes; i++) {
+		if (modes[i].mode != mode ||
+		    !modes[i].num_channels || !modes[i].channels)
+			continue;
+		return &modes[i];
+	}
+
+	return NULL;
+}
+
+
+static void
+hostapd_dpp_chirp_scan_res_handler(struct hostapd_iface *iface)
+{
+	struct hostapd_data *hapd = iface->bss[0];
+	struct wpa_scan_results *scan_res;
+	struct dpp_bootstrap_info *bi = hapd->dpp_chirp_bi;
+	unsigned int i;
+	struct hostapd_hw_modes *mode;
+	int c;
+	bool chan6 = hapd->iface->hw_features == NULL;
+
+	if (!bi)
+		return;
+
+	hapd->dpp_chirp_scan_done = 1;
+
+	scan_res = hostapd_driver_get_scan_results(hapd);
+
+	os_free(hapd->dpp_chirp_freqs);
+	hapd->dpp_chirp_freqs = NULL;
+
+	/* Channels from own bootstrapping info */
+	if (bi) {
+		for (i = 0; i < bi->num_freq; i++)
+			int_array_add_unique(&hapd->dpp_chirp_freqs,
+					     bi->freq[i]);
+	}
+
+	/* Preferred chirping channels */
+	mode = dpp_get_mode(hapd, HOSTAPD_MODE_IEEE80211G);
+	if (mode) {
+		for (c = 0; c < mode->num_channels; c++) {
+			struct hostapd_channel_data *chan = &mode->channels[c];
+
+			if (chan->flag & (HOSTAPD_CHAN_DISABLED |
+					  HOSTAPD_CHAN_RADAR) ||
+			    chan->freq != 2437)
+				continue;
+			chan6 = true;
+			break;
+		}
+	}
+	if (chan6)
+		int_array_add_unique(&hapd->dpp_chirp_freqs, 2437);
+
+	mode = dpp_get_mode(hapd, HOSTAPD_MODE_IEEE80211A);
+	if (mode) {
+		int chan44 = 0, chan149 = 0;
+
+		for (c = 0; c < mode->num_channels; c++) {
+			struct hostapd_channel_data *chan = &mode->channels[c];
+
+			if (chan->flag & (HOSTAPD_CHAN_DISABLED |
+					  HOSTAPD_CHAN_RADAR))
+				continue;
+			if (chan->freq == 5220)
+				chan44 = 1;
+			if (chan->freq == 5745)
+				chan149 = 1;
+		}
+		if (chan149)
+			int_array_add_unique(&hapd->dpp_chirp_freqs, 5745);
+		else if (chan44)
+			int_array_add_unique(&hapd->dpp_chirp_freqs, 5220);
+	}
+
+	mode = dpp_get_mode(hapd, HOSTAPD_MODE_IEEE80211AD);
+	if (mode) {
+		for (c = 0; c < mode->num_channels; c++) {
+			struct hostapd_channel_data *chan = &mode->channels[c];
+
+			if ((chan->flag & (HOSTAPD_CHAN_DISABLED |
+					   HOSTAPD_CHAN_RADAR)) ||
+			    chan->freq != 60480)
+				continue;
+			int_array_add_unique(&hapd->dpp_chirp_freqs, 60480);
+			break;
+		}
+	}
+
+	/* Add channels from scan results for APs that advertise Configurator
+	 * Connectivity element */
+	for (i = 0; scan_res && i < scan_res->num; i++) {
+		struct wpa_scan_res *bss = scan_res->res[i];
+		size_t ie_len = bss->ie_len;
+
+		if (!ie_len)
+			ie_len = bss->beacon_ie_len;
+		if (get_vendor_ie((const u8 *) (bss + 1), ie_len,
+				  DPP_CC_IE_VENDOR_TYPE))
+			int_array_add_unique(&hapd->dpp_chirp_freqs,
+					     bss->freq);
+	}
+
+	if (!hapd->dpp_chirp_freqs ||
+	    eloop_register_timeout(0, 0, hostapd_dpp_chirp_next,
+				   hapd, NULL) < 0)
+		hostapd_dpp_chirp_stop(hapd);
+
+	wpa_scan_results_free(scan_res);
+}
+
+
+static void hostapd_dpp_chirp_next(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	int i;
+
+	if (hapd->dpp_chirp_listen)
+		hostapd_dpp_listen_stop(hapd);
+
+	if (hapd->dpp_chirp_freq == 0) {
+		if (hapd->dpp_chirp_round % 4 == 0 &&
+		    !hapd->dpp_chirp_scan_done) {
+			struct wpa_driver_scan_params params;
+			int ret;
+
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Update channel list for chirping");
+			os_memset(&params, 0, sizeof(params));
+			ret = hostapd_driver_scan(hapd, &params);
+			if (ret < 0) {
+				wpa_printf(MSG_DEBUG,
+					   "DPP: Failed to request a scan ret=%d (%s)",
+					   ret, strerror(-ret));
+				hostapd_dpp_chirp_scan_res_handler(hapd->iface);
+			} else {
+				hapd->iface->scan_cb =
+					hostapd_dpp_chirp_scan_res_handler;
+			}
+			return;
+		}
+		hapd->dpp_chirp_freq = hapd->dpp_chirp_freqs[0];
+		hapd->dpp_chirp_round++;
+		wpa_printf(MSG_DEBUG, "DPP: Start chirping round %d",
+			   hapd->dpp_chirp_round);
+	} else {
+		for (i = 0; hapd->dpp_chirp_freqs[i]; i++)
+			if (hapd->dpp_chirp_freqs[i] == hapd->dpp_chirp_freq)
+				break;
+		if (!hapd->dpp_chirp_freqs[i]) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Previous chirp freq %d not found",
+				   hapd->dpp_chirp_freq);
+			return;
+		}
+		i++;
+		if (hapd->dpp_chirp_freqs[i]) {
+			hapd->dpp_chirp_freq = hapd->dpp_chirp_freqs[i];
+		} else {
+			hapd->dpp_chirp_iter--;
+			if (hapd->dpp_chirp_iter <= 0) {
+				wpa_printf(MSG_DEBUG,
+					   "DPP: Chirping iterations completed");
+				hostapd_dpp_chirp_stop(hapd);
+				return;
+			}
+			hapd->dpp_chirp_freq = 0;
+			hapd->dpp_chirp_scan_done = 0;
+			if (eloop_register_timeout(30, 0,
+						   hostapd_dpp_chirp_next,
+						   hapd, NULL) < 0) {
+				hostapd_dpp_chirp_stop(hapd);
+				return;
+			}
+			if (hapd->dpp_chirp_listen) {
+				wpa_printf(MSG_DEBUG,
+					   "DPP: Listen on %d MHz during chirp 30 second wait",
+					hapd->dpp_chirp_listen);
+				/* TODO: start listen on the channel */
+			} else {
+				wpa_printf(MSG_DEBUG,
+					   "DPP: Wait 30 seconds before starting the next chirping round");
+			}
+			return;
+		}
+	}
+
+	hostapd_dpp_chirp_start(hapd);
+}
+
+
+int hostapd_dpp_chirp(struct hostapd_data *hapd, const char *cmd)
+{
+	const char *pos;
+	int iter = 1, listen_freq = 0;
+	struct dpp_bootstrap_info *bi;
+
+	pos = os_strstr(cmd, " own=");
+	if (!pos)
+		return -1;
+	pos += 5;
+	bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp, atoi(pos));
+	if (!bi) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Identified bootstrap info not found");
+		return -1;
+	}
+
+	pos = os_strstr(cmd, " iter=");
+	if (pos) {
+		iter = atoi(pos + 6);
+		if (iter <= 0)
+			return -1;
+	}
+
+	pos = os_strstr(cmd, " listen=");
+	if (pos) {
+		listen_freq = atoi(pos + 8);
+		if (listen_freq <= 0)
+			return -1;
+	}
+
+	hostapd_dpp_chirp_stop(hapd);
+	hapd->dpp_allowed_roles = DPP_CAPAB_ENROLLEE;
+	hapd->dpp_qr_mutual = 0;
+	hapd->dpp_chirp_bi = bi;
+	hapd->dpp_presence_announcement = dpp_build_presence_announcement(bi);
+	if (!hapd->dpp_presence_announcement)
+		return -1;
+	hapd->dpp_chirp_iter = iter;
+	hapd->dpp_chirp_round = 0;
+	hapd->dpp_chirp_scan_done = 0;
+	hapd->dpp_chirp_listen = listen_freq;
+
+	return eloop_register_timeout(0, 0, hostapd_dpp_chirp_next, hapd, NULL);
+}
+
+
+void hostapd_dpp_chirp_stop(struct hostapd_data *hapd)
+{
+	if (hapd->dpp_presence_announcement) {
+		hostapd_drv_send_action_cancel_wait(hapd);
+		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CHIRP_STOPPED);
+	}
+	hapd->dpp_chirp_bi = NULL;
+	wpabuf_free(hapd->dpp_presence_announcement);
+	hapd->dpp_presence_announcement = NULL;
+	if (hapd->dpp_chirp_listen)
+		hostapd_dpp_listen_stop(hapd);
+	hapd->dpp_chirp_listen = 0;
+	hapd->dpp_chirp_freq = 0;
+	os_free(hapd->dpp_chirp_freqs);
+	hapd->dpp_chirp_freqs = NULL;
+	eloop_cancel_timeout(hostapd_dpp_chirp_next, hapd, NULL);
+	eloop_cancel_timeout(hostapd_dpp_chirp_timeout, hapd, NULL);
+	if (hapd->iface->scan_cb == hostapd_dpp_chirp_scan_res_handler) {
+		/* TODO: abort ongoing scan */
+		hapd->iface->scan_cb = NULL;
+	}
+}
+
+
+static int handle_dpp_remove_bi(struct hostapd_iface *iface, void *ctx)
+{
+	struct dpp_bootstrap_info *bi = ctx;
+	size_t i;
+
+	for (i = 0; i < iface->num_bss; i++) {
+		struct hostapd_data *hapd = iface->bss[i];
+
+		if (bi == hapd->dpp_chirp_bi)
+			hostapd_dpp_chirp_stop(hapd);
+	}
+
+	return 0;
+}
+
+
+void hostapd_dpp_remove_bi(void *ctx, struct dpp_bootstrap_info *bi)
+{
+	struct hapd_interfaces *interfaces = ctx;
+
+	hostapd_for_each_interface(interfaces, handle_dpp_remove_bi, bi);
+}
+
+#endif /* CONFIG_DPP2 */
+
+
+#ifdef CONFIG_DPP3
+
+static void hostapd_dpp_push_button_expire(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+
+	wpa_printf(MSG_DEBUG, "DPP: Active push button mode expired");
+	hostapd_dpp_push_button_stop(hapd);
+}
+
+
+int hostapd_dpp_push_button(struct hostapd_data *hapd, const char *cmd)
+{
+	struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+
+	if (!ifaces || !ifaces->dpp)
+		return -1;
+	os_get_reltime(&ifaces->dpp_pb_time);
+	ifaces->dpp_pb_announce_time.sec = 0;
+	ifaces->dpp_pb_announce_time.usec = 0;
+	str_clear_free(ifaces->dpp_pb_cmd);
+	ifaces->dpp_pb_cmd = NULL;
+	if (cmd) {
+		ifaces->dpp_pb_cmd = os_strdup(cmd);
+		if (!ifaces->dpp_pb_cmd)
+			return -1;
+	}
+	eloop_register_timeout(100, 0, hostapd_dpp_push_button_expire,
+			       hapd, NULL);
+
+	return 0;
+}
+
+
+void hostapd_dpp_push_button_stop(struct hostapd_data *hapd)
+{
+	struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+
+	if (!ifaces || !ifaces->dpp)
+		return;
+	eloop_cancel_timeout(hostapd_dpp_push_button_expire, hapd, NULL);
+	if (hostapd_dpp_pb_active(hapd)) {
+		wpa_printf(MSG_DEBUG, "DPP: Stop active push button mode");
+		if (!ifaces->dpp_pb_result_indicated)
+			wpa_msg(hapd->msg_ctx, MSG_INFO,
+				DPP_EVENT_PB_RESULT "failed");
+	}
+	ifaces->dpp_pb_time.sec = 0;
+	ifaces->dpp_pb_time.usec = 0;
+	dpp_pkex_free(hapd->dpp_pkex);
+	hapd->dpp_pkex = NULL;
+	os_free(hapd->dpp_pkex_auth_cmd);
+	hapd->dpp_pkex_auth_cmd = NULL;
+
+	if (ifaces->dpp_pb_bi) {
+		char id[20];
+
+		os_snprintf(id, sizeof(id), "%u", ifaces->dpp_pb_bi->id);
+		dpp_bootstrap_remove(ifaces->dpp, id);
+		ifaces->dpp_pb_bi = NULL;
+	}
+
+	ifaces->dpp_pb_result_indicated = false;
+
+	str_clear_free(ifaces->dpp_pb_cmd);
+	ifaces->dpp_pb_cmd = NULL;
+}
+
+#endif /* CONFIG_DPP3 */
+
+
+#ifdef CONFIG_DPP2
+bool hostapd_dpp_configurator_connectivity(struct hostapd_data *hapd)
+{
+	return hapd->conf->dpp_configurator_connectivity ||
+		(hapd->iface->interfaces &&
+		 dpp_relay_controller_available(hapd->iface->interfaces->dpp));
+}
+#endif /* CONFIG_DPP2 */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/dpp_hostapd.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/dpp_hostapd.h
new file mode 100644
index 0000000..55f1fce
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/dpp_hostapd.h
@@ -0,0 +1,54 @@
+/*
+ * hostapd / DPP integration
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2018-2020, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DPP_HOSTAPD_H
+#define DPP_HOSTAPD_H
+
+struct dpp_bootstrap_info;
+
+int hostapd_dpp_qr_code(struct hostapd_data *hapd, const char *cmd);
+int hostapd_dpp_nfc_uri(struct hostapd_data *hapd, const char *cmd);
+int hostapd_dpp_nfc_handover_req(struct hostapd_data *hapd, const char *cmd);
+int hostapd_dpp_nfc_handover_sel(struct hostapd_data *hapd, const char *cmd);
+int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd);
+int hostapd_dpp_listen(struct hostapd_data *hapd, const char *cmd);
+void hostapd_dpp_listen_stop(struct hostapd_data *hapd);
+void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src,
+			   const u8 *buf, size_t len, unsigned int freq);
+void hostapd_dpp_tx_status(struct hostapd_data *hapd, const u8 *dst,
+			   const u8 *data, size_t data_len, int ok);
+struct wpabuf *
+hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa,
+			    const u8 *query, size_t query_len,
+			    const u8 *data, size_t data_len);
+void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok);
+int hostapd_dpp_configurator_add(struct hostapd_data *hapd, const char *cmd);
+int hostapd_dpp_configurator_remove(struct hostapd_data *hapd, const char *id);
+int hostapd_dpp_configurator_sign(struct hostapd_data *hapd, const char *cmd);
+int hostapd_dpp_configurator_get_key(struct hostapd_data *hapd, unsigned int id,
+				     char *buf, size_t buflen);
+int hostapd_dpp_pkex_add(struct hostapd_data *hapd, const char *cmd);
+int hostapd_dpp_pkex_remove(struct hostapd_data *hapd, const char *id);
+void hostapd_dpp_stop(struct hostapd_data *hapd);
+int hostapd_dpp_init(struct hostapd_data *hapd);
+void hostapd_dpp_deinit(struct hostapd_data *hapd);
+void hostapd_dpp_init_global(struct hapd_interfaces *ifaces);
+void hostapd_dpp_deinit_global(struct hapd_interfaces *ifaces);
+
+int hostapd_dpp_controller_start(struct hostapd_data *hapd, const char *cmd);
+int hostapd_dpp_chirp(struct hostapd_data *hapd, const char *cmd);
+void hostapd_dpp_chirp_stop(struct hostapd_data *hapd);
+void hostapd_dpp_remove_bi(void *ctx, struct dpp_bootstrap_info *bi);
+int hostapd_dpp_push_button(struct hostapd_data *hapd, const char *cmd);
+void hostapd_dpp_push_button_stop(struct hostapd_data *hapd);
+bool hostapd_dpp_configurator_connectivity(struct hostapd_data *hapd);
+int hostapd_dpp_add_controller(struct hostapd_data *hapd, const char *cmd);
+void hostapd_dpp_remove_controller(struct hostapd_data *hapd, const char *cmd);
+
+#endif /* DPP_HOSTAPD_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/drv_callbacks.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/drv_callbacks.c
new file mode 100644
index 0000000..b0bba99
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/drv_callbacks.c
@@ -0,0 +1,2310 @@
+/*
+ * hostapd / Callback functions for driver wrappers
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "radius/radius.h"
+#include "drivers/driver.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/wpa_ctrl.h"
+#include "common/dpp.h"
+#include "common/sae.h"
+#include "common/hw_features_common.h"
+#include "crypto/random.h"
+#include "p2p/p2p.h"
+#include "wps/wps.h"
+#include "fst/fst.h"
+#include "wnm_ap.h"
+#include "hostapd.h"
+#include "ieee802_11.h"
+#include "ieee802_11_auth.h"
+#include "sta_info.h"
+#include "accounting.h"
+#include "tkip_countermeasures.h"
+#include "ieee802_1x.h"
+#include "wpa_auth.h"
+#include "wps_hostapd.h"
+#include "ap_drv_ops.h"
+#include "ap_config.h"
+#include "ap_mlme.h"
+#include "hw_features.h"
+#include "dfs.h"
+#include "beacon.h"
+#include "mbo_ap.h"
+#include "dpp_hostapd.h"
+#include "fils_hlp.h"
+#include "neighbor_db.h"
+
+
+#ifdef CONFIG_FILS
+void hostapd_notify_assoc_fils_finish(struct hostapd_data *hapd,
+				      struct sta_info *sta)
+{
+	u16 reply_res = WLAN_STATUS_SUCCESS;
+	struct ieee802_11_elems elems;
+	u8 buf[IEEE80211_MAX_MMPDU_SIZE], *p = buf;
+	int new_assoc;
+
+	wpa_printf(MSG_DEBUG, "%s FILS: Finish association with " MACSTR,
+		   __func__, MAC2STR(sta->addr));
+	eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
+	if (!sta->fils_pending_assoc_req)
+		return;
+
+	ieee802_11_parse_elems(sta->fils_pending_assoc_req,
+			       sta->fils_pending_assoc_req_len, &elems, 0);
+	if (!elems.fils_session) {
+		wpa_printf(MSG_DEBUG, "%s failed to find FILS Session element",
+			   __func__);
+		return;
+	}
+
+	p = hostapd_eid_assoc_fils_session(sta->wpa_sm, p,
+					   elems.fils_session,
+					   sta->fils_hlp_resp);
+
+	reply_res = hostapd_sta_assoc(hapd, sta->addr,
+				      sta->fils_pending_assoc_is_reassoc,
+				      WLAN_STATUS_SUCCESS,
+				      buf, p - buf);
+	ap_sta_set_authorized(hapd, sta, 1);
+	new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0;
+	sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
+	sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
+	hostapd_set_sta_flags(hapd, sta);
+	wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FILS);
+	ieee802_1x_notify_port_enabled(sta->eapol_sm, 1);
+	hostapd_new_assoc_sta(hapd, sta, !new_assoc);
+	os_free(sta->fils_pending_assoc_req);
+	sta->fils_pending_assoc_req = NULL;
+	sta->fils_pending_assoc_req_len = 0;
+	wpabuf_free(sta->fils_hlp_resp);
+	sta->fils_hlp_resp = NULL;
+	wpabuf_free(sta->hlp_dhcp_discover);
+	sta->hlp_dhcp_discover = NULL;
+	fils_hlp_deinit(hapd);
+
+	/*
+	 * Remove the station in case transmission of a success response fails
+	 * (the STA was added associated to the driver) or if the station was
+	 * previously added unassociated.
+	 */
+	if (reply_res != WLAN_STATUS_SUCCESS || sta->added_unassoc) {
+		hostapd_drv_sta_remove(hapd, sta->addr);
+		sta->added_unassoc = 0;
+	}
+}
+#endif /* CONFIG_FILS */
+
+
+static bool check_sa_query_need(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	if ((sta->flags &
+	     (WLAN_STA_ASSOC | WLAN_STA_MFP | WLAN_STA_AUTHORIZED)) !=
+	    (WLAN_STA_ASSOC | WLAN_STA_MFP | WLAN_STA_AUTHORIZED))
+		return false;
+
+	if (!sta->sa_query_timed_out && sta->sa_query_count > 0)
+		ap_check_sa_query_timeout(hapd, sta);
+
+	if (!sta->sa_query_timed_out && (sta->auth_alg != WLAN_AUTH_FT)) {
+		/*
+		 * STA has already been associated with MFP and SA Query timeout
+		 * has not been reached. Reject the association attempt
+		 * temporarily and start SA Query, if one is not pending.
+		 */
+		if (sta->sa_query_count == 0)
+			ap_sta_start_sa_query(hapd, sta);
+
+		return true;
+	}
+
+	return false;
+}
+
+
+int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
+			const u8 *req_ies, size_t req_ies_len, int reassoc)
+{
+	struct sta_info *sta;
+	int new_assoc;
+	enum wpa_validate_result res;
+	struct ieee802_11_elems elems;
+	const u8 *ie;
+	size_t ielen;
+	u8 buf[sizeof(struct ieee80211_mgmt) + 1024];
+	u8 *p = buf;
+	u16 reason = WLAN_REASON_UNSPECIFIED;
+	int status = WLAN_STATUS_SUCCESS;
+	const u8 *p2p_dev_addr = NULL;
+	struct hostapd_ubus_request req = {
+		.type = HOSTAPD_UBUS_ASSOC_REQ,
+		.addr = addr,
+	};
+
+	if (addr == NULL) {
+		/*
+		 * This could potentially happen with unexpected event from the
+		 * driver wrapper. This was seen at least in one case where the
+		 * driver ended up being set to station mode while hostapd was
+		 * running, so better make sure we stop processing such an
+		 * event here.
+		 */
+		wpa_printf(MSG_DEBUG,
+			   "hostapd_notif_assoc: Skip event with no address");
+		return -1;
+	}
+
+	if (is_multicast_ether_addr(addr) ||
+	    is_zero_ether_addr(addr) ||
+	    os_memcmp(addr, hapd->own_addr, ETH_ALEN) == 0) {
+		/* Do not process any frames with unexpected/invalid SA so that
+		 * we do not add any state for unexpected STA addresses or end
+		 * up sending out frames to unexpected destination. */
+		wpa_printf(MSG_DEBUG, "%s: Invalid SA=" MACSTR
+			   " in received indication - ignore this indication silently",
+			   __func__, MAC2STR(addr));
+		return 0;
+	}
+
+	random_add_randomness(addr, ETH_ALEN);
+
+	hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_INFO, "associated");
+
+	ieee802_11_parse_elems(req_ies, req_ies_len, &elems, 0);
+	if (elems.wps_ie) {
+		ie = elems.wps_ie - 2;
+		ielen = elems.wps_ie_len + 2;
+		wpa_printf(MSG_DEBUG, "STA included WPS IE in (Re)AssocReq");
+	} else if (elems.rsn_ie) {
+		ie = elems.rsn_ie - 2;
+		ielen = elems.rsn_ie_len + 2;
+		wpa_printf(MSG_DEBUG, "STA included RSN IE in (Re)AssocReq");
+	} else if (elems.wpa_ie) {
+		ie = elems.wpa_ie - 2;
+		ielen = elems.wpa_ie_len + 2;
+		wpa_printf(MSG_DEBUG, "STA included WPA IE in (Re)AssocReq");
+#ifdef CONFIG_HS20
+	} else if (elems.osen) {
+		ie = elems.osen - 2;
+		ielen = elems.osen_len + 2;
+		wpa_printf(MSG_DEBUG, "STA included OSEN IE in (Re)AssocReq");
+#endif /* CONFIG_HS20 */
+	} else {
+		ie = NULL;
+		ielen = 0;
+		wpa_printf(MSG_DEBUG,
+			   "STA did not include WPS/RSN/WPA IE in (Re)AssocReq");
+	}
+
+	sta = ap_get_sta(hapd, addr);
+	if (sta) {
+		ap_sta_no_session_timeout(hapd, sta);
+		accounting_sta_stop(hapd, sta);
+
+		/*
+		 * Make sure that the previously registered inactivity timer
+		 * will not remove the STA immediately.
+		 */
+		sta->timeout_next = STA_NULLFUNC;
+	} else {
+		sta = ap_sta_add(hapd, addr);
+		if (sta == NULL) {
+			hostapd_drv_sta_disassoc(hapd, addr,
+						 WLAN_REASON_DISASSOC_AP_BUSY);
+			return -1;
+		}
+	}
+	sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2);
+
+	/*
+	 * ACL configurations to the drivers (implementing AP SME and ACL
+	 * offload) without hostapd's knowledge, can result in a disconnection
+	 * though the driver accepts the connection. Skip the hostapd check for
+	 * ACL if the driver supports ACL offload to avoid potentially
+	 * conflicting ACL rules.
+	 */
+	if (hapd->iface->drv_max_acl_mac_addrs == 0 &&
+	    hostapd_check_acl(hapd, addr, NULL) != HOSTAPD_ACL_ACCEPT) {
+		wpa_printf(MSG_INFO, "STA " MACSTR " not allowed to connect",
+			   MAC2STR(addr));
+		reason = WLAN_REASON_UNSPECIFIED;
+		goto fail;
+	}
+
+	if (hostapd_ubus_handle_event(hapd, &req)) {
+		wpa_printf(MSG_DEBUG, "Station " MACSTR " assoc rejected by ubus handler.\n",
+			   MAC2STR(req.addr));
+		goto fail;
+	}
+
+#ifdef CONFIG_P2P
+	if (elems.p2p) {
+		wpabuf_free(sta->p2p_ie);
+		sta->p2p_ie = ieee802_11_vendor_ie_concat(req_ies, req_ies_len,
+							  P2P_IE_VENDOR_TYPE);
+		if (sta->p2p_ie)
+			p2p_dev_addr = p2p_get_go_dev_addr(sta->p2p_ie);
+	}
+#endif /* CONFIG_P2P */
+
+#ifdef NEED_AP_MLME
+	if (elems.ht_capabilities &&
+	    (hapd->iface->conf->ht_capab &
+	     HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) {
+		struct ieee80211_ht_capabilities *ht_cap =
+			(struct ieee80211_ht_capabilities *)
+			elems.ht_capabilities;
+
+		if (le_to_host16(ht_cap->ht_capabilities_info) &
+		    HT_CAP_INFO_40MHZ_INTOLERANT)
+			ht40_intolerant_add(hapd->iface, sta);
+	}
+#endif /* NEED_AP_MLME */
+
+	check_ext_capab(hapd, sta, elems.ext_capab, elems.ext_capab_len);
+
+#ifdef CONFIG_HS20
+	wpabuf_free(sta->hs20_ie);
+	if (elems.hs20 && elems.hs20_len > 4) {
+		sta->hs20_ie = wpabuf_alloc_copy(elems.hs20 + 4,
+						 elems.hs20_len - 4);
+	} else
+		sta->hs20_ie = NULL;
+
+	wpabuf_free(sta->roaming_consortium);
+	if (elems.roaming_cons_sel)
+		sta->roaming_consortium = wpabuf_alloc_copy(
+			elems.roaming_cons_sel + 4,
+			elems.roaming_cons_sel_len - 4);
+	else
+		sta->roaming_consortium = NULL;
+#endif /* CONFIG_HS20 */
+
+#ifdef CONFIG_FST
+	wpabuf_free(sta->mb_ies);
+	if (hapd->iface->fst)
+		sta->mb_ies = mb_ies_by_info(&elems.mb_ies);
+	else
+		sta->mb_ies = NULL;
+#endif /* CONFIG_FST */
+
+	mbo_ap_check_sta_assoc(hapd, sta, &elems);
+
+	ap_copy_sta_supp_op_classes(sta, elems.supp_op_classes,
+				    elems.supp_op_classes_len);
+
+	if (hapd->conf->wpa) {
+		if (ie == NULL || ielen == 0) {
+#ifdef CONFIG_WPS
+			if (hapd->conf->wps_state) {
+				wpa_printf(MSG_DEBUG,
+					   "STA did not include WPA/RSN IE in (Re)Association Request - possible WPS use");
+				sta->flags |= WLAN_STA_MAYBE_WPS;
+				goto skip_wpa_check;
+			}
+#endif /* CONFIG_WPS */
+
+			wpa_printf(MSG_DEBUG, "No WPA/RSN IE from STA");
+			reason = WLAN_REASON_INVALID_IE;
+			status = WLAN_STATUS_INVALID_IE;
+			goto fail;
+		}
+#ifdef CONFIG_WPS
+		if (hapd->conf->wps_state && ie[0] == 0xdd && ie[1] >= 4 &&
+		    os_memcmp(ie + 2, "\x00\x50\xf2\x04", 4) == 0) {
+			struct wpabuf *wps;
+
+			if (check_sa_query_need(hapd, sta)) {
+				status = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
+
+				p = hostapd_eid_assoc_comeback_time(hapd, sta,
+								    p);
+
+				hostapd_sta_assoc(hapd, addr, reassoc, status,
+						  buf, p - buf);
+				return 0;
+			}
+
+			sta->flags |= WLAN_STA_WPS;
+			wps = ieee802_11_vendor_ie_concat(ie, ielen,
+							  WPS_IE_VENDOR_TYPE);
+			if (wps) {
+				if (wps_is_20(wps)) {
+					wpa_printf(MSG_DEBUG,
+						   "WPS: STA supports WPS 2.0");
+					sta->flags |= WLAN_STA_WPS2;
+				}
+				wpabuf_free(wps);
+			}
+			goto skip_wpa_check;
+		}
+#endif /* CONFIG_WPS */
+
+		if (check_sa_query_need(hapd, sta)) {
+			status = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
+
+			p = hostapd_eid_assoc_comeback_time(hapd, sta, p);
+
+			hostapd_sta_assoc(hapd, addr, reassoc, status, buf,
+					  p - buf);
+			return 0;
+		}
+
+		if (sta->wpa_sm == NULL)
+			sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
+							sta->addr,
+							p2p_dev_addr);
+		if (sta->wpa_sm == NULL) {
+			wpa_printf(MSG_ERROR,
+				   "Failed to initialize WPA state machine");
+			return -1;
+		}
+		res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
+					  hapd->iface->freq,
+					  ie, ielen,
+					  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);
+		reason = WLAN_REASON_INVALID_IE;
+		status = WLAN_STATUS_INVALID_IE;
+		switch (res) {
+		case WPA_IE_OK:
+			reason = WLAN_REASON_UNSPECIFIED;
+			status = WLAN_STATUS_SUCCESS;
+			break;
+		case WPA_INVALID_IE:
+			reason = WLAN_REASON_INVALID_IE;
+			status = WLAN_STATUS_INVALID_IE;
+			break;
+		case WPA_INVALID_GROUP:
+			reason = WLAN_REASON_GROUP_CIPHER_NOT_VALID;
+			status = WLAN_STATUS_GROUP_CIPHER_NOT_VALID;
+			break;
+		case WPA_INVALID_PAIRWISE:
+			reason = WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID;
+			status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
+			break;
+		case WPA_INVALID_AKMP:
+			reason = WLAN_REASON_AKMP_NOT_VALID;
+			status = WLAN_STATUS_AKMP_NOT_VALID;
+			break;
+		case WPA_NOT_ENABLED:
+			reason = WLAN_REASON_INVALID_IE;
+			status = WLAN_STATUS_INVALID_IE;
+			break;
+		case WPA_ALLOC_FAIL:
+			reason = WLAN_REASON_UNSPECIFIED;
+			status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			break;
+		case WPA_MGMT_FRAME_PROTECTION_VIOLATION:
+			reason = WLAN_REASON_INVALID_IE;
+			status = WLAN_STATUS_INVALID_IE;
+			break;
+		case WPA_INVALID_MGMT_GROUP_CIPHER:
+			reason = WLAN_REASON_CIPHER_SUITE_REJECTED;
+			status = WLAN_STATUS_CIPHER_REJECTED_PER_POLICY;
+			break;
+		case WPA_INVALID_MDIE:
+			reason = WLAN_REASON_INVALID_MDE;
+			status = WLAN_STATUS_INVALID_MDIE;
+			break;
+		case WPA_INVALID_PROTO:
+			reason = WLAN_REASON_INVALID_IE;
+			status = WLAN_STATUS_INVALID_IE;
+			break;
+		case WPA_INVALID_PMKID:
+			reason = WLAN_REASON_INVALID_PMKID;
+			status = WLAN_STATUS_INVALID_PMKID;
+			break;
+		case WPA_DENIED_OTHER_REASON:
+			reason = WLAN_REASON_UNSPECIFIED;
+			status = WLAN_STATUS_ASSOC_DENIED_UNSPEC;
+			break;
+		}
+		if (status != WLAN_STATUS_SUCCESS) {
+			wpa_printf(MSG_DEBUG,
+				   "WPA/RSN information element rejected? (res %u)",
+				   res);
+			wpa_hexdump(MSG_DEBUG, "IE", ie, ielen);
+			goto fail;
+		}
+
+		if (wpa_auth_uses_mfp(sta->wpa_sm))
+			sta->flags |= WLAN_STA_MFP;
+		else
+			sta->flags &= ~WLAN_STA_MFP;
+
+#ifdef CONFIG_IEEE80211R_AP
+		if (sta->auth_alg == WLAN_AUTH_FT) {
+			status = wpa_ft_validate_reassoc(sta->wpa_sm, req_ies,
+							 req_ies_len);
+			if (status != WLAN_STATUS_SUCCESS) {
+				if (status == WLAN_STATUS_INVALID_PMKID)
+					reason = WLAN_REASON_INVALID_IE;
+				if (status == WLAN_STATUS_INVALID_MDIE)
+					reason = WLAN_REASON_INVALID_IE;
+				if (status == WLAN_STATUS_INVALID_FTIE)
+					reason = WLAN_REASON_INVALID_IE;
+				goto fail;
+			}
+		}
+#endif /* CONFIG_IEEE80211R_AP */
+#ifdef CONFIG_SAE
+		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,
+					      WLAN_RSNX_CAPAB_SAE_H2E)) {
+			wpa_printf(MSG_INFO, "SAE: " MACSTR
+				   " indicates support for SAE H2E, but did not use it",
+				   MAC2STR(sta->addr));
+			status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			reason = WLAN_REASON_UNSPECIFIED;
+			goto fail;
+		}
+#endif /* CONFIG_SAE */
+	} else if (hapd->conf->wps_state) {
+#ifdef CONFIG_WPS
+		struct wpabuf *wps;
+
+		if (req_ies)
+			wps = ieee802_11_vendor_ie_concat(req_ies, req_ies_len,
+							  WPS_IE_VENDOR_TYPE);
+		else
+			wps = NULL;
+#ifdef CONFIG_WPS_STRICT
+		if (wps && wps_validate_assoc_req(wps) < 0) {
+			reason = WLAN_REASON_INVALID_IE;
+			status = WLAN_STATUS_INVALID_IE;
+			wpabuf_free(wps);
+			goto fail;
+		}
+#endif /* CONFIG_WPS_STRICT */
+		if (wps) {
+			sta->flags |= WLAN_STA_WPS;
+			if (wps_is_20(wps)) {
+				wpa_printf(MSG_DEBUG,
+					   "WPS: STA supports WPS 2.0");
+				sta->flags |= WLAN_STA_WPS2;
+			}
+		} else
+			sta->flags |= WLAN_STA_MAYBE_WPS;
+		wpabuf_free(wps);
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_HS20
+	} else if (hapd->conf->osen) {
+		if (elems.osen == NULL) {
+			hostapd_logger(
+				hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+				HOSTAPD_LEVEL_INFO,
+				"No HS 2.0 OSEN element in association request");
+			return WLAN_STATUS_INVALID_IE;
+		}
+
+		wpa_printf(MSG_DEBUG, "HS 2.0: OSEN association");
+		if (sta->wpa_sm == NULL)
+			sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
+							sta->addr, NULL);
+		if (sta->wpa_sm == NULL) {
+			wpa_printf(MSG_WARNING,
+				   "Failed to initialize WPA state machine");
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+		}
+		if (wpa_validate_osen(hapd->wpa_auth, sta->wpa_sm,
+				      elems.osen - 2, elems.osen_len + 2) < 0)
+			return WLAN_STATUS_INVALID_IE;
+#endif /* CONFIG_HS20 */
+	}
+#ifdef CONFIG_WPS
+skip_wpa_check:
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_MBO
+	if (hapd->conf->mbo_enabled && (hapd->conf->wpa & 2) &&
+	    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");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+#endif /* CONFIG_MBO */
+
+#ifdef CONFIG_IEEE80211R_AP
+	p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, buf, sizeof(buf),
+					sta->auth_alg, req_ies, req_ies_len,
+					!elems.rsnxe);
+	if (!p) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to write AssocResp IEs");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+
+#ifdef CONFIG_FILS
+	if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+	    sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+	    sta->auth_alg == WLAN_AUTH_FILS_PK) {
+		int delay_assoc = 0;
+
+		if (!req_ies)
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+		if (!wpa_fils_validate_fils_session(sta->wpa_sm, req_ies,
+						    req_ies_len,
+						    sta->fils_session)) {
+			wpa_printf(MSG_DEBUG,
+				   "FILS: Session validation failed");
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+		}
+
+		res = wpa_fils_validate_key_confirm(sta->wpa_sm, req_ies,
+						    req_ies_len);
+		if (res < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "FILS: Key Confirm validation failed");
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+		}
+
+		if (fils_process_hlp(hapd, sta, req_ies, req_ies_len) > 0) {
+			wpa_printf(MSG_DEBUG,
+				   "FILS: Delaying Assoc Response (HLP)");
+			delay_assoc = 1;
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "FILS: Going ahead with Assoc Response (no HLP)");
+		}
+
+		if (sta) {
+			wpa_printf(MSG_DEBUG, "FILS: HLP callback cleanup");
+			eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
+			os_free(sta->fils_pending_assoc_req);
+			sta->fils_pending_assoc_req = NULL;
+			sta->fils_pending_assoc_req_len = 0;
+			wpabuf_free(sta->fils_hlp_resp);
+			sta->fils_hlp_resp = NULL;
+			sta->fils_drv_assoc_finish = 0;
+		}
+
+		if (sta && delay_assoc && status == WLAN_STATUS_SUCCESS) {
+			u8 *req_tmp;
+
+			req_tmp = os_malloc(req_ies_len);
+			if (!req_tmp) {
+				wpa_printf(MSG_DEBUG,
+					   "FILS: buffer allocation failed for assoc req");
+				goto fail;
+			}
+			os_memcpy(req_tmp, req_ies, req_ies_len);
+			sta->fils_pending_assoc_req = req_tmp;
+			sta->fils_pending_assoc_req_len = req_ies_len;
+			sta->fils_pending_assoc_is_reassoc = reassoc;
+			sta->fils_drv_assoc_finish = 1;
+			wpa_printf(MSG_DEBUG,
+				   "FILS: Waiting for HLP processing before sending (Re)Association Response frame to "
+				   MACSTR, MAC2STR(sta->addr));
+			eloop_register_timeout(
+				0, hapd->conf->fils_hlp_wait_time * 1024,
+				fils_hlp_timeout, hapd, sta);
+			return 0;
+		}
+		p = hostapd_eid_assoc_fils_session(sta->wpa_sm, p,
+						   elems.fils_session,
+						   sta->fils_hlp_resp);
+		wpa_hexdump(MSG_DEBUG, "FILS Assoc Resp BUF (IEs)",
+			    buf, p - buf);
+	}
+#endif /* CONFIG_FILS */
+
+#ifdef CONFIG_OWE
+	if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
+	    wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE &&
+	    elems.owe_dh) {
+		u8 *npos;
+		u16 ret_status;
+
+		npos = owe_assoc_req_process(hapd, sta,
+					     elems.owe_dh, elems.owe_dh_len,
+					     p, sizeof(buf) - (p - buf),
+					     &ret_status);
+		status = ret_status;
+		if (npos)
+			p = npos;
+
+		if (!npos &&
+		    status == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) {
+			hostapd_sta_assoc(hapd, addr, reassoc, ret_status, buf,
+					  p - buf);
+			return 0;
+		}
+
+		if (!npos || status != WLAN_STATUS_SUCCESS)
+			goto fail;
+	}
+#endif /* CONFIG_OWE */
+
+#ifdef CONFIG_DPP2
+		dpp_pfs_free(sta->dpp_pfs);
+		sta->dpp_pfs = NULL;
+
+		if ((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) {
+			sta->dpp_pfs = dpp_pfs_init(
+				wpabuf_head(hapd->conf->dpp_netaccesskey),
+				wpabuf_len(hapd->conf->dpp_netaccesskey));
+			if (!sta->dpp_pfs) {
+				wpa_printf(MSG_DEBUG,
+					   "DPP: Could not initialize PFS");
+				/* Try to continue without PFS */
+				goto pfs_fail;
+			}
+
+			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;
+				reason = WLAN_REASON_UNSPECIFIED;
+				goto fail;
+			}
+		}
+
+		wpa_auth_set_dpp_z(sta->wpa_sm, sta->dpp_pfs ?
+				   sta->dpp_pfs->secret : NULL);
+	pfs_fail:
+#endif /* CONFIG_DPP2 */
+
+	if (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 defined(CONFIG_IEEE80211R_AP) || defined(CONFIG_FILS) || defined(CONFIG_OWE)
+	hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf);
+
+	if (sta->auth_alg == WLAN_AUTH_FT ||
+	    sta->auth_alg == WLAN_AUTH_FILS_SK ||
+	    sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+	    sta->auth_alg == WLAN_AUTH_FILS_PK)
+		ap_sta_set_authorized(hapd, sta, 1);
+#else /* CONFIG_IEEE80211R_AP || CONFIG_FILS */
+	/* Keep compiler silent about unused variables */
+	if (status) {
+	}
+#endif /* CONFIG_IEEE80211R_AP || CONFIG_FILS */
+
+	new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0;
+	sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
+	sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
+
+	hostapd_set_sta_flags(hapd, sta);
+
+	if (reassoc && (sta->auth_alg == WLAN_AUTH_FT))
+		wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT);
+#ifdef CONFIG_FILS
+	else if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+		 sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+		 sta->auth_alg == WLAN_AUTH_FILS_PK)
+		wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FILS);
+#endif /* CONFIG_FILS */
+	else
+		wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC);
+
+	hostapd_new_assoc_sta(hapd, sta, !new_assoc);
+
+	ieee802_1x_notify_port_enabled(sta->eapol_sm, 1);
+
+#ifdef CONFIG_P2P
+	if (req_ies) {
+		p2p_group_notif_assoc(hapd->p2p_group, sta->addr,
+				      req_ies, req_ies_len);
+	}
+#endif /* CONFIG_P2P */
+
+	return 0;
+
+fail:
+#ifdef CONFIG_IEEE80211R_AP
+	if (status >= 0)
+		hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf);
+#endif /* CONFIG_IEEE80211R_AP */
+	hostapd_drv_sta_disassoc(hapd, sta->addr, reason);
+	ap_free_sta(hapd, sta);
+	return -1;
+}
+
+
+void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr)
+{
+	struct sta_info *sta;
+
+	if (addr == NULL) {
+		/*
+		 * This could potentially happen with unexpected event from the
+		 * driver wrapper. This was seen at least in one case where the
+		 * driver ended up reporting a station mode event while hostapd
+		 * was running, so better make sure we stop processing such an
+		 * event here.
+		 */
+		wpa_printf(MSG_DEBUG,
+			   "hostapd_notif_disassoc: Skip event with no address");
+		return;
+	}
+
+	hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_INFO, "disassociated");
+
+	sta = ap_get_sta(hapd, addr);
+	if (sta == NULL) {
+		wpa_printf(MSG_DEBUG,
+			   "Disassociation notification for unknown STA "
+			   MACSTR, MAC2STR(addr));
+		return;
+	}
+
+	ap_sta_set_authorized(hapd, sta, 0);
+	sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
+	hostapd_set_sta_flags(hapd, sta);
+	wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC);
+	sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
+	ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
+	ap_free_sta(hapd, sta);
+}
+
+
+void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr)
+{
+	struct sta_info *sta = ap_get_sta(hapd, addr);
+
+	if (!sta || !hapd->conf->disassoc_low_ack || sta->agreed_to_steer)
+		return;
+
+	hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_INFO,
+		       "disconnected due to excessive missing ACKs");
+	hostapd_drv_sta_disassoc(hapd, addr, WLAN_REASON_DISASSOC_LOW_ACK);
+	ap_sta_disassociate(hapd, sta, WLAN_REASON_DISASSOC_LOW_ACK);
+}
+
+
+void hostapd_event_sta_opmode_changed(struct hostapd_data *hapd, const u8 *addr,
+				      enum smps_mode smps_mode,
+				      enum chan_width chan_width, u8 rx_nss)
+{
+	struct sta_info *sta = ap_get_sta(hapd, addr);
+	const char *txt;
+
+	if (!sta)
+		return;
+
+	switch (smps_mode) {
+	case SMPS_AUTOMATIC:
+		txt = "automatic";
+		break;
+	case SMPS_OFF:
+		txt = "off";
+		break;
+	case SMPS_DYNAMIC:
+		txt = "dynamic";
+		break;
+	case SMPS_STATIC:
+		txt = "static";
+		break;
+	default:
+		txt = NULL;
+		break;
+	}
+	if (txt) {
+		wpa_msg(hapd->msg_ctx, MSG_INFO, STA_OPMODE_SMPS_MODE_CHANGED
+			MACSTR " %s", MAC2STR(addr), txt);
+	}
+
+	switch (chan_width) {
+	case CHAN_WIDTH_20_NOHT:
+		txt = "20(no-HT)";
+		break;
+	case CHAN_WIDTH_20:
+		txt = "20";
+		break;
+	case CHAN_WIDTH_40:
+		txt = "40";
+		break;
+	case CHAN_WIDTH_80:
+		txt = "80";
+		break;
+	case CHAN_WIDTH_80P80:
+		txt = "80+80";
+		break;
+	case CHAN_WIDTH_160:
+		txt = "160";
+		break;
+	case CHAN_WIDTH_320:
+		txt = "320";
+		break;
+	default:
+		txt = NULL;
+		break;
+	}
+	if (txt) {
+		wpa_msg(hapd->msg_ctx, MSG_INFO, STA_OPMODE_MAX_BW_CHANGED
+			MACSTR " %s", MAC2STR(addr), txt);
+	}
+
+	if (rx_nss != 0xff) {
+		wpa_msg(hapd->msg_ctx, MSG_INFO, STA_OPMODE_N_SS_CHANGED
+			MACSTR " %d", MAC2STR(addr), rx_nss);
+	}
+}
+
+
+void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
+			     int offset, int width, int cf1, int cf2,
+			     u16 punct_bitmap, int finished)
+{
+#ifdef NEED_AP_MLME
+	int channel, chwidth, is_dfs0, is_dfs;
+	u8 seg0_idx = 0, seg1_idx = 0;
+	size_t i;
+
+	hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_INFO,
+		       "driver %s channel switch: 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,
+		       punct_bitmap);
+
+	if (!hapd->iface->current_mode) {
+		hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_WARNING,
+			       "ignore channel switch since the interface is not yet ready");
+		return;
+	}
+
+	/* Check if any of configured channels require DFS */
+	is_dfs0 = hostapd_is_dfs_required(hapd->iface);
+	hapd->iface->freq = freq;
+
+	channel = hostapd_hw_get_channel(hapd, freq);
+	if (!channel) {
+		hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_WARNING,
+			       "driver switched to bad channel!");
+		return;
+	}
+
+	switch (width) {
+	case CHAN_WIDTH_80:
+		chwidth = CONF_OPER_CHWIDTH_80MHZ;
+		break;
+	case CHAN_WIDTH_80P80:
+		chwidth = CONF_OPER_CHWIDTH_80P80MHZ;
+		break;
+	case CHAN_WIDTH_160:
+		chwidth = CONF_OPER_CHWIDTH_160MHZ;
+		break;
+	case CHAN_WIDTH_320:
+		chwidth = CONF_OPER_CHWIDTH_320MHZ;
+		break;
+	case CHAN_WIDTH_20_NOHT:
+	case CHAN_WIDTH_20:
+	case CHAN_WIDTH_40:
+	default:
+		chwidth = CONF_OPER_CHWIDTH_USE_HT;
+		break;
+	}
+
+	switch (hapd->iface->current_mode->mode) {
+	case HOSTAPD_MODE_IEEE80211A:
+		if (cf1 == 5935)
+			seg0_idx = (cf1 - 5925) / 5;
+		else if (cf1 > 5950)
+			seg0_idx = (cf1 - 5950) / 5;
+		else if (cf1 > 5000)
+			seg0_idx = (cf1 - 5000) / 5;
+
+		if (cf2 == 5935)
+			seg1_idx = (cf2 - 5925) / 5;
+		else if (cf2 > 5950)
+			seg1_idx = (cf2 - 5950) / 5;
+		else if (cf2 > 5000)
+			seg1_idx = (cf2 - 5000) / 5;
+		break;
+	default:
+		ieee80211_freq_to_chan(cf1, &seg0_idx);
+		ieee80211_freq_to_chan(cf2, &seg1_idx);
+		break;
+	}
+
+	hapd->iconf->channel = channel;
+	hapd->iconf->ieee80211n = ht;
+	if (!ht) {
+		hapd->iconf->ieee80211ac = 0;
+	} else if (hapd->iconf->ch_switch_vht_config) {
+		/* CHAN_SWITCH VHT config */
+		if (hapd->iconf->ch_switch_vht_config &
+		    CH_SWITCH_VHT_ENABLED)
+			hapd->iconf->ieee80211ac = 1;
+		else if (hapd->iconf->ch_switch_vht_config &
+			 CH_SWITCH_VHT_DISABLED)
+			hapd->iconf->ieee80211ac = 0;
+	} else if (hapd->iconf->ch_switch_he_config) {
+		/* CHAN_SWITCH HE config */
+		if (hapd->iconf->ch_switch_he_config &
+		    CH_SWITCH_HE_ENABLED)
+			hapd->iconf->ieee80211ax = 1;
+		else if (hapd->iconf->ch_switch_he_config &
+			 CH_SWITCH_HE_DISABLED)
+			hapd->iconf->ieee80211ax = 0;
+#ifdef CONFIG_IEEE80211BE
+	} else if (hapd->iconf->ch_switch_eht_config) {
+		/* CHAN_SWITCH EHT config */
+		if (hapd->iconf->ch_switch_eht_config &
+		    CH_SWITCH_EHT_ENABLED) {
+			hapd->iconf->ieee80211be = 1;
+			hapd->iconf->ieee80211ax = 1;
+			if (!is_6ghz_freq(hapd->iface->freq))
+				hapd->iconf->ieee80211ac = 1;
+		} else if (hapd->iconf->ch_switch_eht_config &
+			   CH_SWITCH_EHT_DISABLED)
+			hapd->iconf->ieee80211be = 0;
+#endif /* CONFIG_IEEE80211BE */
+	}
+	hapd->iconf->ch_switch_vht_config = 0;
+	hapd->iconf->ch_switch_he_config = 0;
+	hapd->iconf->ch_switch_eht_config = 0;
+
+	if (width == CHAN_WIDTH_40 || width == CHAN_WIDTH_80 ||
+	    width == CHAN_WIDTH_80P80 || width == CHAN_WIDTH_160 ||
+	    width == CHAN_WIDTH_320)
+		hapd->iconf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
+	else if (width == CHAN_WIDTH_20 || width == CHAN_WIDTH_20_NOHT)
+		hapd->iconf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
+
+	hapd->iconf->secondary_channel = offset;
+	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)
+			hapd->iconf->vht_capab |=
+				VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+		else if (chwidth == CONF_OPER_CHWIDTH_80P80MHZ)
+			hapd->iconf->vht_capab |=
+				VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
+	}
+
+	is_dfs = ieee80211_is_dfs(freq, hapd->iface->hw_features,
+				  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 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, punct_bitmap);
+	if (!finished)
+		return;
+
+	if (hapd->csa_in_progress &&
+	    freq == hapd->cs_freq_params.freq) {
+		hostapd_cleanup_cs_params(hapd);
+		ieee802_11_set_beacon(hapd);
+
+		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_CSA_FINISHED
+			"freq=%d dfs=%d", freq, is_dfs);
+	} else if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) {
+		/* Complete AP configuration for the first bring up. */
+		if (is_dfs0 > 0 &&
+		    hostapd_is_dfs_required(hapd->iface) <= 0 &&
+		    hapd->iface->state != HAPD_IFACE_ENABLED) {
+			/* Fake a CAC start bit to skip setting channel */
+			hapd->iface->cac_started = 1;
+			hostapd_setup_interface_complete(hapd->iface, 0);
+		}
+		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_CSA_FINISHED
+			"freq=%d dfs=%d", freq, is_dfs);
+	} else if (is_dfs &&
+		   hostapd_is_dfs_required(hapd->iface) &&
+		   !hostapd_is_dfs_chan_available(hapd->iface) &&
+		   !hapd->iface->cac_started) {
+		hostapd_disable_iface(hapd->iface);
+		hostapd_enable_iface(hapd->iface);
+	}
+
+	for (i = 0; i < hapd->iface->num_bss; i++)
+		hostapd_neighbor_set_own_report(hapd->iface->bss[i]);
+
+#ifdef CONFIG_OCV
+	if (hapd->conf->ocv &&
+	    !(hapd->iface->drv_flags2 &
+	      WPA_DRIVER_FLAGS2_SA_QUERY_OFFLOAD_AP)) {
+		struct sta_info *sta;
+		bool check_sa_query = false;
+
+		for (sta = hapd->sta_list; sta; sta = sta->next) {
+			if (wpa_auth_uses_ocv(sta->wpa_sm) &&
+			    !(sta->flags & WLAN_STA_WNM_SLEEP_MODE)) {
+				sta->post_csa_sa_query = 1;
+				check_sa_query = true;
+			}
+		}
+
+		if (check_sa_query) {
+			wpa_printf(MSG_DEBUG,
+				   "OCV: Check post-CSA SA Query initiation in 15 seconds");
+			eloop_register_timeout(15, 0,
+					       hostapd_ocv_check_csa_sa_query,
+					       hapd, NULL);
+		}
+	}
+#endif /* CONFIG_OCV */
+#endif /* NEED_AP_MLME */
+}
+
+
+void hostapd_event_connect_failed_reason(struct hostapd_data *hapd,
+					 const u8 *addr, int reason_code)
+{
+	switch (reason_code) {
+	case MAX_CLIENT_REACHED:
+		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_REJECTED_MAX_STA MACSTR,
+			MAC2STR(addr));
+		break;
+	case BLOCKED_CLIENT:
+		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_REJECTED_BLOCKED_STA MACSTR,
+			MAC2STR(addr));
+		break;
+	}
+}
+
+
+#ifdef CONFIG_ACS
+void hostapd_acs_channel_selected(struct hostapd_data *hapd,
+				  struct acs_selected_channels *acs_res)
+{
+	int ret, i;
+	int err = 0;
+	struct hostapd_channel_data *pri_chan;
+
+	if (hapd->iconf->channel) {
+		wpa_printf(MSG_INFO, "ACS: Channel was already set to %d",
+			   hapd->iconf->channel);
+		return;
+	}
+
+	hapd->iface->freq = acs_res->pri_freq;
+
+	if (!hapd->iface->current_mode) {
+		for (i = 0; i < hapd->iface->num_hw_features; i++) {
+			struct hostapd_hw_modes *mode =
+				&hapd->iface->hw_features[i];
+
+			if (mode->mode == acs_res->hw_mode) {
+				if (hapd->iface->freq > 0 &&
+				    !hw_get_chan(mode->mode,
+						 hapd->iface->freq,
+						 hapd->iface->hw_features,
+						 hapd->iface->num_hw_features))
+					continue;
+				hapd->iface->current_mode = mode;
+				break;
+			}
+		}
+		if (!hapd->iface->current_mode) {
+			hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_WARNING,
+				       "driver selected to bad hw_mode");
+			err = 1;
+			goto out;
+		}
+	}
+
+	if (!acs_res->pri_freq) {
+		hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_WARNING,
+			       "driver switched to bad channel");
+		err = 1;
+		goto out;
+	}
+	pri_chan = hw_get_channel_freq(hapd->iface->current_mode->mode,
+				       acs_res->pri_freq, NULL,
+				       hapd->iface->hw_features,
+				       hapd->iface->num_hw_features);
+	if (!pri_chan) {
+		wpa_printf(MSG_ERROR,
+			   "ACS: Could not determine primary channel number from pri_freq %u",
+			   acs_res->pri_freq);
+		err = 1;
+		goto out;
+	}
+
+	hapd->iconf->channel = pri_chan->chan;
+	hapd->iconf->acs = 1;
+
+	if (acs_res->sec_freq == 0)
+		hapd->iconf->secondary_channel = 0;
+	else if (acs_res->sec_freq < acs_res->pri_freq)
+		hapd->iconf->secondary_channel = -1;
+	else if (acs_res->sec_freq > acs_res->pri_freq)
+		hapd->iconf->secondary_channel = 1;
+	else {
+		wpa_printf(MSG_ERROR, "Invalid secondary channel!");
+		err = 1;
+		goto out;
+	}
+
+	hapd->iconf->edmg_channel = acs_res->edmg_channel;
+
+	if (hapd->iface->conf->ieee80211ac || hapd->iface->conf->ieee80211ax) {
+		/* set defaults for backwards compatibility */
+		hostapd_set_oper_centr_freq_seg1_idx(hapd->iconf, 0);
+		hostapd_set_oper_centr_freq_seg0_idx(hapd->iconf, 0);
+		hostapd_set_oper_chwidth(hapd->iconf, CONF_OPER_CHWIDTH_USE_HT);
+		if (acs_res->ch_width == 40) {
+			if (is_6ghz_freq(acs_res->pri_freq))
+				hostapd_set_oper_centr_freq_seg0_idx(
+					hapd->iconf,
+					acs_res->vht_seg0_center_ch);
+		} else if (acs_res->ch_width == 80) {
+			hostapd_set_oper_centr_freq_seg0_idx(
+				hapd->iconf, acs_res->vht_seg0_center_ch);
+			if (acs_res->vht_seg1_center_ch == 0) {
+				hostapd_set_oper_chwidth(
+					hapd->iconf, CONF_OPER_CHWIDTH_80MHZ);
+			} else {
+				hostapd_set_oper_chwidth(
+					hapd->iconf,
+					CONF_OPER_CHWIDTH_80P80MHZ);
+				hostapd_set_oper_centr_freq_seg1_idx(
+					hapd->iconf,
+					acs_res->vht_seg1_center_ch);
+			}
+		} else if (acs_res->ch_width == 160) {
+			hostapd_set_oper_chwidth(hapd->iconf,
+						 CONF_OPER_CHWIDTH_160MHZ);
+			hostapd_set_oper_centr_freq_seg0_idx(
+				hapd->iconf, acs_res->vht_seg1_center_ch);
+		}
+	}
+
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->iface->conf->ieee80211be && acs_res->ch_width == 320) {
+		hostapd_set_oper_chwidth(hapd->iconf, CONF_OPER_CHWIDTH_320MHZ);
+		hostapd_set_oper_centr_freq_seg0_idx(
+			hapd->iconf, acs_res->vht_seg1_center_ch);
+		hostapd_set_oper_centr_freq_seg1_idx(hapd->iconf, 0);
+	}
+
+	if (hapd->iface->conf->ieee80211be && acs_res->puncture_bitmap)
+		hapd->iconf->punct_bitmap = acs_res->puncture_bitmap;
+#endif /* CONFIG_IEEE80211BE */
+
+out:
+	ret = hostapd_acs_completed(hapd->iface, err);
+	if (ret) {
+		wpa_printf(MSG_ERROR,
+			   "ACS: Possibly channel configuration is invalid");
+	}
+}
+#endif /* CONFIG_ACS */
+
+
+int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da,
+			 const u8 *bssid, const u8 *ie, size_t ie_len,
+			 int ssi_signal)
+{
+	size_t i;
+	int ret = 0;
+
+	if (sa == NULL || ie == NULL)
+		return -1;
+
+	random_add_randomness(sa, ETH_ALEN);
+	for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++) {
+		if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx,
+					    sa, da, bssid, ie, ie_len,
+					    ssi_signal) > 0) {
+			ret = 1;
+			break;
+		}
+	}
+	return ret;
+}
+
+
+#ifdef HOSTAPD
+
+#ifdef CONFIG_IEEE80211R_AP
+static void hostapd_notify_auth_ft_finish(void *ctx, const u8 *dst,
+					  const u8 *bssid,
+					  u16 auth_transaction, u16 status,
+					  const u8 *ies, size_t ies_len)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+
+	sta = ap_get_sta(hapd, dst);
+	if (sta == NULL)
+		return;
+
+	hostapd_logger(hapd, dst, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_DEBUG, "authentication OK (FT)");
+	sta->flags |= WLAN_STA_AUTH;
+
+	hostapd_sta_auth(hapd, dst, auth_transaction, status, ies, ies_len);
+}
+#endif /* CONFIG_IEEE80211R_AP */
+
+
+#ifdef CONFIG_FILS
+static void hostapd_notify_auth_fils_finish(struct hostapd_data *hapd,
+					    struct sta_info *sta, u16 resp,
+					    struct wpabuf *data, int pub)
+{
+	if (resp == WLAN_STATUS_SUCCESS) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG, "authentication OK (FILS)");
+		sta->flags |= WLAN_STA_AUTH;
+		wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
+		sta->auth_alg = WLAN_AUTH_FILS_SK;
+		mlme_authenticate_indication(hapd, sta);
+	} else {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "authentication failed (FILS)");
+	}
+
+	hostapd_sta_auth(hapd, sta->addr, 2, resp,
+			 data ? wpabuf_head(data) : NULL,
+			 data ? wpabuf_len(data) : 0);
+	wpabuf_free(data);
+}
+#endif /* CONFIG_FILS */
+
+
+static void hostapd_notif_auth(struct hostapd_data *hapd,
+			       struct auth_info *rx_auth)
+{
+	struct sta_info *sta;
+	u16 status = WLAN_STATUS_SUCCESS;
+	u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN];
+	size_t resp_ies_len = 0;
+
+	sta = ap_get_sta(hapd, rx_auth->peer);
+	if (!sta) {
+		sta = ap_sta_add(hapd, rx_auth->peer);
+		if (sta == NULL) {
+			status = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+			goto fail;
+		}
+	}
+	sta->flags &= ~WLAN_STA_PREAUTH;
+	ieee802_1x_notify_pre_auth(sta->eapol_sm, 0);
+#ifdef CONFIG_IEEE80211R_AP
+	if (rx_auth->auth_type == WLAN_AUTH_FT && hapd->wpa_auth) {
+		sta->auth_alg = WLAN_AUTH_FT;
+		if (sta->wpa_sm == NULL)
+			sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
+							sta->addr, NULL);
+		if (sta->wpa_sm == NULL) {
+			wpa_printf(MSG_DEBUG,
+				   "FT: Failed to initialize WPA state machine");
+			status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+		}
+		wpa_ft_process_auth(sta->wpa_sm, rx_auth->bssid,
+				    rx_auth->auth_transaction, rx_auth->ies,
+				    rx_auth->ies_len,
+				    hostapd_notify_auth_ft_finish, hapd);
+		return;
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+
+#ifdef CONFIG_FILS
+	if (rx_auth->auth_type == WLAN_AUTH_FILS_SK) {
+		sta->auth_alg = WLAN_AUTH_FILS_SK;
+		handle_auth_fils(hapd, sta, rx_auth->ies, rx_auth->ies_len,
+				 rx_auth->auth_type, rx_auth->auth_transaction,
+				 rx_auth->status_code,
+				 hostapd_notify_auth_fils_finish);
+		return;
+	}
+#endif /* CONFIG_FILS */
+
+fail:
+	hostapd_sta_auth(hapd, rx_auth->peer, rx_auth->auth_transaction + 1,
+			 status, resp_ies, resp_ies_len);
+}
+
+
+#ifndef NEED_AP_MLME
+static void hostapd_action_rx(struct hostapd_data *hapd,
+			      struct rx_mgmt *drv_mgmt)
+{
+	struct ieee80211_mgmt *mgmt;
+	struct sta_info *sta;
+	size_t plen __maybe_unused;
+	u16 fc;
+	u8 *action __maybe_unused;
+
+	if (drv_mgmt->frame_len < IEEE80211_HDRLEN + 2 + 1)
+		return;
+
+	plen = drv_mgmt->frame_len - IEEE80211_HDRLEN;
+
+	mgmt = (struct ieee80211_mgmt *) drv_mgmt->frame;
+	fc = le_to_host16(mgmt->frame_control);
+	if (WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_ACTION)
+		return; /* handled by the driver */
+
+	action = (u8 *) &mgmt->u.action.u;
+	wpa_printf(MSG_DEBUG, "RX_ACTION category %u action %u sa " MACSTR
+		   " da " MACSTR " plen %d",
+		   mgmt->u.action.category, *action,
+		   MAC2STR(mgmt->sa), MAC2STR(mgmt->da), (int) plen);
+
+	sta = ap_get_sta(hapd, mgmt->sa);
+	if (sta == NULL) {
+		wpa_printf(MSG_DEBUG, "%s: station not found", __func__);
+		return;
+	}
+#ifdef CONFIG_IEEE80211R_AP
+	if (mgmt->u.action.category == WLAN_ACTION_FT) {
+		wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action, plen);
+		return;
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+	if (mgmt->u.action.category == WLAN_ACTION_SA_QUERY) {
+		ieee802_11_sa_query_action(hapd, mgmt, drv_mgmt->frame_len);
+		return;
+	}
+#ifdef CONFIG_WNM_AP
+	if (mgmt->u.action.category == WLAN_ACTION_WNM) {
+		ieee802_11_rx_wnm_action_ap(hapd, mgmt, drv_mgmt->frame_len);
+		return;
+	}
+#endif /* CONFIG_WNM_AP */
+#ifdef CONFIG_FST
+	if (mgmt->u.action.category == WLAN_ACTION_FST && hapd->iface->fst) {
+		fst_rx_action(hapd->iface->fst, mgmt, drv_mgmt->frame_len);
+		return;
+	}
+#endif /* CONFIG_FST */
+#ifdef CONFIG_DPP
+	if (plen >= 2 + 4 &&
+	    mgmt->u.action.u.vs_public_action.action ==
+	    WLAN_PA_VENDOR_SPECIFIC &&
+	    WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) ==
+	    OUI_WFA &&
+	    mgmt->u.action.u.vs_public_action.variable[0] ==
+	    DPP_OUI_TYPE) {
+		const u8 *pos, *end;
+
+		pos = mgmt->u.action.u.vs_public_action.oui;
+		end = drv_mgmt->frame + drv_mgmt->frame_len;
+		hostapd_dpp_rx_action(hapd, mgmt->sa, pos, end - pos,
+				      drv_mgmt->freq);
+		return;
+	}
+#endif /* CONFIG_DPP */
+}
+#endif /* NEED_AP_MLME */
+
+
+#ifdef NEED_AP_MLME
+
+static struct hostapd_data *
+switch_link_hapd(struct hostapd_data *hapd, int link_id)
+{
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->conf->mld_ap && link_id >= 0) {
+		struct hostapd_data *link_bss;
+
+		link_bss = hostapd_mld_get_link_bss(hapd, link_id);
+		if (link_bss)
+			return link_bss;
+	}
+#endif /* CONFIG_IEEE80211BE */
+
+	return hapd;
+}
+
+
+#define HAPD_BROADCAST ((struct hostapd_data *) -1)
+
+static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
+					    const u8 *bssid)
+{
+	size_t i;
+
+	if (bssid == NULL)
+		return NULL;
+	if (bssid[0] == 0xff && bssid[1] == 0xff && bssid[2] == 0xff &&
+	    bssid[3] == 0xff && bssid[4] == 0xff && bssid[5] == 0xff)
+		return HAPD_BROADCAST;
+
+	for (i = 0; i < iface->num_bss; i++) {
+		if (os_memcmp(bssid, iface->bss[i]->own_addr, ETH_ALEN) == 0)
+			return iface->bss[i];
+	}
+
+	return NULL;
+}
+
+
+static void hostapd_rx_from_unknown_sta(struct hostapd_data *hapd,
+					const u8 *bssid, const u8 *addr,
+					int wds)
+{
+	hapd = get_hapd_bssid(hapd->iface, bssid);
+	if (hapd == NULL || hapd == HAPD_BROADCAST)
+		return;
+
+	ieee802_11_rx_from_unknown(hapd, addr, wds);
+}
+
+
+static int hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt)
+{
+	struct hostapd_iface *iface;
+	const struct ieee80211_hdr *hdr;
+	const u8 *bssid;
+	struct hostapd_frame_info fi;
+	int ret;
+	bool is_mld = false;
+
+	hapd = switch_link_hapd(hapd, rx_mgmt->link_id);
+	iface = hapd->iface;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (hapd->ext_mgmt_frame_handling) {
+		size_t hex_len = 2 * rx_mgmt->frame_len + 1;
+		char *hex = os_malloc(hex_len);
+
+		if (hex) {
+			wpa_snprintf_hex(hex, hex_len, rx_mgmt->frame,
+					 rx_mgmt->frame_len);
+			wpa_msg(hapd->msg_ctx, MSG_INFO, "MGMT-RX %s", hex);
+			os_free(hex);
+		}
+		return 1;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	hdr = (const struct ieee80211_hdr *) rx_mgmt->frame;
+	bssid = get_hdr_bssid(hdr, rx_mgmt->frame_len);
+	if (bssid == NULL)
+		return 0;
+
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->conf->mld_ap &&
+	    os_memcmp(hapd->mld_addr, bssid, ETH_ALEN) == 0)
+		is_mld = true;
+#endif /* CONFIG_IEEE80211BE */
+
+	if (!is_mld)
+		hapd = get_hapd_bssid(iface, bssid);
+
+	if (!hapd) {
+		u16 fc = le_to_host16(hdr->frame_control);
+
+		/*
+		 * Drop frames to unknown BSSIDs except for Beacon frames which
+		 * could be used to update neighbor information.
+		 */
+		if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+		    WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON)
+			hapd = iface->bss[0];
+		else
+			return 0;
+	}
+
+	os_memset(&fi, 0, sizeof(fi));
+	fi.freq = rx_mgmt->freq;
+	fi.datarate = rx_mgmt->datarate;
+	fi.ssi_signal = rx_mgmt->ssi_signal;
+
+	if (hapd == HAPD_BROADCAST) {
+		size_t i;
+
+		ret = 0;
+		for (i = 0; i < iface->num_bss; i++) {
+			/* if bss is set, driver will call this function for
+			 * each bss individually. */
+			if (rx_mgmt->drv_priv &&
+			    (iface->bss[i]->drv_priv != rx_mgmt->drv_priv))
+				continue;
+
+			if (ieee802_11_mgmt(iface->bss[i], rx_mgmt->frame,
+					    rx_mgmt->frame_len, &fi) > 0)
+				ret = 1;
+		}
+	} else
+		ret = ieee802_11_mgmt(hapd, rx_mgmt->frame, rx_mgmt->frame_len,
+				      &fi);
+
+	random_add_randomness(&fi, sizeof(fi));
+
+	return ret;
+}
+
+
+static void hostapd_mgmt_tx_cb(struct hostapd_data *hapd, const u8 *buf,
+			       size_t len, u16 stype, int ok, int link_id)
+{
+	struct ieee80211_hdr *hdr;
+	struct hostapd_data *orig_hapd, *tmp_hapd;
+
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->conf->mld_ap && link_id != -1) {
+		tmp_hapd = hostapd_mld_get_link_bss(hapd, link_id);
+		if (tmp_hapd)
+			hapd = tmp_hapd;
+	}
+#endif /* CONFIG_IEEE80211BE */
+	orig_hapd = hapd;
+
+	hdr = (struct ieee80211_hdr *) buf;
+	tmp_hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len));
+	if (tmp_hapd) {
+		hapd = tmp_hapd;
+#ifdef CONFIG_IEEE80211BE
+	} else if (hapd->conf->mld_ap &&
+		   os_memcmp(hapd->mld_addr, get_hdr_bssid(hdr, len),
+			     ETH_ALEN) == 0) {
+		/* AP MLD address match - use hapd pointer as-is */
+#endif /* CONFIG_IEEE80211BE */
+	} else {
+		return;
+	}
+
+	if (hapd == HAPD_BROADCAST) {
+		if (stype != WLAN_FC_STYPE_ACTION || len <= 25 ||
+		    buf[24] != WLAN_ACTION_PUBLIC)
+			return;
+		hapd = get_hapd_bssid(orig_hapd->iface, hdr->addr2);
+		if (!hapd || hapd == HAPD_BROADCAST)
+			return;
+		/*
+		 * Allow processing of TX status for a Public Action frame that
+		 * used wildcard BBSID.
+		 */
+	}
+	ieee802_11_mgmt_cb(hapd, buf, len, stype, ok);
+}
+
+#endif /* NEED_AP_MLME */
+
+
+static int hostapd_event_new_sta(struct hostapd_data *hapd, const u8 *addr)
+{
+	struct sta_info *sta = ap_get_sta(hapd, addr);
+
+	if (sta)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "Data frame from unknown STA " MACSTR
+		   " - adding a new STA", MAC2STR(addr));
+	sta = ap_sta_add(hapd, addr);
+	if (sta) {
+		hostapd_new_assoc_sta(hapd, sta, 0);
+	} else {
+		wpa_printf(MSG_DEBUG, "Failed to add STA entry for " MACSTR,
+			   MAC2STR(addr));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static struct hostapd_data * hostapd_find_by_sta(struct hostapd_iface *iface,
+						 const u8 *src)
+{
+	struct sta_info *sta;
+	unsigned int j;
+
+	for (j = 0; j < iface->num_bss; j++) {
+		sta = ap_get_sta(iface->bss[j], src);
+		if (sta && sta->flags & WLAN_STA_ASSOC)
+			return iface->bss[j];
+	}
+
+	return NULL;
+}
+
+
+static void hostapd_event_eapol_rx(struct hostapd_data *hapd, const u8 *src,
+				   const u8 *data, size_t data_len,
+				   enum frame_encryption encrypted,
+				   int link_id)
+{
+	struct hostapd_data *orig_hapd = hapd;
+
+#ifdef CONFIG_IEEE80211BE
+	if (link_id != -1) {
+		struct hostapd_data *h_hapd;
+
+		hapd = switch_link_hapd(hapd, link_id);
+		h_hapd = hostapd_find_by_sta(hapd->iface, src);
+		if (!h_hapd)
+			h_hapd = hostapd_find_by_sta(orig_hapd->iface, src);
+		if (h_hapd)
+			hapd = h_hapd;
+	} else if (hapd->conf->mld_ap) {
+		unsigned int i;
+
+		/* Search for STA on other MLO BSSs */
+		for (i = 0; i < hapd->iface->interfaces->count; i++) {
+			struct hostapd_iface *h =
+				hapd->iface->interfaces->iface[i];
+			struct hostapd_data *h_hapd = h->bss[0];
+			struct hostapd_bss_config *hconf = h_hapd->conf;
+
+			if (!hconf->mld_ap ||
+			    hconf->mld_id != hapd->conf->mld_id)
+				continue;
+
+			h_hapd = hostapd_find_by_sta(h, src);
+			if (h_hapd) {
+				hapd = h_hapd;
+				break;
+			}
+		}
+	} else {
+		hapd = hostapd_find_by_sta(hapd->iface, src);
+	}
+#else /* CONFIG_IEEE80211BE */
+	hapd = hostapd_find_by_sta(hapd->iface, src);
+#endif /* CONFIG_IEEE80211BE */
+
+	if (!hapd) {
+		/* WLAN cases need to have an existing association, but non-WLAN
+		 * cases (mainly, wired IEEE 802.1X) need to be able to process
+		 * EAPOL frames from new devices that do not yet have a STA
+		 * entry and as such, do not get a match in
+		 * hostapd_find_by_sta(). */
+		wpa_printf(MSG_DEBUG,
+			   "No STA-specific hostapd instance for EAPOL RX found - fall back to initial context");
+		hapd = orig_hapd;
+	}
+
+	ieee802_1x_receive(hapd, src, data, data_len, encrypted);
+}
+
+#endif /* HOSTAPD */
+
+
+static struct hostapd_channel_data *
+hostapd_get_mode_chan(struct hostapd_hw_modes *mode, unsigned int freq)
+{
+	int i;
+	struct hostapd_channel_data *chan;
+
+	for (i = 0; i < mode->num_channels; i++) {
+		chan = &mode->channels[i];
+		if ((unsigned int) chan->freq == freq)
+			return chan;
+	}
+
+	return NULL;
+}
+
+
+static struct hostapd_channel_data * hostapd_get_mode_channel(
+	struct hostapd_iface *iface, unsigned int freq)
+{
+	int i;
+	struct hostapd_channel_data *chan;
+
+	for (i = 0; i < iface->num_hw_features; i++) {
+		if (hostapd_hw_skip_mode(iface, &iface->hw_features[i]))
+			continue;
+		chan = hostapd_get_mode_chan(&iface->hw_features[i], freq);
+		if (chan)
+			return chan;
+	}
+
+	return NULL;
+}
+
+
+static void hostapd_update_nf(struct hostapd_iface *iface,
+			      struct hostapd_channel_data *chan,
+			      struct freq_survey *survey)
+{
+	if (!iface->chans_surveyed) {
+		chan->min_nf = survey->nf;
+		iface->lowest_nf = survey->nf;
+	} else {
+		if (dl_list_empty(&chan->survey_list))
+			chan->min_nf = survey->nf;
+		else if (survey->nf < chan->min_nf)
+			chan->min_nf = survey->nf;
+		if (survey->nf < iface->lowest_nf)
+			iface->lowest_nf = survey->nf;
+	}
+}
+
+
+static void hostapd_single_channel_get_survey(struct hostapd_iface *iface,
+					      struct survey_results *survey_res)
+{
+	struct hostapd_channel_data *chan;
+	struct freq_survey *survey;
+	u64 divisor, dividend;
+
+	survey = dl_list_first(&survey_res->survey_list, struct freq_survey,
+			       list);
+	if (!survey || !survey->freq)
+		return;
+
+	chan = hostapd_get_mode_channel(iface, survey->freq);
+	if (!chan || chan->flag & HOSTAPD_CHAN_DISABLED)
+		return;
+
+	wpa_printf(MSG_DEBUG,
+		   "Single Channel Survey: (freq=%d channel_time=%ld channel_time_busy=%ld)",
+		   survey->freq,
+		   (unsigned long int) survey->channel_time,
+		   (unsigned long int) survey->channel_time_busy);
+
+	if (survey->channel_time > iface->last_channel_time &&
+	    survey->channel_time > survey->channel_time_busy) {
+		dividend = survey->channel_time_busy -
+			iface->last_channel_time_busy;
+		divisor = survey->channel_time - iface->last_channel_time;
+
+		iface->channel_utilization = dividend * 255 / divisor;
+		wpa_printf(MSG_DEBUG, "Channel Utilization: %d",
+			   iface->channel_utilization);
+	}
+	iface->last_channel_time = survey->channel_time;
+	iface->last_channel_time_busy = survey->channel_time_busy;
+}
+
+
+void hostapd_event_get_survey(struct hostapd_iface *iface,
+			      struct survey_results *survey_results)
+{
+	struct freq_survey *survey, *tmp;
+	struct hostapd_channel_data *chan;
+
+	if (dl_list_empty(&survey_results->survey_list)) {
+		wpa_printf(MSG_DEBUG, "No survey data received");
+		return;
+	}
+
+	if (survey_results->freq_filter) {
+		hostapd_single_channel_get_survey(iface, survey_results);
+		return;
+	}
+
+	dl_list_for_each_safe(survey, tmp, &survey_results->survey_list,
+			      struct freq_survey, list) {
+		chan = hostapd_get_mode_channel(iface, survey->freq);
+		if (!chan)
+			continue;
+		if (chan->flag & HOSTAPD_CHAN_DISABLED)
+			continue;
+
+		dl_list_del(&survey->list);
+		dl_list_add_tail(&chan->survey_list, &survey->list);
+
+		hostapd_update_nf(iface, chan, survey);
+
+		iface->chans_surveyed++;
+	}
+}
+
+
+#ifdef HOSTAPD
+#ifdef NEED_AP_MLME
+
+static void hostapd_event_iface_unavailable(struct hostapd_data *hapd)
+{
+	wpa_printf(MSG_DEBUG, "Interface %s is unavailable -- stopped",
+		   hapd->conf->iface);
+
+	if (hapd->csa_in_progress) {
+		wpa_printf(MSG_INFO, "CSA failed (%s was stopped)",
+			   hapd->conf->iface);
+		hostapd_switch_channel_fallback(hapd->iface,
+						&hapd->cs_freq_params);
+	}
+}
+
+
+static void hostapd_event_dfs_radar_detected(struct hostapd_data *hapd,
+					     struct dfs_event *radar)
+{
+	wpa_printf(MSG_DEBUG, "DFS radar detected on %d MHz", radar->freq);
+	hostapd_dfs_radar_detected(hapd->iface, radar->freq, radar->ht_enabled,
+				   radar->chan_offset, radar->chan_width,
+				   radar->cf1, radar->cf2);
+}
+
+
+static void hostapd_event_dfs_pre_cac_expired(struct hostapd_data *hapd,
+					      struct dfs_event *radar)
+{
+	wpa_printf(MSG_DEBUG, "DFS Pre-CAC expired on %d MHz", radar->freq);
+	hostapd_dfs_pre_cac_expired(hapd->iface, radar->freq, radar->ht_enabled,
+				    radar->chan_offset, radar->chan_width,
+				    radar->cf1, radar->cf2);
+}
+
+
+static void hostapd_event_dfs_cac_finished(struct hostapd_data *hapd,
+					   struct dfs_event *radar)
+{
+	wpa_printf(MSG_DEBUG, "DFS CAC finished on %d MHz", radar->freq);
+	hostapd_dfs_complete_cac(hapd->iface, 1, radar->freq, radar->ht_enabled,
+				 radar->chan_offset, radar->chan_width,
+				 radar->cf1, radar->cf2);
+}
+
+
+static void hostapd_event_dfs_cac_aborted(struct hostapd_data *hapd,
+					  struct dfs_event *radar)
+{
+	wpa_printf(MSG_DEBUG, "DFS CAC aborted on %d MHz", radar->freq);
+	hostapd_dfs_complete_cac(hapd->iface, 0, radar->freq, radar->ht_enabled,
+				 radar->chan_offset, radar->chan_width,
+				 radar->cf1, radar->cf2);
+}
+
+
+static void hostapd_event_dfs_nop_finished(struct hostapd_data *hapd,
+					   struct dfs_event *radar)
+{
+	wpa_printf(MSG_DEBUG, "DFS NOP finished on %d MHz", radar->freq);
+	hostapd_dfs_nop_finished(hapd->iface, radar->freq, radar->ht_enabled,
+				 radar->chan_offset, radar->chan_width,
+				 radar->cf1, radar->cf2);
+}
+
+
+static void hostapd_event_dfs_cac_started(struct hostapd_data *hapd,
+					  struct dfs_event *radar)
+{
+	wpa_printf(MSG_DEBUG, "DFS offload CAC started on %d MHz", radar->freq);
+	hostapd_dfs_start_cac(hapd->iface, radar->freq, radar->ht_enabled,
+			      radar->chan_offset, radar->chan_width,
+			      radar->cf1, radar->cf2);
+}
+
+#endif /* NEED_AP_MLME */
+
+
+static void hostapd_event_wds_sta_interface_status(struct hostapd_data *hapd,
+						   int istatus,
+						   const char *ifname,
+						   const u8 *addr)
+{
+	struct sta_info *sta = ap_get_sta(hapd, addr);
+
+	if (sta) {
+		os_free(sta->ifname_wds);
+		if (istatus == INTERFACE_ADDED)
+			sta->ifname_wds = os_strdup(ifname);
+		else
+			sta->ifname_wds = NULL;
+	}
+
+	wpa_msg(hapd->msg_ctx, MSG_INFO, "%sifname=%s sta_addr=" MACSTR,
+		istatus == INTERFACE_ADDED ?
+		WDS_STA_INTERFACE_ADDED : WDS_STA_INTERFACE_REMOVED,
+		ifname, MAC2STR(addr));
+}
+
+
+#ifdef CONFIG_OWE
+static int hostapd_notif_update_dh_ie(struct hostapd_data *hapd,
+				      const u8 *peer, const u8 *ie,
+				      size_t ie_len)
+{
+	u16 status;
+	struct sta_info *sta;
+	struct ieee802_11_elems elems;
+
+	if (!hapd || !hapd->wpa_auth) {
+		wpa_printf(MSG_DEBUG, "OWE: Invalid hapd context");
+		return -1;
+	}
+	if (!peer) {
+		wpa_printf(MSG_DEBUG, "OWE: Peer unknown");
+		return -1;
+	}
+	if (!(hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE)) {
+		wpa_printf(MSG_DEBUG, "OWE: No OWE AKM configured");
+		status = WLAN_STATUS_AKMP_NOT_VALID;
+		goto err;
+	}
+	if (ieee802_11_parse_elems(ie, ie_len, &elems, 1) == ParseFailed) {
+		wpa_printf(MSG_DEBUG, "OWE: Failed to parse OWE IE for "
+			   MACSTR, MAC2STR(peer));
+		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto err;
+	}
+	status = owe_validate_request(hapd, peer, elems.rsn_ie,
+				      elems.rsn_ie_len,
+				      elems.owe_dh, elems.owe_dh_len);
+	if (status != WLAN_STATUS_SUCCESS)
+		goto err;
+
+	sta = ap_get_sta(hapd, peer);
+	if (sta) {
+		ap_sta_no_session_timeout(hapd, sta);
+		accounting_sta_stop(hapd, sta);
+
+		/*
+		 * Make sure that the previously registered inactivity timer
+		 * will not remove the STA immediately.
+		 */
+		sta->timeout_next = STA_NULLFUNC;
+	} else {
+		sta = ap_sta_add(hapd, peer);
+		if (!sta) {
+			status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto err;
+		}
+	}
+	sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2);
+
+	status = owe_process_rsn_ie(hapd, sta, elems.rsn_ie,
+				    elems.rsn_ie_len, elems.owe_dh,
+				    elems.owe_dh_len);
+	if (status != WLAN_STATUS_SUCCESS)
+		ap_free_sta(hapd, sta);
+
+	return 0;
+err:
+	hostapd_drv_update_dh_ie(hapd, peer, status, NULL, 0);
+	return 0;
+}
+#endif /* CONFIG_OWE */
+
+
+void hostapd_wpa_event(void *ctx, enum wpa_event_type event,
+		       union wpa_event_data *data)
+{
+	struct hostapd_data *hapd = ctx;
+#ifndef CONFIG_NO_STDOUT_DEBUG
+	int level = MSG_DEBUG;
+
+	if (event == EVENT_RX_MGMT && data->rx_mgmt.frame &&
+	    data->rx_mgmt.frame_len >= 24) {
+		const struct ieee80211_hdr *hdr;
+		u16 fc;
+
+		hdr = (const struct ieee80211_hdr *) data->rx_mgmt.frame;
+		fc = le_to_host16(hdr->frame_control);
+		if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+		    WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON)
+			level = MSG_EXCESSIVE;
+		if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+		    WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_REQ)
+			level = MSG_EXCESSIVE;
+	}
+
+	wpa_dbg(hapd->msg_ctx, level, "Event %s (%d) received",
+		event_to_string(event), event);
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+	switch (event) {
+	case EVENT_MICHAEL_MIC_FAILURE:
+		michael_mic_failure(hapd, data->michael_mic_failure.src, 1);
+		break;
+	case EVENT_SCAN_RESULTS:
+		if (hapd->iface->scan_cb)
+			hapd->iface->scan_cb(hapd->iface);
+		break;
+	case EVENT_WPS_BUTTON_PUSHED:
+		hostapd_wps_button_pushed(hapd, NULL);
+		break;
+#ifdef NEED_AP_MLME
+	case EVENT_TX_STATUS:
+		switch (data->tx_status.type) {
+		case WLAN_FC_TYPE_MGMT:
+			hostapd_mgmt_tx_cb(hapd, data->tx_status.data,
+					   data->tx_status.data_len,
+					   data->tx_status.stype,
+					   data->tx_status.ack,
+					   data->tx_status.link_id);
+			break;
+		case WLAN_FC_TYPE_DATA:
+			hostapd_tx_status(hapd, data->tx_status.dst,
+					  data->tx_status.data,
+					  data->tx_status.data_len,
+					  data->tx_status.ack);
+			break;
+		}
+		break;
+	case EVENT_EAPOL_TX_STATUS:
+		hapd = switch_link_hapd(hapd, data->eapol_tx_status.link_id);
+		hostapd_eapol_tx_status(hapd, data->eapol_tx_status.dst,
+					data->eapol_tx_status.data,
+					data->eapol_tx_status.data_len,
+					data->eapol_tx_status.ack);
+		break;
+	case EVENT_DRIVER_CLIENT_POLL_OK:
+		hostapd_client_poll_ok(hapd, data->client_poll.addr);
+		break;
+	case EVENT_RX_FROM_UNKNOWN:
+		hostapd_rx_from_unknown_sta(hapd, data->rx_from_unknown.bssid,
+					    data->rx_from_unknown.addr,
+					    data->rx_from_unknown.wds);
+		break;
+#endif /* NEED_AP_MLME */
+	case EVENT_RX_MGMT:
+		if (!data->rx_mgmt.frame)
+			break;
+#ifdef NEED_AP_MLME
+		hostapd_mgmt_rx(hapd, &data->rx_mgmt);
+#else /* NEED_AP_MLME */
+		hostapd_action_rx(hapd, &data->rx_mgmt);
+#endif /* NEED_AP_MLME */
+		break;
+	case EVENT_RX_PROBE_REQ:
+		if (data->rx_probe_req.sa == NULL ||
+		    data->rx_probe_req.ie == NULL)
+			break;
+		hostapd_probe_req_rx(hapd, data->rx_probe_req.sa,
+				     data->rx_probe_req.da,
+				     data->rx_probe_req.bssid,
+				     data->rx_probe_req.ie,
+				     data->rx_probe_req.ie_len,
+				     data->rx_probe_req.ssi_signal);
+		break;
+	case EVENT_NEW_STA:
+		hostapd_event_new_sta(hapd, data->new_sta.addr);
+		break;
+	case EVENT_EAPOL_RX:
+		hostapd_event_eapol_rx(hapd, data->eapol_rx.src,
+				       data->eapol_rx.data,
+				       data->eapol_rx.data_len,
+				       data->eapol_rx.encrypted,
+				       data->eapol_rx.link_id);
+		break;
+	case EVENT_ASSOC:
+		if (!data)
+			return;
+		hostapd_notif_assoc(hapd, data->assoc_info.addr,
+				    data->assoc_info.req_ies,
+				    data->assoc_info.req_ies_len,
+				    data->assoc_info.reassoc);
+		break;
+#ifdef CONFIG_OWE
+	case EVENT_UPDATE_DH:
+		if (!data)
+			return;
+		hostapd_notif_update_dh_ie(hapd, data->update_dh.peer,
+					   data->update_dh.ie,
+					   data->update_dh.ie_len);
+		break;
+#endif /* CONFIG_OWE */
+	case EVENT_DISASSOC:
+		if (data)
+			hostapd_notif_disassoc(hapd, data->disassoc_info.addr);
+		break;
+	case EVENT_DEAUTH:
+		if (data)
+			hostapd_notif_disassoc(hapd, data->deauth_info.addr);
+		break;
+	case EVENT_STATION_LOW_ACK:
+		if (!data)
+			break;
+		hostapd_event_sta_low_ack(hapd, data->low_ack.addr);
+		break;
+	case EVENT_AUTH:
+		hostapd_notif_auth(hapd, &data->auth);
+		break;
+	case EVENT_CH_SWITCH_STARTED:
+	case EVENT_CH_SWITCH:
+		if (!data)
+			break;
+		hostapd_event_ch_switch(hapd, data->ch_switch.freq,
+					data->ch_switch.ht_enabled,
+					data->ch_switch.ch_offset,
+					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:
+		if (!data)
+			break;
+		hostapd_event_connect_failed_reason(
+			hapd, data->connect_failed_reason.addr,
+			data->connect_failed_reason.code);
+		break;
+	case EVENT_SURVEY:
+		hostapd_event_get_survey(hapd->iface, &data->survey_results);
+		break;
+#ifdef NEED_AP_MLME
+	case EVENT_INTERFACE_UNAVAILABLE:
+		hostapd_event_iface_unavailable(hapd);
+		break;
+	case EVENT_DFS_RADAR_DETECTED:
+		if (!data)
+			break;
+		hostapd_event_dfs_radar_detected(hapd, &data->dfs_event);
+		break;
+	case EVENT_DFS_PRE_CAC_EXPIRED:
+		if (!data)
+			break;
+		hostapd_event_dfs_pre_cac_expired(hapd, &data->dfs_event);
+		break;
+	case EVENT_DFS_CAC_FINISHED:
+		if (!data)
+			break;
+		hostapd_event_dfs_cac_finished(hapd, &data->dfs_event);
+		break;
+	case EVENT_DFS_CAC_ABORTED:
+		if (!data)
+			break;
+		hostapd_event_dfs_cac_aborted(hapd, &data->dfs_event);
+		break;
+	case EVENT_DFS_NOP_FINISHED:
+		if (!data)
+			break;
+		hostapd_event_dfs_nop_finished(hapd, &data->dfs_event);
+		break;
+	case EVENT_CHANNEL_LIST_CHANGED:
+		/* channel list changed (regulatory?), update channel list */
+		/* TODO: check this. hostapd_get_hw_features() initializes
+		 * too much stuff. */
+		/* hostapd_get_hw_features(hapd->iface); */
+		hostapd_channel_list_updated(
+			hapd->iface, data->channel_list_changed.initiator);
+		break;
+	case EVENT_DFS_CAC_STARTED:
+		if (!data)
+			break;
+		hostapd_event_dfs_cac_started(hapd, &data->dfs_event);
+		break;
+#endif /* NEED_AP_MLME */
+	case EVENT_INTERFACE_ENABLED:
+		wpa_msg(hapd->msg_ctx, MSG_INFO, INTERFACE_ENABLED);
+		if (hapd->disabled && hapd->started) {
+			hapd->disabled = 0;
+			/*
+			 * Try to re-enable interface if the driver stopped it
+			 * when the interface got disabled.
+			 */
+			if (hapd->wpa_auth)
+				wpa_auth_reconfig_group_keys(hapd->wpa_auth);
+			else
+				hostapd_reconfig_encryption(hapd);
+			hapd->reenable_beacon = 1;
+			ieee802_11_set_beacon(hapd);
+#ifdef NEED_AP_MLME
+		} else if (hapd->disabled && hapd->iface->cac_started) {
+			wpa_printf(MSG_DEBUG, "DFS: restarting pending CAC");
+			hostapd_handle_dfs(hapd->iface);
+#endif /* NEED_AP_MLME */
+		}
+		break;
+	case EVENT_INTERFACE_DISABLED:
+		hostapd_free_stas(hapd);
+		wpa_msg(hapd->msg_ctx, MSG_INFO, INTERFACE_DISABLED);
+		hapd->disabled = 1;
+		break;
+#ifdef CONFIG_ACS
+	case EVENT_ACS_CHANNEL_SELECTED:
+		hostapd_acs_channel_selected(hapd,
+					     &data->acs_selected_channels);
+		break;
+#endif /* CONFIG_ACS */
+	case EVENT_STATION_OPMODE_CHANGED:
+		hostapd_event_sta_opmode_changed(hapd, data->sta_opmode.addr,
+						 data->sta_opmode.smps_mode,
+						 data->sta_opmode.chan_width,
+						 data->sta_opmode.rx_nss);
+		break;
+	case EVENT_WDS_STA_INTERFACE_STATUS:
+		hostapd_event_wds_sta_interface_status(
+			hapd, data->wds_sta_interface.istatus,
+			data->wds_sta_interface.ifname,
+			data->wds_sta_interface.sta_addr);
+		break;
+#ifdef CONFIG_IEEE80211AX
+	case EVENT_BSS_COLOR_COLLISION:
+		/* The BSS color is shared amongst all BBSs on a specific phy.
+		 * Therefore we always start the color change on the primary
+		 * BSS. */
+		wpa_printf(MSG_DEBUG, "BSS color collision on %s",
+			   hapd->conf->iface);
+		hostapd_switch_color(hapd->iface->bss[0],
+				     data->bss_color_collision.bitmap);
+		break;
+	case EVENT_CCA_STARTED_NOTIFY:
+		wpa_printf(MSG_DEBUG, "CCA started on on %s",
+			   hapd->conf->iface);
+		break;
+	case EVENT_CCA_ABORTED_NOTIFY:
+		wpa_printf(MSG_DEBUG, "CCA aborted on on %s",
+			   hapd->conf->iface);
+		hostapd_cleanup_cca_params(hapd);
+		break;
+	case EVENT_CCA_NOTIFY:
+		wpa_printf(MSG_DEBUG, "CCA finished on on %s",
+			   hapd->conf->iface);
+		hapd->iface->conf->he_op.he_bss_color = hapd->cca_color;
+		hostapd_cleanup_cca_params(hapd);
+		break;
+#endif /* CONFIG_IEEE80211AX */
+	default:
+		wpa_printf(MSG_DEBUG, "Unknown event %d", event);
+		break;
+	}
+}
+
+
+void hostapd_wpa_event_global(void *ctx, enum wpa_event_type event,
+				 union wpa_event_data *data)
+{
+	struct hapd_interfaces *interfaces = ctx;
+	struct hostapd_data *hapd;
+
+	if (event != EVENT_INTERFACE_STATUS)
+		return;
+
+	hapd = hostapd_get_iface(interfaces, data->interface_status.ifname);
+	if (hapd && hapd->driver && hapd->driver->get_ifindex &&
+	    hapd->drv_priv) {
+		unsigned int ifindex;
+
+		ifindex = hapd->driver->get_ifindex(hapd->drv_priv);
+		if (ifindex != data->interface_status.ifindex) {
+			wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
+				"interface status ifindex %d mismatch (%d)",
+				ifindex, data->interface_status.ifindex);
+			return;
+		}
+	}
+	if (hapd)
+		wpa_supplicant_event(hapd, event, data);
+}
+
+#endif /* HOSTAPD */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/eap_user_db.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/eap_user_db.c
new file mode 100644
index 0000000..a510ee3
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/eap_user_db.c
@@ -0,0 +1,290 @@
+/*
+ * hostapd / EAP user database
+ * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#ifdef CONFIG_SQLITE
+#include <sqlite3.h>
+#endif /* CONFIG_SQLITE */
+
+#include "common.h"
+#include "eap_common/eap_wsc_common.h"
+#include "eap_server/eap_methods.h"
+#include "eap_server/eap.h"
+#include "ap_config.h"
+#include "hostapd.h"
+
+#ifdef CONFIG_SQLITE
+
+static void set_user_methods(struct hostapd_eap_user *user, const char *methods)
+{
+	char *buf, *start;
+	int num_methods;
+
+	buf = os_strdup(methods);
+	if (buf == NULL)
+		return;
+
+	os_memset(&user->methods, 0, sizeof(user->methods));
+	num_methods = 0;
+	start = buf;
+	while (*start) {
+		char *pos3 = os_strchr(start, ',');
+		if (pos3)
+			*pos3++ = '\0';
+		user->methods[num_methods].method =
+			eap_server_get_type(start,
+					    &user->methods[num_methods].vendor);
+		if (user->methods[num_methods].vendor == EAP_VENDOR_IETF &&
+		    user->methods[num_methods].method == EAP_TYPE_NONE) {
+			if (os_strcmp(start, "TTLS-PAP") == 0) {
+				user->ttls_auth |= EAP_TTLS_AUTH_PAP;
+				goto skip_eap;
+			}
+			if (os_strcmp(start, "TTLS-CHAP") == 0) {
+				user->ttls_auth |= EAP_TTLS_AUTH_CHAP;
+				goto skip_eap;
+			}
+			if (os_strcmp(start, "TTLS-MSCHAP") == 0) {
+				user->ttls_auth |= EAP_TTLS_AUTH_MSCHAP;
+				goto skip_eap;
+			}
+			if (os_strcmp(start, "TTLS-MSCHAPV2") == 0) {
+				user->ttls_auth |= EAP_TTLS_AUTH_MSCHAPV2;
+				goto skip_eap;
+			}
+			wpa_printf(MSG_INFO, "DB: Unsupported EAP type '%s'",
+				   start);
+			os_free(buf);
+			return;
+		}
+
+		num_methods++;
+		if (num_methods >= EAP_MAX_METHODS)
+			break;
+	skip_eap:
+		if (pos3 == NULL)
+			break;
+		start = pos3;
+	}
+
+	os_free(buf);
+}
+
+
+static int get_user_cb(void *ctx, int argc, char *argv[], char *col[])
+{
+	struct hostapd_eap_user *user = ctx;
+	int i;
+
+	for (i = 0; i < argc; i++) {
+		if (os_strcmp(col[i], "password") == 0 && argv[i]) {
+			bin_clear_free(user->password, user->password_len);
+			user->password_len = os_strlen(argv[i]);
+			user->password = (u8 *) os_strdup(argv[i]);
+			user->next = (void *) 1;
+		} else if (os_strcmp(col[i], "methods") == 0 && argv[i]) {
+			set_user_methods(user, argv[i]);
+		} else if (os_strcmp(col[i], "remediation") == 0 && argv[i]) {
+			user->remediation = strlen(argv[i]) > 0;
+		} else if (os_strcmp(col[i], "t_c_timestamp") == 0 && argv[i]) {
+			user->t_c_timestamp = strtol(argv[i], NULL, 10);
+		}
+	}
+
+	return 0;
+}
+
+
+static int get_wildcard_cb(void *ctx, int argc, char *argv[], char *col[])
+{
+	struct hostapd_eap_user *user = ctx;
+	int i, id = -1, methods = -1;
+	size_t len;
+
+	for (i = 0; i < argc; i++) {
+		if (os_strcmp(col[i], "identity") == 0 && argv[i])
+			id = i;
+		else if (os_strcmp(col[i], "methods") == 0 && argv[i])
+			methods = i;
+	}
+
+	if (id < 0 || methods < 0)
+		return 0;
+
+	len = os_strlen(argv[id]);
+	if (len <= user->identity_len &&
+	    os_memcmp(argv[id], user->identity, len) == 0 &&
+	    (user->password == NULL || len > user->password_len)) {
+		bin_clear_free(user->password, user->password_len);
+		user->password_len = os_strlen(argv[id]);
+		user->password = (u8 *) os_strdup(argv[id]);
+		user->next = (void *) 1;
+		set_user_methods(user, argv[methods]);
+	}
+
+	return 0;
+}
+
+
+static const struct hostapd_eap_user *
+eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity,
+		    size_t identity_len, int phase2)
+{
+	sqlite3 *db;
+	struct hostapd_eap_user *user = NULL;
+	char id_str[256], cmd[300];
+	size_t i;
+	int res;
+
+	if (identity_len >= sizeof(id_str)) {
+		wpa_printf(MSG_DEBUG, "%s: identity len too big: %d >= %d",
+			   __func__, (int) identity_len,
+			   (int) (sizeof(id_str)));
+		return NULL;
+	}
+	os_memcpy(id_str, identity, identity_len);
+	id_str[identity_len] = '\0';
+	for (i = 0; i < identity_len; i++) {
+		if (id_str[i] >= 'a' && id_str[i] <= 'z')
+			continue;
+		if (id_str[i] >= 'A' && id_str[i] <= 'Z')
+			continue;
+		if (id_str[i] >= '0' && id_str[i] <= '9')
+			continue;
+		if (id_str[i] == '-' || id_str[i] == '_' || id_str[i] == '.' ||
+		    id_str[i] == ',' || id_str[i] == '@' || id_str[i] == '\\' ||
+		    id_str[i] == '!' || id_str[i] == '#' || id_str[i] == '%' ||
+		    id_str[i] == '=' || id_str[i] == ' ')
+			continue;
+		wpa_printf(MSG_INFO, "DB: Unsupported character in identity");
+		return NULL;
+	}
+
+	bin_clear_free(hapd->tmp_eap_user.identity,
+		       hapd->tmp_eap_user.identity_len);
+	bin_clear_free(hapd->tmp_eap_user.password,
+		       hapd->tmp_eap_user.password_len);
+	os_memset(&hapd->tmp_eap_user, 0, sizeof(hapd->tmp_eap_user));
+	hapd->tmp_eap_user.phase2 = phase2;
+	hapd->tmp_eap_user.identity = os_zalloc(identity_len + 1);
+	if (hapd->tmp_eap_user.identity == NULL)
+		return NULL;
+	os_memcpy(hapd->tmp_eap_user.identity, identity, identity_len);
+	hapd->tmp_eap_user.identity_len = identity_len;
+
+	if (sqlite3_open(hapd->conf->eap_user_sqlite, &db)) {
+		wpa_printf(MSG_INFO, "DB: Failed to open database %s: %s",
+			   hapd->conf->eap_user_sqlite, sqlite3_errmsg(db));
+		sqlite3_close(db);
+		return NULL;
+	}
+
+	res = os_snprintf(cmd, sizeof(cmd),
+			  "SELECT * FROM users WHERE identity='%s' AND phase2=%d;",
+			  id_str, phase2);
+	if (os_snprintf_error(sizeof(cmd), res))
+		goto fail;
+
+	wpa_printf(MSG_DEBUG, "DB: %s", cmd);
+	if (sqlite3_exec(db, cmd, get_user_cb, &hapd->tmp_eap_user, NULL) !=
+	    SQLITE_OK) {
+		wpa_printf(MSG_DEBUG,
+			   "DB: Failed to complete SQL operation: %s  db: %s",
+			   sqlite3_errmsg(db), hapd->conf->eap_user_sqlite);
+	} else if (hapd->tmp_eap_user.next)
+		user = &hapd->tmp_eap_user;
+
+	if (user == NULL && !phase2) {
+		os_snprintf(cmd, sizeof(cmd),
+			    "SELECT identity,methods FROM wildcards;");
+		wpa_printf(MSG_DEBUG, "DB: %s", cmd);
+		if (sqlite3_exec(db, cmd, get_wildcard_cb, &hapd->tmp_eap_user,
+				 NULL) != SQLITE_OK) {
+			wpa_printf(MSG_DEBUG,
+				   "DB: Failed to complete SQL operation: %s  db: %s",
+				   sqlite3_errmsg(db),
+				   hapd->conf->eap_user_sqlite);
+		} else if (hapd->tmp_eap_user.next) {
+			user = &hapd->tmp_eap_user;
+			os_free(user->identity);
+			user->identity = user->password;
+			user->identity_len = user->password_len;
+			user->password = NULL;
+			user->password_len = 0;
+		}
+	}
+
+fail:
+	sqlite3_close(db);
+
+	return user;
+}
+
+#endif /* CONFIG_SQLITE */
+
+
+const struct hostapd_eap_user *
+hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity,
+		     size_t identity_len, int phase2)
+{
+	const struct hostapd_bss_config *conf = hapd->conf;
+	struct hostapd_eap_user *user = conf->eap_user;
+
+#ifdef CONFIG_WPS
+	if (conf->wps_state && identity_len == WSC_ID_ENROLLEE_LEN &&
+	    os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0) {
+		static struct hostapd_eap_user wsc_enrollee;
+		os_memset(&wsc_enrollee, 0, sizeof(wsc_enrollee));
+		wsc_enrollee.methods[0].method = eap_server_get_type(
+			"WSC", &wsc_enrollee.methods[0].vendor);
+		return &wsc_enrollee;
+	}
+
+	if (conf->wps_state && identity_len == WSC_ID_REGISTRAR_LEN &&
+	    os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0) {
+		static struct hostapd_eap_user wsc_registrar;
+		os_memset(&wsc_registrar, 0, sizeof(wsc_registrar));
+		wsc_registrar.methods[0].method = eap_server_get_type(
+			"WSC", &wsc_registrar.methods[0].vendor);
+		wsc_registrar.password = (u8 *) conf->ap_pin;
+		wsc_registrar.password_len = conf->ap_pin ?
+			os_strlen(conf->ap_pin) : 0;
+		return &wsc_registrar;
+	}
+#endif /* CONFIG_WPS */
+
+	while (user) {
+		if (!phase2 && user->identity == NULL) {
+			/* Wildcard match */
+			break;
+		}
+
+		if (user->phase2 == !!phase2 && user->wildcard_prefix &&
+		    identity_len >= user->identity_len &&
+		    os_memcmp(user->identity, identity, user->identity_len) ==
+		    0) {
+			/* Wildcard prefix match */
+			break;
+		}
+
+		if (user->phase2 == !!phase2 &&
+		    user->identity_len == identity_len &&
+		    os_memcmp(user->identity, identity, identity_len) == 0)
+			break;
+		user = user->next;
+	}
+
+#ifdef CONFIG_SQLITE
+	if (user == NULL && conf->eap_user_sqlite) {
+		return eap_user_sqlite_get(hapd, identity, identity_len,
+					   phase2);
+	}
+#endif /* CONFIG_SQLITE */
+
+	return user;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/eth_p_oui.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/eth_p_oui.c
new file mode 100644
index 0000000..aba901e
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/eth_p_oui.c
@@ -0,0 +1,191 @@
+/*
+ * hostapd / IEEE 802 OUI Extended EtherType 88-B7
+ * Copyright (c) 2016, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "l2_packet/l2_packet.h"
+#include "hostapd.h"
+#include "eth_p_oui.h"
+
+/*
+ * See IEEE Std 802-2014, Clause 9.2.4 for the definition of the OUI Extended
+ * EtherType 88-B7. This file implements this with OUI 00:13:74 and
+ * vendor-specific subtype 0x0001.
+ */
+static const u8 global_oui[] = { 0x00, 0x13, 0x74, 0x00, 0x01 };
+
+struct eth_p_oui_iface {
+	struct dl_list list;
+	char ifname[IFNAMSIZ + 1];
+	struct l2_packet_data *l2;
+	struct dl_list receiver;
+};
+
+struct eth_p_oui_ctx {
+	struct dl_list list;
+	struct eth_p_oui_iface *iface;
+	/* all data needed to deliver and unregister */
+	u8 oui_suffix; /* last byte of OUI */
+	void (*rx_callback)(void *ctx, const u8 *src_addr,
+			    const u8 *dst_addr, u8 oui_suffix,
+			    const u8 *buf, size_t len);
+	void *rx_callback_ctx;
+};
+
+
+void eth_p_oui_deliver(struct eth_p_oui_ctx *ctx, const u8 *src_addr,
+		       const u8 *dst_addr, const u8 *buf, size_t len)
+{
+	ctx->rx_callback(ctx->rx_callback_ctx, src_addr, dst_addr,
+			 ctx->oui_suffix, buf, len);
+}
+
+
+static void eth_p_rx(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
+{
+	struct eth_p_oui_iface *iface = ctx;
+	struct eth_p_oui_ctx *receiver;
+	const struct l2_ethhdr *ethhdr;
+
+	if (len < sizeof(*ethhdr) + sizeof(global_oui) + 1) {
+		/* too short packet */
+		return;
+	}
+
+	ethhdr = (struct l2_ethhdr *) buf;
+	/* trim eth_hdr from buf and len */
+	buf += sizeof(*ethhdr);
+	len -= sizeof(*ethhdr);
+
+	/* verify OUI and vendor-specific subtype match */
+	if (os_memcmp(buf, global_oui, sizeof(global_oui)) != 0)
+		return;
+	buf += sizeof(global_oui);
+	len -= sizeof(global_oui);
+
+	dl_list_for_each(receiver, &iface->receiver,
+			 struct eth_p_oui_ctx, list) {
+		if (buf[0] != receiver->oui_suffix)
+			continue;
+
+		eth_p_oui_deliver(receiver, ethhdr->h_source, ethhdr->h_dest,
+				  buf + 1, len - 1);
+	}
+}
+
+
+struct eth_p_oui_ctx *
+eth_p_oui_register(struct hostapd_data *hapd, const char *ifname, u8 oui_suffix,
+		   void (*rx_callback)(void *ctx, const u8 *src_addr,
+				       const u8 *dst_addr, u8 oui_suffix,
+				       const u8 *buf, size_t len),
+		   void *rx_callback_ctx)
+{
+	struct eth_p_oui_iface *iface;
+	struct eth_p_oui_ctx *receiver;
+	int found = 0;
+	struct hapd_interfaces *interfaces;
+
+	receiver = os_zalloc(sizeof(*receiver));
+	if (!receiver)
+		goto err;
+
+	receiver->oui_suffix = oui_suffix;
+	receiver->rx_callback = rx_callback;
+	receiver->rx_callback_ctx = rx_callback_ctx;
+
+	interfaces = hapd->iface->interfaces;
+
+	dl_list_for_each(iface, &interfaces->eth_p_oui, struct eth_p_oui_iface,
+			 list) {
+		if (os_strcmp(iface->ifname, ifname) != 0)
+			continue;
+		found = 1;
+		break;
+	}
+
+	if (!found) {
+		iface = os_zalloc(sizeof(*iface));
+		if (!iface)
+			goto err;
+
+		os_strlcpy(iface->ifname, ifname, sizeof(iface->ifname));
+		iface->l2 = l2_packet_init(ifname, NULL, ETH_P_OUI, eth_p_rx,
+					   iface, 1);
+		if (!iface->l2) {
+			os_free(iface);
+			goto err;
+		}
+		dl_list_init(&iface->receiver);
+
+		dl_list_add_tail(&interfaces->eth_p_oui, &iface->list);
+	}
+
+	dl_list_add_tail(&iface->receiver, &receiver->list);
+	receiver->iface = iface;
+
+	return receiver;
+err:
+	os_free(receiver);
+	return NULL;
+}
+
+
+void eth_p_oui_unregister(struct eth_p_oui_ctx *ctx)
+{
+	struct eth_p_oui_iface *iface;
+
+	if (!ctx)
+		return;
+
+	iface = ctx->iface;
+
+	dl_list_del(&ctx->list);
+	os_free(ctx);
+
+	if (dl_list_empty(&iface->receiver)) {
+		dl_list_del(&iface->list);
+		l2_packet_deinit(iface->l2);
+		os_free(iface);
+	}
+}
+
+
+int eth_p_oui_send(struct eth_p_oui_ctx *ctx, const u8 *src_addr,
+		   const u8 *dst_addr, const u8 *buf, size_t len)
+{
+	struct eth_p_oui_iface *iface = ctx->iface;
+	u8 *packet, *p;
+	size_t packet_len;
+	int ret;
+	struct l2_ethhdr *ethhdr;
+
+	packet_len = sizeof(*ethhdr) + sizeof(global_oui) + 1 + len;
+	packet = os_zalloc(packet_len);
+	if (!packet)
+		return -1;
+	p = packet;
+
+	ethhdr = (struct l2_ethhdr *) packet;
+	os_memcpy(ethhdr->h_source, src_addr, ETH_ALEN);
+	os_memcpy(ethhdr->h_dest, dst_addr, ETH_ALEN);
+	ethhdr->h_proto = host_to_be16(ETH_P_OUI);
+	p += sizeof(*ethhdr);
+
+	os_memcpy(p, global_oui, sizeof(global_oui));
+	p[sizeof(global_oui)] = ctx->oui_suffix;
+	p += sizeof(global_oui) + 1;
+
+	os_memcpy(p, buf, len);
+
+	ret = l2_packet_send(iface->l2, NULL, 0, packet, packet_len);
+	os_free(packet);
+	return ret;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/eth_p_oui.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/eth_p_oui.h
new file mode 100644
index 0000000..466fdc3
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/eth_p_oui.h
@@ -0,0 +1,28 @@
+/*
+ * hostapd / IEEE 802 OUI Extended Ethertype
+ * Copyright (c) 2016, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef ETH_P_OUI_H
+#define ETH_P_OUI_H
+
+struct eth_p_oui_ctx;
+struct hostapd_data;
+
+/* rx_callback only gets payload after OUI passed as buf */
+struct eth_p_oui_ctx *
+eth_p_oui_register(struct hostapd_data *hapd, const char *ifname, u8 oui_suffix,
+		   void (*rx_callback)(void *ctx, const u8 *src_addr,
+				       const u8 *dst_addr, u8 oui_suffix,
+				       const u8 *buf, size_t len),
+		   void *rx_callback_ctx);
+void eth_p_oui_unregister(struct eth_p_oui_ctx *eth_p_oui);
+int eth_p_oui_send(struct eth_p_oui_ctx *ctx, const u8 *src_addr,
+		   const u8 *dst_addr, const u8 *buf, size_t len);
+void eth_p_oui_deliver(struct eth_p_oui_ctx *ctx, const u8 *src_addr,
+		       const u8 *dst_addr, const u8 *buf, size_t len);
+
+#endif /* ETH_P_OUI_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/fils_hlp.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/fils_hlp.c
new file mode 100644
index 0000000..d64fb8c
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/fils_hlp.c
@@ -0,0 +1,654 @@
+/*
+ * FILS HLP request processing
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/dhcp.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ieee802_11.h"
+#include "fils_hlp.h"
+
+
+static be16 ip_checksum(const void *buf, size_t len)
+{
+	u32 sum = 0;
+	const u16 *pos;
+
+	for (pos = buf; len >= 2; len -= 2)
+		sum += ntohs(*pos++);
+	if (len)
+		sum += ntohs(*pos << 8);
+
+	sum = (sum >> 16) + (sum & 0xffff);
+	sum += sum >> 16;
+	return htons(~sum);
+}
+
+
+static int fils_dhcp_request(struct hostapd_data *hapd, struct sta_info *sta,
+			     struct dhcp_data *dhcpoffer, u8 *dhcpofferend)
+{
+	u8 *pos, *end;
+	struct dhcp_data *dhcp;
+	struct sockaddr_in addr;
+	ssize_t res;
+	const u8 *server_id = NULL;
+
+	if (!sta->hlp_dhcp_discover) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: No pending HLP DHCPDISCOVER available");
+		return -1;
+	}
+
+	/* Convert to DHCPREQUEST, remove rapid commit option, replace requested
+	 * IP address option with yiaddr. */
+	pos = wpabuf_mhead(sta->hlp_dhcp_discover);
+	end = pos + wpabuf_len(sta->hlp_dhcp_discover);
+	dhcp = (struct dhcp_data *) pos;
+	pos = (u8 *) (dhcp + 1);
+	pos += 4; /* skip magic */
+	while (pos < end && *pos != DHCP_OPT_END) {
+		u8 opt, olen;
+
+		opt = *pos++;
+		if (opt == DHCP_OPT_PAD)
+			continue;
+		if (pos >= end)
+			break;
+		olen = *pos++;
+		if (olen > end - pos)
+			break;
+
+		switch (opt) {
+		case DHCP_OPT_MSG_TYPE:
+			if (olen > 0)
+				*pos = DHCPREQUEST;
+			break;
+		case DHCP_OPT_RAPID_COMMIT:
+		case DHCP_OPT_REQUESTED_IP_ADDRESS:
+		case DHCP_OPT_SERVER_ID:
+			/* Remove option */
+			pos -= 2;
+			os_memmove(pos, pos + 2 + olen, end - pos - 2 - olen);
+			end -= 2 + olen;
+			olen = 0;
+			break;
+		}
+		pos += olen;
+	}
+	if (pos >= end || *pos != DHCP_OPT_END) {
+		wpa_printf(MSG_DEBUG, "FILS: Could not update DHCPDISCOVER");
+		return -1;
+	}
+	sta->hlp_dhcp_discover->used = pos - (u8 *) dhcp;
+
+	/* Copy Server ID option from DHCPOFFER to DHCPREQUEST */
+	pos = (u8 *) (dhcpoffer + 1);
+	end = dhcpofferend;
+	pos += 4; /* skip magic */
+	while (pos < end && *pos != DHCP_OPT_END) {
+		u8 opt, olen;
+
+		opt = *pos++;
+		if (opt == DHCP_OPT_PAD)
+			continue;
+		if (pos >= end)
+			break;
+		olen = *pos++;
+		if (olen > end - pos)
+			break;
+
+		switch (opt) {
+		case DHCP_OPT_SERVER_ID:
+			server_id = pos - 2;
+			break;
+		}
+		pos += olen;
+	}
+
+	if (wpabuf_resize(&sta->hlp_dhcp_discover,
+			  6 + 1 + (server_id ? 2 + server_id[1] : 0)))
+		return -1;
+	if (server_id)
+		wpabuf_put_data(sta->hlp_dhcp_discover, server_id,
+				2 + server_id[1]);
+	wpabuf_put_u8(sta->hlp_dhcp_discover, DHCP_OPT_REQUESTED_IP_ADDRESS);
+	wpabuf_put_u8(sta->hlp_dhcp_discover, 4);
+	wpabuf_put_data(sta->hlp_dhcp_discover, &dhcpoffer->your_ip, 4);
+	wpabuf_put_u8(sta->hlp_dhcp_discover, DHCP_OPT_END);
+
+	os_memset(&addr, 0, sizeof(addr));
+	addr.sin_family = AF_INET;
+	addr.sin_addr.s_addr = hapd->conf->dhcp_server.u.v4.s_addr;
+	addr.sin_port = htons(hapd->conf->dhcp_server_port);
+	res = sendto(hapd->dhcp_sock, wpabuf_head(sta->hlp_dhcp_discover),
+		     wpabuf_len(sta->hlp_dhcp_discover), 0,
+		     (const struct sockaddr *) &addr, sizeof(addr));
+	if (res < 0) {
+		wpa_printf(MSG_ERROR, "FILS: DHCP sendto failed: %s",
+			   strerror(errno));
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG,
+		   "FILS: Acting as DHCP rapid commit proxy for %s:%d",
+		   inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
+	wpabuf_free(sta->hlp_dhcp_discover);
+	sta->hlp_dhcp_discover = NULL;
+	sta->fils_dhcp_rapid_commit_proxy = 1;
+	return 0;
+}
+
+
+static void fils_dhcp_handler(int sd, void *eloop_ctx, void *sock_ctx)
+{
+	struct hostapd_data *hapd = sock_ctx;
+	struct sta_info *sta;
+	u8 buf[1500], *pos, *end, *end_opt = NULL;
+	struct dhcp_data *dhcp;
+	struct sockaddr_in addr;
+	socklen_t addr_len;
+	ssize_t res;
+	u8 msgtype = 0;
+	int rapid_commit = 0;
+	struct ip *iph;
+	struct udphdr *udph;
+	struct wpabuf *resp;
+	const u8 *rpos;
+	size_t left, len;
+
+	addr_len = sizeof(addr);
+	res = recvfrom(sd, buf, sizeof(buf), 0,
+		       (struct sockaddr *) &addr, &addr_len);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG, "FILS: DHCP read failed: %s",
+			   strerror(errno));
+		return;
+	}
+	wpa_printf(MSG_DEBUG, "FILS: DHCP response from server %s:%d (len=%d)",
+		   inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), (int) res);
+	wpa_hexdump(MSG_MSGDUMP, "FILS: HLP - DHCP server response", buf, res);
+	if ((size_t) res < sizeof(*dhcp))
+		return;
+	dhcp = (struct dhcp_data *) buf;
+	if (dhcp->op != 2)
+		return; /* Not a BOOTREPLY */
+	if (dhcp->relay_ip != hapd->conf->own_ip_addr.u.v4.s_addr) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: HLP - DHCP response to unknown relay address 0x%x",
+			   dhcp->relay_ip);
+		return;
+	}
+	dhcp->relay_ip = 0;
+	pos = (u8 *) (dhcp + 1);
+	end = &buf[res];
+
+	if (end - pos < 4 || WPA_GET_BE32(pos) != DHCP_MAGIC) {
+		wpa_printf(MSG_DEBUG, "FILS: HLP - no DHCP magic in response");
+		return;
+	}
+	pos += 4;
+
+	wpa_hexdump(MSG_DEBUG, "FILS: HLP - DHCP options in response",
+		    pos, end - pos);
+	while (pos < end && *pos != DHCP_OPT_END) {
+		u8 opt, olen;
+
+		opt = *pos++;
+		if (opt == DHCP_OPT_PAD)
+			continue;
+		if (pos >= end)
+			break;
+		olen = *pos++;
+		if (olen > end - pos)
+			break;
+
+		switch (opt) {
+		case DHCP_OPT_MSG_TYPE:
+			if (olen > 0)
+				msgtype = pos[0];
+			break;
+		case DHCP_OPT_RAPID_COMMIT:
+			rapid_commit = 1;
+			break;
+		}
+		pos += olen;
+	}
+	if (pos < end && *pos == DHCP_OPT_END)
+		end_opt = pos;
+
+	wpa_printf(MSG_DEBUG,
+		   "FILS: HLP - DHCP message type %u (rapid_commit=%d hw_addr="
+		   MACSTR ")",
+		   msgtype, rapid_commit, MAC2STR(dhcp->hw_addr));
+
+	sta = ap_get_sta(hapd, dhcp->hw_addr);
+	if (!sta || !sta->fils_pending_assoc_req) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: No pending HLP DHCP exchange with hw_addr "
+			   MACSTR, MAC2STR(dhcp->hw_addr));
+		return;
+	}
+
+	if (hapd->conf->dhcp_rapid_commit_proxy && msgtype == DHCPOFFER &&
+	    !rapid_commit) {
+		/* Use hostapd to take care of 4-message exchange and convert
+		 * the final DHCPACK to rapid commit version. */
+		if (fils_dhcp_request(hapd, sta, dhcp, end) == 0)
+			return;
+		/* failed, so send the server response as-is */
+	} else if (msgtype != DHCPACK) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: No DHCPACK available from the server and cannot do rapid commit proxying");
+	}
+
+	pos = buf;
+	resp = wpabuf_alloc(2 * ETH_ALEN + 6 + 2 +
+			    sizeof(*iph) + sizeof(*udph) + (end - pos) + 2);
+	if (!resp)
+		return;
+	wpabuf_put_data(resp, sta->addr, ETH_ALEN);
+	wpabuf_put_data(resp, hapd->own_addr, ETH_ALEN);
+	wpabuf_put_data(resp, "\xaa\xaa\x03\x00\x00\x00", 6);
+	wpabuf_put_be16(resp, ETH_P_IP);
+	iph = wpabuf_put(resp, sizeof(*iph));
+	iph->ip_v = 4;
+	iph->ip_hl = sizeof(*iph) / 4;
+	iph->ip_len = htons(sizeof(*iph) + sizeof(*udph) + (end - pos));
+	iph->ip_ttl = 1;
+	iph->ip_p = 17; /* UDP */
+	iph->ip_src.s_addr = hapd->conf->dhcp_server.u.v4.s_addr;
+	iph->ip_dst.s_addr = dhcp->client_ip;
+	iph->ip_sum = ip_checksum(iph, sizeof(*iph));
+	udph = wpabuf_put(resp, sizeof(*udph));
+	udph->uh_sport = htons(DHCP_SERVER_PORT);
+	udph->uh_dport = htons(DHCP_CLIENT_PORT);
+	udph->uh_ulen = htons(sizeof(*udph) + (end - pos));
+	udph->uh_sum = htons(0x0000); /* TODO: calculate checksum */
+	if (hapd->conf->dhcp_rapid_commit_proxy && msgtype == DHCPACK &&
+	    !rapid_commit && sta->fils_dhcp_rapid_commit_proxy && end_opt) {
+		/* Add rapid commit option */
+		wpabuf_put_data(resp, pos, end_opt - pos);
+		wpabuf_put_u8(resp, DHCP_OPT_RAPID_COMMIT);
+		wpabuf_put_u8(resp, 0);
+		wpabuf_put_data(resp, end_opt, end - end_opt);
+	} else {
+		wpabuf_put_data(resp, pos, end - pos);
+	}
+	if (wpabuf_resize(&sta->fils_hlp_resp, wpabuf_len(resp) +
+			  2 * wpabuf_len(resp) / 255 + 100)) {
+		wpabuf_free(resp);
+		return;
+	}
+
+	rpos = wpabuf_head(resp);
+	left = wpabuf_len(resp);
+
+	wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_EXTENSION); /* Element ID */
+	if (left <= 254)
+		len = 1 + left;
+	else
+		len = 255;
+	wpabuf_put_u8(sta->fils_hlp_resp, len); /* Length */
+	/* Element ID Extension */
+	wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_EXT_FILS_HLP_CONTAINER);
+	/* Destination MAC Address, Source MAC Address, HLP Packet.
+	 * HLP Packet is in MSDU format (i.e., including the LLC/SNAP header
+	 * when LPD is used). */
+	wpabuf_put_data(sta->fils_hlp_resp, rpos, len - 1);
+	rpos += len - 1;
+	left -= len - 1;
+	while (left) {
+		wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_FRAGMENT);
+		len = left > 255 ? 255 : left;
+		wpabuf_put_u8(sta->fils_hlp_resp, len);
+		wpabuf_put_data(sta->fils_hlp_resp, rpos, len);
+		rpos += len;
+		left -= len;
+	}
+	wpabuf_free(resp);
+
+	if (sta->fils_drv_assoc_finish)
+		hostapd_notify_assoc_fils_finish(hapd, sta);
+	else
+		fils_hlp_finish_assoc(hapd, sta);
+}
+
+
+static int fils_process_hlp_dhcp(struct hostapd_data *hapd,
+				 struct sta_info *sta,
+				 const u8 *msg, size_t len)
+{
+	const struct dhcp_data *dhcp;
+	struct wpabuf *dhcp_buf;
+	struct dhcp_data *dhcp_msg;
+	u8 msgtype = 0;
+	int rapid_commit = 0;
+	const u8 *pos = msg, *end;
+	struct sockaddr_in addr;
+	ssize_t res;
+
+	if (len < sizeof(*dhcp))
+		return 0;
+	dhcp = (const struct dhcp_data *) pos;
+	end = pos + len;
+	wpa_printf(MSG_DEBUG,
+		   "FILS: HLP request DHCP: op=%u htype=%u hlen=%u hops=%u xid=0x%x",
+		   dhcp->op, dhcp->htype, dhcp->hlen, dhcp->hops,
+		   ntohl(dhcp->xid));
+	pos += sizeof(*dhcp);
+	if (dhcp->op != 1)
+		return 0; /* Not a BOOTREQUEST */
+
+	if (end - pos < 4)
+		return 0;
+	if (WPA_GET_BE32(pos) != DHCP_MAGIC) {
+		wpa_printf(MSG_DEBUG, "FILS: HLP - no DHCP magic");
+		return 0;
+	}
+	pos += 4;
+
+	wpa_hexdump(MSG_DEBUG, "FILS: HLP - DHCP options", pos, end - pos);
+	while (pos < end && *pos != DHCP_OPT_END) {
+		u8 opt, olen;
+
+		opt = *pos++;
+		if (opt == DHCP_OPT_PAD)
+			continue;
+		if (pos >= end)
+			break;
+		olen = *pos++;
+		if (olen > end - pos)
+			break;
+
+		switch (opt) {
+		case DHCP_OPT_MSG_TYPE:
+			if (olen > 0)
+				msgtype = pos[0];
+			break;
+		case DHCP_OPT_RAPID_COMMIT:
+			rapid_commit = 1;
+			break;
+		}
+		pos += olen;
+	}
+
+	wpa_printf(MSG_DEBUG, "FILS: HLP - DHCP message type %u", msgtype);
+	if (msgtype != DHCPDISCOVER)
+		return 0;
+
+	if (hapd->conf->dhcp_server.af != AF_INET ||
+	    hapd->conf->dhcp_server.u.v4.s_addr == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: HLP - no DHCPv4 server configured - drop request");
+		return 0;
+	}
+
+	if (hapd->conf->own_ip_addr.af != AF_INET ||
+	    hapd->conf->own_ip_addr.u.v4.s_addr == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: HLP - no IPv4 own_ip_addr configured - drop request");
+		return 0;
+	}
+
+	if (hapd->dhcp_sock < 0) {
+		int s;
+
+		s = socket(AF_INET, SOCK_DGRAM, 0);
+		if (s < 0) {
+			wpa_printf(MSG_ERROR,
+				   "FILS: Failed to open DHCP socket: %s",
+				   strerror(errno));
+			return 0;
+		}
+
+		if (hapd->conf->dhcp_relay_port) {
+			os_memset(&addr, 0, sizeof(addr));
+			addr.sin_family = AF_INET;
+			addr.sin_addr.s_addr =
+				hapd->conf->own_ip_addr.u.v4.s_addr;
+			addr.sin_port = htons(hapd->conf->dhcp_relay_port);
+			if (bind(s, (struct sockaddr *) &addr, sizeof(addr))) {
+				wpa_printf(MSG_ERROR,
+					   "FILS: Failed to bind DHCP socket: %s",
+					   strerror(errno));
+				close(s);
+				return 0;
+			}
+		}
+		if (eloop_register_sock(s, EVENT_TYPE_READ,
+					fils_dhcp_handler, NULL, hapd)) {
+			close(s);
+			return 0;
+		}
+
+		hapd->dhcp_sock = s;
+	}
+
+	dhcp_buf = wpabuf_alloc(len);
+	if (!dhcp_buf)
+		return 0;
+	dhcp_msg = wpabuf_put(dhcp_buf, len);
+	os_memcpy(dhcp_msg, msg, len);
+	dhcp_msg->relay_ip = hapd->conf->own_ip_addr.u.v4.s_addr;
+	os_memset(&addr, 0, sizeof(addr));
+	addr.sin_family = AF_INET;
+	addr.sin_addr.s_addr = hapd->conf->dhcp_server.u.v4.s_addr;
+	addr.sin_port = htons(hapd->conf->dhcp_server_port);
+	res = sendto(hapd->dhcp_sock, dhcp_msg, len, 0,
+		     (const struct sockaddr *) &addr, sizeof(addr));
+	if (res < 0) {
+		wpa_printf(MSG_ERROR, "FILS: DHCP sendto failed: %s",
+			   strerror(errno));
+		wpabuf_free(dhcp_buf);
+		/* Close the socket to try to recover from error */
+		eloop_unregister_read_sock(hapd->dhcp_sock);
+		close(hapd->dhcp_sock);
+		hapd->dhcp_sock = -1;
+		return 0;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "FILS: HLP relayed DHCP request to server %s:%d (rapid_commit=%d)",
+		   inet_ntoa(addr.sin_addr), ntohs(addr.sin_port),
+		   rapid_commit);
+	if (hapd->conf->dhcp_rapid_commit_proxy && rapid_commit) {
+		/* Store a copy of the DHCPDISCOVER for rapid commit proxying
+		 * purposes if the server does not support the rapid commit
+		 * option. */
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Store DHCPDISCOVER for rapid commit proxy");
+		wpabuf_free(sta->hlp_dhcp_discover);
+		sta->hlp_dhcp_discover = dhcp_buf;
+	} else {
+		wpabuf_free(dhcp_buf);
+	}
+
+	return 1;
+}
+
+
+static int fils_process_hlp_udp(struct hostapd_data *hapd,
+				struct sta_info *sta, const u8 *dst,
+				const u8 *pos, size_t len)
+{
+	const struct ip *iph;
+	const struct udphdr *udph;
+	u16 sport, dport, ulen;
+
+	if (len < sizeof(*iph) + sizeof(*udph))
+		return 0;
+	iph = (const struct ip *) pos;
+	udph = (const struct udphdr *) (iph + 1);
+	sport = ntohs(udph->uh_sport);
+	dport = ntohs(udph->uh_dport);
+	ulen = ntohs(udph->uh_ulen);
+	wpa_printf(MSG_DEBUG,
+		   "FILS: HLP request UDP: sport=%u dport=%u ulen=%u sum=0x%x",
+		   sport, dport, ulen, ntohs(udph->uh_sum));
+	/* TODO: Check UDP checksum */
+	if (ulen < sizeof(*udph) || ulen > len - sizeof(*iph))
+		return 0;
+
+	if (dport == DHCP_SERVER_PORT && sport == DHCP_CLIENT_PORT) {
+		return fils_process_hlp_dhcp(hapd, sta, (const u8 *) (udph + 1),
+					     ulen - sizeof(*udph));
+	}
+
+	return 0;
+}
+
+
+static int fils_process_hlp_ip(struct hostapd_data *hapd,
+			       struct sta_info *sta, const u8 *dst,
+			       const u8 *pos, size_t len)
+{
+	const struct ip *iph;
+	uint16_t ip_len;
+
+	if (len < sizeof(*iph))
+		return 0;
+	iph = (const struct ip *) pos;
+	if (ip_checksum(iph, sizeof(*iph)) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: HLP request IPv4 packet had invalid header checksum - dropped");
+		return 0;
+	}
+	ip_len = ntohs(iph->ip_len);
+	if (ip_len > len)
+		return 0;
+	wpa_printf(MSG_DEBUG,
+		   "FILS: HLP request IPv4: saddr=%08x daddr=%08x protocol=%u",
+		   iph->ip_src.s_addr, iph->ip_dst.s_addr, iph->ip_p);
+	switch (iph->ip_p) {
+	case 17:
+		return fils_process_hlp_udp(hapd, sta, dst, pos, len);
+	default:
+		return 0;
+	}
+}
+
+
+static int fils_process_hlp_req(struct hostapd_data *hapd,
+				struct sta_info *sta,
+				const u8 *pos, size_t len)
+{
+	const u8 *pkt, *end;
+
+	wpa_printf(MSG_DEBUG, "FILS: HLP request from " MACSTR " (dst=" MACSTR
+		   " src=" MACSTR " len=%u)",
+		   MAC2STR(sta->addr), MAC2STR(pos), MAC2STR(pos + ETH_ALEN),
+		   (unsigned int) len);
+	if (os_memcmp(sta->addr, pos + ETH_ALEN, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Ignore HLP request with unexpected source address"
+			   MACSTR, MAC2STR(pos + ETH_ALEN));
+		return 0;
+	}
+
+	end = pos + len;
+	pkt = pos + 2 * ETH_ALEN;
+	if (end - pkt >= 6 &&
+	    os_memcmp(pkt, "\xaa\xaa\x03\x00\x00\x00", 6) == 0)
+		pkt += 6; /* Remove SNAP/LLC header */
+	wpa_hexdump(MSG_MSGDUMP, "FILS: HLP request packet", pkt, end - pkt);
+
+	if (end - pkt < 2)
+		return 0;
+
+	switch (WPA_GET_BE16(pkt)) {
+	case ETH_P_IP:
+		return fils_process_hlp_ip(hapd, sta, pos, pkt + 2,
+					   end - pkt - 2);
+	default:
+		return 0;
+	}
+}
+
+
+int fils_process_hlp(struct hostapd_data *hapd, struct sta_info *sta,
+		     const u8 *pos, int left)
+{
+	const u8 *end = pos + left;
+	u8 *tmp, *tmp_pos;
+	int ret = 0;
+
+	if (sta->fils_pending_assoc_req &&
+	    eloop_is_timeout_registered(fils_hlp_timeout, hapd, sta)) {
+		/* Do not process FILS HLP request again if the station
+		 * retransmits (Re)Association Request frame before the previous
+		 * HLP response has either been received or timed out. */
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Do not relay another HLP request from "
+			   MACSTR
+			   " before processing of the already pending one has been completed",
+			   MAC2STR(sta->addr));
+		return 1;
+	}
+
+	/* Old DHCPDISCOVER is not needed anymore, if it was still pending */
+	wpabuf_free(sta->hlp_dhcp_discover);
+	sta->hlp_dhcp_discover = NULL;
+	sta->fils_dhcp_rapid_commit_proxy = 0;
+
+	/* Check if there are any FILS HLP Container elements */
+	while (end - pos >= 2) {
+		if (2 + pos[1] > end - pos)
+			return 0;
+		if (pos[0] == WLAN_EID_EXTENSION &&
+		    pos[1] >= 1 + 2 * ETH_ALEN &&
+		    pos[2] == WLAN_EID_EXT_FILS_HLP_CONTAINER)
+			break;
+		pos += 2 + pos[1];
+	}
+	if (end - pos < 2)
+		return 0; /* No FILS HLP Container elements */
+
+	tmp = os_malloc(end - pos);
+	if (!tmp)
+		return 0;
+
+	while (end - pos >= 2) {
+		if (2 + pos[1] > end - pos ||
+		    pos[0] != WLAN_EID_EXTENSION ||
+		    pos[1] < 1 + 2 * ETH_ALEN ||
+		    pos[2] != WLAN_EID_EXT_FILS_HLP_CONTAINER)
+			break;
+		tmp_pos = tmp;
+		os_memcpy(tmp_pos, pos + 3, pos[1] - 1);
+		tmp_pos += pos[1] - 1;
+		pos += 2 + pos[1];
+
+		/* Add possible fragments */
+		while (end - pos >= 2 && pos[0] == WLAN_EID_FRAGMENT &&
+		       2 + pos[1] <= end - pos) {
+			os_memcpy(tmp_pos, pos + 2, pos[1]);
+			tmp_pos += pos[1];
+			pos += 2 + pos[1];
+		}
+
+		if (fils_process_hlp_req(hapd, sta, tmp, tmp_pos - tmp) > 0)
+			ret = 1;
+	}
+
+	os_free(tmp);
+
+	return ret;
+}
+
+
+void fils_hlp_deinit(struct hostapd_data *hapd)
+{
+	if (hapd->dhcp_sock >= 0) {
+		eloop_unregister_read_sock(hapd->dhcp_sock);
+		close(hapd->dhcp_sock);
+		hapd->dhcp_sock = -1;
+	}
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/fils_hlp.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/fils_hlp.h
new file mode 100644
index 0000000..e14a6bf
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/fils_hlp.h
@@ -0,0 +1,27 @@
+/*
+ * FILS HLP request processing
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef FILS_HLP_H
+#define FILS_HLP_H
+
+int fils_process_hlp(struct hostapd_data *hapd, struct sta_info *sta,
+		     const u8 *pos, int left);
+
+#ifdef CONFIG_FILS
+
+void fils_hlp_deinit(struct hostapd_data *hapd);
+
+#else /* CONFIG_FILS */
+
+static inline void fils_hlp_deinit(struct hostapd_data *hapd)
+{
+}
+
+#endif /* CONFIG_FILS */
+
+#endif /* FILS_HLP_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/gas_query_ap.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/gas_query_ap.c
new file mode 100644
index 0000000..3d94407
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/gas_query_ap.c
@@ -0,0 +1,718 @@
+/*
+ * Generic advertisement service (GAS) query (hostapd)
+ * Copyright (c) 2009, Atheros Communications
+ * Copyright (c) 2011-2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2014, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "utils/eloop.h"
+#include "utils/list.h"
+#include "common/ieee802_11_defs.h"
+#include "common/gas.h"
+#include "common/wpa_ctrl.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ap_drv_ops.h"
+#include "gas_query_ap.h"
+
+
+/** GAS query timeout in seconds */
+#define GAS_QUERY_TIMEOUT_PERIOD 2
+
+/* GAS query wait-time / duration in ms */
+#define GAS_QUERY_WAIT_TIME_INITIAL 1000
+#define GAS_QUERY_WAIT_TIME_COMEBACK 150
+
+#define GAS_QUERY_MAX_COMEBACK_DELAY 60000
+
+/**
+ * struct gas_query_pending - Pending GAS query
+ */
+struct gas_query_pending {
+	struct dl_list list;
+	struct gas_query_ap *gas;
+	u8 addr[ETH_ALEN];
+	u8 dialog_token;
+	u8 next_frag_id;
+	unsigned int wait_comeback:1;
+	unsigned int offchannel_tx_started:1;
+	unsigned int retry:1;
+	int freq;
+	u16 status_code;
+	struct wpabuf *req;
+	struct wpabuf *adv_proto;
+	struct wpabuf *resp;
+	struct os_reltime last_oper;
+	void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
+		   enum gas_query_ap_result result,
+		   const struct wpabuf *adv_proto,
+		   const struct wpabuf *resp, u16 status_code);
+	void *ctx;
+	u8 sa[ETH_ALEN];
+};
+
+/**
+ * struct gas_query_ap - Internal GAS query data
+ */
+struct gas_query_ap {
+	struct hostapd_data *hapd;
+	void *msg_ctx;
+	struct dl_list pending; /* struct gas_query_pending */
+	struct gas_query_pending *current;
+};
+
+
+static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx);
+static void gas_query_timeout(void *eloop_data, void *user_ctx);
+static void gas_query_rx_comeback_timeout(void *eloop_data, void *user_ctx);
+static void gas_query_tx_initial_req(struct gas_query_ap *gas,
+				     struct gas_query_pending *query);
+static int gas_query_new_dialog_token(struct gas_query_ap *gas, const u8 *dst);
+
+
+static int ms_from_time(struct os_reltime *last)
+{
+	struct os_reltime now, res;
+
+	os_get_reltime(&now);
+	os_reltime_sub(&now, last, &res);
+	return res.sec * 1000 + res.usec / 1000;
+}
+
+
+/**
+ * gas_query_ap_init - Initialize GAS query component
+ * @hapd: Pointer to hostapd data
+ * Returns: Pointer to GAS query data or %NULL on failure
+ */
+struct gas_query_ap * gas_query_ap_init(struct hostapd_data *hapd,
+					void *msg_ctx)
+{
+	struct gas_query_ap *gas;
+
+	gas = os_zalloc(sizeof(*gas));
+	if (!gas)
+		return NULL;
+
+	gas->hapd = hapd;
+	gas->msg_ctx = msg_ctx;
+	dl_list_init(&gas->pending);
+
+	return gas;
+}
+
+
+static const char * gas_result_txt(enum gas_query_ap_result result)
+{
+	switch (result) {
+	case GAS_QUERY_AP_SUCCESS:
+		return "SUCCESS";
+	case GAS_QUERY_AP_FAILURE:
+		return "FAILURE";
+	case GAS_QUERY_AP_TIMEOUT:
+		return "TIMEOUT";
+	case GAS_QUERY_AP_PEER_ERROR:
+		return "PEER_ERROR";
+	case GAS_QUERY_AP_INTERNAL_ERROR:
+		return "INTERNAL_ERROR";
+	case GAS_QUERY_AP_DELETED_AT_DEINIT:
+		return "DELETED_AT_DEINIT";
+	}
+
+	return "N/A";
+}
+
+
+static void gas_query_free(struct gas_query_pending *query, int del_list)
+{
+	if (del_list)
+		dl_list_del(&query->list);
+
+	wpabuf_free(query->req);
+	wpabuf_free(query->adv_proto);
+	wpabuf_free(query->resp);
+	os_free(query);
+}
+
+
+static void gas_query_done(struct gas_query_ap *gas,
+			   struct gas_query_pending *query,
+			   enum gas_query_ap_result result)
+{
+	wpa_msg(gas->msg_ctx, MSG_INFO, GAS_QUERY_DONE "addr=" MACSTR
+		" dialog_token=%u freq=%d status_code=%u result=%s",
+		MAC2STR(query->addr), query->dialog_token, query->freq,
+		query->status_code, gas_result_txt(result));
+	if (gas->current == query)
+		gas->current = NULL;
+	eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
+	eloop_cancel_timeout(gas_query_timeout, gas, query);
+	eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query);
+	dl_list_del(&query->list);
+	query->cb(query->ctx, query->addr, query->dialog_token, result,
+		  query->adv_proto, query->resp, query->status_code);
+	gas_query_free(query, 0);
+}
+
+
+/**
+ * gas_query_ap_deinit - Deinitialize GAS query component
+ * @gas: GAS query data from gas_query_init()
+ */
+void gas_query_ap_deinit(struct gas_query_ap *gas)
+{
+	struct gas_query_pending *query, *next;
+
+	if (gas == NULL)
+		return;
+
+	dl_list_for_each_safe(query, next, &gas->pending,
+			      struct gas_query_pending, list)
+		gas_query_done(gas, query, GAS_QUERY_AP_DELETED_AT_DEINIT);
+
+	os_free(gas);
+}
+
+
+static struct gas_query_pending *
+gas_query_get_pending(struct gas_query_ap *gas, const u8 *addr, u8 dialog_token)
+{
+	struct gas_query_pending *q;
+	dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
+		if (os_memcmp(q->addr, addr, ETH_ALEN) == 0 &&
+		    q->dialog_token == dialog_token)
+			return q;
+	}
+	return NULL;
+}
+
+
+static int gas_query_append(struct gas_query_pending *query, const u8 *data,
+			    size_t len)
+{
+	if (wpabuf_resize(&query->resp, len) < 0) {
+		wpa_printf(MSG_DEBUG, "GAS: No memory to store the response");
+		return -1;
+	}
+	wpabuf_put_data(query->resp, data, len);
+	return 0;
+}
+
+
+void gas_query_ap_tx_status(struct gas_query_ap *gas, const u8 *dst,
+			    const u8 *data, size_t data_len, int ok)
+{
+	struct gas_query_pending *query;
+	int dur;
+
+	if (!gas || !gas->current) {
+		wpa_printf(MSG_DEBUG, "GAS: Unexpected TX status: dst=" MACSTR
+			   " ok=%d - no query in progress", MAC2STR(dst), ok);
+		return;
+	}
+
+	query = gas->current;
+
+	dur = ms_from_time(&query->last_oper);
+	wpa_printf(MSG_DEBUG, "GAS: TX status: dst=" MACSTR
+		   " ok=%d query=%p dialog_token=%u dur=%d ms",
+		   MAC2STR(dst), ok, query, query->dialog_token, dur);
+	if (os_memcmp(dst, query->addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "GAS: TX status for unexpected destination");
+		return;
+	}
+	os_get_reltime(&query->last_oper);
+
+	eloop_cancel_timeout(gas_query_timeout, gas, query);
+	if (!ok) {
+		wpa_printf(MSG_DEBUG, "GAS: No ACK to GAS request");
+		eloop_register_timeout(0, 250000, gas_query_timeout,
+				       gas, query);
+	} else {
+		eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
+				       gas_query_timeout, gas, query);
+	}
+	if (query->wait_comeback && !query->retry) {
+		eloop_cancel_timeout(gas_query_rx_comeback_timeout,
+				     gas, query);
+		eloop_register_timeout(
+			0, (GAS_QUERY_WAIT_TIME_COMEBACK + 10) * 1000,
+			gas_query_rx_comeback_timeout, gas, query);
+	}
+}
+
+
+static int pmf_in_use(struct hostapd_data *hapd, const u8 *addr)
+{
+	struct sta_info *sta;
+
+	sta = ap_get_sta(hapd, addr);
+	return sta && (sta->flags & WLAN_STA_MFP);
+}
+
+
+static int gas_query_tx(struct gas_query_ap *gas,
+			struct gas_query_pending *query,
+			struct wpabuf *req, unsigned int wait_time)
+{
+	int res, prot = pmf_in_use(gas->hapd, query->addr);
+
+	wpa_printf(MSG_DEBUG, "GAS: Send action frame to " MACSTR " len=%u "
+		   "freq=%d prot=%d using src addr " MACSTR,
+		   MAC2STR(query->addr), (unsigned int) wpabuf_len(req),
+		   query->freq, prot, MAC2STR(query->sa));
+	if (prot) {
+		u8 *categ = wpabuf_mhead_u8(req);
+		*categ = WLAN_ACTION_PROTECTED_DUAL;
+	}
+	os_get_reltime(&query->last_oper);
+	res = hostapd_drv_send_action(gas->hapd, query->freq, wait_time,
+				      query->addr, wpabuf_head(req),
+				      wpabuf_len(req));
+	return res;
+}
+
+
+static void gas_query_tx_comeback_req(struct gas_query_ap *gas,
+				      struct gas_query_pending *query)
+{
+	struct wpabuf *req;
+	unsigned int wait_time;
+
+	req = gas_build_comeback_req(query->dialog_token);
+	if (req == NULL) {
+		gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
+		return;
+	}
+
+	wait_time = (query->retry || !query->offchannel_tx_started) ?
+		GAS_QUERY_WAIT_TIME_INITIAL : GAS_QUERY_WAIT_TIME_COMEBACK;
+
+	if (gas_query_tx(gas, query, req, wait_time) < 0) {
+		wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
+			   MACSTR, MAC2STR(query->addr));
+		gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
+	}
+
+	wpabuf_free(req);
+}
+
+
+static void gas_query_rx_comeback_timeout(void *eloop_data, void *user_ctx)
+{
+	struct gas_query_ap *gas = eloop_data;
+	struct gas_query_pending *query = user_ctx;
+	int dialog_token;
+
+	wpa_printf(MSG_DEBUG,
+		   "GAS: No response to comeback request received (retry=%u)",
+		   query->retry);
+	if (gas->current != query || query->retry)
+		return;
+	dialog_token = gas_query_new_dialog_token(gas, query->addr);
+	if (dialog_token < 0)
+		return;
+	wpa_printf(MSG_DEBUG,
+		   "GAS: Retry GAS query due to comeback response timeout");
+	query->retry = 1;
+	query->dialog_token = dialog_token;
+	*(wpabuf_mhead_u8(query->req) + 2) = dialog_token;
+	query->wait_comeback = 0;
+	query->next_frag_id = 0;
+	wpabuf_free(query->adv_proto);
+	query->adv_proto = NULL;
+	eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
+	eloop_cancel_timeout(gas_query_timeout, gas, query);
+	gas_query_tx_initial_req(gas, query);
+}
+
+
+static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx)
+{
+	struct gas_query_ap *gas = eloop_data;
+	struct gas_query_pending *query = user_ctx;
+
+	wpa_printf(MSG_DEBUG, "GAS: Comeback timeout for request to " MACSTR,
+		   MAC2STR(query->addr));
+	gas_query_tx_comeback_req(gas, query);
+}
+
+
+static void gas_query_tx_comeback_req_delay(struct gas_query_ap *gas,
+					    struct gas_query_pending *query,
+					    u16 comeback_delay)
+{
+	unsigned int secs, usecs;
+
+	secs = (comeback_delay * 1024) / 1000000;
+	usecs = comeback_delay * 1024 - secs * 1000000;
+	wpa_printf(MSG_DEBUG, "GAS: Send comeback request to " MACSTR
+		   " in %u secs %u usecs", MAC2STR(query->addr), secs, usecs);
+	eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
+	eloop_register_timeout(secs, usecs, gas_query_tx_comeback_timeout,
+			       gas, query);
+}
+
+
+static void gas_query_rx_initial(struct gas_query_ap *gas,
+				 struct gas_query_pending *query,
+				 const u8 *adv_proto, const u8 *resp,
+				 size_t len, u16 comeback_delay)
+{
+	wpa_printf(MSG_DEBUG, "GAS: Received initial response from "
+		   MACSTR " (dialog_token=%u comeback_delay=%u)",
+		   MAC2STR(query->addr), query->dialog_token, comeback_delay);
+
+	query->adv_proto = wpabuf_alloc_copy(adv_proto, 2 + adv_proto[1]);
+	if (query->adv_proto == NULL) {
+		gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
+		return;
+	}
+
+	if (comeback_delay) {
+		eloop_cancel_timeout(gas_query_timeout, gas, query);
+		query->wait_comeback = 1;
+		gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
+		return;
+	}
+
+	/* Query was completed without comeback mechanism */
+	if (gas_query_append(query, resp, len) < 0) {
+		gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
+		return;
+	}
+
+	gas_query_done(gas, query, GAS_QUERY_AP_SUCCESS);
+}
+
+
+static void gas_query_rx_comeback(struct gas_query_ap *gas,
+				  struct gas_query_pending *query,
+				  const u8 *adv_proto, const u8 *resp,
+				  size_t len, u8 frag_id, u8 more_frags,
+				  u16 comeback_delay)
+{
+	wpa_printf(MSG_DEBUG, "GAS: Received comeback response from "
+		   MACSTR " (dialog_token=%u frag_id=%u more_frags=%u "
+		   "comeback_delay=%u)",
+		   MAC2STR(query->addr), query->dialog_token, frag_id,
+		   more_frags, comeback_delay);
+	eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query);
+
+	if ((size_t) 2 + adv_proto[1] != wpabuf_len(query->adv_proto) ||
+	    os_memcmp(adv_proto, wpabuf_head(query->adv_proto),
+		      wpabuf_len(query->adv_proto)) != 0) {
+		wpa_printf(MSG_DEBUG, "GAS: Advertisement Protocol changed "
+			   "between initial and comeback response from "
+			   MACSTR, MAC2STR(query->addr));
+		gas_query_done(gas, query, GAS_QUERY_AP_PEER_ERROR);
+		return;
+	}
+
+	if (comeback_delay) {
+		if (frag_id) {
+			wpa_printf(MSG_DEBUG, "GAS: Invalid comeback response "
+				   "with non-zero frag_id and comeback_delay "
+				   "from " MACSTR, MAC2STR(query->addr));
+			gas_query_done(gas, query, GAS_QUERY_AP_PEER_ERROR);
+			return;
+		}
+		gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
+		return;
+	}
+
+	if (frag_id != query->next_frag_id) {
+		wpa_printf(MSG_DEBUG, "GAS: Unexpected frag_id in response "
+			   "from " MACSTR, MAC2STR(query->addr));
+		if (frag_id + 1 == query->next_frag_id) {
+			wpa_printf(MSG_DEBUG, "GAS: Drop frame as possible "
+				   "retry of previous fragment");
+			return;
+		}
+		gas_query_done(gas, query, GAS_QUERY_AP_PEER_ERROR);
+		return;
+	}
+	query->next_frag_id++;
+
+	if (gas_query_append(query, resp, len) < 0) {
+		gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
+		return;
+	}
+
+	if (more_frags) {
+		gas_query_tx_comeback_req(gas, query);
+		return;
+	}
+
+	gas_query_done(gas, query, GAS_QUERY_AP_SUCCESS);
+}
+
+
+/**
+ * gas_query_ap_rx - Indicate reception of a Public Action or Protected Dual
+ *	frame
+ * @gas: GAS query data from gas_query_init()
+ * @sa: Source MAC address of the Action frame
+ * @categ: Category of the Action frame
+ * @data: Payload of the Action frame
+ * @len: Length of @data
+ * @freq: Frequency (in MHz) on which the frame was received
+ * Returns: 0 if the Public Action frame was a GAS frame or -1 if not
+ */
+int gas_query_ap_rx(struct gas_query_ap *gas, const u8 *sa, u8 categ,
+		    const u8 *data, size_t len, int freq)
+{
+	struct gas_query_pending *query;
+	u8 action, dialog_token, frag_id = 0, more_frags = 0;
+	u16 comeback_delay, resp_len;
+	const u8 *pos, *adv_proto;
+	int prot, pmf;
+	unsigned int left;
+
+	if (!gas || len < 4)
+		return -1;
+
+	pos = data;
+	action = *pos++;
+	dialog_token = *pos++;
+
+	if (action != WLAN_PA_GAS_INITIAL_RESP &&
+	    action != WLAN_PA_GAS_COMEBACK_RESP)
+		return -1; /* Not a GAS response */
+
+	prot = categ == WLAN_ACTION_PROTECTED_DUAL;
+	pmf = pmf_in_use(gas->hapd, sa);
+	if (prot && !pmf) {
+		wpa_printf(MSG_DEBUG, "GAS: Drop unexpected protected GAS frame when PMF is disabled");
+		return 0;
+	}
+	if (!prot && pmf) {
+		wpa_printf(MSG_DEBUG, "GAS: Drop unexpected unprotected GAS frame when PMF is enabled");
+		return 0;
+	}
+
+	query = gas_query_get_pending(gas, sa, dialog_token);
+	if (query == NULL) {
+		wpa_printf(MSG_DEBUG, "GAS: No pending query found for " MACSTR
+			   " dialog token %u", MAC2STR(sa), dialog_token);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "GAS: Response in %d ms from " MACSTR,
+		   ms_from_time(&query->last_oper), MAC2STR(sa));
+
+	if (query->wait_comeback && action == WLAN_PA_GAS_INITIAL_RESP) {
+		wpa_printf(MSG_DEBUG, "GAS: Unexpected initial response from "
+			   MACSTR " dialog token %u when waiting for comeback "
+			   "response", MAC2STR(sa), dialog_token);
+		return 0;
+	}
+
+	if (!query->wait_comeback && action == WLAN_PA_GAS_COMEBACK_RESP) {
+		wpa_printf(MSG_DEBUG, "GAS: Unexpected comeback response from "
+			   MACSTR " dialog token %u when waiting for initial "
+			   "response", MAC2STR(sa), dialog_token);
+		return 0;
+	}
+
+	query->status_code = WPA_GET_LE16(pos);
+	pos += 2;
+
+	if (query->status_code == WLAN_STATUS_QUERY_RESP_OUTSTANDING &&
+	    action == WLAN_PA_GAS_COMEBACK_RESP) {
+		wpa_printf(MSG_DEBUG, "GAS: Allow non-zero status for outstanding comeback response");
+	} else if (query->status_code != WLAN_STATUS_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "GAS: Query to " MACSTR " dialog token "
+			   "%u failed - status code %u",
+			   MAC2STR(sa), dialog_token, query->status_code);
+		gas_query_done(gas, query, GAS_QUERY_AP_FAILURE);
+		return 0;
+	}
+
+	if (action == WLAN_PA_GAS_COMEBACK_RESP) {
+		if (pos + 1 > data + len)
+			return 0;
+		frag_id = *pos & 0x7f;
+		more_frags = (*pos & 0x80) >> 7;
+		pos++;
+	}
+
+	/* Comeback Delay */
+	if (pos + 2 > data + len)
+		return 0;
+	comeback_delay = WPA_GET_LE16(pos);
+	if (comeback_delay > GAS_QUERY_MAX_COMEBACK_DELAY)
+		comeback_delay = GAS_QUERY_MAX_COMEBACK_DELAY;
+	pos += 2;
+
+	/* Advertisement Protocol element */
+	if (pos + 2 > data + len || pos + 2 + pos[1] > data + len) {
+		wpa_printf(MSG_DEBUG, "GAS: No room for Advertisement "
+			   "Protocol element in the response from " MACSTR,
+			   MAC2STR(sa));
+		return 0;
+	}
+
+	if (*pos != WLAN_EID_ADV_PROTO) {
+		wpa_printf(MSG_DEBUG, "GAS: Unexpected Advertisement "
+			   "Protocol element ID %u in response from " MACSTR,
+			   *pos, MAC2STR(sa));
+		return 0;
+	}
+
+	adv_proto = pos;
+	pos += 2 + pos[1];
+
+	/* Query Response Length */
+	if (pos + 2 > data + len) {
+		wpa_printf(MSG_DEBUG, "GAS: No room for GAS Response Length");
+		return 0;
+	}
+	resp_len = WPA_GET_LE16(pos);
+	pos += 2;
+
+	left = data + len - pos;
+	if (resp_len > left) {
+		wpa_printf(MSG_DEBUG, "GAS: Truncated Query Response in "
+			   "response from " MACSTR, MAC2STR(sa));
+		return 0;
+	}
+
+	if (resp_len < left) {
+		wpa_printf(MSG_DEBUG, "GAS: Ignore %u octets of extra data "
+			   "after Query Response from " MACSTR,
+			   left - resp_len, MAC2STR(sa));
+	}
+
+	if (action == WLAN_PA_GAS_COMEBACK_RESP)
+		gas_query_rx_comeback(gas, query, adv_proto, pos, resp_len,
+				      frag_id, more_frags, comeback_delay);
+	else
+		gas_query_rx_initial(gas, query, adv_proto, pos, resp_len,
+				     comeback_delay);
+
+	return 0;
+}
+
+
+static void gas_query_timeout(void *eloop_data, void *user_ctx)
+{
+	struct gas_query_ap *gas = eloop_data;
+	struct gas_query_pending *query = user_ctx;
+
+	wpa_printf(MSG_DEBUG, "GAS: No response received for query to " MACSTR
+		   " dialog token %u",
+		   MAC2STR(query->addr), query->dialog_token);
+	gas_query_done(gas, query, GAS_QUERY_AP_TIMEOUT);
+}
+
+
+static int gas_query_dialog_token_available(struct gas_query_ap *gas,
+					    const u8 *dst, u8 dialog_token)
+{
+	struct gas_query_pending *q;
+	dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
+		if (os_memcmp(dst, q->addr, ETH_ALEN) == 0 &&
+		    dialog_token == q->dialog_token)
+			return 0;
+	}
+
+	return 1;
+}
+
+
+static void gas_query_tx_initial_req(struct gas_query_ap *gas,
+				     struct gas_query_pending *query)
+{
+	if (gas_query_tx(gas, query, query->req,
+			 GAS_QUERY_WAIT_TIME_INITIAL) < 0) {
+		wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
+			   MACSTR, MAC2STR(query->addr));
+		gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
+		return;
+	}
+	gas->current = query;
+
+	wpa_printf(MSG_DEBUG, "GAS: Starting query timeout for dialog token %u",
+		   query->dialog_token);
+	eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
+			       gas_query_timeout, gas, query);
+}
+
+
+static int gas_query_new_dialog_token(struct gas_query_ap *gas, const u8 *dst)
+{
+	static int next_start = 0;
+	int dialog_token;
+
+	for (dialog_token = 0; dialog_token < 256; dialog_token++) {
+		if (gas_query_dialog_token_available(
+			    gas, dst, (next_start + dialog_token) % 256))
+			break;
+	}
+	if (dialog_token == 256)
+		return -1; /* Too many pending queries */
+	dialog_token = (next_start + dialog_token) % 256;
+	next_start = (dialog_token + 1) % 256;
+	return dialog_token;
+}
+
+
+/**
+ * gas_query_ap_req - Request a GAS query
+ * @gas: GAS query data from gas_query_init()
+ * @dst: Destination MAC address for the query
+ * @freq: Frequency (in MHz) for the channel on which to send the query
+ * @req: GAS query payload (to be freed by gas_query module in case of success
+ *	return)
+ * @cb: Callback function for reporting GAS query result and response
+ * @ctx: Context pointer to use with the @cb call
+ * Returns: dialog token (>= 0) on success or -1 on failure
+ */
+int gas_query_ap_req(struct gas_query_ap *gas, const u8 *dst, int freq,
+		     struct wpabuf *req,
+		     void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
+				enum gas_query_ap_result result,
+				const struct wpabuf *adv_proto,
+				const struct wpabuf *resp, u16 status_code),
+		     void *ctx)
+{
+	struct gas_query_pending *query;
+	int dialog_token;
+
+	if (!gas || wpabuf_len(req) < 3)
+		return -1;
+
+	dialog_token = gas_query_new_dialog_token(gas, dst);
+	if (dialog_token < 0)
+		return -1;
+
+	query = os_zalloc(sizeof(*query));
+	if (query == NULL)
+		return -1;
+
+	query->gas = gas;
+	os_memcpy(query->addr, dst, ETH_ALEN);
+	query->dialog_token = dialog_token;
+	query->freq = freq;
+	query->cb = cb;
+	query->ctx = ctx;
+	query->req = req;
+	dl_list_add(&gas->pending, &query->list);
+
+	*(wpabuf_mhead_u8(req) + 2) = dialog_token;
+
+	wpa_msg(gas->msg_ctx, MSG_INFO, GAS_QUERY_START "addr=" MACSTR
+		" dialog_token=%u freq=%d",
+		MAC2STR(query->addr), query->dialog_token, query->freq);
+
+	gas_query_tx_initial_req(gas, query);
+
+	return dialog_token;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/gas_query_ap.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/gas_query_ap.h
new file mode 100644
index 0000000..70f1f05
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/gas_query_ap.h
@@ -0,0 +1,43 @@
+/*
+ * Generic advertisement service (GAS) query
+ * Copyright (c) 2009, Atheros Communications
+ * Copyright (c) 2011-2017, Qualcomm Atheros
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef GAS_QUERY_AP_H
+#define GAS_QUERY_AP_H
+
+struct gas_query_ap;
+
+struct gas_query_ap * gas_query_ap_init(struct hostapd_data *hapd,
+					void *msg_ctx);
+void gas_query_ap_deinit(struct gas_query_ap *gas);
+int gas_query_ap_rx(struct gas_query_ap *gas, const u8 *sa, u8 categ,
+		    const u8 *data, size_t len, int freq);
+
+/**
+ * enum gas_query_ap_result - GAS query result
+ */
+enum gas_query_ap_result {
+	GAS_QUERY_AP_SUCCESS,
+	GAS_QUERY_AP_FAILURE,
+	GAS_QUERY_AP_TIMEOUT,
+	GAS_QUERY_AP_PEER_ERROR,
+	GAS_QUERY_AP_INTERNAL_ERROR,
+	GAS_QUERY_AP_DELETED_AT_DEINIT
+};
+
+int gas_query_ap_req(struct gas_query_ap *gas, const u8 *dst, int freq,
+		     struct wpabuf *req,
+		     void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
+				enum gas_query_ap_result result,
+				const struct wpabuf *adv_proto,
+				const struct wpabuf *resp, u16 status_code),
+		     void *ctx);
+void gas_query_ap_tx_status(struct gas_query_ap *gas, const u8 *dst,
+			    const u8 *data, size_t data_len, int ok);
+
+#endif /* GAS_QUERY_AP_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/gas_serv.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/gas_serv.c
new file mode 100644
index 0000000..4642e49
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/gas_serv.c
@@ -0,0 +1,1895 @@
+/*
+ * Generic advertisement service (GAS) server
+ * Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/gas.h"
+#include "common/wpa_ctrl.h"
+#include "utils/eloop.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "dpp_hostapd.h"
+#include "sta_info.h"
+#include "gas_serv.h"
+
+
+#ifdef CONFIG_DPP
+static void gas_serv_write_dpp_adv_proto(struct wpabuf *buf)
+{
+	wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
+	wpabuf_put_u8(buf, 8); /* Length */
+	wpabuf_put_u8(buf, 0x7f);
+	wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+	wpabuf_put_u8(buf, 5);
+	wpabuf_put_be24(buf, OUI_WFA);
+	wpabuf_put_u8(buf, DPP_OUI_TYPE);
+	wpabuf_put_u8(buf, 0x01);
+}
+#endif /* CONFIG_DPP */
+
+
+static void convert_to_protected_dual(struct wpabuf *msg)
+{
+	u8 *categ = wpabuf_mhead_u8(msg);
+	*categ = WLAN_ACTION_PROTECTED_DUAL;
+}
+
+
+static struct gas_dialog_info *
+gas_dialog_create(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token)
+{
+	struct sta_info *sta;
+	struct gas_dialog_info *dia = NULL;
+	int i, j;
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta) {
+		/*
+		 * We need a STA entry to be able to maintain state for
+		 * the GAS query.
+		 */
+		wpa_printf(MSG_DEBUG, "ANQP: Add a temporary STA entry for "
+			   "GAS query");
+		sta = ap_sta_add(hapd, addr);
+		if (!sta) {
+			wpa_printf(MSG_DEBUG, "Failed to add STA " MACSTR
+				   " for GAS query", MAC2STR(addr));
+			return NULL;
+		}
+		sta->flags |= WLAN_STA_GAS;
+		/*
+		 * The default inactivity is 300 seconds. We don't need
+		 * it to be that long. Use five second timeout and increase this
+		 * with the comeback_delay for testing cases.
+		 */
+		ap_sta_session_timeout(hapd, sta,
+				       hapd->conf->gas_comeback_delay / 1024 +
+				       5);
+	} else {
+		ap_sta_replenish_timeout(hapd, sta, 5);
+	}
+
+	if (sta->gas_dialog == NULL) {
+		sta->gas_dialog = os_calloc(GAS_DIALOG_MAX,
+					    sizeof(struct gas_dialog_info));
+		if (sta->gas_dialog == NULL)
+			return NULL;
+	}
+
+	for (i = sta->gas_dialog_next, j = 0; j < GAS_DIALOG_MAX; i++, j++) {
+		if (i == GAS_DIALOG_MAX)
+			i = 0;
+		if (sta->gas_dialog[i].valid)
+			continue;
+		dia = &sta->gas_dialog[i];
+		dia->valid = 1;
+		dia->dialog_token = dialog_token;
+		sta->gas_dialog_next = (++i == GAS_DIALOG_MAX) ? 0 : i;
+		return dia;
+	}
+
+	wpa_msg(hapd->msg_ctx, MSG_ERROR, "ANQP: Could not create dialog for "
+		MACSTR " dialog_token %u. Consider increasing "
+		"GAS_DIALOG_MAX.", MAC2STR(addr), dialog_token);
+
+	return NULL;
+}
+
+
+struct gas_dialog_info *
+gas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr,
+		     u8 dialog_token)
+{
+	struct sta_info *sta;
+	int i;
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta) {
+		wpa_printf(MSG_DEBUG, "ANQP: could not find STA " MACSTR,
+			   MAC2STR(addr));
+		return NULL;
+	}
+	for (i = 0; sta->gas_dialog && i < GAS_DIALOG_MAX; i++) {
+		if (sta->gas_dialog[i].dialog_token != dialog_token ||
+		    !sta->gas_dialog[i].valid)
+			continue;
+		ap_sta_replenish_timeout(hapd, sta, 5);
+		return &sta->gas_dialog[i];
+	}
+	wpa_printf(MSG_DEBUG, "ANQP: Could not find dialog for "
+		   MACSTR " dialog_token %u", MAC2STR(addr), dialog_token);
+	return NULL;
+}
+
+
+void gas_serv_dialog_clear(struct gas_dialog_info *dia)
+{
+	wpabuf_free(dia->sd_resp);
+	os_memset(dia, 0, sizeof(*dia));
+}
+
+
+static void gas_serv_free_dialogs(struct hostapd_data *hapd,
+				  const u8 *sta_addr)
+{
+	struct sta_info *sta;
+	int i;
+
+	sta = ap_get_sta(hapd, sta_addr);
+	if (sta == NULL || sta->gas_dialog == NULL)
+		return;
+
+	for (i = 0; i < GAS_DIALOG_MAX; i++) {
+		if (sta->gas_dialog[i].valid)
+			return;
+	}
+
+	os_free(sta->gas_dialog);
+	sta->gas_dialog = NULL;
+}
+
+
+#ifdef CONFIG_HS20
+static void anqp_add_hs_capab_list(struct hostapd_data *hapd,
+				   struct wpabuf *buf)
+{
+	u8 *len;
+
+	len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+	wpabuf_put_be24(buf, OUI_WFA);
+	wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+	wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
+	wpabuf_put_u8(buf, 0); /* Reserved */
+	wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
+	if (hapd->conf->hs20_oper_friendly_name)
+		wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
+	if (hapd->conf->hs20_wan_metrics)
+		wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
+	if (hapd->conf->hs20_connection_capability)
+		wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
+	if (hapd->conf->nai_realm_data)
+		wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY);
+	if (hapd->conf->hs20_operating_class)
+		wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
+	if (hapd->conf->hs20_osu_providers_count)
+		wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
+	if (hapd->conf->hs20_osu_providers_nai_count)
+		wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_NAI_LIST);
+	if (hapd->conf->hs20_icons_count)
+		wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST);
+	if (hapd->conf->hs20_operator_icon_count)
+		wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_ICON_METADATA);
+	gas_anqp_set_element_len(buf, len);
+}
+#endif /* CONFIG_HS20 */
+
+
+static struct anqp_element * get_anqp_elem(struct hostapd_data *hapd,
+					   u16 infoid)
+{
+	struct anqp_element *elem;
+
+	dl_list_for_each(elem, &hapd->conf->anqp_elem, struct anqp_element,
+			 list) {
+		if (elem->infoid == infoid)
+			return elem;
+	}
+
+	return NULL;
+}
+
+
+static void anqp_add_elem(struct hostapd_data *hapd, struct wpabuf *buf,
+			  u16 infoid)
+{
+	struct anqp_element *elem;
+
+	elem = get_anqp_elem(hapd, infoid);
+	if (!elem)
+		return;
+	if (wpabuf_tailroom(buf) < 2 + 2 + wpabuf_len(elem->payload)) {
+		wpa_printf(MSG_DEBUG, "ANQP: No room for InfoID %u payload",
+			   infoid);
+		return;
+	}
+
+	wpabuf_put_le16(buf, infoid);
+	wpabuf_put_le16(buf, wpabuf_len(elem->payload));
+	wpabuf_put_buf(buf, elem->payload);
+}
+
+
+static int anqp_add_override(struct hostapd_data *hapd, struct wpabuf *buf,
+			     u16 infoid)
+{
+	if (get_anqp_elem(hapd, infoid)) {
+		anqp_add_elem(hapd, buf, infoid);
+		return 1;
+	}
+
+	return 0;
+}
+
+
+static void anqp_add_capab_list(struct hostapd_data *hapd,
+				struct wpabuf *buf)
+{
+	u8 *len;
+	u16 id;
+
+	if (anqp_add_override(hapd, buf, ANQP_CAPABILITY_LIST))
+		return;
+
+	len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST);
+	wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST);
+	if (hapd->conf->venue_name || get_anqp_elem(hapd, ANQP_VENUE_NAME))
+		wpabuf_put_le16(buf, ANQP_VENUE_NAME);
+	if (get_anqp_elem(hapd, ANQP_EMERGENCY_CALL_NUMBER))
+		wpabuf_put_le16(buf, ANQP_EMERGENCY_CALL_NUMBER);
+	if (hapd->conf->network_auth_type ||
+	    get_anqp_elem(hapd, ANQP_NETWORK_AUTH_TYPE))
+		wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
+	if (hapd->conf->roaming_consortium ||
+	    get_anqp_elem(hapd, ANQP_ROAMING_CONSORTIUM))
+		wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM);
+	if (hapd->conf->ipaddr_type_configured ||
+	    get_anqp_elem(hapd, ANQP_IP_ADDR_TYPE_AVAILABILITY))
+		wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
+	if (hapd->conf->nai_realm_data ||
+	    get_anqp_elem(hapd, ANQP_NAI_REALM))
+		wpabuf_put_le16(buf, ANQP_NAI_REALM);
+	if (hapd->conf->anqp_3gpp_cell_net ||
+	    get_anqp_elem(hapd, ANQP_3GPP_CELLULAR_NETWORK))
+		wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
+	if (get_anqp_elem(hapd, ANQP_AP_GEOSPATIAL_LOCATION))
+		wpabuf_put_le16(buf, ANQP_AP_GEOSPATIAL_LOCATION);
+	if (get_anqp_elem(hapd, ANQP_AP_CIVIC_LOCATION))
+		wpabuf_put_le16(buf, ANQP_AP_CIVIC_LOCATION);
+	if (get_anqp_elem(hapd, ANQP_AP_LOCATION_PUBLIC_URI))
+		wpabuf_put_le16(buf, ANQP_AP_LOCATION_PUBLIC_URI);
+	if (hapd->conf->domain_name || get_anqp_elem(hapd, ANQP_DOMAIN_NAME))
+		wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
+	if (get_anqp_elem(hapd, ANQP_EMERGENCY_ALERT_URI))
+		wpabuf_put_le16(buf, ANQP_EMERGENCY_ALERT_URI);
+	if (get_anqp_elem(hapd, ANQP_TDLS_CAPABILITY))
+		wpabuf_put_le16(buf, ANQP_TDLS_CAPABILITY);
+	if (get_anqp_elem(hapd, ANQP_EMERGENCY_NAI))
+		wpabuf_put_le16(buf, ANQP_EMERGENCY_NAI);
+	if (get_anqp_elem(hapd, ANQP_NEIGHBOR_REPORT))
+		wpabuf_put_le16(buf, ANQP_NEIGHBOR_REPORT);
+#ifdef CONFIG_FILS
+	if (!dl_list_empty(&hapd->conf->fils_realms) ||
+	    get_anqp_elem(hapd, ANQP_FILS_REALM_INFO))
+		wpabuf_put_le16(buf, ANQP_FILS_REALM_INFO);
+#endif /* CONFIG_FILS */
+	if (get_anqp_elem(hapd, ANQP_CAG))
+		wpabuf_put_le16(buf, ANQP_CAG);
+	if (hapd->conf->venue_url || get_anqp_elem(hapd, ANQP_VENUE_URL))
+		wpabuf_put_le16(buf, ANQP_VENUE_URL);
+	if (get_anqp_elem(hapd, ANQP_ADVICE_OF_CHARGE))
+		wpabuf_put_le16(buf, ANQP_ADVICE_OF_CHARGE);
+	if (get_anqp_elem(hapd, ANQP_LOCAL_CONTENT))
+		wpabuf_put_le16(buf, ANQP_LOCAL_CONTENT);
+	for (id = 280; id < 300; id++) {
+		if (get_anqp_elem(hapd, id))
+			wpabuf_put_le16(buf, id);
+	}
+#ifdef CONFIG_HS20
+	anqp_add_hs_capab_list(hapd, buf);
+#endif /* CONFIG_HS20 */
+	gas_anqp_set_element_len(buf, len);
+}
+
+
+static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf)
+{
+	if (anqp_add_override(hapd, buf, ANQP_VENUE_NAME))
+		return;
+
+	if (hapd->conf->venue_name) {
+		u8 *len;
+		unsigned int i;
+		len = gas_anqp_add_element(buf, ANQP_VENUE_NAME);
+		wpabuf_put_u8(buf, hapd->conf->venue_group);
+		wpabuf_put_u8(buf, hapd->conf->venue_type);
+		for (i = 0; i < hapd->conf->venue_name_count; i++) {
+			struct hostapd_lang_string *vn;
+			vn = &hapd->conf->venue_name[i];
+			wpabuf_put_u8(buf, 3 + vn->name_len);
+			wpabuf_put_data(buf, vn->lang, 3);
+			wpabuf_put_data(buf, vn->name, vn->name_len);
+		}
+		gas_anqp_set_element_len(buf, len);
+	}
+}
+
+
+static void anqp_add_venue_url(struct hostapd_data *hapd, struct wpabuf *buf)
+{
+	if (anqp_add_override(hapd, buf, ANQP_VENUE_URL))
+		return;
+
+	if (hapd->conf->venue_url) {
+		u8 *len;
+		unsigned int i;
+
+		len = gas_anqp_add_element(buf, ANQP_VENUE_URL);
+		for (i = 0; i < hapd->conf->venue_url_count; i++) {
+			struct hostapd_venue_url *url;
+
+			url = &hapd->conf->venue_url[i];
+			wpabuf_put_u8(buf, 1 + url->url_len);
+			wpabuf_put_u8(buf, url->venue_number);
+			wpabuf_put_data(buf, url->url, url->url_len);
+		}
+		gas_anqp_set_element_len(buf, len);
+	}
+}
+
+
+static void anqp_add_network_auth_type(struct hostapd_data *hapd,
+				       struct wpabuf *buf)
+{
+	if (anqp_add_override(hapd, buf, ANQP_NETWORK_AUTH_TYPE))
+		return;
+
+	if (hapd->conf->network_auth_type) {
+		wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
+		wpabuf_put_le16(buf, hapd->conf->network_auth_type_len);
+		wpabuf_put_data(buf, hapd->conf->network_auth_type,
+				hapd->conf->network_auth_type_len);
+	}
+}
+
+
+static void anqp_add_roaming_consortium(struct hostapd_data *hapd,
+					struct wpabuf *buf)
+{
+	unsigned int i;
+	u8 *len;
+
+	if (anqp_add_override(hapd, buf, ANQP_ROAMING_CONSORTIUM))
+		return;
+
+	len = gas_anqp_add_element(buf, ANQP_ROAMING_CONSORTIUM);
+	for (i = 0; i < hapd->conf->roaming_consortium_count; i++) {
+		struct hostapd_roaming_consortium *rc;
+		rc = &hapd->conf->roaming_consortium[i];
+		wpabuf_put_u8(buf, rc->len);
+		wpabuf_put_data(buf, rc->oi, rc->len);
+	}
+	gas_anqp_set_element_len(buf, len);
+}
+
+
+static void anqp_add_ip_addr_type_availability(struct hostapd_data *hapd,
+					       struct wpabuf *buf)
+{
+	if (anqp_add_override(hapd, buf, ANQP_IP_ADDR_TYPE_AVAILABILITY))
+		return;
+
+	if (hapd->conf->ipaddr_type_configured) {
+		wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
+		wpabuf_put_le16(buf, 1);
+		wpabuf_put_u8(buf, hapd->conf->ipaddr_type_availability);
+	}
+}
+
+
+static void anqp_add_nai_realm_eap(struct wpabuf *buf,
+				   struct hostapd_nai_realm_data *realm)
+{
+	unsigned int i, j;
+
+	wpabuf_put_u8(buf, realm->eap_method_count);
+
+	for (i = 0; i < realm->eap_method_count; i++) {
+		struct hostapd_nai_realm_eap *eap = &realm->eap_method[i];
+		wpabuf_put_u8(buf, 2 + (3 * eap->num_auths));
+		wpabuf_put_u8(buf, eap->eap_method);
+		wpabuf_put_u8(buf, eap->num_auths);
+		for (j = 0; j < eap->num_auths; j++) {
+			wpabuf_put_u8(buf, eap->auth_id[j]);
+			wpabuf_put_u8(buf, 1);
+			wpabuf_put_u8(buf, eap->auth_val[j]);
+		}
+	}
+}
+
+
+static void anqp_add_nai_realm_data(struct wpabuf *buf,
+				    struct hostapd_nai_realm_data *realm,
+				    unsigned int realm_idx)
+{
+	u8 *realm_data_len;
+
+	wpa_printf(MSG_DEBUG, "realm=%s, len=%d", realm->realm[realm_idx],
+		   (int) os_strlen(realm->realm[realm_idx]));
+	realm_data_len = wpabuf_put(buf, 2);
+	wpabuf_put_u8(buf, realm->encoding);
+	wpabuf_put_u8(buf, os_strlen(realm->realm[realm_idx]));
+	wpabuf_put_str(buf, realm->realm[realm_idx]);
+	anqp_add_nai_realm_eap(buf, realm);
+	gas_anqp_set_element_len(buf, realm_data_len);
+}
+
+
+static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd,
+					   struct wpabuf *buf,
+					   const u8 *home_realm,
+					   size_t home_realm_len)
+{
+	unsigned int i, j, k;
+	u8 num_realms, num_matching = 0, encoding, realm_len, *realm_list_len;
+	struct hostapd_nai_realm_data *realm;
+	const u8 *pos, *realm_name, *end;
+	struct {
+		unsigned int realm_data_idx;
+		unsigned int realm_idx;
+	} matches[10];
+
+	pos = home_realm;
+	end = pos + home_realm_len;
+	if (end - pos < 1) {
+		wpa_hexdump(MSG_DEBUG, "Too short NAI Home Realm Query",
+			    home_realm, home_realm_len);
+		return -1;
+	}
+	num_realms = *pos++;
+
+	for (i = 0; i < num_realms && num_matching < 10; i++) {
+		if (end - pos < 2) {
+			wpa_hexdump(MSG_DEBUG,
+				    "Truncated NAI Home Realm Query",
+				    home_realm, home_realm_len);
+			return -1;
+		}
+		encoding = *pos++;
+		realm_len = *pos++;
+		if (realm_len > end - pos) {
+			wpa_hexdump(MSG_DEBUG,
+				    "Truncated NAI Home Realm Query",
+				    home_realm, home_realm_len);
+			return -1;
+		}
+		realm_name = pos;
+		for (j = 0; j < hapd->conf->nai_realm_count &&
+			     num_matching < 10; j++) {
+			const u8 *rpos, *rend;
+			realm = &hapd->conf->nai_realm_data[j];
+			if (encoding != realm->encoding)
+				continue;
+
+			rpos = realm_name;
+			while (rpos < realm_name + realm_len &&
+			       num_matching < 10) {
+				for (rend = rpos;
+				     rend < realm_name + realm_len; rend++) {
+					if (*rend == ';')
+						break;
+				}
+				for (k = 0; k < MAX_NAI_REALMS &&
+					     realm->realm[k] &&
+					     num_matching < 10; k++) {
+					if ((int) os_strlen(realm->realm[k]) !=
+					    rend - rpos ||
+					    os_strncmp((char *) rpos,
+						       realm->realm[k],
+						       rend - rpos) != 0)
+						continue;
+					matches[num_matching].realm_data_idx =
+						j;
+					matches[num_matching].realm_idx = k;
+					num_matching++;
+				}
+				rpos = rend + 1;
+			}
+		}
+		pos += realm_len;
+	}
+
+	realm_list_len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
+	wpabuf_put_le16(buf, num_matching);
+
+	/*
+	 * There are two ways to format. 1. each realm in a NAI Realm Data unit
+	 * 2. all realms that share the same EAP methods in a NAI Realm Data
+	 * unit. The first format is likely to be bigger in size than the
+	 * second, but may be easier to parse and process by the receiver.
+	 */
+	for (i = 0; i < num_matching; i++) {
+		wpa_printf(MSG_DEBUG, "realm_idx %d, realm_data_idx %d",
+			   matches[i].realm_data_idx, matches[i].realm_idx);
+		realm = &hapd->conf->nai_realm_data[matches[i].realm_data_idx];
+		anqp_add_nai_realm_data(buf, realm, matches[i].realm_idx);
+	}
+	gas_anqp_set_element_len(buf, realm_list_len);
+	return 0;
+}
+
+
+static void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf,
+			       const u8 *home_realm, size_t home_realm_len,
+			       int nai_realm, int nai_home_realm)
+{
+	if (nai_realm && !nai_home_realm &&
+	    anqp_add_override(hapd, buf, ANQP_NAI_REALM))
+		return;
+
+	if (nai_realm && hapd->conf->nai_realm_data) {
+		u8 *len;
+		unsigned int i, j;
+		len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
+		wpabuf_put_le16(buf, hapd->conf->nai_realm_count);
+		for (i = 0; i < hapd->conf->nai_realm_count; i++) {
+			u8 *realm_data_len, *realm_len;
+			struct hostapd_nai_realm_data *realm;
+
+			realm = &hapd->conf->nai_realm_data[i];
+			realm_data_len = wpabuf_put(buf, 2);
+			wpabuf_put_u8(buf, realm->encoding);
+			realm_len = wpabuf_put(buf, 1);
+			for (j = 0; realm->realm[j]; j++) {
+				if (j > 0)
+					wpabuf_put_u8(buf, ';');
+				wpabuf_put_str(buf, realm->realm[j]);
+			}
+			*realm_len = (u8 *) wpabuf_put(buf, 0) - realm_len - 1;
+			anqp_add_nai_realm_eap(buf, realm);
+			gas_anqp_set_element_len(buf, realm_data_len);
+		}
+		gas_anqp_set_element_len(buf, len);
+	} else if (nai_home_realm && hapd->conf->nai_realm_data && home_realm) {
+		hs20_add_nai_home_realm_matches(hapd, buf, home_realm,
+						home_realm_len);
+	}
+}
+
+
+static void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd,
+					   struct wpabuf *buf)
+{
+	if (anqp_add_override(hapd, buf, ANQP_3GPP_CELLULAR_NETWORK))
+		return;
+
+	if (hapd->conf->anqp_3gpp_cell_net) {
+		wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
+		wpabuf_put_le16(buf,
+				hapd->conf->anqp_3gpp_cell_net_len);
+		wpabuf_put_data(buf, hapd->conf->anqp_3gpp_cell_net,
+				hapd->conf->anqp_3gpp_cell_net_len);
+	}
+}
+
+
+static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf)
+{
+	if (anqp_add_override(hapd, buf, ANQP_DOMAIN_NAME))
+		return;
+
+	if (hapd->conf->domain_name) {
+		wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
+		wpabuf_put_le16(buf, hapd->conf->domain_name_len);
+		wpabuf_put_data(buf, hapd->conf->domain_name,
+				hapd->conf->domain_name_len);
+	}
+}
+
+
+#ifdef CONFIG_FILS
+static void anqp_add_fils_realm_info(struct hostapd_data *hapd,
+				     struct wpabuf *buf)
+{
+	size_t count;
+
+	if (anqp_add_override(hapd, buf, ANQP_FILS_REALM_INFO))
+		return;
+
+	count = dl_list_len(&hapd->conf->fils_realms);
+	if (count > 10000)
+		count = 10000;
+	if (count) {
+		struct fils_realm *realm;
+
+		wpabuf_put_le16(buf, ANQP_FILS_REALM_INFO);
+		wpabuf_put_le16(buf, 2 * count);
+
+		dl_list_for_each(realm, &hapd->conf->fils_realms,
+				 struct fils_realm, list) {
+			if (count == 0)
+				break;
+			wpabuf_put_data(buf, realm->hash, 2);
+			count--;
+		}
+	}
+}
+#endif /* CONFIG_FILS */
+
+
+#ifdef CONFIG_HS20
+
+static void anqp_add_operator_friendly_name(struct hostapd_data *hapd,
+					    struct wpabuf *buf)
+{
+	if (hapd->conf->hs20_oper_friendly_name) {
+		u8 *len;
+		unsigned int i;
+		len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+		wpabuf_put_be24(buf, OUI_WFA);
+		wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+		wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
+		wpabuf_put_u8(buf, 0); /* Reserved */
+		for (i = 0; i < hapd->conf->hs20_oper_friendly_name_count; i++)
+		{
+			struct hostapd_lang_string *vn;
+			vn = &hapd->conf->hs20_oper_friendly_name[i];
+			wpabuf_put_u8(buf, 3 + vn->name_len);
+			wpabuf_put_data(buf, vn->lang, 3);
+			wpabuf_put_data(buf, vn->name, vn->name_len);
+		}
+		gas_anqp_set_element_len(buf, len);
+	}
+}
+
+
+static void anqp_add_wan_metrics(struct hostapd_data *hapd,
+				 struct wpabuf *buf)
+{
+	if (hapd->conf->hs20_wan_metrics) {
+		u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+		wpabuf_put_be24(buf, OUI_WFA);
+		wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+		wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
+		wpabuf_put_u8(buf, 0); /* Reserved */
+		wpabuf_put_data(buf, hapd->conf->hs20_wan_metrics, 13);
+		gas_anqp_set_element_len(buf, len);
+	}
+}
+
+
+static void anqp_add_connection_capability(struct hostapd_data *hapd,
+					   struct wpabuf *buf)
+{
+	if (hapd->conf->hs20_connection_capability) {
+		u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+		wpabuf_put_be24(buf, OUI_WFA);
+		wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+		wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
+		wpabuf_put_u8(buf, 0); /* Reserved */
+		wpabuf_put_data(buf, hapd->conf->hs20_connection_capability,
+				hapd->conf->hs20_connection_capability_len);
+		gas_anqp_set_element_len(buf, len);
+	}
+}
+
+
+static void anqp_add_operating_class(struct hostapd_data *hapd,
+				     struct wpabuf *buf)
+{
+	if (hapd->conf->hs20_operating_class) {
+		u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+		wpabuf_put_be24(buf, OUI_WFA);
+		wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+		wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
+		wpabuf_put_u8(buf, 0); /* Reserved */
+		wpabuf_put_data(buf, hapd->conf->hs20_operating_class,
+				hapd->conf->hs20_operating_class_len);
+		gas_anqp_set_element_len(buf, len);
+	}
+}
+
+
+static void anqp_add_icon(struct wpabuf *buf, struct hostapd_bss_config *bss,
+			  const char *name)
+{
+	size_t j;
+	struct hs20_icon *icon = NULL;
+
+	for (j = 0; j < bss->hs20_icons_count && !icon; j++) {
+		if (os_strcmp(name, bss->hs20_icons[j].name) == 0)
+			icon = &bss->hs20_icons[j];
+	}
+	if (!icon)
+		return; /* icon info not found */
+
+	wpabuf_put_le16(buf, icon->width);
+	wpabuf_put_le16(buf, icon->height);
+	wpabuf_put_data(buf, icon->language, 3);
+	wpabuf_put_u8(buf, os_strlen(icon->type));
+	wpabuf_put_str(buf, icon->type);
+	wpabuf_put_u8(buf, os_strlen(icon->name));
+	wpabuf_put_str(buf, icon->name);
+}
+
+
+static void anqp_add_osu_provider(struct wpabuf *buf,
+				  struct hostapd_bss_config *bss,
+				  struct hs20_osu_provider *p)
+{
+	u8 *len, *len2, *count;
+	unsigned int i;
+
+	len = wpabuf_put(buf, 2); /* OSU Provider Length to be filled */
+
+	/* OSU Friendly Name Duples */
+	len2 = wpabuf_put(buf, 2);
+	for (i = 0; i < p->friendly_name_count; i++) {
+		struct hostapd_lang_string *s = &p->friendly_name[i];
+		wpabuf_put_u8(buf, 3 + s->name_len);
+		wpabuf_put_data(buf, s->lang, 3);
+		wpabuf_put_data(buf, s->name, s->name_len);
+	}
+	WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
+
+	/* OSU Server URI */
+	if (p->server_uri) {
+		wpabuf_put_u8(buf, os_strlen(p->server_uri));
+		wpabuf_put_str(buf, p->server_uri);
+	} else
+		wpabuf_put_u8(buf, 0);
+
+	/* OSU Method List */
+	count = wpabuf_put(buf, 1);
+	for (i = 0; p->method_list && p->method_list[i] >= 0; i++)
+		wpabuf_put_u8(buf, p->method_list[i]);
+	*count = i;
+
+	/* Icons Available */
+	len2 = wpabuf_put(buf, 2);
+	for (i = 0; i < p->icons_count; i++)
+		anqp_add_icon(buf, bss, p->icons[i]);
+	WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
+
+	/* OSU_NAI */
+	if (p->osu_nai) {
+		wpabuf_put_u8(buf, os_strlen(p->osu_nai));
+		wpabuf_put_str(buf, p->osu_nai);
+	} else
+		wpabuf_put_u8(buf, 0);
+
+	/* OSU Service Description Duples */
+	len2 = wpabuf_put(buf, 2);
+	for (i = 0; i < p->service_desc_count; i++) {
+		struct hostapd_lang_string *s = &p->service_desc[i];
+		wpabuf_put_u8(buf, 3 + s->name_len);
+		wpabuf_put_data(buf, s->lang, 3);
+		wpabuf_put_data(buf, s->name, s->name_len);
+	}
+	WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
+
+	WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
+}
+
+
+static void anqp_add_osu_providers_list(struct hostapd_data *hapd,
+					struct wpabuf *buf)
+{
+	if (hapd->conf->hs20_osu_providers_count) {
+		size_t i;
+		u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+		wpabuf_put_be24(buf, OUI_WFA);
+		wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+		wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
+		wpabuf_put_u8(buf, 0); /* Reserved */
+
+		/* OSU SSID */
+		wpabuf_put_u8(buf, hapd->conf->osu_ssid_len);
+		wpabuf_put_data(buf, hapd->conf->osu_ssid,
+				hapd->conf->osu_ssid_len);
+
+		/* Number of OSU Providers */
+		wpabuf_put_u8(buf, hapd->conf->hs20_osu_providers_count);
+
+		for (i = 0; i < hapd->conf->hs20_osu_providers_count; i++) {
+			anqp_add_osu_provider(
+				buf, hapd->conf,
+				&hapd->conf->hs20_osu_providers[i]);
+		}
+
+		gas_anqp_set_element_len(buf, len);
+	}
+}
+
+
+static void anqp_add_osu_provider_nai(struct wpabuf *buf,
+				      struct hs20_osu_provider *p)
+{
+	/* OSU_NAI for shared BSS (Single SSID) */
+	if (p->osu_nai2) {
+		wpabuf_put_u8(buf, os_strlen(p->osu_nai2));
+		wpabuf_put_str(buf, p->osu_nai2);
+	} else {
+		wpabuf_put_u8(buf, 0);
+	}
+}
+
+
+static void anqp_add_osu_providers_nai_list(struct hostapd_data *hapd,
+					    struct wpabuf *buf)
+{
+	if (hapd->conf->hs20_osu_providers_nai_count) {
+		size_t i;
+		u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+		wpabuf_put_be24(buf, OUI_WFA);
+		wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+		wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_NAI_LIST);
+		wpabuf_put_u8(buf, 0); /* Reserved */
+
+		for (i = 0; i < hapd->conf->hs20_osu_providers_count; i++) {
+			anqp_add_osu_provider_nai(
+				buf, &hapd->conf->hs20_osu_providers[i]);
+		}
+
+		gas_anqp_set_element_len(buf, len);
+	}
+}
+
+
+static void anqp_add_icon_binary_file(struct hostapd_data *hapd,
+				      struct wpabuf *buf,
+				      const u8 *name, size_t name_len)
+{
+	struct hs20_icon *icon;
+	size_t i;
+	u8 *len;
+
+	wpa_hexdump_ascii(MSG_DEBUG, "HS 2.0: Requested Icon Filename",
+			  name, name_len);
+	for (i = 0; i < hapd->conf->hs20_icons_count; i++) {
+		icon = &hapd->conf->hs20_icons[i];
+		if (name_len == os_strlen(icon->name) &&
+		    os_memcmp(name, icon->name, name_len) == 0)
+			break;
+	}
+
+	if (i < hapd->conf->hs20_icons_count)
+		icon = &hapd->conf->hs20_icons[i];
+	else
+		icon = NULL;
+
+	len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+	wpabuf_put_be24(buf, OUI_WFA);
+	wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+	wpabuf_put_u8(buf, HS20_STYPE_ICON_BINARY_FILE);
+	wpabuf_put_u8(buf, 0); /* Reserved */
+
+	if (icon) {
+		char *data;
+		size_t data_len;
+
+		data = os_readfile(icon->file, &data_len);
+		if (data == NULL || data_len > 65535) {
+			wpabuf_put_u8(buf, 2); /* Download Status:
+						* Unspecified file error */
+			wpabuf_put_u8(buf, 0);
+			wpabuf_put_le16(buf, 0);
+		} else {
+			wpabuf_put_u8(buf, 0); /* Download Status: Success */
+			wpabuf_put_u8(buf, os_strlen(icon->type));
+			wpabuf_put_str(buf, icon->type);
+			wpabuf_put_le16(buf, data_len);
+			wpabuf_put_data(buf, data, data_len);
+		}
+		os_free(data);
+	} else {
+		wpabuf_put_u8(buf, 1); /* Download Status: File not found */
+		wpabuf_put_u8(buf, 0);
+		wpabuf_put_le16(buf, 0);
+	}
+
+	gas_anqp_set_element_len(buf, len);
+}
+
+
+static void anqp_add_operator_icon_metadata(struct hostapd_data *hapd,
+					    struct wpabuf *buf)
+{
+	struct hostapd_bss_config *bss = hapd->conf;
+	size_t i;
+	u8 *len;
+
+	if (!bss->hs20_operator_icon_count)
+		return;
+
+	len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+
+	wpabuf_put_be24(buf, OUI_WFA);
+	wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+	wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_ICON_METADATA);
+	wpabuf_put_u8(buf, 0); /* Reserved */
+
+	for (i = 0; i < bss->hs20_operator_icon_count; i++)
+		anqp_add_icon(buf, bss, bss->hs20_operator_icon[i]);
+
+	gas_anqp_set_element_len(buf, len);
+}
+
+#endif /* CONFIG_HS20 */
+
+
+#ifdef CONFIG_MBO
+static void anqp_add_mbo_cell_data_conn_pref(struct hostapd_data *hapd,
+					     struct wpabuf *buf)
+{
+	if (hapd->conf->mbo_cell_data_conn_pref >= 0) {
+		u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+		wpabuf_put_be24(buf, OUI_WFA);
+		wpabuf_put_u8(buf, MBO_ANQP_OUI_TYPE);
+		wpabuf_put_u8(buf, MBO_ANQP_SUBTYPE_CELL_CONN_PREF);
+		wpabuf_put_u8(buf, hapd->conf->mbo_cell_data_conn_pref);
+		gas_anqp_set_element_len(buf, len);
+	}
+}
+#endif /* CONFIG_MBO */
+
+
+static size_t anqp_get_required_len(struct hostapd_data *hapd,
+				    const u16 *infoid,
+				    unsigned int num_infoid)
+{
+	size_t len = 0;
+	unsigned int i;
+
+	for (i = 0; i < num_infoid; i++) {
+		struct anqp_element *elem = get_anqp_elem(hapd, infoid[i]);
+
+		if (elem)
+			len += 2 + 2 + wpabuf_len(elem->payload);
+	}
+
+	return len;
+}
+
+
+static struct wpabuf *
+gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
+				unsigned int request,
+				const u8 *home_realm, size_t home_realm_len,
+				const u8 *icon_name, size_t icon_name_len,
+				const u16 *extra_req,
+				unsigned int num_extra_req)
+{
+	struct wpabuf *buf;
+	size_t len;
+	unsigned int i;
+
+	len = 1400;
+	if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
+		len += 1000;
+	if (request & ANQP_REQ_ICON_REQUEST)
+		len += 65536;
+#ifdef CONFIG_FILS
+	if (request & ANQP_FILS_REALM_INFO)
+		len += 2 * dl_list_len(&hapd->conf->fils_realms);
+#endif /* CONFIG_FILS */
+	len += anqp_get_required_len(hapd, extra_req, num_extra_req);
+
+	buf = wpabuf_alloc(len);
+	if (buf == NULL)
+		return NULL;
+
+	if (request & ANQP_REQ_CAPABILITY_LIST)
+		anqp_add_capab_list(hapd, buf);
+	if (request & ANQP_REQ_VENUE_NAME)
+		anqp_add_venue_name(hapd, buf);
+	if (request & ANQP_REQ_EMERGENCY_CALL_NUMBER)
+		anqp_add_elem(hapd, buf, ANQP_EMERGENCY_CALL_NUMBER);
+	if (request & ANQP_REQ_NETWORK_AUTH_TYPE)
+		anqp_add_network_auth_type(hapd, buf);
+	if (request & ANQP_REQ_ROAMING_CONSORTIUM)
+		anqp_add_roaming_consortium(hapd, buf);
+	if (request & ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY)
+		anqp_add_ip_addr_type_availability(hapd, buf);
+	if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
+		anqp_add_nai_realm(hapd, buf, home_realm, home_realm_len,
+				   request & ANQP_REQ_NAI_REALM,
+				   request & ANQP_REQ_NAI_HOME_REALM);
+	if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK)
+		anqp_add_3gpp_cellular_network(hapd, buf);
+	if (request & ANQP_REQ_AP_GEOSPATIAL_LOCATION)
+		anqp_add_elem(hapd, buf, ANQP_AP_GEOSPATIAL_LOCATION);
+	if (request & ANQP_REQ_AP_CIVIC_LOCATION)
+		anqp_add_elem(hapd, buf, ANQP_AP_CIVIC_LOCATION);
+	if (request & ANQP_REQ_AP_LOCATION_PUBLIC_URI)
+		anqp_add_elem(hapd, buf, ANQP_AP_LOCATION_PUBLIC_URI);
+	if (request & ANQP_REQ_DOMAIN_NAME)
+		anqp_add_domain_name(hapd, buf);
+	if (request & ANQP_REQ_EMERGENCY_ALERT_URI)
+		anqp_add_elem(hapd, buf, ANQP_EMERGENCY_ALERT_URI);
+	if (request & ANQP_REQ_TDLS_CAPABILITY)
+		anqp_add_elem(hapd, buf, ANQP_TDLS_CAPABILITY);
+	if (request & ANQP_REQ_EMERGENCY_NAI)
+		anqp_add_elem(hapd, buf, ANQP_EMERGENCY_NAI);
+
+	for (i = 0; i < num_extra_req; i++) {
+#ifdef CONFIG_FILS
+		if (extra_req[i] == ANQP_FILS_REALM_INFO) {
+			anqp_add_fils_realm_info(hapd, buf);
+			continue;
+		}
+#endif /* CONFIG_FILS */
+		if (extra_req[i] == ANQP_VENUE_URL) {
+			anqp_add_venue_url(hapd, buf);
+			continue;
+		}
+		anqp_add_elem(hapd, buf, extra_req[i]);
+	}
+
+#ifdef CONFIG_HS20
+	if (request & ANQP_REQ_HS_CAPABILITY_LIST)
+		anqp_add_hs_capab_list(hapd, buf);
+	if (request & ANQP_REQ_OPERATOR_FRIENDLY_NAME)
+		anqp_add_operator_friendly_name(hapd, buf);
+	if (request & ANQP_REQ_WAN_METRICS)
+		anqp_add_wan_metrics(hapd, buf);
+	if (request & ANQP_REQ_CONNECTION_CAPABILITY)
+		anqp_add_connection_capability(hapd, buf);
+	if (request & ANQP_REQ_OPERATING_CLASS)
+		anqp_add_operating_class(hapd, buf);
+	if (request & ANQP_REQ_OSU_PROVIDERS_LIST)
+		anqp_add_osu_providers_list(hapd, buf);
+	if (request & ANQP_REQ_ICON_REQUEST)
+		anqp_add_icon_binary_file(hapd, buf, icon_name, icon_name_len);
+	if (request & ANQP_REQ_OPERATOR_ICON_METADATA)
+		anqp_add_operator_icon_metadata(hapd, buf);
+	if (request & ANQP_REQ_OSU_PROVIDERS_NAI_LIST)
+		anqp_add_osu_providers_nai_list(hapd, buf);
+#endif /* CONFIG_HS20 */
+
+#ifdef CONFIG_MBO
+	if (request & ANQP_REQ_MBO_CELL_DATA_CONN_PREF)
+		anqp_add_mbo_cell_data_conn_pref(hapd, buf);
+#endif /* CONFIG_MBO */
+
+	return buf;
+}
+
+
+#define ANQP_MAX_EXTRA_REQ 20
+
+struct anqp_query_info {
+	unsigned int request;
+	const u8 *home_realm_query;
+	size_t home_realm_query_len;
+	const u8 *icon_name;
+	size_t icon_name_len;
+	int p2p_sd;
+	u16 extra_req[ANQP_MAX_EXTRA_REQ];
+	unsigned int num_extra_req;
+};
+
+
+static void set_anqp_req(unsigned int bit, const char *name, int local,
+			 struct anqp_query_info *qi)
+{
+	qi->request |= bit;
+	if (local) {
+		wpa_printf(MSG_DEBUG, "ANQP: %s (local)", name);
+	} else {
+		wpa_printf(MSG_DEBUG, "ANQP: %s not available", name);
+	}
+}
+
+
+static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id,
+				  struct anqp_query_info *qi)
+{
+	switch (info_id) {
+	case ANQP_CAPABILITY_LIST:
+		set_anqp_req(ANQP_REQ_CAPABILITY_LIST, "Capability List", 1,
+			     qi);
+		break;
+	case ANQP_VENUE_NAME:
+		set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name",
+			     hapd->conf->venue_name != NULL, qi);
+		break;
+	case ANQP_EMERGENCY_CALL_NUMBER:
+		set_anqp_req(ANQP_REQ_EMERGENCY_CALL_NUMBER,
+			     "Emergency Call Number",
+			     get_anqp_elem(hapd, info_id) != NULL, qi);
+		break;
+	case ANQP_NETWORK_AUTH_TYPE:
+		set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type",
+			     hapd->conf->network_auth_type != NULL, qi);
+		break;
+	case ANQP_ROAMING_CONSORTIUM:
+		set_anqp_req(ANQP_REQ_ROAMING_CONSORTIUM, "Roaming Consortium",
+			     hapd->conf->roaming_consortium != NULL, qi);
+		break;
+	case ANQP_IP_ADDR_TYPE_AVAILABILITY:
+		set_anqp_req(ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY,
+			     "IP Addr Type Availability",
+			     hapd->conf->ipaddr_type_configured, qi);
+		break;
+	case ANQP_NAI_REALM:
+		set_anqp_req(ANQP_REQ_NAI_REALM, "NAI Realm",
+			     hapd->conf->nai_realm_data != NULL, qi);
+		break;
+	case ANQP_3GPP_CELLULAR_NETWORK:
+		set_anqp_req(ANQP_REQ_3GPP_CELLULAR_NETWORK,
+			     "3GPP Cellular Network",
+			     hapd->conf->anqp_3gpp_cell_net != NULL, qi);
+		break;
+	case ANQP_AP_GEOSPATIAL_LOCATION:
+		set_anqp_req(ANQP_REQ_AP_GEOSPATIAL_LOCATION,
+			     "AP Geospatial Location",
+			     get_anqp_elem(hapd, info_id) != NULL, qi);
+		break;
+	case ANQP_AP_CIVIC_LOCATION:
+		set_anqp_req(ANQP_REQ_AP_CIVIC_LOCATION,
+			     "AP Civic Location",
+			     get_anqp_elem(hapd, info_id) != NULL, qi);
+		break;
+	case ANQP_AP_LOCATION_PUBLIC_URI:
+		set_anqp_req(ANQP_REQ_AP_LOCATION_PUBLIC_URI,
+			     "AP Location Public URI",
+			     get_anqp_elem(hapd, info_id) != NULL, qi);
+		break;
+	case ANQP_DOMAIN_NAME:
+		set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name",
+			     hapd->conf->domain_name != NULL, qi);
+		break;
+	case ANQP_EMERGENCY_ALERT_URI:
+		set_anqp_req(ANQP_REQ_EMERGENCY_ALERT_URI,
+			     "Emergency Alert URI",
+			     get_anqp_elem(hapd, info_id) != NULL, qi);
+		break;
+	case ANQP_TDLS_CAPABILITY:
+		set_anqp_req(ANQP_REQ_TDLS_CAPABILITY,
+			     "TDLS Capability",
+			     get_anqp_elem(hapd, info_id) != NULL, qi);
+		break;
+	case ANQP_EMERGENCY_NAI:
+		set_anqp_req(ANQP_REQ_EMERGENCY_NAI,
+			     "Emergency NAI",
+			     get_anqp_elem(hapd, info_id) != NULL, qi);
+		break;
+	default:
+#ifdef CONFIG_FILS
+		if (info_id == ANQP_FILS_REALM_INFO &&
+		    !dl_list_empty(&hapd->conf->fils_realms)) {
+			wpa_printf(MSG_DEBUG,
+				   "ANQP: FILS Realm Information (local)");
+		} else
+#endif /* CONFIG_FILS */
+		if (info_id == ANQP_VENUE_URL && hapd->conf->venue_url) {
+			wpa_printf(MSG_DEBUG,
+				   "ANQP: Venue URL (local)");
+		} else if (!get_anqp_elem(hapd, info_id)) {
+			wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
+				   info_id);
+			break;
+		}
+		if (qi->num_extra_req == ANQP_MAX_EXTRA_REQ) {
+			wpa_printf(MSG_DEBUG,
+				   "ANQP: No more room for extra requests - ignore Info Id %u",
+				   info_id);
+			break;
+		}
+		wpa_printf(MSG_DEBUG, "ANQP: Info Id %u (local)", info_id);
+		qi->extra_req[qi->num_extra_req] = info_id;
+		qi->num_extra_req++;
+		break;
+	}
+}
+
+
+static void rx_anqp_query_list(struct hostapd_data *hapd,
+			       const u8 *pos, const u8 *end,
+			       struct anqp_query_info *qi)
+{
+	wpa_printf(MSG_DEBUG, "ANQP: %u Info IDs requested in Query list",
+		   (unsigned int) (end - pos) / 2);
+
+	while (end - pos >= 2) {
+		rx_anqp_query_list_id(hapd, WPA_GET_LE16(pos), qi);
+		pos += 2;
+	}
+}
+
+
+#ifdef CONFIG_HS20
+
+static void rx_anqp_hs_query_list(struct hostapd_data *hapd, u8 subtype,
+				  struct anqp_query_info *qi)
+{
+	switch (subtype) {
+	case HS20_STYPE_CAPABILITY_LIST:
+		set_anqp_req(ANQP_REQ_HS_CAPABILITY_LIST, "HS Capability List",
+			     1, qi);
+		break;
+	case HS20_STYPE_OPERATOR_FRIENDLY_NAME:
+		set_anqp_req(ANQP_REQ_OPERATOR_FRIENDLY_NAME,
+			     "Operator Friendly Name",
+			     hapd->conf->hs20_oper_friendly_name != NULL, qi);
+		break;
+	case HS20_STYPE_WAN_METRICS:
+		set_anqp_req(ANQP_REQ_WAN_METRICS, "WAN Metrics",
+			     hapd->conf->hs20_wan_metrics != NULL, qi);
+		break;
+	case HS20_STYPE_CONNECTION_CAPABILITY:
+		set_anqp_req(ANQP_REQ_CONNECTION_CAPABILITY,
+			     "Connection Capability",
+			     hapd->conf->hs20_connection_capability != NULL,
+			     qi);
+		break;
+	case HS20_STYPE_OPERATING_CLASS:
+		set_anqp_req(ANQP_REQ_OPERATING_CLASS, "Operating Class",
+			     hapd->conf->hs20_operating_class != NULL, qi);
+		break;
+	case HS20_STYPE_OSU_PROVIDERS_LIST:
+		set_anqp_req(ANQP_REQ_OSU_PROVIDERS_LIST, "OSU Providers list",
+			     hapd->conf->hs20_osu_providers_count, qi);
+		break;
+	case HS20_STYPE_OPERATOR_ICON_METADATA:
+		set_anqp_req(ANQP_REQ_OPERATOR_ICON_METADATA,
+			     "Operator Icon Metadata",
+			     hapd->conf->hs20_operator_icon_count, qi);
+		break;
+	case HS20_STYPE_OSU_PROVIDERS_NAI_LIST:
+		set_anqp_req(ANQP_REQ_OSU_PROVIDERS_NAI_LIST,
+			     "OSU Providers NAI List",
+			     hapd->conf->hs20_osu_providers_nai_count, qi);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u",
+			   subtype);
+		break;
+	}
+}
+
+
+static void rx_anqp_hs_nai_home_realm(struct hostapd_data *hapd,
+				      const u8 *pos, const u8 *end,
+				      struct anqp_query_info *qi)
+{
+	qi->request |= ANQP_REQ_NAI_HOME_REALM;
+	qi->home_realm_query = pos;
+	qi->home_realm_query_len = end - pos;
+	if (hapd->conf->nai_realm_data != NULL) {
+		wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query "
+			   "(local)");
+	} else {
+		wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query not "
+			   "available");
+	}
+}
+
+
+static void rx_anqp_hs_icon_request(struct hostapd_data *hapd,
+				    const u8 *pos, const u8 *end,
+				    struct anqp_query_info *qi)
+{
+	qi->request |= ANQP_REQ_ICON_REQUEST;
+	qi->icon_name = pos;
+	qi->icon_name_len = end - pos;
+	if (hapd->conf->hs20_icons_count) {
+		wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query "
+			   "(local)");
+	} else {
+		wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query not "
+			   "available");
+	}
+}
+
+
+static void rx_anqp_vendor_specific_hs20(struct hostapd_data *hapd,
+					 const u8 *pos, const u8 *end,
+					 struct anqp_query_info *qi)
+{
+	u8 subtype;
+
+	if (end - pos <= 1)
+		return;
+
+	subtype = *pos++;
+	pos++; /* Reserved */
+	switch (subtype) {
+	case HS20_STYPE_QUERY_LIST:
+		wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Query List");
+		while (pos < end) {
+			rx_anqp_hs_query_list(hapd, *pos, qi);
+			pos++;
+		}
+		break;
+	case HS20_STYPE_NAI_HOME_REALM_QUERY:
+		rx_anqp_hs_nai_home_realm(hapd, pos, end, qi);
+		break;
+	case HS20_STYPE_ICON_REQUEST:
+		rx_anqp_hs_icon_request(hapd, pos, end, qi);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 query subtype "
+			   "%u", subtype);
+		break;
+	}
+}
+
+#endif /* CONFIG_HS20 */
+
+
+#ifdef CONFIG_P2P
+static void rx_anqp_vendor_specific_p2p(struct hostapd_data *hapd,
+					struct anqp_query_info *qi)
+{
+	/*
+	 * This is for P2P SD and will be taken care of by the P2P
+	 * implementation. This query needs to be ignored in the generic
+	 * GAS server to avoid duplicated response.
+	 */
+	wpa_printf(MSG_DEBUG,
+		   "ANQP: Ignore WFA vendor type %u (P2P SD) in generic GAS server",
+		   P2P_OUI_TYPE);
+	qi->p2p_sd = 1;
+	return;
+}
+#endif /* CONFIG_P2P */
+
+
+#ifdef CONFIG_MBO
+
+static void rx_anqp_mbo_query_list(struct hostapd_data *hapd, u8 subtype,
+				  struct anqp_query_info *qi)
+{
+	switch (subtype) {
+	case MBO_ANQP_SUBTYPE_CELL_CONN_PREF:
+		set_anqp_req(ANQP_REQ_MBO_CELL_DATA_CONN_PREF,
+			     "Cellular Data Connection Preference",
+			     hapd->conf->mbo_cell_data_conn_pref >= 0, qi);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "ANQP: Unsupported MBO subtype %u",
+			   subtype);
+		break;
+	}
+}
+
+
+static void rx_anqp_vendor_specific_mbo(struct hostapd_data *hapd,
+					const u8 *pos, const u8 *end,
+					struct anqp_query_info *qi)
+{
+	u8 subtype;
+
+	if (end - pos < 1)
+		return;
+
+	subtype = *pos++;
+	switch (subtype) {
+	case MBO_ANQP_SUBTYPE_QUERY_LIST:
+		wpa_printf(MSG_DEBUG, "ANQP: MBO Query List");
+		while (pos < end) {
+			rx_anqp_mbo_query_list(hapd, *pos, qi);
+			pos++;
+		}
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "ANQP: Unsupported MBO query subtype %u",
+			   subtype);
+		break;
+	}
+}
+
+#endif /* CONFIG_MBO */
+
+
+static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
+				    const u8 *pos, const u8 *end,
+				    struct anqp_query_info *qi)
+{
+	u32 oui;
+
+	if (end - pos < 4) {
+		wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP "
+			   "Query element");
+		return;
+	}
+
+	oui = WPA_GET_BE24(pos);
+	pos += 3;
+	if (oui != OUI_WFA) {
+		wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x",
+			   oui);
+		return;
+	}
+
+	switch (*pos) {
+#ifdef CONFIG_P2P
+	case P2P_OUI_TYPE:
+		rx_anqp_vendor_specific_p2p(hapd, qi);
+		break;
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_HS20
+	case HS20_ANQP_OUI_TYPE:
+		rx_anqp_vendor_specific_hs20(hapd, pos + 1, end, qi);
+		break;
+#endif /* CONFIG_HS20 */
+#ifdef CONFIG_MBO
+	case MBO_ANQP_OUI_TYPE:
+		rx_anqp_vendor_specific_mbo(hapd, pos + 1, end, qi);
+		break;
+#endif /* CONFIG_MBO */
+	default:
+		wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u",
+			   *pos);
+		break;
+	}
+}
+
+
+static void gas_serv_req_local_processing(struct hostapd_data *hapd,
+					  const u8 *sa, u8 dialog_token,
+					  struct anqp_query_info *qi, int prot,
+					  int std_addr3)
+{
+	struct wpabuf *buf, *tx_buf;
+
+	buf = gas_serv_build_gas_resp_payload(hapd, qi->request,
+					      qi->home_realm_query,
+					      qi->home_realm_query_len,
+					      qi->icon_name, qi->icon_name_len,
+					      qi->extra_req, qi->num_extra_req);
+	wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses",
+			buf);
+	if (!buf)
+		return;
+#ifdef CONFIG_P2P
+	if (wpabuf_len(buf) == 0 && qi->p2p_sd) {
+		wpa_printf(MSG_DEBUG,
+			   "ANQP: Do not send response to P2P SD from generic GAS service (P2P SD implementation will process this)");
+		wpabuf_free(buf);
+		return;
+	}
+#endif /* CONFIG_P2P */
+
+	if (wpabuf_len(buf) > hapd->conf->gas_frag_limit ||
+	    hapd->conf->gas_comeback_delay) {
+		struct gas_dialog_info *di;
+		u16 comeback_delay = 1;
+
+		if (hapd->conf->gas_comeback_delay) {
+			/* Testing - allow overriding of the delay value */
+			comeback_delay = hapd->conf->gas_comeback_delay;
+		}
+
+		wpa_printf(MSG_DEBUG, "ANQP: Too long response to fit in "
+			   "initial response - use GAS comeback");
+		di = gas_dialog_create(hapd, sa, dialog_token);
+		if (!di) {
+			wpa_printf(MSG_INFO, "ANQP: Could not create dialog "
+				   "for " MACSTR " (dialog token %u)",
+				   MAC2STR(sa), dialog_token);
+			wpabuf_free(buf);
+			tx_buf = gas_anqp_build_initial_resp_buf(
+				dialog_token, WLAN_STATUS_UNSPECIFIED_FAILURE,
+				0, NULL);
+		} else {
+			di->prot = prot;
+			di->sd_resp = buf;
+			di->sd_resp_pos = 0;
+			tx_buf = gas_anqp_build_initial_resp_buf(
+				dialog_token, WLAN_STATUS_SUCCESS,
+				comeback_delay, NULL);
+		}
+	} else {
+		wpa_printf(MSG_DEBUG, "ANQP: Initial response (no comeback)");
+		tx_buf = gas_anqp_build_initial_resp_buf(
+			dialog_token, WLAN_STATUS_SUCCESS, 0, buf);
+		wpabuf_free(buf);
+	}
+	if (!tx_buf)
+		return;
+	if (prot)
+		convert_to_protected_dual(tx_buf);
+	if (std_addr3)
+		hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
+					wpabuf_head(tx_buf),
+					wpabuf_len(tx_buf));
+	else
+		hostapd_drv_send_action_addr3_ap(hapd, hapd->iface->freq, 0, sa,
+						 wpabuf_head(tx_buf),
+						 wpabuf_len(tx_buf));
+	wpabuf_free(tx_buf);
+}
+
+
+#ifdef CONFIG_DPP
+void gas_serv_req_dpp_processing(struct hostapd_data *hapd,
+				 const u8 *sa, u8 dialog_token,
+				 int prot, struct wpabuf *buf, int freq)
+{
+	struct wpabuf *tx_buf;
+
+	if (wpabuf_len(buf) > hapd->conf->gas_frag_limit ||
+	    hapd->conf->gas_comeback_delay) {
+		struct gas_dialog_info *di;
+		u16 comeback_delay = 1;
+
+		if (hapd->conf->gas_comeback_delay) {
+			/* Testing - allow overriding of the delay value */
+			comeback_delay = hapd->conf->gas_comeback_delay;
+		}
+
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Too long response to fit in initial response - use GAS comeback");
+		di = gas_dialog_create(hapd, sa, dialog_token);
+		if (!di) {
+			wpa_printf(MSG_INFO, "DPP: Could not create dialog for "
+				   MACSTR " (dialog token %u)",
+				   MAC2STR(sa), dialog_token);
+			wpabuf_free(buf);
+			tx_buf = gas_build_initial_resp(
+				dialog_token, WLAN_STATUS_UNSPECIFIED_FAILURE,
+				0, 10);
+			if (tx_buf)
+				gas_serv_write_dpp_adv_proto(tx_buf);
+		} else {
+			di->prot = prot;
+			di->sd_resp = buf;
+			di->sd_resp_pos = 0;
+			di->dpp = 1;
+			tx_buf = gas_build_initial_resp(
+				dialog_token, WLAN_STATUS_SUCCESS,
+				comeback_delay, 10 + 2);
+			if (tx_buf) {
+				gas_serv_write_dpp_adv_proto(tx_buf);
+				wpabuf_put_le16(tx_buf, 0);
+			}
+		}
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: GAS Initial response (no comeback)");
+		tx_buf = gas_build_initial_resp(
+			dialog_token, WLAN_STATUS_SUCCESS, 0,
+			10 + 2 + wpabuf_len(buf));
+		if (tx_buf) {
+			gas_serv_write_dpp_adv_proto(tx_buf);
+			wpabuf_put_le16(tx_buf, wpabuf_len(buf));
+			wpabuf_put_buf(tx_buf, buf);
+			hostapd_dpp_gas_status_handler(hapd, 1);
+		}
+		wpabuf_free(buf);
+	}
+	if (!tx_buf)
+		return;
+	if (prot)
+		convert_to_protected_dual(tx_buf);
+	hostapd_drv_send_action(hapd, freq ? freq : hapd->iface->freq, 0, sa,
+				wpabuf_head(tx_buf),
+				wpabuf_len(tx_buf));
+	wpabuf_free(tx_buf);
+}
+#endif /* CONFIG_DPP */
+
+
+static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
+					const u8 *sa,
+					const u8 *data, size_t len, int prot,
+					int std_addr3, int freq)
+{
+	const u8 *pos = data;
+	const u8 *end = data + len;
+	const u8 *next;
+	u8 dialog_token;
+	u16 slen;
+	struct anqp_query_info qi;
+	const u8 *adv_proto;
+#ifdef CONFIG_DPP
+	int dpp = 0;
+#endif /* CONFIG_DPP */
+
+	if (len < 1 + 2)
+		return;
+
+	os_memset(&qi, 0, sizeof(qi));
+
+	dialog_token = *pos++;
+	wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+		"GAS: GAS Initial Request from " MACSTR " (dialog token %u) ",
+		MAC2STR(sa), dialog_token);
+
+	if (*pos != WLAN_EID_ADV_PROTO) {
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+			"GAS: Unexpected IE in GAS Initial Request: %u", *pos);
+		return;
+	}
+	adv_proto = pos++;
+
+	slen = *pos++;
+	if (slen > end - pos || slen < 2) {
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+			"GAS: Invalid IE in GAS Initial Request");
+		return;
+	}
+	next = pos + slen;
+	pos++; /* skip QueryRespLenLimit and PAME-BI */
+
+#ifdef CONFIG_DPP
+	if (slen == 8 && *pos == WLAN_EID_VENDOR_SPECIFIC &&
+	    pos[1] == 5 && WPA_GET_BE24(&pos[2]) == OUI_WFA &&
+	    pos[5] == DPP_OUI_TYPE && pos[6] == 0x01) {
+		wpa_printf(MSG_DEBUG, "DPP: Configuration Request");
+		dpp = 1;
+	} else
+#endif /* CONFIG_DPP */
+
+	if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
+		struct wpabuf *buf;
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+			"GAS: Unsupported GAS advertisement protocol id %u",
+			*pos);
+		if (sa[0] & 0x01)
+			return; /* Invalid source address - drop silently */
+		buf = gas_build_initial_resp(
+			dialog_token, WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED,
+			0, 2 + slen + 2);
+		if (buf == NULL)
+			return;
+		wpabuf_put_data(buf, adv_proto, 2 + slen);
+		wpabuf_put_le16(buf, 0); /* Query Response Length */
+		if (prot)
+			convert_to_protected_dual(buf);
+		if (std_addr3)
+			hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
+						wpabuf_head(buf),
+						wpabuf_len(buf));
+		else
+			hostapd_drv_send_action_addr3_ap(hapd,
+							 hapd->iface->freq, 0,
+							 sa, wpabuf_head(buf),
+							 wpabuf_len(buf));
+		wpabuf_free(buf);
+		return;
+	}
+
+	pos = next;
+	/* Query Request */
+	if (end - pos < 2)
+		return;
+	slen = WPA_GET_LE16(pos);
+	pos += 2;
+	if (slen > end - pos)
+		return;
+	end = pos + slen;
+
+#ifdef CONFIG_DPP
+	if (dpp) {
+		struct wpabuf *msg;
+
+		msg = hostapd_dpp_gas_req_handler(hapd, sa, pos, slen,
+						  data, len);
+		if (!msg)
+			return;
+		gas_serv_req_dpp_processing(hapd, sa, dialog_token, prot, msg,
+					    freq);
+		return;
+	}
+#endif /* CONFIG_DPP */
+
+	/* ANQP Query Request */
+	while (pos < end) {
+		u16 info_id, elen;
+
+		if (end - pos < 4)
+			return;
+
+		info_id = WPA_GET_LE16(pos);
+		pos += 2;
+		elen = WPA_GET_LE16(pos);
+		pos += 2;
+
+		if (elen > end - pos) {
+			wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request");
+			return;
+		}
+
+		switch (info_id) {
+		case ANQP_QUERY_LIST:
+			rx_anqp_query_list(hapd, pos, pos + elen, &qi);
+			break;
+		case ANQP_VENDOR_SPECIFIC:
+			rx_anqp_vendor_specific(hapd, pos, pos + elen, &qi);
+			break;
+		default:
+			wpa_printf(MSG_DEBUG, "ANQP: Unsupported Query "
+				   "Request element %u", info_id);
+			break;
+		}
+
+		pos += elen;
+	}
+
+	gas_serv_req_local_processing(hapd, sa, dialog_token, &qi, prot,
+				      std_addr3);
+}
+
+
+static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
+					 const u8 *sa,
+					 const u8 *data, size_t len, int prot,
+					 int std_addr3)
+{
+	struct gas_dialog_info *dialog;
+	struct wpabuf *buf, *tx_buf;
+	u8 dialog_token;
+	size_t frag_len;
+	int more = 0;
+
+	wpa_hexdump(MSG_DEBUG, "GAS: RX GAS Comeback Request", data, len);
+	if (len < 1)
+		return;
+	dialog_token = *data;
+	wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Dialog Token: %u",
+		dialog_token);
+
+	dialog = gas_serv_dialog_find(hapd, sa, dialog_token);
+	if (!dialog) {
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: No pending SD "
+			"response fragment for " MACSTR " dialog token %u",
+			MAC2STR(sa), dialog_token);
+
+		if (sa[0] & 0x01)
+			return; /* Invalid source address - drop silently */
+		tx_buf = gas_anqp_build_comeback_resp_buf(
+			dialog_token, WLAN_STATUS_NO_OUTSTANDING_GAS_REQ, 0, 0,
+			0, NULL);
+		if (tx_buf == NULL)
+			return;
+		goto send_resp;
+	}
+
+	frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
+	if (frag_len > hapd->conf->gas_frag_limit) {
+		frag_len = hapd->conf->gas_frag_limit;
+		more = 1;
+	}
+	wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: resp frag_len %u",
+		(unsigned int) frag_len);
+	buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) +
+				dialog->sd_resp_pos, frag_len);
+	if (buf == NULL) {
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Failed to allocate "
+			"buffer");
+		gas_serv_dialog_clear(dialog);
+		return;
+	}
+#ifdef CONFIG_DPP
+	if (dialog->dpp) {
+		tx_buf = gas_build_comeback_resp(dialog_token,
+						 WLAN_STATUS_SUCCESS,
+						 dialog->sd_frag_id, more, 0,
+						 10 + 2 + frag_len);
+		if (tx_buf) {
+			gas_serv_write_dpp_adv_proto(tx_buf);
+			wpabuf_put_le16(tx_buf, frag_len);
+			wpabuf_put_buf(tx_buf, buf);
+		}
+	} else
+#endif /* CONFIG_DPP */
+	tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token,
+						  WLAN_STATUS_SUCCESS,
+						  dialog->sd_frag_id,
+						  more, 0, buf);
+	wpabuf_free(buf);
+	if (tx_buf == NULL) {
+		gas_serv_dialog_clear(dialog);
+		return;
+	}
+	wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Comeback Response "
+		"(frag_id %d more=%d frag_len=%d)",
+		dialog->sd_frag_id, more, (int) frag_len);
+	dialog->sd_frag_id++;
+	dialog->sd_resp_pos += frag_len;
+
+	if (more) {
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: %d more bytes remain "
+			"to be sent",
+			(int) (wpabuf_len(dialog->sd_resp) -
+			       dialog->sd_resp_pos));
+	} else {
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of "
+			"SD response sent");
+#ifdef CONFIG_DPP
+		if (dialog->dpp)
+			hostapd_dpp_gas_status_handler(hapd, 1);
+#endif /* CONFIG_DPP */
+		gas_serv_dialog_clear(dialog);
+		gas_serv_free_dialogs(hapd, sa);
+	}
+
+send_resp:
+	if (prot)
+		convert_to_protected_dual(tx_buf);
+	if (std_addr3)
+		hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
+					wpabuf_head(tx_buf),
+					wpabuf_len(tx_buf));
+	else
+		hostapd_drv_send_action_addr3_ap(hapd, hapd->iface->freq, 0, sa,
+						 wpabuf_head(tx_buf),
+						 wpabuf_len(tx_buf));
+	wpabuf_free(tx_buf);
+}
+
+
+static void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len,
+				      int freq)
+{
+	struct hostapd_data *hapd = ctx;
+	const struct ieee80211_mgmt *mgmt;
+	const u8 *sa, *data;
+	int prot, std_addr3;
+
+	mgmt = (const struct ieee80211_mgmt *) buf;
+	if (len < IEEE80211_HDRLEN + 2)
+		return;
+	if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
+	    mgmt->u.action.category != WLAN_ACTION_PROTECTED_DUAL)
+		return;
+	/*
+	 * Note: Public Action and Protected Dual of Public Action frames share
+	 * the same payload structure, so it is fine to use definitions of
+	 * Public Action frames to process both.
+	 */
+	prot = mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL;
+	sa = mgmt->sa;
+	if (hapd->conf->gas_address3 == 1)
+		std_addr3 = 1;
+	else if (hapd->conf->gas_address3 == 2)
+		std_addr3 = 0;
+	else
+		std_addr3 = is_broadcast_ether_addr(mgmt->bssid);
+	len -= IEEE80211_HDRLEN + 1;
+	data = buf + IEEE80211_HDRLEN + 1;
+	switch (data[0]) {
+	case WLAN_PA_GAS_INITIAL_REQ:
+		gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot,
+					    std_addr3, freq);
+		break;
+	case WLAN_PA_GAS_COMEBACK_REQ:
+		gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot,
+					     std_addr3);
+		break;
+	}
+}
+
+
+int gas_serv_init(struct hostapd_data *hapd)
+{
+	hapd->public_action_cb2 = gas_serv_rx_public_action;
+	hapd->public_action_cb2_ctx = hapd;
+	return 0;
+}
+
+
+void gas_serv_deinit(struct hostapd_data *hapd)
+{
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/gas_serv.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/gas_serv.h
new file mode 100644
index 0000000..7646a98
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/gas_serv.h
@@ -0,0 +1,95 @@
+/*
+ * Generic advertisement service (GAS) server
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef GAS_SERV_H
+#define GAS_SERV_H
+
+/* First 16 ANQP InfoIDs can be included in the optimized bitmap */
+#define ANQP_REQ_CAPABILITY_LIST \
+	(1 << (ANQP_CAPABILITY_LIST - ANQP_QUERY_LIST))
+#define ANQP_REQ_VENUE_NAME \
+	(1 << (ANQP_VENUE_NAME - ANQP_QUERY_LIST))
+#define ANQP_REQ_EMERGENCY_CALL_NUMBER \
+	(1 << (ANQP_EMERGENCY_CALL_NUMBER - ANQP_QUERY_LIST))
+#define ANQP_REQ_NETWORK_AUTH_TYPE \
+	(1 << (ANQP_NETWORK_AUTH_TYPE - ANQP_QUERY_LIST))
+#define ANQP_REQ_ROAMING_CONSORTIUM \
+	(1 << (ANQP_ROAMING_CONSORTIUM - ANQP_QUERY_LIST))
+#define ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY \
+	(1 << (ANQP_IP_ADDR_TYPE_AVAILABILITY - ANQP_QUERY_LIST))
+#define ANQP_REQ_NAI_REALM \
+	(1 << (ANQP_NAI_REALM - ANQP_QUERY_LIST))
+#define ANQP_REQ_3GPP_CELLULAR_NETWORK \
+	(1 << (ANQP_3GPP_CELLULAR_NETWORK - ANQP_QUERY_LIST))
+#define ANQP_REQ_AP_GEOSPATIAL_LOCATION \
+	(1 << (ANQP_AP_GEOSPATIAL_LOCATION - ANQP_QUERY_LIST))
+#define ANQP_REQ_AP_CIVIC_LOCATION \
+	(1 << (ANQP_AP_CIVIC_LOCATION - ANQP_QUERY_LIST))
+#define ANQP_REQ_AP_LOCATION_PUBLIC_URI \
+	(1 << (ANQP_AP_LOCATION_PUBLIC_URI - ANQP_QUERY_LIST))
+#define ANQP_REQ_DOMAIN_NAME \
+	(1 << (ANQP_DOMAIN_NAME - ANQP_QUERY_LIST))
+#define ANQP_REQ_EMERGENCY_ALERT_URI \
+	(1 << (ANQP_EMERGENCY_ALERT_URI - ANQP_QUERY_LIST))
+#define ANQP_REQ_TDLS_CAPABILITY \
+	(1 << (ANQP_TDLS_CAPABILITY - ANQP_QUERY_LIST))
+#define ANQP_REQ_EMERGENCY_NAI \
+	(1 << (ANQP_EMERGENCY_NAI - ANQP_QUERY_LIST))
+/*
+ * First 15 Hotspot 2.0 vendor specific ANQP-elements can be included in the
+ * optimized bitmap.
+ */
+#define ANQP_REQ_HS_CAPABILITY_LIST \
+	(0x10000 << HS20_STYPE_CAPABILITY_LIST)
+#define ANQP_REQ_OPERATOR_FRIENDLY_NAME \
+	(0x10000 << HS20_STYPE_OPERATOR_FRIENDLY_NAME)
+#define ANQP_REQ_WAN_METRICS \
+	(0x10000 << HS20_STYPE_WAN_METRICS)
+#define ANQP_REQ_CONNECTION_CAPABILITY \
+	(0x10000 << HS20_STYPE_CONNECTION_CAPABILITY)
+#define ANQP_REQ_NAI_HOME_REALM \
+	(0x10000 << HS20_STYPE_NAI_HOME_REALM_QUERY)
+#define ANQP_REQ_OPERATING_CLASS \
+	(0x10000 << HS20_STYPE_OPERATING_CLASS)
+#define ANQP_REQ_OSU_PROVIDERS_LIST \
+	(0x10000 << HS20_STYPE_OSU_PROVIDERS_LIST)
+#define ANQP_REQ_ICON_REQUEST \
+	(0x10000 << HS20_STYPE_ICON_REQUEST)
+#define ANQP_REQ_OPERATOR_ICON_METADATA \
+	(0x10000 << HS20_STYPE_OPERATOR_ICON_METADATA)
+#define ANQP_REQ_OSU_PROVIDERS_NAI_LIST \
+	(0x10000 << HS20_STYPE_OSU_PROVIDERS_NAI_LIST)
+/* The first MBO ANQP-element can be included in the optimized bitmap. */
+#define ANQP_REQ_MBO_CELL_DATA_CONN_PREF \
+	(BIT(29) << MBO_ANQP_SUBTYPE_CELL_CONN_PREF)
+
+struct gas_dialog_info {
+	u8 valid;
+	struct wpabuf *sd_resp; /* Fragmented response */
+	u8 dialog_token;
+	size_t sd_resp_pos; /* Offset in sd_resp */
+	u8 sd_frag_id;
+	int prot; /* whether Protected Dual of Public Action frame is used */
+	int dpp; /* whether this is a DPP Config Response */
+};
+
+struct hostapd_data;
+
+struct gas_dialog_info *
+gas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr,
+		     u8 dialog_token);
+void gas_serv_dialog_clear(struct gas_dialog_info *dialog);
+
+int gas_serv_init(struct hostapd_data *hapd);
+void gas_serv_deinit(struct hostapd_data *hapd);
+
+void gas_serv_req_dpp_processing(struct hostapd_data *hapd,
+				 const u8 *sa, u8 dialog_token,
+				 int prot, struct wpabuf *buf, int freq);
+
+#endif /* GAS_SERV_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/hostapd.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/hostapd.c
new file mode 100644
index 0000000..5316203
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/hostapd.c
@@ -0,0 +1,4460 @@
+/*
+ * hostapd / Initialization and configuration
+ * Copyright (c) 2002-2021, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#ifdef CONFIG_SQLITE
+#include <sqlite3.h>
+#endif /* CONFIG_SQLITE */
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/crc32.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
+#include "common/hw_features_common.h"
+#include "radius/radius_client.h"
+#include "radius/radius_das.h"
+#include "eap_server/tncs.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
+#include "fst/fst.h"
+#include "hostapd.h"
+#include "authsrv.h"
+#include "sta_info.h"
+#include "accounting.h"
+#include "ap_list.h"
+#include "beacon.h"
+#include "ieee802_1x.h"
+#include "ieee802_11_auth.h"
+#include "vlan_init.h"
+#include "wpa_auth.h"
+#include "wps_hostapd.h"
+#include "dpp_hostapd.h"
+#include "gas_query_ap.h"
+#include "hw_features.h"
+#include "wpa_auth_glue.h"
+#include "ap_drv_ops.h"
+#include "ap_config.h"
+#include "p2p_hostapd.h"
+#include "gas_serv.h"
+#include "dfs.h"
+#include "ieee802_11.h"
+#include "bss_load.h"
+#include "x_snoop.h"
+#include "dhcp_snoop.h"
+#include "ndisc_snoop.h"
+#include "neighbor_db.h"
+#include "rrm.h"
+#include "fils_hlp.h"
+#include "acs.h"
+#include "hs20.h"
+#include "airtime_policy.h"
+#include "wpa_auth_kay.h"
+#include "hw_features.h"
+
+
+static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason);
+#ifdef CONFIG_WEP
+static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd);
+static int hostapd_broadcast_wep_clear(struct hostapd_data *hapd);
+#endif /* CONFIG_WEP */
+static int setup_interface2(struct hostapd_iface *iface);
+static void channel_list_update_timeout(void *eloop_ctx, void *timeout_ctx);
+static void hostapd_interface_setup_failure_handler(void *eloop_ctx,
+						    void *timeout_ctx);
+#ifdef CONFIG_IEEE80211AX
+static void hostapd_switch_color_timeout_handler(void *eloop_data,
+						 void *user_ctx);
+#endif /* CONFIG_IEEE80211AX */
+
+
+int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
+			       int (*cb)(struct hostapd_iface *iface,
+					 void *ctx), void *ctx)
+{
+	size_t i;
+	int ret;
+
+	for (i = 0; i < interfaces->count; i++) {
+		if (!interfaces->iface[i])
+			continue;
+		ret = cb(interfaces->iface[i], ctx);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+
+struct hostapd_data * hostapd_mbssid_get_tx_bss(struct hostapd_data *hapd)
+{
+	if (hapd->iconf->mbssid)
+		return hapd->iface->bss[0];
+
+	return hapd;
+}
+
+
+int hostapd_mbssid_get_bss_index(struct hostapd_data *hapd)
+{
+	if (hapd->iconf->mbssid) {
+		size_t i;
+
+		for (i = 1; i < hapd->iface->num_bss; i++)
+			if (hapd->iface->bss[i] == hapd)
+				return i;
+	}
+
+	return 0;
+}
+
+
+void hostapd_reconfig_encryption(struct hostapd_data *hapd)
+{
+	if (hapd->wpa_auth)
+		return;
+
+	hostapd_set_privacy(hapd, 0);
+#ifdef CONFIG_WEP
+	hostapd_setup_encryption(hapd->conf->iface, hapd);
+#endif /* CONFIG_WEP */
+}
+
+
+static void hostapd_reload_bss(struct hostapd_data *hapd)
+{
+	struct hostapd_ssid *ssid;
+
+	if (!hapd->started)
+		return;
+
+	if (hapd->conf->wmm_enabled < 0)
+		hapd->conf->wmm_enabled = hapd->iconf->ieee80211n |
+			hapd->iconf->ieee80211ax;
+
+#ifndef CONFIG_NO_RADIUS
+	radius_client_reconfig(hapd->radius, hapd->conf->radius);
+#endif /* CONFIG_NO_RADIUS */
+
+	ssid = &hapd->conf->ssid;
+
+	hostapd_set_freq(hapd, hapd->iconf->hw_mode, hapd->iface->freq,
+			 hapd->iconf->channel,
+			 hapd->iconf->enable_edmg,
+			 hapd->iconf->edmg_channel,
+			 hapd->iconf->ieee80211n,
+			 hapd->iconf->ieee80211ac,
+			 hapd->iconf->ieee80211ax,
+			 hapd->iconf->ieee80211be,
+			 hapd->iconf->secondary_channel,
+			 hostapd_get_oper_chwidth(hapd->iconf),
+			 hostapd_get_oper_centr_freq_seg0_idx(hapd->iconf),
+			 hostapd_get_oper_centr_freq_seg1_idx(hapd->iconf));
+
+	if (hapd->iface->current_mode) {
+		if (hostapd_prepare_rates(hapd->iface, hapd->iface->current_mode)) {
+			wpa_printf(MSG_ERROR, "Failed to prepare rates table.");
+			hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_WARNING,
+				       "Failed to prepare rates table.");
+		}
+	}
+
+	if (!ssid->wpa_psk_set && ssid->wpa_psk && !ssid->wpa_psk->next &&
+	    ssid->wpa_passphrase_set && ssid->wpa_passphrase) {
+		/*
+		 * Force PSK to be derived again since SSID or passphrase may
+		 * have changed.
+		 */
+		hostapd_config_clear_wpa_psk(&hapd->conf->ssid.wpa_psk);
+	}
+	if (hostapd_setup_wpa_psk(hapd->conf)) {
+		wpa_printf(MSG_ERROR, "Failed to re-configure WPA PSK "
+			   "after reloading configuration");
+	}
+
+	if (hapd->conf->ieee802_1x || hapd->conf->wpa)
+		hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 1);
+	else
+		hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0);
+
+	if ((hapd->conf->wpa || hapd->conf->osen) && hapd->wpa_auth == NULL) {
+		hostapd_setup_wpa(hapd);
+		if (hapd->wpa_auth)
+			wpa_init_keys(hapd->wpa_auth);
+	} else if (hapd->conf->wpa) {
+		const u8 *wpa_ie;
+		size_t wpa_ie_len;
+		hostapd_reconfig_wpa(hapd);
+		wpa_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &wpa_ie_len);
+		if (hostapd_set_generic_elem(hapd, wpa_ie, wpa_ie_len))
+			wpa_printf(MSG_ERROR, "Failed to configure WPA IE for "
+				   "the kernel driver.");
+	} else if (hapd->wpa_auth) {
+		wpa_deinit(hapd->wpa_auth);
+		hapd->wpa_auth = NULL;
+		hostapd_set_privacy(hapd, 0);
+#ifdef CONFIG_WEP
+		hostapd_setup_encryption(hapd->conf->iface, hapd);
+#endif /* CONFIG_WEP */
+		hostapd_set_generic_elem(hapd, (u8 *) "", 0);
+	}
+
+	ieee802_11_set_beacon(hapd);
+	hostapd_update_wps(hapd);
+
+	if (hapd->conf->ssid.ssid_set &&
+	    hostapd_set_ssid(hapd, hapd->conf->ssid.ssid,
+			     hapd->conf->ssid.ssid_len)) {
+		wpa_printf(MSG_ERROR, "Could not set SSID for kernel driver");
+		/* try to continue */
+	}
+	wpa_printf(MSG_DEBUG, "Reconfigured interface %s", hapd->conf->iface);
+}
+
+
+static void hostapd_clear_old_bss(struct hostapd_data *bss)
+{
+	wpa_printf(MSG_DEBUG, "BSS %s changed - clear old state",
+		   bss->conf->iface);
+
+	/*
+	 * Deauthenticate all stations since the new configuration may not
+	 * allow them to use the BSS anymore.
+	 */
+	hostapd_flush_old_stations(bss, WLAN_REASON_PREV_AUTH_NOT_VALID);
+#ifdef CONFIG_WEP
+	hostapd_broadcast_wep_clear(bss);
+#endif /* CONFIG_WEP */
+
+#ifndef CONFIG_NO_RADIUS
+	/* TODO: update dynamic data based on changed configuration
+	 * items (e.g., open/close sockets, etc.) */
+	radius_client_flush(bss->radius, 0);
+#endif /* CONFIG_NO_RADIUS */
+}
+
+
+static void hostapd_clear_old(struct hostapd_iface *iface)
+{
+	size_t j;
+
+	for (j = 0; j < iface->num_bss; j++)
+		hostapd_clear_old_bss(iface->bss[j]);
+}
+
+
+static int hostapd_iface_conf_changed(struct hostapd_config *newconf,
+				      struct hostapd_config *oldconf)
+{
+	size_t i;
+
+	if (newconf->config_id != oldconf->config_id)
+		if (strcmp(newconf->config_id, oldconf->config_id))
+			return 1;
+
+	if (newconf->num_bss != oldconf->num_bss)
+		return 1;
+
+	for (i = 0; i < newconf->num_bss; i++) {
+		if (os_strcmp(newconf->bss[i]->iface,
+			      oldconf->bss[i]->iface) != 0)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static inline int hostapd_iface_num_sta(struct hostapd_iface *iface)
+{
+	int num_sta = 0;
+	int i;
+
+	for (i = 0; i < iface->num_bss; i++)
+		num_sta += iface->bss[i]->num_sta;
+
+	return num_sta;
+}
+
+
+int hostapd_check_max_sta(struct hostapd_data *hapd)
+{
+	if (hapd->num_sta >= hapd->conf->max_num_sta)
+		return 1;
+
+	if (hapd->iconf->max_num_sta &&
+	    hostapd_iface_num_sta(hapd->iface) >= hapd->iconf->max_num_sta)
+		return 1;
+
+	return 0;
+}
+
+static void hostapd_iface_reload_acs(struct hostapd_config *newconf, struct hostapd_config *oldconf)
+{
+	newconf->channel = oldconf->channel;
+	newconf->secondary_channel = oldconf->secondary_channel;
+
+#ifdef CONFIG_IEEE80211BE
+	if (newconf->ieee80211be && oldconf->ieee80211be)
+	{
+		newconf->eht_oper_chwidth = oldconf->eht_oper_chwidth;
+		newconf->eht_oper_centr_freq_seg0_idx = oldconf->eht_oper_centr_freq_seg0_idx;
+	}
+#endif /* CONFIG_IEEE80211BE */
+
+#ifdef CONFIG_IEEE80211AX
+	if (newconf->ieee80211ax && oldconf->ieee80211ax)
+	{
+		newconf->he_oper_chwidth = oldconf->he_oper_chwidth;
+		newconf->he_oper_centr_freq_seg0_idx = oldconf->he_oper_centr_freq_seg0_idx;
+		newconf->he_oper_centr_freq_seg1_idx = oldconf->he_oper_centr_freq_seg1_idx;
+	}
+#endif /* CONFIG_IEEE80211AX */
+
+	newconf->vht_oper_chwidth = oldconf->vht_oper_chwidth;
+	newconf->vht_oper_centr_freq_seg0_idx = oldconf->vht_oper_centr_freq_seg0_idx;
+	newconf->vht_oper_centr_freq_seg1_idx = oldconf->vht_oper_centr_freq_seg1_idx;
+	return;
+}
+
+int hostapd_reload_config(struct hostapd_iface *iface, int reconf)
+{
+	struct hapd_interfaces *interfaces = iface->interfaces;
+	struct hostapd_data *hapd = iface->bss[0];
+	struct hostapd_config *newconf, *oldconf;
+	size_t j;
+	int i;
+
+	if (iface->config_fname == NULL) {
+		/* Only in-memory config in use - assume it has been updated */
+		hostapd_clear_old(iface);
+		for (j = 0; j < iface->num_bss; j++)
+			hostapd_reload_bss(iface->bss[j]);
+		return 0;
+	}
+
+	if (iface->interfaces == NULL ||
+	    iface->interfaces->config_read_cb == NULL)
+		return -1;
+	newconf = iface->interfaces->config_read_cb(iface->config_fname);
+	if (newconf == NULL)
+		return -1;
+	wpa_printf(MSG_INFO, "hostapd_reload_config, newconf, channel:%d, secondary_channel:%d, bw: %d, centr_freq_seg0_idx:%d, centr_freq_seg1_idx:%d",
+						newconf->channel, newconf->secondary_channel,
+						hostapd_get_oper_chwidth(newconf),
+						hostapd_get_oper_centr_freq_seg0_idx(newconf),
+						hostapd_get_oper_centr_freq_seg1_idx(newconf));
+
+	oldconf = hapd->iconf;
+	wpa_printf(MSG_INFO, "hostapd_reload_config, oldconf, channel:%d, secondary_channel:%d, bw: %d, centr_freq_seg0_idx:%d, centr_freq_seg1_idx:%d",
+						oldconf->channel, oldconf->secondary_channel,
+						hostapd_get_oper_chwidth(oldconf),
+						hostapd_get_oper_centr_freq_seg0_idx(oldconf),
+						hostapd_get_oper_centr_freq_seg1_idx(oldconf));
+
+	if (hostapd_iface_conf_changed(newconf, oldconf)) {
+		char *fname;
+		int res;
+
+		if (reconf)
+			return -1;
+
+		hostapd_clear_old(iface);
+
+		wpa_printf(MSG_DEBUG,
+			   "Configuration changes include interface/BSS modification - force full disable+enable sequence");
+		fname = os_strdup(iface->config_fname);
+		if (!fname) {
+			hostapd_config_free(newconf);
+			return -1;
+		}
+		hostapd_remove_iface(interfaces, hapd->conf->iface);
+		iface = hostapd_init(interfaces, fname);
+		os_free(fname);
+		hostapd_config_free(newconf);
+		if (!iface) {
+			wpa_printf(MSG_ERROR,
+				   "Failed to initialize interface on config reload");
+			return -1;
+		}
+		iface->interfaces = interfaces;
+		interfaces->iface[interfaces->count] = iface;
+		interfaces->count++;
+		res = hostapd_enable_iface(iface);
+		if (res < 0)
+			wpa_printf(MSG_ERROR,
+				   "Failed to enable interface on config reload");
+		return res;
+	} else {
+		for (j = 0; j < iface->num_bss; j++) {
+			hapd = iface->bss[j];
+			if (!hapd->config_id || strcmp(hapd->config_id, newconf->bss[j]->config_id)) {
+				hostapd_flush_old_stations(iface->bss[j],
+							   WLAN_REASON_PREV_AUTH_NOT_VALID);
+#ifdef CONFIG_WEP
+				hostapd_broadcast_wep_clear(iface->bss[j]);
+#endif
+
+#ifndef CONFIG_NO_RADIUS
+				/* TODO: update dynamic data based on changed configuration
+				 * items (e.g., open/close sockets, etc.) */
+				radius_client_flush(iface->bss[j]->radius, 0);
+#endif /* CONFIG_NO_RADIUS */
+				wpa_printf(MSG_INFO, "bss %zu changed", j);
+			}
+		}
+	}
+
+	// Avoid channel=0 when reload config using ACS
+	if ((newconf->channel == 0) && (newconf->channel != oldconf->channel))
+	{
+		hostapd_iface_reload_acs(newconf, oldconf);
+	}
+	iface->conf = newconf;
+	wpa_printf(MSG_INFO, "hostapd_reload_config, iface->conf, channel:%d, secondary_channel:%d, bw: %d, centr_freq_seg0_idx:%d, centr_freq_seg1_idx:%d",
+						iface->conf->channel, iface->conf->secondary_channel,
+						hostapd_get_oper_chwidth(iface->conf),
+						hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
+						hostapd_get_oper_centr_freq_seg1_idx(iface->conf));
+
+	for (i = 0; i < iface->num_hw_features; i++) {
+		struct hostapd_hw_modes *mode = &iface->hw_features[i];
+		if (mode->mode == iface->conf->hw_mode) {
+			iface->current_mode = mode;
+			break;
+		}
+	}
+
+	if (iface->conf->channel)
+		iface->freq = hostapd_hw_get_freq(hapd, iface->conf->channel);
+
+	for (j = 0; j < iface->num_bss; j++) {
+		hapd = iface->bss[j];
+		if (hapd->config_id) {
+			os_free(hapd->config_id);
+			hapd->config_id = NULL;
+		}
+		if (newconf->bss[j]->config_id)
+			hapd->config_id = strdup(newconf->bss[j]->config_id);
+		if (!hapd->conf->config_id || !newconf->bss[j]->config_id ||
+		    os_strcmp(hapd->conf->config_id,
+			      newconf->bss[j]->config_id) != 0)
+			hostapd_clear_old_bss(hapd);
+		hapd->iconf = newconf;
+		hapd->conf = newconf->bss[j];
+		hostapd_reload_bss(hapd);
+	}
+
+	hostapd_config_free(oldconf);
+
+
+	return 0;
+}
+
+
+#ifdef CONFIG_WEP
+
+static void hostapd_broadcast_key_clear_iface(struct hostapd_data *hapd,
+					      const char *ifname)
+{
+	int i;
+
+	if (!ifname || !hapd->drv_priv)
+		return;
+	for (i = 0; i < NUM_WEP_KEYS; i++) {
+		if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE, NULL, i, 0,
+					0, NULL, 0, NULL, 0, KEY_FLAG_GROUP)) {
+			wpa_printf(MSG_DEBUG, "Failed to clear default "
+				   "encryption keys (ifname=%s keyidx=%d)",
+				   ifname, i);
+		}
+	}
+	if (hapd->conf->ieee80211w) {
+		for (i = NUM_WEP_KEYS; i < NUM_WEP_KEYS + 2; i++) {
+			if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE,
+						NULL, i, 0, 0, NULL,
+						0, NULL, 0, KEY_FLAG_GROUP)) {
+				wpa_printf(MSG_DEBUG, "Failed to clear "
+					   "default mgmt encryption keys "
+					   "(ifname=%s keyidx=%d)", ifname, i);
+			}
+		}
+	}
+}
+
+
+static int hostapd_broadcast_wep_clear(struct hostapd_data *hapd)
+{
+	hostapd_broadcast_key_clear_iface(hapd, hapd->conf->iface);
+	return 0;
+}
+
+
+static int hostapd_broadcast_wep_set(struct hostapd_data *hapd)
+{
+	int errors = 0, idx;
+	struct hostapd_ssid *ssid = &hapd->conf->ssid;
+
+	idx = ssid->wep.idx;
+	if (ssid->wep.default_len && ssid->wep.key[idx] &&
+	    hostapd_drv_set_key(hapd->conf->iface,
+				hapd, WPA_ALG_WEP, broadcast_ether_addr, idx, 0,
+				1, NULL, 0, ssid->wep.key[idx],
+				ssid->wep.len[idx],
+				KEY_FLAG_GROUP_RX_TX_DEFAULT)) {
+		wpa_printf(MSG_WARNING, "Could not set WEP encryption.");
+		errors++;
+	}
+
+	return errors;
+}
+
+#endif /* CONFIG_WEP */
+
+
+static void hostapd_clear_drv_priv(struct hostapd_data *hapd)
+{
+	unsigned int i;
+
+	for (i = 0; i < hapd->iface->interfaces->count; i++) {
+		struct hostapd_iface *iface = hapd->iface->interfaces->iface[i];
+
+		if (hapd->iface == iface)
+			continue;
+
+		if (iface && iface->bss && iface->bss[0] && iface->bss[0]->mld_first_bss && /* Avoid null pointer by Liangyu Chu*/
+		    iface->bss[0]->mld_first_bss == hapd)
+			iface->bss[0]->drv_priv = NULL;
+	}
+
+	hapd->drv_priv = NULL;
+}
+
+
+void hostapd_free_hapd_data(struct hostapd_data *hapd)
+{
+	os_free(hapd->probereq_cb);
+	hapd->probereq_cb = NULL;
+	hapd->num_probereq_cb = 0;
+
+#ifdef CONFIG_P2P
+	wpabuf_free(hapd->p2p_beacon_ie);
+	hapd->p2p_beacon_ie = NULL;
+	wpabuf_free(hapd->p2p_probe_resp_ie);
+	hapd->p2p_probe_resp_ie = NULL;
+#endif /* CONFIG_P2P */
+
+	if (!hapd->started) {
+		wpa_printf(MSG_ERROR, "%s: Interface %s wasn't started",
+			   __func__, hapd->conf ? hapd->conf->iface : "N/A");
+		return;
+	}
+	hapd->started = 0;
+	hapd->beacon_set_done = 0;
+
+	wpa_printf(MSG_DEBUG, "%s(%s)", __func__, hapd->conf->iface);
+	hostapd_ubus_free_bss(hapd);
+	accounting_deinit(hapd);
+	hostapd_deinit_wpa(hapd);
+	vlan_deinit(hapd);
+	hostapd_acl_deinit(hapd);
+#ifndef CONFIG_NO_RADIUS
+	if (!hapd->mld_first_bss) {
+		radius_client_deinit(hapd->radius);
+		radius_das_deinit(hapd->radius_das);
+	}
+	hapd->radius = NULL;
+	hapd->radius_das = NULL;
+#endif /* CONFIG_NO_RADIUS */
+
+	hostapd_deinit_wps(hapd);
+	ieee802_1x_dealloc_kay_sm_hapd(hapd);
+#ifdef CONFIG_DPP
+	hostapd_dpp_deinit(hapd);
+	gas_query_ap_deinit(hapd->gas);
+	hapd->gas = NULL;
+#endif /* CONFIG_DPP */
+
+	authsrv_deinit(hapd);
+
+	if (hapd->interface_added) {
+		hapd->interface_added = 0;
+		if (hostapd_if_remove(hapd, WPA_IF_AP_BSS, hapd->conf->iface)) {
+			wpa_printf(MSG_WARNING,
+				   "Failed to remove BSS interface %s",
+				   hapd->conf->iface);
+			hapd->interface_added = 1;
+		} else {
+			/*
+			 * Since this was a dynamically added interface, the
+			 * driver wrapper may have removed its internal instance
+			 * and hapd->drv_priv is not valid anymore.
+			 */
+			hostapd_clear_drv_priv(hapd);
+		}
+	}
+
+	wpabuf_free(hapd->time_adv);
+	hapd->time_adv = NULL;
+
+#ifdef CONFIG_INTERWORKING
+	gas_serv_deinit(hapd);
+#endif /* CONFIG_INTERWORKING */
+
+	bss_load_update_deinit(hapd);
+	ndisc_snoop_deinit(hapd);
+	dhcp_snoop_deinit(hapd);
+	x_snoop_deinit(hapd);
+
+#ifdef CONFIG_SQLITE
+	bin_clear_free(hapd->tmp_eap_user.identity,
+		       hapd->tmp_eap_user.identity_len);
+	bin_clear_free(hapd->tmp_eap_user.password,
+		       hapd->tmp_eap_user.password_len);
+	os_memset(&hapd->tmp_eap_user, 0, sizeof(hapd->tmp_eap_user));
+#endif /* CONFIG_SQLITE */
+
+#ifdef CONFIG_MESH
+	wpabuf_free(hapd->mesh_pending_auth);
+	hapd->mesh_pending_auth = NULL;
+	/* handling setup failure is already done */
+	hapd->setup_complete_cb = NULL;
+#endif /* CONFIG_MESH */
+
+	hostapd_clean_rrm(hapd);
+	fils_hlp_deinit(hapd);
+
+#ifdef CONFIG_OCV
+	eloop_cancel_timeout(hostapd_ocv_check_csa_sa_query, hapd, NULL);
+#endif /* CONFIG_OCV */
+
+#ifdef CONFIG_SAE
+	{
+		struct hostapd_sae_commit_queue *q;
+
+		while ((q = dl_list_first(&hapd->sae_commit_queue,
+					  struct hostapd_sae_commit_queue,
+					  list))) {
+			dl_list_del(&q->list);
+			os_free(q);
+		}
+	}
+	eloop_cancel_timeout(auth_sae_process_commit, hapd, NULL);
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_IEEE80211AX
+	eloop_cancel_timeout(hostapd_switch_color_timeout_handler, hapd, NULL);
+#endif /* CONFIG_IEEE80211AX */
+}
+
+
+/**
+ * hostapd_cleanup - Per-BSS cleanup (deinitialization)
+ * @hapd: Pointer to BSS data
+ *
+ * This function is used to free all per-BSS data structures and resources.
+ * Most of the modules that are initialized in hostapd_setup_bss() are
+ * deinitialized here.
+ */
+static void hostapd_cleanup(struct hostapd_data *hapd)
+{
+	wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s))", __func__, hapd,
+		   hapd->conf ? hapd->conf->iface : "N/A");
+	if (hapd->iface->interfaces &&
+	    hapd->iface->interfaces->ctrl_iface_deinit) {
+		wpa_msg(hapd->msg_ctx, MSG_INFO, WPA_EVENT_TERMINATING);
+		hapd->iface->interfaces->ctrl_iface_deinit(hapd);
+	}
+	hostapd_free_hapd_data(hapd);
+}
+
+
+static void sta_track_deinit(struct hostapd_iface *iface)
+{
+	struct hostapd_sta_info *info;
+
+	if (!iface->num_sta_seen)
+		return;
+
+	while ((info = dl_list_first(&iface->sta_seen, struct hostapd_sta_info,
+				     list))) {
+		dl_list_del(&info->list);
+		iface->num_sta_seen--;
+		sta_track_del(info);
+	}
+}
+
+
+void hostapd_cleanup_iface_partial(struct hostapd_iface *iface)
+{
+	wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
+#ifdef NEED_AP_MLME
+	hostapd_stop_setup_timers(iface);
+#endif /* NEED_AP_MLME */
+	if (iface->current_mode)
+		acs_cleanup(iface);
+	hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
+	iface->hw_features = NULL;
+	iface->current_mode = NULL;
+	os_free(iface->current_rates);
+	iface->current_rates = NULL;
+	os_free(iface->basic_rates);
+	iface->basic_rates = NULL;
+	iface->cac_started = 0;
+	ap_list_deinit(iface);
+	sta_track_deinit(iface);
+	airtime_policy_update_deinit(iface);
+}
+
+
+/**
+ * hostapd_cleanup_iface - Complete per-interface cleanup
+ * @iface: Pointer to interface data
+ *
+ * This function is called after per-BSS data structures are deinitialized
+ * with hostapd_cleanup().
+ */
+static void hostapd_cleanup_iface(struct hostapd_iface *iface)
+{
+	wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
+	eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
+	eloop_cancel_timeout(hostapd_interface_setup_failure_handler, iface,
+			     NULL);
+
+	hostapd_cleanup_iface_partial(iface);
+	hostapd_config_free(iface->conf);
+	iface->conf = NULL;
+
+	os_free(iface->config_fname);
+	os_free(iface->bss);
+	wpa_printf(MSG_DEBUG, "%s: free iface=%p", __func__, iface);
+	os_free(iface);
+}
+
+
+#ifdef CONFIG_WEP
+
+static void hostapd_clear_wep(struct hostapd_data *hapd)
+{
+	if (hapd->drv_priv && !hapd->iface->driver_ap_teardown && hapd->conf) {
+		hostapd_set_privacy(hapd, 0);
+		hostapd_broadcast_wep_clear(hapd);
+	}
+}
+
+
+static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd)
+{
+	int i;
+
+	hostapd_broadcast_wep_set(hapd);
+
+	if (hapd->conf->ssid.wep.default_len) {
+		hostapd_set_privacy(hapd, 1);
+		return 0;
+	}
+
+	/*
+	 * When IEEE 802.1X is not enabled, the driver may need to know how to
+	 * set authentication algorithms for static WEP.
+	 */
+	hostapd_drv_set_authmode(hapd, hapd->conf->auth_algs);
+
+	for (i = 0; i < 4; i++) {
+		if (hapd->conf->ssid.wep.key[i] &&
+		    hostapd_drv_set_key(iface, hapd, WPA_ALG_WEP, NULL, i, 0,
+					i == hapd->conf->ssid.wep.idx, NULL, 0,
+					hapd->conf->ssid.wep.key[i],
+					hapd->conf->ssid.wep.len[i],
+					i == hapd->conf->ssid.wep.idx ?
+					KEY_FLAG_GROUP_RX_TX_DEFAULT :
+					KEY_FLAG_GROUP_RX_TX)) {
+			wpa_printf(MSG_WARNING, "Could not set WEP "
+				   "encryption.");
+			return -1;
+		}
+		if (hapd->conf->ssid.wep.key[i] &&
+		    i == hapd->conf->ssid.wep.idx)
+			hostapd_set_privacy(hapd, 1);
+	}
+
+	return 0;
+}
+
+#endif /* CONFIG_WEP */
+
+
+static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason)
+{
+	int ret = 0;
+	u8 addr[ETH_ALEN];
+
+	if (hostapd_drv_none(hapd) || hapd->drv_priv == NULL)
+		return 0;
+
+	if (!hapd->iface->driver_ap_teardown) {
+		wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
+			"Flushing old station entries");
+
+		if (hostapd_flush(hapd)) {
+			wpa_msg(hapd->msg_ctx, MSG_WARNING,
+				"Could not connect to kernel driver");
+			ret = -1;
+		}
+	}
+	if (hapd->conf && hapd->conf->broadcast_deauth) {
+		wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
+			"Deauthenticate all stations");
+		os_memset(addr, 0xff, ETH_ALEN);
+		hostapd_drv_sta_deauth(hapd, addr, reason);
+	}
+	hostapd_free_stas(hapd);
+
+	return ret;
+}
+
+
+void hostapd_bss_deinit_no_free(struct hostapd_data *hapd)
+{
+	hostapd_free_stas(hapd);
+	hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING);
+#ifdef CONFIG_WEP
+	hostapd_clear_wep(hapd);
+#endif /* CONFIG_WEP */
+}
+
+
+/**
+ * hostapd_validate_bssid_configuration - Validate BSSID configuration
+ * @iface: Pointer to interface data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to validate that the configured BSSIDs are valid.
+ */
+static int hostapd_validate_bssid_configuration(struct hostapd_iface *iface)
+{
+	u8 mask[ETH_ALEN] = { 0 };
+	struct hostapd_data *hapd = iface->bss[0];
+	unsigned int i = iface->conf->num_bss, bits = 0, j;
+	int auto_addr = 0;
+
+	if (hostapd_drv_none(hapd))
+		return 0;
+
+	if (iface->conf->use_driver_iface_addr)
+		return 0;
+
+	/* Generate BSSID mask that is large enough to cover the BSSIDs. */
+
+	/* Determine the bits necessary to cover the number of BSSIDs. */
+	for (i--; i; i >>= 1)
+		bits++;
+
+	/* Determine the bits necessary to any configured BSSIDs,
+	   if they are higher than the number of BSSIDs. */
+	for (j = 0; j < iface->conf->num_bss; j++) {
+		if (is_zero_ether_addr(iface->conf->bss[j]->bssid)) {
+			if (j)
+				auto_addr++;
+			continue;
+		}
+
+		for (i = 0; i < ETH_ALEN; i++) {
+			mask[i] |=
+				iface->conf->bss[j]->bssid[i] ^
+				hapd->own_addr[i];
+		}
+	}
+
+	if (!auto_addr)
+		goto skip_mask_ext;
+
+	for (i = 0; i < ETH_ALEN && mask[i] == 0; i++)
+		;
+	j = 0;
+	if (i < ETH_ALEN) {
+		j = (5 - i) * 8;
+
+		while (mask[i] != 0) {
+			mask[i] >>= 1;
+			j++;
+		}
+	}
+
+	if (bits < j)
+		bits = j;
+
+	if (bits > 40) {
+		wpa_printf(MSG_ERROR, "Too many bits in the BSSID mask (%u)",
+			   bits);
+		return -1;
+	}
+
+	os_memset(mask, 0xff, ETH_ALEN);
+	j = bits / 8;
+	for (i = 5; i > 5 - j; i--)
+		mask[i] = 0;
+	j = bits % 8;
+	while (j) {
+		j--;
+		mask[i] <<= 1;
+	}
+
+skip_mask_ext:
+	wpa_printf(MSG_DEBUG, "BSS count %lu, BSSID mask " MACSTR " (%d bits)",
+		   (unsigned long) iface->conf->num_bss, MAC2STR(mask), bits);
+
+	if (!auto_addr)
+		return 0;
+
+	for (i = 0; i < ETH_ALEN; i++) {
+		if ((hapd->own_addr[i] & mask[i]) != hapd->own_addr[i]) {
+			wpa_printf(MSG_ERROR, "Invalid BSSID mask " MACSTR
+				   " for start address " MACSTR ".",
+				   MAC2STR(mask), MAC2STR(hapd->own_addr));
+			wpa_printf(MSG_ERROR, "Start address must be the "
+				   "first address in the block (i.e., addr "
+				   "AND mask == addr).");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+static int mac_in_conf(struct hostapd_config *conf, const void *a)
+{
+	size_t i;
+
+	for (i = 0; i < conf->num_bss; i++) {
+		if (hostapd_mac_comp(conf->bss[i]->bssid, a) == 0) {
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+
+#ifndef CONFIG_NO_RADIUS
+
+static int hostapd_das_nas_mismatch(struct hostapd_data *hapd,
+				    struct radius_das_attrs *attr)
+{
+	if (attr->nas_identifier &&
+	    (!hapd->conf->nas_identifier ||
+	     os_strlen(hapd->conf->nas_identifier) !=
+	     attr->nas_identifier_len ||
+	     os_memcmp(hapd->conf->nas_identifier, attr->nas_identifier,
+		       attr->nas_identifier_len) != 0)) {
+		wpa_printf(MSG_DEBUG, "RADIUS DAS: NAS-Identifier mismatch");
+		return 1;
+	}
+
+	if (attr->nas_ip_addr &&
+	    (hapd->conf->own_ip_addr.af != AF_INET ||
+	     os_memcmp(&hapd->conf->own_ip_addr.u.v4, attr->nas_ip_addr, 4) !=
+	     0)) {
+		wpa_printf(MSG_DEBUG, "RADIUS DAS: NAS-IP-Address mismatch");
+		return 1;
+	}
+
+#ifdef CONFIG_IPV6
+	if (attr->nas_ipv6_addr &&
+	    (hapd->conf->own_ip_addr.af != AF_INET6 ||
+	     os_memcmp(&hapd->conf->own_ip_addr.u.v6, attr->nas_ipv6_addr, 16)
+	     != 0)) {
+		wpa_printf(MSG_DEBUG, "RADIUS DAS: NAS-IPv6-Address mismatch");
+		return 1;
+	}
+#endif /* CONFIG_IPV6 */
+
+	return 0;
+}
+
+
+static struct sta_info * hostapd_das_find_sta(struct hostapd_data *hapd,
+					      struct radius_das_attrs *attr,
+					      int *multi)
+{
+	struct sta_info *selected, *sta;
+	char buf[128];
+	int num_attr = 0;
+	int count;
+
+	*multi = 0;
+
+	for (sta = hapd->sta_list; sta; sta = sta->next)
+		sta->radius_das_match = 1;
+
+	if (attr->sta_addr) {
+		num_attr++;
+		sta = ap_get_sta(hapd, attr->sta_addr);
+		if (!sta) {
+			wpa_printf(MSG_DEBUG,
+				   "RADIUS DAS: No Calling-Station-Id match");
+			return NULL;
+		}
+
+		selected = sta;
+		for (sta = hapd->sta_list; sta; sta = sta->next) {
+			if (sta != selected)
+				sta->radius_das_match = 0;
+		}
+		wpa_printf(MSG_DEBUG, "RADIUS DAS: Calling-Station-Id match");
+	}
+
+	if (attr->acct_session_id) {
+		num_attr++;
+		if (attr->acct_session_id_len != 16) {
+			wpa_printf(MSG_DEBUG,
+				   "RADIUS DAS: Acct-Session-Id cannot match");
+			return NULL;
+		}
+		count = 0;
+
+		for (sta = hapd->sta_list; sta; sta = sta->next) {
+			if (!sta->radius_das_match)
+				continue;
+			os_snprintf(buf, sizeof(buf), "%016llX",
+				    (unsigned long long) sta->acct_session_id);
+			if (os_memcmp(attr->acct_session_id, buf, 16) != 0)
+				sta->radius_das_match = 0;
+			else
+				count++;
+		}
+
+		if (count == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "RADIUS DAS: No matches remaining after Acct-Session-Id check");
+			return NULL;
+		}
+		wpa_printf(MSG_DEBUG, "RADIUS DAS: Acct-Session-Id match");
+	}
+
+	if (attr->acct_multi_session_id) {
+		num_attr++;
+		if (attr->acct_multi_session_id_len != 16) {
+			wpa_printf(MSG_DEBUG,
+				   "RADIUS DAS: Acct-Multi-Session-Id cannot match");
+			return NULL;
+		}
+		count = 0;
+
+		for (sta = hapd->sta_list; sta; sta = sta->next) {
+			if (!sta->radius_das_match)
+				continue;
+			if (!sta->eapol_sm ||
+			    !sta->eapol_sm->acct_multi_session_id) {
+				sta->radius_das_match = 0;
+				continue;
+			}
+			os_snprintf(buf, sizeof(buf), "%016llX",
+				    (unsigned long long)
+				    sta->eapol_sm->acct_multi_session_id);
+			if (os_memcmp(attr->acct_multi_session_id, buf, 16) !=
+			    0)
+				sta->radius_das_match = 0;
+			else
+				count++;
+		}
+
+		if (count == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "RADIUS DAS: No matches remaining after Acct-Multi-Session-Id check");
+			return NULL;
+		}
+		wpa_printf(MSG_DEBUG,
+			   "RADIUS DAS: Acct-Multi-Session-Id match");
+	}
+
+	if (attr->cui) {
+		num_attr++;
+		count = 0;
+
+		for (sta = hapd->sta_list; sta; sta = sta->next) {
+			struct wpabuf *cui;
+
+			if (!sta->radius_das_match)
+				continue;
+			cui = ieee802_1x_get_radius_cui(sta->eapol_sm);
+			if (!cui || wpabuf_len(cui) != attr->cui_len ||
+			    os_memcmp(wpabuf_head(cui), attr->cui,
+				      attr->cui_len) != 0)
+				sta->radius_das_match = 0;
+			else
+				count++;
+		}
+
+		if (count == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "RADIUS DAS: No matches remaining after Chargeable-User-Identity check");
+			return NULL;
+		}
+		wpa_printf(MSG_DEBUG,
+			   "RADIUS DAS: Chargeable-User-Identity match");
+	}
+
+	if (attr->user_name) {
+		num_attr++;
+		count = 0;
+
+		for (sta = hapd->sta_list; sta; sta = sta->next) {
+			u8 *identity;
+			size_t identity_len;
+
+			if (!sta->radius_das_match)
+				continue;
+			identity = ieee802_1x_get_identity(sta->eapol_sm,
+							   &identity_len);
+			if (!identity ||
+			    identity_len != attr->user_name_len ||
+			    os_memcmp(identity, attr->user_name, identity_len)
+			    != 0)
+				sta->radius_das_match = 0;
+			else
+				count++;
+		}
+
+		if (count == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "RADIUS DAS: No matches remaining after User-Name check");
+			return NULL;
+		}
+		wpa_printf(MSG_DEBUG,
+			   "RADIUS DAS: User-Name match");
+	}
+
+	if (num_attr == 0) {
+		/*
+		 * In theory, we could match all current associations, but it
+		 * seems safer to just reject requests that do not include any
+		 * session identification attributes.
+		 */
+		wpa_printf(MSG_DEBUG,
+			   "RADIUS DAS: No session identification attributes included");
+		return NULL;
+	}
+
+	selected = NULL;
+	for (sta = hapd->sta_list; sta; sta = sta->next) {
+		if (sta->radius_das_match) {
+			if (selected) {
+				*multi = 1;
+				return NULL;
+			}
+			selected = sta;
+		}
+	}
+
+	return selected;
+}
+
+
+static int hostapd_das_disconnect_pmksa(struct hostapd_data *hapd,
+					struct radius_das_attrs *attr)
+{
+	if (!hapd->wpa_auth)
+		return -1;
+	return wpa_auth_radius_das_disconnect_pmksa(hapd->wpa_auth, attr);
+}
+
+
+static enum radius_das_res
+hostapd_das_disconnect(void *ctx, struct radius_das_attrs *attr)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+	int multi;
+
+	if (hostapd_das_nas_mismatch(hapd, attr))
+		return RADIUS_DAS_NAS_MISMATCH;
+
+	sta = hostapd_das_find_sta(hapd, attr, &multi);
+	if (sta == NULL) {
+		if (multi) {
+			wpa_printf(MSG_DEBUG,
+				   "RADIUS DAS: Multiple sessions match - not supported");
+			return RADIUS_DAS_MULTI_SESSION_MATCH;
+		}
+		if (hostapd_das_disconnect_pmksa(hapd, attr) == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "RADIUS DAS: PMKSA cache entry matched");
+			return RADIUS_DAS_SUCCESS;
+		}
+		wpa_printf(MSG_DEBUG, "RADIUS DAS: No matching session found");
+		return RADIUS_DAS_SESSION_NOT_FOUND;
+	}
+
+	wpa_printf(MSG_DEBUG, "RADIUS DAS: Found a matching session " MACSTR
+		   " - disconnecting", MAC2STR(sta->addr));
+	wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
+
+	hostapd_drv_sta_deauth(hapd, sta->addr,
+			       WLAN_REASON_PREV_AUTH_NOT_VALID);
+	ap_sta_deauthenticate(hapd, sta, WLAN_REASON_PREV_AUTH_NOT_VALID);
+
+	return RADIUS_DAS_SUCCESS;
+}
+
+
+#ifdef CONFIG_HS20
+static enum radius_das_res
+hostapd_das_coa(void *ctx, struct radius_das_attrs *attr)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+	int multi;
+
+	if (hostapd_das_nas_mismatch(hapd, attr))
+		return RADIUS_DAS_NAS_MISMATCH;
+
+	sta = hostapd_das_find_sta(hapd, attr, &multi);
+	if (!sta) {
+		if (multi) {
+			wpa_printf(MSG_DEBUG,
+				   "RADIUS DAS: Multiple sessions match - not supported");
+			return RADIUS_DAS_MULTI_SESSION_MATCH;
+		}
+		wpa_printf(MSG_DEBUG, "RADIUS DAS: No matching session found");
+		return RADIUS_DAS_SESSION_NOT_FOUND;
+	}
+
+	wpa_printf(MSG_DEBUG, "RADIUS DAS: Found a matching session " MACSTR
+		   " - CoA", MAC2STR(sta->addr));
+
+	if (attr->hs20_t_c_filtering) {
+		if (attr->hs20_t_c_filtering[0] & BIT(0)) {
+			wpa_printf(MSG_DEBUG,
+				   "HS 2.0: Unexpected Terms and Conditions filtering required in CoA-Request");
+			return RADIUS_DAS_COA_FAILED;
+		}
+
+		hs20_t_c_filtering(hapd, sta, 0);
+	}
+
+	return RADIUS_DAS_SUCCESS;
+}
+#else /* CONFIG_HS20 */
+#define hostapd_das_coa NULL
+#endif /* CONFIG_HS20 */
+
+
+#ifdef CONFIG_SQLITE
+
+static int db_table_exists(sqlite3 *db, const char *name)
+{
+	char cmd[128];
+
+	os_snprintf(cmd, sizeof(cmd), "SELECT 1 FROM %s;", name);
+	return sqlite3_exec(db, cmd, NULL, NULL, NULL) == SQLITE_OK;
+}
+
+
+static int db_table_create_radius_attributes(sqlite3 *db)
+{
+	char *err = NULL;
+	const char *sql =
+		"CREATE TABLE radius_attributes("
+		" id INTEGER PRIMARY KEY,"
+		" sta TEXT,"
+		" reqtype TEXT,"
+		" attr TEXT"
+		");"
+		"CREATE INDEX idx_sta_reqtype ON radius_attributes(sta,reqtype);";
+
+	wpa_printf(MSG_DEBUG,
+		   "Adding database table for RADIUS attribute information");
+	if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
+		wpa_printf(MSG_ERROR, "SQLite error: %s", err);
+		sqlite3_free(err);
+		return -1;
+	}
+
+	return 0;
+}
+
+#endif /* CONFIG_SQLITE */
+
+#endif /* CONFIG_NO_RADIUS */
+
+
+static int hostapd_start_beacon(struct hostapd_data *hapd,
+				bool flush_old_stations)
+{
+	struct hostapd_bss_config *conf = hapd->conf;
+
+	if (!conf->start_disabled && ieee802_11_set_beacon(hapd) < 0)
+		return -1;
+
+	if (flush_old_stations && !conf->start_disabled &&
+	    conf->broadcast_deauth) {
+		u8 addr[ETH_ALEN];
+
+		/* Should any previously associated STA not have noticed that
+		 * the AP had stopped and restarted, send one more
+		 * deauthentication notification now that the AP is ready to
+		 * operate. */
+		wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
+			"Deauthenticate all stations at BSS start");
+		os_memset(addr, 0xff, ETH_ALEN);
+		hostapd_drv_sta_deauth(hapd, addr,
+				       WLAN_REASON_PREV_AUTH_NOT_VALID);
+	}
+
+	if (hapd->driver && hapd->driver->set_operstate)
+		hapd->driver->set_operstate(hapd->drv_priv, 1);
+
+	hostapd_ubus_add_bss(hapd);
+
+	return 0;
+}
+
+
+/**
+ * hostapd_setup_bss - Per-BSS setup (initialization)
+ * @hapd: Pointer to BSS data
+ * @first: Whether this BSS is the first BSS of an interface; -1 = not first,
+ *	but interface may exist
+ * @start_beacon: Whether Beacon frame template should be configured and
+ *	transmission of Beaconf rames started at this time. This is used when
+ *	MBSSID element is enabled where the information regarding all BSSes
+ *	should be retrieved before configuring the Beacon frame template. The
+ *	calling functions are responsible for configuring the Beacon frame
+ *	explicitly if this is set to false.
+ *
+ * This function is used to initialize all per-BSS data structures and
+ * resources. This gets called in a loop for each BSS when an interface is
+ * initialized. Most of the modules that are initialized here will be
+ * deinitialized in hostapd_cleanup().
+ */
+static int hostapd_setup_bss(struct hostapd_data *hapd, int first,
+			     bool start_beacon)
+{
+	struct hostapd_bss_config *conf = hapd->conf;
+	u8 ssid[SSID_MAX_LEN + 1];
+	int ssid_len, set_ssid;
+	char force_ifname[IFNAMSIZ];
+	u8 if_addr[ETH_ALEN];
+	int flush_old_stations = 1;
+
+	if (hapd->mld_first_bss)
+		wpa_printf(MSG_DEBUG,
+			   "MLD: %s: Setting non-first BSS", __func__);
+
+	wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s), first=%d)",
+		   __func__, hapd, conf->iface, first);
+
+#ifdef EAP_SERVER_TNC
+	if (conf->tnc && tncs_global_init() < 0) {
+		wpa_printf(MSG_ERROR, "Failed to initialize TNCS");
+		return -1;
+	}
+#endif /* EAP_SERVER_TNC */
+
+	if (hapd->started) {
+		wpa_printf(MSG_ERROR, "%s: Interface %s was already started",
+			   __func__, conf->iface);
+		return -1;
+	}
+	hapd->started = 1;
+
+	if (!first || first == -1) {
+		u8 *addr = hapd->own_addr;
+
+		if (!is_zero_ether_addr(conf->bssid)) {
+			/* Allocate the configured BSSID. */
+			os_memcpy(hapd->own_addr, conf->bssid, ETH_ALEN);
+
+			if (hostapd_mac_comp(hapd->own_addr,
+					     hapd->iface->bss[0]->own_addr) ==
+			    0) {
+				wpa_printf(MSG_ERROR, "BSS '%s' may not have "
+					   "BSSID set to the MAC address of "
+					   "the radio", conf->iface);
+				return -1;
+			}
+		} else if (hapd->iconf->use_driver_iface_addr) {
+			addr = NULL;
+		} else {
+			/* Allocate the next available BSSID. */
+			do {
+				inc_byte_array(hapd->own_addr, ETH_ALEN);
+			} while (mac_in_conf(hapd->iconf, hapd->own_addr));
+		}
+
+		hapd->interface_added = 1;
+		if (hostapd_if_add(hapd->iface->bss[0], WPA_IF_AP_BSS,
+				   conf->iface, addr, hapd,
+				   &hapd->drv_priv, force_ifname, if_addr,
+				   conf->bridge[0] ? conf->bridge : NULL,
+				   first == -1)) {
+			wpa_printf(MSG_ERROR, "Failed to add BSS (BSSID="
+				   MACSTR ")", MAC2STR(hapd->own_addr));
+			hapd->interface_added = 0;
+			return -1;
+		}
+
+		if (!addr)
+			os_memcpy(hapd->own_addr, if_addr, ETH_ALEN);
+	}
+
+	if (conf->wmm_enabled < 0)
+		conf->wmm_enabled = hapd->iconf->ieee80211n |
+			hapd->iconf->ieee80211ax;
+
+#ifdef CONFIG_IEEE80211R_AP
+	if (is_zero_ether_addr(conf->r1_key_holder))
+		os_memcpy(conf->r1_key_holder, hapd->own_addr, ETH_ALEN);
+#endif /* CONFIG_IEEE80211R_AP */
+
+#ifdef CONFIG_MESH
+	if ((hapd->conf->mesh & MESH_ENABLED) && hapd->iface->mconf == NULL)
+		flush_old_stations = 0;
+#endif /* CONFIG_MESH */
+
+	if (flush_old_stations)
+		hostapd_flush(hapd);
+	hostapd_set_privacy(hapd, 0);
+
+#ifdef CONFIG_WEP
+	if (!hostapd_drv_nl80211(hapd))
+		hostapd_broadcast_wep_clear(hapd);
+	if (hostapd_setup_encryption(conf->iface, hapd))
+		return -1;
+#endif /* CONFIG_WEP */
+
+	/*
+	 * Fetch the SSID from the system and use it or,
+	 * if one was specified in the config file, verify they
+	 * match.
+	 */
+	ssid_len = hostapd_get_ssid(hapd, ssid, sizeof(ssid));
+	if (ssid_len < 0) {
+		wpa_printf(MSG_ERROR, "Could not read SSID from system");
+		return -1;
+	}
+	if (conf->ssid.ssid_set) {
+		/*
+		 * If SSID is specified in the config file and it differs
+		 * from what is being used then force installation of the
+		 * new SSID.
+		 */
+		set_ssid = (conf->ssid.ssid_len != (size_t) ssid_len ||
+			    os_memcmp(conf->ssid.ssid, ssid, ssid_len) != 0);
+	} else {
+		/*
+		 * No SSID in the config file; just use the one we got
+		 * from the system.
+		 */
+		set_ssid = 0;
+		conf->ssid.ssid_len = ssid_len;
+		os_memcpy(conf->ssid.ssid, ssid, conf->ssid.ssid_len);
+	}
+
+	/*
+	 * Short SSID calculation is identical to FCS and it is defined in
+	 * IEEE P802.11-REVmd/D3.0, 9.4.2.170.3 (Calculating the Short-SSID).
+	 */
+	conf->ssid.short_ssid = ieee80211_crc32(conf->ssid.ssid,
+						conf->ssid.ssid_len);
+
+	if (!hostapd_drv_none(hapd)) {
+		wpa_printf(MSG_DEBUG, "Using interface %s with hwaddr " MACSTR
+			   " and ssid \"%s\"",
+			   conf->iface, MAC2STR(hapd->own_addr),
+			   wpa_ssid_txt(conf->ssid.ssid, conf->ssid.ssid_len));
+	}
+
+	if (hostapd_setup_wpa_psk(conf)) {
+		wpa_printf(MSG_ERROR, "WPA-PSK setup failed.");
+		return -1;
+	}
+
+	/* Set SSID for the kernel driver (to be used in beacon and probe
+	 * response frames) */
+	if (set_ssid && hostapd_set_ssid(hapd, conf->ssid.ssid,
+					 conf->ssid.ssid_len)) {
+		wpa_printf(MSG_ERROR, "Could not set SSID for kernel driver");
+		return -1;
+	}
+
+	if (wpa_debug_level <= MSG_MSGDUMP)
+		conf->radius->msg_dumps = 1;
+#ifndef CONFIG_NO_RADIUS
+
+#ifdef CONFIG_SQLITE
+	if (conf->radius_req_attr_sqlite) {
+		if (sqlite3_open(conf->radius_req_attr_sqlite,
+				 &hapd->rad_attr_db)) {
+			wpa_printf(MSG_ERROR, "Could not open SQLite file '%s'",
+				   conf->radius_req_attr_sqlite);
+			return -1;
+		}
+
+		wpa_printf(MSG_DEBUG, "Opening RADIUS attribute database: %s",
+			   conf->radius_req_attr_sqlite);
+		if (!db_table_exists(hapd->rad_attr_db, "radius_attributes") &&
+		    db_table_create_radius_attributes(hapd->rad_attr_db) < 0)
+			return -1;
+	}
+#endif /* CONFIG_SQLITE */
+
+	if (!hapd->mld_first_bss) {
+		hapd->radius = radius_client_init(hapd, conf->radius);
+		if (!hapd->radius) {
+			wpa_printf(MSG_ERROR,
+				   "RADIUS client initialization failed.");
+			return -1;
+		}
+
+		if (conf->radius_das_port) {
+			struct radius_das_conf das_conf;
+
+			os_memset(&das_conf, 0, sizeof(das_conf));
+			das_conf.port = conf->radius_das_port;
+			das_conf.nas_identifier = conf->nas_identifier;
+			das_conf.shared_secret = conf->radius_das_shared_secret;
+			das_conf.shared_secret_len =
+				conf->radius_das_shared_secret_len;
+			das_conf.client_addr = &conf->radius_das_client_addr;
+			das_conf.time_window = conf->radius_das_time_window;
+			das_conf.require_event_timestamp =
+				conf->radius_das_require_event_timestamp;
+			das_conf.require_message_authenticator =
+				conf->radius_das_require_message_authenticator;
+			das_conf.ctx = hapd;
+			das_conf.disconnect = hostapd_das_disconnect;
+			das_conf.coa = hostapd_das_coa;
+			hapd->radius_das = radius_das_init(&das_conf);
+			if (!hapd->radius_das) {
+				wpa_printf(MSG_ERROR,
+					   "RADIUS DAS initialization failed.");
+				return -1;
+			}
+		}
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "MLD: Using RADIUS client of the first BSS");
+		hapd->radius = hapd->mld_first_bss->radius;
+		hapd->radius_das = hapd->mld_first_bss->radius_das;
+	}
+#endif /* CONFIG_NO_RADIUS */
+
+	if (hostapd_acl_init(hapd)) {
+		wpa_printf(MSG_ERROR, "ACL initialization failed.");
+		return -1;
+	}
+	if (hostapd_init_wps(hapd, conf))
+		return -1;
+
+#ifdef CONFIG_DPP
+	hapd->gas = gas_query_ap_init(hapd, hapd->msg_ctx);
+	if (!hapd->gas)
+		return -1;
+	if (hostapd_dpp_init(hapd))
+		return -1;
+#endif /* CONFIG_DPP */
+
+	if (authsrv_init(hapd) < 0)
+		return -1;
+
+	if (ieee802_1x_init(hapd)) {
+		wpa_printf(MSG_ERROR, "IEEE 802.1X initialization failed.");
+		return -1;
+	}
+
+	if ((conf->wpa || conf->osen) && hostapd_setup_wpa(hapd))
+		return -1;
+
+	if (accounting_init(hapd)) {
+		wpa_printf(MSG_ERROR, "Accounting initialization failed.");
+		return -1;
+	}
+
+#ifdef CONFIG_INTERWORKING
+	if (gas_serv_init(hapd)) {
+		wpa_printf(MSG_ERROR, "GAS server initialization failed");
+		return -1;
+	}
+#endif /* CONFIG_INTERWORKING */
+
+	if (conf->qos_map_set_len &&
+	    hostapd_drv_set_qos_map(hapd, conf->qos_map_set,
+				    conf->qos_map_set_len)) {
+		wpa_printf(MSG_ERROR, "Failed to initialize QoS Map");
+		return -1;
+	}
+
+	if (conf->bss_load_update_period && bss_load_update_init(hapd)) {
+		wpa_printf(MSG_ERROR, "BSS Load initialization failed");
+		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,
+				   "Generic snooping infrastructure initialization failed");
+			return -1;
+		}
+
+		if (dhcp_snoop_init(hapd)) {
+			wpa_printf(MSG_ERROR,
+				   "DHCP snooping initialization failed");
+			return -1;
+		}
+
+		if (ndisc_snoop_init(hapd)) {
+			wpa_printf(MSG_ERROR,
+				   "Neighbor Discovery snooping initialization failed");
+			return -1;
+		}
+	}
+
+	if (!hostapd_drv_none(hapd) && vlan_init(hapd)) {
+		wpa_printf(MSG_ERROR, "VLAN initialization failed.");
+		return -1;
+	}
+
+	if (start_beacon && hostapd_start_beacon(hapd, flush_old_stations) < 0)
+		return -1;
+
+	if (hapd->wpa_auth && wpa_init_keys(hapd->wpa_auth) < 0)
+		return -1;
+
+	return 0;
+}
+
+
+static void hostapd_tx_queue_params(struct hostapd_iface *iface)
+{
+	struct hostapd_data *hapd = iface->bss[0];
+	int i;
+	struct hostapd_tx_queue_params *p;
+
+#ifdef CONFIG_MESH
+	if ((hapd->conf->mesh & MESH_ENABLED) && iface->mconf == NULL)
+		return;
+#endif /* CONFIG_MESH */
+
+	for (i = 0; i < NUM_TX_QUEUES; i++) {
+		p = &iface->conf->tx_queue[i];
+
+		if (hostapd_set_tx_queue_params(hapd, i, p->aifs, p->cwmin,
+						p->cwmax, p->burst)) {
+			wpa_printf(MSG_DEBUG, "Failed to set TX queue "
+				   "parameters for queue %d.", i);
+			/* Continue anyway */
+		}
+	}
+}
+
+
+static int hostapd_set_acl_list(struct hostapd_data *hapd,
+				struct mac_acl_entry *mac_acl,
+				int n_entries, u8 accept_acl)
+{
+	struct hostapd_acl_params *acl_params;
+	int i, err;
+
+	acl_params = os_zalloc(sizeof(*acl_params) +
+			       (n_entries * sizeof(acl_params->mac_acl[0])));
+	if (!acl_params)
+		return -ENOMEM;
+
+	for (i = 0; i < n_entries; i++)
+		os_memcpy(acl_params->mac_acl[i].addr, mac_acl[i].addr,
+			  ETH_ALEN);
+
+	acl_params->acl_policy = accept_acl;
+	acl_params->num_mac_acl = n_entries;
+
+	err = hostapd_drv_set_acl(hapd, acl_params);
+
+	os_free(acl_params);
+
+	return err;
+}
+
+
+int hostapd_set_acl(struct hostapd_data *hapd)
+{
+	struct hostapd_config *conf = hapd->iconf;
+	int err = 0;
+	u8 accept_acl;
+
+	if (hapd->iface->drv_max_acl_mac_addrs == 0)
+		return 0;
+
+	if (conf->bss[0]->macaddr_acl == DENY_UNLESS_ACCEPTED) {
+		accept_acl = 1;
+		err = hostapd_set_acl_list(hapd, conf->bss[0]->accept_mac,
+					   conf->bss[0]->num_accept_mac,
+					   accept_acl);
+		if (err) {
+			wpa_printf(MSG_DEBUG, "Failed to set accept acl");
+			return -1;
+		}
+	} else if (conf->bss[0]->macaddr_acl == ACCEPT_UNLESS_DENIED) {
+		accept_acl = 0;
+		err = hostapd_set_acl_list(hapd, conf->bss[0]->deny_mac,
+					   conf->bss[0]->num_deny_mac,
+					   accept_acl);
+		if (err) {
+			wpa_printf(MSG_DEBUG, "Failed to set deny acl");
+			return -1;
+		}
+	}
+	return err;
+}
+
+
+static int start_ctrl_iface_bss(struct hostapd_data *hapd)
+{
+	if (!hapd->iface->interfaces ||
+	    !hapd->iface->interfaces->ctrl_iface_init)
+		return 0;
+
+	if (hapd->iface->interfaces->ctrl_iface_init(hapd)) {
+		wpa_printf(MSG_ERROR,
+			   "Failed to setup control interface for %s",
+			   hapd->conf->iface);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int start_ctrl_iface(struct hostapd_iface *iface)
+{
+	size_t i;
+
+	if (!iface->interfaces || !iface->interfaces->ctrl_iface_init)
+		return 0;
+
+	for (i = 0; i < iface->num_bss; i++) {
+		struct hostapd_data *hapd = iface->bss[i];
+		if (iface->interfaces->ctrl_iface_init(hapd)) {
+			wpa_printf(MSG_ERROR,
+				   "Failed to setup control interface for %s",
+				   hapd->conf->iface);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+/* When NO_IR flag is set and AP is stopped, clean up BSS parameters without
+ * deinitializing the driver and the control interfaces. A subsequent
+ * REG_CHANGE event can bring the AP back up.
+ */
+static void hostapd_no_ir_cleanup(struct hostapd_data *bss)
+{
+	hostapd_bss_deinit_no_free(bss);
+	hostapd_free_hapd_data(bss);
+	hostapd_cleanup_iface_partial(bss->iface);
+}
+
+
+static int hostapd_no_ir_channel_list_updated(struct hostapd_iface *iface,
+					      void *ctx)
+{
+	bool all_no_ir, is_6ghz;
+	int i, j;
+	struct hostapd_hw_modes *mode = NULL;
+
+	if (hostapd_get_hw_features(iface))
+		return 0;
+
+	all_no_ir = true;
+	is_6ghz = false;
+
+	for (i = 0; i < iface->num_hw_features; i++) {
+		mode = &iface->hw_features[i];
+
+		if (mode->mode == iface->conf->hw_mode) {
+			if (iface->freq > 0 &&
+			    !hw_mode_get_channel(mode, iface->freq, NULL)) {
+				mode = NULL;
+				continue;
+			}
+
+			for (j = 0; j < mode->num_channels; j++) {
+				if (!(mode->channels[j].flag &
+				      HOSTAPD_CHAN_NO_IR))
+					all_no_ir = false;
+
+				if (is_6ghz_freq(mode->channels[j].freq))
+					is_6ghz = true;
+			}
+			break;
+		}
+	}
+
+	if (!mode || !is_6ghz)
+		return 0;
+	iface->current_mode = mode;
+
+	if (iface->state == HAPD_IFACE_ENABLED) {
+		if (!all_no_ir) {
+			struct hostapd_channel_data *chan;
+
+			chan = hw_get_channel_freq(iface->current_mode->mode,
+						   iface->freq, NULL,
+						   iface->hw_features,
+						   iface->num_hw_features);
+
+			if (!chan) {
+				wpa_printf(MSG_ERROR,
+					   "NO_IR: Could not derive chan from freq");
+				return 0;
+			}
+
+			if (!(chan->flag & HOSTAPD_CHAN_NO_IR))
+				return 0;
+			wpa_printf(MSG_DEBUG,
+				   "NO_IR: The current channel has NO_IR flag now, stop AP.");
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "NO_IR: All chan in new chanlist are NO_IR, stop AP.");
+		}
+
+		hostapd_set_state(iface, HAPD_IFACE_NO_IR);
+		iface->is_no_ir = true;
+		hostapd_drv_stop_ap(iface->bss[0]);
+		hostapd_no_ir_cleanup(iface->bss[0]);
+		wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_NO_IR);
+	} else if (iface->state == HAPD_IFACE_NO_IR) {
+		if (all_no_ir) {
+			wpa_printf(MSG_DEBUG,
+				   "NO_IR: AP in NO_IR and all chan in the new chanlist are NO_IR. Ignore");
+			return 0;
+		}
+
+		if (!iface->conf->acs) {
+			struct hostapd_channel_data *chan;
+
+			chan = hw_get_channel_freq(iface->current_mode->mode,
+						   iface->freq, NULL,
+						   iface->hw_features,
+						   iface->num_hw_features);
+			if (!chan) {
+				wpa_printf(MSG_ERROR,
+					   "NO_IR: Could not derive chan from freq");
+				return 0;
+			}
+
+			/* If the last operating channel is NO_IR, trigger ACS.
+			 */
+			if (chan->flag & HOSTAPD_CHAN_NO_IR) {
+				iface->freq = 0;
+				iface->conf->channel = 0;
+				if (acs_init(iface) != HOSTAPD_CHAN_ACS)
+					wpa_printf(MSG_ERROR,
+						   "NO_IR: Could not start ACS");
+				return 0;
+			}
+		}
+
+		setup_interface2(iface);
+	}
+
+	return 0;
+}
+
+
+static void channel_list_update_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_iface *iface = eloop_ctx;
+
+	if (!iface->wait_channel_update) {
+		wpa_printf(MSG_INFO, "Channel list update timeout, but interface was not waiting for it");
+		return;
+	}
+
+	/*
+	 * It is possible that the existing channel list is acceptable, so try
+	 * to proceed.
+	 */
+	wpa_printf(MSG_DEBUG, "Channel list update timeout - try to continue anyway");
+	setup_interface2(iface);
+}
+
+
+void hostapd_channel_list_updated(struct hostapd_iface *iface, int initiator)
+{
+	if (initiator == REGDOM_SET_BY_DRIVER) {
+		hostapd_for_each_interface(iface->interfaces,
+					   hostapd_no_ir_channel_list_updated,
+					   NULL);
+		return;
+	}
+
+	if (!iface->wait_channel_update || initiator != REGDOM_SET_BY_USER)
+		return;
+
+	wpa_printf(MSG_DEBUG, "Channel list updated - continue setup");
+	eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
+	setup_interface2(iface);
+}
+
+
+static int setup_interface(struct hostapd_iface *iface)
+{
+	struct hostapd_data *hapd = iface->bss[0];
+	size_t i;
+
+	/*
+	 * It is possible that setup_interface() is called after the interface
+	 * was disabled etc., in which case driver_ap_teardown is possibly set
+	 * to 1. Clear it here so any other key/station deletion, which is not
+	 * part of a teardown flow, would also call the relevant driver
+	 * callbacks.
+	 */
+	iface->driver_ap_teardown = 0;
+
+	if (!iface->phy[0]) {
+		const char *phy = hostapd_drv_get_radio_name(hapd);
+		if (phy) {
+			wpa_printf(MSG_DEBUG, "phy: %s", phy);
+			os_strlcpy(iface->phy, phy, sizeof(iface->phy));
+		}
+	}
+
+	/*
+	 * Make sure that all BSSes get configured with a pointer to the same
+	 * driver interface.
+	 */
+	for (i = 1; i < iface->num_bss; i++) {
+		iface->bss[i]->driver = hapd->driver;
+		iface->bss[i]->drv_priv = hapd->drv_priv;
+	}
+
+	if (hostapd_validate_bssid_configuration(iface))
+		return -1;
+
+	/*
+	 * Initialize control interfaces early to allow external monitoring of
+	 * channel setup operations that may take considerable amount of time
+	 * especially for DFS cases.
+	 */
+	if (start_ctrl_iface(iface))
+		return -1;
+
+	if (hapd->iconf->country[0] && hapd->iconf->country[1]) {
+		char country[4], previous_country[4];
+
+		hostapd_set_state(iface, HAPD_IFACE_COUNTRY_UPDATE);
+		if (hostapd_get_country(hapd, previous_country) < 0)
+			previous_country[0] = '\0';
+
+		os_memcpy(country, hapd->iconf->country, 3);
+		country[3] = '\0';
+		if (hostapd_set_country(hapd, country) < 0) {
+			wpa_printf(MSG_ERROR, "Failed to set country code");
+			return -1;
+		}
+
+		wpa_printf(MSG_DEBUG, "Previous country code %s, new country code %s",
+			   previous_country, country);
+
+		if (os_strncmp(previous_country, country, 2) != 0) {
+			wpa_printf(MSG_DEBUG, "Continue interface setup after channel list update");
+			iface->wait_channel_update = 1;
+			eloop_register_timeout(5, 0,
+					       channel_list_update_timeout,
+					       iface, NULL);
+			return 0;
+		}
+	}
+
+	return setup_interface2(iface);
+}
+
+
+static int configured_fixed_chan_to_freq(struct hostapd_iface *iface)
+{
+	int freq, i, j;
+
+	if (!iface->conf->channel)
+		return 0;
+	if (iface->conf->op_class) {
+		freq = ieee80211_chan_to_freq(NULL, iface->conf->op_class,
+					      iface->conf->channel);
+		if (freq < 0) {
+			wpa_printf(MSG_INFO,
+				   "Could not convert op_class %u channel %u to operating frequency",
+				   iface->conf->op_class, iface->conf->channel);
+			return -1;
+		}
+		iface->freq = freq;
+		return 0;
+	}
+
+	/* Old configurations using only 2.4/5/60 GHz bands may not specify the
+	 * op_class parameter. Select a matching channel from the configured
+	 * mode using the channel parameter for these cases.
+	 */
+	for (j = 0; j < iface->num_hw_features; j++) {
+		struct hostapd_hw_modes *mode = &iface->hw_features[j];
+
+		if (iface->conf->hw_mode != HOSTAPD_MODE_IEEE80211ANY &&
+		    iface->conf->hw_mode != mode->mode)
+			continue;
+		for (i = 0; i < mode->num_channels; i++) {
+			struct hostapd_channel_data *chan = &mode->channels[i];
+
+			if (chan->chan == iface->conf->channel &&
+			    !is_6ghz_freq(chan->freq)) {
+				iface->freq = chan->freq;
+				return 0;
+			}
+		}
+	}
+
+	wpa_printf(MSG_INFO, "Could not determine operating frequency");
+	return -1;
+}
+
+
+static void hostapd_set_6ghz_sec_chan(struct hostapd_iface *iface)
+{
+	int bw;
+
+	if (!is_6ghz_op_class(iface->conf->op_class))
+		return;
+
+	bw = op_class_to_bandwidth(iface->conf->op_class);
+	/* Assign the secondary channel if absent in config for
+	 * bandwidths > 20 MHz */
+	if (bw >= 40 && !iface->conf->secondary_channel) {
+		if (((iface->conf->channel - 1) / 4) % 2)
+			iface->conf->secondary_channel = -1;
+		else
+			iface->conf->secondary_channel = 1;
+	}
+}
+
+
+static int setup_interface2(struct hostapd_iface *iface)
+{
+	iface->wait_channel_update = 0;
+	iface->is_no_ir = false;
+
+	if (hostapd_get_hw_features(iface)) {
+		/* Not all drivers support this yet, so continue without hw
+		 * feature data. */
+	} 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;
+
+		if (iface->conf->op_class) {
+			enum oper_chan_width ch_width;
+
+			ch_width = op_class_to_ch_width(iface->conf->op_class);
+			hostapd_set_oper_chwidth(iface->conf, ch_width);
+			hostapd_set_6ghz_sec_chan(iface);
+		}
+
+		ret = hostapd_select_hw_mode(iface);
+		if (ret < 0) {
+			wpa_printf(MSG_ERROR, "Could not select hw_mode and "
+				   "channel. (%d)", ret);
+			goto fail;
+		}
+		if (ret == 1) {
+			wpa_printf(MSG_DEBUG, "Interface initialization will be completed in a callback (ACS)");
+			return 0;
+		}
+		ret = hostapd_check_edmg_capab(iface);
+		if (ret < 0)
+			goto fail;
+		ret = hostapd_check_he_6ghz_capab(iface);
+		if (ret < 0)
+			goto fail;
+		ret = hostapd_check_ht_capab(iface);
+		if (ret < 0)
+			goto fail;
+		if (ret == 1) {
+			wpa_printf(MSG_DEBUG, "Interface initialization will "
+				   "be completed in a callback");
+			return 0;
+		}
+
+		if (iface->conf->ieee80211h)
+			wpa_printf(MSG_DEBUG, "DFS support is enabled");
+	}
+	return hostapd_setup_interface_complete(iface, 0);
+
+fail:
+	if (iface->is_no_ir) {
+		/* If AP is in NO_IR state, it can be reenabled by the driver
+		 * regulatory update and EVENT_CHANNEL_LIST_CHANGED. */
+		hostapd_set_state(iface, HAPD_IFACE_NO_IR);
+		wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_NO_IR);
+		return 0;
+	}
+
+	hostapd_set_state(iface, HAPD_IFACE_DISABLED);
+	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
+	if (iface->interfaces && iface->interfaces->terminate_on_error)
+		eloop_terminate();
+	return -1;
+}
+
+
+#ifdef CONFIG_FST
+
+static const u8 * fst_hostapd_get_bssid_cb(void *ctx)
+{
+	struct hostapd_data *hapd = ctx;
+
+	return hapd->own_addr;
+}
+
+
+static void fst_hostapd_get_channel_info_cb(void *ctx,
+					    enum hostapd_hw_mode *hw_mode,
+					    u8 *channel)
+{
+	struct hostapd_data *hapd = ctx;
+
+	*hw_mode = ieee80211_freq_to_chan(hapd->iface->freq, channel);
+}
+
+
+static int fst_hostapd_get_hw_modes_cb(void *ctx,
+				       struct hostapd_hw_modes **modes)
+{
+	struct hostapd_data *hapd = ctx;
+
+	*modes = hapd->iface->hw_features;
+	return hapd->iface->num_hw_features;
+}
+
+
+static void fst_hostapd_set_ies_cb(void *ctx, const struct wpabuf *fst_ies)
+{
+	struct hostapd_data *hapd = ctx;
+
+	if (hapd->iface->fst_ies != fst_ies) {
+		hapd->iface->fst_ies = fst_ies;
+		if (ieee802_11_set_beacon(hapd))
+			wpa_printf(MSG_WARNING, "FST: Cannot set beacon");
+	}
+}
+
+
+static int fst_hostapd_send_action_cb(void *ctx, const u8 *da,
+				      struct wpabuf *buf)
+{
+	struct hostapd_data *hapd = ctx;
+
+	return hostapd_drv_send_action(hapd, hapd->iface->freq, 0, da,
+				       wpabuf_head(buf), wpabuf_len(buf));
+}
+
+
+static const struct wpabuf * fst_hostapd_get_mb_ie_cb(void *ctx, const u8 *addr)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta = ap_get_sta(hapd, addr);
+
+	return sta ? sta->mb_ies : NULL;
+}
+
+
+static void fst_hostapd_update_mb_ie_cb(void *ctx, const u8 *addr,
+					const u8 *buf, size_t size)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta = ap_get_sta(hapd, addr);
+
+	if (sta) {
+		struct mb_ies_info info;
+
+		if (!mb_ies_info_by_ies(&info, buf, size)) {
+			wpabuf_free(sta->mb_ies);
+			sta->mb_ies = mb_ies_by_info(&info);
+		}
+	}
+}
+
+
+static const u8 * fst_hostapd_get_sta(struct fst_get_peer_ctx **get_ctx,
+				      bool mb_only)
+{
+	struct sta_info *s = (struct sta_info *) *get_ctx;
+
+	if (mb_only) {
+		for (; s && !s->mb_ies; s = s->next)
+			;
+	}
+
+	if (s) {
+		*get_ctx = (struct fst_get_peer_ctx *) s->next;
+
+		return s->addr;
+	}
+
+	*get_ctx = NULL;
+	return NULL;
+}
+
+
+static const u8 * fst_hostapd_get_peer_first(void *ctx,
+					     struct fst_get_peer_ctx **get_ctx,
+					     bool mb_only)
+{
+	struct hostapd_data *hapd = ctx;
+
+	*get_ctx = (struct fst_get_peer_ctx *) hapd->sta_list;
+
+	return fst_hostapd_get_sta(get_ctx, mb_only);
+}
+
+
+static const u8 * fst_hostapd_get_peer_next(void *ctx,
+					    struct fst_get_peer_ctx **get_ctx,
+					    bool mb_only)
+{
+	return fst_hostapd_get_sta(get_ctx, mb_only);
+}
+
+
+void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd,
+				struct fst_wpa_obj *iface_obj)
+{
+	os_memset(iface_obj, 0, sizeof(*iface_obj));
+	iface_obj->ctx = hapd;
+	iface_obj->get_bssid = fst_hostapd_get_bssid_cb;
+	iface_obj->get_channel_info = fst_hostapd_get_channel_info_cb;
+	iface_obj->get_hw_modes = fst_hostapd_get_hw_modes_cb;
+	iface_obj->set_ies = fst_hostapd_set_ies_cb;
+	iface_obj->send_action = fst_hostapd_send_action_cb;
+	iface_obj->get_mb_ie = fst_hostapd_get_mb_ie_cb;
+	iface_obj->update_mb_ie = fst_hostapd_update_mb_ie_cb;
+	iface_obj->get_peer_first = fst_hostapd_get_peer_first;
+	iface_obj->get_peer_next = fst_hostapd_get_peer_next;
+}
+
+#endif /* CONFIG_FST */
+
+#ifdef CONFIG_OWE
+
+static int hostapd_owe_iface_iter(struct hostapd_iface *iface, void *ctx)
+{
+	struct hostapd_data *hapd = ctx;
+	size_t i;
+
+	for (i = 0; i < iface->num_bss; i++) {
+		struct hostapd_data *bss = iface->bss[i];
+
+		if (os_strcmp(hapd->conf->owe_transition_ifname,
+			      bss->conf->iface) != 0)
+			continue;
+
+		wpa_printf(MSG_DEBUG,
+			   "OWE: ifname=%s found transition mode ifname=%s BSSID "
+			   MACSTR " SSID %s",
+			   hapd->conf->iface, bss->conf->iface,
+			   MAC2STR(bss->own_addr),
+			   wpa_ssid_txt(bss->conf->ssid.ssid,
+					bss->conf->ssid.ssid_len));
+		if (!bss->conf->ssid.ssid_set || !bss->conf->ssid.ssid_len ||
+		    is_zero_ether_addr(bss->own_addr))
+			continue;
+
+		os_memcpy(hapd->conf->owe_transition_bssid, bss->own_addr,
+			  ETH_ALEN);
+		os_memcpy(hapd->conf->owe_transition_ssid,
+			  bss->conf->ssid.ssid, bss->conf->ssid.ssid_len);
+		hapd->conf->owe_transition_ssid_len = bss->conf->ssid.ssid_len;
+		wpa_printf(MSG_DEBUG,
+			   "OWE: Copied transition mode information");
+		return 1;
+	}
+
+	return 0;
+}
+
+
+int hostapd_owe_trans_get_info(struct hostapd_data *hapd)
+{
+	if (hapd->conf->owe_transition_ssid_len > 0 &&
+	    !is_zero_ether_addr(hapd->conf->owe_transition_bssid))
+		return 0;
+
+	/* Find transition mode SSID/BSSID information from a BSS operated by
+	 * this hostapd instance. */
+	if (!hapd->iface->interfaces ||
+	    !hapd->iface->interfaces->for_each_interface)
+		return hostapd_owe_iface_iter(hapd->iface, hapd);
+	else
+		return hapd->iface->interfaces->for_each_interface(
+			hapd->iface->interfaces, hostapd_owe_iface_iter, hapd);
+}
+
+
+static int hostapd_owe_iface_iter2(struct hostapd_iface *iface, void *ctx)
+{
+	size_t i;
+
+	for (i = 0; i < iface->num_bss; i++) {
+		struct hostapd_data *bss = iface->bss[i];
+		int res;
+
+		if (!bss->conf->owe_transition_ifname[0])
+			continue;
+		if (bss->iface->state != HAPD_IFACE_ENABLED) {
+			wpa_printf(MSG_DEBUG,
+				   "OWE: Interface %s state %s - defer beacon update",
+				   bss->conf->iface,
+				   hostapd_state_text(bss->iface->state));
+			continue;
+		}
+		res = hostapd_owe_trans_get_info(bss);
+		if (res == 0)
+			continue;
+		wpa_printf(MSG_DEBUG,
+			   "OWE: Matching transition mode interface enabled - update beacon data for %s",
+			   bss->conf->iface);
+		ieee802_11_set_beacon(bss);
+	}
+
+	return 0;
+}
+
+#endif /* CONFIG_OWE */
+
+
+static void hostapd_owe_update_trans(struct hostapd_iface *iface)
+{
+#ifdef CONFIG_OWE
+	/* Check whether the enabled BSS can complete OWE transition mode
+	 * configuration for any pending interface. */
+	if (!iface->interfaces ||
+	    !iface->interfaces->for_each_interface)
+		hostapd_owe_iface_iter2(iface, NULL);
+	else
+		iface->interfaces->for_each_interface(
+			iface->interfaces, hostapd_owe_iface_iter2, NULL);
+#endif /* CONFIG_OWE */
+}
+
+
+static void hostapd_interface_setup_failure_handler(void *eloop_ctx,
+						    void *timeout_ctx)
+{
+	struct hostapd_iface *iface = eloop_ctx;
+	struct hostapd_data *hapd;
+
+	if (iface->num_bss < 1 || !iface->bss || !iface->bss[0])
+		return;
+	hapd = iface->bss[0];
+	if (hapd->setup_complete_cb)
+		hapd->setup_complete_cb(hapd->setup_complete_cb_ctx);
+}
+
+
+static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
+						 int err)
+{
+	struct hostapd_data *hapd = iface->bss[0];
+	size_t j;
+	u8 *prev_addr;
+	int delay_apply_cfg = 0;
+	int res_dfs_offload = 0;
+
+	if (err)
+		goto fail;
+
+	hostapd_ubus_add_iface(iface);
+	wpa_printf(MSG_DEBUG, "Completing interface initialization");
+	if (iface->freq) {
+#ifdef NEED_AP_MLME
+		int res;
+#endif /* NEED_AP_MLME */
+
+		wpa_printf(MSG_DEBUG, "Mode: %s  Channel: %d  "
+			   "Frequency: %d MHz",
+			   hostapd_hw_mode_txt(iface->conf->hw_mode),
+			   iface->conf->channel, iface->freq);
+
+#ifdef NEED_AP_MLME
+		/* Handle DFS only if it is not offloaded to the driver */
+		if (!(iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)) {
+			/* Check DFS */
+			res = hostapd_handle_dfs(iface);
+			if (res <= 0) {
+				if (res < 0)
+					goto fail;
+				return res;
+			}
+		} else {
+			/* If DFS is offloaded to the driver */
+			res_dfs_offload = hostapd_handle_dfs_offload(iface);
+			if (res_dfs_offload <= 0) {
+				if (res_dfs_offload < 0)
+					goto fail;
+			} else {
+				wpa_printf(MSG_DEBUG,
+					   "Proceed with AP/channel setup");
+				/*
+				 * If this is a DFS channel, move to completing
+				 * AP setup.
+				 */
+				if (res_dfs_offload == 1)
+					goto dfs_offload;
+				/* Otherwise fall through. */
+			}
+		}
+#endif /* NEED_AP_MLME */
+
+#ifdef CONFIG_MESH
+		if (iface->mconf != NULL) {
+			wpa_printf(MSG_DEBUG,
+				   "%s: Mesh configuration will be applied while joining the mesh network",
+				   iface->bss[0]->conf->iface);
+			delay_apply_cfg = 1;
+		}
+#endif /* CONFIG_MESH */
+
+		if (!delay_apply_cfg &&
+		    hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq,
+				     hapd->iconf->channel,
+				     hapd->iconf->enable_edmg,
+				     hapd->iconf->edmg_channel,
+				     hapd->iconf->ieee80211n,
+				     hapd->iconf->ieee80211ac,
+				     hapd->iconf->ieee80211ax,
+				     hapd->iconf->ieee80211be,
+				     hapd->iconf->secondary_channel,
+				     hostapd_get_oper_chwidth(hapd->iconf),
+				     hostapd_get_oper_centr_freq_seg0_idx(
+					     hapd->iconf),
+				     hostapd_get_oper_centr_freq_seg1_idx(
+					     hapd->iconf))) {
+			wpa_printf(MSG_ERROR, "Could not set channel for "
+				   "kernel driver");
+			goto fail;
+		}
+	}
+
+	if (iface->current_mode) {
+		if (hostapd_prepare_rates(iface, iface->current_mode)) {
+			wpa_printf(MSG_ERROR, "Failed to prepare rates "
+				   "table.");
+			hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_WARNING,
+				       "Failed to prepare rates table.");
+			goto fail;
+		}
+	}
+
+	if (hapd->iconf->rts_threshold >= -1 &&
+	    hostapd_set_rts(hapd, hapd->iconf->rts_threshold) &&
+	    hapd->iconf->rts_threshold >= -1) {
+		wpa_printf(MSG_ERROR, "Could not set RTS threshold for "
+			   "kernel driver");
+		goto fail;
+	}
+
+	if (hapd->iconf->fragm_threshold >= -1 &&
+	    hostapd_set_frag(hapd, hapd->iconf->fragm_threshold) &&
+	    hapd->iconf->fragm_threshold != -1) {
+		wpa_printf(MSG_ERROR, "Could not set fragmentation threshold "
+			   "for kernel driver");
+		goto fail;
+	}
+
+	prev_addr = hapd->own_addr;
+
+	for (j = 0; j < iface->num_bss; j++) {
+		hapd = iface->bss[j];
+		if (j)
+			os_memcpy(hapd->own_addr, prev_addr, ETH_ALEN);
+		if (hostapd_setup_bss(hapd, j == 0, !iface->conf->mbssid)) {
+			for (;;) {
+				hapd = iface->bss[j];
+				hostapd_bss_deinit_no_free(hapd);
+				hostapd_free_hapd_data(hapd);
+				if (j == 0)
+					break;
+				j--;
+			}
+			goto fail;
+		}
+		if (is_zero_ether_addr(hapd->conf->bssid))
+			prev_addr = hapd->own_addr;
+	}
+
+	if (hapd->iconf->mbssid) {
+		for (j = 0; hapd->iconf->mbssid && j < iface->num_bss; j++) {
+			hapd = iface->bss[j];
+			if (hostapd_start_beacon(hapd, true)) {
+				for (;;) {
+					hapd = iface->bss[j];
+					hostapd_bss_deinit_no_free(hapd);
+					hostapd_free_hapd_data(hapd);
+					if (j == 0)
+						break;
+					j--;
+				}
+				goto fail;
+			}
+		}
+	}
+
+	hapd = iface->bss[0];
+
+	hostapd_tx_queue_params(iface);
+
+	ap_list_init(iface);
+
+	hostapd_set_acl(hapd);
+
+	if (hostapd_driver_commit(hapd) < 0) {
+		wpa_printf(MSG_ERROR, "%s: Failed to commit driver "
+			   "configuration", __func__);
+		goto fail;
+	}
+
+	/*
+	 * WPS UPnP module can be initialized only when the "upnp_iface" is up.
+	 * If "interface" and "upnp_iface" are the same (e.g., non-bridge
+	 * mode), the interface is up only after driver_commit, so initialize
+	 * WPS after driver_commit.
+	 */
+	for (j = 0; j < iface->num_bss; j++) {
+		if (hostapd_init_wps_complete(iface->bss[j]))
+			goto fail;
+	}
+
+	if ((iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
+	    !res_dfs_offload) {
+		/*
+		 * If freq is DFS, and DFS is offloaded to the driver, then wait
+		 * for CAC to complete.
+		 */
+		wpa_printf(MSG_DEBUG, "%s: Wait for CAC to complete", __func__);
+		return res_dfs_offload;
+	}
+
+#ifdef NEED_AP_MLME
+dfs_offload:
+#endif /* NEED_AP_MLME */
+
+#ifdef CONFIG_FST
+	if (hapd->iconf->fst_cfg.group_id[0]) {
+		struct fst_wpa_obj iface_obj;
+
+		fst_hostapd_fill_iface_obj(hapd, &iface_obj);
+		iface->fst = fst_attach(hapd->conf->iface, hapd->own_addr,
+					&iface_obj, &hapd->iconf->fst_cfg);
+		if (!iface->fst) {
+			wpa_printf(MSG_ERROR, "Could not attach to FST %s",
+				   hapd->iconf->fst_cfg.group_id);
+			goto fail;
+		}
+	}
+#endif /* CONFIG_FST */
+
+	hostapd_set_state(iface, HAPD_IFACE_ENABLED);
+	hostapd_owe_update_trans(iface);
+	airtime_policy_update_init(iface);
+	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_ENABLED);
+	if (hapd->setup_complete_cb)
+		hapd->setup_complete_cb(hapd->setup_complete_cb_ctx);
+
+#ifdef CONFIG_MESH
+	if (delay_apply_cfg && !iface->mconf) {
+		wpa_printf(MSG_ERROR, "Error while completing mesh init");
+		goto fail;
+	}
+#endif /* CONFIG_MESH */
+
+	wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
+		   iface->bss[0]->conf->iface);
+	if (iface->interfaces && iface->interfaces->terminate_on_error > 0)
+		iface->interfaces->terminate_on_error--;
+
+	for (j = 0; j < iface->num_bss; j++)
+		hostapd_neighbor_set_own_report(iface->bss[j]);
+
+	if (iface->interfaces && iface->interfaces->count > 1)
+		ieee802_11_set_beacons(iface);
+
+	return 0;
+
+fail:
+	wpa_printf(MSG_ERROR, "Interface initialization failed");
+	hostapd_ubus_free_iface(iface);
+
+	if (iface->is_no_ir) {
+		hostapd_set_state(iface, HAPD_IFACE_NO_IR);
+		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_NO_IR);
+		return 0;
+	}
+
+	hostapd_set_state(iface, HAPD_IFACE_DISABLED);
+	wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
+#ifdef CONFIG_FST
+	if (iface->fst) {
+		fst_detach(iface->fst);
+		iface->fst = NULL;
+	}
+#endif /* CONFIG_FST */
+
+	if (iface->interfaces && iface->interfaces->terminate_on_error) {
+		eloop_terminate();
+	} else if (hapd->setup_complete_cb) {
+		/*
+		 * Calling hapd->setup_complete_cb directly may cause iface
+		 * deinitialization which may be accessed later by the caller.
+		 */
+		eloop_register_timeout(0, 0,
+				       hostapd_interface_setup_failure_handler,
+				       iface, NULL);
+	}
+
+	return -1;
+}
+
+
+/**
+ * hostapd_setup_interface_complete - Complete interface setup
+ *
+ * This function is called when previous steps in the interface setup has been
+ * completed. This can also start operations, e.g., DFS, that will require
+ * additional processing before interface is ready to be enabled. Such
+ * operations will call this function from eloop callbacks when finished.
+ */
+int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
+{
+	struct hapd_interfaces *interfaces = iface->interfaces;
+	struct hostapd_data *hapd = iface->bss[0];
+	unsigned int i;
+	int not_ready_in_sync_ifaces = 0;
+
+	if (!iface->need_to_start_in_sync)
+		return hostapd_setup_interface_complete_sync(iface, err);
+
+	if (err) {
+		wpa_printf(MSG_ERROR, "Interface initialization failed");
+		iface->need_to_start_in_sync = 0;
+
+		if (iface->is_no_ir) {
+			hostapd_set_state(iface, HAPD_IFACE_NO_IR);
+			wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_NO_IR);
+			return 0;
+		}
+
+		hostapd_set_state(iface, HAPD_IFACE_DISABLED);
+		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
+		if (interfaces && interfaces->terminate_on_error)
+			eloop_terminate();
+		return -1;
+	}
+
+	if (iface->ready_to_start_in_sync) {
+		/* Already in ready and waiting. should never happpen */
+		return 0;
+	}
+
+	for (i = 0; i < interfaces->count; i++) {
+		if (interfaces->iface[i]->need_to_start_in_sync &&
+		    !interfaces->iface[i]->ready_to_start_in_sync)
+			not_ready_in_sync_ifaces++;
+	}
+
+	/*
+	 * Check if this is the last interface, if yes then start all the other
+	 * waiting interfaces. If not, add this interface to the waiting list.
+	 */
+	if (not_ready_in_sync_ifaces > 1 && iface->state == HAPD_IFACE_DFS) {
+		/*
+		 * If this interface went through CAC, do not synchronize, just
+		 * start immediately.
+		 */
+		iface->need_to_start_in_sync = 0;
+		wpa_printf(MSG_INFO,
+			   "%s: Finished CAC - bypass sync and start interface",
+			   iface->bss[0]->conf->iface);
+		return hostapd_setup_interface_complete_sync(iface, err);
+	}
+
+	if (not_ready_in_sync_ifaces > 1) {
+		/* need to wait as there are other interfaces still coming up */
+		iface->ready_to_start_in_sync = 1;
+		wpa_printf(MSG_INFO,
+			   "%s: Interface waiting to sync with other interfaces",
+			   iface->bss[0]->conf->iface);
+		return 0;
+	}
+
+	wpa_printf(MSG_INFO,
+		   "%s: Last interface to sync - starting all interfaces",
+		   iface->bss[0]->conf->iface);
+	iface->need_to_start_in_sync = 0;
+	hostapd_setup_interface_complete_sync(iface, err);
+	for (i = 0; i < interfaces->count; i++) {
+		if (interfaces->iface[i]->need_to_start_in_sync &&
+		    interfaces->iface[i]->ready_to_start_in_sync) {
+			hostapd_setup_interface_complete_sync(
+				interfaces->iface[i], 0);
+			/* Only once the interfaces are sync started */
+			interfaces->iface[i]->need_to_start_in_sync = 0;
+		}
+	}
+
+	return 0;
+}
+
+
+/**
+ * hostapd_setup_interface - Setup of an interface
+ * @iface: Pointer to interface data.
+ * Returns: 0 on success, -1 on failure
+ *
+ * Initializes the driver interface, validates the configuration,
+ * and sets driver parameters based on the configuration.
+ * Flushes old stations, sets the channel, encryption,
+ * beacons, and WDS links based on the configuration.
+ *
+ * If interface setup requires more time, e.g., to perform HT co-ex scans, ACS,
+ * or DFS operations, this function returns 0 before such operations have been
+ * completed. The pending operations are registered into eloop and will be
+ * completed from eloop callbacks. Those callbacks end up calling
+ * hostapd_setup_interface_complete() once setup has been completed.
+ */
+int hostapd_setup_interface(struct hostapd_iface *iface)
+{
+	int ret;
+
+	if (!iface->conf)
+		return -1;
+	ret = setup_interface(iface);
+	if (ret) {
+		wpa_printf(MSG_ERROR, "%s: Unable to setup interface.",
+			   iface->conf->bss[0]->iface);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/**
+ * hostapd_alloc_bss_data - Allocate and initialize per-BSS data
+ * @hapd_iface: Pointer to interface data
+ * @conf: Pointer to per-interface configuration
+ * @bss: Pointer to per-BSS configuration for this BSS
+ * Returns: Pointer to allocated BSS data
+ *
+ * This function is used to allocate per-BSS data structure. This data will be
+ * freed after hostapd_cleanup() is called for it during interface
+ * deinitialization.
+ */
+struct hostapd_data *
+hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
+		       struct hostapd_config *conf,
+		       struct hostapd_bss_config *bss)
+{
+	struct hostapd_data *hapd;
+
+	hapd = os_zalloc(sizeof(*hapd));
+	if (hapd == NULL)
+		return NULL;
+
+	hapd->new_assoc_sta_cb = hostapd_new_assoc_sta;
+	hapd->iconf = conf;
+	hapd->conf = bss;
+	hapd->iface = hapd_iface;
+	if (bss && bss->config_id)
+		hapd->config_id = strdup(bss->config_id);
+	else
+		hapd->config_id = NULL;
+	if (conf)
+		hapd->driver = conf->driver;
+	hapd->ctrl_sock = -1;
+	dl_list_init(&hapd->ctrl_dst);
+	dl_list_init(&hapd->nr_db);
+	hapd->dhcp_sock = -1;
+#ifdef CONFIG_IEEE80211R_AP
+	dl_list_init(&hapd->l2_queue);
+	dl_list_init(&hapd->l2_oui_queue);
+#endif /* CONFIG_IEEE80211R_AP */
+#ifdef CONFIG_SAE
+	dl_list_init(&hapd->sae_commit_queue);
+#endif /* CONFIG_SAE */
+
+	return hapd;
+}
+
+
+static void hostapd_bss_deinit(struct hostapd_data *hapd)
+{
+	if (!hapd)
+		return;
+	wpa_printf(MSG_DEBUG, "%s: deinit bss %s", __func__,
+		   hapd->conf ? hapd->conf->iface : "N/A");
+	hostapd_bss_deinit_no_free(hapd);
+	wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
+#ifdef CONFIG_SQLITE
+	if (hapd->rad_attr_db) {
+		sqlite3_close(hapd->rad_attr_db);
+		hapd->rad_attr_db = NULL;
+	}
+#endif /* CONFIG_SQLITE */
+	hostapd_cleanup(hapd);
+}
+
+
+void hostapd_interface_deinit(struct hostapd_iface *iface)
+{
+	int j;
+
+	wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
+	if (iface == NULL)
+		return;
+
+	hostapd_set_state(iface, HAPD_IFACE_DISABLED);
+
+	eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
+	iface->wait_channel_update = 0;
+	iface->is_no_ir = false;
+
+#ifdef CONFIG_FST
+	if (iface->fst) {
+		fst_detach(iface->fst);
+		iface->fst = NULL;
+	}
+#endif /* CONFIG_FST */
+
+	for (j = (int) iface->num_bss - 1; j >= 0; j--) {
+		if (!iface->bss)
+			break;
+		hostapd_bss_deinit(iface->bss[j]);
+	}
+
+#ifdef NEED_AP_MLME
+	hostapd_stop_setup_timers(iface);
+	eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL);
+#endif /* NEED_AP_MLME */
+}
+
+
+void hostapd_interface_free(struct hostapd_iface *iface)
+{
+	size_t j;
+	wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
+	for (j = 0; j < iface->num_bss; j++) {
+		if (!iface->bss)
+			break;
+		wpa_printf(MSG_DEBUG, "%s: free hapd %p",
+			   __func__, iface->bss[j]);
+		os_free(iface->bss[j]);
+	}
+	hostapd_cleanup_iface(iface);
+}
+
+
+struct hostapd_iface * hostapd_alloc_iface(void)
+{
+	struct hostapd_iface *hapd_iface;
+
+	hapd_iface = os_zalloc(sizeof(*hapd_iface));
+	if (!hapd_iface)
+		return NULL;
+
+	dl_list_init(&hapd_iface->sta_seen);
+
+	return hapd_iface;
+}
+
+
+/**
+ * hostapd_init - Allocate and initialize per-interface data
+ * @config_file: Path to the configuration file
+ * Returns: Pointer to the allocated interface data or %NULL on failure
+ *
+ * This function is used to allocate main data structures for per-interface
+ * data. The allocated data buffer will be freed by calling
+ * hostapd_cleanup_iface().
+ */
+struct hostapd_iface * hostapd_init(struct hapd_interfaces *interfaces,
+				    const char *config_file)
+{
+	struct hostapd_iface *hapd_iface = NULL;
+	struct hostapd_config *conf = NULL;
+	struct hostapd_data *hapd;
+	size_t i;
+
+	hapd_iface = hostapd_alloc_iface();
+	if (hapd_iface == NULL)
+		goto fail;
+
+	hapd_iface->config_fname = os_strdup(config_file);
+	if (hapd_iface->config_fname == NULL)
+		goto fail;
+
+	conf = interfaces->config_read_cb(hapd_iface->config_fname);
+	if (conf == NULL)
+		goto fail;
+	hapd_iface->conf = conf;
+
+	hapd_iface->num_bss = conf->num_bss;
+	hapd_iface->bss = os_calloc(conf->num_bss,
+				    sizeof(struct hostapd_data *));
+	if (hapd_iface->bss == NULL)
+		goto fail;
+
+	for (i = 0; i < conf->num_bss; i++) {
+		hapd = hapd_iface->bss[i] =
+			hostapd_alloc_bss_data(hapd_iface, conf,
+					       conf->bss[i]);
+		if (hapd == NULL)
+			goto fail;
+		hapd->msg_ctx = hapd;
+	}
+
+	return hapd_iface;
+
+fail:
+	wpa_printf(MSG_ERROR, "Failed to set up interface with %s",
+		   config_file);
+	if (conf)
+		hostapd_config_free(conf);
+	if (hapd_iface) {
+		os_free(hapd_iface->config_fname);
+		os_free(hapd_iface->bss);
+		wpa_printf(MSG_DEBUG, "%s: free iface %p",
+			   __func__, hapd_iface);
+		os_free(hapd_iface);
+	}
+	return NULL;
+}
+
+
+static int ifname_in_use(struct hapd_interfaces *interfaces, const char *ifname)
+{
+	size_t i, j;
+
+	for (i = 0; i < interfaces->count; i++) {
+		struct hostapd_iface *iface = interfaces->iface[i];
+		for (j = 0; j < iface->num_bss; j++) {
+			struct hostapd_data *hapd = iface->bss[j];
+			if (os_strcmp(ifname, hapd->conf->iface) == 0)
+				return 1;
+		}
+	}
+
+	return 0;
+}
+
+
+/**
+ * hostapd_interface_init_bss - Read configuration file and init BSS data
+ *
+ * This function is used to parse configuration file for a BSS. This BSS is
+ * added to an existing interface sharing the same radio (if any) or a new
+ * interface is created if this is the first interface on a radio. This
+ * allocate memory for the BSS. No actual driver operations are started.
+ *
+ * This is similar to hostapd_interface_init(), but for a case where the
+ * configuration is used to add a single BSS instead of all BSSes for a radio.
+ */
+struct hostapd_iface *
+hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy,
+			   const char *config_fname, int debug)
+{
+	struct hostapd_iface *new_iface = NULL, *iface = NULL;
+	struct hostapd_data *hapd;
+	int k;
+	size_t i, bss_idx;
+
+	if (!phy || !*phy)
+		return NULL;
+
+	for (i = 0; i < interfaces->count; i++) {
+		if (os_strcmp(interfaces->iface[i]->phy, phy) == 0) {
+			iface = interfaces->iface[i];
+			break;
+		}
+	}
+
+	wpa_printf(MSG_INFO, "Configuration file: %s (phy %s)%s",
+		   config_fname, phy, iface ? "" : " --> new PHY");
+	if (iface) {
+		struct hostapd_config *conf;
+		struct hostapd_bss_config **tmp_conf;
+		struct hostapd_data **tmp_bss;
+		struct hostapd_bss_config *bss;
+		const char *ifname;
+
+		/* Add new BSS to existing iface */
+		conf = interfaces->config_read_cb(config_fname);
+		if (conf == NULL)
+			return NULL;
+		if (conf->num_bss > 1) {
+			wpa_printf(MSG_ERROR, "Multiple BSSes specified in BSS-config");
+			hostapd_config_free(conf);
+			return NULL;
+		}
+
+		ifname = conf->bss[0]->iface;
+		if (ifname[0] != '\0' && ifname_in_use(interfaces, ifname)) {
+			wpa_printf(MSG_ERROR,
+				   "Interface name %s already in use", ifname);
+			hostapd_config_free(conf);
+			return NULL;
+		}
+
+		tmp_conf = os_realloc_array(
+			iface->conf->bss, iface->conf->num_bss + 1,
+			sizeof(struct hostapd_bss_config *));
+		tmp_bss = os_realloc_array(iface->bss, iface->num_bss + 1,
+					   sizeof(struct hostapd_data *));
+		if (tmp_bss)
+			iface->bss = tmp_bss;
+		if (tmp_conf) {
+			iface->conf->bss = tmp_conf;
+			iface->conf->last_bss = tmp_conf[0];
+		}
+		if (tmp_bss == NULL || tmp_conf == NULL) {
+			hostapd_config_free(conf);
+			return NULL;
+		}
+		bss = iface->conf->bss[iface->conf->num_bss] = conf->bss[0];
+		iface->conf->num_bss++;
+
+		hapd = hostapd_alloc_bss_data(iface, iface->conf, bss);
+		if (hapd == NULL) {
+			iface->conf->num_bss--;
+			hostapd_config_free(conf);
+			return NULL;
+		}
+		iface->conf->last_bss = bss;
+		iface->bss[iface->num_bss] = hapd;
+		hapd->msg_ctx = hapd;
+
+		bss_idx = iface->num_bss++;
+		conf->num_bss--;
+		conf->bss[0] = NULL;
+		hostapd_config_free(conf);
+	} else {
+		/* Add a new iface with the first BSS */
+		new_iface = iface = hostapd_init(interfaces, config_fname);
+		if (!iface)
+			return NULL;
+		os_strlcpy(iface->phy, phy, sizeof(iface->phy));
+		iface->interfaces = interfaces;
+		bss_idx = 0;
+	}
+
+	for (k = 0; k < debug; k++) {
+		if (iface->bss[bss_idx]->conf->logger_stdout_level > 0)
+			iface->bss[bss_idx]->conf->logger_stdout_level--;
+	}
+
+	if (iface->conf->bss[bss_idx]->iface[0] == '\0' &&
+	    !hostapd_drv_none(iface->bss[bss_idx])) {
+		wpa_printf(MSG_ERROR, "Interface name not specified in %s",
+			   config_fname);
+		if (new_iface)
+			hostapd_interface_deinit_free(new_iface);
+		return NULL;
+	}
+
+	return iface;
+}
+
+
+void hostapd_interface_deinit_free(struct hostapd_iface *iface)
+{
+	const struct wpa_driver_ops *driver;
+	void *drv_priv;
+
+	wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
+	if (iface == NULL)
+		return;
+	wpa_printf(MSG_DEBUG, "%s: num_bss=%u conf->num_bss=%u",
+		   __func__, (unsigned int) iface->num_bss,
+		   (unsigned int) iface->conf->num_bss);
+	driver = iface->bss[0]->driver;
+	drv_priv = iface->bss[0]->drv_priv;
+	hostapd_ubus_free_iface(iface);
+	hostapd_interface_deinit(iface);
+	wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit",
+		   __func__, driver, drv_priv);
+	if (driver && driver->hapd_deinit && drv_priv) {
+		if (!iface->bss[0]->mld_first_bss)
+			driver->hapd_deinit(drv_priv);
+		hostapd_clear_drv_priv(iface->bss[0]);
+	}
+	hostapd_interface_free(iface);
+}
+
+
+static void hostapd_deinit_driver(const struct wpa_driver_ops *driver,
+				  void *drv_priv,
+				  struct hostapd_iface *hapd_iface)
+{
+	size_t j;
+
+	wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit",
+		   __func__, driver, drv_priv);
+	if (driver && driver->hapd_deinit && drv_priv) {
+		if (!hapd_iface->bss[0]->mld_first_bss)
+			driver->hapd_deinit(drv_priv);
+		for (j = 0; j < hapd_iface->num_bss; j++) {
+			wpa_printf(MSG_DEBUG, "%s:bss[%d]->drv_priv=%p",
+				   __func__, (int) j,
+				   hapd_iface->bss[j]->drv_priv);
+			if (hapd_iface->bss[j]->drv_priv == drv_priv) {
+				hostapd_clear_drv_priv(hapd_iface->bss[j]);
+				hapd_iface->extended_capa = NULL;
+				hapd_iface->extended_capa_mask = NULL;
+				hapd_iface->extended_capa_len = 0;
+			}
+		}
+	}
+}
+
+
+int hostapd_enable_iface(struct hostapd_iface *hapd_iface)
+{
+	size_t j;
+
+	if (!hapd_iface)
+		return -1;
+
+	if (hapd_iface->enable_iface_cb)
+		return hapd_iface->enable_iface_cb(hapd_iface);
+
+	if (hapd_iface->bss[0]->drv_priv != NULL) {
+		wpa_printf(MSG_ERROR, "Interface %s already enabled",
+			   hapd_iface->conf->bss[0]->iface);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "Enable interface %s",
+		   hapd_iface->conf->bss[0]->iface);
+
+	for (j = 0; j < hapd_iface->num_bss; j++)
+		hostapd_set_security_params(hapd_iface->conf->bss[j], 1);
+	if (hostapd_config_check(hapd_iface->conf, 1) < 0) {
+		wpa_printf(MSG_INFO, "Invalid configuration - cannot enable");
+		return -1;
+	}
+
+	if (hapd_iface->interfaces == NULL ||
+	    hapd_iface->interfaces->driver_init == NULL ||
+	    hapd_iface->interfaces->driver_init(hapd_iface))
+		return -1;
+
+	if (hostapd_setup_interface(hapd_iface)) {
+		hostapd_deinit_driver(hapd_iface->bss[0]->driver,
+				      hapd_iface->bss[0]->drv_priv,
+				      hapd_iface);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int hostapd_reload_iface(struct hostapd_iface *hapd_iface)
+{
+	size_t j;
+
+	wpa_printf(MSG_DEBUG, "Reload interface %s",
+		   hapd_iface->conf->bss[0]->iface);
+	for (j = 0; j < hapd_iface->num_bss; j++)
+		hostapd_set_security_params(hapd_iface->conf->bss[j], 1);
+	if (hostapd_config_check(hapd_iface->conf, 1) < 0) {
+		wpa_printf(MSG_ERROR, "Updated configuration is invalid");
+		return -1;
+	}
+	hostapd_clear_old(hapd_iface);
+	for (j = 0; j < hapd_iface->num_bss; j++)
+		hostapd_reload_bss(hapd_iface->bss[j]);
+
+	return 0;
+}
+
+
+int hostapd_reload_bss_only(struct hostapd_data *bss)
+{
+
+	wpa_printf(MSG_DEBUG, "Reload BSS %s", bss->conf->iface);
+	hostapd_set_security_params(bss->conf, 1);
+	if (hostapd_config_check(bss->iconf, 1) < 0) {
+		wpa_printf(MSG_ERROR, "Updated BSS configuration is invalid");
+		return -1;
+	}
+	hostapd_clear_old_bss(bss);
+	hostapd_reload_bss(bss);
+	return 0;
+}
+
+
+int hostapd_disable_iface(struct hostapd_iface *hapd_iface)
+{
+	size_t j;
+	const struct wpa_driver_ops *driver;
+	void *drv_priv;
+
+	if (hapd_iface == NULL)
+		return -1;
+
+	if (hapd_iface->disable_iface_cb)
+		return hapd_iface->disable_iface_cb(hapd_iface);
+
+	if (hapd_iface->bss[0]->drv_priv == NULL) {
+		wpa_printf(MSG_INFO, "Interface %s already disabled",
+			   hapd_iface->conf->bss[0]->iface);
+		return -1;
+	}
+
+	wpa_msg(hapd_iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
+	driver = hapd_iface->bss[0]->driver;
+	drv_priv = hapd_iface->bss[0]->drv_priv;
+
+	hapd_iface->driver_ap_teardown =
+		!!(hapd_iface->drv_flags &
+		   WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
+
+#ifdef NEED_AP_MLME
+	for (j = 0; j < hapd_iface->num_bss; j++)
+		hostapd_cleanup_cs_params(hapd_iface->bss[j]);
+#endif /* NEED_AP_MLME */
+
+	/* same as hostapd_interface_deinit without deinitializing ctrl-iface */
+	for (j = 0; j < hapd_iface->num_bss; j++) {
+		struct hostapd_data *hapd = hapd_iface->bss[j];
+		hostapd_bss_deinit_no_free(hapd);
+		hostapd_free_hapd_data(hapd);
+	}
+
+	hostapd_deinit_driver(driver, drv_priv, hapd_iface);
+
+	/* From hostapd_cleanup_iface: These were initialized in
+	 * hostapd_setup_interface and hostapd_setup_interface_complete
+	 */
+	hostapd_cleanup_iface_partial(hapd_iface);
+
+	wpa_printf(MSG_DEBUG, "Interface %s disabled",
+		   hapd_iface->bss[0]->conf->iface);
+	hostapd_set_state(hapd_iface, HAPD_IFACE_DISABLED);
+	return 0;
+}
+
+
+static struct hostapd_iface *
+hostapd_iface_alloc(struct hapd_interfaces *interfaces)
+{
+	struct hostapd_iface **iface, *hapd_iface;
+
+	iface = os_realloc_array(interfaces->iface, interfaces->count + 1,
+				 sizeof(struct hostapd_iface *));
+	if (iface == NULL)
+		return NULL;
+	interfaces->iface = iface;
+	hapd_iface = interfaces->iface[interfaces->count] =
+		hostapd_alloc_iface();
+	if (hapd_iface == NULL) {
+		wpa_printf(MSG_ERROR, "%s: Failed to allocate memory for "
+			   "the interface", __func__);
+		return NULL;
+	}
+	interfaces->count++;
+	hapd_iface->interfaces = interfaces;
+
+	return hapd_iface;
+}
+
+
+static struct hostapd_config *
+hostapd_config_alloc(struct hapd_interfaces *interfaces, const char *ifname,
+		     const char *ctrl_iface, const char *driver)
+{
+	struct hostapd_bss_config *bss;
+	struct hostapd_config *conf;
+
+	/* Allocates memory for bss and conf */
+	conf = hostapd_config_defaults();
+	if (conf == NULL) {
+		 wpa_printf(MSG_ERROR, "%s: Failed to allocate memory for "
+				"configuration", __func__);
+		 return NULL;
+	}
+
+	if (driver) {
+		int j;
+
+		for (j = 0; wpa_drivers[j]; j++) {
+			if (os_strcmp(driver, wpa_drivers[j]->name) == 0) {
+				conf->driver = wpa_drivers[j];
+				goto skip;
+			}
+		}
+
+		wpa_printf(MSG_ERROR,
+			   "Invalid/unknown driver '%s' - registering the default driver",
+			   driver);
+	}
+
+	conf->driver = wpa_drivers[0];
+	if (conf->driver == NULL) {
+		wpa_printf(MSG_ERROR, "No driver wrappers registered!");
+		hostapd_config_free(conf);
+		return NULL;
+	}
+
+skip:
+	bss = conf->last_bss = conf->bss[0];
+
+	os_strlcpy(bss->iface, ifname, sizeof(bss->iface));
+	bss->ctrl_interface = os_strdup(ctrl_iface);
+	if (bss->ctrl_interface == NULL) {
+		hostapd_config_free(conf);
+		return NULL;
+	}
+
+	/* Reading configuration file skipped, will be done in SET!
+	 * From reading the configuration till the end has to be done in
+	 * SET
+	 */
+	return conf;
+}
+
+
+static int hostapd_data_alloc(struct hostapd_iface *hapd_iface,
+			      struct hostapd_config *conf)
+{
+	size_t i;
+	struct hostapd_data *hapd;
+
+	hapd_iface->bss = os_calloc(conf->num_bss,
+				    sizeof(struct hostapd_data *));
+	if (hapd_iface->bss == NULL)
+		return -1;
+
+	for (i = 0; i < conf->num_bss; i++) {
+		hapd = hapd_iface->bss[i] =
+			hostapd_alloc_bss_data(hapd_iface, conf, conf->bss[i]);
+		if (hapd == NULL) {
+			while (i > 0) {
+				i--;
+				os_free(hapd_iface->bss[i]);
+				hapd_iface->bss[i] = NULL;
+			}
+			os_free(hapd_iface->bss);
+			hapd_iface->bss = NULL;
+			return -1;
+		}
+		hapd->msg_ctx = hapd;
+	}
+
+	hapd_iface->conf = conf;
+	hapd_iface->num_bss = conf->num_bss;
+
+	return 0;
+}
+
+
+int hostapd_add_iface(struct hapd_interfaces *interfaces, char *buf)
+{
+	struct hostapd_config *conf = NULL;
+	struct hostapd_iface *hapd_iface = NULL, *new_iface = NULL;
+	struct hostapd_data *hapd;
+	char *ptr;
+	size_t i, j;
+	const char *conf_file = NULL, *phy_name = NULL;
+
+	if (os_strncmp(buf, "bss_config=", 11) == 0) {
+		char *pos;
+		phy_name = buf + 11;
+		pos = os_strchr(phy_name, ':');
+		if (!pos)
+			return -1;
+		*pos++ = '\0';
+		conf_file = pos;
+		if (!os_strlen(conf_file))
+			return -1;
+
+		hapd_iface = hostapd_interface_init_bss(interfaces, phy_name,
+							conf_file, 0);
+		if (!hapd_iface)
+			return -1;
+		for (j = 0; j < interfaces->count; j++) {
+			if (interfaces->iface[j] == hapd_iface)
+				break;
+		}
+		if (j == interfaces->count) {
+			struct hostapd_iface **tmp;
+			tmp = os_realloc_array(interfaces->iface,
+					       interfaces->count + 1,
+					       sizeof(struct hostapd_iface *));
+			if (!tmp) {
+				hostapd_interface_deinit_free(hapd_iface);
+				return -1;
+			}
+			interfaces->iface = tmp;
+			interfaces->iface[interfaces->count++] = hapd_iface;
+			new_iface = hapd_iface;
+		}
+
+		if (new_iface) {
+			if (interfaces->driver_init(hapd_iface))
+				goto fail;
+
+			if (hostapd_setup_interface(hapd_iface)) {
+				hostapd_deinit_driver(
+					hapd_iface->bss[0]->driver,
+					hapd_iface->bss[0]->drv_priv,
+					hapd_iface);
+				goto fail;
+			}
+		} else {
+			/* Assign new BSS with bss[0]'s driver info */
+			hapd = hapd_iface->bss[hapd_iface->num_bss - 1];
+			hapd->driver = hapd_iface->bss[0]->driver;
+			hapd->drv_priv = hapd_iface->bss[0]->drv_priv;
+			os_memcpy(hapd->own_addr, hapd_iface->bss[0]->own_addr,
+				  ETH_ALEN);
+
+			if (start_ctrl_iface_bss(hapd) < 0 ||
+			    (hapd_iface->state == HAPD_IFACE_ENABLED &&
+			     hostapd_setup_bss(hapd, -1, true))) {
+				hostapd_cleanup(hapd);
+				hapd_iface->bss[hapd_iface->num_bss - 1] = NULL;
+				hapd_iface->conf->num_bss--;
+				hapd_iface->num_bss--;
+				wpa_printf(MSG_DEBUG, "%s: free hapd %p %s",
+					   __func__, hapd, hapd->conf->iface);
+				hostapd_config_free_bss(hapd->conf);
+				hapd->conf = NULL;
+				os_free(hapd);
+				return -1;
+			}
+		}
+		hostapd_owe_update_trans(hapd_iface);
+		return 0;
+	}
+
+	ptr = os_strchr(buf, ' ');
+	if (ptr == NULL)
+		return -1;
+	*ptr++ = '\0';
+
+	if (os_strncmp(ptr, "config=", 7) == 0)
+		conf_file = ptr + 7;
+
+	for (i = 0; i < interfaces->count; i++) {
+		bool mld_ap = false;
+
+#ifdef CONFIG_IEEE80211BE
+		mld_ap = interfaces->iface[i]->conf->bss[0]->mld_ap;
+#endif /* CONFIG_IEEE80211BE */
+
+		if (!os_strcmp(interfaces->iface[i]->conf->bss[0]->iface,
+			       buf) && !mld_ap) {
+			wpa_printf(MSG_INFO, "Cannot add interface - it "
+				   "already exists");
+			return -1;
+		}
+	}
+
+	hapd_iface = hostapd_iface_alloc(interfaces);
+	if (hapd_iface == NULL) {
+		wpa_printf(MSG_ERROR, "%s: Failed to allocate memory "
+			   "for interface", __func__);
+		goto fail;
+	}
+	new_iface = hapd_iface;
+
+	if (conf_file && interfaces->config_read_cb) {
+		conf = interfaces->config_read_cb(conf_file);
+		if (conf && conf->bss)
+			os_strlcpy(conf->bss[0]->iface, buf,
+				   sizeof(conf->bss[0]->iface));
+	} else {
+		char *driver = os_strchr(ptr, ' ');
+
+		if (driver)
+			*driver++ = '\0';
+		conf = hostapd_config_alloc(interfaces, buf, ptr, driver);
+	}
+
+	if (conf == NULL || conf->bss == NULL) {
+		wpa_printf(MSG_ERROR, "%s: Failed to allocate memory "
+			   "for configuration", __func__);
+		goto fail;
+	}
+
+	if (hostapd_data_alloc(hapd_iface, conf) < 0) {
+		wpa_printf(MSG_ERROR, "%s: Failed to allocate memory "
+			   "for hostapd", __func__);
+		goto fail;
+	}
+	conf = NULL;
+
+	if (start_ctrl_iface(hapd_iface) < 0)
+		goto fail;
+
+	wpa_printf(MSG_INFO, "Add interface '%s'",
+		   hapd_iface->conf->bss[0]->iface);
+
+	return 0;
+
+fail:
+	if (conf)
+		hostapd_config_free(conf);
+	if (hapd_iface) {
+		if (hapd_iface->bss) {
+			for (i = 0; i < hapd_iface->num_bss; i++) {
+				hapd = hapd_iface->bss[i];
+				if (!hapd)
+					continue;
+				if (hapd_iface->interfaces &&
+				    hapd_iface->interfaces->ctrl_iface_deinit)
+					hapd_iface->interfaces->
+						ctrl_iface_deinit(hapd);
+				wpa_printf(MSG_DEBUG, "%s: free hapd %p (%s)",
+					   __func__, hapd_iface->bss[i],
+					   hapd->conf->iface);
+				hostapd_cleanup(hapd);
+				os_free(hapd);
+				hapd_iface->bss[i] = NULL;
+			}
+			os_free(hapd_iface->bss);
+			hapd_iface->bss = NULL;
+		}
+		if (new_iface) {
+			interfaces->count--;
+			interfaces->iface[interfaces->count] = NULL;
+		}
+		hostapd_cleanup_iface(hapd_iface);
+	}
+	return -1;
+}
+
+
+static int hostapd_remove_bss(struct hostapd_iface *iface, unsigned int idx)
+{
+	size_t i;
+
+	wpa_printf(MSG_INFO, "Remove BSS '%s'", iface->conf->bss[idx]->iface);
+
+	/* Remove hostapd_data only if it has already been initialized */
+	if (idx < iface->num_bss) {
+		struct hostapd_data *hapd = iface->bss[idx];
+
+		hostapd_bss_deinit(hapd);
+		wpa_printf(MSG_DEBUG, "%s: free hapd %p (%s)",
+			   __func__, hapd, hapd->conf->iface);
+		hostapd_config_free_bss(hapd->conf);
+		hapd->conf = NULL;
+		os_free(hapd);
+
+		iface->num_bss--;
+
+		for (i = idx; i < iface->num_bss; i++)
+			iface->bss[i] = iface->bss[i + 1];
+	} else {
+		hostapd_config_free_bss(iface->conf->bss[idx]);
+		iface->conf->bss[idx] = NULL;
+	}
+
+	iface->conf->num_bss--;
+	for (i = idx; i < iface->conf->num_bss; i++)
+		iface->conf->bss[i] = iface->conf->bss[i + 1];
+
+	return 0;
+}
+
+
+int hostapd_remove_iface(struct hapd_interfaces *interfaces, char *buf)
+{
+	struct hostapd_iface *hapd_iface;
+	size_t i, j, k = 0;
+
+	for (i = 0; i < interfaces->count; i++) {
+		hapd_iface = interfaces->iface[i];
+		if (hapd_iface == NULL)
+			return -1;
+		if (!os_strcmp(hapd_iface->conf->bss[0]->iface, buf)) {
+			wpa_printf(MSG_INFO, "Remove interface '%s'", buf);
+			hapd_iface->driver_ap_teardown =
+				!!(hapd_iface->drv_flags &
+				   WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
+
+			hostapd_interface_deinit_free(hapd_iface);
+			k = i;
+			while (k < (interfaces->count - 1)) {
+				interfaces->iface[k] =
+					interfaces->iface[k + 1];
+				k++;
+			}
+			interfaces->count--;
+			return 0;
+		}
+
+		for (j = 0; j < hapd_iface->conf->num_bss; j++) {
+			if (!os_strcmp(hapd_iface->conf->bss[j]->iface, buf)) {
+				hapd_iface->driver_ap_teardown =
+					!(hapd_iface->drv_flags &
+					  WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
+				return hostapd_remove_bss(hapd_iface, j);
+			}
+		}
+	}
+	return -1;
+}
+
+
+/**
+ * hostapd_new_assoc_sta - Notify that a new station associated with the AP
+ * @hapd: Pointer to BSS data
+ * @sta: Pointer to the associated STA data
+ * @reassoc: 1 to indicate this was a re-association; 0 = first association
+ *
+ * This function will be called whenever a station associates with the AP. It
+ * can be called from ieee802_11.c for drivers that export MLME to hostapd and
+ * from drv_callbacks.c based on driver events for drivers that take care of
+ * management frames (IEEE 802.11 authentication and association) internally.
+ */
+void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
+			   int reassoc)
+{
+	if (hapd->tkip_countermeasures) {
+		hostapd_drv_sta_deauth(hapd, sta->addr,
+				       WLAN_REASON_MICHAEL_MIC_FAILURE);
+		return;
+	}
+
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->conf->mld_ap && sta->mld_info.mld_sta &&
+	    sta->mld_assoc_link_id != hapd->mld_link_id)
+		return;
+#endif /* CONFIG_IEEE80211BE */
+
+	ap_sta_clear_disconnect_timeouts(hapd, sta);
+	sta->post_csa_sa_query = 0;
+
+#ifdef CONFIG_P2P
+	if (sta->p2p_ie == NULL && !sta->no_p2p_set) {
+		sta->no_p2p_set = 1;
+		hapd->num_sta_no_p2p++;
+		if (hapd->num_sta_no_p2p == 1)
+			hostapd_p2p_non_p2p_sta_connected(hapd);
+	}
+#endif /* CONFIG_P2P */
+
+	airtime_policy_new_sta(hapd, sta);
+
+	/* Start accounting here, if IEEE 802.1X and WPA are not used.
+	 * IEEE 802.1X/WPA code will start accounting after the station has
+	 * been authorized. */
+	if (!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen) {
+		ap_sta_set_authorized(hapd, sta, 1);
+		os_get_reltime(&sta->connected_time);
+		accounting_sta_start(hapd, sta);
+	}
+
+	/* Start IEEE 802.1X authentication process for new stations */
+	ieee802_1x_new_station(hapd, sta);
+	if (reassoc) {
+		if (sta->auth_alg != WLAN_AUTH_FT &&
+		    sta->auth_alg != WLAN_AUTH_FILS_SK &&
+		    sta->auth_alg != WLAN_AUTH_FILS_SK_PFS &&
+		    sta->auth_alg != WLAN_AUTH_FILS_PK &&
+		    !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)))
+			wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH);
+	} else
+		wpa_auth_sta_associated(hapd->wpa_auth, sta->wpa_sm);
+
+	if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED) {
+		if (eloop_cancel_timeout(ap_handle_timer, hapd, sta) > 0) {
+			wpa_printf(MSG_DEBUG,
+				   "%s: %s: canceled wired ap_handle_timer timeout for "
+				   MACSTR,
+				   hapd->conf->iface, __func__,
+				   MAC2STR(sta->addr));
+		}
+	} else if (!(hapd->iface->drv_flags &
+		     WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) {
+		wpa_printf(MSG_DEBUG,
+			   "%s: %s: reschedule ap_handle_timer timeout for "
+			   MACSTR " (%d seconds - ap_max_inactivity)",
+			   hapd->conf->iface, __func__, MAC2STR(sta->addr),
+			   hapd->conf->ap_max_inactivity);
+		eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+		eloop_register_timeout(hapd->conf->ap_max_inactivity, 0,
+				       ap_handle_timer, hapd, sta);
+	}
+
+#ifdef CONFIG_MACSEC
+	if (hapd->conf->wpa_key_mgmt == WPA_KEY_MGMT_NONE &&
+	    hapd->conf->mka_psk_set)
+		ieee802_1x_create_preshared_mka_hapd(hapd, sta);
+	else
+		ieee802_1x_alloc_kay_sm_hapd(hapd, sta);
+#endif /* CONFIG_MACSEC */
+}
+
+
+const char * hostapd_state_text(enum hostapd_iface_state s)
+{
+	switch (s) {
+	case HAPD_IFACE_UNINITIALIZED:
+		return "UNINITIALIZED";
+	case HAPD_IFACE_DISABLED:
+		return "DISABLED";
+	case HAPD_IFACE_COUNTRY_UPDATE:
+		return "COUNTRY_UPDATE";
+	case HAPD_IFACE_ACS:
+		return "ACS";
+	case HAPD_IFACE_HT_SCAN:
+		return "HT_SCAN";
+	case HAPD_IFACE_DFS:
+		return "DFS";
+	case HAPD_IFACE_ENABLED:
+		return "ENABLED";
+	case HAPD_IFACE_NO_IR:
+		return "NO_IR";
+	}
+
+	return "UNKNOWN";
+}
+
+
+void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s)
+{
+	wpa_printf(MSG_INFO, "%s: interface state %s->%s",
+		   iface->conf ? iface->conf->bss[0]->iface : "N/A",
+		   hostapd_state_text(iface->state), hostapd_state_text(s));
+	iface->state = s;
+}
+
+
+int hostapd_csa_in_progress(struct hostapd_iface *iface)
+{
+	unsigned int i;
+
+	for (i = 0; i < iface->num_bss; i++)
+		if (iface->bss[i]->csa_in_progress)
+			return 1;
+	return 0;
+}
+
+
+#ifdef NEED_AP_MLME
+
+static void free_beacon_data(struct beacon_data *beacon)
+{
+	os_free(beacon->head);
+	beacon->head = NULL;
+	os_free(beacon->tail);
+	beacon->tail = NULL;
+	os_free(beacon->probe_resp);
+	beacon->probe_resp = NULL;
+	os_free(beacon->beacon_ies);
+	beacon->beacon_ies = NULL;
+	os_free(beacon->proberesp_ies);
+	beacon->proberesp_ies = NULL;
+	os_free(beacon->assocresp_ies);
+	beacon->assocresp_ies = NULL;
+}
+
+
+static int hostapd_build_beacon_data(struct hostapd_data *hapd,
+				     struct beacon_data *beacon)
+{
+	struct wpabuf *beacon_extra, *proberesp_extra, *assocresp_extra;
+	struct wpa_driver_ap_params params;
+	int ret;
+
+	os_memset(beacon, 0, sizeof(*beacon));
+	ret = ieee802_11_build_ap_params(hapd, &params);
+	if (ret < 0)
+		return ret;
+
+	ret = hostapd_build_ap_extra_ies(hapd, &beacon_extra,
+					 &proberesp_extra,
+					 &assocresp_extra);
+	if (ret)
+		goto free_ap_params;
+
+	ret = -1;
+	beacon->head = os_memdup(params.head, params.head_len);
+	if (!beacon->head)
+		goto free_ap_extra_ies;
+
+	beacon->head_len = params.head_len;
+
+	beacon->tail = os_memdup(params.tail, params.tail_len);
+	if (!beacon->tail)
+		goto free_beacon;
+
+	beacon->tail_len = params.tail_len;
+
+	if (params.proberesp != NULL) {
+		beacon->probe_resp = os_memdup(params.proberesp,
+					       params.proberesp_len);
+		if (!beacon->probe_resp)
+			goto free_beacon;
+
+		beacon->probe_resp_len = params.proberesp_len;
+	}
+
+	/* copy the extra ies */
+	if (beacon_extra) {
+		beacon->beacon_ies = os_memdup(beacon_extra->buf,
+					       wpabuf_len(beacon_extra));
+		if (!beacon->beacon_ies)
+			goto free_beacon;
+
+		beacon->beacon_ies_len = wpabuf_len(beacon_extra);
+	}
+
+	if (proberesp_extra) {
+		beacon->proberesp_ies = os_memdup(proberesp_extra->buf,
+						  wpabuf_len(proberesp_extra));
+		if (!beacon->proberesp_ies)
+			goto free_beacon;
+
+		beacon->proberesp_ies_len = wpabuf_len(proberesp_extra);
+	}
+
+	if (assocresp_extra) {
+		beacon->assocresp_ies = os_memdup(assocresp_extra->buf,
+						  wpabuf_len(assocresp_extra));
+		if (!beacon->assocresp_ies)
+			goto free_beacon;
+
+		beacon->assocresp_ies_len = wpabuf_len(assocresp_extra);
+	}
+
+	ret = 0;
+free_beacon:
+	/* if the function fails, the caller should not free beacon data */
+	if (ret)
+		free_beacon_data(beacon);
+
+free_ap_extra_ies:
+	hostapd_free_ap_extra_ies(hapd, beacon_extra, proberesp_extra,
+				  assocresp_extra);
+free_ap_params:
+	ieee802_11_free_ap_params(&params);
+	return ret;
+}
+
+
+/*
+ * TODO: This flow currently supports only changing channel and width within
+ * the same hw_mode. Any other changes to MAC parameters or provided settings
+ * are not supported.
+ */
+static int hostapd_change_config_freq(struct hostapd_data *hapd,
+				      struct hostapd_config *conf,
+				      struct hostapd_freq_params *params,
+				      struct hostapd_freq_params *old_params)
+{
+	int channel;
+	u8 seg0 = 0, seg1 = 0;
+	struct hostapd_hw_modes *mode;
+
+	if (!params->channel) {
+		/* check if the new channel is supported by hw */
+		params->channel = hostapd_hw_get_channel(hapd, params->freq);
+	}
+
+	channel = params->channel;
+	if (!channel)
+		return -1;
+
+	hostapd_determine_mode(hapd->iface);
+	mode = hapd->iface->current_mode;
+
+	/* if a pointer to old_params is provided we save previous state */
+	if (old_params &&
+	    hostapd_set_freq_params(old_params, conf->hw_mode,
+				    hostapd_hw_get_freq(hapd, conf->channel),
+				    conf->channel, conf->enable_edmg,
+				    conf->edmg_channel, conf->ieee80211n,
+				    conf->ieee80211ac, conf->ieee80211ax,
+				    conf->ieee80211be, conf->secondary_channel,
+				    hostapd_get_oper_chwidth(conf),
+				    hostapd_get_oper_centr_freq_seg0_idx(conf),
+				    hostapd_get_oper_centr_freq_seg1_idx(conf),
+				    conf->vht_capab,
+				    mode ? &mode->he_capab[IEEE80211_MODE_AP] :
+				    NULL,
+				    mode ? &mode->eht_capab[IEEE80211_MODE_AP] :
+				    NULL))
+		return -1;
+
+	switch (params->bandwidth) {
+	case 0:
+	case 20:
+		conf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
+		break;
+	case 40:
+	case 80:
+	case 160:
+	case 320:
+		conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
+		break;
+	default:
+		return -1;
+	}
+
+	switch (params->bandwidth) {
+	case 0:
+	case 20:
+	case 40:
+		hostapd_set_oper_chwidth(conf, CONF_OPER_CHWIDTH_USE_HT);
+		break;
+	case 80:
+		if (params->center_freq2)
+			hostapd_set_oper_chwidth(conf,
+						 CONF_OPER_CHWIDTH_80P80MHZ);
+		else
+			hostapd_set_oper_chwidth(conf,
+						 CONF_OPER_CHWIDTH_80MHZ);
+		break;
+	case 160:
+		hostapd_set_oper_chwidth(conf, CONF_OPER_CHWIDTH_160MHZ);
+		break;
+	case 320:
+		hostapd_set_oper_chwidth(conf, CONF_OPER_CHWIDTH_320MHZ);
+		break;
+	default:
+		return -1;
+	}
+
+	conf->channel = channel;
+	conf->ieee80211n = params->ht_enabled;
+	conf->ieee80211ac = params->vht_enabled;
+	conf->secondary_channel = params->sec_channel_offset;
+	ieee80211_freq_to_chan(params->center_freq1,
+			       &seg0);
+	ieee80211_freq_to_chan(params->center_freq2,
+			       &seg1);
+	hostapd_set_oper_centr_freq_seg0_idx(conf, seg0);
+	hostapd_set_oper_centr_freq_seg1_idx(conf, seg1);
+
+	/* TODO: maybe call here hostapd_config_check here? */
+
+	return 0;
+}
+
+
+static int hostapd_fill_csa_settings(struct hostapd_data *hapd,
+				     struct csa_settings *settings)
+{
+	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));
+	if (!iface || !iface->freq || hapd->csa_in_progress)
+		return -1;
+
+	switch (settings->freq_params.bandwidth) {
+	case 80:
+		if (settings->freq_params.center_freq2)
+			bandwidth = CONF_OPER_CHWIDTH_80P80MHZ;
+		else
+			bandwidth = CONF_OPER_CHWIDTH_80MHZ;
+		break;
+	case 160:
+		bandwidth = CONF_OPER_CHWIDTH_160MHZ;
+		break;
+	case 320:
+		bandwidth = CONF_OPER_CHWIDTH_320MHZ;
+		break;
+	default:
+		bandwidth = CONF_OPER_CHWIDTH_USE_HT;
+		break;
+	}
+
+	if (ieee80211_freq_to_channel_ext(
+		    settings->freq_params.freq,
+		    settings->freq_params.sec_channel_offset,
+		    bandwidth,
+		    &hapd->iface->cs_oper_class,
+		    &chan) == NUM_HOSTAPD_MODES) {
+		wpa_printf(MSG_DEBUG,
+			   "invalid frequency for channel switch (freq=%d, sec_channel_offset=%d, vht_enabled=%d, he_enabled=%d, eht_enabled=%d)",
+			   settings->freq_params.freq,
+			   settings->freq_params.sec_channel_offset,
+			   settings->freq_params.vht_enabled,
+			   settings->freq_params.he_enabled,
+			   settings->freq_params.eht_enabled);
+		return -1;
+	}
+
+	settings->freq_params.channel = chan;
+
+	ret = hostapd_change_config_freq(iface->bss[0], iface->conf,
+					 &settings->freq_params,
+					 &old_freq);
+	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);
+
+	if (ret)
+		return ret;
+
+	/* set channel switch parameters for csa ie */
+	hapd->cs_freq_params = settings->freq_params;
+	hapd->cs_count = settings->cs_count;
+	hapd->cs_block_tx = settings->block_tx;
+
+	ret = hostapd_build_beacon_data(hapd, &settings->beacon_csa);
+	if (ret) {
+		free_beacon_data(&settings->beacon_after);
+		return ret;
+	}
+
+	settings->counter_offset_beacon[0] = hapd->cs_c_off_beacon;
+	settings->counter_offset_presp[0] = hapd->cs_c_off_proberesp;
+	settings->counter_offset_beacon[1] = hapd->cs_c_off_ecsa_beacon;
+	settings->counter_offset_presp[1] = hapd->cs_c_off_ecsa_proberesp;
+
+	return 0;
+}
+
+
+void hostapd_cleanup_cs_params(struct hostapd_data *hapd)
+{
+	os_memset(&hapd->cs_freq_params, 0, sizeof(hapd->cs_freq_params));
+	hapd->cs_count = 0;
+	hapd->cs_block_tx = 0;
+	hapd->cs_c_off_beacon = 0;
+	hapd->cs_c_off_proberesp = 0;
+	hapd->csa_in_progress = 0;
+	hapd->cs_c_off_ecsa_beacon = 0;
+	hapd->cs_c_off_ecsa_proberesp = 0;
+}
+
+
+void hostapd_chan_switch_config(struct hostapd_data *hapd,
+				struct hostapd_freq_params *freq_params)
+{
+	if (freq_params->eht_enabled)
+		hapd->iconf->ch_switch_eht_config |= CH_SWITCH_EHT_ENABLED;
+	else
+		hapd->iconf->ch_switch_eht_config |= CH_SWITCH_EHT_DISABLED;
+
+	if (freq_params->he_enabled)
+		hapd->iconf->ch_switch_he_config |= CH_SWITCH_HE_ENABLED;
+	else
+		hapd->iconf->ch_switch_he_config |= CH_SWITCH_HE_DISABLED;
+
+	if (freq_params->vht_enabled)
+		hapd->iconf->ch_switch_vht_config |= CH_SWITCH_VHT_ENABLED;
+	else
+		hapd->iconf->ch_switch_vht_config |= CH_SWITCH_VHT_DISABLED;
+
+	hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_INFO,
+		       "CHAN_SWITCH EHT config 0x%x HE config 0x%x VHT config 0x%x",
+		       hapd->iconf->ch_switch_eht_config,
+		       hapd->iconf->ch_switch_he_config,
+		       hapd->iconf->ch_switch_vht_config);
+}
+
+
+int hostapd_switch_channel(struct hostapd_data *hapd,
+			   struct csa_settings *settings)
+{
+	int ret;
+
+	if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA)) {
+		wpa_printf(MSG_INFO, "CSA is not supported");
+		return -1;
+	}
+
+	ret = hostapd_fill_csa_settings(hapd, settings);
+	if (ret)
+		return ret;
+
+	ret = hostapd_drv_switch_channel(hapd, settings);
+	free_beacon_data(&settings->beacon_csa);
+	free_beacon_data(&settings->beacon_after);
+
+	if (ret) {
+		/* if we failed, clean cs parameters */
+		hostapd_cleanup_cs_params(hapd);
+		return ret;
+	}
+
+	hapd->csa_in_progress = 1;
+	return 0;
+}
+
+
+void
+hostapd_switch_channel_fallback(struct hostapd_iface *iface,
+				const struct hostapd_freq_params *freq_params)
+{
+	int seg0_idx = 0, seg1_idx = 0;
+	enum oper_chan_width bw = CONF_OPER_CHWIDTH_USE_HT;
+
+	wpa_printf(MSG_DEBUG, "Restarting all CSA-related BSSes");
+
+	if (freq_params->center_freq1)
+		seg0_idx = 36 + (freq_params->center_freq1 - 5180) / 5;
+	if (freq_params->center_freq2)
+		seg1_idx = 36 + (freq_params->center_freq2 - 5180) / 5;
+
+	switch (freq_params->bandwidth) {
+	case 0:
+	case 20:
+	case 40:
+		bw = CONF_OPER_CHWIDTH_USE_HT;
+		break;
+	case 80:
+		if (freq_params->center_freq2)
+			bw = CONF_OPER_CHWIDTH_80P80MHZ;
+		else
+			bw = CONF_OPER_CHWIDTH_80MHZ;
+		break;
+	case 160:
+		bw = CONF_OPER_CHWIDTH_160MHZ;
+		break;
+	case 320:
+		bw = CONF_OPER_CHWIDTH_320MHZ;
+		break;
+	default:
+		wpa_printf(MSG_WARNING, "Unknown CSA bandwidth: %d",
+			   freq_params->bandwidth);
+		break;
+	}
+
+	iface->freq = freq_params->freq;
+	iface->conf->channel = freq_params->channel;
+	iface->conf->secondary_channel = freq_params->sec_channel_offset;
+	hostapd_set_oper_centr_freq_seg0_idx(iface->conf, seg0_idx);
+	hostapd_set_oper_centr_freq_seg1_idx(iface->conf, seg1_idx);
+	hostapd_set_oper_chwidth(iface->conf, bw);
+	iface->conf->ieee80211n = freq_params->ht_enabled;
+	iface->conf->ieee80211ac = freq_params->vht_enabled;
+	iface->conf->ieee80211ax = freq_params->he_enabled;
+	iface->conf->ieee80211be = freq_params->eht_enabled;
+
+	/*
+	 * cs_params must not be cleared earlier because the freq_params
+	 * argument may actually point to one of these.
+	 * These params will be cleared during interface disable below.
+	 */
+	hostapd_disable_iface(iface);
+	hostapd_enable_iface(iface);
+}
+
+
+#ifdef CONFIG_IEEE80211AX
+
+void hostapd_cleanup_cca_params(struct hostapd_data *hapd)
+{
+	hapd->cca_count = 0;
+	hapd->cca_color = 0;
+	hapd->cca_c_off_beacon = 0;
+	hapd->cca_c_off_proberesp = 0;
+	hapd->cca_in_progress = false;
+}
+
+
+static int hostapd_fill_cca_settings(struct hostapd_data *hapd,
+				     struct cca_settings *settings)
+{
+	struct hostapd_iface *iface = hapd->iface;
+	u8 old_color;
+	int ret;
+
+	if (!iface || iface->conf->he_op.he_bss_color_disabled)
+		return -1;
+
+	old_color = iface->conf->he_op.he_bss_color;
+	iface->conf->he_op.he_bss_color = hapd->cca_color;
+	ret = hostapd_build_beacon_data(hapd, &settings->beacon_after);
+	if (ret)
+		return ret;
+
+	iface->conf->he_op.he_bss_color = old_color;
+
+	settings->cca_count = hapd->cca_count;
+	settings->cca_color = hapd->cca_color,
+	hapd->cca_in_progress = true;
+
+	ret = hostapd_build_beacon_data(hapd, &settings->beacon_cca);
+	if (ret) {
+		free_beacon_data(&settings->beacon_after);
+		return ret;
+	}
+
+	settings->counter_offset_beacon = hapd->cca_c_off_beacon;
+	settings->counter_offset_presp = hapd->cca_c_off_proberesp;
+
+	return 0;
+}
+
+
+static void hostapd_switch_color_timeout_handler(void *eloop_data,
+						 void *user_ctx)
+{
+	struct hostapd_data *hapd = (struct hostapd_data *) eloop_data;
+	os_time_t delta_t;
+	unsigned int b;
+	int i, r;
+
+	 /* CCA can be triggered once the handler constantly receives
+	  * color collision events to for at least
+	  * DOT11BSS_COLOR_COLLISION_AP_PERIOD (50 s by default). */
+	delta_t = hapd->last_color_collision.sec -
+		hapd->first_color_collision.sec;
+	if (delta_t < DOT11BSS_COLOR_COLLISION_AP_PERIOD)
+		return;
+
+	r = os_random() % HE_OPERATION_BSS_COLOR_MAX;
+	for (i = 0; i < HE_OPERATION_BSS_COLOR_MAX; i++) {
+		if (r && !(hapd->color_collision_bitmap & (1ULL << r)))
+			break;
+
+		r = (r + 1) % HE_OPERATION_BSS_COLOR_MAX;
+	}
+
+	if (i == HE_OPERATION_BSS_COLOR_MAX) {
+		/* There are no free colors so turn BSS coloring off */
+		wpa_printf(MSG_INFO,
+			   "No free colors left, turning off BSS coloring");
+		hapd->iface->conf->he_op.he_bss_color_disabled = 1;
+		hapd->iface->conf->he_op.he_bss_color = os_random() % 63 + 1;
+		for (b = 0; b < hapd->iface->num_bss; b++)
+			ieee802_11_set_beacon(hapd->iface->bss[b]);
+		return;
+	}
+
+	for (b = 0; b < hapd->iface->num_bss; b++) {
+		struct hostapd_data *bss = hapd->iface->bss[b];
+		struct cca_settings settings;
+		int ret;
+
+		hostapd_cleanup_cca_params(bss);
+		bss->cca_color = r;
+		bss->cca_count = 10;
+
+		if (hostapd_fill_cca_settings(bss, &settings)) {
+			hostapd_cleanup_cca_params(bss);
+			continue;
+		}
+
+		ret = hostapd_drv_switch_color(bss, &settings);
+		if (ret)
+			hostapd_cleanup_cca_params(bss);
+
+		free_beacon_data(&settings.beacon_cca);
+		free_beacon_data(&settings.beacon_after);
+	}
+}
+
+
+void hostapd_switch_color(struct hostapd_data *hapd, u64 bitmap)
+{
+	struct os_reltime now;
+
+	if (hapd->cca_in_progress)
+		return;
+
+	if (os_get_reltime(&now))
+		return;
+
+	hapd->color_collision_bitmap = bitmap;
+	hapd->last_color_collision = now;
+
+	if (eloop_is_timeout_registered(hostapd_switch_color_timeout_handler,
+					hapd, NULL))
+		return;
+
+	hapd->first_color_collision = now;
+	/* 10 s window as margin for persistent color collision reporting */
+	eloop_register_timeout(DOT11BSS_COLOR_COLLISION_AP_PERIOD + 10, 0,
+			       hostapd_switch_color_timeout_handler,
+			       hapd, NULL);
+}
+
+#endif /* CONFIG_IEEE80211AX */
+
+#endif /* NEED_AP_MLME */
+
+
+struct hostapd_data * hostapd_get_iface(struct hapd_interfaces *interfaces,
+					const char *ifname)
+{
+	size_t i, j;
+
+	for (i = 0; i < interfaces->count; i++) {
+		struct hostapd_iface *iface = interfaces->iface[i];
+
+		for (j = 0; j < iface->num_bss; j++) {
+			struct hostapd_data *hapd = iface->bss[j];
+
+			if (os_strcmp(ifname, hapd->conf->iface) == 0)
+				return hapd;
+		}
+	}
+
+	return NULL;
+}
+
+
+#ifdef CONFIG_BSS_TRANS_IMPL
+// Periodic check of STA info and trigger BSS transition if necessary
+static void hostapd_sta_bss_transition_check(struct hostapd_data *hapd)
+{
+	struct sta_info *sta;
+	char shellBuf[100];
+	char *s;
+
+	for (sta = hapd->sta_list; sta; sta = sta->next)
+	{
+		if (sta)
+		{
+			s = shellBuf;
+			s += sprintf(s, "./etc/log/bss_tm_trigger.sh ");
+			s += sprintf(s, "%s " MACSTR, hapd->iface->phy, MAC2STR(sta->addr));
+			wpa_printf(MSG_DEBUG,
+				   "hostapd_sta_bss_transition_check, shell command: %s", shellBuf);
+			system(shellBuf);
+			memset(shellBuf, 0, sizeof(shellBuf));
+		}
+	}
+}
+#endif /* CONFIG_BSS_TRANS_IMPL */
+
+
+void hostapd_periodic_iface(struct hostapd_iface *iface)
+{
+	size_t i;
+
+	ap_list_timer(iface);
+
+	for (i = 0; i < iface->num_bss; i++) {
+		struct hostapd_data *hapd = iface->bss[i];
+
+		if (!hapd->started)
+			continue;
+
+#ifndef CONFIG_NO_RADIUS
+		hostapd_acl_expire(hapd);
+#endif /* CONFIG_NO_RADIUS */
+
+#ifdef CONFIG_BSS_TRANS_IMPL
+		// Add periodic BSS transition check for connected STAs by Liangyu Chu
+		if (hapd->conf->bss_transition)
+			hostapd_sta_bss_transition_check(hapd);
+#endif /* CONFIG_BSS_TRANS_IMPL */
+	}
+}
+
+
+#ifdef CONFIG_OCV
+void hostapd_ocv_check_csa_sa_query(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct sta_info *sta;
+
+	wpa_printf(MSG_DEBUG, "OCV: Post-CSA SA Query initiation check");
+
+	for (sta = hapd->sta_list; sta; sta = sta->next) {
+		if (!sta->post_csa_sa_query)
+			continue;
+
+		wpa_printf(MSG_DEBUG, "OCV: OCVC STA " MACSTR
+			   " did not start SA Query after CSA - disconnect",
+			   MAC2STR(sta->addr));
+		ap_sta_disconnect(hapd, sta, sta->addr,
+				  WLAN_REASON_PREV_AUTH_NOT_VALID);
+	}
+}
+#endif /* CONFIG_OCV */
+
+
+#ifdef CONFIG_IEEE80211BE
+struct hostapd_data * hostapd_mld_get_link_bss(struct hostapd_data *hapd,
+					       u8 link_id)
+{
+	unsigned int i;
+
+	for (i = 0; i < hapd->iface->interfaces->count; i++) {
+		struct hostapd_iface *h = hapd->iface->interfaces->iface[i];
+		struct hostapd_data *h_hapd = h->bss[0];
+		struct hostapd_bss_config *hconf = h_hapd->conf;
+
+		if (!hconf->mld_ap || hconf->mld_id != hapd->conf->mld_id)
+			continue;
+
+		if (h_hapd->mld_link_id == link_id)
+			return h_hapd;
+	}
+
+	return NULL;
+}
+#endif /* CONFIG_IEEE80211BE */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/hostapd.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/hostapd.h
new file mode 100644
index 0000000..8c44e52
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/hostapd.h
@@ -0,0 +1,805 @@
+/*
+ * hostapd / Initialization and configuration
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef HOSTAPD_H
+#define HOSTAPD_H
+
+#ifdef CONFIG_SQLITE
+#include <sqlite3.h>
+#endif /* CONFIG_SQLITE */
+
+#include "common/defs.h"
+#include "common/dpp.h"
+#include "utils/list.h"
+#include "ap_config.h"
+#include "drivers/driver.h"
+#include "ubus.h"
+
+#define OCE_STA_CFON_ENABLED(hapd) \
+	((hapd->conf->oce & OCE_STA_CFON) && \
+	 (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_OCE_STA_CFON))
+#define OCE_AP_ENABLED(hapd) \
+	((hapd->conf->oce & OCE_AP) && \
+	 (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_OCE_AP))
+
+struct wpa_ctrl_dst;
+struct radius_server_data;
+struct upnp_wps_device_sm;
+struct hostapd_data;
+struct sta_info;
+struct ieee80211_ht_capabilities;
+struct full_dynamic_vlan;
+enum wps_event;
+union wps_event_data;
+#ifdef CONFIG_MESH
+struct mesh_conf;
+#endif /* CONFIG_MESH */
+
+#ifdef CONFIG_CTRL_IFACE_UDP
+#define CTRL_IFACE_COOKIE_LEN 8
+#endif /* CONFIG_CTRL_IFACE_UDP */
+
+struct hostapd_iface;
+
+struct hapd_interfaces {
+	int (*reload_config)(struct hostapd_iface *iface, int reconf);
+	struct hostapd_config * (*config_read_cb)(const char *config_fname);
+	int (*ctrl_iface_init)(struct hostapd_data *hapd);
+	void (*ctrl_iface_deinit)(struct hostapd_data *hapd);
+	int (*for_each_interface)(struct hapd_interfaces *interfaces,
+				  int (*cb)(struct hostapd_iface *iface,
+					    void *ctx), void *ctx);
+	int (*driver_init)(struct hostapd_iface *iface);
+
+	size_t count;
+	int global_ctrl_sock;
+	struct dl_list global_ctrl_dst;
+	char *global_iface_path;
+	char *global_iface_name;
+#ifndef CONFIG_NATIVE_WINDOWS
+	gid_t ctrl_iface_group;
+#endif /* CONFIG_NATIVE_WINDOWS */
+	struct hostapd_iface **iface;
+
+	size_t terminate_on_error;
+#ifndef CONFIG_NO_VLAN
+	struct dynamic_iface *vlan_priv;
+#endif /* CONFIG_NO_VLAN */
+#ifdef CONFIG_ETH_P_OUI
+	struct dl_list eth_p_oui; /* OUI Extended EtherType handlers */
+#endif /* CONFIG_ETH_P_OUI */
+	int eloop_initialized;
+
+#ifdef CONFIG_DPP
+	struct dpp_global *dpp;
+#ifdef CONFIG_DPP3
+	struct os_reltime dpp_pb_time;
+	struct os_reltime dpp_pb_announce_time;
+	struct dpp_pb_info dpp_pb[DPP_PB_INFO_COUNT];
+	struct dpp_bootstrap_info *dpp_pb_bi;
+	u8 dpp_pb_c_nonce[DPP_MAX_NONCE_LEN];
+	u8 dpp_pb_resp_hash[SHA256_MAC_LEN];
+	struct os_reltime dpp_pb_last_resp;
+	bool dpp_pb_result_indicated;
+	char *dpp_pb_cmd;
+#endif /* CONFIG_DPP3 */
+#endif /* CONFIG_DPP */
+
+#ifdef CONFIG_CTRL_IFACE_UDP
+       unsigned char ctrl_iface_cookie[CTRL_IFACE_COOKIE_LEN];
+#endif /* CONFIG_CTRL_IFACE_UDP */
+	struct ubus_object ubus;
+};
+
+enum hostapd_chan_status {
+	HOSTAPD_CHAN_VALID = 0, /* channel is ready */
+	HOSTAPD_CHAN_INVALID = 1, /* no usable channel found */
+	HOSTAPD_CHAN_ACS = 2, /* ACS work being performed */
+	HOSTAPD_CHAN_INVALID_NO_IR = 3, /* channel invalid due to AFC NO IR */
+};
+
+struct hostapd_probereq_cb {
+	int (*cb)(void *ctx, const u8 *sa, const u8 *da, const u8 *bssid,
+		  const u8 *ie, size_t ie_len, int ssi_signal);
+	void *ctx;
+};
+
+#define HOSTAPD_RATE_BASIC 0x00000001
+
+struct hostapd_rate_data {
+	int rate; /* rate in 100 kbps */
+	int flags; /* HOSTAPD_RATE_ flags */
+};
+
+struct hostapd_frame_info {
+	unsigned int freq;
+	u32 channel;
+	u32 datarate;
+	int ssi_signal; /* dBm */
+};
+
+enum wps_status {
+	WPS_STATUS_SUCCESS = 1,
+	WPS_STATUS_FAILURE
+};
+
+enum pbc_status {
+	WPS_PBC_STATUS_DISABLE,
+	WPS_PBC_STATUS_ACTIVE,
+	WPS_PBC_STATUS_TIMEOUT,
+	WPS_PBC_STATUS_OVERLAP
+};
+
+struct wps_stat {
+	enum wps_status status;
+	enum wps_error_indication failure_reason;
+	enum pbc_status pbc_status;
+	u8 peer_addr[ETH_ALEN];
+};
+
+struct hostapd_neighbor_entry {
+	struct dl_list list;
+	u8 bssid[ETH_ALEN];
+	struct wpa_ssid_value ssid;
+	struct wpabuf *nr;
+	struct wpabuf *lci;
+	struct wpabuf *civic;
+	/* LCI update time */
+	struct os_time lci_date;
+	int stationary;
+	u32 short_ssid;
+	u8 bss_parameters;
+};
+
+struct hostapd_sae_commit_queue {
+	struct dl_list list;
+	int rssi;
+	size_t len;
+	u8 msg[];
+};
+
+/**
+ * struct hostapd_openwrt_stats - OpenWrt custom STA/AP statistics
+ */
+struct hostapd_openwrt_stats {
+	struct {
+		u64 neighbor_report_tx;
+	} rrm;
+
+	struct {
+		u64 bss_transition_query_rx;
+		u64 bss_transition_request_tx;
+		u64 bss_transition_response_rx;
+	} wnm;
+};
+
+/**
+ * struct hostapd_data - hostapd per-BSS data structure
+ */
+struct hostapd_data {
+	struct hostapd_iface *iface;
+	struct hostapd_config *iconf;
+	struct hostapd_bss_config *conf;
+	struct hostapd_ubus_bss ubus;
+	char *config_id;
+	int interface_added; /* virtual interface added for this BSS */
+	unsigned int started:1;
+	unsigned int disabled:1;
+	unsigned int reenable_beacon:1;
+
+	u8 own_addr[ETH_ALEN];
+	u8 mld_addr[ETH_ALEN];
+	u8 mld_link_id;
+	/* Used for mld_link_id assignment - valid on the first MLD BSS only */
+	u8 mld_next_link_id;
+
+	struct hostapd_data *mld_first_bss;
+
+	/* OpenWrt specific statistics */
+	struct hostapd_openwrt_stats openwrt_stats;
+
+	int num_sta; /* number of entries in sta_list */
+	struct sta_info *sta_list; /* STA info list head */
+#define STA_HASH_SIZE 256
+#define STA_HASH(sta) (sta[5])
+	struct sta_info *sta_hash[STA_HASH_SIZE];
+
+	/*
+	 * Bitfield for indicating which AIDs are allocated. Only AID values
+	 * 1-2007 are used and as such, the bit at index 0 corresponds to AID
+	 * 1.
+	 */
+#define AID_WORDS ((2008 + 31) / 32)
+	u32 sta_aid[AID_WORDS];
+
+	const struct wpa_driver_ops *driver;
+	void *drv_priv;
+
+	void (*new_assoc_sta_cb)(struct hostapd_data *hapd,
+				 struct sta_info *sta, int reassoc);
+
+	void *msg_ctx; /* ctx for wpa_msg() calls */
+	void *msg_ctx_parent; /* parent interface ctx for wpa_msg() calls */
+
+	struct radius_client_data *radius;
+	u64 acct_session_id;
+	struct radius_das_data *radius_das;
+
+	struct hostapd_cached_radius_acl *acl_cache;
+	struct hostapd_acl_query_data *acl_queries;
+
+	struct wpa_authenticator *wpa_auth;
+	struct eapol_authenticator *eapol_auth;
+	struct eap_config *eap_cfg;
+
+	struct rsn_preauth_interface *preauth_iface;
+	struct os_reltime michael_mic_failure;
+	int michael_mic_failures;
+	int tkip_countermeasures;
+
+	int ctrl_sock;
+	struct dl_list ctrl_dst;
+
+	void *ssl_ctx;
+	void *eap_sim_db_priv;
+	struct crypto_rsa_key *imsi_privacy_key;
+	struct radius_server_data *radius_srv;
+	struct dl_list erp_keys; /* struct eap_server_erp_key */
+
+	int parameter_set_count;
+
+	/* Time Advertisement */
+	u8 time_update_counter;
+	struct wpabuf *time_adv;
+
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+	struct full_dynamic_vlan *full_dynamic_vlan;
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+
+	struct l2_packet_data *l2;
+
+#ifdef CONFIG_IEEE80211R_AP
+	struct dl_list l2_queue;
+	struct dl_list l2_oui_queue;
+	struct eth_p_oui_ctx *oui_pull;
+	struct eth_p_oui_ctx *oui_resp;
+	struct eth_p_oui_ctx *oui_push;
+	struct eth_p_oui_ctx *oui_sreq;
+	struct eth_p_oui_ctx *oui_sresp;
+#endif /* CONFIG_IEEE80211R_AP */
+
+	struct wps_context *wps;
+
+	int beacon_set_done;
+	struct wpabuf *wps_beacon_ie;
+	struct wpabuf *wps_probe_resp_ie;
+#ifdef CONFIG_WPS
+	unsigned int ap_pin_failures;
+	unsigned int ap_pin_failures_consecutive;
+	struct upnp_wps_device_sm *wps_upnp;
+	unsigned int ap_pin_lockout_time;
+
+	struct wps_stat wps_stats;
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_MACSEC
+	struct ieee802_1x_kay *kay;
+#endif /* CONFIG_MACSEC */
+
+	struct hostapd_probereq_cb *probereq_cb;
+	size_t num_probereq_cb;
+
+	void (*public_action_cb)(void *ctx, const u8 *buf, size_t len,
+				 int freq);
+	void *public_action_cb_ctx;
+	void (*public_action_cb2)(void *ctx, const u8 *buf, size_t len,
+				  int freq);
+	void *public_action_cb2_ctx;
+
+	int (*vendor_action_cb)(void *ctx, const u8 *buf, size_t len,
+				int freq);
+	void *vendor_action_cb_ctx;
+
+	void (*wps_reg_success_cb)(void *ctx, const u8 *mac_addr,
+				   const u8 *uuid_e);
+	void *wps_reg_success_cb_ctx;
+
+	void (*wps_event_cb)(void *ctx, enum wps_event event,
+			     union wps_event_data *data);
+	void *wps_event_cb_ctx;
+
+	void (*sta_authorized_cb)(void *ctx, const u8 *mac_addr,
+				  int authorized, const u8 *p2p_dev_addr);
+	void *sta_authorized_cb_ctx;
+
+	void (*setup_complete_cb)(void *ctx);
+	void *setup_complete_cb_ctx;
+
+	void (*new_psk_cb)(void *ctx, const u8 *mac_addr,
+			   const u8 *p2p_dev_addr, const u8 *psk,
+			   size_t psk_len);
+	void *new_psk_cb_ctx;
+
+	/* channel switch parameters */
+	struct hostapd_freq_params cs_freq_params;
+	u8 cs_count;
+	int cs_block_tx;
+	unsigned int cs_c_off_beacon;
+	unsigned int cs_c_off_proberesp;
+	int csa_in_progress;
+	unsigned int cs_c_off_ecsa_beacon;
+	unsigned int cs_c_off_ecsa_proberesp;
+
+#ifdef CONFIG_IEEE80211AX
+	bool cca_in_progress;
+	u8 cca_count;
+	u8 cca_color;
+	unsigned int cca_c_off_beacon;
+	unsigned int cca_c_off_proberesp;
+	struct os_reltime first_color_collision;
+	struct os_reltime last_color_collision;
+	u64 color_collision_bitmap;
+#endif /* CONFIG_IEEE80211AX */
+
+#ifdef CONFIG_P2P
+	struct p2p_data *p2p;
+	struct p2p_group *p2p_group;
+	struct wpabuf *p2p_beacon_ie;
+	struct wpabuf *p2p_probe_resp_ie;
+
+	/* Number of non-P2P association stations */
+	int num_sta_no_p2p;
+
+	/* Periodic NoA (used only when no non-P2P clients in the group) */
+	int noa_enabled;
+	int noa_start;
+	int noa_duration;
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_PROXYARP
+	struct l2_packet_data *sock_dhcp;
+	struct l2_packet_data *sock_ndisc;
+	bool x_snoop_initialized;
+#endif /* CONFIG_PROXYARP */
+#ifdef CONFIG_MESH
+	int num_plinks;
+	int max_plinks;
+	void (*mesh_sta_free_cb)(struct hostapd_data *hapd,
+				 struct sta_info *sta);
+	struct wpabuf *mesh_pending_auth;
+	struct os_reltime mesh_pending_auth_time;
+	u8 mesh_required_peer[ETH_ALEN];
+#endif /* CONFIG_MESH */
+
+#ifdef CONFIG_SQLITE
+	struct hostapd_eap_user tmp_eap_user;
+#endif /* CONFIG_SQLITE */
+
+#ifdef CONFIG_SAE
+
+#define COMEBACK_KEY_SIZE 8
+#define COMEBACK_PENDING_IDX_SIZE 256
+
+	/** Key used for generating SAE anti-clogging tokens */
+	u8 comeback_key[COMEBACK_KEY_SIZE];
+	struct os_reltime last_comeback_key_update;
+	u16 comeback_idx;
+	u16 comeback_pending_idx[COMEBACK_PENDING_IDX_SIZE];
+	int dot11RSNASAERetransPeriod; /* msec */
+	struct dl_list sae_commit_queue; /* struct hostapd_sae_commit_queue */
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_TESTING_OPTIONS
+	unsigned int ext_mgmt_frame_handling:1;
+	unsigned int ext_eapol_frame_io:1;
+
+	struct l2_packet_data *l2_test;
+
+	enum wpa_alg last_gtk_alg;
+	int last_gtk_key_idx;
+	u8 last_gtk[WPA_GTK_MAX_LEN];
+	size_t last_gtk_len;
+
+	enum wpa_alg last_igtk_alg;
+	int last_igtk_key_idx;
+	u8 last_igtk[WPA_IGTK_MAX_LEN];
+	size_t last_igtk_len;
+
+	enum wpa_alg last_bigtk_alg;
+	int last_bigtk_key_idx;
+	u8 last_bigtk[WPA_BIGTK_MAX_LEN];
+	size_t last_bigtk_len;
+
+	bool force_backlog_bytes;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#ifdef CONFIG_MBO
+	unsigned int mbo_assoc_disallow;
+#endif /* CONFIG_MBO */
+
+	struct dl_list nr_db;
+
+	u8 beacon_req_token;
+	u8 lci_req_token;
+	u8 range_req_token;
+	unsigned int lci_req_active:1;
+	unsigned int range_req_active:1;
+
+	int dhcp_sock; /* UDP socket used with the DHCP server */
+
+	struct ptksa_cache *ptksa;
+
+#ifdef CONFIG_DPP
+	int dpp_init_done;
+	struct dpp_authentication *dpp_auth;
+	u8 dpp_allowed_roles;
+	int dpp_qr_mutual;
+	int dpp_auth_ok_on_ack;
+	int dpp_in_response_listen;
+	struct gas_query_ap *gas;
+	struct dpp_pkex *dpp_pkex;
+	struct dpp_bootstrap_info *dpp_pkex_bi;
+	char *dpp_pkex_code;
+	size_t dpp_pkex_code_len;
+	char *dpp_pkex_identifier;
+	enum dpp_pkex_ver dpp_pkex_ver;
+	char *dpp_pkex_auth_cmd;
+	char *dpp_configurator_params;
+	struct os_reltime dpp_last_init;
+	struct os_reltime dpp_init_iter_start;
+	unsigned int dpp_init_max_tries;
+	unsigned int dpp_init_retry_time;
+	unsigned int dpp_resp_wait_time;
+	unsigned int dpp_resp_max_tries;
+	unsigned int dpp_resp_retry_time;
+#ifdef CONFIG_DPP2
+	struct wpabuf *dpp_presence_announcement;
+	struct dpp_bootstrap_info *dpp_chirp_bi;
+	int dpp_chirp_freq;
+	int *dpp_chirp_freqs;
+	int dpp_chirp_iter;
+	int dpp_chirp_round;
+	int dpp_chirp_scan_done;
+	int dpp_chirp_listen;
+	struct os_reltime dpp_relay_last_needs_ctrl;
+#endif /* CONFIG_DPP2 */
+#ifdef CONFIG_TESTING_OPTIONS
+	char *dpp_config_obj_override;
+	char *dpp_discovery_override;
+	char *dpp_groups_override;
+	unsigned int dpp_ignore_netaccesskey_mismatch:1;
+#endif /* CONFIG_TESTING_OPTIONS */
+#endif /* CONFIG_DPP */
+
+#ifdef CONFIG_AIRTIME_POLICY
+	unsigned int num_backlogged_sta;
+	unsigned int airtime_weight;
+#endif /* CONFIG_AIRTIME_POLICY */
+
+	u8 last_1x_eapol_key_replay_counter[8];
+
+#ifdef CONFIG_SQLITE
+	sqlite3 *rad_attr_db;
+#endif /* CONFIG_SQLITE */
+
+#ifdef CONFIG_CTRL_IFACE_UDP
+       unsigned char ctrl_iface_cookie[CTRL_IFACE_COOKIE_LEN];
+#endif /* CONFIG_CTRL_IFACE_UDP */
+};
+
+
+struct hostapd_sta_info {
+	struct dl_list list;
+	u8 addr[ETH_ALEN];
+	struct os_reltime last_seen;
+	int ssi_signal;
+#ifdef CONFIG_TAXONOMY
+	struct wpabuf *probe_ie_taxonomy;
+#endif /* CONFIG_TAXONOMY */
+};
+
+/**
+ * struct hostapd_iface - hostapd per-interface data structure
+ */
+struct hostapd_iface {
+	struct hapd_interfaces *interfaces;
+	void *owner;
+	char *config_fname;
+	struct hostapd_config *conf;
+	char phy[16]; /* Name of the PHY (radio) */
+
+	enum hostapd_iface_state {
+		HAPD_IFACE_UNINITIALIZED,
+		HAPD_IFACE_DISABLED,
+		HAPD_IFACE_COUNTRY_UPDATE,
+		HAPD_IFACE_ACS,
+		HAPD_IFACE_HT_SCAN,
+		HAPD_IFACE_DFS,
+		HAPD_IFACE_NO_IR,
+		HAPD_IFACE_ENABLED
+	} state;
+
+#ifdef CONFIG_MESH
+	struct mesh_conf *mconf;
+#endif /* CONFIG_MESH */
+
+	size_t num_bss;
+	struct hostapd_data **bss;
+
+	unsigned int wait_channel_update:1;
+	unsigned int cac_started:1;
+#ifdef CONFIG_FST
+	struct fst_iface *fst;
+	const struct wpabuf *fst_ies;
+#endif /* CONFIG_FST */
+
+	/*
+	 * When set, indicates that the driver will handle the AP
+	 * teardown: delete global keys, station keys, and stations.
+	 */
+	unsigned int driver_ap_teardown:1;
+
+	/*
+	 * When set, indicates that this interface is part of list of
+	 * interfaces that need to be started together (synchronously).
+	 */
+	unsigned int need_to_start_in_sync:1;
+
+	/* Ready to start but waiting for other interfaces to become ready. */
+	unsigned int ready_to_start_in_sync:1;
+
+	int num_ap; /* number of entries in ap_list */
+#ifdef CONFIG_5G_BW_SWITCH
+	int max_rssi; /* maximum RSSI level of co-channel APs. Added by Liangyu Chu */
+#endif /* CONFIG_5G_BW_SWITCH */
+	struct ap_info *ap_list; /* AP info list head */
+	struct ap_info *ap_hash[STA_HASH_SIZE];
+
+	u64 drv_flags;
+	u64 drv_flags2;
+
+	/*
+	 * A bitmap of supported protocols for probe response offload. See
+	 * struct wpa_driver_capa in driver.h
+	 */
+	unsigned int probe_resp_offloads;
+
+	/* extended capabilities supported by the driver */
+	const u8 *extended_capa, *extended_capa_mask;
+	unsigned int extended_capa_len;
+
+	u16 mld_eml_capa, mld_mld_capa;
+
+	unsigned int drv_max_acl_mac_addrs;
+
+	struct hostapd_hw_modes *hw_features;
+	int num_hw_features;
+	struct hostapd_hw_modes *current_mode;
+	/* Rates that are currently used (i.e., filtered copy of
+	 * current_mode->channels */
+	int num_rates;
+	struct hostapd_rate_data *current_rates;
+	int *basic_rates;
+	int freq;
+
+	/* Background radar configuration */
+	struct {
+		int channel;
+		int secondary_channel;
+		int freq;
+		int centr_freq_seg0_idx;
+		int centr_freq_seg1_idx;
+		/* Main chain is on temporary channel during
+		 * CAC detection on radar offchain.
+		 */
+		unsigned int temp_ch:1;
+		/* CAC started on radar offchain */
+		unsigned int cac_started:1;
+	} radar_background;
+
+	u16 hw_flags;
+
+	/* Number of associated Non-ERP stations (i.e., stations using 802.11b
+	 * in 802.11g BSS) */
+	int num_sta_non_erp;
+
+	/* Number of associated stations that do not support Short Slot Time */
+	int num_sta_no_short_slot_time;
+
+	/* Number of associated stations that do not support Short Preamble */
+	int num_sta_no_short_preamble;
+
+	int olbc; /* Overlapping Legacy BSS Condition */
+
+	/* Number of HT associated stations that do not support greenfield */
+	int num_sta_ht_no_gf;
+
+	/* Number of associated non-HT stations */
+	int num_sta_no_ht;
+
+	/* Number of HT associated stations 20 MHz */
+	int num_sta_ht_20mhz;
+
+	/* Number of HT40 intolerant stations */
+	int num_sta_ht40_intolerant;
+
+	/* Overlapping BSS information */
+	int olbc_ht;
+
+	u16 ht_op_mode;
+
+	/* surveying helpers */
+
+	/* number of channels surveyed */
+	unsigned int chans_surveyed;
+
+	/* lowest observed noise floor in dBm */
+	s8 lowest_nf;
+
+	/* channel utilization calculation */
+	u64 last_channel_time;
+	u64 last_channel_time_busy;
+	u8 channel_utilization;
+
+	unsigned int chan_util_samples_sum;
+	unsigned int chan_util_num_sample_periods;
+	unsigned int chan_util_average;
+
+	/* eCSA IE will be added only if operating class is specified */
+	u8 cs_oper_class;
+
+	unsigned int dfs_cac_ms;
+	struct os_reltime dfs_cac_start;
+
+	/* Latched with the actual secondary channel information and will be
+	 * used while juggling between HT20 and HT40 modes. */
+	int secondary_ch;
+#ifdef CONFIG_5G_BW_SWITCH
+	// Record VHT operation channel bandwidth. Added by Liangyu Chu
+	u8  vht_oper_ch_bw;
+	// Record VHT operation center_freq_seg0_index for 80M bandwidth
+	u8  vht_center_freq_seg0_80m;
+	// Record VHT operation center_freq_seg0_index for 40M bandwidth
+	u8  vht_center_freq_seg0_40m;
+#endif /* CONFIG_5G_BW_SWITCH */
+
+#ifdef CONFIG_ACS
+	unsigned int acs_num_completed_scans;
+#endif /* CONFIG_ACS */
+
+	void (*scan_cb)(struct hostapd_iface *iface);
+	int num_ht40_scan_tries;
+
+	struct dl_list sta_seen; /* struct hostapd_sta_info */
+	unsigned int num_sta_seen;
+
+	u8 dfs_domain;
+#ifdef CONFIG_AIRTIME_POLICY
+	unsigned int airtime_quantum;
+#endif /* CONFIG_AIRTIME_POLICY */
+
+	/* Previous WMM element information */
+	struct hostapd_wmm_ac_params prev_wmm[WMM_AC_NUM];
+
+	/* Maximum number of interfaces supported for MBSSID advertisement */
+	unsigned int mbssid_max_interfaces;
+	/* Maximum profile periodicity for enhanced MBSSID advertisement */
+	unsigned int ema_max_periodicity;
+
+	int (*enable_iface_cb)(struct hostapd_iface *iface);
+	int (*disable_iface_cb)(struct hostapd_iface *iface);
+
+	/* Configured freq of interface is NO_IR */
+	bool is_no_ir;
+};
+
+/* hostapd.c */
+int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
+			       int (*cb)(struct hostapd_iface *iface,
+					 void *ctx), void *ctx);
+int hostapd_reload_config(struct hostapd_iface *iface, int reconf);
+void hostapd_reconfig_encryption(struct hostapd_data *hapd);
+struct hostapd_data *
+hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
+		       struct hostapd_config *conf,
+		       struct hostapd_bss_config *bss);
+int hostapd_setup_interface(struct hostapd_iface *iface);
+int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err);
+void hostapd_set_own_neighbor_report(struct hostapd_data *hapd);
+void hostapd_interface_deinit(struct hostapd_iface *iface);
+void hostapd_interface_free(struct hostapd_iface *iface);
+struct hostapd_iface * hostapd_alloc_iface(void);
+struct hostapd_iface * hostapd_init(struct hapd_interfaces *interfaces,
+				    const char *config_file);
+struct hostapd_iface *
+hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy,
+			   const char *config_fname, int debug);
+void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
+			   int reassoc);
+void hostapd_interface_deinit_free(struct hostapd_iface *iface);
+int hostapd_enable_iface(struct hostapd_iface *hapd_iface);
+int hostapd_reload_iface(struct hostapd_iface *hapd_iface);
+int hostapd_reload_bss_only(struct hostapd_data *bss);
+int hostapd_disable_iface(struct hostapd_iface *hapd_iface);
+void hostapd_bss_deinit_no_free(struct hostapd_data *hapd);
+void hostapd_free_hapd_data(struct hostapd_data *hapd);
+void hostapd_cleanup_iface_partial(struct hostapd_iface *iface);
+int hostapd_add_iface(struct hapd_interfaces *ifaces, char *buf);
+int hostapd_remove_iface(struct hapd_interfaces *ifaces, char *buf);
+void hostapd_channel_list_updated(struct hostapd_iface *iface, int initiator);
+void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s);
+const char * hostapd_state_text(enum hostapd_iface_state s);
+int hostapd_csa_in_progress(struct hostapd_iface *iface);
+void hostapd_chan_switch_config(struct hostapd_data *hapd,
+				struct hostapd_freq_params *freq_params);
+int hostapd_switch_channel(struct hostapd_data *hapd,
+			   struct csa_settings *settings);
+void
+hostapd_switch_channel_fallback(struct hostapd_iface *iface,
+				const struct hostapd_freq_params *freq_params);
+void hostapd_cleanup_cs_params(struct hostapd_data *hapd);
+void hostapd_periodic_iface(struct hostapd_iface *iface);
+int hostapd_owe_trans_get_info(struct hostapd_data *hapd);
+void hostapd_ocv_check_csa_sa_query(void *eloop_ctx, void *timeout_ctx);
+int hostapd_check_max_sta(struct hostapd_data *hapd);
+
+void hostapd_switch_color(struct hostapd_data *hapd, u64 bitmap);
+void hostapd_cleanup_cca_params(struct hostapd_data *hapd);
+
+/* utils.c */
+int hostapd_register_probereq_cb(struct hostapd_data *hapd,
+				 int (*cb)(void *ctx, const u8 *sa,
+					   const u8 *da, const u8 *bssid,
+					   const u8 *ie, size_t ie_len,
+					   int ssi_signal),
+				 void *ctx);
+void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr,
+				int mld_assoc_link_id);
+
+/* drv_callbacks.c (TODO: move to somewhere else?) */
+void hostapd_notify_assoc_fils_finish(struct hostapd_data *hapd,
+				      struct sta_info *sta);
+int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
+			const u8 *ie, size_t ielen, int reassoc);
+void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr);
+void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr);
+void hostapd_event_connect_failed_reason(struct hostapd_data *hapd,
+					 const u8 *addr, int reason_code);
+int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da,
+			 const u8 *bssid, const u8 *ie, size_t ie_len,
+			 int ssi_signal);
+void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
+			     int offset, int width, int cf1, int cf2,
+			     u16 punct_bitmap, int finished);
+struct survey_results;
+void hostapd_event_get_survey(struct hostapd_iface *iface,
+			      struct survey_results *survey_results);
+void hostapd_acs_channel_selected(struct hostapd_data *hapd,
+				  struct acs_selected_channels *acs_res);
+
+const struct hostapd_eap_user *
+hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity,
+		     size_t identity_len, int phase2);
+
+struct hostapd_data * hostapd_get_iface(struct hapd_interfaces *interfaces,
+					const char *ifname);
+void hostapd_event_sta_opmode_changed(struct hostapd_data *hapd, const u8 *addr,
+				      enum smps_mode smps_mode,
+				      enum chan_width chan_width, u8 rx_nss);
+
+#ifdef CONFIG_FST
+void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd,
+				struct fst_wpa_obj *iface_obj);
+#endif /* CONFIG_FST */
+
+int hostapd_set_acl(struct hostapd_data *hapd);
+struct hostapd_data * hostapd_mbssid_get_tx_bss(struct hostapd_data *hapd);
+int hostapd_mbssid_get_bss_index(struct hostapd_data *hapd);
+struct hostapd_data * hostapd_mld_get_link_bss(struct hostapd_data *hapd,
+					       u8 link_id);
+
+#endif /* HOSTAPD_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/hs20.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/hs20.c
new file mode 100644
index 0000000..05e9b9d
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/hs20.c
@@ -0,0 +1,255 @@
+/*
+ * Hotspot 2.0 AP ANQP processing
+ * Copyright (c) 2009, Atheros Communications, Inc.
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "sta_info.h"
+#include "hs20.h"
+
+
+u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 conf;
+	if (!hapd->conf->hs20)
+		return eid;
+	*eid++ = WLAN_EID_VENDOR_SPECIFIC;
+	*eid++ = hapd->conf->hs20_release < 2 ? 5 : 7;
+	WPA_PUT_BE24(eid, OUI_WFA);
+	eid += 3;
+	*eid++ = HS20_INDICATION_OUI_TYPE;
+	conf = (hapd->conf->hs20_release - 1) << 4; /* Release Number */
+	if (hapd->conf->hs20_release >= 2)
+		conf |= HS20_ANQP_DOMAIN_ID_PRESENT;
+	if (hapd->conf->disable_dgaf)
+		conf |= HS20_DGAF_DISABLED;
+	*eid++ = conf;
+	if (hapd->conf->hs20_release >= 2) {
+		WPA_PUT_LE16(eid, hapd->conf->anqp_domain_id);
+		eid += 2;
+	}
+
+	return eid;
+}
+
+
+u8 * hostapd_eid_osen(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 *len;
+	u16 capab;
+
+	if (!hapd->conf->osen)
+		return eid;
+
+	*eid++ = WLAN_EID_VENDOR_SPECIFIC;
+	len = eid++; /* to be filled */
+	WPA_PUT_BE24(eid, OUI_WFA);
+	eid += 3;
+	*eid++ = HS20_OSEN_OUI_TYPE;
+
+	/* Group Data Cipher Suite */
+	RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
+	eid += RSN_SELECTOR_LEN;
+
+	/* Pairwise Cipher Suite Count and List */
+	WPA_PUT_LE16(eid, 1);
+	eid += 2;
+	RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_CCMP);
+	eid += RSN_SELECTOR_LEN;
+
+	/* AKM Suite Count and List */
+	WPA_PUT_LE16(eid, 1);
+	eid += 2;
+	RSN_SELECTOR_PUT(eid, RSN_AUTH_KEY_MGMT_OSEN);
+	eid += RSN_SELECTOR_LEN;
+
+	/* RSN Capabilities */
+	capab = 0;
+	if (hapd->conf->wmm_enabled) {
+		/* 4 PTKSA replay counters when using WMM */
+		capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2);
+	}
+	if (hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+		capab |= WPA_CAPABILITY_MFPC;
+		if (hapd->conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED)
+			capab |= WPA_CAPABILITY_MFPR;
+	}
+#ifdef CONFIG_OCV
+	if (hapd->conf->ocv &&
+	    (hapd->iface->drv_flags2 &
+	     (WPA_DRIVER_FLAGS2_AP_SME | WPA_DRIVER_FLAGS2_OCV)))
+		capab |= WPA_CAPABILITY_OCVC;
+#endif /* CONFIG_OCV */
+	WPA_PUT_LE16(eid, capab);
+	eid += 2;
+
+	*len = eid - len - 1;
+
+	return eid;
+}
+
+
+int hs20_send_wnm_notification(struct hostapd_data *hapd, const u8 *addr,
+			       u8 osu_method, const char *url)
+{
+	struct wpabuf *buf;
+	size_t len = 0;
+	int ret;
+
+	/* TODO: should refuse to send notification if the STA is not associated
+	 * or if the STA did not indicate support for WNM-Notification */
+
+	if (url) {
+		len = 1 + os_strlen(url);
+		if (5 + len > 255) {
+			wpa_printf(MSG_INFO, "HS 2.0: Too long URL for "
+				   "WNM-Notification: '%s'", url);
+			return -1;
+		}
+	}
+
+	buf = wpabuf_alloc(4 + 7 + len);
+	if (buf == NULL)
+		return -1;
+
+	wpabuf_put_u8(buf, WLAN_ACTION_WNM);
+	wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ);
+	wpabuf_put_u8(buf, 1); /* Dialog token */
+	wpabuf_put_u8(buf, 1); /* Type - 1 reserved for WFA */
+
+	/* Subscription Remediation subelement */
+	wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+	wpabuf_put_u8(buf, 5 + len);
+	wpabuf_put_be24(buf, OUI_WFA);
+	wpabuf_put_u8(buf, HS20_WNM_SUB_REM_NEEDED);
+	if (url) {
+		wpabuf_put_u8(buf, len - 1);
+		wpabuf_put_data(buf, url, len - 1);
+		wpabuf_put_u8(buf, osu_method);
+	} else {
+		/* Server URL and Server Method fields not included */
+		wpabuf_put_u8(buf, 0);
+	}
+
+	ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+				      wpabuf_head(buf), wpabuf_len(buf));
+
+	wpabuf_free(buf);
+
+	return ret;
+}
+
+
+int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd,
+					  const u8 *addr,
+					  const struct wpabuf *payload)
+{
+	struct wpabuf *buf;
+	int ret;
+
+	/* TODO: should refuse to send notification if the STA is not associated
+	 * or if the STA did not indicate support for WNM-Notification */
+
+	buf = wpabuf_alloc(4 + 6 + wpabuf_len(payload));
+	if (buf == NULL)
+		return -1;
+
+	wpabuf_put_u8(buf, WLAN_ACTION_WNM);
+	wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ);
+	wpabuf_put_u8(buf, 1); /* Dialog token */
+	wpabuf_put_u8(buf, 1); /* Type - 1 reserved for WFA */
+
+	/* Deauthentication Imminent Notice subelement */
+	wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+	wpabuf_put_u8(buf, 4 + wpabuf_len(payload));
+	wpabuf_put_be24(buf, OUI_WFA);
+	wpabuf_put_u8(buf, HS20_WNM_DEAUTH_IMMINENT_NOTICE);
+	wpabuf_put_buf(buf, payload);
+
+	ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+				      wpabuf_head(buf), wpabuf_len(buf));
+
+	wpabuf_free(buf);
+
+	return ret;
+}
+
+
+int hs20_send_wnm_notification_t_c(struct hostapd_data *hapd,
+				   const u8 *addr, const char *url)
+{
+	struct wpabuf *buf;
+	int ret;
+	size_t url_len;
+
+	if (!url) {
+		wpa_printf(MSG_INFO, "HS 2.0: No T&C Server URL available");
+		return -1;
+	}
+
+	url_len = os_strlen(url);
+	if (5 + url_len > 255) {
+		wpa_printf(MSG_INFO,
+			   "HS 2.0: Too long T&C Server URL for WNM-Notification: '%s'",
+			   url);
+		return -1;
+	}
+
+	buf = wpabuf_alloc(4 + 7 + url_len);
+	if (!buf)
+		return -1;
+
+	wpabuf_put_u8(buf, WLAN_ACTION_WNM);
+	wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ);
+	wpabuf_put_u8(buf, 1); /* Dialog token */
+	wpabuf_put_u8(buf, 1); /* Type - 1 reserved for WFA */
+
+	/* Terms and Conditions Acceptance subelement */
+	wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+	wpabuf_put_u8(buf, 4 + 1 + url_len);
+	wpabuf_put_be24(buf, OUI_WFA);
+	wpabuf_put_u8(buf, HS20_WNM_T_C_ACCEPTANCE);
+	wpabuf_put_u8(buf, url_len);
+	wpabuf_put_str(buf, url);
+
+	ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+				      wpabuf_head(buf), wpabuf_len(buf));
+
+	wpabuf_free(buf);
+
+	return ret;
+}
+
+
+void hs20_t_c_filtering(struct hostapd_data *hapd, struct sta_info *sta,
+			int enabled)
+{
+	if (enabled) {
+		wpa_printf(MSG_DEBUG,
+			   "HS 2.0: Terms and Conditions filtering required for "
+			   MACSTR, MAC2STR(sta->addr));
+		sta->hs20_t_c_filtering = 1;
+		/* TODO: Enable firewall filtering for the STA */
+		wpa_msg(hapd->msg_ctx, MSG_INFO, HS20_T_C_FILTERING_ADD MACSTR,
+			MAC2STR(sta->addr));
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "HS 2.0: Terms and Conditions filtering not required for "
+			   MACSTR, MAC2STR(sta->addr));
+		sta->hs20_t_c_filtering = 0;
+		/* TODO: Disable firewall filtering for the STA */
+		wpa_msg(hapd->msg_ctx, MSG_INFO,
+			HS20_T_C_FILTERING_REMOVE MACSTR, MAC2STR(sta->addr));
+	}
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/hs20.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/hs20.h
new file mode 100644
index 0000000..e99e26e
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/hs20.h
@@ -0,0 +1,26 @@
+/*
+ * Hotspot 2.0 AP ANQP processing
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef HS20_H
+#define HS20_H
+
+struct hostapd_data;
+
+u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_osen(struct hostapd_data *hapd, u8 *eid);
+int hs20_send_wnm_notification(struct hostapd_data *hapd, const u8 *addr,
+			       u8 osu_method, const char *url);
+int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd,
+					  const u8 *addr,
+					  const struct wpabuf *payload);
+int hs20_send_wnm_notification_t_c(struct hostapd_data *hapd,
+				   const u8 *addr, const char *url);
+void hs20_t_c_filtering(struct hostapd_data *hapd, struct sta_info *sta,
+			int enabled);
+
+#endif /* HS20_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/hw_features.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/hw_features.c
new file mode 100644
index 0000000..2152eef
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/hw_features.c
@@ -0,0 +1,1333 @@
+/*
+ * hostapd / Hardware feature query and different modes
+ * Copyright 2002-2003, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/wpa_ctrl.h"
+#include "common/hw_features_common.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "acs.h"
+#include "ieee802_11.h"
+#include "beacon.h"
+#include "hw_features.h"
+
+
+void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
+			      size_t num_hw_features)
+{
+	size_t i;
+
+	if (hw_features == NULL)
+		return;
+
+	for (i = 0; i < num_hw_features; i++) {
+		os_free(hw_features[i].channels);
+		os_free(hw_features[i].rates);
+	}
+
+	os_free(hw_features);
+}
+
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+static char * dfs_info(struct hostapd_channel_data *chan)
+{
+	static char info[256];
+	char *state;
+
+	switch (chan->flag & HOSTAPD_CHAN_DFS_MASK) {
+	case HOSTAPD_CHAN_DFS_UNKNOWN:
+		state = "unknown";
+		break;
+	case HOSTAPD_CHAN_DFS_USABLE:
+		state = "usable";
+		break;
+	case HOSTAPD_CHAN_DFS_UNAVAILABLE:
+		state = "unavailable";
+		break;
+	case HOSTAPD_CHAN_DFS_AVAILABLE:
+		state = "available";
+		break;
+	default:
+		return "";
+	}
+	os_snprintf(info, sizeof(info), " (DFS state = %s)", state);
+	info[sizeof(info) - 1] = '\0';
+
+	return info;
+}
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+int hostapd_get_hw_features(struct hostapd_iface *iface)
+{
+	struct hostapd_data *hapd = iface->bss[0];
+	int i, j;
+	u16 num_modes, flags;
+	struct hostapd_hw_modes *modes;
+	u8 dfs_domain;
+
+	if (hostapd_drv_none(hapd))
+		return -1;
+	modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags,
+					    &dfs_domain);
+	if (modes == NULL) {
+		hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "Fetching hardware channel/rate support not "
+			       "supported.");
+		return -1;
+	}
+
+	iface->hw_flags = flags;
+	iface->dfs_domain = dfs_domain;
+
+	hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
+	iface->hw_features = modes;
+	iface->num_hw_features = num_modes;
+
+	for (i = 0; i < num_modes; i++) {
+		struct hostapd_hw_modes *feature = &modes[i];
+		int dfs_enabled = hapd->iconf->ieee80211h &&
+			(iface->drv_flags & WPA_DRIVER_FLAGS_RADAR);
+
+		/* set flag for channels we can use in current regulatory
+		 * domain */
+		for (j = 0; j < feature->num_channels; j++) {
+			int dfs = 0;
+
+			/*
+			 * Disable all channels that are marked not to allow
+			 * to initiate radiation (a.k.a. passive scan and no
+			 * IBSS).
+			 * Use radar channels only if the driver supports DFS.
+			 */
+			if ((feature->channels[j].flag &
+			     HOSTAPD_CHAN_RADAR) && dfs_enabled) {
+				dfs = 1;
+			} else if (((feature->channels[j].flag &
+				     HOSTAPD_CHAN_RADAR) &&
+				    !(iface->drv_flags &
+				      WPA_DRIVER_FLAGS_DFS_OFFLOAD)) ||
+				   (feature->channels[j].flag &
+				    HOSTAPD_CHAN_NO_IR)) {
+				feature->channels[j].flag |=
+					HOSTAPD_CHAN_DISABLED;
+			}
+
+			if (feature->channels[j].flag & HOSTAPD_CHAN_DISABLED)
+				continue;
+
+			wpa_printf(MSG_MSGDUMP, "Allowed channel: mode=%d "
+				   "chan=%d freq=%d MHz max_tx_power=%d dBm%s",
+				   feature->mode,
+				   feature->channels[j].chan,
+				   feature->channels[j].freq,
+				   feature->channels[j].max_tx_power,
+				   dfs ? dfs_info(&feature->channels[j]) : "");
+		}
+	}
+
+	return 0;
+}
+
+
+int hostapd_prepare_rates(struct hostapd_iface *iface,
+			  struct hostapd_hw_modes *mode)
+{
+	int i, num_basic_rates = 0;
+	int basic_rates_a[] = { 60, 120, 240, -1 };
+	int basic_rates_b[] = { 10, 20, -1 };
+	int basic_rates_g[] = { 10, 20, 55, 110, -1 };
+	int *basic_rates;
+
+	if (iface->conf->basic_rates)
+		basic_rates = iface->conf->basic_rates;
+	else switch (mode->mode) {
+	case HOSTAPD_MODE_IEEE80211A:
+		basic_rates = basic_rates_a;
+		break;
+	case HOSTAPD_MODE_IEEE80211B:
+		basic_rates = basic_rates_b;
+		break;
+	case HOSTAPD_MODE_IEEE80211G:
+		basic_rates = basic_rates_g;
+		break;
+	case HOSTAPD_MODE_IEEE80211AD:
+		return 0; /* No basic rates for 11ad */
+	default:
+		return -1;
+	}
+
+	i = 0;
+	while (basic_rates[i] >= 0)
+		i++;
+	if (i)
+		i++; /* -1 termination */
+	os_free(iface->basic_rates);
+	iface->basic_rates = os_malloc(i * sizeof(int));
+	if (iface->basic_rates)
+		os_memcpy(iface->basic_rates, basic_rates, i * sizeof(int));
+
+	os_free(iface->current_rates);
+	iface->num_rates = 0;
+
+	iface->current_rates =
+		os_calloc(mode->num_rates, sizeof(struct hostapd_rate_data));
+	if (!iface->current_rates) {
+		wpa_printf(MSG_ERROR, "Failed to allocate memory for rate "
+			   "table.");
+		return -1;
+	}
+
+	for (i = 0; i < mode->num_rates; i++) {
+		struct hostapd_rate_data *rate;
+
+		if (iface->conf->supported_rates &&
+		    !hostapd_rate_found(iface->conf->supported_rates,
+					mode->rates[i]))
+			continue;
+
+		rate = &iface->current_rates[iface->num_rates];
+		rate->rate = mode->rates[i];
+		if (hostapd_rate_found(basic_rates, rate->rate)) {
+			rate->flags |= HOSTAPD_RATE_BASIC;
+			num_basic_rates++;
+		}
+		wpa_printf(MSG_DEBUG, "RATE[%d] rate=%d flags=0x%x",
+			   iface->num_rates, rate->rate, rate->flags);
+		iface->num_rates++;
+	}
+
+	if ((iface->num_rates == 0 || num_basic_rates == 0) &&
+	    (!iface->conf->ieee80211n || !iface->conf->require_ht)) {
+		wpa_printf(MSG_ERROR, "No rates remaining in supported/basic "
+			   "rate sets (%d,%d).",
+			   iface->num_rates, num_basic_rates);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int ieee80211n_allowed_ht40_channel_pair(struct hostapd_iface *iface)
+{
+	int pri_freq, sec_freq;
+	struct hostapd_channel_data *p_chan, *s_chan;
+
+	pri_freq = iface->freq;
+	sec_freq = pri_freq + iface->conf->secondary_channel * 20;
+
+	if (!iface->current_mode)
+		return 0;
+
+	p_chan = hw_get_channel_freq(iface->current_mode->mode, pri_freq, NULL,
+				     iface->hw_features,
+				     iface->num_hw_features);
+
+	s_chan = hw_get_channel_freq(iface->current_mode->mode, sec_freq, NULL,
+				     iface->hw_features,
+				     iface->num_hw_features);
+
+	return allowed_ht40_channel_pair(iface->current_mode->mode,
+					 p_chan, s_chan);
+}
+
+
+static void ieee80211n_switch_pri_sec(struct hostapd_iface *iface)
+{
+	if (iface->conf->secondary_channel > 0) {
+		iface->conf->channel += 4;
+		iface->freq += 20;
+		iface->conf->secondary_channel = -1;
+	} else {
+		iface->conf->channel -= 4;
+		iface->freq -= 20;
+		iface->conf->secondary_channel = 1;
+	}
+}
+
+
+static int ieee80211n_check_40mhz_5g(struct hostapd_iface *iface,
+				     struct wpa_scan_results *scan_res)
+{
+	unsigned int pri_freq, sec_freq;
+	int res;
+	struct hostapd_channel_data *pri_chan, *sec_chan;
+
+	pri_freq = iface->freq;
+	sec_freq = pri_freq + iface->conf->secondary_channel * 20;
+
+	if (!iface->current_mode)
+		return 0;
+	pri_chan = hw_get_channel_freq(iface->current_mode->mode, pri_freq,
+				       NULL, iface->hw_features,
+				       iface->num_hw_features);
+	sec_chan = hw_get_channel_freq(iface->current_mode->mode, sec_freq,
+				       NULL, iface->hw_features,
+				       iface->num_hw_features);
+
+	res = check_40mhz_5g(scan_res, pri_chan, sec_chan);
+
+	if (res == 2) {
+		if (iface->conf->no_pri_sec_switch) {
+			wpa_printf(MSG_DEBUG,
+				   "Cannot switch PRI/SEC channels due to local constraint");
+		} else {
+			ieee80211n_switch_pri_sec(iface);
+		}
+	}
+
+	return !!res;
+}
+
+
+static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface,
+				      struct wpa_scan_results *scan_res)
+{
+	int pri_chan, sec_chan;
+
+	pri_chan = iface->conf->channel;
+	sec_chan = pri_chan + iface->conf->secondary_channel * 4;
+
+	return check_40mhz_2g4(iface->current_mode, scan_res, pri_chan,
+			       sec_chan);
+}
+
+
+static void ieee80211n_check_scan(struct hostapd_iface *iface)
+{
+	struct wpa_scan_results *scan_res;
+	int oper40;
+#ifdef CONFIG_24G_BW_SWITCH
+	int res = 1;
+#else
+	int res = 0;
+#endif /* CONFIG_24G_BW_SWITCH */
+
+	/* Check list of neighboring BSSes (from scan) to see whether 40 MHz is
+	 * allowed per IEEE Std 802.11-2012, 10.15.3.2 */
+
+	iface->scan_cb = NULL;
+
+	scan_res = hostapd_driver_get_scan_results(iface->bss[0]);
+	if (scan_res == NULL) {
+		hostapd_setup_interface_complete(iface, 1);
+		return;
+	}
+
+	if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A)
+		oper40 = ieee80211n_check_40mhz_5g(iface, scan_res);
+	else
+		oper40 = ieee80211n_check_40mhz_2g4(iface, scan_res);
+	wpa_scan_results_free(scan_res);
+
+	iface->secondary_ch = iface->conf->secondary_channel;
+	if (!oper40) {
+		wpa_printf(MSG_INFO, "20/40 MHz operation not permitted on "
+			   "channel pri=%d sec=%d based on overlapping BSSes",
+			   iface->conf->channel,
+			   iface->conf->channel +
+			   iface->conf->secondary_channel * 4);
+#ifdef CONFIG_24G_BW_SWITCH
+		if (iface->conf->auto_20m_40m)
+#endif /* CONFIG_24G_BW_SWITCH */
+		{
+			iface->conf->secondary_channel = 0;
+		}
+		if (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX) {
+			/*
+			 * TODO: Could consider scheduling another scan to check
+			 * if channel width can be changed if no coex reports
+			 * are received from associating stations.
+			 */
+		}
+	}
+
+#ifdef CONFIG_IEEE80211AX
+	if (iface->conf->secondary_channel &&
+	    iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G &&
+	    iface->conf->ieee80211ax) {
+		struct he_capabilities *he_cap;
+
+		he_cap = &iface->current_mode->he_capab[IEEE80211_MODE_AP];
+		if (!(he_cap->phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
+		      HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G)) {
+			wpa_printf(MSG_DEBUG,
+				   "HE: 40 MHz channel width is not supported in 2.4 GHz; clear secondary channel configuration");
+			iface->conf->secondary_channel = 0;
+		}
+	}
+#endif /* CONFIG_IEEE80211AX */
+
+	if (iface->conf->secondary_channel)
+		res = ieee80211n_allowed_ht40_channel_pair(iface);
+	if (!res) {
+		iface->conf->secondary_channel = 0;
+		hostapd_set_oper_centr_freq_seg0_idx(iface->conf, 0);
+		hostapd_set_oper_centr_freq_seg1_idx(iface->conf, 0);
+		hostapd_set_oper_chwidth(iface->conf, CONF_OPER_CHWIDTH_USE_HT);
+		res = 1;
+		wpa_printf(MSG_INFO, "Fallback to 20 MHz");
+	}
+
+	hostapd_setup_interface_complete(iface, !res);
+}
+
+
+static void ieee80211n_scan_channels_2g4(struct hostapd_iface *iface,
+					 struct wpa_driver_scan_params *params)
+{
+	/* Scan only the affected frequency range */
+	int pri_freq, sec_freq;
+	int affected_start, affected_end;
+	int i, pos;
+	struct hostapd_hw_modes *mode;
+
+	if (iface->current_mode == NULL)
+		return;
+
+	pri_freq = iface->freq;
+	if (iface->conf->secondary_channel > 0)
+		sec_freq = pri_freq + 20;
+	else
+		sec_freq = pri_freq - 20;
+	/*
+	 * Note: Need to find the PRI channel also in cases where the affected
+	 * channel is the SEC channel of a 40 MHz BSS, so need to include the
+	 * scanning coverage here to be 40 MHz from the center frequency.
+	 */
+	affected_start = (pri_freq + sec_freq) / 2 - 40;
+	affected_end = (pri_freq + sec_freq) / 2 + 40;
+	wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
+		   affected_start, affected_end);
+
+	mode = iface->current_mode;
+	params->freqs = os_calloc(mode->num_channels + 1, sizeof(int));
+	if (params->freqs == NULL)
+		return;
+	pos = 0;
+
+	for (i = 0; i < mode->num_channels; i++) {
+		struct hostapd_channel_data *chan = &mode->channels[i];
+		if (chan->flag & HOSTAPD_CHAN_DISABLED)
+			continue;
+		if (chan->freq < affected_start ||
+		    chan->freq > affected_end)
+			continue;
+		params->freqs[pos++] = chan->freq;
+	}
+}
+
+
+static void ieee80211n_scan_channels_5g(struct hostapd_iface *iface,
+					struct wpa_driver_scan_params *params)
+{
+	/* Scan only the affected frequency range */
+	int pri_freq;
+	int affected_start, affected_end;
+	int i, pos;
+	struct hostapd_hw_modes *mode;
+
+	if (iface->current_mode == NULL)
+		return;
+
+	pri_freq = iface->freq;
+	if (iface->conf->secondary_channel > 0) {
+		affected_start = pri_freq - 10;
+		affected_end = pri_freq + 30;
+	} else {
+		affected_start = pri_freq - 30;
+		affected_end = pri_freq + 10;
+	}
+	wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
+		   affected_start, affected_end);
+
+	mode = iface->current_mode;
+	params->freqs = os_calloc(mode->num_channels + 1, sizeof(int));
+	if (params->freqs == NULL)
+		return;
+	pos = 0;
+
+	for (i = 0; i < mode->num_channels; i++) {
+		struct hostapd_channel_data *chan = &mode->channels[i];
+		if (chan->flag & HOSTAPD_CHAN_DISABLED)
+			continue;
+		if (chan->freq < affected_start ||
+		    chan->freq > affected_end)
+			continue;
+		params->freqs[pos++] = chan->freq;
+	}
+}
+
+
+static void ap_ht40_scan_retry(void *eloop_data, void *user_data)
+{
+#define HT2040_COEX_SCAN_RETRY 15
+	struct hostapd_iface *iface = eloop_data;
+	struct wpa_driver_scan_params params;
+	int ret;
+
+	os_memset(&params, 0, sizeof(params));
+	if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
+		ieee80211n_scan_channels_2g4(iface, &params);
+	else
+		ieee80211n_scan_channels_5g(iface, &params);
+
+	ret = hostapd_driver_scan(iface->bss[0], &params);
+	iface->num_ht40_scan_tries++;
+	os_free(params.freqs);
+
+	if (ret == -EBUSY &&
+	    iface->num_ht40_scan_tries < HT2040_COEX_SCAN_RETRY) {
+		wpa_printf(MSG_ERROR,
+			   "Failed to request a scan of neighboring BSSes ret=%d (%s) - try to scan again (attempt %d)",
+			   ret, strerror(-ret), iface->num_ht40_scan_tries);
+		eloop_register_timeout(1, 0, ap_ht40_scan_retry, iface, NULL);
+		return;
+	}
+
+	if (ret == 0) {
+		iface->scan_cb = ieee80211n_check_scan;
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "Failed to request a scan in device, bringing up in HT20 mode");
+	iface->conf->secondary_channel = 0;
+	iface->conf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
+	hostapd_setup_interface_complete(iface, 0);
+}
+
+
+void hostapd_stop_setup_timers(struct hostapd_iface *iface)
+{
+	eloop_cancel_timeout(ap_ht40_scan_retry, iface, NULL);
+}
+
+
+static int ieee80211n_check_40mhz(struct hostapd_iface *iface)
+{
+	struct wpa_driver_scan_params params;
+	int ret;
+
+	/* Check that HT40 is used and PRI / SEC switch is allowed */
+	if (!iface->conf->secondary_channel || iface->conf->no_pri_sec_switch ||
+		iface->conf->noscan)
+		return 0;
+
+	hostapd_set_state(iface, HAPD_IFACE_HT_SCAN);
+	wpa_printf(MSG_DEBUG, "Scan for neighboring BSSes prior to enabling "
+		   "40 MHz channel");
+	os_memset(&params, 0, sizeof(params));
+	if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
+		ieee80211n_scan_channels_2g4(iface, &params);
+	else
+		ieee80211n_scan_channels_5g(iface, &params);
+
+	ret = hostapd_driver_scan(iface->bss[0], &params);
+	os_free(params.freqs);
+
+	if (ret == -EBUSY) {
+		wpa_printf(MSG_ERROR,
+			   "Failed to request a scan of neighboring BSSes ret=%d (%s) - try to scan again",
+			   ret, strerror(-ret));
+		iface->num_ht40_scan_tries = 1;
+		eloop_cancel_timeout(ap_ht40_scan_retry, iface, NULL);
+		eloop_register_timeout(1, 0, ap_ht40_scan_retry, iface, NULL);
+		return 1;
+	}
+
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR,
+			   "Failed to request a scan of neighboring BSSes ret=%d (%s)",
+			   ret, strerror(-ret));
+		return -1;
+	}
+
+	iface->scan_cb = ieee80211n_check_scan;
+	return 1;
+}
+
+
+static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface)
+{
+	u16 hw = iface->current_mode->ht_capab;
+	u16 conf = iface->conf->ht_capab;
+
+	if ((conf & HT_CAP_INFO_LDPC_CODING_CAP) &&
+	    !(hw & HT_CAP_INFO_LDPC_CODING_CAP)) {
+		wpa_printf(MSG_ERROR, "Driver does not support configured "
+			   "HT capability [LDPC]");
+		return 0;
+	}
+
+	/*
+	 * Driver ACS chosen channel may not be HT40 due to internal driver
+	 * restrictions.
+	 */
+	if (!iface->conf->acs && (conf & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
+	    !(hw & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) {
+		wpa_printf(MSG_ERROR, "Driver does not support configured "
+			   "HT capability [HT40*]");
+		return 0;
+	}
+
+	if ((conf & HT_CAP_INFO_GREEN_FIELD) &&
+	    !(hw & HT_CAP_INFO_GREEN_FIELD)) {
+		wpa_printf(MSG_ERROR, "Driver does not support configured "
+			   "HT capability [GF]");
+		return 0;
+	}
+
+	if ((conf & HT_CAP_INFO_SHORT_GI20MHZ) &&
+	    !(hw & HT_CAP_INFO_SHORT_GI20MHZ)) {
+		wpa_printf(MSG_ERROR, "Driver does not support configured "
+			   "HT capability [SHORT-GI-20]");
+		return 0;
+	}
+
+	if ((conf & HT_CAP_INFO_SHORT_GI40MHZ) &&
+	    !(hw & HT_CAP_INFO_SHORT_GI40MHZ)) {
+		wpa_printf(MSG_ERROR, "Driver does not support configured "
+			   "HT capability [SHORT-GI-40]");
+		return 0;
+	}
+
+	if ((conf & HT_CAP_INFO_TX_STBC) && !(hw & HT_CAP_INFO_TX_STBC)) {
+		wpa_printf(MSG_ERROR, "Driver does not support configured "
+			   "HT capability [TX-STBC]");
+		return 0;
+	}
+
+	if ((conf & HT_CAP_INFO_RX_STBC_MASK) >
+	    (hw & HT_CAP_INFO_RX_STBC_MASK)) {
+		wpa_printf(MSG_ERROR, "Driver does not support configured "
+			   "HT capability [RX-STBC*]");
+		return 0;
+	}
+
+	if ((conf & HT_CAP_INFO_DELAYED_BA) &&
+	    !(hw & HT_CAP_INFO_DELAYED_BA)) {
+		wpa_printf(MSG_ERROR, "Driver does not support configured "
+			   "HT capability [DELAYED-BA]");
+		return 0;
+	}
+
+	if ((conf & HT_CAP_INFO_MAX_AMSDU_SIZE) &&
+	    !(hw & HT_CAP_INFO_MAX_AMSDU_SIZE)) {
+		wpa_printf(MSG_ERROR, "Driver does not support configured "
+			   "HT capability [MAX-AMSDU-7935]");
+		return 0;
+	}
+
+	if ((conf & HT_CAP_INFO_DSSS_CCK40MHZ) &&
+	    !(hw & HT_CAP_INFO_DSSS_CCK40MHZ)) {
+		wpa_printf(MSG_ERROR, "Driver does not support configured "
+			   "HT capability [DSSS_CCK-40]");
+		return 0;
+	}
+
+	if ((conf & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT) &&
+	    !(hw & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT)) {
+		wpa_printf(MSG_ERROR, "Driver does not support configured "
+			   "HT capability [LSIG-TXOP-PROT]");
+		return 0;
+	}
+
+	return 1;
+}
+
+
+#ifdef CONFIG_IEEE80211AC
+static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface)
+{
+	struct hostapd_hw_modes *mode = iface->current_mode;
+	u32 hw = mode->vht_capab;
+	u32 conf = iface->conf->vht_capab;
+
+	wpa_printf(MSG_DEBUG, "hw vht capab: 0x%x, conf vht capab: 0x%x",
+		   hw, conf);
+
+	if (mode->mode == HOSTAPD_MODE_IEEE80211G &&
+	    iface->conf->bss[0]->vendor_vht &&
+	    mode->vht_capab == 0 && iface->hw_features) {
+		int i;
+
+		for (i = 0; i < iface->num_hw_features; i++) {
+			if (iface->hw_features[i].mode ==
+			    HOSTAPD_MODE_IEEE80211A) {
+				mode = &iface->hw_features[i];
+				hw = mode->vht_capab;
+				wpa_printf(MSG_DEBUG,
+					   "update hw vht capab based on 5 GHz band: 0x%x",
+					   hw);
+				break;
+			}
+		}
+	}
+
+	return ieee80211ac_cap_check(hw, conf);
+}
+#endif /* CONFIG_IEEE80211AC */
+
+
+#ifdef CONFIG_IEEE80211AX
+static int ieee80211ax_supported_he_capab(struct hostapd_iface *iface)
+{
+	return 1;
+}
+#endif /* CONFIG_IEEE80211AX */
+
+
+int hostapd_check_ht_capab(struct hostapd_iface *iface)
+{
+	int ret;
+
+	if (is_6ghz_freq(iface->freq))
+		return 0;
+	if (!iface->conf->ieee80211n)
+		return 0;
+
+	if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211B &&
+	    iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G &&
+	    (iface->conf->ht_capab & HT_CAP_INFO_DSSS_CCK40MHZ)) {
+		wpa_printf(MSG_DEBUG,
+			   "Disable HT capability [DSSS_CCK-40] on 5 GHz band");
+		iface->conf->ht_capab &= ~HT_CAP_INFO_DSSS_CCK40MHZ;
+	}
+
+	if (!ieee80211n_supported_ht_capab(iface))
+		return -1;
+#ifdef CONFIG_IEEE80211AX
+	if (iface->conf->ieee80211ax &&
+	    !ieee80211ax_supported_he_capab(iface))
+		return -1;
+#endif /* CONFIG_IEEE80211AX */
+#ifdef CONFIG_IEEE80211AC
+	if (iface->conf->ieee80211ac &&
+	    !ieee80211ac_supported_vht_capab(iface))
+		return -1;
+#endif /* CONFIG_IEEE80211AC */
+	ret = ieee80211n_check_40mhz(iface);
+	if (ret)
+		return ret;
+	if (!ieee80211n_allowed_ht40_channel_pair(iface))
+		return -1;
+
+	return 0;
+}
+
+
+int hostapd_check_edmg_capab(struct hostapd_iface *iface)
+{
+	struct hostapd_hw_modes *mode = iface->hw_features;
+	struct ieee80211_edmg_config edmg;
+
+	if (!iface->conf->enable_edmg)
+		return 0;
+
+	hostapd_encode_edmg_chan(iface->conf->enable_edmg,
+				 iface->conf->edmg_channel,
+				 iface->conf->channel,
+				 &edmg);
+
+	if (mode->edmg.channels && ieee802_edmg_is_allowed(mode->edmg, edmg))
+		return 0;
+
+	wpa_printf(MSG_WARNING, "Requested EDMG configuration is not valid");
+	wpa_printf(MSG_INFO, "EDMG capab: channels 0x%x, bw_config %d",
+		   mode->edmg.channels, mode->edmg.bw_config);
+	wpa_printf(MSG_INFO,
+		   "Requested EDMG configuration: channels 0x%x, bw_config %d",
+		   edmg.channels, edmg.bw_config);
+	return -1;
+}
+
+
+int hostapd_check_he_6ghz_capab(struct hostapd_iface *iface)
+{
+#ifdef CONFIG_IEEE80211AX
+	struct he_capabilities *he_cap;
+	u16 hw;
+
+	if (!iface->current_mode || !is_6ghz_freq(iface->freq))
+		return 0;
+
+	he_cap = &iface->current_mode->he_capab[IEEE80211_MODE_AP];
+	hw = he_cap->he_6ghz_capa;
+	if (iface->conf->he_6ghz_max_mpdu >
+	    ((hw & HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_MASK) >>
+	     HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_SHIFT)) {
+		wpa_printf(MSG_ERROR,
+			   "The driver does not support the configured HE 6 GHz Max MPDU length");
+		return -1;
+	}
+
+	if (iface->conf->he_6ghz_max_ampdu_len_exp >
+	    ((hw & HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_MASK) >>
+	     HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_SHIFT)) {
+		wpa_printf(MSG_ERROR,
+			   "The driver does not support the configured HE 6 GHz Max AMPDU Length Exponent");
+		return -1;
+	}
+
+	if (iface->conf->he_6ghz_rx_ant_pat &&
+	    !(hw & HE_6GHZ_BAND_CAP_RX_ANTPAT_CONS)) {
+		wpa_printf(MSG_ERROR,
+			   "The driver does not support the configured HE 6 GHz Rx Antenna Pattern");
+		return -1;
+	}
+
+	if (iface->conf->he_6ghz_tx_ant_pat &&
+	    !(hw & HE_6GHZ_BAND_CAP_TX_ANTPAT_CONS)) {
+		wpa_printf(MSG_ERROR,
+			   "The driver does not support the configured HE 6 GHz Tx Antenna Pattern");
+		return -1;
+	}
+#endif /* CONFIG_IEEE80211AX */
+	return 0;
+}
+
+
+/* Returns:
+ * 1 = usable
+ * 0 = not usable
+ * -1 = not currently usable due to 6 GHz NO-IR
+ */
+static int hostapd_is_usable_chan(struct hostapd_iface *iface,
+				  int frequency, int primary)
+{
+	struct hostapd_channel_data *chan;
+
+	if (!iface->current_mode)
+		return 0;
+
+	chan = hw_get_channel_freq(iface->current_mode->mode, frequency, NULL,
+				   iface->hw_features, iface->num_hw_features);
+	if (!chan)
+		return 0;
+
+	if ((primary && chan_pri_allowed(chan)) ||
+	    (!primary && !(chan->flag & HOSTAPD_CHAN_DISABLED)))
+		return 1;
+
+	wpa_printf(MSG_INFO,
+		   "Frequency %d (%s) not allowed for AP mode, flags: 0x%x%s%s",
+		   frequency, primary ? "primary" : "secondary",
+		   chan->flag,
+		   chan->flag & HOSTAPD_CHAN_NO_IR ? " NO-IR" : "",
+		   chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : "");
+
+	if (is_6ghz_freq(chan->freq) && (chan->flag & HOSTAPD_CHAN_NO_IR))
+		return -1;
+
+	return 0;
+}
+
+
+static int hostapd_is_usable_edmg(struct hostapd_iface *iface)
+{
+	int i, contiguous = 0;
+	int num_of_enabled = 0;
+	int max_contiguous = 0;
+	int err;
+	struct ieee80211_edmg_config edmg;
+	struct hostapd_channel_data *pri_chan;
+
+	if (!iface->conf->enable_edmg)
+		return 1;
+
+	if (!iface->current_mode)
+		return 0;
+	pri_chan = hw_get_channel_freq(iface->current_mode->mode,
+				       iface->freq, NULL,
+				       iface->hw_features,
+				       iface->num_hw_features);
+	if (!pri_chan)
+		return 0;
+	hostapd_encode_edmg_chan(iface->conf->enable_edmg,
+				 iface->conf->edmg_channel,
+				 pri_chan->chan,
+				 &edmg);
+	if (!(edmg.channels & BIT(pri_chan->chan - 1)))
+		return 0;
+
+	/* 60 GHz channels 1..6 */
+	for (i = 0; i < 6; i++) {
+		int freq = 56160 + 2160 * (i + 1);
+
+		if (edmg.channels & BIT(i)) {
+			contiguous++;
+			num_of_enabled++;
+		} else {
+			contiguous = 0;
+			continue;
+		}
+
+		/* P802.11ay defines that the total number of subfields
+		 * set to one does not exceed 4.
+		 */
+		if (num_of_enabled > 4)
+			return 0;
+
+		err = hostapd_is_usable_chan(iface, freq, 1);
+		if (err <= 0)
+			return err;
+
+		if (contiguous > max_contiguous)
+			max_contiguous = contiguous;
+	}
+
+	/* Check if the EDMG configuration is valid under the limitations
+	 * of P802.11ay.
+	 */
+	/* check bw_config against contiguous EDMG channels */
+	switch (edmg.bw_config) {
+	case EDMG_BW_CONFIG_4:
+		if (!max_contiguous)
+			return 0;
+		break;
+	case EDMG_BW_CONFIG_5:
+		if (max_contiguous < 2)
+			return 0;
+		break;
+	default:
+		return 0;
+	}
+
+	return 1;
+}
+
+
+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;
+}
+
+
+/* Returns:
+ * 1 = usable
+ * 0 = not usable
+ * -1 = not currently usable due to 6 GHz NO-IR
+ */
+static int hostapd_is_usable_chans(struct hostapd_iface *iface)
+{
+	int secondary_freq;
+	struct hostapd_channel_data *pri_chan;
+	int err;
+
+	if (!iface->current_mode)
+		return 0;
+	pri_chan = hw_get_channel_freq(iface->current_mode->mode,
+				       iface->freq, NULL,
+				       iface->hw_features,
+				       iface->num_hw_features);
+	if (!pri_chan) {
+		wpa_printf(MSG_ERROR, "Primary frequency not present");
+		return 0;
+	}
+
+	err = hostapd_is_usable_chan(iface, pri_chan->freq, 1);
+	if (err <= 0) {
+		wpa_printf(MSG_ERROR, "Primary frequency not allowed");
+		return err;
+	}
+	err = hostapd_is_usable_edmg(iface);
+	if (err <= 0)
+		return err;
+
+	if (!hostapd_is_usable_punct_bitmap(iface))
+		return 0;
+
+	if (!iface->conf->secondary_channel)
+		return 1;
+
+	err = hostapd_is_usable_chan(iface, iface->freq +
+				     iface->conf->secondary_channel * 20, 0);
+	if (err > 0) {
+		if (iface->conf->secondary_channel == 1 &&
+		    (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P))
+			return 1;
+		if (iface->conf->secondary_channel == -1 &&
+		    (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M))
+			return 1;
+	}
+	if (!iface->conf->ht40_plus_minus_allowed)
+		return err;
+
+	/* Both HT40+ and HT40- are set, pick a valid secondary channel */
+	secondary_freq = iface->freq + 20;
+	err = hostapd_is_usable_chan(iface, secondary_freq, 0);
+	if (err > 0 && (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) {
+		iface->conf->secondary_channel = 1;
+		return 1;
+	}
+
+	secondary_freq = iface->freq - 20;
+	err = hostapd_is_usable_chan(iface, secondary_freq, 0);
+	if (err > 0 && (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M)) {
+		iface->conf->secondary_channel = -1;
+		return 1;
+	}
+
+	return err;
+}
+
+
+static bool skip_mode(struct hostapd_iface *iface,
+		      struct hostapd_hw_modes *mode)
+{
+	int chan;
+
+	if (iface->freq > 0 && !hw_mode_get_channel(mode, iface->freq, &chan))
+		return true;
+
+	if (is_6ghz_op_class(iface->conf->op_class) && iface->freq == 0 &&
+	    (mode->mode != HOSTAPD_MODE_IEEE80211A ||
+	     mode->num_channels == 0 ||
+	     !is_6ghz_freq(mode->channels[0].freq)))
+		return true;
+
+	return false;
+}
+
+
+void hostapd_determine_mode(struct hostapd_iface *iface)
+{
+	int i;
+	enum hostapd_hw_mode target_mode;
+
+	if (iface->current_mode ||
+	    iface->conf->hw_mode != HOSTAPD_MODE_IEEE80211ANY)
+		return;
+
+	if (iface->freq < 4000)
+		target_mode = HOSTAPD_MODE_IEEE80211G;
+	else if (iface->freq > 50000)
+		target_mode = HOSTAPD_MODE_IEEE80211AD;
+	else
+		target_mode = HOSTAPD_MODE_IEEE80211A;
+
+	for (i = 0; i < iface->num_hw_features; i++) {
+		struct hostapd_hw_modes *mode;
+
+		mode = &iface->hw_features[i];
+		if (mode->mode == target_mode) {
+			if (skip_mode(iface, mode))
+				continue;
+
+			iface->current_mode = mode;
+			iface->conf->hw_mode = mode->mode;
+			break;
+		}
+	}
+
+	if (!iface->current_mode)
+		wpa_printf(MSG_ERROR, "ACS: Cannot decide mode");
+}
+
+
+static enum hostapd_chan_status
+hostapd_check_chans(struct hostapd_iface *iface)
+{
+	if (iface->freq) {
+		int err;
+
+		hostapd_determine_mode(iface);
+
+		err = hostapd_is_usable_chans(iface);
+		if (err <= 0) {
+			if (!err)
+				return HOSTAPD_CHAN_INVALID;
+			return HOSTAPD_CHAN_INVALID_NO_IR;
+		}
+		return HOSTAPD_CHAN_VALID;
+	}
+
+	/*
+	 * The user set channel=0 or channel=acs_survey
+	 * which is used to trigger ACS.
+	 */
+
+	switch (acs_init(iface)) {
+	case HOSTAPD_CHAN_ACS:
+		return HOSTAPD_CHAN_ACS;
+	case HOSTAPD_CHAN_INVALID_NO_IR:
+		return HOSTAPD_CHAN_INVALID_NO_IR;
+	case HOSTAPD_CHAN_VALID:
+	case HOSTAPD_CHAN_INVALID:
+	default:
+		return HOSTAPD_CHAN_INVALID;
+	}
+}
+
+
+static void hostapd_notify_bad_chans(struct hostapd_iface *iface)
+{
+	if (!iface->current_mode) {
+		hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_WARNING,
+			       "Hardware does not support configured mode");
+		return;
+	}
+	hostapd_logger(iface->bss[0], NULL,
+		       HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_WARNING,
+		       "Configured channel (%d) or frequency (%d) (secondary_channel=%d) not found from the channel list of the current mode (%d) %s",
+		       iface->conf->channel,
+		       iface->freq, iface->conf->secondary_channel,
+		       iface->current_mode->mode,
+		       hostapd_hw_mode_txt(iface->current_mode->mode));
+	hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_WARNING,
+		       "Hardware does not support configured channel");
+}
+
+
+int hostapd_acs_completed(struct hostapd_iface *iface, int err)
+{
+	int ret = -1;
+
+	if (err)
+		goto out;
+
+	switch (hostapd_check_chans(iface)) {
+	case HOSTAPD_CHAN_VALID:
+		iface->is_no_ir = false;
+		wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO,
+			ACS_EVENT_COMPLETED "freq=%d channel=%d",
+			iface->freq, iface->conf->channel);
+		break;
+	case HOSTAPD_CHAN_ACS:
+		wpa_printf(MSG_ERROR, "ACS error - reported complete, but no result available");
+		wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_FAILED);
+		hostapd_notify_bad_chans(iface);
+		goto out;
+	case HOSTAPD_CHAN_INVALID_NO_IR:
+		iface->is_no_ir = true;
+		/* fall through */
+	case HOSTAPD_CHAN_INVALID:
+	default:
+		wpa_printf(MSG_ERROR, "ACS picked unusable channels");
+		wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_FAILED);
+		hostapd_notify_bad_chans(iface);
+		goto out;
+	}
+
+	ret = hostapd_check_ht_capab(iface);
+	if (ret < 0)
+		goto out;
+	if (ret == 1) {
+		wpa_printf(MSG_DEBUG, "Interface initialization will be completed in a callback");
+		return 0;
+	}
+
+	ret = 0;
+out:
+	return hostapd_setup_interface_complete(iface, ret);
+}
+
+
+/**
+ * hostapd_select_hw_mode - Select the hardware mode
+ * @iface: Pointer to interface data.
+ * Returns: 0 on success, < 0 on failure
+ *
+ * Sets up the hardware mode, channel, rates, and passive scanning
+ * based on the configuration.
+ */
+int hostapd_select_hw_mode(struct hostapd_iface *iface)
+{
+	int i;
+
+	if (iface->num_hw_features < 1)
+		return -1;
+
+	if ((iface->conf->hw_mode == HOSTAPD_MODE_IEEE80211G ||
+	     iface->conf->ieee80211n || iface->conf->ieee80211ac ||
+	     iface->conf->ieee80211ax || iface->conf->ieee80211be) &&
+	    iface->conf->channel == 14) {
+		wpa_printf(MSG_INFO, "Disable OFDM/HT/VHT/HE/EHT on channel 14");
+		iface->conf->hw_mode = HOSTAPD_MODE_IEEE80211B;
+		iface->conf->ieee80211n = 0;
+		iface->conf->ieee80211ac = 0;
+		iface->conf->ieee80211ax = 0;
+		iface->conf->ieee80211be = 0;
+	}
+
+	iface->current_mode = NULL;
+	for (i = 0; i < iface->num_hw_features; i++) {
+		struct hostapd_hw_modes *mode = &iface->hw_features[i];
+
+		if (mode->mode == iface->conf->hw_mode) {
+			if (skip_mode(iface, mode))
+				continue;
+
+			iface->current_mode = mode;
+			break;
+		}
+	}
+
+	if (iface->current_mode == NULL) {
+		if ((iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) &&
+		    (iface->drv_flags & WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY)) {
+			wpa_printf(MSG_DEBUG,
+				   "Using offloaded hw_mode=any ACS");
+		} else if (!(iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) &&
+			   iface->conf->hw_mode == HOSTAPD_MODE_IEEE80211ANY) {
+			wpa_printf(MSG_DEBUG,
+				   "Using internal ACS for hw_mode=any");
+		} else {
+			wpa_printf(MSG_ERROR,
+				   "Hardware does not support configured mode");
+			hostapd_logger(iface->bss[0], NULL,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_WARNING,
+				       "Hardware does not support configured mode (%d) (hw_mode in hostapd.conf)",
+				       (int) iface->conf->hw_mode);
+			return -2;
+		}
+	}
+
+	switch (hostapd_check_chans(iface)) {
+	case HOSTAPD_CHAN_VALID:
+		iface->is_no_ir = false;
+		return 0;
+	case HOSTAPD_CHAN_ACS: /* ACS will run and later complete */
+		return 1;
+	case HOSTAPD_CHAN_INVALID_NO_IR:
+		iface->is_no_ir = true;
+		/* fall through */
+	case HOSTAPD_CHAN_INVALID:
+	default:
+		hostapd_notify_bad_chans(iface);
+		return -3;
+	}
+}
+
+
+const char * hostapd_hw_mode_txt(int mode)
+{
+	switch (mode) {
+	case HOSTAPD_MODE_IEEE80211A:
+		return "IEEE 802.11a";
+	case HOSTAPD_MODE_IEEE80211B:
+		return "IEEE 802.11b";
+	case HOSTAPD_MODE_IEEE80211G:
+		return "IEEE 802.11g";
+	case HOSTAPD_MODE_IEEE80211AD:
+		return "IEEE 802.11ad";
+	default:
+		return "UNKNOWN";
+	}
+}
+
+
+int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan)
+{
+	return hw_get_freq(hapd->iface->current_mode, chan);
+}
+
+
+int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq)
+{
+	int i, channel;
+	struct hostapd_hw_modes *mode;
+
+	if (hapd->iface->current_mode) {
+		channel = hw_get_chan(hapd->iface->current_mode->mode, freq,
+				      hapd->iface->hw_features,
+				      hapd->iface->num_hw_features);
+		if (channel)
+			return channel;
+	}
+
+	/* Check other available modes since the channel list for the current
+	 * mode did not include the specified frequency. */
+	if (!hapd->iface->hw_features)
+		return 0;
+	for (i = 0; i < hapd->iface->num_hw_features; i++) {
+		mode = &hapd->iface->hw_features[i];
+		channel = hw_get_chan(mode->mode, freq,
+				      hapd->iface->hw_features,
+				      hapd->iface->num_hw_features);
+		if (channel)
+			return channel;
+	}
+	return 0;
+}
+
+
+int hostapd_hw_skip_mode(struct hostapd_iface *iface,
+			 struct hostapd_hw_modes *mode)
+{
+	int i;
+
+	if (iface->current_mode)
+		return mode != iface->current_mode;
+	if (mode->mode != HOSTAPD_MODE_IEEE80211B)
+		return 0;
+	for (i = 0; i < iface->num_hw_features; i++) {
+		if (iface->hw_features[i].mode == HOSTAPD_MODE_IEEE80211G)
+			return 1;
+	}
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/hw_features.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/hw_features.h
new file mode 100644
index 0000000..092941f
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/hw_features.h
@@ -0,0 +1,101 @@
+/*
+ * hostapd / Hardware feature query and different modes
+ * Copyright 2002-2003, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2008-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef HW_FEATURES_H
+#define HW_FEATURES_H
+
+#ifdef NEED_AP_MLME
+void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
+			      size_t num_hw_features);
+int hostapd_get_hw_features(struct hostapd_iface *iface);
+int hostapd_acs_completed(struct hostapd_iface *iface, int err);
+int hostapd_select_hw_mode(struct hostapd_iface *iface);
+const char * hostapd_hw_mode_txt(int mode);
+int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan);
+int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq);
+int hostapd_check_ht_capab(struct hostapd_iface *iface);
+int hostapd_check_edmg_capab(struct hostapd_iface *iface);
+int hostapd_check_he_6ghz_capab(struct hostapd_iface *iface);
+int hostapd_prepare_rates(struct hostapd_iface *iface,
+			  struct hostapd_hw_modes *mode);
+void hostapd_stop_setup_timers(struct hostapd_iface *iface);
+int hostapd_hw_skip_mode(struct hostapd_iface *iface,
+			 struct hostapd_hw_modes *mode);
+void hostapd_determine_mode(struct hostapd_iface *iface);
+#else /* NEED_AP_MLME */
+static inline void
+hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
+			 size_t num_hw_features)
+{
+}
+
+static inline int hostapd_get_hw_features(struct hostapd_iface *iface)
+{
+	return -1;
+}
+
+static inline int hostapd_acs_completed(struct hostapd_iface *iface, int err)
+{
+	return -1;
+}
+
+static inline int hostapd_select_hw_mode(struct hostapd_iface *iface)
+{
+	return -100;
+}
+
+static inline const char * hostapd_hw_mode_txt(int mode)
+{
+	return "UNKNOWN";
+}
+
+static inline int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan)
+{
+	return -1;
+}
+
+static inline int hostapd_check_ht_capab(struct hostapd_iface *iface)
+{
+	return 0;
+}
+
+static inline int hostapd_check_edmg_capab(struct hostapd_iface *iface)
+{
+	return 0;
+}
+
+static inline int hostapd_prepare_rates(struct hostapd_iface *iface,
+					struct hostapd_hw_modes *mode)
+{
+	return 0;
+}
+
+static inline void hostapd_stop_setup_timers(struct hostapd_iface *iface)
+{
+}
+
+static inline int hostapd_hw_skip_mode(struct hostapd_iface *iface,
+				       struct hostapd_hw_modes *mode)
+{
+	return 0;
+}
+
+static inline int hostapd_check_he_6ghz_capab(struct hostapd_iface *iface)
+{
+	return 0;
+}
+
+static inline void hostapd_determine_mode(struct hostapd_iface *iface)
+{
+}
+
+#endif /* NEED_AP_MLME */
+
+#endif /* HW_FEATURES_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11.c
new file mode 100644
index 0000000..634e795
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11.c
@@ -0,0 +1,7950 @@
+/*
+ * hostapd / IEEE 802.11 Management
+ * Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#ifndef CONFIG_NATIVE_WINDOWS
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "crypto/crypto.h"
+#include "crypto/sha256.h"
+#include "crypto/sha384.h"
+#include "crypto/sha512.h"
+#include "crypto/random.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/wpa_ctrl.h"
+#include "common/sae.h"
+#include "common/dpp.h"
+#include "common/ocv.h"
+#include "common/wpa_common.h"
+#include "common/wpa_ctrl.h"
+#include "common/ptksa_cache.h"
+#include "radius/radius.h"
+#include "radius/radius_client.h"
+#include "p2p/p2p.h"
+#include "wps/wps.h"
+#include "fst/fst.h"
+#include "hostapd.h"
+#include "beacon.h"
+#include "ieee802_11_auth.h"
+#include "sta_info.h"
+#include "ieee802_1x.h"
+#include "wpa_auth.h"
+#include "pmksa_cache_auth.h"
+#include "wmm.h"
+#include "ap_list.h"
+#include "accounting.h"
+#include "ap_config.h"
+#include "ap_mlme.h"
+#include "p2p_hostapd.h"
+#include "ap_drv_ops.h"
+#include "wnm_ap.h"
+#include "hw_features.h"
+#include "ieee802_11.h"
+#include "dfs.h"
+#include "mbo_ap.h"
+#include "rrm.h"
+#include "taxonomy.h"
+#include "fils_hlp.h"
+#include "dpp_hostapd.h"
+#include "gas_query_ap.h"
+#include "comeback_token.h"
+#include "pasn/pasn_common.h"
+
+
+#ifdef CONFIG_FILS
+static struct wpabuf *
+prepare_auth_resp_fils(struct hostapd_data *hapd,
+		       struct sta_info *sta, u16 *resp,
+		       struct rsn_pmksa_cache_entry *pmksa,
+		       struct wpabuf *erp_resp,
+		       const u8 *msk, size_t msk_len,
+		       int *is_pub);
+#endif /* CONFIG_FILS */
+
+#ifdef CONFIG_PASN
+#ifdef CONFIG_FILS
+
+static void pasn_fils_auth_resp(struct hostapd_data *hapd,
+				struct sta_info *sta, u16 status,
+				struct wpabuf *erp_resp,
+				const u8 *msk, size_t msk_len);
+
+#endif /* CONFIG_FILS */
+#endif /* CONFIG_PASN */
+
+static void handle_auth(struct hostapd_data *hapd,
+			const struct ieee80211_mgmt *mgmt, size_t len,
+			int rssi, int from_queue);
+static int add_associated_sta(struct hostapd_data *hapd,
+			      struct sta_info *sta, int reassoc);
+
+
+u8 * hostapd_eid_multi_ap(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 multi_ap_val = 0;
+
+	if (!hapd->conf->multi_ap)
+		return eid;
+	if (hapd->conf->multi_ap & BACKHAUL_BSS)
+		multi_ap_val |= MULTI_AP_BACKHAUL_BSS;
+	if (hapd->conf->multi_ap & FRONTHAUL_BSS)
+		multi_ap_val |= MULTI_AP_FRONTHAUL_BSS;
+
+	return eid + add_multi_ap_ie(eid, 9, multi_ap_val);
+}
+
+
+u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 *pos = eid;
+	int i, num, count;
+	int h2e_required;
+
+	if (hapd->iface->current_rates == NULL)
+		return eid;
+
+	*pos++ = WLAN_EID_SUPP_RATES;
+	num = hapd->iface->num_rates;
+	if (hapd->iconf->ieee80211n && hapd->iconf->require_ht)
+		num++;
+	if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht)
+		num++;
+#ifdef CONFIG_IEEE80211AX
+	if (hapd->iconf->ieee80211ax && hapd->iconf->require_he)
+		num++;
+#endif /* CONFIG_IEEE80211AX */
+	h2e_required = (hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+			hostapd_sae_pw_id_in_use(hapd->conf) == 2) &&
+		hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK &&
+		wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt);
+	if (h2e_required)
+		num++;
+	if (num > 8) {
+		/* rest of the rates are encoded in Extended supported
+		 * rates element */
+		num = 8;
+	}
+
+	*pos++ = num;
+	for (i = 0, count = 0; i < hapd->iface->num_rates && count < num;
+	     i++) {
+		count++;
+		*pos = hapd->iface->current_rates[i].rate / 5;
+		if (hapd->iface->current_rates[i].flags & HOSTAPD_RATE_BASIC)
+			*pos |= 0x80;
+		pos++;
+	}
+
+	if (hapd->iconf->ieee80211n && hapd->iconf->require_ht && count < 8) {
+		count++;
+		*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY;
+	}
+
+	if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht && count < 8) {
+		count++;
+		*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY;
+	}
+
+#ifdef CONFIG_IEEE80211AX
+	if (hapd->iconf->ieee80211ax && hapd->iconf->require_he && count < 8) {
+		count++;
+		*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HE_PHY;
+	}
+#endif /* CONFIG_IEEE80211AX */
+
+	if (h2e_required && count < 8) {
+		count++;
+		*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY;
+	}
+
+	return pos;
+}
+
+
+u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 *pos = eid;
+	int i, num, count;
+	int h2e_required;
+
+	hapd->conf->xrates_supported = false;
+	if (hapd->iface->current_rates == NULL)
+		return eid;
+
+	num = hapd->iface->num_rates;
+	if (hapd->iconf->ieee80211n && hapd->iconf->require_ht)
+		num++;
+	if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht)
+		num++;
+#ifdef CONFIG_IEEE80211AX
+	if (hapd->iconf->ieee80211ax && hapd->iconf->require_he)
+		num++;
+#endif /* CONFIG_IEEE80211AX */
+	h2e_required = (hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+			hostapd_sae_pw_id_in_use(hapd->conf) == 2) &&
+		hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK &&
+		wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt);
+	if (h2e_required)
+		num++;
+	if (num <= 8)
+		return eid;
+	num -= 8;
+
+	*pos++ = WLAN_EID_EXT_SUPP_RATES;
+	*pos++ = num;
+	for (i = 0, count = 0; i < hapd->iface->num_rates && count < num + 8;
+	     i++) {
+		count++;
+		if (count <= 8)
+			continue; /* already in SuppRates IE */
+		*pos = hapd->iface->current_rates[i].rate / 5;
+		if (hapd->iface->current_rates[i].flags & HOSTAPD_RATE_BASIC)
+			*pos |= 0x80;
+		pos++;
+	}
+
+	if (hapd->iconf->ieee80211n && hapd->iconf->require_ht) {
+		count++;
+		if (count > 8)
+			*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY;
+	}
+
+	if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht) {
+		count++;
+		if (count > 8)
+			*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY;
+	}
+
+#ifdef CONFIG_IEEE80211AX
+	if (hapd->iconf->ieee80211ax && hapd->iconf->require_he) {
+		count++;
+		if (count > 8)
+			*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HE_PHY;
+	}
+#endif /* CONFIG_IEEE80211AX */
+
+	if (h2e_required) {
+		count++;
+		if (count > 8)
+			*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY;
+	}
+
+	hapd->conf->xrates_supported = true;
+	return pos;
+}
+
+
+u8 * hostapd_eid_rm_enabled_capab(struct hostapd_data *hapd, u8 *eid,
+				  size_t len)
+{
+	size_t i;
+
+	for (i = 0; i < RRM_CAPABILITIES_IE_LEN; i++) {
+		if (hapd->conf->radio_measurements[i])
+			break;
+	}
+
+	if (i == RRM_CAPABILITIES_IE_LEN || len < 2 + RRM_CAPABILITIES_IE_LEN)
+		return eid;
+
+	*eid++ = WLAN_EID_RRM_ENABLED_CAPABILITIES;
+	*eid++ = RRM_CAPABILITIES_IE_LEN;
+	os_memcpy(eid, hapd->conf->radio_measurements, RRM_CAPABILITIES_IE_LEN);
+
+	return eid + RRM_CAPABILITIES_IE_LEN;
+}
+
+
+u16 hostapd_own_capab_info(struct hostapd_data *hapd)
+{
+	int capab = WLAN_CAPABILITY_ESS;
+	int privacy = 0;
+	int dfs;
+	int i;
+
+	/* Check if any of configured channels require DFS */
+	dfs = hostapd_is_dfs_required(hapd->iface);
+	if (dfs < 0) {
+		wpa_printf(MSG_WARNING, "Failed to check if DFS is required; ret=%d",
+			   dfs);
+		dfs = 0;
+	}
+
+	if (hapd->iface->num_sta_no_short_preamble == 0 &&
+	    hapd->iconf->preamble == SHORT_PREAMBLE)
+		capab |= WLAN_CAPABILITY_SHORT_PREAMBLE;
+
+#ifdef CONFIG_WEP
+	privacy = hapd->conf->ssid.wep.keys_set;
+
+	if (hapd->conf->ieee802_1x &&
+	    (hapd->conf->default_wep_key_len ||
+	     hapd->conf->individual_wep_key_len))
+		privacy = 1;
+#endif /* CONFIG_WEP */
+
+	if (hapd->conf->wpa)
+		privacy = 1;
+
+#ifdef CONFIG_HS20
+	if (hapd->conf->osen)
+		privacy = 1;
+#endif /* CONFIG_HS20 */
+
+	if (privacy)
+		capab |= WLAN_CAPABILITY_PRIVACY;
+
+	if (hapd->iface->current_mode &&
+	    hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G &&
+	    hapd->iface->num_sta_no_short_slot_time == 0)
+		capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
+
+	/*
+	 * Currently, Spectrum Management capability bit is set when directly
+	 * requested in configuration by spectrum_mgmt_required or when AP is
+	 * running on DFS channel.
+	 * TODO: Also consider driver support for TPC to set Spectrum Mgmt bit
+	 */
+	if (hapd->iface->current_mode &&
+	    hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
+	    (hapd->iconf->spectrum_mgmt_required || dfs))
+		capab |= WLAN_CAPABILITY_SPECTRUM_MGMT;
+
+	for (i = 0; i < RRM_CAPABILITIES_IE_LEN; i++) {
+		if (hapd->conf->radio_measurements[i]) {
+			capab |= IEEE80211_CAP_RRM;
+			break;
+		}
+	}
+
+	return capab;
+}
+
+
+#ifdef CONFIG_WEP
+#ifndef CONFIG_NO_RC4
+static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta,
+			   u16 auth_transaction, const u8 *challenge,
+			   int iswep)
+{
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "authentication (shared key, transaction %d)",
+		       auth_transaction);
+
+	if (auth_transaction == 1) {
+		if (!sta->challenge) {
+			/* Generate a pseudo-random challenge */
+			u8 key[8];
+
+			sta->challenge = os_zalloc(WLAN_AUTH_CHALLENGE_LEN);
+			if (sta->challenge == NULL)
+				return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+			if (os_get_random(key, sizeof(key)) < 0) {
+				os_free(sta->challenge);
+				sta->challenge = NULL;
+				return WLAN_STATUS_UNSPECIFIED_FAILURE;
+			}
+
+			rc4_skip(key, sizeof(key), 0,
+				 sta->challenge, WLAN_AUTH_CHALLENGE_LEN);
+		}
+		return 0;
+	}
+
+	if (auth_transaction != 3)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+	/* Transaction 3 */
+	if (!iswep || !sta->challenge || !challenge ||
+	    os_memcmp_const(sta->challenge, challenge,
+			    WLAN_AUTH_CHALLENGE_LEN)) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO,
+			       "shared key authentication - invalid "
+			       "challenge-response");
+		return WLAN_STATUS_CHALLENGE_FAIL;
+	}
+
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "authentication OK (shared key)");
+	sta->flags |= WLAN_STA_AUTH;
+	wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
+	os_free(sta->challenge);
+	sta->challenge = NULL;
+
+	return 0;
+}
+#endif /* CONFIG_NO_RC4 */
+#endif /* CONFIG_WEP */
+
+
+static int send_auth_reply(struct hostapd_data *hapd, struct sta_info *sta,
+			   const u8 *dst, const u8 *bssid,
+			   u16 auth_alg, u16 auth_transaction, u16 resp,
+			   const u8 *ies, size_t ies_len, const char *dbg)
+{
+	struct ieee80211_mgmt *reply;
+	u8 *buf;
+	size_t rlen;
+	int reply_res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+	const u8 *sa = hapd->own_addr;
+	struct wpabuf *ml_resp = NULL;
+
+#ifdef CONFIG_IEEE80211BE
+	/*
+	 * Once a non-AP MLD is added to the driver, the addressing should use
+	 * the MLD MAC address. Thus, use the MLD address instead of translating
+	 * the addresses.
+	 */
+	if (hapd->conf->mld_ap && sta && sta->mld_info.mld_sta) {
+		sa = hapd->mld_addr;
+
+		ml_resp = hostapd_ml_auth_resp(hapd);
+		if (!ml_resp)
+			return -1;
+	}
+#endif /* CONFIG_IEEE80211BE */
+
+	rlen = IEEE80211_HDRLEN + sizeof(reply->u.auth) + ies_len;
+	if (ml_resp)
+		rlen += wpabuf_len(ml_resp);
+	buf = os_zalloc(rlen);
+	if (!buf) {
+		wpabuf_free(ml_resp);
+		return -1;
+	}
+
+	reply = (struct ieee80211_mgmt *) buf;
+	reply->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					    WLAN_FC_STYPE_AUTH);
+	os_memcpy(reply->da, dst, ETH_ALEN);
+	os_memcpy(reply->sa, sa, ETH_ALEN);
+	os_memcpy(reply->bssid, bssid, ETH_ALEN);
+
+	reply->u.auth.auth_alg = host_to_le16(auth_alg);
+	reply->u.auth.auth_transaction = host_to_le16(auth_transaction);
+	reply->u.auth.status_code = host_to_le16(resp);
+
+	if (ies && ies_len)
+		os_memcpy(reply->u.auth.variable, ies, ies_len);
+
+#ifdef CONFIG_IEEE80211BE
+	if (ml_resp)
+		os_memcpy(reply->u.auth.variable + ies_len,
+			  wpabuf_head(ml_resp), wpabuf_len(ml_resp));
+
+	wpabuf_free(ml_resp);
+#endif /* CONFIG_IEEE80211BE */
+
+	wpa_printf(MSG_DEBUG, "authentication reply: STA=" MACSTR
+		   " auth_alg=%d auth_transaction=%d resp=%d (IE len=%lu) (dbg=%s)",
+		   MAC2STR(dst), auth_alg, auth_transaction,
+		   resp, (unsigned long) ies_len, dbg);
+#ifdef CONFIG_TESTING_OPTIONS
+#ifdef CONFIG_SAE
+	if (hapd->conf->sae_confirm_immediate == 2 &&
+	    auth_alg == WLAN_AUTH_SAE) {
+		if (auth_transaction == 1 && sta &&
+		    (resp == WLAN_STATUS_SUCCESS ||
+		     resp == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
+		     resp == WLAN_STATUS_SAE_PK)) {
+			wpa_printf(MSG_DEBUG,
+				   "TESTING: Postpone SAE Commit transmission until Confirm is ready");
+			os_free(sta->sae_postponed_commit);
+			sta->sae_postponed_commit = buf;
+			sta->sae_postponed_commit_len = rlen;
+			return WLAN_STATUS_SUCCESS;
+		}
+
+		if (auth_transaction == 2 && sta && sta->sae_postponed_commit) {
+			wpa_printf(MSG_DEBUG,
+				   "TESTING: Send postponed SAE Commit first, immediately followed by SAE Confirm");
+			if (hostapd_drv_send_mlme(hapd,
+						  sta->sae_postponed_commit,
+						  sta->sae_postponed_commit_len,
+						  0, NULL, 0, 0) < 0)
+				wpa_printf(MSG_INFO, "send_auth_reply: send failed");
+			os_free(sta->sae_postponed_commit);
+			sta->sae_postponed_commit = NULL;
+			sta->sae_postponed_commit_len = 0;
+		}
+	}
+#endif /* CONFIG_SAE */
+#endif /* CONFIG_TESTING_OPTIONS */
+	if (hostapd_drv_send_mlme(hapd, reply, rlen, 0, NULL, 0, 0) < 0)
+		wpa_printf(MSG_INFO, "send_auth_reply: send failed");
+	else
+		reply_res = WLAN_STATUS_SUCCESS;
+
+	os_free(buf);
+
+	return reply_res;
+}
+
+
+#ifdef CONFIG_IEEE80211R_AP
+static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid,
+				  u16 auth_transaction, u16 status,
+				  const u8 *ies, size_t ies_len)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+	int reply_res;
+
+	reply_res = send_auth_reply(hapd, NULL, dst, bssid, WLAN_AUTH_FT,
+				    auth_transaction, status, ies, ies_len,
+				    "auth-ft-finish");
+
+	sta = ap_get_sta(hapd, dst);
+	if (sta == NULL)
+		return;
+
+	if (sta->added_unassoc && (reply_res != WLAN_STATUS_SUCCESS ||
+				   status != WLAN_STATUS_SUCCESS)) {
+		hostapd_drv_sta_remove(hapd, sta->addr);
+		sta->added_unassoc = 0;
+		return;
+	}
+
+	if (status != WLAN_STATUS_SUCCESS)
+		return;
+
+	hostapd_logger(hapd, dst, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_DEBUG, "authentication OK (FT)");
+	sta->flags |= WLAN_STA_AUTH;
+	mlme_authenticate_indication(hapd, sta);
+}
+#endif /* CONFIG_IEEE80211R_AP */
+
+
+#ifdef CONFIG_SAE
+
+static void sae_set_state(struct sta_info *sta, enum sae_state state,
+			  const char *reason)
+{
+	wpa_printf(MSG_DEBUG, "SAE: State %s -> %s for peer " MACSTR " (%s)",
+		   sae_state_txt(sta->sae->state), sae_state_txt(state),
+		   MAC2STR(sta->addr), reason);
+	sta->sae->state = state;
+}
+
+
+static const char * sae_get_password(struct hostapd_data *hapd,
+				     struct sta_info *sta,
+				     const char *rx_id,
+				     struct sae_password_entry **pw_entry,
+				     struct sae_pt **s_pt,
+				     const struct sae_pk **s_pk)
+{
+	const char *password = NULL;
+	struct sae_password_entry *pw;
+	struct sae_pt *pt = NULL;
+	const struct sae_pk *pk = NULL;
+	struct hostapd_sta_wpa_psk_short *psk = NULL;
+
+	for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
+		if (!is_broadcast_ether_addr(pw->peer_addr) &&
+		    os_memcmp(pw->peer_addr, sta->addr, ETH_ALEN) != 0)
+			continue;
+		if ((rx_id && !pw->identifier) || (!rx_id && pw->identifier))
+			continue;
+		if (rx_id && pw->identifier &&
+		    os_strcmp(rx_id, pw->identifier) != 0)
+			continue;
+		password = pw->password;
+		pt = pw->pt;
+		if (!(hapd->conf->mesh & MESH_ENABLED))
+			pk = pw->pk;
+		break;
+	}
+	if (!password) {
+		password = hapd->conf->ssid.wpa_passphrase;
+		pt = hapd->conf->ssid.pt;
+	}
+
+	if (!password) {
+		for (psk = sta->psk; psk; psk = psk->next) {
+			if (psk->is_passphrase) {
+				password = psk->passphrase;
+				break;
+			}
+		}
+	}
+
+	if (pw_entry)
+		*pw_entry = pw;
+	if (s_pt)
+		*s_pt = pt;
+	if (s_pk)
+		*s_pk = pk;
+
+	return password;
+}
+
+
+static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd,
+					     struct sta_info *sta, int update,
+					     int status_code)
+{
+	struct wpabuf *buf;
+	const char *password = NULL;
+	struct sae_password_entry *pw;
+	const char *rx_id = NULL;
+	int use_pt = 0;
+	struct sae_pt *pt = NULL;
+	const struct sae_pk *pk = NULL;
+	const u8 *own_addr = hapd->own_addr;
+
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->conf->mld_ap && sta->mld_info.mld_sta)
+		own_addr = hapd->mld_addr;
+#endif /* CONFIG_IEEE80211BE */
+
+	if (sta->sae->tmp) {
+		rx_id = sta->sae->tmp->pw_id;
+		use_pt = sta->sae->h2e;
+#ifdef CONFIG_SAE_PK
+		os_memcpy(sta->sae->tmp->own_addr, own_addr, ETH_ALEN);
+		os_memcpy(sta->sae->tmp->peer_addr, sta->addr, ETH_ALEN);
+#endif /* CONFIG_SAE_PK */
+	}
+
+	if (rx_id && hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
+		use_pt = 1;
+	else if (status_code == WLAN_STATUS_SUCCESS)
+		use_pt = 0;
+	else if (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
+		 status_code == WLAN_STATUS_SAE_PK)
+		use_pt = 1;
+
+	password = sae_get_password(hapd, sta, rx_id, &pw, &pt, &pk);
+	if (!password || (use_pt && !pt)) {
+		wpa_printf(MSG_DEBUG, "SAE: No password available");
+		return NULL;
+	}
+
+	if (update && use_pt &&
+	    sae_prepare_commit_pt(sta->sae, pt, own_addr, sta->addr,
+				  NULL, pk) < 0)
+		return NULL;
+
+	if (update && !use_pt &&
+	    sae_prepare_commit(own_addr, sta->addr,
+			       (u8 *) password, os_strlen(password),
+			       sta->sae) < 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE");
+		return NULL;
+	}
+
+	if (pw && pw->vlan_id) {
+		if (!sta->sae->tmp) {
+			wpa_printf(MSG_INFO,
+				   "SAE: No temporary data allocated - cannot store VLAN ID");
+			return NULL;
+		}
+		sta->sae->tmp->vlan_id = pw->vlan_id;
+	}
+
+	buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN +
+			   (rx_id ? 3 + os_strlen(rx_id) : 0));
+	if (buf &&
+	    sae_write_commit(sta->sae, buf, sta->sae->tmp ?
+			     sta->sae->tmp->anti_clogging_token : NULL,
+			     rx_id) < 0) {
+		wpabuf_free(buf);
+		buf = NULL;
+	}
+
+	return buf;
+}
+
+
+static struct wpabuf * auth_build_sae_confirm(struct hostapd_data *hapd,
+					      struct sta_info *sta)
+{
+	struct wpabuf *buf;
+
+	buf = wpabuf_alloc(SAE_CONFIRM_MAX_LEN);
+	if (buf == NULL)
+		return NULL;
+
+#ifdef CONFIG_SAE_PK
+#ifdef CONFIG_TESTING_OPTIONS
+	if (sta->sae->tmp)
+		sta->sae->tmp->omit_pk_elem = hapd->conf->sae_pk_omit;
+#endif /* CONFIG_TESTING_OPTIONS */
+#endif /* CONFIG_SAE_PK */
+
+	if (sae_write_confirm(sta->sae, buf) < 0) {
+		wpabuf_free(buf);
+		return NULL;
+	}
+
+	return buf;
+}
+
+
+static int auth_sae_send_commit(struct hostapd_data *hapd,
+				struct sta_info *sta,
+				const u8 *bssid, int update, int status_code)
+{
+	struct wpabuf *data;
+	int reply_res;
+	u16 status;
+
+	data = auth_build_sae_commit(hapd, sta, update, status_code);
+	if (!data && sta->sae->tmp && sta->sae->tmp->pw_id)
+		return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
+	if (data == NULL)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+	if (sta->sae->tmp && sta->sae->pk)
+		status = WLAN_STATUS_SAE_PK;
+	else if (sta->sae->tmp && sta->sae->h2e)
+		status = WLAN_STATUS_SAE_HASH_TO_ELEMENT;
+	else
+		status = WLAN_STATUS_SUCCESS;
+#ifdef CONFIG_TESTING_OPTIONS
+	if (hapd->conf->sae_commit_status >= 0 &&
+	    hapd->conf->sae_commit_status != status) {
+		wpa_printf(MSG_INFO,
+			   "TESTING: Override SAE commit status code %u --> %d",
+			   status, hapd->conf->sae_commit_status);
+		status = hapd->conf->sae_commit_status;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+	reply_res = send_auth_reply(hapd, sta, sta->addr, bssid,
+				    WLAN_AUTH_SAE, 1,
+				    status, wpabuf_head(data),
+				    wpabuf_len(data), "sae-send-commit");
+
+	wpabuf_free(data);
+
+	return reply_res;
+}
+
+
+static int auth_sae_send_confirm(struct hostapd_data *hapd,
+				 struct sta_info *sta,
+				 const u8 *bssid)
+{
+	struct wpabuf *data;
+	int reply_res;
+
+	data = auth_build_sae_confirm(hapd, sta);
+	if (data == NULL)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+	reply_res = send_auth_reply(hapd, sta, sta->addr, bssid,
+				    WLAN_AUTH_SAE, 2,
+				    WLAN_STATUS_SUCCESS, wpabuf_head(data),
+				    wpabuf_len(data), "sae-send-confirm");
+
+	wpabuf_free(data);
+
+	return reply_res;
+}
+
+#endif /* CONFIG_SAE */
+
+
+#if defined(CONFIG_SAE) || defined(CONFIG_PASN)
+
+static int use_anti_clogging(struct hostapd_data *hapd)
+{
+	struct sta_info *sta;
+	unsigned int open = 0;
+
+	if (hapd->conf->anti_clogging_threshold == 0)
+		return 1;
+
+	for (sta = hapd->sta_list; sta; sta = sta->next) {
+#ifdef CONFIG_SAE
+		if (sta->sae &&
+		    (sta->sae->state == SAE_COMMITTED ||
+		     sta->sae->state == SAE_CONFIRMED))
+			open++;
+#endif /* CONFIG_SAE */
+#ifdef CONFIG_PASN
+		if (sta->pasn && sta->pasn->ecdh)
+			open++;
+#endif /* CONFIG_PASN */
+		if (open >= hapd->conf->anti_clogging_threshold)
+			return 1;
+	}
+
+#ifdef CONFIG_SAE
+	/* In addition to already existing open SAE sessions, check whether
+	 * there are enough pending commit messages in the processing queue to
+	 * potentially result in too many open sessions. */
+	if (open + dl_list_len(&hapd->sae_commit_queue) >=
+	    hapd->conf->anti_clogging_threshold)
+		return 1;
+#endif /* CONFIG_SAE */
+
+	return 0;
+}
+
+#endif /* defined(CONFIG_SAE) || defined(CONFIG_PASN) */
+
+
+#ifdef CONFIG_SAE
+
+static int sae_check_big_sync(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	if (sta->sae->sync > hapd->conf->sae_sync) {
+		sae_set_state(sta, SAE_NOTHING, "Sync > dot11RSNASAESync");
+		sta->sae->sync = 0;
+		return -1;
+	}
+	return 0;
+}
+
+
+static void auth_sae_retransmit_timer(void *eloop_ctx, void *eloop_data)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct sta_info *sta = eloop_data;
+	int ret;
+
+	if (sae_check_big_sync(hapd, sta))
+		return;
+	sta->sae->sync++;
+	wpa_printf(MSG_DEBUG, "SAE: Auth SAE retransmit timer for " MACSTR
+		   " (sync=%d state=%s)",
+		   MAC2STR(sta->addr), sta->sae->sync,
+		   sae_state_txt(sta->sae->state));
+
+	switch (sta->sae->state) {
+	case SAE_COMMITTED:
+		ret = auth_sae_send_commit(hapd, sta, hapd->own_addr, 0, -1);
+		eloop_register_timeout(0,
+				       hapd->dot11RSNASAERetransPeriod * 1000,
+				       auth_sae_retransmit_timer, hapd, sta);
+		break;
+	case SAE_CONFIRMED:
+		ret = auth_sae_send_confirm(hapd, sta, hapd->own_addr);
+		eloop_register_timeout(0,
+				       hapd->dot11RSNASAERetransPeriod * 1000,
+				       auth_sae_retransmit_timer, hapd, sta);
+		break;
+	default:
+		ret = -1;
+		break;
+	}
+
+	if (ret != WLAN_STATUS_SUCCESS)
+		wpa_printf(MSG_INFO, "SAE: Failed to retransmit: ret=%d", ret);
+}
+
+
+void sae_clear_retransmit_timer(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	eloop_cancel_timeout(auth_sae_retransmit_timer, hapd, sta);
+}
+
+
+static void sae_set_retransmit_timer(struct hostapd_data *hapd,
+				     struct sta_info *sta)
+{
+	if (!(hapd->conf->mesh & MESH_ENABLED))
+		return;
+
+	eloop_cancel_timeout(auth_sae_retransmit_timer, hapd, sta);
+	eloop_register_timeout(0, hapd->dot11RSNASAERetransPeriod * 1000,
+			       auth_sae_retransmit_timer, hapd, sta);
+}
+
+
+static void sae_sme_send_external_auth_status(struct hostapd_data *hapd,
+					      struct sta_info *sta, u16 status)
+{
+	struct external_auth params;
+
+	os_memset(&params, 0, sizeof(params));
+	params.status = status;
+	params.bssid = sta->addr;
+	if (status == WLAN_STATUS_SUCCESS && sta->sae &&
+	    !hapd->conf->disable_pmksa_caching)
+		params.pmkid = sta->sae->pmkid;
+
+	hostapd_drv_send_external_auth_status(hapd, &params);
+}
+
+
+void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta)
+{
+#ifndef CONFIG_NO_VLAN
+	struct vlan_description vlan_desc;
+
+	if (sta->sae->tmp && sta->sae->tmp->vlan_id > 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Assign STA " MACSTR
+			   " to VLAN ID %d",
+			   MAC2STR(sta->addr), sta->sae->tmp->vlan_id);
+
+		os_memset(&vlan_desc, 0, sizeof(vlan_desc));
+		vlan_desc.notempty = 1;
+		vlan_desc.untagged = sta->sae->tmp->vlan_id;
+		if (!hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) {
+			wpa_printf(MSG_INFO,
+				   "Invalid VLAN ID %d in sae_password",
+				   sta->sae->tmp->vlan_id);
+			return;
+		}
+
+		if (ap_sta_set_vlan(hapd, sta, &vlan_desc) < 0 ||
+		    ap_sta_bind_vlan(hapd, sta) < 0) {
+			wpa_printf(MSG_INFO,
+				   "Failed to assign VLAN ID %d from sae_password to "
+				   MACSTR, sta->sae->tmp->vlan_id,
+				   MAC2STR(sta->addr));
+			return;
+		}
+	}
+#endif /* CONFIG_NO_VLAN */
+
+	sta->flags |= WLAN_STA_AUTH;
+	sta->auth_alg = WLAN_AUTH_SAE;
+	mlme_authenticate_indication(hapd, sta);
+	wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
+	sae_set_state(sta, SAE_ACCEPTED, "Accept Confirm");
+	crypto_bignum_deinit(sta->sae->peer_commit_scalar_accepted, 0);
+	sta->sae->peer_commit_scalar_accepted = sta->sae->peer_commit_scalar;
+	sta->sae->peer_commit_scalar = NULL;
+	wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr,
+			       sta->sae->pmk, sta->sae->pmk_len,
+			       sta->sae->pmkid, sta->sae->akmp);
+	sae_sme_send_external_auth_status(hapd, sta, WLAN_STATUS_SUCCESS);
+}
+
+
+static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
+		       const u8 *bssid, u16 auth_transaction, u16 status_code,
+		       int allow_reuse, int *sta_removed)
+{
+	int ret;
+
+	*sta_removed = 0;
+
+	if (auth_transaction != 1 && auth_transaction != 2)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+	wpa_printf(MSG_DEBUG, "SAE: Peer " MACSTR " state=%s auth_trans=%u",
+		   MAC2STR(sta->addr), sae_state_txt(sta->sae->state),
+		   auth_transaction);
+	switch (sta->sae->state) {
+	case SAE_NOTHING:
+		if (auth_transaction == 1) {
+			if (sta->sae->tmp) {
+				sta->sae->h2e =
+					(status_code ==
+					 WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
+					 status_code == WLAN_STATUS_SAE_PK);
+				sta->sae->pk =
+					status_code == WLAN_STATUS_SAE_PK;
+			}
+			ret = auth_sae_send_commit(hapd, sta, bssid,
+						   !allow_reuse, status_code);
+			if (ret)
+				return ret;
+			sae_set_state(sta, SAE_COMMITTED, "Sent Commit");
+
+			if (sae_process_commit(sta->sae) < 0)
+				return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+			/*
+			 * In mesh case, both Commit and Confirm are sent
+			 * immediately. In infrastructure BSS, by default, only
+			 * a single Authentication frame (Commit) is expected
+			 * from the AP here and the second one (Confirm) will
+			 * be sent once the STA has sent its second
+			 * Authentication frame (Confirm). This behavior can be
+			 * overridden with explicit configuration so that the
+			 * infrastructure BSS case sends both frames together.
+			 */
+			if ((hapd->conf->mesh & MESH_ENABLED) ||
+			    hapd->conf->sae_confirm_immediate) {
+				/*
+				 * Send both Commit and Confirm immediately
+				 * based on SAE finite state machine
+				 * Nothing -> Confirm transition.
+				 */
+				ret = auth_sae_send_confirm(hapd, sta, bssid);
+				if (ret)
+					return ret;
+				sae_set_state(sta, SAE_CONFIRMED,
+					      "Sent Confirm (mesh)");
+			} else {
+				/*
+				 * For infrastructure BSS, send only the Commit
+				 * message now to get alternating sequence of
+				 * Authentication frames between the AP and STA.
+				 * Confirm will be sent in
+				 * Committed -> Confirmed/Accepted transition
+				 * when receiving Confirm from STA.
+				 */
+			}
+			sta->sae->sync = 0;
+			sae_set_retransmit_timer(hapd, sta);
+		} else {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "SAE confirm before commit");
+		}
+		break;
+	case SAE_COMMITTED:
+		sae_clear_retransmit_timer(hapd, sta);
+		if (auth_transaction == 1) {
+			if (sae_process_commit(sta->sae) < 0)
+				return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+			ret = auth_sae_send_confirm(hapd, sta, bssid);
+			if (ret)
+				return ret;
+			sae_set_state(sta, SAE_CONFIRMED, "Sent Confirm");
+			sta->sae->sync = 0;
+			sae_set_retransmit_timer(hapd, sta);
+		} else if (hapd->conf->mesh & MESH_ENABLED) {
+			/*
+			 * In mesh case, follow SAE finite state machine and
+			 * send Commit now, if sync count allows.
+			 */
+			if (sae_check_big_sync(hapd, sta))
+				return WLAN_STATUS_SUCCESS;
+			sta->sae->sync++;
+
+			ret = auth_sae_send_commit(hapd, sta, bssid, 0,
+						   status_code);
+			if (ret)
+				return ret;
+
+			sae_set_retransmit_timer(hapd, sta);
+		} else {
+			/*
+			 * For instructure BSS, send the postponed Confirm from
+			 * Nothing -> Confirmed transition that was reduced to
+			 * Nothing -> Committed above.
+			 */
+			ret = auth_sae_send_confirm(hapd, sta, bssid);
+			if (ret)
+				return ret;
+
+			sae_set_state(sta, SAE_CONFIRMED, "Sent Confirm");
+
+			/*
+			 * Since this was triggered on Confirm RX, run another
+			 * step to get to Accepted without waiting for
+			 * additional events.
+			 */
+			return sae_sm_step(hapd, sta, bssid, auth_transaction,
+					   WLAN_STATUS_SUCCESS, 0, sta_removed);
+		}
+		break;
+	case SAE_CONFIRMED:
+		sae_clear_retransmit_timer(hapd, sta);
+		if (auth_transaction == 1) {
+			if (sae_check_big_sync(hapd, sta))
+				return WLAN_STATUS_SUCCESS;
+			sta->sae->sync++;
+
+			ret = auth_sae_send_commit(hapd, sta, bssid, 1,
+						   status_code);
+			if (ret)
+				return ret;
+
+			if (sae_process_commit(sta->sae) < 0)
+				return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+			ret = auth_sae_send_confirm(hapd, sta, bssid);
+			if (ret)
+				return ret;
+
+			sae_set_retransmit_timer(hapd, sta);
+		} else {
+			sta->sae->send_confirm = 0xffff;
+			sae_accept_sta(hapd, sta);
+		}
+		break;
+	case SAE_ACCEPTED:
+		if (auth_transaction == 1 &&
+		    (hapd->conf->mesh & MESH_ENABLED)) {
+			wpa_printf(MSG_DEBUG, "SAE: remove the STA (" MACSTR
+				   ") doing reauthentication",
+				   MAC2STR(sta->addr));
+			wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
+			ap_free_sta(hapd, sta);
+			*sta_removed = 1;
+		} else if (auth_transaction == 1) {
+			wpa_printf(MSG_DEBUG, "SAE: Start reauthentication");
+			ret = auth_sae_send_commit(hapd, sta, bssid, 1,
+						   status_code);
+			if (ret)
+				return ret;
+			sae_set_state(sta, SAE_COMMITTED, "Sent Commit");
+
+			if (sae_process_commit(sta->sae) < 0)
+				return WLAN_STATUS_UNSPECIFIED_FAILURE;
+			sta->sae->sync = 0;
+			sae_set_retransmit_timer(hapd, sta);
+		} else {
+			if (sae_check_big_sync(hapd, sta))
+				return WLAN_STATUS_SUCCESS;
+			sta->sae->sync++;
+
+			ret = auth_sae_send_confirm(hapd, sta, bssid);
+			sae_clear_temp_data(sta->sae);
+			if (ret)
+				return ret;
+		}
+		break;
+	default:
+		wpa_printf(MSG_ERROR, "SAE: invalid state %d",
+			   sta->sae->state);
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+static void sae_pick_next_group(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	struct sae_data *sae = sta->sae;
+	int i, *groups = hapd->conf->sae_groups;
+	int default_groups[] = { 19, 0 };
+
+	if (sae->state != SAE_COMMITTED)
+		return;
+
+	wpa_printf(MSG_DEBUG, "SAE: Previously selected group: %d", sae->group);
+
+	if (!groups)
+		groups = default_groups;
+	for (i = 0; groups[i] > 0; i++) {
+		if (sae->group == groups[i])
+			break;
+	}
+
+	if (groups[i] <= 0) {
+		wpa_printf(MSG_DEBUG,
+			   "SAE: Previously selected group not found from the current configuration");
+		return;
+	}
+
+	for (;;) {
+		i++;
+		if (groups[i] <= 0) {
+			wpa_printf(MSG_DEBUG,
+				   "SAE: No alternative group enabled");
+			return;
+		}
+
+		if (sae_set_group(sae, groups[i]) < 0)
+			continue;
+
+		break;
+	}
+	wpa_printf(MSG_DEBUG, "SAE: Selected new group: %d", groups[i]);
+}
+
+
+static int sae_status_success(struct hostapd_data *hapd, u16 status_code)
+{
+	enum sae_pwe sae_pwe = hapd->conf->sae_pwe;
+	int id_in_use;
+	bool sae_pk = false;
+
+	id_in_use = hostapd_sae_pw_id_in_use(hapd->conf);
+	if (id_in_use == 2 && sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
+		sae_pwe = SAE_PWE_HASH_TO_ELEMENT;
+	else if (id_in_use == 1 && sae_pwe == SAE_PWE_HUNT_AND_PECK)
+		sae_pwe = SAE_PWE_BOTH;
+#ifdef CONFIG_SAE_PK
+	sae_pk = hostapd_sae_pk_in_use(hapd->conf);
+	if (sae_pwe == SAE_PWE_HUNT_AND_PECK && sae_pk)
+		sae_pwe = SAE_PWE_BOTH;
+#endif /* CONFIG_SAE_PK */
+	if (sae_pwe == SAE_PWE_HUNT_AND_PECK &&
+	    (hapd->conf->wpa_key_mgmt &
+	     (WPA_KEY_MGMT_SAE_EXT_KEY | WPA_KEY_MGMT_FT_SAE_EXT_KEY)))
+		sae_pwe = SAE_PWE_BOTH;
+
+	return ((sae_pwe == SAE_PWE_HUNT_AND_PECK ||
+		 sae_pwe == SAE_PWE_FORCE_HUNT_AND_PECK) &&
+		status_code == WLAN_STATUS_SUCCESS) ||
+		(sae_pwe == SAE_PWE_HASH_TO_ELEMENT &&
+		 (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
+		  (sae_pk && status_code == WLAN_STATUS_SAE_PK))) ||
+		(sae_pwe == SAE_PWE_BOTH &&
+		 (status_code == WLAN_STATUS_SUCCESS ||
+		  status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
+		  (sae_pk && status_code == WLAN_STATUS_SAE_PK)));
+}
+
+
+static int sae_is_group_enabled(struct hostapd_data *hapd, int group)
+{
+	int *groups = hapd->conf->sae_groups;
+	int default_groups[] = { 19, 0 };
+	int i;
+
+	if (!groups)
+		groups = default_groups;
+
+	for (i = 0; groups[i] > 0; i++) {
+		if (groups[i] == group)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static int check_sae_rejected_groups(struct hostapd_data *hapd,
+				     struct sae_data *sae)
+{
+	const struct wpabuf *groups;
+	size_t i, count;
+	const u8 *pos;
+
+	if (!sae->tmp)
+		return 0;
+	groups = sae->tmp->peer_rejected_groups;
+	if (!groups)
+		return 0;
+
+	pos = wpabuf_head(groups);
+	count = wpabuf_len(groups) / 2;
+	for (i = 0; i < count; i++) {
+		int enabled;
+		u16 group;
+
+		group = WPA_GET_LE16(pos);
+		pos += 2;
+		enabled = sae_is_group_enabled(hapd, group);
+		wpa_printf(MSG_DEBUG, "SAE: Rejected group %u is %s",
+			   group, enabled ? "enabled" : "disabled");
+		if (enabled)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
+			    const struct ieee80211_mgmt *mgmt, size_t len,
+			    u16 auth_transaction, u16 status_code)
+{
+	int resp = WLAN_STATUS_SUCCESS;
+	struct wpabuf *data = NULL;
+	int *groups = hapd->conf->sae_groups;
+	int default_groups[] = { 19, 0 };
+	const u8 *pos, *end;
+	int sta_removed = 0;
+	bool success_status;
+
+	if (!groups)
+		groups = default_groups;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (hapd->conf->sae_reflection_attack && auth_transaction == 1) {
+		wpa_printf(MSG_DEBUG, "SAE: TESTING - reflection attack");
+		pos = mgmt->u.auth.variable;
+		end = ((const u8 *) mgmt) + len;
+		resp = status_code;
+		send_auth_reply(hapd, sta, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
+				auth_transaction, resp, pos, end - pos,
+				"auth-sae-reflection-attack");
+		goto remove_sta;
+	}
+
+	if (hapd->conf->sae_commit_override && auth_transaction == 1) {
+		wpa_printf(MSG_DEBUG, "SAE: TESTING - commit override");
+		send_auth_reply(hapd, sta, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
+				auth_transaction, resp,
+				wpabuf_head(hapd->conf->sae_commit_override),
+				wpabuf_len(hapd->conf->sae_commit_override),
+				"sae-commit-override");
+		goto remove_sta;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+	if (!sta->sae) {
+		if (auth_transaction != 1 ||
+		    !sae_status_success(hapd, status_code)) {
+			wpa_printf(MSG_DEBUG, "SAE: Unexpected Status Code %u",
+				   status_code);
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto reply;
+		}
+		sta->sae = os_zalloc(sizeof(*sta->sae));
+		if (!sta->sae) {
+			resp = -1;
+			goto remove_sta;
+		}
+		sae_set_state(sta, SAE_NOTHING, "Init");
+		sta->sae->sync = 0;
+	}
+
+	if (sta->mesh_sae_pmksa_caching) {
+		wpa_printf(MSG_DEBUG,
+			   "SAE: Cancel use of mesh PMKSA caching because peer starts SAE authentication");
+		wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
+		sta->mesh_sae_pmksa_caching = 0;
+	}
+
+	if (auth_transaction == 1) {
+		const u8 *token = NULL;
+		size_t token_len = 0;
+		int allow_reuse = 0;
+
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "start SAE authentication (RX commit, status=%u (%s))",
+			       status_code, status2str(status_code));
+
+		if ((hapd->conf->mesh & MESH_ENABLED) &&
+		    status_code == WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ &&
+		    sta->sae->tmp) {
+			pos = mgmt->u.auth.variable;
+			end = ((const u8 *) mgmt) + len;
+			if (pos + sizeof(le16) > end) {
+				wpa_printf(MSG_ERROR,
+					   "SAE: Too short anti-clogging token request");
+				resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+				goto reply;
+			}
+			resp = sae_group_allowed(sta->sae, groups,
+						 WPA_GET_LE16(pos));
+			if (resp != WLAN_STATUS_SUCCESS) {
+				wpa_printf(MSG_ERROR,
+					   "SAE: Invalid group in anti-clogging token request");
+				goto reply;
+			}
+			pos += sizeof(le16);
+
+			wpabuf_free(sta->sae->tmp->anti_clogging_token);
+			sta->sae->tmp->anti_clogging_token =
+				wpabuf_alloc_copy(pos, end - pos);
+			if (sta->sae->tmp->anti_clogging_token == NULL) {
+				wpa_printf(MSG_ERROR,
+					   "SAE: Failed to alloc for anti-clogging token");
+				resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+				goto remove_sta;
+			}
+
+			/*
+			 * IEEE Std 802.11-2012, 11.3.8.6.4: If the Status code
+			 * is 76, a new Commit Message shall be constructed
+			 * with the Anti-Clogging Token from the received
+			 * Authentication frame, and the commit-scalar and
+			 * COMMIT-ELEMENT previously sent.
+			 */
+			resp = auth_sae_send_commit(hapd, sta, mgmt->bssid, 0,
+						    status_code);
+			if (resp != WLAN_STATUS_SUCCESS) {
+				wpa_printf(MSG_ERROR,
+					   "SAE: Failed to send commit message");
+				goto remove_sta;
+			}
+			sae_set_state(sta, SAE_COMMITTED,
+				      "Sent Commit (anti-clogging token case in mesh)");
+			sta->sae->sync = 0;
+			sae_set_retransmit_timer(hapd, sta);
+			return;
+		}
+
+		if ((hapd->conf->mesh & MESH_ENABLED) &&
+		    status_code ==
+		    WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED &&
+		    sta->sae->tmp) {
+			wpa_printf(MSG_DEBUG,
+				   "SAE: Peer did not accept our SAE group");
+			sae_pick_next_group(hapd, sta);
+			goto remove_sta;
+		}
+
+		if (!sae_status_success(hapd, status_code))
+			goto remove_sta;
+
+		if (!(hapd->conf->mesh & MESH_ENABLED) &&
+		    sta->sae->state == SAE_COMMITTED) {
+			/* This is needed in the infrastructure BSS case to
+			 * address a sequence where a STA entry may remain in
+			 * hostapd across two attempts to do SAE authentication
+			 * by the same STA. The second attempt may end up trying
+			 * to use a different group and that would not be
+			 * allowed if we remain in Committed state with the
+			 * previously set parameters. */
+			pos = mgmt->u.auth.variable;
+			end = ((const u8 *) mgmt) + len;
+			if (end - pos >= (int) sizeof(le16) &&
+			    sae_group_allowed(sta->sae, groups,
+					      WPA_GET_LE16(pos)) ==
+			    WLAN_STATUS_SUCCESS) {
+				/* Do not waste resources deriving the same PWE
+				 * again since the same group is reused. */
+				sae_set_state(sta, SAE_NOTHING,
+					      "Allow previous PWE to be reused");
+				allow_reuse = 1;
+			} else {
+				sae_set_state(sta, SAE_NOTHING,
+					      "Clear existing state to allow restart");
+				sae_clear_data(sta->sae);
+			}
+		}
+
+		resp = sae_parse_commit(sta->sae, mgmt->u.auth.variable,
+					((const u8 *) mgmt) + len -
+					mgmt->u.auth.variable, &token,
+					&token_len, groups, status_code ==
+					WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
+					status_code == WLAN_STATUS_SAE_PK,
+					NULL);
+		if (resp == SAE_SILENTLY_DISCARD) {
+			wpa_printf(MSG_DEBUG,
+				   "SAE: Drop commit message from " MACSTR " due to reflection attack",
+				   MAC2STR(sta->addr));
+			goto remove_sta;
+		}
+
+		if (resp == WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER) {
+			wpa_msg(hapd->msg_ctx, MSG_INFO,
+				WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER
+				MACSTR, MAC2STR(sta->addr));
+			sae_clear_retransmit_timer(hapd, sta);
+			sae_set_state(sta, SAE_NOTHING,
+				      "Unknown Password Identifier");
+			goto remove_sta;
+		}
+
+		if (token &&
+		    check_comeback_token(hapd->comeback_key,
+					 hapd->comeback_pending_idx, sta->addr,
+					 token, token_len)
+		    < 0) {
+			wpa_printf(MSG_DEBUG, "SAE: Drop commit message with "
+				   "incorrect token from " MACSTR,
+				   MAC2STR(sta->addr));
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto remove_sta;
+		}
+
+		if (resp != WLAN_STATUS_SUCCESS)
+			goto reply;
+
+		if (check_sae_rejected_groups(hapd, sta->sae)) {
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto reply;
+		}
+
+		if (!token && use_anti_clogging(hapd) && !allow_reuse) {
+			int h2e = 0;
+
+			wpa_printf(MSG_DEBUG,
+				   "SAE: Request anti-clogging token from "
+				   MACSTR, MAC2STR(sta->addr));
+			if (sta->sae->tmp)
+				h2e = sta->sae->h2e;
+			if (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
+			    status_code == WLAN_STATUS_SAE_PK)
+				h2e = 1;
+			data = auth_build_token_req(
+				&hapd->last_comeback_key_update,
+				hapd->comeback_key,
+				hapd->comeback_idx,
+				hapd->comeback_pending_idx,
+				sizeof(hapd->comeback_pending_idx),
+				sta->sae->group,
+				sta->addr, h2e);
+			resp = WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ;
+			if (hapd->conf->mesh & MESH_ENABLED)
+				sae_set_state(sta, SAE_NOTHING,
+					      "Request anti-clogging token case in mesh");
+			goto reply;
+		}
+
+		resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction,
+				   status_code, allow_reuse, &sta_removed);
+	} else if (auth_transaction == 2) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "SAE authentication (RX confirm, status=%u (%s))",
+			       status_code, status2str(status_code));
+		if (status_code != WLAN_STATUS_SUCCESS)
+			goto remove_sta;
+		if (sta->sae->state >= SAE_CONFIRMED ||
+		    !(hapd->conf->mesh & MESH_ENABLED)) {
+			const u8 *var;
+			size_t var_len;
+			u16 peer_send_confirm;
+
+			var = mgmt->u.auth.variable;
+			var_len = ((u8 *) mgmt) + len - mgmt->u.auth.variable;
+			if (var_len < 2) {
+				resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+				goto reply;
+			}
+
+			peer_send_confirm = WPA_GET_LE16(var);
+
+			if (sta->sae->state == SAE_ACCEPTED &&
+			    (peer_send_confirm <= sta->sae->rc ||
+			     peer_send_confirm == 0xffff)) {
+				wpa_printf(MSG_DEBUG,
+					   "SAE: Silently ignore unexpected Confirm from peer "
+					   MACSTR
+					   " (peer-send-confirm=%u Rc=%u)",
+					   MAC2STR(sta->addr),
+					   peer_send_confirm, sta->sae->rc);
+				return;
+			}
+
+			if (sae_check_confirm(sta->sae, var, var_len,
+					      NULL) < 0) {
+				resp = WLAN_STATUS_CHALLENGE_FAIL;
+				goto reply;
+			}
+			sta->sae->rc = peer_send_confirm;
+		}
+		resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction,
+				   status_code, 0, &sta_removed);
+	} else {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "unexpected SAE authentication transaction %u (status=%u (%s))",
+			       auth_transaction, status_code,
+			       status2str(status_code));
+		if (status_code != WLAN_STATUS_SUCCESS)
+			goto remove_sta;
+		resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
+	}
+
+reply:
+	if (!sta_removed && resp != WLAN_STATUS_SUCCESS) {
+		pos = mgmt->u.auth.variable;
+		end = ((const u8 *) mgmt) + len;
+
+		/* Copy the Finite Cyclic Group field from the request if we
+		 * rejected it as unsupported group. */
+		if (resp == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED &&
+		    !data && end - pos >= 2)
+			data = wpabuf_alloc_copy(pos, 2);
+
+		sae_sme_send_external_auth_status(hapd, sta, resp);
+		send_auth_reply(hapd, sta, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
+				auth_transaction, resp,
+				data ? wpabuf_head(data) : (u8 *) "",
+				data ? wpabuf_len(data) : 0, "auth-sae");
+	}
+
+remove_sta:
+	if (auth_transaction == 1)
+		success_status = sae_status_success(hapd, status_code);
+	else
+		success_status = status_code == WLAN_STATUS_SUCCESS;
+	if (!sta_removed && sta->added_unassoc &&
+	    (resp != WLAN_STATUS_SUCCESS || !success_status)) {
+		hostapd_drv_sta_remove(hapd, sta->addr);
+		sta->added_unassoc = 0;
+	}
+	wpabuf_free(data);
+}
+
+
+/**
+ * auth_sae_init_committed - Send COMMIT and start SAE in committed state
+ * @hapd: BSS data for the device initiating the authentication
+ * @sta: the peer to which commit authentication frame is sent
+ *
+ * This function implements Init event handling (IEEE Std 802.11-2012,
+ * 11.3.8.6.3) in which initial COMMIT message is sent. Prior to calling, the
+ * sta->sae structure should be initialized appropriately via a call to
+ * sae_prepare_commit().
+ */
+int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	int ret;
+
+	if (!sta->sae || !sta->sae->tmp)
+		return -1;
+
+	if (sta->sae->state != SAE_NOTHING)
+		return -1;
+
+	ret = auth_sae_send_commit(hapd, sta, hapd->own_addr, 0, -1);
+	if (ret)
+		return -1;
+
+	sae_set_state(sta, SAE_COMMITTED, "Init and sent commit");
+	sta->sae->sync = 0;
+	sae_set_retransmit_timer(hapd, sta);
+
+	return 0;
+}
+
+
+void auth_sae_process_commit(void *eloop_ctx, void *user_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct hostapd_sae_commit_queue *q;
+	unsigned int queue_len;
+
+	q = dl_list_first(&hapd->sae_commit_queue,
+			  struct hostapd_sae_commit_queue, list);
+	if (!q)
+		return;
+	wpa_printf(MSG_DEBUG,
+		   "SAE: Process next available message from queue");
+	dl_list_del(&q->list);
+	handle_auth(hapd, (const struct ieee80211_mgmt *) q->msg, q->len,
+		    q->rssi, 1);
+	os_free(q);
+
+	if (eloop_is_timeout_registered(auth_sae_process_commit, hapd, NULL))
+		return;
+	queue_len = dl_list_len(&hapd->sae_commit_queue);
+	eloop_register_timeout(0, queue_len * 10000, auth_sae_process_commit,
+			       hapd, NULL);
+}
+
+
+static void auth_sae_queue(struct hostapd_data *hapd,
+			   const struct ieee80211_mgmt *mgmt, size_t len,
+			   int rssi)
+{
+	struct hostapd_sae_commit_queue *q, *q2;
+	unsigned int queue_len;
+	const struct ieee80211_mgmt *mgmt2;
+
+	queue_len = dl_list_len(&hapd->sae_commit_queue);
+	if (queue_len >= 15) {
+		wpa_printf(MSG_DEBUG,
+			   "SAE: No more room in message queue - drop the new frame from "
+			   MACSTR, MAC2STR(mgmt->sa));
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "SAE: Queue Authentication message from "
+		   MACSTR " for processing (queue_len %u)", MAC2STR(mgmt->sa),
+		   queue_len);
+	q = os_zalloc(sizeof(*q) + len);
+	if (!q)
+		return;
+	q->rssi = rssi;
+	q->len = len;
+	os_memcpy(q->msg, mgmt, len);
+
+	/* Check whether there is already a queued Authentication frame from the
+	 * same station with the same transaction number and if so, replace that
+	 * queue entry with the new one. This avoids issues with a peer that
+	 * sends multiple times (e.g., due to frequent SAE retries). There is no
+	 * point in us trying to process the old attempts after a new one has
+	 * obsoleted them. */
+	dl_list_for_each(q2, &hapd->sae_commit_queue,
+			 struct hostapd_sae_commit_queue, list) {
+		mgmt2 = (const struct ieee80211_mgmt *) q2->msg;
+		if (os_memcmp(mgmt->sa, mgmt2->sa, ETH_ALEN) == 0 &&
+		    mgmt->u.auth.auth_transaction ==
+		    mgmt2->u.auth.auth_transaction) {
+			wpa_printf(MSG_DEBUG,
+				   "SAE: Replace queued message from same STA with same transaction number");
+			dl_list_add(&q2->list, &q->list);
+			dl_list_del(&q2->list);
+			os_free(q2);
+			goto queued;
+		}
+	}
+
+	/* No pending identical entry, so add to the end of the queue */
+	dl_list_add_tail(&hapd->sae_commit_queue, &q->list);
+
+queued:
+	if (eloop_is_timeout_registered(auth_sae_process_commit, hapd, NULL))
+		return;
+	eloop_register_timeout(0, queue_len * 10000, auth_sae_process_commit,
+			       hapd, NULL);
+}
+
+
+static int auth_sae_queued_addr(struct hostapd_data *hapd, const u8 *addr)
+{
+	struct hostapd_sae_commit_queue *q;
+	const struct ieee80211_mgmt *mgmt;
+
+	dl_list_for_each(q, &hapd->sae_commit_queue,
+			 struct hostapd_sae_commit_queue, list) {
+		mgmt = (const struct ieee80211_mgmt *) q->msg;
+		if (os_memcmp(addr, mgmt->sa, ETH_ALEN) == 0)
+			return 1;
+	}
+
+	return 0;
+}
+
+#endif /* CONFIG_SAE */
+
+
+static u16 wpa_res_to_status_code(enum wpa_validate_result res)
+{
+	switch (res) {
+	case WPA_IE_OK:
+		return WLAN_STATUS_SUCCESS;
+	case WPA_INVALID_IE:
+		return WLAN_STATUS_INVALID_IE;
+	case WPA_INVALID_GROUP:
+		return WLAN_STATUS_GROUP_CIPHER_NOT_VALID;
+	case WPA_INVALID_PAIRWISE:
+		return WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
+	case WPA_INVALID_AKMP:
+		return WLAN_STATUS_AKMP_NOT_VALID;
+	case WPA_NOT_ENABLED:
+		return WLAN_STATUS_INVALID_IE;
+	case WPA_ALLOC_FAIL:
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	case WPA_MGMT_FRAME_PROTECTION_VIOLATION:
+		return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
+	case WPA_INVALID_MGMT_GROUP_CIPHER:
+		return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY;
+	case WPA_INVALID_MDIE:
+		return WLAN_STATUS_INVALID_MDIE;
+	case WPA_INVALID_PROTO:
+		return WLAN_STATUS_INVALID_IE;
+	case WPA_INVALID_PMKID:
+		return WLAN_STATUS_INVALID_PMKID;
+	case WPA_DENIED_OTHER_REASON:
+		return WLAN_STATUS_ASSOC_DENIED_UNSPEC;
+	}
+	return WLAN_STATUS_INVALID_IE;
+}
+
+
+#ifdef CONFIG_FILS
+
+static void handle_auth_fils_finish(struct hostapd_data *hapd,
+				    struct sta_info *sta, u16 resp,
+				    struct wpabuf *data, int pub);
+
+void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
+		      const u8 *pos, size_t len, u16 auth_alg,
+		      u16 auth_transaction, u16 status_code,
+		      void (*cb)(struct hostapd_data *hapd,
+				 struct sta_info *sta, u16 resp,
+				 struct wpabuf *data, int pub))
+{
+	u16 resp = WLAN_STATUS_SUCCESS;
+	const u8 *end;
+	struct ieee802_11_elems elems;
+	enum wpa_validate_result res;
+	struct wpa_ie_data rsn;
+	struct rsn_pmksa_cache_entry *pmksa = NULL;
+
+	if (auth_transaction != 1 || status_code != WLAN_STATUS_SUCCESS)
+		return;
+
+	end = pos + len;
+
+	wpa_hexdump(MSG_DEBUG, "FILS: Authentication frame fields",
+		    pos, end - pos);
+
+	/* TODO: FILS PK */
+#ifdef CONFIG_FILS_SK_PFS
+	if (auth_alg == WLAN_AUTH_FILS_SK_PFS) {
+		u16 group;
+		struct wpabuf *pub;
+		size_t elem_len;
+
+		/* Using FILS PFS */
+
+		/* Finite Cyclic Group */
+		if (end - pos < 2) {
+			wpa_printf(MSG_DEBUG,
+				   "FILS: No room for Finite Cyclic Group");
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+		}
+		group = WPA_GET_LE16(pos);
+		pos += 2;
+		if (group != hapd->conf->fils_dh_group) {
+			wpa_printf(MSG_DEBUG,
+				   "FILS: Unsupported Finite Cyclic Group: %u (expected %u)",
+				   group, hapd->conf->fils_dh_group);
+			resp = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+			goto fail;
+		}
+
+		crypto_ecdh_deinit(sta->fils_ecdh);
+		sta->fils_ecdh = crypto_ecdh_init(group);
+		if (!sta->fils_ecdh) {
+			wpa_printf(MSG_INFO,
+				   "FILS: Could not initialize ECDH with group %d",
+				   group);
+			resp = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+			goto fail;
+		}
+
+		pub = crypto_ecdh_get_pubkey(sta->fils_ecdh, 1);
+		if (!pub) {
+			wpa_printf(MSG_DEBUG,
+				   "FILS: Failed to derive ECDH public key");
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+		}
+		elem_len = wpabuf_len(pub);
+		wpabuf_free(pub);
+
+		/* Element */
+		if ((size_t) (end - pos) < elem_len) {
+			wpa_printf(MSG_DEBUG, "FILS: No room for Element");
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+		}
+
+		wpabuf_free(sta->fils_g_sta);
+		sta->fils_g_sta = wpabuf_alloc_copy(pos, elem_len);
+		wpabuf_clear_free(sta->fils_dh_ss);
+		sta->fils_dh_ss = crypto_ecdh_set_peerkey(sta->fils_ecdh, 1,
+							  pos, elem_len);
+		if (!sta->fils_dh_ss) {
+			wpa_printf(MSG_DEBUG, "FILS: ECDH operation failed");
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+		}
+		wpa_hexdump_buf_key(MSG_DEBUG, "FILS: DH_SS", sta->fils_dh_ss);
+		pos += elem_len;
+	} else {
+		crypto_ecdh_deinit(sta->fils_ecdh);
+		sta->fils_ecdh = NULL;
+		wpabuf_clear_free(sta->fils_dh_ss);
+		sta->fils_dh_ss = NULL;
+	}
+#endif /* CONFIG_FILS_SK_PFS */
+
+	wpa_hexdump(MSG_DEBUG, "FILS: Remaining IEs", pos, end - pos);
+	if (ieee802_11_parse_elems(pos, end - pos, &elems, 1) == ParseFailed) {
+		wpa_printf(MSG_DEBUG, "FILS: Could not parse elements");
+		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto fail;
+	}
+
+	/* RSNE */
+	wpa_hexdump(MSG_DEBUG, "FILS: RSN element",
+		    elems.rsn_ie, elems.rsn_ie_len);
+	if (!elems.rsn_ie ||
+	    wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
+				 &rsn) < 0) {
+		wpa_printf(MSG_DEBUG, "FILS: No valid RSN element");
+		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto fail;
+	}
+
+	if (!sta->wpa_sm)
+		sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr,
+						NULL);
+	if (!sta->wpa_sm) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Failed to initialize RSN state machine");
+		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto fail;
+	}
+
+	res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
+				  hapd->iface->freq,
+				  elems.rsn_ie - 2, elems.rsn_ie_len + 2,
+				  elems.rsnxe ? elems.rsnxe - 2 : NULL,
+				  elems.rsnxe ? elems.rsnxe_len + 2 : 0,
+				  elems.mdie, elems.mdie_len, NULL, 0);
+	resp = wpa_res_to_status_code(res);
+	if (resp != WLAN_STATUS_SUCCESS)
+		goto fail;
+
+	if (!elems.fils_nonce) {
+		wpa_printf(MSG_DEBUG, "FILS: No FILS Nonce field");
+		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "FILS: SNonce", elems.fils_nonce,
+		    FILS_NONCE_LEN);
+	os_memcpy(sta->fils_snonce, elems.fils_nonce, FILS_NONCE_LEN);
+
+	/* PMKID List */
+	if (rsn.pmkid && rsn.num_pmkid > 0) {
+		u8 num;
+		const u8 *pmkid;
+
+		wpa_hexdump(MSG_DEBUG, "FILS: PMKID List",
+			    rsn.pmkid, rsn.num_pmkid * PMKID_LEN);
+
+		pmkid = rsn.pmkid;
+		num = rsn.num_pmkid;
+		while (num) {
+			wpa_hexdump(MSG_DEBUG, "FILS: PMKID", pmkid, PMKID_LEN);
+			pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr,
+						   pmkid);
+			if (pmksa)
+				break;
+			pmksa = wpa_auth_pmksa_get_fils_cache_id(hapd->wpa_auth,
+								 sta->addr,
+								 pmkid);
+			if (pmksa)
+				break;
+			pmkid += PMKID_LEN;
+			num--;
+		}
+	}
+	if (pmksa && wpa_auth_sta_key_mgmt(sta->wpa_sm) != pmksa->akmp) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Matching PMKSA cache entry has different AKMP (0x%x != 0x%x) - ignore",
+			   wpa_auth_sta_key_mgmt(sta->wpa_sm), pmksa->akmp);
+		pmksa = NULL;
+	}
+	if (pmksa)
+		wpa_printf(MSG_DEBUG, "FILS: Found matching PMKSA cache entry");
+
+	/* FILS Session */
+	if (!elems.fils_session) {
+		wpa_printf(MSG_DEBUG, "FILS: No FILS Session element");
+		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "FILS: FILS Session", elems.fils_session,
+		    FILS_SESSION_LEN);
+	os_memcpy(sta->fils_session, elems.fils_session, FILS_SESSION_LEN);
+
+	/* Wrapped Data */
+	if (elems.wrapped_data) {
+		wpa_hexdump(MSG_DEBUG, "FILS: Wrapped Data",
+			    elems.wrapped_data,
+			    elems.wrapped_data_len);
+		if (!pmksa) {
+#ifndef CONFIG_NO_RADIUS
+			if (!sta->eapol_sm) {
+				sta->eapol_sm =
+					ieee802_1x_alloc_eapol_sm(hapd, sta);
+			}
+			wpa_printf(MSG_DEBUG,
+				   "FILS: Forward EAP-Initiate/Re-auth to authentication server");
+			ieee802_1x_encapsulate_radius(
+				hapd, sta, elems.wrapped_data,
+				elems.wrapped_data_len);
+			sta->fils_pending_cb = cb;
+			wpa_printf(MSG_DEBUG,
+				   "FILS: Will send Authentication frame once the response from authentication server is available");
+			sta->flags |= WLAN_STA_PENDING_FILS_ERP;
+			/* Calculate pending PMKID here so that we do not need
+			 * to maintain a copy of the EAP-Initiate/Reauth
+			 * message. */
+			if (fils_pmkid_erp(wpa_auth_sta_key_mgmt(sta->wpa_sm),
+					   elems.wrapped_data,
+					   elems.wrapped_data_len,
+					   sta->fils_erp_pmkid) == 0)
+				sta->fils_erp_pmkid_set = 1;
+			return;
+#else /* CONFIG_NO_RADIUS */
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+#endif /* CONFIG_NO_RADIUS */
+		}
+	}
+
+fail:
+	if (cb) {
+		struct wpabuf *data;
+		int pub = 0;
+
+		data = prepare_auth_resp_fils(hapd, sta, &resp, pmksa, NULL,
+					      NULL, 0, &pub);
+		if (!data) {
+			wpa_printf(MSG_DEBUG,
+				   "%s: prepare_auth_resp_fils() returned failure",
+				   __func__);
+		}
+
+		cb(hapd, sta, resp, data, pub);
+	}
+}
+
+
+static struct wpabuf *
+prepare_auth_resp_fils(struct hostapd_data *hapd,
+		       struct sta_info *sta, u16 *resp,
+		       struct rsn_pmksa_cache_entry *pmksa,
+		       struct wpabuf *erp_resp,
+		       const u8 *msk, size_t msk_len,
+		       int *is_pub)
+{
+	u8 fils_nonce[FILS_NONCE_LEN];
+	size_t ielen;
+	struct wpabuf *data = NULL;
+	const u8 *ie;
+	u8 *ie_buf = NULL;
+	const u8 *pmk = NULL;
+	size_t pmk_len = 0;
+	u8 pmk_buf[PMK_LEN_MAX];
+	struct wpabuf *pub = NULL;
+
+	if (*resp != WLAN_STATUS_SUCCESS)
+		goto fail;
+
+	ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &ielen);
+	if (!ie) {
+		*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto fail;
+	}
+
+	if (pmksa) {
+		/* Add PMKID of the selected PMKSA into RSNE */
+		ie_buf = os_malloc(ielen + 2 + 2 + PMKID_LEN);
+		if (!ie_buf) {
+			*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+		}
+
+		os_memcpy(ie_buf, ie, ielen);
+		if (wpa_insert_pmkid(ie_buf, &ielen, pmksa->pmkid) < 0) {
+			*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+		}
+		ie = ie_buf;
+	}
+
+	if (random_get_bytes(fils_nonce, FILS_NONCE_LEN) < 0) {
+		*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "RSN: Generated FILS Nonce",
+		    fils_nonce, FILS_NONCE_LEN);
+
+#ifdef CONFIG_FILS_SK_PFS
+	if (sta->fils_dh_ss && sta->fils_ecdh) {
+		pub = crypto_ecdh_get_pubkey(sta->fils_ecdh, 1);
+		if (!pub) {
+			*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+		}
+	}
+#endif /* CONFIG_FILS_SK_PFS */
+
+	data = wpabuf_alloc(1000 + ielen + (pub ? wpabuf_len(pub) : 0));
+	if (!data) {
+		*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto fail;
+	}
+
+	/* TODO: FILS PK */
+#ifdef CONFIG_FILS_SK_PFS
+	if (pub) {
+		/* Finite Cyclic Group */
+		wpabuf_put_le16(data, hapd->conf->fils_dh_group);
+
+		/* Element */
+		wpabuf_put_buf(data, pub);
+	}
+#endif /* CONFIG_FILS_SK_PFS */
+
+	/* RSNE */
+	wpabuf_put_data(data, ie, ielen);
+
+	/* MDE when using FILS+FT (already included in ie,ielen with RSNE) */
+
+#ifdef CONFIG_IEEE80211R_AP
+	if (wpa_key_mgmt_ft(wpa_auth_sta_key_mgmt(sta->wpa_sm))) {
+		/* FTE[R1KH-ID,R0KH-ID] when using FILS+FT */
+		int res;
+
+		res = wpa_auth_write_fte(hapd->wpa_auth, sta->wpa_sm,
+					 wpabuf_put(data, 0),
+					 wpabuf_tailroom(data));
+		if (res < 0) {
+			*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+		}
+		wpabuf_put(data, res);
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+
+	/* FILS Nonce */
+	wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */
+	wpabuf_put_u8(data, 1 + FILS_NONCE_LEN); /* Length */
+	/* Element ID Extension */
+	wpabuf_put_u8(data, WLAN_EID_EXT_FILS_NONCE);
+	wpabuf_put_data(data, fils_nonce, FILS_NONCE_LEN);
+
+	/* FILS Session */
+	wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */
+	wpabuf_put_u8(data, 1 + FILS_SESSION_LEN); /* Length */
+	/* Element ID Extension */
+	wpabuf_put_u8(data, WLAN_EID_EXT_FILS_SESSION);
+	wpabuf_put_data(data, sta->fils_session, FILS_SESSION_LEN);
+
+	/* Wrapped Data */
+	if (!pmksa && erp_resp) {
+		wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */
+		wpabuf_put_u8(data, 1 + wpabuf_len(erp_resp)); /* Length */
+		/* Element ID Extension */
+		wpabuf_put_u8(data, WLAN_EID_EXT_WRAPPED_DATA);
+		wpabuf_put_buf(data, erp_resp);
+
+		if (fils_rmsk_to_pmk(wpa_auth_sta_key_mgmt(sta->wpa_sm),
+				     msk, msk_len, sta->fils_snonce, fils_nonce,
+				     sta->fils_dh_ss ?
+				     wpabuf_head(sta->fils_dh_ss) : NULL,
+				     sta->fils_dh_ss ?
+				     wpabuf_len(sta->fils_dh_ss) : 0,
+				     pmk_buf, &pmk_len)) {
+			wpa_printf(MSG_DEBUG, "FILS: Failed to derive PMK");
+			*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			wpabuf_free(data);
+			data = NULL;
+			goto fail;
+		}
+		pmk = pmk_buf;
+
+		/* Don't use DHss in PTK derivation if PMKSA caching is not
+		 * used. */
+		wpabuf_clear_free(sta->fils_dh_ss);
+		sta->fils_dh_ss = NULL;
+
+		if (sta->fils_erp_pmkid_set) {
+			/* TODO: get PMKLifetime from WPA parameters */
+			unsigned int dot11RSNAConfigPMKLifetime = 43200;
+			int session_timeout;
+
+			session_timeout = dot11RSNAConfigPMKLifetime;
+			if (sta->session_timeout_set) {
+				struct os_reltime now, diff;
+
+				os_get_reltime(&now);
+				os_reltime_sub(&sta->session_timeout, &now,
+					       &diff);
+				session_timeout = diff.sec;
+			}
+
+			sta->fils_erp_pmkid_set = 0;
+			wpa_auth_add_fils_pmk_pmkid(sta->wpa_sm, pmk, pmk_len,
+						    sta->fils_erp_pmkid);
+			if (!hapd->conf->disable_pmksa_caching &&
+			    wpa_auth_pmksa_add2(
+				    hapd->wpa_auth, sta->addr,
+				    pmk, pmk_len,
+				    sta->fils_erp_pmkid,
+				    session_timeout,
+				    wpa_auth_sta_key_mgmt(sta->wpa_sm)) < 0) {
+				wpa_printf(MSG_ERROR,
+					   "FILS: Failed to add PMKSA cache entry based on ERP");
+			}
+		}
+	} else if (pmksa) {
+		pmk = pmksa->pmk;
+		pmk_len = pmksa->pmk_len;
+	}
+
+	if (!pmk) {
+		wpa_printf(MSG_DEBUG, "FILS: No PMK available");
+		*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		wpabuf_free(data);
+		data = NULL;
+		goto fail;
+	}
+
+	if (fils_auth_pmk_to_ptk(sta->wpa_sm, pmk, pmk_len,
+				 sta->fils_snonce, fils_nonce,
+				 sta->fils_dh_ss ?
+				 wpabuf_head(sta->fils_dh_ss) : NULL,
+				 sta->fils_dh_ss ?
+				 wpabuf_len(sta->fils_dh_ss) : 0,
+				 sta->fils_g_sta, pub) < 0) {
+		*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		wpabuf_free(data);
+		data = NULL;
+		goto fail;
+	}
+
+fail:
+	if (is_pub)
+		*is_pub = pub != NULL;
+	os_free(ie_buf);
+	wpabuf_free(pub);
+	wpabuf_clear_free(sta->fils_dh_ss);
+	sta->fils_dh_ss = NULL;
+#ifdef CONFIG_FILS_SK_PFS
+	crypto_ecdh_deinit(sta->fils_ecdh);
+	sta->fils_ecdh = NULL;
+#endif /* CONFIG_FILS_SK_PFS */
+	return data;
+}
+
+
+static void handle_auth_fils_finish(struct hostapd_data *hapd,
+				    struct sta_info *sta, u16 resp,
+				    struct wpabuf *data, int pub)
+{
+	u16 auth_alg;
+
+	auth_alg = (pub ||
+		    resp == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) ?
+		WLAN_AUTH_FILS_SK_PFS : WLAN_AUTH_FILS_SK;
+	send_auth_reply(hapd, sta, sta->addr, hapd->own_addr, auth_alg, 2, resp,
+			data ? wpabuf_head(data) : (u8 *) "",
+			data ? wpabuf_len(data) : 0, "auth-fils-finish");
+	wpabuf_free(data);
+
+	if (resp == WLAN_STATUS_SUCCESS) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "authentication OK (FILS)");
+		sta->flags |= WLAN_STA_AUTH;
+		wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
+		sta->auth_alg = pub ? WLAN_AUTH_FILS_SK_PFS : WLAN_AUTH_FILS_SK;
+		mlme_authenticate_indication(hapd, sta);
+	}
+}
+
+
+void ieee802_11_finish_fils_auth(struct hostapd_data *hapd,
+				 struct sta_info *sta, int success,
+				 struct wpabuf *erp_resp,
+				 const u8 *msk, size_t msk_len)
+{
+	u16 resp;
+	u32 flags = sta->flags;
+
+	sta->flags &= ~(WLAN_STA_PENDING_FILS_ERP |
+			WLAN_STA_PENDING_PASN_FILS_ERP);
+
+	resp = success ? WLAN_STATUS_SUCCESS : WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+	if (flags & WLAN_STA_PENDING_FILS_ERP) {
+		struct wpabuf *data;
+		int pub = 0;
+
+		if (!sta->fils_pending_cb)
+			return;
+
+		data = prepare_auth_resp_fils(hapd, sta, &resp, NULL, erp_resp,
+					      msk, msk_len, &pub);
+		if (!data) {
+			wpa_printf(MSG_DEBUG,
+				   "%s: prepare_auth_resp_fils() failure",
+				   __func__);
+		}
+		sta->fils_pending_cb(hapd, sta, resp, data, pub);
+#ifdef CONFIG_PASN
+	} else if (flags & WLAN_STA_PENDING_PASN_FILS_ERP) {
+		pasn_fils_auth_resp(hapd, sta, resp, erp_resp,
+				    msk, msk_len);
+#endif /* CONFIG_PASN */
+	}
+}
+
+#endif /* CONFIG_FILS */
+
+
+static int ieee802_11_allowed_address(struct hostapd_data *hapd, const u8 *addr,
+				      const u8 *msg, size_t len,
+				      struct radius_sta *info)
+{
+	int res;
+
+	res = hostapd_allowed_address(hapd, addr, msg, len, info, 0);
+
+	if (res == HOSTAPD_ACL_REJECT) {
+		wpa_printf(MSG_DEBUG, "Station " MACSTR
+			   " not allowed to authenticate",
+			   MAC2STR(addr));
+		return HOSTAPD_ACL_REJECT;
+	}
+
+	if (res == HOSTAPD_ACL_PENDING) {
+		wpa_printf(MSG_DEBUG, "Authentication frame from " MACSTR
+			   " waiting for an external authentication",
+			   MAC2STR(addr));
+		/* Authentication code will re-send the authentication frame
+		 * after it has received (and cached) information from the
+		 * external source. */
+		return HOSTAPD_ACL_PENDING;
+	}
+
+	return res;
+}
+
+
+int ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta,
+			       int res, struct radius_sta *info)
+{
+	u32 session_timeout = info->session_timeout;
+	u32 acct_interim_interval = info->acct_interim_interval;
+	struct vlan_description *vlan_id = &info->vlan_id;
+	struct hostapd_sta_wpa_psk_short *psk = info->psk;
+	char *identity = info->identity;
+	char *radius_cui = info->radius_cui;
+
+	if (vlan_id->notempty &&
+	    !hostapd_vlan_valid(hapd->conf->vlan, vlan_id)) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+			       HOSTAPD_LEVEL_INFO,
+			       "Invalid VLAN %d%s received from RADIUS server",
+			       vlan_id->untagged,
+			       vlan_id->tagged[0] ? "+" : "");
+		return -1;
+	}
+	if (ap_sta_set_vlan(hapd, sta, vlan_id) < 0)
+		return -1;
+	if (sta->vlan_id)
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+			       HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id);
+
+	hostapd_free_psk_list(sta->psk);
+	if (hapd->conf->wpa_psk_radius != PSK_RADIUS_IGNORED)
+		hostapd_copy_psk_list(&sta->psk, psk);
+	else
+		sta->psk = NULL;
+
+	os_free(sta->identity);
+	if (identity)
+		sta->identity = os_strdup(identity);
+	else
+		sta->identity = NULL;
+
+	os_free(sta->radius_cui);
+	if (radius_cui)
+		sta->radius_cui = os_strdup(radius_cui);
+	else
+		sta->radius_cui = NULL;
+
+	if (hapd->conf->acct_interim_interval == 0 && acct_interim_interval)
+		sta->acct_interim_interval = acct_interim_interval;
+	if (res == HOSTAPD_ACL_ACCEPT_TIMEOUT) {
+		sta->session_timeout_set = 1;
+		os_get_reltime(&sta->session_timeout);
+		sta->session_timeout.sec += session_timeout;
+		ap_sta_session_timeout(hapd, sta, session_timeout);
+	} else {
+		sta->session_timeout_set = 0;
+		ap_sta_no_session_timeout(hapd, sta);
+	}
+
+	return 0;
+}
+
+
+#ifdef CONFIG_PASN
+#ifdef CONFIG_FILS
+
+static void pasn_fils_auth_resp(struct hostapd_data *hapd,
+				struct sta_info *sta, u16 status,
+				struct wpabuf *erp_resp,
+				const u8 *msk, size_t msk_len)
+{
+	struct pasn_data *pasn = sta->pasn;
+	struct pasn_fils *fils = &pasn->fils;
+	u8 pmk[PMK_LEN_MAX];
+	size_t pmk_len;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "PASN: FILS: Handle AS response - status=%u",
+		   status);
+
+	if (status != WLAN_STATUS_SUCCESS)
+		goto fail;
+
+	if (!pasn->secret) {
+		wpa_printf(MSG_DEBUG, "PASN: FILS: Missing secret");
+		goto fail;
+	}
+
+	if (random_get_bytes(fils->anonce, FILS_NONCE_LEN) < 0) {
+		wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to get ANonce");
+		goto fail;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "RSN: Generated FILS ANonce",
+		    fils->anonce, FILS_NONCE_LEN);
+
+	ret = fils_rmsk_to_pmk(pasn->akmp, msk, msk_len, fils->nonce,
+			       fils->anonce, NULL, 0, pmk, &pmk_len);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "FILS: Failed to derive PMK");
+		goto fail;
+	}
+
+	ret = pasn_pmk_to_ptk(pmk, pmk_len, sta->addr, hapd->own_addr,
+			      wpabuf_head(pasn->secret),
+			      wpabuf_len(pasn->secret),
+			      &sta->pasn->ptk, sta->pasn->akmp,
+			      sta->pasn->cipher, sta->pasn->kdk_len);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to derive PTK");
+		goto fail;
+	}
+
+	if (pasn->secure_ltf) {
+		ret = wpa_ltf_keyseed(&pasn->ptk, pasn->akmp, pasn->cipher);
+		if (ret) {
+			wpa_printf(MSG_DEBUG,
+				   "PASN: FILS: Failed to derive LTF keyseed");
+			goto fail;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "PASN: PTK successfully derived");
+
+	wpabuf_free(pasn->secret);
+	pasn->secret = NULL;
+
+	fils->erp_resp = erp_resp;
+	ret = handle_auth_pasn_resp(sta->pasn, hapd->own_addr, sta->addr, NULL,
+				    WLAN_STATUS_SUCCESS);
+	fils->erp_resp = NULL;
+
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to send response");
+		goto fail;
+	}
+
+	fils->state = PASN_FILS_STATE_COMPLETE;
+	return;
+fail:
+	ap_free_sta(hapd, sta);
+}
+
+
+static int pasn_wd_handle_fils(struct hostapd_data *hapd, struct sta_info *sta,
+			       struct wpabuf *wd)
+{
+#ifdef CONFIG_NO_RADIUS
+	wpa_printf(MSG_DEBUG, "PASN: FILS: RADIUS is not configured. Fail");
+	return -1;
+#else /* CONFIG_NO_RADIUS */
+	struct pasn_data *pasn = sta->pasn;
+	struct pasn_fils *fils = &pasn->fils;
+	struct ieee802_11_elems elems;
+	struct wpa_ie_data rsne_data;
+	struct wpabuf *fils_wd;
+	const u8 *data;
+	size_t buf_len;
+	u16 alg, seq, status;
+	int ret;
+
+	if (fils->state != PASN_FILS_STATE_NONE) {
+		wpa_printf(MSG_DEBUG, "PASN: FILS: Not expecting wrapped data");
+		return -1;
+	}
+
+	if (!wd) {
+		wpa_printf(MSG_DEBUG, "PASN: FILS: No wrapped data");
+		return -1;
+	}
+
+	data = wpabuf_head_u8(wd);
+	buf_len = wpabuf_len(wd);
+
+	if (buf_len < 6) {
+		wpa_printf(MSG_DEBUG, "PASN: FILS: Buffer too short. len=%zu",
+			   buf_len);
+		return -1;
+	}
+
+	alg = WPA_GET_LE16(data);
+	seq = WPA_GET_LE16(data + 2);
+	status = WPA_GET_LE16(data + 4);
+
+	wpa_printf(MSG_DEBUG, "PASN: FILS: alg=%u, seq=%u, status=%u",
+		   alg, seq, status);
+
+	if (alg != WLAN_AUTH_FILS_SK || seq != 1 ||
+	    status != WLAN_STATUS_SUCCESS) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: FILS: Dropping peer authentication");
+		return -1;
+	}
+
+	data += 6;
+	buf_len -= 6;
+
+	if (ieee802_11_parse_elems(data, buf_len, &elems, 1) == ParseFailed) {
+		wpa_printf(MSG_DEBUG, "PASN: FILS: Could not parse elements");
+		return -1;
+	}
+
+	if (!elems.rsn_ie || !elems.fils_nonce || !elems.fils_nonce ||
+	    !elems.wrapped_data || !elems.fils_session) {
+		wpa_printf(MSG_DEBUG, "PASN: FILS: Missing IEs");
+		return -1;
+	}
+
+	ret = wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
+				   &rsne_data);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: FILS: Failed parsing RSNE");
+		return -1;
+	}
+
+	ret = wpa_pasn_validate_rsne(&rsne_data);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: FILS: Failed validating RSNE");
+		return -1;
+	}
+
+	if (rsne_data.num_pmkid) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: FILS: Not expecting PMKID in RSNE");
+		return -1;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "PASN: FILS: Nonce", elems.fils_nonce,
+		    FILS_NONCE_LEN);
+	os_memcpy(fils->nonce, elems.fils_nonce, FILS_NONCE_LEN);
+
+	wpa_hexdump(MSG_DEBUG, "PASN: FILS: Session", elems.fils_session,
+		    FILS_SESSION_LEN);
+	os_memcpy(fils->session, elems.fils_session, FILS_SESSION_LEN);
+
+	fils_wd = ieee802_11_defrag(&elems, WLAN_EID_EXTENSION,
+				    WLAN_EID_EXT_WRAPPED_DATA);
+
+	if (!fils_wd) {
+		wpa_printf(MSG_DEBUG, "PASN: FILS: Missing wrapped data");
+		return -1;
+	}
+
+	if (!sta->eapol_sm)
+		sta->eapol_sm = ieee802_1x_alloc_eapol_sm(hapd, sta);
+
+	wpa_printf(MSG_DEBUG,
+		   "PASN: FILS: Forward EAP-Initiate/Re-auth to AS");
+
+	ieee802_1x_encapsulate_radius(hapd, sta, wpabuf_head(fils_wd),
+				      wpabuf_len(fils_wd));
+
+	sta->flags |= WLAN_STA_PENDING_PASN_FILS_ERP;
+
+	fils->state = PASN_FILS_STATE_PENDING_AS;
+
+	/*
+	 * Calculate pending PMKID here so that we do not need to maintain a
+	 * copy of the EAP-Initiate/Reautt message.
+	 */
+	fils_pmkid_erp(pasn->akmp, wpabuf_head(fils_wd), wpabuf_len(fils_wd),
+		       fils->erp_pmkid);
+
+	wpabuf_free(fils_wd);
+	return 0;
+#endif /* CONFIG_NO_RADIUS */
+}
+
+#endif /* CONFIG_FILS */
+
+
+static int hapd_pasn_send_mlme(void *ctx, const u8 *data, size_t data_len,
+			       int noack, unsigned int freq, unsigned int wait)
+{
+	struct hostapd_data *hapd = ctx;
+
+	return hostapd_drv_send_mlme(hapd, data, data_len, 0, NULL, 0, 0);
+}
+
+
+static void hapd_initialize_pasn(struct hostapd_data *hapd,
+				 struct sta_info *sta)
+{
+	struct pasn_data *pasn = sta->pasn;
+
+	pasn->cb_ctx = hapd;
+	pasn->send_mgmt = hapd_pasn_send_mlme;
+	pasn->pasn_groups = hapd->conf->pasn_groups;
+	pasn->noauth = hapd->conf->pasn_noauth;
+	pasn->wpa_key_mgmt = hapd->conf->wpa_key_mgmt;
+	pasn->rsn_pairwise = hapd->conf->rsn_pairwise;
+	pasn->derive_kdk = hapd->iface->drv_flags2 &
+		WPA_DRIVER_FLAGS2_SEC_LTF_AP;
+#ifdef CONFIG_TESTING_OPTIONS
+	pasn->corrupt_mic = hapd->conf->pasn_corrupt_mic;
+	if (hapd->conf->force_kdk_derivation)
+		pasn->derive_kdk = true;
+#endif /* CONFIG_TESTING_OPTIONS */
+	pasn->use_anti_clogging = use_anti_clogging(hapd);
+	pasn->password = sae_get_password(hapd, sta, NULL, NULL, &pasn->pt,
+					  NULL);
+	pasn->rsn_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &pasn->rsn_ie_len);
+	pasn->rsnxe_ie = hostapd_wpa_ie(hapd, WLAN_EID_RSNX);
+	pasn->disable_pmksa_caching = hapd->conf->disable_pmksa_caching;
+	pasn->pmksa = wpa_auth_get_pmksa_cache(hapd->wpa_auth);
+
+	pasn->comeback_after = hapd->conf->pasn_comeback_after;
+	pasn->comeback_idx = hapd->comeback_idx;
+	pasn->comeback_key =  hapd->comeback_key;
+	pasn->comeback_pending_idx = hapd->comeback_pending_idx;
+	os_memcpy(pasn->bssid, hapd->own_addr, ETH_ALEN);
+}
+
+
+static int pasn_set_keys_from_cache(struct hostapd_data *hapd,
+				    const u8 *own_addr, const u8 *sta_addr,
+				    int cipher, int akmp)
+{
+	struct ptksa_cache_entry *entry;
+
+	entry = ptksa_cache_get(hapd->ptksa, sta_addr, cipher);
+	if (!entry) {
+		wpa_printf(MSG_DEBUG, "PASN: peer " MACSTR
+			   " not present in PTKSA cache", MAC2STR(sta_addr));
+		return -1;
+	}
+
+	if (os_memcmp(entry->own_addr, own_addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: own addr " MACSTR " and PTKSA entry own addr "
+			   MACSTR " differ",
+			   MAC2STR(own_addr), MAC2STR(entry->own_addr));
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "PASN: " MACSTR " present in PTKSA cache",
+		   MAC2STR(sta_addr));
+	hostapd_drv_set_secure_ranging_ctx(hapd, own_addr, sta_addr, cipher,
+					   entry->ptk.tk_len, entry->ptk.tk,
+					   entry->ptk.ltf_keyseed_len,
+					   entry->ptk.ltf_keyseed, 0);
+
+	return 0;
+}
+
+
+static void hapd_pasn_update_params(struct hostapd_data *hapd,
+				    struct sta_info *sta,
+				    const struct ieee80211_mgmt *mgmt,
+				    size_t len)
+{
+	struct pasn_data *pasn = sta->pasn;
+	struct ieee802_11_elems elems;
+	struct wpa_ie_data rsn_data;
+	struct wpa_pasn_params_data pasn_params;
+	struct wpabuf *wrapped_data = NULL;
+
+	if (ieee802_11_parse_elems(mgmt->u.auth.variable,
+				   len - offsetof(struct ieee80211_mgmt,
+						  u.auth.variable),
+				   &elems, 0) == ParseFailed) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: Failed parsing Authentication frame");
+		return;
+	}
+
+	if (!elems.rsn_ie ||
+	    wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
+				 &rsn_data)) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed parsing RSNE");
+		return;
+	}
+
+	if (!(rsn_data.key_mgmt & pasn->wpa_key_mgmt) ||
+	    !(rsn_data.pairwise_cipher & pasn->rsn_pairwise)) {
+		wpa_printf(MSG_DEBUG, "PASN: Mismatch in AKMP/cipher");
+		return;
+	}
+
+	pasn->akmp = rsn_data.key_mgmt;
+	pasn->cipher = rsn_data.pairwise_cipher;
+
+	if (wpa_key_mgmt_ft(pasn->akmp) && rsn_data.num_pmkid) {
+#ifdef CONFIG_IEEE80211R_AP
+		pasn->pmk_r1_len = 0;
+		wpa_ft_fetch_pmk_r1(hapd->wpa_auth, sta->addr,
+				    rsn_data.pmkid,
+				    pasn->pmk_r1, &pasn->pmk_r1_len, NULL,
+				    NULL, NULL, NULL,
+				    NULL, NULL, NULL);
+#endif /* CONFIG_IEEE80211R_AP */
+	}
+#ifdef CONFIG_FILS
+	if (pasn->akmp != WPA_KEY_MGMT_FILS_SHA256 &&
+	    pasn->akmp != WPA_KEY_MGMT_FILS_SHA384)
+		return;
+	if (!elems.pasn_params ||
+	    wpa_pasn_parse_parameter_ie(elems.pasn_params - 3,
+					elems.pasn_params_len + 3,
+					false, &pasn_params)) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: Failed validation of PASN Parameters element");
+		return;
+	}
+	if (pasn_params.wrapped_data_format != WPA_PASN_WRAPPED_DATA_NO) {
+		wrapped_data = ieee802_11_defrag(&elems, WLAN_EID_EXTENSION,
+						 WLAN_EID_EXT_WRAPPED_DATA);
+		if (!wrapped_data) {
+			wpa_printf(MSG_DEBUG, "PASN: Missing wrapped data");
+			return;
+		}
+		if (pasn_wd_handle_fils(hapd, sta, wrapped_data))
+			wpa_printf(MSG_DEBUG,
+				   "PASN: Failed processing FILS wrapped data");
+		else
+			pasn->fils_wd_valid = true;
+	}
+	wpabuf_free(wrapped_data);
+#endif /* CONFIG_FILS */
+}
+
+
+static void handle_auth_pasn(struct hostapd_data *hapd, struct sta_info *sta,
+			     const struct ieee80211_mgmt *mgmt, size_t len,
+			     u16 trans_seq, u16 status)
+{
+	if (hapd->conf->wpa != WPA_PROTO_RSN) {
+		wpa_printf(MSG_INFO, "PASN: RSN is not configured");
+		return;
+	}
+
+	wpa_printf(MSG_INFO, "PASN authentication: sta=" MACSTR,
+		   MAC2STR(sta->addr));
+
+	if (trans_seq == 1) {
+		if (sta->pasn) {
+			wpa_printf(MSG_DEBUG,
+				   "PASN: Not expecting transaction == 1");
+			return;
+		}
+
+		if (status != WLAN_STATUS_SUCCESS) {
+			wpa_printf(MSG_DEBUG,
+				   "PASN: Failure status in transaction == 1");
+			return;
+		}
+
+		sta->pasn = os_zalloc(sizeof(*sta->pasn));
+		if (!sta->pasn) {
+			wpa_printf(MSG_DEBUG,
+				   "PASN: Failed to allocate PASN context");
+			return;
+		}
+
+		hapd_initialize_pasn(hapd, sta);
+
+		hapd_pasn_update_params(hapd, sta, mgmt, len);
+		if (handle_auth_pasn_1(sta->pasn, hapd->own_addr,
+				       sta->addr, mgmt, len) < 0)
+			ap_free_sta(hapd, sta);
+	} else if (trans_seq == 3) {
+		if (!sta->pasn) {
+			wpa_printf(MSG_DEBUG,
+				   "PASN: Not expecting transaction == 3");
+			return;
+		}
+
+		if (status != WLAN_STATUS_SUCCESS) {
+			wpa_printf(MSG_DEBUG,
+				   "PASN: Failure status in transaction == 3");
+			ap_free_sta_pasn(hapd, sta);
+			return;
+		}
+
+		if (handle_auth_pasn_3(sta->pasn, hapd->own_addr,
+				       sta->addr, mgmt, len) == 0) {
+			ptksa_cache_add(hapd->ptksa, hapd->own_addr, sta->addr,
+					sta->pasn->cipher, 43200,
+					&sta->pasn->ptk, NULL, NULL,
+					sta->pasn->akmp);
+
+			pasn_set_keys_from_cache(hapd, hapd->own_addr,
+						 sta->addr, sta->pasn->cipher,
+						 sta->pasn->akmp);
+		}
+		ap_free_sta(hapd, sta);
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: Invalid transaction %u - ignore", trans_seq);
+	}
+}
+
+#endif /* CONFIG_PASN */
+
+
+static void handle_auth(struct hostapd_data *hapd,
+			const struct ieee80211_mgmt *mgmt, size_t len,
+			int rssi, int from_queue)
+{
+	u16 auth_alg, auth_transaction, status_code;
+	u16 resp = WLAN_STATUS_SUCCESS;
+	struct sta_info *sta = NULL;
+	int res, reply_res, ubus_resp;
+	u16 fc;
+	const u8 *challenge = NULL;
+	u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN];
+	size_t resp_ies_len = 0;
+	u16 seq_ctrl;
+	struct radius_sta rad_info;
+	const u8 *dst, *sa, *bssid;
+	bool mld_sta = false;
+	struct hostapd_ubus_request req = {
+		.type = HOSTAPD_UBUS_AUTH_REQ,
+		.mgmt_frame = mgmt,
+		.ssi_signal = rssi,
+	};
+
+	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
+		wpa_printf(MSG_INFO, "handle_auth - too short payload (len=%lu)",
+			   (unsigned long) len);
+		return;
+	}
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (hapd->iconf->ignore_auth_probability > 0.0 &&
+	    drand48() < hapd->iconf->ignore_auth_probability) {
+		wpa_printf(MSG_INFO,
+			   "TESTING: ignoring auth frame from " MACSTR,
+			   MAC2STR(mgmt->sa));
+		return;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	sa = mgmt->sa;
+#ifdef CONFIG_IEEE80211BE
+	/*
+	 * Handle MLO authentication before the station is added to hostapd and
+	 * the driver so that the station MLD MAC address would be used in both
+	 * hostapd and the driver.
+	 */
+	sa = hostapd_process_ml_auth(hapd, mgmt, len);
+	if (sa)
+		mld_sta = true;
+	else
+		sa = mgmt->sa;
+#endif /* CONFIG_IEEE80211BE */
+
+	auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
+	auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
+	status_code = le_to_host16(mgmt->u.auth.status_code);
+	fc = le_to_host16(mgmt->frame_control);
+	seq_ctrl = le_to_host16(mgmt->seq_ctrl);
+
+	if (len >= IEEE80211_HDRLEN + sizeof(mgmt->u.auth) +
+	    2 + WLAN_AUTH_CHALLENGE_LEN &&
+	    mgmt->u.auth.variable[0] == WLAN_EID_CHALLENGE &&
+	    mgmt->u.auth.variable[1] == WLAN_AUTH_CHALLENGE_LEN)
+		challenge = &mgmt->u.auth.variable[2];
+
+	wpa_printf(MSG_DEBUG, "authentication: STA=" MACSTR " auth_alg=%d "
+		   "auth_transaction=%d status_code=%d wep=%d%s "
+		   "seq_ctrl=0x%x%s%s",
+		   MAC2STR(sa), auth_alg, auth_transaction,
+		   status_code, !!(fc & WLAN_FC_ISWEP),
+		   challenge ? " challenge" : "",
+		   seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "",
+		   from_queue ? " (from queue)" : "");
+
+#ifdef CONFIG_NO_RC4
+	if (auth_alg == WLAN_AUTH_SHARED_KEY) {
+		wpa_printf(MSG_INFO,
+			   "Unsupported authentication algorithm (%d)",
+			   auth_alg);
+		resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
+		goto fail;
+	}
+#endif /* CONFIG_NO_RC4 */
+
+	if (hapd->tkip_countermeasures) {
+		wpa_printf(MSG_DEBUG,
+			   "Ongoing TKIP countermeasures (Michael MIC failure) - reject authentication");
+		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto fail;
+	}
+
+	if (!(((hapd->conf->auth_algs & WPA_AUTH_ALG_OPEN) &&
+	       auth_alg == WLAN_AUTH_OPEN) ||
+#ifdef CONFIG_IEEE80211R_AP
+	      (hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) &&
+	       auth_alg == WLAN_AUTH_FT) ||
+#endif /* CONFIG_IEEE80211R_AP */
+#ifdef CONFIG_SAE
+	      (hapd->conf->wpa && wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
+	       auth_alg == WLAN_AUTH_SAE) ||
+#endif /* CONFIG_SAE */
+#ifdef CONFIG_FILS
+	      (hapd->conf->wpa && wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt) &&
+	       auth_alg == WLAN_AUTH_FILS_SK) ||
+	      (hapd->conf->wpa && wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt) &&
+	       hapd->conf->fils_dh_group &&
+	       auth_alg == WLAN_AUTH_FILS_SK_PFS) ||
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_PASN
+	      (hapd->conf->wpa &&
+	       (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PASN) &&
+	       auth_alg == WLAN_AUTH_PASN) ||
+#endif /* CONFIG_PASN */
+	      ((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) &&
+	       auth_alg == WLAN_AUTH_SHARED_KEY))) {
+		wpa_printf(MSG_INFO, "Unsupported authentication algorithm (%d)",
+			   auth_alg);
+		resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
+		goto fail;
+	}
+
+	if (!(auth_transaction == 1 || auth_alg == WLAN_AUTH_SAE ||
+#ifdef CONFIG_PASN
+	      (auth_alg == WLAN_AUTH_PASN && auth_transaction == 3) ||
+#endif /* CONFIG_PASN */
+	      (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 3))) {
+		wpa_printf(MSG_INFO, "Unknown authentication transaction number (%d)",
+			   auth_transaction);
+		resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
+		goto fail;
+	}
+
+	if (os_memcmp(mgmt->sa, hapd->own_addr, ETH_ALEN) == 0) {
+		wpa_printf(MSG_INFO, "Station " MACSTR " not allowed to authenticate",
+			   MAC2STR(sa));
+		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto fail;
+	}
+
+	if (mld_sta &&
+	    (os_memcmp(sa, hapd->own_addr, ETH_ALEN) == 0 ||
+	     os_memcmp(sa, hapd->mld_addr, ETH_ALEN) == 0)) {
+		wpa_printf(MSG_INFO,
+			   "Station " MACSTR " not allowed to authenticate",
+			   MAC2STR(sa));
+		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto fail;
+	}
+
+	if (hapd->conf->no_auth_if_seen_on) {
+		struct hostapd_data *other;
+
+		other = sta_track_seen_on(hapd->iface, sa,
+					  hapd->conf->no_auth_if_seen_on);
+		if (other) {
+			u8 *pos;
+			u32 info;
+			u8 op_class, channel, phytype;
+
+			wpa_printf(MSG_DEBUG, "%s: Reject authentication from "
+				   MACSTR " since STA has been seen on %s",
+				   hapd->conf->iface, MAC2STR(sa),
+				   hapd->conf->no_auth_if_seen_on);
+
+			resp = WLAN_STATUS_REJECTED_WITH_SUGGESTED_BSS_TRANSITION;
+			pos = &resp_ies[0];
+			*pos++ = WLAN_EID_NEIGHBOR_REPORT;
+			*pos++ = 13;
+			os_memcpy(pos, other->own_addr, ETH_ALEN);
+			pos += ETH_ALEN;
+			info = 0; /* TODO: BSSID Information */
+			WPA_PUT_LE32(pos, info);
+			pos += 4;
+			if (other->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD)
+				phytype = 8; /* dmg */
+			else if (other->iconf->ieee80211ac)
+				phytype = 9; /* vht */
+			else if (other->iconf->ieee80211n)
+				phytype = 7; /* ht */
+			else if (other->iconf->hw_mode ==
+				 HOSTAPD_MODE_IEEE80211A)
+				phytype = 4; /* ofdm */
+			else if (other->iconf->hw_mode ==
+				 HOSTAPD_MODE_IEEE80211G)
+				phytype = 6; /* erp */
+			else
+				phytype = 5; /* hrdsss */
+			if (ieee80211_freq_to_channel_ext(
+				    hostapd_hw_get_freq(other,
+							other->iconf->channel),
+				    other->iconf->secondary_channel,
+				    other->iconf->ieee80211ac,
+				    &op_class, &channel) == NUM_HOSTAPD_MODES) {
+				op_class = 0;
+				channel = other->iconf->channel;
+			}
+			*pos++ = op_class;
+			*pos++ = channel;
+			*pos++ = phytype;
+			resp_ies_len = pos - &resp_ies[0];
+			goto fail;
+		}
+	}
+
+	res = ieee802_11_allowed_address(hapd, sa, (const u8 *) mgmt, len,
+					 &rad_info);
+	if (res == HOSTAPD_ACL_REJECT) {
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+			"Ignore Authentication frame from " MACSTR
+			" due to ACL reject", MAC2STR(sa));
+		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto fail;
+	}
+	ubus_resp = hostapd_ubus_handle_event(hapd, &req);
+	if (ubus_resp) {
+		wpa_printf(MSG_DEBUG, "Station " MACSTR " rejected by ubus handler.\n",
+			MAC2STR(mgmt->sa));
+		resp = ubus_resp > 0 ? (u16) ubus_resp : WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto fail;
+	}
+	if (res == HOSTAPD_ACL_PENDING)
+		return;
+
+#ifdef CONFIG_SAE
+	if (auth_alg == WLAN_AUTH_SAE && !from_queue &&
+	    (auth_transaction == 1 ||
+	     (auth_transaction == 2 && auth_sae_queued_addr(hapd, sa)))) {
+		/* Handle SAE Authentication commit message through a queue to
+		 * provide more control for postponing the needed heavy
+		 * processing under a possible DoS attack scenario. In addition,
+		 * queue SAE Authentication confirm message if there happens to
+		 * be a queued commit message from the same peer. This is needed
+		 * to avoid reordering Authentication frames within the same
+		 * SAE exchange. */
+		auth_sae_queue(hapd, mgmt, len, rssi);
+		return;
+	}
+#endif /* CONFIG_SAE */
+
+	sta = ap_get_sta(hapd, sa);
+	if (sta) {
+		sta->flags &= ~WLAN_STA_PENDING_FILS_ERP;
+		sta->ft_over_ds = 0;
+		if ((fc & WLAN_FC_RETRY) &&
+		    sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ &&
+		    sta->last_seq_ctrl == seq_ctrl &&
+		    sta->last_subtype == WLAN_FC_STYPE_AUTH) {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "Drop repeated authentication frame seq_ctrl=0x%x",
+				       seq_ctrl);
+			return;
+		}
+#ifdef CONFIG_PASN
+		if (auth_alg == WLAN_AUTH_PASN &&
+		    (sta->flags & WLAN_STA_ASSOC)) {
+			wpa_printf(MSG_DEBUG,
+				   "PASN: auth: Existing station: " MACSTR,
+				   MAC2STR(sta->addr));
+			return;
+		}
+#endif /* CONFIG_PASN */
+	} else {
+#ifdef CONFIG_MESH
+		if (hapd->conf->mesh & MESH_ENABLED) {
+			/* if the mesh peer is not available, we don't do auth.
+			 */
+			wpa_printf(MSG_DEBUG, "Mesh peer " MACSTR
+				   " not yet known - drop Authentication frame",
+				   MAC2STR(sa));
+			/*
+			 * Save a copy of the frame so that it can be processed
+			 * if a new peer entry is added shortly after this.
+			 */
+			wpabuf_free(hapd->mesh_pending_auth);
+			hapd->mesh_pending_auth = wpabuf_alloc_copy(mgmt, len);
+			os_get_reltime(&hapd->mesh_pending_auth_time);
+			return;
+		}
+#endif /* CONFIG_MESH */
+
+		sta = ap_sta_add(hapd, sa);
+		if (!sta) {
+			wpa_printf(MSG_DEBUG, "ap_sta_add() failed");
+			resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+			goto fail;
+		}
+	}
+
+#ifdef CONFIG_IEEE80211BE
+	if (auth_transaction == 1) {
+		os_memset(&sta->mld_info, 0, sizeof(sta->mld_info));
+
+		if (mld_sta) {
+			u8 link_id = hapd->mld_link_id;
+
+			sta->mld_info.mld_sta = true;
+			sta->mld_assoc_link_id = link_id;
+
+			/*
+			 * Set the MLD address as the station address and the
+			 * station addresses.
+			 */
+			os_memcpy(sta->mld_info.common_info.mld_addr, sa,
+				  ETH_ALEN);
+			os_memcpy(sta->mld_info.links[link_id].peer_addr,
+				  mgmt->sa, ETH_ALEN);
+			os_memcpy(sta->mld_info.links[link_id].local_addr,
+				  hapd->own_addr, ETH_ALEN);
+		}
+	}
+#endif /* CONFIG_IEEE80211BE */
+
+	sta->last_seq_ctrl = seq_ctrl;
+	sta->last_subtype = WLAN_FC_STYPE_AUTH;
+#ifdef CONFIG_MBO
+	sta->auth_rssi = rssi;
+#endif /* CONFIG_MBO */
+
+	res = ieee802_11_set_radius_info(hapd, sta, res, &rad_info);
+	if (res) {
+		wpa_printf(MSG_DEBUG, "ieee802_11_set_radius_info() failed");
+		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto fail;
+	}
+
+	sta->flags &= ~WLAN_STA_PREAUTH;
+	ieee802_1x_notify_pre_auth(sta->eapol_sm, 0);
+
+	/*
+	 * If the driver supports full AP client state, add a station to the
+	 * driver before sending authentication reply to make sure the driver
+	 * has resources, and not to go through the entire authentication and
+	 * association handshake, and fail it at the end.
+	 *
+	 * If this is not the first transaction, in a multi-step authentication
+	 * algorithm, the station already exists in the driver
+	 * (sta->added_unassoc = 1) so skip it.
+	 *
+	 * In mesh mode, the station was already added to the driver when the
+	 * NEW_PEER_CANDIDATE event is received.
+	 *
+	 * If PMF was negotiated for the existing association, skip this to
+	 * avoid dropping the STA entry and the associated keys. This is needed
+	 * to allow the original connection work until the attempt can complete
+	 * (re)association, so that unprotected Authentication frame cannot be
+	 * used to bypass PMF protection.
+	 *
+	 * PASN authentication does not require adding/removing station to the
+	 * driver so skip this flow in case of PASN authentication.
+	 */
+	if (FULL_AP_CLIENT_STATE_SUPP(hapd->iface->drv_flags) &&
+	    (!(sta->flags & WLAN_STA_MFP) || !ap_sta_is_authorized(sta)) &&
+	    !(hapd->conf->mesh & MESH_ENABLED) &&
+	    !(sta->added_unassoc) && auth_alg != WLAN_AUTH_PASN) {
+		if (ap_sta_re_add(hapd, sta) < 0) {
+			resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+			goto fail;
+		}
+	}
+
+	switch (auth_alg) {
+	case WLAN_AUTH_OPEN:
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "authentication OK (open system)");
+		sta->flags |= WLAN_STA_AUTH;
+		wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
+		sta->auth_alg = WLAN_AUTH_OPEN;
+		mlme_authenticate_indication(hapd, sta);
+		break;
+#ifdef CONFIG_WEP
+#ifndef CONFIG_NO_RC4
+	case WLAN_AUTH_SHARED_KEY:
+		resp = auth_shared_key(hapd, sta, auth_transaction, challenge,
+				       fc & WLAN_FC_ISWEP);
+		if (resp != 0)
+			wpa_printf(MSG_DEBUG,
+				   "auth_shared_key() failed: status=%d", resp);
+		sta->auth_alg = WLAN_AUTH_SHARED_KEY;
+		mlme_authenticate_indication(hapd, sta);
+		if (sta->challenge && auth_transaction == 1) {
+			resp_ies[0] = WLAN_EID_CHALLENGE;
+			resp_ies[1] = WLAN_AUTH_CHALLENGE_LEN;
+			os_memcpy(resp_ies + 2, sta->challenge,
+				  WLAN_AUTH_CHALLENGE_LEN);
+			resp_ies_len = 2 + WLAN_AUTH_CHALLENGE_LEN;
+		}
+		break;
+#endif /* CONFIG_NO_RC4 */
+#endif /* CONFIG_WEP */
+#ifdef CONFIG_IEEE80211R_AP
+	case WLAN_AUTH_FT:
+		sta->auth_alg = WLAN_AUTH_FT;
+		if (sta->wpa_sm == NULL)
+			sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
+							sta->addr, NULL);
+		if (sta->wpa_sm == NULL) {
+			wpa_printf(MSG_DEBUG, "FT: Failed to initialize WPA "
+				   "state machine");
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+		}
+		wpa_ft_process_auth(sta->wpa_sm, mgmt->bssid,
+				    auth_transaction, mgmt->u.auth.variable,
+				    len - IEEE80211_HDRLEN -
+				    sizeof(mgmt->u.auth),
+				    handle_auth_ft_finish, hapd);
+		/* handle_auth_ft_finish() callback will complete auth. */
+		return;
+#endif /* CONFIG_IEEE80211R_AP */
+#ifdef CONFIG_SAE
+	case WLAN_AUTH_SAE:
+#ifdef CONFIG_MESH
+		if (status_code == WLAN_STATUS_SUCCESS &&
+		    hapd->conf->mesh & MESH_ENABLED) {
+			if (sta->wpa_sm == NULL)
+				sta->wpa_sm =
+					wpa_auth_sta_init(hapd->wpa_auth,
+							  sta->addr, NULL);
+			if (sta->wpa_sm == NULL) {
+				wpa_printf(MSG_DEBUG,
+					   "SAE: Failed to initialize WPA state machine");
+				resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+				goto fail;
+			}
+		}
+#endif /* CONFIG_MESH */
+		handle_auth_sae(hapd, sta, mgmt, len, auth_transaction,
+				status_code);
+		return;
+#endif /* CONFIG_SAE */
+#ifdef CONFIG_FILS
+	case WLAN_AUTH_FILS_SK:
+	case WLAN_AUTH_FILS_SK_PFS:
+		handle_auth_fils(hapd, sta, mgmt->u.auth.variable,
+				 len - IEEE80211_HDRLEN - sizeof(mgmt->u.auth),
+				 auth_alg, auth_transaction, status_code,
+				 handle_auth_fils_finish);
+		return;
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_PASN
+	case WLAN_AUTH_PASN:
+		handle_auth_pasn(hapd, sta, mgmt, len, auth_transaction,
+				 status_code);
+		return;
+#endif /* CONFIG_PASN */
+	}
+
+ fail:
+	dst = mgmt->sa;
+	bssid = mgmt->bssid;
+
+#ifdef CONFIG_IEEE80211BE
+	 /*
+	  * Once a non-AP MLD is added to the driver, the addressing should use
+	  * the MLD MAC address. It is the responsibility of the driver to
+	  * handle the translations.
+	  */
+	if (hapd->conf->mld_ap && sta && sta->mld_info.mld_sta) {
+		dst = sta->addr;
+		bssid = hapd->mld_addr;
+	}
+#endif /* CONFIG_IEEE80211BE */
+
+	reply_res = send_auth_reply(hapd, sta, dst, bssid, auth_alg,
+				    auth_alg == WLAN_AUTH_SAE ?
+				    auth_transaction : auth_transaction + 1,
+				    resp, resp_ies, resp_ies_len,
+				    "handle-auth");
+
+	if (sta && sta->added_unassoc && (resp != WLAN_STATUS_SUCCESS ||
+					  reply_res != WLAN_STATUS_SUCCESS)) {
+		hostapd_drv_sta_remove(hapd, sta->addr);
+		sta->added_unassoc = 0;
+	}
+}
+
+
+static u8 hostapd_max_bssid_indicator(struct hostapd_data *hapd)
+{
+	size_t num_bss_nontx;
+	u8 max_bssid_ind = 0;
+
+	if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1)
+		return 0;
+
+	num_bss_nontx = hapd->iface->num_bss - 1;
+	while (num_bss_nontx > 0) {
+		max_bssid_ind++;
+		num_bss_nontx >>= 1;
+	}
+	return max_bssid_ind;
+}
+
+
+static u32 hostapd_get_aid_word(struct hostapd_data *hapd,
+				struct sta_info *sta, int i)
+{
+#ifdef CONFIG_IEEE80211BE
+	u32 aid_word = 0;
+
+	/* Do not assign an AID that is in use on any of the affiliated links
+	 * when finding an AID for a non-AP MLD. */
+	if (hapd->conf->mld_ap) {
+		int j;
+
+		for (j = 0; j < MAX_NUM_MLD_LINKS; j++) {
+			struct hostapd_data *link_bss;
+
+			if (!sta->mld_info.links[j].valid)
+				continue;
+
+			link_bss = hostapd_mld_get_link_bss(hapd, j);
+			if (!link_bss) {
+				/* This shouldn't happen, just skip */
+				wpa_printf(MSG_ERROR,
+					   "MLD: Failed to get link BSS for AID");
+				continue;
+			}
+
+			aid_word |= link_bss->sta_aid[i];
+		}
+
+		return aid_word;
+	}
+#endif /* CONFIG_IEEE80211BE */
+
+	return hapd->sta_aid[i];
+}
+
+
+int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	int i, j = 32, aid;
+
+	/* get a unique AID */
+	if (sta->aid > 0) {
+		wpa_printf(MSG_DEBUG, "  old AID %d", sta->aid);
+		return 0;
+	}
+
+	if (TEST_FAIL())
+		return -1;
+
+	for (i = 0; i < AID_WORDS; i++) {
+		u32 aid_word = hostapd_get_aid_word(hapd, sta, i);
+
+		if (aid_word == (u32) -1)
+			continue;
+		for (j = 0; j < 32; j++) {
+			if (!(aid_word & BIT(j)))
+				break;
+		}
+		if (j < 32)
+			break;
+	}
+	if (j == 32)
+		return -1;
+	aid = i * 32 + j + (1 << hostapd_max_bssid_indicator(hapd));
+	if (aid > 2007)
+		return -1;
+
+	sta->aid = aid;
+	hapd->sta_aid[i] |= BIT(j);
+	wpa_printf(MSG_DEBUG, "  new AID %d", sta->aid);
+	return 0;
+}
+
+
+static u16 check_ssid(struct hostapd_data *hapd, struct sta_info *sta,
+		      const u8 *ssid_ie, size_t ssid_ie_len)
+{
+	if (ssid_ie == NULL)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+	if (ssid_ie_len != hapd->conf->ssid.ssid_len ||
+	    os_memcmp(ssid_ie, hapd->conf->ssid.ssid, ssid_ie_len) != 0) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO,
+			       "Station tried to associate with unknown SSID "
+			       "'%s'", wpa_ssid_txt(ssid_ie, ssid_ie_len));
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+static u16 check_wmm(struct hostapd_data *hapd, struct sta_info *sta,
+		     const u8 *wmm_ie, size_t wmm_ie_len)
+{
+	sta->flags &= ~WLAN_STA_WMM;
+	sta->qosinfo = 0;
+	if (wmm_ie && hapd->conf->wmm_enabled) {
+		struct wmm_information_element *wmm;
+
+		if (!hostapd_eid_wmm_valid(hapd, wmm_ie, wmm_ie_len)) {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_WPA,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "invalid WMM element in association "
+				       "request");
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+		}
+
+		sta->flags |= WLAN_STA_WMM;
+		wmm = (struct wmm_information_element *) wmm_ie;
+		sta->qosinfo = wmm->qos_info;
+	}
+	return WLAN_STATUS_SUCCESS;
+}
+
+static u16 check_multi_ap(struct hostapd_data *hapd, struct sta_info *sta,
+			  const u8 *multi_ap_ie, size_t multi_ap_len)
+{
+	u8 multi_ap_value = 0;
+
+	sta->flags &= ~WLAN_STA_MULTI_AP;
+
+	if (!hapd->conf->multi_ap)
+		return WLAN_STATUS_SUCCESS;
+
+	if (multi_ap_ie) {
+		const u8 *multi_ap_subelem;
+
+		multi_ap_subelem = get_ie(multi_ap_ie + 4,
+					  multi_ap_len - 4,
+					  MULTI_AP_SUB_ELEM_TYPE);
+		if (multi_ap_subelem && multi_ap_subelem[1] == 1) {
+			multi_ap_value = multi_ap_subelem[2];
+		} else {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_INFO,
+				       "Multi-AP IE has missing or invalid Multi-AP subelement");
+			return WLAN_STATUS_INVALID_IE;
+		}
+	}
+
+	if (multi_ap_value && multi_ap_value != MULTI_AP_BACKHAUL_STA)
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO,
+			       "Multi-AP IE with unexpected value 0x%02x",
+			       multi_ap_value);
+
+	if (!(multi_ap_value & MULTI_AP_BACKHAUL_STA)) {
+		if (hapd->conf->multi_ap & FRONTHAUL_BSS)
+			return WLAN_STATUS_SUCCESS;
+
+		hostapd_logger(hapd, sta->addr,
+			       HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO,
+			       "Non-Multi-AP STA tries to associate with backhaul-only BSS");
+		return WLAN_STATUS_ASSOC_DENIED_UNSPEC;
+	}
+
+	if (!(hapd->conf->multi_ap & BACKHAUL_BSS))
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "Backhaul STA tries to associate with fronthaul-only BSS");
+
+	sta->flags |= WLAN_STA_MULTI_AP;
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+static u16 copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta,
+			   struct ieee802_11_elems *elems)
+{
+	/* Supported rates not used in IEEE 802.11ad/DMG */
+	if (hapd->iface->current_mode &&
+	    hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD)
+		return WLAN_STATUS_SUCCESS;
+
+	if (!elems->supp_rates) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "No supported rates element in AssocReq");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	if (elems->supp_rates_len + elems->ext_supp_rates_len >
+	    sizeof(sta->supported_rates)) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "Invalid supported rates element length %d+%d",
+			       elems->supp_rates_len,
+			       elems->ext_supp_rates_len);
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	sta->supported_rates_len = merge_byte_arrays(
+		sta->supported_rates, sizeof(sta->supported_rates),
+		elems->supp_rates, elems->supp_rates_len,
+		elems->ext_supp_rates, elems->ext_supp_rates_len);
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+#ifdef CONFIG_OWE
+
+static int owe_group_supported(struct hostapd_data *hapd, u16 group)
+{
+	int i;
+	int *groups = hapd->conf->owe_groups;
+
+	if (group != 19 && group != 20 && group != 21)
+		return 0;
+
+	if (!groups)
+		return 1;
+
+	for (i = 0; groups[i] > 0; i++) {
+		if (groups[i] == group)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static u16 owe_process_assoc_req(struct hostapd_data *hapd,
+				 struct sta_info *sta, const u8 *owe_dh,
+				 u8 owe_dh_len)
+{
+	struct wpabuf *secret, *pub, *hkey;
+	int res;
+	u8 prk[SHA512_MAC_LEN], pmkid[SHA512_MAC_LEN];
+	const char *info = "OWE Key Generation";
+	const u8 *addr[2];
+	size_t len[2];
+	u16 group;
+	size_t hash_len, prime_len;
+
+	if (wpa_auth_sta_get_pmksa(sta->wpa_sm)) {
+		wpa_printf(MSG_DEBUG, "OWE: Using PMKSA caching");
+		return WLAN_STATUS_SUCCESS;
+	}
+
+	group = WPA_GET_LE16(owe_dh);
+	if (!owe_group_supported(hapd, group)) {
+		wpa_printf(MSG_DEBUG, "OWE: Unsupported DH group %u", group);
+		return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+	}
+	if (group == 19)
+		prime_len = 32;
+	else if (group == 20)
+		prime_len = 48;
+	else if (group == 21)
+		prime_len = 66;
+	else
+		return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+
+	if (sta->owe_group == group && sta->owe_ecdh) {
+		/* This is a workaround for mac80211 behavior of retransmitting
+		 * the Association Request frames multiple times if the link
+		 * layer retries (i.e., seq# remains same) fail. The mac80211
+		 * initiated retransmission will use a different seq# and as
+		 * such, will go through duplicate detection. If we were to
+		 * change our DH key for that attempt, there would be two
+		 * different DH shared secrets and the STA would likely select
+		 * the wrong one. */
+		wpa_printf(MSG_DEBUG,
+			   "OWE: Try to reuse own previous DH key since the STA tried to go through OWE association again");
+	} else {
+		crypto_ecdh_deinit(sta->owe_ecdh);
+		sta->owe_ecdh = crypto_ecdh_init(group);
+	}
+	if (!sta->owe_ecdh)
+		return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+	sta->owe_group = group;
+
+	secret = crypto_ecdh_set_peerkey(sta->owe_ecdh, 0, owe_dh + 2,
+					 owe_dh_len - 2);
+	secret = wpabuf_zeropad(secret, prime_len);
+	if (!secret) {
+		wpa_printf(MSG_DEBUG, "OWE: Invalid peer DH public key");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+	wpa_hexdump_buf_key(MSG_DEBUG, "OWE: DH shared secret", secret);
+
+	/* prk = HKDF-extract(C | A | group, z) */
+
+	pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
+	if (!pub) {
+		wpabuf_clear_free(secret);
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	/* PMKID = Truncate-128(Hash(C | A)) */
+	addr[0] = owe_dh + 2;
+	len[0] = owe_dh_len - 2;
+	addr[1] = wpabuf_head(pub);
+	len[1] = wpabuf_len(pub);
+	if (group == 19) {
+		res = sha256_vector(2, addr, len, pmkid);
+		hash_len = SHA256_MAC_LEN;
+	} else if (group == 20) {
+		res = sha384_vector(2, addr, len, pmkid);
+		hash_len = SHA384_MAC_LEN;
+	} else if (group == 21) {
+		res = sha512_vector(2, addr, len, pmkid);
+		hash_len = SHA512_MAC_LEN;
+	} else {
+		wpabuf_free(pub);
+		wpabuf_clear_free(secret);
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+	pub = wpabuf_zeropad(pub, prime_len);
+	if (res < 0 || !pub) {
+		wpabuf_free(pub);
+		wpabuf_clear_free(secret);
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	hkey = wpabuf_alloc(owe_dh_len - 2 + wpabuf_len(pub) + 2);
+	if (!hkey) {
+		wpabuf_free(pub);
+		wpabuf_clear_free(secret);
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	wpabuf_put_data(hkey, owe_dh + 2, owe_dh_len - 2); /* C */
+	wpabuf_put_buf(hkey, pub); /* A */
+	wpabuf_free(pub);
+	wpabuf_put_le16(hkey, group); /* group */
+	if (group == 19)
+		res = hmac_sha256(wpabuf_head(hkey), wpabuf_len(hkey),
+				  wpabuf_head(secret), wpabuf_len(secret), prk);
+	else if (group == 20)
+		res = hmac_sha384(wpabuf_head(hkey), wpabuf_len(hkey),
+				  wpabuf_head(secret), wpabuf_len(secret), prk);
+	else if (group == 21)
+		res = hmac_sha512(wpabuf_head(hkey), wpabuf_len(hkey),
+				  wpabuf_head(secret), wpabuf_len(secret), prk);
+	wpabuf_clear_free(hkey);
+	wpabuf_clear_free(secret);
+	if (res < 0)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+	wpa_hexdump_key(MSG_DEBUG, "OWE: prk", prk, hash_len);
+
+	/* PMK = HKDF-expand(prk, "OWE Key Generation", n) */
+
+	os_free(sta->owe_pmk);
+	sta->owe_pmk = os_malloc(hash_len);
+	if (!sta->owe_pmk) {
+		os_memset(prk, 0, SHA512_MAC_LEN);
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	if (group == 19)
+		res = hmac_sha256_kdf(prk, hash_len, NULL, (const u8 *) info,
+				      os_strlen(info), sta->owe_pmk, hash_len);
+	else if (group == 20)
+		res = hmac_sha384_kdf(prk, hash_len, NULL, (const u8 *) info,
+				      os_strlen(info), sta->owe_pmk, hash_len);
+	else if (group == 21)
+		res = hmac_sha512_kdf(prk, hash_len, NULL, (const u8 *) info,
+				      os_strlen(info), sta->owe_pmk, hash_len);
+	os_memset(prk, 0, SHA512_MAC_LEN);
+	if (res < 0) {
+		os_free(sta->owe_pmk);
+		sta->owe_pmk = NULL;
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+	sta->owe_pmk_len = hash_len;
+
+	wpa_hexdump_key(MSG_DEBUG, "OWE: PMK", sta->owe_pmk, sta->owe_pmk_len);
+	wpa_hexdump(MSG_DEBUG, "OWE: PMKID", pmkid, PMKID_LEN);
+	wpa_auth_pmksa_add2(hapd->wpa_auth, sta->addr, sta->owe_pmk,
+			    sta->owe_pmk_len, pmkid, 0, WPA_KEY_MGMT_OWE);
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+u16 owe_validate_request(struct hostapd_data *hapd, const u8 *peer,
+			 const u8 *rsn_ie, size_t rsn_ie_len,
+			 const u8 *owe_dh, size_t owe_dh_len)
+{
+	struct wpa_ie_data data;
+	int res;
+
+	if (!rsn_ie || rsn_ie_len < 2) {
+		wpa_printf(MSG_DEBUG, "OWE: Invalid RSNE from " MACSTR,
+			   MAC2STR(peer));
+		return WLAN_STATUS_INVALID_IE;
+	}
+	rsn_ie -= 2;
+	rsn_ie_len += 2;
+
+	res = wpa_parse_wpa_ie_rsn(rsn_ie, rsn_ie_len, &data);
+	if (res) {
+		wpa_printf(MSG_DEBUG, "Failed to parse RSNE from " MACSTR
+			   " (res=%d)", MAC2STR(peer), res);
+		wpa_hexdump(MSG_DEBUG, "RSNE", rsn_ie, rsn_ie_len);
+		return wpa_res_to_status_code(res);
+	}
+	if (!(data.key_mgmt & WPA_KEY_MGMT_OWE)) {
+		wpa_printf(MSG_DEBUG,
+			   "OWE: Unexpected key mgmt 0x%x from " MACSTR,
+			   (unsigned int) data.key_mgmt, MAC2STR(peer));
+		return WLAN_STATUS_AKMP_NOT_VALID;
+	}
+	if (!owe_dh) {
+		wpa_printf(MSG_DEBUG,
+			   "OWE: No Diffie-Hellman Parameter element from "
+			   MACSTR, MAC2STR(peer));
+		return WLAN_STATUS_AKMP_NOT_VALID;
+	}
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+u16 owe_process_rsn_ie(struct hostapd_data *hapd,
+		       struct sta_info *sta,
+		       const u8 *rsn_ie, size_t rsn_ie_len,
+		       const u8 *owe_dh, size_t owe_dh_len)
+{
+	u16 status;
+	u8 *owe_buf, ie[256 * 2];
+	size_t ie_len = 0;
+	enum wpa_validate_result res;
+
+	if (!rsn_ie || rsn_ie_len < 2) {
+		wpa_printf(MSG_DEBUG, "OWE: No RSNE in (Re)AssocReq");
+		status = WLAN_STATUS_INVALID_IE;
+		goto end;
+	}
+
+	if (!sta->wpa_sm)
+		sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,	sta->addr,
+						NULL);
+	if (!sta->wpa_sm) {
+		wpa_printf(MSG_WARNING,
+			   "OWE: Failed to initialize WPA state machine");
+		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto end;
+	}
+	rsn_ie -= 2;
+	rsn_ie_len += 2;
+	res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
+				  hapd->iface->freq, rsn_ie, rsn_ie_len,
+				  NULL, 0, NULL, 0, owe_dh, owe_dh_len);
+	status = wpa_res_to_status_code(res);
+	if (status != WLAN_STATUS_SUCCESS)
+		goto end;
+	status = owe_process_assoc_req(hapd, sta, owe_dh, owe_dh_len);
+	if (status != WLAN_STATUS_SUCCESS)
+		goto end;
+	owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, ie, sizeof(ie),
+						NULL, 0);
+	if (!owe_buf) {
+		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto end;
+	}
+
+	if (sta->owe_ecdh) {
+		struct wpabuf *pub;
+
+		pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
+		if (!pub) {
+			status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto end;
+		}
+
+		/* OWE Diffie-Hellman Parameter element */
+		*owe_buf++ = WLAN_EID_EXTENSION; /* Element ID */
+		*owe_buf++ = 1 + 2 + wpabuf_len(pub); /* Length */
+		*owe_buf++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension
+							 */
+		WPA_PUT_LE16(owe_buf, sta->owe_group);
+		owe_buf += 2;
+		os_memcpy(owe_buf, wpabuf_head(pub), wpabuf_len(pub));
+		owe_buf += wpabuf_len(pub);
+		wpabuf_free(pub);
+		sta->external_dh_updated = 1;
+	}
+	ie_len = owe_buf - ie;
+
+end:
+	wpa_printf(MSG_DEBUG, "OWE: Update status %d, ie len %d for peer "
+			      MACSTR, status, (unsigned int) ie_len,
+			      MAC2STR(sta->addr));
+	hostapd_drv_update_dh_ie(hapd, sta->addr, status,
+				 status == WLAN_STATUS_SUCCESS ? ie : NULL,
+				 ie_len);
+
+	return status;
+}
+
+#endif /* CONFIG_OWE */
+
+
+static bool check_sa_query(struct hostapd_data *hapd, struct sta_info *sta,
+			   int reassoc)
+{
+	if ((sta->flags &
+	     (WLAN_STA_ASSOC | WLAN_STA_MFP | WLAN_STA_AUTHORIZED)) !=
+	    (WLAN_STA_ASSOC | WLAN_STA_MFP | WLAN_STA_AUTHORIZED))
+		return false;
+
+	if (!sta->sa_query_timed_out && sta->sa_query_count > 0)
+		ap_check_sa_query_timeout(hapd, sta);
+
+	if (!sta->sa_query_timed_out &&
+	    (!reassoc || sta->auth_alg != WLAN_AUTH_FT)) {
+		/*
+		 * STA has already been associated with MFP and SA Query timeout
+		 * has not been reached. Reject the association attempt
+		 * temporarily and start SA Query, if one is not pending.
+		 */
+		if (sta->sa_query_count == 0)
+			ap_sta_start_sa_query(hapd, sta);
+
+		return true;
+	}
+
+	return false;
+}
+
+
+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,
+			     bool link)
+{
+	int resp;
+	const u8 *wpa_ie;
+	size_t wpa_ie_len;
+	const u8 *p2p_dev_addr = NULL;
+
+	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);
+	if (resp != WLAN_STATUS_SUCCESS)
+		return resp;
+	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);
+	if (resp != WLAN_STATUS_SUCCESS)
+		return resp;
+
+	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);
+	if (resp != WLAN_STATUS_SUCCESS)
+		return resp;
+	if (hapd->iconf->ieee80211n && hapd->iconf->require_ht &&
+	    !(sta->flags & WLAN_STA_HT)) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO, "Station does not support "
+			       "mandatory HT PHY - reject association");
+		return WLAN_STATUS_ASSOC_DENIED_NO_HT;
+	}
+
+#ifdef CONFIG_IEEE80211AC
+	if (hapd->iconf->ieee80211ac) {
+		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->opmode_notif);
+		if (resp != WLAN_STATUS_SUCCESS)
+			return resp;
+	}
+
+	if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht &&
+	    !(sta->flags & WLAN_STA_VHT)) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO, "Station does not support "
+			       "mandatory VHT PHY - reject association");
+		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 (resp != WLAN_STATUS_SUCCESS)
+			return resp;
+	}
+#endif /* CONFIG_IEEE80211AC */
+#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);
+		if (resp != WLAN_STATUS_SUCCESS)
+			return resp;
+
+		if (hapd->iconf->require_he && !(sta->flags & WLAN_STA_HE)) {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_INFO,
+				       "Station does not support mandatory HE PHY - reject association");
+			return WLAN_STATUS_DENIED_HE_NOT_SUPPORTED;
+		}
+
+		if (is_6ghz_op_class(hapd->iconf->op_class)) {
+			if (!(sta->flags & WLAN_STA_HE)) {
+				hostapd_logger(hapd, sta->addr,
+					       HOSTAPD_MODULE_IEEE80211,
+					       HOSTAPD_LEVEL_INFO,
+					       "Station does not support mandatory HE PHY - reject association");
+				return WLAN_STATUS_DENIED_HE_NOT_SUPPORTED;
+			}
+			resp = copy_sta_he_6ghz_capab(hapd, sta,
+						      elems->he_6ghz_band_cap);
+			if (resp != WLAN_STATUS_SUCCESS)
+				return resp;
+		}
+	}
+#endif /* CONFIG_IEEE80211AX */
+#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);
+		if (resp != WLAN_STATUS_SUCCESS)
+			return resp;
+
+		if (!link) {
+			resp = hostapd_process_ml_assoc_req(hapd, elems, sta);
+			if (resp != WLAN_STATUS_SUCCESS)
+				return resp;
+		}
+	}
+#endif /* CONFIG_IEEE80211BE */
+
+#ifdef CONFIG_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);
+		if (sta->p2p_ie)
+			p2p_dev_addr = p2p_get_go_dev_addr(sta->p2p_ie);
+	} else {
+		wpabuf_free(sta->p2p_ie);
+		sta->p2p_ie = NULL;
+	}
+#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;
+	} else if ((hapd->conf->wpa & WPA_PROTO_WPA) &&
+		   elems->wpa_ie) {
+		wpa_ie = elems->wpa_ie;
+		wpa_ie_len = elems->wpa_ie_len;
+	} else {
+		wpa_ie = NULL;
+		wpa_ie_len = 0;
+	}
+
+#ifdef CONFIG_WPS
+	sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2);
+	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))
+			return WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
+		sta->flags |= WLAN_STA_WPS;
+		wpabuf_free(sta->wps_ie);
+		sta->wps_ie = ieee802_11_vendor_ie_concat(ies, ies_len,
+							  WPS_IE_VENDOR_TYPE);
+		if (sta->wps_ie && wps_is_20(sta->wps_ie)) {
+			wpa_printf(MSG_DEBUG, "WPS: STA supports WPS 2.0");
+			sta->flags |= WLAN_STA_WPS2;
+		}
+		wpa_ie = NULL;
+		wpa_ie_len = 0;
+		if (sta->wps_ie && wps_validate_assoc_req(sta->wps_ie) < 0) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid WPS IE in "
+				   "(Re)Association Request - reject");
+			return WLAN_STATUS_INVALID_IE;
+		}
+	} else if (hapd->conf->wps_state && wpa_ie == NULL) {
+		wpa_printf(MSG_DEBUG, "STA did not include WPA/RSN IE in "
+			   "(Re)Association Request - possible WPS use");
+		sta->flags |= WLAN_STA_MAYBE_WPS;
+	} else
+#endif /* CONFIG_WPS */
+	if (hapd->conf->wpa && wpa_ie == NULL) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO,
+			       "No WPA/RSN IE in association request");
+		return WLAN_STATUS_INVALID_IE;
+	}
+
+	if (hapd->conf->wpa && wpa_ie) {
+		enum wpa_validate_result res;
+
+		if (check_sa_query(hapd, sta, reassoc))
+			return WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
+
+		wpa_ie -= 2;
+		wpa_ie_len += 2;
+
+		if (!sta->wpa_sm) {
+#ifdef CONFIG_IEEE80211BE
+			struct mld_info *info = &sta->mld_info;
+#endif /* CONFIG_IEEE80211BE */
+
+			sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
+							sta->addr,
+							p2p_dev_addr);
+
+			if (!sta->wpa_sm) {
+				wpa_printf(MSG_WARNING,
+					   "Failed to initialize RSN state machine");
+				return WLAN_STATUS_UNSPECIFIED_FAILURE;
+			}
+
+#ifdef CONFIG_IEEE80211BE
+			if (info->mld_sta) {
+				wpa_printf(MSG_DEBUG,
+					   "MLD: Set ML info in RSN Authenticator");
+				wpa_auth_set_ml_info(sta->wpa_sm,
+						     hapd->mld_addr,
+						     sta->mld_assoc_link_id,
+						     info);
+			}
+#endif /* CONFIG_IEEE80211BE */
+		}
+
+		wpa_auth_set_auth_alg(sta->wpa_sm, sta->auth_alg);
+		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);
+		resp = wpa_res_to_status_code(res);
+		if (resp != WLAN_STATUS_SUCCESS)
+			return resp;
+
+		if (wpa_auth_uses_mfp(sta->wpa_sm))
+			sta->flags |= WLAN_STA_MFP;
+		else
+			sta->flags &= ~WLAN_STA_MFP;
+
+#ifdef CONFIG_IEEE80211R_AP
+		if (sta->auth_alg == WLAN_AUTH_FT) {
+			if (!reassoc) {
+				wpa_printf(MSG_DEBUG, "FT: " MACSTR " tried "
+					   "to use association (not "
+					   "re-association) with FT auth_alg",
+					   MAC2STR(sta->addr));
+				return WLAN_STATUS_UNSPECIFIED_FAILURE;
+			}
+
+			resp = wpa_ft_validate_reassoc(sta->wpa_sm, ies,
+						       ies_len);
+			if (resp != WLAN_STATUS_SUCCESS)
+				return resp;
+		}
+#endif /* CONFIG_IEEE80211R_AP */
+
+		if (link)
+			goto skip_sae_owe;
+#ifdef CONFIG_SAE
+		if (wpa_auth_uses_sae(sta->wpa_sm) && sta->sae &&
+		    sta->sae->state == SAE_ACCEPTED)
+			wpa_auth_add_sae_pmkid(sta->wpa_sm, sta->sae->pmkid);
+
+		if (wpa_auth_uses_sae(sta->wpa_sm) &&
+		    sta->auth_alg == WLAN_AUTH_OPEN) {
+			struct rsn_pmksa_cache_entry *sa;
+			sa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
+			if (!sa || !wpa_key_mgmt_sae(sa->akmp)) {
+				wpa_printf(MSG_DEBUG,
+					   "SAE: No PMKSA cache entry found for "
+					   MACSTR, MAC2STR(sta->addr));
+				return WLAN_STATUS_INVALID_PMKID;
+			}
+			wpa_printf(MSG_DEBUG, "SAE: " MACSTR
+				   " using PMKSA caching", MAC2STR(sta->addr));
+		} else if (wpa_auth_uses_sae(sta->wpa_sm) &&
+			   sta->auth_alg != WLAN_AUTH_SAE &&
+			   !(sta->auth_alg == WLAN_AUTH_FT &&
+			     wpa_auth_uses_ft_sae(sta->wpa_sm))) {
+			wpa_printf(MSG_DEBUG, "SAE: " MACSTR " tried to use "
+				   "SAE AKM after non-SAE auth_alg %u",
+				   MAC2STR(sta->addr), sta->auth_alg);
+			return WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
+		}
+
+		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,
+					      WLAN_RSNX_CAPAB_SAE_H2E)) {
+			wpa_printf(MSG_INFO, "SAE: " MACSTR
+				   " indicates support for SAE H2E, but did not use it",
+				   MAC2STR(sta->addr));
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+		}
+#endif /* CONFIG_SAE */
+
+#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);
+			if (resp != WLAN_STATUS_SUCCESS)
+				return resp;
+		}
+#endif /* CONFIG_OWE */
+	skip_sae_owe:
+
+#ifdef CONFIG_DPP2
+		dpp_pfs_free(sta->dpp_pfs);
+		sta->dpp_pfs = NULL;
+
+		if (DPP_VERSION > 1 &&
+		    (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) {
+			sta->dpp_pfs = dpp_pfs_init(
+				wpabuf_head(hapd->conf->dpp_netaccesskey),
+				wpabuf_len(hapd->conf->dpp_netaccesskey));
+			if (!sta->dpp_pfs) {
+				wpa_printf(MSG_DEBUG,
+					   "DPP: Could not initialize PFS");
+				/* Try to continue without PFS */
+				goto pfs_fail;
+			}
+
+			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;
+			}
+		}
+
+		wpa_auth_set_dpp_z(sta->wpa_sm, sta->dpp_pfs ?
+				   sta->dpp_pfs->secret : NULL);
+	pfs_fail:
+#endif /* CONFIG_DPP2 */
+
+		if ((sta->flags & (WLAN_STA_HT | WLAN_STA_VHT)) &&
+		    wpa_auth_get_pairwise(sta->wpa_sm) == WPA_CIPHER_TKIP) {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_INFO,
+				       "Station tried to use TKIP with HT "
+				       "association");
+			return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY;
+		}
+#ifdef CONFIG_HS20
+	} else if (hapd->conf->osen) {
+		if (!elems->osen) {
+			hostapd_logger(
+				hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+				HOSTAPD_LEVEL_INFO,
+				"No HS 2.0 OSEN element in association request");
+			return WLAN_STATUS_INVALID_IE;
+		}
+
+		wpa_printf(MSG_DEBUG, "HS 2.0: OSEN association");
+		if (sta->wpa_sm == NULL)
+			sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
+							sta->addr, NULL);
+		if (sta->wpa_sm == NULL) {
+			wpa_printf(MSG_WARNING, "Failed to initialize WPA "
+				   "state machine");
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+		}
+		if (wpa_validate_osen(hapd->wpa_auth, sta->wpa_sm,
+				      elems->osen - 2, elems->osen_len + 2) < 0)
+			return WLAN_STATUS_INVALID_IE;
+#endif /* CONFIG_HS20 */
+	} else
+		wpa_auth_sta_no_wpa(sta->wpa_sm);
+
+#ifdef CONFIG_P2P
+	p2p_group_notif_assoc(hapd->p2p_group, sta->addr, ies, ies_len);
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_HS20
+	wpabuf_free(sta->hs20_ie);
+	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;
+		if (release >= 2 && !wpa_auth_uses_mfp(sta->wpa_sm) &&
+		    hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+			wpa_printf(MSG_DEBUG,
+				   "HS 2.0: PMF not negotiated by release %d station "
+				   MACSTR, release, MAC2STR(sta->addr));
+			return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
+		}
+	} else {
+		sta->hs20_ie = NULL;
+	}
+
+	wpabuf_free(sta->roaming_consortium);
+	if (elems->roaming_cons_sel)
+		sta->roaming_consortium = wpabuf_alloc_copy(
+			elems->roaming_cons_sel + 4,
+			elems->roaming_cons_sel_len - 4);
+	else
+		sta->roaming_consortium = NULL;
+#endif /* CONFIG_HS20 */
+
+#ifdef CONFIG_FST
+	wpabuf_free(sta->mb_ies);
+	if (hapd->iface->fst)
+		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);
+
+	if (hapd->conf->mbo_enabled && (hapd->conf->wpa & 2) &&
+	    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");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+#endif /* CONFIG_MBO */
+
+#if defined(CONFIG_FILS) && defined(CONFIG_OCV)
+	if (wpa_auth_uses_ocv(sta->wpa_sm) &&
+	    (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+	     sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+	     sta->auth_alg == WLAN_AUTH_FILS_PK)) {
+		struct wpa_channel_info ci;
+		int tx_chanwidth;
+		int tx_seg1_idx;
+		enum oci_verify_result res;
+
+		if (hostapd_drv_channel_info(hapd, &ci) != 0) {
+			wpa_printf(MSG_WARNING,
+				   "Failed to get channel info to validate received OCI in FILS (Re)Association Request frame");
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+		}
+
+		if (get_sta_tx_parameters(sta->wpa_sm,
+					  channel_width_to_int(ci.chanwidth),
+					  ci.seg1_idx, &tx_chanwidth,
+					  &tx_seg1_idx) < 0)
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+		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) {
+			/* Work around misbehaving STAs */
+			wpa_printf(MSG_INFO,
+				   "FILS: Disable OCV with a STA that does not send OCI");
+			wpa_auth_set_ocv(sta->wpa_sm, 0);
+		} else if (res != OCI_SUCCESS) {
+			wpa_printf(MSG_WARNING, "FILS: OCV failed: %s",
+				   ocv_errorstr);
+			wpa_msg(hapd->msg_ctx, MSG_INFO, OCV_FAILURE "addr="
+				MACSTR " frame=fils-reassoc-req error=%s",
+				MAC2STR(sta->addr), ocv_errorstr);
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+		}
+	}
+#endif /* CONFIG_FILS && CONFIG_OCV */
+
+	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,
+			  sizeof(sta->rrm_enabled_capa));
+
+	if (elems->power_capab) {
+		sta->min_tx_power = elems->power_capab[0];
+		sta->max_tx_power = elems->power_capab[1];
+		sta->power_capab = 1;
+	} else {
+		sta->power_capab = 0;
+	}
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+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,
+				 false);
+}
+
+
+#ifdef CONFIG_IEEE80211BE
+
+static size_t ieee80211_ml_build_assoc_resp(struct hostapd_data *hapd,
+					    u16 status_code,
+					    u8 *buf, size_t buflen)
+{
+	u8 *p = buf;
+
+	/* Capability Info */
+	WPA_PUT_LE16(p, hostapd_own_capab_info(hapd));
+	p += 2;
+
+	/* Status Code */
+	WPA_PUT_LE16(p, status_code);
+	p += 2;
+
+	if (status_code != WLAN_STATUS_SUCCESS)
+		return p - buf;
+
+	/* AID is not included */
+	p = hostapd_eid_supp_rates(hapd, p);
+	p = hostapd_eid_ext_supp_rates(hapd, p);
+	p = hostapd_eid_rm_enabled_capab(hapd, p, buf + buflen - p);
+	p = hostapd_eid_ht_capabilities(hapd, p);
+	p = hostapd_eid_ht_operation(hapd, p);
+
+	if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) {
+		p = hostapd_eid_vht_capabilities(hapd, p, 0);
+		p = hostapd_eid_vht_operation(hapd, p);
+	}
+
+	if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
+		p = hostapd_eid_he_capab(hapd, p, IEEE80211_MODE_AP);
+		p = hostapd_eid_he_operation(hapd, p);
+		p = hostapd_eid_spatial_reuse(hapd, p);
+		p = hostapd_eid_he_mu_edca_parameter_set(hapd, p);
+		p = hostapd_eid_he_6ghz_band_cap(hapd, p);
+		if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
+			p = hostapd_eid_eht_capab(hapd, p, IEEE80211_MODE_AP);
+			p = hostapd_eid_eht_operation(hapd, p);
+		}
+	}
+
+	p = hostapd_eid_ext_capab(hapd, p, false);
+	p = hostapd_eid_mbo(hapd, p, buf + buflen - p);
+	p = hostapd_eid_wmm(hapd, p);
+
+	if (hapd->conf->assocresp_elements &&
+	    (size_t) (buf + buflen - p) >=
+	    wpabuf_len(hapd->conf->assocresp_elements)) {
+		os_memcpy(p, wpabuf_head(hapd->conf->assocresp_elements),
+			  wpabuf_len(hapd->conf->assocresp_elements));
+		p += wpabuf_len(hapd->conf->assocresp_elements);
+	}
+
+	return p - buf;
+}
+
+
+static void ieee80211_ml_process_link(struct hostapd_data *hapd,
+				      struct sta_info *origin_sta,
+				      struct mld_link_info *link,
+				      const u8 *ies, size_t ies_len,
+				      bool reassoc)
+{
+	struct ieee802_11_elems elems;
+	struct wpabuf *mlbuf = NULL;
+	struct sta_info *sta = NULL;
+	u16 status = WLAN_STATUS_SUCCESS;
+
+	wpa_printf(MSG_DEBUG, "MLD: link: link_id=%u, peer=" MACSTR,
+		   hapd->mld_link_id, MAC2STR(link->peer_addr));
+
+	if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) {
+		wpa_printf(MSG_DEBUG, "MLD: link: Element parsing failed");
+		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto out;
+	}
+
+	sta = ap_get_sta(hapd, origin_sta->addr);
+	if (sta) {
+		wpa_printf(MSG_INFO, "MLD: link: Station already exists");
+		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		sta = NULL;
+		goto out;
+	}
+
+	sta = ap_sta_add(hapd, origin_sta->addr);
+	if (!sta) {
+		wpa_printf(MSG_DEBUG, "MLD: link: ap_sta_add() failed");
+		status = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+		goto out;
+	}
+
+	mlbuf = ieee802_11_defrag_mle(&elems, MULTI_LINK_CONTROL_TYPE_BASIC);
+	if (!mlbuf)
+		goto out;
+
+	if (ieee802_11_parse_link_assoc_req(ies, ies_len, &elems, mlbuf,
+					    hapd->mld_link_id, true) ==
+	    ParseFailed) {
+		wpa_printf(MSG_DEBUG,
+			   "MLD: link: Failed to parse association request Multi-Link element");
+		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto out;
+	}
+
+	sta->flags |= origin_sta->flags | WLAN_STA_ASSOC_REQ_OK;
+	status = __check_assoc_ies(hapd, sta, NULL, 0, &elems, reassoc, true);
+	if (status != WLAN_STATUS_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "MLD: link: Element check failed");
+		goto out;
+	}
+
+	sta->mld_info.mld_sta = true;
+	sta->mld_assoc_link_id = origin_sta->mld_assoc_link_id;
+
+	os_memcpy(&sta->mld_info, &origin_sta->mld_info, sizeof(sta->mld_info));
+
+	/*
+	 * Get the AID from the station on which the association was performed,
+	 * and mark it as used.
+	 */
+	sta->aid = origin_sta->aid;
+	if (sta->aid == 0) {
+		wpa_printf(MSG_DEBUG, "MLD: link: No AID assigned");
+		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto out;
+	}
+	hapd->sta_aid[(sta->aid - 1) / 32] |= BIT((sta->aid - 1) % 32);
+	sta->listen_interval = origin_sta->listen_interval;
+	update_ht_state(hapd, sta);
+
+	/* RSN Authenticator should always be the one on the original station */
+	wpa_auth_sta_deinit(sta->wpa_sm);
+	sta->wpa_sm = NULL;
+
+	/*
+	 * Do not initialize the EAPOL state machine.
+	 * TODO: Maybe it is needed?
+	 */
+	sta->eapol_sm = NULL;
+
+	wpa_printf(MSG_DEBUG, "MLD: link=%u, association OK (aid=%u)",
+		   hapd->mld_link_id, sta->aid);
+
+	/*
+	 * Get RSNE and RSNXE for the current BSS as they are required by the
+	 * Authenticator.
+	 */
+	link->rsne = hostapd_wpa_ie(hapd, WLAN_EID_RSN);
+	link->rsnxe = hostapd_wpa_ie(hapd, WLAN_EID_RSNX);
+
+	sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC_REQ_OK;
+
+	/* TODO: What other processing is required? */
+
+	if (add_associated_sta(hapd, sta, reassoc))
+		status = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+out:
+	wpabuf_free(mlbuf);
+	link->status = status;
+
+	wpa_printf(MSG_DEBUG, "MLD: link: status=%u", status);
+	if (sta && status != WLAN_STATUS_SUCCESS)
+		ap_free_sta(hapd, sta);
+
+	link->resp_sta_profile_len =
+		ieee80211_ml_build_assoc_resp(hapd, link->status,
+					      link->resp_sta_profile,
+					      sizeof(link->resp_sta_profile));
+}
+
+
+bool hostapd_is_mld_ap(struct hostapd_data *hapd)
+{
+	if (!hapd->conf->mld_ap)
+		return false;
+
+	if (!hapd->iface || !hapd->iface->interfaces ||
+	    hapd->iface->interfaces->count <= 1)
+		return false;
+
+	return true;
+}
+
+#endif /* CONFIG_IEEE80211BE */
+
+
+static void hostapd_process_assoc_ml_info(struct hostapd_data *hapd,
+					  struct sta_info *sta,
+					  const u8 *ies, size_t ies_len,
+					  bool reassoc)
+{
+#ifdef CONFIG_IEEE80211BE
+	unsigned int i, j;
+
+	if (!hostapd_is_mld_ap(hapd))
+		return;
+
+	/*
+	 * This is not really needed, but make the interaction with the RSN
+	 * Authenticator more consistent
+	 */
+	sta->mld_info.links[hapd->mld_link_id].rsne =
+		hostapd_wpa_ie(hapd, WLAN_EID_RSN);
+	sta->mld_info.links[hapd->mld_link_id].rsnxe =
+		hostapd_wpa_ie(hapd, WLAN_EID_RSNX);
+
+	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+		struct hostapd_iface *iface = NULL;
+		struct mld_link_info *link = &sta->mld_info.links[i];
+
+		if (!link->valid)
+			continue;
+
+		for (j = 0; j < hapd->iface->interfaces->count; j++) {
+			iface = hapd->iface->interfaces->iface[j];
+
+			if (hapd->iface == iface)
+				continue;
+
+			if (iface->bss[0]->conf->mld_ap &&
+			    hapd->conf->mld_id == iface->bss[0]->conf->mld_id &&
+			    i == iface->bss[0]->mld_link_id)
+				break;
+		}
+
+		if (!iface || j == hapd->iface->interfaces->count) {
+			wpa_printf(MSG_DEBUG,
+				   "MLD: No link match for link_id=%u", i);
+
+			link->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			link->resp_sta_profile_len =
+				ieee80211_ml_build_assoc_resp(
+					hapd, link->status,
+					link->resp_sta_profile,
+					sizeof(link->resp_sta_profile));
+		} else {
+			ieee80211_ml_process_link(iface->bss[0], sta, link,
+						  ies, ies_len, reassoc);
+		}
+	}
+#endif /* CONFIG_IEEE80211BE */
+}
+
+
+static void send_deauth(struct hostapd_data *hapd, const u8 *addr,
+			u16 reason_code)
+{
+	int send_len;
+	struct ieee80211_mgmt reply;
+
+	os_memset(&reply, 0, sizeof(reply));
+	reply.frame_control =
+		IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_DEAUTH);
+	os_memcpy(reply.da, addr, ETH_ALEN);
+	os_memcpy(reply.sa, hapd->own_addr, ETH_ALEN);
+	os_memcpy(reply.bssid, hapd->own_addr, ETH_ALEN);
+
+	send_len = IEEE80211_HDRLEN + sizeof(reply.u.deauth);
+	reply.u.deauth.reason_code = host_to_le16(reason_code);
+
+	if (hostapd_drv_send_mlme(hapd, &reply, send_len, 0, NULL, 0, 0) < 0)
+		wpa_printf(MSG_INFO, "Failed to send deauth: %s",
+			   strerror(errno));
+}
+
+
+static int add_associated_sta(struct hostapd_data *hapd,
+			      struct sta_info *sta, int reassoc)
+{
+	struct ieee80211_ht_capabilities ht_cap;
+	struct ieee80211_vht_capabilities vht_cap;
+	struct ieee80211_he_capabilities he_cap;
+	struct ieee80211_eht_capabilities eht_cap;
+	int set = 1;
+	const u8 *mld_link_addr = NULL;
+	bool mld_link_sta = false;
+
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->conf->mld_ap && sta->mld_info.mld_sta) {
+		u8 mld_link_id = hapd->mld_link_id;
+
+		mld_link_sta = sta->mld_assoc_link_id != mld_link_id;
+		mld_link_addr = sta->mld_info.links[mld_link_id].peer_addr;
+
+		if (hapd->mld_link_id != sta->mld_assoc_link_id)
+			set = 0;
+	}
+#endif /* CONFIG_IEEE80211BE */
+
+	/*
+	 * Remove the STA entry to ensure the STA PS state gets cleared and
+	 * configuration gets updated. This is relevant for cases, such as
+	 * FT-over-the-DS, where a station re-associates back to the same AP but
+	 * skips the authentication flow, or if working with a driver that
+	 * does not support full AP client state.
+	 *
+	 * Skip this if the STA has already completed FT reassociation and the
+	 * TK has been configured since the TX/RX PN must not be reset to 0 for
+	 * the same key.
+	 *
+	 * FT-over-the-DS has a special case where the STA entry (and as such,
+	 * the TK) has not yet been configured to the driver depending on which
+	 * driver interface is used. For that case, allow add-STA operation to
+	 * be used (instead of set-STA). This is needed to allow mac80211-based
+	 * drivers to accept the STA parameter configuration. Since this is
+	 * after a new FT-over-DS exchange, a new TK has been derived, so key
+	 * reinstallation is not a concern for this case.
+	 *
+	 * If the STA was associated and authorized earlier, but came for a new
+	 * connection (!added_unassoc + !reassoc), remove the existing STA entry
+	 * so that it can be re-added. This case is rarely seen when the AP could
+	 * not receive the deauth/disassoc frame from the STA. And the STA comes
+	 * back with new connection within a short period or before the inactive
+	 * STA entry is removed from the list.
+	 */
+	wpa_printf(MSG_DEBUG, "Add associated STA " MACSTR
+		   " (added_unassoc=%d auth_alg=%u ft_over_ds=%u reassoc=%d authorized=%d ft_tk=%d fils_tk=%d)",
+		   MAC2STR(sta->addr), sta->added_unassoc, sta->auth_alg,
+		   sta->ft_over_ds, reassoc,
+		   !!(sta->flags & WLAN_STA_AUTHORIZED),
+		   wpa_auth_sta_ft_tk_already_set(sta->wpa_sm),
+		   wpa_auth_sta_fils_tk_already_set(sta->wpa_sm));
+
+	if (!mld_link_sta && !sta->added_unassoc &&
+	    (!(sta->flags & WLAN_STA_AUTHORIZED) ||
+	     (reassoc && sta->ft_over_ds && sta->auth_alg == WLAN_AUTH_FT) ||
+	     (!wpa_auth_sta_ft_tk_already_set(sta->wpa_sm) &&
+	      !wpa_auth_sta_fils_tk_already_set(sta->wpa_sm)) ||
+	     (!reassoc && (sta->flags & WLAN_STA_AUTHORIZED)))) {
+		hostapd_drv_sta_remove(hapd, sta->addr);
+		wpa_auth_sm_event(sta->wpa_sm, WPA_DRV_STA_REMOVED);
+		set = 0;
+
+		 /* Do not allow the FT-over-DS exception to be used more than
+		  * once per authentication exchange to guarantee a new TK is
+		  * used here */
+		sta->ft_over_ds = 0;
+	}
+
+	if (sta->flags & WLAN_STA_HT)
+		hostapd_get_ht_capab(hapd, sta->ht_capabilities, &ht_cap);
+#ifdef CONFIG_IEEE80211AC
+	if (sta->flags & WLAN_STA_VHT)
+		hostapd_get_vht_capab(hapd, sta->vht_capabilities, &vht_cap);
+#endif /* CONFIG_IEEE80211AC */
+#ifdef CONFIG_IEEE80211AX
+	if (sta->flags & WLAN_STA_HE) {
+		hostapd_get_he_capab(hapd, sta->he_capab, &he_cap,
+				     sta->he_capab_len);
+	}
+#endif /* CONFIG_IEEE80211AX */
+#ifdef CONFIG_IEEE80211BE
+	if (sta->flags & WLAN_STA_EHT)
+		hostapd_get_eht_capab(hapd, sta->eht_capab, &eht_cap,
+				      sta->eht_capab_len);
+#endif /* CONFIG_IEEE80211BE */
+
+	/*
+	 * Add the station with forced WLAN_STA_ASSOC flag. The sta->flags
+	 * will be set when the ACK frame for the (Re)Association Response frame
+	 * is processed (TX status driver event).
+	 */
+	if (hostapd_sta_add(hapd, sta->addr, sta->aid, sta->capability,
+			    sta->supported_rates, sta->supported_rates_len,
+			    sta->listen_interval,
+			    sta->flags & WLAN_STA_HT ? &ht_cap : NULL,
+			    sta->flags & WLAN_STA_VHT ? &vht_cap : NULL,
+			    sta->flags & WLAN_STA_HE ? &he_cap : NULL,
+			    sta->flags & WLAN_STA_HE ? sta->he_capab_len : 0,
+			    sta->flags & WLAN_STA_EHT ? &eht_cap : NULL,
+			    sta->flags & WLAN_STA_EHT ? sta->eht_capab_len : 0,
+			    sta->he_6ghz_capab,
+			    sta->flags | WLAN_STA_ASSOC, sta->qosinfo,
+			    sta->vht_opmode, sta->p2p_ie ? 1 : 0,
+			    set, mld_link_addr, mld_link_sta)) {
+		hostapd_logger(hapd, sta->addr,
+			       HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE,
+			       "Could not %s STA to kernel driver",
+			       set ? "set" : "add");
+
+		if (sta->added_unassoc) {
+			hostapd_drv_sta_remove(hapd, sta->addr);
+			sta->added_unassoc = 0;
+		}
+
+		return -1;
+	}
+
+	sta->added_unassoc = 0;
+
+	return 0;
+}
+
+
+static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
+			   const u8 *addr, u16 status_code, int reassoc,
+			   const u8 *ies, size_t ies_len, int rssi,
+			   int omit_rsnxe)
+{
+	int send_len;
+	u8 *buf;
+	size_t buflen;
+	struct ieee80211_mgmt *reply;
+	u8 *p;
+	u16 res = WLAN_STATUS_SUCCESS;
+	const u8 *sa = hapd->own_addr;
+
+	buflen = sizeof(struct ieee80211_mgmt) + 1024;
+#ifdef CONFIG_FILS
+	if (sta && sta->fils_hlp_resp)
+		buflen += wpabuf_len(sta->fils_hlp_resp);
+	if (sta)
+		buflen += 150;
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_OWE
+	if (sta && (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE))
+		buflen += 150;
+#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP2
+	if (sta && sta->dpp_pfs)
+		buflen += 5 + sta->dpp_pfs->curve->prime_len;
+#endif /* CONFIG_DPP2 */
+#ifdef CONFIG_IEEE80211BE
+	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 */
+
+	buf = os_zalloc(buflen);
+	if (!buf) {
+		res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto done;
+	}
+	reply = (struct ieee80211_mgmt *) buf;
+	reply->frame_control =
+		IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+			     (reassoc ? WLAN_FC_STYPE_REASSOC_RESP :
+			      WLAN_FC_STYPE_ASSOC_RESP));
+
+#ifdef CONFIG_IEEE80211BE
+	/*
+	 * Once a non-AP MLD is added to the driver, the addressing should use
+	 * MLD MAC address.
+	 */
+	if (hapd->conf->mld_ap && sta && sta->mld_info.mld_sta)
+		sa = hapd->mld_addr;
+#endif /* CONFIG_IEEE80211BE */
+
+	os_memcpy(reply->da, addr, ETH_ALEN);
+	os_memcpy(reply->sa, sa, ETH_ALEN);
+	os_memcpy(reply->bssid, sa, ETH_ALEN);
+
+	send_len = IEEE80211_HDRLEN;
+	send_len += sizeof(reply->u.assoc_resp);
+	reply->u.assoc_resp.capab_info =
+		host_to_le16(hostapd_own_capab_info(hapd));
+	reply->u.assoc_resp.status_code = host_to_le16(status_code);
+
+	reply->u.assoc_resp.aid = host_to_le16((sta ? sta->aid : 0) |
+					       BIT(14) | BIT(15));
+	/* Supported rates */
+	p = hostapd_eid_supp_rates(hapd, reply->u.assoc_resp.variable);
+	/* Extended supported rates */
+	p = hostapd_eid_ext_supp_rates(hapd, p);
+
+	/* Radio measurement capabilities */
+	p = hostapd_eid_rm_enabled_capab(hapd, p, buf + buflen - p);
+
+#ifdef CONFIG_MBO
+	if (status_code == WLAN_STATUS_DENIED_POOR_CHANNEL_CONDITIONS &&
+	    rssi != 0) {
+		int delta = hapd->iconf->rssi_reject_assoc_rssi - rssi;
+
+		p = hostapd_eid_mbo_rssi_assoc_rej(hapd, p, buf + buflen - p,
+						   delta);
+	}
+#endif /* CONFIG_MBO */
+
+#ifdef CONFIG_IEEE80211R_AP
+	if (sta && status_code == WLAN_STATUS_SUCCESS) {
+		/* IEEE 802.11r: Mobility Domain Information, Fast BSS
+		 * Transition Information, RSN, [RIC Response] */
+		p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, p,
+						buf + buflen - p,
+						sta->auth_alg, ies, ies_len,
+						omit_rsnxe);
+		if (!p) {
+			wpa_printf(MSG_DEBUG,
+				   "FT: Failed to write AssocResp IEs");
+			res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto done;
+		}
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+#ifdef CONFIG_FILS
+	if (sta && status_code == WLAN_STATUS_SUCCESS &&
+	    (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+	     sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+	     sta->auth_alg == WLAN_AUTH_FILS_PK))
+		p = wpa_auth_write_assoc_resp_fils(sta->wpa_sm, p,
+						   buf + buflen - p,
+						   ies, ies_len);
+#endif /* CONFIG_FILS */
+
+#ifdef CONFIG_OWE
+	if (sta && status_code == WLAN_STATUS_SUCCESS &&
+	    (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE))
+		p = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, p,
+						  buf + buflen - p,
+						  ies, ies_len);
+#endif /* CONFIG_OWE */
+
+	if (sta && status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY)
+		p = hostapd_eid_assoc_comeback_time(hapd, sta, p);
+
+	p = hostapd_eid_ht_capabilities(hapd, p);
+	p = hostapd_eid_ht_operation(hapd, p);
+
+#ifdef CONFIG_IEEE80211AC
+	if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac &&
+	    !is_6ghz_op_class(hapd->iconf->op_class)) {
+		u32 nsts = 0, sta_nsts;
+
+		if (sta && hapd->conf->use_sta_nsts && sta->vht_capabilities) {
+			struct ieee80211_vht_capabilities *capa;
+
+			nsts = (hapd->iface->conf->vht_capab >>
+				VHT_CAP_BEAMFORMEE_STS_OFFSET) & 7;
+			capa = sta->vht_capabilities;
+			sta_nsts = (le_to_host32(capa->vht_capabilities_info) >>
+				    VHT_CAP_BEAMFORMEE_STS_OFFSET) & 7;
+
+			if (nsts < sta_nsts)
+				nsts = 0;
+			else
+				nsts = sta_nsts;
+		}
+		p = hostapd_eid_vht_capabilities(hapd, p, nsts);
+		p = hostapd_eid_vht_operation(hapd, p);
+	}
+#endif /* CONFIG_IEEE80211AC */
+
+#ifdef CONFIG_IEEE80211AX
+	if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
+		p = hostapd_eid_he_capab(hapd, p, IEEE80211_MODE_AP);
+		p = hostapd_eid_he_operation(hapd, p);
+		p = hostapd_eid_cca(hapd, p);
+		p = hostapd_eid_spatial_reuse(hapd, p);
+		p = hostapd_eid_he_mu_edca_parameter_set(hapd, p);
+		p = hostapd_eid_he_6ghz_band_cap(hapd, p);
+	}
+#endif /* CONFIG_IEEE80211AX */
+
+	p = hostapd_eid_ext_capab(hapd, p, false);
+	p = hostapd_eid_bss_max_idle_period(hapd, p);
+	if (sta && sta->qos_map_enabled)
+		p = hostapd_eid_qos_map_set(hapd, p);
+
+#ifdef CONFIG_FST
+	if (hapd->iface->fst_ies) {
+		os_memcpy(p, wpabuf_head(hapd->iface->fst_ies),
+			  wpabuf_len(hapd->iface->fst_ies));
+		p += wpabuf_len(hapd->iface->fst_ies);
+	}
+#endif /* CONFIG_FST */
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (hapd->conf->rsnxe_override_ft &&
+	    buf + buflen - p >=
+	    (long int) wpabuf_len(hapd->conf->rsnxe_override_ft) &&
+	    sta && sta->auth_alg == WLAN_AUTH_FT) {
+		wpa_printf(MSG_DEBUG, "TESTING: RSNXE FT override");
+		os_memcpy(p, wpabuf_head(hapd->conf->rsnxe_override_ft),
+			  wpabuf_len(hapd->conf->rsnxe_override_ft));
+		p += wpabuf_len(hapd->conf->rsnxe_override_ft);
+		goto rsnxe_done;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+	if (!omit_rsnxe)
+		p = hostapd_eid_rsnxe(hapd, p, buf + buflen - p);
+#ifdef CONFIG_TESTING_OPTIONS
+rsnxe_done:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
+		if (hapd->conf->mld_ap)
+			p = hostapd_eid_eht_basic_ml(hapd, p, sta, false);
+		p = hostapd_eid_eht_capab(hapd, p, IEEE80211_MODE_AP);
+		p = hostapd_eid_eht_operation(hapd, p);
+	}
+#endif /* CONFIG_IEEE80211BE */
+
+#ifdef CONFIG_OWE
+	if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
+	    sta && sta->owe_ecdh && status_code == WLAN_STATUS_SUCCESS &&
+	    wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE &&
+	    !wpa_auth_sta_get_pmksa(sta->wpa_sm)) {
+		struct wpabuf *pub;
+
+		pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
+		if (!pub) {
+			res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto done;
+		}
+		/* OWE Diffie-Hellman Parameter element */
+		*p++ = WLAN_EID_EXTENSION; /* Element ID */
+		*p++ = 1 + 2 + wpabuf_len(pub); /* Length */
+		*p++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension */
+		WPA_PUT_LE16(p, sta->owe_group);
+		p += 2;
+		os_memcpy(p, wpabuf_head(pub), wpabuf_len(pub));
+		p += wpabuf_len(pub);
+		wpabuf_free(pub);
+	}
+#endif /* CONFIG_OWE */
+
+#ifdef CONFIG_DPP2
+	if (DPP_VERSION > 1 && (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) &&
+	    sta && sta->dpp_pfs && status_code == WLAN_STATUS_SUCCESS &&
+	    wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_DPP) {
+		os_memcpy(p, wpabuf_head(sta->dpp_pfs->ie),
+			  wpabuf_len(sta->dpp_pfs->ie));
+		p += wpabuf_len(sta->dpp_pfs->ie);
+	}
+#endif /* CONFIG_DPP2 */
+
+#ifdef CONFIG_IEEE80211AC
+	if (sta && hapd->conf->vendor_vht && (sta->flags & WLAN_STA_VENDOR_VHT))
+		p = hostapd_eid_vendor_vht(hapd, p);
+#endif /* CONFIG_IEEE80211AC */
+
+	if (sta && (sta->flags & WLAN_STA_WMM))
+		p = hostapd_eid_wmm(hapd, p);
+
+#ifdef CONFIG_WPS
+	if (sta &&
+	    ((sta->flags & WLAN_STA_WPS) ||
+	     ((sta->flags & WLAN_STA_MAYBE_WPS) && hapd->conf->wpa))) {
+		struct wpabuf *wps = wps_build_assoc_resp_ie();
+		if (wps) {
+			os_memcpy(p, wpabuf_head(wps), wpabuf_len(wps));
+			p += wpabuf_len(wps);
+			wpabuf_free(wps);
+		}
+	}
+#endif /* CONFIG_WPS */
+
+	if (sta && (sta->flags & WLAN_STA_MULTI_AP))
+		p = hostapd_eid_multi_ap(hapd, p);
+
+#ifdef CONFIG_P2P
+	if (sta && sta->p2p_ie && hapd->p2p_group) {
+		struct wpabuf *p2p_resp_ie;
+		enum p2p_status_code status;
+		switch (status_code) {
+		case WLAN_STATUS_SUCCESS:
+			status = P2P_SC_SUCCESS;
+			break;
+		case WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA:
+			status = P2P_SC_FAIL_LIMIT_REACHED;
+			break;
+		default:
+			status = P2P_SC_FAIL_INVALID_PARAMS;
+			break;
+		}
+		p2p_resp_ie = p2p_group_assoc_resp_ie(hapd->p2p_group, status);
+		if (p2p_resp_ie) {
+			os_memcpy(p, wpabuf_head(p2p_resp_ie),
+				  wpabuf_len(p2p_resp_ie));
+			p += wpabuf_len(p2p_resp_ie);
+			wpabuf_free(p2p_resp_ie);
+		}
+	}
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_P2P_MANAGER
+	if (hapd->conf->p2p & P2P_MANAGE)
+		p = hostapd_eid_p2p_manage(hapd, p);
+#endif /* CONFIG_P2P_MANAGER */
+
+	p = hostapd_eid_mbo(hapd, p, buf + buflen - p);
+
+	if (hapd->conf->assocresp_elements &&
+	    (size_t) (buf + buflen - p) >=
+	    wpabuf_len(hapd->conf->assocresp_elements)) {
+		os_memcpy(p, wpabuf_head(hapd->conf->assocresp_elements),
+			  wpabuf_len(hapd->conf->assocresp_elements));
+		p += wpabuf_len(hapd->conf->assocresp_elements);
+	}
+
+	send_len += p - reply->u.assoc_resp.variable;
+
+#ifdef CONFIG_FILS
+	if (sta &&
+	    (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+	     sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+	     sta->auth_alg == WLAN_AUTH_FILS_PK) &&
+	    status_code == WLAN_STATUS_SUCCESS) {
+		struct ieee802_11_elems elems;
+
+		if (ieee802_11_parse_elems(ies, ies_len, &elems, 0) ==
+		    ParseFailed || !elems.fils_session) {
+			res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto done;
+		}
+
+		/* FILS Session */
+		*p++ = WLAN_EID_EXTENSION; /* Element ID */
+		*p++ = 1 + FILS_SESSION_LEN; /* Length */
+		*p++ = WLAN_EID_EXT_FILS_SESSION; /* Element ID Extension */
+		os_memcpy(p, elems.fils_session, FILS_SESSION_LEN);
+		send_len += 2 + 1 + FILS_SESSION_LEN;
+
+		send_len = fils_encrypt_assoc(sta->wpa_sm, buf, send_len,
+					      buflen, sta->fils_hlp_resp);
+		if (send_len < 0) {
+			res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto done;
+		}
+	}
+#endif /* CONFIG_FILS */
+
+	if (hostapd_drv_send_mlme(hapd, reply, send_len, 0, NULL, 0, 0) < 0) {
+		wpa_printf(MSG_INFO, "Failed to send assoc resp: %s",
+			   strerror(errno));
+		res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+done:
+	os_free(buf);
+	return res;
+}
+
+
+#ifdef CONFIG_OWE
+u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta,
+			   const u8 *owe_dh, u8 owe_dh_len,
+			   u8 *owe_buf, size_t owe_buf_len, u16 *status)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+	if (hapd->conf->own_ie_override) {
+		wpa_printf(MSG_DEBUG, "OWE: Using IE override");
+		*status = WLAN_STATUS_SUCCESS;
+		return wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf,
+						     owe_buf_len, NULL, 0);
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	if (wpa_auth_sta_get_pmksa(sta->wpa_sm)) {
+		wpa_printf(MSG_DEBUG, "OWE: Using PMKSA caching");
+		owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf,
+							owe_buf_len, NULL, 0);
+		*status = WLAN_STATUS_SUCCESS;
+		return owe_buf;
+	}
+
+	if (sta->owe_pmk && sta->external_dh_updated) {
+		wpa_printf(MSG_DEBUG, "OWE: Using previously derived PMK");
+		*status = WLAN_STATUS_SUCCESS;
+		return owe_buf;
+	}
+
+	*status = owe_process_assoc_req(hapd, sta, owe_dh, owe_dh_len);
+	if (*status != WLAN_STATUS_SUCCESS)
+		return NULL;
+
+	owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf,
+						owe_buf_len, NULL, 0);
+
+	if (sta->owe_ecdh && owe_buf) {
+		struct wpabuf *pub;
+
+		pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
+		if (!pub) {
+			*status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			return owe_buf;
+		}
+
+		/* OWE Diffie-Hellman Parameter element */
+		*owe_buf++ = WLAN_EID_EXTENSION; /* Element ID */
+		*owe_buf++ = 1 + 2 + wpabuf_len(pub); /* Length */
+		*owe_buf++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension
+							 */
+		WPA_PUT_LE16(owe_buf, sta->owe_group);
+		owe_buf += 2;
+		os_memcpy(owe_buf, wpabuf_head(pub), wpabuf_len(pub));
+		owe_buf += wpabuf_len(pub);
+		wpabuf_free(pub);
+	}
+
+	return owe_buf;
+}
+#endif /* CONFIG_OWE */
+
+
+#ifdef CONFIG_FILS
+
+void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	u16 reply_res;
+
+	wpa_printf(MSG_DEBUG, "FILS: Finish association with " MACSTR,
+		   MAC2STR(sta->addr));
+	eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
+	if (!sta->fils_pending_assoc_req)
+		return;
+	reply_res = send_assoc_resp(hapd, sta, sta->addr, WLAN_STATUS_SUCCESS,
+				    sta->fils_pending_assoc_is_reassoc,
+				    sta->fils_pending_assoc_req,
+				    sta->fils_pending_assoc_req_len, 0, 0);
+	os_free(sta->fils_pending_assoc_req);
+	sta->fils_pending_assoc_req = NULL;
+	sta->fils_pending_assoc_req_len = 0;
+	wpabuf_free(sta->fils_hlp_resp);
+	sta->fils_hlp_resp = NULL;
+	wpabuf_free(sta->hlp_dhcp_discover);
+	sta->hlp_dhcp_discover = NULL;
+
+	/*
+	 * Remove the station in case transmission of a success response fails.
+	 * At this point the station was already added associated to the driver.
+	 */
+	if (reply_res != WLAN_STATUS_SUCCESS)
+		hostapd_drv_sta_remove(hapd, sta->addr);
+}
+
+
+void fils_hlp_timeout(void *eloop_ctx, void *eloop_data)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct sta_info *sta = eloop_data;
+
+	wpa_printf(MSG_DEBUG,
+		   "FILS: HLP response timeout - continue with association response for "
+		   MACSTR, MAC2STR(sta->addr));
+	if (sta->fils_drv_assoc_finish)
+		hostapd_notify_assoc_fils_finish(hapd, sta);
+	else
+		fils_hlp_finish_assoc(hapd, sta);
+}
+
+#endif /* CONFIG_FILS */
+
+
+static void handle_assoc(struct hostapd_data *hapd,
+			 const struct ieee80211_mgmt *mgmt, size_t len,
+			 int reassoc, int rssi)
+{
+	u16 capab_info, listen_interval, seq_ctrl, fc;
+	int resp = WLAN_STATUS_SUCCESS;
+	u16 reply_res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+	const u8 *pos;
+	int left, i, ubus_resp;
+	struct sta_info *sta;
+	u8 *tmp = NULL;
+#ifdef CONFIG_FILS
+	int delay_assoc = 0;
+#endif /* CONFIG_FILS */
+	int omit_rsnxe = 0;
+
+	if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) :
+				      sizeof(mgmt->u.assoc_req))) {
+		wpa_printf(MSG_INFO, "handle_assoc(reassoc=%d) - too short payload (len=%lu)",
+			   reassoc, (unsigned long) len);
+		return;
+	}
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (reassoc) {
+		if (hapd->iconf->ignore_reassoc_probability > 0.0 &&
+		    drand48() < hapd->iconf->ignore_reassoc_probability) {
+			wpa_printf(MSG_INFO,
+				   "TESTING: ignoring reassoc request from "
+				   MACSTR, MAC2STR(mgmt->sa));
+			return;
+		}
+	} else {
+		if (hapd->iconf->ignore_assoc_probability > 0.0 &&
+		    drand48() < hapd->iconf->ignore_assoc_probability) {
+			wpa_printf(MSG_INFO,
+				   "TESTING: ignoring assoc request from "
+				   MACSTR, MAC2STR(mgmt->sa));
+			return;
+		}
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	fc = le_to_host16(mgmt->frame_control);
+	seq_ctrl = le_to_host16(mgmt->seq_ctrl);
+
+	if (reassoc) {
+		capab_info = le_to_host16(mgmt->u.reassoc_req.capab_info);
+		listen_interval = le_to_host16(
+			mgmt->u.reassoc_req.listen_interval);
+		wpa_printf(MSG_DEBUG, "reassociation request: STA=" MACSTR
+			   " capab_info=0x%02x listen_interval=%d current_ap="
+			   MACSTR " seq_ctrl=0x%x%s",
+			   MAC2STR(mgmt->sa), capab_info, listen_interval,
+			   MAC2STR(mgmt->u.reassoc_req.current_ap),
+			   seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "");
+		left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req));
+		pos = mgmt->u.reassoc_req.variable;
+	} else {
+		capab_info = le_to_host16(mgmt->u.assoc_req.capab_info);
+		listen_interval = le_to_host16(
+			mgmt->u.assoc_req.listen_interval);
+		wpa_printf(MSG_DEBUG, "association request: STA=" MACSTR
+			   " capab_info=0x%02x listen_interval=%d "
+			   "seq_ctrl=0x%x%s",
+			   MAC2STR(mgmt->sa), capab_info, listen_interval,
+			   seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "");
+		left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req));
+		pos = mgmt->u.assoc_req.variable;
+	}
+
+	sta = ap_get_sta(hapd, mgmt->sa);
+#ifdef CONFIG_IEEE80211R_AP
+	if (sta && sta->auth_alg == WLAN_AUTH_FT &&
+	    (sta->flags & WLAN_STA_AUTH) == 0) {
+		wpa_printf(MSG_DEBUG, "FT: Allow STA " MACSTR " to associate "
+			   "prior to authentication since it is using "
+			   "over-the-DS FT", MAC2STR(mgmt->sa));
+
+		/*
+		 * Mark station as authenticated, to avoid adding station
+		 * entry in the driver as associated and not authenticated
+		 */
+		sta->flags |= WLAN_STA_AUTH;
+	} else
+#endif /* CONFIG_IEEE80211R_AP */
+	if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) {
+		if (hapd->iface->current_mode &&
+		    hapd->iface->current_mode->mode ==
+			HOSTAPD_MODE_IEEE80211AD) {
+			int acl_res;
+			struct radius_sta info;
+
+			acl_res = ieee802_11_allowed_address(hapd, mgmt->sa,
+							     (const u8 *) mgmt,
+							     len, &info);
+			if (acl_res == HOSTAPD_ACL_REJECT) {
+				wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+					"Ignore Association Request frame from "
+					MACSTR " due to ACL reject",
+					MAC2STR(mgmt->sa));
+				resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+				goto fail;
+			}
+			if (acl_res == HOSTAPD_ACL_PENDING)
+				return;
+
+			/* DMG/IEEE 802.11ad does not use authentication.
+			 * Allocate sta entry upon association. */
+			sta = ap_sta_add(hapd, mgmt->sa);
+			if (!sta) {
+				hostapd_logger(hapd, mgmt->sa,
+					       HOSTAPD_MODULE_IEEE80211,
+					       HOSTAPD_LEVEL_INFO,
+					       "Failed to add STA");
+				resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+				goto fail;
+			}
+
+			acl_res = ieee802_11_set_radius_info(
+				hapd, sta, acl_res, &info);
+			if (acl_res) {
+				resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+				goto fail;
+			}
+
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "Skip authentication for DMG/IEEE 802.11ad");
+			sta->flags |= WLAN_STA_AUTH;
+			wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
+			sta->auth_alg = WLAN_AUTH_OPEN;
+		} else {
+			hostapd_logger(hapd, mgmt->sa,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_INFO,
+				       "Station tried to associate before authentication (aid=%d flags=0x%x)",
+				       sta ? sta->aid : -1,
+				       sta ? sta->flags : 0);
+			send_deauth(hapd, mgmt->sa,
+				    WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA);
+			return;
+		}
+	}
+
+	if ((fc & WLAN_FC_RETRY) &&
+	    sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ &&
+	    sta->last_seq_ctrl == seq_ctrl &&
+	    sta->last_subtype == (reassoc ? WLAN_FC_STYPE_REASSOC_REQ :
+				  WLAN_FC_STYPE_ASSOC_REQ)) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "Drop repeated association frame seq_ctrl=0x%x",
+			       seq_ctrl);
+		return;
+	}
+	sta->last_seq_ctrl = seq_ctrl;
+	sta->last_subtype = reassoc ? WLAN_FC_STYPE_REASSOC_REQ :
+		WLAN_FC_STYPE_ASSOC_REQ;
+
+	if (hapd->tkip_countermeasures) {
+		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto fail;
+	}
+
+	if (listen_interval > hapd->conf->max_listen_interval) {
+		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "Too large Listen Interval (%d)",
+			       listen_interval);
+		resp = WLAN_STATUS_ASSOC_DENIED_LISTEN_INT_TOO_LARGE;
+		goto fail;
+	}
+
+#ifdef CONFIG_MBO
+	if (hapd->conf->mbo_enabled && hapd->mbo_assoc_disallow) {
+		resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+		goto fail;
+	}
+
+	if (hapd->iconf->rssi_reject_assoc_rssi && rssi &&
+	    rssi < hapd->iconf->rssi_reject_assoc_rssi &&
+	    (sta->auth_rssi == 0 ||
+	     sta->auth_rssi < hapd->iconf->rssi_reject_assoc_rssi)) {
+		resp = WLAN_STATUS_DENIED_POOR_CHANNEL_CONDITIONS;
+		goto fail;
+	}
+#endif /* CONFIG_MBO */
+
+	/*
+	 * sta->capability is used in check_assoc_ies() for RRM enabled
+	 * capability element.
+	 */
+	sta->capability = capab_info;
+
+#ifdef CONFIG_FILS
+	if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+	    sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+	    sta->auth_alg == WLAN_AUTH_FILS_PK) {
+		int res;
+
+		/* The end of the payload is encrypted. Need to decrypt it
+		 * before parsing. */
+
+		tmp = os_memdup(pos, left);
+		if (!tmp) {
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+		}
+
+		res = fils_decrypt_assoc(sta->wpa_sm, sta->fils_session, mgmt,
+					 len, tmp, left);
+		if (res < 0) {
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+		}
+		pos = tmp;
+		left = res;
+	}
+#endif /* CONFIG_FILS */
+	struct hostapd_ubus_request req = {
+		.type = HOSTAPD_UBUS_ASSOC_REQ,
+		.mgmt_frame = mgmt,
+		.ssi_signal = rssi,
+	};
+
+	/* followed by SSID and Supported rates; and HT capabilities if 802.11n
+	 * is used */
+	resp = check_assoc_ies(hapd, sta, pos, left, reassoc);
+	if (resp != WLAN_STATUS_SUCCESS)
+		goto fail;
+	omit_rsnxe = !get_ie(pos, left, WLAN_EID_RSNX);
+
+	if (hostapd_get_aid(hapd, sta) < 0) {
+		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO, "No room for more AIDs");
+		resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+		goto fail;
+	}
+
+	sta->listen_interval = listen_interval;
+
+	if (hapd->iface->current_mode &&
+	    hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
+		sta->flags |= WLAN_STA_NONERP;
+	for (i = 0; i < sta->supported_rates_len; i++) {
+		if ((sta->supported_rates[i] & 0x7f) > 22) {
+			sta->flags &= ~WLAN_STA_NONERP;
+			break;
+		}
+	}
+	if (sta->flags & WLAN_STA_NONERP && !sta->nonerp_set) {
+		sta->nonerp_set = 1;
+		hapd->iface->num_sta_non_erp++;
+		if (hapd->iface->num_sta_non_erp == 1)
+			ieee802_11_set_beacons(hapd->iface);
+	}
+
+	if (!(sta->capability & WLAN_CAPABILITY_SHORT_SLOT_TIME) &&
+	    !sta->no_short_slot_time_set) {
+		sta->no_short_slot_time_set = 1;
+		hapd->iface->num_sta_no_short_slot_time++;
+		if (hapd->iface->current_mode &&
+		    hapd->iface->current_mode->mode ==
+		    HOSTAPD_MODE_IEEE80211G &&
+		    hapd->iface->num_sta_no_short_slot_time == 1)
+			ieee802_11_set_beacons(hapd->iface);
+	}
+
+	if (sta->capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
+		sta->flags |= WLAN_STA_SHORT_PREAMBLE;
+	else
+		sta->flags &= ~WLAN_STA_SHORT_PREAMBLE;
+
+	if (!(sta->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) &&
+	    !sta->no_short_preamble_set) {
+		sta->no_short_preamble_set = 1;
+		hapd->iface->num_sta_no_short_preamble++;
+		if (hapd->iface->current_mode &&
+		    hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
+		    && hapd->iface->num_sta_no_short_preamble == 1)
+			ieee802_11_set_beacons(hapd->iface);
+	}
+
+	update_ht_state(hapd, sta);
+
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "association OK (aid %d)", sta->aid);
+	/* Station will be marked associated, after it acknowledges AssocResp
+	 */
+	sta->flags |= WLAN_STA_ASSOC_REQ_OK;
+
+	if ((sta->flags & WLAN_STA_MFP) && sta->sa_query_timed_out) {
+		wpa_printf(MSG_DEBUG, "Allowing %sassociation after timed out "
+			   "SA Query procedure", reassoc ? "re" : "");
+		/* TODO: Send a protected Disassociate frame to the STA using
+		 * the old key and Reason Code "Previous Authentication no
+		 * longer valid". Make sure this is only sent protected since
+		 * unprotected frame would be received by the STA that is now
+		 * trying to associate.
+		 */
+	}
+
+	/* Make sure that the previously registered inactivity timer will not
+	 * remove the STA immediately. */
+	sta->timeout_next = STA_NULLFUNC;
+
+#ifdef CONFIG_TAXONOMY
+	taxonomy_sta_info_assoc_req(hapd, sta, pos, left);
+#endif /* CONFIG_TAXONOMY */
+
+	sta->pending_wds_enable = 0;
+
+#ifdef CONFIG_FILS
+	if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+	    sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+	    sta->auth_alg == WLAN_AUTH_FILS_PK) {
+		if (fils_process_hlp(hapd, sta, pos, left) > 0)
+			delay_assoc = 1;
+	}
+#endif /* CONFIG_FILS */
+
+	ubus_resp = hostapd_ubus_handle_event(hapd, &req);
+	if (ubus_resp) {
+		wpa_printf(MSG_DEBUG, "Station " MACSTR " assoc rejected by ubus handler.\n",
+		       MAC2STR(mgmt->sa));
+		resp = ubus_resp > 0 ? (u16) ubus_resp : WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto fail;
+	}
+ fail:
+
+	/*
+	 * In case of a successful response, add the station to the driver.
+	 * Otherwise, the kernel may ignore Data frames before we process the
+	 * ACK frame (TX status). In case of a failure, this station will be
+	 * removed.
+	 *
+	 * Note that this is not compliant with the IEEE 802.11 standard that
+	 * states that a non-AP station should transition into the
+	 * authenticated/associated state only after the station acknowledges
+	 * the (Re)Association Response frame. However, still do this as:
+	 *
+	 * 1. In case the station does not acknowledge the (Re)Association
+	 *    Response frame, it will be removed.
+	 * 2. Data frames will be dropped in the kernel until the station is
+	 *    set into authorized state, and there are no significant known
+	 *    issues with processing other non-Data Class 3 frames during this
+	 *    window.
+	 */
+	if (resp == WLAN_STATUS_SUCCESS)
+		hostapd_process_assoc_ml_info(hapd, sta, pos, left, reassoc);
+
+	if (resp == WLAN_STATUS_SUCCESS && sta &&
+	    add_associated_sta(hapd, sta, reassoc))
+		resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+
+#ifdef CONFIG_FILS
+	if (sta && delay_assoc && resp == WLAN_STATUS_SUCCESS &&
+	    eloop_is_timeout_registered(fils_hlp_timeout, hapd, sta) &&
+	    sta->fils_pending_assoc_req) {
+		/* Do not reschedule fils_hlp_timeout in case the station
+		 * retransmits (Re)Association Request frame while waiting for
+		 * the previously started FILS HLP wait, so that the timeout can
+		 * be determined from the first pending attempt. */
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Continue waiting for HLP processing before sending (Re)Association Response frame to "
+			   MACSTR, MAC2STR(sta->addr));
+		os_free(tmp);
+		return;
+	}
+	if (sta) {
+		eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
+		os_free(sta->fils_pending_assoc_req);
+		sta->fils_pending_assoc_req = NULL;
+		sta->fils_pending_assoc_req_len = 0;
+		wpabuf_free(sta->fils_hlp_resp);
+		sta->fils_hlp_resp = NULL;
+	}
+	if (sta && delay_assoc && resp == WLAN_STATUS_SUCCESS) {
+		sta->fils_pending_assoc_req = tmp;
+		sta->fils_pending_assoc_req_len = left;
+		sta->fils_pending_assoc_is_reassoc = reassoc;
+		sta->fils_drv_assoc_finish = 0;
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Waiting for HLP processing before sending (Re)Association Response frame to "
+			   MACSTR, MAC2STR(sta->addr));
+		eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
+		eloop_register_timeout(0, hapd->conf->fils_hlp_wait_time * 1024,
+				       fils_hlp_timeout, hapd, sta);
+		return;
+	}
+#endif /* CONFIG_FILS */
+
+	if (resp >= 0)
+		reply_res = send_assoc_resp(hapd, sta, mgmt->sa, resp, reassoc,
+					    pos, left, rssi, omit_rsnxe);
+	os_free(tmp);
+
+	/*
+	 * Remove the station in case transmission of a success response fails
+	 * (the STA was added associated to the driver) or if the station was
+	 * previously added unassociated.
+	 */
+	if (sta && ((reply_res != WLAN_STATUS_SUCCESS &&
+		     resp == WLAN_STATUS_SUCCESS) || sta->added_unassoc)) {
+		hostapd_drv_sta_remove(hapd, sta->addr);
+		sta->added_unassoc = 0;
+	}
+}
+
+
+static void hostapd_deauth_sta(struct hostapd_data *hapd,
+			       struct sta_info *sta,
+			       const struct ieee80211_mgmt *mgmt)
+{
+	wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+		"deauthentication: STA=" MACSTR " reason_code=%d",
+		MAC2STR(mgmt->sa), le_to_host16(mgmt->u.deauth.reason_code));
+
+	ap_sta_set_authorized(hapd, sta, 0);
+	sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
+	sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC |
+			WLAN_STA_ASSOC_REQ_OK);
+	hostapd_set_sta_flags(hapd, sta);
+	wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH);
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_DEBUG, "deauthenticated");
+	mlme_deauthenticate_indication(
+		hapd, sta, le_to_host16(mgmt->u.deauth.reason_code));
+	sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
+	ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
+	ap_free_sta(hapd, sta);
+}
+
+
+static void hostapd_disassoc_sta(struct hostapd_data *hapd,
+				 struct sta_info *sta,
+				 const struct ieee80211_mgmt *mgmt)
+{
+	wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+		"disassocation: STA=" MACSTR " reason_code=%d",
+		MAC2STR(mgmt->sa), le_to_host16(mgmt->u.disassoc.reason_code));
+
+	ap_sta_set_authorized(hapd, sta, 0);
+	sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
+	sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
+	hostapd_set_sta_flags(hapd, sta);
+	wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC);
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_INFO, "disassociated");
+	sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
+	ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
+	/* Stop Accounting and IEEE 802.1X sessions, but leave the STA
+	 * authenticated. */
+	accounting_sta_stop(hapd, sta);
+	ieee802_1x_free_station(hapd, sta);
+	if (sta->ipaddr)
+		hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr);
+	ap_sta_ip6addr_del(hapd, sta);
+	hostapd_drv_sta_remove(hapd, sta->addr);
+	sta->added_unassoc = 0;
+
+	if (sta->timeout_next == STA_NULLFUNC ||
+	    sta->timeout_next == STA_DISASSOC) {
+		sta->timeout_next = STA_DEAUTH;
+		eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+		eloop_register_timeout(AP_DEAUTH_DELAY, 0, ap_handle_timer,
+				       hapd, sta);
+	}
+
+	mlme_disassociate_indication(
+		hapd, sta, le_to_host16(mgmt->u.disassoc.reason_code));
+
+	/* DMG/IEEE 802.11ad does not use deauthication. Deallocate sta upon
+	 * disassociation. */
+	if (hapd->iface->current_mode &&
+	    hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
+		sta->flags &= ~WLAN_STA_AUTH;
+		wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH);
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG, "deauthenticated");
+		ap_free_sta(hapd, sta);
+	}
+}
+
+
+#ifdef CONFIG_IEEE80211BE
+static struct sta_info *
+hostapd_ml_get_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
+			 struct hostapd_data **assoc_hapd)
+{
+	struct hostapd_data *other_hapd = NULL;
+	struct sta_info *tmp_sta;
+
+	*assoc_hapd = hapd;
+
+	/* The station is the one on which the association was performed */
+	if (sta->mld_assoc_link_id == hapd->mld_link_id)
+		return sta;
+
+	other_hapd = hostapd_mld_get_link_bss(hapd, sta->mld_assoc_link_id);
+	if (!other_hapd) {
+		wpa_printf(MSG_DEBUG, "MLD: No link match for link_id=%u",
+			   sta->mld_assoc_link_id);
+		return sta;
+	}
+
+	/*
+	 * Iterate over the stations and find the one with the matching link ID
+	 * and association ID.
+	 */
+	for (tmp_sta = other_hapd->sta_list; tmp_sta; tmp_sta = tmp_sta->next) {
+		if (tmp_sta->mld_assoc_link_id == sta->mld_assoc_link_id &&
+		    tmp_sta->aid == sta->aid) {
+			*assoc_hapd = other_hapd;
+			return tmp_sta;
+		}
+	}
+
+	return sta;
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
+static bool hostapd_ml_handle_disconnect(struct hostapd_data *hapd,
+					 struct sta_info *sta,
+					 const struct ieee80211_mgmt *mgmt,
+					 bool disassoc)
+{
+#ifdef CONFIG_IEEE80211BE
+	struct hostapd_data *assoc_hapd, *tmp_hapd;
+	struct sta_info *assoc_sta;
+	unsigned int i, link_id;
+
+	if (!hostapd_is_mld_ap(hapd))
+		return false;
+
+	/*
+	 * Get the station on which the association was performed, as it holds
+	 * the information about all the other links.
+	 */
+	assoc_sta = hostapd_ml_get_assoc_sta(hapd, sta, &assoc_hapd);
+
+	for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+		for (i = 0; i < assoc_hapd->iface->interfaces->count; i++) {
+			struct sta_info *tmp_sta;
+
+			if (!assoc_sta->mld_info.links[link_id].valid)
+				continue;
+
+			tmp_hapd =
+				assoc_hapd->iface->interfaces->iface[i]->bss[0];
+
+			if (!tmp_hapd->conf->mld_ap ||
+			    assoc_hapd->conf->mld_id != tmp_hapd->conf->mld_id)
+				continue;
+
+			for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
+			     tmp_sta = tmp_sta->next) {
+				/*
+				 * Remove the station on which the association
+				 * was done only after all other link stations
+				 * are removed. Since there is only a single
+				 * station per struct hostapd_hapd with the
+				 * same association link simply break out from
+				 * the loop.
+				 */
+				if (tmp_sta == assoc_sta)
+					break;
+
+				if (tmp_sta->mld_assoc_link_id !=
+				    assoc_sta->mld_assoc_link_id ||
+				    tmp_sta->aid != assoc_sta->aid)
+					continue;
+
+				if (!disassoc)
+					hostapd_deauth_sta(tmp_hapd, tmp_sta,
+							   mgmt);
+				else
+					hostapd_disassoc_sta(tmp_hapd, tmp_sta,
+							     mgmt);
+				break;
+			}
+		}
+	}
+
+	/* Remove the station on which the association was performed. */
+	if (!disassoc)
+		hostapd_deauth_sta(assoc_hapd, assoc_sta, mgmt);
+	else
+		hostapd_disassoc_sta(assoc_hapd, assoc_sta, mgmt);
+
+	return true;
+#else /* CONFIG_IEEE80211BE */
+	return false;
+#endif /* CONFIG_IEEE80211BE */
+}
+
+
+static void handle_disassoc(struct hostapd_data *hapd,
+			    const struct ieee80211_mgmt *mgmt, size_t len)
+{
+	struct sta_info *sta;
+
+	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.disassoc)) {
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+			   "handle_disassoc - too short payload (len=%lu)",
+			   (unsigned long) len);
+		return;
+	}
+	hostapd_ubus_notify(hapd, "disassoc", mgmt->sa);
+
+	sta = ap_get_sta(hapd, mgmt->sa);
+	if (!sta) {
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR
+			" trying to disassociate, but it is not associated",
+			MAC2STR(mgmt->sa));
+		return;
+	}
+
+	if (hostapd_ml_handle_disconnect(hapd, sta, mgmt, true))
+		return;
+
+	hostapd_disassoc_sta(hapd, sta, mgmt);
+}
+
+
+static void handle_deauth(struct hostapd_data *hapd,
+			  const struct ieee80211_mgmt *mgmt, size_t len)
+{
+	struct sta_info *sta;
+
+	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.deauth)) {
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+			"handle_deauth - too short payload (len=%lu)",
+			(unsigned long) len);
+		return;
+	}
+
+	/* Clear the PTKSA cache entries for PASN */
+	ptksa_cache_flush(hapd->ptksa, mgmt->sa, WPA_CIPHER_NONE);
+
+	hostapd_ubus_notify(hapd, "deauth", mgmt->sa);
+
+	sta = ap_get_sta(hapd, mgmt->sa);
+	if (!sta) {
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR
+			" trying to deauthenticate, but it is not authenticated",
+			MAC2STR(mgmt->sa));
+		return;
+	}
+
+	if (hostapd_ml_handle_disconnect(hapd, sta, mgmt, false))
+		return;
+
+	hostapd_deauth_sta(hapd, sta, mgmt);
+}
+
+
+static void handle_beacon(struct hostapd_data *hapd,
+			  const struct ieee80211_mgmt *mgmt, size_t len,
+			  struct hostapd_frame_info *fi)
+{
+	struct ieee802_11_elems elems;
+
+	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.beacon)) {
+		wpa_printf(MSG_INFO, "handle_beacon - too short payload (len=%lu)",
+			   (unsigned long) len);
+		return;
+	}
+
+	(void) ieee802_11_parse_elems(mgmt->u.beacon.variable,
+				      len - (IEEE80211_HDRLEN +
+					     sizeof(mgmt->u.beacon)), &elems,
+				      0);
+#if defined CONFIG_24G_BW_SWITCH || defined CONFIG_5G_BW_SWITCH
+	ap_list_process_beacon(hapd, mgmt, &elems, fi);
+#else
+	ap_list_process_beacon(hapd->iface, mgmt, &elems, fi);
+#endif /* defined CONFIG_24G_BW_SWITCH || defined CONFIG_5G_BW_SWITCH */
+}
+
+
+static int robust_action_frame(u8 category)
+{
+	return category != WLAN_ACTION_PUBLIC &&
+		category != WLAN_ACTION_HT;
+}
+
+
+static int handle_action(struct hostapd_data *hapd,
+			 const struct ieee80211_mgmt *mgmt, size_t len,
+			 unsigned int freq)
+{
+	struct sta_info *sta;
+	u8 *action __maybe_unused;
+
+	if (len < IEEE80211_HDRLEN + 2 + 1) {
+		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "handle_action - too short payload (len=%lu)",
+			       (unsigned long) len);
+		return 0;
+	}
+
+	action = (u8 *) &mgmt->u.action.u;
+	wpa_printf(MSG_DEBUG, "RX_ACTION category %u action %u sa " MACSTR
+		   " da " MACSTR " len %d freq %u",
+		   mgmt->u.action.category, *action,
+		   MAC2STR(mgmt->sa), MAC2STR(mgmt->da), (int) len, freq);
+
+	sta = ap_get_sta(hapd, mgmt->sa);
+
+	if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
+	    (sta == NULL || !(sta->flags & WLAN_STA_ASSOC))) {
+		wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignored Action "
+			   "frame (category=%u) from unassociated STA " MACSTR,
+			   mgmt->u.action.category, MAC2STR(mgmt->sa));
+		return 0;
+	}
+
+	if (sta && (sta->flags & WLAN_STA_MFP) &&
+	    !(mgmt->frame_control & host_to_le16(WLAN_FC_ISWEP)) &&
+	    robust_action_frame(mgmt->u.action.category)) {
+		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "Dropped unprotected Robust Action frame from "
+			       "an MFP STA");
+		return 0;
+	}
+
+	if (sta) {
+		u16 fc = le_to_host16(mgmt->frame_control);
+		u16 seq_ctrl = le_to_host16(mgmt->seq_ctrl);
+
+		if ((fc & WLAN_FC_RETRY) &&
+		    sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ &&
+		    sta->last_seq_ctrl == seq_ctrl &&
+		    sta->last_subtype == WLAN_FC_STYPE_ACTION) {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "Drop repeated action frame seq_ctrl=0x%x",
+				       seq_ctrl);
+			return 1;
+		}
+
+		sta->last_seq_ctrl = seq_ctrl;
+		sta->last_subtype = WLAN_FC_STYPE_ACTION;
+	}
+
+	switch (mgmt->u.action.category) {
+#ifdef CONFIG_IEEE80211R_AP
+	case WLAN_ACTION_FT:
+		if (!sta ||
+		    wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action,
+				     len - IEEE80211_HDRLEN))
+			break;
+		return 1;
+#endif /* CONFIG_IEEE80211R_AP */
+	case WLAN_ACTION_WMM:
+		hostapd_wmm_action(hapd, mgmt, len);
+		return 1;
+	case WLAN_ACTION_SA_QUERY:
+		ieee802_11_sa_query_action(hapd, mgmt, len);
+		return 1;
+#ifdef CONFIG_WNM_AP
+	case WLAN_ACTION_WNM:
+		ieee802_11_rx_wnm_action_ap(hapd, mgmt, len);
+		return 1;
+#endif /* CONFIG_WNM_AP */
+#ifdef CONFIG_FST
+	case WLAN_ACTION_FST:
+		if (hapd->iface->fst)
+			fst_rx_action(hapd->iface->fst, mgmt, len);
+		else
+			wpa_printf(MSG_DEBUG,
+				   "FST: Ignore FST Action frame - no FST attached");
+		return 1;
+#endif /* CONFIG_FST */
+	case WLAN_ACTION_PUBLIC:
+	case WLAN_ACTION_PROTECTED_DUAL:
+		if (len >= IEEE80211_HDRLEN + 2 &&
+		    mgmt->u.action.u.public_action.action ==
+		    WLAN_PA_20_40_BSS_COEX) {
+			hostapd_2040_coex_action(hapd, mgmt, len);
+			return 1;
+		}
+#ifdef CONFIG_DPP
+		if (len >= IEEE80211_HDRLEN + 6 &&
+		    mgmt->u.action.u.vs_public_action.action ==
+		    WLAN_PA_VENDOR_SPECIFIC &&
+		    WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) ==
+		    OUI_WFA &&
+		    mgmt->u.action.u.vs_public_action.variable[0] ==
+		    DPP_OUI_TYPE) {
+			const u8 *pos, *end;
+
+			pos = mgmt->u.action.u.vs_public_action.oui;
+			end = ((const u8 *) mgmt) + len;
+			hostapd_dpp_rx_action(hapd, mgmt->sa, pos, end - pos,
+					      freq);
+			return 1;
+		}
+		if (len >= IEEE80211_HDRLEN + 2 &&
+		    (mgmt->u.action.u.public_action.action ==
+		     WLAN_PA_GAS_INITIAL_RESP ||
+		     mgmt->u.action.u.public_action.action ==
+		     WLAN_PA_GAS_COMEBACK_RESP)) {
+			const u8 *pos, *end;
+
+			pos = &mgmt->u.action.u.public_action.action;
+			end = ((const u8 *) mgmt) + len;
+			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) {
+			hapd->public_action_cb(hapd->public_action_cb_ctx,
+					       (u8 *) mgmt, len, freq);
+		}
+		if (hapd->public_action_cb2) {
+			hapd->public_action_cb2(hapd->public_action_cb2_ctx,
+						(u8 *) mgmt, len, freq);
+		}
+		if (hapd->public_action_cb || hapd->public_action_cb2)
+			return 1;
+		break;
+	case WLAN_ACTION_VENDOR_SPECIFIC:
+		if (hapd->vendor_action_cb) {
+			if (hapd->vendor_action_cb(hapd->vendor_action_cb_ctx,
+						   (u8 *) mgmt, len, freq) == 0)
+				return 1;
+		}
+		break;
+	case WLAN_ACTION_RADIO_MEASUREMENT:
+		hostapd_handle_radio_measurement(hapd, (const u8 *) mgmt, len);
+		return 1;
+	}
+
+	hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "handle_action - unknown action category %d or invalid "
+		       "frame",
+		       mgmt->u.action.category);
+	if (!is_multicast_ether_addr(mgmt->da) &&
+	    !(mgmt->u.action.category & 0x80) &&
+	    !is_multicast_ether_addr(mgmt->sa)) {
+		struct ieee80211_mgmt *resp;
+
+		/*
+		 * IEEE 802.11-REVma/D9.0 - 7.3.1.11
+		 * Return the Action frame to the source without change
+		 * except that MSB of the Category set to 1.
+		 */
+		wpa_printf(MSG_DEBUG, "IEEE 802.11: Return unknown Action "
+			   "frame back to sender");
+		resp = os_memdup(mgmt, len);
+		if (resp == NULL)
+			return 0;
+		os_memcpy(resp->da, resp->sa, ETH_ALEN);
+		os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
+		os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
+		resp->u.action.category |= 0x80;
+
+		if (hostapd_drv_send_mlme(hapd, resp, len, 0, NULL, 0, 0) < 0) {
+			wpa_printf(MSG_ERROR, "IEEE 802.11: Failed to send "
+				   "Action frame");
+		}
+		os_free(resp);
+	}
+
+	return 1;
+}
+
+
+/**
+ * notify_mgmt_frame - Notify of Management frames on the control interface
+ * @hapd: hostapd BSS data structure (the BSS to which the Management frame was
+ * sent to)
+ * @buf: Management frame data (starting from the IEEE 802.11 header)
+ * @len: Length of frame data in octets
+ *
+ * Notify the control interface of any received Management frame.
+ */
+static void notify_mgmt_frame(struct hostapd_data *hapd, const u8 *buf,
+			      size_t len)
+{
+
+	int hex_len = len * 2 + 1;
+	char *hex = os_malloc(hex_len);
+
+	if (hex) {
+		wpa_snprintf_hex(hex, hex_len, buf, len);
+		wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO,
+			     AP_MGMT_FRAME_RECEIVED "buf=%s", hex);
+		os_free(hex);
+	}
+}
+
+
+/**
+ * ieee802_11_mgmt - process incoming IEEE 802.11 management frames
+ * @hapd: hostapd BSS data structure (the BSS to which the management frame was
+ * sent to)
+ * @buf: management frame data (starting from IEEE 802.11 header)
+ * @len: length of frame data in octets
+ * @fi: meta data about received frame (signal level, etc.)
+ *
+ * Process all incoming IEEE 802.11 management frames. This will be called for
+ * each frame received from the kernel driver through wlan#ap interface. In
+ * addition, it can be called to re-inserted pending frames (e.g., when using
+ * external RADIUS server as an MAC ACL).
+ */
+int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
+		    struct hostapd_frame_info *fi)
+{
+	struct ieee80211_mgmt *mgmt;
+	u16 fc, stype;
+	int ret = 0;
+	unsigned int freq;
+	int ssi_signal = fi ? fi->ssi_signal : 0;
+
+	if (len < 24)
+		return 0;
+
+	if (fi && fi->freq)
+		freq = fi->freq;
+	else
+		freq = hapd->iface->freq;
+
+	mgmt = (struct ieee80211_mgmt *) buf;
+	fc = le_to_host16(mgmt->frame_control);
+	stype = WLAN_FC_GET_STYPE(fc);
+
+	if (is_multicast_ether_addr(mgmt->sa) ||
+	    is_zero_ether_addr(mgmt->sa) ||
+	    os_memcmp(mgmt->sa, hapd->own_addr, ETH_ALEN) == 0) {
+		/* Do not process any frames with unexpected/invalid SA so that
+		 * we do not add any state for unexpected STA addresses or end
+		 * up sending out frames to unexpected destination. */
+		wpa_printf(MSG_DEBUG, "MGMT: Invalid SA=" MACSTR
+			   " in received frame - ignore this frame silently",
+			   MAC2STR(mgmt->sa));
+		return 0;
+	}
+
+	if (stype == WLAN_FC_STYPE_BEACON) {
+		handle_beacon(hapd, mgmt, len, fi);
+		return 1;
+	}
+
+	if (!is_broadcast_ether_addr(mgmt->bssid) &&
+#ifdef CONFIG_P2P
+	    /* Invitation responses can be sent with the peer MAC as BSSID */
+	    !((hapd->conf->p2p & P2P_GROUP_OWNER) &&
+	      stype == WLAN_FC_STYPE_ACTION) &&
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_MESH
+	    !(hapd->conf->mesh & MESH_ENABLED) &&
+#endif /* CONFIG_MESH */
+#ifdef CONFIG_IEEE80211BE
+	    !(hapd->conf->mld_ap &&
+	      os_memcmp(hapd->mld_addr, mgmt->bssid, ETH_ALEN) == 0) &&
+#endif /* CONFIG_IEEE80211BE */
+	    os_memcmp(mgmt->bssid, hapd->own_addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_INFO, "MGMT: BSSID=" MACSTR " not our address",
+			   MAC2STR(mgmt->bssid));
+		return 0;
+	}
+
+	if (hapd->iface->state != HAPD_IFACE_ENABLED) {
+		wpa_printf(MSG_DEBUG, "MGMT: Ignore management frame while interface is not enabled (SA=" MACSTR " DA=" MACSTR " subtype=%u)",
+			   MAC2STR(mgmt->sa), MAC2STR(mgmt->da), stype);
+		return 1;
+	}
+
+	if (stype == WLAN_FC_STYPE_PROBE_REQ) {
+		handle_probe_req(hapd, mgmt, len, ssi_signal);
+		return 1;
+	}
+
+	if ((!is_broadcast_ether_addr(mgmt->da) ||
+	     stype != WLAN_FC_STYPE_ACTION) &&
+#ifdef CONFIG_IEEE80211BE
+	    !(hapd->conf->mld_ap &&
+	      os_memcmp(hapd->mld_addr, mgmt->bssid, ETH_ALEN) == 0) &&
+#endif /* CONFIG_IEEE80211BE */
+	    os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) {
+		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "MGMT: DA=" MACSTR " not our address",
+			       MAC2STR(mgmt->da));
+		return 0;
+	}
+
+	if (hapd->iconf->track_sta_max_num)
+		sta_track_add(hapd->iface, mgmt->sa, ssi_signal);
+
+	if (hapd->conf->notify_mgmt_frames)
+		notify_mgmt_frame(hapd, buf, len);
+
+	switch (stype) {
+	case WLAN_FC_STYPE_AUTH:
+		wpa_printf(MSG_DEBUG, "mgmt::auth");
+		handle_auth(hapd, mgmt, len, ssi_signal, 0);
+		ret = 1;
+		break;
+	case WLAN_FC_STYPE_ASSOC_REQ:
+		wpa_printf(MSG_DEBUG, "mgmt::assoc_req");
+		handle_assoc(hapd, mgmt, len, 0, ssi_signal);
+		ret = 1;
+		break;
+	case WLAN_FC_STYPE_REASSOC_REQ:
+		wpa_printf(MSG_DEBUG, "mgmt::reassoc_req");
+		handle_assoc(hapd, mgmt, len, 1, ssi_signal);
+		ret = 1;
+		break;
+	case WLAN_FC_STYPE_DISASSOC:
+		wpa_printf(MSG_DEBUG, "mgmt::disassoc");
+		handle_disassoc(hapd, mgmt, len);
+		ret = 1;
+		break;
+	case WLAN_FC_STYPE_DEAUTH:
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "mgmt::deauth");
+		handle_deauth(hapd, mgmt, len);
+		ret = 1;
+		break;
+	case WLAN_FC_STYPE_ACTION:
+		wpa_printf(MSG_DEBUG, "mgmt::action");
+		ret = handle_action(hapd, mgmt, len, freq);
+		break;
+	default:
+		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "unknown mgmt frame subtype %d", stype);
+		break;
+	}
+
+	return ret;
+}
+
+
+static void handle_auth_cb(struct hostapd_data *hapd,
+			   const struct ieee80211_mgmt *mgmt,
+			   size_t len, int ok)
+{
+	u16 auth_alg, auth_transaction, status_code;
+	struct sta_info *sta;
+	bool success_status;
+
+	sta = ap_get_sta(hapd, mgmt->da);
+	if (!sta) {
+		wpa_printf(MSG_DEBUG, "handle_auth_cb: STA " MACSTR
+			   " not found",
+			   MAC2STR(mgmt->da));
+		return;
+	}
+
+	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
+		wpa_printf(MSG_INFO, "handle_auth_cb - too short payload (len=%lu)",
+			   (unsigned long) len);
+		auth_alg = 0;
+		auth_transaction = 0;
+		status_code = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto fail;
+	}
+
+	auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
+	auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
+	status_code = le_to_host16(mgmt->u.auth.status_code);
+
+	if (!ok) {
+		hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_NOTICE,
+			       "did not acknowledge authentication response");
+		goto fail;
+	}
+
+	if (status_code == WLAN_STATUS_SUCCESS &&
+	    ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 2) ||
+	     (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 4))) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO, "authenticated");
+		sta->flags |= WLAN_STA_AUTH;
+		if (sta->added_unassoc)
+			hostapd_set_sta_flags(hapd, sta);
+		return;
+	}
+
+fail:
+	success_status = status_code == WLAN_STATUS_SUCCESS;
+#ifdef CONFIG_SAE
+	if (auth_alg == WLAN_AUTH_SAE && auth_transaction == 1)
+		success_status = sae_status_success(hapd, status_code);
+#endif /* CONFIG_SAE */
+	if (!success_status && sta->added_unassoc) {
+		hostapd_drv_sta_remove(hapd, sta->addr);
+		sta->added_unassoc = 0;
+	}
+}
+
+
+static void hostapd_set_wds_encryption(struct hostapd_data *hapd,
+				       struct sta_info *sta,
+				       char *ifname_wds)
+{
+#ifdef CONFIG_WEP
+	int i;
+	struct hostapd_ssid *ssid = &hapd->conf->ssid;
+
+	if (hapd->conf->ieee802_1x || hapd->conf->wpa)
+		return;
+
+	for (i = 0; i < 4; i++) {
+		if (ssid->wep.key[i] &&
+		    hostapd_drv_set_key(ifname_wds, hapd, WPA_ALG_WEP, NULL, i,
+					0, i == ssid->wep.idx, NULL, 0,
+					ssid->wep.key[i], ssid->wep.len[i],
+					i == ssid->wep.idx ?
+					KEY_FLAG_GROUP_RX_TX_DEFAULT :
+					KEY_FLAG_GROUP_RX_TX)) {
+			wpa_printf(MSG_WARNING,
+				   "Could not set WEP keys for WDS interface; %s",
+				   ifname_wds);
+			break;
+		}
+	}
+#endif /* CONFIG_WEP */
+}
+
+
+#ifdef CONFIG_IEEE80211BE
+static void ieee80211_ml_link_sta_assoc_cb(struct hostapd_data *hapd,
+					   struct sta_info *sta,
+					   struct mld_link_info *link,
+					   bool ok)
+{
+	if (!ok) {
+		hostapd_logger(hapd, link->peer_addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "did not acknowledge association response");
+		sta->flags &= ~WLAN_STA_ASSOC_REQ_OK;
+
+		/* The STA is added only in case of SUCCESS */
+		if (link->status == WLAN_STATUS_SUCCESS)
+			hostapd_drv_sta_remove(hapd, sta->addr);
+
+		return;
+	}
+
+	if (link->status != WLAN_STATUS_SUCCESS)
+		return;
+
+	sta->flags |= WLAN_STA_ASSOC;
+	sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
+
+	if (!hapd->conf->ieee802_1x && !hapd->conf->wpa)
+		ap_sta_set_authorized(hapd, sta, 1);
+
+	hostapd_set_sta_flags(hapd, sta);
+
+	/*
+	 * TODOs:
+	 * - IEEE 802.1X port enablement is not needed as done on the station
+	 *     doing the connection.
+	 * - Not handling accounting
+	 * - Need to handle VLAN configuration
+	 */
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
+static void hostapd_ml_handle_assoc_cb(struct hostapd_data *hapd,
+				       struct sta_info *sta, bool ok)
+{
+#ifdef CONFIG_IEEE80211BE
+	unsigned int i, link_id;
+
+	if (!hostapd_is_mld_ap(hapd))
+		return;
+
+	for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+		struct mld_link_info *link = &sta->mld_info.links[link_id];
+
+		if (!link->valid)
+			continue;
+
+		for (i = 0; i < hapd->iface->interfaces->count; i++) {
+			struct sta_info *tmp_sta;
+			struct hostapd_data *tmp_hapd =
+				hapd->iface->interfaces->iface[i]->bss[0];
+
+			if (tmp_hapd->conf->mld_ap ||
+			    hapd->conf->mld_id != tmp_hapd->conf->mld_id)
+				continue;
+
+			for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
+			     tmp_sta = tmp_sta->next) {
+				if (tmp_sta == sta ||
+				    tmp_sta->mld_assoc_link_id !=
+				    sta->mld_assoc_link_id ||
+				    tmp_sta->aid != sta->aid)
+					continue;
+
+				ieee80211_ml_link_sta_assoc_cb(tmp_hapd,
+							       tmp_sta, link,
+							       ok);
+				break;
+			}
+		}
+	}
+#endif /* CONFIG_IEEE80211BE */
+}
+
+
+static void handle_assoc_cb(struct hostapd_data *hapd,
+			    const struct ieee80211_mgmt *mgmt,
+			    size_t len, int reassoc, int ok)
+{
+	u16 status;
+	struct sta_info *sta;
+	int new_assoc = 1;
+
+	sta = ap_get_sta(hapd, mgmt->da);
+	if (!sta) {
+		wpa_printf(MSG_INFO, "handle_assoc_cb: STA " MACSTR " not found",
+			   MAC2STR(mgmt->da));
+		return;
+	}
+
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->conf->mld_ap && sta->mld_info.mld_sta &&
+	    hapd->mld_link_id != sta->mld_assoc_link_id) {
+		/* See ieee80211_ml_link_sta_assoc_cb() for the MLD case */
+		wpa_printf(MSG_DEBUG,
+			   "%s: MLD: ignore on link station (%d != %d)",
+			   __func__, hapd->mld_link_id, sta->mld_assoc_link_id);
+		return;
+	}
+#endif /* CONFIG_IEEE80211BE */
+
+	if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_resp) :
+				      sizeof(mgmt->u.assoc_resp))) {
+		wpa_printf(MSG_INFO,
+			   "handle_assoc_cb(reassoc=%d) - too short payload (len=%lu)",
+			   reassoc, (unsigned long) len);
+		hostapd_drv_sta_remove(hapd, sta->addr);
+		return;
+	}
+
+	if (reassoc)
+		status = le_to_host16(mgmt->u.reassoc_resp.status_code);
+	else
+		status = le_to_host16(mgmt->u.assoc_resp.status_code);
+
+	if (!ok) {
+		hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "did not acknowledge association response");
+		sta->flags &= ~WLAN_STA_ASSOC_REQ_OK;
+		/* The STA is added only in case of SUCCESS */
+		if (status == WLAN_STATUS_SUCCESS)
+			hostapd_drv_sta_remove(hapd, sta->addr);
+
+		goto handle_ml;
+	}
+
+	if (status != WLAN_STATUS_SUCCESS)
+		goto handle_ml;
+
+	/* Stop previous accounting session, if one is started, and allocate
+	 * new session id for the new session. */
+	accounting_sta_stop(hapd, sta);
+
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_INFO,
+		       "associated (aid %d)",
+		       sta->aid);
+
+	if (sta->flags & WLAN_STA_ASSOC)
+		new_assoc = 0;
+	sta->flags |= WLAN_STA_ASSOC;
+	sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
+	if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa &&
+	     !hapd->conf->osen) ||
+	    sta->auth_alg == WLAN_AUTH_FILS_SK ||
+	    sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+	    sta->auth_alg == WLAN_AUTH_FILS_PK ||
+	    sta->auth_alg == WLAN_AUTH_FT) {
+		/*
+		 * Open, static WEP, FT protocol, or FILS; no separate
+		 * authorization step.
+		 */
+		ap_sta_set_authorized(hapd, sta, 1);
+	}
+
+	if (reassoc)
+		mlme_reassociate_indication(hapd, sta);
+	else
+		mlme_associate_indication(hapd, sta);
+
+	sta->sa_query_timed_out = 0;
+
+	if (sta->eapol_sm == NULL) {
+		/*
+		 * This STA does not use RADIUS server for EAP authentication,
+		 * so bind it to the selected VLAN interface now, since the
+		 * interface selection is not going to change anymore.
+		 */
+		if (ap_sta_bind_vlan(hapd, sta) < 0)
+			goto handle_ml;
+	} else if (sta->vlan_id) {
+		/* VLAN ID already set (e.g., by PMKSA caching), so bind STA */
+		if (ap_sta_bind_vlan(hapd, sta) < 0)
+			goto handle_ml;
+	}
+
+	hostapd_set_sta_flags(hapd, sta);
+
+	if (!(sta->flags & WLAN_STA_WDS) && sta->pending_wds_enable) {
+		wpa_printf(MSG_DEBUG, "Enable 4-address WDS mode for STA "
+			   MACSTR " based on pending request",
+			   MAC2STR(sta->addr));
+		sta->pending_wds_enable = 0;
+		sta->flags |= WLAN_STA_WDS;
+	}
+
+	/* WPS not supported on backhaul BSS. Disable 4addr mode on fronthaul */
+	if ((sta->flags & WLAN_STA_WDS) ||
+	    (sta->flags & WLAN_STA_MULTI_AP &&
+	     !(hapd->conf->multi_ap & FRONTHAUL_BSS) &&
+	     !(sta->flags & WLAN_STA_WPS))) {
+		int ret;
+		char ifname_wds[IFNAMSIZ + 1];
+
+		wpa_printf(MSG_DEBUG, "Reenable 4-address WDS mode for STA "
+			   MACSTR " (aid %u)",
+			   MAC2STR(sta->addr), sta->aid);
+		ret = hostapd_set_wds_sta(hapd, ifname_wds, sta->addr,
+					  sta->aid, 1);
+		if (!ret)
+			hostapd_set_wds_encryption(hapd, sta, ifname_wds);
+	}
+
+	if (sta->auth_alg == WLAN_AUTH_FT)
+		wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT);
+	else
+		wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC);
+	hapd->new_assoc_sta_cb(hapd, sta, !new_assoc);
+	ieee802_1x_notify_port_enabled(sta->eapol_sm, 1);
+
+#ifdef CONFIG_FILS
+	if ((sta->auth_alg == WLAN_AUTH_FILS_SK ||
+	     sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+	     sta->auth_alg == WLAN_AUTH_FILS_PK) &&
+	    fils_set_tk(sta->wpa_sm) < 0) {
+		wpa_printf(MSG_DEBUG, "FILS: TK configuration failed");
+		ap_sta_disconnect(hapd, sta, sta->addr,
+				  WLAN_REASON_UNSPECIFIED);
+		return;
+	}
+#endif /* CONFIG_FILS */
+
+	if (sta->pending_eapol_rx) {
+		struct os_reltime now, age;
+
+		os_get_reltime(&now);
+		os_reltime_sub(&now, &sta->pending_eapol_rx->rx_time, &age);
+		if (age.sec == 0 && age.usec < 200000) {
+			wpa_printf(MSG_DEBUG,
+				   "Process pending EAPOL frame that was received from " MACSTR " just before association notification",
+				   MAC2STR(sta->addr));
+			ieee802_1x_receive(
+				hapd, mgmt->da,
+				wpabuf_head(sta->pending_eapol_rx->buf),
+				wpabuf_len(sta->pending_eapol_rx->buf),
+				sta->pending_eapol_rx->encrypted);
+		}
+		wpabuf_free(sta->pending_eapol_rx->buf);
+		os_free(sta->pending_eapol_rx);
+		sta->pending_eapol_rx = NULL;
+	}
+
+handle_ml:
+	hostapd_ml_handle_assoc_cb(hapd, sta, ok);
+}
+
+
+static void handle_deauth_cb(struct hostapd_data *hapd,
+			     const struct ieee80211_mgmt *mgmt,
+			     size_t len, int ok)
+{
+	struct sta_info *sta;
+	if (is_multicast_ether_addr(mgmt->da))
+		return;
+	sta = ap_get_sta(hapd, mgmt->da);
+	if (!sta) {
+		wpa_printf(MSG_DEBUG, "handle_deauth_cb: STA " MACSTR
+			   " not found", MAC2STR(mgmt->da));
+		return;
+	}
+	if (ok)
+		wpa_printf(MSG_DEBUG, "STA " MACSTR " acknowledged deauth",
+			   MAC2STR(sta->addr));
+	else
+		wpa_printf(MSG_DEBUG, "STA " MACSTR " did not acknowledge "
+			   "deauth", MAC2STR(sta->addr));
+
+	ap_sta_deauth_cb(hapd, sta);
+}
+
+
+static void handle_disassoc_cb(struct hostapd_data *hapd,
+			       const struct ieee80211_mgmt *mgmt,
+			       size_t len, int ok)
+{
+	struct sta_info *sta;
+	if (is_multicast_ether_addr(mgmt->da))
+		return;
+	sta = ap_get_sta(hapd, mgmt->da);
+	if (!sta) {
+		wpa_printf(MSG_DEBUG, "handle_disassoc_cb: STA " MACSTR
+			   " not found", MAC2STR(mgmt->da));
+		return;
+	}
+	if (ok)
+		wpa_printf(MSG_DEBUG, "STA " MACSTR " acknowledged disassoc",
+			   MAC2STR(sta->addr));
+	else
+		wpa_printf(MSG_DEBUG, "STA " MACSTR " did not acknowledge "
+			   "disassoc", MAC2STR(sta->addr));
+
+	ap_sta_disassoc_cb(hapd, sta);
+}
+
+
+static void handle_action_cb(struct hostapd_data *hapd,
+			     const struct ieee80211_mgmt *mgmt,
+			     size_t len, int ok)
+{
+	struct sta_info *sta;
+	const struct rrm_measurement_report_element *report;
+
+#ifdef CONFIG_DPP
+	if (len >= IEEE80211_HDRLEN + 6 &&
+	    mgmt->u.action.category == WLAN_ACTION_PUBLIC &&
+	    mgmt->u.action.u.vs_public_action.action ==
+	    WLAN_PA_VENDOR_SPECIFIC &&
+	    WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) ==
+	    OUI_WFA &&
+	    mgmt->u.action.u.vs_public_action.variable[0] ==
+	    DPP_OUI_TYPE) {
+		const u8 *pos, *end;
+
+		pos = &mgmt->u.action.u.vs_public_action.variable[1];
+		end = ((const u8 *) mgmt) + len;
+		hostapd_dpp_tx_status(hapd, mgmt->da, pos, end - pos, ok);
+		return;
+	}
+	if (len >= IEEE80211_HDRLEN + 2 &&
+	    mgmt->u.action.category == WLAN_ACTION_PUBLIC &&
+	    (mgmt->u.action.u.public_action.action ==
+	     WLAN_PA_GAS_INITIAL_REQ ||
+	     mgmt->u.action.u.public_action.action ==
+	     WLAN_PA_GAS_COMEBACK_REQ)) {
+		const u8 *pos, *end;
+
+		pos = mgmt->u.action.u.public_action.variable;
+		end = ((const u8 *) mgmt) + len;
+		gas_query_ap_tx_status(hapd->gas, mgmt->da, pos, end - pos, ok);
+		return;
+	}
+#endif /* CONFIG_DPP */
+	if (is_multicast_ether_addr(mgmt->da))
+		return;
+	sta = ap_get_sta(hapd, mgmt->da);
+	if (!sta) {
+		wpa_printf(MSG_DEBUG, "handle_action_cb: STA " MACSTR
+			   " not found", MAC2STR(mgmt->da));
+		return;
+	}
+
+#ifdef CONFIG_HS20
+	if (ok && len >= IEEE80211_HDRLEN + 2 &&
+	    mgmt->u.action.category == WLAN_ACTION_WNM &&
+	    mgmt->u.action.u.vs_public_action.action == WNM_NOTIFICATION_REQ &&
+	    sta->hs20_deauth_on_ack) {
+		wpa_printf(MSG_DEBUG, "HS 2.0: Deauthenticate STA " MACSTR
+			   " on acknowledging the WNM-Notification",
+			   MAC2STR(sta->addr));
+		ap_sta_session_timeout(hapd, sta, 0);
+		return;
+	}
+#endif /* CONFIG_HS20 */
+
+	if (len < 24 + 5 + sizeof(*report))
+		return;
+	report = (const struct rrm_measurement_report_element *)
+		&mgmt->u.action.u.rrm.variable[2];
+	if (mgmt->u.action.category == WLAN_ACTION_RADIO_MEASUREMENT &&
+	    mgmt->u.action.u.rrm.action == WLAN_RRM_RADIO_MEASUREMENT_REQUEST &&
+	    report->eid == WLAN_EID_MEASURE_REQUEST &&
+	    report->len >= 3 &&
+	    report->type == MEASURE_TYPE_BEACON)
+		hostapd_rrm_beacon_req_tx_status(hapd, mgmt, len, ok);
+}
+
+
+/**
+ * ieee802_11_mgmt_cb - Process management frame TX status callback
+ * @hapd: hostapd BSS data structure (the BSS from which the management frame
+ * was sent from)
+ * @buf: management frame data (starting from IEEE 802.11 header)
+ * @len: length of frame data in octets
+ * @stype: management frame subtype from frame control field
+ * @ok: Whether the frame was ACK'ed
+ */
+void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len,
+			u16 stype, int ok)
+{
+	const struct ieee80211_mgmt *mgmt;
+	mgmt = (const struct ieee80211_mgmt *) buf;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (hapd->ext_mgmt_frame_handling) {
+		size_t hex_len = 2 * len + 1;
+		char *hex = os_malloc(hex_len);
+
+		if (hex) {
+			wpa_snprintf_hex(hex, hex_len, buf, len);
+			wpa_msg(hapd->msg_ctx, MSG_INFO,
+				"MGMT-TX-STATUS stype=%u ok=%d buf=%s",
+				stype, ok, hex);
+			os_free(hex);
+		}
+		return;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	switch (stype) {
+	case WLAN_FC_STYPE_AUTH:
+		wpa_printf(MSG_DEBUG, "mgmt::auth cb");
+		handle_auth_cb(hapd, mgmt, len, ok);
+		break;
+	case WLAN_FC_STYPE_ASSOC_RESP:
+		wpa_printf(MSG_DEBUG, "mgmt::assoc_resp cb");
+		handle_assoc_cb(hapd, mgmt, len, 0, ok);
+		break;
+	case WLAN_FC_STYPE_REASSOC_RESP:
+		wpa_printf(MSG_DEBUG, "mgmt::reassoc_resp cb");
+		handle_assoc_cb(hapd, mgmt, len, 1, ok);
+		break;
+	case WLAN_FC_STYPE_PROBE_RESP:
+		wpa_printf(MSG_EXCESSIVE, "mgmt::proberesp cb ok=%d", ok);
+		break;
+	case WLAN_FC_STYPE_DEAUTH:
+		wpa_printf(MSG_DEBUG, "mgmt::deauth cb");
+		handle_deauth_cb(hapd, mgmt, len, ok);
+		break;
+	case WLAN_FC_STYPE_DISASSOC:
+		wpa_printf(MSG_DEBUG, "mgmt::disassoc cb");
+		handle_disassoc_cb(hapd, mgmt, len, ok);
+		break;
+	case WLAN_FC_STYPE_ACTION:
+		wpa_printf(MSG_DEBUG, "mgmt::action cb ok=%d", ok);
+		handle_action_cb(hapd, mgmt, len, ok);
+		break;
+	default:
+		wpa_printf(MSG_INFO, "unknown mgmt cb frame subtype %d", stype);
+		break;
+	}
+}
+
+
+int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen)
+{
+	/* TODO */
+	return 0;
+}
+
+
+int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+			   char *buf, size_t buflen)
+{
+	/* TODO */
+	return 0;
+}
+
+
+void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
+		       const u8 *buf, size_t len, int ack)
+{
+	struct sta_info *sta;
+	struct hostapd_iface *iface = hapd->iface;
+
+	sta = ap_get_sta(hapd, addr);
+	if (sta == NULL && iface->num_bss > 1) {
+		size_t j;
+		for (j = 0; j < iface->num_bss; j++) {
+			hapd = iface->bss[j];
+			sta = ap_get_sta(hapd, addr);
+			if (sta)
+				break;
+		}
+	}
+	if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC))
+		return;
+	if (sta->flags & WLAN_STA_PENDING_POLL) {
+		wpa_printf(MSG_DEBUG, "STA " MACSTR " %s pending "
+			   "activity poll", MAC2STR(sta->addr),
+			   ack ? "ACKed" : "did not ACK");
+		if (ack)
+			sta->flags &= ~WLAN_STA_PENDING_POLL;
+	}
+
+	ieee802_1x_tx_status(hapd, sta, buf, len, ack);
+}
+
+
+void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
+			     const u8 *data, size_t len, int ack)
+{
+	struct sta_info *sta;
+	struct hostapd_iface *iface = hapd->iface;
+
+	sta = ap_get_sta(hapd, dst);
+	if (sta == NULL && iface->num_bss > 1) {
+		size_t j;
+		for (j = 0; j < iface->num_bss; j++) {
+			hapd = iface->bss[j];
+			sta = ap_get_sta(hapd, dst);
+			if (sta)
+				break;
+		}
+	}
+	if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) {
+		wpa_printf(MSG_DEBUG, "Ignore TX status for Data frame to STA "
+			   MACSTR " that is not currently associated",
+			   MAC2STR(dst));
+		return;
+	}
+
+	ieee802_1x_eapol_tx_status(hapd, sta, data, len, ack);
+}
+
+
+void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr)
+{
+	struct sta_info *sta;
+	struct hostapd_iface *iface = hapd->iface;
+
+	sta = ap_get_sta(hapd, addr);
+	if (sta == NULL && iface->num_bss > 1) {
+		size_t j;
+		for (j = 0; j < iface->num_bss; j++) {
+			hapd = iface->bss[j];
+			sta = ap_get_sta(hapd, addr);
+			if (sta)
+				break;
+		}
+	}
+	if (sta == NULL)
+		return;
+	wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_POLL_OK MACSTR,
+		MAC2STR(sta->addr));
+	if (!(sta->flags & WLAN_STA_PENDING_POLL))
+		return;
+
+	wpa_printf(MSG_DEBUG, "STA " MACSTR " ACKed pending "
+		   "activity poll", MAC2STR(sta->addr));
+	sta->flags &= ~WLAN_STA_PENDING_POLL;
+}
+
+
+void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
+				int wds)
+{
+	struct sta_info *sta;
+
+	sta = ap_get_sta(hapd, src);
+	if (sta &&
+	    ((sta->flags & WLAN_STA_ASSOC) ||
+	     ((sta->flags & WLAN_STA_ASSOC_REQ_OK) && wds))) {
+		if (!hapd->conf->wds_sta)
+			return;
+
+		if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK)) ==
+		    WLAN_STA_ASSOC_REQ_OK) {
+			wpa_printf(MSG_DEBUG,
+				   "Postpone 4-address WDS mode enabling for STA "
+				   MACSTR " since TX status for AssocResp is not yet known",
+				   MAC2STR(sta->addr));
+			sta->pending_wds_enable = 1;
+			return;
+		}
+
+		if (wds && !(sta->flags & WLAN_STA_WDS)) {
+			int ret;
+			char ifname_wds[IFNAMSIZ + 1];
+
+			wpa_printf(MSG_DEBUG, "Enable 4-address WDS mode for "
+				   "STA " MACSTR " (aid %u)",
+				   MAC2STR(sta->addr), sta->aid);
+			sta->flags |= WLAN_STA_WDS;
+			ret = hostapd_set_wds_sta(hapd, ifname_wds,
+						  sta->addr, sta->aid, 1);
+			if (!ret)
+				hostapd_set_wds_encryption(hapd, sta,
+							   ifname_wds);
+		}
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "Data/PS-poll frame from not associated STA "
+		   MACSTR, MAC2STR(src));
+	if (is_multicast_ether_addr(src) || is_zero_ether_addr(src) ||
+	    os_memcmp(src, hapd->own_addr, ETH_ALEN) == 0) {
+		/* Broadcast bit set in SA or unexpected SA?! Ignore the frame
+		 * silently. */
+		return;
+	}
+
+	if (sta && (sta->flags & WLAN_STA_ASSOC_REQ_OK)) {
+		wpa_printf(MSG_DEBUG, "Association Response to the STA has "
+			   "already been sent, but no TX status yet known - "
+			   "ignore Class 3 frame issue with " MACSTR,
+			   MAC2STR(src));
+		return;
+	}
+
+	if (sta && (sta->flags & WLAN_STA_AUTH))
+		hostapd_drv_sta_disassoc(
+			hapd, src,
+			WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA);
+	else
+		hostapd_drv_sta_deauth(
+			hapd, src,
+			WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA);
+}
+
+
+static u8 * hostapd_add_tpe_info(u8 *eid, u8 tx_pwr_count,
+				 enum max_tx_pwr_interpretation tx_pwr_intrpn,
+				 u8 tx_pwr_cat, u8 tx_pwr)
+{
+	int i;
+
+	*eid++ = WLAN_EID_TRANSMIT_POWER_ENVELOPE; /* Element ID */
+	*eid++ = 2 + tx_pwr_count; /* Length */
+
+	/*
+	 * Transmit Power Information field
+	 *	bits 0-2 : Maximum Transmit Power Count
+	 *	bits 3-5 : Maximum Transmit Power Interpretation
+	 *	bits 6-7 : Maximum Transmit Power Category
+	 */
+	*eid++ = tx_pwr_count | (tx_pwr_intrpn << 3) | (tx_pwr_cat << 6);
+
+	/* Maximum Transmit Power field */
+	for (i = 0; i <= tx_pwr_count; i++)
+		*eid++ = tx_pwr;
+
+	return eid;
+}
+
+
+/*
+ * TODO: Extract power limits from channel data after 6G regulatory
+ *	support.
+ */
+#define REG_PSD_MAX_TXPOWER_FOR_DEFAULT_CLIENT      (-1) /* dBm/MHz */
+#define REG_PSD_MAX_TXPOWER_FOR_SUBORDINATE_CLIENT  5    /* dBm/MHz */
+
+u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid)
+{
+	struct hostapd_iface *iface = hapd->iface;
+	struct hostapd_config *iconf = iface->conf;
+	struct hostapd_hw_modes *mode = iface->current_mode;
+	struct hostapd_channel_data *chan;
+	int dfs, i;
+	u8 channel, tx_pwr_count, local_pwr_constraint;
+	int max_tx_power;
+	u8 tx_pwr;
+
+	if (!mode)
+		return eid;
+
+	if (ieee80211_freq_to_chan(iface->freq, &channel) == NUM_HOSTAPD_MODES)
+		return eid;
+
+	for (i = 0; i < mode->num_channels; i++) {
+		if (mode->channels[i].freq == iface->freq)
+			break;
+	}
+	if (i == mode->num_channels)
+		return eid;
+
+#ifdef CONFIG_IEEE80211AX
+	/* IEEE Std 802.11ax-2021, Annex E.2.7 (6 GHz band in the United
+	 * States): An AP that is an Indoor Access Point per regulatory rules
+	 * shall send at least two Transmit Power Envelope elements in Beacon
+	 * and Probe Response frames as follows:
+	 *  - Maximum Transmit Power Category subfield = Default;
+	 *	Unit interpretation = Regulatory client EIRP PSD
+	 *  - Maximum Transmit Power Category subfield = Subordinate Device;
+	 *	Unit interpretation = Regulatory client EIRP PSD
+	 */
+	if (is_6ghz_op_class(iconf->op_class)) {
+		enum max_tx_pwr_interpretation tx_pwr_intrpn;
+
+		/* Same Maximum Transmit Power for all 20 MHz bands */
+		tx_pwr_count = 0;
+		tx_pwr_intrpn = REGULATORY_CLIENT_EIRP_PSD;
+
+		/* Default Transmit Power Envelope for Global Operating Class */
+		tx_pwr = REG_PSD_MAX_TXPOWER_FOR_DEFAULT_CLIENT * 2;
+		eid = hostapd_add_tpe_info(eid, tx_pwr_count, tx_pwr_intrpn,
+					   REG_DEFAULT_CLIENT, tx_pwr);
+
+		/* Indoor Access Point must include an additional TPE for
+		 * subordinate devices */
+		if (iconf->he_6ghz_reg_pwr_type == HE_6GHZ_INDOOR_AP) {
+			/* TODO: Extract PSD limits from channel data */
+			tx_pwr = REG_PSD_MAX_TXPOWER_FOR_SUBORDINATE_CLIENT * 2;
+			eid = hostapd_add_tpe_info(eid, tx_pwr_count,
+						   tx_pwr_intrpn,
+						   REG_SUBORDINATE_CLIENT,
+						   tx_pwr);
+		}
+
+		return eid;
+	}
+#endif /* CONFIG_IEEE80211AX */
+
+	switch (hostapd_get_oper_chwidth(iconf)) {
+	case CONF_OPER_CHWIDTH_USE_HT:
+		if (iconf->secondary_channel == 0) {
+			/* Max Transmit Power count = 0 (20 MHz) */
+			tx_pwr_count = 0;
+		} else {
+			/* Max Transmit Power count = 1 (20, 40 MHz) */
+			tx_pwr_count = 1;
+		}
+		break;
+	case CONF_OPER_CHWIDTH_80MHZ:
+		/* Max Transmit Power count = 2 (20, 40, and 80 MHz) */
+		tx_pwr_count = 2;
+		break;
+	case CONF_OPER_CHWIDTH_80P80MHZ:
+	case CONF_OPER_CHWIDTH_160MHZ:
+		/* Max Transmit Power count = 3 (20, 40, 80, 160/80+80 MHz) */
+		tx_pwr_count = 3;
+		break;
+	default:
+		return eid;
+	}
+
+	/*
+	 * Below local_pwr_constraint logic is referred from
+	 * hostapd_eid_pwr_constraint.
+	 *
+	 * Check if DFS is required by regulatory.
+	 */
+	dfs = hostapd_is_dfs_required(hapd->iface);
+	if (dfs < 0)
+		dfs = 0;
+
+	/*
+	 * In order to meet regulations when TPC is not implemented using
+	 * a transmit power that is below the legal maximum (including any
+	 * mitigation factor) should help. In this case, indicate 3 dB below
+	 * maximum allowed transmit power.
+	 */
+	if (hapd->iconf->local_pwr_constraint == -1)
+		local_pwr_constraint = (dfs == 0) ? 0 : 3;
+	else
+		local_pwr_constraint = hapd->iconf->local_pwr_constraint;
+
+	/*
+	 * A STA that is not an AP shall use a transmit power less than or
+	 * equal to the local maximum transmit power level for the channel.
+	 * The local maximum transmit power can be calculated from the formula:
+	 * local max TX pwr = max TX pwr - local pwr constraint
+	 * Where max TX pwr is maximum transmit power level specified for
+	 * channel in Country element and local pwr constraint is specified
+	 * for channel in this Power Constraint element.
+	 */
+	chan = &mode->channels[i];
+	max_tx_power = chan->max_tx_power - local_pwr_constraint;
+
+	/*
+	 * Local Maximum Transmit power is encoded as two's complement
+	 * with a 0.5 dB step.
+	 */
+	max_tx_power *= 2; /* in 0.5 dB steps */
+	if (max_tx_power > 127) {
+		/* 63.5 has special meaning of 63.5 dBm or higher */
+		max_tx_power = 127;
+	}
+	if (max_tx_power < -128)
+		max_tx_power = -128;
+	if (max_tx_power < 0)
+		tx_pwr = 0x80 + max_tx_power + 128;
+	else
+		tx_pwr = max_tx_power;
+
+	return hostapd_add_tpe_info(eid, tx_pwr_count, LOCAL_EIRP,
+				    0 /* Reserved for bands other than 6 GHz */,
+				    tx_pwr);
+}
+
+
+u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 bw, chan1, chan2 = 0;
+	int freq1;
+
+	if (!hapd->cs_freq_params.channel ||
+	    (!hapd->cs_freq_params.vht_enabled &&
+	     !hapd->cs_freq_params.he_enabled &&
+	     !hapd->cs_freq_params.eht_enabled))
+		return eid;
+
+	/* bandwidth: 0: 40, 1: 80, 2: 160, 3: 80+80, 4: 320 */
+	switch (hapd->cs_freq_params.bandwidth) {
+	case 40:
+		bw = 0;
+		break;
+	case 80:
+		/* check if it's 80+80 */
+		if (!hapd->cs_freq_params.center_freq2)
+			bw = 1;
+		else
+			bw = 3;
+		break;
+	case 160:
+		bw = 2;
+		break;
+	case 320:
+		bw = 4;
+		break;
+	default:
+		/* not valid VHT bandwidth or not in CSA */
+		return eid;
+	}
+
+	freq1 = hapd->cs_freq_params.center_freq1 ?
+		hapd->cs_freq_params.center_freq1 :
+		hapd->cs_freq_params.freq;
+	if (ieee80211_freq_to_chan(freq1, &chan1) !=
+	    HOSTAPD_MODE_IEEE80211A)
+		return eid;
+
+	if (hapd->cs_freq_params.center_freq2 &&
+	    ieee80211_freq_to_chan(hapd->cs_freq_params.center_freq2,
+				   &chan2) != HOSTAPD_MODE_IEEE80211A)
+		return eid;
+
+	*eid++ = WLAN_EID_CHANNEL_SWITCH_WRAPPER;
+	*eid++ = 5; /* Length of Channel Switch Wrapper */
+	*eid++ = WLAN_EID_WIDE_BW_CHSWITCH;
+	*eid++ = 3; /* Length of Wide Bandwidth Channel Switch element */
+	*eid++ = bw; /* New Channel Width */
+	*eid++ = chan1; /* New Channel Center Frequency Segment 0 */
+	*eid++ = chan2; /* New Channel Center Frequency Segment 1 */
+
+	return eid;
+}
+
+
+static size_t hostapd_eid_nr_db_len(struct hostapd_data *hapd,
+				    size_t *current_len)
+{
+	struct hostapd_neighbor_entry *nr;
+	size_t total_len = 0, len = *current_len;
+
+	dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
+			 list) {
+		if (!nr->nr || wpabuf_len(nr->nr) < 12)
+			continue;
+
+		if (nr->short_ssid == hapd->conf->ssid.short_ssid)
+			continue;
+
+		/* Start a new element */
+		if (!len ||
+		    len + RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN > 255) {
+			len = RNR_HEADER_LEN;
+			total_len += RNR_HEADER_LEN;
+		}
+
+		len += RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN;
+		total_len += RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN;
+	}
+
+	*current_len = len;
+	return total_len;
+}
+
+
+struct mbssid_ie_profiles {
+	u8 start;
+	u8 end;
+};
+
+static size_t
+hostapd_eid_rnr_iface_len(struct hostapd_data *hapd,
+			  struct hostapd_data *reporting_hapd,
+			  size_t *current_len,
+			  struct mbssid_ie_profiles *skip_profiles)
+{
+	size_t total_len = 0, len = *current_len;
+	int tbtt_count = 0;
+	size_t i, start = 0;
+	bool ap_mld = false;
+
+#ifdef CONFIG_IEEE80211BE
+	ap_mld = !!hapd->conf->mld_ap;
+#endif /* CONFIG_IEEE80211BE */
+
+	while (start < hapd->iface->num_bss) {
+		if (!len ||
+		    len + RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN > 255) {
+			len = RNR_HEADER_LEN;
+			total_len += RNR_HEADER_LEN;
+		}
+
+		len += RNR_TBTT_HEADER_LEN;
+		total_len += RNR_TBTT_HEADER_LEN;
+
+		for (i = start; i < hapd->iface->num_bss; i++) {
+			struct hostapd_data *bss = hapd->iface->bss[i];
+
+			if (!bss || !bss->conf || !bss->started)
+				continue;
+
+			if (bss == reporting_hapd ||
+			    bss->conf->ignore_broadcast_ssid)
+				continue;
+
+			if (skip_profiles &&
+			    i >= skip_profiles->start && i < skip_profiles->end)
+				continue;
+
+			if (len + RNR_TBTT_INFO_LEN > 255 ||
+			    tbtt_count >= RNR_TBTT_INFO_COUNT_MAX)
+				break;
+
+			if (!ap_mld) {
+				len += RNR_TBTT_INFO_LEN;
+				total_len += RNR_TBTT_INFO_LEN;
+			} else {
+				len += RNR_TBTT_INFO_MLD_LEN;
+				total_len += RNR_TBTT_INFO_MLD_LEN;
+			}
+			tbtt_count++;
+		}
+		start = i;
+	}
+
+	if (!tbtt_count)
+		total_len = 0;
+	else
+		*current_len = len;
+
+	return total_len;
+}
+
+
+enum colocation_mode {
+	NO_COLOCATED_6GHZ,
+	STANDALONE_6GHZ,
+	COLOCATED_6GHZ,
+	COLOCATED_LOWER_BAND,
+};
+
+static enum colocation_mode get_colocation_mode(struct hostapd_data *hapd)
+{
+	u8 i;
+	bool is_6ghz = is_6ghz_op_class(hapd->iconf->op_class);
+
+	if (!hapd->iface || !hapd->iface->interfaces)
+		return NO_COLOCATED_6GHZ;
+
+	if (is_6ghz && hapd->iface->interfaces->count == 1)
+		return STANDALONE_6GHZ;
+
+	for (i = 0; i < hapd->iface->interfaces->count; i++) {
+		struct hostapd_iface *iface;
+		bool is_colocated_6ghz;
+
+		iface = hapd->iface->interfaces->iface[i];
+		if (iface == hapd->iface || !iface || !iface->conf)
+			continue;
+
+		is_colocated_6ghz = is_6ghz_op_class(iface->conf->op_class);
+		if (!is_6ghz && is_colocated_6ghz)
+			return COLOCATED_LOWER_BAND;
+		if (is_6ghz && !is_colocated_6ghz)
+			return COLOCATED_6GHZ;
+	}
+
+	if (is_6ghz)
+		return STANDALONE_6GHZ;
+
+	return NO_COLOCATED_6GHZ;
+}
+
+
+static size_t hostapd_eid_rnr_multi_iface_len(struct hostapd_data *hapd,
+					      size_t *current_len)
+{
+	struct hostapd_iface *iface;
+	size_t len = 0;
+	size_t i;
+
+	if (!hapd->iface || !hapd->iface->interfaces)
+		return 0;
+
+	for (i = 0; i < hapd->iface->interfaces->count; i++) {
+		iface = hapd->iface->interfaces->iface[i];
+		bool ap_mld = false;
+
+#ifdef CONFIG_IEEE80211BE
+		if (hapd->conf->mld_ap && iface->bss[0]->conf->mld_ap &&
+		    hapd->conf->mld_id == iface->bss[0]->conf->mld_id)
+			ap_mld = true;
+#endif /* CONFIG_IEEE80211BE */
+
+		if (iface == hapd->iface ||
+		    !(is_6ghz_op_class(iface->conf->op_class) || ap_mld))
+			continue;
+
+		len += hostapd_eid_rnr_iface_len(iface->bss[0], hapd,
+						 current_len, NULL);
+	}
+
+	return len;
+}
+
+
+size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type)
+{
+	size_t total_len = 0, current_len = 0;
+	enum colocation_mode mode = get_colocation_mode(hapd);
+	bool ap_mld = false;
+
+#ifdef CONFIG_IEEE80211BE
+	ap_mld = !!hapd->conf->mld_ap;
+#endif /* CONFIG_IEEE80211BE */
+
+	switch (type) {
+	case WLAN_FC_STYPE_BEACON:
+		if (hapd->conf->rnr)
+			total_len += hostapd_eid_nr_db_len(hapd, &current_len);
+		/* fallthrough */
+
+	case WLAN_FC_STYPE_PROBE_RESP:
+		if (mode == COLOCATED_LOWER_BAND || ap_mld)
+			total_len +=
+				hostapd_eid_rnr_multi_iface_len(hapd,
+								&current_len);
+
+		if (hapd->conf->rnr && hapd->iface->num_bss > 1 &&
+		    !hapd->iconf->mbssid)
+			total_len += hostapd_eid_rnr_iface_len(hapd, hapd,
+							       &current_len,
+							       NULL);
+		break;
+
+	case WLAN_FC_STYPE_ACTION:
+		if (hapd->iface->num_bss > 1 && mode == STANDALONE_6GHZ)
+			total_len += hostapd_eid_rnr_iface_len(hapd, hapd,
+							       &current_len,
+							       NULL);
+		break;
+
+	default:
+		break;
+	}
+
+	return total_len;
+}
+
+
+static u8 * hostapd_eid_nr_db(struct hostapd_data *hapd, u8 *eid,
+			      size_t *current_len)
+{
+	struct hostapd_neighbor_entry *nr;
+	size_t len = *current_len;
+	u8 *size_offset = (eid - len) + 1;
+
+	dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
+			 list) {
+		if (!nr->nr || wpabuf_len(nr->nr) < 12)
+			continue;
+
+		if (nr->short_ssid == hapd->conf->ssid.short_ssid)
+			continue;
+
+		/* Start a new element */
+		if (!len ||
+		    len + RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN > 255) {
+			*eid++ = WLAN_EID_REDUCED_NEIGHBOR_REPORT;
+			size_offset = eid++;
+			len = RNR_HEADER_LEN;
+		}
+
+		/* TBTT Information Header subfield (2 octets) */
+		*eid++ = 0;
+		/* TBTT Information Length */
+		*eid++ = RNR_TBTT_INFO_LEN;
+		/* Operating Class */
+		*eid++ = wpabuf_head_u8(nr->nr)[10];
+		/* Channel Number */
+		*eid++ = wpabuf_head_u8(nr->nr)[11];
+		len += RNR_TBTT_HEADER_LEN;
+		/* TBTT Information Set */
+		/* TBTT Information field */
+		/* Neighbor AP TBTT Offset */
+		*eid++ = RNR_NEIGHBOR_AP_OFFSET_UNKNOWN;
+		/* BSSID */
+		os_memcpy(eid, nr->bssid, ETH_ALEN);
+		eid += ETH_ALEN;
+		/* Short SSID */
+		os_memcpy(eid, &nr->short_ssid, 4);
+		eid += 4;
+		/* BSS parameters */
+		*eid++ = nr->bss_parameters;
+		/* 20 MHz PSD */
+		*eid++ = RNR_20_MHZ_PSD_MAX_TXPOWER - 1;
+		len += RNR_TBTT_INFO_LEN;
+		*size_offset = (eid - size_offset) - 1;
+	}
+
+	*current_len = len;
+	return eid;
+}
+
+
+static u8 * hostapd_eid_rnr_iface(struct hostapd_data *hapd,
+				  struct hostapd_data *reporting_hapd,
+				  u8 *eid, size_t *current_len,
+				  struct mbssid_ie_profiles *skip_profiles)
+{
+	struct hostapd_data *bss;
+	struct hostapd_iface *iface = hapd->iface;
+	size_t i, start = 0;
+	size_t len = *current_len;
+	u8 *tbtt_count_pos, *eid_start = eid, *size_offset = (eid - len) + 1;
+	u8 tbtt_count = 0, op_class, channel, bss_param;
+	bool ap_mld = false;
+
+#ifdef CONFIG_IEEE80211BE
+	ap_mld = !!hapd->conf->mld_ap;
+#endif /* CONFIG_IEEE80211BE */
+
+	if (!(iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA) || !iface->freq)
+		return eid;
+
+	if (ieee80211_freq_to_channel_ext(iface->freq,
+					  hapd->iconf->secondary_channel,
+					  hostapd_get_oper_chwidth(hapd->iconf),
+					  &op_class, &channel) ==
+	    NUM_HOSTAPD_MODES)
+		return eid;
+
+	while (start < iface->num_bss) {
+		if (!len ||
+		    len + RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN > 255) {
+			eid_start = eid;
+			*eid++ = WLAN_EID_REDUCED_NEIGHBOR_REPORT;
+			size_offset = eid++;
+			len = RNR_HEADER_LEN;
+			tbtt_count = 0;
+		}
+
+		tbtt_count_pos = eid++;
+		*eid++ = ap_mld ? RNR_TBTT_INFO_MLD_LEN : RNR_TBTT_INFO_LEN;
+		*eid++ = op_class;
+		*eid++ = hapd->iconf->channel;
+		len += RNR_TBTT_HEADER_LEN;
+
+		for (i = start; i < iface->num_bss; i++) {
+			bss_param = 0;
+			bss = iface->bss[i];
+			if (!bss || !bss->conf || !bss->started)
+				continue;
+
+			if (bss == reporting_hapd ||
+			    bss->conf->ignore_broadcast_ssid)
+				continue;
+
+			if (skip_profiles &&
+			    i >= skip_profiles->start && i < skip_profiles->end)
+				continue;
+
+			if (len + RNR_TBTT_INFO_LEN > 255 ||
+			    tbtt_count >= RNR_TBTT_INFO_COUNT_MAX)
+				break;
+
+			*eid++ = RNR_NEIGHBOR_AP_OFFSET_UNKNOWN;
+			os_memcpy(eid, bss->own_addr, ETH_ALEN);
+			eid += ETH_ALEN;
+			os_memcpy(eid, &bss->conf->ssid.short_ssid, 4);
+			eid += 4;
+			if (bss->conf->ssid.short_ssid ==
+			    reporting_hapd->conf->ssid.short_ssid)
+				bss_param |= RNR_BSS_PARAM_SAME_SSID;
+
+			if (iface->conf->mbssid != MBSSID_DISABLED &&
+			    iface->num_bss > 1) {
+				bss_param |= RNR_BSS_PARAM_MULTIPLE_BSSID;
+				if (bss == hostapd_mbssid_get_tx_bss(hapd))
+					bss_param |=
+						RNR_BSS_PARAM_TRANSMITTED_BSSID;
+			}
+
+			if (is_6ghz_op_class(hapd->iconf->op_class) &&
+			    bss->conf->unsol_bcast_probe_resp_interval)
+				bss_param |=
+					RNR_BSS_PARAM_UNSOLIC_PROBE_RESP_ACTIVE;
+
+			bss_param |= RNR_BSS_PARAM_CO_LOCATED;
+
+			*eid++ = bss_param;
+			*eid++ = RNR_20_MHZ_PSD_MAX_TXPOWER - 1;
+
+			if (!ap_mld) {
+				len += RNR_TBTT_INFO_LEN;
+			} else {
+#ifdef CONFIG_IEEE80211BE
+				*eid++ = hapd->conf->mld_id;
+				*eid++ = hapd->mld_link_id | (1 << 4);
+				*eid++ = 0;
+				len += RNR_TBTT_INFO_MLD_LEN;
+#endif /* CONFIG_IEEE80211BE */
+			}
+
+			tbtt_count += 1;
+		}
+
+		start = i;
+		*tbtt_count_pos = RNR_TBTT_INFO_COUNT(tbtt_count - 1);
+		*size_offset = (eid - size_offset) - 1;
+	}
+
+	if (tbtt_count == 0)
+		return eid_start;
+
+	*current_len = len;
+	return eid;
+}
+
+
+static u8 * hostapd_eid_rnr_multi_iface(struct hostapd_data *hapd, u8 *eid,
+					size_t *current_len)
+{
+	struct hostapd_iface *iface;
+	size_t i;
+
+	if (!hapd->iface || !hapd->iface->interfaces)
+		return eid;
+
+	for (i = 0; i < hapd->iface->interfaces->count; i++) {
+		iface = hapd->iface->interfaces->iface[i];
+		bool ap_mld = false;
+
+#ifdef CONFIG_IEEE80211BE
+		if (hapd->conf->mld_ap && iface->bss[0]->conf->mld_ap &&
+		    hapd->conf->mld_id == iface->bss[0]->conf->mld_id)
+			ap_mld = true;
+#endif /* CONFIG_IEEE80211BE */
+
+		if (iface == hapd->iface ||
+		    !(is_6ghz_op_class(iface->conf->op_class) || ap_mld))
+			continue;
+
+		eid = hostapd_eid_rnr_iface(iface->bss[0], hapd, eid,
+					    current_len, NULL);
+	}
+
+	return eid;
+}
+
+
+u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type)
+{
+	u8 *eid_start = eid;
+	size_t current_len = 0;
+	enum colocation_mode mode = get_colocation_mode(hapd);
+	bool ap_mld = false;
+
+#ifdef CONFIG_IEEE80211BE
+	ap_mld = !!hapd->conf->mld_ap;
+#endif /* CONFIG_IEEE80211BE */
+
+	switch (type) {
+	case WLAN_FC_STYPE_BEACON:
+		if (hapd->conf->rnr)
+			eid = hostapd_eid_nr_db(hapd, eid, &current_len);
+		/* fallthrough */
+
+	case WLAN_FC_STYPE_PROBE_RESP:
+		if (mode == COLOCATED_LOWER_BAND || ap_mld)
+			eid = hostapd_eid_rnr_multi_iface(hapd, eid,
+							  &current_len);
+
+		if (hapd->conf->rnr && hapd->iface->num_bss > 1 &&
+		    !hapd->iconf->mbssid)
+			eid = hostapd_eid_rnr_iface(hapd, hapd, eid,
+						    &current_len, NULL);
+		break;
+
+	case WLAN_FC_STYPE_ACTION:
+		if (hapd->iface->num_bss > 1 && mode == STANDALONE_6GHZ)
+			eid = hostapd_eid_rnr_iface(hapd, hapd,	eid,
+						    &current_len, NULL);
+		break;
+
+	default:
+		return eid_start;
+	}
+
+	if (eid == eid_start + 2)
+		return eid_start;
+
+	return eid;
+}
+
+
+static bool mbssid_known_bss(unsigned int i, const u8 *known_bss,
+			     size_t known_bss_len)
+{
+	if (!known_bss || known_bss_len <= i / 8)
+		return false;
+	known_bss = &known_bss[i / 8];
+	return *known_bss & (u8) (BIT(i % 8));
+}
+
+
+static size_t hostapd_eid_mbssid_elem_len(struct hostapd_data *hapd,
+					  u32 frame_type, size_t *bss_index,
+					  const u8 *known_bss,
+					  size_t known_bss_len)
+{
+	struct hostapd_data *tx_bss = hostapd_mbssid_get_tx_bss(hapd);
+	size_t len = 3, i;
+
+	for (i = *bss_index; i < hapd->iface->num_bss; i++) {
+		struct hostapd_data *bss = hapd->iface->bss[i];
+		const u8 *auth, *rsn = NULL, *rsnx = NULL;
+		size_t nontx_profile_len, auth_len;
+		u8 ie_count = 0;
+
+		if (!bss || !bss->conf || !bss->started ||
+		    mbssid_known_bss(i, known_bss, known_bss_len))
+			continue;
+
+		/*
+		 * Sublement ID: 1 octet
+		 * Length: 1 octet
+		 * Nontransmitted capabilities: 4 octets
+		 * SSID element: 2 + variable
+		 * Multiple BSSID Index Element: 3 octets (+2 octets in beacons)
+		 * Fixed length = 1 + 1 + 4 + 2 + 3 = 11
+		 */
+		nontx_profile_len = 11 + bss->conf->ssid.ssid_len;
+
+		if (frame_type == WLAN_FC_STYPE_BEACON)
+			nontx_profile_len += 2;
+
+		auth = wpa_auth_get_wpa_ie(bss->wpa_auth, &auth_len);
+		if (auth) {
+			rsn = get_ie(auth, auth_len, WLAN_EID_RSN);
+			if (rsn)
+				nontx_profile_len += 2 + rsn[1];
+
+			rsnx = get_ie(auth, auth_len, WLAN_EID_RSNX);
+			if (rsnx)
+				nontx_profile_len += 2 + rsnx[1];
+		}
+		if (!rsn && hostapd_wpa_ie(tx_bss, WLAN_EID_RSN))
+			ie_count++;
+		if (!rsnx && hostapd_wpa_ie(tx_bss, WLAN_EID_RSNX))
+			ie_count++;
+		if (bss->conf->xrates_supported)
+			nontx_profile_len += 8;
+		else if (hapd->conf->xrates_supported)
+			ie_count++;
+		if (ie_count)
+			nontx_profile_len += 4 + ie_count;
+
+		if (len + nontx_profile_len > 255)
+			break;
+
+		len += nontx_profile_len;
+	}
+
+	*bss_index = i;
+	return len;
+}
+
+
+size_t hostapd_eid_mbssid_len(struct hostapd_data *hapd, u32 frame_type,
+			      u8 *elem_count, const u8 *known_bss,
+			      size_t known_bss_len, size_t *rnr_len)
+{
+	size_t len = 0, bss_index = 1;
+
+	if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1 ||
+	    (frame_type != WLAN_FC_STYPE_BEACON &&
+	     frame_type != WLAN_FC_STYPE_PROBE_RESP))
+		return 0;
+
+	if (frame_type == WLAN_FC_STYPE_BEACON) {
+		if (!elem_count) {
+			wpa_printf(MSG_INFO,
+				   "MBSSID: Insufficient data for Beacon frames");
+			return 0;
+		}
+		*elem_count = 0;
+	}
+
+	while (bss_index < hapd->iface->num_bss) {
+		size_t rnr_count = bss_index;
+
+		len += hostapd_eid_mbssid_elem_len(hapd, frame_type,
+						   &bss_index, known_bss,
+						   known_bss_len);
+
+		if (frame_type == WLAN_FC_STYPE_BEACON)
+			*elem_count += 1;
+		if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED && rnr_len) {
+			size_t rnr_cur_len = 0;
+			struct mbssid_ie_profiles skip_profiles = {
+				rnr_count, bss_index
+			};
+
+			*rnr_len += hostapd_eid_rnr_iface_len(
+				hapd, hostapd_mbssid_get_tx_bss(hapd),
+				&rnr_cur_len, &skip_profiles);
+		}
+	}
+
+	if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED && rnr_len)
+		*rnr_len += hostapd_eid_rnr_len(hapd, frame_type);
+
+	return len;
+}
+
+
+static u8 * hostapd_eid_mbssid_elem(struct hostapd_data *hapd, u8 *eid, u8 *end,
+				    u32 frame_type, u8 max_bssid_indicator,
+				    size_t *bss_index, u8 elem_count,
+				    const u8 *known_bss, size_t known_bss_len)
+{
+	struct hostapd_data *tx_bss = hostapd_mbssid_get_tx_bss(hapd);
+	size_t i;
+	u8 *eid_len_offset, *max_bssid_indicator_offset;
+
+	*eid++ = WLAN_EID_MULTIPLE_BSSID;
+	eid_len_offset = eid++;
+	max_bssid_indicator_offset = eid++;
+
+	for (i = *bss_index; i < hapd->iface->num_bss; i++) {
+		struct hostapd_data *bss = hapd->iface->bss[i];
+		struct hostapd_bss_config *conf;
+		u8 *eid_len_pos, *nontx_bss_start = eid;
+		const u8 *auth, *rsn = NULL, *rsnx = NULL;
+		u8 ie_count = 0, non_inherit_ie[3];
+		size_t auth_len = 0;
+		u16 capab_info;
+
+		if (!bss || !bss->conf || !bss->started ||
+		    mbssid_known_bss(i, known_bss, known_bss_len))
+			continue;
+		conf = bss->conf;
+
+		*eid++ = WLAN_MBSSID_SUBELEMENT_NONTRANSMITTED_BSSID_PROFILE;
+		eid_len_pos = eid++;
+
+		capab_info = hostapd_own_capab_info(bss);
+		*eid++ = WLAN_EID_NONTRANSMITTED_BSSID_CAPA;
+		*eid++ = sizeof(capab_info);
+		WPA_PUT_LE16(eid, capab_info);
+		eid += sizeof(capab_info);
+
+		*eid++ = WLAN_EID_SSID;
+		*eid++ = conf->ssid.ssid_len;
+		os_memcpy(eid, conf->ssid.ssid, conf->ssid.ssid_len);
+		eid += conf->ssid.ssid_len;
+
+		*eid++ = WLAN_EID_MULTIPLE_BSSID_INDEX;
+		if (frame_type == WLAN_FC_STYPE_BEACON) {
+			*eid++ = 3;
+			*eid++ = i; /* BSSID Index */
+			if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
+			    (conf->dtim_period % elem_count))
+				conf->dtim_period = elem_count;
+			*eid++ = conf->dtim_period;
+			*eid++ = 0xFF; /* DTIM Count */
+		} else {
+			/* Probe Request frame does not include DTIM Period and
+			 * DTIM Count fields. */
+			*eid++ = 1;
+			*eid++ = i; /* BSSID Index */
+		}
+
+		auth = wpa_auth_get_wpa_ie(bss->wpa_auth, &auth_len);
+		if (auth) {
+			rsn = get_ie(auth, auth_len, WLAN_EID_RSN);
+			if (rsn) {
+				os_memcpy(eid, rsn, 2 + rsn[1]);
+				eid += 2 + rsn[1];
+			}
+
+			rsnx = get_ie(auth, auth_len, WLAN_EID_RSNX);
+			if (rsnx) {
+				os_memcpy(eid, rsnx, 2 + rsnx[1]);
+				eid += 2 + rsnx[1];
+			}
+		}
+		if (!rsn && hostapd_wpa_ie(tx_bss, WLAN_EID_RSN))
+			non_inherit_ie[ie_count++] = WLAN_EID_RSN;
+		if (!rsnx && hostapd_wpa_ie(tx_bss, WLAN_EID_RSNX))
+			non_inherit_ie[ie_count++] = WLAN_EID_RSNX;
+		if (hapd->conf->xrates_supported &&
+		    !bss->conf->xrates_supported)
+			non_inherit_ie[ie_count++] = WLAN_EID_EXT_SUPP_RATES;
+		if (ie_count) {
+			*eid++ = WLAN_EID_EXTENSION;
+			*eid++ = 2 + ie_count;
+			*eid++ = WLAN_EID_EXT_NON_INHERITANCE;
+			*eid++ = ie_count;
+			os_memcpy(eid, non_inherit_ie, ie_count);
+			eid += ie_count;
+		}
+
+		*eid_len_pos = (eid - eid_len_pos) - 1;
+
+		if (((eid - eid_len_offset) - 1) > 255) {
+			eid = nontx_bss_start;
+			break;
+		}
+	}
+
+	*bss_index = i;
+	*max_bssid_indicator_offset = max_bssid_indicator;
+	if (*max_bssid_indicator_offset < 1)
+		*max_bssid_indicator_offset = 1;
+	*eid_len_offset = (eid - eid_len_offset) - 1;
+	return eid;
+}
+
+
+u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end,
+			unsigned int frame_stype, u8 elem_count,
+			u8 **elem_offset,
+			const u8 *known_bss, size_t known_bss_len, u8 *rnr_eid,
+			u8 *rnr_count, u8 **rnr_offset, size_t rnr_len)
+{
+	size_t bss_index = 1, cur_len = 0;
+	u8 elem_index = 0, *rnr_start_eid = rnr_eid;
+	bool add_rnr;
+
+	if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1 ||
+	    (frame_stype != WLAN_FC_STYPE_BEACON &&
+	     frame_stype != WLAN_FC_STYPE_PROBE_RESP))
+		return eid;
+
+	if (frame_stype == WLAN_FC_STYPE_BEACON && !elem_offset) {
+		wpa_printf(MSG_INFO,
+			   "MBSSID: Insufficient data for Beacon frames");
+		return eid;
+	}
+
+	add_rnr = hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
+		frame_stype == WLAN_FC_STYPE_BEACON &&
+		rnr_eid && rnr_count && rnr_offset && rnr_len;
+
+	while (bss_index < hapd->iface->num_bss) {
+		unsigned int rnr_start_count = bss_index;
+
+		if (frame_stype == WLAN_FC_STYPE_BEACON) {
+			if (elem_index == elem_count) {
+				wpa_printf(MSG_WARNING,
+					   "MBSSID: Larger number of elements than there is room in the provided array");
+				break;
+			}
+
+			elem_offset[elem_index] = eid;
+			elem_index = elem_index + 1;
+		}
+		eid = hostapd_eid_mbssid_elem(hapd, eid, end, frame_stype,
+					      hostapd_max_bssid_indicator(hapd),
+					      &bss_index, elem_count,
+					      known_bss, known_bss_len);
+
+		if (add_rnr) {
+			struct mbssid_ie_profiles skip_profiles = {
+				rnr_start_count, bss_index
+			};
+
+			rnr_offset[*rnr_count] = rnr_eid;
+			*rnr_count = *rnr_count + 1;
+			cur_len = 0;
+			rnr_eid = hostapd_eid_rnr_iface(
+				hapd, hostapd_mbssid_get_tx_bss(hapd),
+				rnr_eid, &cur_len, &skip_profiles);
+		}
+	}
+
+	if (add_rnr && (size_t) (rnr_eid - rnr_start_eid) < rnr_len) {
+		rnr_offset[*rnr_count] = rnr_eid;
+		*rnr_count = *rnr_count + 1;
+		cur_len = 0;
+
+		if (hapd->conf->rnr)
+			rnr_eid = hostapd_eid_nr_db(hapd, rnr_eid, &cur_len);
+		if (get_colocation_mode(hapd) == COLOCATED_LOWER_BAND)
+			rnr_eid = hostapd_eid_rnr_multi_iface(hapd, rnr_eid,
+							      &cur_len);
+	}
+
+	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/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11.h
new file mode 100644
index 0000000..71e0074
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11.h
@@ -0,0 +1,241 @@
+/*
+ * hostapd / IEEE 802.11 Management
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IEEE802_11_H
+#define IEEE802_11_H
+
+struct hostapd_iface;
+struct hostapd_data;
+struct sta_info;
+struct hostapd_frame_info;
+struct ieee80211_ht_capabilities;
+struct ieee80211_vht_capabilities;
+struct ieee80211_mgmt;
+struct radius_sta;
+enum ieee80211_op_mode;
+enum oper_chan_width;
+struct ieee802_11_elems;
+
+int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
+		    struct hostapd_frame_info *fi);
+void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len,
+			u16 stype, int ok);
+void hostapd_2040_coex_action(struct hostapd_data *hapd,
+			      const struct ieee80211_mgmt *mgmt, size_t len);
+#ifdef NEED_AP_MLME
+int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen);
+int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+			   char *buf, size_t buflen);
+#else /* NEED_AP_MLME */
+static inline int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf,
+				     size_t buflen)
+{
+	return 0;
+}
+
+static inline int ieee802_11_get_mib_sta(struct hostapd_data *hapd,
+					 struct sta_info *sta,
+					 char *buf, size_t buflen)
+{
+	return 0;
+}
+#endif /* NEED_AP_MLME */
+u16 hostapd_own_capab_info(struct hostapd_data *hapd);
+void ap_ht2040_timeout(void *eloop_data, void *user_data);
+u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid,
+			   bool mbssid_complete);
+u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_rm_enabled_capab(struct hostapd_data *hapd, u8 *eid,
+				  size_t len);
+u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts);
+u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid,
+			  enum ieee80211_op_mode opmode);
+u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_spatial_reuse(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_he_6ghz_band_cap(struct hostapd_data *hapd, u8 *eid);
+
+int hostapd_ht_operation_update(struct hostapd_iface *iface);
+void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
+				  const u8 *addr, const u8 *trans_id);
+void hostapd_get_ht_capab(struct hostapd_data *hapd,
+			  struct ieee80211_ht_capabilities *ht_cap,
+			  struct ieee80211_ht_capabilities *neg_ht_cap);
+void hostapd_get_vht_capab(struct hostapd_data *hapd,
+			   struct ieee80211_vht_capabilities *vht_cap,
+			   struct ieee80211_vht_capabilities *neg_vht_cap);
+void hostapd_get_he_capab(struct hostapd_data *hapd,
+			  const struct ieee80211_he_capabilities *he_cap,
+			  struct ieee80211_he_capabilities *neg_he_cap,
+			  size_t he_capab_len);
+void hostapd_get_eht_capab(struct hostapd_data *hapd,
+			   const struct ieee80211_eht_capabilities *src,
+			   struct ieee80211_eht_capabilities *dest,
+			   size_t len);
+u8 * hostapd_eid_eht_basic_ml(struct hostapd_data *hapd, u8 *eid,
+			      struct sta_info *info, bool include_mld_id);
+struct wpabuf * hostapd_ml_auth_resp(struct hostapd_data *hapd);
+const u8 * hostapd_process_ml_auth(struct hostapd_data *hapd,
+				   const struct ieee80211_mgmt *mgmt,
+				   size_t len);
+u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd,
+				 struct ieee802_11_elems *elems,
+				 struct sta_info *sta);
+int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta);
+u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+		      const u8 *ht_capab);
+u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta,
+			const u8 *ie, size_t len);
+
+void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta);
+void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta);
+void ht40_intolerant_remove(struct hostapd_iface *iface, struct sta_info *sta);
+u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+		       const u8 *vht_capab);
+u16 copy_sta_vht_oper(struct hostapd_data *hapd, struct sta_info *sta,
+		      const u8 *vht_oper);
+u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta,
+		       const u8 *vht_opmode);
+u16 copy_sta_he_capab(struct hostapd_data *hapd, struct sta_info *sta,
+		      enum ieee80211_op_mode opmode, const u8 *he_capab,
+		      size_t he_capab_len);
+u16 copy_sta_he_6ghz_capab(struct hostapd_data *hapd, struct sta_info *sta,
+			   const u8 *he_6ghz_capab);
+int hostapd_get_he_twt_responder(struct hostapd_data *hapd,
+				 enum ieee80211_op_mode mode);
+u8 * hostapd_eid_cca(struct hostapd_data *hapd, u8 *eid);
+void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
+		       const u8 *buf, size_t len, int ack);
+void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
+			     const u8 *data, size_t len, int ack);
+void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
+				int wds);
+u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
+				     struct sta_info *sta, u8 *eid);
+void ieee802_11_sa_query_action(struct hostapd_data *hapd,
+				const struct ieee80211_mgmt *mgmt,
+				size_t len);
+u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_adv_proto(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_roaming_consortium(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_time_adv(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid);
+int hostapd_update_time_adv(struct hostapd_data *hapd);
+void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr);
+u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid);
+
+int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta);
+#ifdef CONFIG_SAE
+void sae_clear_retransmit_timer(struct hostapd_data *hapd,
+				struct sta_info *sta);
+void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta);
+#else /* CONFIG_SAE */
+static inline void sae_clear_retransmit_timer(struct hostapd_data *hapd,
+					      struct sta_info *sta)
+{
+}
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_MBO
+
+u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, size_t len);
+
+u8 hostapd_mbo_ie_len(struct hostapd_data *hapd);
+
+u8 * hostapd_eid_mbo_rssi_assoc_rej(struct hostapd_data *hapd, u8 *eid,
+				    size_t len, int delta);
+
+#else /* CONFIG_MBO */
+
+static inline u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid,
+				   size_t len)
+{
+	return eid;
+}
+
+static inline u8 hostapd_mbo_ie_len(struct hostapd_data *hapd)
+{
+	return 0;
+}
+
+#endif /* CONFIG_MBO */
+
+void ap_copy_sta_supp_op_classes(struct sta_info *sta,
+				 const u8 *supp_op_classes,
+				 size_t supp_op_classes_len);
+
+u8 * hostapd_eid_fils_indic(struct hostapd_data *hapd, u8 *eid, int hessid);
+void ieee802_11_finish_fils_auth(struct hostapd_data *hapd,
+				 struct sta_info *sta, int success,
+				 struct wpabuf *erp_resp,
+				 const u8 *msk, size_t msk_len);
+u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta,
+			   const u8 *owe_dh, u8 owe_dh_len,
+			   u8 *owe_buf, size_t owe_buf_len, u16 *status);
+u16 owe_process_rsn_ie(struct hostapd_data *hapd, struct sta_info *sta,
+		       const u8 *rsn_ie, size_t rsn_ie_len,
+		       const u8 *owe_dh, size_t owe_dh_len);
+u16 owe_validate_request(struct hostapd_data *hapd, const u8 *peer,
+			 const u8 *rsn_ie, size_t rsn_ie_len,
+			 const u8 *owe_dh, size_t owe_dh_len);
+void fils_hlp_timeout(void *eloop_ctx, void *eloop_data);
+void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta);
+void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
+		      const u8 *pos, size_t len, u16 auth_alg,
+		      u16 auth_transaction, u16 status_code,
+		      void (*cb)(struct hostapd_data *hapd,
+				 struct sta_info *sta,
+				 u16 resp, struct wpabuf *data, int pub));
+
+size_t hostapd_eid_owe_trans_len(struct hostapd_data *hapd);
+u8 * hostapd_eid_owe_trans(struct hostapd_data *hapd, u8 *eid, size_t len);
+
+size_t hostapd_eid_dpp_cc_len(struct hostapd_data *hapd);
+u8 * hostapd_eid_dpp_cc(struct hostapd_data *hapd, u8 *eid, size_t len);
+
+int get_tx_parameters(struct sta_info *sta, int ap_max_chanwidth,
+		      int ap_seg1_idx, int *bandwidth, int *seg1_idx);
+
+void auth_sae_process_commit(void *eloop_ctx, void *user_ctx);
+u8 * hostapd_eid_rsnxe(struct hostapd_data *hapd, u8 *eid, size_t len);
+u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta,
+		    const u8 *ext_capab_ie, size_t ext_capab_ie_len);
+size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type);
+u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type);
+int ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta,
+			       int res, struct radius_sta *info);
+size_t hostapd_eid_eht_capab_len(struct hostapd_data *hapd,
+				 enum ieee80211_op_mode opmode);
+u8 * hostapd_eid_eht_capab(struct hostapd_data *hapd, u8 *eid,
+			   enum ieee80211_op_mode opmode);
+u8 * hostapd_eid_eht_operation(struct hostapd_data *hapd, u8 *eid);
+u16 copy_sta_eht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+		       enum ieee80211_op_mode opmode,
+		       const u8 *he_capab, size_t he_capab_len,
+		       const u8 *eht_capab, size_t eht_capab_len);
+size_t hostapd_eid_mbssid_len(struct hostapd_data *hapd, u32 frame_type,
+			      u8 *elem_count, const u8 *known_bss,
+			      size_t known_bss_len, size_t *rnr_len);
+u8 * hostapd_eid_mbssid(struct hostapd_data *hapd, u8 *eid, u8 *end,
+			unsigned int frame_stype, u8 elem_count,
+			u8 **elem_offset,
+			const u8 *known_bss, size_t known_bss_len, u8 *rnr_eid,
+			u8 *rnr_count, u8 **rnr_offset, size_t rnr_len);
+void punct_update_legacy_bw(u16 bitmap, u8 pri_chan,
+			    enum oper_chan_width *width, u8 *seg0, u8 *seg1);
+bool hostapd_is_mld_ap(struct hostapd_data *hapd);
+
+#endif /* IEEE802_11_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11_auth.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11_auth.c
new file mode 100644
index 0000000..4277d82
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11_auth.c
@@ -0,0 +1,745 @@
+/*
+ * hostapd / IEEE 802.11 authentication (ACL)
+ * Copyright (c) 2003-2022, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * Access control list for IEEE 802.11 authentication can uses statically
+ * configured ACL from configuration files or an external RADIUS server.
+ * Results from external RADIUS queries are cached to allow faster
+ * authentication frame processing.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "radius/radius.h"
+#include "radius/radius_client.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "sta_info.h"
+#include "wpa_auth.h"
+#include "ieee802_11.h"
+#include "ieee802_1x.h"
+#include "ieee802_11_auth.h"
+
+#define RADIUS_ACL_TIMEOUT 30
+
+
+struct hostapd_cached_radius_acl {
+	struct os_reltime timestamp;
+	macaddr addr;
+	int accepted; /* HOSTAPD_ACL_* */
+	struct hostapd_cached_radius_acl *next;
+	struct radius_sta info;
+};
+
+
+struct hostapd_acl_query_data {
+	struct os_reltime timestamp;
+	u8 radius_id;
+	macaddr addr;
+	u8 *auth_msg; /* IEEE 802.11 authentication frame from station */
+	size_t auth_msg_len;
+	struct hostapd_acl_query_data *next;
+	bool radius_psk;
+	int akm;
+	u8 *anonce;
+	u8 *eapol;
+	size_t eapol_len;
+};
+
+
+#ifndef CONFIG_NO_RADIUS
+static void hostapd_acl_cache_free_entry(struct hostapd_cached_radius_acl *e)
+{
+	os_free(e->info.identity);
+	os_free(e->info.radius_cui);
+	hostapd_free_psk_list(e->info.psk);
+	os_free(e);
+}
+
+
+static void hostapd_acl_cache_free(struct hostapd_cached_radius_acl *acl_cache)
+{
+	struct hostapd_cached_radius_acl *prev;
+
+	while (acl_cache) {
+		prev = acl_cache;
+		acl_cache = acl_cache->next;
+		hostapd_acl_cache_free_entry(prev);
+	}
+}
+
+
+static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr,
+				 struct radius_sta *out)
+{
+	struct hostapd_cached_radius_acl *entry;
+	struct os_reltime now;
+
+	os_get_reltime(&now);
+
+	for (entry = hapd->acl_cache; entry; entry = entry->next) {
+		if (os_memcmp(entry->addr, addr, ETH_ALEN) != 0)
+			continue;
+
+		if (os_reltime_expired(&now, &entry->timestamp,
+				       RADIUS_ACL_TIMEOUT))
+			return -1; /* entry has expired */
+		*out = entry->info;
+
+		return entry->accepted;
+	}
+
+	return -1;
+}
+#endif /* CONFIG_NO_RADIUS */
+
+
+static void hostapd_acl_query_free(struct hostapd_acl_query_data *query)
+{
+	if (!query)
+		return;
+	os_free(query->auth_msg);
+	os_free(query->anonce);
+	os_free(query->eapol);
+	os_free(query);
+}
+
+
+#ifndef CONFIG_NO_RADIUS
+static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr,
+				    struct hostapd_acl_query_data *query)
+{
+	struct radius_msg *msg;
+	char buf[128];
+
+	query->radius_id = radius_client_get_id(hapd->radius);
+	msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST, query->radius_id);
+	if (!msg)
+		return -1;
+
+	if (radius_msg_make_authenticator(msg) < 0) {
+		wpa_printf(MSG_INFO, "Could not make Request Authenticator");
+		goto fail;
+	}
+
+	os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, MAC2STR(addr));
+	if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, (u8 *) buf,
+				 os_strlen(buf))) {
+		wpa_printf(MSG_DEBUG, "Could not add User-Name");
+		goto fail;
+	}
+
+	if (!radius_msg_add_attr_user_password(
+		    msg, (u8 *) buf, os_strlen(buf),
+		    hapd->conf->radius->auth_server->shared_secret,
+		    hapd->conf->radius->auth_server->shared_secret_len)) {
+		wpa_printf(MSG_DEBUG, "Could not add User-Password");
+		goto fail;
+	}
+
+	if (add_common_radius_attr(hapd, hapd->conf->radius_auth_req_attr,
+				   NULL, msg) < 0)
+		goto fail;
+
+	os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
+		    MAC2STR(addr));
+	if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
+				 (u8 *) buf, os_strlen(buf))) {
+		wpa_printf(MSG_DEBUG, "Could not add Calling-Station-Id");
+		goto fail;
+	}
+
+	os_snprintf(buf, sizeof(buf), "CONNECT 11Mbps 802.11b");
+	if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
+				 (u8 *) buf, os_strlen(buf))) {
+		wpa_printf(MSG_DEBUG, "Could not add Connect-Info");
+		goto fail;
+	}
+
+	if (query->akm &&
+	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_WLAN_AKM_SUITE,
+				       wpa_akm_to_suite(query->akm))) {
+		wpa_printf(MSG_DEBUG, "Could not add WLAN-AKM-Suite");
+		goto fail;
+	}
+
+	if (query->anonce &&
+	    !radius_msg_add_ext_vs(msg, RADIUS_ATTR_EXT_VENDOR_SPECIFIC_5,
+				   RADIUS_VENDOR_ID_FREERADIUS,
+				   RADIUS_VENDOR_ATTR_FREERADIUS_802_1X_ANONCE,
+				   query->anonce, WPA_NONCE_LEN)) {
+		wpa_printf(MSG_DEBUG, "Could not add FreeRADIUS-802.1X-Anonce");
+		goto fail;
+	}
+
+	if (query->eapol &&
+	    !radius_msg_add_ext_vs(msg, RADIUS_ATTR_EXT_VENDOR_SPECIFIC_5,
+				   RADIUS_VENDOR_ID_FREERADIUS,
+				   RADIUS_VENDOR_ATTR_FREERADIUS_802_1X_EAPOL_KEY_MSG,
+				   query->eapol, query->eapol_len)) {
+		wpa_printf(MSG_DEBUG, "Could not add FreeRADIUS-802.1X-EAPoL-Key-Msg");
+		goto fail;
+	}
+
+	if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, addr) < 0)
+		goto fail;
+	return 0;
+
+ fail:
+	radius_msg_free(msg);
+	return -1;
+}
+#endif /* CONFIG_NO_RADIUS */
+
+
+/**
+ * hostapd_check_acl - Check a specified STA against accept/deny ACLs
+ * @hapd: hostapd BSS data
+ * @addr: MAC address of the STA
+ * @vlan_id: Buffer for returning VLAN ID
+ * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING
+ */
+int hostapd_check_acl(struct hostapd_data *hapd, const u8 *addr,
+		      struct vlan_description *vlan_id)
+{
+	if (hostapd_maclist_found(hapd->conf->accept_mac,
+				  hapd->conf->num_accept_mac, addr, vlan_id))
+		return HOSTAPD_ACL_ACCEPT;
+
+	if (hostapd_maclist_found(hapd->conf->deny_mac,
+				  hapd->conf->num_deny_mac, addr, vlan_id))
+		return HOSTAPD_ACL_REJECT;
+
+	if (hapd->conf->macaddr_acl == ACCEPT_UNLESS_DENIED)
+		return HOSTAPD_ACL_ACCEPT;
+	if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED)
+		return HOSTAPD_ACL_REJECT;
+
+	return HOSTAPD_ACL_PENDING;
+}
+
+
+/**
+ * hostapd_allowed_address - Check whether a specified STA can be authenticated
+ * @hapd: hostapd BSS data
+ * @addr: MAC address of the STA
+ * @msg: Authentication message
+ * @len: Length of msg in octets
+ * @out.session_timeout: Buffer for returning session timeout (from RADIUS)
+ * @out.acct_interim_interval: Buffer for returning account interval (from
+ *	RADIUS)
+ * @out.vlan_id: Buffer for returning VLAN ID
+ * @out.psk: Linked list buffer for returning WPA PSK
+ * @out.identity: Buffer for returning identity (from RADIUS)
+ * @out.radius_cui: Buffer for returning CUI (from RADIUS)
+ * @is_probe_req: Whether this query for a Probe Request frame
+ * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING
+ *
+ * The caller is responsible for properly cloning the returned out->identity and
+ * out->radius_cui and out->psk values.
+ */
+int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
+			    const u8 *msg, size_t len, struct radius_sta *out,
+			    int is_probe_req)
+{
+	int res;
+
+	os_memset(out, 0, sizeof(*out));
+
+	res = hostapd_check_acl(hapd, addr, &out->vlan_id);
+	if (res != HOSTAPD_ACL_PENDING)
+		return res;
+
+	if (hapd->conf->macaddr_acl == USE_EXTERNAL_RADIUS_AUTH) {
+#ifdef CONFIG_NO_RADIUS
+		return HOSTAPD_ACL_REJECT;
+#else /* CONFIG_NO_RADIUS */
+		struct hostapd_acl_query_data *query;
+
+		if (is_probe_req) {
+			/* Skip RADIUS queries for Probe Request frames to avoid
+			 * excessive load on the authentication server. */
+			return HOSTAPD_ACL_ACCEPT;
+		};
+
+		if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED)
+			os_memset(&out->vlan_id, 0, sizeof(out->vlan_id));
+
+		/* Check whether ACL cache has an entry for this station */
+		res = hostapd_acl_cache_get(hapd, addr, out);
+		if (res == HOSTAPD_ACL_ACCEPT ||
+		    res == HOSTAPD_ACL_ACCEPT_TIMEOUT)
+			return res;
+		if (res == HOSTAPD_ACL_REJECT)
+			return HOSTAPD_ACL_REJECT;
+
+		query = hapd->acl_queries;
+		while (query) {
+			if (os_memcmp(query->addr, addr, ETH_ALEN) == 0) {
+				/* pending query in RADIUS retransmit queue;
+				 * do not generate a new one */
+				return HOSTAPD_ACL_PENDING;
+			}
+			query = query->next;
+		}
+
+		if (!hapd->conf->radius->auth_server)
+			return HOSTAPD_ACL_REJECT;
+
+		/* No entry in the cache - query external RADIUS server */
+		query = os_zalloc(sizeof(*query));
+		if (!query) {
+			wpa_printf(MSG_ERROR, "malloc for query data failed");
+			return HOSTAPD_ACL_REJECT;
+		}
+		os_get_reltime(&query->timestamp);
+		os_memcpy(query->addr, addr, ETH_ALEN);
+		if (hostapd_radius_acl_query(hapd, addr, query)) {
+			wpa_printf(MSG_DEBUG,
+				   "Failed to send Access-Request for ACL query.");
+			hostapd_acl_query_free(query);
+			return HOSTAPD_ACL_REJECT;
+		}
+
+		query->auth_msg = os_memdup(msg, len);
+		if (!query->auth_msg) {
+			wpa_printf(MSG_ERROR,
+				   "Failed to allocate memory for auth frame.");
+			hostapd_acl_query_free(query);
+			return HOSTAPD_ACL_REJECT;
+		}
+		query->auth_msg_len = len;
+		query->next = hapd->acl_queries;
+		hapd->acl_queries = query;
+
+		/* Queued data will be processed in hostapd_acl_recv_radius()
+		 * when RADIUS server replies to the sent Access-Request. */
+		return HOSTAPD_ACL_PENDING;
+#endif /* CONFIG_NO_RADIUS */
+	}
+
+	return HOSTAPD_ACL_REJECT;
+}
+
+
+#ifndef CONFIG_NO_RADIUS
+static void hostapd_acl_expire_cache(struct hostapd_data *hapd,
+				     struct os_reltime *now)
+{
+	struct hostapd_cached_radius_acl *prev, *entry, *tmp;
+
+	prev = NULL;
+	entry = hapd->acl_cache;
+
+	while (entry) {
+		if (os_reltime_expired(now, &entry->timestamp,
+				       RADIUS_ACL_TIMEOUT)) {
+			wpa_printf(MSG_DEBUG, "Cached ACL entry for " MACSTR
+				   " has expired.", MAC2STR(entry->addr));
+			if (prev)
+				prev->next = entry->next;
+			else
+				hapd->acl_cache = entry->next;
+			hostapd_drv_set_radius_acl_expire(hapd, entry->addr);
+			tmp = entry;
+			entry = entry->next;
+			hostapd_acl_cache_free_entry(tmp);
+			continue;
+		}
+
+		prev = entry;
+		entry = entry->next;
+	}
+}
+
+
+static void hostapd_acl_expire_queries(struct hostapd_data *hapd,
+				       struct os_reltime *now)
+{
+	struct hostapd_acl_query_data *prev, *entry, *tmp;
+
+	prev = NULL;
+	entry = hapd->acl_queries;
+
+	while (entry) {
+		if (os_reltime_expired(now, &entry->timestamp,
+				       RADIUS_ACL_TIMEOUT)) {
+			wpa_printf(MSG_DEBUG, "ACL query for " MACSTR
+				   " has expired.", MAC2STR(entry->addr));
+			if (prev)
+				prev->next = entry->next;
+			else
+				hapd->acl_queries = entry->next;
+
+			tmp = entry;
+			entry = entry->next;
+			hostapd_acl_query_free(tmp);
+			continue;
+		}
+
+		prev = entry;
+		entry = entry->next;
+	}
+}
+
+
+/**
+ * hostapd_acl_expire - ACL cache expiration callback
+ * @hapd: struct hostapd_data *
+ */
+void hostapd_acl_expire(struct hostapd_data *hapd)
+{
+	struct os_reltime now;
+
+	os_get_reltime(&now);
+	hostapd_acl_expire_cache(hapd, &now);
+	hostapd_acl_expire_queries(hapd, &now);
+}
+
+
+static void decode_tunnel_passwords(struct hostapd_data *hapd,
+				    const u8 *shared_secret,
+				    size_t shared_secret_len,
+				    struct radius_msg *msg,
+				    struct radius_msg *req,
+				    struct hostapd_cached_radius_acl *cache)
+{
+	int passphraselen;
+	char *passphrase;
+	size_t i;
+	struct hostapd_sta_wpa_psk_short *psk;
+
+	/*
+	 * Decode all tunnel passwords as PSK and save them into a linked list.
+	 */
+	for (i = 0; ; i++) {
+		passphrase = radius_msg_get_tunnel_password(
+			msg, &passphraselen, shared_secret, shared_secret_len,
+			req, i);
+		/*
+		 * Passphrase is NULL iff there is no i-th Tunnel-Password
+		 * attribute in msg.
+		 */
+		if (!passphrase)
+			break;
+
+		/*
+		 * Passphase should be 8..63 chars (to be hashed with SSID)
+		 * or 64 chars hex string (no separate hashing with SSID).
+		 */
+
+		if (passphraselen < MIN_PASSPHRASE_LEN ||
+		    passphraselen > MAX_PASSPHRASE_LEN + 1)
+			goto free_pass;
+
+		/*
+		 * passphrase does not contain the NULL termination.
+		 * Add it here as pbkdf2_sha1() requires it.
+		 */
+		psk = os_zalloc(sizeof(struct hostapd_sta_wpa_psk_short));
+		if (psk) {
+			if ((passphraselen == MAX_PASSPHRASE_LEN + 1) &&
+			    (hexstr2bin(passphrase, psk->psk, PMK_LEN) < 0)) {
+				hostapd_logger(hapd, cache->addr,
+					       HOSTAPD_MODULE_RADIUS,
+					       HOSTAPD_LEVEL_WARNING,
+					       "invalid hex string (%d chars) in Tunnel-Password",
+					       passphraselen);
+				goto skip;
+			} else if (passphraselen <= MAX_PASSPHRASE_LEN) {
+				os_memcpy(psk->passphrase, passphrase,
+					  passphraselen);
+				psk->is_passphrase = 1;
+			}
+			psk->next = cache->info.psk;
+			cache->info.psk = psk;
+			psk = NULL;
+		}
+skip:
+		os_free(psk);
+free_pass:
+		os_free(passphrase);
+	}
+}
+
+
+/**
+ * hostapd_acl_recv_radius - Process incoming RADIUS Authentication messages
+ * @msg: RADIUS response message
+ * @req: RADIUS request message
+ * @shared_secret: RADIUS shared secret
+ * @shared_secret_len: Length of shared_secret in octets
+ * @data: Context data (struct hostapd_data *)
+ * Returns: RADIUS_RX_PROCESSED if RADIUS message was a reply to ACL query (and
+ * was processed here) or RADIUS_RX_UNKNOWN if not.
+ */
+static RadiusRxResult
+hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req,
+			const u8 *shared_secret, size_t shared_secret_len,
+			void *data)
+{
+	struct hostapd_data *hapd = data;
+	struct hostapd_acl_query_data *query, *prev;
+	struct hostapd_cached_radius_acl *cache;
+	struct radius_sta *info;
+	struct radius_hdr *hdr = radius_msg_get_hdr(msg);
+
+	query = hapd->acl_queries;
+	prev = NULL;
+	while (query) {
+		if (query->radius_id == hdr->identifier)
+			break;
+		prev = query;
+		query = query->next;
+	}
+	if (!query)
+		return RADIUS_RX_UNKNOWN;
+
+	wpa_printf(MSG_DEBUG,
+		   "Found matching Access-Request for RADIUS message (id=%d)",
+		   query->radius_id);
+
+	if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) {
+		wpa_printf(MSG_INFO,
+			   "Incoming RADIUS packet did not have correct authenticator - dropped");
+		return RADIUS_RX_INVALID_AUTHENTICATOR;
+	}
+
+	if (hdr->code != RADIUS_CODE_ACCESS_ACCEPT &&
+	    hdr->code != RADIUS_CODE_ACCESS_REJECT) {
+		wpa_printf(MSG_DEBUG,
+			   "Unknown RADIUS message code %d to ACL query",
+			   hdr->code);
+		return RADIUS_RX_UNKNOWN;
+	}
+
+	/* Insert Accept/Reject info into ACL cache */
+	cache = os_zalloc(sizeof(*cache));
+	if (!cache) {
+		wpa_printf(MSG_DEBUG, "Failed to add ACL cache entry");
+		goto done;
+	}
+	os_get_reltime(&cache->timestamp);
+	os_memcpy(cache->addr, query->addr, sizeof(cache->addr));
+	info = &cache->info;
+	if (hdr->code == RADIUS_CODE_ACCESS_ACCEPT) {
+		u8 *buf;
+		size_t len;
+
+		if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT,
+					      &info->session_timeout) == 0)
+			cache->accepted = HOSTAPD_ACL_ACCEPT_TIMEOUT;
+		else
+			cache->accepted = HOSTAPD_ACL_ACCEPT;
+
+		if (radius_msg_get_attr_int32(
+			    msg, RADIUS_ATTR_ACCT_INTERIM_INTERVAL,
+			    &info->acct_interim_interval) == 0 &&
+		    info->acct_interim_interval < 60) {
+			wpa_printf(MSG_DEBUG,
+				   "Ignored too small Acct-Interim-Interval %d for STA "
+				   MACSTR,
+				   info->acct_interim_interval,
+				   MAC2STR(query->addr));
+			info->acct_interim_interval = 0;
+		}
+
+		if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED)
+			info->vlan_id.notempty = !!radius_msg_get_vlanid(
+				msg, &info->vlan_id.untagged,
+				MAX_NUM_TAGGED_VLAN, info->vlan_id.tagged);
+
+		decode_tunnel_passwords(hapd, shared_secret, shared_secret_len,
+					msg, req, cache);
+
+		if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME,
+					    &buf, &len, NULL) == 0) {
+			info->identity = os_zalloc(len + 1);
+			if (info->identity)
+				os_memcpy(info->identity, buf, len);
+		}
+		if (radius_msg_get_attr_ptr(
+			    msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+			    &buf, &len, NULL) == 0) {
+			info->radius_cui = os_zalloc(len + 1);
+			if (info->radius_cui)
+				os_memcpy(info->radius_cui, buf, len);
+		}
+
+		if (hapd->conf->wpa_psk_radius == PSK_RADIUS_REQUIRED &&
+		    !info->psk)
+			cache->accepted = HOSTAPD_ACL_REJECT;
+
+		if (info->vlan_id.notempty &&
+		    !hostapd_vlan_valid(hapd->conf->vlan, &info->vlan_id)) {
+			hostapd_logger(hapd, query->addr,
+				       HOSTAPD_MODULE_RADIUS,
+				       HOSTAPD_LEVEL_INFO,
+				       "Invalid VLAN %d%s received from RADIUS server",
+				       info->vlan_id.untagged,
+				       info->vlan_id.tagged[0] ? "+" : "");
+			os_memset(&info->vlan_id, 0, sizeof(info->vlan_id));
+		}
+		if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_REQUIRED &&
+		    !info->vlan_id.notempty)
+			cache->accepted = HOSTAPD_ACL_REJECT;
+	} else
+		cache->accepted = HOSTAPD_ACL_REJECT;
+	cache->next = hapd->acl_cache;
+	hapd->acl_cache = cache;
+
+	if (query->radius_psk) {
+		struct sta_info *sta;
+		bool success = cache->accepted == HOSTAPD_ACL_ACCEPT;
+
+		sta = ap_get_sta(hapd, query->addr);
+		if (!sta || !sta->wpa_sm) {
+			wpa_printf(MSG_DEBUG,
+				   "No STA/SM entry found for the RADIUS PSK response");
+			goto done;
+		}
+#ifdef NEED_AP_MLME
+		if (success &&
+		    (ieee802_11_set_radius_info(hapd, sta, cache->accepted,
+						info) < 0 ||
+		     ap_sta_bind_vlan(hapd, sta) < 0))
+			success = false;
+#endif /* NEED_AP_MLME */
+		wpa_auth_sta_radius_psk_resp(sta->wpa_sm, success);
+	} else {
+#ifdef CONFIG_DRIVER_RADIUS_ACL
+		hostapd_drv_set_radius_acl_auth(hapd, query->addr,
+						cache->accepted,
+						info->session_timeout);
+#else /* CONFIG_DRIVER_RADIUS_ACL */
+#ifdef NEED_AP_MLME
+		/* Re-send original authentication frame for 802.11 processing
+		 */
+		wpa_printf(MSG_DEBUG,
+			   "Re-sending authentication frame after successful RADIUS ACL query");
+		ieee802_11_mgmt(hapd, query->auth_msg, query->auth_msg_len,
+				NULL);
+#endif /* NEED_AP_MLME */
+#endif /* CONFIG_DRIVER_RADIUS_ACL */
+	}
+
+ done:
+	if (!prev)
+		hapd->acl_queries = query->next;
+	else
+		prev->next = query->next;
+
+	hostapd_acl_query_free(query);
+
+	return RADIUS_RX_PROCESSED;
+}
+#endif /* CONFIG_NO_RADIUS */
+
+
+/**
+ * hostapd_acl_init: Initialize IEEE 802.11 ACL
+ * @hapd: hostapd BSS data
+ * Returns: 0 on success, -1 on failure
+ */
+int hostapd_acl_init(struct hostapd_data *hapd)
+{
+#ifndef CONFIG_NO_RADIUS
+	if (radius_client_register(hapd->radius, RADIUS_AUTH,
+				   hostapd_acl_recv_radius, hapd))
+		return -1;
+#endif /* CONFIG_NO_RADIUS */
+
+	return 0;
+}
+
+
+/**
+ * hostapd_acl_deinit - Deinitialize IEEE 802.11 ACL
+ * @hapd: hostapd BSS data
+ */
+void hostapd_acl_deinit(struct hostapd_data *hapd)
+{
+	struct hostapd_acl_query_data *query, *prev;
+
+#ifndef CONFIG_NO_RADIUS
+	hostapd_acl_cache_free(hapd->acl_cache);
+	hapd->acl_cache = NULL;
+#endif /* CONFIG_NO_RADIUS */
+
+	query = hapd->acl_queries;
+	hapd->acl_queries = NULL;
+	while (query) {
+		prev = query;
+		query = query->next;
+		hostapd_acl_query_free(prev);
+	}
+}
+
+
+void hostapd_copy_psk_list(struct hostapd_sta_wpa_psk_short **psk,
+			   struct hostapd_sta_wpa_psk_short *src)
+{
+	if (!psk)
+		return;
+
+	if (src)
+		src->ref++;
+
+	*psk = src;
+}
+
+
+void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk)
+{
+	if (psk && psk->ref) {
+		/* This will be freed when the last reference is dropped. */
+		psk->ref--;
+		return;
+	}
+
+	while (psk) {
+		struct hostapd_sta_wpa_psk_short *prev = psk;
+		psk = psk->next;
+		bin_clear_free(prev, sizeof(*prev));
+	}
+}
+
+
+#ifndef CONFIG_NO_RADIUS
+void hostapd_acl_req_radius_psk(struct hostapd_data *hapd, const u8 *addr,
+				int key_mgmt, const u8 *anonce,
+				const u8 *eapol, size_t eapol_len)
+{
+	struct hostapd_acl_query_data *query;
+
+	query = os_zalloc(sizeof(*query));
+	if (!query)
+		return;
+
+	query->radius_psk = true;
+	query->akm = key_mgmt;
+	os_get_reltime(&query->timestamp);
+	os_memcpy(query->addr, addr, ETH_ALEN);
+	if (anonce)
+		query->anonce = os_memdup(anonce, WPA_NONCE_LEN);
+	if (eapol) {
+		query->eapol = os_memdup(eapol, eapol_len);
+		query->eapol_len = eapol_len;
+	}
+	if (hostapd_radius_acl_query(hapd, addr, query)) {
+		wpa_printf(MSG_DEBUG,
+			   "Failed to send Access-Request for RADIUS PSK/ACL query");
+		hostapd_acl_query_free(query);
+		return;
+	}
+
+	query->next = hapd->acl_queries;
+	hapd->acl_queries = query;
+}
+#endif /* CONFIG_NO_RADIUS */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11_auth.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11_auth.h
new file mode 100644
index 0000000..22ae1a9
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11_auth.h
@@ -0,0 +1,43 @@
+/*
+ * hostapd / IEEE 802.11 authentication (ACL)
+ * Copyright (c) 2003-2022, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IEEE802_11_AUTH_H
+#define IEEE802_11_AUTH_H
+
+enum {
+	HOSTAPD_ACL_REJECT = 0,
+	HOSTAPD_ACL_ACCEPT = 1,
+	HOSTAPD_ACL_PENDING = 2,
+	HOSTAPD_ACL_ACCEPT_TIMEOUT = 3
+};
+
+struct radius_sta {
+	u32 session_timeout;
+	u32 acct_interim_interval;
+	struct vlan_description vlan_id;
+	struct hostapd_sta_wpa_psk_short *psk;
+	char *identity;
+	char *radius_cui;
+};
+
+int hostapd_check_acl(struct hostapd_data *hapd, const u8 *addr,
+		      struct vlan_description *vlan_id);
+int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
+			    const u8 *msg, size_t len, struct radius_sta *out,
+			    int is_probe_req);
+int hostapd_acl_init(struct hostapd_data *hapd);
+void hostapd_acl_deinit(struct hostapd_data *hapd);
+void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk);
+void hostapd_acl_expire(struct hostapd_data *hapd);
+void hostapd_copy_psk_list(struct hostapd_sta_wpa_psk_short **psk,
+			   struct hostapd_sta_wpa_psk_short *src);
+void hostapd_acl_req_radius_psk(struct hostapd_data *hapd, const u8 *addr,
+				int key_mgmt, const u8 *anonce,
+				const u8 *eapol, size_t eapol_len);
+
+#endif /* IEEE802_11_AUTH_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11_eht.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11_eht.c
new file mode 100644
index 0000000..9a07f75
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11_eht.c
@@ -0,0 +1,1139 @@
+/*
+ * hostapd / IEEE 802.11be EHT
+ * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "crypto/dh_groups.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ieee802_11.h"
+
+
+static u16 ieee80211_eht_ppet_size(u16 ppe_thres_hdr, const u8 *phy_cap_info)
+{
+	u8 ru;
+	u16 sz = 0;
+
+	if ((phy_cap_info[EHT_PHYCAP_PPE_THRESHOLD_PRESENT_IDX] &
+	     EHT_PHYCAP_PPE_THRESHOLD_PRESENT) == 0)
+		return 0;
+
+	ru = (ppe_thres_hdr &
+	      EHT_PPE_THRES_RU_INDEX_MASK) >> EHT_PPE_THRES_RU_INDEX_SHIFT;
+	while (ru) {
+		if (ru & 0x1)
+			sz++;
+		ru >>= 1;
+	}
+
+	sz = sz * (1 + ((ppe_thres_hdr & EHT_PPE_THRES_NSS_MASK) >>
+			EHT_PPE_THRES_NSS_SHIFT));
+	sz = (sz * 6) + 9;
+	if (sz % 8)
+		sz += 8;
+	sz /= 8;
+
+	return sz;
+}
+
+
+static u8 ieee80211_eht_mcs_set_size(enum hostapd_hw_mode mode, u8 opclass,
+				     u8 he_oper_chwidth, const u8 *he_phy_cap,
+				     const u8 *eht_phy_cap)
+{
+	u8 sz = EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS;
+	bool band24, band5, band6;
+	u8 he_phy_cap_chwidth = ~HE_PHYCAP_CHANNEL_WIDTH_MASK;
+
+	switch (he_oper_chwidth) {
+	case CONF_OPER_CHWIDTH_80P80MHZ:
+		he_phy_cap_chwidth |=
+			HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G;
+		/* fall through */
+	case CONF_OPER_CHWIDTH_160MHZ:
+		he_phy_cap_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
+		/* fall through */
+	case CONF_OPER_CHWIDTH_80MHZ:
+	case CONF_OPER_CHWIDTH_USE_HT:
+		he_phy_cap_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
+			HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G;
+		break;
+	}
+
+	he_phy_cap_chwidth &= he_phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX];
+
+	band24 = mode == HOSTAPD_MODE_IEEE80211B ||
+		mode == HOSTAPD_MODE_IEEE80211G ||
+		mode == NUM_HOSTAPD_MODES;
+	band5 = mode == HOSTAPD_MODE_IEEE80211A ||
+		mode == NUM_HOSTAPD_MODES;
+	band6 = is_6ghz_op_class(opclass);
+
+	if (band24 &&
+	    (he_phy_cap_chwidth & HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G) == 0)
+		return EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY;
+
+	if (band5 &&
+	    (he_phy_cap_chwidth &
+	     (HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G |
+	      HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
+	      HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G)) == 0)
+		return EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY;
+
+	if (band5 &&
+	    (he_phy_cap_chwidth &
+	     (HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
+	      HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G)))
+	    sz += EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS;
+
+	if (band6 &&
+	    (eht_phy_cap[EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_IDX] &
+	     EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_MASK))
+		sz += EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS;
+
+	return sz;
+}
+
+
+size_t hostapd_eid_eht_capab_len(struct hostapd_data *hapd,
+				 enum ieee80211_op_mode opmode)
+{
+	struct hostapd_hw_modes *mode;
+	struct eht_capabilities *eht_cap;
+	size_t len = 3 + 2 + EHT_PHY_CAPAB_LEN;
+
+	mode = hapd->iface->current_mode;
+	if (!mode)
+		return 0;
+
+	eht_cap = &mode->eht_capab[opmode];
+	if (!eht_cap->eht_supported)
+		return 0;
+
+	len += ieee80211_eht_mcs_set_size(mode->mode, hapd->iconf->op_class,
+					  hapd->iconf->he_oper_chwidth,
+					  mode->he_capab[opmode].phy_cap,
+					  eht_cap->phy_cap);
+	len += ieee80211_eht_ppet_size(WPA_GET_LE16(&eht_cap->ppet[0]),
+				       eht_cap->phy_cap);
+
+	return len;
+}
+
+
+u8 * hostapd_eid_eht_capab(struct hostapd_data *hapd, u8 *eid,
+			   enum ieee80211_op_mode opmode)
+{
+	struct hostapd_hw_modes *mode;
+	struct eht_capabilities *eht_cap;
+	struct ieee80211_eht_capabilities *cap;
+	size_t mcs_nss_len, ppe_thresh_len;
+	u8 *pos = eid, *length_pos;
+
+	mode = hapd->iface->current_mode;
+	if (!mode)
+		return eid;
+
+	eht_cap = &mode->eht_capab[opmode];
+	if (!eht_cap->eht_supported)
+		return eid;
+
+	*pos++ = WLAN_EID_EXTENSION;
+	length_pos = pos++;
+	*pos++ = WLAN_EID_EXT_EHT_CAPABILITIES;
+
+	cap = (struct ieee80211_eht_capabilities *) pos;
+	os_memset(cap, 0, sizeof(*cap));
+	cap->mac_cap = host_to_le16(eht_cap->mac_cap);
+	os_memcpy(cap->phy_cap, eht_cap->phy_cap, EHT_PHY_CAPAB_LEN);
+
+	if (!is_6ghz_op_class(hapd->iconf->op_class))
+		cap->phy_cap[EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_IDX] &=
+			~EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_MASK;
+	if (!hapd->iface->conf->eht_phy_capab.su_beamformer)
+		cap->phy_cap[EHT_PHYCAP_SU_BEAMFORMER_IDX] &=
+			~EHT_PHYCAP_SU_BEAMFORMER;
+
+	if (!hapd->iface->conf->eht_phy_capab.su_beamformee)
+		cap->phy_cap[EHT_PHYCAP_SU_BEAMFORMEE_IDX] &=
+			~EHT_PHYCAP_SU_BEAMFORMEE;
+
+	if (!hapd->iface->conf->eht_phy_capab.mu_beamformer)
+		cap->phy_cap[EHT_PHYCAP_MU_BEAMFORMER_IDX] &=
+			~EHT_PHYCAP_MU_BEAMFORMER_MASK;
+
+	pos = cap->optional;
+
+	mcs_nss_len = ieee80211_eht_mcs_set_size(mode->mode,
+						 hapd->iconf->op_class,
+						 hapd->iconf->he_oper_chwidth,
+						 mode->he_capab[opmode].phy_cap,
+						 eht_cap->phy_cap);
+	if (mcs_nss_len) {
+		os_memcpy(pos, eht_cap->mcs, mcs_nss_len);
+		pos += mcs_nss_len;
+	}
+
+	ppe_thresh_len = ieee80211_eht_ppet_size(
+				WPA_GET_LE16(&eht_cap->ppet[0]),
+				eht_cap->phy_cap);
+	if (ppe_thresh_len) {
+		os_memcpy(pos, eht_cap->ppet, ppe_thresh_len);
+		pos += ppe_thresh_len;
+	}
+
+	*length_pos = pos - (eid + 2);
+	return pos;
+}
+
+
+u8 * hostapd_eid_eht_operation(struct hostapd_data *hapd, u8 *eid)
+{
+	struct hostapd_config *conf = hapd->iconf;
+	struct ieee80211_eht_operation *oper;
+	u8 *pos = eid, seg0 = 0, seg1 = 0;
+	enum oper_chan_width chwidth;
+	size_t elen = 1 + 4 + 3;
+
+	if (!hapd->iface->current_mode)
+		return eid;
+
+	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;
+
+	oper = (struct ieee80211_eht_operation *) pos;
+	oper->oper_params = EHT_OPER_INFO_PRESENT;
+
+	/* TODO: Fill in appropriate EHT-MCS max Nss information */
+	oper->basic_eht_mcs_nss_set[0] = 0x11;
+	oper->basic_eht_mcs_nss_set[1] = 0x00;
+	oper->basic_eht_mcs_nss_set[2] = 0x00;
+	oper->basic_eht_mcs_nss_set[3] = 0x00;
+
+	if (is_6ghz_op_class(conf->op_class))
+		chwidth = op_class_to_ch_width(conf->op_class);
+	else
+		chwidth = conf->eht_oper_chwidth;
+
+	seg0 = hostapd_get_oper_centr_freq_seg0_idx(conf);
+
+	switch (chwidth) {
+	case CONF_OPER_CHWIDTH_320MHZ:
+		oper->oper_info.control |= EHT_OPER_CHANNEL_WIDTH_320MHZ;
+		seg1 = seg0;
+		if (hapd->iconf->channel < seg0)
+			seg0 -= 16;
+		else
+			seg0 += 16;
+		break;
+	case CONF_OPER_CHWIDTH_160MHZ:
+		oper->oper_info.control |= EHT_OPER_CHANNEL_WIDTH_160MHZ;
+		seg1 = seg0;
+		if (hapd->iconf->channel < seg0)
+			seg0 -= 8;
+		else
+			seg0 += 8;
+		break;
+	case CONF_OPER_CHWIDTH_80MHZ:
+		oper->oper_info.control |= EHT_OPER_CHANNEL_WIDTH_80MHZ;
+		break;
+	case CONF_OPER_CHWIDTH_USE_HT:
+		if (seg0)
+			oper->oper_info.control |= EHT_OPER_CHANNEL_WIDTH_40MHZ;
+		break;
+	default:
+		return eid;
+	}
+
+	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;
+}
+
+
+static bool check_valid_eht_mcs_nss(struct hostapd_data *hapd, const u8 *ap_mcs,
+				    const u8 *sta_mcs, u8 mcs_count, u8 map_len)
+{
+	unsigned int i, j;
+
+	for (i = 0; i < mcs_count; i++) {
+		ap_mcs += i * 3;
+		sta_mcs += i * 3;
+
+		for (j = 0; j < map_len; j++) {
+			if (((ap_mcs[j] >> 4) & 0xFF) == 0)
+				continue;
+
+			if ((sta_mcs[j] & 0xFF) == 0)
+				continue;
+
+			return true;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "No matching EHT MCS found between AP TX and STA RX");
+	return false;
+}
+
+
+static bool check_valid_eht_mcs(struct hostapd_data *hapd,
+				const u8 *sta_eht_capab,
+				enum ieee80211_op_mode opmode)
+{
+	struct hostapd_hw_modes *mode;
+	const struct ieee80211_eht_capabilities *capab;
+	const u8 *ap_mcs, *sta_mcs;
+	u8 mcs_count = 1;
+
+	mode = hapd->iface->current_mode;
+	if (!mode)
+		return true;
+
+	ap_mcs = mode->eht_capab[opmode].mcs;
+	capab = (const struct ieee80211_eht_capabilities *) sta_eht_capab;
+	sta_mcs = capab->optional;
+
+	if (ieee80211_eht_mcs_set_size(mode->mode, hapd->iconf->op_class,
+				       hapd->iconf->he_oper_chwidth,
+				       mode->he_capab[opmode].phy_cap,
+				       mode->eht_capab[opmode].phy_cap) ==
+	    EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY)
+		return check_valid_eht_mcs_nss(
+			hapd, ap_mcs, sta_mcs, 1,
+			EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY);
+
+	switch (hapd->iface->conf->eht_oper_chwidth) {
+	case CONF_OPER_CHWIDTH_320MHZ:
+		mcs_count++;
+		/* fall through */
+	case CONF_OPER_CHWIDTH_80P80MHZ:
+	case CONF_OPER_CHWIDTH_160MHZ:
+		mcs_count++;
+		break;
+	default:
+		break;
+	}
+
+	return check_valid_eht_mcs_nss(hapd, ap_mcs, sta_mcs, mcs_count,
+				       EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS);
+}
+
+
+static bool ieee80211_invalid_eht_cap_size(enum hostapd_hw_mode mode,
+					   u8 opclass, u8 he_oper_chwidth,
+					   const u8 *he_cap, const u8 *eht_cap,
+					   size_t len)
+{
+	const struct ieee80211_he_capabilities *he_capab;
+	struct ieee80211_eht_capabilities *cap;
+	const u8 *he_phy_cap;
+	size_t cap_len;
+	u16 ppe_thres_hdr;
+
+	he_capab = (const struct ieee80211_he_capabilities *) he_cap;
+	he_phy_cap = he_capab->he_phy_capab_info;
+	cap = (struct ieee80211_eht_capabilities *) eht_cap;
+	cap_len = sizeof(*cap) - sizeof(cap->optional);
+	if (len < cap_len)
+		return true;
+
+	cap_len += ieee80211_eht_mcs_set_size(mode, opclass, he_oper_chwidth,
+					      he_phy_cap, cap->phy_cap);
+	if (len < cap_len)
+		return true;
+
+	ppe_thres_hdr = len > cap_len + 1 ?
+		WPA_GET_LE16(&eht_cap[cap_len]) : 0x01ff;
+	cap_len += ieee80211_eht_ppet_size(ppe_thres_hdr, cap->phy_cap);
+
+	return len < cap_len;
+}
+
+
+u16 copy_sta_eht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+		       enum ieee80211_op_mode opmode,
+		       const u8 *he_capab, size_t he_capab_len,
+		       const u8 *eht_capab, size_t eht_capab_len)
+{
+	struct hostapd_hw_modes *c_mode = hapd->iface->current_mode;
+	enum hostapd_hw_mode mode = c_mode ? c_mode->mode : NUM_HOSTAPD_MODES;
+
+	if (!hapd->iconf->ieee80211be || hapd->conf->disable_11be ||
+	    !he_capab || he_capab_len < IEEE80211_HE_CAPAB_MIN_LEN ||
+	    !eht_capab ||
+	    ieee80211_invalid_eht_cap_size(mode, hapd->iconf->op_class,
+					   hapd->iconf->he_oper_chwidth,
+					   he_capab, eht_capab,
+					   eht_capab_len) ||
+	    !check_valid_eht_mcs(hapd, eht_capab, opmode)) {
+		sta->flags &= ~WLAN_STA_EHT;
+		os_free(sta->eht_capab);
+		sta->eht_capab = NULL;
+		return WLAN_STATUS_SUCCESS;
+	}
+
+	os_free(sta->eht_capab);
+	sta->eht_capab = os_memdup(eht_capab, eht_capab_len);
+	if (!sta->eht_capab) {
+		sta->eht_capab_len = 0;
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	sta->flags |= WLAN_STA_EHT;
+	sta->eht_capab_len = eht_capab_len;
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+void hostapd_get_eht_capab(struct hostapd_data *hapd,
+			   const struct ieee80211_eht_capabilities *src,
+			   struct ieee80211_eht_capabilities *dest,
+			   size_t len)
+{
+	if (!src || !dest)
+		return;
+
+	if (len > sizeof(*dest))
+		len = sizeof(*dest);
+	/* TODO: mask out unsupported features */
+
+	os_memset(dest, 0, sizeof(*dest));
+	os_memcpy(dest, src, len);
+}
+
+
+u8 * hostapd_eid_eht_basic_ml(struct hostapd_data *hapd, u8 *eid,
+			      struct sta_info *info, bool include_mld_id)
+{
+	struct wpabuf *buf;
+	u16 control;
+	u8 *pos = eid;
+	const u8 *ptr;
+	size_t len, slice_len;
+	u8 link_id;
+	u8 common_info_len;
+
+	/*
+	 * As the Multi-Link element can exceed the size of 255 bytes need to
+	 * first build it and then handle fragmentation.
+	 */
+	buf = wpabuf_alloc(1024);
+	if (!buf)
+		return pos;
+
+	/* Multi-Link Control field */
+	control = MULTI_LINK_CONTROL_TYPE_BASIC |
+		BASIC_MULTI_LINK_CTRL_PRES_LINK_ID |
+		BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT |
+		BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA |
+		BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA;
+
+	/*
+	 * Set the basic Multi-Link common information. Hard code the common
+	 * info length to 13 based on the length of the present fields:
+	 * Length (1) + MLD address (6) + Link ID (1) +
+	 * BSS Parameters Change Count (1) + EML Capabilities (2) +
+	 * MLD Capabilities and Operations (2)
+	 */
+	common_info_len = 13;
+
+	if (include_mld_id) {
+		/* AP MLD ID */
+		control |= BASIC_MULTI_LINK_CTRL_PRES_AP_MLD_ID;
+		common_info_len++;
+	}
+
+	wpabuf_put_le16(buf, control);
+
+	wpabuf_put_u8(buf, common_info_len);
+
+	/* Own MLD MAC Address */
+	wpabuf_put_data(buf, hapd->mld_addr, ETH_ALEN);
+
+	/* Own Link ID */
+	wpabuf_put_u8(buf, hapd->mld_link_id);
+
+	/* Currently hard code the BSS Parameters Change Count to 0x1 */
+	wpabuf_put_u8(buf, 0x1);
+
+	wpa_printf(MSG_DEBUG, "MLD: EML Capabilities=0x%x",
+		   hapd->iface->mld_eml_capa);
+	wpabuf_put_le16(buf, hapd->iface->mld_eml_capa);
+
+	wpa_printf(MSG_DEBUG, "MLD: MLD Capabilities and Operations=0x%x",
+		   hapd->iface->mld_mld_capa);
+	wpabuf_put_le16(buf, hapd->iface->mld_mld_capa);
+
+	if (include_mld_id) {
+		wpa_printf(MSG_DEBUG, "MLD: AP MLD ID=0x%x",
+			   hapd->conf->mld_id);
+		wpabuf_put_u8(buf, hapd->conf->mld_id);
+	}
+
+	if (!info)
+		goto out;
+
+	/* Add link info for the other links */
+	for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+		struct mld_link_info *link = &info->mld_info.links[link_id];
+		struct hostapd_data *link_bss;
+
+		/*
+		 * control (2) + station info length (1) + MAC address (6) +
+		 * beacon interval (2) + TSF offset (8) + DTIM info (2) + BSS
+		 * parameters change counter (1) + station profile length.
+		 */
+		const size_t fixed_len = 22;
+		size_t total_len = fixed_len + link->resp_sta_profile_len;
+
+		/* Skip the local one */
+		if (link_id == hapd->mld_link_id || !link->valid)
+			continue;
+
+		link_bss = hostapd_mld_get_link_bss(hapd, link_id);
+		if (!link_bss) {
+			wpa_printf(MSG_ERROR,
+				   "MLD: Couldn't find link BSS - skip it");
+			continue;
+		}
+
+		/* Per-STA Profile subelement */
+		wpabuf_put_u8(buf, EHT_ML_SUB_ELEM_PER_STA_PROFILE);
+
+		if (total_len <= 255)
+			wpabuf_put_u8(buf, total_len);
+		else
+			wpabuf_put_u8(buf, 255);
+
+		/* STA Control */
+		control = (link_id & 0xf) |
+			EHT_PER_STA_CTRL_MAC_ADDR_PRESENT_MSK |
+			EHT_PER_STA_CTRL_COMPLETE_PROFILE_MSK |
+			EHT_PER_STA_CTRL_TSF_OFFSET_PRESENT_MSK |
+			EHT_PER_STA_CTRL_BEACON_INTERVAL_PRESENT_MSK |
+			EHT_PER_STA_CTRL_DTIM_INFO_PRESENT_MSK |
+			EHT_PER_STA_CTRL_BSS_PARAM_CNT_PRESENT_MSK;
+		wpabuf_put_le16(buf, control);
+
+		/* STA Info */
+
+		/* STA Info Length */
+		wpabuf_put_u8(buf, fixed_len - 2);
+		wpabuf_put_data(buf, link->local_addr, ETH_ALEN);
+		wpabuf_put_le16(buf, link_bss->iconf->beacon_int);
+
+		/* TSF Offset */
+		/*
+		 * TODO: Currently setting TSF offset to zero. However, this
+		 * information needs to come from the driver.
+		 */
+		wpabuf_put_le64(buf, 0);
+
+		/* DTIM Info */
+		wpabuf_put_le16(buf, link_bss->conf->dtim_period);
+
+		/* BSS Parameters Change Count */
+		/* TODO: Currently hard code the BSS Parameters Change Count to
+		 * 0x1 */
+		wpabuf_put_u8(buf, 0x1);
+
+		/* Fragment the sub element if needed */
+		if (total_len <= 255) {
+			wpabuf_put_data(buf, link->resp_sta_profile,
+					link->resp_sta_profile_len);
+		} else {
+			ptr = link->resp_sta_profile;
+			len = link->resp_sta_profile_len;
+
+			slice_len = 255 - fixed_len;
+
+			wpabuf_put_data(buf, ptr, slice_len);
+			len -= slice_len;
+			ptr += slice_len;
+
+			while (len) {
+				if (len <= 255)
+					slice_len = len;
+				else
+					slice_len = 255;
+
+				wpabuf_put_u8(buf, EHT_ML_SUB_ELEM_FRAGMENT);
+				wpabuf_put_u8(buf, slice_len);
+				wpabuf_put_data(buf, ptr, slice_len);
+
+				len -= slice_len;
+				ptr += slice_len;
+			}
+		}
+	}
+
+out:
+	/* Fragment the Multi-Link element, if needed */
+	len = wpabuf_len(buf);
+	ptr = wpabuf_head(buf);
+
+	if (len <= 254)
+		slice_len = len;
+	else
+		slice_len = 254;
+
+	*pos++ = WLAN_EID_EXTENSION;
+	*pos++ = slice_len + 1;
+	*pos++ = WLAN_EID_EXT_MULTI_LINK;
+	os_memcpy(pos, ptr, slice_len);
+
+	ptr += slice_len;
+	pos += slice_len;
+	len -= slice_len;
+
+	while (len) {
+		if (len <= 255)
+			slice_len = len;
+		else
+			slice_len = 255;
+
+		*pos++ = WLAN_EID_FRAGMENT;
+		*pos++ = slice_len;
+		os_memcpy(pos, ptr, slice_len);
+
+		ptr += slice_len;
+		pos += slice_len;
+		len -= slice_len;
+	}
+
+	wpabuf_free(buf);
+	return pos;
+}
+
+
+struct wpabuf * hostapd_ml_auth_resp(struct hostapd_data *hapd)
+{
+	struct wpabuf *buf = wpabuf_alloc(12);
+
+	if (!buf)
+		return NULL;
+
+	wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+	wpabuf_put_u8(buf, 10);
+	wpabuf_put_u8(buf, WLAN_EID_EXT_MULTI_LINK);
+	wpabuf_put_le16(buf, MULTI_LINK_CONTROL_TYPE_BASIC);
+	wpabuf_put_u8(buf, ETH_ALEN + 1);
+	wpabuf_put_data(buf, hapd->mld_addr, ETH_ALEN);
+
+	return buf;
+}
+
+
+static const u8 *
+sae_commit_skip_fixed_fields(const struct ieee80211_mgmt *mgmt, size_t len,
+			     const u8 *pos, u16 status_code)
+{
+	u16 group;
+	size_t prime_len;
+	struct crypto_ec *ec;
+
+	if (status_code != WLAN_STATUS_SAE_HASH_TO_ELEMENT)
+		return pos;
+
+	/* SAE H2E commit message (group, scalar, FFE) */
+	if (len < 2) {
+		wpa_printf(MSG_DEBUG,
+			   "EHT: SAE Group is not present");
+		return NULL;
+	}
+
+	group = WPA_GET_LE16(pos);
+	pos += 2;
+
+	/* TODO: How to parse when the group is unknown? */
+	ec = crypto_ec_init(group);
+	if (!ec) {
+		const struct dh_group *dh = dh_groups_get(group);
+
+		if (!dh) {
+			wpa_printf(MSG_DEBUG, "EHT: Unknown SAE group %u",
+				   group);
+			return NULL;
+		}
+
+		prime_len = dh->prime_len;
+	} else {
+		prime_len = crypto_ec_prime_len(ec);
+	}
+
+	wpa_printf(MSG_DEBUG, "EHT: SAE scalar length is %zu", prime_len);
+
+	/* scalar */
+	pos += prime_len;
+
+	if (ec) {
+		pos += prime_len * 2;
+		crypto_ec_deinit(ec);
+	} else {
+		pos += prime_len;
+	}
+
+	if (pos - mgmt->u.auth.variable > (int) len) {
+		wpa_printf(MSG_DEBUG,
+			   "EHT: Too short SAE commit Authentication frame");
+		return NULL;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "EHT: SAE: Authentication frame elements",
+		    pos, (int) len - (pos - mgmt->u.auth.variable));
+
+	return pos;
+}
+
+
+static const u8 *
+sae_confirm_skip_fixed_fields(struct hostapd_data *hapd,
+			      const struct ieee80211_mgmt *mgmt, size_t len,
+			      const u8 *pos, u16 status_code)
+{
+	struct sta_info *sta;
+
+	if (status_code == WLAN_STATUS_REJECTED_WITH_SUGGESTED_BSS_TRANSITION)
+		return pos;
+
+	/* send confirm integer */
+	pos += 2;
+
+	/*
+	 * At this stage we should already have an MLD station and actually SA
+	 * will be replaced with the MLD MAC address by the driver.
+	 */
+	sta = ap_get_sta(hapd, mgmt->sa);
+	if (!sta) {
+		wpa_printf(MSG_DEBUG, "SAE: No MLD STA for SAE confirm");
+		return NULL;
+	}
+
+	if (!sta->sae || sta->sae->state < SAE_COMMITTED || !sta->sae->tmp) {
+		if (sta->sae)
+			wpa_printf(MSG_DEBUG, "SAE: Invalid state=%u",
+				   sta->sae->state);
+		else
+			wpa_printf(MSG_DEBUG, "SAE: No SAE context");
+		return NULL;
+	}
+
+	wpa_printf(MSG_DEBUG, "SAE: confirm: kck_len=%zu",
+		   sta->sae->tmp->kck_len);
+
+	pos += sta->sae->tmp->kck_len;
+
+	if (pos - mgmt->u.auth.variable > (int) len) {
+		wpa_printf(MSG_DEBUG,
+			   "EHT: Too short SAE confirm Authentication frame");
+		return NULL;
+	}
+
+	return pos;
+}
+
+
+static const u8 * auth_skip_fixed_fields(struct hostapd_data *hapd,
+					 const struct ieee80211_mgmt *mgmt,
+					 size_t len)
+{
+	u16 auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
+	u16 auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
+	u16 status_code = le_to_host16(mgmt->u.auth.status_code);
+	const u8 *pos = mgmt->u.auth.variable;
+
+	/* Skip fixed fields as based on IEE P802.11-REVme/D3.0, Table 9-69
+	 * (Presence of fields and elements in Authentications frames) */
+	switch (auth_alg) {
+	case WLAN_AUTH_OPEN:
+		return pos;
+	case WLAN_AUTH_SAE:
+		if (auth_transaction == 1) {
+			if (status_code == WLAN_STATUS_SUCCESS) {
+				wpa_printf(MSG_DEBUG,
+					   "EHT: SAE H2E is mandatory for MLD");
+				goto out;
+			}
+
+			return sae_commit_skip_fixed_fields(mgmt, len, pos,
+							    status_code);
+		} else if (auth_transaction == 2) {
+			return sae_confirm_skip_fixed_fields(hapd, mgmt, len,
+							     pos, status_code);
+		}
+
+		return pos;
+	/* TODO: Support additional algorithms that can be used for MLO */
+	case WLAN_AUTH_FT:
+	case WLAN_AUTH_FILS_SK:
+	case WLAN_AUTH_FILS_SK_PFS:
+	case WLAN_AUTH_FILS_PK:
+	case WLAN_AUTH_PASN:
+	default:
+		break;
+	}
+
+out:
+	wpa_printf(MSG_DEBUG,
+		   "TODO: Authentication algorithm %u not supported with MLD",
+		   auth_alg);
+	return NULL;
+}
+
+
+const u8 * hostapd_process_ml_auth(struct hostapd_data *hapd,
+				   const struct ieee80211_mgmt *mgmt,
+				   size_t len)
+{
+	struct ieee802_11_elems elems;
+	const u8 *pos;
+
+	if (!hapd->conf->mld_ap)
+		return NULL;
+
+	len -= offsetof(struct ieee80211_mgmt, u.auth.variable);
+
+	pos = auth_skip_fixed_fields(hapd, mgmt, len);
+	if (!pos)
+		return NULL;
+
+	if (ieee802_11_parse_elems(pos,
+				   (int)len - (pos - mgmt->u.auth.variable),
+				   &elems, 0) == ParseFailed) {
+		wpa_printf(MSG_DEBUG,
+			   "MLD: Failed parsing Authentication frame");
+	}
+
+	if (!elems.basic_mle || !elems.basic_mle_len)
+		return NULL;
+
+	return get_basic_mle_mld_addr(elems.basic_mle, elems.basic_mle_len);
+}
+
+
+static int hostapd_mld_validate_assoc_info(struct hostapd_data *hapd,
+					   struct mld_info *info)
+{
+	u8 i, link_id;
+
+	if (!info->mld_sta) {
+		wpa_printf(MSG_DEBUG, "MLD: Not a non-AP MLD");
+		return 0;
+	}
+
+	/*
+	 * Iterate over the links negotiated in the (Re)Association Request
+	 * frame and validate that they are indeed valid links in the local AP
+	 * MLD.
+	 *
+	 * While at it, also update the local address for the links in the
+	 * mld_info, so it could be easily available for later flows, e.g., for
+	 * the RSN Authenticator, etc.
+	 */
+	for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+		struct hostapd_data *other_hapd;
+
+		if (!info->links[link_id].valid)
+			continue;
+
+		for (i = 0; i < hapd->iface->interfaces->count; i++) {
+			other_hapd = hapd->iface->interfaces->iface[i]->bss[0];
+
+			if (hapd == other_hapd)
+				continue;
+
+			if (other_hapd->conf->mld_ap &&
+			    other_hapd->conf->mld_id == hapd->conf->mld_id &&
+			    link_id == other_hapd->mld_link_id)
+				break;
+		}
+
+		if (i == hapd->iface->interfaces->count &&
+		    link_id != hapd->mld_link_id) {
+			wpa_printf(MSG_DEBUG, "MLD: Invalid link ID=%u",
+				   link_id);
+			return -1;
+		}
+
+		if (i < hapd->iface->interfaces->count)
+			os_memcpy(info->links[link_id].local_addr,
+				  other_hapd->own_addr,
+				  ETH_ALEN);
+	}
+
+	return 0;
+}
+
+
+u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd,
+				 struct ieee802_11_elems *elems,
+				 struct sta_info *sta)
+{
+	struct wpabuf *mlbuf;
+	const struct ieee80211_eht_ml *ml;
+	const struct eht_ml_basic_common_info *common_info;
+	size_t ml_len, common_info_len;
+	struct mld_link_info *link_info;
+	struct mld_info *info = &sta->mld_info;
+	const u8 *pos;
+	int ret = -1;
+	u16 ml_control;
+
+	mlbuf = ieee802_11_defrag_mle(elems, MULTI_LINK_CONTROL_TYPE_BASIC);
+	if (!mlbuf)
+		return WLAN_STATUS_SUCCESS;
+
+	ml = wpabuf_head(mlbuf);
+	ml_len = wpabuf_len(mlbuf);
+
+	ml_control = le_to_host16(ml->ml_control);
+	if ((ml_control & MULTI_LINK_CONTROL_TYPE_MASK) !=
+	    MULTI_LINK_CONTROL_TYPE_BASIC) {
+		wpa_printf(MSG_DEBUG, "MLD: Invalid ML type=%u",
+			   ml_control & MULTI_LINK_CONTROL_TYPE_MASK);
+		goto out;
+	}
+
+	/* Common Info length and MLD MAC address must always be present */
+	common_info_len = 1 + ETH_ALEN;
+
+	if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_LINK_ID) {
+		wpa_printf(MSG_DEBUG, "MLD: Link ID info not expected");
+		goto out;
+	}
+
+	if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT) {
+		wpa_printf(MSG_DEBUG, "MLD: BSS params change not expected");
+		goto out;
+	}
+
+	if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_MSD_INFO) {
+		wpa_printf(MSG_DEBUG, "MLD: Sync delay not expected");
+		goto out;
+	}
+
+	if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA) {
+		common_info_len += 2;
+	} else {
+		wpa_printf(MSG_DEBUG, "MLD: EML capabilities not present");
+	}
+
+	if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA) {
+		common_info_len += 2;
+
+	} else {
+		wpa_printf(MSG_DEBUG, "MLD: MLD capabilities not present");
+		goto out;
+	}
+
+	wpa_printf(MSG_DEBUG, "MLD: expected_common_info_len=%lu",
+		   common_info_len);
+
+	if (sizeof(*ml) + common_info_len > ml_len) {
+		wpa_printf(MSG_DEBUG, "MLD: Not enough bytes for common info");
+		goto out;
+	}
+
+	common_info = (const struct eht_ml_basic_common_info *) ml->variable;
+
+	/* Common information length includes the length octet */
+	if (common_info->len != common_info_len) {
+		wpa_printf(MSG_DEBUG,
+			   "MLD: Invalid common info len=%u (expected %zu)",
+			   common_info->len, common_info_len);
+		goto out;
+	}
+
+	pos = common_info->variable;
+
+	if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA) {
+		info->common_info.eml_capa = WPA_GET_LE16(pos);
+		pos += 2;
+	} else {
+		info->common_info.eml_capa = 0;
+	}
+
+	info->common_info.mld_capa = WPA_GET_LE16(pos);
+	pos += 2;
+
+	wpa_printf(MSG_DEBUG, "MLD: addr=" MACSTR ", eml=0x%x, mld=0x%x",
+		   MAC2STR(info->common_info.mld_addr),
+		   info->common_info.eml_capa, info->common_info.mld_capa);
+
+	/* Check the MLD MAC Address */
+	if (os_memcmp(info->common_info.mld_addr, common_info->mld_addr,
+		      ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "MLD: MLD address mismatch between authentication ("
+			   MACSTR ") and association (" MACSTR ")",
+			   MAC2STR(info->common_info.mld_addr),
+			   MAC2STR(common_info->mld_addr));
+		goto out;
+	}
+
+	info->links[hapd->mld_link_id].valid = true;
+
+	/* Parse the link info field */
+	ml_len -= sizeof(*ml) + common_info_len;
+
+	while (ml_len > 2) {
+		size_t sub_elem_len = *(pos + 1);
+		size_t sta_info_len;
+		u16 control;
+
+		wpa_printf(MSG_DEBUG, "MLD: sub element len=%zu",
+			   sub_elem_len);
+
+		if (2 + sub_elem_len > ml_len) {
+			wpa_printf(MSG_DEBUG,
+				   "MLD: Invalid link info len: %zu %zu",
+				   2 + sub_elem_len, ml_len);
+			goto out;
+		}
+
+		if (*pos == MULTI_LINK_SUB_ELEM_ID_VENDOR) {
+			wpa_printf(MSG_DEBUG,
+				   "MLD: Skip vendor specific subelement");
+
+			pos += 2 + sub_elem_len;
+			ml_len -= 2 + sub_elem_len;
+			continue;
+		}
+
+		if (*pos != MULTI_LINK_SUB_ELEM_ID_PER_STA_PROFILE) {
+			wpa_printf(MSG_DEBUG,
+				   "MLD: Unexpected Multi-Link element subelement ID=%u",
+				   *pos);
+			goto out;
+		}
+
+		/* Skip the subelement ID and the length */
+		pos += 2;
+		ml_len -= 2;
+
+		/* Get the station control field */
+		if (sub_elem_len < 2) {
+			wpa_printf(MSG_DEBUG,
+				   "MLD: Too short Per-STA Profile subelement");
+			goto out;
+		}
+		control = WPA_GET_LE16(pos);
+		link_info = &info->links[control &
+					 EHT_PER_STA_CTRL_LINK_ID_MSK];
+		pos += 2;
+		ml_len -= 2;
+		sub_elem_len -= 2;
+
+		if (!(control & EHT_PER_STA_CTRL_COMPLETE_PROFILE_MSK)) {
+			wpa_printf(MSG_DEBUG,
+				   "MLD: Per-STA complete profile expected");
+			goto out;
+		}
+
+		if (!(control & EHT_PER_STA_CTRL_MAC_ADDR_PRESENT_MSK)) {
+			wpa_printf(MSG_DEBUG,
+				   "MLD: Per-STA MAC address not present");
+			goto out;
+		}
+
+		if ((control & (EHT_PER_STA_CTRL_BEACON_INTERVAL_PRESENT_MSK |
+				EHT_PER_STA_CTRL_DTIM_INFO_PRESENT_MSK))) {
+			wpa_printf(MSG_DEBUG,
+				   "MLD: Beacon/DTIM interval not expected");
+			goto out;
+		}
+
+		/* The length octet and the MAC address must be present */
+		sta_info_len = 1 + ETH_ALEN;
+
+		if (control & EHT_PER_STA_CTRL_NSTR_LINK_PAIR_PRESENT_MSK) {
+			if (control & EHT_PER_STA_CTRL_NSTR_BM_SIZE_MSK)
+				link_info->nstr_bitmap_len = 2;
+			else
+				link_info->nstr_bitmap_len = 1;
+		}
+
+		sta_info_len += link_info->nstr_bitmap_len;
+
+		if (sta_info_len > ml_len || sta_info_len != *pos ||
+		    sta_info_len > sub_elem_len) {
+			wpa_printf(MSG_DEBUG, "MLD: Invalid STA Info length");
+			goto out;
+		}
+
+		/* skip the length */
+		pos++;
+		ml_len--;
+
+		/* get the link address */
+		os_memcpy(link_info->peer_addr, pos, ETH_ALEN);
+		wpa_printf(MSG_DEBUG,
+			   "MLD: assoc: link id=%u, addr=" MACSTR,
+			   control & EHT_PER_STA_CTRL_LINK_ID_MSK,
+			   MAC2STR(link_info->peer_addr));
+
+		pos += ETH_ALEN;
+		ml_len -= ETH_ALEN;
+
+		/* Get the NSTR bitmap */
+		if (link_info->nstr_bitmap_len) {
+			os_memcpy(link_info->nstr_bitmap, pos,
+				  link_info->nstr_bitmap_len);
+			pos += link_info->nstr_bitmap_len;
+			ml_len -= link_info->nstr_bitmap_len;
+		}
+
+		sub_elem_len -= sta_info_len;
+
+		wpa_printf(MSG_DEBUG, "MLD: STA Profile len=%zu", sub_elem_len);
+		if (sub_elem_len > ml_len)
+			goto out;
+
+		if (sub_elem_len > 2)
+			link_info->capability = WPA_GET_LE16(pos);
+
+		pos += sub_elem_len;
+		ml_len -= sub_elem_len;
+
+		wpa_printf(MSG_DEBUG, "MLD: link ctrl=0x%x, " MACSTR
+			   ", nstr bitmap len=%lu",
+			   control, MAC2STR(link_info->peer_addr),
+			   link_info->nstr_bitmap_len);
+
+		link_info->valid = true;
+	}
+
+	if (ml_len) {
+		wpa_printf(MSG_DEBUG, "MLD: %zu bytes left after parsing. fail",
+			   ml_len);
+		goto out;
+	}
+
+	ret = hostapd_mld_validate_assoc_info(hapd, info);
+out:
+	wpabuf_free(mlbuf);
+	if (ret) {
+		os_memset(info, 0, sizeof(*info));
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	return WLAN_STATUS_SUCCESS;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11_he.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11_he.c
new file mode 100644
index 0000000..548a448
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11_he.c
@@ -0,0 +1,570 @@
+/*
+ * hostapd / IEEE 802.11ax HE
+ * Copyright (c) 2016-2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2019 John Crispin <john@phrozen.org>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "beacon.h"
+#include "sta_info.h"
+#include "ieee802_11.h"
+#include "dfs.h"
+
+static u8 ieee80211_he_ppet_size(u8 ppe_thres_hdr, const u8 *phy_cap_info)
+{
+	u8 sz = 0, ru;
+
+	if ((phy_cap_info[HE_PHYCAP_PPE_THRESHOLD_PRESENT_IDX] &
+	     HE_PHYCAP_PPE_THRESHOLD_PRESENT) == 0)
+		return 0;
+
+	ru = (ppe_thres_hdr >> HE_PPE_THRES_RU_INDEX_BITMASK_SHIFT) &
+		HE_PPE_THRES_RU_INDEX_BITMASK_MASK;
+	/* Count the number of 1 bits in RU Index Bitmask */
+	while (ru) {
+		if (ru & 0x1)
+			sz++;
+		ru >>= 1;
+	}
+
+	/* fixed header of 3 (NSTS) + 4 (RU Index Bitmask) = 7 bits */
+	/* 6 * (NSTS + 1) bits for bit 1 in RU Index Bitmask */
+	sz *= 1 + (ppe_thres_hdr & HE_PPE_THRES_NSS_MASK);
+	sz = (sz * 6) + 7;
+	/* PPE Pad to count the number of needed full octets */
+	sz = (sz + 7) / 8;
+
+	return sz;
+}
+
+
+static u8 ieee80211_he_mcs_set_size(const u8 *phy_cap_info)
+{
+	u8 sz = 4;
+
+	if (phy_cap_info[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
+	    HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G)
+		sz += 4;
+	if (phy_cap_info[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
+	    HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G)
+		sz += 4;
+
+	return sz;
+}
+
+
+static int ieee80211_invalid_he_cap_size(const u8 *buf, size_t len)
+{
+	struct ieee80211_he_capabilities *cap;
+	size_t cap_len;
+	u8 ppe_thres_hdr;
+
+	cap = (struct ieee80211_he_capabilities *) buf;
+	cap_len = sizeof(*cap) - sizeof(cap->optional);
+	if (len < cap_len)
+		return 1;
+
+	cap_len += ieee80211_he_mcs_set_size(cap->he_phy_capab_info);
+	if (len < cap_len)
+		return 1;
+
+	ppe_thres_hdr = len > cap_len ? buf[cap_len] : 0xff;
+	cap_len += ieee80211_he_ppet_size(ppe_thres_hdr,
+					  cap->he_phy_capab_info);
+
+	return len < cap_len;
+}
+
+
+u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid,
+			  enum ieee80211_op_mode opmode)
+{
+	struct ieee80211_he_capabilities *cap;
+	struct hostapd_hw_modes *mode = hapd->iface->current_mode;
+	u8 he_oper_chwidth = ~HE_PHYCAP_CHANNEL_WIDTH_MASK;
+	u8 *pos = eid;
+	u8 ie_size = 0, mcs_nss_size = 4, ppet_size = 0;
+
+	if (!mode)
+		return eid;
+
+	ie_size = sizeof(*cap) - sizeof(cap->optional);
+	ppet_size = ieee80211_he_ppet_size(mode->he_capab[opmode].ppet[0],
+					   mode->he_capab[opmode].phy_cap);
+
+	switch (hapd->iface->conf->he_oper_chwidth) {
+	case CONF_OPER_CHWIDTH_80P80MHZ:
+		he_oper_chwidth |=
+			HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G;
+		mcs_nss_size += 4;
+		/* fall through */
+	case CONF_OPER_CHWIDTH_160MHZ:
+		he_oper_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
+		mcs_nss_size += 4;
+		/* fall through */
+	case CONF_OPER_CHWIDTH_80MHZ:
+	case CONF_OPER_CHWIDTH_USE_HT:
+		he_oper_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
+			HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G;
+		break;
+	default:
+		break;
+	}
+
+	ie_size += mcs_nss_size + ppet_size;
+
+	*pos++ = WLAN_EID_EXTENSION;
+	*pos++ = 1 + ie_size;
+	*pos++ = WLAN_EID_EXT_HE_CAPABILITIES;
+
+	cap = (struct ieee80211_he_capabilities *) pos;
+	os_memset(cap, 0, sizeof(*cap));
+
+	os_memcpy(cap->he_mac_capab_info, mode->he_capab[opmode].mac_cap,
+		  HE_MAX_MAC_CAPAB_SIZE);
+	os_memcpy(cap->he_phy_capab_info, mode->he_capab[opmode].phy_cap,
+		  HE_MAX_PHY_CAPAB_SIZE);
+	os_memcpy(cap->optional, mode->he_capab[opmode].mcs, mcs_nss_size);
+	if (ppet_size)
+		os_memcpy(&cap->optional[mcs_nss_size],
+			  mode->he_capab[opmode].ppet,  ppet_size);
+
+	if (hapd->iface->conf->he_phy_capab.he_su_beamformer)
+		cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] |=
+			HE_PHYCAP_SU_BEAMFORMER_CAPAB;
+	else
+		cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] &=
+			~HE_PHYCAP_SU_BEAMFORMER_CAPAB;
+
+	if (hapd->iface->conf->he_phy_capab.he_su_beamformee)
+		cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX] |=
+			HE_PHYCAP_SU_BEAMFORMEE_CAPAB;
+	else
+		cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX] &=
+			~HE_PHYCAP_SU_BEAMFORMEE_CAPAB;
+
+	if (hapd->iface->conf->he_phy_capab.he_mu_beamformer)
+		cap->he_phy_capab_info[HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX] |=
+			HE_PHYCAP_MU_BEAMFORMER_CAPAB;
+	else
+		cap->he_phy_capab_info[HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX] &=
+			~HE_PHYCAP_MU_BEAMFORMER_CAPAB;
+
+	cap->he_phy_capab_info[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &=
+		he_oper_chwidth;
+
+	pos += ie_size;
+
+	return pos;
+}
+
+
+u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid)
+{
+	struct ieee80211_he_operation *oper;
+	u8 *pos = eid;
+	int oper_size = 6;
+	u32 params = 0;
+
+	if (!hapd->iface->current_mode)
+		return eid;
+
+	if (is_6ghz_op_class(hapd->iconf->op_class))
+		oper_size += 5;
+
+	*pos++ = WLAN_EID_EXTENSION;
+	*pos++ = 1 + oper_size;
+	*pos++ = WLAN_EID_EXT_HE_OPERATION;
+
+	oper = (struct ieee80211_he_operation *) pos;
+	os_memset(oper, 0, sizeof(*oper));
+
+	if (hapd->iface->conf->he_op.he_default_pe_duration)
+		params |= (hapd->iface->conf->he_op.he_default_pe_duration <<
+			   HE_OPERATION_DFLT_PE_DURATION_OFFSET);
+
+	if (hapd->iface->conf->he_op.he_twt_required)
+		params |= HE_OPERATION_TWT_REQUIRED;
+
+	if (hapd->iface->conf->he_op.he_rts_threshold)
+		params |= (hapd->iface->conf->he_op.he_rts_threshold <<
+			   HE_OPERATION_RTS_THRESHOLD_OFFSET);
+
+	if (hapd->iface->conf->he_op.he_er_su_disable)
+		params |= HE_OPERATION_ER_SU_DISABLE;
+
+	if (hapd->iface->conf->he_op.he_bss_color_disabled ||
+	    hapd->cca_in_progress)
+		params |= HE_OPERATION_BSS_COLOR_DISABLED;
+	if (hapd->iface->conf->he_op.he_bss_color_partial)
+		params |= HE_OPERATION_BSS_COLOR_PARTIAL;
+	params |= hapd->iface->conf->he_op.he_bss_color <<
+		HE_OPERATION_BSS_COLOR_OFFSET;
+
+	/* HE minimum required basic MCS and NSS for STAs */
+	oper->he_mcs_nss_set =
+		host_to_le16(hapd->iface->conf->he_op.he_basic_mcs_nss_set);
+
+	/* TODO: conditional MaxBSSID Indicator subfield */
+
+	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;
+
+		params |= HE_OPERATION_6GHZ_OPER_INFO;
+
+		/* 6 GHz Operation Information field
+		 * IEEE Std 802.11ax-2021, 9.4.2.249 HE Operation element,
+		 * Figure 9-788k
+		 */
+		*pos++ = hapd->iconf->channel; /* Primary Channel */
+
+		/* Control:
+		 *	bits 0-1: Channel Width
+		 *	bit 2: Duplicate Beacon
+		 *	bits 3-5: Regulatory Info
+		 */
+		/* Channel Width */
+		if (seg1)
+			control = 3;
+		else
+			control = center_idx_to_bw_6ghz(seg0);
+		if (hapd->iconf->he_6ghz_reg_pwr_type == 1)
+			control |= HE_6GHZ_STANDARD_POWER_AP <<
+				HE_6GHZ_OPER_INFO_CTRL_REG_INFO_SHIFT;
+		else
+			control |= HE_6GHZ_INDOOR_AP <<
+				HE_6GHZ_OPER_INFO_CTRL_REG_INFO_SHIFT;
+		*pos++ = control;
+
+		/* Channel Center Freq Seg0/Seg1 */
+		if (oper_chwidth == 2) {
+			/*
+			 * Seg 0 indicates the channel center frequency index of
+			 * the 160 MHz channel.
+			 */
+			seg1 = seg0;
+			if (hapd->iconf->channel < seg0)
+				seg0 -= 8;
+			else
+				seg0 += 8;
+		}
+
+		*pos++ = seg0;
+		*pos++ = seg1;
+		/* Minimum Rate */
+		*pos++ = 6; /* TODO: what should be set here? */
+	}
+
+	oper->he_oper_params = host_to_le32(params);
+
+	return pos;
+}
+
+
+u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid)
+{
+	struct ieee80211_he_mu_edca_parameter_set *edca;
+	u8 *pos;
+	size_t i;
+
+	pos = (u8 *) &hapd->iface->conf->he_mu_edca;
+	for (i = 0; i < sizeof(*edca); i++) {
+		if (pos[i])
+			break;
+	}
+	if (i == sizeof(*edca))
+		return eid; /* no MU EDCA Parameters configured */
+
+	pos = eid;
+	*pos++ = WLAN_EID_EXTENSION;
+	*pos++ = 1 + sizeof(*edca);
+	*pos++ = WLAN_EID_EXT_HE_MU_EDCA_PARAMS;
+
+	edca = (struct ieee80211_he_mu_edca_parameter_set *) pos;
+	os_memcpy(edca, &hapd->iface->conf->he_mu_edca, sizeof(*edca));
+
+	wpa_hexdump(MSG_DEBUG, "HE: MU EDCA Parameter Set element",
+		    pos, sizeof(*edca));
+
+	pos += sizeof(*edca);
+
+	return pos;
+}
+
+
+u8 * hostapd_eid_spatial_reuse(struct hostapd_data *hapd, u8 *eid)
+{
+	struct ieee80211_spatial_reuse *spr;
+	u8 *pos = eid, *spr_param;
+	u8 sz = 1;
+
+	if (!hapd->iface->conf->spr.sr_control)
+		return eid;
+
+	if (hapd->iface->conf->spr.sr_control &
+	    SPATIAL_REUSE_NON_SRG_OFFSET_PRESENT)
+		sz++;
+
+	if (hapd->iface->conf->spr.sr_control &
+	    SPATIAL_REUSE_SRG_INFORMATION_PRESENT)
+		sz += 18;
+
+	*pos++ = WLAN_EID_EXTENSION;
+	*pos++ = 1 + sz;
+	*pos++ = WLAN_EID_EXT_SPATIAL_REUSE;
+
+	spr = (struct ieee80211_spatial_reuse *) pos;
+	os_memset(spr, 0, sizeof(*spr));
+
+	spr->sr_ctrl = hapd->iface->conf->spr.sr_control;
+	pos++;
+	spr_param = spr->params;
+	if (spr->sr_ctrl & SPATIAL_REUSE_NON_SRG_OFFSET_PRESENT) {
+		*spr_param++ =
+			hapd->iface->conf->spr.non_srg_obss_pd_max_offset;
+		pos++;
+	}
+	if (spr->sr_ctrl & SPATIAL_REUSE_SRG_INFORMATION_PRESENT) {
+		*spr_param++ = hapd->iface->conf->spr.srg_obss_pd_min_offset;
+		*spr_param++ = hapd->iface->conf->spr.srg_obss_pd_max_offset;
+		os_memcpy(spr_param,
+			  hapd->iface->conf->spr.srg_bss_color_bitmap, 8);
+		spr_param += 8;
+		os_memcpy(spr_param,
+			  hapd->iface->conf->spr.srg_partial_bssid_bitmap, 8);
+		pos += 18;
+	}
+
+	return pos;
+}
+
+
+u8 * hostapd_eid_he_6ghz_band_cap(struct hostapd_data *hapd, u8 *eid)
+{
+	struct hostapd_config *conf = hapd->iface->conf;
+	struct hostapd_hw_modes *mode = hapd->iface->current_mode;
+	struct he_capabilities *he_cap;
+	struct ieee80211_he_6ghz_band_cap *cap;
+	u16 capab;
+	u8 *pos;
+
+	if (!mode || !is_6ghz_op_class(hapd->iconf->op_class) ||
+	    !is_6ghz_freq(hapd->iface->freq))
+		return eid;
+
+	he_cap = &mode->he_capab[IEEE80211_MODE_AP];
+	capab = he_cap->he_6ghz_capa & HE_6GHZ_BAND_CAP_MIN_MPDU_START;
+	capab |= (conf->he_6ghz_max_ampdu_len_exp <<
+		  HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_SHIFT) &
+		HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_MASK;
+	capab |= (conf->he_6ghz_max_mpdu <<
+		  HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_SHIFT) &
+		HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_MASK;
+	capab |= HE_6GHZ_BAND_CAP_SMPS_DISABLED;
+	if (conf->he_6ghz_rx_ant_pat)
+		capab |= HE_6GHZ_BAND_CAP_RX_ANTPAT_CONS;
+	if (conf->he_6ghz_tx_ant_pat)
+		capab |= HE_6GHZ_BAND_CAP_TX_ANTPAT_CONS;
+
+	pos = eid;
+	*pos++ = WLAN_EID_EXTENSION;
+	*pos++ = 1 + sizeof(*cap);
+	*pos++ = WLAN_EID_EXT_HE_6GHZ_BAND_CAP;
+
+	cap = (struct ieee80211_he_6ghz_band_cap *) pos;
+	cap->capab = host_to_le16(capab);
+	pos += sizeof(*cap);
+
+	return pos;
+}
+
+
+void hostapd_get_he_capab(struct hostapd_data *hapd,
+			  const struct ieee80211_he_capabilities *he_cap,
+			  struct ieee80211_he_capabilities *neg_he_cap,
+			  size_t he_capab_len)
+{
+	if (!he_cap)
+		return;
+
+	if (he_capab_len > sizeof(*neg_he_cap))
+		he_capab_len = sizeof(*neg_he_cap);
+	/* TODO: mask out unsupported features */
+
+	os_memcpy(neg_he_cap, he_cap, he_capab_len);
+}
+
+
+static int check_valid_he_mcs(struct hostapd_data *hapd, const u8 *sta_he_capab,
+			      enum ieee80211_op_mode opmode)
+{
+	u16 sta_rx_mcs_set, ap_tx_mcs_set;
+	u8 mcs_count = 0;
+	const u16 *ap_mcs_set, *sta_mcs_set;
+	int i;
+
+	if (!hapd->iface->current_mode)
+		return 1;
+	ap_mcs_set = (u16 *) hapd->iface->current_mode->he_capab[opmode].mcs;
+	sta_mcs_set = (u16 *) ((const struct ieee80211_he_capabilities *)
+			       sta_he_capab)->optional;
+
+	/*
+	 * Disable HE capabilities for STAs for which there is not even a single
+	 * allowed MCS in any supported number of streams, i.e., STA is
+	 * advertising 3 (not supported) as HE MCS rates for all supported
+	 * band/stream cases.
+	 */
+	switch (hapd->iface->conf->he_oper_chwidth) {
+	case CONF_OPER_CHWIDTH_80P80MHZ:
+		mcs_count = 3;
+		break;
+	case CONF_OPER_CHWIDTH_160MHZ:
+		mcs_count = 2;
+		break;
+	default:
+		mcs_count = 1;
+		break;
+	}
+
+	for (i = 0; i < mcs_count; i++) {
+		int j;
+
+		/* AP Tx MCS map vs. STA Rx MCS map */
+		sta_rx_mcs_set = WPA_GET_LE16((const u8 *) &sta_mcs_set[i * 2]);
+		ap_tx_mcs_set = WPA_GET_LE16((const u8 *)
+					     &ap_mcs_set[(i * 2) + 1]);
+
+		for (j = 0; j < HE_NSS_MAX_STREAMS; j++) {
+			if (((ap_tx_mcs_set >> (j * 2)) & 0x3) == 3)
+				continue;
+
+			if (((sta_rx_mcs_set >> (j * 2)) & 0x3) == 3)
+				continue;
+
+			return 1;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "No matching HE MCS found between AP TX and STA RX");
+
+	return 0;
+}
+
+
+u16 copy_sta_he_capab(struct hostapd_data *hapd, struct sta_info *sta,
+		      enum ieee80211_op_mode opmode, const u8 *he_capab,
+		      size_t he_capab_len)
+{
+	if (!he_capab || !(sta->flags & WLAN_STA_WMM) ||
+	    !hapd->iconf->ieee80211ax || hapd->conf->disable_11ax ||
+	    !check_valid_he_mcs(hapd, he_capab, opmode) ||
+	    ieee80211_invalid_he_cap_size(he_capab, he_capab_len) ||
+	    he_capab_len > sizeof(struct ieee80211_he_capabilities)) {
+		sta->flags &= ~WLAN_STA_HE;
+		os_free(sta->he_capab);
+		sta->he_capab = NULL;
+		return WLAN_STATUS_SUCCESS;
+	}
+
+	if (!sta->he_capab) {
+		sta->he_capab =
+			os_zalloc(sizeof(struct ieee80211_he_capabilities));
+		if (!sta->he_capab)
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	sta->flags |= WLAN_STA_HE;
+	os_memset(sta->he_capab, 0, sizeof(struct ieee80211_he_capabilities));
+	os_memcpy(sta->he_capab, he_capab, he_capab_len);
+	sta->he_capab_len = he_capab_len;
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+u16 copy_sta_he_6ghz_capab(struct hostapd_data *hapd, struct sta_info *sta,
+			   const u8 *he_6ghz_capab)
+{
+	if (!he_6ghz_capab || !hapd->iconf->ieee80211ax ||
+	    hapd->conf->disable_11ax ||
+	    !is_6ghz_op_class(hapd->iconf->op_class)) {
+		sta->flags &= ~WLAN_STA_6GHZ;
+		os_free(sta->he_6ghz_capab);
+		sta->he_6ghz_capab = NULL;
+		return WLAN_STATUS_SUCCESS;
+	}
+
+	if (!sta->he_6ghz_capab) {
+		sta->he_6ghz_capab =
+			os_zalloc(sizeof(struct ieee80211_he_6ghz_band_cap));
+		if (!sta->he_6ghz_capab)
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	sta->flags |= WLAN_STA_6GHZ;
+	os_memcpy(sta->he_6ghz_capab, he_6ghz_capab,
+		  sizeof(struct ieee80211_he_6ghz_band_cap));
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+int hostapd_get_he_twt_responder(struct hostapd_data *hapd,
+				 enum ieee80211_op_mode mode)
+{
+	u8 *mac_cap;
+
+	if (!hapd->iface->current_mode ||
+	    !hapd->iface->current_mode->he_capab[mode].he_supported ||
+	    !hapd->iconf->ieee80211ax || hapd->conf->disable_11ax)
+		return 0;
+
+	mac_cap = hapd->iface->current_mode->he_capab[mode].mac_cap;
+
+	return !!(mac_cap[HE_MAC_CAPAB_0] & HE_MACCAP_TWT_RESPONDER) &&
+		hapd->iface->conf->he_op.he_twt_responder;
+}
+
+
+u8 * hostapd_eid_cca(struct hostapd_data *hapd, u8 *eid)
+{
+	if (!hapd->cca_in_progress)
+		return eid;
+
+	/* BSS Color Change Announcement element */
+	*eid++ = WLAN_EID_EXTENSION;
+	*eid++ = 3;
+	*eid++ = WLAN_EID_EXT_COLOR_CHANGE_ANNOUNCEMENT;
+	*eid++ = hapd->cca_count; /* Color Switch Countdown */
+	*eid++ = hapd->cca_color; /* New BSS Color Information */
+
+	return eid;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11_ht.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11_ht.c
new file mode 100644
index 0000000..0b166d9
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11_ht.c
@@ -0,0 +1,551 @@
+/*
+ * hostapd / IEEE 802.11n HT
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007-2008, Intel Corporation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "sta_info.h"
+#include "beacon.h"
+#include "ieee802_11.h"
+#include "hw_features.h"
+#include "ap_drv_ops.h"
+
+
+u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid)
+{
+	struct ieee80211_ht_capabilities *cap;
+	u8 *pos = eid;
+
+	if (!hapd->iconf->ieee80211n || !hapd->iface->current_mode ||
+	    hapd->conf->disable_11n || is_6ghz_op_class(hapd->iconf->op_class))
+		return eid;
+
+	*pos++ = WLAN_EID_HT_CAP;
+	*pos++ = sizeof(*cap);
+
+	cap = (struct ieee80211_ht_capabilities *) pos;
+	os_memset(cap, 0, sizeof(*cap));
+	cap->ht_capabilities_info = host_to_le16(hapd->iconf->ht_capab);
+	cap->a_mpdu_params = hapd->iface->current_mode->a_mpdu_params;
+	os_memcpy(cap->supported_mcs_set, hapd->iface->current_mode->mcs_set,
+		  16);
+
+	/* TODO: ht_extended_capabilities (now fully disabled) */
+	/* TODO: tx_bf_capability_info (now fully disabled) */
+	/* TODO: asel_capabilities (now fully disabled) */
+
+ 	pos += sizeof(*cap);
+
+	if (hapd->iconf->obss_interval) {
+		struct ieee80211_obss_scan_parameters *scan_params;
+
+		*pos++ = WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS;
+		*pos++ = sizeof(*scan_params);
+
+		scan_params = (struct ieee80211_obss_scan_parameters *) pos;
+		os_memset(scan_params, 0, sizeof(*scan_params));
+		scan_params->width_trigger_scan_interval =
+			host_to_le16(hapd->iconf->obss_interval);
+
+		/* Fill in default values for remaining parameters
+		 * (IEEE Std 802.11-2012, 8.4.2.61 and MIB defval) */
+		scan_params->scan_passive_dwell =
+			host_to_le16(20);
+		scan_params->scan_active_dwell =
+			host_to_le16(10);
+		scan_params->scan_passive_total_per_channel =
+			host_to_le16(200);
+		scan_params->scan_active_total_per_channel =
+			host_to_le16(20);
+		scan_params->channel_transition_delay_factor =
+			host_to_le16(5);
+		scan_params->scan_activity_threshold =
+			host_to_le16(25);
+
+		pos += sizeof(*scan_params);
+	}
+
+	return pos;
+}
+
+
+u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid)
+{
+	struct ieee80211_ht_operation *oper;
+	le32 vht_capabilities_info;
+	u8 *pos = eid;
+	u8 chwidth;
+
+	if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n ||
+	    is_6ghz_op_class(hapd->iconf->op_class))
+		return eid;
+
+	*pos++ = WLAN_EID_HT_OPERATION;
+	*pos++ = sizeof(*oper);
+
+	oper = (struct ieee80211_ht_operation *) pos;
+	os_memset(oper, 0, sizeof(*oper));
+
+	oper->primary_chan = hapd->iconf->channel;
+	oper->operation_mode = host_to_le16(hapd->iface->ht_op_mode);
+	if (hapd->iconf->secondary_channel == 1)
+		oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE |
+			HT_INFO_HT_PARAM_STA_CHNL_WIDTH;
+	if (hapd->iconf->secondary_channel == -1)
+		oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW |
+			HT_INFO_HT_PARAM_STA_CHNL_WIDTH;
+
+	vht_capabilities_info = host_to_le32(hapd->iface->current_mode->vht_capab);
+	chwidth = hostapd_get_oper_chwidth(hapd->iconf);
+	if (vht_capabilities_info & VHT_CAP_EXTENDED_NSS_BW_SUPPORT
+		&& ((chwidth == CHANWIDTH_160MHZ) || (chwidth == CHANWIDTH_80P80MHZ))) {
+		oper->operation_mode = host_to_le16(hapd->iconf->vht_oper_centr_freq_seg0_idx << 5);
+	}
+
+	pos += sizeof(*oper);
+
+	return pos;
+}
+
+
+/*
+op_mode
+Set to 0 (HT pure) under the following conditions
+	- all STAs in the BSS are 20/40 MHz HT in 20/40 MHz BSS or
+	- all STAs in the BSS are 20 MHz HT in 20 MHz BSS
+Set to 1 (HT non-member protection) if there may be non-HT STAs
+	in both the primary and the secondary channel
+Set to 2 if only HT STAs are associated in BSS,
+	however and at least one 20 MHz HT STA is associated
+Set to 3 (HT mixed mode) when one or more non-HT STAs are associated
+*/
+int hostapd_ht_operation_update(struct hostapd_iface *iface)
+{
+	u16 cur_op_mode, new_op_mode;
+	int op_mode_changes = 0;
+
+	if (!iface->conf->ieee80211n || iface->conf->ht_op_mode_fixed)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "%s current operation mode=0x%X",
+		   __func__, iface->ht_op_mode);
+
+	if (!(iface->ht_op_mode & HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT)
+	    && iface->num_sta_ht_no_gf) {
+		iface->ht_op_mode |= HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT;
+		op_mode_changes++;
+	} else if ((iface->ht_op_mode &
+		    HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT) &&
+		   iface->num_sta_ht_no_gf == 0) {
+		iface->ht_op_mode &= ~HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT;
+		op_mode_changes++;
+	}
+
+	if (!(iface->ht_op_mode & HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT) &&
+	    (iface->num_sta_no_ht || iface->olbc_ht)) {
+		iface->ht_op_mode |= HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT;
+		op_mode_changes++;
+	} else if ((iface->ht_op_mode &
+		    HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT) &&
+		   (iface->num_sta_no_ht == 0 && !iface->olbc_ht)) {
+		iface->ht_op_mode &= ~HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT;
+		op_mode_changes++;
+	}
+
+	if (iface->num_sta_no_ht)
+		new_op_mode = HT_PROT_NON_HT_MIXED;
+	else if (iface->conf->secondary_channel && iface->num_sta_ht_20mhz)
+		new_op_mode = HT_PROT_20MHZ_PROTECTION;
+	else if (iface->olbc_ht)
+		new_op_mode = HT_PROT_NONMEMBER_PROTECTION;
+	else
+		new_op_mode = HT_PROT_NO_PROTECTION;
+
+	cur_op_mode = iface->ht_op_mode & HT_OPER_OP_MODE_HT_PROT_MASK;
+	if (cur_op_mode != new_op_mode) {
+		iface->ht_op_mode &= ~HT_OPER_OP_MODE_HT_PROT_MASK;
+		iface->ht_op_mode |= new_op_mode;
+		op_mode_changes++;
+	}
+
+	wpa_printf(MSG_DEBUG, "%s new operation mode=0x%X changes=%d",
+		   __func__, iface->ht_op_mode, op_mode_changes);
+
+	return op_mode_changes;
+}
+
+
+static int is_40_allowed(struct hostapd_iface *iface, int channel)
+{
+	int pri_freq, sec_freq;
+	int affected_start, affected_end;
+	int pri = 2407 + 5 * channel;
+
+	if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
+		return 1;
+
+	pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel);
+
+	if (iface->conf->secondary_channel > 0)
+		sec_freq = pri_freq + 20;
+	else
+		sec_freq = pri_freq - 20;
+
+	affected_start = (pri_freq + sec_freq) / 2 - 25;
+	affected_end = (pri_freq + sec_freq) / 2 + 25;
+	if ((pri < affected_start || pri > affected_end))
+		return 1; /* not within affected channel range */
+
+	wpa_printf(MSG_ERROR, "40 MHz affected channel range: [%d,%d] MHz",
+		   affected_start, affected_end);
+	wpa_printf(MSG_ERROR, "Neighboring BSS: freq=%d", pri);
+	return 0;
+}
+
+
+void hostapd_2040_coex_action(struct hostapd_data *hapd,
+			      const struct ieee80211_mgmt *mgmt, size_t len)
+{
+	struct hostapd_iface *iface = hapd->iface;
+	struct ieee80211_2040_bss_coex_ie *bc_ie;
+	struct ieee80211_2040_intol_chan_report *ic_report;
+	int is_ht40_allowed = 1;
+	int i;
+	const u8 *start = (const u8 *) mgmt;
+	const u8 *data = start + IEEE80211_HDRLEN + 2;
+	struct sta_info *sta;
+
+	wpa_printf(MSG_DEBUG,
+		   "HT: Received 20/40 BSS Coexistence Management frame from "
+		   MACSTR, MAC2STR(mgmt->sa));
+
+	hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_DEBUG, "hostapd_public_action - action=%d",
+		       mgmt->u.action.u.public_action.action);
+
+	if (!(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) {
+		wpa_printf(MSG_DEBUG,
+			   "Ignore 20/40 BSS Coexistence Management frame since 40 MHz capability is not enabled");
+		return;
+	}
+
+	if (iface->conf->noscan || iface->conf->no_ht_coex)
+		return;
+
+	if (len < IEEE80211_HDRLEN + 2 + sizeof(*bc_ie)) {
+		wpa_printf(MSG_DEBUG,
+			   "Ignore too short 20/40 BSS Coexistence Management frame");
+		return;
+	}
+
+	/* 20/40 BSS Coexistence element */
+	bc_ie = (struct ieee80211_2040_bss_coex_ie *) data;
+	if (bc_ie->element_id != WLAN_EID_20_40_BSS_COEXISTENCE ||
+	    bc_ie->length < 1) {
+		wpa_printf(MSG_DEBUG, "Unexpected IE (%u,%u) in coex report",
+			   bc_ie->element_id, bc_ie->length);
+		return;
+	}
+	if (len < IEEE80211_HDRLEN + 2 + 2 + bc_ie->length) {
+		wpa_printf(MSG_DEBUG,
+			   "Truncated 20/40 BSS Coexistence element");
+		return;
+	}
+	data += 2 + bc_ie->length;
+
+	wpa_printf(MSG_DEBUG,
+		   "20/40 BSS Coexistence Information field: 0x%x (%s%s%s%s%s%s)",
+		   bc_ie->coex_param,
+		   (bc_ie->coex_param & BIT(0)) ? "[InfoReq]" : "",
+		   (bc_ie->coex_param & BIT(1)) ? "[40MHzIntolerant]" : "",
+		   (bc_ie->coex_param & BIT(2)) ? "[20MHzBSSWidthReq]" : "",
+		   (bc_ie->coex_param & BIT(3)) ? "[OBSSScanExemptionReq]" : "",
+		   (bc_ie->coex_param & BIT(4)) ?
+		   "[OBSSScanExemptionGrant]" : "",
+		   (bc_ie->coex_param & (BIT(5) | BIT(6) | BIT(7))) ?
+		   "[Reserved]" : "");
+
+	if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ) {
+		/* Intra-BSS communication prohibiting 20/40 MHz BSS operation
+		 */
+		sta = ap_get_sta(hapd, mgmt->sa);
+		if (!sta || !(sta->flags & WLAN_STA_ASSOC)) {
+			wpa_printf(MSG_DEBUG,
+				   "Ignore intra-BSS 20/40 BSS Coexistence Management frame from not-associated STA");
+			return;
+		}
+
+		hostapd_logger(hapd, mgmt->sa,
+			       HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "20 MHz BSS width request bit is set in BSS coexistence information field");
+		is_ht40_allowed = 0;
+	}
+
+	if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_40MHZ_INTOL) {
+		/* Inter-BSS communication prohibiting 20/40 MHz BSS operation
+		 */
+		hostapd_logger(hapd, mgmt->sa,
+			       HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "40 MHz intolerant bit is set in BSS coexistence information field");
+		is_ht40_allowed = 0;
+	}
+
+	/* 20/40 BSS Intolerant Channel Report element (zero or more times) */
+	while (start + len - data >= 3 &&
+	       data[0] == WLAN_EID_20_40_BSS_INTOLERANT && data[1] >= 1) {
+		u8 ielen = data[1];
+
+		if (ielen > start + len - data - 2) {
+			wpa_printf(MSG_DEBUG,
+				   "Truncated 20/40 BSS Intolerant Channel Report element");
+			return;
+		}
+		ic_report = (struct ieee80211_2040_intol_chan_report *) data;
+		wpa_printf(MSG_DEBUG,
+			   "20/40 BSS Intolerant Channel Report: Operating Class %u",
+			   ic_report->op_class);
+
+		/* Go through the channel report to find any BSS there in the
+		 * affected channel range */
+		for (i = 0; i < ielen - 1; i++) {
+			u8 chan = ic_report->variable[i];
+
+			if (chan == iface->conf->channel)
+				continue; /* matching own primary channel */
+			if (is_40_allowed(iface, chan))
+				continue; /* not within affected channels */
+			hostapd_logger(hapd, mgmt->sa,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "20_40_INTOLERANT channel %d reported",
+				       chan);
+			is_ht40_allowed = 0;
+		}
+
+		data += 2 + ielen;
+	}
+	wpa_printf(MSG_DEBUG, "is_ht40_allowed=%d num_sta_ht40_intolerant=%d",
+		   is_ht40_allowed, iface->num_sta_ht40_intolerant);
+
+	if (!is_ht40_allowed &&
+	    (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) {
+		if (iface->conf->secondary_channel) {
+			hostapd_logger(hapd, mgmt->sa,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_INFO,
+				       "Switching to 20 MHz operation");
+			iface->conf->secondary_channel = 0;
+			ieee802_11_set_beacons(iface);
+		}
+		if (!iface->num_sta_ht40_intolerant &&
+		    iface->conf->obss_interval) {
+			unsigned int delay_time;
+			delay_time = OVERLAPPING_BSS_TRANS_DELAY_FACTOR *
+				iface->conf->obss_interval;
+			eloop_cancel_timeout(ap_ht2040_timeout, hapd->iface,
+					     NULL);
+			eloop_register_timeout(delay_time, 0, ap_ht2040_timeout,
+					       hapd->iface, NULL);
+			wpa_printf(MSG_DEBUG,
+				   "Reschedule HT 20/40 timeout to occur in %u seconds",
+				   delay_time);
+		}
+	}
+}
+
+
+u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+		      const u8 *ht_capab)
+{
+	/*
+	 * Disable HT caps for STAs associated to no-HT BSSes, or for stations
+	 * that did not specify a valid WMM IE in the (Re)Association Request
+	 * frame.
+	 */
+	if (!ht_capab || !(sta->flags & WLAN_STA_WMM) ||
+	    !hapd->iconf->ieee80211n || hapd->conf->disable_11n) {
+		sta->flags &= ~WLAN_STA_HT;
+		os_free(sta->ht_capabilities);
+		sta->ht_capabilities = NULL;
+		return WLAN_STATUS_SUCCESS;
+	}
+
+	if (sta->ht_capabilities == NULL) {
+		sta->ht_capabilities =
+			os_zalloc(sizeof(struct ieee80211_ht_capabilities));
+		if (sta->ht_capabilities == NULL)
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	sta->flags |= WLAN_STA_HT;
+	os_memcpy(sta->ht_capabilities, ht_capab,
+		  sizeof(struct ieee80211_ht_capabilities));
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta)
+{
+	if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
+		return;
+
+	if (iface->conf->noscan || iface->conf->no_ht_coex)
+		return;
+
+	wpa_printf(MSG_INFO, "HT: Forty MHz Intolerant is set by STA " MACSTR
+		   " in Association Request", MAC2STR(sta->addr));
+
+	if (sta->ht40_intolerant_set)
+		return;
+
+	sta->ht40_intolerant_set = 1;
+	iface->num_sta_ht40_intolerant++;
+	eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL);
+
+	if (iface->conf->secondary_channel &&
+	    (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) {
+		iface->conf->secondary_channel = 0;
+		ieee802_11_set_beacons(iface);
+	}
+}
+
+
+void ht40_intolerant_remove(struct hostapd_iface *iface, struct sta_info *sta)
+{
+	if (!sta->ht40_intolerant_set)
+		return;
+
+	sta->ht40_intolerant_set = 0;
+	iface->num_sta_ht40_intolerant--;
+
+	if (iface->num_sta_ht40_intolerant == 0 &&
+	    (iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
+	    (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) {
+		unsigned int delay_time = OVERLAPPING_BSS_TRANS_DELAY_FACTOR *
+			iface->conf->obss_interval;
+		wpa_printf(MSG_DEBUG,
+			   "HT: Start 20->40 MHz transition timer (%d seconds)",
+			   delay_time);
+		eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL);
+		eloop_register_timeout(delay_time, 0, ap_ht2040_timeout,
+				       iface, NULL);
+	}
+}
+
+
+static void update_sta_ht(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	u16 ht_capab;
+
+	ht_capab = le_to_host16(sta->ht_capabilities->ht_capabilities_info);
+	wpa_printf(MSG_DEBUG, "HT: STA " MACSTR " HT Capabilities Info: "
+		   "0x%04x", MAC2STR(sta->addr), ht_capab);
+	if ((ht_capab & HT_CAP_INFO_GREEN_FIELD) == 0) {
+		if (!sta->no_ht_gf_set) {
+			sta->no_ht_gf_set = 1;
+			hapd->iface->num_sta_ht_no_gf++;
+		}
+		wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - no greenfield, num "
+			   "of non-gf stations %d",
+			   __func__, MAC2STR(sta->addr),
+			   hapd->iface->num_sta_ht_no_gf);
+	}
+	if ((ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) == 0) {
+		if (!sta->ht_20mhz_set) {
+			sta->ht_20mhz_set = 1;
+			hapd->iface->num_sta_ht_20mhz++;
+		}
+		wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - 20 MHz HT, num of "
+			   "20MHz HT STAs %d",
+			   __func__, MAC2STR(sta->addr),
+			   hapd->iface->num_sta_ht_20mhz);
+	}
+#ifndef CONFIG_24G_BW_SWITCH
+	if (ht_capab & HT_CAP_INFO_40MHZ_INTOLERANT)
+		ht40_intolerant_add(hapd->iface, sta);
+#endif /* CONFIG_24G_BW_SWITCH */
+}
+
+
+static void update_sta_no_ht(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	if (!sta->no_ht_set) {
+		sta->no_ht_set = 1;
+		hapd->iface->num_sta_no_ht++;
+	}
+	if (hapd->iconf->ieee80211n) {
+		wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - no HT, num of "
+			   "non-HT stations %d",
+			   __func__, MAC2STR(sta->addr),
+			   hapd->iface->num_sta_no_ht);
+	}
+}
+
+
+void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	if ((sta->flags & WLAN_STA_HT) && sta->ht_capabilities)
+		update_sta_ht(hapd, sta);
+	else
+		update_sta_no_ht(hapd, sta);
+
+	if (hostapd_ht_operation_update(hapd->iface) > 0)
+		ieee802_11_set_beacons(hapd->iface);
+}
+
+
+void hostapd_get_ht_capab(struct hostapd_data *hapd,
+			  struct ieee80211_ht_capabilities *ht_cap,
+			  struct ieee80211_ht_capabilities *neg_ht_cap)
+{
+	u16 cap;
+
+	if (ht_cap == NULL)
+		return;
+	os_memcpy(neg_ht_cap, ht_cap, sizeof(*neg_ht_cap));
+	cap = le_to_host16(neg_ht_cap->ht_capabilities_info);
+
+	/*
+	 * Mask out HT features we don't support, but don't overwrite
+	 * non-symmetric features like STBC and SMPS. Just because
+	 * we're not in dynamic SMPS mode the STA might still be.
+	 */
+	cap &= (hapd->iconf->ht_capab | HT_CAP_INFO_RX_STBC_MASK |
+		HT_CAP_INFO_TX_STBC | HT_CAP_INFO_SMPS_MASK);
+
+	/*
+	 * STBC needs to be handled specially
+	 * if we don't support RX STBC, mask out TX STBC in the STA's HT caps
+	 * if we don't support TX STBC, mask out RX STBC in the STA's HT caps
+	 */
+	if (!(hapd->iconf->ht_capab & HT_CAP_INFO_RX_STBC_MASK))
+		cap &= ~HT_CAP_INFO_TX_STBC;
+	if (!(hapd->iconf->ht_capab & HT_CAP_INFO_TX_STBC))
+		cap &= ~HT_CAP_INFO_RX_STBC_MASK;
+
+	neg_ht_cap->ht_capabilities_info = host_to_le16(cap);
+}
+
+
+void ap_ht2040_timeout(void *eloop_data, void *user_data)
+{
+	struct hostapd_iface *iface = eloop_data;
+
+	wpa_printf(MSG_INFO, "Switching to 40 MHz operation");
+
+	iface->conf->secondary_channel = iface->secondary_ch;
+	ieee802_11_set_beacons(iface);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11_shared.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11_shared.c
new file mode 100644
index 0000000..0f61e58
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11_shared.c
@@ -0,0 +1,1137 @@
+/*
+ * hostapd / IEEE 802.11 Management
+ * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ocv.h"
+#include "common/wpa_ctrl.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "wpa_auth.h"
+#include "dpp_hostapd.h"
+#include "ieee802_11.h"
+
+
+u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
+				     struct sta_info *sta, u8 *eid)
+{
+	u8 *pos = eid;
+	u32 timeout, tu;
+	struct os_reltime now, passed;
+
+	*pos++ = WLAN_EID_TIMEOUT_INTERVAL;
+	*pos++ = 5;
+	*pos++ = WLAN_TIMEOUT_ASSOC_COMEBACK;
+	os_get_reltime(&now);
+	os_reltime_sub(&now, &sta->sa_query_start, &passed);
+	tu = (passed.sec * 1000000 + passed.usec) / 1024;
+	if (hapd->conf->assoc_sa_query_max_timeout > tu)
+		timeout = hapd->conf->assoc_sa_query_max_timeout - tu;
+	else
+		timeout = 0;
+	if (timeout < hapd->conf->assoc_sa_query_max_timeout)
+		timeout++; /* add some extra time for local timers */
+	WPA_PUT_LE32(pos, timeout);
+	pos += 4;
+
+	return pos;
+}
+
+
+/* MLME-SAQuery.request */
+void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
+				  const u8 *addr, const u8 *trans_id)
+{
+#ifdef CONFIG_OCV
+	struct sta_info *sta;
+#endif /* CONFIG_OCV */
+	struct ieee80211_mgmt *mgmt;
+	u8 *oci_ie = NULL;
+	u8 oci_ie_len = 0;
+	u8 *end;
+
+	wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Request to "
+		   MACSTR, MAC2STR(addr));
+	wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
+		    trans_id, WLAN_SA_QUERY_TR_ID_LEN);
+
+#ifdef CONFIG_OCV
+	sta = ap_get_sta(hapd, addr);
+	if (sta && wpa_auth_uses_ocv(sta->wpa_sm)) {
+		struct wpa_channel_info ci;
+
+		if (hostapd_drv_channel_info(hapd, &ci) != 0) {
+			wpa_printf(MSG_WARNING,
+				   "Failed to get channel info for OCI element in SA Query Request");
+			return;
+		}
+#ifdef CONFIG_TESTING_OPTIONS
+		if (hapd->conf->oci_freq_override_saquery_req) {
+			wpa_printf(MSG_INFO,
+				   "TEST: Override OCI frequency %d -> %u MHz",
+				   ci.frequency,
+				   hapd->conf->oci_freq_override_saquery_req);
+			ci.frequency =
+				hapd->conf->oci_freq_override_saquery_req;
+		}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+		oci_ie_len = OCV_OCI_EXTENDED_LEN;
+		oci_ie = os_zalloc(oci_ie_len);
+		if (!oci_ie) {
+			wpa_printf(MSG_WARNING,
+				   "Failed to allocate buffer for OCI element in SA Query Request");
+			return;
+		}
+
+		if (ocv_insert_extended_oci(&ci, oci_ie) < 0) {
+			os_free(oci_ie);
+			return;
+		}
+	}
+#endif /* CONFIG_OCV */
+
+	mgmt = os_zalloc(sizeof(*mgmt) + oci_ie_len);
+	if (!mgmt) {
+		wpa_printf(MSG_DEBUG,
+			   "Failed to allocate buffer for SA Query Response frame");
+		os_free(oci_ie);
+		return;
+	}
+
+	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					   WLAN_FC_STYPE_ACTION);
+	os_memcpy(mgmt->da, addr, ETH_ALEN);
+	os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+	os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+	mgmt->u.action.category = WLAN_ACTION_SA_QUERY;
+	mgmt->u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST;
+	os_memcpy(mgmt->u.action.u.sa_query_req.trans_id, trans_id,
+		  WLAN_SA_QUERY_TR_ID_LEN);
+	end = mgmt->u.action.u.sa_query_req.variable;
+#ifdef CONFIG_OCV
+	if (oci_ie_len > 0) {
+		os_memcpy(end, oci_ie, oci_ie_len);
+		end += oci_ie_len;
+	}
+#endif /* CONFIG_OCV */
+	if (hostapd_drv_send_mlme(hapd, mgmt, end - (u8 *) mgmt, 0, NULL, 0, 0)
+	    < 0)
+		wpa_printf(MSG_INFO, "ieee802_11_send_sa_query_req: send failed");
+
+	os_free(mgmt);
+	os_free(oci_ie);
+}
+
+
+static void ieee802_11_send_sa_query_resp(struct hostapd_data *hapd,
+					  const u8 *sa, const u8 *trans_id)
+{
+	struct sta_info *sta;
+	struct ieee80211_mgmt *resp;
+	u8 *oci_ie = NULL;
+	u8 oci_ie_len = 0;
+	u8 *end;
+
+	wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Request from "
+		   MACSTR, MAC2STR(sa));
+	wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
+		    trans_id, WLAN_SA_QUERY_TR_ID_LEN);
+
+	sta = ap_get_sta(hapd, sa);
+	if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) {
+		wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignore SA Query Request "
+			   "from unassociated STA " MACSTR, MAC2STR(sa));
+		return;
+	}
+
+#ifdef CONFIG_OCV
+	if (wpa_auth_uses_ocv(sta->wpa_sm)) {
+		struct wpa_channel_info ci;
+
+		if (hostapd_drv_channel_info(hapd, &ci) != 0) {
+			wpa_printf(MSG_WARNING,
+				   "Failed to get channel info for OCI element in SA Query Response");
+			return;
+		}
+#ifdef CONFIG_TESTING_OPTIONS
+		if (hapd->conf->oci_freq_override_saquery_resp) {
+			wpa_printf(MSG_INFO,
+				   "TEST: Override OCI frequency %d -> %u MHz",
+				   ci.frequency,
+				   hapd->conf->oci_freq_override_saquery_resp);
+			ci.frequency =
+				hapd->conf->oci_freq_override_saquery_resp;
+		}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+		oci_ie_len = OCV_OCI_EXTENDED_LEN;
+		oci_ie = os_zalloc(oci_ie_len);
+		if (!oci_ie) {
+			wpa_printf(MSG_WARNING,
+				   "Failed to allocate buffer for for OCI element in SA Query Response");
+			return;
+		}
+
+		if (ocv_insert_extended_oci(&ci, oci_ie) < 0) {
+			os_free(oci_ie);
+			return;
+		}
+	}
+#endif /* CONFIG_OCV */
+
+	resp = os_zalloc(sizeof(*resp) + oci_ie_len);
+	if (!resp) {
+		wpa_printf(MSG_DEBUG,
+			   "Failed to allocate buffer for SA Query Response frame");
+		os_free(oci_ie);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Response to "
+		   MACSTR, MAC2STR(sa));
+
+	resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					   WLAN_FC_STYPE_ACTION);
+	os_memcpy(resp->da, sa, ETH_ALEN);
+	os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
+	os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
+	resp->u.action.category = WLAN_ACTION_SA_QUERY;
+	resp->u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE;
+	os_memcpy(resp->u.action.u.sa_query_req.trans_id, trans_id,
+		  WLAN_SA_QUERY_TR_ID_LEN);
+	end = resp->u.action.u.sa_query_req.variable;
+#ifdef CONFIG_OCV
+	if (oci_ie_len > 0) {
+		os_memcpy(end, oci_ie, oci_ie_len);
+		end += oci_ie_len;
+	}
+#endif /* CONFIG_OCV */
+	if (hostapd_drv_send_mlme(hapd, resp, end - (u8 *) resp, 0, NULL, 0, 0)
+	    < 0)
+		wpa_printf(MSG_INFO, "ieee80211_mgmt_sa_query_request: send failed");
+
+	os_free(resp);
+	os_free(oci_ie);
+}
+
+
+void ieee802_11_sa_query_action(struct hostapd_data *hapd,
+				const struct ieee80211_mgmt *mgmt,
+				size_t len)
+{
+	struct sta_info *sta;
+	int i;
+	const u8 *sa = mgmt->sa;
+	const u8 action_type = mgmt->u.action.u.sa_query_resp.action;
+	const u8 *trans_id = mgmt->u.action.u.sa_query_resp.trans_id;
+
+	if (((const u8 *) mgmt) + len <
+	    mgmt->u.action.u.sa_query_resp.variable) {
+		wpa_printf(MSG_DEBUG,
+			   "IEEE 802.11: Too short SA Query Action frame (len=%lu)",
+			   (unsigned long) len);
+		return;
+	}
+	if (is_multicast_ether_addr(mgmt->da)) {
+		wpa_printf(MSG_DEBUG,
+			   "IEEE 802.11: Ignore group-addressed SA Query frame (A1=" MACSTR " A2=" MACSTR ")",
+			   MAC2STR(mgmt->da), MAC2STR(mgmt->sa));
+		return;
+	}
+
+	sta = ap_get_sta(hapd, sa);
+
+#ifdef CONFIG_OCV
+	if (sta && wpa_auth_uses_ocv(sta->wpa_sm)) {
+		struct ieee802_11_elems elems;
+		struct wpa_channel_info ci;
+		int tx_chanwidth;
+		int tx_seg1_idx;
+		size_t ies_len;
+		const u8 *ies;
+
+		ies = mgmt->u.action.u.sa_query_resp.variable;
+		ies_len = len - (ies - (u8 *) mgmt);
+		if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) ==
+		    ParseFailed) {
+			wpa_printf(MSG_DEBUG,
+				   "SA Query: Failed to parse elements");
+			return;
+		}
+
+		if (hostapd_drv_channel_info(hapd, &ci) != 0) {
+			wpa_printf(MSG_WARNING,
+				   "Failed to get channel info to validate received OCI in SA Query Action frame");
+			return;
+		}
+
+		if (get_sta_tx_parameters(sta->wpa_sm,
+					  channel_width_to_int(ci.chanwidth),
+					  ci.seg1_idx, &tx_chanwidth,
+					  &tx_seg1_idx) < 0)
+			return;
+
+		if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci,
+					 tx_chanwidth, tx_seg1_idx) !=
+		    OCI_SUCCESS) {
+			wpa_msg(hapd->msg_ctx, MSG_INFO, OCV_FAILURE "addr="
+				MACSTR " frame=saquery%s error=%s",
+				MAC2STR(sa),
+				action_type == WLAN_SA_QUERY_REQUEST ?
+				"req" : "resp", ocv_errorstr);
+			return;
+		}
+	}
+#endif /* CONFIG_OCV */
+
+	if (action_type == WLAN_SA_QUERY_REQUEST) {
+		if (sta)
+			sta->post_csa_sa_query = 0;
+		ieee802_11_send_sa_query_resp(hapd, sa, trans_id);
+		return;
+	}
+
+	if (action_type != WLAN_SA_QUERY_RESPONSE) {
+		wpa_printf(MSG_DEBUG, "IEEE 802.11: Unexpected SA Query "
+			   "Action %d", action_type);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Response from "
+		   MACSTR, MAC2STR(sa));
+	wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
+		    trans_id, WLAN_SA_QUERY_TR_ID_LEN);
+
+	/* MLME-SAQuery.confirm */
+
+	if (sta == NULL || sta->sa_query_trans_id == NULL) {
+		wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching STA with "
+			   "pending SA Query request found");
+		return;
+	}
+
+	for (i = 0; i < sta->sa_query_count; i++) {
+		if (os_memcmp(sta->sa_query_trans_id +
+			      i * WLAN_SA_QUERY_TR_ID_LEN,
+			      trans_id, WLAN_SA_QUERY_TR_ID_LEN) == 0)
+			break;
+	}
+
+	if (i >= sta->sa_query_count) {
+		wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching SA Query "
+			   "transaction identifier found");
+		return;
+	}
+
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "Reply to pending SA Query received");
+	ap_sta_stop_sa_query(hapd, sta);
+}
+
+
+static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx,
+				   bool mbssid_complete)
+{
+	*pos = 0x00;
+
+	switch (idx) {
+	case 0: /* Bits 0-7 */
+		if (hapd->iconf->obss_interval)
+			*pos |= 0x01; /* Bit 0 - Coexistence management */
+		if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA)
+			*pos |= 0x04; /* Bit 2 - Extended Channel Switching */
+		break;
+	case 1: /* Bits 8-15 */
+		if (hapd->conf->proxy_arp)
+			*pos |= 0x10; /* Bit 12 - Proxy ARP */
+		if (hapd->conf->coloc_intf_reporting) {
+			/* Bit 13 - Collocated Interference Reporting */
+			*pos |= 0x20;
+		}
+		break;
+	case 2: /* Bits 16-23 */
+		if (hapd->conf->wnm_sleep_mode)
+			*pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */
+		if (hapd->conf->bss_transition)
+			*pos |= 0x08; /* Bit 19 - BSS Transition */
+		if (hapd->iconf->mbssid)
+			*pos |= 0x40; /* Bit 22 - Multiple BSSID */
+		break;
+	case 3: /* Bits 24-31 */
+#ifdef CONFIG_WNM_AP
+		*pos |= 0x02; /* Bit 25 - SSID List */
+#endif /* CONFIG_WNM_AP */
+		if (hapd->conf->time_advertisement == 2)
+			*pos |= 0x08; /* Bit 27 - UTC TSF Offset */
+		if (hapd->conf->interworking)
+			*pos |= 0x80; /* Bit 31 - Interworking */
+		break;
+	case 4: /* Bits 32-39 */
+		if (hapd->conf->qos_map_set_len)
+			*pos |= 0x01; /* Bit 32 - QoS Map */
+		if (hapd->conf->tdls & TDLS_PROHIBIT)
+			*pos |= 0x40; /* Bit 38 - TDLS Prohibited */
+		if (hapd->conf->tdls & TDLS_PROHIBIT_CHAN_SWITCH) {
+			/* Bit 39 - TDLS Channel Switching Prohibited */
+			*pos |= 0x80;
+		}
+		break;
+	case 5: /* Bits 40-47 */
+#ifdef CONFIG_HS20
+		if (hapd->conf->hs20)
+			*pos |= 0x40; /* Bit 46 - WNM-Notification */
+#endif /* CONFIG_HS20 */
+#ifdef CONFIG_MBO
+		if (hapd->conf->mbo_enabled)
+			*pos |= 0x40; /* Bit 46 - WNM-Notification */
+#endif /* CONFIG_MBO */
+		break;
+	case 6: /* Bits 48-55 */
+		if (hapd->conf->ssid.utf8_ssid)
+			*pos |= 0x01; /* Bit 48 - UTF-8 SSID */
+		break;
+	case 7: /* Bits 56-63 */
+		break;
+	case 8: /* Bits 64-71 */
+		if (hapd->conf->ftm_responder)
+			*pos |= 0x40; /* Bit 70 - FTM responder */
+		if (hapd->conf->ftm_initiator)
+			*pos |= 0x80; /* Bit 71 - FTM initiator */
+		break;
+	case 9: /* Bits 72-79 */
+#ifdef CONFIG_FILS
+		if ((hapd->conf->wpa & WPA_PROTO_RSN) &&
+		    wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt))
+			*pos |= 0x01;
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_IEEE80211AX
+		if (hostapd_get_he_twt_responder(hapd, IEEE80211_MODE_AP))
+			*pos |= 0x40; /* Bit 78 - TWT responder */
+#endif /* CONFIG_IEEE80211AX */
+		break;
+	case 10: /* Bits 80-87 */
+#ifdef CONFIG_SAE
+		if (hapd->conf->wpa &&
+		    wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt)) {
+			int in_use = hostapd_sae_pw_id_in_use(hapd->conf);
+
+			if (in_use)
+				*pos |= 0x02; /* Bit 81 - SAE Password
+					       * Identifiers In Use */
+			if (in_use == 2)
+				*pos |= 0x04; /* Bit 82 - SAE Password
+					       * Identifiers Used Exclusively */
+		}
+#endif /* CONFIG_SAE */
+		if (hapd->conf->beacon_prot &&
+		    (hapd->iface->drv_flags &
+		     WPA_DRIVER_FLAGS_BEACON_PROTECTION))
+			*pos |= 0x10; /* Bit 84 - Beacon Protection Enabled */
+		if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED)
+			*pos |= 0x08; /* Bit 83 - Enhanced multiple BSSID */
+		if (mbssid_complete)
+			*pos |= 0x01; /* Bit 80 - Complete List of NonTxBSSID
+				       * Profiles */
+		break;
+	case 11: /* Bits 88-95 */
+#ifdef CONFIG_SAE_PK
+		if (hapd->conf->wpa &&
+		    wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
+		    hostapd_sae_pk_exclusively(hapd->conf))
+			*pos |= 0x01; /* Bit 88 - SAE PK Exclusively */
+#endif /* CONFIG_SAE_PK */
+		break;
+	}
+}
+
+
+u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid,
+			   bool mbssid_complete)
+{
+	u8 *pos = eid;
+	u8 len = EXT_CAPA_MAX_LEN, i;
+
+	if (len < hapd->iface->extended_capa_len)
+		len = hapd->iface->extended_capa_len;
+
+	*pos++ = WLAN_EID_EXT_CAPAB;
+	*pos++ = len;
+	for (i = 0; i < len; i++, pos++) {
+		hostapd_ext_capab_byte(hapd, pos, i, mbssid_complete);
+
+		if (i < hapd->iface->extended_capa_len) {
+			*pos &= ~hapd->iface->extended_capa_mask[i];
+			*pos |= hapd->iface->extended_capa[i];
+		}
+
+		if (i < EXT_CAPA_MAX_LEN) {
+			*pos &= ~hapd->conf->ext_capa_mask[i];
+			*pos |= hapd->conf->ext_capa[i];
+		}
+
+		/* Clear bits 83 and 22 if EMA and MBSSID are not enabled
+		 * otherwise association fails with some clients */
+		if (i == 10 && hapd->iconf->mbssid < ENHANCED_MBSSID_ENABLED)
+			*pos &= ~0x08;
+		if (i == 2 && !hapd->iconf->mbssid)
+			*pos &= ~0x40;
+	}
+
+	while (len > 0 && eid[1 + len] == 0) {
+		len--;
+		eid[1] = len;
+	}
+	if (len == 0)
+		return eid;
+
+	return eid + 2 + len;
+}
+
+
+u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 *pos = eid;
+	u8 len = hapd->conf->qos_map_set_len;
+
+	if (!len)
+		return eid;
+
+	*pos++ = WLAN_EID_QOS_MAP_SET;
+	*pos++ = len;
+	os_memcpy(pos, hapd->conf->qos_map_set, len);
+	pos += len;
+
+	return pos;
+}
+
+
+u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 *pos = eid;
+#ifdef CONFIG_INTERWORKING
+	u8 *len;
+
+	if (!hapd->conf->interworking)
+		return eid;
+
+	*pos++ = WLAN_EID_INTERWORKING;
+	len = pos++;
+
+	*pos = hapd->conf->access_network_type;
+	if (hapd->conf->internet)
+		*pos |= INTERWORKING_ANO_INTERNET;
+	if (hapd->conf->asra)
+		*pos |= INTERWORKING_ANO_ASRA;
+	if (hapd->conf->esr)
+		*pos |= INTERWORKING_ANO_ESR;
+	if (hapd->conf->uesa)
+		*pos |= INTERWORKING_ANO_UESA;
+	pos++;
+
+	if (hapd->conf->venue_info_set) {
+		*pos++ = hapd->conf->venue_group;
+		*pos++ = hapd->conf->venue_type;
+	}
+
+	if (!is_zero_ether_addr(hapd->conf->hessid)) {
+		os_memcpy(pos, hapd->conf->hessid, ETH_ALEN);
+		pos += ETH_ALEN;
+	}
+
+	*len = pos - len - 1;
+#endif /* CONFIG_INTERWORKING */
+
+	return pos;
+}
+
+
+u8 * hostapd_eid_adv_proto(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 *pos = eid;
+#ifdef CONFIG_INTERWORKING
+
+	/* TODO: Separate configuration for ANQP? */
+	if (!hapd->conf->interworking)
+		return eid;
+
+	*pos++ = WLAN_EID_ADV_PROTO;
+	*pos++ = 2;
+	*pos++ = 0x7F; /* Query Response Length Limit | PAME-BI */
+	*pos++ = ACCESS_NETWORK_QUERY_PROTOCOL;
+#endif /* CONFIG_INTERWORKING */
+
+	return pos;
+}
+
+
+u8 * hostapd_eid_roaming_consortium(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 *pos = eid;
+#ifdef CONFIG_INTERWORKING
+	u8 *len;
+	unsigned int i, count;
+
+	if (!hapd->conf->interworking ||
+	    hapd->conf->roaming_consortium == NULL ||
+	    hapd->conf->roaming_consortium_count == 0)
+		return eid;
+
+	*pos++ = WLAN_EID_ROAMING_CONSORTIUM;
+	len = pos++;
+
+	/* Number of ANQP OIs (in addition to the max 3 listed here) */
+	if (hapd->conf->roaming_consortium_count > 3 + 255)
+		*pos++ = 255;
+	else if (hapd->conf->roaming_consortium_count > 3)
+		*pos++ = hapd->conf->roaming_consortium_count - 3;
+	else
+		*pos++ = 0;
+
+	/* OU #1 and #2 Lengths */
+	*pos = hapd->conf->roaming_consortium[0].len;
+	if (hapd->conf->roaming_consortium_count > 1)
+		*pos |= hapd->conf->roaming_consortium[1].len << 4;
+	pos++;
+
+	if (hapd->conf->roaming_consortium_count > 3)
+		count = 3;
+	else
+		count = hapd->conf->roaming_consortium_count;
+
+	for (i = 0; i < count; i++) {
+		os_memcpy(pos, hapd->conf->roaming_consortium[i].oi,
+			  hapd->conf->roaming_consortium[i].len);
+		pos += hapd->conf->roaming_consortium[i].len;
+	}
+
+	*len = pos - len - 1;
+#endif /* CONFIG_INTERWORKING */
+
+	return pos;
+}
+
+
+u8 * hostapd_eid_time_adv(struct hostapd_data *hapd, u8 *eid)
+{
+	if (hapd->conf->time_advertisement != 2)
+		return eid;
+
+	if (hapd->time_adv == NULL &&
+	    hostapd_update_time_adv(hapd) < 0)
+		return eid;
+
+	if (hapd->time_adv == NULL)
+		return eid;
+
+	os_memcpy(eid, wpabuf_head(hapd->time_adv),
+		  wpabuf_len(hapd->time_adv));
+	eid += wpabuf_len(hapd->time_adv);
+
+	return eid;
+}
+
+
+u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid)
+{
+	size_t len;
+
+	if (hapd->conf->time_advertisement != 2 || !hapd->conf->time_zone)
+		return eid;
+
+	len = os_strlen(hapd->conf->time_zone);
+
+	*eid++ = WLAN_EID_TIME_ZONE;
+	*eid++ = len;
+	os_memcpy(eid, hapd->conf->time_zone, len);
+	eid += len;
+
+	return eid;
+}
+
+
+int hostapd_update_time_adv(struct hostapd_data *hapd)
+{
+	const int elen = 2 + 1 + 10 + 5 + 1;
+	struct os_time t;
+	struct os_tm tm;
+	u8 *pos;
+
+	if (hapd->conf->time_advertisement != 2)
+		return 0;
+
+	if (os_get_time(&t) < 0 || os_gmtime(t.sec, &tm) < 0)
+		return -1;
+
+	if (!hapd->time_adv) {
+		hapd->time_adv = wpabuf_alloc(elen);
+		if (hapd->time_adv == NULL)
+			return -1;
+		pos = wpabuf_put(hapd->time_adv, elen);
+	} else
+		pos = wpabuf_mhead_u8(hapd->time_adv);
+
+	*pos++ = WLAN_EID_TIME_ADVERTISEMENT;
+	*pos++ = 1 + 10 + 5 + 1;
+
+	*pos++ = 2; /* UTC time at which the TSF timer is 0 */
+
+	/* Time Value at TSF 0 */
+	/* FIX: need to calculate this based on the current TSF value */
+	WPA_PUT_LE16(pos, tm.year); /* Year */
+	pos += 2;
+	*pos++ = tm.month; /* Month */
+	*pos++ = tm.day; /* Day of month */
+	*pos++ = tm.hour; /* Hours */
+	*pos++ = tm.min; /* Minutes */
+	*pos++ = tm.sec; /* Seconds */
+	WPA_PUT_LE16(pos, 0); /* Milliseconds (not used) */
+	pos += 2;
+	*pos++ = 0; /* Reserved */
+
+	/* Time Error */
+	/* TODO: fill in an estimate on the error */
+	*pos++ = 0;
+	*pos++ = 0;
+	*pos++ = 0;
+	*pos++ = 0;
+	*pos++ = 0;
+
+	*pos++ = hapd->time_update_counter++;
+
+	return 0;
+}
+
+
+u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 *pos = eid;
+
+#ifdef CONFIG_WNM_AP
+	if (hapd->conf->ap_max_inactivity > 0) {
+		unsigned int val;
+		*pos++ = WLAN_EID_BSS_MAX_IDLE_PERIOD;
+		*pos++ = 3;
+		val = hapd->conf->ap_max_inactivity;
+		if (val > 68000)
+			val = 68000;
+		val *= 1000;
+		val /= 1024;
+		if (val == 0)
+			val = 1;
+		if (val > 65535)
+			val = 65535;
+		WPA_PUT_LE16(pos, val);
+		pos += 2;
+		*pos++ = 0x00; /* TODO: Protected Keep-Alive Required */
+	}
+#endif /* CONFIG_WNM_AP */
+
+	return pos;
+}
+
+
+#ifdef CONFIG_MBO
+
+u8 * hostapd_eid_mbo_rssi_assoc_rej(struct hostapd_data *hapd, u8 *eid,
+				    size_t len, int delta)
+{
+	u8 mbo[4];
+
+	mbo[0] = OCE_ATTR_ID_RSSI_BASED_ASSOC_REJECT;
+	mbo[1] = 2;
+	/* Delta RSSI */
+	mbo[2] = delta;
+	/* Retry delay */
+	mbo[3] = hapd->iconf->rssi_reject_assoc_timeout;
+
+	return eid + mbo_add_ie(eid, len, mbo, 4);
+}
+
+
+u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, size_t len)
+{
+	u8 mbo[9], *mbo_pos = mbo;
+	u8 *pos = eid;
+
+	if (!hapd->conf->mbo_enabled &&
+	    !OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd))
+		return eid;
+
+	if (hapd->conf->mbo_enabled) {
+		*mbo_pos++ = MBO_ATTR_ID_AP_CAPA_IND;
+		*mbo_pos++ = 1;
+		/* Not Cellular aware */
+		*mbo_pos++ = 0;
+	}
+
+	if (hapd->conf->mbo_enabled && hapd->mbo_assoc_disallow) {
+		*mbo_pos++ = MBO_ATTR_ID_ASSOC_DISALLOW;
+		*mbo_pos++ = 1;
+		*mbo_pos++ = hapd->mbo_assoc_disallow;
+	}
+
+	if (OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd)) {
+		u8 ctrl;
+
+		ctrl = OCE_RELEASE;
+		if (OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd))
+			ctrl |= OCE_IS_STA_CFON;
+
+		*mbo_pos++ = OCE_ATTR_ID_CAPA_IND;
+		*mbo_pos++ = 1;
+		*mbo_pos++ = ctrl;
+	}
+
+	pos += mbo_add_ie(pos, len, mbo, mbo_pos - mbo);
+
+	return pos;
+}
+
+
+u8 hostapd_mbo_ie_len(struct hostapd_data *hapd)
+{
+	u8 len;
+
+	if (!hapd->conf->mbo_enabled &&
+	    !OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd))
+		return 0;
+
+	/*
+	 * MBO IE header (6) + Capability Indication attribute (3) +
+	 * Association Disallowed attribute (3) = 12
+	 */
+	len = 6;
+	if (hapd->conf->mbo_enabled)
+		len += 3 + (hapd->mbo_assoc_disallow ? 3 : 0);
+
+	/* OCE capability indication attribute (3) */
+	if (OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd))
+		len += 3;
+
+	return len;
+}
+
+#endif /* CONFIG_MBO */
+
+
+#ifdef CONFIG_OWE
+static int hostapd_eid_owe_trans_enabled(struct hostapd_data *hapd)
+{
+	return hapd->conf->owe_transition_ssid_len > 0 &&
+		!is_zero_ether_addr(hapd->conf->owe_transition_bssid);
+}
+#endif /* CONFIG_OWE */
+
+
+size_t hostapd_eid_owe_trans_len(struct hostapd_data *hapd)
+{
+#ifdef CONFIG_OWE
+	if (!hostapd_eid_owe_trans_enabled(hapd))
+		return 0;
+	return 6 + ETH_ALEN + 1 + hapd->conf->owe_transition_ssid_len;
+#else /* CONFIG_OWE */
+	return 0;
+#endif /* CONFIG_OWE */
+}
+
+
+u8 * hostapd_eid_owe_trans(struct hostapd_data *hapd, u8 *eid,
+				  size_t len)
+{
+#ifdef CONFIG_OWE
+	u8 *pos = eid;
+	size_t elen;
+
+	if (hapd->conf->owe_transition_ifname[0] &&
+	    !hostapd_eid_owe_trans_enabled(hapd))
+		hostapd_owe_trans_get_info(hapd);
+
+	if (!hostapd_eid_owe_trans_enabled(hapd))
+		return pos;
+
+	elen = hostapd_eid_owe_trans_len(hapd);
+	if (len < elen) {
+		wpa_printf(MSG_DEBUG,
+			   "OWE: Not enough room in the buffer for OWE IE");
+		return pos;
+	}
+
+	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
+	*pos++ = elen - 2;
+	WPA_PUT_BE24(pos, OUI_WFA);
+	pos += 3;
+	*pos++ = OWE_OUI_TYPE;
+	os_memcpy(pos, hapd->conf->owe_transition_bssid, ETH_ALEN);
+	pos += ETH_ALEN;
+	*pos++ = hapd->conf->owe_transition_ssid_len;
+	os_memcpy(pos, hapd->conf->owe_transition_ssid,
+		  hapd->conf->owe_transition_ssid_len);
+	pos += hapd->conf->owe_transition_ssid_len;
+
+	return pos;
+#else /* CONFIG_OWE */
+	return eid;
+#endif /* CONFIG_OWE */
+}
+
+
+size_t hostapd_eid_dpp_cc_len(struct hostapd_data *hapd)
+{
+#ifdef CONFIG_DPP2
+	if (hostapd_dpp_configurator_connectivity(hapd))
+		return 6;
+#endif /* CONFIG_DPP2 */
+	return 0;
+}
+
+
+u8 * hostapd_eid_dpp_cc(struct hostapd_data *hapd, u8 *eid, size_t len)
+{
+	u8 *pos = eid;
+
+#ifdef CONFIG_DPP2
+	if (!hostapd_dpp_configurator_connectivity(hapd) || len < 6)
+		return pos;
+
+	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
+	*pos++ = 4;
+	WPA_PUT_BE24(pos, OUI_WFA);
+	pos += 3;
+	*pos++ = DPP_CC_OUI_TYPE;
+#endif /* CONFIG_DPP2 */
+
+	return pos;
+}
+
+
+void ap_copy_sta_supp_op_classes(struct sta_info *sta,
+				 const u8 *supp_op_classes,
+				 size_t supp_op_classes_len)
+{
+	if (!supp_op_classes)
+		return;
+	os_free(sta->supp_op_classes);
+	sta->supp_op_classes = os_malloc(1 + supp_op_classes_len);
+	if (!sta->supp_op_classes)
+		return;
+
+	sta->supp_op_classes[0] = supp_op_classes_len;
+	os_memcpy(sta->supp_op_classes + 1, supp_op_classes,
+		  supp_op_classes_len);
+}
+
+
+u8 * hostapd_eid_fils_indic(struct hostapd_data *hapd, u8 *eid, int hessid)
+{
+	u8 *pos = eid;
+#ifdef CONFIG_FILS
+	u8 *len;
+	u16 fils_info = 0;
+	size_t realms;
+	struct fils_realm *realm;
+
+	if (!(hapd->conf->wpa & WPA_PROTO_RSN) ||
+	    !wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt))
+		return pos;
+
+	realms = dl_list_len(&hapd->conf->fils_realms);
+	if (realms > 7)
+		realms = 7; /* 3 bit count field limits this to max 7 */
+
+	*pos++ = WLAN_EID_FILS_INDICATION;
+	len = pos++;
+	/* TODO: B0..B2: Number of Public Key Identifiers */
+	if (hapd->conf->erp_domain) {
+		/* B3..B5: Number of Realm Identifiers */
+		fils_info |= realms << 3;
+	}
+	/* TODO: B6: FILS IP Address Configuration */
+	if (hapd->conf->fils_cache_id_set)
+		fils_info |= BIT(7);
+	if (hessid && !is_zero_ether_addr(hapd->conf->hessid))
+		fils_info |= BIT(8); /* HESSID Included */
+	/* FILS Shared Key Authentication without PFS Supported */
+	fils_info |= BIT(9);
+	if (hapd->conf->fils_dh_group) {
+		/* FILS Shared Key Authentication with PFS Supported */
+		fils_info |= BIT(10);
+	}
+	/* TODO: B11: FILS Public Key Authentication Supported */
+	/* B12..B15: Reserved */
+	WPA_PUT_LE16(pos, fils_info);
+	pos += 2;
+	if (hapd->conf->fils_cache_id_set) {
+		os_memcpy(pos, hapd->conf->fils_cache_id, FILS_CACHE_ID_LEN);
+		pos += FILS_CACHE_ID_LEN;
+	}
+	if (hessid && !is_zero_ether_addr(hapd->conf->hessid)) {
+		os_memcpy(pos, hapd->conf->hessid, ETH_ALEN);
+		pos += ETH_ALEN;
+	}
+
+	dl_list_for_each(realm, &hapd->conf->fils_realms, struct fils_realm,
+			 list) {
+		if (realms == 0)
+			break;
+		realms--;
+		os_memcpy(pos, realm->hash, 2);
+		pos += 2;
+	}
+	*len = pos - len - 1;
+#endif /* CONFIG_FILS */
+
+	return pos;
+}
+
+
+#ifdef CONFIG_OCV
+int get_tx_parameters(struct sta_info *sta, int ap_max_chanwidth,
+		      int ap_seg1_idx, int *bandwidth, int *seg1_idx)
+{
+	int ht_40mhz = 0;
+	int vht_80p80 = 0;
+	int requested_bw;
+
+	if (sta->ht_capabilities)
+		ht_40mhz = !!(sta->ht_capabilities->ht_capabilities_info &
+			      HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET);
+
+	if (sta->vht_operation) {
+		struct ieee80211_vht_operation *oper = sta->vht_operation;
+
+		/*
+		 * If a VHT Operation element was present, use it to determine
+		 * the supported channel bandwidth.
+		 */
+		if (oper->vht_op_info_chwidth == CHANWIDTH_USE_HT) {
+			requested_bw = ht_40mhz ? 40 : 20;
+		} else if (oper->vht_op_info_chan_center_freq_seg1_idx == 0) {
+			requested_bw = 80;
+		} else {
+			int diff;
+
+			requested_bw = 160;
+			diff = abs((int)
+				   oper->vht_op_info_chan_center_freq_seg0_idx -
+				   (int)
+				   oper->vht_op_info_chan_center_freq_seg1_idx);
+			vht_80p80 = oper->vht_op_info_chan_center_freq_seg1_idx
+				!= 0 &&	diff > 16;
+		}
+	} else if (sta->vht_capabilities) {
+		struct ieee80211_vht_capabilities *capab;
+		int vht_chanwidth;
+
+		capab = sta->vht_capabilities;
+
+		/*
+		 * If only the VHT Capabilities element is present (e.g., for
+		 * normal clients), use it to determine the supported channel
+		 * bandwidth.
+		 */
+		vht_chanwidth = capab->vht_capabilities_info &
+			VHT_CAP_SUPP_CHAN_WIDTH_MASK;
+		vht_80p80 = capab->vht_capabilities_info &
+			VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
+
+		/* TODO: Also take into account Extended NSS BW Support field */
+		requested_bw = vht_chanwidth ? 160 : 80;
+	} else {
+		requested_bw = ht_40mhz ? 40 : 20;
+	}
+
+	*bandwidth = requested_bw < ap_max_chanwidth ?
+		requested_bw : ap_max_chanwidth;
+
+	*seg1_idx = 0;
+	if (ap_seg1_idx && vht_80p80)
+		*seg1_idx = ap_seg1_idx;
+
+	return 0;
+}
+#endif /* CONFIG_OCV */
+
+
+u8 * hostapd_eid_rsnxe(struct hostapd_data *hapd, u8 *eid, size_t len)
+{
+	u8 *pos = eid;
+	bool sae_pk = false;
+	u16 capab = 0;
+	size_t flen;
+
+	if (!(hapd->conf->wpa & WPA_PROTO_RSN))
+		return eid;
+
+#ifdef CONFIG_SAE_PK
+	sae_pk = hostapd_sae_pk_in_use(hapd->conf);
+#endif /* CONFIG_SAE_PK */
+
+	if (wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
+	    (hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+	     hapd->conf->sae_pwe == SAE_PWE_BOTH ||
+	     hostapd_sae_pw_id_in_use(hapd->conf) || sae_pk ||
+	     wpa_key_mgmt_sae_ext_key(hapd->conf->wpa_key_mgmt)) &&
+	    hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK) {
+		capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
+#ifdef CONFIG_SAE_PK
+		if (sae_pk)
+			capab |= BIT(WLAN_RSNX_CAPAB_SAE_PK);
+#endif /* CONFIG_SAE_PK */
+	}
+
+	if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_AP)
+		capab |= BIT(WLAN_RSNX_CAPAB_SECURE_LTF);
+	if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_RTT_AP)
+		capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT);
+	if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP)
+		capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR);
+
+	flen = (capab & 0xff00) ? 2 : 1;
+	if (len < 2 + flen || !capab)
+		return eid; /* no supported extended RSN capabilities */
+	capab |= flen - 1; /* bit 0-3 = Field length (n - 1) */
+
+	*pos++ = WLAN_EID_RSNX;
+	*pos++ = flen;
+	*pos++ = capab & 0x00ff;
+	capab >>= 8;
+	if (capab)
+		*pos++ = capab;
+
+	return pos;
+}
+
+
+u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta,
+		    const u8 *ext_capab_ie, size_t ext_capab_ie_len)
+{
+	/* check for QoS Map support */
+	if (ext_capab_ie_len >= 5) {
+		if (ext_capab_ie[4] & 0x01)
+			sta->qos_map_enabled = 1;
+	}
+
+	if (ext_capab_ie_len > 0) {
+		sta->ecsa_supported = !!(ext_capab_ie[0] & BIT(2));
+		os_free(sta->ext_capability);
+		sta->ext_capability = os_malloc(1 + ext_capab_ie_len);
+		if (sta->ext_capability) {
+			sta->ext_capability[0] = ext_capab_ie_len;
+			os_memcpy(sta->ext_capability + 1, ext_capab_ie,
+				  ext_capab_ie_len);
+		}
+	}
+
+	return WLAN_STATUS_SUCCESS;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11_vht.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11_vht.c
new file mode 100644
index 0000000..bd6462f
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_11_vht.c
@@ -0,0 +1,384 @@
+/*
+ * hostapd / IEEE 802.11ac VHT
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of BSD license
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "sta_info.h"
+#include "beacon.h"
+#include "ieee802_11.h"
+#include "dfs.h"
+
+
+u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts)
+{
+	struct ieee80211_vht_capabilities *cap;
+	struct hostapd_hw_modes *mode = hapd->iface->current_mode;
+	u8 *pos = eid;
+	u8 chwidth;
+
+	if (!mode || is_6ghz_op_class(hapd->iconf->op_class))
+		return eid;
+
+	if (mode->mode == HOSTAPD_MODE_IEEE80211G && hapd->conf->vendor_vht &&
+	    mode->vht_capab == 0 && hapd->iface->hw_features) {
+		int i;
+
+		for (i = 0; i < hapd->iface->num_hw_features; i++) {
+			if (hapd->iface->hw_features[i].mode ==
+			    HOSTAPD_MODE_IEEE80211A) {
+				mode = &hapd->iface->hw_features[i];
+				break;
+			}
+		}
+	}
+
+	*pos++ = WLAN_EID_VHT_CAP;
+	*pos++ = sizeof(*cap);
+
+	cap = (struct ieee80211_vht_capabilities *) pos;
+	os_memset(cap, 0, sizeof(*cap));
+	cap->vht_capabilities_info = host_to_le32(
+		hapd->iface->conf->vht_capab);
+
+	if (nsts != 0) {
+		u32 hapd_nsts;
+
+		hapd_nsts = le_to_host32(cap->vht_capabilities_info);
+		hapd_nsts = (hapd_nsts >> VHT_CAP_BEAMFORMEE_STS_OFFSET) & 7;
+		cap->vht_capabilities_info &=
+			~(host_to_le32(hapd_nsts <<
+				       VHT_CAP_BEAMFORMEE_STS_OFFSET));
+		cap->vht_capabilities_info |=
+			host_to_le32(nsts << VHT_CAP_BEAMFORMEE_STS_OFFSET);
+	}
+
+	chwidth = hostapd_get_oper_chwidth(hapd->iconf);
+	if (((host_to_le32(mode->vht_capab)) & VHT_CAP_EXTENDED_NSS_BW_SUPPORT)
+		&& ((chwidth == CHANWIDTH_160MHZ) || (chwidth == CHANWIDTH_80P80MHZ))) {
+		cap->vht_capabilities_info |= VHT_CAP_EXTENDED_NSS_BW_SUPPORT;
+		cap->vht_capabilities_info &= ~(host_to_le32(VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ));
+		cap->vht_capabilities_info &= ~(host_to_le32(VHT_CAP_SUPP_CHAN_WIDTH_160MHZ));
+		cap->vht_capabilities_info &= ~(host_to_le32(VHT_CAP_SUPP_CHAN_WIDTH_MASK));
+	} else {
+		cap->vht_capabilities_info &= ~VHT_CAP_EXTENDED_NSS_BW_SUPPORT_MASK;
+	}
+
+	/* Supported MCS set comes from hw */
+	os_memcpy(&cap->vht_supported_mcs_set, mode->vht_mcs_set, 8);
+
+	pos += sizeof(*cap);
+
+	return pos;
+}
+
+
+u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid)
+{
+	struct ieee80211_vht_operation *oper;
+	le32 vht_capabilities_info;
+	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;
+
+	*pos++ = WLAN_EID_VHT_OPERATION;
+	*pos++ = sizeof(*oper);
+
+	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 = seg0;
+	oper->vht_op_info_chan_center_freq_seg1_idx = seg1;
+
+	oper->vht_op_info_chwidth = oper_chwidth;
+	vht_capabilities_info = host_to_le32(hapd->iface->current_mode->vht_capab);
+	if (oper_chwidth == CONF_OPER_CHWIDTH_160MHZ) {
+		/*
+		 * Convert 160 MHz channel width to new style as interop
+		 * workaround.
+		 */
+		oper->vht_op_info_chwidth = CHANWIDTH_80MHZ;
+		oper->vht_op_info_chan_center_freq_seg1_idx =
+			oper->vht_op_info_chan_center_freq_seg0_idx;
+		if (hapd->iconf->channel <
+		    hapd->iconf->vht_oper_centr_freq_seg0_idx)
+			oper->vht_op_info_chan_center_freq_seg0_idx -= 8;
+		else
+			oper->vht_op_info_chan_center_freq_seg0_idx += 8;
+
+		if (vht_capabilities_info & VHT_CAP_EXTENDED_NSS_BW_SUPPORT)
+			oper->vht_op_info_chan_center_freq_seg1_idx = 0;
+	} else if (oper_chwidth == CONF_OPER_CHWIDTH_80P80MHZ) {
+		/*
+		 * Convert 80+80 MHz channel width to new style as interop
+		 * workaround.
+		 */
+		oper->vht_op_info_chwidth = CHANWIDTH_80MHZ;
+	}
+
+	/* VHT Basic MCS set comes from hw */
+	/* Hard code 1 stream, MCS0-7 is a min Basic VHT MCS rates */
+	oper->vht_basic_mcs_set = host_to_le16(0xfffc);
+	pos += sizeof(*oper);
+
+	return pos;
+}
+
+
+static int check_valid_vht_mcs(struct hostapd_hw_modes *mode,
+			       const u8 *sta_vht_capab)
+{
+	const struct ieee80211_vht_capabilities *vht_cap;
+	struct ieee80211_vht_capabilities ap_vht_cap;
+	u16 sta_rx_mcs_set, ap_tx_mcs_set;
+	int i;
+
+	if (!mode)
+		return 1;
+
+	/*
+	 * Disable VHT caps for STAs for which there is not even a single
+	 * allowed MCS in any supported number of streams, i.e., STA is
+	 * advertising 3 (not supported) as VHT MCS rates for all supported
+	 * stream cases.
+	 */
+	os_memcpy(&ap_vht_cap.vht_supported_mcs_set, mode->vht_mcs_set,
+		  sizeof(ap_vht_cap.vht_supported_mcs_set));
+	vht_cap = (const struct ieee80211_vht_capabilities *) sta_vht_capab;
+
+	/* AP Tx MCS map vs. STA Rx MCS map */
+	sta_rx_mcs_set = le_to_host16(vht_cap->vht_supported_mcs_set.rx_map);
+	ap_tx_mcs_set = le_to_host16(ap_vht_cap.vht_supported_mcs_set.tx_map);
+
+	for (i = 0; i < VHT_RX_NSS_MAX_STREAMS; i++) {
+		if ((ap_tx_mcs_set & (0x3 << (i * 2))) == 3)
+			continue;
+
+		if ((sta_rx_mcs_set & (0x3 << (i * 2))) == 3)
+			continue;
+
+		return 1;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "No matching VHT MCS found between AP TX and STA RX");
+	return 0;
+}
+
+
+u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+		       const u8 *vht_capab)
+{
+	/* Disable VHT caps for STAs associated to no-VHT BSSes. */
+	if (!vht_capab || !(sta->flags & WLAN_STA_WMM) ||
+	    !hapd->iconf->ieee80211ac || hapd->conf->disable_11ac ||
+	    !check_valid_vht_mcs(hapd->iface->current_mode, vht_capab)) {
+		sta->flags &= ~WLAN_STA_VHT;
+		os_free(sta->vht_capabilities);
+		sta->vht_capabilities = NULL;
+		return WLAN_STATUS_SUCCESS;
+	}
+
+	if (sta->vht_capabilities == NULL) {
+		sta->vht_capabilities =
+			os_zalloc(sizeof(struct ieee80211_vht_capabilities));
+		if (sta->vht_capabilities == NULL)
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	sta->flags |= WLAN_STA_VHT;
+	os_memcpy(sta->vht_capabilities, vht_capab,
+		  sizeof(struct ieee80211_vht_capabilities));
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+u16 copy_sta_vht_oper(struct hostapd_data *hapd, struct sta_info *sta,
+		      const u8 *vht_oper)
+{
+	if (!vht_oper) {
+		os_free(sta->vht_operation);
+		sta->vht_operation = NULL;
+		return WLAN_STATUS_SUCCESS;
+	}
+
+	if (!sta->vht_operation) {
+		sta->vht_operation =
+			os_zalloc(sizeof(struct ieee80211_vht_operation));
+		if (!sta->vht_operation)
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	os_memcpy(sta->vht_operation, vht_oper,
+		  sizeof(struct ieee80211_vht_operation));
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta,
+			const u8 *ie, size_t len)
+{
+	const u8 *vht_capab;
+	unsigned int vht_capab_len;
+
+	if (!ie || len < 5 + 2 + sizeof(struct ieee80211_vht_capabilities) ||
+	    hapd->conf->disable_11ac)
+		goto no_capab;
+
+	/* The VHT Capabilities element embedded in vendor VHT */
+	vht_capab = ie + 5;
+	if (vht_capab[0] != WLAN_EID_VHT_CAP)
+		goto no_capab;
+	vht_capab_len = vht_capab[1];
+	if (vht_capab_len < sizeof(struct ieee80211_vht_capabilities) ||
+	    (int) vht_capab_len > ie + len - vht_capab - 2)
+		goto no_capab;
+	vht_capab += 2;
+
+	if (sta->vht_capabilities == NULL) {
+		sta->vht_capabilities =
+			os_zalloc(sizeof(struct ieee80211_vht_capabilities));
+		if (sta->vht_capabilities == NULL)
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	sta->flags |= WLAN_STA_VHT | WLAN_STA_VENDOR_VHT;
+	os_memcpy(sta->vht_capabilities, vht_capab,
+		  sizeof(struct ieee80211_vht_capabilities));
+	return WLAN_STATUS_SUCCESS;
+
+no_capab:
+	sta->flags &= ~WLAN_STA_VENDOR_VHT;
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 *pos = eid;
+
+	/* Vendor VHT is applicable only to 2.4 GHz */
+	if (!hapd->iface->current_mode ||
+	    hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
+		return eid;
+
+	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
+	*pos++ = (5 +		/* The Vendor OUI, type and subtype */
+		  2 + sizeof(struct ieee80211_vht_capabilities) +
+		  2 + sizeof(struct ieee80211_vht_operation));
+
+	WPA_PUT_BE32(pos, (OUI_BROADCOM << 8) | VENDOR_VHT_TYPE);
+	pos += 4;
+	*pos++ = VENDOR_VHT_SUBTYPE;
+	pos = hostapd_eid_vht_capabilities(hapd, pos, 0);
+	pos = hostapd_eid_vht_operation(hapd, pos);
+
+	return pos;
+}
+
+
+u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta,
+		       const u8 *vht_oper_notif)
+{
+	if (!vht_oper_notif) {
+		sta->flags &= ~WLAN_STA_VHT_OPMODE_ENABLED;
+		return WLAN_STATUS_SUCCESS;
+	}
+
+	sta->flags |= WLAN_STA_VHT_OPMODE_ENABLED;
+	sta->vht_opmode = *vht_oper_notif;
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+void hostapd_get_vht_capab(struct hostapd_data *hapd,
+			   struct ieee80211_vht_capabilities *vht_cap,
+			   struct ieee80211_vht_capabilities *neg_vht_cap)
+{
+	u32 cap, own_cap, sym_caps;
+
+	if (vht_cap == NULL)
+		return;
+	os_memcpy(neg_vht_cap, vht_cap, sizeof(*neg_vht_cap));
+
+	cap = le_to_host32(neg_vht_cap->vht_capabilities_info);
+	own_cap = hapd->iconf->vht_capab;
+
+	/* mask out symmetric VHT capabilities we don't support */
+	sym_caps = VHT_CAP_SHORT_GI_80 | VHT_CAP_SHORT_GI_160;
+	cap &= ~sym_caps | (own_cap & sym_caps);
+
+	/* mask out beamformer/beamformee caps if not supported */
+	if (!(own_cap & VHT_CAP_SU_BEAMFORMER_CAPABLE))
+		cap &= ~(VHT_CAP_SU_BEAMFORMEE_CAPABLE |
+			 VHT_CAP_BEAMFORMEE_STS_MAX);
+
+	if (!(own_cap & VHT_CAP_SU_BEAMFORMEE_CAPABLE))
+		cap &= ~(VHT_CAP_SU_BEAMFORMER_CAPABLE |
+			 VHT_CAP_SOUNDING_DIMENSION_MAX);
+
+	if (!(own_cap & VHT_CAP_MU_BEAMFORMER_CAPABLE))
+		cap &= ~VHT_CAP_MU_BEAMFORMEE_CAPABLE;
+
+	if (!(own_cap & VHT_CAP_MU_BEAMFORMEE_CAPABLE))
+		cap &= ~VHT_CAP_MU_BEAMFORMER_CAPABLE;
+
+	/* mask channel widths we don't support */
+	switch (own_cap & VHT_CAP_SUPP_CHAN_WIDTH_MASK) {
+	case VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ:
+		break;
+	case VHT_CAP_SUPP_CHAN_WIDTH_160MHZ:
+		if (cap & VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) {
+			cap &= ~VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
+			cap |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+		}
+		break;
+	default:
+		cap &= ~VHT_CAP_SUPP_CHAN_WIDTH_MASK;
+		break;
+	}
+
+	if (!(cap & VHT_CAP_SUPP_CHAN_WIDTH_MASK))
+		cap &= ~VHT_CAP_SHORT_GI_160;
+
+	/*
+	 * if we don't support RX STBC, mask out TX STBC in the STA's HT caps
+	 * if we don't support TX STBC, mask out RX STBC in the STA's HT caps
+	 */
+	if (!(own_cap & VHT_CAP_RXSTBC_MASK))
+		cap &= ~VHT_CAP_TXSTBC;
+	if (!(own_cap & VHT_CAP_TXSTBC))
+		cap &= ~VHT_CAP_RXSTBC_MASK;
+
+	neg_vht_cap->vht_capabilities_info = host_to_le32(cap);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_1x.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_1x.c
new file mode 100644
index 0000000..46b120d
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_1x.c
@@ -0,0 +1,3136 @@
+/*
+ * hostapd / IEEE 802.1X-2004 Authenticator
+ * Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#ifdef CONFIG_SQLITE
+#include <sqlite3.h>
+#endif /* CONFIG_SQLITE */
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "crypto/md5.h"
+#include "crypto/crypto.h"
+#include "crypto/random.h"
+#include "common/ieee802_11_defs.h"
+#include "radius/radius.h"
+#include "radius/radius_client.h"
+#include "eap_server/eap.h"
+#include "eap_common/eap_wsc_common.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
+#include "p2p/p2p.h"
+#include "hostapd.h"
+#include "accounting.h"
+#include "sta_info.h"
+#include "wpa_auth.h"
+#include "preauth_auth.h"
+#include "pmksa_cache_auth.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "wps_hostapd.h"
+#include "hs20.h"
+/* FIX: Not really a good thing to require ieee802_11.h here.. (FILS) */
+#include "ieee802_11.h"
+#include "ieee802_1x.h"
+#include "wpa_auth_kay.h"
+
+
+#ifdef CONFIG_HS20
+static void ieee802_1x_wnm_notif_send(void *eloop_ctx, void *timeout_ctx);
+#endif /* CONFIG_HS20 */
+static bool ieee802_1x_finished(struct hostapd_data *hapd,
+				struct sta_info *sta, int success,
+				int remediation, bool logoff);
+
+
+static void ieee802_1x_send(struct hostapd_data *hapd, struct sta_info *sta,
+			    u8 type, const u8 *data, size_t datalen)
+{
+	u8 *buf;
+	struct ieee802_1x_hdr *xhdr;
+	size_t len;
+	int encrypt = 0;
+
+	len = sizeof(*xhdr) + datalen;
+	buf = os_zalloc(len);
+	if (!buf) {
+		wpa_printf(MSG_ERROR, "malloc() failed for %s(len=%lu)",
+			   __func__, (unsigned long) len);
+		return;
+	}
+
+	xhdr = (struct ieee802_1x_hdr *) buf;
+	xhdr->version = hapd->conf->eapol_version;
+#ifdef CONFIG_MACSEC
+	if (xhdr->version > 2 && hapd->conf->macsec_policy == 0)
+		xhdr->version = 2;
+#endif /* CONFIG_MACSEC */
+	xhdr->type = type;
+	xhdr->length = host_to_be16(datalen);
+
+	if (datalen > 0 && data != NULL)
+		os_memcpy(xhdr + 1, data, datalen);
+
+	if (wpa_auth_pairwise_set(sta->wpa_sm))
+		encrypt = 1;
+#ifdef CONFIG_TESTING_OPTIONS
+	if (hapd->ext_eapol_frame_io) {
+		size_t hex_len = 2 * len + 1;
+		char *hex = os_malloc(hex_len);
+
+		if (hex) {
+			wpa_snprintf_hex(hex, hex_len, buf, len);
+			wpa_msg(hapd->msg_ctx, MSG_INFO,
+				"EAPOL-TX " MACSTR " %s",
+				MAC2STR(sta->addr), hex);
+			os_free(hex);
+		}
+	} else
+#endif /* CONFIG_TESTING_OPTIONS */
+	if (sta->flags & WLAN_STA_PREAUTH) {
+		rsn_preauth_send(hapd, sta, buf, len);
+	} else {
+		int link_id = -1;
+
+#ifdef CONFIG_IEEE80211BE
+		link_id = hapd->conf->mld_ap ? hapd->mld_link_id : -1;
+#endif /* CONFIG_IEEE80211BE */
+		hostapd_drv_hapd_send_eapol(
+			hapd, sta->addr, buf, len,
+			encrypt, hostapd_sta_flags_to_drv(sta->flags), link_id);
+	}
+
+	os_free(buf);
+}
+
+
+static void ieee802_1x_set_authorized(struct hostapd_data *hapd,
+				      struct sta_info *sta,
+				      bool authorized, bool mld)
+{
+	int res;
+
+	if (sta->flags & WLAN_STA_PREAUTH)
+		return;
+
+	ap_sta_set_authorized(hapd, sta, authorized);
+	res = hostapd_set_authorized(hapd, sta, authorized);
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+		       HOSTAPD_LEVEL_DEBUG, "%sauthorizing port",
+		       authorized ? "" : "un");
+
+	if (!mld && res && errno != ENOENT) {
+		wpa_printf(MSG_DEBUG, "Could not set station " MACSTR
+			   " flags for kernel driver (errno=%d).",
+			   MAC2STR(sta->addr), errno);
+	} else if (mld && res) {
+		wpa_printf(MSG_DEBUG,
+			   "MLD: Could not set station " MACSTR " flags",
+			   MAC2STR(sta->addr));
+	}
+
+	if (authorized) {
+		os_get_reltime(&sta->connected_time);
+		accounting_sta_start(hapd, sta);
+	}
+}
+
+
+static void ieee802_1x_ml_set_sta_authorized(struct hostapd_data *hapd,
+					     struct sta_info *sta,
+					     bool authorized)
+{
+#ifdef CONFIG_IEEE80211BE
+	unsigned int i, link_id;
+
+	if (!hostapd_is_mld_ap(hapd))
+		return;
+
+	/*
+	 * Authorizing the station should be done only in the station
+	 * performing the association
+	 */
+	if (authorized && hapd->mld_link_id != sta->mld_assoc_link_id)
+		return;
+
+	for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+		struct mld_link_info *link = &sta->mld_info.links[link_id];
+
+		if (!link->valid)
+			continue;
+
+		for (i = 0; i < hapd->iface->interfaces->count; i++) {
+			struct sta_info *tmp_sta;
+			struct hostapd_data *tmp_hapd =
+				hapd->iface->interfaces->iface[i]->bss[0];
+
+			if (tmp_hapd->conf->mld_ap ||
+			    hapd->conf->mld_id != tmp_hapd->conf->mld_id)
+				continue;
+
+			for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
+			     tmp_sta = tmp_sta->next) {
+				if (tmp_sta == sta ||
+				    tmp_sta->mld_assoc_link_id !=
+				    sta->mld_assoc_link_id ||
+				    tmp_sta->aid != sta->aid)
+					continue;
+
+				ieee802_1x_set_authorized(tmp_hapd, tmp_sta,
+							  authorized, true);
+				break;
+			}
+		}
+	}
+#endif /* CONFIG_IEEE80211BE */
+}
+
+
+
+void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd,
+				   struct sta_info *sta, int authorized)
+{
+	ieee802_1x_set_authorized(hapd, sta, authorized, false);
+	ieee802_1x_ml_set_sta_authorized(hapd, sta, !!authorized);
+}
+
+
+#ifdef CONFIG_WEP
+#ifndef CONFIG_FIPS
+#ifndef CONFIG_NO_RC4
+
+static void ieee802_1x_tx_key_one(struct hostapd_data *hapd,
+				  struct sta_info *sta,
+				  int idx, int broadcast,
+				  u8 *key_data, size_t key_len)
+{
+	u8 *buf, *ekey;
+	struct ieee802_1x_hdr *hdr;
+	struct ieee802_1x_eapol_key *key;
+	size_t len, ekey_len;
+	struct eapol_state_machine *sm = sta->eapol_sm;
+
+	if (!sm)
+		return;
+
+	len = sizeof(*key) + key_len;
+	buf = os_zalloc(sizeof(*hdr) + len);
+	if (!buf)
+		return;
+
+	hdr = (struct ieee802_1x_hdr *) buf;
+	key = (struct ieee802_1x_eapol_key *) (hdr + 1);
+	key->type = EAPOL_KEY_TYPE_RC4;
+	WPA_PUT_BE16(key->key_length, key_len);
+	wpa_get_ntp_timestamp(key->replay_counter);
+	if (os_memcmp(key->replay_counter,
+		      hapd->last_1x_eapol_key_replay_counter,
+		      IEEE8021X_REPLAY_COUNTER_LEN) <= 0) {
+		/* NTP timestamp did not increment from last EAPOL-Key frame;
+		 * use previously used value + 1 instead. */
+		inc_byte_array(hapd->last_1x_eapol_key_replay_counter,
+			       IEEE8021X_REPLAY_COUNTER_LEN);
+		os_memcpy(key->replay_counter,
+			  hapd->last_1x_eapol_key_replay_counter,
+			  IEEE8021X_REPLAY_COUNTER_LEN);
+	} else {
+		os_memcpy(hapd->last_1x_eapol_key_replay_counter,
+			  key->replay_counter,
+			  IEEE8021X_REPLAY_COUNTER_LEN);
+	}
+
+	if (random_get_bytes(key->key_iv, sizeof(key->key_iv))) {
+		wpa_printf(MSG_ERROR, "Could not get random numbers");
+		os_free(buf);
+		return;
+	}
+
+	key->key_index = idx | (broadcast ? 0 : BIT(7));
+	if (hapd->conf->eapol_key_index_workaround) {
+		/* According to some information, WinXP Supplicant seems to
+		 * interpret bit7 as an indication whether the key is to be
+		 * activated, so make it possible to enable workaround that
+		 * sets this bit for all keys. */
+		key->key_index |= BIT(7);
+	}
+
+	/* Key is encrypted using "Key-IV + MSK[0..31]" as the RC4-key and
+	 * MSK[32..63] is used to sign the message. */
+	if (!sm->eap_if->eapKeyData || sm->eap_if->eapKeyDataLen < 64) {
+		wpa_printf(MSG_ERROR,
+			   "No eapKeyData available for encrypting and signing EAPOL-Key");
+		os_free(buf);
+		return;
+	}
+	os_memcpy((u8 *) (key + 1), key_data, key_len);
+	ekey_len = sizeof(key->key_iv) + 32;
+	ekey = os_malloc(ekey_len);
+	if (!ekey) {
+		wpa_printf(MSG_ERROR, "Could not encrypt key");
+		os_free(buf);
+		return;
+	}
+	os_memcpy(ekey, key->key_iv, sizeof(key->key_iv));
+	os_memcpy(ekey + sizeof(key->key_iv), sm->eap_if->eapKeyData, 32);
+	rc4_skip(ekey, ekey_len, 0, (u8 *) (key + 1), key_len);
+	os_free(ekey);
+
+	/* This header is needed here for HMAC-MD5, but it will be regenerated
+	 * in ieee802_1x_send() */
+	hdr->version = hapd->conf->eapol_version;
+#ifdef CONFIG_MACSEC
+	if (hdr->version > 2)
+		hdr->version = 2;
+#endif /* CONFIG_MACSEC */
+	hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
+	hdr->length = host_to_be16(len);
+	hmac_md5(sm->eap_if->eapKeyData + 32, 32, buf, sizeof(*hdr) + len,
+		 key->key_signature);
+
+	wpa_printf(MSG_DEBUG, "IEEE 802.1X: Sending EAPOL-Key to " MACSTR
+		   " (%s index=%d)", MAC2STR(sm->addr),
+		   broadcast ? "broadcast" : "unicast", idx);
+	ieee802_1x_send(hapd, sta, IEEE802_1X_TYPE_EAPOL_KEY, (u8 *) key, len);
+	if (sta->eapol_sm)
+		sta->eapol_sm->dot1xAuthEapolFramesTx++;
+	os_free(buf);
+}
+
+
+static void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	struct eapol_authenticator *eapol = hapd->eapol_auth;
+	struct eapol_state_machine *sm = sta->eapol_sm;
+
+	if (!sm || !sm->eap_if->eapKeyData)
+		return;
+
+	wpa_printf(MSG_DEBUG, "IEEE 802.1X: Sending EAPOL-Key(s) to " MACSTR,
+		   MAC2STR(sta->addr));
+
+#ifndef CONFIG_NO_VLAN
+	if (sta->vlan_id > 0) {
+		wpa_printf(MSG_ERROR, "Using WEP with vlans is not supported.");
+		return;
+	}
+#endif /* CONFIG_NO_VLAN */
+
+	if (eapol->default_wep_key) {
+		ieee802_1x_tx_key_one(hapd, sta, eapol->default_wep_key_idx, 1,
+				      eapol->default_wep_key,
+				      hapd->conf->default_wep_key_len);
+	}
+
+	if (hapd->conf->individual_wep_key_len > 0) {
+		u8 *ikey;
+
+		ikey = os_malloc(hapd->conf->individual_wep_key_len);
+		if (!ikey ||
+		    random_get_bytes(ikey, hapd->conf->individual_wep_key_len))
+		{
+			wpa_printf(MSG_ERROR,
+				   "Could not generate random individual WEP key");
+			os_free(ikey);
+			return;
+		}
+
+		wpa_hexdump_key(MSG_DEBUG, "Individual WEP key",
+				ikey, hapd->conf->individual_wep_key_len);
+
+		ieee802_1x_tx_key_one(hapd, sta, 0, 0, ikey,
+				      hapd->conf->individual_wep_key_len);
+
+		/* TODO: set encryption in TX callback, i.e., only after STA
+		 * has ACKed EAPOL-Key frame */
+		if (hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_WEP,
+					sta->addr, 0, 0, 1, NULL, 0, ikey,
+					hapd->conf->individual_wep_key_len,
+					KEY_FLAG_PAIRWISE_RX_TX)) {
+			wpa_printf(MSG_ERROR,
+				   "Could not set individual WEP encryption");
+		}
+
+		os_free(ikey);
+	}
+}
+
+#endif /* CONFIG_NO_RC4 */
+#endif /* CONFIG_FIPS */
+#endif /* CONFIG_WEP */
+
+
+const char *radius_mode_txt(struct hostapd_data *hapd)
+{
+	switch (hapd->iface->conf->hw_mode) {
+	case HOSTAPD_MODE_IEEE80211AD:
+		return "802.11ad";
+	case HOSTAPD_MODE_IEEE80211A:
+		return "802.11a";
+	case HOSTAPD_MODE_IEEE80211G:
+		return "802.11g";
+	case HOSTAPD_MODE_IEEE80211B:
+	default:
+		return "802.11b";
+	}
+}
+
+
+int radius_sta_rate(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	int i;
+	u8 rate = 0;
+
+	for (i = 0; i < sta->supported_rates_len; i++)
+		if ((sta->supported_rates[i] & 0x7f) > rate)
+			rate = sta->supported_rates[i] & 0x7f;
+
+	return rate;
+}
+
+
+#ifndef CONFIG_NO_RADIUS
+static void ieee802_1x_learn_identity(struct hostapd_data *hapd,
+				      struct eapol_state_machine *sm,
+				      const u8 *eap, size_t len)
+{
+	const u8 *identity;
+	size_t identity_len;
+	const struct eap_hdr *hdr = (const struct eap_hdr *) eap;
+
+	if (len <= sizeof(struct eap_hdr) ||
+	    (hdr->code == EAP_CODE_RESPONSE &&
+	     eap[sizeof(struct eap_hdr)] != EAP_TYPE_IDENTITY) ||
+	    (hdr->code == EAP_CODE_INITIATE &&
+	     eap[sizeof(struct eap_hdr)] != EAP_ERP_TYPE_REAUTH) ||
+	    (hdr->code != EAP_CODE_RESPONSE &&
+	     hdr->code != EAP_CODE_INITIATE))
+		return;
+
+	eap_erp_update_identity(sm->eap, eap, len);
+	identity = eap_get_identity(sm->eap, &identity_len);
+	if (!identity)
+		return;
+
+	/* Save station identity for future RADIUS packets */
+	os_free(sm->identity);
+	sm->identity = (u8 *) dup_binstr(identity, identity_len);
+	if (!sm->identity) {
+		sm->identity_len = 0;
+		return;
+	}
+
+	sm->identity_len = identity_len;
+	hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X,
+		       HOSTAPD_LEVEL_DEBUG, "STA identity '%s'", sm->identity);
+	sm->dot1xAuthEapolRespIdFramesRx++;
+}
+
+
+static int add_common_radius_sta_attr_rsn(struct hostapd_data *hapd,
+					  struct hostapd_radius_attr *req_attr,
+					  struct sta_info *sta,
+					  struct radius_msg *msg)
+{
+	u32 suite;
+	int ver, val;
+
+	ver = wpa_auth_sta_wpa_version(sta->wpa_sm);
+	val = wpa_auth_get_pairwise(sta->wpa_sm);
+	suite = wpa_cipher_to_suite(ver, val);
+	if (val != -1 &&
+	    !hostapd_config_get_radius_attr(req_attr,
+					    RADIUS_ATTR_WLAN_PAIRWISE_CIPHER) &&
+	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_WLAN_PAIRWISE_CIPHER,
+				       suite)) {
+		wpa_printf(MSG_ERROR, "Could not add WLAN-Pairwise-Cipher");
+		return -1;
+	}
+
+	suite = wpa_cipher_to_suite(((hapd->conf->wpa & 0x2) ||
+				     hapd->conf->osen) ?
+				    WPA_PROTO_RSN : WPA_PROTO_WPA,
+				    hapd->conf->wpa_group);
+	if (!hostapd_config_get_radius_attr(req_attr,
+					    RADIUS_ATTR_WLAN_GROUP_CIPHER) &&
+	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_WLAN_GROUP_CIPHER,
+				       suite)) {
+		wpa_printf(MSG_ERROR, "Could not add WLAN-Group-Cipher");
+		return -1;
+	}
+
+	val = wpa_auth_sta_key_mgmt(sta->wpa_sm);
+	suite = wpa_akm_to_suite(val);
+	if (val != -1 &&
+	    !hostapd_config_get_radius_attr(req_attr,
+					    RADIUS_ATTR_WLAN_AKM_SUITE) &&
+	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_WLAN_AKM_SUITE,
+				       suite)) {
+		wpa_printf(MSG_ERROR, "Could not add WLAN-AKM-Suite");
+		return -1;
+	}
+
+	if (hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+		suite = wpa_cipher_to_suite(WPA_PROTO_RSN,
+					    hapd->conf->group_mgmt_cipher);
+		if (!hostapd_config_get_radius_attr(
+			    req_attr, RADIUS_ATTR_WLAN_GROUP_MGMT_CIPHER) &&
+		    !radius_msg_add_attr_int32(
+			    msg, RADIUS_ATTR_WLAN_GROUP_MGMT_CIPHER, suite)) {
+			wpa_printf(MSG_ERROR,
+				   "Could not add WLAN-Group-Mgmt-Cipher");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+static int add_common_radius_sta_attr(struct hostapd_data *hapd,
+				      struct hostapd_radius_attr *req_attr,
+				      struct sta_info *sta,
+				      struct radius_msg *msg)
+{
+	char buf[128];
+
+	if (!hostapd_config_get_radius_attr(req_attr,
+					    RADIUS_ATTR_SERVICE_TYPE) &&
+	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_SERVICE_TYPE,
+				       RADIUS_SERVICE_TYPE_FRAMED)) {
+		wpa_printf(MSG_ERROR, "Could not add Service-Type");
+		return -1;
+	}
+
+	if (!hostapd_config_get_radius_attr(req_attr,
+					    RADIUS_ATTR_NAS_PORT) &&
+	    sta->aid > 0 &&
+	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) {
+		wpa_printf(MSG_ERROR, "Could not add NAS-Port");
+		return -1;
+	}
+
+	os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
+		    MAC2STR(sta->addr));
+	buf[sizeof(buf) - 1] = '\0';
+	if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
+				 (u8 *) buf, os_strlen(buf))) {
+		wpa_printf(MSG_ERROR, "Could not add Calling-Station-Id");
+		return -1;
+	}
+
+	if (sta->flags & WLAN_STA_PREAUTH) {
+		os_strlcpy(buf, "IEEE 802.11i Pre-Authentication",
+			   sizeof(buf));
+	} else {
+		os_snprintf(buf, sizeof(buf), "CONNECT %d%sMbps %s",
+			    radius_sta_rate(hapd, sta) / 2,
+			    (radius_sta_rate(hapd, sta) & 1) ? ".5" : "",
+			    radius_mode_txt(hapd));
+		buf[sizeof(buf) - 1] = '\0';
+	}
+	if (!hostapd_config_get_radius_attr(req_attr,
+					    RADIUS_ATTR_CONNECT_INFO) &&
+	    !radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
+				 (u8 *) buf, os_strlen(buf))) {
+		wpa_printf(MSG_ERROR, "Could not add Connect-Info");
+		return -1;
+	}
+
+	if (sta->acct_session_id) {
+		os_snprintf(buf, sizeof(buf), "%016llX",
+			    (unsigned long long) sta->acct_session_id);
+		if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
+					 (u8 *) buf, os_strlen(buf))) {
+			wpa_printf(MSG_ERROR, "Could not add Acct-Session-Id");
+			return -1;
+		}
+	}
+
+	if ((hapd->conf->wpa & 2) &&
+	    !hapd->conf->disable_pmksa_caching &&
+	    sta->eapol_sm && sta->eapol_sm->acct_multi_session_id) {
+		os_snprintf(buf, sizeof(buf), "%016llX",
+			    (unsigned long long)
+			    sta->eapol_sm->acct_multi_session_id);
+		if (!radius_msg_add_attr(
+			    msg, RADIUS_ATTR_ACCT_MULTI_SESSION_ID,
+			    (u8 *) buf, os_strlen(buf))) {
+			wpa_printf(MSG_INFO,
+				   "Could not add Acct-Multi-Session-Id");
+			return -1;
+		}
+	}
+
+#ifdef CONFIG_IEEE80211R_AP
+	if (hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) &&
+	    sta->wpa_sm &&
+	    (wpa_key_mgmt_ft(wpa_auth_sta_key_mgmt(sta->wpa_sm)) ||
+	     sta->auth_alg == WLAN_AUTH_FT) &&
+	    !hostapd_config_get_radius_attr(req_attr,
+					    RADIUS_ATTR_MOBILITY_DOMAIN_ID) &&
+	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_MOBILITY_DOMAIN_ID,
+				       WPA_GET_BE16(
+					       hapd->conf->mobility_domain))) {
+		wpa_printf(MSG_ERROR, "Could not add Mobility-Domain-Id");
+		return -1;
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+
+	if ((hapd->conf->wpa || hapd->conf->osen) && sta->wpa_sm &&
+	    add_common_radius_sta_attr_rsn(hapd, req_attr, sta, msg) < 0)
+		return -1;
+
+	return 0;
+}
+
+
+int add_common_radius_attr(struct hostapd_data *hapd,
+			   struct hostapd_radius_attr *req_attr,
+			   struct sta_info *sta,
+			   struct radius_msg *msg)
+{
+	char buf[128];
+	struct hostapd_radius_attr *attr;
+	int len;
+
+	if (hapd->conf->dynamic_own_ip_addr)
+		radius_client_get_local_addr(hapd->radius,
+					     &hapd->conf->own_ip_addr);
+
+	if (!hostapd_config_get_radius_attr(req_attr,
+					    RADIUS_ATTR_NAS_IP_ADDRESS) &&
+	    hapd->conf->own_ip_addr.af == AF_INET &&
+	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
+				 (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) {
+		wpa_printf(MSG_ERROR, "Could not add NAS-IP-Address");
+		return -1;
+	}
+
+#ifdef CONFIG_IPV6
+	if (!hostapd_config_get_radius_attr(req_attr,
+					    RADIUS_ATTR_NAS_IPV6_ADDRESS) &&
+	    hapd->conf->own_ip_addr.af == AF_INET6 &&
+	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS,
+				 (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) {
+		wpa_printf(MSG_ERROR, "Could not add NAS-IPv6-Address");
+		return -1;
+	}
+#endif /* CONFIG_IPV6 */
+
+	if (!hostapd_config_get_radius_attr(req_attr,
+					    RADIUS_ATTR_NAS_IDENTIFIER) &&
+	    hapd->conf->nas_identifier &&
+	    !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
+				 (u8 *) hapd->conf->nas_identifier,
+				 os_strlen(hapd->conf->nas_identifier))) {
+		wpa_printf(MSG_ERROR, "Could not add NAS-Identifier");
+		return -1;
+	}
+
+	len = os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":",
+			  MAC2STR(hapd->own_addr));
+	os_memcpy(&buf[len], hapd->conf->ssid.ssid,
+		  hapd->conf->ssid.ssid_len);
+	len += hapd->conf->ssid.ssid_len;
+	if (!hostapd_config_get_radius_attr(req_attr,
+					    RADIUS_ATTR_CALLED_STATION_ID) &&
+	    !radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID,
+				 (u8 *) buf, len)) {
+		wpa_printf(MSG_ERROR, "Could not add Called-Station-Id");
+		return -1;
+	}
+
+	if (!hostapd_config_get_radius_attr(req_attr,
+					    RADIUS_ATTR_NAS_PORT_TYPE) &&
+	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE,
+				       RADIUS_NAS_PORT_TYPE_IEEE_802_11)) {
+		wpa_printf(MSG_ERROR, "Could not add NAS-Port-Type");
+		return -1;
+	}
+
+#ifdef CONFIG_INTERWORKING
+	if (hapd->conf->interworking &&
+	    !is_zero_ether_addr(hapd->conf->hessid)) {
+		os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
+			    MAC2STR(hapd->conf->hessid));
+		buf[sizeof(buf) - 1] = '\0';
+		if (!hostapd_config_get_radius_attr(req_attr,
+						    RADIUS_ATTR_WLAN_HESSID) &&
+		    !radius_msg_add_attr(msg, RADIUS_ATTR_WLAN_HESSID,
+					 (u8 *) buf, os_strlen(buf))) {
+			wpa_printf(MSG_ERROR, "Could not add WLAN-HESSID");
+			return -1;
+		}
+	}
+#endif /* CONFIG_INTERWORKING */
+
+	if (sta && add_common_radius_sta_attr(hapd, req_attr, sta, msg) < 0)
+		return -1;
+
+	for (attr = req_attr; attr; attr = attr->next) {
+		if (!radius_msg_add_attr(msg, attr->type,
+					 wpabuf_head(attr->val),
+					 wpabuf_len(attr->val))) {
+			wpa_printf(MSG_ERROR, "Could not add RADIUS attribute");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+int add_sqlite_radius_attr(struct hostapd_data *hapd, struct sta_info *sta,
+			   struct radius_msg *msg, int acct)
+{
+#ifdef CONFIG_SQLITE
+	const char *attrtxt;
+	char addrtxt[3 * ETH_ALEN];
+	char *sql;
+	sqlite3_stmt *stmt = NULL;
+
+	if (!hapd->rad_attr_db)
+		return 0;
+
+	os_snprintf(addrtxt, sizeof(addrtxt), MACSTR, MAC2STR(sta->addr));
+
+	sql = "SELECT attr FROM radius_attributes WHERE sta=? AND (reqtype=? OR reqtype IS NULL);";
+	if (sqlite3_prepare_v2(hapd->rad_attr_db, sql, os_strlen(sql), &stmt,
+			       NULL) != SQLITE_OK) {
+		wpa_printf(MSG_ERROR, "DB: Failed to prepare SQL statement: %s",
+			   sqlite3_errmsg(hapd->rad_attr_db));
+		return -1;
+	}
+	sqlite3_bind_text(stmt, 1, addrtxt, os_strlen(addrtxt), SQLITE_STATIC);
+	sqlite3_bind_text(stmt, 2, acct ? "acct" : "auth", 4, SQLITE_STATIC);
+	while (sqlite3_step(stmt) == SQLITE_ROW) {
+		struct hostapd_radius_attr *attr;
+		struct radius_attr_hdr *hdr;
+
+		attrtxt = (const char *) sqlite3_column_text(stmt, 0);
+		attr = hostapd_parse_radius_attr(attrtxt);
+		if (!attr) {
+			wpa_printf(MSG_ERROR,
+				   "Skipping invalid attribute from SQL: %s",
+				   attrtxt);
+			continue;
+		}
+		wpa_printf(MSG_DEBUG, "Adding RADIUS attribute from SQL: %s",
+			   attrtxt);
+		hdr = radius_msg_add_attr(msg, attr->type,
+					  wpabuf_head(attr->val),
+					  wpabuf_len(attr->val));
+		hostapd_config_free_radius_attr(attr);
+		if (!hdr) {
+			wpa_printf(MSG_ERROR,
+				   "Could not add RADIUS attribute from SQL");
+			continue;
+		}
+	}
+
+	sqlite3_reset(stmt);
+	sqlite3_clear_bindings(stmt);
+	sqlite3_finalize(stmt);
+#endif /* CONFIG_SQLITE */
+
+	return 0;
+}
+
+
+void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
+				   struct sta_info *sta,
+				   const u8 *eap, size_t len)
+{
+	struct radius_msg *msg;
+	struct eapol_state_machine *sm = sta->eapol_sm;
+
+	if (!sm)
+		return;
+
+	ieee802_1x_learn_identity(hapd, sm, eap, len);
+
+	wpa_printf(MSG_DEBUG, "Encapsulating EAP message into a RADIUS packet");
+
+	sm->radius_identifier = radius_client_get_id(hapd->radius);
+	msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST,
+			     sm->radius_identifier);
+	if (!msg) {
+		wpa_printf(MSG_INFO, "Could not create new RADIUS packet");
+		return;
+	}
+
+	if (radius_msg_make_authenticator(msg) < 0) {
+		wpa_printf(MSG_INFO, "Could not make Request Authenticator");
+		goto fail;
+	}
+
+	if (sm->identity &&
+	    !radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME,
+				 sm->identity, sm->identity_len)) {
+		wpa_printf(MSG_INFO, "Could not add User-Name");
+		goto fail;
+	}
+
+	if (add_common_radius_attr(hapd, hapd->conf->radius_auth_req_attr, sta,
+				   msg) < 0)
+		goto fail;
+
+	if (sta && add_sqlite_radius_attr(hapd, sta, msg, 0) < 0)
+		goto fail;
+
+	/* TODO: should probably check MTU from driver config; 2304 is max for
+	 * IEEE 802.11, but use 1400 to avoid problems with too large packets
+	 */
+	if (!hostapd_config_get_radius_attr(hapd->conf->radius_auth_req_attr,
+					    RADIUS_ATTR_FRAMED_MTU) &&
+	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_FRAMED_MTU, 1400)) {
+		wpa_printf(MSG_INFO, "Could not add Framed-MTU");
+		goto fail;
+	}
+
+	if (!radius_msg_add_eap(msg, eap, len)) {
+		wpa_printf(MSG_INFO, "Could not add EAP-Message");
+		goto fail;
+	}
+
+	/* State attribute must be copied if and only if this packet is
+	 * Access-Request reply to the previous Access-Challenge */
+	if (sm->last_recv_radius &&
+	    radius_msg_get_hdr(sm->last_recv_radius)->code ==
+	    RADIUS_CODE_ACCESS_CHALLENGE) {
+		int res = radius_msg_copy_attr(msg, sm->last_recv_radius,
+					       RADIUS_ATTR_STATE);
+		if (res < 0) {
+			wpa_printf(MSG_INFO,
+				   "Could not copy State attribute from previous Access-Challenge");
+			goto fail;
+		}
+		if (res > 0)
+			wpa_printf(MSG_DEBUG, "Copied RADIUS State Attribute");
+	}
+
+	if (hapd->conf->radius_request_cui) {
+		const u8 *cui;
+		size_t cui_len;
+		/* Add previously learned CUI or nul CUI to request CUI */
+		if (sm->radius_cui) {
+			cui = wpabuf_head(sm->radius_cui);
+			cui_len = wpabuf_len(sm->radius_cui);
+		} else {
+			cui = (const u8 *) "\0";
+			cui_len = 1;
+		}
+		if (!radius_msg_add_attr(msg,
+					 RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+					 cui, cui_len)) {
+			wpa_printf(MSG_ERROR, "Could not add CUI");
+			goto fail;
+		}
+	}
+
+#ifdef CONFIG_HS20
+	if (hapd->conf->hs20) {
+		u8 ver = hapd->conf->hs20_release - 1;
+
+		if (!radius_msg_add_wfa(
+			    msg, RADIUS_VENDOR_ATTR_WFA_HS20_AP_VERSION,
+			    &ver, 1)) {
+			wpa_printf(MSG_ERROR,
+				   "Could not add HS 2.0 AP version");
+			goto fail;
+		}
+
+		if (sta->hs20_ie && wpabuf_len(sta->hs20_ie) > 0) {
+			const u8 *pos;
+			u8 buf[3];
+			u16 id;
+
+			pos = wpabuf_head_u8(sta->hs20_ie);
+			buf[0] = (*pos) >> 4;
+			if (((*pos) & HS20_PPS_MO_ID_PRESENT) &&
+			    wpabuf_len(sta->hs20_ie) >= 3)
+				id = WPA_GET_LE16(pos + 1);
+			else
+				id = 0;
+			WPA_PUT_BE16(buf + 1, id);
+			if (!radius_msg_add_wfa(
+				    msg,
+				    RADIUS_VENDOR_ATTR_WFA_HS20_STA_VERSION,
+				    buf, sizeof(buf))) {
+				wpa_printf(MSG_ERROR,
+					   "Could not add HS 2.0 STA version");
+				goto fail;
+			}
+		}
+
+		if (sta->roaming_consortium &&
+		    !radius_msg_add_wfa(
+			    msg, RADIUS_VENDOR_ATTR_WFA_HS20_ROAMING_CONSORTIUM,
+			    wpabuf_head(sta->roaming_consortium),
+			    wpabuf_len(sta->roaming_consortium))) {
+			wpa_printf(MSG_ERROR,
+				   "Could not add HS 2.0 Roaming Consortium");
+			goto fail;
+		}
+
+		if (hapd->conf->t_c_filename) {
+			be32 timestamp;
+
+			if (!radius_msg_add_wfa(
+				    msg,
+				    RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILENAME,
+				    (const u8 *) hapd->conf->t_c_filename,
+				    os_strlen(hapd->conf->t_c_filename))) {
+				wpa_printf(MSG_ERROR,
+					   "Could not add HS 2.0 T&C Filename");
+				goto fail;
+			}
+
+			timestamp = host_to_be32(hapd->conf->t_c_timestamp);
+			if (!radius_msg_add_wfa(
+				    msg,
+				    RADIUS_VENDOR_ATTR_WFA_HS20_TIMESTAMP,
+				    (const u8 *) &timestamp,
+				    sizeof(timestamp))) {
+				wpa_printf(MSG_ERROR,
+					   "Could not add HS 2.0 Timestamp");
+				goto fail;
+			}
+		}
+	}
+#endif /* CONFIG_HS20 */
+
+	if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, sta->addr) < 0)
+		goto fail;
+
+	return;
+
+ fail:
+	radius_msg_free(msg);
+}
+#endif /* CONFIG_NO_RADIUS */
+
+
+static void handle_eap_response(struct hostapd_data *hapd,
+				struct sta_info *sta, struct eap_hdr *eap,
+				size_t len)
+{
+	u8 type, *data;
+	struct eapol_state_machine *sm = sta->eapol_sm;
+
+	if (!sm)
+		return;
+
+	data = (u8 *) (eap + 1);
+
+	if (len < sizeof(*eap) + 1) {
+		wpa_printf(MSG_INFO, "%s: too short response data", __func__);
+		return;
+	}
+
+	sm->eap_type_supp = type = data[0];
+
+	hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X,
+		       HOSTAPD_LEVEL_DEBUG, "received EAP packet (code=%d "
+		       "id=%d len=%d) from STA: EAP Response-%s (%d)",
+		       eap->code, eap->identifier, be_to_host16(eap->length),
+		       eap_server_get_name(0, type), type);
+
+	sm->dot1xAuthEapolRespFramesRx++;
+
+	wpabuf_free(sm->eap_if->eapRespData);
+	sm->eap_if->eapRespData = wpabuf_alloc_copy(eap, len);
+	sm->eapolEap = true;
+}
+
+
+static void handle_eap_initiate(struct hostapd_data *hapd,
+				struct sta_info *sta, struct eap_hdr *eap,
+				size_t len)
+{
+#ifdef CONFIG_ERP
+	u8 type, *data;
+	struct eapol_state_machine *sm = sta->eapol_sm;
+
+	if (!sm)
+		return;
+
+	if (len < sizeof(*eap) + 1) {
+		wpa_printf(MSG_INFO, "%s: too short response data", __func__);
+		return;
+	}
+
+	data = (u8 *) (eap + 1);
+	type = data[0];
+
+	hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "received EAP packet (code=%d id=%d len=%d) from STA: EAP Initiate type %u",
+		       eap->code, eap->identifier, be_to_host16(eap->length),
+		       type);
+
+	wpabuf_free(sm->eap_if->eapRespData);
+	sm->eap_if->eapRespData = wpabuf_alloc_copy(eap, len);
+	sm->eapolEap = true;
+#endif /* CONFIG_ERP */
+}
+
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+static const char * eap_code_str(u8 code)
+{
+	switch (code) {
+	case EAP_CODE_REQUEST:
+		return "request";
+	case EAP_CODE_RESPONSE:
+		return "response";
+	case EAP_CODE_SUCCESS:
+		return "success";
+	case EAP_CODE_FAILURE:
+		return "failure";
+	case EAP_CODE_INITIATE:
+		return "initiate";
+	case EAP_CODE_FINISH:
+		return "finish";
+	default:
+		return "unknown";
+	}
+}
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+/* Process incoming EAP packet from Supplicant */
+static void handle_eap(struct hostapd_data *hapd, struct sta_info *sta,
+		       u8 *buf, size_t len)
+{
+	struct eap_hdr *eap;
+	u16 eap_len;
+
+	if (len < sizeof(*eap)) {
+		wpa_printf(MSG_INFO, "   too short EAP packet");
+		return;
+	}
+
+	eap = (struct eap_hdr *) buf;
+
+	eap_len = be_to_host16(eap->length);
+	wpa_printf(MSG_DEBUG, "EAP: code=%d (%s) identifier=%d length=%d",
+		   eap->code, eap_code_str(eap->code), eap->identifier,
+		   eap_len);
+	if (eap_len < sizeof(*eap)) {
+		wpa_printf(MSG_DEBUG, "   Invalid EAP length");
+		return;
+	} else if (eap_len > len) {
+		wpa_printf(MSG_DEBUG,
+			   "   Too short frame to contain this EAP packet");
+		return;
+	} else if (eap_len < len) {
+		wpa_printf(MSG_DEBUG,
+			   "   Ignoring %lu extra bytes after EAP packet",
+			   (unsigned long) len - eap_len);
+	}
+
+	switch (eap->code) {
+	case EAP_CODE_RESPONSE:
+		handle_eap_response(hapd, sta, eap, eap_len);
+		break;
+	case EAP_CODE_INITIATE:
+		handle_eap_initiate(hapd, sta, eap, eap_len);
+		break;
+	}
+}
+
+
+struct eapol_state_machine *
+ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	int flags = 0;
+
+	if (sta->flags & WLAN_STA_PREAUTH)
+		flags |= EAPOL_SM_PREAUTH;
+	if (sta->wpa_sm) {
+		flags |= EAPOL_SM_USES_WPA;
+		if (wpa_auth_sta_get_pmksa(sta->wpa_sm))
+			flags |= EAPOL_SM_FROM_PMKSA_CACHE;
+	}
+	return eapol_auth_alloc(hapd->eapol_auth, sta->addr, flags,
+				sta->wps_ie, sta->p2p_ie, sta,
+				sta->identity, sta->radius_cui);
+}
+
+
+static void ieee802_1x_save_eapol(struct sta_info *sta, const u8 *buf,
+				  size_t len, enum frame_encryption encrypted)
+{
+	if (sta->pending_eapol_rx) {
+		wpabuf_free(sta->pending_eapol_rx->buf);
+	} else {
+		sta->pending_eapol_rx =
+			os_malloc(sizeof(*sta->pending_eapol_rx));
+		if (!sta->pending_eapol_rx)
+			return;
+	}
+
+	sta->pending_eapol_rx->buf = wpabuf_alloc_copy(buf, len);
+	if (!sta->pending_eapol_rx->buf) {
+		os_free(sta->pending_eapol_rx);
+		sta->pending_eapol_rx = NULL;
+		return;
+	}
+
+	sta->pending_eapol_rx->encrypted = encrypted;
+	os_get_reltime(&sta->pending_eapol_rx->rx_time);
+}
+
+
+static bool ieee802_1x_check_encryption(struct sta_info *sta,
+					enum frame_encryption encrypted,
+					u8 type)
+{
+	if (encrypted != FRAME_NOT_ENCRYPTED)
+		return true;
+	if (type != IEEE802_1X_TYPE_EAP_PACKET &&
+	    type != IEEE802_1X_TYPE_EAPOL_START &&
+	    type != IEEE802_1X_TYPE_EAPOL_LOGOFF)
+		return true;
+	if (!(sta->flags & WLAN_STA_MFP))
+		return true;
+	return !wpa_auth_pairwise_set(sta->wpa_sm);
+}
+
+
+/**
+ * ieee802_1x_receive - Process the EAPOL frames from the Supplicant
+ * @hapd: hostapd BSS data
+ * @sa: Source address (sender of the EAPOL frame)
+ * @buf: EAPOL frame
+ * @len: Length of buf in octets
+ * @encrypted: Whether the frame was encrypted
+ *
+ * This function is called for each incoming EAPOL frame from the interface
+ */
+void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
+			size_t len, enum frame_encryption encrypted)
+{
+	struct sta_info *sta;
+	struct ieee802_1x_hdr *hdr;
+	struct ieee802_1x_eapol_key *key;
+	u16 datalen;
+	struct rsn_pmksa_cache_entry *pmksa;
+	int key_mgmt;
+
+	if (!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen &&
+	    !hapd->conf->wps_state)
+		return;
+
+	wpa_printf(MSG_DEBUG, "IEEE 802.1X: %lu bytes from " MACSTR
+		   " (encrypted=%d)",
+		   (unsigned long) len, MAC2STR(sa), encrypted);
+	sta = ap_get_sta(hapd, sa);
+	if (!sta || (!(sta->flags & (WLAN_STA_ASSOC | WLAN_STA_PREAUTH)) &&
+		     !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED))) {
+		wpa_printf(MSG_DEBUG,
+			   "IEEE 802.1X data frame from not associated/Pre-authenticating STA");
+
+		if (sta && (sta->flags & WLAN_STA_AUTH)) {
+			wpa_printf(MSG_DEBUG, "Saving EAPOL frame from " MACSTR
+				   " for later use", MAC2STR(sta->addr));
+			ieee802_1x_save_eapol(sta, buf, len, encrypted);
+		}
+
+		return;
+	}
+
+	if (len < sizeof(*hdr)) {
+		wpa_printf(MSG_INFO, "   too short IEEE 802.1X packet");
+		return;
+	}
+
+	hdr = (struct ieee802_1x_hdr *) buf;
+	datalen = be_to_host16(hdr->length);
+	wpa_printf(MSG_DEBUG, "   IEEE 802.1X: version=%d type=%d length=%d",
+		   hdr->version, hdr->type, datalen);
+
+	if (len - sizeof(*hdr) < datalen) {
+		wpa_printf(MSG_INFO,
+			   "   frame too short for this IEEE 802.1X packet");
+		if (sta->eapol_sm)
+			sta->eapol_sm->dot1xAuthEapLengthErrorFramesRx++;
+		return;
+	}
+	if (len - sizeof(*hdr) > datalen) {
+		wpa_printf(MSG_DEBUG,
+			   "   ignoring %lu extra octets after IEEE 802.1X packet",
+			   (unsigned long) len - sizeof(*hdr) - datalen);
+	}
+
+	if (sta->eapol_sm) {
+		sta->eapol_sm->dot1xAuthLastEapolFrameVersion = hdr->version;
+		sta->eapol_sm->dot1xAuthEapolFramesRx++;
+	}
+
+	key = (struct ieee802_1x_eapol_key *) (hdr + 1);
+	if (datalen >= sizeof(struct ieee802_1x_eapol_key) &&
+	    hdr->type == IEEE802_1X_TYPE_EAPOL_KEY &&
+	    (key->type == EAPOL_KEY_TYPE_WPA ||
+	     key->type == EAPOL_KEY_TYPE_RSN)) {
+		wpa_receive(hapd->wpa_auth, sta->wpa_sm, (u8 *) hdr,
+			    sizeof(*hdr) + datalen);
+		return;
+	}
+
+	if (!hapd->conf->ieee802_1x && !hapd->conf->osen &&
+	    !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) {
+		wpa_printf(MSG_DEBUG,
+			   "IEEE 802.1X: Ignore EAPOL message - 802.1X not enabled and WPS not used");
+		return;
+	}
+
+	key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm);
+	if (key_mgmt != -1 &&
+	    (wpa_key_mgmt_wpa_psk(key_mgmt) || key_mgmt == WPA_KEY_MGMT_OWE ||
+	     key_mgmt == WPA_KEY_MGMT_DPP)) {
+		wpa_printf(MSG_DEBUG,
+			   "IEEE 802.1X: Ignore EAPOL message - STA is using PSK");
+		return;
+	}
+
+	if (!ieee802_1x_check_encryption(sta, encrypted, hdr->type)) {
+		wpa_printf(MSG_DEBUG,
+			   "IEEE 802.1X: Discard unencrypted EAPOL message - encryption was expected");
+		return;
+	}
+
+	if (!sta->eapol_sm) {
+		sta->eapol_sm = ieee802_1x_alloc_eapol_sm(hapd, sta);
+		if (!sta->eapol_sm)
+			return;
+
+#ifdef CONFIG_WPS
+		if (!hapd->conf->ieee802_1x && hapd->conf->wps_state) {
+			u32 wflags = sta->flags & (WLAN_STA_WPS |
+						   WLAN_STA_WPS2 |
+						   WLAN_STA_MAYBE_WPS);
+			if (wflags == WLAN_STA_MAYBE_WPS ||
+			    wflags == (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)) {
+				/*
+				 * Delay EAPOL frame transmission until a
+				 * possible WPS STA initiates the handshake
+				 * with EAPOL-Start. Only allow the wait to be
+				 * skipped if the STA is known to support WPS
+				 * 2.0.
+				 */
+				wpa_printf(MSG_DEBUG,
+					   "WPS: Do not start EAPOL until EAPOL-Start is received");
+				sta->eapol_sm->flags |= EAPOL_SM_WAIT_START;
+			}
+		}
+#endif /* CONFIG_WPS */
+
+		sta->eapol_sm->eap_if->portEnabled = true;
+	}
+
+	/* since we support version 1, we can ignore version field and proceed
+	 * as specified in version 1 standard [IEEE Std 802.1X-2001, 7.5.5] */
+	/* TODO: actually, we are not version 1 anymore.. However, Version 2
+	 * does not change frame contents, so should be ok to process frames
+	 * more or less identically. Some changes might be needed for
+	 * verification of fields. */
+
+	switch (hdr->type) {
+	case IEEE802_1X_TYPE_EAP_PACKET:
+		handle_eap(hapd, sta, (u8 *) (hdr + 1), datalen);
+		break;
+
+	case IEEE802_1X_TYPE_EAPOL_START:
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "received EAPOL-Start from STA");
+		sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START;
+		pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
+		if (pmksa) {
+			hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "cached PMKSA available - ignore it since STA sent EAPOL-Start");
+			wpa_auth_sta_clear_pmksa(sta->wpa_sm, pmksa);
+		}
+		sta->eapol_sm->eapolStart = true;
+		sta->eapol_sm->dot1xAuthEapolStartFramesRx++;
+		eap_server_clear_identity(sta->eapol_sm->eap);
+		wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH_EAPOL);
+		break;
+
+	case IEEE802_1X_TYPE_EAPOL_LOGOFF:
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "received EAPOL-Logoff from STA");
+		sta->acct_terminate_cause =
+			RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
+		accounting_sta_stop(hapd, sta);
+		sta->eapol_sm->eapolLogoff = true;
+		sta->eapol_sm->dot1xAuthEapolLogoffFramesRx++;
+		eap_server_clear_identity(sta->eapol_sm->eap);
+		break;
+
+	case IEEE802_1X_TYPE_EAPOL_KEY:
+		wpa_printf(MSG_DEBUG, "   EAPOL-Key");
+		if (!ap_sta_is_authorized(sta)) {
+			wpa_printf(MSG_DEBUG,
+				   "   Dropped key data from unauthorized Supplicant");
+			break;
+		}
+		break;
+
+	case IEEE802_1X_TYPE_EAPOL_ENCAPSULATED_ASF_ALERT:
+		wpa_printf(MSG_DEBUG, "   EAPOL-Encapsulated-ASF-Alert");
+		/* TODO: implement support for this; show data */
+		break;
+
+#ifdef CONFIG_MACSEC
+	case IEEE802_1X_TYPE_EAPOL_MKA:
+		wpa_printf(MSG_EXCESSIVE,
+			   "EAPOL type %d will be handled by MKA", hdr->type);
+		break;
+#endif /* CONFIG_MACSEC */
+
+	default:
+		wpa_printf(MSG_DEBUG, "   unknown IEEE 802.1X packet type");
+		sta->eapol_sm->dot1xAuthInvalidEapolFramesRx++;
+		break;
+	}
+
+	eapol_auth_step(sta->eapol_sm);
+}
+
+
+/**
+ * ieee802_1x_new_station - Start IEEE 802.1X authentication
+ * @hapd: hostapd BSS data
+ * @sta: The station
+ *
+ * This function is called to start IEEE 802.1X authentication when a new
+ * station completes IEEE 802.11 association.
+ */
+void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	struct rsn_pmksa_cache_entry *pmksa;
+	int reassoc = 1;
+	int force_1x = 0;
+	int key_mgmt;
+
+#ifdef CONFIG_WPS
+	if (hapd->conf->wps_state &&
+	    ((hapd->conf->wpa && (sta->flags & WLAN_STA_MAYBE_WPS)) ||
+	     (sta->flags & WLAN_STA_WPS))) {
+		/*
+		 * Need to enable IEEE 802.1X/EAPOL state machines for possible
+		 * WPS handshake even if IEEE 802.1X/EAPOL is not used for
+		 * authentication in this BSS.
+		 */
+		force_1x = 1;
+	}
+#endif /* CONFIG_WPS */
+
+	if (!force_1x && !hapd->conf->ieee802_1x && !hapd->conf->osen) {
+		wpa_printf(MSG_DEBUG,
+			   "IEEE 802.1X: Ignore STA - 802.1X not enabled or forced for WPS");
+		/*
+		 * Clear any possible EAPOL authenticator state to support
+		 * reassociation change from WPS to PSK.
+		 */
+		ieee802_1x_free_station(hapd, sta);
+		return;
+	}
+
+	key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm);
+	if (key_mgmt != -1 &&
+	    (wpa_key_mgmt_wpa_psk(key_mgmt) || key_mgmt == WPA_KEY_MGMT_OWE ||
+	     key_mgmt == WPA_KEY_MGMT_DPP)) {
+		wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore STA - using PSK");
+		/*
+		 * Clear any possible EAPOL authenticator state to support
+		 * reassociation change from WPA-EAP to PSK.
+		 */
+		ieee802_1x_free_station(hapd, sta);
+		return;
+	}
+
+	if (!sta->eapol_sm) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+			       HOSTAPD_LEVEL_DEBUG, "start authentication");
+		sta->eapol_sm = ieee802_1x_alloc_eapol_sm(hapd, sta);
+		if (!sta->eapol_sm) {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE8021X,
+				       HOSTAPD_LEVEL_INFO,
+				       "failed to allocate state machine");
+			return;
+		}
+		reassoc = 0;
+	}
+
+#ifdef CONFIG_WPS
+	sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START;
+	if (!hapd->conf->ieee802_1x && hapd->conf->wps_state &&
+	    !(sta->flags & WLAN_STA_WPS2)) {
+		/*
+		 * Delay EAPOL frame transmission until a possible WPS STA
+		 * initiates the handshake with EAPOL-Start. Only allow the
+		 * wait to be skipped if the STA is known to support WPS 2.0.
+		 */
+		wpa_printf(MSG_DEBUG,
+			   "WPS: Do not start EAPOL until EAPOL-Start is received");
+		sta->eapol_sm->flags |= EAPOL_SM_WAIT_START;
+	}
+#endif /* CONFIG_WPS */
+
+	sta->eapol_sm->eap_if->portEnabled = true;
+
+#ifdef CONFIG_IEEE80211R_AP
+	if (sta->auth_alg == WLAN_AUTH_FT) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "PMK from FT - skip IEEE 802.1X/EAP");
+		/* Setup EAPOL state machines to already authenticated state
+		 * because of existing FT information from R0KH. */
+		sta->eapol_sm->keyRun = true;
+		sta->eapol_sm->eap_if->eapKeyAvailable = true;
+		sta->eapol_sm->auth_pae_state = AUTH_PAE_AUTHENTICATING;
+		sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS;
+		sta->eapol_sm->authSuccess = true;
+		sta->eapol_sm->authFail = false;
+		sta->eapol_sm->portValid = true;
+		if (sta->eapol_sm->eap)
+			eap_sm_notify_cached(sta->eapol_sm->eap);
+		ap_sta_bind_vlan(hapd, sta);
+		return;
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+
+#ifdef CONFIG_FILS
+	if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+	    sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+	    sta->auth_alg == WLAN_AUTH_FILS_PK) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "PMK from FILS - skip IEEE 802.1X/EAP");
+		/* Setup EAPOL state machines to already authenticated state
+		 * because of existing FILS information. */
+		sta->eapol_sm->keyRun = true;
+		sta->eapol_sm->eap_if->eapKeyAvailable = true;
+		sta->eapol_sm->auth_pae_state = AUTH_PAE_AUTHENTICATING;
+		sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS;
+		sta->eapol_sm->authSuccess = true;
+		sta->eapol_sm->authFail = false;
+		sta->eapol_sm->portValid = true;
+		if (sta->eapol_sm->eap)
+			eap_sm_notify_cached(sta->eapol_sm->eap);
+		wpa_auth_set_ptk_rekey_timer(sta->wpa_sm);
+		return;
+	}
+#endif /* CONFIG_FILS */
+
+	pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
+	if (pmksa) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "PMK from PMKSA cache - skip IEEE 802.1X/EAP");
+		/* Setup EAPOL state machines to already authenticated state
+		 * because of existing PMKSA information in the cache. */
+		sta->eapol_sm->keyRun = true;
+		sta->eapol_sm->eap_if->eapKeyAvailable = true;
+		sta->eapol_sm->auth_pae_state = AUTH_PAE_AUTHENTICATING;
+		sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS;
+		sta->eapol_sm->authSuccess = true;
+		sta->eapol_sm->authFail = false;
+		if (sta->eapol_sm->eap)
+			eap_sm_notify_cached(sta->eapol_sm->eap);
+		pmksa_cache_to_eapol_data(hapd, pmksa, sta->eapol_sm);
+		ap_sta_bind_vlan(hapd, sta);
+	} else {
+		if (reassoc) {
+			/*
+			 * Force EAPOL state machines to start
+			 * re-authentication without having to wait for the
+			 * Supplicant to send EAPOL-Start.
+			 */
+			sta->eapol_sm->reAuthenticate = true;
+		}
+		eapol_auth_step(sta->eapol_sm);
+	}
+}
+
+
+void ieee802_1x_free_station(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	struct eapol_state_machine *sm = sta->eapol_sm;
+
+#ifdef CONFIG_HS20
+	eloop_cancel_timeout(ieee802_1x_wnm_notif_send, hapd, sta);
+#endif /* CONFIG_HS20 */
+
+	if (sta->pending_eapol_rx) {
+		wpabuf_free(sta->pending_eapol_rx->buf);
+		os_free(sta->pending_eapol_rx);
+		sta->pending_eapol_rx = NULL;
+	}
+
+	if (!sm)
+		return;
+
+	sta->eapol_sm = NULL;
+
+#ifndef CONFIG_NO_RADIUS
+	radius_msg_free(sm->last_recv_radius);
+	radius_free_class(&sm->radius_class);
+#endif /* CONFIG_NO_RADIUS */
+
+	eapol_auth_free(sm);
+}
+
+
+#ifndef CONFIG_NO_RADIUS
+static void ieee802_1x_decapsulate_radius(struct hostapd_data *hapd,
+					  struct sta_info *sta)
+{
+	struct wpabuf *eap;
+	const struct eap_hdr *hdr;
+	int eap_type = -1;
+	char buf[64];
+	struct radius_msg *msg;
+	struct eapol_state_machine *sm = sta->eapol_sm;
+
+	if (!sm || !sm->last_recv_radius) {
+		if (sm)
+			sm->eap_if->aaaEapNoReq = true;
+		return;
+	}
+
+	msg = sm->last_recv_radius;
+
+	eap = radius_msg_get_eap(msg);
+	if (!eap) {
+		/* RFC 3579, Chap. 2.6.3:
+		 * RADIUS server SHOULD NOT send Access-Reject/no EAP-Message
+		 * attribute */
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+			       HOSTAPD_LEVEL_WARNING,
+			       "could not extract EAP-Message from RADIUS message");
+		sm->eap_if->aaaEapNoReq = true;
+		return;
+	}
+
+	if (wpabuf_len(eap) < sizeof(*hdr)) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+			       HOSTAPD_LEVEL_WARNING,
+			       "too short EAP packet received from authentication server");
+		wpabuf_free(eap);
+		sm->eap_if->aaaEapNoReq = true;
+		return;
+	}
+
+	if (wpabuf_len(eap) > sizeof(*hdr))
+		eap_type = (wpabuf_head_u8(eap))[sizeof(*hdr)];
+
+	hdr = wpabuf_head(eap);
+	switch (hdr->code) {
+	case EAP_CODE_REQUEST:
+		if (eap_type >= 0)
+			sm->eap_type_authsrv = eap_type;
+		os_snprintf(buf, sizeof(buf), "EAP-Request-%s (%d)",
+			    eap_server_get_name(0, eap_type), eap_type);
+		break;
+	case EAP_CODE_RESPONSE:
+		os_snprintf(buf, sizeof(buf), "EAP Response-%s (%d)",
+			    eap_server_get_name(0, eap_type), eap_type);
+		break;
+	case EAP_CODE_SUCCESS:
+		os_strlcpy(buf, "EAP Success", sizeof(buf));
+		break;
+	case EAP_CODE_FAILURE:
+		os_strlcpy(buf, "EAP Failure", sizeof(buf));
+		break;
+	default:
+		os_strlcpy(buf, "unknown EAP code", sizeof(buf));
+		break;
+	}
+	buf[sizeof(buf) - 1] = '\0';
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "decapsulated EAP packet (code=%d id=%d len=%d) from RADIUS server: %s",
+		       hdr->code, hdr->identifier, be_to_host16(hdr->length),
+		       buf);
+	sm->eap_if->aaaEapReq = true;
+
+	wpabuf_free(sm->eap_if->aaaEapReqData);
+	sm->eap_if->aaaEapReqData = eap;
+}
+
+
+static void ieee802_1x_get_keys(struct hostapd_data *hapd,
+				struct sta_info *sta, struct radius_msg *msg,
+				struct radius_msg *req,
+				const u8 *shared_secret,
+				size_t shared_secret_len)
+{
+	struct radius_ms_mppe_keys *keys;
+	u8 *buf;
+	size_t len;
+	struct eapol_state_machine *sm = sta->eapol_sm;
+
+	if (!sm)
+		return;
+
+	keys = radius_msg_get_ms_keys(msg, req, shared_secret,
+				      shared_secret_len);
+
+	if (keys && keys->send && keys->recv) {
+		len = keys->send_len + keys->recv_len;
+		wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Send-Key",
+				keys->send, keys->send_len);
+		wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Recv-Key",
+				keys->recv, keys->recv_len);
+
+		os_free(sm->eap_if->aaaEapKeyData);
+		sm->eap_if->aaaEapKeyData = os_malloc(len);
+		if (sm->eap_if->aaaEapKeyData) {
+			os_memcpy(sm->eap_if->aaaEapKeyData, keys->recv,
+				  keys->recv_len);
+			os_memcpy(sm->eap_if->aaaEapKeyData + keys->recv_len,
+				  keys->send, keys->send_len);
+			sm->eap_if->aaaEapKeyDataLen = len;
+			sm->eap_if->aaaEapKeyAvailable = true;
+		}
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "MS-MPPE: 1x_get_keys, could not get keys: %p  send: %p  recv: %p",
+			   keys, keys ? keys->send : NULL,
+			   keys ? keys->recv : NULL);
+	}
+
+	if (keys) {
+		os_free(keys->send);
+		os_free(keys->recv);
+		os_free(keys);
+	}
+
+	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_EAP_KEY_NAME, &buf, &len,
+				    NULL) == 0) {
+		os_free(sm->eap_if->eapSessionId);
+		sm->eap_if->eapSessionId = os_memdup(buf, len);
+		if (sm->eap_if->eapSessionId) {
+			sm->eap_if->eapSessionIdLen = len;
+			wpa_hexdump(MSG_DEBUG, "EAP-Key Name",
+				    sm->eap_if->eapSessionId,
+				    sm->eap_if->eapSessionIdLen);
+		}
+	} else {
+		sm->eap_if->eapSessionIdLen = 0;
+	}
+}
+
+
+static void ieee802_1x_store_radius_class(struct hostapd_data *hapd,
+					  struct sta_info *sta,
+					  struct radius_msg *msg)
+{
+	u8 *attr_class;
+	size_t class_len;
+	struct eapol_state_machine *sm = sta->eapol_sm;
+	int count, i;
+	struct radius_attr_data *nclass;
+	size_t nclass_count;
+
+	if (!hapd->conf->radius->acct_server || !hapd->radius || !sm)
+		return;
+
+	radius_free_class(&sm->radius_class);
+	count = radius_msg_count_attr(msg, RADIUS_ATTR_CLASS, 1);
+	if (count <= 0)
+		return;
+
+	nclass = os_calloc(count, sizeof(struct radius_attr_data));
+	if (!nclass)
+		return;
+
+	nclass_count = 0;
+
+	attr_class = NULL;
+	for (i = 0; i < count; i++) {
+		do {
+			if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CLASS,
+						    &attr_class, &class_len,
+						    attr_class) < 0) {
+				i = count;
+				break;
+			}
+		} while (class_len < 1);
+
+		nclass[nclass_count].data = os_memdup(attr_class, class_len);
+		if (!nclass[nclass_count].data)
+			break;
+
+		nclass[nclass_count].len = class_len;
+		nclass_count++;
+	}
+
+	sm->radius_class.attr = nclass;
+	sm->radius_class.count = nclass_count;
+	wpa_printf(MSG_DEBUG,
+		   "IEEE 802.1X: Stored %lu RADIUS Class attributes for "
+		   MACSTR,
+		   (unsigned long) sm->radius_class.count,
+		   MAC2STR(sta->addr));
+}
+
+
+/* Update sta->identity based on User-Name attribute in Access-Accept */
+static void ieee802_1x_update_sta_identity(struct hostapd_data *hapd,
+					   struct sta_info *sta,
+					   struct radius_msg *msg)
+{
+	u8 *buf, *identity;
+	size_t len;
+	struct eapol_state_machine *sm = sta->eapol_sm;
+
+	if (!sm)
+		return;
+
+	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME, &buf, &len,
+				    NULL) < 0)
+		return;
+
+	identity = (u8 *) dup_binstr(buf, len);
+	if (!identity)
+		return;
+
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "old identity '%s' updated with User-Name from Access-Accept '%s'",
+		       sm->identity ? (char *) sm->identity : "N/A",
+		       (char *) identity);
+
+	os_free(sm->identity);
+	sm->identity = identity;
+	sm->identity_len = len;
+}
+
+
+/* Update CUI based on Chargeable-User-Identity attribute in Access-Accept */
+static void ieee802_1x_update_sta_cui(struct hostapd_data *hapd,
+				      struct sta_info *sta,
+				      struct radius_msg *msg)
+{
+	struct eapol_state_machine *sm = sta->eapol_sm;
+	struct wpabuf *cui;
+	u8 *buf;
+	size_t len;
+
+	if (!sm)
+		return;
+
+	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+				    &buf, &len, NULL) < 0)
+		return;
+
+	cui = wpabuf_alloc_copy(buf, len);
+	if (!cui)
+		return;
+
+	wpabuf_free(sm->radius_cui);
+	sm->radius_cui = cui;
+}
+
+
+#ifdef CONFIG_HS20
+
+static void ieee802_1x_hs20_sub_rem(struct sta_info *sta, u8 *pos, size_t len)
+{
+	sta->remediation = 1;
+	os_free(sta->remediation_url);
+	if (len > 2) {
+		sta->remediation_url = os_malloc(len);
+		if (!sta->remediation_url)
+			return;
+		sta->remediation_method = pos[0];
+		os_memcpy(sta->remediation_url, pos + 1, len - 1);
+		sta->remediation_url[len - 1] = '\0';
+		wpa_printf(MSG_DEBUG,
+			   "HS 2.0: Subscription remediation needed for "
+			   MACSTR " - server method %u URL %s",
+			   MAC2STR(sta->addr), sta->remediation_method,
+			   sta->remediation_url);
+	} else {
+		sta->remediation_url = NULL;
+		wpa_printf(MSG_DEBUG,
+			   "HS 2.0: Subscription remediation needed for "
+			   MACSTR, MAC2STR(sta->addr));
+	}
+	/* TODO: assign the STA into remediation VLAN or add filtering */
+}
+
+
+static void ieee802_1x_hs20_deauth_req(struct hostapd_data *hapd,
+				       struct sta_info *sta, const u8 *pos,
+				       size_t len)
+{
+	size_t url_len;
+	unsigned int timeout;
+
+	if (len < 3)
+		return; /* Malformed information */
+	url_len = len - 3;
+	sta->hs20_deauth_requested = 1;
+	sta->hs20_deauth_on_ack = url_len == 0;
+	wpa_printf(MSG_DEBUG,
+		   "HS 2.0: Deauthentication request - Code %u  Re-auth Delay %u  URL length %zu",
+		   *pos, WPA_GET_LE16(pos + 1), url_len);
+	wpabuf_free(sta->hs20_deauth_req);
+	sta->hs20_deauth_req = wpabuf_alloc(len + 1);
+	if (sta->hs20_deauth_req) {
+		wpabuf_put_data(sta->hs20_deauth_req, pos, 3);
+		wpabuf_put_u8(sta->hs20_deauth_req, url_len);
+		wpabuf_put_data(sta->hs20_deauth_req, pos + 3, url_len);
+	}
+	timeout = hapd->conf->hs20_deauth_req_timeout;
+	/* If there is no URL, no need to provide time to fetch it. Use a short
+	 * timeout here to allow maximum time for completing 4-way handshake and
+	 * WNM-Notification delivery. Acknowledgement of the frame will result
+	 * in cutting this wait further. */
+	if (!url_len && timeout > 2)
+		timeout = 2;
+	ap_sta_session_timeout(hapd, sta, timeout);
+}
+
+
+static void ieee802_1x_hs20_session_info(struct hostapd_data *hapd,
+					 struct sta_info *sta, u8 *pos,
+					 size_t len, int session_timeout)
+{
+	unsigned int swt;
+	int warning_time, beacon_int;
+
+	if (len < 1)
+		return; /* Malformed information */
+	os_free(sta->hs20_session_info_url);
+	sta->hs20_session_info_url = os_malloc(len);
+	if (!sta->hs20_session_info_url)
+		return;
+	swt = pos[0];
+	os_memcpy(sta->hs20_session_info_url, pos + 1, len - 1);
+	sta->hs20_session_info_url[len - 1] = '\0';
+	wpa_printf(MSG_DEBUG,
+		   "HS 2.0: Session Information URL='%s' SWT=%u (session_timeout=%d)",
+		   sta->hs20_session_info_url, swt, session_timeout);
+	if (session_timeout < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "HS 2.0: No Session-Timeout set - ignore session info URL");
+		return;
+	}
+	if (swt == 255)
+		swt = 1; /* Use one minute as the AP selected value */
+
+	if ((unsigned int) session_timeout < swt * 60)
+		warning_time = 0;
+	else
+		warning_time = session_timeout - swt * 60;
+
+	beacon_int = hapd->iconf->beacon_int;
+	if (beacon_int < 1)
+		beacon_int = 100; /* best guess */
+	sta->hs20_disassoc_timer = swt * 60 * 1000 / beacon_int * 125 / 128;
+	if (sta->hs20_disassoc_timer > 65535)
+		sta->hs20_disassoc_timer = 65535;
+
+	ap_sta_session_warning_timeout(hapd, sta, warning_time);
+}
+
+
+static void ieee802_1x_hs20_t_c_filtering(struct hostapd_data *hapd,
+					  struct sta_info *sta, u8 *pos,
+					  size_t len)
+{
+	if (len < 4)
+		return; /* Malformed information */
+	wpa_printf(MSG_DEBUG,
+		   "HS 2.0: Terms and Conditions filtering %02x %02x %02x %02x",
+		   pos[0], pos[1], pos[2], pos[3]);
+	hs20_t_c_filtering(hapd, sta, pos[0] & BIT(0));
+}
+
+
+static void ieee802_1x_hs20_t_c_url(struct hostapd_data *hapd,
+				    struct sta_info *sta, u8 *pos, size_t len)
+{
+	os_free(sta->t_c_url);
+	sta->t_c_url = os_malloc(len + 1);
+	if (!sta->t_c_url)
+		return;
+	os_memcpy(sta->t_c_url, pos, len);
+	sta->t_c_url[len] = '\0';
+	wpa_printf(MSG_DEBUG,
+		   "HS 2.0: Terms and Conditions URL %s", sta->t_c_url);
+}
+
+#endif /* CONFIG_HS20 */
+
+
+static void ieee802_1x_check_hs20(struct hostapd_data *hapd,
+				  struct sta_info *sta,
+				  struct radius_msg *msg,
+				  int session_timeout)
+{
+#ifdef CONFIG_HS20
+	u8 *buf, *pos, *end, type, sublen;
+	size_t len;
+
+	buf = NULL;
+	sta->remediation = 0;
+	sta->hs20_deauth_requested = 0;
+	sta->hs20_deauth_on_ack = 0;
+
+	for (;;) {
+		if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
+					    &buf, &len, buf) < 0)
+			break;
+		if (len < 6)
+			continue;
+		pos = buf;
+		end = buf + len;
+		if (WPA_GET_BE32(pos) != RADIUS_VENDOR_ID_WFA)
+			continue;
+		pos += 4;
+
+		type = *pos++;
+		sublen = *pos++;
+		if (sublen < 2)
+			continue; /* invalid length */
+		sublen -= 2; /* skip header */
+		if (pos + sublen > end)
+			continue; /* invalid WFA VSA */
+
+		switch (type) {
+		case RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION:
+			ieee802_1x_hs20_sub_rem(sta, pos, sublen);
+			break;
+		case RADIUS_VENDOR_ATTR_WFA_HS20_DEAUTH_REQ:
+			ieee802_1x_hs20_deauth_req(hapd, sta, pos, sublen);
+			break;
+		case RADIUS_VENDOR_ATTR_WFA_HS20_SESSION_INFO_URL:
+			ieee802_1x_hs20_session_info(hapd, sta, pos, sublen,
+						     session_timeout);
+			break;
+		case RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING:
+			ieee802_1x_hs20_t_c_filtering(hapd, sta, pos, sublen);
+			break;
+		case RADIUS_VENDOR_ATTR_WFA_HS20_T_C_URL:
+			ieee802_1x_hs20_t_c_url(hapd, sta, pos, sublen);
+			break;
+		}
+	}
+#endif /* CONFIG_HS20 */
+}
+
+
+struct sta_id_search {
+	u8 identifier;
+	struct eapol_state_machine *sm;
+};
+
+
+static int ieee802_1x_select_radius_identifier(struct hostapd_data *hapd,
+					       struct sta_info *sta,
+					       void *ctx)
+{
+	struct sta_id_search *id_search = ctx;
+	struct eapol_state_machine *sm = sta->eapol_sm;
+
+	if (sm && sm->radius_identifier >= 0 &&
+	    sm->radius_identifier == id_search->identifier) {
+		id_search->sm = sm;
+		return 1;
+	}
+	return 0;
+}
+
+
+static struct eapol_state_machine *
+ieee802_1x_search_radius_identifier(struct hostapd_data *hapd, u8 identifier)
+{
+	struct sta_id_search id_search;
+
+	id_search.identifier = identifier;
+	id_search.sm = NULL;
+	ap_for_each_sta(hapd, ieee802_1x_select_radius_identifier, &id_search);
+	return id_search.sm;
+}
+
+
+#ifndef CONFIG_NO_VLAN
+static int ieee802_1x_update_vlan(struct radius_msg *msg,
+				  struct hostapd_data *hapd,
+				  struct sta_info *sta)
+{
+	struct vlan_description vlan_desc;
+
+	os_memset(&vlan_desc, 0, sizeof(vlan_desc));
+	vlan_desc.notempty = !!radius_msg_get_vlanid(msg, &vlan_desc.untagged,
+						     MAX_NUM_TAGGED_VLAN,
+						     vlan_desc.tagged);
+
+	if (vlan_desc.notempty &&
+	    !hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) {
+		sta->eapol_sm->authFail = true;
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+			       HOSTAPD_LEVEL_INFO,
+			       "Invalid VLAN %d%s received from RADIUS server",
+			       vlan_desc.untagged,
+			       vlan_desc.tagged[0] ? "+" : "");
+		os_memset(&vlan_desc, 0, sizeof(vlan_desc));
+		ap_sta_set_vlan(hapd, sta, &vlan_desc);
+		return -1;
+	}
+
+	if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_REQUIRED &&
+	    !vlan_desc.notempty) {
+		sta->eapol_sm->authFail = true;
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+			       HOSTAPD_LEVEL_INFO,
+			       "authentication server did not include required VLAN ID in Access-Accept");
+		return -1;
+	}
+
+	return ap_sta_set_vlan(hapd, sta, &vlan_desc);
+}
+#endif /* CONFIG_NO_VLAN */
+
+
+/**
+ * ieee802_1x_receive_auth - Process RADIUS frames from Authentication Server
+ * @msg: RADIUS response message
+ * @req: RADIUS request message
+ * @shared_secret: RADIUS shared secret
+ * @shared_secret_len: Length of shared_secret in octets
+ * @data: Context data (struct hostapd_data *)
+ * Returns: Processing status
+ */
+static RadiusRxResult
+ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req,
+			const u8 *shared_secret, size_t shared_secret_len,
+			void *data)
+{
+	struct hostapd_data *hapd = data;
+	struct sta_info *sta;
+	u32 session_timeout = 0, termination_action, acct_interim_interval;
+	int session_timeout_set;
+	u32 reason_code;
+	struct eapol_state_machine *sm;
+	int override_eapReq = 0;
+	struct radius_hdr *hdr = radius_msg_get_hdr(msg);
+
+	sm = ieee802_1x_search_radius_identifier(hapd, hdr->identifier);
+	if (!sm) {
+		wpa_printf(MSG_DEBUG,
+			   "IEEE 802.1X: Could not find matching station for this RADIUS message");
+		return RADIUS_RX_UNKNOWN;
+	}
+	sta = sm->sta;
+
+	/* RFC 2869, Ch. 5.13: valid Message-Authenticator attribute MUST be
+	 * present when packet contains an EAP-Message attribute */
+	if (hdr->code == RADIUS_CODE_ACCESS_REJECT &&
+	    radius_msg_get_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, NULL,
+				0) < 0 &&
+	    radius_msg_get_attr(msg, RADIUS_ATTR_EAP_MESSAGE, NULL, 0) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "Allowing RADIUS Access-Reject without Message-Authenticator since it does not include EAP-Message");
+	} else if (radius_msg_verify(msg, shared_secret, shared_secret_len,
+				     req, 1)) {
+		wpa_printf(MSG_INFO,
+			   "Incoming RADIUS packet did not have correct Message-Authenticator - dropped");
+		return RADIUS_RX_INVALID_AUTHENTICATOR;
+	}
+
+	if (hdr->code != RADIUS_CODE_ACCESS_ACCEPT &&
+	    hdr->code != RADIUS_CODE_ACCESS_REJECT &&
+	    hdr->code != RADIUS_CODE_ACCESS_CHALLENGE) {
+		wpa_printf(MSG_INFO, "Unknown RADIUS message code");
+		return RADIUS_RX_UNKNOWN;
+	}
+
+	sm->radius_identifier = -1;
+	wpa_printf(MSG_DEBUG, "RADIUS packet matching with station " MACSTR,
+		   MAC2STR(sta->addr));
+
+	radius_msg_free(sm->last_recv_radius);
+	sm->last_recv_radius = msg;
+
+	session_timeout_set =
+		!radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT,
+					   &session_timeout);
+	if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_TERMINATION_ACTION,
+				      &termination_action))
+		termination_action = RADIUS_TERMINATION_ACTION_DEFAULT;
+
+	if (hapd->conf->acct_interim_interval == 0 &&
+	    hdr->code == RADIUS_CODE_ACCESS_ACCEPT &&
+	    radius_msg_get_attr_int32(msg, RADIUS_ATTR_ACCT_INTERIM_INTERVAL,
+				      &acct_interim_interval) == 0) {
+		if (acct_interim_interval < 60) {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE8021X,
+				       HOSTAPD_LEVEL_INFO,
+				       "ignored too small Acct-Interim-Interval %d",
+				       acct_interim_interval);
+		} else
+			sta->acct_interim_interval = acct_interim_interval;
+	}
+
+
+	switch (hdr->code) {
+	case RADIUS_CODE_ACCESS_ACCEPT:
+#ifndef CONFIG_NO_VLAN
+		if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED &&
+		    ieee802_1x_update_vlan(msg, hapd, sta) < 0)
+			break;
+
+		if (sta->vlan_id > 0) {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_RADIUS,
+				       HOSTAPD_LEVEL_INFO,
+				       "VLAN ID %d", sta->vlan_id);
+		}
+
+		if ((sta->flags & WLAN_STA_ASSOC) &&
+		    ap_sta_bind_vlan(hapd, sta) < 0)
+			break;
+#endif /* CONFIG_NO_VLAN */
+
+		sta->session_timeout_set = !!session_timeout_set;
+		os_get_reltime(&sta->session_timeout);
+		sta->session_timeout.sec += session_timeout;
+
+		/* RFC 3580, Ch. 3.17 */
+		if (session_timeout_set && termination_action ==
+		    RADIUS_TERMINATION_ACTION_RADIUS_REQUEST)
+			sm->reAuthPeriod = session_timeout;
+		else if (session_timeout_set)
+			ap_sta_session_timeout(hapd, sta, session_timeout);
+		else
+			ap_sta_no_session_timeout(hapd, sta);
+
+		sm->eap_if->aaaSuccess = true;
+		override_eapReq = 1;
+		ieee802_1x_get_keys(hapd, sta, msg, req, shared_secret,
+				    shared_secret_len);
+		ieee802_1x_store_radius_class(hapd, sta, msg);
+		ieee802_1x_update_sta_identity(hapd, sta, msg);
+		ieee802_1x_update_sta_cui(hapd, sta, msg);
+		ieee802_1x_check_hs20(hapd, sta, msg,
+				      session_timeout_set ?
+				      (int) session_timeout : -1);
+		break;
+	case RADIUS_CODE_ACCESS_REJECT:
+		sm->eap_if->aaaFail = true;
+		override_eapReq = 1;
+		if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_WLAN_REASON_CODE,
+					      &reason_code) == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "RADIUS server indicated WLAN-Reason-Code %u in Access-Reject for "
+				   MACSTR, reason_code, MAC2STR(sta->addr));
+			sta->disconnect_reason_code = reason_code;
+		}
+		break;
+	case RADIUS_CODE_ACCESS_CHALLENGE:
+		sm->eap_if->aaaEapReq = true;
+		if (session_timeout_set) {
+			/* RFC 2869, Ch. 2.3.2; RFC 3580, Ch. 3.17 */
+			sm->eap_if->aaaMethodTimeout = session_timeout;
+			hostapd_logger(hapd, sm->addr,
+				       HOSTAPD_MODULE_IEEE8021X,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "using EAP timeout of %d seconds (from RADIUS)",
+				       sm->eap_if->aaaMethodTimeout);
+		} else {
+			/*
+			 * Use dynamic retransmission behavior per EAP
+			 * specification.
+			 */
+			sm->eap_if->aaaMethodTimeout = 0;
+		}
+		break;
+	}
+
+	ieee802_1x_decapsulate_radius(hapd, sta);
+	if (override_eapReq)
+		sm->eap_if->aaaEapReq = false;
+
+#ifdef CONFIG_FILS
+#ifdef NEED_AP_MLME
+	if (sta->flags &
+	    (WLAN_STA_PENDING_FILS_ERP | WLAN_STA_PENDING_PASN_FILS_ERP)) {
+		/* TODO: Add a PMKSA entry on success? */
+		ieee802_11_finish_fils_auth(
+			hapd, sta, hdr->code == RADIUS_CODE_ACCESS_ACCEPT,
+			sm->eap_if->aaaEapReqData,
+			sm->eap_if->aaaEapKeyData,
+			sm->eap_if->aaaEapKeyDataLen);
+	}
+#endif /* NEED_AP_MLME */
+#endif /* CONFIG_FILS */
+
+	eapol_auth_step(sm);
+
+	return RADIUS_RX_QUEUED;
+}
+#endif /* CONFIG_NO_RADIUS */
+
+
+void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	struct eapol_state_machine *sm = sta->eapol_sm;
+
+	if (!sm)
+		return;
+
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+		       HOSTAPD_LEVEL_DEBUG, "aborting authentication");
+
+#ifndef CONFIG_NO_RADIUS
+	radius_msg_free(sm->last_recv_radius);
+	sm->last_recv_radius = NULL;
+#endif /* CONFIG_NO_RADIUS */
+
+	if (sm->eap_if->eapTimeout) {
+		/*
+		 * Disconnect the STA since it did not reply to the last EAP
+		 * request and we cannot continue EAP processing (EAP-Failure
+		 * could only be sent if the EAP peer actually replied).
+		 */
+		wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "EAP Timeout, STA " MACSTR,
+			MAC2STR(sta->addr));
+
+		sm->eap_if->portEnabled = false;
+		ap_sta_disconnect(hapd, sta, sta->addr,
+				  WLAN_REASON_PREV_AUTH_NOT_VALID);
+	}
+}
+
+
+#ifdef CONFIG_WEP
+
+static int ieee802_1x_rekey_broadcast(struct hostapd_data *hapd)
+{
+	struct eapol_authenticator *eapol = hapd->eapol_auth;
+
+	if (hapd->conf->default_wep_key_len < 1)
+		return 0;
+
+	os_free(eapol->default_wep_key);
+	eapol->default_wep_key = os_malloc(hapd->conf->default_wep_key_len);
+	if (!eapol->default_wep_key ||
+	    random_get_bytes(eapol->default_wep_key,
+			     hapd->conf->default_wep_key_len)) {
+		wpa_printf(MSG_INFO, "Could not generate random WEP key");
+		os_free(eapol->default_wep_key);
+		eapol->default_wep_key = NULL;
+		return -1;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "IEEE 802.1X: New default WEP key",
+			eapol->default_wep_key,
+			hapd->conf->default_wep_key_len);
+
+	return 0;
+}
+
+
+static int ieee802_1x_sta_key_available(struct hostapd_data *hapd,
+					struct sta_info *sta, void *ctx)
+{
+	if (sta->eapol_sm) {
+		sta->eapol_sm->eap_if->eapKeyAvailable = true;
+		eapol_auth_step(sta->eapol_sm);
+	}
+	return 0;
+}
+
+
+static void ieee802_1x_rekey(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct eapol_authenticator *eapol = hapd->eapol_auth;
+
+	if (eapol->default_wep_key_idx >= 3)
+		eapol->default_wep_key_idx =
+			hapd->conf->individual_wep_key_len > 0 ? 1 : 0;
+	else
+		eapol->default_wep_key_idx++;
+
+	wpa_printf(MSG_DEBUG, "IEEE 802.1X: New default WEP key index %d",
+		   eapol->default_wep_key_idx);
+
+	if (ieee802_1x_rekey_broadcast(hapd)) {
+		hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X,
+			       HOSTAPD_LEVEL_WARNING,
+			       "failed to generate a new broadcast key");
+		os_free(eapol->default_wep_key);
+		eapol->default_wep_key = NULL;
+		return;
+	}
+
+	/* TODO: Could setup key for RX here, but change default TX keyid only
+	 * after new broadcast key has been sent to all stations. */
+	if (hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_WEP,
+				broadcast_ether_addr,
+				eapol->default_wep_key_idx, 0, 1, NULL, 0,
+				eapol->default_wep_key,
+				hapd->conf->default_wep_key_len,
+				KEY_FLAG_GROUP_RX_TX_DEFAULT)) {
+		hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X,
+			       HOSTAPD_LEVEL_WARNING,
+			       "failed to configure a new broadcast key");
+		os_free(eapol->default_wep_key);
+		eapol->default_wep_key = NULL;
+		return;
+	}
+
+	ap_for_each_sta(hapd, ieee802_1x_sta_key_available, NULL);
+
+	if (hapd->conf->wep_rekeying_period > 0) {
+		eloop_register_timeout(hapd->conf->wep_rekeying_period, 0,
+				       ieee802_1x_rekey, hapd, NULL);
+	}
+}
+
+#endif /* CONFIG_WEP */
+
+
+static void ieee802_1x_eapol_send(void *ctx, void *sta_ctx, u8 type,
+				  const u8 *data, size_t datalen)
+{
+#ifdef CONFIG_WPS
+	struct sta_info *sta = sta_ctx;
+
+	if ((sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)) ==
+	    WLAN_STA_MAYBE_WPS) {
+		const u8 *identity;
+		size_t identity_len;
+		struct eapol_state_machine *sm = sta->eapol_sm;
+
+		identity = eap_get_identity(sm->eap, &identity_len);
+		if (identity &&
+		    ((identity_len == WSC_ID_ENROLLEE_LEN &&
+		      os_memcmp(identity, WSC_ID_ENROLLEE,
+				WSC_ID_ENROLLEE_LEN) == 0) ||
+		     (identity_len == WSC_ID_REGISTRAR_LEN &&
+		      os_memcmp(identity, WSC_ID_REGISTRAR,
+				WSC_ID_REGISTRAR_LEN) == 0))) {
+			wpa_printf(MSG_DEBUG,
+				   "WPS: WLAN_STA_MAYBE_WPS -> WLAN_STA_WPS");
+			sta->flags |= WLAN_STA_WPS;
+		}
+	}
+#endif /* CONFIG_WPS */
+
+	ieee802_1x_send(ctx, sta_ctx, type, data, datalen);
+}
+
+
+static void ieee802_1x_aaa_send(void *ctx, void *sta_ctx,
+				const u8 *data, size_t datalen)
+{
+#ifndef CONFIG_NO_RADIUS
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta = sta_ctx;
+
+	ieee802_1x_encapsulate_radius(hapd, sta, data, datalen);
+#endif /* CONFIG_NO_RADIUS */
+}
+
+
+static bool _ieee802_1x_finished(void *ctx, void *sta_ctx, int success,
+				 int preauth, int remediation, bool logoff)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta = sta_ctx;
+
+	if (preauth) {
+		rsn_preauth_finished(hapd, sta, success);
+		return false;
+	}
+
+	return ieee802_1x_finished(hapd, sta, success, remediation, logoff);
+}
+
+
+static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity,
+				   size_t identity_len, int phase2,
+				   struct eap_user *user)
+{
+	struct hostapd_data *hapd = ctx;
+	const struct hostapd_eap_user *eap_user;
+	int i;
+	int rv = -1;
+
+	eap_user = hostapd_get_eap_user(hapd, identity, identity_len, phase2);
+	if (!eap_user)
+		goto out;
+
+	os_memset(user, 0, sizeof(*user));
+	user->phase2 = phase2;
+	for (i = 0; i < EAP_MAX_METHODS; i++) {
+		user->methods[i].vendor = eap_user->methods[i].vendor;
+		user->methods[i].method = eap_user->methods[i].method;
+	}
+
+	if (eap_user->password) {
+		user->password = os_memdup(eap_user->password,
+					   eap_user->password_len);
+		if (!user->password)
+			goto out;
+		user->password_len = eap_user->password_len;
+		user->password_hash = eap_user->password_hash;
+		if (eap_user->salt && eap_user->salt_len) {
+			user->salt = os_memdup(eap_user->salt,
+					       eap_user->salt_len);
+			if (!user->salt)
+				goto out;
+			user->salt_len = eap_user->salt_len;
+		}
+	}
+	user->force_version = eap_user->force_version;
+	user->macacl = eap_user->macacl;
+	user->ttls_auth = eap_user->ttls_auth;
+	user->remediation = eap_user->remediation;
+	rv = 0;
+
+out:
+	if (rv)
+		wpa_printf(MSG_DEBUG, "%s: Failed to find user", __func__);
+
+	return rv;
+}
+
+
+static int ieee802_1x_sta_entry_alive(void *ctx, const u8 *addr)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta || !sta->eapol_sm)
+		return 0;
+	return 1;
+}
+
+
+static void ieee802_1x_logger(void *ctx, const u8 *addr,
+			      eapol_logger_level level, const char *txt)
+{
+#ifndef CONFIG_NO_HOSTAPD_LOGGER
+	struct hostapd_data *hapd = ctx;
+	int hlevel;
+
+	switch (level) {
+	case EAPOL_LOGGER_WARNING:
+		hlevel = HOSTAPD_LEVEL_WARNING;
+		break;
+	case EAPOL_LOGGER_INFO:
+		hlevel = HOSTAPD_LEVEL_INFO;
+		break;
+	case EAPOL_LOGGER_DEBUG:
+	default:
+		hlevel = HOSTAPD_LEVEL_DEBUG;
+		break;
+	}
+
+	hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE8021X, hlevel, "%s",
+		       txt);
+#endif /* CONFIG_NO_HOSTAPD_LOGGER */
+}
+
+
+static void ieee802_1x_set_port_authorized(void *ctx, void *sta_ctx,
+					   int authorized)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta = sta_ctx;
+
+	ieee802_1x_set_sta_authorized(hapd, sta, authorized);
+}
+
+
+static void _ieee802_1x_abort_auth(void *ctx, void *sta_ctx)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta = sta_ctx;
+
+	ieee802_1x_abort_auth(hapd, sta);
+}
+
+
+#ifdef CONFIG_WEP
+static void _ieee802_1x_tx_key(void *ctx, void *sta_ctx)
+{
+#ifndef CONFIG_FIPS
+#ifndef CONFIG_NO_RC4
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta = sta_ctx;
+
+	ieee802_1x_tx_key(hapd, sta);
+#endif /* CONFIG_NO_RC4 */
+#endif /* CONFIG_FIPS */
+}
+#endif /* CONFIG_WEP */
+
+
+static void ieee802_1x_eapol_event(void *ctx, void *sta_ctx,
+				   enum eapol_event type)
+{
+	/* struct hostapd_data *hapd = ctx; */
+	struct sta_info *sta = sta_ctx;
+
+	switch (type) {
+	case EAPOL_AUTH_SM_CHANGE:
+		wpa_auth_sm_notify(sta->wpa_sm);
+		break;
+	case EAPOL_AUTH_REAUTHENTICATE:
+		wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH_EAPOL);
+		break;
+	}
+}
+
+
+#ifdef CONFIG_ERP
+
+static struct eap_server_erp_key *
+ieee802_1x_erp_get_key(void *ctx, const char *keyname)
+{
+	struct hostapd_data *hapd = ctx;
+	struct eap_server_erp_key *erp;
+
+	dl_list_for_each(erp, &hapd->erp_keys, struct eap_server_erp_key,
+			 list) {
+		if (os_strcmp(erp->keyname_nai, keyname) == 0)
+			return erp;
+	}
+
+	return NULL;
+}
+
+
+static int ieee802_1x_erp_add_key(void *ctx, struct eap_server_erp_key *erp)
+{
+	struct hostapd_data *hapd = ctx;
+
+	dl_list_add(&hapd->erp_keys, &erp->list);
+	return 0;
+}
+
+#endif /* CONFIG_ERP */
+
+
+int ieee802_1x_init(struct hostapd_data *hapd)
+{
+	struct eapol_auth_config conf;
+	struct eapol_auth_cb cb;
+
+	if (hapd->mld_first_bss) {
+		wpa_printf(MSG_DEBUG,
+			   "MLD: Using IEEE 802.1X state machine of the first BSS");
+
+		hapd->eapol_auth = hapd->mld_first_bss->eapol_auth;
+		return 0;
+	}
+
+	dl_list_init(&hapd->erp_keys);
+
+	os_memset(&conf, 0, sizeof(conf));
+	conf.eap_cfg = hapd->eap_cfg;
+	conf.ctx = hapd;
+	conf.eap_reauth_period = hapd->conf->eap_reauth_period;
+	conf.wpa = hapd->conf->wpa;
+#ifdef CONFIG_WEP
+	conf.individual_wep_key_len = hapd->conf->individual_wep_key_len;
+#endif /* CONFIG_WEP */
+	conf.eap_req_id_text = hapd->conf->eap_req_id_text;
+	conf.eap_req_id_text_len = hapd->conf->eap_req_id_text_len;
+	conf.erp_send_reauth_start = hapd->conf->erp_send_reauth_start;
+	conf.erp_domain = hapd->conf->erp_domain;
+#ifdef CONFIG_TESTING_OPTIONS
+	conf.eap_skip_prot_success = hapd->conf->eap_skip_prot_success;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	os_memset(&cb, 0, sizeof(cb));
+	cb.eapol_send = ieee802_1x_eapol_send;
+	cb.aaa_send = ieee802_1x_aaa_send;
+	cb.finished = _ieee802_1x_finished;
+	cb.get_eap_user = ieee802_1x_get_eap_user;
+	cb.sta_entry_alive = ieee802_1x_sta_entry_alive;
+	cb.logger = ieee802_1x_logger;
+	cb.set_port_authorized = ieee802_1x_set_port_authorized;
+	cb.abort_auth = _ieee802_1x_abort_auth;
+#ifdef CONFIG_WEP
+	cb.tx_key = _ieee802_1x_tx_key;
+#endif /* CONFIG_WEP */
+	cb.eapol_event = ieee802_1x_eapol_event;
+#ifdef CONFIG_ERP
+	cb.erp_get_key = ieee802_1x_erp_get_key;
+	cb.erp_add_key = ieee802_1x_erp_add_key;
+#endif /* CONFIG_ERP */
+
+	hapd->eapol_auth = eapol_auth_init(&conf, &cb);
+	if (!hapd->eapol_auth)
+		return -1;
+
+	if ((hapd->conf->ieee802_1x || hapd->conf->wpa) &&
+	    hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 1))
+		return -1;
+
+#ifndef CONFIG_NO_RADIUS
+	if (radius_client_register(hapd->radius, RADIUS_AUTH,
+				   ieee802_1x_receive_auth, hapd))
+		return -1;
+#endif /* CONFIG_NO_RADIUS */
+
+#ifdef CONFIG_WEP
+	if (hapd->conf->default_wep_key_len) {
+		int i;
+
+		for (i = 0; i < 4; i++)
+			hostapd_drv_set_key(hapd->conf->iface, hapd,
+					    WPA_ALG_NONE, NULL, i, 0, 0, NULL,
+					    0, NULL, 0, KEY_FLAG_GROUP);
+
+		ieee802_1x_rekey(hapd, NULL);
+
+		if (!hapd->eapol_auth->default_wep_key)
+			return -1;
+	}
+#endif /* CONFIG_WEP */
+
+	return 0;
+}
+
+
+void ieee802_1x_erp_flush(struct hostapd_data *hapd)
+{
+	struct eap_server_erp_key *erp;
+
+	while ((erp = dl_list_first(&hapd->erp_keys, struct eap_server_erp_key,
+				    list)) != NULL) {
+		dl_list_del(&erp->list);
+		bin_clear_free(erp, sizeof(*erp));
+	}
+}
+
+
+void ieee802_1x_deinit(struct hostapd_data *hapd)
+{
+	if (hapd->mld_first_bss) {
+		wpa_printf(MSG_DEBUG,
+			   "MLD: Deinit IEEE 802.1X state machine of a non-first BSS");
+
+		hapd->eapol_auth = NULL;
+		return;
+	}
+
+#ifdef CONFIG_WEP
+	eloop_cancel_timeout(ieee802_1x_rekey, hapd, NULL);
+#endif /* CONFIG_WEP */
+
+	if (hapd->driver && hapd->drv_priv &&
+	    (hapd->conf->ieee802_1x || hapd->conf->wpa))
+		hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0);
+
+	eapol_auth_deinit(hapd->eapol_auth);
+	hapd->eapol_auth = NULL;
+
+	ieee802_1x_erp_flush(hapd);
+}
+
+
+int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta,
+			 const u8 *buf, size_t len, int ack)
+{
+	struct ieee80211_hdr *hdr;
+	u8 *pos;
+	const unsigned char rfc1042_hdr[ETH_ALEN] =
+		{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
+
+	if (!sta)
+		return -1;
+	if (len < sizeof(*hdr) + sizeof(rfc1042_hdr) + 2)
+		return 0;
+
+	hdr = (struct ieee80211_hdr *) buf;
+	pos = (u8 *) (hdr + 1);
+	if (os_memcmp(pos, rfc1042_hdr, sizeof(rfc1042_hdr)) != 0)
+		return 0;
+	pos += sizeof(rfc1042_hdr);
+	if (WPA_GET_BE16(pos) != ETH_P_PAE)
+		return 0;
+	pos += 2;
+
+	return ieee802_1x_eapol_tx_status(hapd, sta, pos, buf + len - pos,
+					  ack);
+}
+
+
+int ieee802_1x_eapol_tx_status(struct hostapd_data *hapd, struct sta_info *sta,
+			       const u8 *buf, int len, int ack)
+{
+	const struct ieee802_1x_hdr *xhdr =
+		(const struct ieee802_1x_hdr *) buf;
+	const u8 *pos = buf + sizeof(*xhdr);
+	struct ieee802_1x_eapol_key *key;
+
+	if (len < (int) sizeof(*xhdr))
+		return 0;
+	wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR
+		   " TX status - version=%d type=%d length=%d - ack=%d",
+		   MAC2STR(sta->addr), xhdr->version, xhdr->type,
+		   be_to_host16(xhdr->length), ack);
+
+#ifdef CONFIG_WPS
+	if (xhdr->type == IEEE802_1X_TYPE_EAP_PACKET && ack &&
+	    (sta->flags & WLAN_STA_WPS) &&
+	    ap_sta_pending_delayed_1x_auth_fail_disconnect(hapd, sta)) {
+		wpa_printf(MSG_DEBUG,
+			   "WPS: Indicate EAP completion on ACK for EAP-Failure");
+		hostapd_wps_eap_completed(hapd);
+	}
+#endif /* CONFIG_WPS */
+
+	if (xhdr->type != IEEE802_1X_TYPE_EAPOL_KEY)
+		return 0;
+
+	if (pos + sizeof(struct wpa_eapol_key) <= buf + len) {
+		const struct wpa_eapol_key *wpa;
+
+		wpa = (const struct wpa_eapol_key *) pos;
+		if (wpa->type == EAPOL_KEY_TYPE_RSN ||
+		    wpa->type == EAPOL_KEY_TYPE_WPA)
+			wpa_auth_eapol_key_tx_status(hapd->wpa_auth,
+						     sta->wpa_sm, ack);
+	}
+
+	/* EAPOL EAP-Packet packets are eventually re-sent by either Supplicant
+	 * or Authenticator state machines, but EAPOL-Key packets are not
+	 * retransmitted in case of failure. Try to re-send failed EAPOL-Key
+	 * packets couple of times because otherwise STA keys become
+	 * unsynchronized with AP. */
+	if (!ack && pos + sizeof(*key) <= buf + len) {
+		key = (struct ieee802_1x_eapol_key *) pos;
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "did not Ack EAPOL-Key frame (%scast index=%d)",
+			       key->key_index & BIT(7) ? "uni" : "broad",
+			       key->key_index & ~BIT(7));
+		/* TODO: re-send EAPOL-Key couple of times (with short delay
+		 * between them?). If all attempt fail, report error and
+		 * deauthenticate STA so that it will get new keys when
+		 * authenticating again (e.g., after returning in range).
+		 * Separate limit/transmit state needed both for unicast and
+		 * broadcast keys(?) */
+	}
+	/* TODO: could move unicast key configuration from ieee802_1x_tx_key()
+	 * to here and change the key only if the EAPOL-Key packet was Acked.
+	 */
+
+	return 1;
+}
+
+
+u8 * ieee802_1x_get_identity(struct eapol_state_machine *sm, size_t *len)
+{
+	if (!sm || !sm->identity)
+		return NULL;
+
+	*len = sm->identity_len;
+	return sm->identity;
+}
+
+
+u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len,
+				 int idx)
+{
+	if (!sm || !sm->radius_class.attr ||
+	    idx >= (int) sm->radius_class.count)
+		return NULL;
+
+	*len = sm->radius_class.attr[idx].len;
+	return sm->radius_class.attr[idx].data;
+}
+
+
+struct wpabuf * ieee802_1x_get_radius_cui(struct eapol_state_machine *sm)
+{
+	if (!sm)
+		return NULL;
+	return sm->radius_cui;
+}
+
+
+const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len)
+{
+	*len = 0;
+	if (!sm)
+		return NULL;
+
+	*len = sm->eap_if->eapKeyDataLen;
+	return sm->eap_if->eapKeyData;
+}
+
+
+#ifdef CONFIG_MACSEC
+const u8 * ieee802_1x_get_session_id(struct eapol_state_machine *sm,
+				     size_t *len)
+{
+	*len = 0;
+	if (!sm || !sm->eap_if)
+		return NULL;
+
+	*len = sm->eap_if->eapSessionIdLen;
+	return sm->eap_if->eapSessionId;
+}
+#endif /* CONFIG_MACSEC */
+
+
+void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm,
+				    bool enabled)
+{
+	if (!sm)
+		return;
+	sm->eap_if->portEnabled = enabled;
+	eapol_auth_step(sm);
+}
+
+
+void ieee802_1x_notify_port_valid(struct eapol_state_machine *sm, bool valid)
+{
+	if (!sm)
+		return;
+	sm->portValid = valid;
+	eapol_auth_step(sm);
+}
+
+
+void ieee802_1x_notify_pre_auth(struct eapol_state_machine *sm, bool pre_auth)
+{
+	if (!sm)
+		return;
+	if (pre_auth)
+		sm->flags |= EAPOL_SM_PREAUTH;
+	else
+		sm->flags &= ~EAPOL_SM_PREAUTH;
+}
+
+
+static const char * bool_txt(bool val)
+{
+	return val ? "TRUE" : "FALSE";
+}
+
+#ifdef CONFIG_CTRL_IFACE_MIB
+
+int ieee802_1x_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen)
+{
+	/* TODO */
+	return 0;
+}
+
+
+int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+			   char *buf, size_t buflen)
+{
+	int len = 0, ret;
+	struct eapol_state_machine *sm = sta->eapol_sm;
+	struct os_reltime diff;
+	const char *name1;
+	const char *name2;
+	char *identity_buf = NULL;
+
+	if (!sm)
+		return 0;
+
+	ret = os_snprintf(buf + len, buflen - len,
+			  "dot1xPaePortNumber=%d\n"
+			  "dot1xPaePortProtocolVersion=%d\n"
+			  "dot1xPaePortCapabilities=1\n"
+			  "dot1xPaePortInitialize=%d\n"
+			  "dot1xPaePortReauthenticate=FALSE\n",
+			  sta->aid,
+			  EAPOL_VERSION,
+			  sm->initialize);
+	if (os_snprintf_error(buflen - len, ret))
+		return len;
+	len += ret;
+
+	/* dot1xAuthConfigTable */
+	ret = os_snprintf(buf + len, buflen - len,
+			  "dot1xAuthPaeState=%d\n"
+			  "dot1xAuthBackendAuthState=%d\n"
+			  "dot1xAuthAdminControlledDirections=%d\n"
+			  "dot1xAuthOperControlledDirections=%d\n"
+			  "dot1xAuthAuthControlledPortStatus=%d\n"
+			  "dot1xAuthAuthControlledPortControl=%d\n"
+			  "dot1xAuthQuietPeriod=%u\n"
+			  "dot1xAuthServerTimeout=%u\n"
+			  "dot1xAuthReAuthPeriod=%u\n"
+			  "dot1xAuthReAuthEnabled=%s\n"
+			  "dot1xAuthKeyTxEnabled=%s\n",
+			  sm->auth_pae_state + 1,
+			  sm->be_auth_state + 1,
+			  sm->adminControlledDirections,
+			  sm->operControlledDirections,
+			  sm->authPortStatus,
+			  sm->portControl,
+			  sm->quietPeriod,
+			  sm->serverTimeout,
+			  sm->reAuthPeriod,
+			  bool_txt(sm->reAuthEnabled),
+			  bool_txt(sm->keyTxEnabled));
+	if (os_snprintf_error(buflen - len, ret))
+		return len;
+	len += ret;
+
+	/* dot1xAuthStatsTable */
+	ret = os_snprintf(buf + len, buflen - len,
+			  "dot1xAuthEapolFramesRx=%u\n"
+			  "dot1xAuthEapolFramesTx=%u\n"
+			  "dot1xAuthEapolStartFramesRx=%u\n"
+			  "dot1xAuthEapolLogoffFramesRx=%u\n"
+			  "dot1xAuthEapolRespIdFramesRx=%u\n"
+			  "dot1xAuthEapolRespFramesRx=%u\n"
+			  "dot1xAuthEapolReqIdFramesTx=%u\n"
+			  "dot1xAuthEapolReqFramesTx=%u\n"
+			  "dot1xAuthInvalidEapolFramesRx=%u\n"
+			  "dot1xAuthEapLengthErrorFramesRx=%u\n"
+			  "dot1xAuthLastEapolFrameVersion=%u\n"
+			  "dot1xAuthLastEapolFrameSource=" MACSTR "\n",
+			  sm->dot1xAuthEapolFramesRx,
+			  sm->dot1xAuthEapolFramesTx,
+			  sm->dot1xAuthEapolStartFramesRx,
+			  sm->dot1xAuthEapolLogoffFramesRx,
+			  sm->dot1xAuthEapolRespIdFramesRx,
+			  sm->dot1xAuthEapolRespFramesRx,
+			  sm->dot1xAuthEapolReqIdFramesTx,
+			  sm->dot1xAuthEapolReqFramesTx,
+			  sm->dot1xAuthInvalidEapolFramesRx,
+			  sm->dot1xAuthEapLengthErrorFramesRx,
+			  sm->dot1xAuthLastEapolFrameVersion,
+			  MAC2STR(sm->addr));
+	if (os_snprintf_error(buflen - len, ret))
+		return len;
+	len += ret;
+
+	/* dot1xAuthDiagTable */
+	ret = os_snprintf(buf + len, buflen - len,
+			  "dot1xAuthEntersConnecting=%u\n"
+			  "dot1xAuthEapLogoffsWhileConnecting=%u\n"
+			  "dot1xAuthEntersAuthenticating=%u\n"
+			  "dot1xAuthAuthSuccessesWhileAuthenticating=%u\n"
+			  "dot1xAuthAuthTimeoutsWhileAuthenticating=%u\n"
+			  "dot1xAuthAuthFailWhileAuthenticating=%u\n"
+			  "dot1xAuthAuthEapStartsWhileAuthenticating=%u\n"
+			  "dot1xAuthAuthEapLogoffWhileAuthenticating=%u\n"
+			  "dot1xAuthAuthReauthsWhileAuthenticated=%u\n"
+			  "dot1xAuthAuthEapStartsWhileAuthenticated=%u\n"
+			  "dot1xAuthAuthEapLogoffWhileAuthenticated=%u\n"
+			  "dot1xAuthBackendResponses=%u\n"
+			  "dot1xAuthBackendAccessChallenges=%u\n"
+			  "dot1xAuthBackendOtherRequestsToSupplicant=%u\n"
+			  "dot1xAuthBackendAuthSuccesses=%u\n"
+			  "dot1xAuthBackendAuthFails=%u\n",
+			  sm->authEntersConnecting,
+			  sm->authEapLogoffsWhileConnecting,
+			  sm->authEntersAuthenticating,
+			  sm->authAuthSuccessesWhileAuthenticating,
+			  sm->authAuthTimeoutsWhileAuthenticating,
+			  sm->authAuthFailWhileAuthenticating,
+			  sm->authAuthEapStartsWhileAuthenticating,
+			  sm->authAuthEapLogoffWhileAuthenticating,
+			  sm->authAuthReauthsWhileAuthenticated,
+			  sm->authAuthEapStartsWhileAuthenticated,
+			  sm->authAuthEapLogoffWhileAuthenticated,
+			  sm->backendResponses,
+			  sm->backendAccessChallenges,
+			  sm->backendOtherRequestsToSupplicant,
+			  sm->backendAuthSuccesses,
+			  sm->backendAuthFails);
+	if (os_snprintf_error(buflen - len, ret))
+		return len;
+	len += ret;
+
+	/* dot1xAuthSessionStatsTable */
+	os_reltime_age(&sta->acct_session_start, &diff);
+	if (sm->eap && !sm->identity) {
+		const u8 *id;
+		size_t id_len;
+
+		id = eap_get_identity(sm->eap, &id_len);
+		if (id)
+			identity_buf = dup_binstr(id, id_len);
+	}
+	ret = os_snprintf(buf + len, buflen - len,
+			  /* TODO: dot1xAuthSessionOctetsRx */
+			  /* TODO: dot1xAuthSessionOctetsTx */
+			  /* TODO: dot1xAuthSessionFramesRx */
+			  /* TODO: dot1xAuthSessionFramesTx */
+			  "dot1xAuthSessionId=%016llX\n"
+			  "dot1xAuthSessionAuthenticMethod=%d\n"
+			  "dot1xAuthSessionTime=%u\n"
+			  "dot1xAuthSessionTerminateCause=999\n"
+			  "dot1xAuthSessionUserName=%s\n",
+			  (unsigned long long) sta->acct_session_id,
+			  (wpa_key_mgmt_wpa_ieee8021x(
+				   wpa_auth_sta_key_mgmt(sta->wpa_sm))) ?
+			  1 : 2,
+			  (unsigned int) diff.sec,
+			  sm->identity ? (char *) sm->identity :
+					 (identity_buf ? identity_buf : "N/A"));
+	os_free(identity_buf);
+	if (os_snprintf_error(buflen - len, ret))
+		return len;
+	len += ret;
+
+	if (sm->acct_multi_session_id) {
+		ret = os_snprintf(buf + len, buflen - len,
+				  "authMultiSessionId=%016llX\n",
+				  (unsigned long long)
+				  sm->acct_multi_session_id);
+		if (os_snprintf_error(buflen - len, ret))
+			return len;
+		len += ret;
+	}
+
+	name1 = eap_server_get_name(0, sm->eap_type_authsrv);
+	name2 = eap_server_get_name(0, sm->eap_type_supp);
+	ret = os_snprintf(buf + len, buflen - len,
+			  "last_eap_type_as=%d (%s)\n"
+			  "last_eap_type_sta=%d (%s)\n",
+			  sm->eap_type_authsrv, name1,
+			  sm->eap_type_supp, name2);
+	if (os_snprintf_error(buflen - len, ret))
+		return len;
+	len += ret;
+
+	return len;
+}
+
+#endif
+
+#ifdef CONFIG_HS20
+static void ieee802_1x_wnm_notif_send(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct sta_info *sta = timeout_ctx;
+
+	if (sta->remediation) {
+		wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to "
+			   MACSTR " to indicate Subscription Remediation",
+			   MAC2STR(sta->addr));
+		hs20_send_wnm_notification(hapd, sta->addr,
+					   sta->remediation_method,
+					   sta->remediation_url);
+		os_free(sta->remediation_url);
+		sta->remediation_url = NULL;
+	}
+
+	if (sta->hs20_deauth_req) {
+		wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to "
+			   MACSTR " to indicate imminent deauthentication",
+			   MAC2STR(sta->addr));
+		hs20_send_wnm_notification_deauth_req(hapd, sta->addr,
+						      sta->hs20_deauth_req);
+	}
+
+	if (sta->hs20_t_c_filtering) {
+		wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to "
+			   MACSTR " to indicate Terms and Conditions filtering",
+			   MAC2STR(sta->addr));
+		hs20_send_wnm_notification_t_c(hapd, sta->addr, sta->t_c_url);
+		os_free(sta->t_c_url);
+		sta->t_c_url = NULL;
+	}
+}
+#endif /* CONFIG_HS20 */
+
+
+static bool ieee802_1x_finished(struct hostapd_data *hapd,
+				struct sta_info *sta, int success,
+				int remediation, bool logoff)
+{
+	const u8 *key;
+	size_t len;
+	/* TODO: get PMKLifetime from WPA parameters */
+	static const int dot11RSNAConfigPMKLifetime = 43200;
+	unsigned int session_timeout;
+	struct os_reltime now, remaining;
+
+#ifdef CONFIG_HS20
+	if (remediation && !sta->remediation) {
+		sta->remediation = 1;
+		os_free(sta->remediation_url);
+		sta->remediation_url =
+			os_strdup(hapd->conf->subscr_remediation_url);
+		sta->remediation_method = 1; /* SOAP-XML SPP */
+	}
+
+	if (success && (sta->remediation || sta->hs20_deauth_req ||
+			sta->hs20_t_c_filtering)) {
+		wpa_printf(MSG_DEBUG, "HS 2.0: Schedule WNM-Notification to "
+			   MACSTR " in 100 ms", MAC2STR(sta->addr));
+		eloop_cancel_timeout(ieee802_1x_wnm_notif_send, hapd, sta);
+		eloop_register_timeout(0, 100000, ieee802_1x_wnm_notif_send,
+				       hapd, sta);
+	}
+#endif /* CONFIG_HS20 */
+
+#ifdef CONFIG_MACSEC
+	ieee802_1x_notify_create_actor_hapd(hapd, sta);
+#endif /* CONFIG_MACSEC */
+
+	key = ieee802_1x_get_key(sta->eapol_sm, &len);
+	if (sta->session_timeout_set) {
+		os_get_reltime(&now);
+		os_reltime_sub(&sta->session_timeout, &now, &remaining);
+		session_timeout = (remaining.sec > 0) ? remaining.sec : 1;
+	} else {
+		session_timeout = dot11RSNAConfigPMKLifetime;
+	}
+	if (success && key && len >= PMK_LEN && !sta->remediation &&
+	    !sta->hs20_deauth_requested &&
+	    wpa_auth_pmksa_add(sta->wpa_sm, key, len, session_timeout,
+			       sta->eapol_sm) == 0) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "Added PMKSA cache entry (IEEE 802.1X)");
+	}
+
+	if (!success) {
+		/*
+		 * Many devices require deauthentication after WPS provisioning
+		 * and some may not be be able to do that themselves, so
+		 * disconnect the client here. In addition, this may also
+		 * benefit IEEE 802.1X/EAPOL authentication cases, too since
+		 * the EAPOL PAE state machine would remain in HELD state for
+		 * considerable amount of time and some EAP methods, like
+		 * EAP-FAST with anonymous provisioning, may require another
+		 * EAPOL authentication to be started to complete connection.
+		 */
+		ap_sta_delayed_1x_auth_fail_disconnect(hapd, sta,
+						       logoff ? 0 : 10);
+		if (logoff && sta->wpa_sm)
+			return true;
+	}
+
+	return false;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_1x.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_1x.h
new file mode 100644
index 0000000..1469351
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ieee802_1x.h
@@ -0,0 +1,69 @@
+/*
+ * hostapd / IEEE 802.1X-2004 Authenticator
+ * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IEEE802_1X_H
+#define IEEE802_1X_H
+
+struct hostapd_data;
+struct sta_info;
+struct eapol_state_machine;
+struct hostapd_config;
+struct hostapd_bss_config;
+struct hostapd_radius_attr;
+struct radius_msg;
+
+
+void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
+			size_t len, enum frame_encryption encrypted);
+void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta);
+void ieee802_1x_free_station(struct hostapd_data *hapd, struct sta_info *sta);
+
+void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta);
+void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd,
+				   struct sta_info *sta, int authorized);
+void ieee802_1x_dump_state(FILE *f, const char *prefix, struct sta_info *sta);
+int ieee802_1x_init(struct hostapd_data *hapd);
+void ieee802_1x_erp_flush(struct hostapd_data *hapd);
+void ieee802_1x_deinit(struct hostapd_data *hapd);
+int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta,
+			 const u8 *buf, size_t len, int ack);
+int ieee802_1x_eapol_tx_status(struct hostapd_data *hapd, struct sta_info *sta,
+			       const u8 *data, int len, int ack);
+u8 * ieee802_1x_get_identity(struct eapol_state_machine *sm, size_t *len);
+u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len,
+				 int idx);
+struct wpabuf * ieee802_1x_get_radius_cui(struct eapol_state_machine *sm);
+const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len);
+const u8 * ieee802_1x_get_session_id(struct eapol_state_machine *sm,
+				     size_t *len);
+void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm,
+				    bool enabled);
+void ieee802_1x_notify_port_valid(struct eapol_state_machine *sm, bool valid);
+void ieee802_1x_notify_pre_auth(struct eapol_state_machine *sm, bool pre_auth);
+int ieee802_1x_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen);
+int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+			   char *buf, size_t buflen);
+void hostapd_get_ntp_timestamp(u8 *buf);
+char *eap_type_text(u8 type);
+
+const char *radius_mode_txt(struct hostapd_data *hapd);
+int radius_sta_rate(struct hostapd_data *hapd, struct sta_info *sta);
+
+int add_common_radius_attr(struct hostapd_data *hapd,
+			   struct hostapd_radius_attr *req_attr,
+			   struct sta_info *sta,
+			   struct radius_msg *msg);
+int add_sqlite_radius_attr(struct hostapd_data *hapd, struct sta_info *sta,
+			   struct radius_msg *msg, int acct);
+void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
+				   struct sta_info *sta,
+				   const u8 *eap, size_t len);
+struct eapol_state_machine *
+ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta);
+
+#endif /* IEEE802_1X_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/mbo_ap.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/mbo_ap.c
new file mode 100644
index 0000000..43b0bf1
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/mbo_ap.c
@@ -0,0 +1,244 @@
+/*
+ * hostapd - MBO
+ * Copyright (c) 2016, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "mbo_ap.h"
+
+
+void mbo_ap_sta_free(struct sta_info *sta)
+{
+	struct mbo_non_pref_chan_info *info, *prev;
+
+	info = sta->non_pref_chan;
+	sta->non_pref_chan = NULL;
+	while (info) {
+		prev = info;
+		info = info->next;
+		os_free(prev);
+	}
+}
+
+
+static void mbo_ap_parse_non_pref_chan(struct sta_info *sta,
+				       const u8 *buf, size_t len)
+{
+	struct mbo_non_pref_chan_info *info, *tmp;
+	char channels[200], *pos, *end;
+	size_t num_chan, i;
+	int ret;
+
+	if (len <= 3)
+		return; /* Not enough room for any channels */
+
+	num_chan = len - 3;
+	info = os_zalloc(sizeof(*info) + num_chan);
+	if (!info)
+		return;
+	info->op_class = buf[0];
+	info->pref = buf[len - 2];
+	info->reason_code = buf[len - 1];
+	info->num_channels = num_chan;
+	buf++;
+	os_memcpy(info->channels, buf, num_chan);
+	if (!sta->non_pref_chan) {
+		sta->non_pref_chan = info;
+	} else {
+		tmp = sta->non_pref_chan;
+		while (tmp->next)
+			tmp = tmp->next;
+		tmp->next = info;
+	}
+
+	pos = channels;
+	end = pos + sizeof(channels);
+	*pos = '\0';
+	for (i = 0; i < num_chan; i++) {
+		ret = os_snprintf(pos, end - pos, "%s%u",
+				  i == 0 ? "" : " ", buf[i]);
+		if (os_snprintf_error(end - pos, ret)) {
+			*pos = '\0';
+			break;
+		}
+		pos += ret;
+	}
+
+	wpa_printf(MSG_DEBUG, "MBO: STA " MACSTR
+		   " non-preferred channel list (op class %u, pref %u, reason code %u, channels %s)",
+		   MAC2STR(sta->addr), info->op_class, info->pref,
+		   info->reason_code, channels);
+}
+
+
+void mbo_ap_check_sta_assoc(struct hostapd_data *hapd, struct sta_info *sta,
+			    struct ieee802_11_elems *elems)
+{
+	const u8 *pos, *attr, *end;
+	size_t len;
+
+	if (!hapd->conf->mbo_enabled || !elems->mbo)
+		return;
+
+	pos = elems->mbo + 4;
+	len = elems->mbo_len - 4;
+	wpa_hexdump(MSG_DEBUG, "MBO: Association Request attributes", pos, len);
+
+	attr = get_ie(pos, len, MBO_ATTR_ID_CELL_DATA_CAPA);
+	if (attr && attr[1] >= 1)
+		sta->cell_capa = attr[2];
+
+	mbo_ap_sta_free(sta);
+	end = pos + len;
+	while (end - pos > 1) {
+		u8 ie_len = pos[1];
+
+		if (2 + ie_len > end - pos)
+			break;
+
+		if (pos[0] == MBO_ATTR_ID_NON_PREF_CHAN_REPORT)
+			mbo_ap_parse_non_pref_chan(sta, pos + 2, ie_len);
+		pos += 2 + pos[1];
+	}
+}
+
+
+int mbo_ap_get_info(struct sta_info *sta, char *buf, size_t buflen)
+{
+	char *pos = buf, *end = buf + buflen;
+	int ret;
+	struct mbo_non_pref_chan_info *info;
+	u8 i;
+	unsigned int count = 0;
+
+	if (!sta->cell_capa)
+		return 0;
+
+	ret = os_snprintf(pos, end - pos, "mbo_cell_capa=%u\n", sta->cell_capa);
+	if (os_snprintf_error(end - pos, ret))
+		return pos - buf;
+	pos += ret;
+
+	for (info = sta->non_pref_chan; info; info = info->next) {
+		char *pos2 = pos;
+
+		ret = os_snprintf(pos2, end - pos2,
+				  "non_pref_chan[%u]=%u:%u:%u:",
+				  count, info->op_class, info->pref,
+				  info->reason_code);
+		count++;
+		if (os_snprintf_error(end - pos2, ret))
+			break;
+		pos2 += ret;
+
+		for (i = 0; i < info->num_channels; i++) {
+			ret = os_snprintf(pos2, end - pos2, "%u%s",
+					  info->channels[i],
+					  i + 1 < info->num_channels ?
+					  "," : "");
+			if (os_snprintf_error(end - pos2, ret)) {
+				pos2 = NULL;
+				break;
+			}
+			pos2 += ret;
+		}
+
+		if (!pos2)
+			break;
+		ret = os_snprintf(pos2, end - pos2, "\n");
+		if (os_snprintf_error(end - pos2, ret))
+			break;
+		pos2 += ret;
+		pos = pos2;
+	}
+
+	return pos - buf;
+}
+
+
+static void mbo_ap_wnm_notif_req_cell_capa(struct sta_info *sta,
+					   const u8 *buf, size_t len)
+{
+	if (len < 1)
+		return;
+	wpa_printf(MSG_DEBUG, "MBO: STA " MACSTR
+		   " updated cellular data capability: %u",
+		   MAC2STR(sta->addr), buf[0]);
+	sta->cell_capa = buf[0];
+}
+
+
+static void mbo_ap_wnm_notif_req_elem(struct sta_info *sta, u8 type,
+				      const u8 *buf, size_t len,
+				      int *first_non_pref_chan)
+{
+	switch (type) {
+	case WFA_WNM_NOTIF_SUBELEM_NON_PREF_CHAN_REPORT:
+		if (*first_non_pref_chan) {
+			/*
+			 * Need to free the previously stored entries now to
+			 * allow the update to replace all entries.
+			 */
+			*first_non_pref_chan = 0;
+			mbo_ap_sta_free(sta);
+		}
+		mbo_ap_parse_non_pref_chan(sta, buf, len);
+		break;
+	case WFA_WNM_NOTIF_SUBELEM_CELL_DATA_CAPA:
+		mbo_ap_wnm_notif_req_cell_capa(sta, buf, len);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG,
+			   "MBO: Ignore unknown WNM Notification WFA subelement %u",
+			   type);
+		break;
+	}
+}
+
+
+void mbo_ap_wnm_notification_req(struct hostapd_data *hapd, const u8 *addr,
+				 const u8 *buf, size_t len)
+{
+	const u8 *pos, *end;
+	u8 ie_len;
+	struct sta_info *sta;
+	int first_non_pref_chan = 1;
+
+	if (!hapd->conf->mbo_enabled)
+		return;
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta)
+		return;
+
+	pos = buf;
+	end = buf + len;
+
+	while (end - pos > 1) {
+		ie_len = pos[1];
+
+		if (2 + ie_len > end - pos)
+			break;
+
+		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC &&
+		    ie_len >= 4 && WPA_GET_BE24(pos + 2) == OUI_WFA)
+			mbo_ap_wnm_notif_req_elem(sta, pos[5],
+						  pos + 6, ie_len - 4,
+						  &first_non_pref_chan);
+		else
+			wpa_printf(MSG_DEBUG,
+				   "MBO: Ignore unknown WNM Notification element %u (len=%u)",
+				   pos[0], pos[1]);
+
+		pos += 2 + pos[1];
+	}
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/mbo_ap.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/mbo_ap.h
new file mode 100644
index 0000000..9f37f28
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/mbo_ap.h
@@ -0,0 +1,51 @@
+/*
+ * MBO related functions and structures
+ * Copyright (c) 2016, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef MBO_AP_H
+#define MBO_AP_H
+
+struct hostapd_data;
+struct sta_info;
+struct ieee802_11_elems;
+
+#ifdef CONFIG_MBO
+
+void mbo_ap_check_sta_assoc(struct hostapd_data *hapd, struct sta_info *sta,
+			    struct ieee802_11_elems *elems);
+int mbo_ap_get_info(struct sta_info *sta, char *buf, size_t buflen);
+void mbo_ap_wnm_notification_req(struct hostapd_data *hapd, const u8 *addr,
+				 const u8 *buf, size_t len);
+void mbo_ap_sta_free(struct sta_info *sta);
+
+#else /* CONFIG_MBO */
+
+static inline void mbo_ap_check_sta_assoc(struct hostapd_data *hapd,
+					  struct sta_info *sta,
+					  struct ieee802_11_elems *elems)
+{
+}
+
+static inline int mbo_ap_get_info(struct sta_info *sta, char *buf,
+				  size_t buflen)
+{
+	return 0;
+}
+
+static inline void mbo_ap_wnm_notification_req(struct hostapd_data *hapd,
+					       const u8 *addr,
+					       const u8 *buf, size_t len)
+{
+}
+
+static inline void mbo_ap_sta_free(struct sta_info *sta)
+{
+}
+
+#endif /* CONFIG_MBO */
+
+#endif /* MBO_AP_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ndisc_snoop.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ndisc_snoop.c
new file mode 100644
index 0000000..bc1eb62
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ndisc_snoop.c
@@ -0,0 +1,189 @@
+/*
+ * Neighbor Discovery snooping for Proxy ARP
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+
+#include "utils/common.h"
+#include "l2_packet/l2_packet.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ap_drv_ops.h"
+#include "list.h"
+#include "x_snoop.h"
+#include "ndisc_snoop.h"
+
+struct ip6addr {
+	struct in6_addr addr;
+	struct dl_list list;
+};
+
+struct icmpv6_ndmsg {
+	struct ip6_hdr ipv6h;
+	struct icmp6_hdr icmp6h;
+	struct in6_addr target_addr;
+	u8 opt_type;
+	u8 len;
+	u8 opt_lladdr[0];
+} STRUCT_PACKED;
+
+#define ROUTER_ADVERTISEMENT	134
+#define NEIGHBOR_SOLICITATION	135
+#define NEIGHBOR_ADVERTISEMENT	136
+#define SOURCE_LL_ADDR		1
+
+static int sta_ip6addr_add(struct sta_info *sta, struct in6_addr *addr)
+{
+	struct ip6addr *ip6addr;
+
+	ip6addr = os_zalloc(sizeof(*ip6addr));
+	if (!ip6addr)
+		return -1;
+
+	os_memcpy(&ip6addr->addr, addr, sizeof(*addr));
+
+	dl_list_add_tail(&sta->ip6addr, &ip6addr->list);
+
+	return 0;
+}
+
+
+void sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	struct ip6addr *ip6addr, *prev;
+
+	dl_list_for_each_safe(ip6addr, prev, &sta->ip6addr, struct ip6addr,
+			      list) {
+		hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) &ip6addr->addr);
+		dl_list_del(&ip6addr->list);
+		os_free(ip6addr);
+	}
+}
+
+
+static int sta_has_ip6addr(struct sta_info *sta, struct in6_addr *addr)
+{
+	struct ip6addr *ip6addr;
+
+	dl_list_for_each(ip6addr, &sta->ip6addr, struct ip6addr, list) {
+		if (ip6addr->addr.s6_addr32[0] == addr->s6_addr32[0] &&
+		    ip6addr->addr.s6_addr32[1] == addr->s6_addr32[1] &&
+		    ip6addr->addr.s6_addr32[2] == addr->s6_addr32[2] &&
+		    ip6addr->addr.s6_addr32[3] == addr->s6_addr32[3])
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static void ucast_to_stas(struct hostapd_data *hapd, const u8 *buf, size_t len)
+{
+	struct sta_info *sta;
+
+	for (sta = hapd->sta_list; sta; sta = sta->next) {
+		if (!(sta->flags & WLAN_STA_AUTHORIZED))
+			continue;
+		x_snoop_mcast_to_ucast_convert_send(hapd, sta, (u8 *) buf, len);
+	}
+}
+
+
+static void handle_ndisc(void *ctx, const u8 *src_addr, const u8 *buf,
+			 size_t len)
+{
+	struct hostapd_data *hapd = ctx;
+	struct icmpv6_ndmsg *msg;
+	struct in6_addr saddr;
+	struct sta_info *sta;
+	int res;
+	char addrtxt[INET6_ADDRSTRLEN + 1];
+
+	if (len < ETH_HLEN + sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr))
+		return;
+	msg = (struct icmpv6_ndmsg *) &buf[ETH_HLEN];
+	switch (msg->icmp6h.icmp6_type) {
+	case NEIGHBOR_SOLICITATION:
+		if (len < ETH_HLEN + sizeof(*msg))
+			return;
+		if (msg->opt_type != SOURCE_LL_ADDR)
+			return;
+
+		/*
+		 * IPv6 header may not be 32-bit aligned in the buffer, so use
+		 * a local copy to avoid unaligned reads.
+		 */
+		os_memcpy(&saddr, &msg->ipv6h.ip6_src, sizeof(saddr));
+		if (!(saddr.s6_addr32[0] == 0 && saddr.s6_addr32[1] == 0 &&
+		      saddr.s6_addr32[2] == 0 && saddr.s6_addr32[3] == 0)) {
+			if (len < ETH_HLEN + sizeof(*msg) + ETH_ALEN)
+				return;
+			sta = ap_get_sta(hapd, msg->opt_lladdr);
+			if (!sta)
+				return;
+
+			if (sta_has_ip6addr(sta, &saddr))
+				return;
+
+			if (inet_ntop(AF_INET6, &saddr, addrtxt,
+				      sizeof(addrtxt)) == NULL)
+				addrtxt[0] = '\0';
+			wpa_printf(MSG_DEBUG, "ndisc_snoop: Learned new IPv6 address %s for "
+				   MACSTR, addrtxt, MAC2STR(sta->addr));
+			hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) &saddr);
+			res = hostapd_drv_br_add_ip_neigh(hapd, 6,
+							  (u8 *) &saddr,
+							  128, sta->addr);
+			if (res) {
+				wpa_printf(MSG_ERROR,
+					   "ndisc_snoop: Adding ip neigh failed: %d",
+					   res);
+				return;
+			}
+
+			if (sta_ip6addr_add(sta, &saddr))
+				return;
+		}
+		break;
+#ifdef CONFIG_HS20
+	case ROUTER_ADVERTISEMENT:
+		if (hapd->conf->disable_dgaf)
+			ucast_to_stas(hapd, buf, len);
+		break;
+#endif /* CONFIG_HS20 */
+	case NEIGHBOR_ADVERTISEMENT:
+		if (hapd->conf->na_mcast_to_ucast)
+			ucast_to_stas(hapd, buf, len);
+		break;
+	default:
+		break;
+	}
+}
+
+
+int ndisc_snoop_init(struct hostapd_data *hapd)
+{
+	hapd->sock_ndisc = x_snoop_get_l2_packet(hapd, handle_ndisc,
+						 L2_PACKET_FILTER_NDISC);
+	if (hapd->sock_ndisc == NULL) {
+		wpa_printf(MSG_DEBUG,
+			   "ndisc_snoop: Failed to initialize L2 packet processing for NDISC packets: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void ndisc_snoop_deinit(struct hostapd_data *hapd)
+{
+	l2_packet_deinit(hapd->sock_ndisc);
+	hapd->sock_ndisc = NULL;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ndisc_snoop.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ndisc_snoop.h
new file mode 100644
index 0000000..3cc9a55
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ndisc_snoop.h
@@ -0,0 +1,36 @@
+/*
+ * Neighbor Discovery snooping for Proxy ARP
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef NDISC_SNOOP_H
+#define NDISC_SNOOP_H
+
+#if defined(CONFIG_PROXYARP) && defined(CONFIG_IPV6)
+
+int ndisc_snoop_init(struct hostapd_data *hapd);
+void ndisc_snoop_deinit(struct hostapd_data *hapd);
+void sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta);
+
+#else /* CONFIG_PROXYARP && CONFIG_IPV6 */
+
+static inline int ndisc_snoop_init(struct hostapd_data *hapd)
+{
+	return 0;
+}
+
+static inline void ndisc_snoop_deinit(struct hostapd_data *hapd)
+{
+}
+
+static inline void sta_ip6addr_del(struct hostapd_data *hapd,
+				   struct sta_info *sta)
+{
+}
+
+#endif /* CONFIG_PROXYARP && CONFIG_IPV6 */
+
+#endif /* NDISC_SNOOP_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/neighbor_db.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/neighbor_db.c
new file mode 100644
index 0000000..5b276e8
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/neighbor_db.c
@@ -0,0 +1,327 @@
+/*
+ * hostapd / Neighboring APs DB
+ * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH.
+ * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/crc32.h"
+#include "hostapd.h"
+#include "ieee802_11.h"
+#include "neighbor_db.h"
+
+
+struct hostapd_neighbor_entry *
+hostapd_neighbor_get(struct hostapd_data *hapd, const u8 *bssid,
+		     const struct wpa_ssid_value *ssid)
+{
+	struct hostapd_neighbor_entry *nr;
+
+	dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
+			 list) {
+		if (os_memcmp(bssid, nr->bssid, ETH_ALEN) == 0 &&
+		    (!ssid ||
+		     (ssid->ssid_len == nr->ssid.ssid_len &&
+		      os_memcmp(ssid->ssid, nr->ssid.ssid,
+				ssid->ssid_len) == 0)))
+			return nr;
+	}
+	return NULL;
+}
+
+
+int hostapd_neighbor_show(struct hostapd_data *hapd, char *buf, size_t buflen)
+{
+	struct hostapd_neighbor_entry *nr;
+	char *pos, *end;
+
+	pos = buf;
+	end = buf + buflen;
+
+	dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
+			 list) {
+		int ret;
+		char nrie[2 * 255 + 1];
+		char lci[2 * 255 + 1];
+		char civic[2 * 255 + 1];
+		char ssid[SSID_MAX_LEN * 2 + 1];
+
+		ssid[0] = '\0';
+		wpa_snprintf_hex(ssid, sizeof(ssid), nr->ssid.ssid,
+				 nr->ssid.ssid_len);
+
+		nrie[0] = '\0';
+		if (nr->nr)
+			wpa_snprintf_hex(nrie, sizeof(nrie),
+					 wpabuf_head(nr->nr),
+					 wpabuf_len(nr->nr));
+
+		lci[0] = '\0';
+		if (nr->lci)
+			wpa_snprintf_hex(lci, sizeof(lci),
+					 wpabuf_head(nr->lci),
+					 wpabuf_len(nr->lci));
+
+		civic[0] = '\0';
+		if (nr->civic)
+			wpa_snprintf_hex(civic, sizeof(civic),
+					 wpabuf_head(nr->civic),
+					 wpabuf_len(nr->civic));
+
+		ret = os_snprintf(pos, end - pos, MACSTR
+				  " ssid=%s%s%s%s%s%s%s%s\n",
+				  MAC2STR(nr->bssid), ssid,
+				  nr->nr ? " nr=" : "", nrie,
+				  nr->lci ? " lci=" : "", lci,
+				  nr->civic ? " civic=" : "", civic,
+				  nr->stationary ? " stat" : "");
+		if (os_snprintf_error(end - pos, ret))
+			break;
+		pos += ret;
+	}
+
+	return pos - buf;
+}
+
+
+static void hostapd_neighbor_clear_entry(struct hostapd_neighbor_entry *nr)
+{
+	wpabuf_free(nr->nr);
+	nr->nr = NULL;
+	wpabuf_free(nr->lci);
+	nr->lci = NULL;
+	wpabuf_free(nr->civic);
+	nr->civic = NULL;
+	os_memset(nr->bssid, 0, sizeof(nr->bssid));
+	os_memset(&nr->ssid, 0, sizeof(nr->ssid));
+	nr->stationary = 0;
+}
+
+
+static struct hostapd_neighbor_entry *
+hostapd_neighbor_add(struct hostapd_data *hapd)
+{
+	struct hostapd_neighbor_entry *nr;
+
+	nr = os_zalloc(sizeof(struct hostapd_neighbor_entry));
+	if (!nr)
+		return NULL;
+
+	dl_list_add(&hapd->nr_db, &nr->list);
+
+	return nr;
+}
+
+
+int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid,
+			 const struct wpa_ssid_value *ssid,
+			 const struct wpabuf *nr, const struct wpabuf *lci,
+			 const struct wpabuf *civic, int stationary,
+			 u8 bss_parameters)
+{
+	struct hostapd_neighbor_entry *entry;
+
+	entry = hostapd_neighbor_get(hapd, bssid, ssid);
+	if (!entry)
+		entry = hostapd_neighbor_add(hapd);
+	if (!entry)
+		return -1;
+
+	hostapd_neighbor_clear_entry(entry);
+
+	os_memcpy(entry->bssid, bssid, ETH_ALEN);
+	os_memcpy(&entry->ssid, ssid, sizeof(entry->ssid));
+	entry->short_ssid = ieee80211_crc32(ssid->ssid, ssid->ssid_len);
+
+	entry->nr = wpabuf_dup(nr);
+	if (!entry->nr)
+		goto fail;
+
+	if (lci && wpabuf_len(lci)) {
+		entry->lci = wpabuf_dup(lci);
+		if (!entry->lci || os_get_time(&entry->lci_date))
+			goto fail;
+	}
+
+	if (civic && wpabuf_len(civic)) {
+		entry->civic = wpabuf_dup(civic);
+		if (!entry->civic)
+			goto fail;
+	}
+
+	entry->stationary = stationary;
+	entry->bss_parameters = bss_parameters;
+
+	return 0;
+
+fail:
+	hostapd_neighbor_remove(hapd, bssid, ssid);
+	return -1;
+}
+
+
+int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid,
+			    const struct wpa_ssid_value *ssid)
+{
+	struct hostapd_neighbor_entry *nr;
+
+	nr = hostapd_neighbor_get(hapd, bssid, ssid);
+	if (!nr)
+		return -1;
+
+	hostapd_neighbor_clear_entry(nr);
+	dl_list_del(&nr->list);
+	os_free(nr);
+
+	return 0;
+}
+
+
+void hostapd_free_neighbor_db(struct hostapd_data *hapd)
+{
+	struct hostapd_neighbor_entry *nr, *prev;
+
+	dl_list_for_each_safe(nr, prev, &hapd->nr_db,
+			      struct hostapd_neighbor_entry, list) {
+		hostapd_neighbor_clear_entry(nr);
+		dl_list_del(&nr->list);
+		os_free(nr);
+	}
+}
+
+
+#ifdef NEED_AP_MLME
+static enum nr_chan_width hostapd_get_nr_chan_width(struct hostapd_data *hapd,
+						    int ht, int vht, int he)
+{
+	enum oper_chan_width oper_chwidth;
+
+	oper_chwidth = hostapd_get_oper_chwidth(hapd->iconf);
+
+	if (!ht && !vht && !he)
+		return NR_CHAN_WIDTH_20;
+	if (!hapd->iconf->secondary_channel)
+		return NR_CHAN_WIDTH_20;
+	if ((!vht && !he) || oper_chwidth == CONF_OPER_CHWIDTH_USE_HT)
+		return NR_CHAN_WIDTH_40;
+	if (oper_chwidth == CONF_OPER_CHWIDTH_80MHZ)
+		return NR_CHAN_WIDTH_80;
+	if (oper_chwidth == CONF_OPER_CHWIDTH_160MHZ)
+		return NR_CHAN_WIDTH_160;
+	if (oper_chwidth == CONF_OPER_CHWIDTH_80P80MHZ)
+		return NR_CHAN_WIDTH_80P80;
+	return NR_CHAN_WIDTH_20;
+}
+#endif /* NEED_AP_MLME */
+
+
+void hostapd_neighbor_set_own_report(struct hostapd_data *hapd)
+{
+#ifdef NEED_AP_MLME
+	u16 capab = hostapd_own_capab_info(hapd);
+	int ht = hapd->iconf->ieee80211n && !hapd->conf->disable_11n;
+	int vht = hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac;
+	int he = hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax;
+	bool eht = he && hapd->iconf->ieee80211be && !hapd->conf->disable_11be;
+	struct wpa_ssid_value ssid;
+	u8 channel, op_class;
+	u8 center_freq1_idx = 0, center_freq2_idx = 0;
+	enum nr_chan_width width;
+	u32 bssid_info;
+	struct wpabuf *nr;
+
+	if (!(hapd->conf->radio_measurements[0] &
+	      WLAN_RRM_CAPS_NEIGHBOR_REPORT))
+		return;
+
+	bssid_info = 3; /* AP is reachable */
+	bssid_info |= NEI_REP_BSSID_INFO_SECURITY; /* "same as the AP" */
+	bssid_info |= NEI_REP_BSSID_INFO_KEY_SCOPE; /* "same as the AP" */
+
+	if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT)
+		bssid_info |= NEI_REP_BSSID_INFO_SPECTRUM_MGMT;
+
+	bssid_info |= NEI_REP_BSSID_INFO_RM; /* RRM is supported */
+
+	if (hapd->conf->wmm_enabled) {
+		bssid_info |= NEI_REP_BSSID_INFO_QOS;
+
+		if (hapd->conf->wmm_uapsd &&
+		    (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD))
+			bssid_info |= NEI_REP_BSSID_INFO_APSD;
+	}
+
+	if (ht) {
+		bssid_info |= NEI_REP_BSSID_INFO_HT |
+			NEI_REP_BSSID_INFO_DELAYED_BA;
+
+		/* VHT bit added in IEEE P802.11-REVmc/D4.3 */
+		if (vht)
+			bssid_info |= NEI_REP_BSSID_INFO_VHT;
+	}
+
+	if (he)
+		bssid_info |= NEI_REP_BSSID_INFO_HE;
+	if (eht)
+		bssid_info |= NEI_REP_BSSID_INFO_EHT;
+	/* TODO: Set NEI_REP_BSSID_INFO_MOBILITY_DOMAIN if MDE is set */
+
+	if (ieee80211_freq_to_channel_ext(hapd->iface->freq,
+					  hapd->iconf->secondary_channel,
+					  hostapd_get_oper_chwidth(hapd->iconf),
+					  &op_class, &channel) ==
+	    NUM_HOSTAPD_MODES)
+		return;
+	width = hostapd_get_nr_chan_width(hapd, ht, vht, he);
+	if (vht) {
+		center_freq1_idx = hostapd_get_oper_centr_freq_seg0_idx(
+			hapd->iconf);
+		if (width == NR_CHAN_WIDTH_80P80)
+			center_freq2_idx =
+				hostapd_get_oper_centr_freq_seg1_idx(
+					hapd->iconf);
+	} else if (ht) {
+		ieee80211_freq_to_chan(hapd->iface->freq +
+				       10 * hapd->iconf->secondary_channel,
+				       &center_freq1_idx);
+	}
+
+	ssid.ssid_len = hapd->conf->ssid.ssid_len;
+	os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len);
+
+	/*
+	 * Neighbor Report element size = BSSID + BSSID info + op_class + chan +
+	 * phy type + wide bandwidth channel subelement.
+	 */
+	nr = wpabuf_alloc(ETH_ALEN + 4 + 1 + 1 + 1 + 5);
+	if (!nr)
+		return;
+
+	wpabuf_put_data(nr, hapd->own_addr, ETH_ALEN);
+	wpabuf_put_le32(nr, bssid_info);
+	wpabuf_put_u8(nr, op_class);
+	wpabuf_put_u8(nr, channel);
+	wpabuf_put_u8(nr, ieee80211_get_phy_type(hapd->iface->freq, ht, vht));
+
+	/*
+	 * Wide Bandwidth Channel subelement may be needed to allow the
+	 * receiving STA to send packets to the AP. See IEEE P802.11-REVmc/D5.0
+	 * Figure 9-301.
+	 */
+	wpabuf_put_u8(nr, WNM_NEIGHBOR_WIDE_BW_CHAN);
+	wpabuf_put_u8(nr, 3);
+	wpabuf_put_u8(nr, width);
+	wpabuf_put_u8(nr, center_freq1_idx);
+	wpabuf_put_u8(nr, center_freq2_idx);
+
+	hostapd_neighbor_set(hapd, hapd->own_addr, &ssid, nr, hapd->iconf->lci,
+			     hapd->iconf->civic, hapd->iconf->stationary_ap, 0);
+
+	wpabuf_free(nr);
+#endif /* NEED_AP_MLME */
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/neighbor_db.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/neighbor_db.h
new file mode 100644
index 0000000..992671b
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/neighbor_db.h
@@ -0,0 +1,27 @@
+/*
+ * hostapd / Neighboring APs DB
+ * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH.
+ * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef NEIGHBOR_DB_H
+#define NEIGHBOR_DB_H
+
+struct hostapd_neighbor_entry *
+hostapd_neighbor_get(struct hostapd_data *hapd, const u8 *bssid,
+		     const struct wpa_ssid_value *ssid);
+int hostapd_neighbor_show(struct hostapd_data *hapd, char *buf, size_t buflen);
+int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid,
+			 const struct wpa_ssid_value *ssid,
+			 const struct wpabuf *nr, const struct wpabuf *lci,
+			 const struct wpabuf *civic, int stationary,
+			 u8 bss_parameters);
+void hostapd_neighbor_set_own_report(struct hostapd_data *hapd);
+int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid,
+			    const struct wpa_ssid_value *ssid);
+void hostapd_free_neighbor_db(struct hostapd_data *hapd);
+
+#endif /* NEIGHBOR_DB_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/p2p_hostapd.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/p2p_hostapd.c
new file mode 100644
index 0000000..9be640c
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/p2p_hostapd.c
@@ -0,0 +1,113 @@
+/*
+ * hostapd / P2P integration
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "p2p/p2p.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "sta_info.h"
+#include "p2p_hostapd.h"
+
+
+#ifdef CONFIG_P2P
+
+int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+			    char *buf, size_t buflen)
+{
+	if (sta->p2p_ie == NULL)
+		return 0;
+
+	return p2p_ie_text(sta->p2p_ie, buf, buf + buflen);
+}
+
+
+int hostapd_p2p_set_noa(struct hostapd_data *hapd, u8 count, int start,
+			int duration)
+{
+	wpa_printf(MSG_DEBUG, "P2P: Set NoA parameters: count=%u start=%d "
+		   "duration=%d", count, start, duration);
+
+	if (count == 0) {
+		hapd->noa_enabled = 0;
+		hapd->noa_start = 0;
+		hapd->noa_duration = 0;
+	}
+
+	if (count != 255) {
+		wpa_printf(MSG_DEBUG, "P2P: Non-periodic NoA - set "
+			   "NoA parameters");
+		return hostapd_driver_set_noa(hapd, count, start, duration);
+	}
+
+	hapd->noa_enabled = 1;
+	hapd->noa_start = start;
+	hapd->noa_duration = duration;
+
+	if (hapd->num_sta_no_p2p == 0) {
+		wpa_printf(MSG_DEBUG, "P2P: No legacy STAs connected - update "
+			   "periodic NoA parameters");
+		return hostapd_driver_set_noa(hapd, count, start, duration);
+	}
+
+	wpa_printf(MSG_DEBUG, "P2P: Legacy STA(s) connected - do not enable "
+		   "periodic NoA");
+
+	return 0;
+}
+
+
+void hostapd_p2p_non_p2p_sta_connected(struct hostapd_data *hapd)
+{
+	wpa_printf(MSG_DEBUG, "P2P: First non-P2P device connected");
+
+	if (hapd->noa_enabled) {
+		wpa_printf(MSG_DEBUG, "P2P: Disable periodic NoA");
+		hostapd_driver_set_noa(hapd, 0, 0, 0);
+	}
+}
+
+
+void hostapd_p2p_non_p2p_sta_disconnected(struct hostapd_data *hapd)
+{
+	wpa_printf(MSG_DEBUG, "P2P: Last non-P2P device disconnected");
+
+	if (hapd->noa_enabled) {
+		wpa_printf(MSG_DEBUG, "P2P: Enable periodic NoA");
+		hostapd_driver_set_noa(hapd, 255, hapd->noa_start,
+				       hapd->noa_duration);
+	}
+}
+
+#endif /* CONFIG_P2P */
+
+
+#ifdef CONFIG_P2P_MANAGER
+u8 * hostapd_eid_p2p_manage(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 bitmap;
+	*eid++ = WLAN_EID_VENDOR_SPECIFIC;
+	*eid++ = 4 + 3 + 1;
+	WPA_PUT_BE32(eid, P2P_IE_VENDOR_TYPE);
+	eid += 4;
+
+	*eid++ = P2P_ATTR_MANAGEABILITY;
+	WPA_PUT_LE16(eid, 1);
+	eid += 2;
+	bitmap = P2P_MAN_DEVICE_MANAGEMENT;
+	if (hapd->conf->p2p & P2P_ALLOW_CROSS_CONNECTION)
+		bitmap |= P2P_MAN_CROSS_CONNECTION_PERMITTED;
+	bitmap |= P2P_MAN_COEXISTENCE_OPTIONAL;
+	*eid++ = bitmap;
+
+	return eid;
+}
+#endif /* CONFIG_P2P_MANAGER */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/p2p_hostapd.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/p2p_hostapd.h
new file mode 100644
index 0000000..0e3921c
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/p2p_hostapd.h
@@ -0,0 +1,35 @@
+/*
+ * hostapd / P2P integration
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef P2P_HOSTAPD_H
+#define P2P_HOSTAPD_H
+
+#ifdef CONFIG_P2P
+
+int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+			    char *buf, size_t buflen);
+int hostapd_p2p_set_noa(struct hostapd_data *hapd, u8 count, int start,
+			int duration);
+void hostapd_p2p_non_p2p_sta_connected(struct hostapd_data *hapd);
+void hostapd_p2p_non_p2p_sta_disconnected(struct hostapd_data *hapd);
+
+
+#else /* CONFIG_P2P */
+
+static inline int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd,
+					  struct sta_info *sta,
+					  char *buf, size_t buflen)
+{
+	return 0;
+}
+
+#endif /* CONFIG_P2P */
+
+u8 * hostapd_eid_p2p_manage(struct hostapd_data *hapd, u8 *eid);
+
+#endif /* P2P_HOSTAPD_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/pmksa_cache_auth.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/pmksa_cache_auth.c
new file mode 100644
index 0000000..32d291d
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/pmksa_cache_auth.c
@@ -0,0 +1,736 @@
+/*
+ * hostapd - PMKSA cache for IEEE 802.11i RSN
+ * Copyright (c) 2004-2008, 2012-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
+#include "radius/radius_das.h"
+#include "sta_info.h"
+#include "ap_config.h"
+#include "pmksa_cache_auth.h"
+
+
+static const int pmksa_cache_max_entries = 1024;
+static const int dot11RSNAConfigPMKLifetime = 43200;
+
+struct rsn_pmksa_cache {
+#define PMKID_HASH_SIZE 128
+#define PMKID_HASH(pmkid) (unsigned int) ((pmkid)[0] & 0x7f)
+	struct rsn_pmksa_cache_entry *pmkid[PMKID_HASH_SIZE];
+	struct rsn_pmksa_cache_entry *pmksa;
+	int pmksa_count;
+
+	void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx);
+	void *ctx;
+};
+
+
+static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa);
+
+
+static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry)
+{
+	os_free(entry->vlan_desc);
+	os_free(entry->identity);
+	os_free(entry->dpp_pkhash);
+	wpabuf_free(entry->cui);
+#ifndef CONFIG_NO_RADIUS
+	radius_free_class(&entry->radius_class);
+#endif /* CONFIG_NO_RADIUS */
+	bin_clear_free(entry, sizeof(*entry));
+}
+
+
+void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
+			    struct rsn_pmksa_cache_entry *entry)
+{
+	struct rsn_pmksa_cache_entry *pos, *prev;
+	unsigned int hash;
+
+	pmksa->pmksa_count--;
+	pmksa->free_cb(entry, pmksa->ctx);
+
+	/* unlink from hash list */
+	hash = PMKID_HASH(entry->pmkid);
+	pos = pmksa->pmkid[hash];
+	prev = NULL;
+	while (pos) {
+		if (pos == entry) {
+			if (prev != NULL)
+				prev->hnext = entry->hnext;
+			else
+				pmksa->pmkid[hash] = entry->hnext;
+			break;
+		}
+		prev = pos;
+		pos = pos->hnext;
+	}
+
+	/* unlink from entry list */
+	pos = pmksa->pmksa;
+	prev = NULL;
+	while (pos) {
+		if (pos == entry) {
+			if (prev != NULL)
+				prev->next = entry->next;
+			else
+				pmksa->pmksa = entry->next;
+			break;
+		}
+		prev = pos;
+		pos = pos->next;
+	}
+
+	_pmksa_cache_free_entry(entry);
+}
+
+
+/**
+ * pmksa_cache_auth_flush - Flush all PMKSA cache entries
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
+ */
+void pmksa_cache_auth_flush(struct rsn_pmksa_cache *pmksa)
+{
+	while (pmksa->pmksa) {
+		wpa_printf(MSG_DEBUG, "RSN: Flush PMKSA cache entry for "
+			   MACSTR, MAC2STR(pmksa->pmksa->spa));
+		pmksa_cache_free_entry(pmksa, pmksa->pmksa);
+	}
+}
+
+
+static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
+{
+	struct rsn_pmksa_cache *pmksa = eloop_ctx;
+	struct os_reltime now;
+
+	os_get_reltime(&now);
+	while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) {
+		wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for "
+			   MACSTR, MAC2STR(pmksa->pmksa->spa));
+		pmksa_cache_free_entry(pmksa, pmksa->pmksa);
+	}
+
+	pmksa_cache_set_expiration(pmksa);
+}
+
+
+static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
+{
+	int sec;
+	struct os_reltime now;
+
+	eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
+	if (pmksa->pmksa == NULL)
+		return;
+	os_get_reltime(&now);
+	sec = pmksa->pmksa->expiration - now.sec;
+	if (sec < 0)
+		sec = 0;
+	eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL);
+}
+
+
+static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry,
+					struct eapol_state_machine *eapol)
+{
+	struct vlan_description *vlan_desc;
+
+	if (eapol == NULL)
+		return;
+
+	if (eapol->identity) {
+		entry->identity = os_malloc(eapol->identity_len);
+		if (entry->identity) {
+			entry->identity_len = eapol->identity_len;
+			os_memcpy(entry->identity, eapol->identity,
+				  eapol->identity_len);
+		}
+	}
+
+	if (eapol->radius_cui)
+		entry->cui = wpabuf_dup(eapol->radius_cui);
+
+#ifndef CONFIG_NO_RADIUS
+	radius_copy_class(&entry->radius_class, &eapol->radius_class);
+#endif /* CONFIG_NO_RADIUS */
+
+	entry->eap_type_authsrv = eapol->eap_type_authsrv;
+
+	vlan_desc = ((struct sta_info *) eapol->sta)->vlan_desc;
+	if (vlan_desc && vlan_desc->notempty) {
+		entry->vlan_desc = os_zalloc(sizeof(struct vlan_description));
+		if (entry->vlan_desc)
+			*entry->vlan_desc = *vlan_desc;
+	} else {
+		entry->vlan_desc = NULL;
+	}
+
+	entry->acct_multi_session_id = eapol->acct_multi_session_id;
+}
+
+
+void pmksa_cache_to_eapol_data(struct hostapd_data *hapd,
+			       struct rsn_pmksa_cache_entry *entry,
+			       struct eapol_state_machine *eapol)
+{
+	if (entry == NULL || eapol == NULL)
+		return;
+
+	if (entry->identity) {
+		os_free(eapol->identity);
+		eapol->identity = os_malloc(entry->identity_len);
+		if (eapol->identity) {
+			eapol->identity_len = entry->identity_len;
+			os_memcpy(eapol->identity, entry->identity,
+				  entry->identity_len);
+		}
+		wpa_hexdump_ascii(MSG_DEBUG, "STA identity from PMKSA",
+				  eapol->identity, eapol->identity_len);
+	}
+
+	if (entry->cui) {
+		wpabuf_free(eapol->radius_cui);
+		eapol->radius_cui = wpabuf_dup(entry->cui);
+	}
+
+#ifndef CONFIG_NO_RADIUS
+	radius_free_class(&eapol->radius_class);
+	radius_copy_class(&eapol->radius_class, &entry->radius_class);
+#endif /* CONFIG_NO_RADIUS */
+	if (eapol->radius_class.attr) {
+		wpa_printf(MSG_DEBUG, "Copied %lu Class attribute(s) from "
+			   "PMKSA", (unsigned long) eapol->radius_class.count);
+	}
+
+	eapol->eap_type_authsrv = entry->eap_type_authsrv;
+#ifndef CONFIG_NO_VLAN
+	ap_sta_set_vlan(hapd, eapol->sta, entry->vlan_desc);
+#endif /* CONFIG_NO_VLAN */
+
+	eapol->acct_multi_session_id = entry->acct_multi_session_id;
+}
+
+
+static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa,
+				   struct rsn_pmksa_cache_entry *entry)
+{
+	struct rsn_pmksa_cache_entry *pos, *prev;
+	int hash;
+
+	/* Add the new entry; order by expiration time */
+	pos = pmksa->pmksa;
+	prev = NULL;
+	while (pos) {
+		if (pos->expiration > entry->expiration)
+			break;
+		prev = pos;
+		pos = pos->next;
+	}
+	if (prev == NULL) {
+		entry->next = pmksa->pmksa;
+		pmksa->pmksa = entry;
+	} else {
+		entry->next = prev->next;
+		prev->next = entry;
+	}
+
+	hash = PMKID_HASH(entry->pmkid);
+	entry->hnext = pmksa->pmkid[hash];
+	pmksa->pmkid[hash] = entry;
+
+	pmksa->pmksa_count++;
+	if (prev == NULL)
+		pmksa_cache_set_expiration(pmksa);
+	wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR,
+		   MAC2STR(entry->spa));
+	wpa_hexdump(MSG_DEBUG, "RSN: added PMKID", entry->pmkid, PMKID_LEN);
+}
+
+
+/**
+ * pmksa_cache_auth_add - Add a PMKSA cache entry
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
+ * @pmk: The new pairwise master key
+ * @pmk_len: PMK length in bytes, usually PMK_LEN (32)
+ * @pmkid: Calculated PMKID
+ * @kck: Key confirmation key or %NULL if not yet derived
+ * @kck_len: KCK length in bytes
+ * @aa: Authenticator address
+ * @spa: Supplicant address
+ * @session_timeout: Session timeout
+ * @eapol: Pointer to EAPOL state machine data
+ * @akmp: WPA_KEY_MGMT_* used in key derivation
+ * Returns: Pointer to the added PMKSA cache entry or %NULL on error
+ *
+ * This function create a PMKSA entry for a new PMK and adds it to the PMKSA
+ * cache. If an old entry is already in the cache for the same Supplicant,
+ * this entry will be replaced with the new entry. PMKID will be calculated
+ * based on the PMK.
+ */
+struct rsn_pmksa_cache_entry *
+pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa,
+		     const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+		     const u8 *kck, size_t kck_len,
+		     const u8 *aa, const u8 *spa, int session_timeout,
+		     struct eapol_state_machine *eapol, int akmp)
+{
+	struct rsn_pmksa_cache_entry *entry;
+
+	entry = pmksa_cache_auth_create_entry(pmk, pmk_len, pmkid, kck, kck_len,
+					      aa, spa, session_timeout, eapol,
+					      akmp);
+
+	if (pmksa_cache_auth_add_entry(pmksa, entry) < 0)
+		return NULL;
+
+	return entry;
+}
+
+
+/**
+ * pmksa_cache_auth_create_entry - Create a PMKSA cache entry
+ * @pmk: The new pairwise master key
+ * @pmk_len: PMK length in bytes, usually PMK_LEN (32)
+ * @pmkid: Calculated PMKID
+ * @kck: Key confirmation key or %NULL if not yet derived
+ * @kck_len: KCK length in bytes
+ * @aa: Authenticator address
+ * @spa: Supplicant address
+ * @session_timeout: Session timeout
+ * @eapol: Pointer to EAPOL state machine data
+ * @akmp: WPA_KEY_MGMT_* used in key derivation
+ * Returns: Pointer to the added PMKSA cache entry or %NULL on error
+ *
+ * This function creates a PMKSA entry.
+ */
+struct rsn_pmksa_cache_entry *
+pmksa_cache_auth_create_entry(const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+			      const u8 *kck, size_t kck_len, const u8 *aa,
+			      const u8 *spa, int session_timeout,
+			      struct eapol_state_machine *eapol, int akmp)
+{
+	struct rsn_pmksa_cache_entry *entry;
+	struct os_reltime now;
+
+	if (pmk_len > PMK_LEN_MAX)
+		return NULL;
+
+	if (wpa_key_mgmt_suite_b(akmp) && !kck)
+		return NULL;
+
+	entry = os_zalloc(sizeof(*entry));
+	if (entry == NULL)
+		return NULL;
+	os_memcpy(entry->pmk, pmk, pmk_len);
+	entry->pmk_len = pmk_len;
+	if (pmkid)
+		os_memcpy(entry->pmkid, pmkid, PMKID_LEN);
+	else if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+		rsn_pmkid_suite_b_192(kck, kck_len, aa, spa, entry->pmkid);
+	else if (wpa_key_mgmt_suite_b(akmp))
+		rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid);
+	else
+		rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, akmp);
+	os_get_reltime(&now);
+	entry->expiration = now.sec;
+	if (session_timeout > 0)
+		entry->expiration += session_timeout;
+	else
+		entry->expiration += dot11RSNAConfigPMKLifetime;
+	entry->akmp = akmp;
+	os_memcpy(entry->spa, spa, ETH_ALEN);
+	pmksa_cache_from_eapol_data(entry, eapol);
+
+	return entry;
+}
+
+
+/**
+ * pmksa_cache_auth_add_entry - Add a PMKSA cache entry
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
+ * @entry: Pointer to PMKSA cache entry
+ *
+ * This function adds PMKSA cache entry to the PMKSA cache. If an old entry is
+ * already in the cache for the same Supplicant, this entry will be replaced
+ * with the new entry. PMKID will be calculated based on the PMK.
+ */
+int pmksa_cache_auth_add_entry(struct rsn_pmksa_cache *pmksa,
+			       struct rsn_pmksa_cache_entry *entry)
+{
+	struct rsn_pmksa_cache_entry *pos;
+
+	if (entry == NULL)
+		return -1;
+
+	/* Replace an old entry for the same STA (if found) with the new entry
+	 */
+	pos = pmksa_cache_auth_get(pmksa, entry->spa, NULL);
+	if (pos)
+		pmksa_cache_free_entry(pmksa, pos);
+
+	if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) {
+		/* Remove the oldest entry to make room for the new entry */
+		wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache "
+			   "entry (for " MACSTR ") to make room for new one",
+			   MAC2STR(pmksa->pmksa->spa));
+		pmksa_cache_free_entry(pmksa, pmksa->pmksa);
+	}
+
+	pmksa_cache_link_entry(pmksa, entry);
+
+	return 0;
+}
+
+
+struct rsn_pmksa_cache_entry *
+pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa,
+		    const struct rsn_pmksa_cache_entry *old_entry,
+		    const u8 *aa, const u8 *pmkid)
+{
+	struct rsn_pmksa_cache_entry *entry;
+
+	entry = os_zalloc(sizeof(*entry));
+	if (entry == NULL)
+		return NULL;
+	os_memcpy(entry->pmkid, pmkid, PMKID_LEN);
+	os_memcpy(entry->pmk, old_entry->pmk, old_entry->pmk_len);
+	entry->pmk_len = old_entry->pmk_len;
+	entry->expiration = old_entry->expiration;
+	entry->akmp = old_entry->akmp;
+	os_memcpy(entry->spa, old_entry->spa, ETH_ALEN);
+	entry->opportunistic = 1;
+	if (old_entry->identity) {
+		entry->identity = os_malloc(old_entry->identity_len);
+		if (entry->identity) {
+			entry->identity_len = old_entry->identity_len;
+			os_memcpy(entry->identity, old_entry->identity,
+				  old_entry->identity_len);
+		}
+	}
+	if (old_entry->cui)
+		entry->cui = wpabuf_dup(old_entry->cui);
+#ifndef CONFIG_NO_RADIUS
+	radius_copy_class(&entry->radius_class, &old_entry->radius_class);
+#endif /* CONFIG_NO_RADIUS */
+	entry->eap_type_authsrv = old_entry->eap_type_authsrv;
+	if (old_entry->vlan_desc) {
+		entry->vlan_desc = os_zalloc(sizeof(struct vlan_description));
+		if (entry->vlan_desc)
+			*entry->vlan_desc = *old_entry->vlan_desc;
+	} else {
+		entry->vlan_desc = NULL;
+	}
+	entry->opportunistic = 1;
+
+	pmksa_cache_link_entry(pmksa, entry);
+
+	return entry;
+}
+
+
+/**
+ * pmksa_cache_auth_deinit - Free all entries in PMKSA cache
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
+ */
+void pmksa_cache_auth_deinit(struct rsn_pmksa_cache *pmksa)
+{
+	struct rsn_pmksa_cache_entry *entry, *prev;
+	int i;
+
+	if (pmksa == NULL)
+		return;
+
+	entry = pmksa->pmksa;
+	while (entry) {
+		prev = entry;
+		entry = entry->next;
+		_pmksa_cache_free_entry(prev);
+	}
+	eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
+	pmksa->pmksa_count = 0;
+	pmksa->pmksa = NULL;
+	for (i = 0; i < PMKID_HASH_SIZE; i++)
+		pmksa->pmkid[i] = NULL;
+	os_free(pmksa);
+}
+
+
+/**
+ * pmksa_cache_auth_get - Fetch a PMKSA cache entry
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
+ * @spa: Supplicant address or %NULL to match any
+ * @pmkid: PMKID or %NULL to match any
+ * Returns: Pointer to PMKSA cache entry or %NULL if no match was found
+ */
+struct rsn_pmksa_cache_entry *
+pmksa_cache_auth_get(struct rsn_pmksa_cache *pmksa,
+		     const u8 *spa, const u8 *pmkid)
+{
+	struct rsn_pmksa_cache_entry *entry;
+
+	if (pmkid) {
+		for (entry = pmksa->pmkid[PMKID_HASH(pmkid)]; entry;
+		     entry = entry->hnext) {
+			if ((spa == NULL ||
+			     os_memcmp(entry->spa, spa, ETH_ALEN) == 0) &&
+			    os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0)
+				return entry;
+		}
+	} else {
+		for (entry = pmksa->pmksa; entry; entry = entry->next) {
+			if (spa == NULL ||
+			    os_memcmp(entry->spa, spa, ETH_ALEN) == 0)
+				return entry;
+		}
+	}
+
+	return NULL;
+}
+
+
+/**
+ * pmksa_cache_get_okc - Fetch a PMKSA cache entry using OKC
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
+ * @aa: Authenticator address
+ * @spa: Supplicant address
+ * @pmkid: PMKID
+ * Returns: Pointer to PMKSA cache entry or %NULL if no match was found
+ *
+ * Use opportunistic key caching (OKC) to find a PMK for a supplicant.
+ */
+struct rsn_pmksa_cache_entry * pmksa_cache_get_okc(
+	struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *spa,
+	const u8 *pmkid)
+{
+	struct rsn_pmksa_cache_entry *entry;
+	u8 new_pmkid[PMKID_LEN];
+
+	for (entry = pmksa->pmksa; entry; entry = entry->next) {
+		if (os_memcmp(entry->spa, spa, ETH_ALEN) != 0)
+			continue;
+		if (wpa_key_mgmt_sae(entry->akmp) ||
+		    wpa_key_mgmt_fils(entry->akmp)) {
+			if (os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0)
+				return entry;
+			continue;
+		}
+		rsn_pmkid(entry->pmk, entry->pmk_len, aa, spa, new_pmkid,
+			  entry->akmp);
+		if (os_memcmp(new_pmkid, pmkid, PMKID_LEN) == 0)
+			return entry;
+	}
+	return NULL;
+}
+
+
+/**
+ * pmksa_cache_auth_init - Initialize PMKSA cache
+ * @free_cb: Callback function to be called when a PMKSA cache entry is freed
+ * @ctx: Context pointer for free_cb function
+ * Returns: Pointer to PMKSA cache data or %NULL on failure
+ */
+struct rsn_pmksa_cache *
+pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
+				      void *ctx), void *ctx)
+{
+	struct rsn_pmksa_cache *pmksa;
+
+	pmksa = os_zalloc(sizeof(*pmksa));
+	if (pmksa) {
+		pmksa->free_cb = free_cb;
+		pmksa->ctx = ctx;
+	}
+
+	return pmksa;
+}
+
+
+static int das_attr_match(struct rsn_pmksa_cache_entry *entry,
+			  struct radius_das_attrs *attr)
+{
+	int match = 0;
+
+	if (attr->sta_addr) {
+		if (os_memcmp(attr->sta_addr, entry->spa, ETH_ALEN) != 0)
+			return 0;
+		match++;
+	}
+
+	if (attr->acct_multi_session_id) {
+		char buf[20];
+
+		if (attr->acct_multi_session_id_len != 16)
+			return 0;
+		os_snprintf(buf, sizeof(buf), "%016llX",
+			    (unsigned long long) entry->acct_multi_session_id);
+		if (os_memcmp(attr->acct_multi_session_id, buf, 16) != 0)
+			return 0;
+		match++;
+	}
+
+	if (attr->cui) {
+		if (!entry->cui ||
+		    attr->cui_len != wpabuf_len(entry->cui) ||
+		    os_memcmp(attr->cui, wpabuf_head(entry->cui),
+			      attr->cui_len) != 0)
+			return 0;
+		match++;
+	}
+
+	if (attr->user_name) {
+		if (!entry->identity ||
+		    attr->user_name_len != entry->identity_len ||
+		    os_memcmp(attr->user_name, entry->identity,
+			      attr->user_name_len) != 0)
+			return 0;
+		match++;
+	}
+
+	return match;
+}
+
+
+int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa,
+					   struct radius_das_attrs *attr)
+{
+	int found = 0;
+	struct rsn_pmksa_cache_entry *entry, *prev;
+
+	if (attr->acct_session_id)
+		return -1;
+
+	entry = pmksa->pmksa;
+	while (entry) {
+		if (das_attr_match(entry, attr)) {
+			found++;
+			prev = entry;
+			entry = entry->next;
+			pmksa_cache_free_entry(pmksa, prev);
+			continue;
+		}
+		entry = entry->next;
+	}
+
+	return found ? 0 : -1;
+}
+
+
+/**
+ * pmksa_cache_auth_list - Dump text list of entries in PMKSA cache
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
+ * @buf: Buffer for the list
+ * @len: Length of the buffer
+ * Returns: Number of bytes written to buffer
+ *
+ * This function is used to generate a text format representation of the
+ * current PMKSA cache contents for the ctrl_iface PMKSA command.
+ */
+int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len)
+{
+	int i, ret;
+	char *pos = buf;
+	struct rsn_pmksa_cache_entry *entry;
+	struct os_reltime now;
+
+	os_get_reltime(&now);
+	ret = os_snprintf(pos, buf + len - pos,
+			  "Index / SPA / PMKID / expiration (in seconds) / opportunistic\n");
+	if (os_snprintf_error(buf + len - pos, ret))
+		return pos - buf;
+	pos += ret;
+	i = 0;
+	entry = pmksa->pmksa;
+	while (entry) {
+		ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ",
+				  i, MAC2STR(entry->spa));
+		if (os_snprintf_error(buf + len - pos, ret))
+			return pos - buf;
+		pos += ret;
+		pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid,
+					PMKID_LEN);
+		ret = os_snprintf(pos, buf + len - pos, " %d %d\n",
+				  (int) (entry->expiration - now.sec),
+				  entry->opportunistic);
+		if (os_snprintf_error(buf + len - pos, ret))
+			return pos - buf;
+		pos += ret;
+		entry = entry->next;
+	}
+	return pos - buf;
+}
+
+
+#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
+#ifdef CONFIG_MESH
+
+/**
+ * pmksa_cache_auth_list_mesh - Dump text list of entries in PMKSA cache
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
+ * @addr: MAC address of the peer (NULL means any)
+ * @buf: Buffer for the list
+ * @len: Length of the buffer
+ * Returns: Number of bytes written to buffer
+ *
+ * This function is used to generate a text format representation of the
+ * current PMKSA cache contents for the ctrl_iface PMKSA_GET command to store
+ * in external storage.
+ */
+int pmksa_cache_auth_list_mesh(struct rsn_pmksa_cache *pmksa, const u8 *addr,
+			       char *buf, size_t len)
+{
+	int ret;
+	char *pos, *end;
+	struct rsn_pmksa_cache_entry *entry;
+	struct os_reltime now;
+
+	pos = buf;
+	end = buf + len;
+	os_get_reltime(&now);
+
+
+	/*
+	 * Entry format:
+	 * <BSSID> <PMKID> <PMK> <expiration in seconds>
+	 */
+	for (entry = pmksa->pmksa; entry; entry = entry->next) {
+		if (addr && os_memcmp(entry->spa, addr, ETH_ALEN) != 0)
+			continue;
+
+		ret = os_snprintf(pos, end - pos, MACSTR " ",
+				  MAC2STR(entry->spa));
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+
+		pos += wpa_snprintf_hex(pos, end - pos, entry->pmkid,
+					PMKID_LEN);
+
+		ret = os_snprintf(pos, end - pos, " ");
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+
+		pos += wpa_snprintf_hex(pos, end - pos, entry->pmk,
+					entry->pmk_len);
+
+		ret = os_snprintf(pos, end - pos, " %d\n",
+				  (int) (entry->expiration - now.sec));
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+	}
+
+	return pos - buf;
+}
+
+#endif /* CONFIG_MESH */
+#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/pmksa_cache_auth.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/pmksa_cache_auth.h
new file mode 100644
index 0000000..e3cee4a
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/pmksa_cache_auth.h
@@ -0,0 +1,81 @@
+/*
+ * hostapd - PMKSA cache for IEEE 802.11i RSN
+ * Copyright (c) 2004-2008, 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef PMKSA_CACHE_H
+#define PMKSA_CACHE_H
+
+#include "radius/radius.h"
+
+/**
+ * struct rsn_pmksa_cache_entry - PMKSA cache entry
+ */
+struct rsn_pmksa_cache_entry {
+	struct rsn_pmksa_cache_entry *next, *hnext;
+	u8 pmkid[PMKID_LEN];
+	u8 pmk[PMK_LEN_MAX];
+	size_t pmk_len;
+	os_time_t expiration;
+	int akmp; /* WPA_KEY_MGMT_* */
+	u8 spa[ETH_ALEN];
+
+	u8 *dpp_pkhash; /* SHA256_MAC_LEN octet hash value of DPP Connector
+			 * public key */
+	u8 *identity;
+	size_t identity_len;
+	struct wpabuf *cui;
+	struct radius_class_data radius_class;
+	u8 eap_type_authsrv;
+	struct vlan_description *vlan_desc;
+	int opportunistic;
+
+	u64 acct_multi_session_id;
+};
+
+struct rsn_pmksa_cache;
+struct radius_das_attrs;
+
+struct rsn_pmksa_cache *
+pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
+				      void *ctx), void *ctx);
+void pmksa_cache_auth_deinit(struct rsn_pmksa_cache *pmksa);
+struct rsn_pmksa_cache_entry *
+pmksa_cache_auth_get(struct rsn_pmksa_cache *pmksa,
+		     const u8 *spa, const u8 *pmkid);
+struct rsn_pmksa_cache_entry * pmksa_cache_get_okc(
+	struct rsn_pmksa_cache *pmksa, const u8 *spa, const u8 *aa,
+	const u8 *pmkid);
+struct rsn_pmksa_cache_entry *
+pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa,
+		     const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+		     const u8 *kck, size_t kck_len,
+		     const u8 *aa, const u8 *spa, int session_timeout,
+		     struct eapol_state_machine *eapol, int akmp);
+struct rsn_pmksa_cache_entry *
+pmksa_cache_auth_create_entry(const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+			      const u8 *kck, size_t kck_len, const u8 *aa,
+			      const u8 *spa, int session_timeout,
+			      struct eapol_state_machine *eapol, int akmp);
+int pmksa_cache_auth_add_entry(struct rsn_pmksa_cache *pmksa,
+			       struct rsn_pmksa_cache_entry *entry);
+struct rsn_pmksa_cache_entry *
+pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa,
+		    const struct rsn_pmksa_cache_entry *old_entry,
+		    const u8 *aa, const u8 *pmkid);
+void pmksa_cache_to_eapol_data(struct hostapd_data *hapd,
+			       struct rsn_pmksa_cache_entry *entry,
+			       struct eapol_state_machine *eapol);
+void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
+			    struct rsn_pmksa_cache_entry *entry);
+int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa,
+					   struct radius_das_attrs *attr);
+int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len);
+void pmksa_cache_auth_flush(struct rsn_pmksa_cache *pmksa);
+int pmksa_cache_auth_list_mesh(struct rsn_pmksa_cache *pmksa, const u8 *addr,
+			       char *buf, size_t len);
+
+#endif /* PMKSA_CACHE_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/preauth_auth.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/preauth_auth.c
new file mode 100644
index 0000000..3284a10
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/preauth_auth.c
@@ -0,0 +1,273 @@
+/*
+ * hostapd - Authenticator for IEEE 802.11i RSN pre-authentication
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#ifdef CONFIG_RSN_PREAUTH
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "l2_packet/l2_packet.h"
+#include "common/wpa_common.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ieee802_1x.h"
+#include "sta_info.h"
+#include "wpa_auth.h"
+#include "preauth_auth.h"
+
+#ifndef ETH_P_PREAUTH
+#define ETH_P_PREAUTH 0x88C7 /* IEEE 802.11i pre-authentication */
+#endif /* ETH_P_PREAUTH */
+
+static const int dot11RSNAConfigPMKLifetime = 43200;
+
+struct rsn_preauth_interface {
+	struct rsn_preauth_interface *next;
+	struct hostapd_data *hapd;
+	struct l2_packet_data *l2;
+	char *ifname;
+	int ifindex;
+};
+
+
+static void rsn_preauth_receive(void *ctx, const u8 *src_addr,
+				const u8 *buf, size_t len)
+{
+	struct rsn_preauth_interface *piface = ctx;
+	struct hostapd_data *hapd = piface->hapd;
+	struct ieee802_1x_hdr *hdr;
+	struct sta_info *sta;
+	struct l2_ethhdr *ethhdr;
+
+	wpa_printf(MSG_DEBUG, "RSN: receive pre-auth packet "
+		   "from interface '%s'", piface->ifname);
+	if (len < sizeof(*ethhdr) + sizeof(*hdr)) {
+		wpa_printf(MSG_DEBUG, "RSN: too short pre-auth packet "
+			   "(len=%lu)", (unsigned long) len);
+		return;
+	}
+
+	ethhdr = (struct l2_ethhdr *) buf;
+	hdr = (struct ieee802_1x_hdr *) (ethhdr + 1);
+
+	if (os_memcmp(ethhdr->h_dest, hapd->own_addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "RSN: pre-auth for foreign address "
+			   MACSTR, MAC2STR(ethhdr->h_dest));
+		return;
+	}
+
+	sta = ap_get_sta(hapd, ethhdr->h_source);
+	if (sta && (sta->flags & WLAN_STA_ASSOC)) {
+		wpa_printf(MSG_DEBUG, "RSN: pre-auth for already association "
+			   "STA " MACSTR, MAC2STR(sta->addr));
+		return;
+	}
+	if (!sta && hdr->type == IEEE802_1X_TYPE_EAPOL_START) {
+		sta = ap_sta_add(hapd, ethhdr->h_source);
+		if (sta == NULL)
+			return;
+		sta->flags = WLAN_STA_PREAUTH;
+
+		ieee802_1x_new_station(hapd, sta);
+		if (sta->eapol_sm == NULL) {
+			ap_free_sta(hapd, sta);
+			sta = NULL;
+		} else {
+			sta->eapol_sm->radius_identifier = -1;
+			sta->eapol_sm->portValid = true;
+			sta->eapol_sm->flags |= EAPOL_SM_PREAUTH;
+		}
+	}
+	if (sta == NULL)
+		return;
+	sta->preauth_iface = piface;
+	ieee802_1x_receive(hapd, ethhdr->h_source, (u8 *) (ethhdr + 1),
+			   len - sizeof(*ethhdr), FRAME_ENCRYPTION_UNKNOWN);
+}
+
+
+static int rsn_preauth_iface_add(struct hostapd_data *hapd, const char *ifname)
+{
+	struct rsn_preauth_interface *piface;
+
+	wpa_printf(MSG_DEBUG, "RSN pre-auth interface '%s'", ifname);
+
+	piface = os_zalloc(sizeof(*piface));
+	if (piface == NULL)
+		return -1;
+	piface->hapd = hapd;
+
+	piface->ifname = os_strdup(ifname);
+	if (piface->ifname == NULL) {
+		goto fail1;
+	}
+
+	piface->l2 = l2_packet_init(piface->ifname, NULL, ETH_P_PREAUTH,
+				    rsn_preauth_receive, piface, 1);
+	if (piface->l2 == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to open register layer 2 access "
+			   "to ETH_P_PREAUTH");
+		goto fail2;
+	}
+
+	piface->next = hapd->preauth_iface;
+	hapd->preauth_iface = piface;
+	return 0;
+
+fail2:
+	os_free(piface->ifname);
+fail1:
+	os_free(piface);
+	return -1;
+}
+
+
+void rsn_preauth_iface_deinit(struct hostapd_data *hapd)
+{
+	struct rsn_preauth_interface *piface, *prev;
+
+	piface = hapd->preauth_iface;
+	hapd->preauth_iface = NULL;
+	while (piface) {
+		prev = piface;
+		piface = piface->next;
+		l2_packet_deinit(prev->l2);
+		os_free(prev->ifname);
+		os_free(prev);
+	}
+}
+
+
+int rsn_preauth_iface_init(struct hostapd_data *hapd)
+{
+	char *tmp, *start, *end;
+
+	if (hapd->conf->rsn_preauth_interfaces == NULL)
+		return 0;
+
+	tmp = os_strdup(hapd->conf->rsn_preauth_interfaces);
+	if (tmp == NULL)
+		return -1;
+	start = tmp;
+	for (;;) {
+		while (*start == ' ')
+			start++;
+		if (*start == '\0')
+			break;
+		end = os_strchr(start, ' ');
+		if (end)
+			*end = '\0';
+
+		if (rsn_preauth_iface_add(hapd, start)) {
+			rsn_preauth_iface_deinit(hapd);
+			os_free(tmp);
+			return -1;
+		}
+
+		if (end)
+			start = end + 1;
+		else
+			break;
+	}
+	os_free(tmp);
+	return 0;
+}
+
+
+static void rsn_preauth_finished_cb(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct sta_info *sta = timeout_ctx;
+	wpa_printf(MSG_DEBUG, "RSN: Removing pre-authentication STA entry for "
+		   MACSTR, MAC2STR(sta->addr));
+	ap_free_sta(hapd, sta);
+}
+
+
+void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta,
+			  int success)
+{
+	const u8 *key;
+	size_t len;
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+		       HOSTAPD_LEVEL_INFO, "pre-authentication %s",
+		       success ? "succeeded" : "failed");
+
+	key = ieee802_1x_get_key(sta->eapol_sm, &len);
+	if (len > PMK_LEN)
+		len = PMK_LEN;
+	if (success && key) {
+		if (wpa_auth_pmksa_add_preauth(hapd->wpa_auth, key, len,
+					       sta->addr,
+					       dot11RSNAConfigPMKLifetime,
+					       sta->eapol_sm) == 0) {
+			hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "added PMKSA cache entry (pre-auth)");
+		} else {
+			hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "failed to add PMKSA cache entry "
+				       "(pre-auth)");
+		}
+	}
+
+	/*
+	 * Finish STA entry removal from timeout in order to avoid freeing
+	 * STA data before the caller has finished processing.
+	 */
+	eloop_register_timeout(0, 0, rsn_preauth_finished_cb, hapd, sta);
+}
+
+
+void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta,
+		      u8 *buf, size_t len)
+{
+	struct rsn_preauth_interface *piface;
+	struct l2_ethhdr *ethhdr;
+
+	piface = hapd->preauth_iface;
+	while (piface) {
+		if (piface == sta->preauth_iface)
+			break;
+		piface = piface->next;
+	}
+
+	if (piface == NULL) {
+		wpa_printf(MSG_DEBUG, "RSN: Could not find pre-authentication "
+			   "interface for " MACSTR, MAC2STR(sta->addr));
+		return;
+	}
+
+	ethhdr = os_malloc(sizeof(*ethhdr) + len);
+	if (ethhdr == NULL)
+		return;
+
+	os_memcpy(ethhdr->h_dest, sta->addr, ETH_ALEN);
+	os_memcpy(ethhdr->h_source, hapd->own_addr, ETH_ALEN);
+	ethhdr->h_proto = host_to_be16(ETH_P_PREAUTH);
+	os_memcpy(ethhdr + 1, buf, len);
+
+	if (l2_packet_send(piface->l2, sta->addr, ETH_P_PREAUTH, (u8 *) ethhdr,
+			   sizeof(*ethhdr) + len) < 0) {
+		wpa_printf(MSG_ERROR, "Failed to send preauth packet using "
+			   "l2_packet_send\n");
+	}
+	os_free(ethhdr);
+}
+
+
+void rsn_preauth_free_station(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	eloop_cancel_timeout(rsn_preauth_finished_cb, hapd, sta);
+}
+
+#endif /* CONFIG_RSN_PREAUTH */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/preauth_auth.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/preauth_auth.h
new file mode 100644
index 0000000..69fb356
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/preauth_auth.h
@@ -0,0 +1,52 @@
+/*
+ * hostapd - Authenticator for IEEE 802.11i RSN pre-authentication
+ * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef PREAUTH_H
+#define PREAUTH_H
+
+#ifdef CONFIG_RSN_PREAUTH
+
+int rsn_preauth_iface_init(struct hostapd_data *hapd);
+void rsn_preauth_iface_deinit(struct hostapd_data *hapd);
+void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta,
+			  int success);
+void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta,
+		      u8 *buf, size_t len);
+void rsn_preauth_free_station(struct hostapd_data *hapd, struct sta_info *sta);
+
+#else /* CONFIG_RSN_PREAUTH */
+
+static inline int rsn_preauth_iface_init(struct hostapd_data *hapd)
+{
+	return 0;
+}
+
+static inline void rsn_preauth_iface_deinit(struct hostapd_data *hapd)
+{
+}
+
+static inline void rsn_preauth_finished(struct hostapd_data *hapd,
+					struct sta_info *sta,
+					int success)
+{
+}
+
+static inline void rsn_preauth_send(struct hostapd_data *hapd,
+				    struct sta_info *sta,
+				    u8 *buf, size_t len)
+{
+}
+
+static inline void rsn_preauth_free_station(struct hostapd_data *hapd,
+					    struct sta_info *sta)
+{
+}
+
+#endif /* CONFIG_RSN_PREAUTH */
+
+#endif /* PREAUTH_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/rrm.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/rrm.c
new file mode 100644
index 0000000..8220590
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/rrm.c
@@ -0,0 +1,682 @@
+/*
+ * hostapd / Radio Measurement (RRM)
+ * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH.
+ * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved.
+ * Copyright (c) 2016-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/wpa_ctrl.h"
+#include "hostapd.h"
+#include "ap_drv_ops.h"
+#include "sta_info.h"
+#include "eloop.h"
+#include "neighbor_db.h"
+#include "rrm.h"
+
+#define HOSTAPD_RRM_REQUEST_TIMEOUT 5
+
+
+static void hostapd_lci_rep_timeout_handler(void *eloop_data, void *user_ctx)
+{
+	struct hostapd_data *hapd = eloop_data;
+
+	wpa_printf(MSG_DEBUG, "RRM: LCI request (token %u) timed out",
+		   hapd->lci_req_token);
+	hapd->lci_req_active = 0;
+}
+
+
+static void hostapd_handle_lci_report(struct hostapd_data *hapd, u8 token,
+				      const u8 *pos, size_t len)
+{
+	if (!hapd->lci_req_active || hapd->lci_req_token != token) {
+		wpa_printf(MSG_DEBUG, "Unexpected LCI report, token %u", token);
+		return;
+	}
+
+	hapd->lci_req_active = 0;
+	eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd, NULL);
+	wpa_printf(MSG_DEBUG, "LCI report token %u len %zu", token, len);
+}
+
+
+static void hostapd_range_rep_timeout_handler(void *eloop_data, void *user_ctx)
+{
+	struct hostapd_data *hapd = eloop_data;
+
+	wpa_printf(MSG_DEBUG, "RRM: Range request (token %u) timed out",
+		   hapd->range_req_token);
+	hapd->range_req_active = 0;
+}
+
+
+static void hostapd_handle_range_report(struct hostapd_data *hapd, u8 token,
+					const u8 *pos, size_t len)
+{
+	if (!hapd->range_req_active || hapd->range_req_token != token) {
+		wpa_printf(MSG_DEBUG, "Unexpected range report, token %u",
+			   token);
+		return;
+	}
+
+	hapd->range_req_active = 0;
+	eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL);
+	wpa_printf(MSG_DEBUG, "Range report token %u len %zu", token, len);
+}
+
+
+static void hostapd_handle_beacon_report(struct hostapd_data *hapd,
+					 const u8 *addr, u8 token, u8 rep_mode,
+					 const u8 *pos, size_t len)
+{
+	char report[2 * 255 + 1];
+
+	wpa_printf(MSG_DEBUG, "Beacon report token %u len %zu from " MACSTR,
+		   token, len, MAC2STR(addr));
+	/* Skip to the beginning of the Beacon report */
+	if (len < 3)
+		return;
+	pos += 3;
+	len -= 3;
+	report[0] = '\0';
+	if (wpa_snprintf_hex(report, sizeof(report), pos, len) < 0)
+		return;
+	wpa_msg(hapd->msg_ctx, MSG_INFO, BEACON_RESP_RX MACSTR " %u %02x %s",
+		MAC2STR(addr), token, rep_mode, report);
+	if (len < sizeof(struct rrm_measurement_beacon_report))
+		return;
+	hostapd_ubus_notify_beacon_report(hapd, addr, token, rep_mode, (struct rrm_measurement_beacon_report*) pos, len);
+}
+
+
+static void hostapd_handle_radio_msmt_report(struct hostapd_data *hapd,
+					     const u8 *buf, size_t len)
+{
+	const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
+	const u8 *pos, *ie, *end;
+	u8 token, rep_mode;
+
+	end = buf + len;
+	token = mgmt->u.action.u.rrm.dialog_token;
+	pos = mgmt->u.action.u.rrm.variable;
+
+	while ((ie = get_ie(pos, end - pos, WLAN_EID_MEASURE_REPORT))) {
+		if (ie[1] < 3) {
+			wpa_printf(MSG_DEBUG, "Bad Measurement Report element");
+			break;
+		}
+
+		rep_mode = ie[3];
+		wpa_printf(MSG_DEBUG, "Measurement report mode 0x%x type %u",
+			   rep_mode, ie[4]);
+
+		switch (ie[4]) {
+		case MEASURE_TYPE_LCI:
+			hostapd_handle_lci_report(hapd, token, ie + 2, ie[1]);
+			break;
+		case MEASURE_TYPE_FTM_RANGE:
+			hostapd_handle_range_report(hapd, token, ie + 2, ie[1]);
+			break;
+		case MEASURE_TYPE_BEACON:
+			hostapd_handle_beacon_report(hapd, mgmt->sa, token,
+						     rep_mode, ie + 2, ie[1]);
+			break;
+		default:
+			wpa_printf(MSG_DEBUG,
+				   "Measurement report type %u is not supported",
+				   ie[4]);
+			break;
+		}
+
+		pos = ie + ie[1] + 2;
+	}
+}
+
+
+static u16 hostapd_parse_location_lci_req_age(const u8 *buf, size_t len)
+{
+	const u8 *subelem;
+
+	/* Range Request element + Location Subject + Maximum Age subelement */
+	if (len < 3 + 1 + 4)
+		return 0;
+
+	/* Subelements are arranged as IEs */
+	subelem = get_ie(buf + 4, len - 4, LCI_REQ_SUBELEM_MAX_AGE);
+	if (subelem && subelem[1] == 2)
+		return WPA_GET_LE16(subelem + 2);
+
+	return 0;
+}
+
+
+static int hostapd_check_lci_age(struct hostapd_neighbor_entry *nr, u16 max_age)
+{
+	struct os_time curr, diff;
+	unsigned long diff_l;
+
+	if (nr->stationary || max_age == 0xffff)
+		return 1;
+
+	if (!max_age)
+		return 0;
+
+	if (os_get_time(&curr))
+		return 0;
+
+	os_time_sub(&curr, &nr->lci_date, &diff);
+
+	/* avoid overflow */
+	if (diff.sec > 0xffff)
+		return 0;
+
+	/* LCI age is calculated in 10th of a second units. */
+	diff_l = diff.sec * 10 + diff.usec / 100000;
+
+	return max_age > diff_l;
+}
+
+
+static size_t hostapd_neighbor_report_len(struct wpabuf *buf,
+					  struct hostapd_neighbor_entry *nr,
+					  int send_lci, int send_civic)
+{
+	size_t len = 2 + wpabuf_len(nr->nr);
+
+	if (send_lci && nr->lci)
+		len += 2 + wpabuf_len(nr->lci);
+
+	if (send_civic && nr->civic)
+		len += 2 + wpabuf_len(nr->civic);
+
+	return len;
+}
+
+
+static void hostapd_send_nei_report_resp(struct hostapd_data *hapd,
+					 const u8 *addr, u8 dialog_token,
+					 struct wpa_ssid_value *ssid, u8 lci,
+					 u8 civic, u16 lci_max_age)
+{
+	struct hostapd_neighbor_entry *nr;
+	struct wpabuf *buf;
+	u8 *msmt_token;
+
+	/*
+	 * The number and length of the Neighbor Report elements in a Neighbor
+	 * Report frame is limited by the maximum allowed MMPDU size; + 3 bytes
+	 * of RRM header.
+	 */
+	buf = wpabuf_alloc(3 + IEEE80211_MAX_MMPDU_SIZE);
+	if (!buf)
+		return;
+
+	wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
+	wpabuf_put_u8(buf, WLAN_RRM_NEIGHBOR_REPORT_RESPONSE);
+	wpabuf_put_u8(buf, dialog_token);
+
+	dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
+			 list) {
+		int send_lci;
+		size_t len;
+
+		if (ssid->ssid_len != nr->ssid.ssid_len ||
+		    os_memcmp(ssid->ssid, nr->ssid.ssid, ssid->ssid_len) != 0)
+			continue;
+
+		send_lci = (lci != 0) && hostapd_check_lci_age(nr, lci_max_age);
+		len = hostapd_neighbor_report_len(buf, nr, send_lci, civic);
+
+		if (len - 2 > 0xff) {
+			wpa_printf(MSG_DEBUG,
+				   "NR entry for " MACSTR " exceeds 0xFF bytes",
+				   MAC2STR(nr->bssid));
+			continue;
+		}
+
+		if (len > wpabuf_tailroom(buf))
+			break;
+
+		wpabuf_put_u8(buf, WLAN_EID_NEIGHBOR_REPORT);
+		wpabuf_put_u8(buf, len - 2);
+		wpabuf_put_buf(buf, nr->nr);
+
+		if (send_lci && nr->lci) {
+			wpabuf_put_u8(buf, WLAN_EID_MEASURE_REPORT);
+			wpabuf_put_u8(buf, wpabuf_len(nr->lci));
+			/*
+			 * Override measurement token - the first byte of the
+			 * Measurement Report element.
+			 */
+			msmt_token = wpabuf_put(buf, 0);
+			wpabuf_put_buf(buf, nr->lci);
+			*msmt_token = lci;
+		}
+
+		if (civic && nr->civic) {
+			wpabuf_put_u8(buf, WLAN_EID_MEASURE_REPORT);
+			wpabuf_put_u8(buf, wpabuf_len(nr->civic));
+			/*
+			 * Override measurement token - the first byte of the
+			 * Measurement Report element.
+			 */
+			msmt_token = wpabuf_put(buf, 0);
+			wpabuf_put_buf(buf, nr->civic);
+			*msmt_token = civic;
+		}
+	}
+
+	hapd->openwrt_stats.rrm.neighbor_report_tx++;
+
+	hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+				wpabuf_head(buf), wpabuf_len(buf));
+	wpabuf_free(buf);
+}
+
+
+static void hostapd_handle_nei_report_req(struct hostapd_data *hapd,
+					  const u8 *buf, size_t len)
+{
+	const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
+	const u8 *pos, *ie, *end;
+	struct wpa_ssid_value ssid = {
+		.ssid_len = 0
+	};
+	u8 token;
+	u8 lci = 0, civic = 0; /* Measurement tokens */
+	u16 lci_max_age = 0;
+
+	if (!(hapd->conf->radio_measurements[0] &
+	      WLAN_RRM_CAPS_NEIGHBOR_REPORT))
+		return;
+
+	end = buf + len;
+
+	token = mgmt->u.action.u.rrm.dialog_token;
+	pos = mgmt->u.action.u.rrm.variable;
+	len = end - pos;
+
+	ie = get_ie(pos, len, WLAN_EID_SSID);
+	if (ie && ie[1] && ie[1] <= SSID_MAX_LEN) {
+		ssid.ssid_len = ie[1];
+		os_memcpy(ssid.ssid, ie + 2, ssid.ssid_len);
+	} else {
+		ssid.ssid_len = hapd->conf->ssid.ssid_len;
+		os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len);
+	}
+
+	while ((ie = get_ie(pos, len, WLAN_EID_MEASURE_REQUEST))) {
+		if (ie[1] < 3)
+			break;
+
+		wpa_printf(MSG_DEBUG,
+			   "Neighbor report request, measure type %u",
+			   ie[4]);
+
+		switch (ie[4]) { /* Measurement Type */
+		case MEASURE_TYPE_LCI:
+			lci = ie[2]; /* Measurement Token */
+			lci_max_age = hostapd_parse_location_lci_req_age(ie + 2,
+									 ie[1]);
+			break;
+		case MEASURE_TYPE_LOCATION_CIVIC:
+			civic = ie[2]; /* Measurement token */
+			break;
+		}
+
+		pos = ie + ie[1] + 2;
+		len = end - pos;
+	}
+
+	hostapd_send_nei_report_resp(hapd, mgmt->sa, token, &ssid, lci, civic,
+				     lci_max_age);
+}
+
+
+void hostapd_handle_radio_measurement(struct hostapd_data *hapd,
+				      const u8 *buf, size_t len)
+{
+	const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
+
+	/*
+	 * Check for enough bytes: header + (1B)Category + (1B)Action +
+	 * (1B)Dialog Token.
+	 */
+	if (len < IEEE80211_HDRLEN + 3)
+		return;
+
+	wpa_printf(MSG_DEBUG, "Radio measurement frame, action %u from " MACSTR,
+		   mgmt->u.action.u.rrm.action, MAC2STR(mgmt->sa));
+
+	switch (mgmt->u.action.u.rrm.action) {
+	case WLAN_RRM_LINK_MEASUREMENT_REPORT:
+		hostapd_ubus_handle_link_measurement(hapd, buf, len);
+		break;
+	case WLAN_RRM_RADIO_MEASUREMENT_REPORT:
+		hostapd_handle_radio_msmt_report(hapd, buf, len);
+		break;
+	case WLAN_RRM_NEIGHBOR_REPORT_REQUEST:
+		hostapd_handle_nei_report_req(hapd, buf, len);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "RRM action %u is not supported",
+			   mgmt->u.action.u.rrm.action);
+		break;
+	}
+}
+
+
+int hostapd_send_lci_req(struct hostapd_data *hapd, const u8 *addr)
+{
+	struct wpabuf *buf;
+	struct sta_info *sta = ap_get_sta(hapd, addr);
+	int ret;
+
+	if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) {
+		wpa_printf(MSG_INFO,
+			   "Request LCI: Destination address is not connected");
+		return -1;
+	}
+
+	if (!(sta->rrm_enabled_capa[1] & WLAN_RRM_CAPS_LCI_MEASUREMENT)) {
+		wpa_printf(MSG_INFO,
+			   "Request LCI: Station does not support LCI in RRM");
+		return -1;
+	}
+
+	if (hapd->lci_req_active) {
+		wpa_printf(MSG_DEBUG,
+			   "Request LCI: LCI request is already in process, overriding");
+		hapd->lci_req_active = 0;
+		eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd,
+				     NULL);
+	}
+
+	/* Measurement request (5) + Measurement element with LCI (10) */
+	buf = wpabuf_alloc(5 + 10);
+	if (!buf)
+		return -1;
+
+	hapd->lci_req_token++;
+	/* For wraparounds - the token must be nonzero */
+	if (!hapd->lci_req_token)
+		hapd->lci_req_token++;
+
+	wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
+	wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST);
+	wpabuf_put_u8(buf, hapd->lci_req_token);
+	wpabuf_put_le16(buf, 0); /* Number of repetitions */
+
+	wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
+	wpabuf_put_u8(buf, 3 + 1 + 4);
+
+	wpabuf_put_u8(buf, 1); /* Measurement Token */
+	/*
+	 * Parallel and Enable bits are 0, Duration, Request, and Report are
+	 * reserved.
+	 */
+	wpabuf_put_u8(buf, 0);
+	wpabuf_put_u8(buf, MEASURE_TYPE_LCI);
+
+	wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE);
+
+	wpabuf_put_u8(buf, LCI_REQ_SUBELEM_MAX_AGE);
+	wpabuf_put_u8(buf, 2);
+	wpabuf_put_le16(buf, 0xffff);
+
+	ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+				      wpabuf_head(buf), wpabuf_len(buf));
+	wpabuf_free(buf);
+	if (ret)
+		return ret;
+
+	hapd->lci_req_active = 1;
+
+	eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0,
+			       hostapd_lci_rep_timeout_handler, hapd, NULL);
+
+	return 0;
+}
+
+
+int hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr,
+			   u16 random_interval, u8 min_ap,
+			   const u8 *responders, unsigned int n_responders)
+{
+	struct wpabuf *buf;
+	struct sta_info *sta;
+	u8 *len;
+	unsigned int i;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "Request range: dest addr " MACSTR
+		   " rand interval %u min AP %u n_responders %u", MAC2STR(addr),
+		   random_interval, min_ap, n_responders);
+
+	if (min_ap == 0 || min_ap > n_responders) {
+		wpa_printf(MSG_INFO, "Request range: Wrong min AP count");
+		return -1;
+	}
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) {
+		wpa_printf(MSG_INFO,
+			   "Request range: Destination address is not connected");
+		return -1;
+	}
+
+	if (!(sta->rrm_enabled_capa[4] & WLAN_RRM_CAPS_FTM_RANGE_REPORT)) {
+		wpa_printf(MSG_ERROR,
+			   "Request range: Destination station does not support FTM range report in RRM");
+		return -1;
+	}
+
+	if (hapd->range_req_active) {
+		wpa_printf(MSG_DEBUG,
+			   "Request range: Range request is already in process; overriding");
+		hapd->range_req_active = 0;
+		eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd,
+				     NULL);
+	}
+
+	/* Action + measurement type + token + reps + EID + len = 7 */
+	buf = wpabuf_alloc(7 + 255);
+	if (!buf)
+		return -1;
+
+	hapd->range_req_token++;
+	if (!hapd->range_req_token) /* For wraparounds */
+		hapd->range_req_token++;
+
+	/* IEEE P802.11-REVmc/D5.0, 9.6.7.2 */
+	wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
+	wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST);
+	wpabuf_put_u8(buf, hapd->range_req_token); /* Dialog Token */
+	wpabuf_put_le16(buf, 0); /* Number of Repetitions */
+
+	/* IEEE P802.11-REVmc/D5.0, 9.4.2.21 */
+	wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
+	len = wpabuf_put(buf, 1); /* Length will be set later */
+
+	wpabuf_put_u8(buf, 1); /* Measurement Token */
+	/*
+	 * Parallel and Enable bits are 0; Duration, Request, and Report are
+	 * reserved.
+	 */
+	wpabuf_put_u8(buf, 0); /* Measurement Request Mode */
+	wpabuf_put_u8(buf, MEASURE_TYPE_FTM_RANGE); /* Measurement Type */
+
+	/* IEEE P802.11-REVmc/D5.0, 9.4.2.21.19 */
+	wpabuf_put_le16(buf, random_interval); /* Randomization Interval */
+	wpabuf_put_u8(buf, min_ap); /* Minimum AP Count */
+
+	/* FTM Range Subelements */
+
+	/*
+	 * Taking the neighbor report part of the range request from neighbor
+	 * database instead of requesting the separate bits of data from the
+	 * user.
+	 */
+	for (i = 0; i < n_responders; i++) {
+		struct hostapd_neighbor_entry *nr;
+
+		nr = hostapd_neighbor_get(hapd, responders + ETH_ALEN * i,
+					  NULL);
+		if (!nr) {
+			wpa_printf(MSG_INFO, "Missing neighbor report for "
+				   MACSTR, MAC2STR(responders + ETH_ALEN * i));
+			wpabuf_free(buf);
+			return -1;
+		}
+
+		if (wpabuf_tailroom(buf) < 2 + wpabuf_len(nr->nr)) {
+			wpa_printf(MSG_ERROR, "Too long range request");
+			wpabuf_free(buf);
+			return -1;
+		}
+
+		wpabuf_put_u8(buf, WLAN_EID_NEIGHBOR_REPORT);
+		wpabuf_put_u8(buf, wpabuf_len(nr->nr));
+		wpabuf_put_buf(buf, nr->nr);
+	}
+
+	/* Action + measurement type + token + reps + EID + len = 7 */
+	*len = wpabuf_len(buf) - 7;
+
+	ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+				      wpabuf_head(buf), wpabuf_len(buf));
+	wpabuf_free(buf);
+	if (ret)
+		return ret;
+
+	hapd->range_req_active = 1;
+
+	eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0,
+			       hostapd_range_rep_timeout_handler, hapd, NULL);
+
+	return 0;
+}
+
+
+void hostapd_clean_rrm(struct hostapd_data *hapd)
+{
+	hostapd_free_neighbor_db(hapd);
+	eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd, NULL);
+	hapd->lci_req_active = 0;
+	eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL);
+	hapd->range_req_active = 0;
+}
+
+
+int hostapd_send_beacon_req(struct hostapd_data *hapd, const u8 *addr,
+			    u8 req_mode, const struct wpabuf *req)
+{
+	struct wpabuf *buf;
+	struct sta_info *sta = ap_get_sta(hapd, addr);
+	int ret;
+	enum beacon_report_mode mode;
+	const u8 *pos;
+
+	/* Request data:
+	 * Operating Class (1), Channel Number (1), Randomization Interval (2),
+	 * Measurement Duration (2), Measurement Mode (1), BSSID (6),
+	 * Optional Subelements (variable)
+	 */
+	if (wpabuf_len(req) < 13) {
+		wpa_printf(MSG_INFO, "Beacon request: Too short request data");
+		return -1;
+	}
+	pos = wpabuf_head(req);
+	mode = pos[6];
+
+	if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) {
+		wpa_printf(MSG_INFO,
+			   "Beacon request: " MACSTR " is not connected",
+			   MAC2STR(addr));
+		return -1;
+	}
+
+	switch (mode) {
+	case BEACON_REPORT_MODE_PASSIVE:
+		if (!(sta->rrm_enabled_capa[0] &
+		      WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE)) {
+			wpa_printf(MSG_INFO,
+				   "Beacon request: " MACSTR
+				   " does not support passive beacon report",
+				   MAC2STR(addr));
+			return -1;
+		}
+		break;
+	case BEACON_REPORT_MODE_ACTIVE:
+		if (!(sta->rrm_enabled_capa[0] &
+		      WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE)) {
+			wpa_printf(MSG_INFO,
+				   "Beacon request: " MACSTR
+				   " does not support active beacon report",
+				   MAC2STR(addr));
+			return -1;
+		}
+		break;
+	case BEACON_REPORT_MODE_TABLE:
+		if (!(sta->rrm_enabled_capa[0] &
+		      WLAN_RRM_CAPS_BEACON_REPORT_TABLE)) {
+			wpa_printf(MSG_INFO,
+				   "Beacon request: " MACSTR
+				   " does not support table beacon report",
+				   MAC2STR(addr));
+			return -1;
+		}
+		break;
+	default:
+		wpa_printf(MSG_INFO,
+			   "Beacon request: Unknown measurement mode %d", mode);
+		return -1;
+	}
+
+	buf = wpabuf_alloc(5 + 2 + 3 + wpabuf_len(req));
+	if (!buf)
+		return -1;
+
+	hapd->beacon_req_token++;
+	if (!hapd->beacon_req_token)
+		hapd->beacon_req_token++;
+
+	wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
+	wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST);
+	wpabuf_put_u8(buf, hapd->beacon_req_token);
+	wpabuf_put_le16(buf, 0); /* Number of repetitions */
+
+	/* Measurement Request element */
+	wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
+	wpabuf_put_u8(buf, 3 + wpabuf_len(req));
+	wpabuf_put_u8(buf, 1); /* Measurement Token */
+	wpabuf_put_u8(buf, req_mode); /* Measurement Request Mode */
+	wpabuf_put_u8(buf, MEASURE_TYPE_BEACON); /* Measurement Type */
+	wpabuf_put_buf(buf, req);
+
+	ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+				      wpabuf_head(buf), wpabuf_len(buf));
+	wpabuf_free(buf);
+	if (ret < 0)
+		return ret;
+
+	return hapd->beacon_req_token;
+}
+
+
+void hostapd_rrm_beacon_req_tx_status(struct hostapd_data *hapd,
+				      const struct ieee80211_mgmt *mgmt,
+				      size_t len, int ok)
+{
+	if (len < 24 + 3)
+		return;
+	wpa_msg(hapd->msg_ctx, MSG_INFO, BEACON_REQ_TX_STATUS MACSTR
+		" %u ack=%d", MAC2STR(mgmt->da),
+		mgmt->u.action.u.rrm.dialog_token, ok);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/rrm.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/rrm.h
new file mode 100644
index 0000000..02cd522
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/rrm.h
@@ -0,0 +1,33 @@
+/*
+ * hostapd / Radio Measurement (RRM)
+ * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH.
+ * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef RRM_H
+#define RRM_H
+
+/*
+ * Max measure request length is 255, -6 of the body we have 249 for the
+ * neighbor report elements. Each neighbor report element is at least 2 + 13
+ * bytes, so we can't have more than 16 responders in the request.
+ */
+#define RRM_RANGE_REQ_MAX_RESPONDERS 16
+
+void hostapd_handle_radio_measurement(struct hostapd_data *hapd,
+				      const u8 *buf, size_t len);
+int hostapd_send_lci_req(struct hostapd_data *hapd, const u8 *addr);
+int hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr,
+			   u16 random_interval, u8 min_ap,
+			   const u8 *responders, unsigned int n_responders);
+void hostapd_clean_rrm(struct hostapd_data *hapd);
+int hostapd_send_beacon_req(struct hostapd_data *hapd, const u8 *addr,
+			    u8 req_mode, const struct wpabuf *req);
+void hostapd_rrm_beacon_req_tx_status(struct hostapd_data *hapd,
+				      const struct ieee80211_mgmt *mgmt,
+				      size_t len, int ok);
+
+#endif /* RRM_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/sta_info.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/sta_info.c
new file mode 100644
index 0000000..1843c1a
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/sta_info.c
@@ -0,0 +1,1678 @@
+/*
+ * hostapd / Station table
+ * Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
+#include "common/sae.h"
+#include "common/dpp.h"
+#include "radius/radius.h"
+#include "radius/radius_client.h"
+#include "p2p/p2p.h"
+#include "fst/fst.h"
+#include "crypto/crypto.h"
+#include "hostapd.h"
+#include "accounting.h"
+#include "ieee802_1x.h"
+#include "ieee802_11.h"
+#include "ieee802_11_auth.h"
+#include "wpa_auth.h"
+#include "preauth_auth.h"
+#include "ap_config.h"
+#include "beacon.h"
+#include "ap_mlme.h"
+#include "vlan_init.h"
+#include "p2p_hostapd.h"
+#include "ap_drv_ops.h"
+#include "gas_serv.h"
+#include "wnm_ap.h"
+#include "mbo_ap.h"
+#include "ndisc_snoop.h"
+#include "sta_info.h"
+#include "vlan.h"
+#include "wps_hostapd.h"
+
+static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd,
+				       struct sta_info *sta);
+static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx);
+static void ap_handle_session_warning_timer(void *eloop_ctx, void *timeout_ctx);
+static void ap_sta_deauth_cb_timeout(void *eloop_ctx, void *timeout_ctx);
+static void ap_sta_disassoc_cb_timeout(void *eloop_ctx, void *timeout_ctx);
+static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx);
+static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta);
+static void ap_sta_delayed_1x_auth_fail_cb(void *eloop_ctx, void *timeout_ctx);
+
+int ap_for_each_sta(struct hostapd_data *hapd,
+		    int (*cb)(struct hostapd_data *hapd, struct sta_info *sta,
+			      void *ctx),
+		    void *ctx)
+{
+	struct sta_info *sta;
+
+	for (sta = hapd->sta_list; sta; sta = sta->next) {
+		if (cb(hapd, sta, ctx))
+			return 1;
+	}
+
+	return 0;
+}
+
+
+struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta)
+{
+	struct sta_info *s;
+
+	s = hapd->sta_hash[STA_HASH(sta)];
+	while (s != NULL && os_memcmp(s->addr, sta, 6) != 0)
+		s = s->hnext;
+	return s;
+}
+
+
+#ifdef CONFIG_P2P
+struct sta_info * ap_get_sta_p2p(struct hostapd_data *hapd, const u8 *addr)
+{
+	struct sta_info *sta;
+
+	for (sta = hapd->sta_list; sta; sta = sta->next) {
+		const u8 *p2p_dev_addr;
+
+		if (sta->p2p_ie == NULL)
+			continue;
+
+		p2p_dev_addr = p2p_get_go_dev_addr(sta->p2p_ie);
+		if (p2p_dev_addr == NULL)
+			continue;
+
+		if (os_memcmp(p2p_dev_addr, addr, ETH_ALEN) == 0)
+			return sta;
+	}
+
+	return NULL;
+}
+#endif /* CONFIG_P2P */
+
+
+static void ap_sta_list_del(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	struct sta_info *tmp;
+
+	if (hapd->sta_list == sta) {
+		hapd->sta_list = sta->next;
+		return;
+	}
+
+	tmp = hapd->sta_list;
+	while (tmp != NULL && tmp->next != sta)
+		tmp = tmp->next;
+	if (tmp == NULL) {
+		wpa_printf(MSG_DEBUG, "Could not remove STA " MACSTR " from "
+			   "list.", MAC2STR(sta->addr));
+	} else
+		tmp->next = sta->next;
+}
+
+
+void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	sta->hnext = hapd->sta_hash[STA_HASH(sta->addr)];
+	hapd->sta_hash[STA_HASH(sta->addr)] = sta;
+}
+
+
+static void ap_sta_hash_del(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	struct sta_info *s;
+
+	s = hapd->sta_hash[STA_HASH(sta->addr)];
+	if (s == NULL) return;
+	if (os_memcmp(s->addr, sta->addr, 6) == 0) {
+		hapd->sta_hash[STA_HASH(sta->addr)] = s->hnext;
+		return;
+	}
+
+	while (s->hnext != NULL &&
+	       os_memcmp(s->hnext->addr, sta->addr, ETH_ALEN) != 0)
+		s = s->hnext;
+	if (s->hnext != NULL)
+		s->hnext = s->hnext->hnext;
+	else
+		wpa_printf(MSG_DEBUG, "AP: could not remove STA " MACSTR
+			   " from hash table", MAC2STR(sta->addr));
+}
+
+
+void ap_sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	sta_ip6addr_del(hapd, sta);
+}
+
+
+#ifdef CONFIG_PASN
+
+void ap_free_sta_pasn(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	if (sta->pasn) {
+		wpa_printf(MSG_DEBUG, "PASN: Free PASN context: " MACSTR,
+			   MAC2STR(sta->addr));
+
+		if (sta->pasn->ecdh)
+			crypto_ecdh_deinit(sta->pasn->ecdh);
+
+		wpabuf_free(sta->pasn->secret);
+		sta->pasn->secret = NULL;
+
+#ifdef CONFIG_SAE
+		sae_clear_data(&sta->pasn->sae);
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_FILS
+		/* In practice this pointer should be NULL */
+		wpabuf_free(sta->pasn->fils.erp_resp);
+		sta->pasn->fils.erp_resp = NULL;
+#endif /* CONFIG_FILS */
+
+		bin_clear_free(sta->pasn, sizeof(*sta->pasn));
+		sta->pasn = NULL;
+	}
+}
+
+#endif /* CONFIG_PASN */
+
+void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	int set_beacon = 0;
+
+	accounting_sta_stop(hapd, sta);
+
+	/* just in case */
+	ap_sta_set_authorized(hapd, sta, 0);
+	hostapd_set_sta_flags(hapd, sta);
+
+	if ((sta->flags & WLAN_STA_WDS) ||
+	    (sta->flags & WLAN_STA_MULTI_AP &&
+	     !(hapd->conf->multi_ap & FRONTHAUL_BSS) &&
+	     !(sta->flags & WLAN_STA_WPS)))
+		hostapd_set_wds_sta(hapd, NULL, sta->addr, sta->aid, 0);
+
+	if (sta->ipaddr)
+		hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr);
+	ap_sta_ip6addr_del(hapd, sta);
+
+	if (!hapd->iface->driver_ap_teardown &&
+	    !(sta->flags & WLAN_STA_PREAUTH)) {
+		hostapd_drv_sta_remove(hapd, sta->addr);
+		sta->added_unassoc = 0;
+	}
+
+	ap_sta_hash_del(hapd, sta);
+	ap_sta_list_del(hapd, sta);
+
+	if (sta->aid > 0)
+		hapd->sta_aid[(sta->aid - 1) / 32] &=
+			~BIT((sta->aid - 1) % 32);
+
+	hapd->num_sta--;
+	if (sta->nonerp_set) {
+		sta->nonerp_set = 0;
+		hapd->iface->num_sta_non_erp--;
+		if (hapd->iface->num_sta_non_erp == 0)
+			set_beacon++;
+	}
+
+	if (sta->no_short_slot_time_set) {
+		sta->no_short_slot_time_set = 0;
+		hapd->iface->num_sta_no_short_slot_time--;
+		if (hapd->iface->current_mode &&
+		    hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
+		    && hapd->iface->num_sta_no_short_slot_time == 0)
+			set_beacon++;
+	}
+
+	if (sta->no_short_preamble_set) {
+		sta->no_short_preamble_set = 0;
+		hapd->iface->num_sta_no_short_preamble--;
+		if (hapd->iface->current_mode &&
+		    hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
+		    && hapd->iface->num_sta_no_short_preamble == 0)
+			set_beacon++;
+	}
+
+	if (sta->no_ht_gf_set) {
+		sta->no_ht_gf_set = 0;
+		hapd->iface->num_sta_ht_no_gf--;
+	}
+
+	if (sta->no_ht_set) {
+		sta->no_ht_set = 0;
+		hapd->iface->num_sta_no_ht--;
+	}
+
+	if (sta->ht_20mhz_set) {
+		sta->ht_20mhz_set = 0;
+		hapd->iface->num_sta_ht_20mhz--;
+	}
+
+#ifdef CONFIG_TAXONOMY
+	wpabuf_free(sta->probe_ie_taxonomy);
+	sta->probe_ie_taxonomy = NULL;
+	wpabuf_free(sta->assoc_ie_taxonomy);
+	sta->assoc_ie_taxonomy = NULL;
+#endif /* CONFIG_TAXONOMY */
+
+	ht40_intolerant_remove(hapd->iface, sta);
+
+#ifdef CONFIG_P2P
+	if (sta->no_p2p_set) {
+		sta->no_p2p_set = 0;
+		hapd->num_sta_no_p2p--;
+		if (hapd->num_sta_no_p2p == 0)
+			hostapd_p2p_non_p2p_sta_disconnected(hapd);
+	}
+#endif /* CONFIG_P2P */
+
+#ifdef NEED_AP_MLME
+	if (hostapd_ht_operation_update(hapd->iface) > 0)
+		set_beacon++;
+#endif /* NEED_AP_MLME */
+
+#ifdef CONFIG_MESH
+	if (hapd->mesh_sta_free_cb)
+		hapd->mesh_sta_free_cb(hapd, sta);
+#endif /* CONFIG_MESH */
+
+	if (set_beacon)
+		ieee802_11_set_beacons(hapd->iface);
+
+	wpa_printf(MSG_DEBUG, "%s: cancel ap_handle_timer for " MACSTR,
+		   __func__, MAC2STR(sta->addr));
+	eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+	eloop_cancel_timeout(ap_handle_session_timer, hapd, sta);
+	eloop_cancel_timeout(ap_handle_session_warning_timer, hapd, sta);
+	ap_sta_clear_disconnect_timeouts(hapd, sta);
+	sae_clear_retransmit_timer(hapd, sta);
+
+	ieee802_1x_free_station(hapd, sta);
+
+#ifdef CONFIG_IEEE80211BE
+	if (!hapd->conf->mld_ap || !sta->mld_info.mld_sta ||
+	    hapd->mld_link_id == sta->mld_assoc_link_id)
+		wpa_auth_sta_deinit(sta->wpa_sm);
+#else
+	wpa_auth_sta_deinit(sta->wpa_sm);
+#endif /* CONFIG_IEEE80211BE */
+
+	rsn_preauth_free_station(hapd, sta);
+#ifndef CONFIG_NO_RADIUS
+	if (hapd->radius)
+		radius_client_flush_auth(hapd->radius, sta->addr);
+#endif /* CONFIG_NO_RADIUS */
+
+#ifndef CONFIG_NO_VLAN
+	/*
+	 * sta->wpa_sm->group needs to be released before so that
+	 * vlan_remove_dynamic() can check that no stations are left on the
+	 * AP_VLAN netdev.
+	 */
+	if (sta->vlan_id)
+		vlan_remove_dynamic(hapd, sta->vlan_id);
+	if (sta->vlan_id_bound) {
+		/*
+		 * Need to remove the STA entry before potentially removing the
+		 * VLAN.
+		 */
+		if (hapd->iface->driver_ap_teardown &&
+		    !(sta->flags & WLAN_STA_PREAUTH)) {
+			hostapd_drv_sta_remove(hapd, sta->addr);
+			sta->added_unassoc = 0;
+		}
+		vlan_remove_dynamic(hapd, sta->vlan_id_bound);
+	}
+#endif /* CONFIG_NO_VLAN */
+
+	os_free(sta->challenge);
+
+	os_free(sta->sa_query_trans_id);
+	eloop_cancel_timeout(ap_sa_query_timer, hapd, sta);
+
+#ifdef CONFIG_P2P
+	p2p_group_notif_disassoc(hapd->p2p_group, sta->addr);
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_INTERWORKING
+	if (sta->gas_dialog) {
+		int i;
+		for (i = 0; i < GAS_DIALOG_MAX; i++)
+			gas_serv_dialog_clear(&sta->gas_dialog[i]);
+		os_free(sta->gas_dialog);
+	}
+#endif /* CONFIG_INTERWORKING */
+
+	wpabuf_free(sta->wps_ie);
+	wpabuf_free(sta->p2p_ie);
+	wpabuf_free(sta->hs20_ie);
+	wpabuf_free(sta->roaming_consortium);
+#ifdef CONFIG_FST
+	wpabuf_free(sta->mb_ies);
+#endif /* CONFIG_FST */
+
+	os_free(sta->ht_capabilities);
+	os_free(sta->vht_capabilities);
+	os_free(sta->vht_operation);
+	os_free(sta->he_capab);
+	os_free(sta->he_6ghz_capab);
+	os_free(sta->eht_capab);
+	hostapd_free_psk_list(sta->psk);
+	os_free(sta->identity);
+	os_free(sta->radius_cui);
+	os_free(sta->remediation_url);
+	os_free(sta->t_c_url);
+	wpabuf_free(sta->hs20_deauth_req);
+	os_free(sta->hs20_session_info_url);
+
+#ifdef CONFIG_SAE
+	sae_clear_data(sta->sae);
+	os_free(sta->sae);
+#endif /* CONFIG_SAE */
+
+	mbo_ap_sta_free(sta);
+	os_free(sta->supp_op_classes);
+
+#ifdef CONFIG_FILS
+	os_free(sta->fils_pending_assoc_req);
+	wpabuf_free(sta->fils_hlp_resp);
+	wpabuf_free(sta->hlp_dhcp_discover);
+	eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
+#ifdef CONFIG_FILS_SK_PFS
+	crypto_ecdh_deinit(sta->fils_ecdh);
+	wpabuf_clear_free(sta->fils_dh_ss);
+	wpabuf_free(sta->fils_g_sta);
+#endif /* CONFIG_FILS_SK_PFS */
+#endif /* CONFIG_FILS */
+
+#ifdef CONFIG_OWE
+	bin_clear_free(sta->owe_pmk, sta->owe_pmk_len);
+	crypto_ecdh_deinit(sta->owe_ecdh);
+#endif /* CONFIG_OWE */
+
+#ifdef CONFIG_DPP2
+	dpp_pfs_free(sta->dpp_pfs);
+	sta->dpp_pfs = NULL;
+#endif /* CONFIG_DPP2 */
+
+	os_free(sta->ext_capability);
+
+#ifdef CONFIG_WNM_AP
+	eloop_cancel_timeout(ap_sta_reset_steer_flag_timer, hapd, sta);
+#endif /* CONFIG_WNM_AP */
+
+#ifdef CONFIG_PASN
+	ap_free_sta_pasn(hapd, sta);
+#endif /* CONFIG_PASN */
+
+	os_free(sta->ifname_wds);
+
+#ifdef CONFIG_TESTING_OPTIONS
+	os_free(sta->sae_postponed_commit);
+	forced_memzero(sta->last_tk, WPA_TK_MAX_LEN);
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	os_free(sta);
+}
+
+
+void hostapd_free_stas(struct hostapd_data *hapd)
+{
+	struct sta_info *sta, *prev;
+
+	sta = hapd->sta_list;
+
+	while (sta) {
+		prev = sta;
+		if (sta->flags & WLAN_STA_AUTH) {
+			mlme_deauthenticate_indication(
+				hapd, sta, WLAN_REASON_UNSPECIFIED);
+		}
+		sta = sta->next;
+		wpa_printf(MSG_DEBUG, "Removing station " MACSTR,
+			   MAC2STR(prev->addr));
+		ap_free_sta(hapd, prev);
+	}
+}
+
+
+/**
+ * ap_handle_timer - Per STA timer handler
+ * @eloop_ctx: struct hostapd_data *
+ * @timeout_ctx: struct sta_info *
+ *
+ * This function is called to check station activity and to remove inactive
+ * stations.
+ */
+void ap_handle_timer(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct sta_info *sta = timeout_ctx;
+	unsigned long next_time = 0;
+	int reason;
+
+	wpa_printf(MSG_DEBUG, "%s: %s: " MACSTR " flags=0x%x timeout_next=%d",
+		   hapd->conf->iface, __func__, MAC2STR(sta->addr), sta->flags,
+		   sta->timeout_next);
+	if (sta->timeout_next == STA_REMOVE) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO, "deauthenticated due to "
+			       "local deauth request");
+		hostapd_ubus_notify(hapd, "local-deauth", sta->addr);
+		ap_free_sta(hapd, sta);
+		return;
+	}
+
+	if ((sta->flags & WLAN_STA_ASSOC) &&
+	    (sta->timeout_next == STA_NULLFUNC ||
+	     sta->timeout_next == STA_DISASSOC)) {
+		int inactive_sec;
+		/*
+		 * Add random value to timeout so that we don't end up bouncing
+		 * all stations at the same time if we have lots of associated
+		 * stations that are idle (but keep re-associating).
+		 */
+		int fuzz = os_random() % 20;
+		inactive_sec = hostapd_drv_get_inact_sec(hapd, sta->addr);
+		if (inactive_sec == -1) {
+			wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+				"Check inactivity: Could not "
+				"get station info from kernel driver for "
+				MACSTR, MAC2STR(sta->addr));
+			/*
+			 * The driver may not support this functionality.
+			 * Anyway, try again after the next inactivity timeout,
+			 * but do not disconnect the station now.
+			 */
+			next_time = hapd->conf->ap_max_inactivity + fuzz;
+		} else if (inactive_sec == -ENOENT) {
+			wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+				"Station " MACSTR " has lost its driver entry",
+				MAC2STR(sta->addr));
+
+			/* Avoid sending client probe on removed client */
+			sta->timeout_next = STA_DISASSOC;
+			goto skip_poll;
+		} else if (inactive_sec < hapd->conf->ap_max_inactivity) {
+			/* station activity detected; reset timeout state */
+			wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+				"Station " MACSTR " has been active %is ago",
+				MAC2STR(sta->addr), inactive_sec);
+			sta->timeout_next = STA_NULLFUNC;
+			next_time = hapd->conf->ap_max_inactivity + fuzz -
+				inactive_sec;
+		} else {
+			wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+				"Station " MACSTR " has been "
+				"inactive too long: %d sec, max allowed: %d",
+				MAC2STR(sta->addr), inactive_sec,
+				hapd->conf->ap_max_inactivity);
+
+			if (hapd->conf->skip_inactivity_poll)
+				sta->timeout_next = STA_DISASSOC;
+		}
+	}
+
+	if ((sta->flags & WLAN_STA_ASSOC) &&
+	    sta->timeout_next == STA_DISASSOC &&
+	    !(sta->flags & WLAN_STA_PENDING_POLL) &&
+	    !hapd->conf->skip_inactivity_poll) {
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR
+			" has ACKed data poll", MAC2STR(sta->addr));
+		/* data nullfunc frame poll did not produce TX errors; assume
+		 * station ACKed it */
+		sta->timeout_next = STA_NULLFUNC;
+		next_time = hapd->conf->ap_max_inactivity;
+	}
+
+skip_poll:
+	if (next_time) {
+		wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
+			   "for " MACSTR " (%lu seconds)",
+			   __func__, MAC2STR(sta->addr), next_time);
+		eloop_register_timeout(next_time, 0, ap_handle_timer, hapd,
+				       sta);
+		return;
+	}
+
+	if (sta->timeout_next == STA_NULLFUNC &&
+	    (sta->flags & WLAN_STA_ASSOC)) {
+		wpa_printf(MSG_DEBUG, "  Polling STA");
+		sta->flags |= WLAN_STA_PENDING_POLL;
+		hostapd_drv_poll_client(hapd, hapd->own_addr, sta->addr,
+					sta->flags & WLAN_STA_WMM);
+	} else if (sta->timeout_next != STA_REMOVE) {
+		int deauth = sta->timeout_next == STA_DEAUTH;
+
+		if (!deauth && !(sta->flags & WLAN_STA_ASSOC)) {
+			/* Cannot disassociate not-associated STA, so move
+			 * directly to deauthentication. */
+			sta->timeout_next = STA_DEAUTH;
+			deauth = 1;
+		}
+
+		wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
+			"Timeout, sending %s info to STA " MACSTR,
+			deauth ? "deauthentication" : "disassociation",
+			MAC2STR(sta->addr));
+
+		if (deauth) {
+			hostapd_drv_sta_deauth(
+				hapd, sta->addr,
+				WLAN_REASON_PREV_AUTH_NOT_VALID);
+		} else {
+			reason = (sta->timeout_next == STA_DISASSOC) ?
+				WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY :
+				WLAN_REASON_PREV_AUTH_NOT_VALID;
+
+			hostapd_drv_sta_disassoc(hapd, sta->addr, reason);
+		}
+	}
+
+	switch (sta->timeout_next) {
+	case STA_NULLFUNC:
+		sta->timeout_next = STA_DISASSOC;
+		wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
+			   "for " MACSTR " (%d seconds - AP_DISASSOC_DELAY)",
+			   __func__, MAC2STR(sta->addr), AP_DISASSOC_DELAY);
+		eloop_register_timeout(AP_DISASSOC_DELAY, 0, ap_handle_timer,
+				       hapd, sta);
+		break;
+	case STA_DISASSOC:
+	case STA_DISASSOC_FROM_CLI:
+		ap_sta_set_authorized(hapd, sta, 0);
+		sta->flags &= ~WLAN_STA_ASSOC;
+		hostapd_set_sta_flags(hapd, sta);
+		ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
+		if (!sta->acct_terminate_cause)
+			sta->acct_terminate_cause =
+				RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT;
+		accounting_sta_stop(hapd, sta);
+		ieee802_1x_free_station(hapd, sta);
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO, "disassociated due to "
+			       "inactivity");
+		reason = (sta->timeout_next == STA_DISASSOC) ?
+			WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY :
+			WLAN_REASON_PREV_AUTH_NOT_VALID;
+		sta->timeout_next = STA_DEAUTH;
+		wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
+			   "for " MACSTR " (%d seconds - AP_DEAUTH_DELAY)",
+			   __func__, MAC2STR(sta->addr), AP_DEAUTH_DELAY);
+		eloop_register_timeout(AP_DEAUTH_DELAY, 0, ap_handle_timer,
+				       hapd, sta);
+		mlme_disassociate_indication(hapd, sta, reason);
+		break;
+	case STA_DEAUTH:
+	case STA_REMOVE:
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO, "deauthenticated due to "
+			       "inactivity (timer DEAUTH/REMOVE)");
+		if (!sta->acct_terminate_cause)
+			sta->acct_terminate_cause =
+				RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT;
+		mlme_deauthenticate_indication(
+			hapd, sta,
+			WLAN_REASON_PREV_AUTH_NOT_VALID);
+		hostapd_ubus_notify(hapd, "inactive-deauth", sta->addr);
+		ap_free_sta(hapd, sta);
+		break;
+	}
+}
+
+
+static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct sta_info *sta = timeout_ctx;
+
+	wpa_printf(MSG_DEBUG, "%s: Session timer for STA " MACSTR,
+		   hapd->conf->iface, MAC2STR(sta->addr));
+	if (!(sta->flags & (WLAN_STA_AUTH | WLAN_STA_ASSOC |
+			    WLAN_STA_AUTHORIZED))) {
+		if (sta->flags & WLAN_STA_GAS) {
+			wpa_printf(MSG_DEBUG, "GAS: Remove temporary STA "
+				   "entry " MACSTR, MAC2STR(sta->addr));
+			ap_free_sta(hapd, sta);
+		}
+		return;
+	}
+
+	hostapd_drv_sta_deauth(hapd, sta->addr,
+			       WLAN_REASON_PREV_AUTH_NOT_VALID);
+	mlme_deauthenticate_indication(hapd, sta,
+				       WLAN_REASON_PREV_AUTH_NOT_VALID);
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_INFO, "deauthenticated due to "
+		       "session timeout");
+	sta->acct_terminate_cause =
+		RADIUS_ACCT_TERMINATE_CAUSE_SESSION_TIMEOUT;
+	ap_free_sta(hapd, sta);
+}
+
+
+void ap_sta_replenish_timeout(struct hostapd_data *hapd, struct sta_info *sta,
+			      u32 session_timeout)
+{
+	if (eloop_replenish_timeout(session_timeout, 0,
+				    ap_handle_session_timer, hapd, sta) == 1) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG, "setting session timeout "
+			       "to %d seconds", session_timeout);
+	}
+}
+
+
+void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta,
+			    u32 session_timeout)
+{
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_DEBUG, "setting session timeout to %d "
+		       "seconds", session_timeout);
+	eloop_cancel_timeout(ap_handle_session_timer, hapd, sta);
+	eloop_register_timeout(session_timeout, 0, ap_handle_session_timer,
+			       hapd, sta);
+}
+
+
+void ap_sta_no_session_timeout(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	eloop_cancel_timeout(ap_handle_session_timer, hapd, sta);
+}
+
+
+static void ap_handle_session_warning_timer(void *eloop_ctx, void *timeout_ctx)
+{
+#ifdef CONFIG_WNM_AP
+	struct hostapd_data *hapd = eloop_ctx;
+	struct sta_info *sta = timeout_ctx;
+
+	wpa_printf(MSG_DEBUG, "%s: WNM: Session warning time reached for "
+		   MACSTR, hapd->conf->iface, MAC2STR(sta->addr));
+	if (sta->hs20_session_info_url == NULL)
+		return;
+
+	wnm_send_ess_disassoc_imminent(hapd, sta, sta->hs20_session_info_url,
+				       sta->hs20_disassoc_timer);
+#endif /* CONFIG_WNM_AP */
+}
+
+
+void ap_sta_session_warning_timeout(struct hostapd_data *hapd,
+				    struct sta_info *sta, int warning_time)
+{
+	eloop_cancel_timeout(ap_handle_session_warning_timer, hapd, sta);
+	eloop_register_timeout(warning_time, 0, ap_handle_session_warning_timer,
+			       hapd, sta);
+}
+
+
+struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr)
+{
+	struct sta_info *sta;
+	int i;
+
+	sta = ap_get_sta(hapd, addr);
+	if (sta)
+		return sta;
+
+	wpa_printf(MSG_DEBUG, "  New STA");
+	//if (hapd->num_sta >= hapd->conf->max_num_sta) {
+	if (hostapd_check_max_sta(hapd)) {
+		/* FIX: might try to remove some old STAs first? */
+		wpa_printf(MSG_DEBUG, "no more room for new STAs (%d/%d)",
+			   hapd->num_sta, hapd->conf->max_num_sta);
+		return NULL;
+	}
+
+	sta = os_zalloc(sizeof(struct sta_info));
+	if (sta == NULL) {
+		wpa_printf(MSG_ERROR, "malloc failed");
+		return NULL;
+	}
+	sta->acct_interim_interval = hapd->conf->acct_interim_interval;
+	if (accounting_sta_get_id(hapd, sta) < 0) {
+		os_free(sta);
+		return NULL;
+	}
+
+	for (i = 0; i < WLAN_SUPP_RATES_MAX; i++) {
+		if (!hapd->iface->basic_rates)
+			break;
+		if (hapd->iface->basic_rates[i] < 0)
+			break;
+		sta->supported_rates[i] = hapd->iface->basic_rates[i] / 5;
+	}
+	sta->supported_rates_len = i;
+
+	if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) {
+		wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
+			   "for " MACSTR " (%d seconds - ap_max_inactivity)",
+			   __func__, MAC2STR(addr),
+			   hapd->conf->ap_max_inactivity);
+		eloop_register_timeout(hapd->conf->ap_max_inactivity, 0,
+				       ap_handle_timer, hapd, sta);
+	}
+
+	/* initialize STA info data */
+	os_memcpy(sta->addr, addr, ETH_ALEN);
+	sta->next = hapd->sta_list;
+	hapd->sta_list = sta;
+	hapd->num_sta++;
+	ap_sta_hash_add(hapd, sta);
+	ap_sta_remove_in_other_bss(hapd, sta);
+	sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
+	dl_list_init(&sta->ip6addr);
+
+#ifdef CONFIG_TAXONOMY
+	sta_track_claim_taxonomy_info(hapd->iface, addr,
+				      &sta->probe_ie_taxonomy);
+#endif /* CONFIG_TAXONOMY */
+
+	return sta;
+}
+
+
+static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
+
+	if (sta->ipaddr)
+		hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr);
+	ap_sta_ip6addr_del(hapd, sta);
+
+	wpa_printf(MSG_DEBUG, "%s: Removing STA " MACSTR " from kernel driver",
+		   hapd->conf->iface, MAC2STR(sta->addr));
+	if (hostapd_drv_sta_remove(hapd, sta->addr) &&
+	    sta->flags & WLAN_STA_ASSOC) {
+		wpa_printf(MSG_DEBUG, "%s: Could not remove station " MACSTR
+			   " from kernel driver",
+			   hapd->conf->iface, MAC2STR(sta->addr));
+		return -1;
+	}
+	sta->added_unassoc = 0;
+	return 0;
+}
+
+
+static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd,
+				       struct sta_info *sta)
+{
+	struct hostapd_iface *iface = hapd->iface;
+	size_t i;
+
+	for (i = 0; i < iface->num_bss; i++) {
+		struct hostapd_data *bss = iface->bss[i];
+		struct sta_info *sta2;
+		/* bss should always be set during operation, but it may be
+		 * NULL during reconfiguration. Assume the STA is not
+		 * associated to another BSS in that case to avoid NULL pointer
+		 * dereferences. */
+		if (bss == hapd || bss == NULL)
+			continue;
+		sta2 = ap_get_sta(bss, sta->addr);
+		if (!sta2)
+			continue;
+
+		wpa_printf(MSG_DEBUG, "%s: disconnect old STA " MACSTR
+			   " association from another BSS %s",
+			   hapd->conf->iface, MAC2STR(sta2->addr),
+			   bss->conf->iface);
+		ap_sta_disconnect(bss, sta2, sta2->addr,
+				  WLAN_REASON_PREV_AUTH_NOT_VALID);
+	}
+}
+
+
+static void ap_sta_disassoc_cb_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct sta_info *sta = timeout_ctx;
+
+	wpa_printf(MSG_DEBUG, "%s: Disassociation callback for STA " MACSTR,
+		   hapd->conf->iface, MAC2STR(sta->addr));
+	ap_sta_remove(hapd, sta);
+	mlme_disassociate_indication(hapd, sta, sta->disassoc_reason);
+}
+
+
+void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta,
+			 u16 reason)
+{
+	wpa_printf(MSG_DEBUG, "%s: disassociate STA " MACSTR,
+		   hapd->conf->iface, MAC2STR(sta->addr));
+	sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
+	if (hapd->iface->current_mode &&
+	    hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
+		/* Skip deauthentication in DMG/IEEE 802.11ad */
+		sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC |
+				WLAN_STA_ASSOC_REQ_OK);
+		sta->timeout_next = STA_REMOVE;
+	} else {
+		sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
+		sta->timeout_next = STA_DEAUTH;
+	}
+	ap_sta_set_authorized(hapd, sta, 0);
+	hostapd_set_sta_flags(hapd, sta);
+	wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
+		   "for " MACSTR " (%d seconds - "
+		   "AP_MAX_INACTIVITY_AFTER_DISASSOC)",
+		   __func__, MAC2STR(sta->addr),
+		   AP_MAX_INACTIVITY_AFTER_DISASSOC);
+	eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+	eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DISASSOC, 0,
+			       ap_handle_timer, hapd, sta);
+	accounting_sta_stop(hapd, sta);
+	ieee802_1x_free_station(hapd, sta);
+#ifdef CONFIG_IEEE80211BE
+	if (!hapd->conf->mld_ap ||
+	    hapd->mld_link_id == sta->mld_assoc_link_id)
+		wpa_auth_sta_deinit(sta->wpa_sm);
+#else
+	wpa_auth_sta_deinit(sta->wpa_sm);
+#endif /* CONFIG_IEEE80211BE */
+
+	sta->wpa_sm = NULL;
+
+	sta->disassoc_reason = reason;
+	sta->flags |= WLAN_STA_PENDING_DISASSOC_CB;
+	eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta);
+	eloop_register_timeout(hapd->iface->drv_flags &
+			       WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0,
+			       ap_sta_disassoc_cb_timeout, hapd, sta);
+}
+
+
+static void ap_sta_deauth_cb_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct sta_info *sta = timeout_ctx;
+
+	wpa_printf(MSG_DEBUG, "%s: Deauthentication callback for STA " MACSTR,
+		   hapd->conf->iface, MAC2STR(sta->addr));
+	ap_sta_remove(hapd, sta);
+	mlme_deauthenticate_indication(hapd, sta, sta->deauth_reason);
+}
+
+
+void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta,
+			   u16 reason)
+{
+	if (hapd->iface->current_mode &&
+	    hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
+		/* Deauthentication is not used in DMG/IEEE 802.11ad;
+		 * disassociate the STA instead. */
+		ap_sta_disassociate(hapd, sta, reason);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR,
+		   hapd->conf->iface, MAC2STR(sta->addr));
+	sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
+	sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
+	ap_sta_set_authorized(hapd, sta, 0);
+	hostapd_set_sta_flags(hapd, sta);
+	sta->timeout_next = STA_REMOVE;
+	wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
+		   "for " MACSTR " (%d seconds - "
+		   "AP_MAX_INACTIVITY_AFTER_DEAUTH)",
+		   __func__, MAC2STR(sta->addr),
+		   AP_MAX_INACTIVITY_AFTER_DEAUTH);
+	eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+	eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0,
+			       ap_handle_timer, hapd, sta);
+	accounting_sta_stop(hapd, sta);
+	ieee802_1x_free_station(hapd, sta);
+
+	sta->deauth_reason = reason;
+	sta->flags |= WLAN_STA_PENDING_DEAUTH_CB;
+	eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
+	eloop_register_timeout(hapd->iface->drv_flags &
+			       WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0,
+			       ap_sta_deauth_cb_timeout, hapd, sta);
+}
+
+
+#ifdef CONFIG_WPS
+int ap_sta_wps_cancel(struct hostapd_data *hapd,
+		      struct sta_info *sta, void *ctx)
+{
+	if (sta && (sta->flags & WLAN_STA_WPS)) {
+		ap_sta_deauthenticate(hapd, sta,
+				      WLAN_REASON_PREV_AUTH_NOT_VALID);
+		wpa_printf(MSG_DEBUG, "WPS: %s: Deauth sta=" MACSTR,
+			   __func__, MAC2STR(sta->addr));
+		return 1;
+	}
+
+	return 0;
+}
+#endif /* CONFIG_WPS */
+
+
+static int ap_sta_get_free_vlan_id(struct hostapd_data *hapd)
+{
+	struct hostapd_vlan *vlan;
+	int vlan_id = MAX_VLAN_ID + 2;
+
+retry:
+	for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) {
+		if (vlan->vlan_id == vlan_id) {
+			vlan_id++;
+			goto retry;
+		}
+	}
+	return vlan_id;
+}
+
+
+int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta,
+		    struct vlan_description *vlan_desc)
+{
+	struct hostapd_vlan *vlan = NULL, *wildcard_vlan = NULL;
+	int old_vlan_id, vlan_id = 0, ret = 0;
+
+	/* Check if there is something to do */
+	if (hapd->conf->ssid.per_sta_vif && !sta->vlan_id) {
+		/* This sta is lacking its own vif */
+	} else if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED &&
+		   !hapd->conf->ssid.per_sta_vif && sta->vlan_id) {
+		/* sta->vlan_id needs to be reset */
+	} else if (!vlan_compare(vlan_desc, sta->vlan_desc)) {
+		return 0; /* nothing to change */
+	}
+
+	/* Now the real VLAN changed or the STA just needs its own vif */
+	if (hapd->conf->ssid.per_sta_vif) {
+		/* Assign a new vif, always */
+		/* find a free vlan_id sufficiently big */
+		vlan_id = ap_sta_get_free_vlan_id(hapd);
+		/* Get wildcard VLAN */
+		for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) {
+			if (vlan->vlan_id == VLAN_ID_WILDCARD)
+				break;
+		}
+		if (!vlan) {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "per_sta_vif missing wildcard");
+			vlan_id = 0;
+			ret = -1;
+			goto done;
+		}
+	} else if (vlan_desc && vlan_desc->notempty) {
+		for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) {
+			if (!vlan_compare(&vlan->vlan_desc, vlan_desc))
+				break;
+			if (vlan->vlan_id == VLAN_ID_WILDCARD)
+				wildcard_vlan = vlan;
+		}
+		if (vlan) {
+			vlan_id = vlan->vlan_id;
+		} else if (wildcard_vlan) {
+			vlan = wildcard_vlan;
+			vlan_id = vlan_desc->untagged;
+			if (vlan_desc->tagged[0]) {
+				/* Tagged VLAN configuration */
+				vlan_id = ap_sta_get_free_vlan_id(hapd);
+			}
+		} else {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "missing vlan and wildcard for vlan=%d%s",
+				       vlan_desc->untagged,
+				       vlan_desc->tagged[0] ? "+" : "");
+			vlan_id = 0;
+			ret = -1;
+			goto done;
+		}
+	}
+
+	if (vlan && vlan->vlan_id == VLAN_ID_WILDCARD) {
+		vlan = vlan_add_dynamic(hapd, vlan, vlan_id, vlan_desc);
+		if (vlan == NULL) {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "could not add dynamic VLAN interface for vlan=%d%s",
+				       vlan_desc ? vlan_desc->untagged : -1,
+				       (vlan_desc && vlan_desc->tagged[0]) ?
+				       "+" : "");
+			vlan_id = 0;
+			ret = -1;
+			goto done;
+		}
+
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "added new dynamic VLAN interface '%s'",
+			       vlan->ifname);
+	} else if (vlan && vlan->dynamic_vlan > 0) {
+		vlan->dynamic_vlan++;
+		hostapd_logger(hapd, sta->addr,
+			       HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "updated existing dynamic VLAN interface '%s'",
+			       vlan->ifname);
+	}
+done:
+	old_vlan_id = sta->vlan_id;
+	sta->vlan_id = vlan_id;
+	sta->vlan_desc = vlan ? &vlan->vlan_desc : NULL;
+
+	if (vlan_id != old_vlan_id && old_vlan_id)
+		vlan_remove_dynamic(hapd, old_vlan_id);
+
+	return ret;
+}
+
+
+int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta)
+{
+#ifndef CONFIG_NO_VLAN
+	const char *iface;
+	struct hostapd_vlan *vlan = NULL;
+	int ret;
+	int old_vlanid = sta->vlan_id_bound;
+	int mld_link_id = -1;
+
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->conf->mld_ap)
+		mld_link_id = hapd->mld_link_id;
+#endif /* CONFIG_IEEE80211BE */
+
+	if ((sta->flags & WLAN_STA_WDS) && sta->vlan_id == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "Do not override WDS VLAN assignment for STA "
+			   MACSTR, MAC2STR(sta->addr));
+		return 0;
+	}
+
+	iface = hapd->conf->iface;
+	if (hapd->conf->ssid.vlan[0])
+		iface = hapd->conf->ssid.vlan;
+
+	if (sta->vlan_id > 0) {
+		for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) {
+			if (vlan->vlan_id == sta->vlan_id)
+				break;
+		}
+		if (vlan)
+			iface = vlan->ifname;
+	}
+
+	/*
+	 * Do not increment ref counters if the VLAN ID remains same, but do
+	 * not skip hostapd_drv_set_sta_vlan() as hostapd_drv_sta_remove() might
+	 * have been called before.
+	 */
+	if (sta->vlan_id == old_vlanid)
+		goto skip_counting;
+
+	if (sta->vlan_id > 0 && !vlan &&
+	    !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_VLAN_OFFLOAD)) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG, "could not find VLAN for "
+			       "binding station to (vlan_id=%d)",
+			       sta->vlan_id);
+		ret = -1;
+		goto done;
+	} else if (vlan && vlan->dynamic_vlan > 0) {
+		vlan->dynamic_vlan++;
+		hostapd_logger(hapd, sta->addr,
+			       HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "updated existing dynamic VLAN interface '%s'",
+			       iface);
+	}
+
+	/* ref counters have been increased, so mark the station */
+	sta->vlan_id_bound = sta->vlan_id;
+
+skip_counting:
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_DEBUG, "binding station to interface "
+		       "'%s'", iface);
+
+	if (wpa_auth_sta_set_vlan(sta->wpa_sm, sta->vlan_id) < 0)
+		wpa_printf(MSG_INFO, "Failed to update VLAN-ID for WPA");
+
+	ret = hostapd_drv_set_sta_vlan(iface, hapd, sta->addr, sta->vlan_id,
+				       mld_link_id);
+	if (ret < 0) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG, "could not bind the STA "
+			       "entry to vlan_id=%d", sta->vlan_id);
+	}
+
+	/* During 1x reauth, if the vlan id changes, then remove the old id. */
+	if (old_vlanid > 0 && old_vlanid != sta->vlan_id)
+		vlan_remove_dynamic(hapd, old_vlanid);
+done:
+
+	return ret;
+#else /* CONFIG_NO_VLAN */
+	return 0;
+#endif /* CONFIG_NO_VLAN */
+}
+
+
+int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	u32 tu;
+	struct os_reltime now, passed;
+	os_get_reltime(&now);
+	os_reltime_sub(&now, &sta->sa_query_start, &passed);
+	tu = (passed.sec * 1000000 + passed.usec) / 1024;
+	if (hapd->conf->assoc_sa_query_max_timeout < tu) {
+		hostapd_logger(hapd, sta->addr,
+			       HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "association SA Query timed out");
+		sta->sa_query_timed_out = 1;
+		os_free(sta->sa_query_trans_id);
+		sta->sa_query_trans_id = NULL;
+		sta->sa_query_count = 0;
+		eloop_cancel_timeout(ap_sa_query_timer, hapd, sta);
+		return 1;
+	}
+
+	return 0;
+}
+
+
+static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct sta_info *sta = timeout_ctx;
+	unsigned int timeout, sec, usec;
+	u8 *trans_id, *nbuf;
+
+	wpa_printf(MSG_DEBUG, "%s: SA Query timer for STA " MACSTR
+		   " (count=%d)",
+		   hapd->conf->iface, MAC2STR(sta->addr), sta->sa_query_count);
+
+	if (sta->sa_query_count > 0 &&
+	    ap_check_sa_query_timeout(hapd, sta))
+		return;
+	if (sta->sa_query_count >= 1000)
+		return;
+
+	nbuf = os_realloc_array(sta->sa_query_trans_id,
+				sta->sa_query_count + 1,
+				WLAN_SA_QUERY_TR_ID_LEN);
+	if (nbuf == NULL)
+		return;
+	if (sta->sa_query_count == 0) {
+		/* Starting a new SA Query procedure */
+		os_get_reltime(&sta->sa_query_start);
+	}
+	trans_id = nbuf + sta->sa_query_count * WLAN_SA_QUERY_TR_ID_LEN;
+	sta->sa_query_trans_id = nbuf;
+	sta->sa_query_count++;
+
+	if (os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN) < 0) {
+		/*
+		 * We don't really care which ID is used here, so simply
+		 * hardcode this if the mostly theoretical os_get_random()
+		 * failure happens.
+		 */
+		trans_id[0] = 0x12;
+		trans_id[1] = 0x34;
+	}
+
+	timeout = hapd->conf->assoc_sa_query_retry_timeout;
+	sec = ((timeout / 1000) * 1024) / 1000;
+	usec = (timeout % 1000) * 1024;
+	eloop_register_timeout(sec, usec, ap_sa_query_timer, hapd, sta);
+
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "association SA Query attempt %d", sta->sa_query_count);
+
+	ieee802_11_send_sa_query_req(hapd, sta->addr, trans_id);
+}
+
+
+void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	ap_sa_query_timer(hapd, sta);
+}
+
+
+void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	eloop_cancel_timeout(ap_sa_query_timer, hapd, sta);
+	os_free(sta->sa_query_trans_id);
+	sta->sa_query_trans_id = NULL;
+	sta->sa_query_count = 0;
+}
+
+
+const char * ap_sta_wpa_get_keyid(struct hostapd_data *hapd,
+				  struct sta_info *sta)
+{
+	struct hostapd_wpa_psk *psk;
+	struct hostapd_ssid *ssid;
+	const u8 *pmk;
+	int pmk_len;
+
+	ssid = &hapd->conf->ssid;
+
+	pmk = wpa_auth_get_pmk(sta->wpa_sm, &pmk_len);
+	if (!pmk || pmk_len != PMK_LEN)
+		return NULL;
+
+	for (psk = ssid->wpa_psk; psk; psk = psk->next)
+		if (os_memcmp(pmk, psk->psk, PMK_LEN) == 0)
+			break;
+	if (!psk || !psk->keyid[0])
+		return NULL;
+
+	return psk->keyid;
+}
+
+
+const u8 * ap_sta_wpa_get_dpp_pkhash(struct hostapd_data *hapd,
+				     struct sta_info *sta)
+{
+	return wpa_auth_get_dpp_pkhash(sta->wpa_sm);
+}
+
+
+void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta,
+			   int authorized)
+{
+	const u8 *dev_addr = NULL;
+	char buf[100];
+#ifdef CONFIG_P2P
+	u8 addr[ETH_ALEN];
+	u8 ip_addr_buf[4];
+#endif /* CONFIG_P2P */
+	struct hostapd_ubus_request req;
+
+	if (!!authorized == !!(sta->flags & WLAN_STA_AUTHORIZED))
+		return;
+
+	os_memset(&req, 0, sizeof(req));
+	if (authorized) {
+		int mld_assoc_link_id = -1;
+
+#ifdef CONFIG_IEEE80211BE
+		if (hapd->conf->mld_ap && sta->mld_info.mld_sta) {
+			if (sta->mld_assoc_link_id == hapd->mld_link_id)
+				mld_assoc_link_id = sta->mld_assoc_link_id;
+			else
+				mld_assoc_link_id = -2;
+		}
+#endif /* CONFIG_IEEE80211BE */
+		if (mld_assoc_link_id != -2)
+			hostapd_prune_associations(hapd, sta->addr,
+						   mld_assoc_link_id);
+		sta->flags |= WLAN_STA_AUTHORIZED;
+		req.type = HOSTAPD_UBUS_CONNECTED_REQ;
+		req.addr = sta->addr;
+	} else {
+		sta->flags &= ~WLAN_STA_AUTHORIZED;
+		req.type = HOSTAPD_UBUS_DISCONNECTED_REQ;
+		req.addr = sta->addr;
+	}
+
+#ifdef CONFIG_P2P
+	if (hapd->p2p_group == NULL) {
+		if (sta->p2p_ie != NULL &&
+		    p2p_parse_dev_addr_in_p2p_ie(sta->p2p_ie, addr) == 0)
+			dev_addr = addr;
+	} else
+		dev_addr = p2p_group_get_dev_addr(hapd->p2p_group, sta->addr);
+
+	if (dev_addr)
+		os_snprintf(buf, sizeof(buf), MACSTR " p2p_dev_addr=" MACSTR,
+			    MAC2STR(sta->addr), MAC2STR(dev_addr));
+	else
+#endif /* CONFIG_P2P */
+		os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(sta->addr));
+
+	if (hapd->sta_authorized_cb)
+		hapd->sta_authorized_cb(hapd->sta_authorized_cb_ctx,
+					sta->addr, authorized, dev_addr);
+
+	if (authorized) {
+		static const char * const auth_algs[] = {
+			[WLAN_AUTH_OPEN] = "open",
+			[WLAN_AUTH_SHARED_KEY] = "shared",
+			[WLAN_AUTH_FT] = "ft",
+			[WLAN_AUTH_SAE] = "sae",
+			[WLAN_AUTH_FILS_SK] = "fils-sk",
+			[WLAN_AUTH_FILS_SK_PFS] = "fils-sk-pfs",
+			[WLAN_AUTH_FILS_PK] = "fils-pk",
+			[WLAN_AUTH_PASN] = "pasn",
+		};
+		const char *auth_alg = NULL;
+		const u8 *dpp_pkhash;
+		const char *keyid;
+		char dpp_pkhash_buf[100];
+		char keyid_buf[100];
+		char ip_addr[100];
+		char alg_buf[100];
+
+		dpp_pkhash_buf[0] = '\0';
+		keyid_buf[0] = '\0';
+		ip_addr[0] = '\0';
+		alg_buf[0] = '\0';
+#ifdef CONFIG_P2P
+		if (wpa_auth_get_ip_addr(sta->wpa_sm, ip_addr_buf) == 0) {
+			os_snprintf(ip_addr, sizeof(ip_addr),
+				    " ip_addr=%u.%u.%u.%u",
+				    ip_addr_buf[0], ip_addr_buf[1],
+				    ip_addr_buf[2], ip_addr_buf[3]);
+		}
+#endif /* CONFIG_P2P */
+
+		if (sta->auth_alg < ARRAY_SIZE(auth_algs))
+			auth_alg = auth_algs[sta->auth_alg];
+
+		if (auth_alg)
+			os_snprintf(alg_buf, sizeof(alg_buf),
+				" auth_alg=%s", auth_alg);
+
+		keyid = ap_sta_wpa_get_keyid(hapd, sta);
+		if (keyid) {
+			os_snprintf(keyid_buf, sizeof(keyid_buf),
+				    " keyid=%s", keyid);
+		}
+
+		dpp_pkhash = ap_sta_wpa_get_dpp_pkhash(hapd, sta);
+		if (dpp_pkhash) {
+			const char *prefix = " dpp_pkhash=";
+			size_t plen = os_strlen(prefix);
+
+			os_strlcpy(dpp_pkhash_buf, prefix,
+				   sizeof(dpp_pkhash_buf));
+			wpa_snprintf_hex(&dpp_pkhash_buf[plen],
+					 sizeof(dpp_pkhash_buf) - plen,
+					 dpp_pkhash, SHA256_MAC_LEN);
+		}
+
+		hostapd_ubus_notify_authorized(hapd, sta, auth_alg);
+		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s%s%s%s",
+			buf, ip_addr, keyid_buf, dpp_pkhash_buf, alg_buf);
+
+		if (hapd->msg_ctx_parent &&
+		    hapd->msg_ctx_parent != hapd->msg_ctx)
+			wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO,
+					  AP_STA_CONNECTED "%s%s%s%s%s",
+					  buf, ip_addr, keyid_buf,
+					  dpp_pkhash_buf, alg_buf);
+	} else {
+		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED "%s", buf);
+		hostapd_ubus_notify(hapd, "disassoc", sta->addr);
+
+		if (hapd->msg_ctx_parent &&
+		    hapd->msg_ctx_parent != hapd->msg_ctx)
+			wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO,
+					  AP_STA_DISCONNECTED "%s", buf);
+	}
+
+#ifdef CONFIG_FST
+	if (hapd->iface->fst) {
+		if (authorized)
+			fst_notify_peer_connected(hapd->iface->fst, sta->addr);
+		else
+			fst_notify_peer_disconnected(hapd->iface->fst,
+						     sta->addr);
+	}
+#endif /* CONFIG_FST */
+
+	hostapd_ubus_handle_event(hapd, &req);
+}
+
+
+void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
+		       const u8 *addr, u16 reason)
+{
+	if (sta)
+		wpa_printf(MSG_DEBUG, "%s: %s STA " MACSTR " reason=%u",
+			   hapd->conf->iface, __func__, MAC2STR(sta->addr),
+			   reason);
+	else if (addr)
+		wpa_printf(MSG_DEBUG, "%s: %s addr " MACSTR " reason=%u",
+			   hapd->conf->iface, __func__, MAC2STR(addr),
+			   reason);
+
+	if (sta == NULL && addr)
+		sta = ap_get_sta(hapd, addr);
+
+	if (addr)
+		hostapd_drv_sta_deauth(hapd, addr, reason);
+
+	if (sta == NULL)
+		return;
+	ap_sta_set_authorized(hapd, sta, 0);
+	sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
+	hostapd_set_sta_flags(hapd, sta);
+	wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH);
+	ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
+	wpa_printf(MSG_DEBUG, "%s: %s: reschedule ap_handle_timer timeout "
+		   "for " MACSTR " (%d seconds - "
+		   "AP_MAX_INACTIVITY_AFTER_DEAUTH)",
+		   hapd->conf->iface, __func__, MAC2STR(sta->addr),
+		   AP_MAX_INACTIVITY_AFTER_DEAUTH);
+	eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+	eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0,
+			       ap_handle_timer, hapd, sta);
+	sta->timeout_next = STA_REMOVE;
+
+	if (hapd->iface->current_mode &&
+	    hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
+		/* Deauthentication is not used in DMG/IEEE 802.11ad;
+		 * disassociate the STA instead. */
+		sta->disassoc_reason = reason;
+		sta->flags |= WLAN_STA_PENDING_DISASSOC_CB;
+		eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta);
+		eloop_register_timeout(hapd->iface->drv_flags &
+				       WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ?
+				       2 : 0, 0, ap_sta_disassoc_cb_timeout,
+				       hapd, sta);
+		return;
+	}
+
+	sta->deauth_reason = reason;
+	sta->flags |= WLAN_STA_PENDING_DEAUTH_CB;
+	eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
+	eloop_register_timeout(hapd->iface->drv_flags &
+			       WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0,
+			       ap_sta_deauth_cb_timeout, hapd, sta);
+}
+
+
+void ap_sta_deauth_cb(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	if (!(sta->flags & WLAN_STA_PENDING_DEAUTH_CB)) {
+		wpa_printf(MSG_DEBUG, "Ignore deauth cb for test frame");
+		return;
+	}
+	sta->flags &= ~WLAN_STA_PENDING_DEAUTH_CB;
+	eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
+	ap_sta_deauth_cb_timeout(hapd, sta);
+}
+
+
+void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	if (!(sta->flags & WLAN_STA_PENDING_DISASSOC_CB)) {
+		wpa_printf(MSG_DEBUG, "Ignore disassoc cb for test frame");
+		return;
+	}
+	sta->flags &= ~WLAN_STA_PENDING_DISASSOC_CB;
+	eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta);
+	ap_sta_disassoc_cb_timeout(hapd, sta);
+}
+
+
+void ap_sta_clear_disconnect_timeouts(struct hostapd_data *hapd,
+				      struct sta_info *sta)
+{
+	if (eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta) > 0)
+		wpa_printf(MSG_DEBUG,
+			   "%s: Removed ap_sta_deauth_cb_timeout timeout for "
+			   MACSTR,
+			   hapd->conf->iface, MAC2STR(sta->addr));
+	if (eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta) > 0)
+		wpa_printf(MSG_DEBUG,
+			   "%s: Removed ap_sta_disassoc_cb_timeout timeout for "
+			   MACSTR,
+			   hapd->conf->iface, MAC2STR(sta->addr));
+	if (eloop_cancel_timeout(ap_sta_delayed_1x_auth_fail_cb, hapd, sta) > 0)
+	{
+		wpa_printf(MSG_DEBUG,
+			   "%s: Removed ap_sta_delayed_1x_auth_fail_cb timeout for "
+			   MACSTR,
+			   hapd->conf->iface, MAC2STR(sta->addr));
+		if (sta->flags & WLAN_STA_WPS)
+			hostapd_wps_eap_completed(hapd);
+	}
+}
+
+
+int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen)
+{
+	int res;
+
+	buf[0] = '\0';
+	res = os_snprintf(buf, buflen,
+			  "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+			  (flags & WLAN_STA_AUTH ? "[AUTH]" : ""),
+			  (flags & WLAN_STA_ASSOC ? "[ASSOC]" : ""),
+			  (flags & WLAN_STA_AUTHORIZED ? "[AUTHORIZED]" : ""),
+			  (flags & WLAN_STA_PENDING_POLL ? "[PENDING_POLL" :
+			   ""),
+			  (flags & WLAN_STA_SHORT_PREAMBLE ?
+			   "[SHORT_PREAMBLE]" : ""),
+			  (flags & WLAN_STA_PREAUTH ? "[PREAUTH]" : ""),
+			  (flags & WLAN_STA_WMM ? "[WMM]" : ""),
+			  (flags & WLAN_STA_MFP ? "[MFP]" : ""),
+			  (flags & WLAN_STA_WPS ? "[WPS]" : ""),
+			  (flags & WLAN_STA_MAYBE_WPS ? "[MAYBE_WPS]" : ""),
+			  (flags & WLAN_STA_WDS ? "[WDS]" : ""),
+			  (flags & WLAN_STA_NONERP ? "[NonERP]" : ""),
+			  (flags & WLAN_STA_WPS2 ? "[WPS2]" : ""),
+			  (flags & WLAN_STA_GAS ? "[GAS]" : ""),
+			  (flags & WLAN_STA_HT ? "[HT]" : ""),
+			  (flags & WLAN_STA_VHT ? "[VHT]" : ""),
+			  (flags & WLAN_STA_HE ? "[HE]" : ""),
+			  (flags & WLAN_STA_EHT ? "[EHT]" : ""),
+			  (flags & WLAN_STA_6GHZ ? "[6GHZ]" : ""),
+			  (flags & WLAN_STA_VENDOR_VHT ? "[VENDOR_VHT]" : ""),
+			  (flags & WLAN_STA_WNM_SLEEP_MODE ?
+			   "[WNM_SLEEP_MODE]" : ""));
+	if (os_snprintf_error(buflen, res))
+		res = -1;
+
+	return res;
+}
+
+
+static void ap_sta_delayed_1x_auth_fail_cb(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct sta_info *sta = timeout_ctx;
+	u16 reason;
+
+	wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
+		"IEEE 802.1X: Scheduled disconnection of " MACSTR
+		" after EAP-Failure", MAC2STR(sta->addr));
+
+	reason = sta->disconnect_reason_code;
+	if (!reason)
+		reason = WLAN_REASON_IEEE_802_1X_AUTH_FAILED;
+	ap_sta_disconnect(hapd, sta, sta->addr, reason);
+	if (sta->flags & WLAN_STA_WPS)
+		hostapd_wps_eap_completed(hapd);
+}
+
+
+void ap_sta_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd,
+					    struct sta_info *sta,
+					    unsigned timeout)
+{
+	wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
+		"IEEE 802.1X: Force disconnection of " MACSTR
+		" after EAP-Failure in %u ms", MAC2STR(sta->addr), timeout);
+
+	/*
+	 * Add a small sleep to increase likelihood of previously requested
+	 * EAP-Failure TX getting out before this should the driver reorder
+	 * operations.
+	 */
+	eloop_cancel_timeout(ap_sta_delayed_1x_auth_fail_cb, hapd, sta);
+	eloop_register_timeout(0, timeout * 1000,
+			       ap_sta_delayed_1x_auth_fail_cb, hapd, sta);
+}
+
+
+int ap_sta_pending_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd,
+						   struct sta_info *sta)
+{
+	return eloop_is_timeout_registered(ap_sta_delayed_1x_auth_fail_cb,
+					   hapd, sta);
+}
+
+
+int ap_sta_re_add(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	const u8 *mld_link_addr = NULL;
+	bool mld_link_sta = false;
+
+	/*
+	 * If a station that is already associated to the AP, is trying to
+	 * authenticate again, remove the STA entry, in order to make sure the
+	 * STA PS state gets cleared and configuration gets updated. To handle
+	 * this, station's added_unassoc flag is cleared once the station has
+	 * completed association.
+	 */
+
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->conf->mld_ap && sta->mld_info.mld_sta) {
+		u8 mld_link_id = hapd->mld_link_id;
+
+		mld_link_sta = sta->mld_assoc_link_id != mld_link_id;
+		mld_link_addr = sta->mld_info.links[mld_link_id].peer_addr;
+	}
+#endif /* CONFIG_IEEE80211BE */
+
+	ap_sta_set_authorized(hapd, sta, 0);
+	hostapd_drv_sta_remove(hapd, sta->addr);
+	sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_AUTH | WLAN_STA_AUTHORIZED);
+
+	if (hostapd_sta_add(hapd, sta->addr, 0, 0,
+			    sta->supported_rates,
+			    sta->supported_rates_len,
+			    0, NULL, NULL, NULL, 0, NULL, 0, NULL,
+			    sta->flags, 0, 0, 0, 0,
+			    mld_link_addr, mld_link_sta)) {
+		hostapd_logger(hapd, sta->addr,
+			       HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_NOTICE,
+			       "Could not add STA to kernel driver");
+		return -1;
+	}
+
+	sta->added_unassoc = 1;
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/sta_info.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/sta_info.h
new file mode 100644
index 0000000..e6b6881
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/sta_info.h
@@ -0,0 +1,419 @@
+/*
+ * hostapd / Station table
+ * Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef STA_INFO_H
+#define STA_INFO_H
+
+#include "common/defs.h"
+#include "list.h"
+#include "vlan.h"
+#include "common/wpa_common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/sae.h"
+#include "crypto/sha384.h"
+#include "pasn/pasn_common.h"
+
+/* STA flags */
+#define WLAN_STA_AUTH BIT(0)
+#define WLAN_STA_ASSOC BIT(1)
+#define WLAN_STA_AUTHORIZED BIT(5)
+#define WLAN_STA_PENDING_POLL BIT(6) /* pending activity poll not ACKed */
+#define WLAN_STA_SHORT_PREAMBLE BIT(7)
+#define WLAN_STA_PREAUTH BIT(8)
+#define WLAN_STA_WMM BIT(9)
+#define WLAN_STA_MFP BIT(10)
+#define WLAN_STA_HT BIT(11)
+#define WLAN_STA_WPS BIT(12)
+#define WLAN_STA_MAYBE_WPS BIT(13)
+#define WLAN_STA_WDS BIT(14)
+#define WLAN_STA_ASSOC_REQ_OK BIT(15)
+#define WLAN_STA_WPS2 BIT(16)
+#define WLAN_STA_GAS BIT(17)
+#define WLAN_STA_VHT BIT(18)
+#define WLAN_STA_WNM_SLEEP_MODE BIT(19)
+#define WLAN_STA_VHT_OPMODE_ENABLED BIT(20)
+#define WLAN_STA_VENDOR_VHT BIT(21)
+#define WLAN_STA_PENDING_FILS_ERP BIT(22)
+#define WLAN_STA_MULTI_AP BIT(23)
+#define WLAN_STA_HE BIT(24)
+#define WLAN_STA_6GHZ BIT(25)
+#define WLAN_STA_PENDING_PASN_FILS_ERP BIT(26)
+#define WLAN_STA_EHT BIT(27)
+#define WLAN_STA_PENDING_DISASSOC_CB BIT(29)
+#define WLAN_STA_PENDING_DEAUTH_CB BIT(30)
+#define WLAN_STA_NONERP BIT(31)
+
+/* Maximum number of supported rates (from both Supported Rates and Extended
+ * Supported Rates IEs). */
+#define WLAN_SUPP_RATES_MAX 32
+
+struct hostapd_data;
+
+struct mbo_non_pref_chan_info {
+	struct mbo_non_pref_chan_info *next;
+	u8 op_class;
+	u8 pref;
+	u8 reason_code;
+	u8 num_channels;
+	u8 channels[];
+};
+
+struct pending_eapol_rx {
+	struct wpabuf *buf;
+	struct os_reltime rx_time;
+	enum frame_encryption encrypted;
+};
+
+#define EHT_ML_MAX_STA_PROF_LEN 1024
+struct mld_info {
+	bool mld_sta;
+
+	struct ml_common_info {
+		u8 mld_addr[ETH_ALEN];
+		u16 medium_sync_delay;
+		u16 eml_capa;
+		u16 mld_capa;
+	} common_info;
+
+	struct mld_link_info {
+		u8 valid;
+		u8 local_addr[ETH_ALEN];
+		u8 peer_addr[ETH_ALEN];
+
+		size_t nstr_bitmap_len;
+		u8 nstr_bitmap[2];
+
+		u16 capability;
+
+		u16 status;
+		size_t resp_sta_profile_len;
+		u8 resp_sta_profile[EHT_ML_MAX_STA_PROF_LEN];
+
+		const u8 *rsne, *rsnxe;
+	} links[MAX_NUM_MLD_LINKS];
+};
+
+struct sta_info {
+	struct sta_info *next; /* next entry in sta list */
+	struct sta_info *hnext; /* next entry in hash table list */
+	u8 addr[6];
+	be32 ipaddr;
+	struct dl_list ip6addr; /* list head for struct ip6addr */
+	u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */
+	u16 disconnect_reason_code; /* RADIUS server override */
+	u32 flags; /* Bitfield of WLAN_STA_* */
+	u16 capability;
+	u16 listen_interval; /* or beacon_int for APs */
+	u8 supported_rates[WLAN_SUPP_RATES_MAX];
+	int supported_rates_len;
+	u8 qosinfo; /* Valid when WLAN_STA_WMM is set */
+
+#ifdef CONFIG_MESH
+	enum mesh_plink_state plink_state;
+	u16 peer_lid;
+	u16 my_lid;
+	u16 peer_aid;
+	u16 mpm_close_reason;
+	int mpm_retries;
+	u8 my_nonce[WPA_NONCE_LEN];
+	u8 peer_nonce[WPA_NONCE_LEN];
+	u8 aek[32];	/* SHA256 digest length */
+	u8 mtk[WPA_TK_MAX_LEN];
+	size_t mtk_len;
+	u8 mgtk_rsc[6];
+	u8 mgtk_key_id;
+	u8 mgtk[WPA_TK_MAX_LEN];
+	size_t mgtk_len;
+	u8 igtk_rsc[6];
+	u8 igtk[WPA_TK_MAX_LEN];
+	size_t igtk_len;
+	u16 igtk_key_id;
+	u8 sae_auth_retry;
+#endif /* CONFIG_MESH */
+
+	unsigned int nonerp_set:1;
+	unsigned int no_short_slot_time_set:1;
+	unsigned int no_short_preamble_set:1;
+	unsigned int no_ht_gf_set:1;
+	unsigned int no_ht_set:1;
+	unsigned int ht40_intolerant_set:1;
+	unsigned int ht_20mhz_set:1;
+	unsigned int no_p2p_set:1;
+	unsigned int qos_map_enabled:1;
+	unsigned int remediation:1;
+	unsigned int hs20_deauth_requested:1;
+	unsigned int hs20_deauth_on_ack:1;
+	unsigned int session_timeout_set:1;
+	unsigned int radius_das_match:1;
+	unsigned int ecsa_supported:1;
+	unsigned int added_unassoc:1;
+	unsigned int pending_wds_enable:1;
+	unsigned int power_capab:1;
+	unsigned int agreed_to_steer:1;
+	unsigned int hs20_t_c_filtering:1;
+	unsigned int ft_over_ds:1;
+	unsigned int external_dh_updated:1;
+	unsigned int post_csa_sa_query:1;
+
+	u16 auth_alg;
+
+	enum {
+		STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH, STA_REMOVE,
+		STA_DISASSOC_FROM_CLI
+	} timeout_next;
+
+	u16 deauth_reason;
+	u16 disassoc_reason;
+
+	/* IEEE 802.1X related data */
+	struct eapol_state_machine *eapol_sm;
+
+	struct pending_eapol_rx *pending_eapol_rx;
+
+	u64 acct_session_id;
+	struct os_reltime acct_session_start;
+	int acct_session_started;
+	int acct_terminate_cause; /* Acct-Terminate-Cause */
+	int acct_interim_interval; /* Acct-Interim-Interval */
+	unsigned int acct_interim_errors;
+
+	/* For extending 32-bit driver counters to 64-bit counters */
+	u32 last_rx_bytes_hi;
+	u32 last_rx_bytes_lo;
+	u32 last_tx_bytes_hi;
+	u32 last_tx_bytes_lo;
+
+	u8 *challenge; /* IEEE 802.11 Shared Key Authentication Challenge */
+
+	struct wpa_state_machine *wpa_sm;
+	struct rsn_preauth_interface *preauth_iface;
+
+	int vlan_id; /* 0: none, >0: VID */
+	struct vlan_description *vlan_desc;
+	int vlan_id_bound; /* updated by ap_sta_bind_vlan() */
+	 /* PSKs from RADIUS authentication server */
+	struct hostapd_sta_wpa_psk_short *psk;
+
+	char *identity; /* User-Name from RADIUS */
+	char *radius_cui; /* Chargeable-User-Identity from RADIUS */
+
+	struct ieee80211_ht_capabilities *ht_capabilities;
+	struct ieee80211_vht_capabilities *vht_capabilities;
+	struct ieee80211_vht_operation *vht_operation;
+	u8 vht_opmode;
+	struct ieee80211_he_capabilities *he_capab;
+	size_t he_capab_len;
+	struct ieee80211_he_6ghz_band_cap *he_6ghz_capab;
+	struct ieee80211_eht_capabilities *eht_capab;
+	size_t eht_capab_len;
+
+	int sa_query_count; /* number of pending SA Query requests;
+			     * 0 = no SA Query in progress */
+	int sa_query_timed_out;
+	u8 *sa_query_trans_id; /* buffer of WLAN_SA_QUERY_TR_ID_LEN *
+				* sa_query_count octets of pending SA Query
+				* transaction identifiers */
+	struct os_reltime sa_query_start;
+
+#if defined(CONFIG_INTERWORKING) || defined(CONFIG_DPP)
+#define GAS_DIALOG_MAX 8 /* Max concurrent dialog number */
+	struct gas_dialog_info *gas_dialog;
+	u8 gas_dialog_next;
+#endif /* CONFIG_INTERWORKING || CONFIG_DPP */
+
+	struct wpabuf *wps_ie; /* WPS IE from (Re)Association Request */
+	struct wpabuf *p2p_ie; /* P2P IE from (Re)Association Request */
+	struct wpabuf *hs20_ie; /* HS 2.0 IE from (Re)Association Request */
+	/* Hotspot 2.0 Roaming Consortium from (Re)Association Request */
+	struct wpabuf *roaming_consortium;
+	u8 remediation_method;
+	char *remediation_url; /* HS 2.0 Subscription Remediation Server URL */
+	char *t_c_url; /* HS 2.0 Terms and Conditions Server URL */
+	struct wpabuf *hs20_deauth_req;
+	char *hs20_session_info_url;
+	int hs20_disassoc_timer;
+#ifdef CONFIG_FST
+	struct wpabuf *mb_ies; /* MB IEs from (Re)Association Request */
+#endif /* CONFIG_FST */
+
+	struct os_reltime connected_time;
+
+#ifdef CONFIG_SAE
+	struct sae_data *sae;
+	unsigned int mesh_sae_pmksa_caching:1;
+#endif /* CONFIG_SAE */
+
+	/* valid only if session_timeout_set == 1 */
+	struct os_reltime session_timeout;
+
+	/* Last Authentication/(Re)Association Request/Action frame sequence
+	 * control */
+	u16 last_seq_ctrl;
+	/* Last Authentication/(Re)Association Request/Action frame subtype */
+	u8 last_subtype;
+
+#ifdef CONFIG_MBO
+	u8 cell_capa; /* 0 = unknown (not an MBO STA); otherwise,
+		       * enum mbo_cellular_capa values */
+	struct mbo_non_pref_chan_info *non_pref_chan;
+	int auth_rssi; /* Last Authentication frame RSSI */
+#endif /* CONFIG_MBO */
+
+	u8 *supp_op_classes; /* Supported Operating Classes element, if
+			      * received, starting from the Length field */
+
+	u8 rrm_enabled_capa[5];
+
+	s8 min_tx_power;
+	s8 max_tx_power;
+
+#ifdef CONFIG_TAXONOMY
+	struct wpabuf *probe_ie_taxonomy;
+	struct wpabuf *assoc_ie_taxonomy;
+#endif /* CONFIG_TAXONOMY */
+
+#ifdef CONFIG_FILS
+	u8 fils_snonce[FILS_NONCE_LEN];
+	u8 fils_session[FILS_SESSION_LEN];
+	u8 fils_erp_pmkid[PMKID_LEN];
+	u8 *fils_pending_assoc_req;
+	size_t fils_pending_assoc_req_len;
+	unsigned int fils_pending_assoc_is_reassoc:1;
+	unsigned int fils_dhcp_rapid_commit_proxy:1;
+	unsigned int fils_erp_pmkid_set:1;
+	unsigned int fils_drv_assoc_finish:1;
+	struct wpabuf *fils_hlp_resp;
+	struct wpabuf *hlp_dhcp_discover;
+	void (*fils_pending_cb)(struct hostapd_data *hapd, struct sta_info *sta,
+				u16 resp, struct wpabuf *data, int pub);
+#ifdef CONFIG_FILS_SK_PFS
+	struct crypto_ecdh *fils_ecdh;
+#endif /* CONFIG_FILS_SK_PFS */
+	struct wpabuf *fils_dh_ss;
+	struct wpabuf *fils_g_sta;
+#endif /* CONFIG_FILS */
+
+#ifdef CONFIG_OWE
+	u8 *owe_pmk;
+	size_t owe_pmk_len;
+	struct crypto_ecdh *owe_ecdh;
+	u16 owe_group;
+#endif /* CONFIG_OWE */
+
+	u8 *ext_capability;
+	char *ifname_wds; /* WDS ifname, if in use */
+
+#ifdef CONFIG_DPP2
+	struct dpp_pfs *dpp_pfs;
+#endif /* CONFIG_DPP2 */
+
+#ifdef CONFIG_TESTING_OPTIONS
+	enum wpa_alg last_tk_alg;
+	int last_tk_key_idx;
+	u8 last_tk[WPA_TK_MAX_LEN];
+	size_t last_tk_len;
+	u8 *sae_postponed_commit;
+	size_t sae_postponed_commit_len;
+#endif /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_AIRTIME_POLICY
+	unsigned int airtime_weight;
+	unsigned int dyn_airtime_weight;
+	struct os_reltime backlogged_until;
+#endif /* CONFIG_AIRTIME_POLICY */
+
+#ifdef CONFIG_PASN
+	struct pasn_data *pasn;
+#endif /* CONFIG_PASN */
+
+#ifdef CONFIG_IEEE80211BE
+	struct mld_info mld_info;
+	u8 mld_assoc_link_id;
+#endif /* CONFIG_IEEE80211BE */
+};
+
+
+/* Default value for maximum station inactivity. After AP_MAX_INACTIVITY has
+ * passed since last received frame from the station, a nullfunc data frame is
+ * sent to the station. If this frame is not acknowledged and no other frames
+ * have been received, the station will be disassociated after
+ * AP_DISASSOC_DELAY seconds. Similarly, the station will be deauthenticated
+ * after AP_DEAUTH_DELAY seconds has passed after disassociation. */
+#define AP_MAX_INACTIVITY (5 * 60)
+#define AP_DISASSOC_DELAY (3)
+#define AP_DEAUTH_DELAY (1)
+/* Number of seconds to keep STA entry with Authenticated flag after it has
+ * been disassociated. */
+#define AP_MAX_INACTIVITY_AFTER_DISASSOC (1 * 30)
+/* Number of seconds to keep STA entry after it has been deauthenticated. */
+#define AP_MAX_INACTIVITY_AFTER_DEAUTH (1 * 5)
+
+
+int ap_for_each_sta(struct hostapd_data *hapd,
+		    int (*cb)(struct hostapd_data *hapd, struct sta_info *sta,
+			      void *ctx),
+		    void *ctx);
+struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta);
+struct sta_info * ap_get_sta_p2p(struct hostapd_data *hapd, const u8 *addr);
+void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta);
+void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta);
+void ap_sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta);
+void hostapd_free_stas(struct hostapd_data *hapd);
+void ap_handle_timer(void *eloop_ctx, void *timeout_ctx);
+void ap_sta_replenish_timeout(struct hostapd_data *hapd, struct sta_info *sta,
+			      u32 session_timeout);
+void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta,
+			    u32 session_timeout);
+void ap_sta_no_session_timeout(struct hostapd_data *hapd,
+			       struct sta_info *sta);
+void ap_sta_session_warning_timeout(struct hostapd_data *hapd,
+				    struct sta_info *sta, int warning_time);
+struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr);
+void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta,
+			 u16 reason);
+void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta,
+			   u16 reason);
+#ifdef CONFIG_WPS
+int ap_sta_wps_cancel(struct hostapd_data *hapd,
+		      struct sta_info *sta, void *ctx);
+#endif /* CONFIG_WPS */
+int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta);
+int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta,
+		    struct vlan_description *vlan_desc);
+void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta);
+void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta);
+int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta);
+const char * ap_sta_wpa_get_keyid(struct hostapd_data *hapd,
+				  struct sta_info *sta);
+const u8 * ap_sta_wpa_get_dpp_pkhash(struct hostapd_data *hapd,
+				     struct sta_info *sta);
+void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
+		       const u8 *addr, u16 reason);
+
+void ap_sta_set_authorized(struct hostapd_data *hapd,
+			   struct sta_info *sta, int authorized);
+static inline int ap_sta_is_authorized(struct sta_info *sta)
+{
+	return sta->flags & WLAN_STA_AUTHORIZED;
+}
+
+void ap_sta_deauth_cb(struct hostapd_data *hapd, struct sta_info *sta);
+void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta);
+void ap_sta_clear_disconnect_timeouts(struct hostapd_data *hapd,
+				      struct sta_info *sta);
+
+int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen);
+void ap_sta_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd,
+					    struct sta_info *sta,
+					    unsigned timeout);
+int ap_sta_pending_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd,
+						   struct sta_info *sta);
+int ap_sta_re_add(struct hostapd_data *hapd, struct sta_info *sta);
+
+void ap_free_sta_pasn(struct hostapd_data *hapd, struct sta_info *sta);
+
+#endif /* STA_INFO_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/taxonomy.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/taxonomy.c
new file mode 100644
index 0000000..ae157a7
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/taxonomy.c
@@ -0,0 +1,292 @@
+/*
+ * hostapd / Client taxonomy
+ * Copyright (c) 2015 Google, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * Parse a series of IEs, as in Probe Request or (Re)Association Request frames,
+ * and render them to a descriptive string. The tag number of standard options
+ * is written to the string, while the vendor ID and subtag are written for
+ * vendor options.
+ *
+ * Example strings:
+ * 0,1,50,45,221(00904c,51)
+ * 0,1,33,36,48,45,221(00904c,51),221(0050f2,2)
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/wpa_ctrl.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "taxonomy.h"
+
+
+/* Copy a string with no funny schtuff allowed; only alphanumerics. */
+static void no_mischief_strncpy(char *dst, const char *src, size_t n)
+{
+	size_t i;
+
+	for (i = 0; i < n; i++) {
+		unsigned char s = src[i];
+		int is_lower = s >= 'a' && s <= 'z';
+		int is_upper = s >= 'A' && s <= 'Z';
+		int is_digit = s >= '0' && s <= '9';
+
+		if (is_lower || is_upper || is_digit) {
+			/* TODO: if any manufacturer uses Unicode within the
+			 * WPS header, it will get mangled here. */
+			dst[i] = s;
+		} else {
+			/* Note that even spaces will be transformed to
+			 * underscores, so 'Nexus 7' will turn into 'Nexus_7'.
+			 * This is deliberate, to make the string easier to
+			 * parse. */
+			dst[i] = '_';
+		}
+	}
+}
+
+
+static int get_wps_name(char *name, size_t name_len,
+			const u8 *data, size_t data_len)
+{
+	/* Inside the WPS IE are a series of attributes, using two byte IDs
+	 * and two byte lengths. We're looking for the model name, if
+	 * present. */
+	while (data_len >= 4) {
+		u16 id, elen;
+
+		id = WPA_GET_BE16(data);
+		elen = WPA_GET_BE16(data + 2);
+		data += 4;
+		data_len -= 4;
+
+		if (elen > data_len)
+			return 0;
+
+		if (id == 0x1023) {
+			/* Model name, like 'Nexus 7' */
+			size_t n = (elen < name_len) ? elen : name_len;
+			no_mischief_strncpy(name, (const char *) data, n);
+			return n;
+		}
+
+		data += elen;
+		data_len -= elen;
+	}
+
+	return 0;
+}
+
+
+static void ie_to_string(char *fstr, size_t fstr_len, const struct wpabuf *ies)
+{
+	char *fpos = fstr;
+	char *fend = fstr + fstr_len;
+	char htcap[7 + 4 + 1]; /* ",htcap:" + %04hx + trailing NUL */
+	char htagg[7 + 2 + 1]; /* ",htagg:" + %02hx + trailing NUL */
+	char htmcs[7 + 8 + 1]; /* ",htmcs:" + %08x + trailing NUL */
+	char vhtcap[8 + 8 + 1]; /* ",vhtcap:" + %08x + trailing NUL */
+	char vhtrxmcs[10 + 8 + 1]; /* ",vhtrxmcs:" + %08x + trailing NUL */
+	char vhttxmcs[10 + 8 + 1]; /* ",vhttxmcs:" + %08x + trailing NUL */
+#define MAX_EXTCAP	254
+	char extcap[8 + 2 * MAX_EXTCAP + 1]; /* ",extcap:" + hex + trailing NUL
+					      */
+	char txpow[7 + 4 + 1]; /* ",txpow:" + %04hx + trailing NUL */
+#define WPS_NAME_LEN		32
+	char wps[WPS_NAME_LEN + 5 + 1]; /* room to prepend ",wps:" + trailing
+					 * NUL */
+	int num = 0;
+	const u8 *ie;
+	size_t ie_len;
+	int ret;
+
+	os_memset(htcap, 0, sizeof(htcap));
+	os_memset(htagg, 0, sizeof(htagg));
+	os_memset(htmcs, 0, sizeof(htmcs));
+	os_memset(vhtcap, 0, sizeof(vhtcap));
+	os_memset(vhtrxmcs, 0, sizeof(vhtrxmcs));
+	os_memset(vhttxmcs, 0, sizeof(vhttxmcs));
+	os_memset(extcap, 0, sizeof(extcap));
+	os_memset(txpow, 0, sizeof(txpow));
+	os_memset(wps, 0, sizeof(wps));
+	*fpos = '\0';
+
+	if (!ies)
+		return;
+	ie = wpabuf_head(ies);
+	ie_len = wpabuf_len(ies);
+
+	while (ie_len >= 2) {
+		u8 id, elen;
+		char *sep = (num++ == 0) ? "" : ",";
+
+		id = *ie++;
+		elen = *ie++;
+		ie_len -= 2;
+
+		if (elen > ie_len)
+			break;
+
+		if (id == WLAN_EID_VENDOR_SPECIFIC && elen >= 4) {
+			/* Vendor specific */
+			if (WPA_GET_BE32(ie) == WPS_IE_VENDOR_TYPE) {
+				/* WPS */
+				char model_name[WPS_NAME_LEN + 1];
+				const u8 *data = &ie[4];
+				size_t data_len = elen - 4;
+
+				os_memset(model_name, 0, sizeof(model_name));
+				if (get_wps_name(model_name, WPS_NAME_LEN, data,
+						 data_len)) {
+					os_snprintf(wps, sizeof(wps),
+						    ",wps:%s", model_name);
+				}
+			}
+
+			ret = os_snprintf(fpos, fend - fpos,
+					  "%s%d(%02x%02x%02x,%d)",
+					  sep, id, ie[0], ie[1], ie[2], ie[3]);
+		} else {
+			if (id == WLAN_EID_HT_CAP && elen >= 2) {
+				/* HT Capabilities (802.11n) */
+				os_snprintf(htcap, sizeof(htcap),
+					    ",htcap:%04hx",
+					    WPA_GET_LE16(ie));
+			}
+			if (id == WLAN_EID_HT_CAP && elen >= 3) {
+				/* HT Capabilities (802.11n), A-MPDU information
+				 */
+				os_snprintf(htagg, sizeof(htagg),
+					    ",htagg:%02hx", (u16) ie[2]);
+			}
+			if (id == WLAN_EID_HT_CAP && elen >= 7) {
+				/* HT Capabilities (802.11n), MCS information */
+				os_snprintf(htmcs, sizeof(htmcs),
+					    ",htmcs:%08hx",
+					    (u16) WPA_GET_LE32(ie + 3));
+			}
+			if (id == WLAN_EID_VHT_CAP && elen >= 4) {
+				/* VHT Capabilities (802.11ac) */
+				os_snprintf(vhtcap, sizeof(vhtcap),
+					    ",vhtcap:%08x",
+					    WPA_GET_LE32(ie));
+			}
+			if (id == WLAN_EID_VHT_CAP && elen >= 8) {
+				/* VHT Capabilities (802.11ac), RX MCS
+				 * information */
+				os_snprintf(vhtrxmcs, sizeof(vhtrxmcs),
+					    ",vhtrxmcs:%08x",
+					    WPA_GET_LE32(ie + 4));
+			}
+			if (id == WLAN_EID_VHT_CAP && elen >= 12) {
+				/* VHT Capabilities (802.11ac), TX MCS
+				 * information */
+				os_snprintf(vhttxmcs, sizeof(vhttxmcs),
+					    ",vhttxmcs:%08x",
+					    WPA_GET_LE32(ie + 8));
+			}
+			if (id == WLAN_EID_EXT_CAPAB) {
+				/* Extended Capabilities */
+				int i;
+				int len = (elen < MAX_EXTCAP) ? elen :
+					MAX_EXTCAP;
+				char *p = extcap;
+
+				p += os_snprintf(extcap, sizeof(extcap),
+						 ",extcap:");
+				for (i = 0; i < len; i++) {
+					int lim;
+
+					lim = sizeof(extcap) -
+						os_strlen(extcap);
+					if (lim <= 0)
+						break;
+					p += os_snprintf(p, lim, "%02x",
+							 *(ie + i));
+				}
+			}
+			if (id == WLAN_EID_PWR_CAPABILITY && elen == 2) {
+				/* TX Power */
+				os_snprintf(txpow, sizeof(txpow),
+					    ",txpow:%04hx",
+					    WPA_GET_LE16(ie));
+			}
+
+			ret = os_snprintf(fpos, fend - fpos, "%s%d", sep, id);
+		}
+		if (os_snprintf_error(fend - fpos, ret))
+			goto fail;
+		fpos += ret;
+
+		ie += elen;
+		ie_len -= elen;
+	}
+
+	ret = os_snprintf(fpos, fend - fpos, "%s%s%s%s%s%s%s%s%s",
+			  htcap, htagg, htmcs, vhtcap, vhtrxmcs, vhttxmcs,
+			  txpow, extcap, wps);
+	if (os_snprintf_error(fend - fpos, ret)) {
+	fail:
+		fstr[0] = '\0';
+	}
+}
+
+
+int retrieve_sta_taxonomy(const struct hostapd_data *hapd,
+			  struct sta_info *sta, char *buf, size_t buflen)
+{
+	int ret;
+	char *pos, *end;
+
+	if (!sta->probe_ie_taxonomy || !sta->assoc_ie_taxonomy)
+		return 0;
+
+	ret = os_snprintf(buf, buflen, "wifi4|probe:");
+	if (os_snprintf_error(buflen, ret))
+		return 0;
+	pos = buf + ret;
+	end = buf + buflen;
+
+	ie_to_string(pos, end - pos, sta->probe_ie_taxonomy);
+	pos = os_strchr(pos, '\0');
+	if (pos >= end)
+		return 0;
+	ret = os_snprintf(pos, end - pos, "|assoc:");
+	if (os_snprintf_error(end - pos, ret))
+		return 0;
+	pos += ret;
+	ie_to_string(pos, end - pos, sta->assoc_ie_taxonomy);
+	pos = os_strchr(pos, '\0');
+	return pos - buf;
+}
+
+
+void taxonomy_sta_info_probe_req(const struct hostapd_data *hapd,
+				 struct sta_info *sta,
+				 const u8 *ie, size_t ie_len)
+{
+	wpabuf_free(sta->probe_ie_taxonomy);
+	sta->probe_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len);
+}
+
+
+void taxonomy_hostapd_sta_info_probe_req(const struct hostapd_data *hapd,
+					 struct hostapd_sta_info *info,
+					 const u8 *ie, size_t ie_len)
+{
+	wpabuf_free(info->probe_ie_taxonomy);
+	info->probe_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len);
+}
+
+
+void taxonomy_sta_info_assoc_req(const struct hostapd_data *hapd,
+				 struct sta_info *sta,
+				 const u8 *ie, size_t ie_len)
+{
+	wpabuf_free(sta->assoc_ie_taxonomy);
+	sta->assoc_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/taxonomy.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/taxonomy.h
new file mode 100644
index 0000000..80f245c
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/taxonomy.h
@@ -0,0 +1,24 @@
+/*
+ * hostapd / Station client taxonomy
+ * Copyright (c) 2015 Google, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef TAXONOMY_H
+#define TAXONOMY_H
+
+void taxonomy_sta_info_probe_req(const struct hostapd_data *hapd,
+				 struct sta_info *sta,
+				 const u8 *ie, size_t ie_len);
+void taxonomy_hostapd_sta_info_probe_req(const struct hostapd_data *hapd,
+					 struct hostapd_sta_info *sta,
+					 const u8 *ie, size_t ie_len);
+void taxonomy_sta_info_assoc_req(const struct hostapd_data *hapd,
+				 struct sta_info *sta,
+				 const u8 *ie, size_t ie_len);
+int retrieve_sta_taxonomy(const struct hostapd_data *hapd,
+			  struct sta_info *sta, char *buf, size_t buflen);
+
+#endif /* TAXONOMY_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/tkip_countermeasures.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/tkip_countermeasures.c
new file mode 100644
index 0000000..557570c
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/tkip_countermeasures.c
@@ -0,0 +1,110 @@
+/*
+ * hostapd / TKIP countermeasures
+ * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "radius/radius.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ap_mlme.h"
+#include "wpa_auth.h"
+#include "ap_drv_ops.h"
+#include "tkip_countermeasures.h"
+
+
+static void ieee80211_tkip_countermeasures_stop(void *eloop_ctx,
+						void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	hapd->tkip_countermeasures = 0;
+	hostapd_drv_set_countermeasures(hapd, 0);
+	hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_INFO, "TKIP countermeasures ended");
+}
+
+
+static void ieee80211_tkip_countermeasures_start(struct hostapd_data *hapd)
+{
+	struct sta_info *sta;
+
+	hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_INFO, "TKIP countermeasures initiated");
+
+	wpa_auth_countermeasures_start(hapd->wpa_auth);
+	hapd->tkip_countermeasures = 1;
+	hostapd_drv_set_countermeasures(hapd, 1);
+	wpa_gtk_rekey(hapd->wpa_auth);
+	eloop_cancel_timeout(ieee80211_tkip_countermeasures_stop, hapd, NULL);
+	eloop_register_timeout(60, 0, ieee80211_tkip_countermeasures_stop,
+			       hapd, NULL);
+	while ((sta = hapd->sta_list)) {
+		sta->acct_terminate_cause =
+			RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_RESET;
+		if (sta->flags & WLAN_STA_AUTH) {
+			mlme_deauthenticate_indication(
+				hapd, sta,
+				WLAN_REASON_MICHAEL_MIC_FAILURE);
+		}
+		hostapd_drv_sta_deauth(hapd, sta->addr,
+				       WLAN_REASON_MICHAEL_MIC_FAILURE);
+		ap_free_sta(hapd, sta);
+	}
+}
+
+
+void ieee80211_tkip_countermeasures_deinit(struct hostapd_data *hapd)
+{
+	eloop_cancel_timeout(ieee80211_tkip_countermeasures_stop, hapd, NULL);
+}
+
+
+int michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local)
+{
+	struct os_reltime now;
+	int ret = 0;
+
+	hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_INFO,
+		       "Michael MIC failure detected in received frame%s",
+		       local ? " (local)" : "");
+
+	if (addr && local) {
+		struct sta_info *sta = ap_get_sta(hapd, addr);
+		if (sta != NULL) {
+			wpa_auth_sta_local_mic_failure_report(sta->wpa_sm);
+			hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_INFO,
+				       "Michael MIC failure detected in "
+				       "received frame");
+			mlme_michaelmicfailure_indication(hapd, addr);
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "MLME-MICHAELMICFAILURE.indication "
+				   "for not associated STA (" MACSTR
+				   ") ignored", MAC2STR(addr));
+			return ret;
+		}
+	}
+
+	os_get_reltime(&now);
+	if (os_reltime_expired(&now, &hapd->michael_mic_failure, 60)) {
+		hapd->michael_mic_failures = 1;
+	} else {
+		hapd->michael_mic_failures++;
+		if (hapd->michael_mic_failures > 1) {
+			ieee80211_tkip_countermeasures_start(hapd);
+			ret = 1;
+		}
+	}
+	hapd->michael_mic_failure = now;
+
+	return ret;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/tkip_countermeasures.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/tkip_countermeasures.h
new file mode 100644
index 0000000..d3eaed3
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/tkip_countermeasures.h
@@ -0,0 +1,15 @@
+/*
+ * hostapd / TKIP countermeasures
+ * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef TKIP_COUNTERMEASURES_H
+#define TKIP_COUNTERMEASURES_H
+
+int michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local);
+void ieee80211_tkip_countermeasures_deinit(struct hostapd_data *hapd);
+
+#endif /* TKIP_COUNTERMEASURES_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ubus.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ubus.c
new file mode 100644
index 0000000..0c68408
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ubus.c
@@ -0,0 +1,2152 @@
+/*
+ * hostapd / ubus support
+ * Copyright (c) 2013, Felix Fietkau <nbd@nbd.name>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/wpabuf.h"
+#include "common/ieee802_11_defs.h"
+#include "common/hw_features_common.h"
+#include "hostapd.h"
+#include "neighbor_db.h"
+#include "wps_hostapd.h"
+#include "sta_info.h"
+#include "ubus.h"
+#include "ap_drv_ops.h"
+#include "beacon.h"
+#include "rrm.h"
+#include "wnm_ap.h"
+#include "taxonomy.h"
+#include "airtime_policy.h"
+#include "hw_features.h"
+
+static struct ubus_context *ctx;
+static struct blob_buf b;
+static int ctx_ref;
+
+static inline struct hapd_interfaces *get_hapd_interfaces_from_object(struct ubus_object *obj)
+{
+	return container_of(obj, struct hapd_interfaces, ubus);
+}
+
+static inline struct hostapd_data *get_hapd_from_object(struct ubus_object *obj)
+{
+	return container_of(obj, struct hostapd_data, ubus.obj);
+}
+
+struct ubus_banned_client {
+	struct avl_node avl;
+	u8 addr[ETH_ALEN];
+};
+
+static void ubus_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct ubus_context *ctx = eloop_ctx;
+	ubus_handle_event(ctx);
+}
+
+static void ubus_reconnect_timeout(void *eloop_data, void *user_ctx)
+{
+	if (ubus_reconnect(ctx, NULL)) {
+		eloop_register_timeout(1, 0, ubus_reconnect_timeout, ctx, NULL);
+		return;
+	}
+
+	eloop_register_read_sock(ctx->sock.fd, ubus_receive, ctx, NULL);
+}
+
+static void hostapd_ubus_connection_lost(struct ubus_context *ctx)
+{
+	eloop_unregister_read_sock(ctx->sock.fd);
+	eloop_register_timeout(1, 0, ubus_reconnect_timeout, ctx, NULL);
+}
+
+static bool hostapd_ubus_init(void)
+{
+	if (ctx)
+		return true;
+
+	ctx = ubus_connect(NULL);
+	if (!ctx)
+		return false;
+
+	ctx->connection_lost = hostapd_ubus_connection_lost;
+	eloop_register_read_sock(ctx->sock.fd, ubus_receive, ctx, NULL);
+	return true;
+}
+
+static void hostapd_ubus_ref_inc(void)
+{
+	ctx_ref++;
+}
+
+static void hostapd_ubus_ref_dec(void)
+{
+	ctx_ref--;
+	if (!ctx)
+		return;
+
+	if (ctx_ref)
+		return;
+
+	eloop_unregister_read_sock(ctx->sock.fd);
+	ubus_free(ctx);
+	ctx = NULL;
+}
+
+void hostapd_ubus_add_iface(struct hostapd_iface *iface)
+{
+	if (!hostapd_ubus_init())
+		return;
+}
+
+void hostapd_ubus_free_iface(struct hostapd_iface *iface)
+{
+	if (!ctx)
+		return;
+}
+
+static void hostapd_notify_ubus(struct ubus_object *obj, char *bssname, char *event)
+{
+	char *event_type;
+
+	if (!ctx || !obj)
+		return;
+
+	if (asprintf(&event_type, "bss.%s", event) < 0)
+		return;
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_string(&b, "name", bssname);
+	ubus_notify(ctx, obj, event_type, b.head, -1);
+	free(event_type);
+}
+
+static void hostapd_send_procd_event(char *bssname, char *event)
+{
+	char *name, *s;
+	uint32_t id;
+	void *v;
+
+	if (!ctx || ubus_lookup_id(ctx, "service", &id))
+		return;
+
+	if (asprintf(&name, "hostapd.%s.%s", bssname, event) < 0)
+		return;
+
+	blob_buf_init(&b, 0);
+
+	s = blobmsg_alloc_string_buffer(&b, "type", strlen(name) + 1);
+	sprintf(s, "%s", name);
+	blobmsg_add_string_buffer(&b);
+
+	v = blobmsg_open_table(&b, "data");
+	blobmsg_close_table(&b, v);
+
+	ubus_invoke(ctx, id, "event", b.head, NULL, NULL, 1000);
+
+	free(name);
+}
+
+static void hostapd_send_shared_event(struct ubus_object *obj, char *bssname, char *event)
+{
+	hostapd_send_procd_event(bssname, event);
+	hostapd_notify_ubus(obj, bssname, event);
+}
+
+static void
+hostapd_bss_del_ban(void *eloop_data, void *user_ctx)
+{
+	struct ubus_banned_client *ban = eloop_data;
+	struct hostapd_data *hapd = user_ctx;
+
+	avl_delete(&hapd->ubus.banned, &ban->avl);
+	free(ban);
+}
+
+static void
+hostapd_bss_ban_client(struct hostapd_data *hapd, u8 *addr, int time)
+{
+	struct ubus_banned_client *ban;
+
+	if (time < 0)
+		time = 0;
+
+	ban = avl_find_element(&hapd->ubus.banned, addr, ban, avl);
+	if (!ban) {
+		if (!time)
+			return;
+
+		ban = os_zalloc(sizeof(*ban));
+		memcpy(ban->addr, addr, sizeof(ban->addr));
+		ban->avl.key = ban->addr;
+		avl_insert(&hapd->ubus.banned, &ban->avl);
+	} else {
+		eloop_cancel_timeout(hostapd_bss_del_ban, ban, hapd);
+		if (!time) {
+			hostapd_bss_del_ban(ban, hapd);
+			return;
+		}
+	}
+
+	eloop_register_timeout(0, time * 1000, hostapd_bss_del_ban, ban, hapd);
+}
+
+static int
+hostapd_bss_reload(struct ubus_context *ctx, struct ubus_object *obj,
+		   struct ubus_request_data *req, const char *method,
+		   struct blob_attr *msg)
+{
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+	int ret = hostapd_reload_config(hapd->iface, 1);
+
+	hostapd_send_shared_event(&hapd->iface->interfaces->ubus, hapd->conf->iface, "reload");
+	return ret;
+}
+
+
+static void
+hostapd_parse_vht_map_blobmsg(uint16_t map)
+{
+	char label[4];
+	int16_t val;
+	int i;
+
+	for (i = 0; i < 8; i++) {
+		snprintf(label, 4, "%dss", i + 1);
+
+		val = (map & (BIT(1) | BIT(0))) + 7;
+		blobmsg_add_u16(&b, label, val == 10 ? -1 : val);
+		map = map >> 2;
+	}
+}
+
+static void
+hostapd_parse_vht_capab_blobmsg(struct ieee80211_vht_capabilities *vhtc)
+{
+	void *supported_mcs;
+	void *map;
+	int i;
+
+	static const struct {
+		const char *name;
+		uint32_t flag;
+	} vht_capas[] = {
+		{ "su_beamformee", VHT_CAP_SU_BEAMFORMEE_CAPABLE },
+		{ "mu_beamformee", VHT_CAP_MU_BEAMFORMEE_CAPABLE },
+	};
+
+	for (i = 0; i < ARRAY_SIZE(vht_capas); i++)
+		blobmsg_add_u8(&b, vht_capas[i].name,
+				!!(vhtc->vht_capabilities_info & vht_capas[i].flag));
+
+	supported_mcs = blobmsg_open_table(&b, "mcs_map");
+
+	/* RX map */
+	map = blobmsg_open_table(&b, "rx");
+	hostapd_parse_vht_map_blobmsg(le_to_host16(vhtc->vht_supported_mcs_set.rx_map));
+	blobmsg_close_table(&b, map);
+
+	/* TX map */
+	map = blobmsg_open_table(&b, "tx");
+	hostapd_parse_vht_map_blobmsg(le_to_host16(vhtc->vht_supported_mcs_set.tx_map));
+	blobmsg_close_table(&b, map);
+
+	blobmsg_close_table(&b, supported_mcs);
+}
+
+static void
+hostapd_parse_capab_blobmsg(struct sta_info *sta)
+{
+	void *r, *v;
+
+	v = blobmsg_open_table(&b, "capabilities");
+
+	if (sta->vht_capabilities) {
+		r = blobmsg_open_table(&b, "vht");
+		hostapd_parse_vht_capab_blobmsg(sta->vht_capabilities);
+		blobmsg_close_table(&b, r);
+	}
+
+	/* ToDo: Add HT / HE capability parsing */
+
+	blobmsg_close_table(&b, v);
+}
+
+static int
+hostapd_bss_get_clients(struct ubus_context *ctx, struct ubus_object *obj,
+			struct ubus_request_data *req, const char *method,
+			struct blob_attr *msg)
+{
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+	struct hostap_sta_driver_data sta_driver_data;
+	struct sta_info *sta;
+	void *list, *c;
+	char mac_buf[20];
+	static const struct {
+		const char *name;
+		uint32_t flag;
+	} sta_flags[] = {
+		{ "auth", WLAN_STA_AUTH },
+		{ "assoc", WLAN_STA_ASSOC },
+		{ "authorized", WLAN_STA_AUTHORIZED },
+		{ "preauth", WLAN_STA_PREAUTH },
+		{ "wds", WLAN_STA_WDS },
+		{ "wmm", WLAN_STA_WMM },
+		{ "ht", WLAN_STA_HT },
+		{ "vht", WLAN_STA_VHT },
+		{ "he", WLAN_STA_HE },
+		{ "wps", WLAN_STA_WPS },
+		{ "mfp", WLAN_STA_MFP },
+	};
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_u32(&b, "freq", hapd->iface->freq);
+	list = blobmsg_open_table(&b, "clients");
+	for (sta = hapd->sta_list; sta; sta = sta->next) {
+		void *r;
+		int i;
+
+		sprintf(mac_buf, MACSTR, MAC2STR(sta->addr));
+		c = blobmsg_open_table(&b, mac_buf);
+		for (i = 0; i < ARRAY_SIZE(sta_flags); i++)
+			blobmsg_add_u8(&b, sta_flags[i].name,
+				       !!(sta->flags & sta_flags[i].flag));
+
+#ifdef CONFIG_MBO
+		blobmsg_add_u8(&b, "mbo", !!(sta->cell_capa));
+#endif
+
+		r = blobmsg_open_array(&b, "rrm");
+		for (i = 0; i < ARRAY_SIZE(sta->rrm_enabled_capa); i++)
+			blobmsg_add_u32(&b, "", sta->rrm_enabled_capa[i]);
+		blobmsg_close_array(&b, r);
+
+		r = blobmsg_open_array(&b, "extended_capabilities");
+		/* Check if client advertises extended capabilities */
+		if (sta->ext_capability && sta->ext_capability[0] > 0) {
+			for (i = 0; i < sta->ext_capability[0]; i++) {
+				blobmsg_add_u32(&b, "", sta->ext_capability[1 + i]);
+			}
+		}
+		blobmsg_close_array(&b, r);
+
+		blobmsg_add_u32(&b, "aid", sta->aid);
+#ifdef CONFIG_TAXONOMY
+		r = blobmsg_alloc_string_buffer(&b, "signature", 1024);
+		if (retrieve_sta_taxonomy(hapd, sta, r, 1024) > 0)
+			blobmsg_add_string_buffer(&b);
+#endif
+
+		/* Driver information */
+		if (hostapd_drv_read_sta_data(hapd, &sta_driver_data, sta->addr) >= 0) {
+			r = blobmsg_open_table(&b, "bytes");
+			blobmsg_add_u64(&b, "rx", sta_driver_data.rx_bytes);
+			blobmsg_add_u64(&b, "tx", sta_driver_data.tx_bytes);
+			blobmsg_close_table(&b, r);
+			r = blobmsg_open_table(&b, "airtime");
+			blobmsg_add_u64(&b, "rx", sta_driver_data.rx_airtime);
+			blobmsg_add_u64(&b, "tx", sta_driver_data.tx_airtime);
+			blobmsg_close_table(&b, r);
+			r = blobmsg_open_table(&b, "packets");
+			blobmsg_add_u32(&b, "rx", sta_driver_data.rx_packets);
+			blobmsg_add_u32(&b, "tx", sta_driver_data.tx_packets);
+			blobmsg_close_table(&b, r);
+			r = blobmsg_open_table(&b, "rate");
+			/* Rate in kbits */
+			blobmsg_add_u32(&b, "rx", sta_driver_data.current_rx_rate * 100);
+			blobmsg_add_u32(&b, "tx", sta_driver_data.current_tx_rate * 100);
+			blobmsg_close_table(&b, r);
+			blobmsg_add_u32(&b, "signal", sta_driver_data.signal);
+		}
+
+		hostapd_parse_capab_blobmsg(sta);
+
+		blobmsg_close_table(&b, c);
+	}
+	blobmsg_close_array(&b, list);
+	ubus_send_reply(ctx, req, b.head);
+
+	return 0;
+}
+
+static int
+hostapd_bss_get_features(struct ubus_context *ctx, struct ubus_object *obj,
+			struct ubus_request_data *req, const char *method,
+			struct blob_attr *msg)
+{
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_u8(&b, "ht_supported", ht_supported(hapd->iface->hw_features));
+	blobmsg_add_u8(&b, "vht_supported", vht_supported(hapd->iface->hw_features));
+	ubus_send_reply(ctx, req, b.head);
+
+	return 0;
+}
+
+/* Imported from iw/util.c
+ *  https://git.kernel.org/pub/scm/linux/kernel/git/jberg/iw.git/tree/util.c?id=4b25ae3537af48dbf9d0abf94132e5ba01b32c18#n200
+ */
+int ieee80211_frequency_to_channel(int freq)
+{
+	/* see 802.11-2007 17.3.8.3.2 and Annex J */
+	if (freq == 2484)
+		return 14;
+	/* see 802.11ax D6.1 27.3.23.2 and Annex E */
+	else if (freq == 5935)
+		return 2;
+	else if (freq < 2484)
+		return (freq - 2407) / 5;
+	else if (freq >= 4910 && freq <= 4980)
+		return (freq - 4000) / 5;
+	else if (freq < 5950)
+		return (freq - 5000) / 5;
+	else if (freq <= 45000) /* DMG band lower limit */
+		/* see 802.11ax D6.1 27.3.23.2 */
+		return (freq - 5950) / 5;
+	else if (freq >= 58320 && freq <= 70200)
+		return (freq - 56160) / 2160;
+	else
+		return 0;
+}
+
+static int
+hostapd_bss_get_status(struct ubus_context *ctx, struct ubus_object *obj,
+		       struct ubus_request_data *req, const char *method,
+		       struct blob_attr *msg)
+{
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+	void *airtime_table, *dfs_table, *rrm_table, *wnm_table;
+	struct os_reltime now;
+	char ssid[SSID_MAX_LEN + 1];
+	char phy_name[17];
+	size_t ssid_len = SSID_MAX_LEN;
+	u8 channel = 0, op_class = 0;
+
+	if (hapd->conf->ssid.ssid_len < SSID_MAX_LEN)
+		ssid_len = hapd->conf->ssid.ssid_len;
+	
+	ieee80211_freq_to_channel_ext(hapd->iface->freq,
+				      hapd->iconf->secondary_channel,
+				      hostapd_get_oper_chwidth(hapd->iconf),
+				      &op_class, &channel);
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_string(&b, "status", hostapd_state_text(hapd->iface->state));
+	blobmsg_printf(&b, "bssid", MACSTR, MAC2STR(hapd->conf->bssid));
+
+	memset(ssid, 0, SSID_MAX_LEN + 1);
+	memcpy(ssid, hapd->conf->ssid.ssid, ssid_len);
+	blobmsg_add_string(&b, "ssid", ssid);
+
+	blobmsg_add_u32(&b, "freq", hapd->iface->freq);
+	blobmsg_add_u32(&b, "channel", channel);
+	blobmsg_add_u32(&b, "op_class", op_class);
+	blobmsg_add_u32(&b, "beacon_interval", hapd->iconf->beacon_int);
+#ifdef CONFIG_IEEE80211AX
+	blobmsg_add_u32(&b, "bss_color", hapd->iface->conf->he_op.he_bss_color_disabled ? -1 :
+					 hapd->iface->conf->he_op.he_bss_color);
+#else
+	blobmsg_add_u32(&b, "bss_color", -1);
+#endif
+
+	snprintf(phy_name, 17, "%s", hapd->iface->phy);
+	blobmsg_add_string(&b, "phy", phy_name);
+
+	/* RRM */
+	rrm_table = blobmsg_open_table(&b, "rrm");
+	blobmsg_add_u64(&b, "neighbor_report_tx", hapd->openwrt_stats.rrm.neighbor_report_tx);
+	blobmsg_close_table(&b, rrm_table);
+
+	/* WNM */
+	wnm_table = blobmsg_open_table(&b, "wnm");
+	blobmsg_add_u64(&b, "bss_transition_query_rx", hapd->openwrt_stats.wnm.bss_transition_query_rx);
+	blobmsg_add_u64(&b, "bss_transition_request_tx", hapd->openwrt_stats.wnm.bss_transition_request_tx);
+	blobmsg_add_u64(&b, "bss_transition_response_rx", hapd->openwrt_stats.wnm.bss_transition_response_rx);
+	blobmsg_close_table(&b, wnm_table);
+
+	/* Airtime */
+	airtime_table = blobmsg_open_table(&b, "airtime");
+	blobmsg_add_u64(&b, "time", hapd->iface->last_channel_time);
+	blobmsg_add_u64(&b, "time_busy", hapd->iface->last_channel_time_busy);
+	blobmsg_add_u16(&b, "utilization", hapd->iface->channel_utilization);
+	blobmsg_close_table(&b, airtime_table);
+
+	/* DFS */
+	dfs_table = blobmsg_open_table(&b, "dfs");
+	blobmsg_add_u32(&b, "cac_seconds", hapd->iface->dfs_cac_ms / 1000);
+	blobmsg_add_u8(&b, "cac_active", !!(hapd->iface->cac_started));
+	os_reltime_age(&hapd->iface->dfs_cac_start, &now);
+	blobmsg_add_u32(&b, "cac_seconds_left",
+			hapd->iface->cac_started ? hapd->iface->dfs_cac_ms / 1000 - now.sec : 0);
+	blobmsg_close_table(&b, dfs_table);
+
+	ubus_send_reply(ctx, req, b.head);
+
+	return 0;
+}
+
+enum {
+	NOTIFY_RESPONSE,
+	__NOTIFY_MAX
+};
+
+static const struct blobmsg_policy notify_policy[__NOTIFY_MAX] = {
+	[NOTIFY_RESPONSE] = { "notify_response", BLOBMSG_TYPE_INT32 },
+};
+
+static int
+hostapd_notify_response(struct ubus_context *ctx, struct ubus_object *obj,
+			struct ubus_request_data *req, const char *method,
+			struct blob_attr *msg)
+{
+	struct blob_attr *tb[__NOTIFY_MAX];
+	struct hostapd_data *hapd = get_hapd_from_object(obj);
+	struct wpabuf *elems;
+	const char *pos;
+	size_t len;
+
+	blobmsg_parse(notify_policy, __NOTIFY_MAX, tb,
+		      blob_data(msg), blob_len(msg));
+
+	if (!tb[NOTIFY_RESPONSE])
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	hapd->ubus.notify_response = blobmsg_get_u32(tb[NOTIFY_RESPONSE]);
+
+	return UBUS_STATUS_OK;
+}
+
+enum {
+	DEL_CLIENT_ADDR,
+	DEL_CLIENT_REASON,
+	DEL_CLIENT_DEAUTH,
+	DEL_CLIENT_BAN_TIME,
+	__DEL_CLIENT_MAX
+};
+
+static const struct blobmsg_policy del_policy[__DEL_CLIENT_MAX] = {
+	[DEL_CLIENT_ADDR] = { "addr", BLOBMSG_TYPE_STRING },
+	[DEL_CLIENT_REASON] = { "reason", BLOBMSG_TYPE_INT32 },
+	[DEL_CLIENT_DEAUTH] = { "deauth", BLOBMSG_TYPE_INT8 },
+	[DEL_CLIENT_BAN_TIME] = { "ban_time", BLOBMSG_TYPE_INT32 },
+};
+
+static int
+hostapd_bss_del_client(struct ubus_context *ctx, struct ubus_object *obj,
+			struct ubus_request_data *req, const char *method,
+			struct blob_attr *msg)
+{
+	struct blob_attr *tb[__DEL_CLIENT_MAX];
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+	struct sta_info *sta;
+	bool deauth = false;
+	int reason;
+	u8 addr[ETH_ALEN];
+
+	blobmsg_parse(del_policy, __DEL_CLIENT_MAX, tb, blob_data(msg), blob_len(msg));
+
+	if (!tb[DEL_CLIENT_ADDR])
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	if (hwaddr_aton(blobmsg_data(tb[DEL_CLIENT_ADDR]), addr))
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	if (tb[DEL_CLIENT_REASON])
+		reason = blobmsg_get_u32(tb[DEL_CLIENT_REASON]);
+
+	if (tb[DEL_CLIENT_DEAUTH])
+		deauth = blobmsg_get_bool(tb[DEL_CLIENT_DEAUTH]);
+
+	sta = ap_get_sta(hapd, addr);
+	if (sta) {
+		if (deauth) {
+			hostapd_drv_sta_deauth(hapd, addr, reason);
+			ap_sta_deauthenticate(hapd, sta, reason);
+		} else {
+			hostapd_drv_sta_disassoc(hapd, addr, reason);
+			ap_sta_disassociate(hapd, sta, reason);
+		}
+	}
+
+	if (tb[DEL_CLIENT_BAN_TIME])
+		hostapd_bss_ban_client(hapd, addr, blobmsg_get_u32(tb[DEL_CLIENT_BAN_TIME]));
+
+	return 0;
+}
+
+static void
+blobmsg_add_macaddr(struct blob_buf *buf, const char *name, const u8 *addr)
+{
+	char *s;
+
+	s = blobmsg_alloc_string_buffer(buf, name, 20);
+	sprintf(s, MACSTR, MAC2STR(addr));
+	blobmsg_add_string_buffer(buf);
+}
+
+static int
+hostapd_bss_list_bans(struct ubus_context *ctx, struct ubus_object *obj,
+		      struct ubus_request_data *req, const char *method,
+		      struct blob_attr *msg)
+{
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+	struct ubus_banned_client *ban;
+	void *c;
+
+	blob_buf_init(&b, 0);
+	c = blobmsg_open_array(&b, "clients");
+	avl_for_each_element(&hapd->ubus.banned, ban, avl)
+		blobmsg_add_macaddr(&b, NULL, ban->addr);
+	blobmsg_close_array(&b, c);
+	ubus_send_reply(ctx, req, b.head);
+
+	return 0;
+}
+
+#ifdef CONFIG_WPS
+static int
+hostapd_bss_wps_start(struct ubus_context *ctx, struct ubus_object *obj,
+			struct ubus_request_data *req, const char *method,
+			struct blob_attr *msg)
+{
+	int rc;
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+
+	rc = hostapd_wps_button_pushed(hapd, NULL);
+
+	if (rc != 0)
+		return UBUS_STATUS_NOT_SUPPORTED;
+
+	return 0;
+}
+
+
+static const char * pbc_status_enum_str(enum pbc_status status)
+{
+	switch (status) {
+	case WPS_PBC_STATUS_DISABLE:
+		return "Disabled";
+	case WPS_PBC_STATUS_ACTIVE:
+		return "Active";
+	case WPS_PBC_STATUS_TIMEOUT:
+		return "Timed-out";
+	case WPS_PBC_STATUS_OVERLAP:
+		return "Overlap";
+	default:
+		return "Unknown";
+	}
+}
+
+static int
+hostapd_bss_wps_status(struct ubus_context *ctx, struct ubus_object *obj,
+			struct ubus_request_data *req, const char *method,
+			struct blob_attr *msg)
+{
+	int rc;
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+
+	blob_buf_init(&b, 0);
+
+	blobmsg_add_string(&b, "pbc_status", pbc_status_enum_str(hapd->wps_stats.pbc_status));
+	blobmsg_add_string(&b, "last_wps_result",
+			   (hapd->wps_stats.status == WPS_STATUS_SUCCESS ?
+			    "Success":
+			    (hapd->wps_stats.status == WPS_STATUS_FAILURE ?
+			     "Failed" : "None")));
+
+	/* If status == Failure - Add possible Reasons */
+	if(hapd->wps_stats.status == WPS_STATUS_FAILURE &&
+	   hapd->wps_stats.failure_reason > 0)
+		blobmsg_add_string(&b, "reason", wps_ei_str(hapd->wps_stats.failure_reason));
+
+	if (hapd->wps_stats.status)
+		blobmsg_printf(&b, "peer_address", MACSTR, MAC2STR(hapd->wps_stats.peer_addr));
+
+	ubus_send_reply(ctx, req, b.head);
+
+	return 0;
+}
+
+static int
+hostapd_bss_wps_cancel(struct ubus_context *ctx, struct ubus_object *obj,
+			struct ubus_request_data *req, const char *method,
+			struct blob_attr *msg)
+{
+	int rc;
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+
+	rc = hostapd_wps_cancel(hapd);
+
+	if (rc != 0)
+		return UBUS_STATUS_NOT_SUPPORTED;
+
+	return 0;
+}
+#endif /* CONFIG_WPS */
+
+static int
+hostapd_bss_update_beacon(struct ubus_context *ctx, struct ubus_object *obj,
+			struct ubus_request_data *req, const char *method,
+			struct blob_attr *msg)
+{
+	int rc;
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+
+	rc = ieee802_11_set_beacon(hapd);
+
+	if (rc != 0)
+		return UBUS_STATUS_NOT_SUPPORTED;
+
+	return 0;
+}
+
+enum {
+	CONFIG_IFACE,
+	CONFIG_FILE,
+	__CONFIG_MAX
+};
+
+static const struct blobmsg_policy config_add_policy[__CONFIG_MAX] = {
+	[CONFIG_IFACE] = { "iface", BLOBMSG_TYPE_STRING },
+	[CONFIG_FILE] = { "config", BLOBMSG_TYPE_STRING },
+};
+
+static int
+hostapd_config_add(struct ubus_context *ctx, struct ubus_object *obj,
+		   struct ubus_request_data *req, const char *method,
+		   struct blob_attr *msg)
+{
+	struct blob_attr *tb[__CONFIG_MAX];
+	struct hapd_interfaces *interfaces = get_hapd_interfaces_from_object(obj);
+	char buf[128];
+
+	blobmsg_parse(config_add_policy, __CONFIG_MAX, tb, blob_data(msg), blob_len(msg));
+
+	if (!tb[CONFIG_FILE] || !tb[CONFIG_IFACE])
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	snprintf(buf, sizeof(buf), "bss_config=%s:%s",
+		blobmsg_get_string(tb[CONFIG_IFACE]),
+		blobmsg_get_string(tb[CONFIG_FILE]));
+
+	if (hostapd_add_iface(interfaces, buf))
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_u32(&b, "pid", getpid());
+	ubus_send_reply(ctx, req, b.head);
+
+	return UBUS_STATUS_OK;
+}
+
+enum {
+	CONFIG_REM_IFACE,
+	__CONFIG_REM_MAX
+};
+
+static const struct blobmsg_policy config_remove_policy[__CONFIG_REM_MAX] = {
+	[CONFIG_REM_IFACE] = { "iface", BLOBMSG_TYPE_STRING },
+};
+
+static int
+hostapd_config_remove(struct ubus_context *ctx, struct ubus_object *obj,
+		      struct ubus_request_data *req, const char *method,
+		      struct blob_attr *msg)
+{
+	struct blob_attr *tb[__CONFIG_REM_MAX];
+	struct hapd_interfaces *interfaces = get_hapd_interfaces_from_object(obj);
+	char buf[128];
+
+	blobmsg_parse(config_remove_policy, __CONFIG_REM_MAX, tb, blob_data(msg), blob_len(msg));
+
+	if (!tb[CONFIG_REM_IFACE])
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	if (hostapd_remove_iface(interfaces, blobmsg_get_string(tb[CONFIG_REM_IFACE])))
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	return UBUS_STATUS_OK;
+}
+
+enum {
+	CSA_FREQ,
+	CSA_BCN_COUNT,
+	CSA_CENTER_FREQ1,
+	CSA_CENTER_FREQ2,
+	CSA_BANDWIDTH,
+	CSA_SEC_CHANNEL_OFFSET,
+	CSA_HT,
+	CSA_VHT,
+	CSA_HE,
+	CSA_BLOCK_TX,
+	CSA_FORCE,
+	__CSA_MAX
+};
+
+static const struct blobmsg_policy csa_policy[__CSA_MAX] = {
+	[CSA_FREQ] = { "freq", BLOBMSG_TYPE_INT32 },
+	[CSA_BCN_COUNT] = { "bcn_count", BLOBMSG_TYPE_INT32 },
+	[CSA_CENTER_FREQ1] = { "center_freq1", BLOBMSG_TYPE_INT32 },
+	[CSA_CENTER_FREQ2] = { "center_freq2", BLOBMSG_TYPE_INT32 },
+	[CSA_BANDWIDTH] = { "bandwidth", BLOBMSG_TYPE_INT32 },
+	[CSA_SEC_CHANNEL_OFFSET] = { "sec_channel_offset", BLOBMSG_TYPE_INT32 },
+	[CSA_HT] = { "ht", BLOBMSG_TYPE_BOOL },
+	[CSA_VHT] = { "vht", BLOBMSG_TYPE_BOOL },
+	[CSA_HE] = { "he", BLOBMSG_TYPE_BOOL },
+	[CSA_BLOCK_TX] = { "block_tx", BLOBMSG_TYPE_BOOL },
+	[CSA_FORCE] = { "force", BLOBMSG_TYPE_BOOL },
+};
+
+
+static void switch_chan_fallback_cb(void *eloop_data, void *user_ctx)
+{
+	struct hostapd_iface *iface = eloop_data;
+	struct hostapd_freq_params *freq_params = user_ctx;
+
+	hostapd_switch_channel_fallback(iface, freq_params);
+}
+
+#ifdef NEED_AP_MLME
+static int
+hostapd_switch_chan(struct ubus_context *ctx, struct ubus_object *obj,
+		    struct ubus_request_data *req, const char *method,
+		    struct blob_attr *msg)
+{
+	struct blob_attr *tb[__CSA_MAX];
+	struct hostapd_data *hapd = get_hapd_from_object(obj);
+	struct hostapd_config *iconf = hapd->iface->conf;
+	struct hostapd_freq_params *freq_params;
+	struct hostapd_hw_modes *mode = hapd->iface->current_mode;
+	struct csa_settings css = {
+		.freq_params = {
+			.ht_enabled = iconf->ieee80211n,
+			.vht_enabled = iconf->ieee80211ac,
+			.he_enabled = iconf->ieee80211ax,
+			.sec_channel_offset = iconf->secondary_channel,
+		}
+	};
+	u8 chwidth = hostapd_get_oper_chwidth(iconf);
+	u8 seg0 = 0, seg1 = 0;
+	int ret = UBUS_STATUS_OK;
+	int i;
+
+	blobmsg_parse(csa_policy, __CSA_MAX, tb, blob_data(msg), blob_len(msg));
+
+	if (!tb[CSA_FREQ])
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	switch (iconf->vht_oper_chwidth) {
+	case CHANWIDTH_USE_HT:
+		if (iconf->secondary_channel)
+			css.freq_params.bandwidth = 40;
+		else
+			css.freq_params.bandwidth = 20;
+		break;
+	case CHANWIDTH_160MHZ:
+		css.freq_params.bandwidth = 160;
+		break;
+	default:
+		css.freq_params.bandwidth = 80;
+		break;
+	}
+
+	css.freq_params.freq = blobmsg_get_u32(tb[CSA_FREQ]);
+
+#define SET_CSA_SETTING(name, field, type) \
+	do { \
+		if (tb[name]) \
+			css.field = blobmsg_get_ ## type(tb[name]); \
+	} while(0)
+
+	SET_CSA_SETTING(CSA_BCN_COUNT, cs_count, u32);
+	SET_CSA_SETTING(CSA_CENTER_FREQ1, freq_params.center_freq1, u32);
+	SET_CSA_SETTING(CSA_CENTER_FREQ2, freq_params.center_freq2, u32);
+	SET_CSA_SETTING(CSA_BANDWIDTH, freq_params.bandwidth, u32);
+	SET_CSA_SETTING(CSA_SEC_CHANNEL_OFFSET, freq_params.sec_channel_offset, u32);
+	SET_CSA_SETTING(CSA_HT, freq_params.ht_enabled, bool);
+	SET_CSA_SETTING(CSA_VHT, freq_params.vht_enabled, bool);
+	SET_CSA_SETTING(CSA_HE, freq_params.he_enabled, bool);
+	SET_CSA_SETTING(CSA_BLOCK_TX, block_tx, bool);
+
+	css.freq_params.channel = hostapd_hw_get_channel(hapd, css.freq_params.freq);
+	if (!css.freq_params.channel)
+		return UBUS_STATUS_NOT_SUPPORTED;
+
+	switch (css.freq_params.bandwidth) {
+	case 160:
+		chwidth = CHANWIDTH_160MHZ;
+		break;
+	case 80:
+		chwidth = css.freq_params.center_freq2 ? CHANWIDTH_80P80MHZ : CHANWIDTH_80MHZ;
+		break;
+	default:
+		chwidth = CHANWIDTH_USE_HT;
+		break;
+	}
+
+	hostapd_set_freq_params(&css.freq_params, iconf->hw_mode,
+				css.freq_params.freq,
+				css.freq_params.channel, iconf->enable_edmg,
+				iconf->edmg_channel,
+				css.freq_params.ht_enabled,
+				css.freq_params.vht_enabled,
+				css.freq_params.he_enabled,
+				css.freq_params.eht_enabled,
+				css.freq_params.sec_channel_offset,
+				chwidth, seg0, seg1,
+				iconf->vht_capab,
+				mode ? &mode->he_capab[IEEE80211_MODE_AP] :
+				NULL,
+				mode ? &mode->eht_capab[IEEE80211_MODE_AP] :
+				NULL);
+
+	for (i = 0; i < hapd->iface->num_bss; i++) {
+		struct hostapd_data *bss = hapd->iface->bss[i];
+
+		if (hostapd_switch_channel(bss, &css) != 0)
+			ret = UBUS_STATUS_NOT_SUPPORTED;
+	}
+
+	if (!ret || !tb[CSA_FORCE] || !blobmsg_get_bool(tb[CSA_FORCE]))
+		return ret;
+
+	freq_params = malloc(sizeof(*freq_params));
+	memcpy(freq_params, &css.freq_params, sizeof(*freq_params));
+	eloop_register_timeout(0, 1, switch_chan_fallback_cb,
+			       hapd->iface, freq_params);
+
+	return 0;
+#undef SET_CSA_SETTING
+}
+#endif
+
+enum {
+	VENDOR_ELEMENTS,
+	__VENDOR_ELEMENTS_MAX
+};
+
+static const struct blobmsg_policy ve_policy[__VENDOR_ELEMENTS_MAX] = {
+	/* vendor elements are provided as hex-string */
+	[VENDOR_ELEMENTS] = { "vendor_elements", BLOBMSG_TYPE_STRING },
+};
+
+static int
+hostapd_vendor_elements(struct ubus_context *ctx, struct ubus_object *obj,
+			struct ubus_request_data *req, const char *method,
+			struct blob_attr *msg)
+{
+	struct blob_attr *tb[__VENDOR_ELEMENTS_MAX];
+	struct hostapd_data *hapd = get_hapd_from_object(obj);
+	struct hostapd_bss_config *bss = hapd->conf;
+	struct wpabuf *elems;
+	const char *pos;
+	size_t len;
+
+	blobmsg_parse(ve_policy, __VENDOR_ELEMENTS_MAX, tb,
+		      blob_data(msg), blob_len(msg));
+
+	if (!tb[VENDOR_ELEMENTS])
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	pos = blobmsg_data(tb[VENDOR_ELEMENTS]);
+	len = os_strlen(pos);
+	if (len & 0x01)
+			return UBUS_STATUS_INVALID_ARGUMENT;
+
+	len /= 2;
+	if (len == 0) {
+		wpabuf_free(bss->vendor_elements);
+		bss->vendor_elements = NULL;
+		return 0;
+	}
+
+	elems = wpabuf_alloc(len);
+	if (elems == NULL)
+		return 1;
+
+	if (hexstr2bin(pos, wpabuf_put(elems, len), len)) {
+		wpabuf_free(elems);
+		return UBUS_STATUS_INVALID_ARGUMENT;
+	}
+
+	wpabuf_free(bss->vendor_elements);
+	bss->vendor_elements = elems;
+
+	/* update beacons if vendor elements were set successfully */
+	if (ieee802_11_update_beacons(hapd->iface) != 0)
+		return UBUS_STATUS_NOT_SUPPORTED;
+	return UBUS_STATUS_OK;
+}
+
+static void
+hostapd_rrm_print_nr(struct hostapd_neighbor_entry *nr)
+{
+	const u8 *data;
+	char *str;
+	int len;
+
+	blobmsg_printf(&b, "", MACSTR, MAC2STR(nr->bssid));
+
+	str = blobmsg_alloc_string_buffer(&b, "", nr->ssid.ssid_len + 1);
+	memcpy(str, nr->ssid.ssid, nr->ssid.ssid_len);
+	str[nr->ssid.ssid_len] = 0;
+	blobmsg_add_string_buffer(&b);
+
+	len = wpabuf_len(nr->nr);
+	str = blobmsg_alloc_string_buffer(&b, "", 2 * len + 1);
+	wpa_snprintf_hex(str, 2 * len + 1, wpabuf_head_u8(nr->nr), len);
+	blobmsg_add_string_buffer(&b);
+}
+
+enum {
+	BSS_MGMT_EN_NEIGHBOR,
+	BSS_MGMT_EN_BEACON,
+	BSS_MGMT_EN_LINK_MEASUREMENT,
+#ifdef CONFIG_WNM_AP
+	BSS_MGMT_EN_BSS_TRANSITION,
+#endif
+	__BSS_MGMT_EN_MAX
+};
+
+static bool
+__hostapd_bss_mgmt_enable_f(struct hostapd_data *hapd, int flag)
+{
+	struct hostapd_bss_config *bss = hapd->conf;
+	uint32_t flags;
+
+	switch (flag) {
+	case BSS_MGMT_EN_NEIGHBOR:
+		if (bss->radio_measurements[0] &
+		    WLAN_RRM_CAPS_NEIGHBOR_REPORT)
+			return false;
+
+		bss->radio_measurements[0] |=
+			WLAN_RRM_CAPS_NEIGHBOR_REPORT;
+		hostapd_neighbor_set_own_report(hapd);
+		return true;
+	case BSS_MGMT_EN_BEACON:
+		flags = WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE |
+			WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE |
+			WLAN_RRM_CAPS_BEACON_REPORT_TABLE;
+
+		if (bss->radio_measurements[0] & flags == flags)
+			return false;
+
+		bss->radio_measurements[0] |= (u8) flags;
+		return true;
+	case BSS_MGMT_EN_LINK_MEASUREMENT:
+		flags = WLAN_RRM_CAPS_LINK_MEASUREMENT;
+
+		if (bss->radio_measurements[0] & flags == flags)
+			return false;
+
+		bss->radio_measurements[0] |= (u8) flags;
+		return true;
+#ifdef CONFIG_WNM_AP
+	case BSS_MGMT_EN_BSS_TRANSITION:
+		if (bss->bss_transition)
+			return false;
+
+		bss->bss_transition = 1;
+		return true;
+#endif
+	}
+}
+
+static void
+__hostapd_bss_mgmt_enable(struct hostapd_data *hapd, uint32_t flags)
+{
+	bool update = false;
+	int i;
+
+	for (i = 0; i < __BSS_MGMT_EN_MAX; i++) {
+		if (!(flags & (1 << i)))
+			continue;
+
+		update |= __hostapd_bss_mgmt_enable_f(hapd, i);
+	}
+
+	if (update)
+		ieee802_11_update_beacons(hapd->iface);
+}
+
+
+static const struct blobmsg_policy bss_mgmt_enable_policy[__BSS_MGMT_EN_MAX] = {
+	[BSS_MGMT_EN_NEIGHBOR] = { "neighbor_report", BLOBMSG_TYPE_BOOL },
+	[BSS_MGMT_EN_BEACON] = { "beacon_report", BLOBMSG_TYPE_BOOL },
+	[BSS_MGMT_EN_LINK_MEASUREMENT] = { "link_measurement", BLOBMSG_TYPE_BOOL },
+#ifdef CONFIG_WNM_AP
+	[BSS_MGMT_EN_BSS_TRANSITION] = { "bss_transition", BLOBMSG_TYPE_BOOL },
+#endif
+};
+
+static int
+hostapd_bss_mgmt_enable(struct ubus_context *ctx, struct ubus_object *obj,
+		   struct ubus_request_data *req, const char *method,
+		   struct blob_attr *msg)
+
+{
+	struct hostapd_data *hapd = get_hapd_from_object(obj);
+	struct blob_attr *tb[__BSS_MGMT_EN_MAX];
+	struct blob_attr *cur;
+	uint32_t flags = 0;
+	int i;
+	bool neigh = false, beacon = false;
+
+	blobmsg_parse(bss_mgmt_enable_policy, __BSS_MGMT_EN_MAX, tb, blob_data(msg), blob_len(msg));
+
+	for (i = 0; i < ARRAY_SIZE(tb); i++) {
+		if (!tb[i] || !blobmsg_get_bool(tb[i]))
+			continue;
+
+		flags |= (1 << i);
+	}
+
+	__hostapd_bss_mgmt_enable(hapd, flags);
+
+	return 0;
+}
+
+
+static void
+hostapd_rrm_nr_enable(struct hostapd_data *hapd)
+{
+	__hostapd_bss_mgmt_enable(hapd, 1 << BSS_MGMT_EN_NEIGHBOR);
+}
+
+static int
+hostapd_rrm_nr_get_own(struct ubus_context *ctx, struct ubus_object *obj,
+		       struct ubus_request_data *req, const char *method,
+		       struct blob_attr *msg)
+{
+	struct hostapd_data *hapd = get_hapd_from_object(obj);
+	struct hostapd_neighbor_entry *nr;
+	void *c;
+
+	hostapd_rrm_nr_enable(hapd);
+
+	nr = hostapd_neighbor_get(hapd, hapd->own_addr, NULL);
+	if (!nr)
+		return UBUS_STATUS_NOT_FOUND;
+
+	blob_buf_init(&b, 0);
+
+	c = blobmsg_open_array(&b, "value");
+	hostapd_rrm_print_nr(nr);
+	blobmsg_close_array(&b, c);
+
+	ubus_send_reply(ctx, req, b.head);
+
+	return 0;
+}
+
+static int
+hostapd_rrm_nr_list(struct ubus_context *ctx, struct ubus_object *obj,
+		    struct ubus_request_data *req, const char *method,
+		    struct blob_attr *msg)
+{
+	struct hostapd_data *hapd = get_hapd_from_object(obj);
+	struct hostapd_neighbor_entry *nr;
+	void *c;
+
+	hostapd_rrm_nr_enable(hapd);
+	blob_buf_init(&b, 0);
+
+	c = blobmsg_open_array(&b, "list");
+	dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry, list) {
+		void *cur;
+
+		if (!memcmp(nr->bssid, hapd->own_addr, ETH_ALEN))
+			continue;
+
+		cur = blobmsg_open_array(&b, NULL);
+		hostapd_rrm_print_nr(nr);
+		blobmsg_close_array(&b, cur);
+	}
+	blobmsg_close_array(&b, c);
+
+	ubus_send_reply(ctx, req, b.head);
+
+	return 0;
+}
+
+enum {
+	NR_SET_LIST,
+	__NR_SET_LIST_MAX
+};
+
+static const struct blobmsg_policy nr_set_policy[__NR_SET_LIST_MAX] = {
+	[NR_SET_LIST] = { "list", BLOBMSG_TYPE_ARRAY },
+};
+
+
+static void
+hostapd_rrm_nr_clear(struct hostapd_data *hapd)
+{
+	struct hostapd_neighbor_entry *nr;
+
+restart:
+	dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry, list) {
+		if (!memcmp(nr->bssid, hapd->own_addr, ETH_ALEN))
+			continue;
+
+		hostapd_neighbor_remove(hapd, nr->bssid, &nr->ssid);
+		goto restart;
+	}
+}
+
+static int
+hostapd_rrm_nr_set(struct ubus_context *ctx, struct ubus_object *obj,
+		   struct ubus_request_data *req, const char *method,
+		   struct blob_attr *msg)
+{
+	static const struct blobmsg_policy nr_e_policy[] = {
+		{ .type = BLOBMSG_TYPE_STRING },
+		{ .type = BLOBMSG_TYPE_STRING },
+		{ .type = BLOBMSG_TYPE_STRING },
+	};
+	struct hostapd_data *hapd = get_hapd_from_object(obj);
+	struct blob_attr *tb_l[__NR_SET_LIST_MAX];
+	struct blob_attr *tb[ARRAY_SIZE(nr_e_policy)];
+	struct blob_attr *cur;
+	int rem;
+
+	hostapd_rrm_nr_enable(hapd);
+
+	blobmsg_parse(nr_set_policy, __NR_SET_LIST_MAX, tb_l, blob_data(msg), blob_len(msg));
+	if (!tb_l[NR_SET_LIST])
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	hostapd_rrm_nr_clear(hapd);
+	blobmsg_for_each_attr(cur, tb_l[NR_SET_LIST], rem) {
+		struct wpa_ssid_value ssid;
+		struct wpabuf *data;
+		u8 bssid[ETH_ALEN];
+		char *s, *nr_s;
+
+		blobmsg_parse_array(nr_e_policy, ARRAY_SIZE(nr_e_policy), tb, blobmsg_data(cur), blobmsg_data_len(cur));
+		if (!tb[0] || !tb[1] || !tb[2])
+			goto invalid;
+
+		/* Neighbor Report binary */
+		nr_s = blobmsg_get_string(tb[2]);
+		data = wpabuf_parse_bin(nr_s);
+		if (!data)
+			goto invalid;
+
+		/* BSSID */
+		s = blobmsg_get_string(tb[0]);
+		if (strlen(s) == 0) {
+			/* Copy BSSID from neighbor report */
+			if (hwaddr_compact_aton(nr_s, bssid))
+				goto invalid;
+		} else if (hwaddr_aton(s, bssid)) {
+			goto invalid;
+		}
+
+		/* SSID */
+		s = blobmsg_get_string(tb[1]);
+		if (strlen(s) == 0) {
+			/* Copy SSID from hostapd BSS conf */
+			memcpy(&ssid, &hapd->conf->ssid, sizeof(ssid));
+		} else {
+			ssid.ssid_len = strlen(s);
+			if (ssid.ssid_len > sizeof(ssid.ssid))
+				goto invalid;
+
+			memcpy(&ssid, s, ssid.ssid_len);
+		}
+
+		hostapd_neighbor_set(hapd, bssid, &ssid, data, NULL, NULL, 0, 0);
+		wpabuf_free(data);
+		continue;
+
+invalid:
+		return UBUS_STATUS_INVALID_ARGUMENT;
+	}
+
+	return 0;
+}
+
+enum {
+	BEACON_REQ_ADDR,
+	BEACON_REQ_MODE,
+	BEACON_REQ_OP_CLASS,
+	BEACON_REQ_CHANNEL,
+	BEACON_REQ_DURATION,
+	BEACON_REQ_BSSID,
+	BEACON_REQ_SSID,
+	__BEACON_REQ_MAX,
+};
+
+static const struct blobmsg_policy beacon_req_policy[__BEACON_REQ_MAX] = {
+	[BEACON_REQ_ADDR] = { "addr", BLOBMSG_TYPE_STRING },
+	[BEACON_REQ_OP_CLASS] { "op_class", BLOBMSG_TYPE_INT32 },
+	[BEACON_REQ_CHANNEL] { "channel", BLOBMSG_TYPE_INT32 },
+	[BEACON_REQ_DURATION] { "duration", BLOBMSG_TYPE_INT32 },
+	[BEACON_REQ_MODE] { "mode", BLOBMSG_TYPE_INT32 },
+	[BEACON_REQ_BSSID] { "bssid", BLOBMSG_TYPE_STRING },
+	[BEACON_REQ_SSID] { "ssid", BLOBMSG_TYPE_STRING },
+};
+
+static int
+hostapd_rrm_beacon_req(struct ubus_context *ctx, struct ubus_object *obj,
+		       struct ubus_request_data *ureq, const char *method,
+		       struct blob_attr *msg)
+{
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+	struct blob_attr *tb[__BEACON_REQ_MAX];
+	struct blob_attr *cur;
+	struct wpabuf *req;
+	u8 bssid[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+	u8 addr[ETH_ALEN];
+	int mode, rem, ret;
+	int buf_len = 13;
+
+	blobmsg_parse(beacon_req_policy, __BEACON_REQ_MAX, tb, blob_data(msg), blob_len(msg));
+
+	if (!tb[BEACON_REQ_ADDR] || !tb[BEACON_REQ_MODE] || !tb[BEACON_REQ_DURATION] ||
+	    !tb[BEACON_REQ_OP_CLASS] || !tb[BEACON_REQ_CHANNEL])
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	if (tb[BEACON_REQ_SSID])
+		buf_len += blobmsg_data_len(tb[BEACON_REQ_SSID]) + 2 - 1;
+
+	mode = blobmsg_get_u32(tb[BEACON_REQ_MODE]);
+	if (hwaddr_aton(blobmsg_data(tb[BEACON_REQ_ADDR]), addr))
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	if (tb[BEACON_REQ_BSSID] &&
+	    hwaddr_aton(blobmsg_data(tb[BEACON_REQ_BSSID]), bssid))
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	req = wpabuf_alloc(buf_len);
+	if (!req)
+		return UBUS_STATUS_UNKNOWN_ERROR;
+
+	/* 1: regulatory class */
+	wpabuf_put_u8(req, blobmsg_get_u32(tb[BEACON_REQ_OP_CLASS]));
+
+	/* 2: channel number */
+	wpabuf_put_u8(req, blobmsg_get_u32(tb[BEACON_REQ_CHANNEL]));
+
+	/* 3-4: randomization interval */
+	wpabuf_put_le16(req, 0);
+
+	/* 5-6: duration */
+	wpabuf_put_le16(req, blobmsg_get_u32(tb[BEACON_REQ_DURATION]));
+
+	/* 7: mode */
+	wpabuf_put_u8(req, blobmsg_get_u32(tb[BEACON_REQ_MODE]));
+
+	/* 8-13: BSSID */
+	wpabuf_put_data(req, bssid, ETH_ALEN);
+
+	if ((cur = tb[BEACON_REQ_SSID]) != NULL) {
+		wpabuf_put_u8(req, WLAN_EID_SSID);
+		wpabuf_put_u8(req, blobmsg_data_len(cur) - 1);
+		wpabuf_put_data(req, blobmsg_data(cur), blobmsg_data_len(cur) - 1);
+	}
+
+	ret = hostapd_send_beacon_req(hapd, addr, 0, req);
+	if (ret < 0)
+		return -ret;
+
+	return 0;
+}
+
+enum {
+	LM_REQ_ADDR,
+	LM_REQ_TX_POWER_USED,
+	LM_REQ_TX_POWER_MAX,
+	__LM_REQ_MAX,
+};
+
+static const struct blobmsg_policy lm_req_policy[__LM_REQ_MAX] = {
+	[LM_REQ_ADDR] = { "addr", BLOBMSG_TYPE_STRING },
+	[LM_REQ_TX_POWER_USED] = { "tx-power-used", BLOBMSG_TYPE_INT32 },
+	[LM_REQ_TX_POWER_MAX] = { "tx-power-max", BLOBMSG_TYPE_INT32 },
+};
+
+static int
+hostapd_rrm_lm_req(struct ubus_context *ctx, struct ubus_object *obj,
+		   struct ubus_request_data *ureq, const char *method,
+		   struct blob_attr *msg)
+{
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+	struct blob_attr *tb[__LM_REQ_MAX];
+	struct wpabuf *buf;
+	u8 addr[ETH_ALEN];
+	int ret;
+	int8_t txp_used, txp_max;
+
+	txp_used = 0;
+	txp_max = 0;
+
+	blobmsg_parse(lm_req_policy, __LM_REQ_MAX, tb, blob_data(msg), blob_len(msg));
+
+	if (!tb[LM_REQ_ADDR])
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	if (tb[LM_REQ_TX_POWER_USED])
+		txp_used = (int8_t) blobmsg_get_u32(tb[LM_REQ_TX_POWER_USED]);
+
+	if (tb[LM_REQ_TX_POWER_MAX])
+		txp_max = (int8_t) blobmsg_get_u32(tb[LM_REQ_TX_POWER_MAX]);
+
+	if (hwaddr_aton(blobmsg_data(tb[LM_REQ_ADDR]), addr))
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	buf = wpabuf_alloc(5);
+	if (!buf)
+		return UBUS_STATUS_UNKNOWN_ERROR;
+
+	wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
+	wpabuf_put_u8(buf, WLAN_RRM_LINK_MEASUREMENT_REQUEST);
+	wpabuf_put_u8(buf, 1);
+	/* TX-Power used */
+	wpabuf_put_u8(buf, txp_used);
+	/* Max TX Power */
+	wpabuf_put_u8(buf, txp_max);
+
+	ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+				      wpabuf_head(buf), wpabuf_len(buf));
+
+	wpabuf_free(buf);
+	if (ret < 0)
+		return -ret;
+
+	return 0;
+}
+
+
+void hostapd_ubus_handle_link_measurement(struct hostapd_data *hapd, const u8 *data, size_t len)
+{
+	const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) data;
+	const u8 *pos, *end;
+	u8 token;
+
+	end = data + len;
+	token = mgmt->u.action.u.rrm.dialog_token;
+	pos = mgmt->u.action.u.rrm.variable;
+
+	if (end - pos < 8)
+		return;
+
+	if (!hapd->ubus.obj.has_subscribers)
+		return;
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_macaddr(&b, "address", mgmt->sa);
+	blobmsg_add_u16(&b, "dialog-token", token);
+	blobmsg_add_u16(&b, "rx-antenna-id", pos[4]);
+	blobmsg_add_u16(&b, "tx-antenna-id", pos[5]);
+	blobmsg_add_u16(&b, "rcpi", pos[6]);
+	blobmsg_add_u16(&b, "rsni", pos[7]);
+
+	ubus_notify(ctx, &hapd->ubus.obj, "link-measurement-report", b.head, -1);
+}
+
+
+#ifdef CONFIG_WNM_AP
+
+static int
+hostapd_bss_tr_send(struct hostapd_data *hapd, u8 *addr, bool disassoc_imminent, bool abridged,
+		    u16 disassoc_timer, u8 validity_period, u8 dialog_token,
+		    struct blob_attr *neighbors, u8 mbo_reason, u8 cell_pref, u8 reassoc_delay)
+{
+	struct blob_attr *cur;
+	struct sta_info *sta;
+	int nr_len = 0;
+	int rem;
+	u8 *nr = NULL;
+	u8 req_mode = 0;
+	u8 mbo[10];
+	size_t mbo_len = 0;
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta)
+		return UBUS_STATUS_NOT_FOUND;
+
+	if (neighbors) {
+		u8 *nr_cur;
+
+		if (blobmsg_check_array(neighbors,
+					BLOBMSG_TYPE_STRING) < 0)
+			return UBUS_STATUS_INVALID_ARGUMENT;
+
+		blobmsg_for_each_attr(cur, neighbors, rem) {
+			int len = strlen(blobmsg_get_string(cur));
+
+			if (len % 2)
+				return UBUS_STATUS_INVALID_ARGUMENT;
+
+			nr_len += (len / 2) + 2;
+		}
+
+		if (nr_len) {
+			nr = os_zalloc(nr_len);
+			if (!nr)
+				return UBUS_STATUS_UNKNOWN_ERROR;
+		}
+
+		nr_cur = nr;
+		blobmsg_for_each_attr(cur, neighbors, rem) {
+			int len = strlen(blobmsg_get_string(cur)) / 2;
+
+			*nr_cur++ = WLAN_EID_NEIGHBOR_REPORT;
+			*nr_cur++ = (u8) len;
+			if (hexstr2bin(blobmsg_data(cur), nr_cur, len)) {
+				free(nr);
+				return UBUS_STATUS_INVALID_ARGUMENT;
+			}
+
+			nr_cur += len;
+		}
+	}
+
+	if (nr)
+		req_mode |= WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED;
+
+	if (abridged)
+		req_mode |= WNM_BSS_TM_REQ_ABRIDGED;
+
+	if (disassoc_imminent)
+		req_mode |= WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
+
+#ifdef CONFIG_MBO
+	u8 *mbo_pos = mbo;
+
+	if (mbo_reason > MBO_TRANSITION_REASON_PREMIUM_AP)
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	if (cell_pref != 0 && cell_pref != 1 && cell_pref != 255)
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	if (reassoc_delay > 65535 || (reassoc_delay && !disassoc_imminent))
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	*mbo_pos++ = MBO_ATTR_ID_TRANSITION_REASON;
+	*mbo_pos++ = 1;
+	*mbo_pos++ = mbo_reason;
+	*mbo_pos++ = MBO_ATTR_ID_CELL_DATA_PREF;
+	*mbo_pos++ = 1;
+	*mbo_pos++ = cell_pref;
+
+	if (reassoc_delay) {
+		*mbo_pos++ = MBO_ATTR_ID_ASSOC_RETRY_DELAY;
+		*mbo_pos++ = 2;
+		WPA_PUT_LE16(mbo_pos, reassoc_delay);
+		mbo_pos += 2;
+	}
+
+	mbo_len = mbo_pos - mbo;
+#endif
+
+	if (wnm_send_bss_tm_req(hapd, sta, req_mode, disassoc_timer, validity_period, NULL,
+				dialog_token, NULL, nr, nr_len, mbo_len ? mbo : NULL, mbo_len))
+		return UBUS_STATUS_UNKNOWN_ERROR;
+
+	return 0;
+}
+
+enum {
+	BSS_TR_ADDR,
+	BSS_TR_DA_IMMINENT,
+	BSS_TR_DA_TIMER,
+	BSS_TR_VALID_PERIOD,
+	BSS_TR_NEIGHBORS,
+	BSS_TR_ABRIDGED,
+	BSS_TR_DIALOG_TOKEN,
+#ifdef CONFIG_MBO
+	BSS_TR_MBO_REASON,
+	BSS_TR_CELL_PREF,
+	BSS_TR_REASSOC_DELAY,
+#endif
+	__BSS_TR_DISASSOC_MAX
+};
+
+static const struct blobmsg_policy bss_tr_policy[__BSS_TR_DISASSOC_MAX] = {
+	[BSS_TR_ADDR] = { "addr", BLOBMSG_TYPE_STRING },
+	[BSS_TR_DA_IMMINENT] = { "disassociation_imminent", BLOBMSG_TYPE_BOOL },
+	[BSS_TR_DA_TIMER] = { "disassociation_timer", BLOBMSG_TYPE_INT32 },
+	[BSS_TR_VALID_PERIOD] = { "validity_period", BLOBMSG_TYPE_INT32 },
+	[BSS_TR_NEIGHBORS] = { "neighbors", BLOBMSG_TYPE_ARRAY },
+	[BSS_TR_ABRIDGED] = { "abridged", BLOBMSG_TYPE_BOOL },
+	[BSS_TR_DIALOG_TOKEN] = { "dialog_token", BLOBMSG_TYPE_INT32 },
+#ifdef CONFIG_MBO
+	[BSS_TR_MBO_REASON] = { "mbo_reason", BLOBMSG_TYPE_INT32 },
+	[BSS_TR_CELL_PREF] = { "cell_pref", BLOBMSG_TYPE_INT32 },
+	[BSS_TR_REASSOC_DELAY] = { "reassoc_delay", BLOBMSG_TYPE_INT32 },
+#endif
+};
+
+static int
+hostapd_bss_transition_request(struct ubus_context *ctx, struct ubus_object *obj,
+			       struct ubus_request_data *ureq, const char *method,
+			       struct blob_attr *msg)
+{
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+	struct blob_attr *tb[__BSS_TR_DISASSOC_MAX];
+	struct sta_info *sta;
+	u32 da_timer = 0;
+	u32 valid_period = 0;
+	u8 addr[ETH_ALEN];
+	u32 dialog_token = 1;
+	bool abridged;
+	bool da_imminent;
+	u8 mbo_reason;
+	u8 cell_pref;
+	u8 reassoc_delay;
+
+	blobmsg_parse(bss_tr_policy, __BSS_TR_DISASSOC_MAX, tb, blob_data(msg), blob_len(msg));
+
+	if (!tb[BSS_TR_ADDR])
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	if (hwaddr_aton(blobmsg_data(tb[BSS_TR_ADDR]), addr))
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	if (tb[BSS_TR_DA_TIMER])
+		da_timer = blobmsg_get_u32(tb[BSS_TR_DA_TIMER]);
+
+	if (tb[BSS_TR_VALID_PERIOD])
+		valid_period = blobmsg_get_u32(tb[BSS_TR_VALID_PERIOD]);
+
+	if (tb[BSS_TR_DIALOG_TOKEN])
+		dialog_token = blobmsg_get_u32(tb[BSS_TR_DIALOG_TOKEN]);
+
+	da_imminent = !!(tb[BSS_TR_DA_IMMINENT] && blobmsg_get_bool(tb[BSS_TR_DA_IMMINENT]));
+	abridged = !!(tb[BSS_TR_ABRIDGED] && blobmsg_get_bool(tb[BSS_TR_ABRIDGED]));
+
+#ifdef CONFIG_MBO
+	if (tb[BSS_TR_MBO_REASON])
+		mbo_reason = blobmsg_get_u32(tb[BSS_TR_MBO_REASON]);
+
+	if (tb[BSS_TR_CELL_PREF])
+		cell_pref = blobmsg_get_u32(tb[BSS_TR_CELL_PREF]);
+
+	if (tb[BSS_TR_REASSOC_DELAY])
+		reassoc_delay = blobmsg_get_u32(tb[BSS_TR_REASSOC_DELAY]);
+#endif
+
+	return hostapd_bss_tr_send(hapd, addr, da_imminent, abridged, da_timer, valid_period,
+				   dialog_token, tb[BSS_TR_NEIGHBORS], mbo_reason, cell_pref, reassoc_delay);
+}
+#endif
+
+#ifdef CONFIG_AIRTIME_POLICY
+enum {
+	UPDATE_AIRTIME_STA,
+	UPDATE_AIRTIME_WEIGHT,
+	__UPDATE_AIRTIME_MAX,
+};
+
+
+static const struct blobmsg_policy airtime_policy[__UPDATE_AIRTIME_MAX] = {
+	[UPDATE_AIRTIME_STA] = { "sta", BLOBMSG_TYPE_STRING },
+	[UPDATE_AIRTIME_WEIGHT] = { "weight", BLOBMSG_TYPE_INT32 },
+};
+
+static int
+hostapd_bss_update_airtime(struct ubus_context *ctx, struct ubus_object *obj,
+			   struct ubus_request_data *ureq, const char *method,
+			   struct blob_attr *msg)
+{
+	struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+	struct blob_attr *tb[__UPDATE_AIRTIME_MAX];
+	struct sta_info *sta = NULL;
+	u8 addr[ETH_ALEN];
+	int weight;
+
+	blobmsg_parse(airtime_policy, __UPDATE_AIRTIME_MAX, tb, blob_data(msg), blob_len(msg));
+
+	if (!tb[UPDATE_AIRTIME_WEIGHT])
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	weight = blobmsg_get_u32(tb[UPDATE_AIRTIME_WEIGHT]);
+
+	if (!tb[UPDATE_AIRTIME_STA]) {
+		if (!weight)
+			return UBUS_STATUS_INVALID_ARGUMENT;
+
+		hapd->conf->airtime_weight = weight;
+		return 0;
+	}
+
+	if (hwaddr_aton(blobmsg_data(tb[UPDATE_AIRTIME_STA]), addr))
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta)
+		return UBUS_STATUS_NOT_FOUND;
+
+	sta->dyn_airtime_weight = weight;
+	airtime_policy_new_sta(hapd, sta);
+
+	return 0;
+}
+#endif
+
+
+static const struct ubus_method bss_methods[] = {
+	UBUS_METHOD_NOARG("reload", hostapd_bss_reload),
+	UBUS_METHOD_NOARG("get_clients", hostapd_bss_get_clients),
+	UBUS_METHOD_NOARG("get_status", hostapd_bss_get_status),
+	UBUS_METHOD("del_client", hostapd_bss_del_client, del_policy),
+#ifdef CONFIG_AIRTIME_POLICY
+	UBUS_METHOD("update_airtime", hostapd_bss_update_airtime, airtime_policy),
+#endif
+	UBUS_METHOD_NOARG("list_bans", hostapd_bss_list_bans),
+#ifdef CONFIG_WPS
+	UBUS_METHOD_NOARG("wps_start", hostapd_bss_wps_start),
+	UBUS_METHOD_NOARG("wps_status", hostapd_bss_wps_status),
+	UBUS_METHOD_NOARG("wps_cancel", hostapd_bss_wps_cancel),
+#endif
+	UBUS_METHOD_NOARG("update_beacon", hostapd_bss_update_beacon),
+	UBUS_METHOD_NOARG("get_features", hostapd_bss_get_features),
+#ifdef NEED_AP_MLME
+	UBUS_METHOD("switch_chan", hostapd_switch_chan, csa_policy),
+#endif
+	UBUS_METHOD("set_vendor_elements", hostapd_vendor_elements, ve_policy),
+	UBUS_METHOD("notify_response", hostapd_notify_response, notify_policy),
+	UBUS_METHOD("bss_mgmt_enable", hostapd_bss_mgmt_enable, bss_mgmt_enable_policy),
+	UBUS_METHOD_NOARG("rrm_nr_get_own", hostapd_rrm_nr_get_own),
+	UBUS_METHOD_NOARG("rrm_nr_list", hostapd_rrm_nr_list),
+	UBUS_METHOD("rrm_nr_set", hostapd_rrm_nr_set, nr_set_policy),
+	UBUS_METHOD("rrm_beacon_req", hostapd_rrm_beacon_req, beacon_req_policy),
+	UBUS_METHOD("link_measurement_req", hostapd_rrm_lm_req, lm_req_policy),
+#ifdef CONFIG_WNM_AP
+	UBUS_METHOD("bss_transition_request", hostapd_bss_transition_request, bss_tr_policy),
+#endif
+};
+
+static struct ubus_object_type bss_object_type =
+	UBUS_OBJECT_TYPE("hostapd_bss", bss_methods);
+
+static int avl_compare_macaddr(const void *k1, const void *k2, void *ptr)
+{
+	return memcmp(k1, k2, ETH_ALEN);
+}
+
+void hostapd_ubus_add_bss(struct hostapd_data *hapd)
+{
+	struct ubus_object *obj = &hapd->ubus.obj;
+	char *name;
+	int ret;
+
+#ifdef CONFIG_MESH
+	if (hapd->conf->mesh & MESH_ENABLED)
+		return;
+#endif
+
+	if (!hostapd_ubus_init())
+		return;
+
+	if (asprintf(&name, "hostapd.%s", hapd->conf->iface) < 0)
+		return;
+
+	avl_init(&hapd->ubus.banned, avl_compare_macaddr, false, NULL);
+	obj->name = name;
+	obj->type = &bss_object_type;
+	obj->methods = bss_object_type.methods;
+	obj->n_methods = bss_object_type.n_methods;
+	ret = ubus_add_object(ctx, obj);
+	hostapd_ubus_ref_inc();
+
+	hostapd_send_shared_event(&hapd->iface->interfaces->ubus, hapd->conf->iface, "add");
+}
+
+void hostapd_ubus_free_bss(struct hostapd_data *hapd)
+{
+	struct ubus_object *obj = &hapd->ubus.obj;
+	char *name = (char *) obj->name;
+
+#ifdef CONFIG_MESH
+	if (hapd->conf->mesh & MESH_ENABLED)
+		return;
+#endif
+
+	if (!ctx)
+		return;
+
+	hostapd_send_shared_event(&hapd->iface->interfaces->ubus, hapd->conf->iface, "remove");
+
+	if (obj->id) {
+		ubus_remove_object(ctx, obj);
+		hostapd_ubus_ref_dec();
+	}
+
+	free(name);
+}
+
+static void
+hostapd_ubus_vlan_action(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
+			 const char *action)
+{
+	struct vlan_description *desc = &vlan->vlan_desc;
+	void *c;
+	int i;
+
+	if (!hapd->ubus.obj.has_subscribers)
+		return;
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_string(&b, "ifname", vlan->ifname);
+	blobmsg_add_string(&b, "bridge", vlan->bridge);
+	blobmsg_add_u32(&b, "vlan_id", vlan->vlan_id);
+
+	if (desc->notempty) {
+		blobmsg_add_u32(&b, "untagged", desc->untagged);
+		c = blobmsg_open_array(&b, "tagged");
+		for (i = 0; i < ARRAY_SIZE(desc->tagged) && desc->tagged[i]; i++)
+			blobmsg_add_u32(&b, "", desc->tagged[i]);
+		blobmsg_close_array(&b, c);
+	}
+
+	ubus_notify(ctx, &hapd->ubus.obj, action, b.head, -1);
+}
+
+void hostapd_ubus_add_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
+{
+	hostapd_ubus_vlan_action(hapd, vlan, "vlan_add");
+}
+
+void hostapd_ubus_remove_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
+{
+	hostapd_ubus_vlan_action(hapd, vlan, "vlan_remove");
+}
+
+static const struct ubus_method daemon_methods[] = {
+	UBUS_METHOD("config_add", hostapd_config_add, config_add_policy),
+	UBUS_METHOD("config_remove", hostapd_config_remove, config_remove_policy),
+};
+
+static struct ubus_object_type daemon_object_type =
+	UBUS_OBJECT_TYPE("hostapd", daemon_methods);
+
+void hostapd_ubus_add(struct hapd_interfaces *interfaces)
+{
+	struct ubus_object *obj = &interfaces->ubus;
+	int ret;
+
+	if (!hostapd_ubus_init())
+		return;
+
+	obj->name = strdup("hostapd");
+
+	obj->type = &daemon_object_type;
+	obj->methods = daemon_object_type.methods;
+	obj->n_methods = daemon_object_type.n_methods;
+	ret = ubus_add_object(ctx, obj);
+	hostapd_ubus_ref_inc();
+}
+
+void hostapd_ubus_free(struct hapd_interfaces *interfaces)
+{
+	struct ubus_object *obj = &interfaces->ubus;
+	char *name = (char *) obj->name;
+
+	if (!ctx)
+		return;
+
+	if (obj->id) {
+		ubus_remove_object(ctx, obj);
+		hostapd_ubus_ref_dec();
+	}
+
+	free(name);
+}
+
+struct ubus_event_req {
+	struct ubus_notify_request nreq;
+	int resp;
+};
+
+static void
+ubus_event_cb(struct ubus_notify_request *req, int idx, int ret)
+{
+	struct ubus_event_req *ureq = container_of(req, struct ubus_event_req, nreq);
+
+	ureq->resp = ret;
+}
+
+static void hostapd_ubus_event_log(const char *fmt, ...)
+{
+	FILE *kmsg;
+	va_list ap;
+
+	va_start(ap, fmt);
+	if ((kmsg = fopen("/dev/kmsg", "r+")) != NULL) {
+		vfprintf(kmsg, fmt, ap);
+		fclose(kmsg);
+	}
+	va_end(ap);
+}
+
+int hostapd_ubus_handle_event(struct hostapd_data *hapd, struct hostapd_ubus_request *req)
+{
+	struct ubus_banned_client *ban;
+	const char *types[HOSTAPD_UBUS_TYPE_MAX] = {
+		[HOSTAPD_UBUS_PROBE_REQ] = "probe",
+		[HOSTAPD_UBUS_AUTH_REQ] = "auth",
+		[HOSTAPD_UBUS_ASSOC_REQ] = "assoc",
+		[HOSTAPD_UBUS_CONNECTED_REQ] = "connected",
+		[HOSTAPD_UBUS_DISCONNECTED_REQ] = "disconnected",
+		[HOSTAPD_UBUS_WPS_START] = "WPS-START",
+		[HOSTAPD_UBUS_WPS_FAIL] = "WPS-FAIL",
+		[HOSTAPD_UBUS_WPS_SUCCESS] = "WPS-SUCCESS",
+	};
+	const char *type = "mgmt";
+	struct ubus_event_req ureq = {};
+	const u8 *addr;
+
+	/* ignore first three events */
+	if (req->type <= HOSTAPD_UBUS_ASSOC_REQ)
+		return 0;
+
+	hostapd_ubus_event_log("STA " MACSTR " %s\n", MAC2STR(req->addr),
+				types[req->type]);
+
+	if (req->mgmt_frame)
+		addr = req->mgmt_frame->sa;
+	else
+		addr = req->addr;
+
+	ban = avl_find_element(&hapd->ubus.banned, addr, ban, avl);
+	if (ban)
+		return WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+
+	if (!hapd->ubus.obj.has_subscribers)
+		return WLAN_STATUS_SUCCESS;
+
+	if (req->type < ARRAY_SIZE(types))
+		type = types[req->type];
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_macaddr(&b, "address", addr);
+	if (req->mgmt_frame)
+		blobmsg_add_macaddr(&b, "target", req->mgmt_frame->da);
+	if (req->ssi_signal)
+		blobmsg_add_u32(&b, "signal", req->ssi_signal);
+	blobmsg_add_u32(&b, "freq", hapd->iface->freq);
+
+	if (req->elems) {
+		if(req->elems->ht_capabilities)
+		{
+			struct ieee80211_ht_capabilities *ht_capabilities;
+			void *ht_cap, *ht_cap_mcs_set, *mcs_set;
+
+
+			ht_capabilities = (struct ieee80211_ht_capabilities*) req->elems->ht_capabilities;
+			ht_cap = blobmsg_open_table(&b, "ht_capabilities");
+			blobmsg_add_u16(&b, "ht_capabilities_info", ht_capabilities->ht_capabilities_info);
+			ht_cap_mcs_set = blobmsg_open_table(&b, "supported_mcs_set");
+			blobmsg_add_u16(&b, "a_mpdu_params", ht_capabilities->a_mpdu_params);
+			blobmsg_add_u16(&b, "ht_extended_capabilities", ht_capabilities->ht_extended_capabilities);
+			blobmsg_add_u32(&b, "tx_bf_capability_info", ht_capabilities->tx_bf_capability_info);
+			blobmsg_add_u16(&b, "asel_capabilities", ht_capabilities->asel_capabilities);
+			mcs_set = blobmsg_open_array(&b, "supported_mcs_set");
+			for (int i = 0; i < 16; i++) {
+				blobmsg_add_u16(&b, NULL, (u16) ht_capabilities->supported_mcs_set[i]);
+			}
+			blobmsg_close_array(&b, mcs_set);
+			blobmsg_close_table(&b, ht_cap_mcs_set);
+			blobmsg_close_table(&b, ht_cap);
+		}
+		if(req->elems->vht_capabilities)
+		{
+			struct ieee80211_vht_capabilities *vht_capabilities;
+			void *vht_cap, *vht_cap_mcs_set;
+
+			vht_capabilities = (struct ieee80211_vht_capabilities*) req->elems->vht_capabilities;
+			vht_cap = blobmsg_open_table(&b, "vht_capabilities");
+			blobmsg_add_u32(&b, "vht_capabilities_info", vht_capabilities->vht_capabilities_info);
+			vht_cap_mcs_set = blobmsg_open_table(&b, "vht_supported_mcs_set");
+			blobmsg_add_u16(&b, "rx_map", vht_capabilities->vht_supported_mcs_set.rx_map);
+			blobmsg_add_u16(&b, "rx_highest", vht_capabilities->vht_supported_mcs_set.rx_highest);
+			blobmsg_add_u16(&b, "tx_map", vht_capabilities->vht_supported_mcs_set.tx_map);
+			blobmsg_add_u16(&b, "tx_highest", vht_capabilities->vht_supported_mcs_set.tx_highest);
+			blobmsg_close_table(&b, vht_cap_mcs_set);
+			blobmsg_close_table(&b, vht_cap);
+		}
+	}
+
+	if (!hapd->ubus.notify_response) {
+		ubus_notify(ctx, &hapd->ubus.obj, type, b.head, -1);
+		return WLAN_STATUS_SUCCESS;
+	}
+
+	if (ubus_notify_async(ctx, &hapd->ubus.obj, type, b.head, &ureq.nreq))
+		return WLAN_STATUS_SUCCESS;
+
+	ureq.nreq.status_cb = ubus_event_cb;
+	ubus_complete_request(ctx, &ureq.nreq.req, 100);
+
+	if (ureq.resp)
+		return ureq.resp;
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+void hostapd_ubus_notify(struct hostapd_data *hapd, const char *type, const u8 *addr)
+{
+	if (!hapd->ubus.obj.has_subscribers)
+		return;
+
+	if (!addr)
+		return;
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_macaddr(&b, "address", addr);
+
+	ubus_notify(ctx, &hapd->ubus.obj, type, b.head, -1);
+}
+
+void hostapd_ubus_notify_authorized(struct hostapd_data *hapd, struct sta_info *sta,
+				    const char *auth_alg)
+{
+	if (!hapd->ubus.obj.has_subscribers)
+		return;
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_macaddr(&b, "address", sta->addr);
+	if (auth_alg)
+		blobmsg_add_string(&b, "auth-alg", auth_alg);
+
+	ubus_notify(ctx, &hapd->ubus.obj, "sta-authorized", b.head, -1);
+}
+
+void hostapd_ubus_notify_beacon_report(
+	struct hostapd_data *hapd, const u8 *addr, u8 token, u8 rep_mode,
+	struct rrm_measurement_beacon_report *rep, size_t len)
+{
+	if (!hapd->ubus.obj.has_subscribers)
+		return;
+
+	if (!addr || !rep)
+		return;
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_macaddr(&b, "address", addr);
+	blobmsg_add_u16(&b, "op-class", rep->op_class);
+	blobmsg_add_u16(&b, "channel", rep->channel);
+	blobmsg_add_u64(&b, "start-time", rep->start_time);
+	blobmsg_add_u16(&b, "duration", rep->duration);
+	blobmsg_add_u16(&b, "report-info", rep->report_info);
+	blobmsg_add_u16(&b, "rcpi", rep->rcpi);
+	blobmsg_add_u16(&b, "rsni", rep->rsni);
+	blobmsg_add_macaddr(&b, "bssid", rep->bssid);
+	blobmsg_add_u16(&b, "antenna-id", rep->antenna_id);
+	blobmsg_add_u16(&b, "parent-tsf", rep->parent_tsf);
+	blobmsg_add_u16(&b, "rep-mode", rep_mode);
+
+	ubus_notify(ctx, &hapd->ubus.obj, "beacon-report", b.head, -1);
+}
+
+void hostapd_ubus_notify_radar_detected(struct hostapd_iface *iface, int frequency,
+					int chan_width, int cf1, int cf2)
+{
+	struct hostapd_data *hapd;
+	int i;
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_u16(&b, "frequency", frequency);
+	blobmsg_add_u16(&b, "width", chan_width);
+	blobmsg_add_u16(&b, "center1", cf1);
+	blobmsg_add_u16(&b, "center2", cf2);
+
+	for (i = 0; i < iface->num_bss; i++) {
+		hapd = iface->bss[i];
+		ubus_notify(ctx, &hapd->ubus.obj, "radar-detected", b.head, -1);
+	}
+}
+
+#ifdef CONFIG_WNM_AP
+static void hostapd_ubus_notify_bss_transition_add_candidate_list(
+	const u8 *candidate_list, u16 candidate_list_len)
+{
+	char *cl_str;
+	int i;
+
+	if (candidate_list_len == 0)
+		return;
+
+	cl_str = blobmsg_alloc_string_buffer(&b, "candidate-list", candidate_list_len * 2 + 1);
+	for (i = 0; i < candidate_list_len; i++)
+		snprintf(&cl_str[i*2], 3, "%02X", candidate_list[i]);
+	blobmsg_add_string_buffer(&b);
+
+}
+#endif
+
+void hostapd_ubus_notify_bss_transition_response(
+	struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 status_code,
+	u8 bss_termination_delay, const u8 *target_bssid,
+	const u8 *candidate_list, u16 candidate_list_len)
+{
+#ifdef CONFIG_WNM_AP
+	u16 i;
+
+	if (!hapd->ubus.obj.has_subscribers)
+		return;
+
+	if (!addr)
+		return;
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_macaddr(&b, "address", addr);
+	blobmsg_add_u8(&b, "dialog-token", dialog_token);
+	blobmsg_add_u8(&b, "status-code", status_code);
+	blobmsg_add_u8(&b, "bss-termination-delay", bss_termination_delay);
+	if (target_bssid)
+		blobmsg_add_macaddr(&b, "target-bssid", target_bssid);
+	
+	hostapd_ubus_notify_bss_transition_add_candidate_list(candidate_list, candidate_list_len);
+
+	ubus_notify(ctx, &hapd->ubus.obj, "bss-transition-response", b.head, -1);
+#endif
+}
+
+int hostapd_ubus_notify_bss_transition_query(
+	struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 reason,
+	const u8 *candidate_list, u16 candidate_list_len)
+{
+#ifdef CONFIG_WNM_AP
+	struct ubus_event_req ureq = {};
+	char *cl_str;
+	u16 i;
+
+	if (!hapd->ubus.obj.has_subscribers)
+		return 0;
+
+	if (!addr)
+		return 0;
+
+	blob_buf_init(&b, 0);
+	blobmsg_add_macaddr(&b, "address", addr);
+	blobmsg_add_u8(&b, "dialog-token", dialog_token);
+	blobmsg_add_u8(&b, "reason", reason);
+	hostapd_ubus_notify_bss_transition_add_candidate_list(candidate_list, candidate_list_len);
+
+	if (!hapd->ubus.notify_response) {
+		ubus_notify(ctx, &hapd->ubus.obj, "bss-transition-query", b.head, -1);
+		return 0;
+	}
+
+	if (ubus_notify_async(ctx, &hapd->ubus.obj, "bss-transition-query", b.head, &ureq.nreq))
+		return 0;
+
+	ureq.nreq.status_cb = ubus_event_cb;
+	ubus_complete_request(ctx, &ureq.nreq.req, 100);
+
+	return ureq.resp;
+#endif
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ubus.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ubus.h
new file mode 100644
index 0000000..1a493e1
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/ubus.h
@@ -0,0 +1,159 @@
+/*
+ * hostapd / ubus support
+ * Copyright (c) 2013, Felix Fietkau <nbd@nbd.name>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+#ifndef __HOSTAPD_UBUS_H
+#define __HOSTAPD_UBUS_H
+
+enum hostapd_ubus_event_type {
+	HOSTAPD_UBUS_PROBE_REQ,
+	HOSTAPD_UBUS_AUTH_REQ,
+	HOSTAPD_UBUS_ASSOC_REQ,
+	HOSTAPD_UBUS_CONNECTED_REQ,
+	HOSTAPD_UBUS_DISCONNECTED_REQ,
+	HOSTAPD_UBUS_WPS_START,
+	HOSTAPD_UBUS_WPS_FAIL,
+	HOSTAPD_UBUS_WPS_SUCCESS,
+	HOSTAPD_UBUS_TYPE_MAX
+};
+
+struct hostapd_ubus_request {
+	enum hostapd_ubus_event_type type;
+	const struct ieee80211_mgmt *mgmt_frame;
+	const struct ieee802_11_elems *elems;
+	int ssi_signal; /* dBm */
+	const u8 *addr;
+};
+
+struct hostapd_iface;
+struct hostapd_data;
+struct hapd_interfaces;
+struct rrm_measurement_beacon_report;
+
+#ifdef UBUS_SUPPORT
+
+#include <libubox/avl.h>
+#include <libubus.h>
+
+struct hostapd_ubus_bss {
+	struct ubus_object obj;
+	struct avl_tree banned;
+	int notify_response;
+};
+
+void hostapd_ubus_add_iface(struct hostapd_iface *iface);
+void hostapd_ubus_free_iface(struct hostapd_iface *iface);
+void hostapd_ubus_add_bss(struct hostapd_data *hapd);
+void hostapd_ubus_free_bss(struct hostapd_data *hapd);
+void hostapd_ubus_add_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan);
+void hostapd_ubus_remove_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan);
+
+int hostapd_ubus_handle_event(struct hostapd_data *hapd, struct hostapd_ubus_request *req);
+void hostapd_ubus_handle_link_measurement(struct hostapd_data *hapd, const u8 *data, size_t len);
+void hostapd_ubus_notify(struct hostapd_data *hapd, const char *type, const u8 *mac);
+void hostapd_ubus_notify_beacon_report(struct hostapd_data *hapd,
+				       const u8 *addr, u8 token, u8 rep_mode,
+				       struct rrm_measurement_beacon_report *rep,
+				       size_t len);
+void hostapd_ubus_notify_radar_detected(struct hostapd_iface *iface, int frequency,
+					int chan_width, int cf1, int cf2);
+
+void hostapd_ubus_notify_bss_transition_response(
+	struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 status_code,
+	u8 bss_termination_delay, const u8 *target_bssid,
+	const u8 *candidate_list, u16 candidate_list_len);
+void hostapd_ubus_add(struct hapd_interfaces *interfaces);
+void hostapd_ubus_free(struct hapd_interfaces *interfaces);
+int hostapd_ubus_notify_bss_transition_query(
+	struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 reason,
+	const u8 *candidate_list, u16 candidate_list_len);
+void hostapd_ubus_notify_authorized(struct hostapd_data *hapd, struct sta_info *sta,
+				    const char *auth_alg);
+
+#else
+
+struct hostapd_ubus_bss {};
+
+static inline void hostapd_ubus_add_iface(struct hostapd_iface *iface)
+{
+}
+
+static inline void hostapd_ubus_free_iface(struct hostapd_iface *iface)
+{
+}
+
+static inline void hostapd_ubus_add_bss(struct hostapd_data *hapd)
+{
+}
+
+static inline void hostapd_ubus_free_bss(struct hostapd_data *hapd)
+{
+}
+
+static inline void hostapd_ubus_add_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
+{
+}
+
+static inline void hostapd_ubus_remove_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
+{
+}
+
+static inline int hostapd_ubus_handle_event(struct hostapd_data *hapd, struct hostapd_ubus_request *req)
+{
+	return 0;
+}
+
+static inline void hostapd_ubus_handle_link_measurement(struct hostapd_data *hapd, const u8 *data, size_t len)
+{
+}
+
+static inline void hostapd_ubus_notify(struct hostapd_data *hapd, const char *type, const u8 *mac)
+{
+}
+
+static inline void hostapd_ubus_notify_beacon_report(struct hostapd_data *hapd,
+						     const u8 *addr, u8 token,
+						     u8 rep_mode,
+						     struct rrm_measurement_beacon_report *rep,
+						     size_t len)
+{
+}
+static inline void hostapd_ubus_notify_radar_detected(struct hostapd_iface *iface, int frequency,
+						      int chan_width, int cf1, int cf2)
+{
+}
+
+static inline void hostapd_ubus_notify_bss_transition_response(
+	struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 status_code,
+	u8 bss_termination_delay, const u8 *target_bssid,
+	const u8 *candidate_list, u16 candidate_list_len)
+{
+}
+
+static inline void hostapd_ubus_add(struct hapd_interfaces *interfaces)
+{
+}
+
+static inline void hostapd_ubus_free(struct hapd_interfaces *interfaces)
+{
+}
+
+static inline int hostapd_ubus_notify_bss_transition_query(
+	struct hostapd_data *hapd, const u8 *addr, u8 dialog_token, u8 reason,
+	const u8 *candidate_list, u16 candidate_list_len)
+{
+	return 0;
+}
+
+static inline void
+hostapd_ubus_notify_authorized(struct hostapd_data *hapd, struct sta_info *sta,
+			       const char *auth_alg)
+{
+}
+
+#endif
+
+#endif
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/utils.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/utils.c
new file mode 100644
index 0000000..e93e531
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/utils.c
@@ -0,0 +1,112 @@
+/*
+ * AP mode helper functions
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "fst/fst.h"
+#include "sta_info.h"
+#include "hostapd.h"
+
+
+int hostapd_register_probereq_cb(struct hostapd_data *hapd,
+				 int (*cb)(void *ctx, const u8 *sa,
+					   const u8 *da, const u8 *bssid,
+					   const u8 *ie, size_t ie_len,
+					   int ssi_signal),
+				 void *ctx)
+{
+	struct hostapd_probereq_cb *n;
+
+	n = os_realloc_array(hapd->probereq_cb, hapd->num_probereq_cb + 1,
+			     sizeof(struct hostapd_probereq_cb));
+	if (n == NULL)
+		return -1;
+
+	hapd->probereq_cb = n;
+	n = &hapd->probereq_cb[hapd->num_probereq_cb];
+	hapd->num_probereq_cb++;
+
+	n->cb = cb;
+	n->ctx = ctx;
+
+	return 0;
+}
+
+
+struct prune_data {
+	struct hostapd_data *hapd;
+	const u8 *addr;
+	int mld_assoc_link_id;
+};
+
+static int prune_associations(struct hostapd_iface *iface, void *ctx)
+{
+	struct prune_data *data = ctx;
+	struct sta_info *osta;
+	struct hostapd_data *ohapd;
+	size_t j;
+
+	for (j = 0; j < iface->num_bss; j++) {
+		ohapd = iface->bss[j];
+		if (ohapd == data->hapd)
+			continue;
+#ifdef CONFIG_TESTING_OPTIONS
+		if (ohapd->conf->skip_prune_assoc)
+			continue;
+#endif /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_FST
+		/* Don't prune STAs belong to same FST */
+		if (ohapd->iface->fst &&
+		    data->hapd->iface->fst &&
+		    fst_are_ifaces_aggregated(ohapd->iface->fst,
+					      data->hapd->iface->fst))
+			continue;
+#endif /* CONFIG_FST */
+		osta = ap_get_sta(ohapd, data->addr);
+		if (!osta)
+			continue;
+
+#ifdef CONFIG_IEEE80211BE
+		if (data->mld_assoc_link_id >= 0 &&
+		    osta->mld_assoc_link_id == data->mld_assoc_link_id)
+			continue;
+#endif /* CONFIG_IEEE80211BE */
+
+		wpa_printf(MSG_INFO, "%s: Prune association for " MACSTR,
+			   ohapd->conf->iface, MAC2STR(osta->addr));
+		ap_sta_disassociate(ohapd, osta, WLAN_REASON_UNSPECIFIED);
+	}
+
+	return 0;
+}
+
+/**
+ * hostapd_prune_associations - Remove extraneous associations
+ * @hapd: Pointer to BSS data for the most recent association
+ * @addr: Associated STA address
+ * @mld_assoc_link_id: MLD link id used for association or -1 for non MLO
+ *
+ * This function looks through all radios and BSS's for previous
+ * (stale) associations of STA. If any are found they are removed.
+ */
+void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr,
+				int mld_assoc_link_id)
+{
+	struct prune_data data;
+
+	data.hapd = hapd;
+	data.addr = addr;
+	data.mld_assoc_link_id = mld_assoc_link_id;
+
+	if (hapd->iface->interfaces &&
+	    hapd->iface->interfaces->for_each_interface)
+		hapd->iface->interfaces->for_each_interface(
+			hapd->iface->interfaces, prune_associations, &data);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan.c
new file mode 100644
index 0000000..b6f6bb1
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan.c
@@ -0,0 +1,34 @@
+/*
+ * hostapd / VLAN definition
+ * Copyright (c) 2016, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "ap/vlan.h"
+
+/* compare the two arguments, NULL is treated as empty
+ * return zero iff they are equal
+ */
+int vlan_compare(struct vlan_description *a, struct vlan_description *b)
+{
+	int i;
+	const int a_empty = !a || !a->notempty;
+	const int b_empty = !b || !b->notempty;
+
+	if (a_empty && b_empty)
+		return 0;
+	if (a_empty || b_empty)
+		return 1;
+	if (a->untagged != b->untagged)
+		return 1;
+	for (i = 0; i < MAX_NUM_TAGGED_VLAN; i++) {
+		if (a->tagged[i] != b->tagged[i])
+			return 1;
+	}
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan.h
new file mode 100644
index 0000000..af84929
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan.h
@@ -0,0 +1,30 @@
+/*
+ * hostapd / VLAN definition
+ * Copyright (c) 2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef VLAN_H
+#define VLAN_H
+
+#define MAX_NUM_TAGGED_VLAN 32
+
+struct vlan_description {
+	int notempty; /* 0 : no vlan information present, 1: else */
+	int untagged; /* >0 802.1q vid */
+	int tagged[MAX_NUM_TAGGED_VLAN]; /* first k items, ascending order */
+};
+
+#ifndef CONFIG_NO_VLAN
+int vlan_compare(struct vlan_description *a, struct vlan_description *b);
+#else /* CONFIG_NO_VLAN */
+static inline int
+vlan_compare(struct vlan_description *a, struct vlan_description *b)
+{
+	return 0;
+}
+#endif /* CONFIG_NO_VLAN */
+
+#endif /* VLAN_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan_full.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan_full.c
new file mode 100644
index 0000000..053d633
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan_full.c
@@ -0,0 +1,805 @@
+/*
+ * hostapd / VLAN initialization - full dynamic VLAN
+ * Copyright 2003, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <net/if.h>
+/* Avoid conflicts due to NetBSD net/if.h if_type define with driver.h */
+#undef if_type
+#include <sys/ioctl.h>
+
+#include "utils/common.h"
+#include "drivers/priv_netlink.h"
+#include "drivers/linux_ioctl.h"
+#include "common/linux_bridge.h"
+#include "common/linux_vlan.h"
+#include "utils/eloop.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "wpa_auth.h"
+#include "vlan_init.h"
+#include "vlan_util.h"
+
+
+struct full_dynamic_vlan {
+	int s; /* socket on which to listen for new/removed interfaces. */
+};
+
+#define DVLAN_CLEAN_BR         0x1
+#define DVLAN_CLEAN_VLAN       0x2
+#define DVLAN_CLEAN_VLAN_PORT  0x4
+
+struct dynamic_iface {
+	char ifname[IFNAMSIZ + 1];
+	int usage;
+	int clean;
+	struct dynamic_iface *next;
+};
+
+
+/* Increment ref counter for ifname and add clean flag.
+ * If not in list, add it only if some flags are given.
+ */
+static void dyn_iface_get(struct hostapd_data *hapd, const char *ifname,
+			  int clean)
+{
+	struct dynamic_iface *next, **dynamic_ifaces;
+	struct hapd_interfaces *interfaces;
+
+	interfaces = hapd->iface->interfaces;
+	dynamic_ifaces = &interfaces->vlan_priv;
+
+	for (next = *dynamic_ifaces; next; next = next->next) {
+		if (os_strcmp(ifname, next->ifname) == 0)
+			break;
+	}
+
+	if (next) {
+		next->usage++;
+		next->clean |= clean;
+		return;
+	}
+
+	if (!clean)
+		return;
+
+	next = os_zalloc(sizeof(*next));
+	if (!next)
+		return;
+	os_strlcpy(next->ifname, ifname, sizeof(next->ifname));
+	next->usage = 1;
+	next->clean = clean;
+	next->next = *dynamic_ifaces;
+	*dynamic_ifaces = next;
+}
+
+
+/* Decrement reference counter for given ifname.
+ * Return clean flag iff reference counter was decreased to zero, else zero
+ */
+static int dyn_iface_put(struct hostapd_data *hapd, const char *ifname)
+{
+	struct dynamic_iface *next, *prev = NULL, **dynamic_ifaces;
+	struct hapd_interfaces *interfaces;
+	int clean;
+
+	interfaces = hapd->iface->interfaces;
+	dynamic_ifaces = &interfaces->vlan_priv;
+
+	for (next = *dynamic_ifaces; next; next = next->next) {
+		if (os_strcmp(ifname, next->ifname) == 0)
+			break;
+		prev = next;
+	}
+
+	if (!next)
+		return 0;
+
+	next->usage--;
+	if (next->usage)
+		return 0;
+
+	if (prev)
+		prev->next = next->next;
+	else
+		*dynamic_ifaces = next->next;
+	clean = next->clean;
+	os_free(next);
+
+	return clean;
+}
+
+
+static int ifconfig_down(const char *if_name)
+{
+	wpa_printf(MSG_DEBUG, "VLAN: Set interface %s down", if_name);
+	return ifconfig_helper(if_name, 0);
+}
+
+
+/* This value should be 256 ONLY. If it is something else, then hostapd
+ * might crash!, as this value has been hard-coded in 2.4.x kernel
+ * bridging code.
+ */
+#define MAX_BR_PORTS      		256
+
+static int br_delif(const char *br_name, const char *if_name)
+{
+	int fd;
+	struct ifreq ifr;
+	unsigned long args[2];
+	int if_index;
+
+	wpa_printf(MSG_DEBUG, "VLAN: br_delif(%s, %s)", br_name, if_name);
+	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+			   "failed: %s", __func__, strerror(errno));
+		return -1;
+	}
+
+	if (linux_br_del_if(fd, br_name, if_name) == 0)
+		goto done;
+
+	if_index = if_nametoindex(if_name);
+
+	if (if_index == 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining "
+			   "interface index for '%s'",
+			   __func__, if_name);
+		close(fd);
+		return -1;
+	}
+
+	args[0] = BRCTL_DEL_IF;
+	args[1] = if_index;
+
+	os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
+	ifr.ifr_data = (void *) args;
+
+	if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0 && errno != EINVAL) {
+		/* No error if interface already removed. */
+		wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE,"
+			   "BRCTL_DEL_IF] failed for br_name=%s if_name=%s: "
+			   "%s", __func__, br_name, if_name, strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+done:
+	close(fd);
+	return 0;
+}
+
+
+/*
+	Add interface 'if_name' to the bridge 'br_name'
+
+	returns -1 on error
+	returns 1 if the interface is already part of the bridge
+	returns 0 otherwise
+*/
+static int br_addif(const char *br_name, const char *if_name)
+{
+	int fd;
+	struct ifreq ifr;
+	unsigned long args[2];
+	int if_index;
+
+	wpa_printf(MSG_DEBUG, "VLAN: br_addif(%s, %s)", br_name, if_name);
+	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+			   "failed: %s", __func__, strerror(errno));
+		return -1;
+	}
+
+	if (linux_br_add_if(fd, br_name, if_name) == 0)
+		goto done;
+	if (errno == EBUSY) {
+		/* The interface is already added. */
+		close(fd);
+		return 1;
+	}
+
+	if_index = if_nametoindex(if_name);
+
+	if (if_index == 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining "
+			   "interface index for '%s'",
+			   __func__, if_name);
+		close(fd);
+		return -1;
+	}
+
+	args[0] = BRCTL_ADD_IF;
+	args[1] = if_index;
+
+	os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
+	ifr.ifr_data = (void *) args;
+
+	if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
+		if (errno == EBUSY) {
+			/* The interface is already added. */
+			close(fd);
+			return 1;
+		}
+
+		wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE,"
+			   "BRCTL_ADD_IF] failed for br_name=%s if_name=%s: "
+			   "%s", __func__, br_name, if_name, strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+done:
+	close(fd);
+	return 0;
+}
+
+
+static int br_delbr(const char *br_name)
+{
+	int fd;
+	unsigned long arg[2];
+
+	wpa_printf(MSG_DEBUG, "VLAN: br_delbr(%s)", br_name);
+	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+			   "failed: %s", __func__, strerror(errno));
+		return -1;
+	}
+
+	if (linux_br_del(fd, br_name) == 0)
+		goto done;
+
+	arg[0] = BRCTL_DEL_BRIDGE;
+	arg[1] = (unsigned long) br_name;
+
+	if (ioctl(fd, SIOCGIFBR, arg) < 0 && errno != ENXIO) {
+		/* No error if bridge already removed. */
+		wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_DEL_BRIDGE failed for "
+			   "%s: %s", __func__, br_name, strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+done:
+	close(fd);
+	return 0;
+}
+
+
+/*
+	Add a bridge with the name 'br_name'.
+
+	returns -1 on error
+	returns 1 if the bridge already exists
+	returns 0 otherwise
+*/
+static int br_addbr(const char *br_name)
+{
+	int fd;
+	unsigned long arg[4];
+	struct ifreq ifr;
+
+	wpa_printf(MSG_DEBUG, "VLAN: br_addbr(%s)", br_name);
+	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+			   "failed: %s", __func__, strerror(errno));
+		return -1;
+	}
+
+	if (linux_br_add(fd, br_name) == 0)
+		goto done;
+	if (errno == EEXIST) {
+		/* The bridge is already added. */
+		close(fd);
+		return 1;
+	}
+
+	arg[0] = BRCTL_ADD_BRIDGE;
+	arg[1] = (unsigned long) br_name;
+
+	if (ioctl(fd, SIOCGIFBR, arg) < 0) {
+		if (errno == EEXIST) {
+			/* The bridge is already added. */
+			close(fd);
+			return 1;
+		} else {
+			wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_ADD_BRIDGE "
+				   "failed for %s: %s",
+				   __func__, br_name, strerror(errno));
+			close(fd);
+			return -1;
+		}
+	}
+
+done:
+	/* Decrease forwarding delay to avoid EAPOL timeouts. */
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, br_name, IFNAMSIZ);
+	arg[0] = BRCTL_SET_BRIDGE_FORWARD_DELAY;
+	arg[1] = 1;
+	arg[2] = 0;
+	arg[3] = 0;
+	ifr.ifr_data = (char *) &arg;
+	if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: "
+			   "BRCTL_SET_BRIDGE_FORWARD_DELAY (1 sec) failed for "
+			   "%s: %s", __func__, br_name, strerror(errno));
+		/* Continue anyway */
+	}
+
+	close(fd);
+	return 0;
+}
+
+
+static int br_getnumports(const char *br_name)
+{
+	int fd;
+	int i;
+	int port_cnt = 0;
+	unsigned long arg[4];
+	int ifindices[MAX_BR_PORTS];
+	struct ifreq ifr;
+
+	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+			   "failed: %s", __func__, strerror(errno));
+		return -1;
+	}
+
+	arg[0] = BRCTL_GET_PORT_LIST;
+	arg[1] = (unsigned long) ifindices;
+	arg[2] = MAX_BR_PORTS;
+	arg[3] = 0;
+
+	os_memset(ifindices, 0, sizeof(ifindices));
+	os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
+	ifr.ifr_data = (void *) arg;
+
+	if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_GET_PORT_LIST "
+			   "failed for %s: %s",
+			   __func__, br_name, strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+	for (i = 1; i < MAX_BR_PORTS; i++) {
+		if (ifindices[i] > 0) {
+			port_cnt++;
+		}
+	}
+
+	close(fd);
+	return port_cnt;
+}
+
+
+static void vlan_newlink_tagged(int vlan_naming, const char *tagged_interface,
+				const char *br_name, int vid,
+				struct hostapd_data *hapd)
+{
+	char vlan_ifname[IFNAMSIZ];
+	int clean;
+	int ret;
+
+	if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE)
+		ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d",
+				  tagged_interface, vid);
+	else
+		ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d",
+				  vid);
+	if (ret >= (int) sizeof(vlan_ifname))
+		wpa_printf(MSG_WARNING,
+			   "VLAN: Interface name was truncated to %s",
+			   vlan_ifname);
+
+	clean = 0;
+	ifconfig_up(tagged_interface);
+	if (!vlan_add(tagged_interface, vid, vlan_ifname))
+		clean |= DVLAN_CLEAN_VLAN;
+
+	if (!br_addif(br_name, vlan_ifname))
+		clean |= DVLAN_CLEAN_VLAN_PORT;
+
+	dyn_iface_get(hapd, vlan_ifname, clean);
+
+	ifconfig_up(vlan_ifname);
+}
+
+
+static void vlan_bridge_name(char *br_name, struct hostapd_data *hapd,
+			     struct hostapd_vlan *vlan, int vid)
+{
+	char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
+	int ret;
+
+	if (vlan->bridge[0]) {
+		os_strlcpy(br_name, vlan->bridge, IFNAMSIZ);
+		ret = 0;
+	} else if (hapd->conf->vlan_bridge[0]) {
+		ret = os_snprintf(br_name, IFNAMSIZ, "%s%d",
+				  hapd->conf->vlan_bridge, vid);
+	} else if (tagged_interface) {
+		ret = os_snprintf(br_name, IFNAMSIZ, "br%s.%d",
+				  tagged_interface, vid);
+	} else {
+		ret = os_snprintf(br_name, IFNAMSIZ, "brvlan%d", vid);
+	}
+	if (ret >= IFNAMSIZ)
+		wpa_printf(MSG_WARNING,
+			   "VLAN: Interface name was truncated to %s",
+			   br_name);
+}
+
+
+static void vlan_get_bridge(const char *br_name, struct hostapd_data *hapd,
+			    int vid)
+{
+	char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
+	int vlan_naming = hapd->conf->ssid.vlan_naming;
+
+	dyn_iface_get(hapd, br_name, br_addbr(br_name) ? 0 : DVLAN_CLEAN_BR);
+
+	ifconfig_up(br_name);
+
+	if (tagged_interface)
+		vlan_newlink_tagged(vlan_naming, tagged_interface, br_name,
+				    vid, hapd);
+}
+
+
+void vlan_newlink(const char *ifname, struct hostapd_data *hapd)
+{
+	char br_name[IFNAMSIZ];
+	struct hostapd_vlan *vlan;
+	int untagged, *tagged, i, notempty;
+
+	wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname);
+
+	for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) {
+		if (vlan->configured ||
+		    os_strcmp(ifname, vlan->ifname) != 0)
+			continue;
+		break;
+	}
+	if (!vlan)
+		return;
+
+	if (hapd->conf->ssid.vlan_no_bridge)
+		goto out;
+
+	vlan->configured = 1;
+
+	notempty = vlan->vlan_desc.notempty;
+	untagged = vlan->vlan_desc.untagged;
+	tagged = vlan->vlan_desc.tagged;
+
+	if (!notempty) {
+		/* Non-VLAN STA */
+		if (hapd->conf->bridge[0] &&
+		    !br_addif(hapd->conf->bridge, ifname))
+			vlan->clean |= DVLAN_CLEAN_WLAN_PORT;
+	} else if (untagged > 0 && untagged <= MAX_VLAN_ID) {
+		vlan_bridge_name(br_name, hapd, vlan, untagged);
+
+		vlan_get_bridge(br_name, hapd, untagged);
+
+		if (!br_addif(br_name, ifname))
+			vlan->clean |= DVLAN_CLEAN_WLAN_PORT;
+	}
+
+	for (i = 0; i < MAX_NUM_TAGGED_VLAN && tagged[i]; i++) {
+		if (tagged[i] == untagged ||
+		    tagged[i] <= 0 || tagged[i] > MAX_VLAN_ID ||
+		    (i > 0 && tagged[i] == tagged[i - 1]))
+			continue;
+		vlan_bridge_name(br_name, hapd, vlan, tagged[i]);
+		vlan_get_bridge(br_name, hapd, tagged[i]);
+		vlan_newlink_tagged(DYNAMIC_VLAN_NAMING_WITH_DEVICE,
+				    ifname, br_name, tagged[i], hapd);
+	}
+
+out:
+	ifconfig_up(ifname);
+}
+
+
+static void vlan_dellink_tagged(int vlan_naming, const char *tagged_interface,
+				const char *br_name, int vid,
+				struct hostapd_data *hapd)
+{
+	char vlan_ifname[IFNAMSIZ];
+	int clean;
+	int ret;
+
+	if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE)
+		ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d",
+				  tagged_interface, vid);
+	else
+		ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d",
+				  vid);
+	if (ret >= (int) sizeof(vlan_ifname))
+		wpa_printf(MSG_WARNING,
+			   "VLAN: Interface name was truncated to %s",
+			   vlan_ifname);
+
+
+	clean = dyn_iface_put(hapd, vlan_ifname);
+
+	if (clean & DVLAN_CLEAN_VLAN_PORT)
+		br_delif(br_name, vlan_ifname);
+
+	if (clean & DVLAN_CLEAN_VLAN) {
+		ifconfig_down(vlan_ifname);
+		vlan_rem(vlan_ifname);
+	}
+}
+
+
+static void vlan_put_bridge(const char *br_name, struct hostapd_data *hapd,
+			    int vid)
+{
+	int clean;
+	char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
+	int vlan_naming = hapd->conf->ssid.vlan_naming;
+
+	if (tagged_interface)
+		vlan_dellink_tagged(vlan_naming, tagged_interface, br_name,
+				    vid, hapd);
+
+	clean = dyn_iface_put(hapd, br_name);
+	if ((clean & DVLAN_CLEAN_BR) && br_getnumports(br_name) == 0) {
+		ifconfig_down(br_name);
+		br_delbr(br_name);
+	}
+}
+
+
+void vlan_dellink(const char *ifname, struct hostapd_data *hapd)
+{
+	struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan;
+
+	wpa_printf(MSG_DEBUG, "VLAN: vlan_dellink(%s)", ifname);
+
+	first = prev = vlan;
+
+	while (vlan) {
+		if (os_strcmp(ifname, vlan->ifname) != 0) {
+			prev = vlan;
+			vlan = vlan->next;
+			continue;
+		}
+		break;
+	}
+	if (!vlan)
+		return;
+
+	if (vlan->configured) {
+		int notempty = vlan->vlan_desc.notempty;
+		int untagged = vlan->vlan_desc.untagged;
+		int *tagged = vlan->vlan_desc.tagged;
+		char br_name[IFNAMSIZ];
+		int i;
+
+		for (i = 0; i < MAX_NUM_TAGGED_VLAN && tagged[i]; i++) {
+			if (tagged[i] == untagged ||
+			    tagged[i] <= 0 || tagged[i] > MAX_VLAN_ID ||
+			    (i > 0 && tagged[i] == tagged[i - 1]))
+				continue;
+			vlan_bridge_name(br_name, hapd, vlan, tagged[i]);
+			vlan_dellink_tagged(DYNAMIC_VLAN_NAMING_WITH_DEVICE,
+					    ifname, br_name, tagged[i], hapd);
+			vlan_put_bridge(br_name, hapd, tagged[i]);
+		}
+
+		if (!notempty) {
+			/* Non-VLAN STA */
+			if (hapd->conf->bridge[0] &&
+			    (vlan->clean & DVLAN_CLEAN_WLAN_PORT))
+				br_delif(hapd->conf->bridge, ifname);
+		} else if (untagged > 0 && untagged <= MAX_VLAN_ID) {
+			vlan_bridge_name(br_name, hapd, vlan, untagged);
+
+			if (vlan->clean & DVLAN_CLEAN_WLAN_PORT)
+				br_delif(br_name, vlan->ifname);
+
+			vlan_put_bridge(br_name, hapd, untagged);
+		}
+	}
+
+	/*
+	 * Ensure this VLAN interface is actually removed even if
+	 * NEWLINK message is only received later.
+	 */
+	if (if_nametoindex(vlan->ifname) && vlan_if_remove(hapd, vlan))
+		wpa_printf(MSG_ERROR,
+			   "VLAN: Could not remove VLAN iface: %s: %s",
+			   vlan->ifname, strerror(errno));
+
+	if (vlan == first)
+		hapd->conf->vlan = vlan->next;
+	else
+		prev->next = vlan->next;
+
+	os_free(vlan);
+}
+
+
+static void
+vlan_read_ifnames(struct nlmsghdr *h, size_t len, int del,
+		  struct hostapd_data *hapd)
+{
+	struct ifinfomsg *ifi;
+	int attrlen, nlmsg_len, rta_len;
+	struct rtattr *attr;
+	char ifname[IFNAMSIZ + 1];
+
+	if (len < sizeof(*ifi))
+		return;
+
+	ifi = NLMSG_DATA(h);
+
+	nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg));
+
+	attrlen = h->nlmsg_len - nlmsg_len;
+	if (attrlen < 0)
+		return;
+
+	attr = (struct rtattr *) (((char *) ifi) + nlmsg_len);
+
+	os_memset(ifname, 0, sizeof(ifname));
+	rta_len = RTA_ALIGN(sizeof(struct rtattr));
+	while (RTA_OK(attr, attrlen)) {
+		if (attr->rta_type == IFLA_IFNAME) {
+			int n = attr->rta_len - rta_len;
+			if (n < 0)
+				break;
+
+			if ((size_t) n >= sizeof(ifname))
+				n = sizeof(ifname) - 1;
+			os_memcpy(ifname, ((char *) attr) + rta_len, n);
+
+		}
+
+		attr = RTA_NEXT(attr, attrlen);
+	}
+
+	if (!ifname[0])
+		return;
+	if (del && if_nametoindex(ifname)) {
+		 /* interface still exists, race condition ->
+		  * iface has just been recreated */
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "VLAN: RTM_%sLINK: ifi_index=%d ifname=%s ifi_family=%d ifi_flags=0x%x (%s%s%s%s)",
+		   del ? "DEL" : "NEW",
+		   ifi->ifi_index, ifname, ifi->ifi_family, ifi->ifi_flags,
+		   (ifi->ifi_flags & IFF_UP) ? "[UP]" : "",
+		   (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "",
+		   (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
+		   (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
+
+	if (del)
+		vlan_dellink(ifname, hapd);
+	else
+		vlan_newlink(ifname, hapd);
+}
+
+
+static void vlan_event_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	char buf[8192];
+	int left;
+	struct sockaddr_nl from;
+	socklen_t fromlen;
+	struct nlmsghdr *h;
+	struct hostapd_data *hapd = eloop_ctx;
+
+	fromlen = sizeof(from);
+	left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT,
+			(struct sockaddr *) &from, &fromlen);
+	if (left < 0) {
+		if (errno != EINTR && errno != EAGAIN)
+			wpa_printf(MSG_ERROR, "VLAN: %s: recvfrom failed: %s",
+				   __func__, strerror(errno));
+		return;
+	}
+
+	h = (struct nlmsghdr *) buf;
+	while (NLMSG_OK(h, left)) {
+		int len, plen;
+
+		len = h->nlmsg_len;
+		plen = len - sizeof(*h);
+		if (len > left || plen < 0) {
+			wpa_printf(MSG_DEBUG, "VLAN: Malformed netlink "
+				   "message: len=%d left=%d plen=%d",
+				   len, left, plen);
+			break;
+		}
+
+		switch (h->nlmsg_type) {
+		case RTM_NEWLINK:
+			vlan_read_ifnames(h, plen, 0, hapd);
+			break;
+		case RTM_DELLINK:
+			vlan_read_ifnames(h, plen, 1, hapd);
+			break;
+		}
+
+		h = NLMSG_NEXT(h, left);
+	}
+
+	if (left > 0) {
+		wpa_printf(MSG_DEBUG, "VLAN: %s: %d extra bytes in the end of "
+			   "netlink message", __func__, left);
+	}
+}
+
+
+struct full_dynamic_vlan *
+full_dynamic_vlan_init(struct hostapd_data *hapd)
+{
+	struct sockaddr_nl local;
+	struct full_dynamic_vlan *priv;
+
+	priv = os_zalloc(sizeof(*priv));
+	if (priv == NULL)
+		return NULL;
+
+	vlan_set_name_type(hapd->conf->ssid.vlan_naming ==
+			   DYNAMIC_VLAN_NAMING_WITH_DEVICE ?
+			   VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD :
+			   VLAN_NAME_TYPE_PLUS_VID_NO_PAD);
+
+	priv->s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+	if (priv->s < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: socket(PF_NETLINK,SOCK_RAW,"
+			   "NETLINK_ROUTE) failed: %s",
+			   __func__, strerror(errno));
+		os_free(priv);
+		return NULL;
+	}
+
+	os_memset(&local, 0, sizeof(local));
+	local.nl_family = AF_NETLINK;
+	local.nl_groups = RTMGRP_LINK;
+	if (bind(priv->s, (struct sockaddr *) &local, sizeof(local)) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: bind(netlink) failed: %s",
+			   __func__, strerror(errno));
+		close(priv->s);
+		os_free(priv);
+		return NULL;
+	}
+
+	if (eloop_register_read_sock(priv->s, vlan_event_receive, hapd, NULL))
+	{
+		close(priv->s);
+		os_free(priv);
+		return NULL;
+	}
+
+	return priv;
+}
+
+
+void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv)
+{
+	if (priv == NULL)
+		return;
+	eloop_unregister_read_sock(priv->s);
+	close(priv->s);
+	os_free(priv);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan_ifconfig.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan_ifconfig.c
new file mode 100644
index 0000000..ef953a5
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan_ifconfig.c
@@ -0,0 +1,69 @@
+/*
+ * hostapd / VLAN ifconfig helpers
+ * Copyright 2003, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <net/if.h>
+#include <sys/ioctl.h>
+
+#include "utils/common.h"
+#include "vlan_util.h"
+
+
+int ifconfig_helper(const char *if_name, int up)
+{
+	int fd;
+	struct ifreq ifr;
+
+	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+			   "failed: %s", __func__, strerror(errno));
+		return -1;
+	}
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, if_name, IFNAMSIZ);
+
+	if (ioctl(fd, SIOCGIFFLAGS, &ifr) != 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCGIFFLAGS) failed "
+			   "for interface %s: %s",
+			   __func__, if_name, strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+	if (up)
+		ifr.ifr_flags |= IFF_UP;
+	else
+		ifr.ifr_flags &= ~IFF_UP;
+
+	if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCSIFFLAGS) failed "
+			   "for interface %s (up=%d): %s",
+			   __func__, if_name, up, strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+	close(fd);
+	return 0;
+}
+
+
+int ifconfig_up(const char *if_name)
+{
+	wpa_printf(MSG_DEBUG, "VLAN: Set interface %s up", if_name);
+	return ifconfig_helper(if_name, 1);
+}
+
+
+int iface_exists(const char *ifname)
+{
+	return if_nametoindex(ifname);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan_init.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan_init.c
new file mode 100644
index 0000000..b69f3de
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan_init.c
@@ -0,0 +1,273 @@
+/*
+ * hostapd / VLAN initialization
+ * Copyright 2003, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "wpa_auth.h"
+#include "vlan_init.h"
+#include "vlan_util.h"
+
+
+static int vlan_if_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
+		       int existsok)
+{
+	bool vlan_exists = iface_exists(vlan->ifname);
+	int ret;
+#ifdef CONFIG_WEP
+	int i;
+
+	for (i = 0; i < NUM_WEP_KEYS; i++) {
+		if (!hapd->conf->ssid.wep.key[i])
+			continue;
+		wpa_printf(MSG_ERROR,
+			   "VLAN: Refusing to set up VLAN iface %s with WEP",
+			   vlan->ifname);
+		return -1;
+	}
+#endif /* CONFIG_WEP */
+
+	if (!vlan_exists)
+		ret = hostapd_vlan_if_add(hapd, vlan->ifname);
+	else if (!existsok)
+		return -1;
+	else
+		ret = 0;
+
+	if (ret)
+		return ret;
+
+	ifconfig_up(vlan->ifname); /* else wpa group will fail fatal */
+
+	if (hapd->wpa_auth)
+		ret = wpa_auth_ensure_group(hapd->wpa_auth, vlan->vlan_id);
+
+	if (!ret && !vlan_exists)
+		hostapd_ubus_add_vlan(hapd, vlan);
+
+	if (ret == 0)
+		return ret;
+
+	wpa_printf(MSG_ERROR, "WPA initialization for VLAN %d failed (%d)",
+		   vlan->vlan_id, ret);
+	if (wpa_auth_release_group(hapd->wpa_auth, vlan->vlan_id))
+		wpa_printf(MSG_ERROR, "WPA deinit of %s failed", vlan->ifname);
+
+	/* group state machine setup failed */
+	if (hostapd_vlan_if_remove(hapd, vlan->ifname))
+		wpa_printf(MSG_ERROR, "Removal of %s failed", vlan->ifname);
+
+	return ret;
+}
+
+
+int vlan_if_remove(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
+{
+	int ret;
+
+	ret = wpa_auth_release_group(hapd->wpa_auth, vlan->vlan_id);
+	if (ret)
+		wpa_printf(MSG_ERROR,
+			   "WPA deinitialization for VLAN %d failed (%d)",
+			   vlan->vlan_id, ret);
+
+	hostapd_ubus_remove_vlan(hapd, vlan);
+
+	return hostapd_vlan_if_remove(hapd, vlan->ifname);
+}
+
+
+static int vlan_dynamic_add(struct hostapd_data *hapd,
+			    struct hostapd_vlan *vlan)
+{
+	while (vlan) {
+		if (vlan->vlan_id != VLAN_ID_WILDCARD) {
+			if (vlan_if_add(hapd, vlan, 1)) {
+				wpa_printf(MSG_ERROR,
+					   "VLAN: Could not add VLAN %s: %s",
+					   vlan->ifname, strerror(errno));
+				return -1;
+			}
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+			vlan_newlink(vlan->ifname, hapd);
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+		}
+
+		vlan = vlan->next;
+	}
+
+	return 0;
+}
+
+
+static void vlan_dynamic_remove(struct hostapd_data *hapd,
+				struct hostapd_vlan *vlan)
+{
+	struct hostapd_vlan *next;
+
+	while (vlan) {
+		next = vlan->next;
+
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+		/* vlan_dellink() takes care of cleanup and interface removal */
+		if (vlan->vlan_id != VLAN_ID_WILDCARD)
+			vlan_dellink(vlan->ifname, hapd);
+#else /* CONFIG_FULL_DYNAMIC_VLAN */
+		if (vlan->vlan_id != VLAN_ID_WILDCARD &&
+		    vlan_if_remove(hapd, vlan)) {
+			wpa_printf(MSG_ERROR, "VLAN: Could not remove VLAN "
+				   "iface: %s: %s",
+				   vlan->ifname, strerror(errno));
+		}
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+
+		vlan = next;
+	}
+}
+
+
+int vlan_init(struct hostapd_data *hapd)
+{
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+	hapd->full_dynamic_vlan = full_dynamic_vlan_init(hapd);
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+
+	if ((hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED ||
+	     hapd->conf->ssid.per_sta_vif) &&
+	    !hapd->conf->vlan) {
+		/* dynamic vlans enabled but no (or empty) vlan_file given */
+		struct hostapd_vlan *vlan;
+		int ret;
+
+		vlan = os_zalloc(sizeof(*vlan));
+		if (vlan == NULL) {
+			wpa_printf(MSG_ERROR, "Out of memory while assigning "
+				   "VLAN interfaces");
+			return -1;
+		}
+
+		vlan->vlan_id = VLAN_ID_WILDCARD;
+		ret = os_snprintf(vlan->ifname, sizeof(vlan->ifname), "%s.#",
+				  hapd->conf->iface);
+		if (ret >= (int) sizeof(vlan->ifname)) {
+			wpa_printf(MSG_WARNING,
+				   "VLAN: Interface name was truncated to %s",
+				   vlan->ifname);
+		} else if (ret < 0) {
+			os_free(vlan);
+			return ret;
+		}
+		vlan->next = hapd->conf->vlan;
+		hapd->conf->vlan = vlan;
+	}
+
+	if (vlan_dynamic_add(hapd, hapd->conf->vlan))
+		return -1;
+
+        return 0;
+}
+
+
+void vlan_deinit(struct hostapd_data *hapd)
+{
+	vlan_dynamic_remove(hapd, hapd->conf->vlan);
+
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+	full_dynamic_vlan_deinit(hapd->full_dynamic_vlan);
+	hapd->full_dynamic_vlan = NULL;
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+}
+
+
+struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
+				       struct hostapd_vlan *vlan,
+				       int vlan_id,
+				       struct vlan_description *vlan_desc)
+{
+	struct hostapd_vlan *n;
+	char ifname[IFNAMSIZ + 1], *pos;
+	int ret;
+
+	if (vlan == NULL || vlan->vlan_id != VLAN_ID_WILDCARD)
+		return NULL;
+
+	wpa_printf(MSG_DEBUG, "VLAN: %s(vlan_id=%d ifname=%s)",
+		   __func__, vlan_id, vlan->ifname);
+	os_strlcpy(ifname, vlan->ifname, sizeof(ifname));
+	pos = os_strchr(ifname, '#');
+	if (pos == NULL)
+		return NULL;
+	*pos++ = '\0';
+
+	n = os_zalloc(sizeof(*n));
+	if (n == NULL)
+		return NULL;
+
+	n->vlan_id = vlan_id;
+	if (vlan_desc)
+		n->vlan_desc = *vlan_desc;
+	n->dynamic_vlan = 1;
+
+	ret = os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s",
+			  ifname, vlan_id, pos);
+	if (os_snprintf_error(sizeof(n->ifname), ret)) {
+		os_free(n);
+		return NULL;
+	}
+	os_strlcpy(n->bridge, vlan->bridge, sizeof(n->bridge));
+
+	n->next = hapd->conf->vlan;
+	hapd->conf->vlan = n;
+
+	/* hapd->conf->vlan needs this new VLAN here for WPA setup */
+	if (vlan_if_add(hapd, n, 0)) {
+		hapd->conf->vlan = n->next;
+		os_free(n);
+		n = NULL;
+	}
+
+	return n;
+}
+
+
+int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id)
+{
+	struct hostapd_vlan *vlan;
+
+	if (vlan_id <= 0)
+		return 1;
+
+	wpa_printf(MSG_DEBUG, "VLAN: %s(ifname=%s vlan_id=%d)",
+		   __func__, hapd->conf->iface, vlan_id);
+
+	vlan = hapd->conf->vlan;
+	while (vlan) {
+		if (vlan->vlan_id == vlan_id && vlan->dynamic_vlan > 0) {
+			vlan->dynamic_vlan--;
+			break;
+		}
+		vlan = vlan->next;
+	}
+
+	if (vlan == NULL)
+		return 1;
+
+	if (vlan->dynamic_vlan == 0) {
+		vlan_if_remove(hapd, vlan);
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+		vlan_dellink(vlan->ifname, hapd);
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+	}
+
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan_init.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan_init.h
new file mode 100644
index 0000000..d17c82c
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan_init.h
@@ -0,0 +1,44 @@
+/*
+ * hostapd / VLAN initialization
+ * Copyright 2003, Instant802 Networks, Inc.
+ * Copyright 2005, Devicescape Software, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef VLAN_INIT_H
+#define VLAN_INIT_H
+
+#ifndef CONFIG_NO_VLAN
+int vlan_init(struct hostapd_data *hapd);
+void vlan_deinit(struct hostapd_data *hapd);
+struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
+				       struct hostapd_vlan *vlan,
+				       int vlan_id,
+				       struct vlan_description *vlan_desc);
+int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id);
+#else /* CONFIG_NO_VLAN */
+static inline int vlan_init(struct hostapd_data *hapd)
+{
+	return 0;
+}
+
+static inline void vlan_deinit(struct hostapd_data *hapd)
+{
+}
+
+static inline struct hostapd_vlan *
+vlan_add_dynamic(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
+		 int vlan_id, struct vlan_description *vlan_desc)
+{
+	return NULL;
+}
+
+static inline int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id)
+{
+	return -1;
+}
+#endif /* CONFIG_NO_VLAN */
+
+#endif /* VLAN_INIT_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan_ioctl.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan_ioctl.c
new file mode 100644
index 0000000..987b612
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan_ioctl.c
@@ -0,0 +1,155 @@
+/*
+ * hostapd / VLAN ioctl API
+ * Copyright 2003, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <sys/ioctl.h>
+
+#include "utils/common.h"
+#include "common/linux_vlan.h"
+#include "vlan_util.h"
+
+
+int vlan_rem(const char *if_name)
+{
+	int fd;
+	struct vlan_ioctl_args if_request;
+
+	wpa_printf(MSG_DEBUG, "VLAN: vlan_rem(%s)", if_name);
+	if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) {
+		wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
+			   if_name);
+		return -1;
+	}
+
+	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+			   "failed: %s", __func__, strerror(errno));
+		return -1;
+	}
+
+	os_memset(&if_request, 0, sizeof(if_request));
+
+	os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1));
+	if_request.cmd = DEL_VLAN_CMD;
+
+	if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: DEL_VLAN_CMD failed for %s: "
+			   "%s", __func__, if_name, strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+	close(fd);
+	return 0;
+}
+
+
+/*
+	Add a vlan interface with VLAN ID 'vid' and tagged interface
+	'if_name'.
+
+	returns -1 on error
+	returns 1 if the interface already exists
+	returns 0 otherwise
+*/
+int vlan_add(const char *if_name, int vid, const char *vlan_if_name)
+{
+	int fd;
+	struct vlan_ioctl_args if_request;
+
+	wpa_printf(MSG_DEBUG, "VLAN: vlan_add(if_name=%s, vid=%d)",
+		   if_name, vid);
+	ifconfig_up(if_name);
+
+	if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) {
+		wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
+			   if_name);
+		return -1;
+	}
+
+	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+			   "failed: %s", __func__, strerror(errno));
+		return -1;
+	}
+
+	os_memset(&if_request, 0, sizeof(if_request));
+
+	/* Determine if a suitable vlan device already exists. */
+
+	os_snprintf(if_request.device1, sizeof(if_request.device1), "vlan%d",
+		    vid);
+
+	if_request.cmd = GET_VLAN_VID_CMD;
+
+	if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0 &&
+	    if_request.u.VID == vid) {
+		if_request.cmd = GET_VLAN_REALDEV_NAME_CMD;
+
+		if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0 &&
+		    os_strncmp(if_request.u.device2, if_name,
+			       sizeof(if_request.u.device2)) == 0) {
+			close(fd);
+			wpa_printf(MSG_DEBUG,
+				   "VLAN: vlan_add: if_name %s exists already",
+				   if_request.device1);
+			return 1;
+		}
+	}
+
+	/* A suitable vlan device does not already exist, add one. */
+
+	os_memset(&if_request, 0, sizeof(if_request));
+	os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1));
+	if_request.u.VID = vid;
+	if_request.cmd = ADD_VLAN_CMD;
+
+	if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
+		wpa_printf(MSG_ERROR,
+			   "VLAN: %s: ADD_VLAN_CMD failed for %s: %s",
+			   __func__, if_request.device1, strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+	close(fd);
+	return 0;
+}
+
+
+int vlan_set_name_type(unsigned int name_type)
+{
+	int fd;
+	struct vlan_ioctl_args if_request;
+
+	wpa_printf(MSG_DEBUG, "VLAN: vlan_set_name_type(name_type=%u)",
+		   name_type);
+	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		wpa_printf(MSG_ERROR,
+			   "VLAN: %s: socket(AF_INET,SOCK_STREAM) failed: %s",
+			   __func__, strerror(errno));
+		return -1;
+	}
+
+	os_memset(&if_request, 0, sizeof(if_request));
+
+	if_request.u.name_type = name_type;
+	if_request.cmd = SET_VLAN_NAME_TYPE_CMD;
+	if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
+		wpa_printf(MSG_ERROR,
+			   "VLAN: %s: SET_VLAN_NAME_TYPE_CMD name_type=%u failed: %s",
+			   __func__, name_type, strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+	close(fd);
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan_util.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan_util.c
new file mode 100644
index 0000000..56d1d3d
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan_util.c
@@ -0,0 +1,174 @@
+/*
+ * hostapd / VLAN netlink api
+ * Copyright (c) 2012, Michael Braun <michael-dev@fami-braun.de>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <netlink/route/link.h>
+#include <netlink/route/link/vlan.h>
+
+#include "utils/common.h"
+#include "vlan_util.h"
+
+/*
+ * Add a vlan interface with name 'vlan_if_name', VLAN ID 'vid' and
+ * tagged interface 'if_name'.
+ *
+ * returns -1 on error
+ * returns 1 if the interface already exists
+ * returns 0 otherwise
+*/
+int vlan_add(const char *if_name, int vid, const char *vlan_if_name)
+{
+	int err, ret = -1;
+	struct nl_sock *handle = NULL;
+	struct rtnl_link *rlink = NULL;
+	int if_idx = 0;
+
+	wpa_printf(MSG_DEBUG, "VLAN: vlan_add(if_name=%s, vid=%d, "
+		   "vlan_if_name=%s)", if_name, vid, vlan_if_name);
+
+	if ((os_strlen(if_name) + 1) > IFNAMSIZ) {
+		wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
+			   if_name);
+		return -1;
+	}
+
+	if ((os_strlen(vlan_if_name) + 1) > IFNAMSIZ) {
+		wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
+			   vlan_if_name);
+		return -1;
+	}
+
+	handle = nl_socket_alloc();
+	if (!handle) {
+		wpa_printf(MSG_ERROR, "VLAN: failed to open netlink socket");
+		goto vlan_add_error;
+	}
+
+	err = nl_connect(handle, NETLINK_ROUTE);
+	if (err < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink: %s",
+			   nl_geterror(err));
+		goto vlan_add_error;
+	}
+
+	err = rtnl_link_get_kernel(handle, 0, if_name, &rlink);
+	if (err < 0) {
+		/* link does not exist */
+		wpa_printf(MSG_ERROR, "VLAN: interface %s does not exist",
+			   if_name);
+		goto vlan_add_error;
+	}
+	if_idx = rtnl_link_get_ifindex(rlink);
+	rtnl_link_put(rlink);
+	rlink = NULL;
+
+	err = rtnl_link_get_kernel(handle, 0, vlan_if_name, &rlink);
+	if (err >= 0) {
+		/* link does exist */
+		rtnl_link_put(rlink);
+		rlink = NULL;
+		wpa_printf(MSG_ERROR, "VLAN: interface %s already exists",
+			   vlan_if_name);
+		ret = 1;
+		goto vlan_add_error;
+	}
+
+	rlink = rtnl_link_alloc();
+	if (!rlink) {
+		wpa_printf(MSG_ERROR, "VLAN: failed to allocate new link");
+		goto vlan_add_error;
+	}
+
+	err = rtnl_link_set_type(rlink, "vlan");
+	if (err < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: failed to set link type: %s",
+			   nl_geterror(err));
+		goto vlan_add_error;
+	}
+
+	rtnl_link_set_link(rlink, if_idx);
+	rtnl_link_set_name(rlink, vlan_if_name);
+
+	err = rtnl_link_vlan_set_id(rlink, vid);
+	if (err < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: failed to set link vlan id: %s",
+			   nl_geterror(err));
+		goto vlan_add_error;
+	}
+
+	err = rtnl_link_add(handle, rlink, NLM_F_CREATE);
+	if (err < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: failed to create link %s for "
+			   "vlan %d on %s (%d): %s",
+			   vlan_if_name, vid, if_name, if_idx,
+			   nl_geterror(err));
+		goto vlan_add_error;
+	}
+
+	ret = 0;
+
+vlan_add_error:
+	if (rlink)
+		rtnl_link_put(rlink);
+	if (handle)
+		nl_socket_free(handle);
+	return ret;
+}
+
+
+int vlan_rem(const char *if_name)
+{
+	int err, ret = -1;
+	struct nl_sock *handle = NULL;
+	struct rtnl_link *rlink = NULL;
+
+	wpa_printf(MSG_DEBUG, "VLAN: vlan_rem(if_name=%s)", if_name);
+
+	handle = nl_socket_alloc();
+	if (!handle) {
+		wpa_printf(MSG_ERROR, "VLAN: failed to open netlink socket");
+		goto vlan_rem_error;
+	}
+
+	err = nl_connect(handle, NETLINK_ROUTE);
+	if (err < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink: %s",
+			   nl_geterror(err));
+		goto vlan_rem_error;
+	}
+
+	err = rtnl_link_get_kernel(handle, 0, if_name, &rlink);
+	if (err < 0) {
+		/* link does not exist */
+		wpa_printf(MSG_ERROR, "VLAN: interface %s does not exists",
+			   if_name);
+		goto vlan_rem_error;
+	}
+
+	err = rtnl_link_delete(handle, rlink);
+	if (err < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: failed to remove link %s: %s",
+			   if_name, nl_geterror(err));
+		goto vlan_rem_error;
+	}
+
+	ret = 0;
+
+vlan_rem_error:
+	if (rlink)
+		rtnl_link_put(rlink);
+	if (handle)
+		nl_socket_free(handle);
+	return ret;
+}
+
+
+int vlan_set_name_type(unsigned int name_type)
+{
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan_util.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan_util.h
new file mode 100644
index 0000000..2446859
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/vlan_util.h
@@ -0,0 +1,31 @@
+/*
+ * hostapd / VLAN netlink/ioctl api
+ * Copyright (c) 2012, Michael Braun <michael-dev@fami-braun.de>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef VLAN_UTIL_H
+#define VLAN_UTIL_H
+
+struct hostapd_data;
+struct hostapd_vlan;
+struct full_dynamic_vlan;
+
+int vlan_add(const char *if_name, int vid, const char *vlan_if_name);
+int vlan_rem(const char *if_name);
+int vlan_set_name_type(unsigned int name_type);
+
+int ifconfig_helper(const char *if_name, int up);
+int ifconfig_up(const char *if_name);
+int iface_exists(const char *ifname);
+int vlan_if_remove(struct hostapd_data *hapd, struct hostapd_vlan *vlan);
+
+struct full_dynamic_vlan *
+full_dynamic_vlan_init(struct hostapd_data *hapd);
+void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv);
+void vlan_newlink(const char *ifname, struct hostapd_data *hapd);
+void vlan_dellink(const char *ifname, struct hostapd_data *hapd);
+
+#endif /* VLAN_UTIL_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wmm.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wmm.c
new file mode 100644
index 0000000..9ebb01e
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wmm.c
@@ -0,0 +1,392 @@
+/*
+ * hostapd / WMM (Wi-Fi Multimedia)
+ * Copyright 2002-2003, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "hostapd.h"
+#include "ieee802_11.h"
+#include "sta_info.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "wmm.h"
+
+#ifndef MIN
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+#ifndef MAX
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+#endif
+
+
+static inline u8 wmm_aci_aifsn(int aifsn, int acm, int aci)
+{
+	u8 ret;
+	ret = (aifsn << WMM_AC_AIFNS_SHIFT) & WMM_AC_AIFSN_MASK;
+	if (acm)
+		ret |= WMM_AC_ACM;
+	ret |= (aci << WMM_AC_ACI_SHIFT) & WMM_AC_ACI_MASK;
+	return ret;
+}
+
+
+static inline u8 wmm_ecw(int ecwmin, int ecwmax)
+{
+	return ((ecwmin << WMM_AC_ECWMIN_SHIFT) & WMM_AC_ECWMIN_MASK) |
+		((ecwmax << WMM_AC_ECWMAX_SHIFT) & WMM_AC_ECWMAX_MASK);
+}
+
+
+static void
+wmm_set_regulatory_limit(const struct hostapd_wmm_ac_params *wmm_conf,
+			 struct hostapd_wmm_ac_params *wmm,
+			 const struct hostapd_wmm_rule *wmm_reg)
+{
+	int ac;
+
+	for (ac = 0; ac < WMM_AC_NUM; ac++) {
+		wmm[ac].cwmin = MAX(wmm_conf[ac].cwmin, wmm_reg[ac].min_cwmin);
+		wmm[ac].cwmax = MAX(wmm_conf[ac].cwmax, wmm_reg[ac].min_cwmax);
+		wmm[ac].aifs = MAX(wmm_conf[ac].aifs, wmm_reg[ac].min_aifs);
+		wmm[ac].txop_limit =
+			MIN(wmm_conf[ac].txop_limit, wmm_reg[ac].max_txop);
+		wmm[ac].admission_control_mandatory =
+			wmm_conf[ac].admission_control_mandatory;
+	}
+}
+
+
+/*
+ * Calculate WMM regulatory limit if any.
+ */
+static void wmm_calc_regulatory_limit(struct hostapd_data *hapd,
+				      struct hostapd_wmm_ac_params *acp)
+{
+	struct hostapd_hw_modes *mode = hapd->iface->current_mode;
+	int c;
+
+	os_memcpy(acp, hapd->iconf->wmm_ac_params,
+		  sizeof(hapd->iconf->wmm_ac_params));
+
+	for (c = 0; mode && c < mode->num_channels; c++) {
+		struct hostapd_channel_data *chan = &mode->channels[c];
+
+		if (chan->freq != hapd->iface->freq)
+			continue;
+
+		if (chan->wmm_rules_valid)
+			wmm_set_regulatory_limit(hapd->iconf->wmm_ac_params,
+						 acp, chan->wmm_rules);
+		break;
+	}
+
+	/*
+	 * Check if we need to update set count. Since both were initialized to
+	 * zero we can compare the whole array in one shot.
+	 */
+	if (os_memcmp(acp, hapd->iface->prev_wmm,
+		      sizeof(hapd->iconf->wmm_ac_params)) != 0) {
+		os_memcpy(hapd->iface->prev_wmm, acp,
+			  sizeof(hapd->iconf->wmm_ac_params));
+		hapd->parameter_set_count++;
+	}
+}
+
+
+/*
+ * Add WMM Parameter Element to Beacon, Probe Response, and (Re)Association
+ * Response frames.
+ */
+u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 *pos = eid;
+	struct wmm_parameter_element *wmm =
+		(struct wmm_parameter_element *) (pos + 2);
+	struct hostapd_wmm_ac_params wmmp[WMM_AC_NUM];
+	int e;
+
+	os_memset(wmmp, 0, sizeof(wmmp));
+
+	if (!hapd->conf->wmm_enabled)
+		return eid;
+	wmm_calc_regulatory_limit(hapd, wmmp);
+	eid[0] = WLAN_EID_VENDOR_SPECIFIC;
+	wmm->oui[0] = 0x00;
+	wmm->oui[1] = 0x50;
+	wmm->oui[2] = 0xf2;
+	wmm->oui_type = WMM_OUI_TYPE;
+	wmm->oui_subtype = WMM_OUI_SUBTYPE_PARAMETER_ELEMENT;
+	wmm->version = WMM_VERSION;
+	wmm->qos_info = hapd->parameter_set_count & 0xf;
+
+	if (hapd->conf->wmm_uapsd &&
+	    (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD))
+		wmm->qos_info |= 0x80;
+
+	wmm->reserved = 0;
+
+	/* fill in a parameter set record for each AC */
+	for (e = 0; e < 4; e++) {
+		struct wmm_ac_parameter *ac = &wmm->ac[e];
+		struct hostapd_wmm_ac_params *acp = &wmmp[e];
+
+		ac->aci_aifsn = wmm_aci_aifsn(acp->aifs,
+					      acp->admission_control_mandatory,
+					      e);
+		ac->cw = wmm_ecw(acp->cwmin, acp->cwmax);
+		ac->txop_limit = host_to_le16(acp->txop_limit);
+	}
+
+	pos = (u8 *) (wmm + 1);
+	eid[1] = pos - eid - 2; /* element length */
+
+	return pos;
+}
+
+
+/*
+ * This function is called when a station sends an association request with
+ * WMM info element. The function returns 1 on success or 0 on any error in WMM
+ * element. eid does not include Element ID and Length octets.
+ */
+int hostapd_eid_wmm_valid(struct hostapd_data *hapd, const u8 *eid, size_t len)
+{
+	struct wmm_information_element *wmm;
+
+	wpa_hexdump(MSG_MSGDUMP, "WMM IE", eid, len);
+
+	if (len < sizeof(struct wmm_information_element)) {
+		wpa_printf(MSG_DEBUG, "Too short WMM IE (len=%lu)",
+			   (unsigned long) len);
+		return 0;
+	}
+
+	wmm = (struct wmm_information_element *) eid;
+	wpa_printf(MSG_DEBUG, "Validating WMM IE: OUI %02x:%02x:%02x  "
+		   "OUI type %d  OUI sub-type %d  version %d  QoS info 0x%x",
+		   wmm->oui[0], wmm->oui[1], wmm->oui[2], wmm->oui_type,
+		   wmm->oui_subtype, wmm->version, wmm->qos_info);
+	if (wmm->oui_subtype != WMM_OUI_SUBTYPE_INFORMATION_ELEMENT ||
+	    wmm->version != WMM_VERSION) {
+		wpa_printf(MSG_DEBUG, "Unsupported WMM IE Subtype/Version");
+		return 0;
+	}
+
+	return 1;
+}
+
+
+static void wmm_send_action(struct hostapd_data *hapd, const u8 *addr,
+			    const struct wmm_tspec_element *tspec,
+			    u8 action_code, u8 dialogue_token, u8 status_code)
+{
+	u8 buf[256];
+	struct ieee80211_mgmt *m = (struct ieee80211_mgmt *) buf;
+	struct wmm_tspec_element *t = (struct wmm_tspec_element *)
+		m->u.action.u.wmm_action.variable;
+	int len;
+
+	hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "action response - reason %d", status_code);
+	os_memset(buf, 0, sizeof(buf));
+	m->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					WLAN_FC_STYPE_ACTION);
+	os_memcpy(m->da, addr, ETH_ALEN);
+	os_memcpy(m->sa, hapd->own_addr, ETH_ALEN);
+	os_memcpy(m->bssid, hapd->own_addr, ETH_ALEN);
+	m->u.action.category = WLAN_ACTION_WMM;
+	m->u.action.u.wmm_action.action_code = action_code;
+	m->u.action.u.wmm_action.dialog_token = dialogue_token;
+	m->u.action.u.wmm_action.status_code = status_code;
+	os_memcpy(t, tspec, sizeof(struct wmm_tspec_element));
+	len = ((u8 *) (t + 1)) - buf;
+
+	if (hostapd_drv_send_mlme(hapd, m, len, 0, NULL, 0, 0) < 0)
+		wpa_printf(MSG_INFO, "wmm_send_action: send failed");
+}
+
+
+int wmm_process_tspec(struct wmm_tspec_element *tspec)
+{
+	u64 medium_time;
+	unsigned int pps, duration;
+	unsigned int up, psb, dir, tid;
+	u16 val, surplus;
+
+	up = (tspec->ts_info[1] >> 3) & 0x07;
+	psb = (tspec->ts_info[1] >> 2) & 0x01;
+	dir = (tspec->ts_info[0] >> 5) & 0x03;
+	tid = (tspec->ts_info[0] >> 1) & 0x0f;
+	wpa_printf(MSG_DEBUG, "WMM: TS Info: UP=%d PSB=%d Direction=%d TID=%d",
+		   up, psb, dir, tid);
+	val = le_to_host16(tspec->nominal_msdu_size);
+	wpa_printf(MSG_DEBUG, "WMM: Nominal MSDU Size: %d%s",
+		   val & 0x7fff, val & 0x8000 ? " (fixed)" : "");
+	wpa_printf(MSG_DEBUG, "WMM: Mean Data Rate: %u bps",
+		   le_to_host32(tspec->mean_data_rate));
+	wpa_printf(MSG_DEBUG, "WMM: Minimum PHY Rate: %u bps",
+		   le_to_host32(tspec->minimum_phy_rate));
+	val = le_to_host16(tspec->surplus_bandwidth_allowance);
+	wpa_printf(MSG_DEBUG, "WMM: Surplus Bandwidth Allowance: %u.%04u",
+		   val >> 13, 10000 * (val & 0x1fff) / 0x2000);
+
+	val = le_to_host16(tspec->nominal_msdu_size);
+	if (val == 0) {
+		wpa_printf(MSG_DEBUG, "WMM: Invalid Nominal MSDU Size (0)");
+		return WMM_ADDTS_STATUS_INVALID_PARAMETERS;
+	}
+	/* pps = Ceiling((Mean Data Rate / 8) / Nominal MSDU Size) */
+	pps = ((le_to_host32(tspec->mean_data_rate) / 8) + val - 1) / val;
+	wpa_printf(MSG_DEBUG, "WMM: Packets-per-second estimate for TSPEC: %d",
+		   pps);
+
+	if (le_to_host32(tspec->minimum_phy_rate) < 1000000) {
+		wpa_printf(MSG_DEBUG, "WMM: Too small Minimum PHY Rate");
+		return WMM_ADDTS_STATUS_INVALID_PARAMETERS;
+	}
+
+	duration = (le_to_host16(tspec->nominal_msdu_size) & 0x7fff) * 8 /
+		(le_to_host32(tspec->minimum_phy_rate) / 1000000) +
+		50 /* FIX: proper SIFS + ACK duration */;
+
+	/* unsigned binary number with an implicit binary point after the
+	 * leftmost 3 bits, i.e., 0x2000 = 1.0 */
+	surplus = le_to_host16(tspec->surplus_bandwidth_allowance);
+	if (surplus <= 0x2000) {
+		wpa_printf(MSG_DEBUG, "WMM: Surplus Bandwidth Allowance not "
+			   "greater than unity");
+		return WMM_ADDTS_STATUS_INVALID_PARAMETERS;
+	}
+
+	medium_time = (u64) surplus * pps * duration / 0x2000;
+	wpa_printf(MSG_DEBUG, "WMM: Estimated medium time: %lu",
+		   (unsigned long) medium_time);
+
+	/*
+	 * TODO: store list of granted (and still active) TSPECs and check
+	 * whether there is available medium time for this request. For now,
+	 * just refuse requests that would by themselves take very large
+	 * portion of the available bandwidth.
+	 */
+	if (medium_time > 750000) {
+		wpa_printf(MSG_DEBUG, "WMM: Refuse TSPEC request for over "
+			   "75%% of available bandwidth");
+		return WMM_ADDTS_STATUS_REFUSED;
+	}
+
+	/* Convert to 32 microseconds per second unit */
+	tspec->medium_time = host_to_le16(medium_time / 32);
+
+	return WMM_ADDTS_STATUS_ADMISSION_ACCEPTED;
+}
+
+
+static void wmm_addts_req(struct hostapd_data *hapd,
+			  const struct ieee80211_mgmt *mgmt,
+			  const struct wmm_tspec_element *tspec, size_t len)
+{
+	const u8 *end = ((const u8 *) mgmt) + len;
+	int res;
+	struct wmm_tspec_element tspec_resp;
+
+	if ((const u8 *) (tspec + 1) > end) {
+		wpa_printf(MSG_DEBUG, "WMM: TSPEC overflow in ADDTS Request");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "WMM: ADDTS Request (Dialog Token %d) for TSPEC "
+		   "from " MACSTR,
+		   mgmt->u.action.u.wmm_action.dialog_token,
+		   MAC2STR(mgmt->sa));
+
+	os_memcpy(&tspec_resp, tspec, sizeof(struct wmm_tspec_element));
+	res = wmm_process_tspec(&tspec_resp);
+	wpa_printf(MSG_DEBUG, "WMM: ADDTS processing result: %d", res);
+
+	wmm_send_action(hapd, mgmt->sa, &tspec_resp, WMM_ACTION_CODE_ADDTS_RESP,
+			mgmt->u.action.u.wmm_action.dialog_token, res);
+}
+
+
+void hostapd_wmm_action(struct hostapd_data *hapd,
+			const struct ieee80211_mgmt *mgmt, size_t len)
+{
+	int action_code;
+	int left = len - IEEE80211_HDRLEN - 4;
+	const u8 *pos = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 4;
+	struct ieee802_11_elems elems;
+	struct sta_info *sta = ap_get_sta(hapd, mgmt->sa);
+
+	/* check that the request comes from a valid station */
+	if (!sta ||
+	    (sta->flags & (WLAN_STA_ASSOC | WLAN_STA_WMM)) !=
+	    (WLAN_STA_ASSOC | WLAN_STA_WMM)) {
+		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "wmm action received is not from associated wmm"
+			       " station");
+		/* TODO: respond with action frame refused status code */
+		return;
+	}
+
+	if (left < 0)
+		return; /* not a valid WMM Action frame */
+
+	/* extract the tspec info element */
+	if (ieee802_11_parse_elems(pos, left, &elems, 1) == ParseFailed) {
+		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "hostapd_wmm_action - could not parse wmm "
+			       "action");
+		/* TODO: respond with action frame invalid parameters status
+		 * code */
+		return;
+	}
+
+	if (!elems.wmm_tspec ||
+	    elems.wmm_tspec_len != (sizeof(struct wmm_tspec_element) - 2)) {
+		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "hostapd_wmm_action - missing or wrong length "
+			       "tspec");
+		/* TODO: respond with action frame invalid parameters status
+		 * code */
+		return;
+	}
+
+	/* TODO: check the request is for an AC with ACM set, if not, refuse
+	 * request */
+
+	action_code = mgmt->u.action.u.wmm_action.action_code;
+	switch (action_code) {
+	case WMM_ACTION_CODE_ADDTS_REQ:
+		wmm_addts_req(hapd, mgmt, (struct wmm_tspec_element *)
+			      (elems.wmm_tspec - 2), len);
+		return;
+#if 0
+	/* TODO: needed for client implementation */
+	case WMM_ACTION_CODE_ADDTS_RESP:
+		wmm_setup_request(hapd, mgmt, len);
+		return;
+	/* TODO: handle station teardown requests */
+	case WMM_ACTION_CODE_DELTS:
+		wmm_teardown(hapd, mgmt, len);
+		return;
+#endif
+	}
+
+	hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_DEBUG,
+		       "hostapd_wmm_action - unknown action code %d",
+		       action_code);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wmm.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wmm.h
new file mode 100644
index 0000000..b70b863
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wmm.h
@@ -0,0 +1,23 @@
+/*
+ * hostapd / WMM (Wi-Fi Multimedia)
+ * Copyright 2002-2003, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WME_H
+#define WME_H
+
+struct ieee80211_mgmt;
+struct wmm_tspec_element;
+
+u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid);
+int hostapd_eid_wmm_valid(struct hostapd_data *hapd, const u8 *eid,
+			  size_t len);
+void hostapd_wmm_action(struct hostapd_data *hapd,
+			const struct ieee80211_mgmt *mgmt, size_t len);
+int wmm_process_tspec(struct wmm_tspec_element *tspec);
+
+#endif /* WME_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wnm_ap.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wnm_ap.c
new file mode 100644
index 0000000..ba1dd2e
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wnm_ap.c
@@ -0,0 +1,1059 @@
+/*
+ * hostapd - WNM
+ * Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
+#include "common/ocv.h"
+#include "ap/hostapd.h"
+#include "ap/sta_info.h"
+#include "ap/ap_config.h"
+#include "ap/ap_drv_ops.h"
+#include "ap/wpa_auth.h"
+#include "mbo_ap.h"
+#include "wnm_ap.h"
+
+#define MAX_TFS_IE_LEN  1024
+
+
+/* get the TFS IE from driver */
+static int ieee80211_11_get_tfs_ie(struct hostapd_data *hapd, const u8 *addr,
+				   u8 *buf, u16 *buf_len, enum wnm_oper oper)
+{
+	wpa_printf(MSG_DEBUG, "%s: TFS get operation %d", __func__, oper);
+
+	return hostapd_drv_wnm_oper(hapd, oper, addr, buf, buf_len);
+}
+
+
+/* set the TFS IE to driver */
+static int ieee80211_11_set_tfs_ie(struct hostapd_data *hapd, const u8 *addr,
+				   u8 *buf, u16 *buf_len, enum wnm_oper oper)
+{
+	wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper);
+
+	return hostapd_drv_wnm_oper(hapd, oper, addr, buf, buf_len);
+}
+
+
+/* MLME-SLEEPMODE.response */
+static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
+					 const u8 *addr, u8 dialog_token,
+					 u8 action_type, u16 intval)
+{
+	struct ieee80211_mgmt *mgmt;
+	int res;
+	size_t len;
+	size_t gtk_elem_len = 0;
+	size_t igtk_elem_len = 0;
+	size_t bigtk_elem_len = 0;
+	struct wnm_sleep_element wnmsleep_ie;
+	u8 *wnmtfs_ie, *oci_ie;
+	u8 wnmsleep_ie_len, oci_ie_len;
+	u16 wnmtfs_ie_len;
+	u8 *pos;
+	struct sta_info *sta;
+	enum wnm_oper tfs_oper = action_type == WNM_SLEEP_MODE_ENTER ?
+		WNM_SLEEP_TFS_RESP_IE_ADD : WNM_SLEEP_TFS_RESP_IE_NONE;
+
+	sta = ap_get_sta(hapd, addr);
+	if (sta == NULL) {
+		wpa_printf(MSG_DEBUG, "%s: station not found", __func__);
+		return -EINVAL;
+	}
+
+	/* WNM-Sleep Mode IE */
+	os_memset(&wnmsleep_ie, 0, sizeof(struct wnm_sleep_element));
+	wnmsleep_ie_len = sizeof(struct wnm_sleep_element);
+	wnmsleep_ie.eid = WLAN_EID_WNMSLEEP;
+	wnmsleep_ie.len = wnmsleep_ie_len - 2;
+	wnmsleep_ie.action_type = action_type;
+	wnmsleep_ie.status = WNM_STATUS_SLEEP_ACCEPT;
+	wnmsleep_ie.intval = host_to_le16(intval);
+
+	/* TFS IE(s) */
+	wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN);
+	if (wnmtfs_ie == NULL)
+		return -1;
+	if (ieee80211_11_get_tfs_ie(hapd, addr, wnmtfs_ie, &wnmtfs_ie_len,
+				    tfs_oper)) {
+		wnmtfs_ie_len = 0;
+		os_free(wnmtfs_ie);
+		wnmtfs_ie = NULL;
+	}
+
+	oci_ie = NULL;
+	oci_ie_len = 0;
+#ifdef CONFIG_OCV
+	if (action_type == WNM_SLEEP_MODE_EXIT &&
+	    wpa_auth_uses_ocv(sta->wpa_sm)) {
+		struct wpa_channel_info ci;
+
+		if (hostapd_drv_channel_info(hapd, &ci) != 0) {
+			wpa_printf(MSG_WARNING,
+				   "Failed to get channel info for OCI element in WNM-Sleep Mode frame");
+			os_free(wnmtfs_ie);
+			return -1;
+		}
+#ifdef CONFIG_TESTING_OPTIONS
+		if (hapd->conf->oci_freq_override_wnm_sleep) {
+			wpa_printf(MSG_INFO,
+				   "TEST: Override OCI frequency %d -> %u MHz",
+				   ci.frequency,
+				   hapd->conf->oci_freq_override_wnm_sleep);
+			ci.frequency = hapd->conf->oci_freq_override_wnm_sleep;
+		}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+		oci_ie_len = OCV_OCI_EXTENDED_LEN;
+		oci_ie = os_zalloc(oci_ie_len);
+		if (!oci_ie) {
+			wpa_printf(MSG_WARNING,
+				   "Failed to allocate buffer for OCI element in WNM-Sleep Mode frame");
+			os_free(wnmtfs_ie);
+			return -1;
+		}
+
+		if (ocv_insert_extended_oci(&ci, oci_ie) < 0) {
+			os_free(wnmtfs_ie);
+			os_free(oci_ie);
+			return -1;
+		}
+	}
+#endif /* CONFIG_OCV */
+
+#define MAX_GTK_SUBELEM_LEN 45
+#define MAX_IGTK_SUBELEM_LEN 26
+#define MAX_BIGTK_SUBELEM_LEN 26
+	mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len +
+			 MAX_GTK_SUBELEM_LEN + MAX_IGTK_SUBELEM_LEN +
+			 MAX_BIGTK_SUBELEM_LEN +
+			 oci_ie_len);
+	if (mgmt == NULL) {
+		wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
+			   "WNM-Sleep Response action frame");
+		res = -1;
+		goto fail;
+	}
+	os_memcpy(mgmt->da, addr, ETH_ALEN);
+	os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+	os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					   WLAN_FC_STYPE_ACTION);
+	mgmt->u.action.category = WLAN_ACTION_WNM;
+	mgmt->u.action.u.wnm_sleep_resp.action = WNM_SLEEP_MODE_RESP;
+	mgmt->u.action.u.wnm_sleep_resp.dialogtoken = dialog_token;
+	pos = (u8 *)mgmt->u.action.u.wnm_sleep_resp.variable;
+	/* add key data if MFP is enabled */
+	if (!wpa_auth_uses_mfp(sta->wpa_sm) ||
+	    hapd->conf->wnm_sleep_mode_no_keys ||
+	    action_type != WNM_SLEEP_MODE_EXIT) {
+		mgmt->u.action.u.wnm_sleep_resp.keydata_len = 0;
+	} else {
+		gtk_elem_len = wpa_wnmsleep_gtk_subelem(sta->wpa_sm, pos);
+		pos += gtk_elem_len;
+		wpa_printf(MSG_DEBUG, "Pass 4, gtk_len = %d",
+			   (int) gtk_elem_len);
+		res = wpa_wnmsleep_igtk_subelem(sta->wpa_sm, pos);
+		if (res < 0)
+			goto fail;
+		igtk_elem_len = res;
+		pos += igtk_elem_len;
+		wpa_printf(MSG_DEBUG, "Pass 4 igtk_len = %d",
+			   (int) igtk_elem_len);
+		if (hapd->conf->beacon_prot &&
+		    (hapd->iface->drv_flags &
+		     WPA_DRIVER_FLAGS_BEACON_PROTECTION)) {
+			res = wpa_wnmsleep_bigtk_subelem(sta->wpa_sm, pos);
+			if (res < 0)
+				goto fail;
+			bigtk_elem_len = res;
+			pos += bigtk_elem_len;
+			wpa_printf(MSG_DEBUG, "Pass 4 bigtk_len = %d",
+				   (int) bigtk_elem_len);
+		}
+
+		WPA_PUT_LE16((u8 *)
+			     &mgmt->u.action.u.wnm_sleep_resp.keydata_len,
+			     gtk_elem_len + igtk_elem_len + bigtk_elem_len);
+	}
+	os_memcpy(pos, &wnmsleep_ie, wnmsleep_ie_len);
+	/* copy TFS IE here */
+	pos += wnmsleep_ie_len;
+	if (wnmtfs_ie) {
+		os_memcpy(pos, wnmtfs_ie, wnmtfs_ie_len);
+		pos += wnmtfs_ie_len;
+	}
+#ifdef CONFIG_OCV
+	/* copy OCV OCI here */
+	if (oci_ie_len > 0)
+		os_memcpy(pos, oci_ie, oci_ie_len);
+#endif /* CONFIG_OCV */
+
+	len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_resp) + gtk_elem_len +
+		igtk_elem_len + bigtk_elem_len +
+		wnmsleep_ie_len + wnmtfs_ie_len + oci_ie_len;
+
+	/* In driver, response frame should be forced to sent when STA is in
+	 * PS mode */
+	res = hostapd_drv_send_action(hapd, hapd->iface->freq, 0,
+				      mgmt->da, &mgmt->u.action.category, len);
+
+	if (!res) {
+		wpa_printf(MSG_DEBUG, "Successfully send WNM-Sleep Response "
+			   "frame");
+
+		/* when entering wnmsleep
+		 * 1. pause the node in driver
+		 * 2. mark the node so that AP won't update GTK/IGTK/BIGTK
+		 * during WNM Sleep
+		 */
+		if (wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT &&
+		    wnmsleep_ie.action_type == WNM_SLEEP_MODE_ENTER) {
+			sta->flags |= WLAN_STA_WNM_SLEEP_MODE;
+			hostapd_drv_wnm_oper(hapd, WNM_SLEEP_ENTER_CONFIRM,
+					     addr, NULL, NULL);
+			wpa_set_wnmsleep(sta->wpa_sm, 1);
+		}
+		/* when exiting wnmsleep
+		 * 1. unmark the node
+		 * 2. start GTK/IGTK/BIGTK update if MFP is not used
+		 * 3. unpause the node in driver
+		 */
+		if ((wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT ||
+		     wnmsleep_ie.status ==
+		     WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) &&
+		    wnmsleep_ie.action_type == WNM_SLEEP_MODE_EXIT) {
+			sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
+			wpa_set_wnmsleep(sta->wpa_sm, 0);
+			hostapd_drv_wnm_oper(hapd, WNM_SLEEP_EXIT_CONFIRM,
+					     addr, NULL, NULL);
+			if (!wpa_auth_uses_mfp(sta->wpa_sm) ||
+			    hapd->conf->wnm_sleep_mode_no_keys)
+				wpa_wnmsleep_rekey_gtk(sta->wpa_sm);
+		}
+	} else
+		wpa_printf(MSG_DEBUG, "Fail to send WNM-Sleep Response frame");
+
+#undef MAX_GTK_SUBELEM_LEN
+#undef MAX_IGTK_SUBELEM_LEN
+#undef MAX_BIGTK_SUBELEM_LEN
+fail:
+	os_free(wnmtfs_ie);
+	os_free(oci_ie);
+	os_free(mgmt);
+	return res;
+}
+
+
+static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd,
+				       const u8 *addr, const u8 *frm, int len)
+{
+	/* Dialog Token [1] | WNM-Sleep Mode IE | TFS Response IE */
+	const u8 *pos = frm;
+	u8 dialog_token;
+	struct wnm_sleep_element *wnmsleep_ie = NULL;
+	/* multiple TFS Req IE (assuming consecutive) */
+	u8 *tfsreq_ie_start = NULL;
+	u8 *tfsreq_ie_end = NULL;
+	u16 tfsreq_ie_len = 0;
+#ifdef CONFIG_OCV
+	struct sta_info *sta;
+	const u8 *oci_ie = NULL;
+	u8 oci_ie_len = 0;
+#endif /* CONFIG_OCV */
+
+	if (!hapd->conf->wnm_sleep_mode) {
+		wpa_printf(MSG_DEBUG, "Ignore WNM-Sleep Mode Request from "
+			   MACSTR " since WNM-Sleep Mode is disabled",
+			   MAC2STR(addr));
+		return;
+	}
+
+	if (len < 1) {
+		wpa_printf(MSG_DEBUG,
+			   "WNM: Ignore too short WNM-Sleep Mode Request from "
+			   MACSTR, MAC2STR(addr));
+		return;
+	}
+
+	dialog_token = *pos++;
+	while (pos + 1 < frm + len) {
+		u8 ie_len = pos[1];
+		if (pos + 2 + ie_len > frm + len)
+			break;
+		if (*pos == WLAN_EID_WNMSLEEP &&
+		    ie_len >= (int) sizeof(*wnmsleep_ie) - 2)
+			wnmsleep_ie = (struct wnm_sleep_element *) pos;
+		else if (*pos == WLAN_EID_TFS_REQ) {
+			if (!tfsreq_ie_start)
+				tfsreq_ie_start = (u8 *) pos;
+			tfsreq_ie_end = (u8 *) pos;
+#ifdef CONFIG_OCV
+		} else if (*pos == WLAN_EID_EXTENSION && ie_len >= 1 &&
+			   pos[2] == WLAN_EID_EXT_OCV_OCI) {
+			oci_ie = pos + 3;
+			oci_ie_len = ie_len - 1;
+#endif /* CONFIG_OCV */
+		} else
+			wpa_printf(MSG_DEBUG, "WNM: EID %d not recognized",
+				   *pos);
+		pos += ie_len + 2;
+	}
+
+	if (!wnmsleep_ie) {
+		wpa_printf(MSG_DEBUG, "No WNM-Sleep IE found");
+		return;
+	}
+
+#ifdef CONFIG_OCV
+	sta = ap_get_sta(hapd, addr);
+	if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT &&
+	    sta && wpa_auth_uses_ocv(sta->wpa_sm)) {
+		struct wpa_channel_info ci;
+
+		if (hostapd_drv_channel_info(hapd, &ci) != 0) {
+			wpa_printf(MSG_WARNING,
+				   "Failed to get channel info to validate received OCI in WNM-Sleep Mode frame");
+			return;
+		}
+
+		if (ocv_verify_tx_params(oci_ie, oci_ie_len, &ci,
+					 channel_width_to_int(ci.chanwidth),
+					 ci.seg1_idx) != OCI_SUCCESS) {
+			wpa_msg(hapd, MSG_WARNING, "WNM: OCV failed: %s",
+				ocv_errorstr);
+			return;
+		}
+	}
+#endif /* CONFIG_OCV */
+
+	if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER &&
+	    tfsreq_ie_start && tfsreq_ie_end &&
+	    tfsreq_ie_end - tfsreq_ie_start >= 0) {
+		tfsreq_ie_len = (tfsreq_ie_end + tfsreq_ie_end[1] + 2) -
+			tfsreq_ie_start;
+		wpa_printf(MSG_DEBUG, "TFS Req IE(s) found");
+		/* pass the TFS Req IE(s) to driver for processing */
+		if (ieee80211_11_set_tfs_ie(hapd, addr, tfsreq_ie_start,
+					    &tfsreq_ie_len,
+					    WNM_SLEEP_TFS_REQ_IE_SET))
+			wpa_printf(MSG_DEBUG, "Fail to set TFS Req IE");
+	}
+
+	ieee802_11_send_wnmsleep_resp(hapd, addr, dialog_token,
+				      wnmsleep_ie->action_type,
+				      le_to_host16(wnmsleep_ie->intval));
+
+	if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) {
+		/* clear the tfs after sending the resp frame */
+		ieee80211_11_set_tfs_ie(hapd, addr, tfsreq_ie_start,
+					&tfsreq_ie_len, WNM_SLEEP_TFS_IE_DEL);
+	}
+}
+
+
+static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd,
+						  const u8 *addr,
+						  u8 dialog_token)
+{
+	struct ieee80211_mgmt *mgmt;
+	size_t len;
+	u8 *pos;
+	int res;
+
+	mgmt = os_zalloc(sizeof(*mgmt));
+	if (mgmt == NULL)
+		return -1;
+	os_memcpy(mgmt->da, addr, ETH_ALEN);
+	os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+	os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					   WLAN_FC_STYPE_ACTION);
+	mgmt->u.action.category = WLAN_ACTION_WNM;
+	mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
+	mgmt->u.action.u.bss_tm_req.dialog_token = dialog_token;
+	mgmt->u.action.u.bss_tm_req.req_mode = 0;
+	mgmt->u.action.u.bss_tm_req.disassoc_timer = host_to_le16(0);
+	mgmt->u.action.u.bss_tm_req.validity_interval = 1;
+	pos = mgmt->u.action.u.bss_tm_req.variable;
+
+	hapd->openwrt_stats.wnm.bss_transition_request_tx++;
+	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to "
+		   MACSTR " dialog_token=%u req_mode=0x%x disassoc_timer=%u "
+		   "validity_interval=%u",
+		   MAC2STR(addr), dialog_token,
+		   mgmt->u.action.u.bss_tm_req.req_mode,
+		   le_to_host16(mgmt->u.action.u.bss_tm_req.disassoc_timer),
+		   mgmt->u.action.u.bss_tm_req.validity_interval);
+
+	len = pos - &mgmt->u.action.category;
+	res = hostapd_drv_send_action(hapd, hapd->iface->freq, 0,
+				      mgmt->da, &mgmt->u.action.category, len);
+	os_free(mgmt);
+	return res;
+}
+
+
+static void ieee802_11_rx_bss_trans_mgmt_query(struct hostapd_data *hapd,
+					       const u8 *addr, const u8 *frm,
+					       size_t len)
+{
+	u8 dialog_token, reason;
+	const u8 *pos, *end;
+	int enabled = hapd->conf->bss_transition;
+	char *hex = NULL;
+	size_t hex_len;
+
+#ifdef CONFIG_MBO
+	if (hapd->conf->mbo_enabled)
+		enabled = 1;
+#endif /* CONFIG_MBO */
+	if (!enabled) {
+		wpa_printf(MSG_DEBUG,
+			   "Ignore BSS Transition Management Query from "
+			   MACSTR
+			   " since BSS Transition Management is disabled",
+			   MAC2STR(addr));
+		return;
+	}
+
+	if (len < 2) {
+		wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Query from "
+			   MACSTR, MAC2STR(addr));
+		return;
+	}
+
+	pos = frm;
+	end = pos + len;
+	dialog_token = *pos++;
+	reason = *pos++;
+
+	wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Query from "
+		   MACSTR " dialog_token=%u reason=%u",
+		   MAC2STR(addr), dialog_token, reason);
+
+	wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries",
+		    pos, end - pos);
+
+	hex_len = 2 * (end - pos) + 1;
+	if (hex_len > 1) {
+		hex = os_malloc(hex_len);
+		if (hex)
+			wpa_snprintf_hex(hex, hex_len, pos, end - pos);
+	}
+	wpa_msg(hapd->msg_ctx, MSG_INFO,
+		BSS_TM_QUERY MACSTR " reason=%u%s%s",
+		MAC2STR(addr), reason, hex ? " neighbor=" : "", hex);
+	os_free(hex);
+
+	if (!hostapd_ubus_notify_bss_transition_query(hapd, addr, dialog_token, reason, pos, end - pos))
+		ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token);
+}
+
+
+void ap_sta_reset_steer_flag_timer(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct sta_info *sta = timeout_ctx;
+
+	if (sta->agreed_to_steer) {
+		wpa_printf(MSG_DEBUG, "%s: Reset steering flag for STA " MACSTR,
+			   hapd->conf->iface, MAC2STR(sta->addr));
+		sta->agreed_to_steer = 0;
+	}
+}
+
+
+static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd,
+					      const u8 *addr, const u8 *frm,
+					      size_t len)
+{
+	u8 dialog_token, status_code, bss_termination_delay;
+	const u8 *pos, *end, *target_bssid = NULL;
+	int enabled = hapd->conf->bss_transition;
+	struct sta_info *sta;
+
+#ifdef CONFIG_MBO
+	if (hapd->conf->mbo_enabled)
+		enabled = 1;
+#endif /* CONFIG_MBO */
+	if (!enabled) {
+		wpa_printf(MSG_DEBUG,
+			   "Ignore BSS Transition Management Response from "
+			   MACSTR
+			   " since BSS Transition Management is disabled",
+			   MAC2STR(addr));
+		return;
+	}
+
+	if (len < 3) {
+		wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Response from "
+			   MACSTR, MAC2STR(addr));
+		return;
+	}
+
+	pos = frm;
+	end = pos + len;
+	dialog_token = *pos++;
+	status_code = *pos++;
+	bss_termination_delay = *pos++;
+
+	wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Response from "
+		   MACSTR " dialog_token=%u status_code=%u "
+		   "bss_termination_delay=%u", MAC2STR(addr), dialog_token,
+		   status_code, bss_termination_delay);
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta) {
+		wpa_printf(MSG_DEBUG, "Station " MACSTR
+			   " not found for received BSS TM Response",
+			   MAC2STR(addr));
+		return;
+	}
+
+	if (status_code == WNM_BSS_TM_ACCEPT) {
+		if (end - pos < ETH_ALEN) {
+			wpa_printf(MSG_DEBUG, "WNM: not enough room for Target BSSID field");
+			return;
+		}
+		target_bssid = pos;
+		sta->agreed_to_steer = 1;
+		eloop_cancel_timeout(ap_sta_reset_steer_flag_timer, hapd, sta);
+		eloop_register_timeout(2, 0, ap_sta_reset_steer_flag_timer,
+				       hapd, sta);
+		wpa_printf(MSG_DEBUG, "WNM: Target BSSID: " MACSTR,
+			   MAC2STR(pos));
+		wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR
+			" status_code=%u bss_termination_delay=%u target_bssid="
+			MACSTR,
+			MAC2STR(addr), status_code, bss_termination_delay,
+			MAC2STR(pos));
+		pos += ETH_ALEN;
+	} else {
+		sta->agreed_to_steer = 0;
+		wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR
+			" status_code=%u bss_termination_delay=%u",
+			MAC2STR(addr), status_code, bss_termination_delay);
+	}
+
+	hostapd_ubus_notify_bss_transition_response(hapd, sta->addr, dialog_token,
+						    status_code, bss_termination_delay,
+						    target_bssid, pos, end - pos);
+
+	wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries",
+		    pos, end - pos);
+}
+
+
+static void wnm_beacon_protection_failure(struct hostapd_data *hapd,
+					  const u8 *addr)
+{
+	struct sta_info *sta;
+
+	if (!hapd->conf->beacon_prot ||
+	    !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_BEACON_PROTECTION))
+		return;
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) {
+		wpa_printf(MSG_DEBUG, "Station " MACSTR
+			   " not found for received WNM-Notification Request",
+			   MAC2STR(addr));
+		return;
+	}
+
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_INFO,
+		       "Beacon protection failure reported");
+	wpa_msg(hapd->msg_ctx, MSG_INFO, WPA_EVENT_UNPROT_BEACON "reporter="
+		MACSTR, MAC2STR(addr));
+}
+
+
+static void ieee802_11_rx_wnm_notification_req(struct hostapd_data *hapd,
+					       const u8 *addr, const u8 *buf,
+					       size_t len)
+{
+	u8 dialog_token, type;
+
+	if (len < 2)
+		return;
+	dialog_token = *buf++;
+	type = *buf++;
+	len -= 2;
+
+	wpa_printf(MSG_DEBUG,
+		   "WNM: Received WNM Notification Request frame from "
+		   MACSTR " (dialog_token=%u type=%u)",
+		   MAC2STR(addr), dialog_token, type);
+	wpa_hexdump(MSG_MSGDUMP, "WNM: Notification Request subelements",
+		    buf, len);
+	switch (type) {
+	case WNM_NOTIF_TYPE_BEACON_PROTECTION_FAILURE:
+		wnm_beacon_protection_failure(hapd, addr);
+		break;
+	case WNM_NOTIF_TYPE_VENDOR_SPECIFIC:
+		mbo_ap_wnm_notification_req(hapd, addr, buf, len);
+		break;
+	}
+}
+
+
+static void ieee802_11_rx_wnm_coloc_intf_report(struct hostapd_data *hapd,
+						const u8 *addr, const u8 *buf,
+						size_t len)
+{
+	u8 dialog_token;
+	char *hex;
+	size_t hex_len;
+
+	if (!hapd->conf->coloc_intf_reporting) {
+		wpa_printf(MSG_DEBUG,
+			   "WNM: Ignore unexpected Collocated Interference Report from "
+			   MACSTR, MAC2STR(addr));
+		return;
+	}
+
+	if (len < 1) {
+		wpa_printf(MSG_DEBUG,
+			   "WNM: Ignore too short Collocated Interference Report from "
+			   MACSTR, MAC2STR(addr));
+		return;
+	}
+	dialog_token = *buf++;
+	len--;
+
+	wpa_printf(MSG_DEBUG,
+		   "WNM: Received Collocated Interference Report frame from "
+		   MACSTR " (dialog_token=%u)",
+		   MAC2STR(addr), dialog_token);
+	wpa_hexdump(MSG_MSGDUMP, "WNM: Collocated Interference Report Elements",
+		    buf, len);
+
+	hex_len = 2 * len + 1;
+	hex = os_malloc(hex_len);
+	if (!hex)
+		return;
+	wpa_snprintf_hex(hex, hex_len, buf, len);
+	wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, COLOC_INTF_REPORT MACSTR " %d %s",
+		     MAC2STR(addr), dialog_token, hex);
+	os_free(hex);
+}
+
+
+
+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)
+{
+	u8 action;
+	const u8 *payload;
+	size_t plen;
+
+	if (len < IEEE80211_HDRLEN + 2)
+		return -1;
+
+	payload = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 1;
+	action = *payload++;
+	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:
+		hapd->openwrt_stats.wnm.bss_transition_query_rx++;
+		ieee802_11_rx_bss_trans_mgmt_query(hapd, mgmt->sa, payload,
+						   plen);
+		return 0;
+	case WNM_BSS_TRANS_MGMT_RESP:
+		hapd->openwrt_stats.wnm.bss_transition_response_rx++;
+		ieee802_11_rx_bss_trans_mgmt_resp(hapd, mgmt->sa, payload,
+						  plen);
+		return 0;
+	case WNM_SLEEP_MODE_REQ:
+		ieee802_11_rx_wnmsleep_req(hapd, mgmt->sa, payload, plen);
+		return 0;
+	case WNM_NOTIFICATION_REQ:
+		ieee802_11_rx_wnm_notification_req(hapd, mgmt->sa, payload,
+						   plen);
+		return 0;
+	case WNM_COLLOCATED_INTERFERENCE_REPORT:
+		ieee802_11_rx_wnm_coloc_intf_report(hapd, mgmt->sa, payload,
+						    plen);
+		return 0;
+	}
+
+	wpa_printf(MSG_DEBUG, "WNM: Unsupported WNM Action %u from " MACSTR,
+		   action, MAC2STR(mgmt->sa));
+	return -1;
+}
+
+
+int wnm_send_disassoc_imminent(struct hostapd_data *hapd,
+			       struct sta_info *sta, int disassoc_timer)
+{
+	u8 buf[1000], *pos;
+	struct ieee80211_mgmt *mgmt;
+
+	os_memset(buf, 0, sizeof(buf));
+	mgmt = (struct ieee80211_mgmt *) buf;
+	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					   WLAN_FC_STYPE_ACTION);
+	os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
+	os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+	os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+	mgmt->u.action.category = WLAN_ACTION_WNM;
+	mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
+	mgmt->u.action.u.bss_tm_req.dialog_token = 1;
+	mgmt->u.action.u.bss_tm_req.req_mode =
+		WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
+	mgmt->u.action.u.bss_tm_req.disassoc_timer =
+		host_to_le16(disassoc_timer);
+	mgmt->u.action.u.bss_tm_req.validity_interval = 0;
+
+	pos = mgmt->u.action.u.bss_tm_req.variable;
+
+	hapd->openwrt_stats.wnm.bss_transition_request_tx++;
+	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request frame to indicate imminent disassociation (disassoc_timer=%d) to "
+		   MACSTR, disassoc_timer, MAC2STR(sta->addr));
+	if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0, NULL, 0, 0) < 0) {
+		wpa_printf(MSG_DEBUG, "Failed to send BSS Transition "
+			   "Management Request frame");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static void set_disassoc_timer(struct hostapd_data *hapd, struct sta_info *sta,
+			       int disassoc_timer)
+{
+	int timeout, beacon_int;
+
+	/*
+	 * Prevent STA from reconnecting using cached PMKSA to force
+	 * full authentication with the authentication server (which may
+	 * decide to reject the connection),
+	 */
+	wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
+
+	beacon_int = hapd->iconf->beacon_int;
+	if (beacon_int < 1)
+		beacon_int = 100; /* best guess */
+	/* Calculate timeout in ms based on beacon_int in TU */
+	timeout = disassoc_timer * beacon_int * 128 / 125;
+	wpa_printf(MSG_DEBUG, "Disassociation timer for " MACSTR
+		   " set to %d ms", MAC2STR(sta->addr), timeout);
+
+	sta->timeout_next = STA_DISASSOC_FROM_CLI;
+	eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+	eloop_register_timeout(timeout / 1000,
+			       timeout % 1000 * 1000,
+			       ap_handle_timer, hapd, sta);
+}
+
+
+int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd,
+				   struct sta_info *sta, const char *url,
+				   int disassoc_timer)
+{
+	u8 buf[1000], *pos;
+	struct ieee80211_mgmt *mgmt;
+	size_t url_len;
+
+	os_memset(buf, 0, sizeof(buf));
+	mgmt = (struct ieee80211_mgmt *) buf;
+	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					   WLAN_FC_STYPE_ACTION);
+	os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
+	os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+	os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+	mgmt->u.action.category = WLAN_ACTION_WNM;
+	mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
+	mgmt->u.action.u.bss_tm_req.dialog_token = 1;
+	mgmt->u.action.u.bss_tm_req.req_mode =
+		WNM_BSS_TM_REQ_DISASSOC_IMMINENT |
+		WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT;
+	mgmt->u.action.u.bss_tm_req.disassoc_timer =
+		host_to_le16(disassoc_timer);
+	mgmt->u.action.u.bss_tm_req.validity_interval = 0x01;
+
+	pos = mgmt->u.action.u.bss_tm_req.variable;
+
+	/* Session Information URL */
+	url_len = os_strlen(url);
+	if (url_len > 255)
+		return -1;
+	*pos++ = url_len;
+	os_memcpy(pos, url, url_len);
+	pos += url_len;
+
+	if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0, NULL, 0, 0) < 0) {
+		wpa_printf(MSG_DEBUG, "Failed to send BSS Transition "
+			   "Management Request frame");
+		return -1;
+	}
+
+	hapd->openwrt_stats.wnm.bss_transition_request_tx++;
+	if (disassoc_timer) {
+		/* send disassociation frame after time-out */
+		set_disassoc_timer(hapd, sta, disassoc_timer);
+	}
+
+	return 0;
+}
+
+
+int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
+			u8 req_mode, int disassoc_timer, u8 valid_int,
+			const u8 *bss_term_dur, u8 dialog_token,
+			const char *url, const u8 *nei_rep, size_t nei_rep_len,
+			const u8 *mbo_attrs, size_t mbo_len)
+{
+	u8 *buf, *pos;
+	struct ieee80211_mgmt *mgmt;
+	size_t url_len;
+
+	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to "
+		   MACSTR
+		   " req_mode=0x%x disassoc_timer=%d valid_int=0x%x dialog_token=%u",
+		   MAC2STR(sta->addr), req_mode, disassoc_timer, valid_int,
+		   dialog_token);
+	buf = os_zalloc(1000 + nei_rep_len + mbo_len);
+	if (buf == NULL)
+		return -1;
+	mgmt = (struct ieee80211_mgmt *) buf;
+	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					   WLAN_FC_STYPE_ACTION);
+	os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
+	os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+	os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+	mgmt->u.action.category = WLAN_ACTION_WNM;
+	mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
+	mgmt->u.action.u.bss_tm_req.dialog_token = dialog_token;
+	mgmt->u.action.u.bss_tm_req.req_mode = req_mode;
+	mgmt->u.action.u.bss_tm_req.disassoc_timer =
+		host_to_le16(disassoc_timer);
+	mgmt->u.action.u.bss_tm_req.validity_interval = valid_int;
+
+	pos = mgmt->u.action.u.bss_tm_req.variable;
+
+	if ((req_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) &&
+	    bss_term_dur) {
+		os_memcpy(pos, bss_term_dur, 12);
+		pos += 12;
+	}
+
+	if (url) {
+		/* Session Information URL */
+		url_len = os_strlen(url);
+		if (url_len > 255) {
+			os_free(buf);
+			return -1;
+		}
+
+		*pos++ = url_len;
+		os_memcpy(pos, url, url_len);
+		pos += url_len;
+	}
+
+	if (nei_rep) {
+		os_memcpy(pos, nei_rep, nei_rep_len);
+		pos += nei_rep_len;
+	}
+
+	if (mbo_len > 0) {
+		pos += mbo_add_ie(pos, buf + sizeof(buf) - pos, mbo_attrs,
+				  mbo_len);
+	}
+
+	if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0, NULL, 0, 0) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "Failed to send BSS Transition Management Request frame");
+		os_free(buf);
+		return -1;
+	}
+	os_free(buf);
+
+	hapd->openwrt_stats.wnm.bss_transition_request_tx++;
+	if (disassoc_timer) {
+		/* send disassociation frame after time-out */
+		set_disassoc_timer(hapd, sta, disassoc_timer);
+	}
+
+	return 0;
+}
+
+
+int wnm_send_coloc_intf_req(struct hostapd_data *hapd, struct sta_info *sta,
+			    unsigned int auto_report, unsigned int timeout)
+{
+	u8 buf[100], *pos;
+	struct ieee80211_mgmt *mgmt;
+	u8 dialog_token = 1;
+
+	if (auto_report > 3 || timeout > 63)
+		return -1;
+	os_memset(buf, 0, sizeof(buf));
+	mgmt = (struct ieee80211_mgmt *) buf;
+	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					   WLAN_FC_STYPE_ACTION);
+	os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
+	os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+	os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+	mgmt->u.action.category = WLAN_ACTION_WNM;
+	mgmt->u.action.u.coloc_intf_req.action =
+		WNM_COLLOCATED_INTERFERENCE_REQ;
+	mgmt->u.action.u.coloc_intf_req.dialog_token = dialog_token;
+	mgmt->u.action.u.coloc_intf_req.req_info = auto_report | (timeout << 2);
+	pos = &mgmt->u.action.u.coloc_intf_req.req_info;
+	pos++;
+
+	wpa_printf(MSG_DEBUG, "WNM: Sending Collocated Interference Request to "
+		   MACSTR " (dialog_token=%u auto_report=%u timeout=%u)",
+		   MAC2STR(sta->addr), dialog_token, auto_report, timeout);
+	if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0, NULL, 0, 0) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "WNM: Failed to send Collocated Interference Request frame");
+		return -1;
+	}
+
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wnm_ap.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wnm_ap.h
new file mode 100644
index 0000000..f86c6b2
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wnm_ap.h
@@ -0,0 +1,30 @@
+/*
+ * IEEE 802.11v WNM related functions and structures
+ * Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WNM_AP_H
+#define WNM_AP_H
+
+struct sta_info;
+
+int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
+				const struct ieee80211_mgmt *mgmt, size_t len);
+int wnm_send_disassoc_imminent(struct hostapd_data *hapd,
+			       struct sta_info *sta, int disassoc_timer);
+int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd,
+				   struct sta_info *sta, const char *url,
+				   int disassoc_timer);
+int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
+			u8 req_mode, int disassoc_timer, u8 valid_int,
+			const u8 *bss_term_dur, u8 dialog_token,
+			const char *url, const u8 *nei_rep, size_t nei_rep_len,
+			const u8 *mbo_attrs, size_t mbo_len);
+void ap_sta_reset_steer_flag_timer(void *eloop_ctx, void *timeout_ctx);
+int wnm_send_coloc_intf_req(struct hostapd_data *hapd, struct sta_info *sta,
+			    unsigned int auto_report, unsigned int timeout);
+
+#endif /* WNM_AP_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth.c
new file mode 100644
index 0000000..88f42a0
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth.c
@@ -0,0 +1,6649 @@
+/*
+ * IEEE 802.11 RSN / WPA Authenticator
+ * Copyright (c) 2004-2022, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/state_machine.h"
+#include "utils/bitfield.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ocv.h"
+#include "common/dpp.h"
+#include "common/wpa_ctrl.h"
+#include "crypto/aes.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/aes_siv.h"
+#include "crypto/crypto.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "crypto/sha384.h"
+#include "crypto/sha512.h"
+#include "crypto/random.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "drivers/driver.h"
+#include "ap_config.h"
+#include "ieee802_11.h"
+#include "sta_info.h"
+#include "wpa_auth.h"
+#include "pmksa_cache_auth.h"
+#include "wpa_auth_i.h"
+#include "wpa_auth_ie.h"
+
+#define STATE_MACHINE_DATA struct wpa_state_machine
+#define STATE_MACHINE_DEBUG_PREFIX "WPA"
+#define STATE_MACHINE_ADDR wpa_auth_get_spa(sm)
+
+
+static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx);
+static int wpa_sm_step(struct wpa_state_machine *sm);
+static int wpa_verify_key_mic(int akmp, size_t pmk_len, struct wpa_ptk *PTK,
+			      u8 *data, size_t data_len);
+#ifdef CONFIG_FILS
+static int wpa_aead_decrypt(struct wpa_state_machine *sm, struct wpa_ptk *ptk,
+			    u8 *buf, size_t buf_len, u16 *_key_data_len);
+static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm,
+					     const struct wpabuf *hlp);
+#endif /* CONFIG_FILS */
+static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx);
+static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth,
+			      struct wpa_group *group);
+static void wpa_request_new_ptk(struct wpa_state_machine *sm);
+static int wpa_gtk_update(struct wpa_authenticator *wpa_auth,
+			  struct wpa_group *group);
+static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth,
+				       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,
+			  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,
+			  struct wpa_group *group);
+static void wpa_group_put(struct wpa_authenticator *wpa_auth,
+			  struct wpa_group *group);
+static int ieee80211w_kde_len(struct wpa_state_machine *sm);
+static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos);
+
+static const u32 eapol_key_timeout_first = 100; /* ms */
+static const u32 eapol_key_timeout_subseq = 1000; /* ms */
+static const u32 eapol_key_timeout_first_group = 500; /* ms */
+static const u32 eapol_key_timeout_no_retrans = 4000; /* ms */
+
+/* TODO: make these configurable */
+static const int dot11RSNAConfigPMKLifetime = 43200;
+static const int dot11RSNAConfigPMKReauthThreshold = 70;
+static const int dot11RSNAConfigSATimeout = 60;
+
+
+static const u8 * wpa_auth_get_aa(const struct wpa_state_machine *sm)
+{
+#ifdef CONFIG_IEEE80211BE
+	if (sm->mld_assoc_link_id >= 0)
+		return sm->own_mld_addr;
+#endif /* CONFIG_IEEE80211BE */
+	return sm->wpa_auth->addr;
+}
+
+
+static const u8 * wpa_auth_get_spa(const struct wpa_state_machine *sm)
+{
+#ifdef CONFIG_IEEE80211BE
+	if (sm->mld_assoc_link_id >= 0)
+		return sm->peer_mld_addr;
+#endif /* CONFIG_IEEE80211BE */
+	return sm->addr;
+}
+
+
+static inline int wpa_auth_mic_failure_report(
+	struct wpa_authenticator *wpa_auth, const u8 *addr)
+{
+	if (wpa_auth->cb->mic_failure_report)
+		return wpa_auth->cb->mic_failure_report(wpa_auth->cb_ctx, addr);
+	return 0;
+}
+
+
+static inline void wpa_auth_psk_failure_report(
+	struct wpa_authenticator *wpa_auth, const u8 *addr)
+{
+	if (wpa_auth->cb->psk_failure_report)
+		wpa_auth->cb->psk_failure_report(wpa_auth->cb_ctx, addr);
+}
+
+
+static inline void wpa_auth_set_eapol(struct wpa_authenticator *wpa_auth,
+				      const u8 *addr, wpa_eapol_variable var,
+				      int value)
+{
+	if (wpa_auth->cb->set_eapol)
+		wpa_auth->cb->set_eapol(wpa_auth->cb_ctx, addr, var, value);
+}
+
+
+static inline int wpa_auth_get_eapol(struct wpa_authenticator *wpa_auth,
+				     const u8 *addr, wpa_eapol_variable var)
+{
+	if (!wpa_auth->cb->get_eapol)
+		return -1;
+	return wpa_auth->cb->get_eapol(wpa_auth->cb_ctx, addr, var);
+}
+
+
+static inline const u8 * wpa_auth_get_psk(struct wpa_authenticator *wpa_auth,
+					  const u8 *addr,
+					  const u8 *p2p_dev_addr,
+					  const u8 *prev_psk, size_t *psk_len,
+					  int *vlan_id)
+{
+	if (!wpa_auth->cb->get_psk)
+		return NULL;
+	return wpa_auth->cb->get_psk(wpa_auth->cb_ctx, addr, p2p_dev_addr,
+				     prev_psk, psk_len, vlan_id);
+}
+
+
+static inline int wpa_auth_get_msk(struct wpa_authenticator *wpa_auth,
+				   const u8 *addr, u8 *msk, size_t *len)
+{
+	if (!wpa_auth->cb->get_msk)
+		return -1;
+	return wpa_auth->cb->get_msk(wpa_auth->cb_ctx, addr, msk, len);
+}
+
+
+static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth,
+				   int vlan_id,
+				   enum wpa_alg alg, const u8 *addr, int idx,
+				   u8 *key, size_t key_len,
+				   enum key_flag key_flag)
+{
+	if (!wpa_auth->cb->set_key)
+		return -1;
+	return wpa_auth->cb->set_key(wpa_auth->cb_ctx, vlan_id, alg, addr, idx,
+				     key, key_len, key_flag);
+}
+
+
+#ifdef CONFIG_PASN
+static inline int wpa_auth_set_ltf_keyseed(struct wpa_authenticator *wpa_auth,
+					   const u8 *peer_addr,
+					   const u8 *ltf_keyseed,
+					   size_t ltf_keyseed_len)
+{
+	if (!wpa_auth->cb->set_ltf_keyseed)
+		return -1;
+	return wpa_auth->cb->set_ltf_keyseed(wpa_auth->cb_ctx, peer_addr,
+					     ltf_keyseed, ltf_keyseed_len);
+}
+#endif /* CONFIG_PASN */
+
+
+static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth,
+				      const u8 *addr, int idx, u8 *seq)
+{
+	int res;
+
+	if (!wpa_auth->cb->get_seqnum)
+		return -1;
+	res = wpa_auth->cb->get_seqnum(wpa_auth->cb_ctx, addr, idx, seq);
+#ifdef CONFIG_TESTING_OPTIONS
+	if (!addr && idx < 4 && wpa_auth->conf.gtk_rsc_override_set) {
+		wpa_printf(MSG_DEBUG,
+			   "TESTING: Override GTK RSC %016llx --> %016llx",
+			   (long long unsigned) WPA_GET_LE64(seq),
+			   (long long unsigned)
+			   WPA_GET_LE64(wpa_auth->conf.gtk_rsc_override));
+		os_memcpy(seq, wpa_auth->conf.gtk_rsc_override,
+			  WPA_KEY_RSC_LEN);
+	}
+	if (!addr && idx >= 4 && idx <= 5 &&
+	    wpa_auth->conf.igtk_rsc_override_set) {
+		wpa_printf(MSG_DEBUG,
+			   "TESTING: Override IGTK RSC %016llx --> %016llx",
+			   (long long unsigned) WPA_GET_LE64(seq),
+			   (long long unsigned)
+			   WPA_GET_LE64(wpa_auth->conf.igtk_rsc_override));
+		os_memcpy(seq, wpa_auth->conf.igtk_rsc_override,
+			  WPA_KEY_RSC_LEN);
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+	return res;
+}
+
+
+static inline int
+wpa_auth_send_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr,
+		    const u8 *data, size_t data_len, int encrypt)
+{
+	if (!wpa_auth->cb->send_eapol)
+		return -1;
+	return wpa_auth->cb->send_eapol(wpa_auth->cb_ctx, addr, data, data_len,
+					encrypt);
+}
+
+
+#ifdef CONFIG_MESH
+static inline int wpa_auth_start_ampe(struct wpa_authenticator *wpa_auth,
+				      const u8 *addr)
+{
+	if (!wpa_auth->cb->start_ampe)
+		return -1;
+	return wpa_auth->cb->start_ampe(wpa_auth->cb_ctx, addr);
+}
+#endif /* CONFIG_MESH */
+
+
+int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth,
+			  int (*cb)(struct wpa_state_machine *sm, void *ctx),
+			  void *cb_ctx)
+{
+	if (!wpa_auth->cb->for_each_sta)
+		return 0;
+	return wpa_auth->cb->for_each_sta(wpa_auth->cb_ctx, cb, cb_ctx);
+}
+
+
+int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth,
+			   int (*cb)(struct wpa_authenticator *a, void *ctx),
+			   void *cb_ctx)
+{
+	if (!wpa_auth->cb->for_each_auth)
+		return 0;
+	return wpa_auth->cb->for_each_auth(wpa_auth->cb_ctx, cb, 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)
+{
+	if (wpa_auth->cb->store_ptksa)
+		wpa_auth->cb->store_ptksa(wpa_auth->cb_ctx, addr, cipher,
+					  life_time, ptk);
+}
+
+
+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)
+{
+	if (!wpa_auth->cb->logger)
+		return;
+	wpa_auth->cb->logger(wpa_auth->cb_ctx, addr, level, txt);
+}
+
+
+void wpa_auth_vlogger(struct wpa_authenticator *wpa_auth, const u8 *addr,
+		      logger_level level, const char *fmt, ...)
+{
+	char *format;
+	int maxlen;
+	va_list ap;
+
+	if (!wpa_auth->cb->logger)
+		return;
+
+	maxlen = os_strlen(fmt) + 100;
+	format = os_malloc(maxlen);
+	if (!format)
+		return;
+
+	va_start(ap, fmt);
+	vsnprintf(format, maxlen, fmt, ap);
+	va_end(ap);
+
+	wpa_auth_logger(wpa_auth, addr, level, format);
+
+	os_free(format);
+}
+
+
+static void wpa_sta_disconnect(struct wpa_authenticator *wpa_auth,
+			       const u8 *addr, u16 reason)
+{
+	if (!wpa_auth->cb->disconnect)
+		return;
+	wpa_printf(MSG_DEBUG, "wpa_sta_disconnect STA " MACSTR " (reason %u)",
+		   MAC2STR(addr), reason);
+	wpa_auth->cb->disconnect(wpa_auth->cb_ctx, addr, reason);
+}
+
+
+#ifdef CONFIG_OCV
+static int wpa_channel_info(struct wpa_authenticator *wpa_auth,
+			    struct wpa_channel_info *ci)
+{
+	if (!wpa_auth->cb->channel_info)
+		return -1;
+	return wpa_auth->cb->channel_info(wpa_auth->cb_ctx, ci);
+}
+#endif /* CONFIG_OCV */
+
+
+static int wpa_auth_update_vlan(struct wpa_authenticator *wpa_auth,
+				const u8 *addr, int vlan_id)
+{
+	if (!wpa_auth->cb->update_vlan)
+		return -1;
+	return wpa_auth->cb->update_vlan(wpa_auth->cb_ctx, addr, vlan_id);
+}
+
+
+static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_authenticator *wpa_auth = eloop_ctx;
+
+	if (random_get_bytes(wpa_auth->group->GMK, WPA_GMK_LEN)) {
+		wpa_printf(MSG_ERROR,
+			   "Failed to get random data for WPA initialization.");
+	} else {
+		wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "GMK rekeyd");
+		wpa_hexdump_key(MSG_DEBUG, "GMK",
+				wpa_auth->group->GMK, WPA_GMK_LEN);
+	}
+
+	if (wpa_auth->conf.wpa_gmk_rekey) {
+		eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0,
+				       wpa_rekey_gmk, wpa_auth, NULL);
+	}
+}
+
+
+static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_authenticator *wpa_auth = eloop_ctx;
+	struct wpa_group *group, *next;
+
+	wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "rekeying GTK");
+	group = wpa_auth->group;
+	while (group) {
+		wpa_group_get(wpa_auth, group);
+
+		group->GTKReKey = true;
+		do {
+			group->changed = false;
+			wpa_group_sm_step(wpa_auth, group);
+		} while (group->changed);
+
+		next = group->next;
+		wpa_group_put(wpa_auth, group);
+		group = next;
+	}
+
+	if (wpa_auth->conf.wpa_group_rekey) {
+		eloop_register_timeout(wpa_auth->conf.wpa_group_rekey,
+				       0, wpa_rekey_gtk, wpa_auth, NULL);
+	}
+}
+
+
+static void wpa_rekey_ptk(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_authenticator *wpa_auth = eloop_ctx;
+	struct wpa_state_machine *sm = timeout_ctx;
+
+	wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
+			"rekeying PTK");
+	wpa_request_new_ptk(sm);
+	wpa_sm_step(sm);
+}
+
+
+void wpa_auth_set_ptk_rekey_timer(struct wpa_state_machine *sm)
+{
+	if (sm && sm->wpa_auth->conf.wpa_ptk_rekey) {
+		wpa_printf(MSG_DEBUG, "WPA: Start PTK rekeying timer for "
+			   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,
+				       wpa_rekey_ptk, sm->wpa_auth, sm);
+	}
+}
+
+
+static int wpa_auth_pmksa_clear_cb(struct wpa_state_machine *sm, void *ctx)
+{
+	if (sm->pmksa == ctx)
+		sm->pmksa = NULL;
+	return 0;
+}
+
+
+static void wpa_auth_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry,
+				   void *ctx)
+{
+	struct wpa_authenticator *wpa_auth = ctx;
+	wpa_auth_for_each_sta(wpa_auth, wpa_auth_pmksa_clear_cb, entry);
+}
+
+
+static int wpa_group_init_gmk_and_counter(struct wpa_authenticator *wpa_auth,
+					  struct wpa_group *group)
+{
+	u8 buf[ETH_ALEN + 8 + sizeof(unsigned long)];
+	u8 rkey[32];
+	unsigned long ptr;
+
+	if (random_get_bytes(group->GMK, WPA_GMK_LEN) < 0)
+		return -1;
+	wpa_hexdump_key(MSG_DEBUG, "GMK", group->GMK, WPA_GMK_LEN);
+
+	/*
+	 * Counter = PRF-256(Random number, "Init Counter",
+	 *                   Local MAC Address || Time)
+	 */
+	os_memcpy(buf, wpa_auth->addr, ETH_ALEN);
+	wpa_get_ntp_timestamp(buf + ETH_ALEN);
+	ptr = (unsigned long) group;
+	os_memcpy(buf + ETH_ALEN + 8, &ptr, sizeof(ptr));
+#ifdef TEST_FUZZ
+	os_memset(buf + ETH_ALEN, 0xab, 8);
+	os_memset(buf + ETH_ALEN + 8, 0xcd, sizeof(ptr));
+#endif /* TEST_FUZZ */
+	if (random_get_bytes(rkey, sizeof(rkey)) < 0)
+		return -1;
+
+	if (sha1_prf(rkey, sizeof(rkey), "Init Counter", buf, sizeof(buf),
+		     group->Counter, WPA_NONCE_LEN) < 0)
+		return -1;
+	wpa_hexdump_key(MSG_DEBUG, "Key Counter",
+			group->Counter, WPA_NONCE_LEN);
+
+	return 0;
+}
+
+
+static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth,
+					 int vlan_id, int delay_init)
+{
+	struct wpa_group *group;
+
+	group = os_zalloc(sizeof(struct wpa_group));
+	if (!group)
+		return NULL;
+
+	group->GTKAuthenticator = true;
+	group->vlan_id = vlan_id;
+	group->GTK_len = wpa_cipher_key_len(wpa_auth->conf.wpa_group);
+
+	if (random_pool_ready() != 1) {
+		wpa_printf(MSG_INFO,
+			   "WPA: Not enough entropy in random pool for secure operations - update keys later when the first station connects");
+	}
+
+	/*
+	 * Set initial GMK/Counter value here. The actual values that will be
+	 * used in negotiations will be set once the first station tries to
+	 * connect. This allows more time for collecting additional randomness
+	 * on embedded devices.
+	 */
+	if (wpa_group_init_gmk_and_counter(wpa_auth, group) < 0) {
+		wpa_printf(MSG_ERROR,
+			   "Failed to get random data for WPA initialization.");
+		os_free(group);
+		return NULL;
+	}
+
+	group->GInit = true;
+	if (delay_init) {
+		wpa_printf(MSG_DEBUG,
+			   "WPA: Delay group state machine start until Beacon frames have been configured");
+		/* Initialization is completed in wpa_init_keys(). */
+	} else {
+		wpa_group_sm_step(wpa_auth, group);
+		group->GInit = false;
+		wpa_group_sm_step(wpa_auth, group);
+	}
+
+	return group;
+}
+
+
+/**
+ * wpa_init - Initialize WPA authenticator
+ * @addr: Authenticator address
+ * @conf: Configuration for WPA authenticator
+ * @cb: Callback functions for WPA authenticator
+ * Returns: Pointer to WPA authenticator data or %NULL on failure
+ */
+struct wpa_authenticator * wpa_init(const u8 *addr,
+				    struct wpa_auth_config *conf,
+				    const struct wpa_auth_callbacks *cb,
+				    void *cb_ctx)
+{
+	struct wpa_authenticator *wpa_auth;
+
+	wpa_auth = os_zalloc(sizeof(struct wpa_authenticator));
+	if (!wpa_auth)
+		return NULL;
+	os_memcpy(wpa_auth->addr, addr, ETH_ALEN);
+	os_memcpy(&wpa_auth->conf, conf, sizeof(*conf));
+	wpa_auth->cb = cb;
+	wpa_auth->cb_ctx = cb_ctx;
+
+	if (wpa_auth_gen_wpa_ie(wpa_auth)) {
+		wpa_printf(MSG_ERROR, "Could not generate WPA IE.");
+		os_free(wpa_auth);
+		return NULL;
+	}
+
+	wpa_auth->group = wpa_group_init(wpa_auth, 0, 1);
+	if (!wpa_auth->group) {
+		os_free(wpa_auth->wpa_ie);
+		os_free(wpa_auth);
+		return NULL;
+	}
+
+	wpa_auth->pmksa = pmksa_cache_auth_init(wpa_auth_pmksa_free_cb,
+						wpa_auth);
+	if (!wpa_auth->pmksa) {
+		wpa_printf(MSG_ERROR, "PMKSA cache initialization failed.");
+		os_free(wpa_auth->group);
+		os_free(wpa_auth->wpa_ie);
+		os_free(wpa_auth);
+		return NULL;
+	}
+
+#ifdef CONFIG_IEEE80211R_AP
+	wpa_auth->ft_pmk_cache = wpa_ft_pmk_cache_init();
+	if (!wpa_auth->ft_pmk_cache) {
+		wpa_printf(MSG_ERROR, "FT PMK cache initialization failed.");
+		os_free(wpa_auth->group);
+		os_free(wpa_auth->wpa_ie);
+		pmksa_cache_auth_deinit(wpa_auth->pmksa);
+		os_free(wpa_auth);
+		return NULL;
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+
+	if (wpa_auth->conf.wpa_gmk_rekey) {
+		eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0,
+				       wpa_rekey_gmk, wpa_auth, NULL);
+	}
+
+	if (wpa_auth->conf.wpa_group_rekey) {
+		eloop_register_timeout(wpa_auth->conf.wpa_group_rekey, 0,
+				       wpa_rekey_gtk, wpa_auth, NULL);
+	}
+
+#ifdef CONFIG_P2P
+	if (WPA_GET_BE32(conf->ip_addr_start)) {
+		int count = WPA_GET_BE32(conf->ip_addr_end) -
+			WPA_GET_BE32(conf->ip_addr_start) + 1;
+		if (count > 1000)
+			count = 1000;
+		if (count > 0)
+			wpa_auth->ip_pool = bitfield_alloc(count);
+	}
+#endif /* CONFIG_P2P */
+
+	return wpa_auth;
+}
+
+
+int wpa_init_keys(struct wpa_authenticator *wpa_auth)
+{
+	struct wpa_group *group = wpa_auth->group;
+
+	wpa_printf(MSG_DEBUG,
+		   "WPA: Start group state machine to set initial keys");
+	wpa_group_sm_step(wpa_auth, group);
+	group->GInit = false;
+	wpa_group_sm_step(wpa_auth, group);
+	if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
+		return -1;
+	return 0;
+}
+
+
+/**
+ * wpa_deinit - Deinitialize WPA authenticator
+ * @wpa_auth: Pointer to WPA authenticator data from wpa_init()
+ */
+void wpa_deinit(struct wpa_authenticator *wpa_auth)
+{
+	struct wpa_group *group, *prev;
+
+	eloop_cancel_timeout(wpa_rekey_gmk, wpa_auth, NULL);
+	eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
+
+	pmksa_cache_auth_deinit(wpa_auth->pmksa);
+
+#ifdef CONFIG_IEEE80211R_AP
+	wpa_ft_pmk_cache_deinit(wpa_auth->ft_pmk_cache);
+	wpa_auth->ft_pmk_cache = NULL;
+	wpa_ft_deinit(wpa_auth);
+#endif /* CONFIG_IEEE80211R_AP */
+
+#ifdef CONFIG_P2P
+	bitfield_free(wpa_auth->ip_pool);
+#endif /* CONFIG_P2P */
+
+
+	os_free(wpa_auth->wpa_ie);
+
+	group = wpa_auth->group;
+	while (group) {
+		prev = group;
+		group = group->next;
+		bin_clear_free(prev, sizeof(*prev));
+	}
+
+	os_free(wpa_auth);
+}
+
+
+/**
+ * wpa_reconfig - Update WPA authenticator configuration
+ * @wpa_auth: Pointer to WPA authenticator data from wpa_init()
+ * @conf: Configuration for WPA authenticator
+ */
+int wpa_reconfig(struct wpa_authenticator *wpa_auth,
+		 struct wpa_auth_config *conf)
+{
+	struct wpa_group *group;
+
+	if (!wpa_auth)
+		return 0;
+
+	os_memcpy(&wpa_auth->conf, conf, sizeof(*conf));
+	if (wpa_auth_gen_wpa_ie(wpa_auth)) {
+		wpa_printf(MSG_ERROR, "Could not generate WPA IE.");
+		return -1;
+	}
+
+	/*
+	 * Reinitialize GTK to make sure it is suitable for the new
+	 * configuration.
+	 */
+	group = wpa_auth->group;
+	group->GTK_len = wpa_cipher_key_len(wpa_auth->conf.wpa_group);
+	group->GInit = true;
+	wpa_group_sm_step(wpa_auth, group);
+	group->GInit = false;
+	wpa_group_sm_step(wpa_auth, group);
+
+	return 0;
+}
+
+
+struct wpa_state_machine *
+wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr,
+		  const u8 *p2p_dev_addr)
+{
+	struct wpa_state_machine *sm;
+
+	if (wpa_auth->group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
+		return NULL;
+
+	sm = os_zalloc(sizeof(struct wpa_state_machine));
+	if (!sm)
+		return NULL;
+	os_memcpy(sm->addr, addr, ETH_ALEN);
+	if (p2p_dev_addr)
+		os_memcpy(sm->p2p_dev_addr, p2p_dev_addr, ETH_ALEN);
+
+	sm->wpa_auth = wpa_auth;
+	sm->group = wpa_auth->group;
+	wpa_group_get(sm->wpa_auth, sm->group);
+#ifdef CONFIG_IEEE80211BE
+	sm->mld_assoc_link_id = -1;
+#endif /* CONFIG_IEEE80211BE */
+
+	return sm;
+}
+
+
+int wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth,
+			    struct wpa_state_machine *sm)
+{
+	if (!wpa_auth || !wpa_auth->conf.wpa || !sm)
+		return -1;
+
+#ifdef CONFIG_IEEE80211R_AP
+	if (sm->ft_completed) {
+		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;
+		sm->Pair = true;
+		return 0;
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+
+#ifdef CONFIG_FILS
+	if (sm->fils_completed) {
+		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;
+		sm->Pair = true;
+		return 0;
+	}
+#endif /* CONFIG_FILS */
+
+	if (sm->started) {
+		os_memset(&sm->key_replay, 0, sizeof(sm->key_replay));
+		sm->ReAuthenticationRequest = true;
+		return wpa_sm_step(sm);
+	}
+
+	wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
+			"start authentication");
+	sm->started = 1;
+
+	sm->Init = true;
+	if (wpa_sm_step(sm) == 1)
+		return 1; /* should not really happen */
+	sm->Init = false;
+	sm->AuthenticationRequest = true;
+	return wpa_sm_step(sm);
+}
+
+
+void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm)
+{
+	/* WPA/RSN was not used - clear WPA state. This is needed if the STA
+	 * reassociates back to the same AP while the previous entry for the
+	 * STA has not yet been removed. */
+	if (!sm)
+		return;
+
+	sm->wpa_key_mgmt = 0;
+}
+
+
+static void wpa_free_sta_sm(struct wpa_state_machine *sm)
+{
+#ifdef CONFIG_P2P
+	if (WPA_GET_BE32(sm->ip_addr)) {
+		wpa_printf(MSG_DEBUG,
+			   "P2P: Free assigned IP address %u.%u.%u.%u from "
+			   MACSTR " (bit %u)",
+			   sm->ip_addr[0], sm->ip_addr[1],
+			   sm->ip_addr[2], sm->ip_addr[3],
+			   MAC2STR(wpa_auth_get_spa(sm)),
+			   sm->ip_addr_bit);
+		bitfield_clear(sm->wpa_auth->ip_pool, sm->ip_addr_bit);
+	}
+#endif /* CONFIG_P2P */
+	if (sm->GUpdateStationKeys) {
+		sm->group->GKeyDoneStations--;
+		sm->GUpdateStationKeys = false;
+	}
+#ifdef CONFIG_IEEE80211R_AP
+	os_free(sm->assoc_resp_ftie);
+	wpabuf_free(sm->ft_pending_req_ies);
+#endif /* CONFIG_IEEE80211R_AP */
+	os_free(sm->last_rx_eapol_key);
+	os_free(sm->wpa_ie);
+	os_free(sm->rsnxe);
+	wpa_group_put(sm->wpa_auth, sm->group);
+#ifdef CONFIG_DPP2
+	wpabuf_clear_free(sm->dpp_z);
+#endif /* CONFIG_DPP2 */
+	bin_clear_free(sm, sizeof(*sm));
+}
+
+
+void wpa_auth_sta_deinit(struct wpa_state_machine *sm)
+{
+	struct wpa_authenticator *wpa_auth;
+
+	if (!sm)
+		return;
+
+	wpa_auth = sm->wpa_auth;
+	if (wpa_auth->conf.wpa_strict_rekey && sm->has_GTK) {
+		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)
+			eloop_register_timeout(0, 500000, wpa_rekey_gtk,
+					       wpa_auth, NULL);
+	}
+
+	eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm);
+	sm->pending_1_of_4_timeout = 0;
+	eloop_cancel_timeout(wpa_sm_call_step, sm, NULL);
+	eloop_cancel_timeout(wpa_rekey_ptk, wpa_auth, sm);
+#ifdef CONFIG_IEEE80211R_AP
+	wpa_ft_sta_deinit(sm);
+#endif /* CONFIG_IEEE80211R_AP */
+	if (sm->in_step_loop) {
+		/* Must not free state machine while wpa_sm_step() is running.
+		 * 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(wpa_auth_get_spa(sm)));
+		sm->pending_deinit = 1;
+	} else
+		wpa_free_sta_sm(sm);
+}
+
+
+static void wpa_request_new_ptk(struct wpa_state_machine *sm)
+{
+	if (!sm)
+		return;
+
+	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(wpa_auth_get_spa(sm)));
+		sm->Disconnect = true;
+		/* Try to encourage the STA to reconnect */
+		sm->disconnect_reason =
+			WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA;
+	} else {
+		if (sm->use_ext_key_id)
+			sm->keyidx_active ^= 1; /* flip Key ID */
+		sm->PTKRequest = true;
+		sm->PTK_valid = 0;
+	}
+}
+
+
+static int wpa_replay_counter_valid(struct wpa_key_replay_counter *ctr,
+				    const u8 *replay_counter)
+{
+	int i;
+	for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) {
+		if (!ctr[i].valid)
+			break;
+		if (os_memcmp(replay_counter, ctr[i].counter,
+			      WPA_REPLAY_COUNTER_LEN) == 0)
+			return 1;
+	}
+	return 0;
+}
+
+
+static void wpa_replay_counter_mark_invalid(struct wpa_key_replay_counter *ctr,
+					    const u8 *replay_counter)
+{
+	int i;
+	for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) {
+		if (ctr[i].valid &&
+		    (!replay_counter ||
+		     os_memcmp(replay_counter, ctr[i].counter,
+			       WPA_REPLAY_COUNTER_LEN) == 0))
+			ctr[i].valid = false;
+	}
+}
+
+
+#ifdef CONFIG_IEEE80211R_AP
+static int ft_check_msg_2_of_4(struct wpa_authenticator *wpa_auth,
+			       struct wpa_state_machine *sm,
+			       struct wpa_eapol_ie_parse *kde)
+{
+	struct wpa_ie_data ie;
+	struct rsn_mdie *mdie;
+
+	if (wpa_parse_wpa_ie_rsn(kde->rsn_ie, kde->rsn_ie_len, &ie) < 0 ||
+	    ie.num_pmkid != 1 || !ie.pmkid) {
+		wpa_printf(MSG_DEBUG,
+			   "FT: No PMKR1Name in FT 4-way handshake message 2/4");
+		return -1;
+	}
+
+	os_memcpy(sm->sup_pmk_r1_name, ie.pmkid, PMKID_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from Supplicant",
+		    sm->sup_pmk_r1_name, PMKID_LEN);
+
+	if (!kde->mdie || !kde->ftie) {
+		wpa_printf(MSG_DEBUG,
+			   "FT: No %s in FT 4-way handshake message 2/4",
+			   kde->mdie ? "FTIE" : "MDIE");
+		return -1;
+	}
+
+	mdie = (struct rsn_mdie *) (kde->mdie + 2);
+	if (kde->mdie[1] < sizeof(struct rsn_mdie) ||
+	    os_memcmp(wpa_auth->conf.mobility_domain, mdie->mobility_domain,
+		      MOBILITY_DOMAIN_ID_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "FT: MDIE mismatch");
+		return -1;
+	}
+
+	if (sm->assoc_resp_ftie &&
+	    (kde->ftie[1] != sm->assoc_resp_ftie[1] ||
+	     os_memcmp(kde->ftie, sm->assoc_resp_ftie,
+		       2 + sm->assoc_resp_ftie[1]) != 0)) {
+		wpa_printf(MSG_DEBUG, "FT: FTIE mismatch");
+		wpa_hexdump(MSG_DEBUG, "FT: FTIE in EAPOL-Key msg 2/4",
+			    kde->ftie, kde->ftie_len);
+		wpa_hexdump(MSG_DEBUG, "FT: FTIE in (Re)AssocResp",
+			    sm->assoc_resp_ftie, 2 + sm->assoc_resp_ftie[1]);
+		return -1;
+	}
+
+	return 0;
+}
+#endif /* CONFIG_IEEE80211R_AP */
+
+
+static int wpa_receive_error_report(struct wpa_authenticator *wpa_auth,
+				    struct wpa_state_machine *sm, int group)
+{
+	/* Supplicant reported a Michael MIC error */
+	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, 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, 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,
+						wpa_auth_get_spa(sm)) > 0)
+			return 1; /* STA entry was removed */
+		sm->dot11RSNAStatsTKIPRemoteMICFailures++;
+		wpa_auth->dot11RSNAStatsTKIPRemoteMICFailures++;
+	}
+
+	/*
+	 * Error report is not a request for a new key handshake, but since
+	 * Authenticator may do it, let's change the keys now anyway.
+	 */
+	wpa_request_new_ptk(sm);
+	return 0;
+}
+
+
+static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data,
+			      size_t data_len)
+{
+	struct wpa_ptk PTK;
+	int ok = 0;
+	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 (;;) {
+		if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) &&
+		    !wpa_key_mgmt_sae(sm->wpa_key_mgmt)) {
+			pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr,
+					       sm->p2p_dev_addr, pmk, &pmk_len,
+					       &vlan_id);
+			if (!pmk)
+				break;
+#ifdef CONFIG_IEEE80211R_AP
+			if (wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) {
+				os_memcpy(sm->xxkey, pmk, pmk_len);
+				sm->xxkey_len = pmk_len;
+			}
+#endif /* CONFIG_IEEE80211R_AP */
+		} else {
+			pmk = sm->PMK;
+			pmk_len = sm->pmk_len;
+		}
+
+		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,
+				       data, data_len) == 0) {
+			if (sm->PMK != pmk) {
+				os_memcpy(sm->PMK, pmk, pmk_len);
+				sm->pmk_len = pmk_len;
+			}
+			ok = 1;
+			break;
+		}
+
+		if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
+		    wpa_key_mgmt_sae(sm->wpa_key_mgmt))
+			break;
+	}
+
+	if (!ok) {
+		wpa_printf(MSG_DEBUG,
+			   "WPA: Earlier SNonce did not result in matching MIC");
+		goto fail;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "WPA: Earlier SNonce resulted in matching MIC");
+	sm->alt_snonce_valid = 0;
+
+	if (vlan_id && wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) &&
+	    wpa_auth_update_vlan(sm->wpa_auth, sm->addr, vlan_id) < 0)
+		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;
+
+	ret = 0;
+fail:
+	forced_memzero(pmk_r0, sizeof(pmk_r0));
+	forced_memzero(pmk_r1, sizeof(pmk_r1));
+	return ret;
+}
+
+
+static bool wpa_auth_gtk_rekey_in_process(struct wpa_authenticator *wpa_auth)
+{
+	struct wpa_group *group;
+
+	for (group = wpa_auth->group; group; group = group->next) {
+		if (group->GKeyDoneStations)
+			return true;
+	}
+	return false;
+}
+
+
+void wpa_receive(struct wpa_authenticator *wpa_auth,
+		 struct wpa_state_machine *sm,
+		 u8 *data, size_t data_len)
+{
+	struct ieee802_1x_hdr *hdr;
+	struct wpa_eapol_key *key;
+	u16 key_info, key_data_length;
+	enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST } msg;
+	char *msgtxt;
+	struct wpa_eapol_ie_parse kde;
+	const u8 *key_data;
+	size_t keyhdrlen, mic_len;
+	u8 *mic;
+	bool is_mld = false;
+
+	if (!wpa_auth || !wpa_auth->conf.wpa || !sm)
+		return;
+
+#ifdef CONFIG_IEEE80211BE
+	is_mld = sm->mld_assoc_link_id >= 0;
+#endif /* CONFIG_IEEE80211BE */
+
+	wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL data", data, data_len);
+
+	mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
+	keyhdrlen = sizeof(*key) + mic_len + 2;
+
+	if (data_len < sizeof(*hdr) + keyhdrlen) {
+		wpa_printf(MSG_DEBUG, "WPA: Ignore too short EAPOL-Key frame");
+		return;
+	}
+
+	hdr = (struct ieee802_1x_hdr *) data;
+	key = (struct wpa_eapol_key *) (hdr + 1);
+	mic = (u8 *) (key + 1);
+	key_info = WPA_GET_BE16(key->key_info);
+	key_data = mic + mic_len + 2;
+	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(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)",
+		    key, sizeof(*key));
+	wpa_hexdump(MSG_MSGDUMP, "WPA: EAPOL-Key Key MIC",
+		    mic, mic_len);
+	if (key_data_length > data_len - sizeof(*hdr) - keyhdrlen) {
+		wpa_printf(MSG_INFO,
+			   "WPA: Invalid EAPOL-Key frame - key_data overflow (%d > %zu)",
+			   key_data_length,
+			   data_len - sizeof(*hdr) - keyhdrlen);
+		return;
+	}
+
+	if (sm->wpa == WPA_VERSION_WPA2) {
+		if (key->type == EAPOL_KEY_TYPE_WPA) {
+			/*
+			 * Some deployed station implementations seem to send
+			 * msg 4/4 with incorrect type value in WPA2 mode.
+			 */
+			wpa_printf(MSG_DEBUG,
+				   "Workaround: Allow EAPOL-Key with unexpected WPA type in RSN mode");
+		} else if (key->type != EAPOL_KEY_TYPE_RSN) {
+			wpa_printf(MSG_DEBUG,
+				   "Ignore EAPOL-Key with unexpected type %d in RSN mode",
+				   key->type);
+			return;
+		}
+	} else {
+		if (key->type != EAPOL_KEY_TYPE_WPA) {
+			wpa_printf(MSG_DEBUG,
+				   "Ignore EAPOL-Key with unexpected type %d in WPA mode",
+				   key->type);
+			return;
+		}
+	}
+
+	wpa_hexdump(MSG_DEBUG, "WPA: Received Key Nonce", key->key_nonce,
+		    WPA_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "WPA: Received Replay Counter",
+		    key->replay_counter, WPA_REPLAY_COUNTER_LEN);
+
+	/* FIX: verify that the EAPOL-Key frame was encrypted if pairwise keys
+	 * are set */
+
+	if (key_info & WPA_KEY_INFO_SMK_MESSAGE) {
+		wpa_printf(MSG_DEBUG, "WPA: Ignore SMK message");
+		return;
+	}
+
+	/* TODO: Make this more robust for distinguising EAPOL-Key msg 2/4 from
+	 * 4/4. Secure=1 is used in msg 2/4 when doing PTK rekeying, so the
+	 * MLD mechanism here does not work without the somewhat undesired check
+	 * on wpa_ptk_state.. Would likely need to decrypt Key Data first to be
+	 * able to know which message this is in MLO cases.. */
+	if (key_info & WPA_KEY_INFO_REQUEST) {
+		msg = REQUEST;
+		msgtxt = "Request";
+	} else if (!(key_info & WPA_KEY_INFO_KEY_TYPE)) {
+		msg = GROUP_2;
+		msgtxt = "2/2 Group";
+	} else if (key_data_length == 0 ||
+		   (mic_len == 0 && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) &&
+		    key_data_length == AES_BLOCK_SIZE) ||
+		   (is_mld && (key_info & WPA_KEY_INFO_SECURE) &&
+		    sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING)) {
+		msg = PAIRWISE_4;
+		msgtxt = "4/4 Pairwise";
+	} else {
+		msg = PAIRWISE_2;
+		msgtxt = "2/4 Pairwise";
+	}
+
+	if (msg == REQUEST || msg == PAIRWISE_2 || msg == PAIRWISE_4 ||
+	    msg == GROUP_2) {
+		u16 ver = key_info & WPA_KEY_INFO_TYPE_MASK;
+		if (sm->pairwise == WPA_CIPHER_CCMP ||
+		    sm->pairwise == WPA_CIPHER_GCMP) {
+			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, wpa_auth_get_spa(sm),
+						LOGGER_WARNING,
+						"advertised support for AES-128-CMAC, but did not use it");
+				return;
+			}
+
+			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, wpa_auth_get_spa(sm),
+						LOGGER_WARNING,
+						"did not use HMAC-SHA1-AES with CCMP/GCMP");
+				return;
+			}
+		}
+
+		if (wpa_use_akm_defined(sm->wpa_key_mgmt) &&
+		    ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
+			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;
+		}
+	}
+
+	if (key_info & WPA_KEY_INFO_REQUEST) {
+		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, wpa_auth_get_spa(sm),
+					LOGGER_WARNING,
+					"received EAPOL-Key request with replayed counter");
+			return;
+		}
+	}
+
+	if (!(key_info & WPA_KEY_INFO_REQUEST) &&
+	    !wpa_replay_counter_valid(sm->key_replay, key->replay_counter)) {
+		int i;
+
+		if (msg == PAIRWISE_2 &&
+		    wpa_replay_counter_valid(sm->prev_key_replay,
+					     key->replay_counter) &&
+		    sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING &&
+		    os_memcmp(sm->SNonce, key->key_nonce, WPA_NONCE_LEN) != 0)
+		{
+			/*
+			 * Some supplicant implementations (e.g., Windows XP
+			 * WZC) update SNonce for each EAPOL-Key 2/4. This
+			 * breaks the workaround on accepting any of the
+			 * 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, 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);
+			sm->alt_snonce_valid = true;
+			os_memcpy(sm->alt_replay_counter,
+				  sm->key_replay[0].counter,
+				  WPA_REPLAY_COUNTER_LEN);
+			goto continue_processing;
+		}
+
+		if (msg == PAIRWISE_4 && sm->alt_snonce_valid &&
+		    sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING &&
+		    os_memcmp(key->replay_counter, sm->alt_replay_counter,
+			      WPA_REPLAY_COUNTER_LEN) == 0) {
+			/*
+			 * Supplicant may still be using the old SNonce since
+			 * there was two EAPOL-Key 2/4 messages and they had
+			 * different SNonce values.
+			 */
+			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;
+		}
+
+		if (msg == PAIRWISE_2 &&
+		    wpa_replay_counter_valid(sm->prev_key_replay,
+					     key->replay_counter) &&
+		    sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING) {
+			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, wpa_auth_get_spa(sm),
+					 LOGGER_DEBUG,
+					 "received EAPOL-Key %s with unexpected replay counter",
+					 msgtxt);
+		}
+		for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) {
+			if (!sm->key_replay[i].valid)
+				break;
+			wpa_hexdump(MSG_DEBUG, "pending replay counter",
+				    sm->key_replay[i].counter,
+				    WPA_REPLAY_COUNTER_LEN);
+		}
+		wpa_hexdump(MSG_DEBUG, "received replay counter",
+			    key->replay_counter, WPA_REPLAY_COUNTER_LEN);
+		return;
+	}
+
+continue_processing:
+#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, 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;
+	}
+#endif /* CONFIG_FILS */
+
+	switch (msg) {
+	case PAIRWISE_2:
+		if (sm->wpa_ptk_state != WPA_PTK_PTKSTART &&
+		    sm->wpa_ptk_state != WPA_PTK_PTKCALCNEGOTIATING &&
+		    (!sm->update_snonce ||
+		     sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING)) {
+			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;
+		}
+		random_add_randomness(key->key_nonce, WPA_NONCE_LEN);
+		if (sm->group->reject_4way_hs_for_entropy) {
+			/*
+			 * The system did not have enough entropy to generate
+			 * strong random numbers. Reject the first 4-way
+			 * handshake(s) and collect some entropy based on the
+			 * information from it. Once enough entropy is
+			 * available, the next atempt will trigger GMK/Key
+			 * Counter update and the station will be allowed to
+			 * continue.
+			 */
+			wpa_printf(MSG_DEBUG,
+				   "WPA: Reject 4-way handshake to collect more entropy for random number generation");
+			random_mark_pool_ready();
+			wpa_sta_disconnect(wpa_auth, sm->addr,
+					   WLAN_REASON_PREV_AUTH_NOT_VALID);
+			return;
+		}
+		break;
+	case PAIRWISE_4:
+		if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING ||
+		    !sm->PTK_valid) {
+			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;
+		}
+		break;
+	case GROUP_2:
+		if (sm->wpa_ptk_group_state != WPA_PTK_GROUP_REKEYNEGOTIATING
+		    || !sm->PTK_valid) {
+			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;
+		}
+		break;
+	case REQUEST:
+		break;
+	}
+
+	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, 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, wpa_auth_get_spa(sm), LOGGER_INFO,
+				"received invalid EAPOL-Key: Key MIC not set");
+		return;
+	}
+
+#ifdef CONFIG_FILS
+	if (wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
+	    (key_info & WPA_KEY_INFO_MIC)) {
+		wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
+				"received invalid EAPOL-Key: Key MIC set");
+		return;
+	}
+#endif /* CONFIG_FILS */
+
+	sm->MICVerified = false;
+	if (sm->PTK_valid && !sm->update_snonce) {
+		if (mic_len &&
+		    wpa_verify_key_mic(sm->wpa_key_mgmt, sm->pmk_len, &sm->PTK,
+				       data, data_len) &&
+		    (msg != PAIRWISE_4 || !sm->alt_snonce_valid ||
+		     wpa_try_alt_snonce(sm, data, data_len))) {
+			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,
+				   "TEST: Ignore Key MIC failure for fuzz testing");
+			goto continue_fuzz;
+#endif /* TEST_FUZZ */
+			return;
+		}
+#ifdef CONFIG_FILS
+		if (!mic_len &&
+		    wpa_aead_decrypt(sm, &sm->PTK, data, data_len,
+				     &key_data_length) < 0) {
+			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,
+				   "TEST: Ignore Key MIC failure for fuzz testing");
+			goto continue_fuzz;
+#endif /* TEST_FUZZ */
+			return;
+		}
+#endif /* CONFIG_FILS */
+#ifdef TEST_FUZZ
+	continue_fuzz:
+#endif /* TEST_FUZZ */
+		sm->MICVerified = true;
+		eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm);
+		sm->pending_1_of_4_timeout = 0;
+	}
+
+	if (key_info & WPA_KEY_INFO_REQUEST) {
+		if (sm->MICVerified) {
+			sm->req_replay_counter_used = 1;
+			os_memcpy(sm->req_replay_counter, key->replay_counter,
+				  WPA_REPLAY_COUNTER_LEN);
+		} else {
+			wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+					LOGGER_INFO,
+					"received EAPOL-Key request with invalid MIC");
+			return;
+		}
+
+		/*
+		 * TODO: should decrypt key data field if encryption was used;
+		 * even though MAC address KDE is not normally encrypted,
+		 * supplicant is allowed to encrypt it.
+		 */
+		if (key_info & WPA_KEY_INFO_ERROR) {
+			if (wpa_receive_error_report(
+				    wpa_auth, sm,
+				    !(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, 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 &&
+			   wpa_parse_kde_ies(key_data, key_data_length,
+					     &kde) == 0 &&
+			   kde.mac_addr) {
+		} else {
+			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))
+				wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG,
+						"skip new GTK rekey - already in process");
+			else
+				wpa_rekey_gtk(wpa_auth, NULL);
+		}
+	} else {
+		/* Do not allow the same key replay counter to be reused. */
+		wpa_replay_counter_mark_invalid(sm->key_replay,
+						key->replay_counter);
+
+		if (msg == PAIRWISE_2) {
+			/*
+			 * Maintain a copy of the pending EAPOL-Key frames in
+			 * case the EAPOL-Key frame was retransmitted. This is
+			 * needed to allow EAPOL-Key msg 2/4 reply to another
+			 * pending msg 1/4 to update the SNonce to work around
+			 * unexpected supplicant behavior.
+			 */
+			os_memcpy(sm->prev_key_replay, sm->key_replay,
+				  sizeof(sm->key_replay));
+		} else {
+			os_memset(sm->prev_key_replay, 0,
+				  sizeof(sm->prev_key_replay));
+		}
+
+		/*
+		 * Make sure old valid counters are not accepted anymore and
+		 * do not get copied again.
+		 */
+		wpa_replay_counter_mark_invalid(sm->key_replay, NULL);
+	}
+
+	os_free(sm->last_rx_eapol_key);
+	sm->last_rx_eapol_key = os_memdup(data, data_len);
+	if (!sm->last_rx_eapol_key)
+		return;
+	sm->last_rx_eapol_key_len = data_len;
+
+	sm->rx_eapol_key_secure = !!(key_info & WPA_KEY_INFO_SECURE);
+	sm->EAPOLKeyReceived = true;
+	sm->EAPOLKeyPairwise = !!(key_info & WPA_KEY_INFO_KEY_TYPE);
+	sm->EAPOLKeyRequest = !!(key_info & WPA_KEY_INFO_REQUEST);
+	os_memcpy(sm->SNonce, key->key_nonce, WPA_NONCE_LEN);
+	wpa_sm_step(sm);
+}
+
+
+static int wpa_gmk_to_gtk(const u8 *gmk, const char *label, const u8 *addr,
+			  const u8 *gnonce, u8 *gtk, size_t gtk_len)
+{
+	u8 data[ETH_ALEN + WPA_NONCE_LEN + 8 + WPA_GTK_MAX_LEN];
+	u8 *pos;
+	int ret = 0;
+
+	/* GTK = PRF-X(GMK, "Group key expansion",
+	 *	AA || GNonce || Time || random data)
+	 * The example described in the IEEE 802.11 standard uses only AA and
+	 * GNonce as inputs here. Add some more entropy since this derivation
+	 * is done only at the Authenticator and as such, does not need to be
+	 * exactly same.
+	 */
+	os_memset(data, 0, sizeof(data));
+	os_memcpy(data, addr, ETH_ALEN);
+	os_memcpy(data + ETH_ALEN, gnonce, WPA_NONCE_LEN);
+	pos = data + ETH_ALEN + WPA_NONCE_LEN;
+	wpa_get_ntp_timestamp(pos);
+#ifdef TEST_FUZZ
+	os_memset(pos, 0xef, 8);
+#endif /* TEST_FUZZ */
+	pos += 8;
+	if (random_get_bytes(pos, gtk_len) < 0)
+		ret = -1;
+
+#ifdef CONFIG_SHA384
+	if (sha384_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data),
+		       gtk, gtk_len) < 0)
+		ret = -1;
+#else /* CONFIG_SHA384 */
+#ifdef CONFIG_SHA256
+	if (sha256_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data),
+		       gtk, gtk_len) < 0)
+		ret = -1;
+#else /* CONFIG_SHA256 */
+	if (sha1_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data),
+		     gtk, gtk_len) < 0)
+		ret = -1;
+#endif /* CONFIG_SHA256 */
+#endif /* CONFIG_SHA384 */
+
+	forced_memzero(data, sizeof(data));
+
+	return ret;
+}
+
+
+static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_authenticator *wpa_auth = eloop_ctx;
+	struct wpa_state_machine *sm = timeout_ctx;
+
+	if (sm->waiting_radius_psk) {
+		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
+				"Ignore EAPOL-Key timeout while waiting for RADIUS PSK");
+		return;
+	}
+
+	sm->pending_1_of_4_timeout = 0;
+	wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
+			"EAPOL-Key timeout");
+	sm->TimeoutEvt = true;
+	wpa_sm_step(sm);
+}
+
+
+void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
+		      struct wpa_state_machine *sm, int key_info,
+		      const u8 *key_rsc, const u8 *nonce,
+		      const u8 *kde, size_t kde_len,
+		      int keyidx, int encr, int force_version)
+{
+	struct wpa_auth_config *conf = &wpa_auth->conf;
+	struct ieee802_1x_hdr *hdr;
+	struct wpa_eapol_key *key;
+	size_t len, mic_len, keyhdrlen;
+	int alg;
+	int key_data_len, pad_len = 0;
+	u8 *buf, *pos;
+	int version, pairwise;
+	int i;
+	u8 *key_mic, *key_data;
+
+	mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
+	keyhdrlen = sizeof(*key) + mic_len + 2;
+
+	len = sizeof(struct ieee802_1x_hdr) + keyhdrlen;
+
+	if (force_version)
+		version = force_version;
+	else if (wpa_use_akm_defined(sm->wpa_key_mgmt))
+		version = WPA_KEY_INFO_TYPE_AKM_DEFINED;
+	else if (wpa_use_cmac(sm->wpa_key_mgmt))
+		version = WPA_KEY_INFO_TYPE_AES_128_CMAC;
+	else if (sm->pairwise != WPA_CIPHER_TKIP)
+		version = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
+	else
+		version = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
+
+	pairwise = !!(key_info & WPA_KEY_INFO_KEY_TYPE);
+
+	wpa_printf(MSG_DEBUG,
+		   "WPA: Send EAPOL(version=%d secure=%d mic=%d ack=%d install=%d pairwise=%d kde_len=%zu keyidx=%d encr=%d)",
+		   version,
+		   (key_info & WPA_KEY_INFO_SECURE) ? 1 : 0,
+		   (key_info & WPA_KEY_INFO_MIC) ? 1 : 0,
+		   (key_info & WPA_KEY_INFO_ACK) ? 1 : 0,
+		   (key_info & WPA_KEY_INFO_INSTALL) ? 1 : 0,
+		   pairwise, kde_len, keyidx, encr);
+
+	key_data_len = kde_len;
+
+	if ((version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
+	     wpa_use_aes_key_wrap(sm->wpa_key_mgmt) ||
+	     version == WPA_KEY_INFO_TYPE_AES_128_CMAC) && encr) {
+		pad_len = key_data_len % 8;
+		if (pad_len)
+			pad_len = 8 - pad_len;
+		key_data_len += pad_len + 8;
+	}
+
+	len += key_data_len;
+	if (!mic_len && encr)
+		len += AES_BLOCK_SIZE;
+
+	hdr = os_zalloc(len);
+	if (!hdr)
+		return;
+	hdr->version = conf->eapol_version;
+	hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
+	hdr->length = host_to_be16(len  - sizeof(*hdr));
+	key = (struct wpa_eapol_key *) (hdr + 1);
+	key_mic = (u8 *) (key + 1);
+	key_data = ((u8 *) (hdr + 1)) + keyhdrlen;
+
+	key->type = sm->wpa == WPA_VERSION_WPA2 ?
+		EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
+	key_info |= version;
+	if (encr && sm->wpa == WPA_VERSION_WPA2)
+		key_info |= WPA_KEY_INFO_ENCR_KEY_DATA;
+	if (sm->wpa != WPA_VERSION_WPA2)
+		key_info |= keyidx << WPA_KEY_INFO_KEY_INDEX_SHIFT;
+	WPA_PUT_BE16(key->key_info, key_info);
+
+	alg = pairwise ? sm->pairwise : conf->wpa_group;
+	if (sm->wpa == WPA_VERSION_WPA2 && !pairwise)
+		WPA_PUT_BE16(key->key_length, 0);
+	else
+		WPA_PUT_BE16(key->key_length, wpa_cipher_key_len(alg));
+
+	for (i = RSNA_MAX_EAPOL_RETRIES - 1; i > 0; i--) {
+		sm->key_replay[i].valid = sm->key_replay[i - 1].valid;
+		os_memcpy(sm->key_replay[i].counter,
+			  sm->key_replay[i - 1].counter,
+			  WPA_REPLAY_COUNTER_LEN);
+	}
+	inc_byte_array(sm->key_replay[0].counter, WPA_REPLAY_COUNTER_LEN);
+	os_memcpy(key->replay_counter, sm->key_replay[0].counter,
+		  WPA_REPLAY_COUNTER_LEN);
+	wpa_hexdump(MSG_DEBUG, "WPA: Replay Counter",
+		    key->replay_counter, WPA_REPLAY_COUNTER_LEN);
+	sm->key_replay[0].valid = true;
+
+	if (nonce)
+		os_memcpy(key->key_nonce, nonce, WPA_NONCE_LEN);
+
+	if (key_rsc)
+		os_memcpy(key->key_rsc, key_rsc, WPA_KEY_RSC_LEN);
+
+	if (kde && !encr) {
+		os_memcpy(key_data, kde, kde_len);
+		WPA_PUT_BE16(key_mic + mic_len, kde_len);
+#ifdef CONFIG_FILS
+	} else if (!mic_len && kde) {
+		const u8 *aad[1];
+		size_t aad_len[1];
+
+		WPA_PUT_BE16(key_mic, AES_BLOCK_SIZE + kde_len);
+		wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data",
+				kde, kde_len);
+
+		wpa_hexdump_key(MSG_DEBUG, "WPA: KEK",
+				sm->PTK.kek, sm->PTK.kek_len);
+		/* AES-SIV AAD from EAPOL protocol version field (inclusive) to
+		 * to Key Data (exclusive). */
+		aad[0] = (u8 *) hdr;
+		aad_len[0] = key_mic + 2 - (u8 *) hdr;
+		if (aes_siv_encrypt(sm->PTK.kek, sm->PTK.kek_len, kde, kde_len,
+				    1, aad, aad_len, key_mic + 2) < 0) {
+			wpa_printf(MSG_DEBUG, "WPA: AES-SIV encryption failed");
+			return;
+		}
+
+		wpa_hexdump(MSG_DEBUG, "WPA: Encrypted Key Data from SIV",
+			    key_mic + 2, AES_BLOCK_SIZE + kde_len);
+#endif /* CONFIG_FILS */
+	} else if (encr && kde) {
+		buf = os_zalloc(key_data_len);
+		if (!buf) {
+			os_free(hdr);
+			return;
+		}
+		pos = buf;
+		os_memcpy(pos, kde, kde_len);
+		pos += kde_len;
+
+		if (pad_len)
+			*pos++ = 0xdd;
+
+		wpa_hexdump_key(MSG_DEBUG,
+				"Plaintext EAPOL-Key Key Data (+ padding)",
+				buf, key_data_len);
+		if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
+		    wpa_use_aes_key_wrap(sm->wpa_key_mgmt) ||
+		    version == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+			wpa_hexdump_key(MSG_DEBUG, "RSN: AES-WRAP using KEK",
+					sm->PTK.kek, sm->PTK.kek_len);
+			if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len,
+				     (key_data_len - 8) / 8, buf, key_data)) {
+				os_free(hdr);
+				bin_clear_free(buf, key_data_len);
+				return;
+			}
+			wpa_hexdump(MSG_DEBUG,
+				    "RSN: Encrypted Key Data from AES-WRAP",
+				    key_data, key_data_len);
+			WPA_PUT_BE16(key_mic + mic_len, key_data_len);
+#if !defined(CONFIG_NO_RC4) && !defined(CONFIG_FIPS)
+		} else if (sm->PTK.kek_len == 16) {
+			u8 ek[32];
+
+			wpa_printf(MSG_DEBUG,
+				   "WPA: Encrypt Key Data using RC4");
+			os_memcpy(key->key_iv,
+				  sm->group->Counter + WPA_NONCE_LEN - 16, 16);
+			inc_byte_array(sm->group->Counter, WPA_NONCE_LEN);
+			os_memcpy(ek, key->key_iv, 16);
+			os_memcpy(ek + 16, sm->PTK.kek, sm->PTK.kek_len);
+			os_memcpy(key_data, buf, key_data_len);
+			rc4_skip(ek, 32, 256, key_data, key_data_len);
+			WPA_PUT_BE16(key_mic + mic_len, key_data_len);
+#endif /* !(CONFIG_NO_RC4 || CONFIG_FIPS) */
+		} else {
+			os_free(hdr);
+			bin_clear_free(buf, key_data_len);
+			return;
+		}
+		bin_clear_free(buf, key_data_len);
+	}
+
+	if (key_info & WPA_KEY_INFO_MIC) {
+		if (!sm->PTK_valid || !mic_len) {
+			wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+					LOGGER_DEBUG,
+					"PTK not valid when sending EAPOL-Key frame");
+			os_free(hdr);
+			return;
+		}
+
+		if (wpa_eapol_key_mic(sm->PTK.kck, sm->PTK.kck_len,
+				      sm->wpa_key_mgmt, version,
+				      (u8 *) hdr, len, key_mic) < 0) {
+			os_free(hdr);
+			return;
+		}
+#ifdef CONFIG_TESTING_OPTIONS
+		if (!pairwise &&
+		    conf->corrupt_gtk_rekey_mic_probability > 0.0 &&
+		    drand48() < conf->corrupt_gtk_rekey_mic_probability) {
+			wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+					LOGGER_INFO,
+					"Corrupting group EAPOL-Key Key MIC");
+			key_mic[0]++;
+		}
+#endif /* CONFIG_TESTING_OPTIONS */
+	}
+
+	wpa_auth_set_eapol(wpa_auth, sm->addr, WPA_EAPOL_inc_EapolFramesTx, 1);
+	wpa_hexdump(MSG_DEBUG, "Send EAPOL-Key msg", hdr, len);
+	wpa_auth_send_eapol(wpa_auth, sm->addr, (u8 *) hdr, len,
+			    sm->pairwise_set);
+	os_free(hdr);
+}
+
+
+static void wpa_send_eapol(struct wpa_authenticator *wpa_auth,
+			   struct wpa_state_machine *sm, int key_info,
+			   const u8 *key_rsc, const u8 *nonce,
+			   const u8 *kde, size_t kde_len,
+			   int keyidx, int encr)
+{
+	int timeout_ms;
+	int pairwise = key_info & WPA_KEY_INFO_KEY_TYPE;
+	u32 ctr;
+
+	if (!sm)
+		return;
+
+	ctr = pairwise ? sm->TimeoutCtr : sm->GTimeoutCtr;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	/* When delay_eapol_tx is true, delay the EAPOL-Key transmission by
+	 * sending it only on the last attempt after all timeouts for the prior
+	 * skipped attemps. */
+	if (wpa_auth->conf.delay_eapol_tx &&
+	    ctr != wpa_auth->conf.wpa_pairwise_update_count) {
+		wpa_msg(sm->wpa_auth->conf.msg_ctx, MSG_INFO,
+			"DELAY-EAPOL-TX-%d", ctr);
+		goto skip_tx;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+	__wpa_send_eapol(wpa_auth, sm, key_info, key_rsc, nonce, kde, kde_len,
+			 keyidx, encr, 0);
+#ifdef CONFIG_TESTING_OPTIONS
+skip_tx:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	if (ctr == 1 && wpa_auth->conf.tx_status)
+		timeout_ms = pairwise ? eapol_key_timeout_first :
+			eapol_key_timeout_first_group;
+	else
+		timeout_ms = eapol_key_timeout_subseq;
+	if (wpa_auth->conf.wpa_disable_eapol_key_retries &&
+	    (!pairwise || (key_info & WPA_KEY_INFO_MIC)))
+		timeout_ms = eapol_key_timeout_no_retrans;
+	if (pairwise && ctr == 1 && !(key_info & WPA_KEY_INFO_MIC))
+		sm->pending_1_of_4_timeout = 1;
+#ifdef TEST_FUZZ
+	timeout_ms = 1;
+#endif /* TEST_FUZZ */
+	wpa_printf(MSG_DEBUG,
+		   "WPA: Use EAPOL-Key timeout of %u ms (retry counter %u)",
+		   timeout_ms, ctr);
+	eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000,
+			       wpa_send_eapol_timeout, wpa_auth, sm);
+}
+
+
+static int wpa_verify_key_mic(int akmp, size_t pmk_len, struct wpa_ptk *PTK,
+			      u8 *data, size_t data_len)
+{
+	struct ieee802_1x_hdr *hdr;
+	struct wpa_eapol_key *key;
+	u16 key_info;
+	int ret = 0;
+	u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN], *mic_pos;
+	size_t mic_len = wpa_mic_len(akmp, pmk_len);
+
+	if (data_len < sizeof(*hdr) + sizeof(*key))
+		return -1;
+
+	hdr = (struct ieee802_1x_hdr *) data;
+	key = (struct wpa_eapol_key *) (hdr + 1);
+	mic_pos = (u8 *) (key + 1);
+	key_info = WPA_GET_BE16(key->key_info);
+	os_memcpy(mic, mic_pos, mic_len);
+	os_memset(mic_pos, 0, mic_len);
+	if (wpa_eapol_key_mic(PTK->kck, PTK->kck_len, akmp,
+			      key_info & WPA_KEY_INFO_TYPE_MASK,
+			      data, data_len, mic_pos) ||
+	    os_memcmp_const(mic, mic_pos, mic_len) != 0)
+		ret = -1;
+	os_memcpy(mic_pos, mic, mic_len);
+	return ret;
+}
+
+
+void wpa_remove_ptk(struct wpa_state_machine *sm)
+{
+	sm->PTK_valid = false;
+	os_memset(&sm->PTK, 0, sizeof(sm->PTK));
+
+	wpa_auth_remove_ptksa(sm->wpa_auth, sm->addr, sm->pairwise);
+
+	if (wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, NULL,
+			     0, KEY_FLAG_PAIRWISE))
+		wpa_printf(MSG_DEBUG,
+			   "RSN: PTK removal from the driver failed");
+	if (sm->use_ext_key_id &&
+	    wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 1, NULL,
+			     0, KEY_FLAG_PAIRWISE))
+		wpa_printf(MSG_DEBUG,
+			   "RSN: PTK Key ID 1 removal from the driver failed");
+	sm->pairwise_set = false;
+	eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
+}
+
+
+int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event)
+{
+	int remove_ptk = 1;
+
+	if (!sm)
+		return -1;
+
+	wpa_auth_vlogger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
+			 "event %d notification", event);
+
+	switch (event) {
+	case WPA_AUTH:
+#ifdef CONFIG_MESH
+		/* PTKs are derived through AMPE */
+		if (wpa_auth_start_ampe(sm->wpa_auth, sm->addr)) {
+			/* not mesh */
+			break;
+		}
+		return 0;
+#endif /* CONFIG_MESH */
+	case WPA_ASSOC:
+		break;
+	case WPA_DEAUTH:
+	case WPA_DISASSOC:
+		sm->DeauthenticationRequest = true;
+		os_memset(sm->PMK, 0, sizeof(sm->PMK));
+		sm->pmk_len = 0;
+#ifdef CONFIG_IEEE80211R_AP
+		os_memset(sm->xxkey, 0, sizeof(sm->xxkey));
+		sm->xxkey_len = 0;
+		os_memset(sm->pmk_r1, 0, sizeof(sm->pmk_r1));
+		sm->pmk_r1_len = 0;
+#endif /* CONFIG_IEEE80211R_AP */
+		break;
+	case WPA_REAUTH:
+	case WPA_REAUTH_EAPOL:
+		if (!sm->started) {
+			/*
+			 * When using WPS, we may end up here if the STA
+			 * manages to re-associate without the previous STA
+			 * entry getting removed. Consequently, we need to make
+			 * sure that the WPA state machines gets initialized
+			 * properly at this point.
+			 */
+			wpa_printf(MSG_DEBUG,
+				   "WPA state machine had not been started - initialize now");
+			sm->started = 1;
+			sm->Init = true;
+			if (wpa_sm_step(sm) == 1)
+				return 1; /* should not really happen */
+			sm->Init = false;
+			sm->AuthenticationRequest = true;
+			break;
+		}
+
+		if (sm->ptkstart_without_success > 3) {
+			wpa_printf(MSG_INFO,
+				   "WPA: Multiple EAP reauth attempts without 4-way handshake completion, disconnect "
+				   MACSTR, MAC2STR(sm->addr));
+			sm->Disconnect = true;
+			break;
+		}
+
+		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(wpa_auth_get_spa(sm)));
+			sm->Disconnect = true;
+			/* Try to encourage the STA to reconnect */
+			sm->disconnect_reason =
+				WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA;
+			break;
+		}
+
+		if (sm->use_ext_key_id)
+			sm->keyidx_active ^= 1; /* flip Key ID */
+
+		if (sm->GUpdateStationKeys) {
+			/*
+			 * Reauthentication cancels the pending group key
+			 * update for this STA.
+			 */
+			sm->group->GKeyDoneStations--;
+			sm->GUpdateStationKeys = false;
+			sm->PtkGroupInit = true;
+		}
+		sm->ReAuthenticationRequest = true;
+		break;
+	case WPA_ASSOC_FT:
+#ifdef CONFIG_IEEE80211R_AP
+		wpa_printf(MSG_DEBUG,
+			   "FT: Retry PTK configuration after association");
+		wpa_ft_install_ptk(sm, 1);
+
+		/* Using FT protocol, not WPA auth state machine */
+		sm->ft_completed = 1;
+		wpa_auth_set_ptk_rekey_timer(sm);
+		return 0;
+#else /* CONFIG_IEEE80211R_AP */
+		break;
+#endif /* CONFIG_IEEE80211R_AP */
+	case WPA_ASSOC_FILS:
+#ifdef CONFIG_FILS
+		wpa_printf(MSG_DEBUG,
+			   "FILS: TK configuration after association");
+		fils_set_tk(sm);
+		sm->fils_completed = 1;
+		return 0;
+#else /* CONFIG_FILS */
+		break;
+#endif /* CONFIG_FILS */
+	case WPA_DRV_STA_REMOVED:
+		sm->tk_already_set = false;
+		return 0;
+	}
+
+#ifdef CONFIG_IEEE80211R_AP
+	sm->ft_completed = 0;
+#endif /* CONFIG_IEEE80211R_AP */
+
+	if (sm->mgmt_frame_prot && event == WPA_AUTH)
+		remove_ptk = 0;
+#ifdef CONFIG_FILS
+	if (wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
+	    (event == WPA_AUTH || event == WPA_ASSOC))
+		remove_ptk = 0;
+#endif /* CONFIG_FILS */
+
+	if (remove_ptk) {
+		sm->PTK_valid = false;
+		os_memset(&sm->PTK, 0, sizeof(sm->PTK));
+
+		if (event != WPA_REAUTH_EAPOL)
+			wpa_remove_ptk(sm);
+	}
+
+	if (sm->in_step_loop) {
+		/*
+		 * wpa_sm_step() is already running - avoid recursive call to
+		 * it by making the existing loop process the new update.
+		 */
+		sm->changed = true;
+		return 0;
+	}
+	return wpa_sm_step(sm);
+}
+
+
+SM_STATE(WPA_PTK, INITIALIZE)
+{
+	SM_ENTRY_MA(WPA_PTK, INITIALIZE, wpa_ptk);
+	if (sm->Init) {
+		/* Init flag is not cleared here, so avoid busy
+		 * loop by claiming nothing changed. */
+		sm->changed = false;
+	}
+
+	sm->keycount = 0;
+	if (sm->GUpdateStationKeys)
+		sm->group->GKeyDoneStations--;
+	sm->GUpdateStationKeys = false;
+	if (sm->wpa == WPA_VERSION_WPA)
+		sm->PInitAKeys = false;
+	if (1 /* Unicast cipher supported AND (ESS OR ((IBSS or WDS) and
+	       * Local AA > Remote AA)) */) {
+		sm->Pair = true;
+	}
+	wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portEnabled, 0);
+	wpa_remove_ptk(sm);
+	wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid, 0);
+	sm->TimeoutCtr = 0;
+	if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
+	    sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP ||
+	    sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE) {
+		wpa_auth_set_eapol(sm->wpa_auth, sm->addr,
+				   WPA_EAPOL_authorized, 0);
+	}
+}
+
+
+SM_STATE(WPA_PTK, DISCONNECT)
+{
+	u16 reason = sm->disconnect_reason;
+
+	SM_ENTRY_MA(WPA_PTK, DISCONNECT, wpa_ptk);
+	sm->Disconnect = false;
+	sm->disconnect_reason = 0;
+	if (!reason)
+		reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
+	wpa_sta_disconnect(sm->wpa_auth, sm->addr, reason);
+}
+
+
+SM_STATE(WPA_PTK, DISCONNECTED)
+{
+	SM_ENTRY_MA(WPA_PTK, DISCONNECTED, wpa_ptk);
+	sm->DeauthenticationRequest = false;
+}
+
+
+SM_STATE(WPA_PTK, AUTHENTICATION)
+{
+	SM_ENTRY_MA(WPA_PTK, AUTHENTICATION, wpa_ptk);
+	os_memset(&sm->PTK, 0, sizeof(sm->PTK));
+	sm->PTK_valid = false;
+	wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portControl_Auto,
+			   1);
+	wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portEnabled, 1);
+	sm->AuthenticationRequest = false;
+}
+
+
+static void wpa_group_ensure_init(struct wpa_authenticator *wpa_auth,
+				  struct wpa_group *group)
+{
+	if (group->first_sta_seen)
+		return;
+	/*
+	 * System has run bit further than at the time hostapd was started
+	 * potentially very early during boot up. This provides better chances
+	 * of collecting more randomness on embedded systems. Re-initialize the
+	 * GMK and Counter here to improve their strength if there was not
+	 * enough entropy available immediately after system startup.
+	 */
+	wpa_printf(MSG_DEBUG,
+		   "WPA: Re-initialize GMK/Counter on first station");
+	if (random_pool_ready() != 1) {
+		wpa_printf(MSG_INFO,
+			   "WPA: Not enough entropy in random pool to proceed - reject first 4-way handshake");
+		group->reject_4way_hs_for_entropy = true;
+	} else {
+		group->first_sta_seen = true;
+		group->reject_4way_hs_for_entropy = false;
+	}
+
+	if (wpa_group_init_gmk_and_counter(wpa_auth, group) < 0 ||
+	    wpa_gtk_update(wpa_auth, group) < 0 ||
+	    wpa_group_config_group_keys(wpa_auth, group) < 0) {
+		wpa_printf(MSG_INFO, "WPA: GMK/GTK setup failed");
+		group->first_sta_seen = false;
+		group->reject_4way_hs_for_entropy = true;
+	}
+}
+
+
+SM_STATE(WPA_PTK, AUTHENTICATION2)
+{
+	SM_ENTRY_MA(WPA_PTK, AUTHENTICATION2, wpa_ptk);
+
+	wpa_group_ensure_init(sm->wpa_auth, sm->group);
+	sm->ReAuthenticationRequest = false;
+
+	/*
+	 * Definition of ANonce selection in IEEE Std 802.11i-2004 is somewhat
+	 * ambiguous. The Authenticator state machine uses a counter that is
+	 * incremented by one for each 4-way handshake. However, the security
+	 * analysis of 4-way handshake points out that unpredictable nonces
+	 * help in preventing precomputation attacks. Instead of the state
+	 * machine definition, use an unpredictable nonce value here to provide
+	 * stronger protection against potential precomputation attacks.
+	 */
+	if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) {
+		wpa_printf(MSG_ERROR,
+			   "WPA: Failed to get random data for ANonce.");
+		sm->Disconnect = true;
+		return;
+	}
+	wpa_hexdump(MSG_DEBUG, "WPA: Assign ANonce", sm->ANonce,
+		    WPA_NONCE_LEN);
+	/* IEEE 802.11i does not clear TimeoutCtr here, but this is more
+	 * logical place than INITIALIZE since AUTHENTICATION2 can be
+	 * re-entered on ReAuthenticationRequest without going through
+	 * INITIALIZE. */
+	sm->TimeoutCtr = 0;
+}
+
+
+static int wpa_auth_sm_ptk_update(struct wpa_state_machine *sm)
+{
+	if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) {
+		wpa_printf(MSG_ERROR,
+			   "WPA: Failed to get random data for ANonce");
+		sm->Disconnect = true;
+		return -1;
+	}
+	wpa_hexdump(MSG_DEBUG, "WPA: Assign new ANonce", sm->ANonce,
+		    WPA_NONCE_LEN);
+	sm->TimeoutCtr = 0;
+	return 0;
+}
+
+
+SM_STATE(WPA_PTK, INITPMK)
+{
+	u8 msk[2 * PMK_LEN];
+	size_t len = 2 * PMK_LEN;
+
+	SM_ENTRY_MA(WPA_PTK, INITPMK, wpa_ptk);
+#ifdef CONFIG_IEEE80211R_AP
+	sm->xxkey_len = 0;
+#endif /* CONFIG_IEEE80211R_AP */
+	if (sm->pmksa) {
+		wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache");
+		os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len);
+		sm->pmk_len = sm->pmksa->pmk_len;
+#ifdef CONFIG_DPP
+	} else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No PMKSA cache entry for STA - reject connection");
+		sm->Disconnect = true;
+		sm->disconnect_reason = WLAN_REASON_INVALID_PMKID;
+		return;
+#endif /* CONFIG_DPP */
+	} 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))
+			pmk_len = PMK_LEN_SUITE_B_192;
+		else
+			pmk_len = PMK_LEN;
+		wpa_printf(MSG_DEBUG,
+			   "WPA: PMK from EAPOL state machine (MSK len=%zu PMK len=%u)",
+			   len, pmk_len);
+		if (len < pmk_len) {
+			wpa_printf(MSG_DEBUG,
+				   "WPA: MSK not long enough (%zu) to create PMK (%u)",
+				   len, pmk_len);
+			sm->Disconnect = true;
+			return;
+		}
+		os_memcpy(sm->PMK, msk, pmk_len);
+		sm->pmk_len = pmk_len;
+#ifdef CONFIG_IEEE80211R_AP
+		if (len >= 2 * PMK_LEN) {
+			if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) {
+				os_memcpy(sm->xxkey, msk, SHA384_MAC_LEN);
+				sm->xxkey_len = SHA384_MAC_LEN;
+			} else {
+				os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN);
+				sm->xxkey_len = PMK_LEN;
+			}
+		}
+#endif /* CONFIG_IEEE80211R_AP */
+	} else {
+		wpa_printf(MSG_DEBUG, "WPA: Could not get PMK, get_msk: %p",
+			   sm->wpa_auth->cb->get_msk);
+		sm->Disconnect = true;
+		return;
+	}
+	forced_memzero(msk, sizeof(msk));
+
+	sm->req_replay_counter_used = 0;
+	/* IEEE 802.11i does not set keyRun to false, but not doing this
+	 * will break reauthentication since EAPOL state machines may not be
+	 * get into AUTHENTICATING state that clears keyRun before WPA state
+	 * machine enters AUTHENTICATION2 state and goes immediately to INITPMK
+	 * state and takes PMK from the previously used AAA Key. This will
+	 * eventually fail in 4-Way Handshake because Supplicant uses PMK
+	 * derived from the new AAA Key. Setting keyRun = false here seems to
+	 * be good workaround for this issue. */
+	wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyRun, false);
+}
+
+
+SM_STATE(WPA_PTK, INITPSK)
+{
+	const u8 *psk;
+	size_t psk_len;
+
+	SM_ENTRY_MA(WPA_PTK, INITPSK, wpa_ptk);
+	psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL,
+			       &psk_len, NULL);
+	if (psk) {
+		os_memcpy(sm->PMK, psk, psk_len);
+		sm->pmk_len = psk_len;
+#ifdef CONFIG_IEEE80211R_AP
+		sm->xxkey_len = PMK_LEN;
+#ifdef CONFIG_SAE
+		if (sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+		    (psk_len == SHA512_MAC_LEN || psk_len == SHA384_MAC_LEN ||
+		     psk_len == SHA256_MAC_LEN))
+			sm->xxkey_len = psk_len;
+#endif /* CONFIG_SAE */
+		os_memcpy(sm->xxkey, psk, sm->xxkey_len);
+#endif /* CONFIG_IEEE80211R_AP */
+	}
+#ifdef CONFIG_SAE
+	if (wpa_auth_uses_sae(sm) && sm->pmksa) {
+		wpa_printf(MSG_DEBUG, "SAE: PMK from PMKSA cache (len=%zu)",
+			   sm->pmksa->pmk_len);
+		os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len);
+		sm->pmk_len = sm->pmksa->pmk_len;
+#ifdef CONFIG_IEEE80211R_AP
+		os_memcpy(sm->xxkey, sm->pmksa->pmk, sm->pmksa->pmk_len);
+		sm->xxkey_len = sm->pmksa->pmk_len;
+#endif /* CONFIG_IEEE80211R_AP */
+	}
+#endif /* CONFIG_SAE */
+	sm->req_replay_counter_used = 0;
+}
+
+
+SM_STATE(WPA_PTK, PTKSTART)
+{
+	u8 buf[2 * (2 + RSN_SELECTOR_LEN) + PMKID_LEN + ETH_ALEN];
+	u8 *pmkid = NULL;
+	size_t kde_len = 0;
+	u16 key_info;
+
+	SM_ENTRY_MA(WPA_PTK, PTKSTART, wpa_ptk);
+	sm->PTKRequest = false;
+	sm->TimeoutEvt = false;
+	sm->alt_snonce_valid = false;
+	sm->ptkstart_without_success++;
+
+	sm->TimeoutCtr++;
+	if (sm->TimeoutCtr > sm->wpa_auth->conf.wpa_pairwise_update_count) {
+		/* No point in sending the EAPOL-Key - we will disconnect
+		 * immediately following this. */
+		return;
+	}
+
+	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
+	 * the PMKID KDE in EAPOL-Key msg 1/4 since it could be used to initiate
+	 * offline search for the passphrase/PSK without having to be able to
+	 * capture a 4-way handshake from a STA that has access to the network.
+	 *
+	 * For IBSS cases, addition of PMKID KDE could be considered even with
+	 * WPA2-PSK cases that use multiple PSKs, but only if there is a single
+	 * possible PSK for this STA. However, this should not be done unless
+	 * there is support for using that information on the supplicant side.
+	 * The concern about exposing PMKID unnecessarily in infrastructure BSS
+	 * cases would also apply here, but at least in the IBSS case, this
+	 * would cover a potential real use case.
+	 */
+	if (sm->wpa == WPA_VERSION_WPA2 &&
+	    (wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) ||
+	     (sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE && sm->pmksa) ||
+	     wpa_key_mgmt_sae(sm->wpa_key_mgmt)) &&
+	    sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN) {
+		pmkid = buf;
+		kde_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN;
+		pmkid[0] = WLAN_EID_VENDOR_SPECIFIC;
+		pmkid[1] = RSN_SELECTOR_LEN + PMKID_LEN;
+		RSN_SELECTOR_PUT(&pmkid[2], RSN_KEY_DATA_PMKID);
+		if (sm->pmksa) {
+			wpa_hexdump(MSG_DEBUG,
+				    "RSN: Message 1/4 PMKID from PMKSA entry",
+				    sm->pmksa->pmkid, PMKID_LEN);
+			os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN],
+				  sm->pmksa->pmkid, PMKID_LEN);
+		} else if (wpa_key_mgmt_suite_b(sm->wpa_key_mgmt)) {
+			/* No KCK available to derive PMKID */
+			wpa_printf(MSG_DEBUG,
+				   "RSN: No KCK available to derive PMKID for message 1/4");
+			pmkid = NULL;
+#ifdef CONFIG_FILS
+		} else if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+			if (sm->pmkid_set) {
+				wpa_hexdump(MSG_DEBUG,
+					    "RSN: Message 1/4 PMKID from FILS/ERP",
+					    sm->pmkid, PMKID_LEN);
+				os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN],
+					  sm->pmkid, PMKID_LEN);
+			} else {
+				/* No PMKID available */
+				wpa_printf(MSG_DEBUG,
+					   "RSN: No FILS/ERP PMKID available for message 1/4");
+				pmkid = NULL;
+			}
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_IEEE80211R_AP
+		} else if (wpa_key_mgmt_ft(sm->wpa_key_mgmt) &&
+			   sm->ft_completed) {
+			wpa_printf(MSG_DEBUG,
+				   "FT: No PMKID in message 1/4 when using FT protocol");
+			pmkid = NULL;
+#endif /* CONFIG_IEEE80211R_AP */
+#ifdef CONFIG_SAE
+		} else if (wpa_key_mgmt_sae(sm->wpa_key_mgmt)) {
+			if (sm->pmkid_set) {
+				wpa_hexdump(MSG_DEBUG,
+					    "RSN: Message 1/4 PMKID from SAE",
+					    sm->pmkid, PMKID_LEN);
+				os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN],
+					  sm->pmkid, PMKID_LEN);
+			} else {
+				/* No PMKID available */
+				wpa_printf(MSG_DEBUG,
+					   "RSN: No SAE PMKID available for message 1/4");
+				pmkid = NULL;
+			}
+#endif /* CONFIG_SAE */
+		} else {
+			/*
+			 * Calculate PMKID since no PMKSA cache entry was
+			 * available with pre-calculated PMKID.
+			 */
+			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",
+				    &pmkid[2 + RSN_SELECTOR_LEN], PMKID_LEN);
+		}
+	}
+	if (!pmkid)
+		kde_len = 0;
+
+#ifdef CONFIG_IEEE80211BE
+	if (sm->mld_assoc_link_id >= 0) {
+		wpa_printf(MSG_DEBUG,
+			   "RSN: MLD: Add MAC Address KDE: kde_len=%zu",
+			   kde_len);
+		wpa_add_kde(buf + kde_len, RSN_KEY_DATA_MAC_ADDR,
+			    sm->own_mld_addr, ETH_ALEN, NULL, 0);
+		kde_len += 2 + RSN_SELECTOR_LEN + ETH_ALEN;
+	}
+#endif /* CONFIG_IEEE80211BE */
+
+	key_info = WPA_KEY_INFO_ACK | WPA_KEY_INFO_KEY_TYPE;
+	if (sm->pairwise_set && sm->wpa != WPA_VERSION_WPA)
+		key_info |= WPA_KEY_INFO_SECURE;
+	wpa_send_eapol(sm->wpa_auth, sm, key_info, NULL,
+		       sm->ANonce, kde_len ? buf : NULL, kde_len, 0, 0);
+}
+
+
+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,
+			  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;
+	int akmp;
+	int ret;
+
+	if (sm->wpa_auth->conf.force_kdk_derivation ||
+	    (sm->wpa_auth->conf.secure_ltf &&
+	     ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF)))
+		kdk_len = WPA_KDK_MAX_LEN;
+	else
+		kdk_len = 0;
+
+#ifdef CONFIG_IEEE80211R_AP
+	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+		if (sm->ft_completed) {
+			u8 ptk_name[WPA_PMK_NAME_LEN];
+
+			ret = wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len,
+						sm->SNonce, sm->ANonce,
+						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, pmk_r0, pmk_r1,
+						     pmk_r0_name, key_len,
+						     kdk_len);
+		}
+		if (ret) {
+			wpa_printf(MSG_ERROR, "FT: PTK derivation failed");
+			return ret;
+		}
+
+#ifdef CONFIG_PASN
+		if (sm->wpa_auth->conf.secure_ltf &&
+		    ieee802_11_rsnx_capab(sm->rsnxe,
+					  WLAN_RSNX_CAPAB_SECURE_LTF)) {
+			ret = wpa_ltf_keyseed(ptk, sm->wpa_key_mgmt,
+					      sm->pairwise);
+			if (ret) {
+				wpa_printf(MSG_ERROR,
+					   "FT: LTF keyseed derivation failed");
+			}
+		}
+#endif /* CONFIG_PASN */
+		return ret;
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+
+#ifdef CONFIG_DPP2
+	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && sm->dpp_z) {
+		z = wpabuf_head(sm->dpp_z);
+		z_len = wpabuf_len(sm->dpp_z);
+	}
+#endif /* CONFIG_DPP2 */
+
+	akmp = sm->wpa_key_mgmt;
+	if (force_sha256)
+		akmp |= WPA_KEY_MGMT_PSK_SHA256;
+	ret = wpa_pmk_to_ptk(pmk, pmk_len, "Pairwise key expansion",
+			     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");
+		return ret;
+	}
+
+#ifdef CONFIG_PASN
+	if (sm->wpa_auth->conf.secure_ltf &&
+	    ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF)) {
+		ret = wpa_ltf_keyseed(ptk, sm->wpa_key_mgmt, sm->pairwise);
+		if (ret) {
+			wpa_printf(MSG_DEBUG,
+				   "WPA: LTF keyseed derivation failed");
+		}
+	}
+#endif /* CONFIG_PASN */
+	return ret;
+}
+
+
+#ifdef CONFIG_FILS
+
+int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk,
+			 size_t pmk_len, const u8 *snonce, const u8 *anonce,
+			 const u8 *dhss, size_t dhss_len,
+			 struct wpabuf *g_sta, struct wpabuf *g_ap)
+{
+	u8 ick[FILS_ICK_MAX_LEN];
+	size_t ick_len;
+	int res;
+	u8 fils_ft[FILS_FT_MAX_LEN];
+	size_t fils_ft_len = 0, kdk_len;
+
+	if (sm->wpa_auth->conf.force_kdk_derivation ||
+	    (sm->wpa_auth->conf.secure_ltf &&
+	     ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF)))
+		kdk_len = WPA_KDK_MAX_LEN;
+	else
+		kdk_len = 0;
+
+	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,
+			      fils_ft, &fils_ft_len, kdk_len);
+	if (res < 0)
+		return res;
+
+#ifdef CONFIG_PASN
+	if (sm->wpa_auth->conf.secure_ltf &&
+	    ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF)) {
+		res = wpa_ltf_keyseed(&sm->PTK, sm->wpa_key_mgmt, sm->pairwise);
+		if (res) {
+			wpa_printf(MSG_ERROR,
+				   "FILS: LTF keyseed derivation failed");
+			return res;
+		}
+	}
+#endif /* CONFIG_PASN */
+
+	sm->PTK_valid = true;
+	sm->tk_already_set = false;
+
+#ifdef CONFIG_IEEE80211R_AP
+	if (fils_ft_len) {
+		struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+		struct wpa_auth_config *conf = &wpa_auth->conf;
+		u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN];
+
+		if (wpa_derive_pmk_r0(fils_ft, fils_ft_len,
+				      conf->ssid, conf->ssid_len,
+				      conf->mobility_domain,
+				      conf->r0_key_holder,
+				      conf->r0_key_holder_len,
+				      wpa_auth_get_spa(sm), pmk_r0, pmk_r0_name,
+				      sm->wpa_key_mgmt) < 0)
+			return -1;
+
+		wpa_ft_store_pmk_fils(sm, pmk_r0, pmk_r0_name);
+		forced_memzero(fils_ft, sizeof(fils_ft));
+
+		res = wpa_derive_pmk_r1_name(pmk_r0_name, conf->r1_key_holder,
+					     wpa_auth_get_spa(sm),
+					     sm->pmk_r1_name,
+					     fils_ft_len);
+		forced_memzero(pmk_r0, PMK_LEN_MAX);
+		if (res < 0)
+			return -1;
+		wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR1Name", sm->pmk_r1_name,
+			    WPA_PMK_NAME_LEN);
+		sm->pmk_r1_name_valid = 1;
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+
+	res = fils_key_auth_sk(ick, ick_len, snonce, anonce,
+			       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,
+			       g_ap ? wpabuf_len(g_ap) : 0,
+			       sm->wpa_key_mgmt, sm->fils_key_auth_sta,
+			       sm->fils_key_auth_ap,
+			       &sm->fils_key_auth_len);
+	forced_memzero(ick, sizeof(ick));
+
+	/* Store nonces for (Re)Association Request/Response frame processing */
+	os_memcpy(sm->SNonce, snonce, FILS_NONCE_LEN);
+	os_memcpy(sm->ANonce, anonce, FILS_NONCE_LEN);
+
+	return res;
+}
+
+
+static int wpa_aead_decrypt(struct wpa_state_machine *sm, struct wpa_ptk *ptk,
+			    u8 *buf, size_t buf_len, u16 *_key_data_len)
+{
+	struct ieee802_1x_hdr *hdr;
+	struct wpa_eapol_key *key;
+	u8 *pos;
+	u16 key_data_len;
+	u8 *tmp;
+	const u8 *aad[1];
+	size_t aad_len[1];
+
+	hdr = (struct ieee802_1x_hdr *) buf;
+	key = (struct wpa_eapol_key *) (hdr + 1);
+	pos = (u8 *) (key + 1);
+	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, wpa_auth_get_spa(sm), LOGGER_INFO,
+				"No room for AES-SIV data in the frame");
+		return -1;
+	}
+	pos += 2; /* Pointing at the Encrypted Key Data field */
+
+	tmp = os_malloc(key_data_len);
+	if (!tmp)
+		return -1;
+
+	/* AES-SIV AAD from EAPOL protocol version field (inclusive) to
+	 * to Key Data (exclusive). */
+	aad[0] = buf;
+	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, wpa_auth_get_spa(sm), LOGGER_INFO,
+				"Invalid AES-SIV data in the frame");
+		bin_clear_free(tmp, key_data_len);
+		return -1;
+	}
+
+	/* AEAD decryption and validation completed successfully */
+	key_data_len -= AES_BLOCK_SIZE;
+	wpa_hexdump_key(MSG_DEBUG, "WPA: Decrypted Key Data",
+			tmp, key_data_len);
+
+	/* Replace Key Data field with the decrypted version */
+	os_memcpy(pos, tmp, key_data_len);
+	pos -= 2; /* Key Data Length field */
+	WPA_PUT_BE16(pos, key_data_len);
+	bin_clear_free(tmp, key_data_len);
+	if (_key_data_len)
+		*_key_data_len = key_data_len;
+	return 0;
+}
+
+
+const u8 * wpa_fils_validate_fils_session(struct wpa_state_machine *sm,
+					  const u8 *ies, size_t ies_len,
+					  const u8 *fils_session)
+{
+	const u8 *ie, *end;
+	const u8 *session = NULL;
+
+	if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Not a FILS AKM - reject association");
+		return NULL;
+	}
+
+	/* Verify Session element */
+	ie = ies;
+	end = ((const u8 *) ie) + ies_len;
+	while (ie + 1 < end) {
+		if (ie + 2 + ie[1] > end)
+			break;
+		if (ie[0] == WLAN_EID_EXTENSION &&
+		    ie[1] >= 1 + FILS_SESSION_LEN &&
+		    ie[2] == WLAN_EID_EXT_FILS_SESSION) {
+			session = ie;
+			break;
+		}
+		ie += 2 + ie[1];
+	}
+
+	if (!session) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: %s: Could not find FILS Session element in Assoc Req - reject",
+			   __func__);
+		return NULL;
+	}
+
+	if (!fils_session) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: %s: Could not find FILS Session element in STA entry - reject",
+			   __func__);
+		return NULL;
+	}
+
+	if (os_memcmp(fils_session, session + 3, FILS_SESSION_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "FILS: Session mismatch");
+		wpa_hexdump(MSG_DEBUG, "FILS: Expected FILS Session",
+			    fils_session, FILS_SESSION_LEN);
+		wpa_hexdump(MSG_DEBUG, "FILS: Received FILS Session",
+			    session + 3, FILS_SESSION_LEN);
+		return NULL;
+	}
+	return session;
+}
+
+
+int wpa_fils_validate_key_confirm(struct wpa_state_machine *sm, const u8 *ies,
+				  size_t ies_len)
+{
+	struct ieee802_11_elems elems;
+
+	if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Failed to parse decrypted elements");
+		return -1;
+	}
+
+	if (!elems.fils_session) {
+		wpa_printf(MSG_DEBUG, "FILS: No FILS Session element");
+		return -1;
+	}
+
+	if (!elems.fils_key_confirm) {
+		wpa_printf(MSG_DEBUG, "FILS: No FILS Key Confirm element");
+		return -1;
+	}
+
+	if (elems.fils_key_confirm_len != sm->fils_key_auth_len) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Unexpected Key-Auth length %d (expected %zu)",
+			   elems.fils_key_confirm_len,
+			   sm->fils_key_auth_len);
+		return -1;
+	}
+
+	if (os_memcmp(elems.fils_key_confirm, sm->fils_key_auth_sta,
+		      sm->fils_key_auth_len) != 0) {
+		wpa_printf(MSG_DEBUG, "FILS: Key-Auth mismatch");
+		wpa_hexdump(MSG_DEBUG, "FILS: Received Key-Auth",
+			    elems.fils_key_confirm, elems.fils_key_confirm_len);
+		wpa_hexdump(MSG_DEBUG, "FILS: Expected Key-Auth",
+			    sm->fils_key_auth_sta, sm->fils_key_auth_len);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int fils_decrypt_assoc(struct wpa_state_machine *sm, const u8 *fils_session,
+		       const struct ieee80211_mgmt *mgmt, size_t frame_len,
+		       u8 *pos, size_t left)
+{
+	u16 fc, stype;
+	const u8 *end, *ie_start, *ie, *session, *crypt;
+	const u8 *aad[5];
+	size_t aad_len[5];
+
+	if (!sm || !sm->PTK_valid) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: No KEK to decrypt Assocication Request frame");
+		return -1;
+	}
+
+	if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Not a FILS AKM - reject association");
+		return -1;
+	}
+
+	end = ((const u8 *) mgmt) + frame_len;
+	fc = le_to_host16(mgmt->frame_control);
+	stype = WLAN_FC_GET_STYPE(fc);
+	if (stype == WLAN_FC_STYPE_REASSOC_REQ)
+		ie_start = mgmt->u.reassoc_req.variable;
+	else
+		ie_start = mgmt->u.assoc_req.variable;
+	ie = ie_start;
+
+	/*
+	 * Find FILS Session element which is the last unencrypted element in
+	 * the frame.
+	 */
+	session = wpa_fils_validate_fils_session(sm, ie, end - ie,
+						 fils_session);
+	if (!session) {
+		wpa_printf(MSG_DEBUG, "FILS: Session validation failed");
+		return -1;
+	}
+
+	crypt = session + 2 + session[1];
+
+	if (end - crypt < AES_BLOCK_SIZE) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Too short frame to include AES-SIV data");
+		return -1;
+	}
+
+	/* AES-SIV AAD vectors */
+
+	/* The STA's MAC address */
+	aad[0] = mgmt->sa;
+	aad_len[0] = ETH_ALEN;
+	/* The AP's BSSID */
+	aad[1] = mgmt->da;
+	aad_len[1] = ETH_ALEN;
+	/* The STA's nonce */
+	aad[2] = sm->SNonce;
+	aad_len[2] = FILS_NONCE_LEN;
+	/* The AP's nonce */
+	aad[3] = sm->ANonce;
+	aad_len[3] = FILS_NONCE_LEN;
+	/*
+	 * The (Re)Association Request frame from the Capability Information
+	 * field to the FILS Session element (both inclusive).
+	 */
+	aad[4] = (const u8 *) &mgmt->u.assoc_req.capab_info;
+	aad_len[4] = crypt - aad[4];
+
+	if (aes_siv_decrypt(sm->PTK.kek, sm->PTK.kek_len, crypt, end - crypt,
+			    5, aad, aad_len, pos + (crypt - ie_start)) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Invalid AES-SIV data in the frame");
+		return -1;
+	}
+	wpa_hexdump(MSG_DEBUG, "FILS: Decrypted Association Request elements",
+		    pos, left - AES_BLOCK_SIZE);
+
+	if (wpa_fils_validate_key_confirm(sm, pos, left - AES_BLOCK_SIZE) < 0) {
+		wpa_printf(MSG_DEBUG, "FILS: Key Confirm validation failed");
+		return -1;
+	}
+
+	return left - AES_BLOCK_SIZE;
+}
+
+
+int fils_encrypt_assoc(struct wpa_state_machine *sm, u8 *buf,
+		       size_t current_len, size_t max_len,
+		       const struct wpabuf *hlp)
+{
+	u8 *end = buf + max_len;
+	u8 *pos = buf + current_len;
+	struct ieee80211_mgmt *mgmt;
+	struct wpabuf *plain;
+	const u8 *aad[5];
+	size_t aad_len[5];
+
+	if (!sm || !sm->PTK_valid)
+		return -1;
+
+	wpa_hexdump(MSG_DEBUG,
+		    "FILS: Association Response frame before FILS processing",
+		    buf, current_len);
+
+	mgmt = (struct ieee80211_mgmt *) buf;
+
+	/* AES-SIV AAD vectors */
+
+	/* The AP's BSSID */
+	aad[0] = mgmt->sa;
+	aad_len[0] = ETH_ALEN;
+	/* The STA's MAC address */
+	aad[1] = mgmt->da;
+	aad_len[1] = ETH_ALEN;
+	/* The AP's nonce */
+	aad[2] = sm->ANonce;
+	aad_len[2] = FILS_NONCE_LEN;
+	/* The STA's nonce */
+	aad[3] = sm->SNonce;
+	aad_len[3] = FILS_NONCE_LEN;
+	/*
+	 * The (Re)Association Response frame from the Capability Information
+	 * field (the same offset in both Association and Reassociation
+	 * Response frames) to the FILS Session element (both inclusive).
+	 */
+	aad[4] = (const u8 *) &mgmt->u.assoc_resp.capab_info;
+	aad_len[4] = pos - aad[4];
+
+	/* The following elements will be encrypted with AES-SIV */
+	plain = fils_prepare_plainbuf(sm, hlp);
+	if (!plain) {
+		wpa_printf(MSG_DEBUG, "FILS: Plain buffer prep failed");
+		return -1;
+	}
+
+	if (pos + wpabuf_len(plain) + AES_BLOCK_SIZE > end) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Not enough room for FILS elements");
+		wpabuf_clear_free(plain);
+		return -1;
+	}
+
+	wpa_hexdump_buf_key(MSG_DEBUG, "FILS: Association Response plaintext",
+			    plain);
+
+	if (aes_siv_encrypt(sm->PTK.kek, sm->PTK.kek_len,
+			    wpabuf_head(plain), wpabuf_len(plain),
+			    5, aad, aad_len, pos) < 0) {
+		wpabuf_clear_free(plain);
+		return -1;
+	}
+
+	wpa_hexdump(MSG_DEBUG,
+		    "FILS: Encrypted Association Response elements",
+		    pos, AES_BLOCK_SIZE + wpabuf_len(plain));
+	current_len += wpabuf_len(plain) + AES_BLOCK_SIZE;
+	wpabuf_clear_free(plain);
+
+	sm->fils_completed = 1;
+
+	return current_len;
+}
+
+
+static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm,
+					     const struct wpabuf *hlp)
+{
+	struct wpabuf *plain;
+	u8 *len, *tmp, *tmp2;
+	u8 hdr[2];
+	u8 *gtk, stub_gtk[32];
+	size_t gtk_len;
+	struct wpa_group *gsm;
+	size_t plain_len;
+	struct wpa_auth_config *conf = &sm->wpa_auth->conf;
+
+	plain_len = 1000 + ieee80211w_kde_len(sm);
+	if (conf->transition_disable)
+		plain_len += 2 + RSN_SELECTOR_LEN + 1;
+	plain = wpabuf_alloc(plain_len);
+	if (!plain)
+		return NULL;
+
+	/* TODO: FILS Public Key */
+
+	/* FILS Key Confirmation */
+	wpabuf_put_u8(plain, WLAN_EID_EXTENSION); /* Element ID */
+	wpabuf_put_u8(plain, 1 + sm->fils_key_auth_len); /* Length */
+	/* Element ID Extension */
+	wpabuf_put_u8(plain, WLAN_EID_EXT_FILS_KEY_CONFIRM);
+	wpabuf_put_data(plain, sm->fils_key_auth_ap, sm->fils_key_auth_len);
+
+	/* FILS HLP Container */
+	if (hlp)
+		wpabuf_put_buf(plain, hlp);
+
+	/* TODO: FILS IP Address Assignment */
+
+	/* Key Delivery */
+	gsm = sm->group;
+	wpabuf_put_u8(plain, WLAN_EID_EXTENSION); /* Element ID */
+	len = wpabuf_put(plain, 1);
+	wpabuf_put_u8(plain, WLAN_EID_EXT_KEY_DELIVERY);
+	wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN,
+			    wpabuf_put(plain, WPA_KEY_RSC_LEN));
+	/* GTK KDE */
+	gtk = gsm->GTK[gsm->GN - 1];
+	gtk_len = gsm->GTK_len;
+	if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+		/*
+		 * Provide unique random GTK to each STA to prevent use
+		 * of GTK in the BSS.
+		 */
+		if (random_get_bytes(stub_gtk, gtk_len) < 0) {
+			wpabuf_clear_free(plain);
+			return NULL;
+		}
+		gtk = stub_gtk;
+	}
+	hdr[0] = gsm->GN & 0x03;
+	hdr[1] = 0;
+	tmp = wpabuf_put(plain, 0);
+	tmp2 = wpa_add_kde(tmp, RSN_KEY_DATA_GROUPKEY, hdr, 2,
+			   gtk, gtk_len);
+	wpabuf_put(plain, tmp2 - tmp);
+
+	/* IGTK KDE and BIGTK KDE */
+	tmp = wpabuf_put(plain, 0);
+	tmp2 = ieee80211w_kde_add(sm, tmp);
+	wpabuf_put(plain, tmp2 - tmp);
+
+	if (conf->transition_disable) {
+		tmp = wpabuf_put(plain, 0);
+		tmp2 = wpa_add_kde(tmp, WFA_KEY_DATA_TRANSITION_DISABLE,
+				   &conf->transition_disable, 1, NULL, 0);
+		wpabuf_put(plain, tmp2 - tmp);
+	}
+
+	*len = (u8 *) wpabuf_put(plain, 0) - len - 1;
+
+#ifdef CONFIG_OCV
+	if (wpa_auth_uses_ocv(sm)) {
+		struct wpa_channel_info ci;
+		u8 *pos;
+
+		if (wpa_channel_info(sm->wpa_auth, &ci) != 0) {
+			wpa_printf(MSG_WARNING,
+				   "FILS: Failed to get channel info for OCI element");
+			wpabuf_clear_free(plain);
+			return NULL;
+		}
+#ifdef CONFIG_TESTING_OPTIONS
+		if (conf->oci_freq_override_fils_assoc) {
+			wpa_printf(MSG_INFO,
+				   "TEST: Override OCI frequency %d -> %u MHz",
+				   ci.frequency,
+				   conf->oci_freq_override_fils_assoc);
+			ci.frequency = conf->oci_freq_override_fils_assoc;
+		}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+		pos = wpabuf_put(plain, OCV_OCI_EXTENDED_LEN);
+		if (ocv_insert_extended_oci(&ci, pos) < 0) {
+			wpabuf_clear_free(plain);
+			return NULL;
+		}
+	}
+#endif /* CONFIG_OCV */
+
+	return plain;
+}
+
+
+int fils_set_tk(struct wpa_state_machine *sm)
+{
+	enum wpa_alg alg;
+	int klen;
+
+	if (!sm || !sm->PTK_valid) {
+		wpa_printf(MSG_DEBUG, "FILS: No valid PTK available to set TK");
+		return -1;
+	}
+	if (sm->tk_already_set) {
+		wpa_printf(MSG_DEBUG, "FILS: TK already set to the driver");
+		return -1;
+	}
+
+	alg = wpa_cipher_to_alg(sm->pairwise);
+	klen = wpa_cipher_key_len(sm->pairwise);
+
+	wpa_printf(MSG_DEBUG, "FILS: Configure TK to the driver");
+	if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
+			     sm->PTK.tk, klen, KEY_FLAG_PAIRWISE_RX_TX)) {
+		wpa_printf(MSG_DEBUG, "FILS: Failed to set TK to the driver");
+		return -1;
+	}
+
+#ifdef CONFIG_PASN
+	if (sm->wpa_auth->conf.secure_ltf &&
+	    ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF) &&
+	    wpa_auth_set_ltf_keyseed(sm->wpa_auth, sm->addr,
+				     sm->PTK.ltf_keyseed,
+				     sm->PTK.ltf_keyseed_len)) {
+		wpa_printf(MSG_ERROR,
+			   "FILS: Failed to set LTF keyseed to driver");
+		return -1;
+	}
+#endif /* CONFIG_PASN */
+
+	sm->pairwise_set = true;
+	sm->tk_already_set = true;
+
+	wpa_auth_store_ptksa(sm->wpa_auth, sm->addr, sm->pairwise,
+			     dot11RSNAConfigPMKLifetime, &sm->PTK);
+
+	return 0;
+}
+
+
+u8 * hostapd_eid_assoc_fils_session(struct wpa_state_machine *sm, u8 *buf,
+				    const u8 *fils_session, struct wpabuf *hlp)
+{
+	struct wpabuf *plain;
+	u8 *pos = buf;
+
+	/* FILS Session */
+	*pos++ = WLAN_EID_EXTENSION; /* Element ID */
+	*pos++ = 1 + FILS_SESSION_LEN; /* Length */
+	*pos++ = WLAN_EID_EXT_FILS_SESSION; /* Element ID Extension */
+	os_memcpy(pos, fils_session, FILS_SESSION_LEN);
+	pos += FILS_SESSION_LEN;
+
+	plain = fils_prepare_plainbuf(sm, hlp);
+	if (!plain) {
+		wpa_printf(MSG_DEBUG, "FILS: Plain buffer prep failed");
+		return NULL;
+	}
+
+	os_memcpy(pos, wpabuf_head(plain), wpabuf_len(plain));
+	pos += wpabuf_len(plain);
+
+	wpa_printf(MSG_DEBUG, "%s: plain buf_len: %zu", __func__,
+		   wpabuf_len(plain));
+	wpabuf_clear_free(plain);
+	sm->fils_completed = 1;
+	return pos;
+}
+
+#endif /* CONFIG_FILS */
+
+
+#ifdef CONFIG_OCV
+int get_sta_tx_parameters(struct wpa_state_machine *sm, int ap_max_chanwidth,
+			  int ap_seg1_idx, int *bandwidth, int *seg1_idx)
+{
+	struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+
+	if (!wpa_auth->cb->get_sta_tx_params)
+		return -1;
+	return wpa_auth->cb->get_sta_tx_params(wpa_auth->cb_ctx, sm->addr,
+					       ap_max_chanwidth, ap_seg1_idx,
+					       bandwidth, seg1_idx);
+}
+#endif /* CONFIG_OCV */
+
+
+static int wpa_auth_validate_ml_kdes_m2(struct wpa_state_machine *sm,
+					struct wpa_eapol_ie_parse *kde)
+{
+#ifdef CONFIG_IEEE80211BE
+	int i;
+	unsigned int n_links = 0;
+
+	if (sm->mld_assoc_link_id < 0)
+		return 0;
+
+	/* MLD MAC address must be the same */
+	if (!kde->mac_addr ||
+	    os_memcmp(kde->mac_addr, sm->peer_mld_addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "RSN: MLD: Invalid MLD address");
+		return -1;
+	}
+
+	/* Find matching link ID and the MAC address for each link */
+	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+		if (!(kde->valid_mlo_links & BIT(i)))
+			continue;
+
+		/*
+		 * Each entry should contain the link information and the MAC
+		 * address.
+		 */
+		if (kde->mlo_link_len[i] != 1 + ETH_ALEN) {
+			wpa_printf(MSG_DEBUG,
+				   "RSN: MLD: Invalid MLO Link (ID %u) KDE len=%zu",
+				   i, kde->mlo_link_len[i]);
+			return -1;
+		}
+
+		if (!sm->mld_links[i].valid || i == sm->mld_assoc_link_id) {
+			wpa_printf(MSG_DEBUG,
+				   "RSN: MLD: Invalid link ID=%u", i);
+			return -1;
+		}
+
+		if (os_memcmp(sm->mld_links[i].peer_addr, kde->mlo_link[i] + 1,
+			      ETH_ALEN) != 0) {
+			wpa_printf(MSG_DEBUG,
+				   "RSN: MLD: invalid MAC address=" MACSTR
+				   " expected " MACSTR " (link ID %u)",
+				   MAC2STR(kde->mlo_link[i] + 1),
+				   MAC2STR(sm->mld_links[i].peer_addr), i);
+			return -1;
+		}
+
+		n_links++;
+	}
+
+	/* Must have the same number of MLO links (excluding the local one) */
+	if (n_links != sm->n_mld_affiliated_links) {
+		wpa_printf(MSG_DEBUG,
+			   "RSN: MLD: Expecting %u MLD links in msg 2, but got %u",
+			   sm->n_mld_affiliated_links, n_links);
+		return -1;
+	}
+#endif /* CONFIG_IEEE80211BE */
+
+	return 0;
+}
+
+
+SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
+{
+	struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+	struct wpa_ptk PTK;
+	int ok = 0, psk_found = 0;
+	const u8 *pmk = NULL;
+	size_t pmk_len;
+	int ft;
+	const u8 *eapol_key_ie, *key_data, *mic;
+	u16 key_data_length;
+	size_t mic_len, eapol_key_ie_len;
+	struct ieee802_1x_hdr *hdr;
+	struct wpa_eapol_key *key;
+	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;
+	sm->update_snonce = false;
+	os_memset(&PTK, 0, sizeof(PTK));
+
+	mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
+
+	/* WPA with IEEE 802.1X: use the derived PMK from EAP
+	 * WPA-PSK: iterate through possible PSKs and select the one matching
+	 * the packet */
+	for (;;) {
+		if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) &&
+		    !wpa_key_mgmt_sae(sm->wpa_key_mgmt)) {
+			pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr,
+					       sm->p2p_dev_addr, pmk, &pmk_len,
+					       &vlan_id);
+			if (!pmk)
+				break;
+			psk_found = 1;
+#ifdef CONFIG_IEEE80211R_AP
+			if (wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) {
+				os_memcpy(sm->xxkey, pmk, pmk_len);
+				sm->xxkey_len = pmk_len;
+			}
+#endif /* CONFIG_IEEE80211R_AP */
+		} else {
+			pmk = sm->PMK;
+			pmk_len = sm->pmk_len;
+		}
+
+		if ((!pmk || !pmk_len) && sm->pmksa) {
+			wpa_printf(MSG_DEBUG, "WPA: Use PMK from PMKSA cache");
+			pmk = sm->pmksa->pmk;
+			pmk_len = sm->pmksa->pmk_len;
+		}
+
+		if (wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK,
+				   owe_ptk_workaround == 2, pmk_r0, pmk_r1,
+				   pmk_r0_name, &key_len) < 0)
+			break;
+
+		if (mic_len &&
+		    wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK,
+				       sm->last_rx_eapol_key,
+				       sm->last_rx_eapol_key_len) == 0) {
+			if (sm->PMK != pmk) {
+				os_memcpy(sm->PMK, pmk, pmk_len);
+				sm->pmk_len = pmk_len;
+			}
+			ok = 1;
+			break;
+		}
+
+#ifdef CONFIG_FILS
+		if (!mic_len &&
+		    wpa_aead_decrypt(sm, &PTK, sm->last_rx_eapol_key,
+				     sm->last_rx_eapol_key_len, NULL) == 0) {
+			ok = 1;
+			break;
+		}
+#endif /* CONFIG_FILS */
+
+#ifdef CONFIG_OWE
+		if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE && pmk_len > 32 &&
+		    owe_ptk_workaround == 1) {
+			wpa_printf(MSG_DEBUG,
+				   "OWE: Try PTK derivation workaround with SHA256");
+			owe_ptk_workaround = 2;
+			continue;
+		}
+#endif /* CONFIG_OWE */
+
+		if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
+		    wpa_key_mgmt_sae(sm->wpa_key_mgmt))
+			break;
+	}
+
+	if (!ok && wpa_key_mgmt_wpa_psk_no_sae(sm->wpa_key_mgmt) &&
+	    wpa_auth->conf.radius_psk && wpa_auth->cb->request_radius_psk &&
+	    !sm->waiting_radius_psk) {
+		wpa_printf(MSG_DEBUG, "No PSK available - ask RADIUS server");
+		wpa_auth->cb->request_radius_psk(wpa_auth->cb_ctx, sm->addr,
+						 sm->wpa_key_mgmt,
+						 sm->ANonce,
+						 sm->last_rx_eapol_key,
+						 sm->last_rx_eapol_key_len);
+		sm->waiting_radius_psk = 1;
+		goto out;
+	}
+
+	if (!ok) {
+		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);
+		goto out;
+	}
+
+	/*
+	 * Note: last_rx_eapol_key length fields have already been validated in
+	 * wpa_receive().
+	 */
+	hdr = (struct ieee802_1x_hdr *) sm->last_rx_eapol_key;
+	key = (struct wpa_eapol_key *) (hdr + 1);
+	mic = (u8 *) (key + 1);
+	key_data = mic + mic_len + 2;
+	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)
+		goto out;
+
+	if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) {
+		wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
+				 "received EAPOL-Key msg 2/4 with invalid Key Data contents");
+		goto out;
+	}
+	if (kde.rsn_ie) {
+		eapol_key_ie = kde.rsn_ie;
+		eapol_key_ie_len = kde.rsn_ie_len;
+	} else if (kde.osen) {
+		eapol_key_ie = kde.osen;
+		eapol_key_ie_len = kde.osen_len;
+	} else {
+		eapol_key_ie = kde.wpa_ie;
+		eapol_key_ie_len = kde.wpa_ie_len;
+	}
+	ft = sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt);
+	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, 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",
+				    sm->wpa_ie, sm->wpa_ie_len);
+		}
+		wpa_hexdump(MSG_DEBUG, "WPA IE in msg 2/4",
+			    eapol_key_ie, eapol_key_ie_len);
+		/* MLME-DEAUTHENTICATE.request */
+		wpa_sta_disconnect(wpa_auth, sm->addr,
+				   WLAN_REASON_PREV_AUTH_NOT_VALID);
+		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, 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);
+		wpa_hexdump(MSG_DEBUG, "RSNXE in EAPOL-Key msg 2/4",
+			    kde.rsnxe, kde.rsnxe_len);
+		/* MLME-DEAUTHENTICATE.request */
+		wpa_sta_disconnect(wpa_auth, sm->addr,
+				   WLAN_REASON_PREV_AUTH_NOT_VALID);
+		goto out;
+	}
+#ifdef CONFIG_OCV
+	if (wpa_auth_uses_ocv(sm)) {
+		struct wpa_channel_info ci;
+		int tx_chanwidth;
+		int tx_seg1_idx;
+		enum oci_verify_result res;
+
+		if (wpa_channel_info(wpa_auth, &ci) != 0) {
+			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");
+			goto out;
+		}
+
+		if (get_sta_tx_parameters(sm,
+					  channel_width_to_int(ci.chanwidth),
+					  ci.seg1_idx, &tx_chanwidth,
+					  &tx_seg1_idx) < 0)
+			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, 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, 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(wpa_auth_get_spa(sm)),
+					ocv_errorstr);
+			goto out;
+		}
+	}
+#endif /* CONFIG_OCV */
+#ifdef CONFIG_IEEE80211R_AP
+	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);
+		goto out;
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+#ifdef CONFIG_P2P
+	if (kde.ip_addr_req && kde.ip_addr_req[0] &&
+	    wpa_auth->ip_pool && WPA_GET_BE32(sm->ip_addr) == 0) {
+		int idx;
+		wpa_printf(MSG_DEBUG,
+			   "P2P: IP address requested in EAPOL-Key exchange");
+		idx = bitfield_get_first_zero(wpa_auth->ip_pool);
+		if (idx >= 0) {
+			u32 start = WPA_GET_BE32(wpa_auth->conf.ip_addr_start);
+			bitfield_set(wpa_auth->ip_pool, idx);
+			sm->ip_addr_bit = idx;
+			WPA_PUT_BE32(sm->ip_addr, start + idx);
+			wpa_printf(MSG_DEBUG,
+				   "P2P: Assigned IP address %u.%u.%u.%u to "
+				   MACSTR " (bit %u)",
+				   sm->ip_addr[0], sm->ip_addr[1],
+				   sm->ip_addr[2], sm->ip_addr[3],
+				   MAC2STR(wpa_auth_get_spa(sm)),
+				   sm->ip_addr_bit);
+		}
+	}
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_DPP2
+	if (DPP_VERSION > 1 && kde.dpp_kde) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: peer Protocol Version %u Flags 0x%x",
+			   kde.dpp_kde[0], kde.dpp_kde[1]);
+		if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP &&
+		    wpa_auth->conf.dpp_pfs != 2 &&
+		    (kde.dpp_kde[1] & DPP_KDE_PFS_ALLOWED) &&
+		    !sm->dpp_z) {
+			wpa_printf(MSG_INFO,
+				   "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);
+			goto out;
+		}
+	}
+#endif /* CONFIG_DPP2 */
+
+	if (wpa_auth_validate_ml_kdes_m2(sm, &kde) < 0) {
+		wpa_sta_disconnect(wpa_auth, sm->addr,
+				   WLAN_REASON_PREV_AUTH_NOT_VALID);
+		return;
+	}
+
+#ifdef CONFIG_IEEE80211R_AP
+	if (sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+		/*
+		 * Verify that PMKR1Name from EAPOL-Key message 2/4 matches
+		 * with the value we derived.
+		 */
+		if (os_memcmp_const(sm->sup_pmk_r1_name, sm->pmk_r1_name,
+				    WPA_PMK_NAME_LEN) != 0) {
+			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);
+			goto out;
+		}
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+
+	if (vlan_id && wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) &&
+	    wpa_auth_update_vlan(wpa_auth, sm->addr, vlan_id) < 0) {
+		wpa_sta_disconnect(wpa_auth, sm->addr,
+				   WLAN_REASON_PREV_AUTH_NOT_VALID);
+		goto out;
+	}
+
+	sm->pending_1_of_4_timeout = 0;
+	eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm);
+
+	if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) && sm->PMK != pmk) {
+		/* PSK may have changed from the previous choice, so update
+		 * state machine data based on whatever PSK was selected here.
+		 */
+		os_memcpy(sm->PMK, pmk, PMK_LEN);
+		sm->pmk_len = PMK_LEN;
+	}
+
+	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));
+}
+
+
+SM_STATE(WPA_PTK, PTKCALCNEGOTIATING2)
+{
+	SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING2, wpa_ptk);
+	sm->TimeoutCtr = 0;
+}
+
+
+static int ieee80211w_kde_len(struct wpa_state_machine *sm)
+{
+	size_t len = 0;
+
+	if (sm->mgmt_frame_prot) {
+		len += 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN;
+		len += wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
+	}
+	if (sm->mgmt_frame_prot && sm->wpa_auth->conf.beacon_prot) {
+		len += 2 + RSN_SELECTOR_LEN + WPA_BIGTK_KDE_PREFIX_LEN;
+		len += wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
+	}
+
+	return len;
+}
+
+
+static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos)
+{
+	struct wpa_igtk_kde igtk;
+	struct wpa_bigtk_kde bigtk;
+	struct wpa_group *gsm = sm->group;
+	u8 rsc[WPA_KEY_RSC_LEN];
+	struct wpa_auth_config *conf = &sm->wpa_auth->conf;
+	size_t len = wpa_cipher_key_len(conf->group_mgmt_cipher);
+
+	if (!sm->mgmt_frame_prot)
+		return pos;
+
+#ifdef CONFIG_IEEE80211BE
+	if (sm->mld_assoc_link_id >= 0)
+		return pos; /* Use per-link MLO KDEs instead */
+#endif /* CONFIG_IEEE80211BE */
+
+	igtk.keyid[0] = gsm->GN_igtk;
+	igtk.keyid[1] = 0;
+	if (gsm->wpa_group_state != WPA_GROUP_SETKEYSDONE ||
+	    wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, rsc) < 0)
+		os_memset(igtk.pn, 0, sizeof(igtk.pn));
+	else
+		os_memcpy(igtk.pn, rsc, sizeof(igtk.pn));
+	os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], len);
+	if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+		/*
+		 * Provide unique random IGTK to each STA to prevent use of
+		 * IGTK in the BSS.
+		 */
+		if (random_get_bytes(igtk.igtk, len) < 0)
+			return pos;
+	}
+	pos = wpa_add_kde(pos, RSN_KEY_DATA_IGTK,
+			  (const u8 *) &igtk, WPA_IGTK_KDE_PREFIX_LEN + len,
+			  NULL, 0);
+	forced_memzero(&igtk, sizeof(igtk));
+
+	if (!conf->beacon_prot)
+		return pos;
+
+	bigtk.keyid[0] = gsm->GN_bigtk;
+	bigtk.keyid[1] = 0;
+	if (gsm->wpa_group_state != WPA_GROUP_SETKEYSDONE ||
+	    wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_bigtk, rsc) < 0)
+		os_memset(bigtk.pn, 0, sizeof(bigtk.pn));
+	else
+		os_memcpy(bigtk.pn, rsc, sizeof(bigtk.pn));
+	os_memcpy(bigtk.bigtk, gsm->BIGTK[gsm->GN_bigtk - 6], len);
+	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+		/*
+		 * Provide unique random BIGTK to each OSEN STA to prevent use
+		 * of BIGTK in the BSS.
+		 */
+		if (random_get_bytes(bigtk.bigtk, len) < 0)
+			return pos;
+	}
+	pos = wpa_add_kde(pos, RSN_KEY_DATA_BIGTK,
+			  (const u8 *) &bigtk, WPA_BIGTK_KDE_PREFIX_LEN + len,
+			  NULL, 0);
+	forced_memzero(&bigtk, sizeof(bigtk));
+
+	return pos;
+}
+
+
+static int ocv_oci_len(struct wpa_state_machine *sm)
+{
+#ifdef CONFIG_OCV
+	if (wpa_auth_uses_ocv(sm))
+		return OCV_OCI_KDE_LEN;
+#endif /* CONFIG_OCV */
+	return 0;
+}
+
+
+static int ocv_oci_add(struct wpa_state_machine *sm, u8 **argpos,
+		       unsigned int freq)
+{
+#ifdef CONFIG_OCV
+	struct wpa_channel_info ci;
+
+	if (!wpa_auth_uses_ocv(sm))
+		return 0;
+
+	if (wpa_channel_info(sm->wpa_auth, &ci) != 0) {
+		wpa_printf(MSG_WARNING,
+			   "Failed to get channel info for OCI element");
+		return -1;
+	}
+#ifdef CONFIG_TESTING_OPTIONS
+	if (freq) {
+		wpa_printf(MSG_INFO,
+			   "TEST: Override OCI KDE frequency %d -> %u MHz",
+			   ci.frequency, freq);
+		ci.frequency = freq;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	return ocv_insert_oci_kde(&ci, argpos);
+#else /* CONFIG_OCV */
+	return 0;
+#endif /* CONFIG_OCV */
+}
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+static u8 * replace_ie(const char *name, const u8 *old_buf, size_t *len, u8 eid,
+		       const u8 *ie, size_t ie_len)
+{
+	const u8 *elem;
+	u8 *buf;
+
+	wpa_printf(MSG_DEBUG, "TESTING: %s EAPOL override", name);
+	wpa_hexdump(MSG_DEBUG, "TESTING: wpa_ie before override",
+		    old_buf, *len);
+	buf = os_malloc(*len + ie_len);
+	if (!buf)
+		return NULL;
+	os_memcpy(buf, old_buf, *len);
+	elem = get_ie(buf, *len, eid);
+	if (elem) {
+		u8 elem_len = 2 + elem[1];
+
+		os_memmove((void *) elem, elem + elem_len,
+			   *len - (elem - buf) - elem_len);
+		*len -= elem_len;
+	}
+	os_memcpy(buf + *len, ie, ie_len);
+	*len += ie_len;
+	wpa_hexdump(MSG_DEBUG, "TESTING: wpa_ie after EAPOL override",
+		    buf, *len);
+
+	return buf;
+}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+#ifdef CONFIG_IEEE80211BE
+
+void wpa_auth_ml_get_rsn_info(struct wpa_authenticator *a,
+			      struct wpa_auth_ml_link_rsn_info *info)
+{
+	info->rsn_ies = a->wpa_ie;
+	info->rsn_ies_len = a->wpa_ie_len;
+
+	wpa_printf(MSG_DEBUG, "RSN: MLD: link_id=%u, rsn_ies_len=%zu",
+		   info->link_id, info->rsn_ies_len);
+}
+
+
+static void wpa_auth_get_ml_rsn_info(struct wpa_authenticator *wpa_auth,
+				     struct wpa_auth_ml_rsn_info *info)
+{
+	if (!wpa_auth->cb->get_ml_rsn_info)
+		return;
+
+	wpa_auth->cb->get_ml_rsn_info(wpa_auth->cb_ctx, info);
+}
+
+
+void wpa_auth_ml_get_key_info(struct wpa_authenticator *a,
+			      struct wpa_auth_ml_link_key_info *info,
+			      bool mgmt_frame_prot, bool beacon_prot)
+{
+	struct wpa_group *gsm = a->group;
+	u8 rsc[WPA_KEY_RSC_LEN];
+
+	wpa_printf(MSG_DEBUG,
+		   "MLD: Get group key info: link_id=%u, IGTK=%u, BIGTK=%u",
+		   info->link_id, mgmt_frame_prot, beacon_prot);
+
+	info->gtkidx = gsm->GN & 0x03;
+	info->gtk = gsm->GTK[gsm->GN - 1];
+	info->gtk_len = gsm->GTK_len;
+
+	if (wpa_auth_get_seqnum(a, NULL, gsm->GN, rsc) < 0)
+		os_memset(info->pn, 0, sizeof(info->pn));
+	else
+		os_memcpy(info->pn, rsc, sizeof(info->pn));
+
+	if (!mgmt_frame_prot)
+		return;
+
+	info->igtkidx = gsm->GN_igtk;
+	info->igtk = gsm->IGTK[gsm->GN_igtk - 4];
+	info->igtk_len = wpa_cipher_key_len(a->conf.group_mgmt_cipher);
+
+	if (wpa_auth_get_seqnum(a, NULL, gsm->GN_igtk, rsc) < 0)
+		os_memset(info->ipn, 0, sizeof(info->ipn));
+	else
+		os_memcpy(info->ipn, rsc, sizeof(info->ipn));
+
+	if (!beacon_prot)
+		return;
+
+	info->bigtkidx = gsm->GN_bigtk;
+	info->bigtk = gsm->BIGTK[gsm->GN_bigtk - 6];
+
+	if (wpa_auth_get_seqnum(a, NULL, gsm->GN_bigtk, rsc) < 0)
+		os_memset(info->bipn, 0, sizeof(info->bipn));
+	else
+		os_memcpy(info->bipn, rsc, sizeof(info->bipn));
+}
+
+
+static void wpa_auth_get_ml_key_info(struct wpa_authenticator *wpa_auth,
+				     struct wpa_auth_ml_key_info *info)
+{
+	if (!wpa_auth->cb->get_ml_key_info)
+		return;
+
+	wpa_auth->cb->get_ml_key_info(wpa_auth->cb_ctx, info);
+}
+
+
+static size_t wpa_auth_ml_group_kdes_len(struct wpa_state_machine *sm)
+{
+	struct wpa_group *gsm = sm->group;
+	size_t gtk_len = gsm->GTK_len;
+	size_t igtk_len;
+	size_t kde_len;
+	unsigned int n_links;
+
+	if (sm->mld_assoc_link_id < 0)
+		return 0;
+
+	n_links = sm->n_mld_affiliated_links + 1;
+
+	/* MLO GTK KDE for each link */
+	kde_len = n_links * (2 + RSN_SELECTOR_LEN + 1 + 6 + gtk_len);
+
+	if (!sm->mgmt_frame_prot)
+		return kde_len;
+
+	/* MLO IGTK KDE for each link */
+	igtk_len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
+	kde_len += n_links * (2 + RSN_SELECTOR_LEN + 2 + 6 + 1 + igtk_len);
+
+	if (!sm->wpa_auth->conf.beacon_prot)
+		return kde_len;
+
+	/* MLO BIGTK KDE for each link */
+	kde_len += n_links * (2 + RSN_SELECTOR_LEN + 2 + 6 + 1 + igtk_len);
+
+	return kde_len;
+}
+
+
+static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos)
+{
+	struct wpa_auth_ml_key_info ml_key_info;
+	unsigned int i, link_id;
+
+	/* First fetch the key information from all the authenticators */
+	os_memset(&ml_key_info, 0, sizeof(ml_key_info));
+	ml_key_info.n_mld_links = sm->n_mld_affiliated_links + 1;
+
+	/*
+	 * Assume that management frame protection and beacon protection are the
+	 * same on all links.
+	 */
+	ml_key_info.mgmt_frame_prot = sm->mgmt_frame_prot;
+	ml_key_info.beacon_prot = sm->wpa_auth->conf.beacon_prot;
+
+	for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+		if (!sm->mld_links[link_id].valid)
+			continue;
+
+		ml_key_info.links[i++].link_id = link_id;
+	}
+
+	wpa_auth_get_ml_key_info(sm->wpa_auth, &ml_key_info);
+
+	/* Add MLO GTK KDEs */
+	for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+		if (!sm->mld_links[link_id].valid)
+			continue;
+
+		wpa_printf(MSG_DEBUG, "RSN: MLO GTK: link=%u", link_id);
+		wpa_hexdump_key(MSG_DEBUG, "RSN: MLO GTK",
+				ml_key_info.links[i].gtk,
+				ml_key_info.links[i].gtk_len);
+
+		*pos++ = WLAN_EID_VENDOR_SPECIFIC;
+		*pos++ = RSN_SELECTOR_LEN + 1 + 6 +
+			ml_key_info.links[i].gtk_len;
+
+		RSN_SELECTOR_PUT(pos, RSN_KEY_DATA_MLO_GTK);
+		pos += RSN_SELECTOR_LEN;
+
+		*pos++ = (ml_key_info.links[i].gtkidx & 0x3) | (link_id << 4);
+
+		os_memcpy(pos, ml_key_info.links[i].pn, 6);
+		pos += 6;
+
+		os_memcpy(pos, ml_key_info.links[i].gtk,
+			  ml_key_info.links[i].gtk_len);
+		pos += ml_key_info.links[i].gtk_len;
+
+		i++;
+	}
+
+	if (!sm->mgmt_frame_prot)
+		return pos;
+
+	/* Add MLO IGTK KDEs */
+	for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+		if (!sm->mld_links[link_id].valid)
+			continue;
+
+		wpa_printf(MSG_DEBUG, "RSN: MLO IGTK: link=%u", link_id);
+		wpa_hexdump_key(MSG_DEBUG, "RSN: MLO IGTK",
+				ml_key_info.links[i].igtk,
+				ml_key_info.links[i].igtk_len);
+
+		*pos++ = WLAN_EID_VENDOR_SPECIFIC;
+		*pos++ = RSN_SELECTOR_LEN + 2 + 1 +
+			sizeof(ml_key_info.links[i].ipn) +
+			ml_key_info.links[i].igtk_len;
+
+		RSN_SELECTOR_PUT(pos, RSN_KEY_DATA_MLO_IGTK);
+		pos += RSN_SELECTOR_LEN;
+
+		/* Add the Key ID */
+		*pos++ = ml_key_info.links[i].igtkidx;
+		*pos++ = 0;
+
+		/* Add the IPN */
+		os_memcpy(pos, ml_key_info.links[i].ipn,
+			  sizeof(ml_key_info.links[i].ipn));
+		pos += sizeof(ml_key_info.links[i].ipn);
+
+		*pos++ = ml_key_info.links[i].link_id << 4;
+
+		os_memcpy(pos, ml_key_info.links[i].igtk,
+			  ml_key_info.links[i].igtk_len);
+		pos += ml_key_info.links[i].igtk_len;
+
+		i++;
+	}
+
+	if (!sm->wpa_auth->conf.beacon_prot)
+		return pos;
+
+	/* Add MLO BIGTK KDEs */
+	for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+		if (!sm->mld_links[link_id].valid)
+			continue;
+
+		wpa_printf(MSG_DEBUG, "RSN: MLO BIGTK: link=%u", link_id);
+		wpa_hexdump_key(MSG_DEBUG, "RSN: MLO BIGTK",
+				ml_key_info.links[i].bigtk,
+				ml_key_info.links[i].igtk_len);
+
+		*pos++ = WLAN_EID_VENDOR_SPECIFIC;
+		*pos++ = RSN_SELECTOR_LEN + 2 + 1 +
+			sizeof(ml_key_info.links[i].bipn) +
+			ml_key_info.links[i].igtk_len;
+
+		RSN_SELECTOR_PUT(pos, RSN_KEY_DATA_MLO_BIGTK);
+		pos += RSN_SELECTOR_LEN;
+
+		/* Add the Key ID */
+		*pos++ = ml_key_info.links[i].bigtkidx;
+		*pos++ = 0;
+
+		/* Add the BIPN */
+		os_memcpy(pos, ml_key_info.links[i].bipn,
+			  sizeof(ml_key_info.links[i].bipn));
+		pos += sizeof(ml_key_info.links[i].bipn);
+
+		*pos++ = ml_key_info.links[i].link_id << 4;
+
+		os_memcpy(pos, ml_key_info.links[i].bigtk,
+			  ml_key_info.links[i].igtk_len);
+		pos += ml_key_info.links[i].igtk_len;
+
+		i++;
+	}
+
+	return pos;
+}
+
+#endif /* CONFIG_IEEE80211BE */
+
+
+static size_t wpa_auth_ml_kdes_len(struct wpa_state_machine *sm)
+{
+	size_t kde_len = 0;
+
+#ifdef CONFIG_IEEE80211BE
+	unsigned int link_id;
+
+	if (sm->mld_assoc_link_id < 0)
+		return 0;
+
+	/* For the MAC Address KDE */
+	kde_len = 2 + RSN_SELECTOR_LEN + ETH_ALEN;
+
+	/* MLO Link KDE for each link */
+	for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+		if (!sm->mld_links[link_id].valid)
+			continue;
+
+		kde_len += 2 + RSN_SELECTOR_LEN + 1 + ETH_ALEN +
+			sm->mld_links[link_id].rsne_len +
+			sm->mld_links[link_id].rsnxe_len;
+	}
+
+	kde_len += wpa_auth_ml_group_kdes_len(sm);
+#endif /* CONFIG_IEEE80211BE */
+
+	return kde_len;
+}
+
+
+static u8 * wpa_auth_ml_kdes(struct wpa_state_machine *sm, u8 *pos)
+{
+#ifdef CONFIG_IEEE80211BE
+	u8 link_id;
+
+	if (sm->mld_assoc_link_id < 0)
+		return pos;
+
+	wpa_printf(MSG_DEBUG, "RSN: MLD: Adding MAC Address KDE");
+	pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR,
+			  sm->own_mld_addr, ETH_ALEN, NULL, 0);
+
+	for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+		if (!sm->mld_links[link_id].valid)
+			continue;
+
+		wpa_printf(MSG_DEBUG,
+			   "RSN: MLO Link: link=%u, len=%zu", link_id,
+			   RSN_SELECTOR_LEN + 1 + ETH_ALEN +
+			   sm->mld_links[link_id].rsne_len +
+			   sm->mld_links[link_id].rsnxe_len);
+
+		*pos++ = WLAN_EID_VENDOR_SPECIFIC;
+		*pos++ = RSN_SELECTOR_LEN + 1 + ETH_ALEN +
+			sm->mld_links[link_id].rsne_len +
+			sm->mld_links[link_id].rsnxe_len;
+
+		RSN_SELECTOR_PUT(pos, RSN_KEY_DATA_MLO_LINK);
+		pos += RSN_SELECTOR_LEN;
+
+		/* Add the Link Information */
+		*pos = link_id;
+		if (sm->mld_links[link_id].rsne_len)
+			*pos |= RSN_MLO_LINK_KDE_LI_RSNE_INFO;
+		if (sm->mld_links[link_id].rsnxe_len)
+			*pos |= RSN_MLO_LINK_KDE_LI_RSNXE_INFO;
+
+		pos++;
+		os_memcpy(pos, sm->mld_links[link_id].own_addr, ETH_ALEN);
+		pos += ETH_ALEN;
+
+		if (sm->mld_links[link_id].rsne_len) {
+			os_memcpy(pos, sm->mld_links[link_id].rsne,
+				  sm->mld_links[link_id].rsne_len);
+			pos += sm->mld_links[link_id].rsne_len;
+		}
+
+		if (sm->mld_links[link_id].rsnxe_len) {
+			os_memcpy(pos, sm->mld_links[link_id].rsnxe,
+				  sm->mld_links[link_id].rsnxe_len);
+			pos += sm->mld_links[link_id].rsnxe_len;
+		}
+	}
+
+	pos = wpa_auth_ml_group_kdes(sm, pos);
+#endif /* CONFIG_IEEE80211BE */
+
+	return pos;
+}
+
+
+SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
+{
+	u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde = NULL, *pos, stub_gtk[32];
+	size_t gtk_len, kde_len = 0, wpa_ie_len;
+	struct wpa_group *gsm = sm->group;
+	u8 *wpa_ie;
+	int secure, gtkidx, encr = 0;
+	u8 *wpa_ie_buf = NULL, *wpa_ie_buf2 = NULL;
+	u8 hdr[2];
+	struct wpa_auth_config *conf = &sm->wpa_auth->conf;
+#ifdef CONFIG_IEEE80211BE
+	bool is_mld = sm->mld_assoc_link_id >= 0;
+#else /* CONFIG_IEEE80211BE */
+	bool is_mld = false;
+#endif /* CONFIG_IEEE80211BE */
+
+	SM_ENTRY_MA(WPA_PTK, PTKINITNEGOTIATING, wpa_ptk);
+	sm->TimeoutEvt = false;
+
+	sm->TimeoutCtr++;
+	if (conf->wpa_disable_eapol_key_retries && sm->TimeoutCtr > 1) {
+		/* Do not allow retransmission of EAPOL-Key msg 3/4 */
+		return;
+	}
+	if (sm->TimeoutCtr > conf->wpa_pairwise_update_count) {
+		/* No point in sending the EAPOL-Key - we will disconnect
+		 * immediately following this. */
+		return;
+	}
+
+	/* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, [MDIE],
+	   GTK[GN], IGTK, [BIGTK], [FTIE], [TIE * 2])
+	 */
+	os_memset(rsc, 0, WPA_KEY_RSC_LEN);
+	wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc);
+	/* If FT is used, wpa_auth->wpa_ie includes both RSNIE and MDIE */
+	wpa_ie = sm->wpa_auth->wpa_ie;
+	wpa_ie_len = sm->wpa_auth->wpa_ie_len;
+	if (sm->wpa == WPA_VERSION_WPA && (conf->wpa & WPA_PROTO_RSN) &&
+	    wpa_ie_len > wpa_ie[1] + 2U && wpa_ie[0] == WLAN_EID_RSN) {
+		/* WPA-only STA, remove RSN IE and possible MDIE */
+		wpa_ie = wpa_ie + wpa_ie[1] + 2;
+		if (wpa_ie[0] == WLAN_EID_RSNX)
+			wpa_ie = wpa_ie + wpa_ie[1] + 2;
+		if (wpa_ie[0] == WLAN_EID_MOBILITY_DOMAIN)
+			wpa_ie = wpa_ie + wpa_ie[1] + 2;
+		wpa_ie_len = wpa_ie[1] + 2;
+	}
+#ifdef CONFIG_TESTING_OPTIONS
+	if (conf->rsne_override_eapol_set) {
+		wpa_ie_buf2 = replace_ie(
+			"RSNE", wpa_ie, &wpa_ie_len, WLAN_EID_RSN,
+			conf->rsne_override_eapol,
+			conf->rsne_override_eapol_len);
+		if (!wpa_ie_buf2)
+			goto done;
+		wpa_ie = wpa_ie_buf2;
+	}
+	if (conf->rsnxe_override_eapol_set) {
+		wpa_ie_buf = replace_ie(
+			"RSNXE", wpa_ie, &wpa_ie_len, WLAN_EID_RSNX,
+			conf->rsnxe_override_eapol,
+			conf->rsnxe_override_eapol_len);
+		if (!wpa_ie_buf)
+			goto done;
+		wpa_ie = wpa_ie_buf;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+	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 &&
+		    wpa_auth_set_key(sm->wpa_auth, 0,
+				     wpa_cipher_to_alg(sm->pairwise),
+				     sm->addr,
+				     sm->keyidx_active, sm->PTK.tk,
+				     wpa_cipher_key_len(sm->pairwise),
+				     KEY_FLAG_PAIRWISE_RX)) {
+			wpa_sta_disconnect(sm->wpa_auth, sm->addr,
+					   WLAN_REASON_PREV_AUTH_NOT_VALID);
+			return;
+		}
+
+#ifdef CONFIG_PASN
+		if (sm->wpa_auth->conf.secure_ltf &&
+		    ieee802_11_rsnx_capab(sm->rsnxe,
+					  WLAN_RSNX_CAPAB_SECURE_LTF) &&
+		    wpa_auth_set_ltf_keyseed(sm->wpa_auth, sm->addr,
+					     sm->PTK.ltf_keyseed,
+					     sm->PTK.ltf_keyseed_len)) {
+			wpa_printf(MSG_ERROR,
+				   "WPA: Failed to set LTF keyseed to driver");
+			wpa_sta_disconnect(sm->wpa_auth, sm->addr,
+					   WLAN_REASON_PREV_AUTH_NOT_VALID);
+			return;
+		}
+#endif /* CONFIG_PASN */
+
+		/* WPA2 send GTK in the 4-way handshake */
+		secure = 1;
+		gtk = gsm->GTK[gsm->GN - 1];
+		gtk_len = gsm->GTK_len;
+		if (conf->disable_gtk ||
+		    sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+			/*
+			 * Provide unique random GTK to each STA to prevent use
+			 * of GTK in the BSS.
+			 */
+			if (random_get_bytes(stub_gtk, gtk_len) < 0)
+				goto done;
+			gtk = stub_gtk;
+		}
+		gtkidx = gsm->GN;
+		_rsc = rsc;
+		encr = 1;
+	} else {
+		/* WPA does not include GTK in msg 3/4 */
+		secure = 0;
+		gtk = NULL;
+		gtk_len = 0;
+		gtkidx = 0;
+		_rsc = NULL;
+		if (sm->rx_eapol_key_secure) {
+			/*
+			 * It looks like Windows 7 supplicant tries to use
+			 * Secure bit in msg 2/4 after having reported Michael
+			 * MIC failure and it then rejects the 4-way handshake
+			 * if msg 3/4 does not set Secure bit. Work around this
+			 * by setting the Secure bit here even in the case of
+			 * WPA if the supplicant used it first.
+			 */
+			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;
+		}
+	}
+
+	kde_len = wpa_ie_len + ieee80211w_kde_len(sm) + ocv_oci_len(sm);
+
+	if (sm->use_ext_key_id)
+		kde_len += 2 + RSN_SELECTOR_LEN + 2;
+
+	if (gtk)
+		kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len;
+#ifdef CONFIG_IEEE80211R_AP
+	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+		kde_len += 2 + PMKID_LEN; /* PMKR1Name into RSN IE */
+		kde_len += 300; /* FTIE + 2 * TIE */
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+#ifdef CONFIG_P2P
+	if (WPA_GET_BE32(sm->ip_addr) > 0)
+		kde_len += 2 + RSN_SELECTOR_LEN + 3 * 4;
+#endif /* CONFIG_P2P */
+
+	if (conf->transition_disable)
+		kde_len += 2 + RSN_SELECTOR_LEN + 1;
+
+#ifdef CONFIG_DPP2
+	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP)
+		kde_len += 2 + RSN_SELECTOR_LEN + 2;
+#endif /* CONFIG_DPP2 */
+
+	kde_len += wpa_auth_ml_kdes_len(sm);
+
+	kde = os_malloc(kde_len);
+	if (!kde)
+		goto done;
+
+	pos = kde;
+	if (!is_mld) {
+		os_memcpy(pos, wpa_ie, wpa_ie_len);
+		pos += wpa_ie_len;
+	}
+#ifdef CONFIG_IEEE80211R_AP
+	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+		int res;
+		size_t elen;
+
+		elen = pos - kde;
+		res = wpa_insert_pmkid(kde, &elen, sm->pmk_r1_name);
+		if (res < 0) {
+			wpa_printf(MSG_ERROR,
+				   "FT: Failed to insert PMKR1Name into RSN IE in EAPOL-Key data");
+			goto done;
+		}
+		pos -= wpa_ie_len;
+		pos += elen;
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+	hdr[1] = 0;
+
+	if (sm->use_ext_key_id) {
+		hdr[0] = sm->keyidx_active & 0x01;
+		pos = wpa_add_kde(pos, RSN_KEY_DATA_KEYID, hdr, 2, NULL, 0);
+	}
+
+	if (gtk && !is_mld) {
+		hdr[0] = gtkidx & 0x03;
+		pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
+				  gtk, gtk_len);
+	}
+	pos = ieee80211w_kde_add(sm, pos);
+	if (ocv_oci_add(sm, &pos, conf->oci_freq_override_eapol_m3) < 0)
+		goto done;
+
+#ifdef CONFIG_IEEE80211R_AP
+	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+		int res;
+
+		if (sm->assoc_resp_ftie &&
+		    kde + kde_len - pos >= 2 + sm->assoc_resp_ftie[1]) {
+			os_memcpy(pos, sm->assoc_resp_ftie,
+				  2 + sm->assoc_resp_ftie[1]);
+			res = 2 + sm->assoc_resp_ftie[1];
+		} else {
+			res = wpa_write_ftie(conf, sm->wpa_key_mgmt,
+					     sm->xxkey_len,
+					     conf->r0_key_holder,
+					     conf->r0_key_holder_len,
+					     NULL, NULL, pos,
+					     kde + kde_len - pos,
+					     NULL, 0, 0);
+		}
+		if (res < 0) {
+			wpa_printf(MSG_ERROR,
+				   "FT: Failed to insert FTIE into EAPOL-Key Key Data");
+			goto done;
+		}
+		pos += res;
+
+		/* TIE[ReassociationDeadline] (TU) */
+		*pos++ = WLAN_EID_TIMEOUT_INTERVAL;
+		*pos++ = 5;
+		*pos++ = WLAN_TIMEOUT_REASSOC_DEADLINE;
+		WPA_PUT_LE32(pos, conf->reassociation_deadline);
+		pos += 4;
+
+		/* TIE[KeyLifetime] (seconds) */
+		*pos++ = WLAN_EID_TIMEOUT_INTERVAL;
+		*pos++ = 5;
+		*pos++ = WLAN_TIMEOUT_KEY_LIFETIME;
+		WPA_PUT_LE32(pos, conf->r0_key_lifetime);
+		pos += 4;
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+#ifdef CONFIG_P2P
+	if (WPA_GET_BE32(sm->ip_addr) > 0) {
+		u8 addr[3 * 4];
+		os_memcpy(addr, sm->ip_addr, 4);
+		os_memcpy(addr + 4, conf->ip_addr_mask, 4);
+		os_memcpy(addr + 8, conf->ip_addr_go, 4);
+		pos = wpa_add_kde(pos, WFA_KEY_DATA_IP_ADDR_ALLOC,
+				  addr, sizeof(addr), NULL, 0);
+	}
+#endif /* CONFIG_P2P */
+
+	if (conf->transition_disable)
+		pos = wpa_add_kde(pos, WFA_KEY_DATA_TRANSITION_DISABLE,
+				  &conf->transition_disable, 1, NULL, 0);
+
+#ifdef CONFIG_DPP2
+	if (DPP_VERSION > 1 && sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP) {
+		u8 payload[2];
+
+		payload[0] = DPP_VERSION; /* Protocol Version */
+		payload[1] = 0; /* Flags */
+		if (conf->dpp_pfs == 0)
+			payload[1] |= DPP_KDE_PFS_ALLOWED;
+		else if (conf->dpp_pfs == 1)
+			payload[1] |= DPP_KDE_PFS_ALLOWED |
+				DPP_KDE_PFS_REQUIRED;
+		pos = wpa_add_kde(pos, WFA_KEY_DATA_DPP,
+				  payload, sizeof(payload), NULL, 0);
+	}
+#endif /* CONFIG_DPP2 */
+
+	pos = wpa_auth_ml_kdes(sm, pos);
+
+	wpa_send_eapol(sm->wpa_auth, sm,
+		       (secure ? WPA_KEY_INFO_SECURE : 0) |
+		       (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
+			WPA_KEY_INFO_MIC : 0) |
+		       WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL |
+		       WPA_KEY_INFO_KEY_TYPE,
+		       _rsc, sm->ANonce, kde, pos - kde, 0, encr);
+done:
+	bin_clear_free(kde, kde_len);
+	os_free(wpa_ie_buf);
+	os_free(wpa_ie_buf2);
+}
+
+
+static int wpa_auth_validate_ml_kdes_m4(struct wpa_state_machine *sm)
+{
+#ifdef CONFIG_IEEE80211BE
+	const struct ieee802_1x_hdr *hdr;
+	const struct wpa_eapol_key *key;
+	struct wpa_eapol_ie_parse kde;
+	const u8 *key_data, *mic;
+	u16 key_data_length;
+	size_t mic_len;
+
+	if (sm->mld_assoc_link_id < 0)
+		return 0;
+
+	/*
+	 * Note: last_rx_eapol_key length fields have already been validated in
+	 * wpa_receive().
+	 */
+	mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
+
+	hdr = (const struct ieee802_1x_hdr *) sm->last_rx_eapol_key;
+	key = (const struct wpa_eapol_key *) (hdr + 1);
+	mic = (const u8 *) (key + 1);
+	key_data = mic + mic_len + 2;
+	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 -1;
+
+	if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) {
+		wpa_auth_vlogger(sm->wpa_auth, wpa_auth_get_spa(sm),
+				 LOGGER_INFO,
+				 "received EAPOL-Key msg 4/4 with invalid Key Data contents");
+		return -1;
+	}
+
+	/* MLD MAC address must be the same */
+	if (!kde.mac_addr ||
+	    os_memcmp(kde.mac_addr, sm->peer_mld_addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "MLD: Mismatching or missing MLD address in EAPOL-Key msg 4/4");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "MLD: MLD address in EAPOL-Key msg 4/4: " MACSTR,
+		   MAC2STR(kde.mac_addr));
+#endif /* CONFIG_IEEE80211BE */
+
+	return 0;
+}
+
+
+SM_STATE(WPA_PTK, PTKINITDONE)
+{
+	SM_ENTRY_MA(WPA_PTK, PTKINITDONE, wpa_ptk);
+	sm->EAPOLKeyReceived = false;
+
+	if (wpa_auth_validate_ml_kdes_m4(sm) < 0) {
+		wpa_sta_disconnect(sm->wpa_auth, sm->addr,
+				   WLAN_REASON_PREV_AUTH_NOT_VALID);
+		return;
+	}
+
+	if (sm->Pair) {
+		enum wpa_alg alg = wpa_cipher_to_alg(sm->pairwise);
+		int klen = wpa_cipher_key_len(sm->pairwise);
+		int res;
+
+		if (sm->use_ext_key_id)
+			res = wpa_auth_set_key(sm->wpa_auth, 0, 0, sm->addr,
+					       sm->keyidx_active, NULL, 0,
+					       KEY_FLAG_PAIRWISE_RX_TX_MODIFY);
+		else
+			res = wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr,
+					       0, sm->PTK.tk, klen,
+					       KEY_FLAG_PAIRWISE_RX_TX);
+		if (res) {
+			wpa_sta_disconnect(sm->wpa_auth, sm->addr,
+					   WLAN_REASON_PREV_AUTH_NOT_VALID);
+			return;
+		}
+
+#ifdef CONFIG_PASN
+		if (sm->wpa_auth->conf.secure_ltf &&
+		    ieee802_11_rsnx_capab(sm->rsnxe,
+					  WLAN_RSNX_CAPAB_SECURE_LTF) &&
+		    wpa_auth_set_ltf_keyseed(sm->wpa_auth, sm->addr,
+					     sm->PTK.ltf_keyseed,
+					     sm->PTK.ltf_keyseed_len)) {
+			wpa_printf(MSG_ERROR,
+				   "WPA: Failed to set LTF keyseed to driver");
+			wpa_sta_disconnect(sm->wpa_auth, sm->addr,
+					   WLAN_REASON_PREV_AUTH_NOT_VALID);
+			return;
+		}
+#endif /* CONFIG_PASN */
+
+		/* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */
+		sm->pairwise_set = true;
+
+		wpa_auth_set_ptk_rekey_timer(sm);
+		wpa_auth_store_ptksa(sm->wpa_auth, sm->addr, sm->pairwise,
+				     dot11RSNAConfigPMKLifetime, &sm->PTK);
+
+		if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
+		    sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP ||
+		    sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE) {
+			wpa_auth_set_eapol(sm->wpa_auth, sm->addr,
+					   WPA_EAPOL_authorized, 1);
+		}
+	}
+
+	if (0 /* IBSS == TRUE */) {
+		sm->keycount++;
+		if (sm->keycount == 2) {
+			wpa_auth_set_eapol(sm->wpa_auth, sm->addr,
+					   WPA_EAPOL_portValid, 1);
+		}
+	} else {
+		wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid,
+				   1);
+	}
+	wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyAvailable,
+			   false);
+	wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyDone, true);
+	if (sm->wpa == WPA_VERSION_WPA)
+		sm->PInitAKeys = true;
+	else
+		sm->has_GTK = true;
+	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, wpa_auth_get_spa(sm));
+#endif /* CONFIG_IEEE80211R_AP */
+
+	sm->ptkstart_without_success = 0;
+}
+
+
+SM_STEP(WPA_PTK)
+{
+	struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+	struct wpa_auth_config *conf = &wpa_auth->conf;
+
+	if (sm->Init)
+		SM_ENTER(WPA_PTK, INITIALIZE);
+	else if (sm->Disconnect
+		 /* || FIX: dot11RSNAConfigSALifetime timeout */) {
+		wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
+				"WPA_PTK: sm->Disconnect");
+		SM_ENTER(WPA_PTK, DISCONNECT);
+	}
+	else if (sm->DeauthenticationRequest)
+		SM_ENTER(WPA_PTK, DISCONNECTED);
+	else if (sm->AuthenticationRequest)
+		SM_ENTER(WPA_PTK, AUTHENTICATION);
+	else if (sm->ReAuthenticationRequest)
+		SM_ENTER(WPA_PTK, AUTHENTICATION2);
+	else if (sm->PTKRequest) {
+		if (wpa_auth_sm_ptk_update(sm) < 0)
+			SM_ENTER(WPA_PTK, DISCONNECTED);
+		else
+			SM_ENTER(WPA_PTK, PTKSTART);
+	} else switch (sm->wpa_ptk_state) {
+	case WPA_PTK_INITIALIZE:
+		break;
+	case WPA_PTK_DISCONNECT:
+		SM_ENTER(WPA_PTK, DISCONNECTED);
+		break;
+	case WPA_PTK_DISCONNECTED:
+		SM_ENTER(WPA_PTK, INITIALIZE);
+		break;
+	case WPA_PTK_AUTHENTICATION:
+		SM_ENTER(WPA_PTK, AUTHENTICATION2);
+		break;
+	case WPA_PTK_AUTHENTICATION2:
+		if (wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) &&
+		    wpa_auth_get_eapol(wpa_auth, sm->addr,
+				       WPA_EAPOL_keyRun))
+			SM_ENTER(WPA_PTK, INITPMK);
+		else if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
+			 sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE
+			 /* FIX: && 802.1X::keyRun */)
+			SM_ENTER(WPA_PTK, INITPSK);
+		else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP)
+			SM_ENTER(WPA_PTK, INITPMK);
+		break;
+	case WPA_PTK_INITPMK:
+		if (wpa_auth_get_eapol(wpa_auth, sm->addr,
+				       WPA_EAPOL_keyAvailable)) {
+			SM_ENTER(WPA_PTK, PTKSTART);
+#ifdef CONFIG_DPP
+		} else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && sm->pmksa) {
+			SM_ENTER(WPA_PTK, PTKSTART);
+#endif /* CONFIG_DPP */
+		} else {
+			wpa_auth->dot11RSNA4WayHandshakeFailures++;
+			wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+					LOGGER_INFO,
+					"INITPMK - keyAvailable = false");
+			SM_ENTER(WPA_PTK, DISCONNECT);
+		}
+		break;
+	case WPA_PTK_INITPSK:
+		if (wpa_auth_get_psk(wpa_auth, sm->addr, sm->p2p_dev_addr,
+				     NULL, NULL, NULL)) {
+			SM_ENTER(WPA_PTK, PTKSTART);
+#ifdef CONFIG_SAE
+		} else if (wpa_auth_uses_sae(sm) && sm->pmksa) {
+			SM_ENTER(WPA_PTK, PTKSTART);
+#endif /* CONFIG_SAE */
+		} else if (wpa_key_mgmt_wpa_psk_no_sae(sm->wpa_key_mgmt) &&
+			   wpa_auth->conf.radius_psk) {
+			wpa_printf(MSG_DEBUG,
+				   "INITPSK: No PSK yet available for STA - use RADIUS later");
+			SM_ENTER(WPA_PTK, PTKSTART);
+		} else {
+			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);
+		}
+		break;
+	case WPA_PTK_PTKSTART:
+		if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest &&
+		    sm->EAPOLKeyPairwise)
+			SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING);
+		else if (sm->TimeoutCtr > conf->wpa_pairwise_update_count) {
+			wpa_auth->dot11RSNA4WayHandshakeFailures++;
+			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 =
+				WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT;
+			SM_ENTER(WPA_PTK, DISCONNECT);
+		} else if (sm->TimeoutEvt)
+			SM_ENTER(WPA_PTK, PTKSTART);
+		break;
+	case WPA_PTK_PTKCALCNEGOTIATING:
+		if (sm->MICVerified)
+			SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING2);
+		else if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest &&
+			 sm->EAPOLKeyPairwise)
+			SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING);
+		else if (sm->TimeoutEvt)
+			SM_ENTER(WPA_PTK, PTKSTART);
+		break;
+	case WPA_PTK_PTKCALCNEGOTIATING2:
+		SM_ENTER(WPA_PTK, PTKINITNEGOTIATING);
+		break;
+	case WPA_PTK_PTKINITNEGOTIATING:
+		if (sm->update_snonce)
+			SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING);
+		else if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest &&
+			 sm->EAPOLKeyPairwise && sm->MICVerified)
+			SM_ENTER(WPA_PTK, PTKINITDONE);
+		else if (sm->TimeoutCtr >
+			 conf->wpa_pairwise_update_count ||
+			 (conf->wpa_disable_eapol_key_retries &&
+			  sm->TimeoutCtr > 1)) {
+			wpa_auth->dot11RSNA4WayHandshakeFailures++;
+			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 =
+				WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT;
+			SM_ENTER(WPA_PTK, DISCONNECT);
+		} else if (sm->TimeoutEvt)
+			SM_ENTER(WPA_PTK, PTKINITNEGOTIATING);
+		break;
+	case WPA_PTK_PTKINITDONE:
+		break;
+	}
+}
+
+
+SM_STATE(WPA_PTK_GROUP, IDLE)
+{
+	SM_ENTRY_MA(WPA_PTK_GROUP, IDLE, wpa_ptk_group);
+	if (sm->Init) {
+		/* Init flag is not cleared here, so avoid busy
+		 * loop by claiming nothing changed. */
+		sm->changed = false;
+	}
+	sm->GTimeoutCtr = 0;
+}
+
+
+SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING)
+{
+	u8 rsc[WPA_KEY_RSC_LEN];
+	struct wpa_group *gsm = sm->group;
+	const u8 *kde = NULL;
+	u8 *kde_buf = NULL, *pos, hdr[2];
+	size_t kde_len = 0;
+	u8 *gtk, stub_gtk[32];
+	struct wpa_auth_config *conf = &sm->wpa_auth->conf;
+	bool is_mld = false;
+
+#ifdef CONFIG_IEEE80211BE
+	is_mld = sm->mld_assoc_link_id >= 0;
+#endif /* CONFIG_IEEE80211BE */
+
+	SM_ENTRY_MA(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group);
+
+	sm->GTimeoutCtr++;
+	if (conf->wpa_disable_eapol_key_retries && sm->GTimeoutCtr > 1) {
+		/* Do not allow retransmission of EAPOL-Key group msg 1/2 */
+		return;
+	}
+	if (sm->GTimeoutCtr > conf->wpa_group_update_count) {
+		/* No point in sending the EAPOL-Key - we will disconnect
+		 * immediately following this. */
+		return;
+	}
+
+	if (sm->wpa == WPA_VERSION_WPA)
+		sm->PInitAKeys = false;
+	sm->TimeoutEvt = false;
+	/* Send EAPOL(1, 1, 1, !Pair, G, RSC, GNonce, MIC(PTK), GTK[GN]) */
+	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, wpa_auth_get_spa(sm), LOGGER_DEBUG,
+			"sending 1/2 msg of Group Key Handshake");
+
+	gtk = gsm->GTK[gsm->GN - 1];
+	if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+		/*
+		 * Provide unique random GTK to each STA to prevent use
+		 * of GTK in the BSS.
+		 */
+		if (random_get_bytes(stub_gtk, gsm->GTK_len) < 0)
+			return;
+		gtk = stub_gtk;
+	}
+
+	if (sm->wpa == WPA_VERSION_WPA2 && !is_mld) {
+		kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len +
+			ieee80211w_kde_len(sm) + ocv_oci_len(sm);
+		kde_buf = os_malloc(kde_len);
+		if (!kde_buf)
+			return;
+
+		kde = pos = kde_buf;
+		hdr[0] = gsm->GN & 0x03;
+		hdr[1] = 0;
+		pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
+				  gtk, gsm->GTK_len);
+		pos = ieee80211w_kde_add(sm, pos);
+		if (ocv_oci_add(sm, &pos,
+				conf->oci_freq_override_eapol_g1) < 0) {
+			os_free(kde_buf);
+			return;
+		}
+		kde_len = pos - kde;
+#ifdef CONFIG_IEEE80211BE
+	} else if (sm->wpa == WPA_VERSION_WPA2 && is_mld) {
+		kde_len = wpa_auth_ml_group_kdes_len(sm);
+		if (kde_len) {
+			kde_buf = os_malloc(kde_len);
+			if (!kde_buf)
+				return;
+
+			kde = pos = kde_buf;
+			wpa_auth_ml_group_kdes(sm, pos);
+		}
+#endif /* CONFIG_IEEE80211BE */
+	} else {
+		kde = gtk;
+		kde_len = gsm->GTK_len;
+	}
+
+	wpa_send_eapol(sm->wpa_auth, sm,
+		       WPA_KEY_INFO_SECURE |
+		       (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
+			WPA_KEY_INFO_MIC : 0) |
+		       WPA_KEY_INFO_ACK |
+		       (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0),
+		       rsc, NULL, kde, kde_len, gsm->GN, 1);
+
+	bin_clear_free(kde_buf, kde_len);
+}
+
+
+SM_STATE(WPA_PTK_GROUP, REKEYESTABLISHED)
+{
+	struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+#ifdef CONFIG_OCV
+	const u8 *key_data, *mic;
+	struct ieee802_1x_hdr *hdr;
+	struct wpa_eapol_key *key;
+	struct wpa_eapol_ie_parse kde;
+	size_t mic_len;
+	u16 key_data_length;
+#endif /* CONFIG_OCV */
+
+	SM_ENTRY_MA(WPA_PTK_GROUP, REKEYESTABLISHED, wpa_ptk_group);
+	sm->EAPOLKeyReceived = false;
+
+#ifdef CONFIG_OCV
+	mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
+
+	/*
+	 * Note: last_rx_eapol_key length fields have already been validated in
+	 * wpa_receive().
+	 */
+	hdr = (struct ieee802_1x_hdr *) sm->last_rx_eapol_key;
+	key = (struct wpa_eapol_key *) (hdr + 1);
+	mic = (u8 *) (key + 1);
+	key_data = mic + mic_len + 2;
+	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;
+
+	if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) {
+		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;
+	}
+
+	if (wpa_auth_uses_ocv(sm)) {
+		struct wpa_channel_info ci;
+		int tx_chanwidth;
+		int tx_seg1_idx;
+
+		if (wpa_channel_info(wpa_auth, &ci) != 0) {
+			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;
+		}
+
+		if (get_sta_tx_parameters(sm,
+					  channel_width_to_int(ci.chanwidth),
+					  ci.seg1_idx, &tx_chanwidth,
+					  &tx_seg1_idx) < 0)
+			return;
+
+		if (ocv_verify_tx_params(kde.oci, kde.oci_len, &ci,
+					 tx_chanwidth, tx_seg1_idx) !=
+		    OCI_SUCCESS) {
+			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(wpa_auth_get_spa(sm)),
+					ocv_errorstr);
+			return;
+		}
+	}
+#endif /* CONFIG_OCV */
+
+	if (sm->GUpdateStationKeys)
+		sm->group->GKeyDoneStations--;
+	sm->GUpdateStationKeys = false;
+	sm->GTimeoutCtr = 0;
+	/* FIX: MLME.SetProtection.Request(TA, Tx_Rx) */
+	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;
+}
+
+
+SM_STATE(WPA_PTK_GROUP, KEYERROR)
+{
+	SM_ENTRY_MA(WPA_PTK_GROUP, KEYERROR, wpa_ptk_group);
+	if (sm->GUpdateStationKeys)
+		sm->group->GKeyDoneStations--;
+	sm->GUpdateStationKeys = false;
+	sm->Disconnect = true;
+	sm->disconnect_reason = WLAN_REASON_GROUP_KEY_UPDATE_TIMEOUT;
+	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);
+}
+
+
+SM_STEP(WPA_PTK_GROUP)
+{
+	if (sm->Init || sm->PtkGroupInit) {
+		SM_ENTER(WPA_PTK_GROUP, IDLE);
+		sm->PtkGroupInit = false;
+	} else switch (sm->wpa_ptk_group_state) {
+	case WPA_PTK_GROUP_IDLE:
+		if (sm->GUpdateStationKeys ||
+		    (sm->wpa == WPA_VERSION_WPA && sm->PInitAKeys))
+			SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING);
+		break;
+	case WPA_PTK_GROUP_REKEYNEGOTIATING:
+		if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest &&
+		    !sm->EAPOLKeyPairwise && sm->MICVerified)
+			SM_ENTER(WPA_PTK_GROUP, REKEYESTABLISHED);
+		else if (sm->GTimeoutCtr >
+			 sm->wpa_auth->conf.wpa_group_update_count ||
+			 (sm->wpa_auth->conf.wpa_disable_eapol_key_retries &&
+			  sm->GTimeoutCtr > 1))
+			SM_ENTER(WPA_PTK_GROUP, KEYERROR);
+		else if (sm->TimeoutEvt)
+			SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING);
+		break;
+	case WPA_PTK_GROUP_KEYERROR:
+		SM_ENTER(WPA_PTK_GROUP, IDLE);
+		break;
+	case WPA_PTK_GROUP_REKEYESTABLISHED:
+		SM_ENTER(WPA_PTK_GROUP, IDLE);
+		break;
+	}
+}
+
+
+static int wpa_gtk_update(struct wpa_authenticator *wpa_auth,
+			  struct wpa_group *group)
+{
+	struct wpa_auth_config *conf = &wpa_auth->conf;
+	int ret = 0;
+	size_t len;
+
+	os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN);
+	inc_byte_array(group->Counter, WPA_NONCE_LEN);
+	if (wpa_gmk_to_gtk(group->GMK, "Group key expansion",
+			   wpa_auth->addr, group->GNonce,
+			   group->GTK[group->GN - 1], group->GTK_len) < 0)
+		ret = -1;
+	wpa_hexdump_key(MSG_DEBUG, "GTK",
+			group->GTK[group->GN - 1], group->GTK_len);
+
+	if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+		len = wpa_cipher_key_len(conf->group_mgmt_cipher);
+		os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN);
+		inc_byte_array(group->Counter, WPA_NONCE_LEN);
+		if (wpa_gmk_to_gtk(group->GMK, "IGTK key expansion",
+				   wpa_auth->addr, group->GNonce,
+				   group->IGTK[group->GN_igtk - 4], len) < 0)
+			ret = -1;
+		wpa_hexdump_key(MSG_DEBUG, "IGTK",
+				group->IGTK[group->GN_igtk - 4], len);
+	}
+
+	if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION &&
+	    conf->beacon_prot) {
+		len = wpa_cipher_key_len(conf->group_mgmt_cipher);
+		os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN);
+		inc_byte_array(group->Counter, WPA_NONCE_LEN);
+		if (wpa_gmk_to_gtk(group->GMK, "BIGTK key expansion",
+				   wpa_auth->addr, group->GNonce,
+				   group->BIGTK[group->GN_bigtk - 6], len) < 0)
+			ret = -1;
+		wpa_hexdump_key(MSG_DEBUG, "BIGTK",
+				group->BIGTK[group->GN_bigtk - 6], len);
+	}
+
+	return ret;
+}
+
+
+static void wpa_group_gtk_init(struct wpa_authenticator *wpa_auth,
+			       struct wpa_group *group)
+{
+	wpa_printf(MSG_DEBUG,
+		   "WPA: group state machine entering state GTK_INIT (VLAN-ID %d)",
+		   group->vlan_id);
+	group->changed = false; /* GInit is not cleared here; avoid loop */
+	group->wpa_group_state = WPA_GROUP_GTK_INIT;
+
+	/* GTK[0..N] = 0 */
+	os_memset(group->GTK, 0, sizeof(group->GTK));
+	group->GN = 1;
+	group->GM = 2;
+	group->GN_igtk = 4;
+	group->GM_igtk = 5;
+	group->GN_bigtk = 6;
+	group->GM_bigtk = 7;
+	/* GTK[GN] = CalcGTK() */
+	wpa_gtk_update(wpa_auth, group);
+}
+
+
+static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx)
+{
+	if (ctx != NULL && ctx != sm->group)
+		return 0;
+
+	if (sm->wpa_ptk_state != WPA_PTK_PTKINITDONE) {
+		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;
+	}
+	if (sm->GUpdateStationKeys) {
+		/*
+		 * This should not really happen, so add a debug log entry.
+		 * Since we clear the GKeyDoneStations before the loop, the
+		 * station needs to be counted here anyway.
+		 */
+		wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm),
+				LOGGER_DEBUG,
+				"GUpdateStationKeys was already set when marking station for GTK rekeying");
+	}
+
+	/* Do not rekey GTK/IGTK when STA is in WNM-Sleep Mode */
+	if (sm->is_wnmsleep)
+		return 0;
+
+	sm->group->GKeyDoneStations++;
+	sm->GUpdateStationKeys = true;
+
+	wpa_sm_step(sm);
+	return 0;
+}
+
+
+#ifdef CONFIG_WNM_AP
+/* update GTK when exiting WNM-Sleep Mode */
+void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm)
+{
+	if (!sm || sm->is_wnmsleep)
+		return;
+
+	wpa_group_update_sta(sm, NULL);
+}
+
+
+void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag)
+{
+	if (sm)
+		sm->is_wnmsleep = !!flag;
+}
+
+
+int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos)
+{
+	struct wpa_auth_config *conf = &sm->wpa_auth->conf;
+	struct wpa_group *gsm = sm->group;
+	u8 *start = pos;
+
+	/*
+	 * GTK subelement:
+	 * Sub-elem ID[1] | Length[1] | Key Info[2] | Key Length[1] | RSC[8] |
+	 * Key[5..32]
+	 */
+	*pos++ = WNM_SLEEP_SUBELEM_GTK;
+	*pos++ = 11 + gsm->GTK_len;
+	/* Key ID in B0-B1 of Key Info */
+	WPA_PUT_LE16(pos, gsm->GN & 0x03);
+	pos += 2;
+	*pos++ = gsm->GTK_len;
+	if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, pos) != 0)
+		return 0;
+	pos += 8;
+	os_memcpy(pos, gsm->GTK[gsm->GN - 1], gsm->GTK_len);
+	if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+		/*
+		 * Provide unique random GTK to each STA to prevent use
+		 * of GTK in the BSS.
+		 */
+		if (random_get_bytes(pos, gsm->GTK_len) < 0)
+			return 0;
+	}
+	pos += gsm->GTK_len;
+
+	wpa_printf(MSG_DEBUG, "WNM: GTK Key ID %u in WNM-Sleep Mode exit",
+		   gsm->GN);
+	wpa_hexdump_key(MSG_DEBUG, "WNM: GTK in WNM-Sleep Mode exit",
+			gsm->GTK[gsm->GN - 1], gsm->GTK_len);
+
+	return pos - start;
+}
+
+
+int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos)
+{
+	struct wpa_auth_config *conf = &sm->wpa_auth->conf;
+	struct wpa_group *gsm = sm->group;
+	u8 *start = pos;
+	size_t len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
+
+	/*
+	 * IGTK subelement:
+	 * Sub-elem ID[1] | Length[1] | KeyID[2] | PN[6] | Key[16]
+	 */
+	*pos++ = WNM_SLEEP_SUBELEM_IGTK;
+	*pos++ = 2 + 6 + len;
+	WPA_PUT_LE16(pos, gsm->GN_igtk);
+	pos += 2;
+	if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos) != 0)
+		return 0;
+	pos += 6;
+
+	os_memcpy(pos, gsm->IGTK[gsm->GN_igtk - 4], len);
+	if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+		/*
+		 * Provide unique random IGTK to each STA to prevent use
+		 * of IGTK in the BSS.
+		 */
+		if (random_get_bytes(pos, len) < 0)
+			return 0;
+	}
+	pos += len;
+
+	wpa_printf(MSG_DEBUG, "WNM: IGTK Key ID %u in WNM-Sleep Mode exit",
+		   gsm->GN_igtk);
+	wpa_hexdump_key(MSG_DEBUG, "WNM: IGTK in WNM-Sleep Mode exit",
+			gsm->IGTK[gsm->GN_igtk - 4], len);
+
+	return pos - start;
+}
+
+
+int wpa_wnmsleep_bigtk_subelem(struct wpa_state_machine *sm, u8 *pos)
+{
+	struct wpa_group *gsm = sm->group;
+	u8 *start = pos;
+	size_t len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
+
+	/*
+	 * BIGTK subelement:
+	 * Sub-elem ID[1] | Length[1] | KeyID[2] | PN[6] | Key[16]
+	 */
+	*pos++ = WNM_SLEEP_SUBELEM_BIGTK;
+	*pos++ = 2 + 6 + len;
+	WPA_PUT_LE16(pos, gsm->GN_bigtk);
+	pos += 2;
+	if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_bigtk, pos) != 0)
+		return 0;
+	pos += 6;
+
+	os_memcpy(pos, gsm->BIGTK[gsm->GN_bigtk - 6], len);
+	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+		/*
+		 * Provide unique random BIGTK to each STA to prevent use
+		 * of BIGTK in the BSS.
+		 */
+		if (random_get_bytes(pos, len) < 0)
+			return 0;
+	}
+	pos += len;
+
+	wpa_printf(MSG_DEBUG, "WNM: BIGTK Key ID %u in WNM-Sleep Mode exit",
+		   gsm->GN_bigtk);
+	wpa_hexdump_key(MSG_DEBUG, "WNM: BIGTK in WNM-Sleep Mode exit",
+			gsm->BIGTK[gsm->GN_bigtk - 6], len);
+
+	return pos - start;
+}
+
+#endif /* CONFIG_WNM_AP */
+
+
+static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
+			      struct wpa_group *group)
+{
+	int tmp;
+
+	wpa_printf(MSG_DEBUG,
+		   "WPA: group state machine entering state SETKEYS (VLAN-ID %d)",
+		   group->vlan_id);
+	group->changed = true;
+	group->wpa_group_state = WPA_GROUP_SETKEYS;
+	group->GTKReKey = false;
+	tmp = group->GM;
+	group->GM = group->GN;
+	group->GN = tmp;
+	tmp = group->GM_igtk;
+	group->GM_igtk = group->GN_igtk;
+	group->GN_igtk = tmp;
+	tmp = group->GM_bigtk;
+	group->GM_bigtk = group->GN_bigtk;
+	group->GN_bigtk = tmp;
+	/* "GKeyDoneStations = GNoStations" is done in more robust way by
+	 * counting the STAs that are marked with GUpdateStationKeys instead of
+	 * including all STAs that could be in not-yet-completed state. */
+	wpa_gtk_update(wpa_auth, group);
+
+	if (group->GKeyDoneStations) {
+		wpa_printf(MSG_DEBUG,
+			   "wpa_group_setkeys: Unexpected GKeyDoneStations=%d when starting new GTK rekey",
+			   group->GKeyDoneStations);
+		group->GKeyDoneStations = 0;
+	}
+	wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, group);
+	wpa_printf(MSG_DEBUG, "wpa_group_setkeys: GKeyDoneStations=%d",
+		   group->GKeyDoneStations);
+}
+
+
+static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth,
+				       struct wpa_group *group)
+{
+	struct wpa_auth_config *conf = &wpa_auth->conf;
+	int ret = 0;
+
+	if (wpa_auth_set_key(wpa_auth, group->vlan_id,
+			     wpa_cipher_to_alg(conf->wpa_group),
+			     broadcast_ether_addr, group->GN,
+			     group->GTK[group->GN - 1], group->GTK_len,
+			     KEY_FLAG_GROUP_TX_DEFAULT) < 0)
+		ret = -1;
+
+	if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+		enum wpa_alg alg;
+		size_t len;
+
+		alg = wpa_cipher_to_alg(conf->group_mgmt_cipher);
+		len = wpa_cipher_key_len(conf->group_mgmt_cipher);
+
+		if (ret == 0 &&
+		    wpa_auth_set_key(wpa_auth, group->vlan_id, alg,
+				     broadcast_ether_addr, group->GN_igtk,
+				     group->IGTK[group->GN_igtk - 4], len,
+				     KEY_FLAG_GROUP_TX_DEFAULT) < 0)
+			ret = -1;
+
+		if (ret == 0 && conf->beacon_prot &&
+		    wpa_auth_set_key(wpa_auth, group->vlan_id, alg,
+				     broadcast_ether_addr, group->GN_bigtk,
+				     group->BIGTK[group->GN_bigtk - 6], len,
+				     KEY_FLAG_GROUP_TX_DEFAULT) < 0)
+			ret = -1;
+	}
+
+	return ret;
+}
+
+
+static int wpa_group_disconnect_cb(struct wpa_state_machine *sm, void *ctx)
+{
+	if (sm->group == ctx) {
+		wpa_printf(MSG_DEBUG, "WPA: Mark STA " MACSTR
+			   " for disconnection due to fatal failure",
+			   MAC2STR(wpa_auth_get_spa(sm)));
+		sm->Disconnect = true;
+	}
+
+	return 0;
+}
+
+
+static void wpa_group_fatal_failure(struct wpa_authenticator *wpa_auth,
+				    struct wpa_group *group)
+{
+	wpa_printf(MSG_DEBUG,
+		   "WPA: group state machine entering state FATAL_FAILURE");
+	group->changed = true;
+	group->wpa_group_state = WPA_GROUP_FATAL_FAILURE;
+	wpa_auth_for_each_sta(wpa_auth, wpa_group_disconnect_cb, group);
+}
+
+
+static int wpa_group_setkeysdone(struct wpa_authenticator *wpa_auth,
+				 struct wpa_group *group)
+{
+	wpa_printf(MSG_DEBUG,
+		   "WPA: group state machine entering state SETKEYSDONE (VLAN-ID %d)",
+		   group->vlan_id);
+	group->changed = true;
+	group->wpa_group_state = WPA_GROUP_SETKEYSDONE;
+
+	if (wpa_group_config_group_keys(wpa_auth, group) < 0) {
+		wpa_group_fatal_failure(wpa_auth, group);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth,
+			      struct wpa_group *group)
+{
+	if (group->GInit) {
+		wpa_group_gtk_init(wpa_auth, group);
+	} else if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE) {
+		/* Do not allow group operations */
+	} else if (group->wpa_group_state == WPA_GROUP_GTK_INIT &&
+		   group->GTKAuthenticator) {
+		wpa_group_setkeysdone(wpa_auth, group);
+	} else if (group->wpa_group_state == WPA_GROUP_SETKEYSDONE &&
+		   group->GTKReKey) {
+		wpa_group_setkeys(wpa_auth, group);
+	} else if (group->wpa_group_state == WPA_GROUP_SETKEYS) {
+		if (group->GKeyDoneStations == 0)
+			wpa_group_setkeysdone(wpa_auth, group);
+		else if (group->GTKReKey)
+			wpa_group_setkeys(wpa_auth, group);
+	}
+}
+
+
+static int wpa_sm_step(struct wpa_state_machine *sm)
+{
+	if (!sm)
+		return 0;
+
+	if (sm->in_step_loop) {
+		/* This should not happen, but if it does, make sure we do not
+		 * end up freeing the state machine too early by exiting the
+		 * recursive call. */
+		wpa_printf(MSG_ERROR, "WPA: wpa_sm_step() called recursively");
+		return 0;
+	}
+
+	sm->in_step_loop = 1;
+	do {
+		if (sm->pending_deinit)
+			break;
+
+		sm->changed = false;
+		sm->wpa_auth->group->changed = false;
+
+		SM_STEP_RUN(WPA_PTK);
+		if (sm->pending_deinit)
+			break;
+		SM_STEP_RUN(WPA_PTK_GROUP);
+		if (sm->pending_deinit)
+			break;
+		wpa_group_sm_step(sm->wpa_auth, sm->group);
+	} while (sm->changed || sm->wpa_auth->group->changed);
+	sm->in_step_loop = 0;
+
+	if (sm->pending_deinit) {
+		wpa_printf(MSG_DEBUG,
+			   "WPA: Completing pending STA state machine deinit for "
+			   MACSTR, MAC2STR(wpa_auth_get_spa(sm)));
+		wpa_free_sta_sm(sm);
+		return 1;
+	}
+	return 0;
+}
+
+
+static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_state_machine *sm = eloop_ctx;
+	wpa_sm_step(sm);
+}
+
+
+void wpa_auth_sm_notify(struct wpa_state_machine *sm)
+{
+	if (!sm)
+		return;
+	eloop_register_timeout(0, 0, wpa_sm_call_step, sm, NULL);
+}
+
+
+void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth)
+{
+	int tmp, i;
+	struct wpa_group *group;
+
+	if (!wpa_auth)
+		return;
+
+	group = wpa_auth->group;
+
+	for (i = 0; i < 2; i++) {
+		tmp = group->GM;
+		group->GM = group->GN;
+		group->GN = tmp;
+		tmp = group->GM_igtk;
+		group->GM_igtk = group->GN_igtk;
+		group->GN_igtk = tmp;
+		tmp = group->GM_bigtk;
+		group->GM_bigtk = group->GN_bigtk;
+		group->GN_bigtk = tmp;
+		wpa_gtk_update(wpa_auth, group);
+		wpa_group_config_group_keys(wpa_auth, group);
+	}
+}
+
+
+static const char * wpa_bool_txt(int val)
+{
+	return val ? "TRUE" : "FALSE";
+}
+
+#ifdef CONFIG_CTRL_IFACE_MIB
+
+#define RSN_SUITE "%02x-%02x-%02x-%d"
+#define RSN_SUITE_ARG(s) \
+((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff
+
+int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen)
+{
+	struct wpa_auth_config *conf;
+	int len = 0, ret;
+	char pmkid_txt[PMKID_LEN * 2 + 1];
+#ifdef CONFIG_RSN_PREAUTH
+	const int preauth = 1;
+#else /* CONFIG_RSN_PREAUTH */
+	const int preauth = 0;
+#endif /* CONFIG_RSN_PREAUTH */
+
+	if (!wpa_auth)
+		return len;
+	conf = &wpa_auth->conf;
+
+	ret = os_snprintf(buf + len, buflen - len,
+			  "dot11RSNAOptionImplemented=TRUE\n"
+			  "dot11RSNAPreauthenticationImplemented=%s\n"
+			  "dot11RSNAEnabled=%s\n"
+			  "dot11RSNAPreauthenticationEnabled=%s\n",
+			  wpa_bool_txt(preauth),
+			  wpa_bool_txt(conf->wpa & WPA_PROTO_RSN),
+			  wpa_bool_txt(conf->rsn_preauth));
+	if (os_snprintf_error(buflen - len, ret))
+		return len;
+	len += ret;
+
+	wpa_snprintf_hex(pmkid_txt, sizeof(pmkid_txt),
+			 wpa_auth->dot11RSNAPMKIDUsed, PMKID_LEN);
+
+	ret = os_snprintf(
+		buf + len, buflen - len,
+		"dot11RSNAConfigVersion=%u\n"
+		"dot11RSNAConfigPairwiseKeysSupported=9999\n"
+		/* FIX: dot11RSNAConfigGroupCipher */
+		/* FIX: dot11RSNAConfigGroupRekeyMethod */
+		/* FIX: dot11RSNAConfigGroupRekeyTime */
+		/* FIX: dot11RSNAConfigGroupRekeyPackets */
+		"dot11RSNAConfigGroupRekeyStrict=%u\n"
+		"dot11RSNAConfigGroupUpdateCount=%u\n"
+		"dot11RSNAConfigPairwiseUpdateCount=%u\n"
+		"dot11RSNAConfigGroupCipherSize=%u\n"
+		"dot11RSNAConfigPMKLifetime=%u\n"
+		"dot11RSNAConfigPMKReauthThreshold=%u\n"
+		"dot11RSNAConfigNumberOfPTKSAReplayCounters=0\n"
+		"dot11RSNAConfigSATimeout=%u\n"
+		"dot11RSNAAuthenticationSuiteSelected=" RSN_SUITE "\n"
+		"dot11RSNAPairwiseCipherSelected=" RSN_SUITE "\n"
+		"dot11RSNAGroupCipherSelected=" RSN_SUITE "\n"
+		"dot11RSNAPMKIDUsed=%s\n"
+		"dot11RSNAAuthenticationSuiteRequested=" RSN_SUITE "\n"
+		"dot11RSNAPairwiseCipherRequested=" RSN_SUITE "\n"
+		"dot11RSNAGroupCipherRequested=" RSN_SUITE "\n"
+		"dot11RSNATKIPCounterMeasuresInvoked=%u\n"
+		"dot11RSNA4WayHandshakeFailures=%u\n"
+		"dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n",
+		RSN_VERSION,
+		!!conf->wpa_strict_rekey,
+		conf->wpa_group_update_count,
+		conf->wpa_pairwise_update_count,
+		wpa_cipher_key_len(conf->wpa_group) * 8,
+		dot11RSNAConfigPMKLifetime,
+		dot11RSNAConfigPMKReauthThreshold,
+		dot11RSNAConfigSATimeout,
+		RSN_SUITE_ARG(wpa_auth->dot11RSNAAuthenticationSuiteSelected),
+		RSN_SUITE_ARG(wpa_auth->dot11RSNAPairwiseCipherSelected),
+		RSN_SUITE_ARG(wpa_auth->dot11RSNAGroupCipherSelected),
+		pmkid_txt,
+		RSN_SUITE_ARG(wpa_auth->dot11RSNAAuthenticationSuiteRequested),
+		RSN_SUITE_ARG(wpa_auth->dot11RSNAPairwiseCipherRequested),
+		RSN_SUITE_ARG(wpa_auth->dot11RSNAGroupCipherRequested),
+		wpa_auth->dot11RSNATKIPCounterMeasuresInvoked,
+		wpa_auth->dot11RSNA4WayHandshakeFailures);
+	if (os_snprintf_error(buflen - len, ret))
+		return len;
+	len += ret;
+
+	/* TODO: dot11RSNAConfigPairwiseCiphersTable */
+	/* TODO: dot11RSNAConfigAuthenticationSuitesTable */
+
+	/* Private MIB */
+	ret = os_snprintf(buf + len, buflen - len, "hostapdWPAGroupState=%d\n",
+			  wpa_auth->group->wpa_group_state);
+	if (os_snprintf_error(buflen - len, ret))
+		return len;
+	len += ret;
+
+	return len;
+}
+
+
+int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen)
+{
+	int len = 0, ret;
+	u32 pairwise = 0;
+
+	if (!sm)
+		return 0;
+
+	/* TODO: FF-FF-FF-FF-FF-FF entry for broadcast/multicast stats */
+
+	/* dot11RSNAStatsEntry */
+
+	pairwise = wpa_cipher_to_suite(sm->wpa == WPA_VERSION_WPA2 ?
+				       WPA_PROTO_RSN : WPA_PROTO_WPA,
+				       sm->pairwise);
+	if (pairwise == 0)
+		return 0;
+
+	ret = os_snprintf(
+		buf + len, buflen - len,
+		/* TODO: dot11RSNAStatsIndex */
+		"dot11RSNAStatsSTAAddress=" MACSTR "\n"
+		"dot11RSNAStatsVersion=1\n"
+		"dot11RSNAStatsSelectedPairwiseCipher=" RSN_SUITE "\n"
+		/* TODO: dot11RSNAStatsTKIPICVErrors */
+		"dot11RSNAStatsTKIPLocalMICFailures=%u\n"
+		"dot11RSNAStatsTKIPRemoteMICFailures=%u\n"
+		/* TODO: dot11RSNAStatsCCMPReplays */
+		/* TODO: dot11RSNAStatsCCMPDecryptErrors */
+		/* TODO: dot11RSNAStatsTKIPReplays */,
+		MAC2STR(sm->addr),
+		RSN_SUITE_ARG(pairwise),
+		sm->dot11RSNAStatsTKIPLocalMICFailures,
+		sm->dot11RSNAStatsTKIPRemoteMICFailures);
+	if (os_snprintf_error(buflen - len, ret))
+		return len;
+	len += ret;
+
+	/* Private MIB */
+	ret = os_snprintf(buf + len, buflen - len,
+			  "wpa=%d\n"
+			  "AKMSuiteSelector=" RSN_SUITE "\n"
+			  "hostapdWPAPTKState=%d\n"
+			  "hostapdWPAPTKGroupState=%d\n"
+			  "hostapdMFPR=%d\n",
+			  sm->wpa,
+			  RSN_SUITE_ARG(wpa_akm_to_suite(sm->wpa_key_mgmt)),
+			  sm->wpa_ptk_state,
+			  sm->wpa_ptk_group_state,
+			  sm->mfpr);
+	if (os_snprintf_error(buflen - len, ret))
+		return len;
+	len += ret;
+
+	return len;
+}
+#endif
+
+void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth)
+{
+	if (wpa_auth)
+		wpa_auth->dot11RSNATKIPCounterMeasuresInvoked++;
+}
+
+
+int wpa_auth_pairwise_set(struct wpa_state_machine *sm)
+{
+	return sm && sm->pairwise_set;
+}
+
+
+int wpa_auth_get_pairwise(struct wpa_state_machine *sm)
+{
+	return sm->pairwise;
+}
+
+
+const u8 * wpa_auth_get_pmk(struct wpa_state_machine *sm, int *len)
+{
+	if (!sm)
+		return NULL;
+	*len = sm->pmk_len;
+	return sm->PMK;
+}
+
+
+const u8 * wpa_auth_get_dpp_pkhash(struct wpa_state_machine *sm)
+{
+	if (!sm || !sm->pmksa)
+		return NULL;
+	return sm->pmksa->dpp_pkhash;
+}
+
+
+int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm)
+{
+	if (!sm)
+		return -1;
+	return sm->wpa_key_mgmt;
+}
+
+
+int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm)
+{
+	if (!sm)
+		return 0;
+	return sm->wpa;
+}
+
+
+int wpa_auth_sta_ft_tk_already_set(struct wpa_state_machine *sm)
+{
+	if (!sm || !wpa_key_mgmt_ft(sm->wpa_key_mgmt))
+		return 0;
+	return sm->tk_already_set;
+}
+
+
+int wpa_auth_sta_fils_tk_already_set(struct wpa_state_machine *sm)
+{
+	if (!sm || !wpa_key_mgmt_fils(sm->wpa_key_mgmt))
+		return 0;
+	return sm->tk_already_set;
+}
+
+
+int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm,
+			     struct rsn_pmksa_cache_entry *entry)
+{
+	if (!sm || sm->pmksa != entry)
+		return -1;
+	sm->pmksa = NULL;
+	return 0;
+}
+
+
+struct rsn_pmksa_cache_entry *
+wpa_auth_sta_get_pmksa(struct wpa_state_machine *sm)
+{
+	return sm ? sm->pmksa : NULL;
+}
+
+
+void wpa_auth_sta_local_mic_failure_report(struct wpa_state_machine *sm)
+{
+	if (sm)
+		sm->dot11RSNAStatsTKIPLocalMICFailures++;
+}
+
+
+const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth, size_t *len)
+{
+	if (!wpa_auth)
+		return NULL;
+	*len = wpa_auth->wpa_ie_len;
+	return wpa_auth->wpa_ie;
+}
+
+
+int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk,
+		       unsigned int pmk_len,
+		       int session_timeout, struct eapol_state_machine *eapol)
+{
+	if (!sm || sm->wpa != WPA_VERSION_WPA2 ||
+	    sm->wpa_auth->conf.disable_pmksa_caching)
+		return -1;
+
+#ifdef CONFIG_IEEE80211R_AP
+	if (pmk_len >= 2 * PMK_LEN && wpa_key_mgmt_ft(sm->wpa_key_mgmt) &&
+	    wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) &&
+	    !wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) {
+		/* Cache MPMK/XXKey instead of initial part from MSK */
+		pmk = pmk + PMK_LEN;
+		pmk_len = PMK_LEN;
+	} else
+#endif /* CONFIG_IEEE80211R_AP */
+	if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) {
+		if (pmk_len > PMK_LEN_SUITE_B_192)
+			pmk_len = PMK_LEN_SUITE_B_192;
+	} else if (pmk_len > PMK_LEN) {
+		pmk_len = PMK_LEN;
+	}
+
+	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,
+				 wpa_auth_get_aa(sm),
+				 wpa_auth_get_spa(sm), session_timeout,
+				 eapol, sm->wpa_key_mgmt))
+		return 0;
+
+	return -1;
+}
+
+
+int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth,
+			       const u8 *pmk, size_t len, const u8 *sta_addr,
+			       int session_timeout,
+			       struct eapol_state_machine *eapol)
+{
+	if (!wpa_auth)
+		return -1;
+
+	wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK from preauth", pmk, len);
+	if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len, NULL,
+				 NULL, 0,
+				 wpa_auth->addr,
+				 sta_addr, session_timeout, eapol,
+				 WPA_KEY_MGMT_IEEE8021X))
+		return 0;
+
+	return -1;
+}
+
+
+int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
+			   const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+			   int akmp)
+{
+	if (wpa_auth->conf.disable_pmksa_caching)
+		return -1;
+
+	wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK from SAE", pmk, pmk_len);
+	if (!akmp)
+		akmp = WPA_KEY_MGMT_SAE;
+	if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, pmk_len, pmkid,
+				 NULL, 0, wpa_auth->addr, addr, 0, NULL, akmp))
+		return 0;
+
+	return -1;
+}
+
+
+void wpa_auth_add_sae_pmkid(struct wpa_state_machine *sm, const u8 *pmkid)
+{
+	os_memcpy(sm->pmkid, pmkid, PMKID_LEN);
+	sm->pmkid_set = 1;
+}
+
+
+int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr,
+			const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+			int session_timeout, int akmp)
+{
+	if (!wpa_auth || wpa_auth->conf.disable_pmksa_caching)
+		return -1;
+
+	wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK (2)", pmk, PMK_LEN);
+	if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, pmk_len, pmkid,
+				 NULL, 0, wpa_auth->addr, addr, session_timeout,
+				 NULL, akmp))
+		return 0;
+
+	return -1;
+}
+
+
+int wpa_auth_pmksa_add3(struct wpa_authenticator *wpa_auth, const u8 *addr,
+			const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+			int session_timeout, int akmp, const u8 *dpp_pkhash)
+{
+	struct rsn_pmksa_cache_entry *entry;
+
+	if (wpa_auth->conf.disable_pmksa_caching)
+		return -1;
+
+	wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK (3)", pmk, PMK_LEN);
+	entry = pmksa_cache_auth_add(wpa_auth->pmksa, pmk, pmk_len, pmkid,
+				 NULL, 0, wpa_auth->addr, addr, session_timeout,
+				 NULL, akmp);
+	if (!entry)
+		return -1;
+
+	if (dpp_pkhash)
+		entry->dpp_pkhash = os_memdup(dpp_pkhash, SHA256_MAC_LEN);
+
+	return 0;
+}
+
+
+void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
+			   const u8 *sta_addr)
+{
+	struct rsn_pmksa_cache_entry *pmksa;
+
+	if (!wpa_auth || !wpa_auth->pmksa)
+		return;
+	pmksa = pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, NULL);
+	if (pmksa) {
+		wpa_printf(MSG_DEBUG, "WPA: Remove PMKSA cache entry for "
+			   MACSTR " based on request", MAC2STR(sta_addr));
+		pmksa_cache_free_entry(wpa_auth->pmksa, pmksa);
+	}
+}
+
+
+int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf,
+			size_t len)
+{
+	if (!wpa_auth || !wpa_auth->pmksa)
+		return 0;
+	return pmksa_cache_auth_list(wpa_auth->pmksa, buf, len);
+}
+
+
+void wpa_auth_pmksa_flush(struct wpa_authenticator *wpa_auth)
+{
+	if (wpa_auth && wpa_auth->pmksa)
+		pmksa_cache_auth_flush(wpa_auth->pmksa);
+}
+
+
+#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
+#ifdef CONFIG_MESH
+
+int wpa_auth_pmksa_list_mesh(struct wpa_authenticator *wpa_auth, const u8 *addr,
+			     char *buf, size_t len)
+{
+	if (!wpa_auth || !wpa_auth->pmksa)
+		return 0;
+
+	return pmksa_cache_auth_list_mesh(wpa_auth->pmksa, addr, buf, len);
+}
+
+
+struct rsn_pmksa_cache_entry *
+wpa_auth_pmksa_create_entry(const u8 *aa, const u8 *spa, const u8 *pmk,
+			    size_t pmk_len, int akmp,
+			    const u8 *pmkid, int expiration)
+{
+	struct rsn_pmksa_cache_entry *entry;
+	struct os_reltime now;
+
+	entry = pmksa_cache_auth_create_entry(pmk, pmk_len, pmkid, NULL, 0, aa,
+					      spa, 0, NULL, akmp);
+	if (!entry)
+		return NULL;
+
+	os_get_reltime(&now);
+	entry->expiration = now.sec + expiration;
+	return entry;
+}
+
+
+int wpa_auth_pmksa_add_entry(struct wpa_authenticator *wpa_auth,
+			     struct rsn_pmksa_cache_entry *entry)
+{
+	int ret;
+
+	if (!wpa_auth || !wpa_auth->pmksa)
+		return -1;
+
+	ret = pmksa_cache_auth_add_entry(wpa_auth->pmksa, entry);
+	if (ret < 0)
+		wpa_printf(MSG_DEBUG,
+			   "RSN: Failed to store external PMKSA cache for "
+			   MACSTR, MAC2STR(entry->spa));
+
+	return ret;
+}
+
+#endif /* CONFIG_MESH */
+#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
+
+
+struct rsn_pmksa_cache *
+wpa_auth_get_pmksa_cache(struct wpa_authenticator *wpa_auth)
+{
+	if (!wpa_auth || !wpa_auth->pmksa)
+		return NULL;
+	return wpa_auth->pmksa;
+}
+
+
+struct rsn_pmksa_cache_entry *
+wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
+		   const u8 *pmkid)
+{
+	if (!wpa_auth || !wpa_auth->pmksa)
+		return NULL;
+	return pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, pmkid);
+}
+
+
+void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa,
+			      struct wpa_state_machine *sm,
+			      struct wpa_authenticator *wpa_auth,
+			      u8 *pmkid, u8 *pmk)
+{
+	if (!sm)
+		return;
+
+	sm->pmksa = pmksa;
+	os_memcpy(pmk, pmksa->pmk, PMK_LEN);
+	os_memcpy(pmkid, pmksa->pmkid, PMKID_LEN);
+	os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmksa->pmkid, PMKID_LEN);
+}
+
+
+/*
+ * Remove and free the group from wpa_authenticator. This is triggered by a
+ * callback to make sure nobody is currently iterating the group list while it
+ * gets modified.
+ */
+static void wpa_group_free(struct wpa_authenticator *wpa_auth,
+			   struct wpa_group *group)
+{
+	struct wpa_group *prev = wpa_auth->group;
+
+	wpa_printf(MSG_DEBUG, "WPA: Remove group state machine for VLAN-ID %d",
+		   group->vlan_id);
+
+	while (prev) {
+		if (prev->next == group) {
+			/* This never frees the special first group as needed */
+			prev->next = group->next;
+			os_free(group);
+			break;
+		}
+		prev = prev->next;
+	}
+
+}
+
+
+/* Increase the reference counter for group */
+static void wpa_group_get(struct wpa_authenticator *wpa_auth,
+			  struct wpa_group *group)
+{
+	/* Skip the special first group */
+	if (wpa_auth->group == group)
+		return;
+
+	group->references++;
+}
+
+
+/* Decrease the reference counter and maybe free the group */
+static void wpa_group_put(struct wpa_authenticator *wpa_auth,
+			  struct wpa_group *group)
+{
+	/* Skip the special first group */
+	if (wpa_auth->group == group)
+		return;
+
+	group->references--;
+	if (group->references)
+		return;
+	wpa_group_free(wpa_auth, group);
+}
+
+
+/*
+ * Add a group that has its references counter set to zero. Caller needs to
+ * call wpa_group_get() on the return value to mark the entry in use.
+ */
+static struct wpa_group *
+wpa_auth_add_group(struct wpa_authenticator *wpa_auth, int vlan_id)
+{
+	struct wpa_group *group;
+
+	if (!wpa_auth || !wpa_auth->group)
+		return NULL;
+
+	wpa_printf(MSG_DEBUG, "WPA: Add group state machine for VLAN-ID %d",
+		   vlan_id);
+	group = wpa_group_init(wpa_auth, vlan_id, 0);
+	if (!group)
+		return NULL;
+
+	group->next = wpa_auth->group->next;
+	wpa_auth->group->next = group;
+
+	return group;
+}
+
+
+/*
+ * Enforce that the group state machine for the VLAN is running, increase
+ * reference counter as interface is up. References might have been increased
+ * even if a negative value is returned.
+ * Returns: -1 on error (group missing, group already failed); otherwise, 0
+ */
+int wpa_auth_ensure_group(struct wpa_authenticator *wpa_auth, int vlan_id)
+{
+	struct wpa_group *group;
+
+	if (!wpa_auth)
+		return 0;
+
+	group = wpa_auth->group;
+	while (group) {
+		if (group->vlan_id == vlan_id)
+			break;
+		group = group->next;
+	}
+
+	if (!group) {
+		group = wpa_auth_add_group(wpa_auth, vlan_id);
+		if (!group)
+			return -1;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "WPA: Ensure group state machine running for VLAN ID %d",
+		   vlan_id);
+
+	wpa_group_get(wpa_auth, group);
+	group->num_setup_iface++;
+
+	if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
+		return -1;
+
+	return 0;
+}
+
+
+/*
+ * Decrease reference counter, expected to be zero afterwards.
+ * returns: -1 on error (group not found, group in fail state)
+ *          -2 if wpa_group is still referenced
+ *           0 else
+ */
+int wpa_auth_release_group(struct wpa_authenticator *wpa_auth, int vlan_id)
+{
+	struct wpa_group *group;
+	int ret = 0;
+
+	if (!wpa_auth)
+		return 0;
+
+	group = wpa_auth->group;
+	while (group) {
+		if (group->vlan_id == vlan_id)
+			break;
+		group = group->next;
+	}
+
+	if (!group)
+		return -1;
+
+	wpa_printf(MSG_DEBUG,
+		   "WPA: Try stopping group state machine for VLAN ID %d",
+		   vlan_id);
+
+	if (group->num_setup_iface <= 0) {
+		wpa_printf(MSG_ERROR,
+			   "WPA: wpa_auth_release_group called more often than wpa_auth_ensure_group for VLAN ID %d, skipping.",
+			   vlan_id);
+		return -1;
+	}
+	group->num_setup_iface--;
+
+	if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
+		ret = -1;
+
+	if (group->references > 1) {
+		wpa_printf(MSG_DEBUG,
+			   "WPA: Cannot stop group state machine for VLAN ID %d as references are still hold",
+			   vlan_id);
+		ret = -2;
+	}
+
+	wpa_group_put(wpa_auth, group);
+
+	return ret;
+}
+
+
+int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id)
+{
+	struct wpa_group *group;
+
+	if (!sm || !sm->wpa_auth)
+		return 0;
+
+	group = sm->wpa_auth->group;
+	while (group) {
+		if (group->vlan_id == vlan_id)
+			break;
+		group = group->next;
+	}
+
+	if (!group) {
+		group = wpa_auth_add_group(sm->wpa_auth, vlan_id);
+		if (!group)
+			return -1;
+	}
+
+	if (sm->group == group)
+		return 0;
+
+	if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "WPA: Moving STA " MACSTR
+		   " to use group state machine for VLAN ID %d",
+		   MAC2STR(wpa_auth_get_spa(sm)), vlan_id);
+
+	wpa_group_get(sm->wpa_auth, group);
+	wpa_group_put(sm->wpa_auth, sm->group);
+	sm->group = group;
+
+	return 0;
+}
+
+
+void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth,
+				  struct wpa_state_machine *sm, int ack)
+{
+	if (!wpa_auth || !sm)
+		return;
+	wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key TX status for STA " MACSTR
+		   " ack=%d", MAC2STR(wpa_auth_get_spa(sm)), ack);
+	if (sm->pending_1_of_4_timeout && ack) {
+		/*
+		 * Some deployed supplicant implementations update their SNonce
+		 * for each EAPOL-Key 2/4 message even within the same 4-way
+		 * handshake and then fail to use the first SNonce when
+		 * deriving the PTK. This results in unsuccessful 4-way
+		 * handshake whenever the relatively short initial timeout is
+		 * reached and EAPOL-Key 1/4 is retransmitted. Try to work
+		 * around this by increasing the timeout now that we know that
+		 * the station has received the frame.
+		 */
+		int timeout_ms = eapol_key_timeout_subseq;
+		wpa_printf(MSG_DEBUG,
+			   "WPA: Increase initial EAPOL-Key 1/4 timeout by %u ms because of acknowledged frame",
+			   timeout_ms);
+		eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm);
+		eloop_register_timeout(timeout_ms / 1000,
+				       (timeout_ms % 1000) * 1000,
+				       wpa_send_eapol_timeout, wpa_auth, sm);
+	}
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (sm->eapol_status_cb) {
+		sm->eapol_status_cb(sm->eapol_status_cb_ctx1,
+				    sm->eapol_status_cb_ctx2);
+		sm->eapol_status_cb = NULL;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+}
+
+
+int wpa_auth_uses_sae(struct wpa_state_machine *sm)
+{
+	if (!sm)
+		return 0;
+	return wpa_key_mgmt_sae(sm->wpa_key_mgmt);
+}
+
+
+int wpa_auth_uses_ft_sae(struct wpa_state_machine *sm)
+{
+	if (!sm)
+		return 0;
+	return sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE ||
+		sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY;
+}
+
+
+#ifdef CONFIG_P2P
+int wpa_auth_get_ip_addr(struct wpa_state_machine *sm, u8 *addr)
+{
+	if (!sm || WPA_GET_BE32(sm->ip_addr) == 0)
+		return -1;
+	os_memcpy(addr, sm->ip_addr, 4);
+	return 0;
+}
+#endif /* CONFIG_P2P */
+
+
+int wpa_auth_radius_das_disconnect_pmksa(struct wpa_authenticator *wpa_auth,
+					 struct radius_das_attrs *attr)
+{
+	return pmksa_cache_auth_radius_das_disconnect(wpa_auth->pmksa, attr);
+}
+
+
+void wpa_auth_reconfig_group_keys(struct wpa_authenticator *wpa_auth)
+{
+	struct wpa_group *group;
+
+	if (!wpa_auth)
+		return;
+	for (group = wpa_auth->group; group; group = group->next)
+		wpa_group_config_group_keys(wpa_auth, group);
+}
+
+
+#ifdef CONFIG_FILS
+
+struct wpa_auth_fils_iter_data {
+	struct wpa_authenticator *auth;
+	const u8 *cache_id;
+	struct rsn_pmksa_cache_entry *pmksa;
+	const u8 *spa;
+	const u8 *pmkid;
+};
+
+
+static int wpa_auth_fils_iter(struct wpa_authenticator *a, void *ctx)
+{
+	struct wpa_auth_fils_iter_data *data = ctx;
+
+	if (a == data->auth || !a->conf.fils_cache_id_set ||
+	    os_memcmp(a->conf.fils_cache_id, data->cache_id,
+		      FILS_CACHE_ID_LEN) != 0)
+		return 0;
+	data->pmksa = pmksa_cache_auth_get(a->pmksa, data->spa, data->pmkid);
+	return data->pmksa != NULL;
+}
+
+
+struct rsn_pmksa_cache_entry *
+wpa_auth_pmksa_get_fils_cache_id(struct wpa_authenticator *wpa_auth,
+				 const u8 *sta_addr, const u8 *pmkid)
+{
+	struct wpa_auth_fils_iter_data idata;
+
+	if (!wpa_auth->conf.fils_cache_id_set)
+		return NULL;
+	idata.auth = wpa_auth;
+	idata.cache_id = wpa_auth->conf.fils_cache_id;
+	idata.pmksa = NULL;
+	idata.spa = sta_addr;
+	idata.pmkid = pmkid;
+	wpa_auth_for_each_auth(wpa_auth, wpa_auth_fils_iter, &idata);
+	return idata.pmksa;
+}
+
+
+#ifdef CONFIG_IEEE80211R_AP
+int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth,
+		       struct wpa_state_machine *sm,
+		       u8 *buf, size_t len)
+{
+	struct wpa_auth_config *conf = &wpa_auth->conf;
+
+	return wpa_write_ftie(conf, sm->wpa_key_mgmt, sm->xxkey_len,
+			      conf->r0_key_holder, conf->r0_key_holder_len,
+			      NULL, NULL, buf, len, NULL, 0, 0);
+}
+#endif /* CONFIG_IEEE80211R_AP */
+
+
+void wpa_auth_get_fils_aead_params(struct wpa_state_machine *sm,
+				   u8 *fils_anonce, u8 *fils_snonce,
+				   u8 *fils_kek, size_t *fils_kek_len)
+{
+	os_memcpy(fils_anonce, sm->ANonce, WPA_NONCE_LEN);
+	os_memcpy(fils_snonce, sm->SNonce, WPA_NONCE_LEN);
+	os_memcpy(fils_kek, sm->PTK.kek, WPA_KEK_MAX_LEN);
+	*fils_kek_len = sm->PTK.kek_len;
+}
+
+
+void wpa_auth_add_fils_pmk_pmkid(struct wpa_state_machine *sm, const u8 *pmk,
+				 size_t pmk_len, const u8 *pmkid)
+{
+	os_memcpy(sm->PMK, pmk, pmk_len);
+	sm->pmk_len = pmk_len;
+	os_memcpy(sm->pmkid, pmkid, PMKID_LEN);
+	sm->pmkid_set = 1;
+}
+
+#endif /* CONFIG_FILS */
+
+
+void wpa_auth_set_auth_alg(struct wpa_state_machine *sm, u16 auth_alg)
+{
+	if (sm)
+		sm->auth_alg = auth_alg;
+}
+
+
+#ifdef CONFIG_DPP2
+void wpa_auth_set_dpp_z(struct wpa_state_machine *sm, const struct wpabuf *z)
+{
+	if (sm) {
+		wpabuf_clear_free(sm->dpp_z);
+		sm->dpp_z = z ? wpabuf_dup(z) : NULL;
+	}
+}
+#endif /* CONFIG_DPP2 */
+
+
+void wpa_auth_set_transition_disable(struct wpa_authenticator *wpa_auth,
+				     u8 val)
+{
+	if (wpa_auth)
+		wpa_auth->conf.transition_disable = val;
+}
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+
+int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce,
+		       void (*cb)(void *ctx1, void *ctx2),
+		       void *ctx1, void *ctx2)
+{
+	const u8 *anonce = sm->ANonce;
+	u8 anonce_buf[WPA_NONCE_LEN];
+
+	if (change_anonce) {
+		if (random_get_bytes(anonce_buf, WPA_NONCE_LEN))
+			return -1;
+		anonce = anonce_buf;
+	}
+
+	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,
+		       anonce, NULL, 0, 0, 0);
+	return 0;
+}
+
+
+int wpa_auth_resend_m3(struct wpa_state_machine *sm,
+		       void (*cb)(void *ctx1, void *ctx2),
+		       void *ctx1, void *ctx2)
+{
+	u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos;
+	u8 *opos;
+	size_t gtk_len, kde_len;
+	struct wpa_auth_config *conf = &sm->wpa_auth->conf;
+	struct wpa_group *gsm = sm->group;
+	u8 *wpa_ie;
+	int wpa_ie_len, secure, gtkidx, encr = 0;
+	u8 hdr[2];
+
+	/* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, [MDIE],
+	   GTK[GN], IGTK, [BIGTK], [FTIE], [TIE * 2])
+	 */
+
+	/* Use 0 RSC */
+	os_memset(rsc, 0, WPA_KEY_RSC_LEN);
+	/* If FT is used, wpa_auth->wpa_ie includes both RSNIE and MDIE */
+	wpa_ie = sm->wpa_auth->wpa_ie;
+	wpa_ie_len = sm->wpa_auth->wpa_ie_len;
+	if (sm->wpa == WPA_VERSION_WPA &&
+	    (sm->wpa_auth->conf.wpa & WPA_PROTO_RSN) &&
+	    wpa_ie_len > wpa_ie[1] + 2 && wpa_ie[0] == WLAN_EID_RSN) {
+		/* WPA-only STA, remove RSN IE and possible MDIE */
+		wpa_ie = wpa_ie + wpa_ie[1] + 2;
+		if (wpa_ie[0] == WLAN_EID_RSNX)
+			wpa_ie = wpa_ie + wpa_ie[1] + 2;
+		if (wpa_ie[0] == WLAN_EID_MOBILITY_DOMAIN)
+			wpa_ie = wpa_ie + wpa_ie[1] + 2;
+		wpa_ie_len = wpa_ie[1] + 2;
+	}
+	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 */
+		secure = 1;
+		gtk = gsm->GTK[gsm->GN - 1];
+		gtk_len = gsm->GTK_len;
+		gtkidx = gsm->GN;
+		_rsc = rsc;
+		encr = 1;
+	} else {
+		/* WPA does not include GTK in msg 3/4 */
+		secure = 0;
+		gtk = NULL;
+		gtk_len = 0;
+		_rsc = NULL;
+		if (sm->rx_eapol_key_secure) {
+			/*
+			 * It looks like Windows 7 supplicant tries to use
+			 * Secure bit in msg 2/4 after having reported Michael
+			 * MIC failure and it then rejects the 4-way handshake
+			 * if msg 3/4 does not set Secure bit. Work around this
+			 * by setting the Secure bit here even in the case of
+			 * WPA if the supplicant used it first.
+			 */
+			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;
+		}
+	}
+
+	kde_len = wpa_ie_len + ieee80211w_kde_len(sm) + ocv_oci_len(sm);
+
+	if (sm->use_ext_key_id)
+		kde_len += 2 + RSN_SELECTOR_LEN + 2;
+
+	if (gtk)
+		kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len;
+#ifdef CONFIG_IEEE80211R_AP
+	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+		kde_len += 2 + PMKID_LEN; /* PMKR1Name into RSN IE */
+		kde_len += 300; /* FTIE + 2 * TIE */
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+	kde = os_malloc(kde_len);
+	if (!kde)
+		return -1;
+
+	pos = kde;
+	os_memcpy(pos, wpa_ie, wpa_ie_len);
+	pos += wpa_ie_len;
+#ifdef CONFIG_IEEE80211R_AP
+	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+		int res;
+		size_t elen;
+
+		elen = pos - kde;
+		res = wpa_insert_pmkid(kde, &elen, sm->pmk_r1_name);
+		if (res < 0) {
+			wpa_printf(MSG_ERROR,
+				   "FT: Failed to insert PMKR1Name into RSN IE in EAPOL-Key data");
+			os_free(kde);
+			return -1;
+		}
+		pos -= wpa_ie_len;
+		pos += elen;
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+	hdr[1] = 0;
+
+	if (sm->use_ext_key_id) {
+		hdr[0] = sm->keyidx_active & 0x01;
+		pos = wpa_add_kde(pos, RSN_KEY_DATA_KEYID, hdr, 2, NULL, 0);
+	}
+
+	if (gtk) {
+		hdr[0] = gtkidx & 0x03;
+		pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
+				  gtk, gtk_len);
+	}
+	opos = pos;
+	pos = ieee80211w_kde_add(sm, pos);
+	if (pos - opos >= 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN) {
+		/* skip KDE header and keyid */
+		opos += 2 + RSN_SELECTOR_LEN + 2;
+		os_memset(opos, 0, 6); /* clear PN */
+	}
+	if (ocv_oci_add(sm, &pos, conf->oci_freq_override_eapol_m3) < 0) {
+		os_free(kde);
+		return -1;
+	}
+
+#ifdef CONFIG_IEEE80211R_AP
+	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+		int res;
+
+		if (sm->assoc_resp_ftie &&
+		    kde + kde_len - pos >= 2 + sm->assoc_resp_ftie[1]) {
+			os_memcpy(pos, sm->assoc_resp_ftie,
+				  2 + sm->assoc_resp_ftie[1]);
+			res = 2 + sm->assoc_resp_ftie[1];
+		} else {
+			res = wpa_write_ftie(conf, sm->wpa_key_mgmt,
+					     sm->xxkey_len,
+					     conf->r0_key_holder,
+					     conf->r0_key_holder_len,
+					     NULL, NULL, pos,
+					     kde + kde_len - pos,
+					     NULL, 0, 0);
+		}
+		if (res < 0) {
+			wpa_printf(MSG_ERROR,
+				   "FT: Failed to insert FTIE into EAPOL-Key Key Data");
+			os_free(kde);
+			return -1;
+		}
+		pos += res;
+
+		/* TIE[ReassociationDeadline] (TU) */
+		*pos++ = WLAN_EID_TIMEOUT_INTERVAL;
+		*pos++ = 5;
+		*pos++ = WLAN_TIMEOUT_REASSOC_DEADLINE;
+		WPA_PUT_LE32(pos, conf->reassociation_deadline);
+		pos += 4;
+
+		/* TIE[KeyLifetime] (seconds) */
+		*pos++ = WLAN_EID_TIMEOUT_INTERVAL;
+		*pos++ = 5;
+		*pos++ = WLAN_TIMEOUT_KEY_LIFETIME;
+		WPA_PUT_LE32(pos, conf->r0_key_lifetime);
+		pos += 4;
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+
+	wpa_send_eapol(sm->wpa_auth, sm,
+		       (secure ? WPA_KEY_INFO_SECURE : 0) |
+		       (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
+			WPA_KEY_INFO_MIC : 0) |
+		       WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL |
+		       WPA_KEY_INFO_KEY_TYPE,
+		       _rsc, sm->ANonce, kde, pos - kde, 0, encr);
+	bin_clear_free(kde, kde_len);
+	return 0;
+}
+
+
+int wpa_auth_resend_group_m1(struct wpa_state_machine *sm,
+			     void (*cb)(void *ctx1, void *ctx2),
+			     void *ctx1, void *ctx2)
+{
+	u8 rsc[WPA_KEY_RSC_LEN];
+	struct wpa_auth_config *conf = &sm->wpa_auth->conf;
+	struct wpa_group *gsm = sm->group;
+	const u8 *kde;
+	u8 *kde_buf = NULL, *pos, hdr[2];
+	u8 *opos;
+	size_t kde_len;
+	u8 *gtk;
+
+	/* 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, wpa_auth_get_spa(sm), LOGGER_DEBUG,
+			"sending 1/2 msg of Group Key Handshake (TESTING)");
+
+	gtk = gsm->GTK[gsm->GN - 1];
+	if (sm->wpa == WPA_VERSION_WPA2) {
+		kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len +
+			ieee80211w_kde_len(sm) + ocv_oci_len(sm);
+		kde_buf = os_malloc(kde_len);
+		if (!kde_buf)
+			return -1;
+
+		kde = pos = kde_buf;
+		hdr[0] = gsm->GN & 0x03;
+		hdr[1] = 0;
+		pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
+				  gtk, gsm->GTK_len);
+		opos = pos;
+		pos = ieee80211w_kde_add(sm, pos);
+		if (pos - opos >=
+		    2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN) {
+			/* skip KDE header and keyid */
+			opos += 2 + RSN_SELECTOR_LEN + 2;
+			os_memset(opos, 0, 6); /* clear PN */
+		}
+		if (ocv_oci_add(sm, &pos,
+				conf->oci_freq_override_eapol_g1) < 0) {
+			os_free(kde_buf);
+			return -1;
+		}
+		kde_len = pos - kde;
+	} else {
+		kde = gtk;
+		kde_len = gsm->GTK_len;
+	}
+
+	sm->eapol_status_cb = cb;
+	sm->eapol_status_cb_ctx1 = ctx1;
+	sm->eapol_status_cb_ctx2 = ctx2;
+
+	wpa_send_eapol(sm->wpa_auth, sm,
+		       WPA_KEY_INFO_SECURE |
+		       (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
+			WPA_KEY_INFO_MIC : 0) |
+		       WPA_KEY_INFO_ACK |
+		       (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0),
+		       rsc, NULL, kde, kde_len, gsm->GN, 1);
+
+	bin_clear_free(kde_buf, kde_len);
+	return 0;
+}
+
+
+int wpa_auth_rekey_gtk(struct wpa_authenticator *wpa_auth)
+{
+	if (!wpa_auth)
+		return -1;
+	eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
+	return eloop_register_timeout(0, 0, wpa_rekey_gtk, wpa_auth, NULL);
+}
+
+
+int wpa_auth_rekey_ptk(struct wpa_authenticator *wpa_auth,
+		       struct wpa_state_machine *sm)
+{
+	if (!wpa_auth || !sm)
+		return -1;
+	wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "rekeying PTK");
+	wpa_request_new_ptk(sm);
+	wpa_sm_step(sm);
+	return 0;
+}
+
+
+void wpa_auth_set_ft_rsnxe_used(struct wpa_authenticator *wpa_auth, int val)
+{
+	if (wpa_auth)
+		wpa_auth->conf.ft_rsnxe_used = val;
+}
+
+
+void wpa_auth_set_ocv_override_freq(struct wpa_authenticator *wpa_auth,
+				    enum wpa_auth_ocv_override_frame frame,
+				    unsigned int freq)
+{
+	if (!wpa_auth)
+		return;
+	switch (frame) {
+	case WPA_AUTH_OCV_OVERRIDE_EAPOL_M3:
+		wpa_auth->conf.oci_freq_override_eapol_m3 = freq;
+		break;
+	case WPA_AUTH_OCV_OVERRIDE_EAPOL_G1:
+		wpa_auth->conf.oci_freq_override_eapol_g1 = freq;
+		break;
+	case WPA_AUTH_OCV_OVERRIDE_FT_ASSOC:
+		wpa_auth->conf.oci_freq_override_ft_assoc = freq;
+		break;
+	case WPA_AUTH_OCV_OVERRIDE_FILS_ASSOC:
+		wpa_auth->conf.oci_freq_override_fils_assoc = freq;
+		break;
+	}
+}
+
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+void wpa_auth_sta_radius_psk_resp(struct wpa_state_machine *sm, bool success)
+{
+	if (!sm->waiting_radius_psk) {
+		wpa_printf(MSG_DEBUG,
+			   "Ignore RADIUS PSK response for " MACSTR
+			   " that did not wait one",
+			   MAC2STR(sm->addr));
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "RADIUS PSK response for " MACSTR " (%s)",
+		   MAC2STR(sm->addr), success ? "success" : "fail");
+	sm->waiting_radius_psk = 0;
+
+	if (success) {
+		/* Try to process the EAPOL-Key msg 2/4 again */
+		sm->EAPOLKeyReceived = true;
+	} else {
+		sm->Disconnect = true;
+	}
+
+	eloop_register_timeout(0, 0, wpa_sm_call_step, sm, NULL);
+}
+
+
+void wpa_auth_set_ml_info(struct wpa_state_machine *sm, const u8 *mld_addr,
+			  u8 mld_assoc_link_id, struct mld_info *info)
+{
+#ifdef CONFIG_IEEE80211BE
+	struct wpa_auth_ml_rsn_info ml_rsn_info;
+	unsigned int link_id, i;
+
+	if (!info)
+		return;
+
+	os_memset(sm->mld_links, 0, sizeof(sm->mld_links));
+
+	wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
+			"MLD: Initialization");
+
+	os_memcpy(sm->own_mld_addr, mld_addr, ETH_ALEN);
+	os_memcpy(sm->peer_mld_addr, info->common_info.mld_addr, ETH_ALEN);
+
+	sm->mld_assoc_link_id = mld_assoc_link_id;
+
+	os_memset(&ml_rsn_info, 0, sizeof(ml_rsn_info));
+
+	for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+		struct mld_link_info *link = &info->links[link_id];
+		struct mld_link *sm_link = &sm->mld_links[link_id];
+
+		sm_link->valid = link->valid;
+		if (!link->valid)
+			continue;
+
+		os_memcpy(sm_link->peer_addr, link->peer_addr, ETH_ALEN);
+		os_memcpy(sm_link->own_addr, link->local_addr, ETH_ALEN);
+
+		wpa_printf(MSG_DEBUG,
+			   "WPA_AUTH: MLD: id=%u, addr=" MACSTR " peer=" MACSTR,
+			   link_id,
+			   MAC2STR(sm_link->own_addr),
+			   MAC2STR(sm_link->peer_addr));
+
+		if (link_id != mld_assoc_link_id)
+			sm->n_mld_affiliated_links++;
+
+		ml_rsn_info.links[i++].link_id = link_id;
+	}
+
+	ml_rsn_info.n_mld_links = i;
+
+	wpa_auth_get_ml_rsn_info(sm->wpa_auth, &ml_rsn_info);
+
+	for (i = 0; i < ml_rsn_info.n_mld_links; i++) {
+		struct mld_link *sm_link;
+		const u8 *rsn_ies;
+		u8 rsn_ies_len;
+
+		sm_link = &sm->mld_links[ml_rsn_info.links[i].link_id];
+		rsn_ies = ml_rsn_info.links[i].rsn_ies;
+		rsn_ies_len = ml_rsn_info.links[i].rsn_ies_len;
+
+		/* This should not really happen */
+		if (!rsn_ies || rsn_ies_len < 2 || rsn_ies[0] != WLAN_EID_RSN ||
+		    rsn_ies[1] + 2 > rsn_ies_len) {
+			wpa_printf(MSG_INFO, "WPA_AUTH: MLD: Invalid RSNE");
+			continue;
+		}
+
+		sm_link->rsne = rsn_ies;
+		sm_link->rsne_len = rsn_ies[1] + 2;
+
+		if (rsn_ies[1] + 2UL + 2UL < rsn_ies_len &&
+		    rsn_ies[rsn_ies[1] + 2] == WLAN_EID_RSNX) {
+			sm_link->rsnxe = rsn_ies + 2 + rsn_ies[1];
+			sm_link->rsnxe_len = sm_link->rsnxe[1] + 2;
+		}
+	}
+#endif /* CONFIG_IEEE80211BE */
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth.h
new file mode 100644
index 0000000..c076d71
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth.h
@@ -0,0 +1,643 @@
+/*
+ * hostapd - IEEE 802.11i-2004 / WPA Authenticator
+ * Copyright (c) 2004-2022, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPA_AUTH_H
+#define WPA_AUTH_H
+
+#include "common/defs.h"
+#include "common/eapol_common.h"
+#include "common/wpa_common.h"
+#include "common/ieee802_11_defs.h"
+
+struct vlan_description;
+struct mld_info;
+
+#define MAX_OWN_IE_OVERRIDE 256
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+/* IEEE Std 802.11r-2008, 11A.10.3 - Remote request/response frame definition
+ */
+struct ft_rrb_frame {
+	u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */
+	u8 packet_type; /* FT_PACKET_REQUEST/FT_PACKET_RESPONSE */
+	le16 action_length; /* little endian length of action_frame */
+	u8 ap_address[ETH_ALEN];
+	/*
+	 * Followed by action_length bytes of FT Action frame (from Category
+	 * field to the end of Action Frame body.
+	 */
+} STRUCT_PACKED;
+
+#define RSN_REMOTE_FRAME_TYPE_FT_RRB 1
+
+#define FT_PACKET_REQUEST 0
+#define FT_PACKET_RESPONSE 1
+
+/* Vendor-specific types for R0KH-R1KH protocol; not defined in 802.11r. These
+ * use OUI Extended EtherType as the encapsulating format. */
+#define FT_PACKET_R0KH_R1KH_PULL 0x01
+#define FT_PACKET_R0KH_R1KH_RESP 0x02
+#define FT_PACKET_R0KH_R1KH_PUSH 0x03
+#define FT_PACKET_R0KH_R1KH_SEQ_REQ 0x04
+#define FT_PACKET_R0KH_R1KH_SEQ_RESP 0x05
+
+/* packet layout
+ *  IEEE 802 extended OUI ethertype frame header
+ *  u16 authlen (little endian)
+ *  multiple of struct ft_rrb_tlv (authenticated only, length = authlen)
+ *  multiple of struct ft_rrb_tlv (AES-SIV encrypted, AES-SIV needs an extra
+ *                                 blocksize length)
+ *
+ * AES-SIV AAD;
+ *  source MAC address (6)
+ *  authenticated-only TLVs (authlen)
+ *  subtype (1; FT_PACKET_*)
+ */
+
+#define FT_RRB_NONCE_LEN 16
+
+#define FT_RRB_LAST_EMPTY     0 /* placeholder or padding */
+
+#define FT_RRB_SEQ            1 /* struct ft_rrb_seq */
+#define FT_RRB_NONCE          2 /* size FT_RRB_NONCE_LEN */
+#define FT_RRB_TIMESTAMP      3 /* le32 unix seconds */
+
+#define FT_RRB_R0KH_ID        4 /* FT_R0KH_ID_MAX_LEN */
+#define FT_RRB_R1KH_ID        5 /* FT_R1KH_ID_LEN */
+#define FT_RRB_S1KH_ID        6 /* ETH_ALEN */
+
+#define FT_RRB_PMK_R0_NAME    7 /* WPA_PMK_NAME_LEN */
+#define FT_RRB_PMK_R0         8 /* PMK_LEN */
+#define FT_RRB_PMK_R1_NAME    9 /* WPA_PMK_NAME_LEN */
+#define FT_RRB_PMK_R1        10 /* PMK_LEN */
+
+#define FT_RRB_PAIRWISE      11 /* le16 */
+#define FT_RRB_EXPIRES_IN    12 /* le16 seconds */
+
+#define FT_RRB_VLAN_UNTAGGED 13 /* le16 */
+#define FT_RRB_VLAN_TAGGED   14 /* n times le16 */
+
+#define FT_RRB_IDENTITY      15
+#define FT_RRB_RADIUS_CUI    16
+#define FT_RRB_SESSION_TIMEOUT  17 /* le32 seconds */
+
+struct ft_rrb_tlv {
+	le16 type;
+	le16 len;
+	/* followed by data of length len */
+} STRUCT_PACKED;
+
+struct ft_rrb_seq {
+	le32 dom;
+	le32 seq;
+	le32 ts;
+} STRUCT_PACKED;
+
+/* session TLVs:
+ *   required: PMK_R1, PMK_R1_NAME, PAIRWISE
+ *   optional: VLAN_UNTAGGED, VLAN_TAGGED, EXPIRES_IN, IDENTITY, RADIUS_CUI,
+ *		 SESSION_TIMEOUT
+ *
+ * pull frame TLVs:
+ *   auth:
+ *     required: SEQ, NONCE, R0KH_ID, R1KH_ID
+ *   encrypted:
+ *     required: PMK_R0_NAME, S1KH_ID
+ *
+ * response frame TLVs:
+ *   auth:
+ *     required: SEQ, NONCE, R0KH_ID, R1KH_ID
+ *   encrypted:
+ *     required: S1KH_ID
+ *     optional: session TLVs
+ *
+ * push frame TLVs:
+ *   auth:
+ *     required: SEQ, R0KH_ID, R1KH_ID
+ *   encrypted:
+ *     required: S1KH_ID, PMK_R0_NAME, session TLVs
+ *
+ * sequence number request frame TLVs:
+ *   auth:
+ *     required: R0KH_ID, R1KH_ID, NONCE
+ *
+ * sequence number response frame TLVs:
+ *   auth:
+ *     required: SEQ, NONCE, R0KH_ID, R1KH_ID
+ */
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+/* per STA state machine data */
+
+struct wpa_authenticator;
+struct wpa_state_machine;
+struct rsn_pmksa_cache_entry;
+struct eapol_state_machine;
+struct ft_remote_seq;
+struct wpa_channel_info;
+
+
+struct ft_remote_r0kh {
+	struct ft_remote_r0kh *next;
+	u8 addr[ETH_ALEN];
+	u8 id[FT_R0KH_ID_MAX_LEN];
+	size_t id_len;
+	u8 key[32];
+	struct ft_remote_seq *seq;
+};
+
+
+struct ft_remote_r1kh {
+	struct ft_remote_r1kh *next;
+	u8 addr[ETH_ALEN];
+	u8 id[FT_R1KH_ID_LEN];
+	u8 key[32];
+	struct ft_remote_seq *seq;
+};
+
+
+struct wpa_auth_config {
+	void *msg_ctx;
+	int wpa;
+	int extended_key_id;
+	int wpa_key_mgmt;
+	int wpa_pairwise;
+	int wpa_group;
+	int wpa_group_rekey;
+	int wpa_strict_rekey;
+	int wpa_gmk_rekey;
+	int wpa_ptk_rekey;
+	int wpa_deny_ptk0_rekey;
+	u32 wpa_group_update_count;
+	u32 wpa_pairwise_update_count;
+	int wpa_disable_eapol_key_retries;
+	int rsn_pairwise;
+	int rsn_preauth;
+	int eapol_version;
+	int wmm_enabled;
+	int wmm_uapsd;
+	int disable_pmksa_caching;
+	int okc;
+	int tx_status;
+	enum mfp_options ieee80211w;
+	int beacon_prot;
+	int group_mgmt_cipher;
+	int sae_require_mfp;
+#ifdef CONFIG_OCV
+	int ocv; /* Operating Channel Validation */
+#endif /* CONFIG_OCV */
+#ifdef CONFIG_IEEE80211R_AP
+	u8 ssid[SSID_MAX_LEN];
+	size_t ssid_len;
+	u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
+	u8 r0_key_holder[FT_R0KH_ID_MAX_LEN];
+	size_t r0_key_holder_len;
+	u8 r1_key_holder[FT_R1KH_ID_LEN];
+	u32 r0_key_lifetime; /* PMK-R0 lifetime seconds */
+	int rkh_pos_timeout;
+	int rkh_neg_timeout;
+	int rkh_pull_timeout; /* ms */
+	int rkh_pull_retries;
+	int r1_max_key_lifetime;
+	u32 reassociation_deadline;
+	struct ft_remote_r0kh **r0kh_list;
+	struct ft_remote_r1kh **r1kh_list;
+	int pmk_r1_push;
+	int ft_over_ds;
+	int ft_psk_generate_local;
+#endif /* CONFIG_IEEE80211R_AP */
+	int disable_gtk;
+	int ap_mlme;
+#ifdef CONFIG_TESTING_OPTIONS
+	double corrupt_gtk_rekey_mic_probability;
+	u8 own_ie_override[MAX_OWN_IE_OVERRIDE];
+	size_t own_ie_override_len;
+	u8 rsne_override_eapol[MAX_OWN_IE_OVERRIDE];
+	size_t rsne_override_eapol_len;
+	u8 rsnxe_override_eapol[MAX_OWN_IE_OVERRIDE];
+	size_t rsnxe_override_eapol_len;
+	u8 rsne_override_ft[MAX_OWN_IE_OVERRIDE];
+	size_t rsne_override_ft_len;
+	u8 rsnxe_override_ft[MAX_OWN_IE_OVERRIDE];
+	size_t rsnxe_override_ft_len;
+	u8 gtk_rsc_override[WPA_KEY_RSC_LEN];
+	u8 igtk_rsc_override[WPA_KEY_RSC_LEN];
+	unsigned int rsne_override_eapol_set:1;
+	unsigned int rsnxe_override_eapol_set:1;
+	unsigned int rsne_override_ft_set:1;
+	unsigned int rsnxe_override_ft_set:1;
+	unsigned int gtk_rsc_override_set:1;
+	unsigned int igtk_rsc_override_set:1;
+	int ft_rsnxe_used;
+	bool delay_eapol_tx;
+#endif /* CONFIG_TESTING_OPTIONS */
+	unsigned int oci_freq_override_eapol_m3;
+	unsigned int oci_freq_override_eapol_g1;
+	unsigned int oci_freq_override_ft_assoc;
+	unsigned int oci_freq_override_fils_assoc;
+#ifdef CONFIG_P2P
+	u8 ip_addr_go[4];
+	u8 ip_addr_mask[4];
+	u8 ip_addr_start[4];
+	u8 ip_addr_end[4];
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_FILS
+	unsigned int fils_cache_id_set:1;
+	u8 fils_cache_id[FILS_CACHE_ID_LEN];
+#endif /* CONFIG_FILS */
+	enum sae_pwe sae_pwe;
+	bool sae_pk;
+
+	unsigned int secure_ltf:1;
+	unsigned int secure_rtt:1;
+	unsigned int prot_range_neg:1;
+
+	int owe_ptk_workaround;
+	u8 transition_disable;
+#ifdef CONFIG_DPP2
+	int dpp_pfs;
+#endif /* CONFIG_DPP2 */
+
+	/*
+	 * If set Key Derivation Key should be derived as part of PMK to
+	 * PTK derivation regardless of advertised capabilities.
+	 */
+	bool force_kdk_derivation;
+
+	bool radius_psk;
+};
+
+typedef enum {
+	LOGGER_DEBUG, LOGGER_INFO, LOGGER_WARNING
+} logger_level;
+
+typedef enum {
+	WPA_EAPOL_portEnabled, WPA_EAPOL_portValid, WPA_EAPOL_authorized,
+	WPA_EAPOL_portControl_Auto, WPA_EAPOL_keyRun, WPA_EAPOL_keyAvailable,
+	WPA_EAPOL_keyDone, WPA_EAPOL_inc_EapolFramesTx
+} wpa_eapol_variable;
+
+struct wpa_auth_ml_rsn_info {
+	unsigned int n_mld_links;
+
+	struct wpa_auth_ml_link_rsn_info {
+		unsigned int link_id;
+		const u8 *rsn_ies;
+		size_t rsn_ies_len;
+	} links[MAX_NUM_MLD_LINKS];
+};
+
+struct wpa_auth_ml_key_info {
+	unsigned int n_mld_links;
+	bool mgmt_frame_prot;
+	bool beacon_prot;
+
+	struct wpa_auth_ml_link_key_info {
+		u8 link_id;
+
+		u8 gtkidx;
+		u8 gtk_len;
+		u8 pn[6];
+		const u8 *gtk;
+
+		u8 igtkidx;
+		u8 igtk_len;
+		const u8 *igtk;
+		u8 ipn[6];
+
+		u8 bigtkidx;
+		const u8 *bigtk;
+		u8 bipn[6];
+	} links[MAX_NUM_MLD_LINKS];
+};
+
+struct wpa_auth_callbacks {
+	void (*logger)(void *ctx, const u8 *addr, logger_level level,
+		       const char *txt);
+	void (*disconnect)(void *ctx, const u8 *addr, u16 reason);
+	int (*mic_failure_report)(void *ctx, const u8 *addr);
+	void (*psk_failure_report)(void *ctx, const u8 *addr);
+	void (*set_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var,
+			  int value);
+	int (*get_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var);
+	const u8 * (*get_psk)(void *ctx, const u8 *addr, const u8 *p2p_dev_addr,
+			      const u8 *prev_psk, size_t *psk_len,
+			      int *vlan_id);
+	int (*get_msk)(void *ctx, const u8 *addr, u8 *msk, size_t *len);
+	int (*set_key)(void *ctx, int vlan_id, enum wpa_alg alg,
+		       const u8 *addr, int idx, u8 *key, size_t key_len,
+		       enum key_flag key_flag);
+	int (*get_seqnum)(void *ctx, const u8 *addr, int idx, u8 *seq);
+	int (*send_eapol)(void *ctx, const u8 *addr, const u8 *data,
+			  size_t data_len, int encrypt);
+	int (*for_each_sta)(void *ctx, int (*cb)(struct wpa_state_machine *sm,
+						 void *ctx), void *cb_ctx);
+	int (*for_each_auth)(void *ctx, int (*cb)(struct wpa_authenticator *a,
+						  void *ctx), void *cb_ctx);
+	int (*send_ether)(void *ctx, const u8 *dst, u16 proto, const u8 *data,
+			  size_t data_len);
+	int (*send_oui)(void *ctx, const u8 *dst, u8 oui_suffix, const u8 *data,
+			size_t data_len);
+	int (*channel_info)(void *ctx, struct wpa_channel_info *ci);
+	int (*update_vlan)(void *ctx, const u8 *addr, int vlan_id);
+	int (*get_sta_tx_params)(void *ctx, const u8 *addr,
+				 int ap_max_chanwidth, int ap_seg1_idx,
+				 int *bandwidth, int *seg1_idx);
+	void (*store_ptksa)(void *ctx, const u8 *addr, int cipher,
+			    u32 life_time, const struct wpa_ptk *ptk);
+	void (*clear_ptksa)(void *ctx, const u8 *addr, int cipher);
+	void (*request_radius_psk)(void *ctx, const u8 *addr, int key_mgmt,
+				   const u8 *anonce,
+				   const u8 *eapol, size_t eapol_len);
+#ifdef CONFIG_IEEE80211R_AP
+	struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr);
+	int (*add_sta_ft)(void *ctx, const u8 *sta_addr);
+	int (*set_vlan)(void *ctx, const u8 *sta_addr,
+			struct vlan_description *vlan);
+	int (*get_vlan)(void *ctx, const u8 *sta_addr,
+			struct vlan_description *vlan);
+	int (*set_identity)(void *ctx, const u8 *sta_addr,
+			    const u8 *identity, size_t identity_len);
+	size_t (*get_identity)(void *ctx, const u8 *sta_addr, const u8 **buf);
+	int (*set_radius_cui)(void *ctx, const u8 *sta_addr,
+			      const u8 *radius_cui, size_t radius_cui_len);
+	size_t (*get_radius_cui)(void *ctx, const u8 *sta_addr, const u8 **buf);
+	void (*set_session_timeout)(void *ctx, const u8 *sta_addr,
+				    int session_timeout);
+	int (*get_session_timeout)(void *ctx, const u8 *sta_addr);
+
+	int (*send_ft_action)(void *ctx, const u8 *dst,
+			      const u8 *data, size_t data_len);
+	int (*add_tspec)(void *ctx, const u8 *sta_addr, u8 *tspec_ie,
+			 size_t tspec_ielen);
+#endif /* CONFIG_IEEE80211R_AP */
+#ifdef CONFIG_MESH
+	int (*start_ampe)(void *ctx, const u8 *sta_addr);
+#endif /* CONFIG_MESH */
+#ifdef CONFIG_PASN
+	int (*set_ltf_keyseed)(void *ctx, const u8 *addr, const u8 *ltf_keyseed,
+			       size_t ltf_keyseed_len);
+#endif /* CONFIG_PASN */
+#ifdef CONFIG_IEEE80211BE
+	int (*get_ml_rsn_info)(void *ctx, struct wpa_auth_ml_rsn_info *info);
+	int (*get_ml_key_info)(void *ctx, struct wpa_auth_ml_key_info *info);
+#endif /* CONFIG_IEEE80211BE */
+};
+
+struct wpa_authenticator * wpa_init(const u8 *addr,
+				    struct wpa_auth_config *conf,
+				    const struct wpa_auth_callbacks *cb,
+				    void *cb_ctx);
+int wpa_init_keys(struct wpa_authenticator *wpa_auth);
+void wpa_deinit(struct wpa_authenticator *wpa_auth);
+int wpa_reconfig(struct wpa_authenticator *wpa_auth,
+		 struct wpa_auth_config *conf);
+
+enum wpa_validate_result {
+	WPA_IE_OK, WPA_INVALID_IE, WPA_INVALID_GROUP, WPA_INVALID_PAIRWISE,
+	WPA_INVALID_AKMP, WPA_NOT_ENABLED, WPA_ALLOC_FAIL,
+	WPA_MGMT_FRAME_PROTECTION_VIOLATION, WPA_INVALID_MGMT_GROUP_CIPHER,
+	WPA_INVALID_MDIE, WPA_INVALID_PROTO, WPA_INVALID_PMKID,
+	WPA_DENIED_OTHER_REASON
+};
+
+enum wpa_validate_result
+wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
+		    struct wpa_state_machine *sm, int freq,
+		    const u8 *wpa_ie, size_t wpa_ie_len,
+		    const u8 *rsnxe, size_t rsnxe_len,
+		    const u8 *mdie, size_t mdie_len,
+		    const u8 *owe_dh, size_t owe_dh_len);
+int wpa_validate_osen(struct wpa_authenticator *wpa_auth,
+		      struct wpa_state_machine *sm,
+		      const u8 *osen_ie, size_t osen_ie_len);
+int wpa_auth_uses_mfp(struct wpa_state_machine *sm);
+void wpa_auth_set_ocv(struct wpa_state_machine *sm, int ocv);
+int wpa_auth_uses_ocv(struct wpa_state_machine *sm);
+struct wpa_state_machine *
+wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr,
+		  const u8 *p2p_dev_addr);
+int wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth,
+			    struct wpa_state_machine *sm);
+void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm);
+void wpa_auth_sta_deinit(struct wpa_state_machine *sm);
+void wpa_receive(struct wpa_authenticator *wpa_auth,
+		 struct wpa_state_machine *sm,
+		 u8 *data, size_t data_len);
+enum wpa_event {
+	WPA_AUTH, WPA_ASSOC, WPA_DISASSOC, WPA_DEAUTH, WPA_REAUTH,
+	WPA_REAUTH_EAPOL, WPA_ASSOC_FT, WPA_ASSOC_FILS, WPA_DRV_STA_REMOVED
+};
+void wpa_remove_ptk(struct wpa_state_machine *sm);
+int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event);
+void wpa_auth_sm_notify(struct wpa_state_machine *sm);
+void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth);
+int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen);
+int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen);
+void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth);
+int wpa_auth_pairwise_set(struct wpa_state_machine *sm);
+int wpa_auth_get_pairwise(struct wpa_state_machine *sm);
+const u8 * wpa_auth_get_pmk(struct wpa_state_machine *sm, int *len);
+const u8 * wpa_auth_get_dpp_pkhash(struct wpa_state_machine *sm);
+int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm);
+int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm);
+int wpa_auth_sta_ft_tk_already_set(struct wpa_state_machine *sm);
+int wpa_auth_sta_fils_tk_already_set(struct wpa_state_machine *sm);
+int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm,
+			     struct rsn_pmksa_cache_entry *entry);
+struct rsn_pmksa_cache_entry *
+wpa_auth_sta_get_pmksa(struct wpa_state_machine *sm);
+void wpa_auth_sta_local_mic_failure_report(struct wpa_state_machine *sm);
+const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth,
+			       size_t *len);
+int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk,
+		       unsigned int pmk_len,
+		       int session_timeout, struct eapol_state_machine *eapol);
+int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth,
+			       const u8 *pmk, size_t len, const u8 *sta_addr,
+			       int session_timeout,
+			       struct eapol_state_machine *eapol);
+int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
+			   const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+			   int akmp);
+void wpa_auth_add_sae_pmkid(struct wpa_state_machine *sm, const u8 *pmkid);
+int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr,
+			const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+			int session_timeout, int akmp);
+int wpa_auth_pmksa_add3(struct wpa_authenticator *wpa_auth, const u8 *addr,
+			const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+			int session_timeout, int akmp, const u8 *dpp_pkhash);
+void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
+			   const u8 *sta_addr);
+int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf,
+			size_t len);
+void wpa_auth_pmksa_flush(struct wpa_authenticator *wpa_auth);
+int wpa_auth_pmksa_list_mesh(struct wpa_authenticator *wpa_auth, const u8 *addr,
+			     char *buf, size_t len);
+struct rsn_pmksa_cache_entry *
+wpa_auth_pmksa_create_entry(const u8 *aa, const u8 *spa, const u8 *pmk,
+			    size_t pmk_len, int akmp,
+			    const u8 *pmkid, int expiration);
+int wpa_auth_pmksa_add_entry(struct wpa_authenticator *wpa_auth,
+			     struct rsn_pmksa_cache_entry *entry);
+struct rsn_pmksa_cache *
+wpa_auth_get_pmksa_cache(struct wpa_authenticator *wpa_auth);
+struct rsn_pmksa_cache_entry *
+wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
+		   const u8 *pmkid);
+struct rsn_pmksa_cache_entry *
+wpa_auth_pmksa_get_fils_cache_id(struct wpa_authenticator *wpa_auth,
+				 const u8 *sta_addr, const u8 *pmkid);
+void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa,
+			      struct wpa_state_machine *sm,
+			      struct wpa_authenticator *wpa_auth,
+			      u8 *pmkid, u8 *pmk);
+int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id);
+void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth,
+				  struct wpa_state_machine *sm, int ack);
+
+#ifdef CONFIG_IEEE80211R_AP
+u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
+				 size_t max_len, int auth_alg,
+				 const u8 *req_ies, size_t req_ies_len,
+				 int omit_rsnxe);
+void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid,
+			 u16 auth_transaction, const u8 *ies, size_t ies_len,
+			 void (*cb)(void *ctx, const u8 *dst, const u8 *bssid,
+				    u16 auth_transaction, u16 resp,
+				    const u8 *ies, size_t ies_len),
+			 void *ctx);
+int wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
+			    size_t ies_len);
+int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len);
+int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
+		  const u8 *data, size_t data_len);
+void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
+		       const u8 *dst_addr, u8 oui_suffix, const u8 *data,
+		       size_t data_len);
+void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr);
+void wpa_ft_deinit(struct wpa_authenticator *wpa_auth);
+void wpa_ft_sta_deinit(struct wpa_state_machine *sm);
+int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth,
+			const u8 *spa, const u8 *pmk_r1_name,
+			u8 *pmk_r1, size_t *pmk_r1_len, int *pairwise,
+			struct vlan_description *vlan,
+			const u8 **identity, size_t *identity_len,
+			const u8 **radius_cui, size_t *radius_cui_len,
+			int *session_timeout);
+
+#endif /* CONFIG_IEEE80211R_AP */
+
+void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm);
+void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag);
+int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos);
+int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos);
+int wpa_wnmsleep_bigtk_subelem(struct wpa_state_machine *sm, u8 *pos);
+
+int wpa_auth_uses_sae(struct wpa_state_machine *sm);
+int wpa_auth_uses_ft_sae(struct wpa_state_machine *sm);
+
+int wpa_auth_get_ip_addr(struct wpa_state_machine *sm, u8 *addr);
+
+struct radius_das_attrs;
+int wpa_auth_radius_das_disconnect_pmksa(struct wpa_authenticator *wpa_auth,
+					 struct radius_das_attrs *attr);
+void wpa_auth_reconfig_group_keys(struct wpa_authenticator *wpa_auth);
+
+int wpa_auth_ensure_group(struct wpa_authenticator *wpa_auth, int vlan_id);
+int wpa_auth_release_group(struct wpa_authenticator *wpa_auth, int vlan_id);
+int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk,
+			 size_t pmk_len, const u8 *snonce, const u8 *anonce,
+			 const u8 *dhss, size_t dhss_len,
+			 struct wpabuf *g_sta, struct wpabuf *g_ap);
+int fils_decrypt_assoc(struct wpa_state_machine *sm, const u8 *fils_session,
+		       const struct ieee80211_mgmt *mgmt, size_t frame_len,
+		       u8 *pos, size_t left);
+int fils_encrypt_assoc(struct wpa_state_machine *sm, u8 *buf,
+		       size_t current_len, size_t max_len,
+		       const struct wpabuf *hlp);
+int fils_set_tk(struct wpa_state_machine *sm);
+u8 * hostapd_eid_assoc_fils_session(struct wpa_state_machine *sm, u8 *eid,
+				    const u8 *fils_session,
+				    struct wpabuf *fils_hlp_resp);
+const u8 *  wpa_fils_validate_fils_session(struct wpa_state_machine *sm,
+					   const u8 *ies, size_t ies_len,
+					   const u8 *fils_session);
+int wpa_fils_validate_key_confirm(struct wpa_state_machine *sm, const u8 *ies,
+				  size_t ies_len);
+
+int get_sta_tx_parameters(struct wpa_state_machine *sm, int ap_max_chanwidth,
+			  int ap_seg1_idx, int *bandwidth, int *seg1_idx);
+
+int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth,
+		       struct wpa_state_machine *sm,
+		       u8 *buf, size_t len);
+void wpa_auth_get_fils_aead_params(struct wpa_state_machine *sm,
+				   u8 *fils_anonce, u8 *fils_snonce,
+				   u8 *fils_kek, size_t *fils_kek_len);
+void wpa_auth_add_fils_pmk_pmkid(struct wpa_state_machine *sm, const u8 *pmk,
+				 size_t pmk_len, const u8 *pmkid);
+u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm,
+				   u8 *pos, size_t max_len,
+				   const u8 *req_ies, size_t req_ies_len);
+u8 * wpa_auth_write_assoc_resp_fils(struct wpa_state_machine *sm,
+				    u8 *pos, size_t max_len,
+				    const u8 *req_ies, size_t req_ies_len);
+bool wpa_auth_write_fd_rsn_info(struct wpa_authenticator *wpa_auth,
+				u8 *fd_rsn_info);
+void wpa_auth_set_auth_alg(struct wpa_state_machine *sm, u16 auth_alg);
+void wpa_auth_set_dpp_z(struct wpa_state_machine *sm, const struct wpabuf *z);
+void wpa_auth_set_transition_disable(struct wpa_authenticator *wpa_auth,
+				     u8 val);
+
+int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce,
+		       void (*cb)(void *ctx1, void *ctx2),
+		       void *ctx1, void *ctx2);
+int wpa_auth_resend_m3(struct wpa_state_machine *sm,
+		       void (*cb)(void *ctx1, void *ctx2),
+		       void *ctx1, void *ctx2);
+int wpa_auth_resend_group_m1(struct wpa_state_machine *sm,
+			     void (*cb)(void *ctx1, void *ctx2),
+			     void *ctx1, void *ctx2);
+int wpa_auth_rekey_ptk(struct wpa_authenticator *wpa_auth,
+		       struct wpa_state_machine *sm);
+int wpa_auth_rekey_gtk(struct wpa_authenticator *wpa_auth);
+int hostapd_wpa_auth_send_eapol(void *ctx, const u8 *addr,
+				const u8 *data, size_t data_len,
+				int encrypt);
+void wpa_auth_set_ptk_rekey_timer(struct wpa_state_machine *sm);
+void wpa_auth_set_ft_rsnxe_used(struct wpa_authenticator *wpa_auth, int val);
+
+enum wpa_auth_ocv_override_frame {
+	WPA_AUTH_OCV_OVERRIDE_EAPOL_M3,
+	WPA_AUTH_OCV_OVERRIDE_EAPOL_G1,
+	WPA_AUTH_OCV_OVERRIDE_FT_ASSOC,
+	WPA_AUTH_OCV_OVERRIDE_FILS_ASSOC,
+};
+void wpa_auth_set_ocv_override_freq(struct wpa_authenticator *wpa_auth,
+				    enum wpa_auth_ocv_override_frame frame,
+				    unsigned int freq);
+
+void wpa_auth_sta_radius_psk_resp(struct wpa_state_machine *sm, bool success);
+
+void wpa_auth_set_ml_info(struct wpa_state_machine *sm, const u8 *mld_addr,
+			  u8 mld_assoc_link_id, struct mld_info *info);
+void wpa_auth_ml_get_rsn_info(struct wpa_authenticator *a,
+			      struct wpa_auth_ml_link_rsn_info *info);
+void wpa_auth_ml_get_key_info(struct wpa_authenticator *a,
+			      struct wpa_auth_ml_link_key_info *info,
+			      bool mgmt_frame_prot, bool beacon_prot);
+
+#endif /* WPA_AUTH_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_ft.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_ft.c
new file mode 100644
index 0000000..2402ad9
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_ft.c
@@ -0,0 +1,4953 @@
+/*
+ * hostapd - IEEE 802.11r - Fast BSS Transition
+ * Copyright (c) 2004-2018, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/list.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/ocv.h"
+#include "common/wpa_ctrl.h"
+#include "drivers/driver.h"
+#include "crypto/aes.h"
+#include "crypto/aes_siv.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/sha384.h"
+#include "crypto/sha512.h"
+#include "crypto/random.h"
+#include "ap_config.h"
+#include "ieee802_11.h"
+#include "wmm.h"
+#include "wpa_auth.h"
+#include "wpa_auth_i.h"
+#include "pmksa_cache_auth.h"
+
+
+#ifdef CONFIG_IEEE80211R_AP
+
+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,
+				     u16 status, const u8 *resp_ies,
+				     size_t resp_ies_len);
+static void ft_finish_pull(struct wpa_state_machine *sm);
+static void wpa_ft_expire_pull(void *eloop_ctx, void *timeout_ctx);
+static void wpa_ft_rrb_seq_timeout(void *eloop_ctx, void *timeout_ctx);
+
+struct tlv_list {
+	u16 type;
+	size_t len;
+	const u8 *data;
+};
+
+
+/**
+ * wpa_ft_rrb_decrypt - Decrypt FT RRB message
+ * @key: AES-SIV key for AEAD
+ * @key_len: Length of key in octets
+ * @enc: Pointer to encrypted TLVs
+ * @enc_len: Length of encrypted TLVs in octets
+ * @auth: Pointer to authenticated TLVs
+ * @auth_len: Length of authenticated TLVs in octets
+ * @src_addr: MAC address of the frame sender
+ * @type: Vendor-specific subtype of the RRB frame (FT_PACKET_*)
+ * @plain: Pointer to return the pointer to the allocated plaintext buffer;
+ *	needs to be freed by the caller if not NULL;
+ *	will only be returned on success
+ * @plain_len: Pointer to return the length of the allocated plaintext buffer
+ *	in octets
+ * Returns: 0 on success, -1 on error
+ */
+static int wpa_ft_rrb_decrypt(const u8 *key, const size_t key_len,
+			      const u8 *enc, size_t enc_len,
+			      const u8 *auth, const size_t auth_len,
+			      const u8 *src_addr, u8 type,
+			      u8 **plain, size_t *plain_size)
+{
+	const u8 *ad[3] = { src_addr, auth, &type };
+	size_t ad_len[3] = { ETH_ALEN, auth_len, sizeof(type) };
+
+	wpa_printf(MSG_DEBUG, "FT(RRB): src_addr=" MACSTR " type=%u",
+		   MAC2STR(src_addr), type);
+	wpa_hexdump_key(MSG_DEBUG, "FT(RRB): decrypt using key", key, key_len);
+	wpa_hexdump(MSG_DEBUG, "FT(RRB): encrypted TLVs", enc, enc_len);
+	wpa_hexdump(MSG_DEBUG, "FT(RRB): authenticated TLVs", auth, auth_len);
+
+	if (!key) { /* skip decryption */
+		*plain = os_memdup(enc, enc_len);
+		if (enc_len > 0 && !*plain)
+			goto err;
+
+		*plain_size = enc_len;
+
+		return 0;
+	}
+
+	*plain = NULL;
+
+	/* SIV overhead */
+	if (enc_len < AES_BLOCK_SIZE)
+		goto err;
+
+	*plain = os_zalloc(enc_len - AES_BLOCK_SIZE);
+	if (!*plain)
+		goto err;
+
+	if (aes_siv_decrypt(key, key_len, enc, enc_len, 3, ad, ad_len,
+			    *plain) < 0) {
+		if (enc_len < AES_BLOCK_SIZE + 2)
+			goto err;
+
+		/* Try to work around Ethernet devices that add extra
+		 * two octet padding even if the frame is longer than
+		 * the minimum Ethernet frame. */
+		enc_len -= 2;
+		if (aes_siv_decrypt(key, key_len, enc, enc_len, 3, ad, ad_len,
+				    *plain) < 0)
+			goto err;
+	}
+
+	*plain_size = enc_len - AES_BLOCK_SIZE;
+	wpa_hexdump_key(MSG_DEBUG, "FT(RRB): decrypted TLVs",
+			*plain, *plain_size);
+	return 0;
+err:
+	os_free(*plain);
+	*plain = NULL;
+	*plain_size = 0;
+
+	wpa_printf(MSG_ERROR, "FT(RRB): Failed to decrypt");
+
+	return -1;
+}
+
+
+/* get first tlv record in packet matching type
+ * @data (decrypted) packet
+ * @return 0 on success else -1
+ */
+static int wpa_ft_rrb_get_tlv(const u8 *plain, size_t plain_len,
+			      u16 type, size_t *tlv_len, const u8 **tlv_data)
+{
+	const struct ft_rrb_tlv *f;
+	size_t left;
+	le16 type16;
+	size_t len;
+
+	left = plain_len;
+	type16 = host_to_le16(type);
+
+	while (left >= sizeof(*f)) {
+		f = (const struct ft_rrb_tlv *) plain;
+
+		left -= sizeof(*f);
+		plain += sizeof(*f);
+		len = le_to_host16(f->len);
+
+		if (left < len) {
+			wpa_printf(MSG_DEBUG, "FT: RRB message truncated");
+			break;
+		}
+
+		if (f->type == type16) {
+			*tlv_len = len;
+			*tlv_data = plain;
+			return 0;
+		}
+
+		left -= len;
+		plain += len;
+	}
+
+	return -1;
+}
+
+
+static void wpa_ft_rrb_dump(const u8 *plain, const size_t plain_len)
+{
+	const struct ft_rrb_tlv *f;
+	size_t left;
+	size_t len;
+
+	left = plain_len;
+
+	wpa_printf(MSG_DEBUG, "FT: RRB dump message");
+	while (left >= sizeof(*f)) {
+		f = (const struct ft_rrb_tlv *) plain;
+
+		left -= sizeof(*f);
+		plain += sizeof(*f);
+		len = le_to_host16(f->len);
+
+		wpa_printf(MSG_DEBUG, "FT: RRB TLV type = %d, len = %zu",
+			   le_to_host16(f->type), len);
+
+		if (left < len) {
+			wpa_printf(MSG_DEBUG,
+				   "FT: RRB message truncated: left %zu bytes, need %zu",
+				   left, len);
+			break;
+		}
+
+		wpa_hexdump(MSG_DEBUG, "FT: RRB TLV data", plain, len);
+
+		left -= len;
+		plain += len;
+	}
+
+	if (left > 0)
+		wpa_hexdump(MSG_DEBUG, "FT: RRB TLV padding", plain, left);
+
+	wpa_printf(MSG_DEBUG, "FT: RRB dump message end");
+}
+
+
+static int cmp_int(const void *a, const void *b)
+{
+	int x, y;
+
+	x = *((int *) a);
+	y = *((int *) b);
+	return x - y;
+}
+
+
+static int wpa_ft_rrb_get_tlv_vlan(const u8 *plain, const size_t plain_len,
+				   struct vlan_description *vlan)
+{
+	struct ft_rrb_tlv *f;
+	size_t left;
+	size_t len;
+	int taggedidx;
+	int vlan_id;
+	int type;
+
+	left = plain_len;
+	taggedidx = 0;
+	os_memset(vlan, 0, sizeof(*vlan));
+
+	while (left >= sizeof(*f)) {
+		f = (struct ft_rrb_tlv *) plain;
+
+		left -= sizeof(*f);
+		plain += sizeof(*f);
+
+		len = le_to_host16(f->len);
+		type = le_to_host16(f->type);
+
+		if (left < len) {
+			wpa_printf(MSG_DEBUG, "FT: RRB message truncated");
+			return -1;
+		}
+
+		if (type != FT_RRB_VLAN_UNTAGGED && type != FT_RRB_VLAN_TAGGED)
+			goto skip;
+
+		if (type == FT_RRB_VLAN_UNTAGGED && len != sizeof(le16)) {
+			wpa_printf(MSG_DEBUG,
+				   "FT: RRB VLAN_UNTAGGED invalid length");
+			return -1;
+		}
+
+		if (type == FT_RRB_VLAN_TAGGED && len % sizeof(le16) != 0) {
+			wpa_printf(MSG_DEBUG,
+				   "FT: RRB VLAN_TAGGED invalid length");
+			return -1;
+		}
+
+		while (len >= sizeof(le16)) {
+			vlan_id = WPA_GET_LE16(plain);
+			plain += sizeof(le16);
+			left -= sizeof(le16);
+			len -= sizeof(le16);
+
+			if (vlan_id <= 0 || vlan_id > MAX_VLAN_ID) {
+				wpa_printf(MSG_DEBUG,
+					   "FT: RRB VLAN ID invalid %d",
+					   vlan_id);
+				continue;
+			}
+
+			if (type == FT_RRB_VLAN_UNTAGGED)
+				vlan->untagged = vlan_id;
+
+			if (type == FT_RRB_VLAN_TAGGED &&
+			    taggedidx < MAX_NUM_TAGGED_VLAN) {
+				vlan->tagged[taggedidx] = vlan_id;
+				taggedidx++;
+			} else if (type == FT_RRB_VLAN_TAGGED) {
+				wpa_printf(MSG_DEBUG, "FT: RRB too many VLANs");
+			}
+		}
+
+	skip:
+		left -= len;
+		plain += len;
+	}
+
+	if (taggedidx)
+		qsort(vlan->tagged, taggedidx, sizeof(int), cmp_int);
+
+	vlan->notempty = vlan->untagged || vlan->tagged[0];
+
+	return 0;
+}
+
+
+static size_t wpa_ft_tlv_len(const struct tlv_list *tlvs)
+{
+	size_t tlv_len = 0;
+	int i;
+
+	if (!tlvs)
+		return 0;
+
+	for (i = 0; tlvs[i].type != FT_RRB_LAST_EMPTY; i++) {
+		tlv_len += sizeof(struct ft_rrb_tlv);
+		tlv_len += tlvs[i].len;
+	}
+
+	return tlv_len;
+}
+
+
+static size_t wpa_ft_tlv_lin(const struct tlv_list *tlvs, u8 *start,
+			     u8 *endpos)
+{
+	int i;
+	size_t tlv_len;
+	struct ft_rrb_tlv *hdr;
+	u8 *pos;
+
+	if (!tlvs)
+		return 0;
+
+	tlv_len = 0;
+	pos = start;
+	for (i = 0; tlvs[i].type != FT_RRB_LAST_EMPTY; i++) {
+		if (tlv_len + sizeof(*hdr) > (size_t) (endpos - start))
+			return tlv_len;
+		tlv_len += sizeof(*hdr);
+		hdr = (struct ft_rrb_tlv *) pos;
+		hdr->type = host_to_le16(tlvs[i].type);
+		hdr->len = host_to_le16(tlvs[i].len);
+		pos = start + tlv_len;
+
+		if (tlv_len + tlvs[i].len > (size_t) (endpos - start))
+			return tlv_len;
+		if (tlvs[i].len == 0)
+			continue;
+		tlv_len += tlvs[i].len;
+		os_memcpy(pos, tlvs[i].data, tlvs[i].len);
+		pos = start + tlv_len;
+	}
+
+	return tlv_len;
+}
+
+
+static size_t wpa_ft_vlan_len(const struct vlan_description *vlan)
+{
+	size_t tlv_len = 0;
+	int i;
+
+	if (!vlan || !vlan->notempty)
+		return 0;
+
+	if (vlan->untagged) {
+		tlv_len += sizeof(struct ft_rrb_tlv);
+		tlv_len += sizeof(le16);
+	}
+	if (vlan->tagged[0])
+		tlv_len += sizeof(struct ft_rrb_tlv);
+	for (i = 0; i < MAX_NUM_TAGGED_VLAN && vlan->tagged[i]; i++)
+		tlv_len += sizeof(le16);
+
+	return tlv_len;
+}
+
+
+static size_t wpa_ft_vlan_lin(const struct vlan_description *vlan,
+			      u8 *start, u8 *endpos)
+{
+	size_t tlv_len;
+	int i, len;
+	struct ft_rrb_tlv *hdr;
+	u8 *pos = start;
+
+	if (!vlan || !vlan->notempty)
+		return 0;
+
+	tlv_len = 0;
+	if (vlan->untagged) {
+		tlv_len += sizeof(*hdr);
+		if (start + tlv_len > endpos)
+			return tlv_len;
+		hdr = (struct ft_rrb_tlv *) pos;
+		hdr->type = host_to_le16(FT_RRB_VLAN_UNTAGGED);
+		hdr->len = host_to_le16(sizeof(le16));
+		pos = start + tlv_len;
+
+		tlv_len += sizeof(le16);
+		if (start + tlv_len > endpos)
+			return tlv_len;
+		WPA_PUT_LE16(pos, vlan->untagged);
+		pos = start + tlv_len;
+	}
+
+	if (!vlan->tagged[0])
+		return tlv_len;
+
+	tlv_len += sizeof(*hdr);
+	if (start + tlv_len > endpos)
+		return tlv_len;
+	hdr = (struct ft_rrb_tlv *) pos;
+	hdr->type = host_to_le16(FT_RRB_VLAN_TAGGED);
+	len = 0; /* len is computed below */
+	pos = start + tlv_len;
+
+	for (i = 0; i < MAX_NUM_TAGGED_VLAN && vlan->tagged[i]; i++) {
+		tlv_len += sizeof(le16);
+		if (start + tlv_len > endpos)
+			break;
+		len += sizeof(le16);
+		WPA_PUT_LE16(pos, vlan->tagged[i]);
+		pos = start + tlv_len;
+	}
+
+	hdr->len = host_to_le16(len);
+
+	return tlv_len;
+}
+
+
+static int wpa_ft_rrb_lin(const struct tlv_list *tlvs1,
+			  const struct tlv_list *tlvs2,
+			  const struct vlan_description *vlan,
+			  u8 **plain, size_t *plain_len)
+{
+	u8 *pos, *endpos;
+	size_t tlv_len;
+
+	tlv_len = wpa_ft_tlv_len(tlvs1);
+	tlv_len += wpa_ft_tlv_len(tlvs2);
+	tlv_len += wpa_ft_vlan_len(vlan);
+
+	*plain_len = tlv_len;
+	*plain = os_zalloc(tlv_len);
+	if (!*plain) {
+		wpa_printf(MSG_ERROR, "FT: Failed to allocate plaintext");
+		goto err;
+	}
+
+	pos = *plain;
+	endpos = *plain + tlv_len;
+	pos += wpa_ft_tlv_lin(tlvs1, pos, endpos);
+	pos += wpa_ft_tlv_lin(tlvs2, pos, endpos);
+	pos += wpa_ft_vlan_lin(vlan, pos, endpos);
+
+	/* validity check */
+	if (pos != endpos) {
+		wpa_printf(MSG_ERROR, "FT: Length error building RRB");
+		goto err;
+	}
+
+	return 0;
+
+err:
+	os_free(*plain);
+	*plain = NULL;
+	*plain_len = 0;
+	return -1;
+}
+
+
+static int wpa_ft_rrb_encrypt(const u8 *key, const size_t key_len,
+			      const u8 *plain, const size_t plain_len,
+			      const u8 *auth, const size_t auth_len,
+			      const u8 *src_addr, u8 type, u8 *enc)
+{
+	const u8 *ad[3] = { src_addr, auth, &type };
+	size_t ad_len[3] = { ETH_ALEN, auth_len, sizeof(type) };
+
+	wpa_printf(MSG_DEBUG, "FT(RRB): src_addr=" MACSTR " type=%u",
+		   MAC2STR(src_addr), type);
+	wpa_hexdump_key(MSG_DEBUG, "FT(RRB): plaintext message",
+			plain, plain_len);
+	wpa_hexdump_key(MSG_DEBUG, "FT(RRB): encrypt using key", key, key_len);
+	wpa_hexdump(MSG_DEBUG, "FT(RRB): authenticated TLVs", auth, auth_len);
+
+	if (!key) {
+		/* encryption not needed, return plaintext as packet */
+		os_memcpy(enc, plain, plain_len);
+	} else if (aes_siv_encrypt(key, key_len, plain, plain_len,
+				   3, ad, ad_len, enc) < 0) {
+		wpa_printf(MSG_ERROR, "FT: Failed to encrypt RRB-OUI message");
+		return -1;
+	}
+	wpa_hexdump(MSG_DEBUG, "FT(RRB): encrypted TLVs",
+		    enc, plain_len + AES_BLOCK_SIZE);
+
+	return 0;
+}
+
+
+/**
+ * wpa_ft_rrb_build - Build and encrypt an FT RRB message
+ * @key: AES-SIV key for AEAD
+ * @key_len: Length of key in octets
+ * @tlvs_enc0: First set of to-be-encrypted TLVs
+ * @tlvs_enc1: Second set of to-be-encrypted TLVs
+ * @tlvs_auth: Set of to-be-authenticated TLVs
+ * @src_addr: MAC address of the frame sender
+ * @type: Vendor-specific subtype of the RRB frame (FT_PACKET_*)
+ * @packet Pointer to return the pointer to the allocated packet buffer;
+ *         needs to be freed by the caller if not null;
+ *         will only be returned on success
+ * @packet_len: Pointer to return the length of the allocated buffer in octets
+ * Returns: 0 on success, -1 on error
+ */
+static int wpa_ft_rrb_build(const u8 *key, const size_t key_len,
+			    const struct tlv_list *tlvs_enc0,
+			    const struct tlv_list *tlvs_enc1,
+			    const struct tlv_list *tlvs_auth,
+			    const struct vlan_description *vlan,
+			    const u8 *src_addr, u8 type,
+			    u8 **packet, size_t *packet_len)
+{
+	u8 *plain = NULL, *auth = NULL, *pos, *tmp;
+	size_t plain_len = 0, auth_len = 0;
+	int ret = -1;
+	size_t pad_len = 0;
+
+	*packet = NULL;
+	if (wpa_ft_rrb_lin(tlvs_enc0, tlvs_enc1, vlan, &plain, &plain_len) < 0)
+		goto out;
+
+	if (wpa_ft_rrb_lin(tlvs_auth, NULL, NULL, &auth, &auth_len) < 0)
+		goto out;
+
+	*packet_len = sizeof(u16) + auth_len + plain_len;
+	if (key)
+		*packet_len += AES_BLOCK_SIZE;
+#define RRB_MIN_MSG_LEN 64
+	if (*packet_len < RRB_MIN_MSG_LEN) {
+		pad_len = RRB_MIN_MSG_LEN - *packet_len;
+		if (pad_len < sizeof(struct ft_rrb_tlv))
+			pad_len = sizeof(struct ft_rrb_tlv);
+		wpa_printf(MSG_DEBUG,
+			   "FT: Pad message to minimum Ethernet frame length (%d --> %d)",
+			   (int) *packet_len, (int) (*packet_len + pad_len));
+		*packet_len += pad_len;
+		tmp = os_realloc(auth, auth_len + pad_len);
+		if (!tmp)
+			goto out;
+		auth = tmp;
+		pos = auth + auth_len;
+		WPA_PUT_LE16(pos, FT_RRB_LAST_EMPTY);
+		pos += 2;
+		WPA_PUT_LE16(pos, pad_len - sizeof(struct ft_rrb_tlv));
+		pos += 2;
+		os_memset(pos, 0, pad_len - sizeof(struct ft_rrb_tlv));
+		auth_len += pad_len;
+
+	}
+	*packet = os_zalloc(*packet_len);
+	if (!*packet)
+		goto out;
+
+	pos = *packet;
+	WPA_PUT_LE16(pos, auth_len);
+	pos += 2;
+	os_memcpy(pos, auth, auth_len);
+	pos += auth_len;
+	if (wpa_ft_rrb_encrypt(key, key_len, plain, plain_len, auth,
+			       auth_len, src_addr, type, pos) < 0)
+		goto out;
+	wpa_hexdump(MSG_MSGDUMP, "FT: RRB frame payload", *packet, *packet_len);
+
+	ret = 0;
+
+out:
+	bin_clear_free(plain, plain_len);
+	os_free(auth);
+
+	if (ret) {
+		wpa_printf(MSG_ERROR, "FT: Failed to build RRB-OUI message");
+		os_free(*packet);
+		*packet = NULL;
+		*packet_len = 0;
+	}
+
+	return ret;
+}
+
+
+#define RRB_GET_SRC(srcfield, type, field, txt, checklength) do { \
+	if (wpa_ft_rrb_get_tlv(srcfield, srcfield##_len, type, \
+				&f_##field##_len, &f_##field) < 0 || \
+	    (checklength > 0 && ((size_t) checklength) != f_##field##_len)) { \
+		wpa_printf(MSG_INFO, "FT: Missing required " #field \
+			   " in %s from " MACSTR, txt, MAC2STR(src_addr)); \
+		wpa_ft_rrb_dump(srcfield, srcfield##_len); \
+		goto out; \
+	} \
+} while (0)
+
+#define RRB_GET(type, field, txt, checklength) \
+	RRB_GET_SRC(plain, type, field, txt, checklength)
+#define RRB_GET_AUTH(type, field, txt, checklength) \
+	RRB_GET_SRC(auth, type, field, txt, checklength)
+
+#define RRB_GET_OPTIONAL_SRC(srcfield, type, field, txt, checklength) do { \
+	if (wpa_ft_rrb_get_tlv(srcfield, srcfield##_len, type, \
+				&f_##field##_len, &f_##field) < 0 || \
+	    (checklength > 0 && ((size_t) checklength) != f_##field##_len)) { \
+		wpa_printf(MSG_DEBUG, "FT: Missing optional " #field \
+			   " in %s from " MACSTR, txt, MAC2STR(src_addr)); \
+		f_##field##_len = 0; \
+		f_##field = NULL; \
+	} \
+} while (0)
+
+#define RRB_GET_OPTIONAL(type, field, txt, checklength) \
+	RRB_GET_OPTIONAL_SRC(plain, type, field, txt, checklength)
+#define RRB_GET_OPTIONAL_AUTH(type, field, txt, checklength) \
+	RRB_GET_OPTIONAL_SRC(auth, type, field, txt, checklength)
+
+static int wpa_ft_rrb_send(struct wpa_authenticator *wpa_auth, const u8 *dst,
+			   const u8 *data, size_t data_len)
+{
+	if (wpa_auth->cb->send_ether == NULL)
+		return -1;
+	wpa_printf(MSG_DEBUG, "FT: RRB send to " MACSTR, MAC2STR(dst));
+	return wpa_auth->cb->send_ether(wpa_auth->cb_ctx, dst, ETH_P_RRB,
+					data, data_len);
+}
+
+
+static int wpa_ft_rrb_oui_send(struct wpa_authenticator *wpa_auth,
+			       const u8 *dst, u8 oui_suffix,
+			       const u8 *data, size_t data_len)
+{
+	if (!wpa_auth->cb->send_oui)
+		return -1;
+	wpa_printf(MSG_DEBUG, "FT: RRB-OUI type %u send to " MACSTR " (len=%u)",
+		   oui_suffix, MAC2STR(dst), (unsigned int) data_len);
+	return wpa_auth->cb->send_oui(wpa_auth->cb_ctx, dst, oui_suffix, data,
+				      data_len);
+}
+
+
+static int wpa_ft_action_send(struct wpa_authenticator *wpa_auth,
+			      const u8 *dst, const u8 *data, size_t data_len)
+{
+	if (wpa_auth->cb->send_ft_action == NULL)
+		return -1;
+	return wpa_auth->cb->send_ft_action(wpa_auth->cb_ctx, dst,
+					    data, data_len);
+}
+
+
+static const u8 * wpa_ft_get_psk(struct wpa_authenticator *wpa_auth,
+				 const u8 *addr, const u8 *p2p_dev_addr,
+				 const u8 *prev_psk)
+{
+	if (wpa_auth->cb->get_psk == NULL)
+		return NULL;
+	return wpa_auth->cb->get_psk(wpa_auth->cb_ctx, addr, p2p_dev_addr,
+				     prev_psk, NULL, NULL);
+}
+
+
+static struct wpa_state_machine *
+wpa_ft_add_sta(struct wpa_authenticator *wpa_auth, const u8 *sta_addr)
+{
+	if (wpa_auth->cb->add_sta == NULL)
+		return NULL;
+	return wpa_auth->cb->add_sta(wpa_auth->cb_ctx, sta_addr);
+}
+
+
+static int wpa_ft_set_vlan(struct wpa_authenticator *wpa_auth,
+			   const u8 *sta_addr, struct vlan_description *vlan)
+{
+	if (!wpa_auth->cb->set_vlan)
+		return -1;
+	return wpa_auth->cb->set_vlan(wpa_auth->cb_ctx, sta_addr, vlan);
+}
+
+
+static int wpa_ft_get_vlan(struct wpa_authenticator *wpa_auth,
+			   const u8 *sta_addr, struct vlan_description *vlan)
+{
+	if (!wpa_auth->cb->get_vlan)
+		return -1;
+	return wpa_auth->cb->get_vlan(wpa_auth->cb_ctx, sta_addr, vlan);
+}
+
+
+static int
+wpa_ft_set_identity(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
+		    const u8 *identity, size_t identity_len)
+{
+	if (!wpa_auth->cb->set_identity)
+		return -1;
+	return wpa_auth->cb->set_identity(wpa_auth->cb_ctx, sta_addr, identity,
+					  identity_len);
+}
+
+
+static size_t
+wpa_ft_get_identity(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
+		    const u8 **buf)
+{
+	*buf = NULL;
+	if (!wpa_auth->cb->get_identity)
+		return 0;
+	return wpa_auth->cb->get_identity(wpa_auth->cb_ctx, sta_addr, buf);
+}
+
+
+static int
+wpa_ft_set_radius_cui(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
+		      const u8 *radius_cui, size_t radius_cui_len)
+{
+	if (!wpa_auth->cb->set_radius_cui)
+		return -1;
+	return wpa_auth->cb->set_radius_cui(wpa_auth->cb_ctx, sta_addr,
+					    radius_cui, radius_cui_len);
+}
+
+
+static size_t
+wpa_ft_get_radius_cui(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
+		      const u8 **buf)
+{
+	*buf = NULL;
+	if (!wpa_auth->cb->get_radius_cui)
+		return 0;
+	return wpa_auth->cb->get_radius_cui(wpa_auth->cb_ctx, sta_addr, buf);
+}
+
+
+static void
+wpa_ft_set_session_timeout(struct wpa_authenticator *wpa_auth,
+			    const u8 *sta_addr, int session_timeout)
+{
+	if (!wpa_auth->cb->set_session_timeout)
+		return;
+	wpa_auth->cb->set_session_timeout(wpa_auth->cb_ctx, sta_addr,
+					  session_timeout);
+}
+
+
+static int
+wpa_ft_get_session_timeout(struct wpa_authenticator *wpa_auth,
+			    const u8 *sta_addr)
+{
+	if (!wpa_auth->cb->get_session_timeout)
+		return 0;
+	return wpa_auth->cb->get_session_timeout(wpa_auth->cb_ctx, sta_addr);
+}
+
+
+static int wpa_ft_add_tspec(struct wpa_authenticator *wpa_auth,
+			    const u8 *sta_addr,
+			    u8 *tspec_ie, size_t tspec_ielen)
+{
+	if (wpa_auth->cb->add_tspec == NULL) {
+		wpa_printf(MSG_DEBUG, "FT: add_tspec is not initialized");
+		return -1;
+	}
+	return wpa_auth->cb->add_tspec(wpa_auth->cb_ctx, sta_addr, tspec_ie,
+				       tspec_ielen);
+}
+
+
+#ifdef CONFIG_OCV
+static int wpa_channel_info(struct wpa_authenticator *wpa_auth,
+			       struct wpa_channel_info *ci)
+{
+	if (!wpa_auth->cb->channel_info)
+		return -1;
+	return wpa_auth->cb->channel_info(wpa_auth->cb_ctx, ci);
+}
+#endif /* CONFIG_OCV */
+
+
+int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len)
+{
+	u8 *pos = buf;
+	u8 capab;
+	if (len < 2 + sizeof(struct rsn_mdie))
+		return -1;
+
+	*pos++ = WLAN_EID_MOBILITY_DOMAIN;
+	*pos++ = MOBILITY_DOMAIN_ID_LEN + 1;
+	os_memcpy(pos, conf->mobility_domain, MOBILITY_DOMAIN_ID_LEN);
+	pos += MOBILITY_DOMAIN_ID_LEN;
+	capab = 0;
+	if (conf->ft_over_ds)
+		capab |= RSN_FT_CAPAB_FT_OVER_DS;
+	*pos++ = capab;
+
+	return pos - buf;
+}
+
+
+int wpa_write_ftie(struct wpa_auth_config *conf, int key_mgmt, size_t key_len,
+		   const u8 *r0kh_id, size_t r0kh_id_len,
+		   const u8 *anonce, const u8 *snonce,
+		   u8 *buf, size_t len, const u8 *subelem,
+		   size_t subelem_len, int rsnxe_used)
+{
+	u8 *pos = buf, *ielen;
+	size_t hdrlen;
+	u16 mic_control = rsnxe_used ? FTE_MIC_CTRL_RSNXE_USED : 0;
+
+	if (key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+	    key_len == SHA256_MAC_LEN)
+		hdrlen = sizeof(struct rsn_ftie);
+	else if (key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+		 key_len == SHA384_MAC_LEN)
+		hdrlen = sizeof(struct rsn_ftie_sha384);
+	else if (key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+		 key_len == SHA512_MAC_LEN)
+		hdrlen = sizeof(struct rsn_ftie_sha512);
+	else if (wpa_key_mgmt_sha384(key_mgmt))
+		hdrlen = sizeof(struct rsn_ftie_sha384);
+	else
+		hdrlen = sizeof(struct rsn_ftie);
+
+	if (len < 2 + hdrlen + 2 + FT_R1KH_ID_LEN + 2 + r0kh_id_len +
+	    subelem_len)
+		return -1;
+
+	*pos++ = WLAN_EID_FAST_BSS_TRANSITION;
+	ielen = pos++;
+
+	if (key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+	    key_len == SHA512_MAC_LEN) {
+		struct rsn_ftie_sha512 *hdr = (struct rsn_ftie_sha512 *) pos;
+
+		os_memset(hdr, 0, sizeof(*hdr));
+		pos += sizeof(*hdr);
+		mic_control |= FTE_MIC_LEN_32 << FTE_MIC_CTRL_MIC_LEN_SHIFT;
+		WPA_PUT_LE16(hdr->mic_control, mic_control);
+		if (anonce)
+			os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN);
+		if (snonce)
+			os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN);
+	} else if ((key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+		    key_len == SHA384_MAC_LEN) ||
+		   wpa_key_mgmt_sha384(key_mgmt)) {
+		struct rsn_ftie_sha384 *hdr = (struct rsn_ftie_sha384 *) pos;
+
+		os_memset(hdr, 0, sizeof(*hdr));
+		pos += sizeof(*hdr);
+		mic_control |= FTE_MIC_LEN_24 << FTE_MIC_CTRL_MIC_LEN_SHIFT;
+		WPA_PUT_LE16(hdr->mic_control, mic_control);
+		if (anonce)
+			os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN);
+		if (snonce)
+			os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN);
+	} else {
+		struct rsn_ftie *hdr = (struct rsn_ftie *) pos;
+
+		os_memset(hdr, 0, sizeof(*hdr));
+		pos += sizeof(*hdr);
+		mic_control |= FTE_MIC_LEN_16 << FTE_MIC_CTRL_MIC_LEN_SHIFT;
+		WPA_PUT_LE16(hdr->mic_control, mic_control);
+		if (anonce)
+			os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN);
+		if (snonce)
+			os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN);
+	}
+
+	/* Optional Parameters */
+	*pos++ = FTIE_SUBELEM_R1KH_ID;
+	*pos++ = FT_R1KH_ID_LEN;
+	os_memcpy(pos, conf->r1_key_holder, FT_R1KH_ID_LEN);
+	pos += FT_R1KH_ID_LEN;
+
+	if (r0kh_id) {
+		*pos++ = FTIE_SUBELEM_R0KH_ID;
+		*pos++ = r0kh_id_len;
+		os_memcpy(pos, r0kh_id, r0kh_id_len);
+		pos += r0kh_id_len;
+	}
+
+	if (subelem) {
+		os_memcpy(pos, subelem, subelem_len);
+		pos += subelem_len;
+	}
+
+	*ielen = pos - buf - 2;
+
+	return pos - buf;
+}
+
+
+/* A packet to be handled after seq response */
+struct ft_remote_item {
+	struct dl_list list;
+
+	u8 nonce[FT_RRB_NONCE_LEN];
+	struct os_reltime nonce_ts;
+
+	u8 src_addr[ETH_ALEN];
+	u8 *enc;
+	size_t enc_len;
+	u8 *auth;
+	size_t auth_len;
+	int (*cb)(struct wpa_authenticator *wpa_auth,
+		  const u8 *src_addr,
+		  const u8 *enc, size_t enc_len,
+		  const u8 *auth, size_t auth_len,
+		  int no_defer);
+};
+
+
+static void wpa_ft_rrb_seq_free(struct ft_remote_item *item)
+{
+	eloop_cancel_timeout(wpa_ft_rrb_seq_timeout, ELOOP_ALL_CTX, item);
+	dl_list_del(&item->list);
+	bin_clear_free(item->enc, item->enc_len);
+	os_free(item->auth);
+	os_free(item);
+}
+
+
+static void wpa_ft_rrb_seq_flush(struct wpa_authenticator *wpa_auth,
+				 struct ft_remote_seq *rkh_seq, int cb)
+{
+	struct ft_remote_item *item, *n;
+
+	dl_list_for_each_safe(item, n, &rkh_seq->rx.queue,
+			      struct ft_remote_item, list) {
+		if (cb && item->cb)
+			item->cb(wpa_auth, item->src_addr, item->enc,
+				 item->enc_len, item->auth, item->auth_len, 1);
+		wpa_ft_rrb_seq_free(item);
+	}
+}
+
+
+static void wpa_ft_rrb_seq_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct ft_remote_item *item = timeout_ctx;
+
+	wpa_ft_rrb_seq_free(item);
+}
+
+
+static int
+wpa_ft_rrb_seq_req(struct wpa_authenticator *wpa_auth,
+		   struct ft_remote_seq *rkh_seq, const u8 *src_addr,
+		   const u8 *f_r0kh_id, size_t f_r0kh_id_len,
+		   const u8 *f_r1kh_id, const u8 *key, size_t key_len,
+		   const u8 *enc, size_t enc_len,
+		   const u8 *auth, size_t auth_len,
+		   int (*cb)(struct wpa_authenticator *wpa_auth,
+			     const u8 *src_addr,
+			     const u8 *enc, size_t enc_len,
+			     const u8 *auth, size_t auth_len,
+			     int no_defer))
+{
+	struct ft_remote_item *item = NULL;
+	u8 *packet = NULL;
+	size_t packet_len;
+	struct tlv_list seq_req_auth[] = {
+		{ .type = FT_RRB_NONCE, .len = FT_RRB_NONCE_LEN,
+		  .data = NULL /* to be filled: item->nonce */ },
+		{ .type = FT_RRB_R0KH_ID, .len = f_r0kh_id_len,
+		  .data = f_r0kh_id },
+		{ .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN,
+		  .data = f_r1kh_id },
+		{ .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+	};
+
+	if (dl_list_len(&rkh_seq->rx.queue) >= ftRRBmaxQueueLen) {
+		wpa_printf(MSG_DEBUG, "FT: Sequence number queue too long");
+		goto err;
+	}
+
+	wpa_printf(MSG_DEBUG, "FT: Send sequence number request from " MACSTR
+		   " to " MACSTR,
+		   MAC2STR(wpa_auth->addr), MAC2STR(src_addr));
+	item = os_zalloc(sizeof(*item));
+	if (!item)
+		goto err;
+
+	os_memcpy(item->src_addr, src_addr, ETH_ALEN);
+	item->cb = cb;
+
+	if (random_get_bytes(item->nonce, FT_RRB_NONCE_LEN) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Seq num nonce: out of random bytes");
+		goto err;
+	}
+
+	if (os_get_reltime(&item->nonce_ts) < 0)
+		goto err;
+
+	if (enc && enc_len > 0) {
+		item->enc = os_memdup(enc, enc_len);
+		item->enc_len = enc_len;
+		if (!item->enc)
+			goto err;
+	}
+
+	if (auth && auth_len > 0) {
+		item->auth = os_memdup(auth, auth_len);
+		item->auth_len = auth_len;
+		if (!item->auth)
+			goto err;
+	}
+
+	eloop_register_timeout(ftRRBseqTimeout, 0, wpa_ft_rrb_seq_timeout,
+			       wpa_auth, item);
+
+	seq_req_auth[0].data = item->nonce;
+
+	if (wpa_ft_rrb_build(key, key_len, NULL, NULL, seq_req_auth, NULL,
+			     wpa_auth->addr, FT_PACKET_R0KH_R1KH_SEQ_REQ,
+			     &packet, &packet_len) < 0) {
+		item = NULL; /* some other seq resp might still accept this */
+		goto err;
+	}
+
+	dl_list_add(&rkh_seq->rx.queue, &item->list);
+
+	wpa_ft_rrb_oui_send(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_REQ,
+			    packet, packet_len);
+
+	os_free(packet);
+
+	return 0;
+err:
+	wpa_printf(MSG_DEBUG, "FT: Failed to send sequence number request");
+	if (item) {
+		os_free(item->auth);
+		bin_clear_free(item->enc, item->enc_len);
+		os_free(item);
+	}
+
+	return -1;
+}
+
+
+#define FT_RRB_SEQ_OK    0
+#define FT_RRB_SEQ_DROP  1
+#define FT_RRB_SEQ_DEFER 2
+
+static int
+wpa_ft_rrb_seq_chk(struct ft_remote_seq *rkh_seq, const u8 *src_addr,
+		   const u8 *enc, size_t enc_len,
+		   const u8 *auth, size_t auth_len,
+		   const char *msgtype, int no_defer)
+{
+	const u8 *f_seq;
+	size_t f_seq_len;
+	const struct ft_rrb_seq *msg_both;
+	u32 msg_seq, msg_off, rkh_off;
+	struct os_reltime now;
+	unsigned int i;
+
+	RRB_GET_AUTH(FT_RRB_SEQ, seq, msgtype, sizeof(*msg_both));
+	wpa_hexdump(MSG_DEBUG, "FT: sequence number", f_seq, f_seq_len);
+	msg_both = (const struct ft_rrb_seq *) f_seq;
+
+	if (rkh_seq->rx.num_last == 0) {
+		/* first packet from remote */
+		goto defer;
+	}
+
+	if (le_to_host32(msg_both->dom) != rkh_seq->rx.dom) {
+		/* remote might have rebooted */
+		goto defer;
+	}
+
+	if (os_get_reltime(&now) == 0) {
+		u32 msg_ts_now_remote, msg_ts_off;
+		struct os_reltime now_remote;
+
+		os_reltime_sub(&now, &rkh_seq->rx.time_offset, &now_remote);
+		msg_ts_now_remote = now_remote.sec;
+		msg_ts_off = le_to_host32(msg_both->ts) -
+			(msg_ts_now_remote - ftRRBseqTimeout);
+		if (msg_ts_off > 2 * ftRRBseqTimeout)
+			goto defer;
+	}
+
+	msg_seq = le_to_host32(msg_both->seq);
+	rkh_off = rkh_seq->rx.last[rkh_seq->rx.offsetidx];
+	msg_off = msg_seq - rkh_off;
+	if (msg_off > 0xC0000000)
+		goto out; /* too old message, drop it */
+
+	if (msg_off <= 0x40000000) {
+		for (i = 0; i < rkh_seq->rx.num_last; i++) {
+			if (rkh_seq->rx.last[i] == msg_seq)
+				goto out; /* duplicate message, drop it */
+		}
+
+		return FT_RRB_SEQ_OK;
+	}
+
+defer:
+	if (no_defer)
+		goto out;
+
+	wpa_printf(MSG_DEBUG, "FT: Possibly invalid sequence number in %s from "
+		   MACSTR, msgtype, MAC2STR(src_addr));
+
+	return FT_RRB_SEQ_DEFER;
+out:
+	wpa_printf(MSG_DEBUG, "FT: Invalid sequence number in %s from " MACSTR,
+		   msgtype, MAC2STR(src_addr));
+
+	return FT_RRB_SEQ_DROP;
+}
+
+
+static void
+wpa_ft_rrb_seq_accept(struct wpa_authenticator *wpa_auth,
+		      struct ft_remote_seq *rkh_seq, const u8 *src_addr,
+		      const u8 *auth, size_t auth_len,
+		      const char *msgtype)
+{
+	const u8 *f_seq;
+	size_t f_seq_len;
+	const struct ft_rrb_seq *msg_both;
+	u32 msg_seq, msg_off, min_off, rkh_off;
+	int minidx = 0;
+	unsigned int i;
+
+	RRB_GET_AUTH(FT_RRB_SEQ, seq, msgtype, sizeof(*msg_both));
+	msg_both = (const struct ft_rrb_seq *) f_seq;
+
+	msg_seq = le_to_host32(msg_both->seq);
+
+	if (rkh_seq->rx.num_last < FT_REMOTE_SEQ_BACKLOG) {
+		rkh_seq->rx.last[rkh_seq->rx.num_last] = msg_seq;
+		rkh_seq->rx.num_last++;
+		return;
+	}
+
+	rkh_off = rkh_seq->rx.last[rkh_seq->rx.offsetidx];
+	for (i = 0; i < rkh_seq->rx.num_last; i++) {
+		msg_off = rkh_seq->rx.last[i] - rkh_off;
+		min_off = rkh_seq->rx.last[minidx] - rkh_off;
+		if (msg_off < min_off && i != rkh_seq->rx.offsetidx)
+			minidx = i;
+	}
+	rkh_seq->rx.last[rkh_seq->rx.offsetidx] = msg_seq;
+	rkh_seq->rx.offsetidx = minidx;
+
+	return;
+out:
+	/* RRB_GET_AUTH should never fail here as
+	 * wpa_ft_rrb_seq_chk() verified FT_RRB_SEQ presence. */
+	wpa_printf(MSG_ERROR, "FT: %s() failed", __func__);
+}
+
+
+static int wpa_ft_new_seq(struct ft_remote_seq *rkh_seq,
+			  struct ft_rrb_seq *f_seq)
+{
+	struct os_reltime now;
+
+	if (os_get_reltime(&now) < 0)
+		return -1;
+
+	if (!rkh_seq->tx.dom) {
+		if (random_get_bytes((u8 *) &rkh_seq->tx.seq,
+				     sizeof(rkh_seq->tx.seq))) {
+			wpa_printf(MSG_ERROR,
+				   "FT: Failed to get random data for sequence number initialization");
+			rkh_seq->tx.seq = now.usec;
+		}
+		if (random_get_bytes((u8 *) &rkh_seq->tx.dom,
+				     sizeof(rkh_seq->tx.dom))) {
+			wpa_printf(MSG_ERROR,
+				   "FT: Failed to get random data for sequence number initialization");
+			rkh_seq->tx.dom = now.usec;
+		}
+		rkh_seq->tx.dom |= 1;
+	}
+
+	f_seq->dom = host_to_le32(rkh_seq->tx.dom);
+	f_seq->seq = host_to_le32(rkh_seq->tx.seq);
+	f_seq->ts = host_to_le32(now.sec);
+
+	rkh_seq->tx.seq++;
+
+	return 0;
+}
+
+
+struct wpa_ft_pmk_r0_sa {
+	struct dl_list list;
+	u8 pmk_r0[PMK_LEN_MAX];
+	size_t pmk_r0_len;
+	u8 pmk_r0_name[WPA_PMK_NAME_LEN];
+	u8 spa[ETH_ALEN];
+	int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */
+	struct vlan_description *vlan;
+	os_time_t expiration; /* 0 for no expiration */
+	u8 *identity;
+	size_t identity_len;
+	u8 *radius_cui;
+	size_t radius_cui_len;
+	os_time_t session_timeout; /* 0 for no expiration */
+	/* TODO: radius_class, EAP type */
+	int pmk_r1_pushed;
+};
+
+struct wpa_ft_pmk_r1_sa {
+	struct dl_list list;
+	u8 pmk_r1[PMK_LEN_MAX];
+	size_t pmk_r1_len;
+	u8 pmk_r1_name[WPA_PMK_NAME_LEN];
+	u8 spa[ETH_ALEN];
+	int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */
+	struct vlan_description *vlan;
+	u8 *identity;
+	size_t identity_len;
+	u8 *radius_cui;
+	size_t radius_cui_len;
+	os_time_t session_timeout; /* 0 for no expiration */
+	/* TODO: radius_class, EAP type */
+};
+
+struct wpa_ft_pmk_cache {
+	struct dl_list pmk_r0; /* struct wpa_ft_pmk_r0_sa */
+	struct dl_list pmk_r1; /* struct wpa_ft_pmk_r1_sa */
+};
+
+
+static void wpa_ft_expire_pmk_r0(void *eloop_ctx, void *timeout_ctx);
+static void wpa_ft_expire_pmk_r1(void *eloop_ctx, void *timeout_ctx);
+
+
+static void wpa_ft_free_pmk_r0(struct wpa_ft_pmk_r0_sa *r0)
+{
+	if (!r0)
+		return;
+
+	dl_list_del(&r0->list);
+	eloop_cancel_timeout(wpa_ft_expire_pmk_r0, r0, NULL);
+
+	os_memset(r0->pmk_r0, 0, PMK_LEN_MAX);
+	os_free(r0->vlan);
+	os_free(r0->identity);
+	os_free(r0->radius_cui);
+	os_free(r0);
+}
+
+
+static void wpa_ft_expire_pmk_r0(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_ft_pmk_r0_sa *r0 = eloop_ctx;
+	struct os_reltime now;
+	int expires_in;
+	int session_timeout;
+
+	os_get_reltime(&now);
+
+	if (!r0)
+		return;
+
+	expires_in = r0->expiration - now.sec;
+	session_timeout = r0->session_timeout - now.sec;
+	/* conditions to remove from cache:
+	 * a) r0->expiration is set and hit
+	 * -or-
+	 * b) r0->session_timeout is set and hit
+	 */
+	if ((!r0->expiration || expires_in > 0) &&
+	    (!r0->session_timeout || session_timeout > 0)) {
+		wpa_printf(MSG_ERROR,
+			   "FT: %s() called for non-expired entry %p",
+			   __func__, r0);
+		eloop_cancel_timeout(wpa_ft_expire_pmk_r0, r0, NULL);
+		if (r0->expiration && expires_in > 0)
+			eloop_register_timeout(expires_in + 1, 0,
+					       wpa_ft_expire_pmk_r0, r0, NULL);
+		if (r0->session_timeout && session_timeout > 0)
+			eloop_register_timeout(session_timeout + 1, 0,
+					       wpa_ft_expire_pmk_r0, r0, NULL);
+		return;
+	}
+
+	wpa_ft_free_pmk_r0(r0);
+}
+
+
+static void wpa_ft_free_pmk_r1(struct wpa_ft_pmk_r1_sa *r1)
+{
+	if (!r1)
+		return;
+
+	dl_list_del(&r1->list);
+	eloop_cancel_timeout(wpa_ft_expire_pmk_r1, r1, NULL);
+
+	os_memset(r1->pmk_r1, 0, PMK_LEN_MAX);
+	os_free(r1->vlan);
+	os_free(r1->identity);
+	os_free(r1->radius_cui);
+	os_free(r1);
+}
+
+
+static void wpa_ft_expire_pmk_r1(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_ft_pmk_r1_sa *r1 = eloop_ctx;
+
+	wpa_ft_free_pmk_r1(r1);
+}
+
+
+struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void)
+{
+	struct wpa_ft_pmk_cache *cache;
+
+	cache = os_zalloc(sizeof(*cache));
+	if (cache) {
+		dl_list_init(&cache->pmk_r0);
+		dl_list_init(&cache->pmk_r1);
+	}
+
+	return cache;
+}
+
+
+void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache)
+{
+	struct wpa_ft_pmk_r0_sa *r0, *r0prev;
+	struct wpa_ft_pmk_r1_sa *r1, *r1prev;
+
+	dl_list_for_each_safe(r0, r0prev, &cache->pmk_r0,
+			      struct wpa_ft_pmk_r0_sa, list)
+		wpa_ft_free_pmk_r0(r0);
+
+	dl_list_for_each_safe(r1, r1prev, &cache->pmk_r1,
+			      struct wpa_ft_pmk_r1_sa, list)
+		wpa_ft_free_pmk_r1(r1);
+
+	os_free(cache);
+}
+
+
+static int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth,
+			       const u8 *spa, const u8 *pmk_r0,
+			       size_t pmk_r0_len,
+			       const u8 *pmk_r0_name, int pairwise,
+			       const struct vlan_description *vlan,
+			       int expires_in, int session_timeout,
+			       const u8 *identity, size_t identity_len,
+			       const u8 *radius_cui, size_t radius_cui_len)
+{
+	struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
+	struct wpa_ft_pmk_r0_sa *r0;
+	struct os_reltime now;
+
+	/* TODO: add limit on number of entries in cache */
+	os_get_reltime(&now);
+
+	r0 = os_zalloc(sizeof(*r0));
+	if (r0 == NULL)
+		return -1;
+
+	os_memcpy(r0->pmk_r0, pmk_r0, pmk_r0_len);
+	r0->pmk_r0_len = pmk_r0_len;
+	os_memcpy(r0->pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN);
+	os_memcpy(r0->spa, spa, ETH_ALEN);
+	r0->pairwise = pairwise;
+	if (expires_in > 0)
+		r0->expiration = now.sec + expires_in;
+	if (vlan && vlan->notempty) {
+		r0->vlan = os_zalloc(sizeof(*vlan));
+		if (!r0->vlan) {
+			bin_clear_free(r0, sizeof(*r0));
+			return -1;
+		}
+		*r0->vlan = *vlan;
+	}
+	if (identity) {
+		r0->identity = os_malloc(identity_len);
+		if (r0->identity) {
+			os_memcpy(r0->identity, identity, identity_len);
+			r0->identity_len = identity_len;
+		}
+	}
+	if (radius_cui) {
+		r0->radius_cui = os_malloc(radius_cui_len);
+		if (r0->radius_cui) {
+			os_memcpy(r0->radius_cui, radius_cui, radius_cui_len);
+			r0->radius_cui_len = radius_cui_len;
+		}
+	}
+	if (session_timeout > 0)
+		r0->session_timeout = now.sec + session_timeout;
+
+	dl_list_add(&cache->pmk_r0, &r0->list);
+	if (expires_in > 0)
+		eloop_register_timeout(expires_in + 1, 0, wpa_ft_expire_pmk_r0,
+				       r0, NULL);
+	if (session_timeout > 0)
+		eloop_register_timeout(session_timeout + 1, 0,
+				       wpa_ft_expire_pmk_r0, r0, NULL);
+
+	return 0;
+}
+
+
+static int wpa_ft_fetch_pmk_r0(struct wpa_authenticator *wpa_auth,
+			       const u8 *spa, const u8 *pmk_r0_name,
+			       const struct wpa_ft_pmk_r0_sa **r0_out)
+{
+	struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
+	struct wpa_ft_pmk_r0_sa *r0;
+	struct os_reltime now;
+
+	os_get_reltime(&now);
+	dl_list_for_each(r0, &cache->pmk_r0, struct wpa_ft_pmk_r0_sa, list) {
+		if (os_memcmp(r0->spa, spa, ETH_ALEN) == 0 &&
+		    os_memcmp_const(r0->pmk_r0_name, pmk_r0_name,
+				    WPA_PMK_NAME_LEN) == 0) {
+			*r0_out = r0;
+			return 0;
+		}
+	}
+
+	*r0_out = NULL;
+	return -1;
+}
+
+
+static int wpa_ft_store_pmk_r1(struct wpa_authenticator *wpa_auth,
+			       const u8 *spa, const u8 *pmk_r1,
+			       size_t pmk_r1_len,
+			       const u8 *pmk_r1_name, int pairwise,
+			       const struct vlan_description *vlan,
+			       int expires_in, int session_timeout,
+			       const u8 *identity, size_t identity_len,
+			       const u8 *radius_cui, size_t radius_cui_len)
+{
+	struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
+	int max_expires_in = wpa_auth->conf.r1_max_key_lifetime;
+	struct wpa_ft_pmk_r1_sa *r1;
+	struct os_reltime now;
+
+	/* TODO: limit on number of entries in cache */
+	os_get_reltime(&now);
+
+	if (max_expires_in && (max_expires_in < expires_in || expires_in == 0))
+		expires_in = max_expires_in;
+
+	r1 = os_zalloc(sizeof(*r1));
+	if (r1 == NULL)
+		return -1;
+
+	os_memcpy(r1->pmk_r1, pmk_r1, pmk_r1_len);
+	r1->pmk_r1_len = pmk_r1_len;
+	os_memcpy(r1->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN);
+	os_memcpy(r1->spa, spa, ETH_ALEN);
+	r1->pairwise = pairwise;
+	if (vlan && vlan->notempty) {
+		r1->vlan = os_zalloc(sizeof(*vlan));
+		if (!r1->vlan) {
+			bin_clear_free(r1, sizeof(*r1));
+			return -1;
+		}
+		*r1->vlan = *vlan;
+	}
+	if (identity) {
+		r1->identity = os_malloc(identity_len);
+		if (r1->identity) {
+			os_memcpy(r1->identity, identity, identity_len);
+			r1->identity_len = identity_len;
+		}
+	}
+	if (radius_cui) {
+		r1->radius_cui = os_malloc(radius_cui_len);
+		if (r1->radius_cui) {
+			os_memcpy(r1->radius_cui, radius_cui, radius_cui_len);
+			r1->radius_cui_len = radius_cui_len;
+		}
+	}
+	if (session_timeout > 0)
+		r1->session_timeout = now.sec + session_timeout;
+
+	dl_list_add(&cache->pmk_r1, &r1->list);
+
+	if (expires_in > 0)
+		eloop_register_timeout(expires_in + 1, 0, wpa_ft_expire_pmk_r1,
+				       r1, NULL);
+	if (session_timeout > 0)
+		eloop_register_timeout(session_timeout + 1, 0,
+				       wpa_ft_expire_pmk_r1, r1, NULL);
+
+	return 0;
+}
+
+
+int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth,
+			const u8 *spa, const u8 *pmk_r1_name,
+			u8 *pmk_r1, size_t *pmk_r1_len, int *pairwise,
+			struct vlan_description *vlan,
+			const u8 **identity, size_t *identity_len,
+			const u8 **radius_cui, size_t *radius_cui_len,
+			int *session_timeout)
+{
+	struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
+	struct wpa_ft_pmk_r1_sa *r1;
+	struct os_reltime now;
+
+	os_get_reltime(&now);
+
+	dl_list_for_each(r1, &cache->pmk_r1, struct wpa_ft_pmk_r1_sa, list) {
+		if (os_memcmp(r1->spa, spa, ETH_ALEN) == 0 &&
+		    os_memcmp_const(r1->pmk_r1_name, pmk_r1_name,
+				    WPA_PMK_NAME_LEN) == 0) {
+			os_memcpy(pmk_r1, r1->pmk_r1, r1->pmk_r1_len);
+			*pmk_r1_len = r1->pmk_r1_len;
+			if (pairwise)
+				*pairwise = r1->pairwise;
+			if (vlan && r1->vlan)
+				*vlan = *r1->vlan;
+			if (vlan && !r1->vlan)
+				os_memset(vlan, 0, sizeof(*vlan));
+			if (identity && identity_len) {
+				*identity = r1->identity;
+				*identity_len = r1->identity_len;
+			}
+			if (radius_cui && radius_cui_len) {
+				*radius_cui = r1->radius_cui;
+				*radius_cui_len = r1->radius_cui_len;
+			}
+			if (session_timeout && r1->session_timeout > now.sec)
+				*session_timeout = r1->session_timeout -
+					now.sec;
+			else if (session_timeout && r1->session_timeout)
+				*session_timeout = 1;
+			else if (session_timeout)
+				*session_timeout = 0;
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+
+static int wpa_ft_rrb_init_r0kh_seq(struct ft_remote_r0kh *r0kh)
+{
+	if (r0kh->seq)
+		return 0;
+
+	r0kh->seq = os_zalloc(sizeof(*r0kh->seq));
+	if (!r0kh->seq) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to allocate r0kh->seq");
+		return -1;
+	}
+
+	dl_list_init(&r0kh->seq->rx.queue);
+
+	return 0;
+}
+
+
+static void wpa_ft_rrb_lookup_r0kh(struct wpa_authenticator *wpa_auth,
+				   const u8 *f_r0kh_id, size_t f_r0kh_id_len,
+				   struct ft_remote_r0kh **r0kh_out,
+				   struct ft_remote_r0kh **r0kh_wildcard)
+{
+	struct ft_remote_r0kh *r0kh;
+
+	*r0kh_wildcard = NULL;
+	*r0kh_out = NULL;
+
+	if (wpa_auth->conf.r0kh_list)
+		r0kh = *wpa_auth->conf.r0kh_list;
+	else
+		r0kh = NULL;
+	for (; r0kh; r0kh = r0kh->next) {
+		if (r0kh->id_len == 1 && r0kh->id[0] == '*')
+			*r0kh_wildcard = r0kh;
+		if (f_r0kh_id && r0kh->id_len == f_r0kh_id_len &&
+		    os_memcmp_const(f_r0kh_id, r0kh->id, f_r0kh_id_len) == 0)
+			*r0kh_out = r0kh;
+	}
+
+	if (!*r0kh_out && !*r0kh_wildcard)
+		wpa_printf(MSG_DEBUG, "FT: No matching R0KH found");
+
+	if (*r0kh_out && wpa_ft_rrb_init_r0kh_seq(*r0kh_out) < 0)
+		*r0kh_out = NULL;
+}
+
+
+static int wpa_ft_rrb_init_r1kh_seq(struct ft_remote_r1kh *r1kh)
+{
+	if (r1kh->seq)
+		return 0;
+
+	r1kh->seq = os_zalloc(sizeof(*r1kh->seq));
+	if (!r1kh->seq) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to allocate r1kh->seq");
+		return -1;
+	}
+
+	dl_list_init(&r1kh->seq->rx.queue);
+
+	return 0;
+}
+
+
+static void wpa_ft_rrb_lookup_r1kh(struct wpa_authenticator *wpa_auth,
+				   const u8 *f_r1kh_id,
+				   struct ft_remote_r1kh **r1kh_out,
+				   struct ft_remote_r1kh **r1kh_wildcard)
+{
+	struct ft_remote_r1kh *r1kh;
+
+	*r1kh_wildcard = NULL;
+	*r1kh_out = NULL;
+
+	if (wpa_auth->conf.r1kh_list)
+		r1kh = *wpa_auth->conf.r1kh_list;
+	else
+		r1kh = NULL;
+	for (; r1kh; r1kh = r1kh->next) {
+		if (is_zero_ether_addr(r1kh->addr) &&
+		    is_zero_ether_addr(r1kh->id))
+			*r1kh_wildcard = r1kh;
+		if (f_r1kh_id &&
+		    os_memcmp_const(r1kh->id, f_r1kh_id, FT_R1KH_ID_LEN) == 0)
+			*r1kh_out = r1kh;
+	}
+
+	if (!*r1kh_out && !*r1kh_wildcard)
+		wpa_printf(MSG_DEBUG, "FT: No matching R1KH found");
+
+	if (*r1kh_out && wpa_ft_rrb_init_r1kh_seq(*r1kh_out) < 0)
+		*r1kh_out = NULL;
+}
+
+
+static int wpa_ft_rrb_check_r0kh(struct wpa_authenticator *wpa_auth,
+				 const u8 *f_r0kh_id, size_t f_r0kh_id_len)
+{
+	if (f_r0kh_id_len != wpa_auth->conf.r0_key_holder_len ||
+	    os_memcmp_const(f_r0kh_id, wpa_auth->conf.r0_key_holder,
+			    f_r0kh_id_len) != 0)
+		return -1;
+
+	return 0;
+}
+
+
+static int wpa_ft_rrb_check_r1kh(struct wpa_authenticator *wpa_auth,
+				 const u8 *f_r1kh_id)
+{
+	if (os_memcmp_const(f_r1kh_id, wpa_auth->conf.r1_key_holder,
+			    FT_R1KH_ID_LEN) != 0)
+		return -1;
+
+	return 0;
+}
+
+
+static void wpa_ft_rrb_del_r0kh(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_authenticator *wpa_auth = eloop_ctx;
+	struct ft_remote_r0kh *r0kh, *prev = NULL;
+
+	if (!wpa_auth->conf.r0kh_list)
+		return;
+
+	for (r0kh = *wpa_auth->conf.r0kh_list; r0kh; r0kh = r0kh->next) {
+		if (r0kh == timeout_ctx)
+			break;
+		prev = r0kh;
+	}
+	if (!r0kh)
+		return;
+	if (prev)
+		prev->next = r0kh->next;
+	else
+		*wpa_auth->conf.r0kh_list = r0kh->next;
+	if (r0kh->seq)
+		wpa_ft_rrb_seq_flush(wpa_auth, r0kh->seq, 0);
+	os_free(r0kh->seq);
+	os_free(r0kh);
+}
+
+
+static void wpa_ft_rrb_r0kh_replenish(struct wpa_authenticator *wpa_auth,
+				      struct ft_remote_r0kh *r0kh, int timeout)
+{
+	if (timeout > 0)
+		eloop_replenish_timeout(timeout, 0, wpa_ft_rrb_del_r0kh,
+					wpa_auth, r0kh);
+}
+
+
+static void wpa_ft_rrb_r0kh_timeout(struct wpa_authenticator *wpa_auth,
+				    struct ft_remote_r0kh *r0kh, int timeout)
+{
+	eloop_cancel_timeout(wpa_ft_rrb_del_r0kh, wpa_auth, r0kh);
+
+	if (timeout > 0)
+		eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r0kh,
+				       wpa_auth, r0kh);
+}
+
+
+static struct ft_remote_r0kh *
+wpa_ft_rrb_add_r0kh(struct wpa_authenticator *wpa_auth,
+		    struct ft_remote_r0kh *r0kh_wildcard,
+		    const u8 *src_addr, const u8 *r0kh_id, size_t id_len,
+		    int timeout)
+{
+	struct ft_remote_r0kh *r0kh;
+
+	if (!wpa_auth->conf.r0kh_list)
+		return NULL;
+
+	r0kh = os_zalloc(sizeof(*r0kh));
+	if (!r0kh)
+		return NULL;
+
+	if (src_addr)
+		os_memcpy(r0kh->addr, src_addr, sizeof(r0kh->addr));
+
+	if (id_len > FT_R0KH_ID_MAX_LEN)
+		id_len = FT_R0KH_ID_MAX_LEN;
+	os_memcpy(r0kh->id, r0kh_id, id_len);
+	r0kh->id_len = id_len;
+
+	os_memcpy(r0kh->key, r0kh_wildcard->key, sizeof(r0kh->key));
+
+	r0kh->next = *wpa_auth->conf.r0kh_list;
+	*wpa_auth->conf.r0kh_list = r0kh;
+
+	if (timeout > 0)
+		eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r0kh,
+				       wpa_auth, r0kh);
+
+	if (wpa_ft_rrb_init_r0kh_seq(r0kh) < 0)
+		return NULL;
+
+	return r0kh;
+}
+
+
+static void wpa_ft_rrb_del_r1kh(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_authenticator *wpa_auth = eloop_ctx;
+	struct ft_remote_r1kh *r1kh, *prev = NULL;
+
+	if (!wpa_auth->conf.r1kh_list)
+		return;
+
+	for (r1kh = *wpa_auth->conf.r1kh_list; r1kh; r1kh = r1kh->next) {
+		if (r1kh == timeout_ctx)
+			break;
+		prev = r1kh;
+	}
+	if (!r1kh)
+		return;
+	if (prev)
+		prev->next = r1kh->next;
+	else
+		*wpa_auth->conf.r1kh_list = r1kh->next;
+	if (r1kh->seq)
+		wpa_ft_rrb_seq_flush(wpa_auth, r1kh->seq, 0);
+	os_free(r1kh->seq);
+	os_free(r1kh);
+}
+
+
+static void wpa_ft_rrb_r1kh_replenish(struct wpa_authenticator *wpa_auth,
+				      struct ft_remote_r1kh *r1kh, int timeout)
+{
+	if (timeout > 0)
+		eloop_replenish_timeout(timeout, 0, wpa_ft_rrb_del_r1kh,
+					wpa_auth, r1kh);
+}
+
+
+static struct ft_remote_r1kh *
+wpa_ft_rrb_add_r1kh(struct wpa_authenticator *wpa_auth,
+		    struct ft_remote_r1kh *r1kh_wildcard,
+		    const u8 *src_addr, const u8 *r1kh_id, int timeout)
+{
+	struct ft_remote_r1kh *r1kh;
+
+	if (!wpa_auth->conf.r1kh_list)
+		return NULL;
+
+	r1kh = os_zalloc(sizeof(*r1kh));
+	if (!r1kh)
+		return NULL;
+
+	os_memcpy(r1kh->addr, src_addr, sizeof(r1kh->addr));
+	os_memcpy(r1kh->id, r1kh_id, sizeof(r1kh->id));
+	os_memcpy(r1kh->key, r1kh_wildcard->key, sizeof(r1kh->key));
+	r1kh->next = *wpa_auth->conf.r1kh_list;
+	*wpa_auth->conf.r1kh_list = r1kh;
+
+	if (timeout > 0)
+		eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r1kh,
+				       wpa_auth, r1kh);
+
+	if (wpa_ft_rrb_init_r1kh_seq(r1kh) < 0)
+		return NULL;
+
+	return r1kh;
+}
+
+
+void wpa_ft_sta_deinit(struct wpa_state_machine *sm)
+{
+	eloop_cancel_timeout(wpa_ft_expire_pull, sm, NULL);
+}
+
+
+static void wpa_ft_deinit_seq(struct wpa_authenticator *wpa_auth)
+{
+	struct ft_remote_r0kh *r0kh;
+	struct ft_remote_r1kh *r1kh;
+
+	eloop_cancel_timeout(wpa_ft_rrb_seq_timeout, wpa_auth, ELOOP_ALL_CTX);
+
+	if (wpa_auth->conf.r0kh_list)
+		r0kh = *wpa_auth->conf.r0kh_list;
+	else
+		r0kh = NULL;
+	for (; r0kh; r0kh = r0kh->next) {
+		if (!r0kh->seq)
+			continue;
+		wpa_ft_rrb_seq_flush(wpa_auth, r0kh->seq, 0);
+		os_free(r0kh->seq);
+		r0kh->seq = NULL;
+	}
+
+	if (wpa_auth->conf.r1kh_list)
+		r1kh = *wpa_auth->conf.r1kh_list;
+	else
+		r1kh = NULL;
+	for (; r1kh; r1kh = r1kh->next) {
+		if (!r1kh->seq)
+			continue;
+		wpa_ft_rrb_seq_flush(wpa_auth, r1kh->seq, 0);
+		os_free(r1kh->seq);
+		r1kh->seq = NULL;
+	}
+}
+
+
+static void wpa_ft_deinit_rkh_tmp(struct wpa_authenticator *wpa_auth)
+{
+	struct ft_remote_r0kh *r0kh, *r0kh_next, *r0kh_prev = NULL;
+	struct ft_remote_r1kh *r1kh, *r1kh_next, *r1kh_prev = NULL;
+
+	if (wpa_auth->conf.r0kh_list)
+		r0kh = *wpa_auth->conf.r0kh_list;
+	else
+		r0kh = NULL;
+	while (r0kh) {
+		r0kh_next = r0kh->next;
+		if (eloop_cancel_timeout(wpa_ft_rrb_del_r0kh, wpa_auth,
+					 r0kh) > 0) {
+			if (r0kh_prev)
+				r0kh_prev->next = r0kh_next;
+			else
+				*wpa_auth->conf.r0kh_list = r0kh_next;
+			os_free(r0kh);
+		} else {
+			r0kh_prev = r0kh;
+		}
+		r0kh = r0kh_next;
+	}
+
+	if (wpa_auth->conf.r1kh_list)
+		r1kh = *wpa_auth->conf.r1kh_list;
+	else
+		r1kh = NULL;
+	while (r1kh) {
+		r1kh_next = r1kh->next;
+		if (eloop_cancel_timeout(wpa_ft_rrb_del_r1kh, wpa_auth,
+					 r1kh) > 0) {
+			if (r1kh_prev)
+				r1kh_prev->next = r1kh_next;
+			else
+				*wpa_auth->conf.r1kh_list = r1kh_next;
+			os_free(r1kh);
+		} else {
+			r1kh_prev = r1kh;
+		}
+		r1kh = r1kh_next;
+	}
+}
+
+
+void wpa_ft_deinit(struct wpa_authenticator *wpa_auth)
+{
+	wpa_ft_deinit_seq(wpa_auth);
+	wpa_ft_deinit_rkh_tmp(wpa_auth);
+}
+
+
+static void wpa_ft_block_r0kh(struct wpa_authenticator *wpa_auth,
+			      const u8 *f_r0kh_id, size_t f_r0kh_id_len)
+{
+	struct ft_remote_r0kh *r0kh, *r0kh_wildcard;
+
+	if (!wpa_auth->conf.rkh_neg_timeout)
+		return;
+
+	wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len,
+			       &r0kh, &r0kh_wildcard);
+
+	if (!r0kh_wildcard) {
+		/* r0kh removed after neg_timeout and might need re-adding */
+		return;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "FT: Temporarily block R0KH-ID",
+		    f_r0kh_id, f_r0kh_id_len);
+
+	if (r0kh) {
+		wpa_ft_rrb_r0kh_timeout(wpa_auth, r0kh,
+					wpa_auth->conf.rkh_neg_timeout);
+		os_memset(r0kh->addr, 0, ETH_ALEN);
+	} else
+		wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard, NULL, f_r0kh_id,
+				    f_r0kh_id_len,
+				    wpa_auth->conf.rkh_neg_timeout);
+}
+
+
+static void wpa_ft_expire_pull(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_state_machine *sm = eloop_ctx;
+
+	wpa_printf(MSG_DEBUG, "FT: Timeout pending pull request for " MACSTR,
+		   MAC2STR(sm->addr));
+	if (sm->ft_pending_pull_left_retries <= 0)
+		wpa_ft_block_r0kh(sm->wpa_auth, sm->r0kh_id, sm->r0kh_id_len);
+
+	/* cancel multiple timeouts */
+	eloop_cancel_timeout(wpa_ft_expire_pull, sm, NULL);
+	ft_finish_pull(sm);
+}
+
+
+static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm,
+			      const u8 *ies, size_t ies_len,
+			      const u8 *pmk_r0_name)
+{
+	struct ft_remote_r0kh *r0kh, *r0kh_wildcard;
+	u8 *packet = NULL;
+	const u8 *key, *f_r1kh_id = sm->wpa_auth->conf.r1_key_holder;
+	size_t packet_len, key_len;
+	struct ft_rrb_seq f_seq;
+	int tsecs, tusecs, first;
+	struct wpabuf *ft_pending_req_ies;
+	int r0kh_timeout;
+	struct tlv_list req_enc[] = {
+		{ .type = FT_RRB_PMK_R0_NAME, .len = WPA_PMK_NAME_LEN,
+		  .data = pmk_r0_name },
+		{ .type = FT_RRB_S1KH_ID, .len = ETH_ALEN,
+		  .data = sm->addr },
+		{ .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+	};
+	struct tlv_list req_auth[] = {
+		{ .type = FT_RRB_NONCE, .len = FT_RRB_NONCE_LEN,
+		  .data = sm->ft_pending_pull_nonce },
+		{ .type = FT_RRB_SEQ, .len = sizeof(f_seq),
+		  .data = (u8 *) &f_seq },
+		{ .type = FT_RRB_R0KH_ID, .len = sm->r0kh_id_len,
+		  .data = sm->r0kh_id },
+		{ .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN,
+		  .data = f_r1kh_id },
+		{ .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+	};
+
+	if (sm->ft_pending_pull_left_retries <= 0)
+		return -1;
+	first = sm->ft_pending_pull_left_retries ==
+		sm->wpa_auth->conf.rkh_pull_retries;
+	sm->ft_pending_pull_left_retries--;
+
+	wpa_ft_rrb_lookup_r0kh(sm->wpa_auth, sm->r0kh_id, sm->r0kh_id_len,
+			       &r0kh, &r0kh_wildcard);
+
+	/* Keep r0kh sufficiently long in the list for seq num check */
+	r0kh_timeout = sm->wpa_auth->conf.rkh_pull_timeout / 1000 +
+		1 + ftRRBseqTimeout;
+	if (r0kh) {
+		wpa_ft_rrb_r0kh_replenish(sm->wpa_auth, r0kh, r0kh_timeout);
+	} else if (r0kh_wildcard) {
+		wpa_printf(MSG_DEBUG, "FT: Using wildcard R0KH-ID");
+		/* r0kh->addr: updated by SEQ_RESP and wpa_ft_expire_pull */
+		r0kh = wpa_ft_rrb_add_r0kh(sm->wpa_auth, r0kh_wildcard,
+					   r0kh_wildcard->addr,
+					   sm->r0kh_id, sm->r0kh_id_len,
+					   r0kh_timeout);
+	}
+	if (r0kh == NULL) {
+		wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID",
+			    sm->r0kh_id, sm->r0kh_id_len);
+		return -1;
+	}
+	if (is_zero_ether_addr(r0kh->addr)) {
+		wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID is temporarily blocked",
+			    sm->r0kh_id, sm->r0kh_id_len);
+		return -1;
+	}
+	if (os_memcmp(r0kh->addr, sm->wpa_auth->addr, ETH_ALEN) == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "FT: R0KH-ID points to self - no matching key available");
+		return -1;
+	}
+
+	key = r0kh->key;
+	key_len = sizeof(r0kh->key);
+
+	if (r0kh->seq->rx.num_last == 0) {
+		/* A sequence request will be sent out anyway when pull
+		 * response is received. Send it out now to avoid one RTT. */
+		wpa_ft_rrb_seq_req(sm->wpa_auth, r0kh->seq, r0kh->addr,
+				   r0kh->id, r0kh->id_len, f_r1kh_id, key,
+				   key_len, NULL, 0, NULL, 0, NULL);
+	}
+
+	wpa_printf(MSG_DEBUG, "FT: Send PMK-R1 pull request from " MACSTR
+		   " to remote R0KH address " MACSTR,
+		   MAC2STR(sm->wpa_auth->addr), MAC2STR(r0kh->addr));
+
+	if (first &&
+	    random_get_bytes(sm->ft_pending_pull_nonce, FT_RRB_NONCE_LEN) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to get random data for "
+			   "nonce");
+		return -1;
+	}
+
+	if (wpa_ft_new_seq(r0kh->seq, &f_seq) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to get seq num");
+		return -1;
+	}
+
+	if (wpa_ft_rrb_build(key, key_len, req_enc, NULL, req_auth, NULL,
+			     sm->wpa_auth->addr, FT_PACKET_R0KH_R1KH_PULL,
+			     &packet, &packet_len) < 0)
+		return -1;
+
+	ft_pending_req_ies = wpabuf_alloc_copy(ies, ies_len);
+	wpabuf_free(sm->ft_pending_req_ies);
+	sm->ft_pending_req_ies = ft_pending_req_ies;
+	if (!sm->ft_pending_req_ies) {
+		os_free(packet);
+		return -1;
+	}
+
+	tsecs = sm->wpa_auth->conf.rkh_pull_timeout / 1000;
+	tusecs = (sm->wpa_auth->conf.rkh_pull_timeout % 1000) * 1000;
+	eloop_register_timeout(tsecs, tusecs, wpa_ft_expire_pull, sm, NULL);
+
+	wpa_ft_rrb_oui_send(sm->wpa_auth, r0kh->addr, FT_PACKET_R0KH_R1KH_PULL,
+			    packet, packet_len);
+
+	os_free(packet);
+
+	return 0;
+}
+
+
+int wpa_ft_store_pmk_fils(struct wpa_state_machine *sm,
+			  const u8 *pmk_r0, const u8 *pmk_r0_name)
+{
+	int expires_in = sm->wpa_auth->conf.r0_key_lifetime;
+	struct vlan_description vlan;
+	const u8 *identity, *radius_cui;
+	size_t identity_len, radius_cui_len;
+	int session_timeout;
+	size_t pmk_r0_len = wpa_key_mgmt_sha384(sm->wpa_key_mgmt) ?
+		SHA384_MAC_LEN : PMK_LEN;
+
+	if (wpa_ft_get_vlan(sm->wpa_auth, sm->addr, &vlan) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: vlan not available for STA " MACSTR,
+			   MAC2STR(sm->addr));
+		return -1;
+	}
+
+	identity_len = wpa_ft_get_identity(sm->wpa_auth, sm->addr, &identity);
+	radius_cui_len = wpa_ft_get_radius_cui(sm->wpa_auth, sm->addr,
+					       &radius_cui);
+	session_timeout = wpa_ft_get_session_timeout(sm->wpa_auth, sm->addr);
+
+	return wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_len,
+				   pmk_r0_name, sm->pairwise, &vlan, expires_in,
+				   session_timeout, identity, identity_len,
+				   radius_cui, radius_cui_len);
+}
+
+
+int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk,
+			   u8 *pmk_r0, u8 *pmk_r1, u8 *pmk_r0_name,
+			   size_t *key_len, size_t kdk_len)
+{
+	size_t pmk_r0_len, pmk_r1_len;
+	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;
+	size_t r0kh_len = sm->wpa_auth->conf.r0_key_holder_len;
+	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;
+	const u8 *mpmk;
+	size_t mpmk_len;
+
+	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+	    (sm->xxkey_len == SHA256_MAC_LEN ||
+	     sm->xxkey_len == SHA384_MAC_LEN ||
+	     sm->xxkey_len == SHA512_MAC_LEN))
+		pmk_r0_len = sm->xxkey_len;
+	else if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt))
+		pmk_r0_len = SHA384_MAC_LEN;
+	else
+		pmk_r0_len = PMK_LEN;
+	*key_len = pmk_r1_len = pmk_r0_len;
+
+	if (sm->xxkey_len > 0) {
+		mpmk = sm->xxkey;
+		mpmk_len = sm->xxkey_len;
+	} else if (sm->pmksa) {
+		mpmk = sm->pmksa->pmk;
+		mpmk_len = sm->pmksa->pmk_len;
+	} else {
+		wpa_printf(MSG_DEBUG, "FT: XXKey not available for key "
+			   "derivation");
+		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;
+	}
+
+	identity_len = wpa_ft_get_identity(sm->wpa_auth, sm->addr, &identity);
+	radius_cui_len = wpa_ft_get_radius_cui(sm->wpa_auth, sm->addr,
+					       &radius_cui);
+	session_timeout = wpa_ft_get_session_timeout(sm->wpa_auth, sm->addr);
+
+
+	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);
+}
+
+
+static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth,
+				      const u8 *addr, int idx, u8 *seq)
+{
+	if (wpa_auth->cb->get_seqnum == NULL)
+		return -1;
+	return wpa_auth->cb->get_seqnum(wpa_auth->cb_ctx, addr, idx, seq);
+}
+
+
+static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len)
+{
+	u8 *subelem;
+	struct wpa_auth_config *conf = &sm->wpa_auth->conf;
+	struct wpa_group *gsm = sm->group;
+	size_t subelem_len, pad_len;
+	const u8 *key;
+	size_t key_len;
+	u8 keybuf[WPA_GTK_MAX_LEN];
+	const u8 *kek;
+	size_t kek_len;
+
+	if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+		kek = sm->PTK.kek2;
+		kek_len = sm->PTK.kek2_len;
+	} else {
+		kek = sm->PTK.kek;
+		kek_len = sm->PTK.kek_len;
+	}
+
+	key_len = gsm->GTK_len;
+	if (key_len > sizeof(keybuf))
+		return NULL;
+
+	/*
+	 * Pad key for AES Key Wrap if it is not multiple of 8 bytes or is less
+	 * than 16 bytes.
+	 */
+	pad_len = key_len % 8;
+	if (pad_len)
+		pad_len = 8 - pad_len;
+	if (key_len + pad_len < 16)
+		pad_len += 8;
+	if (pad_len && key_len < sizeof(keybuf)) {
+		os_memcpy(keybuf, gsm->GTK[gsm->GN - 1], key_len);
+		if (conf->disable_gtk ||
+		    sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+			/*
+			 * Provide unique random GTK to each STA to prevent use
+			 * of GTK in the BSS.
+			 */
+			if (random_get_bytes(keybuf, key_len) < 0)
+				return NULL;
+		}
+		os_memset(keybuf + key_len, 0, pad_len);
+		keybuf[key_len] = 0xdd;
+		key_len += pad_len;
+		key = keybuf;
+	} else if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+		/*
+		 * Provide unique random GTK to each STA to prevent use of GTK
+		 * in the BSS.
+		 */
+		if (random_get_bytes(keybuf, key_len) < 0)
+			return NULL;
+		key = keybuf;
+	} else {
+		key = gsm->GTK[gsm->GN - 1];
+	}
+
+	/*
+	 * Sub-elem ID[1] | Length[1] | Key Info[2] | Key Length[1] | RSC[8] |
+	 * Key[5..32].
+	 */
+	subelem_len = 13 + key_len + 8;
+	subelem = os_zalloc(subelem_len);
+	if (subelem == NULL)
+		return NULL;
+
+	subelem[0] = FTIE_SUBELEM_GTK;
+	subelem[1] = 11 + key_len + 8;
+	/* Key ID in B0-B1 of Key Info */
+	WPA_PUT_LE16(&subelem[2], gsm->GN & 0x03);
+	subelem[4] = gsm->GTK_len;
+	wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, subelem + 5);
+	if (aes_wrap(kek, kek_len, key_len / 8, key, subelem + 13)) {
+		wpa_printf(MSG_DEBUG,
+			   "FT: GTK subelem encryption failed: kek_len=%d",
+			   (int) kek_len);
+		forced_memzero(keybuf, sizeof(keybuf));
+		os_free(subelem);
+		return NULL;
+	}
+
+	forced_memzero(keybuf, sizeof(keybuf));
+	*len = subelem_len;
+	return subelem;
+}
+
+
+static u8 * wpa_ft_igtk_subelem(struct wpa_state_machine *sm, size_t *len)
+{
+	u8 *subelem, *pos;
+	struct wpa_auth_config *conf = &sm->wpa_auth->conf;
+	struct wpa_group *gsm = sm->group;
+	size_t subelem_len;
+	const u8 *kek, *igtk;
+	size_t kek_len;
+	size_t igtk_len;
+	u8 stub_igtk[WPA_IGTK_MAX_LEN];
+
+	if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+		kek = sm->PTK.kek2;
+		kek_len = sm->PTK.kek2_len;
+	} else {
+		kek = sm->PTK.kek;
+		kek_len = sm->PTK.kek_len;
+	}
+
+	igtk_len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
+
+	/* Sub-elem ID[1] | Length[1] | KeyID[2] | IPN[6] | Key Length[1] |
+	 * Key[16+8] */
+	subelem_len = 1 + 1 + 2 + 6 + 1 + igtk_len + 8;
+	subelem = os_zalloc(subelem_len);
+	if (subelem == NULL)
+		return NULL;
+
+	pos = subelem;
+	*pos++ = FTIE_SUBELEM_IGTK;
+	*pos++ = subelem_len - 2;
+	WPA_PUT_LE16(pos, gsm->GN_igtk);
+	pos += 2;
+	wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos);
+	pos += 6;
+	*pos++ = igtk_len;
+	igtk = gsm->IGTK[gsm->GN_igtk - 4];
+	if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+		/*
+		 * Provide unique random IGTK to each STA to prevent use of
+		 * IGTK in the BSS.
+		 */
+		if (random_get_bytes(stub_igtk, igtk_len / 8) < 0) {
+			os_free(subelem);
+			return NULL;
+		}
+		igtk = stub_igtk;
+	}
+	if (aes_wrap(kek, kek_len, igtk_len / 8, igtk, pos)) {
+		wpa_printf(MSG_DEBUG,
+			   "FT: IGTK subelem encryption failed: kek_len=%d",
+			   (int) kek_len);
+		os_free(subelem);
+		return NULL;
+	}
+
+	*len = subelem_len;
+	return subelem;
+}
+
+
+static u8 * wpa_ft_bigtk_subelem(struct wpa_state_machine *sm, size_t *len)
+{
+	u8 *subelem, *pos;
+	struct wpa_group *gsm = sm->group;
+	size_t subelem_len;
+	const u8 *kek, *bigtk;
+	size_t kek_len;
+	size_t bigtk_len;
+	u8 stub_bigtk[WPA_IGTK_MAX_LEN];
+
+	if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+		kek = sm->PTK.kek2;
+		kek_len = sm->PTK.kek2_len;
+	} else {
+		kek = sm->PTK.kek;
+		kek_len = sm->PTK.kek_len;
+	}
+
+	bigtk_len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
+
+	/* Sub-elem ID[1] | Length[1] | KeyID[2] | BIPN[6] | Key Length[1] |
+	 * Key[16+8] */
+	subelem_len = 1 + 1 + 2 + 6 + 1 + bigtk_len + 8;
+	subelem = os_zalloc(subelem_len);
+	if (subelem == NULL)
+		return NULL;
+
+	pos = subelem;
+	*pos++ = FTIE_SUBELEM_BIGTK;
+	*pos++ = subelem_len - 2;
+	WPA_PUT_LE16(pos, gsm->GN_bigtk);
+	pos += 2;
+	wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_bigtk, pos);
+	pos += 6;
+	*pos++ = bigtk_len;
+	bigtk = gsm->IGTK[gsm->GN_bigtk - 6];
+	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+		/*
+		 * Provide unique random BIGTK to each OSEN STA to prevent use
+		 * of BIGTK in the BSS.
+		 */
+		if (random_get_bytes(stub_bigtk, bigtk_len / 8) < 0) {
+			os_free(subelem);
+			return NULL;
+		}
+		bigtk = stub_bigtk;
+	}
+	if (aes_wrap(kek, kek_len, bigtk_len / 8, bigtk, pos)) {
+		wpa_printf(MSG_DEBUG,
+			   "FT: BIGTK subelem encryption failed: kek_len=%d",
+			   (int) kek_len);
+		os_free(subelem);
+		return NULL;
+	}
+
+	*len = subelem_len;
+	return subelem;
+}
+
+
+static u8 * wpa_ft_process_rdie(struct wpa_state_machine *sm,
+				u8 *pos, u8 *end, u8 id, u8 descr_count,
+				const u8 *ies, size_t ies_len)
+{
+	struct ieee802_11_elems parse;
+	struct rsn_rdie *rdie;
+
+	wpa_printf(MSG_DEBUG, "FT: Resource Request: id=%d descr_count=%d",
+		   id, descr_count);
+	wpa_hexdump(MSG_MSGDUMP, "FT: Resource descriptor IE(s)",
+		    ies, ies_len);
+
+	if (end - pos < (int) sizeof(*rdie)) {
+		wpa_printf(MSG_ERROR, "FT: Not enough room for response RDIE");
+		return pos;
+	}
+
+	*pos++ = WLAN_EID_RIC_DATA;
+	*pos++ = sizeof(*rdie);
+	rdie = (struct rsn_rdie *) pos;
+	rdie->id = id;
+	rdie->descr_count = 0;
+	rdie->status_code = host_to_le16(WLAN_STATUS_SUCCESS);
+	pos += sizeof(*rdie);
+
+	if (ieee802_11_parse_elems((u8 *) ies, ies_len, &parse, 1) ==
+	    ParseFailed) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to parse request IEs");
+		rdie->status_code =
+			host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE);
+		return pos;
+	}
+
+	if (parse.wmm_tspec) {
+		struct wmm_tspec_element *tspec;
+
+		if (parse.wmm_tspec_len + 2 < (int) sizeof(*tspec)) {
+			wpa_printf(MSG_DEBUG, "FT: Too short WMM TSPEC IE "
+				   "(%d)", (int) parse.wmm_tspec_len);
+			rdie->status_code =
+				host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE);
+			return pos;
+		}
+		if (end - pos < (int) sizeof(*tspec)) {
+			wpa_printf(MSG_ERROR, "FT: Not enough room for "
+				   "response TSPEC");
+			rdie->status_code =
+				host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE);
+			return pos;
+		}
+		tspec = (struct wmm_tspec_element *) pos;
+		os_memcpy(tspec, parse.wmm_tspec - 2, sizeof(*tspec));
+	}
+
+#ifdef NEED_AP_MLME
+	if (parse.wmm_tspec && sm->wpa_auth->conf.ap_mlme) {
+		int res;
+
+		res = wmm_process_tspec((struct wmm_tspec_element *) pos);
+		wpa_printf(MSG_DEBUG, "FT: ADDTS processing result: %d", res);
+		if (res == WMM_ADDTS_STATUS_INVALID_PARAMETERS)
+			rdie->status_code =
+				host_to_le16(WLAN_STATUS_INVALID_PARAMETERS);
+		else if (res == WMM_ADDTS_STATUS_REFUSED)
+			rdie->status_code =
+				host_to_le16(WLAN_STATUS_REQUEST_DECLINED);
+		else {
+			/* TSPEC accepted; include updated TSPEC in response */
+			rdie->descr_count = 1;
+			pos += sizeof(struct wmm_tspec_element);
+		}
+		return pos;
+	}
+#endif /* NEED_AP_MLME */
+
+	if (parse.wmm_tspec && !sm->wpa_auth->conf.ap_mlme) {
+		int res;
+
+		res = wpa_ft_add_tspec(sm->wpa_auth, sm->addr, pos,
+				       sizeof(struct wmm_tspec_element));
+		if (res >= 0) {
+			if (res)
+				rdie->status_code = host_to_le16(res);
+			else {
+				/* TSPEC accepted; include updated TSPEC in
+				 * response */
+				rdie->descr_count = 1;
+				pos += sizeof(struct wmm_tspec_element);
+			}
+			return pos;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "FT: No supported resource requested");
+	rdie->status_code = host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE);
+	return pos;
+}
+
+
+static u8 * wpa_ft_process_ric(struct wpa_state_machine *sm, u8 *pos, u8 *end,
+			       const u8 *ric, size_t ric_len)
+{
+	const u8 *rpos, *start;
+	const struct rsn_rdie *rdie;
+
+	wpa_hexdump(MSG_MSGDUMP, "FT: RIC Request", ric, ric_len);
+
+	rpos = ric;
+	while (rpos + sizeof(*rdie) < ric + ric_len) {
+		if (rpos[0] != WLAN_EID_RIC_DATA || rpos[1] < sizeof(*rdie) ||
+		    rpos + 2 + rpos[1] > ric + ric_len)
+			break;
+		rdie = (const struct rsn_rdie *) (rpos + 2);
+		rpos += 2 + rpos[1];
+		start = rpos;
+
+		while (rpos + 2 <= ric + ric_len &&
+		       rpos + 2 + rpos[1] <= ric + ric_len) {
+			if (rpos[0] == WLAN_EID_RIC_DATA)
+				break;
+			rpos += 2 + rpos[1];
+		}
+		pos = wpa_ft_process_rdie(sm, pos, end, rdie->id,
+					  rdie->descr_count,
+					  start, rpos - start);
+	}
+
+	return pos;
+}
+
+
+u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
+				 size_t max_len, int auth_alg,
+				 const u8 *req_ies, size_t req_ies_len,
+				 int omit_rsnxe)
+{
+	u8 *end, *mdie, *ftie, *rsnie = NULL, *r0kh_id, *subelem = NULL;
+	u8 *fte_mic, *elem_count;
+	size_t mdie_len, ftie_len, rsnie_len = 0, r0kh_id_len, subelem_len = 0;
+	u8 rsnxe_buf[10], *rsnxe = rsnxe_buf;
+	size_t rsnxe_len;
+	int rsnxe_used;
+	int res;
+	struct wpa_auth_config *conf;
+	struct wpa_ft_ies parse;
+	u8 *ric_start;
+	u8 *anonce, *snonce;
+	const u8 *kck;
+	size_t kck_len;
+	size_t key_len;
+
+	if (sm == NULL)
+		return pos;
+
+	conf = &sm->wpa_auth->conf;
+
+	if (!wpa_key_mgmt_ft(sm->wpa_key_mgmt))
+		return pos;
+
+	end = pos + max_len;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (auth_alg == WLAN_AUTH_FT &&
+	    sm->wpa_auth->conf.rsne_override_ft_set) {
+		wpa_printf(MSG_DEBUG,
+			   "TESTING: RSNE FT override for MIC calculation");
+		rsnie = sm->wpa_auth->conf.rsne_override_ft;
+		rsnie_len = sm->wpa_auth->conf.rsne_override_ft_len;
+		if (end - pos < (long int) rsnie_len)
+			return pos;
+		os_memcpy(pos, rsnie, rsnie_len);
+		rsnie = pos;
+		pos += rsnie_len;
+		if (rsnie_len > PMKID_LEN && sm->pmk_r1_name_valid) {
+			int idx;
+
+			/* Replace all 0xff PMKID with the valid PMKR1Name */
+			for (idx = 0; idx < PMKID_LEN; idx++) {
+				if (rsnie[rsnie_len - 1 - idx] != 0xff)
+					break;
+			}
+			if (idx == PMKID_LEN)
+				os_memcpy(&rsnie[rsnie_len - PMKID_LEN],
+					  sm->pmk_r1_name, WPA_PMK_NAME_LEN);
+		}
+	} else
+#endif /* CONFIG_TESTING_OPTIONS */
+	if (auth_alg == WLAN_AUTH_FT ||
+	    ((auth_alg == WLAN_AUTH_FILS_SK ||
+	      auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+	      auth_alg == WLAN_AUTH_FILS_PK) &&
+	     (sm->wpa_key_mgmt & (WPA_KEY_MGMT_FT_FILS_SHA256 |
+				  WPA_KEY_MGMT_FT_FILS_SHA384)))) {
+		if (!sm->pmk_r1_name_valid) {
+			wpa_printf(MSG_ERROR,
+				   "FT: PMKR1Name is not valid for Assoc Resp RSNE");
+			return NULL;
+		}
+		wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name for Assoc Resp RSNE",
+			    sm->pmk_r1_name, WPA_PMK_NAME_LEN);
+		/*
+		 * RSN (only present if this is a Reassociation Response and
+		 * part of a fast BSS transition; or if this is a
+		 * (Re)Association Response frame during an FT initial mobility
+		 * domain association using FILS)
+		 */
+		res = wpa_write_rsn_ie(conf, pos, end - pos, sm->pmk_r1_name);
+		if (res < 0)
+			return NULL;
+		rsnie = pos;
+		rsnie_len = res;
+		pos += res;
+	}
+
+	/* Mobility Domain Information */
+	res = wpa_write_mdie(conf, pos, end - pos);
+	if (res < 0)
+		return NULL;
+	mdie = pos;
+	mdie_len = res;
+	pos += res;
+
+	/* Fast BSS Transition Information */
+	if (auth_alg == WLAN_AUTH_FT) {
+		subelem = wpa_ft_gtk_subelem(sm, &subelem_len);
+		if (!subelem) {
+			wpa_printf(MSG_DEBUG,
+				   "FT: Failed to add GTK subelement");
+			return NULL;
+		}
+		r0kh_id = sm->r0kh_id;
+		r0kh_id_len = sm->r0kh_id_len;
+		anonce = sm->ANonce;
+		snonce = sm->SNonce;
+		if (sm->mgmt_frame_prot) {
+			u8 *igtk;
+			size_t igtk_len;
+			u8 *nbuf;
+			igtk = wpa_ft_igtk_subelem(sm, &igtk_len);
+			if (igtk == NULL) {
+				wpa_printf(MSG_DEBUG,
+					   "FT: Failed to add IGTK subelement");
+				os_free(subelem);
+				return NULL;
+			}
+			nbuf = os_realloc(subelem, subelem_len + igtk_len);
+			if (nbuf == NULL) {
+				os_free(subelem);
+				os_free(igtk);
+				return NULL;
+			}
+			subelem = nbuf;
+			os_memcpy(subelem + subelem_len, igtk, igtk_len);
+			subelem_len += igtk_len;
+			os_free(igtk);
+		}
+		if (sm->mgmt_frame_prot && conf->beacon_prot) {
+			u8 *bigtk;
+			size_t bigtk_len;
+			u8 *nbuf;
+
+			bigtk = wpa_ft_bigtk_subelem(sm, &bigtk_len);
+			if (!bigtk) {
+				wpa_printf(MSG_DEBUG,
+					   "FT: Failed to add BIGTK subelement");
+				os_free(subelem);
+				return NULL;
+			}
+			nbuf = os_realloc(subelem, subelem_len + bigtk_len);
+			if (!nbuf) {
+				os_free(subelem);
+				os_free(bigtk);
+				return NULL;
+			}
+			subelem = nbuf;
+			os_memcpy(subelem + subelem_len, bigtk, bigtk_len);
+			subelem_len += bigtk_len;
+			os_free(bigtk);
+		}
+#ifdef CONFIG_OCV
+		if (wpa_auth_uses_ocv(sm)) {
+			struct wpa_channel_info ci;
+			u8 *nbuf, *ocipos;
+
+			if (wpa_channel_info(sm->wpa_auth, &ci) != 0) {
+				wpa_printf(MSG_WARNING,
+					   "Failed to get channel info for OCI element");
+				os_free(subelem);
+				return NULL;
+			}
+#ifdef CONFIG_TESTING_OPTIONS
+			if (conf->oci_freq_override_ft_assoc) {
+				wpa_printf(MSG_INFO,
+					   "TEST: Override OCI frequency %d -> %u MHz",
+					   ci.frequency,
+					   conf->oci_freq_override_ft_assoc);
+				ci.frequency = conf->oci_freq_override_ft_assoc;
+			}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+			subelem_len += 2 + OCV_OCI_LEN;
+			nbuf = os_realloc(subelem, subelem_len);
+			if (!nbuf) {
+				os_free(subelem);
+				return NULL;
+			}
+			subelem = nbuf;
+
+			ocipos = subelem + subelem_len - 2 - OCV_OCI_LEN;
+			*ocipos++ = FTIE_SUBELEM_OCI;
+			*ocipos++ = OCV_OCI_LEN;
+			if (ocv_insert_oci(&ci, &ocipos) < 0) {
+				os_free(subelem);
+				return NULL;
+			}
+		}
+#endif /* CONFIG_OCV */
+	} else {
+		r0kh_id = conf->r0_key_holder;
+		r0kh_id_len = conf->r0_key_holder_len;
+		anonce = NULL;
+		snonce = NULL;
+	}
+	rsnxe_used = (auth_alg == WLAN_AUTH_FT) &&
+		(conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+		 conf->sae_pwe == SAE_PWE_BOTH);
+#ifdef CONFIG_TESTING_OPTIONS
+	if (sm->wpa_auth->conf.ft_rsnxe_used) {
+		rsnxe_used = sm->wpa_auth->conf.ft_rsnxe_used == 1;
+		wpa_printf(MSG_DEBUG, "TESTING: FT: Force RSNXE Used %d",
+			   rsnxe_used);
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+	key_len = sm->xxkey_len;
+	if (!key_len)
+		key_len = sm->pmk_r1_len;
+	if (!key_len && sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+	    sm->wpa_auth->cb->get_psk) {
+		size_t psk_len;
+
+		if (sm->wpa_auth->cb->get_psk(sm->wpa_auth->cb_ctx,
+					      sm->addr, sm->p2p_dev_addr,
+					      NULL, &psk_len, NULL))
+			key_len = psk_len;
+	}
+	res = wpa_write_ftie(conf, sm->wpa_key_mgmt, key_len,
+			     r0kh_id, r0kh_id_len,
+			     anonce, snonce, pos, end - pos,
+			     subelem, subelem_len, rsnxe_used);
+	os_free(subelem);
+	if (res < 0)
+		return NULL;
+	ftie = pos;
+	ftie_len = res;
+	pos += res;
+
+	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+	    key_len == SHA512_MAC_LEN) {
+		struct rsn_ftie_sha512 *_ftie =
+			(struct rsn_ftie_sha512 *) (ftie + 2);
+
+		fte_mic = _ftie->mic;
+		elem_count = &_ftie->mic_control[1];
+	} else if ((sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+		    key_len == SHA384_MAC_LEN) ||
+		   wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) {
+		struct rsn_ftie_sha384 *_ftie =
+			(struct rsn_ftie_sha384 *) (ftie + 2);
+
+		fte_mic = _ftie->mic;
+		elem_count = &_ftie->mic_control[1];
+	} else {
+		struct rsn_ftie *_ftie = (struct rsn_ftie *) (ftie + 2);
+
+		fte_mic = _ftie->mic;
+		elem_count = &_ftie->mic_control[1];
+	}
+	if (auth_alg == WLAN_AUTH_FT)
+		*elem_count = 3; /* Information element count */
+
+	ric_start = pos;
+	if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse,
+			     sm->wpa_key_mgmt) == 0 && parse.ric) {
+		pos = wpa_ft_process_ric(sm, pos, end, parse.ric,
+					 parse.ric_len);
+		if (auth_alg == WLAN_AUTH_FT)
+			*elem_count +=
+				ieee802_11_ie_count(ric_start,
+						    pos - ric_start);
+	}
+	if (ric_start == pos)
+		ric_start = NULL;
+
+	if (omit_rsnxe) {
+		rsnxe_len = 0;
+	} else {
+		res = wpa_write_rsnxe(&sm->wpa_auth->conf, rsnxe,
+				      sizeof(rsnxe_buf));
+		if (res < 0)
+			return NULL;
+		rsnxe_len = res;
+	}
+#ifdef CONFIG_TESTING_OPTIONS
+	if (auth_alg == WLAN_AUTH_FT &&
+	    sm->wpa_auth->conf.rsnxe_override_ft_set) {
+		wpa_printf(MSG_DEBUG,
+			   "TESTING: RSNXE FT override for MIC calculation");
+		rsnxe = sm->wpa_auth->conf.rsnxe_override_ft;
+		rsnxe_len = sm->wpa_auth->conf.rsnxe_override_ft_len;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+	if (auth_alg == WLAN_AUTH_FT && rsnxe_len)
+		*elem_count += 1;
+
+	if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+		kck = sm->PTK.kck2;
+		kck_len = sm->PTK.kck2_len;
+	} else {
+		kck = sm->PTK.kck;
+		kck_len = sm->PTK.kck_len;
+	}
+	if (auth_alg == WLAN_AUTH_FT &&
+	    wpa_ft_mic(sm->wpa_key_mgmt, kck, kck_len,
+		       sm->addr, sm->wpa_auth->addr, 6,
+		       mdie, mdie_len, ftie, ftie_len,
+		       rsnie, rsnie_len,
+		       ric_start, ric_start ? pos - ric_start : 0,
+		       rsnxe_len ? rsnxe : NULL, rsnxe_len,
+		       fte_mic) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC");
+		return NULL;
+	}
+
+	os_free(sm->assoc_resp_ftie);
+	sm->assoc_resp_ftie = os_malloc(ftie_len);
+	if (!sm->assoc_resp_ftie)
+		return NULL;
+	os_memcpy(sm->assoc_resp_ftie, ftie, ftie_len);
+
+	return pos;
+}
+
+
+static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth,
+				   int vlan_id,
+				   enum wpa_alg alg, const u8 *addr, int idx,
+				   u8 *key, size_t key_len,
+				   enum key_flag key_flag)
+{
+	if (wpa_auth->cb->set_key == NULL)
+		return -1;
+	return wpa_auth->cb->set_key(wpa_auth->cb_ctx, vlan_id, alg, addr, idx,
+				     key, key_len, key_flag);
+}
+
+
+#ifdef CONFIG_PASN
+static inline int wpa_auth_set_ltf_keyseed(struct wpa_authenticator *wpa_auth,
+					   const u8 *peer_addr,
+					   const u8 *ltf_keyseed,
+					   size_t ltf_keyseed_len)
+{
+	if (!wpa_auth->cb->set_ltf_keyseed)
+		return -1;
+	return wpa_auth->cb->set_ltf_keyseed(wpa_auth->cb_ctx, peer_addr,
+					     ltf_keyseed, ltf_keyseed_len);
+}
+#endif /* CONFIG_PASN */
+
+
+static inline int wpa_auth_add_sta_ft(struct wpa_authenticator *wpa_auth,
+				      const u8 *addr)
+{
+	if (!wpa_auth->cb->add_sta_ft)
+		return -1;
+	return wpa_auth->cb->add_sta_ft(wpa_auth->cb_ctx, addr);
+}
+
+
+void wpa_ft_install_ptk(struct wpa_state_machine *sm, int retry)
+{
+	enum wpa_alg alg;
+	int klen;
+
+	/* MLME-SETKEYS.request(PTK) */
+	alg = wpa_cipher_to_alg(sm->pairwise);
+	klen = wpa_cipher_key_len(sm->pairwise);
+	if (!wpa_cipher_valid_pairwise(sm->pairwise)) {
+		wpa_printf(MSG_DEBUG, "FT: Unknown pairwise alg 0x%x - skip "
+			   "PTK configuration", sm->pairwise);
+		return;
+	}
+
+	if (sm->tk_already_set) {
+		/* Must avoid TK reconfiguration to prevent clearing of TX/RX
+		 * PN in the driver */
+		wpa_printf(MSG_DEBUG,
+			   "FT: Do not re-install same PTK to the driver");
+		return;
+	}
+
+	if (!retry)
+		wpa_auth_add_sta_ft(sm->wpa_auth, sm->addr);
+
+	/* FIX: add STA entry to kernel/driver here? The set_key will fail
+	 * most likely without this.. At the moment, STA entry is added only
+	 * after association has been completed. This function will be called
+	 * again after association to get the PTK configured, but that could be
+	 * optimized by adding the STA entry earlier.
+	 */
+	if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, sm->keyidx_active,
+			     sm->PTK.tk, klen, KEY_FLAG_PAIRWISE_RX_TX))
+		return;
+
+#ifdef CONFIG_PASN
+	if (sm->wpa_auth->conf.secure_ltf &&
+	    ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF) &&
+	    wpa_auth_set_ltf_keyseed(sm->wpa_auth, sm->addr,
+				     sm->PTK.ltf_keyseed,
+				     sm->PTK.ltf_keyseed_len)) {
+		wpa_printf(MSG_ERROR,
+			   "FT: Failed to set LTF keyseed to driver");
+		return;
+	}
+#endif /* CONFIG_PASN */
+
+	/* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */
+	sm->pairwise_set = true;
+	sm->tk_already_set = true;
+
+	wpa_auth_store_ptksa(sm->wpa_auth, sm->addr, sm->pairwise,
+			     dot11RSNAConfigPMKLifetime, &sm->PTK);
+}
+
+
+/* Derive PMK-R1 from PSK, check all available PSK */
+static int wpa_ft_psk_pmk_r1(struct wpa_state_machine *sm,
+			     const u8 *req_pmk_r1_name,
+			     u8 *out_pmk_r1, int *out_pairwise,
+			     struct vlan_description *out_vlan,
+			     const u8 **out_identity, size_t *out_identity_len,
+			     const u8 **out_radius_cui,
+			     size_t *out_radius_cui_len,
+			     int *out_session_timeout)
+{
+	const u8 *pmk = NULL;
+	u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN];
+	u8 pmk_r1[PMK_LEN], pmk_r1_name[WPA_PMK_NAME_LEN];
+	struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+	const u8 *mdid = wpa_auth->conf.mobility_domain;
+	const u8 *r0kh = sm->r0kh_id;
+	size_t r0kh_len = sm->r0kh_id_len;
+	const u8 *r1kh = wpa_auth->conf.r1_key_holder;
+	const u8 *ssid = wpa_auth->conf.ssid;
+	size_t ssid_len = wpa_auth->conf.ssid_len;
+	int pairwise;
+
+	pairwise = sm->pairwise;
+
+	for (;;) {
+		pmk = wpa_ft_get_psk(wpa_auth, sm->addr, sm->p2p_dev_addr,
+				     pmk);
+		if (pmk == NULL)
+			break;
+
+		if (wpa_derive_pmk_r0(pmk, PMK_LEN, ssid, ssid_len, mdid, r0kh,
+				      r0kh_len, sm->addr,
+				      pmk_r0, pmk_r0_name,
+				      WPA_KEY_MGMT_FT_PSK) < 0 ||
+		    wpa_derive_pmk_r1(pmk_r0, PMK_LEN, pmk_r0_name, r1kh,
+				      sm->addr, pmk_r1, pmk_r1_name) < 0 ||
+		    os_memcmp_const(pmk_r1_name, req_pmk_r1_name,
+				    WPA_PMK_NAME_LEN) != 0)
+			continue;
+
+		/* We found a PSK that matches the requested pmk_r1_name */
+		wpa_printf(MSG_DEBUG,
+			   "FT: Found PSK to generate PMK-R1 locally");
+		os_memcpy(out_pmk_r1, pmk_r1, PMK_LEN);
+		if (out_pairwise)
+			*out_pairwise = pairwise;
+		os_memcpy(sm->PMK, pmk, PMK_LEN);
+		sm->pmk_len = PMK_LEN;
+		if (out_vlan &&
+		    wpa_ft_get_vlan(sm->wpa_auth, sm->addr, out_vlan) < 0) {
+			wpa_printf(MSG_DEBUG, "FT: vlan not available for STA "
+				   MACSTR, MAC2STR(sm->addr));
+			return -1;
+		}
+
+		if (out_identity && out_identity_len) {
+			*out_identity_len = wpa_ft_get_identity(
+				sm->wpa_auth, sm->addr, out_identity);
+		}
+
+		if (out_radius_cui && out_radius_cui_len) {
+			*out_radius_cui_len = wpa_ft_get_radius_cui(
+				sm->wpa_auth, sm->addr, out_radius_cui);
+		}
+
+		if (out_session_timeout) {
+			*out_session_timeout = wpa_ft_get_session_timeout(
+				sm->wpa_auth, sm->addr);
+		}
+
+		return 0;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "FT: Did not find PSK to generate PMK-R1 locally");
+	return -1;
+}
+
+
+/* Detect the configuration the station asked for.
+ * Required to detect FT-PSK and pairwise cipher.
+ */
+static int wpa_ft_set_key_mgmt(struct wpa_state_machine *sm,
+			       struct wpa_ft_ies *parse)
+{
+	int key_mgmt, ciphers;
+
+	if (sm->wpa_key_mgmt)
+		return 0;
+
+	key_mgmt = parse->key_mgmt & sm->wpa_auth->conf.wpa_key_mgmt;
+	if (!key_mgmt) {
+		wpa_printf(MSG_DEBUG, "FT: Invalid key mgmt (0x%x) from "
+			   MACSTR, parse->key_mgmt, MAC2STR(sm->addr));
+		return -1;
+	}
+	if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
+#ifdef CONFIG_SHA384
+	else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X_SHA384;
+#endif /* CONFIG_SHA384 */
+	else if (key_mgmt & WPA_KEY_MGMT_FT_PSK)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_PSK;
+#ifdef CONFIG_FILS
+	else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA256;
+	else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA384;
+#endif /* CONFIG_FILS */
+	ciphers = parse->pairwise_cipher & sm->wpa_auth->conf.rsn_pairwise;
+	if (!ciphers) {
+		wpa_printf(MSG_DEBUG, "FT: Invalid pairwise cipher (0x%x) from "
+			   MACSTR,
+			   parse->pairwise_cipher, MAC2STR(sm->addr));
+		return -1;
+	}
+	sm->pairwise = wpa_pick_pairwise_cipher(ciphers, 0);
+
+	return 0;
+}
+
+
+static int wpa_ft_local_derive_pmk_r1(struct wpa_authenticator *wpa_auth,
+				      struct wpa_state_machine *sm,
+				      const u8 *r0kh_id, size_t r0kh_id_len,
+				      const u8 *req_pmk_r0_name,
+				      u8 *out_pmk_r1_name,
+				      u8 *out_pmk_r1, int *out_pairwise,
+				      struct vlan_description *vlan,
+				      const u8 **identity, size_t *identity_len,
+				      const u8 **radius_cui,
+				      size_t *radius_cui_len,
+				      int *out_session_timeout,
+				      size_t *pmk_r1_len)
+{
+	struct wpa_auth_config *conf = &wpa_auth->conf;
+	const struct wpa_ft_pmk_r0_sa *r0;
+	int expires_in = 0;
+	int session_timeout = 0;
+	struct os_reltime now;
+
+	if (conf->r0_key_holder_len != r0kh_id_len ||
+	    os_memcmp(conf->r0_key_holder, r0kh_id, conf->r0_key_holder_len) !=
+	    0)
+		return -1; /* not our R0KH-ID */
+
+	wpa_printf(MSG_DEBUG, "FT: STA R0KH-ID matching local configuration");
+	if (wpa_ft_fetch_pmk_r0(sm->wpa_auth, sm->addr, req_pmk_r0_name, &r0) <
+	    0)
+		return -1; /* no matching PMKR0Name in local cache */
+
+	wpa_printf(MSG_DEBUG, "FT: Requested PMKR0Name found in local cache");
+
+	if (wpa_derive_pmk_r1(r0->pmk_r0, r0->pmk_r0_len, r0->pmk_r0_name,
+			      conf->r1_key_holder,
+			      sm->addr, out_pmk_r1, out_pmk_r1_name) < 0)
+		return -1;
+
+	os_get_reltime(&now);
+	if (r0->expiration)
+		expires_in = r0->expiration - now.sec;
+
+	if (r0->session_timeout)
+		session_timeout = r0->session_timeout - now.sec;
+
+	wpa_ft_store_pmk_r1(wpa_auth, sm->addr, out_pmk_r1, r0->pmk_r0_len,
+			    out_pmk_r1_name,
+			    sm->pairwise, r0->vlan, expires_in, session_timeout,
+			    r0->identity, r0->identity_len,
+			    r0->radius_cui, r0->radius_cui_len);
+
+	*out_pairwise = sm->pairwise;
+	if (vlan) {
+		if (r0->vlan)
+			*vlan = *r0->vlan;
+		else
+			os_memset(vlan, 0, sizeof(*vlan));
+	}
+
+	if (identity && identity_len) {
+		*identity = r0->identity;
+		*identity_len = r0->identity_len;
+	}
+
+	if (radius_cui && radius_cui_len) {
+		*radius_cui = r0->radius_cui;
+		*radius_cui_len = r0->radius_cui_len;
+	}
+
+	*out_session_timeout = session_timeout;
+
+	*pmk_r1_len = r0->pmk_r0_len;
+
+	return 0;
+}
+
+
+static int wpa_ft_process_auth_req(struct wpa_state_machine *sm,
+				   const u8 *ies, size_t ies_len,
+				   u8 **resp_ies, size_t *resp_ies_len)
+{
+	struct rsn_mdie *mdie;
+	u8 pmk_r1[PMK_LEN_MAX], pmk_r1_name[WPA_PMK_NAME_LEN];
+	u8 ptk_name[WPA_PMK_NAME_LEN];
+	struct wpa_auth_config *conf;
+	struct wpa_ft_ies parse;
+	size_t buflen;
+	int ret;
+	u8 *pos, *end;
+	int pairwise, session_timeout = 0;
+	struct vlan_description vlan;
+	const u8 *identity, *radius_cui;
+	size_t identity_len = 0, radius_cui_len = 0;
+	size_t pmk_r1_len, kdk_len, len;
+
+	*resp_ies = NULL;
+	*resp_ies_len = 0;
+
+	sm->pmk_r1_name_valid = 0;
+	conf = &sm->wpa_auth->conf;
+
+	wpa_hexdump(MSG_DEBUG, "FT: Received authentication frame IEs",
+		    ies, ies_len);
+
+	if (wpa_ft_parse_ies(ies, ies_len, &parse, 0)) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	mdie = (struct rsn_mdie *) parse.mdie;
+	if (mdie == NULL || parse.mdie_len < sizeof(*mdie) ||
+	    os_memcmp(mdie->mobility_domain,
+		      sm->wpa_auth->conf.mobility_domain,
+		      MOBILITY_DOMAIN_ID_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "FT: Invalid MDIE");
+		return WLAN_STATUS_INVALID_MDIE;
+	}
+
+	if (!parse.ftie || parse.ftie_len < sizeof(struct rsn_ftie)) {
+		wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+		return WLAN_STATUS_INVALID_FTIE;
+	}
+
+	if (parse.r0kh_id == NULL) {
+		wpa_printf(MSG_DEBUG, "FT: Invalid FTIE - no R0KH-ID");
+		return WLAN_STATUS_INVALID_FTIE;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "FT: STA R0KH-ID",
+		    parse.r0kh_id, parse.r0kh_id_len);
+	os_memcpy(sm->r0kh_id, parse.r0kh_id, parse.r0kh_id_len);
+	sm->r0kh_id_len = parse.r0kh_id_len;
+
+	if (parse.rsn_pmkid == NULL) {
+		wpa_printf(MSG_DEBUG, "FT: No PMKID in RSNIE");
+		return WLAN_STATUS_INVALID_PMKID;
+	}
+
+	if (wpa_ft_set_key_mgmt(sm, &parse) < 0)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+	wpa_hexdump(MSG_DEBUG, "FT: Requested PMKR0Name",
+		    parse.rsn_pmkid, WPA_PMK_NAME_LEN);
+
+	if (conf->ft_psk_generate_local &&
+	    wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) {
+		if (wpa_derive_pmk_r1_name(parse.rsn_pmkid,
+					   sm->wpa_auth->conf.r1_key_holder,
+					   sm->addr, pmk_r1_name, PMK_LEN) < 0)
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+		if (wpa_ft_psk_pmk_r1(sm, pmk_r1_name, pmk_r1, &pairwise,
+				      &vlan, &identity, &identity_len,
+				      &radius_cui, &radius_cui_len,
+				      &session_timeout) < 0)
+			return WLAN_STATUS_INVALID_PMKID;
+		pmk_r1_len = PMK_LEN;
+		wpa_printf(MSG_DEBUG,
+			   "FT: Generated PMK-R1 for FT-PSK locally");
+		goto pmk_r1_derived;
+	}
+
+	/* Need to test all possible hash algorithms for FT-SAE-EXT-KEY since
+	 * the key length is not yet known. For other AKMs, only the length
+	 * identified by the AKM is used. */
+	for (len = SHA256_MAC_LEN; len <= SHA512_MAC_LEN; len += 16) {
+		if (parse.key_mgmt != WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+		    ((wpa_key_mgmt_sha384(parse.key_mgmt) &&
+		      len != SHA384_MAC_LEN) ||
+		     (!wpa_key_mgmt_sha384(parse.key_mgmt) &&
+		      len != SHA256_MAC_LEN)))
+			continue;
+		if (wpa_derive_pmk_r1_name(parse.rsn_pmkid,
+					   sm->wpa_auth->conf.r1_key_holder,
+					   sm->addr, pmk_r1_name, len) < 0)
+			continue;
+
+		if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name,
+					pmk_r1, &pmk_r1_len, &pairwise, &vlan,
+					&identity, &identity_len, &radius_cui,
+					&radius_cui_len,
+					&session_timeout) == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "FT: Found PMKR1Name (using SHA%zu) from local cache",
+				   pmk_r1_len * 8);
+			goto pmk_r1_derived;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "FT: No PMK-R1 available in local cache for the requested PMKR1Name");
+	if (wpa_ft_local_derive_pmk_r1(sm->wpa_auth, sm,
+				       parse.r0kh_id, parse.r0kh_id_len,
+				       parse.rsn_pmkid,
+				       pmk_r1_name, pmk_r1, &pairwise,
+				       &vlan, &identity, &identity_len,
+				       &radius_cui, &radius_cui_len,
+				       &session_timeout, &pmk_r1_len) == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "FT: Generated PMK-R1 based on local PMK-R0");
+		goto pmk_r1_derived;
+	}
+
+	if (wpa_ft_pull_pmk_r1(sm, ies, ies_len, parse.rsn_pmkid) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "FT: Did not have matching PMK-R1 and either unknown or blocked R0KH-ID or NAK from R0KH");
+		return WLAN_STATUS_INVALID_PMKID;
+	}
+
+	return -1; /* Status pending */
+
+pmk_r1_derived:
+	wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, pmk_r1_len);
+	sm->pmk_r1_name_valid = 1;
+	os_memcpy(sm->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN);
+	os_memcpy(sm->pmk_r1, pmk_r1, pmk_r1_len);
+	sm->pmk_r1_len = pmk_r1_len;
+
+	if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to get random data for "
+			   "ANonce");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	/* Now that we know the correct PMK-R1 length and as such, the length
+	 * of the MIC field, fetch the SNonce. */
+	if (pmk_r1_len == SHA512_MAC_LEN) {
+		const struct rsn_ftie_sha512 *ftie;
+
+		ftie = (const struct rsn_ftie_sha512 *) parse.ftie;
+		if (!ftie || parse.ftie_len < sizeof(*ftie)) {
+			wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+			return WLAN_STATUS_INVALID_FTIE;
+		}
+
+		os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
+	} else if (pmk_r1_len == SHA384_MAC_LEN) {
+		const struct rsn_ftie_sha384 *ftie;
+
+		ftie = (const struct rsn_ftie_sha384 *) parse.ftie;
+		if (!ftie || parse.ftie_len < sizeof(*ftie)) {
+			wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+			return WLAN_STATUS_INVALID_FTIE;
+		}
+
+		os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
+	} else {
+		const struct rsn_ftie *ftie;
+
+		ftie = (const struct rsn_ftie *) parse.ftie;
+		if (!ftie || parse.ftie_len < sizeof(*ftie)) {
+			wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+			return WLAN_STATUS_INVALID_FTIE;
+		}
+
+		os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
+	}
+
+	wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
+		    sm->SNonce, WPA_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: Generated ANonce",
+		    sm->ANonce, WPA_NONCE_LEN);
+
+	if (sm->wpa_auth->conf.force_kdk_derivation ||
+	    (sm->wpa_auth->conf.secure_ltf &&
+	     ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF)))
+		kdk_len = WPA_KDK_MAX_LEN;
+	else
+		kdk_len = 0;
+
+	if (wpa_pmk_r1_to_ptk(pmk_r1, pmk_r1_len, sm->SNonce, sm->ANonce,
+			      sm->addr, sm->wpa_auth->addr, pmk_r1_name,
+			      &sm->PTK, ptk_name, parse.key_mgmt,
+			      pairwise, kdk_len) < 0)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+#ifdef CONFIG_PASN
+	if (sm->wpa_auth->conf.secure_ltf &&
+	    ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF) &&
+	    wpa_ltf_keyseed(&sm->PTK, parse.key_mgmt, pairwise)) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to derive LTF keyseed");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+#endif /* CONFIG_PASN */
+
+	sm->pairwise = pairwise;
+	sm->PTK_valid = true;
+	sm->tk_already_set = false;
+	wpa_ft_install_ptk(sm, 0);
+
+	if (wpa_ft_set_vlan(sm->wpa_auth, sm->addr, &vlan) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to configure VLAN");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+	if (wpa_ft_set_identity(sm->wpa_auth, sm->addr,
+				identity, identity_len) < 0 ||
+	    wpa_ft_set_radius_cui(sm->wpa_auth, sm->addr,
+				  radius_cui, radius_cui_len) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to configure identity/CUI");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+	wpa_ft_set_session_timeout(sm->wpa_auth, sm->addr, session_timeout);
+
+	buflen = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) +
+		2 + FT_R1KH_ID_LEN + 200;
+	*resp_ies = os_zalloc(buflen);
+	if (*resp_ies == NULL)
+		goto fail;
+
+	pos = *resp_ies;
+	end = *resp_ies + buflen;
+
+	ret = wpa_write_rsn_ie(conf, pos, end - pos, parse.rsn_pmkid);
+	if (ret < 0)
+		goto fail;
+	pos += ret;
+
+	ret = wpa_write_mdie(conf, pos, end - pos);
+	if (ret < 0)
+		goto fail;
+	pos += ret;
+
+	ret = wpa_write_ftie(conf, parse.key_mgmt, pmk_r1_len,
+			     parse.r0kh_id, parse.r0kh_id_len,
+			     sm->ANonce, sm->SNonce, pos, end - pos, NULL, 0,
+			     0);
+	if (ret < 0)
+		goto fail;
+	pos += ret;
+
+	*resp_ies_len = pos - *resp_ies;
+
+	return WLAN_STATUS_SUCCESS;
+fail:
+	os_free(*resp_ies);
+	*resp_ies = NULL;
+	return WLAN_STATUS_UNSPECIFIED_FAILURE;
+}
+
+
+void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid,
+			 u16 auth_transaction, const u8 *ies, size_t ies_len,
+			 void (*cb)(void *ctx, const u8 *dst, const u8 *bssid,
+				    u16 auth_transaction, u16 status,
+				    const u8 *ies, size_t ies_len),
+			 void *ctx)
+{
+	u16 status;
+	u8 *resp_ies;
+	size_t resp_ies_len;
+	int res;
+
+	if (sm == NULL) {
+		wpa_printf(MSG_DEBUG, "FT: Received authentication frame, but "
+			   "WPA SM not available");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "FT: Received authentication frame: STA=" MACSTR
+		   " BSSID=" MACSTR " transaction=%d",
+		   MAC2STR(sm->addr), MAC2STR(bssid), auth_transaction);
+	sm->ft_pending_cb = cb;
+	sm->ft_pending_cb_ctx = ctx;
+	sm->ft_pending_auth_transaction = auth_transaction;
+	sm->ft_pending_pull_left_retries = sm->wpa_auth->conf.rkh_pull_retries;
+	res = wpa_ft_process_auth_req(sm, ies, ies_len, &resp_ies,
+				      &resp_ies_len);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Callback postponed until response is available");
+		return;
+	}
+	status = res;
+
+	wpa_printf(MSG_DEBUG, "FT: FT authentication response: dst=" MACSTR
+		   " auth_transaction=%d status=%u (%s)",
+		   MAC2STR(sm->addr), auth_transaction + 1, status,
+		   status2str(status));
+	wpa_hexdump(MSG_DEBUG, "FT: Response IEs", resp_ies, resp_ies_len);
+	cb(ctx, sm->addr, bssid, auth_transaction + 1, status,
+	   resp_ies, resp_ies_len);
+	os_free(resp_ies);
+}
+
+
+int wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
+			    size_t ies_len)
+{
+	struct wpa_ft_ies parse;
+	struct rsn_mdie *mdie;
+	u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
+	size_t mic_len;
+	unsigned int count;
+	const u8 *kck;
+	size_t kck_len;
+	struct wpa_auth_config *conf;
+
+	if (sm == NULL)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+	conf = &sm->wpa_auth->conf;
+
+	wpa_hexdump(MSG_DEBUG, "FT: Reassoc Req IEs", ies, ies_len);
+
+	if (wpa_ft_parse_ies(ies, ies_len, &parse, sm->wpa_key_mgmt) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	if (parse.rsn == NULL) {
+		wpa_printf(MSG_DEBUG, "FT: No RSNIE in Reassoc Req");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	if (parse.rsn_pmkid == NULL) {
+		wpa_printf(MSG_DEBUG, "FT: No PMKID in RSNIE");
+		return WLAN_STATUS_INVALID_PMKID;
+	}
+
+	if (os_memcmp_const(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN)
+	    != 0) {
+		wpa_printf(MSG_DEBUG, "FT: PMKID in Reassoc Req did not match "
+			   "with the PMKR1Name derived from auth request");
+		return WLAN_STATUS_INVALID_PMKID;
+	}
+
+	mdie = (struct rsn_mdie *) parse.mdie;
+	if (mdie == NULL || parse.mdie_len < sizeof(*mdie) ||
+	    os_memcmp(mdie->mobility_domain, conf->mobility_domain,
+		      MOBILITY_DOMAIN_ID_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "FT: Invalid MDIE");
+		return WLAN_STATUS_INVALID_MDIE;
+	}
+
+	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+	    sm->pmk_r1_len == SHA512_MAC_LEN)
+		mic_len = 32;
+	else if ((sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
+		  sm->pmk_r1_len == SHA384_MAC_LEN) ||
+		 wpa_key_mgmt_sha384(sm->wpa_key_mgmt))
+		mic_len = 24;
+	else
+		mic_len = 16;
+
+	if (!parse.ftie || !parse.fte_anonce || !parse.fte_snonce ||
+	    parse.fte_mic_len != mic_len) {
+		wpa_printf(MSG_DEBUG,
+			   "FT: Invalid FTE (fte_mic_len=%zu mic_len=%zu)",
+			   parse.fte_mic_len, mic_len);
+		return WLAN_STATUS_INVALID_FTIE;
+	}
+
+	if (os_memcmp(parse.fte_snonce, sm->SNonce, WPA_NONCE_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE");
+		wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
+			    parse.fte_snonce, WPA_NONCE_LEN);
+		wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
+			    sm->SNonce, WPA_NONCE_LEN);
+		return WLAN_STATUS_INVALID_FTIE;
+	}
+
+	if (os_memcmp(parse.fte_anonce, sm->ANonce, WPA_NONCE_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE");
+		wpa_hexdump(MSG_DEBUG, "FT: Received ANonce",
+			    parse.fte_anonce, WPA_NONCE_LEN);
+		wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce",
+			    sm->ANonce, WPA_NONCE_LEN);
+		return WLAN_STATUS_INVALID_FTIE;
+	}
+
+	if (parse.r0kh_id == NULL) {
+		wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE");
+		return WLAN_STATUS_INVALID_FTIE;
+	}
+
+	if (parse.r0kh_id_len != sm->r0kh_id_len ||
+	    os_memcmp_const(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0)
+	{
+		wpa_printf(MSG_DEBUG, "FT: R0KH-ID in FTIE did not match with "
+			   "the current R0KH-ID");
+		wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID in FTIE",
+			    parse.r0kh_id, parse.r0kh_id_len);
+		wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID",
+			    sm->r0kh_id, sm->r0kh_id_len);
+		return WLAN_STATUS_INVALID_FTIE;
+	}
+
+	if (parse.r1kh_id == NULL) {
+		wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE");
+		return WLAN_STATUS_INVALID_FTIE;
+	}
+
+	if (os_memcmp_const(parse.r1kh_id, conf->r1_key_holder,
+			    FT_R1KH_ID_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "FT: Unknown R1KH-ID used in "
+			   "ReassocReq");
+		wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID in FTIE",
+			    parse.r1kh_id, FT_R1KH_ID_LEN);
+		wpa_hexdump(MSG_DEBUG, "FT: Expected R1KH-ID",
+			    conf->r1_key_holder, FT_R1KH_ID_LEN);
+		return WLAN_STATUS_INVALID_FTIE;
+	}
+
+	if (parse.rsn_pmkid == NULL ||
+	    os_memcmp_const(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN))
+	{
+		wpa_printf(MSG_DEBUG, "FT: No matching PMKR1Name (PMKID) in "
+			   "RSNIE (pmkid=%d)", !!parse.rsn_pmkid);
+		return WLAN_STATUS_INVALID_PMKID;
+	}
+
+	count = 3;
+	if (parse.ric)
+		count += ieee802_11_ie_count(parse.ric, parse.ric_len);
+	if (parse.rsnxe)
+		count++;
+	if (parse.fte_elem_count != count) {
+		wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC "
+			   "Control: received %u expected %u",
+			   parse.fte_elem_count, count);
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+		kck = sm->PTK.kck2;
+		kck_len = sm->PTK.kck2_len;
+	} else {
+		kck = sm->PTK.kck;
+		kck_len = sm->PTK.kck_len;
+	}
+	if (wpa_ft_mic(sm->wpa_key_mgmt, kck, kck_len,
+		       sm->addr, sm->wpa_auth->addr, 5,
+		       parse.mdie - 2, parse.mdie_len + 2,
+		       parse.ftie - 2, parse.ftie_len + 2,
+		       parse.rsn - 2, parse.rsn_len + 2,
+		       parse.ric, parse.ric_len,
+		       parse.rsnxe ? parse.rsnxe - 2 : NULL,
+		       parse.rsnxe ? parse.rsnxe_len + 2 : 0,
+		       mic) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	if (os_memcmp_const(mic, parse.fte_mic, mic_len) != 0) {
+		wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE");
+		wpa_printf(MSG_DEBUG, "FT: addr=" MACSTR " auth_addr=" MACSTR,
+			   MAC2STR(sm->addr), MAC2STR(sm->wpa_auth->addr));
+		wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC",
+			    parse.fte_mic, mic_len);
+		wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, mic_len);
+		wpa_hexdump(MSG_MSGDUMP, "FT: MDIE",
+			    parse.mdie - 2, parse.mdie_len + 2);
+		wpa_hexdump(MSG_MSGDUMP, "FT: FTIE",
+			    parse.ftie - 2, parse.ftie_len + 2);
+		wpa_hexdump(MSG_MSGDUMP, "FT: RSN",
+			    parse.rsn - 2, parse.rsn_len + 2);
+		wpa_hexdump(MSG_MSGDUMP, "FT: RSNXE",
+			    parse.rsnxe ? parse.rsnxe - 2 : NULL,
+			    parse.rsnxe ? parse.rsnxe_len + 2 : 0);
+		return WLAN_STATUS_INVALID_FTIE;
+	}
+
+	if (parse.fte_rsnxe_used &&
+	    (conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+	     conf->sae_pwe == SAE_PWE_BOTH) &&
+	    !parse.rsnxe) {
+		wpa_printf(MSG_INFO,
+			   "FT: FTE indicated that STA uses RSNXE, but RSNXE was not included");
+		return -1; /* discard request */
+	}
+
+#ifdef CONFIG_OCV
+	if (wpa_auth_uses_ocv(sm)) {
+		struct wpa_channel_info ci;
+		int tx_chanwidth;
+		int tx_seg1_idx;
+		enum oci_verify_result res;
+
+		if (wpa_channel_info(sm->wpa_auth, &ci) != 0) {
+			wpa_printf(MSG_WARNING,
+				   "Failed to get channel info to validate received OCI in (Re)Assoc Request");
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+		}
+
+		if (get_sta_tx_parameters(sm,
+					  channel_width_to_int(ci.chanwidth),
+					  ci.seg1_idx, &tx_chanwidth,
+					  &tx_seg1_idx) < 0)
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+		res = ocv_verify_tx_params(parse.oci, parse.oci_len, &ci,
+					   tx_chanwidth, tx_seg1_idx);
+		if (wpa_auth_uses_ocv(sm) == 2 && res == OCI_NOT_FOUND) {
+			/* Work around misbehaving STAs */
+			wpa_printf(MSG_INFO,
+				   "Disable OCV with a STA that does not send OCI");
+			wpa_auth_set_ocv(sm, 0);
+		} else if (res != OCI_SUCCESS) {
+			wpa_printf(MSG_WARNING, "OCV failed: %s", ocv_errorstr);
+			if (sm->wpa_auth->conf.msg_ctx)
+				wpa_msg(sm->wpa_auth->conf.msg_ctx, MSG_INFO,
+					OCV_FAILURE "addr=" MACSTR
+					" frame=ft-reassoc-req error=%s",
+					MAC2STR(sm->addr), ocv_errorstr);
+			return WLAN_STATUS_INVALID_FTIE;
+		}
+	}
+#endif /* CONFIG_OCV */
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len)
+{
+	const u8 *sta_addr, *target_ap;
+	const u8 *ies;
+	size_t ies_len;
+	u8 action;
+	struct ft_rrb_frame *frame;
+
+	if (sm == NULL)
+		return -1;
+
+	/*
+	 * data: Category[1] Action[1] STA_Address[6] Target_AP_Address[6]
+	 * FT Request action frame body[variable]
+	 */
+
+	if (len < 14) {
+		wpa_printf(MSG_DEBUG, "FT: Too short FT Action frame "
+			   "(len=%lu)", (unsigned long) len);
+		return -1;
+	}
+
+	action = data[1];
+	sta_addr = data + 2;
+	target_ap = data + 8;
+	ies = data + 14;
+	ies_len = len - 14;
+
+	wpa_printf(MSG_DEBUG, "FT: Received FT Action frame (STA=" MACSTR
+		   " Target AP=" MACSTR " Action=%d)",
+		   MAC2STR(sta_addr), MAC2STR(target_ap), action);
+
+	if (os_memcmp(sta_addr, sm->addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "FT: Mismatch in FT Action STA address: "
+			   "STA=" MACSTR " STA-Address=" MACSTR,
+			   MAC2STR(sm->addr), MAC2STR(sta_addr));
+		return -1;
+	}
+
+	/*
+	 * Do some validity checking on the target AP address (not own and not
+	 * broadcast. This could be extended to filter based on a list of known
+	 * APs in the MD (if such a list were configured).
+	 */
+	if ((target_ap[0] & 0x01) ||
+	    os_memcmp(target_ap, sm->wpa_auth->addr, ETH_ALEN) == 0) {
+		wpa_printf(MSG_DEBUG, "FT: Invalid Target AP in FT Action "
+			   "frame");
+		return -1;
+	}
+
+	wpa_hexdump(MSG_MSGDUMP, "FT: Action frame body", ies, ies_len);
+
+	if (!sm->wpa_auth->conf.ft_over_ds) {
+		wpa_printf(MSG_DEBUG, "FT: Over-DS option disabled - reject");
+		return -1;
+	}
+
+	/* RRB - Forward action frame to the target AP */
+	frame = os_malloc(sizeof(*frame) + len);
+	if (frame == NULL)
+		return -1;
+	frame->frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
+	frame->packet_type = FT_PACKET_REQUEST;
+	frame->action_length = host_to_le16(len);
+	os_memcpy(frame->ap_address, sm->wpa_auth->addr, ETH_ALEN);
+	os_memcpy(frame + 1, data, len);
+
+	wpa_ft_rrb_send(sm->wpa_auth, target_ap, (u8 *) frame,
+			sizeof(*frame) + len);
+	os_free(frame);
+
+	return 0;
+}
+
+
+static void wpa_ft_rrb_rx_request_cb(void *ctx, const u8 *dst, const u8 *bssid,
+				     u16 auth_transaction, u16 resp,
+				     const u8 *ies, size_t ies_len)
+{
+	struct wpa_state_machine *sm = ctx;
+	wpa_printf(MSG_DEBUG, "FT: Over-the-DS RX request cb for " MACSTR,
+		   MAC2STR(sm->addr));
+	wpa_ft_send_rrb_auth_resp(sm, sm->ft_pending_current_ap, sm->addr,
+				  WLAN_STATUS_SUCCESS, ies, ies_len);
+}
+
+
+static int wpa_ft_rrb_rx_request(struct wpa_authenticator *wpa_auth,
+				 const u8 *current_ap, const u8 *sta_addr,
+				 const u8 *body, size_t len)
+{
+	struct wpa_state_machine *sm;
+	u16 status;
+	u8 *resp_ies;
+	size_t resp_ies_len;
+	int res;
+
+	sm = wpa_ft_add_sta(wpa_auth, sta_addr);
+	if (sm == NULL) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to add new STA based on "
+			   "RRB Request");
+		return -1;
+	}
+
+	wpa_hexdump(MSG_MSGDUMP, "FT: RRB Request Frame body", body, len);
+
+	sm->ft_pending_cb = wpa_ft_rrb_rx_request_cb;
+	sm->ft_pending_cb_ctx = sm;
+	os_memcpy(sm->ft_pending_current_ap, current_ap, ETH_ALEN);
+	sm->ft_pending_pull_left_retries = sm->wpa_auth->conf.rkh_pull_retries;
+	res = wpa_ft_process_auth_req(sm, body, len, &resp_ies,
+				      &resp_ies_len);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG, "FT: No immediate response available - wait for pull response");
+		return 0;
+	}
+	status = res;
+
+	res = wpa_ft_send_rrb_auth_resp(sm, current_ap, sta_addr, status,
+					resp_ies, resp_ies_len);
+	os_free(resp_ies);
+	return res;
+}
+
+
+static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm,
+				     const u8 *current_ap, const u8 *sta_addr,
+				     u16 status, const u8 *resp_ies,
+				     size_t resp_ies_len)
+{
+	struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+	size_t rlen;
+	struct ft_rrb_frame *frame;
+	u8 *pos;
+
+	wpa_printf(MSG_DEBUG, "FT: RRB authentication response: STA=" MACSTR
+		   " CurrentAP=" MACSTR " status=%u (%s)",
+		   MAC2STR(sm->addr), MAC2STR(current_ap), status,
+		   status2str(status));
+	wpa_hexdump(MSG_DEBUG, "FT: Response IEs", resp_ies, resp_ies_len);
+
+	/* RRB - Forward action frame response to the Current AP */
+
+	/*
+	 * data: Category[1] Action[1] STA_Address[6] Target_AP_Address[6]
+	 * Status_Code[2] FT Request action frame body[variable]
+	 */
+	rlen = 2 + 2 * ETH_ALEN + 2 + resp_ies_len;
+
+	frame = os_malloc(sizeof(*frame) + rlen);
+	if (frame == NULL)
+		return -1;
+	frame->frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
+	frame->packet_type = FT_PACKET_RESPONSE;
+	frame->action_length = host_to_le16(rlen);
+	os_memcpy(frame->ap_address, wpa_auth->addr, ETH_ALEN);
+	pos = (u8 *) (frame + 1);
+	*pos++ = WLAN_ACTION_FT;
+	*pos++ = 2; /* Action: Response */
+	os_memcpy(pos, sta_addr, ETH_ALEN);
+	pos += ETH_ALEN;
+	os_memcpy(pos, wpa_auth->addr, ETH_ALEN);
+	pos += ETH_ALEN;
+	WPA_PUT_LE16(pos, status);
+	pos += 2;
+	if (resp_ies)
+		os_memcpy(pos, resp_ies, resp_ies_len);
+
+	wpa_ft_rrb_send(wpa_auth, current_ap, (u8 *) frame,
+			sizeof(*frame) + rlen);
+	os_free(frame);
+
+	return 0;
+}
+
+
+static int wpa_ft_rrb_build_r0(const u8 *key, const size_t key_len,
+			       const struct tlv_list *tlvs,
+			       const struct wpa_ft_pmk_r0_sa *pmk_r0,
+			       const u8 *r1kh_id, const u8 *s1kh_id,
+			       const struct tlv_list *tlv_auth,
+			       const u8 *src_addr, u8 type,
+			       u8 **packet, size_t *packet_len)
+{
+	u8 pmk_r1[PMK_LEN_MAX];
+	size_t pmk_r1_len = pmk_r0->pmk_r0_len;
+	u8 pmk_r1_name[WPA_PMK_NAME_LEN];
+	u8 f_pairwise[sizeof(le16)];
+	u8 f_expires_in[sizeof(le16)];
+	u8 f_session_timeout[sizeof(le32)];
+	int expires_in;
+	int session_timeout;
+	struct os_reltime now;
+	int ret;
+	struct tlv_list sess_tlv[] = {
+		{ .type = FT_RRB_PMK_R1, .len = pmk_r1_len,
+		  .data = pmk_r1 },
+		{ .type = FT_RRB_PMK_R1_NAME, .len = sizeof(pmk_r1_name),
+		  .data = pmk_r1_name },
+		{ .type = FT_RRB_PAIRWISE, .len = sizeof(f_pairwise),
+		  .data = f_pairwise },
+		{ .type = FT_RRB_EXPIRES_IN, .len = sizeof(f_expires_in),
+		  .data = f_expires_in },
+		{ .type = FT_RRB_IDENTITY, .len = pmk_r0->identity_len,
+		  .data = pmk_r0->identity },
+		{ .type = FT_RRB_RADIUS_CUI, .len = pmk_r0->radius_cui_len,
+		  .data = pmk_r0->radius_cui },
+		{ .type = FT_RRB_SESSION_TIMEOUT,
+		  .len = sizeof(f_session_timeout),
+		  .data = f_session_timeout },
+		{ .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+	};
+
+	wpa_printf(MSG_DEBUG, "FT: Derive PMK-R1 for peer AP");
+	if (wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_len,
+			      pmk_r0->pmk_r0_name, r1kh_id,
+			      s1kh_id, pmk_r1, pmk_r1_name) < 0)
+		return -1;
+	WPA_PUT_LE16(f_pairwise, pmk_r0->pairwise);
+
+	os_get_reltime(&now);
+	if (pmk_r0->expiration > now.sec)
+		expires_in = pmk_r0->expiration - now.sec;
+	else if (pmk_r0->expiration)
+		expires_in = 1;
+	else
+		expires_in = 0;
+	WPA_PUT_LE16(f_expires_in, expires_in);
+
+	if (pmk_r0->session_timeout > now.sec)
+		session_timeout = pmk_r0->session_timeout - now.sec;
+	else if (pmk_r0->session_timeout)
+		session_timeout = 1;
+	else
+		session_timeout = 0;
+	WPA_PUT_LE32(f_session_timeout, session_timeout);
+
+	ret = wpa_ft_rrb_build(key, key_len, tlvs, sess_tlv, tlv_auth,
+			       pmk_r0->vlan, src_addr, type,
+			       packet, packet_len);
+
+	forced_memzero(pmk_r1, sizeof(pmk_r1));
+
+	return ret;
+}
+
+
+static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
+			      const u8 *src_addr,
+			      const u8 *enc, size_t enc_len,
+			      const u8 *auth, size_t auth_len,
+			      int no_defer)
+{
+	const char *msgtype = "pull request";
+	u8 *plain = NULL, *packet = NULL;
+	size_t plain_len = 0, packet_len = 0;
+	struct ft_remote_r1kh *r1kh, *r1kh_wildcard;
+	const u8 *key;
+	size_t key_len;
+	int seq_ret;
+	const u8 *f_nonce, *f_r0kh_id, *f_r1kh_id, *f_s1kh_id, *f_pmk_r0_name;
+	size_t f_nonce_len, f_r0kh_id_len, f_r1kh_id_len, f_s1kh_id_len;
+	size_t f_pmk_r0_name_len;
+	const struct wpa_ft_pmk_r0_sa *r0;
+	int ret;
+	struct tlv_list resp[2];
+	struct tlv_list resp_auth[5];
+	struct ft_rrb_seq f_seq;
+
+	wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull");
+
+	RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, msgtype, -1);
+	wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID", f_r0kh_id, f_r0kh_id_len);
+
+	if (wpa_ft_rrb_check_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len)) {
+		wpa_printf(MSG_DEBUG, "FT: R0KH-ID mismatch");
+		goto out;
+	}
+
+	RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, msgtype, FT_R1KH_ID_LEN);
+	wpa_printf(MSG_DEBUG, "FT: R1KH-ID=" MACSTR, MAC2STR(f_r1kh_id));
+
+	wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh, &r1kh_wildcard);
+	if (r1kh) {
+		key = r1kh->key;
+		key_len = sizeof(r1kh->key);
+	} else if (r1kh_wildcard) {
+		wpa_printf(MSG_DEBUG, "FT: Using wildcard R1KH-ID");
+		key = r1kh_wildcard->key;
+		key_len = sizeof(r1kh_wildcard->key);
+	} else {
+		goto out;
+	}
+
+	RRB_GET_AUTH(FT_RRB_NONCE, nonce, "pull request", FT_RRB_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: nonce", f_nonce, f_nonce_len);
+
+	seq_ret = FT_RRB_SEQ_DROP;
+	if (r1kh)
+		seq_ret = wpa_ft_rrb_seq_chk(r1kh->seq, src_addr, enc, enc_len,
+					     auth, auth_len, msgtype, no_defer);
+	if (!no_defer && r1kh_wildcard &&
+	    (!r1kh || os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0)) {
+		/* wildcard: r1kh-id unknown or changed addr -> do a seq req */
+		seq_ret = FT_RRB_SEQ_DEFER;
+	}
+
+	if (seq_ret == FT_RRB_SEQ_DROP)
+		goto out;
+
+	if (wpa_ft_rrb_decrypt(key, key_len, enc, enc_len, auth, auth_len,
+			       src_addr, FT_PACKET_R0KH_R1KH_PULL,
+			       &plain, &plain_len) < 0)
+		goto out;
+
+	if (!r1kh)
+		r1kh = wpa_ft_rrb_add_r1kh(wpa_auth, r1kh_wildcard, src_addr,
+					   f_r1kh_id,
+					   wpa_auth->conf.rkh_pos_timeout);
+	if (!r1kh)
+		goto out;
+
+	if (seq_ret == FT_RRB_SEQ_DEFER) {
+		wpa_ft_rrb_seq_req(wpa_auth, r1kh->seq, src_addr, f_r0kh_id,
+				   f_r0kh_id_len, f_r1kh_id, key, key_len,
+				   enc, enc_len, auth, auth_len,
+				   &wpa_ft_rrb_rx_pull);
+		goto out;
+	}
+
+	wpa_ft_rrb_seq_accept(wpa_auth, r1kh->seq, src_addr, auth, auth_len,
+			      msgtype);
+	wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh,
+				  wpa_auth->conf.rkh_pos_timeout);
+
+	RRB_GET(FT_RRB_PMK_R0_NAME, pmk_r0_name, msgtype, WPA_PMK_NAME_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", f_pmk_r0_name,
+		    f_pmk_r0_name_len);
+
+	RRB_GET(FT_RRB_S1KH_ID, s1kh_id, msgtype, ETH_ALEN);
+	wpa_printf(MSG_DEBUG, "FT: S1KH-ID=" MACSTR, MAC2STR(f_s1kh_id));
+
+	if (wpa_ft_new_seq(r1kh->seq, &f_seq) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to get seq num");
+		goto out;
+	}
+
+	wpa_printf(MSG_DEBUG, "FT: Send PMK-R1 pull response from " MACSTR
+		   " to " MACSTR,
+		   MAC2STR(wpa_auth->addr), MAC2STR(src_addr));
+
+	resp[0].type = FT_RRB_S1KH_ID;
+	resp[0].len = f_s1kh_id_len;
+	resp[0].data = f_s1kh_id;
+	resp[1].type = FT_RRB_LAST_EMPTY;
+	resp[1].len = 0;
+	resp[1].data = NULL;
+
+	resp_auth[0].type = FT_RRB_NONCE;
+	resp_auth[0].len = f_nonce_len;
+	resp_auth[0].data = f_nonce;
+	resp_auth[1].type = FT_RRB_SEQ;
+	resp_auth[1].len = sizeof(f_seq);
+	resp_auth[1].data = (u8 *) &f_seq;
+	resp_auth[2].type = FT_RRB_R0KH_ID;
+	resp_auth[2].len = f_r0kh_id_len;
+	resp_auth[2].data = f_r0kh_id;
+	resp_auth[3].type = FT_RRB_R1KH_ID;
+	resp_auth[3].len = f_r1kh_id_len;
+	resp_auth[3].data = f_r1kh_id;
+	resp_auth[4].type = FT_RRB_LAST_EMPTY;
+	resp_auth[4].len = 0;
+	resp_auth[4].data = NULL;
+
+	if (wpa_ft_fetch_pmk_r0(wpa_auth, f_s1kh_id, f_pmk_r0_name, &r0) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: No matching PMK-R0-Name found");
+		ret = wpa_ft_rrb_build(key, key_len, resp, NULL, resp_auth,
+				       NULL, wpa_auth->addr,
+				       FT_PACKET_R0KH_R1KH_RESP,
+				       &packet, &packet_len);
+	} else {
+		ret = wpa_ft_rrb_build_r0(key, key_len, resp, r0, f_r1kh_id,
+					  f_s1kh_id, resp_auth, wpa_auth->addr,
+					  FT_PACKET_R0KH_R1KH_RESP,
+					  &packet, &packet_len);
+	}
+
+	if (!ret)
+		wpa_ft_rrb_oui_send(wpa_auth, src_addr,
+				    FT_PACKET_R0KH_R1KH_RESP, packet,
+				    packet_len);
+
+out:
+	os_free(plain);
+	os_free(packet);
+
+	return 0;
+}
+
+
+/* @returns  0 on success
+ *          -1 on error
+ *          -2 if FR_RRB_PAIRWISE is missing
+ */
+static int wpa_ft_rrb_rx_r1(struct wpa_authenticator *wpa_auth,
+			    const u8 *src_addr, u8 type,
+			    const u8 *enc, size_t enc_len,
+			    const u8 *auth, size_t auth_len,
+			    const char *msgtype, u8 *s1kh_id_out,
+			    int (*cb)(struct wpa_authenticator *wpa_auth,
+				      const u8 *src_addr,
+				      const u8 *enc, size_t enc_len,
+				      const u8 *auth, size_t auth_len,
+				      int no_defer))
+{
+	u8 *plain = NULL;
+	size_t plain_len = 0;
+	struct ft_remote_r0kh *r0kh, *r0kh_wildcard;
+	const u8 *key;
+	size_t key_len;
+	int seq_ret;
+	const u8 *f_r1kh_id, *f_s1kh_id, *f_r0kh_id;
+	const u8 *f_pmk_r1_name, *f_pairwise, *f_pmk_r1;
+	const u8 *f_expires_in;
+	size_t f_r1kh_id_len, f_s1kh_id_len, f_r0kh_id_len;
+	const u8 *f_identity, *f_radius_cui;
+	const u8 *f_session_timeout;
+	size_t f_pmk_r1_name_len, f_pairwise_len, f_pmk_r1_len;
+	size_t f_expires_in_len;
+	size_t f_identity_len, f_radius_cui_len;
+	size_t f_session_timeout_len;
+	int pairwise;
+	int ret = -1;
+	int expires_in;
+	int session_timeout;
+	struct vlan_description vlan;
+	size_t pmk_r1_len;
+
+	RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, msgtype, -1);
+	wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID", f_r0kh_id, f_r0kh_id_len);
+
+	RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, msgtype, FT_R1KH_ID_LEN);
+	wpa_printf(MSG_DEBUG, "FT: R1KH-ID=" MACSTR, MAC2STR(f_r1kh_id));
+
+	if (wpa_ft_rrb_check_r1kh(wpa_auth, f_r1kh_id)) {
+		wpa_printf(MSG_DEBUG, "FT: R1KH-ID mismatch");
+		goto out;
+	}
+
+	wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len, &r0kh,
+			       &r0kh_wildcard);
+	if (r0kh) {
+		key = r0kh->key;
+		key_len = sizeof(r0kh->key);
+	} else if (r0kh_wildcard) {
+		wpa_printf(MSG_DEBUG, "FT: Using wildcard R0KH-ID");
+		key = r0kh_wildcard->key;
+		key_len = sizeof(r0kh_wildcard->key);
+	} else {
+		goto out;
+	}
+
+	seq_ret = FT_RRB_SEQ_DROP;
+	if (r0kh) {
+		seq_ret = wpa_ft_rrb_seq_chk(r0kh->seq, src_addr, enc, enc_len,
+					     auth, auth_len, msgtype,
+					     cb ? 0 : 1);
+	}
+	if (cb && r0kh_wildcard &&
+	    (!r0kh || os_memcmp(r0kh->addr, src_addr, ETH_ALEN) != 0)) {
+		/* wildcard: r0kh-id unknown or changed addr -> do a seq req */
+		seq_ret = FT_RRB_SEQ_DEFER;
+	}
+
+	if (seq_ret == FT_RRB_SEQ_DROP)
+		goto out;
+
+	if (wpa_ft_rrb_decrypt(key, key_len, enc, enc_len, auth, auth_len,
+			       src_addr, type, &plain, &plain_len) < 0)
+		goto out;
+
+	if (!r0kh)
+		r0kh = wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard, src_addr,
+					   f_r0kh_id, f_r0kh_id_len,
+					   wpa_auth->conf.rkh_pos_timeout);
+	if (!r0kh)
+		goto out;
+
+	if (seq_ret == FT_RRB_SEQ_DEFER) {
+		wpa_ft_rrb_seq_req(wpa_auth, r0kh->seq, src_addr, f_r0kh_id,
+				   f_r0kh_id_len, f_r1kh_id, key, key_len,
+				   enc, enc_len, auth, auth_len, cb);
+		goto out;
+	}
+
+	wpa_ft_rrb_seq_accept(wpa_auth, r0kh->seq, src_addr, auth, auth_len,
+			      msgtype);
+	wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh,
+				  wpa_auth->conf.rkh_pos_timeout);
+
+	RRB_GET(FT_RRB_S1KH_ID, s1kh_id, msgtype, ETH_ALEN);
+	wpa_printf(MSG_DEBUG, "FT: S1KH-ID=" MACSTR, MAC2STR(f_s1kh_id));
+
+	if (s1kh_id_out)
+		os_memcpy(s1kh_id_out, f_s1kh_id, ETH_ALEN);
+
+	ret = -2;
+	RRB_GET(FT_RRB_PAIRWISE, pairwise, msgtype, sizeof(le16));
+	wpa_hexdump(MSG_DEBUG, "FT: pairwise", f_pairwise, f_pairwise_len);
+
+	ret = -1;
+	RRB_GET(FT_RRB_PMK_R1_NAME, pmk_r1_name, msgtype, WPA_PMK_NAME_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name",
+		    f_pmk_r1_name, WPA_PMK_NAME_LEN);
+
+	pmk_r1_len = PMK_LEN;
+	if (wpa_ft_rrb_get_tlv(plain, plain_len, FT_RRB_PMK_R1, &f_pmk_r1_len,
+			       &f_pmk_r1) == 0 &&
+	    (f_pmk_r1_len == PMK_LEN || f_pmk_r1_len == SHA384_MAC_LEN ||
+	     f_pmk_r1_len == SHA512_MAC_LEN))
+		pmk_r1_len = f_pmk_r1_len;
+	RRB_GET(FT_RRB_PMK_R1, pmk_r1, msgtype, pmk_r1_len);
+	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f_pmk_r1, pmk_r1_len);
+
+	pairwise = WPA_GET_LE16(f_pairwise);
+
+	RRB_GET_OPTIONAL(FT_RRB_EXPIRES_IN, expires_in, msgtype,
+			 sizeof(le16));
+	if (f_expires_in)
+		expires_in = WPA_GET_LE16(f_expires_in);
+	else
+		expires_in = 0;
+
+	wpa_printf(MSG_DEBUG, "FT: PMK-R1 %s - expires_in=%d", msgtype,
+		   expires_in);
+
+	if (wpa_ft_rrb_get_tlv_vlan(plain, plain_len, &vlan) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Cannot parse vlan");
+		wpa_ft_rrb_dump(plain, plain_len);
+		goto out;
+	}
+
+	wpa_printf(MSG_DEBUG, "FT: vlan %d%s",
+		   le_to_host16(vlan.untagged), vlan.tagged[0] ? "+" : "");
+
+	RRB_GET_OPTIONAL(FT_RRB_IDENTITY, identity, msgtype, -1);
+	if (f_identity)
+		wpa_hexdump_ascii(MSG_DEBUG, "FT: Identity", f_identity,
+				  f_identity_len);
+
+	RRB_GET_OPTIONAL(FT_RRB_RADIUS_CUI, radius_cui, msgtype, -1);
+	if (f_radius_cui)
+		wpa_hexdump_ascii(MSG_DEBUG, "FT: CUI", f_radius_cui,
+				  f_radius_cui_len);
+
+	RRB_GET_OPTIONAL(FT_RRB_SESSION_TIMEOUT, session_timeout, msgtype,
+			 sizeof(le32));
+	if (f_session_timeout)
+		session_timeout = WPA_GET_LE32(f_session_timeout);
+	else
+		session_timeout = 0;
+	wpa_printf(MSG_DEBUG, "FT: session_timeout %d", session_timeout);
+
+	if (wpa_ft_store_pmk_r1(wpa_auth, f_s1kh_id, f_pmk_r1, pmk_r1_len,
+				f_pmk_r1_name,
+				pairwise, &vlan, expires_in, session_timeout,
+				f_identity, f_identity_len, f_radius_cui,
+				f_radius_cui_len) < 0)
+		goto out;
+
+	ret = 0;
+out:
+	bin_clear_free(plain, plain_len);
+
+	return ret;
+
+}
+
+
+static void ft_finish_pull(struct wpa_state_machine *sm)
+{
+	int res;
+	u8 *resp_ies;
+	size_t resp_ies_len;
+	u16 status;
+
+	if (!sm->ft_pending_cb || !sm->ft_pending_req_ies)
+		return;
+
+	res = wpa_ft_process_auth_req(sm, wpabuf_head(sm->ft_pending_req_ies),
+				      wpabuf_len(sm->ft_pending_req_ies),
+				      &resp_ies, &resp_ies_len);
+	if (res < 0) {
+		/* this loop is broken by ft_pending_pull_left_retries */
+		wpa_printf(MSG_DEBUG,
+			   "FT: Callback postponed until response is available");
+		return;
+	}
+	wpabuf_free(sm->ft_pending_req_ies);
+	sm->ft_pending_req_ies = NULL;
+	status = res;
+	wpa_printf(MSG_DEBUG, "FT: Postponed auth callback result for " MACSTR
+		   " - status %u", MAC2STR(sm->addr), status);
+
+	sm->ft_pending_cb(sm->ft_pending_cb_ctx, sm->addr, sm->wpa_auth->addr,
+			  sm->ft_pending_auth_transaction + 1, status,
+			  resp_ies, resp_ies_len);
+	os_free(resp_ies);
+}
+
+
+struct ft_get_sta_ctx {
+	const u8 *nonce;
+	const u8 *s1kh_id;
+	struct wpa_state_machine *sm;
+};
+
+
+static int ft_get_sta_cb(struct wpa_state_machine *sm, void *ctx)
+{
+	struct ft_get_sta_ctx *info = ctx;
+
+	if ((info->s1kh_id &&
+	     os_memcmp(info->s1kh_id, sm->addr, ETH_ALEN) != 0) ||
+	    os_memcmp(info->nonce, sm->ft_pending_pull_nonce,
+		      FT_RRB_NONCE_LEN) != 0 ||
+	    sm->ft_pending_cb == NULL || sm->ft_pending_req_ies == NULL)
+		return 0;
+
+	info->sm = sm;
+
+	return 1;
+}
+
+
+static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth,
+			      const u8 *src_addr,
+			      const u8 *enc, size_t enc_len,
+			      const u8 *auth, size_t auth_len,
+			      int no_defer)
+{
+	const char *msgtype = "pull response";
+	int nak, ret = -1;
+	struct ft_get_sta_ctx ctx;
+	u8 s1kh_id[ETH_ALEN];
+	const u8 *f_nonce;
+	size_t f_nonce_len;
+
+	wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull response");
+
+	RRB_GET_AUTH(FT_RRB_NONCE, nonce, msgtype, FT_RRB_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: nonce", f_nonce, f_nonce_len);
+
+	os_memset(&ctx, 0, sizeof(ctx));
+	ctx.nonce = f_nonce;
+	if (!wpa_auth_for_each_sta(wpa_auth, ft_get_sta_cb, &ctx)) {
+		/* nonce not found */
+		wpa_printf(MSG_DEBUG, "FT: Invalid nonce");
+		return -1;
+	}
+
+	ret = wpa_ft_rrb_rx_r1(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_RESP,
+			       enc, enc_len, auth, auth_len, msgtype, s1kh_id,
+			       no_defer ? NULL : &wpa_ft_rrb_rx_resp);
+	if (ret == -2) {
+		ret = 0;
+		nak = 1;
+	} else {
+		nak = 0;
+	}
+	if (ret < 0)
+		return -1;
+
+	ctx.s1kh_id = s1kh_id;
+	if (wpa_auth_for_each_sta(wpa_auth, ft_get_sta_cb, &ctx)) {
+		wpa_printf(MSG_DEBUG,
+			   "FT: Response to a pending pull request for " MACSTR,
+			   MAC2STR(ctx.sm->addr));
+		eloop_cancel_timeout(wpa_ft_expire_pull, ctx.sm, NULL);
+		if (nak)
+			ctx.sm->ft_pending_pull_left_retries = 0;
+		ft_finish_pull(ctx.sm);
+	}
+
+out:
+	return ret;
+}
+
+
+static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth,
+			      const u8 *src_addr,
+			      const u8 *enc, size_t enc_len,
+			      const u8 *auth, size_t auth_len, int no_defer)
+{
+	const char *msgtype = "push";
+
+	wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 push");
+
+	if (wpa_ft_rrb_rx_r1(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_PUSH,
+			     enc, enc_len, auth, auth_len, msgtype, NULL,
+			     no_defer ? NULL : wpa_ft_rrb_rx_push) < 0)
+		return -1;
+
+	return 0;
+}
+
+
+static int wpa_ft_rrb_rx_seq(struct wpa_authenticator *wpa_auth,
+			     const u8 *src_addr, int type,
+			     const u8 *enc, size_t enc_len,
+			     const u8 *auth, size_t auth_len,
+			     struct ft_remote_seq **rkh_seq,
+			     u8 **key, size_t *key_len,
+			     struct ft_remote_r0kh **r0kh_out,
+			     struct ft_remote_r1kh **r1kh_out,
+			     struct ft_remote_r0kh **r0kh_wildcard_out,
+			     struct ft_remote_r1kh **r1kh_wildcard_out)
+{
+	struct ft_remote_r0kh *r0kh = NULL;
+	struct ft_remote_r1kh *r1kh = NULL;
+	const u8 *f_r0kh_id, *f_r1kh_id;
+	size_t f_r0kh_id_len, f_r1kh_id_len;
+	int to_r0kh, to_r1kh;
+	u8 *plain = NULL;
+	size_t plain_len = 0;
+	struct ft_remote_r0kh *r0kh_wildcard;
+	struct ft_remote_r1kh *r1kh_wildcard;
+
+	RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, "seq", -1);
+	RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, "seq", FT_R1KH_ID_LEN);
+
+	to_r0kh = !wpa_ft_rrb_check_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len);
+	to_r1kh = !wpa_ft_rrb_check_r1kh(wpa_auth, f_r1kh_id);
+
+	if (to_r0kh && to_r1kh) {
+		wpa_printf(MSG_DEBUG, "FT: seq - local R0KH-ID and R1KH-ID");
+		goto out;
+	}
+
+	if (!to_r0kh && !to_r1kh) {
+		wpa_printf(MSG_DEBUG, "FT: seq - remote R0KH-ID and R1KH-ID");
+		goto out;
+	}
+
+	if (!to_r0kh) {
+		wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len,
+				       &r0kh, &r0kh_wildcard);
+		if (!r0kh_wildcard &&
+		    (!r0kh || os_memcmp(r0kh->addr, src_addr, ETH_ALEN) != 0)) {
+			wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID",
+				    f_r0kh_id, f_r0kh_id_len);
+			goto out;
+		}
+		if (r0kh) {
+			*key = r0kh->key;
+			*key_len = sizeof(r0kh->key);
+		} else {
+			*key = r0kh_wildcard->key;
+			*key_len = sizeof(r0kh_wildcard->key);
+		}
+	}
+
+	if (!to_r1kh) {
+		wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh,
+				       &r1kh_wildcard);
+		if (!r1kh_wildcard &&
+		    (!r1kh || os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0)) {
+			wpa_hexdump(MSG_DEBUG, "FT: Did not find R1KH-ID",
+				    f_r1kh_id, FT_R1KH_ID_LEN);
+			goto out;
+		}
+		if (r1kh) {
+			*key = r1kh->key;
+			*key_len = sizeof(r1kh->key);
+		} else {
+			*key = r1kh_wildcard->key;
+			*key_len = sizeof(r1kh_wildcard->key);
+		}
+	}
+
+	if (wpa_ft_rrb_decrypt(*key, *key_len, enc, enc_len, auth, auth_len,
+			       src_addr, type, &plain, &plain_len) < 0)
+		goto out;
+
+	os_free(plain);
+
+	if (!to_r0kh) {
+		if (!r0kh)
+			r0kh = wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard,
+						   src_addr, f_r0kh_id,
+						   f_r0kh_id_len,
+						   ftRRBseqTimeout);
+		if (!r0kh)
+			goto out;
+
+		wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh, ftRRBseqTimeout);
+		*rkh_seq = r0kh->seq;
+		if (r0kh_out)
+			*r0kh_out = r0kh;
+		if (r0kh_wildcard_out)
+			*r0kh_wildcard_out = r0kh_wildcard;
+	}
+
+	if (!to_r1kh) {
+		if (!r1kh)
+			r1kh = wpa_ft_rrb_add_r1kh(wpa_auth, r1kh_wildcard,
+						   src_addr, f_r1kh_id,
+						   ftRRBseqTimeout);
+		if (!r1kh)
+			goto out;
+
+		wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh, ftRRBseqTimeout);
+		*rkh_seq = r1kh->seq;
+		if (r1kh_out)
+			*r1kh_out = r1kh;
+		if (r1kh_wildcard_out)
+			*r1kh_wildcard_out = r1kh_wildcard;
+	}
+
+	return 0;
+out:
+	return -1;
+}
+
+
+static int wpa_ft_rrb_rx_seq_req(struct wpa_authenticator *wpa_auth,
+				 const u8 *src_addr,
+				 const u8 *enc, size_t enc_len,
+				 const u8 *auth, size_t auth_len,
+				 int no_defer)
+{
+	int ret = -1;
+	struct ft_rrb_seq f_seq;
+	const u8 *f_nonce, *f_r0kh_id, *f_r1kh_id;
+	size_t f_nonce_len, f_r0kh_id_len, f_r1kh_id_len;
+	struct ft_remote_seq *rkh_seq = NULL;
+	u8 *packet = NULL, *key = NULL;
+	size_t packet_len = 0, key_len = 0;
+	struct tlv_list seq_resp_auth[5];
+
+	wpa_printf(MSG_DEBUG, "FT: Received sequence number request");
+
+	if (wpa_ft_rrb_rx_seq(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_REQ,
+			      enc, enc_len, auth, auth_len, &rkh_seq, &key,
+			      &key_len, NULL, NULL, NULL, NULL) < 0)
+		goto out;
+
+	RRB_GET_AUTH(FT_RRB_NONCE, nonce, "seq request", FT_RRB_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: seq request - nonce", f_nonce, f_nonce_len);
+
+	RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, "seq", -1);
+	RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, "seq", FT_R1KH_ID_LEN);
+
+	if (wpa_ft_new_seq(rkh_seq, &f_seq) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to get seq num");
+		goto out;
+	}
+
+	wpa_printf(MSG_DEBUG, "FT: Send sequence number response from " MACSTR
+		   " to " MACSTR,
+		   MAC2STR(wpa_auth->addr), MAC2STR(src_addr));
+
+	seq_resp_auth[0].type = FT_RRB_NONCE;
+	seq_resp_auth[0].len = f_nonce_len;
+	seq_resp_auth[0].data = f_nonce;
+	seq_resp_auth[1].type = FT_RRB_SEQ;
+	seq_resp_auth[1].len = sizeof(f_seq);
+	seq_resp_auth[1].data = (u8 *) &f_seq;
+	seq_resp_auth[2].type = FT_RRB_R0KH_ID;
+	seq_resp_auth[2].len = f_r0kh_id_len;
+	seq_resp_auth[2].data = f_r0kh_id;
+	seq_resp_auth[3].type = FT_RRB_R1KH_ID;
+	seq_resp_auth[3].len = FT_R1KH_ID_LEN;
+	seq_resp_auth[3].data = f_r1kh_id;
+	seq_resp_auth[4].type = FT_RRB_LAST_EMPTY;
+	seq_resp_auth[4].len = 0;
+	seq_resp_auth[4].data = NULL;
+
+	if (wpa_ft_rrb_build(key, key_len, NULL, NULL, seq_resp_auth, NULL,
+			     wpa_auth->addr, FT_PACKET_R0KH_R1KH_SEQ_RESP,
+			     &packet, &packet_len) < 0)
+		goto out;
+
+	wpa_ft_rrb_oui_send(wpa_auth, src_addr,
+			    FT_PACKET_R0KH_R1KH_SEQ_RESP, packet,
+			    packet_len);
+
+out:
+	os_free(packet);
+
+	return ret;
+}
+
+
+static int wpa_ft_rrb_rx_seq_resp(struct wpa_authenticator *wpa_auth,
+				  const u8 *src_addr,
+				  const u8 *enc, size_t enc_len,
+				  const u8 *auth, size_t auth_len,
+				  int no_defer)
+{
+	u8 *key = NULL;
+	size_t key_len = 0;
+	struct ft_remote_r0kh *r0kh = NULL, *r0kh_wildcard = NULL;
+	struct ft_remote_r1kh *r1kh = NULL, *r1kh_wildcard = NULL;
+	const u8 *f_nonce, *f_seq;
+	size_t f_nonce_len, f_seq_len;
+	struct ft_remote_seq *rkh_seq = NULL;
+	struct ft_remote_item *item;
+	struct os_reltime now, now_remote;
+	int seq_ret, found;
+	const struct ft_rrb_seq *msg_both;
+	u32 msg_dom, msg_seq;
+
+	wpa_printf(MSG_DEBUG, "FT: Received sequence number response");
+
+	if (wpa_ft_rrb_rx_seq(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_RESP,
+			      enc, enc_len, auth, auth_len, &rkh_seq, &key,
+			      &key_len, &r0kh, &r1kh, &r0kh_wildcard,
+			      &r1kh_wildcard) < 0)
+		goto out;
+
+	RRB_GET_AUTH(FT_RRB_NONCE, nonce, "seq response", FT_RRB_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "FT: seq response - nonce", f_nonce,
+		    f_nonce_len);
+
+	found = 0;
+	dl_list_for_each(item, &rkh_seq->rx.queue, struct ft_remote_item,
+			 list) {
+		if (os_memcmp_const(f_nonce, item->nonce,
+				    FT_RRB_NONCE_LEN) != 0 ||
+		    os_get_reltime(&now) < 0 ||
+		    os_reltime_expired(&now, &item->nonce_ts, ftRRBseqTimeout))
+			continue;
+
+		found = 1;
+		break;
+	}
+	if (!found) {
+		wpa_printf(MSG_DEBUG, "FT: seq response - bad nonce");
+		goto out;
+	}
+
+	if (r0kh) {
+		wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh,
+					  wpa_auth->conf.rkh_pos_timeout);
+		if (r0kh_wildcard)
+			os_memcpy(r0kh->addr, src_addr, ETH_ALEN);
+	}
+
+	if (r1kh) {
+		wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh,
+					  wpa_auth->conf.rkh_pos_timeout);
+		if (r1kh_wildcard)
+			os_memcpy(r1kh->addr, src_addr, ETH_ALEN);
+	}
+
+	seq_ret = wpa_ft_rrb_seq_chk(rkh_seq, src_addr, enc, enc_len, auth,
+				     auth_len, "seq response", 1);
+	if (seq_ret == FT_RRB_SEQ_OK) {
+		wpa_printf(MSG_DEBUG, "FT: seq response - valid seq number");
+		wpa_ft_rrb_seq_accept(wpa_auth, rkh_seq, src_addr, auth,
+				      auth_len, "seq response");
+	} else {
+		wpa_printf(MSG_DEBUG, "FT: seq response - reset seq number");
+
+		RRB_GET_AUTH(FT_RRB_SEQ, seq, "seq response",
+			     sizeof(*msg_both));
+		msg_both = (const struct ft_rrb_seq *) f_seq;
+
+		msg_dom = le_to_host32(msg_both->dom);
+		msg_seq = le_to_host32(msg_both->seq);
+		now_remote.sec = le_to_host32(msg_both->ts);
+		now_remote.usec = 0;
+
+		rkh_seq->rx.num_last = 2;
+		rkh_seq->rx.dom = msg_dom;
+		rkh_seq->rx.offsetidx = 0;
+		/* Accept some older, possibly cached packets as well */
+		rkh_seq->rx.last[0] = msg_seq - FT_REMOTE_SEQ_BACKLOG -
+			dl_list_len(&rkh_seq->rx.queue);
+		rkh_seq->rx.last[1] = msg_seq;
+
+		/* local time - offset = remote time
+		 * <=> local time - remote time = offset */
+		os_reltime_sub(&now, &now_remote, &rkh_seq->rx.time_offset);
+	}
+
+	wpa_ft_rrb_seq_flush(wpa_auth, rkh_seq, 1);
+
+	return 0;
+out:
+	return -1;
+}
+
+
+int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
+		  const u8 *data, size_t data_len)
+{
+	struct ft_rrb_frame *frame;
+	u16 alen;
+	const u8 *pos, *end, *start;
+	u8 action;
+	const u8 *sta_addr, *target_ap_addr;
+
+	wpa_printf(MSG_DEBUG, "FT: RRB received frame from remote AP " MACSTR,
+		   MAC2STR(src_addr));
+
+	if (data_len < sizeof(*frame)) {
+		wpa_printf(MSG_DEBUG, "FT: Too short RRB frame (data_len=%lu)",
+			   (unsigned long) data_len);
+		return -1;
+	}
+
+	pos = data;
+	frame = (struct ft_rrb_frame *) pos;
+	pos += sizeof(*frame);
+
+	alen = le_to_host16(frame->action_length);
+	wpa_printf(MSG_DEBUG, "FT: RRB frame - frame_type=%d packet_type=%d "
+		   "action_length=%d ap_address=" MACSTR,
+		   frame->frame_type, frame->packet_type, alen,
+		   MAC2STR(frame->ap_address));
+
+	if (frame->frame_type != RSN_REMOTE_FRAME_TYPE_FT_RRB) {
+		/* Discard frame per IEEE Std 802.11r-2008, 11A.10.3 */
+		wpa_printf(MSG_DEBUG, "FT: RRB discarded frame with "
+			   "unrecognized type %d", frame->frame_type);
+		return -1;
+	}
+
+	if (alen > data_len - sizeof(*frame)) {
+		wpa_printf(MSG_DEBUG, "FT: RRB frame too short for action "
+			   "frame");
+		return -1;
+	}
+
+	wpa_hexdump(MSG_MSGDUMP, "FT: RRB - FT Action frame", pos, alen);
+
+	if (alen < 1 + 1 + 2 * ETH_ALEN) {
+		wpa_printf(MSG_DEBUG, "FT: Too short RRB frame (not enough "
+			   "room for Action Frame body); alen=%lu",
+			   (unsigned long) alen);
+		return -1;
+	}
+	start = pos;
+	end = pos + alen;
+
+	if (*pos != WLAN_ACTION_FT) {
+		wpa_printf(MSG_DEBUG, "FT: Unexpected Action frame category "
+			   "%d", *pos);
+		return -1;
+	}
+
+	pos++;
+	action = *pos++;
+	sta_addr = pos;
+	pos += ETH_ALEN;
+	target_ap_addr = pos;
+	pos += ETH_ALEN;
+	wpa_printf(MSG_DEBUG, "FT: RRB Action Frame: action=%d sta_addr="
+		   MACSTR " target_ap_addr=" MACSTR,
+		   action, MAC2STR(sta_addr), MAC2STR(target_ap_addr));
+
+	if (frame->packet_type == FT_PACKET_REQUEST) {
+		wpa_printf(MSG_DEBUG, "FT: FT Packet Type - Request");
+
+		if (action != 1) {
+			wpa_printf(MSG_DEBUG, "FT: Unexpected Action %d in "
+				   "RRB Request", action);
+			return -1;
+		}
+
+		if (os_memcmp(target_ap_addr, wpa_auth->addr, ETH_ALEN) != 0) {
+			wpa_printf(MSG_DEBUG, "FT: Target AP address in the "
+				   "RRB Request does not match with own "
+				   "address");
+			return -1;
+		}
+
+		if (wpa_ft_rrb_rx_request(wpa_auth, frame->ap_address,
+					  sta_addr, pos, end - pos) < 0)
+			return -1;
+	} else if (frame->packet_type == FT_PACKET_RESPONSE) {
+		u16 status_code;
+
+		if (end - pos < 2) {
+			wpa_printf(MSG_DEBUG, "FT: Not enough room for status "
+				   "code in RRB Response");
+			return -1;
+		}
+		status_code = WPA_GET_LE16(pos);
+
+		wpa_printf(MSG_DEBUG, "FT: FT Packet Type - Response "
+			   "(status_code=%d)", status_code);
+
+		if (wpa_ft_action_send(wpa_auth, sta_addr, start, alen) < 0)
+			return -1;
+	} else {
+		wpa_printf(MSG_DEBUG, "FT: RRB discarded frame with unknown "
+			   "packet_type %d", frame->packet_type);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
+		       const u8 *dst_addr, u8 oui_suffix, const u8 *data,
+		       size_t data_len)
+{
+	const u8 *auth, *enc;
+	size_t alen, elen;
+	int no_defer = 0;
+
+	wpa_printf(MSG_DEBUG, "FT: RRB-OUI(" MACSTR
+		   ") received frame from remote AP "
+		   MACSTR " oui_suffix=%u dst=" MACSTR,
+		   MAC2STR(wpa_auth->addr), MAC2STR(src_addr), oui_suffix,
+		   MAC2STR(dst_addr));
+	wpa_hexdump(MSG_MSGDUMP, "FT: RRB frame payload", data, data_len);
+
+	if (is_multicast_ether_addr(src_addr)) {
+		wpa_printf(MSG_DEBUG,
+			   "FT: RRB-OUI received frame from multicast address "
+			   MACSTR, MAC2STR(src_addr));
+		return;
+	}
+
+	if (is_multicast_ether_addr(dst_addr))
+		no_defer = 1;
+
+	if (data_len < sizeof(u16)) {
+		wpa_printf(MSG_DEBUG, "FT: RRB-OUI frame too short");
+		return;
+	}
+
+	alen = WPA_GET_LE16(data);
+	if (data_len < sizeof(u16) + alen) {
+		wpa_printf(MSG_DEBUG, "FT: RRB-OUI frame too short");
+		return;
+	}
+
+	auth = data + sizeof(u16);
+	wpa_hexdump(MSG_MSGDUMP, "FT: Authenticated payload", auth, alen);
+	enc = data + sizeof(u16) + alen;
+	elen = data_len - sizeof(u16) - alen;
+	wpa_hexdump(MSG_MSGDUMP, "FT: Encrypted payload", enc, elen);
+
+	switch (oui_suffix) {
+	case FT_PACKET_R0KH_R1KH_PULL:
+		wpa_ft_rrb_rx_pull(wpa_auth, src_addr, enc, elen, auth, alen,
+				   no_defer);
+		break;
+	case FT_PACKET_R0KH_R1KH_RESP:
+		wpa_ft_rrb_rx_resp(wpa_auth, src_addr, enc, elen, auth, alen,
+				   no_defer);
+		break;
+	case FT_PACKET_R0KH_R1KH_PUSH:
+		wpa_ft_rrb_rx_push(wpa_auth, src_addr, enc, elen, auth, alen,
+				   no_defer);
+		break;
+	case FT_PACKET_R0KH_R1KH_SEQ_REQ:
+		wpa_ft_rrb_rx_seq_req(wpa_auth, src_addr, enc, elen, auth, alen,
+				      no_defer);
+		break;
+	case FT_PACKET_R0KH_R1KH_SEQ_RESP:
+		wpa_ft_rrb_rx_seq_resp(wpa_auth, src_addr, enc, elen, auth,
+				       alen, no_defer);
+		break;
+	}
+}
+
+
+static int wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth,
+				  struct wpa_ft_pmk_r0_sa *pmk_r0,
+				  struct ft_remote_r1kh *r1kh,
+				  const u8 *s1kh_id)
+{
+	u8 *packet;
+	size_t packet_len;
+	struct ft_rrb_seq f_seq;
+	struct tlv_list push[] = {
+		{ .type = FT_RRB_S1KH_ID, .len = ETH_ALEN,
+		  .data = s1kh_id },
+		{ .type = FT_RRB_PMK_R0_NAME, .len = WPA_PMK_NAME_LEN,
+		  .data = pmk_r0->pmk_r0_name },
+		{ .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+	};
+	struct tlv_list push_auth[] = {
+		{ .type = FT_RRB_SEQ, .len = sizeof(f_seq),
+		  .data = (u8 *) &f_seq },
+		{ .type = FT_RRB_R0KH_ID,
+		  .len = wpa_auth->conf.r0_key_holder_len,
+		  .data = wpa_auth->conf.r0_key_holder },
+		{ .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN,
+		  .data = r1kh->id },
+		{ .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+	};
+
+	if (wpa_ft_new_seq(r1kh->seq, &f_seq) < 0) {
+		wpa_printf(MSG_DEBUG, "FT: Failed to get seq num");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "FT: Send PMK-R1 push from " MACSTR
+		   " to remote R0KH address " MACSTR,
+		   MAC2STR(wpa_auth->addr), MAC2STR(r1kh->addr));
+
+	if (wpa_ft_rrb_build_r0(r1kh->key, sizeof(r1kh->key), push, pmk_r0,
+				r1kh->id, s1kh_id, push_auth, wpa_auth->addr,
+				FT_PACKET_R0KH_R1KH_PUSH,
+				&packet, &packet_len) < 0)
+		return -1;
+
+	wpa_ft_rrb_oui_send(wpa_auth, r1kh->addr, FT_PACKET_R0KH_R1KH_PUSH,
+			    packet, packet_len);
+
+	os_free(packet);
+	return 0;
+}
+
+
+void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr)
+{
+	struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
+	struct wpa_ft_pmk_r0_sa *r0, *r0found = NULL;
+	struct ft_remote_r1kh *r1kh;
+
+	if (!wpa_auth->conf.pmk_r1_push)
+		return;
+	if (!wpa_auth->conf.r1kh_list)
+		return;
+
+	dl_list_for_each(r0, &cache->pmk_r0, struct wpa_ft_pmk_r0_sa, list) {
+		if (os_memcmp(r0->spa, addr, ETH_ALEN) == 0) {
+			r0found = r0;
+			break;
+		}
+	}
+
+	r0 = r0found;
+	if (r0 == NULL || r0->pmk_r1_pushed)
+		return;
+	r0->pmk_r1_pushed = 1;
+
+	wpa_printf(MSG_DEBUG, "FT: Deriving and pushing PMK-R1 keys to R1KHs "
+		   "for STA " MACSTR, MAC2STR(addr));
+
+	for (r1kh = *wpa_auth->conf.r1kh_list; r1kh; r1kh = r1kh->next) {
+		if (is_zero_ether_addr(r1kh->addr) ||
+		    is_zero_ether_addr(r1kh->id))
+			continue;
+		if (wpa_ft_rrb_init_r1kh_seq(r1kh) < 0)
+			continue;
+		wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr);
+	}
+}
+
+#endif /* CONFIG_IEEE80211R_AP */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_glue.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_glue.c
new file mode 100644
index 0000000..17186bf
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_glue.c
@@ -0,0 +1,1806 @@
+/*
+ * hostapd / WPA authenticator glue code
+ * Copyright (c) 2002-2022, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/list.h"
+#include "common/ieee802_11_defs.h"
+#include "common/sae.h"
+#include "common/wpa_ctrl.h"
+#include "common/ptksa_cache.h"
+#include "crypto/sha1.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
+#include "eap_server/eap.h"
+#include "l2_packet/l2_packet.h"
+#include "eth_p_oui.h"
+#include "hostapd.h"
+#include "ieee802_1x.h"
+#include "preauth_auth.h"
+#include "sta_info.h"
+#include "tkip_countermeasures.h"
+#include "ap_drv_ops.h"
+#include "ap_config.h"
+#include "ieee802_11.h"
+#include "ieee802_11_auth.h"
+#include "pmksa_cache_auth.h"
+#include "wpa_auth.h"
+#include "wpa_auth_glue.h"
+
+
+static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
+				  struct hostapd_config *iconf,
+				  struct wpa_auth_config *wconf)
+{
+	int sae_pw_id;
+
+	os_memset(wconf, 0, sizeof(*wconf));
+	wconf->wpa = conf->wpa;
+	wconf->extended_key_id = conf->extended_key_id;
+	wconf->wpa_key_mgmt = conf->wpa_key_mgmt;
+	wconf->wpa_pairwise = conf->wpa_pairwise;
+	wconf->wpa_group = conf->wpa_group;
+	wconf->wpa_group_rekey = conf->wpa_group_rekey;
+	wconf->wpa_strict_rekey = conf->wpa_strict_rekey;
+	wconf->wpa_gmk_rekey = conf->wpa_gmk_rekey;
+	wconf->wpa_ptk_rekey = conf->wpa_ptk_rekey;
+	wconf->wpa_group_update_count = conf->wpa_group_update_count;
+	wconf->wpa_disable_eapol_key_retries =
+		conf->wpa_disable_eapol_key_retries;
+	wconf->wpa_pairwise_update_count = conf->wpa_pairwise_update_count;
+	wconf->rsn_pairwise = conf->rsn_pairwise;
+	wconf->rsn_preauth = conf->rsn_preauth;
+	wconf->eapol_version = conf->eapol_version;
+#ifdef CONFIG_MACSEC
+	if (wconf->eapol_version > 2)
+		wconf->eapol_version = 2;
+#endif /* CONFIG_MACSEC */
+	wconf->wmm_enabled = conf->wmm_enabled;
+	wconf->wmm_uapsd = conf->wmm_uapsd;
+	wconf->disable_pmksa_caching = conf->disable_pmksa_caching;
+#ifdef CONFIG_OCV
+	wconf->ocv = conf->ocv;
+#endif /* CONFIG_OCV */
+	wconf->okc = conf->okc;
+	wconf->ieee80211w = conf->ieee80211w;
+	wconf->beacon_prot = conf->beacon_prot;
+	wconf->group_mgmt_cipher = conf->group_mgmt_cipher;
+	wconf->sae_require_mfp = conf->sae_require_mfp;
+#ifdef CONFIG_IEEE80211R_AP
+	wconf->ssid_len = conf->ssid.ssid_len;
+	if (wconf->ssid_len > SSID_MAX_LEN)
+		wconf->ssid_len = SSID_MAX_LEN;
+	os_memcpy(wconf->ssid, conf->ssid.ssid, wconf->ssid_len);
+	os_memcpy(wconf->mobility_domain, conf->mobility_domain,
+		  MOBILITY_DOMAIN_ID_LEN);
+	if (conf->nas_identifier &&
+	    os_strlen(conf->nas_identifier) <= FT_R0KH_ID_MAX_LEN) {
+		wconf->r0_key_holder_len = os_strlen(conf->nas_identifier);
+		os_memcpy(wconf->r0_key_holder, conf->nas_identifier,
+			  wconf->r0_key_holder_len);
+	}
+	os_memcpy(wconf->r1_key_holder, conf->r1_key_holder, FT_R1KH_ID_LEN);
+	wconf->r0_key_lifetime = conf->r0_key_lifetime;
+	wconf->r1_max_key_lifetime = conf->r1_max_key_lifetime;
+	wconf->reassociation_deadline = conf->reassociation_deadline;
+	wconf->rkh_pos_timeout = conf->rkh_pos_timeout;
+	wconf->rkh_neg_timeout = conf->rkh_neg_timeout;
+	wconf->rkh_pull_timeout = conf->rkh_pull_timeout;
+	wconf->rkh_pull_retries = conf->rkh_pull_retries;
+	wconf->r0kh_list = &conf->r0kh_list;
+	wconf->r1kh_list = &conf->r1kh_list;
+	wconf->pmk_r1_push = conf->pmk_r1_push;
+	wconf->ft_over_ds = conf->ft_over_ds;
+	wconf->ft_psk_generate_local = conf->ft_psk_generate_local;
+#endif /* CONFIG_IEEE80211R_AP */
+#ifdef CONFIG_HS20
+	wconf->disable_gtk = conf->disable_dgaf;
+	if (conf->osen) {
+		wconf->disable_gtk = 1;
+		wconf->wpa = WPA_PROTO_OSEN;
+		wconf->wpa_key_mgmt = WPA_KEY_MGMT_OSEN;
+		wconf->wpa_pairwise = 0;
+		wconf->wpa_group = WPA_CIPHER_CCMP;
+		wconf->rsn_pairwise = WPA_CIPHER_CCMP;
+		wconf->rsn_preauth = 0;
+		wconf->disable_pmksa_caching = 1;
+		wconf->ieee80211w = 1;
+	}
+#endif /* CONFIG_HS20 */
+#ifdef CONFIG_TESTING_OPTIONS
+	wconf->corrupt_gtk_rekey_mic_probability =
+		iconf->corrupt_gtk_rekey_mic_probability;
+	wconf->delay_eapol_tx = iconf->delay_eapol_tx;
+	if (conf->own_ie_override &&
+	    wpabuf_len(conf->own_ie_override) <= MAX_OWN_IE_OVERRIDE) {
+		wconf->own_ie_override_len = wpabuf_len(conf->own_ie_override);
+		os_memcpy(wconf->own_ie_override,
+			  wpabuf_head(conf->own_ie_override),
+			  wconf->own_ie_override_len);
+	}
+	if (conf->rsne_override_eapol &&
+	    wpabuf_len(conf->rsne_override_eapol) <= MAX_OWN_IE_OVERRIDE) {
+		wconf->rsne_override_eapol_set = 1;
+		wconf->rsne_override_eapol_len =
+			wpabuf_len(conf->rsne_override_eapol);
+		os_memcpy(wconf->rsne_override_eapol,
+			  wpabuf_head(conf->rsne_override_eapol),
+			  wconf->rsne_override_eapol_len);
+	}
+	if (conf->rsnxe_override_eapol &&
+	    wpabuf_len(conf->rsnxe_override_eapol) <= MAX_OWN_IE_OVERRIDE) {
+		wconf->rsnxe_override_eapol_set = 1;
+		wconf->rsnxe_override_eapol_len =
+			wpabuf_len(conf->rsnxe_override_eapol);
+		os_memcpy(wconf->rsnxe_override_eapol,
+			  wpabuf_head(conf->rsnxe_override_eapol),
+			  wconf->rsnxe_override_eapol_len);
+	}
+	if (conf->rsne_override_ft &&
+	    wpabuf_len(conf->rsne_override_ft) <= MAX_OWN_IE_OVERRIDE) {
+		wconf->rsne_override_ft_set = 1;
+		wconf->rsne_override_ft_len =
+			wpabuf_len(conf->rsne_override_ft);
+		os_memcpy(wconf->rsne_override_ft,
+			  wpabuf_head(conf->rsne_override_ft),
+			  wconf->rsne_override_ft_len);
+	}
+	if (conf->rsnxe_override_ft &&
+	    wpabuf_len(conf->rsnxe_override_ft) <= MAX_OWN_IE_OVERRIDE) {
+		wconf->rsnxe_override_ft_set = 1;
+		wconf->rsnxe_override_ft_len =
+			wpabuf_len(conf->rsnxe_override_ft);
+		os_memcpy(wconf->rsnxe_override_ft,
+			  wpabuf_head(conf->rsnxe_override_ft),
+			  wconf->rsnxe_override_ft_len);
+	}
+	if (conf->gtk_rsc_override &&
+	    wpabuf_len(conf->gtk_rsc_override) > 0 &&
+	    wpabuf_len(conf->gtk_rsc_override) <= WPA_KEY_RSC_LEN) {
+		os_memcpy(wconf->gtk_rsc_override,
+			  wpabuf_head(conf->gtk_rsc_override),
+			  wpabuf_len(conf->gtk_rsc_override));
+		wconf->gtk_rsc_override_set = 1;
+	}
+	if (conf->igtk_rsc_override &&
+	    wpabuf_len(conf->igtk_rsc_override) > 0 &&
+	    wpabuf_len(conf->igtk_rsc_override) <= WPA_KEY_RSC_LEN) {
+		os_memcpy(wconf->igtk_rsc_override,
+			  wpabuf_head(conf->igtk_rsc_override),
+			  wpabuf_len(conf->igtk_rsc_override));
+		wconf->igtk_rsc_override_set = 1;
+	}
+	wconf->ft_rsnxe_used = conf->ft_rsnxe_used;
+	wconf->oci_freq_override_eapol_m3 = conf->oci_freq_override_eapol_m3;
+	wconf->oci_freq_override_eapol_g1 = conf->oci_freq_override_eapol_g1;
+	wconf->oci_freq_override_ft_assoc = conf->oci_freq_override_ft_assoc;
+	wconf->oci_freq_override_fils_assoc =
+		conf->oci_freq_override_fils_assoc;
+#endif /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_P2P
+	os_memcpy(wconf->ip_addr_go, conf->ip_addr_go, 4);
+	os_memcpy(wconf->ip_addr_mask, conf->ip_addr_mask, 4);
+	os_memcpy(wconf->ip_addr_start, conf->ip_addr_start, 4);
+	os_memcpy(wconf->ip_addr_end, conf->ip_addr_end, 4);
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_FILS
+	wconf->fils_cache_id_set = conf->fils_cache_id_set;
+	os_memcpy(wconf->fils_cache_id, conf->fils_cache_id,
+		  FILS_CACHE_ID_LEN);
+#endif /* CONFIG_FILS */
+	wconf->sae_pwe = conf->sae_pwe;
+	sae_pw_id = hostapd_sae_pw_id_in_use(conf);
+	if (sae_pw_id == 2 && wconf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
+		wconf->sae_pwe = SAE_PWE_HASH_TO_ELEMENT;
+	else if (sae_pw_id == 1 && wconf->sae_pwe == SAE_PWE_HUNT_AND_PECK)
+		wconf->sae_pwe = SAE_PWE_BOTH;
+#ifdef CONFIG_SAE_PK
+	wconf->sae_pk = hostapd_sae_pk_in_use(conf);
+#endif /* CONFIG_SAE_PK */
+#ifdef CONFIG_OWE
+	wconf->owe_ptk_workaround = conf->owe_ptk_workaround;
+#endif /* CONFIG_OWE */
+	wconf->transition_disable = conf->transition_disable;
+#ifdef CONFIG_DPP2
+	wconf->dpp_pfs = conf->dpp_pfs;
+#endif /* CONFIG_DPP2 */
+#ifdef CONFIG_PASN
+#ifdef CONFIG_TESTING_OPTIONS
+	wconf->force_kdk_derivation = conf->force_kdk_derivation;
+#endif /* CONFIG_TESTING_OPTIONS */
+#endif /* CONFIG_PASN */
+
+	wconf->radius_psk = conf->wpa_psk_radius == PSK_RADIUS_DURING_4WAY_HS;
+}
+
+
+static void hostapd_wpa_auth_logger(void *ctx, const u8 *addr,
+				    logger_level level, const char *txt)
+{
+#ifndef CONFIG_NO_HOSTAPD_LOGGER
+	struct hostapd_data *hapd = ctx;
+	int hlevel;
+
+	switch (level) {
+	case LOGGER_WARNING:
+		hlevel = HOSTAPD_LEVEL_WARNING;
+		break;
+	case LOGGER_INFO:
+		hlevel = HOSTAPD_LEVEL_INFO;
+		break;
+	case LOGGER_DEBUG:
+	default:
+		hlevel = HOSTAPD_LEVEL_DEBUG;
+		break;
+	}
+
+	hostapd_logger(hapd, addr, HOSTAPD_MODULE_WPA, hlevel, "%s", txt);
+#endif /* CONFIG_NO_HOSTAPD_LOGGER */
+}
+
+
+static void hostapd_wpa_auth_disconnect(void *ctx, const u8 *addr,
+					u16 reason)
+{
+	struct hostapd_data *hapd = ctx;
+	wpa_printf(MSG_DEBUG, "%s: WPA authenticator requests disconnect: "
+		   "STA " MACSTR " reason %d",
+		   __func__, MAC2STR(addr), reason);
+	ap_sta_disconnect(hapd, NULL, addr, reason);
+}
+
+
+static int hostapd_wpa_auth_mic_failure_report(void *ctx, const u8 *addr)
+{
+	struct hostapd_data *hapd = ctx;
+	return michael_mic_failure(hapd, addr, 0);
+}
+
+
+static void hostapd_wpa_auth_psk_failure_report(void *ctx, const u8 *addr)
+{
+	struct hostapd_data *hapd = ctx;
+	wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_POSSIBLE_PSK_MISMATCH MACSTR,
+		MAC2STR(addr));
+	hostapd_ubus_notify(hapd, "key-mismatch", addr);
+}
+
+
+static void hostapd_wpa_auth_set_eapol(void *ctx, const u8 *addr,
+				       wpa_eapol_variable var, int value)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta = ap_get_sta(hapd, addr);
+	if (sta == NULL)
+		return;
+	switch (var) {
+	case WPA_EAPOL_portEnabled:
+		ieee802_1x_notify_port_enabled(sta->eapol_sm, value);
+		break;
+	case WPA_EAPOL_portValid:
+		ieee802_1x_notify_port_valid(sta->eapol_sm, value);
+		break;
+	case WPA_EAPOL_authorized:
+		ieee802_1x_set_sta_authorized(hapd, sta, value);
+		break;
+	case WPA_EAPOL_portControl_Auto:
+		if (sta->eapol_sm)
+			sta->eapol_sm->portControl = Auto;
+		break;
+	case WPA_EAPOL_keyRun:
+		if (sta->eapol_sm)
+			sta->eapol_sm->keyRun = value;
+		break;
+	case WPA_EAPOL_keyAvailable:
+		if (sta->eapol_sm)
+			sta->eapol_sm->eap_if->eapKeyAvailable = value;
+		break;
+	case WPA_EAPOL_keyDone:
+		if (sta->eapol_sm)
+			sta->eapol_sm->keyDone = value;
+		break;
+	case WPA_EAPOL_inc_EapolFramesTx:
+		if (sta->eapol_sm)
+			sta->eapol_sm->dot1xAuthEapolFramesTx++;
+		break;
+	}
+}
+
+
+static int hostapd_wpa_auth_get_eapol(void *ctx, const u8 *addr,
+				      wpa_eapol_variable var)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta = ap_get_sta(hapd, addr);
+	if (sta == NULL || sta->eapol_sm == NULL)
+		return -1;
+	switch (var) {
+	case WPA_EAPOL_keyRun:
+		return sta->eapol_sm->keyRun;
+	case WPA_EAPOL_keyAvailable:
+		return sta->eapol_sm->eap_if->eapKeyAvailable;
+	default:
+		return -1;
+	}
+}
+
+
+static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr,
+					   const u8 *p2p_dev_addr,
+					   const u8 *prev_psk, size_t *psk_len,
+					   int *vlan_id)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta = ap_get_sta(hapd, addr);
+	const u8 *psk;
+
+	if (vlan_id)
+		*vlan_id = 0;
+	if (psk_len)
+		*psk_len = PMK_LEN;
+
+#ifdef CONFIG_SAE
+	if (sta && sta->auth_alg == WLAN_AUTH_SAE) {
+		if (!sta->sae || prev_psk)
+			return NULL;
+		if (psk_len)
+			*psk_len = sta->sae->pmk_len;
+		return sta->sae->pmk;
+	}
+	if (sta && wpa_auth_uses_sae(sta->wpa_sm)) {
+		wpa_printf(MSG_DEBUG,
+			   "No PSK for STA trying to use SAE with PMKSA caching");
+		return NULL;
+	}
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_OWE
+	if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
+	    sta && sta->owe_pmk) {
+		if (psk_len)
+			*psk_len = sta->owe_pmk_len;
+		return sta->owe_pmk;
+	}
+	if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && sta) {
+		struct rsn_pmksa_cache_entry *sa;
+
+		sa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
+		if (sa && sa->akmp == WPA_KEY_MGMT_OWE) {
+			if (psk_len)
+				*psk_len = sa->pmk_len;
+			return sa->pmk;
+		}
+	}
+#endif /* CONFIG_OWE */
+
+	psk = hostapd_get_psk(hapd->conf, addr, p2p_dev_addr, prev_psk,
+			      vlan_id);
+	/*
+	 * This is about to iterate over all psks, prev_psk gives the last
+	 * returned psk which should not be returned again.
+	 * logic list (all hostapd_get_psk; all sta->psk)
+	 */
+	if (sta && sta->psk && !psk) {
+		struct hostapd_sta_wpa_psk_short *pos;
+
+		if (vlan_id)
+			*vlan_id = 0;
+		psk = sta->psk->psk;
+		for (pos = sta->psk; pos; pos = pos->next) {
+			if (pos->is_passphrase) {
+				if (pbkdf2_sha1(pos->passphrase,
+						hapd->conf->ssid.ssid,
+						hapd->conf->ssid.ssid_len, 4096,
+						pos->psk, PMK_LEN) != 0) {
+					wpa_printf(MSG_WARNING,
+						   "Error in pbkdf2_sha1()");
+					continue;
+				}
+				pos->is_passphrase = 0;
+			}
+			if (pos->psk == prev_psk) {
+				psk = pos->next ? pos->next->psk : NULL;
+				break;
+			}
+		}
+	}
+	return psk;
+}
+
+
+static int hostapd_wpa_auth_get_msk(void *ctx, const u8 *addr, u8 *msk,
+				    size_t *len)
+{
+	struct hostapd_data *hapd = ctx;
+	const u8 *key;
+	size_t keylen;
+	struct sta_info *sta;
+
+	sta = ap_get_sta(hapd, addr);
+	if (sta == NULL) {
+		wpa_printf(MSG_DEBUG, "AUTH_GET_MSK: Cannot find STA");
+		return -1;
+	}
+
+	key = ieee802_1x_get_key(sta->eapol_sm, &keylen);
+	if (key == NULL) {
+		wpa_printf(MSG_DEBUG, "AUTH_GET_MSK: Key is null, eapol_sm: %p",
+			   sta->eapol_sm);
+		return -1;
+	}
+
+	if (keylen > *len)
+		keylen = *len;
+	os_memcpy(msk, key, keylen);
+	*len = keylen;
+
+	return 0;
+}
+
+
+static int hostapd_wpa_auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg,
+				    const u8 *addr, int idx, u8 *key,
+				    size_t key_len, enum key_flag key_flag)
+{
+	struct hostapd_data *hapd = ctx;
+	const char *ifname = hapd->conf->iface;
+
+	if (vlan_id > 0) {
+		ifname = hostapd_get_vlan_id_ifname(hapd->conf->vlan, vlan_id);
+		if (!ifname) {
+			if (!(hapd->iface->drv_flags &
+			      WPA_DRIVER_FLAGS_VLAN_OFFLOAD))
+				return -1;
+			ifname = hapd->conf->iface;
+		}
+	}
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (key_flag & KEY_FLAG_MODIFY) {
+		/* We are updating an already installed key. Don't overwrite
+		 * the already stored key information with zeros.
+		 */
+	} else if (addr && !is_broadcast_ether_addr(addr)) {
+		struct sta_info *sta;
+
+		sta = ap_get_sta(hapd, addr);
+		if (sta) {
+			sta->last_tk_alg = alg;
+			sta->last_tk_key_idx = idx;
+			if (key)
+				os_memcpy(sta->last_tk, key, key_len);
+			sta->last_tk_len = key_len;
+		}
+	} else if (alg == WPA_ALG_BIP_CMAC_128 ||
+		   alg == WPA_ALG_BIP_GMAC_128 ||
+		   alg == WPA_ALG_BIP_GMAC_256 ||
+		   alg == WPA_ALG_BIP_CMAC_256) {
+		if (idx == 4 || idx == 5) {
+			hapd->last_igtk_alg = alg;
+			hapd->last_igtk_key_idx = idx;
+			if (key)
+				os_memcpy(hapd->last_igtk, key, key_len);
+			hapd->last_igtk_len = key_len;
+		} else if (idx == 6 || idx == 7) {
+			hapd->last_bigtk_alg = alg;
+			hapd->last_bigtk_key_idx = idx;
+			if (key)
+				os_memcpy(hapd->last_bigtk, key, key_len);
+			hapd->last_bigtk_len = key_len;
+		}
+	} else {
+		hapd->last_gtk_alg = alg;
+		hapd->last_gtk_key_idx = idx;
+		if (key)
+			os_memcpy(hapd->last_gtk, key, key_len);
+		hapd->last_gtk_len = key_len;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+	return hostapd_drv_set_key(ifname, hapd, alg, addr, idx, vlan_id, 1,
+				   NULL, 0, key, key_len, key_flag);
+}
+
+
+static int hostapd_wpa_auth_get_seqnum(void *ctx, const u8 *addr, int idx,
+				       u8 *seq)
+{
+	struct hostapd_data *hapd = ctx;
+	int link_id = -1;
+
+#ifdef CONFIG_IEEE80211BE
+	if (hapd->conf->mld_ap && idx)
+		link_id = hapd->mld_link_id;
+#endif /* CONFIG_IEEE80211BE */
+	return hostapd_get_seqnum(hapd->conf->iface, hapd, addr, idx, link_id,
+				  seq);
+}
+
+
+int hostapd_wpa_auth_send_eapol(void *ctx, const u8 *addr,
+				const u8 *data, size_t data_len,
+				int encrypt)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+	u32 flags = 0;
+	int link_id = -1;
+
+#ifdef CONFIG_IEEE80211BE
+	link_id = hapd->conf->mld_ap ? hapd->mld_link_id : -1;
+#endif /* CONFIG_IEEE80211BE */
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (hapd->ext_eapol_frame_io) {
+		size_t hex_len = 2 * data_len + 1;
+		char *hex = os_malloc(hex_len);
+
+		if (hex == NULL)
+			return -1;
+		wpa_snprintf_hex(hex, hex_len, data, data_len);
+		wpa_msg(hapd->msg_ctx, MSG_INFO, "EAPOL-TX " MACSTR " %s",
+			MAC2STR(addr), hex);
+		os_free(hex);
+		return 0;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	sta = ap_get_sta(hapd, addr);
+	if (sta) {
+		flags = hostapd_sta_flags_to_drv(sta->flags);
+#ifdef CONFIG_IEEE80211BE
+		if (sta->mld_info.mld_sta && (sta->flags & WLAN_STA_AUTHORIZED))
+			link_id = -1;
+#endif /* CONFIG_IEEE80211BE */
+	}
+
+	return hostapd_drv_hapd_send_eapol(hapd, addr, data, data_len,
+					   encrypt, flags, link_id);
+}
+
+
+static int hostapd_wpa_auth_for_each_sta(
+	void *ctx, int (*cb)(struct wpa_state_machine *sm, void *ctx),
+	void *cb_ctx)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+
+	for (sta = hapd->sta_list; sta; sta = sta->next) {
+		if (sta->wpa_sm && cb(sta->wpa_sm, cb_ctx))
+			return 1;
+	}
+	return 0;
+}
+
+
+struct wpa_auth_iface_iter_data {
+	int (*cb)(struct wpa_authenticator *sm, void *ctx);
+	void *cb_ctx;
+};
+
+static int wpa_auth_iface_iter(struct hostapd_iface *iface, void *ctx)
+{
+	struct wpa_auth_iface_iter_data *data = ctx;
+	size_t i;
+	for (i = 0; i < iface->num_bss; i++) {
+		if (iface->bss[i]->wpa_auth &&
+		    data->cb(iface->bss[i]->wpa_auth, data->cb_ctx))
+			return 1;
+	}
+	return 0;
+}
+
+
+static int hostapd_wpa_auth_for_each_auth(
+	void *ctx, int (*cb)(struct wpa_authenticator *sm, void *ctx),
+	void *cb_ctx)
+{
+	struct hostapd_data *hapd = ctx;
+	struct wpa_auth_iface_iter_data data;
+	if (hapd->iface->interfaces == NULL ||
+	    hapd->iface->interfaces->for_each_interface == NULL)
+		return -1;
+	data.cb = cb;
+	data.cb_ctx = cb_ctx;
+	return hapd->iface->interfaces->for_each_interface(
+		hapd->iface->interfaces, wpa_auth_iface_iter, &data);
+}
+
+
+#ifdef CONFIG_IEEE80211R_AP
+
+struct wpa_ft_rrb_rx_later_data {
+	struct dl_list list;
+	u8 addr[ETH_ALEN];
+	size_t data_len;
+	/* followed by data_len octets of data */
+};
+
+static void hostapd_wpa_ft_rrb_rx_later(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct wpa_ft_rrb_rx_later_data *data, *n;
+
+	dl_list_for_each_safe(data, n, &hapd->l2_queue,
+			      struct wpa_ft_rrb_rx_later_data, list) {
+		if (hapd->wpa_auth) {
+			wpa_ft_rrb_rx(hapd->wpa_auth, data->addr,
+				      (const u8 *) (data + 1),
+				      data->data_len);
+		}
+		dl_list_del(&data->list);
+		os_free(data);
+	}
+}
+
+
+struct wpa_auth_ft_iface_iter_data {
+	struct hostapd_data *src_hapd;
+	const u8 *dst;
+	const u8 *data;
+	size_t data_len;
+};
+
+
+static int hostapd_wpa_auth_ft_iter(struct hostapd_iface *iface, void *ctx)
+{
+	struct wpa_auth_ft_iface_iter_data *idata = ctx;
+	struct wpa_ft_rrb_rx_later_data *data;
+	struct hostapd_data *hapd;
+	size_t j;
+
+	for (j = 0; j < iface->num_bss; j++) {
+		hapd = iface->bss[j];
+		if (hapd == idata->src_hapd ||
+		    !hapd->wpa_auth ||
+		    os_memcmp(hapd->own_addr, idata->dst, ETH_ALEN) != 0)
+			continue;
+
+		wpa_printf(MSG_DEBUG,
+			   "FT: Send RRB data directly to locally managed BSS "
+			   MACSTR "@%s -> " MACSTR "@%s",
+			   MAC2STR(idata->src_hapd->own_addr),
+			   idata->src_hapd->conf->iface,
+			   MAC2STR(hapd->own_addr), hapd->conf->iface);
+
+		/* Defer wpa_ft_rrb_rx() until next eloop step as this is
+		 * when it would be triggered when reading from a socket.
+		 * This avoids
+		 * hapd0:send -> hapd1:recv -> hapd1:send -> hapd0:recv,
+		 * that is calling hapd0:recv handler from within
+		 * hapd0:send directly.
+		 */
+		data = os_zalloc(sizeof(*data) + idata->data_len);
+		if (!data)
+			return 1;
+
+		os_memcpy(data->addr, idata->src_hapd->own_addr, ETH_ALEN);
+		os_memcpy(data + 1, idata->data, idata->data_len);
+		data->data_len = idata->data_len;
+
+		dl_list_add(&hapd->l2_queue, &data->list);
+
+		if (!eloop_is_timeout_registered(hostapd_wpa_ft_rrb_rx_later,
+						 hapd, NULL))
+			eloop_register_timeout(0, 0,
+					       hostapd_wpa_ft_rrb_rx_later,
+					       hapd, NULL);
+
+		return 1;
+	}
+
+	return 0;
+}
+
+#endif /* CONFIG_IEEE80211R_AP */
+
+
+static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto,
+				       const u8 *data, size_t data_len)
+{
+	struct hostapd_data *hapd = ctx;
+	struct l2_ethhdr *buf;
+	int ret;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (hapd->ext_eapol_frame_io && proto == ETH_P_EAPOL) {
+		size_t hex_len = 2 * data_len + 1;
+		char *hex = os_malloc(hex_len);
+
+		if (hex == NULL)
+			return -1;
+		wpa_snprintf_hex(hex, hex_len, data, data_len);
+		wpa_msg(hapd->msg_ctx, MSG_INFO, "EAPOL-TX " MACSTR " %s",
+			MAC2STR(dst), hex);
+		os_free(hex);
+		return 0;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#ifdef CONFIG_IEEE80211R_AP
+	if (proto == ETH_P_RRB && hapd->iface->interfaces &&
+	    hapd->iface->interfaces->for_each_interface) {
+		int res;
+		struct wpa_auth_ft_iface_iter_data idata;
+		idata.src_hapd = hapd;
+		idata.dst = dst;
+		idata.data = data;
+		idata.data_len = data_len;
+		res = hapd->iface->interfaces->for_each_interface(
+			hapd->iface->interfaces, hostapd_wpa_auth_ft_iter,
+			&idata);
+		if (res == 1)
+			return data_len;
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+
+	if (hapd->l2 == NULL)
+		return -1;
+
+	buf = os_malloc(sizeof(*buf) + data_len);
+	if (buf == NULL)
+		return -1;
+	os_memcpy(buf->h_dest, dst, ETH_ALEN);
+	os_memcpy(buf->h_source, hapd->own_addr, ETH_ALEN);
+	buf->h_proto = host_to_be16(proto);
+	os_memcpy(buf + 1, data, data_len);
+	ret = l2_packet_send(hapd->l2, dst, proto, (u8 *) buf,
+			     sizeof(*buf) + data_len);
+	os_free(buf);
+	return ret;
+}
+
+
+#ifdef CONFIG_ETH_P_OUI
+static struct eth_p_oui_ctx * hostapd_wpa_get_oui(struct hostapd_data *hapd,
+						  u8 oui_suffix)
+{
+	switch (oui_suffix) {
+#ifdef CONFIG_IEEE80211R_AP
+	case FT_PACKET_R0KH_R1KH_PULL:
+		return hapd->oui_pull;
+	case FT_PACKET_R0KH_R1KH_RESP:
+		return hapd->oui_resp;
+	case FT_PACKET_R0KH_R1KH_PUSH:
+		return hapd->oui_push;
+	case FT_PACKET_R0KH_R1KH_SEQ_REQ:
+		return hapd->oui_sreq;
+	case FT_PACKET_R0KH_R1KH_SEQ_RESP:
+		return hapd->oui_sresp;
+#endif /* CONFIG_IEEE80211R_AP */
+	default:
+		return NULL;
+	}
+}
+#endif /* CONFIG_ETH_P_OUI */
+
+
+#ifdef CONFIG_IEEE80211R_AP
+
+struct oui_deliver_later_data {
+	struct dl_list list;
+	u8 src_addr[ETH_ALEN];
+	u8 dst_addr[ETH_ALEN];
+	size_t data_len;
+	u8 oui_suffix;
+	/* followed by data_len octets of data */
+};
+
+static void hostapd_oui_deliver_later(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct oui_deliver_later_data *data, *n;
+	struct eth_p_oui_ctx *oui_ctx;
+
+	dl_list_for_each_safe(data, n, &hapd->l2_oui_queue,
+			      struct oui_deliver_later_data, list) {
+		oui_ctx = hostapd_wpa_get_oui(hapd, data->oui_suffix);
+		wpa_printf(MSG_DEBUG, "RRB(%s): %s src=" MACSTR " dst=" MACSTR
+			   " oui_suffix=%u data_len=%u data=%p",
+			   hapd->conf->iface, __func__,
+			   MAC2STR(data->src_addr), MAC2STR(data->dst_addr),
+			   data->oui_suffix, (unsigned int) data->data_len,
+			   data);
+		if (hapd->wpa_auth && oui_ctx) {
+			eth_p_oui_deliver(oui_ctx, data->src_addr,
+					  data->dst_addr,
+					  (const u8 *) (data + 1),
+					  data->data_len);
+		}
+		dl_list_del(&data->list);
+		os_free(data);
+	}
+}
+
+
+struct wpa_auth_oui_iface_iter_data {
+	struct hostapd_data *src_hapd;
+	const u8 *dst_addr;
+	const u8 *data;
+	size_t data_len;
+	u8 oui_suffix;
+};
+
+static int hostapd_wpa_auth_oui_iter(struct hostapd_iface *iface, void *ctx)
+{
+	struct wpa_auth_oui_iface_iter_data *idata = ctx;
+	struct oui_deliver_later_data *data;
+	struct hostapd_data *hapd, *src_hapd = idata->src_hapd;
+	size_t j;
+
+	for (j = 0; j < iface->num_bss; j++) {
+		hapd = iface->bss[j];
+		if (hapd == src_hapd)
+			continue; /* don't deliver back to same interface */
+		if (!wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) ||
+		    hapd->conf->ssid.ssid_len !=
+		    src_hapd->conf->ssid.ssid_len ||
+		    os_memcmp(hapd->conf->ssid.ssid,
+			      src_hapd->conf->ssid.ssid,
+			      hapd->conf->ssid.ssid_len) != 0 ||
+		    os_memcmp(hapd->conf->mobility_domain,
+			      src_hapd->conf->mobility_domain,
+			      MOBILITY_DOMAIN_ID_LEN) != 0)
+			continue; /* no matching FT SSID/mobility domain */
+		if (!is_multicast_ether_addr(idata->dst_addr) &&
+		    os_memcmp(hapd->own_addr, idata->dst_addr, ETH_ALEN) != 0)
+			continue; /* destination address does not match */
+
+		/* defer eth_p_oui_deliver until next eloop step as this is
+		 * when it would be triggerd from reading from sock
+		 * This avoids
+		 * hapd0:send -> hapd1:recv -> hapd1:send -> hapd0:recv,
+		 * that is calling hapd0:recv handler from within
+		 * hapd0:send directly.
+		 */
+		data = os_zalloc(sizeof(*data) + idata->data_len);
+		if (!data)
+			return 1;
+		wpa_printf(MSG_DEBUG,
+			   "RRB(%s): local delivery to %s dst=" MACSTR
+			   " oui_suffix=%u data_len=%u data=%p",
+			   src_hapd->conf->iface, hapd->conf->iface,
+			   MAC2STR(idata->dst_addr), idata->oui_suffix,
+			   (unsigned int) idata->data_len, data);
+
+		os_memcpy(data->src_addr, src_hapd->own_addr, ETH_ALEN);
+		os_memcpy(data->dst_addr, idata->dst_addr, ETH_ALEN);
+		os_memcpy(data + 1, idata->data, idata->data_len);
+		data->data_len = idata->data_len;
+		data->oui_suffix = idata->oui_suffix;
+
+		dl_list_add_tail(&hapd->l2_oui_queue, &data->list);
+
+		if (!eloop_is_timeout_registered(hostapd_oui_deliver_later,
+						 hapd, NULL))
+			eloop_register_timeout(0, 0,
+					       hostapd_oui_deliver_later,
+					       hapd, NULL);
+
+		/* If dst_addr is a multicast address, do not return any
+		 * non-zero value here. Otherwise, the iteration of
+		 * for_each_interface() will be stopped. */
+		if (!is_multicast_ether_addr(idata->dst_addr))
+			return 1;
+	}
+
+	return 0;
+}
+
+#endif /* CONFIG_IEEE80211R_AP */
+
+
+static int hostapd_wpa_auth_send_oui(void *ctx, const u8 *dst, u8 oui_suffix,
+				     const u8 *data, size_t data_len)
+{
+#ifdef CONFIG_ETH_P_OUI
+	struct hostapd_data *hapd = ctx;
+	struct eth_p_oui_ctx *oui_ctx;
+
+	wpa_printf(MSG_DEBUG, "RRB(%s): send to dst=" MACSTR
+		   " oui_suffix=%u data_len=%u",
+		   hapd->conf->iface, MAC2STR(dst), oui_suffix,
+		   (unsigned int) data_len);
+#ifdef CONFIG_IEEE80211R_AP
+	if (hapd->iface->interfaces &&
+	    hapd->iface->interfaces->for_each_interface) {
+		struct wpa_auth_oui_iface_iter_data idata;
+		int res;
+
+		idata.src_hapd = hapd;
+		idata.dst_addr = dst;
+		idata.data = data;
+		idata.data_len = data_len;
+		idata.oui_suffix = oui_suffix;
+		res = hapd->iface->interfaces->for_each_interface(
+			hapd->iface->interfaces, hostapd_wpa_auth_oui_iter,
+			&idata);
+		if (res == 1)
+			return data_len;
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+
+	oui_ctx = hostapd_wpa_get_oui(hapd, oui_suffix);
+	if (!oui_ctx)
+		return -1;
+
+	return eth_p_oui_send(oui_ctx, hapd->own_addr, dst, data, data_len);
+#else /* CONFIG_ETH_P_OUI */
+	return -1;
+#endif /* CONFIG_ETH_P_OUI */
+}
+
+
+static int hostapd_channel_info(void *ctx, struct wpa_channel_info *ci)
+{
+	struct hostapd_data *hapd = ctx;
+
+	return hostapd_drv_channel_info(hapd, ci);
+}
+
+
+#ifdef CONFIG_PASN
+
+static void hostapd_store_ptksa(void *ctx, const u8 *addr,int cipher,
+				u32 life_time, const struct wpa_ptk *ptk)
+{
+	struct hostapd_data *hapd = ctx;
+
+	ptksa_cache_add(hapd->ptksa, hapd->own_addr, addr, cipher, life_time,
+			ptk, NULL, NULL, 0);
+}
+
+
+static void hostapd_clear_ptksa(void *ctx, const u8 *addr, int cipher)
+{
+	struct hostapd_data *hapd = ctx;
+
+	ptksa_cache_flush(hapd->ptksa, addr, cipher);
+}
+
+#endif /* CONFIG_PASN */
+
+
+static int hostapd_wpa_auth_update_vlan(void *ctx, const u8 *addr, int vlan_id)
+{
+#ifndef CONFIG_NO_VLAN
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta)
+		return -1;
+
+	if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_VLAN_OFFLOAD)) {
+		struct vlan_description vlan_desc;
+
+		os_memset(&vlan_desc, 0, sizeof(vlan_desc));
+		vlan_desc.notempty = 1;
+		vlan_desc.untagged = vlan_id;
+		if (!hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) {
+			wpa_printf(MSG_INFO,
+				   "Invalid VLAN ID %d in wpa_psk_file",
+				   vlan_id);
+			return -1;
+		}
+
+		if (ap_sta_set_vlan(hapd, sta, &vlan_desc) < 0) {
+			wpa_printf(MSG_INFO,
+				   "Failed to assign VLAN ID %d from wpa_psk_file to "
+				   MACSTR, vlan_id, MAC2STR(sta->addr));
+			return -1;
+		}
+	} else {
+		sta->vlan_id = vlan_id;
+	}
+
+	wpa_printf(MSG_INFO,
+		   "Assigned VLAN ID %d from wpa_psk_file to " MACSTR,
+		   vlan_id, MAC2STR(sta->addr));
+	if ((sta->flags & WLAN_STA_ASSOC) &&
+	    ap_sta_bind_vlan(hapd, sta) < 0)
+		return -1;
+#endif /* CONFIG_NO_VLAN */
+
+	return 0;
+}
+
+
+#ifdef CONFIG_OCV
+static int hostapd_get_sta_tx_params(void *ctx, const u8 *addr,
+				     int ap_max_chanwidth, int ap_seg1_idx,
+				     int *bandwidth, int *seg1_idx)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta) {
+		hostapd_wpa_auth_logger(hapd, addr, LOGGER_INFO,
+					"Failed to get STA info to validate received OCI");
+		return -1;
+	}
+
+	return get_tx_parameters(sta, ap_max_chanwidth, ap_seg1_idx, bandwidth,
+				 seg1_idx);
+}
+#endif /* CONFIG_OCV */
+
+
+#ifdef CONFIG_IEEE80211R_AP
+
+static int hostapd_wpa_auth_send_ft_action(void *ctx, const u8 *dst,
+					   const u8 *data, size_t data_len)
+{
+	struct hostapd_data *hapd = ctx;
+	int res;
+	struct ieee80211_mgmt *m;
+	size_t mlen;
+	struct sta_info *sta;
+
+	sta = ap_get_sta(hapd, dst);
+	if (sta == NULL || sta->wpa_sm == NULL)
+		return -1;
+
+	m = os_zalloc(sizeof(*m) + data_len);
+	if (m == NULL)
+		return -1;
+	mlen = ((u8 *) &m->u - (u8 *) m) + data_len;
+	m->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					WLAN_FC_STYPE_ACTION);
+	os_memcpy(m->da, dst, ETH_ALEN);
+	os_memcpy(m->sa, hapd->own_addr, ETH_ALEN);
+	os_memcpy(m->bssid, hapd->own_addr, ETH_ALEN);
+	os_memcpy(&m->u, data, data_len);
+
+	res = hostapd_drv_send_mlme(hapd, (u8 *) m, mlen, 0, NULL, 0, 0);
+	os_free(m);
+	return res;
+}
+
+
+static struct wpa_state_machine *
+hostapd_wpa_auth_add_sta(void *ctx, const u8 *sta_addr)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "Add station entry for " MACSTR
+		   " based on WPA authenticator callback",
+		   MAC2STR(sta_addr));
+	ret = hostapd_add_sta_node(hapd, sta_addr, WLAN_AUTH_FT);
+
+	/*
+	 * The expected return values from hostapd_add_sta_node() are
+	 * 0: successfully added STA entry
+	 * -EOPNOTSUPP: driver or driver wrapper does not support/need this
+	 *	operations
+	 * any other negative value: error in adding the STA entry */
+	if (ret < 0 && ret != -EOPNOTSUPP)
+		return NULL;
+
+	sta = ap_sta_add(hapd, sta_addr);
+	if (sta == NULL)
+		return NULL;
+	if (ret == 0)
+		sta->added_unassoc = 1;
+
+	sta->ft_over_ds = 1;
+	if (sta->wpa_sm) {
+		sta->auth_alg = WLAN_AUTH_FT;
+		return sta->wpa_sm;
+	}
+
+	sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr, NULL);
+	if (sta->wpa_sm == NULL) {
+		ap_free_sta(hapd, sta);
+		return NULL;
+	}
+	sta->auth_alg = WLAN_AUTH_FT;
+
+	return sta->wpa_sm;
+}
+
+
+static int hostapd_wpa_auth_add_sta_ft(void *ctx, const u8 *sta_addr)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+
+	sta = ap_get_sta(hapd, sta_addr);
+	if (!sta)
+		return -1;
+
+	if (FULL_AP_CLIENT_STATE_SUPP(hapd->iface->drv_flags) &&
+	    (sta->flags & WLAN_STA_MFP) && ap_sta_is_authorized(sta) &&
+	    !(hapd->conf->mesh & MESH_ENABLED) && !(sta->added_unassoc)) {
+		/* We could not do this in handle_auth() since there was a
+		 * PMF-enabled association for the STA and the new
+		 * authentication attempt was not yet fully processed. Now that
+		 * we are ready to configure the TK to the driver,
+		 * authentication has succeeded and we can clean up the driver
+		 * STA entry to avoid issues with any maintained state from the
+		 * previous association. */
+		wpa_printf(MSG_DEBUG,
+			   "FT: Remove and re-add driver STA entry after successful FT authentication");
+		return ap_sta_re_add(hapd, sta);
+	}
+
+	return 0;
+}
+
+
+static int hostapd_wpa_auth_set_vlan(void *ctx, const u8 *sta_addr,
+				     struct vlan_description *vlan)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+
+	sta = ap_get_sta(hapd, sta_addr);
+	if (!sta || !sta->wpa_sm)
+		return -1;
+
+	if (vlan->notempty &&
+	    !hostapd_vlan_valid(hapd->conf->vlan, vlan)) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO,
+			       "Invalid VLAN %d%s received from FT",
+			       vlan->untagged, vlan->tagged[0] ? "+" : "");
+		return -1;
+	}
+
+	if (ap_sta_set_vlan(hapd, sta, vlan) < 0)
+		return -1;
+	/* Configure wpa_group for GTK but ignore error due to driver not
+	 * knowing this STA. */
+	ap_sta_bind_vlan(hapd, sta);
+
+	if (sta->vlan_id)
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id);
+
+	return 0;
+}
+
+
+static int hostapd_wpa_auth_get_vlan(void *ctx, const u8 *sta_addr,
+				     struct vlan_description *vlan)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+
+	sta = ap_get_sta(hapd, sta_addr);
+	if (!sta)
+		return -1;
+
+	if (sta->vlan_desc)
+		*vlan = *sta->vlan_desc;
+	else
+		os_memset(vlan, 0, sizeof(*vlan));
+
+	return 0;
+}
+
+
+static int
+hostapd_wpa_auth_set_identity(void *ctx, const u8 *sta_addr,
+			      const u8 *identity, size_t identity_len)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+
+	sta = ap_get_sta(hapd, sta_addr);
+	if (!sta)
+		return -1;
+
+	os_free(sta->identity);
+	sta->identity = NULL;
+
+	if (sta->eapol_sm) {
+		os_free(sta->eapol_sm->identity);
+		sta->eapol_sm->identity = NULL;
+		sta->eapol_sm->identity_len = 0;
+	}
+
+	if (!identity_len)
+		return 0;
+
+	/* sta->identity is NULL terminated */
+	sta->identity = os_zalloc(identity_len + 1);
+	if (!sta->identity)
+		return -1;
+	os_memcpy(sta->identity, identity, identity_len);
+
+	if (sta->eapol_sm) {
+		sta->eapol_sm->identity = os_zalloc(identity_len);
+		if (!sta->eapol_sm->identity)
+			return -1;
+		os_memcpy(sta->eapol_sm->identity, identity, identity_len);
+		sta->eapol_sm->identity_len = identity_len;
+	}
+
+	return 0;
+}
+
+
+static size_t
+hostapd_wpa_auth_get_identity(void *ctx, const u8 *sta_addr, const u8 **buf)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+	size_t len;
+	char *identity;
+
+	sta = ap_get_sta(hapd, sta_addr);
+	if (!sta)
+		return 0;
+
+	*buf = ieee802_1x_get_identity(sta->eapol_sm, &len);
+	if (*buf && len)
+		return len;
+
+	if (!sta->identity) {
+		*buf = NULL;
+		return 0;
+	}
+
+	identity = sta->identity;
+	len = os_strlen(identity);
+	*buf = (u8 *) identity;
+
+	return len;
+}
+
+
+static int
+hostapd_wpa_auth_set_radius_cui(void *ctx, const u8 *sta_addr,
+				const u8 *radius_cui, size_t radius_cui_len)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+
+	sta = ap_get_sta(hapd, sta_addr);
+	if (!sta)
+		return -1;
+
+	os_free(sta->radius_cui);
+	sta->radius_cui = NULL;
+
+	if (sta->eapol_sm) {
+		wpabuf_free(sta->eapol_sm->radius_cui);
+		sta->eapol_sm->radius_cui = NULL;
+	}
+
+	if (!radius_cui)
+		return 0;
+
+	/* sta->radius_cui is NULL terminated */
+	sta->radius_cui = os_zalloc(radius_cui_len + 1);
+	if (!sta->radius_cui)
+		return -1;
+	os_memcpy(sta->radius_cui, radius_cui, radius_cui_len);
+
+	if (sta->eapol_sm) {
+		sta->eapol_sm->radius_cui = wpabuf_alloc_copy(radius_cui,
+							      radius_cui_len);
+		if (!sta->eapol_sm->radius_cui)
+			return -1;
+	}
+
+	return 0;
+}
+
+
+static size_t
+hostapd_wpa_auth_get_radius_cui(void *ctx, const u8 *sta_addr, const u8 **buf)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+	struct wpabuf *b;
+	size_t len;
+	char *radius_cui;
+
+	sta = ap_get_sta(hapd, sta_addr);
+	if (!sta)
+		return 0;
+
+	b = ieee802_1x_get_radius_cui(sta->eapol_sm);
+	if (b) {
+		len = wpabuf_len(b);
+		*buf = wpabuf_head(b);
+		return len;
+	}
+
+	if (!sta->radius_cui) {
+		*buf = NULL;
+		return 0;
+	}
+
+	radius_cui = sta->radius_cui;
+	len = os_strlen(radius_cui);
+	*buf = (u8 *) radius_cui;
+
+	return len;
+}
+
+
+static void hostapd_wpa_auth_set_session_timeout(void *ctx, const u8 *sta_addr,
+						 int session_timeout)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+
+	sta = ap_get_sta(hapd, sta_addr);
+	if (!sta)
+		return;
+
+	if (session_timeout) {
+		os_get_reltime(&sta->session_timeout);
+		sta->session_timeout.sec += session_timeout;
+		sta->session_timeout_set = 1;
+		ap_sta_session_timeout(hapd, sta, session_timeout);
+	} else {
+		sta->session_timeout_set = 0;
+		ap_sta_no_session_timeout(hapd, sta);
+	}
+}
+
+
+static int hostapd_wpa_auth_get_session_timeout(void *ctx, const u8 *sta_addr)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+	struct os_reltime now, remaining;
+
+	sta = ap_get_sta(hapd, sta_addr);
+	if (!sta || !sta->session_timeout_set)
+		return 0;
+
+	os_get_reltime(&now);
+	if (os_reltime_before(&sta->session_timeout, &now)) {
+		/* already expired, return >0 as timeout was set */
+		return 1;
+	}
+
+	os_reltime_sub(&sta->session_timeout, &now, &remaining);
+
+	return (remaining.sec > 0) ? remaining.sec : 1;
+}
+
+
+static void hostapd_rrb_receive(void *ctx, const u8 *src_addr, const u8 *buf,
+				size_t len)
+{
+	struct hostapd_data *hapd = ctx;
+	struct l2_ethhdr *ethhdr;
+	if (len < sizeof(*ethhdr))
+		return;
+	ethhdr = (struct l2_ethhdr *) buf;
+	wpa_printf(MSG_DEBUG, "FT: RRB received packet " MACSTR " -> "
+		   MACSTR, MAC2STR(ethhdr->h_source), MAC2STR(ethhdr->h_dest));
+	if (!is_multicast_ether_addr(ethhdr->h_dest) &&
+	    os_memcmp(hapd->own_addr, ethhdr->h_dest, ETH_ALEN) != 0)
+		return;
+	wpa_ft_rrb_rx(hapd->wpa_auth, ethhdr->h_source, buf + sizeof(*ethhdr),
+		      len - sizeof(*ethhdr));
+}
+
+
+static void hostapd_rrb_oui_receive(void *ctx, const u8 *src_addr,
+				    const u8 *dst_addr, u8 oui_suffix,
+				    const u8 *buf, size_t len)
+{
+	struct hostapd_data *hapd = ctx;
+
+	wpa_printf(MSG_DEBUG, "FT: RRB received packet " MACSTR " -> "
+		   MACSTR, MAC2STR(src_addr), MAC2STR(dst_addr));
+	if (!is_multicast_ether_addr(dst_addr) &&
+	    os_memcmp(hapd->own_addr, dst_addr, ETH_ALEN) != 0)
+		return;
+	wpa_ft_rrb_oui_rx(hapd->wpa_auth, src_addr, dst_addr, oui_suffix, buf,
+			  len);
+}
+
+
+static int hostapd_wpa_auth_add_tspec(void *ctx, const u8 *sta_addr,
+				      u8 *tspec_ie, size_t tspec_ielen)
+{
+	struct hostapd_data *hapd = ctx;
+	return hostapd_add_tspec(hapd, sta_addr, tspec_ie, tspec_ielen);
+}
+
+
+
+static int hostapd_wpa_register_ft_oui(struct hostapd_data *hapd,
+				       const char *ft_iface)
+{
+	hapd->oui_pull = eth_p_oui_register(hapd, ft_iface,
+					    FT_PACKET_R0KH_R1KH_PULL,
+					    hostapd_rrb_oui_receive, hapd);
+	if (!hapd->oui_pull)
+		return -1;
+
+	hapd->oui_resp = eth_p_oui_register(hapd, ft_iface,
+					    FT_PACKET_R0KH_R1KH_RESP,
+					    hostapd_rrb_oui_receive, hapd);
+	if (!hapd->oui_resp)
+		return -1;
+
+	hapd->oui_push = eth_p_oui_register(hapd, ft_iface,
+					    FT_PACKET_R0KH_R1KH_PUSH,
+					    hostapd_rrb_oui_receive, hapd);
+	if (!hapd->oui_push)
+		return -1;
+
+	hapd->oui_sreq = eth_p_oui_register(hapd, ft_iface,
+					    FT_PACKET_R0KH_R1KH_SEQ_REQ,
+					    hostapd_rrb_oui_receive, hapd);
+	if (!hapd->oui_sreq)
+		return -1;
+
+	hapd->oui_sresp = eth_p_oui_register(hapd, ft_iface,
+					     FT_PACKET_R0KH_R1KH_SEQ_RESP,
+					     hostapd_rrb_oui_receive, hapd);
+	if (!hapd->oui_sresp)
+		return -1;
+
+	return 0;
+}
+
+
+static void hostapd_wpa_unregister_ft_oui(struct hostapd_data *hapd)
+{
+	eth_p_oui_unregister(hapd->oui_pull);
+	hapd->oui_pull = NULL;
+	eth_p_oui_unregister(hapd->oui_resp);
+	hapd->oui_resp = NULL;
+	eth_p_oui_unregister(hapd->oui_push);
+	hapd->oui_push = NULL;
+	eth_p_oui_unregister(hapd->oui_sreq);
+	hapd->oui_sreq = NULL;
+	eth_p_oui_unregister(hapd->oui_sresp);
+	hapd->oui_sresp = NULL;
+}
+#endif /* CONFIG_IEEE80211R_AP */
+
+
+#ifndef CONFIG_NO_RADIUS
+static void hostapd_request_radius_psk(void *ctx, const u8 *addr, int key_mgmt,
+				       const u8 *anonce,
+				       const u8 *eapol, size_t eapol_len)
+{
+	struct hostapd_data *hapd = ctx;
+
+	wpa_printf(MSG_DEBUG, "RADIUS PSK request for " MACSTR " key_mgmt=0x%x",
+		   MAC2STR(addr), key_mgmt);
+	wpa_hexdump(MSG_DEBUG, "ANonce", anonce, WPA_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "EAPOL", eapol, eapol_len);
+	hostapd_acl_req_radius_psk(hapd, addr, key_mgmt, anonce, eapol,
+				   eapol_len);
+}
+#endif /* CONFIG_NO_RADIUS */
+
+
+#ifdef CONFIG_PASN
+static int hostapd_set_ltf_keyseed(void *ctx, const u8 *peer_addr,
+				   const u8 *ltf_keyseed,
+				   size_t ltf_keyseed_len)
+{
+	struct hostapd_data *hapd = ctx;
+
+	return hostapd_drv_set_secure_ranging_ctx(hapd, hapd->own_addr,
+						  peer_addr, 0, 0, NULL,
+						  ltf_keyseed_len,
+						  ltf_keyseed, 0);
+}
+#endif /* CONFIG_PASN */
+
+
+#ifdef CONFIG_IEEE80211BE
+
+static int hostapd_wpa_auth_get_ml_rsn_info(void *ctx,
+					    struct wpa_auth_ml_rsn_info *info)
+{
+	struct hostapd_data *hapd = ctx;
+	unsigned int i, j;
+
+	wpa_printf(MSG_DEBUG, "WPA_AUTH: MLD: Get RSN info CB: n_mld_links=%u",
+		   info->n_mld_links);
+
+	if (!hapd->conf->mld_ap || !hapd->iface || !hapd->iface->interfaces)
+		return -1;
+
+	for (i = 0; i < info->n_mld_links; i++) {
+		unsigned int link_id = info->links[i].link_id;
+
+		wpa_printf(MSG_DEBUG,
+			   "WPA_AUTH: MLD: Get link RSN CB: link_id=%u",
+			   link_id);
+
+		for (j = 0; j < hapd->iface->interfaces->count; j++) {
+			struct hostapd_iface *iface =
+				hapd->iface->interfaces->iface[j];
+
+			if (!iface->bss[0]->conf->mld_ap ||
+			    hapd->conf->mld_id != iface->bss[0]->conf->mld_id ||
+			    link_id != iface->bss[0]->mld_link_id)
+				continue;
+
+			wpa_auth_ml_get_rsn_info(iface->bss[0]->wpa_auth,
+						 &info->links[i]);
+			break;
+		}
+
+		if (j == hapd->iface->interfaces->count)
+			wpa_printf(MSG_DEBUG,
+				   "WPA_AUTH: MLD: link=%u not found", link_id);
+	}
+
+	return 0;
+}
+
+
+static int hostapd_wpa_auth_get_ml_key_info(void *ctx,
+					    struct wpa_auth_ml_key_info *info)
+{
+	struct hostapd_data *hapd = ctx;
+	unsigned int i, j;
+
+	wpa_printf(MSG_DEBUG, "WPA_AUTH: MLD: Get key info CB: n_mld_links=%u",
+		   info->n_mld_links);
+
+	if (!hapd->conf->mld_ap || !hapd->iface || !hapd->iface->interfaces)
+		return -1;
+
+	for (i = 0; i < info->n_mld_links; i++) {
+		u8 link_id = info->links[i].link_id;
+
+		wpa_printf(MSG_DEBUG,
+			   "WPA_AUTH: MLD: Get link info CB: link_id=%u",
+			   link_id);
+
+		for (j = 0; j < hapd->iface->interfaces->count; j++) {
+			struct hostapd_iface *iface =
+				hapd->iface->interfaces->iface[j];
+
+			if (!iface->bss[0]->conf->mld_ap ||
+			    hapd->conf->mld_id != iface->bss[0]->conf->mld_id ||
+			    link_id != iface->bss[0]->mld_link_id)
+				continue;
+
+			wpa_auth_ml_get_key_info(iface->bss[0]->wpa_auth,
+						 &info->links[i],
+						 info->mgmt_frame_prot,
+						 info->beacon_prot);
+			break;
+		}
+
+		if (j == hapd->iface->interfaces->count)
+			wpa_printf(MSG_DEBUG,
+				   "WPA_AUTH: MLD: link=%u not found", link_id);
+	}
+
+	return 0;
+}
+
+#endif /* CONFIG_IEEE80211BE */
+
+
+int hostapd_setup_wpa(struct hostapd_data *hapd)
+{
+	struct wpa_auth_config _conf;
+	static const struct wpa_auth_callbacks cb = {
+		.logger = hostapd_wpa_auth_logger,
+		.disconnect = hostapd_wpa_auth_disconnect,
+		.mic_failure_report = hostapd_wpa_auth_mic_failure_report,
+		.psk_failure_report = hostapd_wpa_auth_psk_failure_report,
+		.set_eapol = hostapd_wpa_auth_set_eapol,
+		.get_eapol = hostapd_wpa_auth_get_eapol,
+		.get_psk = hostapd_wpa_auth_get_psk,
+		.get_msk = hostapd_wpa_auth_get_msk,
+		.set_key = hostapd_wpa_auth_set_key,
+		.get_seqnum = hostapd_wpa_auth_get_seqnum,
+		.send_eapol = hostapd_wpa_auth_send_eapol,
+		.for_each_sta = hostapd_wpa_auth_for_each_sta,
+		.for_each_auth = hostapd_wpa_auth_for_each_auth,
+		.send_ether = hostapd_wpa_auth_send_ether,
+		.send_oui = hostapd_wpa_auth_send_oui,
+		.channel_info = hostapd_channel_info,
+		.update_vlan = hostapd_wpa_auth_update_vlan,
+#ifdef CONFIG_PASN
+		.store_ptksa = hostapd_store_ptksa,
+		.clear_ptksa = hostapd_clear_ptksa,
+#endif /* CONFIG_PASN */
+
+#ifdef CONFIG_OCV
+		.get_sta_tx_params = hostapd_get_sta_tx_params,
+#endif /* CONFIG_OCV */
+#ifdef CONFIG_IEEE80211R_AP
+		.send_ft_action = hostapd_wpa_auth_send_ft_action,
+		.add_sta = hostapd_wpa_auth_add_sta,
+		.add_sta_ft = hostapd_wpa_auth_add_sta_ft,
+		.add_tspec = hostapd_wpa_auth_add_tspec,
+		.set_vlan = hostapd_wpa_auth_set_vlan,
+		.get_vlan = hostapd_wpa_auth_get_vlan,
+		.set_identity = hostapd_wpa_auth_set_identity,
+		.get_identity = hostapd_wpa_auth_get_identity,
+		.set_radius_cui = hostapd_wpa_auth_set_radius_cui,
+		.get_radius_cui = hostapd_wpa_auth_get_radius_cui,
+		.set_session_timeout = hostapd_wpa_auth_set_session_timeout,
+		.get_session_timeout = hostapd_wpa_auth_get_session_timeout,
+#endif /* CONFIG_IEEE80211R_AP */
+#ifndef CONFIG_NO_RADIUS
+		.request_radius_psk = hostapd_request_radius_psk,
+#endif /* CONFIG_NO_RADIUS */
+#ifdef CONFIG_PASN
+		.set_ltf_keyseed = hostapd_set_ltf_keyseed,
+#endif /* CONFIG_PASN */
+#ifdef CONFIG_IEEE80211BE
+		.get_ml_rsn_info = hostapd_wpa_auth_get_ml_rsn_info,
+		.get_ml_key_info = hostapd_wpa_auth_get_ml_key_info,
+#endif /* CONFIG_IEEE80211BE */
+	};
+	const u8 *wpa_ie;
+	size_t wpa_ie_len;
+
+	hostapd_wpa_auth_conf(hapd->conf, hapd->iconf, &_conf);
+	_conf.msg_ctx = hapd->msg_ctx;
+	if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_EAPOL_TX_STATUS)
+		_conf.tx_status = 1;
+	if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_MLME)
+		_conf.ap_mlme = 1;
+
+	if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED) &&
+	    (hapd->conf->wpa_deny_ptk0_rekey == PTK0_REKEY_ALLOW_NEVER ||
+	     (hapd->conf->wpa_deny_ptk0_rekey == PTK0_REKEY_ALLOW_LOCAL_OK &&
+	      !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_SAFE_PTK0_REKEYS)))) {
+		wpa_msg(hapd->msg_ctx, MSG_INFO,
+			"Disable PTK0 rekey support - replaced with disconnect");
+		_conf.wpa_deny_ptk0_rekey = 1;
+	}
+
+	if (_conf.extended_key_id &&
+	    (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_EXTENDED_KEY_ID))
+		wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Extended Key ID supported");
+	else
+		_conf.extended_key_id = 0;
+
+	if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_BEACON_PROTECTION))
+		_conf.beacon_prot = 0;
+
+#ifdef CONFIG_OCV
+	if (!(hapd->iface->drv_flags2 &
+	      (WPA_DRIVER_FLAGS2_AP_SME | WPA_DRIVER_FLAGS2_OCV)))
+		_conf.ocv = 0;
+#endif /* CONFIG_OCV */
+
+	_conf.secure_ltf =
+		!!(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_AP);
+	_conf.secure_rtt =
+		!!(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_RTT_AP);
+	_conf.prot_range_neg =
+		!!(hapd->iface->drv_flags2 &
+		   WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP);
+
+	hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb, hapd);
+	if (hapd->wpa_auth == NULL) {
+		wpa_printf(MSG_ERROR, "WPA initialization failed.");
+		return -1;
+	}
+
+	if (hostapd_set_privacy(hapd, 1)) {
+		wpa_printf(MSG_ERROR, "Could not set PrivacyInvoked "
+			   "for interface %s", hapd->conf->iface);
+		return -1;
+	}
+
+	wpa_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &wpa_ie_len);
+	if (hostapd_set_generic_elem(hapd, wpa_ie, wpa_ie_len)) {
+		wpa_printf(MSG_ERROR, "Failed to configure WPA IE for "
+			   "the kernel driver.");
+		return -1;
+	}
+
+	if (rsn_preauth_iface_init(hapd)) {
+		wpa_printf(MSG_ERROR, "Initialization of RSN "
+			   "pre-authentication failed.");
+		return -1;
+	}
+
+	if (!hapd->ptksa)
+		hapd->ptksa = ptksa_cache_init();
+	if (!hapd->ptksa) {
+		wpa_printf(MSG_ERROR, "Failed to allocate PTKSA cache");
+		return -1;
+	}
+
+#ifdef CONFIG_IEEE80211R_AP
+	if (!hostapd_drv_none(hapd) &&
+	    wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt)) {
+		const char *ft_iface;
+
+		if (hapd->conf->ft_iface[0])
+			ft_iface = hapd->conf->ft_iface;
+		else if (hapd->conf->bridge[0])
+			ft_iface = hapd->conf->bridge;
+		else
+			ft_iface = hapd->conf->iface;
+		hapd->l2 = l2_packet_init(ft_iface, NULL, ETH_P_RRB,
+					  hostapd_rrb_receive, hapd, 1);
+		if (!hapd->l2) {
+			wpa_printf(MSG_ERROR, "Failed to open l2_packet "
+				   "interface");
+			return -1;
+		}
+
+		if (hostapd_wpa_register_ft_oui(hapd, ft_iface)) {
+			wpa_printf(MSG_ERROR,
+				   "Failed to open ETH_P_OUI interface");
+			return -1;
+		}
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+
+	return 0;
+
+}
+
+
+void hostapd_reconfig_wpa(struct hostapd_data *hapd)
+{
+	struct wpa_auth_config wpa_auth_conf;
+	hostapd_wpa_auth_conf(hapd->conf, hapd->iconf, &wpa_auth_conf);
+	wpa_reconfig(hapd->wpa_auth, &wpa_auth_conf);
+}
+
+
+void hostapd_deinit_wpa(struct hostapd_data *hapd)
+{
+	ieee80211_tkip_countermeasures_deinit(hapd);
+	ptksa_cache_deinit(hapd->ptksa);
+	hapd->ptksa = NULL;
+
+	rsn_preauth_iface_deinit(hapd);
+	if (hapd->wpa_auth) {
+		wpa_deinit(hapd->wpa_auth);
+		hapd->wpa_auth = NULL;
+
+		if (hapd->drv_priv && hostapd_set_privacy(hapd, 0)) {
+			wpa_printf(MSG_DEBUG, "Could not disable "
+				   "PrivacyInvoked for interface %s",
+				   hapd->conf->iface);
+		}
+
+		if (hapd->drv_priv &&
+		    hostapd_set_generic_elem(hapd, (u8 *) "", 0)) {
+			wpa_printf(MSG_DEBUG, "Could not remove generic "
+				   "information element from interface %s",
+				   hapd->conf->iface);
+		}
+	}
+	ieee802_1x_deinit(hapd);
+
+#ifdef CONFIG_IEEE80211R_AP
+	eloop_cancel_timeout(hostapd_wpa_ft_rrb_rx_later, hapd, ELOOP_ALL_CTX);
+	hostapd_wpa_ft_rrb_rx_later(hapd, NULL); /* flush without delivering */
+	eloop_cancel_timeout(hostapd_oui_deliver_later, hapd, ELOOP_ALL_CTX);
+	hostapd_oui_deliver_later(hapd, NULL); /* flush without delivering */
+	l2_packet_deinit(hapd->l2);
+	hapd->l2 = NULL;
+	hostapd_wpa_unregister_ft_oui(hapd);
+#endif /* CONFIG_IEEE80211R_AP */
+
+#ifdef CONFIG_TESTING_OPTIONS
+	forced_memzero(hapd->last_gtk, WPA_GTK_MAX_LEN);
+	forced_memzero(hapd->last_igtk, WPA_IGTK_MAX_LEN);
+	forced_memzero(hapd->last_bigtk, WPA_BIGTK_MAX_LEN);
+#endif /* CONFIG_TESTING_OPTIONS */
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_glue.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_glue.h
new file mode 100644
index 0000000..1b13ae7
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_glue.h
@@ -0,0 +1,16 @@
+/*
+ * hostapd / WPA authenticator glue code
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPA_AUTH_GLUE_H
+#define WPA_AUTH_GLUE_H
+
+int hostapd_setup_wpa(struct hostapd_data *hapd);
+void hostapd_reconfig_wpa(struct hostapd_data *hapd);
+void hostapd_deinit_wpa(struct hostapd_data *hapd);
+
+#endif /* WPA_AUTH_GLUE_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_i.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_i.h
new file mode 100644
index 0000000..74ae5ad
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_i.h
@@ -0,0 +1,341 @@
+/*
+ * hostapd - IEEE 802.11i-2004 / WPA Authenticator: Internal definitions
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPA_AUTH_I_H
+#define WPA_AUTH_I_H
+
+#include "utils/list.h"
+
+/* max(dot11RSNAConfigGroupUpdateCount,dot11RSNAConfigPairwiseUpdateCount) */
+#define RSNA_MAX_EAPOL_RETRIES 4
+
+struct wpa_group;
+
+struct wpa_state_machine {
+	struct wpa_authenticator *wpa_auth;
+	struct wpa_group *group;
+
+	u8 addr[ETH_ALEN];
+	u8 p2p_dev_addr[ETH_ALEN];
+	u16 auth_alg;
+
+	enum {
+		WPA_PTK_INITIALIZE, WPA_PTK_DISCONNECT, WPA_PTK_DISCONNECTED,
+		WPA_PTK_AUTHENTICATION, WPA_PTK_AUTHENTICATION2,
+		WPA_PTK_INITPMK, WPA_PTK_INITPSK, WPA_PTK_PTKSTART,
+		WPA_PTK_PTKCALCNEGOTIATING, WPA_PTK_PTKCALCNEGOTIATING2,
+		WPA_PTK_PTKINITNEGOTIATING, WPA_PTK_PTKINITDONE
+	} wpa_ptk_state;
+
+	enum {
+		WPA_PTK_GROUP_IDLE = 0,
+		WPA_PTK_GROUP_REKEYNEGOTIATING,
+		WPA_PTK_GROUP_REKEYESTABLISHED,
+		WPA_PTK_GROUP_KEYERROR
+	} wpa_ptk_group_state;
+
+	bool Init;
+	bool DeauthenticationRequest;
+	bool AuthenticationRequest;
+	bool ReAuthenticationRequest;
+	bool Disconnect;
+	u16 disconnect_reason; /* specific reason code to use with Disconnect */
+	u32 TimeoutCtr;
+	u32 GTimeoutCtr;
+	bool TimeoutEvt;
+	bool EAPOLKeyReceived;
+	bool EAPOLKeyPairwise;
+	bool EAPOLKeyRequest;
+	bool MICVerified;
+	bool GUpdateStationKeys;
+	u8 ANonce[WPA_NONCE_LEN];
+	u8 SNonce[WPA_NONCE_LEN];
+	u8 alt_SNonce[WPA_NONCE_LEN];
+	u8 alt_replay_counter[WPA_REPLAY_COUNTER_LEN];
+	u8 PMK[PMK_LEN_MAX];
+	unsigned int pmk_len;
+	u8 pmkid[PMKID_LEN]; /* valid if pmkid_set == 1 */
+	struct wpa_ptk PTK;
+	u8 keyidx_active;
+	bool use_ext_key_id;
+	bool PTK_valid;
+	bool pairwise_set;
+	bool tk_already_set;
+	int keycount;
+	bool Pair;
+	struct wpa_key_replay_counter {
+		u8 counter[WPA_REPLAY_COUNTER_LEN];
+		bool valid;
+	} key_replay[RSNA_MAX_EAPOL_RETRIES],
+		prev_key_replay[RSNA_MAX_EAPOL_RETRIES];
+	bool PInitAKeys; /* WPA only, not in IEEE 802.11i */
+	bool PTKRequest; /* not in IEEE 802.11i state machine */
+	bool has_GTK;
+	bool PtkGroupInit; /* init request for PTK Group state machine */
+
+	u8 *last_rx_eapol_key; /* starting from IEEE 802.1X header */
+	size_t last_rx_eapol_key_len;
+
+	unsigned int changed:1;
+	unsigned int in_step_loop:1;
+	unsigned int pending_deinit:1;
+	unsigned int started:1;
+	unsigned int mgmt_frame_prot:1;
+	unsigned int mfpr:1;
+	unsigned int rx_eapol_key_secure:1;
+	unsigned int update_snonce:1;
+	unsigned int alt_snonce_valid:1;
+	unsigned int waiting_radius_psk:1;
+#ifdef CONFIG_IEEE80211R_AP
+	unsigned int ft_completed:1;
+	unsigned int pmk_r1_name_valid:1;
+#endif /* CONFIG_IEEE80211R_AP */
+	unsigned int is_wnmsleep:1;
+	unsigned int pmkid_set:1;
+
+	unsigned int ptkstart_without_success;
+
+#ifdef CONFIG_OCV
+	int ocv_enabled;
+#endif /* CONFIG_OCV */
+
+	u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN];
+	int req_replay_counter_used;
+
+	u8 *wpa_ie;
+	size_t wpa_ie_len;
+	u8 *rsnxe;
+	size_t rsnxe_len;
+
+	enum {
+		WPA_VERSION_NO_WPA = 0 /* WPA not used */,
+		WPA_VERSION_WPA = 1 /* WPA / IEEE 802.11i/D3.0 */,
+		WPA_VERSION_WPA2 = 2 /* WPA2 / IEEE 802.11i */
+	} wpa;
+	int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */
+	int wpa_key_mgmt; /* the selected WPA_KEY_MGMT_* */
+	struct rsn_pmksa_cache_entry *pmksa;
+
+	u32 dot11RSNAStatsTKIPLocalMICFailures;
+	u32 dot11RSNAStatsTKIPRemoteMICFailures;
+
+#ifdef CONFIG_IEEE80211R_AP
+	u8 xxkey[PMK_LEN_MAX]; /* PSK or the second 256 bits of MSK, or the
+				* first 384 bits of MSK */
+	size_t xxkey_len;
+	u8 pmk_r1[PMK_LEN_MAX];
+	unsigned int pmk_r1_len;
+	u8 pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name derived from FT Auth
+					   * Request */
+	u8 r0kh_id[FT_R0KH_ID_MAX_LEN]; /* R0KH-ID from FT Auth Request */
+	size_t r0kh_id_len;
+	u8 sup_pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name from EAPOL-Key
+					       * message 2/4 */
+	u8 *assoc_resp_ftie;
+
+	void (*ft_pending_cb)(void *ctx, const u8 *dst, const u8 *bssid,
+			      u16 auth_transaction, u16 status,
+			      const u8 *ies, size_t ies_len);
+	void *ft_pending_cb_ctx;
+	struct wpabuf *ft_pending_req_ies;
+	u8 ft_pending_pull_nonce[FT_RRB_NONCE_LEN];
+	u8 ft_pending_auth_transaction;
+	u8 ft_pending_current_ap[ETH_ALEN];
+	int ft_pending_pull_left_retries;
+#endif /* CONFIG_IEEE80211R_AP */
+
+	int pending_1_of_4_timeout;
+
+#ifdef CONFIG_P2P
+	u8 ip_addr[4];
+	unsigned int ip_addr_bit;
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_FILS
+	u8 fils_key_auth_sta[FILS_MAX_KEY_AUTH_LEN];
+	u8 fils_key_auth_ap[FILS_MAX_KEY_AUTH_LEN];
+	size_t fils_key_auth_len;
+	unsigned int fils_completed:1;
+#endif /* CONFIG_FILS */
+
+#ifdef CONFIG_DPP2
+	struct wpabuf *dpp_z;
+#endif /* CONFIG_DPP2 */
+
+#ifdef CONFIG_TESTING_OPTIONS
+	void (*eapol_status_cb)(void *ctx1, void *ctx2);
+	void *eapol_status_cb_ctx1;
+	void *eapol_status_cb_ctx2;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#ifdef CONFIG_IEEE80211BE
+	u8 own_mld_addr[ETH_ALEN];
+	u8 peer_mld_addr[ETH_ALEN];
+	s8 mld_assoc_link_id;
+	u8 n_mld_affiliated_links;
+
+	struct mld_link {
+		bool valid;
+		u8 peer_addr[ETH_ALEN];
+		u8 own_addr[ETH_ALEN];
+
+		const u8 *rsne;
+		size_t rsne_len;
+		const u8 *rsnxe;
+		size_t rsnxe_len;
+	} mld_links[MAX_NUM_MLD_LINKS];
+#endif /* CONFIG_IEEE80211BE */
+};
+
+
+/* per group key state machine data */
+struct wpa_group {
+	struct wpa_group *next;
+	int vlan_id;
+
+	bool GInit;
+	int GKeyDoneStations;
+	bool GTKReKey;
+	int GTK_len;
+	int GN, GM;
+	bool GTKAuthenticator;
+	u8 Counter[WPA_NONCE_LEN];
+
+	enum {
+		WPA_GROUP_GTK_INIT = 0,
+		WPA_GROUP_SETKEYS, WPA_GROUP_SETKEYSDONE,
+		WPA_GROUP_FATAL_FAILURE
+	} wpa_group_state;
+
+	u8 GMK[WPA_GMK_LEN];
+	u8 GTK[2][WPA_GTK_MAX_LEN];
+	u8 GNonce[WPA_NONCE_LEN];
+	bool changed;
+	bool first_sta_seen;
+	bool reject_4way_hs_for_entropy;
+	u8 IGTK[2][WPA_IGTK_MAX_LEN];
+	u8 BIGTK[2][WPA_IGTK_MAX_LEN];
+	int GN_igtk, GM_igtk;
+	int GN_bigtk, GM_bigtk;
+	/* Number of references except those in struct wpa_group->next */
+	unsigned int references;
+	unsigned int num_setup_iface;
+};
+
+
+struct wpa_ft_pmk_cache;
+
+/* per authenticator data */
+struct wpa_authenticator {
+	struct wpa_group *group;
+
+	unsigned int dot11RSNAStatsTKIPRemoteMICFailures;
+	u32 dot11RSNAAuthenticationSuiteSelected;
+	u32 dot11RSNAPairwiseCipherSelected;
+	u32 dot11RSNAGroupCipherSelected;
+	u8 dot11RSNAPMKIDUsed[PMKID_LEN];
+	u32 dot11RSNAAuthenticationSuiteRequested; /* FIX: update */
+	u32 dot11RSNAPairwiseCipherRequested; /* FIX: update */
+	u32 dot11RSNAGroupCipherRequested; /* FIX: update */
+	unsigned int dot11RSNATKIPCounterMeasuresInvoked;
+	unsigned int dot11RSNA4WayHandshakeFailures;
+
+	struct wpa_auth_config conf;
+	const struct wpa_auth_callbacks *cb;
+	void *cb_ctx;
+
+	u8 *wpa_ie;
+	size_t wpa_ie_len;
+
+	u8 addr[ETH_ALEN];
+
+	struct rsn_pmksa_cache *pmksa;
+	struct wpa_ft_pmk_cache *ft_pmk_cache;
+
+#ifdef CONFIG_P2P
+	struct bitfield *ip_pool;
+#endif /* CONFIG_P2P */
+};
+
+
+#ifdef CONFIG_IEEE80211R_AP
+
+#define FT_REMOTE_SEQ_BACKLOG 16
+struct ft_remote_seq_rx {
+	u32 dom;
+	struct os_reltime time_offset; /* local time - offset = remote time */
+
+	/* accepted sequence numbers: (offset ... offset + 0x40000000]
+	 *   (except those in last)
+	 * dropped sequence numbers: (offset - 0x40000000 ... offset]
+	 * all others trigger SEQ_REQ message (except first message)
+	 */
+	u32 last[FT_REMOTE_SEQ_BACKLOG];
+	unsigned int num_last;
+	u32 offsetidx;
+
+	struct dl_list queue; /* send nonces + rrb msgs awaiting seq resp */
+};
+
+struct ft_remote_seq_tx {
+	u32 dom; /* non zero if initialized */
+	u32 seq;
+};
+
+struct ft_remote_seq {
+	struct ft_remote_seq_rx rx;
+	struct ft_remote_seq_tx tx;
+};
+
+#endif /* CONFIG_IEEE80211R_AP */
+
+
+int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
+		     const u8 *pmkid);
+int wpa_write_rsnxe(struct wpa_auth_config *conf, u8 *buf, size_t len);
+void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr,
+		     logger_level level, const char *txt);
+void wpa_auth_vlogger(struct wpa_authenticator *wpa_auth, const u8 *addr,
+		      logger_level level, const char *fmt, ...)
+	PRINTF_FORMAT(4, 5);
+void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
+		      struct wpa_state_machine *sm, int key_info,
+		      const u8 *key_rsc, const u8 *nonce,
+		      const u8 *kde, size_t kde_len,
+		      int keyidx, int encr, int force_version);
+int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth,
+			  int (*cb)(struct wpa_state_machine *sm, void *ctx),
+			  void *cb_ctx);
+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);
+int wpa_write_ftie(struct wpa_auth_config *conf, int key_mgmt, size_t key_len,
+		   const u8 *r0kh_id, size_t r0kh_id_len,
+		   const u8 *anonce, const u8 *snonce,
+		   u8 *buf, size_t len, const u8 *subelem,
+		   size_t subelem_len, int rsnxe_used);
+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);
+int wpa_ft_store_pmk_fils(struct wpa_state_machine *sm, const u8 *pmk_r0,
+			  const u8 *pmk_r0_name);
+#endif /* CONFIG_IEEE80211R_AP */
+
+#endif /* WPA_AUTH_I_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_ie.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_ie.c
new file mode 100644
index 0000000..43ccec9
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_ie.c
@@ -0,0 +1,1253 @@
+/*
+ * hostapd - WPA/RSN IE and KDE definitions
+ * Copyright (c) 2004-2018, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "ap_config.h"
+#include "ieee802_11.h"
+#include "wpa_auth.h"
+#include "pmksa_cache_auth.h"
+#include "wpa_auth_ie.h"
+#include "wpa_auth_i.h"
+
+
+#ifdef CONFIG_RSN_TESTING
+int rsn_testing = 0;
+#endif /* CONFIG_RSN_TESTING */
+
+
+static int wpa_write_wpa_ie(struct wpa_auth_config *conf, u8 *buf, size_t len)
+{
+	struct wpa_ie_hdr *hdr;
+	int num_suites;
+	u8 *pos, *count;
+	u32 suite;
+
+	hdr = (struct wpa_ie_hdr *) buf;
+	hdr->elem_id = WLAN_EID_VENDOR_SPECIFIC;
+	RSN_SELECTOR_PUT(hdr->oui, WPA_OUI_TYPE);
+	WPA_PUT_LE16(hdr->version, WPA_VERSION);
+	pos = (u8 *) (hdr + 1);
+
+	suite = wpa_cipher_to_suite(WPA_PROTO_WPA, conf->wpa_group);
+	if (suite == 0) {
+		wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).",
+			   conf->wpa_group);
+		return -1;
+	}
+	RSN_SELECTOR_PUT(pos, suite);
+	pos += WPA_SELECTOR_LEN;
+
+	count = pos;
+	pos += 2;
+
+	num_suites = wpa_cipher_put_suites(pos, conf->wpa_pairwise);
+	if (num_suites == 0) {
+		wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).",
+			   conf->wpa_pairwise);
+		return -1;
+	}
+	pos += num_suites * WPA_SELECTOR_LEN;
+	WPA_PUT_LE16(count, num_suites);
+
+	num_suites = 0;
+	count = pos;
+	pos += 2;
+
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
+		RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X);
+		pos += WPA_SELECTOR_LEN;
+		num_suites++;
+	}
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) {
+		RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X);
+		pos += WPA_SELECTOR_LEN;
+		num_suites++;
+	}
+
+	if (num_suites == 0) {
+		wpa_printf(MSG_DEBUG, "Invalid key management type (%d).",
+			   conf->wpa_key_mgmt);
+		return -1;
+	}
+	WPA_PUT_LE16(count, num_suites);
+
+	/* WPA Capabilities; use defaults, so no need to include it */
+
+	hdr->len = (pos - buf) - 2;
+
+	return pos - buf;
+}
+
+
+static u16 wpa_own_rsn_capab(struct wpa_auth_config *conf)
+{
+	u16 capab = 0;
+
+	if (conf->rsn_preauth)
+		capab |= WPA_CAPABILITY_PREAUTH;
+	if (conf->wmm_enabled) {
+		/* 4 PTKSA replay counters when using WMM */
+		capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2);
+	}
+	if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+		capab |= WPA_CAPABILITY_MFPC;
+		if (conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED)
+			capab |= WPA_CAPABILITY_MFPR;
+	}
+#ifdef CONFIG_OCV
+	if (conf->ocv)
+		capab |= WPA_CAPABILITY_OCVC;
+#endif /* CONFIG_OCV */
+#ifdef CONFIG_RSN_TESTING
+	if (rsn_testing)
+		capab |= BIT(8) | BIT(15);
+#endif /* CONFIG_RSN_TESTING */
+	if (conf->extended_key_id)
+		capab |= WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST;
+
+	return capab;
+}
+
+
+int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
+		     const u8 *pmkid)
+{
+	struct rsn_ie_hdr *hdr;
+	int num_suites, res;
+	u8 *pos, *count;
+	u32 suite;
+
+	hdr = (struct rsn_ie_hdr *) buf;
+	hdr->elem_id = WLAN_EID_RSN;
+	WPA_PUT_LE16(hdr->version, RSN_VERSION);
+	pos = (u8 *) (hdr + 1);
+
+	suite = wpa_cipher_to_suite(WPA_PROTO_RSN, conf->wpa_group);
+	if (suite == 0) {
+		wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).",
+			   conf->wpa_group);
+		return -1;
+	}
+	RSN_SELECTOR_PUT(pos, suite);
+	pos += RSN_SELECTOR_LEN;
+
+	num_suites = 0;
+	count = pos;
+	pos += 2;
+
+#ifdef CONFIG_RSN_TESTING
+	if (rsn_testing) {
+		RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 1));
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+#endif /* CONFIG_RSN_TESTING */
+
+	res = rsn_cipher_put_suites(pos, conf->rsn_pairwise);
+	num_suites += res;
+	pos += res * RSN_SELECTOR_LEN;
+
+#ifdef CONFIG_RSN_TESTING
+	if (rsn_testing) {
+		RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 2));
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+#endif /* CONFIG_RSN_TESTING */
+
+	if (num_suites == 0) {
+		wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).",
+			   conf->rsn_pairwise);
+		return -1;
+	}
+	WPA_PUT_LE16(count, num_suites);
+
+	num_suites = 0;
+	count = pos;
+	pos += 2;
+
+#ifdef CONFIG_RSN_TESTING
+	if (rsn_testing) {
+		RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 1));
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+#endif /* CONFIG_RSN_TESTING */
+
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+#ifdef CONFIG_IEEE80211R_AP
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+#ifdef CONFIG_SHA384
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+#endif /* CONFIG_SHA384 */
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_SHA256);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+#ifdef CONFIG_SAE
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE_EXT_KEY);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE_EXT_KEY) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+#endif /* CONFIG_SAE */
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+#ifdef CONFIG_FILS
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA256) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA256);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA384) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA384);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+#ifdef CONFIG_IEEE80211R_AP
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA256);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA384);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_OWE
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OWE);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_DPP);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+#endif /* CONFIG_DPP */
+#ifdef CONFIG_HS20
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_OSEN) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OSEN);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+#endif /* CONFIG_HS20 */
+#ifdef CONFIG_PASN
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PASN) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PASN);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+#endif /* CONFIG_PASN */
+
+#ifdef CONFIG_RSN_TESTING
+	if (rsn_testing) {
+		RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 2));
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+#endif /* CONFIG_RSN_TESTING */
+
+	if (num_suites == 0) {
+		wpa_printf(MSG_DEBUG, "Invalid key management type (%d).",
+			   conf->wpa_key_mgmt);
+		return -1;
+	}
+	WPA_PUT_LE16(count, num_suites);
+
+	/* RSN Capabilities */
+	WPA_PUT_LE16(pos, wpa_own_rsn_capab(conf));
+	pos += 2;
+
+	if (pmkid) {
+		if (2 + PMKID_LEN > buf + len - pos)
+			return -1;
+		/* PMKID Count */
+		WPA_PUT_LE16(pos, 1);
+		pos += 2;
+		os_memcpy(pos, pmkid, PMKID_LEN);
+		pos += PMKID_LEN;
+	}
+
+	if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION &&
+	    conf->group_mgmt_cipher != WPA_CIPHER_AES_128_CMAC) {
+		if (2 + 4 > buf + len - pos)
+			return -1;
+		if (pmkid == NULL) {
+			/* PMKID Count */
+			WPA_PUT_LE16(pos, 0);
+			pos += 2;
+		}
+
+		/* Management Group Cipher Suite */
+		switch (conf->group_mgmt_cipher) {
+		case WPA_CIPHER_AES_128_CMAC:
+			RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
+			break;
+		case WPA_CIPHER_BIP_GMAC_128:
+			RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_BIP_GMAC_128);
+			break;
+		case WPA_CIPHER_BIP_GMAC_256:
+			RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_BIP_GMAC_256);
+			break;
+		case WPA_CIPHER_BIP_CMAC_256:
+			RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_BIP_CMAC_256);
+			break;
+		default:
+			wpa_printf(MSG_DEBUG,
+				   "Invalid group management cipher (0x%x)",
+				   conf->group_mgmt_cipher);
+			return -1;
+		}
+		pos += RSN_SELECTOR_LEN;
+	}
+
+#ifdef CONFIG_RSN_TESTING
+	if (rsn_testing) {
+		/*
+		 * Fill in any defined fields and add extra data to the end of
+		 * the element.
+		 */
+		int pmkid_count_set = pmkid != NULL;
+		if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION)
+			pmkid_count_set = 1;
+		/* PMKID Count */
+		WPA_PUT_LE16(pos, 0);
+		pos += 2;
+		if (conf->ieee80211w == NO_MGMT_FRAME_PROTECTION) {
+			/* Management Group Cipher Suite */
+			RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
+			pos += RSN_SELECTOR_LEN;
+		}
+
+		os_memset(pos, 0x12, 17);
+		pos += 17;
+	}
+#endif /* CONFIG_RSN_TESTING */
+
+	hdr->len = (pos - buf) - 2;
+
+	return pos - buf;
+}
+
+
+int wpa_write_rsnxe(struct wpa_auth_config *conf, u8 *buf, size_t len)
+{
+	u8 *pos = buf;
+	u16 capab = 0;
+	size_t flen;
+
+	if (wpa_key_mgmt_sae(conf->wpa_key_mgmt) &&
+	    (conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
+	     conf->sae_pwe == SAE_PWE_BOTH || conf->sae_pk ||
+	     wpa_key_mgmt_sae_ext_key(conf->wpa_key_mgmt))) {
+		capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
+#ifdef CONFIG_SAE_PK
+		if (conf->sae_pk)
+			capab |= BIT(WLAN_RSNX_CAPAB_SAE_PK);
+#endif /* CONFIG_SAE_PK */
+	}
+
+	if (conf->secure_ltf)
+		capab |= BIT(WLAN_RSNX_CAPAB_SECURE_LTF);
+	if (conf->secure_rtt)
+		capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT);
+	if (conf->prot_range_neg)
+		capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR);
+
+	flen = (capab & 0xff00) ? 2 : 1;
+	if (!capab)
+		return 0; /* no supported extended RSN capabilities */
+	if (len < 2 + flen)
+		return -1;
+	capab |= flen - 1; /* bit 0-3 = Field length (n - 1) */
+
+	*pos++ = WLAN_EID_RSNX;
+	*pos++ = flen;
+	*pos++ = capab & 0x00ff;
+	capab >>= 8;
+	if (capab)
+		*pos++ = capab;
+
+	return pos - buf;
+}
+
+
+static u8 * wpa_write_osen(struct wpa_auth_config *conf, u8 *eid)
+{
+	u8 *len;
+	u16 capab;
+
+	*eid++ = WLAN_EID_VENDOR_SPECIFIC;
+	len = eid++; /* to be filled */
+	WPA_PUT_BE24(eid, OUI_WFA);
+	eid += 3;
+	*eid++ = HS20_OSEN_OUI_TYPE;
+
+	/* Group Data Cipher Suite */
+	RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
+	eid += RSN_SELECTOR_LEN;
+
+	/* Pairwise Cipher Suite Count and List */
+	WPA_PUT_LE16(eid, 1);
+	eid += 2;
+	RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_CCMP);
+	eid += RSN_SELECTOR_LEN;
+
+	/* AKM Suite Count and List */
+	WPA_PUT_LE16(eid, 1);
+	eid += 2;
+	RSN_SELECTOR_PUT(eid, RSN_AUTH_KEY_MGMT_OSEN);
+	eid += RSN_SELECTOR_LEN;
+
+	/* RSN Capabilities */
+	capab = 0;
+	if (conf->wmm_enabled) {
+		/* 4 PTKSA replay counters when using WMM */
+		capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2);
+	}
+	if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+		capab |= WPA_CAPABILITY_MFPC;
+		if (conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED)
+			capab |= WPA_CAPABILITY_MFPR;
+	}
+#ifdef CONFIG_OCV
+	if (conf->ocv)
+		capab |= WPA_CAPABILITY_OCVC;
+#endif /* CONFIG_OCV */
+	WPA_PUT_LE16(eid, capab);
+	eid += 2;
+
+	*len = eid - len - 1;
+
+	return eid;
+}
+
+
+int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth)
+{
+	u8 *pos, buf[128];
+	int res;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (wpa_auth->conf.own_ie_override_len) {
+		wpa_hexdump(MSG_DEBUG, "WPA: Forced own IE(s) for testing",
+			    wpa_auth->conf.own_ie_override,
+			    wpa_auth->conf.own_ie_override_len);
+		os_free(wpa_auth->wpa_ie);
+		wpa_auth->wpa_ie =
+			os_malloc(wpa_auth->conf.own_ie_override_len);
+		if (wpa_auth->wpa_ie == NULL)
+			return -1;
+		os_memcpy(wpa_auth->wpa_ie, wpa_auth->conf.own_ie_override,
+			  wpa_auth->conf.own_ie_override_len);
+		wpa_auth->wpa_ie_len = wpa_auth->conf.own_ie_override_len;
+		return 0;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	pos = buf;
+
+	if (wpa_auth->conf.wpa == WPA_PROTO_OSEN) {
+		pos = wpa_write_osen(&wpa_auth->conf, pos);
+	}
+	if (wpa_auth->conf.wpa & WPA_PROTO_RSN) {
+		res = wpa_write_rsn_ie(&wpa_auth->conf,
+				       pos, buf + sizeof(buf) - pos, NULL);
+		if (res < 0)
+			return res;
+		pos += res;
+		res = wpa_write_rsnxe(&wpa_auth->conf, pos,
+				      buf + sizeof(buf) - pos);
+		if (res < 0)
+			return res;
+		pos += res;
+	}
+#ifdef CONFIG_IEEE80211R_AP
+	if (wpa_key_mgmt_ft(wpa_auth->conf.wpa_key_mgmt)) {
+		res = wpa_write_mdie(&wpa_auth->conf, pos,
+				     buf + sizeof(buf) - pos);
+		if (res < 0)
+			return res;
+		pos += res;
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+	if (wpa_auth->conf.wpa & WPA_PROTO_WPA) {
+		res = wpa_write_wpa_ie(&wpa_auth->conf,
+				       pos, buf + sizeof(buf) - pos);
+		if (res < 0)
+			return res;
+		pos += res;
+	}
+
+	os_free(wpa_auth->wpa_ie);
+	wpa_auth->wpa_ie = os_malloc(pos - buf);
+	if (wpa_auth->wpa_ie == NULL)
+		return -1;
+	os_memcpy(wpa_auth->wpa_ie, buf, pos - buf);
+	wpa_auth->wpa_ie_len = pos - buf;
+
+	return 0;
+}
+
+
+u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len,
+		 const u8 *data2, size_t data2_len)
+{
+	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
+	*pos++ = RSN_SELECTOR_LEN + data_len + data2_len;
+	RSN_SELECTOR_PUT(pos, kde);
+	pos += RSN_SELECTOR_LEN;
+	os_memcpy(pos, data, data_len);
+	pos += data_len;
+	if (data2) {
+		os_memcpy(pos, data2, data2_len);
+		pos += data2_len;
+	}
+	return pos;
+}
+
+
+struct wpa_auth_okc_iter_data {
+	struct rsn_pmksa_cache_entry *pmksa;
+	const u8 *aa;
+	const u8 *spa;
+	const u8 *pmkid;
+};
+
+
+static int wpa_auth_okc_iter(struct wpa_authenticator *a, void *ctx)
+{
+	struct wpa_auth_okc_iter_data *data = ctx;
+	data->pmksa = pmksa_cache_get_okc(a->pmksa, data->aa, data->spa,
+					  data->pmkid);
+	if (data->pmksa)
+		return 1;
+	return 0;
+}
+
+
+enum wpa_validate_result
+wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
+		    struct wpa_state_machine *sm, int freq,
+		    const u8 *wpa_ie, size_t wpa_ie_len,
+		    const u8 *rsnxe, size_t rsnxe_len,
+		    const u8 *mdie, size_t mdie_len,
+		    const u8 *owe_dh, size_t owe_dh_len)
+{
+	struct wpa_auth_config *conf = &wpa_auth->conf;
+	struct wpa_ie_data data;
+	int ciphers, key_mgmt, res, version;
+	u32 selector;
+	size_t i;
+	const u8 *pmkid = NULL;
+
+	if (wpa_auth == NULL || sm == NULL)
+		return WPA_NOT_ENABLED;
+
+	if (wpa_ie == NULL || wpa_ie_len < 1)
+		return WPA_INVALID_IE;
+
+	if (wpa_ie[0] == WLAN_EID_RSN)
+		version = WPA_PROTO_RSN;
+	else
+		version = WPA_PROTO_WPA;
+
+	if (!(wpa_auth->conf.wpa & version)) {
+		wpa_printf(MSG_DEBUG, "Invalid WPA proto (%d) from " MACSTR,
+			   version, MAC2STR(sm->addr));
+		return WPA_INVALID_PROTO;
+	}
+
+	if (version == WPA_PROTO_RSN) {
+		res = wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, &data);
+		if (!data.has_pairwise)
+			data.pairwise_cipher = wpa_default_rsn_cipher(freq);
+		if (!data.has_group)
+			data.group_cipher = wpa_default_rsn_cipher(freq);
+
+		if (wpa_key_mgmt_ft(data.key_mgmt) && !mdie &&
+		    !wpa_key_mgmt_only_ft(data.key_mgmt)) {
+			/* Workaround for some HP and Epson printers that seem
+			 * to incorrectly copy the FT-PSK + WPA-PSK AKMs from AP
+			 * advertised RSNE to Association Request frame. */
+			wpa_printf(MSG_DEBUG,
+				   "RSN: FT set in RSNE AKM but MDE is missing from "
+				   MACSTR
+				   " - ignore FT AKM(s) because there's also a non-FT AKM",
+				   MAC2STR(sm->addr));
+			data.key_mgmt &= ~WPA_KEY_MGMT_FT;
+		}
+
+		selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
+		if (0) {
+		}
+		else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+			selector = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192;
+		else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B)
+			selector = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B;
+#ifdef CONFIG_FILS
+#ifdef CONFIG_IEEE80211R_AP
+		else if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384)
+			selector = RSN_AUTH_KEY_MGMT_FT_FILS_SHA384;
+		else if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256)
+			selector = RSN_AUTH_KEY_MGMT_FT_FILS_SHA256;
+#endif /* CONFIG_IEEE80211R_AP */
+		else if (data.key_mgmt & WPA_KEY_MGMT_FILS_SHA384)
+			selector = RSN_AUTH_KEY_MGMT_FILS_SHA384;
+		else if (data.key_mgmt & WPA_KEY_MGMT_FILS_SHA256)
+			selector = RSN_AUTH_KEY_MGMT_FILS_SHA256;
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_IEEE80211R_AP
+#ifdef CONFIG_SHA384
+		else if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384)
+			selector = RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384;
+#endif /* CONFIG_SHA384 */
+		else if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
+			selector = RSN_AUTH_KEY_MGMT_FT_802_1X;
+		else if (data.key_mgmt & WPA_KEY_MGMT_FT_PSK)
+			selector = RSN_AUTH_KEY_MGMT_FT_PSK;
+#endif /* CONFIG_IEEE80211R_AP */
+		else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256)
+			selector = RSN_AUTH_KEY_MGMT_802_1X_SHA256;
+		else if (data.key_mgmt & WPA_KEY_MGMT_PSK_SHA256)
+			selector = RSN_AUTH_KEY_MGMT_PSK_SHA256;
+#ifdef CONFIG_SAE
+		else if (data.key_mgmt & WPA_KEY_MGMT_SAE)
+			selector = RSN_AUTH_KEY_MGMT_SAE;
+		else if (data.key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY)
+			selector = RSN_AUTH_KEY_MGMT_SAE_EXT_KEY;
+		else if (data.key_mgmt & WPA_KEY_MGMT_FT_SAE)
+			selector = RSN_AUTH_KEY_MGMT_FT_SAE;
+		else if (data.key_mgmt & WPA_KEY_MGMT_FT_SAE_EXT_KEY)
+			selector = RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY;
+#endif /* CONFIG_SAE */
+		else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X)
+			selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
+		else if (data.key_mgmt & WPA_KEY_MGMT_PSK)
+			selector = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
+#ifdef CONFIG_OWE
+		else if (data.key_mgmt & WPA_KEY_MGMT_OWE)
+			selector = RSN_AUTH_KEY_MGMT_OWE;
+#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP
+		else if (data.key_mgmt & WPA_KEY_MGMT_DPP)
+			selector = RSN_AUTH_KEY_MGMT_DPP;
+#endif /* CONFIG_DPP */
+#ifdef CONFIG_HS20
+		else if (data.key_mgmt & WPA_KEY_MGMT_OSEN)
+			selector = RSN_AUTH_KEY_MGMT_OSEN;
+#endif /* CONFIG_HS20 */
+		wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector;
+
+		selector = wpa_cipher_to_suite(WPA_PROTO_RSN,
+					       data.pairwise_cipher);
+		if (!selector)
+			selector = RSN_CIPHER_SUITE_CCMP;
+		wpa_auth->dot11RSNAPairwiseCipherSelected = selector;
+
+		selector = wpa_cipher_to_suite(WPA_PROTO_RSN,
+					       data.group_cipher);
+		if (!selector)
+			selector = RSN_CIPHER_SUITE_CCMP;
+		wpa_auth->dot11RSNAGroupCipherSelected = selector;
+	} else {
+		res = wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, &data);
+
+		selector = WPA_AUTH_KEY_MGMT_UNSPEC_802_1X;
+		if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X)
+			selector = WPA_AUTH_KEY_MGMT_UNSPEC_802_1X;
+		else if (data.key_mgmt & WPA_KEY_MGMT_PSK)
+			selector = WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X;
+		wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector;
+
+		selector = wpa_cipher_to_suite(WPA_PROTO_WPA,
+					       data.pairwise_cipher);
+		if (!selector)
+			selector = RSN_CIPHER_SUITE_TKIP;
+		wpa_auth->dot11RSNAPairwiseCipherSelected = selector;
+
+		selector = wpa_cipher_to_suite(WPA_PROTO_WPA,
+					       data.group_cipher);
+		if (!selector)
+			selector = WPA_CIPHER_SUITE_TKIP;
+		wpa_auth->dot11RSNAGroupCipherSelected = selector;
+	}
+	if (res) {
+		wpa_printf(MSG_DEBUG, "Failed to parse WPA/RSN IE from "
+			   MACSTR " (res=%d)", MAC2STR(sm->addr), res);
+		wpa_hexdump(MSG_DEBUG, "WPA/RSN IE", wpa_ie, wpa_ie_len);
+		return WPA_INVALID_IE;
+	}
+
+	if (data.group_cipher != wpa_auth->conf.wpa_group) {
+		wpa_printf(MSG_DEBUG, "Invalid WPA group cipher (0x%x) from "
+			   MACSTR, data.group_cipher, MAC2STR(sm->addr));
+		return WPA_INVALID_GROUP;
+	}
+
+	key_mgmt = data.key_mgmt & wpa_auth->conf.wpa_key_mgmt;
+	if (!key_mgmt) {
+		wpa_printf(MSG_DEBUG, "Invalid WPA key mgmt (0x%x) from "
+			   MACSTR, data.key_mgmt, MAC2STR(sm->addr));
+		return WPA_INVALID_AKMP;
+	}
+	if (0) {
+	}
+	else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
+	else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B;
+#ifdef CONFIG_FILS
+#ifdef CONFIG_IEEE80211R_AP
+	else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA384;
+	else if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA256;
+#endif /* CONFIG_IEEE80211R_AP */
+	else if (key_mgmt & WPA_KEY_MGMT_FILS_SHA384)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_FILS_SHA384;
+	else if (key_mgmt & WPA_KEY_MGMT_FILS_SHA256)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_FILS_SHA256;
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_IEEE80211R_AP
+#ifdef CONFIG_SHA384
+	else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X_SHA384;
+#endif /* CONFIG_SHA384 */
+	else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
+	else if (key_mgmt & WPA_KEY_MGMT_FT_PSK)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_PSK;
+#endif /* CONFIG_IEEE80211R_AP */
+	else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA256;
+	else if (key_mgmt & WPA_KEY_MGMT_PSK_SHA256)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK_SHA256;
+#ifdef CONFIG_SAE
+	else if (key_mgmt & WPA_KEY_MGMT_SAE)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_SAE;
+	else if (key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_SAE_EXT_KEY;
+	else if (key_mgmt & WPA_KEY_MGMT_FT_SAE)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_SAE;
+	else if (key_mgmt & WPA_KEY_MGMT_FT_SAE_EXT_KEY)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_SAE_EXT_KEY;
+#endif /* CONFIG_SAE */
+	else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+#ifdef CONFIG_OWE
+	else if (key_mgmt & WPA_KEY_MGMT_OWE)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_OWE;
+#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP
+	else if (key_mgmt & WPA_KEY_MGMT_DPP)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_DPP;
+#endif /* CONFIG_DPP */
+#ifdef CONFIG_HS20
+	else if (key_mgmt & WPA_KEY_MGMT_OSEN)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_OSEN;
+#endif /* CONFIG_HS20 */
+	else
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
+
+	if (version == WPA_PROTO_RSN)
+		ciphers = data.pairwise_cipher & wpa_auth->conf.rsn_pairwise;
+	else
+		ciphers = data.pairwise_cipher & wpa_auth->conf.wpa_pairwise;
+	if (!ciphers) {
+		wpa_printf(MSG_DEBUG, "Invalid %s pairwise cipher (0x%x) "
+			   "from " MACSTR,
+			   version == WPA_PROTO_RSN ? "RSN" : "WPA",
+			   data.pairwise_cipher, MAC2STR(sm->addr));
+		return WPA_INVALID_PAIRWISE;
+	}
+
+	if (wpa_auth->conf.ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED) {
+		if (!(data.capabilities & WPA_CAPABILITY_MFPC)) {
+			wpa_printf(MSG_DEBUG, "Management frame protection "
+				   "required, but client did not enable it");
+			return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
+		}
+
+		if (data.mgmt_group_cipher != wpa_auth->conf.group_mgmt_cipher)
+		{
+			wpa_printf(MSG_DEBUG, "Unsupported management group "
+				   "cipher %d", data.mgmt_group_cipher);
+			return WPA_INVALID_MGMT_GROUP_CIPHER;
+		}
+	}
+
+#ifdef CONFIG_SAE
+	if (wpa_auth->conf.ieee80211w == MGMT_FRAME_PROTECTION_OPTIONAL &&
+	    wpa_auth->conf.sae_require_mfp &&
+	    wpa_key_mgmt_sae(sm->wpa_key_mgmt) &&
+	    !(data.capabilities & WPA_CAPABILITY_MFPC)) {
+		wpa_printf(MSG_DEBUG,
+			   "Management frame protection required with SAE, but client did not enable it");
+		return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
+	}
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_OCV
+	if (wpa_auth->conf.ocv && (data.capabilities & WPA_CAPABILITY_OCVC) &&
+	    !(data.capabilities & WPA_CAPABILITY_MFPC)) {
+		/* Some legacy MFP incapable STAs wrongly copy OCVC bit from
+		 * AP RSN capabilities. To improve interoperability with such
+		 * legacy STAs allow connection without enabling OCV when the
+		 * workaround mode (ocv=2) is enabled.
+		 */
+		if (wpa_auth->conf.ocv == 2) {
+			wpa_printf(MSG_DEBUG,
+				   "Allow connecting MFP incapable and OCV capable STA without enabling OCV");
+			wpa_auth_set_ocv(sm, 0);
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "Management frame protection required with OCV, but client did not enable it");
+			return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
+		}
+	} else {
+		wpa_auth_set_ocv(sm, (data.capabilities & WPA_CAPABILITY_OCVC) ?
+				 wpa_auth->conf.ocv : 0);
+	}
+#endif /* CONFIG_OCV */
+
+	if (wpa_auth->conf.ieee80211w == NO_MGMT_FRAME_PROTECTION ||
+	    !(data.capabilities & WPA_CAPABILITY_MFPC))
+		sm->mgmt_frame_prot = 0;
+	else
+		sm->mgmt_frame_prot = 1;
+	sm->mfpr = !!(data.capabilities & WPA_CAPABILITY_MFPR);
+
+	if (sm->mgmt_frame_prot && (ciphers & WPA_CIPHER_TKIP)) {
+		    wpa_printf(MSG_DEBUG,
+			       "Management frame protection cannot use TKIP");
+		    return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
+	}
+
+#ifdef CONFIG_IEEE80211R_AP
+	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+		if (mdie == NULL || mdie_len < MOBILITY_DOMAIN_ID_LEN + 1) {
+			wpa_printf(MSG_DEBUG, "RSN: Trying to use FT, but "
+				   "MDIE not included");
+			return WPA_INVALID_MDIE;
+		}
+		if (os_memcmp(mdie, wpa_auth->conf.mobility_domain,
+			      MOBILITY_DOMAIN_ID_LEN) != 0) {
+			wpa_hexdump(MSG_DEBUG, "RSN: Attempted to use unknown "
+				    "MDIE", mdie, MOBILITY_DOMAIN_ID_LEN);
+			return WPA_INVALID_MDIE;
+		}
+	} else if (mdie != NULL) {
+		wpa_printf(MSG_DEBUG,
+			   "RSN: Trying to use non-FT AKM suite, but MDIE included");
+		return WPA_INVALID_AKMP;
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+
+#ifdef CONFIG_OWE
+	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE && !owe_dh) {
+		wpa_printf(MSG_DEBUG,
+			   "OWE: No Diffie-Hellman Parameter element");
+		return WPA_INVALID_AKMP;
+	}
+#endif /* CONFIG_OWE */
+
+#ifdef CONFIG_DPP2
+	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP &&
+	    ((conf->dpp_pfs == 1 && !owe_dh) ||
+	     (conf->dpp_pfs == 2 && owe_dh))) {
+		wpa_printf(MSG_DEBUG, "DPP: PFS %s",
+			   conf->dpp_pfs == 1 ? "required" : "not allowed");
+		return WPA_DENIED_OTHER_REASON;
+	}
+#endif /* CONFIG_DPP2 */
+
+	sm->pairwise = wpa_pick_pairwise_cipher(ciphers, 0);
+	if (sm->pairwise < 0)
+		return WPA_INVALID_PAIRWISE;
+
+	/* TODO: clear WPA/WPA2 state if STA changes from one to another */
+	if (wpa_ie[0] == WLAN_EID_RSN)
+		sm->wpa = WPA_VERSION_WPA2;
+	else
+		sm->wpa = WPA_VERSION_WPA;
+
+#if defined(CONFIG_IEEE80211R_AP) && defined(CONFIG_FILS)
+	if ((sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA256 ||
+	     sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA384) &&
+	    (sm->auth_alg == WLAN_AUTH_FILS_SK ||
+	     sm->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+	     sm->auth_alg == WLAN_AUTH_FILS_PK) &&
+	    (data.num_pmkid != 1 || !data.pmkid || !sm->pmk_r1_name_valid ||
+	     os_memcmp_const(data.pmkid, sm->pmk_r1_name,
+			     WPA_PMK_NAME_LEN) != 0)) {
+		wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+				 "No PMKR1Name match for FILS+FT");
+		return WPA_INVALID_PMKID;
+	}
+#endif /* CONFIG_IEEE80211R_AP && CONFIG_FILS */
+
+	sm->pmksa = NULL;
+	for (i = 0; i < data.num_pmkid; i++) {
+		wpa_hexdump(MSG_DEBUG, "RSN IE: STA PMKID",
+			    &data.pmkid[i * PMKID_LEN], PMKID_LEN);
+		sm->pmksa = pmksa_cache_auth_get(wpa_auth->pmksa, sm->addr,
+						 &data.pmkid[i * PMKID_LEN]);
+		if (sm->pmksa) {
+			pmkid = sm->pmksa->pmkid;
+			break;
+		}
+	}
+	for (i = 0; sm->pmksa == NULL && wpa_auth->conf.okc &&
+		     i < data.num_pmkid; i++) {
+		struct wpa_auth_okc_iter_data idata;
+		idata.pmksa = NULL;
+		idata.aa = wpa_auth->addr;
+		idata.spa = sm->addr;
+		idata.pmkid = &data.pmkid[i * PMKID_LEN];
+		wpa_auth_for_each_auth(wpa_auth, wpa_auth_okc_iter, &idata);
+		if (idata.pmksa) {
+			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+					 "OKC match for PMKID");
+			sm->pmksa = pmksa_cache_add_okc(wpa_auth->pmksa,
+							idata.pmksa,
+							wpa_auth->addr,
+							idata.pmkid);
+			pmkid = idata.pmkid;
+			break;
+		}
+	}
+	if (sm->pmksa && pmkid) {
+		struct vlan_description *vlan;
+
+		vlan = sm->pmksa->vlan_desc;
+		wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+				 "PMKID found from PMKSA cache eap_type=%d vlan=%d%s",
+				 sm->pmksa->eap_type_authsrv,
+				 vlan ? vlan->untagged : 0,
+				 (vlan && vlan->tagged[0]) ? "+" : "");
+		os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmkid, PMKID_LEN);
+	}
+
+#ifdef CONFIG_SAE
+	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_SAE && data.num_pmkid &&
+	    !sm->pmksa) {
+		wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+				 "No PMKSA cache entry found for SAE");
+		return WPA_INVALID_PMKID;
+	}
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_DPP
+	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && !sm->pmksa) {
+		wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+				 "No PMKSA cache entry found for DPP");
+		return WPA_INVALID_PMKID;
+	}
+#endif /* CONFIG_DPP */
+
+	if (conf->extended_key_id && sm->wpa == WPA_VERSION_WPA2 &&
+	    sm->pairwise != WPA_CIPHER_TKIP &&
+	    (data.capabilities & WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST)) {
+		sm->use_ext_key_id = true;
+		if (conf->extended_key_id == 2 &&
+		    !wpa_key_mgmt_ft(sm->wpa_key_mgmt) &&
+		    !wpa_key_mgmt_fils(sm->wpa_key_mgmt))
+			sm->keyidx_active = 1;
+		else
+			sm->keyidx_active = 0;
+		wpa_printf(MSG_DEBUG,
+			   "RSN: Extended Key ID supported (start with %d)",
+			   sm->keyidx_active);
+	} else {
+		sm->use_ext_key_id = false;
+	}
+
+	if (sm->wpa_ie == NULL || sm->wpa_ie_len < wpa_ie_len) {
+		os_free(sm->wpa_ie);
+		sm->wpa_ie = os_malloc(wpa_ie_len);
+		if (sm->wpa_ie == NULL)
+			return WPA_ALLOC_FAIL;
+	}
+	os_memcpy(sm->wpa_ie, wpa_ie, wpa_ie_len);
+	sm->wpa_ie_len = wpa_ie_len;
+
+	if (rsnxe && rsnxe_len) {
+		if (!sm->rsnxe || sm->rsnxe_len < rsnxe_len) {
+			os_free(sm->rsnxe);
+			sm->rsnxe = os_malloc(rsnxe_len);
+			if (!sm->rsnxe)
+				return WPA_ALLOC_FAIL;
+		}
+		os_memcpy(sm->rsnxe, rsnxe, rsnxe_len);
+		sm->rsnxe_len = rsnxe_len;
+	} else {
+		os_free(sm->rsnxe);
+		sm->rsnxe = NULL;
+		sm->rsnxe_len = 0;
+	}
+
+	return WPA_IE_OK;
+}
+
+
+#ifdef CONFIG_HS20
+int wpa_validate_osen(struct wpa_authenticator *wpa_auth,
+		      struct wpa_state_machine *sm,
+		      const u8 *osen_ie, size_t osen_ie_len)
+{
+	if (wpa_auth == NULL || sm == NULL)
+		return -1;
+
+	/* TODO: parse OSEN element */
+	sm->wpa_key_mgmt = WPA_KEY_MGMT_OSEN;
+	sm->mgmt_frame_prot = 1;
+	sm->pairwise = WPA_CIPHER_CCMP;
+	sm->wpa = WPA_VERSION_WPA2;
+
+	if (sm->wpa_ie == NULL || sm->wpa_ie_len < osen_ie_len) {
+		os_free(sm->wpa_ie);
+		sm->wpa_ie = os_malloc(osen_ie_len);
+		if (sm->wpa_ie == NULL)
+			return -1;
+	}
+
+	os_memcpy(sm->wpa_ie, osen_ie, osen_ie_len);
+	sm->wpa_ie_len = osen_ie_len;
+
+	return 0;
+}
+
+#endif /* CONFIG_HS20 */
+
+
+int wpa_auth_uses_mfp(struct wpa_state_machine *sm)
+{
+	return sm ? sm->mgmt_frame_prot : 0;
+}
+
+
+#ifdef CONFIG_OCV
+
+void wpa_auth_set_ocv(struct wpa_state_machine *sm, int ocv)
+{
+	if (sm)
+		sm->ocv_enabled = ocv;
+}
+
+
+int wpa_auth_uses_ocv(struct wpa_state_machine *sm)
+{
+	return sm ? sm->ocv_enabled : 0;
+}
+
+#endif /* CONFIG_OCV */
+
+
+#ifdef CONFIG_OWE
+u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm,
+				   u8 *pos, size_t max_len,
+				   const u8 *req_ies, size_t req_ies_len)
+{
+	int res;
+	struct wpa_auth_config *conf;
+
+	if (!sm)
+		return pos;
+	conf = &sm->wpa_auth->conf;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (conf->own_ie_override_len) {
+		if (max_len < conf->own_ie_override_len)
+			return NULL;
+		wpa_hexdump(MSG_DEBUG, "WPA: Forced own IE(s) for testing",
+			    conf->own_ie_override, conf->own_ie_override_len);
+		os_memcpy(pos, conf->own_ie_override,
+			  conf->own_ie_override_len);
+		return pos + conf->own_ie_override_len;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	res = wpa_write_rsn_ie(conf, pos, max_len,
+			       sm->pmksa ? sm->pmksa->pmkid : NULL);
+	if (res < 0)
+		return pos;
+	return pos + res;
+}
+#endif /* CONFIG_OWE */
+
+
+#ifdef CONFIG_FILS
+
+u8 * wpa_auth_write_assoc_resp_fils(struct wpa_state_machine *sm,
+				    u8 *pos, size_t max_len,
+				    const u8 *req_ies, size_t req_ies_len)
+{
+	int res;
+
+	if (!sm ||
+	    sm->wpa_key_mgmt & (WPA_KEY_MGMT_FT_FILS_SHA256 |
+				WPA_KEY_MGMT_FT_FILS_SHA384))
+		return pos;
+
+	res = wpa_write_rsn_ie(&sm->wpa_auth->conf, pos, max_len, NULL);
+	if (res < 0)
+		return pos;
+	return pos + res;
+}
+
+
+bool wpa_auth_write_fd_rsn_info(struct wpa_authenticator *wpa_auth,
+				u8 *fd_rsn_info)
+{
+	struct wpa_auth_config *conf;
+	u32 selectors = 0;
+	u8 *pos = fd_rsn_info;
+	int i, res;
+	u32 cipher, suite, selector, mask;
+	u8 tmp[10 * RSN_SELECTOR_LEN];
+
+	if (!wpa_auth)
+		return false;
+	conf = &wpa_auth->conf;
+
+	if (!(conf->wpa & WPA_PROTO_RSN))
+		return false;
+
+	/* RSN Capability (B0..B15) */
+	WPA_PUT_LE16(pos, wpa_own_rsn_capab(conf));
+	pos += 2;
+
+	/* Group Data Cipher Suite Selector (B16..B21) */
+	suite = wpa_cipher_to_suite(WPA_PROTO_RSN, conf->wpa_group);
+	if (suite == RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED)
+		cipher = 63; /* No cipher suite selected */
+	else if ((suite >> 8) == 0x000fac && ((suite & 0xff) <= 13))
+		cipher = suite & 0xff;
+	else
+		cipher = 62; /* vendor specific */
+	selectors |= cipher;
+
+	/* Group Management Cipher Suite Selector (B22..B27) */
+	cipher = 63; /* Default to no cipher suite selected */
+	if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+		switch (conf->group_mgmt_cipher) {
+		case WPA_CIPHER_AES_128_CMAC:
+			cipher = RSN_CIPHER_SUITE_AES_128_CMAC & 0xff;
+			break;
+		case WPA_CIPHER_BIP_GMAC_128:
+			cipher = RSN_CIPHER_SUITE_BIP_GMAC_128 & 0xff;
+			break;
+		case WPA_CIPHER_BIP_GMAC_256:
+			cipher = RSN_CIPHER_SUITE_BIP_GMAC_256 & 0xff;
+			break;
+		case WPA_CIPHER_BIP_CMAC_256:
+			cipher = RSN_CIPHER_SUITE_BIP_CMAC_256 & 0xff;
+			break;
+		}
+	}
+	selectors |= cipher << 6;
+
+	/* Pairwise Cipher Suite Selector (B28..B33) */
+	cipher = 63; /* Default to no cipher suite selected */
+	res = rsn_cipher_put_suites(tmp, conf->rsn_pairwise);
+	if (res == 1 && tmp[0] == 0x00 && tmp[1] == 0x0f && tmp[2] == 0xac &&
+	    tmp[3] <= 13)
+		cipher = tmp[3];
+	selectors |= cipher << 12;
+
+	/* AKM Suite Selector (B34..B39) */
+	selector = 0; /* default to AKM from RSNE in Beacon/Probe Response */
+	mask = WPA_KEY_MGMT_FILS_SHA256 | WPA_KEY_MGMT_FILS_SHA384 |
+		WPA_KEY_MGMT_FT_FILS_SHA384;
+	if ((conf->wpa_key_mgmt & mask) && (conf->wpa_key_mgmt & ~mask) == 0) {
+		suite = conf->wpa_key_mgmt & mask;
+		if (suite == WPA_KEY_MGMT_FILS_SHA256)
+			selector = 1; /* 00-0f-ac:14 */
+		else if (suite == WPA_KEY_MGMT_FILS_SHA384)
+			selector = 2; /* 00-0f-ac:15 */
+		else if (suite == (WPA_KEY_MGMT_FILS_SHA256 |
+				   WPA_KEY_MGMT_FILS_SHA384))
+			selector = 3; /* 00-0f-ac:14 or 00-0f-ac:15 */
+		else if (suite == WPA_KEY_MGMT_FT_FILS_SHA384)
+			selector = 4; /* 00-0f-ac:17 */
+	}
+	selectors |= selector << 18;
+
+	for (i = 0; i < 3; i++) {
+		*pos++ = selectors & 0xff;
+		selectors >>= 8;
+	}
+
+	return true;
+}
+
+#endif /* CONFIG_FILS */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_ie.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_ie.h
new file mode 100644
index 0000000..dd44b9e
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_ie.h
@@ -0,0 +1,16 @@
+/*
+ * hostapd - WPA/RSN IE and KDE definitions
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPA_AUTH_IE_H
+#define WPA_AUTH_IE_H
+
+u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len,
+		 const u8 *data2, size_t data2_len);
+int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth);
+
+#endif /* WPA_AUTH_IE_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_kay.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_kay.c
new file mode 100644
index 0000000..625f405
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_kay.c
@@ -0,0 +1,496 @@
+/*
+ * IEEE 802.1X-2010 KaY Interface
+ * Copyright (c) 2019, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "pae/ieee802_1x_key.h"
+#include "pae/ieee802_1x_kay.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "wpa_auth_kay.h"
+#include "ieee802_1x.h"
+
+
+#define DEFAULT_KEY_LEN		16
+/* secure Connectivity Association Key Name (CKN) */
+#define DEFAULT_CKN_LEN		16
+
+
+static int hapd_macsec_init(void *priv, struct macsec_init_params *params)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->macsec_init)
+		return -1;
+	return hapd->driver->macsec_init(hapd->drv_priv, params);
+}
+
+
+static int hapd_macsec_deinit(void *priv)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->macsec_deinit)
+		return -1;
+	return hapd->driver->macsec_deinit(hapd->drv_priv);
+}
+
+
+static int hapd_macsec_get_capability(void *priv, enum macsec_cap *cap)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->macsec_get_capability)
+		return -1;
+	return hapd->driver->macsec_get_capability(hapd->drv_priv, cap);
+}
+
+
+static int hapd_enable_protect_frames(void *priv, bool enabled)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->enable_protect_frames)
+		return -1;
+	return hapd->driver->enable_protect_frames(hapd->drv_priv, enabled);
+}
+
+
+static int hapd_enable_encrypt(void *priv, bool enabled)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->enable_encrypt)
+		return -1;
+	return hapd->driver->enable_encrypt(hapd->drv_priv, enabled);
+}
+
+
+static int hapd_set_replay_protect(void *priv, bool enabled, u32 window)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->set_replay_protect)
+		return -1;
+	return hapd->driver->set_replay_protect(hapd->drv_priv, enabled,
+						 window);
+}
+
+
+static int hapd_set_current_cipher_suite(void *priv, u64 cs)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->set_current_cipher_suite)
+		return -1;
+	return hapd->driver->set_current_cipher_suite(hapd->drv_priv, cs);
+}
+
+
+static int hapd_enable_controlled_port(void *priv, bool enabled)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->enable_controlled_port)
+		return -1;
+	return hapd->driver->enable_controlled_port(hapd->drv_priv, enabled);
+}
+
+
+static int hapd_get_receive_lowest_pn(void *priv, struct receive_sa *sa)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->get_receive_lowest_pn)
+		return -1;
+	return hapd->driver->get_receive_lowest_pn(hapd->drv_priv, sa);
+}
+
+
+static int hapd_get_transmit_next_pn(void *priv, struct transmit_sa *sa)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->get_transmit_next_pn)
+		return -1;
+	return hapd->driver->get_transmit_next_pn(hapd->drv_priv, sa);
+}
+
+
+static int hapd_set_transmit_next_pn(void *priv, struct transmit_sa *sa)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->set_transmit_next_pn)
+		return -1;
+	return hapd->driver->set_transmit_next_pn(hapd->drv_priv, sa);
+}
+
+
+static unsigned int conf_offset_val(enum confidentiality_offset co)
+{
+	switch (co) {
+	case CONFIDENTIALITY_OFFSET_30:
+		return 30;
+	case CONFIDENTIALITY_OFFSET_50:
+		return 50;
+	default:
+		return 0;
+	}
+}
+
+
+static int hapd_create_receive_sc(void *priv, struct receive_sc *sc,
+				  enum validate_frames vf,
+				  enum confidentiality_offset co)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->create_receive_sc)
+		return -1;
+	return hapd->driver->create_receive_sc(hapd->drv_priv, sc,
+					       conf_offset_val(co), vf);
+}
+
+
+static int hapd_delete_receive_sc(void *priv, struct receive_sc *sc)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->delete_receive_sc)
+		return -1;
+	return hapd->driver->delete_receive_sc(hapd->drv_priv, sc);
+}
+
+
+static int hapd_create_receive_sa(void *priv, struct receive_sa *sa)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->create_receive_sa)
+		return -1;
+	return hapd->driver->create_receive_sa(hapd->drv_priv, sa);
+}
+
+
+static int hapd_delete_receive_sa(void *priv, struct receive_sa *sa)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->delete_receive_sa)
+		return -1;
+	return hapd->driver->delete_receive_sa(hapd->drv_priv, sa);
+}
+
+
+static int hapd_enable_receive_sa(void *priv, struct receive_sa *sa)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->enable_receive_sa)
+		return -1;
+	return hapd->driver->enable_receive_sa(hapd->drv_priv, sa);
+}
+
+
+static int hapd_disable_receive_sa(void *priv, struct receive_sa *sa)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->disable_receive_sa)
+		return -1;
+	return hapd->driver->disable_receive_sa(hapd->drv_priv, sa);
+}
+
+
+static int
+hapd_create_transmit_sc(void *priv, struct transmit_sc *sc,
+			enum confidentiality_offset co)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->create_transmit_sc)
+		return -1;
+	return hapd->driver->create_transmit_sc(hapd->drv_priv, sc,
+						conf_offset_val(co));
+}
+
+
+static int hapd_delete_transmit_sc(void *priv, struct transmit_sc *sc)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->delete_transmit_sc)
+		return -1;
+	return hapd->driver->delete_transmit_sc(hapd->drv_priv, sc);
+}
+
+
+static int hapd_create_transmit_sa(void *priv, struct transmit_sa *sa)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->create_transmit_sa)
+		return -1;
+	return hapd->driver->create_transmit_sa(hapd->drv_priv, sa);
+}
+
+
+static int hapd_delete_transmit_sa(void *priv, struct transmit_sa *sa)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->delete_transmit_sa)
+		return -1;
+	return hapd->driver->delete_transmit_sa(hapd->drv_priv, sa);
+}
+
+
+static int hapd_enable_transmit_sa(void *priv, struct transmit_sa *sa)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->enable_transmit_sa)
+		return -1;
+	return hapd->driver->enable_transmit_sa(hapd->drv_priv, sa);
+}
+
+
+static int hapd_disable_transmit_sa(void *priv, struct transmit_sa *sa)
+{
+	struct hostapd_data *hapd = priv;
+
+	if (!hapd->driver->disable_transmit_sa)
+		return -1;
+	return hapd->driver->disable_transmit_sa(hapd->drv_priv, sa);
+}
+
+
+int ieee802_1x_alloc_kay_sm_hapd(struct hostapd_data *hapd,
+				 struct sta_info *sta)
+{
+	struct ieee802_1x_kay_ctx *kay_ctx;
+	struct ieee802_1x_kay *res = NULL;
+	enum macsec_policy policy;
+
+	ieee802_1x_dealloc_kay_sm_hapd(hapd);
+
+	if (!hapd->conf || hapd->conf->macsec_policy == 0)
+		return 0;
+
+	if (hapd->conf->macsec_policy == 1) {
+		if (hapd->conf->macsec_integ_only == 1)
+			policy = SHOULD_SECURE;
+		else
+			policy = SHOULD_ENCRYPT;
+	} else {
+		policy = DO_NOT_SECURE;
+	}
+
+	wpa_printf(MSG_DEBUG, "%s: if_name=%s", __func__, hapd->conf->iface);
+	kay_ctx = os_zalloc(sizeof(*kay_ctx));
+	if (!kay_ctx)
+		return -1;
+
+	kay_ctx->ctx = hapd;
+
+	kay_ctx->macsec_init = hapd_macsec_init;
+	kay_ctx->macsec_deinit = hapd_macsec_deinit;
+	kay_ctx->macsec_get_capability = hapd_macsec_get_capability;
+	kay_ctx->enable_protect_frames = hapd_enable_protect_frames;
+	kay_ctx->enable_encrypt = hapd_enable_encrypt;
+	kay_ctx->set_replay_protect = hapd_set_replay_protect;
+	kay_ctx->set_current_cipher_suite = hapd_set_current_cipher_suite;
+	kay_ctx->enable_controlled_port = hapd_enable_controlled_port;
+	kay_ctx->get_receive_lowest_pn = hapd_get_receive_lowest_pn;
+	kay_ctx->get_transmit_next_pn = hapd_get_transmit_next_pn;
+	kay_ctx->set_transmit_next_pn = hapd_set_transmit_next_pn;
+	kay_ctx->create_receive_sc = hapd_create_receive_sc;
+	kay_ctx->delete_receive_sc = hapd_delete_receive_sc;
+	kay_ctx->create_receive_sa = hapd_create_receive_sa;
+	kay_ctx->delete_receive_sa = hapd_delete_receive_sa;
+	kay_ctx->enable_receive_sa = hapd_enable_receive_sa;
+	kay_ctx->disable_receive_sa = hapd_disable_receive_sa;
+	kay_ctx->create_transmit_sc = hapd_create_transmit_sc;
+	kay_ctx->delete_transmit_sc = hapd_delete_transmit_sc;
+	kay_ctx->create_transmit_sa = hapd_create_transmit_sa;
+	kay_ctx->delete_transmit_sa = hapd_delete_transmit_sa;
+	kay_ctx->enable_transmit_sa = hapd_enable_transmit_sa;
+	kay_ctx->disable_transmit_sa = hapd_disable_transmit_sa;
+
+	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,
+				  hapd->conf->iface,
+				  hapd->own_addr);
+	/* ieee802_1x_kay_init() frees kay_ctx on failure */
+	if (!res)
+		return -1;
+
+	hapd->kay = res;
+
+	return 0;
+}
+
+
+void ieee802_1x_dealloc_kay_sm_hapd(struct hostapd_data *hapd)
+{
+	if (!hapd->kay)
+		return;
+
+	ieee802_1x_kay_deinit(hapd->kay);
+	hapd->kay = NULL;
+}
+
+
+static int ieee802_1x_auth_get_msk(struct hostapd_data *hapd,
+				   struct sta_info *sta, u8 *msk, size_t *len)
+{
+	const u8 *key;
+	size_t keylen;
+
+	if (!sta->eapol_sm)
+		return -1;
+
+	key = ieee802_1x_get_key(sta->eapol_sm, &keylen);
+	if (key == NULL) {
+		wpa_printf(MSG_DEBUG,
+			   "MACsec: Failed to get MSK from EAPOL state machines");
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG, "MACsec: Successfully fetched key (len=%lu)",
+		   (unsigned long) keylen);
+	wpa_hexdump_key(MSG_DEBUG, "MSK: ", key, keylen);
+
+	if (keylen > *len)
+		keylen = *len;
+	os_memcpy(msk, key, keylen);
+	*len = keylen;
+
+	return 0;
+}
+
+
+void * ieee802_1x_notify_create_actor_hapd(struct hostapd_data *hapd,
+					   struct sta_info *sta)
+{
+	const u8 *sid;
+	size_t sid_len;
+	struct mka_key_name *ckn;
+	struct mka_key *cak;
+	struct mka_key *msk;
+	void *res = NULL;
+
+	if (!hapd->kay || hapd->kay->policy == DO_NOT_SECURE)
+		return NULL;
+
+	wpa_printf(MSG_DEBUG,
+		   "IEEE 802.1X: External notification - Create MKA for "
+		   MACSTR, MAC2STR(sta->addr));
+
+	msk = os_zalloc(sizeof(*msk));
+	ckn = os_zalloc(sizeof(*ckn));
+	cak = os_zalloc(sizeof(*cak));
+	if (!msk || !ckn || !cak)
+		goto fail;
+
+	msk->len = DEFAULT_KEY_LEN;
+	if (ieee802_1x_auth_get_msk(hapd, sta, msk->key, &msk->len)) {
+		wpa_printf(MSG_ERROR, "IEEE 802.1X: Could not get MSK");
+		goto fail;
+	}
+
+	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;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "own_addr", hapd->own_addr, ETH_ALEN);
+	wpa_hexdump(MSG_DEBUG, "sta_addr", sta->addr, ETH_ALEN);
+
+	/* Derive CAK from MSK */
+	cak->len = DEFAULT_KEY_LEN;
+	if (ieee802_1x_cak_aes_cmac(msk->key, msk->len, hapd->own_addr,
+				    sta->addr, cak->key, cak->len)) {
+		wpa_printf(MSG_ERROR, "IEEE 802.1X: Deriving CAK failed");
+		goto fail;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "Derived CAK", cak->key, cak->len);
+
+	/* Derive CKN from MSK */
+	ckn->len = DEFAULT_CKN_LEN;
+	if (ieee802_1x_ckn_aes_cmac(msk->key, msk->len, hapd->own_addr,
+				    sta->addr, sid, sid_len, ckn->name)) {
+		wpa_printf(MSG_ERROR, "IEEE 802.1X: Deriving CKN failed");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "Derived CKN", ckn->name, ckn->len);
+
+	res = ieee802_1x_kay_create_mka(hapd->kay, ckn, cak, 0, EAP_EXCHANGE,
+					true);
+
+fail:
+	bin_clear_free(msk, sizeof(*msk));
+	os_free(ckn);
+	bin_clear_free(cak, sizeof(*cak));
+
+	return res;
+}
+
+
+void * ieee802_1x_create_preshared_mka_hapd(struct hostapd_data *hapd,
+					    struct sta_info *sta)
+{
+	struct mka_key *cak;
+	struct mka_key_name *ckn;
+	void *res = NULL;
+
+	if ((hapd->conf->mka_psk_set & MKA_PSK_SET) != MKA_PSK_SET)
+		goto end;
+
+	ckn = os_zalloc(sizeof(*ckn));
+	if (!ckn)
+		goto end;
+
+	cak = os_zalloc(sizeof(*cak));
+	if (!cak)
+		goto free_ckn;
+
+	if (ieee802_1x_alloc_kay_sm_hapd(hapd, sta) < 0 || !hapd->kay)
+		goto free_cak;
+
+	if (hapd->kay->policy == DO_NOT_SECURE)
+		goto dealloc;
+
+	cak->len = hapd->conf->mka_cak_len;
+	os_memcpy(cak->key, hapd->conf->mka_cak, cak->len);
+
+	ckn->len = hapd->conf->mka_ckn_len;;
+	os_memcpy(ckn->name, hapd->conf->mka_ckn, ckn->len);
+
+	res = ieee802_1x_kay_create_mka(hapd->kay, ckn, cak, 0, PSK, true);
+	if (res)
+		goto free_cak;
+
+dealloc:
+	/* Failed to create MKA */
+	ieee802_1x_dealloc_kay_sm_hapd(hapd);
+free_cak:
+	os_free(cak);
+free_ckn:
+	os_free(ckn);
+end:
+	return res;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_kay.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_kay.h
new file mode 100644
index 0000000..0dd7e41
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wpa_auth_kay.h
@@ -0,0 +1,51 @@
+/*
+ * IEEE 802.1X-2010 KaY Interface
+ * Copyright (c) 2019, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPA_AUTH_KAY_H
+#define WPA_AUTH_KAY_H
+
+#ifdef CONFIG_MACSEC
+
+int ieee802_1x_alloc_kay_sm_hapd(struct hostapd_data *hapd,
+				 struct sta_info *sta);
+void * ieee802_1x_notify_create_actor_hapd(struct hostapd_data *hapd,
+					   struct sta_info *sta);
+void ieee802_1x_dealloc_kay_sm_hapd(struct hostapd_data *hapd);
+
+void * ieee802_1x_create_preshared_mka_hapd(struct hostapd_data *hapd,
+					    struct sta_info *sta);
+
+#else /* CONFIG_MACSEC */
+
+static inline int ieee802_1x_alloc_kay_sm_hapd(struct hostapd_data *hapd,
+					       struct sta_info *sta)
+{
+	return 0;
+}
+
+static inline void *
+ieee802_1x_notify_create_actor_hapd(struct hostapd_data *hapd,
+				    struct sta_info *sta)
+{
+	return NULL;
+}
+
+static inline void ieee802_1x_dealloc_kay_sm_hapd(struct hostapd_data *hapd)
+{
+}
+
+static inline void *
+ieee802_1x_create_preshared_mka_hapd(struct hostapd_data *hapd,
+				     struct sta_info *sta)
+{
+	return NULL;
+}
+
+#endif /* CONFIG_MACSEC */
+
+#endif /* WPA_AUTH_KAY_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wps_hostapd.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wps_hostapd.c
new file mode 100644
index 0000000..c04e97d
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wps_hostapd.c
@@ -0,0 +1,2312 @@
+/*
+ * hostapd / WPS integration
+ * Copyright (c) 2008-2016, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/uuid.h"
+#include "common/wpa_ctrl.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
+#include "wps/wps.h"
+#include "wps/wps_defs.h"
+#include "wps/wps_dev_attr.h"
+#include "wps/wps_attr_parse.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "beacon.h"
+#include "sta_info.h"
+#include "wps_hostapd.h"
+
+
+#ifdef CONFIG_WPS_UPNP
+#include "wps/wps_upnp.h"
+static int hostapd_wps_upnp_init(struct hostapd_data *hapd,
+				 struct wps_context *wps);
+static void hostapd_wps_upnp_deinit(struct hostapd_data *hapd);
+#endif /* CONFIG_WPS_UPNP */
+
+static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, const u8 *da,
+				    const u8 *bssid,
+				    const u8 *ie, size_t ie_len,
+				    int ssi_signal);
+static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx);
+static void hostapd_wps_nfc_clear(struct wps_context *wps);
+
+
+struct wps_for_each_data {
+	int (*func)(struct hostapd_data *h, void *ctx);
+	void *ctx;
+	struct hostapd_data *calling_hapd;
+};
+
+
+static int wps_for_each(struct hostapd_iface *iface, void *ctx)
+{
+	struct wps_for_each_data *data = ctx;
+	size_t j;
+
+	if (iface == NULL)
+		return 0;
+	for (j = 0; j < iface->num_bss; j++) {
+		struct hostapd_data *hapd = iface->bss[j];
+		int ret;
+
+		if (hapd != data->calling_hapd &&
+		    (hapd->conf->wps_independent ||
+		     data->calling_hapd->conf->wps_independent))
+			continue;
+
+		ret = data->func(hapd, data->ctx);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+
+static int hostapd_wps_for_each(struct hostapd_data *hapd,
+				int (*func)(struct hostapd_data *h, void *ctx),
+				void *ctx)
+{
+	struct hostapd_iface *iface = hapd->iface;
+	struct wps_for_each_data data;
+	data.func = func;
+	data.ctx = ctx;
+	data.calling_hapd = hapd;
+	if (iface->interfaces == NULL ||
+	    iface->interfaces->for_each_interface == NULL)
+		return wps_for_each(iface, &data);
+	return iface->interfaces->for_each_interface(iface->interfaces,
+						     wps_for_each, &data);
+}
+
+
+static int hostapd_wps_new_psk_cb(void *ctx, const u8 *mac_addr,
+				  const u8 *p2p_dev_addr, const u8 *psk,
+				  size_t psk_len)
+{
+	struct hostapd_data *hapd = ctx;
+	struct hostapd_wpa_psk *p;
+	struct hostapd_ssid *ssid = &hapd->conf->ssid;
+
+	if (is_zero_ether_addr(p2p_dev_addr)) {
+		wpa_printf(MSG_DEBUG,
+			   "Received new WPA/WPA2-PSK from WPS for STA " MACSTR,
+			   MAC2STR(mac_addr));
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "Received new WPA/WPA2-PSK from WPS for STA " MACSTR
+			   " P2P Device Addr " MACSTR,
+			   MAC2STR(mac_addr), MAC2STR(p2p_dev_addr));
+	}
+	wpa_hexdump_key(MSG_DEBUG, "Per-device PSK", psk, psk_len);
+
+	if (psk_len != PMK_LEN) {
+		wpa_printf(MSG_DEBUG, "Unexpected PSK length %lu",
+			   (unsigned long) psk_len);
+		return -1;
+	}
+
+	/* Add the new PSK to runtime PSK list */
+	p = os_zalloc(sizeof(*p));
+	if (p == NULL)
+		return -1;
+	os_memcpy(p->addr, mac_addr, ETH_ALEN);
+	os_memcpy(p->p2p_dev_addr, p2p_dev_addr, ETH_ALEN);
+	os_memcpy(p->psk, psk, PMK_LEN);
+	p->wps = 1;
+
+	if (hapd->new_psk_cb) {
+		hapd->new_psk_cb(hapd->new_psk_cb_ctx, mac_addr, p2p_dev_addr,
+				 psk, psk_len);
+	}
+
+	p->next = ssid->wpa_psk;
+	ssid->wpa_psk = p;
+
+	if (ssid->wpa_psk_file) {
+		FILE *f;
+		char hex[PMK_LEN * 2 + 1];
+
+		/* Add the new PSK to PSK list file */
+		f = fopen(ssid->wpa_psk_file, "a");
+		if (!f) {
+			wpa_printf(MSG_DEBUG, "Failed to add the PSK to '%s'",
+				   ssid->wpa_psk_file);
+			return -1;
+		}
+
+		wpa_snprintf_hex(hex, sizeof(hex), psk, psk_len);
+		fprintf(f, "wps=1 " MACSTR " %s\n", MAC2STR(mac_addr), hex);
+		fclose(f);
+	}
+
+	return 0;
+}
+
+
+static int hostapd_wps_set_ie_cb(void *ctx, struct wpabuf *beacon_ie,
+				 struct wpabuf *probe_resp_ie)
+{
+	struct hostapd_data *hapd = ctx;
+	wpabuf_free(hapd->wps_beacon_ie);
+	hapd->wps_beacon_ie = beacon_ie;
+	wpabuf_free(hapd->wps_probe_resp_ie);
+	hapd->wps_probe_resp_ie = probe_resp_ie;
+	if (hapd->beacon_set_done)
+		ieee802_11_set_beacon(hapd);
+	return hostapd_set_ap_wps_ie(hapd);
+}
+
+
+static void hostapd_wps_pin_needed_cb(void *ctx, const u8 *uuid_e,
+				      const struct wps_device_data *dev)
+{
+	struct hostapd_data *hapd = ctx;
+	char uuid[40], txt[400];
+	int len;
+	char devtype[WPS_DEV_TYPE_BUFSIZE];
+	if (uuid_bin2str(uuid_e, uuid, sizeof(uuid)))
+		return;
+	wpa_printf(MSG_DEBUG, "WPS: PIN needed for E-UUID %s", uuid);
+	len = os_snprintf(txt, sizeof(txt), WPS_EVENT_PIN_NEEDED
+			  "%s " MACSTR " [%s|%s|%s|%s|%s|%s]",
+			  uuid, MAC2STR(dev->mac_addr), dev->device_name,
+			  dev->manufacturer, dev->model_name,
+			  dev->model_number, dev->serial_number,
+			  wps_dev_type_bin2str(dev->pri_dev_type, devtype,
+					       sizeof(devtype)));
+	if (!os_snprintf_error(sizeof(txt), len))
+		wpa_msg(hapd->msg_ctx, MSG_INFO, "%s", txt);
+
+	if (hapd->conf->wps_pin_requests) {
+		FILE *f;
+		struct os_time t;
+		f = fopen(hapd->conf->wps_pin_requests, "a");
+		if (f == NULL)
+			return;
+		os_get_time(&t);
+		fprintf(f, "%ld\t%s\t" MACSTR "\t%s\t%s\t%s\t%s\t%s"
+			"\t%s\n",
+			t.sec, uuid, MAC2STR(dev->mac_addr), dev->device_name,
+			dev->manufacturer, dev->model_name, dev->model_number,
+			dev->serial_number,
+			wps_dev_type_bin2str(dev->pri_dev_type, devtype,
+					     sizeof(devtype)));
+		fclose(f);
+	}
+}
+
+
+struct wps_stop_reg_data {
+	struct hostapd_data *current_hapd;
+	const u8 *uuid_e;
+	const u8 *dev_pw;
+	size_t dev_pw_len;
+};
+
+static int wps_stop_registrar(struct hostapd_data *hapd, void *ctx)
+{
+	struct wps_stop_reg_data *data = ctx;
+	if (hapd != data->current_hapd && hapd->wps != NULL)
+		wps_registrar_complete(hapd->wps->registrar, data->uuid_e,
+				       data->dev_pw, data->dev_pw_len);
+	return 0;
+}
+
+
+static void hostapd_wps_reg_success_cb(void *ctx, const u8 *mac_addr,
+				       const u8 *uuid_e, const u8 *dev_pw,
+				       size_t dev_pw_len)
+{
+	struct hostapd_data *hapd = ctx;
+	char uuid[40];
+	struct wps_stop_reg_data data;
+	if (uuid_bin2str(uuid_e, uuid, sizeof(uuid)))
+		return;
+	wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_REG_SUCCESS MACSTR " %s",
+		MAC2STR(mac_addr), uuid);
+	if (hapd->wps_reg_success_cb)
+		hapd->wps_reg_success_cb(hapd->wps_reg_success_cb_ctx,
+					 mac_addr, uuid_e);
+	data.current_hapd = hapd;
+	data.uuid_e = uuid_e;
+	data.dev_pw = dev_pw;
+	data.dev_pw_len = dev_pw_len;
+	hostapd_wps_for_each(hapd, wps_stop_registrar, &data);
+}
+
+
+static void hostapd_wps_enrollee_seen_cb(void *ctx, const u8 *addr,
+					 const u8 *uuid_e,
+					 const u8 *pri_dev_type,
+					 u16 config_methods,
+					 u16 dev_password_id, u8 request_type,
+					 const char *dev_name)
+{
+	struct hostapd_data *hapd = ctx;
+	char uuid[40];
+	char devtype[WPS_DEV_TYPE_BUFSIZE];
+	if (uuid_bin2str(uuid_e, uuid, sizeof(uuid)))
+		return;
+	if (dev_name == NULL)
+		dev_name = "";
+	wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, WPS_EVENT_ENROLLEE_SEEN MACSTR
+		     " %s %s 0x%x %u %u [%s]",
+		     MAC2STR(addr), uuid,
+		     wps_dev_type_bin2str(pri_dev_type, devtype,
+					  sizeof(devtype)),
+		     config_methods, dev_password_id, request_type, dev_name);
+}
+
+
+static int hostapd_wps_lookup_pskfile_cb(void *ctx, const u8 *mac_addr,
+					 const u8 **psk)
+{
+	const struct hostapd_data *hapd = ctx;
+	const struct hostapd_wpa_psk *wpa_psk;
+	const u8 *any_psk = NULL;
+	const u8 *dev_psk = NULL;
+
+	for (wpa_psk = hapd->conf->ssid.wpa_psk; wpa_psk;
+	     wpa_psk = wpa_psk->next) {
+		if (!wpa_psk->wps)
+			continue;
+
+		if (!any_psk && is_zero_ether_addr(wpa_psk->addr))
+			any_psk = wpa_psk->psk;
+
+		if (mac_addr && !dev_psk &&
+		    os_memcmp(mac_addr, wpa_psk->addr, ETH_ALEN) == 0) {
+			dev_psk = wpa_psk->psk;
+			break;
+		}
+	}
+
+	if (dev_psk) {
+		*psk = dev_psk;
+	} else if (any_psk) {
+		*psk = any_psk;
+	} else {
+		*psk = NULL;
+		wpa_printf(MSG_DEBUG,
+			   "WPS: No appropriate PSK in wpa_psk_file");
+		return 0;
+	}
+
+	return 1;
+}
+
+
+static void wps_reload_config(void *eloop_data, void *user_ctx)
+{
+	struct hostapd_iface *iface = eloop_data;
+
+	wpa_printf(MSG_DEBUG, "WPS: Reload configuration data");
+	if (iface->interfaces == NULL ||
+	    iface->interfaces->reload_config(iface, 1) < 0) {
+		wpa_printf(MSG_WARNING, "WPS: Failed to reload the updated "
+			   "configuration");
+	}
+}
+
+
+void hostapd_wps_eap_completed(struct hostapd_data *hapd)
+{
+	/*
+	 * Reduce race condition of the station trying to reconnect immediately
+	 * after AP reconfiguration through WPS by rescheduling the reload
+	 * timeout to happen after EAP completion rather than the originally
+	 * scheduled 100 ms after new configuration became known.
+	 */
+	if (eloop_deplete_timeout(0, 0, wps_reload_config, hapd->iface, NULL) ==
+	    1)
+		wpa_printf(MSG_DEBUG, "WPS: Reschedule immediate configuration reload");
+}
+
+
+static void hapd_new_ap_event(struct hostapd_data *hapd, const u8 *attr,
+			      size_t attr_len)
+{
+	size_t blen = attr_len * 2 + 1;
+	char *buf = os_malloc(blen);
+	if (buf) {
+		wpa_snprintf_hex(buf, blen, attr, attr_len);
+		wpa_msg(hapd->msg_ctx, MSG_INFO,
+			WPS_EVENT_NEW_AP_SETTINGS "%s", buf);
+		os_free(buf);
+	}
+}
+
+
+static int hapd_wps_reconfig_in_memory(struct hostapd_data *hapd,
+				       const struct wps_credential *cred)
+{
+	struct hostapd_bss_config *bss = hapd->conf;
+
+	wpa_printf(MSG_DEBUG, "WPS: Updating in-memory configuration");
+
+	bss->wps_state = 2;
+	if (cred->ssid_len <= SSID_MAX_LEN) {
+		os_memcpy(bss->ssid.ssid, cred->ssid, cred->ssid_len);
+		bss->ssid.ssid_len = cred->ssid_len;
+		bss->ssid.ssid_set = 1;
+	}
+
+#ifdef CONFIG_NO_TKIP
+	if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK |
+			       WPS_AUTH_WPA | WPS_AUTH_WPAPSK))
+		bss->wpa = 2;
+	else
+		bss->wpa = 0;
+#else /* CONFIG_NO_TKIP */
+	if ((cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) &&
+	    (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK)))
+		bss->wpa = 3;
+	else if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK))
+		bss->wpa = 2;
+	else if (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK))
+		bss->wpa = 1;
+	else
+		bss->wpa = 0;
+#endif /* CONFIG_NO_TKIP */
+
+	if (bss->wpa) {
+		if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA))
+			bss->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+		if (cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK))
+			bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
+
+		bss->wpa_pairwise = 0;
+		if (cred->encr_type & WPS_ENCR_AES) {
+			if (hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD)
+				bss->wpa_pairwise |= WPA_CIPHER_GCMP;
+			else
+				bss->wpa_pairwise |= WPA_CIPHER_CCMP;
+#ifndef CONFIG_NO_TKIP
+		} else if (cred->encr_type & WPS_ENCR_TKIP)
+			bss->wpa_pairwise |= WPA_CIPHER_TKIP;
+#endif /* CONFIG_NO_TKIP */
+		bss->rsn_pairwise = bss->wpa_pairwise;
+		bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa,
+							    bss->wpa_pairwise,
+							    bss->rsn_pairwise);
+
+		if (hapd->conf->wps_cred_add_sae &&
+		    (cred->auth_type & WPS_AUTH_WPA2PSK) &&
+		    cred->key_len != 2 * PMK_LEN) {
+			bss->wpa_key_mgmt |= WPA_KEY_MGMT_SAE;
+			if (bss->ieee80211w == NO_MGMT_FRAME_PROTECTION)
+				bss->ieee80211w =
+					MGMT_FRAME_PROTECTION_OPTIONAL;
+			bss->sae_require_mfp = 1;
+		}
+
+		if (cred->key_len >= 8 && cred->key_len < 64) {
+			os_free(bss->ssid.wpa_passphrase);
+			bss->ssid.wpa_passphrase = os_zalloc(cred->key_len + 1);
+			if (bss->ssid.wpa_passphrase)
+				os_memcpy(bss->ssid.wpa_passphrase, cred->key,
+					  cred->key_len);
+			hostapd_config_clear_wpa_psk(&bss->ssid.wpa_psk);
+		} else if (cred->key_len == 64) {
+			hostapd_config_clear_wpa_psk(&bss->ssid.wpa_psk);
+			bss->ssid.wpa_psk =
+				os_zalloc(sizeof(struct hostapd_wpa_psk));
+			if (bss->ssid.wpa_psk &&
+			    hexstr2bin((const char *) cred->key,
+				       bss->ssid.wpa_psk->psk, PMK_LEN) == 0) {
+				bss->ssid.wpa_psk->group = 1;
+				os_free(bss->ssid.wpa_passphrase);
+				bss->ssid.wpa_passphrase = NULL;
+			}
+		}
+		bss->auth_algs = 1;
+	} else {
+		/*
+		 * WPS 2.0 does not allow WEP to be configured, so no need to
+		 * process that option here either.
+		 */
+		bss->auth_algs = 1;
+	}
+
+	/* Schedule configuration reload after short period of time to allow
+	 * EAP-WSC to be finished.
+	 */
+	eloop_register_timeout(0, 100000, wps_reload_config, hapd->iface,
+			       NULL);
+
+	return 0;
+}
+
+
+static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx)
+{
+	const struct wps_credential *cred = ctx;
+	FILE *oconf, *nconf;
+	size_t len, i;
+	char *tmp_fname;
+	char buf[1024];
+	int multi_bss;
+	int wpa;
+	int pmf_changed = 0;
+
+	if (hapd->wps == NULL)
+		return 0;
+
+	wpa_hexdump_key(MSG_DEBUG, "WPS: Received Credential attribute",
+			cred->cred_attr, cred->cred_attr_len);
+
+	wpa_printf(MSG_DEBUG, "WPS: Received new AP Settings");
+	wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", cred->ssid, cred->ssid_len);
+	wpa_printf(MSG_DEBUG, "WPS: Authentication Type 0x%x",
+		   cred->auth_type);
+	wpa_printf(MSG_DEBUG, "WPS: Encryption Type 0x%x", cred->encr_type);
+	wpa_printf(MSG_DEBUG, "WPS: Network Key Index %d", cred->key_idx);
+	wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key",
+			cred->key, cred->key_len);
+	wpa_printf(MSG_DEBUG, "WPS: MAC Address " MACSTR,
+		   MAC2STR(cred->mac_addr));
+
+	if ((hapd->conf->wps_cred_processing == 1 ||
+	     hapd->conf->wps_cred_processing == 2) && cred->cred_attr) {
+		hapd_new_ap_event(hapd, cred->cred_attr, cred->cred_attr_len);
+	} else if (hapd->conf->wps_cred_processing == 1 ||
+		   hapd->conf->wps_cred_processing == 2) {
+		struct wpabuf *attr;
+		attr = wpabuf_alloc(200);
+		if (attr && wps_build_credential_wrap(attr, cred) == 0)
+			hapd_new_ap_event(hapd, wpabuf_head_u8(attr),
+					  wpabuf_len(attr));
+		wpabuf_free(attr);
+	} else
+		wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_NEW_AP_SETTINGS);
+
+	if (hapd->conf->wps_cred_processing == 1)
+		return 0;
+
+	os_memcpy(hapd->wps->ssid, cred->ssid, cred->ssid_len);
+	hapd->wps->ssid_len = cred->ssid_len;
+	hapd->wps->encr_types = cred->encr_type;
+	hapd->wps->encr_types_rsn = cred->encr_type;
+	hapd->wps->encr_types_wpa = cred->encr_type;
+	hapd->wps->auth_types = cred->auth_type;
+	hapd->wps->ap_encr_type = cred->encr_type;
+	hapd->wps->ap_auth_type = cred->auth_type;
+	if (cred->key_len == 0) {
+		os_free(hapd->wps->network_key);
+		hapd->wps->network_key = NULL;
+		hapd->wps->network_key_len = 0;
+	} else if ((cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) &&
+		   (cred->key_len < 8 || cred->key_len > 2 * PMK_LEN)) {
+		wpa_printf(MSG_INFO, "WPS: Invalid key length %lu for WPA/WPA2",
+			   (unsigned long) cred->key_len);
+		return -1;
+	} else {
+		if (hapd->wps->network_key == NULL ||
+		    hapd->wps->network_key_len < cred->key_len) {
+			hapd->wps->network_key_len = 0;
+			os_free(hapd->wps->network_key);
+			hapd->wps->network_key = os_malloc(cred->key_len);
+			if (hapd->wps->network_key == NULL)
+				return -1;
+		}
+		hapd->wps->network_key_len = cred->key_len;
+		os_memcpy(hapd->wps->network_key, cred->key, cred->key_len);
+	}
+	hapd->wps->wps_state = WPS_STATE_CONFIGURED;
+
+	if (hapd->iface->config_fname == NULL)
+		return hapd_wps_reconfig_in_memory(hapd, cred);
+	len = os_strlen(hapd->iface->config_fname) + 5;
+	tmp_fname = os_malloc(len);
+	if (tmp_fname == NULL)
+		return -1;
+	os_snprintf(tmp_fname, len, "%s-new", hapd->iface->config_fname);
+
+	oconf = fopen(hapd->iface->config_fname, "r");
+	if (oconf == NULL) {
+		wpa_printf(MSG_WARNING, "WPS: Could not open current "
+			   "configuration file");
+		os_free(tmp_fname);
+		return -1;
+	}
+
+	nconf = fopen(tmp_fname, "w");
+	if (nconf == NULL) {
+		wpa_printf(MSG_WARNING, "WPS: Could not write updated "
+			   "configuration file");
+		os_free(tmp_fname);
+		fclose(oconf);
+		return -1;
+	}
+
+	fprintf(nconf, "# WPS configuration - START\n");
+
+	fprintf(nconf, "wps_state=2\n");
+
+	if (is_hex(cred->ssid, cred->ssid_len)) {
+		fprintf(nconf, "ssid2=");
+		for (i = 0; i < cred->ssid_len; i++)
+			fprintf(nconf, "%02x", cred->ssid[i]);
+		fprintf(nconf, "\n");
+	} else {
+		fprintf(nconf, "ssid=");
+		for (i = 0; i < cred->ssid_len; i++)
+			fputc(cred->ssid[i], nconf);
+		fprintf(nconf, "\n");
+	}
+
+#ifdef CONFIG_NO_TKIP
+	if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK |
+			       WPS_AUTH_WPA | WPS_AUTH_WPAPSK))
+		wpa = 2;
+	else
+		wpa = 0;
+#else /* CONFIG_NO_TKIP */
+	if ((cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) &&
+	    (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK)))
+		wpa = 3;
+	else if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK))
+		wpa = 2;
+	else if (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK))
+		wpa = 1;
+	else
+		wpa = 0;
+#endif /* CONFIG_NO_TKIP */
+
+	if (wpa) {
+		char *prefix;
+		int sae = 0;
+
+		fprintf(nconf, "wpa=%d\n", wpa);
+
+		fprintf(nconf, "wpa_key_mgmt=");
+		prefix = "";
+		if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA)) {
+			fprintf(nconf, "WPA-EAP");
+			prefix = " ";
+		}
+		if (cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) {
+			fprintf(nconf, "%sWPA-PSK", prefix);
+			prefix = " ";
+		}
+		if (hapd->conf->wps_cred_add_sae &&
+		    (cred->auth_type & WPS_AUTH_WPA2PSK) &&
+		    cred->key_len != 2 * PMK_LEN) {
+			fprintf(nconf, "%sSAE", prefix);
+			sae = 1;
+		}
+		fprintf(nconf, "\n");
+
+		if (sae && hapd->conf->ieee80211w == NO_MGMT_FRAME_PROTECTION) {
+			fprintf(nconf, "ieee80211w=%d\n",
+				MGMT_FRAME_PROTECTION_OPTIONAL);
+			pmf_changed = 1;
+		}
+		if (sae)
+			fprintf(nconf, "sae_require_mfp=1\n");
+
+		fprintf(nconf, "wpa_pairwise=");
+		prefix = "";
+		if (cred->encr_type & WPS_ENCR_AES) {
+			if (hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD)
+				fprintf(nconf, "GCMP");
+			else
+				fprintf(nconf, "CCMP");
+
+			prefix = " ";
+		}
+#ifndef CONFIG_NO_TKIP
+		if (cred->encr_type & WPS_ENCR_TKIP) {
+			fprintf(nconf, "%sTKIP", prefix);
+		}
+#endif /* CONFIG_NO_TKIP */
+		fprintf(nconf, "\n");
+
+		if (cred->key_len >= 8 && cred->key_len < 64) {
+			fprintf(nconf, "wpa_passphrase=");
+			for (i = 0; i < cred->key_len; i++)
+				fputc(cred->key[i], nconf);
+			fprintf(nconf, "\n");
+		} else if (cred->key_len == 64) {
+			fprintf(nconf, "wpa_psk=");
+			for (i = 0; i < cred->key_len; i++)
+				fputc(cred->key[i], nconf);
+			fprintf(nconf, "\n");
+		} else {
+			wpa_printf(MSG_WARNING, "WPS: Invalid key length %lu "
+				   "for WPA/WPA2",
+				   (unsigned long) cred->key_len);
+		}
+
+		fprintf(nconf, "auth_algs=1\n");
+	} else {
+		/*
+		 * WPS 2.0 does not allow WEP to be configured, so no need to
+		 * process that option here either.
+		 */
+		fprintf(nconf, "auth_algs=1\n");
+	}
+
+	fprintf(nconf, "# WPS configuration - END\n");
+
+	multi_bss = 0;
+	while (fgets(buf, sizeof(buf), oconf)) {
+		if (os_strncmp(buf, "bss=", 4) == 0)
+			multi_bss = 1;
+		if (!multi_bss &&
+		    (str_starts(buf, "ssid=") ||
+		     str_starts(buf, "ssid2=") ||
+		     str_starts(buf, "auth_algs=") ||
+#ifdef CONFIG_WEP
+		     str_starts(buf, "wep_default_key=") ||
+		     str_starts(buf, "wep_key") ||
+#endif /* CONFIG_WEP */
+		     str_starts(buf, "wps_state=") ||
+		     (pmf_changed && str_starts(buf, "ieee80211w=")) ||
+		     str_starts(buf, "wpa=") ||
+		     str_starts(buf, "wpa_psk=") ||
+		     str_starts(buf, "wpa_pairwise=") ||
+		     str_starts(buf, "rsn_pairwise=") ||
+		     str_starts(buf, "wpa_key_mgmt=") ||
+		     str_starts(buf, "wpa_passphrase="))) {
+			fprintf(nconf, "#WPS# %s", buf);
+		} else
+			fprintf(nconf, "%s", buf);
+	}
+
+	fclose(nconf);
+	fclose(oconf);
+
+	if (rename(tmp_fname, hapd->iface->config_fname) < 0) {
+		wpa_printf(MSG_WARNING, "WPS: Failed to rename the updated "
+			   "configuration file: %s", strerror(errno));
+		os_free(tmp_fname);
+		return -1;
+	}
+
+	os_free(tmp_fname);
+
+	/* Schedule configuration reload after short period of time to allow
+	 * EAP-WSC to be finished.
+	 */
+	eloop_register_timeout(0, 100000, wps_reload_config, hapd->iface,
+			       NULL);
+
+	wpa_printf(MSG_DEBUG, "WPS: AP configuration updated");
+
+	return 0;
+}
+
+
+static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred)
+{
+	struct hostapd_data *hapd = ctx;
+	return hostapd_wps_for_each(hapd, hapd_wps_cred_cb, (void *) cred);
+}
+
+
+static void hostapd_wps_reenable_ap_pin(void *eloop_data, void *user_ctx)
+{
+	struct hostapd_data *hapd = eloop_data;
+
+	if (hapd->conf->ap_setup_locked)
+		return;
+	if (hapd->ap_pin_failures_consecutive >= 10)
+		return;
+
+	wpa_printf(MSG_DEBUG, "WPS: Re-enable AP PIN");
+	wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_UNLOCKED);
+	hapd->wps->ap_setup_locked = 0;
+	wps_registrar_update_ie(hapd->wps->registrar);
+}
+
+
+static int wps_pwd_auth_fail(struct hostapd_data *hapd, void *ctx)
+{
+	struct wps_event_pwd_auth_fail *data = ctx;
+
+	if (!data->enrollee || hapd->conf->ap_pin == NULL || hapd->wps == NULL)
+		return 0;
+
+	/*
+	 * Registrar failed to prove its knowledge of the AP PIN. Lock AP setup
+	 * for some time if this happens multiple times to slow down brute
+	 * force attacks.
+	 */
+	hapd->ap_pin_failures++;
+	hapd->ap_pin_failures_consecutive++;
+	wpa_printf(MSG_DEBUG, "WPS: AP PIN authentication failure number %u "
+		   "(%u consecutive)",
+		   hapd->ap_pin_failures, hapd->ap_pin_failures_consecutive);
+	if (hapd->ap_pin_failures < 3)
+		return 0;
+
+	wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_LOCKED);
+	hapd->wps->ap_setup_locked = 1;
+
+	wps_registrar_update_ie(hapd->wps->registrar);
+
+	if (!hapd->conf->ap_setup_locked &&
+	    hapd->ap_pin_failures_consecutive >= 10) {
+		/*
+		 * In indefinite lockdown - disable automatic AP PIN
+		 * reenablement.
+		 */
+		eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
+		wpa_printf(MSG_DEBUG, "WPS: AP PIN disabled indefinitely");
+	} else if (!hapd->conf->ap_setup_locked) {
+		if (hapd->ap_pin_lockout_time == 0)
+			hapd->ap_pin_lockout_time = 60;
+		else if (hapd->ap_pin_lockout_time < 365 * 24 * 60 * 60 &&
+			 (hapd->ap_pin_failures % 3) == 0)
+			hapd->ap_pin_lockout_time *= 2;
+
+		wpa_printf(MSG_DEBUG, "WPS: Disable AP PIN for %u seconds",
+			   hapd->ap_pin_lockout_time);
+		eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
+		eloop_register_timeout(hapd->ap_pin_lockout_time, 0,
+				       hostapd_wps_reenable_ap_pin, hapd,
+				       NULL);
+	}
+
+	return 0;
+}
+
+
+static void hostapd_pwd_auth_fail(struct hostapd_data *hapd,
+				  struct wps_event_pwd_auth_fail *data)
+{
+	/* Update WPS Status - Authentication Failure */
+	wpa_printf(MSG_DEBUG, "WPS: Authentication failure update");
+	hapd->wps_stats.status = WPS_STATUS_FAILURE;
+	hapd->wps_stats.failure_reason = WPS_EI_AUTH_FAILURE;
+	os_memcpy(hapd->wps_stats.peer_addr, data->peer_macaddr, ETH_ALEN);
+
+	hostapd_wps_for_each(hapd, wps_pwd_auth_fail, data);
+}
+
+
+static int wps_ap_pin_success(struct hostapd_data *hapd, void *ctx)
+{
+	if (hapd->conf->ap_pin == NULL || hapd->wps == NULL)
+		return 0;
+
+	if (hapd->ap_pin_failures_consecutive == 0)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "WPS: Clear consecutive AP PIN failure counter "
+		   "- total validation failures %u (%u consecutive)",
+		   hapd->ap_pin_failures, hapd->ap_pin_failures_consecutive);
+	hapd->ap_pin_failures_consecutive = 0;
+
+	return 0;
+}
+
+
+static void hostapd_wps_ap_pin_success(struct hostapd_data *hapd)
+{
+	hostapd_wps_for_each(hapd, wps_ap_pin_success, NULL);
+}
+
+
+static void hostapd_wps_event_pbc_overlap(struct hostapd_data *hapd)
+{
+	/* Update WPS Status - PBC Overlap */
+	hapd->wps_stats.pbc_status = WPS_PBC_STATUS_OVERLAP;
+}
+
+
+static void hostapd_wps_event_pbc_timeout(struct hostapd_data *hapd)
+{
+	/* Update WPS PBC Status:PBC Timeout */
+	hapd->wps_stats.pbc_status = WPS_PBC_STATUS_TIMEOUT;
+}
+
+
+static void hostapd_wps_event_pbc_active(struct hostapd_data *hapd)
+{
+	/* Update WPS PBC status - Active */
+	hapd->wps_stats.pbc_status = WPS_PBC_STATUS_ACTIVE;
+}
+
+
+static void hostapd_wps_event_pbc_disable(struct hostapd_data *hapd)
+{
+	/* Update WPS PBC status - Active */
+	hapd->wps_stats.pbc_status = WPS_PBC_STATUS_DISABLE;
+}
+
+
+static void hostapd_wps_event_success(struct hostapd_data *hapd,
+				      struct wps_event_success *success)
+{
+	/* Update WPS status - Success */
+	hapd->wps_stats.pbc_status = WPS_PBC_STATUS_DISABLE;
+	hapd->wps_stats.status = WPS_STATUS_SUCCESS;
+	os_memcpy(hapd->wps_stats.peer_addr, success->peer_macaddr, ETH_ALEN);
+}
+
+
+static void hostapd_wps_event_fail(struct hostapd_data *hapd,
+				   struct wps_event_fail *fail)
+{
+	/* Update WPS status - Failure */
+	hapd->wps_stats.status = WPS_STATUS_FAILURE;
+	os_memcpy(hapd->wps_stats.peer_addr, fail->peer_macaddr, ETH_ALEN);
+
+	hapd->wps_stats.failure_reason = fail->error_indication;
+
+	if (fail->error_indication > 0 &&
+	    fail->error_indication < NUM_WPS_EI_VALUES) {
+		wpa_msg(hapd->msg_ctx, MSG_INFO,
+			WPS_EVENT_FAIL "msg=%d config_error=%d reason=%d (%s)",
+			fail->msg, fail->config_error, fail->error_indication,
+			wps_ei_str(fail->error_indication));
+	} else {
+		wpa_msg(hapd->msg_ctx, MSG_INFO,
+			WPS_EVENT_FAIL "msg=%d config_error=%d",
+			fail->msg, fail->config_error);
+	}
+}
+
+
+static void hostapd_wps_event_cb(void *ctx, enum wps_event event,
+				 union wps_event_data *data)
+{
+	struct hostapd_data *hapd = ctx;
+	struct hostapd_ubus_request req;
+
+	os_memset(&req, 0, sizeof(req));
+	switch (event) {
+	case WPS_EV_M2D:
+		wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_M2D);
+		break;
+	case WPS_EV_START:
+		req.type = HOSTAPD_UBUS_WPS_START;
+		req.addr = data->success.peer_macaddr;
+		hostapd_ubus_handle_event(hapd, &req);
+		break;
+	case WPS_EV_FAIL:
+		req.type = HOSTAPD_UBUS_WPS_FAIL;
+		req.addr = data->fail.peer_macaddr;
+		hostapd_ubus_handle_event(hapd, &req);
+		hostapd_wps_event_fail(hapd, &data->fail);
+		break;
+	case WPS_EV_SUCCESS:
+		req.type = HOSTAPD_UBUS_WPS_SUCCESS;
+		req.addr = data->success.peer_macaddr;
+		hostapd_ubus_handle_event(hapd, &req);
+		hostapd_wps_event_success(hapd, &data->success);
+		wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_SUCCESS);
+		break;
+	case WPS_EV_PWD_AUTH_FAIL:
+		hostapd_pwd_auth_fail(hapd, &data->pwd_auth_fail);
+		break;
+	case WPS_EV_PBC_OVERLAP:
+		hostapd_wps_event_pbc_overlap(hapd);
+		wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_OVERLAP);
+		break;
+	case WPS_EV_PBC_TIMEOUT:
+		hostapd_wps_event_pbc_timeout(hapd);
+		wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_TIMEOUT);
+		break;
+	case WPS_EV_PBC_ACTIVE:
+		hostapd_wps_event_pbc_active(hapd);
+		wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_ACTIVE);
+		break;
+	case WPS_EV_PBC_DISABLE:
+		hostapd_wps_event_pbc_disable(hapd);
+		wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_DISABLE);
+		break;
+	case WPS_EV_ER_AP_ADD:
+		break;
+	case WPS_EV_ER_AP_REMOVE:
+		break;
+	case WPS_EV_ER_ENROLLEE_ADD:
+		break;
+	case WPS_EV_ER_ENROLLEE_REMOVE:
+		break;
+	case WPS_EV_ER_AP_SETTINGS:
+		break;
+	case WPS_EV_ER_SET_SELECTED_REGISTRAR:
+		break;
+	case WPS_EV_AP_PIN_SUCCESS:
+		hostapd_wps_ap_pin_success(hapd);
+		break;
+	}
+	if (hapd->wps_event_cb)
+		hapd->wps_event_cb(hapd->wps_event_cb_ctx, event, data);
+}
+
+
+static int hostapd_wps_rf_band_cb(void *ctx)
+{
+	struct hostapd_data *hapd = ctx;
+
+	return hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ?
+		WPS_RF_50GHZ :
+		hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD ?
+		WPS_RF_60GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */
+}
+
+
+static void hostapd_wps_clear_ies(struct hostapd_data *hapd, int deinit_only)
+{
+	wpabuf_free(hapd->wps_beacon_ie);
+	hapd->wps_beacon_ie = NULL;
+
+	wpabuf_free(hapd->wps_probe_resp_ie);
+	hapd->wps_probe_resp_ie = NULL;
+
+	if (deinit_only) {
+		if (hapd->drv_priv)
+			hostapd_reset_ap_wps_ie(hapd);
+		return;
+	}
+
+	hostapd_set_ap_wps_ie(hapd);
+}
+
+
+static int get_uuid_cb(struct hostapd_iface *iface, void *ctx)
+{
+	const u8 **uuid = ctx;
+	size_t j;
+
+	if (iface == NULL)
+		return 0;
+	for (j = 0; j < iface->num_bss; j++) {
+		struct hostapd_data *hapd = iface->bss[j];
+		if (hapd->wps && !hapd->conf->wps_independent &&
+		    !is_nil_uuid(hapd->wps->uuid)) {
+			*uuid = hapd->wps->uuid;
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+
+static const u8 * get_own_uuid(struct hostapd_iface *iface)
+{
+	const u8 *uuid;
+	if (iface->interfaces == NULL ||
+	    iface->interfaces->for_each_interface == NULL)
+		return NULL;
+	uuid = NULL;
+	iface->interfaces->for_each_interface(iface->interfaces, get_uuid_cb,
+					      &uuid);
+	return uuid;
+}
+
+
+static int count_interface_cb(struct hostapd_iface *iface, void *ctx)
+{
+	int *count= ctx;
+	(*count)++;
+	return 0;
+}
+
+
+static int interface_count(struct hostapd_iface *iface)
+{
+	int count = 0;
+	if (iface->interfaces == NULL ||
+	    iface->interfaces->for_each_interface == NULL)
+		return 0;
+	iface->interfaces->for_each_interface(iface->interfaces,
+					      count_interface_cb, &count);
+	return count;
+}
+
+
+static int hostapd_wps_set_vendor_ext(struct hostapd_data *hapd,
+				      struct wps_context *wps)
+{
+	int i;
+
+	for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
+		wpabuf_free(wps->dev.vendor_ext[i]);
+		wps->dev.vendor_ext[i] = NULL;
+
+		if (hapd->conf->wps_vendor_ext[i] == NULL)
+			continue;
+
+		wps->dev.vendor_ext[i] =
+			wpabuf_dup(hapd->conf->wps_vendor_ext[i]);
+		if (wps->dev.vendor_ext[i] == NULL) {
+			while (--i >= 0)
+				wpabuf_free(wps->dev.vendor_ext[i]);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+static int hostapd_wps_set_application_ext(struct hostapd_data *hapd,
+					   struct wps_context *wps)
+{
+	wpabuf_free(wps->dev.application_ext);
+
+	if (!hapd->conf->wps_application_ext) {
+		wps->dev.application_ext = NULL;
+		return 0;
+	}
+
+	wps->dev.application_ext = wpabuf_dup(hapd->conf->wps_application_ext);
+	return wps->dev.application_ext ? 0 : -1;
+}
+
+
+static void hostapd_free_wps(struct wps_context *wps)
+{
+	int i;
+
+	for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++)
+		wpabuf_free(wps->dev.vendor_ext[i]);
+	wps_device_data_free(&wps->dev);
+	bin_clear_free(wps->network_key, wps->network_key_len);
+	hostapd_wps_nfc_clear(wps);
+	wpabuf_free(wps->dh_pubkey);
+	wpabuf_free(wps->dh_privkey);
+	forced_memzero(wps->psk, sizeof(wps->psk));
+	os_free(wps);
+}
+
+
+int hostapd_init_wps(struct hostapd_data *hapd,
+		     struct hostapd_bss_config *conf)
+{
+	struct wps_context *wps;
+	struct wps_registrar_config cfg;
+	u8 *multi_ap_netw_key = NULL;
+
+	if (conf->wps_state == 0) {
+		hostapd_wps_clear_ies(hapd, 0);
+		return 0;
+	}
+
+	wps = os_zalloc(sizeof(*wps));
+	if (wps == NULL)
+		return -1;
+
+	wps->cred_cb = hostapd_wps_cred_cb;
+	wps->event_cb = hostapd_wps_event_cb;
+	wps->rf_band_cb = hostapd_wps_rf_band_cb;
+	wps->cb_ctx = hapd;
+
+	os_memset(&cfg, 0, sizeof(cfg));
+	wps->wps_state = hapd->conf->wps_state;
+	wps->ap_setup_locked = hapd->conf->ap_setup_locked;
+	if (is_nil_uuid(hapd->conf->uuid)) {
+		const u8 *uuid;
+		uuid = get_own_uuid(hapd->iface);
+		if (uuid && !conf->wps_independent) {
+			os_memcpy(wps->uuid, uuid, UUID_LEN);
+			wpa_hexdump(MSG_DEBUG, "WPS: Clone UUID from another "
+				    "interface", wps->uuid, UUID_LEN);
+		} else {
+			uuid_gen_mac_addr(hapd->own_addr, wps->uuid);
+			wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC "
+				    "address", wps->uuid, UUID_LEN);
+		}
+	} else {
+		os_memcpy(wps->uuid, hapd->conf->uuid, UUID_LEN);
+		wpa_hexdump(MSG_DEBUG, "WPS: Use configured UUID",
+			    wps->uuid, UUID_LEN);
+	}
+	wps->ssid_len = hapd->conf->ssid.ssid_len;
+	os_memcpy(wps->ssid, hapd->conf->ssid.ssid, wps->ssid_len);
+	wps->ap = 1;
+	os_memcpy(wps->dev.mac_addr, hapd->own_addr, ETH_ALEN);
+	wps->dev.device_name = hapd->conf->device_name ?
+		os_strdup(hapd->conf->device_name) : NULL;
+	wps->dev.manufacturer = hapd->conf->manufacturer ?
+		os_strdup(hapd->conf->manufacturer) : NULL;
+	wps->dev.model_name = hapd->conf->model_name ?
+		os_strdup(hapd->conf->model_name) : NULL;
+	wps->dev.model_number = hapd->conf->model_number ?
+		os_strdup(hapd->conf->model_number) : NULL;
+	wps->dev.serial_number = hapd->conf->serial_number ?
+		os_strdup(hapd->conf->serial_number) : NULL;
+	wps->config_methods =
+		wps_config_methods_str2bin(hapd->conf->config_methods);
+	if ((wps->config_methods &
+	     (WPS_CONFIG_DISPLAY | WPS_CONFIG_VIRT_DISPLAY |
+	      WPS_CONFIG_PHY_DISPLAY)) == WPS_CONFIG_DISPLAY) {
+		wpa_printf(MSG_INFO, "WPS: Converting display to "
+			   "virtual_display for WPS 2.0 compliance");
+		wps->config_methods |= WPS_CONFIG_VIRT_DISPLAY;
+	}
+	if ((wps->config_methods &
+	     (WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_VIRT_PUSHBUTTON |
+	      WPS_CONFIG_PHY_PUSHBUTTON)) == WPS_CONFIG_PUSHBUTTON) {
+		wpa_printf(MSG_INFO, "WPS: Converting push_button to "
+			   "virtual_push_button for WPS 2.0 compliance");
+		wps->config_methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
+	}
+	os_memcpy(wps->dev.pri_dev_type, hapd->conf->device_type,
+		  WPS_DEV_TYPE_LEN);
+
+	if (hostapd_wps_set_vendor_ext(hapd, wps) < 0 ||
+	    hostapd_wps_set_application_ext(hapd, wps) < 0)
+		goto fail;
+
+	wps->dev.os_version = WPA_GET_BE32(hapd->conf->os_version);
+
+	if (conf->wps_rf_bands) {
+		wps->dev.rf_bands = conf->wps_rf_bands;
+	} else {
+		wps->dev.rf_bands =
+			hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ?
+			WPS_RF_50GHZ :
+			hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD ?
+			WPS_RF_60GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */
+	}
+
+	if (conf->wpa & WPA_PROTO_RSN) {
+		if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK)
+			wps->auth_types |= WPS_AUTH_WPA2PSK;
+		if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X)
+			wps->auth_types |= WPS_AUTH_WPA2;
+		if (conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE)
+			wps->auth_types |= WPS_AUTH_WPA2PSK;
+
+		if (conf->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP |
+					  WPA_CIPHER_CCMP_256 |
+					  WPA_CIPHER_GCMP_256)) {
+			wps->encr_types |= WPS_ENCR_AES;
+			wps->encr_types_rsn |= WPS_ENCR_AES;
+		} else if (conf->rsn_pairwise & WPA_CIPHER_TKIP) {
+#ifdef CONFIG_NO_TKIP
+			wpa_printf(MSG_INFO, "WPS: TKIP not supported");
+			goto fail;
+#else /* CONFIG_NO_TKIP */
+			wps->encr_types |= WPS_ENCR_TKIP;
+			wps->encr_types_rsn |= WPS_ENCR_TKIP;
+#endif /* CONFIG_NO_TKIP */
+		}
+	}
+
+	if (conf->wpa & WPA_PROTO_WPA) {
+#ifdef CONFIG_NO_TKIP
+		if (!(conf->wpa & WPA_PROTO_RSN)) {
+			wpa_printf(MSG_INFO, "WPS: WPA(v1) not supported");
+			goto fail;
+		}
+		conf->wpa &= ~WPA_PROTO_WPA;
+#else /* CONFIG_NO_TKIP */
+		if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK)
+			wps->auth_types |= WPS_AUTH_WPAPSK;
+		if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X)
+			wps->auth_types |= WPS_AUTH_WPA;
+
+		if (conf->wpa_pairwise & WPA_CIPHER_CCMP) {
+			wps->encr_types |= WPS_ENCR_AES;
+			wps->encr_types_wpa |= WPS_ENCR_AES;
+		}
+		if (conf->wpa_pairwise & WPA_CIPHER_TKIP) {
+			wps->encr_types |= WPS_ENCR_TKIP;
+			wps->encr_types_wpa |= WPS_ENCR_TKIP;
+		}
+#endif /* CONFIG_NO_TKIP */
+	}
+
+	if (conf->ssid.security_policy == SECURITY_PLAINTEXT) {
+		wps->encr_types |= WPS_ENCR_NONE;
+		wps->auth_types |= WPS_AUTH_OPEN;
+	}
+
+	if (conf->ssid.wpa_psk_file) {
+		/* Use per-device PSKs */
+	} else if (conf->ssid.wpa_passphrase) {
+		wps->network_key = (u8 *) os_strdup(conf->ssid.wpa_passphrase);
+		wps->network_key_len = os_strlen(conf->ssid.wpa_passphrase);
+	} else if (conf->ssid.wpa_psk) {
+		wps->network_key = os_malloc(2 * PMK_LEN + 1);
+		if (wps->network_key == NULL)
+			goto fail;
+		wpa_snprintf_hex((char *) wps->network_key, 2 * PMK_LEN + 1,
+				 conf->ssid.wpa_psk->psk, PMK_LEN);
+		wps->network_key_len = 2 * PMK_LEN;
+#ifdef CONFIG_WEP
+	} else if (conf->ssid.wep.keys_set && conf->ssid.wep.key[0]) {
+		wps->network_key = os_malloc(conf->ssid.wep.len[0]);
+		if (wps->network_key == NULL)
+			goto fail;
+		os_memcpy(wps->network_key, conf->ssid.wep.key[0],
+			  conf->ssid.wep.len[0]);
+		wps->network_key_len = conf->ssid.wep.len[0];
+#endif /* CONFIG_WEP */
+	}
+
+	if (conf->ssid.wpa_psk) {
+		os_memcpy(wps->psk, conf->ssid.wpa_psk->psk, PMK_LEN);
+		wps->psk_set = 1;
+	}
+
+	wps->ap_auth_type = wps->auth_types;
+	wps->ap_encr_type = wps->encr_types;
+	if (conf->wps_state == WPS_STATE_NOT_CONFIGURED) {
+		/* Override parameters to enable security by default */
+#ifdef CONFIG_NO_TKIP
+		wps->auth_types = WPS_AUTH_WPA2PSK;
+		wps->encr_types = WPS_ENCR_AES;
+		wps->encr_types_rsn = WPS_ENCR_AES;
+		wps->encr_types_wpa = WPS_ENCR_AES;
+#else /* CONFIG_NO_TKIP */
+		wps->auth_types = WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK;
+		wps->encr_types = WPS_ENCR_AES | WPS_ENCR_TKIP;
+		wps->encr_types_rsn = WPS_ENCR_AES | WPS_ENCR_TKIP;
+		wps->encr_types_wpa = WPS_ENCR_AES | WPS_ENCR_TKIP;
+#endif /* CONFIG_NO_TKIP */
+	}
+
+	if ((hapd->conf->multi_ap & FRONTHAUL_BSS) &&
+	    hapd->conf->multi_ap_backhaul_ssid.ssid_len) {
+		cfg.multi_ap_backhaul_ssid_len =
+			hapd->conf->multi_ap_backhaul_ssid.ssid_len;
+		cfg.multi_ap_backhaul_ssid =
+			hapd->conf->multi_ap_backhaul_ssid.ssid;
+
+		if (conf->multi_ap_backhaul_ssid.wpa_passphrase) {
+			cfg.multi_ap_backhaul_network_key = (const u8 *)
+				conf->multi_ap_backhaul_ssid.wpa_passphrase;
+			cfg.multi_ap_backhaul_network_key_len =
+				os_strlen(conf->multi_ap_backhaul_ssid.wpa_passphrase);
+		} else if (conf->multi_ap_backhaul_ssid.wpa_psk) {
+			multi_ap_netw_key = os_malloc(2 * PMK_LEN + 1);
+			if (!multi_ap_netw_key)
+				goto fail;
+			wpa_snprintf_hex((char *) multi_ap_netw_key,
+					 2 * PMK_LEN + 1,
+					 conf->multi_ap_backhaul_ssid.wpa_psk->psk,
+					 PMK_LEN);
+			cfg.multi_ap_backhaul_network_key = multi_ap_netw_key;
+			cfg.multi_ap_backhaul_network_key_len = 2 * PMK_LEN;
+		}
+	}
+
+	wps->ap_settings = conf->ap_settings;
+	wps->ap_settings_len = conf->ap_settings_len;
+
+	cfg.new_psk_cb = hostapd_wps_new_psk_cb;
+	cfg.set_ie_cb = hostapd_wps_set_ie_cb;
+	cfg.pin_needed_cb = hostapd_wps_pin_needed_cb;
+	cfg.reg_success_cb = hostapd_wps_reg_success_cb;
+	cfg.enrollee_seen_cb = hostapd_wps_enrollee_seen_cb;
+	cfg.lookup_pskfile_cb = hostapd_wps_lookup_pskfile_cb;
+	cfg.cb_ctx = hapd;
+	cfg.skip_cred_build = conf->skip_cred_build;
+	cfg.extra_cred = conf->extra_cred;
+	cfg.extra_cred_len = conf->extra_cred_len;
+	cfg.disable_auto_conf = (hapd->conf->wps_cred_processing == 1) &&
+		conf->skip_cred_build;
+	cfg.dualband = interface_count(hapd->iface) > 1;
+	if ((wps->dev.rf_bands & (WPS_RF_50GHZ | WPS_RF_24GHZ)) ==
+	    (WPS_RF_50GHZ | WPS_RF_24GHZ))
+		cfg.dualband = 1;
+	if (cfg.dualband)
+		wpa_printf(MSG_DEBUG, "WPS: Dualband AP");
+	cfg.force_per_enrollee_psk = conf->force_per_enrollee_psk;
+
+	wps->registrar = wps_registrar_init(wps, &cfg);
+	if (wps->registrar == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to initialize WPS Registrar");
+		goto fail;
+	}
+
+#ifdef CONFIG_WPS_UPNP
+	wps->friendly_name = hapd->conf->friendly_name;
+	wps->manufacturer_url = hapd->conf->manufacturer_url;
+	wps->model_description = hapd->conf->model_description;
+	wps->model_url = hapd->conf->model_url;
+	wps->upc = hapd->conf->upc;
+#endif /* CONFIG_WPS_UPNP */
+
+	hostapd_register_probereq_cb(hapd, hostapd_wps_probe_req_rx, hapd);
+
+#ifdef CONFIG_P2P
+	if ((hapd->conf->p2p & P2P_ENABLED) &&
+	    is_6ghz_op_class(hapd->iconf->op_class))
+		wps->use_passphrase = true;
+#endif /* CONFIG_P2P */
+	hapd->wps = wps;
+	bin_clear_free(multi_ap_netw_key, 2 * PMK_LEN);
+
+	return 0;
+
+fail:
+	bin_clear_free(multi_ap_netw_key, 2 * PMK_LEN);
+	hostapd_free_wps(wps);
+	return -1;
+}
+
+
+int hostapd_init_wps_complete(struct hostapd_data *hapd)
+{
+	struct wps_context *wps = hapd->wps;
+
+	if (wps == NULL)
+		return 0;
+
+#ifdef CONFIG_WPS_UPNP
+	if (hostapd_wps_upnp_init(hapd, wps) < 0) {
+		wpa_printf(MSG_ERROR, "Failed to initialize WPS UPnP");
+		wps_registrar_deinit(wps->registrar);
+		hostapd_free_wps(wps);
+		hapd->wps = NULL;
+		return -1;
+	}
+#endif /* CONFIG_WPS_UPNP */
+
+	return 0;
+}
+
+
+static void hostapd_wps_nfc_clear(struct wps_context *wps)
+{
+#ifdef CONFIG_WPS_NFC
+	wpa_printf(MSG_DEBUG, "WPS: Clear NFC Tag context %p", wps);
+	wps->ap_nfc_dev_pw_id = 0;
+	wpabuf_free(wps->ap_nfc_dh_pubkey);
+	wps->ap_nfc_dh_pubkey = NULL;
+	wpabuf_free(wps->ap_nfc_dh_privkey);
+	wps->ap_nfc_dh_privkey = NULL;
+	wpabuf_free(wps->ap_nfc_dev_pw);
+	wps->ap_nfc_dev_pw = NULL;
+#endif /* CONFIG_WPS_NFC */
+}
+
+
+static int hostapd_wps_update_multi_ap(struct hostapd_data *hapd,
+				       struct wps_registrar *reg)
+{
+	struct hostapd_bss_config *conf = hapd->conf;
+	u8 *multi_ap_backhaul_network_key = NULL;
+	size_t multi_ap_backhaul_network_key_len = 0;
+	int ret;
+
+	if (!(conf->multi_ap & FRONTHAUL_BSS) ||
+	    !conf->multi_ap_backhaul_ssid.ssid_len)
+		return 0;
+
+	if (conf->multi_ap_backhaul_ssid.wpa_passphrase) {
+		multi_ap_backhaul_network_key =
+			(u8 *) os_strdup(
+				conf->multi_ap_backhaul_ssid.wpa_passphrase);
+		if (!multi_ap_backhaul_network_key)
+			return -1;
+		multi_ap_backhaul_network_key_len =
+			os_strlen(conf->multi_ap_backhaul_ssid.wpa_passphrase);
+	} else if (conf->multi_ap_backhaul_ssid.wpa_psk) {
+		multi_ap_backhaul_network_key = os_malloc(2 * PMK_LEN + 1);
+		if (!multi_ap_backhaul_network_key)
+			return -1;
+		wpa_snprintf_hex((char *) multi_ap_backhaul_network_key,
+				 2 * PMK_LEN + 1,
+				 conf->multi_ap_backhaul_ssid.wpa_psk->psk,
+				 PMK_LEN);
+		multi_ap_backhaul_network_key_len = 2 * PMK_LEN;
+	}
+
+	ret = wps_registrar_update_multi_ap(
+		reg, conf->multi_ap_backhaul_ssid.ssid,
+		conf->multi_ap_backhaul_ssid.ssid_len,
+		multi_ap_backhaul_network_key,
+		multi_ap_backhaul_network_key_len);
+	os_free(multi_ap_backhaul_network_key);
+
+	return ret;
+}
+
+
+void hostapd_deinit_wps(struct hostapd_data *hapd)
+{
+	eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
+	eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL);
+	eloop_cancel_timeout(wps_reload_config, hapd->iface, NULL);
+	if (hapd->wps == NULL) {
+		hostapd_wps_clear_ies(hapd, 1);
+		return;
+	}
+#ifdef CONFIG_WPS_UPNP
+	hostapd_wps_upnp_deinit(hapd);
+#endif /* CONFIG_WPS_UPNP */
+	wps_registrar_deinit(hapd->wps->registrar);
+	wps_free_pending_msgs(hapd->wps->upnp_msgs);
+	hostapd_free_wps(hapd->wps);
+	hapd->wps = NULL;
+	hostapd_wps_clear_ies(hapd, 1);
+}
+
+
+void hostapd_update_wps(struct hostapd_data *hapd)
+{
+	struct wps_context *wps = hapd->wps;
+	struct hostapd_bss_config *conf = hapd->conf;
+
+	if (!wps)
+		return;
+
+#ifdef CONFIG_WPS_UPNP
+	wps->friendly_name = conf->friendly_name;
+	wps->manufacturer_url = conf->manufacturer_url;
+	wps->model_description = conf->model_description;
+	wps->model_url = conf->model_url;
+	wps->upc = conf->upc;
+#endif /* CONFIG_WPS_UPNP */
+
+	os_memcpy(wps->ssid, conf->ssid.ssid, conf->ssid.ssid_len);
+	wps->ssid_len = conf->ssid.ssid_len;
+
+	/* Clear WPS settings, then fill them again */
+	os_free(wps->network_key);
+	wps->network_key = NULL;
+	wps->network_key_len = 0;
+	wps->psk_set = 0;
+	if (conf->ssid.wpa_psk_file) {
+		/* Use per-device PSKs */
+	} else if (conf->ssid.wpa_passphrase) {
+		wps->network_key = (u8 *) os_strdup(conf->ssid.wpa_passphrase);
+		if (!wps->network_key)
+			return;
+		wps->network_key_len = os_strlen(conf->ssid.wpa_passphrase);
+	} else if (conf->ssid.wpa_psk) {
+		wps->network_key = os_malloc(2 * PMK_LEN + 1);
+		if (!wps->network_key)
+			return;
+		wpa_snprintf_hex((char *) wps->network_key, 2 * PMK_LEN + 1,
+				 conf->ssid.wpa_psk->psk, PMK_LEN);
+		wps->network_key_len = 2 * PMK_LEN;
+#ifdef CONFIG_WEP
+	} else if (conf->ssid.wep.keys_set && conf->ssid.wep.key[0]) {
+		wps->network_key = os_malloc(conf->ssid.wep.len[0]);
+		if (!wps->network_key)
+			return;
+		os_memcpy(wps->network_key, conf->ssid.wep.key[0],
+			  conf->ssid.wep.len[0]);
+		wps->network_key_len = conf->ssid.wep.len[0];
+#endif /* CONFIG_WEP */
+	}
+
+	if (conf->ssid.wpa_psk) {
+		os_memcpy(wps->psk, conf->ssid.wpa_psk->psk, PMK_LEN);
+		wps->psk_set = 1;
+	}
+
+	hostapd_wps_update_multi_ap(hapd, wps->registrar);
+
+	hostapd_wps_set_vendor_ext(hapd, wps);
+	hostapd_wps_set_application_ext(hapd, wps);
+
+	if (conf->wps_state)
+		wps_registrar_update_ie(wps->registrar);
+	else
+		hostapd_deinit_wps(hapd);
+}
+
+
+struct wps_add_pin_data {
+	const u8 *addr;
+	const u8 *uuid;
+	const u8 *pin;
+	size_t pin_len;
+	int timeout;
+	int added;
+};
+
+
+static int wps_add_pin(struct hostapd_data *hapd, void *ctx)
+{
+	struct wps_add_pin_data *data = ctx;
+	int ret;
+
+	if (hapd->wps == NULL)
+		return 0;
+	ret = wps_registrar_add_pin(hapd->wps->registrar, data->addr,
+				    data->uuid, data->pin, data->pin_len,
+				    data->timeout);
+	if (ret == 0)
+		data->added++;
+	return ret;
+}
+
+
+int hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *addr,
+			const char *uuid, const char *pin, int timeout)
+{
+	u8 u[UUID_LEN];
+	struct wps_add_pin_data data;
+
+	data.addr = addr;
+	data.uuid = u;
+	data.pin = (const u8 *) pin;
+	data.pin_len = os_strlen(pin);
+	data.timeout = timeout;
+	data.added = 0;
+
+	if (os_strcmp(uuid, "any") == 0)
+		data.uuid = NULL;
+	else {
+		if (uuid_str2bin(uuid, u))
+			return -1;
+		data.uuid = u;
+	}
+	if (hostapd_wps_for_each(hapd, wps_add_pin, &data) < 0)
+		return -1;
+	return data.added ? 0 : -1;
+}
+
+
+struct wps_button_pushed_ctx {
+	const u8 *p2p_dev_addr;
+	unsigned int count;
+};
+
+static int wps_button_pushed(struct hostapd_data *hapd, void *ctx)
+{
+	struct wps_button_pushed_ctx *data = ctx;
+
+	if (hapd->wps) {
+		data->count++;
+		return wps_registrar_button_pushed(hapd->wps->registrar,
+						   data->p2p_dev_addr);
+	}
+
+	return 0;
+}
+
+
+int hostapd_wps_button_pushed(struct hostapd_data *hapd,
+			      const u8 *p2p_dev_addr)
+{
+	struct wps_button_pushed_ctx ctx;
+	int ret;
+
+	os_memset(&ctx, 0, sizeof(ctx));
+	ctx.p2p_dev_addr = p2p_dev_addr;
+	ret = hostapd_wps_for_each(hapd, wps_button_pushed, &ctx);
+	if (ret == 0 && !ctx.count)
+		ret = -1;
+	return ret;
+}
+
+
+struct wps_cancel_ctx {
+	unsigned int count;
+};
+
+static int wps_cancel(struct hostapd_data *hapd, void *ctx)
+{
+	struct wps_cancel_ctx *data = ctx;
+
+	if (hapd->wps) {
+		data->count++;
+		wps_registrar_wps_cancel(hapd->wps->registrar);
+		ap_for_each_sta(hapd, ap_sta_wps_cancel, NULL);
+		wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_CANCEL);
+	}
+
+	return 0;
+}
+
+
+int hostapd_wps_cancel(struct hostapd_data *hapd)
+{
+	struct wps_cancel_ctx ctx;
+	int ret;
+
+	os_memset(&ctx, 0, sizeof(ctx));
+	ret = hostapd_wps_for_each(hapd, wps_cancel, &ctx);
+	if (ret == 0 && !ctx.count)
+		ret = -1;
+	return ret;
+}
+
+
+static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, const u8 *da,
+				    const u8 *bssid,
+				    const u8 *ie, size_t ie_len,
+				    int ssi_signal)
+{
+	struct hostapd_data *hapd = ctx;
+	struct wpabuf *wps_ie;
+	struct ieee802_11_elems elems;
+#ifdef CONFIG_WPS_5G_PREFER
+	int ignore_probe = 0;
+#endif /* CONFIG_WPS_5G_PREFER */
+
+	if (hapd->wps == NULL)
+		return 0;
+
+	if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) {
+		wpa_printf(MSG_DEBUG, "WPS: Could not parse ProbeReq from "
+			   MACSTR, MAC2STR(addr));
+		return 0;
+	}
+
+	if (elems.ssid && elems.ssid_len > 0 &&
+	    (elems.ssid_len != hapd->conf->ssid.ssid_len ||
+	     os_memcmp(elems.ssid, hapd->conf->ssid.ssid, elems.ssid_len) !=
+	     0))
+		return 0; /* Not for us */
+
+	wps_ie = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA);
+	if (wps_ie == NULL)
+		return 0;
+	if (wps_validate_probe_req(wps_ie, addr) < 0) {
+		wpabuf_free(wps_ie);
+		return 0;
+	}
+
+	if (wpabuf_len(wps_ie) > 0) {
+		int p2p_wildcard = 0;
+#ifdef CONFIG_P2P
+		if (elems.ssid && elems.ssid_len == P2P_WILDCARD_SSID_LEN &&
+		    os_memcmp(elems.ssid, P2P_WILDCARD_SSID,
+			      P2P_WILDCARD_SSID_LEN) == 0)
+			p2p_wildcard = 1;
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_WPS_5G_PREFER
+		ignore_probe = wps_registrar_probe_req_rx(hapd->wps->registrar, addr, wps_ie,
+					     p2p_wildcard, hapd->iface->freq);
+#else
+		wps_registrar_probe_req_rx(hapd->wps->registrar, addr, wps_ie,
+					     p2p_wildcard);
+#endif /* CONFIG_WPS_5G_PREFER */
+
+#ifdef CONFIG_WPS_UPNP
+		/* FIX: what exactly should be included in the WLANEvent?
+		 * WPS attributes? Full ProbeReq frame? */
+		if (!p2p_wildcard)
+			upnp_wps_device_send_wlan_event(
+				hapd->wps_upnp, addr,
+				UPNP_WPS_WLANEVENT_TYPE_PROBE, wps_ie);
+#endif /* CONFIG_WPS_UPNP */
+	}
+
+	wpabuf_free(wps_ie);
+
+#ifdef CONFIG_WPS_5G_PREFER
+	wpa_printf(MSG_DEBUG, "WPS: hostapd_wps_probe_req_rx: ignore_probe=%d ", ignore_probe);
+	if (ignore_probe)
+		return 1;
+#endif /* CONFIG_WPS_5G_PREFER */
+
+	return 0;
+}
+
+
+#ifdef CONFIG_WPS_UPNP
+
+static int hostapd_rx_req_put_wlan_response(
+	void *priv, enum upnp_wps_wlanevent_type ev_type,
+	const u8 *mac_addr, const struct wpabuf *msg,
+	enum wps_msg_type msg_type)
+{
+	struct hostapd_data *hapd = priv;
+	struct sta_info *sta;
+	struct upnp_pending_message *p;
+
+	wpa_printf(MSG_DEBUG, "WPS UPnP: PutWLANResponse ev_type=%d mac_addr="
+		   MACSTR, ev_type, MAC2STR(mac_addr));
+	wpa_hexdump(MSG_MSGDUMP, "WPS UPnP: PutWLANResponse NewMessage",
+		    wpabuf_head(msg), wpabuf_len(msg));
+	if (ev_type != UPNP_WPS_WLANEVENT_TYPE_EAP) {
+		wpa_printf(MSG_DEBUG, "WPS UPnP: Ignored unexpected "
+			   "PutWLANResponse WLANEventType %d", ev_type);
+		return -1;
+	}
+
+	/*
+	 * EAP response to ongoing to WPS Registration. Send it to EAP-WSC
+	 * server implementation for delivery to the peer.
+	 */
+
+	sta = ap_get_sta(hapd, mac_addr);
+#ifndef CONFIG_WPS_STRICT
+	if (!sta) {
+		/*
+		 * Workaround - Intel wsccmd uses bogus NewWLANEventMAC:
+		 * Pick STA that is in an ongoing WPS registration without
+		 * checking the MAC address.
+		 */
+		wpa_printf(MSG_DEBUG, "WPS UPnP: No matching STA found based "
+			   "on NewWLANEventMAC; try wildcard match");
+		for (sta = hapd->sta_list; sta; sta = sta->next) {
+			if (sta->eapol_sm && (sta->flags & WLAN_STA_WPS))
+				break;
+		}
+	}
+#endif /* CONFIG_WPS_STRICT */
+
+	if (!sta || !(sta->flags & WLAN_STA_WPS)) {
+		wpa_printf(MSG_DEBUG, "WPS UPnP: No matching STA found");
+		return 0;
+	}
+
+	if (!sta->eapol_sm) {
+		/*
+		 * This can happen, e.g., if an ER sends an extra message after
+		 * the station has disassociated (but not fully
+		 * deauthenticated).
+		 */
+		wpa_printf(MSG_DEBUG, "WPS UPnP: Matching STA did not have EAPOL state machine initialized");
+		return 0;
+	}
+
+	p = os_zalloc(sizeof(*p));
+	if (p == NULL)
+		return -1;
+	os_memcpy(p->addr, sta->addr, ETH_ALEN);
+	p->msg = wpabuf_dup(msg);
+	p->type = msg_type;
+	p->next = hapd->wps->upnp_msgs;
+	hapd->wps->upnp_msgs = p;
+
+	return eapol_auth_eap_pending_cb(sta->eapol_sm, sta->eapol_sm->eap);
+}
+
+
+static int hostapd_wps_upnp_init(struct hostapd_data *hapd,
+				 struct wps_context *wps)
+{
+	struct upnp_wps_device_ctx *ctx;
+
+	if (!hapd->conf->upnp_iface)
+		return 0;
+	ctx = os_zalloc(sizeof(*ctx));
+	if (ctx == NULL)
+		return -1;
+
+	ctx->rx_req_put_wlan_response = hostapd_rx_req_put_wlan_response;
+	if (hapd->conf->ap_pin)
+		ctx->ap_pin = os_strdup(hapd->conf->ap_pin);
+
+	hapd->wps_upnp = upnp_wps_device_init(ctx, wps, hapd,
+					      hapd->conf->upnp_iface);
+	if (hapd->wps_upnp == NULL)
+		return -1;
+	wps->wps_upnp = hapd->wps_upnp;
+
+	return 0;
+}
+
+
+static void hostapd_wps_upnp_deinit(struct hostapd_data *hapd)
+{
+	upnp_wps_device_deinit(hapd->wps_upnp, hapd);
+}
+
+#endif /* CONFIG_WPS_UPNP */
+
+
+int hostapd_wps_get_mib_sta(struct hostapd_data *hapd, const u8 *addr,
+			    char *buf, size_t buflen)
+{
+	if (hapd->wps == NULL)
+		return 0;
+	return wps_registrar_get_info(hapd->wps->registrar, addr, buf, buflen);
+}
+
+
+static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx)
+{
+	struct hostapd_data *hapd = eloop_data;
+	wpa_printf(MSG_DEBUG, "WPS: AP PIN timed out");
+	hostapd_wps_ap_pin_disable(hapd);
+	wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_PIN_DISABLED);
+}
+
+
+static void hostapd_wps_ap_pin_enable(struct hostapd_data *hapd, int timeout)
+{
+	wpa_printf(MSG_DEBUG, "WPS: Enabling AP PIN (timeout=%d)", timeout);
+	hapd->ap_pin_failures = 0;
+	hapd->ap_pin_failures_consecutive = 0;
+	hapd->conf->ap_setup_locked = 0;
+	if (hapd->wps->ap_setup_locked) {
+		wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_UNLOCKED);
+		hapd->wps->ap_setup_locked = 0;
+		wps_registrar_update_ie(hapd->wps->registrar);
+	}
+	eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL);
+	if (timeout > 0)
+		eloop_register_timeout(timeout, 0,
+				       hostapd_wps_ap_pin_timeout, hapd, NULL);
+}
+
+
+static int wps_ap_pin_disable(struct hostapd_data *hapd, void *ctx)
+{
+	os_free(hapd->conf->ap_pin);
+	hapd->conf->ap_pin = NULL;
+#ifdef CONFIG_WPS_UPNP
+	upnp_wps_set_ap_pin(hapd->wps_upnp, NULL);
+#endif /* CONFIG_WPS_UPNP */
+	eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL);
+	return 0;
+}
+
+
+void hostapd_wps_ap_pin_disable(struct hostapd_data *hapd)
+{
+	wpa_printf(MSG_DEBUG, "WPS: Disabling AP PIN");
+	hostapd_wps_for_each(hapd, wps_ap_pin_disable, NULL);
+}
+
+
+struct wps_ap_pin_data {
+	char pin_txt[9];
+	int timeout;
+};
+
+
+static int wps_ap_pin_set(struct hostapd_data *hapd, void *ctx)
+{
+	struct wps_ap_pin_data *data = ctx;
+
+	if (!hapd->wps)
+		return 0;
+
+	os_free(hapd->conf->ap_pin);
+	hapd->conf->ap_pin = os_strdup(data->pin_txt);
+#ifdef CONFIG_WPS_UPNP
+	upnp_wps_set_ap_pin(hapd->wps_upnp, data->pin_txt);
+#endif /* CONFIG_WPS_UPNP */
+	hostapd_wps_ap_pin_enable(hapd, data->timeout);
+	return 0;
+}
+
+
+const char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout)
+{
+	unsigned int pin;
+	struct wps_ap_pin_data data;
+
+	if (wps_generate_pin(&pin) < 0)
+		return NULL;
+	os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%08u", pin);
+	data.timeout = timeout;
+	hostapd_wps_for_each(hapd, wps_ap_pin_set, &data);
+	return hapd->conf->ap_pin;
+}
+
+
+const char * hostapd_wps_ap_pin_get(struct hostapd_data *hapd)
+{
+	return hapd->conf->ap_pin;
+}
+
+
+int hostapd_wps_ap_pin_set(struct hostapd_data *hapd, const char *pin,
+			   int timeout)
+{
+	struct wps_ap_pin_data data;
+	int ret;
+
+	ret = os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%s", pin);
+	if (os_snprintf_error(sizeof(data.pin_txt), ret))
+		return -1;
+	data.timeout = timeout;
+	return hostapd_wps_for_each(hapd, wps_ap_pin_set, &data);
+}
+
+
+static int wps_update_ie(struct hostapd_data *hapd, void *ctx)
+{
+	if (hapd->wps)
+		wps_registrar_update_ie(hapd->wps->registrar);
+	return 0;
+}
+
+
+void hostapd_wps_update_ie(struct hostapd_data *hapd)
+{
+	hostapd_wps_for_each(hapd, wps_update_ie, NULL);
+}
+
+
+int hostapd_wps_config_ap(struct hostapd_data *hapd, const char *ssid,
+			  const char *auth, const char *encr, const char *key)
+{
+	struct wps_credential cred;
+	size_t len;
+
+	os_memset(&cred, 0, sizeof(cred));
+
+	len = os_strlen(ssid);
+	if ((len & 1) || len > 2 * sizeof(cred.ssid) ||
+	    hexstr2bin(ssid, cred.ssid, len / 2))
+		return -1;
+	cred.ssid_len = len / 2;
+
+	if (os_strncmp(auth, "OPEN", 4) == 0)
+		cred.auth_type = WPS_AUTH_OPEN;
+#ifndef CONFIG_NO_TKIP
+	else if (os_strncmp(auth, "WPAPSK", 6) == 0)
+		cred.auth_type = WPS_AUTH_WPAPSK;
+#endif /* CONFIG_NO_TKIP */
+	else if (os_strncmp(auth, "WPA2PSK", 7) == 0)
+		cred.auth_type = WPS_AUTH_WPA2PSK;
+	else
+		return -1;
+
+	if (encr) {
+		if (os_strncmp(encr, "NONE", 4) == 0)
+			cred.encr_type = WPS_ENCR_NONE;
+#ifndef CONFIG_NO_TKIP
+		else if (os_strncmp(encr, "TKIP", 4) == 0)
+			cred.encr_type = WPS_ENCR_TKIP;
+#endif /* CONFIG_NO_TKIP */
+		else if (os_strncmp(encr, "CCMP", 4) == 0)
+			cred.encr_type = WPS_ENCR_AES;
+		else
+			return -1;
+	} else
+		cred.encr_type = WPS_ENCR_NONE;
+
+	if (key) {
+		len = os_strlen(key);
+		if ((len & 1) || len > 2 * sizeof(cred.key) ||
+		    hexstr2bin(key, cred.key, len / 2))
+			return -1;
+		cred.key_len = len / 2;
+	}
+
+	if (!hapd->wps) {
+		wpa_printf(MSG_ERROR, "WPS: WPS config does not exist");
+		return -1;
+	}
+
+	return wps_registrar_config_ap(hapd->wps->registrar, &cred);
+}
+
+
+#ifdef CONFIG_WPS_NFC
+
+struct wps_nfc_password_token_data {
+	const u8 *oob_dev_pw;
+	size_t oob_dev_pw_len;
+	int added;
+};
+
+
+static int wps_add_nfc_password_token(struct hostapd_data *hapd, void *ctx)
+{
+	struct wps_nfc_password_token_data *data = ctx;
+	int ret;
+
+	if (hapd->wps == NULL)
+		return 0;
+	ret = wps_registrar_add_nfc_password_token(hapd->wps->registrar,
+						   data->oob_dev_pw,
+						   data->oob_dev_pw_len);
+	if (ret == 0)
+		data->added++;
+	return ret;
+}
+
+
+static int hostapd_wps_add_nfc_password_token(struct hostapd_data *hapd,
+					      struct wps_parse_attr *attr)
+{
+	struct wps_nfc_password_token_data data;
+
+	data.oob_dev_pw = attr->oob_dev_password;
+	data.oob_dev_pw_len = attr->oob_dev_password_len;
+	data.added = 0;
+	if (hostapd_wps_for_each(hapd, wps_add_nfc_password_token, &data) < 0)
+		return -1;
+	return data.added ? 0 : -1;
+}
+
+
+static int hostapd_wps_nfc_tag_process(struct hostapd_data *hapd,
+				       const struct wpabuf *wps)
+{
+	struct wps_parse_attr attr;
+
+	wpa_hexdump_buf(MSG_DEBUG, "WPS: Received NFC tag payload", wps);
+
+	if (wps_parse_msg(wps, &attr)) {
+		wpa_printf(MSG_DEBUG, "WPS: Ignore invalid data from NFC tag");
+		return -1;
+	}
+
+	if (attr.oob_dev_password)
+		return hostapd_wps_add_nfc_password_token(hapd, &attr);
+
+	wpa_printf(MSG_DEBUG, "WPS: Ignore unrecognized NFC tag");
+	return -1;
+}
+
+
+int hostapd_wps_nfc_tag_read(struct hostapd_data *hapd,
+			     const struct wpabuf *data)
+{
+	const struct wpabuf *wps = data;
+	struct wpabuf *tmp = NULL;
+	int ret;
+
+	if (wpabuf_len(data) < 4)
+		return -1;
+
+	if (*wpabuf_head_u8(data) != 0x10) {
+		/* Assume this contains full NDEF record */
+		tmp = ndef_parse_wifi(data);
+		if (tmp == NULL) {
+			wpa_printf(MSG_DEBUG, "WPS: Could not parse NDEF");
+			return -1;
+		}
+		wps = tmp;
+	}
+
+	ret = hostapd_wps_nfc_tag_process(hapd, wps);
+	wpabuf_free(tmp);
+	return ret;
+}
+
+
+struct wpabuf * hostapd_wps_nfc_config_token(struct hostapd_data *hapd,
+					     int ndef)
+{
+	struct wpabuf *ret;
+
+	if (hapd->wps == NULL)
+		return NULL;
+
+	ret = wps_get_oob_cred(hapd->wps, hostapd_wps_rf_band_cb(hapd),
+			       hapd->iconf->channel);
+	if (ndef && ret) {
+		struct wpabuf *tmp;
+		tmp = ndef_build_wifi(ret);
+		wpabuf_free(ret);
+		if (tmp == NULL)
+			return NULL;
+		ret = tmp;
+	}
+
+	return ret;
+}
+
+
+struct wpabuf * hostapd_wps_nfc_hs_cr(struct hostapd_data *hapd, int ndef)
+{
+	struct wpabuf *ret;
+
+	if (hapd->wps == NULL)
+		return NULL;
+
+	if (hapd->conf->wps_nfc_dh_pubkey == NULL) {
+		struct wps_context *wps = hapd->wps;
+		if (wps_nfc_gen_dh(&hapd->conf->wps_nfc_dh_pubkey,
+				   &hapd->conf->wps_nfc_dh_privkey) < 0)
+			return NULL;
+		hostapd_wps_nfc_clear(wps);
+		wps->ap_nfc_dev_pw_id = DEV_PW_NFC_CONNECTION_HANDOVER;
+		wps->ap_nfc_dh_pubkey =
+			wpabuf_dup(hapd->conf->wps_nfc_dh_pubkey);
+		wps->ap_nfc_dh_privkey =
+			wpabuf_dup(hapd->conf->wps_nfc_dh_privkey);
+		if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey) {
+			hostapd_wps_nfc_clear(wps);
+			return NULL;
+		}
+	}
+
+	ret = wps_build_nfc_handover_sel(hapd->wps,
+					 hapd->conf->wps_nfc_dh_pubkey,
+					 hapd->own_addr, hapd->iface->freq);
+
+	if (ndef && ret) {
+		struct wpabuf *tmp;
+		tmp = ndef_build_wifi(ret);
+		wpabuf_free(ret);
+		if (tmp == NULL)
+			return NULL;
+		ret = tmp;
+	}
+
+	return ret;
+}
+
+
+int hostapd_wps_nfc_report_handover(struct hostapd_data *hapd,
+				    const struct wpabuf *req,
+				    const struct wpabuf *sel)
+{
+	struct wpabuf *wps;
+	int ret = -1;
+	u16 wsc_len;
+	const u8 *pos;
+	struct wpabuf msg;
+	struct wps_parse_attr attr;
+	u16 dev_pw_id;
+
+	/*
+	 * Enrollee/station is always initiator of the NFC connection handover,
+	 * so use the request message here to find Enrollee public key hash.
+	 */
+	wps = ndef_parse_wifi(req);
+	if (wps == NULL)
+		return -1;
+	wpa_printf(MSG_DEBUG, "WPS: Received application/vnd.wfa.wsc "
+		   "payload from NFC connection handover");
+	wpa_hexdump_buf(MSG_DEBUG, "WPS: NFC payload", wps);
+	if (wpabuf_len(wps) < 2) {
+		wpa_printf(MSG_DEBUG, "WPS: Too short Wi-Fi Handover Request "
+			   "Message");
+		goto out;
+	}
+	pos = wpabuf_head(wps);
+	wsc_len = WPA_GET_BE16(pos);
+	if (wsc_len > wpabuf_len(wps) - 2) {
+		wpa_printf(MSG_DEBUG, "WPS: Invalid WSC attribute length (%u) "
+			   "in rt Wi-Fi Handover Request Message", wsc_len);
+		goto out;
+	}
+	pos += 2;
+
+	wpa_hexdump(MSG_DEBUG,
+		    "WPS: WSC attributes in Wi-Fi Handover Request Message",
+		    pos, wsc_len);
+	if (wsc_len < wpabuf_len(wps) - 2) {
+		wpa_hexdump(MSG_DEBUG,
+			    "WPS: Ignore extra data after WSC attributes",
+			    pos + wsc_len, wpabuf_len(wps) - 2 - wsc_len);
+	}
+
+	wpabuf_set(&msg, pos, wsc_len);
+	ret = wps_parse_msg(&msg, &attr);
+	if (ret < 0) {
+		wpa_printf(MSG_DEBUG, "WPS: Could not parse WSC attributes in "
+			   "Wi-Fi Handover Request Message");
+		goto out;
+	}
+
+	if (attr.oob_dev_password == NULL ||
+	    attr.oob_dev_password_len < WPS_OOB_PUBKEY_HASH_LEN + 2) {
+		wpa_printf(MSG_DEBUG, "WPS: No Out-of-Band Device Password "
+			   "included in Wi-Fi Handover Request Message");
+		ret = -1;
+		goto out;
+	}
+
+	if (attr.uuid_e == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No UUID-E included in Wi-Fi "
+			   "Handover Request Message");
+		ret = -1;
+		goto out;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "WPS: UUID-E", attr.uuid_e, WPS_UUID_LEN);
+
+	wpa_hexdump(MSG_DEBUG, "WPS: Out-of-Band Device Password",
+		    attr.oob_dev_password, attr.oob_dev_password_len);
+	dev_pw_id = WPA_GET_BE16(attr.oob_dev_password +
+				 WPS_OOB_PUBKEY_HASH_LEN);
+	if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER) {
+		wpa_printf(MSG_DEBUG, "WPS: Unexpected OOB Device Password ID "
+			   "%u in Wi-Fi Handover Request Message", dev_pw_id);
+		ret = -1;
+		goto out;
+	}
+	wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Public Key hash",
+		    attr.oob_dev_password, WPS_OOB_PUBKEY_HASH_LEN);
+
+	ret = wps_registrar_add_nfc_pw_token(hapd->wps->registrar,
+					     attr.oob_dev_password,
+					     DEV_PW_NFC_CONNECTION_HANDOVER,
+					     NULL, 0, 1);
+
+out:
+	wpabuf_free(wps);
+	return ret;
+}
+
+
+struct wpabuf * hostapd_wps_nfc_token_gen(struct hostapd_data *hapd, int ndef)
+{
+	if (hapd->conf->wps_nfc_pw_from_config) {
+		return wps_nfc_token_build(ndef,
+					   hapd->conf->wps_nfc_dev_pw_id,
+					   hapd->conf->wps_nfc_dh_pubkey,
+					   hapd->conf->wps_nfc_dev_pw);
+	}
+
+	return wps_nfc_token_gen(ndef, &hapd->conf->wps_nfc_dev_pw_id,
+				 &hapd->conf->wps_nfc_dh_pubkey,
+				 &hapd->conf->wps_nfc_dh_privkey,
+				 &hapd->conf->wps_nfc_dev_pw);
+}
+
+
+int hostapd_wps_nfc_token_enable(struct hostapd_data *hapd)
+{
+	struct wps_context *wps = hapd->wps;
+	struct wpabuf *pw;
+
+	if (wps == NULL)
+		return -1;
+
+	if (!hapd->conf->wps_nfc_dh_pubkey ||
+	    !hapd->conf->wps_nfc_dh_privkey ||
+	    !hapd->conf->wps_nfc_dev_pw ||
+	    !hapd->conf->wps_nfc_dev_pw_id)
+		return -1;
+
+	hostapd_wps_nfc_clear(wps);
+	wpa_printf(MSG_DEBUG,
+		   "WPS: Enable NFC Tag (Dev Pw Id %u) for AP interface %s (context %p)",
+		   hapd->conf->wps_nfc_dev_pw_id, hapd->conf->iface, wps);
+	wps->ap_nfc_dev_pw_id = hapd->conf->wps_nfc_dev_pw_id;
+	wps->ap_nfc_dh_pubkey = wpabuf_dup(hapd->conf->wps_nfc_dh_pubkey);
+	wps->ap_nfc_dh_privkey = wpabuf_dup(hapd->conf->wps_nfc_dh_privkey);
+	pw = hapd->conf->wps_nfc_dev_pw;
+	wps->ap_nfc_dev_pw = wpabuf_alloc(
+		wpabuf_len(pw) * 2 + 1);
+	if (wps->ap_nfc_dev_pw) {
+		wpa_snprintf_hex_uppercase(
+			(char *) wpabuf_put(wps->ap_nfc_dev_pw,
+					    wpabuf_len(pw) * 2),
+			wpabuf_len(pw) * 2 + 1,
+			wpabuf_head(pw), wpabuf_len(pw));
+	}
+
+	if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey ||
+	    !wps->ap_nfc_dev_pw) {
+		hostapd_wps_nfc_clear(wps);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void hostapd_wps_nfc_token_disable(struct hostapd_data *hapd)
+{
+	wpa_printf(MSG_DEBUG, "WPS: Disable NFC token for AP interface %s",
+		   hapd->conf->iface);
+	hostapd_wps_nfc_clear(hapd->wps);
+}
+
+#endif /* CONFIG_WPS_NFC */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wps_hostapd.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wps_hostapd.h
new file mode 100644
index 0000000..204bd82
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/wps_hostapd.h
@@ -0,0 +1,92 @@
+/*
+ * hostapd / WPS integration
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPS_HOSTAPD_H
+#define WPS_HOSTAPD_H
+
+#ifdef CONFIG_WPS
+
+int hostapd_init_wps(struct hostapd_data *hapd,
+		     struct hostapd_bss_config *conf);
+int hostapd_init_wps_complete(struct hostapd_data *hapd);
+void hostapd_deinit_wps(struct hostapd_data *hapd);
+void hostapd_update_wps(struct hostapd_data *hapd);
+void hostapd_wps_eap_completed(struct hostapd_data *hapd);
+int hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *addr,
+			const char *uuid, const char *pin, int timeout);
+int hostapd_wps_button_pushed(struct hostapd_data *hapd,
+			      const u8 *p2p_dev_addr);
+int hostapd_wps_cancel(struct hostapd_data *hapd);
+int hostapd_wps_get_mib_sta(struct hostapd_data *hapd, const u8 *addr,
+			    char *buf, size_t buflen);
+void hostapd_wps_ap_pin_disable(struct hostapd_data *hapd);
+const char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout);
+const char * hostapd_wps_ap_pin_get(struct hostapd_data *hapd);
+int hostapd_wps_ap_pin_set(struct hostapd_data *hapd, const char *pin,
+			   int timeout);
+void hostapd_wps_update_ie(struct hostapd_data *hapd);
+int hostapd_wps_config_ap(struct hostapd_data *hapd, const char *ssid,
+			  const char *auth, const char *encr, const char *key);
+int hostapd_wps_nfc_tag_read(struct hostapd_data *hapd,
+			     const struct wpabuf *data);
+struct wpabuf * hostapd_wps_nfc_config_token(struct hostapd_data *hapd,
+					     int ndef);
+struct wpabuf * hostapd_wps_nfc_hs_cr(struct hostapd_data *hapd, int ndef);
+int hostapd_wps_nfc_report_handover(struct hostapd_data *hapd,
+				    const struct wpabuf *req,
+				    const struct wpabuf *sel);
+struct wpabuf * hostapd_wps_nfc_token_gen(struct hostapd_data *hapd, int ndef);
+int hostapd_wps_nfc_token_enable(struct hostapd_data *hapd);
+void hostapd_wps_nfc_token_disable(struct hostapd_data *hapd);
+
+#else /* CONFIG_WPS */
+
+static inline int hostapd_init_wps(struct hostapd_data *hapd,
+				   struct hostapd_bss_config *conf)
+{
+	return 0;
+}
+
+static inline void hostapd_deinit_wps(struct hostapd_data *hapd)
+{
+}
+
+static inline int hostapd_init_wps_complete(struct hostapd_data *hapd)
+{
+    return 0;
+}
+
+static inline void hostapd_update_wps(struct hostapd_data *hapd)
+{
+}
+
+static inline void hostapd_wps_eap_completed(struct hostapd_data *hapd)
+{
+}
+
+static inline int hostapd_wps_get_mib_sta(struct hostapd_data *hapd,
+					  const u8 *addr,
+					  char *buf, size_t buflen)
+{
+	return 0;
+}
+
+static inline int hostapd_wps_button_pushed(struct hostapd_data *hapd,
+					    const u8 *p2p_dev_addr)
+{
+	return 0;
+}
+
+static inline int hostapd_wps_cancel(struct hostapd_data *hapd)
+{
+	return 0;
+}
+
+#endif /* CONFIG_WPS */
+
+#endif /* WPS_HOSTAPD_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/x_snoop.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/x_snoop.c
new file mode 100644
index 0000000..30c990f
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/x_snoop.c
@@ -0,0 +1,143 @@
+/*
+ * Generic Snooping for Proxy ARP
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ap_drv_ops.h"
+#include "x_snoop.h"
+
+
+int x_snoop_init(struct hostapd_data *hapd)
+{
+	struct hostapd_bss_config *conf = hapd->conf;
+
+	if (!conf->isolate) {
+		wpa_printf(MSG_DEBUG,
+			   "x_snoop: ap_isolate must be enabled for x_snoop");
+		return -1;
+	}
+
+	if (conf->bridge[0] == '\0') {
+		wpa_printf(MSG_DEBUG,
+			   "x_snoop: Bridge must be configured for x_snoop");
+		return -1;
+	}
+
+	hapd->x_snoop_initialized = true;
+
+	if (!conf->snoop_iface[0] &&
+	    hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE,
+					 1)) {
+		wpa_printf(MSG_DEBUG,
+			   "x_snoop: Failed to enable hairpin_mode on the bridge port");
+		return -1;
+	}
+
+	if (!conf->snoop_iface[0] &&
+	    hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 1)) {
+		wpa_printf(MSG_DEBUG,
+			   "x_snoop: Failed to enable proxyarp on the bridge port");
+		return -1;
+	}
+
+	if (hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT,
+					 1)) {
+		wpa_printf(MSG_DEBUG,
+			   "x_snoop: Failed to enable accepting gratuitous ARP on the bridge");
+		return -1;
+	}
+
+#ifdef CONFIG_IPV6
+	if (!conf->snoop_iface[0] &&
+	    hostapd_drv_br_set_net_param(hapd, DRV_BR_MULTICAST_SNOOPING, 1)) {
+		wpa_printf(MSG_DEBUG,
+			   "x_snoop: Failed to enable multicast snooping on the bridge");
+		return -1;
+	}
+#endif /* CONFIG_IPV6 */
+
+	return 0;
+}
+
+
+struct l2_packet_data *
+x_snoop_get_l2_packet(struct hostapd_data *hapd,
+		      void (*handler)(void *ctx, const u8 *src_addr,
+				      const u8 *buf, size_t len),
+		      enum l2_packet_filter_type type)
+{
+	struct hostapd_bss_config *conf = hapd->conf;
+	struct l2_packet_data *l2;
+	const char *ifname = conf->bridge;
+
+	if (conf->snoop_iface[0])
+		ifname = conf->snoop_iface;
+
+	l2 = l2_packet_init(ifname, NULL, ETH_P_ALL, handler, hapd, 1);
+	if (l2 == NULL) {
+		wpa_printf(MSG_DEBUG,
+			   "x_snoop: Failed to initialize L2 packet processing %s",
+			   strerror(errno));
+		return NULL;
+	}
+
+	if (l2_packet_set_packet_filter(l2, type)) {
+		wpa_printf(MSG_DEBUG,
+			   "x_snoop: Failed to set L2 packet filter for type: %d",
+			   type);
+		l2_packet_deinit(l2);
+		return NULL;
+	}
+
+	return l2;
+}
+
+
+void x_snoop_mcast_to_ucast_convert_send(struct hostapd_data *hapd,
+					 struct sta_info *sta, u8 *buf,
+					 size_t len)
+{
+	int res;
+	u8 addr[ETH_ALEN];
+	u8 *dst_addr = buf;
+
+	if (!(dst_addr[0] & 0x01))
+		return;
+
+	wpa_printf(MSG_EXCESSIVE, "x_snoop: Multicast-to-unicast conversion "
+		   MACSTR " -> " MACSTR " (len %u)",
+		   MAC2STR(dst_addr), MAC2STR(sta->addr), (unsigned int) len);
+
+	/* save the multicast destination address for restoring it later */
+	os_memcpy(addr, buf, ETH_ALEN);
+
+	os_memcpy(buf, sta->addr, ETH_ALEN);
+	res = l2_packet_send(hapd->sock_dhcp, NULL, 0, buf, len);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "x_snoop: Failed to send mcast to ucast converted packet to "
+			   MACSTR, MAC2STR(sta->addr));
+	}
+
+	/* restore the multicast destination address */
+	os_memcpy(buf, addr, ETH_ALEN);
+}
+
+
+void x_snoop_deinit(struct hostapd_data *hapd)
+{
+	if (!hapd->x_snoop_initialized)
+		return;
+	hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT, 0);
+	hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 0);
+	hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE, 0);
+	hapd->x_snoop_initialized = false;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/x_snoop.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/x_snoop.h
new file mode 100644
index 0000000..e43a78d
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/ap/x_snoop.h
@@ -0,0 +1,56 @@
+/*
+ * Generic Snooping for Proxy ARP
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef X_SNOOP_H
+#define X_SNOOP_H
+
+#include "l2_packet/l2_packet.h"
+
+#ifdef CONFIG_PROXYARP
+
+int x_snoop_init(struct hostapd_data *hapd);
+struct l2_packet_data *
+x_snoop_get_l2_packet(struct hostapd_data *hapd,
+		      void (*handler)(void *ctx, const u8 *src_addr,
+				      const u8 *buf, size_t len),
+		      enum l2_packet_filter_type type);
+void x_snoop_mcast_to_ucast_convert_send(struct hostapd_data *hapd,
+					 struct sta_info *sta, u8 *buf,
+					 size_t len);
+void x_snoop_deinit(struct hostapd_data *hapd);
+
+#else /* CONFIG_PROXYARP */
+
+static inline int x_snoop_init(struct hostapd_data *hapd)
+{
+	return 0;
+}
+
+static inline struct l2_packet_data *
+x_snoop_get_l2_packet(struct hostapd_data *hapd,
+		      void (*handler)(void *ctx, const u8 *src_addr,
+				      const u8 *buf, size_t len),
+		      enum l2_packet_filter_type type)
+{
+	return NULL;
+}
+
+static inline void
+x_snoop_mcast_to_ucast_convert_send(struct hostapd_data *hapd,
+				    struct sta_info *sta, void *buf,
+				    size_t len)
+{
+}
+
+static inline void x_snoop_deinit(struct hostapd_data *hapd)
+{
+}
+
+#endif /* CONFIG_PROXYARP */
+
+#endif /* X_SNOOP_H */