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);
+}