ASR_BASE

Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/.gitignore b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/.gitignore
new file mode 100644
index 0000000..7ffabe6
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/.gitignore
@@ -0,0 +1,4 @@
+libwlantest.a
+test_vectors
+wlantest
+wlantest_cli
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/Makefile b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/Makefile
new file mode 100644
index 0000000..c01452d
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/Makefile
@@ -0,0 +1,89 @@
+ALL=wlantest wlantest_cli test_vectors
+
+include ../src/build.rules
+
+UNAME := $(shell uname -s)
+
+CFLAGS += -I.
+CFLAGS += -I../src
+CFLAGS += -I../src/utils
+
+ifneq ($(UNAME),Darwin)
+# glibc < 2.17 needs -lrt for clock_gettime()
+LIBS += -lrt
+endif
+
+OWN_LIBS += ../src/utils/libutils.a
+OWN_LIBS += ../src/crypto/libcrypto.a
+
+CFLAGS += -DCONFIG_OCV
+CFLAGS += -DCONFIG_IEEE80211R
+CFLAGS += -DCONFIG_HS20
+CFLAGS += -DCONFIG_DEBUG_FILE
+CFLAGS += -DCONFIG_FILS
+CFLAGS += -DCONFIG_SAE
+CFLAGS += -DCONFIG_OWE
+CFLAGS += -DCONFIG_DPP
+CFLAGS += -DCONFIG_SHA384
+CFLAGS += -DCONFIG_SHA512
+CFLAGS += -DCONFIG_PASN
+
+OBJS += ../src/common/ieee802_11_common.o
+OBJS += ../src/common/wpa_common.o
+OBJS += ../src/radius/radius.o
+OBJS += ../src/rsn_supp/wpa_ie.o
+
+OBJS += wlantest.o
+OBJS += readpcap.o
+OBJS += writepcap.o
+OBJS += monitor.o
+OBJS += process.o
+OBJS += wired.o
+OBJS += rx_mgmt.o
+OBJS += rx_data.o
+OBJS += rx_eapol.o
+OBJS += rx_ip.o
+OBJS += rx_tdls.o
+OBJS += bss.o
+OBJS += sta.o
+OBJS += ccmp.o
+OBJS += tkip.o
+OBJS += ctrl.o
+OBJS += inject.o
+OBJS += wep.o
+OBJS += bip.o
+OBJS += gcmp.o
+
+LIBS += -lpcap
+
+TOBJS += test_vectors.o
+TOBJS += ccmp.o
+TOBJS += tkip.o
+TOBJS += wep.o
+TOBJS += bip.o
+TOBJS += gcmp.o
+
+
+OBJS_cli = wlantest_cli.o
+OBJS_cli += ../src/common/cli.o
+
+_OBJS_VAR := OBJS
+include ../src/objs.mk
+_OBJS_VAR := TOBJS
+include ../src/objs.mk
+_OBJS_VAR := OBJS_cli
+include ../src/objs.mk
+_OBJS_VAR := OWN_LIBS
+include ../src/objs.mk
+
+wlantest: $(OBJS) $(OWN_LIBS)
+	$(LDO) $(LDFLAGS) -o wlantest $(OBJS) $(OWN_LIBS) $(LIBS)
+
+wlantest_cli: $(OBJS_cli) $(OWN_LIBS)
+	$(LDO) $(LDFLAGS) -o wlantest_cli $(OBJS_cli) $(OWN_LIBS) $(LIBS)
+
+test_vectors: $(TOBJS) $(OWN_LIBS)
+	$(LDO) $(LDFLAGS) -o test_vectors $(TOBJS) $(OWN_LIBS) $(LIBS)
+
+clean: common-clean
+	rm -f core *~
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/bip.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/bip.c
new file mode 100644
index 0000000..c73a15c
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/bip.c
@@ -0,0 +1,133 @@
+/*
+ * BIP
+ * Copyright (c) 2010-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 "crypto/aes_wrap.h"
+#include "wlantest.h"
+
+
+u8 * bip_protect(const u8 *igtk, size_t igtk_len, u8 *frame, size_t len,
+		 u8 *ipn, int keyid, size_t *prot_len)
+{
+	u8 *prot, *pos, *buf;
+	u8 mic[16];
+	u16 fc;
+	struct ieee80211_hdr *hdr;
+	size_t plen;
+
+	plen = len + (igtk_len == 32 ? 26 : 18);
+	prot = os_malloc(plen);
+	if (prot == NULL)
+		return NULL;
+	os_memcpy(prot, frame, len);
+	pos = prot + len;
+	*pos++ = WLAN_EID_MMIE;
+	*pos++ = igtk_len == 32 ? 24 : 16;
+	WPA_PUT_LE16(pos, keyid);
+	pos += 2;
+	os_memcpy(pos, ipn, 6);
+	pos += 6;
+	os_memset(pos, 0, igtk_len == 32 ? 16 : 8); /* MIC */
+
+	buf = os_malloc(plen + 20 - 24);
+	if (buf == NULL) {
+		os_free(prot);
+		return NULL;
+	}
+
+	/* BIP AAD: FC(masked) A1 A2 A3 */
+	hdr = (struct ieee80211_hdr *) frame;
+	fc = le_to_host16(hdr->frame_control);
+	fc &= ~(WLAN_FC_RETRY | WLAN_FC_PWRMGT | WLAN_FC_MOREDATA);
+	WPA_PUT_LE16(buf, fc);
+	os_memcpy(buf + 2, hdr->addr1, 3 * ETH_ALEN);
+	os_memcpy(buf + 20, prot + 24, plen - 24);
+	wpa_hexdump(MSG_MSGDUMP, "BIP: AAD|Body(masked)", buf, plen + 20 - 24);
+	/* MIC = L(AES-128-CMAC(AAD || Frame Body(masked)), 0, 64) */
+	if (omac1_aes_128(igtk, buf, plen + 20 - 24, mic) < 0) {
+		os_free(prot);
+		os_free(buf);
+		return NULL;
+	}
+	os_free(buf);
+
+	os_memcpy(pos, mic, igtk_len == 32 ? 16 : 8);
+	wpa_hexdump(MSG_DEBUG, "BIP MMIE MIC", pos, igtk_len == 32 ? 16 : 8);
+
+	*prot_len = plen;
+	return prot;
+}
+
+
+u8 * bip_gmac_protect(const u8 *igtk, size_t igtk_len, u8 *frame, size_t len,
+		      u8 *ipn, int keyid, size_t *prot_len)
+{
+	u8 *prot, *pos, *buf;
+	u16 fc;
+	struct ieee80211_hdr *hdr;
+	size_t plen;
+	u8 nonce[12], *npos;
+
+	plen = len + 26;
+	prot = os_malloc(plen);
+	if (prot == NULL)
+		return NULL;
+	os_memcpy(prot, frame, len);
+	pos = prot + len;
+	*pos++ = WLAN_EID_MMIE;
+	*pos++ = 24;
+	WPA_PUT_LE16(pos, keyid);
+	pos += 2;
+	os_memcpy(pos, ipn, 6);
+	pos += 6;
+	os_memset(pos, 0, 16); /* MIC */
+
+	buf = os_malloc(plen + 20 - 24);
+	if (buf == NULL) {
+		os_free(prot);
+		return NULL;
+	}
+
+	/* BIP AAD: FC(masked) A1 A2 A3 */
+	hdr = (struct ieee80211_hdr *) frame;
+	fc = le_to_host16(hdr->frame_control);
+	fc &= ~(WLAN_FC_RETRY | WLAN_FC_PWRMGT | WLAN_FC_MOREDATA);
+	WPA_PUT_LE16(buf, fc);
+	os_memcpy(buf + 2, hdr->addr1, 3 * ETH_ALEN);
+	os_memcpy(buf + 20, prot + 24, plen - 24);
+	wpa_hexdump(MSG_MSGDUMP, "BIP-GMAC: AAD|Body(masked)",
+		    buf, plen + 20 - 24);
+
+	/* Nonce: A2 | IPN */
+	os_memcpy(nonce, hdr->addr2, ETH_ALEN);
+	npos = nonce + ETH_ALEN;
+	*npos++ = ipn[5];
+	*npos++ = ipn[4];
+	*npos++ = ipn[3];
+	*npos++ = ipn[2];
+	*npos++ = ipn[1];
+	*npos++ = ipn[0];
+	wpa_hexdump(MSG_EXCESSIVE, "BIP-GMAC: Nonce", nonce, sizeof(nonce));
+
+	/* MIC = AES-GMAC(AAD || Frame Body(masked)) */
+	if (aes_gmac(igtk, igtk_len, nonce, sizeof(nonce),
+		     buf, plen + 20 - 24, pos) < 0) {
+		os_free(prot);
+		os_free(buf);
+		return NULL;
+	}
+	os_free(buf);
+
+	wpa_hexdump(MSG_DEBUG, "BIP-GMAC MMIE MIC", pos, 16);
+
+	*prot_len = plen;
+	return prot;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/bss.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/bss.c
new file mode 100644
index 0000000..4fc0b17
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/bss.c
@@ -0,0 +1,374 @@
+/*
+ * BSS list
+ * Copyright (c) 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/defs.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "crypto/sha1.h"
+#include "wlantest.h"
+
+
+struct wlantest_bss * bss_find(struct wlantest *wt, const u8 *bssid)
+{
+	struct wlantest_bss *bss;
+
+	dl_list_for_each(bss, &wt->bss, struct wlantest_bss, list) {
+		if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0)
+			return bss;
+	}
+
+	return NULL;
+}
+
+
+struct wlantest_bss * bss_get(struct wlantest *wt, const u8 *bssid)
+{
+	struct wlantest_bss *bss;
+
+	if (bssid[0] & 0x01)
+		return NULL; /* Skip group addressed frames */
+
+	bss = bss_find(wt, bssid);
+	if (bss)
+		return bss;
+
+	bss = os_zalloc(sizeof(*bss));
+	if (bss == NULL)
+		return NULL;
+	dl_list_init(&bss->sta);
+	dl_list_init(&bss->pmk);
+	dl_list_init(&bss->tdls);
+	os_memcpy(bss->bssid, bssid, ETH_ALEN);
+	dl_list_add(&wt->bss, &bss->list);
+	wpa_printf(MSG_DEBUG, "Discovered new BSS - " MACSTR,
+		   MAC2STR(bss->bssid));
+	return bss;
+}
+
+
+void pmk_deinit(struct wlantest_pmk *pmk)
+{
+	dl_list_del(&pmk->list);
+	os_free(pmk);
+}
+
+
+void tdls_deinit(struct wlantest_tdls *tdls)
+{
+	dl_list_del(&tdls->list);
+	os_free(tdls);
+}
+
+
+void bss_deinit(struct wlantest_bss *bss)
+{
+	struct wlantest_sta *sta, *n;
+	struct wlantest_pmk *pmk, *np;
+	struct wlantest_tdls *tdls, *nt;
+	dl_list_for_each_safe(sta, n, &bss->sta, struct wlantest_sta, list)
+		sta_deinit(sta);
+	dl_list_for_each_safe(pmk, np, &bss->pmk, struct wlantest_pmk, list)
+		pmk_deinit(pmk);
+	dl_list_for_each_safe(tdls, nt, &bss->tdls, struct wlantest_tdls, list)
+		tdls_deinit(tdls);
+	dl_list_del(&bss->list);
+	os_free(bss);
+}
+
+
+int bss_add_pmk_from_passphrase(struct wlantest_bss *bss,
+				const char *passphrase)
+{
+	struct wlantest_pmk *pmk;
+
+	pmk = os_zalloc(sizeof(*pmk));
+	if (pmk == NULL)
+		return -1;
+	if (pbkdf2_sha1(passphrase, bss->ssid, bss->ssid_len, 4096,
+			pmk->pmk, PMK_LEN) < 0) {
+		os_free(pmk);
+		return -1;
+	}
+
+	wpa_printf(MSG_INFO, "Add possible PMK for BSSID " MACSTR
+		   " based on passphrase '%s'",
+		   MAC2STR(bss->bssid), passphrase);
+	wpa_hexdump(MSG_DEBUG, "Possible PMK", pmk->pmk, PMK_LEN);
+	pmk->pmk_len = PMK_LEN;
+	dl_list_add(&bss->pmk, &pmk->list);
+
+	return 0;
+}
+
+
+static void bss_add_pmk(struct wlantest *wt, struct wlantest_bss *bss)
+{
+	struct wlantest_passphrase *p;
+
+	dl_list_for_each(p, &wt->passphrase, struct wlantest_passphrase, list)
+	{
+		if (!is_zero_ether_addr(p->bssid) &&
+		    os_memcmp(p->bssid, bss->bssid, ETH_ALEN) != 0)
+			continue;
+		if (p->ssid_len &&
+		    (p->ssid_len != bss->ssid_len ||
+		     os_memcmp(p->ssid, bss->ssid, p->ssid_len) != 0))
+			continue;
+
+		if (bss_add_pmk_from_passphrase(bss, p->passphrase) < 0)
+			break;
+	}
+}
+
+
+void bss_update(struct wlantest *wt, struct wlantest_bss *bss,
+		struct ieee802_11_elems *elems, int beacon)
+{
+	struct wpa_ie_data data;
+	int update = 0;
+
+	if (bss->capab_info != bss->prev_capab_info)
+		update = 1;
+
+	if (beacon && (!elems->ssid || elems->ssid_len > 32)) {
+		wpa_printf(MSG_INFO,
+			   "Invalid or missing SSID in a %s frame for " MACSTR,
+			   beacon == 1 ? "Beacon" : "Probe Response",
+			   MAC2STR(bss->bssid));
+		bss->parse_error_reported = 1;
+		return;
+	}
+
+	if (beacon &&
+	    (bss->ssid_len != elems->ssid_len ||
+	     os_memcmp(bss->ssid, elems->ssid, bss->ssid_len) != 0)) {
+		wpa_printf(MSG_DEBUG, "Store SSID '%s' for BSSID " MACSTR,
+			   wpa_ssid_txt(elems->ssid, elems->ssid_len),
+			   MAC2STR(bss->bssid));
+		os_memcpy(bss->ssid, elems->ssid, elems->ssid_len);
+		bss->ssid_len = elems->ssid_len;
+		bss_add_pmk(wt, bss);
+	}
+
+	if (elems->osen == NULL) {
+		if (bss->osenie[0]) {
+			add_note(wt, MSG_INFO, "BSS " MACSTR
+				 " - OSEN IE removed", MAC2STR(bss->bssid));
+			bss->rsnie[0] = 0;
+			update = 1;
+		}
+	} else {
+		if (bss->osenie[0] == 0 ||
+		    os_memcmp(bss->osenie, elems->osen - 2,
+			      elems->osen_len + 2) != 0) {
+			wpa_printf(MSG_INFO, "BSS " MACSTR " - OSEN IE "
+				   "stored", MAC2STR(bss->bssid));
+			wpa_hexdump(MSG_DEBUG, "OSEN IE", elems->osen - 2,
+				    elems->osen_len + 2);
+			update = 1;
+		}
+		os_memcpy(bss->osenie, elems->osen - 2,
+			  elems->osen_len + 2);
+	}
+
+	/* S1G does not include RSNE in beacon, so only clear it from
+	 * Probe Response frames. Note this assumes short beacons were dropped
+	 * due to missing SSID above.
+	 */
+	if (!elems->rsn_ie && (!elems->s1g_capab || beacon != 1)) {
+		if (bss->rsnie[0]) {
+			add_note(wt, MSG_INFO, "BSS " MACSTR
+				 " - RSN IE removed", MAC2STR(bss->bssid));
+			bss->rsnie[0] = 0;
+			update = 1;
+		}
+	} else if (elems->rsn_ie) {
+		if (bss->rsnie[0] == 0 ||
+		    os_memcmp(bss->rsnie, elems->rsn_ie - 2,
+			      elems->rsn_ie_len + 2) != 0) {
+			wpa_printf(MSG_INFO, "BSS " MACSTR " - RSN IE "
+				   "stored", MAC2STR(bss->bssid));
+			wpa_hexdump(MSG_DEBUG, "RSN IE", elems->rsn_ie - 2,
+				    elems->rsn_ie_len + 2);
+			update = 1;
+		}
+		os_memcpy(bss->rsnie, elems->rsn_ie - 2,
+			  elems->rsn_ie_len + 2);
+	}
+
+	if (elems->wpa_ie == NULL) {
+		if (bss->wpaie[0]) {
+			add_note(wt, MSG_INFO, "BSS " MACSTR
+				 " - WPA IE removed", MAC2STR(bss->bssid));
+			bss->wpaie[0] = 0;
+			update = 1;
+		}
+	} else {
+		if (bss->wpaie[0] == 0 ||
+		    os_memcmp(bss->wpaie, elems->wpa_ie - 2,
+			      elems->wpa_ie_len + 2) != 0) {
+			wpa_printf(MSG_INFO, "BSS " MACSTR " - WPA IE "
+				   "stored", MAC2STR(bss->bssid));
+			wpa_hexdump(MSG_DEBUG, "WPA IE", elems->wpa_ie - 2,
+				    elems->wpa_ie_len + 2);
+			update = 1;
+		}
+		os_memcpy(bss->wpaie, elems->wpa_ie - 2,
+			  elems->wpa_ie_len + 2);
+	}
+
+	if (elems->mdie)
+		os_memcpy(bss->mdid, elems->mdie, 2);
+
+	bss->mesh = elems->mesh_id != NULL;
+
+	if (!update)
+		return;
+
+	if (beacon == 1)
+		bss->beacon_seen = 1;
+	else if (beacon == 2)
+		bss->proberesp_seen = 1;
+	bss->ies_set = 1;
+	bss->prev_capab_info = bss->capab_info;
+	bss->proto = 0;
+	bss->pairwise_cipher = 0;
+	bss->group_cipher = 0;
+	bss->key_mgmt = 0;
+	bss->rsn_capab = 0;
+	bss->mgmt_group_cipher = 0;
+
+	if (bss->wpaie[0]) {
+		if (wpa_parse_wpa_ie_wpa(bss->wpaie, 2 + bss->wpaie[1], &data)
+		    < 0) {
+			add_note(wt, MSG_INFO, "Failed to parse WPA IE from "
+				 MACSTR, MAC2STR(bss->bssid));
+		} else {
+			bss->proto |= data.proto;
+			bss->pairwise_cipher |= data.pairwise_cipher;
+			bss->group_cipher |= data.group_cipher;
+			bss->key_mgmt |= data.key_mgmt;
+			bss->rsn_capab = data.capabilities;
+			bss->mgmt_group_cipher |= data.mgmt_group_cipher;
+		}
+	}
+
+	if (bss->rsnie[0]) {
+		if (wpa_parse_wpa_ie_rsn(bss->rsnie, 2 + bss->rsnie[1], &data)
+		    < 0) {
+			add_note(wt, MSG_INFO, "Failed to parse RSN IE from "
+				 MACSTR, MAC2STR(bss->bssid));
+		} else {
+			bss->proto |= data.proto;
+			bss->pairwise_cipher |= data.pairwise_cipher;
+			bss->group_cipher |= data.group_cipher;
+			bss->key_mgmt |= data.key_mgmt;
+			bss->rsn_capab = data.capabilities;
+			bss->mgmt_group_cipher |= data.mgmt_group_cipher;
+		}
+	}
+
+	if (bss->osenie[0]) {
+		bss->proto |= WPA_PROTO_OSEN;
+		bss->pairwise_cipher |= WPA_CIPHER_CCMP;
+		bss->group_cipher |= WPA_CIPHER_CCMP;
+		bss->key_mgmt |= WPA_KEY_MGMT_OSEN;
+	}
+
+	if (!(bss->proto & WPA_PROTO_RSN) ||
+	    !(bss->rsn_capab & WPA_CAPABILITY_MFPC))
+		bss->mgmt_group_cipher = 0;
+
+	if (!bss->wpaie[0] && !bss->rsnie[0] && !bss->osenie[0] &&
+	    (bss->capab_info & WLAN_CAPABILITY_PRIVACY))
+		bss->group_cipher = WPA_CIPHER_WEP40;
+
+	wpa_printf(MSG_INFO, "BSS " MACSTR
+		   " proto=%s%s%s%s"
+		   "pairwise=%s%s%s%s%s%s%s"
+		   "group=%s%s%s%s%s%s%s%s%s"
+		   "mgmt_group_cipher=%s%s%s%s%s"
+		   "key_mgmt=%s%s%s%s%s%s%s%s%s%s%s%s%s%s"
+		   "rsn_capab=%s%s%s%s%s%s%s%s%s%s",
+		   MAC2STR(bss->bssid),
+		   bss->proto == 0 ? "OPEN " : "",
+		   bss->proto & WPA_PROTO_WPA ? "WPA " : "",
+		   bss->proto & WPA_PROTO_RSN ? "WPA2 " : "",
+		   bss->proto & WPA_PROTO_OSEN ? "OSEN " : "",
+		   bss->pairwise_cipher == 0 ? "N/A " : "",
+		   bss->pairwise_cipher & WPA_CIPHER_NONE ? "NONE " : "",
+		   bss->pairwise_cipher & WPA_CIPHER_TKIP ? "TKIP " : "",
+		   bss->pairwise_cipher & WPA_CIPHER_CCMP ? "CCMP " : "",
+		   bss->pairwise_cipher & WPA_CIPHER_CCMP_256 ? "CCMP-256 " :
+		   "",
+		   bss->pairwise_cipher & WPA_CIPHER_GCMP ? "GCMP " : "",
+		   bss->pairwise_cipher & WPA_CIPHER_GCMP_256 ? "GCMP-256 " :
+		   "",
+		   bss->group_cipher == 0 ? "N/A " : "",
+		   bss->group_cipher & WPA_CIPHER_NONE ? "NONE " : "",
+		   bss->group_cipher & WPA_CIPHER_WEP40 ? "WEP40 " : "",
+		   bss->group_cipher & WPA_CIPHER_WEP104 ? "WEP104 " : "",
+		   bss->group_cipher & WPA_CIPHER_TKIP ? "TKIP " : "",
+		   bss->group_cipher & WPA_CIPHER_CCMP ? "CCMP " : "",
+		   bss->group_cipher & WPA_CIPHER_CCMP_256 ? "CCMP-256 " : "",
+		   bss->group_cipher & WPA_CIPHER_GCMP ? "GCMP " : "",
+		   bss->group_cipher & WPA_CIPHER_GCMP_256 ? "GCMP-256 " : "",
+		   bss->mgmt_group_cipher == 0 ? "N/A " : "",
+		   bss->mgmt_group_cipher & WPA_CIPHER_AES_128_CMAC ?
+		   "BIP " : "",
+		   bss->mgmt_group_cipher & WPA_CIPHER_BIP_GMAC_128 ?
+		   "BIP-GMAC-128 " : "",
+		   bss->mgmt_group_cipher & WPA_CIPHER_BIP_GMAC_256 ?
+		   "BIP-GMAC-256 " : "",
+		   bss->mgmt_group_cipher & WPA_CIPHER_BIP_CMAC_256 ?
+		   "BIP-CMAC-256 " : "",
+		   bss->key_mgmt == 0 ? "N/A " : "",
+		   bss->key_mgmt & WPA_KEY_MGMT_IEEE8021X ? "EAP " : "",
+		   bss->key_mgmt & WPA_KEY_MGMT_PSK ? "PSK " : "",
+		   bss->key_mgmt & WPA_KEY_MGMT_WPA_NONE ? "WPA-NONE " : "",
+		   bss->key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X ? "FT-EAP " : "",
+		   bss->key_mgmt & WPA_KEY_MGMT_FT_PSK ? "FT-PSK " : "",
+		   bss->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256 ?
+		   "EAP-SHA256 " : "",
+		   bss->key_mgmt & WPA_KEY_MGMT_PSK_SHA256 ?
+		   "PSK-SHA256 " : "",
+		   bss->key_mgmt & WPA_KEY_MGMT_OWE ? "OWE " : "",
+		   bss->key_mgmt & WPA_KEY_MGMT_PASN ? "PASN " : "",
+		   bss->key_mgmt & WPA_KEY_MGMT_OSEN ? "OSEN " : "",
+		   bss->key_mgmt & WPA_KEY_MGMT_DPP ? "DPP " : "",
+		   bss->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B ?
+		   "EAP-SUITE-B " : "",
+		   bss->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 ?
+		   "EAP-SUITE-B-192 " : "",
+		   bss->rsn_capab & WPA_CAPABILITY_PREAUTH ? "PREAUTH " : "",
+		   bss->rsn_capab & WPA_CAPABILITY_NO_PAIRWISE ?
+		   "NO_PAIRWISE " : "",
+		   bss->rsn_capab & WPA_CAPABILITY_MFPR ? "MFPR " : "",
+		   bss->rsn_capab & WPA_CAPABILITY_MFPC ? "MFPC " : "",
+		   bss->rsn_capab & WPA_CAPABILITY_PEERKEY_ENABLED ?
+		   "PEERKEY " : "",
+		   bss->rsn_capab & WPA_CAPABILITY_SPP_A_MSDU_CAPABLE ?
+		   "SPP-A-MSDU-CAPAB " : "",
+		   bss->rsn_capab & WPA_CAPABILITY_SPP_A_MSDU_REQUIRED ?
+		   "SPP-A-MSDU-REQUIRED " : "",
+		   bss->rsn_capab & WPA_CAPABILITY_PBAC ? "PBAC " : "",
+		   bss->rsn_capab & WPA_CAPABILITY_OCVC ? "OCVC " : "",
+		   bss->rsn_capab & WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST ?
+		   "ExtKeyID " : "");
+}
+
+
+void bss_flush(struct wlantest *wt)
+{
+	struct wlantest_bss *bss, *n;
+	dl_list_for_each_safe(bss, n, &wt->bss, struct wlantest_bss, list)
+		bss_deinit(bss);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/ccmp.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/ccmp.c
new file mode 100644
index 0000000..894aa74
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/ccmp.c
@@ -0,0 +1,382 @@
+/*
+ * CTR with CBC-MAC Protocol (CCMP)
+ * Copyright (c) 2010-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 "crypto/aes.h"
+#include "crypto/aes_wrap.h"
+#include "wlantest.h"
+
+
+static void ccmp_aad_nonce(const struct ieee80211_hdr *hdr, const u8 *data,
+			   const u8 *a1, const u8 *a2, const u8 *a3,
+			   u8 *aad, size_t *aad_len, u8 *nonce)
+{
+	u16 fc, stype, seq;
+	int qos = 0, addr4 = 0;
+	u8 *pos;
+
+	nonce[0] = 0;
+
+	fc = le_to_host16(hdr->frame_control);
+	stype = WLAN_FC_GET_STYPE(fc);
+	if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) ==
+	    (WLAN_FC_TODS | WLAN_FC_FROMDS))
+		addr4 = 1;
+
+	if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA) {
+		fc &= ~0x0070; /* Mask subtype bits */
+		if (stype & 0x08) {
+			const u8 *qc;
+			qos = 1;
+			fc &= ~WLAN_FC_HTC;
+			qc = (const u8 *) (hdr + 1);
+			if (addr4)
+				qc += ETH_ALEN;
+			nonce[0] = qc[0] & 0x0f;
+		}
+	} else if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT)
+		nonce[0] |= 0x10; /* Management */
+
+	fc &= ~(WLAN_FC_RETRY | WLAN_FC_PWRMGT | WLAN_FC_MOREDATA);
+	fc |= WLAN_FC_ISWEP;
+	WPA_PUT_LE16(aad, fc);
+	pos = aad + 2;
+	os_memcpy(pos, hdr->addr1, 3 * ETH_ALEN);
+	if (a1)
+		os_memcpy(pos, a1, ETH_ALEN);
+	if (a2)
+		os_memcpy(pos + ETH_ALEN, a2, ETH_ALEN);
+	if (a3)
+		os_memcpy(pos + 2 * ETH_ALEN, a3, ETH_ALEN);
+	pos += 3 * ETH_ALEN;
+	seq = le_to_host16(hdr->seq_ctrl);
+	seq &= ~0xfff0; /* Mask Seq#; do not modify Frag# */
+	WPA_PUT_LE16(pos, seq);
+	pos += 2;
+
+	os_memcpy(pos, hdr + 1, addr4 * ETH_ALEN + qos * 2);
+	pos += addr4 * ETH_ALEN;
+	if (qos) {
+		pos[0] &= ~0x70;
+		if (1 /* FIX: either device has SPP A-MSDU Capab = 0 */)
+			pos[0] &= ~0x80;
+		pos++;
+		*pos++ = 0x00;
+	}
+
+	*aad_len = pos - aad;
+
+	if (a2)
+		os_memcpy(nonce + 1, a2, ETH_ALEN);
+	else
+		os_memcpy(nonce + 1, hdr->addr2, ETH_ALEN);
+	nonce[7] = data[7]; /* PN5 */
+	nonce[8] = data[6]; /* PN4 */
+	nonce[9] = data[5]; /* PN3 */
+	nonce[10] = data[4]; /* PN2 */
+	nonce[11] = data[1]; /* PN1 */
+	nonce[12] = data[0]; /* PN0 */
+}
+
+
+static void ccmp_aad_nonce_pv1(const u8 *hdr, const u8 *a1, const u8 *a2,
+			       const u8 *a3, const u8 *pn,
+			       u8 *aad, size_t *aad_len, u8 *nonce)
+{
+	u16 fc, type;
+	u8 *pos;
+
+	nonce[0] = BIT(5); /* PV1 */
+	/* TODO: Priority for QMF; 0 is used for Data frames */
+
+	fc = WPA_GET_LE16(hdr);
+	type = (fc & (BIT(2) | BIT(3) | BIT(4))) >> 2;
+
+	if (type == 1)
+		nonce[0] |= 0x10; /* Management */
+
+	fc &= ~(BIT(10) | BIT(11) | BIT(13) | BIT(14) | BIT(15));
+	fc |= BIT(12);
+	WPA_PUT_LE16(aad, fc);
+	pos = aad + 2;
+	if (type == 0 || type == 3) {
+		const u8 *sc;
+
+		os_memcpy(pos, a1, ETH_ALEN);
+		pos += ETH_ALEN;
+		os_memcpy(pos, a2, ETH_ALEN);
+		pos += ETH_ALEN;
+
+		if (type == 0) {
+			/* Either A1 or A2 contains SID */
+			sc = hdr + 2 + 2 + ETH_ALEN;
+		} else {
+			/* Both A1 and A2 contain full addresses */
+			sc = hdr + 2 + 2 * ETH_ALEN;
+		}
+		/* SC with Sequence Number subfield (bits 4-15 of the Sequence
+		 * Control field) masked to 0. */
+		*pos++ = *sc & 0x0f;
+		*pos++ = 0;
+
+		if (a3) {
+			os_memcpy(pos, a3, ETH_ALEN);
+			pos += ETH_ALEN;
+		}
+	}
+
+	*aad_len = pos - aad;
+
+	os_memcpy(nonce + 1, a2, ETH_ALEN);
+	nonce[7] = pn[5]; /* PN5 */
+	nonce[8] = pn[4]; /* PN4 */
+	nonce[9] = pn[3]; /* PN3 */
+	nonce[10] = pn[2]; /* PN2 */
+	nonce[11] = pn[1]; /* PN1 */
+	nonce[12] = pn[0]; /* PN0 */
+}
+
+
+u8 * ccmp_decrypt(const u8 *tk, const struct ieee80211_hdr *hdr,
+		  const u8 *a1, const u8 *a2, const u8 *a3,
+		  const u8 *data, size_t data_len,
+		  size_t *decrypted_len)
+{
+	u8 aad[30], nonce[13];
+	size_t aad_len;
+	size_t mlen;
+	u8 *plain;
+
+	if (data_len < 8 + 8)
+		return NULL;
+
+	plain = os_malloc(data_len + AES_BLOCK_SIZE);
+	if (plain == NULL)
+		return NULL;
+
+	mlen = data_len - 8 - 8;
+
+	os_memset(aad, 0, sizeof(aad));
+	ccmp_aad_nonce(hdr, data, a1, a2, a3, aad, &aad_len, nonce);
+	wpa_hexdump(MSG_EXCESSIVE, "CCMP AAD", aad, aad_len);
+	wpa_hexdump(MSG_EXCESSIVE, "CCMP nonce", nonce, 13);
+
+	if (aes_ccm_ad(tk, 16, nonce, 8, data + 8, mlen, aad, aad_len,
+		       data + 8 + mlen, plain) < 0) {
+		u16 seq_ctrl = le_to_host16(hdr->seq_ctrl);
+		wpa_printf(MSG_INFO, "Invalid CCMP MIC in frame: A1=" MACSTR
+			   " A2=" MACSTR " A3=" MACSTR " seq=%u frag=%u",
+			   MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+			   MAC2STR(hdr->addr3),
+			   WLAN_GET_SEQ_SEQ(seq_ctrl),
+			   WLAN_GET_SEQ_FRAG(seq_ctrl));
+		os_free(plain);
+		return NULL;
+	}
+	wpa_hexdump(MSG_EXCESSIVE, "CCMP decrypted", plain, mlen);
+
+	*decrypted_len = mlen;
+	return plain;
+}
+
+
+void ccmp_get_pn(u8 *pn, const u8 *data)
+{
+	pn[0] = data[7]; /* PN5 */
+	pn[1] = data[6]; /* PN4 */
+	pn[2] = data[5]; /* PN3 */
+	pn[3] = data[4]; /* PN2 */
+	pn[4] = data[1]; /* PN1 */
+	pn[5] = data[0]; /* PN0 */
+}
+
+
+u8 * ccmp_encrypt(const u8 *tk, u8 *frame, size_t len, size_t hdrlen,
+		  const u8 *qos, const u8 *a1, const u8 *a2, const u8 *a3,
+		  const u8 *pn, int keyid, size_t *encrypted_len)
+{
+	u8 aad[30], nonce[13];
+	size_t aad_len, plen;
+	u8 *crypt, *pos;
+	struct ieee80211_hdr *hdr;
+
+	if (len < hdrlen || hdrlen < 24)
+		return NULL;
+	plen = len - hdrlen;
+
+	crypt = os_malloc(hdrlen + 8 + plen + 8 + AES_BLOCK_SIZE);
+	if (crypt == NULL)
+		return NULL;
+
+	os_memcpy(crypt, frame, hdrlen);
+	hdr = (struct ieee80211_hdr *) crypt;
+	hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP);
+	pos = crypt + hdrlen;
+	*pos++ = pn[5]; /* PN0 */
+	*pos++ = pn[4]; /* PN1 */
+	*pos++ = 0x00; /* Rsvd */
+	*pos++ = 0x20 | (keyid << 6);
+	*pos++ = pn[3]; /* PN2 */
+	*pos++ = pn[2]; /* PN3 */
+	*pos++ = pn[1]; /* PN4 */
+	*pos++ = pn[0]; /* PN5 */
+
+	os_memset(aad, 0, sizeof(aad));
+	ccmp_aad_nonce(hdr, crypt + hdrlen, a1, a2, a3, aad, &aad_len, nonce);
+	wpa_hexdump(MSG_EXCESSIVE, "CCMP AAD", aad, aad_len);
+	wpa_hexdump(MSG_EXCESSIVE, "CCMP nonce", nonce, 13);
+
+	if (aes_ccm_ae(tk, 16, nonce, 8, frame + hdrlen, plen, aad, aad_len,
+		       pos, pos + plen) < 0) {
+		os_free(crypt);
+		return NULL;
+	}
+
+	wpa_hexdump(MSG_EXCESSIVE, "CCMP encrypted", crypt + hdrlen + 8, plen);
+
+	*encrypted_len = hdrlen + 8 + plen + 8;
+
+	return crypt;
+}
+
+
+u8 * ccmp_encrypt_pv1(const u8 *tk, const u8 *a1, const u8 *a2, const u8 *a3,
+		      const u8 *frame, size_t len,
+		      size_t hdrlen, const u8 *pn, int keyid,
+		      size_t *encrypted_len)
+{
+	u8 aad[24], nonce[13];
+	size_t aad_len, plen;
+	u8 *crypt, *pos;
+	struct ieee80211_hdr *hdr;
+
+	if (len < hdrlen || hdrlen < 12)
+		return NULL;
+	plen = len - hdrlen;
+
+	crypt = os_malloc(hdrlen + plen + 8 + AES_BLOCK_SIZE);
+	if (crypt == NULL)
+		return NULL;
+
+	os_memcpy(crypt, frame, hdrlen);
+	hdr = (struct ieee80211_hdr *) crypt;
+	hdr->frame_control |= host_to_le16(BIT(12)); /* Protected Frame */
+	pos = crypt + hdrlen;
+
+	os_memset(aad, 0, sizeof(aad));
+	ccmp_aad_nonce_pv1(crypt, a1, a2, a3, pn, aad, &aad_len, nonce);
+	wpa_hexdump(MSG_EXCESSIVE, "CCMP AAD", aad, aad_len);
+	wpa_hexdump(MSG_EXCESSIVE, "CCMP nonce", nonce, sizeof(nonce));
+
+	if (aes_ccm_ae(tk, 16, nonce, 8, frame + hdrlen, plen, aad, aad_len,
+		       pos, pos + plen) < 0) {
+		os_free(crypt);
+		return NULL;
+	}
+
+	wpa_hexdump(MSG_EXCESSIVE, "CCMP encrypted", crypt + hdrlen, plen);
+
+	*encrypted_len = hdrlen + plen + 8;
+
+	return crypt;
+}
+
+
+u8 * ccmp_256_decrypt(const u8 *tk, const struct ieee80211_hdr *hdr,
+		      const u8 *a1, const u8 *a2, const u8 *a3,
+		      const u8 *data, size_t data_len, size_t *decrypted_len)
+{
+	u8 aad[30], nonce[13];
+	size_t aad_len;
+	size_t mlen;
+	u8 *plain;
+
+	if (data_len < 8 + 16)
+		return NULL;
+
+	plain = os_malloc(data_len + AES_BLOCK_SIZE);
+	if (plain == NULL)
+		return NULL;
+
+	mlen = data_len - 8 - 16;
+
+	os_memset(aad, 0, sizeof(aad));
+	ccmp_aad_nonce(hdr, data, a1, a2, a3, aad, &aad_len, nonce);
+	wpa_hexdump(MSG_EXCESSIVE, "CCMP-256 AAD", aad, aad_len);
+	wpa_hexdump(MSG_EXCESSIVE, "CCMP-256 nonce", nonce, 13);
+
+	if (aes_ccm_ad(tk, 32, nonce, 16, data + 8, mlen, aad, aad_len,
+		       data + 8 + mlen, plain) < 0) {
+		u16 seq_ctrl = le_to_host16(hdr->seq_ctrl);
+		wpa_printf(MSG_INFO, "Invalid CCMP-256 MIC in frame: A1=" MACSTR
+			   " A2=" MACSTR " A3=" MACSTR " seq=%u frag=%u",
+			   MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+			   MAC2STR(hdr->addr3),
+			   WLAN_GET_SEQ_SEQ(seq_ctrl),
+			   WLAN_GET_SEQ_FRAG(seq_ctrl));
+		os_free(plain);
+		return NULL;
+	}
+	wpa_hexdump(MSG_EXCESSIVE, "CCMP-256 decrypted", plain, mlen);
+
+	*decrypted_len = mlen;
+	return plain;
+}
+
+
+u8 * ccmp_256_encrypt(const u8 *tk, u8 *frame, size_t len, size_t hdrlen,
+		      const u8 *qos, const u8 *a1, const u8 *a2, const u8 *a3,
+		      const u8 *pn, int keyid, size_t *encrypted_len)
+{
+	u8 aad[30], nonce[13];
+	size_t aad_len, plen;
+	u8 *crypt, *pos;
+	struct ieee80211_hdr *hdr;
+
+	if (len < hdrlen || hdrlen < 24)
+		return NULL;
+	plen = len - hdrlen;
+
+	crypt = os_malloc(hdrlen + 8 + plen + 16 + AES_BLOCK_SIZE);
+	if (crypt == NULL)
+		return NULL;
+
+	os_memcpy(crypt, frame, hdrlen);
+	hdr = (struct ieee80211_hdr *) crypt;
+	hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP);
+	pos = crypt + hdrlen;
+	*pos++ = pn[5]; /* PN0 */
+	*pos++ = pn[4]; /* PN1 */
+	*pos++ = 0x00; /* Rsvd */
+	*pos++ = 0x20 | (keyid << 6);
+	*pos++ = pn[3]; /* PN2 */
+	*pos++ = pn[2]; /* PN3 */
+	*pos++ = pn[1]; /* PN4 */
+	*pos++ = pn[0]; /* PN5 */
+
+	os_memset(aad, 0, sizeof(aad));
+	ccmp_aad_nonce(hdr, crypt + hdrlen, a1, a2, a3, aad, &aad_len, nonce);
+	wpa_hexdump(MSG_EXCESSIVE, "CCMP-256 AAD", aad, aad_len);
+	wpa_hexdump(MSG_EXCESSIVE, "CCMP-256 nonce", nonce, 13);
+
+	if (aes_ccm_ae(tk, 32, nonce, 16, frame + hdrlen, plen, aad, aad_len,
+		       pos, pos + plen) < 0) {
+		os_free(crypt);
+		return NULL;
+	}
+
+	wpa_hexdump(MSG_EXCESSIVE, "CCMP-256 encrypted", crypt + hdrlen + 8,
+		    plen);
+
+	*encrypted_len = hdrlen + 8 + plen + 16;
+
+	return crypt;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/ctrl.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/ctrl.c
new file mode 100644
index 0000000..587a0d3
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/ctrl.c
@@ -0,0 +1,1471 @@
+/*
+ * wlantest control interface
+ * Copyright (c) 2010-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 <sys/un.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/defs.h"
+#include "common/version.h"
+#include "common/ieee802_11_defs.h"
+#include "wlantest.h"
+#include "wlantest_ctrl.h"
+
+
+static u8 * attr_get(u8 *buf, size_t buflen, enum wlantest_ctrl_attr attr,
+		     size_t *len)
+{
+	u8 *pos = buf;
+
+	while (pos + 8 <= buf + buflen) {
+		enum wlantest_ctrl_attr a;
+		size_t alen;
+		a = WPA_GET_BE32(pos);
+		pos += 4;
+		alen = WPA_GET_BE32(pos);
+		pos += 4;
+		if (pos + alen > buf + buflen) {
+			wpa_printf(MSG_DEBUG, "Invalid control message "
+				   "attribute");
+			return NULL;
+		}
+		if (a == attr) {
+			*len = alen;
+			return pos;
+		}
+		pos += alen;
+	}
+
+	return NULL;
+}
+
+
+static u8 * attr_get_macaddr(u8 *buf, size_t buflen,
+			     enum wlantest_ctrl_attr attr)
+{
+	u8 *addr;
+	size_t addr_len;
+	addr = attr_get(buf, buflen, attr, &addr_len);
+	if (addr && addr_len != ETH_ALEN)
+		addr = NULL;
+	return addr;
+}
+
+
+static int attr_get_int(u8 *buf, size_t buflen, enum wlantest_ctrl_attr attr)
+{
+	u8 *pos;
+	size_t len;
+	pos = attr_get(buf, buflen, attr, &len);
+	if (pos == NULL || len != 4)
+		return -1;
+	return WPA_GET_BE32(pos);
+}
+
+
+static u8 * attr_add_str(u8 *pos, u8 *end, enum wlantest_ctrl_attr attr,
+			 const char *str)
+{
+	size_t len = os_strlen(str);
+
+	if (pos == NULL || end - pos < 8 + len)
+		return NULL;
+	WPA_PUT_BE32(pos, attr);
+	pos += 4;
+	WPA_PUT_BE32(pos, len);
+	pos += 4;
+	os_memcpy(pos, str, len);
+	pos += len;
+	return pos;
+}
+
+
+static u8 * attr_add_be32(u8 *pos, u8 *end, enum wlantest_ctrl_attr attr,
+			  u32 val)
+{
+	if (pos == NULL || end - pos < 12)
+		return NULL;
+	WPA_PUT_BE32(pos, attr);
+	pos += 4;
+	WPA_PUT_BE32(pos, 4);
+	pos += 4;
+	WPA_PUT_BE32(pos, val);
+	pos += 4;
+	return pos;
+}
+
+
+static void ctrl_disconnect(struct wlantest *wt, int sock)
+{
+	int i;
+	wpa_printf(MSG_DEBUG, "Disconnect control interface connection %d",
+		   sock);
+	for (i = 0; i < MAX_CTRL_CONNECTIONS; i++) {
+		if (wt->ctrl_socks[i] == sock) {
+			close(wt->ctrl_socks[i]);
+			eloop_unregister_read_sock(wt->ctrl_socks[i]);
+			wt->ctrl_socks[i] = -1;
+			break;
+		}
+	}
+}
+
+
+static void ctrl_send(struct wlantest *wt, int sock, const u8 *buf,
+		      size_t len)
+{
+	if (send(sock, buf, len, 0) < 0) {
+		wpa_printf(MSG_INFO, "send(ctrl): %s", strerror(errno));
+		ctrl_disconnect(wt, sock);
+	}
+}
+
+
+static void ctrl_send_simple(struct wlantest *wt, int sock,
+			     enum wlantest_ctrl_cmd cmd)
+{
+	u8 buf[4];
+	WPA_PUT_BE32(buf, cmd);
+	ctrl_send(wt, sock, buf, sizeof(buf));
+}
+
+
+static struct wlantest_bss * ctrl_get_bss(struct wlantest *wt, int sock,
+					  u8 *cmd, size_t clen)
+{
+	struct wlantest_bss *bss;
+	u8 *pos;
+	size_t len;
+
+	pos = attr_get(cmd, clen, WLANTEST_ATTR_BSSID, &len);
+	if (pos == NULL || len != ETH_ALEN) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return NULL;
+	}
+
+	bss = bss_find(wt, pos);
+	if (bss == NULL) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+		return NULL;
+	}
+
+	return bss;
+}
+
+
+static struct wlantest_sta * ctrl_get_sta(struct wlantest *wt, int sock,
+					  u8 *cmd, size_t clen,
+					  struct wlantest_bss *bss)
+{
+	struct wlantest_sta *sta;
+	u8 *pos;
+	size_t len;
+
+	if (bss == NULL)
+		return NULL;
+
+	pos = attr_get(cmd, clen, WLANTEST_ATTR_STA_ADDR, &len);
+	if (pos == NULL || len != ETH_ALEN) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return NULL;
+	}
+
+	sta = sta_find(bss, pos);
+	if (sta == NULL) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+		return NULL;
+	}
+
+	return sta;
+}
+
+
+static struct wlantest_sta * ctrl_get_sta2(struct wlantest *wt, int sock,
+					   u8 *cmd, size_t clen,
+					   struct wlantest_bss *bss)
+{
+	struct wlantest_sta *sta;
+	u8 *pos;
+	size_t len;
+
+	if (bss == NULL)
+		return NULL;
+
+	pos = attr_get(cmd, clen, WLANTEST_ATTR_STA2_ADDR, &len);
+	if (pos == NULL || len != ETH_ALEN) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return NULL;
+	}
+
+	sta = sta_find(bss, pos);
+	if (sta == NULL) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+		return NULL;
+	}
+
+	return sta;
+}
+
+
+static void ctrl_list_bss(struct wlantest *wt, int sock)
+{
+	u8 buf[WLANTEST_CTRL_MAX_RESP_LEN], *pos, *len;
+	struct wlantest_bss *bss;
+
+	pos = buf;
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS);
+	pos += 4;
+	WPA_PUT_BE32(pos, WLANTEST_ATTR_BSSID);
+	pos += 4;
+	len = pos; /* to be filled */
+	pos += 4;
+
+	dl_list_for_each(bss, &wt->bss, struct wlantest_bss, list) {
+		if (pos + ETH_ALEN > buf + WLANTEST_CTRL_MAX_RESP_LEN)
+			break;
+		os_memcpy(pos, bss->bssid, ETH_ALEN);
+		pos += ETH_ALEN;
+	}
+
+	WPA_PUT_BE32(len, pos - len - 4);
+	ctrl_send(wt, sock, buf, pos - buf);
+}
+
+
+static void ctrl_list_sta(struct wlantest *wt, int sock, u8 *cmd, size_t clen)
+{
+	u8 buf[WLANTEST_CTRL_MAX_RESP_LEN], *pos, *len;
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+
+	bss = ctrl_get_bss(wt, sock, cmd, clen);
+	if (bss == NULL)
+		return;
+
+	pos = buf;
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS);
+	pos += 4;
+	WPA_PUT_BE32(pos, WLANTEST_ATTR_STA_ADDR);
+	pos += 4;
+	len = pos; /* to be filled */
+	pos += 4;
+
+	dl_list_for_each(sta, &bss->sta, struct wlantest_sta, list) {
+		if (pos + ETH_ALEN > buf + WLANTEST_CTRL_MAX_RESP_LEN)
+			break;
+		os_memcpy(pos, sta->addr, ETH_ALEN);
+		pos += ETH_ALEN;
+	}
+
+	WPA_PUT_BE32(len, pos - len - 4);
+	ctrl_send(wt, sock, buf, pos - buf);
+}
+
+
+static void ctrl_flush(struct wlantest *wt, int sock)
+{
+	wpa_printf(MSG_DEBUG, "Drop all collected BSS data");
+	bss_flush(wt);
+	ctrl_send_simple(wt, sock, WLANTEST_CTRL_SUCCESS);
+}
+
+
+static void ctrl_clear_sta_counters(struct wlantest *wt, int sock, u8 *cmd,
+				    size_t clen)
+{
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+
+	bss = ctrl_get_bss(wt, sock, cmd, clen);
+	sta = ctrl_get_sta(wt, sock, cmd, clen, bss);
+	if (sta == NULL) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+		return;
+	}
+
+	os_memset(sta->counters, 0, sizeof(sta->counters));
+	os_memset(sta->tx_tid, 0, sizeof(sta->tx_tid));
+	os_memset(sta->rx_tid, 0, sizeof(sta->rx_tid));
+	ctrl_send_simple(wt, sock, WLANTEST_CTRL_SUCCESS);
+}
+
+
+static void ctrl_clear_bss_counters(struct wlantest *wt, int sock, u8 *cmd,
+				    size_t clen)
+{
+	struct wlantest_bss *bss;
+
+	bss = ctrl_get_bss(wt, sock, cmd, clen);
+	if (bss == NULL) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+		return;
+	}
+
+	os_memset(bss->counters, 0, sizeof(bss->counters));
+	ctrl_send_simple(wt, sock, WLANTEST_CTRL_SUCCESS);
+}
+
+
+static void ctrl_clear_tdls_counters(struct wlantest *wt, int sock, u8 *cmd,
+				     size_t clen)
+{
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+	struct wlantest_sta *sta2;
+	struct wlantest_tdls *tdls;
+
+	bss = ctrl_get_bss(wt, sock, cmd, clen);
+	sta = ctrl_get_sta(wt, sock, cmd, clen, bss);
+	sta2 = ctrl_get_sta2(wt, sock, cmd, clen, bss);
+	if (sta == NULL || sta2 == NULL) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+		return;
+	}
+
+	dl_list_for_each(tdls, &bss->tdls, struct wlantest_tdls, list) {
+		if ((tdls->init == sta && tdls->resp == sta2) ||
+		    (tdls->init == sta2 && tdls->resp == sta))
+			os_memset(tdls->counters, 0, sizeof(tdls->counters));
+	}
+	ctrl_send_simple(wt, sock, WLANTEST_CTRL_SUCCESS);
+}
+
+
+static void ctrl_get_sta_counter(struct wlantest *wt, int sock, u8 *cmd,
+				 size_t clen)
+{
+	u8 *addr;
+	size_t addr_len;
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+	u32 counter;
+	u8 buf[4 + 12], *end, *pos;
+
+	bss = ctrl_get_bss(wt, sock, cmd, clen);
+	sta = ctrl_get_sta(wt, sock, cmd, clen, bss);
+	if (sta == NULL)
+		return;
+
+	addr = attr_get(cmd, clen, WLANTEST_ATTR_STA_COUNTER, &addr_len);
+	if (addr == NULL || addr_len != 4) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return;
+	}
+	counter = WPA_GET_BE32(addr);
+	if (counter >= NUM_WLANTEST_STA_COUNTER) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return;
+	}
+
+	pos = buf;
+	end = buf + sizeof(buf);
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS);
+	pos += 4;
+	pos = attr_add_be32(pos, end, WLANTEST_ATTR_COUNTER,
+			    sta->counters[counter]);
+	ctrl_send(wt, sock, buf, pos - buf);
+}
+
+
+static void ctrl_get_bss_counter(struct wlantest *wt, int sock, u8 *cmd,
+				 size_t clen)
+{
+	u8 *addr;
+	size_t addr_len;
+	struct wlantest_bss *bss;
+	u32 counter;
+	u8 buf[4 + 12], *end, *pos;
+
+	bss = ctrl_get_bss(wt, sock, cmd, clen);
+	if (bss == NULL)
+		return;
+
+	addr = attr_get(cmd, clen, WLANTEST_ATTR_BSS_COUNTER, &addr_len);
+	if (addr == NULL || addr_len != 4) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return;
+	}
+	counter = WPA_GET_BE32(addr);
+	if (counter >= NUM_WLANTEST_BSS_COUNTER) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return;
+	}
+
+	pos = buf;
+	end = buf + sizeof(buf);
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS);
+	pos += 4;
+	pos = attr_add_be32(pos, end, WLANTEST_ATTR_COUNTER,
+			    bss->counters[counter]);
+	ctrl_send(wt, sock, buf, pos - buf);
+}
+
+
+static void ctrl_get_tdls_counter(struct wlantest *wt, int sock, u8 *cmd,
+				  size_t clen)
+{
+	u8 *addr;
+	size_t addr_len;
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+	struct wlantest_sta *sta2;
+	struct wlantest_tdls *tdls;
+	u32 counter;
+	u8 buf[4 + 12], *end, *pos;
+	int found = 0;
+
+	bss = ctrl_get_bss(wt, sock, cmd, clen);
+	sta = ctrl_get_sta(wt, sock, cmd, clen, bss);
+	sta2 = ctrl_get_sta2(wt, sock, cmd, clen, bss);
+	if (sta == NULL || sta2 == NULL) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+		return;
+	}
+
+	addr = attr_get(cmd, clen, WLANTEST_ATTR_TDLS_COUNTER, &addr_len);
+	if (addr == NULL || addr_len != 4) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return;
+	}
+	counter = WPA_GET_BE32(addr);
+	if (counter >= NUM_WLANTEST_TDLS_COUNTER) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return;
+	}
+
+	dl_list_for_each(tdls, &bss->tdls, struct wlantest_tdls, list) {
+		if (tdls->init == sta && tdls->resp == sta2) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (!found) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+		return;
+	}
+
+	pos = buf;
+	end = buf + sizeof(buf);
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS);
+	pos += 4;
+	pos = attr_add_be32(pos, end, WLANTEST_ATTR_COUNTER,
+			    tdls->counters[counter]);
+	ctrl_send(wt, sock, buf, pos - buf);
+}
+
+
+static void build_mgmt_hdr(struct ieee80211_mgmt *mgmt,
+			   struct wlantest_bss *bss, struct wlantest_sta *sta,
+			   int sender_ap, int stype)
+{
+	os_memset(mgmt, 0, 24);
+	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, stype);
+	if (sender_ap) {
+		if (sta)
+			os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
+		else
+			os_memset(mgmt->da, 0xff, ETH_ALEN);
+		os_memcpy(mgmt->sa, bss->bssid, ETH_ALEN);
+	} else {
+		os_memcpy(mgmt->da, bss->bssid, ETH_ALEN);
+		os_memcpy(mgmt->sa, sta->addr, ETH_ALEN);
+	}
+	os_memcpy(mgmt->bssid, bss->bssid, ETH_ALEN);
+}
+
+
+static int ctrl_inject_auth(struct wlantest *wt, struct wlantest_bss *bss,
+			    struct wlantest_sta *sta, int sender_ap,
+			    enum wlantest_inject_protection prot)
+{
+	struct ieee80211_mgmt mgmt;
+
+	if (prot != WLANTEST_INJECT_NORMAL &&
+	    prot != WLANTEST_INJECT_UNPROTECTED)
+		return -1; /* Authentication frame is never protected */
+	if (sta == NULL)
+		return -1; /* No broadcast Authentication frames */
+
+	if (sender_ap)
+		wpa_printf(MSG_INFO, "INJECT: Auth " MACSTR " -> " MACSTR,
+			   MAC2STR(bss->bssid), MAC2STR(sta->addr));
+	else
+		wpa_printf(MSG_INFO, "INJECT: Auth " MACSTR " -> " MACSTR,
+			   MAC2STR(sta->addr), MAC2STR(bss->bssid));
+	build_mgmt_hdr(&mgmt, bss, sta, sender_ap, WLAN_FC_STYPE_AUTH);
+
+	mgmt.u.auth.auth_alg = host_to_le16(WLAN_AUTH_OPEN);
+	mgmt.u.auth.auth_transaction = host_to_le16(1);
+	mgmt.u.auth.status_code = host_to_le16(WLAN_STATUS_SUCCESS);
+
+	return wlantest_inject(wt, bss, sta, (u8 *) &mgmt, 24 + 6,
+			       WLANTEST_INJECT_UNPROTECTED);
+}
+
+
+static int ctrl_inject_assocreq(struct wlantest *wt, struct wlantest_bss *bss,
+				struct wlantest_sta *sta, int sender_ap,
+				enum wlantest_inject_protection prot)
+{
+	u8 *buf;
+	struct ieee80211_mgmt *mgmt;
+	int ret;
+
+	if (prot != WLANTEST_INJECT_NORMAL &&
+	    prot != WLANTEST_INJECT_UNPROTECTED)
+		return -1; /* Association Request frame is never protected */
+	if (sta == NULL)
+		return -1; /* No broadcast Association Request frames */
+	if (sender_ap)
+		return -1; /* No Association Request frame sent by AP */
+	if (sta->assocreq_ies == NULL) {
+		wpa_printf(MSG_INFO, "INJECT: No previous (Re)Association "
+			   "Request available for " MACSTR,
+			   MAC2STR(sta->addr));
+		return -1;
+	}
+
+	wpa_printf(MSG_INFO, "INJECT: AssocReq " MACSTR " -> " MACSTR,
+		   MAC2STR(sta->addr), MAC2STR(bss->bssid));
+	buf = os_malloc(sizeof(*mgmt) + sta->assocreq_ies_len);
+	if (buf == NULL)
+		return -1;
+	mgmt = (struct ieee80211_mgmt *) buf;
+
+	build_mgmt_hdr(mgmt, bss, sta, sender_ap, WLAN_FC_STYPE_ASSOC_REQ);
+
+	mgmt->u.assoc_req.capab_info = host_to_le16(sta->assocreq_capab_info);
+	mgmt->u.assoc_req.listen_interval =
+		host_to_le16(sta->assocreq_listen_int);
+	os_memcpy(mgmt->u.assoc_req.variable, sta->assocreq_ies,
+		  sta->assocreq_ies_len);
+
+	ret = wlantest_inject(wt, bss, sta, buf,
+			      24 + 4 + sta->assocreq_ies_len,
+			      WLANTEST_INJECT_UNPROTECTED);
+	os_free(buf);
+	return ret;
+}
+
+
+static int ctrl_inject_reassocreq(struct wlantest *wt,
+				  struct wlantest_bss *bss,
+				  struct wlantest_sta *sta, int sender_ap,
+				  enum wlantest_inject_protection prot)
+{
+	u8 *buf;
+	struct ieee80211_mgmt *mgmt;
+	int ret;
+
+	if (prot != WLANTEST_INJECT_NORMAL &&
+	    prot != WLANTEST_INJECT_UNPROTECTED)
+		return -1; /* Reassociation Request frame is never protected */
+	if (sta == NULL)
+		return -1; /* No broadcast Reassociation Request frames */
+	if (sender_ap)
+		return -1; /* No Reassociation Request frame sent by AP */
+	if (sta->assocreq_ies == NULL) {
+		wpa_printf(MSG_INFO, "INJECT: No previous (Re)Association "
+			   "Request available for " MACSTR,
+			   MAC2STR(sta->addr));
+		return -1;
+	}
+
+	wpa_printf(MSG_INFO, "INJECT: ReassocReq " MACSTR " -> " MACSTR,
+		   MAC2STR(sta->addr), MAC2STR(bss->bssid));
+	buf = os_malloc(sizeof(*mgmt) + sta->assocreq_ies_len);
+	if (buf == NULL)
+		return -1;
+	mgmt = (struct ieee80211_mgmt *) buf;
+
+	build_mgmt_hdr(mgmt, bss, sta, sender_ap, WLAN_FC_STYPE_REASSOC_REQ);
+
+	mgmt->u.reassoc_req.capab_info =
+		host_to_le16(sta->assocreq_capab_info);
+	mgmt->u.reassoc_req.listen_interval =
+		host_to_le16(sta->assocreq_listen_int);
+	os_memcpy(mgmt->u.reassoc_req.current_ap, bss->bssid, ETH_ALEN);
+	os_memcpy(mgmt->u.reassoc_req.variable, sta->assocreq_ies,
+		  sta->assocreq_ies_len);
+
+	ret = wlantest_inject(wt, bss, sta, buf,
+			      24 + 10 + sta->assocreq_ies_len,
+			      WLANTEST_INJECT_UNPROTECTED);
+	os_free(buf);
+	return ret;
+}
+
+
+static int ctrl_inject_deauth(struct wlantest *wt, struct wlantest_bss *bss,
+			      struct wlantest_sta *sta, int sender_ap,
+			      enum wlantest_inject_protection prot)
+{
+	struct ieee80211_mgmt mgmt;
+
+	if (sender_ap) {
+		if (sta)
+			wpa_printf(MSG_INFO, "INJECT: Deauth " MACSTR " -> "
+				   MACSTR,
+				   MAC2STR(bss->bssid), MAC2STR(sta->addr));
+		else
+			wpa_printf(MSG_INFO, "INJECT: Deauth " MACSTR
+				   " -> broadcast", MAC2STR(bss->bssid));
+	} else
+		wpa_printf(MSG_INFO, "INJECT: Deauth " MACSTR " -> " MACSTR,
+			   MAC2STR(sta->addr), MAC2STR(bss->bssid));
+	build_mgmt_hdr(&mgmt, bss, sta, sender_ap, WLAN_FC_STYPE_DEAUTH);
+
+	mgmt.u.deauth.reason_code = host_to_le16(WLAN_REASON_UNSPECIFIED);
+
+	return wlantest_inject(wt, bss, sta, (u8 *) &mgmt, 24 + 2, prot);
+}
+
+
+static int ctrl_inject_disassoc(struct wlantest *wt, struct wlantest_bss *bss,
+				struct wlantest_sta *sta, int sender_ap,
+				enum wlantest_inject_protection prot)
+{
+	struct ieee80211_mgmt mgmt;
+
+	if (sender_ap) {
+		if (sta)
+			wpa_printf(MSG_INFO, "INJECT: Disassoc " MACSTR " -> "
+				   MACSTR,
+				   MAC2STR(bss->bssid), MAC2STR(sta->addr));
+		else
+			wpa_printf(MSG_INFO, "INJECT: Disassoc " MACSTR
+				   " -> broadcast", MAC2STR(bss->bssid));
+	} else
+		wpa_printf(MSG_INFO, "INJECT: Disassoc " MACSTR " -> " MACSTR,
+			   MAC2STR(sta->addr), MAC2STR(bss->bssid));
+	build_mgmt_hdr(&mgmt, bss, sta, sender_ap, WLAN_FC_STYPE_DISASSOC);
+
+	mgmt.u.disassoc.reason_code = host_to_le16(WLAN_REASON_UNSPECIFIED);
+
+	return wlantest_inject(wt, bss, sta, (u8 *) &mgmt, 24 + 2, prot);
+}
+
+
+static int ctrl_inject_saqueryreq(struct wlantest *wt,
+				  struct wlantest_bss *bss,
+				  struct wlantest_sta *sta, int sender_ap,
+				  enum wlantest_inject_protection prot)
+{
+	struct ieee80211_mgmt mgmt;
+
+	if (sta == NULL)
+		return -1; /* No broadcast SA Query frames */
+
+	if (sender_ap)
+		wpa_printf(MSG_INFO, "INJECT: SA Query Request " MACSTR " -> "
+			   MACSTR, MAC2STR(bss->bssid), MAC2STR(sta->addr));
+	else
+		wpa_printf(MSG_INFO, "INJECT: SA Query Request " MACSTR " -> "
+			   MACSTR, MAC2STR(sta->addr), MAC2STR(bss->bssid));
+	build_mgmt_hdr(&mgmt, bss, sta, sender_ap, WLAN_FC_STYPE_ACTION);
+
+	mgmt.u.action.category = WLAN_ACTION_SA_QUERY;
+	mgmt.u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST;
+	mgmt.u.action.u.sa_query_req.trans_id[0] = 0x12;
+	mgmt.u.action.u.sa_query_req.trans_id[1] = 0x34;
+	os_memcpy(sender_ap ? sta->ap_sa_query_tr : sta->sta_sa_query_tr,
+		  mgmt.u.action.u.sa_query_req.trans_id,
+		  WLAN_SA_QUERY_TR_ID_LEN);
+	return wlantest_inject(wt, bss, sta, (u8 *) &mgmt, 24 + 4, prot);
+}
+
+
+static void ctrl_inject(struct wlantest *wt, int sock, u8 *cmd, size_t clen)
+{
+	u8 *bssid, *sta_addr;
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+	int frame, sender_ap, prot;
+	int ret = 0;
+
+	bssid = attr_get_macaddr(cmd, clen, WLANTEST_ATTR_BSSID);
+	sta_addr = attr_get_macaddr(cmd, clen, WLANTEST_ATTR_STA_ADDR);
+	frame = attr_get_int(cmd, clen, WLANTEST_ATTR_INJECT_FRAME);
+	sender_ap = attr_get_int(cmd, clen, WLANTEST_ATTR_INJECT_SENDER_AP);
+	if (sender_ap < 0)
+		sender_ap = 0;
+	prot = attr_get_int(cmd, clen, WLANTEST_ATTR_INJECT_PROTECTION);
+	if (bssid == NULL || sta_addr == NULL || frame < 0 || prot < 0) {
+		wpa_printf(MSG_INFO, "Invalid inject command parameters");
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return;
+	}
+
+	bss = bss_find(wt, bssid);
+	if (bss == NULL) {
+		wpa_printf(MSG_INFO, "BSS not found for inject command");
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+		return;
+	}
+
+	if (is_broadcast_ether_addr(sta_addr)) {
+		if (!sender_ap) {
+			wpa_printf(MSG_INFO, "Invalid broadcast inject "
+				   "command without sender_ap set");
+			ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+			return;
+		} sta = NULL;
+	} else {
+		sta = sta_find(bss, sta_addr);
+		if (sta == NULL) {
+			wpa_printf(MSG_INFO, "Station not found for inject "
+				   "command");
+			ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+			return;
+		}
+	}
+
+	switch (frame) {
+	case WLANTEST_FRAME_AUTH:
+		ret = ctrl_inject_auth(wt, bss, sta, sender_ap, prot);
+		break;
+	case WLANTEST_FRAME_ASSOCREQ:
+		ret = ctrl_inject_assocreq(wt, bss, sta, sender_ap, prot);
+		break;
+	case WLANTEST_FRAME_REASSOCREQ:
+		ret = ctrl_inject_reassocreq(wt, bss, sta, sender_ap, prot);
+		break;
+	case WLANTEST_FRAME_DEAUTH:
+		ret = ctrl_inject_deauth(wt, bss, sta, sender_ap, prot);
+		break;
+	case WLANTEST_FRAME_DISASSOC:
+		ret = ctrl_inject_disassoc(wt, bss, sta, sender_ap, prot);
+		break;
+	case WLANTEST_FRAME_SAQUERYREQ:
+		ret = ctrl_inject_saqueryreq(wt, bss, sta, sender_ap, prot);
+		break;
+	default:
+		wpa_printf(MSG_INFO, "Unsupported inject command frame %d",
+			   frame);
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return;
+	}
+
+	if (ret)
+		wpa_printf(MSG_INFO, "Failed to inject frame");
+	else
+		wpa_printf(MSG_INFO, "Frame injected successfully");
+	ctrl_send_simple(wt, sock, ret == 0 ? WLANTEST_CTRL_SUCCESS :
+			 WLANTEST_CTRL_FAILURE);
+}
+
+
+static void ctrl_version(struct wlantest *wt, int sock)
+{
+	u8 buf[WLANTEST_CTRL_MAX_RESP_LEN], *pos;
+
+	pos = buf;
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS);
+	pos += 4;
+	pos = attr_add_str(pos, buf + sizeof(buf), WLANTEST_ATTR_VERSION,
+			   VERSION_STR);
+	ctrl_send(wt, sock, buf, pos - buf);
+}
+
+
+static void ctrl_add_passphrase(struct wlantest *wt, int sock, u8 *cmd,
+				size_t clen)
+{
+	u8 *passphrase;
+	size_t len;
+	struct wlantest_passphrase *p, *pa;
+	u8 *bssid;
+
+	passphrase = attr_get(cmd, clen, WLANTEST_ATTR_PASSPHRASE, &len);
+	if (passphrase == NULL) {
+		u8 *wepkey;
+		char *key;
+		enum wlantest_ctrl_cmd res;
+
+		wepkey = attr_get(cmd, clen, WLANTEST_ATTR_WEPKEY, &len);
+		if (wepkey == NULL) {
+			ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+			return;
+		}
+		key = os_zalloc(len + 1);
+		if (key == NULL) {
+			ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+			return;
+		}
+		os_memcpy(key, wepkey, len);
+		if (add_wep(wt, key) < 0)
+			res = WLANTEST_CTRL_FAILURE;
+		else
+			res = WLANTEST_CTRL_SUCCESS;
+		os_free(key);
+		ctrl_send_simple(wt, sock, res);
+		return;
+	}
+
+	if (len < 8 || len > 63) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return;
+	}
+
+	p = os_zalloc(sizeof(*p));
+	if (p == NULL) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+		return;
+	}
+	os_memcpy(p->passphrase, passphrase, len);
+	wpa_printf(MSG_INFO, "Add passphrase '%s'", p->passphrase);
+
+	bssid = attr_get_macaddr(cmd, clen, WLANTEST_ATTR_BSSID);
+	if (bssid) {
+		os_memcpy(p->bssid, bssid, ETH_ALEN);
+		wpa_printf(MSG_INFO, "Limit passphrase for BSSID " MACSTR,
+			   MAC2STR(p->bssid));
+	}
+
+	dl_list_for_each(pa, &wt->passphrase, struct wlantest_passphrase, list)
+	{
+		if (os_strcmp(p->passphrase, pa->passphrase) == 0 &&
+		    os_memcmp(p->bssid, pa->bssid, ETH_ALEN) == 0) {
+			wpa_printf(MSG_INFO, "Passphrase was already known");
+			os_free(p);
+			p = NULL;
+			break;
+		}
+	}
+
+	if (p) {
+		struct wlantest_bss *bss;
+		dl_list_add(&wt->passphrase, &p->list);
+		dl_list_for_each(bss, &wt->bss, struct wlantest_bss, list) {
+			if (bssid &&
+			    os_memcmp(p->bssid, bss->bssid, ETH_ALEN) != 0)
+				continue;
+			bss_add_pmk_from_passphrase(bss, p->passphrase);
+		}
+	}
+
+	ctrl_send_simple(wt, sock, WLANTEST_CTRL_SUCCESS);
+}
+
+
+static void info_print_proto(char *buf, size_t len, int proto)
+{
+	char *pos, *end;
+
+	if (proto == 0) {
+		os_snprintf(buf, len, "OPEN");
+		return;
+	}
+
+	pos = buf;
+	end = buf + len;
+
+	if (proto & WPA_PROTO_WPA)
+		pos += os_snprintf(pos, end - pos, "%sWPA",
+				   pos == buf ? "" : " ");
+	if (proto & WPA_PROTO_RSN)
+		pos += os_snprintf(pos, end - pos, "%sWPA2",
+				   pos == buf ? "" : " ");
+}
+
+
+static void info_print_cipher(char *buf, size_t len, int cipher)
+{
+	char *pos, *end;
+
+	if (cipher == 0) {
+		os_snprintf(buf, len, "N/A");
+		return;
+	}
+
+	pos = buf;
+	end = buf + len;
+
+	if (cipher & WPA_CIPHER_NONE)
+		pos += os_snprintf(pos, end - pos, "%sNONE",
+				   pos == buf ? "" : " ");
+	if (cipher & WPA_CIPHER_WEP40)
+		pos += os_snprintf(pos, end - pos, "%sWEP40",
+				   pos == buf ? "" : " ");
+	if (cipher & WPA_CIPHER_WEP104)
+		pos += os_snprintf(pos, end - pos, "%sWEP104",
+				   pos == buf ? "" : " ");
+	if (cipher & WPA_CIPHER_TKIP)
+		pos += os_snprintf(pos, end - pos, "%sTKIP",
+				   pos == buf ? "" : " ");
+	if (cipher & WPA_CIPHER_CCMP)
+		pos += os_snprintf(pos, end - pos, "%sCCMP",
+				   pos == buf ? "" : " ");
+	if (cipher & WPA_CIPHER_AES_128_CMAC)
+		pos += os_snprintf(pos, end - pos, "%sBIP",
+				   pos == buf ? "" : " ");
+	if (cipher & WPA_CIPHER_BIP_GMAC_128)
+		pos += os_snprintf(pos, end - pos, "%sBIP-GMAC-128",
+				   pos == buf ? "" : " ");
+	if (cipher & WPA_CIPHER_BIP_GMAC_256)
+		pos += os_snprintf(pos, end - pos, "%sBIP-GMAC-256",
+				   pos == buf ? "" : " ");
+	if (cipher & WPA_CIPHER_BIP_CMAC_256)
+		pos += os_snprintf(pos, end - pos, "%sBIP-CMAC-256",
+				   pos == buf ? "" : " ");
+}
+
+
+static void info_print_key_mgmt(char *buf, size_t len, int key_mgmt)
+{
+	char *pos, *end;
+
+	if (key_mgmt == 0) {
+		os_snprintf(buf, len, "N/A");
+		return;
+	}
+
+	pos = buf;
+	end = buf + len;
+
+	if (key_mgmt & WPA_KEY_MGMT_IEEE8021X)
+		pos += os_snprintf(pos, end - pos, "%sEAP",
+				   pos == buf ? "" : " ");
+	if (key_mgmt & WPA_KEY_MGMT_PSK)
+		pos += os_snprintf(pos, end - pos, "%sPSK",
+				   pos == buf ? "" : " ");
+	if (key_mgmt & WPA_KEY_MGMT_WPA_NONE)
+		pos += os_snprintf(pos, end - pos, "%sWPA-NONE",
+				   pos == buf ? "" : " ");
+	if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
+		pos += os_snprintf(pos, end - pos, "%sFT-EAP",
+				   pos == buf ? "" : " ");
+	if (key_mgmt & WPA_KEY_MGMT_FT_PSK)
+		pos += os_snprintf(pos, end - pos, "%sFT-PSK",
+				   pos == buf ? "" : " ");
+	if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256)
+		pos += os_snprintf(pos, end - pos, "%sEAP-SHA256",
+				   pos == buf ? "" : " ");
+	if (key_mgmt & WPA_KEY_MGMT_PSK_SHA256)
+		pos += os_snprintf(pos, end - pos, "%sPSK-SHA256",
+				   pos == buf ? "" : " ");
+	if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B)
+		pos += os_snprintf(pos, end - pos, "%sEAP-SUITE-B",
+				   pos == buf ? "" : " ");
+	if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+		pos += os_snprintf(pos, end - pos, "%sEAP-SUITE-B-192",
+				   pos == buf ? "" : " ");
+}
+
+
+static void info_print_rsn_capab(char *buf, size_t len, int capab)
+{
+	char *pos, *end;
+
+	pos = buf;
+	end = buf + len;
+
+	if (capab & WPA_CAPABILITY_PREAUTH)
+		pos += os_snprintf(pos, end - pos, "%sPREAUTH",
+				   pos == buf ? "" : " ");
+	if (capab & WPA_CAPABILITY_NO_PAIRWISE)
+		pos += os_snprintf(pos, end - pos, "%sNO_PAIRWISE",
+				   pos == buf ? "" : " ");
+	if (capab & WPA_CAPABILITY_MFPR)
+		pos += os_snprintf(pos, end - pos, "%sMFPR",
+				   pos == buf ? "" : " ");
+	if (capab & WPA_CAPABILITY_MFPC)
+		pos += os_snprintf(pos, end - pos, "%sMFPC",
+				   pos == buf ? "" : " ");
+	if (capab & WPA_CAPABILITY_PEERKEY_ENABLED)
+		pos += os_snprintf(pos, end - pos, "%sPEERKEY",
+				   pos == buf ? "" : " ");
+	if (capab & WPA_CAPABILITY_OCVC)
+		pos += os_snprintf(pos, end - pos, "%sOCVC",
+				   pos == buf ? "" : " ");
+}
+
+
+static void info_print_state(char *buf, size_t len, int state)
+{
+	switch (state) {
+	case STATE1:
+		os_strlcpy(buf, "NOT-AUTH", len);
+		break;
+	case STATE2:
+		os_strlcpy(buf, "AUTH", len);
+		break;
+	case STATE3:
+		os_strlcpy(buf, "AUTH+ASSOC", len);
+		break;
+	}
+}
+
+
+static void info_print_gtk(char *buf, size_t len, struct wlantest_sta *sta)
+{
+	size_t pos;
+
+	pos = os_snprintf(buf, len, "IDX=%d,GTK=", sta->gtk_idx);
+	wpa_snprintf_hex(buf + pos, len - pos, sta->gtk, sta->gtk_len);
+}
+
+
+static void ctrl_info_sta(struct wlantest *wt, int sock, u8 *cmd, size_t clen)
+{
+	u8 *addr;
+	size_t addr_len;
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+	enum wlantest_sta_info info;
+	u8 buf[4 + 108], *end, *pos;
+	char resp[100];
+
+	bss = ctrl_get_bss(wt, sock, cmd, clen);
+	sta = ctrl_get_sta(wt, sock, cmd, clen, bss);
+	if (sta == NULL)
+		return;
+
+	addr = attr_get(cmd, clen, WLANTEST_ATTR_STA_INFO, &addr_len);
+	if (addr == NULL || addr_len != 4) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return;
+	}
+	info = WPA_GET_BE32(addr);
+
+	resp[0] = '\0';
+	switch (info) {
+	case WLANTEST_STA_INFO_PROTO:
+		info_print_proto(resp, sizeof(resp), sta->proto);
+		break;
+	case WLANTEST_STA_INFO_PAIRWISE:
+		info_print_cipher(resp, sizeof(resp), sta->pairwise_cipher);
+		break;
+	case WLANTEST_STA_INFO_KEY_MGMT:
+		info_print_key_mgmt(resp, sizeof(resp), sta->key_mgmt);
+		break;
+	case WLANTEST_STA_INFO_RSN_CAPAB:
+		info_print_rsn_capab(resp, sizeof(resp), sta->rsn_capab);
+		break;
+	case WLANTEST_STA_INFO_STATE:
+		info_print_state(resp, sizeof(resp), sta->state);
+		break;
+	case WLANTEST_STA_INFO_GTK:
+		info_print_gtk(resp, sizeof(resp), sta);
+		break;
+	default:
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return;
+	}
+
+	pos = buf;
+	end = buf + sizeof(buf);
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS);
+	pos += 4;
+	pos = attr_add_str(pos, end, WLANTEST_ATTR_INFO, resp);
+	ctrl_send(wt, sock, buf, pos - buf);
+}
+
+
+static void ctrl_info_bss(struct wlantest *wt, int sock, u8 *cmd, size_t clen)
+{
+	u8 *addr;
+	size_t addr_len;
+	struct wlantest_bss *bss;
+	enum wlantest_bss_info info;
+	u8 buf[4 + 108], *end, *pos;
+	char resp[100];
+
+	bss = ctrl_get_bss(wt, sock, cmd, clen);
+	if (bss == NULL)
+		return;
+
+	addr = attr_get(cmd, clen, WLANTEST_ATTR_BSS_INFO, &addr_len);
+	if (addr == NULL || addr_len != 4) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return;
+	}
+	info = WPA_GET_BE32(addr);
+
+	resp[0] = '\0';
+	switch (info) {
+	case WLANTEST_BSS_INFO_PROTO:
+		info_print_proto(resp, sizeof(resp), bss->proto);
+		break;
+	case WLANTEST_BSS_INFO_PAIRWISE:
+		info_print_cipher(resp, sizeof(resp), bss->pairwise_cipher);
+		break;
+	case WLANTEST_BSS_INFO_GROUP:
+		info_print_cipher(resp, sizeof(resp), bss->group_cipher);
+		break;
+	case WLANTEST_BSS_INFO_GROUP_MGMT:
+		info_print_cipher(resp, sizeof(resp), bss->mgmt_group_cipher);
+		break;
+	case WLANTEST_BSS_INFO_KEY_MGMT:
+		info_print_key_mgmt(resp, sizeof(resp), bss->key_mgmt);
+		break;
+	case WLANTEST_BSS_INFO_RSN_CAPAB:
+		info_print_rsn_capab(resp, sizeof(resp), bss->rsn_capab);
+		break;
+	default:
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return;
+	}
+
+	pos = buf;
+	end = buf + sizeof(buf);
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS);
+	pos += 4;
+	pos = attr_add_str(pos, end, WLANTEST_ATTR_INFO, resp);
+	ctrl_send(wt, sock, buf, pos - buf);
+}
+
+
+static void ctrl_send_(struct wlantest *wt, int sock, u8 *cmd, size_t clen)
+{
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+	u8 *bssid, *sta_addr;
+	int prot;
+	u8 *frame;
+	size_t frame_len;
+	int ret = 0;
+	struct ieee80211_hdr *hdr;
+	u16 fc;
+
+	frame = attr_get(cmd, clen, WLANTEST_ATTR_FRAME, &frame_len);
+	prot = attr_get_int(cmd, clen, WLANTEST_ATTR_INJECT_PROTECTION);
+	if (frame == NULL || frame_len < 24 || prot < 0) {
+		wpa_printf(MSG_INFO, "Invalid send command parameters");
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return;
+	}
+
+	hdr = (struct ieee80211_hdr *) frame;
+	fc = le_to_host16(hdr->frame_control);
+	switch (WLAN_FC_GET_TYPE(fc)) {
+	case WLAN_FC_TYPE_MGMT:
+		bssid = hdr->addr3;
+		if (os_memcmp(hdr->addr2, hdr->addr3, ETH_ALEN) == 0)
+			sta_addr = hdr->addr1;
+		else
+			sta_addr = hdr->addr2;
+		break;
+	case WLAN_FC_TYPE_DATA:
+		switch (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) {
+		case 0:
+			bssid = hdr->addr3;
+			sta_addr = hdr->addr2;
+			break;
+		case WLAN_FC_TODS:
+			bssid = hdr->addr1;
+			sta_addr = hdr->addr2;
+			break;
+		case WLAN_FC_FROMDS:
+			bssid = hdr->addr2;
+			sta_addr = hdr->addr1;
+			break;
+		default:
+			wpa_printf(MSG_INFO, "Unsupported inject frame");
+			ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+			return;
+		}
+		break;
+	default:
+		wpa_printf(MSG_INFO, "Unsupported inject frame");
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+		return;
+	}
+
+	bss = bss_find(wt, bssid);
+	if (bss == NULL && prot != WLANTEST_INJECT_UNPROTECTED) {
+		wpa_printf(MSG_INFO, "Unknown BSSID");
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+		return;
+	}
+
+	if (bss)
+		sta = sta_find(bss, sta_addr);
+	else
+		sta = NULL;
+	if (sta == NULL && prot != WLANTEST_INJECT_UNPROTECTED) {
+		wpa_printf(MSG_INFO, "Unknown STA address");
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+		return;
+	}
+
+	ret = wlantest_inject(wt, bss, sta, frame, frame_len, prot);
+
+	if (ret)
+		wpa_printf(MSG_INFO, "Failed to inject frame");
+	else
+		wpa_printf(MSG_INFO, "Frame injected successfully");
+	ctrl_send_simple(wt, sock, ret == 0 ? WLANTEST_CTRL_SUCCESS :
+			 WLANTEST_CTRL_FAILURE);
+}
+
+
+static void ctrl_relog(struct wlantest *wt, int sock)
+{
+	int res = wlantest_relog(wt);
+	ctrl_send_simple(wt, sock, res ? WLANTEST_CTRL_FAILURE :
+			 WLANTEST_CTRL_SUCCESS);
+}
+
+
+static void ctrl_get_tx_tid(struct wlantest *wt, int sock, u8 *cmd, size_t clen)
+{
+	u8 *addr;
+	size_t addr_len;
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+	u32 counter;
+	u8 buf[4 + 12], *end, *pos;
+
+	bss = ctrl_get_bss(wt, sock, cmd, clen);
+	sta = ctrl_get_sta(wt, sock, cmd, clen, bss);
+	if (sta == NULL)
+		return;
+
+	addr = attr_get(cmd, clen, WLANTEST_ATTR_TID, &addr_len);
+	if (addr == NULL || addr_len != 4) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return;
+	}
+	counter = WPA_GET_BE32(addr);
+	if (counter >= 16 + 1) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return;
+	}
+
+	pos = buf;
+	end = buf + sizeof(buf);
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS);
+	pos += 4;
+	pos = attr_add_be32(pos, end, WLANTEST_ATTR_COUNTER,
+			    sta->tx_tid[counter]);
+	ctrl_send(wt, sock, buf, pos - buf);
+}
+
+
+static void ctrl_get_rx_tid(struct wlantest *wt, int sock, u8 *cmd, size_t clen)
+{
+	u8 *addr;
+	size_t addr_len;
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+	u32 counter;
+	u8 buf[4 + 12], *end, *pos;
+
+	bss = ctrl_get_bss(wt, sock, cmd, clen);
+	sta = ctrl_get_sta(wt, sock, cmd, clen, bss);
+	if (sta == NULL)
+		return;
+
+	addr = attr_get(cmd, clen, WLANTEST_ATTR_TID, &addr_len);
+	if (addr == NULL || addr_len != 4) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return;
+	}
+	counter = WPA_GET_BE32(addr);
+	if (counter >= 16 + 1) {
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+		return;
+	}
+
+	pos = buf;
+	end = buf + sizeof(buf);
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS);
+	pos += 4;
+	pos = attr_add_be32(pos, end, WLANTEST_ATTR_COUNTER,
+			    sta->rx_tid[counter]);
+	ctrl_send(wt, sock, buf, pos - buf);
+}
+
+
+static void ctrl_read(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct wlantest *wt = eloop_ctx;
+	u8 buf[WLANTEST_CTRL_MAX_CMD_LEN];
+	int len;
+	enum wlantest_ctrl_cmd cmd;
+
+	wpa_printf(MSG_EXCESSIVE, "New control interface message from %d",
+		   sock);
+	len = recv(sock, buf, sizeof(buf), 0);
+	if (len < 0) {
+		wpa_printf(MSG_INFO, "recv(ctrl): %s", strerror(errno));
+		ctrl_disconnect(wt, sock);
+		return;
+	}
+	if (len == 0) {
+		ctrl_disconnect(wt, sock);
+		return;
+	}
+
+	if (len < 4) {
+		wpa_printf(MSG_INFO, "Too short control interface command "
+			   "from %d", sock);
+		ctrl_disconnect(wt, sock);
+		return;
+	}
+	cmd = WPA_GET_BE32(buf);
+	wpa_printf(MSG_EXCESSIVE, "Control interface command %d from %d",
+		   cmd, sock);
+
+	switch (cmd) {
+	case WLANTEST_CTRL_PING:
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_SUCCESS);
+		break;
+	case WLANTEST_CTRL_TERMINATE:
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_SUCCESS);
+		eloop_terminate();
+		break;
+	case WLANTEST_CTRL_LIST_BSS:
+		ctrl_list_bss(wt, sock);
+		break;
+	case WLANTEST_CTRL_LIST_STA:
+		ctrl_list_sta(wt, sock, buf + 4, len - 4);
+		break;
+	case WLANTEST_CTRL_FLUSH:
+		ctrl_flush(wt, sock);
+		break;
+	case WLANTEST_CTRL_CLEAR_STA_COUNTERS:
+		ctrl_clear_sta_counters(wt, sock, buf + 4, len - 4);
+		break;
+	case WLANTEST_CTRL_CLEAR_BSS_COUNTERS:
+		ctrl_clear_bss_counters(wt, sock, buf + 4, len - 4);
+		break;
+	case WLANTEST_CTRL_CLEAR_TDLS_COUNTERS:
+		ctrl_clear_tdls_counters(wt, sock, buf + 4, len - 4);
+		break;
+	case WLANTEST_CTRL_GET_STA_COUNTER:
+		ctrl_get_sta_counter(wt, sock, buf + 4, len - 4);
+		break;
+	case WLANTEST_CTRL_GET_BSS_COUNTER:
+		ctrl_get_bss_counter(wt, sock, buf + 4, len - 4);
+		break;
+	case WLANTEST_CTRL_GET_TDLS_COUNTER:
+		ctrl_get_tdls_counter(wt, sock, buf + 4, len - 4);
+		break;
+	case WLANTEST_CTRL_INJECT:
+		ctrl_inject(wt, sock, buf + 4, len - 4);
+		break;
+	case WLANTEST_CTRL_VERSION:
+		ctrl_version(wt, sock);
+		break;
+	case WLANTEST_CTRL_ADD_PASSPHRASE:
+		ctrl_add_passphrase(wt, sock, buf + 4, len - 4);
+		break;
+	case WLANTEST_CTRL_INFO_STA:
+		ctrl_info_sta(wt, sock, buf + 4, len - 4);
+		break;
+	case WLANTEST_CTRL_INFO_BSS:
+		ctrl_info_bss(wt, sock, buf + 4, len - 4);
+		break;
+	case WLANTEST_CTRL_SEND:
+		ctrl_send_(wt, sock, buf + 4, len - 4);
+		break;
+	case WLANTEST_CTRL_RELOG:
+		ctrl_relog(wt, sock);
+		break;
+	case WLANTEST_CTRL_GET_TX_TID:
+		ctrl_get_tx_tid(wt, sock, buf + 4, len - 4);
+		break;
+	case WLANTEST_CTRL_GET_RX_TID:
+		ctrl_get_rx_tid(wt, sock, buf + 4, len - 4);
+		break;
+	default:
+		ctrl_send_simple(wt, sock, WLANTEST_CTRL_UNKNOWN_CMD);
+		break;
+	}
+}
+
+
+static void ctrl_connect(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct wlantest *wt = eloop_ctx;
+	int conn, i;
+
+	conn = accept(sock, NULL, NULL);
+	if (conn < 0) {
+		wpa_printf(MSG_INFO, "accept(ctrl): %s", strerror(errno));
+		return;
+	}
+	wpa_printf(MSG_MSGDUMP, "New control interface connection %d", conn);
+
+	for (i = 0; i < MAX_CTRL_CONNECTIONS; i++) {
+		if (wt->ctrl_socks[i] < 0)
+			break;
+	}
+
+	if (i == MAX_CTRL_CONNECTIONS) {
+		wpa_printf(MSG_INFO, "No room for new control connection");
+		close(conn);
+		return;
+	}
+
+	wt->ctrl_socks[i] = conn;
+	eloop_register_read_sock(conn, ctrl_read, wt, NULL);
+}
+
+
+int ctrl_init(struct wlantest *wt)
+{
+	struct sockaddr_un addr;
+
+	wt->ctrl_sock = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+	if (wt->ctrl_sock < 0) {
+		wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
+		return -1;
+	}
+
+	os_memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+	os_strlcpy(addr.sun_path + 1, WLANTEST_SOCK_NAME,
+		   sizeof(addr.sun_path) - 1);
+	if (bind(wt->ctrl_sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		wpa_printf(MSG_ERROR, "bind: %s", strerror(errno));
+		close(wt->ctrl_sock);
+		wt->ctrl_sock = -1;
+		return -1;
+	}
+
+	if (listen(wt->ctrl_sock, 5) < 0) {
+		wpa_printf(MSG_ERROR, "listen: %s", strerror(errno));
+		close(wt->ctrl_sock);
+		wt->ctrl_sock = -1;
+		return -1;
+	}
+
+	if (eloop_register_read_sock(wt->ctrl_sock, ctrl_connect, wt, NULL)) {
+		close(wt->ctrl_sock);
+		wt->ctrl_sock = -1;
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void ctrl_deinit(struct wlantest *wt)
+{
+	int i;
+
+	if (wt->ctrl_sock < 0)
+		return;
+
+	for (i = 0; i < MAX_CTRL_CONNECTIONS; i++) {
+		if (wt->ctrl_socks[i] >= 0) {
+			close(wt->ctrl_socks[i]);
+			eloop_unregister_read_sock(wt->ctrl_socks[i]);
+			wt->ctrl_socks[i] = -1;
+		}
+	}
+
+	eloop_unregister_read_sock(wt->ctrl_sock);
+	close(wt->ctrl_sock);
+	wt->ctrl_sock = -1;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/gcmp.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/gcmp.c
new file mode 100644
index 0000000..a29496d
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/gcmp.c
@@ -0,0 +1,171 @@
+/*
+ * GCM with GMAC Protocol (GCMP)
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "crypto/aes.h"
+#include "crypto/aes_wrap.h"
+#include "wlantest.h"
+
+
+static void gcmp_aad_nonce(const struct ieee80211_hdr *hdr, const u8 *data,
+			   const u8 *a1, const u8 *a2, const u8 *a3,
+			   u8 *aad, size_t *aad_len, u8 *nonce)
+{
+	u16 fc, stype, seq;
+	int qos = 0, addr4 = 0;
+	u8 *pos;
+
+	fc = le_to_host16(hdr->frame_control);
+	stype = WLAN_FC_GET_STYPE(fc);
+	if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) ==
+	    (WLAN_FC_TODS | WLAN_FC_FROMDS))
+		addr4 = 1;
+
+	if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA) {
+		fc &= ~0x0070; /* Mask subtype bits */
+		if (stype & 0x08) {
+			const u8 *qc;
+			qos = 1;
+			fc &= ~WLAN_FC_HTC;
+			qc = (const u8 *) (hdr + 1);
+			if (addr4)
+				qc += ETH_ALEN;
+		}
+	}
+
+	fc &= ~(WLAN_FC_RETRY | WLAN_FC_PWRMGT | WLAN_FC_MOREDATA);
+	WPA_PUT_LE16(aad, fc);
+	pos = aad + 2;
+	os_memcpy(pos, hdr->addr1, 3 * ETH_ALEN);
+	if (a1)
+		os_memcpy(pos, a1, ETH_ALEN);
+	if (a2)
+		os_memcpy(pos + ETH_ALEN, a2, ETH_ALEN);
+	if (a3)
+		os_memcpy(pos + 2 * ETH_ALEN, a3, ETH_ALEN);
+	pos += 3 * ETH_ALEN;
+	seq = le_to_host16(hdr->seq_ctrl);
+	seq &= ~0xfff0; /* Mask Seq#; do not modify Frag# */
+	WPA_PUT_LE16(pos, seq);
+	pos += 2;
+
+	os_memcpy(pos, hdr + 1, addr4 * ETH_ALEN + qos * 2);
+	pos += addr4 * ETH_ALEN;
+	if (qos) {
+		pos[0] &= ~0x70;
+		if (1 /* FIX: either device has SPP A-MSDU Capab = 0 */)
+			pos[0] &= ~0x80;
+		pos++;
+		*pos++ = 0x00;
+	}
+
+	*aad_len = pos - aad;
+
+	if (a2)
+		os_memcpy(nonce, a2, ETH_ALEN);
+	else
+		os_memcpy(nonce, hdr->addr2, ETH_ALEN);
+	nonce[6] = data[7]; /* PN5 */
+	nonce[7] = data[6]; /* PN4 */
+	nonce[8] = data[5]; /* PN3 */
+	nonce[9] = data[4]; /* PN2 */
+	nonce[10] = data[1]; /* PN1 */
+	nonce[11] = data[0]; /* PN0 */
+}
+
+
+u8 * gcmp_decrypt(const u8 *tk, size_t tk_len, const struct ieee80211_hdr *hdr,
+		  const u8 *a1, const u8 *a2, const u8 *a3,
+		  const u8 *data, size_t data_len, size_t *decrypted_len)
+{
+	u8 aad[30], nonce[12], *plain;
+	size_t aad_len, mlen;
+	const u8 *m;
+
+	if (data_len < 8 + 16)
+		return NULL;
+
+	plain = os_malloc(data_len + AES_BLOCK_SIZE);
+	if (plain == NULL)
+		return NULL;
+
+	m = data + 8;
+	mlen = data_len - 8 - 16;
+
+	os_memset(aad, 0, sizeof(aad));
+	gcmp_aad_nonce(hdr, data, a1, a2, a3, aad, &aad_len, nonce);
+	wpa_hexdump(MSG_EXCESSIVE, "GCMP AAD", aad, aad_len);
+	wpa_hexdump(MSG_EXCESSIVE, "GCMP nonce", nonce, sizeof(nonce));
+
+	if (aes_gcm_ad(tk, tk_len, nonce, sizeof(nonce), m, mlen, aad, aad_len,
+		       m + mlen, plain) < 0) {
+		u16 seq_ctrl = le_to_host16(hdr->seq_ctrl);
+		wpa_printf(MSG_INFO, "Invalid GCMP frame: A1=" MACSTR
+			   " A2=" MACSTR " A3=" MACSTR " seq=%u frag=%u",
+			   MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+			   MAC2STR(hdr->addr3),
+			   WLAN_GET_SEQ_SEQ(seq_ctrl),
+			   WLAN_GET_SEQ_FRAG(seq_ctrl));
+		os_free(plain);
+		return NULL;
+	}
+
+	*decrypted_len = mlen;
+	return plain;
+}
+
+
+u8 * gcmp_encrypt(const u8 *tk, size_t tk_len, const u8 *frame, size_t len,
+		  size_t hdrlen, const u8 *qos, const u8 *a1, const u8 *a2,
+		  const u8 *a3, const u8 *pn, int keyid, size_t *encrypted_len)
+{
+	u8 aad[30], nonce[12], *crypt, *pos;
+	size_t aad_len, plen;
+	struct ieee80211_hdr *hdr;
+
+	if (len < hdrlen || hdrlen < 24)
+		return NULL;
+	plen = len - hdrlen;
+
+	crypt = os_malloc(hdrlen + 8 + plen + 16 + AES_BLOCK_SIZE);
+	if (crypt == NULL)
+		return NULL;
+
+	os_memcpy(crypt, frame, hdrlen);
+	hdr = (struct ieee80211_hdr *) crypt;
+	pos = crypt + hdrlen;
+	*pos++ = pn[5]; /* PN0 */
+	*pos++ = pn[4]; /* PN1 */
+	*pos++ = 0x00; /* Rsvd */
+	*pos++ = 0x20 | (keyid << 6);
+	*pos++ = pn[3]; /* PN2 */
+	*pos++ = pn[2]; /* PN3 */
+	*pos++ = pn[1]; /* PN4 */
+	*pos++ = pn[0]; /* PN5 */
+
+	os_memset(aad, 0, sizeof(aad));
+	gcmp_aad_nonce(hdr, crypt + hdrlen, a1, a2, a3, aad, &aad_len, nonce);
+	wpa_hexdump(MSG_EXCESSIVE, "GCMP AAD", aad, aad_len);
+	wpa_hexdump(MSG_EXCESSIVE, "GCMP nonce", nonce, sizeof(nonce));
+
+	if (aes_gcm_ae(tk, tk_len, nonce, sizeof(nonce), frame + hdrlen, plen,
+		       aad, aad_len, pos, pos + plen) < 0) {
+		os_free(crypt);
+		return NULL;
+	}
+
+	wpa_hexdump(MSG_EXCESSIVE, "GCMP MIC", pos + plen, 16);
+	wpa_hexdump(MSG_EXCESSIVE, "GCMP encrypted", pos, plen);
+
+	*encrypted_len = hdrlen + 8 + plen + 16;
+
+	return crypt;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/inject.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/inject.c
new file mode 100644
index 0000000..bac956d
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/inject.c
@@ -0,0 +1,341 @@
+/*
+ * wlantest frame injection
+ * Copyright (c) 2010-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 "common/defs.h"
+#include "common/ieee802_11_defs.h"
+#include "crypto/aes_wrap.h"
+#include "wlantest.h"
+
+
+static int inject_frame(int s, const void *data, size_t len)
+{
+#define	IEEE80211_RADIOTAP_F_FRAG	0x08
+	unsigned char rtap_hdr[] = {
+		0x00, 0x00, /* radiotap version */
+		0x0e, 0x00, /* radiotap length */
+		0x02, 0xc0, 0x00, 0x00, /* bmap: flags, tx and rx flags */
+		IEEE80211_RADIOTAP_F_FRAG, /* F_FRAG (fragment if required) */
+		0x00,       /* padding */
+		0x00, 0x00, /* RX and TX flags to indicate that */
+		0x00, 0x00, /* this is the injected frame directly */
+	};
+	struct iovec iov[2] = {
+		{
+			.iov_base = &rtap_hdr,
+			.iov_len = sizeof(rtap_hdr),
+		},
+		{
+			.iov_base = (void *) data,
+			.iov_len = len,
+		}
+	};
+	struct msghdr msg = {
+		.msg_name = NULL,
+		.msg_namelen = 0,
+		.msg_iov = iov,
+		.msg_iovlen = 2,
+		.msg_control = NULL,
+		.msg_controllen = 0,
+		.msg_flags = 0,
+	};
+	int ret;
+
+	ret = sendmsg(s, &msg, 0);
+	if (ret < 0)
+		wpa_printf(MSG_ERROR, "sendmsg: %s", strerror(errno));
+	return ret;
+}
+
+
+static int is_robust_mgmt(u8 *frame, size_t len)
+{
+	struct ieee80211_mgmt *mgmt;
+	u16 fc, stype;
+	if (len < 24)
+		return 0;
+	mgmt = (struct ieee80211_mgmt *) frame;
+	fc = le_to_host16(mgmt->frame_control);
+	if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT)
+		return 0;
+	stype = WLAN_FC_GET_STYPE(fc);
+	if (stype == WLAN_FC_STYPE_DEAUTH || stype == WLAN_FC_STYPE_DISASSOC)
+		return 1;
+	if (stype == WLAN_FC_STYPE_ACTION ||
+	    stype == WLAN_FC_STYPE_ACTION_NO_ACK) {
+		if (len < 25)
+			return 0;
+		if (mgmt->u.action.category != WLAN_ACTION_PUBLIC)
+			return 1;
+	}
+	return 0;
+}
+
+
+static int wlantest_inject_bip(struct wlantest *wt, struct wlantest_bss *bss,
+			       u8 *frame, size_t len, int incorrect_key)
+{
+	u8 *prot;
+	u8 stub[32];
+	int ret;
+	size_t plen;
+
+	if (!bss->igtk_len[bss->igtk_idx])
+		return -1;
+
+	os_memset(stub, 0x11, sizeof(stub));
+	inc_byte_array(bss->ipn[bss->igtk_idx], 6);
+
+	prot = bip_protect(incorrect_key ? stub : bss->igtk[bss->igtk_idx],
+			   bss->igtk_len[bss->igtk_idx],
+			   frame, len, bss->ipn[bss->igtk_idx],
+			   bss->igtk_idx, &plen);
+	if (prot == NULL)
+		return -1;
+
+
+	ret = inject_frame(wt->monitor_sock, prot, plen);
+	os_free(prot);
+
+	return (ret < 0) ? -1 : 0;
+}
+
+
+static int wlantest_inject_prot_bc(struct wlantest *wt,
+				   struct wlantest_bss *bss,
+				   u8 *frame, size_t len, int incorrect_key)
+{
+	u8 *crypt;
+	size_t crypt_len;
+	int ret;
+	u8 stub[64];
+	u8 *pn;
+	struct ieee80211_hdr *hdr;
+	u16 fc;
+	int hdrlen;
+
+	hdr = (struct ieee80211_hdr *) frame;
+	hdrlen = 24;
+	fc = le_to_host16(hdr->frame_control);
+
+	if (!bss->gtk_len[bss->gtk_idx])
+		return -1;
+
+	if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) ==
+	    (WLAN_FC_TODS | WLAN_FC_FROMDS))
+		hdrlen += ETH_ALEN;
+	pn = bss->rsc[bss->gtk_idx];
+	inc_byte_array(pn, 6);
+
+	os_memset(stub, 0x11, sizeof(stub));
+	if (bss->group_cipher == WPA_CIPHER_TKIP)
+		crypt = tkip_encrypt(incorrect_key ? stub :
+				     bss->gtk[bss->gtk_idx],
+				     frame, len, hdrlen, NULL, pn,
+				     bss->gtk_idx, &crypt_len);
+	else
+		crypt = ccmp_encrypt(incorrect_key ? stub :
+				     bss->gtk[bss->gtk_idx],
+				     frame, len, hdrlen, NULL, NULL, NULL, NULL,
+				     pn, bss->gtk_idx, &crypt_len);
+
+	if (crypt == NULL)
+		return -1;
+
+	ret = inject_frame(wt->monitor_sock, crypt, crypt_len);
+	os_free(crypt);
+
+	return (ret < 0) ? -1 : 0;
+}
+
+
+static int wlantest_inject_prot(struct wlantest *wt, struct wlantest_bss *bss,
+				struct wlantest_sta *sta, u8 *frame,
+				size_t len, int incorrect_key)
+{
+	u8 *crypt;
+	size_t crypt_len;
+	int ret;
+	u8 stub[64];
+	u8 *pn;
+	struct ieee80211_hdr *hdr;
+	u16 fc;
+	int tid = 0;
+	u8 *qos = NULL;
+	int hdrlen;
+	struct wlantest_tdls *tdls = NULL;
+	const u8 *tk = NULL;
+
+	hdr = (struct ieee80211_hdr *) frame;
+	hdrlen = 24;
+	fc = le_to_host16(hdr->frame_control);
+
+	if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA &&
+	    (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) == 0) {
+		struct wlantest_sta *sta2;
+		bss = bss_get(wt, hdr->addr3);
+		if (bss == NULL) {
+			wpa_printf(MSG_DEBUG, "No BSS found for TDLS "
+				   "injection");
+			return -1;
+		}
+		sta = sta_find(bss, hdr->addr2);
+		sta2 = sta_find(bss, hdr->addr1);
+		if (sta == NULL || sta2 == NULL) {
+			wpa_printf(MSG_DEBUG, "No stations found for TDLS "
+				   "injection");
+			return -1;
+		}
+		dl_list_for_each(tdls, &bss->tdls, struct wlantest_tdls, list)
+		{
+			if ((tdls->init == sta && tdls->resp == sta2) ||
+			    (tdls->init == sta2 && tdls->resp == sta)) {
+				if (!tdls->link_up)
+					wpa_printf(MSG_DEBUG, "TDLS: Link not "
+						   "up, but injecting Data "
+						   "frame on direct link");
+				tk = tdls->tpk.tk;
+				break;
+			}
+		}
+	}
+
+	if (tk == NULL && sta == NULL) {
+		if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT)
+			return wlantest_inject_bip(wt, bss, frame, len,
+						   incorrect_key);
+		return wlantest_inject_prot_bc(wt, bss, frame, len,
+					       incorrect_key);
+	}
+
+	if (tk == NULL && !sta->ptk_set) {
+		wpa_printf(MSG_DEBUG, "No key known for injection");
+		return -1;
+	}
+
+	if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT)
+		tid = 16;
+	else if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA) {
+		if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) ==
+		    (WLAN_FC_TODS | WLAN_FC_FROMDS))
+			hdrlen += ETH_ALEN;
+		if (WLAN_FC_GET_STYPE(fc) & 0x08) {
+			qos = frame + hdrlen;
+			hdrlen += 2;
+			tid = qos[0] & 0x0f;
+		}
+	}
+	if (tk) {
+		if (os_memcmp(hdr->addr2, tdls->init->addr, ETH_ALEN) == 0)
+			pn = tdls->rsc_init[tid];
+		else
+			pn = tdls->rsc_resp[tid];
+	} else if (os_memcmp(hdr->addr2, bss->bssid, ETH_ALEN) == 0)
+		pn = sta->rsc_fromds[tid];
+	else
+		pn = sta->rsc_tods[tid];
+	inc_byte_array(pn, 6);
+
+	os_memset(stub, 0x11, sizeof(stub));
+	if (tk)
+		crypt = ccmp_encrypt(incorrect_key ? stub : tk,
+				     frame, len, hdrlen, qos, NULL, NULL, NULL,
+				     pn, 0, &crypt_len);
+	else if (sta->pairwise_cipher == WPA_CIPHER_TKIP)
+		crypt = tkip_encrypt(incorrect_key ? stub : sta->ptk.tk,
+				     frame, len, hdrlen, qos, pn, 0,
+				     &crypt_len);
+	else
+		crypt = ccmp_encrypt(incorrect_key ? stub : sta->ptk.tk,
+				     frame, len, hdrlen, qos, NULL, NULL, NULL,
+				     pn, 0, &crypt_len);
+
+	if (crypt == NULL) {
+		wpa_printf(MSG_DEBUG, "Frame encryption failed");
+		return -1;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "Inject frame (encrypted)", crypt, crypt_len);
+	ret = inject_frame(wt->monitor_sock, crypt, crypt_len);
+	os_free(crypt);
+	wpa_printf(MSG_DEBUG, "inject_frame for protected frame: %d", ret);
+
+	return (ret < 0) ? -1 : 0;
+}
+
+
+int wlantest_inject(struct wlantest *wt, struct wlantest_bss *bss,
+		    struct wlantest_sta *sta, u8 *frame, size_t len,
+		    enum wlantest_inject_protection prot)
+{
+	int ret;
+	struct ieee80211_hdr *hdr;
+	u16 fc;
+	int protectable, protect = 0;
+
+	wpa_hexdump(MSG_DEBUG, "Inject frame", frame, len);
+	if (wt->monitor_sock < 0) {
+		wpa_printf(MSG_INFO, "Cannot inject frames when monitor "
+			   "interface is not in use");
+		return -1;
+	}
+
+	if (prot != WLANTEST_INJECT_UNPROTECTED && bss == NULL) {
+		wpa_printf(MSG_INFO, "No BSS information to inject "
+			   "protected frames");
+		return -1;
+	}
+
+	hdr = (struct ieee80211_hdr *) frame;
+	fc = le_to_host16(hdr->frame_control);
+	protectable = WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA ||
+		is_robust_mgmt(frame, len);
+
+	if ((prot == WLANTEST_INJECT_PROTECTED ||
+	     prot == WLANTEST_INJECT_INCORRECT_KEY) && bss) {
+		if (!sta &&
+		    ((WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+		      !bss->igtk_len[bss->igtk_idx]) ||
+		     (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA &&
+		      !bss->gtk_len[bss->gtk_idx]))) {
+			wpa_printf(MSG_INFO, "No GTK/IGTK known for "
+				   MACSTR " to protect the injected "
+				   "frame", MAC2STR(bss->bssid));
+			return -1;
+		}
+		if (sta && !sta->ptk_set) {
+			wpa_printf(MSG_INFO, "No PTK known for the STA " MACSTR
+				   " to encrypt the injected frame",
+				   MAC2STR(sta->addr));
+			return -1;
+		}
+		protect = 1;
+	} else if (protectable && prot != WLANTEST_INJECT_UNPROTECTED && bss) {
+		if (sta && sta->ptk_set)
+			protect = 1;
+		else if (!sta) {
+			if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA &&
+			    bss->gtk_len[bss->gtk_idx])
+				protect = 1;
+			if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+			    bss->igtk_len[bss->igtk_idx])
+				protect = 1;
+		}
+	}
+
+	if (protect && bss)
+		return wlantest_inject_prot(
+			wt, bss, sta, frame, len,
+			prot == WLANTEST_INJECT_INCORRECT_KEY);
+
+	ret = inject_frame(wt->monitor_sock, frame, len);
+	wpa_printf(MSG_DEBUG, "inject_frame for unprotected frame: %d", ret);
+	return (ret < 0) ? -1 : 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/monitor.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/monitor.c
new file mode 100644
index 0000000..f287086
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/monitor.c
@@ -0,0 +1,172 @@
+/*
+ * Linux packet socket monitor
+ * Copyright (c) 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"
+#ifndef __APPLE__
+#include <net/if.h>
+#include <netpacket/packet.h>
+#endif /* __APPLE__ */
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "wlantest.h"
+
+
+#ifdef __APPLE__
+
+int monitor_init(struct wlantest *wt, const char *ifname)
+{
+	return -1;
+}
+
+
+int monitor_init_wired(struct wlantest *wt, const char *ifname)
+{
+	return -1;
+}
+
+
+void monitor_deinit(struct wlantest *wt)
+{
+}
+
+#else /* __APPLE__ */
+
+static void monitor_read(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct wlantest *wt = eloop_ctx;
+	u8 buf[3000];
+	int len;
+
+	len = recv(sock, buf, sizeof(buf), 0);
+	if (len < 0) {
+		wpa_printf(MSG_INFO, "recv(PACKET): %s", strerror(errno));
+		return;
+	}
+
+	clear_notes(wt);
+	os_free(wt->decrypted);
+	wt->decrypted = NULL;
+	write_pcap_captured(wt, buf, len);
+	wlantest_process(wt, buf, len);
+	write_pcapng_captured(wt, buf, len);
+}
+
+
+static void monitor_read_wired(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct wlantest *wt = eloop_ctx;
+	u8 buf[3000];
+	int len;
+
+	len = recv(sock, buf, sizeof(buf), 0);
+	if (len < 0) {
+		wpa_printf(MSG_INFO, "recv(PACKET): %s", strerror(errno));
+		return;
+	}
+
+	wlantest_process_wired(wt, buf, len);
+}
+
+
+int monitor_init(struct wlantest *wt, const char *ifname)
+{
+	struct sockaddr_ll ll;
+
+	os_memset(&ll, 0, sizeof(ll));
+	ll.sll_family = AF_PACKET;
+	ll.sll_ifindex = if_nametoindex(ifname);
+	if (ll.sll_ifindex == 0) {
+		wpa_printf(MSG_ERROR, "Monitor interface '%s' does not exist",
+			   ifname);
+		return -1;
+	}
+
+	wt->monitor_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+	if (wt->monitor_sock < 0) {
+		wpa_printf(MSG_ERROR, "socket(PF_PACKET,SOCK_RAW): %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	if (bind(wt->monitor_sock, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
+		wpa_printf(MSG_ERROR, "bind(PACKET): %s", strerror(errno));
+		close(wt->monitor_sock);
+		wt->monitor_sock = -1;
+		return -1;
+	}
+
+	if (eloop_register_read_sock(wt->monitor_sock, monitor_read, wt, NULL))
+	{
+		wpa_printf(MSG_ERROR, "Could not register monitor read "
+			   "socket");
+		close(wt->monitor_sock);
+		wt->monitor_sock = -1;
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int monitor_init_wired(struct wlantest *wt, const char *ifname)
+{
+	struct sockaddr_ll ll;
+
+	os_memset(&ll, 0, sizeof(ll));
+	ll.sll_family = AF_PACKET;
+	ll.sll_ifindex = if_nametoindex(ifname);
+	if (ll.sll_ifindex == 0) {
+		wpa_printf(MSG_ERROR, "Monitor interface '%s' does not exist",
+			   ifname);
+		return -1;
+	}
+
+	wt->monitor_wired = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+	if (wt->monitor_wired < 0) {
+		wpa_printf(MSG_ERROR, "socket(PF_PACKET,SOCK_RAW): %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	if (bind(wt->monitor_wired, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
+		wpa_printf(MSG_ERROR, "bind(PACKET): %s", strerror(errno));
+		close(wt->monitor_wired);
+		wt->monitor_wired = -1;
+		return -1;
+	}
+
+	if (eloop_register_read_sock(wt->monitor_wired, monitor_read_wired,
+				     wt, NULL)) {
+		wpa_printf(MSG_ERROR, "Could not register monitor read "
+			   "socket");
+		close(wt->monitor_wired);
+		wt->monitor_wired = -1;
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void monitor_deinit(struct wlantest *wt)
+{
+	if (wt->monitor_sock >= 0) {
+		eloop_unregister_read_sock(wt->monitor_sock);
+		close(wt->monitor_sock);
+		wt->monitor_sock = -1;
+	}
+
+	if (wt->monitor_wired >= 0) {
+		eloop_unregister_read_sock(wt->monitor_wired);
+		close(wt->monitor_wired);
+		wt->monitor_wired = -1;
+	}
+}
+
+#endif /* __APPLE__ */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/process.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/process.c
new file mode 100644
index 0000000..92dd0a6
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/process.c
@@ -0,0 +1,409 @@
+/*
+ * Received frame processing
+ * Copyright (c) 2010-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 "utils/crc32.h"
+#include "utils/radiotap.h"
+#include "utils/radiotap_iter.h"
+#include "common/ieee802_11_defs.h"
+#include "common/qca-vendor.h"
+#include "wlantest.h"
+
+
+static struct wlantest_sta * rx_get_sta(struct wlantest *wt,
+					const struct ieee80211_hdr *hdr,
+					size_t len, int *to_ap)
+{
+	u16 fc;
+	const u8 *sta_addr, *bssid;
+	struct wlantest_bss *bss;
+
+	*to_ap = 0;
+	if (hdr->addr1[0] & 0x01)
+		return NULL; /* Ignore group addressed frames */
+
+	fc = le_to_host16(hdr->frame_control);
+	switch (WLAN_FC_GET_TYPE(fc)) {
+	case WLAN_FC_TYPE_MGMT:
+		if (len < 24)
+			return NULL;
+		bssid = hdr->addr3;
+		if (os_memcmp(bssid, hdr->addr2, ETH_ALEN) == 0) {
+			sta_addr = hdr->addr1;
+			*to_ap = 0;
+		} else {
+			if (os_memcmp(bssid, hdr->addr1, ETH_ALEN) != 0)
+				return NULL; /* Unsupported STA-to-STA frame */
+			sta_addr = hdr->addr2;
+			*to_ap = 1;
+		}
+		break;
+	case WLAN_FC_TYPE_DATA:
+		if (len < 24)
+			return NULL;
+		switch (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) {
+		case 0:
+			return NULL; /* IBSS not supported */
+		case WLAN_FC_FROMDS:
+			sta_addr = hdr->addr1;
+			bssid = hdr->addr2;
+			*to_ap = 0;
+			break;
+		case WLAN_FC_TODS:
+			sta_addr = hdr->addr2;
+			bssid = hdr->addr1;
+			*to_ap = 1;
+			break;
+		case WLAN_FC_TODS | WLAN_FC_FROMDS:
+			return NULL; /* WDS not supported */
+		default:
+			return NULL;
+		}
+		break;
+	case WLAN_FC_TYPE_CTRL:
+		if (WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PSPOLL &&
+		    len >= 16) {
+			sta_addr = hdr->addr2;
+			bssid = hdr->addr1;
+			*to_ap = 1;
+		} else
+			return NULL;
+		break;
+	default:
+		return NULL;
+	}
+
+	bss = bss_find(wt, bssid);
+	if (bss == NULL)
+		return NULL;
+	return sta_find(bss, sta_addr);
+}
+
+
+static void rx_update_ps(struct wlantest *wt, const struct ieee80211_hdr *hdr,
+			 size_t len, struct wlantest_sta *sta, int to_ap)
+{
+	u16 fc, type, stype;
+
+	if (sta == NULL)
+		return;
+
+	fc = le_to_host16(hdr->frame_control);
+	type = WLAN_FC_GET_TYPE(fc);
+	stype = WLAN_FC_GET_STYPE(fc);
+
+	if (!to_ap) {
+		if (sta->pwrmgt && !sta->pspoll) {
+			u16 seq_ctrl = le_to_host16(hdr->seq_ctrl);
+			add_note(wt, MSG_DEBUG, "AP " MACSTR " sent a frame "
+				 "(%u:%u) to a sleeping STA " MACSTR
+				 " (seq=%u)",
+				 MAC2STR(sta->bss->bssid),
+				 type, stype, MAC2STR(sta->addr),
+				 WLAN_GET_SEQ_SEQ(seq_ctrl));
+		} else
+			sta->pspoll = 0;
+		return;
+	}
+
+	sta->pspoll = 0;
+
+	if (type == WLAN_FC_TYPE_DATA || type == WLAN_FC_TYPE_MGMT ||
+	    (type == WLAN_FC_TYPE_CTRL && stype == WLAN_FC_STYPE_PSPOLL)) {
+		/*
+		 * In theory, the PS state changes only at the end of the frame
+		 * exchange that is ACKed by the AP. However, most cases are
+		 * handled with this simpler implementation that does not
+		 * maintain state through the frame exchange.
+		 */
+		if (sta->pwrmgt && !(fc & WLAN_FC_PWRMGT)) {
+			add_note(wt, MSG_DEBUG, "STA " MACSTR " woke up from "
+				 "sleep", MAC2STR(sta->addr));
+			sta->pwrmgt = 0;
+		} else if (!sta->pwrmgt && (fc & WLAN_FC_PWRMGT)) {
+			add_note(wt, MSG_DEBUG, "STA " MACSTR " went to sleep",
+				 MAC2STR(sta->addr));
+			sta->pwrmgt = 1;
+		}
+	}
+
+	if (type == WLAN_FC_TYPE_CTRL && stype == WLAN_FC_STYPE_PSPOLL)
+		sta->pspoll = 1;
+}
+
+
+static int rx_duplicate(struct wlantest *wt, const struct ieee80211_hdr *hdr,
+			size_t len, struct wlantest_sta *sta, int to_ap)
+{
+	u16 fc;
+	int tid = 16;
+	le16 *seq_ctrl;
+
+	if (sta == NULL)
+		return 0;
+
+	fc = le_to_host16(hdr->frame_control);
+	if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA &&
+	    (WLAN_FC_GET_STYPE(fc) & 0x08) && len >= 26) {
+		const u8 *qos = ((const u8 *) hdr) + 24;
+		tid = qos[0] & 0x0f;
+	}
+
+	if (to_ap)
+		seq_ctrl = &sta->seq_ctrl_to_ap[tid];
+	else
+		seq_ctrl = &sta->seq_ctrl_to_sta[tid];
+
+	if ((fc & WLAN_FC_RETRY) && hdr->seq_ctrl == *seq_ctrl &&
+	    !sta->allow_duplicate) {
+		u16 s = le_to_host16(hdr->seq_ctrl);
+		add_note(wt, MSG_MSGDUMP, "Ignore duplicated frame (seq=%u "
+			 "frag=%u A1=" MACSTR " A2=" MACSTR ")",
+			 WLAN_GET_SEQ_SEQ(s), WLAN_GET_SEQ_FRAG(s),
+			 MAC2STR(hdr->addr1), MAC2STR(hdr->addr2));
+		return 1;
+	}
+
+	*seq_ctrl = hdr->seq_ctrl;
+	sta->allow_duplicate = 0;
+
+	return 0;
+}
+
+
+static void rx_ack(struct wlantest *wt, const struct ieee80211_hdr *hdr)
+{
+	struct ieee80211_hdr *last = (struct ieee80211_hdr *) wt->last_hdr;
+	u16 fc;
+
+	if (wt->last_len < 24 || (last->addr1[0] & 0x01) ||
+	    os_memcmp(hdr->addr1, last->addr2, ETH_ALEN) != 0) {
+		add_note(wt, MSG_MSGDUMP, "Unknown Ack frame (previous frame "
+			 "not seen)");
+		return;
+	}
+
+	/* Ack to the previous frame */
+	fc = le_to_host16(last->frame_control);
+	if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT)
+		rx_mgmt_ack(wt, last);
+}
+
+
+static void rx_frame(struct wlantest *wt, const u8 *data, size_t len)
+{
+	const struct ieee80211_hdr *hdr;
+	u16 fc;
+	struct wlantest_sta *sta;
+	int to_ap;
+
+	wpa_hexdump(MSG_EXCESSIVE, "RX frame", data, len);
+	if (len < 2)
+		return;
+
+	hdr = (const struct ieee80211_hdr *) data;
+	fc = le_to_host16(hdr->frame_control);
+	if (fc & WLAN_FC_PVER) {
+		wpa_printf(MSG_DEBUG, "Drop RX frame with unexpected pver=%d",
+			   fc & WLAN_FC_PVER);
+		return;
+	}
+
+	sta = rx_get_sta(wt, hdr, len, &to_ap);
+
+	switch (WLAN_FC_GET_TYPE(fc)) {
+	case WLAN_FC_TYPE_MGMT:
+		if (len < 24)
+			break;
+		if (rx_duplicate(wt, hdr, len, sta, to_ap))
+			break;
+		rx_update_ps(wt, hdr, len, sta, to_ap);
+		rx_mgmt(wt, data, len);
+		break;
+	case WLAN_FC_TYPE_CTRL:
+		if (len < 10)
+			break;
+		wt->rx_ctrl++;
+		rx_update_ps(wt, hdr, len, sta, to_ap);
+		if (WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACK)
+			rx_ack(wt, hdr);
+		break;
+	case WLAN_FC_TYPE_DATA:
+		if (len < 24)
+			break;
+		if (rx_duplicate(wt, hdr, len, sta, to_ap))
+			break;
+		rx_update_ps(wt, hdr, len, sta, to_ap);
+		rx_data(wt, data, len);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "Drop RX frame with unexpected type %d",
+			   WLAN_FC_GET_TYPE(fc));
+		break;
+	}
+
+	os_memcpy(wt->last_hdr, data, len > sizeof(wt->last_hdr) ?
+		  sizeof(wt->last_hdr) : len);
+	wt->last_len = len;
+}
+
+
+static void tx_status(struct wlantest *wt, const u8 *data, size_t len, int ack)
+{
+	wpa_printf(MSG_DEBUG, "TX status: ack=%d", ack);
+	wpa_hexdump(MSG_EXCESSIVE, "TX status frame", data, len);
+}
+
+
+static int check_fcs(const u8 *frame, size_t frame_len, const u8 *fcs)
+{
+	if (WPA_GET_LE32(fcs) != ieee80211_crc32(frame, frame_len))
+		return -1;
+	return 0;
+}
+
+
+void wlantest_process(struct wlantest *wt, const u8 *data, size_t len)
+{
+	struct ieee80211_radiotap_iterator iter;
+	int ret;
+	int rxflags = 0, txflags = 0, failed = 0, fcs = 0;
+	const u8 *frame, *fcspos;
+	size_t frame_len;
+
+	if (wt->ethernet)
+		return;
+
+	wpa_hexdump(MSG_EXCESSIVE, "Process data", data, len);
+
+	if (ieee80211_radiotap_iterator_init(&iter, (void *) data, len, NULL)) {
+		add_note(wt, MSG_INFO, "Invalid radiotap frame");
+		return;
+	}
+
+	for (;;) {
+		ret = ieee80211_radiotap_iterator_next(&iter);
+		wpa_printf(MSG_EXCESSIVE, "radiotap iter: %d "
+			   "this_arg_index=%d", ret, iter.this_arg_index);
+		if (ret == -ENOENT)
+			break;
+		if (ret) {
+			add_note(wt, MSG_INFO, "Invalid radiotap header: %d",
+				 ret);
+			return;
+		}
+		switch (iter.this_arg_index) {
+		case IEEE80211_RADIOTAP_FLAGS:
+			if (*iter.this_arg & IEEE80211_RADIOTAP_F_FCS)
+				fcs = 1;
+			break;
+		case IEEE80211_RADIOTAP_RX_FLAGS:
+			rxflags = 1;
+			break;
+		case IEEE80211_RADIOTAP_TX_FLAGS:
+			txflags = 1;
+			failed = le_to_host16((*(u16 *) iter.this_arg)) &
+				IEEE80211_RADIOTAP_F_TX_FAIL;
+			break;
+		case IEEE80211_RADIOTAP_VENDOR_NAMESPACE:
+			if (WPA_GET_BE24(iter.this_arg) == OUI_QCA &&
+			    iter.this_arg[3] == QCA_RADIOTAP_VID_WLANTEST) {
+				add_note(wt, MSG_DEBUG,
+					 "Skip frame inserted by wlantest");
+				return;
+			}
+		}
+	}
+
+	frame = data + iter._max_length;
+	frame_len = len - iter._max_length;
+
+	if (fcs && frame_len >= 4) {
+		frame_len -= 4;
+		fcspos = frame + frame_len;
+		if (check_fcs(frame, frame_len, fcspos) < 0) {
+			add_note(wt, MSG_EXCESSIVE, "Drop RX frame with "
+				 "invalid FCS");
+			wt->fcs_error++;
+			return;
+		}
+	}
+
+	if (rxflags && txflags)
+		return;
+	if (!txflags)
+		rx_frame(wt, frame, frame_len);
+	else {
+		add_note(wt, MSG_EXCESSIVE, "TX status - process as RX of "
+			 "local frame");
+		tx_status(wt, frame, frame_len, !failed);
+		/* Process as RX frame to support local monitor interface */
+		rx_frame(wt, frame, frame_len);
+	}
+}
+
+
+void wlantest_process_prism(struct wlantest *wt, const u8 *data, size_t len)
+{
+	int fcs = 0;
+	const u8 *frame, *fcspos;
+	size_t frame_len;
+	u32 hdrlen;
+
+	wpa_hexdump(MSG_EXCESSIVE, "Process data", data, len);
+
+	if (len < 8)
+		return;
+	hdrlen = WPA_GET_LE32(data + 4);
+
+	if (len < hdrlen) {
+		wpa_printf(MSG_INFO, "Too short frame to include prism "
+			   "header");
+		return;
+	}
+
+	frame = data + hdrlen;
+	frame_len = len - hdrlen;
+	fcs = 1;
+
+	if (fcs && frame_len >= 4) {
+		frame_len -= 4;
+		fcspos = frame + frame_len;
+		if (check_fcs(frame, frame_len, fcspos) < 0) {
+			add_note(wt, MSG_EXCESSIVE, "Drop RX frame with "
+				 "invalid FCS");
+			wt->fcs_error++;
+			return;
+		}
+	}
+
+	rx_frame(wt, frame, frame_len);
+}
+
+
+void wlantest_process_80211(struct wlantest *wt, const u8 *data, size_t len)
+{
+	wpa_hexdump(MSG_EXCESSIVE, "Process data", data, len);
+
+	if (wt->assume_fcs && len >= 4) {
+		const u8 *fcspos;
+
+		len -= 4;
+		fcspos = data + len;
+		if (check_fcs(data, len, fcspos) < 0) {
+			add_note(wt, MSG_EXCESSIVE, "Drop RX frame with "
+				 "invalid FCS");
+			wt->fcs_error++;
+			return;
+		}
+	}
+
+	rx_frame(wt, data, len);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/readpcap.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/readpcap.c
new file mode 100644
index 0000000..1e7e662
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/readpcap.c
@@ -0,0 +1,190 @@
+/*
+ * PCAP capture file reader
+ * Copyright (c) 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 <pcap.h>
+
+#include "utils/common.h"
+#include "wlantest.h"
+
+
+static void write_pcap_with_radiotap(struct wlantest *wt,
+				     const u8 *data, size_t data_len)
+{
+	struct pcap_pkthdr h;
+	u8 rtap[] = {
+		0x00 /* rev */,
+		0x00 /* pad */,
+		0x0a, 0x00, /* header len */
+		0x02, 0x00, 0x00, 0x00, /* present flags */
+		0x00, /* flags */
+		0x00 /* pad */
+	};
+	u8 *buf;
+	size_t len;
+
+	if (wt->assume_fcs)
+		rtap[8] |= 0x10;
+
+	os_memset(&h, 0, sizeof(h));
+	h.ts = wt->write_pcap_time;
+	len = sizeof(rtap) + data_len;
+	buf = os_malloc(len);
+	if (buf == NULL)
+		return;
+	os_memcpy(buf, rtap, sizeof(rtap));
+	os_memcpy(buf + sizeof(rtap), data, data_len);
+	h.caplen = len;
+	h.len = len;
+	pcap_dump(wt->write_pcap_dumper, &h, buf);
+	os_free(buf);
+}
+
+
+int read_cap_file(struct wlantest *wt, const char *fname)
+{
+	char errbuf[PCAP_ERRBUF_SIZE];
+	pcap_t *pcap;
+	unsigned int count = 0;
+	struct pcap_pkthdr *hdr;
+	const u_char *data;
+	int res;
+	int dlt;
+
+	pcap = pcap_open_offline(fname, errbuf);
+	if (pcap == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to read pcap file '%s': %s",
+			   fname, errbuf);
+		return -1;
+	}
+	dlt = pcap_datalink(pcap);
+	if (dlt != DLT_IEEE802_11_RADIO && dlt != DLT_PRISM_HEADER &&
+	    dlt != DLT_IEEE802_11) {
+		wpa_printf(MSG_ERROR, "Unsupported pcap datalink type: %d",
+			   dlt);
+		pcap_close(pcap);
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG, "pcap datalink type: %d", dlt);
+
+	for (;;) {
+		clear_notes(wt);
+		os_free(wt->decrypted);
+		wt->decrypted = NULL;
+
+		res = pcap_next_ex(pcap, &hdr, &data);
+		if (res == -2)
+			break; /* No more packets */
+		if (res == -1) {
+			wpa_printf(MSG_INFO, "pcap_next_ex failure: %s",
+				   pcap_geterr(pcap));
+			break;
+		}
+		if (res != 1) {
+			wpa_printf(MSG_INFO, "Unexpected pcap_next_ex return "
+				   "value %d", res);
+			break;
+		}
+
+		/* Packet was read without problems */
+		wt->frame_num++;
+		wpa_printf(MSG_EXCESSIVE, "pcap hdr: ts=%d.%06d "
+			   "len=%u/%u",
+			   (int) hdr->ts.tv_sec, (int) hdr->ts.tv_usec,
+			   hdr->caplen, hdr->len);
+		if (wt->write_pcap_dumper) {
+			wt->write_pcap_time = hdr->ts;
+			if (dlt == DLT_IEEE802_11)
+				write_pcap_with_radiotap(wt, data, hdr->caplen);
+			else
+				pcap_dump(wt->write_pcap_dumper, hdr, data);
+			if (wt->pcap_no_buffer)
+				pcap_dump_flush(wt->write_pcap_dumper);
+		}
+		if (hdr->caplen < hdr->len) {
+			add_note(wt, MSG_DEBUG, "pcap: Dropped incomplete "
+				 "frame (%u/%u captured)",
+				 hdr->caplen, hdr->len);
+			write_pcapng_write_read(wt, dlt, hdr, data);
+			continue;
+		}
+		count++;
+		switch (dlt) {
+		case DLT_IEEE802_11_RADIO:
+			wlantest_process(wt, data, hdr->caplen);
+			break;
+		case DLT_PRISM_HEADER:
+			wlantest_process_prism(wt, data, hdr->caplen);
+			break;
+		case DLT_IEEE802_11:
+			wlantest_process_80211(wt, data, hdr->caplen);
+			break;
+		}
+		write_pcapng_write_read(wt, dlt, hdr, data);
+	}
+
+	pcap_close(pcap);
+
+	wpa_printf(MSG_DEBUG, "Read %s: %u packets", fname, count);
+
+	return 0;
+}
+
+
+int read_wired_cap_file(struct wlantest *wt, const char *fname)
+{
+	char errbuf[PCAP_ERRBUF_SIZE];
+	pcap_t *pcap;
+	unsigned int count = 0;
+	struct pcap_pkthdr *hdr;
+	const u_char *data;
+	int res;
+
+	pcap = pcap_open_offline(fname, errbuf);
+	if (pcap == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to read pcap file '%s': %s",
+			   fname, errbuf);
+		return -1;
+	}
+
+	for (;;) {
+		res = pcap_next_ex(pcap, &hdr, &data);
+		if (res == -2)
+			break; /* No more packets */
+		if (res == -1) {
+			wpa_printf(MSG_INFO, "pcap_next_ex failure: %s",
+				   pcap_geterr(pcap));
+			break;
+		}
+		if (res != 1) {
+			wpa_printf(MSG_INFO, "Unexpected pcap_next_ex return "
+				   "value %d", res);
+			break;
+		}
+
+		/* Packet was read without problems */
+		wpa_printf(MSG_EXCESSIVE, "pcap hdr: ts=%d.%06d "
+			   "len=%u/%u",
+			   (int) hdr->ts.tv_sec, (int) hdr->ts.tv_usec,
+			   hdr->caplen, hdr->len);
+		if (hdr->caplen < hdr->len) {
+			wpa_printf(MSG_DEBUG, "pcap: Dropped incomplete frame "
+				   "(%u/%u captured)",
+				   hdr->caplen, hdr->len);
+			continue;
+		}
+		count++;
+		wlantest_process_wired(wt, data, hdr->caplen);
+	}
+
+	pcap_close(pcap);
+
+	wpa_printf(MSG_DEBUG, "Read %s: %u packets", fname, count);
+
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/rx_data.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/rx_data.c
new file mode 100644
index 0000000..2a03f5c
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/rx_data.c
@@ -0,0 +1,983 @@
+/*
+ * Received Data frame processing
+ * Copyright (c) 2010-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 "common/defs.h"
+#include "common/ieee802_11_defs.h"
+#include "wlantest.h"
+
+
+static const char * data_stype(u16 stype)
+{
+	switch (stype) {
+	case WLAN_FC_STYPE_DATA:
+		return "DATA";
+	case WLAN_FC_STYPE_DATA_CFACK:
+		return "DATA-CFACK";
+	case WLAN_FC_STYPE_DATA_CFPOLL:
+		return "DATA-CFPOLL";
+	case WLAN_FC_STYPE_DATA_CFACKPOLL:
+		return "DATA-CFACKPOLL";
+	case WLAN_FC_STYPE_NULLFUNC:
+		return "NULLFUNC";
+	case WLAN_FC_STYPE_CFACK:
+		return "CFACK";
+	case WLAN_FC_STYPE_CFPOLL:
+		return "CFPOLL";
+	case WLAN_FC_STYPE_CFACKPOLL:
+		return "CFACKPOLL";
+	case WLAN_FC_STYPE_QOS_DATA:
+		return "QOSDATA";
+	case WLAN_FC_STYPE_QOS_DATA_CFACK:
+		return "QOSDATA-CFACK";
+	case WLAN_FC_STYPE_QOS_DATA_CFPOLL:
+		return "QOSDATA-CFPOLL";
+	case WLAN_FC_STYPE_QOS_DATA_CFACKPOLL:
+		return "QOSDATA-CFACKPOLL";
+	case WLAN_FC_STYPE_QOS_NULL:
+		return "QOS-NULL";
+	case WLAN_FC_STYPE_QOS_CFPOLL:
+		return "QOS-CFPOLL";
+	case WLAN_FC_STYPE_QOS_CFACKPOLL:
+		return "QOS-CFACKPOLL";
+	}
+	return "??";
+}
+
+
+static void rx_data_eth(struct wlantest *wt, const u8 *bssid,
+			const u8 *sta_addr, const u8 *dst, const u8 *src,
+			u16 ethertype, const u8 *data, size_t len, int prot,
+			const u8 *peer_addr);
+
+static void rx_data_vlan(struct wlantest *wt, const u8 *bssid,
+			 const u8 *sta_addr, const u8 *dst, const u8 *src,
+			 const u8 *data, size_t len, int prot,
+			 const u8 *peer_addr)
+{
+	u16 tag;
+
+	if (len < 4)
+		return;
+	tag = WPA_GET_BE16(data);
+	wpa_printf(MSG_MSGDUMP, "VLAN tag: Priority=%u ID=%u",
+		   tag >> 12, tag & 0x0ffff);
+	/* ignore VLAN information and process the original frame */
+	rx_data_eth(wt, bssid, sta_addr, dst, src, WPA_GET_BE16(data + 2),
+		    data + 4, len - 4, prot, peer_addr);
+}
+
+
+static void rx_data_eth(struct wlantest *wt, const u8 *bssid,
+			const u8 *sta_addr, const u8 *dst, const u8 *src,
+			u16 ethertype, const u8 *data, size_t len, int prot,
+			const u8 *peer_addr)
+{
+	switch (ethertype) {
+	case ETH_P_PAE:
+		rx_data_eapol(wt, bssid, sta_addr, dst, src, data, len, prot);
+		break;
+	case ETH_P_IP:
+		rx_data_ip(wt, bssid, sta_addr, dst, src, data, len,
+			   peer_addr);
+		break;
+	case 0x890d:
+		rx_data_80211_encap(wt, bssid, sta_addr, dst, src, data, len);
+		break;
+	case ETH_P_8021Q:
+		rx_data_vlan(wt, bssid, sta_addr, dst, src, data, len, prot,
+			     peer_addr);
+		break;
+	}
+}
+
+
+static void rx_data_process(struct wlantest *wt, struct wlantest_bss *bss,
+			    const u8 *bssid,
+			    const u8 *sta_addr,
+			    const u8 *dst, const u8 *src,
+			    const u8 *data, size_t len, int prot,
+			    const u8 *peer_addr, const u8 *qos)
+{
+	if (len == 0)
+		return;
+
+	if (bss && bss->mesh && qos && !(qos[0] & BIT(7)) &&
+	    (qos[1] & BIT(0))) {
+		u8 addr_ext_mode;
+		size_t mesh_control_len = 6;
+
+		/* Skip Mesh Control field if this is not an A-MSDU */
+		if (len < mesh_control_len) {
+			wpa_printf(MSG_DEBUG,
+				   "Not enough room for Mesh Control field");
+			return;
+		}
+
+		addr_ext_mode = data[0] & 0x03;
+		if (addr_ext_mode == 3) {
+			wpa_printf(MSG_DEBUG,
+				   "Reserved Mesh Control :: Address Extension Mode");
+			return;
+		}
+
+		mesh_control_len += addr_ext_mode * ETH_ALEN;
+		if (len < mesh_control_len) {
+			wpa_printf(MSG_DEBUG,
+				   "Not enough room for Mesh Address Extension");
+			return;
+		}
+
+		len -= mesh_control_len;
+		data += mesh_control_len;
+	}
+
+	if (len >= 8 && os_memcmp(data, "\xaa\xaa\x03\x00\x00\x00", 6) == 0) {
+		rx_data_eth(wt, bssid, sta_addr, dst, src,
+			    WPA_GET_BE16(data + 6), data + 8, len - 8, prot,
+			    peer_addr);
+		return;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "Unrecognized LLC", data, len > 8 ? 8 : len);
+}
+
+
+static u8 * try_ptk(struct wlantest *wt, int pairwise_cipher,
+		    struct wpa_ptk *ptk, const struct ieee80211_hdr *hdr,
+		    const u8 *a1, const u8 *a2, const u8 *a3,
+		    const u8 *data, size_t data_len, size_t *decrypted_len)
+{
+	u8 *decrypted;
+	unsigned int tk_len = ptk->tk_len;
+
+	decrypted = NULL;
+	if ((pairwise_cipher == WPA_CIPHER_CCMP ||
+	     pairwise_cipher == 0) && tk_len == 16) {
+		decrypted = ccmp_decrypt(ptk->tk, hdr, a1, a2, a3, data,
+					 data_len, decrypted_len);
+	} else if ((pairwise_cipher == WPA_CIPHER_CCMP_256 ||
+		    pairwise_cipher == 0) && tk_len == 32) {
+		decrypted = ccmp_256_decrypt(ptk->tk, hdr, a1, a2, a3, data,
+					     data_len, decrypted_len);
+	} else if ((pairwise_cipher == WPA_CIPHER_GCMP ||
+		    pairwise_cipher == WPA_CIPHER_GCMP_256 ||
+		    pairwise_cipher == 0) &&
+		   (tk_len == 16 || tk_len == 32)) {
+		decrypted = gcmp_decrypt(ptk->tk, tk_len, hdr, a1, a2, a3,
+					 data, data_len, decrypted_len);
+	} else if ((pairwise_cipher == WPA_CIPHER_TKIP ||
+		    pairwise_cipher == 0) && tk_len == 32) {
+		enum michael_mic_result mic_res;
+
+		decrypted = tkip_decrypt(ptk->tk, hdr, data, data_len,
+					 decrypted_len, &mic_res,
+					 &wt->tkip_frag);
+		if (decrypted && mic_res == MICHAEL_MIC_INCORRECT)
+			add_note(wt, MSG_INFO, "Invalid Michael MIC");
+		else if (decrypted && mic_res == MICHAEL_MIC_NOT_VERIFIED)
+			add_note(wt, MSG_DEBUG, "Michael MIC not verified");
+	}
+
+	return decrypted;
+}
+
+
+static u8 * try_all_ptk(struct wlantest *wt, int pairwise_cipher,
+			const struct ieee80211_hdr *hdr,
+			const u8 *a1, const u8 *a2, const u8 *a3, int keyid,
+			const u8 *data, size_t data_len, size_t *decrypted_len)
+{
+	struct wlantest_ptk *ptk;
+	u8 *decrypted;
+	int prev_level = wpa_debug_level;
+
+	wpa_debug_level = MSG_WARNING;
+	dl_list_for_each(ptk, &wt->ptk, struct wlantest_ptk, list) {
+		decrypted = try_ptk(wt, pairwise_cipher, &ptk->ptk, hdr, a1, a2,
+				    a3, data, data_len, decrypted_len);
+		if (decrypted) {
+			wpa_debug_level = prev_level;
+			add_note(wt, MSG_DEBUG,
+				 "Found PTK match from list of all known PTKs");
+			write_decrypted_note(wt, decrypted, ptk->ptk.tk,
+					     ptk->ptk.tk_len, keyid);
+			return decrypted;
+		}
+	}
+	wpa_debug_level = prev_level;
+
+	return NULL;
+}
+
+
+static void check_plaintext_prot(struct wlantest *wt,
+				 const struct ieee80211_hdr *hdr,
+				 const u8 *data, size_t len)
+{
+	if (len < 8 + 3 || data[8] != 0xaa || data[9] != 0xaa ||
+	    data[10] != 0x03)
+		return;
+
+	add_note(wt, MSG_DEBUG,
+		 "Plaintext payload in protected frame");
+	wpa_printf(MSG_INFO, "Plaintext payload in protected frame #%u: A2="
+		   MACSTR " seq=%u",
+		   wt->frame_num, MAC2STR(hdr->addr2),
+		   WLAN_GET_SEQ_SEQ(le_to_host16(hdr->seq_ctrl)));
+}
+
+
+static void rx_data_bss_prot_group(struct wlantest *wt,
+				   const struct ieee80211_hdr *hdr,
+				   size_t hdrlen,
+				   const u8 *qos, const u8 *dst, const u8 *src,
+				   const u8 *data, size_t len)
+{
+	struct wlantest_bss *bss;
+	int keyid;
+	u8 *decrypted = NULL;
+	size_t dlen;
+	u8 pn[6];
+	int replay = 0;
+
+	bss = bss_get(wt, hdr->addr2);
+	if (bss == NULL)
+		return;
+	if (len < 4) {
+		add_note(wt, MSG_INFO, "Too short group addressed data frame");
+		return;
+	}
+
+	if (bss->group_cipher & (WPA_CIPHER_TKIP | WPA_CIPHER_CCMP) &&
+	    !(data[3] & 0x20)) {
+		add_note(wt, MSG_INFO, "Expected TKIP/CCMP frame from "
+			 MACSTR " did not have ExtIV bit set to 1",
+			 MAC2STR(bss->bssid));
+		return;
+	}
+
+	if (bss->group_cipher == WPA_CIPHER_TKIP) {
+		if (data[3] & 0x1f) {
+			add_note(wt, MSG_INFO, "TKIP frame from " MACSTR
+				 " used non-zero reserved bit",
+				 MAC2STR(bss->bssid));
+		}
+		if (data[1] != ((data[0] | 0x20) & 0x7f)) {
+			add_note(wt, MSG_INFO, "TKIP frame from " MACSTR
+				 " used incorrect WEPSeed[1] (was 0x%x, "
+				 "expected 0x%x)",
+				 MAC2STR(bss->bssid), data[1],
+				 (data[0] | 0x20) & 0x7f);
+		}
+	} else if (bss->group_cipher == WPA_CIPHER_CCMP) {
+		if (data[2] != 0 || (data[3] & 0x1f) != 0) {
+			add_note(wt, MSG_INFO, "CCMP frame from " MACSTR
+				 " used non-zero reserved bit",
+				 MAC2STR(bss->bssid));
+		}
+	}
+
+	check_plaintext_prot(wt, hdr, data, len);
+	keyid = data[3] >> 6;
+	if (bss->gtk_len[keyid] == 0 &&
+	    (bss->group_cipher != WPA_CIPHER_WEP40 ||
+	     dl_list_empty(&wt->wep))) {
+		decrypted = try_all_ptk(wt, bss->group_cipher, hdr, NULL, NULL,
+					NULL, keyid, data, len, &dlen);
+		if (decrypted)
+			goto process;
+		add_note(wt, MSG_MSGDUMP,
+			 "No GTK known to decrypt the frame (A2=" MACSTR
+			 " KeyID=%d)",
+			 MAC2STR(hdr->addr2), keyid);
+		return;
+	}
+
+	if (bss->group_cipher == WPA_CIPHER_TKIP)
+		tkip_get_pn(pn, data);
+	else if (bss->group_cipher == WPA_CIPHER_WEP40)
+		goto skip_replay_det;
+	else
+		ccmp_get_pn(pn, data);
+	if (os_memcmp(pn, bss->rsc[keyid], 6) <= 0) {
+		u16 seq_ctrl = le_to_host16(hdr->seq_ctrl);
+		char pn_hex[6 * 2 + 1], rsc_hex[6 * 2 + 1];
+
+		wpa_snprintf_hex(pn_hex, sizeof(pn_hex), pn, 6);
+		wpa_snprintf_hex(rsc_hex, sizeof(rsc_hex), bss->rsc[keyid], 6);
+		add_note(wt, MSG_INFO, "replay detected: A1=" MACSTR
+			 " A2=" MACSTR " A3=" MACSTR
+			 " seq=%u frag=%u%s keyid=%d #%u %s<=%s",
+			 MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+			 MAC2STR(hdr->addr3),
+			 WLAN_GET_SEQ_SEQ(seq_ctrl),
+			 WLAN_GET_SEQ_FRAG(seq_ctrl),
+			 (le_to_host16(hdr->frame_control) & WLAN_FC_RETRY) ?
+			 " Retry" : "",
+			 keyid, wt->frame_num, pn_hex, rsc_hex);
+		replay = 1;
+	}
+
+skip_replay_det:
+	if (bss->group_cipher == WPA_CIPHER_TKIP) {
+		enum michael_mic_result mic_res;
+
+		decrypted = tkip_decrypt(bss->gtk[keyid], hdr, data, len,
+					 &dlen, &mic_res, &wt->tkip_frag);
+		if (decrypted && mic_res == MICHAEL_MIC_INCORRECT)
+			add_note(wt, MSG_INFO, "Invalid Michael MIC");
+		else if (decrypted && mic_res == MICHAEL_MIC_NOT_VERIFIED)
+			add_note(wt, MSG_DEBUG, "Michael MIC not verified");
+	} else if (bss->group_cipher == WPA_CIPHER_WEP40) {
+		decrypted = wep_decrypt(wt, hdr, data, len, &dlen);
+	} else if (bss->group_cipher == WPA_CIPHER_CCMP) {
+		decrypted = ccmp_decrypt(bss->gtk[keyid], hdr, NULL, NULL, NULL,
+					 data, len, &dlen);
+	} else if (bss->group_cipher == WPA_CIPHER_CCMP_256) {
+		decrypted = ccmp_256_decrypt(bss->gtk[keyid], hdr,
+					     NULL, NULL, NULL,
+					     data, len, &dlen);
+	} else if (bss->group_cipher == WPA_CIPHER_GCMP ||
+		   bss->group_cipher == WPA_CIPHER_GCMP_256) {
+		decrypted = gcmp_decrypt(bss->gtk[keyid], bss->gtk_len[keyid],
+					 hdr, NULL, NULL, NULL,
+					 data, len, &dlen);
+	}
+
+	if (decrypted) {
+		char gtk[65];
+
+		wpa_snprintf_hex(gtk, sizeof(gtk), bss->gtk[keyid],
+				 bss->gtk_len[keyid]);
+		add_note(wt, MSG_EXCESSIVE, "GTK[%d] %s", keyid, gtk);
+	process:
+		rx_data_process(wt, bss, bss->bssid, NULL, dst, src, decrypted,
+				dlen, 1, NULL, qos);
+		if (!replay)
+			os_memcpy(bss->rsc[keyid], pn, 6);
+		write_pcap_decrypted(wt, (const u8 *) hdr, hdrlen,
+				     decrypted, dlen);
+	} else {
+		wpa_printf(MSG_DEBUG, "Failed to decrypt frame (group) #%u A2="
+			   MACSTR " seq=%u",
+			   wt->frame_num, MAC2STR(hdr->addr2),
+			   WLAN_GET_SEQ_SEQ(le_to_host16(hdr->seq_ctrl)));
+		add_note(wt, MSG_DEBUG, "Failed to decrypt frame (group)");
+	}
+	os_free(decrypted);
+}
+
+
+static u8 * try_ptk_decrypt(struct wlantest *wt, struct wlantest_sta *sta,
+			    const struct ieee80211_hdr *hdr, int keyid,
+			    const u8 *data, size_t len,
+			    const u8 *tk, size_t tk_len, size_t *dlen)
+{
+	u8 *decrypted = NULL;
+	u16 fc = le_to_host16(hdr->frame_control);
+	const u8 *a1 = NULL, *a2 = NULL, *a3 = NULL;
+
+	if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) &&
+	    !is_zero_ether_addr(sta->mld_mac_addr) &&
+	    !is_zero_ether_addr(sta->bss->mld_mac_addr)) {
+		if (os_memcmp(hdr->addr1, sta->addr, ETH_ALEN) == 0) {
+			a1 = sta->mld_mac_addr;
+			a2 = sta->bss->mld_mac_addr;
+		} else {
+			a1 = sta->bss->mld_mac_addr;
+			a2 = sta->mld_mac_addr;
+		}
+
+		if (os_memcmp(hdr->addr3, sta->bss->bssid, ETH_ALEN) == 0)
+			a3 = sta->bss->mld_mac_addr;
+	}
+
+	if (sta->pairwise_cipher == WPA_CIPHER_CCMP_256)
+		decrypted = ccmp_256_decrypt(tk, hdr, a1, a2, a3,
+					     data, len, dlen);
+	else if (sta->pairwise_cipher == WPA_CIPHER_GCMP ||
+		 sta->pairwise_cipher == WPA_CIPHER_GCMP_256)
+		decrypted = gcmp_decrypt(tk, tk_len, hdr, a1, a2, a3,
+					 data, len, dlen);
+	else
+		decrypted = ccmp_decrypt(tk, hdr, a1, a2, a3, data, len, dlen);
+	write_decrypted_note(wt, decrypted, tk, tk_len, keyid);
+
+	return decrypted;
+}
+
+
+static void rx_data_bss_prot(struct wlantest *wt,
+			     const struct ieee80211_hdr *hdr, size_t hdrlen,
+			     const u8 *qos, const u8 *dst, const u8 *src,
+			     const u8 *data, size_t len)
+{
+	struct wlantest_bss *bss, *bss2;
+	struct wlantest_sta *sta, *sta2;
+	int keyid;
+	u16 fc = le_to_host16(hdr->frame_control);
+	u8 *decrypted = NULL;
+	size_t dlen;
+	int tid;
+	u8 pn[6], *rsc = NULL;
+	struct wlantest_tdls *tdls = NULL, *found;
+	const u8 *tk = NULL;
+	int ptk_iter_done = 0;
+	int try_ptk_iter = 0;
+	int replay = 0;
+	int only_zero_tk = 0;
+	u16 seq_ctrl = le_to_host16(hdr->seq_ctrl);
+	const u8 *a1 = NULL, *a2 = NULL, *a3 = NULL;
+
+	if (hdr->addr1[0] & 0x01) {
+		rx_data_bss_prot_group(wt, hdr, hdrlen, qos, dst, src,
+				       data, len);
+		return;
+	}
+
+	if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) ==
+	    (WLAN_FC_TODS | WLAN_FC_FROMDS)) {
+		bss = bss_find(wt, hdr->addr1);
+		if (bss) {
+			sta = sta_find(bss, hdr->addr2);
+			if (sta) {
+				sta->counters[
+					WLANTEST_STA_COUNTER_PROT_DATA_TX]++;
+			}
+			if (!sta || !sta->ptk_set) {
+				bss2 = bss_find(wt, hdr->addr2);
+				if (bss2) {
+					sta2 = sta_find(bss2, hdr->addr1);
+					if (sta2 && (!sta || sta2->ptk_set)) {
+						bss = bss2;
+						sta = sta2;
+					}
+				}
+			}
+		} else {
+			bss = bss_find(wt, hdr->addr2);
+			if (!bss)
+				return;
+			sta = sta_find(bss, hdr->addr1);
+		}
+	} else if (fc & WLAN_FC_TODS) {
+		bss = bss_get(wt, hdr->addr1);
+		if (bss == NULL)
+			return;
+		sta = sta_find_mlo(wt, bss, hdr->addr2);
+		if (!sta)
+			sta = sta_get(bss, hdr->addr2);
+		if (sta)
+			sta->counters[WLANTEST_STA_COUNTER_PROT_DATA_TX]++;
+	} else if (fc & WLAN_FC_FROMDS) {
+		bss = bss_get(wt, hdr->addr2);
+		if (bss == NULL)
+			return;
+		sta = sta_find_mlo(wt, bss, hdr->addr1);
+		if (!sta)
+			sta = sta_get(bss, hdr->addr1);
+	} else {
+		bss = bss_get(wt, hdr->addr3);
+		if (bss == NULL)
+			return;
+		sta = sta_find(bss, hdr->addr2);
+		sta2 = sta_find(bss, hdr->addr1);
+		if (sta == NULL || sta2 == NULL)
+			return;
+		found = NULL;
+		dl_list_for_each(tdls, &bss->tdls, struct wlantest_tdls, list)
+		{
+			if ((tdls->init == sta && tdls->resp == sta2) ||
+			    (tdls->init == sta2 && tdls->resp == sta)) {
+				found = tdls;
+				if (tdls->link_up)
+					break;
+			}
+		}
+		if (found) {
+			if (!found->link_up)
+				add_note(wt, MSG_DEBUG,
+					 "TDLS: Link not up, but Data "
+					 "frame seen");
+			tk = found->tpk.tk;
+			tdls = found;
+		}
+	}
+	check_plaintext_prot(wt, hdr, data, len);
+	if ((sta == NULL ||
+	     (!sta->ptk_set && sta->pairwise_cipher != WPA_CIPHER_WEP40)) &&
+	    tk == NULL) {
+		add_note(wt, MSG_MSGDUMP, "No PTK known to decrypt the frame");
+		if (dl_list_empty(&wt->ptk)) {
+			if (len >= 4 && sta) {
+				keyid = data[3] >> 6;
+				only_zero_tk = 1;
+				goto check_zero_tk;
+			}
+			return;
+		}
+
+		try_ptk_iter = 1;
+	}
+
+	if (len < 4) {
+		add_note(wt, MSG_INFO, "Too short encrypted data frame");
+		return;
+	}
+
+	if (sta == NULL)
+		return;
+	if (sta->pairwise_cipher & (WPA_CIPHER_TKIP | WPA_CIPHER_CCMP |
+				    WPA_CIPHER_GCMP | WPA_CIPHER_GCMP_256 |
+				    WPA_CIPHER_CCMP_256) &&
+	    !(data[3] & 0x20)) {
+		add_note(wt, MSG_INFO, "Expected TKIP/CCMP/GCMP frame from "
+			 MACSTR " did not have ExtIV bit set to 1",
+			 MAC2STR(src));
+		return;
+	}
+
+	if (tk == NULL && sta->pairwise_cipher == WPA_CIPHER_TKIP) {
+		if (data[3] & 0x1f) {
+			add_note(wt, MSG_INFO, "TKIP frame from " MACSTR
+				 " used non-zero reserved bit",
+				 MAC2STR(hdr->addr2));
+		}
+		if (data[1] != ((data[0] | 0x20) & 0x7f)) {
+			add_note(wt, MSG_INFO, "TKIP frame from " MACSTR
+				 " used incorrect WEPSeed[1] (was 0x%x, "
+				 "expected 0x%x)",
+				 MAC2STR(hdr->addr2), data[1],
+				 (data[0] | 0x20) & 0x7f);
+		}
+	} else if (tk || sta->pairwise_cipher == WPA_CIPHER_CCMP ||
+		   sta->pairwise_cipher == WPA_CIPHER_GCMP ||
+		   sta->pairwise_cipher == WPA_CIPHER_GCMP_256 ||
+		   sta->pairwise_cipher == WPA_CIPHER_CCMP_256) {
+		if (data[2] != 0 || (data[3] & 0x1f) != 0) {
+			add_note(wt, MSG_INFO, "CCMP/GCMP frame from " MACSTR
+				 " used non-zero reserved bit",
+				 MAC2STR(hdr->addr2));
+		}
+	}
+
+	keyid = data[3] >> 6;
+	if (keyid != 0 &&
+	    (!(sta->rsn_capab & WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST) ||
+	     !(bss->rsn_capab & WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST) ||
+	     keyid != 1)) {
+		add_note(wt, MSG_INFO,
+			 "Unexpected KeyID %d in individually addressed Data frame from "
+			 MACSTR,
+			 keyid, MAC2STR(hdr->addr2));
+	}
+
+	if (qos) {
+		tid = qos[0] & 0x0f;
+		if (fc & WLAN_FC_TODS)
+			sta->tx_tid[tid]++;
+		else
+			sta->rx_tid[tid]++;
+	} else {
+		tid = 0;
+		if (fc & WLAN_FC_TODS)
+			sta->tx_tid[16]++;
+		else
+			sta->rx_tid[16]++;
+	}
+	if (tk) {
+		if (os_memcmp(hdr->addr2, tdls->init->addr, ETH_ALEN) == 0)
+			rsc = tdls->rsc_init[tid];
+		else
+			rsc = tdls->rsc_resp[tid];
+	} else if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) ==
+		   (WLAN_FC_TODS | WLAN_FC_FROMDS)) {
+		if (os_memcmp(sta->addr, hdr->addr2, ETH_ALEN) == 0)
+			rsc = sta->rsc_tods[tid];
+		else
+			rsc = sta->rsc_fromds[tid];
+	} else if (fc & WLAN_FC_TODS)
+		rsc = sta->rsc_tods[tid];
+	else
+		rsc = sta->rsc_fromds[tid];
+
+
+	if (tk == NULL && sta->pairwise_cipher == WPA_CIPHER_TKIP)
+		tkip_get_pn(pn, data);
+	else if (sta->pairwise_cipher == WPA_CIPHER_WEP40)
+		goto skip_replay_det;
+	else
+		ccmp_get_pn(pn, data);
+	if (os_memcmp(pn, rsc, 6) <= 0) {
+		char pn_hex[6 * 2 + 1], rsc_hex[6 * 2 + 1];
+
+		wpa_snprintf_hex(pn_hex, sizeof(pn_hex), pn, 6);
+		wpa_snprintf_hex(rsc_hex, sizeof(rsc_hex), rsc, 6);
+		add_note(wt, MSG_INFO, "replay detected: A1=" MACSTR
+			 " A2=" MACSTR " A3=" MACSTR
+			 " seq=%u frag=%u%s keyid=%d tid=%d #%u %s<=%s",
+			 MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+			 MAC2STR(hdr->addr3),
+			 WLAN_GET_SEQ_SEQ(seq_ctrl),
+			 WLAN_GET_SEQ_FRAG(seq_ctrl),
+			 (le_to_host16(hdr->frame_control) &  WLAN_FC_RETRY) ?
+			 " Retry" : "",
+			 keyid, tid, wt->frame_num, pn_hex, rsc_hex);
+		replay = 1;
+	}
+
+skip_replay_det:
+	if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) &&
+	    !is_zero_ether_addr(sta->mld_mac_addr) &&
+	    !is_zero_ether_addr(bss->mld_mac_addr)) {
+		if (os_memcmp(hdr->addr1, sta->addr, ETH_ALEN) == 0) {
+			a1 = sta->mld_mac_addr;
+			a2 = bss->mld_mac_addr;
+		} else {
+			a1 = bss->mld_mac_addr;
+			a2 = sta->mld_mac_addr;
+		}
+
+		if (os_memcmp(hdr->addr3, bss->bssid, ETH_ALEN) == 0)
+			a3 = bss->mld_mac_addr;
+	}
+
+	if (tk) {
+		if (sta->pairwise_cipher == WPA_CIPHER_CCMP_256) {
+			decrypted = ccmp_256_decrypt(tk, hdr, a1, a2, a3,
+						     data, len, &dlen);
+			write_decrypted_note(wt, decrypted, tk, 32, keyid);
+		} else if (sta->pairwise_cipher == WPA_CIPHER_GCMP ||
+			   sta->pairwise_cipher == WPA_CIPHER_GCMP_256) {
+			decrypted = gcmp_decrypt(tk, sta->ptk.tk_len, hdr,
+						 a1, a2, a3, data, len, &dlen);
+			write_decrypted_note(wt, decrypted, tk, sta->ptk.tk_len,
+					     keyid);
+		} else {
+			decrypted = ccmp_decrypt(tk, hdr, a1, a2, a3, data, len,
+						 &dlen);
+			write_decrypted_note(wt, decrypted, tk, 16, keyid);
+		}
+	} else if (sta->pairwise_cipher == WPA_CIPHER_TKIP) {
+		enum michael_mic_result mic_res;
+
+		decrypted = tkip_decrypt(sta->ptk.tk, hdr, data, len, &dlen,
+					 &mic_res, &wt->tkip_frag);
+		if (decrypted && mic_res == MICHAEL_MIC_INCORRECT)
+			add_note(wt, MSG_INFO, "Invalid Michael MIC");
+		else if (decrypted && mic_res == MICHAEL_MIC_NOT_VERIFIED)
+			add_note(wt, MSG_DEBUG, "Michael MIC not verified");
+		write_decrypted_note(wt, decrypted, sta->ptk.tk, 32, keyid);
+	} else if (sta->pairwise_cipher == WPA_CIPHER_WEP40) {
+		decrypted = wep_decrypt(wt, hdr, data, len, &dlen);
+	} else if (sta->ptk_set) {
+		decrypted = try_ptk_decrypt(wt, sta, hdr, keyid, data, len,
+					    sta->ptk.tk, sta->ptk.tk_len,
+					    &dlen);
+	} else {
+		decrypted = try_all_ptk(wt, sta->pairwise_cipher, hdr,
+					a1, a2, a3,
+					keyid, data, len, &dlen);
+		ptk_iter_done = 1;
+	}
+	if (!decrypted && !ptk_iter_done) {
+		decrypted = try_all_ptk(wt, sta->pairwise_cipher, hdr,
+					a1, a2, a3,
+					keyid, data, len, &dlen);
+		if (decrypted) {
+			add_note(wt, MSG_DEBUG, "Current PTK did not work, but found a match from all known PTKs");
+		}
+	}
+check_zero_tk:
+	if (!decrypted) {
+		struct wpa_ptk zero_ptk;
+		int old_debug_level = wpa_debug_level;
+
+		os_memset(&zero_ptk, 0, sizeof(zero_ptk));
+		zero_ptk.tk_len = wpa_cipher_key_len(sta->pairwise_cipher);
+		wpa_debug_level = MSG_ERROR;
+		decrypted = try_ptk(wt, sta->pairwise_cipher, &zero_ptk, hdr,
+				    a1, a2, a3, data, len, &dlen);
+		wpa_debug_level = old_debug_level;
+		if (decrypted) {
+			add_note(wt, MSG_DEBUG,
+				 "Frame was encrypted with zero TK");
+			wpa_printf(MSG_INFO, "Zero TK used in frame #%u: A2="
+				   MACSTR " seq=%u",
+				   wt->frame_num, MAC2STR(hdr->addr2),
+				   WLAN_GET_SEQ_SEQ(
+					   le_to_host16(hdr->seq_ctrl)));
+			write_decrypted_note(wt, decrypted, zero_ptk.tk,
+					     zero_ptk.tk_len, keyid);
+		}
+	}
+	if (decrypted) {
+		u16 fc = le_to_host16(hdr->frame_control);
+		const u8 *peer_addr = NULL;
+		if (!(fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)))
+			peer_addr = hdr->addr1;
+		if (!replay && rsc)
+			os_memcpy(rsc, pn, 6);
+		rx_data_process(wt, bss, bss->bssid, sta->addr, dst, src,
+				decrypted, dlen, 1, peer_addr, qos);
+		write_pcap_decrypted(wt, (const u8 *) hdr, hdrlen,
+				     decrypted, dlen);
+	} else if (sta->tptk_set) {
+		/* Check whether TPTK has a matching TK that could be used to
+		 * decrypt the frame. That could happen if EAPOL-Key msg 4/4
+		 * was missing in the capture and this was PTK rekeying. */
+		decrypted = try_ptk_decrypt(wt, sta, hdr, keyid, data, len,
+					    sta->tptk.tk, sta->tptk.tk_len,
+					    &dlen);
+		if (decrypted) {
+			add_note(wt, MSG_DEBUG,
+				 "Update PTK (rekeying; no valid EAPOL-Key msg 4/4 seen)");
+			os_memcpy(&sta->ptk, &sta->tptk, sizeof(sta->ptk));
+			sta->ptk_set = 1;
+			sta->tptk_set = 0;
+			os_memset(sta->rsc_tods, 0, sizeof(sta->rsc_tods));
+			os_memset(sta->rsc_fromds, 0, sizeof(sta->rsc_fromds));
+		}
+	} else {
+		if (!try_ptk_iter && !only_zero_tk) {
+			wpa_printf(MSG_DEBUG,
+				   "Failed to decrypt frame #%u A2=" MACSTR
+				   " seq=%u",
+				   wt->frame_num, MAC2STR(hdr->addr2),
+				   WLAN_GET_SEQ_SEQ(seq_ctrl));
+			add_note(wt, MSG_DEBUG, "Failed to decrypt frame");
+		}
+
+		/* Assume the frame was corrupted and there was no FCS to check.
+		 * Allow retry of this particular frame to be processed so that
+		 * it could end up getting decrypted if it was received without
+		 * corruption. */
+		sta->allow_duplicate = 1;
+	}
+	os_free(decrypted);
+}
+
+
+static void rx_data_bss(struct wlantest *wt, const struct ieee80211_hdr *hdr,
+			size_t hdrlen, const u8 *qos, const u8 *dst,
+			const u8 *src, const u8 *data, size_t len)
+{
+	u16 fc = le_to_host16(hdr->frame_control);
+	int prot = !!(fc & WLAN_FC_ISWEP);
+
+	if (qos) {
+		u8 ack = (qos[0] & 0x60) >> 5;
+		wpa_printf(MSG_MSGDUMP, "BSS DATA: " MACSTR " -> " MACSTR
+			   " len=%u%s tid=%u%s%s",
+			   MAC2STR(src), MAC2STR(dst), (unsigned int) len,
+			   prot ? " Prot" : "", qos[0] & 0x0f,
+			   (qos[0] & 0x10) ? " EOSP" : "",
+			   ack == 0 ? "" :
+			   (ack == 1 ? " NoAck" :
+			    (ack == 2 ? " NoExpAck" : " BA")));
+	} else {
+		wpa_printf(MSG_MSGDUMP, "BSS DATA: " MACSTR " -> " MACSTR
+			   " len=%u%s",
+			   MAC2STR(src), MAC2STR(dst), (unsigned int) len,
+			   prot ? " Prot" : "");
+	}
+
+	if (prot)
+		rx_data_bss_prot(wt, hdr, hdrlen, qos, dst, src, data, len);
+	else {
+		const u8 *bssid, *sta_addr, *peer_addr;
+		struct wlantest_bss *bss;
+
+		if (fc & WLAN_FC_TODS) {
+			bssid = hdr->addr1;
+			sta_addr = hdr->addr2;
+			peer_addr = NULL;
+		} else if (fc & WLAN_FC_FROMDS) {
+			bssid = hdr->addr2;
+			sta_addr = hdr->addr1;
+			peer_addr = NULL;
+		} else {
+			bssid = hdr->addr3;
+			sta_addr = hdr->addr2;
+			peer_addr = hdr->addr1;
+		}
+
+		bss = bss_get(wt, bssid);
+		if (bss) {
+			struct wlantest_sta *sta;
+
+			sta = sta_find_mlo(wt, bss, sta_addr);
+			if (!sta)
+				sta = sta_get(bss, sta_addr);
+
+			if (sta) {
+				if (qos) {
+					int tid = qos[0] & 0x0f;
+					if (fc & WLAN_FC_TODS)
+						sta->tx_tid[tid]++;
+					else
+						sta->rx_tid[tid]++;
+				} else {
+					if (fc & WLAN_FC_TODS)
+						sta->tx_tid[16]++;
+					else
+						sta->rx_tid[16]++;
+				}
+			}
+		}
+
+		rx_data_process(wt, bss, bssid, sta_addr, dst, src, data, len,
+				0, peer_addr, qos);
+	}
+}
+
+
+static struct wlantest_tdls * get_tdls(struct wlantest *wt, const u8 *bssid,
+				       const u8 *sta1_addr,
+				       const u8 *sta2_addr)
+{
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta1, *sta2;
+	struct wlantest_tdls *tdls, *found = NULL;
+
+	bss = bss_find(wt, bssid);
+	if (bss == NULL)
+		return NULL;
+	sta1 = sta_find(bss, sta1_addr);
+	if (sta1 == NULL)
+		return NULL;
+	sta2 = sta_find(bss, sta2_addr);
+	if (sta2 == NULL)
+		return NULL;
+
+	dl_list_for_each(tdls, &bss->tdls, struct wlantest_tdls, list) {
+		if ((tdls->init == sta1 && tdls->resp == sta2) ||
+		    (tdls->init == sta2 && tdls->resp == sta1)) {
+			found = tdls;
+			if (tdls->link_up)
+				break;
+		}
+	}
+
+	return found;
+}
+
+
+static void add_direct_link(struct wlantest *wt, const u8 *bssid,
+			    const u8 *sta1_addr, const u8 *sta2_addr)
+{
+	struct wlantest_tdls *tdls;
+
+	tdls = get_tdls(wt, bssid, sta1_addr, sta2_addr);
+	if (tdls == NULL)
+		return;
+
+	if (tdls->link_up)
+		tdls->counters[WLANTEST_TDLS_COUNTER_VALID_DIRECT_LINK]++;
+	else
+		tdls->counters[WLANTEST_TDLS_COUNTER_INVALID_DIRECT_LINK]++;
+}
+
+
+static void add_ap_path(struct wlantest *wt, const u8 *bssid,
+			const u8 *sta1_addr, const u8 *sta2_addr)
+{
+	struct wlantest_tdls *tdls;
+
+	tdls = get_tdls(wt, bssid, sta1_addr, sta2_addr);
+	if (tdls == NULL)
+		return;
+
+	if (tdls->link_up)
+		tdls->counters[WLANTEST_TDLS_COUNTER_INVALID_AP_PATH]++;
+	else
+		tdls->counters[WLANTEST_TDLS_COUNTER_VALID_AP_PATH]++;
+}
+
+
+void rx_data(struct wlantest *wt, const u8 *data, size_t len)
+{
+	const struct ieee80211_hdr *hdr;
+	u16 fc, stype;
+	size_t hdrlen;
+	const u8 *qos = NULL;
+
+	if (len < 24)
+		return;
+
+	hdr = (const struct ieee80211_hdr *) data;
+	fc = le_to_host16(hdr->frame_control);
+	stype = WLAN_FC_GET_STYPE(fc);
+	hdrlen = 24;
+	if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) ==
+	    (WLAN_FC_TODS | WLAN_FC_FROMDS))
+		hdrlen += ETH_ALEN;
+	if (stype & 0x08) {
+		qos = data + hdrlen;
+		hdrlen += 2;
+	}
+	if ((fc & WLAN_FC_HTC) && (stype & 0x08))
+		hdrlen += 4; /* HT Control field */
+	if (len < hdrlen)
+		return;
+	wt->rx_data++;
+
+	switch (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) {
+	case 0:
+		wpa_printf(MSG_EXCESSIVE, "DATA %s%s%s IBSS DA=" MACSTR " SA="
+			   MACSTR " BSSID=" MACSTR,
+			   data_stype(WLAN_FC_GET_STYPE(fc)),
+			   fc & WLAN_FC_PWRMGT ? " PwrMgt" : "",
+			   fc & WLAN_FC_ISWEP ? " Prot" : "",
+			   MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+			   MAC2STR(hdr->addr3));
+		add_direct_link(wt, hdr->addr3, hdr->addr1, hdr->addr2);
+		rx_data_bss(wt, hdr, hdrlen, qos, hdr->addr1, hdr->addr2,
+			    data + hdrlen, len - hdrlen);
+		break;
+	case WLAN_FC_FROMDS:
+		wpa_printf(MSG_EXCESSIVE, "DATA %s%s%s FromDS DA=" MACSTR
+			   " BSSID=" MACSTR " SA=" MACSTR,
+			   data_stype(WLAN_FC_GET_STYPE(fc)),
+			   fc & WLAN_FC_PWRMGT ? " PwrMgt" : "",
+			   fc & WLAN_FC_ISWEP ? " Prot" : "",
+			   MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+			   MAC2STR(hdr->addr3));
+		add_ap_path(wt, hdr->addr2, hdr->addr1, hdr->addr3);
+		rx_data_bss(wt, hdr, hdrlen, qos, hdr->addr1, hdr->addr3,
+			    data + hdrlen, len - hdrlen);
+		break;
+	case WLAN_FC_TODS:
+		wpa_printf(MSG_EXCESSIVE, "DATA %s%s%s ToDS BSSID=" MACSTR
+			   " SA=" MACSTR " DA=" MACSTR,
+			   data_stype(WLAN_FC_GET_STYPE(fc)),
+			   fc & WLAN_FC_PWRMGT ? " PwrMgt" : "",
+			   fc & WLAN_FC_ISWEP ? " Prot" : "",
+			   MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+			   MAC2STR(hdr->addr3));
+		add_ap_path(wt, hdr->addr1, hdr->addr3, hdr->addr2);
+		rx_data_bss(wt, hdr, hdrlen, qos, hdr->addr3, hdr->addr2,
+			    data + hdrlen, len - hdrlen);
+		break;
+	case WLAN_FC_TODS | WLAN_FC_FROMDS:
+		wpa_printf(MSG_EXCESSIVE, "DATA %s%s%s WDS RA=" MACSTR " TA="
+			   MACSTR " DA=" MACSTR " SA=" MACSTR,
+			   data_stype(WLAN_FC_GET_STYPE(fc)),
+			   fc & WLAN_FC_PWRMGT ? " PwrMgt" : "",
+			   fc & WLAN_FC_ISWEP ? " Prot" : "",
+			   MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+			   MAC2STR(hdr->addr3),
+			   MAC2STR((const u8 *) (hdr + 1)));
+		rx_data_bss(wt, hdr, hdrlen, qos, hdr->addr1, hdr->addr2,
+			    data + hdrlen, len - hdrlen);
+		break;
+	}
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/rx_eapol.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/rx_eapol.c
new file mode 100644
index 0000000..5a38123
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/rx_eapol.c
@@ -0,0 +1,1700 @@
+/*
+ * Received Data frame processing for EAPOL messages
+ * Copyright (c) 2010-2020, 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/aes_wrap.h"
+#include "crypto/crypto.h"
+#include "common/defs.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/eapol_common.h"
+#include "common/wpa_common.h"
+#include "rsn_supp/wpa_ie.h"
+#include "wlantest.h"
+
+
+static int is_zero(const u8 *buf, size_t len)
+{
+	size_t i;
+	for (i = 0; i < len; i++) {
+		if (buf[i])
+			return 0;
+	}
+	return 1;
+}
+
+
+static size_t determine_mic_len(struct wlantest_sta *sta)
+{
+	size_t pmk_len = PMK_LEN;
+
+	if (sta && wpa_key_mgmt_sae_ext_key(sta->key_mgmt) &&
+	    sta->sae_group) {
+		switch (sta->sae_group) {
+		case 20:
+			pmk_len = 48;
+			break;
+		case 21:
+			pmk_len = 64;
+			break;
+		}
+	}
+
+	return wpa_mic_len(sta->key_mgmt, pmk_len);
+}
+
+
+static int check_mic(struct wlantest_sta *sta, const u8 *kck, size_t kck_len,
+		     int ver, const u8 *data, size_t len)
+{
+	u8 *buf;
+	int ret = -1;
+	struct ieee802_1x_hdr *hdr;
+	struct wpa_eapol_key *key;
+	u8 rx_mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
+	size_t mic_len = determine_mic_len(sta);
+
+	buf = os_memdup(data, len);
+	if (buf == NULL)
+		return -1;
+	hdr = (struct ieee802_1x_hdr *) buf;
+	key = (struct wpa_eapol_key *) (hdr + 1);
+
+	os_memcpy(rx_mic, key + 1, mic_len);
+	os_memset(key + 1, 0, mic_len);
+
+	if (wpa_eapol_key_mic(kck, kck_len, sta->key_mgmt, ver, buf, len,
+			      (u8 *) (key + 1)) == 0 &&
+	    os_memcmp(rx_mic, key + 1, mic_len) == 0)
+		ret = 0;
+
+	os_free(buf);
+
+	return ret;
+}
+
+
+static void rx_data_eapol_key_1_of_4(struct wlantest *wt, const u8 *dst,
+				     const u8 *src, const u8 *bssid,
+				     const u8 *data, size_t len)
+{
+	struct wlantest_bss *bss, *bss_mld;
+	struct wlantest_sta *sta;
+	const struct ieee802_1x_hdr *eapol;
+	const struct wpa_eapol_key *hdr;
+	const u8 *key_data, *mic;
+	size_t mic_len, left;
+	u16 key_data_len;
+	struct wpa_eapol_ie_parse ie;
+
+	wpa_printf(MSG_DEBUG, "EAPOL-Key 1/4 " MACSTR " -> " MACSTR " (BSSID "
+		   MACSTR ")",
+		   MAC2STR(src), MAC2STR(dst), MAC2STR(bssid));
+	if (os_memcmp(src, bssid, ETH_ALEN) == 0) {
+		bss = bss_get(wt, src);
+	} else {
+		bss = bss_find(wt, bssid);
+		bss_mld = bss_find(wt, src);
+		if (bss_mld && (!bss || sta_find(bss_mld, src)))
+			bss = bss_get(wt, src);
+		else
+			bss = bss_get(wt, bssid);
+	}
+	if (bss == NULL)
+		return;
+	sta = sta_get(bss, dst);
+	if (sta == NULL)
+		return;
+
+	eapol = (const struct ieee802_1x_hdr *) data;
+	hdr = (const struct wpa_eapol_key *) (eapol + 1);
+	left = len - sizeof(*hdr);
+	mic_len = determine_mic_len(sta);
+	if (mic_len > left) {
+		add_note(wt, MSG_INFO, "EAPOL-Key 1/4 from " MACSTR
+			 " has a truncated MIC field", MAC2STR(src));
+		return;
+	}
+	left -= mic_len;
+	mic = (const u8 *) (hdr + 1);
+	if (is_zero(hdr->key_nonce, WPA_NONCE_LEN)) {
+		add_note(wt, MSG_INFO, "EAPOL-Key 1/4 from " MACSTR
+			 " used zero nonce", MAC2STR(src));
+	}
+	if (!is_zero(hdr->key_rsc, 8)) {
+		add_note(wt, MSG_INFO, "EAPOL-Key 1/4 from " MACSTR
+			 " used non-zero Key RSC", MAC2STR(src));
+	}
+	os_memcpy(sta->anonce, hdr->key_nonce, WPA_NONCE_LEN);
+	if (left < 2) {
+		add_note(wt, MSG_INFO, "EAPOL-Key 1/4 from " MACSTR
+			 " has a truncated Key Data Length field",
+			 MAC2STR(src));
+		return;
+	}
+	left -= 2;
+	key_data = mic + mic_len + 2;
+	key_data_len = WPA_GET_BE16(mic + mic_len);
+	if (key_data_len > left) {
+		add_note(wt, MSG_INFO, "EAPOL-Key 1/4 from " MACSTR
+			 " has a truncated Key Data field",
+			 MAC2STR(src));
+		return;
+	}
+
+	if (wpa_parse_kde_ies(key_data, key_data_len, &ie) < 0) {
+		add_note(wt, MSG_INFO, "Failed to parse EAPOL-Key Key Data");
+		return;
+	}
+
+	if (ie.mac_addr) {
+		if (is_zero_ether_addr(bss->mld_mac_addr)) {
+			wpa_printf(MSG_DEBUG,
+				   "Learned AP MLD MAC Address from EAPOL-Key 1/4: "
+				   MACSTR, MAC2STR(ie.mac_addr));
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "Updated AP MLD MAC Address from EAPOL-Key 1/4: "
+				   MACSTR " --> " MACSTR,
+				   MAC2STR(bss->mld_mac_addr),
+				   MAC2STR(ie.mac_addr));
+		}
+		os_memcpy(bss->mld_mac_addr, ie.mac_addr, ETH_ALEN);
+	}
+}
+
+
+static int try_pmk(struct wlantest *wt, struct wlantest_bss *bss,
+		   struct wlantest_sta *sta, u16 ver,
+		   const u8 *data, size_t len,
+		   struct wlantest_pmk *pmk)
+{
+	struct wpa_ptk ptk;
+	const u8 *sa, *aa;
+	bool mlo;
+	size_t kdk_len;
+
+	mlo = !is_zero_ether_addr(sta->mld_mac_addr) &&
+		!is_zero_ether_addr(bss->mld_mac_addr);
+	sa = mlo ? sta->mld_mac_addr : sta->addr;
+	aa = mlo ? bss->mld_mac_addr : bss->bssid;
+
+	if (ieee802_11_rsnx_capab_len(bss->rsnxe, bss->rsnxe_len,
+				      WLAN_RSNX_CAPAB_SECURE_LTF) &&
+	    ieee802_11_rsnx_capab_len(sta->rsnxe, sta->rsnxe_len,
+				      WLAN_RSNX_CAPAB_SECURE_LTF))
+		kdk_len = WPA_KDK_MAX_LEN;
+	else
+		kdk_len = 0;
+
+	if (wpa_key_mgmt_ft(sta->key_mgmt)) {
+		u8 ptk_name[WPA_PMK_NAME_LEN];
+		int use_sha384 = wpa_key_mgmt_sha384(sta->key_mgmt);
+
+		if (wpa_derive_pmk_r0(pmk->pmk, pmk->pmk_len,
+				      bss->ssid, bss->ssid_len, bss->mdid,
+				      bss->r0kh_id, bss->r0kh_id_len,
+				      sa, sta->pmk_r0, sta->pmk_r0_name,
+				      sta->key_mgmt) < 0)
+			return -1;
+		if (wpa_key_mgmt_sae_ext_key(sta->key_mgmt))
+			sta->pmk_r0_len = pmk->pmk_len;
+		else
+			sta->pmk_r0_len = use_sha384 ? PMK_LEN_SUITE_B_192 :
+				PMK_LEN;
+		if (wpa_derive_pmk_r1(sta->pmk_r0, sta->pmk_r0_len,
+				      sta->pmk_r0_name,
+				      bss->r1kh_id, sa,
+				      sta->pmk_r1, sta->pmk_r1_name) < 0)
+			return -1;
+		sta->pmk_r1_len = sta->pmk_r0_len;
+		if (wpa_pmk_r1_to_ptk(sta->pmk_r1, sta->pmk_r1_len,
+				      sta->snonce, sta->anonce, sa,
+				      aa, sta->pmk_r1_name,
+				      &ptk, ptk_name, sta->key_mgmt,
+				      sta->pairwise_cipher, 0) < 0 ||
+		    check_mic(sta, ptk.kck, ptk.kck_len, ver, data, len) < 0)
+			return -1;
+	} else if (wpa_pmk_to_ptk(pmk->pmk, pmk->pmk_len,
+				  "Pairwise key expansion",
+				  aa, sa, sta->anonce,
+				  sta->snonce, &ptk, sta->key_mgmt,
+				  sta->pairwise_cipher, NULL, 0, kdk_len) < 0 ||
+		   check_mic(sta, ptk.kck, ptk.kck_len, ver, data, len) < 0) {
+		return -1;
+	}
+
+	if (mlo) {
+		wpa_printf(MSG_INFO, "Derived PTK for STA " MACSTR " (MLD "
+			   MACSTR ") BSSID " MACSTR " (MLD " MACSTR ")",
+			   MAC2STR(sta->addr), MAC2STR(sta->mld_mac_addr),
+			   MAC2STR(bss->bssid), MAC2STR(bss->mld_mac_addr));
+	} else {
+		wpa_printf(MSG_INFO, "Derived PTK for STA " MACSTR
+			   " BSSID " MACSTR,
+			   MAC2STR(sta->addr), MAC2STR(bss->bssid));
+	}
+	sta->counters[WLANTEST_STA_COUNTER_PTK_LEARNED]++;
+	if (sta->ptk_set) {
+		/*
+		 * Rekeying - use new PTK for EAPOL-Key frames, but continue
+		 * using the old PTK for frame decryption.
+		 */
+		add_note(wt, MSG_DEBUG, "Derived PTK during rekeying");
+		os_memcpy(&sta->tptk, &ptk, sizeof(ptk));
+		wpa_hexdump(MSG_DEBUG, "TPTK:KCK",
+			    sta->tptk.kck, sta->tptk.kck_len);
+		wpa_hexdump(MSG_DEBUG, "TPTK:KEK",
+			    sta->tptk.kek, sta->tptk.kek_len);
+		wpa_hexdump(MSG_DEBUG, "TPTK:TK",
+			    sta->tptk.tk, sta->tptk.tk_len);
+		sta->tptk_set = 1;
+		return 0;
+	}
+	sta_new_ptk(wt, sta, &ptk);
+	return 0;
+}
+
+
+static void derive_ptk(struct wlantest *wt, struct wlantest_bss *bss,
+		       struct wlantest_sta *sta, u16 ver,
+		       const u8 *data, size_t len)
+{
+	struct wlantest_pmk *pmk;
+
+	wpa_printf(MSG_DEBUG, "Trying to derive PTK for " MACSTR " (MLD " MACSTR
+		   ") (ver %u)",
+		   MAC2STR(sta->addr), MAC2STR(sta->mld_mac_addr), ver);
+	dl_list_for_each(pmk, &bss->pmk, struct wlantest_pmk, list) {
+		wpa_printf(MSG_DEBUG, "Try per-BSS PMK");
+		if (try_pmk(wt, bss, sta, ver, data, len, pmk) == 0)
+			return;
+	}
+
+	dl_list_for_each(pmk, &wt->pmk, struct wlantest_pmk, list) {
+		wpa_printf(MSG_DEBUG, "Try global PMK");
+		if (try_pmk(wt, bss, sta, ver, data, len, pmk) == 0)
+			return;
+	}
+
+	if (!sta->ptk_set) {
+		struct wlantest_ptk *ptk;
+		int prev_level = wpa_debug_level;
+
+		wpa_debug_level = MSG_WARNING;
+		dl_list_for_each(ptk, &wt->ptk, struct wlantest_ptk, list) {
+			if (check_mic(sta, ptk->ptk.kck, ptk->ptk.kck_len,
+				      ver, data, len) < 0)
+				continue;
+			wpa_printf(MSG_INFO, "Pre-set PTK matches for STA "
+				   MACSTR " BSSID " MACSTR,
+				   MAC2STR(sta->addr), MAC2STR(bss->bssid));
+			add_note(wt, MSG_DEBUG, "Using pre-set PTK");
+			ptk->ptk_len = 32 +
+				wpa_cipher_key_len(sta->pairwise_cipher);
+			os_memcpy(&sta->ptk, &ptk->ptk, sizeof(ptk->ptk));
+			wpa_hexdump(MSG_DEBUG, "PTK:KCK",
+				    sta->ptk.kck, sta->ptk.kck_len);
+			wpa_hexdump(MSG_DEBUG, "PTK:KEK",
+				    sta->ptk.kek, sta->ptk.kek_len);
+			wpa_hexdump(MSG_DEBUG, "PTK:TK",
+				    sta->ptk.tk, sta->ptk.tk_len);
+			sta->ptk_set = 1;
+			os_memset(sta->rsc_tods, 0, sizeof(sta->rsc_tods));
+			os_memset(sta->rsc_fromds, 0, sizeof(sta->rsc_fromds));
+		}
+		wpa_debug_level = prev_level;
+	}
+
+	add_note(wt, MSG_DEBUG, "No matching PMK found to derive PTK");
+}
+
+
+static void elems_from_eapol_ie(struct ieee802_11_elems *elems,
+				struct wpa_eapol_ie_parse *ie)
+{
+	os_memset(elems, 0, sizeof(*elems));
+	if (ie->wpa_ie) {
+		elems->wpa_ie = ie->wpa_ie + 2;
+		elems->wpa_ie_len = ie->wpa_ie_len - 2;
+	}
+	if (ie->rsn_ie) {
+		elems->rsn_ie = ie->rsn_ie + 2;
+		elems->rsn_ie_len = ie->rsn_ie_len - 2;
+	}
+	if (ie->osen) {
+		elems->osen = ie->osen + 2;
+		elems->osen_len = ie->osen_len - 2;
+	}
+}
+
+
+static void rx_data_eapol_key_2_of_4(struct wlantest *wt, const u8 *dst,
+				     const u8 *src, const u8 *bssid,
+				     const u8 *data, size_t len)
+{
+	struct wlantest_bss *bss, *bss_mld;
+	struct wlantest_sta *sta;
+	const struct ieee802_1x_hdr *eapol;
+	const struct wpa_eapol_key *hdr;
+	const u8 *key_data, *kck, *mic;
+	size_t kck_len, mic_len, left;
+	u16 key_info, key_data_len;
+	struct wpa_eapol_ie_parse ie;
+	int link_id;
+
+	wpa_printf(MSG_DEBUG, "EAPOL-Key 2/4 " MACSTR " -> " MACSTR " (BSSID "
+		   MACSTR ")",
+		   MAC2STR(src), MAC2STR(dst), MAC2STR(bssid));
+	if (os_memcmp(dst, bssid, ETH_ALEN) == 0) {
+		bss = bss_get(wt, dst);
+	} else {
+		bss = bss_find(wt, bssid);
+		bss_mld = bss_find(wt, dst);
+		if (bss_mld && (!bss || sta_find(bss_mld, src)))
+			bss = bss_get(wt, dst);
+		else
+			bss = bss_get(wt, bssid);
+	}
+	if (bss == NULL)
+		return;
+	sta = sta_get(bss, src);
+	if (sta == NULL)
+		return;
+
+	eapol = (const struct ieee802_1x_hdr *) data;
+	hdr = (const struct wpa_eapol_key *) (eapol + 1);
+	left = len - sizeof(*hdr);
+	mic_len = determine_mic_len(sta);
+	if (mic_len > left) {
+		add_note(wt, MSG_INFO, "EAPOL-Key 2/4 from " MACSTR
+			 " has a truncated MIC field", MAC2STR(src));
+		return;
+	}
+	left -= mic_len;
+	mic = (const u8 *) (hdr + 1);
+	if (!is_zero(hdr->key_rsc, 8)) {
+		add_note(wt, MSG_INFO, "EAPOL-Key 2/4 from " MACSTR
+			 " used non-zero Key RSC", MAC2STR(src));
+	}
+	os_memcpy(sta->snonce, hdr->key_nonce, WPA_NONCE_LEN);
+	key_info = WPA_GET_BE16(hdr->key_info);
+	if (left < 2) {
+		add_note(wt, MSG_INFO, "EAPOL-Key 2/4 from " MACSTR
+			 " has a truncated Key Data Length field",
+			 MAC2STR(src));
+		return;
+	}
+	left -= 2;
+	key_data = mic + mic_len + 2;
+	key_data_len = WPA_GET_BE16(mic + mic_len);
+	if (key_data_len > left) {
+		add_note(wt, MSG_INFO, "EAPOL-Key 2/4 from " MACSTR
+			 " has a truncated Key Data field",
+			 MAC2STR(src));
+		return;
+	}
+
+	if (wpa_parse_kde_ies(key_data, key_data_len, &ie) < 0) {
+		add_note(wt, MSG_INFO, "Failed to parse EAPOL-Key Key Data");
+		return;
+	}
+
+	if (!sta->assocreq_seen) {
+		struct ieee802_11_elems elems;
+
+		elems_from_eapol_ie(&elems, &ie);
+		wpa_printf(MSG_DEBUG,
+			   "Update STA data based on IEs in EAPOL-Key 2/4");
+		sta_update_assoc(sta, &elems);
+	}
+
+	if (ie.mac_addr) {
+		if (is_zero_ether_addr(sta->mld_mac_addr)) {
+			wpa_printf(MSG_DEBUG,
+				   "Learned non-AP STA MLD MAC Address from EAPOL-Key 2/4: "
+				   MACSTR, MAC2STR(ie.mac_addr));
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "Updated non-AP STA MLD MAC Address from EAPOL-Key 2/4: "
+				   MACSTR " --> " MACSTR,
+				   MAC2STR(sta->mld_mac_addr),
+				   MAC2STR(ie.mac_addr));
+		}
+		os_memcpy(sta->mld_mac_addr, ie.mac_addr, ETH_ALEN);
+	}
+
+	derive_ptk(wt, bss, sta, key_info & WPA_KEY_INFO_TYPE_MASK, data, len);
+
+	if (!sta->ptk_set && !sta->tptk_set) {
+		add_note(wt, MSG_DEBUG,
+			 "No PTK known to process EAPOL-Key 2/4");
+		return;
+	}
+
+	kck = sta->ptk.kck;
+	kck_len = sta->ptk.kck_len;
+	if (sta->tptk_set) {
+		add_note(wt, MSG_DEBUG,
+			 "Use TPTK for validation EAPOL-Key MIC");
+		kck = sta->tptk.kck;
+		kck_len = sta->tptk.kck_len;
+	}
+	if (check_mic(sta, kck, kck_len,
+		      key_info & WPA_KEY_INFO_TYPE_MASK, data, len) < 0) {
+		add_note(wt, MSG_INFO, "Mismatch in EAPOL-Key 2/4 MIC");
+		return;
+	}
+	add_note(wt, MSG_DEBUG, "Valid MIC found in EAPOL-Key 2/4");
+
+	if (ie.wpa_ie) {
+		wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key Data - WPA IE",
+			    ie.wpa_ie, ie.wpa_ie_len);
+		if (os_memcmp(ie.wpa_ie, sta->rsnie, ie.wpa_ie_len) != 0) {
+			add_note(wt, MSG_INFO,
+				 "Mismatch in WPA IE between EAPOL-Key 2/4 "
+				 "and (Re)Association Request from " MACSTR,
+				 MAC2STR(sta->addr));
+			wpa_hexdump(MSG_INFO, "WPA IE in EAPOL-Key",
+				    ie.wpa_ie, ie.wpa_ie_len);
+			wpa_hexdump(MSG_INFO, "WPA IE in (Re)Association "
+				    "Request",
+				    sta->rsnie,
+				    sta->rsnie[0] ? 2 + sta->rsnie[1] : 0);
+		}
+	}
+
+	if (ie.rsn_ie) {
+		wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key Data - RSN IE",
+			    ie.rsn_ie, ie.rsn_ie_len);
+		if (os_memcmp(ie.rsn_ie, sta->rsnie, ie.rsn_ie_len) != 0) {
+			add_note(wt, MSG_INFO,
+				 "Mismatch in RSN IE between EAPOL-Key 2/4 "
+				 "and (Re)Association Request from " MACSTR,
+				 MAC2STR(sta->addr));
+			wpa_hexdump(MSG_INFO, "RSN IE in EAPOL-Key",
+				    ie.rsn_ie, ie.rsn_ie_len);
+			wpa_hexdump(MSG_INFO, "RSN IE in (Re)Association "
+				    "Request",
+				    sta->rsnie,
+				    sta->rsnie[0] ? 2 + sta->rsnie[1] : 0);
+		}
+	}
+
+	for (link_id = 0; link_id < MAX_NUM_MLO_LINKS; link_id++) {
+		const u8 *addr;
+
+		if (!ie.mlo_link[link_id])
+			continue;
+		addr = &ie.mlo_link[link_id][RSN_MLO_LINK_KDE_LINK_MAC_INDEX];
+		wpa_printf(MSG_DEBUG,
+			   "Learned Link ID %u MAC address " MACSTR
+			   " from EAPOL-Key 2/4",
+			   link_id, MAC2STR(addr));
+		os_memcpy(sta->link_addr[link_id], addr, ETH_ALEN);
+	}
+}
+
+
+static u8 * decrypt_eapol_key_data_rc4(struct wlantest *wt, const u8 *kek,
+				       const struct wpa_eapol_key *hdr,
+				       const u8 *keydata, u16 keydatalen,
+				       size_t *len)
+{
+	u8 ek[32], *buf;
+
+	buf = os_memdup(keydata, keydatalen);
+	if (buf == NULL)
+		return NULL;
+
+	os_memcpy(ek, hdr->key_iv, 16);
+	os_memcpy(ek + 16, kek, 16);
+	if (rc4_skip(ek, 32, 256, buf, keydatalen)) {
+		add_note(wt, MSG_INFO, "RC4 failed");
+		os_free(buf);
+		return NULL;
+	}
+
+	*len = keydatalen;
+	return buf;
+}
+
+
+static u8 * decrypt_eapol_key_data_aes(struct wlantest *wt, const u8 *kek,
+				       size_t kek_len,
+				       const struct wpa_eapol_key *hdr,
+				       const u8 *keydata, u16 keydatalen,
+				       size_t *len)
+{
+	u8 *buf;
+
+	if (keydatalen % 8) {
+		add_note(wt, MSG_INFO, "Unsupported AES-WRAP len %d",
+			 keydatalen);
+		return NULL;
+	}
+	keydatalen -= 8; /* AES-WRAP adds 8 bytes */
+	buf = os_malloc(keydatalen);
+	if (buf == NULL)
+		return NULL;
+	if (aes_unwrap(kek, kek_len, keydatalen / 8, keydata, buf)) {
+		os_free(buf);
+		add_note(wt, MSG_INFO,
+			 "AES unwrap failed - could not decrypt EAPOL-Key "
+			 "key data");
+		return NULL;
+	}
+
+	*len = keydatalen;
+	return buf;
+}
+
+
+static u8 * decrypt_eapol_key_data(struct wlantest *wt,
+				   struct wlantest_sta *sta, const u8 *kek,
+				   size_t kek_len, u16 ver,
+				   const struct wpa_eapol_key *hdr,
+				   const u8 *end, size_t *len)
+{
+	size_t mic_len;
+	u16 keydatalen;
+	const u8 *mic, *keydata;
+
+	mic = (const u8 *) (hdr + 1);
+	mic_len = determine_mic_len(sta);
+	if (mic_len + 2 > end - mic)
+		return NULL;
+	keydata = mic + mic_len + 2;
+	keydatalen = WPA_GET_BE16(mic + mic_len);
+	if (keydatalen > end - keydata)
+		return NULL;
+
+	switch (ver) {
+	case WPA_KEY_INFO_TYPE_HMAC_MD5_RC4:
+		if (kek_len != 16)
+			return NULL;
+		return decrypt_eapol_key_data_rc4(wt, kek, hdr, keydata,
+						  keydatalen, len);
+	case WPA_KEY_INFO_TYPE_HMAC_SHA1_AES:
+	case WPA_KEY_INFO_TYPE_AES_128_CMAC:
+		return decrypt_eapol_key_data_aes(wt, kek, kek_len, hdr,
+						  keydata, keydatalen, len);
+	case WPA_KEY_INFO_TYPE_AKM_DEFINED:
+		/* For now, assume this is OSEN */
+		return decrypt_eapol_key_data_aes(wt, kek, kek_len, hdr,
+						  keydata, keydatalen, len);
+	default:
+		add_note(wt, MSG_INFO,
+			 "Unsupported EAPOL-Key Key Descriptor Version %u",
+			 ver);
+		return NULL;
+	}
+}
+
+
+static void learn_kde_keys_mlo(struct wlantest *wt, struct wlantest_bss *bss,
+			       struct wlantest_sta *sta, int link_id,
+			       struct wpa_eapol_ie_parse *ie)
+{
+	const u8 *key, *pn;
+	size_t key_len;
+	unsigned int key_id;
+	bool tx;
+
+	if (ie->mlo_gtk[link_id]) {
+		pn = ie->mlo_gtk[link_id] + 1;
+		key = ie->mlo_gtk[link_id] + RSN_MLO_GTK_KDE_PREFIX_LENGTH;
+		key_len = ie->mlo_gtk_len[link_id] -
+			RSN_MLO_GTK_KDE_PREFIX_LENGTH;
+		key_id = ie->mlo_gtk[link_id][0] &
+			RSN_MLO_GTK_KDE_PREFIX0_KEY_ID_MASK;
+		tx = ie->mlo_gtk[link_id][0] & RSN_MLO_GTK_KDE_PREFIX0_TX;
+		if (key_len <= WPA_GTK_MAX_LEN) {
+			add_note(wt, MSG_DEBUG, "GTK KeyID=%u tx=%u",
+				 key_id, tx);
+			if (ie->mlo_gtk[link_id][0] & BIT(3)) {
+				add_note(wt, MSG_INFO,
+					 "MLO GTK KDE: Reserved field set");
+			}
+			wpa_hexdump(MSG_DEBUG, "GTK", key, key_len);
+			bss->gtk_len[key_id] = key_len;
+			if (sta)
+				sta->gtk_len = key_len;
+			os_memcpy(bss->gtk[key_id], key, key_len);
+			if (sta)
+				os_memcpy(sta->gtk, key, key_len);
+			bss->rsc[key_id][0] = pn[5];
+			bss->rsc[key_id][1] = pn[4];
+			bss->rsc[key_id][2] = pn[3];
+			bss->rsc[key_id][3] = pn[2];
+			bss->rsc[key_id][4] = pn[1];
+			bss->rsc[key_id][5] = pn[0];
+			bss->gtk_idx = key_id;
+			if (sta)
+				sta->gtk_idx = key_id;
+			wpa_hexdump(MSG_DEBUG, "RSC", bss->rsc[key_id], 6);
+		} else {
+			add_note(wt, MSG_INFO,
+				 "Invalid MLO GTK KDE key length %zu",
+				 key_len);
+		}
+	}
+
+	if (ie->mlo_igtk[link_id]) {
+		pn = ie->mlo_igtk[link_id] + 2;
+		key = ie->mlo_igtk[link_id] + RSN_MLO_IGTK_KDE_PREFIX_LENGTH;
+		key_len = ie->mlo_igtk_len[link_id] -
+			RSN_MLO_IGTK_KDE_PREFIX_LENGTH;
+		key_id = WPA_GET_LE16(ie->mlo_igtk[link_id]);
+		if (key_len <= WPA_IGTK_MAX_LEN && key_id >= 4 && key_id <= 5) {
+			add_note(wt, MSG_DEBUG, "IGTK KeyID=%u", key_id);
+			if (ie->mlo_igtk[link_id][2 + 6] & 0x0f) {
+				add_note(wt, MSG_INFO,
+					 "MLO IGTK KDE: Reserved field set");
+			}
+			wpa_hexdump(MSG_DEBUG, "IGTK", key, key_len);
+			wpa_hexdump(MSG_DEBUG, "IPN", pn, 6);
+			bss->igtk_len[key_id] = key_len;
+			os_memcpy(bss->igtk[key_id], key, key_len);
+			bss->ipn[key_id][0] = pn[5];
+			bss->ipn[key_id][1] = pn[4];
+			bss->ipn[key_id][2] = pn[3];
+			bss->ipn[key_id][3] = pn[2];
+			bss->ipn[key_id][4] = pn[1];
+			bss->ipn[key_id][5] = pn[0];
+			bss->igtk_idx = key_id;
+		} else {
+			add_note(wt, MSG_INFO,
+				 "Invalid MLO IGTK KDE ID %u or key length %zu",
+				 key_id, key_len);
+		}
+	}
+
+	if (ie->mlo_bigtk[link_id]) {
+		pn = ie->mlo_bigtk[link_id] + 2;
+		key = ie->mlo_bigtk[link_id] + RSN_MLO_BIGTK_KDE_PREFIX_LENGTH;
+		key_len = ie->mlo_bigtk_len[link_id] -
+			RSN_MLO_BIGTK_KDE_PREFIX_LENGTH;
+		key_id = WPA_GET_LE16(ie->mlo_bigtk[link_id]);
+		if (key_len <= WPA_BIGTK_MAX_LEN &&
+		    key_id >= 6 && key_id <= 7) {
+			add_note(wt, MSG_DEBUG, "BIGTK KeyID=%u", key_id);
+			if (ie->mlo_bigtk[link_id][2 + 6] & 0x0f) {
+				add_note(wt, MSG_INFO,
+					 "MLO BIGTK KDE: Reserved field set");
+			}
+			wpa_hexdump(MSG_DEBUG, "BIGTK", key, key_len);
+			wpa_hexdump(MSG_DEBUG, "BIPN", pn, 6);
+			bss->igtk_len[key_id] = key_len;
+			os_memcpy(bss->igtk[key_id], key, key_len);
+			bss->ipn[key_id][0] = pn[5];
+			bss->ipn[key_id][1] = pn[4];
+			bss->ipn[key_id][2] = pn[3];
+			bss->ipn[key_id][3] = pn[2];
+			bss->ipn[key_id][4] = pn[1];
+			bss->ipn[key_id][5] = pn[0];
+			bss->bigtk_idx = key_id;
+		} else {
+			add_note(wt, MSG_INFO,
+				 "Invalid MLO IGTK KDE ID %u or key length %zu",
+				 key_id, key_len);
+		}
+	}
+}
+
+
+static void learn_kde_keys(struct wlantest *wt, struct wlantest_bss *bss,
+			   struct wlantest_sta *sta,
+			   const u8 *buf, size_t len, const u8 *rsc)
+{
+	struct wpa_eapol_ie_parse ie;
+	int link_id;
+
+	if (wpa_parse_kde_ies(buf, len, &ie) < 0) {
+		add_note(wt, MSG_INFO, "Failed to parse EAPOL-Key Key Data");
+		return;
+	}
+
+	if (ie.wpa_ie) {
+		wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key Data - WPA IE",
+			    ie.wpa_ie, ie.wpa_ie_len);
+	}
+
+	if (ie.rsn_ie) {
+		wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key Data - RSN IE",
+			    ie.rsn_ie, ie.rsn_ie_len);
+	}
+
+	if (ie.key_id)
+		add_note(wt, MSG_DEBUG, "KeyID %u", ie.key_id[0]);
+
+	if (ie.gtk) {
+		wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key Data - GTK KDE",
+			    ie.gtk, ie.gtk_len);
+		if (ie.gtk_len >= 2 && ie.gtk_len <= 2 + 32) {
+			int id;
+			id = ie.gtk[0] & 0x03;
+			add_note(wt, MSG_DEBUG, "GTK KeyID=%u tx=%u",
+				 id, !!(ie.gtk[0] & 0x04));
+			if ((ie.gtk[0] & 0xf8) || ie.gtk[1]) {
+				add_note(wt, MSG_INFO,
+					 "GTK KDE: Reserved field set: "
+					 "%02x %02x", ie.gtk[0], ie.gtk[1]);
+			}
+			wpa_hexdump(MSG_DEBUG, "GTK", ie.gtk + 2,
+				    ie.gtk_len - 2);
+			bss->gtk_len[id] = ie.gtk_len - 2;
+			sta->gtk_len = ie.gtk_len - 2;
+			os_memcpy(bss->gtk[id], ie.gtk + 2, ie.gtk_len - 2);
+			os_memcpy(sta->gtk, ie.gtk + 2, ie.gtk_len - 2);
+			bss->rsc[id][0] = rsc[5];
+			bss->rsc[id][1] = rsc[4];
+			bss->rsc[id][2] = rsc[3];
+			bss->rsc[id][3] = rsc[2];
+			bss->rsc[id][4] = rsc[1];
+			bss->rsc[id][5] = rsc[0];
+			bss->gtk_idx = id;
+			sta->gtk_idx = id;
+			wpa_hexdump(MSG_DEBUG, "RSC", bss->rsc[id], 6);
+		} else {
+			add_note(wt, MSG_INFO, "Invalid GTK KDE length %u",
+				 (unsigned) ie.gtk_len);
+		}
+	}
+
+	if (ie.igtk) {
+		wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key Data - IGTK KDE",
+			    ie.igtk, ie.igtk_len);
+		if (ie.igtk_len == 24) {
+			u16 id;
+			id = WPA_GET_LE16(ie.igtk);
+			if (id > 5) {
+				add_note(wt, MSG_INFO,
+					 "Unexpected IGTK KeyID %u", id);
+			} else {
+				const u8 *ipn;
+				add_note(wt, MSG_DEBUG, "IGTK KeyID %u", id);
+				wpa_hexdump(MSG_DEBUG, "IPN", ie.igtk + 2, 6);
+				wpa_hexdump(MSG_DEBUG, "IGTK", ie.igtk + 8,
+					    16);
+				os_memcpy(bss->igtk[id], ie.igtk + 8, 16);
+				bss->igtk_len[id] = 16;
+				ipn = ie.igtk + 2;
+				bss->ipn[id][0] = ipn[5];
+				bss->ipn[id][1] = ipn[4];
+				bss->ipn[id][2] = ipn[3];
+				bss->ipn[id][3] = ipn[2];
+				bss->ipn[id][4] = ipn[1];
+				bss->ipn[id][5] = ipn[0];
+				bss->igtk_idx = id;
+			}
+		} else if (ie.igtk_len == 40) {
+			u16 id;
+			id = WPA_GET_LE16(ie.igtk);
+			if (id > 5) {
+				add_note(wt, MSG_INFO,
+					 "Unexpected IGTK KeyID %u", id);
+			} else {
+				const u8 *ipn;
+				add_note(wt, MSG_DEBUG, "IGTK KeyID %u", id);
+				wpa_hexdump(MSG_DEBUG, "IPN", ie.igtk + 2, 6);
+				wpa_hexdump(MSG_DEBUG, "IGTK", ie.igtk + 8,
+					    32);
+				os_memcpy(bss->igtk[id], ie.igtk + 8, 32);
+				bss->igtk_len[id] = 32;
+				ipn = ie.igtk + 2;
+				bss->ipn[id][0] = ipn[5];
+				bss->ipn[id][1] = ipn[4];
+				bss->ipn[id][2] = ipn[3];
+				bss->ipn[id][3] = ipn[2];
+				bss->ipn[id][4] = ipn[1];
+				bss->ipn[id][5] = ipn[0];
+				bss->igtk_idx = id;
+			}
+		} else {
+			add_note(wt, MSG_INFO, "Invalid IGTK KDE length %u",
+				 (unsigned) ie.igtk_len);
+		}
+	}
+
+	if (ie.bigtk) {
+		wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key Data - BIGTK KDE",
+			    ie.bigtk, ie.bigtk_len);
+		if (ie.bigtk_len == 24) {
+			u16 id;
+
+			id = WPA_GET_LE16(ie.bigtk);
+			if (id < 6 || id > 7) {
+				add_note(wt, MSG_INFO,
+					 "Unexpected BIGTK KeyID %u", id);
+			} else {
+				const u8 *ipn;
+
+				add_note(wt, MSG_DEBUG, "BIGTK KeyID %u", id);
+				wpa_hexdump(MSG_DEBUG, "BIPN", ie.bigtk + 2, 6);
+				wpa_hexdump(MSG_DEBUG, "BIGTK", ie.bigtk + 8,
+					    16);
+				os_memcpy(bss->igtk[id], ie.bigtk + 8, 16);
+				bss->igtk_len[id] = 16;
+				ipn = ie.bigtk + 2;
+				bss->ipn[id][0] = ipn[5];
+				bss->ipn[id][1] = ipn[4];
+				bss->ipn[id][2] = ipn[3];
+				bss->ipn[id][3] = ipn[2];
+				bss->ipn[id][4] = ipn[1];
+				bss->ipn[id][5] = ipn[0];
+				bss->bigtk_idx = id;
+			}
+		} else if (ie.bigtk_len == 40) {
+			u16 id;
+
+			id = WPA_GET_LE16(ie.bigtk);
+			if (id < 6 || id > 7) {
+				add_note(wt, MSG_INFO,
+					 "Unexpected BIGTK KeyID %u", id);
+			} else {
+				const u8 *ipn;
+
+				add_note(wt, MSG_DEBUG, "BIGTK KeyID %u", id);
+				wpa_hexdump(MSG_DEBUG, "BIPN", ie.bigtk + 2, 6);
+				wpa_hexdump(MSG_DEBUG, "BIGTK", ie.bigtk + 8,
+					    32);
+				os_memcpy(bss->igtk[id], ie.bigtk + 8, 32);
+				bss->igtk_len[id] = 32;
+				ipn = ie.bigtk + 2;
+				bss->ipn[id][0] = ipn[5];
+				bss->ipn[id][1] = ipn[4];
+				bss->ipn[id][2] = ipn[3];
+				bss->ipn[id][3] = ipn[2];
+				bss->ipn[id][4] = ipn[1];
+				bss->ipn[id][5] = ipn[0];
+				bss->bigtk_idx = id;
+			}
+		} else {
+			add_note(wt, MSG_INFO, "Invalid BIGTK KDE length %u",
+				 (unsigned) ie.bigtk_len);
+		}
+	}
+
+	for (link_id = 0; link_id < MAX_NUM_MLO_LINKS; link_id++) {
+		const u8 *addr;
+
+		if (!ie.mlo_link[link_id])
+			continue;
+		addr = &ie.mlo_link[link_id][RSN_MLO_LINK_KDE_LINK_MAC_INDEX];
+		if (os_memcmp(addr, bss->bssid, ETH_ALEN) == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "Trying to learn keys for the current MLO link (ID %u)",
+				   link_id);
+			learn_kde_keys_mlo(wt, bss, sta, link_id, &ie);
+		} else {
+			struct wlantest_bss *obss;
+
+			wpa_printf(MSG_DEBUG,
+				   "Trying to learn keys for another MLO link (ID %u addr " MACSTR ")",
+				   link_id, MAC2STR(addr));
+			obss = bss_get(wt, addr);
+			if (!obss) {
+				wpa_printf(MSG_DEBUG,
+					   "No BSS entry for the other BSS found");
+				continue;
+			}
+			learn_kde_keys_mlo(wt, obss, NULL, link_id, &ie);
+		}
+	}
+}
+
+
+static void rx_data_eapol_key_3_of_4(struct wlantest *wt, const u8 *dst,
+				     const u8 *src, const u8 *bssid,
+				     const u8 *data, size_t len)
+{
+	struct wlantest_bss *bss, *bss_mld;
+	struct wlantest_sta *sta;
+	const struct ieee802_1x_hdr *eapol;
+	const struct wpa_eapol_key *hdr;
+	const u8 *key_data, *kck, *kek, *mic;
+	size_t kck_len, kek_len, mic_len;
+	int recalc = 0;
+	u16 key_info, ver;
+	u8 *decrypted_buf = NULL;
+	const u8 *decrypted;
+	size_t decrypted_len = 0;
+	struct wpa_eapol_ie_parse ie;
+	struct wpa_ie_data rsn;
+	const u8 *rsne;
+	size_t rsne_len;
+	int link_id;
+
+	wpa_printf(MSG_DEBUG, "EAPOL-Key 3/4 " MACSTR " -> " MACSTR " (BSSID "
+		   MACSTR ")",
+		   MAC2STR(src), MAC2STR(dst), MAC2STR(bssid));
+	if (os_memcmp(src, bssid, ETH_ALEN) == 0) {
+		bss = bss_get(wt, src);
+	} else {
+		bss = bss_find(wt, bssid);
+		bss_mld = bss_find(wt, src);
+		if (bss_mld && (!bss || sta_find(bss_mld, src)))
+			bss = bss_get(wt, src);
+		else
+			bss = bss_get(wt, bssid);
+	}
+	if (bss == NULL)
+		return;
+	sta = sta_get(bss, dst);
+	if (sta == NULL)
+		return;
+	mic_len = determine_mic_len(sta);
+
+	eapol = (const struct ieee802_1x_hdr *) data;
+	hdr = (const struct wpa_eapol_key *) (eapol + 1);
+	mic = (const u8 *) (hdr + 1);
+	key_info = WPA_GET_BE16(hdr->key_info);
+
+	if (os_memcmp(sta->anonce, hdr->key_nonce, WPA_NONCE_LEN) != 0) {
+		add_note(wt, MSG_INFO,
+			 "EAPOL-Key ANonce mismatch between 1/4 and 3/4");
+		recalc = 1;
+	}
+	os_memcpy(sta->anonce, hdr->key_nonce, WPA_NONCE_LEN);
+	if (recalc) {
+		derive_ptk(wt, bss, sta, key_info & WPA_KEY_INFO_TYPE_MASK,
+			   data, len);
+	}
+
+	if (!sta->ptk_set && !sta->tptk_set) {
+		add_note(wt, MSG_DEBUG,
+			 "No PTK known to process EAPOL-Key 3/4");
+		return;
+	}
+
+	kek = sta->ptk.kek;
+	kek_len = sta->ptk.kek_len;
+	kck = sta->ptk.kck;
+	kck_len = sta->ptk.kck_len;
+	if (sta->tptk_set) {
+		add_note(wt, MSG_DEBUG,
+			 "Use TPTK for validation EAPOL-Key MIC");
+		kck = sta->tptk.kck;
+		kck_len = sta->tptk.kck_len;
+		kek = sta->tptk.kek;
+		kek_len = sta->tptk.kek_len;
+	}
+	if (check_mic(sta, kck, kck_len,
+		      key_info & WPA_KEY_INFO_TYPE_MASK, data, len) < 0) {
+		add_note(wt, MSG_INFO, "Mismatch in EAPOL-Key 3/4 MIC");
+		return;
+	}
+	add_note(wt, MSG_DEBUG, "Valid MIC found in EAPOL-Key 3/4");
+
+	key_data = mic + mic_len + 2;
+	if (!(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+		if (sta->proto & WPA_PROTO_RSN)
+			add_note(wt, MSG_INFO,
+				 "EAPOL-Key 3/4 without EncrKeyData bit");
+		decrypted = key_data;
+		decrypted_len = WPA_GET_BE16(mic + mic_len);
+	} else {
+		ver = key_info & WPA_KEY_INFO_TYPE_MASK;
+		decrypted_buf = decrypt_eapol_key_data(wt, sta,
+						       kek, kek_len, ver,
+						       hdr, data + len,
+						       &decrypted_len);
+		if (decrypted_buf == NULL) {
+			add_note(wt, MSG_INFO,
+				 "Failed to decrypt EAPOL-Key Key Data");
+			return;
+		}
+		decrypted = decrypted_buf;
+		wpa_hexdump(MSG_DEBUG, "Decrypted EAPOL-Key Key Data",
+			    decrypted, decrypted_len);
+	}
+	if ((wt->write_pcap_dumper || wt->pcapng) && decrypted != key_data) {
+		/* Fill in a stub Data frame header */
+		u8 buf[24 + 8 + sizeof(*eapol) + sizeof(*hdr) + 64];
+		struct ieee80211_hdr *h;
+		struct wpa_eapol_key *k;
+		const u8 *p;
+		u8 *pos;
+		size_t plain_len;
+
+		plain_len = decrypted_len;
+		p = decrypted;
+		while (p + 1 < decrypted + decrypted_len) {
+			if (p[0] == 0xdd && p[1] == 0x00) {
+				/* Remove padding */
+				plain_len = p - decrypted;
+				p = NULL;
+				break;
+			}
+			p += 2 + p[1];
+		}
+		if (p && p > decrypted && p + 1 == decrypted + decrypted_len &&
+		    *p == 0xdd) {
+			/* Remove padding */
+			plain_len = p - decrypted;
+		}
+
+		os_memset(buf, 0, sizeof(buf));
+		h = (struct ieee80211_hdr *) buf;
+		h->frame_control = host_to_le16(0x0208);
+		os_memcpy(h->addr1, dst, ETH_ALEN);
+		os_memcpy(h->addr2, src, ETH_ALEN);
+		os_memcpy(h->addr3, src, ETH_ALEN);
+		pos = (u8 *) (h + 1);
+		os_memcpy(pos, "\xaa\xaa\x03\x00\x00\x00\x88\x8e", 8);
+		pos += 8;
+		os_memcpy(pos, eapol, sizeof(*eapol));
+		pos += sizeof(*eapol);
+		os_memcpy(pos, hdr, sizeof(*hdr) + mic_len);
+		k = (struct wpa_eapol_key *) pos;
+		pos += sizeof(struct wpa_eapol_key) + mic_len;
+		WPA_PUT_BE16(k->key_info,
+			     key_info & ~WPA_KEY_INFO_ENCR_KEY_DATA);
+		WPA_PUT_BE16(pos, plain_len);
+		write_pcap_decrypted(wt, buf, 24 + 8 + sizeof(*eapol) +
+				     sizeof(*hdr) + mic_len + 2,
+				     decrypted, plain_len);
+	}
+
+	if (wpa_parse_kde_ies(decrypted, decrypted_len, &ie) < 0) {
+		add_note(wt, MSG_INFO, "Failed to parse EAPOL-Key Key Data");
+		os_free(decrypted_buf);
+		return;
+	}
+
+	if (!bss->ies_set) {
+		struct ieee802_11_elems elems;
+
+		elems_from_eapol_ie(&elems, &ie);
+		wpa_printf(MSG_DEBUG,
+			   "Update BSS data based on IEs in EAPOL-Key 3/4");
+		bss_update(wt, bss, &elems, 0);
+	}
+
+	if ((ie.wpa_ie &&
+	     os_memcmp(ie.wpa_ie, bss->wpaie, ie.wpa_ie_len) != 0) ||
+	    (ie.wpa_ie == NULL && bss->wpaie[0])) {
+		add_note(wt, MSG_INFO,
+			 "Mismatch in WPA IE between EAPOL-Key 3/4 and "
+			 "Beacon/Probe Response from " MACSTR,
+			 MAC2STR(bss->bssid));
+		wpa_hexdump(MSG_INFO, "WPA IE in EAPOL-Key",
+			    ie.wpa_ie, ie.wpa_ie_len);
+		wpa_hexdump(MSG_INFO, "WPA IE in Beacon/Probe "
+			    "Response",
+			    bss->wpaie,
+			    bss->wpaie[0] ? 2 + bss->wpaie[1] : 0);
+	}
+
+	rsne = ie.rsn_ie;
+	rsne_len = ie.rsn_ie_len;
+	for (link_id = 0; !rsne && link_id < MAX_NUM_MLO_LINKS; link_id++) {
+		const u8 *addr, *pos, *end;
+
+		if (!ie.mlo_link[link_id])
+			continue;
+		addr = &ie.mlo_link[link_id][RSN_MLO_LINK_KDE_LINK_MAC_INDEX];
+		if (os_memcmp(addr, bss->bssid, ETH_ALEN) != 0)
+			continue;
+		if (!(ie.mlo_link[link_id][0] & RSN_MLO_LINK_KDE_LI_RSNE_INFO))
+			continue;
+		pos = ie.mlo_link[link_id] + RSN_MLO_LINK_KDE_FIXED_LENGTH;
+		end = ie.mlo_link[link_id] + ie.mlo_link_len[link_id];
+		if (end - pos < 2 || pos[0] != WLAN_EID_RSN ||
+		    end - pos < 2 + pos[1]) {
+			add_note(wt, MSG_INFO, "Invalid MLO Link KDE from "
+				 MACSTR " - RSNE info missing",
+				 MAC2STR(bss->bssid));
+			break;
+		}
+		wpa_printf(MSG_DEBUG,
+			   "Using RSNE from MLO Link KDE for Link ID %u",
+			   link_id);
+		rsne = pos;
+		rsne_len = 2 + pos[1];
+		break;
+	}
+
+	if ((rsne &&
+	     wpa_compare_rsn_ie(wpa_key_mgmt_ft(sta->key_mgmt),
+				rsne, rsne_len,
+				bss->rsnie, 2 + bss->rsnie[1])) ||
+	    (!rsne && bss->rsnie[0])) {
+		add_note(wt, MSG_INFO, "Mismatch in RSN IE between EAPOL-Key "
+			 "3/4 and Beacon/Probe Response from " MACSTR,
+			 MAC2STR(bss->bssid));
+		wpa_hexdump(MSG_INFO, "RSN IE in EAPOL-Key",
+			    rsne, rsne_len);
+		wpa_hexdump(MSG_INFO, "RSN IE in Beacon/Probe Response",
+			    bss->rsnie,
+			    bss->rsnie[0] ? 2 + bss->rsnie[1] : 0);
+	}
+
+	if (wpa_key_mgmt_ft(sta->key_mgmt) &&
+	    (wpa_parse_wpa_ie_rsn(rsne, rsne_len, &rsn) < 0 ||
+	     rsn.num_pmkid != 1 || !rsn.pmkid ||
+	     os_memcmp_const(rsn.pmkid, sta->pmk_r1_name,
+			     WPA_PMK_NAME_LEN) != 0))
+		add_note(wt, MSG_INFO,
+			 "FT: No matching PMKR1Name in FT 4-way handshake message 3/4");
+
+	/* TODO: validate MDE and FTE match */
+
+	learn_kde_keys(wt, bss, sta, decrypted, decrypted_len, hdr->key_rsc);
+	os_free(decrypted_buf);
+}
+
+
+static void rx_data_eapol_key_4_of_4(struct wlantest *wt, const u8 *dst,
+				     const u8 *src, const u8 *bssid,
+				     const u8 *data, size_t len)
+{
+	struct wlantest_bss *bss, *bss_mld;
+	struct wlantest_sta *sta;
+	const struct ieee802_1x_hdr *eapol;
+	const struct wpa_eapol_key *hdr;
+	u16 key_info;
+	const u8 *kck;
+	size_t kck_len;
+
+	wpa_printf(MSG_DEBUG, "EAPOL-Key 4/4 " MACSTR " -> " MACSTR " (BSSID "
+		   MACSTR ")",
+		   MAC2STR(src), MAC2STR(dst), MAC2STR(bssid));
+	if (os_memcmp(dst, bssid, ETH_ALEN) == 0) {
+		bss = bss_get(wt, dst);
+	} else {
+		bss = bss_find(wt, bssid);
+		bss_mld = bss_find(wt, dst);
+		if (bss_mld && (!bss || sta_find(bss_mld, src)))
+			bss = bss_get(wt, dst);
+		else
+			bss = bss_get(wt, bssid);
+	}
+	if (bss == NULL)
+		return;
+	sta = sta_get(bss, src);
+	if (sta == NULL)
+		return;
+
+	eapol = (const struct ieee802_1x_hdr *) data;
+	hdr = (const struct wpa_eapol_key *) (eapol + 1);
+	if (!is_zero(hdr->key_rsc, 8)) {
+		add_note(wt, MSG_INFO, "EAPOL-Key 4/4 from " MACSTR " used "
+			 "non-zero Key RSC", MAC2STR(src));
+	}
+	key_info = WPA_GET_BE16(hdr->key_info);
+
+	if (!sta->ptk_set && !sta->tptk_set) {
+		add_note(wt, MSG_DEBUG,
+			 "No PTK known to process EAPOL-Key 4/4");
+		return;
+	}
+
+	kck = sta->ptk.kck;
+	kck_len = sta->ptk.kck_len;
+	if (sta->tptk_set) {
+		add_note(wt, MSG_DEBUG,
+			 "Use TPTK for validation EAPOL-Key MIC");
+		kck = sta->tptk.kck;
+		kck_len = sta->tptk.kck_len;
+	}
+	if (check_mic(sta, kck, kck_len,
+		      key_info & WPA_KEY_INFO_TYPE_MASK, data, len) < 0) {
+		add_note(wt, MSG_INFO, "Mismatch in EAPOL-Key 4/4 MIC");
+		return;
+	}
+	add_note(wt, MSG_DEBUG, "Valid MIC found in EAPOL-Key 4/4");
+	if (sta->tptk_set) {
+		add_note(wt, MSG_DEBUG, "Update PTK (rekeying)");
+		os_memcpy(&sta->ptk, &sta->tptk, sizeof(sta->ptk));
+		sta->ptk_set = 1;
+		sta->tptk_set = 0;
+		os_memset(sta->rsc_tods, 0, sizeof(sta->rsc_tods));
+		os_memset(sta->rsc_fromds, 0, sizeof(sta->rsc_fromds));
+	}
+}
+
+
+static void rx_data_eapol_key_1_of_2(struct wlantest *wt, const u8 *dst,
+				     const u8 *src, const u8 *bssid,
+				     const u8 *data, size_t len)
+{
+	struct wlantest_bss *bss, *bss_mld;
+	struct wlantest_sta *sta;
+	const struct ieee802_1x_hdr *eapol;
+	const struct wpa_eapol_key *hdr;
+	u16 key_info, ver;
+	u8 *decrypted;
+	size_t decrypted_len = 0;
+	size_t mic_len;
+
+	wpa_printf(MSG_DEBUG, "EAPOL-Key 1/2 " MACSTR " -> " MACSTR " (BSSID "
+		   MACSTR ")",
+		   MAC2STR(src), MAC2STR(dst), MAC2STR(bssid));
+	if (os_memcmp(src, bssid, ETH_ALEN) == 0) {
+		bss = bss_get(wt, src);
+	} else {
+		bss = bss_find(wt, bssid);
+		bss_mld = bss_find(wt, src);
+		if (bss_mld && (!bss || sta_find(bss_mld, src)))
+			bss = bss_get(wt, src);
+		else
+			bss = bss_get(wt, bssid);
+	}
+	if (bss == NULL)
+		return;
+	sta = sta_get(bss, dst);
+	if (sta == NULL)
+		return;
+	mic_len = determine_mic_len(sta);
+
+	eapol = (const struct ieee802_1x_hdr *) data;
+	hdr = (const struct wpa_eapol_key *) (eapol + 1);
+	key_info = WPA_GET_BE16(hdr->key_info);
+
+	if (!sta->ptk_set) {
+		add_note(wt, MSG_DEBUG,
+			 "No PTK known to process EAPOL-Key 1/2");
+		return;
+	}
+
+	if (sta->ptk_set &&
+	    check_mic(sta, sta->ptk.kck, sta->ptk.kck_len,
+		      key_info & WPA_KEY_INFO_TYPE_MASK,
+		      data, len) < 0) {
+		add_note(wt, MSG_INFO, "Mismatch in EAPOL-Key 1/2 MIC");
+		return;
+	}
+	add_note(wt, MSG_DEBUG, "Valid MIC found in EAPOL-Key 1/2");
+
+	if (sta->proto & WPA_PROTO_RSN &&
+	    !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+		add_note(wt, MSG_INFO, "EAPOL-Key 1/2 without EncrKeyData bit");
+		return;
+	}
+	ver = key_info & WPA_KEY_INFO_TYPE_MASK;
+	decrypted = decrypt_eapol_key_data(wt, sta,
+					   sta->ptk.kek, sta->ptk.kek_len,
+					   ver, hdr, data + len,
+					   &decrypted_len);
+	if (decrypted == NULL) {
+		add_note(wt, MSG_INFO, "Failed to decrypt EAPOL-Key Key Data");
+		return;
+	}
+	wpa_hexdump(MSG_DEBUG, "Decrypted EAPOL-Key Key Data",
+		    decrypted, decrypted_len);
+	if (wt->write_pcap_dumper || wt->pcapng) {
+		/* Fill in a stub Data frame header */
+		u8 buf[24 + 8 + sizeof(*eapol) + sizeof(*hdr) + 64];
+		struct ieee80211_hdr *h;
+		struct wpa_eapol_key *k;
+		u8 *pos;
+		size_t plain_len;
+
+		plain_len = decrypted_len;
+		pos = decrypted;
+		while (pos + 1 < decrypted + decrypted_len) {
+			if (pos[0] == 0xdd && pos[1] == 0x00) {
+				/* Remove padding */
+				plain_len = pos - decrypted;
+				break;
+			}
+			pos += 2 + pos[1];
+		}
+
+		os_memset(buf, 0, sizeof(buf));
+		h = (struct ieee80211_hdr *) buf;
+		h->frame_control = host_to_le16(0x0208);
+		os_memcpy(h->addr1, dst, ETH_ALEN);
+		os_memcpy(h->addr2, src, ETH_ALEN);
+		os_memcpy(h->addr3, src, ETH_ALEN);
+		pos = (u8 *) (h + 1);
+		os_memcpy(pos, "\xaa\xaa\x03\x00\x00\x00\x88\x8e", 8);
+		pos += 8;
+		os_memcpy(pos, eapol, sizeof(*eapol));
+		pos += sizeof(*eapol);
+		os_memcpy(pos, hdr, sizeof(*hdr) + mic_len);
+		k = (struct wpa_eapol_key *) pos;
+		pos += sizeof(struct wpa_eapol_key) + mic_len;
+		WPA_PUT_BE16(k->key_info,
+			     key_info & ~WPA_KEY_INFO_ENCR_KEY_DATA);
+		WPA_PUT_BE16(pos, plain_len);
+		write_pcap_decrypted(wt, buf, 24 + 8 + sizeof(*eapol) +
+				     sizeof(*hdr) + mic_len + 2,
+				     decrypted, plain_len);
+	}
+	if (sta->proto & WPA_PROTO_RSN)
+		learn_kde_keys(wt, bss, sta, decrypted, decrypted_len,
+			       hdr->key_rsc);
+	else {
+		int klen = bss->group_cipher == WPA_CIPHER_TKIP ? 32 : 16;
+		if (decrypted_len == klen) {
+			const u8 *rsc = hdr->key_rsc;
+			int id;
+			id = (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >>
+				WPA_KEY_INFO_KEY_INDEX_SHIFT;
+			add_note(wt, MSG_DEBUG, "GTK key index %d", id);
+			wpa_hexdump(MSG_DEBUG, "GTK", decrypted,
+				    decrypted_len);
+			bss->gtk_len[id] = decrypted_len;
+			os_memcpy(bss->gtk[id], decrypted, decrypted_len);
+			bss->rsc[id][0] = rsc[5];
+			bss->rsc[id][1] = rsc[4];
+			bss->rsc[id][2] = rsc[3];
+			bss->rsc[id][3] = rsc[2];
+			bss->rsc[id][4] = rsc[1];
+			bss->rsc[id][5] = rsc[0];
+			wpa_hexdump(MSG_DEBUG, "RSC", bss->rsc[id], 6);
+		} else {
+			add_note(wt, MSG_INFO, "Unexpected WPA Key Data length "
+				 "in Group Key msg 1/2 from " MACSTR,
+				 MAC2STR(src));
+		}
+	}
+	os_free(decrypted);
+}
+
+
+static void rx_data_eapol_key_2_of_2(struct wlantest *wt, const u8 *dst,
+				     const u8 *src, const u8 *bssid,
+				     const u8 *data, size_t len)
+{
+	struct wlantest_bss *bss, *bss_mld;
+	struct wlantest_sta *sta;
+	const struct ieee802_1x_hdr *eapol;
+	const struct wpa_eapol_key *hdr;
+	u16 key_info;
+
+	wpa_printf(MSG_DEBUG, "EAPOL-Key 2/2 " MACSTR " -> " MACSTR " (BSSID "
+		   MACSTR ")",
+		   MAC2STR(src), MAC2STR(dst), MAC2STR(bssid));
+	if (os_memcmp(dst, bssid, ETH_ALEN) == 0) {
+		bss = bss_get(wt, dst);
+	} else {
+		bss = bss_find(wt, bssid);
+		bss_mld = bss_find(wt, dst);
+		if (bss_mld && (!bss || sta_find(bss_mld, src)))
+			bss = bss_get(wt, dst);
+		else
+			bss = bss_get(wt, bssid);
+	}
+	if (bss == NULL)
+		return;
+	sta = sta_get(bss, src);
+	if (sta == NULL)
+		return;
+
+	eapol = (const struct ieee802_1x_hdr *) data;
+	hdr = (const struct wpa_eapol_key *) (eapol + 1);
+	if (!is_zero(hdr->key_rsc, 8)) {
+		add_note(wt, MSG_INFO, "EAPOL-Key 2/2 from " MACSTR " used "
+			 "non-zero Key RSC", MAC2STR(src));
+	}
+	key_info = WPA_GET_BE16(hdr->key_info);
+
+	if (!sta->ptk_set) {
+		add_note(wt, MSG_DEBUG,
+			 "No PTK known to process EAPOL-Key 2/2");
+		return;
+	}
+
+	if (sta->ptk_set &&
+	    check_mic(sta, sta->ptk.kck, sta->ptk.kck_len,
+		      key_info & WPA_KEY_INFO_TYPE_MASK,
+		      data, len) < 0) {
+		add_note(wt, MSG_INFO, "Mismatch in EAPOL-Key 2/2 MIC");
+		return;
+	}
+	add_note(wt, MSG_DEBUG, "Valid MIC found in EAPOL-Key 2/2");
+}
+
+
+static void rx_data_eapol_key(struct wlantest *wt, const u8 *bssid,
+			      const u8 *sta_addr, const u8 *dst,
+			      const u8 *src, const u8 *data, size_t len,
+			      int prot)
+{
+	const struct ieee802_1x_hdr *eapol;
+	const struct wpa_eapol_key *hdr;
+	const u8 *key_data;
+	u16 key_info, key_length, ver, key_data_length;
+	size_t mic_len = 16;
+	const u8 *mic;
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+
+	bss = bss_get(wt, bssid);
+	if (bss) {
+		if (sta_addr)
+			sta = sta_get(bss, sta_addr);
+		else
+			sta = NULL;
+		mic_len = determine_mic_len(sta);
+	}
+
+	eapol = (const struct ieee802_1x_hdr *) data;
+	hdr = (const struct wpa_eapol_key *) (eapol + 1);
+
+	wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key",
+		    (const u8 *) hdr, len - sizeof(*eapol));
+	if (len < sizeof(*hdr) + mic_len + 2) {
+		add_note(wt, MSG_INFO, "Too short EAPOL-Key frame from " MACSTR,
+			 MAC2STR(src));
+		return;
+	}
+	mic = (const u8 *) (hdr + 1);
+
+	if (hdr->type == EAPOL_KEY_TYPE_RC4) {
+		/* TODO: EAPOL-Key RC4 for WEP */
+		wpa_printf(MSG_INFO, "EAPOL-Key Descriptor Type RC4 from "
+			   MACSTR, MAC2STR(src));
+		return;
+	}
+
+	if (hdr->type != EAPOL_KEY_TYPE_RSN &&
+	    hdr->type != EAPOL_KEY_TYPE_WPA) {
+		wpa_printf(MSG_INFO, "Unsupported EAPOL-Key Descriptor Type "
+			   "%u from " MACSTR, hdr->type, MAC2STR(src));
+		return;
+	}
+
+	key_info = WPA_GET_BE16(hdr->key_info);
+	key_length = WPA_GET_BE16(hdr->key_length);
+	key_data_length = WPA_GET_BE16(mic + mic_len);
+	key_data = mic + mic_len + 2;
+	if (key_data + key_data_length > data + len) {
+		add_note(wt, MSG_INFO, "Truncated EAPOL-Key from " MACSTR,
+			 MAC2STR(src));
+		return;
+	}
+	if (key_data + key_data_length < data + len) {
+		wpa_hexdump(MSG_DEBUG, "Extra data after EAPOL-Key Key Data "
+			    "field", key_data + key_data_length,
+			data + len - key_data - key_data_length);
+	}
+
+
+	ver = key_info & WPA_KEY_INFO_TYPE_MASK;
+	wpa_printf(MSG_DEBUG, "EAPOL-Key ver=%u %c idx=%u%s%s%s%s%s%s%s%s "
+		   "datalen=%u",
+		   ver, key_info & WPA_KEY_INFO_KEY_TYPE ? 'P' : 'G',
+		   (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >>
+		   WPA_KEY_INFO_KEY_INDEX_SHIFT,
+		   (key_info & WPA_KEY_INFO_INSTALL) ? " Install" : "",
+		   (key_info & WPA_KEY_INFO_ACK) ? " ACK" : "",
+		   (key_info & WPA_KEY_INFO_MIC) ? " MIC" : "",
+		   (key_info & WPA_KEY_INFO_SECURE) ? " Secure" : "",
+		   (key_info & WPA_KEY_INFO_ERROR) ? " Error" : "",
+		   (key_info & WPA_KEY_INFO_REQUEST) ? " Request" : "",
+		   (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) ? " Encr" : "",
+		   (key_info & WPA_KEY_INFO_SMK_MESSAGE) ? " SMK" : "",
+		   key_data_length);
+
+	if (ver != WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 &&
+	    ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES &&
+	    ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
+	    ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
+		wpa_printf(MSG_INFO, "Unsupported EAPOL-Key Key Descriptor "
+			   "Version %u from " MACSTR, ver, MAC2STR(src));
+		return;
+	}
+
+	wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Replay Counter",
+		    hdr->replay_counter, WPA_REPLAY_COUNTER_LEN);
+	wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key Nonce",
+		    hdr->key_nonce, WPA_NONCE_LEN);
+	wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key IV",
+		    hdr->key_iv, 16);
+	wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key RSC",
+		    hdr->key_rsc, WPA_KEY_RSC_LEN);
+	wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key MIC",
+		    mic, mic_len);
+	wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key Data",
+		    key_data, key_data_length);
+
+	if (hdr->type == EAPOL_KEY_TYPE_RSN &&
+	    (key_info & (WPA_KEY_INFO_KEY_INDEX_MASK | BIT(14) | BIT(15))) !=
+	    0) {
+		wpa_printf(MSG_INFO, "RSN EAPOL-Key with non-zero reserved "
+			   "Key Info bits 0x%x from " MACSTR,
+			   key_info, MAC2STR(src));
+	}
+
+	if (hdr->type == EAPOL_KEY_TYPE_WPA &&
+	    (key_info & (WPA_KEY_INFO_ENCR_KEY_DATA |
+			 WPA_KEY_INFO_SMK_MESSAGE |BIT(14) | BIT(15))) != 0) {
+		wpa_printf(MSG_INFO, "WPA EAPOL-Key with non-zero reserved "
+			   "Key Info bits 0x%x from " MACSTR,
+			   key_info, MAC2STR(src));
+	}
+
+	if (key_length > 32) {
+		wpa_printf(MSG_INFO, "EAPOL-Key with invalid Key Length %d "
+			   "from " MACSTR, key_length, MAC2STR(src));
+	}
+
+	if (ver != WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 &&
+	    !is_zero(hdr->key_iv, 16)) {
+		wpa_printf(MSG_INFO, "EAPOL-Key with non-zero Key IV "
+			   "(reserved with ver=%d) field from " MACSTR,
+			   ver, MAC2STR(src));
+		wpa_hexdump(MSG_INFO, "EAPOL-Key Key IV (reserved)",
+			    hdr->key_iv, 16);
+	}
+
+	if (!is_zero(hdr->key_id, 8)) {
+		wpa_printf(MSG_INFO, "EAPOL-Key with non-zero Key ID "
+			   "(reserved) field from " MACSTR, MAC2STR(src));
+		wpa_hexdump(MSG_INFO, "EAPOL-Key Key ID (reserved)",
+			    hdr->key_id, 8);
+	}
+
+	if (hdr->key_rsc[6] || hdr->key_rsc[7]) {
+		wpa_printf(MSG_INFO, "EAPOL-Key with non-zero Key RSC octets "
+			   "(last two are unused)" MACSTR, MAC2STR(src));
+	}
+
+	if (key_info & (WPA_KEY_INFO_ERROR | WPA_KEY_INFO_REQUEST))
+		return;
+
+	if (key_info & WPA_KEY_INFO_SMK_MESSAGE)
+		return;
+
+	if (key_info & WPA_KEY_INFO_KEY_TYPE) {
+		/* 4-Way Handshake */
+		switch (key_info & (WPA_KEY_INFO_SECURE |
+				    WPA_KEY_INFO_MIC |
+				    WPA_KEY_INFO_ACK |
+				    WPA_KEY_INFO_INSTALL)) {
+		case WPA_KEY_INFO_ACK:
+			rx_data_eapol_key_1_of_4(wt, dst, src, bssid,
+						 data, len);
+			break;
+		case WPA_KEY_INFO_MIC:
+			if (key_data_length == 0 ||
+			    is_zero(hdr->key_nonce, WPA_NONCE_LEN))
+				rx_data_eapol_key_4_of_4(wt, dst, src, bssid,
+							 data, len);
+			else
+				rx_data_eapol_key_2_of_4(wt, dst, src, bssid,
+							 data, len);
+			break;
+		case WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK |
+			WPA_KEY_INFO_INSTALL:
+			/* WPA does not include Secure bit in 3/4 */
+			rx_data_eapol_key_3_of_4(wt, dst, src, bssid,
+						 data, len);
+			break;
+		case WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
+			WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL:
+		case WPA_KEY_INFO_SECURE |
+			WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL:
+			rx_data_eapol_key_3_of_4(wt, dst, src, bssid,
+						 data, len);
+			break;
+		case WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC:
+		case WPA_KEY_INFO_SECURE:
+			if (key_data_length == 0 ||
+			    is_zero(hdr->key_nonce, WPA_NONCE_LEN))
+				rx_data_eapol_key_4_of_4(wt, dst, src, bssid,
+							 data, len);
+			else
+				rx_data_eapol_key_2_of_4(wt, dst, src, bssid,
+							 data, len);
+			break;
+		default:
+			wpa_printf(MSG_DEBUG, "Unsupported EAPOL-Key frame");
+			break;
+		}
+	} else {
+		/* Group Key Handshake */
+		switch (key_info & (WPA_KEY_INFO_SECURE |
+				    WPA_KEY_INFO_MIC |
+				    WPA_KEY_INFO_ACK)) {
+		case WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
+			WPA_KEY_INFO_ACK:
+		case WPA_KEY_INFO_SECURE | WPA_KEY_INFO_ACK:
+			rx_data_eapol_key_1_of_2(wt, dst, src, bssid,
+						 data, len);
+			break;
+		case WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC:
+		case WPA_KEY_INFO_SECURE:
+			rx_data_eapol_key_2_of_2(wt, dst, src, bssid,
+						 data, len);
+			break;
+		default:
+			wpa_printf(MSG_DEBUG, "Unsupported EAPOL-Key frame");
+			break;
+		}
+	}
+}
+
+
+void rx_data_eapol(struct wlantest *wt, const u8 *bssid, const u8 *sta_addr,
+		   const u8 *dst, const u8 *src,
+		   const u8 *data, size_t len, int prot)
+{
+	const struct ieee802_1x_hdr *hdr;
+	u16 length;
+	const u8 *p;
+
+	wpa_hexdump(MSG_EXCESSIVE, "EAPOL", data, len);
+	if (len < sizeof(*hdr)) {
+		wpa_printf(MSG_INFO, "Too short EAPOL frame from " MACSTR,
+			   MAC2STR(src));
+		return;
+	}
+
+	hdr = (const struct ieee802_1x_hdr *) data;
+	length = be_to_host16(hdr->length);
+	wpa_printf(MSG_DEBUG, "RX EAPOL: " MACSTR " -> " MACSTR "%s ver=%u "
+		   "type=%u len=%u",
+		   MAC2STR(src), MAC2STR(dst), prot ? " Prot" : "",
+		   hdr->version, hdr->type, length);
+	if (hdr->version < 1 || hdr->version > 3) {
+		wpa_printf(MSG_INFO, "Unexpected EAPOL version %u from "
+			   MACSTR, hdr->version, MAC2STR(src));
+	}
+	if (sizeof(*hdr) + length > len) {
+		wpa_printf(MSG_INFO, "Truncated EAPOL frame from " MACSTR,
+			   MAC2STR(src));
+		return;
+	}
+
+	if (sizeof(*hdr) + length < len) {
+		wpa_printf(MSG_INFO, "EAPOL frame with %d extra bytes",
+			   (int) (len - sizeof(*hdr) - length));
+	}
+	p = (const u8 *) (hdr + 1);
+
+	switch (hdr->type) {
+	case IEEE802_1X_TYPE_EAP_PACKET:
+		wpa_hexdump(MSG_MSGDUMP, "EAPOL - EAP packet", p, length);
+		break;
+	case IEEE802_1X_TYPE_EAPOL_START:
+		wpa_hexdump(MSG_MSGDUMP, "EAPOL-Start", p, length);
+		break;
+	case IEEE802_1X_TYPE_EAPOL_LOGOFF:
+		wpa_hexdump(MSG_MSGDUMP, "EAPOL-Logoff", p, length);
+		break;
+	case IEEE802_1X_TYPE_EAPOL_KEY:
+		rx_data_eapol_key(wt, bssid, sta_addr, dst, src, data,
+				  sizeof(*hdr) + length, prot);
+		break;
+	case IEEE802_1X_TYPE_EAPOL_ENCAPSULATED_ASF_ALERT:
+		wpa_hexdump(MSG_MSGDUMP, "EAPOL - Encapsulated ASF alert",
+			    p, length);
+		break;
+	default:
+		wpa_hexdump(MSG_MSGDUMP, "Unknown EAPOL payload", p, length);
+		break;
+	}
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/rx_ip.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/rx_ip.c
new file mode 100644
index 0000000..b0fdd20
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/rx_ip.c
@@ -0,0 +1,184 @@
+/*
+ * Received Data frame processing for IPv4 packets
+ * Copyright (c) 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 <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+
+#include "utils/common.h"
+#include "wlantest.h"
+
+
+#ifndef __APPLE__
+
+static void ping_update(struct wlantest *wt, struct wlantest_sta *sta, int req,
+			u32 src, u32 dst, u16 id, u16 seq)
+{
+	if (req) {
+		sta->icmp_echo_req_src = src;
+		sta->icmp_echo_req_dst = dst;
+		sta->icmp_echo_req_id = id;
+		sta->icmp_echo_req_seq = seq;
+		return;
+	}
+
+	if (sta->icmp_echo_req_src == dst &&
+	    sta->icmp_echo_req_dst == src &&
+	    sta->icmp_echo_req_id == id &&
+	    sta->icmp_echo_req_seq == seq) {
+		sta->counters[WLANTEST_STA_COUNTER_PING_OK]++;
+		if (sta->counters[WLANTEST_STA_COUNTER_ASSOCREQ_TX] == 0 &&
+		    sta->counters[WLANTEST_STA_COUNTER_REASSOCREQ_TX] == 0)
+			sta->counters[
+				WLANTEST_STA_COUNTER_PING_OK_FIRST_ASSOC]++;
+		add_note(wt, MSG_DEBUG, "ICMP echo (ping) match for STA "
+			 MACSTR, MAC2STR(sta->addr));
+	}
+}
+
+
+static void rx_data_icmp(struct wlantest *wt, const u8 *bssid,
+			 const u8 *sta_addr, u32 dst, u32 src,
+			 const u8 *data, size_t len, const u8 *peer_addr)
+{
+	struct in_addr addr;
+	char buf[20];
+	const struct icmphdr *hdr;
+	u16 id, seq;
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+
+	hdr = (const struct icmphdr *) data;
+	if (len < 4)
+		return;
+
+	/* TODO: check hdr->checksum */
+
+	if (hdr->type != ICMP_ECHOREPLY && hdr->type != ICMP_ECHO)
+		return;
+	if (len < 8)
+		return;
+
+	id = ntohs(hdr->un.echo.id);
+	seq = ntohs(hdr->un.echo.sequence);
+
+	addr.s_addr = dst;
+	snprintf(buf, sizeof(buf), "%s", inet_ntoa(addr));
+	addr.s_addr = src;
+	add_note(wt, MSG_DEBUG, "ICMP echo %s %s -> %s id=%04x seq=%u len=%u%s",
+		 hdr->type == ICMP_ECHO ? "request" : "response",
+		 inet_ntoa(addr), buf, id, seq, (unsigned) len - 8,
+		 peer_addr ? " [DL]" : "");
+
+	bss = bss_find(wt, bssid);
+	if (bss == NULL) {
+		add_note(wt, MSG_INFO, "No BSS " MACSTR
+			 " known for ICMP packet", MAC2STR(bssid));
+		return;
+	}
+
+	if (sta_addr == NULL)
+		return; /* FromDS broadcast ping */
+
+	sta = sta_find(bss, sta_addr);
+	if (sta == NULL) {
+		add_note(wt, MSG_INFO, "No STA " MACSTR
+			 " known for ICMP packet", MAC2STR(sta_addr));
+		return;
+	}
+
+	ping_update(wt, sta, hdr->type == ICMP_ECHO, src, dst, id, seq);
+	if (peer_addr && (sta = sta_find(bss, peer_addr)))
+		ping_update(wt, sta, hdr->type == ICMP_ECHO, src, dst, id, seq);
+}
+
+#endif /* __APPLE__ */
+
+
+static int hwsim_test_packet(const u8 *data, size_t len)
+{
+	size_t i;
+
+	if (len != 1500 - 14)
+		return 0;
+
+	for (i = 0; i < len; i++) {
+		if (data[i] != (i & 0xff))
+			return 0;
+	}
+
+	return 1;
+}
+
+
+void rx_data_ip(struct wlantest *wt, const u8 *bssid, const u8 *sta_addr,
+		const u8 *dst, const u8 *src, const u8 *data, size_t len,
+		const u8 *peer_addr)
+{
+	struct ip ip;
+	const u8 *payload;
+	size_t plen;
+	uint16_t frag_off, ip_len;
+
+	if (len < sizeof(ip))
+		return;
+	os_memcpy(&ip, data, sizeof(ip));
+
+	if (ip.ip_v != 4) {
+		if (hwsim_test_packet(data, len)) {
+			add_note(wt, MSG_INFO, "hwsim_test package");
+			return;
+		}
+		add_note(wt, MSG_DEBUG, "Unexpected IP protocol version %u in "
+			 "IPv4 packet (bssid=" MACSTR " str=" MACSTR
+			 " dst=" MACSTR ")", ip.ip_v, MAC2STR(bssid),
+			 MAC2STR(src), MAC2STR(dst));
+		return;
+	}
+	if (ip.ip_hl * 4 < sizeof(ip)) {
+		add_note(wt, MSG_DEBUG, "Unexpected IP header length %u in "
+			 "IPv4 packet (bssid=" MACSTR " str=" MACSTR
+			 " dst=" MACSTR ")", ip.ip_hl, MAC2STR(bssid),
+			 MAC2STR(src), MAC2STR(dst));
+		return;
+	}
+	if (ip.ip_hl * 4 > len) {
+		add_note(wt, MSG_DEBUG, "Truncated IP header (ihl=%u len=%u) "
+			 "in IPv4 packet (bssid=" MACSTR " str=" MACSTR
+			 " dst=" MACSTR ")", ip.ip_hl, (unsigned) len,
+			 MAC2STR(bssid), MAC2STR(src), MAC2STR(dst));
+		return;
+	}
+
+	/* TODO: check header checksum in ip.ip_sum */
+
+	frag_off = be_to_host16(ip.ip_off);
+	if (frag_off & 0x1fff) {
+		wpa_printf(MSG_EXCESSIVE, "IP fragment reassembly not yet "
+			   "supported");
+		return;
+	}
+
+	ip_len = be_to_host16(ip.ip_len);
+	if (ip_len > len)
+		return;
+	if (ip_len < len)
+		len = ip_len;
+
+	payload = data + 4 * ip.ip_hl;
+	plen = len - 4 * ip.ip_hl;
+
+	switch (ip.ip_p) {
+#ifndef __APPLE__
+	case IPPROTO_ICMP:
+		rx_data_icmp(wt, bssid, sta_addr, ip.ip_dst.s_addr,
+			     ip.ip_src.s_addr, payload, plen, peer_addr);
+		break;
+#endif /* __APPLE__ */
+	}
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/rx_mgmt.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/rx_mgmt.c
new file mode 100644
index 0000000..62adaa8
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/rx_mgmt.c
@@ -0,0 +1,3022 @@
+/*
+ * Received Management frame processing
+ * Copyright (c) 2010-2020, 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/defs.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/wpa_common.h"
+#include "crypto/aes.h"
+#include "crypto/aes_siv.h"
+#include "crypto/aes_wrap.h"
+#include "wlantest.h"
+
+
+static int check_mmie_mic(unsigned int mgmt_group_cipher,
+			  const u8 *igtk, size_t igtk_len,
+			  const u8 *data, size_t len);
+
+
+static const char * mgmt_stype(u16 stype)
+{
+	switch (stype) {
+	case WLAN_FC_STYPE_ASSOC_REQ:
+		return "ASSOC-REQ";
+	case WLAN_FC_STYPE_ASSOC_RESP:
+		return "ASSOC-RESP";
+	case WLAN_FC_STYPE_REASSOC_REQ:
+		return "REASSOC-REQ";
+	case WLAN_FC_STYPE_REASSOC_RESP:
+		return "REASSOC-RESP";
+	case WLAN_FC_STYPE_PROBE_REQ:
+		return "PROBE-REQ";
+	case WLAN_FC_STYPE_PROBE_RESP:
+		return "PROBE-RESP";
+	case WLAN_FC_STYPE_BEACON:
+		return "BEACON";
+	case WLAN_FC_STYPE_ATIM:
+		return "ATIM";
+	case WLAN_FC_STYPE_DISASSOC:
+		return "DISASSOC";
+	case WLAN_FC_STYPE_AUTH:
+		return "AUTH";
+	case WLAN_FC_STYPE_DEAUTH:
+		return "DEAUTH";
+	case WLAN_FC_STYPE_ACTION:
+		return "ACTION";
+	case WLAN_FC_STYPE_ACTION_NO_ACK:
+		return "ACTION-NO-ACK";
+	}
+	return "??";
+}
+
+
+static void parse_basic_ml(const u8 *ie, size_t len, bool ap)
+{
+	const u8 *pos, *end, *ci_end, *info_end;
+	u16 ctrl, eml, cap;
+	const struct element *elem;
+
+	pos = ie;
+	end = ie + len;
+
+	if (end - pos < 2)
+		return;
+	ctrl = WPA_GET_LE16(pos);
+	wpa_printf(MSG_DEBUG,
+		   "Multi-Link Control: Type=%u Reserved=%u Presence Bitmap=0x%x",
+		   ctrl & MULTI_LINK_CONTROL_TYPE_MASK,
+		   ctrl & BIT(3),
+		   ctrl >> 4);
+	pos += 2;
+
+	/* Common Info */
+
+	if (end - pos < 1)
+		return;
+	len = *pos;
+	if (len > end - pos) {
+		wpa_printf(MSG_INFO,
+			   "Truncated Multi-Link Common Info (len=%zu left=%zu)",
+			   len, (size_t) (end - pos));
+		return;
+	}
+	if (len < 1 + ETH_ALEN) {
+		wpa_printf(MSG_INFO,
+			   "No room for MLD MAC Address in Multi-Link Common Info");
+		return;
+	}
+	ci_end = pos + len;
+	pos += 1 + ETH_ALEN;
+
+	if (ctrl & BASIC_MULTI_LINK_CTRL_PRES_LINK_ID) {
+		if (ci_end - pos < 1) {
+			wpa_printf(MSG_INFO,
+				   "No room for Link ID Info in Multi-Link Common Info");
+			return;
+		}
+		wpa_printf(MSG_DEBUG, "Link ID Info: 0x%x", *pos);
+		if (!ap)
+			wpa_printf(MSG_INFO,
+				   "Unexpected Link ID Info in Common Info from a non-AP STA");
+		pos++;
+	}
+	if (ctrl & BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT) {
+		if (ci_end - pos < 1) {
+			wpa_printf(MSG_INFO,
+				   "No room for BSS Parameters Change Count in Multi-Link Common Info");
+			return;
+		}
+		wpa_printf(MSG_DEBUG, "BSS Parameters Change Count: %u", *pos);
+		if (!ap)
+			wpa_printf(MSG_INFO,
+				   "Unexpected BSS Parameters Change Count in Common Info from a non-AP STA");
+		pos++;
+	}
+	if (ctrl & BASIC_MULTI_LINK_CTRL_PRES_MSD_INFO) {
+		if (ci_end - pos < 2) {
+			wpa_printf(MSG_INFO,
+				   "No room for Medium Synchronization Delay Information in Multi-Link Common Info");
+			return;
+		}
+		wpa_printf(MSG_DEBUG,
+			   "Medium Synchronization Delay Information: 0x%x",
+			   WPA_GET_LE16(pos));
+		if (!ap)
+			wpa_printf(MSG_INFO,
+				   "Unexpected Medium Synchronization Delay Information in Common Info from a non-AP STA");
+		pos += 2;
+	}
+	if (ctrl & BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA) {
+		if (ci_end - pos < 2) {
+			wpa_printf(MSG_INFO,
+				   "No room for EML Capabilities in Multi-Link Common Info");
+			return;
+		}
+		eml = WPA_GET_LE16(pos);
+		pos += 2;
+		wpa_printf(MSG_DEBUG,
+			   "EML Capabilities: 0x%x (EMLSR=%u EMLSR_Padding_Delay=%u EMLSR_Transition_Delay=%u EMLMR=%u EMLMR_Delay=%u Transition_Timeout=%u Reserved=%u)",
+			   eml,
+			   !!(eml & EHT_ML_EML_CAPA_EMLSR_SUPP),
+			   (eml & EHT_ML_EML_CAPA_EMLSR_PADDING_DELAY_MASK) >>
+			   1,
+			   (eml & EHT_ML_EML_CAPA_EMLSR_TRANS_DELAY_MASK) >> 4,
+			   !!(eml & EHT_ML_EML_CAPA_EMLMR_SUPP),
+			   (eml & EHT_ML_EML_CAPA_EMLMR_DELAY_MASK) >> 8,
+			   (eml & EHT_ML_EML_CAPA_TRANSITION_TIMEOUT_MASK) >>
+			   11,
+			   !!(eml & BIT(15)));
+	}
+	if (ctrl & BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA) {
+		if (ci_end - pos < 2) {
+			wpa_printf(MSG_INFO,
+				   "No room for MLD Capabilities and Operations in Multi-Link Common Info");
+			return;
+		}
+		cap = WPA_GET_LE16(pos);
+		pos += 2;
+		wpa_printf(MSG_DEBUG,
+			   "MLD Capabilities and Operations: 0x%x (Max_Simultaneous_Links=%u SRS=%u T2L=0x%x Freq_Sep_STR=0x%x AAR=%u Reserved=0x%x)",
+			   cap,
+			   cap & EHT_ML_MLD_CAPA_MAX_NUM_SIM_LINKS_MASK,
+			   !!(cap & EHT_ML_MLD_CAPA_SRS_SUPP),
+			   (cap &
+			    EHT_ML_MLD_CAPA_TID_TO_LINK_MAP_NEG_SUPP_MSK) >> 5,
+			   (cap & EHT_ML_MLD_CAPA_FREQ_SEP_FOR_STR_MASK) >> 7,
+			   !!(cap & EHT_ML_MLD_CAPA_AAR_SUPP),
+			   (cap & 0xe000) >> 13);
+	}
+	if (ctrl & BASIC_MULTI_LINK_CTRL_PRES_AP_MLD_ID) {
+		if (ci_end - pos < 1) {
+			wpa_printf(MSG_INFO,
+				   "No room for AP MLD ID in Multi-Link Common Info");
+			return;
+		}
+		wpa_printf(MSG_DEBUG, "AP MLD ID: %u", *pos);
+		pos++;
+	}
+
+	if (pos < ci_end) {
+		wpa_hexdump(MSG_INFO,
+			    "Extra information at the end of Common Info",
+			    pos, ci_end - pos);
+		pos = ci_end;
+	}
+
+	/* Link Info */
+
+	for_each_element(elem, pos, end - pos) {
+		if (elem->id != EHT_ML_SUB_ELEM_PER_STA_PROFILE) {
+			wpa_printf(MSG_DEBUG, "Link Info subelement id=%u",
+				   elem->id);
+			wpa_hexdump(MSG_DEBUG, "Link Info subelement data",
+				    elem->data, elem->datalen);
+			continue;
+		}
+
+		pos = elem->data;
+		end = pos + elem->datalen;
+
+		if (end - pos < 2) {
+			wpa_printf(MSG_INFO,
+				   "Truncated Per-STA Profile subelement");
+			continue;
+		}
+		ctrl = WPA_GET_LE16(pos);
+		pos += 2;
+
+		wpa_printf(MSG_DEBUG, "Per-STA Profile: len=%u Link_ID=%u Complete=%u Reserved=0x%x",
+			   elem->datalen,
+			   ctrl & BASIC_MLE_STA_CTRL_LINK_ID_MASK,
+			   !!(ctrl & BASIC_MLE_STA_CTRL_COMPLETE_PROFILE),
+			   (ctrl & 0xf000) >> 12);
+
+		if (end - pos < 1) {
+			wpa_printf(MSG_INFO, "No room for STA Info field");
+			continue;
+		}
+		len = *pos;
+		if (len < 1 || len > end - pos) {
+			wpa_printf(MSG_INFO, "Truncated STA Info field");
+			continue;
+		}
+		info_end = pos + len;
+		pos++;
+		if (ctrl & BASIC_MLE_STA_CTRL_PRES_STA_MAC) {
+			if (info_end - pos < ETH_ALEN) {
+				wpa_printf(MSG_INFO,
+					   "Truncated STA MAC Address in STA Info");
+				continue;
+			}
+			wpa_printf(MSG_DEBUG, "STA MAC Address: " MACSTR,
+				   MAC2STR(pos));
+			pos += ETH_ALEN;
+		}
+		if (ctrl & BASIC_MLE_STA_CTRL_PRES_BEACON_INT) {
+			if (info_end - pos < 2) {
+				wpa_printf(MSG_INFO,
+					   "Truncated Beacon Interval in STA Info");
+				continue;
+			}
+			wpa_printf(MSG_DEBUG, "Beacon Interval: %u",
+				   WPA_GET_LE16(pos));
+			pos += 2;
+		}
+		if (ctrl & BASIC_MLE_STA_CTRL_PRES_TSF_OFFSET) {
+			if (info_end - pos < 8) {
+				wpa_printf(MSG_INFO,
+					   "Truncated TSF Offset in STA Info");
+				continue;
+			}
+			wpa_printf(MSG_DEBUG, "TSF Offset: 0x%llx",
+				   (long long unsigned) WPA_GET_LE64(pos));
+			pos += 8;
+		}
+		if (ctrl & BASIC_MLE_STA_CTRL_PRES_DTIM_INFO) {
+			if (info_end - pos < 2) {
+				wpa_printf(MSG_INFO,
+					   "Truncated DTIM Info in STA Info");
+				continue;
+			}
+			wpa_printf(MSG_DEBUG, "DTIM Info: 0x%x",
+				   WPA_GET_LE16(pos));
+			pos += 2;
+		}
+		if ((ctrl & (BASIC_MLE_STA_CTRL_COMPLETE_PROFILE |
+			     BASIC_MLE_STA_CTRL_PRES_NSTR_LINK_PAIR)) ==
+		    (BASIC_MLE_STA_CTRL_COMPLETE_PROFILE |
+		     BASIC_MLE_STA_CTRL_PRES_NSTR_LINK_PAIR)) {
+			if (ctrl & BASIC_MLE_STA_CTRL_NSTR_BITMAP) {
+				if (info_end - pos < 2) {
+					wpa_printf(MSG_INFO,
+						   "Truncated NSTR Indication Bitmap in STA Info");
+					continue;
+				}
+				wpa_printf(MSG_DEBUG, "NSTR Indication Bitmap: 0x%04x",
+					   WPA_GET_LE16(pos));
+				pos += 2;
+			} else {
+				if (info_end - pos < 1) {
+					wpa_printf(MSG_INFO,
+						   "Truncated NSTR Indication Bitmap in STA Info");
+					continue;
+				}
+				wpa_printf(MSG_DEBUG, "NSTR Indication Bitmap: 0x%02x",
+					   *pos);
+				pos++;
+			}
+		}
+		if (ctrl & BASIC_MLE_STA_CTRL_PRES_BSS_PARAM_COUNT) {
+			if (info_end - pos < 1) {
+				wpa_printf(MSG_INFO,
+					   "Truncated BSS Parameters Change Count in STA Info");
+				continue;
+			}
+			wpa_printf(MSG_DEBUG, "BSS Parameters Change Count: %u",
+				   *pos);
+			pos++;
+		}
+		if (info_end > pos) {
+			wpa_hexdump(MSG_INFO,
+				    "Extra information at the end of STA Info",
+				    pos, ci_end - pos);
+			pos = info_end;
+		}
+
+		wpa_hexdump(MSG_DEBUG, "STA Profile", pos, end - pos);
+	}
+}
+
+
+static void rx_mgmt_beacon(struct wlantest *wt, const u8 *data, size_t len)
+{
+	const struct ieee80211_mgmt *mgmt;
+	struct wlantest_bss *bss;
+	struct ieee802_11_elems elems;
+	size_t offset;
+	const u8 *mme;
+	size_t mic_len;
+	u16 keyid;
+
+	mgmt = (const struct ieee80211_mgmt *) data;
+	offset = mgmt->u.beacon.variable - data;
+	if (len < offset)
+		return;
+	bss = bss_get(wt, mgmt->bssid);
+	if (bss == NULL)
+		return;
+	/* do not override with Beacon data */
+	if (!bss->proberesp_seen)
+		bss->capab_info = le_to_host16(mgmt->u.beacon.capab_info);
+	if (ieee802_11_parse_elems(mgmt->u.beacon.variable, len - offset,
+				   &elems, 0) == ParseFailed) {
+		if (bss->parse_error_reported)
+			return;
+		add_note(wt, MSG_INFO, "Invalid IEs in a Beacon frame from "
+			 MACSTR, MAC2STR(mgmt->sa));
+		bss->parse_error_reported = 1;
+		return;
+	}
+
+	if (elems.rsnxe) {
+		os_memcpy(bss->rsnxe, elems.rsnxe, elems.rsnxe_len);
+		bss->rsnxe_len = elems.rsnxe_len;
+	} else {
+		bss->rsnxe_len = 0;
+	}
+
+	if (!bss->proberesp_seen)
+		bss_update(wt, bss, &elems, 1);
+
+	mme = get_ie(mgmt->u.beacon.variable, len - offset, WLAN_EID_MMIE);
+	if (!mme) {
+		if (bss->bigtk_idx) {
+			add_note(wt, MSG_INFO,
+				 "Unexpected unprotected Beacon frame from "
+				 MACSTR, MAC2STR(mgmt->sa));
+			bss->counters[WLANTEST_BSS_COUNTER_MISSING_BIP_MMIE]++;
+		}
+		return;
+	}
+
+	mic_len = bss->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC ? 8 : 16;
+	if (len < 24 + 10 + mic_len ||
+	    data[len - (10 + mic_len)] != WLAN_EID_MMIE ||
+	    data[len - (10 + mic_len - 1)] != 8 + mic_len) {
+		add_note(wt, MSG_INFO, "Invalid MME in a Beacon frame from "
+			 MACSTR, MAC2STR(mgmt->sa));
+		return;
+	}
+
+	mme += 2;
+	keyid = WPA_GET_LE16(mme);
+	if (keyid < 6 || keyid > 7) {
+		add_note(wt, MSG_INFO, "Unexpected MME KeyID %u from " MACSTR,
+			 keyid, MAC2STR(mgmt->sa));
+		bss->counters[WLANTEST_BSS_COUNTER_INVALID_BIP_MMIE]++;
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "Beacon frame MME KeyID %u", keyid);
+	wpa_hexdump(MSG_MSGDUMP, "MME IPN", mme + 2, 6);
+	wpa_hexdump(MSG_MSGDUMP, "MME MIC", mme + 8, mic_len);
+
+	if (!bss->igtk_len[keyid]) {
+		add_note(wt, MSG_DEBUG,
+			 "No BIGTK known to validate BIP frame from " MACSTR,
+			 MAC2STR(mgmt->sa));
+		return;
+	}
+
+	if (os_memcmp(mme + 2, bss->ipn[keyid], 6) <= 0) {
+		add_note(wt, MSG_INFO, "BIP replay detected: SA=" MACSTR,
+			 MAC2STR(mgmt->sa));
+		wpa_hexdump(MSG_INFO, "RX IPN", mme + 2, 6);
+		wpa_hexdump(MSG_INFO, "Last RX IPN", bss->ipn[keyid], 6);
+	}
+
+	if (check_mmie_mic(bss->mgmt_group_cipher, bss->igtk[keyid],
+			   bss->igtk_len[keyid], data, len) < 0) {
+		add_note(wt, MSG_INFO, "Invalid MME MIC in a Beacon frame from "
+			 MACSTR, MAC2STR(mgmt->sa));
+		bss->counters[WLANTEST_BSS_COUNTER_INVALID_BIP_MMIE]++;
+		return;
+	}
+
+	add_note(wt, MSG_DEBUG, "Valid MME MIC in Beacon frame");
+	os_memcpy(bss->ipn[keyid], mme + 2, 6);
+}
+
+
+static void rx_mgmt_probe_resp(struct wlantest *wt, const u8 *data, size_t len)
+{
+	const struct ieee80211_mgmt *mgmt;
+	struct wlantest_bss *bss;
+	struct ieee802_11_elems elems;
+	size_t offset;
+
+	mgmt = (const struct ieee80211_mgmt *) data;
+	offset = mgmt->u.probe_resp.variable - data;
+	if (len < offset)
+		return;
+	bss = bss_get(wt, mgmt->bssid);
+	if (bss == NULL)
+		return;
+
+	bss->counters[WLANTEST_BSS_COUNTER_PROBE_RESPONSE]++;
+	bss->capab_info = le_to_host16(mgmt->u.probe_resp.capab_info);
+	if (ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - offset,
+				   &elems, 0) == ParseFailed) {
+		if (bss->parse_error_reported)
+			return;
+		add_note(wt, MSG_INFO, "Invalid IEs in a Probe Response frame "
+			 "from " MACSTR, MAC2STR(mgmt->sa));
+		bss->parse_error_reported = 1;
+		return;
+	}
+
+	bss_update(wt, bss, &elems, 2);
+}
+
+
+static void process_fils_auth(struct wlantest *wt, struct wlantest_bss *bss,
+			      struct wlantest_sta *sta,
+			      const struct ieee80211_mgmt *mgmt, size_t len)
+{
+	struct ieee802_11_elems elems;
+	u16 trans;
+	struct wpa_ie_data data;
+
+	if (sta->auth_alg != WLAN_AUTH_FILS_SK ||
+	    len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth))
+		return;
+
+	trans = le_to_host16(mgmt->u.auth.auth_transaction);
+
+	if (ieee802_11_parse_elems(mgmt->u.auth.variable,
+				   len - IEEE80211_HDRLEN -
+				   sizeof(mgmt->u.auth), &elems, 0) ==
+	    ParseFailed)
+		return;
+
+	if (trans == 1) {
+		if (!elems.rsn_ie) {
+			add_note(wt, MSG_INFO,
+				 "FILS Authentication frame missing RSNE");
+			return;
+		}
+		if (wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2,
+					 elems.rsn_ie_len + 2, &data) < 0) {
+			add_note(wt, MSG_INFO,
+				 "Invalid RSNE in FILS Authentication frame");
+			return;
+		}
+		sta->key_mgmt = data.key_mgmt;
+		sta->pairwise_cipher = data.pairwise_cipher;
+	}
+
+	if (!elems.fils_nonce) {
+		add_note(wt, MSG_INFO,
+			 "FILS Authentication frame missing nonce");
+		return;
+	}
+
+	if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0)
+		os_memcpy(sta->anonce, elems.fils_nonce, FILS_NONCE_LEN);
+	else
+		os_memcpy(sta->snonce, elems.fils_nonce, FILS_NONCE_LEN);
+}
+
+
+static void process_ft_auth(struct wlantest *wt, struct wlantest_bss *bss,
+			    struct wlantest_sta *sta,
+			    const struct ieee80211_mgmt *mgmt, size_t len)
+{
+	u16 trans;
+	struct wpa_ft_ies parse;
+	struct wpa_ptk ptk;
+	u8 ptk_name[WPA_PMK_NAME_LEN];
+	struct wlantest_bss *old_bss;
+	struct wlantest_sta *old_sta = NULL;
+
+	if (sta->auth_alg != WLAN_AUTH_FT ||
+	    len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth))
+		return;
+
+	trans = le_to_host16(mgmt->u.auth.auth_transaction);
+
+	if (wpa_ft_parse_ies(mgmt->u.auth.variable,
+			     len - IEEE80211_HDRLEN - sizeof(mgmt->u.auth),
+			     &parse, 0)) {
+		add_note(wt, MSG_INFO,
+			 "Could not parse FT Authentication Response frame");
+		return;
+	}
+
+	if (trans == 1) {
+		sta->key_mgmt = parse.key_mgmt;
+		sta->pairwise_cipher = parse.pairwise_cipher;
+		return;
+	}
+
+	if (trans != 2)
+		return;
+
+	/* TODO: Should find the latest updated PMK-R0 value here instead
+	 * copying the one from the first found matching old STA entry. */
+	dl_list_for_each(old_bss, &wt->bss, struct wlantest_bss, list) {
+		if (old_bss == bss)
+			continue;
+		old_sta = sta_find(old_bss, sta->addr);
+		if (old_sta)
+			break;
+	}
+	if (!old_sta)
+		return;
+
+	os_memcpy(sta->pmk_r0, old_sta->pmk_r0, old_sta->pmk_r0_len);
+	sta->pmk_r0_len = old_sta->pmk_r0_len;
+	os_memcpy(sta->pmk_r0_name, old_sta->pmk_r0_name,
+		  sizeof(sta->pmk_r0_name));
+
+	if (parse.r1kh_id)
+		os_memcpy(bss->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN);
+
+	if (wpa_derive_pmk_r1(sta->pmk_r0, sta->pmk_r0_len, sta->pmk_r0_name,
+			      bss->r1kh_id, sta->addr, sta->pmk_r1,
+			      sta->pmk_r1_name) < 0)
+		return;
+	sta->pmk_r1_len = sta->pmk_r0_len;
+
+	if (!parse.fte_anonce || !parse.fte_snonce ||
+	    wpa_pmk_r1_to_ptk(sta->pmk_r1, sta->pmk_r1_len, parse.fte_snonce,
+			      parse.fte_anonce, sta->addr, bss->bssid,
+			      sta->pmk_r1_name, &ptk, ptk_name, sta->key_mgmt,
+			      sta->pairwise_cipher, 0) < 0)
+		return;
+
+	sta_new_ptk(wt, sta, &ptk);
+}
+
+
+static void process_sae_auth(struct wlantest *wt, struct wlantest_bss *bss,
+			     struct wlantest_sta *sta,
+			     const struct ieee80211_mgmt *mgmt, size_t len)
+{
+	u16 trans, status, group;
+
+	if (sta->auth_alg != WLAN_AUTH_SAE ||
+	    len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth) + 2)
+		return;
+
+	trans = le_to_host16(mgmt->u.auth.auth_transaction);
+	if (trans != 1)
+		return;
+
+	status = le_to_host16(mgmt->u.auth.status_code);
+	if (status != WLAN_STATUS_SUCCESS &&
+	    status != WLAN_STATUS_SAE_HASH_TO_ELEMENT &&
+	    status != WLAN_STATUS_SAE_PK)
+		return;
+
+	group = WPA_GET_LE16(mgmt->u.auth.variable);
+	wpa_printf(MSG_DEBUG, "SAE Commit using group %u", group);
+	sta->sae_group = group;
+}
+
+
+static void rx_mgmt_auth(struct wlantest *wt, const u8 *data, size_t len)
+{
+	const struct ieee80211_mgmt *mgmt;
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+	u16 alg, trans, status;
+	bool from_ap;
+
+	mgmt = (const struct ieee80211_mgmt *) data;
+	bss = bss_get(wt, mgmt->bssid);
+	if (bss == NULL)
+		return;
+	from_ap = os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0;
+	if (from_ap)
+		sta = sta_get(bss, mgmt->da);
+	else
+		sta = sta_get(bss, mgmt->sa);
+	if (sta == NULL)
+		return;
+
+	if (len < 24 + 6) {
+		add_note(wt, MSG_INFO, "Too short Authentication frame from "
+			 MACSTR, MAC2STR(mgmt->sa));
+		return;
+	}
+
+	alg = le_to_host16(mgmt->u.auth.auth_alg);
+	sta->auth_alg = alg;
+	trans = le_to_host16(mgmt->u.auth.auth_transaction);
+	status = le_to_host16(mgmt->u.auth.status_code);
+
+	wpa_printf(MSG_DEBUG, "AUTH " MACSTR " -> " MACSTR
+		   " (alg=%u trans=%u status=%u)",
+		   MAC2STR(mgmt->sa), MAC2STR(mgmt->da), alg, trans, status);
+
+	if (status == WLAN_STATUS_SUCCESS &&
+	    ((alg == WLAN_AUTH_OPEN && trans == 2) ||
+	     (alg == WLAN_AUTH_SAE && trans == 2 && from_ap))) {
+		if (sta->state == STATE1) {
+			add_note(wt, MSG_DEBUG, "STA " MACSTR
+				 " moved to State 2 with " MACSTR,
+				 MAC2STR(sta->addr), MAC2STR(bss->bssid));
+			sta->state = STATE2;
+		}
+	}
+
+	if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0)
+		sta->counters[WLANTEST_STA_COUNTER_AUTH_RX]++;
+	else
+		sta->counters[WLANTEST_STA_COUNTER_AUTH_TX]++;
+
+	process_fils_auth(wt, bss, sta, mgmt, len);
+	process_ft_auth(wt, bss, sta, mgmt, len);
+	process_sae_auth(wt, bss, sta, mgmt, len);
+}
+
+
+static void deauth_all_stas(struct wlantest *wt, struct wlantest_bss *bss)
+{
+	struct wlantest_sta *sta;
+	dl_list_for_each(sta, &bss->sta, struct wlantest_sta, list) {
+		if (sta->state == STATE1)
+			continue;
+		add_note(wt, MSG_DEBUG, "STA " MACSTR
+			 " moved to State 1 with " MACSTR,
+			 MAC2STR(sta->addr), MAC2STR(bss->bssid));
+		sta->state = STATE1;
+	}
+}
+
+
+static void tdls_link_down(struct wlantest *wt, struct wlantest_bss *bss,
+			   struct wlantest_sta *sta)
+{
+	struct wlantest_tdls *tdls;
+	dl_list_for_each(tdls, &bss->tdls, struct wlantest_tdls, list) {
+		if ((tdls->init == sta || tdls->resp == sta) && tdls->link_up)
+		{
+			add_note(wt, MSG_DEBUG, "TDLS: Set link down based on "
+				 "STA deauth/disassoc");
+			tdls->link_up = 0;
+		}
+	}
+}
+
+
+static void rx_mgmt_deauth(struct wlantest *wt, const u8 *data, size_t len,
+			   int valid)
+{
+	const struct ieee80211_mgmt *mgmt;
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+	u16 fc, reason;
+
+	mgmt = (const struct ieee80211_mgmt *) data;
+	bss = bss_get(wt, mgmt->bssid);
+	if (bss == NULL)
+		return;
+	if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0)
+		sta = sta_get(bss, mgmt->da);
+	else
+		sta = sta_get(bss, mgmt->sa);
+
+	if (len < 24 + 2) {
+		add_note(wt, MSG_INFO, "Too short Deauthentication frame from "
+			 MACSTR, MAC2STR(mgmt->sa));
+		return;
+	}
+
+	reason = le_to_host16(mgmt->u.deauth.reason_code);
+	wpa_printf(MSG_DEBUG, "DEAUTH " MACSTR " -> " MACSTR
+		   " (reason=%u) (valid=%d)",
+		   MAC2STR(mgmt->sa), MAC2STR(mgmt->da),
+		   reason, valid);
+	wpa_hexdump(MSG_MSGDUMP, "DEAUTH payload", data + 24, len - 24);
+
+	if (sta == NULL) {
+		if (valid && mgmt->da[0] == 0xff)
+			deauth_all_stas(wt, bss);
+		return;
+	}
+
+	if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0) {
+		sta->counters[valid ? WLANTEST_STA_COUNTER_VALID_DEAUTH_RX :
+			      WLANTEST_STA_COUNTER_INVALID_DEAUTH_RX]++;
+		if (sta->pwrmgt && !sta->pspoll)
+			sta->counters[WLANTEST_STA_COUNTER_DEAUTH_RX_ASLEEP]++;
+		else
+			sta->counters[WLANTEST_STA_COUNTER_DEAUTH_RX_AWAKE]++;
+
+		fc = le_to_host16(mgmt->frame_control);
+		if (!(fc & WLAN_FC_ISWEP) && reason == 6)
+			sta->counters[WLANTEST_STA_COUNTER_DEAUTH_RX_RC6]++;
+		else if (!(fc & WLAN_FC_ISWEP) && reason == 7)
+			sta->counters[WLANTEST_STA_COUNTER_DEAUTH_RX_RC7]++;
+	} else
+		sta->counters[valid ? WLANTEST_STA_COUNTER_VALID_DEAUTH_TX :
+			      WLANTEST_STA_COUNTER_INVALID_DEAUTH_TX]++;
+
+	if (!valid) {
+		add_note(wt, MSG_INFO, "Do not change STA " MACSTR " State "
+			 "since Disassociation frame was not protected "
+			 "correctly", MAC2STR(sta->addr));
+		return;
+	}
+
+	if (sta->state != STATE1) {
+		add_note(wt, MSG_DEBUG, "STA " MACSTR
+			 " moved to State 1 with " MACSTR,
+			 MAC2STR(sta->addr), MAC2STR(bss->bssid));
+		sta->state = STATE1;
+	}
+	tdls_link_down(wt, bss, sta);
+}
+
+
+static const u8 * get_fils_session(const u8 *ies, size_t ies_len)
+{
+	const u8 *ie, *end;
+
+	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)
+			return ie;
+		ie += 2 + ie[1];
+	}
+	return NULL;
+}
+
+
+static int try_rmsk(struct wlantest *wt, struct wlantest_bss *bss,
+		    struct wlantest_sta *sta, struct wlantest_pmk *pmk,
+		    const u8 *frame_start, const u8 *frame_ad,
+		    const u8 *frame_ad_end, const u8 *encr_end)
+{
+	size_t pmk_len = 0;
+	u8 pmk_buf[PMK_LEN_MAX];
+	struct wpa_ptk ptk;
+	u8 ick[FILS_ICK_MAX_LEN];
+	size_t ick_len;
+	const u8 *aad[5];
+	size_t aad_len[5];
+	u8 buf[2000];
+
+	if (fils_rmsk_to_pmk(sta->key_mgmt, pmk->pmk, pmk->pmk_len,
+			     sta->snonce, sta->anonce, NULL, 0,
+			     pmk_buf, &pmk_len) < 0)
+		return -1;
+
+	if (fils_pmk_to_ptk(pmk_buf, pmk_len, sta->addr, bss->bssid,
+			    sta->snonce, sta->anonce, NULL, 0,
+			    &ptk, ick, &ick_len,
+			    sta->key_mgmt, sta->pairwise_cipher,
+			    NULL, NULL, 0) < 0)
+		return -1;
+
+	/* Check AES-SIV decryption with the derived key */
+
+	/* AES-SIV AAD vectors */
+
+	/* The STA's MAC address */
+	aad[0] = sta->addr;
+	aad_len[0] = ETH_ALEN;
+	/* The AP's BSSID */
+	aad[1] = bss->bssid;
+	aad_len[1] = ETH_ALEN;
+	/* The STA's nonce */
+	aad[2] = sta->snonce;
+	aad_len[2] = FILS_NONCE_LEN;
+	/* The AP's nonce */
+	aad[3] = sta->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] = frame_ad;
+	aad_len[4] = frame_ad_end - frame_ad;
+
+	if (encr_end - frame_ad_end < AES_BLOCK_SIZE ||
+	    encr_end - frame_ad_end > sizeof(buf))
+		return -1;
+	if (aes_siv_decrypt(ptk.kek, ptk.kek_len,
+			    frame_ad_end, encr_end - frame_ad_end,
+			    5, aad, aad_len, buf) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Derived PTK did not match AES-SIV data");
+		return -1;
+	}
+
+	add_note(wt, MSG_DEBUG, "Derived FILS PTK");
+	os_memcpy(&sta->ptk, &ptk, sizeof(ptk));
+	sta->ptk_set = 1;
+	sta->counters[WLANTEST_STA_COUNTER_PTK_LEARNED]++;
+	wpa_hexdump(MSG_DEBUG, "FILS: Decrypted Association Request elements",
+		    buf, encr_end - frame_ad_end - AES_BLOCK_SIZE);
+
+	if (wt->write_pcap_dumper || wt->pcapng) {
+		write_pcap_decrypted(wt, frame_start,
+				     frame_ad_end - frame_start,
+				     buf,
+				     encr_end - frame_ad_end - AES_BLOCK_SIZE);
+	}
+
+	return 0;
+}
+
+
+static void derive_fils_keys(struct wlantest *wt, struct wlantest_bss *bss,
+			     struct wlantest_sta *sta, const u8 *frame_start,
+			     const u8 *frame_ad, const u8 *frame_ad_end,
+			     const u8 *encr_end)
+{
+	struct wlantest_pmk *pmk;
+
+	wpa_printf(MSG_DEBUG, "Trying to derive PTK for " MACSTR
+		   " from FILS rMSK", MAC2STR(sta->addr));
+
+	dl_list_for_each(pmk, &bss->pmk, struct wlantest_pmk,
+			 list) {
+		wpa_printf(MSG_DEBUG, "Try per-BSS PMK");
+		if (try_rmsk(wt, bss, sta, pmk, frame_start, frame_ad,
+			     frame_ad_end, encr_end) == 0)
+			return;
+	}
+
+	dl_list_for_each(pmk, &wt->pmk, struct wlantest_pmk, list) {
+		wpa_printf(MSG_DEBUG, "Try global PMK");
+		if (try_rmsk(wt, bss, sta, pmk, frame_start, frame_ad,
+			     frame_ad_end, encr_end) == 0)
+			return;
+	}
+}
+
+
+static void rx_mgmt_assoc_req(struct wlantest *wt, const u8 *data, size_t len)
+{
+	const struct ieee80211_mgmt *mgmt;
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+	struct ieee802_11_elems elems;
+	const u8 *ie;
+	size_t ie_len;
+
+	mgmt = (const struct ieee80211_mgmt *) data;
+	bss = bss_get(wt, mgmt->bssid);
+	if (bss == NULL)
+		return;
+	sta = sta_get(bss, mgmt->sa);
+	if (sta == NULL)
+		return;
+
+	if (len < 24 + 4) {
+		add_note(wt, MSG_INFO, "Too short Association Request frame "
+			 "from " MACSTR, MAC2STR(mgmt->sa));
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "ASSOCREQ " MACSTR " -> " MACSTR
+		   " (capab=0x%x listen_int=%u)",
+		   MAC2STR(mgmt->sa), MAC2STR(mgmt->da),
+		   le_to_host16(mgmt->u.assoc_req.capab_info),
+		   le_to_host16(mgmt->u.assoc_req.listen_interval));
+
+	sta->counters[WLANTEST_STA_COUNTER_ASSOCREQ_TX]++;
+
+	ie = mgmt->u.assoc_req.variable;
+	ie_len = len - (mgmt->u.assoc_req.variable - data);
+
+	if (sta->auth_alg == WLAN_AUTH_FILS_SK) {
+		const u8 *session, *frame_ad, *frame_ad_end, *encr_end;
+
+		session = get_fils_session(ie, ie_len);
+		if (session) {
+			frame_ad = (const u8 *) &mgmt->u.assoc_req.capab_info;
+			frame_ad_end = session + 2 + session[1];
+			encr_end = data + len;
+			derive_fils_keys(wt, bss, sta, data, frame_ad,
+					 frame_ad_end, encr_end);
+			ie_len = session - ie;
+		}
+	}
+
+	if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) {
+		add_note(wt, MSG_INFO, "Invalid IEs in Association Request "
+			 "frame from " MACSTR, MAC2STR(mgmt->sa));
+		return;
+	}
+
+	if (elems.rsnxe) {
+		os_memcpy(sta->rsnxe, elems.rsnxe, elems.rsnxe_len);
+		sta->rsnxe_len = elems.rsnxe_len;
+	}
+
+	sta->assocreq_capab_info = le_to_host16(mgmt->u.assoc_req.capab_info);
+	sta->assocreq_listen_int =
+		le_to_host16(mgmt->u.assoc_req.listen_interval);
+	os_free(sta->assocreq_ies);
+	sta->assocreq_ies_len = len - (mgmt->u.assoc_req.variable - data);
+	sta->assocreq_ies = os_malloc(sta->assocreq_ies_len);
+	if (sta->assocreq_ies)
+		os_memcpy(sta->assocreq_ies, mgmt->u.assoc_req.variable,
+			  sta->assocreq_ies_len);
+
+	sta->assocreq_seen = 1;
+	sta_update_assoc(sta, &elems);
+	if (elems.basic_mle)
+		parse_basic_ml(elems.basic_mle, elems.basic_mle_len, false);
+}
+
+
+static void decrypt_fils_assoc_resp(struct wlantest *wt,
+				    struct wlantest_bss *bss,
+				    struct wlantest_sta *sta,
+				    const u8 *frame_start, const u8 *frame_ad,
+				    const u8 *frame_ad_end, const u8 *encr_end)
+{
+	const u8 *aad[5];
+	size_t aad_len[5];
+	u8 buf[2000];
+
+	if (!sta->ptk_set)
+		return;
+
+	/* Check AES-SIV decryption with the derived key */
+
+	/* AES-SIV AAD vectors */
+
+	/* The AP's BSSID */
+	aad[0] = bss->bssid;
+	aad_len[0] = ETH_ALEN;
+	/* The STA's MAC address */
+	aad[1] = sta->addr;
+	aad_len[1] = ETH_ALEN;
+	/* The AP's nonce */
+	aad[2] = sta->anonce;
+	aad_len[2] = FILS_NONCE_LEN;
+	/* The STA's nonce */
+	aad[3] = sta->snonce;
+	aad_len[3] = FILS_NONCE_LEN;
+	/*
+	 * The (Re)Association Response frame from the Capability Information
+	 * field to the FILS Session element (both inclusive).
+	 */
+	aad[4] = frame_ad;
+	aad_len[4] = frame_ad_end - frame_ad;
+
+	if (encr_end - frame_ad_end < AES_BLOCK_SIZE ||
+	    encr_end - frame_ad_end > sizeof(buf))
+		return;
+	if (aes_siv_decrypt(sta->ptk.kek, sta->ptk.kek_len,
+			    frame_ad_end, encr_end - frame_ad_end,
+			    5, aad, aad_len, buf) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Derived PTK did not match AES-SIV data");
+		return;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "FILS: Decrypted Association Response elements",
+		    buf, encr_end - frame_ad_end - AES_BLOCK_SIZE);
+
+	if (wt->write_pcap_dumper || wt->pcapng) {
+		write_pcap_decrypted(wt, frame_start,
+				     frame_ad_end - frame_start,
+				     buf,
+				     encr_end - frame_ad_end - AES_BLOCK_SIZE);
+	}
+}
+
+
+static void rx_mgmt_assoc_resp(struct wlantest *wt, const u8 *data, size_t len)
+{
+	const struct ieee80211_mgmt *mgmt;
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+	u16 capab, status, aid;
+	const u8 *ies;
+	size_t ies_len;
+	struct wpa_ft_ies parse;
+	const u8 *ml;
+
+	mgmt = (const struct ieee80211_mgmt *) data;
+	bss = bss_get(wt, mgmt->bssid);
+	if (bss == NULL)
+		return;
+	sta = sta_get(bss, mgmt->da);
+	if (sta == NULL)
+		return;
+
+	if (len < 24 + 6) {
+		add_note(wt, MSG_INFO, "Too short Association Response frame "
+			 "from " MACSTR, MAC2STR(mgmt->sa));
+		return;
+	}
+
+	ies = mgmt->u.assoc_resp.variable;
+	ies_len = len - (mgmt->u.assoc_resp.variable - data);
+
+	capab = le_to_host16(mgmt->u.assoc_resp.capab_info);
+	status = le_to_host16(mgmt->u.assoc_resp.status_code);
+	aid = le_to_host16(mgmt->u.assoc_resp.aid);
+
+	wpa_printf(MSG_DEBUG, "ASSOCRESP " MACSTR " -> " MACSTR
+		   " (capab=0x%x status=%u aid=%u)",
+		   MAC2STR(mgmt->sa), MAC2STR(mgmt->da), capab, status,
+		   aid & 0x3fff);
+
+	ml = get_ml_ie(ies, ies_len, MULTI_LINK_CONTROL_TYPE_BASIC);
+	if (ml)
+		parse_basic_ml(ml + 3, ml[1], true);
+
+	if (sta->auth_alg == WLAN_AUTH_FILS_SK) {
+		const u8 *session, *frame_ad, *frame_ad_end, *encr_end;
+
+		session = get_fils_session(ies, ies_len);
+		if (session) {
+			frame_ad = (const u8 *) &mgmt->u.assoc_resp.capab_info;
+			frame_ad_end = session + 2 + session[1];
+			encr_end = data + len;
+			decrypt_fils_assoc_resp(wt, bss, sta, data, frame_ad,
+						frame_ad_end, encr_end);
+			ies_len = session - ies;
+		}
+	}
+
+	if (status == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) {
+		struct ieee802_11_elems elems;
+		if (ieee802_11_parse_elems(ies, ies_len, &elems, 0) ==
+		    ParseFailed) {
+			add_note(wt, MSG_INFO, "Failed to parse IEs in "
+				 "AssocResp from " MACSTR,
+				 MAC2STR(mgmt->sa));
+		} else if (elems.timeout_int == NULL ||
+			   elems.timeout_int[0] !=
+			   WLAN_TIMEOUT_ASSOC_COMEBACK) {
+			add_note(wt, MSG_INFO, "No valid Timeout Interval IE "
+				 "with Assoc Comeback time in AssocResp "
+				 "(status=30) from " MACSTR,
+				 MAC2STR(mgmt->sa));
+		} else {
+			sta->counters[
+				WLANTEST_STA_COUNTER_ASSOCRESP_COMEBACK]++;
+		}
+	}
+
+	if (status)
+		return;
+
+	if ((aid & 0xc000) != 0xc000) {
+		add_note(wt, MSG_DEBUG, "Two MSBs of the AID were not set to 1 "
+			 "in Association Response from " MACSTR,
+			 MAC2STR(mgmt->sa));
+	}
+	sta->aid = aid & 0xc000;
+
+	if (sta->state < STATE2) {
+		add_note(wt, MSG_DEBUG,
+			 "STA " MACSTR " was not in State 2 when "
+			 "getting associated", MAC2STR(sta->addr));
+	}
+
+	if (sta->state < STATE3) {
+		add_note(wt, MSG_DEBUG, "STA " MACSTR
+			 " moved to State 3 with " MACSTR,
+			 MAC2STR(sta->addr), MAC2STR(bss->bssid));
+		sta->state = STATE3;
+	}
+
+	if (wpa_ft_parse_ies(ies, ies_len, &parse, 0) == 0) {
+		if (parse.r0kh_id) {
+			os_memcpy(bss->r0kh_id, parse.r0kh_id,
+				  parse.r0kh_id_len);
+			bss->r0kh_id_len = parse.r0kh_id_len;
+		}
+		if (parse.r1kh_id)
+			os_memcpy(bss->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN);
+	}
+}
+
+
+static void rx_mgmt_reassoc_req(struct wlantest *wt, const u8 *data,
+				size_t len)
+{
+	const struct ieee80211_mgmt *mgmt;
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+	struct ieee802_11_elems elems;
+	const u8 *ie;
+	size_t ie_len;
+
+	mgmt = (const struct ieee80211_mgmt *) data;
+	bss = bss_get(wt, mgmt->bssid);
+	if (bss == NULL)
+		return;
+	sta = sta_get(bss, mgmt->sa);
+	if (sta == NULL)
+		return;
+
+	if (len < 24 + 4 + ETH_ALEN) {
+		add_note(wt, MSG_INFO, "Too short Reassociation Request frame "
+			 "from " MACSTR, MAC2STR(mgmt->sa));
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "REASSOCREQ " MACSTR " -> " MACSTR
+		   " (capab=0x%x listen_int=%u current_ap=" MACSTR ")",
+		   MAC2STR(mgmt->sa), MAC2STR(mgmt->da),
+		   le_to_host16(mgmt->u.reassoc_req.capab_info),
+		   le_to_host16(mgmt->u.reassoc_req.listen_interval),
+		   MAC2STR(mgmt->u.reassoc_req.current_ap));
+
+	sta->counters[WLANTEST_STA_COUNTER_REASSOCREQ_TX]++;
+
+	ie = mgmt->u.reassoc_req.variable;
+	ie_len = len - (mgmt->u.reassoc_req.variable - data);
+
+	if (sta->auth_alg == WLAN_AUTH_FILS_SK) {
+		const u8 *session, *frame_ad, *frame_ad_end, *encr_end;
+
+		session = get_fils_session(ie, ie_len);
+		if (session) {
+			frame_ad = (const u8 *) &mgmt->u.reassoc_req.capab_info;
+			frame_ad_end = session + 2 + session[1];
+			encr_end = data + len;
+			derive_fils_keys(wt, bss, sta, data, frame_ad,
+					 frame_ad_end, encr_end);
+			ie_len = session - ie;
+		}
+	}
+
+	if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) {
+		add_note(wt, MSG_INFO, "Invalid IEs in Reassociation Request "
+			 "frame from " MACSTR, MAC2STR(mgmt->sa));
+		return;
+	}
+
+	if (elems.rsnxe) {
+		os_memcpy(sta->rsnxe, elems.rsnxe, elems.rsnxe_len);
+		sta->rsnxe_len = elems.rsnxe_len;
+	}
+
+	sta->assocreq_capab_info =
+		le_to_host16(mgmt->u.reassoc_req.capab_info);
+	sta->assocreq_listen_int =
+		le_to_host16(mgmt->u.reassoc_req.listen_interval);
+	os_free(sta->assocreq_ies);
+	sta->assocreq_ies_len = len - (mgmt->u.reassoc_req.variable - data);
+	sta->assocreq_ies = os_malloc(sta->assocreq_ies_len);
+	if (sta->assocreq_ies)
+		os_memcpy(sta->assocreq_ies, mgmt->u.reassoc_req.variable,
+			  sta->assocreq_ies_len);
+
+	sta->assocreq_seen = 1;
+	sta_update_assoc(sta, &elems);
+	if (elems.basic_mle)
+		parse_basic_ml(elems.basic_mle, elems.basic_mle_len, false);
+
+	if (elems.ftie) {
+		struct wpa_ft_ies parse;
+		int use_sha384;
+		struct rsn_mdie *mde;
+		const u8 *anonce, *snonce, *fte_mic;
+		u8 fte_elem_count;
+		unsigned int count;
+		u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
+		size_t mic_len = 16;
+		const u8 *kck;
+		size_t kck_len;
+
+		use_sha384 = wpa_key_mgmt_sha384(sta->key_mgmt);
+
+		if (wpa_ft_parse_ies(ie, ie_len, &parse, sta->key_mgmt) < 0) {
+			add_note(wt, MSG_INFO, "FT: Failed to parse FT IEs");
+			return;
+		}
+
+		if (!parse.rsn) {
+			add_note(wt, MSG_INFO, "FT: No RSNE in Reassoc Req");
+			return;
+		}
+
+		if (!parse.rsn_pmkid) {
+			add_note(wt, MSG_INFO, "FT: No PMKID in RSNE");
+			return;
+		}
+
+		if (os_memcmp_const(parse.rsn_pmkid, sta->pmk_r1_name,
+				    WPA_PMK_NAME_LEN) != 0) {
+			add_note(wt, MSG_INFO,
+				 "FT: PMKID in Reassoc Req did not match PMKR1Name");
+			wpa_hexdump(MSG_DEBUG,
+				    "FT: Received RSNE[PMKR1Name]",
+				    parse.rsn_pmkid, WPA_PMK_NAME_LEN);
+			wpa_hexdump(MSG_DEBUG,
+				    "FT: Previously derived PMKR1Name",
+				    sta->pmk_r1_name, WPA_PMK_NAME_LEN);
+			return;
+		}
+
+		mde = (struct rsn_mdie *) parse.mdie;
+		if (!mde || parse.mdie_len < sizeof(*mde) ||
+		    os_memcmp(mde->mobility_domain, bss->mdid,
+			      MOBILITY_DOMAIN_ID_LEN) != 0) {
+			add_note(wt, MSG_INFO, "FT: Invalid MDE");
+		}
+
+		if (use_sha384) {
+			struct rsn_ftie_sha384 *fte;
+
+			fte = (struct rsn_ftie_sha384 *) parse.ftie;
+			if (!fte || parse.ftie_len < sizeof(*fte)) {
+				add_note(wt, MSG_INFO, "FT: Invalid FTE");
+				return;
+			}
+
+			anonce = fte->anonce;
+			snonce = fte->snonce;
+			fte_elem_count = fte->mic_control[1];
+			fte_mic = fte->mic;
+		} else {
+			struct rsn_ftie *fte;
+
+			fte = (struct rsn_ftie *) parse.ftie;
+			if (!fte || parse.ftie_len < sizeof(*fte)) {
+				add_note(wt, MSG_INFO, "FT: Invalid FTIE");
+				return;
+			}
+
+			anonce = fte->anonce;
+			snonce = fte->snonce;
+			fte_elem_count = fte->mic_control[1];
+			fte_mic = fte->mic;
+		}
+
+		if (os_memcmp(snonce, sta->snonce, WPA_NONCE_LEN) != 0) {
+			add_note(wt, MSG_INFO, "FT: SNonce mismatch in FTIE");
+			wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
+				    snonce, WPA_NONCE_LEN);
+			wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
+				    sta->snonce, WPA_NONCE_LEN);
+			return;
+		}
+
+		if (os_memcmp(anonce, sta->anonce, WPA_NONCE_LEN) != 0) {
+			add_note(wt, MSG_INFO, "FT: ANonce mismatch in FTIE");
+			wpa_hexdump(MSG_DEBUG, "FT: Received ANonce",
+				    anonce, WPA_NONCE_LEN);
+			wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce",
+				    sta->anonce, WPA_NONCE_LEN);
+			return;
+		}
+
+		if (!parse.r0kh_id) {
+			add_note(wt, MSG_INFO, "FT: No R0KH-ID subelem in FTE");
+			return;
+		}
+		os_memcpy(bss->r0kh_id, parse.r0kh_id, parse.r0kh_id_len);
+		bss->r0kh_id_len = parse.r0kh_id_len;
+
+		if (!parse.r1kh_id) {
+			add_note(wt, MSG_INFO, "FT: No R1KH-ID subelem in FTE");
+			return;
+		}
+
+		os_memcpy(bss->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN);
+
+		if (!parse.rsn_pmkid ||
+		    os_memcmp_const(parse.rsn_pmkid, sta->pmk_r1_name,
+				    WPA_PMK_NAME_LEN)) {
+			add_note(wt, MSG_INFO,
+				 "FT: No matching PMKR1Name (PMKID) in RSNE (pmkid=%d)",
+				 !!parse.rsn_pmkid);
+			return;
+		}
+
+		count = 3;
+		if (parse.ric)
+			count += ieee802_11_ie_count(parse.ric, parse.ric_len);
+		if (parse.rsnxe)
+			count++;
+		if (fte_elem_count != count) {
+			add_note(wt, MSG_INFO,
+				 "FT: Unexpected IE count in MIC Control: received %u expected %u",
+				 fte_elem_count, count);
+			return;
+		}
+
+		if (wpa_key_mgmt_fils(sta->key_mgmt)) {
+			kck = sta->ptk.kck2;
+			kck_len = sta->ptk.kck2_len;
+		} else {
+			kck = sta->ptk.kck;
+			kck_len = sta->ptk.kck_len;
+		}
+		if (wpa_ft_mic(sta->key_mgmt, kck, kck_len,
+			       sta->addr, bss->bssid, 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) {
+			add_note(wt, MSG_INFO, "FT: Failed to calculate MIC");
+			return;
+		}
+
+		if (os_memcmp_const(mic, fte_mic, mic_len) != 0) {
+			add_note(wt, MSG_INFO, "FT: Invalid MIC in FTE");
+			wpa_printf(MSG_DEBUG,
+				   "FT: addr=" MACSTR " auth_addr=" MACSTR,
+				   MAC2STR(sta->addr),
+				   MAC2STR(bss->bssid));
+			wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC",
+				    fte_mic, mic_len);
+			wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC",
+				    mic, mic_len);
+			wpa_hexdump(MSG_MSGDUMP, "FT: MDE",
+				    parse.mdie - 2, parse.mdie_len + 2);
+			wpa_hexdump(MSG_MSGDUMP, "FT: FTE",
+				    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;
+		}
+
+		add_note(wt, MSG_INFO, "FT: Valid FTE MIC");
+	}
+}
+
+
+static void process_gtk_subelem(struct wlantest *wt, struct wlantest_bss *bss,
+				struct wlantest_sta *sta,
+				const u8 *kek, size_t kek_len,
+				const u8 *gtk_elem,
+				size_t gtk_elem_len)
+{
+	u8 gtk[32];
+	int keyidx;
+	enum wpa_alg alg;
+	size_t gtk_len, keylen;
+	const u8 *rsc;
+
+	if (!gtk_elem) {
+		add_note(wt, MSG_INFO, "FT: No GTK included in FTE");
+		return;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "FT: Received GTK in Reassoc Resp",
+		    gtk_elem, gtk_elem_len);
+
+	if (gtk_elem_len < 11 + 24 || (gtk_elem_len - 11) % 8 ||
+	    gtk_elem_len - 19 > sizeof(gtk)) {
+		add_note(wt, MSG_INFO, "FT: Invalid GTK sub-elem length %zu",
+			 gtk_elem_len);
+		return;
+	}
+	gtk_len = gtk_elem_len - 19;
+	if (aes_unwrap(kek, kek_len, gtk_len / 8, gtk_elem + 11, gtk)) {
+		add_note(wt, MSG_INFO,
+			 "FT: AES unwrap failed - could not decrypt GTK");
+		return;
+	}
+
+	keylen = wpa_cipher_key_len(bss->group_cipher);
+	alg = wpa_cipher_to_alg(bss->group_cipher);
+	if (alg == WPA_ALG_NONE) {
+		add_note(wt, MSG_INFO, "FT: Unsupported Group Cipher %d",
+			 bss->group_cipher);
+		return;
+	}
+
+	if (gtk_len < keylen) {
+		add_note(wt, MSG_INFO, "FT: Too short GTK in FTE");
+		return;
+	}
+
+	/* Key Info[2] | Key Length[1] | RSC[8] | Key[5..32]. */
+
+	keyidx = WPA_GET_LE16(gtk_elem) & 0x03;
+
+	if (gtk_elem[2] != keylen) {
+		add_note(wt, MSG_INFO,
+			 "FT: GTK length mismatch: received %u negotiated %zu",
+			 gtk_elem[2], keylen);
+		return;
+	}
+
+	add_note(wt, MSG_DEBUG, "GTK KeyID=%u", keyidx);
+	wpa_hexdump(MSG_DEBUG, "FT: GTK from Reassoc Resp", gtk, keylen);
+	if (bss->group_cipher == WPA_CIPHER_TKIP) {
+		/* Swap Tx/Rx keys for Michael MIC */
+		u8 tmp[8];
+
+		os_memcpy(tmp, gtk + 16, 8);
+		os_memcpy(gtk + 16, gtk + 24, 8);
+		os_memcpy(gtk + 24, tmp, 8);
+	}
+
+	bss->gtk_len[keyidx] = gtk_len;
+	sta->gtk_len = gtk_len;
+	os_memcpy(bss->gtk[keyidx], gtk, gtk_len);
+	os_memcpy(sta->gtk, gtk, gtk_len);
+	rsc = gtk_elem + 2;
+	bss->rsc[keyidx][0] = rsc[5];
+	bss->rsc[keyidx][1] = rsc[4];
+	bss->rsc[keyidx][2] = rsc[3];
+	bss->rsc[keyidx][3] = rsc[2];
+	bss->rsc[keyidx][4] = rsc[1];
+	bss->rsc[keyidx][5] = rsc[0];
+	bss->gtk_idx = keyidx;
+	sta->gtk_idx = keyidx;
+	wpa_hexdump(MSG_DEBUG, "RSC", bss->rsc[keyidx], 6);
+}
+
+
+static void process_igtk_subelem(struct wlantest *wt, struct wlantest_bss *bss,
+				 struct wlantest_sta *sta,
+				 const u8 *kek, size_t kek_len,
+				 const u8 *igtk_elem, size_t igtk_elem_len)
+{
+	u8 igtk[WPA_IGTK_MAX_LEN];
+	size_t igtk_len;
+	u16 keyidx;
+	const u8 *ipn;
+
+	if (bss->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC &&
+	    bss->mgmt_group_cipher != WPA_CIPHER_BIP_GMAC_128 &&
+	    bss->mgmt_group_cipher != WPA_CIPHER_BIP_GMAC_256 &&
+	    bss->mgmt_group_cipher != WPA_CIPHER_BIP_CMAC_256)
+		return;
+
+	if (!igtk_elem) {
+		add_note(wt, MSG_INFO, "FT: No IGTK included in FTE");
+		return;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "FT: Received IGTK in Reassoc Resp",
+		    igtk_elem, igtk_elem_len);
+
+	igtk_len = wpa_cipher_key_len(bss->mgmt_group_cipher);
+	if (igtk_elem_len != 2 + 6 + 1 + igtk_len + 8) {
+		add_note(wt, MSG_INFO, "FT: Invalid IGTK sub-elem length %zu",
+			 igtk_elem_len);
+		return;
+	}
+	if (igtk_elem[8] != igtk_len) {
+		add_note(wt, MSG_INFO,
+			 "FT: Invalid IGTK sub-elem Key Length %d",
+			 igtk_elem[8]);
+		return;
+	}
+
+	if (aes_unwrap(kek, kek_len, igtk_len / 8, igtk_elem + 9, igtk)) {
+		add_note(wt, MSG_INFO,
+			 "FT: AES unwrap failed - could not decrypt IGTK");
+		return;
+	}
+
+	/* KeyID[2] | IPN[6] | Key Length[1] | Key[16+8] */
+
+	keyidx = WPA_GET_LE16(igtk_elem);
+
+	wpa_hexdump(MSG_DEBUG, "FT: IGTK from Reassoc Resp", igtk, igtk_len);
+
+	if (keyidx < 4 || keyidx > 5) {
+		add_note(wt, MSG_INFO, "Unexpected IGTK KeyID %u", keyidx);
+		return;
+	}
+
+	add_note(wt, MSG_DEBUG, "IGTK KeyID %u", keyidx);
+	wpa_hexdump(MSG_DEBUG, "IPN", igtk_elem + 2, 6);
+	wpa_hexdump(MSG_DEBUG, "IGTK", igtk, igtk_len);
+	os_memcpy(bss->igtk[keyidx], igtk, igtk_len);
+	bss->igtk_len[keyidx] = igtk_len;
+	ipn = igtk_elem + 2;
+	bss->ipn[keyidx][0] = ipn[5];
+	bss->ipn[keyidx][1] = ipn[4];
+	bss->ipn[keyidx][2] = ipn[3];
+	bss->ipn[keyidx][3] = ipn[2];
+	bss->ipn[keyidx][4] = ipn[1];
+	bss->ipn[keyidx][5] = ipn[0];
+	bss->igtk_idx = keyidx;
+}
+
+
+static void process_bigtk_subelem(struct wlantest *wt, struct wlantest_bss *bss,
+				  struct wlantest_sta *sta,
+				  const u8 *kek, size_t kek_len,
+				  const u8 *bigtk_elem, size_t bigtk_elem_len)
+{
+	u8 bigtk[WPA_BIGTK_MAX_LEN];
+	size_t bigtk_len;
+	u16 keyidx;
+	const u8 *ipn;
+
+	if (!bigtk_elem ||
+	    (bss->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC &&
+	     bss->mgmt_group_cipher != WPA_CIPHER_BIP_GMAC_128 &&
+	     bss->mgmt_group_cipher != WPA_CIPHER_BIP_GMAC_256 &&
+	     bss->mgmt_group_cipher != WPA_CIPHER_BIP_CMAC_256))
+	    return;
+
+	wpa_hexdump_key(MSG_DEBUG, "FT: Received BIGTK in Reassoc Resp",
+			bigtk_elem, bigtk_elem_len);
+
+	bigtk_len = wpa_cipher_key_len(bss->mgmt_group_cipher);
+	if (bigtk_elem_len != 2 + 6 + 1 + bigtk_len + 8) {
+		add_note(wt, MSG_INFO,
+			 "FT: Invalid BIGTK sub-elem length %zu",
+			 bigtk_elem_len);
+		return;
+	}
+	if (bigtk_elem[8] != bigtk_len) {
+		add_note(wt, MSG_INFO,
+			 "FT: Invalid BIGTK sub-elem Key Length %d",
+			 bigtk_elem[8]);
+		return;
+	}
+
+	if (aes_unwrap(kek, kek_len, bigtk_len / 8, bigtk_elem + 9, bigtk)) {
+		add_note(wt, MSG_INFO,
+			 "FT: AES unwrap failed - could not decrypt BIGTK");
+		return;
+	}
+
+	/* KeyID[2] | IPN[6] | Key Length[1] | Key[16+8] */
+
+	keyidx = WPA_GET_LE16(bigtk_elem);
+
+	wpa_hexdump(MSG_DEBUG, "FT: BIGTK from Reassoc Resp", bigtk, bigtk_len);
+
+	if (keyidx < 6 || keyidx > 7) {
+		add_note(wt, MSG_INFO, "Unexpected BIGTK KeyID %u", keyidx);
+		return;
+	}
+
+	add_note(wt, MSG_DEBUG, "BIGTK KeyID %u", keyidx);
+	wpa_hexdump(MSG_DEBUG, "BIPN", bigtk_elem + 2, 6);
+	wpa_hexdump(MSG_DEBUG, "BIGTK", bigtk, bigtk_len);
+	os_memcpy(bss->igtk[keyidx], bigtk, bigtk_len);
+	bss->igtk_len[keyidx] = bigtk_len;
+	ipn = bigtk_elem + 2;
+	bss->ipn[keyidx][0] = ipn[5];
+	bss->ipn[keyidx][1] = ipn[4];
+	bss->ipn[keyidx][2] = ipn[3];
+	bss->ipn[keyidx][3] = ipn[2];
+	bss->ipn[keyidx][4] = ipn[1];
+	bss->ipn[keyidx][5] = ipn[0];
+	bss->bigtk_idx = keyidx;
+}
+
+
+static void rx_mgmt_reassoc_resp(struct wlantest *wt, const u8 *data,
+				 size_t len)
+{
+	const struct ieee80211_mgmt *mgmt;
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+	u16 capab, status, aid;
+	const u8 *ies;
+	size_t ies_len;
+	struct ieee802_11_elems elems;
+	const u8 *ml;
+
+	mgmt = (const struct ieee80211_mgmt *) data;
+	bss = bss_get(wt, mgmt->bssid);
+	if (bss == NULL)
+		return;
+	sta = sta_get(bss, mgmt->da);
+	if (sta == NULL)
+		return;
+
+	if (len < 24 + 6) {
+		add_note(wt, MSG_INFO, "Too short Reassociation Response frame "
+			 "from " MACSTR, MAC2STR(mgmt->sa));
+		return;
+	}
+
+	ies = mgmt->u.reassoc_resp.variable;
+	ies_len = len - (mgmt->u.reassoc_resp.variable - data);
+
+	capab = le_to_host16(mgmt->u.reassoc_resp.capab_info);
+	status = le_to_host16(mgmt->u.reassoc_resp.status_code);
+	aid = le_to_host16(mgmt->u.reassoc_resp.aid);
+
+	wpa_printf(MSG_DEBUG, "REASSOCRESP " MACSTR " -> " MACSTR
+		   " (capab=0x%x status=%u aid=%u)",
+		   MAC2STR(mgmt->sa), MAC2STR(mgmt->da), capab, status,
+		   aid & 0x3fff);
+
+	ml = get_ml_ie(ies, ies_len, MULTI_LINK_CONTROL_TYPE_BASIC);
+	if (ml)
+		parse_basic_ml(ml + 3, ml[1], true);
+
+	if (sta->auth_alg == WLAN_AUTH_FILS_SK) {
+		const u8 *session, *frame_ad, *frame_ad_end, *encr_end;
+
+		session = get_fils_session(ies, ies_len);
+		if (session) {
+			frame_ad = (const u8 *)
+				&mgmt->u.reassoc_resp.capab_info;
+			frame_ad_end = session + 2 + session[1];
+			encr_end = data + len;
+			decrypt_fils_assoc_resp(wt, bss, sta, data, frame_ad,
+						frame_ad_end, encr_end);
+			ies_len = session - ies;
+		}
+	}
+
+	if (ieee802_11_parse_elems(ies, ies_len, &elems, 0) == ParseFailed) {
+		add_note(wt, MSG_INFO,
+			 "Failed to parse IEs in ReassocResp from " MACSTR,
+			 MAC2STR(mgmt->sa));
+	}
+
+	if (status == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) {
+		if (!elems.timeout_int ||
+		    elems.timeout_int[0] != WLAN_TIMEOUT_ASSOC_COMEBACK) {
+			add_note(wt, MSG_INFO, "No valid Timeout Interval IE "
+				 "with Assoc Comeback time in ReassocResp "
+				 "(status=30) from " MACSTR,
+				 MAC2STR(mgmt->sa));
+		} else {
+			sta->counters[
+				WLANTEST_STA_COUNTER_REASSOCRESP_COMEBACK]++;
+		}
+	}
+
+	if (status)
+		return;
+
+	if ((aid & 0xc000) != 0xc000) {
+		add_note(wt, MSG_DEBUG, "Two MSBs of the AID were not set to 1 "
+			 "in Reassociation Response from " MACSTR,
+			 MAC2STR(mgmt->sa));
+	}
+	sta->aid = aid & 0xc000;
+
+	if (sta->state < STATE2 && !sta->ft_over_ds) {
+		add_note(wt, MSG_DEBUG,
+			 "STA " MACSTR " was not in State 2 when "
+			 "getting associated", MAC2STR(sta->addr));
+	}
+
+	if (sta->state < STATE3) {
+		add_note(wt, MSG_DEBUG, "STA " MACSTR
+			 " moved to State 3 with " MACSTR,
+			 MAC2STR(sta->addr), MAC2STR(bss->bssid));
+		sta->state = STATE3;
+	}
+
+	if (elems.ftie) {
+		struct wpa_ft_ies parse;
+		int use_sha384;
+		struct rsn_mdie *mde;
+		const u8 *anonce, *snonce, *fte_mic;
+		u8 fte_elem_count;
+		unsigned int count;
+		u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
+		size_t mic_len = 16;
+		const u8 *kck, *kek;
+		size_t kck_len, kek_len;
+
+		use_sha384 = wpa_key_mgmt_sha384(sta->key_mgmt);
+
+		if (wpa_ft_parse_ies(ies, ies_len, &parse, sta->key_mgmt) < 0) {
+			add_note(wt, MSG_INFO, "FT: Failed to parse FT IEs");
+			return;
+		}
+
+		if (!parse.rsn) {
+			add_note(wt, MSG_INFO, "FT: No RSNE in Reassoc Resp");
+			return;
+		}
+
+		if (!parse.rsn_pmkid) {
+			add_note(wt, MSG_INFO, "FT: No PMKID in RSNE");
+			return;
+		}
+
+		if (os_memcmp_const(parse.rsn_pmkid, sta->pmk_r1_name,
+				    WPA_PMK_NAME_LEN) != 0) {
+			add_note(wt, MSG_INFO,
+				 "FT: PMKID in Reassoc Resp did not match PMKR1Name");
+			wpa_hexdump(MSG_DEBUG,
+				    "FT: Received RSNE[PMKR1Name]",
+				    parse.rsn_pmkid, WPA_PMK_NAME_LEN);
+			wpa_hexdump(MSG_DEBUG,
+				    "FT: Previously derived PMKR1Name",
+				    sta->pmk_r1_name, WPA_PMK_NAME_LEN);
+			return;
+		}
+
+		mde = (struct rsn_mdie *) parse.mdie;
+		if (!mde || parse.mdie_len < sizeof(*mde) ||
+		    os_memcmp(mde->mobility_domain, bss->mdid,
+			      MOBILITY_DOMAIN_ID_LEN) != 0) {
+			add_note(wt, MSG_INFO, "FT: Invalid MDE");
+		}
+
+		if (use_sha384) {
+			struct rsn_ftie_sha384 *fte;
+
+			fte = (struct rsn_ftie_sha384 *) parse.ftie;
+			if (!fte || parse.ftie_len < sizeof(*fte)) {
+				add_note(wt, MSG_INFO, "FT: Invalid FTE");
+				return;
+			}
+
+			anonce = fte->anonce;
+			snonce = fte->snonce;
+			fte_elem_count = fte->mic_control[1];
+			fte_mic = fte->mic;
+		} else {
+			struct rsn_ftie *fte;
+
+			fte = (struct rsn_ftie *) parse.ftie;
+			if (!fte || parse.ftie_len < sizeof(*fte)) {
+				add_note(wt, MSG_INFO, "FT: Invalid FTIE");
+				return;
+			}
+
+			anonce = fte->anonce;
+			snonce = fte->snonce;
+			fte_elem_count = fte->mic_control[1];
+			fte_mic = fte->mic;
+		}
+
+		if (os_memcmp(snonce, sta->snonce, WPA_NONCE_LEN) != 0) {
+			add_note(wt, MSG_INFO, "FT: SNonce mismatch in FTIE");
+			wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
+				    snonce, WPA_NONCE_LEN);
+			wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
+				    sta->snonce, WPA_NONCE_LEN);
+			return;
+		}
+
+		if (os_memcmp(anonce, sta->anonce, WPA_NONCE_LEN) != 0) {
+			add_note(wt, MSG_INFO, "FT: ANonce mismatch in FTIE");
+			wpa_hexdump(MSG_DEBUG, "FT: Received ANonce",
+				    anonce, WPA_NONCE_LEN);
+			wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce",
+				    sta->anonce, WPA_NONCE_LEN);
+			return;
+		}
+
+		if (!parse.r0kh_id) {
+			add_note(wt, MSG_INFO, "FT: No R0KH-ID subelem in FTE");
+			return;
+		}
+
+		if (parse.r0kh_id_len != bss->r0kh_id_len ||
+		    os_memcmp_const(parse.r0kh_id, bss->r0kh_id,
+				    parse.r0kh_id_len) != 0) {
+			add_note(wt, MSG_INFO,
+				 "FT: R0KH-ID in FTE did not match 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",
+				    bss->r0kh_id, bss->r0kh_id_len);
+			os_memcpy(bss->r0kh_id, parse.r0kh_id,
+				  parse.r0kh_id_len);
+			bss->r0kh_id_len = parse.r0kh_id_len;
+		}
+
+		if (!parse.r1kh_id) {
+			add_note(wt, MSG_INFO, "FT: No R1KH-ID subelem in FTE");
+			return;
+		}
+
+		if (os_memcmp_const(parse.r1kh_id, bss->r1kh_id,
+				    FT_R1KH_ID_LEN) != 0) {
+			add_note(wt, MSG_INFO,
+				 "FT: Unknown R1KH-ID used in ReassocResp");
+			os_memcpy(bss->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN);
+		}
+
+		count = 3;
+		if (parse.ric)
+			count += ieee802_11_ie_count(parse.ric, parse.ric_len);
+		if (parse.rsnxe)
+			count++;
+		if (fte_elem_count != count) {
+			add_note(wt, MSG_INFO,
+				 "FT: Unexpected IE count in MIC Control: received %u expected %u",
+				 fte_elem_count, count);
+			return;
+		}
+
+		if (wpa_key_mgmt_fils(sta->key_mgmt)) {
+			kck = sta->ptk.kck2;
+			kck_len = sta->ptk.kck2_len;
+			kek = sta->ptk.kek2;
+			kek_len = sta->ptk.kek2_len;
+		} else {
+			kck = sta->ptk.kck;
+			kck_len = sta->ptk.kck_len;
+			kek = sta->ptk.kek;
+			kek_len = sta->ptk.kek_len;
+		}
+		if (wpa_ft_mic(sta->key_mgmt, kck, kck_len,
+			       sta->addr, bss->bssid, 6,
+			       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) {
+			add_note(wt, MSG_INFO, "FT: Failed to calculate MIC");
+			return;
+		}
+
+		if (os_memcmp_const(mic, fte_mic, mic_len) != 0) {
+			add_note(wt, MSG_INFO, "FT: Invalid MIC in FTE");
+			wpa_printf(MSG_DEBUG,
+				   "FT: addr=" MACSTR " auth_addr=" MACSTR,
+				   MAC2STR(sta->addr),
+				   MAC2STR(bss->bssid));
+			wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC",
+				    fte_mic, mic_len);
+			wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC",
+				    mic, mic_len);
+			wpa_hexdump(MSG_MSGDUMP, "FT: MDE",
+				    parse.mdie - 2, parse.mdie_len + 2);
+			wpa_hexdump(MSG_MSGDUMP, "FT: FTE",
+				    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;
+		}
+
+		add_note(wt, MSG_INFO, "FT: Valid FTE MIC");
+
+		if (wpa_compare_rsn_ie(wpa_key_mgmt_ft(sta->key_mgmt),
+				       bss->rsnie, 2 + bss->rsnie[1],
+				       parse.rsn - 2, parse.rsn_len + 2)) {
+			add_note(wt, MSG_INFO,
+				 "FT: RSNE mismatch between Beacon/ProbeResp and FT protocol Reassociation Response frame");
+			wpa_hexdump(MSG_INFO, "RSNE in Beacon/ProbeResp",
+				    &bss->rsnie[2], bss->rsnie[1]);
+			wpa_hexdump(MSG_INFO,
+				    "RSNE in FT protocol Reassociation Response frame",
+				    parse.rsn ? parse.rsn - 2 : NULL,
+				    parse.rsn ? parse.rsn_len + 2 : 0);
+		}
+
+		process_gtk_subelem(wt, bss, sta, kek, kek_len,
+				    parse.gtk, parse.gtk_len);
+		process_igtk_subelem(wt, bss, sta, kek, kek_len,
+				     parse.igtk, parse.igtk_len);
+		process_bigtk_subelem(wt, bss, sta, kek, kek_len,
+				      parse.bigtk, parse.bigtk_len);
+	}
+}
+
+
+static void disassoc_all_stas(struct wlantest *wt, struct wlantest_bss *bss)
+{
+	struct wlantest_sta *sta;
+	dl_list_for_each(sta, &bss->sta, struct wlantest_sta, list) {
+		if (sta->state <= STATE2)
+			continue;
+		add_note(wt, MSG_DEBUG, "STA " MACSTR
+			 " moved to State 2 with " MACSTR,
+			 MAC2STR(sta->addr), MAC2STR(bss->bssid));
+		sta->state = STATE2;
+	}
+}
+
+
+static void rx_mgmt_disassoc(struct wlantest *wt, const u8 *data, size_t len,
+			     int valid)
+{
+	const struct ieee80211_mgmt *mgmt;
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+	u16 fc, reason;
+
+	mgmt = (const struct ieee80211_mgmt *) data;
+	bss = bss_get(wt, mgmt->bssid);
+	if (bss == NULL)
+		return;
+	if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0)
+		sta = sta_get(bss, mgmt->da);
+	else
+		sta = sta_get(bss, mgmt->sa);
+
+	if (len < 24 + 2) {
+		add_note(wt, MSG_INFO, "Too short Disassociation frame from "
+			 MACSTR, MAC2STR(mgmt->sa));
+		return;
+	}
+
+	reason = le_to_host16(mgmt->u.disassoc.reason_code);
+	wpa_printf(MSG_DEBUG, "DISASSOC " MACSTR " -> " MACSTR
+		   " (reason=%u) (valid=%d)",
+		   MAC2STR(mgmt->sa), MAC2STR(mgmt->da),
+		   reason, valid);
+	wpa_hexdump(MSG_MSGDUMP, "DISASSOC payload", data + 24, len - 24);
+
+	if (sta == NULL) {
+		if (valid && mgmt->da[0] == 0xff)
+			disassoc_all_stas(wt, bss);
+		return;
+	}
+
+	if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0) {
+		sta->counters[valid ? WLANTEST_STA_COUNTER_VALID_DISASSOC_RX :
+			      WLANTEST_STA_COUNTER_INVALID_DISASSOC_RX]++;
+		if (sta->pwrmgt && !sta->pspoll)
+			sta->counters[
+				WLANTEST_STA_COUNTER_DISASSOC_RX_ASLEEP]++;
+		else
+			sta->counters[
+				WLANTEST_STA_COUNTER_DISASSOC_RX_AWAKE]++;
+
+		fc = le_to_host16(mgmt->frame_control);
+		if (!(fc & WLAN_FC_ISWEP) && reason == 6)
+			sta->counters[WLANTEST_STA_COUNTER_DISASSOC_RX_RC6]++;
+		else if (!(fc & WLAN_FC_ISWEP) && reason == 7)
+			sta->counters[WLANTEST_STA_COUNTER_DISASSOC_RX_RC7]++;
+	} else
+		sta->counters[valid ? WLANTEST_STA_COUNTER_VALID_DISASSOC_TX :
+			      WLANTEST_STA_COUNTER_INVALID_DISASSOC_TX]++;
+
+	if (!valid) {
+		add_note(wt, MSG_INFO, "Do not change STA " MACSTR " State "
+			 "since Disassociation frame was not protected "
+			 "correctly", MAC2STR(sta->addr));
+		return;
+	}
+
+	if (sta->state < STATE2) {
+		add_note(wt, MSG_DEBUG,
+			 "STA " MACSTR " was not in State 2 or 3 "
+			 "when getting disassociated", MAC2STR(sta->addr));
+	}
+
+	if (sta->state > STATE2) {
+		add_note(wt, MSG_DEBUG, "STA " MACSTR
+			 " moved to State 2 with " MACSTR,
+			 MAC2STR(sta->addr), MAC2STR(bss->bssid));
+		sta->state = STATE2;
+	}
+	tdls_link_down(wt, bss, sta);
+}
+
+
+static void rx_mgmt_action_ft_request(struct wlantest *wt,
+				      const struct ieee80211_mgmt *mgmt,
+				      size_t len)
+{
+	const u8 *ies;
+	size_t ies_len;
+	struct wpa_ft_ies parse;
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+
+	if (len < 24 + 2 + 2 * ETH_ALEN) {
+		add_note(wt, MSG_INFO, "Too short FT Request frame");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "FT Request: STA Address: " MACSTR
+		   " Target AP Address: " MACSTR,
+		   MAC2STR(mgmt->u.action.u.ft_action_req.sta_addr),
+		   MAC2STR(mgmt->u.action.u.ft_action_req.target_ap_addr));
+	ies = mgmt->u.action.u.ft_action_req.variable;
+	ies_len = len - (24 + 2 + 2 * ETH_ALEN);
+	wpa_hexdump(MSG_DEBUG, "FT Request frame body", ies, ies_len);
+
+	if (wpa_ft_parse_ies(ies, ies_len, &parse, 0)) {
+		add_note(wt, MSG_INFO, "Could not parse FT Request frame body");
+		return;
+	}
+
+	bss = bss_get(wt, mgmt->u.action.u.ft_action_resp.target_ap_addr);
+	if (!bss) {
+		add_note(wt, MSG_INFO, "No BSS entry for Target AP");
+		return;
+	}
+
+	sta = sta_get(bss, mgmt->sa);
+	if (!sta)
+		return;
+
+	sta->ft_over_ds = true;
+	sta->key_mgmt = parse.key_mgmt;
+	sta->pairwise_cipher = parse.pairwise_cipher;
+}
+
+
+static void rx_mgmt_action_ft_response(struct wlantest *wt,
+				       struct wlantest_sta *sta,
+				       const struct ieee80211_mgmt *mgmt,
+				       size_t len)
+{
+	struct wlantest_bss *bss;
+	struct wlantest_sta *new_sta;
+	const u8 *ies;
+	size_t ies_len;
+	struct wpa_ft_ies parse;
+	struct wpa_ptk ptk;
+	u8 ptk_name[WPA_PMK_NAME_LEN];
+
+	if (len < 24 + 2 + 2 * ETH_ALEN + 2) {
+		add_note(wt, MSG_INFO, "Too short FT Response frame from "
+			 MACSTR, MAC2STR(mgmt->sa));
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "FT Response: STA Address: " MACSTR
+		   " Target AP Address: " MACSTR " Status Code: %u",
+		   MAC2STR(mgmt->u.action.u.ft_action_resp.sta_addr),
+		   MAC2STR(mgmt->u.action.u.ft_action_resp.target_ap_addr),
+		   le_to_host16(mgmt->u.action.u.ft_action_resp.status_code));
+	ies = mgmt->u.action.u.ft_action_req.variable;
+	ies_len = len - (24 + 2 + 2 * ETH_ALEN);
+	wpa_hexdump(MSG_DEBUG, "FT Response frame body", ies, ies_len);
+
+	if (wpa_ft_parse_ies(ies, ies_len, &parse, 0)) {
+		add_note(wt, MSG_INFO,
+			 "Could not parse FT Response frame body");
+		return;
+	}
+
+	bss = bss_get(wt, mgmt->u.action.u.ft_action_resp.target_ap_addr);
+	if (!bss) {
+		add_note(wt, MSG_INFO, "No BSS entry for Target AP");
+		return;
+	}
+
+	if (parse.r1kh_id)
+		os_memcpy(bss->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN);
+
+	if (wpa_derive_pmk_r1(sta->pmk_r0, sta->pmk_r0_len, sta->pmk_r0_name,
+			      bss->r1kh_id, sta->addr, sta->pmk_r1,
+			      sta->pmk_r1_name) < 0)
+		return;
+	sta->pmk_r1_len = sta->pmk_r0_len;
+
+	new_sta = sta_get(bss, sta->addr);
+	if (!new_sta)
+		return;
+	os_memcpy(new_sta->pmk_r0, sta->pmk_r0, sta->pmk_r0_len);
+	new_sta->pmk_r0_len = sta->pmk_r0_len;
+	os_memcpy(new_sta->pmk_r0_name, sta->pmk_r0_name,
+		  sizeof(sta->pmk_r0_name));
+	os_memcpy(new_sta->pmk_r1, sta->pmk_r1, sta->pmk_r1_len);
+	new_sta->pmk_r1_len = sta->pmk_r1_len;
+	os_memcpy(new_sta->pmk_r1_name, sta->pmk_r1_name,
+		  sizeof(sta->pmk_r1_name));
+	if (!parse.fte_anonce || !parse.fte_snonce ||
+	    wpa_pmk_r1_to_ptk(sta->pmk_r1, sta->pmk_r1_len, parse.fte_snonce,
+			      parse.fte_anonce, new_sta->addr, bss->bssid,
+			      sta->pmk_r1_name, &ptk, ptk_name,
+			      new_sta->key_mgmt, new_sta->pairwise_cipher,
+			      0) < 0)
+		return;
+
+	sta_new_ptk(wt, new_sta, &ptk);
+	os_memcpy(new_sta->snonce, parse.fte_snonce, WPA_NONCE_LEN);
+	os_memcpy(new_sta->anonce, parse.fte_anonce, WPA_NONCE_LEN);
+}
+
+
+static void rx_mgmt_action_ft(struct wlantest *wt, struct wlantest_sta *sta,
+			      const struct ieee80211_mgmt *mgmt,
+			      size_t len, int valid)
+{
+	if (len < 24 + 2) {
+		add_note(wt, MSG_INFO, "Too short FT Action frame from " MACSTR,
+			 MAC2STR(mgmt->sa));
+		return;
+	}
+
+	switch (mgmt->u.action.u.ft_action_req.action) {
+	case 1:
+		rx_mgmt_action_ft_request(wt, mgmt, len);
+		break;
+	case 2:
+		rx_mgmt_action_ft_response(wt, sta, mgmt, len);
+		break;
+	default:
+		add_note(wt, MSG_INFO, "Unsupported FT action value %u from "
+			 MACSTR, mgmt->u.action.u.ft_action_req.action,
+			 MAC2STR(mgmt->sa));
+	}
+}
+
+
+static void rx_mgmt_action_sa_query_req(struct wlantest *wt,
+					struct wlantest_sta *sta,
+					const struct ieee80211_mgmt *mgmt,
+					size_t len, int valid)
+{
+	const u8 *rx_id;
+	u8 *id;
+
+	rx_id = (const u8 *) mgmt->u.action.u.sa_query_req.trans_id;
+	if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0)
+		id = sta->ap_sa_query_tr;
+	else
+		id = sta->sta_sa_query_tr;
+	add_note(wt, MSG_INFO, "SA Query Request " MACSTR " -> " MACSTR
+		 " (trans_id=%02x%02x)%s",
+		 MAC2STR(mgmt->sa), MAC2STR(mgmt->da), rx_id[0], rx_id[1],
+		 valid ? "" : " (invalid protection)");
+	os_memcpy(id, mgmt->u.action.u.sa_query_req.trans_id, 2);
+	if (os_memcmp(mgmt->sa, sta->addr, ETH_ALEN) == 0)
+		sta->counters[valid ?
+			      WLANTEST_STA_COUNTER_VALID_SAQUERYREQ_TX :
+			      WLANTEST_STA_COUNTER_INVALID_SAQUERYREQ_TX]++;
+	else
+		sta->counters[valid ?
+			      WLANTEST_STA_COUNTER_VALID_SAQUERYREQ_RX :
+			      WLANTEST_STA_COUNTER_INVALID_SAQUERYREQ_RX]++;
+}
+
+
+static void rx_mgmt_action_sa_query_resp(struct wlantest *wt,
+					 struct wlantest_sta *sta,
+					 const struct ieee80211_mgmt *mgmt,
+					 size_t len, int valid)
+{
+	const u8 *rx_id;
+	u8 *id;
+	int match;
+
+	rx_id = (const u8 *) mgmt->u.action.u.sa_query_resp.trans_id;
+	if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0)
+		id = sta->sta_sa_query_tr;
+	else
+		id = sta->ap_sa_query_tr;
+	match = os_memcmp(rx_id, id, 2) == 0;
+	add_note(wt, MSG_INFO, "SA Query Response " MACSTR " -> " MACSTR
+		 " (trans_id=%02x%02x; %s)%s",
+		 MAC2STR(mgmt->sa), MAC2STR(mgmt->da), rx_id[0], rx_id[1],
+		 match ? "match" : "mismatch",
+		 valid ? "" : " (invalid protection)");
+	if (os_memcmp(mgmt->sa, sta->addr, ETH_ALEN) == 0)
+		sta->counters[(valid && match) ?
+			      WLANTEST_STA_COUNTER_VALID_SAQUERYRESP_TX :
+			      WLANTEST_STA_COUNTER_INVALID_SAQUERYRESP_TX]++;
+	else
+		sta->counters[(valid && match) ?
+			      WLANTEST_STA_COUNTER_VALID_SAQUERYRESP_RX :
+			      WLANTEST_STA_COUNTER_INVALID_SAQUERYRESP_RX]++;
+}
+
+
+static void rx_mgmt_action_sa_query(struct wlantest *wt,
+				    struct wlantest_sta *sta,
+				    const struct ieee80211_mgmt *mgmt,
+				    size_t len, int valid)
+{
+	if (len < 24 + 2 + WLAN_SA_QUERY_TR_ID_LEN) {
+		add_note(wt, MSG_INFO, "Too short SA Query frame from " MACSTR,
+			 MAC2STR(mgmt->sa));
+		return;
+	}
+
+	if (len > 24 + 2 + WLAN_SA_QUERY_TR_ID_LEN) {
+		size_t elen = len - (24 + 2 + WLAN_SA_QUERY_TR_ID_LEN);
+		add_note(wt, MSG_INFO, "Unexpected %u octets of extra data at "
+			 "the end of SA Query frame from " MACSTR,
+			 (unsigned) elen, MAC2STR(mgmt->sa));
+		wpa_hexdump(MSG_INFO, "SA Query extra data",
+			    ((const u8 *) mgmt) + len - elen, elen);
+	}
+
+	switch (mgmt->u.action.u.sa_query_req.action) {
+	case WLAN_SA_QUERY_REQUEST:
+		rx_mgmt_action_sa_query_req(wt, sta, mgmt, len, valid);
+		break;
+	case WLAN_SA_QUERY_RESPONSE:
+		rx_mgmt_action_sa_query_resp(wt, sta, mgmt, len, valid);
+		break;
+	default:
+		add_note(wt, MSG_INFO, "Unexpected SA Query action value %u "
+			 "from " MACSTR,
+			 mgmt->u.action.u.sa_query_req.action,
+			 MAC2STR(mgmt->sa));
+	}
+}
+
+
+static void
+rx_mgmt_location_measurement_report(struct wlantest *wt,
+				    const struct ieee80211_mgmt *mgmt,
+				    size_t len, bool no_ack)
+{
+	const u8 *pos = mgmt->u.action.u.public_action.variable;
+	const u8 *end = ((const u8 *) mgmt) + len;
+
+	if (end - pos < 1) {
+		add_note(wt, MSG_INFO,
+			 "Too short Location Measurement Report frame from "
+			 MACSTR, MAC2STR(mgmt->sa));
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "Location Measurement Report " MACSTR " --> "
+		   MACSTR " (dialog token %u)",
+		   MAC2STR(mgmt->sa), MAC2STR(mgmt->da), *pos);
+	pos++;
+
+	if (!no_ack)
+		add_note(wt, MSG_INFO,
+			 "Protected Fine Timing Measurement Report incorrectly as an Action frame from "
+			 MACSTR, MAC2STR(mgmt->sa));
+
+	wpa_hexdump(MSG_MSGDUMP, "Location Measurement Report contents",
+		    pos, end - pos);
+}
+
+
+static void rx_mgmt_action_no_bss_public(struct wlantest *wt,
+					 const struct ieee80211_mgmt *mgmt,
+					 size_t len, bool no_ack)
+{
+	switch (mgmt->u.action.u.public_action.action) {
+	case WLAN_PA_LOCATION_MEASUREMENT_REPORT:
+		rx_mgmt_location_measurement_report(wt, mgmt, len, no_ack);
+		break;
+	}
+}
+
+
+static void rx_mgmt_prot_ftm_request(struct wlantest *wt,
+				     const struct ieee80211_mgmt *mgmt,
+				     size_t len, bool no_ack)
+{
+	wpa_printf(MSG_DEBUG, "Protected Fine Timing Measurement Request "
+		   MACSTR " --> " MACSTR,
+		   MAC2STR(mgmt->sa), MAC2STR(mgmt->da));
+	if (no_ack)
+		add_note(wt, MSG_INFO,
+			 "Protected Fine Timing Measurement Request incorrectly as an Action No Ack frame from "
+			 MACSTR, MAC2STR(mgmt->sa));
+}
+
+
+static void rx_mgmt_prot_ftm(struct wlantest *wt,
+			     const struct ieee80211_mgmt *mgmt,
+			     size_t len, bool no_ack)
+{
+	wpa_printf(MSG_DEBUG, "Protected Fine Timing Measurement "
+		   MACSTR " --> " MACSTR,
+		   MAC2STR(mgmt->sa), MAC2STR(mgmt->da));
+	if (no_ack)
+		add_note(wt, MSG_INFO,
+			 "Protected Fine Timing Measurement incorrectly as an Action No Ack frame from "
+			 MACSTR, MAC2STR(mgmt->sa));
+}
+
+
+static void rx_mgmt_prot_ftm_report(struct wlantest *wt,
+				     const struct ieee80211_mgmt *mgmt,
+				     size_t len, bool no_ack)
+{
+	wpa_printf(MSG_DEBUG, "Protected Fine Timing Measurement Report "
+		   MACSTR " --> " MACSTR,
+		   MAC2STR(mgmt->sa), MAC2STR(mgmt->da));
+	if (!no_ack)
+		add_note(wt, MSG_INFO,
+			 "Protected Fine Timing Measurement Report incorrectly as an Action frame from "
+			 MACSTR, MAC2STR(mgmt->sa));
+}
+
+
+static void
+rx_mgmt_action_no_bss_protected_ftm(struct wlantest *wt,
+				    const struct ieee80211_mgmt *mgmt,
+				    size_t len, bool no_ack)
+{
+	switch (mgmt->u.action.u.public_action.action) {
+	case WLAN_PROT_FTM_REQUEST:
+		rx_mgmt_prot_ftm_request(wt, mgmt, len, no_ack);
+		break;
+	case WLAN_PROT_FTM:
+		rx_mgmt_prot_ftm(wt, mgmt, len, no_ack);
+		break;
+	case WLAN_PROT_FTM_REPORT:
+		rx_mgmt_prot_ftm_report(wt, mgmt, len, no_ack);
+		break;
+	}
+}
+
+
+static void rx_mgmt_action_no_bss(struct wlantest *wt,
+				  const struct ieee80211_mgmt *mgmt, size_t len,
+				  bool no_ack)
+{
+	switch (mgmt->u.action.category) {
+	case WLAN_ACTION_PUBLIC:
+		rx_mgmt_action_no_bss_public(wt, mgmt, len, no_ack);
+		break;
+	case WLAN_ACTION_PROTECTED_FTM:
+		rx_mgmt_action_no_bss_protected_ftm(wt, mgmt, len, no_ack);
+		break;
+	}
+}
+
+
+static void rx_mgmt_action(struct wlantest *wt, const u8 *data, size_t len,
+			   int valid, bool no_ack)
+{
+	const struct ieee80211_mgmt *mgmt;
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+
+	mgmt = (const struct ieee80211_mgmt *) data;
+	if (mgmt->da[0] & 0x01) {
+		add_note(wt, MSG_DEBUG, "Group addressed Action frame: DA="
+			 MACSTR " SA=" MACSTR " BSSID=" MACSTR
+			 " category=%u",
+			 MAC2STR(mgmt->da), MAC2STR(mgmt->sa),
+			 MAC2STR(mgmt->bssid), mgmt->u.action.category);
+		return; /* Ignore group addressed Action frames for now */
+	}
+
+	if (len < 24 + 2) {
+		add_note(wt, MSG_INFO, "Too short Action frame from " MACSTR,
+			 MAC2STR(mgmt->sa));
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "ACTION%s " MACSTR " -> " MACSTR
+		   " BSSID=" MACSTR " (category=%u) (valid=%d)",
+		   no_ack ? "-NO-ACK" : "",
+		   MAC2STR(mgmt->sa), MAC2STR(mgmt->da), MAC2STR(mgmt->bssid),
+		   mgmt->u.action.category, valid);
+	wpa_hexdump(MSG_MSGDUMP, "ACTION payload", data + 24, len - 24);
+
+	if (is_broadcast_ether_addr(mgmt->bssid)) {
+		rx_mgmt_action_no_bss(wt, mgmt, len, no_ack);
+		return;
+	}
+	bss = bss_get(wt, mgmt->bssid);
+	if (bss == NULL)
+		return;
+	if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0) {
+		sta = sta_find_mlo(wt, bss, mgmt->da);
+		if (!sta)
+			sta = sta_get(bss, mgmt->da);
+	} else {
+		sta = sta_find_mlo(wt, bss, mgmt->sa);
+		if (!sta)
+			sta = sta_get(bss, mgmt->sa);
+	}
+	if (sta == NULL)
+		return;
+
+	if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
+	    sta->state < STATE3) {
+		add_note(wt, MSG_INFO, "Action frame sent when STA is not in "
+			 "State 3 (SA=" MACSTR " DATA=" MACSTR ")",
+			 MAC2STR(mgmt->sa), MAC2STR(mgmt->da));
+	}
+
+	switch (mgmt->u.action.category) {
+	case WLAN_ACTION_FT:
+		rx_mgmt_action_ft(wt, sta, mgmt, len, valid);
+		break;
+	case WLAN_ACTION_SA_QUERY:
+		rx_mgmt_action_sa_query(wt, sta, mgmt, len, valid);
+		break;
+	}
+}
+
+
+static int check_mmie_mic(unsigned int mgmt_group_cipher,
+			  const u8 *igtk, size_t igtk_len,
+			  const u8 *data, size_t len)
+{
+	u8 *buf;
+	u8 mic[16];
+	u16 fc;
+	const struct ieee80211_hdr *hdr;
+	int ret, mic_len;
+
+	if (!mgmt_group_cipher || igtk_len < 16)
+		return -1;
+	mic_len = mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC ? 8 : 16;
+
+	if (len < 24 || len - 24 < mic_len)
+		return -1;
+
+	buf = os_malloc(len + 20 - 24);
+	if (buf == NULL)
+		return -1;
+
+	/* BIP AAD: FC(masked) A1 A2 A3 */
+	hdr = (const struct ieee80211_hdr *) data;
+	fc = le_to_host16(hdr->frame_control);
+	fc &= ~(WLAN_FC_RETRY | WLAN_FC_PWRMGT | WLAN_FC_MOREDATA);
+	WPA_PUT_LE16(buf, fc);
+	os_memcpy(buf + 2, hdr->addr1, 3 * ETH_ALEN);
+
+	/* Frame body with MMIE MIC masked to zero */
+	os_memcpy(buf + 20, data + 24, len - 24 - mic_len);
+	os_memset(buf + 20 + len - 24 - mic_len, 0, mic_len);
+
+	if (WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON) {
+		/* Timestamp field masked to zero */
+		os_memset(buf + 20, 0, 8);
+	}
+
+	wpa_hexdump(MSG_MSGDUMP, "BIP: AAD|Body(masked)", buf, len + 20 - 24);
+	/* MIC = L(AES-128-CMAC(AAD || Frame Body(masked)), 0, 64) */
+	if (mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) {
+		ret = omac1_aes_128(igtk, buf, len + 20 - 24, mic);
+	} else if (mgmt_group_cipher == WPA_CIPHER_BIP_CMAC_256) {
+		ret = omac1_aes_256(igtk, buf, len + 20 - 24, mic);
+	} else if (mgmt_group_cipher == WPA_CIPHER_BIP_GMAC_128 ||
+		 mgmt_group_cipher == WPA_CIPHER_BIP_GMAC_256) {
+		u8 nonce[12], *npos;
+		const u8 *ipn;
+
+		ipn = data + len - mic_len - 6;
+
+		/* Nonce: A2 | IPN */
+		os_memcpy(nonce, hdr->addr2, ETH_ALEN);
+		npos = nonce + ETH_ALEN;
+		*npos++ = ipn[5];
+		*npos++ = ipn[4];
+		*npos++ = ipn[3];
+		*npos++ = ipn[2];
+		*npos++ = ipn[1];
+		*npos++ = ipn[0];
+
+		ret = aes_gmac(igtk, igtk_len, nonce, sizeof(nonce),
+			       buf, len + 20 - 24, mic);
+	} else {
+		ret = -1;
+	}
+	if (ret < 0) {
+		os_free(buf);
+		return -1;
+	}
+
+	os_free(buf);
+
+	if (os_memcmp(data + len - mic_len, mic, mic_len) != 0)
+		return -1;
+
+	return 0;
+}
+
+
+static int check_bip(struct wlantest *wt, const u8 *data, size_t len)
+{
+	const struct ieee80211_mgmt *mgmt;
+	u16 fc, stype;
+	const u8 *mmie;
+	u16 keyid;
+	struct wlantest_bss *bss;
+	size_t mic_len;
+
+	mgmt = (const struct ieee80211_mgmt *) data;
+	fc = le_to_host16(mgmt->frame_control);
+	stype = WLAN_FC_GET_STYPE(fc);
+
+	if (stype == WLAN_FC_STYPE_ACTION ||
+	    stype == WLAN_FC_STYPE_ACTION_NO_ACK) {
+		if (len < 24 + 1)
+			return 0;
+		if (mgmt->u.action.category == WLAN_ACTION_PUBLIC)
+			return 0; /* Not a robust management frame */
+	}
+
+	bss = bss_get(wt, mgmt->bssid);
+	if (bss == NULL)
+		return 0; /* No key known yet */
+
+	mic_len = bss->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC ? 8 : 16;
+
+	if (len < 24 + 10 + mic_len ||
+	    data[len - (10 + mic_len)] != WLAN_EID_MMIE ||
+	    data[len - (10 + mic_len - 1)] != 8 + mic_len) {
+		/* No MMIE */
+		if (bss->rsn_capab & WPA_CAPABILITY_MFPC) {
+			add_note(wt, MSG_INFO, "Robust group-addressed "
+				 "management frame sent without BIP by "
+				 MACSTR, MAC2STR(mgmt->sa));
+			bss->counters[WLANTEST_BSS_COUNTER_MISSING_BIP_MMIE]++;
+			return -1;
+		}
+		return 0;
+	}
+
+	mmie = data + len - (8 + mic_len);
+	keyid = WPA_GET_LE16(mmie);
+	if (keyid & 0xf000) {
+		add_note(wt, MSG_INFO, "MMIE KeyID reserved bits not zero "
+			 "(%04x) from " MACSTR, keyid, MAC2STR(mgmt->sa));
+		keyid &= 0x0fff;
+	}
+	if (keyid < 4 || keyid > 5) {
+		add_note(wt, MSG_INFO, "Unexpected MMIE KeyID %u from " MACSTR,
+			 keyid, MAC2STR(mgmt->sa));
+		bss->counters[WLANTEST_BSS_COUNTER_INVALID_BIP_MMIE]++;
+		return 0;
+	}
+	wpa_printf(MSG_DEBUG, "MMIE KeyID %u", keyid);
+	wpa_hexdump(MSG_MSGDUMP, "MMIE IPN", mmie + 2, 6);
+	wpa_hexdump(MSG_MSGDUMP, "MMIE MIC", mmie + 8, mic_len);
+
+	if (!bss->igtk_len[keyid]) {
+		add_note(wt, MSG_DEBUG, "No IGTK known to validate BIP frame");
+		return 0;
+	}
+
+	if (os_memcmp(mmie + 2, bss->ipn[keyid], 6) <= 0) {
+		add_note(wt, MSG_INFO, "BIP replay detected: SA=" MACSTR,
+			 MAC2STR(mgmt->sa));
+		wpa_hexdump(MSG_INFO, "RX IPN", mmie + 2, 6);
+		wpa_hexdump(MSG_INFO, "Last RX IPN", bss->ipn[keyid], 6);
+	}
+
+	if (check_mmie_mic(bss->mgmt_group_cipher, bss->igtk[keyid],
+			   bss->igtk_len[keyid], data, len) < 0) {
+		add_note(wt, MSG_INFO, "Invalid MMIE MIC in a frame from "
+			 MACSTR, MAC2STR(mgmt->sa));
+		bss->counters[WLANTEST_BSS_COUNTER_INVALID_BIP_MMIE]++;
+		return -1;
+	}
+
+	add_note(wt, MSG_DEBUG, "Valid MMIE MIC");
+	os_memcpy(bss->ipn[keyid], mmie + 2, 6);
+	bss->counters[WLANTEST_BSS_COUNTER_VALID_BIP_MMIE]++;
+
+	if (stype == WLAN_FC_STYPE_DEAUTH)
+		bss->counters[WLANTEST_BSS_COUNTER_BIP_DEAUTH]++;
+	else if (stype == WLAN_FC_STYPE_DISASSOC)
+		bss->counters[WLANTEST_BSS_COUNTER_BIP_DISASSOC]++;
+
+	return 0;
+}
+
+
+static u8 * try_tk(struct wpa_ptk *ptk, size_t ptk_len,
+		   const u8 *data, size_t len, size_t *dlen)
+{
+	const struct ieee80211_hdr *hdr;
+	u8 *decrypted, *frame;
+
+	hdr = (const struct ieee80211_hdr *) data;
+	if (ptk_len == 16) {
+		decrypted = ccmp_decrypt(ptk->tk, hdr, NULL, NULL, NULL,
+					 data + 24, len - 24, dlen);
+		if (!decrypted)
+			decrypted = gcmp_decrypt(ptk->tk, 16, hdr, NULL, NULL,
+						 NULL,
+						 data + 24, len - 24, dlen);
+	} else if (ptk_len == 32) {
+		decrypted = ccmp_256_decrypt(ptk->tk, hdr, NULL, NULL, NULL,
+					     data + 24, len - 24, dlen);
+		if (!decrypted)
+			decrypted = gcmp_decrypt(ptk->tk, 32, hdr, NULL, NULL,
+						 NULL,
+						 data + 24, len - 24, dlen);
+	} else {
+		decrypted = NULL;
+	}
+	if (!decrypted)
+		return NULL;
+
+	frame = os_malloc(24 + *dlen);
+	if (frame) {
+		os_memcpy(frame, data, 24);
+		os_memcpy(frame + 24, decrypted, *dlen);
+		*dlen += 24;
+	}
+	os_free(decrypted);
+	return frame;
+}
+
+
+static u8 * mgmt_decrypt_tk(struct wlantest *wt, const u8 *data, size_t len,
+			    size_t *dlen)
+{
+	struct wlantest_ptk *ptk;
+	u8 *decrypted;
+	int prev_level = wpa_debug_level;
+	int keyid;
+
+	keyid = data[24 + 3] >> 6;
+
+	wpa_debug_level = MSG_WARNING;
+	dl_list_for_each(ptk, &wt->ptk, struct wlantest_ptk, list) {
+		decrypted = try_tk(&ptk->ptk, ptk->ptk_len, data, len, dlen);
+		if (decrypted) {
+			wpa_debug_level = prev_level;
+			add_note(wt, MSG_DEBUG,
+				 "Found TK match from the list of all known TKs");
+			write_decrypted_note(wt, decrypted, ptk->ptk.tk,
+					     ptk->ptk.tk_len, keyid);
+			return decrypted;
+		}
+	}
+	wpa_debug_level = prev_level;
+
+	return NULL;
+}
+
+
+static u8 * mgmt_decrypt(struct wlantest *wt, const u8 *data, size_t len,
+			 size_t *dlen)
+{
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+	const struct ieee80211_hdr *hdr;
+	int keyid;
+	u8 *decrypted, *frame = NULL;
+	u8 pn[6], *rsc;
+	u16 fc;
+	u8 mask;
+	size_t hdrlen = 24;
+
+	hdr = (const struct ieee80211_hdr *) data;
+	fc = le_to_host16(hdr->frame_control);
+
+	if (fc & WLAN_FC_HTC)
+		hdrlen += 4; /* HT Control field */
+
+	if (len < hdrlen + 4)
+		return NULL;
+
+	if (!(data[hdrlen + 3] & 0x20)) {
+		add_note(wt, MSG_INFO, "Expected CCMP/GCMP frame from " MACSTR
+			 " did not have ExtIV bit set to 1",
+			 MAC2STR(hdr->addr2));
+		return NULL;
+	}
+
+	mask = 0x1f;
+	if (WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION ||
+	    WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION_NO_ACK)
+		mask &= ~0x10; /* FTM */
+	if (data[hdrlen + 2] != 0 || (data[hdrlen + 3] & mask) != 0) {
+		add_note(wt, MSG_INFO, "CCMP/GCMP mgmt frame from " MACSTR
+			 " used non-zero reserved bit", MAC2STR(hdr->addr2));
+	}
+
+	keyid = data[hdrlen + 3] >> 6;
+	if (keyid != 0) {
+		add_note(wt, MSG_INFO, "Unexpected non-zero KeyID %d in "
+			 "individually addressed Management frame from "
+			 MACSTR, keyid, MAC2STR(hdr->addr2));
+	}
+
+	bss = bss_get(wt, hdr->addr3);
+	if (bss == NULL)
+		return mgmt_decrypt_tk(wt, data, len, dlen);
+	if (os_memcmp(hdr->addr1, hdr->addr3, ETH_ALEN) == 0) {
+		sta = sta_find_mlo(wt, bss, hdr->addr2);
+		if (!sta)
+			sta = sta_get(bss, hdr->addr2);
+	} else {
+		sta = sta_find_mlo(wt, bss, hdr->addr1);
+		if (!sta)
+			sta = sta_get(bss, hdr->addr1);
+	}
+	if (sta == NULL || !sta->ptk_set) {
+		decrypted = mgmt_decrypt_tk(wt, data, len, dlen);
+		if (!decrypted)
+			add_note(wt, MSG_MSGDUMP,
+				 "No PTK known to decrypt the frame");
+		return decrypted;
+	}
+
+	if (os_memcmp(hdr->addr1, hdr->addr3, ETH_ALEN) == 0)
+		rsc = sta->rsc_tods[16];
+	else
+		rsc = sta->rsc_fromds[16];
+
+	ccmp_get_pn(pn, data + hdrlen);
+	if (os_memcmp(pn, rsc, 6) <= 0) {
+		u16 seq_ctrl = le_to_host16(hdr->seq_ctrl);
+		add_note(wt, MSG_INFO, "replay detected: A1=" MACSTR
+			 " A2=" MACSTR " A3=" MACSTR " seq=%u frag=%u%s",
+			 MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+			 MAC2STR(hdr->addr3),
+			 WLAN_GET_SEQ_SEQ(seq_ctrl),
+			 WLAN_GET_SEQ_FRAG(seq_ctrl),
+			 (le_to_host16(hdr->frame_control) & WLAN_FC_RETRY) ?
+			 " Retry" : "");
+		wpa_hexdump(MSG_INFO, "RX PN", pn, 6);
+		wpa_hexdump(MSG_INFO, "RSC", rsc, 6);
+	}
+
+	if (sta->pairwise_cipher == WPA_CIPHER_CCMP_256) {
+		decrypted = ccmp_256_decrypt(sta->ptk.tk, hdr, NULL, NULL, NULL,
+					     data + hdrlen, len - hdrlen, dlen);
+		write_decrypted_note(wt, decrypted, sta->ptk.tk, 32, keyid);
+	} else if (sta->pairwise_cipher == WPA_CIPHER_GCMP ||
+		   sta->pairwise_cipher == WPA_CIPHER_GCMP_256) {
+		decrypted = gcmp_decrypt(sta->ptk.tk, sta->ptk.tk_len, hdr,
+					 NULL, NULL, NULL,
+					 data + hdrlen, len - hdrlen, dlen);
+		write_decrypted_note(wt, decrypted, sta->ptk.tk,
+				     sta->ptk.tk_len, keyid);
+	} else {
+		decrypted = ccmp_decrypt(sta->ptk.tk, hdr, NULL, NULL, NULL,
+					 data + hdrlen, len - hdrlen, dlen);
+		write_decrypted_note(wt, decrypted, sta->ptk.tk, 16, keyid);
+	}
+	if (decrypted) {
+		os_memcpy(rsc, pn, 6);
+		frame = os_malloc(hdrlen + *dlen);
+		if (frame) {
+			os_memcpy(frame, data, hdrlen);
+			os_memcpy(frame + hdrlen, decrypted, *dlen);
+			*dlen += hdrlen;
+		}
+	} else {
+		/* Assume the frame was corrupted and there was no FCS to check.
+		 * Allow retry of this particular frame to be processed so that
+		 * it could end up getting decrypted if it was received without
+		 * corruption. */
+		sta->allow_duplicate = 1;
+	}
+
+	os_free(decrypted);
+
+	return frame;
+}
+
+
+static bool is_robust_action_category(u8 category)
+{
+	return category != WLAN_ACTION_PUBLIC &&
+		category != WLAN_ACTION_HT &&
+		category != WLAN_ACTION_UNPROTECTED_WNM &&
+		category != WLAN_ACTION_SELF_PROTECTED &&
+		category != WLAN_ACTION_UNPROTECTED_DMG &&
+		category != WLAN_ACTION_VHT &&
+		category != WLAN_ACTION_UNPROTECTED_S1G &&
+		category != WLAN_ACTION_HE &&
+		category != WLAN_ACTION_EHT &&
+		category != WLAN_ACTION_VENDOR_SPECIFIC;
+}
+
+
+static int check_mgmt_ccmp_gcmp(struct wlantest *wt, const u8 *data, size_t len)
+{
+	const struct ieee80211_mgmt *mgmt;
+	u16 fc;
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+	int category = -1;
+
+	mgmt = (const struct ieee80211_mgmt *) data;
+	fc = le_to_host16(mgmt->frame_control);
+
+	if ((WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION ||
+	     WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION_NO_ACK) &&
+	    len > 24) {
+		category = mgmt->u.action.category;
+		if (!is_robust_action_category(category))
+			return 0; /* Not a robust management frame */
+	}
+
+	bss = bss_get(wt, mgmt->bssid);
+	if (bss == NULL)
+		return 0;
+	if (os_memcmp(mgmt->da, mgmt->bssid, ETH_ALEN) == 0)
+		sta = sta_get(bss, mgmt->sa);
+	else
+		sta = sta_get(bss, mgmt->da);
+	if (sta == NULL)
+		return 0;
+
+	if ((bss->rsn_capab & WPA_CAPABILITY_MFPC) &&
+	    (sta->rsn_capab & WPA_CAPABILITY_MFPC) &&
+	    (sta->state == STATE3 ||
+	     WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION ||
+	     WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION_NO_ACK)) {
+		add_note(wt, MSG_INFO,
+			 "Robust individually-addressed management frame (stype=%u category=%d) sent without CCMP/GCMP by "
+			 MACSTR, WLAN_FC_GET_STYPE(fc), category,
+			 MAC2STR(mgmt->sa));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void rx_mgmt(struct wlantest *wt, const u8 *data, size_t len)
+{
+	const struct ieee80211_hdr *hdr;
+	u16 fc, stype;
+	int valid = 1;
+	u8 *decrypted = NULL;
+	size_t dlen;
+
+	if (len < 24)
+		return;
+
+	hdr = (const struct ieee80211_hdr *) data;
+	fc = le_to_host16(hdr->frame_control);
+	wt->rx_mgmt++;
+	stype = WLAN_FC_GET_STYPE(fc);
+
+	if ((hdr->addr1[0] & 0x01) &&
+	    (stype == WLAN_FC_STYPE_DEAUTH ||
+	     stype == WLAN_FC_STYPE_DISASSOC ||
+	     stype == WLAN_FC_STYPE_ACTION ||
+	     stype == WLAN_FC_STYPE_ACTION_NO_ACK)) {
+		if (check_bip(wt, data, len) < 0)
+			valid = 0;
+	}
+
+	wpa_printf((stype == WLAN_FC_STYPE_BEACON ||
+		    stype == WLAN_FC_STYPE_PROBE_RESP ||
+		    stype == WLAN_FC_STYPE_PROBE_REQ) ?
+		   MSG_EXCESSIVE : MSG_MSGDUMP,
+		   "MGMT %s%s%s DA=" MACSTR " SA=" MACSTR " BSSID=" MACSTR,
+		   mgmt_stype(stype),
+		   fc & WLAN_FC_PWRMGT ? " PwrMgt" : "",
+		   fc & WLAN_FC_ISWEP ? " Prot" : "",
+		   MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+		   MAC2STR(hdr->addr3));
+
+	if ((fc & WLAN_FC_ISWEP) &&
+	    !(hdr->addr1[0] & 0x01) &&
+	    (stype == WLAN_FC_STYPE_DEAUTH ||
+	     stype == WLAN_FC_STYPE_DISASSOC ||
+	     stype == WLAN_FC_STYPE_ACTION ||
+	     stype == WLAN_FC_STYPE_ACTION_NO_ACK)) {
+		decrypted = mgmt_decrypt(wt, data, len, &dlen);
+		if (decrypted) {
+			write_pcap_decrypted(wt, decrypted, dlen, NULL, 0);
+			data = decrypted;
+			len = dlen;
+		} else
+			valid = 0;
+	}
+
+	if (!(fc & WLAN_FC_ISWEP) &&
+	    !(hdr->addr1[0] & 0x01) &&
+	    (stype == WLAN_FC_STYPE_DEAUTH ||
+	     stype == WLAN_FC_STYPE_DISASSOC ||
+	     stype == WLAN_FC_STYPE_ACTION ||
+	     stype == WLAN_FC_STYPE_ACTION_NO_ACK)) {
+		if (check_mgmt_ccmp_gcmp(wt, data, len) < 0)
+			valid = 0;
+	}
+
+	switch (stype) {
+	case WLAN_FC_STYPE_BEACON:
+		rx_mgmt_beacon(wt, data, len);
+		break;
+	case WLAN_FC_STYPE_PROBE_RESP:
+		rx_mgmt_probe_resp(wt, data, len);
+		break;
+	case WLAN_FC_STYPE_AUTH:
+		rx_mgmt_auth(wt, data, len);
+		break;
+	case WLAN_FC_STYPE_DEAUTH:
+		rx_mgmt_deauth(wt, data, len, valid);
+		break;
+	case WLAN_FC_STYPE_ASSOC_REQ:
+		rx_mgmt_assoc_req(wt, data, len);
+		break;
+	case WLAN_FC_STYPE_ASSOC_RESP:
+		rx_mgmt_assoc_resp(wt, data, len);
+		break;
+	case WLAN_FC_STYPE_REASSOC_REQ:
+		rx_mgmt_reassoc_req(wt, data, len);
+		break;
+	case WLAN_FC_STYPE_REASSOC_RESP:
+		rx_mgmt_reassoc_resp(wt, data, len);
+		break;
+	case WLAN_FC_STYPE_DISASSOC:
+		rx_mgmt_disassoc(wt, data, len, valid);
+		break;
+	case WLAN_FC_STYPE_ACTION:
+		rx_mgmt_action(wt, data, len, valid, false);
+		break;
+	case WLAN_FC_STYPE_ACTION_NO_ACK:
+		rx_mgmt_action(wt, data, len, valid, true);
+		break;
+	}
+
+	os_free(decrypted);
+
+	wt->last_mgmt_valid = valid;
+}
+
+
+static void rx_mgmt_deauth_ack(struct wlantest *wt,
+			       const struct ieee80211_hdr *hdr)
+{
+	const struct ieee80211_mgmt *mgmt;
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+
+	mgmt = (const struct ieee80211_mgmt *) hdr;
+	bss = bss_get(wt, mgmt->bssid);
+	if (bss == NULL)
+		return;
+	if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0)
+		sta = sta_get(bss, mgmt->da);
+	else
+		sta = sta_get(bss, mgmt->sa);
+	if (sta == NULL)
+		return;
+
+	add_note(wt, MSG_DEBUG, "DEAUTH from " MACSTR " acknowledged by "
+		 MACSTR, MAC2STR(mgmt->sa), MAC2STR(mgmt->da));
+	if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0) {
+		int c;
+		c = wt->last_mgmt_valid ?
+			WLANTEST_STA_COUNTER_VALID_DEAUTH_RX_ACK :
+			WLANTEST_STA_COUNTER_INVALID_DEAUTH_RX_ACK;
+		sta->counters[c]++;
+	}
+}
+
+
+static void rx_mgmt_disassoc_ack(struct wlantest *wt,
+				 const struct ieee80211_hdr *hdr)
+{
+	const struct ieee80211_mgmt *mgmt;
+	struct wlantest_bss *bss;
+	struct wlantest_sta *sta;
+
+	mgmt = (const struct ieee80211_mgmt *) hdr;
+	bss = bss_get(wt, mgmt->bssid);
+	if (bss == NULL)
+		return;
+	if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0)
+		sta = sta_get(bss, mgmt->da);
+	else
+		sta = sta_get(bss, mgmt->sa);
+	if (sta == NULL)
+		return;
+
+	add_note(wt, MSG_DEBUG, "DISASSOC from " MACSTR " acknowledged by "
+		 MACSTR, MAC2STR(mgmt->sa), MAC2STR(mgmt->da));
+	if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0) {
+		int c;
+		c = wt->last_mgmt_valid ?
+			WLANTEST_STA_COUNTER_VALID_DISASSOC_RX_ACK :
+			WLANTEST_STA_COUNTER_INVALID_DISASSOC_RX_ACK;
+		sta->counters[c]++;
+	}
+}
+
+
+void rx_mgmt_ack(struct wlantest *wt, const struct ieee80211_hdr *hdr)
+{
+	u16 fc, stype;
+	fc = le_to_host16(hdr->frame_control);
+	stype = WLAN_FC_GET_STYPE(fc);
+
+	wpa_printf(MSG_MSGDUMP, "MGMT ACK: stype=%u a1=" MACSTR " a2=" MACSTR
+		   " a3=" MACSTR,
+		   stype, MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+		   MAC2STR(hdr->addr3));
+
+	switch (stype) {
+	case WLAN_FC_STYPE_DEAUTH:
+		rx_mgmt_deauth_ack(wt, hdr);
+		break;
+	case WLAN_FC_STYPE_DISASSOC:
+		rx_mgmt_disassoc_ack(wt, hdr);
+		break;
+	}
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/rx_tdls.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/rx_tdls.c
new file mode 100644
index 0000000..0c012a9
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/rx_tdls.c
@@ -0,0 +1,618 @@
+/*
+ * Received Data frame processing for TDLS packets
+ * Copyright (c) 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 "crypto/sha256.h"
+#include "crypto/crypto.h"
+#include "crypto/aes_wrap.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "wlantest.h"
+
+
+static struct wlantest_tdls * get_tdls(struct wlantest *wt, const u8 *linkid,
+				       int create_new, const u8 *bssid)
+{
+	struct wlantest_bss *bss;
+	struct wlantest_sta *init, *resp;
+	struct wlantest_tdls *tdls;
+
+	bss = bss_find(wt, linkid);
+	if (bss == NULL && bssid) {
+		bss = bss_find(wt, bssid);
+		if (bss)
+			add_note(wt, MSG_INFO, "TDLS: Incorrect BSSID " MACSTR
+				 " in LinkId?! (init=" MACSTR " resp="
+				 MACSTR ")",
+				 MAC2STR(linkid), MAC2STR(linkid + ETH_ALEN),
+				 MAC2STR(linkid + 2 * ETH_ALEN));
+	}
+	if (bss == NULL)
+		return NULL;
+
+	init = sta_find(bss, linkid + ETH_ALEN);
+	if (init == NULL)
+		return NULL;
+
+	resp = sta_find(bss, linkid + 2 * ETH_ALEN);
+	if (resp == NULL)
+		return NULL;
+
+	dl_list_for_each(tdls, &bss->tdls, struct wlantest_tdls, list) {
+		if (tdls->init == init && tdls->resp == resp)
+			return tdls;
+	}
+
+	if (!create_new)
+		return NULL;
+
+	add_note(wt, MSG_DEBUG, "Add new TDLS link context: initiator " MACSTR
+		 " responder " MACSTR " BSSID " MACSTR,
+		 MAC2STR(linkid + ETH_ALEN),
+		 MAC2STR(linkid + 2 * ETH_ALEN),
+		 MAC2STR(bssid));
+
+	tdls = os_zalloc(sizeof(*tdls));
+	if (tdls == NULL)
+		return NULL;
+	tdls->init = init;
+	tdls->resp = resp;
+	dl_list_add(&bss->tdls, &tdls->list);
+	return tdls;
+}
+
+
+static int tdls_derive_tpk(struct wlantest_tdls *tdls, const u8 *bssid,
+			   const u8 *ftie, u8 ftie_len)
+{
+	const struct rsn_ftie *f;
+	u8 key_input[SHA256_MAC_LEN];
+	const u8 *nonce[2];
+	size_t len[2];
+	u8 data[3 * ETH_ALEN];
+
+	if (ftie == NULL || ftie_len < sizeof(struct rsn_ftie))
+		return 0;
+
+	f = (const struct rsn_ftie *) ftie;
+	wpa_hexdump(MSG_DEBUG, "TDLS ANonce", f->anonce, WPA_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "TDLS SNonce", f->snonce, WPA_NONCE_LEN);
+
+	/*
+	 * IEEE Std 802.11z-2010 8.5.9.1:
+	 * TPK-Key-Input = SHA-256(min(SNonce, ANonce) || max(SNonce, ANonce))
+	 */
+	len[0] = WPA_NONCE_LEN;
+	len[1] = WPA_NONCE_LEN;
+	if (os_memcmp(f->anonce, f->snonce, WPA_NONCE_LEN) < 0) {
+		nonce[0] = f->anonce;
+		nonce[1] = f->snonce;
+	} else {
+		nonce[0] = f->snonce;
+		nonce[1] = f->anonce;
+	}
+	sha256_vector(2, nonce, len, key_input);
+	wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-Key-Input",
+			key_input, SHA256_MAC_LEN);
+
+	/*
+	 * TPK-Key-Data = KDF-N_KEY(TPK-Key-Input, "TDLS PMK",
+	 *	min(MAC_I, MAC_R) || max(MAC_I, MAC_R) || BSSID || N_KEY)
+	 * TODO: is N_KEY really included in KDF Context and if so, in which
+	 * presentation format (little endian 16-bit?) is it used? It gets
+	 * added by the KDF anyway..
+	 */
+
+	if (os_memcmp(tdls->init->addr, tdls->resp->addr, ETH_ALEN) < 0) {
+		os_memcpy(data, tdls->init->addr, ETH_ALEN);
+		os_memcpy(data + ETH_ALEN, tdls->resp->addr, ETH_ALEN);
+	} else {
+		os_memcpy(data, tdls->resp->addr, ETH_ALEN);
+		os_memcpy(data + ETH_ALEN, tdls->init->addr, ETH_ALEN);
+	}
+	os_memcpy(data + 2 * ETH_ALEN, bssid, ETH_ALEN);
+	wpa_hexdump(MSG_DEBUG, "TDLS: KDF Context", data, sizeof(data));
+
+	sha256_prf(key_input, SHA256_MAC_LEN, "TDLS PMK", data, sizeof(data),
+		   (u8 *) &tdls->tpk, sizeof(tdls->tpk));
+	wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-KCK",
+			tdls->tpk.kck, sizeof(tdls->tpk.kck));
+	wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-TK",
+			tdls->tpk.tk, sizeof(tdls->tpk.tk));
+
+	return 1;
+}
+
+
+static int tdls_verify_mic(struct wlantest *wt, struct wlantest_tdls *tdls,
+			   u8 trans_seq, struct ieee802_11_elems *elems)
+{
+	u8 *buf, *pos;
+	int len;
+	u8 mic[16];
+	int ret;
+	const struct rsn_ftie *rx_ftie;
+	struct rsn_ftie *tmp_ftie;
+
+	if (elems->link_id == NULL || elems->rsn_ie == NULL ||
+	    elems->timeout_int == NULL || elems->ftie == NULL ||
+	    elems->ftie_len < sizeof(struct rsn_ftie))
+		return -1;
+
+	len = 2 * ETH_ALEN + 1 + 2 + 18 + 2 + elems->rsn_ie_len +
+		2 + 5 + 2 + elems->ftie_len;
+
+	buf = os_zalloc(len);
+	if (buf == NULL)
+		return -1;
+
+	pos = buf;
+	/* 1) TDLS initiator STA MAC address */
+	os_memcpy(pos, elems->link_id + ETH_ALEN, ETH_ALEN);
+	pos += ETH_ALEN;
+	/* 2) TDLS responder STA MAC address */
+	os_memcpy(pos, elems->link_id + 2 * ETH_ALEN, ETH_ALEN);
+	pos += ETH_ALEN;
+	/* 3) Transaction Sequence number */
+	*pos++ = trans_seq;
+	/* 4) Link Identifier IE */
+	os_memcpy(pos, elems->link_id - 2, 2 + 18);
+	pos += 2 + 18;
+	/* 5) RSN IE */
+	os_memcpy(pos, elems->rsn_ie - 2, 2 + elems->rsn_ie_len);
+	pos += 2 + elems->rsn_ie_len;
+	/* 6) Timeout Interval IE */
+	os_memcpy(pos, elems->timeout_int - 2, 2 + 5);
+	pos += 2 + 5;
+	/* 7) FTIE, with the MIC field of the FTIE set to 0 */
+	os_memcpy(pos, elems->ftie - 2, 2 + elems->ftie_len);
+	pos += 2;
+	tmp_ftie = (struct rsn_ftie *) pos;
+	os_memset(tmp_ftie->mic, 0, 16);
+	pos += elems->ftie_len;
+
+	wpa_hexdump(MSG_DEBUG, "TDLS: Data for FTIE MIC", buf, pos - buf);
+	wpa_hexdump_key(MSG_DEBUG, "TDLS: KCK", tdls->tpk.kck, 16);
+	ret = omac1_aes_128(tdls->tpk.kck, buf, pos - buf, mic);
+	os_free(buf);
+	if (ret)
+		return -1;
+	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE MIC", mic, 16);
+	rx_ftie = (const struct rsn_ftie *) elems->ftie;
+
+	if (os_memcmp(mic, rx_ftie->mic, 16) == 0) {
+		add_note(wt, MSG_DEBUG, "TDLS: Valid MIC");
+		return 0;
+	}
+	add_note(wt, MSG_DEBUG, "TDLS: Invalid MIC");
+	return -1;
+}
+
+
+static void rx_data_tdls_setup_request(struct wlantest *wt, const u8 *bssid,
+				       const u8 *sta_addr, const u8 *dst,
+				       const u8 *src,
+				       const u8 *data, size_t len)
+{
+	struct ieee802_11_elems elems;
+	struct wlantest_tdls *tdls;
+	u8 linkid[3 * ETH_ALEN];
+
+	if (len < 3) {
+		add_note(wt, MSG_INFO, "Too short TDLS Setup Request " MACSTR
+			 " -> " MACSTR, MAC2STR(src), MAC2STR(dst));
+		return;
+	}
+	wpa_printf(MSG_DEBUG, "TDLS Setup Request " MACSTR " -> "
+		   MACSTR, MAC2STR(src), MAC2STR(dst));
+
+	if (ieee802_11_parse_elems(data + 3, len - 3, &elems, 1) ==
+	    ParseFailed || elems.link_id == NULL)
+		return;
+	wpa_printf(MSG_DEBUG, "TDLS Link Identifier: BSSID " MACSTR
+		   " initiator STA " MACSTR " responder STA " MACSTR,
+		   MAC2STR(elems.link_id), MAC2STR(elems.link_id + ETH_ALEN),
+		   MAC2STR(elems.link_id + 2 * ETH_ALEN));
+	tdls = get_tdls(wt, elems.link_id, 1, bssid);
+	if (tdls) {
+		tdls->counters[WLANTEST_TDLS_COUNTER_SETUP_REQ]++;
+		tdls->dialog_token = data[0];
+		if (elems.ftie && elems.ftie_len >= sizeof(struct rsn_ftie)) {
+			const struct rsn_ftie *f;
+			f = (const struct rsn_ftie *) elems.ftie;
+			os_memcpy(tdls->inonce, f->snonce, WPA_NONCE_LEN);
+		}
+	}
+
+	/* Check whether reverse direction context exists already */
+	os_memcpy(linkid, bssid, ETH_ALEN);
+	os_memcpy(linkid + ETH_ALEN, dst, ETH_ALEN);
+	os_memcpy(linkid + 2 * ETH_ALEN, src, ETH_ALEN);
+	tdls = get_tdls(wt, linkid, 0, bssid);
+	if (tdls)
+		add_note(wt, MSG_INFO, "Reverse direction TDLS context exists");
+}
+
+
+static void rx_data_tdls_setup_response_failure(struct wlantest *wt,
+						const u8 *bssid,
+						const u8 *sta_addr,
+						u8 dialog_token, u16 status)
+{
+	struct wlantest_bss *bss;
+	struct wlantest_tdls *tdls;
+	struct wlantest_sta *sta;
+
+	if (status == WLAN_STATUS_SUCCESS) {
+		add_note(wt, MSG_INFO, "TDLS: Invalid TDLS Setup Response from "
+			 MACSTR, MAC2STR(sta_addr));
+		return;
+	}
+
+	bss = bss_find(wt, bssid);
+	if (!bss)
+		return;
+	sta = sta_find(bss, sta_addr);
+	if (!sta)
+		return;
+
+	dl_list_for_each(tdls, &bss->tdls, struct wlantest_tdls, list) {
+		if (tdls->resp == sta) {
+			if (dialog_token != tdls->dialog_token) {
+				add_note(wt, MSG_DEBUG, "TDLS: Dialog token "
+					 "mismatch in TDLS Setup Response "
+					 "(failure)");
+				break;
+			}
+			add_note(wt, MSG_DEBUG, "TDLS: Found matching TDLS "
+				 "setup session based on dialog token");
+			tdls->counters[
+				WLANTEST_TDLS_COUNTER_SETUP_RESP_FAIL]++;
+			break;
+		}
+	}
+}
+
+
+static void rx_data_tdls_setup_response(struct wlantest *wt, const u8 *bssid,
+					const u8 *sta_addr, const u8 *dst,
+					const u8 *src,
+					const u8 *data, size_t len)
+{
+	u16 status;
+	struct ieee802_11_elems elems;
+	struct wlantest_tdls *tdls;
+
+	if (len < 3) {
+		add_note(wt, MSG_INFO, "Too short TDLS Setup Response " MACSTR
+			 " -> " MACSTR, MAC2STR(src), MAC2STR(dst));
+		return;
+	}
+	status = WPA_GET_LE16(data);
+	wpa_printf(MSG_DEBUG, "TDLS Setup Response " MACSTR " -> "
+		   MACSTR " (status %d)",
+		   MAC2STR(src), MAC2STR(dst), status);
+	if (len < 5 && status == 0) {
+		add_note(wt, MSG_INFO, "Too short TDLS Setup Response " MACSTR
+			 " -> " MACSTR, MAC2STR(src), MAC2STR(dst));
+		return;
+	}
+
+	if (len < 5 ||
+	    ieee802_11_parse_elems(data + 5, len - 5, &elems, 1) ==
+	    ParseFailed || elems.link_id == NULL) {
+		/* Need to match TDLS link based on Dialog Token */
+		rx_data_tdls_setup_response_failure(wt, bssid, sta_addr,
+						    data[2], status);
+		return;
+	}
+	wpa_printf(MSG_DEBUG, "TDLS Link Identifier: BSSID " MACSTR
+		   " initiator STA " MACSTR " responder STA " MACSTR,
+		   MAC2STR(elems.link_id), MAC2STR(elems.link_id + ETH_ALEN),
+		   MAC2STR(elems.link_id + 2 * ETH_ALEN));
+
+	tdls = get_tdls(wt, elems.link_id, 1, bssid);
+	if (!tdls) {
+		add_note(wt, MSG_INFO, "No match TDLS context found");
+		return;
+	}
+	if (status)
+		tdls->counters[WLANTEST_TDLS_COUNTER_SETUP_RESP_FAIL]++;
+	else
+		tdls->counters[WLANTEST_TDLS_COUNTER_SETUP_RESP_OK]++;
+
+	if (status != WLAN_STATUS_SUCCESS)
+		return;
+
+	if (elems.ftie && elems.ftie_len >= sizeof(struct rsn_ftie)) {
+		const struct rsn_ftie *f;
+		f = (const struct rsn_ftie *) elems.ftie;
+		if (os_memcmp(tdls->inonce, f->snonce, WPA_NONCE_LEN) != 0) {
+			add_note(wt, MSG_INFO, "Mismatch in TDLS initiator "
+				 "nonce");
+		}
+		os_memcpy(tdls->rnonce, f->anonce, WPA_NONCE_LEN);
+	}
+
+	if (tdls_derive_tpk(tdls, bssid, elems.ftie, elems.ftie_len) < 1)
+		return;
+	if (tdls_verify_mic(wt, tdls, 2, &elems) == 0) {
+		tdls->dialog_token = data[2];
+		add_note(wt, MSG_DEBUG, "TDLS: Dialog Token for the link: %u",
+			 tdls->dialog_token);
+	}
+}
+
+
+static void rx_data_tdls_setup_confirm_failure(struct wlantest *wt,
+					       const u8 *bssid,
+					       const u8 *src,
+					       u8 dialog_token, u16 status)
+{
+	struct wlantest_bss *bss;
+	struct wlantest_tdls *tdls;
+	struct wlantest_sta *sta;
+
+	if (status == WLAN_STATUS_SUCCESS) {
+		add_note(wt, MSG_INFO, "TDLS: Invalid TDLS Setup Confirm from "
+			 MACSTR, MAC2STR(src));
+		return;
+	}
+
+	bss = bss_find(wt, bssid);
+	if (!bss)
+		return;
+	sta = sta_find(bss, src);
+	if (!sta)
+		return;
+
+	dl_list_for_each(tdls, &bss->tdls, struct wlantest_tdls, list) {
+		if (tdls->init == sta) {
+			if (dialog_token != tdls->dialog_token) {
+				add_note(wt, MSG_DEBUG, "TDLS: Dialog token "
+					 "mismatch in TDLS Setup Confirm "
+					 "(failure)");
+				break;
+			}
+			add_note(wt, MSG_DEBUG, "TDLS: Found matching TDLS "
+				 "setup session based on dialog token");
+			tdls->counters[
+				WLANTEST_TDLS_COUNTER_SETUP_CONF_FAIL]++;
+			break;
+		}
+	}
+}
+
+
+static void rx_data_tdls_setup_confirm(struct wlantest *wt, const u8 *bssid,
+				       const u8 *sta_addr, const u8 *dst,
+				       const u8 *src,
+				       const u8 *data, size_t len)
+{
+	u16 status;
+	struct ieee802_11_elems elems;
+	struct wlantest_tdls *tdls;
+	u8 link_id[3 * ETH_ALEN];
+
+	if (len < 3) {
+		add_note(wt, MSG_INFO, "Too short TDLS Setup Confirm " MACSTR
+			 " -> " MACSTR, MAC2STR(src), MAC2STR(dst));
+		return;
+	}
+	status = WPA_GET_LE16(data);
+	wpa_printf(MSG_DEBUG, "TDLS Setup Confirm " MACSTR " -> "
+		   MACSTR " (status %d)",
+		   MAC2STR(src), MAC2STR(dst), status);
+
+	if (ieee802_11_parse_elems(data + 3, len - 3, &elems, 1) ==
+	    ParseFailed || elems.link_id == NULL) {
+		/* Need to match TDLS link based on Dialog Token */
+		rx_data_tdls_setup_confirm_failure(wt, bssid, src,
+						   data[2], status);
+		return;
+	}
+	wpa_printf(MSG_DEBUG, "TDLS Link Identifier: BSSID " MACSTR
+		   " initiator STA " MACSTR " responder STA " MACSTR,
+		   MAC2STR(elems.link_id), MAC2STR(elems.link_id + ETH_ALEN),
+		   MAC2STR(elems.link_id + 2 * ETH_ALEN));
+
+	tdls = get_tdls(wt, elems.link_id, 1, bssid);
+	if (tdls == NULL)
+		return;
+	if (status)
+		tdls->counters[WLANTEST_TDLS_COUNTER_SETUP_CONF_FAIL]++;
+	else
+		tdls->counters[WLANTEST_TDLS_COUNTER_SETUP_CONF_OK]++;
+
+	if (status != WLAN_STATUS_SUCCESS)
+		return;
+
+	if (elems.ftie && elems.ftie_len >= sizeof(struct rsn_ftie)) {
+		const struct rsn_ftie *f;
+		f = (const struct rsn_ftie *) elems.ftie;
+		if (os_memcmp(tdls->inonce, f->snonce, WPA_NONCE_LEN) != 0) {
+			add_note(wt, MSG_INFO, "Mismatch in TDLS initiator "
+				 "nonce");
+		}
+		if (os_memcmp(tdls->rnonce, f->anonce, WPA_NONCE_LEN) != 0) {
+			add_note(wt, MSG_INFO, "Mismatch in TDLS responder "
+				 "nonce");
+		}
+	}
+
+	tdls->link_up = 1;
+	if (tdls_derive_tpk(tdls, bssid, elems.ftie, elems.ftie_len) < 1) {
+		if (elems.ftie == NULL)
+			goto remove_reverse;
+		return;
+	}
+	if (tdls_verify_mic(wt, tdls, 3, &elems) == 0) {
+		tdls->dialog_token = data[2];
+		add_note(wt, MSG_DEBUG, "TDLS: Link up - Dialog Token: %u",
+			 tdls->dialog_token);
+	}
+
+remove_reverse:
+	/*
+	 * The TDLS link itself is bidirectional, but there is explicit
+	 * initiator/responder roles. Remove the other direction of the link
+	 * (if it exists) to make sure that the link counters are stored for
+	 * the current TDLS entery.
+	 */
+	os_memcpy(link_id, elems.link_id, ETH_ALEN);
+	os_memcpy(link_id + ETH_ALEN, elems.link_id + 2 * ETH_ALEN, ETH_ALEN);
+	os_memcpy(link_id + 2 * ETH_ALEN, elems.link_id + ETH_ALEN, ETH_ALEN);
+	tdls = get_tdls(wt, link_id, 0, bssid);
+	if (tdls) {
+		add_note(wt, MSG_DEBUG, "TDLS: Remove reverse link entry");
+		tdls_deinit(tdls);
+	}
+}
+
+
+static int tdls_verify_mic_teardown(struct wlantest *wt,
+				    struct wlantest_tdls *tdls, u8 trans_seq,
+				    const u8 *reason_code,
+				    struct ieee802_11_elems *elems)
+{
+	u8 *buf, *pos;
+	int len;
+	u8 mic[16];
+	int ret;
+	const struct rsn_ftie *rx_ftie;
+	struct rsn_ftie *tmp_ftie;
+
+	if (elems->link_id == NULL || elems->ftie == NULL ||
+	    elems->ftie_len < sizeof(struct rsn_ftie))
+		return -1;
+
+	len = 2 + 18 + 2 + 1 + 1 + 2 + elems->ftie_len;
+
+	buf = os_zalloc(len);
+	if (buf == NULL)
+		return -1;
+
+	pos = buf;
+	/* 1) Link Identifier IE */
+	os_memcpy(pos, elems->link_id - 2, 2 + 18);
+	pos += 2 + 18;
+	/* 2) Reason Code */
+	os_memcpy(pos, reason_code, 2);
+	pos += 2;
+	/* 3) Dialog token */
+	*pos++ = tdls->dialog_token;
+	/* 4) Transaction Sequence number */
+	*pos++ = trans_seq;
+	/* 5) FTIE, with the MIC field of the FTIE set to 0 */
+	os_memcpy(pos, elems->ftie - 2, 2 + elems->ftie_len);
+	pos += 2;
+	tmp_ftie = (struct rsn_ftie *) pos;
+	os_memset(tmp_ftie->mic, 0, 16);
+	pos += elems->ftie_len;
+
+	wpa_hexdump(MSG_DEBUG, "TDLS: Data for FTIE MIC", buf, pos - buf);
+	wpa_hexdump_key(MSG_DEBUG, "TDLS: KCK", tdls->tpk.kck, 16);
+	ret = omac1_aes_128(tdls->tpk.kck, buf, pos - buf, mic);
+	os_free(buf);
+	if (ret)
+		return -1;
+	wpa_hexdump(MSG_DEBUG, "TDLS: FTIE MIC", mic, 16);
+	rx_ftie = (const struct rsn_ftie *) elems->ftie;
+
+	if (os_memcmp(mic, rx_ftie->mic, 16) == 0) {
+		add_note(wt, MSG_DEBUG, "TDLS: Valid MIC");
+		return 0;
+	}
+	add_note(wt, MSG_DEBUG, "TDLS: Invalid MIC");
+	return -1;
+}
+
+
+static void rx_data_tdls_teardown(struct wlantest *wt, const u8 *bssid,
+				  const u8 *sta_addr, const u8 *dst,
+				  const u8 *src,
+				  const u8 *data, size_t len)
+{
+	u16 reason;
+	struct ieee802_11_elems elems;
+	struct wlantest_tdls *tdls;
+
+	if (len < 2)
+		return;
+	reason = WPA_GET_LE16(data);
+	wpa_printf(MSG_DEBUG, "TDLS Teardown " MACSTR " -> "
+		   MACSTR " (reason %d)",
+		   MAC2STR(src), MAC2STR(dst), reason);
+
+	if (ieee802_11_parse_elems(data + 2, len - 2, &elems, 1) ==
+	    ParseFailed || elems.link_id == NULL)
+		return;
+	wpa_printf(MSG_DEBUG, "TDLS Link Identifier: BSSID " MACSTR
+		   " initiator STA " MACSTR " responder STA " MACSTR,
+		   MAC2STR(elems.link_id), MAC2STR(elems.link_id + ETH_ALEN),
+		   MAC2STR(elems.link_id + 2 * ETH_ALEN));
+
+	tdls = get_tdls(wt, elems.link_id, 1, bssid);
+	if (tdls) {
+		if (tdls->link_up)
+			add_note(wt, MSG_DEBUG, "TDLS: Link down");
+		tdls->link_up = 0;
+		tdls->counters[WLANTEST_TDLS_COUNTER_TEARDOWN]++;
+		tdls_verify_mic_teardown(wt, tdls, 4, data, &elems);
+	}
+}
+
+
+static void rx_data_tdls(struct wlantest *wt, const u8 *bssid,
+			 const u8 *sta_addr, const u8 *dst, const u8 *src,
+			 const u8 *data, size_t len)
+{
+	/* data contains the payload of a TDLS Action frame */
+	if (len < 2 || data[0] != WLAN_ACTION_TDLS) {
+		wpa_hexdump(MSG_DEBUG, "Unrecognized encapsulated TDLS frame",
+			    data, len);
+		return;
+	}
+
+	switch (data[1]) {
+	case WLAN_TDLS_SETUP_REQUEST:
+		rx_data_tdls_setup_request(wt, bssid, sta_addr, dst, src,
+					   data + 2, len - 2);
+		break;
+	case WLAN_TDLS_SETUP_RESPONSE:
+		rx_data_tdls_setup_response(wt, bssid, sta_addr, dst, src,
+					    data + 2, len - 2);
+		break;
+	case WLAN_TDLS_SETUP_CONFIRM:
+		rx_data_tdls_setup_confirm(wt, bssid, sta_addr, dst, src,
+					   data + 2, len - 2);
+		break;
+	case WLAN_TDLS_TEARDOWN:
+		rx_data_tdls_teardown(wt, bssid, sta_addr, dst, src, data + 2,
+				      len - 2);
+		break;
+	case WLAN_TDLS_DISCOVERY_REQUEST:
+		wpa_printf(MSG_DEBUG, "TDLS Discovery Request " MACSTR " -> "
+			   MACSTR, MAC2STR(src), MAC2STR(dst));
+		break;
+	}
+}
+
+
+void rx_data_80211_encap(struct wlantest *wt, const u8 *bssid,
+			 const u8 *sta_addr, const u8 *dst, const u8 *src,
+			 const u8 *data, size_t len)
+{
+	wpa_hexdump(MSG_EXCESSIVE, "802.11 data encap frame", data, len);
+	if (len < 1)
+		return;
+	if (data[0] == 0x02)
+		rx_data_tdls(wt, bssid, sta_addr, dst, src, data + 1, len - 1);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/sta.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/sta.c
new file mode 100644
index 0000000..6b5573e
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/sta.c
@@ -0,0 +1,355 @@
+/*
+ * STA list
+ * Copyright (c) 2010-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 "common/defs.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "wlantest.h"
+
+
+struct wlantest_sta * sta_find(struct wlantest_bss *bss, const u8 *addr)
+{
+	struct wlantest_sta *sta;
+
+	dl_list_for_each(sta, &bss->sta, struct wlantest_sta, list) {
+		if (os_memcmp(sta->addr, addr, ETH_ALEN) == 0)
+			return sta;
+	}
+
+	return NULL;
+}
+
+
+struct wlantest_sta * sta_find_mlo(struct wlantest *wt,
+				   struct wlantest_bss *bss, const u8 *addr)
+{
+	struct wlantest_sta *sta;
+	struct wlantest_bss *obss;
+	int link_id;
+
+	dl_list_for_each(sta, &bss->sta, struct wlantest_sta, list) {
+		if (os_memcmp(sta->addr, addr, ETH_ALEN) == 0)
+			return sta;
+	}
+
+	if (is_zero_ether_addr(addr))
+		return NULL;
+
+	dl_list_for_each(sta, &bss->sta, struct wlantest_sta, list) {
+		for (link_id = 0; link_id < MAX_NUM_MLO_LINKS; link_id++) {
+			if (os_memcmp(sta->link_addr[link_id], addr,
+				      ETH_ALEN) == 0)
+				return sta;
+		}
+	}
+
+	dl_list_for_each(obss, &wt->bss, struct wlantest_bss, list) {
+		if (obss == bss)
+			continue;
+		dl_list_for_each(sta, &obss->sta, struct wlantest_sta, list) {
+			if (os_memcmp(sta->addr, addr, ETH_ALEN) == 0)
+				return sta;
+			for (link_id = 0; link_id < MAX_NUM_MLO_LINKS;
+			     link_id++) {
+				if (os_memcmp(sta->link_addr[link_id], addr,
+					      ETH_ALEN) == 0)
+					return sta;
+			}
+		}
+	}
+
+	return NULL;
+}
+
+
+struct wlantest_sta * sta_get(struct wlantest_bss *bss, const u8 *addr)
+{
+	struct wlantest_sta *sta;
+
+	if (addr[0] & 0x01)
+		return NULL; /* Skip group addressed frames */
+
+	sta = sta_find(bss, addr);
+	if (sta)
+		return sta;
+
+	sta = os_zalloc(sizeof(*sta));
+	if (sta == NULL)
+		return NULL;
+	os_memset(sta->seq_ctrl_to_sta, 0xff, sizeof(sta->seq_ctrl_to_sta));
+	os_memset(sta->seq_ctrl_to_ap, 0xff, sizeof(sta->seq_ctrl_to_ap));
+	sta->bss = bss;
+	os_memcpy(sta->addr, addr, ETH_ALEN);
+	dl_list_add(&bss->sta, &sta->list);
+	wpa_printf(MSG_DEBUG, "Discovered new STA " MACSTR " in BSS " MACSTR,
+		   MAC2STR(sta->addr), MAC2STR(bss->bssid));
+	return sta;
+}
+
+
+void sta_deinit(struct wlantest_sta *sta)
+{
+	dl_list_del(&sta->list);
+	os_free(sta->assocreq_ies);
+	os_free(sta);
+}
+
+
+static void sta_update_assoc_ml(struct wlantest_sta *sta,
+				struct ieee802_11_elems *elems)
+{
+	const u8 *mld_addr;
+
+	if (!elems->basic_mle)
+		return;
+
+	mld_addr = get_basic_mle_mld_addr(elems->basic_mle,
+					  elems->basic_mle_len);
+	if (!mld_addr) {
+		wpa_printf(MSG_INFO, "MLO: Invalid Basic Multi-Link element");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "STA MLD Address: " MACSTR, MAC2STR(mld_addr));
+	os_memcpy(sta->mld_mac_addr, mld_addr, ETH_ALEN);
+}
+
+
+void sta_update_assoc(struct wlantest_sta *sta, struct ieee802_11_elems *elems)
+{
+	struct wpa_ie_data data;
+	struct wlantest_bss *bss = sta->bss;
+
+	if (elems->wpa_ie && !bss->wpaie[0] &&
+	    (bss->beacon_seen || bss->proberesp_seen)) {
+		wpa_printf(MSG_INFO, "WPA IE included in Association Request "
+			   "frame from " MACSTR " even though BSS does not "
+			   "use WPA - ignore IE",
+			   MAC2STR(sta->addr));
+		elems->wpa_ie = NULL;
+	}
+
+	if (elems->rsn_ie && !bss->rsnie[0] &&
+	    (bss->beacon_seen || bss->proberesp_seen)) {
+		wpa_printf(MSG_INFO, "RSN IE included in Association Request "
+			   "frame from " MACSTR " even though BSS does not "
+			   "use RSN - ignore IE",
+			   MAC2STR(sta->addr));
+		elems->rsn_ie = NULL;
+	}
+
+	if (elems->osen && !bss->osenie[0] &&
+	    (bss->beacon_seen || bss->proberesp_seen)) {
+		wpa_printf(MSG_INFO, "OSEN IE included in Association Request "
+			   "frame from " MACSTR " even though BSS does not "
+			   "use OSEN - ignore IE",
+			   MAC2STR(sta->addr));
+		elems->osen = NULL;
+	}
+
+	if (elems->wpa_ie && elems->rsn_ie) {
+		wpa_printf(MSG_INFO, "Both WPA IE and RSN IE included in "
+			   "Association Request frame from " MACSTR,
+			   MAC2STR(sta->addr));
+	}
+
+	if (elems->rsn_ie) {
+		wpa_hexdump(MSG_DEBUG, "RSN IE", elems->rsn_ie - 2,
+			    elems->rsn_ie_len + 2);
+		os_memcpy(sta->rsnie, elems->rsn_ie - 2,
+			  elems->rsn_ie_len + 2);
+		if (wpa_parse_wpa_ie_rsn(sta->rsnie, 2 + sta->rsnie[1], &data)
+		    < 0) {
+			wpa_printf(MSG_INFO, "Failed to parse RSN IE from "
+				   MACSTR, MAC2STR(sta->addr));
+		}
+	} else if (elems->wpa_ie) {
+		wpa_hexdump(MSG_DEBUG, "WPA IE", elems->wpa_ie - 2,
+			    elems->wpa_ie_len + 2);
+		os_memcpy(sta->rsnie, elems->wpa_ie - 2,
+			  elems->wpa_ie_len + 2);
+		if (wpa_parse_wpa_ie_wpa(sta->rsnie, 2 + sta->rsnie[1], &data)
+		    < 0) {
+			wpa_printf(MSG_INFO, "Failed to parse WPA IE from "
+				   MACSTR, MAC2STR(sta->addr));
+		}
+	} else if (elems->osen) {
+		wpa_hexdump(MSG_DEBUG, "OSEN IE", elems->osen - 2,
+			    elems->osen_len + 2);
+		os_memcpy(sta->osenie, elems->osen - 2, elems->osen_len + 2);
+		sta->proto = WPA_PROTO_OSEN;
+		sta->pairwise_cipher = WPA_CIPHER_CCMP;
+		sta->key_mgmt = WPA_KEY_MGMT_OSEN;
+		sta->rsn_capab = 0;
+		goto skip_rsn_wpa;
+	} else {
+		sta->rsnie[0] = 0;
+		sta->proto = 0;
+		sta->pairwise_cipher = 0;
+		sta->key_mgmt = 0;
+		sta->rsn_capab = 0;
+		if (sta->assocreq_capab_info & WLAN_CAPABILITY_PRIVACY)
+			sta->pairwise_cipher = WPA_CIPHER_WEP40;
+		goto skip_rsn_wpa;
+	}
+
+	sta->proto = data.proto;
+	sta->pairwise_cipher = data.pairwise_cipher;
+	sta->key_mgmt = data.key_mgmt;
+	sta->rsn_capab = data.capabilities;
+	if (bss->proto && (sta->proto & bss->proto) == 0) {
+		wpa_printf(MSG_INFO, "Mismatch in WPA/WPA2 proto: STA "
+			   MACSTR " 0x%x  BSS " MACSTR " 0x%x",
+			   MAC2STR(sta->addr), sta->proto,
+			   MAC2STR(bss->bssid), bss->proto);
+	}
+	if (bss->pairwise_cipher &&
+	    (sta->pairwise_cipher & bss->pairwise_cipher) == 0) {
+		wpa_printf(MSG_INFO, "Mismatch in pairwise cipher: STA "
+			   MACSTR " 0x%x  BSS " MACSTR " 0x%x",
+			   MAC2STR(sta->addr), sta->pairwise_cipher,
+			   MAC2STR(bss->bssid), bss->pairwise_cipher);
+	}
+	if (sta->proto && data.group_cipher != bss->group_cipher &&
+	    bss->ies_set) {
+		wpa_printf(MSG_INFO, "Mismatch in group cipher: STA "
+			   MACSTR " 0x%x != BSS " MACSTR " 0x%x",
+			   MAC2STR(sta->addr), data.group_cipher,
+			   MAC2STR(bss->bssid), bss->group_cipher);
+	}
+	if ((bss->rsn_capab & WPA_CAPABILITY_MFPR) &&
+	    !(sta->rsn_capab & WPA_CAPABILITY_MFPC)) {
+		wpa_printf(MSG_INFO, "STA " MACSTR " tries to associate "
+			   "without MFP to BSS " MACSTR " that advertises "
+			   "MFPR", MAC2STR(sta->addr), MAC2STR(bss->bssid));
+	}
+	if ((sta->rsn_capab & WPA_CAPABILITY_OCVC) &&
+	    !(sta->rsn_capab & WPA_CAPABILITY_MFPC)) {
+		wpa_printf(MSG_INFO, "STA " MACSTR " tries to associate "
+			   "without MFP to BSS " MACSTR " while supporting "
+			   "OCV", MAC2STR(sta->addr), MAC2STR(bss->bssid));
+	}
+
+skip_rsn_wpa:
+	wpa_printf(MSG_INFO, "STA " MACSTR
+		   " proto=%s%s%s%s"
+		   "pairwise=%s%s%s%s%s%s%s"
+		   "key_mgmt=%s%s%s%s%s%s%s%s%s%s%s%s%s%s"
+		   "rsn_capab=%s%s%s%s%s%s%s%s%s%s",
+		   MAC2STR(sta->addr),
+		   sta->proto == 0 ? "OPEN " : "",
+		   sta->proto & WPA_PROTO_WPA ? "WPA " : "",
+		   sta->proto & WPA_PROTO_RSN ? "WPA2 " : "",
+		   sta->proto & WPA_PROTO_OSEN ? "OSEN " : "",
+		   sta->pairwise_cipher == 0 ? "N/A " : "",
+		   sta->pairwise_cipher & WPA_CIPHER_NONE ? "NONE " : "",
+		   sta->pairwise_cipher & WPA_CIPHER_TKIP ? "TKIP " : "",
+		   sta->pairwise_cipher & WPA_CIPHER_CCMP ? "CCMP " : "",
+		   bss->pairwise_cipher & WPA_CIPHER_CCMP_256 ? "CCMP-256 " :
+		   "",
+		   bss->pairwise_cipher & WPA_CIPHER_GCMP ? "GCMP " : "",
+		   bss->pairwise_cipher & WPA_CIPHER_GCMP_256 ? "GCMP-256 " :
+		   "",
+		   sta->key_mgmt == 0 ? "N/A " : "",
+		   sta->key_mgmt & WPA_KEY_MGMT_IEEE8021X ? "EAP " : "",
+		   sta->key_mgmt & WPA_KEY_MGMT_PSK ? "PSK " : "",
+		   sta->key_mgmt & WPA_KEY_MGMT_WPA_NONE ? "WPA-NONE " : "",
+		   sta->key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X ? "FT-EAP " : "",
+		   sta->key_mgmt & WPA_KEY_MGMT_FT_PSK ? "FT-PSK " : "",
+		   sta->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256 ?
+		   "EAP-SHA256 " : "",
+		   sta->key_mgmt & WPA_KEY_MGMT_PSK_SHA256 ?
+		   "PSK-SHA256 " : "",
+		   sta->key_mgmt & WPA_KEY_MGMT_OWE ? "OWE " : "",
+		   sta->key_mgmt & WPA_KEY_MGMT_PASN ? "PASN " : "",
+		   sta->key_mgmt & WPA_KEY_MGMT_OSEN ? "OSEN " : "",
+		   sta->key_mgmt & WPA_KEY_MGMT_DPP ? "DPP " : "",
+		   sta->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B ?
+		   "EAP-SUITE-B " : "",
+		   sta->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 ?
+		   "EAP-SUITE-B-192 " : "",
+		   sta->rsn_capab & WPA_CAPABILITY_PREAUTH ? "PREAUTH " : "",
+		   sta->rsn_capab & WPA_CAPABILITY_NO_PAIRWISE ?
+		   "NO_PAIRWISE " : "",
+		   sta->rsn_capab & WPA_CAPABILITY_MFPR ? "MFPR " : "",
+		   sta->rsn_capab & WPA_CAPABILITY_MFPC ? "MFPC " : "",
+		   sta->rsn_capab & WPA_CAPABILITY_PEERKEY_ENABLED ?
+		   "PEERKEY " : "",
+		   sta->rsn_capab & WPA_CAPABILITY_SPP_A_MSDU_CAPABLE ?
+		   "SPP-A-MSDU-CAPAB " : "",
+		   sta->rsn_capab & WPA_CAPABILITY_SPP_A_MSDU_REQUIRED ?
+		   "SPP-A-MSDU-REQUIRED " : "",
+		   sta->rsn_capab & WPA_CAPABILITY_PBAC ? "PBAC " : "",
+		   sta->rsn_capab & WPA_CAPABILITY_OCVC ? "OCVC " : "",
+		   sta->rsn_capab & WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST ?
+		   "ExtKeyID " : "");
+
+	sta_update_assoc_ml(sta, elems);
+}
+
+
+static void sta_copy_ptk(struct wlantest_sta *sta, struct wpa_ptk *ptk)
+{
+	os_memcpy(&sta->ptk, ptk, sizeof(*ptk));
+	sta->ptk_set = 1;
+	os_memset(sta->rsc_tods, 0, sizeof(sta->rsc_tods));
+	os_memset(sta->rsc_fromds, 0, sizeof(sta->rsc_fromds));
+}
+
+
+void sta_new_ptk(struct wlantest *wt, struct wlantest_sta *sta,
+		 struct wpa_ptk *ptk)
+{
+	struct wlantest_bss *bss;
+	struct wlantest_sta *osta;
+
+	add_note(wt, MSG_DEBUG, "Derived new PTK");
+	sta_copy_ptk(sta, ptk);
+	wpa_hexdump(MSG_DEBUG, "PTK:KCK", sta->ptk.kck, sta->ptk.kck_len);
+	wpa_hexdump(MSG_DEBUG, "PTK:KEK", sta->ptk.kek, sta->ptk.kek_len);
+	wpa_hexdump(MSG_DEBUG, "PTK:TK", sta->ptk.tk, sta->ptk.tk_len);
+
+	dl_list_for_each(bss, &wt->bss, struct wlantest_bss, list) {
+		dl_list_for_each(osta, &bss->sta, struct wlantest_sta, list) {
+			bool match = false;
+			int link_id;
+
+			if (osta == sta)
+				continue;
+			if (os_memcmp(sta->addr, osta->addr, ETH_ALEN) == 0)
+				match = true;
+			for (link_id = 0; !match && link_id < MAX_NUM_MLO_LINKS;
+			     link_id++) {
+				if (os_memcmp(osta->link_addr[link_id],
+					      sta->addr, ETH_ALEN) == 0)
+					match = true;
+			}
+
+			if (!match)
+				continue;
+			wpa_printf(MSG_DEBUG,
+				   "Add PTK to another MLO STA entry " MACSTR
+				   " (MLD " MACSTR " --> " MACSTR ") in BSS "
+				   MACSTR " (MLD " MACSTR " --> " MACSTR ")",
+				   MAC2STR(osta->addr),
+				   MAC2STR(osta->mld_mac_addr),
+				   MAC2STR(sta->mld_mac_addr),
+				   MAC2STR(bss->bssid),
+				   MAC2STR(bss->mld_mac_addr),
+				   MAC2STR(sta->bss->mld_mac_addr));
+			sta_copy_ptk(osta, ptk);
+			os_memcpy(osta->mld_mac_addr, sta->mld_mac_addr,
+				  ETH_ALEN);
+			os_memcpy(osta->bss->mld_mac_addr,
+				  sta->bss->mld_mac_addr, ETH_ALEN);
+		}
+	}
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/test_vectors.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/test_vectors.c
new file mode 100644
index 0000000..3638f67
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/test_vectors.c
@@ -0,0 +1,944 @@
+/*
+ * test_vectors - IEEE 802.11 test vector generator
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/crc32.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "wlantest.h"
+
+
+static void test_vector_tkip(void)
+{
+	u8 tk[] = {
+		0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56,
+		0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12,
+		0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78,
+		0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34
+	};
+	u8 pn[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 };
+	u8 frame[] = {
+		0x08, 0x42, 0x2c, 0x00, 0x02, 0x03, 0x04, 0x05,
+		0x06, 0x08, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0xd0, 0x02,
+		/* 0x00, 0x20, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, */
+		0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00,
+		0x45, 0x00, 0x00, 0x54, 0x00, 0x00, 0x40, 0x00,
+		0x40, 0x01, 0xa5, 0x55, 0xc0, 0xa8, 0x0a, 0x02,
+		0xc0, 0xa8, 0x0a, 0x01, 0x08, 0x00, 0x3a, 0xb0,
+		0x00, 0x00, 0x00, 0x00, 0xcd, 0x4c, 0x05, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x08, 0x09, 0x0a, 0x0b,
+		0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13,
+		0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
+		0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
+		0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
+		0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33,
+		0x34, 0x35, 0x36, 0x37,
+		/* 0x68, 0x81, 0xa3, 0xf3, 0xd6, 0x48, 0xd0, 0x3c */
+	};
+	u8 *enc, *plain;
+	size_t enc_len, plain_len;
+
+	wpa_printf(MSG_INFO, "\nIEEE Std 802.11-2012, M.6.3 TKIP test "
+		   "vector\n");
+
+	wpa_hexdump(MSG_INFO, "TK", tk, sizeof(tk));
+	wpa_hexdump(MSG_INFO, "PN", pn, sizeof(pn));
+	wpa_hexdump(MSG_INFO, "Plaintext MPDU", frame, sizeof(frame));
+
+	enc = tkip_encrypt(tk, frame, sizeof(frame), 24, NULL, pn, 0, &enc_len);
+	if (enc == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to encrypt TKIP frame");
+		return;
+	}
+
+	wpa_hexdump(MSG_INFO, "Encrypted MPDU (without FCS)", enc, enc_len);
+
+	wpa_debug_level = MSG_INFO;
+	plain = tkip_decrypt(tk, (const struct ieee80211_hdr *) enc,
+			     enc + 24, enc_len - 24, &plain_len, NULL, NULL);
+	wpa_debug_level = MSG_EXCESSIVE;
+	os_free(enc);
+
+	if (plain == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to decrypt TKIP frame");
+		return;
+	}
+
+	if (plain_len != sizeof(frame) - 24 ||
+	    os_memcmp(plain, frame + 24, plain_len) != 0) {
+		wpa_hexdump(MSG_ERROR, "Decryption result did not match",
+			    plain, plain_len);
+	}
+
+	os_free(plain);
+}
+
+
+static void test_vector_ccmp(void)
+{
+	u8 tk[] = { 0xc9, 0x7c, 0x1f, 0x67, 0xce, 0x37, 0x11, 0x85,
+		    0x51, 0x4a, 0x8a, 0x19, 0xf2, 0xbd, 0xd5, 0x2f };
+	u8 pn[] = { 0xB5, 0x03, 0x97, 0x76, 0xE7, 0x0C };
+	u8 frame[] = {
+		0x08, 0x48, 0xc3, 0x2c, 0x0f, 0xd2, 0xe1, 0x28,
+		0xa5, 0x7c, 0x50, 0x30, 0xf1, 0x84, 0x44, 0x08,
+		0xab, 0xae, 0xa5, 0xb8, 0xfc, 0xba, 0x80, 0x33,
+		0xf8, 0xba, 0x1a, 0x55, 0xd0, 0x2f, 0x85, 0xae,
+		0x96, 0x7b, 0xb6, 0x2f, 0xb6, 0xcd, 0xa8, 0xeb,
+		0x7e, 0x78, 0xa0, 0x50
+	};
+	u8 *enc, *plain;
+	size_t enc_len, plain_len;
+	u8 fcs[4];
+
+	wpa_printf(MSG_INFO, "\nIEEE Std 802.11-2012, M.6.4 CCMP test "
+		   "vector\n");
+
+	wpa_hexdump(MSG_INFO, "TK", tk, sizeof(tk));
+	wpa_hexdump(MSG_INFO, "PN", pn, sizeof(pn));
+	wpa_hexdump(MSG_INFO, "802.11 Header", frame, 24);
+	wpa_hexdump(MSG_INFO, "Plaintext Data", frame + 24, sizeof(frame) - 24);
+
+	enc = ccmp_encrypt(tk, frame, sizeof(frame), 24, NULL, NULL, NULL, NULL,
+			   pn, 0, &enc_len);
+	if (enc == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to encrypt CCMP frame");
+		return;
+	}
+
+	wpa_hexdump(MSG_INFO, "Encrypted MPDU (without FCS)", enc, enc_len);
+	WPA_PUT_LE32(fcs, ieee80211_crc32(enc, enc_len));
+	wpa_hexdump(MSG_INFO, "FCS", fcs, sizeof(fcs));
+
+	wpa_debug_level = MSG_INFO;
+	plain = ccmp_decrypt(tk, (const struct ieee80211_hdr *) enc,
+			     enc + 24, NULL, NULL, NULL, enc_len - 24,
+			     &plain_len);
+	wpa_debug_level = MSG_EXCESSIVE;
+	os_free(enc);
+
+	if (plain == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to decrypt CCMP frame");
+		return;
+	}
+
+	if (plain_len != sizeof(frame) - 24 ||
+	    os_memcmp(plain, frame + 24, plain_len) != 0) {
+		wpa_hexdump(MSG_ERROR, "Decryption result did not match",
+			    plain, plain_len);
+	}
+
+	os_free(plain);
+}
+
+
+static void test_vector_ccmp_pv1(void)
+{
+	u8 tk[] = { 0xc9, 0x7c, 0x1f, 0x67, 0xce, 0x37, 0x11, 0x85,
+		    0x51, 0x4a, 0x8a, 0x19, 0xf2, 0xbd, 0xd5, 0x2f };
+	u8 pn[8];
+	u8 frame1[] = {
+		0x61, 0x00, 0xa2, 0xae, 0xa5, 0xb8, 0xfc, 0xba,
+		0x07, 0x00, 0x80, 0x33,
+		0xf8, 0xba, 0x1a, 0x55, 0xd0, 0x2f, 0x85, 0xae,
+		0x96, 0x7b, 0xb6, 0x2f, 0xb6, 0xcd, 0xa8, 0xeb,
+		0x7e, 0x78, 0xa0, 0x50
+	};
+	u8 frame2[] = {
+		0x61, 0x00, 0xa2, 0xae, 0xa5, 0xb8, 0xfc, 0xba,
+		0x07, 0x20, 0x80, 0x33, 0x02, 0xd2, 0xe1, 0x28,
+		0xa5, 0x7c,
+		0xf8, 0xba, 0x1a, 0x55, 0xd0, 0x2f, 0x85, 0xae,
+		0x96, 0x7b, 0xb6, 0x2f, 0xb6, 0xcd, 0xa8, 0xeb,
+		0x7e, 0x78, 0xa0, 0x50
+	};
+	u8 frame3[] = {
+		0x6d, 0x00, 0xa2, 0xae, 0xa5, 0xb8, 0xfc, 0xba,
+		0x52, 0x30, 0xf1, 0x84, 0x44, 0x08, 0x80, 0x33,
+		0xf8, 0xba, 0x1a, 0x55, 0xd0, 0x2f, 0x85, 0xae,
+		0x96, 0x7b, 0xb6, 0x2f, 0xb6, 0xcd, 0xa8, 0xeb,
+		0x7e, 0x78, 0xa0, 0x50
+	};
+	u8 *enc;
+	size_t enc_len;
+	u8 fcs[4];
+	u8 bssid[ETH_ALEN] = { 0xa2, 0xae, 0xa5, 0xb8, 0xfc, 0xba };
+	u8 da[ETH_ALEN] = { 0x02, 0xd2, 0xe1, 0x28, 0xa5, 0x7c };
+	u8 sa[ETH_ALEN] = { 0x52, 0x30, 0xf1, 0x84, 0x44, 0x08 };
+	u16 aid = 7;
+	u32 bpn = 123;
+	u16 sc = 0x3380;
+	int key_id = 0;
+	u16 fc;
+	int tid = 3;
+	u16 sid;
+
+	wpa_printf(MSG_INFO,
+		   "\nIEEE P802.11ah/D10.0, J.6.4 CCMP PV1 test vectors\n");
+
+	wpa_printf(MSG_INFO, "BSSID: " MACSTR, MAC2STR(bssid));
+	wpa_printf(MSG_INFO, "DA: " MACSTR, MAC2STR(da));
+	wpa_printf(MSG_INFO, "SA: " MACSTR, MAC2STR(sa));
+	wpa_printf(MSG_INFO, "Association ID: %u", aid);
+	wpa_printf(MSG_INFO, "Base PN: %u (0x%08x)", bpn, bpn);
+	wpa_printf(MSG_INFO, "SC = 0x%04x (FragNum=%u SeqNum=%u)",
+		   sc, WLAN_GET_SEQ_FRAG(sc), WLAN_GET_SEQ_SEQ(sc));
+	wpa_printf(MSG_INFO, "TID = %u", tid);
+	wpa_printf(MSG_INFO, "Key ID: %u", key_id);
+	wpa_hexdump(MSG_INFO, "TK", tk, sizeof(tk));
+	wpa_printf(MSG_INFO, "PN = SC||BPN");
+	WPA_PUT_LE16(&pn[0], sc);
+	WPA_PUT_LE32(&pn[2], bpn);
+	wpa_hexdump(MSG_INFO, "PN (PN0..PN5)", pn, sizeof(pn));
+
+	wpa_printf(MSG_INFO,
+		   "\nPV1 test vector #1:\nHeader compression used and A3 was previously stored at the receiver\n");
+	fc = WPA_GET_LE16(frame1);
+	wpa_printf(MSG_INFO,
+		   "FC=0x%04x (PV=%u Type=%u PTID/Subtype=%u From_DS=%u More_Fragments=%u Power_Management=%u More_Data=%u Protected_Frame=%u End_of_SP=%u Relayed_Frame=%u Ack_Policy=%u)",
+		   fc,
+		   fc & WLAN_FC_PVER,
+		   (fc & (BIT(2) | BIT(3) | BIT(4))) >> 2,
+		   (fc & (BIT(5) | BIT(6) | BIT(7))) >> 5,
+		   !!(fc & BIT(8)),
+		   !!(fc & BIT(9)),
+		   !!(fc & BIT(10)),
+		   !!(fc & BIT(11)),
+		   !!(fc & BIT(12)),
+		   !!(fc & BIT(13)),
+		   !!(fc & BIT(14)),
+		   !!(fc & BIT(15)));
+	wpa_printf(MSG_INFO, "A1=" MACSTR, MAC2STR(&frame1[2]));
+	sid = WPA_GET_LE16(&frame1[8]);
+	wpa_printf(MSG_INFO,
+		   "A2=%02x %02x (SID: AID=%u A3_Present=%u A4_Present=%u A-MSDU=%u); corresponds to 52:30:f1:84:44:08 in uncompressed header",
+		   frame1[8], frame1[9],
+		   sid & ~(BIT(13) | BIT(14) | BIT(15)),
+		   !!(sid & BIT(13)),
+		   !!(sid & BIT(14)),
+		   !!(sid & BIT(15)));
+	sc = WPA_GET_LE16(&frame1[10]);
+	wpa_printf(MSG_INFO, "Sequence Control: %02x %02x (FN=%u SN=%u)",
+		   frame1[10], frame1[11],
+		   WLAN_GET_SEQ_FRAG(sc), WLAN_GET_SEQ_SEQ(sc));
+	wpa_printf(MSG_INFO, "A3 not present; corresponds to 02:d2:e1:28:a5:7c in uncompressed header");
+	wpa_printf(MSG_INFO, "A4 not present");
+	wpa_hexdump(MSG_INFO, "Plaintext Frame Header", frame1, 12);
+	wpa_hexdump(MSG_INFO, "Plaintext Frame Body",
+		    frame1 + 12, sizeof(frame1) - 12);
+
+	enc = ccmp_encrypt_pv1(tk, &frame1[2], sa, da, frame1, sizeof(frame1),
+			       12, pn, key_id, &enc_len);
+	if (enc == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to encrypt CCMP frame");
+		return;
+	}
+
+	wpa_hexdump(MSG_INFO, "Encrypted Frame Header", enc, 12);
+	wpa_hexdump(MSG_INFO, "Encrypted Frame Frame Body",
+		    enc + 12, enc_len - 12);
+	WPA_PUT_LE32(fcs, ieee80211_crc32(enc, enc_len));
+	wpa_hexdump(MSG_INFO, "Encrypted Frame FCS", fcs, sizeof(fcs));
+
+	wpa_printf(MSG_INFO,
+		   "\nPV1 test vector #2:\nHeader compression used and A3 was not previously stored at the receiver\n");
+	fc = WPA_GET_LE16(frame2);
+	wpa_printf(MSG_INFO,
+		   "FC=0x%04x (PV=%u Type=%u PTID/Subtype=%u From_DS=%u More_Fragments=%u Power_Management=%u More_Data=%u Protected_Frame=%u End_of_SP=%u Relayed_Frame=%u Ack_Policy=%u)",
+		   fc,
+		   fc & WLAN_FC_PVER,
+		   (fc & (BIT(2) | BIT(3) | BIT(4))) >> 2,
+		   (fc & (BIT(5) | BIT(6) | BIT(7))) >> 5,
+		   !!(fc & BIT(8)),
+		   !!(fc & BIT(9)),
+		   !!(fc & BIT(10)),
+		   !!(fc & BIT(11)),
+		   !!(fc & BIT(12)),
+		   !!(fc & BIT(13)),
+		   !!(fc & BIT(14)),
+		   !!(fc & BIT(15)));
+	wpa_printf(MSG_INFO, "A1=" MACSTR, MAC2STR(&frame2[2]));
+	sid = WPA_GET_LE16(&frame2[8]);
+	wpa_printf(MSG_INFO,
+		   "A2=%02x %02x (SID: AID=%u A3_Present=%u A4_Present=%u A-MSDU=%u); corresponds to 52:30:f1:84:44:08 in uncompressed header",
+		   frame2[8], frame2[9],
+		   sid & ~(BIT(13) | BIT(14) | BIT(15)),
+		   !!(sid & BIT(13)),
+		   !!(sid & BIT(14)),
+		   !!(sid & BIT(15)));
+	sc = WPA_GET_LE16(&frame2[10]);
+	wpa_printf(MSG_INFO, "Sequence Control: %02x %02x (FN=%u SN=%u)",
+		   frame2[10], frame2[11],
+		   WLAN_GET_SEQ_FRAG(sc), WLAN_GET_SEQ_SEQ(sc));
+	wpa_printf(MSG_INFO, "A3=" MACSTR, MAC2STR(&frame2[12]));
+	wpa_printf(MSG_INFO, "A4 not present");
+	wpa_hexdump(MSG_INFO, "Plaintext Frame Header", frame2, 18);
+	wpa_hexdump(MSG_INFO, "Plaintext Frame Body",
+		    frame2 + 18, sizeof(frame2) - 18);
+
+	enc = ccmp_encrypt_pv1(tk, &frame2[2], sa, &frame2[12],
+			       frame2, sizeof(frame2), 18, pn, key_id,
+			       &enc_len);
+	if (enc == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to encrypt CCMP frame");
+		return;
+	}
+
+	wpa_hexdump(MSG_INFO, "Encrypted Frame Header", enc, 18);
+	wpa_hexdump(MSG_INFO, "Encrypted Frame Frame Body",
+		    enc + 18, enc_len - 18);
+	WPA_PUT_LE32(fcs, ieee80211_crc32(enc, enc_len));
+	wpa_hexdump(MSG_INFO, "Encrypted Frame FCS", fcs, sizeof(fcs));
+
+	wpa_printf(MSG_INFO,
+		   "\nPV1 test vector #3:\nType 3 frame from SA to DA(=BSSID) (i.e., no separate DA in this example)\n");
+	fc = WPA_GET_LE16(frame3);
+	wpa_printf(MSG_INFO,
+		   "FC=0x%04x (PV=%u Type=%u PTID/Subtype=%u From_DS=%u More_Fragments=%u Power_Management=%u More_Data=%u Protected_Frame=%u End_of_SP=%u Relayed_Frame=%u Ack_Policy=%u)",
+		   fc,
+		   fc & WLAN_FC_PVER,
+		   (fc & (BIT(2) | BIT(3) | BIT(4))) >> 2,
+		   (fc & (BIT(5) | BIT(6) | BIT(7))) >> 5,
+		   !!(fc & BIT(8)),
+		   !!(fc & BIT(9)),
+		   !!(fc & BIT(10)),
+		   !!(fc & BIT(11)),
+		   !!(fc & BIT(12)),
+		   !!(fc & BIT(13)),
+		   !!(fc & BIT(14)),
+		   !!(fc & BIT(15)));
+	wpa_printf(MSG_INFO, "A1=" MACSTR, MAC2STR(&frame3[2]));
+	wpa_printf(MSG_INFO, "A2=" MACSTR, MAC2STR(&frame3[8]));
+	sc = WPA_GET_LE16(&frame3[14]);
+	wpa_printf(MSG_INFO, "Sequence Control: %02x %02x (FN=%u SN=%u)",
+		   frame3[14], frame3[15],
+		   WLAN_GET_SEQ_FRAG(sc), WLAN_GET_SEQ_SEQ(sc));
+	wpa_printf(MSG_INFO,
+		   "A3 not present; corresponds to 02:d2:e1:28:a5:7c in uncompressed header");
+	wpa_printf(MSG_INFO, "A4 not present");
+	wpa_hexdump(MSG_INFO, "Plaintext Frame Header", frame3, 16);
+	wpa_hexdump(MSG_INFO, "Plaintext Frame Body",
+		    frame3 + 16, sizeof(frame3) - 16);
+
+	enc = ccmp_encrypt_pv1(tk, &frame3[2], &frame3[8], da,
+			       frame3, sizeof(frame3), 16, pn, key_id,
+			       &enc_len);
+	if (enc == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to encrypt CCMP frame");
+		return;
+	}
+
+	wpa_hexdump(MSG_INFO, "Encrypted Frame Header", enc, 16);
+	wpa_hexdump(MSG_INFO, "Encrypted Frame Frame Body",
+		    enc + 16, enc_len - 16);
+	WPA_PUT_LE32(fcs, ieee80211_crc32(enc, enc_len));
+	wpa_hexdump(MSG_INFO, "Encrypted Frame FCS", fcs, sizeof(fcs));
+
+	wpa_debug_level = MSG_INFO;
+}
+
+
+static void test_vector_bip(void)
+{
+	u8 igtk[] = {
+		0x4e, 0xa9, 0x54, 0x3e, 0x09, 0xcf, 0x2b, 0x1e,
+		0xca, 0x66, 0xff, 0xc5, 0x8b, 0xde, 0xcb, 0xcf
+	};
+	u8 ipn[] = { 0x04, 0x00, 0x00, 0x00, 0x00, 0x00 };
+	u8 frame[] = {
+		0xc0, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+		0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+		0x02, 0x00
+	};
+	u8 *prot;
+	size_t prot_len;
+
+	wpa_printf(MSG_INFO, "\nIEEE Std 802.11-2012, M.9.1 BIP with broadcast "
+		   "Deauthentication frame\n");
+
+	wpa_hexdump(MSG_INFO, "IGTK", igtk, sizeof(igtk));
+	wpa_hexdump(MSG_INFO, "IPN", ipn, sizeof(ipn));
+	wpa_hexdump(MSG_INFO, "Plaintext frame", frame, sizeof(frame));
+
+	prot = bip_protect(igtk, sizeof(igtk), frame, sizeof(frame),
+			   ipn, 4, &prot_len);
+	if (prot == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to protect BIP frame");
+		return;
+	}
+
+	wpa_hexdump(MSG_INFO, "Protected MPDU (without FCS)", prot, prot_len);
+	os_free(prot);
+}
+
+
+static void test_vector_ccmp_mgmt(void)
+{
+	u8 tk[] = { 0x66, 0xed, 0x21, 0x04, 0x2f, 0x9f, 0x26, 0xd7,
+		    0x11, 0x57, 0x06, 0xe4, 0x04, 0x14, 0xcf, 0x2e };
+	u8 pn[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 };
+	u8 frame[] = {
+		0xc0, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+		0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00,
+		0x02, 0x00
+	};
+	u8 *enc, *plain;
+	size_t enc_len, plain_len;
+
+	wpa_printf(MSG_INFO, "\nIEEE Std 802.11-2012, M.9.2 CCMP with unicast "
+		   "Deauthentication frame\n");
+
+	wpa_hexdump(MSG_INFO, "TK", tk, sizeof(tk));
+	wpa_hexdump(MSG_INFO, "PN", pn, sizeof(pn));
+	wpa_hexdump(MSG_INFO, "802.11 Header", frame, 24);
+	wpa_hexdump(MSG_INFO, "Plaintext Data", frame + 24, sizeof(frame) - 24);
+
+	enc = ccmp_encrypt(tk, frame, sizeof(frame), 24, NULL, NULL, NULL, NULL,
+			   pn, 0, &enc_len);
+	if (enc == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to encrypt CCMP frame");
+		return;
+	}
+
+	wpa_hexdump(MSG_INFO, "Encrypted MPDU (without FCS)", enc, enc_len);
+
+	wpa_debug_level = MSG_INFO;
+	plain = ccmp_decrypt(tk, (const struct ieee80211_hdr *) enc,
+			     enc + 24, NULL, NULL, NULL, enc_len - 24,
+			     &plain_len);
+	wpa_debug_level = MSG_EXCESSIVE;
+	os_free(enc);
+
+	if (plain == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to decrypt CCMP frame");
+		return;
+	}
+
+	if (plain_len != sizeof(frame) - 24 ||
+	    os_memcmp(plain, frame + 24, plain_len) != 0) {
+		wpa_hexdump(MSG_ERROR, "Decryption result did not match",
+			    plain, plain_len);
+	}
+
+	os_free(plain);
+}
+
+
+struct gcmp_test {
+	u8 tk[16];
+	u8 pn[6];
+	u8 frame[300];
+	size_t hdr_len;
+	size_t payload_len;
+	u8 mic[16];
+	u8 encr[300];
+};
+
+static const struct gcmp_test gcmp_vectors[] =
+{
+	{
+		.tk = { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa },
+		.pn = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 },
+		.frame = {
+			0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		},
+		.hdr_len = 24,
+		.payload_len = 256,
+		.mic = {
+			0x80, 0xCB, 0x06, 0x62, 0xEA, 0x71, 0xAB, 0xFD,
+			0x9F, 0x04, 0xC7, 0xF8, 0x72, 0xF5, 0x80, 0x90 },
+		.encr = {
+			0x5F, 0x55, 0x78, 0xC1, 0x8F, 0x13, 0x7A, 0xD2,
+			0x79, 0xBF, 0x3F, 0x2B, 0x24, 0xC7, 0xBD, 0x8F,
+			0x27, 0x7A, 0x1B, 0xE6, 0x77, 0x0D, 0xA1, 0xD9,
+			0x8B, 0x70, 0xC6, 0xD2, 0x8A, 0xE0, 0x1C, 0x55,
+			0x9E, 0xCB, 0xA6, 0xA0, 0x1D, 0xB0, 0x67, 0xC5,
+			0xA2, 0x7E, 0x4D, 0xB0, 0x8C, 0xDA, 0xDC, 0x77,
+			0x52, 0xAD, 0x63, 0x7E, 0xAF, 0x0A, 0x18, 0xED,
+			0x13, 0xFB, 0xAA, 0x14, 0x3B, 0xAF, 0xEF, 0x18,
+			0xF8, 0xFB, 0xCE, 0x4C, 0x65, 0xE8, 0x6B, 0xD0,
+			0x2A, 0x87, 0xB6, 0x01, 0xB7, 0xEA, 0xB9, 0x3F,
+			0x2B, 0xBC, 0x87, 0x4C, 0x8A, 0x71, 0x05, 0x80,
+			0xF5, 0x02, 0x34, 0x1A, 0x6A, 0x53, 0x39, 0x31,
+			0x43, 0xDE, 0x4C, 0x9E, 0xC6, 0xA2, 0x86, 0xF1,
+			0x25, 0x71, 0x83, 0x78, 0xAE, 0xDC, 0x84, 0xEB,
+			0xA2, 0xB3, 0x0F, 0x5C, 0x28, 0xBB, 0x5D, 0x75,
+			0xC6, 0xB0, 0x25, 0x46, 0x6D, 0x06, 0x51, 0xC7,
+			0x22, 0xDC, 0x71, 0x15, 0x1F, 0x21, 0x2D, 0x68,
+			0x87, 0x82, 0x8A, 0x03, 0x82, 0xE9, 0x28, 0x8A,
+			0x7F, 0x43, 0xD5, 0x2B, 0x7D, 0x25, 0x08, 0x61,
+			0x57, 0x64, 0x69, 0x54, 0xBB, 0x43, 0xB5, 0x7E,
+			0xA5, 0x87, 0xA0, 0x25, 0xF4, 0x0C, 0xE7, 0x45,
+			0x11, 0xE4, 0xDD, 0x22, 0x85, 0xB4, 0x0B, 0xA3,
+			0xF3, 0xB9, 0x62, 0x62, 0xCB, 0xC2, 0x8C, 0x6A,
+			0xA7, 0xBE, 0x44, 0x3E, 0x7B, 0x41, 0xE1, 0xEB,
+			0xFF, 0x52, 0x48, 0x57, 0xA6, 0x81, 0x68, 0x97,
+			0x75, 0x01, 0x15, 0xB0, 0x23, 0x1A, 0xB7, 0xC2,
+			0x84, 0x72, 0xC0, 0x6D, 0xD0, 0xB4, 0x9B, 0xE9,
+			0xF3, 0x69, 0xA8, 0xC3, 0x9C, 0xCD, 0x0D, 0xB7,
+			0x98, 0x35, 0x10, 0xE1, 0xAE, 0x8F, 0x05, 0xD7,
+			0x75, 0x45, 0xE0, 0x23, 0x5C, 0xDB, 0xD6, 0x12,
+			0xF3, 0x15, 0x07, 0x54, 0xCE, 0xE5, 0xCE, 0x6A,
+			0x12, 0x25, 0xD9, 0x95, 0x25, 0x02, 0x6F, 0x74
+		}
+	},
+	{
+		.tk = { 0xc9, 0x7c, 0x1f, 0x67, 0xce, 0x37, 0x11, 0x85,
+			0x51, 0x4a, 0x8a, 0x19, 0xf2, 0xbd, 0xd5, 0x2f },
+		.pn = { 0x00, 0x89, 0x5F, 0x5F, 0x2B, 0x08 },
+		.frame = {
+			0x88, 0x48, 0x0b, 0x00, 0x0f, 0xd2, 0xe1, 0x28,
+			0xa5, 0x7c, 0x50, 0x30, 0xf1, 0x84, 0x44, 0x08,
+			0x50, 0x30, 0xf1, 0x84, 0x44, 0x08, 0x80, 0x33,
+			0x03, 0x00,
+
+			0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+			0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+			0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+			0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+			0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27
+		},
+		.hdr_len = 26,
+		.payload_len = 40,
+		.mic = {
+			0xde, 0xf6, 0x19, 0xc2, 0xa3, 0x74, 0xb6, 0xdf,
+			0x66, 0xff, 0xa5, 0x3b, 0x6c, 0x69, 0xd7, 0x9e },
+		.encr = {
+			0x60, 0xe9, 0x70, 0x0c, 0xc4, 0xd4, 0x0a, 0xc6,
+			0xd2, 0x88, 0xb2, 0x01, 0xc3, 0x8f, 0x5b, 0xf0,
+			0x8b, 0x80, 0x74, 0x42, 0x64, 0x0a, 0x15, 0x96,
+			0xe5, 0xdb, 0xda, 0xd4, 0x1d, 0x1f, 0x36, 0x23,
+			0xf4, 0x5d, 0x7a, 0x12, 0xdb, 0x7a, 0xfb, 0x23
+		}
+	}
+};
+
+
+static int run_gcmp(int idx, const struct gcmp_test *vector)
+{
+	u8 *enc, *plain;
+	size_t enc_len, plain_len;
+	u8 fcs[4];
+	int err = 0;
+
+	wpa_printf(MSG_INFO,
+		   "\nIEEE Std 802.11ad-2012, M.11.1 GCMP test mpdu #%d\n",
+		   idx);
+
+	wpa_hexdump(MSG_INFO, "TK", vector->tk, sizeof(vector->tk));
+	wpa_hexdump(MSG_INFO, "PN", vector->pn, sizeof(vector->pn));
+	wpa_hexdump(MSG_INFO, "802.11 Header", vector->frame, vector->hdr_len);
+	wpa_hexdump(MSG_INFO, "Plaintext Data",
+		    vector->frame + vector->hdr_len,
+		    vector->payload_len);
+
+	enc = gcmp_encrypt(vector->tk, sizeof(vector->tk),
+			   vector->frame,
+			   vector->hdr_len + vector->payload_len,
+			   vector->hdr_len,
+			   vector->hdr_len == 26 ?
+			   vector->frame + vector->hdr_len - 2 : NULL,
+			   NULL, NULL, NULL,
+			   vector->pn, 0, &enc_len);
+	if (enc == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to encrypt GCMP frame");
+		return 1;
+	}
+
+	wpa_hexdump(MSG_INFO, "Encrypted MPDU (without FCS)", enc, enc_len);
+	if (os_memcmp(vector->encr, enc + vector->hdr_len + 8,
+		      vector->payload_len) != 0) {
+		wpa_printf(MSG_ERROR, "GCMP test mpdu #%d enctypted data mismatch",
+			   idx);
+		err++;
+	}
+	if (os_memcmp(vector->mic, enc + enc_len - sizeof(vector->mic),
+		      sizeof(vector->mic)) != 0) {
+		wpa_printf(MSG_ERROR, "GCMP test mpdu #%d MIC mismatch", idx);
+		err++;
+	}
+	WPA_PUT_LE32(fcs, ieee80211_crc32(enc, enc_len));
+	wpa_hexdump(MSG_INFO, "FCS", fcs, sizeof(fcs));
+
+	wpa_debug_level = MSG_INFO;
+	plain = gcmp_decrypt(vector->tk, sizeof(vector->tk),
+			     (const struct ieee80211_hdr *) enc, NULL, NULL,
+			     NULL, enc + vector->hdr_len,
+			     enc_len - vector->hdr_len, &plain_len);
+	wpa_debug_level = MSG_EXCESSIVE;
+	os_free(enc);
+
+	if (plain == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to decrypt GCMP frame");
+		return 1;
+	}
+
+	if (plain_len != vector->payload_len ||
+	    os_memcmp(plain, vector->frame + vector->hdr_len, plain_len) != 0) {
+		wpa_hexdump(MSG_ERROR, "Decryption result did not match",
+			    plain, plain_len);
+		err++;
+	}
+
+	os_free(plain);
+
+	return err;
+}
+
+
+static int test_vector_gcmp(void)
+{
+	int err = 0;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(gcmp_vectors); i++) {
+		if (run_gcmp(i + 1, &gcmp_vectors[i]))
+			err++;
+
+	}
+
+	return err;
+}
+
+
+static int test_vector_gcmp_256(void)
+{
+	u8 tk[] = { 0xc9, 0x7c, 0x1f, 0x67, 0xce, 0x37, 0x11, 0x85,
+		    0x51, 0x4a, 0x8a, 0x19, 0xf2, 0xbd, 0xd5, 0x2f,
+		    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
+	u8 pn[] = {
+		0x00, 0x89, 0x5F, 0x5F, 0x2B, 0x08
+	};
+	u8 frame[] = {
+		0x88, 0x48, 0x0b, 0x00, 0x0f, 0xd2, 0xe1, 0x28,
+		0xa5, 0x7c, 0x50, 0x30, 0xf1, 0x84, 0x44, 0x08,
+		0x50, 0x30, 0xf1, 0x84, 0x44, 0x08, 0x80, 0x33,
+		0x03, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
+		0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
+		0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
+		0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
+		0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
+		0x26, 0x27
+	};
+	u8 encr[] = {
+		0x88, 0x48, 0x0b, 0x00, 0x0f, 0xd2, 0xe1, 0x28,
+		0xa5, 0x7c, 0x50, 0x30, 0xf1, 0x84, 0x44, 0x08,
+		0x50, 0x30, 0xf1, 0x84, 0x44, 0x08, 0x80, 0x33,
+		0x03, 0x00, 0x08, 0x2b, 0x00, 0x20, 0x5f, 0x5f,
+		0x89, 0x00, 0x65, 0x83, 0x43, 0xc8, 0xb1, 0x44,
+		0x47, 0xd9, 0x21, 0x1d, 0xef, 0xd4, 0x6a, 0xd8,
+		0x9c, 0x71, 0x0c, 0x6f, 0xc3, 0x33, 0x33, 0x23,
+		0x6e, 0x39, 0x97, 0xb9, 0x17, 0x6a, 0x5a, 0x8b,
+		0xe7, 0x79, 0xb2, 0x12, 0x66, 0x55, 0x5e, 0x70,
+		0xad, 0x79, 0x11, 0x43, 0x16, 0x85, 0x90, 0x95,
+		0x47, 0x3d, 0x5b, 0x1b, 0xd5, 0x96, 0xb3, 0xde,
+		0xa3, 0xbf
+	};
+	u8 *enc, *plain;
+	size_t enc_len, plain_len;
+	u8 fcs[4];
+	int err = 0;
+
+	wpa_printf(MSG_INFO, "\nIEEE P802.11ac/D7.0, M.11.1 GCMP-256 test vector\n");
+
+	wpa_hexdump(MSG_INFO, "TK", tk, sizeof(tk));
+	wpa_hexdump(MSG_INFO, "PN", pn, sizeof(pn));
+	wpa_hexdump(MSG_INFO, "802.11 Header", frame, 26);
+	wpa_hexdump(MSG_INFO, "Plaintext Data", frame + 26, sizeof(frame) - 26);
+
+	enc = gcmp_encrypt(tk, sizeof(tk), frame, sizeof(frame), 26, frame + 24,
+			   NULL, NULL, NULL, pn, 0, &enc_len);
+	if (enc == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to encrypt GCMP frame");
+		return 1;
+	}
+
+	wpa_hexdump(MSG_INFO, "Encrypted MPDU (without FCS)", enc, enc_len);
+	if (enc_len != sizeof(encr) || os_memcmp(enc, encr, enc_len) != 0) {
+		wpa_printf(MSG_ERROR, "GCMP-256 test vector mismatch");
+		err++;
+	}
+	WPA_PUT_LE32(fcs, ieee80211_crc32(enc, enc_len));
+	wpa_hexdump(MSG_INFO, "FCS", fcs, sizeof(fcs));
+
+	wpa_debug_level = MSG_INFO;
+	plain = gcmp_decrypt(tk, sizeof(tk), (const struct ieee80211_hdr *) enc,
+			     NULL, NULL, NULL, enc + 26, enc_len - 26,
+			     &plain_len);
+	wpa_debug_level = MSG_EXCESSIVE;
+	os_free(enc);
+
+	if (plain == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to decrypt GCMP frame");
+		return 1;
+	}
+
+	if (plain_len != sizeof(frame) - 26 ||
+	    os_memcmp(plain, frame + 26, plain_len) != 0) {
+		wpa_hexdump(MSG_ERROR, "Decryption result did not match",
+			    plain, plain_len);
+		err++;
+	}
+
+	os_free(plain);
+
+	return err;
+}
+
+
+static int test_vector_ccmp_256(void)
+{
+	u8 tk[] = { 0xc9, 0x7c, 0x1f, 0x67, 0xce, 0x37, 0x11, 0x85,
+		    0x51, 0x4a, 0x8a, 0x19, 0xf2, 0xbd, 0xd5, 0x2f,
+		    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
+	u8 pn[] = { 0xB5, 0x03, 0x97, 0x76, 0xE7, 0x0C };
+	u8 frame[] = {
+		0x08, 0x48, 0xc3, 0x2c, 0x0f, 0xd2, 0xe1, 0x28,
+		0xa5, 0x7c, 0x50, 0x30, 0xf1, 0x84, 0x44, 0x08,
+		0xab, 0xae, 0xa5, 0xb8, 0xfc, 0xba, 0x80, 0x33,
+		0xf8, 0xba, 0x1a, 0x55, 0xd0, 0x2f, 0x85, 0xae,
+		0x96, 0x7b, 0xb6, 0x2f, 0xb6, 0xcd, 0xa8, 0xeb,
+		0x7e, 0x78, 0xa0, 0x50
+	};
+	u8 encr[] = {
+		0x08, 0x48, 0xc3, 0x2c, 0x0f, 0xd2, 0xe1, 0x28,
+		0xa5, 0x7c, 0x50, 0x30, 0xf1, 0x84, 0x44, 0x08,
+		0xab, 0xae, 0xa5, 0xb8, 0xfc, 0xba, 0x80, 0x33,
+		0x0c, 0xe7, 0x00, 0x20, 0x76, 0x97, 0x03, 0xb5,
+		0x6d, 0x15, 0x5d, 0x88, 0x32, 0x66, 0x82, 0x56,
+		0xd6, 0xa9, 0x2b, 0x78, 0xe1, 0x1d, 0x8e, 0x54,
+		0x49, 0x5d, 0xd1, 0x74, 0x80, 0xaa, 0x56, 0xc9,
+		0x49, 0x2e, 0x88, 0x2b, 0x97, 0x64, 0x2f, 0x80,
+		0xd5, 0x0f, 0xe9, 0x7b
+
+	};
+	u8 *enc, *plain;
+	size_t enc_len, plain_len;
+	u8 fcs[4];
+	int err = 0;
+
+	wpa_printf(MSG_INFO, "\nIEEE P802.11ac/D7.0, M.6.4 CCMP-256 test vector\n");
+
+	wpa_hexdump(MSG_INFO, "TK", tk, sizeof(tk));
+	wpa_hexdump(MSG_INFO, "PN", pn, sizeof(pn));
+	wpa_hexdump(MSG_INFO, "802.11 Header", frame, 24);
+	wpa_hexdump(MSG_INFO, "Plaintext Data", frame + 24, sizeof(frame) - 24);
+
+	enc = ccmp_256_encrypt(tk, frame, sizeof(frame), 24, NULL, NULL, NULL,
+			       NULL, pn, 0, &enc_len);
+	if (enc == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to encrypt CCMP frame");
+		return 1;
+	}
+
+	wpa_hexdump(MSG_INFO, "Encrypted MPDU (without FCS)", enc, enc_len);
+	if (enc_len != sizeof(encr) || os_memcmp(enc, encr, enc_len) != 0) {
+		wpa_printf(MSG_ERROR, "CCMP-256 test vector mismatch");
+		err++;
+	}
+	WPA_PUT_LE32(fcs, ieee80211_crc32(enc, enc_len));
+	wpa_hexdump(MSG_INFO, "FCS", fcs, sizeof(fcs));
+
+	wpa_debug_level = MSG_INFO;
+	plain = ccmp_256_decrypt(tk, (const struct ieee80211_hdr *) enc,
+				 enc + 24, NULL, NULL, NULL, enc_len - 24,
+				 &plain_len);
+	wpa_debug_level = MSG_EXCESSIVE;
+	os_free(enc);
+
+	if (plain == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to decrypt CCMP-256 frame");
+		return 1;
+	}
+
+	if (plain_len != sizeof(frame) - 24 ||
+	    os_memcmp(plain, frame + 24, plain_len) != 0) {
+		wpa_hexdump(MSG_ERROR, "Decryption result did not match",
+			    plain, plain_len);
+		err++;
+	}
+
+	os_free(plain);
+
+	return err;
+}
+
+
+static int test_vector_bip_gmac_128(void)
+{
+	u8 igtk[] = {
+		0x4e, 0xa9, 0x54, 0x3e, 0x09, 0xcf, 0x2b, 0x1e,
+		0xca, 0x66, 0xff, 0xc5, 0x8b, 0xde, 0xcb, 0xcf
+	};
+	u8 ipn[] = { 0x04, 0x00, 0x00, 0x00, 0x00, 0x00 };
+	u8 frame[] = {
+		0xc0, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+		0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+		0x02, 0x00
+	};
+	u8 res[] = {
+		0xc0, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+		0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+		0x02, 0x00, 0x4c, 0x18, 0x04, 0x00, 0x04, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x3e, 0xd8, 0x62, 0xfb,
+		0x0f, 0x33, 0x38, 0xdd, 0x33, 0x86, 0xc8, 0x97,
+		0xe2, 0xed, 0x05, 0x3d
+	};
+	u8 *prot;
+	size_t prot_len;
+	int err = 0;
+
+	wpa_printf(MSG_INFO, "\nIEEE P802.11ac/D7.0, M.9.1 BIP-GMAC-128 with broadcast "
+		   "Deauthentication frame\n");
+
+	wpa_hexdump(MSG_INFO, "IGTK", igtk, sizeof(igtk));
+	wpa_hexdump(MSG_INFO, "IPN", ipn, sizeof(ipn));
+	wpa_hexdump(MSG_INFO, "Plaintext frame", frame, sizeof(frame));
+
+	prot = bip_gmac_protect(igtk, sizeof(igtk), frame, sizeof(frame),
+				ipn, 4, &prot_len);
+	if (prot == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to protect BIP-GMAC-128 frame");
+		return 1;
+	}
+
+	wpa_hexdump(MSG_INFO, "Protected MPDU (without FCS)", prot, prot_len);
+	if (prot_len != sizeof(res) || os_memcmp(res, prot, prot_len) != 0) {
+		wpa_printf(MSG_ERROR, "BIP-GMAC-128 test vector mismatch");
+		err++;
+	}
+	os_free(prot);
+
+	return err;
+}
+
+
+static int test_vector_bip_gmac_256(void)
+{
+	u8 igtk[] = {
+		0x4e, 0xa9, 0x54, 0x3e, 0x09, 0xcf, 0x2b, 0x1e,
+		0xca, 0x66, 0xff, 0xc5, 0x8b, 0xde, 0xcb, 0xcf,
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+	};
+	u8 ipn[] = { 0x04, 0x00, 0x00, 0x00, 0x00, 0x00 };
+	u8 frame[] = {
+		0xc0, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+		0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+		0x02, 0x00
+	};
+	u8 res[] = {
+		0xc0, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+		0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+		0x02, 0x00, 0x4c, 0x18, 0x04, 0x00, 0x04, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x23, 0xbe, 0x59, 0xdc,
+		0xc7, 0x02, 0x2e, 0xe3, 0x83, 0x62, 0x7e, 0xbb,
+		0x10, 0x17, 0xdd, 0xfc
+	};
+	u8 *prot;
+	size_t prot_len;
+	int err = 0;
+
+	wpa_printf(MSG_INFO, "\nIEEE P802.11ac/D7.0, M.9.1 BIP-GMAC-256 with broadcast Deauthentication frame\n");
+
+	wpa_hexdump(MSG_INFO, "IGTK", igtk, sizeof(igtk));
+	wpa_hexdump(MSG_INFO, "IPN", ipn, sizeof(ipn));
+	wpa_hexdump(MSG_INFO, "Plaintext frame", frame, sizeof(frame));
+
+	prot = bip_gmac_protect(igtk, sizeof(igtk), frame, sizeof(frame),
+				ipn, 4, &prot_len);
+	if (prot == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to protect BIP-GMAC-256 frame");
+		return 1;
+	}
+
+	wpa_hexdump(MSG_INFO, "Protected MPDU (without FCS)", prot, prot_len);
+	if (prot_len != sizeof(res) || os_memcmp(res, prot, prot_len) != 0) {
+		wpa_printf(MSG_ERROR, "BIP-GMAC-128 test vector mismatch");
+		err++;
+	}
+	os_free(prot);
+
+	return err;
+}
+
+
+int main(int argc, char *argv[])
+{
+	int errors = 0;
+
+	wpa_debug_level = MSG_EXCESSIVE;
+	wpa_debug_show_keys = 1;
+
+	if (os_program_init())
+		return -1;
+
+	test_vector_tkip();
+	test_vector_ccmp();
+	test_vector_ccmp_pv1();
+	test_vector_bip();
+	test_vector_ccmp_mgmt();
+	errors += test_vector_gcmp();
+	errors += test_vector_gcmp_256();
+	errors += test_vector_ccmp_256();
+	errors += test_vector_bip_gmac_128();
+	errors += test_vector_bip_gmac_256();
+
+	if (errors)
+		wpa_printf(MSG_INFO, "One or more test vectors failed");
+	os_program_deinit();
+
+	return errors ? -1 : 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/tkip.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/tkip.c
new file mode 100644
index 0000000..2015611
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/tkip.c
@@ -0,0 +1,490 @@
+/*
+ * Temporal Key Integrity Protocol (TKIP)
+ * Copyright (c) 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 "utils/crc32.h"
+#include "common/ieee802_11_defs.h"
+#include "wlantest.h"
+
+
+void wep_crypt(u8 *key, u8 *buf, size_t plen);
+
+
+static inline u16 RotR1(u16 val)
+{
+	return (val >> 1) | (val << 15);
+}
+
+
+static inline u8 Lo8(u16 val)
+{
+	return val & 0xff;
+}
+
+
+static inline u8 Hi8(u16 val)
+{
+	return val >> 8;
+}
+
+
+static inline u16 Lo16(u32 val)
+{
+	return val & 0xffff;
+}
+
+
+static inline u16 Hi16(u32 val)
+{
+	return val >> 16;
+}
+
+
+static inline u16 Mk16(u8 hi, u8 lo)
+{
+	return lo | (((u16) hi) << 8);
+}
+
+
+static inline u16 Mk16_le(u16 *v)
+{
+	return le_to_host16(*v);
+}
+
+
+static const u16 Sbox[256] =
+{
+	0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154,
+	0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A,
+	0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B,
+	0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B,
+	0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F,
+	0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F,
+	0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5,
+	0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F,
+	0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB,
+	0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397,
+	0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED,
+	0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A,
+	0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194,
+	0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3,
+	0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104,
+	0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D,
+	0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39,
+	0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695,
+	0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83,
+	0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76,
+	0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4,
+	0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B,
+	0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0,
+	0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018,
+	0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751,
+	0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85,
+	0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12,
+	0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9,
+	0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7,
+	0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A,
+	0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8,
+	0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A,
+};
+
+
+static inline u16 _S_(u16 v)
+{
+	u16 t = Sbox[Hi8(v)];
+	return Sbox[Lo8(v)] ^ ((t << 8) | (t >> 8));
+}
+
+
+#define PHASE1_LOOP_COUNT 8
+
+static void tkip_mixing_phase1(u16 *TTAK, const u8 *TK, const u8 *TA, u32 IV32)
+{
+	int i, j;
+
+	/* Initialize the 80-bit TTAK from TSC (IV32) and TA[0..5] */
+	TTAK[0] = Lo16(IV32);
+	TTAK[1] = Hi16(IV32);
+	TTAK[2] = Mk16(TA[1], TA[0]);
+	TTAK[3] = Mk16(TA[3], TA[2]);
+	TTAK[4] = Mk16(TA[5], TA[4]);
+
+	for (i = 0; i < PHASE1_LOOP_COUNT; i++) {
+		j = 2 * (i & 1);
+		TTAK[0] += _S_(TTAK[4] ^ Mk16(TK[1 + j], TK[0 + j]));
+		TTAK[1] += _S_(TTAK[0] ^ Mk16(TK[5 + j], TK[4 + j]));
+		TTAK[2] += _S_(TTAK[1] ^ Mk16(TK[9 + j], TK[8 + j]));
+		TTAK[3] += _S_(TTAK[2] ^ Mk16(TK[13 + j], TK[12 + j]));
+		TTAK[4] += _S_(TTAK[3] ^ Mk16(TK[1 + j], TK[0 + j])) + i;
+	}
+}
+
+
+static void tkip_mixing_phase2(u8 *WEPSeed, const u8 *TK, const u16 *TTAK,
+			       u16 IV16)
+{
+	u16 PPK[6];
+
+	/* Step 1 - make copy of TTAK and bring in TSC */
+	PPK[0] = TTAK[0];
+	PPK[1] = TTAK[1];
+	PPK[2] = TTAK[2];
+	PPK[3] = TTAK[3];
+	PPK[4] = TTAK[4];
+	PPK[5] = TTAK[4] + IV16;
+
+	/* Step 2 - 96-bit bijective mixing using S-box */
+	PPK[0] += _S_(PPK[5] ^ Mk16_le((u16 *) &TK[0]));
+	PPK[1] += _S_(PPK[0] ^ Mk16_le((u16 *) &TK[2]));
+	PPK[2] += _S_(PPK[1] ^ Mk16_le((u16 *) &TK[4]));
+	PPK[3] += _S_(PPK[2] ^ Mk16_le((u16 *) &TK[6]));
+	PPK[4] += _S_(PPK[3] ^ Mk16_le((u16 *) &TK[8]));
+	PPK[5] += _S_(PPK[4] ^ Mk16_le((u16 *) &TK[10]));
+
+	PPK[0] += RotR1(PPK[5] ^ Mk16_le((u16 *) &TK[12]));
+	PPK[1] += RotR1(PPK[0] ^ Mk16_le((u16 *) &TK[14]));
+	PPK[2] += RotR1(PPK[1]);
+	PPK[3] += RotR1(PPK[2]);
+	PPK[4] += RotR1(PPK[3]);
+	PPK[5] += RotR1(PPK[4]);
+
+	/* Step 3 - bring in last of TK bits, assign 24-bit WEP IV value
+	 * WEPSeed[0..2] is transmitted as WEP IV */
+	WEPSeed[0] = Hi8(IV16);
+	WEPSeed[1] = (Hi8(IV16) | 0x20) & 0x7F;
+	WEPSeed[2] = Lo8(IV16);
+	WEPSeed[3] = Lo8((PPK[5] ^ Mk16_le((u16 *) &TK[0])) >> 1);
+	WPA_PUT_LE16(&WEPSeed[4], PPK[0]);
+	WPA_PUT_LE16(&WEPSeed[6], PPK[1]);
+	WPA_PUT_LE16(&WEPSeed[8], PPK[2]);
+	WPA_PUT_LE16(&WEPSeed[10], PPK[3]);
+	WPA_PUT_LE16(&WEPSeed[12], PPK[4]);
+	WPA_PUT_LE16(&WEPSeed[14], PPK[5]);
+}
+
+
+static inline u32 rotl(u32 val, int bits)
+{
+	return (val << bits) | (val >> (32 - bits));
+}
+
+
+static inline u32 rotr(u32 val, int bits)
+{
+	return (val >> bits) | (val << (32 - bits));
+}
+
+
+static inline u32 xswap(u32 val)
+{
+	return ((val & 0x00ff00ff) << 8) | ((val & 0xff00ff00) >> 8);
+}
+
+
+#define michael_block(l, r)	\
+do {				\
+	r ^= rotl(l, 17);	\
+	l += r;			\
+	r ^= xswap(l);		\
+	l += r;			\
+	r ^= rotl(l, 3);	\
+	l += r;			\
+	r ^= rotr(l, 2);	\
+	l += r;			\
+} while (0)
+
+
+static void michael_mic(const u8 *key, const u8 *hdr, const u8 *data,
+			size_t data_len, u8 *mic)
+{
+	u32 l, r;
+	int i, blocks, last;
+
+	l = WPA_GET_LE32(key);
+	r = WPA_GET_LE32(key + 4);
+
+	/* Michael MIC pseudo header: DA, SA, 3 x 0, Priority */
+	l ^= WPA_GET_LE32(hdr);
+	michael_block(l, r);
+	l ^= WPA_GET_LE32(&hdr[4]);
+	michael_block(l, r);
+	l ^= WPA_GET_LE32(&hdr[8]);
+	michael_block(l, r);
+	l ^= WPA_GET_LE32(&hdr[12]);
+	michael_block(l, r);
+
+	/* 32-bit blocks of data */
+	blocks = data_len / 4;
+	last = data_len % 4;
+	for (i = 0; i < blocks; i++) {
+		l ^= WPA_GET_LE32(&data[4 * i]);
+		michael_block(l, r);
+	}
+
+	/* Last block and padding (0x5a, 4..7 x 0) */
+	switch (last) {
+	case 0:
+		l ^= 0x5a;
+		break;
+	case 1:
+		l ^= data[4 * i] | 0x5a00;
+		break;
+	case 2:
+		l ^= data[4 * i] | (data[4 * i + 1] << 8) | 0x5a0000;
+		break;
+	case 3:
+		l ^= data[4 * i] | (data[4 * i + 1] << 8) |
+			(data[4 * i + 2] << 16) | 0x5a000000;
+		break;
+	}
+	michael_block(l, r);
+	/* l ^= 0; */
+	michael_block(l, r);
+
+	WPA_PUT_LE32(mic, l);
+	WPA_PUT_LE32(mic + 4, r);
+}
+
+
+static void michael_mic_hdr(const struct ieee80211_hdr *hdr11, u8 *hdr)
+{
+	int hdrlen = 24;
+	u16 fc = le_to_host16(hdr11->frame_control);
+
+	switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) {
+	case WLAN_FC_TODS:
+		os_memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */
+		os_memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */
+		break;
+	case WLAN_FC_FROMDS:
+		os_memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */
+		os_memcpy(hdr + ETH_ALEN, hdr11->addr3, ETH_ALEN); /* SA */
+		break;
+	case WLAN_FC_FROMDS | WLAN_FC_TODS:
+		os_memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */
+		os_memcpy(hdr + ETH_ALEN, hdr11 + 1, ETH_ALEN); /* SA */
+		hdrlen += ETH_ALEN;
+		break;
+	case 0:
+		os_memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */
+		os_memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */
+		break;
+	}
+
+	if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA &&
+	    (WLAN_FC_GET_STYPE(fc) & 0x08)) {
+		const u8 *qos = ((const u8 *) hdr11) + hdrlen;
+		hdr[12] = qos[0] & 0x0f; /* priority */
+	} else
+		hdr[12] = 0; /* priority */
+
+	hdr[13] = hdr[14] = hdr[15] = 0; /* reserved */
+}
+
+
+u8 * tkip_decrypt(const u8 *tk, const struct ieee80211_hdr *hdr,
+		  const u8 *data, size_t data_len, size_t *decrypted_len,
+		  enum michael_mic_result *mic_res, struct tkip_frag *frag)
+{
+	u16 iv16;
+	u32 iv32;
+	u16 ttak[5];
+	u8 rc4key[16];
+	u8 *plain;
+	size_t plain_len;
+	u32 icv, rx_icv;
+	const u8 *mic_key;
+	u8 michael_hdr[16];
+	u8 mic[8];
+	u16 fc = le_to_host16(hdr->frame_control);
+	const u8 *full_payload;
+	size_t full_payload_len;
+	u16 sc = le_to_host16(hdr->seq_ctrl);
+	u16 sn;
+	u8 fn;
+
+	if (data_len < 8 + 4)
+		return NULL;
+
+	iv16 = (data[0] << 8) | data[2];
+	iv32 = WPA_GET_LE32(&data[4]);
+	wpa_printf(MSG_EXCESSIVE, "TKIP decrypt: iv32=%08x iv16=%04x",
+		   iv32, iv16);
+
+	tkip_mixing_phase1(ttak, tk, hdr->addr2, iv32);
+	wpa_hexdump(MSG_EXCESSIVE, "TKIP TTAK", (u8 *) ttak, sizeof(ttak));
+	tkip_mixing_phase2(rc4key, tk, ttak, iv16);
+	wpa_hexdump(MSG_EXCESSIVE, "TKIP RC4KEY", rc4key, sizeof(rc4key));
+
+	plain_len = data_len - 8;
+	plain = os_memdup(data + 8, plain_len);
+	if (plain == NULL)
+		return NULL;
+	wep_crypt(rc4key, plain, plain_len);
+
+	icv = ieee80211_crc32(plain, plain_len - 4);
+	rx_icv = WPA_GET_LE32(plain + plain_len - 4);
+	if (icv != rx_icv) {
+		wpa_printf(MSG_INFO, "TKIP ICV mismatch in frame from " MACSTR,
+			   MAC2STR(hdr->addr2));
+		wpa_printf(MSG_DEBUG, "TKIP calculated ICV %08x  received ICV "
+			   "%08x", icv, rx_icv);
+		os_free(plain);
+		return NULL;
+	}
+	plain_len -= 4;
+
+	full_payload = plain;
+	full_payload_len = plain_len;
+
+	sn = WLAN_GET_SEQ_SEQ(sc);
+	fn = WLAN_GET_SEQ_FRAG(sc);
+
+	if (frag) {
+		/* MSDU reassembly for Michael MIC validation */
+		if (fn == 0 && (fc & WLAN_FC_MOREFRAG)) {
+			/* Start of a new fragmented MSDU */
+			wpabuf_free(frag->buf);
+			frag->buf = NULL;
+			frag->buf = wpabuf_alloc_copy(plain, plain_len);
+			os_memcpy(frag->ra, hdr->addr1, ETH_ALEN);
+			os_memcpy(frag->ta, hdr->addr2, ETH_ALEN);
+			frag->sn = sn;
+			frag->fn = 0;
+		}
+
+		if (frag->buf && (fn || (fc & WLAN_FC_MOREFRAG)) &&
+		    sn == frag->sn && fn == frag->fn + 1 &&
+		    os_memcmp(frag->ra, hdr->addr1, ETH_ALEN) == 0 &&
+		    os_memcmp(frag->ta, hdr->addr2, ETH_ALEN) == 0) {
+			/* Add the next fragment */
+			if (wpabuf_resize(&frag->buf, plain_len) == 0) {
+				wpabuf_put_data(frag->buf, plain, plain_len);
+				frag->fn = fn;
+				if (!(fc & WLAN_FC_MOREFRAG)) {
+					full_payload = wpabuf_head(frag->buf);
+					full_payload_len =
+						wpabuf_len(frag->buf);
+					wpa_hexdump(MSG_MSGDUMP,
+						    "TKIP reassembled full payload",
+						    full_payload,
+						    full_payload_len);
+				}
+			}
+		}
+	}
+
+	if ((fc & WLAN_FC_MOREFRAG) || (fn > 0 && full_payload == plain)) {
+		/* Return the decrypted fragment and do not check the
+		 * Michael MIC value since no reassembled frame is available. */
+		*decrypted_len = plain_len;
+		if (mic_res) {
+			*mic_res = MICHAEL_MIC_NOT_VERIFIED;
+			return plain;
+		}
+	}
+
+	if (full_payload_len < 8) {
+		wpa_printf(MSG_INFO, "TKIP: Not enough room for Michael MIC "
+			   "in a frame from " MACSTR, MAC2STR(hdr->addr2));
+		os_free(plain);
+		return NULL;
+	}
+
+	michael_mic_hdr(hdr, michael_hdr);
+	mic_key = tk + ((fc & WLAN_FC_FROMDS) ? 16 : 24);
+	michael_mic(mic_key, michael_hdr, full_payload, full_payload_len - 8,
+		    mic);
+	if (os_memcmp(mic, full_payload + full_payload_len - 8, 8) != 0) {
+		wpa_printf(MSG_INFO, "TKIP: Michael MIC mismatch in a frame "
+			   "from " MACSTR, MAC2STR(hdr->addr2));
+		wpa_hexdump(MSG_DEBUG, "TKIP: Calculated MIC", mic, 8);
+		wpa_hexdump(MSG_DEBUG, "TKIP: Received MIC",
+			    full_payload + full_payload_len - 8, 8);
+		if (mic_res) {
+			*decrypted_len = plain_len - 8;
+			*mic_res = MICHAEL_MIC_INCORRECT;
+			return plain;
+		}
+		os_free(plain);
+		return NULL;
+	} else if (mic_res) {
+		*mic_res = MICHAEL_MIC_OK;
+	}
+
+	*decrypted_len = plain_len - 8;
+	return plain;
+}
+
+
+void tkip_get_pn(u8 *pn, const u8 *data)
+{
+	pn[0] = data[7]; /* PN5 */
+	pn[1] = data[6]; /* PN4 */
+	pn[2] = data[5]; /* PN3 */
+	pn[3] = data[4]; /* PN2 */
+	pn[4] = data[0]; /* PN1 */
+	pn[5] = data[2]; /* PN0 */
+}
+
+
+u8 * tkip_encrypt(const u8 *tk, u8 *frame, size_t len, size_t hdrlen, u8 *qos,
+		  u8 *pn, int keyid, size_t *encrypted_len)
+{
+	u8 michael_hdr[16];
+	u8 mic[8];
+	struct ieee80211_hdr *hdr;
+	u16 fc;
+	const u8 *mic_key;
+	u8 *crypt, *pos;
+	u16 iv16;
+	u32 iv32;
+	u16 ttak[5];
+	u8 rc4key[16];
+
+	if (len < sizeof(*hdr) || len < hdrlen)
+		return NULL;
+	hdr = (struct ieee80211_hdr *) frame;
+	fc = le_to_host16(hdr->frame_control);
+
+	michael_mic_hdr(hdr, michael_hdr);
+	mic_key = tk + ((fc & WLAN_FC_FROMDS) ? 16 : 24);
+	michael_mic(mic_key, michael_hdr, frame + hdrlen, len - hdrlen, mic);
+	wpa_hexdump(MSG_EXCESSIVE, "TKIP: MIC", mic, sizeof(mic));
+
+	iv32 = WPA_GET_BE32(pn);
+	iv16 = WPA_GET_BE16(pn + 4);
+	tkip_mixing_phase1(ttak, tk, hdr->addr2, iv32);
+	wpa_hexdump(MSG_EXCESSIVE, "TKIP TTAK", (u8 *) ttak, sizeof(ttak));
+	tkip_mixing_phase2(rc4key, tk, ttak, iv16);
+	wpa_hexdump(MSG_EXCESSIVE, "TKIP RC4KEY", rc4key, sizeof(rc4key));
+
+	crypt = os_malloc(len + 8 + sizeof(mic) + 4);
+	if (crypt == NULL)
+		return NULL;
+	os_memcpy(crypt, frame, hdrlen);
+	pos = crypt + hdrlen;
+	os_memcpy(pos, rc4key, 3);
+	pos += 3;
+	*pos++ = keyid << 6 | BIT(5);
+	*pos++ = pn[3];
+	*pos++ = pn[2];
+	*pos++ = pn[1];
+	*pos++ = pn[0];
+
+	os_memcpy(pos, frame + hdrlen, len - hdrlen);
+	os_memcpy(pos + len - hdrlen, mic, sizeof(mic));
+	WPA_PUT_LE32(pos + len - hdrlen + sizeof(mic),
+		     ieee80211_crc32(pos, len - hdrlen + sizeof(mic)));
+	wep_crypt(rc4key, pos, len - hdrlen + sizeof(mic) + 4);
+
+	*encrypted_len = len + 8 + sizeof(mic) + 4;
+	return crypt;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/wep.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/wep.c
new file mode 100644
index 0000000..6f8f13a
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/wep.c
@@ -0,0 +1,104 @@
+/*
+ * Wired Equivalent Privacy (WEP)
+ * Copyright (c) 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 "utils/crc32.h"
+#include "common/ieee802_11_defs.h"
+#include "wlantest.h"
+
+
+void wep_crypt(u8 *key, u8 *buf, size_t plen)
+{
+	u32 i, j, k;
+	u8 S[256];
+#define S_SWAP(a,b) do { u8 t = S[a]; S[a] = S[b]; S[b] = t; } while(0)
+	u8 *pos;
+
+	/* Setup RC4 state */
+	for (i = 0; i < 256; i++)
+		S[i] = i;
+	j = 0;
+	for (i = 0; i < 256; i++) {
+		j = (j + S[i] + key[i & 0x0f]) & 0xff;
+		S_SWAP(i, j);
+	}
+
+	/* Apply RC4 to data */
+	pos = buf;
+	i = j = 0;
+	for (k = 0; k < plen; k++) {
+		i = (i + 1) & 0xff;
+		j = (j + S[i]) & 0xff;
+		S_SWAP(i, j);
+		*pos ^= S[(S[i] + S[j]) & 0xff];
+		pos++;
+	}
+}
+
+
+static int try_wep(const u8 *key, size_t key_len, const u8 *data,
+		   size_t data_len, u8 *plain)
+{
+	u32 icv, rx_icv;
+	u8 k[16];
+	int i, j;
+
+	for (i = 0, j = 0; i < sizeof(k); i++) {
+		k[i] = key[j];
+		j++;
+		if (j >= key_len)
+			j = 0;
+	}
+
+	os_memcpy(plain, data, data_len);
+	wep_crypt(k, plain, data_len);
+	icv = ieee80211_crc32(plain, data_len - 4);
+	rx_icv = WPA_GET_LE32(plain + data_len - 4);
+	if (icv != rx_icv)
+		return -1;
+
+	return 0;
+}
+
+
+u8 * wep_decrypt(struct wlantest *wt, const struct ieee80211_hdr *hdr,
+		 const u8 *data, size_t data_len, size_t *decrypted_len)
+{
+	u8 *plain;
+	struct wlantest_wep *w;
+	int found = 0;
+	u8 key[16];
+
+	if (dl_list_empty(&wt->wep))
+		return NULL;
+
+	if (data_len < 4 + 4)
+		return NULL;
+	plain = os_malloc(data_len - 4);
+	if (plain == NULL)
+		return NULL;
+
+	dl_list_for_each(w, &wt->wep, struct wlantest_wep, list) {
+		os_memcpy(key, data, 3);
+		os_memcpy(key + 3, w->key, w->key_len);
+		if (try_wep(key, 3 + w->key_len, data + 4, data_len - 4, plain)
+		    == 0) {
+			found = 1;
+			break;
+		}
+	}
+	if (!found) {
+		os_free(plain);
+		return NULL;
+	}
+
+	*decrypted_len = data_len - 4 - 4;
+	return plain;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/wired.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/wired.c
new file mode 100644
index 0000000..67ae8a9
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/wired.c
@@ -0,0 +1,295 @@
+/*
+ * Received frame processing for wired interface
+ * Copyright (c) 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 <net/ethernet.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+
+#include "utils/common.h"
+#include "radius/radius.h"
+#include "wlantest.h"
+
+
+static struct wlantest_radius * radius_get(struct wlantest *wt, u32 srv,
+					   u32 cli)
+{
+	struct wlantest_radius *r;
+
+	dl_list_for_each(r, &wt->radius, struct wlantest_radius, list) {
+		if (r->srv == srv && r->cli == cli)
+			return r;
+	}
+
+	r = os_zalloc(sizeof(*r));
+	if (r == NULL)
+		return NULL;
+
+	r->srv = srv;
+	r->cli = cli;
+	dl_list_add(&wt->radius, &r->list);
+
+	return r;
+}
+
+
+static const char * radius_code_string(u8 code)
+{
+	switch (code) {
+	case RADIUS_CODE_ACCESS_REQUEST:
+		return "Access-Request";
+	case RADIUS_CODE_ACCESS_ACCEPT:
+		return "Access-Accept";
+	case RADIUS_CODE_ACCESS_REJECT:
+		return "Access-Reject";
+	case RADIUS_CODE_ACCOUNTING_REQUEST:
+		return "Accounting-Request";
+	case RADIUS_CODE_ACCOUNTING_RESPONSE:
+		return "Accounting-Response";
+	case RADIUS_CODE_ACCESS_CHALLENGE:
+		return "Access-Challenge";
+	case RADIUS_CODE_STATUS_SERVER:
+		return "Status-Server";
+	case RADIUS_CODE_STATUS_CLIENT:
+		return "Status-Client";
+	case RADIUS_CODE_RESERVED:
+		return "Reserved";
+	default:
+		return "?Unknown?";
+	}
+}
+
+
+static void process_radius_access_request(struct wlantest *wt, u32 dst,
+					  u32 src, const u8 *data, size_t len)
+{
+	struct radius_msg *msg;
+	struct wlantest_radius *r;
+
+	msg = radius_msg_parse(data, len);
+	if (msg == NULL) {
+		wpa_printf(MSG_DEBUG, "Failed to parse RADIUS Access-Request");
+		return;
+	}
+
+	r = radius_get(wt, dst, src);
+	if (r) {
+		radius_msg_free(r->last_req);
+		r->last_req = msg;
+		return;
+	}
+	radius_msg_free(msg);
+}
+
+
+static void wlantest_add_pmk(struct wlantest *wt, const u8 *pmk, size_t pmk_len)
+{
+	struct wlantest_pmk *p;
+
+	p = os_zalloc(sizeof(*p));
+	if (p == NULL)
+		return;
+	os_memcpy(p->pmk, pmk, pmk_len);
+	p->pmk_len = pmk_len;
+	dl_list_add(&wt->pmk, &p->list);
+	wpa_hexdump(MSG_INFO, "Add PMK", pmk, pmk_len);
+}
+
+
+static void process_radius_access_accept(struct wlantest *wt, u32 dst, u32 src,
+					 const u8 *data, size_t len)
+{
+	struct radius_msg *msg;
+	struct wlantest_radius *r;
+	struct radius_ms_mppe_keys *keys;
+	struct wlantest_radius_secret *s;
+
+	r = radius_get(wt, src, dst);
+	if (r == NULL || r->last_req == NULL) {
+		wpa_printf(MSG_DEBUG, "No RADIUS Access-Challenge found for "
+			   "decrypting Access-Accept keys");
+		return;
+	}
+
+	msg = radius_msg_parse(data, len);
+	if (msg == NULL) {
+		wpa_printf(MSG_DEBUG, "Failed to parse RADIUS Access-Accept");
+		return;
+	}
+
+	dl_list_for_each(s, &wt->secret, struct wlantest_radius_secret, list) {
+		int found = 0;
+		keys = radius_msg_get_ms_keys(msg, r->last_req,
+					      (u8 *) s->secret,
+					      os_strlen(s->secret));
+		if (keys && keys->send && keys->recv) {
+			u8 pmk[PMK_LEN_MAX];
+			size_t pmk_len, len2;
+
+			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);
+			pmk_len = keys->recv_len;
+			if (pmk_len > PMK_LEN_MAX)
+				pmk_len = PMK_LEN_MAX;
+			os_memcpy(pmk, keys->recv, pmk_len);
+			if (pmk_len < PMK_LEN_MAX) {
+				len2 = keys->send_len;
+				if (pmk_len + len2 > PMK_LEN_MAX)
+					len2 = PMK_LEN_MAX - pmk_len;
+				os_memcpy(pmk + pmk_len, keys->send, len2);
+				pmk_len += len2;
+			}
+			wlantest_add_pmk(wt, pmk, pmk_len);
+			found = 1;
+		}
+
+		if (keys) {
+			os_free(keys->send);
+			os_free(keys->recv);
+			os_free(keys);
+		}
+
+		if (found)
+			break;
+	}
+
+	radius_msg_free(msg);
+}
+
+
+static void process_radius(struct wlantest *wt, u32 dst, u16 dport, u32 src,
+			   u16 sport, const u8 *data, size_t len)
+{
+	struct in_addr addr;
+	char buf[20];
+	const struct radius_hdr *hdr;
+	u16 rlen;
+
+	if (len < sizeof(*hdr))
+		return;
+	hdr = (const struct radius_hdr *) data;
+	rlen = be_to_host16(hdr->length);
+	if (len < rlen)
+		return;
+	if (len > rlen)
+		len = rlen;
+
+	addr.s_addr = dst;
+	snprintf(buf, sizeof(buf), "%s", inet_ntoa(addr));
+
+	addr.s_addr = src;
+	wpa_printf(MSG_DEBUG, "RADIUS %s:%u -> %s:%u id=%u %s",
+		   inet_ntoa(addr), sport, buf, dport, hdr->identifier,
+		   radius_code_string(hdr->code));
+
+	switch (hdr->code) {
+	case RADIUS_CODE_ACCESS_REQUEST:
+		process_radius_access_request(wt, dst, src, data, len);
+		break;
+	case RADIUS_CODE_ACCESS_ACCEPT:
+		process_radius_access_accept(wt, dst, src, data, len);
+		break;
+	}
+}
+
+
+static void process_udp(struct wlantest *wt, u32 dst, u32 src,
+			const u8 *data, size_t len)
+{
+	const struct udphdr *udp;
+	u16 sport, dport, ulen;
+	const u8 *payload;
+	size_t plen;
+
+	if (len < sizeof(*udp))
+		return;
+	udp = (const struct udphdr *) data;
+	/* TODO: check UDP checksum */
+	sport = be_to_host16(udp->uh_sport);
+	dport = be_to_host16(udp->uh_dport);
+	ulen = be_to_host16(udp->uh_ulen);
+
+	if (ulen > len)
+		return;
+	if (len < ulen)
+		len = ulen;
+
+	payload = (const u8 *) (udp + 1);
+	plen = len - sizeof(*udp);
+
+	if (sport == 1812 || dport == 1812)
+		process_radius(wt, dst, dport, src, sport, payload, plen);
+}
+
+
+static void process_ipv4(struct wlantest *wt, const u8 *data, size_t len)
+{
+	const struct ip *ip;
+	const u8 *payload;
+	size_t plen;
+	uint16_t frag_off, ip_len;
+
+	if (len < sizeof(*ip))
+		return;
+
+	ip = (const struct ip *) data;
+	if (ip->ip_v != 4)
+		return;
+	if (ip->ip_hl < 5)
+		return;
+
+	/* TODO: check header checksum in ip->check */
+
+	frag_off = be_to_host16(ip->ip_off);
+	if (frag_off & 0x1fff) {
+		wpa_printf(MSG_EXCESSIVE, "IP fragment reassembly not yet "
+			   "supported");
+		return;
+	}
+
+	ip_len = be_to_host16(ip->ip_len);
+	if (ip_len > len)
+		return;
+	if (ip_len < len)
+		len = ip_len;
+
+	payload = data + 4 * ip->ip_hl;
+	plen = len - 4 * ip->ip_hl;
+	if (payload + plen > data + len)
+		return;
+
+	switch (ip->ip_p) {
+	case IPPROTO_UDP:
+		process_udp(wt, ip->ip_dst.s_addr, ip->ip_src.s_addr,
+			    payload, plen);
+		break;
+	}
+}
+
+
+void wlantest_process_wired(struct wlantest *wt, const u8 *data, size_t len)
+{
+	const struct ether_header *eth;
+	u16 ethertype;
+
+	wpa_hexdump(MSG_EXCESSIVE, "Process wired frame", data, len);
+
+	if (len < sizeof(*eth))
+		return;
+
+	eth = (const struct ether_header *) data;
+	ethertype = be_to_host16(eth->ether_type);
+
+	switch (ethertype) {
+	case ETHERTYPE_IP:
+		process_ipv4(wt, data + sizeof(*eth), len - sizeof(*eth));
+		break;
+	}
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/wlantest.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/wlantest.c
new file mode 100644
index 0000000..fd6dc20
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/wlantest.c
@@ -0,0 +1,520 @@
+/*
+ * wlantest - IEEE 802.11 protocol monitoring and testing tool
+ * Copyright (c) 2010-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 "utils/eloop.h"
+#include "wlantest.h"
+
+
+static void wlantest_terminate(int sig, void *signal_ctx)
+{
+	eloop_terminate();
+}
+
+
+static void usage(void)
+{
+	printf("wlantest [-cddehqqFNt] [-i<ifname>] [-r<pcap file>] "
+	       "[-p<passphrase>]\n"
+	       "         [-I<wired ifname>] [-R<wired pcap file>] "
+	       "[-P<RADIUS shared secret>]\n"
+	       "         [-n<write pcapng file>]\n"
+	       "         [-w<write pcap file>] [-f<MSK/PMK file>]\n"
+	       "         [-L<log file>] [-T<PTK file>] [-W<WEP key>]\n");
+}
+
+
+static void passphrase_deinit(struct wlantest_passphrase *p)
+{
+	dl_list_del(&p->list);
+	os_free(p);
+}
+
+
+static void secret_deinit(struct wlantest_radius_secret *r)
+{
+	dl_list_del(&r->list);
+	os_free(r);
+}
+
+
+static void wlantest_init(struct wlantest *wt)
+{
+	int i;
+	os_memset(wt, 0, sizeof(*wt));
+	wt->monitor_sock = -1;
+	wt->ctrl_sock = -1;
+	for (i = 0; i < MAX_CTRL_CONNECTIONS; i++)
+		wt->ctrl_socks[i] = -1;
+	dl_list_init(&wt->passphrase);
+	dl_list_init(&wt->bss);
+	dl_list_init(&wt->secret);
+	dl_list_init(&wt->radius);
+	dl_list_init(&wt->pmk);
+	dl_list_init(&wt->ptk);
+	dl_list_init(&wt->wep);
+}
+
+
+void radius_deinit(struct wlantest_radius *r)
+{
+	dl_list_del(&r->list);
+	os_free(r);
+}
+
+
+static void ptk_deinit(struct wlantest_ptk *ptk)
+{
+	dl_list_del(&ptk->list);
+	os_free(ptk);
+}
+
+
+static void wep_deinit(struct wlantest_wep *wep)
+{
+	dl_list_del(&wep->list);
+	os_free(wep);
+}
+
+
+static void wlantest_deinit(struct wlantest *wt)
+{
+	struct wlantest_passphrase *p, *pn;
+	struct wlantest_radius_secret *s, *sn;
+	struct wlantest_radius *r, *rn;
+	struct wlantest_pmk *pmk, *np;
+	struct wlantest_ptk *ptk, *npt;
+	struct wlantest_wep *wep, *nw;
+
+	if (wt->ctrl_sock >= 0)
+		ctrl_deinit(wt);
+	if (wt->monitor_sock >= 0)
+		monitor_deinit(wt);
+	bss_flush(wt);
+	dl_list_for_each_safe(p, pn, &wt->passphrase,
+			      struct wlantest_passphrase, list)
+		passphrase_deinit(p);
+	dl_list_for_each_safe(s, sn, &wt->secret,
+			      struct wlantest_radius_secret, list)
+		secret_deinit(s);
+	dl_list_for_each_safe(r, rn, &wt->radius, struct wlantest_radius, list)
+		radius_deinit(r);
+	dl_list_for_each_safe(pmk, np, &wt->pmk, struct wlantest_pmk, list)
+		pmk_deinit(pmk);
+	dl_list_for_each_safe(ptk, npt, &wt->ptk, struct wlantest_ptk, list)
+		ptk_deinit(ptk);
+	dl_list_for_each_safe(wep, nw, &wt->wep, struct wlantest_wep, list)
+		wep_deinit(wep);
+	write_pcap_deinit(wt);
+	write_pcapng_deinit(wt);
+	clear_notes(wt);
+	os_free(wt->decrypted);
+	wt->decrypted = NULL;
+	wpabuf_free(wt->tkip_frag.buf);
+	wt->tkip_frag.buf = NULL;
+}
+
+
+static void add_passphrase(struct wlantest *wt, const char *passphrase)
+{
+	struct wlantest_passphrase *p;
+	size_t len = os_strlen(passphrase);
+
+	if (len < 8 || len > 63)
+		return;
+	p = os_zalloc(sizeof(*p));
+	if (p == NULL)
+		return;
+	os_memcpy(p->passphrase, passphrase, len);
+	dl_list_add(&wt->passphrase, &p->list);
+}
+
+
+static void add_secret(struct wlantest *wt, const char *secret)
+{
+	struct wlantest_radius_secret *s;
+	size_t len = os_strlen(secret);
+
+	if (len >= MAX_RADIUS_SECRET_LEN)
+		return;
+	s = os_zalloc(sizeof(*s));
+	if (s == NULL)
+		return;
+	os_memcpy(s->secret, secret, len);
+	dl_list_add(&wt->secret, &s->list);
+}
+
+
+static int add_pmk_file(struct wlantest *wt, const char *pmk_file)
+{
+	FILE *f;
+	u8 pmk[PMK_LEN_MAX];
+	size_t pmk_len;
+	char buf[300], *pos;
+	struct wlantest_pmk *p;
+
+	f = fopen(pmk_file, "r");
+	if (f == NULL) {
+		wpa_printf(MSG_ERROR, "Could not open '%s'", pmk_file);
+		return -1;
+	}
+
+	while (fgets(buf, sizeof(buf), f)) {
+		pos = buf;
+		while (*pos && *pos != '\r' && *pos != '\n')
+			pos++;
+		*pos = '\0';
+		if (pos - buf < 2 * 32)
+			continue;
+		pmk_len = (pos - buf) / 2;
+		if (pmk_len > PMK_LEN_MAX)
+			pmk_len = PMK_LEN_MAX;
+		if (hexstr2bin(buf, pmk, pmk_len) < 0)
+			continue;
+		p = os_zalloc(sizeof(*p));
+		if (p == NULL)
+			break;
+		os_memcpy(p->pmk, pmk, pmk_len);
+		p->pmk_len = pmk_len;
+		dl_list_add(&wt->pmk, &p->list);
+		wpa_hexdump(MSG_DEBUG, "Added PMK from file", pmk, pmk_len);
+
+		/* For FT, the send half of MSK is used */
+		if (hexstr2bin(&buf[2 * PMK_LEN], pmk, PMK_LEN) < 0)
+			continue;
+		p = os_zalloc(sizeof(*p));
+		if (p == NULL)
+			break;
+		os_memcpy(p->pmk, pmk, PMK_LEN);
+		p->pmk_len = PMK_LEN;
+		dl_list_add(&wt->pmk, &p->list);
+		wpa_hexdump(MSG_DEBUG, "Added PMK from file (2nd half of MSK)",
+			    pmk, PMK_LEN);
+	}
+
+	fclose(f);
+	return 0;
+}
+
+
+static int add_ptk_file(struct wlantest *wt, const char *ptk_file)
+{
+	FILE *f;
+	u8 ptk[64];
+	size_t ptk_len;
+	char buf[300], *pos;
+	struct wlantest_ptk *p;
+
+	f = fopen(ptk_file, "r");
+	if (f == NULL) {
+		wpa_printf(MSG_ERROR, "Could not open '%s'", ptk_file);
+		return -1;
+	}
+
+	while (fgets(buf, sizeof(buf), f)) {
+		pos = buf;
+		while (*pos && *pos != '\r' && *pos != '\n')
+			pos++;
+		*pos = '\0';
+		ptk_len = pos - buf;
+		if (ptk_len & 1)
+			continue;
+		ptk_len /= 2;
+		if (ptk_len != 16 && ptk_len != 32 &&
+		    ptk_len != 48 && ptk_len != 64)
+			continue;
+		if (hexstr2bin(buf, ptk, ptk_len) < 0)
+			continue;
+		p = os_zalloc(sizeof(*p));
+		if (p == NULL)
+			break;
+		if (ptk_len < 48) {
+			os_memcpy(p->ptk.tk, ptk, ptk_len);
+			p->ptk.tk_len = ptk_len;
+			p->ptk_len = 32 + ptk_len;
+		} else {
+			os_memcpy(p->ptk.kck, ptk, 16);
+			p->ptk.kck_len = 16;
+			os_memcpy(p->ptk.kek, ptk + 16, 16);
+			p->ptk.kek_len = 16;
+			os_memcpy(p->ptk.tk, ptk + 32, ptk_len - 32);
+			p->ptk.tk_len = ptk_len - 32;
+			p->ptk_len = ptk_len;
+		}
+		dl_list_add(&wt->ptk, &p->list);
+		wpa_hexdump(MSG_DEBUG, "Added PTK from file", ptk, ptk_len);
+	}
+
+	fclose(f);
+	return 0;
+}
+
+
+int add_wep(struct wlantest *wt, const char *key)
+{
+	struct wlantest_wep *w;
+	size_t len = os_strlen(key);
+
+	if (len != 2 * 5 && len != 2 * 13) {
+		wpa_printf(MSG_INFO, "Invalid WEP key '%s'", key);
+		return -1;
+	}
+	w = os_zalloc(sizeof(*w));
+	if (w == NULL)
+		return -1;
+	if (hexstr2bin(key, w->key, len / 2) < 0) {
+		os_free(w);
+		wpa_printf(MSG_INFO, "Invalid WEP key '%s'", key);
+		return -1;
+	}
+	w->key_len = len / 2;
+	dl_list_add(&wt->wep, &w->list);
+	return 0;
+}
+
+
+void add_note(struct wlantest *wt, int level, const char *fmt, ...)
+{
+	va_list ap;
+	size_t len = 1000;
+	int wlen;
+
+	if (wt->num_notes == MAX_NOTES)
+		return;
+
+	wt->notes[wt->num_notes] = os_malloc(len);
+	if (wt->notes[wt->num_notes] == NULL)
+		return;
+	va_start(ap, fmt);
+	wlen = vsnprintf(wt->notes[wt->num_notes], len, fmt, ap);
+	va_end(ap);
+	if (wlen < 0) {
+		os_free(wt->notes[wt->num_notes]);
+		wt->notes[wt->num_notes] = NULL;
+		return;
+	}
+	if (wlen >= len)
+		wt->notes[wt->num_notes][len - 1] = '\0';
+	wpa_printf(level, "%s", wt->notes[wt->num_notes]);
+	wt->num_notes++;
+}
+
+
+void clear_notes(struct wlantest *wt)
+{
+	size_t i;
+
+	for (i = 0; i < wt->num_notes; i++) {
+		os_free(wt->notes[i]);
+		wt->notes[i] = NULL;
+	}
+
+	wt->num_notes = 0;
+}
+
+
+size_t notes_len(struct wlantest *wt, size_t hdrlen)
+{
+	size_t i;
+	size_t len = wt->num_notes * hdrlen;
+
+	for (i = 0; i < wt->num_notes; i++)
+		len += os_strlen(wt->notes[i]);
+
+	return len;
+}
+
+
+void write_decrypted_note(struct wlantest *wt, const u8 *decrypted,
+			  const u8 *tk, size_t tk_len, int keyid)
+{
+	char tk_hex[65];
+
+	if (!decrypted)
+		return;
+
+	wpa_snprintf_hex(tk_hex, sizeof(tk_hex), tk, tk_len);
+	add_note(wt, MSG_EXCESSIVE, "TK[%d] %s", keyid, tk_hex);
+}
+
+
+int wlantest_relog(struct wlantest *wt)
+{
+	int ret = 0;
+
+	wpa_printf(MSG_INFO, "Re-open log/capture files");
+	if (wpa_debug_reopen_file())
+		ret = -1;
+
+	if (wt->write_file) {
+		write_pcap_deinit(wt);
+		if (write_pcap_init(wt, wt->write_file) < 0)
+			ret = -1;
+	}
+
+	if (wt->pcapng_file) {
+		write_pcapng_deinit(wt);
+		if (write_pcapng_init(wt, wt->pcapng_file) < 0)
+			ret = -1;
+	}
+
+	return ret;
+}
+
+
+int main(int argc, char *argv[])
+{
+	int c, ret = 0;
+	const char *read_file = NULL;
+	const char *read_wired_file = NULL;
+	const char *ifname = NULL;
+	const char *ifname_wired = NULL;
+	const char *logfile = NULL;
+	struct wlantest wt;
+	int ctrl_iface = 0;
+	bool eloop_init_done = false;
+
+	wpa_debug_level = MSG_INFO;
+	wpa_debug_show_keys = 1;
+
+	if (os_program_init())
+		return -1;
+
+	wlantest_init(&wt);
+
+	for (;;) {
+		c = getopt(argc, argv, "cdef:Fhi:I:L:n:Np:P:qr:R:tT:w:W:");
+		if (c < 0)
+			break;
+		switch (c) {
+		case 'c':
+			ctrl_iface = 1;
+			break;
+		case 'd':
+			if (wpa_debug_level > 0)
+				wpa_debug_level--;
+			break;
+		case 'e':
+			wt.ethernet = 1;
+			break;
+		case 'f':
+			if (add_pmk_file(&wt, optarg) < 0) {
+				ret = -1;
+				goto deinit;
+			}
+			break;
+		case 'F':
+			wt.assume_fcs = 1;
+			break;
+		case 'h':
+			usage();
+			ret = 0;
+			goto deinit;
+		case 'i':
+			ifname = optarg;
+			break;
+		case 'I':
+			ifname_wired = optarg;
+			break;
+		case 'L':
+			logfile = optarg;
+			break;
+		case 'n':
+			wt.pcapng_file = optarg;
+			break;
+		case 'N':
+			wt.pcap_no_buffer = 1;
+			break;
+		case 'p':
+			add_passphrase(&wt, optarg);
+			break;
+		case 'P':
+			add_secret(&wt, optarg);
+			break;
+		case 'q':
+			wpa_debug_level++;
+			break;
+		case 'r':
+			read_file = optarg;
+			break;
+		case 'R':
+			read_wired_file = optarg;
+			break;
+		case 't':
+			wpa_debug_timestamp = 1;
+			break;
+		case 'T':
+			if (add_ptk_file(&wt, optarg) < 0) {
+				ret = -1;
+				goto deinit;
+			}
+			break;
+		case 'w':
+			wt.write_file = optarg;
+			break;
+		case 'W':
+			if (add_wep(&wt, optarg) < 0) {
+				ret = -1;
+				goto deinit;
+			}
+			break;
+		default:
+			usage();
+			ret = -1;
+			goto deinit;
+		}
+	}
+
+	if (ifname == NULL && ifname_wired == NULL &&
+	    read_file == NULL && read_wired_file == NULL) {
+		usage();
+		ret = 0;
+		goto deinit;
+	}
+
+	if (eloop_init()) {
+		ret = -1;
+		goto deinit;
+	}
+	eloop_init_done = true;
+
+	if (logfile)
+		wpa_debug_open_file(logfile);
+
+	if ((wt.write_file && write_pcap_init(&wt, wt.write_file) < 0) ||
+	    (wt.pcapng_file && write_pcapng_init(&wt, wt.pcapng_file) < 0) ||
+	    (read_wired_file &&
+	     read_wired_cap_file(&wt, read_wired_file) < 0) ||
+	    (read_file && read_cap_file(&wt, read_file) < 0) ||
+	    (ifname && monitor_init(&wt, ifname) < 0) ||
+	    (ifname_wired && monitor_init_wired(&wt, ifname_wired) < 0) ||
+	    (ctrl_iface && ctrl_init(&wt) < 0)) {
+		ret = -1;
+		goto deinit;
+	}
+
+	eloop_register_signal_terminate(wlantest_terminate, &wt);
+
+	eloop_run();
+
+	wpa_printf(MSG_INFO, "Processed: rx_mgmt=%u rx_ctrl=%u rx_data=%u "
+		   "fcs_error=%u",
+		   wt.rx_mgmt, wt.rx_ctrl, wt.rx_data, wt.fcs_error);
+
+deinit:
+	wlantest_deinit(&wt);
+
+	wpa_debug_close_file();
+	if (eloop_init_done)
+		eloop_destroy();
+	os_program_deinit();
+
+	return ret;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/wlantest.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/wlantest.h
new file mode 100644
index 0000000..6a85cc1
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/wlantest.h
@@ -0,0 +1,370 @@
+/*
+ * wlantest - IEEE 802.11 protocol monitoring and testing tool
+ * Copyright (c) 2010-2020, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WLANTEST_H
+#define WLANTEST_H
+
+#include "utils/list.h"
+#include "common/wpa_common.h"
+#include "wlantest_ctrl.h"
+
+struct ieee802_11_elems;
+struct radius_msg;
+struct ieee80211_hdr;
+struct wlantest_bss;
+
+#define MAX_RADIUS_SECRET_LEN 128
+
+struct wlantest_radius_secret {
+	struct dl_list list;
+	char secret[MAX_RADIUS_SECRET_LEN];
+};
+
+struct wlantest_passphrase {
+	struct dl_list list;
+	char passphrase[64];
+	u8 ssid[32];
+	size_t ssid_len;
+	u8 bssid[ETH_ALEN];
+};
+
+struct wlantest_pmk {
+	struct dl_list list;
+	u8 pmk[PMK_LEN_MAX];
+	size_t pmk_len;
+};
+
+struct wlantest_ptk {
+	struct dl_list list;
+	struct wpa_ptk ptk;
+	size_t ptk_len;
+};
+
+struct wlantest_wep {
+	struct dl_list list;
+	size_t key_len;
+	u8 key[13];
+};
+
+struct wlantest_sta {
+	struct dl_list list;
+	struct wlantest_bss *bss;
+	u8 addr[ETH_ALEN];
+	u8 mld_mac_addr[ETH_ALEN];
+	u8 link_addr[MAX_NUM_MLO_LINKS][ETH_ALEN];
+	enum {
+		STATE1 /* not authenticated */,
+		STATE2 /* authenticated */,
+		STATE3 /* associated */
+	} state;
+	u16 auth_alg;
+	bool ft_over_ds;
+	u16 aid;
+	u8 rsnie[257]; /* WPA/RSN IE */
+	u8 rsnxe[254]; /* RSNXE data */
+	size_t rsnxe_len;
+	u8 osenie[257]; /* OSEN IE */
+	int proto;
+	int pairwise_cipher;
+	int group_cipher;
+	int key_mgmt;
+	int rsn_capab;
+	/* ANonce from the previous EAPOL-Key msg 1/4 or 3/4 */
+	u8 anonce[WPA_NONCE_LEN];
+	/* SNonce from the previous EAPOL-Key msg 2/4 */
+	u8 snonce[WPA_NONCE_LEN];
+	u8 pmk_r0[PMK_LEN_MAX];
+	size_t pmk_r0_len;
+	u8 pmk_r0_name[WPA_PMK_NAME_LEN];
+	u8 pmk_r1[PMK_LEN_MAX];
+	size_t pmk_r1_len;
+	u8 pmk_r1_name[WPA_PMK_NAME_LEN];
+	struct wpa_ptk ptk; /* Derived PTK */
+	int ptk_set;
+	struct wpa_ptk tptk; /* Derived PTK during rekeying */
+	int tptk_set;
+	u8 rsc_tods[16 + 1][6];
+	u8 rsc_fromds[16 + 1][6];
+	u8 ap_sa_query_tr[2];
+	u8 sta_sa_query_tr[2];
+	u32 counters[NUM_WLANTEST_STA_COUNTER];
+	int assocreq_seen;
+	u16 assocreq_capab_info;
+	u16 assocreq_listen_int;
+	u8 *assocreq_ies;
+	size_t assocreq_ies_len;
+
+	/* Last ICMP Echo request information */
+	u32 icmp_echo_req_src;
+	u32 icmp_echo_req_dst;
+	u16 icmp_echo_req_id;
+	u16 icmp_echo_req_seq;
+
+	le16 seq_ctrl_to_sta[17];
+	le16 seq_ctrl_to_ap[17];
+	int allow_duplicate;
+
+	int pwrmgt;
+	int pspoll;
+
+	u8 gtk[32];
+	size_t gtk_len;
+	int gtk_idx;
+
+	u32 tx_tid[16 + 1];
+	u32 rx_tid[16 + 1];
+
+	u16 sae_group;
+};
+
+struct wlantest_tdls {
+	struct dl_list list;
+	struct wlantest_sta *init;
+	struct wlantest_sta *resp;
+	struct tpk {
+		u8 kck[16];
+		u8 tk[16];
+	} tpk;
+	int link_up;
+	u8 dialog_token;
+	u8 rsc_init[16 + 1][6];
+	u8 rsc_resp[16 + 1][6];
+	u32 counters[NUM_WLANTEST_TDLS_COUNTER];
+	u8 inonce[32];
+	u8 rnonce[32];
+};
+
+struct wlantest_bss {
+	struct dl_list list;
+	u8 bssid[ETH_ALEN];
+	u8 mld_mac_addr[ETH_ALEN];
+	u16 capab_info;
+	u16 prev_capab_info;
+	u8 ssid[32];
+	size_t ssid_len;
+	int beacon_seen;
+	int proberesp_seen;
+	int ies_set;
+	int parse_error_reported;
+	u8 wpaie[257];
+	u8 rsnie[257];
+	u8 rsnxe[254]; /* RSNXE data */
+	size_t rsnxe_len;
+	u8 osenie[257];
+	int proto;
+	int pairwise_cipher;
+	int group_cipher;
+	int mgmt_group_cipher;
+	int key_mgmt;
+	int rsn_capab;
+	struct dl_list sta; /* struct wlantest_sta */
+	struct dl_list pmk; /* struct wlantest_pmk */
+	u8 gtk[4][32];
+	size_t gtk_len[4];
+	int gtk_idx;
+	u8 rsc[4][6];
+	u8 igtk[8][32];
+	size_t igtk_len[8];
+	int igtk_idx;
+	u8 ipn[8][6];
+	int bigtk_idx;
+	u32 counters[NUM_WLANTEST_BSS_COUNTER];
+	struct dl_list tdls; /* struct wlantest_tdls */
+	u8 mdid[MOBILITY_DOMAIN_ID_LEN];
+	u8 r0kh_id[FT_R0KH_ID_MAX_LEN];
+	size_t r0kh_id_len;
+	u8 r1kh_id[FT_R1KH_ID_LEN];
+	bool mesh;
+};
+
+struct wlantest_radius {
+	struct dl_list list;
+	u32 srv;
+	u32 cli;
+	struct radius_msg *last_req;
+};
+
+
+#define MAX_CTRL_CONNECTIONS 10
+#define MAX_NOTES 10
+
+struct tkip_frag {
+	struct wpabuf *buf;
+	u8 ra[ETH_ALEN];
+	u8 ta[ETH_ALEN];
+	u16 sn;
+	u8 fn;
+};
+
+struct wlantest {
+	int monitor_sock;
+	int monitor_wired;
+
+	int ctrl_sock;
+	int ctrl_socks[MAX_CTRL_CONNECTIONS];
+
+	struct dl_list passphrase; /* struct wlantest_passphrase */
+	struct dl_list bss; /* struct wlantest_bss */
+	struct dl_list secret; /* struct wlantest_radius_secret */
+	struct dl_list radius; /* struct wlantest_radius */
+	struct dl_list pmk; /* struct wlantest_pmk */
+	struct dl_list ptk; /* struct wlantest_ptk */
+	struct dl_list wep; /* struct wlantest_wep */
+
+	unsigned int rx_mgmt;
+	unsigned int rx_ctrl;
+	unsigned int rx_data;
+	unsigned int fcs_error;
+	unsigned int frame_num;
+
+	void *write_pcap; /* pcap_t* */
+	void *write_pcap_dumper; /* pcpa_dumper_t */
+	struct timeval write_pcap_time;
+	u8 *decrypted;
+	size_t decrypted_len;
+	FILE *pcapng;
+	u32 write_pcapng_time_high;
+	u32 write_pcapng_time_low;
+
+	u8 last_hdr[30];
+	size_t last_len;
+	int last_mgmt_valid;
+
+	unsigned int assume_fcs:1;
+	unsigned int pcap_no_buffer:1;
+	unsigned int ethernet:1;
+
+	char *notes[MAX_NOTES];
+	size_t num_notes;
+
+	const char *write_file;
+	const char *pcapng_file;
+
+	struct tkip_frag tkip_frag;
+};
+
+void add_note(struct wlantest *wt, int level, const char *fmt, ...)
+PRINTF_FORMAT(3, 4);
+void clear_notes(struct wlantest *wt);
+size_t notes_len(struct wlantest *wt, size_t hdrlen);
+void write_decrypted_note(struct wlantest *wt, const u8 *decrypted,
+			  const u8 *tk, size_t tk_len, int keyid);
+
+int add_wep(struct wlantest *wt, const char *key);
+int read_cap_file(struct wlantest *wt, const char *fname);
+int read_wired_cap_file(struct wlantest *wt, const char *fname);
+
+int write_pcap_init(struct wlantest *wt, const char *fname);
+void write_pcap_deinit(struct wlantest *wt);
+void write_pcap_captured(struct wlantest *wt, const u8 *buf, size_t len);
+void write_pcap_decrypted(struct wlantest *wt, const u8 *buf1, size_t len1,
+			  const u8 *buf2, size_t len2);
+
+int write_pcapng_init(struct wlantest *wt, const char *fname);
+void write_pcapng_deinit(struct wlantest *wt);
+struct pcap_pkthdr;
+void write_pcapng_write_read(struct wlantest *wt, int dlt,
+			     struct pcap_pkthdr *hdr, const u8 *data);
+void write_pcapng_captured(struct wlantest *wt, const u8 *buf, size_t len);
+
+void wlantest_process(struct wlantest *wt, const u8 *data, size_t len);
+void wlantest_process_prism(struct wlantest *wt, const u8 *data, size_t len);
+void wlantest_process_80211(struct wlantest *wt, const u8 *data, size_t len);
+void wlantest_process_wired(struct wlantest *wt, const u8 *data, size_t len);
+int monitor_init(struct wlantest *wt, const char *ifname);
+int monitor_init_wired(struct wlantest *wt, const char *ifname);
+void monitor_deinit(struct wlantest *wt);
+void rx_mgmt(struct wlantest *wt, const u8 *data, size_t len);
+void rx_mgmt_ack(struct wlantest *wt, const struct ieee80211_hdr *hdr);
+void rx_data(struct wlantest *wt, const u8 *data, size_t len);
+void rx_data_eapol(struct wlantest *wt, const u8 *bssid, const u8 *sta_addr,
+		   const u8 *dst, const u8 *src,
+		   const u8 *data, size_t len, int prot);
+void rx_data_ip(struct wlantest *wt, const u8 *bssid, const u8 *sta_addr,
+		const u8 *dst, const u8 *src, const u8 *data, size_t len,
+		const u8 *peer_addr);
+void rx_data_80211_encap(struct wlantest *wt, const u8 *bssid,
+			 const u8 *sta_addr, const u8 *dst, const u8 *src,
+			 const u8 *data, size_t len);
+
+struct wlantest_bss * bss_find(struct wlantest *wt, const u8 *bssid);
+struct wlantest_bss * bss_get(struct wlantest *wt, const u8 *bssid);
+void bss_deinit(struct wlantest_bss *bss);
+void bss_update(struct wlantest *wt, struct wlantest_bss *bss,
+		struct ieee802_11_elems *elems, int beacon);
+void bss_flush(struct wlantest *wt);
+int bss_add_pmk_from_passphrase(struct wlantest_bss *bss,
+				const char *passphrase);
+void pmk_deinit(struct wlantest_pmk *pmk);
+void tdls_deinit(struct wlantest_tdls *tdls);
+
+struct wlantest_sta * sta_find(struct wlantest_bss *bss, const u8 *addr);
+struct wlantest_sta * sta_find_mlo(struct wlantest *wt,
+				   struct wlantest_bss *bss, const u8 *addr);
+struct wlantest_sta * sta_get(struct wlantest_bss *bss, const u8 *addr);
+void sta_deinit(struct wlantest_sta *sta);
+void sta_update_assoc(struct wlantest_sta *sta,
+		      struct ieee802_11_elems *elems);
+void sta_new_ptk(struct wlantest *wt, struct wlantest_sta *sta,
+		 struct wpa_ptk *ptk);
+
+u8 * ccmp_decrypt(const u8 *tk, const struct ieee80211_hdr *hdr,
+		  const u8 *a1, const u8 *a2, const u8 *a3,
+		  const u8 *data, size_t data_len, size_t *decrypted_len);
+u8 * ccmp_encrypt(const u8 *tk, u8 *frame, size_t len, size_t hdrlen,
+		  const u8 *qos, const u8 *a1, const u8 *a2, const u8 *a3,
+		  const u8 *pn, int keyid, size_t *encrypted_len);
+u8 * ccmp_encrypt_pv1(const u8 *tk, const u8 *a1, const u8 *a2, const u8 *a3,
+		      const u8 *frame, size_t len,
+		      size_t hdrlen, const u8 *pn, int keyid,
+		      size_t *encrypted_len);
+void ccmp_get_pn(u8 *pn, const u8 *data);
+u8 * ccmp_256_decrypt(const u8 *tk, const struct ieee80211_hdr *hdr,
+		      const u8 *a1, const u8 *a2, const u8 *a3,
+		      const u8 *data, size_t data_len, size_t *decrypted_len);
+u8 * ccmp_256_encrypt(const u8 *tk, u8 *frame, size_t len, size_t hdrlen,
+		      const u8 *qos, const u8 *a1, const u8 *a2, const u8 *a3,
+		      const u8 *pn, int keyid, size_t *encrypted_len);
+
+enum michael_mic_result {
+	MICHAEL_MIC_OK,
+	MICHAEL_MIC_INCORRECT,
+	MICHAEL_MIC_NOT_VERIFIED
+};
+u8 * tkip_decrypt(const u8 *tk, const struct ieee80211_hdr *hdr,
+		  const u8 *data, size_t data_len, size_t *decrypted_len,
+		  enum michael_mic_result *mic_res, struct tkip_frag *frag);
+u8 * tkip_encrypt(const u8 *tk, u8 *frame, size_t len, size_t hdrlen, u8 *qos,
+		  u8 *pn, int keyid, size_t *encrypted_len);
+void tkip_get_pn(u8 *pn, const u8 *data);
+
+u8 * wep_decrypt(struct wlantest *wt, const struct ieee80211_hdr *hdr,
+		 const u8 *data, size_t data_len, size_t *decrypted_len);
+
+u8 * bip_protect(const u8 *igtk, size_t igtk_len, u8 *frame, size_t len,
+		 u8 *ipn, int keyid, size_t *prot_len);
+u8 * bip_gmac_protect(const u8 *igtk, size_t igtk_len, u8 *frame, size_t len,
+		      u8 *ipn, int keyid, size_t *prot_len);
+
+u8 * gcmp_decrypt(const u8 *tk, size_t tk_len, const struct ieee80211_hdr *hdr,
+		  const u8 *a1, const u8 *a2, const u8 *a3,
+		  const u8 *data, size_t data_len, size_t *decrypted_len);
+u8 * gcmp_encrypt(const u8 *tk, size_t tk_len, const u8 *frame, size_t len,
+		  size_t hdrlen, const u8 *qos, const u8 *a1, const u8 *a2,
+		  const u8 *a3, const u8 *pn, int keyid, size_t *encrypted_len);
+
+int ctrl_init(struct wlantest *wt);
+void ctrl_deinit(struct wlantest *wt);
+
+int wlantest_inject(struct wlantest *wt, struct wlantest_bss *bss,
+		    struct wlantest_sta *sta, u8 *frame, size_t len,
+		    enum wlantest_inject_protection prot);
+
+int wlantest_relog(struct wlantest *wt);
+
+#endif /* WLANTEST_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/wlantest_cli.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/wlantest_cli.c
new file mode 100644
index 0000000..0a1384e
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/wlantest_cli.c
@@ -0,0 +1,1876 @@
+/*
+ * wlantest controller
+ * Copyright (c) 2010-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 <sys/un.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/edit.h"
+#include "common/cli.h"
+#include "wlantest_ctrl.h"
+
+static void print_help(FILE *stream, const char *cmd);
+static char ** wlantest_cli_cmd_list(void);
+
+
+static int get_prev_arg_pos(const char *str, int pos)
+{
+	while (pos > 0 && str[pos - 1] != ' ')
+		pos--;
+	while (pos > 0 && str[pos - 1] == ' ')
+		pos--;
+	while (pos > 0 && str[pos - 1] != ' ')
+		pos--;
+	return pos;
+}
+
+
+static u8 * attr_get(u8 *buf, size_t buflen, enum wlantest_ctrl_attr attr,
+		     size_t *len)
+{
+	u8 *pos = buf;
+
+	while (pos + 8 <= buf + buflen) {
+		enum wlantest_ctrl_attr a;
+		size_t alen;
+		a = WPA_GET_BE32(pos);
+		pos += 4;
+		alen = WPA_GET_BE32(pos);
+		pos += 4;
+		if (pos + alen > buf + buflen) {
+			printf("Invalid control message attribute\n");
+			return NULL;
+		}
+		if (a == attr) {
+			*len = alen;
+			return pos;
+		}
+		pos += alen;
+	}
+
+	return NULL;
+}
+
+
+static u8 * attr_hdr_add(u8 *pos, u8 *end, enum wlantest_ctrl_attr attr,
+			 size_t len)
+{
+	if (pos == NULL || end - pos < 8 + len)
+		return NULL;
+	WPA_PUT_BE32(pos, attr);
+	pos += 4;
+	WPA_PUT_BE32(pos, len);
+	pos += 4;
+	return pos;
+}
+
+
+static u8 * attr_add_str(u8 *pos, u8 *end, enum wlantest_ctrl_attr attr,
+			 const char *str)
+{
+	size_t len = os_strlen(str);
+
+	if (pos == NULL || end - pos < 8 + len)
+		return NULL;
+	WPA_PUT_BE32(pos, attr);
+	pos += 4;
+	WPA_PUT_BE32(pos, len);
+	pos += 4;
+	os_memcpy(pos, str, len);
+	pos += len;
+	return pos;
+}
+
+
+static u8 * attr_add_be32(u8 *pos, u8 *end, enum wlantest_ctrl_attr attr,
+			  u32 val)
+{
+	if (pos == NULL || end - pos < 12)
+		return NULL;
+	WPA_PUT_BE32(pos, attr);
+	pos += 4;
+	WPA_PUT_BE32(pos, 4);
+	pos += 4;
+	WPA_PUT_BE32(pos, val);
+	pos += 4;
+	return pos;
+}
+
+
+static int cmd_send_and_recv(int s, const u8 *cmd, size_t cmd_len,
+			     u8 *resp, size_t max_resp_len)
+{
+	int res;
+	enum wlantest_ctrl_cmd cmd_resp;
+
+	if (send(s, cmd, cmd_len, 0) < 0)
+		return -1;
+	res = recv(s, resp, max_resp_len, 0);
+	if (res < 4)
+		return -1;
+
+	cmd_resp = WPA_GET_BE32(resp);
+	if (cmd_resp == WLANTEST_CTRL_SUCCESS)
+		return res;
+
+	if (cmd_resp == WLANTEST_CTRL_UNKNOWN_CMD)
+		printf("Unknown command\n");
+	else if (cmd_resp == WLANTEST_CTRL_INVALID_CMD)
+		printf("Invalid command\n");
+
+	return -1;
+}
+
+
+static int cmd_simple(int s, enum wlantest_ctrl_cmd cmd)
+{
+	u8 buf[4];
+	int res;
+	WPA_PUT_BE32(buf, cmd);
+	res = cmd_send_and_recv(s, buf, sizeof(buf), buf, sizeof(buf));
+	return res < 0 ? -1 : 0;
+}
+
+
+static char ** get_bssid_list(int s)
+{
+	u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+	u8 buf[4];
+	u8 *bssid;
+	size_t len;
+	int rlen, i;
+	char **res;
+
+	WPA_PUT_BE32(buf, WLANTEST_CTRL_LIST_BSS);
+	rlen = cmd_send_and_recv(s, buf, sizeof(buf), resp, sizeof(resp));
+	if (rlen < 0)
+		return NULL;
+
+	bssid = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_BSSID, &len);
+	if (bssid == NULL)
+		return NULL;
+
+	res = os_calloc(len / ETH_ALEN + 1, sizeof(char *));
+	if (res == NULL)
+		return NULL;
+	for (i = 0; i < len / ETH_ALEN; i++) {
+		res[i] = os_zalloc(18);
+		if (res[i] == NULL)
+			break;
+		os_snprintf(res[i], 18, MACSTR, MAC2STR(bssid + ETH_ALEN * i));
+	}
+
+	return res;
+}
+
+
+static char ** get_sta_list(int s, const u8 *bssid, int add_bcast)
+{
+	u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+	u8 buf[100], *pos, *end;
+	u8 *addr;
+	size_t len;
+	int rlen, i;
+	char **res;
+
+	pos = buf;
+	end = buf + sizeof(buf);
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_LIST_STA);
+	pos += 4;
+	pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN);
+	os_memcpy(pos, bssid, ETH_ALEN);
+	pos += ETH_ALEN;
+	rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+	if (rlen < 0)
+		return NULL;
+
+	addr = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_STA_ADDR, &len);
+	if (addr == NULL)
+		return NULL;
+
+	res = os_calloc(len / ETH_ALEN + 1 + add_bcast, sizeof(char *));
+	if (res == NULL)
+		return NULL;
+	for (i = 0; i < len / ETH_ALEN; i++) {
+		res[i] = os_zalloc(18);
+		if (res[i] == NULL)
+			break;
+		os_snprintf(res[i], 18, MACSTR, MAC2STR(addr + ETH_ALEN * i));
+	}
+	if (add_bcast)
+		res[i] = os_strdup("ff:ff:ff:ff:ff:ff");
+
+	return res;
+}
+
+
+static int cmd_ping(int s, int argc, char *argv[])
+{
+	int res = cmd_simple(s, WLANTEST_CTRL_PING);
+	if (res == 0)
+		printf("PONG\n");
+	return res == 0;
+}
+
+
+static int cmd_terminate(int s, int argc, char *argv[])
+{
+	return cmd_simple(s, WLANTEST_CTRL_TERMINATE);
+}
+
+
+static int cmd_list_bss(int s, int argc, char *argv[])
+{
+	u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+	u8 buf[4];
+	u8 *bssid;
+	size_t len;
+	int rlen, i;
+
+	WPA_PUT_BE32(buf, WLANTEST_CTRL_LIST_BSS);
+	rlen = cmd_send_and_recv(s, buf, sizeof(buf), resp, sizeof(resp));
+	if (rlen < 0)
+		return -1;
+
+	bssid = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_BSSID, &len);
+	if (bssid == NULL)
+		return -1;
+
+	for (i = 0; i < len / ETH_ALEN; i++)
+		printf(MACSTR " ", MAC2STR(bssid + ETH_ALEN * i));
+	printf("\n");
+
+	return 0;
+}
+
+
+static int cmd_list_sta(int s, int argc, char *argv[])
+{
+	u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+	u8 buf[100], *pos;
+	u8 *addr;
+	size_t len;
+	int rlen, i;
+
+	if (argc < 1) {
+		printf("list_sta needs one argument: BSSID\n");
+		return -1;
+	}
+
+	pos = buf;
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_LIST_STA);
+	pos += 4;
+	WPA_PUT_BE32(pos, WLANTEST_ATTR_BSSID);
+	pos += 4;
+	WPA_PUT_BE32(pos, ETH_ALEN);
+	pos += 4;
+	if (hwaddr_aton(argv[0], pos) < 0) {
+		printf("Invalid BSSID '%s'\n", argv[0]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+	if (rlen < 0)
+		return -1;
+
+	addr = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_STA_ADDR, &len);
+	if (addr == NULL)
+		return -1;
+
+	for (i = 0; i < len / ETH_ALEN; i++)
+		printf(MACSTR " ", MAC2STR(addr + ETH_ALEN * i));
+	printf("\n");
+
+	return 0;
+}
+
+
+static char ** complete_list_sta(int s, const char *str, int pos)
+{
+	if (get_cmd_arg_num(str, pos) == 1)
+		return get_bssid_list(s);
+	return NULL;
+}
+
+
+static int cmd_flush(int s, int argc, char *argv[])
+{
+	return cmd_simple(s, WLANTEST_CTRL_FLUSH);
+}
+
+
+static int cmd_clear_sta_counters(int s, int argc, char *argv[])
+{
+	u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+	u8 buf[100], *pos;
+	int rlen;
+
+	if (argc < 2) {
+		printf("clear_sta_counters needs two arguments: BSSID and "
+		       "STA address\n");
+		return -1;
+	}
+
+	pos = buf;
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_CLEAR_STA_COUNTERS);
+	pos += 4;
+	WPA_PUT_BE32(pos, WLANTEST_ATTR_BSSID);
+	pos += 4;
+	WPA_PUT_BE32(pos, ETH_ALEN);
+	pos += 4;
+	if (hwaddr_aton(argv[0], pos) < 0) {
+		printf("Invalid BSSID '%s'\n", argv[0]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	WPA_PUT_BE32(pos, WLANTEST_ATTR_STA_ADDR);
+	pos += 4;
+	WPA_PUT_BE32(pos, ETH_ALEN);
+	pos += 4;
+	if (hwaddr_aton(argv[1], pos) < 0) {
+		printf("Invalid STA address '%s'\n", argv[1]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+	if (rlen < 0)
+		return -1;
+	printf("OK\n");
+	return 0;
+}
+
+
+static char ** complete_clear_sta_counters(int s, const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	char **res = NULL;
+	u8 addr[ETH_ALEN];
+
+	switch (arg) {
+	case 1:
+		res = get_bssid_list(s);
+		break;
+	case 2:
+		if (hwaddr_aton(&str[get_prev_arg_pos(str, pos)], addr) < 0)
+			break;
+		res = get_sta_list(s, addr, 0);
+		break;
+	}
+
+	return res;
+}
+
+
+static int cmd_clear_bss_counters(int s, int argc, char *argv[])
+{
+	u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+	u8 buf[100], *pos;
+	int rlen;
+
+	if (argc < 1) {
+		printf("clear_bss_counters needs one argument: BSSID\n");
+		return -1;
+	}
+
+	pos = buf;
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_CLEAR_BSS_COUNTERS);
+	pos += 4;
+	WPA_PUT_BE32(pos, WLANTEST_ATTR_BSSID);
+	pos += 4;
+	WPA_PUT_BE32(pos, ETH_ALEN);
+	pos += 4;
+	if (hwaddr_aton(argv[0], pos) < 0) {
+		printf("Invalid BSSID '%s'\n", argv[0]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+	if (rlen < 0)
+		return -1;
+	printf("OK\n");
+	return 0;
+}
+
+
+static char ** complete_clear_bss_counters(int s, const char *str, int pos)
+{
+	if (get_cmd_arg_num(str, pos) == 1)
+		return get_bssid_list(s);
+	return NULL;
+}
+
+
+static int cmd_clear_tdls_counters(int s, int argc, char *argv[])
+{
+	u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+	u8 buf[100], *pos;
+	int rlen;
+
+	if (argc < 3) {
+		printf("clear_tdls_counters needs three arguments: BSSID, "
+		       "STA1 address, STA2 address\n");
+		return -1;
+	}
+
+	pos = buf;
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_CLEAR_TDLS_COUNTERS);
+	pos += 4;
+	WPA_PUT_BE32(pos, WLANTEST_ATTR_BSSID);
+	pos += 4;
+	WPA_PUT_BE32(pos, ETH_ALEN);
+	pos += 4;
+	if (hwaddr_aton(argv[0], pos) < 0) {
+		printf("Invalid BSSID '%s'\n", argv[0]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	WPA_PUT_BE32(pos, WLANTEST_ATTR_STA_ADDR);
+	pos += 4;
+	WPA_PUT_BE32(pos, ETH_ALEN);
+	pos += 4;
+	if (hwaddr_aton(argv[1], pos) < 0) {
+		printf("Invalid STA1 address '%s'\n", argv[1]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	WPA_PUT_BE32(pos, WLANTEST_ATTR_STA2_ADDR);
+	pos += 4;
+	WPA_PUT_BE32(pos, ETH_ALEN);
+	pos += 4;
+	if (hwaddr_aton(argv[2], pos) < 0) {
+		printf("Invalid STA2 address '%s'\n", argv[2]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+	if (rlen < 0)
+		return -1;
+	printf("OK\n");
+	return 0;
+}
+
+
+static char ** complete_clear_tdls_counters(int s, const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	char **res = NULL;
+	u8 addr[ETH_ALEN];
+
+	switch (arg) {
+	case 1:
+		res = get_bssid_list(s);
+		break;
+	case 2:
+	case 3:
+		if (hwaddr_aton(&str[get_prev_arg_pos(str, pos)], addr) < 0)
+			break;
+		res = get_sta_list(s, addr, 0);
+		break;
+	}
+
+	return res;
+}
+
+
+struct sta_counters {
+	const char *name;
+	enum wlantest_sta_counter num;
+};
+
+static const struct sta_counters sta_counters[] = {
+	{ "auth_tx", WLANTEST_STA_COUNTER_AUTH_TX },
+	{ "auth_rx", WLANTEST_STA_COUNTER_AUTH_RX },
+	{ "assocreq_tx", WLANTEST_STA_COUNTER_ASSOCREQ_TX },
+	{ "reassocreq_tx", WLANTEST_STA_COUNTER_REASSOCREQ_TX },
+	{ "ptk_learned", WLANTEST_STA_COUNTER_PTK_LEARNED },
+	{ "valid_deauth_tx", WLANTEST_STA_COUNTER_VALID_DEAUTH_TX },
+	{ "valid_deauth_rx", WLANTEST_STA_COUNTER_VALID_DEAUTH_RX },
+	{ "invalid_deauth_tx", WLANTEST_STA_COUNTER_INVALID_DEAUTH_TX },
+	{ "invalid_deauth_rx", WLANTEST_STA_COUNTER_INVALID_DEAUTH_RX },
+	{ "valid_disassoc_tx", WLANTEST_STA_COUNTER_VALID_DISASSOC_TX },
+	{ "valid_disassoc_rx", WLANTEST_STA_COUNTER_VALID_DISASSOC_RX },
+	{ "invalid_disassoc_tx", WLANTEST_STA_COUNTER_INVALID_DISASSOC_TX },
+	{ "invalid_disassoc_rx", WLANTEST_STA_COUNTER_INVALID_DISASSOC_RX },
+	{ "valid_saqueryreq_tx", WLANTEST_STA_COUNTER_VALID_SAQUERYREQ_TX },
+	{ "valid_saqueryreq_rx", WLANTEST_STA_COUNTER_VALID_SAQUERYREQ_RX },
+	{ "invalid_saqueryreq_tx",
+	  WLANTEST_STA_COUNTER_INVALID_SAQUERYREQ_TX },
+	{ "invalid_saqueryreq_rx",
+	  WLANTEST_STA_COUNTER_INVALID_SAQUERYREQ_RX },
+	{ "valid_saqueryresp_tx", WLANTEST_STA_COUNTER_VALID_SAQUERYRESP_TX },
+	{ "valid_saqueryresp_rx", WLANTEST_STA_COUNTER_VALID_SAQUERYRESP_RX },
+	{ "invalid_saqueryresp_tx",
+	  WLANTEST_STA_COUNTER_INVALID_SAQUERYRESP_TX },
+	{ "invalid_saqueryresp_rx",
+	  WLANTEST_STA_COUNTER_INVALID_SAQUERYRESP_RX },
+	{ "ping_ok", WLANTEST_STA_COUNTER_PING_OK },
+	{ "assocresp_comeback", WLANTEST_STA_COUNTER_ASSOCRESP_COMEBACK },
+	{ "reassocresp_comeback", WLANTEST_STA_COUNTER_REASSOCRESP_COMEBACK },
+	{ "ping_ok_first_assoc", WLANTEST_STA_COUNTER_PING_OK_FIRST_ASSOC },
+	{ "valid_deauth_rx_ack", WLANTEST_STA_COUNTER_VALID_DEAUTH_RX_ACK },
+	{ "valid_disassoc_rx_ack",
+	  WLANTEST_STA_COUNTER_VALID_DISASSOC_RX_ACK },
+	{ "invalid_deauth_rx_ack",
+	  WLANTEST_STA_COUNTER_INVALID_DEAUTH_RX_ACK },
+	{ "invalid_disassoc_rx_ack",
+	  WLANTEST_STA_COUNTER_INVALID_DISASSOC_RX_ACK },
+	{ "deauth_rx_asleep", WLANTEST_STA_COUNTER_DEAUTH_RX_ASLEEP },
+	{ "deauth_rx_awake", WLANTEST_STA_COUNTER_DEAUTH_RX_AWAKE },
+	{ "disassoc_rx_asleep", WLANTEST_STA_COUNTER_DISASSOC_RX_ASLEEP },
+	{ "disassoc_rx_awake", WLANTEST_STA_COUNTER_DISASSOC_RX_AWAKE },
+	{ "prot_data_tx", WLANTEST_STA_COUNTER_PROT_DATA_TX },
+	{ "deauth_rx_rc6", WLANTEST_STA_COUNTER_DEAUTH_RX_RC6 },
+	{ "deauth_rx_rc7", WLANTEST_STA_COUNTER_DEAUTH_RX_RC7 },
+	{ "disassoc_rx_rc6", WLANTEST_STA_COUNTER_DISASSOC_RX_RC6 },
+	{ "disassoc_rx_rc7", WLANTEST_STA_COUNTER_DISASSOC_RX_RC7 },
+	{ NULL, 0 }
+};
+
+static int cmd_get_sta_counter(int s, int argc, char *argv[])
+{
+	u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+	u8 buf[100], *end, *pos;
+	int rlen, i;
+	size_t len;
+
+	if (argc != 3) {
+		printf("get_sta_counter needs at three arguments: "
+		       "counter name, BSSID, and STA address\n");
+		return -1;
+	}
+
+	pos = buf;
+	end = buf + sizeof(buf);
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_GET_STA_COUNTER);
+	pos += 4;
+
+	for (i = 0; sta_counters[i].name; i++) {
+		if (os_strcasecmp(sta_counters[i].name, argv[0]) == 0)
+			break;
+	}
+	if (sta_counters[i].name == NULL) {
+		printf("Unknown STA counter '%s'\n", argv[0]);
+		printf("Counters:");
+		for (i = 0; sta_counters[i].name; i++)
+			printf(" %s", sta_counters[i].name);
+		printf("\n");
+		return -1;
+	}
+
+	pos = attr_add_be32(pos, end, WLANTEST_ATTR_STA_COUNTER,
+			    sta_counters[i].num);
+	pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN);
+	if (hwaddr_aton(argv[1], pos) < 0) {
+		printf("Invalid BSSID '%s'\n", argv[1]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	pos = attr_hdr_add(pos, end, WLANTEST_ATTR_STA_ADDR, ETH_ALEN);
+	if (hwaddr_aton(argv[2], pos) < 0) {
+		printf("Invalid STA address '%s'\n", argv[2]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+	if (rlen < 0)
+		return -1;
+
+	pos = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_COUNTER, &len);
+	if (pos == NULL || len != 4)
+		return -1;
+	printf("%u\n", WPA_GET_BE32(pos));
+	return 0;
+}
+
+
+static char ** complete_get_sta_counter(int s, const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	char **res = NULL;
+	int i, count;
+	u8 addr[ETH_ALEN];
+
+	switch (arg) {
+	case 1:
+		/* counter list */
+		count = ARRAY_SIZE(sta_counters);
+		res = os_calloc(count, sizeof(char *));
+		if (res == NULL)
+			return NULL;
+		for (i = 0; sta_counters[i].name; i++) {
+			res[i] = os_strdup(sta_counters[i].name);
+			if (res[i] == NULL)
+				break;
+		}
+		break;
+	case 2:
+		res = get_bssid_list(s);
+		break;
+	case 3:
+		if (hwaddr_aton(&str[get_prev_arg_pos(str, pos)], addr) < 0)
+			break;
+		res = get_sta_list(s, addr, 0);
+		break;
+	}
+
+	return res;
+}
+
+
+struct bss_counters {
+	const char *name;
+	enum wlantest_bss_counter num;
+};
+
+static const struct bss_counters bss_counters[] = {
+	{ "valid_bip_mmie", WLANTEST_BSS_COUNTER_VALID_BIP_MMIE },
+	{ "invalid_bip_mmie", WLANTEST_BSS_COUNTER_INVALID_BIP_MMIE },
+	{ "missing_bip_mmie", WLANTEST_BSS_COUNTER_MISSING_BIP_MMIE },
+	{ "bip_deauth", WLANTEST_BSS_COUNTER_BIP_DEAUTH },
+	{ "bip_disassoc", WLANTEST_BSS_COUNTER_BIP_DISASSOC },
+	{ "probe_response", WLANTEST_BSS_COUNTER_PROBE_RESPONSE },
+	{ NULL, 0 }
+};
+
+static int cmd_get_bss_counter(int s, int argc, char *argv[])
+{
+	u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+	u8 buf[100], *end, *pos;
+	int rlen, i;
+	size_t len;
+
+	if (argc != 2) {
+		printf("get_bss_counter needs at two arguments: "
+		       "counter name and BSSID\n");
+		return -1;
+	}
+
+	pos = buf;
+	end = buf + sizeof(buf);
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_GET_BSS_COUNTER);
+	pos += 4;
+
+	for (i = 0; bss_counters[i].name; i++) {
+		if (os_strcasecmp(bss_counters[i].name, argv[0]) == 0)
+			break;
+	}
+	if (bss_counters[i].name == NULL) {
+		printf("Unknown BSS counter '%s'\n", argv[0]);
+		printf("Counters:");
+		for (i = 0; bss_counters[i].name; i++)
+			printf(" %s", bss_counters[i].name);
+		printf("\n");
+		return -1;
+	}
+
+	pos = attr_add_be32(pos, end, WLANTEST_ATTR_BSS_COUNTER,
+			    bss_counters[i].num);
+	pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN);
+	if (hwaddr_aton(argv[1], pos) < 0) {
+		printf("Invalid BSSID '%s'\n", argv[1]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+	if (rlen < 0)
+		return -1;
+
+	pos = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_COUNTER, &len);
+	if (pos == NULL || len != 4)
+		return -1;
+	printf("%u\n", WPA_GET_BE32(pos));
+	return 0;
+}
+
+
+static char ** complete_get_bss_counter(int s, const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	char **res = NULL;
+	int i, count;
+
+	switch (arg) {
+	case 1:
+		/* counter list */
+		count = ARRAY_SIZE(bss_counters);
+		res = os_calloc(count, sizeof(char *));
+		if (res == NULL)
+			return NULL;
+		for (i = 0; bss_counters[i].name; i++) {
+			res[i] = os_strdup(bss_counters[i].name);
+			if (res[i] == NULL)
+				break;
+		}
+		break;
+	case 2:
+		res = get_bssid_list(s);
+		break;
+	}
+
+	return res;
+}
+
+
+static int cmd_relog(int s, int argc, char *argv[])
+{
+	return cmd_simple(s, WLANTEST_CTRL_RELOG);
+}
+
+
+struct tdls_counters {
+	const char *name;
+	enum wlantest_tdls_counter num;
+};
+
+static const struct tdls_counters tdls_counters[] = {
+	{ "valid_direct_link", WLANTEST_TDLS_COUNTER_VALID_DIRECT_LINK },
+	{ "invalid_direct_link", WLANTEST_TDLS_COUNTER_INVALID_DIRECT_LINK },
+	{ "valid_ap_path", WLANTEST_TDLS_COUNTER_VALID_AP_PATH },
+	{ "invalid_ap_path", WLANTEST_TDLS_COUNTER_INVALID_AP_PATH },
+	{ "setup_req", WLANTEST_TDLS_COUNTER_SETUP_REQ },
+	{ "setup_resp_ok", WLANTEST_TDLS_COUNTER_SETUP_RESP_OK },
+	{ "setup_resp_fail", WLANTEST_TDLS_COUNTER_SETUP_RESP_FAIL },
+	{ "setup_conf_ok", WLANTEST_TDLS_COUNTER_SETUP_CONF_OK },
+	{ "setup_conf_fail", WLANTEST_TDLS_COUNTER_SETUP_CONF_FAIL },
+	{ "teardown", WLANTEST_TDLS_COUNTER_TEARDOWN },
+	{ NULL, 0 }
+};
+
+static int cmd_get_tdls_counter(int s, int argc, char *argv[])
+{
+	u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+	u8 buf[100], *end, *pos;
+	int rlen, i;
+	size_t len;
+
+	if (argc != 4) {
+		printf("get_tdls_counter needs four arguments: "
+		       "counter name, BSSID, STA1 address, STA2 address\n");
+		return -1;
+	}
+
+	pos = buf;
+	end = buf + sizeof(buf);
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_GET_TDLS_COUNTER);
+	pos += 4;
+
+	for (i = 0; tdls_counters[i].name; i++) {
+		if (os_strcasecmp(tdls_counters[i].name, argv[0]) == 0)
+			break;
+	}
+	if (tdls_counters[i].name == NULL) {
+		printf("Unknown TDLS counter '%s'\n", argv[0]);
+		printf("Counters:");
+		for (i = 0; tdls_counters[i].name; i++)
+			printf(" %s", tdls_counters[i].name);
+		printf("\n");
+		return -1;
+	}
+
+	pos = attr_add_be32(pos, end, WLANTEST_ATTR_TDLS_COUNTER,
+			    tdls_counters[i].num);
+	pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN);
+	if (hwaddr_aton(argv[1], pos) < 0) {
+		printf("Invalid BSSID '%s'\n", argv[1]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	pos = attr_hdr_add(pos, end, WLANTEST_ATTR_STA_ADDR, ETH_ALEN);
+	if (hwaddr_aton(argv[2], pos) < 0) {
+		printf("Invalid STA1 address '%s'\n", argv[2]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	pos = attr_hdr_add(pos, end, WLANTEST_ATTR_STA2_ADDR, ETH_ALEN);
+	if (hwaddr_aton(argv[3], pos) < 0) {
+		printf("Invalid STA2 address '%s'\n", argv[3]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+	if (rlen < 0)
+		return -1;
+
+	pos = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_COUNTER, &len);
+	if (pos == NULL || len != 4)
+		return -1;
+	printf("%u\n", WPA_GET_BE32(pos));
+	return 0;
+}
+
+
+static char ** complete_get_tdls_counter(int s, const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	char **res = NULL;
+	int i, count;
+	u8 addr[ETH_ALEN];
+
+	switch (arg) {
+	case 1:
+		/* counter list */
+		count = ARRAY_SIZE(tdls_counters);
+		res = os_calloc(count, sizeof(char *));
+		if (res == NULL)
+			return NULL;
+		for (i = 0; tdls_counters[i].name; i++) {
+			res[i] = os_strdup(tdls_counters[i].name);
+			if (res[i] == NULL)
+				break;
+		}
+		break;
+	case 2:
+		res = get_bssid_list(s);
+		break;
+	case 3:
+	case 4:
+		if (hwaddr_aton(&str[get_prev_arg_pos(str, pos)], addr) < 0)
+			break;
+		res = get_sta_list(s, addr, 0);
+		break;
+	}
+
+	return res;
+}
+
+
+struct inject_frames {
+	const char *name;
+	enum wlantest_inject_frame frame;
+};
+
+static const struct inject_frames inject_frames[] = {
+	{ "auth", WLANTEST_FRAME_AUTH },
+	{ "assocreq", WLANTEST_FRAME_ASSOCREQ },
+	{ "reassocreq", WLANTEST_FRAME_REASSOCREQ },
+	{ "deauth", WLANTEST_FRAME_DEAUTH },
+	{ "disassoc", WLANTEST_FRAME_DISASSOC },
+	{ "saqueryreq", WLANTEST_FRAME_SAQUERYREQ },
+	{ NULL, 0 }
+};
+
+static int cmd_inject(int s, int argc, char *argv[])
+{
+	u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+	u8 buf[100], *end, *pos;
+	int rlen, i;
+	enum wlantest_inject_protection prot;
+
+	/* <frame> <prot> <sender> <BSSID> <STA/ff:ff:ff:ff:ff:ff> */
+
+	if (argc < 5) {
+		printf("inject needs five arguments: frame, protection, "
+		       "sender, BSSID, STA/ff:ff:ff:ff:ff:ff\n");
+		return -1;
+	}
+
+	pos = buf;
+	end = buf + sizeof(buf);
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_INJECT);
+	pos += 4;
+
+	for (i = 0; inject_frames[i].name; i++) {
+		if (os_strcasecmp(inject_frames[i].name, argv[0]) == 0)
+			break;
+	}
+	if (inject_frames[i].name == NULL) {
+		printf("Unknown inject frame '%s'\n", argv[0]);
+		printf("Frames:");
+		for (i = 0; inject_frames[i].name; i++)
+			printf(" %s", inject_frames[i].name);
+		printf("\n");
+		return -1;
+	}
+
+	pos = attr_add_be32(pos, end, WLANTEST_ATTR_INJECT_FRAME,
+			    inject_frames[i].frame);
+
+	if (os_strcasecmp(argv[1], "normal") == 0)
+		prot = WLANTEST_INJECT_NORMAL;
+	else if (os_strcasecmp(argv[1], "protected") == 0)
+		prot = WLANTEST_INJECT_PROTECTED;
+	else if (os_strcasecmp(argv[1], "unprotected") == 0)
+		prot = WLANTEST_INJECT_UNPROTECTED;
+	else if (os_strcasecmp(argv[1], "incorrect") == 0)
+		prot = WLANTEST_INJECT_INCORRECT_KEY;
+	else {
+		printf("Unknown protection type '%s'\n", argv[1]);
+		printf("Protection types: normal protected unprotected "
+		       "incorrect\n");
+		return -1;
+	}
+	pos = attr_add_be32(pos, end, WLANTEST_ATTR_INJECT_PROTECTION, prot);
+
+	if (os_strcasecmp(argv[2], "ap") == 0) {
+		pos = attr_add_be32(pos, end, WLANTEST_ATTR_INJECT_SENDER_AP,
+				    1);
+	} else if (os_strcasecmp(argv[2], "sta") == 0) {
+		pos = attr_add_be32(pos, end, WLANTEST_ATTR_INJECT_SENDER_AP,
+				    0);
+	} else {
+		printf("Unknown sender '%s'\n", argv[2]);
+		printf("Sender types: ap sta\n");
+		return -1;
+	}
+
+	pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN);
+	if (hwaddr_aton(argv[3], pos) < 0) {
+		printf("Invalid BSSID '%s'\n", argv[3]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	pos = attr_hdr_add(pos, end, WLANTEST_ATTR_STA_ADDR, ETH_ALEN);
+	if (hwaddr_aton(argv[4], pos) < 0) {
+		printf("Invalid STA '%s'\n", argv[4]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+	if (rlen < 0)
+		return -1;
+	printf("OK\n");
+	return 0;
+}
+
+
+static char ** complete_inject(int s, const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	char **res = NULL;
+	int i, count;
+	u8 addr[ETH_ALEN];
+
+	switch (arg) {
+	case 1:
+		/* frame list */
+		count = ARRAY_SIZE(inject_frames);
+		res = os_calloc(count, sizeof(char *));
+		if (res == NULL)
+			break;
+		for (i = 0; inject_frames[i].name; i++) {
+			res[i] = os_strdup(inject_frames[i].name);
+			if (res[i] == NULL)
+				break;
+		}
+		break;
+	case 2:
+		res = os_calloc(5, sizeof(char *));
+		if (res == NULL)
+			break;
+		res[0] = os_strdup("normal");
+		if (res[0] == NULL)
+			break;
+		res[1] = os_strdup("protected");
+		if (res[1] == NULL)
+			break;
+		res[2] = os_strdup("unprotected");
+		if (res[2] == NULL)
+			break;
+		res[3] = os_strdup("incorrect");
+		if (res[3] == NULL)
+			break;
+		break;
+	case 3:
+		res = os_calloc(3, sizeof(char *));
+		if (res == NULL)
+			break;
+		res[0] = os_strdup("ap");
+		if (res[0] == NULL)
+			break;
+		res[1] = os_strdup("sta");
+		if (res[1] == NULL)
+			break;
+		break;
+	case 4:
+		res = get_bssid_list(s);
+		break;
+	case 5:
+		if (hwaddr_aton(&str[get_prev_arg_pos(str, pos)], addr) < 0)
+			break;
+		res = get_sta_list(s, addr, 1);
+		break;
+	}
+
+	return res;
+}
+
+
+static u8 * add_hex(u8 *pos, u8 *end, const char *str)
+{
+	const char *s;
+	int val;
+
+	s = str;
+	while (*s) {
+		while (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n' ||
+		       *s == ':')
+			s++;
+		if (*s == '\0')
+			break;
+		if (*s == '#') {
+			while (*s != '\0' && *s != '\r' && *s != '\n')
+				s++;
+			continue;
+		}
+
+		val = hex2byte(s);
+		if (val < 0) {
+			printf("Invalid hex encoding '%s'\n", s);
+			return NULL;
+		}
+		if (pos == end) {
+			printf("Too long frame\n");
+			return NULL;
+		}
+		*pos++ = val;
+		s += 2;
+	}
+
+	return pos;
+}
+
+
+static int cmd_send(int s, int argc, char *argv[])
+{
+	u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+	u8 buf[WLANTEST_CTRL_MAX_CMD_LEN], *end, *pos, *len_pos;
+	int rlen;
+	enum wlantest_inject_protection prot;
+	int arg;
+
+	/* <prot> <raw frame as hex dump> */
+
+	if (argc < 2) {
+		printf("send needs two arguments: protected/unprotected, "
+		       "raw frame as hex dump\n");
+		return -1;
+	}
+
+	pos = buf;
+	end = buf + sizeof(buf);
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_SEND);
+	pos += 4;
+
+	if (os_strcasecmp(argv[0], "normal") == 0)
+		prot = WLANTEST_INJECT_NORMAL;
+	else if (os_strcasecmp(argv[0], "protected") == 0)
+		prot = WLANTEST_INJECT_PROTECTED;
+	else if (os_strcasecmp(argv[0], "unprotected") == 0)
+		prot = WLANTEST_INJECT_UNPROTECTED;
+	else if (os_strcasecmp(argv[0], "incorrect") == 0)
+		prot = WLANTEST_INJECT_INCORRECT_KEY;
+	else {
+		printf("Unknown protection type '%s'\n", argv[1]);
+		printf("Protection types: normal protected unprotected "
+		       "incorrect\n");
+		return -1;
+	}
+	pos = attr_add_be32(pos, end, WLANTEST_ATTR_INJECT_PROTECTION, prot);
+
+	WPA_PUT_BE32(pos, WLANTEST_ATTR_FRAME);
+	pos += 4;
+	len_pos = pos;
+	pos += 4;
+
+	for (arg = 1; pos && arg < argc; arg++)
+		pos = add_hex(pos, end, argv[arg]);
+	if (pos == NULL)
+		return -1;
+
+	WPA_PUT_BE32(len_pos, pos - len_pos - 4);
+
+	rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+	if (rlen < 0)
+		return -1;
+	printf("OK\n");
+	return 0;
+}
+
+
+static char ** complete_send(int s, const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	char **res = NULL;
+
+	switch (arg) {
+	case 1:
+		res = os_calloc(5, sizeof(char *));
+		if (res == NULL)
+			break;
+		res[0] = os_strdup("normal");
+		if (res[0] == NULL)
+			break;
+		res[1] = os_strdup("protected");
+		if (res[1] == NULL)
+			break;
+		res[2] = os_strdup("unprotected");
+		if (res[2] == NULL)
+			break;
+		res[3] = os_strdup("incorrect");
+		if (res[3] == NULL)
+			break;
+		break;
+	}
+
+	return res;
+}
+
+
+static int cmd_version(int s, int argc, char *argv[])
+{
+	u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+	u8 buf[4];
+	char *version;
+	size_t len;
+	int rlen, i;
+
+	WPA_PUT_BE32(buf, WLANTEST_CTRL_VERSION);
+	rlen = cmd_send_and_recv(s, buf, sizeof(buf), resp, sizeof(resp));
+	if (rlen < 0)
+		return -1;
+
+	version = (char *) attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_VERSION,
+				    &len);
+	if (version == NULL)
+		return -1;
+
+	for (i = 0; i < len; i++)
+		putchar(version[i]);
+	printf("\n");
+
+	return 0;
+}
+
+
+static int cmd_add_passphrase(int s, int argc, char *argv[])
+{
+	u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+	u8 buf[100], *pos, *end;
+	size_t len;
+	int rlen;
+
+	if (argc < 1) {
+		printf("add_passphrase needs one argument: passphrase\n");
+		return -1;
+	}
+
+	len = os_strlen(argv[0]);
+	if (len < 8 || len > 63) {
+		printf("Invalid passphrase '%s'\n", argv[0]);
+		return -1;
+	}
+	pos = buf;
+	end = buf + sizeof(buf);
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_ADD_PASSPHRASE);
+	pos += 4;
+	pos = attr_add_str(pos, end, WLANTEST_ATTR_PASSPHRASE,
+			   argv[0]);
+	if (argc > 1) {
+		pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN);
+		if (hwaddr_aton(argv[1], pos) < 0) {
+			printf("Invalid BSSID '%s'\n", argv[3]);
+			return -1;
+		}
+		pos += ETH_ALEN;
+	}
+
+	rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+	if (rlen < 0)
+		return -1;
+	return 0;
+}
+
+
+static int cmd_add_wepkey(int s, int argc, char *argv[])
+{
+	u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+	u8 buf[100], *pos, *end;
+	int rlen;
+
+	if (argc < 1) {
+		printf("add_wepkey needs one argument: WEP key\n");
+		return -1;
+	}
+
+	pos = buf;
+	end = buf + sizeof(buf);
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_ADD_PASSPHRASE);
+	pos += 4;
+	pos = attr_add_str(pos, end, WLANTEST_ATTR_WEPKEY, argv[0]);
+
+	rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+	if (rlen < 0)
+		return -1;
+	return 0;
+}
+
+
+struct sta_infos {
+	const char *name;
+	enum wlantest_sta_info num;
+};
+
+static const struct sta_infos sta_infos[] = {
+	{ "proto", WLANTEST_STA_INFO_PROTO },
+	{ "pairwise", WLANTEST_STA_INFO_PAIRWISE },
+	{ "key_mgmt", WLANTEST_STA_INFO_KEY_MGMT },
+	{ "rsn_capab", WLANTEST_STA_INFO_RSN_CAPAB },
+	{ "state", WLANTEST_STA_INFO_STATE },
+	{ "gtk", WLANTEST_STA_INFO_GTK },
+	{ NULL, 0 }
+};
+
+static int cmd_info_sta(int s, int argc, char *argv[])
+{
+	u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+	u8 buf[100], *end, *pos;
+	int rlen, i;
+	size_t len;
+	char info[100];
+
+	if (argc != 3) {
+		printf("sta_info needs at three arguments: "
+		       "counter name, BSSID, and STA address\n");
+		return -1;
+	}
+
+	pos = buf;
+	end = buf + sizeof(buf);
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_INFO_STA);
+	pos += 4;
+
+	for (i = 0; sta_infos[i].name; i++) {
+		if (os_strcasecmp(sta_infos[i].name, argv[0]) == 0)
+			break;
+	}
+	if (sta_infos[i].name == NULL) {
+		printf("Unknown STA info '%s'\n", argv[0]);
+		printf("Info fields:");
+		for (i = 0; sta_infos[i].name; i++)
+			printf(" %s", sta_infos[i].name);
+		printf("\n");
+		return -1;
+	}
+
+	pos = attr_add_be32(pos, end, WLANTEST_ATTR_STA_INFO,
+			    sta_infos[i].num);
+	pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN);
+	if (hwaddr_aton(argv[1], pos) < 0) {
+		printf("Invalid BSSID '%s'\n", argv[1]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	pos = attr_hdr_add(pos, end, WLANTEST_ATTR_STA_ADDR, ETH_ALEN);
+	if (hwaddr_aton(argv[2], pos) < 0) {
+		printf("Invalid STA address '%s'\n", argv[2]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+	if (rlen < 0)
+		return -1;
+
+	pos = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_INFO, &len);
+	if (pos == NULL)
+		return -1;
+	if (len >= sizeof(info))
+		len = sizeof(info) - 1;
+	os_memcpy(info, pos, len);
+	info[len] = '\0';
+	printf("%s\n", info);
+	return 0;
+}
+
+
+static char ** complete_info_sta(int s, const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	char **res = NULL;
+	int i, count;
+	u8 addr[ETH_ALEN];
+
+	switch (arg) {
+	case 1:
+		/* counter list */
+		count = ARRAY_SIZE(sta_infos);
+		res = os_calloc(count, sizeof(char *));
+		if (res == NULL)
+			return NULL;
+		for (i = 0; sta_infos[i].name; i++) {
+			res[i] = os_strdup(sta_infos[i].name);
+			if (res[i] == NULL)
+				break;
+		}
+		break;
+	case 2:
+		res = get_bssid_list(s);
+		break;
+	case 3:
+		if (hwaddr_aton(&str[get_prev_arg_pos(str, pos)], addr) < 0)
+			break;
+		res = get_sta_list(s, addr, 0);
+		break;
+	}
+
+	return res;
+}
+
+
+struct bss_infos {
+	const char *name;
+	enum wlantest_bss_info num;
+};
+
+static const struct bss_infos bss_infos[] = {
+	{ "proto", WLANTEST_BSS_INFO_PROTO },
+	{ "pairwise", WLANTEST_BSS_INFO_PAIRWISE },
+	{ "group", WLANTEST_BSS_INFO_GROUP },
+	{ "group_mgmt", WLANTEST_BSS_INFO_GROUP_MGMT },
+	{ "key_mgmt", WLANTEST_BSS_INFO_KEY_MGMT },
+	{ "rsn_capab", WLANTEST_BSS_INFO_RSN_CAPAB },
+	{ NULL, 0 }
+};
+
+static int cmd_info_bss(int s, int argc, char *argv[])
+{
+	u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+	u8 buf[100], *end, *pos;
+	int rlen, i;
+	size_t len;
+	char info[100];
+
+	if (argc != 2) {
+		printf("bss_info needs at two arguments: "
+		       "field name and BSSID\n");
+		return -1;
+	}
+
+	pos = buf;
+	end = buf + sizeof(buf);
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_INFO_BSS);
+	pos += 4;
+
+	for (i = 0; bss_infos[i].name; i++) {
+		if (os_strcasecmp(bss_infos[i].name, argv[0]) == 0)
+			break;
+	}
+	if (bss_infos[i].name == NULL) {
+		printf("Unknown BSS info '%s'\n", argv[0]);
+		printf("Info fields:");
+		for (i = 0; bss_infos[i].name; i++)
+			printf(" %s", bss_infos[i].name);
+		printf("\n");
+		return -1;
+	}
+
+	pos = attr_add_be32(pos, end, WLANTEST_ATTR_BSS_INFO,
+			    bss_infos[i].num);
+	pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN);
+	if (hwaddr_aton(argv[1], pos) < 0) {
+		printf("Invalid BSSID '%s'\n", argv[1]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+	if (rlen < 0)
+		return -1;
+
+	pos = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_INFO, &len);
+	if (pos == NULL)
+		return -1;
+	if (len >= sizeof(info))
+		len = sizeof(info) - 1;
+	os_memcpy(info, pos, len);
+	info[len] = '\0';
+	printf("%s\n", info);
+	return 0;
+}
+
+
+static char ** complete_info_bss(int s, const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	char **res = NULL;
+	int i, count;
+
+	switch (arg) {
+	case 1:
+		/* counter list */
+		count = ARRAY_SIZE(bss_infos);
+		res = os_calloc(count, sizeof(char *));
+		if (res == NULL)
+			return NULL;
+		for (i = 0; bss_infos[i].name; i++) {
+			res[i] = os_strdup(bss_infos[i].name);
+			if (res[i] == NULL)
+				break;
+		}
+		break;
+	case 2:
+		res = get_bssid_list(s);
+		break;
+	}
+
+	return res;
+}
+
+
+static int cmd_get_tx_tid(int s, int argc, char *argv[])
+{
+	u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+	u8 buf[100], *end, *pos;
+	int rlen;
+	size_t len;
+
+	if (argc != 3) {
+		printf("get_tx_tid needs three arguments: "
+		       "BSSID, STA address, and TID\n");
+		return -1;
+	}
+
+	pos = buf;
+	end = buf + sizeof(buf);
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_GET_TX_TID);
+	pos += 4;
+
+	pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN);
+	if (hwaddr_aton(argv[0], pos) < 0) {
+		printf("Invalid BSSID '%s'\n", argv[0]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	pos = attr_hdr_add(pos, end, WLANTEST_ATTR_STA_ADDR, ETH_ALEN);
+	if (hwaddr_aton(argv[1], pos) < 0) {
+		printf("Invalid STA address '%s'\n", argv[1]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	pos = attr_add_be32(pos, end, WLANTEST_ATTR_TID, atoi(argv[2]));
+
+	rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+	if (rlen < 0)
+		return -1;
+
+	pos = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_COUNTER, &len);
+	if (pos == NULL || len != 4)
+		return -1;
+	printf("%u\n", WPA_GET_BE32(pos));
+	return 0;
+}
+
+
+static int cmd_get_rx_tid(int s, int argc, char *argv[])
+{
+	u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+	u8 buf[100], *end, *pos;
+	int rlen;
+	size_t len;
+
+	if (argc != 3) {
+		printf("get_tx_tid needs three arguments: "
+		       "BSSID, STA address, and TID\n");
+		return -1;
+	}
+
+	pos = buf;
+	end = buf + sizeof(buf);
+	WPA_PUT_BE32(pos, WLANTEST_CTRL_GET_RX_TID);
+	pos += 4;
+
+	pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN);
+	if (hwaddr_aton(argv[0], pos) < 0) {
+		printf("Invalid BSSID '%s'\n", argv[0]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	pos = attr_hdr_add(pos, end, WLANTEST_ATTR_STA_ADDR, ETH_ALEN);
+	if (hwaddr_aton(argv[1], pos) < 0) {
+		printf("Invalid STA address '%s'\n", argv[1]);
+		return -1;
+	}
+	pos += ETH_ALEN;
+
+	pos = attr_add_be32(pos, end, WLANTEST_ATTR_TID, atoi(argv[2]));
+
+	rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+	if (rlen < 0)
+		return -1;
+
+	pos = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_COUNTER, &len);
+	if (pos == NULL || len != 4)
+		return -1;
+	printf("%u\n", WPA_GET_BE32(pos));
+	return 0;
+}
+
+
+static char ** complete_get_tid(int s, const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	char **res = NULL;
+	u8 addr[ETH_ALEN];
+
+	switch (arg) {
+	case 1:
+		res = get_bssid_list(s);
+		break;
+	case 2:
+		if (hwaddr_aton(&str[get_prev_arg_pos(str, pos)], addr) < 0)
+			break;
+		res = get_sta_list(s, addr, 0);
+		break;
+	}
+
+	return res;
+}
+
+
+static int wlantest_cli_cmd_help(int s, int argc, char *argv[])
+{
+	print_help(stdout, argc > 0 ? argv[0] : NULL);
+	return 0;
+}
+
+
+static char ** wlantest_cli_complete_help(int s, const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	char **res = NULL;
+
+	switch (arg) {
+	case 1:
+		res = wlantest_cli_cmd_list();
+		break;
+	}
+
+	return res;
+}
+
+
+struct wlantest_cli_cmd {
+	const char *cmd;
+	int (*handler)(int s, int argc, char *argv[]);
+	const char *usage;
+	char ** (*complete)(int s, const char *str, int pos);
+};
+
+static const struct wlantest_cli_cmd wlantest_cli_commands[] = {
+	{ "ping", cmd_ping, "= test connection to wlantest", NULL },
+	{ "terminate", cmd_terminate, "= terminate wlantest", NULL },
+	{ "list_bss", cmd_list_bss, "= get BSS list", NULL },
+	{ "list_sta", cmd_list_sta, "<BSSID> = get STA list",
+	  complete_list_sta },
+	{ "flush", cmd_flush, "= drop all collected BSS data", NULL },
+	{ "clear_sta_counters", cmd_clear_sta_counters,
+	  "<BSSID> <STA> = clear STA counters", complete_clear_sta_counters },
+	{ "clear_bss_counters", cmd_clear_bss_counters,
+	  "<BSSID> = clear BSS counters", complete_clear_bss_counters },
+	{ "get_sta_counter", cmd_get_sta_counter,
+	  "<counter> <BSSID> <STA> = get STA counter value",
+	  complete_get_sta_counter },
+	{ "get_bss_counter", cmd_get_bss_counter,
+	  "<counter> <BSSID> = get BSS counter value",
+	  complete_get_bss_counter },
+	{ "inject", cmd_inject,
+	  "<frame> <prot> <sender> <BSSID> <STA/ff:ff:ff:ff:ff:ff>",
+	  complete_inject },
+	{ "send", cmd_send,
+	  "<prot> <raw frame as hex dump>",
+	  complete_send },
+	{ "version", cmd_version, "= get wlantest version", NULL },
+	{ "add_passphrase", cmd_add_passphrase,
+	  "<passphrase> = add a known passphrase", NULL },
+	{ "add_wepkey", cmd_add_wepkey,
+	  "<WEP key> = add a known WEP key", NULL },
+	{ "info_sta", cmd_info_sta,
+	  "<field> <BSSID> <STA> = get STA information",
+	  complete_info_sta },
+	{ "info_bss", cmd_info_bss,
+	  "<field> <BSSID> = get BSS information",
+	  complete_info_bss },
+	{ "clear_tdls_counters", cmd_clear_tdls_counters,
+	  "<BSSID> <STA1> <STA2> = clear TDLS counters",
+	  complete_clear_tdls_counters },
+	{ "get_tdls_counter", cmd_get_tdls_counter,
+	  "<counter> <BSSID> <STA1> <STA2> = get TDLS counter value",
+	  complete_get_tdls_counter },
+	{ "get_bss_counter", cmd_get_bss_counter,
+	  "<counter> <BSSID> = get BSS counter value",
+	  complete_get_bss_counter },
+	{ "relog", cmd_relog, "= re-open log-file (allow rolling logs)", NULL },
+	{ "get_tx_tid", cmd_get_tx_tid,
+	  "<BSSID> <STA> <TID> = get STA TX TID counter value",
+	  complete_get_tid },
+	{ "get_rx_tid", cmd_get_rx_tid,
+	  "<BSSID> <STA> <TID> = get STA RX TID counter value",
+	  complete_get_tid },
+	{ "help", wlantest_cli_cmd_help,
+	  "= show this usage help", wlantest_cli_complete_help },
+	{ NULL, NULL, NULL, NULL }
+};
+
+
+/*
+ * Prints command usage, lines are padded with the specified string.
+ */
+static void print_cmd_help(FILE *stream, const struct wlantest_cli_cmd *cmd,
+			   const char *pad)
+{
+	char c;
+	size_t n;
+
+	if (!cmd->usage)
+		return;
+	fprintf(stream, "%s%s ", pad, cmd->cmd);
+	for (n = 0; (c = cmd->usage[n]); n++) {
+		fprintf(stream, "%c", c);
+		if (c == '\n')
+			fprintf(stream, "%s", pad);
+	}
+	fprintf(stream, "\n");
+}
+
+
+static void print_help(FILE *stream, const char *cmd)
+{
+	int n;
+
+	fprintf(stream, "commands:\n");
+	for (n = 0; wlantest_cli_commands[n].cmd; n++) {
+		if (!cmd || str_starts(wlantest_cli_commands[n].cmd, cmd))
+			print_cmd_help(stream, &wlantest_cli_commands[n], "  ");
+	}
+}
+
+
+static int ctrl_command(int s, int argc, char *argv[])
+{
+	const struct wlantest_cli_cmd *cmd, *match = NULL;
+	int count = 0;
+	int ret = 0;
+
+	for (cmd = wlantest_cli_commands; cmd->cmd; cmd++) {
+		if (os_strncasecmp(cmd->cmd, argv[0], os_strlen(argv[0])) == 0)
+		{
+			match = cmd;
+			if (os_strcasecmp(cmd->cmd, argv[0]) == 0) {
+				/* exact match */
+				count = 1;
+				break;
+			}
+			count++;
+		}
+	}
+
+	if (count > 1) {
+		printf("Ambiguous command '%s'; possible commands:", argv[0]);
+		for (cmd = wlantest_cli_commands; cmd->cmd; cmd++) {
+			if (os_strncasecmp(cmd->cmd, argv[0],
+					   os_strlen(argv[0])) == 0) {
+				printf(" %s", cmd->cmd);
+			}
+		}
+		printf("\n");
+		ret = 1;
+	} else if (count == 0) {
+		printf("Unknown command '%s'\n", argv[0]);
+		ret = 1;
+	} else {
+		ret = match->handler(s, argc - 1, &argv[1]);
+	}
+
+	return ret;
+}
+
+
+struct wlantest_cli {
+	int s;
+};
+
+
+static void wlantest_cli_edit_cmd_cb(void *ctx, char *cmd)
+{
+	struct wlantest_cli *cli = ctx;
+	char *argv[max_args];
+	int argc;
+	argc = tokenize_cmd(cmd, argv);
+	if (argc) {
+		int ret = ctrl_command(cli->s, argc, argv);
+		if (ret < 0)
+			printf("FAIL\n");
+	}
+}
+
+
+static void wlantest_cli_eloop_terminate(int sig, void *signal_ctx)
+{
+	eloop_terminate();
+}
+
+
+static void wlantest_cli_edit_eof_cb(void *ctx)
+{
+	eloop_terminate();
+}
+
+
+static char ** wlantest_cli_cmd_list(void)
+{
+	char **res;
+	int i;
+
+	res = os_calloc(ARRAY_SIZE(wlantest_cli_commands), sizeof(char *));
+	if (res == NULL)
+		return NULL;
+
+	for (i = 0; wlantest_cli_commands[i].cmd; i++) {
+		res[i] = os_strdup(wlantest_cli_commands[i].cmd);
+		if (res[i] == NULL)
+			break;
+	}
+
+	return res;
+}
+
+
+static char ** wlantest_cli_cmd_completion(struct wlantest_cli *cli,
+					   const char *cmd, const char *str,
+					   int pos)
+{
+	int i;
+
+	for (i = 0; wlantest_cli_commands[i].cmd; i++) {
+		const struct wlantest_cli_cmd *c = &wlantest_cli_commands[i];
+		if (os_strcasecmp(c->cmd, cmd) == 0) {
+			edit_clear_line();
+			printf("\r%s\n", c->usage);
+			edit_redraw();
+			if (c->complete)
+				return c->complete(cli->s, str, pos);
+			break;
+		}
+	}
+
+	return NULL;
+}
+
+
+static char ** wlantest_cli_edit_completion_cb(void *ctx, const char *str,
+					       int pos)
+{
+	struct wlantest_cli *cli = ctx;
+	char **res;
+	const char *end;
+	char *cmd;
+
+	end = os_strchr(str, ' ');
+	if (end == NULL || str + pos < end)
+		return wlantest_cli_cmd_list();
+
+	cmd = os_malloc(pos + 1);
+	if (cmd == NULL)
+		return NULL;
+	os_memcpy(cmd, str, pos);
+	cmd[end - str] = '\0';
+	res = wlantest_cli_cmd_completion(cli, cmd, str, pos);
+	os_free(cmd);
+	return res;
+}
+
+
+static void wlantest_cli_interactive(int s)
+{
+	struct wlantest_cli cli;
+	char *home, *hfile = NULL;
+
+	if (eloop_init())
+		return;
+
+	home = getenv("HOME");
+	if (home) {
+		const char *fname = ".wlantest_cli_history";
+		int hfile_len = os_strlen(home) + 1 + os_strlen(fname) + 1;
+		hfile = os_malloc(hfile_len);
+		if (hfile)
+			os_snprintf(hfile, hfile_len, "%s/%s", home, fname);
+	}
+
+	cli.s = s;
+	eloop_register_signal_terminate(wlantest_cli_eloop_terminate, &cli);
+	edit_init(wlantest_cli_edit_cmd_cb, wlantest_cli_edit_eof_cb,
+		  wlantest_cli_edit_completion_cb, &cli, hfile, NULL);
+
+	eloop_run();
+
+	edit_deinit(hfile, NULL);
+	os_free(hfile);
+	eloop_destroy();
+}
+
+
+int main(int argc, char *argv[])
+{
+	int s;
+	struct sockaddr_un addr;
+	int ret = 0;
+
+	if (os_program_init())
+		return -1;
+
+	s = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+	if (s < 0) {
+		perror("socket");
+		return -1;
+	}
+
+	os_memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+	os_strlcpy(addr.sun_path + 1, WLANTEST_SOCK_NAME,
+		   sizeof(addr.sun_path) - 1);
+	if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		perror("connect");
+		close(s);
+		return -1;
+	}
+
+	if (argc > 1) {
+		ret = ctrl_command(s, argc - 1, &argv[1]);
+		if (ret < 0)
+			printf("FAIL\n");
+	} else {
+		wlantest_cli_interactive(s);
+	}
+
+	close(s);
+
+	os_program_deinit();
+
+	return ret;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/wlantest_ctrl.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/wlantest_ctrl.h
new file mode 100644
index 0000000..1af6838
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/wlantest_ctrl.h
@@ -0,0 +1,171 @@
+/*
+ * wlantest control interface
+ * Copyright (c) 2010-2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WLANTEST_CTRL_H
+#define WLANTEST_CTRL_H
+
+#define WLANTEST_SOCK_NAME "w1.fi.wlantest"
+#define WLANTEST_CTRL_MAX_CMD_LEN 1000
+#define WLANTEST_CTRL_MAX_RESP_LEN 1000
+
+enum wlantest_ctrl_cmd {
+	WLANTEST_CTRL_SUCCESS,
+	WLANTEST_CTRL_FAILURE,
+	WLANTEST_CTRL_INVALID_CMD,
+	WLANTEST_CTRL_UNKNOWN_CMD,
+	WLANTEST_CTRL_PING,
+	WLANTEST_CTRL_TERMINATE,
+	WLANTEST_CTRL_LIST_BSS,
+	WLANTEST_CTRL_LIST_STA,
+	WLANTEST_CTRL_FLUSH,
+	WLANTEST_CTRL_CLEAR_STA_COUNTERS,
+	WLANTEST_CTRL_CLEAR_BSS_COUNTERS,
+	WLANTEST_CTRL_GET_STA_COUNTER,
+	WLANTEST_CTRL_GET_BSS_COUNTER,
+	WLANTEST_CTRL_INJECT,
+	WLANTEST_CTRL_VERSION,
+	WLANTEST_CTRL_ADD_PASSPHRASE,
+	WLANTEST_CTRL_INFO_STA,
+	WLANTEST_CTRL_INFO_BSS,
+	WLANTEST_CTRL_SEND,
+	WLANTEST_CTRL_CLEAR_TDLS_COUNTERS,
+	WLANTEST_CTRL_GET_TDLS_COUNTER,
+	WLANTEST_CTRL_RELOG,
+	WLANTEST_CTRL_GET_TX_TID,
+	WLANTEST_CTRL_GET_RX_TID,
+};
+
+enum wlantest_ctrl_attr {
+	WLANTEST_ATTR_BSSID,
+	WLANTEST_ATTR_STA_ADDR,
+	WLANTEST_ATTR_STA_COUNTER,
+	WLANTEST_ATTR_BSS_COUNTER,
+	WLANTEST_ATTR_COUNTER,
+	WLANTEST_ATTR_INJECT_FRAME,
+	WLANTEST_ATTR_INJECT_SENDER_AP,
+	WLANTEST_ATTR_INJECT_PROTECTION,
+	WLANTEST_ATTR_VERSION,
+	WLANTEST_ATTR_PASSPHRASE,
+	WLANTEST_ATTR_STA_INFO,
+	WLANTEST_ATTR_BSS_INFO,
+	WLANTEST_ATTR_INFO,
+	WLANTEST_ATTR_FRAME,
+	WLANTEST_ATTR_TDLS_COUNTER,
+	WLANTEST_ATTR_STA2_ADDR,
+	WLANTEST_ATTR_WEPKEY,
+	WLANTEST_ATTR_TID,
+};
+
+enum wlantest_bss_counter {
+	WLANTEST_BSS_COUNTER_VALID_BIP_MMIE,
+	WLANTEST_BSS_COUNTER_INVALID_BIP_MMIE,
+	WLANTEST_BSS_COUNTER_MISSING_BIP_MMIE,
+	WLANTEST_BSS_COUNTER_BIP_DEAUTH,
+	WLANTEST_BSS_COUNTER_BIP_DISASSOC,
+	WLANTEST_BSS_COUNTER_PROBE_RESPONSE,
+	NUM_WLANTEST_BSS_COUNTER
+};
+
+enum wlantest_sta_counter {
+	WLANTEST_STA_COUNTER_AUTH_TX,
+	WLANTEST_STA_COUNTER_AUTH_RX,
+	WLANTEST_STA_COUNTER_ASSOCREQ_TX,
+	WLANTEST_STA_COUNTER_REASSOCREQ_TX,
+	WLANTEST_STA_COUNTER_PTK_LEARNED,
+	WLANTEST_STA_COUNTER_VALID_DEAUTH_TX,
+	WLANTEST_STA_COUNTER_VALID_DEAUTH_RX,
+	WLANTEST_STA_COUNTER_INVALID_DEAUTH_TX,
+	WLANTEST_STA_COUNTER_INVALID_DEAUTH_RX,
+	WLANTEST_STA_COUNTER_VALID_DISASSOC_TX,
+	WLANTEST_STA_COUNTER_VALID_DISASSOC_RX,
+	WLANTEST_STA_COUNTER_INVALID_DISASSOC_TX,
+	WLANTEST_STA_COUNTER_INVALID_DISASSOC_RX,
+	WLANTEST_STA_COUNTER_VALID_SAQUERYREQ_TX,
+	WLANTEST_STA_COUNTER_VALID_SAQUERYREQ_RX,
+	WLANTEST_STA_COUNTER_INVALID_SAQUERYREQ_TX,
+	WLANTEST_STA_COUNTER_INVALID_SAQUERYREQ_RX,
+	WLANTEST_STA_COUNTER_VALID_SAQUERYRESP_TX,
+	WLANTEST_STA_COUNTER_VALID_SAQUERYRESP_RX,
+	WLANTEST_STA_COUNTER_INVALID_SAQUERYRESP_TX,
+	WLANTEST_STA_COUNTER_INVALID_SAQUERYRESP_RX,
+	WLANTEST_STA_COUNTER_PING_OK,
+	WLANTEST_STA_COUNTER_ASSOCRESP_COMEBACK,
+	WLANTEST_STA_COUNTER_REASSOCRESP_COMEBACK,
+	WLANTEST_STA_COUNTER_PING_OK_FIRST_ASSOC,
+	WLANTEST_STA_COUNTER_VALID_DEAUTH_RX_ACK,
+	WLANTEST_STA_COUNTER_VALID_DISASSOC_RX_ACK,
+	WLANTEST_STA_COUNTER_INVALID_DEAUTH_RX_ACK,
+	WLANTEST_STA_COUNTER_INVALID_DISASSOC_RX_ACK,
+	WLANTEST_STA_COUNTER_DEAUTH_RX_ASLEEP,
+	WLANTEST_STA_COUNTER_DEAUTH_RX_AWAKE,
+	WLANTEST_STA_COUNTER_DISASSOC_RX_ASLEEP,
+	WLANTEST_STA_COUNTER_DISASSOC_RX_AWAKE,
+	WLANTEST_STA_COUNTER_PROT_DATA_TX,
+	WLANTEST_STA_COUNTER_DEAUTH_RX_RC6,
+	WLANTEST_STA_COUNTER_DEAUTH_RX_RC7,
+	WLANTEST_STA_COUNTER_DISASSOC_RX_RC6,
+	WLANTEST_STA_COUNTER_DISASSOC_RX_RC7,
+	NUM_WLANTEST_STA_COUNTER
+};
+
+enum wlantest_tdls_counter {
+	WLANTEST_TDLS_COUNTER_VALID_DIRECT_LINK,
+	WLANTEST_TDLS_COUNTER_INVALID_DIRECT_LINK,
+	WLANTEST_TDLS_COUNTER_VALID_AP_PATH,
+	WLANTEST_TDLS_COUNTER_INVALID_AP_PATH,
+	WLANTEST_TDLS_COUNTER_SETUP_REQ,
+	WLANTEST_TDLS_COUNTER_SETUP_RESP_OK,
+	WLANTEST_TDLS_COUNTER_SETUP_RESP_FAIL,
+	WLANTEST_TDLS_COUNTER_SETUP_CONF_OK,
+	WLANTEST_TDLS_COUNTER_SETUP_CONF_FAIL,
+	WLANTEST_TDLS_COUNTER_TEARDOWN,
+	NUM_WLANTEST_TDLS_COUNTER
+};
+
+enum wlantest_inject_frame {
+	WLANTEST_FRAME_AUTH,
+	WLANTEST_FRAME_ASSOCREQ,
+	WLANTEST_FRAME_REASSOCREQ,
+	WLANTEST_FRAME_DEAUTH,
+	WLANTEST_FRAME_DISASSOC,
+	WLANTEST_FRAME_SAQUERYREQ,
+};
+
+/**
+ * enum wlantest_inject_protection - WLANTEST_CTRL_INJECT protection
+ * @WLANTEST_INJECT_NORMAL: Use normal rules (protect if key is set)
+ * @WLANTEST_INJECT_PROTECTED: Force protection (fail if not possible)
+ * @WLANTEST_INJECT_UNPROTECTED: Force unprotected
+ * @WLANTEST_INJECT_INCORRECT_KEY: Force protection with incorrect key
+ */
+enum wlantest_inject_protection {
+	WLANTEST_INJECT_NORMAL,
+	WLANTEST_INJECT_PROTECTED,
+	WLANTEST_INJECT_UNPROTECTED,
+	WLANTEST_INJECT_INCORRECT_KEY,
+};
+
+enum wlantest_sta_info {
+	WLANTEST_STA_INFO_PROTO,
+	WLANTEST_STA_INFO_PAIRWISE,
+	WLANTEST_STA_INFO_KEY_MGMT,
+	WLANTEST_STA_INFO_RSN_CAPAB,
+	WLANTEST_STA_INFO_STATE,
+	WLANTEST_STA_INFO_GTK,
+};
+
+enum wlantest_bss_info {
+	WLANTEST_BSS_INFO_PROTO,
+	WLANTEST_BSS_INFO_PAIRWISE,
+	WLANTEST_BSS_INFO_GROUP,
+	WLANTEST_BSS_INFO_GROUP_MGMT,
+	WLANTEST_BSS_INFO_KEY_MGMT,
+	WLANTEST_BSS_INFO_RSN_CAPAB,
+};
+
+#endif /* WLANTEST_CTRL_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/writepcap.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/writepcap.c
new file mode 100644
index 0000000..fee2c40
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/wlantest/writepcap.c
@@ -0,0 +1,373 @@
+/*
+ * PCAP capture file writer
+ * Copyright (c) 2010-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 <pcap.h>
+#include <pcap-bpf.h>
+
+#include "utils/common.h"
+#include "wlantest.h"
+#include "common/qca-vendor.h"
+
+
+int write_pcap_init(struct wlantest *wt, const char *fname)
+{
+	int linktype = wt->ethernet ? DLT_EN10MB : DLT_IEEE802_11_RADIO;
+
+	wt->write_pcap = pcap_open_dead(linktype, 4000);
+	if (wt->write_pcap == NULL)
+		return -1;
+	wt->write_pcap_dumper = pcap_dump_open(wt->write_pcap, fname);
+	if (wt->write_pcap_dumper == NULL) {
+		pcap_close(wt->write_pcap);
+		wt->write_pcap = NULL;
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "Writing PCAP dump to '%s'", fname);
+
+	return 0;
+}
+
+
+void write_pcap_deinit(struct wlantest *wt)
+{
+	if (wt->write_pcap_dumper) {
+		pcap_dump_close(wt->write_pcap_dumper);
+		wt->write_pcap_dumper = NULL;
+	}
+	if (wt->write_pcap) {
+		pcap_close(wt->write_pcap);
+		wt->write_pcap = NULL;
+	}
+}
+
+
+void write_pcap_captured(struct wlantest *wt, const u8 *buf, size_t len)
+{
+	struct pcap_pkthdr h;
+
+	if (!wt->write_pcap_dumper)
+		return;
+
+	os_memset(&h, 0, sizeof(h));
+	gettimeofday(&wt->write_pcap_time, NULL);
+	h.ts = wt->write_pcap_time;
+	h.caplen = len;
+	h.len = len;
+	pcap_dump(wt->write_pcap_dumper, &h, buf);
+	if (wt->pcap_no_buffer)
+		pcap_dump_flush(wt->write_pcap_dumper);
+}
+
+
+void write_pcap_decrypted(struct wlantest *wt, const u8 *buf1, size_t len1,
+			  const u8 *buf2, size_t len2)
+{
+	struct pcap_pkthdr h;
+	u8 rtap[] = {
+		0x00 /* rev */,
+		0x00 /* pad */,
+		0x0e, 0x00, /* header len */
+		0x00, 0x00, 0x00, 0x40, /* present flags */
+		0x00, 0x13, 0x74, QCA_RADIOTAP_VID_WLANTEST,
+		0x00, 0x00
+	};
+	u8 *buf;
+	size_t len;
+
+	if (!wt->write_pcap_dumper && !wt->pcapng)
+		return;
+
+	os_free(wt->decrypted);
+	len = sizeof(rtap) + len1 + len2;
+	wt->decrypted = buf = os_malloc(len);
+	if (buf == NULL)
+		return;
+	wt->decrypted_len = len;
+	os_memcpy(buf, rtap, sizeof(rtap));
+	if (buf1) {
+		os_memcpy(buf + sizeof(rtap), buf1, len1);
+		buf[sizeof(rtap) + 1] &= ~0x40; /* Clear Protected flag */
+	}
+	if (buf2)
+		os_memcpy(buf + sizeof(rtap) + len1, buf2, len2);
+
+	if (!wt->write_pcap_dumper)
+		return;
+
+	os_memset(&h, 0, sizeof(h));
+	h.ts = wt->write_pcap_time;
+	h.caplen = len;
+	h.len = len;
+	pcap_dump(wt->write_pcap_dumper, &h, buf);
+	if (wt->pcap_no_buffer)
+		pcap_dump_flush(wt->write_pcap_dumper);
+}
+
+
+struct pcapng_section_header {
+	u32 block_type; /* 0x0a0d0d0a */
+	u32 block_total_len;
+	u32 byte_order_magic;
+	u16 major_version;
+	u16 minor_version;
+	u64 section_len;
+	u32 block_total_len2;
+} STRUCT_PACKED;
+
+struct pcapng_interface_description {
+	u32 block_type; /* 0x00000001 */
+	u32 block_total_len;
+	u16 link_type;
+	u16 reserved;
+	u32 snap_len;
+	u32 block_total_len2;
+} STRUCT_PACKED;
+
+struct pcapng_enhanced_packet {
+	u32 block_type; /* 0x00000006 */
+	u32 block_total_len;
+	u32 interface_id;
+	u32 timestamp_high;
+	u32 timestamp_low;
+	u32 captured_len;
+	u32 packet_len;
+	/* Packet data - aligned to 32 bits */
+	/* Options (variable) */
+	/* Block Total Length copy */
+} STRUCT_PACKED;
+
+#define PCAPNG_BYTE_ORDER_MAGIC 0x1a2b3c4d
+#define PCAPNG_BLOCK_IFACE_DESC 0x00000001
+#define PCAPNG_BLOCK_PACKET 0x00000002
+#define PCAPNG_BLOCK_SIMPLE_PACKET 0x00000003
+#define PCAPNG_BLOCK_NAME_RESOLUTION 0x00000004
+#define PCAPNG_BLOCK_INTERFACE_STATISTICS 0x00000005
+#define PCAPNG_BLOCK_ENHANCED_PACKET 0x00000006
+#define PCAPNG_BLOCK_SECTION_HEADER 0x0a0d0d0a
+
+#define LINKTYPE_IEEE802_11 105
+#define LINKTYPE_IEEE802_11_RADIO 127
+
+#define PAD32(a) ((4 - ((a) & 3)) & 3)
+#define ALIGN32(a) ((a) + PAD32((a)))
+
+
+int write_pcapng_init(struct wlantest *wt, const char *fname)
+{
+	struct pcapng_section_header hdr;
+	struct pcapng_interface_description desc;
+
+	wt->pcapng = fopen(fname, "wb");
+	if (wt->pcapng == NULL)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "Writing PCAPNG dump to '%s'", fname);
+
+	os_memset(&hdr, 0, sizeof(hdr));
+	hdr.block_type = PCAPNG_BLOCK_SECTION_HEADER;
+	hdr.block_total_len = sizeof(hdr);
+	hdr.byte_order_magic = PCAPNG_BYTE_ORDER_MAGIC;
+	hdr.major_version = 1;
+	hdr.minor_version = 0;
+	hdr.section_len = -1;
+	hdr.block_total_len2 = hdr.block_total_len;
+	fwrite(&hdr, sizeof(hdr), 1, wt->pcapng);
+
+	os_memset(&desc, 0, sizeof(desc));
+	desc.block_type = PCAPNG_BLOCK_IFACE_DESC;
+	desc.block_total_len = sizeof(desc);
+	desc.block_total_len2 = desc.block_total_len;
+	desc.link_type = wt->ethernet ? DLT_EN10MB : LINKTYPE_IEEE802_11_RADIO;
+	desc.snap_len = 65535;
+	fwrite(&desc, sizeof(desc), 1, wt->pcapng);
+	if (wt->pcap_no_buffer)
+		fflush(wt->pcapng);
+
+	return 0;
+}
+
+
+void write_pcapng_deinit(struct wlantest *wt)
+{
+	if (wt->pcapng) {
+		fclose(wt->pcapng);
+		wt->pcapng = NULL;
+	}
+}
+
+
+static u8 * pcapng_add_comments(struct wlantest *wt, u8 *pos)
+{
+	size_t i;
+	u16 *len;
+
+	if (!wt->num_notes)
+		return pos;
+
+	*((u16 *) pos) = 1 /* opt_comment */;
+	pos += 2;
+	len = (u16 *) pos /* length to be filled in */;
+	pos += 2;
+
+	for (i = 0; i < wt->num_notes; i++) {
+		size_t nlen = os_strlen(wt->notes[i]);
+		if (i > 0)
+			*pos++ = '\n';
+		os_memcpy(pos, wt->notes[i], nlen);
+		pos += nlen;
+	}
+	*len = pos - (u8 *) len - 2;
+	pos += PAD32(*len);
+
+	*((u16 *) pos) = 0 /* opt_endofopt */;
+	pos += 2;
+	*((u16 *) pos) = 0;
+	pos += 2;
+
+	return pos;
+}
+
+
+static void write_pcapng_decrypted(struct wlantest *wt)
+{
+	size_t len;
+	struct pcapng_enhanced_packet *pkt;
+	u8 *pos;
+	u32 *block_len;
+
+	if (!wt->pcapng || wt->decrypted == NULL)
+		return;
+
+	add_note(wt, MSG_EXCESSIVE, "decrypted version of the previous frame");
+
+	len = sizeof(*pkt) + wt->decrypted_len + 100 + notes_len(wt, 32);
+	pkt = os_zalloc(len);
+	if (pkt == NULL)
+		return;
+
+	pkt->block_type = PCAPNG_BLOCK_ENHANCED_PACKET;
+	pkt->interface_id = 0;
+	pkt->timestamp_high = wt->write_pcapng_time_high;
+	pkt->timestamp_low = wt->write_pcapng_time_low;
+	pkt->captured_len = wt->decrypted_len;
+	pkt->packet_len = wt->decrypted_len;
+
+	pos = (u8 *) (pkt + 1);
+
+	os_memcpy(pos, wt->decrypted, wt->decrypted_len);
+	pos += ALIGN32(wt->decrypted_len);
+
+	pos = pcapng_add_comments(wt, pos);
+
+	block_len = (u32 *) pos;
+	pos += 4;
+	*block_len = pkt->block_total_len = pos - (u8 *) pkt;
+
+	fwrite(pkt, pos - (u8 *) pkt, 1, wt->pcapng);
+	if (wt->pcap_no_buffer)
+		fflush(wt->pcapng);
+
+	os_free(pkt);
+}
+
+
+void write_pcapng_write_read(struct wlantest *wt, int dlt,
+			     struct pcap_pkthdr *hdr, const u8 *data)
+{
+	struct pcapng_enhanced_packet *pkt;
+	u8 *pos;
+	u32 *block_len;
+	u64 timestamp;
+	size_t len, datalen = hdr->caplen;
+	u8 rtap[] = {
+		0x00 /* rev */,
+		0x00 /* pad */,
+		0x0a, 0x00, /* header len */
+		0x02, 0x00, 0x00, 0x00, /* present flags */
+		0x00, /* flags */
+		0x00 /* pad */
+	};
+
+	if (wt->assume_fcs)
+		rtap[8] |= 0x10;
+
+	if (!wt->pcapng)
+		return;
+
+	len = sizeof(*pkt) + hdr->len + 100 + notes_len(wt, 32) + sizeof(rtap);
+	pkt = os_zalloc(len);
+	if (pkt == NULL)
+		return;
+
+	pkt->block_type = PCAPNG_BLOCK_ENHANCED_PACKET;
+	pkt->interface_id = 0;
+	timestamp = 1000000 * hdr->ts.tv_sec + hdr->ts.tv_usec;
+	pkt->timestamp_high = timestamp >> 32;
+	pkt->timestamp_low = timestamp & 0xffffffff;
+	wt->write_pcapng_time_high = pkt->timestamp_high;
+	wt->write_pcapng_time_low = pkt->timestamp_low;
+	pkt->captured_len = hdr->caplen;
+	pkt->packet_len = hdr->len;
+
+	pos = (u8 *) (pkt + 1);
+
+	switch (dlt) {
+	case DLT_EN10MB:
+	case DLT_IEEE802_11_RADIO:
+		break;
+	case DLT_PRISM_HEADER:
+		/* remove prism header (could be kept ... lazy) */
+		pkt->captured_len -= WPA_GET_LE32(data + 4);
+		pkt->packet_len -= WPA_GET_LE32(data + 4);
+		datalen -= WPA_GET_LE32(data + 4);
+		data += WPA_GET_LE32(data + 4);
+		/* fall through */
+	case DLT_IEEE802_11:
+		pkt->captured_len += sizeof(rtap);
+		pkt->packet_len += sizeof(rtap);
+		os_memcpy(pos, &rtap, sizeof(rtap));
+		pos += sizeof(rtap);
+		break;
+	default:
+		return;
+	}
+
+	os_memcpy(pos, data, datalen);
+	pos += datalen + PAD32(pkt->captured_len);
+	pos = pcapng_add_comments(wt, pos);
+
+	block_len = (u32 *) pos;
+	pos += 4;
+	*block_len = pkt->block_total_len = pos - (u8 *) pkt;
+
+	fwrite(pkt, pos - (u8 *) pkt, 1, wt->pcapng);
+	if (wt->pcap_no_buffer)
+		fflush(wt->pcapng);
+
+	os_free(pkt);
+
+	write_pcapng_decrypted(wt);
+}
+
+
+void write_pcapng_captured(struct wlantest *wt, const u8 *buf, size_t len)
+{
+	struct pcap_pkthdr h;
+
+	if (!wt->pcapng)
+		return;
+
+	os_memset(&h, 0, sizeof(h));
+	gettimeofday(&h.ts, NULL);
+	h.caplen = len;
+	h.len = len;
+	write_pcapng_write_read(wt, wt->ethernet ? DLT_EN10MB :
+				DLT_IEEE802_11_RADIO, &h, buf);
+}